From: Vivek Gautam Date: Thu, 11 May 2017 06:47:42 +0000 (+0530) Subject: phy: Group vendor specific phy drivers X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=0b56e9a7e8358e59b21d8a425e463072bfae523c;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git phy: Group vendor specific phy drivers Adding vendor specific directories in phy to group phy drivers under their respective vendor umbrella. Also updated the MAINTAINERS file to reflect the correct directory structure for phy drivers. Signed-off-by: Vivek Gautam Acked-by: Heiko Stuebner Acked-by: Viresh Kumar Acked-by: Krzysztof Kozlowski Acked-by: Yoshihiro Shimoda Reviewed-by: Jaehoon Chung Cc: Kishon Vijay Abraham I Cc: David S. Miller Cc: Geert Uytterhoeven Cc: Yoshihiro Shimoda Cc: Guenter Roeck Cc: Heiko Stuebner Cc: Viresh Kumar Cc: Maxime Ripard Cc: Chen-Yu Tsai Cc: Sylwester Nawrocki Cc: Krzysztof Kozlowski Cc: Jaehoon Chung Cc: Stephen Boyd Cc: Martin Blumenstingl Cc: linux-arm-kernel@lists.infradead.org Cc: linux-arm-msm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: linux-omap@vger.kernel.org Cc: linux-renesas-soc@vger.kernel.org Cc: linux-rockchip@lists.infradead.org Cc: linux-samsung-soc@vger.kernel.org Cc: linux-usb@vger.kernel.org Signed-off-by: Kishon Vijay Abraham I --- diff --git a/MAINTAINERS b/MAINTAINERS index f7d568b8f133..a47d3da4d35d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1844,8 +1844,8 @@ F: drivers/i2c/busses/i2c-st.c F: drivers/media/rc/st_rc.c F: drivers/media/platform/sti/c8sectpfe/ F: drivers/mmc/host/sdhci-st.c -F: drivers/phy/phy-miphy28lp.c -F: drivers/phy/phy-stih407-usb.c +F: drivers/phy/st/phy-miphy28lp.c +F: drivers/phy/st/phy-stih407-usb.c F: drivers/pinctrl/pinctrl-st.c F: drivers/remoteproc/st_remoteproc.c F: drivers/remoteproc/st_slim_rproc.c @@ -10833,7 +10833,7 @@ RENESAS USB2 PHY DRIVER M: Yoshihiro Shimoda L: linux-renesas-soc@vger.kernel.org S: Maintained -F: drivers/phy/phy-rcar-gen3-usb2.c +F: drivers/phy/renesas/phy-rcar-gen3-usb2.c RESET CONTROLLER FRAMEWORK M: Philipp Zabel @@ -11235,12 +11235,12 @@ L: linux-kernel@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/phy/samsung-phy.txt F: Documentation/phy/samsung-usb2.txt -F: drivers/phy/phy-exynos4210-usb2.c -F: drivers/phy/phy-exynos4x12-usb2.c -F: drivers/phy/phy-exynos5250-usb2.c -F: drivers/phy/phy-s5pv210-usb2.c -F: drivers/phy/phy-samsung-usb2.c -F: drivers/phy/phy-samsung-usb2.h +F: drivers/phy/samsung/phy-exynos4210-usb2.c +F: drivers/phy/samsung/phy-exynos4x12-usb2.c +F: drivers/phy/samsung/phy-exynos5250-usb2.c +F: drivers/phy/samsung/phy-s5pv210-usb2.c +F: drivers/phy/samsung/phy-samsung-usb2.c +F: drivers/phy/samsung/phy-samsung-usb2.h SERIAL DRIVERS M: Greg Kroah-Hartman diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index afaf7b643eeb..01009b2a7d74 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -15,73 +15,6 @@ config GENERIC_PHY phy users can obtain reference to the PHY. All the users of this framework should select this config. -config PHY_BCM_NS_USB2 - tristate "Broadcom Northstar USB 2.0 PHY Driver" - depends on ARCH_BCM_IPROC || COMPILE_TEST - depends on HAS_IOMEM && OF - select GENERIC_PHY - help - Enable this to support Broadcom USB 2.0 PHY connected to the USB - controller on Northstar family. - -config PHY_BCM_NS_USB3 - tristate "Broadcom Northstar USB 3.0 PHY Driver" - depends on ARCH_BCM_IPROC || COMPILE_TEST - depends on HAS_IOMEM && OF - select GENERIC_PHY - help - Enable this to support Broadcom USB 3.0 PHY connected to the USB - controller on Northstar family. - -config PHY_BERLIN_USB - tristate "Marvell Berlin USB PHY Driver" - depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF - select GENERIC_PHY - help - Enable this to support the USB PHY on Marvell Berlin SoCs. - -config PHY_BERLIN_SATA - tristate "Marvell Berlin SATA PHY driver" - depends on ARCH_BERLIN && HAS_IOMEM && OF - select GENERIC_PHY - help - Enable this to support the SATA PHY on Marvell Berlin SoCs. - -config ARMADA375_USBCLUSTER_PHY - def_bool y - depends on MACH_ARMADA_375 || COMPILE_TEST - depends on OF && HAS_IOMEM - select GENERIC_PHY - -config PHY_DA8XX_USB - tristate "TI DA8xx USB PHY Driver" - depends on ARCH_DAVINCI_DA8XX - select GENERIC_PHY - select MFD_SYSCON - help - Enable this to support the USB PHY on DA8xx SoCs. - - This driver controls both the USB 1.1 PHY and the USB 2.0 PHY. - -config PHY_DM816X_USB - tristate "TI dm816x USB PHY driver" - depends on ARCH_OMAP2PLUS - depends on USB_SUPPORT - select GENERIC_PHY - select USB_PHY - help - Enable this for dm816x USB to work. - -config PHY_EXYNOS_MIPI_VIDEO - tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" - depends on HAS_IOMEM - depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST - select GENERIC_PHY - default y if ARCH_S5PV210 || ARCH_EXYNOS - help - Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P - and EXYNOS SoCs. - config PHY_LPC18XX_USB_OTG tristate "NXP LPC18xx/43xx SoC USB OTG PHY driver" depends on OF && (ARCH_LPC18XX || COMPILE_TEST) @@ -93,146 +26,6 @@ config PHY_LPC18XX_USB_OTG This driver is need for USB0 support on LPC18xx/43xx and takes care of enabling and clock setup. -config PHY_PXA_28NM_HSIC - tristate "Marvell USB HSIC 28nm PHY Driver" - depends on HAS_IOMEM - select GENERIC_PHY - help - Enable this to support Marvell USB HSIC PHY driver for Marvell - SoC. This driver will do the PHY initialization and shutdown. - The PHY driver will be used by Marvell ehci driver. - - To compile this driver as a module, choose M here. - -config PHY_PXA_28NM_USB2 - tristate "Marvell USB 2.0 28nm PHY Driver" - depends on HAS_IOMEM - select GENERIC_PHY - help - Enable this to support Marvell USB 2.0 PHY driver for Marvell - SoC. This driver will do the PHY initialization and shutdown. - The PHY driver will be used by Marvell udc/ehci/otg driver. - - To compile this driver as a module, choose M here. - -config PHY_MVEBU_SATA - def_bool y - depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD - depends on OF - select GENERIC_PHY - -config PHY_MIPHY28LP - tristate "STMicroelectronics MIPHY28LP PHY driver for STiH407" - depends on ARCH_STI - select GENERIC_PHY - help - Enable this to support the miphy transceiver (for SATA/PCIE/USB3) - that is part of STMicroelectronics STiH407 SoC. - -config PHY_RCAR_GEN2 - tristate "Renesas R-Car generation 2 USB PHY driver" - depends on ARCH_RENESAS - depends on GENERIC_PHY - help - Support for USB PHY found on Renesas R-Car generation 2 SoCs. - -config PHY_RCAR_GEN3_USB2 - tristate "Renesas R-Car generation 3 USB 2.0 PHY driver" - depends on ARCH_RENESAS - depends on EXTCON - select GENERIC_PHY - help - Support for USB 2.0 PHY found on Renesas R-Car generation 3 SoCs. - -config OMAP_CONTROL_PHY - tristate "OMAP CONTROL PHY Driver" - depends on ARCH_OMAP2PLUS || COMPILE_TEST - help - Enable this to add support for the PHY part present in the control - module. This driver has API to power on the USB2 PHY and to write to - the mailbox. The mailbox is present only in omap4 and the register to - power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an - additional register to power on USB3 PHY/SATA PHY/PCIE PHY - (PIPE3 PHY). - -config OMAP_USB2 - tristate "OMAP USB2 PHY Driver" - depends on ARCH_OMAP2PLUS - depends on USB_SUPPORT - select GENERIC_PHY - select USB_PHY - select OMAP_CONTROL_PHY - depends on OMAP_OCP2SCP - help - Enable this to support the transceiver that is part of SOC. This - driver takes care of all the PHY functionality apart from comparator. - The USB OTG controller communicates with the comparator using this - driver. - -config TI_PIPE3 - tristate "TI PIPE3 PHY Driver" - depends on ARCH_OMAP2PLUS || COMPILE_TEST - select GENERIC_PHY - select OMAP_CONTROL_PHY - depends on OMAP_OCP2SCP - help - Enable this to support the PIPE3 PHY that is part of TI SOCs. This - driver takes care of all the PHY functionality apart from comparator. - This driver interacts with the "OMAP Control PHY Driver" to power - on/off the PHY. - -config TWL4030_USB - tristate "TWL4030 USB Transceiver Driver" - depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS - depends on USB_SUPPORT - depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't 'y' - select GENERIC_PHY - select USB_PHY - help - Enable this to support the USB OTG transceiver on TWL4030 - family chips (including the TWL5030 and TPS659x0 devices). - This transceiver supports high and full speed devices plus, - in host mode, low speed. - -config PHY_EXYNOS_DP_VIDEO - tristate "EXYNOS SoC series Display Port PHY driver" - depends on OF - depends on ARCH_EXYNOS || COMPILE_TEST - default ARCH_EXYNOS - select GENERIC_PHY - help - Support for Display Port PHY found on Samsung EXYNOS SoCs. - -config BCM_KONA_USB2_PHY - tristate "Broadcom Kona USB2 PHY Driver" - depends on HAS_IOMEM - select GENERIC_PHY - help - Enable this to support the Broadcom Kona USB 2.0 PHY. - -config PHY_EXYNOS5250_SATA - tristate "Exynos5250 Sata SerDes/PHY driver" - depends on SOC_EXYNOS5250 - depends on HAS_IOMEM - depends on OF - select GENERIC_PHY - select I2C - select I2C_S3C2410 - select MFD_SYSCON - help - Enable this to support SATA SerDes/Phy found on Samsung's - Exynos5250 based SoCs.This SerDes/Phy supports SATA 1.5 Gb/s, - SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host - port to accept one SATA device. - -config PHY_HIX5HD2_SATA - tristate "HIX5HD2 SATA PHY Driver" - depends on ARCH_HIX5HD2 && OF && HAS_IOMEM - select GENERIC_PHY - select MFD_SYSCON - help - Support for SATA PHY on Hisilicon hix5hd2 Soc. - config PHY_MT65XX_USB3 tristate "Mediatek USB3.0 PHY Driver" depends on ARCH_MEDIATEK && OF @@ -241,104 +34,6 @@ config PHY_MT65XX_USB3 Say 'Y' here to add support for Mediatek USB3.0 PHY driver, it supports multiple usb2.0 and usb3.0 ports. -config PHY_HI6220_USB - tristate "hi6220 USB PHY support" - depends on (ARCH_HISI && ARM64) || COMPILE_TEST - select GENERIC_PHY - select MFD_SYSCON - help - Enable this to support the HISILICON HI6220 USB PHY. - - To compile this driver as a module, choose M here. - -config PHY_SUN4I_USB - tristate "Allwinner sunxi SoC USB PHY driver" - depends on ARCH_SUNXI && HAS_IOMEM && OF - depends on RESET_CONTROLLER - depends on EXTCON - depends on POWER_SUPPLY - depends on USB_SUPPORT - select GENERIC_PHY - select USB_COMMON - help - Enable this to support the transceiver that is part of Allwinner - sunxi SoCs. - - This driver controls the entire USB PHY block, both the USB OTG - parts, as well as the 2 regular USB 2 host PHYs. - -config PHY_SUN9I_USB - tristate "Allwinner sun9i SoC USB PHY driver" - depends on ARCH_SUNXI && HAS_IOMEM && OF - depends on RESET_CONTROLLER - depends on USB_SUPPORT - select USB_COMMON - select GENERIC_PHY - help - Enable this to support the transceiver that is part of Allwinner - sun9i SoCs. - - This driver controls each individual USB 2 host PHY. - -config PHY_SAMSUNG_USB2 - tristate "Samsung USB 2.0 PHY driver" - depends on HAS_IOMEM - depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 - select GENERIC_PHY - select MFD_SYSCON - default ARCH_EXYNOS - help - Enable this to support the Samsung USB 2.0 PHY driver for Samsung - SoCs. This driver provides the interface for USB 2.0 PHY. Support - for particular PHYs will be enabled based on the SoC type in addition - to this driver. - -config PHY_S5PV210_USB2 - bool "Support for S5PV210" - depends on PHY_SAMSUNG_USB2 - depends on ARCH_S5PV210 - help - Enable USB PHY support for S5PV210. This option requires that Samsung - USB 2.0 PHY driver is enabled and means that support for this - particular SoC is compiled in the driver. In case of S5PV210 two phys - are available - device and host. - -config PHY_EXYNOS4210_USB2 - bool - depends on PHY_SAMSUNG_USB2 - default CPU_EXYNOS4210 - -config PHY_EXYNOS4X12_USB2 - bool - depends on PHY_SAMSUNG_USB2 - default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412 - -config PHY_EXYNOS5250_USB2 - bool - depends on PHY_SAMSUNG_USB2 - default SOC_EXYNOS5250 || SOC_EXYNOS5420 - -config PHY_EXYNOS5_USBDRD - tristate "Exynos5 SoC series USB DRD PHY driver" - depends on ARCH_EXYNOS && OF - depends on HAS_IOMEM - depends on USB_DWC3_EXYNOS - select GENERIC_PHY - select MFD_SYSCON - default y - help - Enable USB DRD PHY support for Exynos 5 SoC series. - 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 OF && (ARCH_EXYNOS || COMPILE_TEST) - 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 @@ -346,83 +41,6 @@ config PHY_PISTACHIO_USB help Enable this to support the USB2.0 PHY on the IMG Pistachio SoC. -config PHY_QCOM_APQ8064_SATA - tristate "Qualcomm APQ8064 SATA SerDes/PHY driver" - depends on ARCH_QCOM - depends on HAS_IOMEM - depends on OF - select GENERIC_PHY - -config PHY_QCOM_IPQ806X_SATA - tristate "Qualcomm IPQ806x SATA SerDes/PHY driver" - depends on ARCH_QCOM - depends on HAS_IOMEM - depends on OF - select GENERIC_PHY - -config PHY_ROCKCHIP_USB - tristate "Rockchip USB2 PHY Driver" - depends on ARCH_ROCKCHIP && OF - select GENERIC_PHY - help - Enable this to support the Rockchip USB 2.0 PHY. - -config PHY_ROCKCHIP_INNO_USB2 - tristate "Rockchip INNO USB2PHY Driver" - depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF - depends on COMMON_CLK - depends on EXTCON - depends on USB_SUPPORT - select GENERIC_PHY - select USB_COMMON - help - Support for Rockchip USB2.0 PHY with Innosilicon IP block. - -config PHY_ROCKCHIP_EMMC - tristate "Rockchip EMMC PHY Driver" - depends on ARCH_ROCKCHIP && OF - select GENERIC_PHY - help - Enable this to support the Rockchip EMMC PHY. - -config PHY_ROCKCHIP_DP - tristate "Rockchip Display Port PHY Driver" - depends on ARCH_ROCKCHIP && OF - select GENERIC_PHY - help - Enable this to support the Rockchip Display Port PHY. - -config PHY_ROCKCHIP_PCIE - tristate "Rockchip PCIe PHY Driver" - depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST - select GENERIC_PHY - select MFD_SYSCON - help - Enable this to support the Rockchip PCIe PHY. - -config PHY_ROCKCHIP_TYPEC - tristate "Rockchip TYPEC PHY Driver" - depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST) - select EXTCON - select GENERIC_PHY - select RESET_CONTROLLER - help - Enable this to support the Rockchip USB TYPEC PHY. - -config PHY_ST_SPEAR1310_MIPHY - tristate "ST SPEAR1310-MIPHY driver" - select GENERIC_PHY - depends on MACH_SPEAR1310 || COMPILE_TEST - help - Support for ST SPEAr1310 MIPHY which can be used for PCIe and SATA. - -config PHY_ST_SPEAR1340_MIPHY - tristate "ST SPEAR1340-MIPHY driver" - select GENERIC_PHY - depends on MACH_SPEAR1340 || COMPILE_TEST - help - Support for ST SPEAr1340 MIPHY which can be used for PCIe and SATA. - config PHY_XGENE tristate "APM X-Gene 15Gbps PHY support" depends on HAS_IOMEM && OF && (ARM64 || COMPILE_TEST) @@ -430,104 +48,17 @@ config PHY_XGENE help This option enables support for APM X-Gene SoC multi-purpose PHY. -config PHY_STIH407_USB - tristate "STMicroelectronics USB2 picoPHY driver for STiH407 family" - depends on RESET_CONTROLLER - depends on ARCH_STI || COMPILE_TEST - select GENERIC_PHY - help - Enable this support to enable the picoPHY device used by USB2 - and USB3 controllers on STMicroelectronics STiH407 SoC families. - -config PHY_QCOM_QMP - tristate "Qualcomm QMP PHY Driver" - depends on OF && COMMON_CLK && (ARCH_QCOM || COMPILE_TEST) - select GENERIC_PHY - help - Enable this to support the QMP PHY transceiver that is used - with controllers such as PCIe, UFS, and USB on Qualcomm chips. - -config PHY_QCOM_QUSB2 - tristate "Qualcomm QUSB2 PHY Driver" - depends on OF && (ARCH_QCOM || COMPILE_TEST) - depends on NVMEM || !NVMEM - select GENERIC_PHY - help - Enable this to support the HighSpeed QUSB2 PHY transceiver for USB - controllers on Qualcomm chips. This driver supports the high-speed - PHY which is usually paired with either the ChipIdea or Synopsys DWC3 - USB IPs on MSM SOCs. - -config PHY_QCOM_UFS - tristate "Qualcomm UFS PHY driver" - depends on OF && ARCH_QCOM - select GENERIC_PHY - help - Support for UFS PHY on QCOM chipsets. - -config PHY_QCOM_USB_HS - tristate "Qualcomm USB HS PHY module" - depends on USB_ULPI_BUS - depends on EXTCON || !EXTCON # if EXTCON=m, this cannot be built-in - select GENERIC_PHY - help - Support for the USB high-speed ULPI compliant phy on Qualcomm - chipsets. - -config PHY_QCOM_USB_HSIC - tristate "Qualcomm USB HSIC ULPI PHY module" - depends on USB_ULPI_BUS - select GENERIC_PHY - help - Support for the USB HSIC ULPI compliant PHY on QCOM chipsets. - -config PHY_TUSB1210 - tristate "TI TUSB1210 ULPI PHY module" - depends on USB_ULPI_BUS - select GENERIC_PHY - help - Support for TI TUSB1210 USB ULPI PHY. - -config PHY_BRCM_SATA - tristate "Broadcom SATA PHY driver" - depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || COMPILE_TEST - depends on OF - select GENERIC_PHY - default ARCH_BCM_IPROC - help - Enable this to support the Broadcom SATA PHY. - If unsure, say N. - -config PHY_CYGNUS_PCIE - tristate "Broadcom Cygnus PCIe PHY driver" - depends on OF && (ARCH_BCM_CYGNUS || COMPILE_TEST) - select GENERIC_PHY - default ARCH_BCM_CYGNUS - help - Enable this to support the Broadcom Cygnus PCIe PHY. - If unsure, say N. - +source "drivers/phy/allwinner/Kconfig" +source "drivers/phy/amlogic/Kconfig" +source "drivers/phy/broadcom/Kconfig" +source "drivers/phy/hisilicon/Kconfig" +source "drivers/phy/marvell/Kconfig" +source "drivers/phy/qualcomm/Kconfig" +source "drivers/phy/renesas/Kconfig" +source "drivers/phy/rockchip/Kconfig" +source "drivers/phy/samsung/Kconfig" +source "drivers/phy/st/Kconfig" source "drivers/phy/tegra/Kconfig" - -config PHY_NS2_PCIE - tristate "Broadcom Northstar2 PCIe PHY driver" - depends on OF && MDIO_BUS_MUX_BCM_IPROC - select GENERIC_PHY - default ARCH_BCM_IPROC - help - Enable this to support the Broadcom Northstar2 PCIe PHY. - If unsure, say N. - -config PHY_MESON8B_USB2 - tristate "Meson8b and GXBB USB2 PHY driver" - default ARCH_MESON - depends on OF && (ARCH_MESON || COMPILE_TEST) - depends on USB_SUPPORT - select USB_COMMON - select GENERIC_PHY - help - Enable this to support the Meson USB2 PHYs found in Meson8b - and GXBB SoCs. - If unsure, say N. +source "drivers/phy/ti/Kconfig" endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f8047b4639fa..c1bd1fa3c853 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -3,64 +3,20 @@ # obj-$(CONFIG_GENERIC_PHY) += phy-core.o -obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o -obj-$(CONFIG_PHY_BCM_NS_USB3) += phy-bcm-ns-usb3.o -obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o -obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o -obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o -obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o -obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o -obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o -obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o -obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o obj-$(CONFIG_PHY_LPC18XX_USB_OTG) += phy-lpc18xx-usb-otg.o -obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o -obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o -obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o -obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o -obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o -obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o -obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o -obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o -obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o -obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o -obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o -obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o -obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o obj-$(CONFIG_PHY_MT65XX_USB3) += phy-mt65xx-usb3.o -obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o -obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o -obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o -phy-exynos-usb2-y += phy-samsung-usb2.o -phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o -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 -obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o -obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o -obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o -obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o -obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o -obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o -obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o obj-$(CONFIG_PHY_XGENE) += phy-xgene.o -obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o -obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o -obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o -obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o -obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o -obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o -obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o -obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o -obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o -obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o -obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o -obj-$(CONFIG_ARCH_TEGRA) += tegra/ -obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o -obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o + +obj-$(CONFIG_ARCH_SUNXI) += allwinner/ +obj-$(CONFIG_ARCH_MESON) += amlogic/ +obj-$(CONFIG_ARCH_RENESAS) += renesas/ +obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ +obj-$(CONFIG_ARCH_TEGRA) += tegra/ +obj-y += broadcom/ \ + hisilicon/ \ + marvell/ \ + qualcomm/ \ + samsung/ \ + st/ \ + ti/ diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig new file mode 100644 index 000000000000..cdc1e745ba47 --- /dev/null +++ b/drivers/phy/allwinner/Kconfig @@ -0,0 +1,31 @@ +# +# Phy drivers for Allwinner platforms +# +config PHY_SUN4I_USB + tristate "Allwinner sunxi SoC USB PHY driver" + depends on ARCH_SUNXI && HAS_IOMEM && OF + depends on RESET_CONTROLLER + depends on EXTCON + depends on POWER_SUPPLY + depends on USB_SUPPORT + select GENERIC_PHY + select USB_COMMON + help + Enable this to support the transceiver that is part of Allwinner + sunxi SoCs. + + This driver controls the entire USB PHY block, both the USB OTG + parts, as well as the 2 regular USB 2 host PHYs. + +config PHY_SUN9I_USB + tristate "Allwinner sun9i SoC USB PHY driver" + depends on ARCH_SUNXI && HAS_IOMEM && OF + depends on RESET_CONTROLLER + depends on USB_SUPPORT + select USB_COMMON + select GENERIC_PHY + help + Enable this to support the transceiver that is part of Allwinner + sun9i SoCs. + + This driver controls each individual USB 2 host PHY. diff --git a/drivers/phy/allwinner/Makefile b/drivers/phy/allwinner/Makefile new file mode 100644 index 000000000000..8605529c01a1 --- /dev/null +++ b/drivers/phy/allwinner/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o +obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c new file mode 100644 index 000000000000..bbf06cfe5898 --- /dev/null +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -0,0 +1,891 @@ +/* + * Allwinner sun4i USB phy driver + * + * Copyright (C) 2014-2015 Hans de Goede + * + * Based on code from + * Allwinner Technology Co., Ltd. + * + * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Author: Sylwester Nawrocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_ISCR 0x00 +#define REG_PHYCTL_A10 0x04 +#define REG_PHYBIST 0x08 +#define REG_PHYTUNE 0x0c +#define REG_PHYCTL_A33 0x10 +#define REG_PHY_OTGCTL 0x20 + +#define REG_PMU_UNK1 0x10 + +#define PHYCTL_DATA BIT(7) + +#define OTGCTL_ROUTE_MUSB BIT(0) + +#define SUNXI_AHB_ICHR8_EN BIT(10) +#define SUNXI_AHB_INCR4_BURST_EN BIT(9) +#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) +#define SUNXI_ULPI_BYPASS_EN BIT(0) + +/* ISCR, Interface Status and Control bits */ +#define ISCR_ID_PULLUP_EN (1 << 17) +#define ISCR_DPDM_PULLUP_EN (1 << 16) +/* sunxi has the phy id/vbus pins not connected, so we use the force bits */ +#define ISCR_FORCE_ID_MASK (3 << 14) +#define ISCR_FORCE_ID_LOW (2 << 14) +#define ISCR_FORCE_ID_HIGH (3 << 14) +#define ISCR_FORCE_VBUS_MASK (3 << 12) +#define ISCR_FORCE_VBUS_LOW (2 << 12) +#define ISCR_FORCE_VBUS_HIGH (3 << 12) + +/* Common Control Bits for Both PHYs */ +#define PHY_PLL_BW 0x03 +#define PHY_RES45_CAL_EN 0x0c + +/* Private Control Bits for Each PHY */ +#define PHY_TX_AMPLITUDE_TUNE 0x20 +#define PHY_TX_SLEWRATE_TUNE 0x22 +#define PHY_VBUSVALID_TH_SEL 0x25 +#define PHY_PULLUP_RES_SEL 0x27 +#define PHY_OTG_FUNC_EN 0x28 +#define PHY_VBUS_DET_EN 0x29 +#define PHY_DISCON_TH_SEL 0x2a +#define PHY_SQUELCH_DETECT 0x3c + +#define MAX_PHYS 4 + +/* + * Note do not raise the debounce time, we must report Vusb high within 100ms + * otherwise we get Vbus errors + */ +#define DEBOUNCE_TIME msecs_to_jiffies(50) +#define POLL_TIME msecs_to_jiffies(250) + +enum sun4i_usb_phy_type { + sun4i_a10_phy, + sun6i_a31_phy, + sun8i_a33_phy, + sun8i_h3_phy, + sun8i_v3s_phy, + sun50i_a64_phy, +}; + +struct sun4i_usb_phy_cfg { + int num_phys; + enum sun4i_usb_phy_type type; + u32 disc_thresh; + u8 phyctl_offset; + bool dedicated_clocks; + bool enable_pmu_unk1; + bool phy0_dual_route; +}; + +struct sun4i_usb_phy_data { + void __iomem *base; + const struct sun4i_usb_phy_cfg *cfg; + enum usb_dr_mode dr_mode; + spinlock_t reg_lock; /* guard access to phyctl reg */ + struct sun4i_usb_phy { + struct phy *phy; + void __iomem *pmu; + struct regulator *vbus; + struct reset_control *reset; + struct clk *clk; + bool regulator_on; + int index; + } phys[MAX_PHYS]; + /* phy0 / otg related variables */ + struct extcon_dev *extcon; + bool phy0_init; + struct gpio_desc *id_det_gpio; + struct gpio_desc *vbus_det_gpio; + struct power_supply *vbus_power_supply; + struct notifier_block vbus_power_nb; + bool vbus_power_nb_registered; + bool force_session_end; + int id_det_irq; + int vbus_det_irq; + int id_det; + int vbus_det; + struct delayed_work detect; +}; + +#define to_sun4i_usb_phy_data(phy) \ + container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index]) + +static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set) +{ + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); + u32 iscr; + + iscr = readl(data->base + REG_ISCR); + iscr &= ~clr; + iscr |= set; + writel(iscr, data->base + REG_ISCR); +} + +static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val) +{ + if (val) + val = ISCR_FORCE_ID_HIGH; + else + val = ISCR_FORCE_ID_LOW; + + sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val); +} + +static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val) +{ + if (val) + val = ISCR_FORCE_VBUS_HIGH; + else + val = ISCR_FORCE_VBUS_LOW; + + sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val); +} + +static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data, + int len) +{ + struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy); + u32 temp, usbc_bit = BIT(phy->index * 2); + void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset; + unsigned long flags; + int i; + + spin_lock_irqsave(&phy_data->reg_lock, flags); + + if (phy_data->cfg->phyctl_offset == REG_PHYCTL_A33) { + /* SoCs newer than A33 need us to set phyctl to 0 explicitly */ + writel(0, phyctl); + } + + for (i = 0; i < len; i++) { + temp = readl(phyctl); + + /* clear the address portion */ + temp &= ~(0xff << 8); + + /* set the address */ + temp |= ((addr + i) << 8); + writel(temp, phyctl); + + /* set the data bit and clear usbc bit*/ + temp = readb(phyctl); + if (data & 0x1) + temp |= PHYCTL_DATA; + else + temp &= ~PHYCTL_DATA; + temp &= ~usbc_bit; + writeb(temp, phyctl); + + /* pulse usbc_bit */ + temp = readb(phyctl); + temp |= usbc_bit; + writeb(temp, phyctl); + + temp = readb(phyctl); + temp &= ~usbc_bit; + writeb(temp, phyctl); + + data >>= 1; + } + + spin_unlock_irqrestore(&phy_data->reg_lock, flags); +} + +static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable) +{ + u32 bits, reg_value; + + if (!phy->pmu) + return; + + bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN | + SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN; + + reg_value = readl(phy->pmu); + + if (enable) + reg_value |= bits; + else + reg_value &= ~bits; + + writel(reg_value, phy->pmu); +} + +static int sun4i_usb_phy_init(struct phy *_phy) +{ + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); + int ret; + u32 val; + + ret = clk_prepare_enable(phy->clk); + if (ret) + return ret; + + ret = reset_control_deassert(phy->reset); + if (ret) { + clk_disable_unprepare(phy->clk); + return ret; + } + + if (phy->pmu && data->cfg->enable_pmu_unk1) { + val = readl(phy->pmu + REG_PMU_UNK1); + writel(val & ~2, phy->pmu + REG_PMU_UNK1); + } + + /* Enable USB 45 Ohm resistor calibration */ + if (phy->index == 0) + sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1); + + /* Adjust PHY's magnitude and rate */ + sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5); + + /* Disconnect threshold adjustment */ + sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, + data->cfg->disc_thresh, 2); + + sun4i_usb_phy_passby(phy, 1); + + if (phy->index == 0) { + data->phy0_init = true; + + /* Enable pull-ups */ + sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN); + sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN); + + /* Force ISCR and cable state updates */ + data->id_det = -1; + data->vbus_det = -1; + queue_delayed_work(system_wq, &data->detect, 0); + } + + return 0; +} + +static int sun4i_usb_phy_exit(struct phy *_phy) +{ + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); + + if (phy->index == 0) { + /* Disable pull-ups */ + sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0); + sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0); + data->phy0_init = false; + } + + sun4i_usb_phy_passby(phy, 0); + reset_control_assert(phy->reset); + clk_disable_unprepare(phy->clk); + + return 0; +} + +static int sun4i_usb_phy0_get_id_det(struct sun4i_usb_phy_data *data) +{ + switch (data->dr_mode) { + case USB_DR_MODE_OTG: + if (data->id_det_gpio) + return gpiod_get_value_cansleep(data->id_det_gpio); + else + return 1; /* Fallback to peripheral mode */ + case USB_DR_MODE_HOST: + return 0; + case USB_DR_MODE_PERIPHERAL: + default: + return 1; + } +} + +static int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data) +{ + if (data->vbus_det_gpio) + return gpiod_get_value_cansleep(data->vbus_det_gpio); + + if (data->vbus_power_supply) { + union power_supply_propval val; + int r; + + r = power_supply_get_property(data->vbus_power_supply, + POWER_SUPPLY_PROP_PRESENT, &val); + if (r == 0) + return val.intval; + } + + /* Fallback: report vbus as high */ + return 1; +} + +static bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data) +{ + return data->vbus_det_gpio || data->vbus_power_supply; +} + +static bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data) +{ + if ((data->id_det_gpio && data->id_det_irq <= 0) || + (data->vbus_det_gpio && data->vbus_det_irq <= 0)) + return true; + + /* + * The A31 companion pmic (axp221) does not generate vbus change + * interrupts when the board is driving vbus, so we must poll + * when using the pmic for vbus-det _and_ we're driving vbus. + */ + if (data->cfg->type == sun6i_a31_phy && + data->vbus_power_supply && data->phys[0].regulator_on) + return true; + + return false; +} + +static int sun4i_usb_phy_power_on(struct phy *_phy) +{ + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); + int ret; + + if (!phy->vbus || phy->regulator_on) + return 0; + + /* For phy0 only turn on Vbus if we don't have an ext. Vbus */ + if (phy->index == 0 && sun4i_usb_phy0_have_vbus_det(data) && + data->vbus_det) { + dev_warn(&_phy->dev, "External vbus detected, not enabling our own vbus\n"); + return 0; + } + + ret = regulator_enable(phy->vbus); + if (ret) + return ret; + + phy->regulator_on = true; + + /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */ + if (phy->index == 0 && sun4i_usb_phy0_poll(data)) + mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); + + return 0; +} + +static int sun4i_usb_phy_power_off(struct phy *_phy) +{ + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); + + if (!phy->vbus || !phy->regulator_on) + return 0; + + regulator_disable(phy->vbus); + phy->regulator_on = false; + + /* + * phy0 vbus typically slowly discharges, sometimes this causes the + * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan. + */ + if (phy->index == 0 && !sun4i_usb_phy0_poll(data)) + mod_delayed_work(system_wq, &data->detect, POLL_TIME); + + return 0; +} + +static int sun4i_usb_phy_set_mode(struct phy *_phy, enum phy_mode mode) +{ + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); + struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); + int new_mode; + + if (phy->index != 0) + return -EINVAL; + + switch (mode) { + case PHY_MODE_USB_HOST: + new_mode = USB_DR_MODE_HOST; + break; + case PHY_MODE_USB_DEVICE: + new_mode = USB_DR_MODE_PERIPHERAL; + break; + case PHY_MODE_USB_OTG: + new_mode = USB_DR_MODE_OTG; + break; + default: + return -EINVAL; + } + + if (new_mode != data->dr_mode) { + dev_info(&_phy->dev, "Changing dr_mode to %d\n", new_mode); + data->dr_mode = new_mode; + } + + data->id_det = -1; /* Force reprocessing of id */ + data->force_session_end = true; + queue_delayed_work(system_wq, &data->detect, 0); + + return 0; +} + +void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled) +{ + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); + + sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2); +} +EXPORT_SYMBOL_GPL(sun4i_usb_phy_set_squelch_detect); + +static const struct phy_ops sun4i_usb_phy_ops = { + .init = sun4i_usb_phy_init, + .exit = sun4i_usb_phy_exit, + .power_on = sun4i_usb_phy_power_on, + .power_off = sun4i_usb_phy_power_off, + .set_mode = sun4i_usb_phy_set_mode, + .owner = THIS_MODULE, +}; + +static void sun4i_usb_phy0_reroute(struct sun4i_usb_phy_data *data, int id_det) +{ + u32 regval; + + regval = readl(data->base + REG_PHY_OTGCTL); + if (id_det == 0) { + /* Host mode. Route phy0 to EHCI/OHCI */ + regval &= ~OTGCTL_ROUTE_MUSB; + } else { + /* Peripheral mode. Route phy0 to MUSB */ + regval |= OTGCTL_ROUTE_MUSB; + } + writel(regval, data->base + REG_PHY_OTGCTL); +} + +static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) +{ + struct sun4i_usb_phy_data *data = + container_of(work, struct sun4i_usb_phy_data, detect.work); + struct phy *phy0 = data->phys[0].phy; + bool force_session_end, id_notify = false, vbus_notify = false; + int id_det, vbus_det; + + if (phy0 == NULL) + return; + + id_det = sun4i_usb_phy0_get_id_det(data); + vbus_det = sun4i_usb_phy0_get_vbus_det(data); + + mutex_lock(&phy0->mutex); + + if (!data->phy0_init) { + mutex_unlock(&phy0->mutex); + return; + } + + force_session_end = data->force_session_end; + data->force_session_end = false; + + if (id_det != data->id_det) { + /* id-change, force session end if we've no vbus detection */ + if (data->dr_mode == USB_DR_MODE_OTG && + !sun4i_usb_phy0_have_vbus_det(data)) + force_session_end = true; + + /* When entering host mode (id = 0) force end the session now */ + if (force_session_end && id_det == 0) { + sun4i_usb_phy0_set_vbus_detect(phy0, 0); + msleep(200); + sun4i_usb_phy0_set_vbus_detect(phy0, 1); + } + sun4i_usb_phy0_set_id_detect(phy0, id_det); + data->id_det = id_det; + id_notify = true; + } + + if (vbus_det != data->vbus_det) { + sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det); + data->vbus_det = vbus_det; + vbus_notify = true; + } + + mutex_unlock(&phy0->mutex); + + if (id_notify) { + extcon_set_state_sync(data->extcon, EXTCON_USB_HOST, + !id_det); + /* When leaving host mode force end the session here */ + if (force_session_end && id_det == 1) { + mutex_lock(&phy0->mutex); + sun4i_usb_phy0_set_vbus_detect(phy0, 0); + msleep(1000); + sun4i_usb_phy0_set_vbus_detect(phy0, 1); + mutex_unlock(&phy0->mutex); + } + + /* Re-route PHY0 if necessary */ + if (data->cfg->phy0_dual_route) + sun4i_usb_phy0_reroute(data, id_det); + } + + if (vbus_notify) + extcon_set_state_sync(data->extcon, EXTCON_USB, vbus_det); + + if (sun4i_usb_phy0_poll(data)) + queue_delayed_work(system_wq, &data->detect, POLL_TIME); +} + +static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id) +{ + struct sun4i_usb_phy_data *data = dev_id; + + /* vbus or id changed, let the pins settle and then scan them */ + mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); + + return IRQ_HANDLED; +} + +static int sun4i_usb_phy0_vbus_notify(struct notifier_block *nb, + unsigned long val, void *v) +{ + struct sun4i_usb_phy_data *data = + container_of(nb, struct sun4i_usb_phy_data, vbus_power_nb); + struct power_supply *psy = v; + + /* Properties on the vbus_power_supply changed, scan vbus_det */ + if (val == PSY_EVENT_PROP_CHANGED && psy == data->vbus_power_supply) + mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); + + return NOTIFY_OK; +} + +static struct phy *sun4i_usb_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); + + if (args->args[0] >= data->cfg->num_phys) + return ERR_PTR(-ENODEV); + + return data->phys[args->args[0]].phy; +} + +static int sun4i_usb_phy_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); + + if (data->vbus_power_nb_registered) + power_supply_unreg_notifier(&data->vbus_power_nb); + if (data->id_det_irq > 0) + devm_free_irq(dev, data->id_det_irq, data); + if (data->vbus_det_irq > 0) + devm_free_irq(dev, data->vbus_det_irq, data); + + cancel_delayed_work_sync(&data->detect); + + return 0; +} + +static const unsigned int sun4i_usb_phy0_cable[] = { + EXTCON_USB, + EXTCON_USB_HOST, + EXTCON_NONE, +}; + +static int sun4i_usb_phy_probe(struct platform_device *pdev) +{ + struct sun4i_usb_phy_data *data; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct phy_provider *phy_provider; + struct resource *res; + int i, ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + spin_lock_init(&data->reg_lock); + INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan); + dev_set_drvdata(dev, data); + data->cfg = of_device_get_match_data(dev); + if (!data->cfg) + return -EINVAL; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl"); + data->base = devm_ioremap_resource(dev, res); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + data->id_det_gpio = devm_gpiod_get_optional(dev, "usb0_id_det", + GPIOD_IN); + if (IS_ERR(data->id_det_gpio)) + return PTR_ERR(data->id_det_gpio); + + data->vbus_det_gpio = devm_gpiod_get_optional(dev, "usb0_vbus_det", + GPIOD_IN); + if (IS_ERR(data->vbus_det_gpio)) + return PTR_ERR(data->vbus_det_gpio); + + if (of_find_property(np, "usb0_vbus_power-supply", NULL)) { + data->vbus_power_supply = devm_power_supply_get_by_phandle(dev, + "usb0_vbus_power-supply"); + if (IS_ERR(data->vbus_power_supply)) + return PTR_ERR(data->vbus_power_supply); + + if (!data->vbus_power_supply) + return -EPROBE_DEFER; + } + + data->dr_mode = of_usb_get_dr_mode_by_phy(np, 0); + + data->extcon = devm_extcon_dev_allocate(dev, sun4i_usb_phy0_cable); + if (IS_ERR(data->extcon)) + return PTR_ERR(data->extcon); + + ret = devm_extcon_dev_register(dev, data->extcon); + if (ret) { + dev_err(dev, "failed to register extcon: %d\n", ret); + return ret; + } + + for (i = 0; i < data->cfg->num_phys; i++) { + struct sun4i_usb_phy *phy = data->phys + i; + char name[16]; + + snprintf(name, sizeof(name), "usb%d_vbus", i); + phy->vbus = devm_regulator_get_optional(dev, name); + if (IS_ERR(phy->vbus)) { + if (PTR_ERR(phy->vbus) == -EPROBE_DEFER) + return -EPROBE_DEFER; + phy->vbus = NULL; + } + + if (data->cfg->dedicated_clocks) + snprintf(name, sizeof(name), "usb%d_phy", i); + else + strlcpy(name, "usb_phy", sizeof(name)); + + phy->clk = devm_clk_get(dev, name); + if (IS_ERR(phy->clk)) { + dev_err(dev, "failed to get clock %s\n", name); + return PTR_ERR(phy->clk); + } + + snprintf(name, sizeof(name), "usb%d_reset", i); + phy->reset = devm_reset_control_get(dev, name); + if (IS_ERR(phy->reset)) { + dev_err(dev, "failed to get reset %s\n", name); + return PTR_ERR(phy->reset); + } + + if (i || data->cfg->phy0_dual_route) { /* No pmu for musb */ + snprintf(name, sizeof(name), "pmu%d", i); + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, name); + phy->pmu = devm_ioremap_resource(dev, res); + if (IS_ERR(phy->pmu)) + return PTR_ERR(phy->pmu); + } + + phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops); + if (IS_ERR(phy->phy)) { + dev_err(dev, "failed to create PHY %d\n", i); + return PTR_ERR(phy->phy); + } + + phy->index = i; + phy_set_drvdata(phy->phy, &data->phys[i]); + } + + data->id_det_irq = gpiod_to_irq(data->id_det_gpio); + if (data->id_det_irq > 0) { + ret = devm_request_irq(dev, data->id_det_irq, + sun4i_usb_phy0_id_vbus_det_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "usb0-id-det", data); + if (ret) { + dev_err(dev, "Err requesting id-det-irq: %d\n", ret); + return ret; + } + } + + data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio); + if (data->vbus_det_irq > 0) { + ret = devm_request_irq(dev, data->vbus_det_irq, + sun4i_usb_phy0_id_vbus_det_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "usb0-vbus-det", data); + if (ret) { + dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret); + data->vbus_det_irq = -1; + sun4i_usb_phy_remove(pdev); /* Stop detect work */ + return ret; + } + } + + if (data->vbus_power_supply) { + data->vbus_power_nb.notifier_call = sun4i_usb_phy0_vbus_notify; + data->vbus_power_nb.priority = 0; + ret = power_supply_reg_notifier(&data->vbus_power_nb); + if (ret) { + sun4i_usb_phy_remove(pdev); /* Stop detect work */ + return ret; + } + data->vbus_power_nb_registered = true; + } + + phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate); + if (IS_ERR(phy_provider)) { + sun4i_usb_phy_remove(pdev); /* Stop detect work */ + return PTR_ERR(phy_provider); + } + + return 0; +} + +static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = { + .num_phys = 3, + .type = sun4i_a10_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A10, + .dedicated_clocks = false, + .enable_pmu_unk1 = false, +}; + +static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { + .num_phys = 2, + .type = sun4i_a10_phy, + .disc_thresh = 2, + .phyctl_offset = REG_PHYCTL_A10, + .dedicated_clocks = false, + .enable_pmu_unk1 = false, +}; + +static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { + .num_phys = 3, + .type = sun6i_a31_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A10, + .dedicated_clocks = true, + .enable_pmu_unk1 = false, +}; + +static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { + .num_phys = 3, + .type = sun4i_a10_phy, + .disc_thresh = 2, + .phyctl_offset = REG_PHYCTL_A10, + .dedicated_clocks = false, + .enable_pmu_unk1 = false, +}; + +static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { + .num_phys = 2, + .type = sun4i_a10_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A10, + .dedicated_clocks = true, + .enable_pmu_unk1 = false, +}; + +static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { + .num_phys = 2, + .type = sun8i_a33_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A33, + .dedicated_clocks = true, + .enable_pmu_unk1 = false, +}; + +static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { + .num_phys = 4, + .type = sun8i_h3_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A33, + .dedicated_clocks = true, + .enable_pmu_unk1 = true, + .phy0_dual_route = true, +}; + +static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { + .num_phys = 1, + .type = sun8i_v3s_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A33, + .dedicated_clocks = true, + .enable_pmu_unk1 = true, +}; + +static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { + .num_phys = 2, + .type = sun50i_a64_phy, + .disc_thresh = 3, + .phyctl_offset = REG_PHYCTL_A33, + .dedicated_clocks = true, + .enable_pmu_unk1 = true, + .phy0_dual_route = true, +}; + +static const struct of_device_id sun4i_usb_phy_of_match[] = { + { .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg }, + { .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg }, + { .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg }, + { .compatible = "allwinner,sun7i-a20-usb-phy", .data = &sun7i_a20_cfg }, + { .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg }, + { .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg }, + { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg }, + { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg }, + { .compatible = "allwinner,sun50i-a64-usb-phy", + .data = &sun50i_a64_cfg}, + { }, +}; +MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match); + +static struct platform_driver sun4i_usb_phy_driver = { + .probe = sun4i_usb_phy_probe, + .remove = sun4i_usb_phy_remove, + .driver = { + .of_match_table = sun4i_usb_phy_of_match, + .name = "sun4i-usb-phy", + } +}; +module_platform_driver(sun4i_usb_phy_driver); + +MODULE_DESCRIPTION("Allwinner sun4i USB phy driver"); +MODULE_AUTHOR("Hans de Goede "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/allwinner/phy-sun9i-usb.c b/drivers/phy/allwinner/phy-sun9i-usb.c new file mode 100644 index 000000000000..28fce4bce638 --- /dev/null +++ b/drivers/phy/allwinner/phy-sun9i-usb.c @@ -0,0 +1,202 @@ +/* + * Allwinner sun9i USB phy driver + * + * Copyright (C) 2014-2015 Chen-Yu Tsai + * + * Based on phy-sun4i-usb.c from + * Hans de Goede + * + * and code from + * Allwinner Technology Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SUNXI_AHB_INCR16_BURST_EN BIT(11) +#define SUNXI_AHB_INCR8_BURST_EN BIT(10) +#define SUNXI_AHB_INCR4_BURST_EN BIT(9) +#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) +#define SUNXI_ULPI_BYPASS_EN BIT(0) + +/* usb1 HSIC specific bits */ +#define SUNXI_EHCI_HS_FORCE BIT(20) +#define SUNXI_HSIC_CONNECT_DET BIT(17) +#define SUNXI_HSIC_CONNECT_INT BIT(16) +#define SUNXI_HSIC BIT(1) + +struct sun9i_usb_phy { + struct phy *phy; + void __iomem *pmu; + struct reset_control *reset; + struct clk *clk; + struct clk *hsic_clk; + enum usb_phy_interface type; +}; + +static void sun9i_usb_phy_passby(struct sun9i_usb_phy *phy, int enable) +{ + u32 bits, reg_value; + + bits = SUNXI_AHB_INCR16_BURST_EN | SUNXI_AHB_INCR8_BURST_EN | + SUNXI_AHB_INCR4_BURST_EN | SUNXI_AHB_INCRX_ALIGN_EN | + SUNXI_ULPI_BYPASS_EN; + + if (phy->type == USBPHY_INTERFACE_MODE_HSIC) + bits |= SUNXI_HSIC | SUNXI_EHCI_HS_FORCE | + SUNXI_HSIC_CONNECT_DET | SUNXI_HSIC_CONNECT_INT; + + reg_value = readl(phy->pmu); + + if (enable) + reg_value |= bits; + else + reg_value &= ~bits; + + writel(reg_value, phy->pmu); +} + +static int sun9i_usb_phy_init(struct phy *_phy) +{ + struct sun9i_usb_phy *phy = phy_get_drvdata(_phy); + int ret; + + ret = clk_prepare_enable(phy->clk); + if (ret) + goto err_clk; + + ret = clk_prepare_enable(phy->hsic_clk); + if (ret) + goto err_hsic_clk; + + ret = reset_control_deassert(phy->reset); + if (ret) + goto err_reset; + + sun9i_usb_phy_passby(phy, 1); + return 0; + +err_reset: + clk_disable_unprepare(phy->hsic_clk); + +err_hsic_clk: + clk_disable_unprepare(phy->clk); + +err_clk: + return ret; +} + +static int sun9i_usb_phy_exit(struct phy *_phy) +{ + struct sun9i_usb_phy *phy = phy_get_drvdata(_phy); + + sun9i_usb_phy_passby(phy, 0); + reset_control_assert(phy->reset); + clk_disable_unprepare(phy->hsic_clk); + clk_disable_unprepare(phy->clk); + + return 0; +} + +static const struct phy_ops sun9i_usb_phy_ops = { + .init = sun9i_usb_phy_init, + .exit = sun9i_usb_phy_exit, + .owner = THIS_MODULE, +}; + +static int sun9i_usb_phy_probe(struct platform_device *pdev) +{ + struct sun9i_usb_phy *phy; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct phy_provider *phy_provider; + struct resource *res; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->type = of_usb_get_phy_mode(np); + if (phy->type == USBPHY_INTERFACE_MODE_HSIC) { + phy->clk = devm_clk_get(dev, "hsic_480M"); + if (IS_ERR(phy->clk)) { + dev_err(dev, "failed to get hsic_480M clock\n"); + return PTR_ERR(phy->clk); + } + + phy->hsic_clk = devm_clk_get(dev, "hsic_12M"); + if (IS_ERR(phy->hsic_clk)) { + dev_err(dev, "failed to get hsic_12M clock\n"); + return PTR_ERR(phy->hsic_clk); + } + + phy->reset = devm_reset_control_get(dev, "hsic"); + if (IS_ERR(phy->reset)) { + dev_err(dev, "failed to get reset control\n"); + return PTR_ERR(phy->reset); + } + } else { + phy->clk = devm_clk_get(dev, "phy"); + if (IS_ERR(phy->clk)) { + dev_err(dev, "failed to get phy clock\n"); + return PTR_ERR(phy->clk); + } + + phy->reset = devm_reset_control_get(dev, "phy"); + if (IS_ERR(phy->reset)) { + dev_err(dev, "failed to get reset control\n"); + return PTR_ERR(phy->reset); + } + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy->pmu = devm_ioremap_resource(dev, res); + if (IS_ERR(phy->pmu)) + return PTR_ERR(phy->pmu); + + phy->phy = devm_phy_create(dev, NULL, &sun9i_usb_phy_ops); + if (IS_ERR(phy->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy->phy); + } + + phy_set_drvdata(phy->phy, phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id sun9i_usb_phy_of_match[] = { + { .compatible = "allwinner,sun9i-a80-usb-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, sun9i_usb_phy_of_match); + +static struct platform_driver sun9i_usb_phy_driver = { + .probe = sun9i_usb_phy_probe, + .driver = { + .of_match_table = sun9i_usb_phy_of_match, + .name = "sun9i-usb-phy", + } +}; +module_platform_driver(sun9i_usb_phy_driver); + +MODULE_DESCRIPTION("Allwinner sun9i USB phy driver"); +MODULE_AUTHOR("Chen-Yu Tsai "); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/amlogic/Kconfig b/drivers/phy/amlogic/Kconfig new file mode 100644 index 000000000000..edcd5b65179f --- /dev/null +++ b/drivers/phy/amlogic/Kconfig @@ -0,0 +1,14 @@ +# +# Phy drivers for Amlogic platforms +# +config PHY_MESON8B_USB2 + tristate "Meson8b and GXBB USB2 PHY driver" + default ARCH_MESON + depends on OF && (ARCH_MESON || COMPILE_TEST) + depends on USB_SUPPORT + select USB_COMMON + select GENERIC_PHY + help + Enable this to support the Meson USB2 PHYs found in Meson8b + and GXBB SoCs. + If unsure, say N. diff --git a/drivers/phy/amlogic/Makefile b/drivers/phy/amlogic/Makefile new file mode 100644 index 000000000000..47b6eecc3864 --- /dev/null +++ b/drivers/phy/amlogic/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o diff --git a/drivers/phy/amlogic/phy-meson8b-usb2.c b/drivers/phy/amlogic/phy-meson8b-usb2.c new file mode 100644 index 000000000000..30f56a6a411f --- /dev/null +++ b/drivers/phy/amlogic/phy-meson8b-usb2.c @@ -0,0 +1,286 @@ +/* + * Meson8b and GXBB USB2 PHY driver + * + * Copyright (C) 2016 Martin Blumenstingl + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_CONFIG 0x00 + #define REG_CONFIG_CLK_EN BIT(0) + #define REG_CONFIG_CLK_SEL_MASK GENMASK(3, 1) + #define REG_CONFIG_CLK_DIV_MASK GENMASK(10, 4) + #define REG_CONFIG_CLK_32k_ALTSEL BIT(15) + #define REG_CONFIG_TEST_TRIG BIT(31) + +#define REG_CTRL 0x04 + #define REG_CTRL_SOFT_PRST BIT(0) + #define REG_CTRL_SOFT_HRESET BIT(1) + #define REG_CTRL_SS_SCALEDOWN_MODE_MASK GENMASK(3, 2) + #define REG_CTRL_CLK_DET_RST BIT(4) + #define REG_CTRL_INTR_SEL BIT(5) + #define REG_CTRL_CLK_DETECTED BIT(8) + #define REG_CTRL_SOF_SENT_RCVD_TGL BIT(9) + #define REG_CTRL_SOF_TOGGLE_OUT BIT(10) + #define REG_CTRL_POWER_ON_RESET BIT(15) + #define REG_CTRL_SLEEPM BIT(16) + #define REG_CTRL_TX_BITSTUFF_ENN_H BIT(17) + #define REG_CTRL_TX_BITSTUFF_ENN BIT(18) + #define REG_CTRL_COMMON_ON BIT(19) + #define REG_CTRL_REF_CLK_SEL_MASK GENMASK(21, 20) + #define REG_CTRL_REF_CLK_SEL_SHIFT 20 + #define REG_CTRL_FSEL_MASK GENMASK(24, 22) + #define REG_CTRL_FSEL_SHIFT 22 + #define REG_CTRL_PORT_RESET BIT(25) + #define REG_CTRL_THREAD_ID_MASK GENMASK(31, 26) + +#define REG_ENDP_INTR 0x08 + +/* bits [31:26], [24:21] and [15:3] seem to be read-only */ +#define REG_ADP_BC 0x0c + #define REG_ADP_BC_VBUS_VLD_EXT_SEL BIT(0) + #define REG_ADP_BC_VBUS_VLD_EXT BIT(1) + #define REG_ADP_BC_OTG_DISABLE BIT(2) + #define REG_ADP_BC_ID_PULLUP BIT(3) + #define REG_ADP_BC_DRV_VBUS BIT(4) + #define REG_ADP_BC_ADP_PRB_EN BIT(5) + #define REG_ADP_BC_ADP_DISCHARGE BIT(6) + #define REG_ADP_BC_ADP_CHARGE BIT(7) + #define REG_ADP_BC_SESS_END BIT(8) + #define REG_ADP_BC_DEVICE_SESS_VLD BIT(9) + #define REG_ADP_BC_B_VALID BIT(10) + #define REG_ADP_BC_A_VALID BIT(11) + #define REG_ADP_BC_ID_DIG BIT(12) + #define REG_ADP_BC_VBUS_VALID BIT(13) + #define REG_ADP_BC_ADP_PROBE BIT(14) + #define REG_ADP_BC_ADP_SENSE BIT(15) + #define REG_ADP_BC_ACA_ENABLE BIT(16) + #define REG_ADP_BC_DCD_ENABLE BIT(17) + #define REG_ADP_BC_VDAT_DET_EN_B BIT(18) + #define REG_ADP_BC_VDAT_SRC_EN_B BIT(19) + #define REG_ADP_BC_CHARGE_SEL BIT(20) + #define REG_ADP_BC_CHARGE_DETECT BIT(21) + #define REG_ADP_BC_ACA_PIN_RANGE_C BIT(22) + #define REG_ADP_BC_ACA_PIN_RANGE_B BIT(23) + #define REG_ADP_BC_ACA_PIN_RANGE_A BIT(24) + #define REG_ADP_BC_ACA_PIN_GND BIT(25) + #define REG_ADP_BC_ACA_PIN_FLOAT BIT(26) + +#define REG_DBG_UART 0x10 + +#define REG_TEST 0x14 + #define REG_TEST_DATA_IN_MASK GENMASK(3, 0) + #define REG_TEST_EN_MASK GENMASK(7, 4) + #define REG_TEST_ADDR_MASK GENMASK(11, 8) + #define REG_TEST_DATA_OUT_SEL BIT(12) + #define REG_TEST_CLK BIT(13) + #define REG_TEST_VA_TEST_EN_B_MASK GENMASK(15, 14) + #define REG_TEST_DATA_OUT_MASK GENMASK(19, 16) + #define REG_TEST_DISABLE_ID_PULLUP BIT(20) + +#define REG_TUNE 0x18 + #define REG_TUNE_TX_RES_TUNE_MASK GENMASK(1, 0) + #define REG_TUNE_TX_HSXV_TUNE_MASK GENMASK(3, 2) + #define REG_TUNE_TX_VREF_TUNE_MASK GENMASK(7, 4) + #define REG_TUNE_TX_RISE_TUNE_MASK GENMASK(9, 8) + #define REG_TUNE_TX_PREEMP_PULSE_TUNE BIT(10) + #define REG_TUNE_TX_PREEMP_AMP_TUNE_MASK GENMASK(12, 11) + #define REG_TUNE_TX_FSLS_TUNE_MASK GENMASK(16, 13) + #define REG_TUNE_SQRX_TUNE_MASK GENMASK(19, 17) + #define REG_TUNE_OTG_TUNE GENMASK(22, 20) + #define REG_TUNE_COMP_DIS_TUNE GENMASK(25, 23) + #define REG_TUNE_HOST_DM_PULLDOWN BIT(26) + #define REG_TUNE_HOST_DP_PULLDOWN BIT(27) + +#define RESET_COMPLETE_TIME 500 +#define ACA_ENABLE_COMPLETE_TIME 50 + +struct phy_meson8b_usb2_priv { + void __iomem *regs; + enum usb_dr_mode dr_mode; + struct clk *clk_usb_general; + struct clk *clk_usb; + struct reset_control *reset; +}; + +static u32 phy_meson8b_usb2_read(struct phy_meson8b_usb2_priv *phy_priv, + u32 reg) +{ + return readl(phy_priv->regs + reg); +} + +static void phy_meson8b_usb2_mask_bits(struct phy_meson8b_usb2_priv *phy_priv, + u32 reg, u32 mask, u32 value) +{ + u32 data; + + data = phy_meson8b_usb2_read(phy_priv, reg); + data &= ~mask; + data |= (value & mask); + + writel(data, phy_priv->regs + reg); +} + +static int phy_meson8b_usb2_power_on(struct phy *phy) +{ + struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); + int ret; + + if (!IS_ERR_OR_NULL(priv->reset)) { + ret = reset_control_reset(priv->reset); + if (ret) { + dev_err(&phy->dev, "Failed to trigger USB reset\n"); + return ret; + } + } + + ret = clk_prepare_enable(priv->clk_usb_general); + if (ret) { + dev_err(&phy->dev, "Failed to enable USB general clock\n"); + return ret; + } + + ret = clk_prepare_enable(priv->clk_usb); + if (ret) { + dev_err(&phy->dev, "Failed to enable USB DDR clock\n"); + clk_disable_unprepare(priv->clk_usb_general); + return ret; + } + + phy_meson8b_usb2_mask_bits(priv, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL, + REG_CONFIG_CLK_32k_ALTSEL); + + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK, + 0x2 << REG_CTRL_REF_CLK_SEL_SHIFT); + + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_FSEL_MASK, + 0x5 << REG_CTRL_FSEL_SHIFT); + + /* reset the PHY */ + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, + REG_CTRL_POWER_ON_RESET); + udelay(RESET_COMPLETE_TIME); + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0); + udelay(RESET_COMPLETE_TIME); + + phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT, + REG_CTRL_SOF_TOGGLE_OUT); + + if (priv->dr_mode == USB_DR_MODE_HOST) { + phy_meson8b_usb2_mask_bits(priv, REG_ADP_BC, + REG_ADP_BC_ACA_ENABLE, + REG_ADP_BC_ACA_ENABLE); + + udelay(ACA_ENABLE_COMPLETE_TIME); + + if (phy_meson8b_usb2_read(priv, REG_ADP_BC) & + REG_ADP_BC_ACA_PIN_FLOAT) { + dev_warn(&phy->dev, "USB ID detect failed!\n"); + clk_disable_unprepare(priv->clk_usb); + clk_disable_unprepare(priv->clk_usb_general); + return -EINVAL; + } + } + + return 0; +} + +static int phy_meson8b_usb2_power_off(struct phy *phy) +{ + struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); + + clk_disable_unprepare(priv->clk_usb); + clk_disable_unprepare(priv->clk_usb_general); + + return 0; +} + +static const struct phy_ops phy_meson8b_usb2_ops = { + .power_on = phy_meson8b_usb2_power_on, + .power_off = phy_meson8b_usb2_power_off, + .owner = THIS_MODULE, +}; + +static int phy_meson8b_usb2_probe(struct platform_device *pdev) +{ + struct phy_meson8b_usb2_priv *priv; + struct resource *res; + struct phy *phy; + struct phy_provider *phy_provider; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + priv->clk_usb_general = devm_clk_get(&pdev->dev, "usb_general"); + if (IS_ERR(priv->clk_usb_general)) + return PTR_ERR(priv->clk_usb_general); + + priv->clk_usb = devm_clk_get(&pdev->dev, "usb"); + if (IS_ERR(priv->clk_usb)) + return PTR_ERR(priv->clk_usb); + + priv->reset = devm_reset_control_get_optional_shared(&pdev->dev, NULL); + if (PTR_ERR(priv->reset) == -EPROBE_DEFER) + return PTR_ERR(priv->reset); + + priv->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1); + if (priv->dr_mode == USB_DR_MODE_UNKNOWN) { + dev_err(&pdev->dev, + "missing dual role configuration of the controller\n"); + return -EINVAL; + } + + phy = devm_phy_create(&pdev->dev, NULL, &phy_meson8b_usb2_ops); + if (IS_ERR(phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, priv); + + phy_provider = + devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id phy_meson8b_usb2_of_match[] = { + { .compatible = "amlogic,meson8b-usb2-phy", }, + { .compatible = "amlogic,meson-gxbb-usb2-phy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, phy_meson8b_usb2_of_match); + +static struct platform_driver phy_meson8b_usb2_driver = { + .probe = phy_meson8b_usb2_probe, + .driver = { + .name = "phy-meson-usb2", + .of_match_table = phy_meson8b_usb2_of_match, + }, +}; +module_platform_driver(phy_meson8b_usb2_driver); + +MODULE_AUTHOR("Martin Blumenstingl "); +MODULE_DESCRIPTION("Meson8b and GXBB USB2 PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig new file mode 100644 index 000000000000..d2d99023ec50 --- /dev/null +++ b/drivers/phy/broadcom/Kconfig @@ -0,0 +1,55 @@ +# +# Phy drivers for Broadcom platforms +# +config PHY_CYGNUS_PCIE + tristate "Broadcom Cygnus PCIe PHY driver" + depends on OF && (ARCH_BCM_CYGNUS || COMPILE_TEST) + select GENERIC_PHY + default ARCH_BCM_CYGNUS + help + Enable this to support the Broadcom Cygnus PCIe PHY. + If unsure, say N. + +config BCM_KONA_USB2_PHY + tristate "Broadcom Kona USB2 PHY Driver" + depends on HAS_IOMEM + select GENERIC_PHY + help + Enable this to support the Broadcom Kona USB 2.0 PHY. + +config PHY_BCM_NS_USB2 + tristate "Broadcom Northstar USB 2.0 PHY Driver" + depends on ARCH_BCM_IPROC || COMPILE_TEST + depends on HAS_IOMEM && OF + select GENERIC_PHY + help + Enable this to support Broadcom USB 2.0 PHY connected to the USB + controller on Northstar family. + +config PHY_BCM_NS_USB3 + tristate "Broadcom Northstar USB 3.0 PHY Driver" + depends on ARCH_BCM_IPROC || COMPILE_TEST + depends on HAS_IOMEM && OF + select GENERIC_PHY + help + Enable this to support Broadcom USB 3.0 PHY connected to the USB + controller on Northstar family. + +config PHY_NS2_PCIE + tristate "Broadcom Northstar2 PCIe PHY driver" + depends on OF && MDIO_BUS_MUX_BCM_IPROC + select GENERIC_PHY + default ARCH_BCM_IPROC + help + Enable this to support the Broadcom Northstar2 PCIe PHY. + If unsure, say N. + +config PHY_BRCM_SATA + tristate "Broadcom SATA PHY driver" + depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || COMPILE_TEST + depends on OF + select GENERIC_PHY + default ARCH_BCM_IPROC + help + Enable this to support the Broadcom SATA PHY. + If unsure, say N. diff --git a/drivers/phy/broadcom/Makefile b/drivers/phy/broadcom/Makefile new file mode 100644 index 000000000000..357a7d16529f --- /dev/null +++ b/drivers/phy/broadcom/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o +obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o +obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o +obj-$(CONFIG_PHY_BCM_NS_USB3) += phy-bcm-ns-usb3.o +obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o +obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o diff --git a/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c b/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c new file mode 100644 index 000000000000..0f4ac5d63cff --- /dev/null +++ b/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#define PCIE_CFG_OFFSET 0x00 +#define PCIE1_PHY_IDDQ_SHIFT 10 +#define PCIE0_PHY_IDDQ_SHIFT 2 + +enum cygnus_pcie_phy_id { + CYGNUS_PHY_PCIE0 = 0, + CYGNUS_PHY_PCIE1, + MAX_NUM_PHYS, +}; + +struct cygnus_pcie_phy_core; + +/** + * struct cygnus_pcie_phy - Cygnus PCIe PHY device + * @core: pointer to the Cygnus PCIe PHY core control + * @id: internal ID to identify the Cygnus PCIe PHY + * @phy: pointer to the kernel PHY device + */ +struct cygnus_pcie_phy { + struct cygnus_pcie_phy_core *core; + enum cygnus_pcie_phy_id id; + struct phy *phy; +}; + +/** + * struct cygnus_pcie_phy_core - Cygnus PCIe PHY core control + * @dev: pointer to device + * @base: base register + * @lock: mutex to protect access to individual PHYs + * @phys: pointer to Cygnus PHY device + */ +struct cygnus_pcie_phy_core { + struct device *dev; + void __iomem *base; + struct mutex lock; + struct cygnus_pcie_phy phys[MAX_NUM_PHYS]; +}; + +static int cygnus_pcie_power_config(struct cygnus_pcie_phy *phy, bool enable) +{ + struct cygnus_pcie_phy_core *core = phy->core; + unsigned shift; + u32 val; + + mutex_lock(&core->lock); + + switch (phy->id) { + case CYGNUS_PHY_PCIE0: + shift = PCIE0_PHY_IDDQ_SHIFT; + break; + + case CYGNUS_PHY_PCIE1: + shift = PCIE1_PHY_IDDQ_SHIFT; + break; + + default: + mutex_unlock(&core->lock); + dev_err(core->dev, "PCIe PHY %d invalid\n", phy->id); + return -EINVAL; + } + + if (enable) { + val = readl(core->base + PCIE_CFG_OFFSET); + val &= ~BIT(shift); + writel(val, core->base + PCIE_CFG_OFFSET); + /* + * Wait 50 ms for the PCIe Serdes to stabilize after the analog + * front end is brought up + */ + msleep(50); + } else { + val = readl(core->base + PCIE_CFG_OFFSET); + val |= BIT(shift); + writel(val, core->base + PCIE_CFG_OFFSET); + } + + mutex_unlock(&core->lock); + dev_dbg(core->dev, "PCIe PHY %d %s\n", phy->id, + enable ? "enabled" : "disabled"); + return 0; +} + +static int cygnus_pcie_phy_power_on(struct phy *p) +{ + struct cygnus_pcie_phy *phy = phy_get_drvdata(p); + + return cygnus_pcie_power_config(phy, true); +} + +static int cygnus_pcie_phy_power_off(struct phy *p) +{ + struct cygnus_pcie_phy *phy = phy_get_drvdata(p); + + return cygnus_pcie_power_config(phy, false); +} + +static const struct phy_ops cygnus_pcie_phy_ops = { + .power_on = cygnus_pcie_phy_power_on, + .power_off = cygnus_pcie_phy_power_off, + .owner = THIS_MODULE, +}; + +static int cygnus_pcie_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node, *child; + struct cygnus_pcie_phy_core *core; + struct phy_provider *provider; + struct resource *res; + unsigned cnt = 0; + int ret; + + if (of_get_child_count(node) == 0) { + dev_err(dev, "PHY no child node\n"); + return -ENODEV; + } + + core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); + if (!core) + return -ENOMEM; + + core->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + core->base = devm_ioremap_resource(dev, res); + if (IS_ERR(core->base)) + return PTR_ERR(core->base); + + mutex_init(&core->lock); + + for_each_available_child_of_node(node, child) { + unsigned int id; + struct cygnus_pcie_phy *p; + + if (of_property_read_u32(child, "reg", &id)) { + dev_err(dev, "missing reg property for %s\n", + child->name); + ret = -EINVAL; + goto put_child; + } + + if (id >= MAX_NUM_PHYS) { + dev_err(dev, "invalid PHY id: %u\n", id); + ret = -EINVAL; + goto put_child; + } + + if (core->phys[id].phy) { + dev_err(dev, "duplicated PHY id: %u\n", id); + ret = -EINVAL; + goto put_child; + } + + p = &core->phys[id]; + p->phy = devm_phy_create(dev, child, &cygnus_pcie_phy_ops); + if (IS_ERR(p->phy)) { + dev_err(dev, "failed to create PHY\n"); + ret = PTR_ERR(p->phy); + goto put_child; + } + + p->core = core; + p->id = id; + phy_set_drvdata(p->phy, p); + cnt++; + } + + dev_set_drvdata(dev, core); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "failed to register PHY provider\n"); + return PTR_ERR(provider); + } + + dev_dbg(dev, "registered %u PCIe PHY(s)\n", cnt); + + return 0; +put_child: + of_node_put(child); + return ret; +} + +static const struct of_device_id cygnus_pcie_phy_match_table[] = { + { .compatible = "brcm,cygnus-pcie-phy" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cygnus_pcie_phy_match_table); + +static struct platform_driver cygnus_pcie_phy_driver = { + .driver = { + .name = "cygnus-pcie-phy", + .of_match_table = cygnus_pcie_phy_match_table, + }, + .probe = cygnus_pcie_phy_probe, +}; +module_platform_driver(cygnus_pcie_phy_driver); + +MODULE_AUTHOR("Ray Jui "); +MODULE_DESCRIPTION("Broadcom Cygnus PCIe PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/broadcom/phy-bcm-kona-usb2.c b/drivers/phy/broadcom/phy-bcm-kona-usb2.c new file mode 100644 index 000000000000..7b67fe49e30b --- /dev/null +++ b/drivers/phy/broadcom/phy-bcm-kona-usb2.c @@ -0,0 +1,155 @@ +/* + * phy-bcm-kona-usb2.c - Broadcom Kona USB2 Phy Driver + * + * Copyright (C) 2013 Linaro Limited + * Matt Porter + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define OTGCTL (0) +#define OTGCTL_OTGSTAT2 BIT(31) +#define OTGCTL_OTGSTAT1 BIT(30) +#define OTGCTL_PRST_N_SW BIT(11) +#define OTGCTL_HRESET_N BIT(10) +#define OTGCTL_UTMI_LINE_STATE1 BIT(9) +#define OTGCTL_UTMI_LINE_STATE0 BIT(8) + +#define P1CTL (8) +#define P1CTL_SOFT_RESET BIT(1) +#define P1CTL_NON_DRIVING BIT(0) + +struct bcm_kona_usb { + void __iomem *regs; +}; + +static void bcm_kona_usb_phy_power(struct bcm_kona_usb *phy, int on) +{ + u32 val; + + val = readl(phy->regs + OTGCTL); + if (on) { + /* Configure and power PHY */ + val &= ~(OTGCTL_OTGSTAT2 | OTGCTL_OTGSTAT1 | + OTGCTL_UTMI_LINE_STATE1 | OTGCTL_UTMI_LINE_STATE0); + val |= OTGCTL_PRST_N_SW | OTGCTL_HRESET_N; + } else { + val &= ~(OTGCTL_PRST_N_SW | OTGCTL_HRESET_N); + } + writel(val, phy->regs + OTGCTL); +} + +static int bcm_kona_usb_phy_init(struct phy *gphy) +{ + struct bcm_kona_usb *phy = phy_get_drvdata(gphy); + u32 val; + + /* Soft reset PHY */ + val = readl(phy->regs + P1CTL); + val &= ~P1CTL_NON_DRIVING; + val |= P1CTL_SOFT_RESET; + writel(val, phy->regs + P1CTL); + writel(val & ~P1CTL_SOFT_RESET, phy->regs + P1CTL); + /* Reset needs to be asserted for 2ms */ + mdelay(2); + writel(val | P1CTL_SOFT_RESET, phy->regs + P1CTL); + + return 0; +} + +static int bcm_kona_usb_phy_power_on(struct phy *gphy) +{ + struct bcm_kona_usb *phy = phy_get_drvdata(gphy); + + bcm_kona_usb_phy_power(phy, 1); + + return 0; +} + +static int bcm_kona_usb_phy_power_off(struct phy *gphy) +{ + struct bcm_kona_usb *phy = phy_get_drvdata(gphy); + + bcm_kona_usb_phy_power(phy, 0); + + return 0; +} + +static const struct phy_ops ops = { + .init = bcm_kona_usb_phy_init, + .power_on = bcm_kona_usb_phy_power_on, + .power_off = bcm_kona_usb_phy_power_off, + .owner = THIS_MODULE, +}; + +static int bcm_kona_usb2_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm_kona_usb *phy; + struct resource *res; + struct phy *gphy; + struct phy_provider *phy_provider; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(phy->regs)) + return PTR_ERR(phy->regs); + + platform_set_drvdata(pdev, phy); + + gphy = devm_phy_create(dev, NULL, &ops); + if (IS_ERR(gphy)) + return PTR_ERR(gphy); + + /* The Kona PHY supports an 8-bit wide UTMI interface */ + phy_set_bus_width(gphy, 8); + + phy_set_drvdata(gphy, phy); + + phy_provider = devm_of_phy_provider_register(dev, + of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id bcm_kona_usb2_dt_ids[] = { + { .compatible = "brcm,kona-usb2-phy" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, bcm_kona_usb2_dt_ids); + +static struct platform_driver bcm_kona_usb2_driver = { + .probe = bcm_kona_usb2_probe, + .driver = { + .name = "bcm-kona-usb2", + .of_match_table = bcm_kona_usb2_dt_ids, + }, +}; + +module_platform_driver(bcm_kona_usb2_driver); + +MODULE_ALIAS("platform:bcm-kona-usb2"); +MODULE_AUTHOR("Matt Porter "); +MODULE_DESCRIPTION("BCM Kona USB 2.0 PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb2.c b/drivers/phy/broadcom/phy-bcm-ns-usb2.c new file mode 100644 index 000000000000..58dff80e9386 --- /dev/null +++ b/drivers/phy/broadcom/phy-bcm-ns-usb2.c @@ -0,0 +1,137 @@ +/* + * Broadcom Northstar USB 2.0 PHY Driver + * + * Copyright (C) 2016 Rafał Miłecki + * + * 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 + +struct bcm_ns_usb2 { + struct device *dev; + struct clk *ref_clk; + struct phy *phy; + void __iomem *dmu; +}; + +static int bcm_ns_usb2_phy_init(struct phy *phy) +{ + struct bcm_ns_usb2 *usb2 = phy_get_drvdata(phy); + struct device *dev = usb2->dev; + void __iomem *dmu = usb2->dmu; + u32 ref_clk_rate, usb2ctl, usb_pll_ndiv, usb_pll_pdiv; + int err = 0; + + err = clk_prepare_enable(usb2->ref_clk); + if (err < 0) { + dev_err(dev, "Failed to prepare ref clock: %d\n", err); + goto err_out; + } + + ref_clk_rate = clk_get_rate(usb2->ref_clk); + if (!ref_clk_rate) { + dev_err(dev, "Failed to get ref clock rate\n"); + err = -EINVAL; + goto err_clk_off; + } + + usb2ctl = readl(dmu + BCMA_DMU_CRU_USB2_CONTROL); + + if (usb2ctl & BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK) { + usb_pll_pdiv = usb2ctl; + usb_pll_pdiv &= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK; + usb_pll_pdiv >>= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_SHIFT; + } else { + usb_pll_pdiv = 1 << 3; + } + + /* Calculate ndiv based on a solid 1920 MHz that is for USB2 PHY */ + usb_pll_ndiv = (1920000000 * usb_pll_pdiv) / ref_clk_rate; + + /* Unlock DMU PLL settings with some magic value */ + writel(0x0000ea68, dmu + BCMA_DMU_CRU_CLKSET_KEY); + + /* Write USB 2.0 PLL control setting */ + usb2ctl &= ~BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK; + usb2ctl |= usb_pll_ndiv << BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT; + writel(usb2ctl, dmu + BCMA_DMU_CRU_USB2_CONTROL); + + /* Lock DMU PLL settings */ + writel(0x00000000, dmu + BCMA_DMU_CRU_CLKSET_KEY); + +err_clk_off: + clk_disable_unprepare(usb2->ref_clk); +err_out: + return err; +} + +static const struct phy_ops ops = { + .init = bcm_ns_usb2_phy_init, + .owner = THIS_MODULE, +}; + +static int bcm_ns_usb2_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm_ns_usb2 *usb2; + struct resource *res; + struct phy_provider *phy_provider; + + usb2 = devm_kzalloc(&pdev->dev, sizeof(*usb2), GFP_KERNEL); + if (!usb2) + return -ENOMEM; + usb2->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmu"); + usb2->dmu = devm_ioremap_resource(dev, res); + if (IS_ERR(usb2->dmu)) { + dev_err(dev, "Failed to map DMU regs\n"); + return PTR_ERR(usb2->dmu); + } + + usb2->ref_clk = devm_clk_get(dev, "phy-ref-clk"); + if (IS_ERR(usb2->ref_clk)) { + dev_err(dev, "Clock not defined\n"); + return PTR_ERR(usb2->ref_clk); + } + + usb2->phy = devm_phy_create(dev, NULL, &ops); + if (IS_ERR(usb2->phy)) + return PTR_ERR(usb2->phy); + + phy_set_drvdata(usb2->phy, usb2); + platform_set_drvdata(pdev, usb2); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id bcm_ns_usb2_id_table[] = { + { .compatible = "brcm,ns-usb2-phy", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm_ns_usb2_id_table); + +static struct platform_driver bcm_ns_usb2_driver = { + .probe = bcm_ns_usb2_probe, + .driver = { + .name = "bcm_ns_usb2", + .of_match_table = bcm_ns_usb2_id_table, + }, +}; +module_platform_driver(bcm_ns_usb2_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb3.c b/drivers/phy/broadcom/phy-bcm-ns-usb3.c new file mode 100644 index 000000000000..22b5e7047fa6 --- /dev/null +++ b/drivers/phy/broadcom/phy-bcm-ns-usb3.c @@ -0,0 +1,303 @@ +/* + * Broadcom Northstar USB 3.0 PHY Driver + * + * Copyright (C) 2016 Rafał Miłecki + * Copyright (C) 2016 Broadcom + * + * All magic values used for initialization (and related comments) were obtained + * from Broadcom's SDK: + * Copyright (c) Broadcom Corp, 2012 + * + * 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 + +#define BCM_NS_USB3_MII_MNG_TIMEOUT_US 1000 /* usecs */ + +#define BCM_NS_USB3_PHY_BASE_ADDR_REG 0x1f +#define BCM_NS_USB3_PHY_PLL30_BLOCK 0x8000 +#define BCM_NS_USB3_PHY_TX_PMD_BLOCK 0x8040 +#define BCM_NS_USB3_PHY_PIPE_BLOCK 0x8060 + +/* Registers of PLL30 block */ +#define BCM_NS_USB3_PLL_CONTROL 0x01 +#define BCM_NS_USB3_PLLA_CONTROL0 0x0a +#define BCM_NS_USB3_PLLA_CONTROL1 0x0b + +/* Registers of TX PMD block */ +#define BCM_NS_USB3_TX_PMD_CONTROL1 0x01 + +/* Registers of PIPE block */ +#define BCM_NS_USB3_LFPS_CMP 0x02 +#define BCM_NS_USB3_LFPS_DEGLITCH 0x03 + +enum bcm_ns_family { + BCM_NS_UNKNOWN, + BCM_NS_AX, + BCM_NS_BX, +}; + +struct bcm_ns_usb3 { + struct device *dev; + enum bcm_ns_family family; + void __iomem *dmp; + void __iomem *ccb_mii; + struct phy *phy; +}; + +static const struct of_device_id bcm_ns_usb3_id_table[] = { + { + .compatible = "brcm,ns-ax-usb3-phy", + .data = (int *)BCM_NS_AX, + }, + { + .compatible = "brcm,ns-bx-usb3-phy", + .data = (int *)BCM_NS_BX, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table); + +static int bcm_ns_usb3_wait_reg(struct bcm_ns_usb3 *usb3, void __iomem *addr, + u32 mask, u32 value, unsigned long timeout) +{ + unsigned long deadline = jiffies + timeout; + u32 val; + + do { + val = readl(addr); + if ((val & mask) == value) + return 0; + cpu_relax(); + udelay(10); + } while (!time_after_eq(jiffies, deadline)); + + dev_err(usb3->dev, "Timeout waiting for register %p\n", addr); + + return -EBUSY; +} + +static inline int bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3 *usb3) +{ + return bcm_ns_usb3_wait_reg(usb3, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL, + 0x0100, 0x0000, + usecs_to_jiffies(BCM_NS_USB3_MII_MNG_TIMEOUT_US)); +} + +static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, + u16 value) +{ + u32 tmp = 0; + int err; + + err = bcm_ns_usb3_mii_mng_wait_idle(usb3); + if (err < 0) { + dev_err(usb3->dev, "Couldn't write 0x%08x value\n", value); + return err; + } + + /* TODO: Use a proper MDIO bus layer */ + tmp |= 0x58020000; /* Magic value for MDIO PHY write */ + tmp |= reg << 18; + tmp |= value; + writel(tmp, usb3->ccb_mii + BCMA_CCB_MII_MNG_CMD_DATA); + + return 0; +} + +static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3) +{ + int err; + + /* Enable MDIO. Setting MDCDIV as 26 */ + writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL); + + /* Wait for MDIO? */ + udelay(2); + + /* USB3 PLL Block */ + err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, + BCM_NS_USB3_PHY_PLL30_BLOCK); + if (err < 0) + return err; + + /* Assert Ana_Pllseq start */ + bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x1000); + + /* Assert CML Divider ratio to 26 */ + bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400); + + /* Asserting PLL Reset */ + bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0xc000); + + /* Deaaserting PLL Reset */ + bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0x8000); + + /* Waiting MII Mgt interface idle */ + bcm_ns_usb3_mii_mng_wait_idle(usb3); + + /* Deasserting USB3 system reset */ + writel(0, usb3->dmp + BCMA_RESET_CTL); + + /* PLL frequency monitor enable */ + bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x9000); + + /* PIPE Block */ + bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, + BCM_NS_USB3_PHY_PIPE_BLOCK); + + /* CMPMAX & CMPMINTH setting */ + bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_CMP, 0xf30d); + + /* DEGLITCH MIN & MAX setting */ + bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_DEGLITCH, 0x6302); + + /* TXPMD block */ + bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, + BCM_NS_USB3_PHY_TX_PMD_BLOCK); + + /* Enabling SSC */ + bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003); + + /* Waiting MII Mgt interface idle */ + bcm_ns_usb3_mii_mng_wait_idle(usb3); + + return 0; +} + +static int bcm_ns_usb3_phy_init_ns_ax(struct bcm_ns_usb3 *usb3) +{ + int err; + + /* Enable MDIO. Setting MDCDIV as 26 */ + writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL); + + /* Wait for MDIO? */ + udelay(2); + + /* PLL30 block */ + err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, + BCM_NS_USB3_PHY_PLL30_BLOCK); + if (err < 0) + return err; + + bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400); + + bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 0x80e0); + + bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x009c); + + /* Enable SSC */ + bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, + BCM_NS_USB3_PHY_TX_PMD_BLOCK); + + bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x21d3); + + bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003); + + /* Waiting MII Mgt interface idle */ + bcm_ns_usb3_mii_mng_wait_idle(usb3); + + /* Deasserting USB3 system reset */ + writel(0, usb3->dmp + BCMA_RESET_CTL); + + return 0; +} + +static int bcm_ns_usb3_phy_init(struct phy *phy) +{ + struct bcm_ns_usb3 *usb3 = phy_get_drvdata(phy); + int err; + + /* Perform USB3 system soft reset */ + writel(BCMA_RESET_CTL_RESET, usb3->dmp + BCMA_RESET_CTL); + + switch (usb3->family) { + case BCM_NS_AX: + err = bcm_ns_usb3_phy_init_ns_ax(usb3); + break; + case BCM_NS_BX: + err = bcm_ns_usb3_phy_init_ns_bx(usb3); + break; + default: + WARN_ON(1); + err = -ENOTSUPP; + } + + return err; +} + +static const struct phy_ops ops = { + .init = bcm_ns_usb3_phy_init, + .owner = THIS_MODULE, +}; + +static int bcm_ns_usb3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *of_id; + struct bcm_ns_usb3 *usb3; + struct resource *res; + struct phy_provider *phy_provider; + + usb3 = devm_kzalloc(dev, sizeof(*usb3), GFP_KERNEL); + if (!usb3) + return -ENOMEM; + + usb3->dev = dev; + + of_id = of_match_device(bcm_ns_usb3_id_table, dev); + if (!of_id) + return -EINVAL; + usb3->family = (enum bcm_ns_family)of_id->data; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmp"); + usb3->dmp = devm_ioremap_resource(dev, res); + if (IS_ERR(usb3->dmp)) { + dev_err(dev, "Failed to map DMP regs\n"); + return PTR_ERR(usb3->dmp); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ccb-mii"); + usb3->ccb_mii = devm_ioremap_resource(dev, res); + if (IS_ERR(usb3->ccb_mii)) { + dev_err(dev, "Failed to map ChipCommon B MII regs\n"); + return PTR_ERR(usb3->ccb_mii); + } + + usb3->phy = devm_phy_create(dev, NULL, &ops); + if (IS_ERR(usb3->phy)) { + dev_err(dev, "Failed to create PHY\n"); + return PTR_ERR(usb3->phy); + } + + phy_set_drvdata(usb3->phy, usb3); + platform_set_drvdata(pdev, usb3); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (!IS_ERR(phy_provider)) + dev_info(dev, "Registered Broadcom Northstar USB 3.0 PHY driver\n"); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver bcm_ns_usb3_driver = { + .probe = bcm_ns_usb3_probe, + .driver = { + .name = "bcm_ns_usb3", + .of_match_table = bcm_ns_usb3_id_table, + }, +}; +module_platform_driver(bcm_ns_usb3_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/broadcom/phy-bcm-ns2-pcie.c b/drivers/phy/broadcom/phy-bcm-ns2-pcie.c new file mode 100644 index 000000000000..4c7d11d2b378 --- /dev/null +++ b/drivers/phy/broadcom/phy-bcm-ns2-pcie.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016 Broadcom + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#define BLK_ADDR_REG_OFFSET 0x1f +#define PLL_AFE1_100MHZ_BLK 0x2100 +#define PLL_CLK_AMP_OFFSET 0x03 +#define PLL_CLK_AMP_2P05V 0x2b18 + +static int ns2_pci_phy_init(struct phy *p) +{ + struct mdio_device *mdiodev = phy_get_drvdata(p); + int rc; + + /* select the AFE 100MHz block page */ + rc = mdiobus_write(mdiodev->bus, mdiodev->addr, + BLK_ADDR_REG_OFFSET, PLL_AFE1_100MHZ_BLK); + if (rc) + goto err; + + /* set the 100 MHz reference clock amplitude to 2.05 v */ + rc = mdiobus_write(mdiodev->bus, mdiodev->addr, + PLL_CLK_AMP_OFFSET, PLL_CLK_AMP_2P05V); + if (rc) + goto err; + + return 0; + +err: + dev_err(&mdiodev->dev, "Error %d writing to phy\n", rc); + return rc; +} + +static const struct phy_ops ns2_pci_phy_ops = { + .init = ns2_pci_phy_init, + .owner = THIS_MODULE, +}; + +static int ns2_pci_phy_probe(struct mdio_device *mdiodev) +{ + struct device *dev = &mdiodev->dev; + struct phy_provider *provider; + struct phy *phy; + + phy = devm_phy_create(dev, dev->of_node, &ns2_pci_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create Phy\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, mdiodev); + + provider = devm_of_phy_provider_register(&phy->dev, + of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "failed to register Phy provider\n"); + return PTR_ERR(provider); + } + + dev_info(dev, "%s PHY registered\n", dev_name(dev)); + + return 0; +} + +static const struct of_device_id ns2_pci_phy_of_match[] = { + { .compatible = "brcm,ns2-pcie-phy", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ns2_pci_phy_of_match); + +static struct mdio_driver ns2_pci_phy_driver = { + .mdiodrv = { + .driver = { + .name = "phy-bcm-ns2-pci", + .of_match_table = ns2_pci_phy_of_match, + }, + }, + .probe = ns2_pci_phy_probe, +}; +mdio_module_driver(ns2_pci_phy_driver); + +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("Broadcom Northstar2 PCI Phy driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:phy-bcm-ns2-pci"); diff --git a/drivers/phy/broadcom/phy-brcm-sata.c b/drivers/phy/broadcom/phy-brcm-sata.c new file mode 100644 index 000000000000..ccbc3d994998 --- /dev/null +++ b/drivers/phy/broadcom/phy-brcm-sata.c @@ -0,0 +1,493 @@ +/* + * Broadcom SATA3 AHCI Controller PHY Driver + * + * Copyright (C) 2016 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SATA_PCB_BANK_OFFSET 0x23c +#define SATA_PCB_REG_OFFSET(ofs) ((ofs) * 4) + +#define MAX_PORTS 2 + +/* Register offset between PHYs in PCB space */ +#define SATA_PCB_REG_28NM_SPACE_SIZE 0x1000 + +/* The older SATA PHY registers duplicated per port registers within the map, + * rather than having a separate map per port. + */ +#define SATA_PCB_REG_40NM_SPACE_SIZE 0x10 + +/* Register offset between PHYs in PHY control space */ +#define SATA_PHY_CTRL_REG_28NM_SPACE_SIZE 0x8 + +enum brcm_sata_phy_version { + BRCM_SATA_PHY_STB_28NM, + BRCM_SATA_PHY_STB_40NM, + BRCM_SATA_PHY_IPROC_NS2, + BRCM_SATA_PHY_IPROC_NSP, +}; + +struct brcm_sata_port { + int portnum; + struct phy *phy; + struct brcm_sata_phy *phy_priv; + bool ssc_en; +}; + +struct brcm_sata_phy { + struct device *dev; + void __iomem *phy_base; + void __iomem *ctrl_base; + enum brcm_sata_phy_version version; + + struct brcm_sata_port phys[MAX_PORTS]; +}; + +enum sata_phy_regs { + BLOCK0_REG_BANK = 0x000, + BLOCK0_XGXSSTATUS = 0x81, + BLOCK0_XGXSSTATUS_PLL_LOCK = BIT(12), + BLOCK0_SPARE = 0x8d, + BLOCK0_SPARE_OOB_CLK_SEL_MASK = 0x3, + BLOCK0_SPARE_OOB_CLK_SEL_REFBY2 = 0x1, + + PLL_REG_BANK_0 = 0x050, + PLL_REG_BANK_0_PLLCONTROL_0 = 0x81, + PLLCONTROL_0_FREQ_DET_RESTART = BIT(13), + PLLCONTROL_0_FREQ_MONITOR = BIT(12), + PLLCONTROL_0_SEQ_START = BIT(15), + PLL_CAP_CONTROL = 0x85, + PLL_ACTRL2 = 0x8b, + PLL_ACTRL2_SELDIV_MASK = 0x1f, + PLL_ACTRL2_SELDIV_SHIFT = 9, + + PLL1_REG_BANK = 0x060, + PLL1_ACTRL2 = 0x82, + PLL1_ACTRL3 = 0x83, + PLL1_ACTRL4 = 0x84, + + OOB_REG_BANK = 0x150, + OOB1_REG_BANK = 0x160, + OOB_CTRL1 = 0x80, + OOB_CTRL1_BURST_MAX_MASK = 0xf, + OOB_CTRL1_BURST_MAX_SHIFT = 12, + OOB_CTRL1_BURST_MIN_MASK = 0xf, + OOB_CTRL1_BURST_MIN_SHIFT = 8, + OOB_CTRL1_WAKE_IDLE_MAX_MASK = 0xf, + OOB_CTRL1_WAKE_IDLE_MAX_SHIFT = 4, + OOB_CTRL1_WAKE_IDLE_MIN_MASK = 0xf, + OOB_CTRL1_WAKE_IDLE_MIN_SHIFT = 0, + OOB_CTRL2 = 0x81, + OOB_CTRL2_SEL_ENA_SHIFT = 15, + OOB_CTRL2_SEL_ENA_RC_SHIFT = 14, + OOB_CTRL2_RESET_IDLE_MAX_MASK = 0x3f, + OOB_CTRL2_RESET_IDLE_MAX_SHIFT = 8, + OOB_CTRL2_BURST_CNT_MASK = 0x3, + OOB_CTRL2_BURST_CNT_SHIFT = 6, + OOB_CTRL2_RESET_IDLE_MIN_MASK = 0x3f, + OOB_CTRL2_RESET_IDLE_MIN_SHIFT = 0, + + TXPMD_REG_BANK = 0x1a0, + TXPMD_CONTROL1 = 0x81, + TXPMD_CONTROL1_TX_SSC_EN_FRC = BIT(0), + TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL = BIT(1), + TXPMD_TX_FREQ_CTRL_CONTROL1 = 0x82, + TXPMD_TX_FREQ_CTRL_CONTROL2 = 0x83, + TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK = 0x3ff, + TXPMD_TX_FREQ_CTRL_CONTROL3 = 0x84, + TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff, +}; + +enum sata_phy_ctrl_regs { + PHY_CTRL_1 = 0x0, + PHY_CTRL_1_RESET = BIT(0), +}; + +static inline void __iomem *brcm_sata_pcb_base(struct brcm_sata_port *port) +{ + struct brcm_sata_phy *priv = port->phy_priv; + u32 size = 0; + + switch (priv->version) { + case BRCM_SATA_PHY_STB_28NM: + case BRCM_SATA_PHY_IPROC_NS2: + size = SATA_PCB_REG_28NM_SPACE_SIZE; + break; + case BRCM_SATA_PHY_STB_40NM: + size = SATA_PCB_REG_40NM_SPACE_SIZE; + break; + default: + dev_err(priv->dev, "invalid phy version\n"); + break; + } + + return priv->phy_base + (port->portnum * size); +} + +static inline void __iomem *brcm_sata_ctrl_base(struct brcm_sata_port *port) +{ + struct brcm_sata_phy *priv = port->phy_priv; + u32 size = 0; + + switch (priv->version) { + case BRCM_SATA_PHY_IPROC_NS2: + size = SATA_PHY_CTRL_REG_28NM_SPACE_SIZE; + break; + default: + dev_err(priv->dev, "invalid phy version\n"); + break; + } + + return priv->ctrl_base + (port->portnum * size); +} + +static void brcm_sata_phy_wr(void __iomem *pcb_base, u32 bank, + u32 ofs, u32 msk, u32 value) +{ + u32 tmp; + + writel(bank, pcb_base + SATA_PCB_BANK_OFFSET); + tmp = readl(pcb_base + SATA_PCB_REG_OFFSET(ofs)); + tmp = (tmp & msk) | value; + writel(tmp, pcb_base + SATA_PCB_REG_OFFSET(ofs)); +} + +static u32 brcm_sata_phy_rd(void __iomem *pcb_base, u32 bank, u32 ofs) +{ + writel(bank, pcb_base + SATA_PCB_BANK_OFFSET); + return readl(pcb_base + SATA_PCB_REG_OFFSET(ofs)); +} + +/* These defaults were characterized by H/W group */ +#define STB_FMIN_VAL_DEFAULT 0x3df +#define STB_FMAX_VAL_DEFAULT 0x3df +#define STB_FMAX_VAL_SSC 0x83 + +static int brcm_stb_sata_init(struct brcm_sata_port *port) +{ + void __iomem *base = brcm_sata_pcb_base(port); + struct brcm_sata_phy *priv = port->phy_priv; + u32 tmp; + + /* override the TX spread spectrum setting */ + tmp = TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL | TXPMD_CONTROL1_TX_SSC_EN_FRC; + brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_CONTROL1, ~tmp, tmp); + + /* set fixed min freq */ + brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL2, + ~TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK, + STB_FMIN_VAL_DEFAULT); + + /* set fixed max freq depending on SSC config */ + if (port->ssc_en) { + dev_info(priv->dev, "enabling SSC on port%d\n", port->portnum); + tmp = STB_FMAX_VAL_SSC; + } else { + tmp = STB_FMAX_VAL_DEFAULT; + } + + brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3, + ~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp); + + return 0; +} + +/* NS2 SATA PLL1 defaults were characterized by H/W group */ +#define NS2_PLL1_ACTRL2_MAGIC 0x1df8 +#define NS2_PLL1_ACTRL3_MAGIC 0x2b00 +#define NS2_PLL1_ACTRL4_MAGIC 0x8824 + +static int brcm_ns2_sata_init(struct brcm_sata_port *port) +{ + int try; + unsigned int val; + void __iomem *base = brcm_sata_pcb_base(port); + void __iomem *ctrl_base = brcm_sata_ctrl_base(port); + struct device *dev = port->phy_priv->dev; + + /* Configure OOB control */ + val = 0x0; + val |= (0xc << OOB_CTRL1_BURST_MAX_SHIFT); + val |= (0x4 << OOB_CTRL1_BURST_MIN_SHIFT); + val |= (0x9 << OOB_CTRL1_WAKE_IDLE_MAX_SHIFT); + val |= (0x3 << OOB_CTRL1_WAKE_IDLE_MIN_SHIFT); + brcm_sata_phy_wr(base, OOB_REG_BANK, OOB_CTRL1, 0x0, val); + val = 0x0; + val |= (0x1b << OOB_CTRL2_RESET_IDLE_MAX_SHIFT); + val |= (0x2 << OOB_CTRL2_BURST_CNT_SHIFT); + val |= (0x9 << OOB_CTRL2_RESET_IDLE_MIN_SHIFT); + brcm_sata_phy_wr(base, OOB_REG_BANK, OOB_CTRL2, 0x0, val); + + /* Configure PHY PLL register bank 1 */ + val = NS2_PLL1_ACTRL2_MAGIC; + brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL2, 0x0, val); + val = NS2_PLL1_ACTRL3_MAGIC; + brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL3, 0x0, val); + val = NS2_PLL1_ACTRL4_MAGIC; + brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL4, 0x0, val); + + /* Configure PHY BLOCK0 register bank */ + /* Set oob_clk_sel to refclk/2 */ + brcm_sata_phy_wr(base, BLOCK0_REG_BANK, BLOCK0_SPARE, + ~BLOCK0_SPARE_OOB_CLK_SEL_MASK, + BLOCK0_SPARE_OOB_CLK_SEL_REFBY2); + + /* Strobe PHY reset using PHY control register */ + writel(PHY_CTRL_1_RESET, ctrl_base + PHY_CTRL_1); + mdelay(1); + writel(0x0, ctrl_base + PHY_CTRL_1); + mdelay(1); + + /* Wait for PHY PLL lock by polling pll_lock bit */ + try = 50; + while (try) { + val = brcm_sata_phy_rd(base, BLOCK0_REG_BANK, + BLOCK0_XGXSSTATUS); + if (val & BLOCK0_XGXSSTATUS_PLL_LOCK) + break; + msleep(20); + try--; + } + if (!try) { + /* PLL did not lock; give up */ + dev_err(dev, "port%d PLL did not lock\n", port->portnum); + return -ETIMEDOUT; + } + + dev_dbg(dev, "port%d initialized\n", port->portnum); + + return 0; +} + +static int brcm_nsp_sata_init(struct brcm_sata_port *port) +{ + struct brcm_sata_phy *priv = port->phy_priv; + struct device *dev = port->phy_priv->dev; + void __iomem *base = priv->phy_base; + unsigned int oob_bank; + unsigned int val, try; + + /* Configure OOB control */ + if (port->portnum == 0) + oob_bank = OOB_REG_BANK; + else if (port->portnum == 1) + oob_bank = OOB1_REG_BANK; + else + return -EINVAL; + + val = 0x0; + val |= (0x0f << OOB_CTRL1_BURST_MAX_SHIFT); + val |= (0x06 << OOB_CTRL1_BURST_MIN_SHIFT); + val |= (0x0f << OOB_CTRL1_WAKE_IDLE_MAX_SHIFT); + val |= (0x06 << OOB_CTRL1_WAKE_IDLE_MIN_SHIFT); + brcm_sata_phy_wr(base, oob_bank, OOB_CTRL1, 0x0, val); + + val = 0x0; + val |= (0x2e << OOB_CTRL2_RESET_IDLE_MAX_SHIFT); + val |= (0x02 << OOB_CTRL2_BURST_CNT_SHIFT); + val |= (0x16 << OOB_CTRL2_RESET_IDLE_MIN_SHIFT); + brcm_sata_phy_wr(base, oob_bank, OOB_CTRL2, 0x0, val); + + + brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_ACTRL2, + ~(PLL_ACTRL2_SELDIV_MASK << PLL_ACTRL2_SELDIV_SHIFT), + 0x0c << PLL_ACTRL2_SELDIV_SHIFT); + + brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_CAP_CONTROL, + 0xff0, 0x4f0); + + val = PLLCONTROL_0_FREQ_DET_RESTART | PLLCONTROL_0_FREQ_MONITOR; + brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, + ~val, val); + val = PLLCONTROL_0_SEQ_START; + brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, + ~val, 0); + mdelay(10); + brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, + ~val, val); + + /* Wait for pll_seq_done bit */ + try = 50; + while (try--) { + val = brcm_sata_phy_rd(base, BLOCK0_REG_BANK, + BLOCK0_XGXSSTATUS); + if (val & BLOCK0_XGXSSTATUS_PLL_LOCK) + break; + msleep(20); + } + if (!try) { + /* PLL did not lock; give up */ + dev_err(dev, "port%d PLL did not lock\n", port->portnum); + return -ETIMEDOUT; + } + + dev_dbg(dev, "port%d initialized\n", port->portnum); + + return 0; +} + +static int brcm_sata_phy_init(struct phy *phy) +{ + int rc; + struct brcm_sata_port *port = phy_get_drvdata(phy); + + switch (port->phy_priv->version) { + case BRCM_SATA_PHY_STB_28NM: + case BRCM_SATA_PHY_STB_40NM: + rc = brcm_stb_sata_init(port); + break; + case BRCM_SATA_PHY_IPROC_NS2: + rc = brcm_ns2_sata_init(port); + break; + case BRCM_SATA_PHY_IPROC_NSP: + rc = brcm_nsp_sata_init(port); + break; + default: + rc = -ENODEV; + } + + return rc; +} + +static const struct phy_ops phy_ops = { + .init = brcm_sata_phy_init, + .owner = THIS_MODULE, +}; + +static const struct of_device_id brcm_sata_phy_of_match[] = { + { .compatible = "brcm,bcm7445-sata-phy", + .data = (void *)BRCM_SATA_PHY_STB_28NM }, + { .compatible = "brcm,bcm7425-sata-phy", + .data = (void *)BRCM_SATA_PHY_STB_40NM }, + { .compatible = "brcm,iproc-ns2-sata-phy", + .data = (void *)BRCM_SATA_PHY_IPROC_NS2 }, + { .compatible = "brcm,iproc-nsp-sata-phy", + .data = (void *)BRCM_SATA_PHY_IPROC_NSP }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match); + +static int brcm_sata_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node, *child; + const struct of_device_id *of_id; + struct brcm_sata_phy *priv; + struct resource *res; + struct phy_provider *provider; + int ret, count = 0; + + if (of_get_child_count(dn) == 0) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + dev_set_drvdata(dev, priv); + priv->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); + priv->phy_base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->phy_base)) + return PTR_ERR(priv->phy_base); + + of_id = of_match_node(brcm_sata_phy_of_match, dn); + if (of_id) + priv->version = (enum brcm_sata_phy_version)of_id->data; + else + priv->version = BRCM_SATA_PHY_STB_28NM; + + if (priv->version == BRCM_SATA_PHY_IPROC_NS2) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "phy-ctrl"); + priv->ctrl_base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->ctrl_base)) + return PTR_ERR(priv->ctrl_base); + } + + for_each_available_child_of_node(dn, child) { + unsigned int id; + struct brcm_sata_port *port; + + if (of_property_read_u32(child, "reg", &id)) { + dev_err(dev, "missing reg property in node %s\n", + child->name); + ret = -EINVAL; + goto put_child; + } + + if (id >= MAX_PORTS) { + dev_err(dev, "invalid reg: %u\n", id); + ret = -EINVAL; + goto put_child; + } + if (priv->phys[id].phy) { + dev_err(dev, "already registered port %u\n", id); + ret = -EINVAL; + goto put_child; + } + + port = &priv->phys[id]; + port->portnum = id; + port->phy_priv = priv; + port->phy = devm_phy_create(dev, child, &phy_ops); + port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc"); + if (IS_ERR(port->phy)) { + dev_err(dev, "failed to create PHY\n"); + ret = PTR_ERR(port->phy); + goto put_child; + } + + phy_set_drvdata(port->phy, port); + count++; + } + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "could not register PHY provider\n"); + return PTR_ERR(provider); + } + + dev_info(dev, "registered %d port(s)\n", count); + + return 0; +put_child: + of_node_put(child); + return ret; +} + +static struct platform_driver brcm_sata_phy_driver = { + .probe = brcm_sata_phy_probe, + .driver = { + .of_match_table = brcm_sata_phy_of_match, + .name = "brcm-sata-phy", + } +}; +module_platform_driver(brcm_sata_phy_driver); + +MODULE_DESCRIPTION("Broadcom SATA PHY driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marc Carino"); +MODULE_AUTHOR("Brian Norris"); +MODULE_ALIAS("platform:phy-brcm-sata"); diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig new file mode 100644 index 000000000000..6164c4cd0f65 --- /dev/null +++ b/drivers/phy/hisilicon/Kconfig @@ -0,0 +1,20 @@ +# +# Phy drivers for Hisilicon platforms +# +config PHY_HI6220_USB + tristate "hi6220 USB PHY support" + depends on (ARCH_HISI && ARM64) || COMPILE_TEST + select GENERIC_PHY + select MFD_SYSCON + help + Enable this to support the HISILICON HI6220 USB PHY. + + To compile this driver as a module, choose M here. + +config PHY_HIX5HD2_SATA + tristate "HIX5HD2 SATA PHY Driver" + depends on ARCH_HIX5HD2 && OF && HAS_IOMEM + select GENERIC_PHY + select MFD_SYSCON + help + Support for SATA PHY on Hisilicon hix5hd2 Soc. diff --git a/drivers/phy/hisilicon/Makefile b/drivers/phy/hisilicon/Makefile new file mode 100644 index 000000000000..541b348187a8 --- /dev/null +++ b/drivers/phy/hisilicon/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_PHY_HI6220_USB) += phy-hi6220-usb.o +obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o diff --git a/drivers/phy/hisilicon/phy-hi6220-usb.c b/drivers/phy/hisilicon/phy-hi6220-usb.c new file mode 100644 index 000000000000..398c1021deec --- /dev/null +++ b/drivers/phy/hisilicon/phy-hi6220-usb.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2015 Linaro Ltd. + * Copyright (c) 2015 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#define SC_PERIPH_CTRL4 0x00c + +#define CTRL4_PICO_SIDDQ BIT(6) +#define CTRL4_PICO_OGDISABLE BIT(8) +#define CTRL4_PICO_VBUSVLDEXT BIT(10) +#define CTRL4_PICO_VBUSVLDEXTSEL BIT(11) +#define CTRL4_OTG_PHY_SEL BIT(21) + +#define SC_PERIPH_CTRL5 0x010 + +#define CTRL5_USBOTG_RES_SEL BIT(3) +#define CTRL5_PICOPHY_ACAENB BIT(4) +#define CTRL5_PICOPHY_BC_MODE BIT(5) +#define CTRL5_PICOPHY_CHRGSEL BIT(6) +#define CTRL5_PICOPHY_VDATSRCEND BIT(7) +#define CTRL5_PICOPHY_VDATDETENB BIT(8) +#define CTRL5_PICOPHY_DCDENB BIT(9) +#define CTRL5_PICOPHY_IDDIG BIT(10) + +#define SC_PERIPH_CTRL8 0x018 +#define SC_PERIPH_RSTEN0 0x300 +#define SC_PERIPH_RSTDIS0 0x304 + +#define RST0_USBOTG_BUS BIT(4) +#define RST0_POR_PICOPHY BIT(5) +#define RST0_USBOTG BIT(6) +#define RST0_USBOTG_32K BIT(7) + +#define EYE_PATTERN_PARA 0x7053348c + +struct hi6220_priv { + struct regmap *reg; + struct device *dev; +}; + +static void hi6220_phy_init(struct hi6220_priv *priv) +{ + struct regmap *reg = priv->reg; + u32 val, mask; + + val = RST0_USBOTG_BUS | RST0_POR_PICOPHY | + RST0_USBOTG | RST0_USBOTG_32K; + mask = val; + regmap_update_bits(reg, SC_PERIPH_RSTEN0, mask, val); + regmap_update_bits(reg, SC_PERIPH_RSTDIS0, mask, val); +} + +static int hi6220_phy_setup(struct hi6220_priv *priv, bool on) +{ + struct regmap *reg = priv->reg; + u32 val, mask; + int ret; + + if (on) { + val = CTRL5_USBOTG_RES_SEL | CTRL5_PICOPHY_ACAENB; + mask = val | CTRL5_PICOPHY_BC_MODE; + ret = regmap_update_bits(reg, SC_PERIPH_CTRL5, mask, val); + if (ret) + goto out; + + val = CTRL4_PICO_VBUSVLDEXT | CTRL4_PICO_VBUSVLDEXTSEL | + CTRL4_OTG_PHY_SEL; + mask = val | CTRL4_PICO_SIDDQ | CTRL4_PICO_OGDISABLE; + ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val); + if (ret) + goto out; + + ret = regmap_write(reg, SC_PERIPH_CTRL8, EYE_PATTERN_PARA); + if (ret) + goto out; + } else { + val = CTRL4_PICO_SIDDQ; + mask = val; + ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val); + if (ret) + goto out; + } + + return 0; +out: + dev_err(priv->dev, "failed to setup phy ret: %d\n", ret); + return ret; +} + +static int hi6220_phy_start(struct phy *phy) +{ + struct hi6220_priv *priv = phy_get_drvdata(phy); + + return hi6220_phy_setup(priv, true); +} + +static int hi6220_phy_exit(struct phy *phy) +{ + struct hi6220_priv *priv = phy_get_drvdata(phy); + + return hi6220_phy_setup(priv, false); +} + +static const struct phy_ops hi6220_phy_ops = { + .init = hi6220_phy_start, + .exit = hi6220_phy_exit, + .owner = THIS_MODULE, +}; + +static int hi6220_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct phy *phy; + struct hi6220_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->reg = syscon_regmap_lookup_by_phandle(dev->of_node, + "hisilicon,peripheral-syscon"); + if (IS_ERR(priv->reg)) { + dev_err(dev, "no hisilicon,peripheral-syscon\n"); + return PTR_ERR(priv->reg); + } + + hi6220_phy_init(priv); + + phy = devm_phy_create(dev, NULL, &hi6220_phy_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + phy_set_drvdata(phy, priv); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id hi6220_phy_of_match[] = { + {.compatible = "hisilicon,hi6220-usb-phy",}, + { }, +}; +MODULE_DEVICE_TABLE(of, hi6220_phy_of_match); + +static struct platform_driver hi6220_phy_driver = { + .probe = hi6220_phy_probe, + .driver = { + .name = "hi6220-usb-phy", + .of_match_table = hi6220_phy_of_match, + } +}; +module_platform_driver(hi6220_phy_driver); + +MODULE_DESCRIPTION("HISILICON HI6220 USB PHY driver"); +MODULE_ALIAS("platform:hi6220-usb-phy"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/hisilicon/phy-hix5hd2-sata.c b/drivers/phy/hisilicon/phy-hix5hd2-sata.c new file mode 100644 index 000000000000..e5ab3aa78b9d --- /dev/null +++ b/drivers/phy/hisilicon/phy-hix5hd2-sata.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2014 Linaro Ltd. + * Copyright (c) 2014 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define SATA_PHY0_CTLL 0xa0 +#define MPLL_MULTIPLIER_SHIFT 1 +#define MPLL_MULTIPLIER_MASK 0xfe +#define MPLL_MULTIPLIER_50M 0x3c +#define MPLL_MULTIPLIER_100M 0x1e +#define PHY_RESET BIT(0) +#define REF_SSP_EN BIT(9) +#define SSC_EN BIT(10) +#define REF_USE_PAD BIT(23) + +#define SATA_PORT_PHYCTL 0x174 +#define SPEED_MODE_MASK 0x6f0000 +#define HALF_RATE_SHIFT 16 +#define PHY_CONFIG_SHIFT 18 +#define GEN2_EN_SHIFT 21 +#define SPEED_CTRL BIT(20) + +#define SATA_PORT_PHYCTL1 0x148 +#define AMPLITUDE_MASK 0x3ffffe +#define AMPLITUDE_GEN3 0x68 +#define AMPLITUDE_GEN3_SHIFT 15 +#define AMPLITUDE_GEN2 0x56 +#define AMPLITUDE_GEN2_SHIFT 8 +#define AMPLITUDE_GEN1 0x56 +#define AMPLITUDE_GEN1_SHIFT 1 + +#define SATA_PORT_PHYCTL2 0x14c +#define PREEMPH_MASK 0x3ffff +#define PREEMPH_GEN3 0x20 +#define PREEMPH_GEN3_SHIFT 12 +#define PREEMPH_GEN2 0x15 +#define PREEMPH_GEN2_SHIFT 6 +#define PREEMPH_GEN1 0x5 +#define PREEMPH_GEN1_SHIFT 0 + +struct hix5hd2_priv { + void __iomem *base; + struct regmap *peri_ctrl; +}; + +enum phy_speed_mode { + SPEED_MODE_GEN1 = 0, + SPEED_MODE_GEN2 = 1, + SPEED_MODE_GEN3 = 2, +}; + +static int hix5hd2_sata_phy_init(struct phy *phy) +{ + struct hix5hd2_priv *priv = phy_get_drvdata(phy); + u32 val, data[2]; + int ret; + + if (priv->peri_ctrl) { + ret = of_property_read_u32_array(phy->dev.of_node, + "hisilicon,power-reg", + &data[0], 2); + if (ret) { + dev_err(&phy->dev, "Fail read hisilicon,power-reg\n"); + return ret; + } + + regmap_update_bits(priv->peri_ctrl, data[0], + BIT(data[1]), BIT(data[1])); + } + + /* reset phy */ + val = readl_relaxed(priv->base + SATA_PHY0_CTLL); + val &= ~(MPLL_MULTIPLIER_MASK | REF_USE_PAD); + val |= MPLL_MULTIPLIER_50M << MPLL_MULTIPLIER_SHIFT | + REF_SSP_EN | PHY_RESET; + writel_relaxed(val, priv->base + SATA_PHY0_CTLL); + msleep(20); + val &= ~PHY_RESET; + writel_relaxed(val, priv->base + SATA_PHY0_CTLL); + + val = readl_relaxed(priv->base + SATA_PORT_PHYCTL1); + val &= ~AMPLITUDE_MASK; + val |= AMPLITUDE_GEN3 << AMPLITUDE_GEN3_SHIFT | + AMPLITUDE_GEN2 << AMPLITUDE_GEN2_SHIFT | + AMPLITUDE_GEN1 << AMPLITUDE_GEN1_SHIFT; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL1); + + val = readl_relaxed(priv->base + SATA_PORT_PHYCTL2); + val &= ~PREEMPH_MASK; + val |= PREEMPH_GEN3 << PREEMPH_GEN3_SHIFT | + PREEMPH_GEN2 << PREEMPH_GEN2_SHIFT | + PREEMPH_GEN1 << PREEMPH_GEN1_SHIFT; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL2); + + /* ensure PHYCTRL setting takes effect */ + val = readl_relaxed(priv->base + SATA_PORT_PHYCTL); + val &= ~SPEED_MODE_MASK; + val |= SPEED_MODE_GEN1 << HALF_RATE_SHIFT | + SPEED_MODE_GEN1 << PHY_CONFIG_SHIFT | + SPEED_MODE_GEN1 << GEN2_EN_SHIFT | SPEED_CTRL; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); + + msleep(20); + val &= ~SPEED_MODE_MASK; + val |= SPEED_MODE_GEN3 << HALF_RATE_SHIFT | + SPEED_MODE_GEN3 << PHY_CONFIG_SHIFT | + SPEED_MODE_GEN3 << GEN2_EN_SHIFT | SPEED_CTRL; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); + + val &= ~(SPEED_MODE_MASK | SPEED_CTRL); + val |= SPEED_MODE_GEN2 << HALF_RATE_SHIFT | + SPEED_MODE_GEN2 << PHY_CONFIG_SHIFT | + SPEED_MODE_GEN2 << GEN2_EN_SHIFT; + writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); + + return 0; +} + +static const struct phy_ops hix5hd2_sata_phy_ops = { + .init = hix5hd2_sata_phy_init, + .owner = THIS_MODULE, +}; + +static int hix5hd2_sata_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct resource *res; + struct phy *phy; + struct hix5hd2_priv *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + priv->base = devm_ioremap(dev, res->start, resource_size(res)); + if (!priv->base) + return -ENOMEM; + + priv->peri_ctrl = syscon_regmap_lookup_by_phandle(dev->of_node, + "hisilicon,peripheral-syscon"); + if (IS_ERR(priv->peri_ctrl)) + priv->peri_ctrl = NULL; + + phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, priv); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id hix5hd2_sata_phy_of_match[] = { + {.compatible = "hisilicon,hix5hd2-sata-phy",}, + { }, +}; +MODULE_DEVICE_TABLE(of, hix5hd2_sata_phy_of_match); + +static struct platform_driver hix5hd2_sata_phy_driver = { + .probe = hix5hd2_sata_phy_probe, + .driver = { + .name = "hix5hd2-sata-phy", + .of_match_table = hix5hd2_sata_phy_of_match, + } +}; +module_platform_driver(hix5hd2_sata_phy_driver); + +MODULE_AUTHOR("Jiancheng Xue "); +MODULE_DESCRIPTION("HISILICON HIX5HD2 SATA PHY driver"); +MODULE_ALIAS("platform:hix5hd2-sata-phy"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/marvell/Kconfig b/drivers/phy/marvell/Kconfig new file mode 100644 index 000000000000..048d8893bc2e --- /dev/null +++ b/drivers/phy/marvell/Kconfig @@ -0,0 +1,50 @@ +# +# Phy drivers for Marvell platforms +# +config ARMADA375_USBCLUSTER_PHY + def_bool y + depends on MACH_ARMADA_375 || COMPILE_TEST + depends on OF && HAS_IOMEM + select GENERIC_PHY + +config PHY_BERLIN_SATA + tristate "Marvell Berlin SATA PHY driver" + depends on ARCH_BERLIN && HAS_IOMEM && OF + select GENERIC_PHY + help + Enable this to support the SATA PHY on Marvell Berlin SoCs. + +config PHY_BERLIN_USB + tristate "Marvell Berlin USB PHY Driver" + depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF + select GENERIC_PHY + help + Enable this to support the USB PHY on Marvell Berlin SoCs. + +config PHY_MVEBU_SATA + def_bool y + depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD + depends on OF + select GENERIC_PHY + +config PHY_PXA_28NM_HSIC + tristate "Marvell USB HSIC 28nm PHY Driver" + depends on HAS_IOMEM + select GENERIC_PHY + help + Enable this to support Marvell USB HSIC PHY driver for Marvell + SoC. This driver will do the PHY initialization and shutdown. + The PHY driver will be used by Marvell ehci driver. + + To compile this driver as a module, choose M here. + +config PHY_PXA_28NM_USB2 + tristate "Marvell USB 2.0 28nm PHY Driver" + depends on HAS_IOMEM + select GENERIC_PHY + help + Enable this to support Marvell USB 2.0 PHY driver for Marvell + SoC. This driver will do the PHY initialization and shutdown. + The PHY driver will be used by Marvell udc/ehci/otg driver. + + To compile this driver as a module, choose M here. diff --git a/drivers/phy/marvell/Makefile b/drivers/phy/marvell/Makefile new file mode 100644 index 000000000000..3fc188f59118 --- /dev/null +++ b/drivers/phy/marvell/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o +obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o +obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o +obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o +obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o +obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o diff --git a/drivers/phy/marvell/phy-armada375-usb2.c b/drivers/phy/marvell/phy-armada375-usb2.c new file mode 100644 index 000000000000..1a3db288c0a9 --- /dev/null +++ b/drivers/phy/marvell/phy-armada375-usb2.c @@ -0,0 +1,158 @@ +/* + * USB cluster support for Armada 375 platform. + * + * Copyright (C) 2014 Marvell + * + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2 or later. This program is licensed "as is" + * without any warranty of any kind, whether express or implied. + * + * Armada 375 comes with an USB2 host and device controller and an + * USB3 controller. The USB cluster control register allows to manage + * common features of both USB controllers. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB2_PHY_CONFIG_DISABLE BIT(0) + +struct armada375_cluster_phy { + struct phy *phy; + void __iomem *reg; + bool use_usb3; + int phy_provided; +}; + +static int armada375_usb_phy_init(struct phy *phy) +{ + struct armada375_cluster_phy *cluster_phy; + u32 reg; + + cluster_phy = phy_get_drvdata(phy); + if (!cluster_phy) + return -ENODEV; + + reg = readl(cluster_phy->reg); + if (cluster_phy->use_usb3) + reg |= USB2_PHY_CONFIG_DISABLE; + else + reg &= ~USB2_PHY_CONFIG_DISABLE; + writel(reg, cluster_phy->reg); + + return 0; +} + +static const struct phy_ops armada375_usb_phy_ops = { + .init = armada375_usb_phy_init, + .owner = THIS_MODULE, +}; + +/* + * Only one controller can use this PHY. We shouldn't have the case + * when two controllers want to use this PHY. But if this case occurs + * then we provide a phy to the first one and return an error for the + * next one. This error has also to be an error returned by + * devm_phy_optional_get() so different from ENODEV for USB2. In the + * USB3 case it still optional and we use ENODEV. + */ +static struct phy *armada375_usb_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct armada375_cluster_phy *cluster_phy = dev_get_drvdata(dev); + + if (!cluster_phy) + return ERR_PTR(-ENODEV); + + /* + * Either the phy had never been requested and then the first + * usb claiming it can get it, or it had already been + * requested in this case, we only allow to use it with the + * same configuration. + */ + if (WARN_ON((cluster_phy->phy_provided != PHY_NONE) && + (cluster_phy->phy_provided != args->args[0]))) { + dev_err(dev, "This PHY has already been provided!\n"); + dev_err(dev, "Check your device tree, only one controller can use it\n."); + if (args->args[0] == PHY_TYPE_USB2) + return ERR_PTR(-EBUSY); + else + return ERR_PTR(-ENODEV); + } + + if (args->args[0] == PHY_TYPE_USB2) + cluster_phy->use_usb3 = false; + else if (args->args[0] == PHY_TYPE_USB3) + cluster_phy->use_usb3 = true; + else { + dev_err(dev, "Invalid PHY mode\n"); + return ERR_PTR(-ENODEV); + } + + /* Store which phy mode is used for next test */ + cluster_phy->phy_provided = args->args[0]; + + return cluster_phy->phy; +} + +static int armada375_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy *phy; + struct phy_provider *phy_provider; + void __iomem *usb_cluster_base; + struct resource *res; + struct armada375_cluster_phy *cluster_phy; + + cluster_phy = devm_kzalloc(dev, sizeof(*cluster_phy), GFP_KERNEL); + if (!cluster_phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + usb_cluster_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(usb_cluster_base)) + return PTR_ERR(usb_cluster_base); + + phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + cluster_phy->phy = phy; + cluster_phy->reg = usb_cluster_base; + + dev_set_drvdata(dev, cluster_phy); + phy_set_drvdata(phy, cluster_phy); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, + armada375_usb_phy_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id of_usb_cluster_table[] = { + { .compatible = "marvell,armada-375-usb-cluster", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, of_usb_cluster_table); + +static struct platform_driver armada375_usb_phy_driver = { + .probe = armada375_usb_phy_probe, + .driver = { + .of_match_table = of_usb_cluster_table, + .name = "armada-375-usb-cluster", + } +}; +module_platform_driver(armada375_usb_phy_driver); + +MODULE_DESCRIPTION("Armada 375 USB cluster driver"); +MODULE_AUTHOR("Gregory CLEMENT "); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/marvell/phy-berlin-sata.c b/drivers/phy/marvell/phy-berlin-sata.c new file mode 100644 index 000000000000..2c7a57f2d595 --- /dev/null +++ b/drivers/phy/marvell/phy-berlin-sata.c @@ -0,0 +1,299 @@ +/* + * Marvell Berlin SATA PHY driver + * + * Copyright (C) 2014 Marvell Technology Group Ltd. + * + * Antoine Ténart + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#define HOST_VSA_ADDR 0x0 +#define HOST_VSA_DATA 0x4 +#define PORT_SCR_CTL 0x2c +#define PORT_VSR_ADDR 0x78 +#define PORT_VSR_DATA 0x7c + +#define CONTROL_REGISTER 0x0 +#define MBUS_SIZE_CONTROL 0x4 + +#define POWER_DOWN_PHY0 BIT(6) +#define POWER_DOWN_PHY1 BIT(14) +#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16) +#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19) + +#define BG2_PHY_BASE 0x080 +#define BG2Q_PHY_BASE 0x200 + +/* register 0x01 */ +#define REF_FREF_SEL_25 BIT(0) +#define PHY_MODE_SATA (0x0 << 5) + +/* register 0x02 */ +#define USE_MAX_PLL_RATE BIT(12) + +/* register 0x23 */ +#define DATA_BIT_WIDTH_10 (0x0 << 10) +#define DATA_BIT_WIDTH_20 (0x1 << 10) +#define DATA_BIT_WIDTH_40 (0x2 << 10) + +/* register 0x25 */ +#define PHY_GEN_MAX_1_5 (0x0 << 10) +#define PHY_GEN_MAX_3_0 (0x1 << 10) +#define PHY_GEN_MAX_6_0 (0x2 << 10) + +struct phy_berlin_desc { + struct phy *phy; + u32 power_bit; + unsigned index; +}; + +struct phy_berlin_priv { + void __iomem *base; + spinlock_t lock; + struct clk *clk; + struct phy_berlin_desc **phys; + unsigned nphys; + u32 phy_base; +}; + +static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, + u32 phy_base, u32 reg, u32 mask, u32 val) +{ + u32 regval; + + /* select register */ + writel(phy_base + reg, ctrl_reg + PORT_VSR_ADDR); + + /* set bits */ + regval = readl(ctrl_reg + PORT_VSR_DATA); + regval &= ~mask; + regval |= val; + writel(regval, ctrl_reg + PORT_VSR_DATA); +} + +static int phy_berlin_sata_power_on(struct phy *phy) +{ + struct phy_berlin_desc *desc = phy_get_drvdata(phy); + struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); + void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80); + u32 regval; + + clk_prepare_enable(priv->clk); + + spin_lock(&priv->lock); + + /* Power on PHY */ + writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); + regval = readl(priv->base + HOST_VSA_DATA); + regval &= ~desc->power_bit; + writel(regval, priv->base + HOST_VSA_DATA); + + /* Configure MBus */ + writel(MBUS_SIZE_CONTROL, priv->base + HOST_VSA_ADDR); + regval = readl(priv->base + HOST_VSA_DATA); + regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128; + writel(regval, priv->base + HOST_VSA_DATA); + + /* set PHY mode and ref freq to 25 MHz */ + phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x01, + 0x00ff, REF_FREF_SEL_25 | PHY_MODE_SATA); + + /* set PHY up to 6 Gbps */ + phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x25, + 0x0c00, PHY_GEN_MAX_6_0); + + /* set 40 bits width */ + phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x23, + 0x0c00, DATA_BIT_WIDTH_40); + + /* use max pll rate */ + phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x02, + 0x0000, USE_MAX_PLL_RATE); + + /* set Gen3 controller speed */ + regval = readl(ctrl_reg + PORT_SCR_CTL); + regval &= ~GENMASK(7, 4); + regval |= 0x30; + writel(regval, ctrl_reg + PORT_SCR_CTL); + + spin_unlock(&priv->lock); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int phy_berlin_sata_power_off(struct phy *phy) +{ + struct phy_berlin_desc *desc = phy_get_drvdata(phy); + struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); + u32 regval; + + clk_prepare_enable(priv->clk); + + spin_lock(&priv->lock); + + /* Power down PHY */ + writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); + regval = readl(priv->base + HOST_VSA_DATA); + regval |= desc->power_bit; + writel(regval, priv->base + HOST_VSA_DATA); + + spin_unlock(&priv->lock); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static struct phy *phy_berlin_sata_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct phy_berlin_priv *priv = dev_get_drvdata(dev); + int i; + + if (WARN_ON(args->args[0] >= priv->nphys)) + return ERR_PTR(-ENODEV); + + for (i = 0; i < priv->nphys; i++) { + if (priv->phys[i]->index == args->args[0]) + break; + } + + if (i == priv->nphys) + return ERR_PTR(-ENODEV); + + return priv->phys[i]->phy; +} + +static const struct phy_ops phy_berlin_sata_ops = { + .power_on = phy_berlin_sata_power_on, + .power_off = phy_berlin_sata_power_off, + .owner = THIS_MODULE, +}; + +static u32 phy_berlin_power_down_bits[] = { + POWER_DOWN_PHY0, + POWER_DOWN_PHY1, +}; + +static int phy_berlin_sata_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *child; + struct phy *phy; + struct phy_provider *phy_provider; + struct phy_berlin_priv *priv; + struct resource *res; + int ret, i = 0; + u32 phy_id; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + priv->base = devm_ioremap(dev, res->start, resource_size(res)); + if (!priv->base) + return -ENOMEM; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + priv->nphys = of_get_child_count(dev->of_node); + if (priv->nphys == 0) + return -ENODEV; + + priv->phys = devm_kcalloc(dev, priv->nphys, sizeof(*priv->phys), + GFP_KERNEL); + if (!priv->phys) + return -ENOMEM; + + if (of_device_is_compatible(dev->of_node, "marvell,berlin2-sata-phy")) + priv->phy_base = BG2_PHY_BASE; + else + priv->phy_base = BG2Q_PHY_BASE; + + dev_set_drvdata(dev, priv); + spin_lock_init(&priv->lock); + + for_each_available_child_of_node(dev->of_node, child) { + struct phy_berlin_desc *phy_desc; + + if (of_property_read_u32(child, "reg", &phy_id)) { + dev_err(dev, "missing reg property in node %s\n", + child->name); + ret = -EINVAL; + goto put_child; + } + + if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) { + dev_err(dev, "invalid reg in node %s\n", child->name); + ret = -EINVAL; + goto put_child; + } + + phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL); + if (!phy_desc) { + ret = -ENOMEM; + goto put_child; + } + + phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY %d\n", phy_id); + ret = PTR_ERR(phy); + goto put_child; + } + + phy_desc->phy = phy; + phy_desc->power_bit = phy_berlin_power_down_bits[phy_id]; + phy_desc->index = phy_id; + phy_set_drvdata(phy, phy_desc); + + priv->phys[i++] = phy_desc; + + /* Make sure the PHY is off */ + phy_berlin_sata_power_off(phy); + } + + phy_provider = + devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +put_child: + of_node_put(child); + return ret; +} + +static const struct of_device_id phy_berlin_sata_of_match[] = { + { .compatible = "marvell,berlin2-sata-phy" }, + { .compatible = "marvell,berlin2q-sata-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match); + +static struct platform_driver phy_berlin_sata_driver = { + .probe = phy_berlin_sata_probe, + .driver = { + .name = "phy-berlin-sata", + .of_match_table = phy_berlin_sata_of_match, + }, +}; +module_platform_driver(phy_berlin_sata_driver); + +MODULE_DESCRIPTION("Marvell Berlin SATA PHY driver"); +MODULE_AUTHOR("Antoine Ténart "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/marvell/phy-berlin-usb.c b/drivers/phy/marvell/phy-berlin-usb.c new file mode 100644 index 000000000000..2017751ede26 --- /dev/null +++ b/drivers/phy/marvell/phy-berlin-usb.c @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2014 Marvell Technology Group Ltd. + * + * Antoine Tenart + * Jisheng Zhang + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include + +#define USB_PHY_PLL 0x04 +#define USB_PHY_PLL_CONTROL 0x08 +#define USB_PHY_TX_CTRL0 0x10 +#define USB_PHY_TX_CTRL1 0x14 +#define USB_PHY_TX_CTRL2 0x18 +#define USB_PHY_RX_CTRL 0x20 +#define USB_PHY_ANALOG 0x34 + +/* USB_PHY_PLL */ +#define CLK_REF_DIV(x) ((x) << 4) +#define FEEDBACK_CLK_DIV(x) ((x) << 8) + +/* USB_PHY_PLL_CONTROL */ +#define CLK_STABLE BIT(0) +#define PLL_CTRL_PIN BIT(1) +#define PLL_CTRL_REG BIT(2) +#define PLL_ON BIT(3) +#define PHASE_OFF_TOL_125 (0x0 << 5) +#define PHASE_OFF_TOL_250 BIT(5) +#define KVC0_CALIB (0x0 << 9) +#define KVC0_REG_CTRL BIT(9) +#define KVC0_HIGH (0x0 << 10) +#define KVC0_LOW (0x3 << 10) +#define CLK_BLK_EN BIT(13) + +/* USB_PHY_TX_CTRL0 */ +#define EXT_HS_RCAL_EN BIT(3) +#define EXT_FS_RCAL_EN BIT(4) +#define IMPCAL_VTH_DIV(x) ((x) << 5) +#define EXT_RS_RCAL_DIV(x) ((x) << 8) +#define EXT_FS_RCAL_DIV(x) ((x) << 12) + +/* USB_PHY_TX_CTRL1 */ +#define TX_VDD15_14 (0x0 << 4) +#define TX_VDD15_15 BIT(4) +#define TX_VDD15_16 (0x2 << 4) +#define TX_VDD15_17 (0x3 << 4) +#define TX_VDD12_VDD (0x0 << 6) +#define TX_VDD12_11 BIT(6) +#define TX_VDD12_12 (0x2 << 6) +#define TX_VDD12_13 (0x3 << 6) +#define LOW_VDD_EN BIT(8) +#define TX_OUT_AMP(x) ((x) << 9) + +/* USB_PHY_TX_CTRL2 */ +#define TX_CHAN_CTRL_REG(x) ((x) << 0) +#define DRV_SLEWRATE(x) ((x) << 4) +#define IMP_CAL_FS_HS_DLY_0 (0x0 << 6) +#define IMP_CAL_FS_HS_DLY_1 BIT(6) +#define IMP_CAL_FS_HS_DLY_2 (0x2 << 6) +#define IMP_CAL_FS_HS_DLY_3 (0x3 << 6) +#define FS_DRV_EN_MASK(x) ((x) << 8) +#define HS_DRV_EN_MASK(x) ((x) << 12) + +/* USB_PHY_RX_CTRL */ +#define PHASE_FREEZE_DLY_2_CL (0x0 << 0) +#define PHASE_FREEZE_DLY_4_CL BIT(0) +#define ACK_LENGTH_8_CL (0x0 << 2) +#define ACK_LENGTH_12_CL BIT(2) +#define ACK_LENGTH_16_CL (0x2 << 2) +#define ACK_LENGTH_20_CL (0x3 << 2) +#define SQ_LENGTH_3 (0x0 << 4) +#define SQ_LENGTH_6 BIT(4) +#define SQ_LENGTH_9 (0x2 << 4) +#define SQ_LENGTH_12 (0x3 << 4) +#define DISCON_THRESHOLD_260 (0x0 << 6) +#define DISCON_THRESHOLD_270 BIT(6) +#define DISCON_THRESHOLD_280 (0x2 << 6) +#define DISCON_THRESHOLD_290 (0x3 << 6) +#define SQ_THRESHOLD(x) ((x) << 8) +#define LPF_COEF(x) ((x) << 12) +#define INTPL_CUR_10 (0x0 << 14) +#define INTPL_CUR_20 BIT(14) +#define INTPL_CUR_30 (0x2 << 14) +#define INTPL_CUR_40 (0x3 << 14) + +/* USB_PHY_ANALOG */ +#define ANA_PWR_UP BIT(1) +#define ANA_PWR_DOWN BIT(2) +#define V2I_VCO_RATIO(x) ((x) << 7) +#define R_ROTATE_90 (0x0 << 10) +#define R_ROTATE_0 BIT(10) +#define MODE_TEST_EN BIT(11) +#define ANA_TEST_DC_CTRL(x) ((x) << 12) + +static const u32 phy_berlin_pll_dividers[] = { + /* Berlin 2 */ + CLK_REF_DIV(0x6) | FEEDBACK_CLK_DIV(0x55), + /* Berlin 2CD/Q */ + CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54), +}; + +struct phy_berlin_usb_priv { + void __iomem *base; + struct reset_control *rst_ctrl; + u32 pll_divider; +}; + +static int phy_berlin_usb_power_on(struct phy *phy) +{ + struct phy_berlin_usb_priv *priv = phy_get_drvdata(phy); + + reset_control_reset(priv->rst_ctrl); + + writel(priv->pll_divider, + priv->base + USB_PHY_PLL); + writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL | + CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL); + writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5), + priv->base + USB_PHY_ANALOG); + writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 | + DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) | + INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL); + + writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1); + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), + priv->base + USB_PHY_TX_CTRL0); + + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4) | + EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0); + + writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), + priv->base + USB_PHY_TX_CTRL0); + writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) | IMP_CAL_FS_HS_DLY_3 | + FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2); + + return 0; +} + +static const struct phy_ops phy_berlin_usb_ops = { + .power_on = phy_berlin_usb_power_on, + .owner = THIS_MODULE, +}; + +static const struct of_device_id phy_berlin_usb_of_match[] = { + { + .compatible = "marvell,berlin2-usb-phy", + .data = &phy_berlin_pll_dividers[0], + }, + { + .compatible = "marvell,berlin2cd-usb-phy", + .data = &phy_berlin_pll_dividers[1], + }, + { }, +}; +MODULE_DEVICE_TABLE(of, phy_berlin_usb_of_match); + +static int phy_berlin_usb_probe(struct platform_device *pdev) +{ + const struct of_device_id *match = + of_match_device(phy_berlin_usb_of_match, &pdev->dev); + struct phy_berlin_usb_priv *priv; + struct resource *res; + struct phy *phy; + struct phy_provider *phy_provider; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(priv->rst_ctrl)) + return PTR_ERR(priv->rst_ctrl); + + priv->pll_divider = *((u32 *)match->data); + + phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops); + if (IS_ERR(phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, priv); + + phy_provider = + devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver phy_berlin_usb_driver = { + .probe = phy_berlin_usb_probe, + .driver = { + .name = "phy-berlin-usb", + .of_match_table = phy_berlin_usb_of_match, + }, +}; +module_platform_driver(phy_berlin_usb_driver); + +MODULE_AUTHOR("Antoine Tenart "); +MODULE_DESCRIPTION("Marvell Berlin PHY driver for USB"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/marvell/phy-mvebu-sata.c b/drivers/phy/marvell/phy-mvebu-sata.c new file mode 100644 index 000000000000..768ce92e81ce --- /dev/null +++ b/drivers/phy/marvell/phy-mvebu-sata.c @@ -0,0 +1,138 @@ +/* + * phy-mvebu-sata.c: SATA Phy driver for the Marvell mvebu SoCs. + * + * Copyright (C) 2013 Andrew Lunn + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +struct priv { + struct clk *clk; + void __iomem *base; +}; + +#define SATA_PHY_MODE_2 0x0330 +#define MODE_2_FORCE_PU_TX BIT(0) +#define MODE_2_FORCE_PU_RX BIT(1) +#define MODE_2_PU_PLL BIT(2) +#define MODE_2_PU_IVREF BIT(3) +#define SATA_IF_CTRL 0x0050 +#define CTRL_PHY_SHUTDOWN BIT(9) + +static int phy_mvebu_sata_power_on(struct phy *phy) +{ + struct priv *priv = phy_get_drvdata(phy); + u32 reg; + + clk_prepare_enable(priv->clk); + + /* Enable PLL and IVREF */ + reg = readl(priv->base + SATA_PHY_MODE_2); + reg |= (MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX | + MODE_2_PU_PLL | MODE_2_PU_IVREF); + writel(reg , priv->base + SATA_PHY_MODE_2); + + /* Enable PHY */ + reg = readl(priv->base + SATA_IF_CTRL); + reg &= ~CTRL_PHY_SHUTDOWN; + writel(reg, priv->base + SATA_IF_CTRL); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static int phy_mvebu_sata_power_off(struct phy *phy) +{ + struct priv *priv = phy_get_drvdata(phy); + u32 reg; + + clk_prepare_enable(priv->clk); + + /* Disable PLL and IVREF */ + reg = readl(priv->base + SATA_PHY_MODE_2); + reg &= ~(MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX | + MODE_2_PU_PLL | MODE_2_PU_IVREF); + writel(reg, priv->base + SATA_PHY_MODE_2); + + /* Disable PHY */ + reg = readl(priv->base + SATA_IF_CTRL); + reg |= CTRL_PHY_SHUTDOWN; + writel(reg, priv->base + SATA_IF_CTRL); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static const struct phy_ops phy_mvebu_sata_ops = { + .power_on = phy_mvebu_sata_power_on, + .power_off = phy_mvebu_sata_power_off, + .owner = THIS_MODULE, +}; + +static int phy_mvebu_sata_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct resource *res; + struct priv *priv; + struct phy *phy; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->clk = devm_clk_get(&pdev->dev, "sata"); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + phy_set_drvdata(phy, priv); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, + of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + /* The boot loader may of left it on. Turn it off. */ + phy_mvebu_sata_power_off(phy); + + return 0; +} + +static const struct of_device_id phy_mvebu_sata_of_match[] = { + { .compatible = "marvell,mvebu-sata-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, phy_mvebu_sata_of_match); + +static struct platform_driver phy_mvebu_sata_driver = { + .probe = phy_mvebu_sata_probe, + .driver = { + .name = "phy-mvebu-sata", + .of_match_table = phy_mvebu_sata_of_match, + } +}; +module_platform_driver(phy_mvebu_sata_driver); + +MODULE_AUTHOR("Andrew Lunn "); +MODULE_DESCRIPTION("Marvell MVEBU SATA PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/marvell/phy-pxa-28nm-hsic.c b/drivers/phy/marvell/phy-pxa-28nm-hsic.c new file mode 100644 index 000000000000..234aacf4db20 --- /dev/null +++ b/drivers/phy/marvell/phy-pxa-28nm-hsic.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2015 Linaro, Ltd. + * Rob Herring + * + * Based on vendor driver: + * Copyright (C) 2013 Marvell Inc. + * Author: Chao Xie + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PHY_28NM_HSIC_CTRL 0x08 +#define PHY_28NM_HSIC_IMPCAL_CAL 0x18 +#define PHY_28NM_HSIC_PLL_CTRL01 0x1c +#define PHY_28NM_HSIC_PLL_CTRL2 0x20 +#define PHY_28NM_HSIC_INT 0x28 + +#define PHY_28NM_HSIC_PLL_SELLPFR_SHIFT 26 +#define PHY_28NM_HSIC_PLL_FBDIV_SHIFT 0 +#define PHY_28NM_HSIC_PLL_REFDIV_SHIFT 9 + +#define PHY_28NM_HSIC_S2H_PU_PLL BIT(10) +#define PHY_28NM_HSIC_H2S_PLL_LOCK BIT(15) +#define PHY_28NM_HSIC_S2H_HSIC_EN BIT(7) +#define S2H_DRV_SE0_4RESUME BIT(14) +#define PHY_28NM_HSIC_H2S_IMPCAL_DONE BIT(27) + +#define PHY_28NM_HSIC_CONNECT_INT BIT(1) +#define PHY_28NM_HSIC_HS_READY_INT BIT(2) + +struct mv_hsic_phy { + struct phy *phy; + struct platform_device *pdev; + void __iomem *base; + struct clk *clk; +}; + +static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) +{ + timeout += jiffies; + while (time_is_after_eq_jiffies(timeout)) { + if ((readl(reg) & mask) == mask) + return true; + msleep(1); + } + return false; +} + +static int mv_hsic_phy_init(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + struct platform_device *pdev = mv_phy->pdev; + void __iomem *base = mv_phy->base; + + clk_prepare_enable(mv_phy->clk); + + /* Set reference clock */ + writel(0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT | + 0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT | + 0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT, + base + PHY_28NM_HSIC_PLL_CTRL01); + + /* Turn on PLL */ + writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) | + PHY_28NM_HSIC_S2H_PU_PLL, + base + PHY_28NM_HSIC_PLL_CTRL2); + + /* Make sure PHY PLL is locked */ + if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2, + PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) { + dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS."); + clk_disable_unprepare(mv_phy->clk); + return -ETIMEDOUT; + } + + return 0; +} + +static int mv_hsic_phy_power_on(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + struct platform_device *pdev = mv_phy->pdev; + void __iomem *base = mv_phy->base; + u32 reg; + + reg = readl(base + PHY_28NM_HSIC_CTRL); + /* Avoid SE0 state when resume for some device will take it as reset */ + reg &= ~S2H_DRV_SE0_4RESUME; + reg |= PHY_28NM_HSIC_S2H_HSIC_EN; /* Enable HSIC PHY */ + writel(reg, base + PHY_28NM_HSIC_CTRL); + + /* + * Calibration Timing + * ____________________________ + * CAL START ___| + * ____________________ + * CAL_DONE ___________| + * | 400us | + */ + + /* Make sure PHY Calibration is ready */ + if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL, + PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) { + dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS."); + return -ETIMEDOUT; + } + + /* Waiting for HSIC connect int*/ + if (!wait_for_reg(base + PHY_28NM_HSIC_INT, + PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) { + dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout."); + return -ETIMEDOUT; + } + + return 0; +} + +static int mv_hsic_phy_power_off(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + writel(readl(base + PHY_28NM_HSIC_CTRL) & ~PHY_28NM_HSIC_S2H_HSIC_EN, + base + PHY_28NM_HSIC_CTRL); + + return 0; +} + +static int mv_hsic_phy_exit(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + /* Turn off PLL */ + writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) & + ~PHY_28NM_HSIC_S2H_PU_PLL, + base + PHY_28NM_HSIC_PLL_CTRL2); + + clk_disable_unprepare(mv_phy->clk); + return 0; +} + + +static const struct phy_ops hsic_ops = { + .init = mv_hsic_phy_init, + .power_on = mv_hsic_phy_power_on, + .power_off = mv_hsic_phy_power_off, + .exit = mv_hsic_phy_exit, + .owner = THIS_MODULE, +}; + +static int mv_hsic_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct mv_hsic_phy *mv_phy; + struct resource *r; + + mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); + if (!mv_phy) + return -ENOMEM; + + mv_phy->pdev = pdev; + + mv_phy->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mv_phy->clk)) { + dev_err(&pdev->dev, "failed to get clock.\n"); + return PTR_ERR(mv_phy->clk); + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mv_phy->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(mv_phy->base)) + return PTR_ERR(mv_phy->base); + + mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &hsic_ops); + if (IS_ERR(mv_phy->phy)) + return PTR_ERR(mv_phy->phy); + + phy_set_drvdata(mv_phy->phy, mv_phy); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id mv_hsic_phy_dt_match[] = { + { .compatible = "marvell,pxa1928-hsic-phy", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mv_hsic_phy_dt_match); + +static struct platform_driver mv_hsic_phy_driver = { + .probe = mv_hsic_phy_probe, + .driver = { + .name = "mv-hsic-phy", + .of_match_table = of_match_ptr(mv_hsic_phy_dt_match), + }, +}; +module_platform_driver(mv_hsic_phy_driver); + +MODULE_AUTHOR("Rob Herring "); +MODULE_DESCRIPTION("Marvell HSIC phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/marvell/phy-pxa-28nm-usb2.c b/drivers/phy/marvell/phy-pxa-28nm-usb2.c new file mode 100644 index 000000000000..37e9c8ca4983 --- /dev/null +++ b/drivers/phy/marvell/phy-pxa-28nm-usb2.c @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2015 Linaro, Ltd. + * Rob Herring + * + * Based on vendor driver: + * Copyright (C) 2013 Marvell Inc. + * Author: Chao Xie + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* USB PXA1928 PHY mapping */ +#define PHY_28NM_PLL_REG0 0x0 +#define PHY_28NM_PLL_REG1 0x4 +#define PHY_28NM_CAL_REG 0x8 +#define PHY_28NM_TX_REG0 0x0c +#define PHY_28NM_TX_REG1 0x10 +#define PHY_28NM_RX_REG0 0x14 +#define PHY_28NM_RX_REG1 0x18 +#define PHY_28NM_DIG_REG0 0x1c +#define PHY_28NM_DIG_REG1 0x20 +#define PHY_28NM_TEST_REG0 0x24 +#define PHY_28NM_TEST_REG1 0x28 +#define PHY_28NM_MOC_REG 0x2c +#define PHY_28NM_PHY_RESERVE 0x30 +#define PHY_28NM_OTG_REG 0x34 +#define PHY_28NM_CHRG_DET 0x38 +#define PHY_28NM_CTRL_REG0 0xc4 +#define PHY_28NM_CTRL_REG1 0xc8 +#define PHY_28NM_CTRL_REG2 0xd4 +#define PHY_28NM_CTRL_REG3 0xdc + +/* PHY_28NM_PLL_REG0 */ +#define PHY_28NM_PLL_READY BIT(31) + +#define PHY_28NM_PLL_SELLPFR_SHIFT 28 +#define PHY_28NM_PLL_SELLPFR_MASK (0x3 << 28) + +#define PHY_28NM_PLL_FBDIV_SHIFT 16 +#define PHY_28NM_PLL_FBDIV_MASK (0x1ff << 16) + +#define PHY_28NM_PLL_ICP_SHIFT 8 +#define PHY_28NM_PLL_ICP_MASK (0x7 << 8) + +#define PHY_28NM_PLL_REFDIV_SHIFT 0 +#define PHY_28NM_PLL_REFDIV_MASK 0x7f + +/* PHY_28NM_PLL_REG1 */ +#define PHY_28NM_PLL_PU_BY_REG BIT(1) + +#define PHY_28NM_PLL_PU_PLL BIT(0) + +/* PHY_28NM_CAL_REG */ +#define PHY_28NM_PLL_PLLCAL_DONE BIT(31) + +#define PHY_28NM_PLL_IMPCAL_DONE BIT(23) + +#define PHY_28NM_PLL_KVCO_SHIFT 16 +#define PHY_28NM_PLL_KVCO_MASK (0x7 << 16) + +#define PHY_28NM_PLL_CAL12_SHIFT 20 +#define PHY_28NM_PLL_CAL12_MASK (0x3 << 20) + +#define PHY_28NM_IMPCAL_VTH_SHIFT 8 +#define PHY_28NM_IMPCAL_VTH_MASK (0x7 << 8) + +#define PHY_28NM_PLLCAL_START_SHIFT 22 +#define PHY_28NM_IMPCAL_START_SHIFT 13 + +/* PHY_28NM_TX_REG0 */ +#define PHY_28NM_TX_PU_BY_REG BIT(25) + +#define PHY_28NM_TX_PU_ANA BIT(24) + +#define PHY_28NM_TX_AMP_SHIFT 20 +#define PHY_28NM_TX_AMP_MASK (0x7 << 20) + +/* PHY_28NM_RX_REG0 */ +#define PHY_28NM_RX_SQ_THRESH_SHIFT 0 +#define PHY_28NM_RX_SQ_THRESH_MASK (0xf << 0) + +/* PHY_28NM_RX_REG1 */ +#define PHY_28NM_RX_SQCAL_DONE BIT(31) + +/* PHY_28NM_DIG_REG0 */ +#define PHY_28NM_DIG_BITSTAFFING_ERR BIT(31) +#define PHY_28NM_DIG_SYNC_ERR BIT(30) + +#define PHY_28NM_DIG_SQ_FILT_SHIFT 16 +#define PHY_28NM_DIG_SQ_FILT_MASK (0x7 << 16) + +#define PHY_28NM_DIG_SQ_BLK_SHIFT 12 +#define PHY_28NM_DIG_SQ_BLK_MASK (0x7 << 12) + +#define PHY_28NM_DIG_SYNC_NUM_SHIFT 0 +#define PHY_28NM_DIG_SYNC_NUM_MASK (0x3 << 0) + +#define PHY_28NM_PLL_LOCK_BYPASS BIT(7) + +/* PHY_28NM_OTG_REG */ +#define PHY_28NM_OTG_CONTROL_BY_PIN BIT(5) +#define PHY_28NM_OTG_PU_OTG BIT(4) + +#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DM_SHIFT_28 13 +#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DP_SHIFT_28 12 +#define PHY_28NM_CHGDTC_VSRC_CHARGE_SHIFT_28 10 +#define PHY_28NM_CHGDTC_VDAT_CHARGE_SHIFT_28 8 +#define PHY_28NM_CHGDTC_CDP_DM_AUTO_SWITCH_SHIFT_28 7 +#define PHY_28NM_CHGDTC_DP_DM_SWAP_SHIFT_28 6 +#define PHY_28NM_CHGDTC_PU_CHRG_DTC_SHIFT_28 5 +#define PHY_28NM_CHGDTC_PD_EN_SHIFT_28 4 +#define PHY_28NM_CHGDTC_DCP_EN_SHIFT_28 3 +#define PHY_28NM_CHGDTC_CDP_EN_SHIFT_28 2 +#define PHY_28NM_CHGDTC_TESTMON_CHRGDTC_SHIFT_28 0 + +#define PHY_28NM_CTRL1_CHRG_DTC_OUT_SHIFT_28 4 +#define PHY_28NM_CTRL1_VBUSDTC_OUT_SHIFT_28 2 + +#define PHY_28NM_CTRL3_OVERWRITE BIT(0) +#define PHY_28NM_CTRL3_VBUS_VALID BIT(4) +#define PHY_28NM_CTRL3_AVALID BIT(5) +#define PHY_28NM_CTRL3_BVALID BIT(6) + +struct mv_usb2_phy { + struct phy *phy; + struct platform_device *pdev; + void __iomem *base; + struct clk *clk; +}; + +static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) +{ + timeout += jiffies; + while (time_is_after_eq_jiffies(timeout)) { + if ((readl(reg) & mask) == mask) + return true; + msleep(1); + } + return false; +} + +static int mv_usb2_phy_28nm_init(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + struct platform_device *pdev = mv_phy->pdev; + void __iomem *base = mv_phy->base; + u32 reg; + int ret; + + clk_prepare_enable(mv_phy->clk); + + /* PHY_28NM_PLL_REG0 */ + reg = readl(base + PHY_28NM_PLL_REG0) & + ~(PHY_28NM_PLL_SELLPFR_MASK | PHY_28NM_PLL_FBDIV_MASK + | PHY_28NM_PLL_ICP_MASK | PHY_28NM_PLL_REFDIV_MASK); + writel(reg | (0x1 << PHY_28NM_PLL_SELLPFR_SHIFT + | 0xf0 << PHY_28NM_PLL_FBDIV_SHIFT + | 0x3 << PHY_28NM_PLL_ICP_SHIFT + | 0xd << PHY_28NM_PLL_REFDIV_SHIFT), + base + PHY_28NM_PLL_REG0); + + /* PHY_28NM_PLL_REG1 */ + reg = readl(base + PHY_28NM_PLL_REG1); + writel(reg | PHY_28NM_PLL_PU_PLL | PHY_28NM_PLL_PU_BY_REG, + base + PHY_28NM_PLL_REG1); + + /* PHY_28NM_TX_REG0 */ + reg = readl(base + PHY_28NM_TX_REG0) & ~PHY_28NM_TX_AMP_MASK; + writel(reg | PHY_28NM_TX_PU_BY_REG | 0x3 << PHY_28NM_TX_AMP_SHIFT | + PHY_28NM_TX_PU_ANA, + base + PHY_28NM_TX_REG0); + + /* PHY_28NM_RX_REG0 */ + reg = readl(base + PHY_28NM_RX_REG0) & ~PHY_28NM_RX_SQ_THRESH_MASK; + writel(reg | 0xa << PHY_28NM_RX_SQ_THRESH_SHIFT, + base + PHY_28NM_RX_REG0); + + /* PHY_28NM_DIG_REG0 */ + reg = readl(base + PHY_28NM_DIG_REG0) & + ~(PHY_28NM_DIG_BITSTAFFING_ERR | PHY_28NM_DIG_SYNC_ERR | + PHY_28NM_DIG_SQ_FILT_MASK | PHY_28NM_DIG_SQ_BLK_MASK | + PHY_28NM_DIG_SYNC_NUM_MASK); + writel(reg | (0x1 << PHY_28NM_DIG_SYNC_NUM_SHIFT | + PHY_28NM_PLL_LOCK_BYPASS), + base + PHY_28NM_DIG_REG0); + + /* PHY_28NM_OTG_REG */ + reg = readl(base + PHY_28NM_OTG_REG) | PHY_28NM_OTG_PU_OTG; + writel(reg & ~PHY_28NM_OTG_CONTROL_BY_PIN, base + PHY_28NM_OTG_REG); + + /* + * Calibration Timing + * ____________________________ + * CAL START ___| + * ____________________ + * CAL_DONE ___________| + * | 400us | + */ + + /* Make sure PHY Calibration is ready */ + if (!wait_for_reg(base + PHY_28NM_CAL_REG, + PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE, + HZ / 10)) { + dev_warn(&pdev->dev, "USB PHY PLL calibrate not done after 100mS."); + ret = -ETIMEDOUT; + goto err_clk; + } + if (!wait_for_reg(base + PHY_28NM_RX_REG1, + PHY_28NM_RX_SQCAL_DONE, HZ / 10)) { + dev_warn(&pdev->dev, "USB PHY RX SQ calibrate not done after 100mS."); + ret = -ETIMEDOUT; + goto err_clk; + } + /* Make sure PHY PLL is ready */ + if (!wait_for_reg(base + PHY_28NM_PLL_REG0, + PHY_28NM_PLL_READY, HZ / 10)) { + dev_warn(&pdev->dev, "PLL_READY not set after 100mS."); + ret = -ETIMEDOUT; + goto err_clk; + } + + return 0; +err_clk: + clk_disable_unprepare(mv_phy->clk); + return ret; +} + +static int mv_usb2_phy_28nm_power_on(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + writel(readl(base + PHY_28NM_CTRL_REG3) | + (PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID | + PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID), + base + PHY_28NM_CTRL_REG3); + + return 0; +} + +static int mv_usb2_phy_28nm_power_off(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + writel(readl(base + PHY_28NM_CTRL_REG3) | + ~(PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID + | PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID), + base + PHY_28NM_CTRL_REG3); + + return 0; +} + +static int mv_usb2_phy_28nm_exit(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + unsigned int val; + + val = readw(base + PHY_28NM_PLL_REG1); + val &= ~PHY_28NM_PLL_PU_PLL; + writew(val, base + PHY_28NM_PLL_REG1); + + /* power down PHY Analog part */ + val = readw(base + PHY_28NM_TX_REG0); + val &= ~PHY_28NM_TX_PU_ANA; + writew(val, base + PHY_28NM_TX_REG0); + + /* power down PHY OTG part */ + val = readw(base + PHY_28NM_OTG_REG); + val &= ~PHY_28NM_OTG_PU_OTG; + writew(val, base + PHY_28NM_OTG_REG); + + clk_disable_unprepare(mv_phy->clk); + return 0; +} + +static const struct phy_ops usb_ops = { + .init = mv_usb2_phy_28nm_init, + .power_on = mv_usb2_phy_28nm_power_on, + .power_off = mv_usb2_phy_28nm_power_off, + .exit = mv_usb2_phy_28nm_exit, + .owner = THIS_MODULE, +}; + +static int mv_usb2_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct mv_usb2_phy *mv_phy; + struct resource *r; + + mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); + if (!mv_phy) + return -ENOMEM; + + mv_phy->pdev = pdev; + + mv_phy->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mv_phy->clk)) { + dev_err(&pdev->dev, "failed to get clock.\n"); + return PTR_ERR(mv_phy->clk); + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mv_phy->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(mv_phy->base)) + return PTR_ERR(mv_phy->base); + + mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &usb_ops); + if (IS_ERR(mv_phy->phy)) + return PTR_ERR(mv_phy->phy); + + phy_set_drvdata(mv_phy->phy, mv_phy); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id mv_usbphy_dt_match[] = { + { .compatible = "marvell,pxa1928-usb-phy", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mv_usbphy_dt_match); + +static struct platform_driver mv_usb2_phy_driver = { + .probe = mv_usb2_phy_probe, + .driver = { + .name = "mv-usb2-phy", + .of_match_table = of_match_ptr(mv_usbphy_dt_match), + }, +}; +module_platform_driver(mv_usb2_phy_driver); + +MODULE_AUTHOR("Rob Herring "); +MODULE_DESCRIPTION("Marvell USB2 phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-armada375-usb2.c b/drivers/phy/phy-armada375-usb2.c deleted file mode 100644 index 1a3db288c0a9..000000000000 --- a/drivers/phy/phy-armada375-usb2.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * USB cluster support for Armada 375 platform. - * - * Copyright (C) 2014 Marvell - * - * Gregory CLEMENT - * - * This file is licensed under the terms of the GNU General Public - * License version 2 or later. This program is licensed "as is" - * without any warranty of any kind, whether express or implied. - * - * Armada 375 comes with an USB2 host and device controller and an - * USB3 controller. The USB cluster control register allows to manage - * common features of both USB controllers. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define USB2_PHY_CONFIG_DISABLE BIT(0) - -struct armada375_cluster_phy { - struct phy *phy; - void __iomem *reg; - bool use_usb3; - int phy_provided; -}; - -static int armada375_usb_phy_init(struct phy *phy) -{ - struct armada375_cluster_phy *cluster_phy; - u32 reg; - - cluster_phy = phy_get_drvdata(phy); - if (!cluster_phy) - return -ENODEV; - - reg = readl(cluster_phy->reg); - if (cluster_phy->use_usb3) - reg |= USB2_PHY_CONFIG_DISABLE; - else - reg &= ~USB2_PHY_CONFIG_DISABLE; - writel(reg, cluster_phy->reg); - - return 0; -} - -static const struct phy_ops armada375_usb_phy_ops = { - .init = armada375_usb_phy_init, - .owner = THIS_MODULE, -}; - -/* - * Only one controller can use this PHY. We shouldn't have the case - * when two controllers want to use this PHY. But if this case occurs - * then we provide a phy to the first one and return an error for the - * next one. This error has also to be an error returned by - * devm_phy_optional_get() so different from ENODEV for USB2. In the - * USB3 case it still optional and we use ENODEV. - */ -static struct phy *armada375_usb_phy_xlate(struct device *dev, - struct of_phandle_args *args) -{ - struct armada375_cluster_phy *cluster_phy = dev_get_drvdata(dev); - - if (!cluster_phy) - return ERR_PTR(-ENODEV); - - /* - * Either the phy had never been requested and then the first - * usb claiming it can get it, or it had already been - * requested in this case, we only allow to use it with the - * same configuration. - */ - if (WARN_ON((cluster_phy->phy_provided != PHY_NONE) && - (cluster_phy->phy_provided != args->args[0]))) { - dev_err(dev, "This PHY has already been provided!\n"); - dev_err(dev, "Check your device tree, only one controller can use it\n."); - if (args->args[0] == PHY_TYPE_USB2) - return ERR_PTR(-EBUSY); - else - return ERR_PTR(-ENODEV); - } - - if (args->args[0] == PHY_TYPE_USB2) - cluster_phy->use_usb3 = false; - else if (args->args[0] == PHY_TYPE_USB3) - cluster_phy->use_usb3 = true; - else { - dev_err(dev, "Invalid PHY mode\n"); - return ERR_PTR(-ENODEV); - } - - /* Store which phy mode is used for next test */ - cluster_phy->phy_provided = args->args[0]; - - return cluster_phy->phy; -} - -static int armada375_usb_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct phy *phy; - struct phy_provider *phy_provider; - void __iomem *usb_cluster_base; - struct resource *res; - struct armada375_cluster_phy *cluster_phy; - - cluster_phy = devm_kzalloc(dev, sizeof(*cluster_phy), GFP_KERNEL); - if (!cluster_phy) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - usb_cluster_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(usb_cluster_base)) - return PTR_ERR(usb_cluster_base); - - phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops); - if (IS_ERR(phy)) { - dev_err(dev, "failed to create PHY\n"); - return PTR_ERR(phy); - } - - cluster_phy->phy = phy; - cluster_phy->reg = usb_cluster_base; - - dev_set_drvdata(dev, cluster_phy); - phy_set_drvdata(phy, cluster_phy); - - phy_provider = devm_of_phy_provider_register(&pdev->dev, - armada375_usb_phy_xlate); - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id of_usb_cluster_table[] = { - { .compatible = "marvell,armada-375-usb-cluster", }, - { /* end of list */ }, -}; -MODULE_DEVICE_TABLE(of, of_usb_cluster_table); - -static struct platform_driver armada375_usb_phy_driver = { - .probe = armada375_usb_phy_probe, - .driver = { - .of_match_table = of_usb_cluster_table, - .name = "armada-375-usb-cluster", - } -}; -module_platform_driver(armada375_usb_phy_driver); - -MODULE_DESCRIPTION("Armada 375 USB cluster driver"); -MODULE_AUTHOR("Gregory CLEMENT "); -MODULE_LICENSE("GPL"); diff --git a/drivers/phy/phy-bcm-cygnus-pcie.c b/drivers/phy/phy-bcm-cygnus-pcie.c deleted file mode 100644 index 0f4ac5d63cff..000000000000 --- a/drivers/phy/phy-bcm-cygnus-pcie.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include - -#define PCIE_CFG_OFFSET 0x00 -#define PCIE1_PHY_IDDQ_SHIFT 10 -#define PCIE0_PHY_IDDQ_SHIFT 2 - -enum cygnus_pcie_phy_id { - CYGNUS_PHY_PCIE0 = 0, - CYGNUS_PHY_PCIE1, - MAX_NUM_PHYS, -}; - -struct cygnus_pcie_phy_core; - -/** - * struct cygnus_pcie_phy - Cygnus PCIe PHY device - * @core: pointer to the Cygnus PCIe PHY core control - * @id: internal ID to identify the Cygnus PCIe PHY - * @phy: pointer to the kernel PHY device - */ -struct cygnus_pcie_phy { - struct cygnus_pcie_phy_core *core; - enum cygnus_pcie_phy_id id; - struct phy *phy; -}; - -/** - * struct cygnus_pcie_phy_core - Cygnus PCIe PHY core control - * @dev: pointer to device - * @base: base register - * @lock: mutex to protect access to individual PHYs - * @phys: pointer to Cygnus PHY device - */ -struct cygnus_pcie_phy_core { - struct device *dev; - void __iomem *base; - struct mutex lock; - struct cygnus_pcie_phy phys[MAX_NUM_PHYS]; -}; - -static int cygnus_pcie_power_config(struct cygnus_pcie_phy *phy, bool enable) -{ - struct cygnus_pcie_phy_core *core = phy->core; - unsigned shift; - u32 val; - - mutex_lock(&core->lock); - - switch (phy->id) { - case CYGNUS_PHY_PCIE0: - shift = PCIE0_PHY_IDDQ_SHIFT; - break; - - case CYGNUS_PHY_PCIE1: - shift = PCIE1_PHY_IDDQ_SHIFT; - break; - - default: - mutex_unlock(&core->lock); - dev_err(core->dev, "PCIe PHY %d invalid\n", phy->id); - return -EINVAL; - } - - if (enable) { - val = readl(core->base + PCIE_CFG_OFFSET); - val &= ~BIT(shift); - writel(val, core->base + PCIE_CFG_OFFSET); - /* - * Wait 50 ms for the PCIe Serdes to stabilize after the analog - * front end is brought up - */ - msleep(50); - } else { - val = readl(core->base + PCIE_CFG_OFFSET); - val |= BIT(shift); - writel(val, core->base + PCIE_CFG_OFFSET); - } - - mutex_unlock(&core->lock); - dev_dbg(core->dev, "PCIe PHY %d %s\n", phy->id, - enable ? "enabled" : "disabled"); - return 0; -} - -static int cygnus_pcie_phy_power_on(struct phy *p) -{ - struct cygnus_pcie_phy *phy = phy_get_drvdata(p); - - return cygnus_pcie_power_config(phy, true); -} - -static int cygnus_pcie_phy_power_off(struct phy *p) -{ - struct cygnus_pcie_phy *phy = phy_get_drvdata(p); - - return cygnus_pcie_power_config(phy, false); -} - -static const struct phy_ops cygnus_pcie_phy_ops = { - .power_on = cygnus_pcie_phy_power_on, - .power_off = cygnus_pcie_phy_power_off, - .owner = THIS_MODULE, -}; - -static int cygnus_pcie_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *node = dev->of_node, *child; - struct cygnus_pcie_phy_core *core; - struct phy_provider *provider; - struct resource *res; - unsigned cnt = 0; - int ret; - - if (of_get_child_count(node) == 0) { - dev_err(dev, "PHY no child node\n"); - return -ENODEV; - } - - core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); - if (!core) - return -ENOMEM; - - core->dev = dev; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - core->base = devm_ioremap_resource(dev, res); - if (IS_ERR(core->base)) - return PTR_ERR(core->base); - - mutex_init(&core->lock); - - for_each_available_child_of_node(node, child) { - unsigned int id; - struct cygnus_pcie_phy *p; - - if (of_property_read_u32(child, "reg", &id)) { - dev_err(dev, "missing reg property for %s\n", - child->name); - ret = -EINVAL; - goto put_child; - } - - if (id >= MAX_NUM_PHYS) { - dev_err(dev, "invalid PHY id: %u\n", id); - ret = -EINVAL; - goto put_child; - } - - if (core->phys[id].phy) { - dev_err(dev, "duplicated PHY id: %u\n", id); - ret = -EINVAL; - goto put_child; - } - - p = &core->phys[id]; - p->phy = devm_phy_create(dev, child, &cygnus_pcie_phy_ops); - if (IS_ERR(p->phy)) { - dev_err(dev, "failed to create PHY\n"); - ret = PTR_ERR(p->phy); - goto put_child; - } - - p->core = core; - p->id = id; - phy_set_drvdata(p->phy, p); - cnt++; - } - - dev_set_drvdata(dev, core); - - provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(provider)) { - dev_err(dev, "failed to register PHY provider\n"); - return PTR_ERR(provider); - } - - dev_dbg(dev, "registered %u PCIe PHY(s)\n", cnt); - - return 0; -put_child: - of_node_put(child); - return ret; -} - -static const struct of_device_id cygnus_pcie_phy_match_table[] = { - { .compatible = "brcm,cygnus-pcie-phy" }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, cygnus_pcie_phy_match_table); - -static struct platform_driver cygnus_pcie_phy_driver = { - .driver = { - .name = "cygnus-pcie-phy", - .of_match_table = cygnus_pcie_phy_match_table, - }, - .probe = cygnus_pcie_phy_probe, -}; -module_platform_driver(cygnus_pcie_phy_driver); - -MODULE_AUTHOR("Ray Jui "); -MODULE_DESCRIPTION("Broadcom Cygnus PCIe PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-bcm-kona-usb2.c b/drivers/phy/phy-bcm-kona-usb2.c deleted file mode 100644 index 7b67fe49e30b..000000000000 --- a/drivers/phy/phy-bcm-kona-usb2.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * phy-bcm-kona-usb2.c - Broadcom Kona USB2 Phy Driver - * - * Copyright (C) 2013 Linaro Limited - * Matt Porter - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define OTGCTL (0) -#define OTGCTL_OTGSTAT2 BIT(31) -#define OTGCTL_OTGSTAT1 BIT(30) -#define OTGCTL_PRST_N_SW BIT(11) -#define OTGCTL_HRESET_N BIT(10) -#define OTGCTL_UTMI_LINE_STATE1 BIT(9) -#define OTGCTL_UTMI_LINE_STATE0 BIT(8) - -#define P1CTL (8) -#define P1CTL_SOFT_RESET BIT(1) -#define P1CTL_NON_DRIVING BIT(0) - -struct bcm_kona_usb { - void __iomem *regs; -}; - -static void bcm_kona_usb_phy_power(struct bcm_kona_usb *phy, int on) -{ - u32 val; - - val = readl(phy->regs + OTGCTL); - if (on) { - /* Configure and power PHY */ - val &= ~(OTGCTL_OTGSTAT2 | OTGCTL_OTGSTAT1 | - OTGCTL_UTMI_LINE_STATE1 | OTGCTL_UTMI_LINE_STATE0); - val |= OTGCTL_PRST_N_SW | OTGCTL_HRESET_N; - } else { - val &= ~(OTGCTL_PRST_N_SW | OTGCTL_HRESET_N); - } - writel(val, phy->regs + OTGCTL); -} - -static int bcm_kona_usb_phy_init(struct phy *gphy) -{ - struct bcm_kona_usb *phy = phy_get_drvdata(gphy); - u32 val; - - /* Soft reset PHY */ - val = readl(phy->regs + P1CTL); - val &= ~P1CTL_NON_DRIVING; - val |= P1CTL_SOFT_RESET; - writel(val, phy->regs + P1CTL); - writel(val & ~P1CTL_SOFT_RESET, phy->regs + P1CTL); - /* Reset needs to be asserted for 2ms */ - mdelay(2); - writel(val | P1CTL_SOFT_RESET, phy->regs + P1CTL); - - return 0; -} - -static int bcm_kona_usb_phy_power_on(struct phy *gphy) -{ - struct bcm_kona_usb *phy = phy_get_drvdata(gphy); - - bcm_kona_usb_phy_power(phy, 1); - - return 0; -} - -static int bcm_kona_usb_phy_power_off(struct phy *gphy) -{ - struct bcm_kona_usb *phy = phy_get_drvdata(gphy); - - bcm_kona_usb_phy_power(phy, 0); - - return 0; -} - -static const struct phy_ops ops = { - .init = bcm_kona_usb_phy_init, - .power_on = bcm_kona_usb_phy_power_on, - .power_off = bcm_kona_usb_phy_power_off, - .owner = THIS_MODULE, -}; - -static int bcm_kona_usb2_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct bcm_kona_usb *phy; - struct resource *res; - struct phy *gphy; - struct phy_provider *phy_provider; - - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(phy->regs)) - return PTR_ERR(phy->regs); - - platform_set_drvdata(pdev, phy); - - gphy = devm_phy_create(dev, NULL, &ops); - if (IS_ERR(gphy)) - return PTR_ERR(gphy); - - /* The Kona PHY supports an 8-bit wide UTMI interface */ - phy_set_bus_width(gphy, 8); - - phy_set_drvdata(gphy, phy); - - phy_provider = devm_of_phy_provider_register(dev, - of_phy_simple_xlate); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id bcm_kona_usb2_dt_ids[] = { - { .compatible = "brcm,kona-usb2-phy" }, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(of, bcm_kona_usb2_dt_ids); - -static struct platform_driver bcm_kona_usb2_driver = { - .probe = bcm_kona_usb2_probe, - .driver = { - .name = "bcm-kona-usb2", - .of_match_table = bcm_kona_usb2_dt_ids, - }, -}; - -module_platform_driver(bcm_kona_usb2_driver); - -MODULE_ALIAS("platform:bcm-kona-usb2"); -MODULE_AUTHOR("Matt Porter "); -MODULE_DESCRIPTION("BCM Kona USB 2.0 PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-bcm-ns-usb2.c b/drivers/phy/phy-bcm-ns-usb2.c deleted file mode 100644 index 58dff80e9386..000000000000 --- a/drivers/phy/phy-bcm-ns-usb2.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Broadcom Northstar USB 2.0 PHY Driver - * - * Copyright (C) 2016 Rafał Miłecki - * - * 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 - -struct bcm_ns_usb2 { - struct device *dev; - struct clk *ref_clk; - struct phy *phy; - void __iomem *dmu; -}; - -static int bcm_ns_usb2_phy_init(struct phy *phy) -{ - struct bcm_ns_usb2 *usb2 = phy_get_drvdata(phy); - struct device *dev = usb2->dev; - void __iomem *dmu = usb2->dmu; - u32 ref_clk_rate, usb2ctl, usb_pll_ndiv, usb_pll_pdiv; - int err = 0; - - err = clk_prepare_enable(usb2->ref_clk); - if (err < 0) { - dev_err(dev, "Failed to prepare ref clock: %d\n", err); - goto err_out; - } - - ref_clk_rate = clk_get_rate(usb2->ref_clk); - if (!ref_clk_rate) { - dev_err(dev, "Failed to get ref clock rate\n"); - err = -EINVAL; - goto err_clk_off; - } - - usb2ctl = readl(dmu + BCMA_DMU_CRU_USB2_CONTROL); - - if (usb2ctl & BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK) { - usb_pll_pdiv = usb2ctl; - usb_pll_pdiv &= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK; - usb_pll_pdiv >>= BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_SHIFT; - } else { - usb_pll_pdiv = 1 << 3; - } - - /* Calculate ndiv based on a solid 1920 MHz that is for USB2 PHY */ - usb_pll_ndiv = (1920000000 * usb_pll_pdiv) / ref_clk_rate; - - /* Unlock DMU PLL settings with some magic value */ - writel(0x0000ea68, dmu + BCMA_DMU_CRU_CLKSET_KEY); - - /* Write USB 2.0 PLL control setting */ - usb2ctl &= ~BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK; - usb2ctl |= usb_pll_ndiv << BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT; - writel(usb2ctl, dmu + BCMA_DMU_CRU_USB2_CONTROL); - - /* Lock DMU PLL settings */ - writel(0x00000000, dmu + BCMA_DMU_CRU_CLKSET_KEY); - -err_clk_off: - clk_disable_unprepare(usb2->ref_clk); -err_out: - return err; -} - -static const struct phy_ops ops = { - .init = bcm_ns_usb2_phy_init, - .owner = THIS_MODULE, -}; - -static int bcm_ns_usb2_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct bcm_ns_usb2 *usb2; - struct resource *res; - struct phy_provider *phy_provider; - - usb2 = devm_kzalloc(&pdev->dev, sizeof(*usb2), GFP_KERNEL); - if (!usb2) - return -ENOMEM; - usb2->dev = dev; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmu"); - usb2->dmu = devm_ioremap_resource(dev, res); - if (IS_ERR(usb2->dmu)) { - dev_err(dev, "Failed to map DMU regs\n"); - return PTR_ERR(usb2->dmu); - } - - usb2->ref_clk = devm_clk_get(dev, "phy-ref-clk"); - if (IS_ERR(usb2->ref_clk)) { - dev_err(dev, "Clock not defined\n"); - return PTR_ERR(usb2->ref_clk); - } - - usb2->phy = devm_phy_create(dev, NULL, &ops); - if (IS_ERR(usb2->phy)) - return PTR_ERR(usb2->phy); - - phy_set_drvdata(usb2->phy, usb2); - platform_set_drvdata(pdev, usb2); - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id bcm_ns_usb2_id_table[] = { - { .compatible = "brcm,ns-usb2-phy", }, - {}, -}; -MODULE_DEVICE_TABLE(of, bcm_ns_usb2_id_table); - -static struct platform_driver bcm_ns_usb2_driver = { - .probe = bcm_ns_usb2_probe, - .driver = { - .name = "bcm_ns_usb2", - .of_match_table = bcm_ns_usb2_id_table, - }, -}; -module_platform_driver(bcm_ns_usb2_driver); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-bcm-ns-usb3.c b/drivers/phy/phy-bcm-ns-usb3.c deleted file mode 100644 index 22b5e7047fa6..000000000000 --- a/drivers/phy/phy-bcm-ns-usb3.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Broadcom Northstar USB 3.0 PHY Driver - * - * Copyright (C) 2016 Rafał Miłecki - * Copyright (C) 2016 Broadcom - * - * All magic values used for initialization (and related comments) were obtained - * from Broadcom's SDK: - * Copyright (c) Broadcom Corp, 2012 - * - * 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 - -#define BCM_NS_USB3_MII_MNG_TIMEOUT_US 1000 /* usecs */ - -#define BCM_NS_USB3_PHY_BASE_ADDR_REG 0x1f -#define BCM_NS_USB3_PHY_PLL30_BLOCK 0x8000 -#define BCM_NS_USB3_PHY_TX_PMD_BLOCK 0x8040 -#define BCM_NS_USB3_PHY_PIPE_BLOCK 0x8060 - -/* Registers of PLL30 block */ -#define BCM_NS_USB3_PLL_CONTROL 0x01 -#define BCM_NS_USB3_PLLA_CONTROL0 0x0a -#define BCM_NS_USB3_PLLA_CONTROL1 0x0b - -/* Registers of TX PMD block */ -#define BCM_NS_USB3_TX_PMD_CONTROL1 0x01 - -/* Registers of PIPE block */ -#define BCM_NS_USB3_LFPS_CMP 0x02 -#define BCM_NS_USB3_LFPS_DEGLITCH 0x03 - -enum bcm_ns_family { - BCM_NS_UNKNOWN, - BCM_NS_AX, - BCM_NS_BX, -}; - -struct bcm_ns_usb3 { - struct device *dev; - enum bcm_ns_family family; - void __iomem *dmp; - void __iomem *ccb_mii; - struct phy *phy; -}; - -static const struct of_device_id bcm_ns_usb3_id_table[] = { - { - .compatible = "brcm,ns-ax-usb3-phy", - .data = (int *)BCM_NS_AX, - }, - { - .compatible = "brcm,ns-bx-usb3-phy", - .data = (int *)BCM_NS_BX, - }, - {}, -}; -MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table); - -static int bcm_ns_usb3_wait_reg(struct bcm_ns_usb3 *usb3, void __iomem *addr, - u32 mask, u32 value, unsigned long timeout) -{ - unsigned long deadline = jiffies + timeout; - u32 val; - - do { - val = readl(addr); - if ((val & mask) == value) - return 0; - cpu_relax(); - udelay(10); - } while (!time_after_eq(jiffies, deadline)); - - dev_err(usb3->dev, "Timeout waiting for register %p\n", addr); - - return -EBUSY; -} - -static inline int bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3 *usb3) -{ - return bcm_ns_usb3_wait_reg(usb3, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL, - 0x0100, 0x0000, - usecs_to_jiffies(BCM_NS_USB3_MII_MNG_TIMEOUT_US)); -} - -static int bcm_ns_usb3_mdio_phy_write(struct bcm_ns_usb3 *usb3, u16 reg, - u16 value) -{ - u32 tmp = 0; - int err; - - err = bcm_ns_usb3_mii_mng_wait_idle(usb3); - if (err < 0) { - dev_err(usb3->dev, "Couldn't write 0x%08x value\n", value); - return err; - } - - /* TODO: Use a proper MDIO bus layer */ - tmp |= 0x58020000; /* Magic value for MDIO PHY write */ - tmp |= reg << 18; - tmp |= value; - writel(tmp, usb3->ccb_mii + BCMA_CCB_MII_MNG_CMD_DATA); - - return 0; -} - -static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3) -{ - int err; - - /* Enable MDIO. Setting MDCDIV as 26 */ - writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL); - - /* Wait for MDIO? */ - udelay(2); - - /* USB3 PLL Block */ - err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, - BCM_NS_USB3_PHY_PLL30_BLOCK); - if (err < 0) - return err; - - /* Assert Ana_Pllseq start */ - bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x1000); - - /* Assert CML Divider ratio to 26 */ - bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400); - - /* Asserting PLL Reset */ - bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0xc000); - - /* Deaaserting PLL Reset */ - bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL1, 0x8000); - - /* Waiting MII Mgt interface idle */ - bcm_ns_usb3_mii_mng_wait_idle(usb3); - - /* Deasserting USB3 system reset */ - writel(0, usb3->dmp + BCMA_RESET_CTL); - - /* PLL frequency monitor enable */ - bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLL_CONTROL, 0x9000); - - /* PIPE Block */ - bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, - BCM_NS_USB3_PHY_PIPE_BLOCK); - - /* CMPMAX & CMPMINTH setting */ - bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_CMP, 0xf30d); - - /* DEGLITCH MIN & MAX setting */ - bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_LFPS_DEGLITCH, 0x6302); - - /* TXPMD block */ - bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, - BCM_NS_USB3_PHY_TX_PMD_BLOCK); - - /* Enabling SSC */ - bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003); - - /* Waiting MII Mgt interface idle */ - bcm_ns_usb3_mii_mng_wait_idle(usb3); - - return 0; -} - -static int bcm_ns_usb3_phy_init_ns_ax(struct bcm_ns_usb3 *usb3) -{ - int err; - - /* Enable MDIO. Setting MDCDIV as 26 */ - writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL); - - /* Wait for MDIO? */ - udelay(2); - - /* PLL30 block */ - err = bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, - BCM_NS_USB3_PHY_PLL30_BLOCK); - if (err < 0) - return err; - - bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PLLA_CONTROL0, 0x6400); - - bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, 0x80e0); - - bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x009c); - - /* Enable SSC */ - bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_PHY_BASE_ADDR_REG, - BCM_NS_USB3_PHY_TX_PMD_BLOCK); - - bcm_ns_usb3_mdio_phy_write(usb3, 0x02, 0x21d3); - - bcm_ns_usb3_mdio_phy_write(usb3, BCM_NS_USB3_TX_PMD_CONTROL1, 0x1003); - - /* Waiting MII Mgt interface idle */ - bcm_ns_usb3_mii_mng_wait_idle(usb3); - - /* Deasserting USB3 system reset */ - writel(0, usb3->dmp + BCMA_RESET_CTL); - - return 0; -} - -static int bcm_ns_usb3_phy_init(struct phy *phy) -{ - struct bcm_ns_usb3 *usb3 = phy_get_drvdata(phy); - int err; - - /* Perform USB3 system soft reset */ - writel(BCMA_RESET_CTL_RESET, usb3->dmp + BCMA_RESET_CTL); - - switch (usb3->family) { - case BCM_NS_AX: - err = bcm_ns_usb3_phy_init_ns_ax(usb3); - break; - case BCM_NS_BX: - err = bcm_ns_usb3_phy_init_ns_bx(usb3); - break; - default: - WARN_ON(1); - err = -ENOTSUPP; - } - - return err; -} - -static const struct phy_ops ops = { - .init = bcm_ns_usb3_phy_init, - .owner = THIS_MODULE, -}; - -static int bcm_ns_usb3_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - const struct of_device_id *of_id; - struct bcm_ns_usb3 *usb3; - struct resource *res; - struct phy_provider *phy_provider; - - usb3 = devm_kzalloc(dev, sizeof(*usb3), GFP_KERNEL); - if (!usb3) - return -ENOMEM; - - usb3->dev = dev; - - of_id = of_match_device(bcm_ns_usb3_id_table, dev); - if (!of_id) - return -EINVAL; - usb3->family = (enum bcm_ns_family)of_id->data; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmp"); - usb3->dmp = devm_ioremap_resource(dev, res); - if (IS_ERR(usb3->dmp)) { - dev_err(dev, "Failed to map DMP regs\n"); - return PTR_ERR(usb3->dmp); - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ccb-mii"); - usb3->ccb_mii = devm_ioremap_resource(dev, res); - if (IS_ERR(usb3->ccb_mii)) { - dev_err(dev, "Failed to map ChipCommon B MII regs\n"); - return PTR_ERR(usb3->ccb_mii); - } - - usb3->phy = devm_phy_create(dev, NULL, &ops); - if (IS_ERR(usb3->phy)) { - dev_err(dev, "Failed to create PHY\n"); - return PTR_ERR(usb3->phy); - } - - phy_set_drvdata(usb3->phy, usb3); - platform_set_drvdata(pdev, usb3); - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (!IS_ERR(phy_provider)) - dev_info(dev, "Registered Broadcom Northstar USB 3.0 PHY driver\n"); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static struct platform_driver bcm_ns_usb3_driver = { - .probe = bcm_ns_usb3_probe, - .driver = { - .name = "bcm_ns_usb3", - .of_match_table = bcm_ns_usb3_id_table, - }, -}; -module_platform_driver(bcm_ns_usb3_driver); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-bcm-ns2-pcie.c b/drivers/phy/phy-bcm-ns2-pcie.c deleted file mode 100644 index 4c7d11d2b378..000000000000 --- a/drivers/phy/phy-bcm-ns2-pcie.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2016 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include - -#define BLK_ADDR_REG_OFFSET 0x1f -#define PLL_AFE1_100MHZ_BLK 0x2100 -#define PLL_CLK_AMP_OFFSET 0x03 -#define PLL_CLK_AMP_2P05V 0x2b18 - -static int ns2_pci_phy_init(struct phy *p) -{ - struct mdio_device *mdiodev = phy_get_drvdata(p); - int rc; - - /* select the AFE 100MHz block page */ - rc = mdiobus_write(mdiodev->bus, mdiodev->addr, - BLK_ADDR_REG_OFFSET, PLL_AFE1_100MHZ_BLK); - if (rc) - goto err; - - /* set the 100 MHz reference clock amplitude to 2.05 v */ - rc = mdiobus_write(mdiodev->bus, mdiodev->addr, - PLL_CLK_AMP_OFFSET, PLL_CLK_AMP_2P05V); - if (rc) - goto err; - - return 0; - -err: - dev_err(&mdiodev->dev, "Error %d writing to phy\n", rc); - return rc; -} - -static const struct phy_ops ns2_pci_phy_ops = { - .init = ns2_pci_phy_init, - .owner = THIS_MODULE, -}; - -static int ns2_pci_phy_probe(struct mdio_device *mdiodev) -{ - struct device *dev = &mdiodev->dev; - struct phy_provider *provider; - struct phy *phy; - - phy = devm_phy_create(dev, dev->of_node, &ns2_pci_phy_ops); - if (IS_ERR(phy)) { - dev_err(dev, "failed to create Phy\n"); - return PTR_ERR(phy); - } - - phy_set_drvdata(phy, mdiodev); - - provider = devm_of_phy_provider_register(&phy->dev, - of_phy_simple_xlate); - if (IS_ERR(provider)) { - dev_err(dev, "failed to register Phy provider\n"); - return PTR_ERR(provider); - } - - dev_info(dev, "%s PHY registered\n", dev_name(dev)); - - return 0; -} - -static const struct of_device_id ns2_pci_phy_of_match[] = { - { .compatible = "brcm,ns2-pcie-phy", }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, ns2_pci_phy_of_match); - -static struct mdio_driver ns2_pci_phy_driver = { - .mdiodrv = { - .driver = { - .name = "phy-bcm-ns2-pci", - .of_match_table = ns2_pci_phy_of_match, - }, - }, - .probe = ns2_pci_phy_probe, -}; -mdio_module_driver(ns2_pci_phy_driver); - -MODULE_AUTHOR("Broadcom"); -MODULE_DESCRIPTION("Broadcom Northstar2 PCI Phy driver"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:phy-bcm-ns2-pci"); diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c deleted file mode 100644 index 2c7a57f2d595..000000000000 --- a/drivers/phy/phy-berlin-sata.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Marvell Berlin SATA PHY driver - * - * Copyright (C) 2014 Marvell Technology Group Ltd. - * - * Antoine Ténart - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include - -#define HOST_VSA_ADDR 0x0 -#define HOST_VSA_DATA 0x4 -#define PORT_SCR_CTL 0x2c -#define PORT_VSR_ADDR 0x78 -#define PORT_VSR_DATA 0x7c - -#define CONTROL_REGISTER 0x0 -#define MBUS_SIZE_CONTROL 0x4 - -#define POWER_DOWN_PHY0 BIT(6) -#define POWER_DOWN_PHY1 BIT(14) -#define MBUS_WRITE_REQUEST_SIZE_128 (BIT(2) << 16) -#define MBUS_READ_REQUEST_SIZE_128 (BIT(2) << 19) - -#define BG2_PHY_BASE 0x080 -#define BG2Q_PHY_BASE 0x200 - -/* register 0x01 */ -#define REF_FREF_SEL_25 BIT(0) -#define PHY_MODE_SATA (0x0 << 5) - -/* register 0x02 */ -#define USE_MAX_PLL_RATE BIT(12) - -/* register 0x23 */ -#define DATA_BIT_WIDTH_10 (0x0 << 10) -#define DATA_BIT_WIDTH_20 (0x1 << 10) -#define DATA_BIT_WIDTH_40 (0x2 << 10) - -/* register 0x25 */ -#define PHY_GEN_MAX_1_5 (0x0 << 10) -#define PHY_GEN_MAX_3_0 (0x1 << 10) -#define PHY_GEN_MAX_6_0 (0x2 << 10) - -struct phy_berlin_desc { - struct phy *phy; - u32 power_bit; - unsigned index; -}; - -struct phy_berlin_priv { - void __iomem *base; - spinlock_t lock; - struct clk *clk; - struct phy_berlin_desc **phys; - unsigned nphys; - u32 phy_base; -}; - -static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, - u32 phy_base, u32 reg, u32 mask, u32 val) -{ - u32 regval; - - /* select register */ - writel(phy_base + reg, ctrl_reg + PORT_VSR_ADDR); - - /* set bits */ - regval = readl(ctrl_reg + PORT_VSR_DATA); - regval &= ~mask; - regval |= val; - writel(regval, ctrl_reg + PORT_VSR_DATA); -} - -static int phy_berlin_sata_power_on(struct phy *phy) -{ - struct phy_berlin_desc *desc = phy_get_drvdata(phy); - struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); - void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80); - u32 regval; - - clk_prepare_enable(priv->clk); - - spin_lock(&priv->lock); - - /* Power on PHY */ - writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); - regval = readl(priv->base + HOST_VSA_DATA); - regval &= ~desc->power_bit; - writel(regval, priv->base + HOST_VSA_DATA); - - /* Configure MBus */ - writel(MBUS_SIZE_CONTROL, priv->base + HOST_VSA_ADDR); - regval = readl(priv->base + HOST_VSA_DATA); - regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128; - writel(regval, priv->base + HOST_VSA_DATA); - - /* set PHY mode and ref freq to 25 MHz */ - phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x01, - 0x00ff, REF_FREF_SEL_25 | PHY_MODE_SATA); - - /* set PHY up to 6 Gbps */ - phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x25, - 0x0c00, PHY_GEN_MAX_6_0); - - /* set 40 bits width */ - phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x23, - 0x0c00, DATA_BIT_WIDTH_40); - - /* use max pll rate */ - phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x02, - 0x0000, USE_MAX_PLL_RATE); - - /* set Gen3 controller speed */ - regval = readl(ctrl_reg + PORT_SCR_CTL); - regval &= ~GENMASK(7, 4); - regval |= 0x30; - writel(regval, ctrl_reg + PORT_SCR_CTL); - - spin_unlock(&priv->lock); - - clk_disable_unprepare(priv->clk); - - return 0; -} - -static int phy_berlin_sata_power_off(struct phy *phy) -{ - struct phy_berlin_desc *desc = phy_get_drvdata(phy); - struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent); - u32 regval; - - clk_prepare_enable(priv->clk); - - spin_lock(&priv->lock); - - /* Power down PHY */ - writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR); - regval = readl(priv->base + HOST_VSA_DATA); - regval |= desc->power_bit; - writel(regval, priv->base + HOST_VSA_DATA); - - spin_unlock(&priv->lock); - - clk_disable_unprepare(priv->clk); - - return 0; -} - -static struct phy *phy_berlin_sata_phy_xlate(struct device *dev, - struct of_phandle_args *args) -{ - struct phy_berlin_priv *priv = dev_get_drvdata(dev); - int i; - - if (WARN_ON(args->args[0] >= priv->nphys)) - return ERR_PTR(-ENODEV); - - for (i = 0; i < priv->nphys; i++) { - if (priv->phys[i]->index == args->args[0]) - break; - } - - if (i == priv->nphys) - return ERR_PTR(-ENODEV); - - return priv->phys[i]->phy; -} - -static const struct phy_ops phy_berlin_sata_ops = { - .power_on = phy_berlin_sata_power_on, - .power_off = phy_berlin_sata_power_off, - .owner = THIS_MODULE, -}; - -static u32 phy_berlin_power_down_bits[] = { - POWER_DOWN_PHY0, - POWER_DOWN_PHY1, -}; - -static int phy_berlin_sata_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *child; - struct phy *phy; - struct phy_provider *phy_provider; - struct phy_berlin_priv *priv; - struct resource *res; - int ret, i = 0; - u32 phy_id; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - - priv->base = devm_ioremap(dev, res->start, resource_size(res)); - if (!priv->base) - return -ENOMEM; - - priv->clk = devm_clk_get(dev, NULL); - if (IS_ERR(priv->clk)) - return PTR_ERR(priv->clk); - - priv->nphys = of_get_child_count(dev->of_node); - if (priv->nphys == 0) - return -ENODEV; - - priv->phys = devm_kcalloc(dev, priv->nphys, sizeof(*priv->phys), - GFP_KERNEL); - if (!priv->phys) - return -ENOMEM; - - if (of_device_is_compatible(dev->of_node, "marvell,berlin2-sata-phy")) - priv->phy_base = BG2_PHY_BASE; - else - priv->phy_base = BG2Q_PHY_BASE; - - dev_set_drvdata(dev, priv); - spin_lock_init(&priv->lock); - - for_each_available_child_of_node(dev->of_node, child) { - struct phy_berlin_desc *phy_desc; - - if (of_property_read_u32(child, "reg", &phy_id)) { - dev_err(dev, "missing reg property in node %s\n", - child->name); - ret = -EINVAL; - goto put_child; - } - - if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) { - dev_err(dev, "invalid reg in node %s\n", child->name); - ret = -EINVAL; - goto put_child; - } - - phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL); - if (!phy_desc) { - ret = -ENOMEM; - goto put_child; - } - - phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops); - if (IS_ERR(phy)) { - dev_err(dev, "failed to create PHY %d\n", phy_id); - ret = PTR_ERR(phy); - goto put_child; - } - - phy_desc->phy = phy; - phy_desc->power_bit = phy_berlin_power_down_bits[phy_id]; - phy_desc->index = phy_id; - phy_set_drvdata(phy, phy_desc); - - priv->phys[i++] = phy_desc; - - /* Make sure the PHY is off */ - phy_berlin_sata_power_off(phy); - } - - phy_provider = - devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate); - return PTR_ERR_OR_ZERO(phy_provider); -put_child: - of_node_put(child); - return ret; -} - -static const struct of_device_id phy_berlin_sata_of_match[] = { - { .compatible = "marvell,berlin2-sata-phy" }, - { .compatible = "marvell,berlin2q-sata-phy" }, - { }, -}; -MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match); - -static struct platform_driver phy_berlin_sata_driver = { - .probe = phy_berlin_sata_probe, - .driver = { - .name = "phy-berlin-sata", - .of_match_table = phy_berlin_sata_of_match, - }, -}; -module_platform_driver(phy_berlin_sata_driver); - -MODULE_DESCRIPTION("Marvell Berlin SATA PHY driver"); -MODULE_AUTHOR("Antoine Ténart "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-berlin-usb.c b/drivers/phy/phy-berlin-usb.c deleted file mode 100644 index 2017751ede26..000000000000 --- a/drivers/phy/phy-berlin-usb.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (C) 2014 Marvell Technology Group Ltd. - * - * Antoine Tenart - * Jisheng Zhang - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include - -#define USB_PHY_PLL 0x04 -#define USB_PHY_PLL_CONTROL 0x08 -#define USB_PHY_TX_CTRL0 0x10 -#define USB_PHY_TX_CTRL1 0x14 -#define USB_PHY_TX_CTRL2 0x18 -#define USB_PHY_RX_CTRL 0x20 -#define USB_PHY_ANALOG 0x34 - -/* USB_PHY_PLL */ -#define CLK_REF_DIV(x) ((x) << 4) -#define FEEDBACK_CLK_DIV(x) ((x) << 8) - -/* USB_PHY_PLL_CONTROL */ -#define CLK_STABLE BIT(0) -#define PLL_CTRL_PIN BIT(1) -#define PLL_CTRL_REG BIT(2) -#define PLL_ON BIT(3) -#define PHASE_OFF_TOL_125 (0x0 << 5) -#define PHASE_OFF_TOL_250 BIT(5) -#define KVC0_CALIB (0x0 << 9) -#define KVC0_REG_CTRL BIT(9) -#define KVC0_HIGH (0x0 << 10) -#define KVC0_LOW (0x3 << 10) -#define CLK_BLK_EN BIT(13) - -/* USB_PHY_TX_CTRL0 */ -#define EXT_HS_RCAL_EN BIT(3) -#define EXT_FS_RCAL_EN BIT(4) -#define IMPCAL_VTH_DIV(x) ((x) << 5) -#define EXT_RS_RCAL_DIV(x) ((x) << 8) -#define EXT_FS_RCAL_DIV(x) ((x) << 12) - -/* USB_PHY_TX_CTRL1 */ -#define TX_VDD15_14 (0x0 << 4) -#define TX_VDD15_15 BIT(4) -#define TX_VDD15_16 (0x2 << 4) -#define TX_VDD15_17 (0x3 << 4) -#define TX_VDD12_VDD (0x0 << 6) -#define TX_VDD12_11 BIT(6) -#define TX_VDD12_12 (0x2 << 6) -#define TX_VDD12_13 (0x3 << 6) -#define LOW_VDD_EN BIT(8) -#define TX_OUT_AMP(x) ((x) << 9) - -/* USB_PHY_TX_CTRL2 */ -#define TX_CHAN_CTRL_REG(x) ((x) << 0) -#define DRV_SLEWRATE(x) ((x) << 4) -#define IMP_CAL_FS_HS_DLY_0 (0x0 << 6) -#define IMP_CAL_FS_HS_DLY_1 BIT(6) -#define IMP_CAL_FS_HS_DLY_2 (0x2 << 6) -#define IMP_CAL_FS_HS_DLY_3 (0x3 << 6) -#define FS_DRV_EN_MASK(x) ((x) << 8) -#define HS_DRV_EN_MASK(x) ((x) << 12) - -/* USB_PHY_RX_CTRL */ -#define PHASE_FREEZE_DLY_2_CL (0x0 << 0) -#define PHASE_FREEZE_DLY_4_CL BIT(0) -#define ACK_LENGTH_8_CL (0x0 << 2) -#define ACK_LENGTH_12_CL BIT(2) -#define ACK_LENGTH_16_CL (0x2 << 2) -#define ACK_LENGTH_20_CL (0x3 << 2) -#define SQ_LENGTH_3 (0x0 << 4) -#define SQ_LENGTH_6 BIT(4) -#define SQ_LENGTH_9 (0x2 << 4) -#define SQ_LENGTH_12 (0x3 << 4) -#define DISCON_THRESHOLD_260 (0x0 << 6) -#define DISCON_THRESHOLD_270 BIT(6) -#define DISCON_THRESHOLD_280 (0x2 << 6) -#define DISCON_THRESHOLD_290 (0x3 << 6) -#define SQ_THRESHOLD(x) ((x) << 8) -#define LPF_COEF(x) ((x) << 12) -#define INTPL_CUR_10 (0x0 << 14) -#define INTPL_CUR_20 BIT(14) -#define INTPL_CUR_30 (0x2 << 14) -#define INTPL_CUR_40 (0x3 << 14) - -/* USB_PHY_ANALOG */ -#define ANA_PWR_UP BIT(1) -#define ANA_PWR_DOWN BIT(2) -#define V2I_VCO_RATIO(x) ((x) << 7) -#define R_ROTATE_90 (0x0 << 10) -#define R_ROTATE_0 BIT(10) -#define MODE_TEST_EN BIT(11) -#define ANA_TEST_DC_CTRL(x) ((x) << 12) - -static const u32 phy_berlin_pll_dividers[] = { - /* Berlin 2 */ - CLK_REF_DIV(0x6) | FEEDBACK_CLK_DIV(0x55), - /* Berlin 2CD/Q */ - CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54), -}; - -struct phy_berlin_usb_priv { - void __iomem *base; - struct reset_control *rst_ctrl; - u32 pll_divider; -}; - -static int phy_berlin_usb_power_on(struct phy *phy) -{ - struct phy_berlin_usb_priv *priv = phy_get_drvdata(phy); - - reset_control_reset(priv->rst_ctrl); - - writel(priv->pll_divider, - priv->base + USB_PHY_PLL); - writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL | - CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL); - writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5), - priv->base + USB_PHY_ANALOG); - writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 | - DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) | - INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL); - - writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1); - writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), - priv->base + USB_PHY_TX_CTRL0); - - writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4) | - EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0); - - writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), - priv->base + USB_PHY_TX_CTRL0); - writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) | IMP_CAL_FS_HS_DLY_3 | - FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2); - - return 0; -} - -static const struct phy_ops phy_berlin_usb_ops = { - .power_on = phy_berlin_usb_power_on, - .owner = THIS_MODULE, -}; - -static const struct of_device_id phy_berlin_usb_of_match[] = { - { - .compatible = "marvell,berlin2-usb-phy", - .data = &phy_berlin_pll_dividers[0], - }, - { - .compatible = "marvell,berlin2cd-usb-phy", - .data = &phy_berlin_pll_dividers[1], - }, - { }, -}; -MODULE_DEVICE_TABLE(of, phy_berlin_usb_of_match); - -static int phy_berlin_usb_probe(struct platform_device *pdev) -{ - const struct of_device_id *match = - of_match_device(phy_berlin_usb_of_match, &pdev->dev); - struct phy_berlin_usb_priv *priv; - struct resource *res; - struct phy *phy; - struct phy_provider *phy_provider; - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); - - priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL); - if (IS_ERR(priv->rst_ctrl)) - return PTR_ERR(priv->rst_ctrl); - - priv->pll_divider = *((u32 *)match->data); - - phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops); - if (IS_ERR(phy)) { - dev_err(&pdev->dev, "failed to create PHY\n"); - return PTR_ERR(phy); - } - - phy_set_drvdata(phy, priv); - - phy_provider = - devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); - return PTR_ERR_OR_ZERO(phy_provider); -} - -static struct platform_driver phy_berlin_usb_driver = { - .probe = phy_berlin_usb_probe, - .driver = { - .name = "phy-berlin-usb", - .of_match_table = phy_berlin_usb_of_match, - }, -}; -module_platform_driver(phy_berlin_usb_driver); - -MODULE_AUTHOR("Antoine Tenart "); -MODULE_DESCRIPTION("Marvell Berlin PHY driver for USB"); -MODULE_LICENSE("GPL"); diff --git a/drivers/phy/phy-brcm-sata.c b/drivers/phy/phy-brcm-sata.c deleted file mode 100644 index ccbc3d994998..000000000000 --- a/drivers/phy/phy-brcm-sata.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Broadcom SATA3 AHCI Controller PHY Driver - * - * Copyright (C) 2016 Broadcom - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SATA_PCB_BANK_OFFSET 0x23c -#define SATA_PCB_REG_OFFSET(ofs) ((ofs) * 4) - -#define MAX_PORTS 2 - -/* Register offset between PHYs in PCB space */ -#define SATA_PCB_REG_28NM_SPACE_SIZE 0x1000 - -/* The older SATA PHY registers duplicated per port registers within the map, - * rather than having a separate map per port. - */ -#define SATA_PCB_REG_40NM_SPACE_SIZE 0x10 - -/* Register offset between PHYs in PHY control space */ -#define SATA_PHY_CTRL_REG_28NM_SPACE_SIZE 0x8 - -enum brcm_sata_phy_version { - BRCM_SATA_PHY_STB_28NM, - BRCM_SATA_PHY_STB_40NM, - BRCM_SATA_PHY_IPROC_NS2, - BRCM_SATA_PHY_IPROC_NSP, -}; - -struct brcm_sata_port { - int portnum; - struct phy *phy; - struct brcm_sata_phy *phy_priv; - bool ssc_en; -}; - -struct brcm_sata_phy { - struct device *dev; - void __iomem *phy_base; - void __iomem *ctrl_base; - enum brcm_sata_phy_version version; - - struct brcm_sata_port phys[MAX_PORTS]; -}; - -enum sata_phy_regs { - BLOCK0_REG_BANK = 0x000, - BLOCK0_XGXSSTATUS = 0x81, - BLOCK0_XGXSSTATUS_PLL_LOCK = BIT(12), - BLOCK0_SPARE = 0x8d, - BLOCK0_SPARE_OOB_CLK_SEL_MASK = 0x3, - BLOCK0_SPARE_OOB_CLK_SEL_REFBY2 = 0x1, - - PLL_REG_BANK_0 = 0x050, - PLL_REG_BANK_0_PLLCONTROL_0 = 0x81, - PLLCONTROL_0_FREQ_DET_RESTART = BIT(13), - PLLCONTROL_0_FREQ_MONITOR = BIT(12), - PLLCONTROL_0_SEQ_START = BIT(15), - PLL_CAP_CONTROL = 0x85, - PLL_ACTRL2 = 0x8b, - PLL_ACTRL2_SELDIV_MASK = 0x1f, - PLL_ACTRL2_SELDIV_SHIFT = 9, - - PLL1_REG_BANK = 0x060, - PLL1_ACTRL2 = 0x82, - PLL1_ACTRL3 = 0x83, - PLL1_ACTRL4 = 0x84, - - OOB_REG_BANK = 0x150, - OOB1_REG_BANK = 0x160, - OOB_CTRL1 = 0x80, - OOB_CTRL1_BURST_MAX_MASK = 0xf, - OOB_CTRL1_BURST_MAX_SHIFT = 12, - OOB_CTRL1_BURST_MIN_MASK = 0xf, - OOB_CTRL1_BURST_MIN_SHIFT = 8, - OOB_CTRL1_WAKE_IDLE_MAX_MASK = 0xf, - OOB_CTRL1_WAKE_IDLE_MAX_SHIFT = 4, - OOB_CTRL1_WAKE_IDLE_MIN_MASK = 0xf, - OOB_CTRL1_WAKE_IDLE_MIN_SHIFT = 0, - OOB_CTRL2 = 0x81, - OOB_CTRL2_SEL_ENA_SHIFT = 15, - OOB_CTRL2_SEL_ENA_RC_SHIFT = 14, - OOB_CTRL2_RESET_IDLE_MAX_MASK = 0x3f, - OOB_CTRL2_RESET_IDLE_MAX_SHIFT = 8, - OOB_CTRL2_BURST_CNT_MASK = 0x3, - OOB_CTRL2_BURST_CNT_SHIFT = 6, - OOB_CTRL2_RESET_IDLE_MIN_MASK = 0x3f, - OOB_CTRL2_RESET_IDLE_MIN_SHIFT = 0, - - TXPMD_REG_BANK = 0x1a0, - TXPMD_CONTROL1 = 0x81, - TXPMD_CONTROL1_TX_SSC_EN_FRC = BIT(0), - TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL = BIT(1), - TXPMD_TX_FREQ_CTRL_CONTROL1 = 0x82, - TXPMD_TX_FREQ_CTRL_CONTROL2 = 0x83, - TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK = 0x3ff, - TXPMD_TX_FREQ_CTRL_CONTROL3 = 0x84, - TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff, -}; - -enum sata_phy_ctrl_regs { - PHY_CTRL_1 = 0x0, - PHY_CTRL_1_RESET = BIT(0), -}; - -static inline void __iomem *brcm_sata_pcb_base(struct brcm_sata_port *port) -{ - struct brcm_sata_phy *priv = port->phy_priv; - u32 size = 0; - - switch (priv->version) { - case BRCM_SATA_PHY_STB_28NM: - case BRCM_SATA_PHY_IPROC_NS2: - size = SATA_PCB_REG_28NM_SPACE_SIZE; - break; - case BRCM_SATA_PHY_STB_40NM: - size = SATA_PCB_REG_40NM_SPACE_SIZE; - break; - default: - dev_err(priv->dev, "invalid phy version\n"); - break; - } - - return priv->phy_base + (port->portnum * size); -} - -static inline void __iomem *brcm_sata_ctrl_base(struct brcm_sata_port *port) -{ - struct brcm_sata_phy *priv = port->phy_priv; - u32 size = 0; - - switch (priv->version) { - case BRCM_SATA_PHY_IPROC_NS2: - size = SATA_PHY_CTRL_REG_28NM_SPACE_SIZE; - break; - default: - dev_err(priv->dev, "invalid phy version\n"); - break; - } - - return priv->ctrl_base + (port->portnum * size); -} - -static void brcm_sata_phy_wr(void __iomem *pcb_base, u32 bank, - u32 ofs, u32 msk, u32 value) -{ - u32 tmp; - - writel(bank, pcb_base + SATA_PCB_BANK_OFFSET); - tmp = readl(pcb_base + SATA_PCB_REG_OFFSET(ofs)); - tmp = (tmp & msk) | value; - writel(tmp, pcb_base + SATA_PCB_REG_OFFSET(ofs)); -} - -static u32 brcm_sata_phy_rd(void __iomem *pcb_base, u32 bank, u32 ofs) -{ - writel(bank, pcb_base + SATA_PCB_BANK_OFFSET); - return readl(pcb_base + SATA_PCB_REG_OFFSET(ofs)); -} - -/* These defaults were characterized by H/W group */ -#define STB_FMIN_VAL_DEFAULT 0x3df -#define STB_FMAX_VAL_DEFAULT 0x3df -#define STB_FMAX_VAL_SSC 0x83 - -static int brcm_stb_sata_init(struct brcm_sata_port *port) -{ - void __iomem *base = brcm_sata_pcb_base(port); - struct brcm_sata_phy *priv = port->phy_priv; - u32 tmp; - - /* override the TX spread spectrum setting */ - tmp = TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL | TXPMD_CONTROL1_TX_SSC_EN_FRC; - brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_CONTROL1, ~tmp, tmp); - - /* set fixed min freq */ - brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL2, - ~TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK, - STB_FMIN_VAL_DEFAULT); - - /* set fixed max freq depending on SSC config */ - if (port->ssc_en) { - dev_info(priv->dev, "enabling SSC on port%d\n", port->portnum); - tmp = STB_FMAX_VAL_SSC; - } else { - tmp = STB_FMAX_VAL_DEFAULT; - } - - brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3, - ~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp); - - return 0; -} - -/* NS2 SATA PLL1 defaults were characterized by H/W group */ -#define NS2_PLL1_ACTRL2_MAGIC 0x1df8 -#define NS2_PLL1_ACTRL3_MAGIC 0x2b00 -#define NS2_PLL1_ACTRL4_MAGIC 0x8824 - -static int brcm_ns2_sata_init(struct brcm_sata_port *port) -{ - int try; - unsigned int val; - void __iomem *base = brcm_sata_pcb_base(port); - void __iomem *ctrl_base = brcm_sata_ctrl_base(port); - struct device *dev = port->phy_priv->dev; - - /* Configure OOB control */ - val = 0x0; - val |= (0xc << OOB_CTRL1_BURST_MAX_SHIFT); - val |= (0x4 << OOB_CTRL1_BURST_MIN_SHIFT); - val |= (0x9 << OOB_CTRL1_WAKE_IDLE_MAX_SHIFT); - val |= (0x3 << OOB_CTRL1_WAKE_IDLE_MIN_SHIFT); - brcm_sata_phy_wr(base, OOB_REG_BANK, OOB_CTRL1, 0x0, val); - val = 0x0; - val |= (0x1b << OOB_CTRL2_RESET_IDLE_MAX_SHIFT); - val |= (0x2 << OOB_CTRL2_BURST_CNT_SHIFT); - val |= (0x9 << OOB_CTRL2_RESET_IDLE_MIN_SHIFT); - brcm_sata_phy_wr(base, OOB_REG_BANK, OOB_CTRL2, 0x0, val); - - /* Configure PHY PLL register bank 1 */ - val = NS2_PLL1_ACTRL2_MAGIC; - brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL2, 0x0, val); - val = NS2_PLL1_ACTRL3_MAGIC; - brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL3, 0x0, val); - val = NS2_PLL1_ACTRL4_MAGIC; - brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL4, 0x0, val); - - /* Configure PHY BLOCK0 register bank */ - /* Set oob_clk_sel to refclk/2 */ - brcm_sata_phy_wr(base, BLOCK0_REG_BANK, BLOCK0_SPARE, - ~BLOCK0_SPARE_OOB_CLK_SEL_MASK, - BLOCK0_SPARE_OOB_CLK_SEL_REFBY2); - - /* Strobe PHY reset using PHY control register */ - writel(PHY_CTRL_1_RESET, ctrl_base + PHY_CTRL_1); - mdelay(1); - writel(0x0, ctrl_base + PHY_CTRL_1); - mdelay(1); - - /* Wait for PHY PLL lock by polling pll_lock bit */ - try = 50; - while (try) { - val = brcm_sata_phy_rd(base, BLOCK0_REG_BANK, - BLOCK0_XGXSSTATUS); - if (val & BLOCK0_XGXSSTATUS_PLL_LOCK) - break; - msleep(20); - try--; - } - if (!try) { - /* PLL did not lock; give up */ - dev_err(dev, "port%d PLL did not lock\n", port->portnum); - return -ETIMEDOUT; - } - - dev_dbg(dev, "port%d initialized\n", port->portnum); - - return 0; -} - -static int brcm_nsp_sata_init(struct brcm_sata_port *port) -{ - struct brcm_sata_phy *priv = port->phy_priv; - struct device *dev = port->phy_priv->dev; - void __iomem *base = priv->phy_base; - unsigned int oob_bank; - unsigned int val, try; - - /* Configure OOB control */ - if (port->portnum == 0) - oob_bank = OOB_REG_BANK; - else if (port->portnum == 1) - oob_bank = OOB1_REG_BANK; - else - return -EINVAL; - - val = 0x0; - val |= (0x0f << OOB_CTRL1_BURST_MAX_SHIFT); - val |= (0x06 << OOB_CTRL1_BURST_MIN_SHIFT); - val |= (0x0f << OOB_CTRL1_WAKE_IDLE_MAX_SHIFT); - val |= (0x06 << OOB_CTRL1_WAKE_IDLE_MIN_SHIFT); - brcm_sata_phy_wr(base, oob_bank, OOB_CTRL1, 0x0, val); - - val = 0x0; - val |= (0x2e << OOB_CTRL2_RESET_IDLE_MAX_SHIFT); - val |= (0x02 << OOB_CTRL2_BURST_CNT_SHIFT); - val |= (0x16 << OOB_CTRL2_RESET_IDLE_MIN_SHIFT); - brcm_sata_phy_wr(base, oob_bank, OOB_CTRL2, 0x0, val); - - - brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_ACTRL2, - ~(PLL_ACTRL2_SELDIV_MASK << PLL_ACTRL2_SELDIV_SHIFT), - 0x0c << PLL_ACTRL2_SELDIV_SHIFT); - - brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_CAP_CONTROL, - 0xff0, 0x4f0); - - val = PLLCONTROL_0_FREQ_DET_RESTART | PLLCONTROL_0_FREQ_MONITOR; - brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, - ~val, val); - val = PLLCONTROL_0_SEQ_START; - brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, - ~val, 0); - mdelay(10); - brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0, - ~val, val); - - /* Wait for pll_seq_done bit */ - try = 50; - while (try--) { - val = brcm_sata_phy_rd(base, BLOCK0_REG_BANK, - BLOCK0_XGXSSTATUS); - if (val & BLOCK0_XGXSSTATUS_PLL_LOCK) - break; - msleep(20); - } - if (!try) { - /* PLL did not lock; give up */ - dev_err(dev, "port%d PLL did not lock\n", port->portnum); - return -ETIMEDOUT; - } - - dev_dbg(dev, "port%d initialized\n", port->portnum); - - return 0; -} - -static int brcm_sata_phy_init(struct phy *phy) -{ - int rc; - struct brcm_sata_port *port = phy_get_drvdata(phy); - - switch (port->phy_priv->version) { - case BRCM_SATA_PHY_STB_28NM: - case BRCM_SATA_PHY_STB_40NM: - rc = brcm_stb_sata_init(port); - break; - case BRCM_SATA_PHY_IPROC_NS2: - rc = brcm_ns2_sata_init(port); - break; - case BRCM_SATA_PHY_IPROC_NSP: - rc = brcm_nsp_sata_init(port); - break; - default: - rc = -ENODEV; - } - - return rc; -} - -static const struct phy_ops phy_ops = { - .init = brcm_sata_phy_init, - .owner = THIS_MODULE, -}; - -static const struct of_device_id brcm_sata_phy_of_match[] = { - { .compatible = "brcm,bcm7445-sata-phy", - .data = (void *)BRCM_SATA_PHY_STB_28NM }, - { .compatible = "brcm,bcm7425-sata-phy", - .data = (void *)BRCM_SATA_PHY_STB_40NM }, - { .compatible = "brcm,iproc-ns2-sata-phy", - .data = (void *)BRCM_SATA_PHY_IPROC_NS2 }, - { .compatible = "brcm,iproc-nsp-sata-phy", - .data = (void *)BRCM_SATA_PHY_IPROC_NSP }, - {}, -}; -MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match); - -static int brcm_sata_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *dn = dev->of_node, *child; - const struct of_device_id *of_id; - struct brcm_sata_phy *priv; - struct resource *res; - struct phy_provider *provider; - int ret, count = 0; - - if (of_get_child_count(dn) == 0) - return -ENODEV; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - dev_set_drvdata(dev, priv); - priv->dev = dev; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); - priv->phy_base = devm_ioremap_resource(dev, res); - if (IS_ERR(priv->phy_base)) - return PTR_ERR(priv->phy_base); - - of_id = of_match_node(brcm_sata_phy_of_match, dn); - if (of_id) - priv->version = (enum brcm_sata_phy_version)of_id->data; - else - priv->version = BRCM_SATA_PHY_STB_28NM; - - if (priv->version == BRCM_SATA_PHY_IPROC_NS2) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "phy-ctrl"); - priv->ctrl_base = devm_ioremap_resource(dev, res); - if (IS_ERR(priv->ctrl_base)) - return PTR_ERR(priv->ctrl_base); - } - - for_each_available_child_of_node(dn, child) { - unsigned int id; - struct brcm_sata_port *port; - - if (of_property_read_u32(child, "reg", &id)) { - dev_err(dev, "missing reg property in node %s\n", - child->name); - ret = -EINVAL; - goto put_child; - } - - if (id >= MAX_PORTS) { - dev_err(dev, "invalid reg: %u\n", id); - ret = -EINVAL; - goto put_child; - } - if (priv->phys[id].phy) { - dev_err(dev, "already registered port %u\n", id); - ret = -EINVAL; - goto put_child; - } - - port = &priv->phys[id]; - port->portnum = id; - port->phy_priv = priv; - port->phy = devm_phy_create(dev, child, &phy_ops); - port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc"); - if (IS_ERR(port->phy)) { - dev_err(dev, "failed to create PHY\n"); - ret = PTR_ERR(port->phy); - goto put_child; - } - - phy_set_drvdata(port->phy, port); - count++; - } - - provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(provider)) { - dev_err(dev, "could not register PHY provider\n"); - return PTR_ERR(provider); - } - - dev_info(dev, "registered %d port(s)\n", count); - - return 0; -put_child: - of_node_put(child); - return ret; -} - -static struct platform_driver brcm_sata_phy_driver = { - .probe = brcm_sata_phy_probe, - .driver = { - .of_match_table = brcm_sata_phy_of_match, - .name = "brcm-sata-phy", - } -}; -module_platform_driver(brcm_sata_phy_driver); - -MODULE_DESCRIPTION("Broadcom SATA PHY driver"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Marc Carino"); -MODULE_AUTHOR("Brian Norris"); -MODULE_ALIAS("platform:phy-brcm-sata"); diff --git a/drivers/phy/phy-da8xx-usb.c b/drivers/phy/phy-da8xx-usb.c deleted file mode 100644 index 1b82bff6330f..000000000000 --- a/drivers/phy/phy-da8xx-usb.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * phy-da8xx-usb - TI DaVinci DA8xx USB PHY driver - * - * Copyright (C) 2016 David Lechner - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PHY_INIT_BITS (CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN) - -struct da8xx_usb_phy { - struct phy_provider *phy_provider; - struct phy *usb11_phy; - struct phy *usb20_phy; - struct clk *usb11_clk; - struct clk *usb20_clk; - struct regmap *regmap; -}; - -static int da8xx_usb11_phy_power_on(struct phy *phy) -{ - struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); - int ret; - - ret = clk_prepare_enable(d_phy->usb11_clk); - if (ret) - return ret; - - regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, - CFGCHIP2_USB1SUSPENDM); - - return 0; -} - -static int da8xx_usb11_phy_power_off(struct phy *phy) -{ - struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); - - regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, 0); - - clk_disable_unprepare(d_phy->usb11_clk); - - return 0; -} - -static const struct phy_ops da8xx_usb11_phy_ops = { - .power_on = da8xx_usb11_phy_power_on, - .power_off = da8xx_usb11_phy_power_off, - .owner = THIS_MODULE, -}; - -static int da8xx_usb20_phy_power_on(struct phy *phy) -{ - struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); - int ret; - - ret = clk_prepare_enable(d_phy->usb20_clk); - if (ret) - return ret; - - regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, 0); - - return 0; -} - -static int da8xx_usb20_phy_power_off(struct phy *phy) -{ - struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); - - regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, - CFGCHIP2_OTGPWRDN); - - clk_disable_unprepare(d_phy->usb20_clk); - - return 0; -} - -static int da8xx_usb20_phy_set_mode(struct phy *phy, enum phy_mode mode) -{ - struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); - u32 val; - - switch (mode) { - case PHY_MODE_USB_HOST: /* Force VBUS valid, ID = 0 */ - val = CFGCHIP2_OTGMODE_FORCE_HOST; - break; - case PHY_MODE_USB_DEVICE: /* Force VBUS valid, ID = 1 */ - val = CFGCHIP2_OTGMODE_FORCE_DEVICE; - break; - case PHY_MODE_USB_OTG: /* Don't override the VBUS/ID comparators */ - val = CFGCHIP2_OTGMODE_NO_OVERRIDE; - break; - default: - return -EINVAL; - } - - regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGMODE_MASK, - val); - - return 0; -} - -static const struct phy_ops da8xx_usb20_phy_ops = { - .power_on = da8xx_usb20_phy_power_on, - .power_off = da8xx_usb20_phy_power_off, - .set_mode = da8xx_usb20_phy_set_mode, - .owner = THIS_MODULE, -}; - -static struct phy *da8xx_usb_phy_of_xlate(struct device *dev, - struct of_phandle_args *args) -{ - struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev); - - if (!d_phy) - return ERR_PTR(-ENODEV); - - switch (args->args[0]) { - case 0: - return d_phy->usb20_phy; - case 1: - return d_phy->usb11_phy; - default: - return ERR_PTR(-EINVAL); - } -} - -static int da8xx_usb_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *node = dev->of_node; - struct da8xx_usb_phy *d_phy; - - d_phy = devm_kzalloc(dev, sizeof(*d_phy), GFP_KERNEL); - if (!d_phy) - return -ENOMEM; - - if (node) - d_phy->regmap = syscon_regmap_lookup_by_compatible( - "ti,da830-cfgchip"); - else - d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon"); - if (IS_ERR(d_phy->regmap)) { - dev_err(dev, "Failed to get syscon\n"); - return PTR_ERR(d_phy->regmap); - } - - d_phy->usb11_clk = devm_clk_get(dev, "usb11_phy"); - if (IS_ERR(d_phy->usb11_clk)) { - dev_err(dev, "Failed to get usb11_phy clock\n"); - return PTR_ERR(d_phy->usb11_clk); - } - - d_phy->usb20_clk = devm_clk_get(dev, "usb20_phy"); - if (IS_ERR(d_phy->usb20_clk)) { - dev_err(dev, "Failed to get usb20_phy clock\n"); - return PTR_ERR(d_phy->usb20_clk); - } - - d_phy->usb11_phy = devm_phy_create(dev, node, &da8xx_usb11_phy_ops); - if (IS_ERR(d_phy->usb11_phy)) { - dev_err(dev, "Failed to create usb11 phy\n"); - return PTR_ERR(d_phy->usb11_phy); - } - - d_phy->usb20_phy = devm_phy_create(dev, node, &da8xx_usb20_phy_ops); - if (IS_ERR(d_phy->usb20_phy)) { - dev_err(dev, "Failed to create usb20 phy\n"); - return PTR_ERR(d_phy->usb20_phy); - } - - platform_set_drvdata(pdev, d_phy); - phy_set_drvdata(d_phy->usb11_phy, d_phy); - phy_set_drvdata(d_phy->usb20_phy, d_phy); - - if (node) { - d_phy->phy_provider = devm_of_phy_provider_register(dev, - da8xx_usb_phy_of_xlate); - if (IS_ERR(d_phy->phy_provider)) { - dev_err(dev, "Failed to create phy provider\n"); - return PTR_ERR(d_phy->phy_provider); - } - } else { - int ret; - - ret = phy_create_lookup(d_phy->usb11_phy, "usb-phy", - "ohci-da8xx"); - if (ret) - dev_warn(dev, "Failed to create usb11 phy lookup\n"); - ret = phy_create_lookup(d_phy->usb20_phy, "usb-phy", - "musb-da8xx"); - if (ret) - dev_warn(dev, "Failed to create usb20 phy lookup\n"); - } - - regmap_write_bits(d_phy->regmap, CFGCHIP(2), - PHY_INIT_BITS, PHY_INIT_BITS); - - return 0; -} - -static int da8xx_usb_phy_remove(struct platform_device *pdev) -{ - struct da8xx_usb_phy *d_phy = platform_get_drvdata(pdev); - - if (!pdev->dev.of_node) { - phy_remove_lookup(d_phy->usb20_phy, "usb-phy", "musb-da8xx"); - phy_remove_lookup(d_phy->usb11_phy, "usb-phy", "ohci-da8xx"); - } - - return 0; -} - -static const struct of_device_id da8xx_usb_phy_ids[] = { - { .compatible = "ti,da830-usb-phy" }, - { } -}; -MODULE_DEVICE_TABLE(of, da8xx_usb_phy_ids); - -static struct platform_driver da8xx_usb_phy_driver = { - .probe = da8xx_usb_phy_probe, - .remove = da8xx_usb_phy_remove, - .driver = { - .name = "da8xx-usb-phy", - .of_match_table = da8xx_usb_phy_ids, - }, -}; - -module_platform_driver(da8xx_usb_phy_driver); - -MODULE_ALIAS("platform:da8xx-usb-phy"); -MODULE_AUTHOR("David Lechner "); -MODULE_DESCRIPTION("TI DA8xx USB PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-dm816x-usb.c b/drivers/phy/phy-dm816x-usb.c deleted file mode 100644 index cbcce7cf0028..000000000000 --- a/drivers/phy/phy-dm816x-usb.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* - * TRM has two sets of USB_CTRL registers.. The correct register bits - * are in TRM section 24.9.8.2 USB_CTRL Register. The TRM documents the - * phy as being SR70LX Synopsys USB 2.0 OTG nanoPHY. It also seems at - * least dm816x rev c ignores writes to USB_CTRL register, but the TI - * kernel is writing to those so it's possible that later revisions - * have worknig USB_CTRL register. - * - * Also note that At least USB_CTRL register seems to be dm816x specific - * according to the TRM. It's possible that USBPHY_CTRL is more generic, - * but that would have to be checked against the SR70LX documentation - * which does not seem to be publicly available. - * - * Finally, the phy on dm814x and am335x is different from dm816x. - */ -#define DM816X_USB_CTRL_PHYCLKSRC BIT(8) /* 1 = PLL ref clock */ -#define DM816X_USB_CTRL_PHYSLEEP1 BIT(1) /* Enable the first phy */ -#define DM816X_USB_CTRL_PHYSLEEP0 BIT(0) /* Enable the second phy */ - -#define DM816X_USBPHY_CTRL_TXRISETUNE 1 -#define DM816X_USBPHY_CTRL_TXVREFTUNE 0xc -#define DM816X_USBPHY_CTRL_TXPREEMTUNE 0x2 - -struct dm816x_usb_phy { - struct regmap *syscon; - struct device *dev; - unsigned int instance; - struct clk *refclk; - struct usb_phy phy; - unsigned int usb_ctrl; /* Shared between phy0 and phy1 */ - unsigned int usbphy_ctrl; -}; - -static int dm816x_usb_phy_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - otg->host = host; - if (!host) - otg->state = OTG_STATE_UNDEFINED; - - return 0; -} - -static int dm816x_usb_phy_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - otg->gadget = gadget; - if (!gadget) - otg->state = OTG_STATE_UNDEFINED; - - return 0; -} - -static int dm816x_usb_phy_init(struct phy *x) -{ - struct dm816x_usb_phy *phy = phy_get_drvdata(x); - unsigned int val; - int error; - - if (clk_get_rate(phy->refclk) != 24000000) - dev_warn(phy->dev, "nonstandard phy refclk\n"); - - /* Set PLL ref clock and put phys to sleep */ - error = regmap_update_bits(phy->syscon, phy->usb_ctrl, - DM816X_USB_CTRL_PHYCLKSRC | - DM816X_USB_CTRL_PHYSLEEP1 | - DM816X_USB_CTRL_PHYSLEEP0, - 0); - regmap_read(phy->syscon, phy->usb_ctrl, &val); - if ((val & 3) != 0) - dev_info(phy->dev, - "Working dm816x USB_CTRL! (0x%08x)\n", - val); - - /* - * TI kernel sets these values for "symmetrical eye diagram and - * better signal quality" so let's assume somebody checked the - * values with a scope and set them here too. - */ - regmap_read(phy->syscon, phy->usbphy_ctrl, &val); - val |= DM816X_USBPHY_CTRL_TXRISETUNE | - DM816X_USBPHY_CTRL_TXVREFTUNE | - DM816X_USBPHY_CTRL_TXPREEMTUNE; - regmap_write(phy->syscon, phy->usbphy_ctrl, val); - - return 0; -} - -static const struct phy_ops ops = { - .init = dm816x_usb_phy_init, - .owner = THIS_MODULE, -}; - -static int __maybe_unused dm816x_usb_phy_runtime_suspend(struct device *dev) -{ - struct dm816x_usb_phy *phy = dev_get_drvdata(dev); - unsigned int mask, val; - int error = 0; - - mask = BIT(phy->instance); - val = ~BIT(phy->instance); - error = regmap_update_bits(phy->syscon, phy->usb_ctrl, - mask, val); - if (error) - dev_err(phy->dev, "phy%i failed to power off\n", - phy->instance); - clk_disable(phy->refclk); - - return 0; -} - -static int __maybe_unused dm816x_usb_phy_runtime_resume(struct device *dev) -{ - struct dm816x_usb_phy *phy = dev_get_drvdata(dev); - unsigned int mask, val; - int error; - - error = clk_enable(phy->refclk); - if (error) - return error; - - /* - * Note that at least dm816x rev c does not seem to do - * anything with the USB_CTRL register. But let's follow - * what the TI tree is doing in case later revisions use - * USB_CTRL. - */ - mask = BIT(phy->instance); - val = BIT(phy->instance); - error = regmap_update_bits(phy->syscon, phy->usb_ctrl, - mask, val); - if (error) { - dev_err(phy->dev, "phy%i failed to power on\n", - phy->instance); - clk_disable(phy->refclk); - return error; - } - - return 0; -} - -static UNIVERSAL_DEV_PM_OPS(dm816x_usb_phy_pm_ops, - dm816x_usb_phy_runtime_suspend, - dm816x_usb_phy_runtime_resume, - NULL); - -#ifdef CONFIG_OF -static const struct of_device_id dm816x_usb_phy_id_table[] = { - { - .compatible = "ti,dm8168-usb-phy", - }, - {}, -}; -MODULE_DEVICE_TABLE(of, dm816x_usb_phy_id_table); -#endif - -static int dm816x_usb_phy_probe(struct platform_device *pdev) -{ - struct dm816x_usb_phy *phy; - struct resource *res; - struct phy *generic_phy; - struct phy_provider *phy_provider; - struct usb_otg *otg; - const struct of_device_id *of_id; - const struct usb_phy_data *phy_data; - int error; - - of_id = of_match_device(of_match_ptr(dm816x_usb_phy_id_table), - &pdev->dev); - if (!of_id) - return -EINVAL; - - phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); - if (!phy) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENOENT; - - phy->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "syscon"); - if (IS_ERR(phy->syscon)) - return PTR_ERR(phy->syscon); - - /* - * According to sprs614e.pdf, the first usb_ctrl is shared and - * the second instance for usb_ctrl is reserved.. Also the - * register bits are different from earlier TRMs. - */ - phy->usb_ctrl = 0x20; - phy->usbphy_ctrl = (res->start & 0xff) + 4; - if (phy->usbphy_ctrl == 0x2c) - phy->instance = 1; - - phy_data = of_id->data; - - otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); - if (!otg) - return -ENOMEM; - - phy->dev = &pdev->dev; - phy->phy.dev = phy->dev; - phy->phy.label = "dm8168_usb_phy"; - phy->phy.otg = otg; - phy->phy.type = USB_PHY_TYPE_USB2; - otg->set_host = dm816x_usb_phy_set_host; - otg->set_peripheral = dm816x_usb_phy_set_peripheral; - otg->usb_phy = &phy->phy; - - platform_set_drvdata(pdev, phy); - - phy->refclk = devm_clk_get(phy->dev, "refclk"); - if (IS_ERR(phy->refclk)) - return PTR_ERR(phy->refclk); - error = clk_prepare(phy->refclk); - if (error) - return error; - - pm_runtime_enable(phy->dev); - generic_phy = devm_phy_create(phy->dev, NULL, &ops); - if (IS_ERR(generic_phy)) - return PTR_ERR(generic_phy); - - phy_set_drvdata(generic_phy, phy); - - phy_provider = devm_of_phy_provider_register(phy->dev, - of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - - usb_add_phy_dev(&phy->phy); - - return 0; -} - -static int dm816x_usb_phy_remove(struct platform_device *pdev) -{ - struct dm816x_usb_phy *phy = platform_get_drvdata(pdev); - - usb_remove_phy(&phy->phy); - pm_runtime_disable(phy->dev); - clk_unprepare(phy->refclk); - - return 0; -} - -static struct platform_driver dm816x_usb_phy_driver = { - .probe = dm816x_usb_phy_probe, - .remove = dm816x_usb_phy_remove, - .driver = { - .name = "dm816x-usb-phy", - .pm = &dm816x_usb_phy_pm_ops, - .of_match_table = of_match_ptr(dm816x_usb_phy_id_table), - }, -}; - -module_platform_driver(dm816x_usb_phy_driver); - -MODULE_ALIAS("platform:dm816x_usb"); -MODULE_AUTHOR("Tony Lindgren "); -MODULE_DESCRIPTION("dm816x usb phy driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c deleted file mode 100644 index bb3279dbf88c..000000000000 --- a/drivers/phy/phy-exynos-dp-video.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Samsung EXYNOS SoC series Display Port PHY driver - * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * Author: Jingoo Han - * - * 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 -#include - -struct exynos_dp_video_phy_drvdata { - u32 phy_ctrl_offset; -}; - -struct exynos_dp_video_phy { - struct regmap *regs; - const struct exynos_dp_video_phy_drvdata *drvdata; -}; - -static int exynos_dp_video_phy_power_on(struct phy *phy) -{ - struct exynos_dp_video_phy *state = phy_get_drvdata(phy); - - /* Disable power isolation on DP-PHY */ - return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset, - EXYNOS4_PHY_ENABLE, EXYNOS4_PHY_ENABLE); -} - -static int exynos_dp_video_phy_power_off(struct phy *phy) -{ - struct exynos_dp_video_phy *state = phy_get_drvdata(phy); - - /* Enable power isolation on DP-PHY */ - return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset, - EXYNOS4_PHY_ENABLE, 0); -} - -static const struct phy_ops exynos_dp_video_phy_ops = { - .power_on = exynos_dp_video_phy_power_on, - .power_off = exynos_dp_video_phy_power_off, - .owner = THIS_MODULE, -}; - -static const struct exynos_dp_video_phy_drvdata exynos5250_dp_video_phy = { - .phy_ctrl_offset = EXYNOS5_DPTX_PHY_CONTROL, -}; - -static const struct exynos_dp_video_phy_drvdata exynos5420_dp_video_phy = { - .phy_ctrl_offset = EXYNOS5420_DPTX_PHY_CONTROL, -}; - -static const struct of_device_id exynos_dp_video_phy_of_match[] = { - { - .compatible = "samsung,exynos5250-dp-video-phy", - .data = &exynos5250_dp_video_phy, - }, { - .compatible = "samsung,exynos5420-dp-video-phy", - .data = &exynos5420_dp_video_phy, - }, - { }, -}; -MODULE_DEVICE_TABLE(of, exynos_dp_video_phy_of_match); - -static int exynos_dp_video_phy_probe(struct platform_device *pdev) -{ - struct exynos_dp_video_phy *state; - struct device *dev = &pdev->dev; - const struct of_device_id *match; - struct phy_provider *phy_provider; - struct phy *phy; - - state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); - if (!state) - return -ENOMEM; - - state->regs = syscon_regmap_lookup_by_phandle(dev->of_node, - "samsung,pmu-syscon"); - if (IS_ERR(state->regs)) { - dev_err(dev, "Failed to lookup PMU regmap\n"); - return PTR_ERR(state->regs); - } - - match = of_match_node(exynos_dp_video_phy_of_match, dev->of_node); - state->drvdata = match->data; - - phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops); - if (IS_ERR(phy)) { - dev_err(dev, "failed to create Display Port PHY\n"); - return PTR_ERR(phy); - } - phy_set_drvdata(phy, state); - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static struct platform_driver exynos_dp_video_phy_driver = { - .probe = exynos_dp_video_phy_probe, - .driver = { - .name = "exynos-dp-video-phy", - .of_match_table = exynos_dp_video_phy_of_match, - } -}; -module_platform_driver(exynos_dp_video_phy_driver); - -MODULE_AUTHOR("Jingoo Han "); -MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c deleted file mode 100644 index c198886f80a3..000000000000 --- a/drivers/phy/phy-exynos-mipi-video.c +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver - * - * Copyright (C) 2013,2016 Samsung Electronics Co., Ltd. - * Author: Sylwester Nawrocki - * - * 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 -#include -#include - -enum exynos_mipi_phy_id { - EXYNOS_MIPI_PHY_ID_NONE = -1, - EXYNOS_MIPI_PHY_ID_CSIS0, - EXYNOS_MIPI_PHY_ID_DSIM0, - EXYNOS_MIPI_PHY_ID_CSIS1, - EXYNOS_MIPI_PHY_ID_DSIM1, - EXYNOS_MIPI_PHY_ID_CSIS2, - EXYNOS_MIPI_PHYS_NUM -}; - -enum exynos_mipi_phy_regmap_id { - EXYNOS_MIPI_REGMAP_PMU, - EXYNOS_MIPI_REGMAP_DISP, - EXYNOS_MIPI_REGMAP_CAM0, - EXYNOS_MIPI_REGMAP_CAM1, - EXYNOS_MIPI_REGMAPS_NUM -}; - -struct mipi_phy_device_desc { - int num_phys; - int num_regmaps; - const char *regmap_names[EXYNOS_MIPI_REGMAPS_NUM]; - struct exynos_mipi_phy_desc { - enum exynos_mipi_phy_id coupled_phy_id; - u32 enable_val; - unsigned int enable_reg; - enum exynos_mipi_phy_regmap_id enable_map; - u32 resetn_val; - unsigned int resetn_reg; - enum exynos_mipi_phy_regmap_id resetn_map; - } phys[EXYNOS_MIPI_PHYS_NUM]; -}; - -static const struct mipi_phy_device_desc s5pv210_mipi_phy = { - .num_regmaps = 1, - .regmap_names = {"syscon"}, - .num_phys = 4, - .phys = { - { - /* EXYNOS_MIPI_PHY_ID_CSIS0 */ - .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, - .enable_val = EXYNOS4_PHY_ENABLE, - .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), - .enable_map = EXYNOS_MIPI_REGMAP_PMU, - .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, - .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0), - .resetn_map = EXYNOS_MIPI_REGMAP_PMU, - }, { - /* EXYNOS_MIPI_PHY_ID_DSIM0 */ - .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, - .enable_val = EXYNOS4_PHY_ENABLE, - .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), - .enable_map = EXYNOS_MIPI_REGMAP_PMU, - .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, - .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0), - .resetn_map = EXYNOS_MIPI_REGMAP_PMU, - }, { - /* EXYNOS_MIPI_PHY_ID_CSIS1 */ - .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1, - .enable_val = EXYNOS4_PHY_ENABLE, - .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), - .enable_map = EXYNOS_MIPI_REGMAP_PMU, - .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, - .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1), - .resetn_map = EXYNOS_MIPI_REGMAP_PMU, - }, { - /* EXYNOS_MIPI_PHY_ID_DSIM1 */ - .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1, - .enable_val = EXYNOS4_PHY_ENABLE, - .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), - .enable_map = EXYNOS_MIPI_REGMAP_PMU, - .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, - .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1), - .resetn_map = EXYNOS_MIPI_REGMAP_PMU, - }, - }, -}; - -static const struct mipi_phy_device_desc exynos5420_mipi_phy = { - .num_regmaps = 1, - .regmap_names = {"syscon"}, - .num_phys = 5, - .phys = { - { - /* EXYNOS_MIPI_PHY_ID_CSIS0 */ - .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, - .enable_val = EXYNOS4_PHY_ENABLE, - .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), - .enable_map = EXYNOS_MIPI_REGMAP_PMU, - .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, - .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), - .resetn_map = EXYNOS_MIPI_REGMAP_PMU, - }, { - /* EXYNOS_MIPI_PHY_ID_DSIM0 */ - .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, - .enable_val = EXYNOS4_PHY_ENABLE, - .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), - .enable_map = EXYNOS_MIPI_REGMAP_PMU, - .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, - .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), - .resetn_map = EXYNOS_MIPI_REGMAP_PMU, - }, { - /* EXYNOS_MIPI_PHY_ID_CSIS1 */ - .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1, - .enable_val = EXYNOS4_PHY_ENABLE, - .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), - .enable_map = EXYNOS_MIPI_REGMAP_PMU, - .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, - .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), - .resetn_map = EXYNOS_MIPI_REGMAP_PMU, - }, { - /* EXYNOS_MIPI_PHY_ID_DSIM1 */ - .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1, - .enable_val = EXYNOS4_PHY_ENABLE, - .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), - .enable_map = EXYNOS_MIPI_REGMAP_PMU, - .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, - .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), - .resetn_map = EXYNOS_MIPI_REGMAP_PMU, - }, { - /* EXYNOS_MIPI_PHY_ID_CSIS2 */ - .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, - .enable_val = EXYNOS4_PHY_ENABLE, - .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(2), - .enable_map = EXYNOS_MIPI_REGMAP_PMU, - .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, - .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(2), - .resetn_map = EXYNOS_MIPI_REGMAP_PMU, - }, - }, -}; - -#define EXYNOS5433_SYSREG_DISP_MIPI_PHY 0x100C -#define EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON 0x1014 -#define EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON 0x1020 - -static const struct mipi_phy_device_desc exynos5433_mipi_phy = { - .num_regmaps = 4, - .regmap_names = { - "samsung,pmu-syscon", - "samsung,disp-sysreg", - "samsung,cam0-sysreg", - "samsung,cam1-sysreg" - }, - .num_phys = 5, - .phys = { - { - /* EXYNOS_MIPI_PHY_ID_CSIS0 */ - .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, - .enable_val = EXYNOS4_PHY_ENABLE, - .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), - .enable_map = EXYNOS_MIPI_REGMAP_PMU, - .resetn_val = BIT(0), - .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON, - .resetn_map = EXYNOS_MIPI_REGMAP_CAM0, - }, { - /* EXYNOS_MIPI_PHY_ID_DSIM0 */ - .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, - .enable_val = EXYNOS4_PHY_ENABLE, - .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), - .enable_map = EXYNOS_MIPI_REGMAP_PMU, - .resetn_val = BIT(0), - .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY, - .resetn_map = EXYNOS_MIPI_REGMAP_DISP, - }, { - /* EXYNOS_MIPI_PHY_ID_CSIS1 */ - .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, - .enable_val = EXYNOS4_PHY_ENABLE, - .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), - .enable_map = EXYNOS_MIPI_REGMAP_PMU, - .resetn_val = BIT(1), - .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON, - .resetn_map = EXYNOS_MIPI_REGMAP_CAM0, - }, { - /* EXYNOS_MIPI_PHY_ID_DSIM1 */ - .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, - .enable_val = EXYNOS4_PHY_ENABLE, - .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), - .enable_map = EXYNOS_MIPI_REGMAP_PMU, - .resetn_val = BIT(1), - .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY, - .resetn_map = EXYNOS_MIPI_REGMAP_DISP, - }, { - /* EXYNOS_MIPI_PHY_ID_CSIS2 */ - .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, - .enable_val = EXYNOS4_PHY_ENABLE, - .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(2), - .enable_map = EXYNOS_MIPI_REGMAP_PMU, - .resetn_val = BIT(0), - .resetn_reg = EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON, - .resetn_map = EXYNOS_MIPI_REGMAP_CAM1, - }, - }, -}; - -struct exynos_mipi_video_phy { - struct regmap *regmaps[EXYNOS_MIPI_REGMAPS_NUM]; - int num_phys; - struct video_phy_desc { - struct phy *phy; - unsigned int index; - const struct exynos_mipi_phy_desc *data; - } phys[EXYNOS_MIPI_PHYS_NUM]; - spinlock_t slock; -}; - -static int __set_phy_state(const struct exynos_mipi_phy_desc *data, - struct exynos_mipi_video_phy *state, unsigned int on) -{ - u32 val; - - spin_lock(&state->slock); - - /* disable in PMU sysreg */ - if (!on && data->coupled_phy_id >= 0 && - state->phys[data->coupled_phy_id].phy->power_count == 0) { - regmap_read(state->regmaps[data->enable_map], data->enable_reg, - &val); - val &= ~data->enable_val; - regmap_write(state->regmaps[data->enable_map], data->enable_reg, - val); - } - - /* PHY reset */ - regmap_read(state->regmaps[data->resetn_map], data->resetn_reg, &val); - val = on ? (val | data->resetn_val) : (val & ~data->resetn_val); - regmap_write(state->regmaps[data->resetn_map], data->resetn_reg, val); - - /* enable in PMU sysreg */ - if (on) { - regmap_read(state->regmaps[data->enable_map], data->enable_reg, - &val); - val |= data->enable_val; - regmap_write(state->regmaps[data->enable_map], data->enable_reg, - val); - } - - spin_unlock(&state->slock); - - return 0; -} - -#define to_mipi_video_phy(desc) \ - container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]) - -static int exynos_mipi_video_phy_power_on(struct phy *phy) -{ - struct video_phy_desc *phy_desc = phy_get_drvdata(phy); - struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); - - return __set_phy_state(phy_desc->data, state, 1); -} - -static int exynos_mipi_video_phy_power_off(struct phy *phy) -{ - struct video_phy_desc *phy_desc = phy_get_drvdata(phy); - struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); - - return __set_phy_state(phy_desc->data, state, 0); -} - -static struct phy *exynos_mipi_video_phy_xlate(struct device *dev, - struct of_phandle_args *args) -{ - struct exynos_mipi_video_phy *state = dev_get_drvdata(dev); - - if (WARN_ON(args->args[0] >= state->num_phys)) - return ERR_PTR(-ENODEV); - - return state->phys[args->args[0]].phy; -} - -static const struct phy_ops exynos_mipi_video_phy_ops = { - .power_on = exynos_mipi_video_phy_power_on, - .power_off = exynos_mipi_video_phy_power_off, - .owner = THIS_MODULE, -}; - -static int exynos_mipi_video_phy_probe(struct platform_device *pdev) -{ - const struct mipi_phy_device_desc *phy_dev; - struct exynos_mipi_video_phy *state; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct phy_provider *phy_provider; - unsigned int i; - - phy_dev = of_device_get_match_data(dev); - if (!phy_dev) - return -ENODEV; - - state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); - if (!state) - return -ENOMEM; - - for (i = 0; i < phy_dev->num_regmaps; i++) { - state->regmaps[i] = syscon_regmap_lookup_by_phandle(np, - phy_dev->regmap_names[i]); - if (IS_ERR(state->regmaps[i])) - return PTR_ERR(state->regmaps[i]); - } - state->num_phys = phy_dev->num_phys; - spin_lock_init(&state->slock); - - dev_set_drvdata(dev, state); - - for (i = 0; i < state->num_phys; i++) { - struct phy *phy = devm_phy_create(dev, NULL, - &exynos_mipi_video_phy_ops); - if (IS_ERR(phy)) { - dev_err(dev, "failed to create PHY %d\n", i); - return PTR_ERR(phy); - } - - state->phys[i].phy = phy; - state->phys[i].index = i; - state->phys[i].data = &phy_dev->phys[i]; - phy_set_drvdata(phy, &state->phys[i]); - } - - phy_provider = devm_of_phy_provider_register(dev, - exynos_mipi_video_phy_xlate); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id exynos_mipi_video_phy_of_match[] = { - { - .compatible = "samsung,s5pv210-mipi-video-phy", - .data = &s5pv210_mipi_phy, - }, { - .compatible = "samsung,exynos5420-mipi-video-phy", - .data = &exynos5420_mipi_phy, - }, { - .compatible = "samsung,exynos5433-mipi-video-phy", - .data = &exynos5433_mipi_phy, - }, - { /* sentinel */ }, -}; -MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match); - -static struct platform_driver exynos_mipi_video_phy_driver = { - .probe = exynos_mipi_video_phy_probe, - .driver = { - .of_match_table = exynos_mipi_video_phy_of_match, - .name = "exynos-mipi-video-phy", - } -}; -module_platform_driver(exynos_mipi_video_phy_driver); - -MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver"); -MODULE_AUTHOR("Sylwester Nawrocki "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-exynos-pcie.c b/drivers/phy/phy-exynos-pcie.c deleted file mode 100644 index a89c12faff39..000000000000 --- a/drivers/phy/phy-exynos-pcie.c +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Samsung EXYNOS SoC series PCIe PHY driver - * - * Phy provider for PCIe controller on Exynos SoC series - * - * Copyright (C) 2017 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 -#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 { - const 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; - - if (readl_poll_timeout(ep->phy_base + PCIE_PHY_PLL_LOCKED, val, - (val != 0), 1, 500)) { - dev_err(&phy->dev, "PLL Locked: 0x%x\n", val); - return -ETIMEDOUT; - } - - 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 const 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, - .owner = THIS_MODULE, -}; - -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, - }, - {}, -}; - -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->blk_base)) - return PTR_ERR(exynos_phy->blk_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", - } -}; - -builtin_platform_driver(exynos_pcie_phy_driver); diff --git a/drivers/phy/phy-exynos4210-usb2.c b/drivers/phy/phy-exynos4210-usb2.c deleted file mode 100644 index 1f50e1004828..000000000000 --- a/drivers/phy/phy-exynos4210-usb2.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support - * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * Author: Kamil Debski - * - * 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 "phy-samsung-usb2.h" - -/* Exynos USB PHY registers */ - -/* PHY power control */ -#define EXYNOS_4210_UPHYPWR 0x0 - -#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND BIT(0) -#define EXYNOS_4210_UPHYPWR_PHY0_PWR BIT(3) -#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR BIT(4) -#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP BIT(5) -#define EXYNOS_4210_UPHYPWR_PHY0 ( \ - EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \ - EXYNOS_4210_UPHYPWR_PHY0_PWR | \ - EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \ - EXYNOS_4210_UPHYPWR_PHY0_SLEEP) - -#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND BIT(6) -#define EXYNOS_4210_UPHYPWR_PHY1_PWR BIT(7) -#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP BIT(8) -#define EXYNOS_4210_UPHYPWR_PHY1 ( \ - EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \ - EXYNOS_4210_UPHYPWR_PHY1_PWR | \ - EXYNOS_4210_UPHYPWR_PHY1_SLEEP) - -#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND BIT(9) -#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP BIT(10) -#define EXYNOS_4210_UPHYPWR_HSIC0 ( \ - EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \ - EXYNOS_4210_UPHYPWR_HSIC0_SLEEP) - -#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND BIT(11) -#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP BIT(12) -#define EXYNOS_4210_UPHYPWR_HSIC1 ( \ - EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \ - EXYNOS_4210_UPHYPWR_HSIC1_SLEEP) - -/* PHY clock control */ -#define EXYNOS_4210_UPHYCLK 0x4 - -#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) -#define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET 0 -#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) -#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) -#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) - -#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP BIT(2) -#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON BIT(4) -#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON BIT(7) - -/* PHY reset control */ -#define EXYNOS_4210_UPHYRST 0x8 - -#define EXYNOS_4210_URSTCON_PHY0 BIT(0) -#define EXYNOS_4210_URSTCON_OTG_HLINK BIT(1) -#define EXYNOS_4210_URSTCON_OTG_PHYLINK BIT(2) -#define EXYNOS_4210_URSTCON_PHY1_ALL BIT(3) -#define EXYNOS_4210_URSTCON_PHY1_P0 BIT(4) -#define EXYNOS_4210_URSTCON_PHY1_P1P2 BIT(5) -#define EXYNOS_4210_URSTCON_HOST_LINK_ALL BIT(6) -#define EXYNOS_4210_URSTCON_HOST_LINK_P0 BIT(7) -#define EXYNOS_4210_URSTCON_HOST_LINK_P1 BIT(8) -#define EXYNOS_4210_URSTCON_HOST_LINK_P2 BIT(9) - -/* Isolation, configured in the power management unit */ -#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET 0x704 -#define EXYNOS_4210_USB_ISOL_DEVICE BIT(0) -#define EXYNOS_4210_USB_ISOL_HOST_OFFSET 0x708 -#define EXYNOS_4210_USB_ISOL_HOST BIT(0) - -/* USBYPHY1 Floating prevention */ -#define EXYNOS_4210_UPHY1CON 0x34 -#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION 0x1 - -/* Mode switching SUB Device <-> Host */ -#define EXYNOS_4210_MODE_SWITCH_OFFSET 0x21c -#define EXYNOS_4210_MODE_SWITCH_MASK 1 -#define EXYNOS_4210_MODE_SWITCH_DEVICE 0 -#define EXYNOS_4210_MODE_SWITCH_HOST 1 - -enum exynos4210_phy_id { - EXYNOS4210_DEVICE, - EXYNOS4210_HOST, - EXYNOS4210_HSIC0, - EXYNOS4210_HSIC1, - EXYNOS4210_NUM_PHYS, -}; - -/* - * exynos4210_rate_to_clk() converts the supplied clock rate to the value that - * can be written to the phy register. - */ -static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg) -{ - switch (rate) { - case 12 * MHZ: - *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ; - break; - case 24 * MHZ: - *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ; - break; - case 48 * MHZ: - *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ; - break; - default: - return -EINVAL; - } - - return 0; -} - -static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on) -{ - struct samsung_usb2_phy_driver *drv = inst->drv; - u32 offset; - u32 mask; - - switch (inst->cfg->id) { - case EXYNOS4210_DEVICE: - offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET; - mask = EXYNOS_4210_USB_ISOL_DEVICE; - break; - case EXYNOS4210_HOST: - offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET; - mask = EXYNOS_4210_USB_ISOL_HOST; - break; - default: - return; - } - - regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); -} - -static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) -{ - struct samsung_usb2_phy_driver *drv = inst->drv; - u32 rstbits = 0; - u32 phypwr = 0; - u32 rst; - u32 pwr; - u32 clk; - - switch (inst->cfg->id) { - case EXYNOS4210_DEVICE: - phypwr = EXYNOS_4210_UPHYPWR_PHY0; - rstbits = EXYNOS_4210_URSTCON_PHY0; - break; - case EXYNOS4210_HOST: - phypwr = EXYNOS_4210_UPHYPWR_PHY1; - rstbits = EXYNOS_4210_URSTCON_PHY1_ALL | - EXYNOS_4210_URSTCON_PHY1_P0 | - EXYNOS_4210_URSTCON_PHY1_P1P2 | - EXYNOS_4210_URSTCON_HOST_LINK_ALL | - EXYNOS_4210_URSTCON_HOST_LINK_P0; - writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON); - break; - case EXYNOS4210_HSIC0: - phypwr = EXYNOS_4210_UPHYPWR_HSIC0; - rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | - EXYNOS_4210_URSTCON_HOST_LINK_P1; - break; - case EXYNOS4210_HSIC1: - phypwr = EXYNOS_4210_UPHYPWR_HSIC1; - rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | - EXYNOS_4210_URSTCON_HOST_LINK_P2; - break; - } - - if (on) { - clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK); - clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK; - clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET; - writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK); - - pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR); - pwr &= ~phypwr; - writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR); - - rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST); - rst |= rstbits; - writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST); - udelay(10); - rst &= ~rstbits; - writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST); - /* The following delay is necessary for the reset sequence to be - * completed */ - udelay(80); - } else { - pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR); - pwr |= phypwr; - writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR); - } -} - -static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst) -{ - /* Order of initialisation is important - first power then isolation */ - exynos4210_phy_pwr(inst, 1); - exynos4210_isol(inst, 0); - - return 0; -} - -static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst) -{ - exynos4210_isol(inst, 1); - exynos4210_phy_pwr(inst, 0); - - return 0; -} - - -static const struct samsung_usb2_common_phy exynos4210_phys[] = { - { - .label = "device", - .id = EXYNOS4210_DEVICE, - .power_on = exynos4210_power_on, - .power_off = exynos4210_power_off, - }, - { - .label = "host", - .id = EXYNOS4210_HOST, - .power_on = exynos4210_power_on, - .power_off = exynos4210_power_off, - }, - { - .label = "hsic0", - .id = EXYNOS4210_HSIC0, - .power_on = exynos4210_power_on, - .power_off = exynos4210_power_off, - }, - { - .label = "hsic1", - .id = EXYNOS4210_HSIC1, - .power_on = exynos4210_power_on, - .power_off = exynos4210_power_off, - }, -}; - -const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = { - .has_mode_switch = 0, - .num_phys = EXYNOS4210_NUM_PHYS, - .phys = exynos4210_phys, - .rate_to_clk = exynos4210_rate_to_clk, -}; diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c deleted file mode 100644 index 7f27a91acf87..000000000000 --- a/drivers/phy/phy-exynos4x12-usb2.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support - * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * Author: Kamil Debski - * - * 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 "phy-samsung-usb2.h" - -/* Exynos USB PHY registers */ - -/* PHY power control */ -#define EXYNOS_4x12_UPHYPWR 0x0 - -#define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND BIT(0) -#define EXYNOS_4x12_UPHYPWR_PHY0_PWR BIT(3) -#define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR BIT(4) -#define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP BIT(5) -#define EXYNOS_4x12_UPHYPWR_PHY0 ( \ - EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \ - EXYNOS_4x12_UPHYPWR_PHY0_PWR | \ - EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \ - EXYNOS_4x12_UPHYPWR_PHY0_SLEEP) - -#define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND BIT(6) -#define EXYNOS_4x12_UPHYPWR_PHY1_PWR BIT(7) -#define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP BIT(8) -#define EXYNOS_4x12_UPHYPWR_PHY1 ( \ - EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \ - EXYNOS_4x12_UPHYPWR_PHY1_PWR | \ - EXYNOS_4x12_UPHYPWR_PHY1_SLEEP) - -#define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND BIT(9) -#define EXYNOS_4x12_UPHYPWR_HSIC0_PWR BIT(10) -#define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP BIT(11) -#define EXYNOS_4x12_UPHYPWR_HSIC0 ( \ - EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \ - EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \ - EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP) - -#define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND BIT(12) -#define EXYNOS_4x12_UPHYPWR_HSIC1_PWR BIT(13) -#define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP BIT(14) -#define EXYNOS_4x12_UPHYPWR_HSIC1 ( \ - EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \ - EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \ - EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP) - -/* PHY clock control */ -#define EXYNOS_4x12_UPHYCLK 0x4 - -#define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK (0x7 << 0) -#define EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET 0 -#define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6 (0x0 << 0) -#define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ (0x1 << 0) -#define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) -#define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2 (0x3 << 0) -#define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ (0x4 << 0) -#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0) -#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 0) - -#define EXYNOS_3250_UPHYCLK_REFCLKSEL (0x2 << 8) - -#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP BIT(3) -#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4) -#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7) - -#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK (0x7f << 10) -#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_OFFSET 10 -#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ (0x24 << 10) -#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ (0x1c << 10) -#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ (0x1a << 10) -#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2 (0x15 << 10) -#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ (0x14 << 10) - -/* PHY reset control */ -#define EXYNOS_4x12_UPHYRST 0x8 - -#define EXYNOS_4x12_URSTCON_PHY0 BIT(0) -#define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1) -#define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2) -#define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3) -/* The following bit defines are presented in the - * order taken from the Exynos4412 reference manual. - * - * During experiments with the hardware and debugging - * it was determined that the hardware behaves contrary - * to the manual. - * - * The following bit values were chaned accordingly to the - * results of real hardware experiments. - */ -#define EXYNOS_4x12_URSTCON_PHY1 BIT(4) -#define EXYNOS_4x12_URSTCON_HSIC0 BIT(6) -#define EXYNOS_4x12_URSTCON_HSIC1 BIT(5) -#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7) -#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(10) -#define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9) -#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(8) - -/* Isolation, configured in the power management unit */ -#define EXYNOS_4x12_USB_ISOL_OFFSET 0x704 -#define EXYNOS_4x12_USB_ISOL_OTG BIT(0) -#define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET 0x708 -#define EXYNOS_4x12_USB_ISOL_HSIC0 BIT(0) -#define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET 0x70c -#define EXYNOS_4x12_USB_ISOL_HSIC1 BIT(0) - -/* Mode switching SUB Device <-> Host */ -#define EXYNOS_4x12_MODE_SWITCH_OFFSET 0x21c -#define EXYNOS_4x12_MODE_SWITCH_MASK 1 -#define EXYNOS_4x12_MODE_SWITCH_DEVICE 0 -#define EXYNOS_4x12_MODE_SWITCH_HOST 1 - -enum exynos4x12_phy_id { - EXYNOS4x12_DEVICE, - EXYNOS4x12_HOST, - EXYNOS4x12_HSIC0, - EXYNOS4x12_HSIC1, - EXYNOS4x12_NUM_PHYS, -}; - -/* - * exynos4x12_rate_to_clk() converts the supplied clock rate to the value that - * can be written to the phy register. - */ -static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg) -{ - /* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */ - - switch (rate) { - case 9600 * KHZ: - *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6; - break; - case 10 * MHZ: - *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ; - break; - case 12 * MHZ: - *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ; - break; - case 19200 * KHZ: - *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2; - break; - case 20 * MHZ: - *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ; - break; - case 24 * MHZ: - *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ; - break; - case 50 * MHZ: - *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ; - break; - default: - return -EINVAL; - } - - return 0; -} - -static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on) -{ - struct samsung_usb2_phy_driver *drv = inst->drv; - u32 offset; - u32 mask; - - switch (inst->cfg->id) { - case EXYNOS4x12_DEVICE: - case EXYNOS4x12_HOST: - offset = EXYNOS_4x12_USB_ISOL_OFFSET; - mask = EXYNOS_4x12_USB_ISOL_OTG; - break; - case EXYNOS4x12_HSIC0: - offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET; - mask = EXYNOS_4x12_USB_ISOL_HSIC0; - break; - case EXYNOS4x12_HSIC1: - offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET; - mask = EXYNOS_4x12_USB_ISOL_HSIC1; - break; - default: - return; - } - - regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); -} - -static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst) -{ - struct samsung_usb2_phy_driver *drv = inst->drv; - u32 clk; - - clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK); - clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK; - - if (drv->cfg->has_refclk_sel) - clk = EXYNOS_3250_UPHYCLK_REFCLKSEL; - - clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET; - clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON; - writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK); -} - -static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) -{ - struct samsung_usb2_phy_driver *drv = inst->drv; - u32 rstbits = 0; - u32 phypwr = 0; - u32 rst; - u32 pwr; - - switch (inst->cfg->id) { - case EXYNOS4x12_DEVICE: - phypwr = EXYNOS_4x12_UPHYPWR_PHY0; - rstbits = EXYNOS_4x12_URSTCON_PHY0; - break; - case EXYNOS4x12_HOST: - phypwr = EXYNOS_4x12_UPHYPWR_PHY1; - rstbits = EXYNOS_4x12_URSTCON_HOST_PHY | - EXYNOS_4x12_URSTCON_PHY1 | - EXYNOS_4x12_URSTCON_HOST_LINK_P0; - break; - case EXYNOS4x12_HSIC0: - phypwr = EXYNOS_4x12_UPHYPWR_HSIC0; - rstbits = EXYNOS_4x12_URSTCON_HSIC0 | - EXYNOS_4x12_URSTCON_HOST_LINK_P1; - break; - case EXYNOS4x12_HSIC1: - phypwr = EXYNOS_4x12_UPHYPWR_HSIC1; - rstbits = EXYNOS_4x12_URSTCON_HSIC1 | - EXYNOS_4x12_URSTCON_HOST_LINK_P1; - break; - } - - if (on) { - pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); - pwr &= ~phypwr; - writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); - - rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST); - rst |= rstbits; - writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST); - udelay(10); - rst &= ~rstbits; - writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST); - /* The following delay is necessary for the reset sequence to be - * completed */ - udelay(80); - } else { - pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); - pwr |= phypwr; - writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); - } -} - -static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst) -{ - if (inst->int_cnt++ > 0) - return; - - exynos4x12_setup_clk(inst); - exynos4x12_isol(inst, 0); - exynos4x12_phy_pwr(inst, 1); -} - -static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst) -{ - struct samsung_usb2_phy_driver *drv = inst->drv; - - if (inst->ext_cnt++ > 0) - return 0; - - if (inst->cfg->id == EXYNOS4x12_HOST) { - regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, - EXYNOS_4x12_MODE_SWITCH_MASK, - EXYNOS_4x12_MODE_SWITCH_HOST); - exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); - } - - if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch) - regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, - EXYNOS_4x12_MODE_SWITCH_MASK, - EXYNOS_4x12_MODE_SWITCH_DEVICE); - - if (inst->cfg->id == EXYNOS4x12_HSIC0 || - inst->cfg->id == EXYNOS4x12_HSIC1) { - exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); - exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_HOST]); - } - - exynos4x12_power_on_int(inst); - - return 0; -} - -static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst) -{ - if (inst->int_cnt-- > 1) - return; - - exynos4x12_isol(inst, 1); - exynos4x12_phy_pwr(inst, 0); -} - -static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst) -{ - struct samsung_usb2_phy_driver *drv = inst->drv; - - if (inst->ext_cnt-- > 1) - return 0; - - if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch) - regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, - EXYNOS_4x12_MODE_SWITCH_MASK, - EXYNOS_4x12_MODE_SWITCH_HOST); - - if (inst->cfg->id == EXYNOS4x12_HOST) - exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); - - if (inst->cfg->id == EXYNOS4x12_HSIC0 || - inst->cfg->id == EXYNOS4x12_HSIC1) { - exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); - exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]); - } - - exynos4x12_power_off_int(inst); - - return 0; -} - - -static const struct samsung_usb2_common_phy exynos4x12_phys[] = { - { - .label = "device", - .id = EXYNOS4x12_DEVICE, - .power_on = exynos4x12_power_on, - .power_off = exynos4x12_power_off, - }, - { - .label = "host", - .id = EXYNOS4x12_HOST, - .power_on = exynos4x12_power_on, - .power_off = exynos4x12_power_off, - }, - { - .label = "hsic0", - .id = EXYNOS4x12_HSIC0, - .power_on = exynos4x12_power_on, - .power_off = exynos4x12_power_off, - }, - { - .label = "hsic1", - .id = EXYNOS4x12_HSIC1, - .power_on = exynos4x12_power_on, - .power_off = exynos4x12_power_off, - }, -}; - -const struct samsung_usb2_phy_config exynos3250_usb2_phy_config = { - .has_refclk_sel = 1, - .num_phys = 1, - .phys = exynos4x12_phys, - .rate_to_clk = exynos4x12_rate_to_clk, -}; - -const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = { - .has_mode_switch = 1, - .num_phys = EXYNOS4x12_NUM_PHYS, - .phys = exynos4x12_phys, - .rate_to_clk = exynos4x12_rate_to_clk, -}; diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c deleted file mode 100644 index 7c41daa2c625..000000000000 --- a/drivers/phy/phy-exynos5-usbdrd.c +++ /dev/null @@ -1,782 +0,0 @@ -/* - * Samsung EXYNOS5 SoC series USB DRD PHY driver - * - * Phy provider for USB 3.0 DRD controller on Exynos5 SoC series - * - * Copyright (C) 2014 Samsung Electronics Co., Ltd. - * Author: Vivek Gautam - * - * 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 -#include -#include -#include -#include - -/* Exynos USB PHY registers */ -#define EXYNOS5_FSEL_9MHZ6 0x0 -#define EXYNOS5_FSEL_10MHZ 0x1 -#define EXYNOS5_FSEL_12MHZ 0x2 -#define EXYNOS5_FSEL_19MHZ2 0x3 -#define EXYNOS5_FSEL_20MHZ 0x4 -#define EXYNOS5_FSEL_24MHZ 0x5 -#define EXYNOS5_FSEL_50MHZ 0x7 - -/* EXYNOS5: USB 3.0 DRD PHY registers */ -#define EXYNOS5_DRD_LINKSYSTEM 0x04 - -#define LINKSYSTEM_FLADJ_MASK (0x3f << 1) -#define LINKSYSTEM_FLADJ(_x) ((_x) << 1) -#define LINKSYSTEM_XHCI_VERSION_CONTROL BIT(27) - -#define EXYNOS5_DRD_PHYUTMI 0x08 - -#define PHYUTMI_OTGDISABLE BIT(6) -#define PHYUTMI_FORCESUSPEND BIT(1) -#define PHYUTMI_FORCESLEEP BIT(0) - -#define EXYNOS5_DRD_PHYPIPE 0x0c - -#define EXYNOS5_DRD_PHYCLKRST 0x10 - -#define PHYCLKRST_EN_UTMISUSPEND BIT(31) - -#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) -#define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23) - -#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) -#define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21) - -#define PHYCLKRST_SSC_EN BIT(20) -#define PHYCLKRST_REF_SSP_EN BIT(19) -#define PHYCLKRST_REF_CLKDIV2 BIT(18) - -#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF (0x32 << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF (0x68 << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF (0x7d << 11) -#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11) - -#define PHYCLKRST_FSEL_UTMI_MASK (0x7 << 5) -#define PHYCLKRST_FSEL_PIPE_MASK (0x7 << 8) -#define PHYCLKRST_FSEL(_x) ((_x) << 5) -#define PHYCLKRST_FSEL_PAD_100MHZ (0x27 << 5) -#define PHYCLKRST_FSEL_PAD_24MHZ (0x2a << 5) -#define PHYCLKRST_FSEL_PAD_20MHZ (0x31 << 5) -#define PHYCLKRST_FSEL_PAD_19_2MHZ (0x38 << 5) - -#define PHYCLKRST_RETENABLEN BIT(4) - -#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) -#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2) -#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2) - -#define PHYCLKRST_PORTRESET BIT(1) -#define PHYCLKRST_COMMONONN BIT(0) - -#define EXYNOS5_DRD_PHYREG0 0x14 -#define EXYNOS5_DRD_PHYREG1 0x18 - -#define EXYNOS5_DRD_PHYPARAM0 0x1c - -#define PHYPARAM0_REF_USE_PAD BIT(31) -#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) -#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) - -#define EXYNOS5_DRD_PHYPARAM1 0x20 - -#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) -#define PHYPARAM1_PCS_TXDEEMPH (0x1c) - -#define EXYNOS5_DRD_PHYTERM 0x24 - -#define EXYNOS5_DRD_PHYTEST 0x28 - -#define PHYTEST_POWERDOWN_SSP BIT(3) -#define PHYTEST_POWERDOWN_HSP BIT(2) - -#define EXYNOS5_DRD_PHYADP 0x2c - -#define EXYNOS5_DRD_PHYUTMICLKSEL 0x30 - -#define PHYUTMICLKSEL_UTMI_CLKSEL BIT(2) - -#define EXYNOS5_DRD_PHYRESUME 0x34 -#define EXYNOS5_DRD_LINKPORT 0x44 - -#define KHZ 1000 -#define MHZ (KHZ * KHZ) - -enum exynos5_usbdrd_phy_id { - EXYNOS5_DRDPHY_UTMI, - EXYNOS5_DRDPHY_PIPE3, - EXYNOS5_DRDPHYS_NUM, -}; - -struct phy_usb_instance; -struct exynos5_usbdrd_phy; - -struct exynos5_usbdrd_phy_config { - u32 id; - void (*phy_isol)(struct phy_usb_instance *inst, u32 on); - void (*phy_init)(struct exynos5_usbdrd_phy *phy_drd); - unsigned int (*set_refclk)(struct phy_usb_instance *inst); -}; - -struct exynos5_usbdrd_phy_drvdata { - const struct exynos5_usbdrd_phy_config *phy_cfg; - u32 pmu_offset_usbdrd0_phy; - u32 pmu_offset_usbdrd1_phy; - bool has_common_clk_gate; -}; - -/** - * struct exynos5_usbdrd_phy - driver data for USB 3.0 PHY - * @dev: pointer to device instance of this platform device - * @reg_phy: usb phy controller register memory base - * @clk: phy clock for register access - * @pipeclk: clock for pipe3 phy - * @utmiclk: clock for utmi+ phy - * @itpclk: clock for ITP generation - * @drv_data: pointer to SoC level driver data structure - * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY - * instances each with its 'phy' and 'phy_cfg'. - * @extrefclk: frequency select settings when using 'separate - * reference clocks' for SS and HS operations - * @ref_clk: reference clock to PHY block from which PHY's - * operational clocks are derived - * vbus: VBUS regulator for phy - * vbus_boost: Boost regulator for VBUS present on few Exynos boards - */ -struct exynos5_usbdrd_phy { - struct device *dev; - void __iomem *reg_phy; - struct clk *clk; - struct clk *pipeclk; - struct clk *utmiclk; - struct clk *itpclk; - const struct exynos5_usbdrd_phy_drvdata *drv_data; - struct phy_usb_instance { - struct phy *phy; - u32 index; - struct regmap *reg_pmu; - u32 pmu_offset; - const struct exynos5_usbdrd_phy_config *phy_cfg; - } phys[EXYNOS5_DRDPHYS_NUM]; - u32 extrefclk; - struct clk *ref_clk; - struct regulator *vbus; - struct regulator *vbus_boost; -}; - -static inline -struct exynos5_usbdrd_phy *to_usbdrd_phy(struct phy_usb_instance *inst) -{ - return container_of((inst), struct exynos5_usbdrd_phy, - phys[(inst)->index]); -} - -/* - * exynos5_rate_to_clk() converts the supplied clock rate to the value that - * can be written to the phy register. - */ -static unsigned int exynos5_rate_to_clk(unsigned long rate, u32 *reg) -{ - /* EXYNOS5_FSEL_MASK */ - - switch (rate) { - case 9600 * KHZ: - *reg = EXYNOS5_FSEL_9MHZ6; - break; - case 10 * MHZ: - *reg = EXYNOS5_FSEL_10MHZ; - break; - case 12 * MHZ: - *reg = EXYNOS5_FSEL_12MHZ; - break; - case 19200 * KHZ: - *reg = EXYNOS5_FSEL_19MHZ2; - break; - case 20 * MHZ: - *reg = EXYNOS5_FSEL_20MHZ; - break; - case 24 * MHZ: - *reg = EXYNOS5_FSEL_24MHZ; - break; - case 50 * MHZ: - *reg = EXYNOS5_FSEL_50MHZ; - break; - default: - return -EINVAL; - } - - return 0; -} - -static void exynos5_usbdrd_phy_isol(struct phy_usb_instance *inst, - unsigned int on) -{ - unsigned int val; - - if (!inst->reg_pmu) - return; - - val = on ? 0 : EXYNOS4_PHY_ENABLE; - - regmap_update_bits(inst->reg_pmu, inst->pmu_offset, - EXYNOS4_PHY_ENABLE, val); -} - -/* - * Sets the pipe3 phy's clk as EXTREFCLK (XXTI) which is internal clock - * from clock core. Further sets multiplier values and spread spectrum - * clock settings for SuperSpeed operations. - */ -static unsigned int -exynos5_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst) -{ - u32 reg; - struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); - - /* restore any previous reference clock settings */ - reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); - - /* Use EXTREFCLK as ref clock */ - reg &= ~PHYCLKRST_REFCLKSEL_MASK; - reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; - - /* FSEL settings corresponding to reference clock */ - reg &= ~PHYCLKRST_FSEL_PIPE_MASK | - PHYCLKRST_MPLL_MULTIPLIER_MASK | - PHYCLKRST_SSC_REFCLKSEL_MASK; - switch (phy_drd->extrefclk) { - case EXYNOS5_FSEL_50MHZ: - reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF | - PHYCLKRST_SSC_REFCLKSEL(0x00)); - break; - case EXYNOS5_FSEL_24MHZ: - reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | - PHYCLKRST_SSC_REFCLKSEL(0x88)); - break; - case EXYNOS5_FSEL_20MHZ: - reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF | - PHYCLKRST_SSC_REFCLKSEL(0x00)); - break; - case EXYNOS5_FSEL_19MHZ2: - reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF | - PHYCLKRST_SSC_REFCLKSEL(0x88)); - break; - default: - dev_dbg(phy_drd->dev, "unsupported ref clk\n"); - break; - } - - return reg; -} - -/* - * Sets the utmi phy's clk as EXTREFCLK (XXTI) which is internal clock - * from clock core. Further sets the FSEL values for HighSpeed operations. - */ -static unsigned int -exynos5_usbdrd_utmi_set_refclk(struct phy_usb_instance *inst) -{ - u32 reg; - struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); - - /* restore any previous reference clock settings */ - reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); - - reg &= ~PHYCLKRST_REFCLKSEL_MASK; - reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; - - reg &= ~PHYCLKRST_FSEL_UTMI_MASK | - PHYCLKRST_MPLL_MULTIPLIER_MASK | - PHYCLKRST_SSC_REFCLKSEL_MASK; - reg |= PHYCLKRST_FSEL(phy_drd->extrefclk); - - return reg; -} - -static void exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd) -{ - u32 reg; - - reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); - /* Set Tx De-Emphasis level */ - reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; - reg |= PHYPARAM1_PCS_TXDEEMPH; - writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); - - reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); - reg &= ~PHYTEST_POWERDOWN_SSP; - writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); -} - -static void exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) -{ - u32 reg; - - reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); - /* Set Loss-of-Signal Detector sensitivity */ - reg &= ~PHYPARAM0_REF_LOSLEVEL_MASK; - reg |= PHYPARAM0_REF_LOSLEVEL; - writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); - - reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); - /* Set Tx De-Emphasis level */ - reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; - reg |= PHYPARAM1_PCS_TXDEEMPH; - writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); - - /* UTMI Power Control */ - writel(PHYUTMI_OTGDISABLE, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); - - reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); - reg &= ~PHYTEST_POWERDOWN_HSP; - writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); -} - -static int exynos5_usbdrd_phy_init(struct phy *phy) -{ - int ret; - u32 reg; - struct phy_usb_instance *inst = phy_get_drvdata(phy); - struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); - - ret = clk_prepare_enable(phy_drd->clk); - if (ret) - return ret; - - /* Reset USB 3.0 PHY */ - writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); - writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYRESUME); - - /* - * Setting the Frame length Adj value[6:1] to default 0x20 - * See xHCI 1.0 spec, 5.2.4 - */ - reg = LINKSYSTEM_XHCI_VERSION_CONTROL | - LINKSYSTEM_FLADJ(0x20); - writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_LINKSYSTEM); - - reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); - /* Select PHY CLK source */ - reg &= ~PHYPARAM0_REF_USE_PAD; - writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); - - /* This bit must be set for both HS and SS operations */ - reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); - reg |= PHYUTMICLKSEL_UTMI_CLKSEL; - writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); - - /* UTMI or PIPE3 specific init */ - inst->phy_cfg->phy_init(phy_drd); - - /* reference clock settings */ - reg = inst->phy_cfg->set_refclk(inst); - - /* Digital power supply in normal operating mode */ - reg |= PHYCLKRST_RETENABLEN | - /* Enable ref clock for SS function */ - PHYCLKRST_REF_SSP_EN | - /* Enable spread spectrum */ - PHYCLKRST_SSC_EN | - /* Power down HS Bias and PLL blocks in suspend mode */ - PHYCLKRST_COMMONONN | - /* Reset the port */ - PHYCLKRST_PORTRESET; - - writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); - - udelay(10); - - reg &= ~PHYCLKRST_PORTRESET; - writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); - - clk_disable_unprepare(phy_drd->clk); - - return 0; -} - -static int exynos5_usbdrd_phy_exit(struct phy *phy) -{ - int ret; - u32 reg; - struct phy_usb_instance *inst = phy_get_drvdata(phy); - struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); - - ret = clk_prepare_enable(phy_drd->clk); - if (ret) - return ret; - - reg = PHYUTMI_OTGDISABLE | - PHYUTMI_FORCESUSPEND | - PHYUTMI_FORCESLEEP; - writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); - - /* Resetting the PHYCLKRST enable bits to reduce leakage current */ - reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); - reg &= ~(PHYCLKRST_REF_SSP_EN | - PHYCLKRST_SSC_EN | - PHYCLKRST_COMMONONN); - writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); - - /* Control PHYTEST to remove leakage current */ - reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); - reg |= PHYTEST_POWERDOWN_SSP | - PHYTEST_POWERDOWN_HSP; - writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); - - clk_disable_unprepare(phy_drd->clk); - - return 0; -} - -static int exynos5_usbdrd_phy_power_on(struct phy *phy) -{ - int ret; - struct phy_usb_instance *inst = phy_get_drvdata(phy); - struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); - - dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n"); - - clk_prepare_enable(phy_drd->ref_clk); - if (!phy_drd->drv_data->has_common_clk_gate) { - clk_prepare_enable(phy_drd->pipeclk); - clk_prepare_enable(phy_drd->utmiclk); - clk_prepare_enable(phy_drd->itpclk); - } - - /* Enable VBUS supply */ - if (phy_drd->vbus_boost) { - ret = regulator_enable(phy_drd->vbus_boost); - if (ret) { - dev_err(phy_drd->dev, - "Failed to enable VBUS boost supply\n"); - goto fail_vbus; - } - } - - if (phy_drd->vbus) { - ret = regulator_enable(phy_drd->vbus); - if (ret) { - dev_err(phy_drd->dev, "Failed to enable VBUS supply\n"); - goto fail_vbus_boost; - } - } - - /* Power-on PHY*/ - inst->phy_cfg->phy_isol(inst, 0); - - return 0; - -fail_vbus_boost: - if (phy_drd->vbus_boost) - regulator_disable(phy_drd->vbus_boost); - -fail_vbus: - clk_disable_unprepare(phy_drd->ref_clk); - if (!phy_drd->drv_data->has_common_clk_gate) { - clk_disable_unprepare(phy_drd->itpclk); - clk_disable_unprepare(phy_drd->utmiclk); - clk_disable_unprepare(phy_drd->pipeclk); - } - - return ret; -} - -static int exynos5_usbdrd_phy_power_off(struct phy *phy) -{ - struct phy_usb_instance *inst = phy_get_drvdata(phy); - struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); - - dev_dbg(phy_drd->dev, "Request to power_off usbdrd_phy phy\n"); - - /* Power-off the PHY */ - inst->phy_cfg->phy_isol(inst, 1); - - /* Disable VBUS supply */ - if (phy_drd->vbus) - regulator_disable(phy_drd->vbus); - if (phy_drd->vbus_boost) - regulator_disable(phy_drd->vbus_boost); - - clk_disable_unprepare(phy_drd->ref_clk); - if (!phy_drd->drv_data->has_common_clk_gate) { - clk_disable_unprepare(phy_drd->itpclk); - clk_disable_unprepare(phy_drd->pipeclk); - clk_disable_unprepare(phy_drd->utmiclk); - } - - return 0; -} - -static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev, - struct of_phandle_args *args) -{ - struct exynos5_usbdrd_phy *phy_drd = dev_get_drvdata(dev); - - if (WARN_ON(args->args[0] >= EXYNOS5_DRDPHYS_NUM)) - return ERR_PTR(-ENODEV); - - return phy_drd->phys[args->args[0]].phy; -} - -static const struct phy_ops exynos5_usbdrd_phy_ops = { - .init = exynos5_usbdrd_phy_init, - .exit = exynos5_usbdrd_phy_exit, - .power_on = exynos5_usbdrd_phy_power_on, - .power_off = exynos5_usbdrd_phy_power_off, - .owner = THIS_MODULE, -}; - -static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd) -{ - unsigned long ref_rate; - int ret; - - phy_drd->clk = devm_clk_get(phy_drd->dev, "phy"); - if (IS_ERR(phy_drd->clk)) { - dev_err(phy_drd->dev, "Failed to get phy clock\n"); - return PTR_ERR(phy_drd->clk); - } - - phy_drd->ref_clk = devm_clk_get(phy_drd->dev, "ref"); - if (IS_ERR(phy_drd->ref_clk)) { - dev_err(phy_drd->dev, "Failed to get phy reference clock\n"); - return PTR_ERR(phy_drd->ref_clk); - } - ref_rate = clk_get_rate(phy_drd->ref_clk); - - ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); - if (ret) { - dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n", - ref_rate); - return ret; - } - - if (!phy_drd->drv_data->has_common_clk_gate) { - phy_drd->pipeclk = devm_clk_get(phy_drd->dev, "phy_pipe"); - if (IS_ERR(phy_drd->pipeclk)) { - dev_info(phy_drd->dev, - "PIPE3 phy operational clock not specified\n"); - phy_drd->pipeclk = NULL; - } - - phy_drd->utmiclk = devm_clk_get(phy_drd->dev, "phy_utmi"); - if (IS_ERR(phy_drd->utmiclk)) { - dev_info(phy_drd->dev, - "UTMI phy operational clock not specified\n"); - phy_drd->utmiclk = NULL; - } - - phy_drd->itpclk = devm_clk_get(phy_drd->dev, "itp"); - if (IS_ERR(phy_drd->itpclk)) { - dev_info(phy_drd->dev, - "ITP clock from main OSC not specified\n"); - phy_drd->itpclk = NULL; - } - } - - return 0; -} - -static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { - { - .id = EXYNOS5_DRDPHY_UTMI, - .phy_isol = exynos5_usbdrd_phy_isol, - .phy_init = exynos5_usbdrd_utmi_init, - .set_refclk = exynos5_usbdrd_utmi_set_refclk, - }, - { - .id = EXYNOS5_DRDPHY_PIPE3, - .phy_isol = exynos5_usbdrd_phy_isol, - .phy_init = exynos5_usbdrd_pipe3_init, - .set_refclk = exynos5_usbdrd_pipe3_set_refclk, - }, -}; - -static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { - .phy_cfg = phy_cfg_exynos5, - .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, - .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL, - .has_common_clk_gate = true, -}; - -static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { - .phy_cfg = phy_cfg_exynos5, - .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, - .has_common_clk_gate = true, -}; - -static const struct exynos5_usbdrd_phy_drvdata exynos5433_usbdrd_phy = { - .phy_cfg = phy_cfg_exynos5, - .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, - .pmu_offset_usbdrd1_phy = EXYNOS5433_USBHOST30_PHY_CONTROL, - .has_common_clk_gate = false, -}; - -static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = { - .phy_cfg = phy_cfg_exynos5, - .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, - .has_common_clk_gate = false, -}; - -static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { - { - .compatible = "samsung,exynos5250-usbdrd-phy", - .data = &exynos5250_usbdrd_phy - }, { - .compatible = "samsung,exynos5420-usbdrd-phy", - .data = &exynos5420_usbdrd_phy - }, { - .compatible = "samsung,exynos5433-usbdrd-phy", - .data = &exynos5433_usbdrd_phy - }, { - .compatible = "samsung,exynos7-usbdrd-phy", - .data = &exynos7_usbdrd_phy - }, - { }, -}; -MODULE_DEVICE_TABLE(of, exynos5_usbdrd_phy_of_match); - -static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *node = dev->of_node; - struct exynos5_usbdrd_phy *phy_drd; - struct phy_provider *phy_provider; - struct resource *res; - const struct of_device_id *match; - const struct exynos5_usbdrd_phy_drvdata *drv_data; - struct regmap *reg_pmu; - u32 pmu_offset; - int i, ret; - int channel; - - phy_drd = devm_kzalloc(dev, sizeof(*phy_drd), GFP_KERNEL); - if (!phy_drd) - return -ENOMEM; - - dev_set_drvdata(dev, phy_drd); - phy_drd->dev = dev; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy_drd->reg_phy = devm_ioremap_resource(dev, res); - if (IS_ERR(phy_drd->reg_phy)) - return PTR_ERR(phy_drd->reg_phy); - - match = of_match_node(exynos5_usbdrd_phy_of_match, pdev->dev.of_node); - - drv_data = match->data; - phy_drd->drv_data = drv_data; - - ret = exynos5_usbdrd_phy_clk_handle(phy_drd); - if (ret) { - dev_err(dev, "Failed to initialize clocks\n"); - return ret; - } - - reg_pmu = syscon_regmap_lookup_by_phandle(dev->of_node, - "samsung,pmu-syscon"); - if (IS_ERR(reg_pmu)) { - dev_err(dev, "Failed to lookup PMU regmap\n"); - return PTR_ERR(reg_pmu); - } - - /* - * Exynos5420 SoC has multiple channels for USB 3.0 PHY, with - * each having separate power control registers. - * 'channel' facilitates to set such registers. - */ - channel = of_alias_get_id(node, "usbdrdphy"); - if (channel < 0) - dev_dbg(dev, "Not a multi-controller usbdrd phy\n"); - - switch (channel) { - case 1: - pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd1_phy; - break; - case 0: - default: - pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd0_phy; - break; - } - - /* Get Vbus regulators */ - phy_drd->vbus = devm_regulator_get(dev, "vbus"); - if (IS_ERR(phy_drd->vbus)) { - ret = PTR_ERR(phy_drd->vbus); - if (ret == -EPROBE_DEFER) - return ret; - - dev_warn(dev, "Failed to get VBUS supply regulator\n"); - phy_drd->vbus = NULL; - } - - phy_drd->vbus_boost = devm_regulator_get(dev, "vbus-boost"); - if (IS_ERR(phy_drd->vbus_boost)) { - ret = PTR_ERR(phy_drd->vbus_boost); - if (ret == -EPROBE_DEFER) - return ret; - - dev_warn(dev, "Failed to get VBUS boost supply regulator\n"); - phy_drd->vbus_boost = NULL; - } - - dev_vdbg(dev, "Creating usbdrd_phy phy\n"); - - for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { - struct phy *phy = devm_phy_create(dev, NULL, - &exynos5_usbdrd_phy_ops); - if (IS_ERR(phy)) { - dev_err(dev, "Failed to create usbdrd_phy phy\n"); - return PTR_ERR(phy); - } - - phy_drd->phys[i].phy = phy; - phy_drd->phys[i].index = i; - phy_drd->phys[i].reg_pmu = reg_pmu; - phy_drd->phys[i].pmu_offset = pmu_offset; - phy_drd->phys[i].phy_cfg = &drv_data->phy_cfg[i]; - phy_set_drvdata(phy, &phy_drd->phys[i]); - } - - phy_provider = devm_of_phy_provider_register(dev, - exynos5_usbdrd_phy_xlate); - if (IS_ERR(phy_provider)) { - dev_err(phy_drd->dev, "Failed to register phy provider\n"); - return PTR_ERR(phy_provider); - } - - return 0; -} - -static struct platform_driver exynos5_usb3drd_phy = { - .probe = exynos5_usbdrd_phy_probe, - .driver = { - .of_match_table = exynos5_usbdrd_phy_of_match, - .name = "exynos5_usb3drd_phy", - } -}; - -module_platform_driver(exynos5_usb3drd_phy); -MODULE_DESCRIPTION("Samsung EXYNOS5 SoCs USB 3.0 DRD controller PHY driver"); -MODULE_AUTHOR("Vivek Gautam "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:exynos5_usb3drd_phy"); diff --git a/drivers/phy/phy-exynos5250-sata.c b/drivers/phy/phy-exynos5250-sata.c deleted file mode 100644 index 60e13afcd9b8..000000000000 --- a/drivers/phy/phy-exynos5250-sata.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Samsung SATA SerDes(PHY) driver - * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * Authors: Girish K S - * Yuvaraj Kumar C D - * - * 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 -#include -#include -#include - -#define SATAPHY_CONTROL_OFFSET 0x0724 -#define EXYNOS5_SATAPHY_PMU_ENABLE BIT(0) -#define EXYNOS5_SATA_RESET 0x4 -#define RESET_GLOBAL_RST_N BIT(0) -#define RESET_CMN_RST_N BIT(1) -#define RESET_CMN_BLOCK_RST_N BIT(2) -#define RESET_CMN_I2C_RST_N BIT(3) -#define RESET_TX_RX_PIPE_RST_N BIT(4) -#define RESET_TX_RX_BLOCK_RST_N BIT(5) -#define RESET_TX_RX_I2C_RST_N (BIT(6) | BIT(7)) -#define LINK_RESET 0xf0000 -#define EXYNOS5_SATA_MODE0 0x10 -#define SATA_SPD_GEN3 BIT(1) -#define EXYNOS5_SATA_CTRL0 0x14 -#define CTRL0_P0_PHY_CALIBRATED_SEL BIT(9) -#define CTRL0_P0_PHY_CALIBRATED BIT(8) -#define EXYNOS5_SATA_PHSATA_CTRLM 0xe0 -#define PHCTRLM_REF_RATE BIT(1) -#define PHCTRLM_HIGH_SPEED BIT(0) -#define EXYNOS5_SATA_PHSATA_STATM 0xf0 -#define PHSTATM_PLL_LOCKED BIT(0) - -#define PHY_PLL_TIMEOUT (usecs_to_jiffies(1000)) - -struct exynos_sata_phy { - struct phy *phy; - struct clk *phyclk; - void __iomem *regs; - struct regmap *pmureg; - struct i2c_client *client; -}; - -static int wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit, - u32 status) -{ - unsigned long timeout = jiffies + PHY_PLL_TIMEOUT; - - while (time_before(jiffies, timeout)) { - if ((readl(base + reg) & checkbit) == status) - return 0; - } - - return -EFAULT; -} - -static int exynos_sata_phy_power_on(struct phy *phy) -{ - struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy); - - return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET, - EXYNOS5_SATAPHY_PMU_ENABLE, true); - -} - -static int exynos_sata_phy_power_off(struct phy *phy) -{ - struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy); - - return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET, - EXYNOS5_SATAPHY_PMU_ENABLE, false); - -} - -static int exynos_sata_phy_init(struct phy *phy) -{ - u32 val = 0; - int ret = 0; - u8 buf[] = { 0x3a, 0x0b }; - struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy); - - ret = regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET, - EXYNOS5_SATAPHY_PMU_ENABLE, true); - if (ret != 0) - dev_err(&sata_phy->phy->dev, "phy init failed\n"); - - writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); - - val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); - val |= RESET_GLOBAL_RST_N | RESET_CMN_RST_N | RESET_CMN_BLOCK_RST_N - | RESET_CMN_I2C_RST_N | RESET_TX_RX_PIPE_RST_N - | RESET_TX_RX_BLOCK_RST_N | RESET_TX_RX_I2C_RST_N; - writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); - - val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); - val |= LINK_RESET; - writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); - - val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); - val |= RESET_CMN_RST_N; - writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); - - val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); - val &= ~PHCTRLM_REF_RATE; - writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); - - /* High speed enable for Gen3 */ - val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); - val |= PHCTRLM_HIGH_SPEED; - writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); - - val = readl(sata_phy->regs + EXYNOS5_SATA_CTRL0); - val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED; - writel(val, sata_phy->regs + EXYNOS5_SATA_CTRL0); - - val = readl(sata_phy->regs + EXYNOS5_SATA_MODE0); - val |= SATA_SPD_GEN3; - writel(val, sata_phy->regs + EXYNOS5_SATA_MODE0); - - ret = i2c_master_send(sata_phy->client, buf, sizeof(buf)); - if (ret < 0) - return ret; - - /* release cmu reset */ - val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); - val &= ~RESET_CMN_RST_N; - writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); - - val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); - val |= RESET_CMN_RST_N; - writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); - - ret = wait_for_reg_status(sata_phy->regs, - EXYNOS5_SATA_PHSATA_STATM, - PHSTATM_PLL_LOCKED, 1); - if (ret < 0) - dev_err(&sata_phy->phy->dev, - "PHY PLL locking failed\n"); - return ret; -} - -static const struct phy_ops exynos_sata_phy_ops = { - .init = exynos_sata_phy_init, - .power_on = exynos_sata_phy_power_on, - .power_off = exynos_sata_phy_power_off, - .owner = THIS_MODULE, -}; - -static int exynos_sata_phy_probe(struct platform_device *pdev) -{ - struct exynos_sata_phy *sata_phy; - struct device *dev = &pdev->dev; - struct resource *res; - struct phy_provider *phy_provider; - struct device_node *node; - int ret = 0; - - sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL); - if (!sata_phy) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - sata_phy->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(sata_phy->regs)) - return PTR_ERR(sata_phy->regs); - - sata_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node, - "samsung,syscon-phandle"); - if (IS_ERR(sata_phy->pmureg)) { - dev_err(dev, "syscon regmap lookup failed.\n"); - return PTR_ERR(sata_phy->pmureg); - } - - node = of_parse_phandle(dev->of_node, - "samsung,exynos-sataphy-i2c-phandle", 0); - if (!node) - return -EINVAL; - - sata_phy->client = of_find_i2c_device_by_node(node); - if (!sata_phy->client) - return -EPROBE_DEFER; - - dev_set_drvdata(dev, sata_phy); - - sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl"); - if (IS_ERR(sata_phy->phyclk)) { - dev_err(dev, "failed to get clk for PHY\n"); - return PTR_ERR(sata_phy->phyclk); - } - - ret = clk_prepare_enable(sata_phy->phyclk); - if (ret < 0) { - dev_err(dev, "failed to enable source clk\n"); - return ret; - } - - sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops); - if (IS_ERR(sata_phy->phy)) { - clk_disable_unprepare(sata_phy->phyclk); - dev_err(dev, "failed to create PHY\n"); - return PTR_ERR(sata_phy->phy); - } - - phy_set_drvdata(sata_phy->phy, sata_phy); - - phy_provider = devm_of_phy_provider_register(dev, - of_phy_simple_xlate); - if (IS_ERR(phy_provider)) { - clk_disable_unprepare(sata_phy->phyclk); - return PTR_ERR(phy_provider); - } - - return 0; -} - -static const struct of_device_id exynos_sata_phy_of_match[] = { - { .compatible = "samsung,exynos5250-sata-phy" }, - { }, -}; -MODULE_DEVICE_TABLE(of, exynos_sata_phy_of_match); - -static struct platform_driver exynos_sata_phy_driver = { - .probe = exynos_sata_phy_probe, - .driver = { - .of_match_table = exynos_sata_phy_of_match, - .name = "samsung,sata-phy", - } -}; -module_platform_driver(exynos_sata_phy_driver); - -MODULE_DESCRIPTION("Samsung SerDes PHY driver"); -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Girish K S "); -MODULE_AUTHOR("Yuvaraj C D "); diff --git a/drivers/phy/phy-exynos5250-usb2.c b/drivers/phy/phy-exynos5250-usb2.c deleted file mode 100644 index aad806272305..000000000000 --- a/drivers/phy/phy-exynos5250-usb2.c +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 5250 support - * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * Author: Kamil Debski - * - * 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 "phy-samsung-usb2.h" - -/* Exynos USB PHY registers */ -#define EXYNOS_5250_REFCLKSEL_CRYSTAL 0x0 -#define EXYNOS_5250_REFCLKSEL_XO 0x1 -#define EXYNOS_5250_REFCLKSEL_CLKCORE 0x2 - -#define EXYNOS_5250_FSEL_9MHZ6 0x0 -#define EXYNOS_5250_FSEL_10MHZ 0x1 -#define EXYNOS_5250_FSEL_12MHZ 0x2 -#define EXYNOS_5250_FSEL_19MHZ2 0x3 -#define EXYNOS_5250_FSEL_20MHZ 0x4 -#define EXYNOS_5250_FSEL_24MHZ 0x5 -#define EXYNOS_5250_FSEL_50MHZ 0x7 - -/* Normal host */ -#define EXYNOS_5250_HOSTPHYCTRL0 0x0 - -#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL BIT(31) -#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT 19 -#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_MASK \ - (0x3 << EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT) -#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT 16 -#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK \ - (0x7 << EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT) -#define EXYNOS_5250_HOSTPHYCTRL0_TESTBURNIN BIT(11) -#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE BIT(10) -#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N BIT(9) -#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_MASK (0x3 << 7) -#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_DUAL (0x0 << 7) -#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ID0 (0x1 << 7) -#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ANALOGTEST (0x2 << 7) -#define EXYNOS_5250_HOSTPHYCTRL0_SIDDQ BIT(6) -#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP BIT(5) -#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND BIT(4) -#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE BIT(3) -#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST BIT(2) -#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST BIT(1) -#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST BIT(0) - -/* HSIC0 & HSIC1 */ -#define EXYNOS_5250_HSICPHYCTRL1 0x10 -#define EXYNOS_5250_HSICPHYCTRL2 0x20 - -#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_MASK (0x3 << 23) -#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT (0x2 << 23) -#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_MASK (0x7f << 16) -#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 (0x24 << 16) -#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_15 (0x1c << 16) -#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_16 (0x1a << 16) -#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_19_2 (0x15 << 16) -#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_20 (0x14 << 16) -#define EXYNOS_5250_HSICPHYCTRLX_SIDDQ BIT(6) -#define EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP BIT(5) -#define EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND BIT(4) -#define EXYNOS_5250_HSICPHYCTRLX_WORDINTERFACE BIT(3) -#define EXYNOS_5250_HSICPHYCTRLX_UTMISWRST BIT(2) -#define EXYNOS_5250_HSICPHYCTRLX_PHYSWRST BIT(0) - -/* EHCI control */ -#define EXYNOS_5250_HOSTEHCICTRL 0x30 -#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN BIT(29) -#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 BIT(28) -#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 BIT(27) -#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16 BIT(26) -#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN BIT(25) -#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT 19 -#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK \ - (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT) -#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT 13 -#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_MASK \ - (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT) -#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL2_SHIFT 7 -#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK \ - (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT) -#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT 1 -#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_MASK \ - (0x1 << EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT) -#define EXYNOS_5250_HOSTEHCICTRL_SIMULATIONMODE BIT(0) - -/* OHCI control */ -#define EXYNOS_5250_HOSTOHCICTRL 0x34 -#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT 1 -#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_MASK \ - (0x3ff << EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT) -#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVALEN BIT(0) - -/* USBOTG */ -#define EXYNOS_5250_USBOTGSYS 0x38 -#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET BIT(14) -#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG BIT(13) -#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST BIT(12) -#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT 9 -#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK \ - (0x3 << EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT) -#define EXYNOS_5250_USBOTGSYS_ID_PULLUP BIT(8) -#define EXYNOS_5250_USBOTGSYS_COMMON_ON BIT(7) -#define EXYNOS_5250_USBOTGSYS_FSEL_SHIFT 4 -#define EXYNOS_5250_USBOTGSYS_FSEL_MASK \ - (0x3 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT) -#define EXYNOS_5250_USBOTGSYS_FORCE_SLEEP BIT(3) -#define EXYNOS_5250_USBOTGSYS_OTGDISABLE BIT(2) -#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG BIT(1) -#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND BIT(0) - -/* Isolation, configured in the power management unit */ -#define EXYNOS_5250_USB_ISOL_OTG_OFFSET 0x704 -#define EXYNOS_5250_USB_ISOL_OTG BIT(0) -#define EXYNOS_5250_USB_ISOL_HOST_OFFSET 0x708 -#define EXYNOS_5250_USB_ISOL_HOST BIT(0) - -/* Mode swtich register */ -#define EXYNOS_5250_MODE_SWITCH_OFFSET 0x230 -#define EXYNOS_5250_MODE_SWITCH_MASK 1 -#define EXYNOS_5250_MODE_SWITCH_DEVICE 0 -#define EXYNOS_5250_MODE_SWITCH_HOST 1 - -enum exynos4x12_phy_id { - EXYNOS5250_DEVICE, - EXYNOS5250_HOST, - EXYNOS5250_HSIC0, - EXYNOS5250_HSIC1, - EXYNOS5250_NUM_PHYS, -}; - -/* - * exynos5250_rate_to_clk() converts the supplied clock rate to the value that - * can be written to the phy register. - */ -static int exynos5250_rate_to_clk(unsigned long rate, u32 *reg) -{ - /* EXYNOS_5250_FSEL_MASK */ - - switch (rate) { - case 9600 * KHZ: - *reg = EXYNOS_5250_FSEL_9MHZ6; - break; - case 10 * MHZ: - *reg = EXYNOS_5250_FSEL_10MHZ; - break; - case 12 * MHZ: - *reg = EXYNOS_5250_FSEL_12MHZ; - break; - case 19200 * KHZ: - *reg = EXYNOS_5250_FSEL_19MHZ2; - break; - case 20 * MHZ: - *reg = EXYNOS_5250_FSEL_20MHZ; - break; - case 24 * MHZ: - *reg = EXYNOS_5250_FSEL_24MHZ; - break; - case 50 * MHZ: - *reg = EXYNOS_5250_FSEL_50MHZ; - break; - default: - return -EINVAL; - } - - return 0; -} - -static void exynos5250_isol(struct samsung_usb2_phy_instance *inst, bool on) -{ - struct samsung_usb2_phy_driver *drv = inst->drv; - u32 offset; - u32 mask; - - switch (inst->cfg->id) { - case EXYNOS5250_DEVICE: - offset = EXYNOS_5250_USB_ISOL_OTG_OFFSET; - mask = EXYNOS_5250_USB_ISOL_OTG; - break; - case EXYNOS5250_HOST: - offset = EXYNOS_5250_USB_ISOL_HOST_OFFSET; - mask = EXYNOS_5250_USB_ISOL_HOST; - break; - default: - return; - } - - regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); -} - -static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst) -{ - struct samsung_usb2_phy_driver *drv = inst->drv; - u32 ctrl0; - u32 otg; - u32 ehci; - u32 ohci; - u32 hsic; - - switch (inst->cfg->id) { - case EXYNOS5250_DEVICE: - regmap_update_bits(drv->reg_sys, - EXYNOS_5250_MODE_SWITCH_OFFSET, - EXYNOS_5250_MODE_SWITCH_MASK, - EXYNOS_5250_MODE_SWITCH_DEVICE); - - /* OTG configuration */ - otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS); - /* The clock */ - otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK; - otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT; - /* Reset */ - otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND | - EXYNOS_5250_USBOTGSYS_FORCE_SLEEP | - EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG); - otg |= EXYNOS_5250_USBOTGSYS_PHY_SW_RST | - EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET | - EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | - EXYNOS_5250_USBOTGSYS_OTGDISABLE; - /* Ref clock */ - otg &= ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK; - otg |= EXYNOS_5250_REFCLKSEL_CLKCORE << - EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT; - writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); - udelay(100); - otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST | - EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | - EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET | - EXYNOS_5250_USBOTGSYS_OTGDISABLE); - writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); - - - break; - case EXYNOS5250_HOST: - case EXYNOS5250_HSIC0: - case EXYNOS5250_HSIC1: - /* Host registers configuration */ - ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); - /* The clock */ - ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK; - ctrl0 |= drv->ref_reg_val << - EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT; - - /* Reset */ - ctrl0 &= ~(EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST | - EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL | - EXYNOS_5250_HOSTPHYCTRL0_SIDDQ | - EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND | - EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP); - ctrl0 |= EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST | - EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST | - EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N; - writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); - udelay(10); - ctrl0 &= ~(EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST | - EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST); - writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); - - /* OTG configuration */ - otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS); - /* The clock */ - otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK; - otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT; - /* Reset */ - otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND | - EXYNOS_5250_USBOTGSYS_FORCE_SLEEP | - EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG); - otg |= EXYNOS_5250_USBOTGSYS_PHY_SW_RST | - EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET | - EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | - EXYNOS_5250_USBOTGSYS_OTGDISABLE; - /* Ref clock */ - otg &= ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK; - otg |= EXYNOS_5250_REFCLKSEL_CLKCORE << - EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT; - writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); - udelay(10); - otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST | - EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | - EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET); - - /* HSIC phy configuration */ - hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 | - EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT | - EXYNOS_5250_HSICPHYCTRLX_PHYSWRST); - writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1); - writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2); - udelay(10); - hsic &= ~EXYNOS_5250_HSICPHYCTRLX_PHYSWRST; - writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1); - writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2); - /* The following delay is necessary for the reset sequence to be - * completed */ - udelay(80); - - /* Enable EHCI DMA burst */ - ehci = readl(drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL); - ehci |= EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN | - EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 | - EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 | - EXYNOS_5250_HOSTEHCICTRL_ENAINCR16; - writel(ehci, drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL); - - /* OHCI settings */ - ohci = readl(drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL); - /* Following code is based on the old driver */ - ohci |= 0x1 << 3; - writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL); - - break; - } - exynos5250_isol(inst, 0); - - return 0; -} - -static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst) -{ - struct samsung_usb2_phy_driver *drv = inst->drv; - u32 ctrl0; - u32 otg; - u32 hsic; - - exynos5250_isol(inst, 1); - - switch (inst->cfg->id) { - case EXYNOS5250_DEVICE: - otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS); - otg |= (EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND | - EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG | - EXYNOS_5250_USBOTGSYS_FORCE_SLEEP); - writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); - break; - case EXYNOS5250_HOST: - ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); - ctrl0 |= (EXYNOS_5250_HOSTPHYCTRL0_SIDDQ | - EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND | - EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP | - EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST | - EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL); - writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); - break; - case EXYNOS5250_HSIC0: - case EXYNOS5250_HSIC1: - hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 | - EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT | - EXYNOS_5250_HSICPHYCTRLX_SIDDQ | - EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP | - EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND - ); - writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1); - writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2); - break; - } - - return 0; -} - - -static const struct samsung_usb2_common_phy exynos5250_phys[] = { - { - .label = "device", - .id = EXYNOS5250_DEVICE, - .power_on = exynos5250_power_on, - .power_off = exynos5250_power_off, - }, - { - .label = "host", - .id = EXYNOS5250_HOST, - .power_on = exynos5250_power_on, - .power_off = exynos5250_power_off, - }, - { - .label = "hsic0", - .id = EXYNOS5250_HSIC0, - .power_on = exynos5250_power_on, - .power_off = exynos5250_power_off, - }, - { - .label = "hsic1", - .id = EXYNOS5250_HSIC1, - .power_on = exynos5250_power_on, - .power_off = exynos5250_power_off, - }, -}; - -const struct samsung_usb2_phy_config exynos5250_usb2_phy_config = { - .has_mode_switch = 1, - .num_phys = EXYNOS5250_NUM_PHYS, - .phys = exynos5250_phys, - .rate_to_clk = exynos5250_rate_to_clk, -}; diff --git a/drivers/phy/phy-hi6220-usb.c b/drivers/phy/phy-hi6220-usb.c deleted file mode 100644 index 398c1021deec..000000000000 --- a/drivers/phy/phy-hi6220-usb.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2015 Linaro Ltd. - * Copyright (c) 2015 Hisilicon Limited. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include -#include -#include -#include -#include - -#define SC_PERIPH_CTRL4 0x00c - -#define CTRL4_PICO_SIDDQ BIT(6) -#define CTRL4_PICO_OGDISABLE BIT(8) -#define CTRL4_PICO_VBUSVLDEXT BIT(10) -#define CTRL4_PICO_VBUSVLDEXTSEL BIT(11) -#define CTRL4_OTG_PHY_SEL BIT(21) - -#define SC_PERIPH_CTRL5 0x010 - -#define CTRL5_USBOTG_RES_SEL BIT(3) -#define CTRL5_PICOPHY_ACAENB BIT(4) -#define CTRL5_PICOPHY_BC_MODE BIT(5) -#define CTRL5_PICOPHY_CHRGSEL BIT(6) -#define CTRL5_PICOPHY_VDATSRCEND BIT(7) -#define CTRL5_PICOPHY_VDATDETENB BIT(8) -#define CTRL5_PICOPHY_DCDENB BIT(9) -#define CTRL5_PICOPHY_IDDIG BIT(10) - -#define SC_PERIPH_CTRL8 0x018 -#define SC_PERIPH_RSTEN0 0x300 -#define SC_PERIPH_RSTDIS0 0x304 - -#define RST0_USBOTG_BUS BIT(4) -#define RST0_POR_PICOPHY BIT(5) -#define RST0_USBOTG BIT(6) -#define RST0_USBOTG_32K BIT(7) - -#define EYE_PATTERN_PARA 0x7053348c - -struct hi6220_priv { - struct regmap *reg; - struct device *dev; -}; - -static void hi6220_phy_init(struct hi6220_priv *priv) -{ - struct regmap *reg = priv->reg; - u32 val, mask; - - val = RST0_USBOTG_BUS | RST0_POR_PICOPHY | - RST0_USBOTG | RST0_USBOTG_32K; - mask = val; - regmap_update_bits(reg, SC_PERIPH_RSTEN0, mask, val); - regmap_update_bits(reg, SC_PERIPH_RSTDIS0, mask, val); -} - -static int hi6220_phy_setup(struct hi6220_priv *priv, bool on) -{ - struct regmap *reg = priv->reg; - u32 val, mask; - int ret; - - if (on) { - val = CTRL5_USBOTG_RES_SEL | CTRL5_PICOPHY_ACAENB; - mask = val | CTRL5_PICOPHY_BC_MODE; - ret = regmap_update_bits(reg, SC_PERIPH_CTRL5, mask, val); - if (ret) - goto out; - - val = CTRL4_PICO_VBUSVLDEXT | CTRL4_PICO_VBUSVLDEXTSEL | - CTRL4_OTG_PHY_SEL; - mask = val | CTRL4_PICO_SIDDQ | CTRL4_PICO_OGDISABLE; - ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val); - if (ret) - goto out; - - ret = regmap_write(reg, SC_PERIPH_CTRL8, EYE_PATTERN_PARA); - if (ret) - goto out; - } else { - val = CTRL4_PICO_SIDDQ; - mask = val; - ret = regmap_update_bits(reg, SC_PERIPH_CTRL4, mask, val); - if (ret) - goto out; - } - - return 0; -out: - dev_err(priv->dev, "failed to setup phy ret: %d\n", ret); - return ret; -} - -static int hi6220_phy_start(struct phy *phy) -{ - struct hi6220_priv *priv = phy_get_drvdata(phy); - - return hi6220_phy_setup(priv, true); -} - -static int hi6220_phy_exit(struct phy *phy) -{ - struct hi6220_priv *priv = phy_get_drvdata(phy); - - return hi6220_phy_setup(priv, false); -} - -static const struct phy_ops hi6220_phy_ops = { - .init = hi6220_phy_start, - .exit = hi6220_phy_exit, - .owner = THIS_MODULE, -}; - -static int hi6220_phy_probe(struct platform_device *pdev) -{ - struct phy_provider *phy_provider; - struct device *dev = &pdev->dev; - struct phy *phy; - struct hi6220_priv *priv; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->dev = dev; - priv->reg = syscon_regmap_lookup_by_phandle(dev->of_node, - "hisilicon,peripheral-syscon"); - if (IS_ERR(priv->reg)) { - dev_err(dev, "no hisilicon,peripheral-syscon\n"); - return PTR_ERR(priv->reg); - } - - hi6220_phy_init(priv); - - phy = devm_phy_create(dev, NULL, &hi6220_phy_ops); - if (IS_ERR(phy)) - return PTR_ERR(phy); - - phy_set_drvdata(phy, priv); - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id hi6220_phy_of_match[] = { - {.compatible = "hisilicon,hi6220-usb-phy",}, - { }, -}; -MODULE_DEVICE_TABLE(of, hi6220_phy_of_match); - -static struct platform_driver hi6220_phy_driver = { - .probe = hi6220_phy_probe, - .driver = { - .name = "hi6220-usb-phy", - .of_match_table = hi6220_phy_of_match, - } -}; -module_platform_driver(hi6220_phy_driver); - -MODULE_DESCRIPTION("HISILICON HI6220 USB PHY driver"); -MODULE_ALIAS("platform:hi6220-usb-phy"); -MODULE_LICENSE("GPL"); diff --git a/drivers/phy/phy-hix5hd2-sata.c b/drivers/phy/phy-hix5hd2-sata.c deleted file mode 100644 index e5ab3aa78b9d..000000000000 --- a/drivers/phy/phy-hix5hd2-sata.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (c) 2014 Linaro Ltd. - * Copyright (c) 2014 Hisilicon Limited. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include - -#define SATA_PHY0_CTLL 0xa0 -#define MPLL_MULTIPLIER_SHIFT 1 -#define MPLL_MULTIPLIER_MASK 0xfe -#define MPLL_MULTIPLIER_50M 0x3c -#define MPLL_MULTIPLIER_100M 0x1e -#define PHY_RESET BIT(0) -#define REF_SSP_EN BIT(9) -#define SSC_EN BIT(10) -#define REF_USE_PAD BIT(23) - -#define SATA_PORT_PHYCTL 0x174 -#define SPEED_MODE_MASK 0x6f0000 -#define HALF_RATE_SHIFT 16 -#define PHY_CONFIG_SHIFT 18 -#define GEN2_EN_SHIFT 21 -#define SPEED_CTRL BIT(20) - -#define SATA_PORT_PHYCTL1 0x148 -#define AMPLITUDE_MASK 0x3ffffe -#define AMPLITUDE_GEN3 0x68 -#define AMPLITUDE_GEN3_SHIFT 15 -#define AMPLITUDE_GEN2 0x56 -#define AMPLITUDE_GEN2_SHIFT 8 -#define AMPLITUDE_GEN1 0x56 -#define AMPLITUDE_GEN1_SHIFT 1 - -#define SATA_PORT_PHYCTL2 0x14c -#define PREEMPH_MASK 0x3ffff -#define PREEMPH_GEN3 0x20 -#define PREEMPH_GEN3_SHIFT 12 -#define PREEMPH_GEN2 0x15 -#define PREEMPH_GEN2_SHIFT 6 -#define PREEMPH_GEN1 0x5 -#define PREEMPH_GEN1_SHIFT 0 - -struct hix5hd2_priv { - void __iomem *base; - struct regmap *peri_ctrl; -}; - -enum phy_speed_mode { - SPEED_MODE_GEN1 = 0, - SPEED_MODE_GEN2 = 1, - SPEED_MODE_GEN3 = 2, -}; - -static int hix5hd2_sata_phy_init(struct phy *phy) -{ - struct hix5hd2_priv *priv = phy_get_drvdata(phy); - u32 val, data[2]; - int ret; - - if (priv->peri_ctrl) { - ret = of_property_read_u32_array(phy->dev.of_node, - "hisilicon,power-reg", - &data[0], 2); - if (ret) { - dev_err(&phy->dev, "Fail read hisilicon,power-reg\n"); - return ret; - } - - regmap_update_bits(priv->peri_ctrl, data[0], - BIT(data[1]), BIT(data[1])); - } - - /* reset phy */ - val = readl_relaxed(priv->base + SATA_PHY0_CTLL); - val &= ~(MPLL_MULTIPLIER_MASK | REF_USE_PAD); - val |= MPLL_MULTIPLIER_50M << MPLL_MULTIPLIER_SHIFT | - REF_SSP_EN | PHY_RESET; - writel_relaxed(val, priv->base + SATA_PHY0_CTLL); - msleep(20); - val &= ~PHY_RESET; - writel_relaxed(val, priv->base + SATA_PHY0_CTLL); - - val = readl_relaxed(priv->base + SATA_PORT_PHYCTL1); - val &= ~AMPLITUDE_MASK; - val |= AMPLITUDE_GEN3 << AMPLITUDE_GEN3_SHIFT | - AMPLITUDE_GEN2 << AMPLITUDE_GEN2_SHIFT | - AMPLITUDE_GEN1 << AMPLITUDE_GEN1_SHIFT; - writel_relaxed(val, priv->base + SATA_PORT_PHYCTL1); - - val = readl_relaxed(priv->base + SATA_PORT_PHYCTL2); - val &= ~PREEMPH_MASK; - val |= PREEMPH_GEN3 << PREEMPH_GEN3_SHIFT | - PREEMPH_GEN2 << PREEMPH_GEN2_SHIFT | - PREEMPH_GEN1 << PREEMPH_GEN1_SHIFT; - writel_relaxed(val, priv->base + SATA_PORT_PHYCTL2); - - /* ensure PHYCTRL setting takes effect */ - val = readl_relaxed(priv->base + SATA_PORT_PHYCTL); - val &= ~SPEED_MODE_MASK; - val |= SPEED_MODE_GEN1 << HALF_RATE_SHIFT | - SPEED_MODE_GEN1 << PHY_CONFIG_SHIFT | - SPEED_MODE_GEN1 << GEN2_EN_SHIFT | SPEED_CTRL; - writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); - - msleep(20); - val &= ~SPEED_MODE_MASK; - val |= SPEED_MODE_GEN3 << HALF_RATE_SHIFT | - SPEED_MODE_GEN3 << PHY_CONFIG_SHIFT | - SPEED_MODE_GEN3 << GEN2_EN_SHIFT | SPEED_CTRL; - writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); - - val &= ~(SPEED_MODE_MASK | SPEED_CTRL); - val |= SPEED_MODE_GEN2 << HALF_RATE_SHIFT | - SPEED_MODE_GEN2 << PHY_CONFIG_SHIFT | - SPEED_MODE_GEN2 << GEN2_EN_SHIFT; - writel_relaxed(val, priv->base + SATA_PORT_PHYCTL); - - return 0; -} - -static const struct phy_ops hix5hd2_sata_phy_ops = { - .init = hix5hd2_sata_phy_init, - .owner = THIS_MODULE, -}; - -static int hix5hd2_sata_phy_probe(struct platform_device *pdev) -{ - struct phy_provider *phy_provider; - struct device *dev = &pdev->dev; - struct resource *res; - struct phy *phy; - struct hix5hd2_priv *priv; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - - priv->base = devm_ioremap(dev, res->start, resource_size(res)); - if (!priv->base) - return -ENOMEM; - - priv->peri_ctrl = syscon_regmap_lookup_by_phandle(dev->of_node, - "hisilicon,peripheral-syscon"); - if (IS_ERR(priv->peri_ctrl)) - priv->peri_ctrl = NULL; - - phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops); - if (IS_ERR(phy)) { - dev_err(dev, "failed to create PHY\n"); - return PTR_ERR(phy); - } - - phy_set_drvdata(phy, priv); - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id hix5hd2_sata_phy_of_match[] = { - {.compatible = "hisilicon,hix5hd2-sata-phy",}, - { }, -}; -MODULE_DEVICE_TABLE(of, hix5hd2_sata_phy_of_match); - -static struct platform_driver hix5hd2_sata_phy_driver = { - .probe = hix5hd2_sata_phy_probe, - .driver = { - .name = "hix5hd2-sata-phy", - .of_match_table = hix5hd2_sata_phy_of_match, - } -}; -module_platform_driver(hix5hd2_sata_phy_driver); - -MODULE_AUTHOR("Jiancheng Xue "); -MODULE_DESCRIPTION("HISILICON HIX5HD2 SATA PHY driver"); -MODULE_ALIAS("platform:hix5hd2-sata-phy"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-meson8b-usb2.c b/drivers/phy/phy-meson8b-usb2.c deleted file mode 100644 index 30f56a6a411f..000000000000 --- a/drivers/phy/phy-meson8b-usb2.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Meson8b and GXBB USB2 PHY driver - * - * Copyright (C) 2016 Martin Blumenstingl - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define REG_CONFIG 0x00 - #define REG_CONFIG_CLK_EN BIT(0) - #define REG_CONFIG_CLK_SEL_MASK GENMASK(3, 1) - #define REG_CONFIG_CLK_DIV_MASK GENMASK(10, 4) - #define REG_CONFIG_CLK_32k_ALTSEL BIT(15) - #define REG_CONFIG_TEST_TRIG BIT(31) - -#define REG_CTRL 0x04 - #define REG_CTRL_SOFT_PRST BIT(0) - #define REG_CTRL_SOFT_HRESET BIT(1) - #define REG_CTRL_SS_SCALEDOWN_MODE_MASK GENMASK(3, 2) - #define REG_CTRL_CLK_DET_RST BIT(4) - #define REG_CTRL_INTR_SEL BIT(5) - #define REG_CTRL_CLK_DETECTED BIT(8) - #define REG_CTRL_SOF_SENT_RCVD_TGL BIT(9) - #define REG_CTRL_SOF_TOGGLE_OUT BIT(10) - #define REG_CTRL_POWER_ON_RESET BIT(15) - #define REG_CTRL_SLEEPM BIT(16) - #define REG_CTRL_TX_BITSTUFF_ENN_H BIT(17) - #define REG_CTRL_TX_BITSTUFF_ENN BIT(18) - #define REG_CTRL_COMMON_ON BIT(19) - #define REG_CTRL_REF_CLK_SEL_MASK GENMASK(21, 20) - #define REG_CTRL_REF_CLK_SEL_SHIFT 20 - #define REG_CTRL_FSEL_MASK GENMASK(24, 22) - #define REG_CTRL_FSEL_SHIFT 22 - #define REG_CTRL_PORT_RESET BIT(25) - #define REG_CTRL_THREAD_ID_MASK GENMASK(31, 26) - -#define REG_ENDP_INTR 0x08 - -/* bits [31:26], [24:21] and [15:3] seem to be read-only */ -#define REG_ADP_BC 0x0c - #define REG_ADP_BC_VBUS_VLD_EXT_SEL BIT(0) - #define REG_ADP_BC_VBUS_VLD_EXT BIT(1) - #define REG_ADP_BC_OTG_DISABLE BIT(2) - #define REG_ADP_BC_ID_PULLUP BIT(3) - #define REG_ADP_BC_DRV_VBUS BIT(4) - #define REG_ADP_BC_ADP_PRB_EN BIT(5) - #define REG_ADP_BC_ADP_DISCHARGE BIT(6) - #define REG_ADP_BC_ADP_CHARGE BIT(7) - #define REG_ADP_BC_SESS_END BIT(8) - #define REG_ADP_BC_DEVICE_SESS_VLD BIT(9) - #define REG_ADP_BC_B_VALID BIT(10) - #define REG_ADP_BC_A_VALID BIT(11) - #define REG_ADP_BC_ID_DIG BIT(12) - #define REG_ADP_BC_VBUS_VALID BIT(13) - #define REG_ADP_BC_ADP_PROBE BIT(14) - #define REG_ADP_BC_ADP_SENSE BIT(15) - #define REG_ADP_BC_ACA_ENABLE BIT(16) - #define REG_ADP_BC_DCD_ENABLE BIT(17) - #define REG_ADP_BC_VDAT_DET_EN_B BIT(18) - #define REG_ADP_BC_VDAT_SRC_EN_B BIT(19) - #define REG_ADP_BC_CHARGE_SEL BIT(20) - #define REG_ADP_BC_CHARGE_DETECT BIT(21) - #define REG_ADP_BC_ACA_PIN_RANGE_C BIT(22) - #define REG_ADP_BC_ACA_PIN_RANGE_B BIT(23) - #define REG_ADP_BC_ACA_PIN_RANGE_A BIT(24) - #define REG_ADP_BC_ACA_PIN_GND BIT(25) - #define REG_ADP_BC_ACA_PIN_FLOAT BIT(26) - -#define REG_DBG_UART 0x10 - -#define REG_TEST 0x14 - #define REG_TEST_DATA_IN_MASK GENMASK(3, 0) - #define REG_TEST_EN_MASK GENMASK(7, 4) - #define REG_TEST_ADDR_MASK GENMASK(11, 8) - #define REG_TEST_DATA_OUT_SEL BIT(12) - #define REG_TEST_CLK BIT(13) - #define REG_TEST_VA_TEST_EN_B_MASK GENMASK(15, 14) - #define REG_TEST_DATA_OUT_MASK GENMASK(19, 16) - #define REG_TEST_DISABLE_ID_PULLUP BIT(20) - -#define REG_TUNE 0x18 - #define REG_TUNE_TX_RES_TUNE_MASK GENMASK(1, 0) - #define REG_TUNE_TX_HSXV_TUNE_MASK GENMASK(3, 2) - #define REG_TUNE_TX_VREF_TUNE_MASK GENMASK(7, 4) - #define REG_TUNE_TX_RISE_TUNE_MASK GENMASK(9, 8) - #define REG_TUNE_TX_PREEMP_PULSE_TUNE BIT(10) - #define REG_TUNE_TX_PREEMP_AMP_TUNE_MASK GENMASK(12, 11) - #define REG_TUNE_TX_FSLS_TUNE_MASK GENMASK(16, 13) - #define REG_TUNE_SQRX_TUNE_MASK GENMASK(19, 17) - #define REG_TUNE_OTG_TUNE GENMASK(22, 20) - #define REG_TUNE_COMP_DIS_TUNE GENMASK(25, 23) - #define REG_TUNE_HOST_DM_PULLDOWN BIT(26) - #define REG_TUNE_HOST_DP_PULLDOWN BIT(27) - -#define RESET_COMPLETE_TIME 500 -#define ACA_ENABLE_COMPLETE_TIME 50 - -struct phy_meson8b_usb2_priv { - void __iomem *regs; - enum usb_dr_mode dr_mode; - struct clk *clk_usb_general; - struct clk *clk_usb; - struct reset_control *reset; -}; - -static u32 phy_meson8b_usb2_read(struct phy_meson8b_usb2_priv *phy_priv, - u32 reg) -{ - return readl(phy_priv->regs + reg); -} - -static void phy_meson8b_usb2_mask_bits(struct phy_meson8b_usb2_priv *phy_priv, - u32 reg, u32 mask, u32 value) -{ - u32 data; - - data = phy_meson8b_usb2_read(phy_priv, reg); - data &= ~mask; - data |= (value & mask); - - writel(data, phy_priv->regs + reg); -} - -static int phy_meson8b_usb2_power_on(struct phy *phy) -{ - struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); - int ret; - - if (!IS_ERR_OR_NULL(priv->reset)) { - ret = reset_control_reset(priv->reset); - if (ret) { - dev_err(&phy->dev, "Failed to trigger USB reset\n"); - return ret; - } - } - - ret = clk_prepare_enable(priv->clk_usb_general); - if (ret) { - dev_err(&phy->dev, "Failed to enable USB general clock\n"); - return ret; - } - - ret = clk_prepare_enable(priv->clk_usb); - if (ret) { - dev_err(&phy->dev, "Failed to enable USB DDR clock\n"); - clk_disable_unprepare(priv->clk_usb_general); - return ret; - } - - phy_meson8b_usb2_mask_bits(priv, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL, - REG_CONFIG_CLK_32k_ALTSEL); - - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK, - 0x2 << REG_CTRL_REF_CLK_SEL_SHIFT); - - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_FSEL_MASK, - 0x5 << REG_CTRL_FSEL_SHIFT); - - /* reset the PHY */ - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, - REG_CTRL_POWER_ON_RESET); - udelay(RESET_COMPLETE_TIME); - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0); - udelay(RESET_COMPLETE_TIME); - - phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT, - REG_CTRL_SOF_TOGGLE_OUT); - - if (priv->dr_mode == USB_DR_MODE_HOST) { - phy_meson8b_usb2_mask_bits(priv, REG_ADP_BC, - REG_ADP_BC_ACA_ENABLE, - REG_ADP_BC_ACA_ENABLE); - - udelay(ACA_ENABLE_COMPLETE_TIME); - - if (phy_meson8b_usb2_read(priv, REG_ADP_BC) & - REG_ADP_BC_ACA_PIN_FLOAT) { - dev_warn(&phy->dev, "USB ID detect failed!\n"); - clk_disable_unprepare(priv->clk_usb); - clk_disable_unprepare(priv->clk_usb_general); - return -EINVAL; - } - } - - return 0; -} - -static int phy_meson8b_usb2_power_off(struct phy *phy) -{ - struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy); - - clk_disable_unprepare(priv->clk_usb); - clk_disable_unprepare(priv->clk_usb_general); - - return 0; -} - -static const struct phy_ops phy_meson8b_usb2_ops = { - .power_on = phy_meson8b_usb2_power_on, - .power_off = phy_meson8b_usb2_power_off, - .owner = THIS_MODULE, -}; - -static int phy_meson8b_usb2_probe(struct platform_device *pdev) -{ - struct phy_meson8b_usb2_priv *priv; - struct resource *res; - struct phy *phy; - struct phy_provider *phy_provider; - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(priv->regs)) - return PTR_ERR(priv->regs); - - priv->clk_usb_general = devm_clk_get(&pdev->dev, "usb_general"); - if (IS_ERR(priv->clk_usb_general)) - return PTR_ERR(priv->clk_usb_general); - - priv->clk_usb = devm_clk_get(&pdev->dev, "usb"); - if (IS_ERR(priv->clk_usb)) - return PTR_ERR(priv->clk_usb); - - priv->reset = devm_reset_control_get_optional_shared(&pdev->dev, NULL); - if (PTR_ERR(priv->reset) == -EPROBE_DEFER) - return PTR_ERR(priv->reset); - - priv->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1); - if (priv->dr_mode == USB_DR_MODE_UNKNOWN) { - dev_err(&pdev->dev, - "missing dual role configuration of the controller\n"); - return -EINVAL; - } - - phy = devm_phy_create(&pdev->dev, NULL, &phy_meson8b_usb2_ops); - if (IS_ERR(phy)) { - dev_err(&pdev->dev, "failed to create PHY\n"); - return PTR_ERR(phy); - } - - phy_set_drvdata(phy, priv); - - phy_provider = - devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id phy_meson8b_usb2_of_match[] = { - { .compatible = "amlogic,meson8b-usb2-phy", }, - { .compatible = "amlogic,meson-gxbb-usb2-phy", }, - { }, -}; -MODULE_DEVICE_TABLE(of, phy_meson8b_usb2_of_match); - -static struct platform_driver phy_meson8b_usb2_driver = { - .probe = phy_meson8b_usb2_probe, - .driver = { - .name = "phy-meson-usb2", - .of_match_table = phy_meson8b_usb2_of_match, - }, -}; -module_platform_driver(phy_meson8b_usb2_driver); - -MODULE_AUTHOR("Martin Blumenstingl "); -MODULE_DESCRIPTION("Meson8b and GXBB USB2 PHY driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c deleted file mode 100644 index 213e2e15339c..000000000000 --- a/drivers/phy/phy-miphy28lp.c +++ /dev/null @@ -1,1286 +0,0 @@ -/* - * Copyright (C) 2014 STMicroelectronics - * - * STMicroelectronics PHY driver MiPHY28lp (for SoC STiH407). - * - * Author: Alexandre Torgue - * - * 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 -#include -#include -#include - -#include - -/* MiPHY registers */ -#define MIPHY_CONF_RESET 0x00 -#define RST_APPLI_SW BIT(0) -#define RST_CONF_SW BIT(1) -#define RST_MACRO_SW BIT(2) - -#define MIPHY_RESET 0x01 -#define RST_PLL_SW BIT(0) -#define RST_COMP_SW BIT(2) - -#define MIPHY_STATUS_1 0x02 -#define PHY_RDY BIT(0) -#define HFC_RDY BIT(1) -#define HFC_PLL BIT(2) - -#define MIPHY_CONTROL 0x04 -#define TERM_EN_SW BIT(2) -#define DIS_LINK_RST BIT(3) -#define AUTO_RST_RX BIT(4) -#define PX_RX_POL BIT(5) - -#define MIPHY_BOUNDARY_SEL 0x0a -#define TX_SEL BIT(6) -#define SSC_SEL BIT(4) -#define GENSEL_SEL BIT(0) - -#define MIPHY_BOUNDARY_1 0x0b -#define MIPHY_BOUNDARY_2 0x0c -#define SSC_EN_SW BIT(2) - -#define MIPHY_PLL_CLKREF_FREQ 0x0d -#define MIPHY_SPEED 0x0e -#define TX_SPDSEL_80DEC 0 -#define TX_SPDSEL_40DEC 1 -#define TX_SPDSEL_20DEC 2 -#define RX_SPDSEL_80DEC 0 -#define RX_SPDSEL_40DEC (1 << 2) -#define RX_SPDSEL_20DEC (2 << 2) - -#define MIPHY_CONF 0x0f -#define MIPHY_CTRL_TEST_SEL 0x20 -#define MIPHY_CTRL_TEST_1 0x21 -#define MIPHY_CTRL_TEST_2 0x22 -#define MIPHY_CTRL_TEST_3 0x23 -#define MIPHY_CTRL_TEST_4 0x24 -#define MIPHY_FEEDBACK_TEST 0x25 -#define MIPHY_DEBUG_BUS 0x26 -#define MIPHY_DEBUG_STATUS_MSB 0x27 -#define MIPHY_DEBUG_STATUS_LSB 0x28 -#define MIPHY_PWR_RAIL_1 0x29 -#define MIPHY_PWR_RAIL_2 0x2a -#define MIPHY_SYNCHAR_CONTROL 0x30 - -#define MIPHY_COMP_FSM_1 0x3a -#define COMP_START BIT(6) - -#define MIPHY_COMP_FSM_6 0x3f -#define COMP_DONE BIT(7) - -#define MIPHY_COMP_POSTP 0x42 -#define MIPHY_TX_CTRL_1 0x49 -#define TX_REG_STEP_0V 0 -#define TX_REG_STEP_P_25MV 1 -#define TX_REG_STEP_P_50MV 2 -#define TX_REG_STEP_N_25MV 7 -#define TX_REG_STEP_N_50MV 6 -#define TX_REG_STEP_N_75MV 5 - -#define MIPHY_TX_CTRL_2 0x4a -#define TX_SLEW_SW_40_PS 0 -#define TX_SLEW_SW_80_PS 1 -#define TX_SLEW_SW_120_PS 2 - -#define MIPHY_TX_CTRL_3 0x4b -#define MIPHY_TX_CAL_MAN 0x4e -#define TX_SLEW_CAL_MAN_EN BIT(0) - -#define MIPHY_TST_BIAS_BOOST_2 0x62 -#define MIPHY_BIAS_BOOST_1 0x63 -#define MIPHY_BIAS_BOOST_2 0x64 -#define MIPHY_RX_DESBUFF_FDB_2 0x67 -#define MIPHY_RX_DESBUFF_FDB_3 0x68 -#define MIPHY_SIGDET_COMPENS1 0x69 -#define MIPHY_SIGDET_COMPENS2 0x6a -#define MIPHY_JITTER_PERIOD 0x6b -#define MIPHY_JITTER_AMPLITUDE_1 0x6c -#define MIPHY_JITTER_AMPLITUDE_2 0x6d -#define MIPHY_JITTER_AMPLITUDE_3 0x6e -#define MIPHY_RX_K_GAIN 0x78 -#define MIPHY_RX_BUFFER_CTRL 0x7a -#define VGA_GAIN BIT(0) -#define EQ_DC_GAIN BIT(2) -#define EQ_BOOST_GAIN BIT(3) - -#define MIPHY_RX_VGA_GAIN 0x7b -#define MIPHY_RX_EQU_GAIN_1 0x7f -#define MIPHY_RX_EQU_GAIN_2 0x80 -#define MIPHY_RX_EQU_GAIN_3 0x81 -#define MIPHY_RX_CAL_CTRL_1 0x97 -#define MIPHY_RX_CAL_CTRL_2 0x98 - -#define MIPHY_RX_CAL_OFFSET_CTRL 0x99 -#define CAL_OFFSET_VGA_64 (0x03 << 0) -#define CAL_OFFSET_THRESHOLD_64 (0x03 << 2) -#define VGA_OFFSET_POLARITY BIT(4) -#define OFFSET_COMPENSATION_EN BIT(6) - -#define MIPHY_RX_CAL_VGA_STEP 0x9a -#define MIPHY_RX_CAL_EYE_MIN 0x9d -#define MIPHY_RX_CAL_OPT_LENGTH 0x9f -#define MIPHY_RX_LOCK_CTRL_1 0xc1 -#define MIPHY_RX_LOCK_SETTINGS_OPT 0xc2 -#define MIPHY_RX_LOCK_STEP 0xc4 - -#define MIPHY_RX_SIGDET_SLEEP_OA 0xc9 -#define MIPHY_RX_SIGDET_SLEEP_SEL 0xca -#define MIPHY_RX_SIGDET_WAIT_SEL 0xcb -#define MIPHY_RX_SIGDET_DATA_SEL 0xcc -#define EN_ULTRA_LOW_POWER BIT(0) -#define EN_FIRST_HALF BIT(1) -#define EN_SECOND_HALF BIT(2) -#define EN_DIGIT_SIGNAL_CHECK BIT(3) - -#define MIPHY_RX_POWER_CTRL_1 0xcd -#define MIPHY_RX_POWER_CTRL_2 0xce -#define MIPHY_PLL_CALSET_CTRL 0xd3 -#define MIPHY_PLL_CALSET_1 0xd4 -#define MIPHY_PLL_CALSET_2 0xd5 -#define MIPHY_PLL_CALSET_3 0xd6 -#define MIPHY_PLL_CALSET_4 0xd7 -#define MIPHY_PLL_SBR_1 0xe3 -#define SET_NEW_CHANGE BIT(1) - -#define MIPHY_PLL_SBR_2 0xe4 -#define MIPHY_PLL_SBR_3 0xe5 -#define MIPHY_PLL_SBR_4 0xe6 -#define MIPHY_PLL_COMMON_MISC_2 0xe9 -#define START_ACT_FILT BIT(6) - -#define MIPHY_PLL_SPAREIN 0xeb - -/* - * On STiH407 the glue logic can be different among MiPHY devices; for example: - * MiPHY0: OSC_FORCE_EXT means: - * 0: 30MHz crystal clk - 1: 100MHz ext clk routed through MiPHY1 - * MiPHY1: OSC_FORCE_EXT means: - * 1: 30MHz crystal clk - 0: 100MHz ext clk routed through MiPHY1 - * Some devices have not the possibility to check if the osc is ready. - */ -#define MIPHY_OSC_FORCE_EXT BIT(3) -#define MIPHY_OSC_RDY BIT(5) - -#define MIPHY_CTRL_MASK 0x0f -#define MIPHY_CTRL_DEFAULT 0 -#define MIPHY_CTRL_SYNC_D_EN BIT(2) - -/* SATA / PCIe defines */ -#define SATA_CTRL_MASK 0x07 -#define PCIE_CTRL_MASK 0xff -#define SATA_CTRL_SELECT_SATA 1 -#define SATA_CTRL_SELECT_PCIE 0 -#define SYSCFG_PCIE_PCIE_VAL 0x80 -#define SATA_SPDMODE 1 - -#define MIPHY_SATA_BANK_NB 3 -#define MIPHY_PCIE_BANK_NB 2 - -enum { - SYSCFG_CTRL, - SYSCFG_STATUS, - SYSCFG_PCI, - SYSCFG_SATA, - SYSCFG_REG_MAX, -}; - -struct miphy28lp_phy { - struct phy *phy; - struct miphy28lp_dev *phydev; - void __iomem *base; - void __iomem *pipebase; - - bool osc_force_ext; - bool osc_rdy; - bool px_rx_pol_inv; - bool ssc; - bool tx_impedance; - - struct reset_control *miphy_rst; - - u32 sata_gen; - - /* Sysconfig registers offsets needed to configure the device */ - u32 syscfg_reg[SYSCFG_REG_MAX]; - u8 type; -}; - -struct miphy28lp_dev { - struct device *dev; - struct regmap *regmap; - struct mutex miphy_mutex; - struct miphy28lp_phy **phys; - int nphys; -}; - -struct miphy_initval { - u16 reg; - u16 val; -}; - -enum miphy_sata_gen { SATA_GEN1, SATA_GEN2, SATA_GEN3 }; - -static char *PHY_TYPE_name[] = { "sata-up", "pcie-up", "", "usb3-up" }; - -struct pll_ratio { - int clk_ref; - int calset_1; - int calset_2; - int calset_3; - int calset_4; - int cal_ctrl; -}; - -static struct pll_ratio sata_pll_ratio = { - .clk_ref = 0x1e, - .calset_1 = 0xc8, - .calset_2 = 0x00, - .calset_3 = 0x00, - .calset_4 = 0x00, - .cal_ctrl = 0x00, -}; - -static struct pll_ratio pcie_pll_ratio = { - .clk_ref = 0x1e, - .calset_1 = 0xa6, - .calset_2 = 0xaa, - .calset_3 = 0xaa, - .calset_4 = 0x00, - .cal_ctrl = 0x00, -}; - -static struct pll_ratio usb3_pll_ratio = { - .clk_ref = 0x1e, - .calset_1 = 0xa6, - .calset_2 = 0xaa, - .calset_3 = 0xaa, - .calset_4 = 0x04, - .cal_ctrl = 0x00, -}; - -struct miphy28lp_pll_gen { - int bank; - int speed; - int bias_boost_1; - int bias_boost_2; - int tx_ctrl_1; - int tx_ctrl_2; - int tx_ctrl_3; - int rx_k_gain; - int rx_vga_gain; - int rx_equ_gain_1; - int rx_equ_gain_2; - int rx_equ_gain_3; - int rx_buff_ctrl; -}; - -static struct miphy28lp_pll_gen sata_pll_gen[] = { - { - .bank = 0x00, - .speed = TX_SPDSEL_80DEC | RX_SPDSEL_80DEC, - .bias_boost_1 = 0x00, - .bias_boost_2 = 0xae, - .tx_ctrl_2 = 0x53, - .tx_ctrl_3 = 0x00, - .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, - .rx_vga_gain = 0x00, - .rx_equ_gain_1 = 0x7d, - .rx_equ_gain_2 = 0x56, - .rx_equ_gain_3 = 0x00, - }, - { - .bank = 0x01, - .speed = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC, - .bias_boost_1 = 0x00, - .bias_boost_2 = 0xae, - .tx_ctrl_2 = 0x72, - .tx_ctrl_3 = 0x20, - .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, - .rx_vga_gain = 0x00, - .rx_equ_gain_1 = 0x7d, - .rx_equ_gain_2 = 0x56, - .rx_equ_gain_3 = 0x00, - }, - { - .bank = 0x02, - .speed = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC, - .bias_boost_1 = 0x00, - .bias_boost_2 = 0xae, - .tx_ctrl_2 = 0xc0, - .tx_ctrl_3 = 0x20, - .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, - .rx_vga_gain = 0x00, - .rx_equ_gain_1 = 0x7d, - .rx_equ_gain_2 = 0x56, - .rx_equ_gain_3 = 0x00, - }, -}; - -static struct miphy28lp_pll_gen pcie_pll_gen[] = { - { - .bank = 0x00, - .speed = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC, - .bias_boost_1 = 0x00, - .bias_boost_2 = 0xa5, - .tx_ctrl_1 = TX_REG_STEP_N_25MV, - .tx_ctrl_2 = 0x71, - .tx_ctrl_3 = 0x60, - .rx_k_gain = 0x98, - .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, - .rx_vga_gain = 0x00, - .rx_equ_gain_1 = 0x79, - .rx_equ_gain_2 = 0x56, - }, - { - .bank = 0x01, - .speed = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC, - .bias_boost_1 = 0x00, - .bias_boost_2 = 0xa5, - .tx_ctrl_1 = TX_REG_STEP_N_25MV, - .tx_ctrl_2 = 0x70, - .tx_ctrl_3 = 0x60, - .rx_k_gain = 0xcc, - .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, - .rx_vga_gain = 0x00, - .rx_equ_gain_1 = 0x78, - .rx_equ_gain_2 = 0x07, - }, -}; - -static inline void miphy28lp_set_reset(struct miphy28lp_phy *miphy_phy) -{ - void __iomem *base = miphy_phy->base; - u8 val; - - /* Putting Macro in reset */ - writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); - - val = RST_APPLI_SW | RST_CONF_SW; - writeb_relaxed(val, base + MIPHY_CONF_RESET); - - writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); - - /* Bringing the MIPHY-CPU registers out of reset */ - if (miphy_phy->type == PHY_TYPE_PCIE) { - val = AUTO_RST_RX | TERM_EN_SW; - writeb_relaxed(val, base + MIPHY_CONTROL); - } else { - val = AUTO_RST_RX | TERM_EN_SW | DIS_LINK_RST; - writeb_relaxed(val, base + MIPHY_CONTROL); - } -} - -static inline void miphy28lp_pll_calibration(struct miphy28lp_phy *miphy_phy, - struct pll_ratio *pll_ratio) -{ - void __iomem *base = miphy_phy->base; - u8 val; - - /* Applying PLL Settings */ - writeb_relaxed(0x1d, base + MIPHY_PLL_SPAREIN); - writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ); - - /* PLL Ratio */ - writeb_relaxed(pll_ratio->calset_1, base + MIPHY_PLL_CALSET_1); - writeb_relaxed(pll_ratio->calset_2, base + MIPHY_PLL_CALSET_2); - writeb_relaxed(pll_ratio->calset_3, base + MIPHY_PLL_CALSET_3); - writeb_relaxed(pll_ratio->calset_4, base + MIPHY_PLL_CALSET_4); - writeb_relaxed(pll_ratio->cal_ctrl, base + MIPHY_PLL_CALSET_CTRL); - - writeb_relaxed(TX_SEL, base + MIPHY_BOUNDARY_SEL); - - val = (0x68 << 1) | TX_SLEW_CAL_MAN_EN; - writeb_relaxed(val, base + MIPHY_TX_CAL_MAN); - - val = VGA_OFFSET_POLARITY | CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64; - - if (miphy_phy->type != PHY_TYPE_SATA) - val |= OFFSET_COMPENSATION_EN; - - writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL); - - if (miphy_phy->type == PHY_TYPE_USB3) { - writeb_relaxed(0x00, base + MIPHY_CONF); - writeb_relaxed(0x70, base + MIPHY_RX_LOCK_STEP); - writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_OA); - writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_SEL); - writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_WAIT_SEL); - - val = EN_DIGIT_SIGNAL_CHECK | EN_FIRST_HALF; - writeb_relaxed(val, base + MIPHY_RX_SIGDET_DATA_SEL); - } - -} - -static inline void miphy28lp_sata_config_gen(struct miphy28lp_phy *miphy_phy) -{ - void __iomem *base = miphy_phy->base; - int i; - - for (i = 0; i < ARRAY_SIZE(sata_pll_gen); i++) { - struct miphy28lp_pll_gen *gen = &sata_pll_gen[i]; - - /* Banked settings */ - writeb_relaxed(gen->bank, base + MIPHY_CONF); - writeb_relaxed(gen->speed, base + MIPHY_SPEED); - writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1); - writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2); - - /* TX buffer Settings */ - writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2); - writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3); - - /* RX Buffer Settings */ - writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL); - writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN); - writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1); - writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2); - writeb_relaxed(gen->rx_equ_gain_3, base + MIPHY_RX_EQU_GAIN_3); - } -} - -static inline void miphy28lp_pcie_config_gen(struct miphy28lp_phy *miphy_phy) -{ - void __iomem *base = miphy_phy->base; - int i; - - for (i = 0; i < ARRAY_SIZE(pcie_pll_gen); i++) { - struct miphy28lp_pll_gen *gen = &pcie_pll_gen[i]; - - /* Banked settings */ - writeb_relaxed(gen->bank, base + MIPHY_CONF); - writeb_relaxed(gen->speed, base + MIPHY_SPEED); - writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1); - writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2); - - /* TX buffer Settings */ - writeb_relaxed(gen->tx_ctrl_1, base + MIPHY_TX_CTRL_1); - writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2); - writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3); - - writeb_relaxed(gen->rx_k_gain, base + MIPHY_RX_K_GAIN); - - /* RX Buffer Settings */ - writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL); - writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN); - writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1); - writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2); - } -} - -static inline int miphy28lp_wait_compensation(struct miphy28lp_phy *miphy_phy) -{ - unsigned long finish = jiffies + 5 * HZ; - u8 val; - - /* Waiting for Compensation to complete */ - do { - val = readb_relaxed(miphy_phy->base + MIPHY_COMP_FSM_6); - - if (time_after_eq(jiffies, finish)) - return -EBUSY; - cpu_relax(); - } while (!(val & COMP_DONE)); - - return 0; -} - - -static inline int miphy28lp_compensation(struct miphy28lp_phy *miphy_phy, - struct pll_ratio *pll_ratio) -{ - void __iomem *base = miphy_phy->base; - - /* Poll for HFC ready after reset release */ - /* Compensation measurement */ - writeb_relaxed(RST_PLL_SW | RST_COMP_SW, base + MIPHY_RESET); - - writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2); - writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ); - writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1); - - if (miphy_phy->type == PHY_TYPE_PCIE) - writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET); - - writeb_relaxed(0x00, base + MIPHY_RESET); - writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2); - writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); - - /* TX compensation offset to re-center TX impedance */ - writeb_relaxed(0x00, base + MIPHY_COMP_POSTP); - - if (miphy_phy->type == PHY_TYPE_PCIE) - return miphy28lp_wait_compensation(miphy_phy); - - return 0; -} - -static inline void miphy28_usb3_miphy_reset(struct miphy28lp_phy *miphy_phy) -{ - void __iomem *base = miphy_phy->base; - u8 val; - - /* MIPHY Reset */ - writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); - writeb_relaxed(0x00, base + MIPHY_CONF_RESET); - writeb_relaxed(RST_COMP_SW, base + MIPHY_RESET); - - val = RST_COMP_SW | RST_PLL_SW; - writeb_relaxed(val, base + MIPHY_RESET); - - writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2); - writeb_relaxed(0x1e, base + MIPHY_PLL_CLKREF_FREQ); - writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1); - writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET); - writeb_relaxed(0x00, base + MIPHY_RESET); - writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2); - writeb_relaxed(0x00, base + MIPHY_CONF); - writeb_relaxed(0x00, base + MIPHY_BOUNDARY_1); - writeb_relaxed(0x00, base + MIPHY_TST_BIAS_BOOST_2); - writeb_relaxed(0x00, base + MIPHY_CONF); - writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); - writeb_relaxed(0xa5, base + MIPHY_DEBUG_BUS); - writeb_relaxed(0x00, base + MIPHY_CONF); -} - -static void miphy_sata_tune_ssc(struct miphy28lp_phy *miphy_phy) -{ - void __iomem *base = miphy_phy->base; - u8 val; - - /* Compensate Tx impedance to avoid out of range values */ - /* - * Enable the SSC on PLL for all banks - * SSC Modulation @ 31 KHz and 4000 ppm modulation amp - */ - val = readb_relaxed(base + MIPHY_BOUNDARY_2); - val |= SSC_EN_SW; - writeb_relaxed(val, base + MIPHY_BOUNDARY_2); - - val = readb_relaxed(base + MIPHY_BOUNDARY_SEL); - val |= SSC_SEL; - writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); - - for (val = 0; val < MIPHY_SATA_BANK_NB; val++) { - writeb_relaxed(val, base + MIPHY_CONF); - - /* Add value to each reference clock cycle */ - /* and define the period length of the SSC */ - writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); - writeb_relaxed(0x6c, base + MIPHY_PLL_SBR_3); - writeb_relaxed(0x81, base + MIPHY_PLL_SBR_4); - - /* Clear any previous request */ - writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); - - /* requests the PLL to take in account new parameters */ - writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); - - /* To be sure there is no other pending requests */ - writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); - } -} - -static void miphy_pcie_tune_ssc(struct miphy28lp_phy *miphy_phy) -{ - void __iomem *base = miphy_phy->base; - u8 val; - - /* Compensate Tx impedance to avoid out of range values */ - /* - * Enable the SSC on PLL for all banks - * SSC Modulation @ 31 KHz and 4000 ppm modulation amp - */ - val = readb_relaxed(base + MIPHY_BOUNDARY_2); - val |= SSC_EN_SW; - writeb_relaxed(val, base + MIPHY_BOUNDARY_2); - - val = readb_relaxed(base + MIPHY_BOUNDARY_SEL); - val |= SSC_SEL; - writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); - - for (val = 0; val < MIPHY_PCIE_BANK_NB; val++) { - writeb_relaxed(val, base + MIPHY_CONF); - - /* Validate Step component */ - writeb_relaxed(0x69, base + MIPHY_PLL_SBR_3); - writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4); - - /* Validate Period component */ - writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); - writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4); - - /* Clear any previous request */ - writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); - - /* requests the PLL to take in account new parameters */ - writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); - - /* To be sure there is no other pending requests */ - writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); - } -} - -static inline void miphy_tune_tx_impedance(struct miphy28lp_phy *miphy_phy) -{ - /* Compensate Tx impedance to avoid out of range values */ - writeb_relaxed(0x02, miphy_phy->base + MIPHY_COMP_POSTP); -} - -static inline int miphy28lp_configure_sata(struct miphy28lp_phy *miphy_phy) -{ - void __iomem *base = miphy_phy->base; - int err; - u8 val; - - /* Putting Macro in reset */ - miphy28lp_set_reset(miphy_phy); - - /* PLL calibration */ - miphy28lp_pll_calibration(miphy_phy, &sata_pll_ratio); - - /* Banked settings Gen1/Gen2/Gen3 */ - miphy28lp_sata_config_gen(miphy_phy); - - /* Power control */ - /* Input bridge enable, manual input bridge control */ - writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); - - /* Macro out of reset */ - writeb_relaxed(0x00, base + MIPHY_CONF_RESET); - - /* Poll for HFC ready after reset release */ - /* Compensation measurement */ - err = miphy28lp_compensation(miphy_phy, &sata_pll_ratio); - if (err) - return err; - - if (miphy_phy->px_rx_pol_inv) { - /* Invert Rx polarity */ - val = readb_relaxed(miphy_phy->base + MIPHY_CONTROL); - val |= PX_RX_POL; - writeb_relaxed(val, miphy_phy->base + MIPHY_CONTROL); - } - - if (miphy_phy->ssc) - miphy_sata_tune_ssc(miphy_phy); - - if (miphy_phy->tx_impedance) - miphy_tune_tx_impedance(miphy_phy); - - return 0; -} - -static inline int miphy28lp_configure_pcie(struct miphy28lp_phy *miphy_phy) -{ - void __iomem *base = miphy_phy->base; - int err; - - /* Putting Macro in reset */ - miphy28lp_set_reset(miphy_phy); - - /* PLL calibration */ - miphy28lp_pll_calibration(miphy_phy, &pcie_pll_ratio); - - /* Banked settings Gen1/Gen2 */ - miphy28lp_pcie_config_gen(miphy_phy); - - /* Power control */ - /* Input bridge enable, manual input bridge control */ - writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); - - /* Macro out of reset */ - writeb_relaxed(0x00, base + MIPHY_CONF_RESET); - - /* Poll for HFC ready after reset release */ - /* Compensation measurement */ - err = miphy28lp_compensation(miphy_phy, &pcie_pll_ratio); - if (err) - return err; - - if (miphy_phy->ssc) - miphy_pcie_tune_ssc(miphy_phy); - - if (miphy_phy->tx_impedance) - miphy_tune_tx_impedance(miphy_phy); - - return 0; -} - - -static inline void miphy28lp_configure_usb3(struct miphy28lp_phy *miphy_phy) -{ - void __iomem *base = miphy_phy->base; - u8 val; - - /* Putting Macro in reset */ - miphy28lp_set_reset(miphy_phy); - - /* PLL calibration */ - miphy28lp_pll_calibration(miphy_phy, &usb3_pll_ratio); - - /* Writing The Speed Rate */ - writeb_relaxed(0x00, base + MIPHY_CONF); - - val = RX_SPDSEL_20DEC | TX_SPDSEL_20DEC; - writeb_relaxed(val, base + MIPHY_SPEED); - - /* RX Channel compensation and calibration */ - writeb_relaxed(0x1c, base + MIPHY_RX_LOCK_SETTINGS_OPT); - writeb_relaxed(0x51, base + MIPHY_RX_CAL_CTRL_1); - writeb_relaxed(0x70, base + MIPHY_RX_CAL_CTRL_2); - - val = OFFSET_COMPENSATION_EN | VGA_OFFSET_POLARITY | - CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64; - writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL); - writeb_relaxed(0x22, base + MIPHY_RX_CAL_VGA_STEP); - writeb_relaxed(0x0e, base + MIPHY_RX_CAL_OPT_LENGTH); - - val = EQ_DC_GAIN | VGA_GAIN; - writeb_relaxed(val, base + MIPHY_RX_BUFFER_CTRL); - writeb_relaxed(0x78, base + MIPHY_RX_EQU_GAIN_1); - writeb_relaxed(0x1b, base + MIPHY_SYNCHAR_CONTROL); - - /* TX compensation offset to re-center TX impedance */ - writeb_relaxed(0x02, base + MIPHY_COMP_POSTP); - - /* Enable GENSEL_SEL and SSC */ - /* TX_SEL=0 swing preemp forced by pipe registres */ - val = SSC_SEL | GENSEL_SEL; - writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); - - /* MIPHY Bias boost */ - writeb_relaxed(0x00, base + MIPHY_BIAS_BOOST_1); - writeb_relaxed(0xa7, base + MIPHY_BIAS_BOOST_2); - - /* SSC modulation */ - writeb_relaxed(SSC_EN_SW, base + MIPHY_BOUNDARY_2); - - /* MIPHY TX control */ - writeb_relaxed(0x00, base + MIPHY_CONF); - - /* Validate Step component */ - writeb_relaxed(0x5a, base + MIPHY_PLL_SBR_3); - writeb_relaxed(0xa0, base + MIPHY_PLL_SBR_4); - - /* Validate Period component */ - writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); - writeb_relaxed(0xa1, base + MIPHY_PLL_SBR_4); - - /* Clear any previous request */ - writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); - - /* requests the PLL to take in account new parameters */ - writeb_relaxed(0x02, base + MIPHY_PLL_SBR_1); - - /* To be sure there is no other pending requests */ - writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); - - /* Rx PI controller settings */ - writeb_relaxed(0xca, base + MIPHY_RX_K_GAIN); - - /* MIPHY RX input bridge control */ - /* INPUT_BRIDGE_EN_SW=1, manual input bridge control[0]=1 */ - writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); - writeb_relaxed(0x29, base + MIPHY_RX_POWER_CTRL_1); - writeb_relaxed(0x1a, base + MIPHY_RX_POWER_CTRL_2); - - /* MIPHY Reset for usb3 */ - miphy28_usb3_miphy_reset(miphy_phy); -} - -static inline int miphy_is_ready(struct miphy28lp_phy *miphy_phy) -{ - unsigned long finish = jiffies + 5 * HZ; - u8 mask = HFC_PLL | HFC_RDY; - u8 val; - - /* - * For PCIe and USB3 check only that PLL and HFC are ready - * For SATA check also that phy is ready! - */ - if (miphy_phy->type == PHY_TYPE_SATA) - mask |= PHY_RDY; - - do { - val = readb_relaxed(miphy_phy->base + MIPHY_STATUS_1); - if ((val & mask) != mask) - cpu_relax(); - else - return 0; - } while (!time_after_eq(jiffies, finish)); - - return -EBUSY; -} - -static int miphy_osc_is_ready(struct miphy28lp_phy *miphy_phy) -{ - struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; - unsigned long finish = jiffies + 5 * HZ; - u32 val; - - if (!miphy_phy->osc_rdy) - return 0; - - if (!miphy_phy->syscfg_reg[SYSCFG_STATUS]) - return -EINVAL; - - do { - regmap_read(miphy_dev->regmap, - miphy_phy->syscfg_reg[SYSCFG_STATUS], &val); - - if ((val & MIPHY_OSC_RDY) != MIPHY_OSC_RDY) - cpu_relax(); - else - return 0; - } while (!time_after_eq(jiffies, finish)); - - return -EBUSY; -} - -static int miphy28lp_get_resource_byname(struct device_node *child, - char *rname, struct resource *res) -{ - int index; - - index = of_property_match_string(child, "reg-names", rname); - if (index < 0) - return -ENODEV; - - return of_address_to_resource(child, index, res); -} - -static int miphy28lp_get_one_addr(struct device *dev, - struct device_node *child, char *rname, - void __iomem **base) -{ - struct resource res; - int ret; - - ret = miphy28lp_get_resource_byname(child, rname, &res); - if (!ret) { - *base = devm_ioremap(dev, res.start, resource_size(&res)); - if (!*base) { - dev_err(dev, "failed to ioremap %s address region\n" - , rname); - return -ENOENT; - } - } - - return 0; -} - -/* MiPHY reset and sysconf setup */ -static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val) -{ - int err; - struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; - - if (!miphy_phy->syscfg_reg[SYSCFG_CTRL]) - return -EINVAL; - - err = reset_control_assert(miphy_phy->miphy_rst); - if (err) { - dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); - return err; - } - - if (miphy_phy->osc_force_ext) - miphy_val |= MIPHY_OSC_FORCE_EXT; - - regmap_update_bits(miphy_dev->regmap, - miphy_phy->syscfg_reg[SYSCFG_CTRL], - MIPHY_CTRL_MASK, miphy_val); - - err = reset_control_deassert(miphy_phy->miphy_rst); - if (err) { - dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); - return err; - } - - return miphy_osc_is_ready(miphy_phy); -} - -static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy) -{ - struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; - int err, sata_conf = SATA_CTRL_SELECT_SATA; - - if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) || - (!miphy_phy->syscfg_reg[SYSCFG_PCI]) || - (!miphy_phy->base)) - return -EINVAL; - - dev_info(miphy_dev->dev, "sata-up mode, addr 0x%p\n", miphy_phy->base); - - /* Configure the glue-logic */ - sata_conf |= ((miphy_phy->sata_gen - SATA_GEN1) << SATA_SPDMODE); - - regmap_update_bits(miphy_dev->regmap, - miphy_phy->syscfg_reg[SYSCFG_SATA], - SATA_CTRL_MASK, sata_conf); - - regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI], - PCIE_CTRL_MASK, SATA_CTRL_SELECT_PCIE); - - /* MiPHY path and clocking init */ - err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT); - - if (err) { - dev_err(miphy_dev->dev, "SATA phy setup failed\n"); - return err; - } - - /* initialize miphy */ - miphy28lp_configure_sata(miphy_phy); - - return miphy_is_ready(miphy_phy); -} - -static int miphy28lp_init_pcie(struct miphy28lp_phy *miphy_phy) -{ - struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; - int err; - - if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) || - (!miphy_phy->syscfg_reg[SYSCFG_PCI]) - || (!miphy_phy->base) || (!miphy_phy->pipebase)) - return -EINVAL; - - dev_info(miphy_dev->dev, "pcie-up mode, addr 0x%p\n", miphy_phy->base); - - /* Configure the glue-logic */ - regmap_update_bits(miphy_dev->regmap, - miphy_phy->syscfg_reg[SYSCFG_SATA], - SATA_CTRL_MASK, SATA_CTRL_SELECT_PCIE); - - regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI], - PCIE_CTRL_MASK, SYSCFG_PCIE_PCIE_VAL); - - /* MiPHY path and clocking init */ - err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT); - - if (err) { - dev_err(miphy_dev->dev, "PCIe phy setup failed\n"); - return err; - } - - /* initialize miphy */ - err = miphy28lp_configure_pcie(miphy_phy); - if (err) - return err; - - /* PIPE Wrapper Configuration */ - writeb_relaxed(0x68, miphy_phy->pipebase + 0x104); /* Rise_0 */ - writeb_relaxed(0x61, miphy_phy->pipebase + 0x105); /* Rise_1 */ - writeb_relaxed(0x68, miphy_phy->pipebase + 0x108); /* Fall_0 */ - writeb_relaxed(0x61, miphy_phy->pipebase + 0x109); /* Fall-1 */ - writeb_relaxed(0x68, miphy_phy->pipebase + 0x10c); /* Threshold_0 */ - writeb_relaxed(0x60, miphy_phy->pipebase + 0x10d); /* Threshold_1 */ - - /* Wait for phy_ready */ - return miphy_is_ready(miphy_phy); -} - -static int miphy28lp_init_usb3(struct miphy28lp_phy *miphy_phy) -{ - struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; - int err; - - if ((!miphy_phy->base) || (!miphy_phy->pipebase)) - return -EINVAL; - - dev_info(miphy_dev->dev, "usb3-up mode, addr 0x%p\n", miphy_phy->base); - - /* MiPHY path and clocking init */ - err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_SYNC_D_EN); - if (err) { - dev_err(miphy_dev->dev, "USB3 phy setup failed\n"); - return err; - } - - /* initialize miphy */ - miphy28lp_configure_usb3(miphy_phy); - - /* PIPE Wrapper Configuration */ - writeb_relaxed(0x68, miphy_phy->pipebase + 0x23); - writeb_relaxed(0x61, miphy_phy->pipebase + 0x24); - writeb_relaxed(0x68, miphy_phy->pipebase + 0x26); - writeb_relaxed(0x61, miphy_phy->pipebase + 0x27); - writeb_relaxed(0x18, miphy_phy->pipebase + 0x29); - writeb_relaxed(0x61, miphy_phy->pipebase + 0x2a); - - /* pipe Wrapper usb3 TX swing de-emph margin PREEMPH[7:4], SWING[3:0] */ - writeb_relaxed(0X67, miphy_phy->pipebase + 0x68); - writeb_relaxed(0x0d, miphy_phy->pipebase + 0x69); - writeb_relaxed(0X67, miphy_phy->pipebase + 0x6a); - writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6b); - writeb_relaxed(0X67, miphy_phy->pipebase + 0x6c); - writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6d); - writeb_relaxed(0X67, miphy_phy->pipebase + 0x6e); - writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6f); - - return miphy_is_ready(miphy_phy); -} - -static int miphy28lp_init(struct phy *phy) -{ - struct miphy28lp_phy *miphy_phy = phy_get_drvdata(phy); - struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; - int ret; - - mutex_lock(&miphy_dev->miphy_mutex); - - switch (miphy_phy->type) { - - case PHY_TYPE_SATA: - ret = miphy28lp_init_sata(miphy_phy); - break; - case PHY_TYPE_PCIE: - ret = miphy28lp_init_pcie(miphy_phy); - break; - case PHY_TYPE_USB3: - ret = miphy28lp_init_usb3(miphy_phy); - break; - default: - ret = -EINVAL; - break; - } - - mutex_unlock(&miphy_dev->miphy_mutex); - - return ret; -} - -static int miphy28lp_get_addr(struct miphy28lp_phy *miphy_phy) -{ - struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; - struct device_node *phynode = miphy_phy->phy->dev.of_node; - int err; - - if ((miphy_phy->type != PHY_TYPE_SATA) && - (miphy_phy->type != PHY_TYPE_PCIE) && - (miphy_phy->type != PHY_TYPE_USB3)) { - return -EINVAL; - } - - err = miphy28lp_get_one_addr(miphy_dev->dev, phynode, - PHY_TYPE_name[miphy_phy->type - PHY_TYPE_SATA], - &miphy_phy->base); - if (err) - return err; - - if ((miphy_phy->type == PHY_TYPE_PCIE) || - (miphy_phy->type == PHY_TYPE_USB3)) { - err = miphy28lp_get_one_addr(miphy_dev->dev, phynode, "pipew", - &miphy_phy->pipebase); - if (err) - return err; - } - - return 0; -} - -static struct phy *miphy28lp_xlate(struct device *dev, - struct of_phandle_args *args) -{ - struct miphy28lp_dev *miphy_dev = dev_get_drvdata(dev); - struct miphy28lp_phy *miphy_phy = NULL; - struct device_node *phynode = args->np; - int ret, index = 0; - - if (args->args_count != 1) { - dev_err(dev, "Invalid number of cells in 'phy' property\n"); - return ERR_PTR(-EINVAL); - } - - for (index = 0; index < miphy_dev->nphys; index++) - if (phynode == miphy_dev->phys[index]->phy->dev.of_node) { - miphy_phy = miphy_dev->phys[index]; - break; - } - - if (!miphy_phy) { - dev_err(dev, "Failed to find appropriate phy\n"); - return ERR_PTR(-EINVAL); - } - - miphy_phy->type = args->args[0]; - - ret = miphy28lp_get_addr(miphy_phy); - if (ret < 0) - return ERR_PTR(ret); - - return miphy_phy->phy; -} - -static const struct phy_ops miphy28lp_ops = { - .init = miphy28lp_init, - .owner = THIS_MODULE, -}; - -static int miphy28lp_probe_resets(struct device_node *node, - struct miphy28lp_phy *miphy_phy) -{ - struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; - int err; - - miphy_phy->miphy_rst = - of_reset_control_get_shared(node, "miphy-sw-rst"); - - if (IS_ERR(miphy_phy->miphy_rst)) { - dev_err(miphy_dev->dev, - "miphy soft reset control not defined\n"); - return PTR_ERR(miphy_phy->miphy_rst); - } - - err = reset_control_deassert(miphy_phy->miphy_rst); - if (err) { - dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); - return err; - } - - return 0; -} - -static int miphy28lp_of_probe(struct device_node *np, - struct miphy28lp_phy *miphy_phy) -{ - int i; - u32 ctrlreg; - - miphy_phy->osc_force_ext = - of_property_read_bool(np, "st,osc-force-ext"); - - miphy_phy->osc_rdy = of_property_read_bool(np, "st,osc-rdy"); - - miphy_phy->px_rx_pol_inv = - of_property_read_bool(np, "st,px_rx_pol_inv"); - - miphy_phy->ssc = of_property_read_bool(np, "st,ssc-on"); - - miphy_phy->tx_impedance = - of_property_read_bool(np, "st,tx-impedance-comp"); - - of_property_read_u32(np, "st,sata-gen", &miphy_phy->sata_gen); - if (!miphy_phy->sata_gen) - miphy_phy->sata_gen = SATA_GEN1; - - for (i = 0; i < SYSCFG_REG_MAX; i++) { - if (!of_property_read_u32_index(np, "st,syscfg", i, &ctrlreg)) - miphy_phy->syscfg_reg[i] = ctrlreg; - } - - return 0; -} - -static int miphy28lp_probe(struct platform_device *pdev) -{ - struct device_node *child, *np = pdev->dev.of_node; - struct miphy28lp_dev *miphy_dev; - struct phy_provider *provider; - struct phy *phy; - int ret, port = 0; - - miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL); - if (!miphy_dev) - return -ENOMEM; - - miphy_dev->nphys = of_get_child_count(np); - miphy_dev->phys = devm_kcalloc(&pdev->dev, miphy_dev->nphys, - sizeof(*miphy_dev->phys), GFP_KERNEL); - if (!miphy_dev->phys) - return -ENOMEM; - - miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); - if (IS_ERR(miphy_dev->regmap)) { - dev_err(miphy_dev->dev, "No syscfg phandle specified\n"); - return PTR_ERR(miphy_dev->regmap); - } - - miphy_dev->dev = &pdev->dev; - - dev_set_drvdata(&pdev->dev, miphy_dev); - - mutex_init(&miphy_dev->miphy_mutex); - - for_each_child_of_node(np, child) { - struct miphy28lp_phy *miphy_phy; - - miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy), - GFP_KERNEL); - if (!miphy_phy) { - ret = -ENOMEM; - goto put_child; - } - - miphy_dev->phys[port] = miphy_phy; - - phy = devm_phy_create(&pdev->dev, child, &miphy28lp_ops); - if (IS_ERR(phy)) { - dev_err(&pdev->dev, "failed to create PHY\n"); - ret = PTR_ERR(phy); - goto put_child; - } - - miphy_dev->phys[port]->phy = phy; - miphy_dev->phys[port]->phydev = miphy_dev; - - ret = miphy28lp_of_probe(child, miphy_phy); - if (ret) - goto put_child; - - ret = miphy28lp_probe_resets(child, miphy_dev->phys[port]); - if (ret) - goto put_child; - - phy_set_drvdata(phy, miphy_dev->phys[port]); - port++; - - } - - provider = devm_of_phy_provider_register(&pdev->dev, miphy28lp_xlate); - return PTR_ERR_OR_ZERO(provider); -put_child: - of_node_put(child); - return ret; -} - -static const struct of_device_id miphy28lp_of_match[] = { - {.compatible = "st,miphy28lp-phy", }, - {}, -}; - -MODULE_DEVICE_TABLE(of, miphy28lp_of_match); - -static struct platform_driver miphy28lp_driver = { - .probe = miphy28lp_probe, - .driver = { - .name = "miphy28lp-phy", - .of_match_table = miphy28lp_of_match, - } -}; - -module_platform_driver(miphy28lp_driver); - -MODULE_AUTHOR("Alexandre Torgue "); -MODULE_DESCRIPTION("STMicroelectronics miphy28lp driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-mvebu-sata.c b/drivers/phy/phy-mvebu-sata.c deleted file mode 100644 index 768ce92e81ce..000000000000 --- a/drivers/phy/phy-mvebu-sata.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * phy-mvebu-sata.c: SATA Phy driver for the Marvell mvebu SoCs. - * - * Copyright (C) 2013 Andrew Lunn - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include - -struct priv { - struct clk *clk; - void __iomem *base; -}; - -#define SATA_PHY_MODE_2 0x0330 -#define MODE_2_FORCE_PU_TX BIT(0) -#define MODE_2_FORCE_PU_RX BIT(1) -#define MODE_2_PU_PLL BIT(2) -#define MODE_2_PU_IVREF BIT(3) -#define SATA_IF_CTRL 0x0050 -#define CTRL_PHY_SHUTDOWN BIT(9) - -static int phy_mvebu_sata_power_on(struct phy *phy) -{ - struct priv *priv = phy_get_drvdata(phy); - u32 reg; - - clk_prepare_enable(priv->clk); - - /* Enable PLL and IVREF */ - reg = readl(priv->base + SATA_PHY_MODE_2); - reg |= (MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX | - MODE_2_PU_PLL | MODE_2_PU_IVREF); - writel(reg , priv->base + SATA_PHY_MODE_2); - - /* Enable PHY */ - reg = readl(priv->base + SATA_IF_CTRL); - reg &= ~CTRL_PHY_SHUTDOWN; - writel(reg, priv->base + SATA_IF_CTRL); - - clk_disable_unprepare(priv->clk); - - return 0; -} - -static int phy_mvebu_sata_power_off(struct phy *phy) -{ - struct priv *priv = phy_get_drvdata(phy); - u32 reg; - - clk_prepare_enable(priv->clk); - - /* Disable PLL and IVREF */ - reg = readl(priv->base + SATA_PHY_MODE_2); - reg &= ~(MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX | - MODE_2_PU_PLL | MODE_2_PU_IVREF); - writel(reg, priv->base + SATA_PHY_MODE_2); - - /* Disable PHY */ - reg = readl(priv->base + SATA_IF_CTRL); - reg |= CTRL_PHY_SHUTDOWN; - writel(reg, priv->base + SATA_IF_CTRL); - - clk_disable_unprepare(priv->clk); - - return 0; -} - -static const struct phy_ops phy_mvebu_sata_ops = { - .power_on = phy_mvebu_sata_power_on, - .power_off = phy_mvebu_sata_power_off, - .owner = THIS_MODULE, -}; - -static int phy_mvebu_sata_probe(struct platform_device *pdev) -{ - struct phy_provider *phy_provider; - struct resource *res; - struct priv *priv; - struct phy *phy; - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(priv->base)) - return PTR_ERR(priv->base); - - priv->clk = devm_clk_get(&pdev->dev, "sata"); - if (IS_ERR(priv->clk)) - return PTR_ERR(priv->clk); - - phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops); - if (IS_ERR(phy)) - return PTR_ERR(phy); - - phy_set_drvdata(phy, priv); - - phy_provider = devm_of_phy_provider_register(&pdev->dev, - of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - - /* The boot loader may of left it on. Turn it off. */ - phy_mvebu_sata_power_off(phy); - - return 0; -} - -static const struct of_device_id phy_mvebu_sata_of_match[] = { - { .compatible = "marvell,mvebu-sata-phy" }, - { }, -}; -MODULE_DEVICE_TABLE(of, phy_mvebu_sata_of_match); - -static struct platform_driver phy_mvebu_sata_driver = { - .probe = phy_mvebu_sata_probe, - .driver = { - .name = "phy-mvebu-sata", - .of_match_table = phy_mvebu_sata_of_match, - } -}; -module_platform_driver(phy_mvebu_sata_driver); - -MODULE_AUTHOR("Andrew Lunn "); -MODULE_DESCRIPTION("Marvell MVEBU SATA PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-omap-control.c b/drivers/phy/phy-omap-control.c deleted file mode 100644 index e9c41b3fa0ee..000000000000 --- a/drivers/phy/phy-omap-control.c +++ /dev/null @@ -1,360 +0,0 @@ -/* - * omap-control-phy.c - The PHY part of control module. - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Author: Kishon Vijay Abraham I - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * omap_control_pcie_pcs - set the PCS delay count - * @dev: the control module device - * @delay: 8 bit delay value - */ -void omap_control_pcie_pcs(struct device *dev, u8 delay) -{ - u32 val; - struct omap_control_phy *control_phy; - - if (IS_ERR(dev) || !dev) { - pr_err("%s: invalid device\n", __func__); - return; - } - - control_phy = dev_get_drvdata(dev); - if (!control_phy) { - dev_err(dev, "%s: invalid control phy device\n", __func__); - return; - } - - if (control_phy->type != OMAP_CTRL_TYPE_PCIE) { - dev_err(dev, "%s: unsupported operation\n", __func__); - return; - } - - val = readl(control_phy->pcie_pcs); - val &= ~(OMAP_CTRL_PCIE_PCS_MASK << - OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); - val |= (delay << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); - writel(val, control_phy->pcie_pcs); -} -EXPORT_SYMBOL_GPL(omap_control_pcie_pcs); - -/** - * omap_control_phy_power - power on/off the phy using control module reg - * @dev: the control module device - * @on: 0 or 1, based on powering on or off the PHY - */ -void omap_control_phy_power(struct device *dev, int on) -{ - u32 val; - unsigned long rate; - struct omap_control_phy *control_phy; - - if (IS_ERR(dev) || !dev) { - pr_err("%s: invalid device\n", __func__); - return; - } - - control_phy = dev_get_drvdata(dev); - if (!control_phy) { - dev_err(dev, "%s: invalid control phy device\n", __func__); - return; - } - - if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) - return; - - val = readl(control_phy->power); - - switch (control_phy->type) { - case OMAP_CTRL_TYPE_USB2: - if (on) - val &= ~OMAP_CTRL_DEV_PHY_PD; - else - val |= OMAP_CTRL_DEV_PHY_PD; - break; - - case OMAP_CTRL_TYPE_PCIE: - case OMAP_CTRL_TYPE_PIPE3: - rate = clk_get_rate(control_phy->sys_clk); - rate = rate/1000000; - - if (on) { - val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | - OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK); - val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON << - OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; - val |= rate << - OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; - } else { - val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK; - val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF << - OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; - } - break; - - case OMAP_CTRL_TYPE_DRA7USB2: - if (on) - val &= ~OMAP_CTRL_USB2_PHY_PD; - else - val |= OMAP_CTRL_USB2_PHY_PD; - break; - - case OMAP_CTRL_TYPE_AM437USB2: - if (on) { - val &= ~(AM437X_CTRL_USB2_PHY_PD | - AM437X_CTRL_USB2_OTG_PD); - val |= (AM437X_CTRL_USB2_OTGVDET_EN | - AM437X_CTRL_USB2_OTGSESSEND_EN); - } else { - val &= ~(AM437X_CTRL_USB2_OTGVDET_EN | - AM437X_CTRL_USB2_OTGSESSEND_EN); - val |= (AM437X_CTRL_USB2_PHY_PD | - AM437X_CTRL_USB2_OTG_PD); - } - break; - default: - dev_err(dev, "%s: type %d not recognized\n", - __func__, control_phy->type); - break; - } - - writel(val, control_phy->power); -} -EXPORT_SYMBOL_GPL(omap_control_phy_power); - -/** - * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded - * @ctrl_phy: struct omap_control_phy * - * - * Writes to the mailbox register to notify the usb core that a usb - * device has been connected. - */ -static void omap_control_usb_host_mode(struct omap_control_phy *ctrl_phy) -{ - u32 val; - - val = readl(ctrl_phy->otghs_control); - val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND); - val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID; - writel(val, ctrl_phy->otghs_control); -} - -/** - * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high - * impedance - * @ctrl_phy: struct omap_control_phy * - * - * Writes to the mailbox register to notify the usb core that it has been - * connected to a usb host. - */ -static void omap_control_usb_device_mode(struct omap_control_phy *ctrl_phy) -{ - u32 val; - - val = readl(ctrl_phy->otghs_control); - val &= ~OMAP_CTRL_DEV_SESSEND; - val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID | - OMAP_CTRL_DEV_VBUSVALID; - writel(val, ctrl_phy->otghs_control); -} - -/** - * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high - * impedance - * @ctrl_phy: struct omap_control_phy * - * - * Writes to the mailbox register to notify the usb core it's now in - * disconnected state. - */ -static void omap_control_usb_set_sessionend(struct omap_control_phy *ctrl_phy) -{ - u32 val; - - val = readl(ctrl_phy->otghs_control); - val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID); - val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND; - writel(val, ctrl_phy->otghs_control); -} - -/** - * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode - * or device mode or to denote disconnected state - * @dev: the control module device - * @mode: The mode to which usb should be configured - * - * This is an API to write to the mailbox register to notify the usb core that - * a usb device has been connected. - */ -void omap_control_usb_set_mode(struct device *dev, - enum omap_control_usb_mode mode) -{ - struct omap_control_phy *ctrl_phy; - - if (IS_ERR(dev) || !dev) - return; - - ctrl_phy = dev_get_drvdata(dev); - if (!ctrl_phy) { - dev_err(dev, "Invalid control phy device\n"); - return; - } - - if (ctrl_phy->type != OMAP_CTRL_TYPE_OTGHS) - return; - - switch (mode) { - case USB_MODE_HOST: - omap_control_usb_host_mode(ctrl_phy); - break; - case USB_MODE_DEVICE: - omap_control_usb_device_mode(ctrl_phy); - break; - case USB_MODE_DISCONNECT: - omap_control_usb_set_sessionend(ctrl_phy); - break; - default: - dev_vdbg(dev, "invalid omap control usb mode\n"); - } -} -EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); - -static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS; -static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2; -static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; -static const enum omap_control_phy_type pcie_data = OMAP_CTRL_TYPE_PCIE; -static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2; -static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; - -static const struct of_device_id omap_control_phy_id_table[] = { - { - .compatible = "ti,control-phy-otghs", - .data = &otghs_data, - }, - { - .compatible = "ti,control-phy-usb2", - .data = &usb2_data, - }, - { - .compatible = "ti,control-phy-pipe3", - .data = &pipe3_data, - }, - { - .compatible = "ti,control-phy-pcie", - .data = &pcie_data, - }, - { - .compatible = "ti,control-phy-usb2-dra7", - .data = &dra7usb2_data, - }, - { - .compatible = "ti,control-phy-usb2-am437", - .data = &am437usb2_data, - }, - {}, -}; -MODULE_DEVICE_TABLE(of, omap_control_phy_id_table); - -static int omap_control_phy_probe(struct platform_device *pdev) -{ - struct resource *res; - const struct of_device_id *of_id; - struct omap_control_phy *control_phy; - - of_id = of_match_device(omap_control_phy_id_table, &pdev->dev); - if (!of_id) - return -EINVAL; - - control_phy = devm_kzalloc(&pdev->dev, sizeof(*control_phy), - GFP_KERNEL); - if (!control_phy) - return -ENOMEM; - - control_phy->dev = &pdev->dev; - control_phy->type = *(enum omap_control_phy_type *)of_id->data; - - if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "otghs_control"); - control_phy->otghs_control = devm_ioremap_resource( - &pdev->dev, res); - if (IS_ERR(control_phy->otghs_control)) - return PTR_ERR(control_phy->otghs_control); - } else { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "power"); - control_phy->power = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(control_phy->power)) { - dev_err(&pdev->dev, "Couldn't get power register\n"); - return PTR_ERR(control_phy->power); - } - } - - if (control_phy->type == OMAP_CTRL_TYPE_PIPE3 || - control_phy->type == OMAP_CTRL_TYPE_PCIE) { - control_phy->sys_clk = devm_clk_get(control_phy->dev, - "sys_clkin"); - if (IS_ERR(control_phy->sys_clk)) { - pr_err("%s: unable to get sys_clkin\n", __func__); - return -EINVAL; - } - } - - if (control_phy->type == OMAP_CTRL_TYPE_PCIE) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "pcie_pcs"); - control_phy->pcie_pcs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(control_phy->pcie_pcs)) - return PTR_ERR(control_phy->pcie_pcs); - } - - dev_set_drvdata(control_phy->dev, control_phy); - - return 0; -} - -static struct platform_driver omap_control_phy_driver = { - .probe = omap_control_phy_probe, - .driver = { - .name = "omap-control-phy", - .of_match_table = omap_control_phy_id_table, - }, -}; - -static int __init omap_control_phy_init(void) -{ - return platform_driver_register(&omap_control_phy_driver); -} -subsys_initcall(omap_control_phy_init); - -static void __exit omap_control_phy_exit(void) -{ - platform_driver_unregister(&omap_control_phy_driver); -} -module_exit(omap_control_phy_exit); - -MODULE_ALIAS("platform:omap_control_phy"); -MODULE_AUTHOR("Texas Instruments Inc."); -MODULE_DESCRIPTION("OMAP Control Module PHY Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c deleted file mode 100644 index fe909fd8144f..000000000000 --- a/drivers/phy/phy-omap-usb2.c +++ /dev/null @@ -1,439 +0,0 @@ -/* - * omap-usb2.c - USB PHY, talking to musb controller in OMAP. - * - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Author: Kishon Vijay Abraham I - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define USB2PHY_DISCON_BYP_LATCH (1 << 31) -#define USB2PHY_ANA_CONFIG1 0x4c - -/** - * omap_usb2_set_comparator - links the comparator present in the sytem with - * this phy - * @comparator - the companion phy(comparator) for this phy - * - * The phy companion driver should call this API passing the phy_companion - * filled with set_vbus and start_srp to be used by usb phy. - * - * For use by phy companion driver - */ -int omap_usb2_set_comparator(struct phy_companion *comparator) -{ - struct omap_usb *phy; - struct usb_phy *x = usb_get_phy(USB_PHY_TYPE_USB2); - - if (IS_ERR(x)) - return -ENODEV; - - phy = phy_to_omapusb(x); - phy->comparator = comparator; - return 0; -} -EXPORT_SYMBOL_GPL(omap_usb2_set_comparator); - -static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled) -{ - struct omap_usb *phy = phy_to_omapusb(otg->usb_phy); - - if (!phy->comparator) - return -ENODEV; - - return phy->comparator->set_vbus(phy->comparator, enabled); -} - -static int omap_usb_start_srp(struct usb_otg *otg) -{ - struct omap_usb *phy = phy_to_omapusb(otg->usb_phy); - - if (!phy->comparator) - return -ENODEV; - - return phy->comparator->start_srp(phy->comparator); -} - -static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - otg->host = host; - if (!host) - otg->state = OTG_STATE_UNDEFINED; - - return 0; -} - -static int omap_usb_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - otg->gadget = gadget; - if (!gadget) - otg->state = OTG_STATE_UNDEFINED; - - return 0; -} - -static int omap_usb_phy_power(struct omap_usb *phy, int on) -{ - u32 val; - int ret; - - if (!phy->syscon_phy_power) { - omap_control_phy_power(phy->control_dev, on); - return 0; - } - - if (on) - val = phy->power_on; - else - val = phy->power_off; - - ret = regmap_update_bits(phy->syscon_phy_power, phy->power_reg, - phy->mask, val); - return ret; -} - -static int omap_usb_power_off(struct phy *x) -{ - struct omap_usb *phy = phy_get_drvdata(x); - - return omap_usb_phy_power(phy, false); -} - -static int omap_usb_power_on(struct phy *x) -{ - struct omap_usb *phy = phy_get_drvdata(x); - - return omap_usb_phy_power(phy, true); -} - -static int omap_usb2_disable_clocks(struct omap_usb *phy) -{ - clk_disable(phy->wkupclk); - if (!IS_ERR(phy->optclk)) - clk_disable(phy->optclk); - - return 0; -} - -static int omap_usb2_enable_clocks(struct omap_usb *phy) -{ - int ret; - - ret = clk_enable(phy->wkupclk); - if (ret < 0) { - dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); - goto err0; - } - - if (!IS_ERR(phy->optclk)) { - ret = clk_enable(phy->optclk); - if (ret < 0) { - dev_err(phy->dev, "Failed to enable optclk %d\n", ret); - goto err1; - } - } - - return 0; - -err1: - clk_disable(phy->wkupclk); - -err0: - return ret; -} - -static int omap_usb_init(struct phy *x) -{ - struct omap_usb *phy = phy_get_drvdata(x); - u32 val; - - omap_usb2_enable_clocks(phy); - - if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) { - /* - * - * Reduce the sensitivity of internal PHY by enabling the - * DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This - * resolves issues with certain devices which can otherwise - * be prone to false disconnects. - * - */ - val = omap_usb_readl(phy->phy_base, USB2PHY_ANA_CONFIG1); - val |= USB2PHY_DISCON_BYP_LATCH; - omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val); - } - - return 0; -} - -static int omap_usb_exit(struct phy *x) -{ - struct omap_usb *phy = phy_get_drvdata(x); - - return omap_usb2_disable_clocks(phy); -} - -static const struct phy_ops ops = { - .init = omap_usb_init, - .exit = omap_usb_exit, - .power_on = omap_usb_power_on, - .power_off = omap_usb_power_off, - .owner = THIS_MODULE, -}; - -static const struct usb_phy_data omap_usb2_data = { - .label = "omap_usb2", - .flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS, - .mask = OMAP_DEV_PHY_PD, - .power_off = OMAP_DEV_PHY_PD, -}; - -static const struct usb_phy_data omap5_usb2_data = { - .label = "omap5_usb2", - .flags = 0, - .mask = OMAP_DEV_PHY_PD, - .power_off = OMAP_DEV_PHY_PD, -}; - -static const struct usb_phy_data dra7x_usb2_data = { - .label = "dra7x_usb2", - .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT, - .mask = OMAP_DEV_PHY_PD, - .power_off = OMAP_DEV_PHY_PD, -}; - -static const struct usb_phy_data dra7x_usb2_phy2_data = { - .label = "dra7x_usb2_phy2", - .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT, - .mask = OMAP_USB2_PHY_PD, - .power_off = OMAP_USB2_PHY_PD, -}; - -static const struct usb_phy_data am437x_usb2_data = { - .label = "am437x_usb2", - .flags = 0, - .mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD | - AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN, - .power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN, - .power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD, -}; - -static const struct of_device_id omap_usb2_id_table[] = { - { - .compatible = "ti,omap-usb2", - .data = &omap_usb2_data, - }, - { - .compatible = "ti,omap5-usb2", - .data = &omap5_usb2_data, - }, - { - .compatible = "ti,dra7x-usb2", - .data = &dra7x_usb2_data, - }, - { - .compatible = "ti,dra7x-usb2-phy2", - .data = &dra7x_usb2_phy2_data, - }, - { - .compatible = "ti,am437x-usb2", - .data = &am437x_usb2_data, - }, - {}, -}; -MODULE_DEVICE_TABLE(of, omap_usb2_id_table); - -static int omap_usb2_probe(struct platform_device *pdev) -{ - struct omap_usb *phy; - struct phy *generic_phy; - struct resource *res; - struct phy_provider *phy_provider; - struct usb_otg *otg; - struct device_node *node = pdev->dev.of_node; - struct device_node *control_node; - struct platform_device *control_pdev; - const struct of_device_id *of_id; - struct usb_phy_data *phy_data; - - of_id = of_match_device(omap_usb2_id_table, &pdev->dev); - - if (!of_id) - return -EINVAL; - - phy_data = (struct usb_phy_data *)of_id->data; - - phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); - if (!phy) - return -ENOMEM; - - otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); - if (!otg) - return -ENOMEM; - - phy->dev = &pdev->dev; - - phy->phy.dev = phy->dev; - phy->phy.label = phy_data->label; - phy->phy.otg = otg; - phy->phy.type = USB_PHY_TYPE_USB2; - phy->mask = phy_data->mask; - phy->power_on = phy_data->power_on; - phy->power_off = phy_data->power_off; - - if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy->phy_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(phy->phy_base)) - return PTR_ERR(phy->phy_base); - phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT; - } - - phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node, - "syscon-phy-power"); - if (IS_ERR(phy->syscon_phy_power)) { - dev_dbg(&pdev->dev, - "can't get syscon-phy-power, using control device\n"); - phy->syscon_phy_power = NULL; - - control_node = of_parse_phandle(node, "ctrl-module", 0); - if (!control_node) { - dev_err(&pdev->dev, - "Failed to get control device phandle\n"); - return -EINVAL; - } - - control_pdev = of_find_device_by_node(control_node); - if (!control_pdev) { - dev_err(&pdev->dev, "Failed to get control device\n"); - return -EINVAL; - } - phy->control_dev = &control_pdev->dev; - } else { - if (of_property_read_u32_index(node, - "syscon-phy-power", 1, - &phy->power_reg)) { - dev_err(&pdev->dev, - "couldn't get power reg. offset\n"); - return -EINVAL; - } - } - - otg->set_host = omap_usb_set_host; - otg->set_peripheral = omap_usb_set_peripheral; - if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS) - otg->set_vbus = omap_usb_set_vbus; - if (phy_data->flags & OMAP_USB2_HAS_START_SRP) - otg->start_srp = omap_usb_start_srp; - otg->usb_phy = &phy->phy; - - platform_set_drvdata(pdev, phy); - pm_runtime_enable(phy->dev); - - generic_phy = devm_phy_create(phy->dev, NULL, &ops); - if (IS_ERR(generic_phy)) { - pm_runtime_disable(phy->dev); - return PTR_ERR(generic_phy); - } - - phy_set_drvdata(generic_phy, phy); - omap_usb_power_off(generic_phy); - - phy_provider = devm_of_phy_provider_register(phy->dev, - of_phy_simple_xlate); - if (IS_ERR(phy_provider)) { - pm_runtime_disable(phy->dev); - return PTR_ERR(phy_provider); - } - - phy->wkupclk = devm_clk_get(phy->dev, "wkupclk"); - if (IS_ERR(phy->wkupclk)) { - dev_warn(&pdev->dev, "unable to get wkupclk, trying old name\n"); - phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); - if (IS_ERR(phy->wkupclk)) { - dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); - pm_runtime_disable(phy->dev); - return PTR_ERR(phy->wkupclk); - } else { - dev_warn(&pdev->dev, - "found usb_phy_cm_clk32k, please fix DTS\n"); - } - } - clk_prepare(phy->wkupclk); - - phy->optclk = devm_clk_get(phy->dev, "refclk"); - if (IS_ERR(phy->optclk)) { - dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n"); - phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); - if (IS_ERR(phy->optclk)) { - dev_dbg(&pdev->dev, - "unable to get usb_otg_ss_refclk960m\n"); - } else { - dev_warn(&pdev->dev, - "found usb_otg_ss_refclk960m, please fix DTS\n"); - } - } - - if (!IS_ERR(phy->optclk)) - clk_prepare(phy->optclk); - - usb_add_phy_dev(&phy->phy); - - return 0; -} - -static int omap_usb2_remove(struct platform_device *pdev) -{ - struct omap_usb *phy = platform_get_drvdata(pdev); - - clk_unprepare(phy->wkupclk); - if (!IS_ERR(phy->optclk)) - clk_unprepare(phy->optclk); - usb_remove_phy(&phy->phy); - pm_runtime_disable(phy->dev); - - return 0; -} - -static struct platform_driver omap_usb2_driver = { - .probe = omap_usb2_probe, - .remove = omap_usb2_remove, - .driver = { - .name = "omap-usb2", - .of_match_table = omap_usb2_id_table, - }, -}; - -module_platform_driver(omap_usb2_driver); - -MODULE_ALIAS("platform:omap_usb2"); -MODULE_AUTHOR("Texas Instruments Inc."); -MODULE_DESCRIPTION("OMAP USB2 phy driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-pxa-28nm-hsic.c b/drivers/phy/phy-pxa-28nm-hsic.c deleted file mode 100644 index 234aacf4db20..000000000000 --- a/drivers/phy/phy-pxa-28nm-hsic.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2015 Linaro, Ltd. - * Rob Herring - * - * Based on vendor driver: - * Copyright (C) 2013 Marvell Inc. - * Author: Chao Xie - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PHY_28NM_HSIC_CTRL 0x08 -#define PHY_28NM_HSIC_IMPCAL_CAL 0x18 -#define PHY_28NM_HSIC_PLL_CTRL01 0x1c -#define PHY_28NM_HSIC_PLL_CTRL2 0x20 -#define PHY_28NM_HSIC_INT 0x28 - -#define PHY_28NM_HSIC_PLL_SELLPFR_SHIFT 26 -#define PHY_28NM_HSIC_PLL_FBDIV_SHIFT 0 -#define PHY_28NM_HSIC_PLL_REFDIV_SHIFT 9 - -#define PHY_28NM_HSIC_S2H_PU_PLL BIT(10) -#define PHY_28NM_HSIC_H2S_PLL_LOCK BIT(15) -#define PHY_28NM_HSIC_S2H_HSIC_EN BIT(7) -#define S2H_DRV_SE0_4RESUME BIT(14) -#define PHY_28NM_HSIC_H2S_IMPCAL_DONE BIT(27) - -#define PHY_28NM_HSIC_CONNECT_INT BIT(1) -#define PHY_28NM_HSIC_HS_READY_INT BIT(2) - -struct mv_hsic_phy { - struct phy *phy; - struct platform_device *pdev; - void __iomem *base; - struct clk *clk; -}; - -static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) -{ - timeout += jiffies; - while (time_is_after_eq_jiffies(timeout)) { - if ((readl(reg) & mask) == mask) - return true; - msleep(1); - } - return false; -} - -static int mv_hsic_phy_init(struct phy *phy) -{ - struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); - struct platform_device *pdev = mv_phy->pdev; - void __iomem *base = mv_phy->base; - - clk_prepare_enable(mv_phy->clk); - - /* Set reference clock */ - writel(0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT | - 0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT | - 0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT, - base + PHY_28NM_HSIC_PLL_CTRL01); - - /* Turn on PLL */ - writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) | - PHY_28NM_HSIC_S2H_PU_PLL, - base + PHY_28NM_HSIC_PLL_CTRL2); - - /* Make sure PHY PLL is locked */ - if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2, - PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) { - dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS."); - clk_disable_unprepare(mv_phy->clk); - return -ETIMEDOUT; - } - - return 0; -} - -static int mv_hsic_phy_power_on(struct phy *phy) -{ - struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); - struct platform_device *pdev = mv_phy->pdev; - void __iomem *base = mv_phy->base; - u32 reg; - - reg = readl(base + PHY_28NM_HSIC_CTRL); - /* Avoid SE0 state when resume for some device will take it as reset */ - reg &= ~S2H_DRV_SE0_4RESUME; - reg |= PHY_28NM_HSIC_S2H_HSIC_EN; /* Enable HSIC PHY */ - writel(reg, base + PHY_28NM_HSIC_CTRL); - - /* - * Calibration Timing - * ____________________________ - * CAL START ___| - * ____________________ - * CAL_DONE ___________| - * | 400us | - */ - - /* Make sure PHY Calibration is ready */ - if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL, - PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) { - dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS."); - return -ETIMEDOUT; - } - - /* Waiting for HSIC connect int*/ - if (!wait_for_reg(base + PHY_28NM_HSIC_INT, - PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) { - dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout."); - return -ETIMEDOUT; - } - - return 0; -} - -static int mv_hsic_phy_power_off(struct phy *phy) -{ - struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); - void __iomem *base = mv_phy->base; - - writel(readl(base + PHY_28NM_HSIC_CTRL) & ~PHY_28NM_HSIC_S2H_HSIC_EN, - base + PHY_28NM_HSIC_CTRL); - - return 0; -} - -static int mv_hsic_phy_exit(struct phy *phy) -{ - struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); - void __iomem *base = mv_phy->base; - - /* Turn off PLL */ - writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) & - ~PHY_28NM_HSIC_S2H_PU_PLL, - base + PHY_28NM_HSIC_PLL_CTRL2); - - clk_disable_unprepare(mv_phy->clk); - return 0; -} - - -static const struct phy_ops hsic_ops = { - .init = mv_hsic_phy_init, - .power_on = mv_hsic_phy_power_on, - .power_off = mv_hsic_phy_power_off, - .exit = mv_hsic_phy_exit, - .owner = THIS_MODULE, -}; - -static int mv_hsic_phy_probe(struct platform_device *pdev) -{ - struct phy_provider *phy_provider; - struct mv_hsic_phy *mv_phy; - struct resource *r; - - mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); - if (!mv_phy) - return -ENOMEM; - - mv_phy->pdev = pdev; - - mv_phy->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(mv_phy->clk)) { - dev_err(&pdev->dev, "failed to get clock.\n"); - return PTR_ERR(mv_phy->clk); - } - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mv_phy->base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(mv_phy->base)) - return PTR_ERR(mv_phy->base); - - mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &hsic_ops); - if (IS_ERR(mv_phy->phy)) - return PTR_ERR(mv_phy->phy); - - phy_set_drvdata(mv_phy->phy, mv_phy); - - phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id mv_hsic_phy_dt_match[] = { - { .compatible = "marvell,pxa1928-hsic-phy", }, - {}, -}; -MODULE_DEVICE_TABLE(of, mv_hsic_phy_dt_match); - -static struct platform_driver mv_hsic_phy_driver = { - .probe = mv_hsic_phy_probe, - .driver = { - .name = "mv-hsic-phy", - .of_match_table = of_match_ptr(mv_hsic_phy_dt_match), - }, -}; -module_platform_driver(mv_hsic_phy_driver); - -MODULE_AUTHOR("Rob Herring "); -MODULE_DESCRIPTION("Marvell HSIC phy driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-pxa-28nm-usb2.c b/drivers/phy/phy-pxa-28nm-usb2.c deleted file mode 100644 index 37e9c8ca4983..000000000000 --- a/drivers/phy/phy-pxa-28nm-usb2.c +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright (C) 2015 Linaro, Ltd. - * Rob Herring - * - * Based on vendor driver: - * Copyright (C) 2013 Marvell Inc. - * Author: Chao Xie - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* USB PXA1928 PHY mapping */ -#define PHY_28NM_PLL_REG0 0x0 -#define PHY_28NM_PLL_REG1 0x4 -#define PHY_28NM_CAL_REG 0x8 -#define PHY_28NM_TX_REG0 0x0c -#define PHY_28NM_TX_REG1 0x10 -#define PHY_28NM_RX_REG0 0x14 -#define PHY_28NM_RX_REG1 0x18 -#define PHY_28NM_DIG_REG0 0x1c -#define PHY_28NM_DIG_REG1 0x20 -#define PHY_28NM_TEST_REG0 0x24 -#define PHY_28NM_TEST_REG1 0x28 -#define PHY_28NM_MOC_REG 0x2c -#define PHY_28NM_PHY_RESERVE 0x30 -#define PHY_28NM_OTG_REG 0x34 -#define PHY_28NM_CHRG_DET 0x38 -#define PHY_28NM_CTRL_REG0 0xc4 -#define PHY_28NM_CTRL_REG1 0xc8 -#define PHY_28NM_CTRL_REG2 0xd4 -#define PHY_28NM_CTRL_REG3 0xdc - -/* PHY_28NM_PLL_REG0 */ -#define PHY_28NM_PLL_READY BIT(31) - -#define PHY_28NM_PLL_SELLPFR_SHIFT 28 -#define PHY_28NM_PLL_SELLPFR_MASK (0x3 << 28) - -#define PHY_28NM_PLL_FBDIV_SHIFT 16 -#define PHY_28NM_PLL_FBDIV_MASK (0x1ff << 16) - -#define PHY_28NM_PLL_ICP_SHIFT 8 -#define PHY_28NM_PLL_ICP_MASK (0x7 << 8) - -#define PHY_28NM_PLL_REFDIV_SHIFT 0 -#define PHY_28NM_PLL_REFDIV_MASK 0x7f - -/* PHY_28NM_PLL_REG1 */ -#define PHY_28NM_PLL_PU_BY_REG BIT(1) - -#define PHY_28NM_PLL_PU_PLL BIT(0) - -/* PHY_28NM_CAL_REG */ -#define PHY_28NM_PLL_PLLCAL_DONE BIT(31) - -#define PHY_28NM_PLL_IMPCAL_DONE BIT(23) - -#define PHY_28NM_PLL_KVCO_SHIFT 16 -#define PHY_28NM_PLL_KVCO_MASK (0x7 << 16) - -#define PHY_28NM_PLL_CAL12_SHIFT 20 -#define PHY_28NM_PLL_CAL12_MASK (0x3 << 20) - -#define PHY_28NM_IMPCAL_VTH_SHIFT 8 -#define PHY_28NM_IMPCAL_VTH_MASK (0x7 << 8) - -#define PHY_28NM_PLLCAL_START_SHIFT 22 -#define PHY_28NM_IMPCAL_START_SHIFT 13 - -/* PHY_28NM_TX_REG0 */ -#define PHY_28NM_TX_PU_BY_REG BIT(25) - -#define PHY_28NM_TX_PU_ANA BIT(24) - -#define PHY_28NM_TX_AMP_SHIFT 20 -#define PHY_28NM_TX_AMP_MASK (0x7 << 20) - -/* PHY_28NM_RX_REG0 */ -#define PHY_28NM_RX_SQ_THRESH_SHIFT 0 -#define PHY_28NM_RX_SQ_THRESH_MASK (0xf << 0) - -/* PHY_28NM_RX_REG1 */ -#define PHY_28NM_RX_SQCAL_DONE BIT(31) - -/* PHY_28NM_DIG_REG0 */ -#define PHY_28NM_DIG_BITSTAFFING_ERR BIT(31) -#define PHY_28NM_DIG_SYNC_ERR BIT(30) - -#define PHY_28NM_DIG_SQ_FILT_SHIFT 16 -#define PHY_28NM_DIG_SQ_FILT_MASK (0x7 << 16) - -#define PHY_28NM_DIG_SQ_BLK_SHIFT 12 -#define PHY_28NM_DIG_SQ_BLK_MASK (0x7 << 12) - -#define PHY_28NM_DIG_SYNC_NUM_SHIFT 0 -#define PHY_28NM_DIG_SYNC_NUM_MASK (0x3 << 0) - -#define PHY_28NM_PLL_LOCK_BYPASS BIT(7) - -/* PHY_28NM_OTG_REG */ -#define PHY_28NM_OTG_CONTROL_BY_PIN BIT(5) -#define PHY_28NM_OTG_PU_OTG BIT(4) - -#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DM_SHIFT_28 13 -#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DP_SHIFT_28 12 -#define PHY_28NM_CHGDTC_VSRC_CHARGE_SHIFT_28 10 -#define PHY_28NM_CHGDTC_VDAT_CHARGE_SHIFT_28 8 -#define PHY_28NM_CHGDTC_CDP_DM_AUTO_SWITCH_SHIFT_28 7 -#define PHY_28NM_CHGDTC_DP_DM_SWAP_SHIFT_28 6 -#define PHY_28NM_CHGDTC_PU_CHRG_DTC_SHIFT_28 5 -#define PHY_28NM_CHGDTC_PD_EN_SHIFT_28 4 -#define PHY_28NM_CHGDTC_DCP_EN_SHIFT_28 3 -#define PHY_28NM_CHGDTC_CDP_EN_SHIFT_28 2 -#define PHY_28NM_CHGDTC_TESTMON_CHRGDTC_SHIFT_28 0 - -#define PHY_28NM_CTRL1_CHRG_DTC_OUT_SHIFT_28 4 -#define PHY_28NM_CTRL1_VBUSDTC_OUT_SHIFT_28 2 - -#define PHY_28NM_CTRL3_OVERWRITE BIT(0) -#define PHY_28NM_CTRL3_VBUS_VALID BIT(4) -#define PHY_28NM_CTRL3_AVALID BIT(5) -#define PHY_28NM_CTRL3_BVALID BIT(6) - -struct mv_usb2_phy { - struct phy *phy; - struct platform_device *pdev; - void __iomem *base; - struct clk *clk; -}; - -static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) -{ - timeout += jiffies; - while (time_is_after_eq_jiffies(timeout)) { - if ((readl(reg) & mask) == mask) - return true; - msleep(1); - } - return false; -} - -static int mv_usb2_phy_28nm_init(struct phy *phy) -{ - struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); - struct platform_device *pdev = mv_phy->pdev; - void __iomem *base = mv_phy->base; - u32 reg; - int ret; - - clk_prepare_enable(mv_phy->clk); - - /* PHY_28NM_PLL_REG0 */ - reg = readl(base + PHY_28NM_PLL_REG0) & - ~(PHY_28NM_PLL_SELLPFR_MASK | PHY_28NM_PLL_FBDIV_MASK - | PHY_28NM_PLL_ICP_MASK | PHY_28NM_PLL_REFDIV_MASK); - writel(reg | (0x1 << PHY_28NM_PLL_SELLPFR_SHIFT - | 0xf0 << PHY_28NM_PLL_FBDIV_SHIFT - | 0x3 << PHY_28NM_PLL_ICP_SHIFT - | 0xd << PHY_28NM_PLL_REFDIV_SHIFT), - base + PHY_28NM_PLL_REG0); - - /* PHY_28NM_PLL_REG1 */ - reg = readl(base + PHY_28NM_PLL_REG1); - writel(reg | PHY_28NM_PLL_PU_PLL | PHY_28NM_PLL_PU_BY_REG, - base + PHY_28NM_PLL_REG1); - - /* PHY_28NM_TX_REG0 */ - reg = readl(base + PHY_28NM_TX_REG0) & ~PHY_28NM_TX_AMP_MASK; - writel(reg | PHY_28NM_TX_PU_BY_REG | 0x3 << PHY_28NM_TX_AMP_SHIFT | - PHY_28NM_TX_PU_ANA, - base + PHY_28NM_TX_REG0); - - /* PHY_28NM_RX_REG0 */ - reg = readl(base + PHY_28NM_RX_REG0) & ~PHY_28NM_RX_SQ_THRESH_MASK; - writel(reg | 0xa << PHY_28NM_RX_SQ_THRESH_SHIFT, - base + PHY_28NM_RX_REG0); - - /* PHY_28NM_DIG_REG0 */ - reg = readl(base + PHY_28NM_DIG_REG0) & - ~(PHY_28NM_DIG_BITSTAFFING_ERR | PHY_28NM_DIG_SYNC_ERR | - PHY_28NM_DIG_SQ_FILT_MASK | PHY_28NM_DIG_SQ_BLK_MASK | - PHY_28NM_DIG_SYNC_NUM_MASK); - writel(reg | (0x1 << PHY_28NM_DIG_SYNC_NUM_SHIFT | - PHY_28NM_PLL_LOCK_BYPASS), - base + PHY_28NM_DIG_REG0); - - /* PHY_28NM_OTG_REG */ - reg = readl(base + PHY_28NM_OTG_REG) | PHY_28NM_OTG_PU_OTG; - writel(reg & ~PHY_28NM_OTG_CONTROL_BY_PIN, base + PHY_28NM_OTG_REG); - - /* - * Calibration Timing - * ____________________________ - * CAL START ___| - * ____________________ - * CAL_DONE ___________| - * | 400us | - */ - - /* Make sure PHY Calibration is ready */ - if (!wait_for_reg(base + PHY_28NM_CAL_REG, - PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE, - HZ / 10)) { - dev_warn(&pdev->dev, "USB PHY PLL calibrate not done after 100mS."); - ret = -ETIMEDOUT; - goto err_clk; - } - if (!wait_for_reg(base + PHY_28NM_RX_REG1, - PHY_28NM_RX_SQCAL_DONE, HZ / 10)) { - dev_warn(&pdev->dev, "USB PHY RX SQ calibrate not done after 100mS."); - ret = -ETIMEDOUT; - goto err_clk; - } - /* Make sure PHY PLL is ready */ - if (!wait_for_reg(base + PHY_28NM_PLL_REG0, - PHY_28NM_PLL_READY, HZ / 10)) { - dev_warn(&pdev->dev, "PLL_READY not set after 100mS."); - ret = -ETIMEDOUT; - goto err_clk; - } - - return 0; -err_clk: - clk_disable_unprepare(mv_phy->clk); - return ret; -} - -static int mv_usb2_phy_28nm_power_on(struct phy *phy) -{ - struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); - void __iomem *base = mv_phy->base; - - writel(readl(base + PHY_28NM_CTRL_REG3) | - (PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID | - PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID), - base + PHY_28NM_CTRL_REG3); - - return 0; -} - -static int mv_usb2_phy_28nm_power_off(struct phy *phy) -{ - struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); - void __iomem *base = mv_phy->base; - - writel(readl(base + PHY_28NM_CTRL_REG3) | - ~(PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID - | PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID), - base + PHY_28NM_CTRL_REG3); - - return 0; -} - -static int mv_usb2_phy_28nm_exit(struct phy *phy) -{ - struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); - void __iomem *base = mv_phy->base; - unsigned int val; - - val = readw(base + PHY_28NM_PLL_REG1); - val &= ~PHY_28NM_PLL_PU_PLL; - writew(val, base + PHY_28NM_PLL_REG1); - - /* power down PHY Analog part */ - val = readw(base + PHY_28NM_TX_REG0); - val &= ~PHY_28NM_TX_PU_ANA; - writew(val, base + PHY_28NM_TX_REG0); - - /* power down PHY OTG part */ - val = readw(base + PHY_28NM_OTG_REG); - val &= ~PHY_28NM_OTG_PU_OTG; - writew(val, base + PHY_28NM_OTG_REG); - - clk_disable_unprepare(mv_phy->clk); - return 0; -} - -static const struct phy_ops usb_ops = { - .init = mv_usb2_phy_28nm_init, - .power_on = mv_usb2_phy_28nm_power_on, - .power_off = mv_usb2_phy_28nm_power_off, - .exit = mv_usb2_phy_28nm_exit, - .owner = THIS_MODULE, -}; - -static int mv_usb2_phy_probe(struct platform_device *pdev) -{ - struct phy_provider *phy_provider; - struct mv_usb2_phy *mv_phy; - struct resource *r; - - mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); - if (!mv_phy) - return -ENOMEM; - - mv_phy->pdev = pdev; - - mv_phy->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(mv_phy->clk)) { - dev_err(&pdev->dev, "failed to get clock.\n"); - return PTR_ERR(mv_phy->clk); - } - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mv_phy->base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(mv_phy->base)) - return PTR_ERR(mv_phy->base); - - mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &usb_ops); - if (IS_ERR(mv_phy->phy)) - return PTR_ERR(mv_phy->phy); - - phy_set_drvdata(mv_phy->phy, mv_phy); - - phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id mv_usbphy_dt_match[] = { - { .compatible = "marvell,pxa1928-usb-phy", }, - {}, -}; -MODULE_DEVICE_TABLE(of, mv_usbphy_dt_match); - -static struct platform_driver mv_usb2_phy_driver = { - .probe = mv_usb2_phy_probe, - .driver = { - .name = "mv-usb2-phy", - .of_match_table = of_match_ptr(mv_usbphy_dt_match), - }, -}; -module_platform_driver(mv_usb2_phy_driver); - -MODULE_AUTHOR("Rob Herring "); -MODULE_DESCRIPTION("Marvell USB2 phy driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-apq8064-sata.c b/drivers/phy/phy-qcom-apq8064-sata.c deleted file mode 100644 index 69ce2afac015..000000000000 --- a/drivers/phy/phy-qcom-apq8064-sata.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (c) 2014, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* PHY registers */ -#define UNIPHY_PLL_REFCLK_CFG 0x000 -#define UNIPHY_PLL_PWRGEN_CFG 0x014 -#define UNIPHY_PLL_GLB_CFG 0x020 -#define UNIPHY_PLL_SDM_CFG0 0x038 -#define UNIPHY_PLL_SDM_CFG1 0x03C -#define UNIPHY_PLL_SDM_CFG2 0x040 -#define UNIPHY_PLL_SDM_CFG3 0x044 -#define UNIPHY_PLL_SDM_CFG4 0x048 -#define UNIPHY_PLL_SSC_CFG0 0x04C -#define UNIPHY_PLL_SSC_CFG1 0x050 -#define UNIPHY_PLL_SSC_CFG2 0x054 -#define UNIPHY_PLL_SSC_CFG3 0x058 -#define UNIPHY_PLL_LKDET_CFG0 0x05C -#define UNIPHY_PLL_LKDET_CFG1 0x060 -#define UNIPHY_PLL_LKDET_CFG2 0x064 -#define UNIPHY_PLL_CAL_CFG0 0x06C -#define UNIPHY_PLL_CAL_CFG8 0x08C -#define UNIPHY_PLL_CAL_CFG9 0x090 -#define UNIPHY_PLL_CAL_CFG10 0x094 -#define UNIPHY_PLL_CAL_CFG11 0x098 -#define UNIPHY_PLL_STATUS 0x0C0 - -#define SATA_PHY_SER_CTRL 0x100 -#define SATA_PHY_TX_DRIV_CTRL0 0x104 -#define SATA_PHY_TX_DRIV_CTRL1 0x108 -#define SATA_PHY_TX_IMCAL0 0x11C -#define SATA_PHY_TX_IMCAL2 0x124 -#define SATA_PHY_RX_IMCAL0 0x128 -#define SATA_PHY_EQUAL 0x13C -#define SATA_PHY_OOB_TERM 0x144 -#define SATA_PHY_CDR_CTRL0 0x148 -#define SATA_PHY_CDR_CTRL1 0x14C -#define SATA_PHY_CDR_CTRL2 0x150 -#define SATA_PHY_CDR_CTRL3 0x154 -#define SATA_PHY_PI_CTRL0 0x168 -#define SATA_PHY_POW_DWN_CTRL0 0x180 -#define SATA_PHY_POW_DWN_CTRL1 0x184 -#define SATA_PHY_TX_DATA_CTRL 0x188 -#define SATA_PHY_ALIGNP 0x1A4 -#define SATA_PHY_TX_IMCAL_STAT 0x1E4 -#define SATA_PHY_RX_IMCAL_STAT 0x1E8 - -#define UNIPHY_PLL_LOCK BIT(0) -#define SATA_PHY_TX_CAL BIT(0) -#define SATA_PHY_RX_CAL BIT(0) - -/* default timeout set to 1 sec */ -#define TIMEOUT_MS 10000 -#define DELAY_INTERVAL_US 100 - -struct qcom_apq8064_sata_phy { - void __iomem *mmio; - struct clk *cfg_clk; - struct device *dev; -}; - -/* Helper function to do poll and timeout */ -static int read_poll_timeout(void __iomem *addr, u32 mask) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS); - - do { - if (readl_relaxed(addr) & mask) - return 0; - - usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); - } while (!time_after(jiffies, timeout)); - - return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT; -} - -static int qcom_apq8064_sata_phy_init(struct phy *generic_phy) -{ - struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy); - void __iomem *base = phy->mmio; - int ret = 0; - - /* SATA phy initialization */ - writel_relaxed(0x01, base + SATA_PHY_SER_CTRL); - writel_relaxed(0xB1, base + SATA_PHY_POW_DWN_CTRL0); - /* Make sure the power down happens before power up */ - mb(); - usleep_range(10, 60); - - writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0); - writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1); - writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0); - writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0); - writel_relaxed(0x02, base + SATA_PHY_TX_IMCAL2); - - /* Write UNIPHYPLL registers to configure PLL */ - writel_relaxed(0x04, base + UNIPHY_PLL_REFCLK_CFG); - writel_relaxed(0x00, base + UNIPHY_PLL_PWRGEN_CFG); - - writel_relaxed(0x0A, base + UNIPHY_PLL_CAL_CFG0); - writel_relaxed(0xF3, base + UNIPHY_PLL_CAL_CFG8); - writel_relaxed(0x01, base + UNIPHY_PLL_CAL_CFG9); - writel_relaxed(0xED, base + UNIPHY_PLL_CAL_CFG10); - writel_relaxed(0x02, base + UNIPHY_PLL_CAL_CFG11); - - writel_relaxed(0x36, base + UNIPHY_PLL_SDM_CFG0); - writel_relaxed(0x0D, base + UNIPHY_PLL_SDM_CFG1); - writel_relaxed(0xA3, base + UNIPHY_PLL_SDM_CFG2); - writel_relaxed(0xF0, base + UNIPHY_PLL_SDM_CFG3); - writel_relaxed(0x00, base + UNIPHY_PLL_SDM_CFG4); - - writel_relaxed(0x19, base + UNIPHY_PLL_SSC_CFG0); - writel_relaxed(0xE1, base + UNIPHY_PLL_SSC_CFG1); - writel_relaxed(0x00, base + UNIPHY_PLL_SSC_CFG2); - writel_relaxed(0x11, base + UNIPHY_PLL_SSC_CFG3); - - writel_relaxed(0x04, base + UNIPHY_PLL_LKDET_CFG0); - writel_relaxed(0xFF, base + UNIPHY_PLL_LKDET_CFG1); - - writel_relaxed(0x02, base + UNIPHY_PLL_GLB_CFG); - /* make sure global config LDO power down happens before power up */ - mb(); - - writel_relaxed(0x03, base + UNIPHY_PLL_GLB_CFG); - writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2); - - /* PLL Lock wait */ - ret = read_poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK); - if (ret) { - dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n"); - return ret; - } - - /* TX Calibration */ - ret = read_poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL); - if (ret) { - dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n"); - return ret; - } - - /* RX Calibration */ - ret = read_poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL); - if (ret) { - dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n"); - return ret; - } - - /* SATA phy calibrated succesfully, power up to functional mode */ - writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1); - writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0); - writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0); - - writel_relaxed(0x00, base + SATA_PHY_POW_DWN_CTRL1); - writel_relaxed(0x59, base + SATA_PHY_CDR_CTRL0); - writel_relaxed(0x04, base + SATA_PHY_CDR_CTRL1); - writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL2); - writel_relaxed(0x00, base + SATA_PHY_PI_CTRL0); - writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL3); - writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0); - - writel_relaxed(0x11, base + SATA_PHY_TX_DATA_CTRL); - writel_relaxed(0x43, base + SATA_PHY_ALIGNP); - writel_relaxed(0x04, base + SATA_PHY_OOB_TERM); - - writel_relaxed(0x01, base + SATA_PHY_EQUAL); - writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL0); - writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL1); - - return 0; -} - -static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy) -{ - struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy); - void __iomem *base = phy->mmio; - - /* Power down PHY */ - writel_relaxed(0xF8, base + SATA_PHY_POW_DWN_CTRL0); - writel_relaxed(0xFE, base + SATA_PHY_POW_DWN_CTRL1); - - /* Power down PLL block */ - writel_relaxed(0x00, base + UNIPHY_PLL_GLB_CFG); - - return 0; -} - -static const struct phy_ops qcom_apq8064_sata_phy_ops = { - .init = qcom_apq8064_sata_phy_init, - .exit = qcom_apq8064_sata_phy_exit, - .owner = THIS_MODULE, -}; - -static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev) -{ - struct qcom_apq8064_sata_phy *phy; - struct device *dev = &pdev->dev; - struct resource *res; - struct phy_provider *phy_provider; - struct phy *generic_phy; - int ret; - - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy->mmio = devm_ioremap_resource(dev, res); - if (IS_ERR(phy->mmio)) - return PTR_ERR(phy->mmio); - - generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops); - if (IS_ERR(generic_phy)) { - dev_err(dev, "%s: failed to create phy\n", __func__); - return PTR_ERR(generic_phy); - } - - phy->dev = dev; - phy_set_drvdata(generic_phy, phy); - platform_set_drvdata(pdev, phy); - - phy->cfg_clk = devm_clk_get(dev, "cfg"); - if (IS_ERR(phy->cfg_clk)) { - dev_err(dev, "Failed to get sata cfg clock\n"); - return PTR_ERR(phy->cfg_clk); - } - - ret = clk_prepare_enable(phy->cfg_clk); - if (ret) - return ret; - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) { - clk_disable_unprepare(phy->cfg_clk); - dev_err(dev, "%s: failed to register phy\n", __func__); - return PTR_ERR(phy_provider); - } - - return 0; -} - -static int qcom_apq8064_sata_phy_remove(struct platform_device *pdev) -{ - struct qcom_apq8064_sata_phy *phy = platform_get_drvdata(pdev); - - clk_disable_unprepare(phy->cfg_clk); - - return 0; -} - -static const struct of_device_id qcom_apq8064_sata_phy_of_match[] = { - { .compatible = "qcom,apq8064-sata-phy" }, - { }, -}; -MODULE_DEVICE_TABLE(of, qcom_apq8064_sata_phy_of_match); - -static struct platform_driver qcom_apq8064_sata_phy_driver = { - .probe = qcom_apq8064_sata_phy_probe, - .remove = qcom_apq8064_sata_phy_remove, - .driver = { - .name = "qcom-apq8064-sata-phy", - .of_match_table = qcom_apq8064_sata_phy_of_match, - } -}; -module_platform_driver(qcom_apq8064_sata_phy_driver); - -MODULE_DESCRIPTION("QCOM apq8064 SATA PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-ipq806x-sata.c b/drivers/phy/phy-qcom-ipq806x-sata.c deleted file mode 100644 index 0ad127cc9298..000000000000 --- a/drivers/phy/phy-qcom-ipq806x-sata.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (c) 2014, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct qcom_ipq806x_sata_phy { - void __iomem *mmio; - struct clk *cfg_clk; - struct device *dev; -}; - -#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b)) - -#define SATA_PHY_P0_PARAM0 0x200 -#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12) -#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12) -#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6) -#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6) -#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0) -#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0) - -#define SATA_PHY_P0_PARAM1 0x204 -#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21) -#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14) -#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14) -#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7) -#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7) -#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0) -#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0) - -#define SATA_PHY_P0_PARAM2 0x208 -#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18) -#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18) - -#define SATA_PHY_P0_PARAM3 0x20C -#define SATA_PHY_SSC_EN 0x8 -#define SATA_PHY_P0_PARAM4 0x210 -#define SATA_PHY_REF_SSP_EN 0x2 -#define SATA_PHY_RESET 0x1 - -static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy) -{ - struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); - u32 reg; - - /* Setting SSC_EN to 1 */ - reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3); - reg = reg | SATA_PHY_SSC_EN; - writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3); - - reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) & - ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK | - SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK | - SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK); - reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf); - writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0); - - reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) & - ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK | - SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK | - SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK); - reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) | - SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) | - SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55); - writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1); - - reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) & - ~SATA_PHY_P0_PARAM2_RX_EQ_MASK; - reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3); - writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2); - - /* Setting PHY_RESET to 1 */ - reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); - reg = reg | SATA_PHY_RESET; - writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); - - /* Setting REF_SSP_EN to 1 */ - reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); - reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET; - writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); - - /* make sure all changes complete before we let the PHY out of reset */ - mb(); - - /* sleep for max. 50us more to combine processor wakeups */ - usleep_range(20, 20 + 50); - - /* Clearing PHY_RESET to 0 */ - reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); - reg = reg & ~SATA_PHY_RESET; - writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); - - return 0; -} - -static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy) -{ - struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); - u32 reg; - - /* Setting PHY_RESET to 1 */ - reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); - reg = reg | SATA_PHY_RESET; - writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); - - return 0; -} - -static const struct phy_ops qcom_ipq806x_sata_phy_ops = { - .init = qcom_ipq806x_sata_phy_init, - .exit = qcom_ipq806x_sata_phy_exit, - .owner = THIS_MODULE, -}; - -static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev) -{ - struct qcom_ipq806x_sata_phy *phy; - struct device *dev = &pdev->dev; - struct resource *res; - struct phy_provider *phy_provider; - struct phy *generic_phy; - int ret; - - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy->mmio = devm_ioremap_resource(dev, res); - if (IS_ERR(phy->mmio)) - return PTR_ERR(phy->mmio); - - generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops); - if (IS_ERR(generic_phy)) { - dev_err(dev, "%s: failed to create phy\n", __func__); - return PTR_ERR(generic_phy); - } - - phy->dev = dev; - phy_set_drvdata(generic_phy, phy); - platform_set_drvdata(pdev, phy); - - phy->cfg_clk = devm_clk_get(dev, "cfg"); - if (IS_ERR(phy->cfg_clk)) { - dev_err(dev, "Failed to get sata cfg clock\n"); - return PTR_ERR(phy->cfg_clk); - } - - ret = clk_prepare_enable(phy->cfg_clk); - if (ret) - return ret; - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) { - clk_disable_unprepare(phy->cfg_clk); - dev_err(dev, "%s: failed to register phy\n", __func__); - return PTR_ERR(phy_provider); - } - - return 0; -} - -static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev) -{ - struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev); - - clk_disable_unprepare(phy->cfg_clk); - - return 0; -} - -static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = { - { .compatible = "qcom,ipq806x-sata-phy" }, - { }, -}; -MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match); - -static struct platform_driver qcom_ipq806x_sata_phy_driver = { - .probe = qcom_ipq806x_sata_phy_probe, - .remove = qcom_ipq806x_sata_phy_remove, - .driver = { - .name = "qcom-ipq806x-sata-phy", - .of_match_table = qcom_ipq806x_sata_phy_of_match, - } -}; -module_platform_driver(qcom_ipq806x_sata_phy_driver); - -MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-qmp.c b/drivers/phy/phy-qcom-qmp.c deleted file mode 100644 index 78ca62897784..000000000000 --- a/drivers/phy/phy-qcom-qmp.c +++ /dev/null @@ -1,1153 +0,0 @@ -/* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* QMP PHY QSERDES COM registers */ -#define QSERDES_COM_BG_TIMER 0x00c -#define QSERDES_COM_SSC_EN_CENTER 0x010 -#define QSERDES_COM_SSC_ADJ_PER1 0x014 -#define QSERDES_COM_SSC_ADJ_PER2 0x018 -#define QSERDES_COM_SSC_PER1 0x01c -#define QSERDES_COM_SSC_PER2 0x020 -#define QSERDES_COM_SSC_STEP_SIZE1 0x024 -#define QSERDES_COM_SSC_STEP_SIZE2 0x028 -#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x034 -#define QSERDES_COM_CLK_ENABLE1 0x038 -#define QSERDES_COM_SYS_CLK_CTRL 0x03c -#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x040 -#define QSERDES_COM_PLL_IVCO 0x048 -#define QSERDES_COM_LOCK_CMP1_MODE0 0x04c -#define QSERDES_COM_LOCK_CMP2_MODE0 0x050 -#define QSERDES_COM_LOCK_CMP3_MODE0 0x054 -#define QSERDES_COM_LOCK_CMP1_MODE1 0x058 -#define QSERDES_COM_LOCK_CMP2_MODE1 0x05c -#define QSERDES_COM_LOCK_CMP3_MODE1 0x060 -#define QSERDES_COM_BG_TRIM 0x070 -#define QSERDES_COM_CLK_EP_DIV 0x074 -#define QSERDES_COM_CP_CTRL_MODE0 0x078 -#define QSERDES_COM_CP_CTRL_MODE1 0x07c -#define QSERDES_COM_PLL_RCTRL_MODE0 0x084 -#define QSERDES_COM_PLL_RCTRL_MODE1 0x088 -#define QSERDES_COM_PLL_CCTRL_MODE0 0x090 -#define QSERDES_COM_PLL_CCTRL_MODE1 0x094 -#define QSERDES_COM_SYSCLK_EN_SEL 0x0ac -#define QSERDES_COM_RESETSM_CNTRL 0x0b4 -#define QSERDES_COM_RESTRIM_CTRL 0x0bc -#define QSERDES_COM_RESCODE_DIV_NUM 0x0c4 -#define QSERDES_COM_LOCK_CMP_EN 0x0c8 -#define QSERDES_COM_LOCK_CMP_CFG 0x0cc -#define QSERDES_COM_DEC_START_MODE0 0x0d0 -#define QSERDES_COM_DEC_START_MODE1 0x0d4 -#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x0dc -#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x0e0 -#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x0e4 -#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8 -#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec -#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0 -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108 -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110 -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x114 -#define QSERDES_COM_VCO_TUNE_CTRL 0x124 -#define QSERDES_COM_VCO_TUNE_MAP 0x128 -#define QSERDES_COM_VCO_TUNE1_MODE0 0x12c -#define QSERDES_COM_VCO_TUNE2_MODE0 0x130 -#define QSERDES_COM_VCO_TUNE1_MODE1 0x134 -#define QSERDES_COM_VCO_TUNE2_MODE1 0x138 -#define QSERDES_COM_VCO_TUNE_TIMER1 0x144 -#define QSERDES_COM_VCO_TUNE_TIMER2 0x148 -#define QSERDES_COM_BG_CTRL 0x170 -#define QSERDES_COM_CLK_SELECT 0x174 -#define QSERDES_COM_HSCLK_SEL 0x178 -#define QSERDES_COM_CORECLK_DIV 0x184 -#define QSERDES_COM_CORE_CLK_EN 0x18c -#define QSERDES_COM_C_READY_STATUS 0x190 -#define QSERDES_COM_CMN_CONFIG 0x194 -#define QSERDES_COM_SVS_MODE_CLK_SEL 0x19c -#define QSERDES_COM_DEBUG_BUS0 0x1a0 -#define QSERDES_COM_DEBUG_BUS1 0x1a4 -#define QSERDES_COM_DEBUG_BUS2 0x1a8 -#define QSERDES_COM_DEBUG_BUS3 0x1ac -#define QSERDES_COM_DEBUG_BUS_SEL 0x1b0 -#define QSERDES_COM_CORECLK_DIV_MODE1 0x1bc - -/* QMP PHY TX registers */ -#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x054 -#define QSERDES_TX_DEBUG_BUS_SEL 0x064 -#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x068 -#define QSERDES_TX_LANE_MODE 0x094 -#define QSERDES_TX_RCV_DETECT_LVL_2 0x0ac - -/* QMP PHY RX registers */ -#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x010 -#define QSERDES_RX_UCDR_SO_GAIN 0x01c -#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN 0x040 -#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x048 -#define QSERDES_RX_RX_TERM_BW 0x090 -#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x0c4 -#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x0c8 -#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x0cc -#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x0d0 -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d8 -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x0dc -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x0e0 -#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x108 -#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x10c -#define QSERDES_RX_SIGDET_ENABLES 0x110 -#define QSERDES_RX_SIGDET_CNTRL 0x114 -#define QSERDES_RX_SIGDET_LVL 0x118 -#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x11c -#define QSERDES_RX_RX_BAND 0x120 -#define QSERDES_RX_RX_INTERFACE_MODE 0x12c - -/* QMP PHY PCS registers */ -#define QPHY_POWER_DOWN_CONTROL 0x04 -#define QPHY_TXDEEMPH_M6DB_V0 0x24 -#define QPHY_TXDEEMPH_M3P5DB_V0 0x28 -#define QPHY_ENDPOINT_REFCLK_DRIVE 0x54 -#define QPHY_RX_IDLE_DTCT_CNTRL 0x58 -#define QPHY_POWER_STATE_CONFIG1 0x60 -#define QPHY_POWER_STATE_CONFIG2 0x64 -#define QPHY_POWER_STATE_CONFIG4 0x6c -#define QPHY_LOCK_DETECT_CONFIG1 0x80 -#define QPHY_LOCK_DETECT_CONFIG2 0x84 -#define QPHY_LOCK_DETECT_CONFIG3 0x88 -#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK 0xa0 -#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK 0xa4 - -/* QPHY_SW_RESET bit */ -#define SW_RESET BIT(0) -/* QPHY_POWER_DOWN_CONTROL */ -#define SW_PWRDN BIT(0) -#define REFCLK_DRV_DSBL BIT(1) -/* QPHY_START_CONTROL bits */ -#define SERDES_START BIT(0) -#define PCS_START BIT(1) -#define PLL_READY_GATE_EN BIT(3) -/* QPHY_PCS_STATUS bit */ -#define PHYSTATUS BIT(6) -/* QPHY_COM_PCS_READY_STATUS bit */ -#define PCS_READY BIT(0) - -#define PHY_INIT_COMPLETE_TIMEOUT 1000 -#define POWER_DOWN_DELAY_US_MIN 10 -#define POWER_DOWN_DELAY_US_MAX 11 - -#define MAX_PROP_NAME 32 - -struct qmp_phy_init_tbl { - unsigned int offset; - unsigned int val; - /* - * register part of layout ? - * if yes, then offset gives index in the reg-layout - */ - int in_layout; -}; - -#define QMP_PHY_INIT_CFG(o, v) \ - { \ - .offset = o, \ - .val = v, \ - } - -#define QMP_PHY_INIT_CFG_L(o, v) \ - { \ - .offset = o, \ - .val = v, \ - .in_layout = 1, \ - } - -/* set of registers with offsets different per-PHY */ -enum qphy_reg_layout { - /* Common block control registers */ - QPHY_COM_SW_RESET, - QPHY_COM_POWER_DOWN_CONTROL, - QPHY_COM_START_CONTROL, - QPHY_COM_PCS_READY_STATUS, - /* PCS registers */ - QPHY_PLL_LOCK_CHK_DLY_TIME, - QPHY_FLL_CNTRL1, - QPHY_FLL_CNTRL2, - QPHY_FLL_CNT_VAL_L, - QPHY_FLL_CNT_VAL_H_TOL, - QPHY_FLL_MAN_CODE, - QPHY_SW_RESET, - QPHY_START_CTRL, - QPHY_PCS_READY_STATUS, -}; - -static const unsigned int pciephy_regs_layout[] = { - [QPHY_COM_SW_RESET] = 0x400, - [QPHY_COM_POWER_DOWN_CONTROL] = 0x404, - [QPHY_COM_START_CONTROL] = 0x408, - [QPHY_COM_PCS_READY_STATUS] = 0x448, - [QPHY_PLL_LOCK_CHK_DLY_TIME] = 0xa8, - [QPHY_FLL_CNTRL1] = 0xc4, - [QPHY_FLL_CNTRL2] = 0xc8, - [QPHY_FLL_CNT_VAL_L] = 0xcc, - [QPHY_FLL_CNT_VAL_H_TOL] = 0xd0, - [QPHY_FLL_MAN_CODE] = 0xd4, - [QPHY_SW_RESET] = 0x00, - [QPHY_START_CTRL] = 0x08, - [QPHY_PCS_READY_STATUS] = 0x174, -}; - -static const unsigned int usb3phy_regs_layout[] = { - [QPHY_FLL_CNTRL1] = 0xc0, - [QPHY_FLL_CNTRL2] = 0xc4, - [QPHY_FLL_CNT_VAL_L] = 0xc8, - [QPHY_FLL_CNT_VAL_H_TOL] = 0xcc, - [QPHY_FLL_MAN_CODE] = 0xd0, - [QPHY_SW_RESET] = 0x00, - [QPHY_START_CTRL] = 0x08, - [QPHY_PCS_READY_STATUS] = 0x17c, -}; - -static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09), - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f), - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19), - QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40), -}; - -static const struct qmp_phy_init_tbl msm8996_pcie_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), - QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), -}; - -static const struct qmp_phy_init_tbl msm8996_pcie_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19), -}; - -static const struct qmp_phy_init_tbl msm8996_pcie_pcs_tbl[] = { - QMP_PHY_INIT_CFG(QPHY_RX_IDLE_DTCT_CNTRL, 0x4c), - QMP_PHY_INIT_CFG(QPHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x00), - QMP_PHY_INIT_CFG(QPHY_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01), - - QMP_PHY_INIT_CFG_L(QPHY_PLL_LOCK_CHK_DLY_TIME, 0x05), - - QMP_PHY_INIT_CFG(QPHY_ENDPOINT_REFCLK_DRIVE, 0x05), - QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x02), - QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG4, 0x00), - QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG1, 0xa3), - QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M3P5DB_V0, 0x0e), -}; - -static const struct qmp_phy_init_tbl msm8996_usb3_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), - QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), - QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x04), - /* PLL and Loop filter settings */ - QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), - QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), - QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), - /* SSC settings */ - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde), - QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07), -}; - -static const struct qmp_phy_init_tbl msm8996_usb3_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), - QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12), - QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), -}; - -static const struct qmp_phy_init_tbl msm8996_usb3_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), - QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x18), - QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16), -}; - -static const struct qmp_phy_init_tbl msm8996_usb3_pcs_tbl[] = { - /* FLL settings */ - QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL2, 0x03), - QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL1, 0x02), - QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_L, 0x09), - QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_H_TOL, 0x42), - QMP_PHY_INIT_CFG_L(QPHY_FLL_MAN_CODE, 0x85), - - /* Lock Det settings */ - QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG1, 0xd1), - QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG2, 0x1f), - QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG3, 0x47), - QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG2, 0x08), -}; - -/* struct qmp_phy_cfg - per-PHY initialization config */ -struct qmp_phy_cfg { - /* phy-type - PCIE/UFS/USB */ - unsigned int type; - /* number of lanes provided by phy */ - int nlanes; - - /* Init sequence for PHY blocks - serdes, tx, rx, pcs */ - const struct qmp_phy_init_tbl *serdes_tbl; - int serdes_tbl_num; - const struct qmp_phy_init_tbl *tx_tbl; - int tx_tbl_num; - const struct qmp_phy_init_tbl *rx_tbl; - int rx_tbl_num; - const struct qmp_phy_init_tbl *pcs_tbl; - int pcs_tbl_num; - - /* clock ids to be requested */ - const char * const *clk_list; - int num_clks; - /* resets to be requested */ - const char * const *reset_list; - int num_resets; - /* regulators to be requested */ - const char * const *vreg_list; - int num_vregs; - - /* array of registers with different offsets */ - const unsigned int *regs; - - unsigned int start_ctrl; - unsigned int pwrdn_ctrl; - unsigned int mask_pcs_ready; - unsigned int mask_com_pcs_ready; - - /* true, if PHY has a separate PHY_COM control block */ - bool has_phy_com_ctrl; - /* true, if PHY has a reset for individual lanes */ - bool has_lane_rst; - /* true, if PHY needs delay after POWER_DOWN */ - bool has_pwrdn_delay; - /* power_down delay in usec */ - int pwrdn_delay_min; - int pwrdn_delay_max; -}; - -/** - * struct qmp_phy - per-lane phy descriptor - * - * @phy: generic phy - * @tx: iomapped memory space for lane's tx - * @rx: iomapped memory space for lane's rx - * @pcs: iomapped memory space for lane's pcs - * @pipe_clk: pipe lock - * @index: lane index - * @qmp: QMP phy to which this lane belongs - * @lane_rst: lane's reset controller - */ -struct qmp_phy { - struct phy *phy; - void __iomem *tx; - void __iomem *rx; - void __iomem *pcs; - struct clk *pipe_clk; - unsigned int index; - struct qcom_qmp *qmp; - struct reset_control *lane_rst; -}; - -/** - * struct qcom_qmp - structure holding QMP phy block attributes - * - * @dev: device - * @serdes: iomapped memory space for phy's serdes - * - * @clks: array of clocks required by phy - * @resets: array of resets required by phy - * @vregs: regulator supplies bulk data - * - * @cfg: phy specific configuration - * @phys: array of per-lane phy descriptors - * @phy_mutex: mutex lock for PHY common block initialization - * @init_count: phy common block initialization count - */ -struct qcom_qmp { - struct device *dev; - void __iomem *serdes; - - struct clk **clks; - struct reset_control **resets; - struct regulator_bulk_data *vregs; - - const struct qmp_phy_cfg *cfg; - struct qmp_phy **phys; - - struct mutex phy_mutex; - int init_count; -}; - -static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) -{ - u32 reg; - - reg = readl(base + offset); - reg |= val; - writel(reg, base + offset); - - /* ensure that above write is through */ - readl(base + offset); -} - -static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) -{ - u32 reg; - - reg = readl(base + offset); - reg &= ~val; - writel(reg, base + offset); - - /* ensure that above write is through */ - readl(base + offset); -} - -/* list of clocks required by phy */ -static const char * const msm8996_phy_clk_l[] = { - "aux", "cfg_ahb", "ref", -}; - -/* list of resets */ -static const char * const msm8996_pciephy_reset_l[] = { - "phy", "common", "cfg", -}; - -static const char * const msm8996_usb3phy_reset_l[] = { - "phy", "common", -}; - -/* list of regulators */ -static const char * const msm8996_phy_vreg_l[] = { - "vdda-phy", "vdda-pll", -}; - -static const struct qmp_phy_cfg msm8996_pciephy_cfg = { - .type = PHY_TYPE_PCIE, - .nlanes = 3, - - .serdes_tbl = msm8996_pcie_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(msm8996_pcie_serdes_tbl), - .tx_tbl = msm8996_pcie_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(msm8996_pcie_tx_tbl), - .rx_tbl = msm8996_pcie_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(msm8996_pcie_rx_tbl), - .pcs_tbl = msm8996_pcie_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(msm8996_pcie_pcs_tbl), - .clk_list = msm8996_phy_clk_l, - .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), - .reset_list = msm8996_pciephy_reset_l, - .num_resets = ARRAY_SIZE(msm8996_pciephy_reset_l), - .vreg_list = msm8996_phy_vreg_l, - .num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l), - .regs = pciephy_regs_layout, - - .start_ctrl = PCS_START | PLL_READY_GATE_EN, - .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, - .mask_com_pcs_ready = PCS_READY, - - .has_phy_com_ctrl = true, - .has_lane_rst = true, - .has_pwrdn_delay = true, - .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, - .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, -}; - -static const struct qmp_phy_cfg msm8996_usb3phy_cfg = { - .type = PHY_TYPE_USB3, - .nlanes = 1, - - .serdes_tbl = msm8996_usb3_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(msm8996_usb3_serdes_tbl), - .tx_tbl = msm8996_usb3_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(msm8996_usb3_tx_tbl), - .rx_tbl = msm8996_usb3_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(msm8996_usb3_rx_tbl), - .pcs_tbl = msm8996_usb3_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(msm8996_usb3_pcs_tbl), - .clk_list = msm8996_phy_clk_l, - .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), - .reset_list = msm8996_usb3phy_reset_l, - .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), - .vreg_list = msm8996_phy_vreg_l, - .num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l), - .regs = usb3phy_regs_layout, - - .start_ctrl = SERDES_START | PCS_START, - .pwrdn_ctrl = SW_PWRDN, - .mask_pcs_ready = PHYSTATUS, -}; - -static void qcom_qmp_phy_configure(void __iomem *base, - const unsigned int *regs, - const struct qmp_phy_init_tbl tbl[], - int num) -{ - int i; - const struct qmp_phy_init_tbl *t = tbl; - - if (!t) - return; - - for (i = 0; i < num; i++, t++) { - if (t->in_layout) - writel(t->val, base + regs[t->offset]); - else - writel(t->val, base + t->offset); - } -} - -static int qcom_qmp_phy_poweron(struct phy *phy) -{ - struct qmp_phy *qphy = phy_get_drvdata(phy); - struct qcom_qmp *qmp = qphy->qmp; - int num = qmp->cfg->num_vregs; - int ret; - - dev_vdbg(&phy->dev, "Powering on QMP phy\n"); - - /* turn on regulator supplies */ - ret = regulator_bulk_enable(num, qmp->vregs); - if (ret) { - dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); - return ret; - } - - ret = clk_prepare_enable(qphy->pipe_clk); - if (ret) { - dev_err(qmp->dev, "pipe_clk enable failed, err=%d\n", ret); - regulator_bulk_disable(num, qmp->vregs); - return ret; - } - - return 0; -} - -static int qcom_qmp_phy_poweroff(struct phy *phy) -{ - struct qmp_phy *qphy = phy_get_drvdata(phy); - struct qcom_qmp *qmp = qphy->qmp; - - clk_disable_unprepare(qphy->pipe_clk); - - regulator_bulk_disable(qmp->cfg->num_vregs, qmp->vregs); - - return 0; -} - -static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp) -{ - const struct qmp_phy_cfg *cfg = qmp->cfg; - void __iomem *serdes = qmp->serdes; - int ret, i; - - mutex_lock(&qmp->phy_mutex); - if (qmp->init_count++) { - mutex_unlock(&qmp->phy_mutex); - return 0; - } - - for (i = 0; i < cfg->num_resets; i++) { - ret = reset_control_deassert(qmp->resets[i]); - if (ret) { - dev_err(qmp->dev, "%s reset deassert failed\n", - qmp->cfg->reset_list[i]); - while (--i >= 0) - reset_control_assert(qmp->resets[i]); - goto err_rst; - } - } - - if (cfg->has_phy_com_ctrl) - qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], - SW_PWRDN); - - /* Serdes configuration */ - qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl, - cfg->serdes_tbl_num); - - if (cfg->has_phy_com_ctrl) { - void __iomem *status; - unsigned int mask, val; - - qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET); - qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], - SERDES_START | PCS_START); - - status = serdes + cfg->regs[QPHY_COM_PCS_READY_STATUS]; - mask = cfg->mask_com_pcs_ready; - - ret = readl_poll_timeout(status, val, (val & mask), 10, - PHY_INIT_COMPLETE_TIMEOUT); - if (ret) { - dev_err(qmp->dev, - "phy common block init timed-out\n"); - goto err_com_init; - } - } - - mutex_unlock(&qmp->phy_mutex); - - return 0; - -err_com_init: - while (--i >= 0) - reset_control_assert(qmp->resets[i]); -err_rst: - mutex_unlock(&qmp->phy_mutex); - return ret; -} - -static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp) -{ - const struct qmp_phy_cfg *cfg = qmp->cfg; - void __iomem *serdes = qmp->serdes; - int i = cfg->num_resets; - - mutex_lock(&qmp->phy_mutex); - if (--qmp->init_count) { - mutex_unlock(&qmp->phy_mutex); - return 0; - } - - if (cfg->has_phy_com_ctrl) { - qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], - SERDES_START | PCS_START); - qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], - SW_RESET); - qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], - SW_PWRDN); - } - - while (--i >= 0) - reset_control_assert(qmp->resets[i]); - - mutex_unlock(&qmp->phy_mutex); - - return 0; -} - -/* PHY Initialization */ -static int qcom_qmp_phy_init(struct phy *phy) -{ - struct qmp_phy *qphy = phy_get_drvdata(phy); - struct qcom_qmp *qmp = qphy->qmp; - const struct qmp_phy_cfg *cfg = qmp->cfg; - void __iomem *tx = qphy->tx; - void __iomem *rx = qphy->rx; - void __iomem *pcs = qphy->pcs; - void __iomem *status; - unsigned int mask, val; - int ret, i; - - dev_vdbg(qmp->dev, "Initializing QMP phy\n"); - - for (i = 0; i < qmp->cfg->num_clks; i++) { - ret = clk_prepare_enable(qmp->clks[i]); - if (ret) { - dev_err(qmp->dev, "failed to enable %s clk, err=%d\n", - qmp->cfg->clk_list[i], ret); - while (--i >= 0) - clk_disable_unprepare(qmp->clks[i]); - } - } - - ret = qcom_qmp_phy_com_init(qmp); - if (ret) - goto err_com_init; - - if (cfg->has_lane_rst) { - ret = reset_control_deassert(qphy->lane_rst); - if (ret) { - dev_err(qmp->dev, "lane%d reset deassert failed\n", - qphy->index); - goto err_lane_rst; - } - } - - /* Tx, Rx, and PCS configurations */ - qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num); - qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num); - qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num); - - /* - * Pull out PHY from POWER DOWN state. - * This is active low enable signal to power-down PHY. - */ - qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); - - if (cfg->has_pwrdn_delay) - usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max); - - /* start SerDes and Phy-Coding-Sublayer */ - qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); - - /* Pull PHY out of reset state */ - qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); - - status = pcs + cfg->regs[QPHY_PCS_READY_STATUS]; - mask = cfg->mask_pcs_ready; - - ret = readl_poll_timeout(status, val, !(val & mask), 1, - PHY_INIT_COMPLETE_TIMEOUT); - if (ret) { - dev_err(qmp->dev, "phy initialization timed-out\n"); - goto err_pcs_ready; - } - - return ret; - -err_pcs_ready: - if (cfg->has_lane_rst) - reset_control_assert(qphy->lane_rst); -err_lane_rst: - qcom_qmp_phy_com_exit(qmp); -err_com_init: - while (--i >= 0) - clk_disable_unprepare(qmp->clks[i]); - - return ret; -} - -static int qcom_qmp_phy_exit(struct phy *phy) -{ - struct qmp_phy *qphy = phy_get_drvdata(phy); - struct qcom_qmp *qmp = qphy->qmp; - const struct qmp_phy_cfg *cfg = qmp->cfg; - int i = cfg->num_clks; - - /* PHY reset */ - qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); - - /* stop SerDes and Phy-Coding-Sublayer */ - qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); - - /* Put PHY into POWER DOWN state: active low */ - qphy_clrbits(qphy->pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); - - if (cfg->has_lane_rst) - reset_control_assert(qphy->lane_rst); - - qcom_qmp_phy_com_exit(qmp); - - while (--i >= 0) - clk_disable_unprepare(qmp->clks[i]); - - return 0; -} - -static int qcom_qmp_phy_vreg_init(struct device *dev) -{ - struct qcom_qmp *qmp = dev_get_drvdata(dev); - int num = qmp->cfg->num_vregs; - int i; - - qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); - if (!qmp->vregs) - return -ENOMEM; - - for (i = 0; i < num; i++) - qmp->vregs[i].supply = qmp->cfg->vreg_list[i]; - - return devm_regulator_bulk_get(dev, num, qmp->vregs); -} - -static int qcom_qmp_phy_reset_init(struct device *dev) -{ - struct qcom_qmp *qmp = dev_get_drvdata(dev); - int i; - - qmp->resets = devm_kcalloc(dev, qmp->cfg->num_resets, - sizeof(*qmp->resets), GFP_KERNEL); - if (!qmp->resets) - return -ENOMEM; - - for (i = 0; i < qmp->cfg->num_resets; i++) { - struct reset_control *rst; - const char *name = qmp->cfg->reset_list[i]; - - rst = devm_reset_control_get(dev, name); - if (IS_ERR(rst)) { - dev_err(dev, "failed to get %s reset\n", name); - return PTR_ERR(rst); - } - qmp->resets[i] = rst; - } - - return 0; -} - -static int qcom_qmp_phy_clk_init(struct device *dev) -{ - struct qcom_qmp *qmp = dev_get_drvdata(dev); - int ret, i; - - qmp->clks = devm_kcalloc(dev, qmp->cfg->num_clks, - sizeof(*qmp->clks), GFP_KERNEL); - if (!qmp->clks) - return -ENOMEM; - - for (i = 0; i < qmp->cfg->num_clks; i++) { - struct clk *_clk; - const char *name = qmp->cfg->clk_list[i]; - - _clk = devm_clk_get(dev, name); - if (IS_ERR(_clk)) { - ret = PTR_ERR(_clk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get %s clk, %d\n", - name, ret); - return ret; - } - qmp->clks[i] = _clk; - } - - return 0; -} - -/* - * Register a fixed rate pipe clock. - * - * The _pipe_clksrc generated by PHY goes to the GCC that gate - * controls it. The _pipe_clk coming out of the GCC is requested - * by the PHY driver for its operations. - * We register the _pipe_clksrc here. The gcc driver takes care - * of assigning this _pipe_clksrc as parent to _pipe_clk. - * Below picture shows this relationship. - * - * +---------------+ - * | PHY block |<<---------------------------------------+ - * | | | - * | +-------+ | +-----+ | - * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+ - * clk | +-------+ | +-----+ - * +---------------+ - */ -static int phy_pipe_clk_register(struct qcom_qmp *qmp, int id) -{ - char name[24]; - struct clk_fixed_rate *fixed; - struct clk_init_data init = { }; - - switch (qmp->cfg->type) { - case PHY_TYPE_USB3: - snprintf(name, sizeof(name), "usb3_phy_pipe_clk_src"); - break; - case PHY_TYPE_PCIE: - snprintf(name, sizeof(name), "pcie_%d_pipe_clk_src", id); - break; - default: - /* not all phys register pipe clocks, so return success */ - return 0; - } - - fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL); - if (!fixed) - return -ENOMEM; - - init.name = name; - init.ops = &clk_fixed_rate_ops; - - /* controllers using QMP phys use 125MHz pipe clock interface */ - fixed->fixed_rate = 125000000; - fixed->hw.init = &init; - - return devm_clk_hw_register(qmp->dev, &fixed->hw); -} - -static const struct phy_ops qcom_qmp_phy_gen_ops = { - .init = qcom_qmp_phy_init, - .exit = qcom_qmp_phy_exit, - .power_on = qcom_qmp_phy_poweron, - .power_off = qcom_qmp_phy_poweroff, - .owner = THIS_MODULE, -}; - -static -int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) -{ - struct qcom_qmp *qmp = dev_get_drvdata(dev); - struct phy *generic_phy; - struct qmp_phy *qphy; - char prop_name[MAX_PROP_NAME]; - int ret; - - qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); - if (!qphy) - return -ENOMEM; - - /* - * Get memory resources for each phy lane: - * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. - */ - qphy->tx = of_iomap(np, 0); - if (!qphy->tx) - return -ENOMEM; - - qphy->rx = of_iomap(np, 1); - if (!qphy->rx) - return -ENOMEM; - - qphy->pcs = of_iomap(np, 2); - if (!qphy->pcs) - return -ENOMEM; - - /* - * Get PHY's Pipe clock, if any. USB3 and PCIe are PIPE3 - * based phys, so they essentially have pipe clock. So, - * we return error in case phy is USB3 or PIPE type. - * Otherwise, we initialize pipe clock to NULL for - * all phys that don't need this. - */ - snprintf(prop_name, sizeof(prop_name), "pipe%d", id); - qphy->pipe_clk = of_clk_get_by_name(np, prop_name); - if (IS_ERR(qphy->pipe_clk)) { - if (qmp->cfg->type == PHY_TYPE_PCIE || - qmp->cfg->type == PHY_TYPE_USB3) { - ret = PTR_ERR(qphy->pipe_clk); - if (ret != -EPROBE_DEFER) - dev_err(dev, - "failed to get lane%d pipe_clk, %d\n", - id, ret); - return ret; - } - qphy->pipe_clk = NULL; - } - - /* Get lane reset, if any */ - if (qmp->cfg->has_lane_rst) { - snprintf(prop_name, sizeof(prop_name), "lane%d", id); - qphy->lane_rst = of_reset_control_get(np, prop_name); - if (IS_ERR(qphy->lane_rst)) { - dev_err(dev, "failed to get lane%d reset\n", id); - return PTR_ERR(qphy->lane_rst); - } - } - - generic_phy = devm_phy_create(dev, np, &qcom_qmp_phy_gen_ops); - if (IS_ERR(generic_phy)) { - ret = PTR_ERR(generic_phy); - dev_err(dev, "failed to create qphy %d\n", ret); - return ret; - } - - qphy->phy = generic_phy; - qphy->index = id; - qphy->qmp = qmp; - qmp->phys[id] = qphy; - phy_set_drvdata(generic_phy, qphy); - - return 0; -} - -static const struct of_device_id qcom_qmp_phy_of_match_table[] = { - { - .compatible = "qcom,msm8996-qmp-pcie-phy", - .data = &msm8996_pciephy_cfg, - }, { - .compatible = "qcom,msm8996-qmp-usb3-phy", - .data = &msm8996_usb3phy_cfg, - }, - { }, -}; -MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table); - -static int qcom_qmp_phy_probe(struct platform_device *pdev) -{ - struct qcom_qmp *qmp; - struct device *dev = &pdev->dev; - struct resource *res; - struct device_node *child; - struct phy_provider *phy_provider; - void __iomem *base; - int num, id; - int ret; - - qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL); - if (!qmp) - return -ENOMEM; - - qmp->dev = dev; - dev_set_drvdata(dev, qmp); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - /* per PHY serdes; usually located at base address */ - qmp->serdes = base; - - mutex_init(&qmp->phy_mutex); - - /* Get the specific init parameters of QMP phy */ - qmp->cfg = of_device_get_match_data(dev); - - ret = qcom_qmp_phy_clk_init(dev); - if (ret) - return ret; - - ret = qcom_qmp_phy_reset_init(dev); - if (ret) - return ret; - - ret = qcom_qmp_phy_vreg_init(dev); - if (ret) { - dev_err(dev, "failed to get regulator supplies\n"); - return ret; - } - - num = of_get_available_child_count(dev->of_node); - /* do we have a rogue child node ? */ - if (num > qmp->cfg->nlanes) - return -EINVAL; - - qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL); - if (!qmp->phys) - return -ENOMEM; - - id = 0; - for_each_available_child_of_node(dev->of_node, child) { - /* Create per-lane phy */ - ret = qcom_qmp_phy_create(dev, child, id); - if (ret) { - dev_err(dev, "failed to create lane%d phy, %d\n", - id, ret); - return ret; - } - - /* - * Register the pipe clock provided by phy. - * See function description to see details of this pipe clock. - */ - ret = phy_pipe_clk_register(qmp, id); - if (ret) { - dev_err(qmp->dev, - "failed to register pipe clock source\n"); - return ret; - } - id++; - } - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (!IS_ERR(phy_provider)) - dev_info(dev, "Registered Qcom-QMP phy\n"); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static struct platform_driver qcom_qmp_phy_driver = { - .probe = qcom_qmp_phy_probe, - .driver = { - .name = "qcom-qmp-phy", - .of_match_table = qcom_qmp_phy_of_match_table, - }, -}; - -module_platform_driver(qcom_qmp_phy_driver); - -MODULE_AUTHOR("Vivek Gautam "); -MODULE_DESCRIPTION("Qualcomm QMP PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-qusb2.c b/drivers/phy/phy-qcom-qusb2.c deleted file mode 100644 index 6c575244c0fb..000000000000 --- a/drivers/phy/phy-qcom-qusb2.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define QUSB2PHY_PLL_TEST 0x04 -#define CLK_REF_SEL BIT(7) - -#define QUSB2PHY_PLL_TUNE 0x08 -#define QUSB2PHY_PLL_USER_CTL1 0x0c -#define QUSB2PHY_PLL_USER_CTL2 0x10 -#define QUSB2PHY_PLL_AUTOPGM_CTL1 0x1c -#define QUSB2PHY_PLL_PWR_CTRL 0x18 - -#define QUSB2PHY_PLL_STATUS 0x38 -#define PLL_LOCKED BIT(5) - -#define QUSB2PHY_PORT_TUNE1 0x80 -#define QUSB2PHY_PORT_TUNE2 0x84 -#define QUSB2PHY_PORT_TUNE3 0x88 -#define QUSB2PHY_PORT_TUNE4 0x8c -#define QUSB2PHY_PORT_TUNE5 0x90 -#define QUSB2PHY_PORT_TEST2 0x9c - -#define QUSB2PHY_PORT_POWERDOWN 0xb4 -#define CLAMP_N_EN BIT(5) -#define FREEZIO_N BIT(1) -#define POWER_DOWN BIT(0) - -#define QUSB2PHY_REFCLK_ENABLE BIT(0) - -#define PHY_CLK_SCHEME_SEL BIT(0) - -struct qusb2_phy_init_tbl { - unsigned int offset; - unsigned int val; -}; - -#define QUSB2_PHY_INIT_CFG(o, v) \ - { \ - .offset = o, \ - .val = v, \ - } - -static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = { - QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE1, 0xf8), - QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE2, 0xb3), - QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE3, 0x83), - QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE4, 0xc0), - QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30), - QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79), - QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21), - QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TEST2, 0x14), - QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f), - QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), -}; - -struct qusb2_phy_cfg { - const struct qusb2_phy_init_tbl *tbl; - /* number of entries in the table */ - unsigned int tbl_num; - /* offset to PHY_CLK_SCHEME register in TCSR map */ - unsigned int clk_scheme_offset; -}; - -static const struct qusb2_phy_cfg msm8996_phy_cfg = { - .tbl = msm8996_init_tbl, - .tbl_num = ARRAY_SIZE(msm8996_init_tbl), -}; - -static const char * const qusb2_phy_vreg_names[] = { - "vdda-pll", "vdda-phy-dpdm", -}; - -#define QUSB2_NUM_VREGS ARRAY_SIZE(qusb2_phy_vreg_names) - -/** - * struct qusb2_phy - structure holding qusb2 phy attributes - * - * @phy: generic phy - * @base: iomapped memory space for qubs2 phy - * - * @cfg_ahb_clk: AHB2PHY interface clock - * @ref_clk: phy reference clock - * @iface_clk: phy interface clock - * @phy_reset: phy reset control - * @vregs: regulator supplies bulk data - * - * @tcsr: TCSR syscon register map - * @cell: nvmem cell containing phy tuning value - * - * @cfg: phy config data - * @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme - */ -struct qusb2_phy { - struct phy *phy; - void __iomem *base; - - struct clk *cfg_ahb_clk; - struct clk *ref_clk; - struct clk *iface_clk; - struct reset_control *phy_reset; - struct regulator_bulk_data vregs[QUSB2_NUM_VREGS]; - - struct regmap *tcsr; - struct nvmem_cell *cell; - - const struct qusb2_phy_cfg *cfg; - bool has_se_clk_scheme; -}; - -static inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val) -{ - u32 reg; - - reg = readl(base + offset); - reg |= val; - writel(reg, base + offset); - - /* Ensure above write is completed */ - readl(base + offset); -} - -static inline void qusb2_clrbits(void __iomem *base, u32 offset, u32 val) -{ - u32 reg; - - reg = readl(base + offset); - reg &= ~val; - writel(reg, base + offset); - - /* Ensure above write is completed */ - readl(base + offset); -} - -static inline -void qcom_qusb2_phy_configure(void __iomem *base, - const struct qusb2_phy_init_tbl tbl[], int num) -{ - int i; - - for (i = 0; i < num; i++) - writel(tbl[i].val, base + tbl[i].offset); -} - -/* - * Fetches HS Tx tuning value from nvmem and sets the - * QUSB2PHY_PORT_TUNE2 register. - * For error case, skip setting the value and use the default value. - */ -static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) -{ - struct device *dev = &qphy->phy->dev; - u8 *val; - - /* - * Read efuse register having TUNE2 parameter's high nibble. - * If efuse register shows value as 0x0, or if we fail to find - * a valid efuse register settings, then use default value - * as 0xB for high nibble that we have already set while - * configuring phy. - */ - val = nvmem_cell_read(qphy->cell, NULL); - if (IS_ERR(val) || !val[0]) { - dev_dbg(dev, "failed to read a valid hs-tx trim value\n"); - return; - } - - /* Fused TUNE2 value is the higher nibble only */ - qusb2_setbits(qphy->base, QUSB2PHY_PORT_TUNE2, val[0] << 0x4); -} - -static int qusb2_phy_poweron(struct phy *phy) -{ - struct qusb2_phy *qphy = phy_get_drvdata(phy); - int num = ARRAY_SIZE(qphy->vregs); - int ret; - - dev_vdbg(&phy->dev, "%s(): Powering-on QUSB2 phy\n", __func__); - - /* turn on regulator supplies */ - ret = regulator_bulk_enable(num, qphy->vregs); - if (ret) - return ret; - - ret = clk_prepare_enable(qphy->iface_clk); - if (ret) { - dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret); - regulator_bulk_disable(num, qphy->vregs); - return ret; - } - - return 0; -} - -static int qusb2_phy_poweroff(struct phy *phy) -{ - struct qusb2_phy *qphy = phy_get_drvdata(phy); - - clk_disable_unprepare(qphy->iface_clk); - - regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs); - - return 0; -} - -static int qusb2_phy_init(struct phy *phy) -{ - struct qusb2_phy *qphy = phy_get_drvdata(phy); - unsigned int val; - unsigned int clk_scheme; - int ret; - - dev_vdbg(&phy->dev, "%s(): Initializing QUSB2 phy\n", __func__); - - /* enable ahb interface clock to program phy */ - ret = clk_prepare_enable(qphy->cfg_ahb_clk); - if (ret) { - dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret); - return ret; - } - - /* Perform phy reset */ - ret = reset_control_assert(qphy->phy_reset); - if (ret) { - dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret); - goto disable_ahb_clk; - } - - /* 100 us delay to keep PHY in reset mode */ - usleep_range(100, 150); - - ret = reset_control_deassert(qphy->phy_reset); - if (ret) { - dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret); - goto disable_ahb_clk; - } - - /* Disable the PHY */ - qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, - CLAMP_N_EN | FREEZIO_N | POWER_DOWN); - - /* save reset value to override reference clock scheme later */ - val = readl(qphy->base + QUSB2PHY_PLL_TEST); - - qcom_qusb2_phy_configure(qphy->base, qphy->cfg->tbl, - qphy->cfg->tbl_num); - - /* Set efuse value for tuning the PHY */ - qusb2_phy_set_tune2_param(qphy); - - /* Enable the PHY */ - qusb2_clrbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, POWER_DOWN); - - /* Required to get phy pll lock successfully */ - usleep_range(150, 160); - - /* Default is single-ended clock on msm8996 */ - qphy->has_se_clk_scheme = true; - /* - * read TCSR_PHY_CLK_SCHEME register to check if single-ended - * clock scheme is selected. If yes, then disable differential - * ref_clk and use single-ended clock, otherwise use differential - * ref_clk only. - */ - if (qphy->tcsr) { - ret = regmap_read(qphy->tcsr, qphy->cfg->clk_scheme_offset, - &clk_scheme); - if (ret) { - dev_err(&phy->dev, "failed to read clk scheme reg\n"); - goto assert_phy_reset; - } - - /* is it a differential clock scheme ? */ - if (!(clk_scheme & PHY_CLK_SCHEME_SEL)) { - dev_vdbg(&phy->dev, "%s(): select differential clk\n", - __func__); - qphy->has_se_clk_scheme = false; - } else { - dev_vdbg(&phy->dev, "%s(): select single-ended clk\n", - __func__); - } - } - - if (!qphy->has_se_clk_scheme) { - val &= ~CLK_REF_SEL; - ret = clk_prepare_enable(qphy->ref_clk); - if (ret) { - dev_err(&phy->dev, "failed to enable ref clk, %d\n", - ret); - goto assert_phy_reset; - } - } else { - val |= CLK_REF_SEL; - } - - writel(val, qphy->base + QUSB2PHY_PLL_TEST); - - /* ensure above write is through */ - readl(qphy->base + QUSB2PHY_PLL_TEST); - - /* Required to get phy pll lock successfully */ - usleep_range(100, 110); - - val = readb(qphy->base + QUSB2PHY_PLL_STATUS); - if (!(val & PLL_LOCKED)) { - dev_err(&phy->dev, - "QUSB2PHY pll lock failed: status reg = %x\n", val); - ret = -EBUSY; - goto disable_ref_clk; - } - - return 0; - -disable_ref_clk: - if (!qphy->has_se_clk_scheme) - clk_disable_unprepare(qphy->ref_clk); -assert_phy_reset: - reset_control_assert(qphy->phy_reset); -disable_ahb_clk: - clk_disable_unprepare(qphy->cfg_ahb_clk); - return ret; -} - -static int qusb2_phy_exit(struct phy *phy) -{ - struct qusb2_phy *qphy = phy_get_drvdata(phy); - - /* Disable the PHY */ - qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, - CLAMP_N_EN | FREEZIO_N | POWER_DOWN); - - if (!qphy->has_se_clk_scheme) - clk_disable_unprepare(qphy->ref_clk); - - reset_control_assert(qphy->phy_reset); - - clk_disable_unprepare(qphy->cfg_ahb_clk); - - return 0; -} - -static const struct phy_ops qusb2_phy_gen_ops = { - .init = qusb2_phy_init, - .exit = qusb2_phy_exit, - .power_on = qusb2_phy_poweron, - .power_off = qusb2_phy_poweroff, - .owner = THIS_MODULE, -}; - -static const struct of_device_id qusb2_phy_of_match_table[] = { - { - .compatible = "qcom,msm8996-qusb2-phy", - .data = &msm8996_phy_cfg, - }, - { }, -}; -MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table); - -static int qusb2_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct qusb2_phy *qphy; - struct phy_provider *phy_provider; - struct phy *generic_phy; - struct resource *res; - int ret, i; - int num; - - qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); - if (!qphy) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - qphy->base = devm_ioremap_resource(dev, res); - if (IS_ERR(qphy->base)) - return PTR_ERR(qphy->base); - - qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb"); - if (IS_ERR(qphy->cfg_ahb_clk)) { - ret = PTR_ERR(qphy->cfg_ahb_clk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get cfg ahb clk, %d\n", ret); - return ret; - } - - qphy->ref_clk = devm_clk_get(dev, "ref"); - if (IS_ERR(qphy->ref_clk)) { - ret = PTR_ERR(qphy->ref_clk); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get ref clk, %d\n", ret); - return ret; - } - - qphy->iface_clk = devm_clk_get(dev, "iface"); - if (IS_ERR(qphy->iface_clk)) { - ret = PTR_ERR(qphy->iface_clk); - if (ret == -EPROBE_DEFER) - return ret; - qphy->iface_clk = NULL; - dev_dbg(dev, "failed to get iface clk, %d\n", ret); - } - - qphy->phy_reset = devm_reset_control_get_by_index(&pdev->dev, 0); - if (IS_ERR(qphy->phy_reset)) { - dev_err(dev, "failed to get phy core reset\n"); - return PTR_ERR(qphy->phy_reset); - } - - num = ARRAY_SIZE(qphy->vregs); - for (i = 0; i < num; i++) - qphy->vregs[i].supply = qusb2_phy_vreg_names[i]; - - ret = devm_regulator_bulk_get(dev, num, qphy->vregs); - if (ret) { - dev_err(dev, "failed to get regulator supplies\n"); - return ret; - } - - /* Get the specific init parameters of QMP phy */ - qphy->cfg = of_device_get_match_data(dev); - - qphy->tcsr = syscon_regmap_lookup_by_phandle(dev->of_node, - "qcom,tcsr-syscon"); - if (IS_ERR(qphy->tcsr)) { - dev_dbg(dev, "failed to lookup TCSR regmap\n"); - qphy->tcsr = NULL; - } - - qphy->cell = devm_nvmem_cell_get(dev, NULL); - if (IS_ERR(qphy->cell)) { - if (PTR_ERR(qphy->cell) == -EPROBE_DEFER) - return -EPROBE_DEFER; - qphy->cell = NULL; - dev_dbg(dev, "failed to lookup tune2 hstx trim value\n"); - } - - generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops); - if (IS_ERR(generic_phy)) { - ret = PTR_ERR(generic_phy); - dev_err(dev, "failed to create phy, %d\n", ret); - return ret; - } - qphy->phy = generic_phy; - - dev_set_drvdata(dev, qphy); - phy_set_drvdata(generic_phy, qphy); - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (!IS_ERR(phy_provider)) - dev_info(dev, "Registered Qcom-QUSB2 phy\n"); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static struct platform_driver qusb2_phy_driver = { - .probe = qusb2_phy_probe, - .driver = { - .name = "qcom-qusb2-phy", - .of_match_table = qusb2_phy_of_match_table, - }, -}; - -module_platform_driver(qusb2_phy_driver); - -MODULE_AUTHOR("Vivek Gautam "); -MODULE_DESCRIPTION("Qualcomm QUSB2 PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h deleted file mode 100644 index 13b02b7de30b..000000000000 --- a/drivers/phy/phy-qcom-ufs-i.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#ifndef UFS_QCOM_PHY_I_H_ -#define UFS_QCOM_PHY_I_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ -({ \ - ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \ - might_sleep_if(timeout_us); \ - for (;;) { \ - (val) = readl(addr); \ - if (cond) \ - break; \ - if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \ - (val) = readl(addr); \ - break; \ - } \ - if (sleep_us) \ - usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \ - } \ - (cond) ? 0 : -ETIMEDOUT; \ -}) - -#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \ - { \ - .reg_offset = reg, \ - .cfg_value = val, \ - } - -#define UFS_QCOM_PHY_NAME_LEN 30 - -enum { - MASK_SERDES_START = 0x1, - MASK_PCS_READY = 0x1, -}; - -enum { - OFFSET_SERDES_START = 0x0, -}; - -struct ufs_qcom_phy_stored_attributes { - u32 att; - u32 value; -}; - - -struct ufs_qcom_phy_calibration { - u32 reg_offset; - u32 cfg_value; -}; - -struct ufs_qcom_phy_vreg { - const char *name; - struct regulator *reg; - int max_uA; - int min_uV; - int max_uV; - bool enabled; -}; - -struct ufs_qcom_phy { - struct list_head list; - struct device *dev; - void __iomem *mmio; - void __iomem *dev_ref_clk_ctrl_mmio; - struct clk *tx_iface_clk; - struct clk *rx_iface_clk; - bool is_iface_clk_enabled; - struct clk *ref_clk_src; - struct clk *ref_clk_parent; - struct clk *ref_clk; - bool is_ref_clk_enabled; - bool is_dev_ref_clk_enabled; - struct ufs_qcom_phy_vreg vdda_pll; - struct ufs_qcom_phy_vreg vdda_phy; - struct ufs_qcom_phy_vreg vddp_ref_clk; - unsigned int quirks; - - /* - * If UFS link is put into Hibern8 and if UFS PHY analog hardware is - * power collapsed (by clearing UFS_PHY_POWER_DOWN_CONTROL), Hibern8 - * exit might fail even after powering on UFS PHY analog hardware. - * Enabling this quirk will help to solve above issue by doing - * custom PHY settings just before PHY analog power collapse. - */ - #define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE BIT(0) - - u8 host_ctrl_rev_major; - u16 host_ctrl_rev_minor; - u16 host_ctrl_rev_step; - - char name[UFS_QCOM_PHY_NAME_LEN]; - struct ufs_qcom_phy_calibration *cached_regs; - int cached_regs_table_size; - bool is_powered_on; - struct ufs_qcom_phy_specific_ops *phy_spec_ops; -}; - -/** - * struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a - * specific implementation per phy. Each UFS phy, should implement - * those functions according to its spec and requirements - * @calibrate_phy: pointer to a function that calibrate the phy - * @start_serdes: pointer to a function that starts the serdes - * @is_physical_coding_sublayer_ready: pointer to a function that - * checks pcs readiness. returns 0 for success and non-zero for error. - * @set_tx_lane_enable: pointer to a function that enable tx lanes - * @power_control: pointer to a function that controls analog rail of phy - * and writes to QSERDES_RX_SIGDET_CNTRL attribute - */ -struct ufs_qcom_phy_specific_ops { - int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B); - void (*start_serdes)(struct ufs_qcom_phy *phy); - int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy); - void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val); - void (*power_control)(struct ufs_qcom_phy *phy, bool val); -}; - -struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy); -int ufs_qcom_phy_power_on(struct phy *generic_phy); -int ufs_qcom_phy_power_off(struct phy *generic_phy); -int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common); -int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common); -int ufs_qcom_phy_remove(struct phy *generic_phy, - struct ufs_qcom_phy *ufs_qcom_phy); -struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, - struct ufs_qcom_phy *common_cfg, - const struct phy_ops *ufs_qcom_phy_gen_ops, - struct ufs_qcom_phy_specific_ops *phy_spec_ops); -int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A, - struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B, - bool is_rate_B); -#endif diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/phy-qcom-ufs-qmp-14nm.c deleted file mode 100644 index 12a1b498dc4b..000000000000 --- a/drivers/phy/phy-qcom-ufs-qmp-14nm.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include "phy-qcom-ufs-qmp-14nm.h" - -#define UFS_PHY_NAME "ufs_phy_qmp_14nm" -#define UFS_PHY_VDDA_PHY_UV (925000) - -static -int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B) -{ - int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); - int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); - int err; - - err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A, - tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B); - - if (err) - dev_err(ufs_qcom_phy->dev, - "%s: ufs_qcom_phy_calibrate() failed %d\n", - __func__, err); - return err; -} - -static -void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common) -{ - phy_common->quirks = - UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; -} - -static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy) -{ - return 0; -} - -static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy) -{ - return 0; -} - -static -void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val) -{ - writel_relaxed(val ? 0x1 : 0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); - /* - * Before any transactions involving PHY, ensure PHY knows - * that it's analog rail is powered ON (or OFF). - */ - mb(); -} - -static inline -void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) -{ - /* - * 14nm PHY does not have TX_LANE_ENABLE register. - * Implement this function so as not to propagate error to caller. - */ -} - -static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy *phy) -{ - u32 tmp; - - tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); - tmp &= ~MASK_SERDES_START; - tmp |= (1 << OFFSET_SERDES_START); - writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); - /* Ensure register value is committed */ - mb(); -} - -static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) -{ - int err = 0; - u32 val; - - err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, - val, (val & MASK_PCS_READY), 10, 1000000); - if (err) - dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", - __func__, err); - return err; -} - -static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { - .init = ufs_qcom_phy_qmp_14nm_init, - .exit = ufs_qcom_phy_qmp_14nm_exit, - .power_on = ufs_qcom_phy_power_on, - .power_off = ufs_qcom_phy_power_off, - .owner = THIS_MODULE, -}; - -static struct ufs_qcom_phy_specific_ops phy_14nm_ops = { - .calibrate_phy = ufs_qcom_phy_qmp_14nm_phy_calibrate, - .start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes, - .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready, - .set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable, - .power_control = ufs_qcom_phy_qmp_14nm_power_control, -}; - -static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct phy *generic_phy; - struct ufs_qcom_phy_qmp_14nm *phy; - struct ufs_qcom_phy *phy_common; - int err = 0; - - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) { - err = -ENOMEM; - goto out; - } - phy_common = &phy->common_cfg; - - generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common, - &ufs_qcom_phy_qmp_14nm_phy_ops, &phy_14nm_ops); - - if (!generic_phy) { - err = -EIO; - goto out; - } - - err = ufs_qcom_phy_init_clks(phy_common); - if (err) - goto out; - - err = ufs_qcom_phy_init_vregulators(phy_common); - if (err) - goto out; - - phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV; - phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV; - - ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common); - - phy_set_drvdata(generic_phy, phy); - - strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name)); - -out: - return err; -} - -static const struct of_device_id ufs_qcom_phy_qmp_14nm_of_match[] = { - {.compatible = "qcom,ufs-phy-qmp-14nm"}, - {.compatible = "qcom,msm8996-ufs-phy-qmp-14nm"}, - {}, -}; -MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_14nm_of_match); - -static struct platform_driver ufs_qcom_phy_qmp_14nm_driver = { - .probe = ufs_qcom_phy_qmp_14nm_probe, - .driver = { - .of_match_table = ufs_qcom_phy_qmp_14nm_of_match, - .name = "ufs_qcom_phy_qmp_14nm", - }, -}; - -module_platform_driver(ufs_qcom_phy_qmp_14nm_driver); - -MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 14nm"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/phy-qcom-ufs-qmp-14nm.h deleted file mode 100644 index 3aefdbacbcd0..000000000000 --- a/drivers/phy/phy-qcom-ufs-qmp-14nm.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#ifndef UFS_QCOM_PHY_QMP_14NM_H_ -#define UFS_QCOM_PHY_QMP_14NM_H_ - -#include "phy-qcom-ufs-i.h" - -/* QCOM UFS PHY control registers */ -#define COM_OFF(x) (0x000 + x) -#define PHY_OFF(x) (0xC00 + x) -#define TX_OFF(n, x) (0x400 + (0x400 * n) + x) -#define RX_OFF(n, x) (0x600 + (0x400 * n) + x) - -/* UFS PHY QSERDES COM registers */ -#define QSERDES_COM_BG_TIMER COM_OFF(0x0C) -#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34) -#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C) -#define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0x4C) -#define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0x50) -#define QSERDES_COM_LOCK_CMP3_MODE0 COM_OFF(0x54) -#define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0x58) -#define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0x5C) -#define QSERDES_COM_LOCK_CMP3_MODE1 COM_OFF(0x60) -#define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x78) -#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x7C) -#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x84) -#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x88) -#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x90) -#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x94) -#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0xAC) -#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0xB4) -#define QSERDES_COM_LOCK_CMP_EN COM_OFF(0xC8) -#define QSERDES_COM_LOCK_CMP_CFG COM_OFF(0xCC) -#define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xD0) -#define QSERDES_COM_DEC_START_MODE1 COM_OFF(0xD4) -#define QSERDES_COM_DIV_FRAC_START1_MODE0 COM_OFF(0xDC) -#define QSERDES_COM_DIV_FRAC_START2_MODE0 COM_OFF(0xE0) -#define QSERDES_COM_DIV_FRAC_START3_MODE0 COM_OFF(0xE4) -#define QSERDES_COM_DIV_FRAC_START1_MODE1 COM_OFF(0xE8) -#define QSERDES_COM_DIV_FRAC_START2_MODE1 COM_OFF(0xEC) -#define QSERDES_COM_DIV_FRAC_START3_MODE1 COM_OFF(0xF0) -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 COM_OFF(0x108) -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 COM_OFF(0x10C) -#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 COM_OFF(0x110) -#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 COM_OFF(0x114) -#define QSERDES_COM_VCO_TUNE_CTRL COM_OFF(0x124) -#define QSERDES_COM_VCO_TUNE_MAP COM_OFF(0x128) -#define QSERDES_COM_VCO_TUNE1_MODE0 COM_OFF(0x12C) -#define QSERDES_COM_VCO_TUNE2_MODE0 COM_OFF(0x130) -#define QSERDES_COM_VCO_TUNE1_MODE1 COM_OFF(0x134) -#define QSERDES_COM_VCO_TUNE2_MODE1 COM_OFF(0x138) -#define QSERDES_COM_VCO_TUNE_TIMER1 COM_OFF(0x144) -#define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x148) -#define QSERDES_COM_CLK_SELECT COM_OFF(0x174) -#define QSERDES_COM_HSCLK_SEL COM_OFF(0x178) -#define QSERDES_COM_CORECLK_DIV COM_OFF(0x184) -#define QSERDES_COM_CORE_CLK_EN COM_OFF(0x18C) -#define QSERDES_COM_CMN_CONFIG COM_OFF(0x194) -#define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x19C) -#define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x1BC) - -/* UFS PHY registers */ -#define UFS_PHY_PHY_START PHY_OFF(0x00) -#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04) -#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x168) - -/* UFS PHY TX registers */ -#define QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN TX_OFF(0, 0x68) -#define QSERDES_TX_LANE_MODE TX_OFF(0, 0x94) - -/* UFS PHY RX registers */ -#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN RX_OFF(0, 0x40) -#define QSERDES_RX_RX_TERM_BW RX_OFF(0, 0x90) -#define QSERDES_RX_RX_EQ_GAIN1_LSB RX_OFF(0, 0xC4) -#define QSERDES_RX_RX_EQ_GAIN1_MSB RX_OFF(0, 0xC8) -#define QSERDES_RX_RX_EQ_GAIN2_LSB RX_OFF(0, 0xCC) -#define QSERDES_RX_RX_EQ_GAIN2_MSB RX_OFF(0, 0xD0) -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0, 0xD8) -#define QSERDES_RX_SIGDET_CNTRL RX_OFF(0, 0x114) -#define QSERDES_RX_SIGDET_LVL RX_OFF(0, 0x118) -#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x11C) -#define QSERDES_RX_RX_INTERFACE_MODE RX_OFF(0, 0x12C) - -/* - * This structure represents the 14nm specific phy. - * common_cfg MUST remain the first field in this structure - * in case extra fields are added. This way, when calling - * get_ufs_qcom_phy() of generic phy, we can extract the - * common phy structure (struct ufs_qcom_phy) out of it - * regardless of the relevant specific phy. - */ -struct ufs_qcom_phy_qmp_14nm { - struct ufs_qcom_phy common_cfg; -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = { - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xd7), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x06), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x05), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x10), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x14), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), - - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x02), - - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E), -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x54), -}; - -#endif diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/phy-qcom-ufs-qmp-20nm.c deleted file mode 100644 index 4f68acb58b73..000000000000 --- a/drivers/phy/phy-qcom-ufs-qmp-20nm.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include "phy-qcom-ufs-qmp-20nm.h" - -#define UFS_PHY_NAME "ufs_phy_qmp_20nm" - -static -int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - bool is_rate_B) -{ - struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; - int tbl_size_A, tbl_size_B; - u8 major = ufs_qcom_phy->host_ctrl_rev_major; - u16 minor = ufs_qcom_phy->host_ctrl_rev_minor; - u16 step = ufs_qcom_phy->host_ctrl_rev_step; - int err; - - if ((major == 0x1) && (minor == 0x002) && (step == 0x0000)) { - tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_2_0); - tbl_A = phy_cal_table_rate_A_1_2_0; - } else if ((major == 0x1) && (minor == 0x003) && (step == 0x0000)) { - tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_3_0); - tbl_A = phy_cal_table_rate_A_1_3_0; - } else { - dev_err(ufs_qcom_phy->dev, "%s: Unknown UFS-PHY version, no calibration values\n", - __func__); - err = -ENODEV; - goto out; - } - - tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); - tbl_B = phy_cal_table_rate_B; - - err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A, - tbl_B, tbl_size_B, is_rate_B); - - if (err) - dev_err(ufs_qcom_phy->dev, "%s: ufs_qcom_phy_calibrate() failed %d\n", - __func__, err); - -out: - return err; -} - -static -void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common) -{ - phy_common->quirks = - UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; -} - -static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy) -{ - return 0; -} - -static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy) -{ - return 0; -} - -static -void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy *phy, bool val) -{ - bool hibern8_exit_after_pwr_collapse = phy->quirks & - UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; - - if (val) { - writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); - /* - * Before any transactions involving PHY, ensure PHY knows - * that it's analog rail is powered ON. - */ - mb(); - - if (hibern8_exit_after_pwr_collapse) { - /* - * Give atleast 1us delay after restoring PHY analog - * power. - */ - usleep_range(1, 2); - writel_relaxed(0x0A, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - writel_relaxed(0x08, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - /* - * Make sure workaround is deactivated before proceeding - * with normal PHY operations. - */ - mb(); - } - } else { - if (hibern8_exit_after_pwr_collapse) { - writel_relaxed(0x0A, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - writel_relaxed(0x02, phy->mmio + - QSERDES_COM_SYSCLK_EN_SEL_TXBAND); - /* - * Make sure that above workaround is activated before - * PHY analog power collapse. - */ - mb(); - } - - writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); - /* - * ensure that PHY knows its PHY analog rail is going - * to be powered down - */ - mb(); - } -} - -static -void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) -{ - writel_relaxed(val & UFS_PHY_TX_LANE_ENABLE_MASK, - phy->mmio + UFS_PHY_TX_LANE_ENABLE); - mb(); -} - -static inline void ufs_qcom_phy_qmp_20nm_start_serdes(struct ufs_qcom_phy *phy) -{ - u32 tmp; - - tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); - tmp &= ~MASK_SERDES_START; - tmp |= (1 << OFFSET_SERDES_START); - writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); - mb(); -} - -static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) -{ - int err = 0; - u32 val; - - err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, - val, (val & MASK_PCS_READY), 10, 1000000); - if (err) - dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", - __func__, err); - return err; -} - -static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { - .init = ufs_qcom_phy_qmp_20nm_init, - .exit = ufs_qcom_phy_qmp_20nm_exit, - .power_on = ufs_qcom_phy_power_on, - .power_off = ufs_qcom_phy_power_off, - .owner = THIS_MODULE, -}; - -static struct ufs_qcom_phy_specific_ops phy_20nm_ops = { - .calibrate_phy = ufs_qcom_phy_qmp_20nm_phy_calibrate, - .start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes, - .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready, - .set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable, - .power_control = ufs_qcom_phy_qmp_20nm_power_control, -}; - -static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct phy *generic_phy; - struct ufs_qcom_phy_qmp_20nm *phy; - struct ufs_qcom_phy *phy_common; - int err = 0; - - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) { - err = -ENOMEM; - goto out; - } - phy_common = &phy->common_cfg; - - generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common, - &ufs_qcom_phy_qmp_20nm_phy_ops, &phy_20nm_ops); - - if (!generic_phy) { - err = -EIO; - goto out; - } - - err = ufs_qcom_phy_init_clks(phy_common); - if (err) - goto out; - - err = ufs_qcom_phy_init_vregulators(phy_common); - if (err) - goto out; - - ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common); - - phy_set_drvdata(generic_phy, phy); - - strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name)); - -out: - return err; -} - -static const struct of_device_id ufs_qcom_phy_qmp_20nm_of_match[] = { - {.compatible = "qcom,ufs-phy-qmp-20nm"}, - {}, -}; -MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_20nm_of_match); - -static struct platform_driver ufs_qcom_phy_qmp_20nm_driver = { - .probe = ufs_qcom_phy_qmp_20nm_probe, - .driver = { - .of_match_table = ufs_qcom_phy_qmp_20nm_of_match, - .name = "ufs_qcom_phy_qmp_20nm", - }, -}; - -module_platform_driver(ufs_qcom_phy_qmp_20nm_driver); - -MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 20nm"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.h b/drivers/phy/phy-qcom-ufs-qmp-20nm.h deleted file mode 100644 index 4f3076bb3d71..000000000000 --- a/drivers/phy/phy-qcom-ufs-qmp-20nm.h +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#ifndef UFS_QCOM_PHY_QMP_20NM_H_ -#define UFS_QCOM_PHY_QMP_20NM_H_ - -#include "phy-qcom-ufs-i.h" - -/* QCOM UFS PHY control registers */ - -#define COM_OFF(x) (0x000 + x) -#define PHY_OFF(x) (0xC00 + x) -#define TX_OFF(n, x) (0x400 + (0x400 * n) + x) -#define RX_OFF(n, x) (0x600 + (0x400 * n) + x) - -/* UFS PHY PLL block registers */ -#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x0) -#define QSERDES_COM_PLL_VCOTAIL_EN COM_OFF(0x04) -#define QSERDES_COM_PLL_CNTRL COM_OFF(0x14) -#define QSERDES_COM_PLL_IP_SETI COM_OFF(0x24) -#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL COM_OFF(0x28) -#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x30) -#define QSERDES_COM_PLL_CP_SETI COM_OFF(0x34) -#define QSERDES_COM_PLL_IP_SETP COM_OFF(0x38) -#define QSERDES_COM_PLL_CP_SETP COM_OFF(0x3C) -#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND COM_OFF(0x48) -#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0x4C) -#define QSERDES_COM_RESETSM_CNTRL2 COM_OFF(0x50) -#define QSERDES_COM_PLLLOCK_CMP1 COM_OFF(0x90) -#define QSERDES_COM_PLLLOCK_CMP2 COM_OFF(0x94) -#define QSERDES_COM_PLLLOCK_CMP3 COM_OFF(0x98) -#define QSERDES_COM_PLLLOCK_CMP_EN COM_OFF(0x9C) -#define QSERDES_COM_BGTC COM_OFF(0xA0) -#define QSERDES_COM_DEC_START1 COM_OFF(0xAC) -#define QSERDES_COM_PLL_AMP_OS COM_OFF(0xB0) -#define QSERDES_COM_RES_CODE_UP_OFFSET COM_OFF(0xD8) -#define QSERDES_COM_RES_CODE_DN_OFFSET COM_OFF(0xDC) -#define QSERDES_COM_DIV_FRAC_START1 COM_OFF(0x100) -#define QSERDES_COM_DIV_FRAC_START2 COM_OFF(0x104) -#define QSERDES_COM_DIV_FRAC_START3 COM_OFF(0x108) -#define QSERDES_COM_DEC_START2 COM_OFF(0x10C) -#define QSERDES_COM_PLL_RXTXEPCLK_EN COM_OFF(0x110) -#define QSERDES_COM_PLL_CRCTRL COM_OFF(0x114) -#define QSERDES_COM_PLL_CLKEPDIV COM_OFF(0x118) - -/* TX LANE n (0, 1) registers */ -#define QSERDES_TX_EMP_POST1_LVL(n) TX_OFF(n, 0x08) -#define QSERDES_TX_DRV_LVL(n) TX_OFF(n, 0x0C) -#define QSERDES_TX_LANE_MODE(n) TX_OFF(n, 0x54) - -/* RX LANE n (0, 1) registers */ -#define QSERDES_RX_CDR_CONTROL1(n) RX_OFF(n, 0x0) -#define QSERDES_RX_CDR_CONTROL_HALF(n) RX_OFF(n, 0x8) -#define QSERDES_RX_RX_EQ_GAIN1_LSB(n) RX_OFF(n, 0xA8) -#define QSERDES_RX_RX_EQ_GAIN1_MSB(n) RX_OFF(n, 0xAC) -#define QSERDES_RX_RX_EQ_GAIN2_LSB(n) RX_OFF(n, 0xB0) -#define QSERDES_RX_RX_EQ_GAIN2_MSB(n) RX_OFF(n, 0xB4) -#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(n) RX_OFF(n, 0xBC) -#define QSERDES_RX_CDR_CONTROL_QUARTER(n) RX_OFF(n, 0xC) -#define QSERDES_RX_SIGDET_CNTRL(n) RX_OFF(n, 0x100) - -/* UFS PHY registers */ -#define UFS_PHY_PHY_START PHY_OFF(0x00) -#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x4) -#define UFS_PHY_TX_LANE_ENABLE PHY_OFF(0x44) -#define UFS_PHY_PWM_G1_CLK_DIVIDER PHY_OFF(0x08) -#define UFS_PHY_PWM_G2_CLK_DIVIDER PHY_OFF(0x0C) -#define UFS_PHY_PWM_G3_CLK_DIVIDER PHY_OFF(0x10) -#define UFS_PHY_PWM_G4_CLK_DIVIDER PHY_OFF(0x14) -#define UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER PHY_OFF(0x34) -#define UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER PHY_OFF(0x38) -#define UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER PHY_OFF(0x3C) -#define UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER PHY_OFF(0x40) -#define UFS_PHY_OMC_STATUS_RDVAL PHY_OFF(0x68) -#define UFS_PHY_LINE_RESET_TIME PHY_OFF(0x28) -#define UFS_PHY_LINE_RESET_GRANULARITY PHY_OFF(0x2C) -#define UFS_PHY_TSYNC_RSYNC_CNTL PHY_OFF(0x48) -#define UFS_PHY_PLL_CNTL PHY_OFF(0x50) -#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x54) -#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x5C) -#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL PHY_OFF(0x58) -#define UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL PHY_OFF(0x60) -#define UFS_PHY_CFG_CHANGE_CNT_VAL PHY_OFF(0x64) -#define UFS_PHY_RX_SYNC_WAIT_TIME PHY_OFF(0x6C) -#define UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB4) -#define UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE0) -#define UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB8) -#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE4) -#define UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xBC) -#define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xE8) -#define UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY PHY_OFF(0xFC) -#define UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY PHY_OFF(0x100) -#define UFS_PHY_RX_SIGDET_CTRL3 PHY_OFF(0x14c) -#define UFS_PHY_RMMI_ATTR_CTRL PHY_OFF(0x160) -#define UFS_PHY_RMMI_RX_CFGUPDT_L1 (1 << 7) -#define UFS_PHY_RMMI_TX_CFGUPDT_L1 (1 << 6) -#define UFS_PHY_RMMI_CFGWR_L1 (1 << 5) -#define UFS_PHY_RMMI_CFGRD_L1 (1 << 4) -#define UFS_PHY_RMMI_RX_CFGUPDT_L0 (1 << 3) -#define UFS_PHY_RMMI_TX_CFGUPDT_L0 (1 << 2) -#define UFS_PHY_RMMI_CFGWR_L0 (1 << 1) -#define UFS_PHY_RMMI_CFGRD_L0 (1 << 0) -#define UFS_PHY_RMMI_ATTRID PHY_OFF(0x164) -#define UFS_PHY_RMMI_ATTRWRVAL PHY_OFF(0x168) -#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS PHY_OFF(0x16C) -#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS PHY_OFF(0x170) -#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x174) - -#define UFS_PHY_TX_LANE_ENABLE_MASK 0x3 - -/* - * This structure represents the 20nm specific phy. - * common_cfg MUST remain the first field in this structure - * in case extra fields are added. This way, when calling - * get_ufs_qcom_phy() of generic phy, we can extract the - * common phy structure (struct ufs_qcom_phy) out of it - * regardless of the relevant specific phy. - */ -struct ufs_qcom_phy_qmp_20nm { - struct ufs_qcom_phy common_cfg; -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_2_0[] = { - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x1b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x0f), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(0), 0x2F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(0), 0x20), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(1), 0x2F), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(1), 0x20), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3), -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_3_0[] = { - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x2b), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x38), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x3c), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_UP_OFFSET, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_DN_OFFSET, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x40), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3), -}; - -static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0x65), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x1e), -}; - -#endif diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c deleted file mode 100644 index 43865ef340e2..000000000000 --- a/drivers/phy/phy-qcom-ufs.c +++ /dev/null @@ -1,691 +0,0 @@ -/* - * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include "phy-qcom-ufs-i.h" - -#define MAX_PROP_NAME 32 -#define VDDA_PHY_MIN_UV 1000000 -#define VDDA_PHY_MAX_UV 1000000 -#define VDDA_PLL_MIN_UV 1800000 -#define VDDA_PLL_MAX_UV 1800000 -#define VDDP_REF_CLK_MIN_UV 1200000 -#define VDDP_REF_CLK_MAX_UV 1200000 - -int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, - struct ufs_qcom_phy_calibration *tbl_A, - int tbl_size_A, - struct ufs_qcom_phy_calibration *tbl_B, - int tbl_size_B, bool is_rate_B) -{ - int i; - int ret = 0; - - if (!tbl_A) { - dev_err(ufs_qcom_phy->dev, "%s: tbl_A is NULL", __func__); - ret = EINVAL; - goto out; - } - - for (i = 0; i < tbl_size_A; i++) - writel_relaxed(tbl_A[i].cfg_value, - ufs_qcom_phy->mmio + tbl_A[i].reg_offset); - - /* - * In case we would like to work in rate B, we need - * to override a registers that were configured in rate A table - * with registers of rate B table. - * table. - */ - if (is_rate_B) { - if (!tbl_B) { - dev_err(ufs_qcom_phy->dev, "%s: tbl_B is NULL", - __func__); - ret = EINVAL; - goto out; - } - - for (i = 0; i < tbl_size_B; i++) - writel_relaxed(tbl_B[i].cfg_value, - ufs_qcom_phy->mmio + tbl_B[i].reg_offset); - } - - /* flush buffered writes */ - mb(); - -out: - return ret; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate); - -/* - * This assumes the embedded phy structure inside generic_phy is of type - * struct ufs_qcom_phy. In order to function properly it's crucial - * to keep the embedded struct "struct ufs_qcom_phy common_cfg" - * as the first inside generic_phy. - */ -struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy) -{ - return (struct ufs_qcom_phy *)phy_get_drvdata(generic_phy); -} -EXPORT_SYMBOL_GPL(get_ufs_qcom_phy); - -static -int ufs_qcom_phy_base_init(struct platform_device *pdev, - struct ufs_qcom_phy *phy_common) -{ - struct device *dev = &pdev->dev; - struct resource *res; - int err = 0; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem"); - phy_common->mmio = devm_ioremap_resource(dev, res); - if (IS_ERR((void const *)phy_common->mmio)) { - err = PTR_ERR((void const *)phy_common->mmio); - phy_common->mmio = NULL; - dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n", - __func__, err); - return err; - } - - /* "dev_ref_clk_ctrl_mem" is optional resource */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "dev_ref_clk_ctrl_mem"); - phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res); - if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio)) - phy_common->dev_ref_clk_ctrl_mmio = NULL; - - return 0; -} - -struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, - struct ufs_qcom_phy *common_cfg, - const struct phy_ops *ufs_qcom_phy_gen_ops, - struct ufs_qcom_phy_specific_ops *phy_spec_ops) -{ - int err; - struct device *dev = &pdev->dev; - struct phy *generic_phy = NULL; - struct phy_provider *phy_provider; - - err = ufs_qcom_phy_base_init(pdev, common_cfg); - if (err) { - dev_err(dev, "%s: phy base init failed %d\n", __func__, err); - goto out; - } - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) { - err = PTR_ERR(phy_provider); - dev_err(dev, "%s: failed to register phy %d\n", __func__, err); - goto out; - } - - generic_phy = devm_phy_create(dev, NULL, ufs_qcom_phy_gen_ops); - if (IS_ERR(generic_phy)) { - err = PTR_ERR(generic_phy); - dev_err(dev, "%s: failed to create phy %d\n", __func__, err); - generic_phy = NULL; - goto out; - } - - common_cfg->phy_spec_ops = phy_spec_ops; - common_cfg->dev = dev; - -out: - return generic_phy; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe); - -static int __ufs_qcom_phy_clk_get(struct device *dev, - const char *name, struct clk **clk_out, bool err_print) -{ - struct clk *clk; - int err = 0; - - clk = devm_clk_get(dev, name); - if (IS_ERR(clk)) { - err = PTR_ERR(clk); - if (err_print) - dev_err(dev, "failed to get %s err %d", name, err); - } else { - *clk_out = clk; - } - - return err; -} - -static int ufs_qcom_phy_clk_get(struct device *dev, - const char *name, struct clk **clk_out) -{ - return __ufs_qcom_phy_clk_get(dev, name, clk_out, true); -} - -int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common) -{ - int err; - - if (of_device_is_compatible(phy_common->dev->of_node, - "qcom,msm8996-ufs-phy-qmp-14nm")) - goto skip_txrx_clk; - - err = ufs_qcom_phy_clk_get(phy_common->dev, "tx_iface_clk", - &phy_common->tx_iface_clk); - if (err) - goto out; - - err = ufs_qcom_phy_clk_get(phy_common->dev, "rx_iface_clk", - &phy_common->rx_iface_clk); - if (err) - goto out; - -skip_txrx_clk: - err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_src", - &phy_common->ref_clk_src); - if (err) - goto out; - - /* - * "ref_clk_parent" is optional hence don't abort init if it's not - * found. - */ - __ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_parent", - &phy_common->ref_clk_parent, false); - - err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk", - &phy_common->ref_clk); - -out: - return err; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_clks); - -static int ufs_qcom_phy_init_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg, - const char *name) -{ - int err = 0; - - char prop_name[MAX_PROP_NAME]; - - vreg->name = name; - vreg->reg = devm_regulator_get(dev, name); - if (IS_ERR(vreg->reg)) { - err = PTR_ERR(vreg->reg); - dev_err(dev, "failed to get %s, %d\n", name, err); - goto out; - } - - if (dev->of_node) { - snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name); - err = of_property_read_u32(dev->of_node, - prop_name, &vreg->max_uA); - if (err && err != -EINVAL) { - dev_err(dev, "%s: failed to read %s\n", - __func__, prop_name); - goto out; - } else if (err == -EINVAL || !vreg->max_uA) { - if (regulator_count_voltages(vreg->reg) > 0) { - dev_err(dev, "%s: %s is mandatory\n", - __func__, prop_name); - goto out; - } - err = 0; - } - } - - if (!strcmp(name, "vdda-pll")) { - vreg->max_uV = VDDA_PLL_MAX_UV; - vreg->min_uV = VDDA_PLL_MIN_UV; - } else if (!strcmp(name, "vdda-phy")) { - vreg->max_uV = VDDA_PHY_MAX_UV; - vreg->min_uV = VDDA_PHY_MIN_UV; - } else if (!strcmp(name, "vddp-ref-clk")) { - vreg->max_uV = VDDP_REF_CLK_MAX_UV; - vreg->min_uV = VDDP_REF_CLK_MIN_UV; - } - -out: - return err; -} - -int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common) -{ - int err; - - err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_pll, - "vdda-pll"); - if (err) - goto out; - - err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_phy, - "vdda-phy"); - - if (err) - goto out; - - err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk, - "vddp-ref-clk"); - -out: - return err; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_vregulators); - -static int ufs_qcom_phy_cfg_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg, bool on) -{ - int ret = 0; - struct regulator *reg = vreg->reg; - const char *name = vreg->name; - int min_uV; - int uA_load; - - if (regulator_count_voltages(reg) > 0) { - min_uV = on ? vreg->min_uV : 0; - ret = regulator_set_voltage(reg, min_uV, vreg->max_uV); - if (ret) { - dev_err(dev, "%s: %s set voltage failed, err=%d\n", - __func__, name, ret); - goto out; - } - uA_load = on ? vreg->max_uA : 0; - ret = regulator_set_load(reg, uA_load); - if (ret >= 0) { - /* - * regulator_set_load() returns new regulator - * mode upon success. - */ - ret = 0; - } else { - dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n", - __func__, name, uA_load, ret); - goto out; - } - } -out: - return ret; -} - -static int ufs_qcom_phy_enable_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg) -{ - int ret = 0; - - if (!vreg || vreg->enabled) - goto out; - - ret = ufs_qcom_phy_cfg_vreg(dev, vreg, true); - if (ret) { - dev_err(dev, "%s: ufs_qcom_phy_cfg_vreg() failed, err=%d\n", - __func__, ret); - goto out; - } - - ret = regulator_enable(vreg->reg); - if (ret) { - dev_err(dev, "%s: enable failed, err=%d\n", - __func__, ret); - goto out; - } - - vreg->enabled = true; -out: - return ret; -} - -static int ufs_qcom_phy_enable_ref_clk(struct ufs_qcom_phy *phy) -{ - int ret = 0; - - if (phy->is_ref_clk_enabled) - goto out; - - /* - * reference clock is propagated in a daisy-chained manner from - * source to phy, so ungate them at each stage. - */ - ret = clk_prepare_enable(phy->ref_clk_src); - if (ret) { - dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n", - __func__, ret); - goto out; - } - - /* - * "ref_clk_parent" is optional clock hence make sure that clk reference - * is available before trying to enable the clock. - */ - if (phy->ref_clk_parent) { - ret = clk_prepare_enable(phy->ref_clk_parent); - if (ret) { - dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n", - __func__, ret); - goto out_disable_src; - } - } - - ret = clk_prepare_enable(phy->ref_clk); - if (ret) { - dev_err(phy->dev, "%s: ref_clk enable failed %d\n", - __func__, ret); - goto out_disable_parent; - } - - phy->is_ref_clk_enabled = true; - goto out; - -out_disable_parent: - if (phy->ref_clk_parent) - clk_disable_unprepare(phy->ref_clk_parent); -out_disable_src: - clk_disable_unprepare(phy->ref_clk_src); -out: - return ret; -} - -static int ufs_qcom_phy_disable_vreg(struct device *dev, - struct ufs_qcom_phy_vreg *vreg) -{ - int ret = 0; - - if (!vreg || !vreg->enabled) - goto out; - - ret = regulator_disable(vreg->reg); - - if (!ret) { - /* ignore errors on applying disable config */ - ufs_qcom_phy_cfg_vreg(dev, vreg, false); - vreg->enabled = false; - } else { - dev_err(dev, "%s: %s disable failed, err=%d\n", - __func__, vreg->name, ret); - } -out: - return ret; -} - -static void ufs_qcom_phy_disable_ref_clk(struct ufs_qcom_phy *phy) -{ - if (phy->is_ref_clk_enabled) { - clk_disable_unprepare(phy->ref_clk); - /* - * "ref_clk_parent" is optional clock hence make sure that clk - * reference is available before trying to disable the clock. - */ - if (phy->ref_clk_parent) - clk_disable_unprepare(phy->ref_clk_parent); - clk_disable_unprepare(phy->ref_clk_src); - phy->is_ref_clk_enabled = false; - } -} - -#define UFS_REF_CLK_EN (1 << 5) - -static void ufs_qcom_phy_dev_ref_clk_ctrl(struct phy *generic_phy, bool enable) -{ - struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy); - - if (phy->dev_ref_clk_ctrl_mmio && - (enable ^ phy->is_dev_ref_clk_enabled)) { - u32 temp = readl_relaxed(phy->dev_ref_clk_ctrl_mmio); - - if (enable) - temp |= UFS_REF_CLK_EN; - else - temp &= ~UFS_REF_CLK_EN; - - /* - * If we are here to disable this clock immediately after - * entering into hibern8, we need to make sure that device - * ref_clk is active atleast 1us after the hibern8 enter. - */ - if (!enable) - udelay(1); - - writel_relaxed(temp, phy->dev_ref_clk_ctrl_mmio); - /* ensure that ref_clk is enabled/disabled before we return */ - wmb(); - /* - * If we call hibern8 exit after this, we need to make sure that - * device ref_clk is stable for atleast 1us before the hibern8 - * exit command. - */ - if (enable) - udelay(1); - - phy->is_dev_ref_clk_enabled = enable; - } -} - -void ufs_qcom_phy_enable_dev_ref_clk(struct phy *generic_phy) -{ - ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, true); -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_enable_dev_ref_clk); - -void ufs_qcom_phy_disable_dev_ref_clk(struct phy *generic_phy) -{ - ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, false); -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_disable_dev_ref_clk); - -/* Turn ON M-PHY RMMI interface clocks */ -static int ufs_qcom_phy_enable_iface_clk(struct ufs_qcom_phy *phy) -{ - int ret = 0; - - if (phy->is_iface_clk_enabled) - goto out; - - ret = clk_prepare_enable(phy->tx_iface_clk); - if (ret) { - dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n", - __func__, ret); - goto out; - } - ret = clk_prepare_enable(phy->rx_iface_clk); - if (ret) { - clk_disable_unprepare(phy->tx_iface_clk); - dev_err(phy->dev, "%s: rx_iface_clk enable failed %d. disabling also tx_iface_clk\n", - __func__, ret); - goto out; - } - phy->is_iface_clk_enabled = true; - -out: - return ret; -} - -/* Turn OFF M-PHY RMMI interface clocks */ -void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy) -{ - if (phy->is_iface_clk_enabled) { - clk_disable_unprepare(phy->tx_iface_clk); - clk_disable_unprepare(phy->rx_iface_clk); - phy->is_iface_clk_enabled = false; - } -} - -int ufs_qcom_phy_start_serdes(struct phy *generic_phy) -{ - struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); - int ret = 0; - - if (!ufs_qcom_phy->phy_spec_ops->start_serdes) { - dev_err(ufs_qcom_phy->dev, "%s: start_serdes() callback is not supported\n", - __func__); - ret = -ENOTSUPP; - } else { - ufs_qcom_phy->phy_spec_ops->start_serdes(ufs_qcom_phy); - } - - return ret; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_start_serdes); - -int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes) -{ - struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); - int ret = 0; - - if (!ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable) { - dev_err(ufs_qcom_phy->dev, "%s: set_tx_lane_enable() callback is not supported\n", - __func__); - ret = -ENOTSUPP; - } else { - ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable(ufs_qcom_phy, - tx_lanes); - } - - return ret; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_set_tx_lane_enable); - -void ufs_qcom_phy_save_controller_version(struct phy *generic_phy, - u8 major, u16 minor, u16 step) -{ - struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); - - ufs_qcom_phy->host_ctrl_rev_major = major; - ufs_qcom_phy->host_ctrl_rev_minor = minor; - ufs_qcom_phy->host_ctrl_rev_step = step; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version); - -int ufs_qcom_phy_calibrate_phy(struct phy *generic_phy, bool is_rate_B) -{ - struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); - int ret = 0; - - if (!ufs_qcom_phy->phy_spec_ops->calibrate_phy) { - dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() callback is not supported\n", - __func__); - ret = -ENOTSUPP; - } else { - ret = ufs_qcom_phy->phy_spec_ops-> - calibrate_phy(ufs_qcom_phy, is_rate_B); - if (ret) - dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() failed %d\n", - __func__, ret); - } - - return ret; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate_phy); - -int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy) -{ - struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); - - if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) { - dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n", - __func__); - return -ENOTSUPP; - } - - return ufs_qcom_phy->phy_spec_ops-> - is_physical_coding_sublayer_ready(ufs_qcom_phy); -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_is_pcs_ready); - -int ufs_qcom_phy_power_on(struct phy *generic_phy) -{ - struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); - struct device *dev = phy_common->dev; - int err; - - if (phy_common->is_powered_on) - return 0; - - err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy); - if (err) { - dev_err(dev, "%s enable vdda_phy failed, err=%d\n", - __func__, err); - goto out; - } - - phy_common->phy_spec_ops->power_control(phy_common, true); - - /* vdda_pll also enables ref clock LDOs so enable it first */ - err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_pll); - if (err) { - dev_err(dev, "%s enable vdda_pll failed, err=%d\n", - __func__, err); - goto out_disable_phy; - } - - err = ufs_qcom_phy_enable_iface_clk(phy_common); - if (err) { - dev_err(dev, "%s enable phy iface clock failed, err=%d\n", - __func__, err); - goto out_disable_pll; - } - - err = ufs_qcom_phy_enable_ref_clk(phy_common); - if (err) { - dev_err(dev, "%s enable phy ref clock failed, err=%d\n", - __func__, err); - goto out_disable_iface_clk; - } - - /* enable device PHY ref_clk pad rail */ - if (phy_common->vddp_ref_clk.reg) { - err = ufs_qcom_phy_enable_vreg(dev, - &phy_common->vddp_ref_clk); - if (err) { - dev_err(dev, "%s enable vddp_ref_clk failed, err=%d\n", - __func__, err); - goto out_disable_ref_clk; - } - } - - phy_common->is_powered_on = true; - goto out; - -out_disable_ref_clk: - ufs_qcom_phy_disable_ref_clk(phy_common); -out_disable_iface_clk: - ufs_qcom_phy_disable_iface_clk(phy_common); -out_disable_pll: - ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_pll); -out_disable_phy: - ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_phy); -out: - return err; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_on); - -int ufs_qcom_phy_power_off(struct phy *generic_phy) -{ - struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); - - if (!phy_common->is_powered_on) - return 0; - - phy_common->phy_spec_ops->power_control(phy_common, false); - - if (phy_common->vddp_ref_clk.reg) - ufs_qcom_phy_disable_vreg(phy_common->dev, - &phy_common->vddp_ref_clk); - ufs_qcom_phy_disable_ref_clk(phy_common); - ufs_qcom_phy_disable_iface_clk(phy_common); - - ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_pll); - ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_phy); - phy_common->is_powered_on = false; - - return 0; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off); diff --git a/drivers/phy/phy-qcom-usb-hs.c b/drivers/phy/phy-qcom-usb-hs.c deleted file mode 100644 index 4b20abc3ae2f..000000000000 --- a/drivers/phy/phy-qcom-usb-hs.c +++ /dev/null @@ -1,295 +0,0 @@ -/** - * Copyright (C) 2016 Linaro Ltd - * - * 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 - -#define ULPI_PWR_CLK_MNG_REG 0x88 -# define ULPI_PWR_OTG_COMP_DISABLE BIT(0) - -#define ULPI_MISC_A 0x96 -# define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1) -# define ULPI_MISC_A_VBUSVLDEXT BIT(0) - - -struct ulpi_seq { - u8 addr; - u8 val; -}; - -struct qcom_usb_hs_phy { - struct ulpi *ulpi; - struct phy *phy; - struct clk *ref_clk; - struct clk *sleep_clk; - struct regulator *v1p8; - struct regulator *v3p3; - struct reset_control *reset; - struct ulpi_seq *init_seq; - struct extcon_dev *vbus_edev; - struct notifier_block vbus_notify; -}; - -static int qcom_usb_hs_phy_set_mode(struct phy *phy, enum phy_mode mode) -{ - struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); - u8 addr; - int ret; - - if (!uphy->vbus_edev) { - u8 val = 0; - - switch (mode) { - case PHY_MODE_USB_OTG: - case PHY_MODE_USB_HOST: - val |= ULPI_INT_IDGRD; - case PHY_MODE_USB_DEVICE: - val |= ULPI_INT_SESS_VALID; - default: - break; - } - - ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_RISE, val); - if (ret) - return ret; - ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_FALL, val); - } else { - switch (mode) { - case PHY_MODE_USB_OTG: - case PHY_MODE_USB_DEVICE: - addr = ULPI_SET(ULPI_MISC_A); - break; - case PHY_MODE_USB_HOST: - addr = ULPI_CLR(ULPI_MISC_A); - break; - default: - return -EINVAL; - } - - ret = ulpi_write(uphy->ulpi, ULPI_SET(ULPI_PWR_CLK_MNG_REG), - ULPI_PWR_OTG_COMP_DISABLE); - if (ret) - return ret; - ret = ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXTSEL); - } - - return ret; -} - -static int -qcom_usb_hs_phy_vbus_notifier(struct notifier_block *nb, unsigned long event, - void *ptr) -{ - struct qcom_usb_hs_phy *uphy; - u8 addr; - - uphy = container_of(nb, struct qcom_usb_hs_phy, vbus_notify); - - if (event) - addr = ULPI_SET(ULPI_MISC_A); - else - addr = ULPI_CLR(ULPI_MISC_A); - - return ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXT); -} - -static int qcom_usb_hs_phy_power_on(struct phy *phy) -{ - struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); - struct ulpi *ulpi = uphy->ulpi; - const struct ulpi_seq *seq; - int ret, state; - - ret = clk_prepare_enable(uphy->ref_clk); - if (ret) - return ret; - - ret = clk_prepare_enable(uphy->sleep_clk); - if (ret) - goto err_sleep; - - ret = regulator_set_load(uphy->v1p8, 50000); - if (ret < 0) - goto err_1p8; - - ret = regulator_enable(uphy->v1p8); - if (ret) - goto err_1p8; - - ret = regulator_set_voltage_triplet(uphy->v3p3, 3050000, 3300000, - 3300000); - if (ret) - goto err_3p3; - - ret = regulator_set_load(uphy->v3p3, 50000); - if (ret < 0) - goto err_3p3; - - ret = regulator_enable(uphy->v3p3); - if (ret) - goto err_3p3; - - for (seq = uphy->init_seq; seq->addr; seq++) { - ret = ulpi_write(ulpi, ULPI_EXT_VENDOR_SPECIFIC + seq->addr, - seq->val); - if (ret) - goto err_ulpi; - } - - if (uphy->reset) { - ret = reset_control_reset(uphy->reset); - if (ret) - goto err_ulpi; - } - - if (uphy->vbus_edev) { - state = extcon_get_cable_state_(uphy->vbus_edev, EXTCON_USB); - /* setup initial state */ - qcom_usb_hs_phy_vbus_notifier(&uphy->vbus_notify, state, - uphy->vbus_edev); - ret = extcon_register_notifier(uphy->vbus_edev, EXTCON_USB, - &uphy->vbus_notify); - if (ret) - goto err_ulpi; - } - - return 0; -err_ulpi: - regulator_disable(uphy->v3p3); -err_3p3: - regulator_disable(uphy->v1p8); -err_1p8: - clk_disable_unprepare(uphy->sleep_clk); -err_sleep: - clk_disable_unprepare(uphy->ref_clk); - return ret; -} - -static int qcom_usb_hs_phy_power_off(struct phy *phy) -{ - int ret; - struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); - - if (uphy->vbus_edev) { - ret = extcon_unregister_notifier(uphy->vbus_edev, EXTCON_USB, - &uphy->vbus_notify); - if (ret) - return ret; - } - - regulator_disable(uphy->v3p3); - regulator_disable(uphy->v1p8); - clk_disable_unprepare(uphy->sleep_clk); - clk_disable_unprepare(uphy->ref_clk); - - return 0; -} - -static const struct phy_ops qcom_usb_hs_phy_ops = { - .power_on = qcom_usb_hs_phy_power_on, - .power_off = qcom_usb_hs_phy_power_off, - .set_mode = qcom_usb_hs_phy_set_mode, - .owner = THIS_MODULE, -}; - -static int qcom_usb_hs_phy_probe(struct ulpi *ulpi) -{ - struct qcom_usb_hs_phy *uphy; - struct phy_provider *p; - struct clk *clk; - struct regulator *reg; - struct reset_control *reset; - int size; - int ret; - - uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); - if (!uphy) - return -ENOMEM; - ulpi_set_drvdata(ulpi, uphy); - uphy->ulpi = ulpi; - - size = of_property_count_u8_elems(ulpi->dev.of_node, "qcom,init-seq"); - if (size < 0) - size = 0; - uphy->init_seq = devm_kmalloc_array(&ulpi->dev, (size / 2) + 1, - sizeof(*uphy->init_seq), GFP_KERNEL); - if (!uphy->init_seq) - return -ENOMEM; - ret = of_property_read_u8_array(ulpi->dev.of_node, "qcom,init-seq", - (u8 *)uphy->init_seq, size); - if (ret && size) - return ret; - /* NUL terminate */ - uphy->init_seq[size / 2].addr = uphy->init_seq[size / 2].val = 0; - - uphy->ref_clk = clk = devm_clk_get(&ulpi->dev, "ref"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - uphy->sleep_clk = clk = devm_clk_get(&ulpi->dev, "sleep"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - uphy->v1p8 = reg = devm_regulator_get(&ulpi->dev, "v1p8"); - if (IS_ERR(reg)) - return PTR_ERR(reg); - - uphy->v3p3 = reg = devm_regulator_get(&ulpi->dev, "v3p3"); - if (IS_ERR(reg)) - return PTR_ERR(reg); - - uphy->reset = reset = devm_reset_control_get(&ulpi->dev, "por"); - if (IS_ERR(reset)) { - if (PTR_ERR(reset) == -EPROBE_DEFER) - return PTR_ERR(reset); - uphy->reset = NULL; - } - - uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node, - &qcom_usb_hs_phy_ops); - if (IS_ERR(uphy->phy)) - return PTR_ERR(uphy->phy); - - uphy->vbus_edev = extcon_get_edev_by_phandle(&ulpi->dev, 0); - if (IS_ERR(uphy->vbus_edev)) { - if (PTR_ERR(uphy->vbus_edev) != -ENODEV) - return PTR_ERR(uphy->vbus_edev); - uphy->vbus_edev = NULL; - } - - uphy->vbus_notify.notifier_call = qcom_usb_hs_phy_vbus_notifier; - phy_set_drvdata(uphy->phy, uphy); - - p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate); - return PTR_ERR_OR_ZERO(p); -} - -static const struct of_device_id qcom_usb_hs_phy_match[] = { - { .compatible = "qcom,usb-hs-phy", }, - { } -}; -MODULE_DEVICE_TABLE(of, qcom_usb_hs_phy_match); - -static struct ulpi_driver qcom_usb_hs_phy_driver = { - .probe = qcom_usb_hs_phy_probe, - .driver = { - .name = "qcom_usb_hs_phy", - .of_match_table = qcom_usb_hs_phy_match, - }, -}; -module_ulpi_driver(qcom_usb_hs_phy_driver); - -MODULE_DESCRIPTION("Qualcomm USB HS phy"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-qcom-usb-hsic.c b/drivers/phy/phy-qcom-usb-hsic.c deleted file mode 100644 index c110563a73cb..000000000000 --- a/drivers/phy/phy-qcom-usb-hsic.c +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Copyright (C) 2016 Linaro Ltd - * - * 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 - -#define ULPI_HSIC_CFG 0x30 -#define ULPI_HSIC_IO_CAL 0x33 - -struct qcom_usb_hsic_phy { - struct ulpi *ulpi; - struct phy *phy; - struct pinctrl *pctl; - struct clk *phy_clk; - struct clk *cal_clk; - struct clk *cal_sleep_clk; -}; - -static int qcom_usb_hsic_phy_power_on(struct phy *phy) -{ - struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); - struct ulpi *ulpi = uphy->ulpi; - struct pinctrl_state *pins_default; - int ret; - - ret = clk_prepare_enable(uphy->phy_clk); - if (ret) - return ret; - - ret = clk_prepare_enable(uphy->cal_clk); - if (ret) - goto err_cal; - - ret = clk_prepare_enable(uphy->cal_sleep_clk); - if (ret) - goto err_sleep; - - /* Set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */ - ret = ulpi_write(ulpi, ULPI_HSIC_IO_CAL, 0xff); - if (ret) - goto err_ulpi; - - /* Enable periodic IO calibration in HSIC_CFG register */ - ret = ulpi_write(ulpi, ULPI_HSIC_CFG, 0xa8); - if (ret) - goto err_ulpi; - - /* Configure pins for HSIC functionality */ - pins_default = pinctrl_lookup_state(uphy->pctl, PINCTRL_STATE_DEFAULT); - if (IS_ERR(pins_default)) - return PTR_ERR(pins_default); - - ret = pinctrl_select_state(uphy->pctl, pins_default); - if (ret) - goto err_ulpi; - - /* Enable HSIC mode in HSIC_CFG register */ - ret = ulpi_write(ulpi, ULPI_SET(ULPI_HSIC_CFG), 0x01); - if (ret) - goto err_ulpi; - - /* Disable auto-resume */ - ret = ulpi_write(ulpi, ULPI_CLR(ULPI_IFC_CTRL), - ULPI_IFC_CTRL_AUTORESUME); - if (ret) - goto err_ulpi; - - return ret; -err_ulpi: - clk_disable_unprepare(uphy->cal_sleep_clk); -err_sleep: - clk_disable_unprepare(uphy->cal_clk); -err_cal: - clk_disable_unprepare(uphy->phy_clk); - return ret; -} - -static int qcom_usb_hsic_phy_power_off(struct phy *phy) -{ - struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); - - clk_disable_unprepare(uphy->cal_sleep_clk); - clk_disable_unprepare(uphy->cal_clk); - clk_disable_unprepare(uphy->phy_clk); - - return 0; -} - -static const struct phy_ops qcom_usb_hsic_phy_ops = { - .power_on = qcom_usb_hsic_phy_power_on, - .power_off = qcom_usb_hsic_phy_power_off, - .owner = THIS_MODULE, -}; - -static int qcom_usb_hsic_phy_probe(struct ulpi *ulpi) -{ - struct qcom_usb_hsic_phy *uphy; - struct phy_provider *p; - struct clk *clk; - - uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); - if (!uphy) - return -ENOMEM; - ulpi_set_drvdata(ulpi, uphy); - - uphy->ulpi = ulpi; - uphy->pctl = devm_pinctrl_get(&ulpi->dev); - if (IS_ERR(uphy->pctl)) - return PTR_ERR(uphy->pctl); - - uphy->phy_clk = clk = devm_clk_get(&ulpi->dev, "phy"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - uphy->cal_clk = clk = devm_clk_get(&ulpi->dev, "cal"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - uphy->cal_sleep_clk = clk = devm_clk_get(&ulpi->dev, "cal_sleep"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node, - &qcom_usb_hsic_phy_ops); - if (IS_ERR(uphy->phy)) - return PTR_ERR(uphy->phy); - phy_set_drvdata(uphy->phy, uphy); - - p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate); - return PTR_ERR_OR_ZERO(p); -} - -static const struct of_device_id qcom_usb_hsic_phy_match[] = { - { .compatible = "qcom,usb-hsic-phy", }, - { } -}; -MODULE_DEVICE_TABLE(of, qcom_usb_hsic_phy_match); - -static struct ulpi_driver qcom_usb_hsic_phy_driver = { - .probe = qcom_usb_hsic_phy_probe, - .driver = { - .name = "qcom_usb_hsic_phy", - .of_match_table = qcom_usb_hsic_phy_match, - }, -}; -module_ulpi_driver(qcom_usb_hsic_phy_driver); - -MODULE_DESCRIPTION("Qualcomm USB HSIC phy"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-rcar-gen2.c b/drivers/phy/phy-rcar-gen2.c deleted file mode 100644 index 97d4dd6ea924..000000000000 --- a/drivers/phy/phy-rcar-gen2.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Renesas R-Car Gen2 PHY driver - * - * Copyright (C) 2014 Renesas Solutions Corp. - * Copyright (C) 2014 Cogent Embedded, Inc. - * - * 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 - -#define USBHS_LPSTS 0x02 -#define USBHS_UGCTRL 0x80 -#define USBHS_UGCTRL2 0x84 -#define USBHS_UGSTS 0x88 /* From technical update */ - -/* Low Power Status register (LPSTS) */ -#define USBHS_LPSTS_SUSPM 0x4000 - -/* USB General control register (UGCTRL) */ -#define USBHS_UGCTRL_CONNECT 0x00000004 -#define USBHS_UGCTRL_PLLRESET 0x00000001 - -/* USB General control register 2 (UGCTRL2) */ -#define USBHS_UGCTRL2_USB2SEL 0x80000000 -#define USBHS_UGCTRL2_USB2SEL_PCI 0x00000000 -#define USBHS_UGCTRL2_USB2SEL_USB30 0x80000000 -#define USBHS_UGCTRL2_USB0SEL 0x00000030 -#define USBHS_UGCTRL2_USB0SEL_PCI 0x00000010 -#define USBHS_UGCTRL2_USB0SEL_HS_USB 0x00000030 - -/* USB General status register (UGSTS) */ -#define USBHS_UGSTS_LOCK 0x00000100 /* From technical update */ - -#define PHYS_PER_CHANNEL 2 - -struct rcar_gen2_phy { - struct phy *phy; - struct rcar_gen2_channel *channel; - int number; - u32 select_value; -}; - -struct rcar_gen2_channel { - struct device_node *of_node; - struct rcar_gen2_phy_driver *drv; - struct rcar_gen2_phy phys[PHYS_PER_CHANNEL]; - int selected_phy; - u32 select_mask; -}; - -struct rcar_gen2_phy_driver { - void __iomem *base; - struct clk *clk; - spinlock_t lock; - int num_channels; - struct rcar_gen2_channel *channels; -}; - -static int rcar_gen2_phy_init(struct phy *p) -{ - struct rcar_gen2_phy *phy = phy_get_drvdata(p); - struct rcar_gen2_channel *channel = phy->channel; - struct rcar_gen2_phy_driver *drv = channel->drv; - unsigned long flags; - u32 ugctrl2; - - /* - * Try to acquire exclusive access to PHY. The first driver calling - * phy_init() on a given channel wins, and all attempts to use another - * PHY on this channel will fail until phy_exit() is called by the first - * driver. Achieving this with cmpxcgh() should be SMP-safe. - */ - if (cmpxchg(&channel->selected_phy, -1, phy->number) != -1) - return -EBUSY; - - clk_prepare_enable(drv->clk); - - spin_lock_irqsave(&drv->lock, flags); - ugctrl2 = readl(drv->base + USBHS_UGCTRL2); - ugctrl2 &= ~channel->select_mask; - ugctrl2 |= phy->select_value; - writel(ugctrl2, drv->base + USBHS_UGCTRL2); - spin_unlock_irqrestore(&drv->lock, flags); - return 0; -} - -static int rcar_gen2_phy_exit(struct phy *p) -{ - struct rcar_gen2_phy *phy = phy_get_drvdata(p); - struct rcar_gen2_channel *channel = phy->channel; - - clk_disable_unprepare(channel->drv->clk); - - channel->selected_phy = -1; - - return 0; -} - -static int rcar_gen2_phy_power_on(struct phy *p) -{ - struct rcar_gen2_phy *phy = phy_get_drvdata(p); - struct rcar_gen2_phy_driver *drv = phy->channel->drv; - void __iomem *base = drv->base; - unsigned long flags; - u32 value; - int err = 0, i; - - /* Skip if it's not USBHS */ - if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB) - return 0; - - spin_lock_irqsave(&drv->lock, flags); - - /* Power on USBHS PHY */ - value = readl(base + USBHS_UGCTRL); - value &= ~USBHS_UGCTRL_PLLRESET; - writel(value, base + USBHS_UGCTRL); - - value = readw(base + USBHS_LPSTS); - value |= USBHS_LPSTS_SUSPM; - writew(value, base + USBHS_LPSTS); - - for (i = 0; i < 20; i++) { - value = readl(base + USBHS_UGSTS); - if ((value & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) { - value = readl(base + USBHS_UGCTRL); - value |= USBHS_UGCTRL_CONNECT; - writel(value, base + USBHS_UGCTRL); - goto out; - } - udelay(1); - } - - /* Timed out waiting for the PLL lock */ - err = -ETIMEDOUT; - -out: - spin_unlock_irqrestore(&drv->lock, flags); - - return err; -} - -static int rcar_gen2_phy_power_off(struct phy *p) -{ - struct rcar_gen2_phy *phy = phy_get_drvdata(p); - struct rcar_gen2_phy_driver *drv = phy->channel->drv; - void __iomem *base = drv->base; - unsigned long flags; - u32 value; - - /* Skip if it's not USBHS */ - if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB) - return 0; - - spin_lock_irqsave(&drv->lock, flags); - - /* Power off USBHS PHY */ - value = readl(base + USBHS_UGCTRL); - value &= ~USBHS_UGCTRL_CONNECT; - writel(value, base + USBHS_UGCTRL); - - value = readw(base + USBHS_LPSTS); - value &= ~USBHS_LPSTS_SUSPM; - writew(value, base + USBHS_LPSTS); - - value = readl(base + USBHS_UGCTRL); - value |= USBHS_UGCTRL_PLLRESET; - writel(value, base + USBHS_UGCTRL); - - spin_unlock_irqrestore(&drv->lock, flags); - - return 0; -} - -static const struct phy_ops rcar_gen2_phy_ops = { - .init = rcar_gen2_phy_init, - .exit = rcar_gen2_phy_exit, - .power_on = rcar_gen2_phy_power_on, - .power_off = rcar_gen2_phy_power_off, - .owner = THIS_MODULE, -}; - -static const struct of_device_id rcar_gen2_phy_match_table[] = { - { .compatible = "renesas,usb-phy-r8a7790" }, - { .compatible = "renesas,usb-phy-r8a7791" }, - { .compatible = "renesas,usb-phy-r8a7794" }, - { .compatible = "renesas,rcar-gen2-usb-phy" }, - { } -}; -MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table); - -static struct phy *rcar_gen2_phy_xlate(struct device *dev, - struct of_phandle_args *args) -{ - struct rcar_gen2_phy_driver *drv; - struct device_node *np = args->np; - int i; - - drv = dev_get_drvdata(dev); - if (!drv) - return ERR_PTR(-EINVAL); - - for (i = 0; i < drv->num_channels; i++) { - if (np == drv->channels[i].of_node) - break; - } - - if (i >= drv->num_channels || args->args[0] >= 2) - return ERR_PTR(-ENODEV); - - return drv->channels[i].phys[args->args[0]].phy; -} - -static const u32 select_mask[] = { - [0] = USBHS_UGCTRL2_USB0SEL, - [2] = USBHS_UGCTRL2_USB2SEL, -}; - -static const u32 select_value[][PHYS_PER_CHANNEL] = { - [0] = { USBHS_UGCTRL2_USB0SEL_PCI, USBHS_UGCTRL2_USB0SEL_HS_USB }, - [2] = { USBHS_UGCTRL2_USB2SEL_PCI, USBHS_UGCTRL2_USB2SEL_USB30 }, -}; - -static int rcar_gen2_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct rcar_gen2_phy_driver *drv; - struct phy_provider *provider; - struct device_node *np; - struct resource *res; - void __iomem *base; - struct clk *clk; - int i = 0; - - if (!dev->of_node) { - dev_err(dev, - "This driver is required to be instantiated from device tree\n"); - return -EINVAL; - } - - clk = devm_clk_get(dev, "usbhs"); - if (IS_ERR(clk)) { - dev_err(dev, "Can't get USBHS clock\n"); - return PTR_ERR(clk); - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); - if (!drv) - return -ENOMEM; - - spin_lock_init(&drv->lock); - - drv->clk = clk; - drv->base = base; - - drv->num_channels = of_get_child_count(dev->of_node); - drv->channels = devm_kcalloc(dev, drv->num_channels, - sizeof(struct rcar_gen2_channel), - GFP_KERNEL); - if (!drv->channels) - return -ENOMEM; - - for_each_child_of_node(dev->of_node, np) { - struct rcar_gen2_channel *channel = drv->channels + i; - u32 channel_num; - int error, n; - - channel->of_node = np; - channel->drv = drv; - channel->selected_phy = -1; - - error = of_property_read_u32(np, "reg", &channel_num); - if (error || channel_num > 2) { - dev_err(dev, "Invalid \"reg\" property\n"); - return error; - } - channel->select_mask = select_mask[channel_num]; - - for (n = 0; n < PHYS_PER_CHANNEL; n++) { - struct rcar_gen2_phy *phy = &channel->phys[n]; - - phy->channel = channel; - phy->number = n; - phy->select_value = select_value[channel_num][n]; - - phy->phy = devm_phy_create(dev, NULL, - &rcar_gen2_phy_ops); - if (IS_ERR(phy->phy)) { - dev_err(dev, "Failed to create PHY\n"); - return PTR_ERR(phy->phy); - } - phy_set_drvdata(phy->phy, phy); - } - - i++; - } - - provider = devm_of_phy_provider_register(dev, rcar_gen2_phy_xlate); - if (IS_ERR(provider)) { - dev_err(dev, "Failed to register PHY provider\n"); - return PTR_ERR(provider); - } - - dev_set_drvdata(dev, drv); - - return 0; -} - -static struct platform_driver rcar_gen2_phy_driver = { - .driver = { - .name = "phy_rcar_gen2", - .of_match_table = rcar_gen2_phy_match_table, - }, - .probe = rcar_gen2_phy_probe, -}; - -module_platform_driver(rcar_gen2_phy_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Renesas R-Car Gen2 PHY"); -MODULE_AUTHOR("Sergei Shtylyov "); diff --git a/drivers/phy/phy-rcar-gen3-usb2.c b/drivers/phy/phy-rcar-gen3-usb2.c deleted file mode 100644 index 54c34298a000..000000000000 --- a/drivers/phy/phy-rcar-gen3-usb2.c +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Renesas R-Car Gen3 for USB2.0 PHY driver - * - * Copyright (C) 2015 Renesas Electronics Corporation - * - * This is based on the phy-rcar-gen2 driver: - * Copyright (C) 2014 Renesas Solutions Corp. - * Copyright (C) 2014 Cogent Embedded, Inc. - * - * 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 -#include - -/******* USB2.0 Host registers (original offset is +0x200) *******/ -#define USB2_INT_ENABLE 0x000 -#define USB2_USBCTR 0x00c -#define USB2_SPD_RSM_TIMSET 0x10c -#define USB2_OC_TIMSET 0x110 -#define USB2_COMMCTRL 0x600 -#define USB2_OBINTSTA 0x604 -#define USB2_OBINTEN 0x608 -#define USB2_VBCTRL 0x60c -#define USB2_LINECTRL1 0x610 -#define USB2_ADPCTRL 0x630 - -/* INT_ENABLE */ -#define USB2_INT_ENABLE_UCOM_INTEN BIT(3) -#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2) -#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1) -#define USB2_INT_ENABLE_INIT (USB2_INT_ENABLE_UCOM_INTEN | \ - USB2_INT_ENABLE_USBH_INTB_EN | \ - USB2_INT_ENABLE_USBH_INTA_EN) - -/* USBCTR */ -#define USB2_USBCTR_DIRPD BIT(2) -#define USB2_USBCTR_PLL_RST BIT(1) - -/* SPD_RSM_TIMSET */ -#define USB2_SPD_RSM_TIMSET_INIT 0x014e029b - -/* OC_TIMSET */ -#define USB2_OC_TIMSET_INIT 0x000209ab - -/* COMMCTRL */ -#define USB2_COMMCTRL_OTG_PERI BIT(31) /* 1 = Peripheral mode */ - -/* OBINTSTA and OBINTEN */ -#define USB2_OBINT_SESSVLDCHG BIT(12) -#define USB2_OBINT_IDDIGCHG BIT(11) -#define USB2_OBINT_BITS (USB2_OBINT_SESSVLDCHG | \ - USB2_OBINT_IDDIGCHG) - -/* VBCTRL */ -#define USB2_VBCTRL_DRVVBUSSEL BIT(8) - -/* LINECTRL1 */ -#define USB2_LINECTRL1_DPRPD_EN BIT(19) -#define USB2_LINECTRL1_DP_RPD BIT(18) -#define USB2_LINECTRL1_DMRPD_EN BIT(17) -#define USB2_LINECTRL1_DM_RPD BIT(16) -#define USB2_LINECTRL1_OPMODE_NODRV BIT(6) - -/* ADPCTRL */ -#define USB2_ADPCTRL_OTGSESSVLD BIT(20) -#define USB2_ADPCTRL_IDDIG BIT(19) -#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ -#define USB2_ADPCTRL_DRVVBUS BIT(4) - -struct rcar_gen3_chan { - void __iomem *base; - struct extcon_dev *extcon; - struct phy *phy; - struct regulator *vbus; - struct work_struct work; - bool extcon_host; - bool has_otg; -}; - -static void rcar_gen3_phy_usb2_work(struct work_struct *work) -{ - struct rcar_gen3_chan *ch = container_of(work, struct rcar_gen3_chan, - work); - - if (ch->extcon_host) { - extcon_set_state_sync(ch->extcon, EXTCON_USB_HOST, true); - extcon_set_state_sync(ch->extcon, EXTCON_USB, false); - } else { - extcon_set_state_sync(ch->extcon, EXTCON_USB_HOST, false); - extcon_set_state_sync(ch->extcon, EXTCON_USB, true); - } -} - -static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host) -{ - void __iomem *usb2_base = ch->base; - u32 val = readl(usb2_base + USB2_COMMCTRL); - - dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, host); - if (host) - val &= ~USB2_COMMCTRL_OTG_PERI; - else - val |= USB2_COMMCTRL_OTG_PERI; - writel(val, usb2_base + USB2_COMMCTRL); -} - -static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm) -{ - void __iomem *usb2_base = ch->base; - u32 val = readl(usb2_base + USB2_LINECTRL1); - - dev_vdbg(&ch->phy->dev, "%s: %08x, %d, %d\n", __func__, val, dp, dm); - val &= ~(USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD); - if (dp) - val |= USB2_LINECTRL1_DP_RPD; - if (dm) - val |= USB2_LINECTRL1_DM_RPD; - writel(val, usb2_base + USB2_LINECTRL1); -} - -static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus) -{ - void __iomem *usb2_base = ch->base; - u32 val = readl(usb2_base + USB2_ADPCTRL); - - dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, vbus); - if (vbus) - val |= USB2_ADPCTRL_DRVVBUS; - else - val &= ~USB2_ADPCTRL_DRVVBUS; - writel(val, usb2_base + USB2_ADPCTRL); -} - -static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch) -{ - rcar_gen3_set_linectrl(ch, 1, 1); - rcar_gen3_set_host_mode(ch, 1); - rcar_gen3_enable_vbus_ctrl(ch, 1); - - ch->extcon_host = true; - schedule_work(&ch->work); -} - -static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch) -{ - rcar_gen3_set_linectrl(ch, 0, 1); - rcar_gen3_set_host_mode(ch, 0); - rcar_gen3_enable_vbus_ctrl(ch, 0); - - ch->extcon_host = false; - schedule_work(&ch->work); -} - -static void rcar_gen3_init_for_b_host(struct rcar_gen3_chan *ch) -{ - void __iomem *usb2_base = ch->base; - u32 val; - - val = readl(usb2_base + USB2_LINECTRL1); - writel(val | USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1); - - rcar_gen3_set_linectrl(ch, 1, 1); - rcar_gen3_set_host_mode(ch, 1); - rcar_gen3_enable_vbus_ctrl(ch, 0); - - val = readl(usb2_base + USB2_LINECTRL1); - writel(val & ~USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1); -} - -static void rcar_gen3_init_for_a_peri(struct rcar_gen3_chan *ch) -{ - rcar_gen3_set_linectrl(ch, 0, 1); - rcar_gen3_set_host_mode(ch, 0); - rcar_gen3_enable_vbus_ctrl(ch, 1); -} - -static void rcar_gen3_init_from_a_peri_to_a_host(struct rcar_gen3_chan *ch) -{ - void __iomem *usb2_base = ch->base; - u32 val; - - val = readl(usb2_base + USB2_OBINTEN); - writel(val & ~USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); - - rcar_gen3_enable_vbus_ctrl(ch, 0); - rcar_gen3_init_for_host(ch); - - writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); -} - -static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch) -{ - return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG); -} - -static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch) -{ - if (!rcar_gen3_check_id(ch)) - rcar_gen3_init_for_host(ch); - else - rcar_gen3_init_for_peri(ch); -} - -static bool rcar_gen3_is_host(struct rcar_gen3_chan *ch) -{ - return !(readl(ch->base + USB2_COMMCTRL) & USB2_COMMCTRL_OTG_PERI); -} - -static ssize_t role_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct rcar_gen3_chan *ch = dev_get_drvdata(dev); - bool is_b_device, is_host, new_mode_is_host; - - if (!ch->has_otg || !ch->phy->init_count) - return -EIO; - - /* - * is_b_device: true is B-Device. false is A-Device. - * If {new_mode_}is_host: true is Host mode. false is Peripheral mode. - */ - is_b_device = rcar_gen3_check_id(ch); - is_host = rcar_gen3_is_host(ch); - if (!strncmp(buf, "host", strlen("host"))) - new_mode_is_host = true; - else if (!strncmp(buf, "peripheral", strlen("peripheral"))) - new_mode_is_host = false; - else - return -EINVAL; - - /* If current and new mode is the same, this returns the error */ - if (is_host == new_mode_is_host) - return -EINVAL; - - if (new_mode_is_host) { /* And is_host must be false */ - if (!is_b_device) /* A-Peripheral */ - rcar_gen3_init_from_a_peri_to_a_host(ch); - else /* B-Peripheral */ - rcar_gen3_init_for_b_host(ch); - } else { /* And is_host must be true */ - if (!is_b_device) /* A-Host */ - rcar_gen3_init_for_a_peri(ch); - else /* B-Host */ - rcar_gen3_init_for_peri(ch); - } - - return count; -} - -static ssize_t role_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct rcar_gen3_chan *ch = dev_get_drvdata(dev); - - if (!ch->has_otg || !ch->phy->init_count) - return -EIO; - - return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" : - "peripheral"); -} -static DEVICE_ATTR_RW(role); - -static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) -{ - void __iomem *usb2_base = ch->base; - u32 val; - - val = readl(usb2_base + USB2_VBCTRL); - writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL); - writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); - val = readl(usb2_base + USB2_OBINTEN); - writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); - val = readl(usb2_base + USB2_ADPCTRL); - writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL); - val = readl(usb2_base + USB2_LINECTRL1); - rcar_gen3_set_linectrl(ch, 0, 0); - writel(val | USB2_LINECTRL1_DPRPD_EN | USB2_LINECTRL1_DMRPD_EN, - usb2_base + USB2_LINECTRL1); - - rcar_gen3_device_recognition(ch); -} - -static int rcar_gen3_phy_usb2_init(struct phy *p) -{ - struct rcar_gen3_chan *channel = phy_get_drvdata(p); - void __iomem *usb2_base = channel->base; - - /* Initialize USB2 part */ - writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE); - writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET); - writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET); - - /* Initialize otg part */ - if (channel->has_otg) - rcar_gen3_init_otg(channel); - - return 0; -} - -static int rcar_gen3_phy_usb2_exit(struct phy *p) -{ - struct rcar_gen3_chan *channel = phy_get_drvdata(p); - - writel(0, channel->base + USB2_INT_ENABLE); - - return 0; -} - -static int rcar_gen3_phy_usb2_power_on(struct phy *p) -{ - struct rcar_gen3_chan *channel = phy_get_drvdata(p); - void __iomem *usb2_base = channel->base; - u32 val; - int ret; - - if (channel->vbus) { - ret = regulator_enable(channel->vbus); - if (ret) - return ret; - } - - val = readl(usb2_base + USB2_USBCTR); - val |= USB2_USBCTR_PLL_RST; - writel(val, usb2_base + USB2_USBCTR); - val &= ~USB2_USBCTR_PLL_RST; - writel(val, usb2_base + USB2_USBCTR); - - return 0; -} - -static int rcar_gen3_phy_usb2_power_off(struct phy *p) -{ - struct rcar_gen3_chan *channel = phy_get_drvdata(p); - int ret = 0; - - if (channel->vbus) - ret = regulator_disable(channel->vbus); - - return ret; -} - -static const struct phy_ops rcar_gen3_phy_usb2_ops = { - .init = rcar_gen3_phy_usb2_init, - .exit = rcar_gen3_phy_usb2_exit, - .power_on = rcar_gen3_phy_usb2_power_on, - .power_off = rcar_gen3_phy_usb2_power_off, - .owner = THIS_MODULE, -}; - -static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) -{ - struct rcar_gen3_chan *ch = _ch; - void __iomem *usb2_base = ch->base; - u32 status = readl(usb2_base + USB2_OBINTSTA); - irqreturn_t ret = IRQ_NONE; - - if (status & USB2_OBINT_BITS) { - dev_vdbg(&ch->phy->dev, "%s: %08x\n", __func__, status); - writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); - rcar_gen3_device_recognition(ch); - ret = IRQ_HANDLED; - } - - return ret; -} - -static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { - { .compatible = "renesas,usb2-phy-r8a7795" }, - { .compatible = "renesas,usb2-phy-r8a7796" }, - { .compatible = "renesas,rcar-gen3-usb2-phy" }, - { } -}; -MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table); - -static const unsigned int rcar_gen3_phy_cable[] = { - EXTCON_USB, - EXTCON_USB_HOST, - EXTCON_NONE, -}; - -static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct rcar_gen3_chan *channel; - struct phy_provider *provider; - struct resource *res; - int irq, ret = 0; - - if (!dev->of_node) { - dev_err(dev, "This driver needs device tree\n"); - return -EINVAL; - } - - channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL); - if (!channel) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - channel->base = devm_ioremap_resource(dev, res); - if (IS_ERR(channel->base)) - return PTR_ERR(channel->base); - - /* call request_irq for OTG */ - irq = platform_get_irq(pdev, 0); - if (irq >= 0) { - int ret; - - INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); - irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq, - IRQF_SHARED, dev_name(dev), channel); - if (irq < 0) - dev_err(dev, "No irq handler (%d)\n", irq); - channel->has_otg = true; - channel->extcon = devm_extcon_dev_allocate(dev, - rcar_gen3_phy_cable); - if (IS_ERR(channel->extcon)) - return PTR_ERR(channel->extcon); - - ret = devm_extcon_dev_register(dev, channel->extcon); - if (ret < 0) { - dev_err(dev, "Failed to register extcon\n"); - return ret; - } - } - - /* - * devm_phy_create() will call pm_runtime_enable(&phy->dev); - * And then, phy-core will manage runtime pm for this device. - */ - pm_runtime_enable(dev); - channel->phy = devm_phy_create(dev, NULL, &rcar_gen3_phy_usb2_ops); - if (IS_ERR(channel->phy)) { - dev_err(dev, "Failed to create USB2 PHY\n"); - ret = PTR_ERR(channel->phy); - goto error; - } - - channel->vbus = devm_regulator_get_optional(dev, "vbus"); - if (IS_ERR(channel->vbus)) { - if (PTR_ERR(channel->vbus) == -EPROBE_DEFER) { - ret = PTR_ERR(channel->vbus); - goto error; - } - channel->vbus = NULL; - } - - platform_set_drvdata(pdev, channel); - phy_set_drvdata(channel->phy, channel); - - provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(provider)) { - dev_err(dev, "Failed to register PHY provider\n"); - ret = PTR_ERR(provider); - goto error; - } else if (channel->has_otg) { - int ret; - - ret = device_create_file(dev, &dev_attr_role); - if (ret < 0) - goto error; - } - - return 0; - -error: - pm_runtime_disable(dev); - - return ret; -} - -static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev) -{ - struct rcar_gen3_chan *channel = platform_get_drvdata(pdev); - - if (channel->has_otg) - device_remove_file(&pdev->dev, &dev_attr_role); - - pm_runtime_disable(&pdev->dev); - - return 0; -}; - -static struct platform_driver rcar_gen3_phy_usb2_driver = { - .driver = { - .name = "phy_rcar_gen3_usb2", - .of_match_table = rcar_gen3_phy_usb2_match_table, - }, - .probe = rcar_gen3_phy_usb2_probe, - .remove = rcar_gen3_phy_usb2_remove, -}; -module_platform_driver(rcar_gen3_phy_usb2_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Renesas R-Car Gen3 USB 2.0 PHY"); -MODULE_AUTHOR("Yoshihiro Shimoda "); diff --git a/drivers/phy/phy-rockchip-dp.c b/drivers/phy/phy-rockchip-dp.c deleted file mode 100644 index 8b267a746576..000000000000 --- a/drivers/phy/phy-rockchip-dp.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Rockchip DP PHY driver - * - * Copyright (C) 2016 FuZhou Rockchip Co., Ltd. - * Author: Yakir Yang - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - */ - -#include -#include -#include -#include -#include -#include -#include - -#define GRF_SOC_CON12 0x0274 - -#define GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK BIT(20) -#define GRF_EDP_REF_CLK_SEL_INTER BIT(4) - -#define GRF_EDP_PHY_SIDDQ_HIWORD_MASK BIT(21) -#define GRF_EDP_PHY_SIDDQ_ON 0 -#define GRF_EDP_PHY_SIDDQ_OFF BIT(5) - -struct rockchip_dp_phy { - struct device *dev; - struct regmap *grf; - struct clk *phy_24m; -}; - -static int rockchip_set_phy_state(struct phy *phy, bool enable) -{ - struct rockchip_dp_phy *dp = phy_get_drvdata(phy); - int ret; - - if (enable) { - ret = regmap_write(dp->grf, GRF_SOC_CON12, - GRF_EDP_PHY_SIDDQ_HIWORD_MASK | - GRF_EDP_PHY_SIDDQ_ON); - if (ret < 0) { - dev_err(dp->dev, "Can't enable PHY power %d\n", ret); - return ret; - } - - ret = clk_prepare_enable(dp->phy_24m); - } else { - clk_disable_unprepare(dp->phy_24m); - - ret = regmap_write(dp->grf, GRF_SOC_CON12, - GRF_EDP_PHY_SIDDQ_HIWORD_MASK | - GRF_EDP_PHY_SIDDQ_OFF); - } - - return ret; -} - -static int rockchip_dp_phy_power_on(struct phy *phy) -{ - return rockchip_set_phy_state(phy, true); -} - -static int rockchip_dp_phy_power_off(struct phy *phy) -{ - return rockchip_set_phy_state(phy, false); -} - -static const struct phy_ops rockchip_dp_phy_ops = { - .power_on = rockchip_dp_phy_power_on, - .power_off = rockchip_dp_phy_power_off, - .owner = THIS_MODULE, -}; - -static int rockchip_dp_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct phy_provider *phy_provider; - struct rockchip_dp_phy *dp; - struct phy *phy; - int ret; - - if (!np) - return -ENODEV; - - if (!dev->parent || !dev->parent->of_node) - return -ENODEV; - - dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); - if (!dp) - return -ENOMEM; - - dp->dev = dev; - - dp->phy_24m = devm_clk_get(dev, "24m"); - if (IS_ERR(dp->phy_24m)) { - dev_err(dev, "cannot get clock 24m\n"); - return PTR_ERR(dp->phy_24m); - } - - ret = clk_set_rate(dp->phy_24m, 24000000); - if (ret < 0) { - dev_err(dp->dev, "cannot set clock phy_24m %d\n", ret); - return ret; - } - - dp->grf = syscon_node_to_regmap(dev->parent->of_node); - if (IS_ERR(dp->grf)) { - dev_err(dev, "rk3288-dp needs the General Register Files syscon\n"); - return PTR_ERR(dp->grf); - } - - ret = regmap_write(dp->grf, GRF_SOC_CON12, GRF_EDP_REF_CLK_SEL_INTER | - GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK); - if (ret != 0) { - dev_err(dp->dev, "Could not config GRF edp ref clk: %d\n", ret); - return ret; - } - - phy = devm_phy_create(dev, np, &rockchip_dp_phy_ops); - if (IS_ERR(phy)) { - dev_err(dev, "failed to create phy\n"); - return PTR_ERR(phy); - } - phy_set_drvdata(phy, dp); - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id rockchip_dp_phy_dt_ids[] = { - { .compatible = "rockchip,rk3288-dp-phy" }, - {} -}; - -MODULE_DEVICE_TABLE(of, rockchip_dp_phy_dt_ids); - -static struct platform_driver rockchip_dp_phy_driver = { - .probe = rockchip_dp_phy_probe, - .driver = { - .name = "rockchip-dp-phy", - .of_match_table = rockchip_dp_phy_dt_ids, - }, -}; - -module_platform_driver(rockchip_dp_phy_driver); - -MODULE_AUTHOR("Yakir Yang "); -MODULE_DESCRIPTION("Rockchip DP PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c deleted file mode 100644 index f1b24f18e9b2..000000000000 --- a/drivers/phy/phy-rockchip-emmc.c +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Rockchip emmc PHY driver - * - * Copyright (C) 2016 Shawn Lin - * Copyright (C) 2016 ROCKCHIP, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * The higher 16-bit of this register is used for write protection - * only if BIT(x + 16) set to 1 the BIT(x) can be written. - */ -#define HIWORD_UPDATE(val, mask, shift) \ - ((val) << (shift) | (mask) << ((shift) + 16)) - -/* Register definition */ -#define GRF_EMMCPHY_CON0 0x0 -#define GRF_EMMCPHY_CON1 0x4 -#define GRF_EMMCPHY_CON2 0x8 -#define GRF_EMMCPHY_CON3 0xc -#define GRF_EMMCPHY_CON4 0x10 -#define GRF_EMMCPHY_CON5 0x14 -#define GRF_EMMCPHY_CON6 0x18 -#define GRF_EMMCPHY_STATUS 0x20 - -#define PHYCTRL_PDB_MASK 0x1 -#define PHYCTRL_PDB_SHIFT 0x0 -#define PHYCTRL_PDB_PWR_ON 0x1 -#define PHYCTRL_PDB_PWR_OFF 0x0 -#define PHYCTRL_ENDLL_MASK 0x1 -#define PHYCTRL_ENDLL_SHIFT 0x1 -#define PHYCTRL_ENDLL_ENABLE 0x1 -#define PHYCTRL_ENDLL_DISABLE 0x0 -#define PHYCTRL_CALDONE_MASK 0x1 -#define PHYCTRL_CALDONE_SHIFT 0x6 -#define PHYCTRL_CALDONE_DONE 0x1 -#define PHYCTRL_CALDONE_GOING 0x0 -#define PHYCTRL_DLLRDY_MASK 0x1 -#define PHYCTRL_DLLRDY_SHIFT 0x5 -#define PHYCTRL_DLLRDY_DONE 0x1 -#define PHYCTRL_DLLRDY_GOING 0x0 -#define PHYCTRL_FREQSEL_200M 0x0 -#define PHYCTRL_FREQSEL_50M 0x1 -#define PHYCTRL_FREQSEL_100M 0x2 -#define PHYCTRL_FREQSEL_150M 0x3 -#define PHYCTRL_FREQSEL_MASK 0x3 -#define PHYCTRL_FREQSEL_SHIFT 0xc -#define PHYCTRL_DR_MASK 0x7 -#define PHYCTRL_DR_SHIFT 0x4 -#define PHYCTRL_DR_50OHM 0x0 -#define PHYCTRL_DR_33OHM 0x1 -#define PHYCTRL_DR_66OHM 0x2 -#define PHYCTRL_DR_100OHM 0x3 -#define PHYCTRL_DR_40OHM 0x4 -#define PHYCTRL_OTAPDLYENA 0x1 -#define PHYCTRL_OTAPDLYENA_MASK 0x1 -#define PHYCTRL_OTAPDLYENA_SHIFT 0xb -#define PHYCTRL_OTAPDLYSEL_MASK 0xf -#define PHYCTRL_OTAPDLYSEL_SHIFT 0x7 - -struct rockchip_emmc_phy { - unsigned int reg_offset; - struct regmap *reg_base; - struct clk *emmcclk; -}; - -static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) -{ - struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); - unsigned int caldone; - unsigned int dllrdy; - unsigned int freqsel = PHYCTRL_FREQSEL_200M; - unsigned long rate; - unsigned long timeout; - - /* - * Keep phyctrl_pdb and phyctrl_endll low to allow - * initialization of CALIO state M/C DFFs - */ - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON6, - HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF, - PHYCTRL_PDB_MASK, - PHYCTRL_PDB_SHIFT)); - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON6, - HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE, - PHYCTRL_ENDLL_MASK, - PHYCTRL_ENDLL_SHIFT)); - - /* Already finish power_off above */ - if (on_off == PHYCTRL_PDB_PWR_OFF) - return 0; - - rate = clk_get_rate(rk_phy->emmcclk); - - if (rate != 0) { - unsigned long ideal_rate; - unsigned long diff; - - switch (rate) { - case 1 ... 74999999: - ideal_rate = 50000000; - freqsel = PHYCTRL_FREQSEL_50M; - break; - case 75000000 ... 124999999: - ideal_rate = 100000000; - freqsel = PHYCTRL_FREQSEL_100M; - break; - case 125000000 ... 174999999: - ideal_rate = 150000000; - freqsel = PHYCTRL_FREQSEL_150M; - break; - default: - ideal_rate = 200000000; - break; - } - - diff = (rate > ideal_rate) ? - rate - ideal_rate : ideal_rate - rate; - - /* - * In order for tuning delays to be accurate we need to be - * pretty spot on for the DLL range, so warn if we're too - * far off. Also warn if we're above the 200 MHz max. Don't - * warn for really slow rates since we won't be tuning then. - */ - if ((rate > 50000000 && diff > 15000000) || (rate > 200000000)) - dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate); - } - - /* - * According to the user manual, calpad calibration - * cycle takes more than 2us without the minimal recommended - * value, so we may need a little margin here - */ - udelay(3); - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON6, - HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON, - PHYCTRL_PDB_MASK, - PHYCTRL_PDB_SHIFT)); - - /* - * According to the user manual, it asks driver to - * wait 5us for calpad busy trimming - */ - udelay(5); - regmap_read(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_STATUS, - &caldone); - caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK; - if (caldone != PHYCTRL_CALDONE_DONE) { - pr_err("rockchip_emmc_phy_power: caldone timeout.\n"); - return -ETIMEDOUT; - } - - /* Set the frequency of the DLL operation */ - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON0, - HIWORD_UPDATE(freqsel, PHYCTRL_FREQSEL_MASK, - PHYCTRL_FREQSEL_SHIFT)); - - /* Turn on the DLL */ - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON6, - HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE, - PHYCTRL_ENDLL_MASK, - PHYCTRL_ENDLL_SHIFT)); - - /* - * We turned on the DLL even though the rate was 0 because we the - * clock might be turned on later. ...but we can't wait for the DLL - * to lock when the rate is 0 because it will never lock with no - * input clock. - * - * Technically we should be checking the lock later when the clock - * is turned on, but for now we won't. - */ - if (rate == 0) - return 0; - - /* - * After enabling analog DLL circuits docs say that we need 10.2 us if - * our source clock is at 50 MHz and that lock time scales linearly - * with clock speed. If we are powering on the PHY and the card clock - * is super slow (like 100 kHZ) this could take as long as 5.1 ms as - * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms - * Hopefully we won't be running at 100 kHz, but we should still make - * sure we wait long enough. - * - * NOTE: There appear to be corner cases where the DLL seems to take - * extra long to lock for reasons that aren't understood. In some - * extreme cases we've seen it take up to over 10ms (!). We'll be - * generous and give it 50ms. We still busy wait here because: - * - In most cases it should be super fast. - * - This is not called lots during normal operation so it shouldn't - * be a power or performance problem to busy wait. We expect it - * only at boot / resume. In both cases, eMMC is probably on the - * critical path so busy waiting a little extra time should be OK. - */ - timeout = jiffies + msecs_to_jiffies(50); - do { - udelay(1); - - regmap_read(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_STATUS, - &dllrdy); - dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; - if (dllrdy == PHYCTRL_DLLRDY_DONE) - break; - } while (!time_after(jiffies, timeout)); - - if (dllrdy != PHYCTRL_DLLRDY_DONE) { - pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static int rockchip_emmc_phy_init(struct phy *phy) -{ - struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); - int ret = 0; - - /* - * We purposely get the clock here and not in probe to avoid the - * circular dependency problem. We expect: - * - PHY driver to probe - * - SDHCI driver to start probe - * - SDHCI driver to register it's clock - * - SDHCI driver to get the PHY - * - SDHCI driver to init the PHY - * - * The clock is optional, so upon any error we just set to NULL. - * - * NOTE: we don't do anything special for EPROBE_DEFER here. Given the - * above expected use case, EPROBE_DEFER isn't sensible to expect, so - * it's just like any other error. - */ - rk_phy->emmcclk = clk_get(&phy->dev, "emmcclk"); - if (IS_ERR(rk_phy->emmcclk)) { - dev_dbg(&phy->dev, "Error getting emmcclk: %d\n", ret); - rk_phy->emmcclk = NULL; - } - - return ret; -} - -static int rockchip_emmc_phy_exit(struct phy *phy) -{ - struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); - - clk_put(rk_phy->emmcclk); - - return 0; -} - -static int rockchip_emmc_phy_power_off(struct phy *phy) -{ - /* Power down emmc phy analog blocks */ - return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_OFF); -} - -static int rockchip_emmc_phy_power_on(struct phy *phy) -{ - struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); - - /* Drive impedance: 50 Ohm */ - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON6, - HIWORD_UPDATE(PHYCTRL_DR_50OHM, - PHYCTRL_DR_MASK, - PHYCTRL_DR_SHIFT)); - - /* Output tap delay: enable */ - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON0, - HIWORD_UPDATE(PHYCTRL_OTAPDLYENA, - PHYCTRL_OTAPDLYENA_MASK, - PHYCTRL_OTAPDLYENA_SHIFT)); - - /* Output tap delay */ - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON0, - HIWORD_UPDATE(4, - PHYCTRL_OTAPDLYSEL_MASK, - PHYCTRL_OTAPDLYSEL_SHIFT)); - - /* Power up emmc phy analog blocks */ - return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON); -} - -static const struct phy_ops ops = { - .init = rockchip_emmc_phy_init, - .exit = rockchip_emmc_phy_exit, - .power_on = rockchip_emmc_phy_power_on, - .power_off = rockchip_emmc_phy_power_off, - .owner = THIS_MODULE, -}; - -static int rockchip_emmc_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct rockchip_emmc_phy *rk_phy; - struct phy *generic_phy; - struct phy_provider *phy_provider; - struct regmap *grf; - unsigned int reg_offset; - - if (!dev->parent || !dev->parent->of_node) - return -ENODEV; - - grf = syscon_node_to_regmap(dev->parent->of_node); - if (IS_ERR(grf)) { - dev_err(dev, "Missing rockchip,grf property\n"); - return PTR_ERR(grf); - } - - rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL); - if (!rk_phy) - return -ENOMEM; - - if (of_property_read_u32(dev->of_node, "reg", ®_offset)) { - dev_err(dev, "missing reg property in node %s\n", - dev->of_node->name); - return -EINVAL; - } - - rk_phy->reg_offset = reg_offset; - rk_phy->reg_base = grf; - - generic_phy = devm_phy_create(dev, dev->of_node, &ops); - if (IS_ERR(generic_phy)) { - dev_err(dev, "failed to create PHY\n"); - return PTR_ERR(generic_phy); - } - - phy_set_drvdata(generic_phy, rk_phy); - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id rockchip_emmc_phy_dt_ids[] = { - { .compatible = "rockchip,rk3399-emmc-phy" }, - {} -}; - -MODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids); - -static struct platform_driver rockchip_emmc_driver = { - .probe = rockchip_emmc_phy_probe, - .driver = { - .name = "rockchip-emmc-phy", - .of_match_table = rockchip_emmc_phy_dt_ids, - }, -}; - -module_platform_driver(rockchip_emmc_driver); - -MODULE_AUTHOR("Shawn Lin "); -MODULE_DESCRIPTION("Rockchip EMMC PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c deleted file mode 100644 index 8efe78a49916..000000000000 --- a/drivers/phy/phy-rockchip-inno-usb2.c +++ /dev/null @@ -1,1284 +0,0 @@ -/* - * Rockchip USB2.0 PHY with Innosilicon IP block driver - * - * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BIT_WRITEABLE_SHIFT 16 -#define SCHEDULE_DELAY (60 * HZ) -#define OTG_SCHEDULE_DELAY (2 * HZ) - -enum rockchip_usb2phy_port_id { - USB2PHY_PORT_OTG, - USB2PHY_PORT_HOST, - USB2PHY_NUM_PORTS, -}; - -enum rockchip_usb2phy_host_state { - PHY_STATE_HS_ONLINE = 0, - PHY_STATE_DISCONNECT = 1, - PHY_STATE_CONNECT = 2, - PHY_STATE_FS_LS_ONLINE = 4, -}; - -/** - * Different states involved in USB charger detection. - * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection - * process is not yet started. - * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact. - * USB_CHG_STATE_DCD_DONE Data pin contact is detected. - * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects - * between SDP and DCP/CDP). - * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects - * between DCP and CDP). - * USB_CHG_STATE_DETECTED USB charger type is determined. - */ -enum usb_chg_state { - USB_CHG_STATE_UNDEFINED = 0, - USB_CHG_STATE_WAIT_FOR_DCD, - USB_CHG_STATE_DCD_DONE, - USB_CHG_STATE_PRIMARY_DONE, - USB_CHG_STATE_SECONDARY_DONE, - USB_CHG_STATE_DETECTED, -}; - -static const unsigned int rockchip_usb2phy_extcon_cable[] = { - EXTCON_USB, - EXTCON_USB_HOST, - EXTCON_CHG_USB_SDP, - EXTCON_CHG_USB_CDP, - EXTCON_CHG_USB_DCP, - EXTCON_CHG_USB_SLOW, - EXTCON_NONE, -}; - -struct usb2phy_reg { - unsigned int offset; - unsigned int bitend; - unsigned int bitstart; - unsigned int disable; - unsigned int enable; -}; - -/** - * struct rockchip_chg_det_reg: usb charger detect registers - * @cp_det: charging port detected successfully. - * @dcp_det: dedicated charging port detected successfully. - * @dp_det: assert data pin connect successfully. - * @idm_sink_en: open dm sink curren. - * @idp_sink_en: open dp sink current. - * @idp_src_en: open dm source current. - * @rdm_pdwn_en: open dm pull down resistor. - * @vdm_src_en: open dm voltage source. - * @vdp_src_en: open dp voltage source. - * @opmode: utmi operational mode. - */ -struct rockchip_chg_det_reg { - struct usb2phy_reg cp_det; - struct usb2phy_reg dcp_det; - struct usb2phy_reg dp_det; - struct usb2phy_reg idm_sink_en; - struct usb2phy_reg idp_sink_en; - struct usb2phy_reg idp_src_en; - struct usb2phy_reg rdm_pdwn_en; - struct usb2phy_reg vdm_src_en; - struct usb2phy_reg vdp_src_en; - struct usb2phy_reg opmode; -}; - -/** - * struct rockchip_usb2phy_port_cfg: usb-phy port configuration. - * @phy_sus: phy suspend register. - * @bvalid_det_en: vbus valid rise detection enable register. - * @bvalid_det_st: vbus valid rise detection status register. - * @bvalid_det_clr: vbus valid rise detection clear register. - * @ls_det_en: linestate detection enable register. - * @ls_det_st: linestate detection state register. - * @ls_det_clr: linestate detection clear register. - * @utmi_avalid: utmi vbus avalid status register. - * @utmi_bvalid: utmi vbus bvalid status register. - * @utmi_ls: utmi linestate state register. - * @utmi_hstdet: utmi host disconnect register. - */ -struct rockchip_usb2phy_port_cfg { - struct usb2phy_reg phy_sus; - struct usb2phy_reg bvalid_det_en; - struct usb2phy_reg bvalid_det_st; - struct usb2phy_reg bvalid_det_clr; - struct usb2phy_reg ls_det_en; - struct usb2phy_reg ls_det_st; - struct usb2phy_reg ls_det_clr; - struct usb2phy_reg utmi_avalid; - struct usb2phy_reg utmi_bvalid; - struct usb2phy_reg utmi_ls; - struct usb2phy_reg utmi_hstdet; -}; - -/** - * struct rockchip_usb2phy_cfg: usb-phy configuration. - * @reg: the address offset of grf for usb-phy config. - * @num_ports: specify how many ports that the phy has. - * @clkout_ctl: keep on/turn off output clk of phy. - * @chg_det: charger detection registers. - */ -struct rockchip_usb2phy_cfg { - unsigned int reg; - unsigned int num_ports; - struct usb2phy_reg clkout_ctl; - const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS]; - const struct rockchip_chg_det_reg chg_det; -}; - -/** - * struct rockchip_usb2phy_port: usb-phy port data. - * @port_id: flag for otg port or host port. - * @suspended: phy suspended flag. - * @utmi_avalid: utmi avalid status usage flag. - * true - use avalid to get vbus status - * flase - use bvalid to get vbus status - * @vbus_attached: otg device vbus status. - * @bvalid_irq: IRQ number assigned for vbus valid rise detection. - * @ls_irq: IRQ number assigned for linestate detection. - * @mutex: for register updating in sm_work. - * @chg_work: charge detect work. - * @otg_sm_work: OTG state machine work. - * @sm_work: HOST state machine work. - * @phy_cfg: port register configuration, assigned by driver data. - * @event_nb: hold event notification callback. - * @state: define OTG enumeration states before device reset. - * @mode: the dr_mode of the controller. - */ -struct rockchip_usb2phy_port { - struct phy *phy; - unsigned int port_id; - bool suspended; - bool utmi_avalid; - bool vbus_attached; - int bvalid_irq; - int ls_irq; - struct mutex mutex; - struct delayed_work chg_work; - struct delayed_work otg_sm_work; - struct delayed_work sm_work; - const struct rockchip_usb2phy_port_cfg *port_cfg; - struct notifier_block event_nb; - enum usb_otg_state state; - enum usb_dr_mode mode; -}; - -/** - * struct rockchip_usb2phy: usb2.0 phy driver data. - * @grf: General Register Files regmap. - * @clk: clock struct of phy input clk. - * @clk480m: clock struct of phy output clk. - * @clk_hw: clock struct of phy output clk management. - * @chg_state: states involved in USB charger detection. - * @chg_type: USB charger types. - * @dcd_retries: The retry count used to track Data contact - * detection process. - * @edev: extcon device for notification registration - * @phy_cfg: phy register configuration, assigned by driver data. - * @ports: phy port instance. - */ -struct rockchip_usb2phy { - struct device *dev; - struct regmap *grf; - struct clk *clk; - struct clk *clk480m; - struct clk_hw clk480m_hw; - enum usb_chg_state chg_state; - enum power_supply_type chg_type; - u8 dcd_retries; - struct extcon_dev *edev; - const struct rockchip_usb2phy_cfg *phy_cfg; - struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS]; -}; - -static inline int property_enable(struct rockchip_usb2phy *rphy, - const struct usb2phy_reg *reg, bool en) -{ - unsigned int val, mask, tmp; - - tmp = en ? reg->enable : reg->disable; - mask = GENMASK(reg->bitend, reg->bitstart); - val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); - - return regmap_write(rphy->grf, reg->offset, val); -} - -static inline bool property_enabled(struct rockchip_usb2phy *rphy, - const struct usb2phy_reg *reg) -{ - int ret; - unsigned int tmp, orig; - unsigned int mask = GENMASK(reg->bitend, reg->bitstart); - - ret = regmap_read(rphy->grf, reg->offset, &orig); - if (ret) - return false; - - tmp = (orig & mask) >> reg->bitstart; - return tmp == reg->enable; -} - -static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) -{ - struct rockchip_usb2phy *rphy = - container_of(hw, struct rockchip_usb2phy, clk480m_hw); - int ret; - - /* turn on 480m clk output if it is off */ - if (!property_enabled(rphy, &rphy->phy_cfg->clkout_ctl)) { - ret = property_enable(rphy, &rphy->phy_cfg->clkout_ctl, true); - if (ret) - return ret; - - /* waiting for the clk become stable */ - usleep_range(1200, 1300); - } - - return 0; -} - -static void rockchip_usb2phy_clk480m_unprepare(struct clk_hw *hw) -{ - struct rockchip_usb2phy *rphy = - container_of(hw, struct rockchip_usb2phy, clk480m_hw); - - /* turn off 480m clk output */ - property_enable(rphy, &rphy->phy_cfg->clkout_ctl, false); -} - -static int rockchip_usb2phy_clk480m_prepared(struct clk_hw *hw) -{ - struct rockchip_usb2phy *rphy = - container_of(hw, struct rockchip_usb2phy, clk480m_hw); - - return property_enabled(rphy, &rphy->phy_cfg->clkout_ctl); -} - -static unsigned long -rockchip_usb2phy_clk480m_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - return 480000000; -} - -static const struct clk_ops rockchip_usb2phy_clkout_ops = { - .prepare = rockchip_usb2phy_clk480m_prepare, - .unprepare = rockchip_usb2phy_clk480m_unprepare, - .is_prepared = rockchip_usb2phy_clk480m_prepared, - .recalc_rate = rockchip_usb2phy_clk480m_recalc_rate, -}; - -static void rockchip_usb2phy_clk480m_unregister(void *data) -{ - struct rockchip_usb2phy *rphy = data; - - of_clk_del_provider(rphy->dev->of_node); - clk_unregister(rphy->clk480m); -} - -static int -rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) -{ - struct device_node *node = rphy->dev->of_node; - struct clk_init_data init; - const char *clk_name; - int ret; - - init.flags = 0; - init.name = "clk_usbphy_480m"; - init.ops = &rockchip_usb2phy_clkout_ops; - - /* optional override of the clockname */ - of_property_read_string(node, "clock-output-names", &init.name); - - if (rphy->clk) { - clk_name = __clk_get_name(rphy->clk); - init.parent_names = &clk_name; - init.num_parents = 1; - } else { - init.parent_names = NULL; - init.num_parents = 0; - } - - rphy->clk480m_hw.init = &init; - - /* register the clock */ - rphy->clk480m = clk_register(rphy->dev, &rphy->clk480m_hw); - if (IS_ERR(rphy->clk480m)) { - ret = PTR_ERR(rphy->clk480m); - goto err_ret; - } - - ret = of_clk_add_provider(node, of_clk_src_simple_get, rphy->clk480m); - if (ret < 0) - goto err_clk_provider; - - ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister, - rphy); - if (ret < 0) - goto err_unreg_action; - - return 0; - -err_unreg_action: - of_clk_del_provider(node); -err_clk_provider: - clk_unregister(rphy->clk480m); -err_ret: - return ret; -} - -static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy) -{ - int ret; - struct device_node *node = rphy->dev->of_node; - struct extcon_dev *edev; - - if (of_property_read_bool(node, "extcon")) { - edev = extcon_get_edev_by_phandle(rphy->dev, 0); - if (IS_ERR(edev)) { - if (PTR_ERR(edev) != -EPROBE_DEFER) - dev_err(rphy->dev, "Invalid or missing extcon\n"); - return PTR_ERR(edev); - } - } else { - /* Initialize extcon device */ - edev = devm_extcon_dev_allocate(rphy->dev, - rockchip_usb2phy_extcon_cable); - - if (IS_ERR(edev)) - return -ENOMEM; - - ret = devm_extcon_dev_register(rphy->dev, edev); - if (ret) { - dev_err(rphy->dev, "failed to register extcon device\n"); - return ret; - } - } - - rphy->edev = edev; - - return 0; -} - -static int rockchip_usb2phy_init(struct phy *phy) -{ - struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); - struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); - int ret = 0; - - mutex_lock(&rport->mutex); - - if (rport->port_id == USB2PHY_PORT_OTG) { - if (rport->mode != USB_DR_MODE_HOST) { - /* clear bvalid status and enable bvalid detect irq */ - ret = property_enable(rphy, - &rport->port_cfg->bvalid_det_clr, - true); - if (ret) - goto out; - - ret = property_enable(rphy, - &rport->port_cfg->bvalid_det_en, - true); - if (ret) - goto out; - - schedule_delayed_work(&rport->otg_sm_work, - OTG_SCHEDULE_DELAY); - } else { - /* If OTG works in host only mode, do nothing. */ - dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode); - } - } else if (rport->port_id == USB2PHY_PORT_HOST) { - /* clear linestate and enable linestate detect irq */ - ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true); - if (ret) - goto out; - - ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true); - if (ret) - goto out; - - schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY); - } - -out: - mutex_unlock(&rport->mutex); - return ret; -} - -static int rockchip_usb2phy_power_on(struct phy *phy) -{ - struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); - struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); - int ret; - - dev_dbg(&rport->phy->dev, "port power on\n"); - - if (!rport->suspended) - return 0; - - ret = clk_prepare_enable(rphy->clk480m); - if (ret) - return ret; - - ret = property_enable(rphy, &rport->port_cfg->phy_sus, false); - if (ret) - return ret; - - rport->suspended = false; - return 0; -} - -static int rockchip_usb2phy_power_off(struct phy *phy) -{ - struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); - struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); - int ret; - - dev_dbg(&rport->phy->dev, "port power off\n"); - - if (rport->suspended) - return 0; - - ret = property_enable(rphy, &rport->port_cfg->phy_sus, true); - if (ret) - return ret; - - rport->suspended = true; - clk_disable_unprepare(rphy->clk480m); - - return 0; -} - -static int rockchip_usb2phy_exit(struct phy *phy) -{ - struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); - - if (rport->port_id == USB2PHY_PORT_OTG && - rport->mode != USB_DR_MODE_HOST) { - cancel_delayed_work_sync(&rport->otg_sm_work); - cancel_delayed_work_sync(&rport->chg_work); - } else if (rport->port_id == USB2PHY_PORT_HOST) - cancel_delayed_work_sync(&rport->sm_work); - - return 0; -} - -static const struct phy_ops rockchip_usb2phy_ops = { - .init = rockchip_usb2phy_init, - .exit = rockchip_usb2phy_exit, - .power_on = rockchip_usb2phy_power_on, - .power_off = rockchip_usb2phy_power_off, - .owner = THIS_MODULE, -}; - -static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) -{ - struct rockchip_usb2phy_port *rport = - container_of(work, struct rockchip_usb2phy_port, - otg_sm_work.work); - struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); - static unsigned int cable; - unsigned long delay; - bool vbus_attach, sch_work, notify_charger; - - if (rport->utmi_avalid) - vbus_attach = - property_enabled(rphy, &rport->port_cfg->utmi_avalid); - else - vbus_attach = - property_enabled(rphy, &rport->port_cfg->utmi_bvalid); - - sch_work = false; - notify_charger = false; - delay = OTG_SCHEDULE_DELAY; - dev_dbg(&rport->phy->dev, "%s otg sm work\n", - usb_otg_state_string(rport->state)); - - switch (rport->state) { - case OTG_STATE_UNDEFINED: - rport->state = OTG_STATE_B_IDLE; - if (!vbus_attach) - rockchip_usb2phy_power_off(rport->phy); - /* fall through */ - case OTG_STATE_B_IDLE: - if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) > 0) { - dev_dbg(&rport->phy->dev, "usb otg host connect\n"); - rport->state = OTG_STATE_A_HOST; - rockchip_usb2phy_power_on(rport->phy); - return; - } else if (vbus_attach) { - dev_dbg(&rport->phy->dev, "vbus_attach\n"); - switch (rphy->chg_state) { - case USB_CHG_STATE_UNDEFINED: - schedule_delayed_work(&rport->chg_work, 0); - return; - case USB_CHG_STATE_DETECTED: - switch (rphy->chg_type) { - case POWER_SUPPLY_TYPE_USB: - dev_dbg(&rport->phy->dev, "sdp cable is connected\n"); - rockchip_usb2phy_power_on(rport->phy); - rport->state = OTG_STATE_B_PERIPHERAL; - notify_charger = true; - sch_work = true; - cable = EXTCON_CHG_USB_SDP; - break; - case POWER_SUPPLY_TYPE_USB_DCP: - dev_dbg(&rport->phy->dev, "dcp cable is connected\n"); - rockchip_usb2phy_power_off(rport->phy); - notify_charger = true; - sch_work = true; - cable = EXTCON_CHG_USB_DCP; - break; - case POWER_SUPPLY_TYPE_USB_CDP: - dev_dbg(&rport->phy->dev, "cdp cable is connected\n"); - rockchip_usb2phy_power_on(rport->phy); - rport->state = OTG_STATE_B_PERIPHERAL; - notify_charger = true; - sch_work = true; - cable = EXTCON_CHG_USB_CDP; - break; - default: - break; - } - break; - default: - break; - } - } else { - notify_charger = true; - rphy->chg_state = USB_CHG_STATE_UNDEFINED; - rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; - } - - if (rport->vbus_attached != vbus_attach) { - rport->vbus_attached = vbus_attach; - - if (notify_charger && rphy->edev) { - extcon_set_cable_state_(rphy->edev, - cable, vbus_attach); - if (cable == EXTCON_CHG_USB_SDP) - extcon_set_state_sync(rphy->edev, - EXTCON_USB, - vbus_attach); - } - } - break; - case OTG_STATE_B_PERIPHERAL: - if (!vbus_attach) { - dev_dbg(&rport->phy->dev, "usb disconnect\n"); - rphy->chg_state = USB_CHG_STATE_UNDEFINED; - rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; - rport->state = OTG_STATE_B_IDLE; - delay = 0; - rockchip_usb2phy_power_off(rport->phy); - } - sch_work = true; - break; - case OTG_STATE_A_HOST: - if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) == 0) { - dev_dbg(&rport->phy->dev, "usb otg host disconnect\n"); - rport->state = OTG_STATE_B_IDLE; - rockchip_usb2phy_power_off(rport->phy); - } - break; - default: - break; - } - - if (sch_work) - schedule_delayed_work(&rport->otg_sm_work, delay); -} - -static const char *chg_to_string(enum power_supply_type chg_type) -{ - switch (chg_type) { - case POWER_SUPPLY_TYPE_USB: - return "USB_SDP_CHARGER"; - case POWER_SUPPLY_TYPE_USB_DCP: - return "USB_DCP_CHARGER"; - case POWER_SUPPLY_TYPE_USB_CDP: - return "USB_CDP_CHARGER"; - default: - return "INVALID_CHARGER"; - } -} - -static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy, - bool en) -{ - property_enable(rphy, &rphy->phy_cfg->chg_det.rdm_pdwn_en, en); - property_enable(rphy, &rphy->phy_cfg->chg_det.idp_src_en, en); -} - -static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy, - bool en) -{ - property_enable(rphy, &rphy->phy_cfg->chg_det.vdp_src_en, en); - property_enable(rphy, &rphy->phy_cfg->chg_det.idm_sink_en, en); -} - -static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy, - bool en) -{ - property_enable(rphy, &rphy->phy_cfg->chg_det.vdm_src_en, en); - property_enable(rphy, &rphy->phy_cfg->chg_det.idp_sink_en, en); -} - -#define CHG_DCD_POLL_TIME (100 * HZ / 1000) -#define CHG_DCD_MAX_RETRIES 6 -#define CHG_PRIMARY_DET_TIME (40 * HZ / 1000) -#define CHG_SECONDARY_DET_TIME (40 * HZ / 1000) -static void rockchip_chg_detect_work(struct work_struct *work) -{ - struct rockchip_usb2phy_port *rport = - container_of(work, struct rockchip_usb2phy_port, chg_work.work); - struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); - bool is_dcd, tmout, vout; - unsigned long delay; - - dev_dbg(&rport->phy->dev, "chg detection work state = %d\n", - rphy->chg_state); - switch (rphy->chg_state) { - case USB_CHG_STATE_UNDEFINED: - if (!rport->suspended) - rockchip_usb2phy_power_off(rport->phy); - /* put the controller in non-driving mode */ - property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, false); - /* Start DCD processing stage 1 */ - rockchip_chg_enable_dcd(rphy, true); - rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; - rphy->dcd_retries = 0; - delay = CHG_DCD_POLL_TIME; - break; - case USB_CHG_STATE_WAIT_FOR_DCD: - /* get data contact detection status */ - is_dcd = property_enabled(rphy, &rphy->phy_cfg->chg_det.dp_det); - tmout = ++rphy->dcd_retries == CHG_DCD_MAX_RETRIES; - /* stage 2 */ - if (is_dcd || tmout) { - /* stage 4 */ - /* Turn off DCD circuitry */ - rockchip_chg_enable_dcd(rphy, false); - /* Voltage Source on DP, Probe on DM */ - rockchip_chg_enable_primary_det(rphy, true); - delay = CHG_PRIMARY_DET_TIME; - rphy->chg_state = USB_CHG_STATE_DCD_DONE; - } else { - /* stage 3 */ - delay = CHG_DCD_POLL_TIME; - } - break; - case USB_CHG_STATE_DCD_DONE: - vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.cp_det); - rockchip_chg_enable_primary_det(rphy, false); - if (vout) { - /* Voltage Source on DM, Probe on DP */ - rockchip_chg_enable_secondary_det(rphy, true); - delay = CHG_SECONDARY_DET_TIME; - rphy->chg_state = USB_CHG_STATE_PRIMARY_DONE; - } else { - if (rphy->dcd_retries == CHG_DCD_MAX_RETRIES) { - /* floating charger found */ - rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; - rphy->chg_state = USB_CHG_STATE_DETECTED; - delay = 0; - } else { - rphy->chg_type = POWER_SUPPLY_TYPE_USB; - rphy->chg_state = USB_CHG_STATE_DETECTED; - delay = 0; - } - } - break; - case USB_CHG_STATE_PRIMARY_DONE: - vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.dcp_det); - /* Turn off voltage source */ - rockchip_chg_enable_secondary_det(rphy, false); - if (vout) - rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; - else - rphy->chg_type = POWER_SUPPLY_TYPE_USB_CDP; - /* fall through */ - case USB_CHG_STATE_SECONDARY_DONE: - rphy->chg_state = USB_CHG_STATE_DETECTED; - delay = 0; - /* fall through */ - case USB_CHG_STATE_DETECTED: - /* put the controller in normal mode */ - property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, true); - rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); - dev_info(&rport->phy->dev, "charger = %s\n", - chg_to_string(rphy->chg_type)); - return; - default: - return; - } - - schedule_delayed_work(&rport->chg_work, delay); -} - -/* - * The function manage host-phy port state and suspend/resume phy port - * to save power. - * - * we rely on utmi_linestate and utmi_hostdisconnect to identify whether - * devices is disconnect or not. Besides, we do not need care it is FS/LS - * disconnected or HS disconnected, actually, we just only need get the - * device is disconnected at last through rearm the delayed work, - * to suspend the phy port in _PHY_STATE_DISCONNECT_ case. - * - * NOTE: It may invoke *phy_powr_off or *phy_power_on which will invoke - * some clk related APIs, so do not invoke it from interrupt context directly. - */ -static void rockchip_usb2phy_sm_work(struct work_struct *work) -{ - struct rockchip_usb2phy_port *rport = - container_of(work, struct rockchip_usb2phy_port, sm_work.work); - struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); - unsigned int sh = rport->port_cfg->utmi_hstdet.bitend - - rport->port_cfg->utmi_hstdet.bitstart + 1; - unsigned int ul, uhd, state; - unsigned int ul_mask, uhd_mask; - int ret; - - mutex_lock(&rport->mutex); - - ret = regmap_read(rphy->grf, rport->port_cfg->utmi_ls.offset, &ul); - if (ret < 0) - goto next_schedule; - - ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, - &uhd); - if (ret < 0) - goto next_schedule; - - uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend, - rport->port_cfg->utmi_hstdet.bitstart); - ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend, - rport->port_cfg->utmi_ls.bitstart); - - /* stitch on utmi_ls and utmi_hstdet as phy state */ - state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) | - (((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh); - - switch (state) { - case PHY_STATE_HS_ONLINE: - dev_dbg(&rport->phy->dev, "HS online\n"); - break; - case PHY_STATE_FS_LS_ONLINE: - /* - * For FS/LS device, the online state share with connect state - * from utmi_ls and utmi_hstdet register, so we distinguish - * them via suspended flag. - * - * Plus, there are two cases, one is D- Line pull-up, and D+ - * line pull-down, the state is 4; another is D+ line pull-up, - * and D- line pull-down, the state is 2. - */ - if (!rport->suspended) { - /* D- line pull-up, D+ line pull-down */ - dev_dbg(&rport->phy->dev, "FS/LS online\n"); - break; - } - /* fall through */ - case PHY_STATE_CONNECT: - if (rport->suspended) { - dev_dbg(&rport->phy->dev, "Connected\n"); - rockchip_usb2phy_power_on(rport->phy); - rport->suspended = false; - } else { - /* D+ line pull-up, D- line pull-down */ - dev_dbg(&rport->phy->dev, "FS/LS online\n"); - } - break; - case PHY_STATE_DISCONNECT: - if (!rport->suspended) { - dev_dbg(&rport->phy->dev, "Disconnected\n"); - rockchip_usb2phy_power_off(rport->phy); - rport->suspended = true; - } - - /* - * activate the linestate detection to get the next device - * plug-in irq. - */ - property_enable(rphy, &rport->port_cfg->ls_det_clr, true); - property_enable(rphy, &rport->port_cfg->ls_det_en, true); - - /* - * we don't need to rearm the delayed work when the phy port - * is suspended. - */ - mutex_unlock(&rport->mutex); - return; - default: - dev_dbg(&rport->phy->dev, "unknown phy state\n"); - break; - } - -next_schedule: - mutex_unlock(&rport->mutex); - schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY); -} - -static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data) -{ - struct rockchip_usb2phy_port *rport = data; - struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); - - if (!property_enabled(rphy, &rport->port_cfg->ls_det_st)) - return IRQ_NONE; - - mutex_lock(&rport->mutex); - - /* disable linestate detect irq and clear its status */ - property_enable(rphy, &rport->port_cfg->ls_det_en, false); - property_enable(rphy, &rport->port_cfg->ls_det_clr, true); - - mutex_unlock(&rport->mutex); - - /* - * In this case for host phy port, a new device is plugged in, - * meanwhile, if the phy port is suspended, we need rearm the work to - * resume it and mange its states; otherwise, we do nothing about that. - */ - if (rport->suspended && rport->port_id == USB2PHY_PORT_HOST) - rockchip_usb2phy_sm_work(&rport->sm_work.work); - - return IRQ_HANDLED; -} - -static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data) -{ - struct rockchip_usb2phy_port *rport = data; - struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); - - if (!property_enabled(rphy, &rport->port_cfg->bvalid_det_st)) - return IRQ_NONE; - - mutex_lock(&rport->mutex); - - /* clear bvalid detect irq pending status */ - property_enable(rphy, &rport->port_cfg->bvalid_det_clr, true); - - mutex_unlock(&rport->mutex); - - rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); - - return IRQ_HANDLED; -} - -static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, - struct rockchip_usb2phy_port *rport, - struct device_node *child_np) -{ - int ret; - - rport->port_id = USB2PHY_PORT_HOST; - rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_HOST]; - rport->suspended = true; - - mutex_init(&rport->mutex); - INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work); - - rport->ls_irq = of_irq_get_byname(child_np, "linestate"); - if (rport->ls_irq < 0) { - dev_err(rphy->dev, "no linestate irq provided\n"); - return rport->ls_irq; - } - - ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL, - rockchip_usb2phy_linestate_irq, - IRQF_ONESHOT, - "rockchip_usb2phy", rport); - if (ret) { - dev_err(rphy->dev, "failed to request linestate irq handle\n"); - return ret; - } - - return 0; -} - -static int rockchip_otg_event(struct notifier_block *nb, - unsigned long event, void *ptr) -{ - struct rockchip_usb2phy_port *rport = - container_of(nb, struct rockchip_usb2phy_port, event_nb); - - schedule_delayed_work(&rport->otg_sm_work, OTG_SCHEDULE_DELAY); - - return NOTIFY_DONE; -} - -static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, - struct rockchip_usb2phy_port *rport, - struct device_node *child_np) -{ - int ret; - - rport->port_id = USB2PHY_PORT_OTG; - rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG]; - rport->state = OTG_STATE_UNDEFINED; - - /* - * set suspended flag to true, but actually don't - * put phy in suspend mode, it aims to enable usb - * phy and clock in power_on() called by usb controller - * driver during probe. - */ - rport->suspended = true; - rport->vbus_attached = false; - - mutex_init(&rport->mutex); - - rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1); - if (rport->mode == USB_DR_MODE_HOST) { - ret = 0; - goto out; - } - - INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work); - INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work); - - rport->utmi_avalid = - of_property_read_bool(child_np, "rockchip,utmi-avalid"); - - rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid"); - if (rport->bvalid_irq < 0) { - dev_err(rphy->dev, "no vbus valid irq provided\n"); - ret = rport->bvalid_irq; - goto out; - } - - ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, NULL, - rockchip_usb2phy_bvalid_irq, - IRQF_ONESHOT, - "rockchip_usb2phy_bvalid", rport); - if (ret) { - dev_err(rphy->dev, "failed to request otg-bvalid irq handle\n"); - goto out; - } - - if (!IS_ERR(rphy->edev)) { - rport->event_nb.notifier_call = rockchip_otg_event; - - ret = extcon_register_notifier(rphy->edev, EXTCON_USB_HOST, - &rport->event_nb); - if (ret) - dev_err(rphy->dev, "register USB HOST notifier failed\n"); - } - -out: - return ret; -} - -static int rockchip_usb2phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct device_node *child_np; - struct phy_provider *provider; - struct rockchip_usb2phy *rphy; - const struct rockchip_usb2phy_cfg *phy_cfgs; - const struct of_device_id *match; - unsigned int reg; - int index, ret; - - rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL); - if (!rphy) - return -ENOMEM; - - match = of_match_device(dev->driver->of_match_table, dev); - if (!match || !match->data) { - dev_err(dev, "phy configs are not assigned!\n"); - return -EINVAL; - } - - if (!dev->parent || !dev->parent->of_node) - return -EINVAL; - - rphy->grf = syscon_node_to_regmap(dev->parent->of_node); - if (IS_ERR(rphy->grf)) - return PTR_ERR(rphy->grf); - - if (of_property_read_u32(np, "reg", ®)) { - dev_err(dev, "the reg property is not assigned in %s node\n", - np->name); - return -EINVAL; - } - - rphy->dev = dev; - phy_cfgs = match->data; - rphy->chg_state = USB_CHG_STATE_UNDEFINED; - rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; - platform_set_drvdata(pdev, rphy); - - ret = rockchip_usb2phy_extcon_register(rphy); - if (ret) - return ret; - - /* find out a proper config which can be matched with dt. */ - index = 0; - while (phy_cfgs[index].reg) { - if (phy_cfgs[index].reg == reg) { - rphy->phy_cfg = &phy_cfgs[index]; - break; - } - - ++index; - } - - if (!rphy->phy_cfg) { - dev_err(dev, "no phy-config can be matched with %s node\n", - np->name); - return -EINVAL; - } - - rphy->clk = of_clk_get_by_name(np, "phyclk"); - if (!IS_ERR(rphy->clk)) { - clk_prepare_enable(rphy->clk); - } else { - dev_info(&pdev->dev, "no phyclk specified\n"); - rphy->clk = NULL; - } - - ret = rockchip_usb2phy_clk480m_register(rphy); - if (ret) { - dev_err(dev, "failed to register 480m output clock\n"); - goto disable_clks; - } - - index = 0; - for_each_available_child_of_node(np, child_np) { - struct rockchip_usb2phy_port *rport = &rphy->ports[index]; - struct phy *phy; - - /* This driver aims to support both otg-port and host-port */ - if (of_node_cmp(child_np->name, "host-port") && - of_node_cmp(child_np->name, "otg-port")) - goto next_child; - - phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops); - if (IS_ERR(phy)) { - dev_err(dev, "failed to create phy\n"); - ret = PTR_ERR(phy); - goto put_child; - } - - rport->phy = phy; - phy_set_drvdata(rport->phy, rport); - - /* initialize otg/host port separately */ - if (!of_node_cmp(child_np->name, "host-port")) { - ret = rockchip_usb2phy_host_port_init(rphy, rport, - child_np); - if (ret) - goto put_child; - } else { - ret = rockchip_usb2phy_otg_port_init(rphy, rport, - child_np); - if (ret) - goto put_child; - } - -next_child: - /* to prevent out of boundary */ - if (++index >= rphy->phy_cfg->num_ports) - break; - } - - provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - return PTR_ERR_OR_ZERO(provider); - -put_child: - of_node_put(child_np); -disable_clks: - if (rphy->clk) { - clk_disable_unprepare(rphy->clk); - clk_put(rphy->clk); - } - return ret; -} - -static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = { - { - .reg = 0x100, - .num_ports = 2, - .clkout_ctl = { 0x108, 4, 4, 1, 0 }, - .port_cfgs = { - [USB2PHY_PORT_OTG] = { - .phy_sus = { 0x0100, 15, 0, 0, 0x1d1 }, - .bvalid_det_en = { 0x0110, 2, 2, 0, 1 }, - .bvalid_det_st = { 0x0114, 2, 2, 0, 1 }, - .bvalid_det_clr = { 0x0118, 2, 2, 0, 1 }, - .ls_det_en = { 0x0110, 0, 0, 0, 1 }, - .ls_det_st = { 0x0114, 0, 0, 0, 1 }, - .ls_det_clr = { 0x0118, 0, 0, 0, 1 }, - .utmi_avalid = { 0x0120, 10, 10, 0, 1 }, - .utmi_bvalid = { 0x0120, 9, 9, 0, 1 }, - .utmi_ls = { 0x0120, 5, 4, 0, 1 }, - }, - [USB2PHY_PORT_HOST] = { - .phy_sus = { 0x104, 15, 0, 0, 0x1d1 }, - .ls_det_en = { 0x110, 1, 1, 0, 1 }, - .ls_det_st = { 0x114, 1, 1, 0, 1 }, - .ls_det_clr = { 0x118, 1, 1, 0, 1 }, - .utmi_ls = { 0x120, 17, 16, 0, 1 }, - .utmi_hstdet = { 0x120, 19, 19, 0, 1 } - } - }, - .chg_det = { - .opmode = { 0x0100, 3, 0, 5, 1 }, - .cp_det = { 0x0120, 24, 24, 0, 1 }, - .dcp_det = { 0x0120, 23, 23, 0, 1 }, - .dp_det = { 0x0120, 25, 25, 0, 1 }, - .idm_sink_en = { 0x0108, 8, 8, 0, 1 }, - .idp_sink_en = { 0x0108, 7, 7, 0, 1 }, - .idp_src_en = { 0x0108, 9, 9, 0, 1 }, - .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 }, - .vdm_src_en = { 0x0108, 12, 12, 0, 1 }, - .vdp_src_en = { 0x0108, 11, 11, 0, 1 }, - }, - }, - { /* sentinel */ } -}; - -static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = { - { - .reg = 0x700, - .num_ports = 2, - .clkout_ctl = { 0x0724, 15, 15, 1, 0 }, - .port_cfgs = { - [USB2PHY_PORT_HOST] = { - .phy_sus = { 0x0728, 15, 0, 0, 0x1d1 }, - .ls_det_en = { 0x0680, 4, 4, 0, 1 }, - .ls_det_st = { 0x0690, 4, 4, 0, 1 }, - .ls_det_clr = { 0x06a0, 4, 4, 0, 1 }, - .utmi_ls = { 0x049c, 14, 13, 0, 1 }, - .utmi_hstdet = { 0x049c, 12, 12, 0, 1 } - } - }, - }, - { /* sentinel */ } -}; - -static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { - { - .reg = 0xe450, - .num_ports = 2, - .clkout_ctl = { 0xe450, 4, 4, 1, 0 }, - .port_cfgs = { - [USB2PHY_PORT_OTG] = { - .phy_sus = { 0xe454, 1, 0, 2, 1 }, - .bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 }, - .bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 }, - .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 }, - .utmi_avalid = { 0xe2ac, 7, 7, 0, 1 }, - .utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 }, - }, - [USB2PHY_PORT_HOST] = { - .phy_sus = { 0xe458, 1, 0, 0x2, 0x1 }, - .ls_det_en = { 0xe3c0, 6, 6, 0, 1 }, - .ls_det_st = { 0xe3e0, 6, 6, 0, 1 }, - .ls_det_clr = { 0xe3d0, 6, 6, 0, 1 }, - .utmi_ls = { 0xe2ac, 22, 21, 0, 1 }, - .utmi_hstdet = { 0xe2ac, 23, 23, 0, 1 } - } - }, - .chg_det = { - .opmode = { 0xe454, 3, 0, 5, 1 }, - .cp_det = { 0xe2ac, 2, 2, 0, 1 }, - .dcp_det = { 0xe2ac, 1, 1, 0, 1 }, - .dp_det = { 0xe2ac, 0, 0, 0, 1 }, - .idm_sink_en = { 0xe450, 8, 8, 0, 1 }, - .idp_sink_en = { 0xe450, 7, 7, 0, 1 }, - .idp_src_en = { 0xe450, 9, 9, 0, 1 }, - .rdm_pdwn_en = { 0xe450, 10, 10, 0, 1 }, - .vdm_src_en = { 0xe450, 12, 12, 0, 1 }, - .vdp_src_en = { 0xe450, 11, 11, 0, 1 }, - }, - }, - { - .reg = 0xe460, - .num_ports = 2, - .clkout_ctl = { 0xe460, 4, 4, 1, 0 }, - .port_cfgs = { - [USB2PHY_PORT_OTG] = { - .phy_sus = { 0xe464, 1, 0, 2, 1 }, - .bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 }, - .bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 }, - .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 }, - .utmi_avalid = { 0xe2ac, 10, 10, 0, 1 }, - .utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 }, - }, - [USB2PHY_PORT_HOST] = { - .phy_sus = { 0xe468, 1, 0, 0x2, 0x1 }, - .ls_det_en = { 0xe3c0, 11, 11, 0, 1 }, - .ls_det_st = { 0xe3e0, 11, 11, 0, 1 }, - .ls_det_clr = { 0xe3d0, 11, 11, 0, 1 }, - .utmi_ls = { 0xe2ac, 26, 25, 0, 1 }, - .utmi_hstdet = { 0xe2ac, 27, 27, 0, 1 } - } - }, - }, - { /* sentinel */ } -}; - -static const struct of_device_id rockchip_usb2phy_dt_match[] = { - { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, - { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, - { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, - {} -}; -MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match); - -static struct platform_driver rockchip_usb2phy_driver = { - .probe = rockchip_usb2phy_probe, - .driver = { - .name = "rockchip-usb2phy", - .of_match_table = rockchip_usb2phy_dt_match, - }, -}; -module_platform_driver(rockchip_usb2phy_driver); - -MODULE_AUTHOR("Frank Wang "); -MODULE_DESCRIPTION("Rockchip USB2.0 PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-rockchip-pcie.c b/drivers/phy/phy-rockchip-pcie.c deleted file mode 100644 index 6904633cad68..000000000000 --- a/drivers/phy/phy-rockchip-pcie.c +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Rockchip PCIe PHY driver - * - * Copyright (C) 2016 Shawn Lin - * Copyright (C) 2016 ROCKCHIP, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * The higher 16-bit of this register is used for write protection - * only if BIT(x + 16) set to 1 the BIT(x) can be written. - */ -#define HIWORD_UPDATE(val, mask, shift) \ - ((val) << (shift) | (mask) << ((shift) + 16)) - -#define PHY_MAX_LANE_NUM 4 -#define PHY_CFG_DATA_SHIFT 7 -#define PHY_CFG_ADDR_SHIFT 1 -#define PHY_CFG_DATA_MASK 0xf -#define PHY_CFG_ADDR_MASK 0x3f -#define PHY_CFG_RD_MASK 0x3ff -#define PHY_CFG_WR_ENABLE 1 -#define PHY_CFG_WR_DISABLE 1 -#define PHY_CFG_WR_SHIFT 0 -#define PHY_CFG_WR_MASK 1 -#define PHY_CFG_PLL_LOCK 0x10 -#define PHY_CFG_CLK_TEST 0x10 -#define PHY_CFG_CLK_SCC 0x12 -#define PHY_CFG_SEPE_RATE BIT(3) -#define PHY_CFG_PLL_100M BIT(3) -#define PHY_PLL_LOCKED BIT(9) -#define PHY_PLL_OUTPUT BIT(10) -#define PHY_LANE_A_STATUS 0x30 -#define PHY_LANE_B_STATUS 0x31 -#define PHY_LANE_C_STATUS 0x32 -#define PHY_LANE_D_STATUS 0x33 -#define PHY_LANE_RX_DET_SHIFT 11 -#define PHY_LANE_RX_DET_TH 0x1 -#define PHY_LANE_IDLE_OFF 0x1 -#define PHY_LANE_IDLE_MASK 0x1 -#define PHY_LANE_IDLE_A_SHIFT 3 -#define PHY_LANE_IDLE_B_SHIFT 4 -#define PHY_LANE_IDLE_C_SHIFT 5 -#define PHY_LANE_IDLE_D_SHIFT 6 - -struct rockchip_pcie_data { - unsigned int pcie_conf; - unsigned int pcie_status; - unsigned int pcie_laneoff; -}; - -struct rockchip_pcie_phy { - struct rockchip_pcie_data *phy_data; - struct regmap *reg_base; - struct reset_control *phy_rst; - struct clk *clk_pciephy_ref; -}; - -static inline void phy_wr_cfg(struct rockchip_pcie_phy *rk_phy, - u32 addr, u32 data) -{ - regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, - HIWORD_UPDATE(data, - PHY_CFG_DATA_MASK, - PHY_CFG_DATA_SHIFT) | - HIWORD_UPDATE(addr, - PHY_CFG_ADDR_MASK, - PHY_CFG_ADDR_SHIFT)); - udelay(1); - regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, - HIWORD_UPDATE(PHY_CFG_WR_ENABLE, - PHY_CFG_WR_MASK, - PHY_CFG_WR_SHIFT)); - udelay(1); - regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, - HIWORD_UPDATE(PHY_CFG_WR_DISABLE, - PHY_CFG_WR_MASK, - PHY_CFG_WR_SHIFT)); -} - -static inline u32 phy_rd_cfg(struct rockchip_pcie_phy *rk_phy, - u32 addr) -{ - u32 val; - - regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, - HIWORD_UPDATE(addr, - PHY_CFG_RD_MASK, - PHY_CFG_ADDR_SHIFT)); - regmap_read(rk_phy->reg_base, - rk_phy->phy_data->pcie_status, - &val); - return val; -} - -static int rockchip_pcie_phy_power_off(struct phy *phy) -{ - struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); - int err = 0; - - err = reset_control_assert(rk_phy->phy_rst); - if (err) { - dev_err(&phy->dev, "assert phy_rst err %d\n", err); - return err; - } - - return 0; -} - -static int rockchip_pcie_phy_power_on(struct phy *phy) -{ - struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); - int err = 0; - u32 status; - unsigned long timeout; - - err = reset_control_deassert(rk_phy->phy_rst); - if (err) { - dev_err(&phy->dev, "deassert phy_rst err %d\n", err); - return err; - } - - regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, - HIWORD_UPDATE(PHY_CFG_PLL_LOCK, - PHY_CFG_ADDR_MASK, - PHY_CFG_ADDR_SHIFT)); - - /* - * No documented timeout value for phy operation below, - * so we make it large enough here. And we use loop-break - * method which should not be harmful. - */ - timeout = jiffies + msecs_to_jiffies(1000); - - err = -EINVAL; - while (time_before(jiffies, timeout)) { - regmap_read(rk_phy->reg_base, - rk_phy->phy_data->pcie_status, - &status); - if (status & PHY_PLL_LOCKED) { - dev_dbg(&phy->dev, "pll locked!\n"); - err = 0; - break; - } - msleep(20); - } - - if (err) { - dev_err(&phy->dev, "pll lock timeout!\n"); - goto err_pll_lock; - } - - phy_wr_cfg(rk_phy, PHY_CFG_CLK_TEST, PHY_CFG_SEPE_RATE); - phy_wr_cfg(rk_phy, PHY_CFG_CLK_SCC, PHY_CFG_PLL_100M); - - err = -ETIMEDOUT; - while (time_before(jiffies, timeout)) { - regmap_read(rk_phy->reg_base, - rk_phy->phy_data->pcie_status, - &status); - if (!(status & PHY_PLL_OUTPUT)) { - dev_dbg(&phy->dev, "pll output enable done!\n"); - err = 0; - break; - } - msleep(20); - } - - if (err) { - dev_err(&phy->dev, "pll output enable timeout!\n"); - goto err_pll_lock; - } - - regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, - HIWORD_UPDATE(PHY_CFG_PLL_LOCK, - PHY_CFG_ADDR_MASK, - PHY_CFG_ADDR_SHIFT)); - err = -EINVAL; - while (time_before(jiffies, timeout)) { - regmap_read(rk_phy->reg_base, - rk_phy->phy_data->pcie_status, - &status); - if (status & PHY_PLL_LOCKED) { - dev_dbg(&phy->dev, "pll relocked!\n"); - err = 0; - break; - } - msleep(20); - } - - if (err) { - dev_err(&phy->dev, "pll relock timeout!\n"); - goto err_pll_lock; - } - - return 0; - -err_pll_lock: - reset_control_assert(rk_phy->phy_rst); - return err; -} - -static int rockchip_pcie_phy_init(struct phy *phy) -{ - struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); - int err = 0; - - err = clk_prepare_enable(rk_phy->clk_pciephy_ref); - if (err) { - dev_err(&phy->dev, "Fail to enable pcie ref clock.\n"); - goto err_refclk; - } - - err = reset_control_assert(rk_phy->phy_rst); - if (err) { - dev_err(&phy->dev, "assert phy_rst err %d\n", err); - goto err_reset; - } - - return err; - -err_reset: - clk_disable_unprepare(rk_phy->clk_pciephy_ref); -err_refclk: - return err; -} - -static int rockchip_pcie_phy_exit(struct phy *phy) -{ - struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); - - clk_disable_unprepare(rk_phy->clk_pciephy_ref); - - return 0; -} - -static const struct phy_ops ops = { - .init = rockchip_pcie_phy_init, - .exit = rockchip_pcie_phy_exit, - .power_on = rockchip_pcie_phy_power_on, - .power_off = rockchip_pcie_phy_power_off, - .owner = THIS_MODULE, -}; - -static const struct rockchip_pcie_data rk3399_pcie_data = { - .pcie_conf = 0xe220, - .pcie_status = 0xe2a4, - .pcie_laneoff = 0xe214, -}; - -static const struct of_device_id rockchip_pcie_phy_dt_ids[] = { - { - .compatible = "rockchip,rk3399-pcie-phy", - .data = &rk3399_pcie_data, - }, - {} -}; - -MODULE_DEVICE_TABLE(of, rockchip_pcie_phy_dt_ids); - -static int rockchip_pcie_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct rockchip_pcie_phy *rk_phy; - struct phy *generic_phy; - struct phy_provider *phy_provider; - struct regmap *grf; - const struct of_device_id *of_id; - - grf = syscon_node_to_regmap(dev->parent->of_node); - if (IS_ERR(grf)) { - dev_err(dev, "Cannot find GRF syscon\n"); - return PTR_ERR(grf); - } - - rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL); - if (!rk_phy) - return -ENOMEM; - - of_id = of_match_device(rockchip_pcie_phy_dt_ids, &pdev->dev); - if (!of_id) - return -EINVAL; - - rk_phy->phy_data = (struct rockchip_pcie_data *)of_id->data; - rk_phy->reg_base = grf; - - rk_phy->phy_rst = devm_reset_control_get(dev, "phy"); - if (IS_ERR(rk_phy->phy_rst)) { - if (PTR_ERR(rk_phy->phy_rst) != -EPROBE_DEFER) - dev_err(dev, - "missing phy property for reset controller\n"); - return PTR_ERR(rk_phy->phy_rst); - } - - rk_phy->clk_pciephy_ref = devm_clk_get(dev, "refclk"); - if (IS_ERR(rk_phy->clk_pciephy_ref)) { - dev_err(dev, "refclk not found.\n"); - return PTR_ERR(rk_phy->clk_pciephy_ref); - } - - generic_phy = devm_phy_create(dev, dev->of_node, &ops); - if (IS_ERR(generic_phy)) { - dev_err(dev, "failed to create PHY\n"); - return PTR_ERR(generic_phy); - } - - phy_set_drvdata(generic_phy, rk_phy); - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static struct platform_driver rockchip_pcie_driver = { - .probe = rockchip_pcie_phy_probe, - .driver = { - .name = "rockchip-pcie-phy", - .of_match_table = rockchip_pcie_phy_dt_ids, - }, -}; - -module_platform_driver(rockchip_pcie_driver); - -MODULE_AUTHOR("Shawn Lin "); -MODULE_DESCRIPTION("Rockchip PCIe PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-rockchip-typec.c b/drivers/phy/phy-rockchip-typec.c deleted file mode 100644 index 7cfb0f8995de..000000000000 --- a/drivers/phy/phy-rockchip-typec.c +++ /dev/null @@ -1,1023 +0,0 @@ -/* - * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd - * Author: Chris Zhong - * Kever Yang - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * The ROCKCHIP Type-C PHY has two PLL clocks. The first PLL clock - * is used for USB3, the second PLL clock is used for DP. This Type-C PHY has - * 3 working modes: USB3 only mode, DP only mode, and USB3+DP mode. - * At USB3 only mode, both PLL clocks need to be initialized, this allows the - * PHY to switch mode between USB3 and USB3+DP, without disconnecting the USB - * device. - * In The DP only mode, only the DP PLL needs to be powered on, and the 4 lanes - * are all used for DP. - * - * This driver gets extcon cable state and property, then decides which mode to - * select: - * - * 1. USB3 only mode: - * EXTCON_USB or EXTCON_USB_HOST state is true, and - * EXTCON_PROP_USB_SS property is true. - * EXTCON_DISP_DP state is false. - * - * 2. DP only mode: - * EXTCON_DISP_DP state is true, and - * EXTCON_PROP_USB_SS property is false. - * If EXTCON_USB_HOST state is true, it is DP + USB2 mode, since the USB2 phy - * is a separate phy, so this case is still DP only mode. - * - * 3. USB3+DP mode: - * EXTCON_USB_HOST and EXTCON_DISP_DP are both true, and - * EXTCON_PROP_USB_SS property is true. - * - * This Type-C PHY driver supports normal and flip orientation. The orientation - * is reported by the EXTCON_PROP_USB_TYPEC_POLARITY property: true is flip - * orientation, false is normal orientation. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define CMN_SSM_BANDGAP (0x21 << 2) -#define CMN_SSM_BIAS (0x22 << 2) -#define CMN_PLLSM0_PLLEN (0x29 << 2) -#define CMN_PLLSM0_PLLPRE (0x2a << 2) -#define CMN_PLLSM0_PLLVREF (0x2b << 2) -#define CMN_PLLSM0_PLLLOCK (0x2c << 2) -#define CMN_PLLSM1_PLLEN (0x31 << 2) -#define CMN_PLLSM1_PLLPRE (0x32 << 2) -#define CMN_PLLSM1_PLLVREF (0x33 << 2) -#define CMN_PLLSM1_PLLLOCK (0x34 << 2) -#define CMN_PLLSM1_USER_DEF_CTRL (0x37 << 2) -#define CMN_ICAL_OVRD (0xc1 << 2) -#define CMN_PLL0_VCOCAL_OVRD (0x83 << 2) -#define CMN_PLL0_VCOCAL_INIT (0x84 << 2) -#define CMN_PLL0_VCOCAL_ITER (0x85 << 2) -#define CMN_PLL0_LOCK_REFCNT_START (0x90 << 2) -#define CMN_PLL0_LOCK_PLLCNT_START (0x92 << 2) -#define CMN_PLL0_LOCK_PLLCNT_THR (0x93 << 2) -#define CMN_PLL0_INTDIV (0x94 << 2) -#define CMN_PLL0_FRACDIV (0x95 << 2) -#define CMN_PLL0_HIGH_THR (0x96 << 2) -#define CMN_PLL0_DSM_DIAG (0x97 << 2) -#define CMN_PLL0_SS_CTRL1 (0x98 << 2) -#define CMN_PLL0_SS_CTRL2 (0x99 << 2) -#define CMN_PLL1_VCOCAL_START (0xa1 << 2) -#define CMN_PLL1_VCOCAL_OVRD (0xa3 << 2) -#define CMN_PLL1_VCOCAL_INIT (0xa4 << 2) -#define CMN_PLL1_VCOCAL_ITER (0xa5 << 2) -#define CMN_PLL1_LOCK_REFCNT_START (0xb0 << 2) -#define CMN_PLL1_LOCK_PLLCNT_START (0xb2 << 2) -#define CMN_PLL1_LOCK_PLLCNT_THR (0xb3 << 2) -#define CMN_PLL1_INTDIV (0xb4 << 2) -#define CMN_PLL1_FRACDIV (0xb5 << 2) -#define CMN_PLL1_HIGH_THR (0xb6 << 2) -#define CMN_PLL1_DSM_DIAG (0xb7 << 2) -#define CMN_PLL1_SS_CTRL1 (0xb8 << 2) -#define CMN_PLL1_SS_CTRL2 (0xb9 << 2) -#define CMN_RXCAL_OVRD (0xd1 << 2) -#define CMN_TXPUCAL_CTRL (0xe0 << 2) -#define CMN_TXPUCAL_OVRD (0xe1 << 2) -#define CMN_TXPDCAL_OVRD (0xf1 << 2) -#define CMN_DIAG_PLL0_FBH_OVRD (0x1c0 << 2) -#define CMN_DIAG_PLL0_FBL_OVRD (0x1c1 << 2) -#define CMN_DIAG_PLL0_OVRD (0x1c2 << 2) -#define CMN_DIAG_PLL0_V2I_TUNE (0x1c5 << 2) -#define CMN_DIAG_PLL0_CP_TUNE (0x1c6 << 2) -#define CMN_DIAG_PLL0_LF_PROG (0x1c7 << 2) -#define CMN_DIAG_PLL1_FBH_OVRD (0x1d0 << 2) -#define CMN_DIAG_PLL1_FBL_OVRD (0x1d1 << 2) -#define CMN_DIAG_PLL1_OVRD (0x1d2 << 2) -#define CMN_DIAG_PLL1_V2I_TUNE (0x1d5 << 2) -#define CMN_DIAG_PLL1_CP_TUNE (0x1d6 << 2) -#define CMN_DIAG_PLL1_LF_PROG (0x1d7 << 2) -#define CMN_DIAG_PLL1_PTATIS_TUNE1 (0x1d8 << 2) -#define CMN_DIAG_PLL1_PTATIS_TUNE2 (0x1d9 << 2) -#define CMN_DIAG_PLL1_INCLK_CTRL (0x1da << 2) -#define CMN_DIAG_HSCLK_SEL (0x1e0 << 2) - -#define XCVR_PSM_RCTRL(n) ((0x4001 | ((n) << 9)) << 2) -#define XCVR_PSM_CAL_TMR(n) ((0x4002 | ((n) << 9)) << 2) -#define XCVR_PSM_A0IN_TMR(n) ((0x4003 | ((n) << 9)) << 2) -#define TX_TXCC_CAL_SCLR_MULT(n) ((0x4047 | ((n) << 9)) << 2) -#define TX_TXCC_CPOST_MULT_00(n) ((0x404c | ((n) << 9)) << 2) -#define TX_TXCC_CPOST_MULT_01(n) ((0x404d | ((n) << 9)) << 2) -#define TX_TXCC_CPOST_MULT_10(n) ((0x404e | ((n) << 9)) << 2) -#define TX_TXCC_CPOST_MULT_11(n) ((0x404f | ((n) << 9)) << 2) -#define TX_TXCC_MGNFS_MULT_000(n) ((0x4050 | ((n) << 9)) << 2) -#define TX_TXCC_MGNFS_MULT_001(n) ((0x4051 | ((n) << 9)) << 2) -#define TX_TXCC_MGNFS_MULT_010(n) ((0x4052 | ((n) << 9)) << 2) -#define TX_TXCC_MGNFS_MULT_011(n) ((0x4053 | ((n) << 9)) << 2) -#define TX_TXCC_MGNFS_MULT_100(n) ((0x4054 | ((n) << 9)) << 2) -#define TX_TXCC_MGNFS_MULT_101(n) ((0x4055 | ((n) << 9)) << 2) -#define TX_TXCC_MGNFS_MULT_110(n) ((0x4056 | ((n) << 9)) << 2) -#define TX_TXCC_MGNFS_MULT_111(n) ((0x4057 | ((n) << 9)) << 2) -#define XCVR_DIAG_PLLDRC_CTRL(n) ((0x40e0 | ((n) << 9)) << 2) -#define XCVR_DIAG_BIDI_CTRL(n) ((0x40e8 | ((n) << 9)) << 2) -#define XCVR_DIAG_LANE_FCM_EN_MGN(n) ((0x40f2 | ((n) << 9)) << 2) -#define TX_PSC_A0(n) ((0x4100 | ((n) << 9)) << 2) -#define TX_PSC_A1(n) ((0x4101 | ((n) << 9)) << 2) -#define TX_PSC_A2(n) ((0x4102 | ((n) << 9)) << 2) -#define TX_PSC_A3(n) ((0x4103 | ((n) << 9)) << 2) -#define TX_RCVDET_CTRL(n) ((0x4120 | ((n) << 9)) << 2) -#define TX_RCVDET_EN_TMR(n) ((0x4122 | ((n) << 9)) << 2) -#define TX_RCVDET_ST_TMR(n) ((0x4123 | ((n) << 9)) << 2) -#define TX_DIAG_TX_DRV(n) ((0x41e1 | ((n) << 9)) << 2) -#define TX_DIAG_BGREF_PREDRV_DELAY (0x41e7 << 2) -#define TX_ANA_CTRL_REG_1 (0x5020 << 2) -#define TX_ANA_CTRL_REG_2 (0x5021 << 2) -#define TXDA_COEFF_CALC_CTRL (0x5022 << 2) -#define TX_DIG_CTRL_REG_2 (0x5024 << 2) -#define TXDA_CYA_AUXDA_CYA (0x5025 << 2) -#define TX_ANA_CTRL_REG_3 (0x5026 << 2) -#define TX_ANA_CTRL_REG_4 (0x5027 << 2) -#define TX_ANA_CTRL_REG_5 (0x5029 << 2) - -#define RX_PSC_A0(n) ((0x8000 | ((n) << 9)) << 2) -#define RX_PSC_A1(n) ((0x8001 | ((n) << 9)) << 2) -#define RX_PSC_A2(n) ((0x8002 | ((n) << 9)) << 2) -#define RX_PSC_A3(n) ((0x8003 | ((n) << 9)) << 2) -#define RX_PSC_CAL(n) ((0x8006 | ((n) << 9)) << 2) -#define RX_PSC_RDY(n) ((0x8007 | ((n) << 9)) << 2) -#define RX_IQPI_ILL_CAL_OVRD (0x8023 << 2) -#define RX_EPI_ILL_CAL_OVRD (0x8033 << 2) -#define RX_SDCAL0_OVRD (0x8041 << 2) -#define RX_SDCAL1_OVRD (0x8049 << 2) -#define RX_SLC_INIT (0x806d << 2) -#define RX_SLC_RUN (0x806e << 2) -#define RX_CDRLF_CNFG2 (0x8081 << 2) -#define RX_SIGDET_HL_FILT_TMR(n) ((0x8090 | ((n) << 9)) << 2) -#define RX_SLC_IOP0_OVRD (0x8101 << 2) -#define RX_SLC_IOP1_OVRD (0x8105 << 2) -#define RX_SLC_QOP0_OVRD (0x8109 << 2) -#define RX_SLC_QOP1_OVRD (0x810d << 2) -#define RX_SLC_EOP0_OVRD (0x8111 << 2) -#define RX_SLC_EOP1_OVRD (0x8115 << 2) -#define RX_SLC_ION0_OVRD (0x8119 << 2) -#define RX_SLC_ION1_OVRD (0x811d << 2) -#define RX_SLC_QON0_OVRD (0x8121 << 2) -#define RX_SLC_QON1_OVRD (0x8125 << 2) -#define RX_SLC_EON0_OVRD (0x8129 << 2) -#define RX_SLC_EON1_OVRD (0x812d << 2) -#define RX_SLC_IEP0_OVRD (0x8131 << 2) -#define RX_SLC_IEP1_OVRD (0x8135 << 2) -#define RX_SLC_QEP0_OVRD (0x8139 << 2) -#define RX_SLC_QEP1_OVRD (0x813d << 2) -#define RX_SLC_EEP0_OVRD (0x8141 << 2) -#define RX_SLC_EEP1_OVRD (0x8145 << 2) -#define RX_SLC_IEN0_OVRD (0x8149 << 2) -#define RX_SLC_IEN1_OVRD (0x814d << 2) -#define RX_SLC_QEN0_OVRD (0x8151 << 2) -#define RX_SLC_QEN1_OVRD (0x8155 << 2) -#define RX_SLC_EEN0_OVRD (0x8159 << 2) -#define RX_SLC_EEN1_OVRD (0x815d << 2) -#define RX_REE_CTRL_DATA_MASK(n) ((0x81bb | ((n) << 9)) << 2) -#define RX_DIAG_SIGDET_TUNE(n) ((0x81dc | ((n) << 9)) << 2) -#define RX_DIAG_SC2C_DELAY (0x81e1 << 2) - -#define PMA_LANE_CFG (0xc000 << 2) -#define PIPE_CMN_CTRL1 (0xc001 << 2) -#define PIPE_CMN_CTRL2 (0xc002 << 2) -#define PIPE_COM_LOCK_CFG1 (0xc003 << 2) -#define PIPE_COM_LOCK_CFG2 (0xc004 << 2) -#define PIPE_RCV_DET_INH (0xc005 << 2) -#define DP_MODE_CTL (0xc008 << 2) -#define DP_CLK_CTL (0xc009 << 2) -#define STS (0xc00F << 2) -#define PHY_ISO_CMN_CTRL (0xc010 << 2) -#define PHY_DP_TX_CTL (0xc408 << 2) -#define PMA_CMN_CTRL1 (0xc800 << 2) -#define PHY_PMA_ISO_CMN_CTRL (0xc810 << 2) -#define PHY_ISOLATION_CTRL (0xc81f << 2) -#define PHY_PMA_ISO_XCVR_CTRL(n) ((0xcc11 | ((n) << 6)) << 2) -#define PHY_PMA_ISO_LINK_MODE(n) ((0xcc12 | ((n) << 6)) << 2) -#define PHY_PMA_ISO_PWRST_CTRL(n) ((0xcc13 | ((n) << 6)) << 2) -#define PHY_PMA_ISO_TX_DATA_LO(n) ((0xcc14 | ((n) << 6)) << 2) -#define PHY_PMA_ISO_TX_DATA_HI(n) ((0xcc15 | ((n) << 6)) << 2) -#define PHY_PMA_ISO_RX_DATA_LO(n) ((0xcc16 | ((n) << 6)) << 2) -#define PHY_PMA_ISO_RX_DATA_HI(n) ((0xcc17 | ((n) << 6)) << 2) -#define TX_BIST_CTRL(n) ((0x4140 | ((n) << 9)) << 2) -#define TX_BIST_UDDWR(n) ((0x4141 | ((n) << 9)) << 2) - -/* - * Selects which PLL clock will be driven on the analog high speed - * clock 0: PLL 0 div 1 - * clock 1: PLL 1 div 2 - */ -#define CLK_PLL_CONFIG 0X30 -#define CLK_PLL_MASK 0x33 - -#define CMN_READY BIT(0) - -#define DP_PLL_CLOCK_ENABLE BIT(2) -#define DP_PLL_ENABLE BIT(0) -#define DP_PLL_DATA_RATE_RBR ((2 << 12) | (4 << 8)) -#define DP_PLL_DATA_RATE_HBR ((2 << 12) | (4 << 8)) -#define DP_PLL_DATA_RATE_HBR2 ((1 << 12) | (2 << 8)) - -#define DP_MODE_A0 BIT(4) -#define DP_MODE_A2 BIT(6) -#define DP_MODE_ENTER_A0 0xc101 -#define DP_MODE_ENTER_A2 0xc104 - -#define PHY_MODE_SET_TIMEOUT 100000 - -#define PIN_ASSIGN_C_E 0x51d9 -#define PIN_ASSIGN_D_F 0x5100 - -#define MODE_DISCONNECT 0 -#define MODE_UFP_USB BIT(0) -#define MODE_DFP_USB BIT(1) -#define MODE_DFP_DP BIT(2) - -struct usb3phy_reg { - u32 offset; - u32 enable_bit; - u32 write_enable; -}; - -struct rockchip_usb3phy_port_cfg { - struct usb3phy_reg typec_conn_dir; - struct usb3phy_reg usb3tousb2_en; - struct usb3phy_reg external_psm; - struct usb3phy_reg pipe_status; -}; - -struct rockchip_typec_phy { - struct device *dev; - void __iomem *base; - struct extcon_dev *extcon; - struct regmap *grf_regs; - struct clk *clk_core; - struct clk *clk_ref; - struct reset_control *uphy_rst; - struct reset_control *pipe_rst; - struct reset_control *tcphy_rst; - struct rockchip_usb3phy_port_cfg port_cfgs; - /* mutex to protect access to individual PHYs */ - struct mutex lock; - - bool flip; - u8 mode; -}; - -struct phy_reg { - u16 value; - u32 addr; -}; - -struct phy_reg usb3_pll_cfg[] = { - { 0xf0, CMN_PLL0_VCOCAL_INIT }, - { 0x18, CMN_PLL0_VCOCAL_ITER }, - { 0xd0, CMN_PLL0_INTDIV }, - { 0x4a4a, CMN_PLL0_FRACDIV }, - { 0x34, CMN_PLL0_HIGH_THR }, - { 0x1ee, CMN_PLL0_SS_CTRL1 }, - { 0x7f03, CMN_PLL0_SS_CTRL2 }, - { 0x20, CMN_PLL0_DSM_DIAG }, - { 0, CMN_DIAG_PLL0_OVRD }, - { 0, CMN_DIAG_PLL0_FBH_OVRD }, - { 0, CMN_DIAG_PLL0_FBL_OVRD }, - { 0x7, CMN_DIAG_PLL0_V2I_TUNE }, - { 0x45, CMN_DIAG_PLL0_CP_TUNE }, - { 0x8, CMN_DIAG_PLL0_LF_PROG }, -}; - -struct phy_reg dp_pll_cfg[] = { - { 0xf0, CMN_PLL1_VCOCAL_INIT }, - { 0x18, CMN_PLL1_VCOCAL_ITER }, - { 0x30b9, CMN_PLL1_VCOCAL_START }, - { 0x21c, CMN_PLL1_INTDIV }, - { 0, CMN_PLL1_FRACDIV }, - { 0x5, CMN_PLL1_HIGH_THR }, - { 0x35, CMN_PLL1_SS_CTRL1 }, - { 0x7f1e, CMN_PLL1_SS_CTRL2 }, - { 0x20, CMN_PLL1_DSM_DIAG }, - { 0, CMN_PLLSM1_USER_DEF_CTRL }, - { 0, CMN_DIAG_PLL1_OVRD }, - { 0, CMN_DIAG_PLL1_FBH_OVRD }, - { 0, CMN_DIAG_PLL1_FBL_OVRD }, - { 0x6, CMN_DIAG_PLL1_V2I_TUNE }, - { 0x45, CMN_DIAG_PLL1_CP_TUNE }, - { 0x8, CMN_DIAG_PLL1_LF_PROG }, - { 0x100, CMN_DIAG_PLL1_PTATIS_TUNE1 }, - { 0x7, CMN_DIAG_PLL1_PTATIS_TUNE2 }, - { 0x4, CMN_DIAG_PLL1_INCLK_CTRL }, -}; - -static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy) -{ - u32 i, rdata; - - /* - * cmn_ref_clk_sel = 3, select the 24Mhz for clk parent - * cmn_psm_clk_dig_div = 2, set the clk division to 2 - */ - writel(0x830, tcphy->base + PMA_CMN_CTRL1); - for (i = 0; i < 4; i++) { - /* - * The following PHY configuration assumes a 24 MHz reference - * clock. - */ - writel(0x90, tcphy->base + XCVR_DIAG_LANE_FCM_EN_MGN(i)); - writel(0x960, tcphy->base + TX_RCVDET_EN_TMR(i)); - writel(0x30, tcphy->base + TX_RCVDET_ST_TMR(i)); - } - - rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL); - rdata &= ~CLK_PLL_MASK; - rdata |= CLK_PLL_CONFIG; - writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL); -} - -static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy) -{ - u32 i; - - /* load the configuration of PLL0 */ - for (i = 0; i < ARRAY_SIZE(usb3_pll_cfg); i++) - writel(usb3_pll_cfg[i].value, - tcphy->base + usb3_pll_cfg[i].addr); -} - -static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy) -{ - u32 i; - - /* set the default mode to RBR */ - writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR, - tcphy->base + DP_CLK_CTL); - - /* load the configuration of PLL1 */ - for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++) - writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr); -} - -static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane) -{ - writel(0x7799, tcphy->base + TX_PSC_A0(lane)); - writel(0x7798, tcphy->base + TX_PSC_A1(lane)); - writel(0x5098, tcphy->base + TX_PSC_A2(lane)); - writel(0x5098, tcphy->base + TX_PSC_A3(lane)); - writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane)); - writel(0xbf, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane)); -} - -static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane) -{ - writel(0xa6fd, tcphy->base + RX_PSC_A0(lane)); - writel(0xa6fd, tcphy->base + RX_PSC_A1(lane)); - writel(0xa410, tcphy->base + RX_PSC_A2(lane)); - writel(0x2410, tcphy->base + RX_PSC_A3(lane)); - writel(0x23ff, tcphy->base + RX_PSC_CAL(lane)); - writel(0x13, tcphy->base + RX_SIGDET_HL_FILT_TMR(lane)); - writel(0x03e7, tcphy->base + RX_REE_CTRL_DATA_MASK(lane)); - writel(0x1004, tcphy->base + RX_DIAG_SIGDET_TUNE(lane)); - writel(0x2010, tcphy->base + RX_PSC_RDY(lane)); - writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane)); -} - -static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane) -{ - u16 rdata; - - writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane)); - writel(0x6799, tcphy->base + TX_PSC_A0(lane)); - writel(0x6798, tcphy->base + TX_PSC_A1(lane)); - writel(0x98, tcphy->base + TX_PSC_A2(lane)); - writel(0x98, tcphy->base + TX_PSC_A3(lane)); - - writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane)); - writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane)); - writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane)); - writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane)); - writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane)); - writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane)); - writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane)); - writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane)); - writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane)); - writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane)); - writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane)); - writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane)); - - writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane)); - writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane)); - - rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane)); - rdata = (rdata & 0x8fff) | 0x6000; - writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane)); -} - -static inline int property_enable(struct rockchip_typec_phy *tcphy, - const struct usb3phy_reg *reg, bool en) -{ - u32 mask = 1 << reg->write_enable; - u32 val = en << reg->enable_bit; - - return regmap_write(tcphy->grf_regs, reg->offset, val | mask); -} - -static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) -{ - u16 rdata, rdata2, val; - - /* disable txda_cal_latch_en for rewrite the calibration values */ - rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1); - val = rdata & 0xdfff; - writel(val, tcphy->base + TX_ANA_CTRL_REG_1); - - /* - * read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and - * write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it - * works. - */ - rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2); - rdata = rdata & 0xffc0; - - rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL); - rdata2 = rdata2 & 0x3f; - - val = rdata | rdata2; - writel(val, tcphy->base + TX_DIG_CTRL_REG_2); - usleep_range(1000, 1050); - - /* - * Enable signal for latch that sample and holds calibration values. - * Activate this signal for 1 clock cycle to sample new calibration - * values. - */ - rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1); - val = rdata | 0x2000; - writel(val, tcphy->base + TX_ANA_CTRL_REG_1); - usleep_range(150, 200); - - /* set TX Voltage Level and TX Deemphasis to 0 */ - writel(0, tcphy->base + PHY_DP_TX_CTL); - /* re-enable decap */ - writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2); - writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2); - writel(0x2008, tcphy->base + TX_ANA_CTRL_REG_1); - writel(0x2018, tcphy->base + TX_ANA_CTRL_REG_1); - - writel(0, tcphy->base + TX_ANA_CTRL_REG_5); - - /* - * Programs txda_drv_ldo_prog[15:0], Sets driver LDO - * voltage 16'h1001 for DP-AUX-TX and RX - */ - writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4); - - /* re-enables Bandgap reference for LDO */ - writel(0x2098, tcphy->base + TX_ANA_CTRL_REG_1); - writel(0x2198, tcphy->base + TX_ANA_CTRL_REG_1); - - /* - * re-enables the transmitter pre-driver, driver data selection MUX, - * and receiver detect circuits. - */ - writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2); - writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2); - - /* - * BIT 12: Controls auxda_polarity, which selects the polarity of the - * xcvr: - * 1, Reverses the polarity (If TYPEC, Pulls ups aux_p and pull - * down aux_m) - * 0, Normal polarity (if TYPE_C, pulls up aux_m and pulls down - * aux_p) - */ - val = 0xa078; - if (!tcphy->flip) - val |= BIT(12); - writel(val, tcphy->base + TX_ANA_CTRL_REG_1); - - writel(0, tcphy->base + TX_ANA_CTRL_REG_3); - writel(0, tcphy->base + TX_ANA_CTRL_REG_4); - writel(0, tcphy->base + TX_ANA_CTRL_REG_5); - - /* - * Controls low_power_swing_en, set the voltage swing of the driver - * to 400mv. The values below are peak to peak (differential) values. - */ - writel(4, tcphy->base + TXDA_COEFF_CALC_CTRL); - writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA); - - /* Controls tx_high_z_tm_en */ - val = readl(tcphy->base + TX_DIG_CTRL_REG_2); - val |= BIT(15); - writel(val, tcphy->base + TX_DIG_CTRL_REG_2); -} - -static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode) -{ - struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; - int ret, i; - u32 val; - - ret = clk_prepare_enable(tcphy->clk_core); - if (ret) { - dev_err(tcphy->dev, "Failed to prepare_enable core clock\n"); - return ret; - } - - ret = clk_prepare_enable(tcphy->clk_ref); - if (ret) { - dev_err(tcphy->dev, "Failed to prepare_enable ref clock\n"); - goto err_clk_core; - } - - reset_control_deassert(tcphy->tcphy_rst); - - property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip); - - tcphy_cfg_24m(tcphy); - - if (mode == MODE_DFP_DP) { - tcphy_cfg_dp_pll(tcphy); - for (i = 0; i < 4; i++) - tcphy_dp_cfg_lane(tcphy, i); - - writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG); - } else { - tcphy_cfg_usb3_pll(tcphy); - tcphy_cfg_dp_pll(tcphy); - if (tcphy->flip) { - tcphy_tx_usb3_cfg_lane(tcphy, 3); - tcphy_rx_usb3_cfg_lane(tcphy, 2); - tcphy_dp_cfg_lane(tcphy, 0); - tcphy_dp_cfg_lane(tcphy, 1); - } else { - tcphy_tx_usb3_cfg_lane(tcphy, 0); - tcphy_rx_usb3_cfg_lane(tcphy, 1); - tcphy_dp_cfg_lane(tcphy, 2); - tcphy_dp_cfg_lane(tcphy, 3); - } - - writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG); - } - - writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL); - - reset_control_deassert(tcphy->uphy_rst); - - ret = readx_poll_timeout(readl, tcphy->base + PMA_CMN_CTRL1, - val, val & CMN_READY, 10, - PHY_MODE_SET_TIMEOUT); - if (ret < 0) { - dev_err(tcphy->dev, "wait pma ready timeout\n"); - ret = -ETIMEDOUT; - goto err_wait_pma; - } - - reset_control_deassert(tcphy->pipe_rst); - - return 0; - -err_wait_pma: - reset_control_assert(tcphy->uphy_rst); - reset_control_assert(tcphy->tcphy_rst); - clk_disable_unprepare(tcphy->clk_ref); -err_clk_core: - clk_disable_unprepare(tcphy->clk_core); - return ret; -} - -static void tcphy_phy_deinit(struct rockchip_typec_phy *tcphy) -{ - reset_control_assert(tcphy->tcphy_rst); - reset_control_assert(tcphy->uphy_rst); - reset_control_assert(tcphy->pipe_rst); - clk_disable_unprepare(tcphy->clk_core); - clk_disable_unprepare(tcphy->clk_ref); -} - -static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) -{ - struct extcon_dev *edev = tcphy->extcon; - union extcon_property_value property; - unsigned int id; - bool dfp, ufp, dp; - u8 mode; - int ret; - - ufp = extcon_get_state(edev, EXTCON_USB); - dfp = extcon_get_state(edev, EXTCON_USB_HOST); - dp = extcon_get_state(edev, EXTCON_DISP_DP); - - mode = MODE_DFP_USB; - id = EXTCON_USB_HOST; - - if (ufp) { - mode = MODE_UFP_USB; - id = EXTCON_USB; - } else if (dp) { - mode = MODE_DFP_DP; - id = EXTCON_DISP_DP; - - ret = extcon_get_property(edev, id, EXTCON_PROP_USB_SS, - &property); - if (ret) { - dev_err(tcphy->dev, "get superspeed property failed\n"); - return ret; - } - - if (property.intval) - mode |= MODE_DFP_USB; - } - - ret = extcon_get_property(edev, id, EXTCON_PROP_USB_TYPEC_POLARITY, - &property); - if (ret) { - dev_err(tcphy->dev, "get polarity property failed\n"); - return ret; - } - - tcphy->flip = property.intval ? 1 : 0; - - return mode; -} - -static int rockchip_usb3_phy_power_on(struct phy *phy) -{ - struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); - struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; - const struct usb3phy_reg *reg = &cfg->pipe_status; - int timeout, new_mode, ret = 0; - u32 val; - - mutex_lock(&tcphy->lock); - - new_mode = tcphy_get_mode(tcphy); - if (new_mode < 0) { - ret = new_mode; - goto unlock_ret; - } - - /* DP-only mode; fall back to USB2 */ - if (!(new_mode & (MODE_DFP_USB | MODE_UFP_USB))) - goto unlock_ret; - - if (tcphy->mode == new_mode) - goto unlock_ret; - - if (tcphy->mode == MODE_DISCONNECT) - tcphy_phy_init(tcphy, new_mode); - - /* wait TCPHY for pipe ready */ - for (timeout = 0; timeout < 100; timeout++) { - regmap_read(tcphy->grf_regs, reg->offset, &val); - if (!(val & BIT(reg->enable_bit))) { - tcphy->mode |= new_mode & (MODE_DFP_USB | MODE_UFP_USB); - goto unlock_ret; - } - usleep_range(10, 20); - } - - if (tcphy->mode == MODE_DISCONNECT) - tcphy_phy_deinit(tcphy); - - ret = -ETIMEDOUT; - -unlock_ret: - mutex_unlock(&tcphy->lock); - return ret; -} - -static int rockchip_usb3_phy_power_off(struct phy *phy) -{ - struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); - - mutex_lock(&tcphy->lock); - - if (tcphy->mode == MODE_DISCONNECT) - goto unlock; - - tcphy->mode &= ~(MODE_UFP_USB | MODE_DFP_USB); - if (tcphy->mode == MODE_DISCONNECT) - tcphy_phy_deinit(tcphy); - -unlock: - mutex_unlock(&tcphy->lock); - return 0; -} - -static const struct phy_ops rockchip_usb3_phy_ops = { - .power_on = rockchip_usb3_phy_power_on, - .power_off = rockchip_usb3_phy_power_off, - .owner = THIS_MODULE, -}; - -static int rockchip_dp_phy_power_on(struct phy *phy) -{ - struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); - int new_mode, ret = 0; - u32 val; - - mutex_lock(&tcphy->lock); - - new_mode = tcphy_get_mode(tcphy); - if (new_mode < 0) { - ret = new_mode; - goto unlock_ret; - } - - if (!(new_mode & MODE_DFP_DP)) { - ret = -ENODEV; - goto unlock_ret; - } - - if (tcphy->mode == new_mode) - goto unlock_ret; - - /* - * If the PHY has been power on, but the mode is not DP only mode, - * re-init the PHY for setting all of 4 lanes to DP. - */ - if (new_mode == MODE_DFP_DP && tcphy->mode != MODE_DISCONNECT) { - tcphy_phy_deinit(tcphy); - tcphy_phy_init(tcphy, new_mode); - } else if (tcphy->mode == MODE_DISCONNECT) { - tcphy_phy_init(tcphy, new_mode); - } - - ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL, - val, val & DP_MODE_A2, 1000, - PHY_MODE_SET_TIMEOUT); - if (ret < 0) { - dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n"); - goto power_on_finish; - } - - tcphy_dp_aux_calibration(tcphy); - - writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL); - - ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL, - val, val & DP_MODE_A0, 1000, - PHY_MODE_SET_TIMEOUT); - if (ret < 0) { - writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL); - dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n"); - goto power_on_finish; - } - - tcphy->mode |= MODE_DFP_DP; - -power_on_finish: - if (tcphy->mode == MODE_DISCONNECT) - tcphy_phy_deinit(tcphy); -unlock_ret: - mutex_unlock(&tcphy->lock); - return ret; -} - -static int rockchip_dp_phy_power_off(struct phy *phy) -{ - struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); - - mutex_lock(&tcphy->lock); - - if (tcphy->mode == MODE_DISCONNECT) - goto unlock; - - tcphy->mode &= ~MODE_DFP_DP; - - writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL); - - if (tcphy->mode == MODE_DISCONNECT) - tcphy_phy_deinit(tcphy); - -unlock: - mutex_unlock(&tcphy->lock); - return 0; -} - -static const struct phy_ops rockchip_dp_phy_ops = { - .power_on = rockchip_dp_phy_power_on, - .power_off = rockchip_dp_phy_power_off, - .owner = THIS_MODULE, -}; - -static int tcphy_get_param(struct device *dev, - struct usb3phy_reg *reg, - const char *name) -{ - u32 buffer[3]; - int ret; - - ret = of_property_read_u32_array(dev->of_node, name, buffer, 3); - if (ret) { - dev_err(dev, "Can not parse %s\n", name); - return ret; - } - - reg->offset = buffer[0]; - reg->enable_bit = buffer[1]; - reg->write_enable = buffer[2]; - return 0; -} - -static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy, - struct device *dev) -{ - struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; - int ret; - - ret = tcphy_get_param(dev, &cfg->typec_conn_dir, - "rockchip,typec-conn-dir"); - if (ret) - return ret; - - ret = tcphy_get_param(dev, &cfg->usb3tousb2_en, - "rockchip,usb3tousb2-en"); - if (ret) - return ret; - - ret = tcphy_get_param(dev, &cfg->external_psm, - "rockchip,external-psm"); - if (ret) - return ret; - - ret = tcphy_get_param(dev, &cfg->pipe_status, - "rockchip,pipe-status"); - if (ret) - return ret; - - tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node, - "rockchip,grf"); - if (IS_ERR(tcphy->grf_regs)) { - dev_err(dev, "could not find grf dt node\n"); - return PTR_ERR(tcphy->grf_regs); - } - - tcphy->clk_core = devm_clk_get(dev, "tcpdcore"); - if (IS_ERR(tcphy->clk_core)) { - dev_err(dev, "could not get uphy core clock\n"); - return PTR_ERR(tcphy->clk_core); - } - - tcphy->clk_ref = devm_clk_get(dev, "tcpdphy-ref"); - if (IS_ERR(tcphy->clk_ref)) { - dev_err(dev, "could not get uphy ref clock\n"); - return PTR_ERR(tcphy->clk_ref); - } - - tcphy->uphy_rst = devm_reset_control_get(dev, "uphy"); - if (IS_ERR(tcphy->uphy_rst)) { - dev_err(dev, "no uphy_rst reset control found\n"); - return PTR_ERR(tcphy->uphy_rst); - } - - tcphy->pipe_rst = devm_reset_control_get(dev, "uphy-pipe"); - if (IS_ERR(tcphy->pipe_rst)) { - dev_err(dev, "no pipe_rst reset control found\n"); - return PTR_ERR(tcphy->pipe_rst); - } - - tcphy->tcphy_rst = devm_reset_control_get(dev, "uphy-tcphy"); - if (IS_ERR(tcphy->tcphy_rst)) { - dev_err(dev, "no tcphy_rst reset control found\n"); - return PTR_ERR(tcphy->tcphy_rst); - } - - return 0; -} - -static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy) -{ - struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; - - reset_control_assert(tcphy->tcphy_rst); - reset_control_assert(tcphy->uphy_rst); - reset_control_assert(tcphy->pipe_rst); - - /* select external psm clock */ - property_enable(tcphy, &cfg->external_psm, 1); - property_enable(tcphy, &cfg->usb3tousb2_en, 0); - - tcphy->mode = MODE_DISCONNECT; -} - -static int rockchip_typec_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct device_node *child_np; - struct rockchip_typec_phy *tcphy; - struct phy_provider *phy_provider; - struct resource *res; - int ret; - - tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL); - if (!tcphy) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - tcphy->base = devm_ioremap_resource(dev, res); - if (IS_ERR(tcphy->base)) - return PTR_ERR(tcphy->base); - - ret = tcphy_parse_dt(tcphy, dev); - if (ret) - return ret; - - tcphy->dev = dev; - platform_set_drvdata(pdev, tcphy); - mutex_init(&tcphy->lock); - - typec_phy_pre_init(tcphy); - - tcphy->extcon = extcon_get_edev_by_phandle(dev, 0); - if (IS_ERR(tcphy->extcon)) { - if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER) - dev_err(dev, "Invalid or missing extcon\n"); - return PTR_ERR(tcphy->extcon); - } - - pm_runtime_enable(dev); - - for_each_available_child_of_node(np, child_np) { - struct phy *phy; - - if (!of_node_cmp(child_np->name, "dp-port")) - phy = devm_phy_create(dev, child_np, - &rockchip_dp_phy_ops); - else if (!of_node_cmp(child_np->name, "usb3-port")) - phy = devm_phy_create(dev, child_np, - &rockchip_usb3_phy_ops); - else - continue; - - if (IS_ERR(phy)) { - dev_err(dev, "failed to create phy: %s\n", - child_np->name); - return PTR_ERR(phy); - } - - phy_set_drvdata(phy, tcphy); - } - - 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); - } - - return 0; -} - -static int rockchip_typec_phy_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static const struct of_device_id rockchip_typec_phy_dt_ids[] = { - { .compatible = "rockchip,rk3399-typec-phy" }, - {} -}; - -MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids); - -static struct platform_driver rockchip_typec_phy_driver = { - .probe = rockchip_typec_phy_probe, - .remove = rockchip_typec_phy_remove, - .driver = { - .name = "rockchip-typec-phy", - .of_match_table = rockchip_typec_phy_dt_ids, - }, -}; - -module_platform_driver(rockchip_typec_phy_driver); - -MODULE_AUTHOR("Chris Zhong "); -MODULE_AUTHOR("Kever Yang "); -MODULE_DESCRIPTION("Rockchip USB TYPE-C PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c deleted file mode 100644 index 3378eeb7a562..000000000000 --- a/drivers/phy/phy-rockchip-usb.c +++ /dev/null @@ -1,540 +0,0 @@ -/* - * Rockchip usb PHY driver - * - * Copyright (C) 2014 Yunzhi Li - * Copyright (C) 2014 ROCKCHIP, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int enable_usb_uart; - -#define HIWORD_UPDATE(val, mask) \ - ((val) | (mask) << 16) - -#define UOC_CON0_SIDDQ BIT(13) - -struct rockchip_usb_phys { - int reg; - const char *pll_name; -}; - -struct rockchip_usb_phy_base; -struct rockchip_usb_phy_pdata { - struct rockchip_usb_phys *phys; - int (*init_usb_uart)(struct regmap *grf); - int usb_uart_phy; -}; - -struct rockchip_usb_phy_base { - struct device *dev; - struct regmap *reg_base; - const struct rockchip_usb_phy_pdata *pdata; -}; - -struct rockchip_usb_phy { - struct rockchip_usb_phy_base *base; - struct device_node *np; - unsigned int reg_offset; - struct clk *clk; - struct clk *clk480m; - struct clk_hw clk480m_hw; - struct phy *phy; - bool uart_enabled; - struct reset_control *reset; - struct regulator *vbus; -}; - -static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy, - bool siddq) -{ - u32 val = HIWORD_UPDATE(siddq ? UOC_CON0_SIDDQ : 0, UOC_CON0_SIDDQ); - - return regmap_write(phy->base->reg_base, phy->reg_offset, val); -} - -static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - return 480000000; -} - -static void rockchip_usb_phy480m_disable(struct clk_hw *hw) -{ - struct rockchip_usb_phy *phy = container_of(hw, - struct rockchip_usb_phy, - clk480m_hw); - - if (phy->vbus) - regulator_disable(phy->vbus); - - /* Power down usb phy analog blocks by set siddq 1 */ - rockchip_usb_phy_power(phy, 1); -} - -static int rockchip_usb_phy480m_enable(struct clk_hw *hw) -{ - struct rockchip_usb_phy *phy = container_of(hw, - struct rockchip_usb_phy, - clk480m_hw); - - /* Power up usb phy analog blocks by set siddq 0 */ - return rockchip_usb_phy_power(phy, 0); -} - -static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw) -{ - struct rockchip_usb_phy *phy = container_of(hw, - struct rockchip_usb_phy, - clk480m_hw); - int ret; - u32 val; - - ret = regmap_read(phy->base->reg_base, phy->reg_offset, &val); - if (ret < 0) - return ret; - - return (val & UOC_CON0_SIDDQ) ? 0 : 1; -} - -static const struct clk_ops rockchip_usb_phy480m_ops = { - .enable = rockchip_usb_phy480m_enable, - .disable = rockchip_usb_phy480m_disable, - .is_enabled = rockchip_usb_phy480m_is_enabled, - .recalc_rate = rockchip_usb_phy480m_recalc_rate, -}; - -static int rockchip_usb_phy_power_off(struct phy *_phy) -{ - struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); - - if (phy->uart_enabled) - return -EBUSY; - - clk_disable_unprepare(phy->clk480m); - - return 0; -} - -static int rockchip_usb_phy_power_on(struct phy *_phy) -{ - struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); - - if (phy->uart_enabled) - return -EBUSY; - - if (phy->vbus) { - int ret; - - ret = regulator_enable(phy->vbus); - if (ret) - return ret; - } - - return clk_prepare_enable(phy->clk480m); -} - -static int rockchip_usb_phy_reset(struct phy *_phy) -{ - struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); - - if (phy->reset) { - reset_control_assert(phy->reset); - udelay(10); - reset_control_deassert(phy->reset); - } - - return 0; -} - -static const struct phy_ops ops = { - .power_on = rockchip_usb_phy_power_on, - .power_off = rockchip_usb_phy_power_off, - .reset = rockchip_usb_phy_reset, - .owner = THIS_MODULE, -}; - -static void rockchip_usb_phy_action(void *data) -{ - struct rockchip_usb_phy *rk_phy = data; - - if (!rk_phy->uart_enabled) { - of_clk_del_provider(rk_phy->np); - clk_unregister(rk_phy->clk480m); - } - - if (rk_phy->clk) - clk_put(rk_phy->clk); -} - -static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base, - struct device_node *child) -{ - struct rockchip_usb_phy *rk_phy; - unsigned int reg_offset; - const char *clk_name; - struct clk_init_data init; - int err, i; - - rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL); - if (!rk_phy) - return -ENOMEM; - - rk_phy->base = base; - rk_phy->np = child; - - if (of_property_read_u32(child, "reg", ®_offset)) { - dev_err(base->dev, "missing reg property in node %s\n", - child->name); - return -EINVAL; - } - - rk_phy->reset = of_reset_control_get(child, "phy-reset"); - if (IS_ERR(rk_phy->reset)) - rk_phy->reset = NULL; - - rk_phy->reg_offset = reg_offset; - - rk_phy->clk = of_clk_get_by_name(child, "phyclk"); - if (IS_ERR(rk_phy->clk)) - rk_phy->clk = NULL; - - i = 0; - init.name = NULL; - while (base->pdata->phys[i].reg) { - if (base->pdata->phys[i].reg == reg_offset) { - init.name = base->pdata->phys[i].pll_name; - break; - } - i++; - } - - if (!init.name) { - dev_err(base->dev, "phy data not found\n"); - return -EINVAL; - } - - if (enable_usb_uart && base->pdata->usb_uart_phy == i) { - dev_dbg(base->dev, "phy%d used as uart output\n", i); - rk_phy->uart_enabled = true; - } else { - if (rk_phy->clk) { - clk_name = __clk_get_name(rk_phy->clk); - init.flags = 0; - init.parent_names = &clk_name; - init.num_parents = 1; - } else { - init.flags = 0; - init.parent_names = NULL; - init.num_parents = 0; - } - - init.ops = &rockchip_usb_phy480m_ops; - rk_phy->clk480m_hw.init = &init; - - rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw); - if (IS_ERR(rk_phy->clk480m)) { - err = PTR_ERR(rk_phy->clk480m); - goto err_clk; - } - - err = of_clk_add_provider(child, of_clk_src_simple_get, - rk_phy->clk480m); - if (err < 0) - goto err_clk_prov; - } - - err = devm_add_action_or_reset(base->dev, rockchip_usb_phy_action, - rk_phy); - if (err) - return err; - - rk_phy->phy = devm_phy_create(base->dev, child, &ops); - if (IS_ERR(rk_phy->phy)) { - dev_err(base->dev, "failed to create PHY\n"); - return PTR_ERR(rk_phy->phy); - } - phy_set_drvdata(rk_phy->phy, rk_phy); - - rk_phy->vbus = devm_regulator_get_optional(&rk_phy->phy->dev, "vbus"); - if (IS_ERR(rk_phy->vbus)) { - if (PTR_ERR(rk_phy->vbus) == -EPROBE_DEFER) - return PTR_ERR(rk_phy->vbus); - rk_phy->vbus = NULL; - } - - /* - * When acting as uart-pipe, just keep clock on otherwise - * only power up usb phy when it use, so disable it when init - */ - if (rk_phy->uart_enabled) - return clk_prepare_enable(rk_phy->clk); - else - return rockchip_usb_phy_power(rk_phy, 1); - -err_clk_prov: - if (!rk_phy->uart_enabled) - clk_unregister(rk_phy->clk480m); -err_clk: - if (rk_phy->clk) - clk_put(rk_phy->clk); - return err; -} - -static const struct rockchip_usb_phy_pdata rk3066a_pdata = { - .phys = (struct rockchip_usb_phys[]){ - { .reg = 0x17c, .pll_name = "sclk_otgphy0_480m" }, - { .reg = 0x188, .pll_name = "sclk_otgphy1_480m" }, - { /* sentinel */ } - }, -}; - -static const struct rockchip_usb_phy_pdata rk3188_pdata = { - .phys = (struct rockchip_usb_phys[]){ - { .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" }, - { .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" }, - { /* sentinel */ } - }, -}; - -#define RK3288_UOC0_CON0 0x320 -#define RK3288_UOC0_CON0_COMMON_ON_N BIT(0) -#define RK3288_UOC0_CON0_DISABLE BIT(4) - -#define RK3288_UOC0_CON2 0x328 -#define RK3288_UOC0_CON2_SOFT_CON_SEL BIT(2) - -#define RK3288_UOC0_CON3 0x32c -#define RK3288_UOC0_CON3_UTMI_SUSPENDN BIT(0) -#define RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING (1 << 1) -#define RK3288_UOC0_CON3_UTMI_OPMODE_MASK (3 << 1) -#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC (1 << 3) -#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK (3 << 3) -#define RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED BIT(5) -#define RK3288_UOC0_CON3_BYPASSDMEN BIT(6) -#define RK3288_UOC0_CON3_BYPASSSEL BIT(7) - -/* - * Enable the bypass of uart2 data through the otg usb phy. - * Original description in the TRM. - * 1. Disable the OTG block by setting OTGDISABLE0 to 1’b1. - * 2. Disable the pull-up resistance on the D+ line by setting - * OPMODE0[1:0] to 2’b01. - * 3. To ensure that the XO, Bias, and PLL blocks are powered down in Suspend - * mode, set COMMONONN to 1’b1. - * 4. Place the USB PHY in Suspend mode by setting SUSPENDM0 to 1’b0. - * 5. Set BYPASSSEL0 to 1’b1. - * 6. To transmit data, controls BYPASSDMEN0, and BYPASSDMDATA0. - * To receive data, monitor FSVPLUS0. - * - * The actual code in the vendor kernel does some things differently. - */ -static int __init rk3288_init_usb_uart(struct regmap *grf) -{ - u32 val; - int ret; - - /* - * COMMON_ON and DISABLE settings are described in the TRM, - * but were not present in the original code. - * Also disable the analog phy components to save power. - */ - val = HIWORD_UPDATE(RK3288_UOC0_CON0_COMMON_ON_N - | RK3288_UOC0_CON0_DISABLE - | UOC_CON0_SIDDQ, - RK3288_UOC0_CON0_COMMON_ON_N - | RK3288_UOC0_CON0_DISABLE - | UOC_CON0_SIDDQ); - ret = regmap_write(grf, RK3288_UOC0_CON0, val); - if (ret) - return ret; - - val = HIWORD_UPDATE(RK3288_UOC0_CON2_SOFT_CON_SEL, - RK3288_UOC0_CON2_SOFT_CON_SEL); - ret = regmap_write(grf, RK3288_UOC0_CON2, val); - if (ret) - return ret; - - val = HIWORD_UPDATE(RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING - | RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC - | RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED, - RK3288_UOC0_CON3_UTMI_SUSPENDN - | RK3288_UOC0_CON3_UTMI_OPMODE_MASK - | RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK - | RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED); - ret = regmap_write(grf, RK3288_UOC0_CON3, val); - if (ret) - return ret; - - val = HIWORD_UPDATE(RK3288_UOC0_CON3_BYPASSSEL - | RK3288_UOC0_CON3_BYPASSDMEN, - RK3288_UOC0_CON3_BYPASSSEL - | RK3288_UOC0_CON3_BYPASSDMEN); - ret = regmap_write(grf, RK3288_UOC0_CON3, val); - if (ret) - return ret; - - return 0; -} - -static const struct rockchip_usb_phy_pdata rk3288_pdata = { - .phys = (struct rockchip_usb_phys[]){ - { .reg = 0x320, .pll_name = "sclk_otgphy0_480m" }, - { .reg = 0x334, .pll_name = "sclk_otgphy1_480m" }, - { .reg = 0x348, .pll_name = "sclk_otgphy2_480m" }, - { /* sentinel */ } - }, - .init_usb_uart = rk3288_init_usb_uart, - .usb_uart_phy = 0, -}; - -static int rockchip_usb_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct rockchip_usb_phy_base *phy_base; - struct phy_provider *phy_provider; - const struct of_device_id *match; - struct device_node *child; - int err; - - phy_base = devm_kzalloc(dev, sizeof(*phy_base), GFP_KERNEL); - if (!phy_base) - return -ENOMEM; - - match = of_match_device(dev->driver->of_match_table, dev); - if (!match || !match->data) { - dev_err(dev, "missing phy data\n"); - return -EINVAL; - } - - phy_base->pdata = match->data; - - phy_base->dev = dev; - phy_base->reg_base = ERR_PTR(-ENODEV); - if (dev->parent && dev->parent->of_node) - phy_base->reg_base = syscon_node_to_regmap( - dev->parent->of_node); - if (IS_ERR(phy_base->reg_base)) - phy_base->reg_base = syscon_regmap_lookup_by_phandle( - dev->of_node, "rockchip,grf"); - if (IS_ERR(phy_base->reg_base)) { - dev_err(&pdev->dev, "Missing rockchip,grf property\n"); - return PTR_ERR(phy_base->reg_base); - } - - for_each_available_child_of_node(dev->of_node, child) { - err = rockchip_usb_phy_init(phy_base, child); - if (err) { - of_node_put(child); - return err; - } - } - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id rockchip_usb_phy_dt_ids[] = { - { .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata }, - { .compatible = "rockchip,rk3188-usb-phy", .data = &rk3188_pdata }, - { .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata }, - {} -}; - -MODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids); - -static struct platform_driver rockchip_usb_driver = { - .probe = rockchip_usb_phy_probe, - .driver = { - .name = "rockchip-usb-phy", - .of_match_table = rockchip_usb_phy_dt_ids, - }, -}; - -module_platform_driver(rockchip_usb_driver); - -#ifndef MODULE -static int __init rockchip_init_usb_uart(void) -{ - const struct of_device_id *match; - const struct rockchip_usb_phy_pdata *data; - struct device_node *np; - struct regmap *grf; - int ret; - - if (!enable_usb_uart) - return 0; - - np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids, - &match); - if (!np) { - pr_err("%s: failed to find usbphy node\n", __func__); - return -ENOTSUPP; - } - - pr_debug("%s: using settings for %s\n", __func__, match->compatible); - data = match->data; - - if (!data->init_usb_uart) { - pr_err("%s: usb-uart not available on %s\n", - __func__, match->compatible); - return -ENOTSUPP; - } - - grf = ERR_PTR(-ENODEV); - if (np->parent) - grf = syscon_node_to_regmap(np->parent); - if (IS_ERR(grf)) - grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); - if (IS_ERR(grf)) { - pr_err("%s: Missing rockchip,grf property, %lu\n", - __func__, PTR_ERR(grf)); - return PTR_ERR(grf); - } - - ret = data->init_usb_uart(grf); - if (ret) { - pr_err("%s: could not init usb_uart, %d\n", __func__, ret); - enable_usb_uart = 0; - return ret; - } - - return 0; -} -early_initcall(rockchip_init_usb_uart); - -static int __init rockchip_usb_uart(char *buf) -{ - enable_usb_uart = true; - return 0; -} -early_param("rockchip.usb_uart", rockchip_usb_uart); -#endif - -MODULE_AUTHOR("Yunzhi Li "); -MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-s5pv210-usb2.c b/drivers/phy/phy-s5pv210-usb2.c deleted file mode 100644 index f6f72339bbc3..000000000000 --- a/drivers/phy/phy-s5pv210-usb2.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support - * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * Authors: Kamil Debski - * - * 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 "phy-samsung-usb2.h" - -/* Exynos USB PHY registers */ - -/* PHY power control */ -#define S5PV210_UPHYPWR 0x0 - -#define S5PV210_UPHYPWR_PHY0_SUSPEND BIT(0) -#define S5PV210_UPHYPWR_PHY0_PWR BIT(3) -#define S5PV210_UPHYPWR_PHY0_OTG_PWR BIT(4) -#define S5PV210_UPHYPWR_PHY0 ( \ - S5PV210_UPHYPWR_PHY0_SUSPEND | \ - S5PV210_UPHYPWR_PHY0_PWR | \ - S5PV210_UPHYPWR_PHY0_OTG_PWR) - -#define S5PV210_UPHYPWR_PHY1_SUSPEND BIT(6) -#define S5PV210_UPHYPWR_PHY1_PWR BIT(7) -#define S5PV210_UPHYPWR_PHY1 ( \ - S5PV210_UPHYPWR_PHY1_SUSPEND | \ - S5PV210_UPHYPWR_PHY1_PWR) - -/* PHY clock control */ -#define S5PV210_UPHYCLK 0x4 - -#define S5PV210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) -#define S5PV210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) -#define S5PV210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) -#define S5PV210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) - -#define S5PV210_UPHYCLK_PHY0_ID_PULLUP BIT(2) -#define S5PV210_UPHYCLK_PHY0_COMMON_ON BIT(4) -#define S5PV210_UPHYCLK_PHY1_COMMON_ON BIT(7) - -/* PHY reset control */ -#define S5PV210_UPHYRST 0x8 - -#define S5PV210_URSTCON_PHY0 BIT(0) -#define S5PV210_URSTCON_OTG_HLINK BIT(1) -#define S5PV210_URSTCON_OTG_PHYLINK BIT(2) -#define S5PV210_URSTCON_PHY1_ALL BIT(3) -#define S5PV210_URSTCON_HOST_LINK_ALL BIT(4) - -/* Isolation, configured in the power management unit */ -#define S5PV210_USB_ISOL_OFFSET 0x680c -#define S5PV210_USB_ISOL_DEVICE BIT(0) -#define S5PV210_USB_ISOL_HOST BIT(1) - - -enum s5pv210_phy_id { - S5PV210_DEVICE, - S5PV210_HOST, - S5PV210_NUM_PHYS, -}; - -/* - * s5pv210_rate_to_clk() converts the supplied clock rate to the value that - * can be written to the phy register. - */ -static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg) -{ - switch (rate) { - case 12 * MHZ: - *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ; - break; - case 24 * MHZ: - *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ; - break; - case 48 * MHZ: - *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ; - break; - default: - return -EINVAL; - } - - return 0; -} - -static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on) -{ - struct samsung_usb2_phy_driver *drv = inst->drv; - u32 mask; - - switch (inst->cfg->id) { - case S5PV210_DEVICE: - mask = S5PV210_USB_ISOL_DEVICE; - break; - case S5PV210_HOST: - mask = S5PV210_USB_ISOL_HOST; - break; - default: - return; - } - - regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET, - mask, on ? 0 : mask); -} - -static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) -{ - struct samsung_usb2_phy_driver *drv = inst->drv; - u32 rstbits = 0; - u32 phypwr = 0; - u32 rst; - u32 pwr; - - switch (inst->cfg->id) { - case S5PV210_DEVICE: - phypwr = S5PV210_UPHYPWR_PHY0; - rstbits = S5PV210_URSTCON_PHY0; - break; - case S5PV210_HOST: - phypwr = S5PV210_UPHYPWR_PHY1; - rstbits = S5PV210_URSTCON_PHY1_ALL | - S5PV210_URSTCON_HOST_LINK_ALL; - break; - } - - if (on) { - writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK); - - pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); - pwr &= ~phypwr; - writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); - - rst = readl(drv->reg_phy + S5PV210_UPHYRST); - rst |= rstbits; - writel(rst, drv->reg_phy + S5PV210_UPHYRST); - udelay(10); - rst &= ~rstbits; - writel(rst, drv->reg_phy + S5PV210_UPHYRST); - } else { - pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); - pwr |= phypwr; - writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); - } -} - -static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst) -{ - s5pv210_isol(inst, 0); - s5pv210_phy_pwr(inst, 1); - - return 0; -} - -static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst) -{ - s5pv210_phy_pwr(inst, 0); - s5pv210_isol(inst, 1); - - return 0; -} - -static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = { - [S5PV210_DEVICE] = { - .label = "device", - .id = S5PV210_DEVICE, - .power_on = s5pv210_power_on, - .power_off = s5pv210_power_off, - }, - [S5PV210_HOST] = { - .label = "host", - .id = S5PV210_HOST, - .power_on = s5pv210_power_on, - .power_off = s5pv210_power_off, - }, -}; - -const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = { - .num_phys = ARRAY_SIZE(s5pv210_phys), - .phys = s5pv210_phys, - .rate_to_clk = s5pv210_rate_to_clk, -}; diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c deleted file mode 100644 index 1d22d93b552d..000000000000 --- a/drivers/phy/phy-samsung-usb2.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Samsung SoC USB 1.1/2.0 PHY driver - * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * Author: Kamil Debski - * - * 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 "phy-samsung-usb2.h" - -static int samsung_usb2_phy_power_on(struct phy *phy) -{ - struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy); - struct samsung_usb2_phy_driver *drv = inst->drv; - int ret; - - dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n", - inst->cfg->label); - - if (drv->vbus) { - ret = regulator_enable(drv->vbus); - if (ret) - goto err_regulator; - } - - ret = clk_prepare_enable(drv->clk); - if (ret) - goto err_main_clk; - ret = clk_prepare_enable(drv->ref_clk); - if (ret) - goto err_instance_clk; - if (inst->cfg->power_on) { - spin_lock(&drv->lock); - ret = inst->cfg->power_on(inst); - spin_unlock(&drv->lock); - if (ret) - goto err_power_on; - } - - return 0; - -err_power_on: - clk_disable_unprepare(drv->ref_clk); -err_instance_clk: - clk_disable_unprepare(drv->clk); -err_main_clk: - if (drv->vbus) - regulator_disable(drv->vbus); -err_regulator: - return ret; -} - -static int samsung_usb2_phy_power_off(struct phy *phy) -{ - struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy); - struct samsung_usb2_phy_driver *drv = inst->drv; - int ret = 0; - - dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n", - inst->cfg->label); - if (inst->cfg->power_off) { - spin_lock(&drv->lock); - ret = inst->cfg->power_off(inst); - spin_unlock(&drv->lock); - if (ret) - return ret; - } - clk_disable_unprepare(drv->ref_clk); - clk_disable_unprepare(drv->clk); - if (drv->vbus) - ret = regulator_disable(drv->vbus); - - return ret; -} - -static const struct phy_ops samsung_usb2_phy_ops = { - .power_on = samsung_usb2_phy_power_on, - .power_off = samsung_usb2_phy_power_off, - .owner = THIS_MODULE, -}; - -static struct phy *samsung_usb2_phy_xlate(struct device *dev, - struct of_phandle_args *args) -{ - struct samsung_usb2_phy_driver *drv; - - drv = dev_get_drvdata(dev); - if (!drv) - return ERR_PTR(-EINVAL); - - if (WARN_ON(args->args[0] >= drv->cfg->num_phys)) - return ERR_PTR(-ENODEV); - - return drv->instances[args->args[0]].phy; -} - -static const struct of_device_id samsung_usb2_phy_of_match[] = { -#ifdef CONFIG_PHY_EXYNOS4X12_USB2 - { - .compatible = "samsung,exynos3250-usb2-phy", - .data = &exynos3250_usb2_phy_config, - }, -#endif -#ifdef CONFIG_PHY_EXYNOS4210_USB2 - { - .compatible = "samsung,exynos4210-usb2-phy", - .data = &exynos4210_usb2_phy_config, - }, -#endif -#ifdef CONFIG_PHY_EXYNOS4X12_USB2 - { - .compatible = "samsung,exynos4x12-usb2-phy", - .data = &exynos4x12_usb2_phy_config, - }, -#endif -#ifdef CONFIG_PHY_EXYNOS5250_USB2 - { - .compatible = "samsung,exynos5250-usb2-phy", - .data = &exynos5250_usb2_phy_config, - }, -#endif -#ifdef CONFIG_PHY_S5PV210_USB2 - { - .compatible = "samsung,s5pv210-usb2-phy", - .data = &s5pv210_usb2_phy_config, - }, -#endif - { }, -}; -MODULE_DEVICE_TABLE(of, samsung_usb2_phy_of_match); - -static int samsung_usb2_phy_probe(struct platform_device *pdev) -{ - const struct of_device_id *match; - const struct samsung_usb2_phy_config *cfg; - struct device *dev = &pdev->dev; - struct phy_provider *phy_provider; - struct resource *mem; - struct samsung_usb2_phy_driver *drv; - int i, ret; - - if (!pdev->dev.of_node) { - dev_err(dev, "This driver is required to be instantiated from device tree\n"); - return -EINVAL; - } - - match = of_match_node(samsung_usb2_phy_of_match, pdev->dev.of_node); - if (!match) { - dev_err(dev, "of_match_node() failed\n"); - return -EINVAL; - } - cfg = match->data; - - drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) + - cfg->num_phys * sizeof(struct samsung_usb2_phy_instance), - GFP_KERNEL); - if (!drv) - return -ENOMEM; - - dev_set_drvdata(dev, drv); - spin_lock_init(&drv->lock); - - drv->cfg = cfg; - drv->dev = dev; - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - drv->reg_phy = devm_ioremap_resource(dev, mem); - if (IS_ERR(drv->reg_phy)) { - dev_err(dev, "Failed to map register memory (phy)\n"); - return PTR_ERR(drv->reg_phy); - } - - drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "samsung,pmureg-phandle"); - if (IS_ERR(drv->reg_pmu)) { - dev_err(dev, "Failed to map PMU registers (via syscon)\n"); - return PTR_ERR(drv->reg_pmu); - } - - if (drv->cfg->has_mode_switch) { - drv->reg_sys = syscon_regmap_lookup_by_phandle( - pdev->dev.of_node, "samsung,sysreg-phandle"); - if (IS_ERR(drv->reg_sys)) { - dev_err(dev, "Failed to map system registers (via syscon)\n"); - return PTR_ERR(drv->reg_sys); - } - } - - drv->clk = devm_clk_get(dev, "phy"); - if (IS_ERR(drv->clk)) { - dev_err(dev, "Failed to get clock of phy controller\n"); - return PTR_ERR(drv->clk); - } - - drv->ref_clk = devm_clk_get(dev, "ref"); - if (IS_ERR(drv->ref_clk)) { - dev_err(dev, "Failed to get reference clock for the phy controller\n"); - return PTR_ERR(drv->ref_clk); - } - - drv->ref_rate = clk_get_rate(drv->ref_clk); - if (drv->cfg->rate_to_clk) { - ret = drv->cfg->rate_to_clk(drv->ref_rate, &drv->ref_reg_val); - if (ret) - return ret; - } - - drv->vbus = devm_regulator_get(dev, "vbus"); - if (IS_ERR(drv->vbus)) { - ret = PTR_ERR(drv->vbus); - if (ret == -EPROBE_DEFER) - return ret; - drv->vbus = NULL; - } - - for (i = 0; i < drv->cfg->num_phys; i++) { - char *label = drv->cfg->phys[i].label; - struct samsung_usb2_phy_instance *p = &drv->instances[i]; - - dev_dbg(dev, "Creating phy \"%s\"\n", label); - p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops); - if (IS_ERR(p->phy)) { - dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n", - label); - return PTR_ERR(p->phy); - } - - p->cfg = &drv->cfg->phys[i]; - p->drv = drv; - phy_set_bus_width(p->phy, 8); - phy_set_drvdata(p->phy, p); - } - - phy_provider = devm_of_phy_provider_register(dev, - samsung_usb2_phy_xlate); - if (IS_ERR(phy_provider)) { - dev_err(drv->dev, "Failed to register phy provider\n"); - return PTR_ERR(phy_provider); - } - - return 0; -} - -static struct platform_driver samsung_usb2_phy_driver = { - .probe = samsung_usb2_phy_probe, - .driver = { - .of_match_table = samsung_usb2_phy_of_match, - .name = "samsung-usb2-phy", - } -}; - -module_platform_driver(samsung_usb2_phy_driver); -MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver"); -MODULE_AUTHOR("Kamil Debski "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:samsung-usb2-phy"); diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h deleted file mode 100644 index 6563e7ca0ac4..000000000000 --- a/drivers/phy/phy-samsung-usb2.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Samsung SoC USB 1.1/2.0 PHY driver - * - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * Author: Kamil Debski - * - * 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. - */ - -#ifndef _PHY_EXYNOS_USB2_H -#define _PHY_EXYNOS_USB2_H - -#include -#include -#include -#include -#include -#include - -#define KHZ 1000 -#define MHZ (KHZ * KHZ) - -struct samsung_usb2_phy_driver; -struct samsung_usb2_phy_instance; -struct samsung_usb2_phy_config; - -struct samsung_usb2_phy_instance { - const struct samsung_usb2_common_phy *cfg; - struct phy *phy; - struct samsung_usb2_phy_driver *drv; - int int_cnt; - int ext_cnt; -}; - -struct samsung_usb2_phy_driver { - const struct samsung_usb2_phy_config *cfg; - struct clk *clk; - struct clk *ref_clk; - struct regulator *vbus; - unsigned long ref_rate; - u32 ref_reg_val; - struct device *dev; - void __iomem *reg_phy; - struct regmap *reg_pmu; - struct regmap *reg_sys; - spinlock_t lock; - struct samsung_usb2_phy_instance instances[0]; -}; - -struct samsung_usb2_common_phy { - int (*power_on)(struct samsung_usb2_phy_instance *); - int (*power_off)(struct samsung_usb2_phy_instance *); - unsigned int id; - char *label; -}; - - -struct samsung_usb2_phy_config { - const struct samsung_usb2_common_phy *phys; - int (*rate_to_clk)(unsigned long, u32 *); - unsigned int num_phys; - bool has_mode_switch; - bool has_refclk_sel; -}; - -extern const struct samsung_usb2_phy_config exynos3250_usb2_phy_config; -extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config; -extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config; -extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config; -extern const struct samsung_usb2_phy_config s5pv210_usb2_phy_config; -#endif diff --git a/drivers/phy/phy-spear1310-miphy.c b/drivers/phy/phy-spear1310-miphy.c deleted file mode 100644 index ed67e98e54ca..000000000000 --- a/drivers/phy/phy-spear1310-miphy.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * ST SPEAr1310-miphy driver - * - * Copyright (C) 2014 ST Microelectronics - * Pratyush Anand - * Mohit Kumar - * - * 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 - -/* SPEAr1310 Registers */ -#define SPEAR1310_PCIE_SATA_CFG 0x3A4 - #define SPEAR1310_PCIE_SATA2_SEL_PCIE (0 << 31) - #define SPEAR1310_PCIE_SATA1_SEL_PCIE (0 << 30) - #define SPEAR1310_PCIE_SATA0_SEL_PCIE (0 << 29) - #define SPEAR1310_PCIE_SATA2_SEL_SATA BIT(31) - #define SPEAR1310_PCIE_SATA1_SEL_SATA BIT(30) - #define SPEAR1310_PCIE_SATA0_SEL_SATA BIT(29) - #define SPEAR1310_SATA2_CFG_TX_CLK_EN BIT(27) - #define SPEAR1310_SATA2_CFG_RX_CLK_EN BIT(26) - #define SPEAR1310_SATA2_CFG_POWERUP_RESET BIT(25) - #define SPEAR1310_SATA2_CFG_PM_CLK_EN BIT(24) - #define SPEAR1310_SATA1_CFG_TX_CLK_EN BIT(23) - #define SPEAR1310_SATA1_CFG_RX_CLK_EN BIT(22) - #define SPEAR1310_SATA1_CFG_POWERUP_RESET BIT(21) - #define SPEAR1310_SATA1_CFG_PM_CLK_EN BIT(20) - #define SPEAR1310_SATA0_CFG_TX_CLK_EN BIT(19) - #define SPEAR1310_SATA0_CFG_RX_CLK_EN BIT(18) - #define SPEAR1310_SATA0_CFG_POWERUP_RESET BIT(17) - #define SPEAR1310_SATA0_CFG_PM_CLK_EN BIT(16) - #define SPEAR1310_PCIE2_CFG_DEVICE_PRESENT BIT(11) - #define SPEAR1310_PCIE2_CFG_POWERUP_RESET BIT(10) - #define SPEAR1310_PCIE2_CFG_CORE_CLK_EN BIT(9) - #define SPEAR1310_PCIE2_CFG_AUX_CLK_EN BIT(8) - #define SPEAR1310_PCIE1_CFG_DEVICE_PRESENT BIT(7) - #define SPEAR1310_PCIE1_CFG_POWERUP_RESET BIT(6) - #define SPEAR1310_PCIE1_CFG_CORE_CLK_EN BIT(5) - #define SPEAR1310_PCIE1_CFG_AUX_CLK_EN BIT(4) - #define SPEAR1310_PCIE0_CFG_DEVICE_PRESENT BIT(3) - #define SPEAR1310_PCIE0_CFG_POWERUP_RESET BIT(2) - #define SPEAR1310_PCIE0_CFG_CORE_CLK_EN BIT(1) - #define SPEAR1310_PCIE0_CFG_AUX_CLK_EN BIT(0) - - #define SPEAR1310_PCIE_CFG_MASK(x) ((0xF << (x * 4)) | BIT((x + 29))) - #define SPEAR1310_SATA_CFG_MASK(x) ((0xF << (x * 4 + 16)) | \ - BIT((x + 29))) - #define SPEAR1310_PCIE_CFG_VAL(x) \ - (SPEAR1310_PCIE_SATA##x##_SEL_PCIE | \ - SPEAR1310_PCIE##x##_CFG_AUX_CLK_EN | \ - SPEAR1310_PCIE##x##_CFG_CORE_CLK_EN | \ - SPEAR1310_PCIE##x##_CFG_POWERUP_RESET | \ - SPEAR1310_PCIE##x##_CFG_DEVICE_PRESENT) - #define SPEAR1310_SATA_CFG_VAL(x) \ - (SPEAR1310_PCIE_SATA##x##_SEL_SATA | \ - SPEAR1310_SATA##x##_CFG_PM_CLK_EN | \ - SPEAR1310_SATA##x##_CFG_POWERUP_RESET | \ - SPEAR1310_SATA##x##_CFG_RX_CLK_EN | \ - SPEAR1310_SATA##x##_CFG_TX_CLK_EN) - -#define SPEAR1310_PCIE_MIPHY_CFG_1 0x3A8 - #define SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT BIT(31) - #define SPEAR1310_MIPHY_DUAL_CLK_REF_DIV2 BIT(28) - #define SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(x) (x << 16) - #define SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT BIT(15) - #define SPEAR1310_MIPHY_SINGLE_CLK_REF_DIV2 BIT(12) - #define SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(x) (x << 0) - #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA_MASK (0xFFFF) - #define SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK (0xFFFF << 16) - #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA \ - (SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT | \ - SPEAR1310_MIPHY_DUAL_CLK_REF_DIV2 | \ - SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(60) | \ - SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT | \ - SPEAR1310_MIPHY_SINGLE_CLK_REF_DIV2 | \ - SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(60)) - #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \ - (SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(120)) - #define SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE \ - (SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT | \ - SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(25) | \ - SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT | \ - SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(25)) - -#define SPEAR1310_PCIE_MIPHY_CFG_2 0x3AC - -enum spear1310_miphy_mode { - SATA, - PCIE, -}; - -struct spear1310_miphy_priv { - /* instance id of this phy */ - u32 id; - /* phy mode: 0 for SATA 1 for PCIe */ - enum spear1310_miphy_mode mode; - /* regmap for any soc specific misc registers */ - struct regmap *misc; - /* phy struct pointer */ - struct phy *phy; -}; - -static int spear1310_miphy_pcie_init(struct spear1310_miphy_priv *priv) -{ - u32 val; - - regmap_update_bits(priv->misc, SPEAR1310_PCIE_MIPHY_CFG_1, - SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK, - SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE); - - switch (priv->id) { - case 0: - val = SPEAR1310_PCIE_CFG_VAL(0); - break; - case 1: - val = SPEAR1310_PCIE_CFG_VAL(1); - break; - case 2: - val = SPEAR1310_PCIE_CFG_VAL(2); - break; - default: - return -EINVAL; - } - - regmap_update_bits(priv->misc, SPEAR1310_PCIE_SATA_CFG, - SPEAR1310_PCIE_CFG_MASK(priv->id), val); - - return 0; -} - -static int spear1310_miphy_pcie_exit(struct spear1310_miphy_priv *priv) -{ - regmap_update_bits(priv->misc, SPEAR1310_PCIE_SATA_CFG, - SPEAR1310_PCIE_CFG_MASK(priv->id), 0); - - regmap_update_bits(priv->misc, SPEAR1310_PCIE_MIPHY_CFG_1, - SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK, 0); - - return 0; -} - -static int spear1310_miphy_init(struct phy *phy) -{ - struct spear1310_miphy_priv *priv = phy_get_drvdata(phy); - int ret = 0; - - if (priv->mode == PCIE) - ret = spear1310_miphy_pcie_init(priv); - - return ret; -} - -static int spear1310_miphy_exit(struct phy *phy) -{ - struct spear1310_miphy_priv *priv = phy_get_drvdata(phy); - int ret = 0; - - if (priv->mode == PCIE) - ret = spear1310_miphy_pcie_exit(priv); - - return ret; -} - -static const struct of_device_id spear1310_miphy_of_match[] = { - { .compatible = "st,spear1310-miphy" }, - { }, -}; -MODULE_DEVICE_TABLE(of, spear1310_miphy_of_match); - -static const struct phy_ops spear1310_miphy_ops = { - .init = spear1310_miphy_init, - .exit = spear1310_miphy_exit, - .owner = THIS_MODULE, -}; - -static struct phy *spear1310_miphy_xlate(struct device *dev, - struct of_phandle_args *args) -{ - struct spear1310_miphy_priv *priv = dev_get_drvdata(dev); - - if (args->args_count < 1) { - dev_err(dev, "DT did not pass correct no of args\n"); - return ERR_PTR(-ENODEV); - } - - priv->mode = args->args[0]; - - if (priv->mode != SATA && priv->mode != PCIE) { - dev_err(dev, "DT did not pass correct phy mode\n"); - return ERR_PTR(-ENODEV); - } - - return priv->phy; -} - -static int spear1310_miphy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct spear1310_miphy_priv *priv; - struct phy_provider *phy_provider; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->misc = - syscon_regmap_lookup_by_phandle(dev->of_node, "misc"); - if (IS_ERR(priv->misc)) { - dev_err(dev, "failed to find misc regmap\n"); - return PTR_ERR(priv->misc); - } - - if (of_property_read_u32(dev->of_node, "phy-id", &priv->id)) { - dev_err(dev, "failed to find phy id\n"); - return -EINVAL; - } - - priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops); - if (IS_ERR(priv->phy)) { - dev_err(dev, "failed to create SATA PCIe PHY\n"); - return PTR_ERR(priv->phy); - } - - dev_set_drvdata(dev, priv); - phy_set_drvdata(priv->phy, priv); - - phy_provider = - devm_of_phy_provider_register(dev, spear1310_miphy_xlate); - if (IS_ERR(phy_provider)) { - dev_err(dev, "failed to register phy provider\n"); - return PTR_ERR(phy_provider); - } - - return 0; -} - -static struct platform_driver spear1310_miphy_driver = { - .probe = spear1310_miphy_probe, - .driver = { - .name = "spear1310-miphy", - .of_match_table = of_match_ptr(spear1310_miphy_of_match), - }, -}; - -module_platform_driver(spear1310_miphy_driver); - -MODULE_DESCRIPTION("ST SPEAR1310-MIPHY driver"); -MODULE_AUTHOR("Pratyush Anand "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-spear1340-miphy.c b/drivers/phy/phy-spear1340-miphy.c deleted file mode 100644 index 97280c0cf612..000000000000 --- a/drivers/phy/phy-spear1340-miphy.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * ST spear1340-miphy driver - * - * Copyright (C) 2014 ST Microelectronics - * Pratyush Anand - * Mohit Kumar - * - * 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 - -/* SPEAr1340 Registers */ -/* Power Management Registers */ -#define SPEAR1340_PCM_CFG 0x100 - #define SPEAR1340_PCM_CFG_SATA_POWER_EN BIT(11) -#define SPEAR1340_PCM_WKUP_CFG 0x104 -#define SPEAR1340_SWITCH_CTR 0x108 - -#define SPEAR1340_PERIP1_SW_RST 0x318 - #define SPEAR1340_PERIP1_SW_RSATA BIT(12) -#define SPEAR1340_PERIP2_SW_RST 0x31C -#define SPEAR1340_PERIP3_SW_RST 0x320 - -/* PCIE - SATA configuration registers */ -#define SPEAR1340_PCIE_SATA_CFG 0x424 - /* PCIE CFG MASks */ - #define SPEAR1340_PCIE_CFG_DEVICE_PRESENT BIT(11) - #define SPEAR1340_PCIE_CFG_POWERUP_RESET BIT(10) - #define SPEAR1340_PCIE_CFG_CORE_CLK_EN BIT(9) - #define SPEAR1340_PCIE_CFG_AUX_CLK_EN BIT(8) - #define SPEAR1340_SATA_CFG_TX_CLK_EN BIT(4) - #define SPEAR1340_SATA_CFG_RX_CLK_EN BIT(3) - #define SPEAR1340_SATA_CFG_POWERUP_RESET BIT(2) - #define SPEAR1340_SATA_CFG_PM_CLK_EN BIT(1) - #define SPEAR1340_PCIE_SATA_SEL_PCIE (0) - #define SPEAR1340_PCIE_SATA_SEL_SATA (1) - #define SPEAR1340_PCIE_SATA_CFG_MASK 0xF1F - #define SPEAR1340_PCIE_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_PCIE | \ - SPEAR1340_PCIE_CFG_AUX_CLK_EN | \ - SPEAR1340_PCIE_CFG_CORE_CLK_EN | \ - SPEAR1340_PCIE_CFG_POWERUP_RESET | \ - SPEAR1340_PCIE_CFG_DEVICE_PRESENT) - #define SPEAR1340_SATA_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_SATA | \ - SPEAR1340_SATA_CFG_PM_CLK_EN | \ - SPEAR1340_SATA_CFG_POWERUP_RESET | \ - SPEAR1340_SATA_CFG_RX_CLK_EN | \ - SPEAR1340_SATA_CFG_TX_CLK_EN) - -#define SPEAR1340_PCIE_MIPHY_CFG 0x428 - #define SPEAR1340_MIPHY_OSC_BYPASS_EXT BIT(31) - #define SPEAR1340_MIPHY_CLK_REF_DIV2 BIT(27) - #define SPEAR1340_MIPHY_CLK_REF_DIV4 (2 << 27) - #define SPEAR1340_MIPHY_CLK_REF_DIV8 (3 << 27) - #define SPEAR1340_MIPHY_PLL_RATIO_TOP(x) (x << 0) - #define SPEAR1340_PCIE_MIPHY_CFG_MASK 0xF80000FF - #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \ - (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ - SPEAR1340_MIPHY_CLK_REF_DIV2 | \ - SPEAR1340_MIPHY_PLL_RATIO_TOP(60)) - #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \ - (SPEAR1340_MIPHY_PLL_RATIO_TOP(120)) - #define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \ - (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ - SPEAR1340_MIPHY_PLL_RATIO_TOP(25)) - -enum spear1340_miphy_mode { - SATA, - PCIE, -}; - -struct spear1340_miphy_priv { - /* phy mode: 0 for SATA 1 for PCIe */ - enum spear1340_miphy_mode mode; - /* regmap for any soc specific misc registers */ - struct regmap *misc; - /* phy struct pointer */ - struct phy *phy; -}; - -static int spear1340_miphy_sata_init(struct spear1340_miphy_priv *priv) -{ - regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, - SPEAR1340_PCIE_SATA_CFG_MASK, - SPEAR1340_SATA_CFG_VAL); - regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, - SPEAR1340_PCIE_MIPHY_CFG_MASK, - SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK); - /* Switch on sata power domain */ - regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG, - SPEAR1340_PCM_CFG_SATA_POWER_EN, - SPEAR1340_PCM_CFG_SATA_POWER_EN); - /* Wait for SATA power domain on */ - msleep(20); - - /* Disable PCIE SATA Controller reset */ - regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST, - SPEAR1340_PERIP1_SW_RSATA, 0); - /* Wait for SATA reset de-assert completion */ - msleep(20); - - return 0; -} - -static int spear1340_miphy_sata_exit(struct spear1340_miphy_priv *priv) -{ - regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, - SPEAR1340_PCIE_SATA_CFG_MASK, 0); - regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, - SPEAR1340_PCIE_MIPHY_CFG_MASK, 0); - - /* Enable PCIE SATA Controller reset */ - regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST, - SPEAR1340_PERIP1_SW_RSATA, - SPEAR1340_PERIP1_SW_RSATA); - /* Wait for SATA power domain off */ - msleep(20); - /* Switch off sata power domain */ - regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG, - SPEAR1340_PCM_CFG_SATA_POWER_EN, 0); - /* Wait for SATA reset assert completion */ - msleep(20); - - return 0; -} - -static int spear1340_miphy_pcie_init(struct spear1340_miphy_priv *priv) -{ - regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, - SPEAR1340_PCIE_MIPHY_CFG_MASK, - SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE); - regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, - SPEAR1340_PCIE_SATA_CFG_MASK, - SPEAR1340_PCIE_CFG_VAL); - - return 0; -} - -static int spear1340_miphy_pcie_exit(struct spear1340_miphy_priv *priv) -{ - regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, - SPEAR1340_PCIE_MIPHY_CFG_MASK, 0); - regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, - SPEAR1340_PCIE_SATA_CFG_MASK, 0); - - return 0; -} - -static int spear1340_miphy_init(struct phy *phy) -{ - struct spear1340_miphy_priv *priv = phy_get_drvdata(phy); - int ret = 0; - - if (priv->mode == SATA) - ret = spear1340_miphy_sata_init(priv); - else if (priv->mode == PCIE) - ret = spear1340_miphy_pcie_init(priv); - - return ret; -} - -static int spear1340_miphy_exit(struct phy *phy) -{ - struct spear1340_miphy_priv *priv = phy_get_drvdata(phy); - int ret = 0; - - if (priv->mode == SATA) - ret = spear1340_miphy_sata_exit(priv); - else if (priv->mode == PCIE) - ret = spear1340_miphy_pcie_exit(priv); - - return ret; -} - -static const struct of_device_id spear1340_miphy_of_match[] = { - { .compatible = "st,spear1340-miphy" }, - { }, -}; -MODULE_DEVICE_TABLE(of, spear1340_miphy_of_match); - -static const struct phy_ops spear1340_miphy_ops = { - .init = spear1340_miphy_init, - .exit = spear1340_miphy_exit, - .owner = THIS_MODULE, -}; - -#ifdef CONFIG_PM_SLEEP -static int spear1340_miphy_suspend(struct device *dev) -{ - struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); - int ret = 0; - - if (priv->mode == SATA) - ret = spear1340_miphy_sata_exit(priv); - - return ret; -} - -static int spear1340_miphy_resume(struct device *dev) -{ - struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); - int ret = 0; - - if (priv->mode == SATA) - ret = spear1340_miphy_sata_init(priv); - - return ret; -} -#endif - -static SIMPLE_DEV_PM_OPS(spear1340_miphy_pm_ops, spear1340_miphy_suspend, - spear1340_miphy_resume); - -static struct phy *spear1340_miphy_xlate(struct device *dev, - struct of_phandle_args *args) -{ - struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); - - if (args->args_count < 1) { - dev_err(dev, "DT did not pass correct no of args\n"); - return ERR_PTR(-ENODEV); - } - - priv->mode = args->args[0]; - - if (priv->mode != SATA && priv->mode != PCIE) { - dev_err(dev, "DT did not pass correct phy mode\n"); - return ERR_PTR(-ENODEV); - } - - return priv->phy; -} - -static int spear1340_miphy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct spear1340_miphy_priv *priv; - struct phy_provider *phy_provider; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->misc = - syscon_regmap_lookup_by_phandle(dev->of_node, "misc"); - if (IS_ERR(priv->misc)) { - dev_err(dev, "failed to find misc regmap\n"); - return PTR_ERR(priv->misc); - } - - priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops); - if (IS_ERR(priv->phy)) { - dev_err(dev, "failed to create SATA PCIe PHY\n"); - return PTR_ERR(priv->phy); - } - - dev_set_drvdata(dev, priv); - phy_set_drvdata(priv->phy, priv); - - phy_provider = - devm_of_phy_provider_register(dev, spear1340_miphy_xlate); - if (IS_ERR(phy_provider)) { - dev_err(dev, "failed to register phy provider\n"); - return PTR_ERR(phy_provider); - } - - return 0; -} - -static struct platform_driver spear1340_miphy_driver = { - .probe = spear1340_miphy_probe, - .driver = { - .name = "spear1340-miphy", - .pm = &spear1340_miphy_pm_ops, - .of_match_table = of_match_ptr(spear1340_miphy_of_match), - }, -}; - -module_platform_driver(spear1340_miphy_driver); - -MODULE_DESCRIPTION("ST SPEAR1340-MIPHY driver"); -MODULE_AUTHOR("Pratyush Anand "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-stih407-usb.c b/drivers/phy/phy-stih407-usb.c deleted file mode 100644 index b1f44ab669fb..000000000000 --- a/drivers/phy/phy-stih407-usb.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2014 STMicroelectronics - * - * STMicroelectronics Generic PHY driver for STiH407 USB2. - * - * Author: Giuseppe Cavallaro - * - * 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 -#include - -#define PHYPARAM_REG 1 -#define PHYCTRL_REG 2 - -/* Default PHY_SEL and REFCLKSEL configuration */ -#define STIH407_USB_PICOPHY_CTRL_PORT_CONF 0x6 -#define STIH407_USB_PICOPHY_CTRL_PORT_MASK 0x1f - -/* ports parameters overriding */ -#define STIH407_USB_PICOPHY_PARAM_DEF 0x39a4dc -#define STIH407_USB_PICOPHY_PARAM_MASK 0xffffffff - -struct stih407_usb2_picophy { - struct phy *phy; - struct regmap *regmap; - struct device *dev; - struct reset_control *rstc; - struct reset_control *rstport; - int ctrl; - int param; -}; - -static int stih407_usb2_pico_ctrl(struct stih407_usb2_picophy *phy_dev) -{ - reset_control_deassert(phy_dev->rstc); - - return regmap_update_bits(phy_dev->regmap, phy_dev->ctrl, - STIH407_USB_PICOPHY_CTRL_PORT_MASK, - STIH407_USB_PICOPHY_CTRL_PORT_CONF); -} - -static int stih407_usb2_init_port(struct phy *phy) -{ - int ret; - struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy); - - stih407_usb2_pico_ctrl(phy_dev); - - ret = regmap_update_bits(phy_dev->regmap, - phy_dev->param, - STIH407_USB_PICOPHY_PARAM_MASK, - STIH407_USB_PICOPHY_PARAM_DEF); - if (ret) - return ret; - - return reset_control_deassert(phy_dev->rstport); -} - -static int stih407_usb2_exit_port(struct phy *phy) -{ - struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy); - - /* - * Only port reset is asserted, phy global reset is kept untouched - * as other ports may still be active. When all ports are in reset - * state, assumption is made that power will be cut off on the phy, in - * case of suspend for instance. Theoretically, asserting individual - * reset (like here) or global reset should be equivalent. - */ - return reset_control_assert(phy_dev->rstport); -} - -static const struct phy_ops stih407_usb2_picophy_data = { - .init = stih407_usb2_init_port, - .exit = stih407_usb2_exit_port, - .owner = THIS_MODULE, -}; - -static int stih407_usb2_picophy_probe(struct platform_device *pdev) -{ - struct stih407_usb2_picophy *phy_dev; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct phy_provider *phy_provider; - struct phy *phy; - int ret; - - phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); - if (!phy_dev) - return -ENOMEM; - - phy_dev->dev = dev; - dev_set_drvdata(dev, phy_dev); - - phy_dev->rstc = devm_reset_control_get_shared(dev, "global"); - if (IS_ERR(phy_dev->rstc)) { - dev_err(dev, "failed to ctrl picoPHY reset\n"); - return PTR_ERR(phy_dev->rstc); - } - - phy_dev->rstport = devm_reset_control_get_exclusive(dev, "port"); - if (IS_ERR(phy_dev->rstport)) { - dev_err(dev, "failed to ctrl picoPHY reset\n"); - return PTR_ERR(phy_dev->rstport); - } - - /* Reset port by default: only deassert it in phy init */ - reset_control_assert(phy_dev->rstport); - - phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); - if (IS_ERR(phy_dev->regmap)) { - dev_err(dev, "No syscfg phandle specified\n"); - return PTR_ERR(phy_dev->regmap); - } - - ret = of_property_read_u32_index(np, "st,syscfg", PHYPARAM_REG, - &phy_dev->param); - if (ret) { - dev_err(dev, "can't get phyparam offset (%d)\n", ret); - return ret; - } - - ret = of_property_read_u32_index(np, "st,syscfg", PHYCTRL_REG, - &phy_dev->ctrl); - if (ret) { - dev_err(dev, "can't get phyctrl offset (%d)\n", ret); - return ret; - } - - phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data); - if (IS_ERR(phy)) { - dev_err(dev, "failed to create Display Port PHY\n"); - return PTR_ERR(phy); - } - - phy_dev->phy = phy; - phy_set_drvdata(phy, phy_dev); - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - - dev_info(dev, "STiH407 USB Generic picoPHY driver probed!"); - - return 0; -} - -static const struct of_device_id stih407_usb2_picophy_of_match[] = { - { .compatible = "st,stih407-usb2-phy" }, - { /*sentinel */ }, -}; - -MODULE_DEVICE_TABLE(of, stih407_usb2_picophy_of_match); - -static struct platform_driver stih407_usb2_picophy_driver = { - .probe = stih407_usb2_picophy_probe, - .driver = { - .name = "stih407-usb-genphy", - .of_match_table = stih407_usb2_picophy_of_match, - } -}; - -module_platform_driver(stih407_usb2_picophy_driver); - -MODULE_AUTHOR("Giuseppe Cavallaro "); -MODULE_DESCRIPTION("STMicroelectronics Generic picoPHY driver for STiH407"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c deleted file mode 100644 index bbf06cfe5898..000000000000 --- a/drivers/phy/phy-sun4i-usb.c +++ /dev/null @@ -1,891 +0,0 @@ -/* - * Allwinner sun4i USB phy driver - * - * Copyright (C) 2014-2015 Hans de Goede - * - * Based on code from - * Allwinner Technology Co., Ltd. - * - * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver - * Copyright (C) 2013 Samsung Electronics Co., Ltd. - * Author: Sylwester Nawrocki - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define REG_ISCR 0x00 -#define REG_PHYCTL_A10 0x04 -#define REG_PHYBIST 0x08 -#define REG_PHYTUNE 0x0c -#define REG_PHYCTL_A33 0x10 -#define REG_PHY_OTGCTL 0x20 - -#define REG_PMU_UNK1 0x10 - -#define PHYCTL_DATA BIT(7) - -#define OTGCTL_ROUTE_MUSB BIT(0) - -#define SUNXI_AHB_ICHR8_EN BIT(10) -#define SUNXI_AHB_INCR4_BURST_EN BIT(9) -#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) -#define SUNXI_ULPI_BYPASS_EN BIT(0) - -/* ISCR, Interface Status and Control bits */ -#define ISCR_ID_PULLUP_EN (1 << 17) -#define ISCR_DPDM_PULLUP_EN (1 << 16) -/* sunxi has the phy id/vbus pins not connected, so we use the force bits */ -#define ISCR_FORCE_ID_MASK (3 << 14) -#define ISCR_FORCE_ID_LOW (2 << 14) -#define ISCR_FORCE_ID_HIGH (3 << 14) -#define ISCR_FORCE_VBUS_MASK (3 << 12) -#define ISCR_FORCE_VBUS_LOW (2 << 12) -#define ISCR_FORCE_VBUS_HIGH (3 << 12) - -/* Common Control Bits for Both PHYs */ -#define PHY_PLL_BW 0x03 -#define PHY_RES45_CAL_EN 0x0c - -/* Private Control Bits for Each PHY */ -#define PHY_TX_AMPLITUDE_TUNE 0x20 -#define PHY_TX_SLEWRATE_TUNE 0x22 -#define PHY_VBUSVALID_TH_SEL 0x25 -#define PHY_PULLUP_RES_SEL 0x27 -#define PHY_OTG_FUNC_EN 0x28 -#define PHY_VBUS_DET_EN 0x29 -#define PHY_DISCON_TH_SEL 0x2a -#define PHY_SQUELCH_DETECT 0x3c - -#define MAX_PHYS 4 - -/* - * Note do not raise the debounce time, we must report Vusb high within 100ms - * otherwise we get Vbus errors - */ -#define DEBOUNCE_TIME msecs_to_jiffies(50) -#define POLL_TIME msecs_to_jiffies(250) - -enum sun4i_usb_phy_type { - sun4i_a10_phy, - sun6i_a31_phy, - sun8i_a33_phy, - sun8i_h3_phy, - sun8i_v3s_phy, - sun50i_a64_phy, -}; - -struct sun4i_usb_phy_cfg { - int num_phys; - enum sun4i_usb_phy_type type; - u32 disc_thresh; - u8 phyctl_offset; - bool dedicated_clocks; - bool enable_pmu_unk1; - bool phy0_dual_route; -}; - -struct sun4i_usb_phy_data { - void __iomem *base; - const struct sun4i_usb_phy_cfg *cfg; - enum usb_dr_mode dr_mode; - spinlock_t reg_lock; /* guard access to phyctl reg */ - struct sun4i_usb_phy { - struct phy *phy; - void __iomem *pmu; - struct regulator *vbus; - struct reset_control *reset; - struct clk *clk; - bool regulator_on; - int index; - } phys[MAX_PHYS]; - /* phy0 / otg related variables */ - struct extcon_dev *extcon; - bool phy0_init; - struct gpio_desc *id_det_gpio; - struct gpio_desc *vbus_det_gpio; - struct power_supply *vbus_power_supply; - struct notifier_block vbus_power_nb; - bool vbus_power_nb_registered; - bool force_session_end; - int id_det_irq; - int vbus_det_irq; - int id_det; - int vbus_det; - struct delayed_work detect; -}; - -#define to_sun4i_usb_phy_data(phy) \ - container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index]) - -static void sun4i_usb_phy0_update_iscr(struct phy *_phy, u32 clr, u32 set) -{ - struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); - struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); - u32 iscr; - - iscr = readl(data->base + REG_ISCR); - iscr &= ~clr; - iscr |= set; - writel(iscr, data->base + REG_ISCR); -} - -static void sun4i_usb_phy0_set_id_detect(struct phy *phy, u32 val) -{ - if (val) - val = ISCR_FORCE_ID_HIGH; - else - val = ISCR_FORCE_ID_LOW; - - sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_ID_MASK, val); -} - -static void sun4i_usb_phy0_set_vbus_detect(struct phy *phy, u32 val) -{ - if (val) - val = ISCR_FORCE_VBUS_HIGH; - else - val = ISCR_FORCE_VBUS_LOW; - - sun4i_usb_phy0_update_iscr(phy, ISCR_FORCE_VBUS_MASK, val); -} - -static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data, - int len) -{ - struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy); - u32 temp, usbc_bit = BIT(phy->index * 2); - void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset; - unsigned long flags; - int i; - - spin_lock_irqsave(&phy_data->reg_lock, flags); - - if (phy_data->cfg->phyctl_offset == REG_PHYCTL_A33) { - /* SoCs newer than A33 need us to set phyctl to 0 explicitly */ - writel(0, phyctl); - } - - for (i = 0; i < len; i++) { - temp = readl(phyctl); - - /* clear the address portion */ - temp &= ~(0xff << 8); - - /* set the address */ - temp |= ((addr + i) << 8); - writel(temp, phyctl); - - /* set the data bit and clear usbc bit*/ - temp = readb(phyctl); - if (data & 0x1) - temp |= PHYCTL_DATA; - else - temp &= ~PHYCTL_DATA; - temp &= ~usbc_bit; - writeb(temp, phyctl); - - /* pulse usbc_bit */ - temp = readb(phyctl); - temp |= usbc_bit; - writeb(temp, phyctl); - - temp = readb(phyctl); - temp &= ~usbc_bit; - writeb(temp, phyctl); - - data >>= 1; - } - - spin_unlock_irqrestore(&phy_data->reg_lock, flags); -} - -static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable) -{ - u32 bits, reg_value; - - if (!phy->pmu) - return; - - bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN | - SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN; - - reg_value = readl(phy->pmu); - - if (enable) - reg_value |= bits; - else - reg_value &= ~bits; - - writel(reg_value, phy->pmu); -} - -static int sun4i_usb_phy_init(struct phy *_phy) -{ - struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); - struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); - int ret; - u32 val; - - ret = clk_prepare_enable(phy->clk); - if (ret) - return ret; - - ret = reset_control_deassert(phy->reset); - if (ret) { - clk_disable_unprepare(phy->clk); - return ret; - } - - if (phy->pmu && data->cfg->enable_pmu_unk1) { - val = readl(phy->pmu + REG_PMU_UNK1); - writel(val & ~2, phy->pmu + REG_PMU_UNK1); - } - - /* Enable USB 45 Ohm resistor calibration */ - if (phy->index == 0) - sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1); - - /* Adjust PHY's magnitude and rate */ - sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5); - - /* Disconnect threshold adjustment */ - sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, - data->cfg->disc_thresh, 2); - - sun4i_usb_phy_passby(phy, 1); - - if (phy->index == 0) { - data->phy0_init = true; - - /* Enable pull-ups */ - sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_DPDM_PULLUP_EN); - sun4i_usb_phy0_update_iscr(_phy, 0, ISCR_ID_PULLUP_EN); - - /* Force ISCR and cable state updates */ - data->id_det = -1; - data->vbus_det = -1; - queue_delayed_work(system_wq, &data->detect, 0); - } - - return 0; -} - -static int sun4i_usb_phy_exit(struct phy *_phy) -{ - struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); - struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); - - if (phy->index == 0) { - /* Disable pull-ups */ - sun4i_usb_phy0_update_iscr(_phy, ISCR_DPDM_PULLUP_EN, 0); - sun4i_usb_phy0_update_iscr(_phy, ISCR_ID_PULLUP_EN, 0); - data->phy0_init = false; - } - - sun4i_usb_phy_passby(phy, 0); - reset_control_assert(phy->reset); - clk_disable_unprepare(phy->clk); - - return 0; -} - -static int sun4i_usb_phy0_get_id_det(struct sun4i_usb_phy_data *data) -{ - switch (data->dr_mode) { - case USB_DR_MODE_OTG: - if (data->id_det_gpio) - return gpiod_get_value_cansleep(data->id_det_gpio); - else - return 1; /* Fallback to peripheral mode */ - case USB_DR_MODE_HOST: - return 0; - case USB_DR_MODE_PERIPHERAL: - default: - return 1; - } -} - -static int sun4i_usb_phy0_get_vbus_det(struct sun4i_usb_phy_data *data) -{ - if (data->vbus_det_gpio) - return gpiod_get_value_cansleep(data->vbus_det_gpio); - - if (data->vbus_power_supply) { - union power_supply_propval val; - int r; - - r = power_supply_get_property(data->vbus_power_supply, - POWER_SUPPLY_PROP_PRESENT, &val); - if (r == 0) - return val.intval; - } - - /* Fallback: report vbus as high */ - return 1; -} - -static bool sun4i_usb_phy0_have_vbus_det(struct sun4i_usb_phy_data *data) -{ - return data->vbus_det_gpio || data->vbus_power_supply; -} - -static bool sun4i_usb_phy0_poll(struct sun4i_usb_phy_data *data) -{ - if ((data->id_det_gpio && data->id_det_irq <= 0) || - (data->vbus_det_gpio && data->vbus_det_irq <= 0)) - return true; - - /* - * The A31 companion pmic (axp221) does not generate vbus change - * interrupts when the board is driving vbus, so we must poll - * when using the pmic for vbus-det _and_ we're driving vbus. - */ - if (data->cfg->type == sun6i_a31_phy && - data->vbus_power_supply && data->phys[0].regulator_on) - return true; - - return false; -} - -static int sun4i_usb_phy_power_on(struct phy *_phy) -{ - struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); - struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); - int ret; - - if (!phy->vbus || phy->regulator_on) - return 0; - - /* For phy0 only turn on Vbus if we don't have an ext. Vbus */ - if (phy->index == 0 && sun4i_usb_phy0_have_vbus_det(data) && - data->vbus_det) { - dev_warn(&_phy->dev, "External vbus detected, not enabling our own vbus\n"); - return 0; - } - - ret = regulator_enable(phy->vbus); - if (ret) - return ret; - - phy->regulator_on = true; - - /* We must report Vbus high within OTG_TIME_A_WAIT_VRISE msec. */ - if (phy->index == 0 && sun4i_usb_phy0_poll(data)) - mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); - - return 0; -} - -static int sun4i_usb_phy_power_off(struct phy *_phy) -{ - struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); - struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); - - if (!phy->vbus || !phy->regulator_on) - return 0; - - regulator_disable(phy->vbus); - phy->regulator_on = false; - - /* - * phy0 vbus typically slowly discharges, sometimes this causes the - * Vbus gpio to not trigger an edge irq on Vbus off, so force a rescan. - */ - if (phy->index == 0 && !sun4i_usb_phy0_poll(data)) - mod_delayed_work(system_wq, &data->detect, POLL_TIME); - - return 0; -} - -static int sun4i_usb_phy_set_mode(struct phy *_phy, enum phy_mode mode) -{ - struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); - struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy); - int new_mode; - - if (phy->index != 0) - return -EINVAL; - - switch (mode) { - case PHY_MODE_USB_HOST: - new_mode = USB_DR_MODE_HOST; - break; - case PHY_MODE_USB_DEVICE: - new_mode = USB_DR_MODE_PERIPHERAL; - break; - case PHY_MODE_USB_OTG: - new_mode = USB_DR_MODE_OTG; - break; - default: - return -EINVAL; - } - - if (new_mode != data->dr_mode) { - dev_info(&_phy->dev, "Changing dr_mode to %d\n", new_mode); - data->dr_mode = new_mode; - } - - data->id_det = -1; /* Force reprocessing of id */ - data->force_session_end = true; - queue_delayed_work(system_wq, &data->detect, 0); - - return 0; -} - -void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled) -{ - struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); - - sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2); -} -EXPORT_SYMBOL_GPL(sun4i_usb_phy_set_squelch_detect); - -static const struct phy_ops sun4i_usb_phy_ops = { - .init = sun4i_usb_phy_init, - .exit = sun4i_usb_phy_exit, - .power_on = sun4i_usb_phy_power_on, - .power_off = sun4i_usb_phy_power_off, - .set_mode = sun4i_usb_phy_set_mode, - .owner = THIS_MODULE, -}; - -static void sun4i_usb_phy0_reroute(struct sun4i_usb_phy_data *data, int id_det) -{ - u32 regval; - - regval = readl(data->base + REG_PHY_OTGCTL); - if (id_det == 0) { - /* Host mode. Route phy0 to EHCI/OHCI */ - regval &= ~OTGCTL_ROUTE_MUSB; - } else { - /* Peripheral mode. Route phy0 to MUSB */ - regval |= OTGCTL_ROUTE_MUSB; - } - writel(regval, data->base + REG_PHY_OTGCTL); -} - -static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work) -{ - struct sun4i_usb_phy_data *data = - container_of(work, struct sun4i_usb_phy_data, detect.work); - struct phy *phy0 = data->phys[0].phy; - bool force_session_end, id_notify = false, vbus_notify = false; - int id_det, vbus_det; - - if (phy0 == NULL) - return; - - id_det = sun4i_usb_phy0_get_id_det(data); - vbus_det = sun4i_usb_phy0_get_vbus_det(data); - - mutex_lock(&phy0->mutex); - - if (!data->phy0_init) { - mutex_unlock(&phy0->mutex); - return; - } - - force_session_end = data->force_session_end; - data->force_session_end = false; - - if (id_det != data->id_det) { - /* id-change, force session end if we've no vbus detection */ - if (data->dr_mode == USB_DR_MODE_OTG && - !sun4i_usb_phy0_have_vbus_det(data)) - force_session_end = true; - - /* When entering host mode (id = 0) force end the session now */ - if (force_session_end && id_det == 0) { - sun4i_usb_phy0_set_vbus_detect(phy0, 0); - msleep(200); - sun4i_usb_phy0_set_vbus_detect(phy0, 1); - } - sun4i_usb_phy0_set_id_detect(phy0, id_det); - data->id_det = id_det; - id_notify = true; - } - - if (vbus_det != data->vbus_det) { - sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det); - data->vbus_det = vbus_det; - vbus_notify = true; - } - - mutex_unlock(&phy0->mutex); - - if (id_notify) { - extcon_set_state_sync(data->extcon, EXTCON_USB_HOST, - !id_det); - /* When leaving host mode force end the session here */ - if (force_session_end && id_det == 1) { - mutex_lock(&phy0->mutex); - sun4i_usb_phy0_set_vbus_detect(phy0, 0); - msleep(1000); - sun4i_usb_phy0_set_vbus_detect(phy0, 1); - mutex_unlock(&phy0->mutex); - } - - /* Re-route PHY0 if necessary */ - if (data->cfg->phy0_dual_route) - sun4i_usb_phy0_reroute(data, id_det); - } - - if (vbus_notify) - extcon_set_state_sync(data->extcon, EXTCON_USB, vbus_det); - - if (sun4i_usb_phy0_poll(data)) - queue_delayed_work(system_wq, &data->detect, POLL_TIME); -} - -static irqreturn_t sun4i_usb_phy0_id_vbus_det_irq(int irq, void *dev_id) -{ - struct sun4i_usb_phy_data *data = dev_id; - - /* vbus or id changed, let the pins settle and then scan them */ - mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); - - return IRQ_HANDLED; -} - -static int sun4i_usb_phy0_vbus_notify(struct notifier_block *nb, - unsigned long val, void *v) -{ - struct sun4i_usb_phy_data *data = - container_of(nb, struct sun4i_usb_phy_data, vbus_power_nb); - struct power_supply *psy = v; - - /* Properties on the vbus_power_supply changed, scan vbus_det */ - if (val == PSY_EVENT_PROP_CHANGED && psy == data->vbus_power_supply) - mod_delayed_work(system_wq, &data->detect, DEBOUNCE_TIME); - - return NOTIFY_OK; -} - -static struct phy *sun4i_usb_phy_xlate(struct device *dev, - struct of_phandle_args *args) -{ - struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); - - if (args->args[0] >= data->cfg->num_phys) - return ERR_PTR(-ENODEV); - - return data->phys[args->args[0]].phy; -} - -static int sun4i_usb_phy_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); - - if (data->vbus_power_nb_registered) - power_supply_unreg_notifier(&data->vbus_power_nb); - if (data->id_det_irq > 0) - devm_free_irq(dev, data->id_det_irq, data); - if (data->vbus_det_irq > 0) - devm_free_irq(dev, data->vbus_det_irq, data); - - cancel_delayed_work_sync(&data->detect); - - return 0; -} - -static const unsigned int sun4i_usb_phy0_cable[] = { - EXTCON_USB, - EXTCON_USB_HOST, - EXTCON_NONE, -}; - -static int sun4i_usb_phy_probe(struct platform_device *pdev) -{ - struct sun4i_usb_phy_data *data; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct phy_provider *phy_provider; - struct resource *res; - int i, ret; - - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - spin_lock_init(&data->reg_lock); - INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan); - dev_set_drvdata(dev, data); - data->cfg = of_device_get_match_data(dev); - if (!data->cfg) - return -EINVAL; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl"); - data->base = devm_ioremap_resource(dev, res); - if (IS_ERR(data->base)) - return PTR_ERR(data->base); - - data->id_det_gpio = devm_gpiod_get_optional(dev, "usb0_id_det", - GPIOD_IN); - if (IS_ERR(data->id_det_gpio)) - return PTR_ERR(data->id_det_gpio); - - data->vbus_det_gpio = devm_gpiod_get_optional(dev, "usb0_vbus_det", - GPIOD_IN); - if (IS_ERR(data->vbus_det_gpio)) - return PTR_ERR(data->vbus_det_gpio); - - if (of_find_property(np, "usb0_vbus_power-supply", NULL)) { - data->vbus_power_supply = devm_power_supply_get_by_phandle(dev, - "usb0_vbus_power-supply"); - if (IS_ERR(data->vbus_power_supply)) - return PTR_ERR(data->vbus_power_supply); - - if (!data->vbus_power_supply) - return -EPROBE_DEFER; - } - - data->dr_mode = of_usb_get_dr_mode_by_phy(np, 0); - - data->extcon = devm_extcon_dev_allocate(dev, sun4i_usb_phy0_cable); - if (IS_ERR(data->extcon)) - return PTR_ERR(data->extcon); - - ret = devm_extcon_dev_register(dev, data->extcon); - if (ret) { - dev_err(dev, "failed to register extcon: %d\n", ret); - return ret; - } - - for (i = 0; i < data->cfg->num_phys; i++) { - struct sun4i_usb_phy *phy = data->phys + i; - char name[16]; - - snprintf(name, sizeof(name), "usb%d_vbus", i); - phy->vbus = devm_regulator_get_optional(dev, name); - if (IS_ERR(phy->vbus)) { - if (PTR_ERR(phy->vbus) == -EPROBE_DEFER) - return -EPROBE_DEFER; - phy->vbus = NULL; - } - - if (data->cfg->dedicated_clocks) - snprintf(name, sizeof(name), "usb%d_phy", i); - else - strlcpy(name, "usb_phy", sizeof(name)); - - phy->clk = devm_clk_get(dev, name); - if (IS_ERR(phy->clk)) { - dev_err(dev, "failed to get clock %s\n", name); - return PTR_ERR(phy->clk); - } - - snprintf(name, sizeof(name), "usb%d_reset", i); - phy->reset = devm_reset_control_get(dev, name); - if (IS_ERR(phy->reset)) { - dev_err(dev, "failed to get reset %s\n", name); - return PTR_ERR(phy->reset); - } - - if (i || data->cfg->phy0_dual_route) { /* No pmu for musb */ - snprintf(name, sizeof(name), "pmu%d", i); - res = platform_get_resource_byname(pdev, - IORESOURCE_MEM, name); - phy->pmu = devm_ioremap_resource(dev, res); - if (IS_ERR(phy->pmu)) - return PTR_ERR(phy->pmu); - } - - phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops); - if (IS_ERR(phy->phy)) { - dev_err(dev, "failed to create PHY %d\n", i); - return PTR_ERR(phy->phy); - } - - phy->index = i; - phy_set_drvdata(phy->phy, &data->phys[i]); - } - - data->id_det_irq = gpiod_to_irq(data->id_det_gpio); - if (data->id_det_irq > 0) { - ret = devm_request_irq(dev, data->id_det_irq, - sun4i_usb_phy0_id_vbus_det_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "usb0-id-det", data); - if (ret) { - dev_err(dev, "Err requesting id-det-irq: %d\n", ret); - return ret; - } - } - - data->vbus_det_irq = gpiod_to_irq(data->vbus_det_gpio); - if (data->vbus_det_irq > 0) { - ret = devm_request_irq(dev, data->vbus_det_irq, - sun4i_usb_phy0_id_vbus_det_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "usb0-vbus-det", data); - if (ret) { - dev_err(dev, "Err requesting vbus-det-irq: %d\n", ret); - data->vbus_det_irq = -1; - sun4i_usb_phy_remove(pdev); /* Stop detect work */ - return ret; - } - } - - if (data->vbus_power_supply) { - data->vbus_power_nb.notifier_call = sun4i_usb_phy0_vbus_notify; - data->vbus_power_nb.priority = 0; - ret = power_supply_reg_notifier(&data->vbus_power_nb); - if (ret) { - sun4i_usb_phy_remove(pdev); /* Stop detect work */ - return ret; - } - data->vbus_power_nb_registered = true; - } - - phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate); - if (IS_ERR(phy_provider)) { - sun4i_usb_phy_remove(pdev); /* Stop detect work */ - return PTR_ERR(phy_provider); - } - - return 0; -} - -static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = { - .num_phys = 3, - .type = sun4i_a10_phy, - .disc_thresh = 3, - .phyctl_offset = REG_PHYCTL_A10, - .dedicated_clocks = false, - .enable_pmu_unk1 = false, -}; - -static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { - .num_phys = 2, - .type = sun4i_a10_phy, - .disc_thresh = 2, - .phyctl_offset = REG_PHYCTL_A10, - .dedicated_clocks = false, - .enable_pmu_unk1 = false, -}; - -static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { - .num_phys = 3, - .type = sun6i_a31_phy, - .disc_thresh = 3, - .phyctl_offset = REG_PHYCTL_A10, - .dedicated_clocks = true, - .enable_pmu_unk1 = false, -}; - -static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { - .num_phys = 3, - .type = sun4i_a10_phy, - .disc_thresh = 2, - .phyctl_offset = REG_PHYCTL_A10, - .dedicated_clocks = false, - .enable_pmu_unk1 = false, -}; - -static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { - .num_phys = 2, - .type = sun4i_a10_phy, - .disc_thresh = 3, - .phyctl_offset = REG_PHYCTL_A10, - .dedicated_clocks = true, - .enable_pmu_unk1 = false, -}; - -static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { - .num_phys = 2, - .type = sun8i_a33_phy, - .disc_thresh = 3, - .phyctl_offset = REG_PHYCTL_A33, - .dedicated_clocks = true, - .enable_pmu_unk1 = false, -}; - -static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { - .num_phys = 4, - .type = sun8i_h3_phy, - .disc_thresh = 3, - .phyctl_offset = REG_PHYCTL_A33, - .dedicated_clocks = true, - .enable_pmu_unk1 = true, - .phy0_dual_route = true, -}; - -static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { - .num_phys = 1, - .type = sun8i_v3s_phy, - .disc_thresh = 3, - .phyctl_offset = REG_PHYCTL_A33, - .dedicated_clocks = true, - .enable_pmu_unk1 = true, -}; - -static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { - .num_phys = 2, - .type = sun50i_a64_phy, - .disc_thresh = 3, - .phyctl_offset = REG_PHYCTL_A33, - .dedicated_clocks = true, - .enable_pmu_unk1 = true, - .phy0_dual_route = true, -}; - -static const struct of_device_id sun4i_usb_phy_of_match[] = { - { .compatible = "allwinner,sun4i-a10-usb-phy", .data = &sun4i_a10_cfg }, - { .compatible = "allwinner,sun5i-a13-usb-phy", .data = &sun5i_a13_cfg }, - { .compatible = "allwinner,sun6i-a31-usb-phy", .data = &sun6i_a31_cfg }, - { .compatible = "allwinner,sun7i-a20-usb-phy", .data = &sun7i_a20_cfg }, - { .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg }, - { .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg }, - { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg }, - { .compatible = "allwinner,sun8i-v3s-usb-phy", .data = &sun8i_v3s_cfg }, - { .compatible = "allwinner,sun50i-a64-usb-phy", - .data = &sun50i_a64_cfg}, - { }, -}; -MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match); - -static struct platform_driver sun4i_usb_phy_driver = { - .probe = sun4i_usb_phy_probe, - .remove = sun4i_usb_phy_remove, - .driver = { - .of_match_table = sun4i_usb_phy_of_match, - .name = "sun4i-usb-phy", - } -}; -module_platform_driver(sun4i_usb_phy_driver); - -MODULE_DESCRIPTION("Allwinner sun4i USB phy driver"); -MODULE_AUTHOR("Hans de Goede "); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-sun9i-usb.c b/drivers/phy/phy-sun9i-usb.c deleted file mode 100644 index 28fce4bce638..000000000000 --- a/drivers/phy/phy-sun9i-usb.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Allwinner sun9i USB phy driver - * - * Copyright (C) 2014-2015 Chen-Yu Tsai - * - * Based on phy-sun4i-usb.c from - * Hans de Goede - * - * and code from - * Allwinner Technology Co., Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define SUNXI_AHB_INCR16_BURST_EN BIT(11) -#define SUNXI_AHB_INCR8_BURST_EN BIT(10) -#define SUNXI_AHB_INCR4_BURST_EN BIT(9) -#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8) -#define SUNXI_ULPI_BYPASS_EN BIT(0) - -/* usb1 HSIC specific bits */ -#define SUNXI_EHCI_HS_FORCE BIT(20) -#define SUNXI_HSIC_CONNECT_DET BIT(17) -#define SUNXI_HSIC_CONNECT_INT BIT(16) -#define SUNXI_HSIC BIT(1) - -struct sun9i_usb_phy { - struct phy *phy; - void __iomem *pmu; - struct reset_control *reset; - struct clk *clk; - struct clk *hsic_clk; - enum usb_phy_interface type; -}; - -static void sun9i_usb_phy_passby(struct sun9i_usb_phy *phy, int enable) -{ - u32 bits, reg_value; - - bits = SUNXI_AHB_INCR16_BURST_EN | SUNXI_AHB_INCR8_BURST_EN | - SUNXI_AHB_INCR4_BURST_EN | SUNXI_AHB_INCRX_ALIGN_EN | - SUNXI_ULPI_BYPASS_EN; - - if (phy->type == USBPHY_INTERFACE_MODE_HSIC) - bits |= SUNXI_HSIC | SUNXI_EHCI_HS_FORCE | - SUNXI_HSIC_CONNECT_DET | SUNXI_HSIC_CONNECT_INT; - - reg_value = readl(phy->pmu); - - if (enable) - reg_value |= bits; - else - reg_value &= ~bits; - - writel(reg_value, phy->pmu); -} - -static int sun9i_usb_phy_init(struct phy *_phy) -{ - struct sun9i_usb_phy *phy = phy_get_drvdata(_phy); - int ret; - - ret = clk_prepare_enable(phy->clk); - if (ret) - goto err_clk; - - ret = clk_prepare_enable(phy->hsic_clk); - if (ret) - goto err_hsic_clk; - - ret = reset_control_deassert(phy->reset); - if (ret) - goto err_reset; - - sun9i_usb_phy_passby(phy, 1); - return 0; - -err_reset: - clk_disable_unprepare(phy->hsic_clk); - -err_hsic_clk: - clk_disable_unprepare(phy->clk); - -err_clk: - return ret; -} - -static int sun9i_usb_phy_exit(struct phy *_phy) -{ - struct sun9i_usb_phy *phy = phy_get_drvdata(_phy); - - sun9i_usb_phy_passby(phy, 0); - reset_control_assert(phy->reset); - clk_disable_unprepare(phy->hsic_clk); - clk_disable_unprepare(phy->clk); - - return 0; -} - -static const struct phy_ops sun9i_usb_phy_ops = { - .init = sun9i_usb_phy_init, - .exit = sun9i_usb_phy_exit, - .owner = THIS_MODULE, -}; - -static int sun9i_usb_phy_probe(struct platform_device *pdev) -{ - struct sun9i_usb_phy *phy; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct phy_provider *phy_provider; - struct resource *res; - - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) - return -ENOMEM; - - phy->type = of_usb_get_phy_mode(np); - if (phy->type == USBPHY_INTERFACE_MODE_HSIC) { - phy->clk = devm_clk_get(dev, "hsic_480M"); - if (IS_ERR(phy->clk)) { - dev_err(dev, "failed to get hsic_480M clock\n"); - return PTR_ERR(phy->clk); - } - - phy->hsic_clk = devm_clk_get(dev, "hsic_12M"); - if (IS_ERR(phy->hsic_clk)) { - dev_err(dev, "failed to get hsic_12M clock\n"); - return PTR_ERR(phy->hsic_clk); - } - - phy->reset = devm_reset_control_get(dev, "hsic"); - if (IS_ERR(phy->reset)) { - dev_err(dev, "failed to get reset control\n"); - return PTR_ERR(phy->reset); - } - } else { - phy->clk = devm_clk_get(dev, "phy"); - if (IS_ERR(phy->clk)) { - dev_err(dev, "failed to get phy clock\n"); - return PTR_ERR(phy->clk); - } - - phy->reset = devm_reset_control_get(dev, "phy"); - if (IS_ERR(phy->reset)) { - dev_err(dev, "failed to get reset control\n"); - return PTR_ERR(phy->reset); - } - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - phy->pmu = devm_ioremap_resource(dev, res); - if (IS_ERR(phy->pmu)) - return PTR_ERR(phy->pmu); - - phy->phy = devm_phy_create(dev, NULL, &sun9i_usb_phy_ops); - if (IS_ERR(phy->phy)) { - dev_err(dev, "failed to create PHY\n"); - return PTR_ERR(phy->phy); - } - - phy_set_drvdata(phy->phy, phy); - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - - return PTR_ERR_OR_ZERO(phy_provider); -} - -static const struct of_device_id sun9i_usb_phy_of_match[] = { - { .compatible = "allwinner,sun9i-a80-usb-phy" }, - { }, -}; -MODULE_DEVICE_TABLE(of, sun9i_usb_phy_of_match); - -static struct platform_driver sun9i_usb_phy_driver = { - .probe = sun9i_usb_phy_probe, - .driver = { - .of_match_table = sun9i_usb_phy_of_match, - .name = "sun9i-usb-phy", - } -}; -module_platform_driver(sun9i_usb_phy_driver); - -MODULE_DESCRIPTION("Allwinner sun9i USB phy driver"); -MODULE_AUTHOR("Chen-Yu Tsai "); -MODULE_LICENSE("GPL"); diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c deleted file mode 100644 index 9c84d32c6f60..000000000000 --- a/drivers/phy/phy-ti-pipe3.c +++ /dev/null @@ -1,697 +0,0 @@ -/* - * phy-ti-pipe3 - PIPE3 PHY driver. - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Author: Kishon Vijay Abraham I - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PLL_STATUS 0x00000004 -#define PLL_GO 0x00000008 -#define PLL_CONFIGURATION1 0x0000000C -#define PLL_CONFIGURATION2 0x00000010 -#define PLL_CONFIGURATION3 0x00000014 -#define PLL_CONFIGURATION4 0x00000020 - -#define PLL_REGM_MASK 0x001FFE00 -#define PLL_REGM_SHIFT 0x9 -#define PLL_REGM_F_MASK 0x0003FFFF -#define PLL_REGM_F_SHIFT 0x0 -#define PLL_REGN_MASK 0x000001FE -#define PLL_REGN_SHIFT 0x1 -#define PLL_SELFREQDCO_MASK 0x0000000E -#define PLL_SELFREQDCO_SHIFT 0x1 -#define PLL_SD_MASK 0x0003FC00 -#define PLL_SD_SHIFT 10 -#define SET_PLL_GO 0x1 -#define PLL_LDOPWDN BIT(15) -#define PLL_TICOPWDN BIT(16) -#define PLL_LOCK 0x2 -#define PLL_IDLE 0x1 - -#define SATA_PLL_SOFT_RESET BIT(18) - -#define PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000 -#define PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 14 - -#define PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000 -#define PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 22 - -#define PIPE3_PHY_TX_RX_POWERON 0x3 -#define PIPE3_PHY_TX_RX_POWEROFF 0x0 - -#define PCIE_PCS_MASK 0xFF0000 -#define PCIE_PCS_DELAY_COUNT_SHIFT 0x10 - -/* - * This is an Empirical value that works, need to confirm the actual - * value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status - * to be correctly reflected in the PIPE3PHY_PLL_STATUS register. - */ -#define PLL_IDLE_TIME 100 /* in milliseconds */ -#define PLL_LOCK_TIME 100 /* in milliseconds */ - -struct pipe3_dpll_params { - u16 m; - u8 n; - u8 freq:3; - u8 sd; - u32 mf; -}; - -struct pipe3_dpll_map { - unsigned long rate; - struct pipe3_dpll_params params; -}; - -struct ti_pipe3 { - void __iomem *pll_ctrl_base; - struct device *dev; - struct device *control_dev; - struct clk *wkupclk; - struct clk *sys_clk; - struct clk *refclk; - struct clk *div_clk; - struct pipe3_dpll_map *dpll_map; - struct regmap *phy_power_syscon; /* ctrl. reg. acces */ - struct regmap *pcs_syscon; /* ctrl. reg. acces */ - struct regmap *dpll_reset_syscon; /* ctrl. reg. acces */ - unsigned int dpll_reset_reg; /* reg. index within syscon */ - unsigned int power_reg; /* power reg. index within syscon */ - unsigned int pcie_pcs_reg; /* pcs reg. index in syscon */ - bool sata_refclk_enabled; -}; - -static struct pipe3_dpll_map dpll_map_usb[] = { - {12000000, {1250, 5, 4, 20, 0} }, /* 12 MHz */ - {16800000, {3125, 20, 4, 20, 0} }, /* 16.8 MHz */ - {19200000, {1172, 8, 4, 20, 65537} }, /* 19.2 MHz */ - {20000000, {1000, 7, 4, 10, 0} }, /* 20 MHz */ - {26000000, {1250, 12, 4, 20, 0} }, /* 26 MHz */ - {38400000, {3125, 47, 4, 20, 92843} }, /* 38.4 MHz */ - { }, /* Terminator */ -}; - -static struct pipe3_dpll_map dpll_map_sata[] = { - {12000000, {1000, 7, 4, 6, 0} }, /* 12 MHz */ - {16800000, {714, 7, 4, 6, 0} }, /* 16.8 MHz */ - {19200000, {625, 7, 4, 6, 0} }, /* 19.2 MHz */ - {20000000, {600, 7, 4, 6, 0} }, /* 20 MHz */ - {26000000, {461, 7, 4, 6, 0} }, /* 26 MHz */ - {38400000, {312, 7, 4, 6, 0} }, /* 38.4 MHz */ - { }, /* Terminator */ -}; - -static inline u32 ti_pipe3_readl(void __iomem *addr, unsigned offset) -{ - return __raw_readl(addr + offset); -} - -static inline void ti_pipe3_writel(void __iomem *addr, unsigned offset, - u32 data) -{ - __raw_writel(data, addr + offset); -} - -static struct pipe3_dpll_params *ti_pipe3_get_dpll_params(struct ti_pipe3 *phy) -{ - unsigned long rate; - struct pipe3_dpll_map *dpll_map = phy->dpll_map; - - rate = clk_get_rate(phy->sys_clk); - - for (; dpll_map->rate; dpll_map++) { - if (rate == dpll_map->rate) - return &dpll_map->params; - } - - dev_err(phy->dev, "No DPLL configuration for %lu Hz SYS CLK\n", rate); - - return NULL; -} - -static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy); -static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy); - -static int ti_pipe3_power_off(struct phy *x) -{ - u32 val; - int ret; - struct ti_pipe3 *phy = phy_get_drvdata(x); - - if (!phy->phy_power_syscon) { - omap_control_phy_power(phy->control_dev, 0); - return 0; - } - - val = PIPE3_PHY_TX_RX_POWEROFF << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; - - ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, - PIPE3_PHY_PWRCTL_CLK_CMD_MASK, val); - return ret; -} - -static int ti_pipe3_power_on(struct phy *x) -{ - u32 val; - u32 mask; - int ret; - unsigned long rate; - struct ti_pipe3 *phy = phy_get_drvdata(x); - - if (!phy->phy_power_syscon) { - omap_control_phy_power(phy->control_dev, 1); - return 0; - } - - rate = clk_get_rate(phy->sys_clk); - if (!rate) { - dev_err(phy->dev, "Invalid clock rate\n"); - return -EINVAL; - } - rate = rate / 1000000; - mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | - OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK; - val = PIPE3_PHY_TX_RX_POWERON << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; - val |= rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; - - ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, - mask, val); - return ret; -} - -static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy) -{ - u32 val; - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(PLL_LOCK_TIME); - do { - cpu_relax(); - val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); - if (val & PLL_LOCK) - return 0; - } while (!time_after(jiffies, timeout)); - - dev_err(phy->dev, "DPLL failed to lock\n"); - return -EBUSY; -} - -static int ti_pipe3_dpll_program(struct ti_pipe3 *phy) -{ - u32 val; - struct pipe3_dpll_params *dpll_params; - - dpll_params = ti_pipe3_get_dpll_params(phy); - if (!dpll_params) - return -EINVAL; - - val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); - val &= ~PLL_REGN_MASK; - val |= dpll_params->n << PLL_REGN_SHIFT; - ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); - - val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); - val &= ~PLL_SELFREQDCO_MASK; - val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT; - ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); - - val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); - val &= ~PLL_REGM_MASK; - val |= dpll_params->m << PLL_REGM_SHIFT; - ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); - - val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4); - val &= ~PLL_REGM_F_MASK; - val |= dpll_params->mf << PLL_REGM_F_SHIFT; - ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val); - - val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3); - val &= ~PLL_SD_MASK; - val |= dpll_params->sd << PLL_SD_SHIFT; - ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val); - - ti_pipe3_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO); - - return ti_pipe3_dpll_wait_lock(phy); -} - -static int ti_pipe3_init(struct phy *x) -{ - struct ti_pipe3 *phy = phy_get_drvdata(x); - u32 val; - int ret = 0; - - ti_pipe3_enable_clocks(phy); - /* - * Set pcie_pcs register to 0x96 for proper functioning of phy - * as recommended in AM572x TRM SPRUHZ6, section 18.5.2.2, table - * 18-1804. - */ - if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { - if (!phy->pcs_syscon) { - omap_control_pcie_pcs(phy->control_dev, 0x96); - return 0; - } - - val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT; - ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg, - PCIE_PCS_MASK, val); - return ret; - } - - /* Bring it out of IDLE if it is IDLE */ - val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); - if (val & PLL_IDLE) { - val &= ~PLL_IDLE; - ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); - ret = ti_pipe3_dpll_wait_lock(phy); - } - - /* SATA has issues if re-programmed when locked */ - val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); - if ((val & PLL_LOCK) && of_device_is_compatible(phy->dev->of_node, - "ti,phy-pipe3-sata")) - return ret; - - /* Program the DPLL */ - ret = ti_pipe3_dpll_program(phy); - if (ret) { - ti_pipe3_disable_clocks(phy); - return -EINVAL; - } - - return ret; -} - -static int ti_pipe3_exit(struct phy *x) -{ - struct ti_pipe3 *phy = phy_get_drvdata(x); - u32 val; - unsigned long timeout; - - /* If dpll_reset_syscon is not present we wont power down SATA DPLL - * due to Errata i783 - */ - if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") && - !phy->dpll_reset_syscon) - return 0; - - /* PCIe doesn't have internal DPLL */ - if (!of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { - /* Put DPLL in IDLE mode */ - val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); - val |= PLL_IDLE; - ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); - - /* wait for LDO and Oscillator to power down */ - timeout = jiffies + msecs_to_jiffies(PLL_IDLE_TIME); - do { - cpu_relax(); - val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); - if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN)) - break; - } while (!time_after(jiffies, timeout)); - - if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) { - dev_err(phy->dev, "Failed to power down: PLL_STATUS 0x%x\n", - val); - return -EBUSY; - } - } - - /* i783: SATA needs control bit toggle after PLL unlock */ - if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata")) { - regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg, - SATA_PLL_SOFT_RESET, SATA_PLL_SOFT_RESET); - regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg, - SATA_PLL_SOFT_RESET, 0); - } - - ti_pipe3_disable_clocks(phy); - - return 0; -} -static const struct phy_ops ops = { - .init = ti_pipe3_init, - .exit = ti_pipe3_exit, - .power_on = ti_pipe3_power_on, - .power_off = ti_pipe3_power_off, - .owner = THIS_MODULE, -}; - -static const struct of_device_id ti_pipe3_id_table[]; - -static int ti_pipe3_get_clk(struct ti_pipe3 *phy) -{ - struct clk *clk; - struct device *dev = phy->dev; - struct device_node *node = dev->of_node; - - phy->refclk = devm_clk_get(dev, "refclk"); - if (IS_ERR(phy->refclk)) { - dev_err(dev, "unable to get refclk\n"); - /* older DTBs have missing refclk in SATA PHY - * so don't bail out in case of SATA PHY. - */ - if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) - return PTR_ERR(phy->refclk); - } - - if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) { - phy->wkupclk = devm_clk_get(dev, "wkupclk"); - if (IS_ERR(phy->wkupclk)) { - dev_err(dev, "unable to get wkupclk\n"); - return PTR_ERR(phy->wkupclk); - } - } else { - phy->wkupclk = ERR_PTR(-ENODEV); - } - - if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie") || - phy->phy_power_syscon) { - phy->sys_clk = devm_clk_get(dev, "sysclk"); - if (IS_ERR(phy->sys_clk)) { - dev_err(dev, "unable to get sysclk\n"); - return -EINVAL; - } - } - - if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { - clk = devm_clk_get(dev, "dpll_ref"); - if (IS_ERR(clk)) { - dev_err(dev, "unable to get dpll ref clk\n"); - return PTR_ERR(clk); - } - clk_set_rate(clk, 1500000000); - - clk = devm_clk_get(dev, "dpll_ref_m2"); - if (IS_ERR(clk)) { - dev_err(dev, "unable to get dpll ref m2 clk\n"); - return PTR_ERR(clk); - } - clk_set_rate(clk, 100000000); - - clk = devm_clk_get(dev, "phy-div"); - if (IS_ERR(clk)) { - dev_err(dev, "unable to get phy-div clk\n"); - return PTR_ERR(clk); - } - clk_set_rate(clk, 100000000); - - phy->div_clk = devm_clk_get(dev, "div-clk"); - if (IS_ERR(phy->div_clk)) { - dev_err(dev, "unable to get div-clk\n"); - return PTR_ERR(phy->div_clk); - } - } else { - phy->div_clk = ERR_PTR(-ENODEV); - } - - return 0; -} - -static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy) -{ - struct device *dev = phy->dev; - struct device_node *node = dev->of_node; - struct device_node *control_node; - struct platform_device *control_pdev; - - phy->phy_power_syscon = syscon_regmap_lookup_by_phandle(node, - "syscon-phy-power"); - if (IS_ERR(phy->phy_power_syscon)) { - dev_dbg(dev, - "can't get syscon-phy-power, using control device\n"); - phy->phy_power_syscon = NULL; - } else { - if (of_property_read_u32_index(node, - "syscon-phy-power", 1, - &phy->power_reg)) { - dev_err(dev, "couldn't get power reg. offset\n"); - return -EINVAL; - } - } - - if (!phy->phy_power_syscon) { - control_node = of_parse_phandle(node, "ctrl-module", 0); - if (!control_node) { - dev_err(dev, "Failed to get control device phandle\n"); - return -EINVAL; - } - - control_pdev = of_find_device_by_node(control_node); - if (!control_pdev) { - dev_err(dev, "Failed to get control device\n"); - return -EINVAL; - } - - phy->control_dev = &control_pdev->dev; - } - - if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { - phy->pcs_syscon = syscon_regmap_lookup_by_phandle(node, - "syscon-pcs"); - if (IS_ERR(phy->pcs_syscon)) { - dev_dbg(dev, - "can't get syscon-pcs, using omap control\n"); - phy->pcs_syscon = NULL; - } else { - if (of_property_read_u32_index(node, - "syscon-pcs", 1, - &phy->pcie_pcs_reg)) { - dev_err(dev, - "couldn't get pcie pcs reg. offset\n"); - return -EINVAL; - } - } - } - - if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) { - phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node, - "syscon-pllreset"); - if (IS_ERR(phy->dpll_reset_syscon)) { - dev_info(dev, - "can't get syscon-pllreset, sata dpll won't idle\n"); - phy->dpll_reset_syscon = NULL; - } else { - if (of_property_read_u32_index(node, - "syscon-pllreset", 1, - &phy->dpll_reset_reg)) { - dev_err(dev, - "couldn't get pllreset reg. offset\n"); - return -EINVAL; - } - } - } - - return 0; -} - -static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy) -{ - struct resource *res; - const struct of_device_id *match; - struct device *dev = phy->dev; - struct device_node *node = dev->of_node; - struct platform_device *pdev = to_platform_device(dev); - - if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) - return 0; - - match = of_match_device(ti_pipe3_id_table, dev); - if (!match) - return -EINVAL; - - phy->dpll_map = (struct pipe3_dpll_map *)match->data; - if (!phy->dpll_map) { - dev_err(dev, "no DPLL data\n"); - return -EINVAL; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "pll_ctrl"); - phy->pll_ctrl_base = devm_ioremap_resource(dev, res); - return PTR_ERR_OR_ZERO(phy->pll_ctrl_base); -} - -static int ti_pipe3_probe(struct platform_device *pdev) -{ - struct ti_pipe3 *phy; - struct phy *generic_phy; - struct phy_provider *phy_provider; - struct device_node *node = pdev->dev.of_node; - struct device *dev = &pdev->dev; - int ret; - - phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); - if (!phy) - return -ENOMEM; - - phy->dev = dev; - - ret = ti_pipe3_get_pll_base(phy); - if (ret) - return ret; - - ret = ti_pipe3_get_sysctrl(phy); - if (ret) - return ret; - - ret = ti_pipe3_get_clk(phy); - if (ret) - return ret; - - platform_set_drvdata(pdev, phy); - pm_runtime_enable(dev); - - /* - * Prevent auto-disable of refclk for SATA PHY due to Errata i783 - */ - if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) { - if (!IS_ERR(phy->refclk)) { - clk_prepare_enable(phy->refclk); - phy->sata_refclk_enabled = true; - } - } - - generic_phy = devm_phy_create(dev, NULL, &ops); - if (IS_ERR(generic_phy)) - return PTR_ERR(generic_phy); - - phy_set_drvdata(generic_phy, phy); - - ti_pipe3_power_off(generic_phy); - - phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); - return PTR_ERR_OR_ZERO(phy_provider); -} - -static int ti_pipe3_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy) -{ - int ret = 0; - - if (!IS_ERR(phy->refclk)) { - ret = clk_prepare_enable(phy->refclk); - if (ret) { - dev_err(phy->dev, "Failed to enable refclk %d\n", ret); - return ret; - } - } - - if (!IS_ERR(phy->wkupclk)) { - ret = clk_prepare_enable(phy->wkupclk); - if (ret) { - dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); - goto disable_refclk; - } - } - - if (!IS_ERR(phy->div_clk)) { - ret = clk_prepare_enable(phy->div_clk); - if (ret) { - dev_err(phy->dev, "Failed to enable div_clk %d\n", ret); - goto disable_wkupclk; - } - } - - return 0; - -disable_wkupclk: - if (!IS_ERR(phy->wkupclk)) - clk_disable_unprepare(phy->wkupclk); - -disable_refclk: - if (!IS_ERR(phy->refclk)) - clk_disable_unprepare(phy->refclk); - - return ret; -} - -static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy) -{ - if (!IS_ERR(phy->wkupclk)) - clk_disable_unprepare(phy->wkupclk); - if (!IS_ERR(phy->refclk)) { - clk_disable_unprepare(phy->refclk); - /* - * SATA refclk needs an additional disable as we left it - * on in probe to avoid Errata i783 - */ - if (phy->sata_refclk_enabled) { - clk_disable_unprepare(phy->refclk); - phy->sata_refclk_enabled = false; - } - } - - if (!IS_ERR(phy->div_clk)) - clk_disable_unprepare(phy->div_clk); -} - -static const struct of_device_id ti_pipe3_id_table[] = { - { - .compatible = "ti,phy-usb3", - .data = dpll_map_usb, - }, - { - .compatible = "ti,omap-usb3", - .data = dpll_map_usb, - }, - { - .compatible = "ti,phy-pipe3-sata", - .data = dpll_map_sata, - }, - { - .compatible = "ti,phy-pipe3-pcie", - }, - {} -}; -MODULE_DEVICE_TABLE(of, ti_pipe3_id_table); - -static struct platform_driver ti_pipe3_driver = { - .probe = ti_pipe3_probe, - .remove = ti_pipe3_remove, - .driver = { - .name = "ti-pipe3", - .of_match_table = ti_pipe3_id_table, - }, -}; - -module_platform_driver(ti_pipe3_driver); - -MODULE_ALIAS("platform:ti_pipe3"); -MODULE_AUTHOR("Texas Instruments Inc."); -MODULE_DESCRIPTION("TI PIPE3 phy driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-tusb1210.c b/drivers/phy/phy-tusb1210.c deleted file mode 100644 index bb3fb031c478..000000000000 --- a/drivers/phy/phy-tusb1210.c +++ /dev/null @@ -1,146 +0,0 @@ -/** - * tusb1210.c - TUSB1210 USB ULPI PHY driver - * - * Copyright (C) 2015 Intel Corporation - * - * Author: Heikki Krogerus - * - * 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 - -#define TUSB1210_VENDOR_SPECIFIC2 0x80 -#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0 -#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT 4 -#define TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT 6 - -struct tusb1210 { - struct ulpi *ulpi; - struct phy *phy; - struct gpio_desc *gpio_reset; - struct gpio_desc *gpio_cs; - u8 vendor_specific2; -}; - -static int tusb1210_power_on(struct phy *phy) -{ - struct tusb1210 *tusb = phy_get_drvdata(phy); - - gpiod_set_value_cansleep(tusb->gpio_reset, 1); - gpiod_set_value_cansleep(tusb->gpio_cs, 1); - - /* Restore the optional eye diagram optimization value */ - if (tusb->vendor_specific2) - ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2, - tusb->vendor_specific2); - - return 0; -} - -static int tusb1210_power_off(struct phy *phy) -{ - struct tusb1210 *tusb = phy_get_drvdata(phy); - - gpiod_set_value_cansleep(tusb->gpio_reset, 0); - gpiod_set_value_cansleep(tusb->gpio_cs, 0); - - return 0; -} - -static const struct phy_ops phy_ops = { - .power_on = tusb1210_power_on, - .power_off = tusb1210_power_off, - .owner = THIS_MODULE, -}; - -static int tusb1210_probe(struct ulpi *ulpi) -{ - struct tusb1210 *tusb; - u8 val, reg; - - tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL); - if (!tusb) - return -ENOMEM; - - tusb->gpio_reset = devm_gpiod_get_optional(&ulpi->dev, "reset", - GPIOD_OUT_LOW); - if (IS_ERR(tusb->gpio_reset)) - return PTR_ERR(tusb->gpio_reset); - - gpiod_set_value_cansleep(tusb->gpio_reset, 1); - - tusb->gpio_cs = devm_gpiod_get_optional(&ulpi->dev, "cs", - GPIOD_OUT_LOW); - if (IS_ERR(tusb->gpio_cs)) - return PTR_ERR(tusb->gpio_cs); - - gpiod_set_value_cansleep(tusb->gpio_cs, 1); - - /* - * VENDOR_SPECIFIC2 register in TUSB1210 can be used for configuring eye - * diagram optimization and DP/DM swap. - */ - - /* High speed output drive strength configuration */ - device_property_read_u8(&ulpi->dev, "ihstx", &val); - reg = val << TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT; - - /* High speed output impedance configuration */ - device_property_read_u8(&ulpi->dev, "zhsdrv", &val); - reg |= val << TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT; - - /* DP/DM swap control */ - device_property_read_u8(&ulpi->dev, "datapolarity", &val); - reg |= val << TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT; - - if (reg) { - ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg); - tusb->vendor_specific2 = reg; - } - - tusb->phy = ulpi_phy_create(ulpi, &phy_ops); - if (IS_ERR(tusb->phy)) - return PTR_ERR(tusb->phy); - - tusb->ulpi = ulpi; - - phy_set_drvdata(tusb->phy, tusb); - ulpi_set_drvdata(ulpi, tusb); - return 0; -} - -static void tusb1210_remove(struct ulpi *ulpi) -{ - struct tusb1210 *tusb = ulpi_get_drvdata(ulpi); - - ulpi_phy_destroy(ulpi, tusb->phy); -} - -#define TI_VENDOR_ID 0x0451 - -static const struct ulpi_device_id tusb1210_ulpi_id[] = { - { TI_VENDOR_ID, 0x1507, }, - { }, -}; -MODULE_DEVICE_TABLE(ulpi, tusb1210_ulpi_id); - -static struct ulpi_driver tusb1210_driver = { - .id_table = tusb1210_ulpi_id, - .probe = tusb1210_probe, - .remove = tusb1210_remove, - .driver = { - .name = "tusb1210", - .owner = THIS_MODULE, - }, -}; - -module_ulpi_driver(tusb1210_driver); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("TUSB1210 ULPI PHY driver"); diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c deleted file mode 100644 index 2990b3965460..000000000000 --- a/drivers/phy/phy-twl4030-usb.c +++ /dev/null @@ -1,839 +0,0 @@ -/* - * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller - * - * Copyright (C) 2004-2007 Texas Instruments - * Copyright (C) 2008 Nokia Corporation - * Contact: Felipe Balbi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Current status: - * - HS USB ULPI mode works. - * - 3-pin mode support may be added in future. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Register defines */ - -#define MCPC_CTRL 0x30 -#define MCPC_CTRL_RTSOL (1 << 7) -#define MCPC_CTRL_EXTSWR (1 << 6) -#define MCPC_CTRL_EXTSWC (1 << 5) -#define MCPC_CTRL_VOICESW (1 << 4) -#define MCPC_CTRL_OUT64K (1 << 3) -#define MCPC_CTRL_RTSCTSSW (1 << 2) -#define MCPC_CTRL_HS_UART (1 << 0) - -#define MCPC_IO_CTRL 0x33 -#define MCPC_IO_CTRL_MICBIASEN (1 << 5) -#define MCPC_IO_CTRL_CTS_NPU (1 << 4) -#define MCPC_IO_CTRL_RXD_PU (1 << 3) -#define MCPC_IO_CTRL_TXDTYP (1 << 2) -#define MCPC_IO_CTRL_CTSTYP (1 << 1) -#define MCPC_IO_CTRL_RTSTYP (1 << 0) - -#define MCPC_CTRL2 0x36 -#define MCPC_CTRL2_MCPC_CK_EN (1 << 0) - -#define OTHER_FUNC_CTRL 0x80 -#define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4) -#define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2) - -#define OTHER_IFC_CTRL 0x83 -#define OTHER_IFC_CTRL_OE_INT_EN (1 << 6) -#define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5) -#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4) -#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT (1 << 3) -#define OTHER_IFC_CTRL_HIZ_ULPI (1 << 2) -#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0) - -#define OTHER_INT_EN_RISE 0x86 -#define OTHER_INT_EN_FALL 0x89 -#define OTHER_INT_STS 0x8C -#define OTHER_INT_LATCH 0x8D -#define OTHER_INT_VB_SESS_VLD (1 << 7) -#define OTHER_INT_DM_HI (1 << 6) /* not valid for "latch" reg */ -#define OTHER_INT_DP_HI (1 << 5) /* not valid for "latch" reg */ -#define OTHER_INT_BDIS_ACON (1 << 3) /* not valid for "fall" regs */ -#define OTHER_INT_MANU (1 << 1) -#define OTHER_INT_ABNORMAL_STRESS (1 << 0) - -#define ID_STATUS 0x96 -#define ID_RES_FLOAT (1 << 4) -#define ID_RES_440K (1 << 3) -#define ID_RES_200K (1 << 2) -#define ID_RES_102K (1 << 1) -#define ID_RES_GND (1 << 0) - -#define POWER_CTRL 0xAC -#define POWER_CTRL_OTG_ENAB (1 << 5) - -#define OTHER_IFC_CTRL2 0xAF -#define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4) -#define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3) -#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2) -#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK (3 << 0) /* bits 0 and 1 */ -#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N (0 << 0) -#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0) - -#define REG_CTRL_EN 0xB2 -#define REG_CTRL_ERROR 0xB5 -#define ULPI_I2C_CONFLICT_INTEN (1 << 0) - -#define OTHER_FUNC_CTRL2 0xB8 -#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0) - -/* following registers do not have separate _clr and _set registers */ -#define VBUS_DEBOUNCE 0xC0 -#define ID_DEBOUNCE 0xC1 -#define VBAT_TIMER 0xD3 -#define PHY_PWR_CTRL 0xFD -#define PHY_PWR_PHYPWD (1 << 0) -#define PHY_CLK_CTRL 0xFE -#define PHY_CLK_CTRL_CLOCKGATING_EN (1 << 2) -#define PHY_CLK_CTRL_CLK32K_EN (1 << 1) -#define REQ_PHY_DPLL_CLK (1 << 0) -#define PHY_CLK_CTRL_STS 0xFF -#define PHY_DPLL_CLK (1 << 0) - -/* In module TWL_MODULE_PM_MASTER */ -#define STS_HW_CONDITIONS 0x0F - -/* In module TWL_MODULE_PM_RECEIVER */ -#define VUSB_DEDICATED1 0x7D -#define VUSB_DEDICATED2 0x7E -#define VUSB1V5_DEV_GRP 0x71 -#define VUSB1V5_TYPE 0x72 -#define VUSB1V5_REMAP 0x73 -#define VUSB1V8_DEV_GRP 0x74 -#define VUSB1V8_TYPE 0x75 -#define VUSB1V8_REMAP 0x76 -#define VUSB3V1_DEV_GRP 0x77 -#define VUSB3V1_TYPE 0x78 -#define VUSB3V1_REMAP 0x79 - -/* In module TWL4030_MODULE_INTBR */ -#define PMBR1 0x0D -#define GPIO_USB_4PIN_ULPI_2430C (3 << 0) - -/* - * If VBUS is valid or ID is ground, then we know a - * cable is present and we need to be runtime-enabled - */ -static inline bool cable_present(enum musb_vbus_id_status stat) -{ - return stat == MUSB_VBUS_VALID || - stat == MUSB_ID_GROUND; -} - -struct twl4030_usb { - struct usb_phy phy; - struct device *dev; - - /* TWL4030 internal USB regulator supplies */ - struct regulator *usb1v5; - struct regulator *usb1v8; - struct regulator *usb3v1; - - /* for vbus reporting with irqs disabled */ - struct mutex lock; - - /* pin configuration */ - enum twl4030_usb_mode usb_mode; - - int irq; - enum musb_vbus_id_status linkstat; - bool vbus_supplied; - bool musb_mailbox_pending; - - struct delayed_work id_workaround_work; -}; - -/* internal define on top of container_of */ -#define phy_to_twl(x) container_of((x), struct twl4030_usb, phy) - -/*-------------------------------------------------------------------------*/ - -static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl, - u8 module, u8 data, u8 address) -{ - u8 check; - - if ((twl_i2c_write_u8(module, data, address) >= 0) && - (twl_i2c_read_u8(module, &check, address) >= 0) && - (check == data)) - return 0; - dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", - 1, module, address, check, data); - - /* Failed once: Try again */ - if ((twl_i2c_write_u8(module, data, address) >= 0) && - (twl_i2c_read_u8(module, &check, address) >= 0) && - (check == data)) - return 0; - dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", - 2, module, address, check, data); - - /* Failed again: Return error */ - return -EBUSY; -} - -#define twl4030_usb_write_verify(twl, address, data) \ - twl4030_i2c_write_u8_verify(twl, TWL_MODULE_USB, (data), (address)) - -static inline int twl4030_usb_write(struct twl4030_usb *twl, - u8 address, u8 data) -{ - int ret = 0; - - ret = twl_i2c_write_u8(TWL_MODULE_USB, data, address); - if (ret < 0) - dev_dbg(twl->dev, - "TWL4030:USB:Write[0x%x] Error %d\n", address, ret); - return ret; -} - -static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address) -{ - u8 data; - int ret = 0; - - ret = twl_i2c_read_u8(module, &data, address); - if (ret >= 0) - ret = data; - else - dev_dbg(twl->dev, - "TWL4030:readb[0x%x,0x%x] Error %d\n", - module, address, ret); - - return ret; -} - -static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address) -{ - return twl4030_readb(twl, TWL_MODULE_USB, address); -} - -/*-------------------------------------------------------------------------*/ - -static inline int -twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits) -{ - return twl4030_usb_write(twl, ULPI_SET(reg), bits); -} - -static inline int -twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) -{ - return twl4030_usb_write(twl, ULPI_CLR(reg), bits); -} - -/*-------------------------------------------------------------------------*/ - -static bool twl4030_is_driving_vbus(struct twl4030_usb *twl) -{ - int ret; - - ret = twl4030_usb_read(twl, PHY_CLK_CTRL_STS); - if (ret < 0 || !(ret & PHY_DPLL_CLK)) - /* - * if clocks are off, registers are not updated, - * but we can assume we don't drive VBUS in this case - */ - return false; - - ret = twl4030_usb_read(twl, ULPI_OTG_CTRL); - if (ret < 0) - return false; - - return (ret & (ULPI_OTG_DRVVBUS | ULPI_OTG_CHRGVBUS)) ? true : false; -} - -static enum musb_vbus_id_status - twl4030_usb_linkstat(struct twl4030_usb *twl) -{ - int status; - enum musb_vbus_id_status linkstat = MUSB_UNKNOWN; - - twl->vbus_supplied = false; - - /* - * For ID/VBUS sensing, see manual section 15.4.8 ... - * except when using only battery backup power, two - * comparators produce VBUS_PRES and ID_PRES signals, - * which don't match docs elsewhere. But ... BIT(7) - * and BIT(2) of STS_HW_CONDITIONS, respectively, do - * seem to match up. If either is true the USB_PRES - * signal is active, the OTG module is activated, and - * its interrupt may be raised (may wake the system). - */ - status = twl4030_readb(twl, TWL_MODULE_PM_MASTER, STS_HW_CONDITIONS); - if (status < 0) - dev_err(twl->dev, "USB link status err %d\n", status); - else if (status & (BIT(7) | BIT(2))) { - if (status & BIT(7)) { - if (twl4030_is_driving_vbus(twl)) - status &= ~BIT(7); - else - twl->vbus_supplied = true; - } - - if (status & BIT(2)) - linkstat = MUSB_ID_GROUND; - else if (status & BIT(7)) - linkstat = MUSB_VBUS_VALID; - else - linkstat = MUSB_VBUS_OFF; - } else { - if (twl->linkstat != MUSB_UNKNOWN) - linkstat = MUSB_VBUS_OFF; - } - - kobject_uevent(&twl->dev->kobj, linkstat == MUSB_VBUS_VALID - ? KOBJ_ONLINE : KOBJ_OFFLINE); - - dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", - status, status, linkstat); - - /* REVISIT this assumes host and peripheral controllers - * are registered, and that both are active... - */ - - return linkstat; -} - -static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode) -{ - twl->usb_mode = mode; - - switch (mode) { - case T2_USB_MODE_ULPI: - twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL, - ULPI_IFC_CTRL_CARKITMODE); - twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); - twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL, - ULPI_FUNC_CTRL_XCVRSEL_MASK | - ULPI_FUNC_CTRL_OPMODE_MASK); - break; - case -1: - /* FIXME: power on defaults */ - break; - default: - dev_err(twl->dev, "unsupported T2 transceiver mode %d\n", - mode); - break; - } -} - -static void twl4030_i2c_access(struct twl4030_usb *twl, int on) -{ - unsigned long timeout; - int val = twl4030_usb_read(twl, PHY_CLK_CTRL); - - if (val >= 0) { - if (on) { - /* enable DPLL to access PHY registers over I2C */ - val |= REQ_PHY_DPLL_CLK; - WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, - (u8)val) < 0); - - timeout = jiffies + HZ; - while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & - PHY_DPLL_CLK) - && time_before(jiffies, timeout)) - udelay(10); - if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & - PHY_DPLL_CLK)) - dev_err(twl->dev, "Timeout setting T2 HSUSB " - "PHY DPLL clock\n"); - } else { - /* let ULPI control the DPLL clock */ - val &= ~REQ_PHY_DPLL_CLK; - WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, - (u8)val) < 0); - } - } -} - -static void __twl4030_phy_power(struct twl4030_usb *twl, int on) -{ - u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); - - if (on) - pwr &= ~PHY_PWR_PHYPWD; - else - pwr |= PHY_PWR_PHYPWD; - - WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); -} - -static int __maybe_unused twl4030_usb_runtime_suspend(struct device *dev) -{ - struct twl4030_usb *twl = dev_get_drvdata(dev); - - dev_dbg(twl->dev, "%s\n", __func__); - - __twl4030_phy_power(twl, 0); - regulator_disable(twl->usb1v5); - regulator_disable(twl->usb1v8); - regulator_disable(twl->usb3v1); - - return 0; -} - -static int __maybe_unused twl4030_usb_runtime_resume(struct device *dev) -{ - struct twl4030_usb *twl = dev_get_drvdata(dev); - int res; - - dev_dbg(twl->dev, "%s\n", __func__); - - res = regulator_enable(twl->usb3v1); - if (res) - dev_err(twl->dev, "Failed to enable usb3v1\n"); - - res = regulator_enable(twl->usb1v8); - if (res) - dev_err(twl->dev, "Failed to enable usb1v8\n"); - - /* - * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP - * in twl4030) resets the VUSB_DEDICATED2 register. This reset - * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to - * SLEEP. We work around this by clearing the bit after usv3v1 - * is re-activated. This ensures that VUSB3V1 is really active. - */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); - - res = regulator_enable(twl->usb1v5); - if (res) - dev_err(twl->dev, "Failed to enable usb1v5\n"); - - __twl4030_phy_power(twl, 1); - twl4030_usb_write(twl, PHY_CLK_CTRL, - twl4030_usb_read(twl, PHY_CLK_CTRL) | - (PHY_CLK_CTRL_CLOCKGATING_EN | - PHY_CLK_CTRL_CLK32K_EN)); - - twl4030_i2c_access(twl, 1); - twl4030_usb_set_mode(twl, twl->usb_mode); - if (twl->usb_mode == T2_USB_MODE_ULPI) - twl4030_i2c_access(twl, 0); - /* - * According to the TPS65950 TRM, there has to be at least 50ms - * delay between setting POWER_CTRL_OTG_ENAB and enabling charging - * so wait here so that a fully enabled phy can be expected after - * resume - */ - msleep(50); - return 0; -} - -static int twl4030_phy_power_off(struct phy *phy) -{ - struct twl4030_usb *twl = phy_get_drvdata(phy); - - dev_dbg(twl->dev, "%s\n", __func__); - - return 0; -} - -static int twl4030_phy_power_on(struct phy *phy) -{ - struct twl4030_usb *twl = phy_get_drvdata(phy); - - dev_dbg(twl->dev, "%s\n", __func__); - pm_runtime_get_sync(twl->dev); - schedule_delayed_work(&twl->id_workaround_work, HZ); - pm_runtime_mark_last_busy(twl->dev); - pm_runtime_put_autosuspend(twl->dev); - - return 0; -} - -static int twl4030_usb_ldo_init(struct twl4030_usb *twl) -{ - /* Enable writing to power configuration registers */ - twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, - TWL4030_PM_MASTER_PROTECT_KEY); - - twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, - TWL4030_PM_MASTER_PROTECT_KEY); - - /* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/ - /*twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/ - - /* input to VUSB3V1 LDO is from VBAT, not VBUS */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); - - /* Initialize 3.1V regulator */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP); - - twl->usb3v1 = devm_regulator_get(twl->dev, "usb3v1"); - if (IS_ERR(twl->usb3v1)) - return -ENODEV; - - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); - - /* Initialize 1.5V regulator */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP); - - twl->usb1v5 = devm_regulator_get(twl->dev, "usb1v5"); - if (IS_ERR(twl->usb1v5)) - return -ENODEV; - - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); - - /* Initialize 1.8V regulator */ - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP); - - twl->usb1v8 = devm_regulator_get(twl->dev, "usb1v8"); - if (IS_ERR(twl->usb1v8)) - return -ENODEV; - - twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); - - /* disable access to power configuration registers */ - twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, - TWL4030_PM_MASTER_PROTECT_KEY); - - return 0; -} - -static ssize_t twl4030_usb_vbus_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct twl4030_usb *twl = dev_get_drvdata(dev); - int ret = -EINVAL; - - mutex_lock(&twl->lock); - ret = sprintf(buf, "%s\n", - twl->vbus_supplied ? "on" : "off"); - mutex_unlock(&twl->lock); - - return ret; -} -static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); - -static irqreturn_t twl4030_usb_irq(int irq, void *_twl) -{ - struct twl4030_usb *twl = _twl; - enum musb_vbus_id_status status; - bool status_changed = false; - int err; - - status = twl4030_usb_linkstat(twl); - - mutex_lock(&twl->lock); - if (status >= 0 && status != twl->linkstat) { - status_changed = - cable_present(twl->linkstat) != - cable_present(status); - twl->linkstat = status; - } - mutex_unlock(&twl->lock); - - if (status_changed) { - /* FIXME add a set_power() method so that B-devices can - * configure the charger appropriately. It's not always - * correct to consume VBUS power, and how much current to - * consume is a function of the USB configuration chosen - * by the host. - * - * REVISIT usb_gadget_vbus_connect(...) as needed, ditto - * its disconnect() sibling, when changing to/from the - * USB_LINK_VBUS state. musb_hdrc won't care until it - * starts to handle softconnect right. - */ - if (cable_present(status)) { - pm_runtime_get_sync(twl->dev); - } else { - pm_runtime_mark_last_busy(twl->dev); - pm_runtime_put_autosuspend(twl->dev); - } - twl->musb_mailbox_pending = true; - } - if (twl->musb_mailbox_pending) { - err = musb_mailbox(status); - if (!err) - twl->musb_mailbox_pending = false; - } - - /* don't schedule during sleep - irq works right then */ - if (status == MUSB_ID_GROUND && pm_runtime_active(twl->dev)) { - cancel_delayed_work(&twl->id_workaround_work); - schedule_delayed_work(&twl->id_workaround_work, HZ); - } - - if (irq) - sysfs_notify(&twl->dev->kobj, NULL, "vbus"); - - return IRQ_HANDLED; -} - -static void twl4030_id_workaround_work(struct work_struct *work) -{ - struct twl4030_usb *twl = container_of(work, struct twl4030_usb, - id_workaround_work.work); - - twl4030_usb_irq(0, twl); -} - -static int twl4030_phy_init(struct phy *phy) -{ - struct twl4030_usb *twl = phy_get_drvdata(phy); - - pm_runtime_get_sync(twl->dev); - twl->linkstat = MUSB_UNKNOWN; - schedule_delayed_work(&twl->id_workaround_work, HZ); - pm_runtime_mark_last_busy(twl->dev); - pm_runtime_put_autosuspend(twl->dev); - - return 0; -} - -static int twl4030_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - if (!otg) - return -ENODEV; - - otg->gadget = gadget; - if (!gadget) - otg->state = OTG_STATE_UNDEFINED; - - return 0; -} - -static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host) -{ - if (!otg) - return -ENODEV; - - otg->host = host; - if (!host) - otg->state = OTG_STATE_UNDEFINED; - - return 0; -} - -static const struct phy_ops ops = { - .init = twl4030_phy_init, - .power_on = twl4030_phy_power_on, - .power_off = twl4030_phy_power_off, - .owner = THIS_MODULE, -}; - -static const struct dev_pm_ops twl4030_usb_pm_ops = { - SET_RUNTIME_PM_OPS(twl4030_usb_runtime_suspend, - twl4030_usb_runtime_resume, NULL) -}; - -static int twl4030_usb_probe(struct platform_device *pdev) -{ - struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev); - struct twl4030_usb *twl; - struct phy *phy; - int status, err; - struct usb_otg *otg; - struct device_node *np = pdev->dev.of_node; - struct phy_provider *phy_provider; - - twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); - if (!twl) - return -ENOMEM; - - if (np) - of_property_read_u32(np, "usb_mode", - (enum twl4030_usb_mode *)&twl->usb_mode); - else if (pdata) { - twl->usb_mode = pdata->usb_mode; - } else { - dev_err(&pdev->dev, "twl4030 initialized without pdata\n"); - return -EINVAL; - } - - otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); - if (!otg) - return -ENOMEM; - - twl->dev = &pdev->dev; - twl->irq = platform_get_irq(pdev, 0); - twl->vbus_supplied = false; - twl->linkstat = MUSB_UNKNOWN; - twl->musb_mailbox_pending = false; - - twl->phy.dev = twl->dev; - twl->phy.label = "twl4030"; - twl->phy.otg = otg; - twl->phy.type = USB_PHY_TYPE_USB2; - - otg->usb_phy = &twl->phy; - otg->set_host = twl4030_set_host; - otg->set_peripheral = twl4030_set_peripheral; - - phy = devm_phy_create(twl->dev, NULL, &ops); - if (IS_ERR(phy)) { - dev_dbg(&pdev->dev, "Failed to create PHY\n"); - return PTR_ERR(phy); - } - - phy_set_drvdata(phy, twl); - - phy_provider = devm_of_phy_provider_register(twl->dev, - of_phy_simple_xlate); - if (IS_ERR(phy_provider)) - return PTR_ERR(phy_provider); - - /* init mutex for workqueue */ - mutex_init(&twl->lock); - - INIT_DELAYED_WORK(&twl->id_workaround_work, twl4030_id_workaround_work); - - err = twl4030_usb_ldo_init(twl); - if (err) { - dev_err(&pdev->dev, "ldo init failed\n"); - return err; - } - usb_add_phy_dev(&twl->phy); - - platform_set_drvdata(pdev, twl); - if (device_create_file(&pdev->dev, &dev_attr_vbus)) - dev_warn(&pdev->dev, "could not create sysfs file\n"); - - ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier); - - pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); - pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); - - /* Our job is to use irqs and status from the power module - * to keep the transceiver disabled when nothing's connected. - * - * FIXME we actually shouldn't start enabling it until the - * USB controller drivers have said they're ready, by calling - * set_host() and/or set_peripheral() ... OTG_capable boards - * need both handles, otherwise just one suffices. - */ - status = devm_request_threaded_irq(twl->dev, twl->irq, NULL, - twl4030_usb_irq, IRQF_TRIGGER_FALLING | - IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl4030_usb", twl); - if (status < 0) { - dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n", - twl->irq, status); - return status; - } - - if (pdata) - err = phy_create_lookup(phy, "usb", "musb-hdrc.0"); - if (err) - return err; - - pm_runtime_mark_last_busy(&pdev->dev); - pm_runtime_put_autosuspend(twl->dev); - - dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); - return 0; -} - -static int twl4030_usb_remove(struct platform_device *pdev) -{ - struct twl4030_usb *twl = platform_get_drvdata(pdev); - int val; - - usb_remove_phy(&twl->phy); - pm_runtime_get_sync(twl->dev); - cancel_delayed_work(&twl->id_workaround_work); - device_remove_file(twl->dev, &dev_attr_vbus); - - /* set transceiver mode to power on defaults */ - twl4030_usb_set_mode(twl, -1); - - /* idle ulpi before powering off */ - if (cable_present(twl->linkstat)) - pm_runtime_put_noidle(twl->dev); - pm_runtime_mark_last_busy(twl->dev); - pm_runtime_dont_use_autosuspend(&pdev->dev); - pm_runtime_put_sync(twl->dev); - pm_runtime_disable(twl->dev); - - /* autogate 60MHz ULPI clock, - * clear dpll clock request for i2c access, - * disable 32KHz - */ - val = twl4030_usb_read(twl, PHY_CLK_CTRL); - if (val >= 0) { - val |= PHY_CLK_CTRL_CLOCKGATING_EN; - val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK); - twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val); - } - - /* disable complete OTG block */ - twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); - - return 0; -} - -#ifdef CONFIG_OF -static const struct of_device_id twl4030_usb_id_table[] = { - { .compatible = "ti,twl4030-usb" }, - {} -}; -MODULE_DEVICE_TABLE(of, twl4030_usb_id_table); -#endif - -static struct platform_driver twl4030_usb_driver = { - .probe = twl4030_usb_probe, - .remove = twl4030_usb_remove, - .driver = { - .name = "twl4030_usb", - .pm = &twl4030_usb_pm_ops, - .of_match_table = of_match_ptr(twl4030_usb_id_table), - }, -}; - -static int __init twl4030_usb_init(void) -{ - return platform_driver_register(&twl4030_usb_driver); -} -subsys_initcall(twl4030_usb_init); - -static void __exit twl4030_usb_exit(void) -{ - platform_driver_unregister(&twl4030_usb_driver); -} -module_exit(twl4030_usb_exit); - -MODULE_ALIAS("platform:twl4030_usb"); -MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation"); -MODULE_DESCRIPTION("TWL4030 USB transceiver driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig new file mode 100644 index 000000000000..7bfa64baf837 --- /dev/null +++ b/drivers/phy/qualcomm/Kconfig @@ -0,0 +1,58 @@ +# +# Phy drivers for Qualcomm platforms +# +config PHY_QCOM_APQ8064_SATA + tristate "Qualcomm APQ8064 SATA SerDes/PHY driver" + depends on ARCH_QCOM + depends on HAS_IOMEM + depends on OF + select GENERIC_PHY + +config PHY_QCOM_IPQ806X_SATA + tristate "Qualcomm IPQ806x SATA SerDes/PHY driver" + depends on ARCH_QCOM + depends on HAS_IOMEM + depends on OF + select GENERIC_PHY + +config PHY_QCOM_QMP + tristate "Qualcomm QMP PHY Driver" + depends on OF && COMMON_CLK && (ARCH_QCOM || COMPILE_TEST) + select GENERIC_PHY + help + Enable this to support the QMP PHY transceiver that is used + with controllers such as PCIe, UFS, and USB on Qualcomm chips. + +config PHY_QCOM_QUSB2 + tristate "Qualcomm QUSB2 PHY Driver" + depends on OF && (ARCH_QCOM || COMPILE_TEST) + depends on NVMEM || !NVMEM + select GENERIC_PHY + help + Enable this to support the HighSpeed QUSB2 PHY transceiver for USB + controllers on Qualcomm chips. This driver supports the high-speed + PHY which is usually paired with either the ChipIdea or Synopsys DWC3 + USB IPs on MSM SOCs. + +config PHY_QCOM_UFS + tristate "Qualcomm UFS PHY driver" + depends on OF && ARCH_QCOM + select GENERIC_PHY + help + Support for UFS PHY on QCOM chipsets. + +config PHY_QCOM_USB_HS + tristate "Qualcomm USB HS PHY module" + depends on USB_ULPI_BUS + depends on EXTCON || !EXTCON # if EXTCON=m, this cannot be built-in + select GENERIC_PHY + help + Support for the USB high-speed ULPI compliant phy on Qualcomm + chipsets. + +config PHY_QCOM_USB_HSIC + tristate "Qualcomm USB HSIC ULPI PHY module" + depends on USB_ULPI_BUS + select GENERIC_PHY + help + Support for the USB HSIC ULPI compliant PHY on QCOM chipsets. diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile new file mode 100644 index 000000000000..2e183d7695fd --- /dev/null +++ b/drivers/phy/qualcomm/Makefile @@ -0,0 +1,9 @@ +obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o +obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o +obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o +obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o +obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o +obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o +obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o +obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o +obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o diff --git a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c new file mode 100644 index 000000000000..69ce2afac015 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PHY registers */ +#define UNIPHY_PLL_REFCLK_CFG 0x000 +#define UNIPHY_PLL_PWRGEN_CFG 0x014 +#define UNIPHY_PLL_GLB_CFG 0x020 +#define UNIPHY_PLL_SDM_CFG0 0x038 +#define UNIPHY_PLL_SDM_CFG1 0x03C +#define UNIPHY_PLL_SDM_CFG2 0x040 +#define UNIPHY_PLL_SDM_CFG3 0x044 +#define UNIPHY_PLL_SDM_CFG4 0x048 +#define UNIPHY_PLL_SSC_CFG0 0x04C +#define UNIPHY_PLL_SSC_CFG1 0x050 +#define UNIPHY_PLL_SSC_CFG2 0x054 +#define UNIPHY_PLL_SSC_CFG3 0x058 +#define UNIPHY_PLL_LKDET_CFG0 0x05C +#define UNIPHY_PLL_LKDET_CFG1 0x060 +#define UNIPHY_PLL_LKDET_CFG2 0x064 +#define UNIPHY_PLL_CAL_CFG0 0x06C +#define UNIPHY_PLL_CAL_CFG8 0x08C +#define UNIPHY_PLL_CAL_CFG9 0x090 +#define UNIPHY_PLL_CAL_CFG10 0x094 +#define UNIPHY_PLL_CAL_CFG11 0x098 +#define UNIPHY_PLL_STATUS 0x0C0 + +#define SATA_PHY_SER_CTRL 0x100 +#define SATA_PHY_TX_DRIV_CTRL0 0x104 +#define SATA_PHY_TX_DRIV_CTRL1 0x108 +#define SATA_PHY_TX_IMCAL0 0x11C +#define SATA_PHY_TX_IMCAL2 0x124 +#define SATA_PHY_RX_IMCAL0 0x128 +#define SATA_PHY_EQUAL 0x13C +#define SATA_PHY_OOB_TERM 0x144 +#define SATA_PHY_CDR_CTRL0 0x148 +#define SATA_PHY_CDR_CTRL1 0x14C +#define SATA_PHY_CDR_CTRL2 0x150 +#define SATA_PHY_CDR_CTRL3 0x154 +#define SATA_PHY_PI_CTRL0 0x168 +#define SATA_PHY_POW_DWN_CTRL0 0x180 +#define SATA_PHY_POW_DWN_CTRL1 0x184 +#define SATA_PHY_TX_DATA_CTRL 0x188 +#define SATA_PHY_ALIGNP 0x1A4 +#define SATA_PHY_TX_IMCAL_STAT 0x1E4 +#define SATA_PHY_RX_IMCAL_STAT 0x1E8 + +#define UNIPHY_PLL_LOCK BIT(0) +#define SATA_PHY_TX_CAL BIT(0) +#define SATA_PHY_RX_CAL BIT(0) + +/* default timeout set to 1 sec */ +#define TIMEOUT_MS 10000 +#define DELAY_INTERVAL_US 100 + +struct qcom_apq8064_sata_phy { + void __iomem *mmio; + struct clk *cfg_clk; + struct device *dev; +}; + +/* Helper function to do poll and timeout */ +static int read_poll_timeout(void __iomem *addr, u32 mask) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS); + + do { + if (readl_relaxed(addr) & mask) + return 0; + + usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50); + } while (!time_after(jiffies, timeout)); + + return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT; +} + +static int qcom_apq8064_sata_phy_init(struct phy *generic_phy) +{ + struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy); + void __iomem *base = phy->mmio; + int ret = 0; + + /* SATA phy initialization */ + writel_relaxed(0x01, base + SATA_PHY_SER_CTRL); + writel_relaxed(0xB1, base + SATA_PHY_POW_DWN_CTRL0); + /* Make sure the power down happens before power up */ + mb(); + usleep_range(10, 60); + + writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0); + writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1); + writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0); + writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0); + writel_relaxed(0x02, base + SATA_PHY_TX_IMCAL2); + + /* Write UNIPHYPLL registers to configure PLL */ + writel_relaxed(0x04, base + UNIPHY_PLL_REFCLK_CFG); + writel_relaxed(0x00, base + UNIPHY_PLL_PWRGEN_CFG); + + writel_relaxed(0x0A, base + UNIPHY_PLL_CAL_CFG0); + writel_relaxed(0xF3, base + UNIPHY_PLL_CAL_CFG8); + writel_relaxed(0x01, base + UNIPHY_PLL_CAL_CFG9); + writel_relaxed(0xED, base + UNIPHY_PLL_CAL_CFG10); + writel_relaxed(0x02, base + UNIPHY_PLL_CAL_CFG11); + + writel_relaxed(0x36, base + UNIPHY_PLL_SDM_CFG0); + writel_relaxed(0x0D, base + UNIPHY_PLL_SDM_CFG1); + writel_relaxed(0xA3, base + UNIPHY_PLL_SDM_CFG2); + writel_relaxed(0xF0, base + UNIPHY_PLL_SDM_CFG3); + writel_relaxed(0x00, base + UNIPHY_PLL_SDM_CFG4); + + writel_relaxed(0x19, base + UNIPHY_PLL_SSC_CFG0); + writel_relaxed(0xE1, base + UNIPHY_PLL_SSC_CFG1); + writel_relaxed(0x00, base + UNIPHY_PLL_SSC_CFG2); + writel_relaxed(0x11, base + UNIPHY_PLL_SSC_CFG3); + + writel_relaxed(0x04, base + UNIPHY_PLL_LKDET_CFG0); + writel_relaxed(0xFF, base + UNIPHY_PLL_LKDET_CFG1); + + writel_relaxed(0x02, base + UNIPHY_PLL_GLB_CFG); + /* make sure global config LDO power down happens before power up */ + mb(); + + writel_relaxed(0x03, base + UNIPHY_PLL_GLB_CFG); + writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2); + + /* PLL Lock wait */ + ret = read_poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK); + if (ret) { + dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n"); + return ret; + } + + /* TX Calibration */ + ret = read_poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL); + if (ret) { + dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n"); + return ret; + } + + /* RX Calibration */ + ret = read_poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL); + if (ret) { + dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n"); + return ret; + } + + /* SATA phy calibrated succesfully, power up to functional mode */ + writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1); + writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0); + writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0); + + writel_relaxed(0x00, base + SATA_PHY_POW_DWN_CTRL1); + writel_relaxed(0x59, base + SATA_PHY_CDR_CTRL0); + writel_relaxed(0x04, base + SATA_PHY_CDR_CTRL1); + writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL2); + writel_relaxed(0x00, base + SATA_PHY_PI_CTRL0); + writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL3); + writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0); + + writel_relaxed(0x11, base + SATA_PHY_TX_DATA_CTRL); + writel_relaxed(0x43, base + SATA_PHY_ALIGNP); + writel_relaxed(0x04, base + SATA_PHY_OOB_TERM); + + writel_relaxed(0x01, base + SATA_PHY_EQUAL); + writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL0); + writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL1); + + return 0; +} + +static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy) +{ + struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy); + void __iomem *base = phy->mmio; + + /* Power down PHY */ + writel_relaxed(0xF8, base + SATA_PHY_POW_DWN_CTRL0); + writel_relaxed(0xFE, base + SATA_PHY_POW_DWN_CTRL1); + + /* Power down PLL block */ + writel_relaxed(0x00, base + UNIPHY_PLL_GLB_CFG); + + return 0; +} + +static const struct phy_ops qcom_apq8064_sata_phy_ops = { + .init = qcom_apq8064_sata_phy_init, + .exit = qcom_apq8064_sata_phy_exit, + .owner = THIS_MODULE, +}; + +static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev) +{ + struct qcom_apq8064_sata_phy *phy; + struct device *dev = &pdev->dev; + struct resource *res; + struct phy_provider *phy_provider; + struct phy *generic_phy; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(phy->mmio)) + return PTR_ERR(phy->mmio); + + generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "%s: failed to create phy\n", __func__); + return PTR_ERR(generic_phy); + } + + phy->dev = dev; + phy_set_drvdata(generic_phy, phy); + platform_set_drvdata(pdev, phy); + + phy->cfg_clk = devm_clk_get(dev, "cfg"); + if (IS_ERR(phy->cfg_clk)) { + dev_err(dev, "Failed to get sata cfg clock\n"); + return PTR_ERR(phy->cfg_clk); + } + + ret = clk_prepare_enable(phy->cfg_clk); + if (ret) + return ret; + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + clk_disable_unprepare(phy->cfg_clk); + dev_err(dev, "%s: failed to register phy\n", __func__); + return PTR_ERR(phy_provider); + } + + return 0; +} + +static int qcom_apq8064_sata_phy_remove(struct platform_device *pdev) +{ + struct qcom_apq8064_sata_phy *phy = platform_get_drvdata(pdev); + + clk_disable_unprepare(phy->cfg_clk); + + return 0; +} + +static const struct of_device_id qcom_apq8064_sata_phy_of_match[] = { + { .compatible = "qcom,apq8064-sata-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcom_apq8064_sata_phy_of_match); + +static struct platform_driver qcom_apq8064_sata_phy_driver = { + .probe = qcom_apq8064_sata_phy_probe, + .remove = qcom_apq8064_sata_phy_remove, + .driver = { + .name = "qcom-apq8064-sata-phy", + .of_match_table = qcom_apq8064_sata_phy_of_match, + } +}; +module_platform_driver(qcom_apq8064_sata_phy_driver); + +MODULE_DESCRIPTION("QCOM apq8064 SATA PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c new file mode 100644 index 000000000000..0ad127cc9298 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct qcom_ipq806x_sata_phy { + void __iomem *mmio; + struct clk *cfg_clk; + struct device *dev; +}; + +#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b)) + +#define SATA_PHY_P0_PARAM0 0x200 +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0) +#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0) + +#define SATA_PHY_P0_PARAM1 0x204 +#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0) +#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0) + +#define SATA_PHY_P0_PARAM2 0x208 +#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18) +#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18) + +#define SATA_PHY_P0_PARAM3 0x20C +#define SATA_PHY_SSC_EN 0x8 +#define SATA_PHY_P0_PARAM4 0x210 +#define SATA_PHY_REF_SSP_EN 0x2 +#define SATA_PHY_RESET 0x1 + +static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy) +{ + struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); + u32 reg; + + /* Setting SSC_EN to 1 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3); + reg = reg | SATA_PHY_SSC_EN; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3); + + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) & + ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK | + SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK | + SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK); + reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf); + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0); + + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) & + ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK | + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK | + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK); + reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) | + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) | + SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55); + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1); + + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) & + ~SATA_PHY_P0_PARAM2_RX_EQ_MASK; + reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3); + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2); + + /* Setting PHY_RESET to 1 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); + reg = reg | SATA_PHY_RESET; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); + + /* Setting REF_SSP_EN to 1 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); + reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); + + /* make sure all changes complete before we let the PHY out of reset */ + mb(); + + /* sleep for max. 50us more to combine processor wakeups */ + usleep_range(20, 20 + 50); + + /* Clearing PHY_RESET to 0 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); + reg = reg & ~SATA_PHY_RESET; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); + + return 0; +} + +static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy) +{ + struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy); + u32 reg; + + /* Setting PHY_RESET to 1 */ + reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4); + reg = reg | SATA_PHY_RESET; + writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4); + + return 0; +} + +static const struct phy_ops qcom_ipq806x_sata_phy_ops = { + .init = qcom_ipq806x_sata_phy_init, + .exit = qcom_ipq806x_sata_phy_exit, + .owner = THIS_MODULE, +}; + +static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev) +{ + struct qcom_ipq806x_sata_phy *phy; + struct device *dev = &pdev->dev; + struct resource *res; + struct phy_provider *phy_provider; + struct phy *generic_phy; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR(phy->mmio)) + return PTR_ERR(phy->mmio); + + generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "%s: failed to create phy\n", __func__); + return PTR_ERR(generic_phy); + } + + phy->dev = dev; + phy_set_drvdata(generic_phy, phy); + platform_set_drvdata(pdev, phy); + + phy->cfg_clk = devm_clk_get(dev, "cfg"); + if (IS_ERR(phy->cfg_clk)) { + dev_err(dev, "Failed to get sata cfg clock\n"); + return PTR_ERR(phy->cfg_clk); + } + + ret = clk_prepare_enable(phy->cfg_clk); + if (ret) + return ret; + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + clk_disable_unprepare(phy->cfg_clk); + dev_err(dev, "%s: failed to register phy\n", __func__); + return PTR_ERR(phy_provider); + } + + return 0; +} + +static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev) +{ + struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev); + + clk_disable_unprepare(phy->cfg_clk); + + return 0; +} + +static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = { + { .compatible = "qcom,ipq806x-sata-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match); + +static struct platform_driver qcom_ipq806x_sata_phy_driver = { + .probe = qcom_ipq806x_sata_phy_probe, + .remove = qcom_ipq806x_sata_phy_remove, + .driver = { + .name = "qcom-ipq806x-sata-phy", + .of_match_table = qcom_ipq806x_sata_phy_of_match, + } +}; +module_platform_driver(qcom_ipq806x_sata_phy_driver); + +MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c new file mode 100644 index 000000000000..78ca62897784 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -0,0 +1,1153 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* QMP PHY QSERDES COM registers */ +#define QSERDES_COM_BG_TIMER 0x00c +#define QSERDES_COM_SSC_EN_CENTER 0x010 +#define QSERDES_COM_SSC_ADJ_PER1 0x014 +#define QSERDES_COM_SSC_ADJ_PER2 0x018 +#define QSERDES_COM_SSC_PER1 0x01c +#define QSERDES_COM_SSC_PER2 0x020 +#define QSERDES_COM_SSC_STEP_SIZE1 0x024 +#define QSERDES_COM_SSC_STEP_SIZE2 0x028 +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x034 +#define QSERDES_COM_CLK_ENABLE1 0x038 +#define QSERDES_COM_SYS_CLK_CTRL 0x03c +#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x040 +#define QSERDES_COM_PLL_IVCO 0x048 +#define QSERDES_COM_LOCK_CMP1_MODE0 0x04c +#define QSERDES_COM_LOCK_CMP2_MODE0 0x050 +#define QSERDES_COM_LOCK_CMP3_MODE0 0x054 +#define QSERDES_COM_LOCK_CMP1_MODE1 0x058 +#define QSERDES_COM_LOCK_CMP2_MODE1 0x05c +#define QSERDES_COM_LOCK_CMP3_MODE1 0x060 +#define QSERDES_COM_BG_TRIM 0x070 +#define QSERDES_COM_CLK_EP_DIV 0x074 +#define QSERDES_COM_CP_CTRL_MODE0 0x078 +#define QSERDES_COM_CP_CTRL_MODE1 0x07c +#define QSERDES_COM_PLL_RCTRL_MODE0 0x084 +#define QSERDES_COM_PLL_RCTRL_MODE1 0x088 +#define QSERDES_COM_PLL_CCTRL_MODE0 0x090 +#define QSERDES_COM_PLL_CCTRL_MODE1 0x094 +#define QSERDES_COM_SYSCLK_EN_SEL 0x0ac +#define QSERDES_COM_RESETSM_CNTRL 0x0b4 +#define QSERDES_COM_RESTRIM_CTRL 0x0bc +#define QSERDES_COM_RESCODE_DIV_NUM 0x0c4 +#define QSERDES_COM_LOCK_CMP_EN 0x0c8 +#define QSERDES_COM_LOCK_CMP_CFG 0x0cc +#define QSERDES_COM_DEC_START_MODE0 0x0d0 +#define QSERDES_COM_DEC_START_MODE1 0x0d4 +#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x0dc +#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x0e0 +#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x0e4 +#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0e8 +#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0ec +#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0f0 +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108 +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10c +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110 +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x114 +#define QSERDES_COM_VCO_TUNE_CTRL 0x124 +#define QSERDES_COM_VCO_TUNE_MAP 0x128 +#define QSERDES_COM_VCO_TUNE1_MODE0 0x12c +#define QSERDES_COM_VCO_TUNE2_MODE0 0x130 +#define QSERDES_COM_VCO_TUNE1_MODE1 0x134 +#define QSERDES_COM_VCO_TUNE2_MODE1 0x138 +#define QSERDES_COM_VCO_TUNE_TIMER1 0x144 +#define QSERDES_COM_VCO_TUNE_TIMER2 0x148 +#define QSERDES_COM_BG_CTRL 0x170 +#define QSERDES_COM_CLK_SELECT 0x174 +#define QSERDES_COM_HSCLK_SEL 0x178 +#define QSERDES_COM_CORECLK_DIV 0x184 +#define QSERDES_COM_CORE_CLK_EN 0x18c +#define QSERDES_COM_C_READY_STATUS 0x190 +#define QSERDES_COM_CMN_CONFIG 0x194 +#define QSERDES_COM_SVS_MODE_CLK_SEL 0x19c +#define QSERDES_COM_DEBUG_BUS0 0x1a0 +#define QSERDES_COM_DEBUG_BUS1 0x1a4 +#define QSERDES_COM_DEBUG_BUS2 0x1a8 +#define QSERDES_COM_DEBUG_BUS3 0x1ac +#define QSERDES_COM_DEBUG_BUS_SEL 0x1b0 +#define QSERDES_COM_CORECLK_DIV_MODE1 0x1bc + +/* QMP PHY TX registers */ +#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x054 +#define QSERDES_TX_DEBUG_BUS_SEL 0x064 +#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x068 +#define QSERDES_TX_LANE_MODE 0x094 +#define QSERDES_TX_RCV_DETECT_LVL_2 0x0ac + +/* QMP PHY RX registers */ +#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x010 +#define QSERDES_RX_UCDR_SO_GAIN 0x01c +#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN 0x040 +#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x048 +#define QSERDES_RX_RX_TERM_BW 0x090 +#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x0c4 +#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x0c8 +#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x0cc +#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x0d0 +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x0d8 +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x0dc +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x0e0 +#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x108 +#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x10c +#define QSERDES_RX_SIGDET_ENABLES 0x110 +#define QSERDES_RX_SIGDET_CNTRL 0x114 +#define QSERDES_RX_SIGDET_LVL 0x118 +#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x11c +#define QSERDES_RX_RX_BAND 0x120 +#define QSERDES_RX_RX_INTERFACE_MODE 0x12c + +/* QMP PHY PCS registers */ +#define QPHY_POWER_DOWN_CONTROL 0x04 +#define QPHY_TXDEEMPH_M6DB_V0 0x24 +#define QPHY_TXDEEMPH_M3P5DB_V0 0x28 +#define QPHY_ENDPOINT_REFCLK_DRIVE 0x54 +#define QPHY_RX_IDLE_DTCT_CNTRL 0x58 +#define QPHY_POWER_STATE_CONFIG1 0x60 +#define QPHY_POWER_STATE_CONFIG2 0x64 +#define QPHY_POWER_STATE_CONFIG4 0x6c +#define QPHY_LOCK_DETECT_CONFIG1 0x80 +#define QPHY_LOCK_DETECT_CONFIG2 0x84 +#define QPHY_LOCK_DETECT_CONFIG3 0x88 +#define QPHY_PWRUP_RESET_DLY_TIME_AUXCLK 0xa0 +#define QPHY_LP_WAKEUP_DLY_TIME_AUXCLK 0xa4 + +/* QPHY_SW_RESET bit */ +#define SW_RESET BIT(0) +/* QPHY_POWER_DOWN_CONTROL */ +#define SW_PWRDN BIT(0) +#define REFCLK_DRV_DSBL BIT(1) +/* QPHY_START_CONTROL bits */ +#define SERDES_START BIT(0) +#define PCS_START BIT(1) +#define PLL_READY_GATE_EN BIT(3) +/* QPHY_PCS_STATUS bit */ +#define PHYSTATUS BIT(6) +/* QPHY_COM_PCS_READY_STATUS bit */ +#define PCS_READY BIT(0) + +#define PHY_INIT_COMPLETE_TIMEOUT 1000 +#define POWER_DOWN_DELAY_US_MIN 10 +#define POWER_DOWN_DELAY_US_MAX 11 + +#define MAX_PROP_NAME 32 + +struct qmp_phy_init_tbl { + unsigned int offset; + unsigned int val; + /* + * register part of layout ? + * if yes, then offset gives index in the reg-layout + */ + int in_layout; +}; + +#define QMP_PHY_INIT_CFG(o, v) \ + { \ + .offset = o, \ + .val = v, \ + } + +#define QMP_PHY_INIT_CFG_L(o, v) \ + { \ + .offset = o, \ + .val = v, \ + .in_layout = 1, \ + } + +/* set of registers with offsets different per-PHY */ +enum qphy_reg_layout { + /* Common block control registers */ + QPHY_COM_SW_RESET, + QPHY_COM_POWER_DOWN_CONTROL, + QPHY_COM_START_CONTROL, + QPHY_COM_PCS_READY_STATUS, + /* PCS registers */ + QPHY_PLL_LOCK_CHK_DLY_TIME, + QPHY_FLL_CNTRL1, + QPHY_FLL_CNTRL2, + QPHY_FLL_CNT_VAL_L, + QPHY_FLL_CNT_VAL_H_TOL, + QPHY_FLL_MAN_CODE, + QPHY_SW_RESET, + QPHY_START_CTRL, + QPHY_PCS_READY_STATUS, +}; + +static const unsigned int pciephy_regs_layout[] = { + [QPHY_COM_SW_RESET] = 0x400, + [QPHY_COM_POWER_DOWN_CONTROL] = 0x404, + [QPHY_COM_START_CONTROL] = 0x408, + [QPHY_COM_PCS_READY_STATUS] = 0x448, + [QPHY_PLL_LOCK_CHK_DLY_TIME] = 0xa8, + [QPHY_FLL_CNTRL1] = 0xc4, + [QPHY_FLL_CNTRL2] = 0xc8, + [QPHY_FLL_CNT_VAL_L] = 0xcc, + [QPHY_FLL_CNT_VAL_H_TOL] = 0xd0, + [QPHY_FLL_MAN_CODE] = 0xd4, + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_READY_STATUS] = 0x174, +}; + +static const unsigned int usb3phy_regs_layout[] = { + [QPHY_FLL_CNTRL1] = 0xc0, + [QPHY_FLL_CNTRL2] = 0xc4, + [QPHY_FLL_CNT_VAL_L] = 0xc8, + [QPHY_FLL_CNT_VAL_H_TOL] = 0xcc, + [QPHY_FLL_MAN_CODE] = 0xd0, + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x08, + [QPHY_PCS_READY_STATUS] = 0x17c, +}; + +static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09), + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), + QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19), + QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40), +}; + +static const struct qmp_phy_init_tbl msm8996_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), +}; + +static const struct qmp_phy_init_tbl msm8996_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19), +}; + +static const struct qmp_phy_init_tbl msm8996_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_RX_IDLE_DTCT_CNTRL, 0x4c), + QMP_PHY_INIT_CFG(QPHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x00), + QMP_PHY_INIT_CFG(QPHY_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01), + + QMP_PHY_INIT_CFG_L(QPHY_PLL_LOCK_CHK_DLY_TIME, 0x05), + + QMP_PHY_INIT_CFG(QPHY_ENDPOINT_REFCLK_DRIVE, 0x05), + QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x02), + QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG4, 0x00), + QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG1, 0xa3), + QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M3P5DB_V0, 0x0e), +}; + +static const struct qmp_phy_init_tbl msm8996_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x04), + /* PLL and Loop filter settings */ + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), + /* SSC settings */ + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x07), +}; + +static const struct qmp_phy_init_tbl msm8996_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), +}; + +static const struct qmp_phy_init_tbl msm8996_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xbb), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x18), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16), +}; + +static const struct qmp_phy_init_tbl msm8996_usb3_pcs_tbl[] = { + /* FLL settings */ + QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL2, 0x03), + QMP_PHY_INIT_CFG_L(QPHY_FLL_CNTRL1, 0x02), + QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG_L(QPHY_FLL_CNT_VAL_H_TOL, 0x42), + QMP_PHY_INIT_CFG_L(QPHY_FLL_MAN_CODE, 0x85), + + /* Lock Det settings */ + QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG1, 0xd1), + QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG2, 0x1f), + QMP_PHY_INIT_CFG(QPHY_LOCK_DETECT_CONFIG3, 0x47), + QMP_PHY_INIT_CFG(QPHY_POWER_STATE_CONFIG2, 0x08), +}; + +/* struct qmp_phy_cfg - per-PHY initialization config */ +struct qmp_phy_cfg { + /* phy-type - PCIE/UFS/USB */ + unsigned int type; + /* number of lanes provided by phy */ + int nlanes; + + /* Init sequence for PHY blocks - serdes, tx, rx, pcs */ + const struct qmp_phy_init_tbl *serdes_tbl; + int serdes_tbl_num; + const struct qmp_phy_init_tbl *tx_tbl; + int tx_tbl_num; + const struct qmp_phy_init_tbl *rx_tbl; + int rx_tbl_num; + const struct qmp_phy_init_tbl *pcs_tbl; + int pcs_tbl_num; + + /* clock ids to be requested */ + const char * const *clk_list; + int num_clks; + /* resets to be requested */ + const char * const *reset_list; + int num_resets; + /* regulators to be requested */ + const char * const *vreg_list; + int num_vregs; + + /* array of registers with different offsets */ + const unsigned int *regs; + + unsigned int start_ctrl; + unsigned int pwrdn_ctrl; + unsigned int mask_pcs_ready; + unsigned int mask_com_pcs_ready; + + /* true, if PHY has a separate PHY_COM control block */ + bool has_phy_com_ctrl; + /* true, if PHY has a reset for individual lanes */ + bool has_lane_rst; + /* true, if PHY needs delay after POWER_DOWN */ + bool has_pwrdn_delay; + /* power_down delay in usec */ + int pwrdn_delay_min; + int pwrdn_delay_max; +}; + +/** + * struct qmp_phy - per-lane phy descriptor + * + * @phy: generic phy + * @tx: iomapped memory space for lane's tx + * @rx: iomapped memory space for lane's rx + * @pcs: iomapped memory space for lane's pcs + * @pipe_clk: pipe lock + * @index: lane index + * @qmp: QMP phy to which this lane belongs + * @lane_rst: lane's reset controller + */ +struct qmp_phy { + struct phy *phy; + void __iomem *tx; + void __iomem *rx; + void __iomem *pcs; + struct clk *pipe_clk; + unsigned int index; + struct qcom_qmp *qmp; + struct reset_control *lane_rst; +}; + +/** + * struct qcom_qmp - structure holding QMP phy block attributes + * + * @dev: device + * @serdes: iomapped memory space for phy's serdes + * + * @clks: array of clocks required by phy + * @resets: array of resets required by phy + * @vregs: regulator supplies bulk data + * + * @cfg: phy specific configuration + * @phys: array of per-lane phy descriptors + * @phy_mutex: mutex lock for PHY common block initialization + * @init_count: phy common block initialization count + */ +struct qcom_qmp { + struct device *dev; + void __iomem *serdes; + + struct clk **clks; + struct reset_control **resets; + struct regulator_bulk_data *vregs; + + const struct qmp_phy_cfg *cfg; + struct qmp_phy **phys; + + struct mutex phy_mutex; + int init_count; +}; + +static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg |= val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg &= ~val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +/* list of clocks required by phy */ +static const char * const msm8996_phy_clk_l[] = { + "aux", "cfg_ahb", "ref", +}; + +/* list of resets */ +static const char * const msm8996_pciephy_reset_l[] = { + "phy", "common", "cfg", +}; + +static const char * const msm8996_usb3phy_reset_l[] = { + "phy", "common", +}; + +/* list of regulators */ +static const char * const msm8996_phy_vreg_l[] = { + "vdda-phy", "vdda-pll", +}; + +static const struct qmp_phy_cfg msm8996_pciephy_cfg = { + .type = PHY_TYPE_PCIE, + .nlanes = 3, + + .serdes_tbl = msm8996_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(msm8996_pcie_serdes_tbl), + .tx_tbl = msm8996_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(msm8996_pcie_tx_tbl), + .rx_tbl = msm8996_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(msm8996_pcie_rx_tbl), + .pcs_tbl = msm8996_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(msm8996_pcie_pcs_tbl), + .clk_list = msm8996_phy_clk_l, + .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), + .reset_list = msm8996_pciephy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_pciephy_reset_l), + .vreg_list = msm8996_phy_vreg_l, + .num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l), + .regs = pciephy_regs_layout, + + .start_ctrl = PCS_START | PLL_READY_GATE_EN, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .mask_com_pcs_ready = PCS_READY, + + .has_phy_com_ctrl = true, + .has_lane_rst = true, + .has_pwrdn_delay = true, + .pwrdn_delay_min = POWER_DOWN_DELAY_US_MIN, + .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, +}; + +static const struct qmp_phy_cfg msm8996_usb3phy_cfg = { + .type = PHY_TYPE_USB3, + .nlanes = 1, + + .serdes_tbl = msm8996_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(msm8996_usb3_serdes_tbl), + .tx_tbl = msm8996_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(msm8996_usb3_tx_tbl), + .rx_tbl = msm8996_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(msm8996_usb3_rx_tbl), + .pcs_tbl = msm8996_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(msm8996_usb3_pcs_tbl), + .clk_list = msm8996_phy_clk_l, + .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = msm8996_phy_vreg_l, + .num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l), + .regs = usb3phy_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN, + .mask_pcs_ready = PHYSTATUS, +}; + +static void qcom_qmp_phy_configure(void __iomem *base, + const unsigned int *regs, + const struct qmp_phy_init_tbl tbl[], + int num) +{ + int i; + const struct qmp_phy_init_tbl *t = tbl; + + if (!t) + return; + + for (i = 0; i < num; i++, t++) { + if (t->in_layout) + writel(t->val, base + regs[t->offset]); + else + writel(t->val, base + t->offset); + } +} + +static int qcom_qmp_phy_poweron(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + int num = qmp->cfg->num_vregs; + int ret; + + dev_vdbg(&phy->dev, "Powering on QMP phy\n"); + + /* turn on regulator supplies */ + ret = regulator_bulk_enable(num, qmp->vregs); + if (ret) { + dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); + return ret; + } + + ret = clk_prepare_enable(qphy->pipe_clk); + if (ret) { + dev_err(qmp->dev, "pipe_clk enable failed, err=%d\n", ret); + regulator_bulk_disable(num, qmp->vregs); + return ret; + } + + return 0; +} + +static int qcom_qmp_phy_poweroff(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + + clk_disable_unprepare(qphy->pipe_clk); + + regulator_bulk_disable(qmp->cfg->num_vregs, qmp->vregs); + + return 0; +} + +static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + void __iomem *serdes = qmp->serdes; + int ret, i; + + mutex_lock(&qmp->phy_mutex); + if (qmp->init_count++) { + mutex_unlock(&qmp->phy_mutex); + return 0; + } + + for (i = 0; i < cfg->num_resets; i++) { + ret = reset_control_deassert(qmp->resets[i]); + if (ret) { + dev_err(qmp->dev, "%s reset deassert failed\n", + qmp->cfg->reset_list[i]); + while (--i >= 0) + reset_control_assert(qmp->resets[i]); + goto err_rst; + } + } + + if (cfg->has_phy_com_ctrl) + qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], + SW_PWRDN); + + /* Serdes configuration */ + qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl, + cfg->serdes_tbl_num); + + if (cfg->has_phy_com_ctrl) { + void __iomem *status; + unsigned int mask, val; + + qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET); + qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], + SERDES_START | PCS_START); + + status = serdes + cfg->regs[QPHY_COM_PCS_READY_STATUS]; + mask = cfg->mask_com_pcs_ready; + + ret = readl_poll_timeout(status, val, (val & mask), 10, + PHY_INIT_COMPLETE_TIMEOUT); + if (ret) { + dev_err(qmp->dev, + "phy common block init timed-out\n"); + goto err_com_init; + } + } + + mutex_unlock(&qmp->phy_mutex); + + return 0; + +err_com_init: + while (--i >= 0) + reset_control_assert(qmp->resets[i]); +err_rst: + mutex_unlock(&qmp->phy_mutex); + return ret; +} + +static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + void __iomem *serdes = qmp->serdes; + int i = cfg->num_resets; + + mutex_lock(&qmp->phy_mutex); + if (--qmp->init_count) { + mutex_unlock(&qmp->phy_mutex); + return 0; + } + + if (cfg->has_phy_com_ctrl) { + qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], + SERDES_START | PCS_START); + qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], + SW_RESET); + qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], + SW_PWRDN); + } + + while (--i >= 0) + reset_control_assert(qmp->resets[i]); + + mutex_unlock(&qmp->phy_mutex); + + return 0; +} + +/* PHY Initialization */ +static int qcom_qmp_phy_init(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qmp->cfg; + void __iomem *tx = qphy->tx; + void __iomem *rx = qphy->rx; + void __iomem *pcs = qphy->pcs; + void __iomem *status; + unsigned int mask, val; + int ret, i; + + dev_vdbg(qmp->dev, "Initializing QMP phy\n"); + + for (i = 0; i < qmp->cfg->num_clks; i++) { + ret = clk_prepare_enable(qmp->clks[i]); + if (ret) { + dev_err(qmp->dev, "failed to enable %s clk, err=%d\n", + qmp->cfg->clk_list[i], ret); + while (--i >= 0) + clk_disable_unprepare(qmp->clks[i]); + } + } + + ret = qcom_qmp_phy_com_init(qmp); + if (ret) + goto err_com_init; + + if (cfg->has_lane_rst) { + ret = reset_control_deassert(qphy->lane_rst); + if (ret) { + dev_err(qmp->dev, "lane%d reset deassert failed\n", + qphy->index); + goto err_lane_rst; + } + } + + /* Tx, Rx, and PCS configurations */ + qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num); + qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num); + qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num); + + /* + * Pull out PHY from POWER DOWN state. + * This is active low enable signal to power-down PHY. + */ + qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); + + if (cfg->has_pwrdn_delay) + usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max); + + /* start SerDes and Phy-Coding-Sublayer */ + qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); + + /* Pull PHY out of reset state */ + qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + + status = pcs + cfg->regs[QPHY_PCS_READY_STATUS]; + mask = cfg->mask_pcs_ready; + + ret = readl_poll_timeout(status, val, !(val & mask), 1, + PHY_INIT_COMPLETE_TIMEOUT); + if (ret) { + dev_err(qmp->dev, "phy initialization timed-out\n"); + goto err_pcs_ready; + } + + return ret; + +err_pcs_ready: + if (cfg->has_lane_rst) + reset_control_assert(qphy->lane_rst); +err_lane_rst: + qcom_qmp_phy_com_exit(qmp); +err_com_init: + while (--i >= 0) + clk_disable_unprepare(qmp->clks[i]); + + return ret; +} + +static int qcom_qmp_phy_exit(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qmp->cfg; + int i = cfg->num_clks; + + /* PHY reset */ + qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + + /* stop SerDes and Phy-Coding-Sublayer */ + qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl); + + /* Put PHY into POWER DOWN state: active low */ + qphy_clrbits(qphy->pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl); + + if (cfg->has_lane_rst) + reset_control_assert(qphy->lane_rst); + + qcom_qmp_phy_com_exit(qmp); + + while (--i >= 0) + clk_disable_unprepare(qmp->clks[i]); + + return 0; +} + +static int qcom_qmp_phy_vreg_init(struct device *dev) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int num = qmp->cfg->num_vregs; + int i; + + qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); + if (!qmp->vregs) + return -ENOMEM; + + for (i = 0; i < num; i++) + qmp->vregs[i].supply = qmp->cfg->vreg_list[i]; + + return devm_regulator_bulk_get(dev, num, qmp->vregs); +} + +static int qcom_qmp_phy_reset_init(struct device *dev) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int i; + + qmp->resets = devm_kcalloc(dev, qmp->cfg->num_resets, + sizeof(*qmp->resets), GFP_KERNEL); + if (!qmp->resets) + return -ENOMEM; + + for (i = 0; i < qmp->cfg->num_resets; i++) { + struct reset_control *rst; + const char *name = qmp->cfg->reset_list[i]; + + rst = devm_reset_control_get(dev, name); + if (IS_ERR(rst)) { + dev_err(dev, "failed to get %s reset\n", name); + return PTR_ERR(rst); + } + qmp->resets[i] = rst; + } + + return 0; +} + +static int qcom_qmp_phy_clk_init(struct device *dev) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + int ret, i; + + qmp->clks = devm_kcalloc(dev, qmp->cfg->num_clks, + sizeof(*qmp->clks), GFP_KERNEL); + if (!qmp->clks) + return -ENOMEM; + + for (i = 0; i < qmp->cfg->num_clks; i++) { + struct clk *_clk; + const char *name = qmp->cfg->clk_list[i]; + + _clk = devm_clk_get(dev, name); + if (IS_ERR(_clk)) { + ret = PTR_ERR(_clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get %s clk, %d\n", + name, ret); + return ret; + } + qmp->clks[i] = _clk; + } + + return 0; +} + +/* + * Register a fixed rate pipe clock. + * + * The _pipe_clksrc generated by PHY goes to the GCC that gate + * controls it. The _pipe_clk coming out of the GCC is requested + * by the PHY driver for its operations. + * We register the _pipe_clksrc here. The gcc driver takes care + * of assigning this _pipe_clksrc as parent to _pipe_clk. + * Below picture shows this relationship. + * + * +---------------+ + * | PHY block |<<---------------------------------------+ + * | | | + * | +-------+ | +-----+ | + * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+ + * clk | +-------+ | +-----+ + * +---------------+ + */ +static int phy_pipe_clk_register(struct qcom_qmp *qmp, int id) +{ + char name[24]; + struct clk_fixed_rate *fixed; + struct clk_init_data init = { }; + + switch (qmp->cfg->type) { + case PHY_TYPE_USB3: + snprintf(name, sizeof(name), "usb3_phy_pipe_clk_src"); + break; + case PHY_TYPE_PCIE: + snprintf(name, sizeof(name), "pcie_%d_pipe_clk_src", id); + break; + default: + /* not all phys register pipe clocks, so return success */ + return 0; + } + + fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL); + if (!fixed) + return -ENOMEM; + + init.name = name; + init.ops = &clk_fixed_rate_ops; + + /* controllers using QMP phys use 125MHz pipe clock interface */ + fixed->fixed_rate = 125000000; + fixed->hw.init = &init; + + return devm_clk_hw_register(qmp->dev, &fixed->hw); +} + +static const struct phy_ops qcom_qmp_phy_gen_ops = { + .init = qcom_qmp_phy_init, + .exit = qcom_qmp_phy_exit, + .power_on = qcom_qmp_phy_poweron, + .power_off = qcom_qmp_phy_poweroff, + .owner = THIS_MODULE, +}; + +static +int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) +{ + struct qcom_qmp *qmp = dev_get_drvdata(dev); + struct phy *generic_phy; + struct qmp_phy *qphy; + char prop_name[MAX_PROP_NAME]; + int ret; + + qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); + if (!qphy) + return -ENOMEM; + + /* + * Get memory resources for each phy lane: + * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. + */ + qphy->tx = of_iomap(np, 0); + if (!qphy->tx) + return -ENOMEM; + + qphy->rx = of_iomap(np, 1); + if (!qphy->rx) + return -ENOMEM; + + qphy->pcs = of_iomap(np, 2); + if (!qphy->pcs) + return -ENOMEM; + + /* + * Get PHY's Pipe clock, if any. USB3 and PCIe are PIPE3 + * based phys, so they essentially have pipe clock. So, + * we return error in case phy is USB3 or PIPE type. + * Otherwise, we initialize pipe clock to NULL for + * all phys that don't need this. + */ + snprintf(prop_name, sizeof(prop_name), "pipe%d", id); + qphy->pipe_clk = of_clk_get_by_name(np, prop_name); + if (IS_ERR(qphy->pipe_clk)) { + if (qmp->cfg->type == PHY_TYPE_PCIE || + qmp->cfg->type == PHY_TYPE_USB3) { + ret = PTR_ERR(qphy->pipe_clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, + "failed to get lane%d pipe_clk, %d\n", + id, ret); + return ret; + } + qphy->pipe_clk = NULL; + } + + /* Get lane reset, if any */ + if (qmp->cfg->has_lane_rst) { + snprintf(prop_name, sizeof(prop_name), "lane%d", id); + qphy->lane_rst = of_reset_control_get(np, prop_name); + if (IS_ERR(qphy->lane_rst)) { + dev_err(dev, "failed to get lane%d reset\n", id); + return PTR_ERR(qphy->lane_rst); + } + } + + generic_phy = devm_phy_create(dev, np, &qcom_qmp_phy_gen_ops); + if (IS_ERR(generic_phy)) { + ret = PTR_ERR(generic_phy); + dev_err(dev, "failed to create qphy %d\n", ret); + return ret; + } + + qphy->phy = generic_phy; + qphy->index = id; + qphy->qmp = qmp; + qmp->phys[id] = qphy; + phy_set_drvdata(generic_phy, qphy); + + return 0; +} + +static const struct of_device_id qcom_qmp_phy_of_match_table[] = { + { + .compatible = "qcom,msm8996-qmp-pcie-phy", + .data = &msm8996_pciephy_cfg, + }, { + .compatible = "qcom,msm8996-qmp-usb3-phy", + .data = &msm8996_usb3phy_cfg, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table); + +static int qcom_qmp_phy_probe(struct platform_device *pdev) +{ + struct qcom_qmp *qmp; + struct device *dev = &pdev->dev; + struct resource *res; + struct device_node *child; + struct phy_provider *phy_provider; + void __iomem *base; + int num, id; + int ret; + + qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL); + if (!qmp) + return -ENOMEM; + + qmp->dev = dev; + dev_set_drvdata(dev, qmp); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + /* per PHY serdes; usually located at base address */ + qmp->serdes = base; + + mutex_init(&qmp->phy_mutex); + + /* Get the specific init parameters of QMP phy */ + qmp->cfg = of_device_get_match_data(dev); + + ret = qcom_qmp_phy_clk_init(dev); + if (ret) + return ret; + + ret = qcom_qmp_phy_reset_init(dev); + if (ret) + return ret; + + ret = qcom_qmp_phy_vreg_init(dev); + if (ret) { + dev_err(dev, "failed to get regulator supplies\n"); + return ret; + } + + num = of_get_available_child_count(dev->of_node); + /* do we have a rogue child node ? */ + if (num > qmp->cfg->nlanes) + return -EINVAL; + + qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL); + if (!qmp->phys) + return -ENOMEM; + + id = 0; + for_each_available_child_of_node(dev->of_node, child) { + /* Create per-lane phy */ + ret = qcom_qmp_phy_create(dev, child, id); + if (ret) { + dev_err(dev, "failed to create lane%d phy, %d\n", + id, ret); + return ret; + } + + /* + * Register the pipe clock provided by phy. + * See function description to see details of this pipe clock. + */ + ret = phy_pipe_clk_register(qmp, id); + if (ret) { + dev_err(qmp->dev, + "failed to register pipe clock source\n"); + return ret; + } + id++; + } + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (!IS_ERR(phy_provider)) + dev_info(dev, "Registered Qcom-QMP phy\n"); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver qcom_qmp_phy_driver = { + .probe = qcom_qmp_phy_probe, + .driver = { + .name = "qcom-qmp-phy", + .of_match_table = qcom_qmp_phy_of_match_table, + }, +}; + +module_platform_driver(qcom_qmp_phy_driver); + +MODULE_AUTHOR("Vivek Gautam "); +MODULE_DESCRIPTION("Qualcomm QMP PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c new file mode 100644 index 000000000000..6c575244c0fb --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -0,0 +1,493 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QUSB2PHY_PLL_TEST 0x04 +#define CLK_REF_SEL BIT(7) + +#define QUSB2PHY_PLL_TUNE 0x08 +#define QUSB2PHY_PLL_USER_CTL1 0x0c +#define QUSB2PHY_PLL_USER_CTL2 0x10 +#define QUSB2PHY_PLL_AUTOPGM_CTL1 0x1c +#define QUSB2PHY_PLL_PWR_CTRL 0x18 + +#define QUSB2PHY_PLL_STATUS 0x38 +#define PLL_LOCKED BIT(5) + +#define QUSB2PHY_PORT_TUNE1 0x80 +#define QUSB2PHY_PORT_TUNE2 0x84 +#define QUSB2PHY_PORT_TUNE3 0x88 +#define QUSB2PHY_PORT_TUNE4 0x8c +#define QUSB2PHY_PORT_TUNE5 0x90 +#define QUSB2PHY_PORT_TEST2 0x9c + +#define QUSB2PHY_PORT_POWERDOWN 0xb4 +#define CLAMP_N_EN BIT(5) +#define FREEZIO_N BIT(1) +#define POWER_DOWN BIT(0) + +#define QUSB2PHY_REFCLK_ENABLE BIT(0) + +#define PHY_CLK_SCHEME_SEL BIT(0) + +struct qusb2_phy_init_tbl { + unsigned int offset; + unsigned int val; +}; + +#define QUSB2_PHY_INIT_CFG(o, v) \ + { \ + .offset = o, \ + .val = v, \ + } + +static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = { + QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE1, 0xf8), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE2, 0xb3), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE3, 0x83), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TUNE4, 0xc0), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_TUNE, 0x30), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL1, 0x79), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_USER_CTL2, 0x21), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PORT_TEST2, 0x14), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_AUTOPGM_CTL1, 0x9f), + QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_PWR_CTRL, 0x00), +}; + +struct qusb2_phy_cfg { + const struct qusb2_phy_init_tbl *tbl; + /* number of entries in the table */ + unsigned int tbl_num; + /* offset to PHY_CLK_SCHEME register in TCSR map */ + unsigned int clk_scheme_offset; +}; + +static const struct qusb2_phy_cfg msm8996_phy_cfg = { + .tbl = msm8996_init_tbl, + .tbl_num = ARRAY_SIZE(msm8996_init_tbl), +}; + +static const char * const qusb2_phy_vreg_names[] = { + "vdda-pll", "vdda-phy-dpdm", +}; + +#define QUSB2_NUM_VREGS ARRAY_SIZE(qusb2_phy_vreg_names) + +/** + * struct qusb2_phy - structure holding qusb2 phy attributes + * + * @phy: generic phy + * @base: iomapped memory space for qubs2 phy + * + * @cfg_ahb_clk: AHB2PHY interface clock + * @ref_clk: phy reference clock + * @iface_clk: phy interface clock + * @phy_reset: phy reset control + * @vregs: regulator supplies bulk data + * + * @tcsr: TCSR syscon register map + * @cell: nvmem cell containing phy tuning value + * + * @cfg: phy config data + * @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme + */ +struct qusb2_phy { + struct phy *phy; + void __iomem *base; + + struct clk *cfg_ahb_clk; + struct clk *ref_clk; + struct clk *iface_clk; + struct reset_control *phy_reset; + struct regulator_bulk_data vregs[QUSB2_NUM_VREGS]; + + struct regmap *tcsr; + struct nvmem_cell *cell; + + const struct qusb2_phy_cfg *cfg; + bool has_se_clk_scheme; +}; + +static inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg |= val; + writel(reg, base + offset); + + /* Ensure above write is completed */ + readl(base + offset); +} + +static inline void qusb2_clrbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg &= ~val; + writel(reg, base + offset); + + /* Ensure above write is completed */ + readl(base + offset); +} + +static inline +void qcom_qusb2_phy_configure(void __iomem *base, + const struct qusb2_phy_init_tbl tbl[], int num) +{ + int i; + + for (i = 0; i < num; i++) + writel(tbl[i].val, base + tbl[i].offset); +} + +/* + * Fetches HS Tx tuning value from nvmem and sets the + * QUSB2PHY_PORT_TUNE2 register. + * For error case, skip setting the value and use the default value. + */ +static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy) +{ + struct device *dev = &qphy->phy->dev; + u8 *val; + + /* + * Read efuse register having TUNE2 parameter's high nibble. + * If efuse register shows value as 0x0, or if we fail to find + * a valid efuse register settings, then use default value + * as 0xB for high nibble that we have already set while + * configuring phy. + */ + val = nvmem_cell_read(qphy->cell, NULL); + if (IS_ERR(val) || !val[0]) { + dev_dbg(dev, "failed to read a valid hs-tx trim value\n"); + return; + } + + /* Fused TUNE2 value is the higher nibble only */ + qusb2_setbits(qphy->base, QUSB2PHY_PORT_TUNE2, val[0] << 0x4); +} + +static int qusb2_phy_poweron(struct phy *phy) +{ + struct qusb2_phy *qphy = phy_get_drvdata(phy); + int num = ARRAY_SIZE(qphy->vregs); + int ret; + + dev_vdbg(&phy->dev, "%s(): Powering-on QUSB2 phy\n", __func__); + + /* turn on regulator supplies */ + ret = regulator_bulk_enable(num, qphy->vregs); + if (ret) + return ret; + + ret = clk_prepare_enable(qphy->iface_clk); + if (ret) { + dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret); + regulator_bulk_disable(num, qphy->vregs); + return ret; + } + + return 0; +} + +static int qusb2_phy_poweroff(struct phy *phy) +{ + struct qusb2_phy *qphy = phy_get_drvdata(phy); + + clk_disable_unprepare(qphy->iface_clk); + + regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs); + + return 0; +} + +static int qusb2_phy_init(struct phy *phy) +{ + struct qusb2_phy *qphy = phy_get_drvdata(phy); + unsigned int val; + unsigned int clk_scheme; + int ret; + + dev_vdbg(&phy->dev, "%s(): Initializing QUSB2 phy\n", __func__); + + /* enable ahb interface clock to program phy */ + ret = clk_prepare_enable(qphy->cfg_ahb_clk); + if (ret) { + dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret); + return ret; + } + + /* Perform phy reset */ + ret = reset_control_assert(qphy->phy_reset); + if (ret) { + dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret); + goto disable_ahb_clk; + } + + /* 100 us delay to keep PHY in reset mode */ + usleep_range(100, 150); + + ret = reset_control_deassert(qphy->phy_reset); + if (ret) { + dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret); + goto disable_ahb_clk; + } + + /* Disable the PHY */ + qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, + CLAMP_N_EN | FREEZIO_N | POWER_DOWN); + + /* save reset value to override reference clock scheme later */ + val = readl(qphy->base + QUSB2PHY_PLL_TEST); + + qcom_qusb2_phy_configure(qphy->base, qphy->cfg->tbl, + qphy->cfg->tbl_num); + + /* Set efuse value for tuning the PHY */ + qusb2_phy_set_tune2_param(qphy); + + /* Enable the PHY */ + qusb2_clrbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, POWER_DOWN); + + /* Required to get phy pll lock successfully */ + usleep_range(150, 160); + + /* Default is single-ended clock on msm8996 */ + qphy->has_se_clk_scheme = true; + /* + * read TCSR_PHY_CLK_SCHEME register to check if single-ended + * clock scheme is selected. If yes, then disable differential + * ref_clk and use single-ended clock, otherwise use differential + * ref_clk only. + */ + if (qphy->tcsr) { + ret = regmap_read(qphy->tcsr, qphy->cfg->clk_scheme_offset, + &clk_scheme); + if (ret) { + dev_err(&phy->dev, "failed to read clk scheme reg\n"); + goto assert_phy_reset; + } + + /* is it a differential clock scheme ? */ + if (!(clk_scheme & PHY_CLK_SCHEME_SEL)) { + dev_vdbg(&phy->dev, "%s(): select differential clk\n", + __func__); + qphy->has_se_clk_scheme = false; + } else { + dev_vdbg(&phy->dev, "%s(): select single-ended clk\n", + __func__); + } + } + + if (!qphy->has_se_clk_scheme) { + val &= ~CLK_REF_SEL; + ret = clk_prepare_enable(qphy->ref_clk); + if (ret) { + dev_err(&phy->dev, "failed to enable ref clk, %d\n", + ret); + goto assert_phy_reset; + } + } else { + val |= CLK_REF_SEL; + } + + writel(val, qphy->base + QUSB2PHY_PLL_TEST); + + /* ensure above write is through */ + readl(qphy->base + QUSB2PHY_PLL_TEST); + + /* Required to get phy pll lock successfully */ + usleep_range(100, 110); + + val = readb(qphy->base + QUSB2PHY_PLL_STATUS); + if (!(val & PLL_LOCKED)) { + dev_err(&phy->dev, + "QUSB2PHY pll lock failed: status reg = %x\n", val); + ret = -EBUSY; + goto disable_ref_clk; + } + + return 0; + +disable_ref_clk: + if (!qphy->has_se_clk_scheme) + clk_disable_unprepare(qphy->ref_clk); +assert_phy_reset: + reset_control_assert(qphy->phy_reset); +disable_ahb_clk: + clk_disable_unprepare(qphy->cfg_ahb_clk); + return ret; +} + +static int qusb2_phy_exit(struct phy *phy) +{ + struct qusb2_phy *qphy = phy_get_drvdata(phy); + + /* Disable the PHY */ + qusb2_setbits(qphy->base, QUSB2PHY_PORT_POWERDOWN, + CLAMP_N_EN | FREEZIO_N | POWER_DOWN); + + if (!qphy->has_se_clk_scheme) + clk_disable_unprepare(qphy->ref_clk); + + reset_control_assert(qphy->phy_reset); + + clk_disable_unprepare(qphy->cfg_ahb_clk); + + return 0; +} + +static const struct phy_ops qusb2_phy_gen_ops = { + .init = qusb2_phy_init, + .exit = qusb2_phy_exit, + .power_on = qusb2_phy_poweron, + .power_off = qusb2_phy_poweroff, + .owner = THIS_MODULE, +}; + +static const struct of_device_id qusb2_phy_of_match_table[] = { + { + .compatible = "qcom,msm8996-qusb2-phy", + .data = &msm8996_phy_cfg, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table); + +static int qusb2_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct qusb2_phy *qphy; + struct phy_provider *phy_provider; + struct phy *generic_phy; + struct resource *res; + int ret, i; + int num; + + qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); + if (!qphy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + qphy->base = devm_ioremap_resource(dev, res); + if (IS_ERR(qphy->base)) + return PTR_ERR(qphy->base); + + qphy->cfg_ahb_clk = devm_clk_get(dev, "cfg_ahb"); + if (IS_ERR(qphy->cfg_ahb_clk)) { + ret = PTR_ERR(qphy->cfg_ahb_clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get cfg ahb clk, %d\n", ret); + return ret; + } + + qphy->ref_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(qphy->ref_clk)) { + ret = PTR_ERR(qphy->ref_clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get ref clk, %d\n", ret); + return ret; + } + + qphy->iface_clk = devm_clk_get(dev, "iface"); + if (IS_ERR(qphy->iface_clk)) { + ret = PTR_ERR(qphy->iface_clk); + if (ret == -EPROBE_DEFER) + return ret; + qphy->iface_clk = NULL; + dev_dbg(dev, "failed to get iface clk, %d\n", ret); + } + + qphy->phy_reset = devm_reset_control_get_by_index(&pdev->dev, 0); + if (IS_ERR(qphy->phy_reset)) { + dev_err(dev, "failed to get phy core reset\n"); + return PTR_ERR(qphy->phy_reset); + } + + num = ARRAY_SIZE(qphy->vregs); + for (i = 0; i < num; i++) + qphy->vregs[i].supply = qusb2_phy_vreg_names[i]; + + ret = devm_regulator_bulk_get(dev, num, qphy->vregs); + if (ret) { + dev_err(dev, "failed to get regulator supplies\n"); + return ret; + } + + /* Get the specific init parameters of QMP phy */ + qphy->cfg = of_device_get_match_data(dev); + + qphy->tcsr = syscon_regmap_lookup_by_phandle(dev->of_node, + "qcom,tcsr-syscon"); + if (IS_ERR(qphy->tcsr)) { + dev_dbg(dev, "failed to lookup TCSR regmap\n"); + qphy->tcsr = NULL; + } + + qphy->cell = devm_nvmem_cell_get(dev, NULL); + if (IS_ERR(qphy->cell)) { + if (PTR_ERR(qphy->cell) == -EPROBE_DEFER) + return -EPROBE_DEFER; + qphy->cell = NULL; + dev_dbg(dev, "failed to lookup tune2 hstx trim value\n"); + } + + generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops); + if (IS_ERR(generic_phy)) { + ret = PTR_ERR(generic_phy); + dev_err(dev, "failed to create phy, %d\n", ret); + return ret; + } + qphy->phy = generic_phy; + + dev_set_drvdata(dev, qphy); + phy_set_drvdata(generic_phy, qphy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (!IS_ERR(phy_provider)) + dev_info(dev, "Registered Qcom-QUSB2 phy\n"); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver qusb2_phy_driver = { + .probe = qusb2_phy_probe, + .driver = { + .name = "qcom-qusb2-phy", + .of_match_table = qusb2_phy_of_match_table, + }, +}; + +module_platform_driver(qusb2_phy_driver); + +MODULE_AUTHOR("Vivek Gautam "); +MODULE_DESCRIPTION("Qualcomm QUSB2 PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h new file mode 100644 index 000000000000..13b02b7de30b --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ufs-i.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef UFS_QCOM_PHY_I_H_ +#define UFS_QCOM_PHY_I_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ +({ \ + ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \ + might_sleep_if(timeout_us); \ + for (;;) { \ + (val) = readl(addr); \ + if (cond) \ + break; \ + if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \ + (val) = readl(addr); \ + break; \ + } \ + if (sleep_us) \ + usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \ + } \ + (cond) ? 0 : -ETIMEDOUT; \ +}) + +#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \ + { \ + .reg_offset = reg, \ + .cfg_value = val, \ + } + +#define UFS_QCOM_PHY_NAME_LEN 30 + +enum { + MASK_SERDES_START = 0x1, + MASK_PCS_READY = 0x1, +}; + +enum { + OFFSET_SERDES_START = 0x0, +}; + +struct ufs_qcom_phy_stored_attributes { + u32 att; + u32 value; +}; + + +struct ufs_qcom_phy_calibration { + u32 reg_offset; + u32 cfg_value; +}; + +struct ufs_qcom_phy_vreg { + const char *name; + struct regulator *reg; + int max_uA; + int min_uV; + int max_uV; + bool enabled; +}; + +struct ufs_qcom_phy { + struct list_head list; + struct device *dev; + void __iomem *mmio; + void __iomem *dev_ref_clk_ctrl_mmio; + struct clk *tx_iface_clk; + struct clk *rx_iface_clk; + bool is_iface_clk_enabled; + struct clk *ref_clk_src; + struct clk *ref_clk_parent; + struct clk *ref_clk; + bool is_ref_clk_enabled; + bool is_dev_ref_clk_enabled; + struct ufs_qcom_phy_vreg vdda_pll; + struct ufs_qcom_phy_vreg vdda_phy; + struct ufs_qcom_phy_vreg vddp_ref_clk; + unsigned int quirks; + + /* + * If UFS link is put into Hibern8 and if UFS PHY analog hardware is + * power collapsed (by clearing UFS_PHY_POWER_DOWN_CONTROL), Hibern8 + * exit might fail even after powering on UFS PHY analog hardware. + * Enabling this quirk will help to solve above issue by doing + * custom PHY settings just before PHY analog power collapse. + */ + #define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE BIT(0) + + u8 host_ctrl_rev_major; + u16 host_ctrl_rev_minor; + u16 host_ctrl_rev_step; + + char name[UFS_QCOM_PHY_NAME_LEN]; + struct ufs_qcom_phy_calibration *cached_regs; + int cached_regs_table_size; + bool is_powered_on; + struct ufs_qcom_phy_specific_ops *phy_spec_ops; +}; + +/** + * struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a + * specific implementation per phy. Each UFS phy, should implement + * those functions according to its spec and requirements + * @calibrate_phy: pointer to a function that calibrate the phy + * @start_serdes: pointer to a function that starts the serdes + * @is_physical_coding_sublayer_ready: pointer to a function that + * checks pcs readiness. returns 0 for success and non-zero for error. + * @set_tx_lane_enable: pointer to a function that enable tx lanes + * @power_control: pointer to a function that controls analog rail of phy + * and writes to QSERDES_RX_SIGDET_CNTRL attribute + */ +struct ufs_qcom_phy_specific_ops { + int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B); + void (*start_serdes)(struct ufs_qcom_phy *phy); + int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy); + void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val); + void (*power_control)(struct ufs_qcom_phy *phy, bool val); +}; + +struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy); +int ufs_qcom_phy_power_on(struct phy *generic_phy); +int ufs_qcom_phy_power_off(struct phy *generic_phy); +int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common); +int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common); +int ufs_qcom_phy_remove(struct phy *generic_phy, + struct ufs_qcom_phy *ufs_qcom_phy); +struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, + struct ufs_qcom_phy *common_cfg, + const struct phy_ops *ufs_qcom_phy_gen_ops, + struct ufs_qcom_phy_specific_ops *phy_spec_ops); +int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, + struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A, + struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B, + bool is_rate_B); +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c new file mode 100644 index 000000000000..12a1b498dc4b --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "phy-qcom-ufs-qmp-14nm.h" + +#define UFS_PHY_NAME "ufs_phy_qmp_14nm" +#define UFS_PHY_VDDA_PHY_UV (925000) + +static +int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, + bool is_rate_B) +{ + int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A); + int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); + int err; + + err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A, + tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B); + + if (err) + dev_err(ufs_qcom_phy->dev, + "%s: ufs_qcom_phy_calibrate() failed %d\n", + __func__, err); + return err; +} + +static +void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common) +{ + phy_common->quirks = + UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; +} + +static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy) +{ + return 0; +} + +static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy) +{ + return 0; +} + +static +void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val) +{ + writel_relaxed(val ? 0x1 : 0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); + /* + * Before any transactions involving PHY, ensure PHY knows + * that it's analog rail is powered ON (or OFF). + */ + mb(); +} + +static inline +void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) +{ + /* + * 14nm PHY does not have TX_LANE_ENABLE register. + * Implement this function so as not to propagate error to caller. + */ +} + +static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy *phy) +{ + u32 tmp; + + tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); + tmp &= ~MASK_SERDES_START; + tmp |= (1 << OFFSET_SERDES_START); + writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); + /* Ensure register value is committed */ + mb(); +} + +static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) +{ + int err = 0; + u32 val; + + err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, + val, (val & MASK_PCS_READY), 10, 1000000); + if (err) + dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", + __func__, err); + return err; +} + +static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { + .init = ufs_qcom_phy_qmp_14nm_init, + .exit = ufs_qcom_phy_qmp_14nm_exit, + .power_on = ufs_qcom_phy_power_on, + .power_off = ufs_qcom_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct ufs_qcom_phy_specific_ops phy_14nm_ops = { + .calibrate_phy = ufs_qcom_phy_qmp_14nm_phy_calibrate, + .start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes, + .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready, + .set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable, + .power_control = ufs_qcom_phy_qmp_14nm_power_control, +}; + +static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy *generic_phy; + struct ufs_qcom_phy_qmp_14nm *phy; + struct ufs_qcom_phy *phy_common; + int err = 0; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + err = -ENOMEM; + goto out; + } + phy_common = &phy->common_cfg; + + generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common, + &ufs_qcom_phy_qmp_14nm_phy_ops, &phy_14nm_ops); + + if (!generic_phy) { + err = -EIO; + goto out; + } + + err = ufs_qcom_phy_init_clks(phy_common); + if (err) + goto out; + + err = ufs_qcom_phy_init_vregulators(phy_common); + if (err) + goto out; + + phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV; + phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV; + + ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common); + + phy_set_drvdata(generic_phy, phy); + + strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name)); + +out: + return err; +} + +static const struct of_device_id ufs_qcom_phy_qmp_14nm_of_match[] = { + {.compatible = "qcom,ufs-phy-qmp-14nm"}, + {.compatible = "qcom,msm8996-ufs-phy-qmp-14nm"}, + {}, +}; +MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_14nm_of_match); + +static struct platform_driver ufs_qcom_phy_qmp_14nm_driver = { + .probe = ufs_qcom_phy_qmp_14nm_probe, + .driver = { + .of_match_table = ufs_qcom_phy_qmp_14nm_of_match, + .name = "ufs_qcom_phy_qmp_14nm", + }, +}; + +module_platform_driver(ufs_qcom_phy_qmp_14nm_driver); + +MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 14nm"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h new file mode 100644 index 000000000000..3aefdbacbcd0 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef UFS_QCOM_PHY_QMP_14NM_H_ +#define UFS_QCOM_PHY_QMP_14NM_H_ + +#include "phy-qcom-ufs-i.h" + +/* QCOM UFS PHY control registers */ +#define COM_OFF(x) (0x000 + x) +#define PHY_OFF(x) (0xC00 + x) +#define TX_OFF(n, x) (0x400 + (0x400 * n) + x) +#define RX_OFF(n, x) (0x600 + (0x400 * n) + x) + +/* UFS PHY QSERDES COM registers */ +#define QSERDES_COM_BG_TIMER COM_OFF(0x0C) +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34) +#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C) +#define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0x4C) +#define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0x50) +#define QSERDES_COM_LOCK_CMP3_MODE0 COM_OFF(0x54) +#define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0x58) +#define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0x5C) +#define QSERDES_COM_LOCK_CMP3_MODE1 COM_OFF(0x60) +#define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x78) +#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x7C) +#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x84) +#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x88) +#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x90) +#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x94) +#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0xAC) +#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0xB4) +#define QSERDES_COM_LOCK_CMP_EN COM_OFF(0xC8) +#define QSERDES_COM_LOCK_CMP_CFG COM_OFF(0xCC) +#define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xD0) +#define QSERDES_COM_DEC_START_MODE1 COM_OFF(0xD4) +#define QSERDES_COM_DIV_FRAC_START1_MODE0 COM_OFF(0xDC) +#define QSERDES_COM_DIV_FRAC_START2_MODE0 COM_OFF(0xE0) +#define QSERDES_COM_DIV_FRAC_START3_MODE0 COM_OFF(0xE4) +#define QSERDES_COM_DIV_FRAC_START1_MODE1 COM_OFF(0xE8) +#define QSERDES_COM_DIV_FRAC_START2_MODE1 COM_OFF(0xEC) +#define QSERDES_COM_DIV_FRAC_START3_MODE1 COM_OFF(0xF0) +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 COM_OFF(0x108) +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 COM_OFF(0x10C) +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 COM_OFF(0x110) +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 COM_OFF(0x114) +#define QSERDES_COM_VCO_TUNE_CTRL COM_OFF(0x124) +#define QSERDES_COM_VCO_TUNE_MAP COM_OFF(0x128) +#define QSERDES_COM_VCO_TUNE1_MODE0 COM_OFF(0x12C) +#define QSERDES_COM_VCO_TUNE2_MODE0 COM_OFF(0x130) +#define QSERDES_COM_VCO_TUNE1_MODE1 COM_OFF(0x134) +#define QSERDES_COM_VCO_TUNE2_MODE1 COM_OFF(0x138) +#define QSERDES_COM_VCO_TUNE_TIMER1 COM_OFF(0x144) +#define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x148) +#define QSERDES_COM_CLK_SELECT COM_OFF(0x174) +#define QSERDES_COM_HSCLK_SEL COM_OFF(0x178) +#define QSERDES_COM_CORECLK_DIV COM_OFF(0x184) +#define QSERDES_COM_CORE_CLK_EN COM_OFF(0x18C) +#define QSERDES_COM_CMN_CONFIG COM_OFF(0x194) +#define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x19C) +#define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x1BC) + +/* UFS PHY registers */ +#define UFS_PHY_PHY_START PHY_OFF(0x00) +#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04) +#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x168) + +/* UFS PHY TX registers */ +#define QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN TX_OFF(0, 0x68) +#define QSERDES_TX_LANE_MODE TX_OFF(0, 0x94) + +/* UFS PHY RX registers */ +#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN RX_OFF(0, 0x40) +#define QSERDES_RX_RX_TERM_BW RX_OFF(0, 0x90) +#define QSERDES_RX_RX_EQ_GAIN1_LSB RX_OFF(0, 0xC4) +#define QSERDES_RX_RX_EQ_GAIN1_MSB RX_OFF(0, 0xC8) +#define QSERDES_RX_RX_EQ_GAIN2_LSB RX_OFF(0, 0xCC) +#define QSERDES_RX_RX_EQ_GAIN2_MSB RX_OFF(0, 0xD0) +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0, 0xD8) +#define QSERDES_RX_SIGDET_CNTRL RX_OFF(0, 0x114) +#define QSERDES_RX_SIGDET_LVL RX_OFF(0, 0x118) +#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x11C) +#define QSERDES_RX_RX_INTERFACE_MODE RX_OFF(0, 0x12C) + +/* + * This structure represents the 14nm specific phy. + * common_cfg MUST remain the first field in this structure + * in case extra fields are added. This way, when calling + * get_ufs_qcom_phy() of generic phy, we can extract the + * common phy structure (struct ufs_qcom_phy) out of it + * regardless of the relevant specific phy. + */ +struct ufs_qcom_phy_qmp_14nm { + struct ufs_qcom_phy common_cfg; +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = { + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xd7), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x06), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x05), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x10), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x14), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00), + + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x02), + + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E), +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x54), +}; + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c new file mode 100644 index 000000000000..4f68acb58b73 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "phy-qcom-ufs-qmp-20nm.h" + +#define UFS_PHY_NAME "ufs_phy_qmp_20nm" + +static +int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, + bool is_rate_B) +{ + struct ufs_qcom_phy_calibration *tbl_A, *tbl_B; + int tbl_size_A, tbl_size_B; + u8 major = ufs_qcom_phy->host_ctrl_rev_major; + u16 minor = ufs_qcom_phy->host_ctrl_rev_minor; + u16 step = ufs_qcom_phy->host_ctrl_rev_step; + int err; + + if ((major == 0x1) && (minor == 0x002) && (step == 0x0000)) { + tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_2_0); + tbl_A = phy_cal_table_rate_A_1_2_0; + } else if ((major == 0x1) && (minor == 0x003) && (step == 0x0000)) { + tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_3_0); + tbl_A = phy_cal_table_rate_A_1_3_0; + } else { + dev_err(ufs_qcom_phy->dev, "%s: Unknown UFS-PHY version, no calibration values\n", + __func__); + err = -ENODEV; + goto out; + } + + tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B); + tbl_B = phy_cal_table_rate_B; + + err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A, + tbl_B, tbl_size_B, is_rate_B); + + if (err) + dev_err(ufs_qcom_phy->dev, "%s: ufs_qcom_phy_calibrate() failed %d\n", + __func__, err); + +out: + return err; +} + +static +void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common) +{ + phy_common->quirks = + UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; +} + +static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy) +{ + return 0; +} + +static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy) +{ + return 0; +} + +static +void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy *phy, bool val) +{ + bool hibern8_exit_after_pwr_collapse = phy->quirks & + UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE; + + if (val) { + writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); + /* + * Before any transactions involving PHY, ensure PHY knows + * that it's analog rail is powered ON. + */ + mb(); + + if (hibern8_exit_after_pwr_collapse) { + /* + * Give atleast 1us delay after restoring PHY analog + * power. + */ + usleep_range(1, 2); + writel_relaxed(0x0A, phy->mmio + + QSERDES_COM_SYSCLK_EN_SEL_TXBAND); + writel_relaxed(0x08, phy->mmio + + QSERDES_COM_SYSCLK_EN_SEL_TXBAND); + /* + * Make sure workaround is deactivated before proceeding + * with normal PHY operations. + */ + mb(); + } + } else { + if (hibern8_exit_after_pwr_collapse) { + writel_relaxed(0x0A, phy->mmio + + QSERDES_COM_SYSCLK_EN_SEL_TXBAND); + writel_relaxed(0x02, phy->mmio + + QSERDES_COM_SYSCLK_EN_SEL_TXBAND); + /* + * Make sure that above workaround is activated before + * PHY analog power collapse. + */ + mb(); + } + + writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL); + /* + * ensure that PHY knows its PHY analog rail is going + * to be powered down + */ + mb(); + } +} + +static +void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val) +{ + writel_relaxed(val & UFS_PHY_TX_LANE_ENABLE_MASK, + phy->mmio + UFS_PHY_TX_LANE_ENABLE); + mb(); +} + +static inline void ufs_qcom_phy_qmp_20nm_start_serdes(struct ufs_qcom_phy *phy) +{ + u32 tmp; + + tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START); + tmp &= ~MASK_SERDES_START; + tmp |= (1 << OFFSET_SERDES_START); + writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START); + mb(); +} + +static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common) +{ + int err = 0; + u32 val; + + err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS, + val, (val & MASK_PCS_READY), 10, 1000000); + if (err) + dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n", + __func__, err); + return err; +} + +static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { + .init = ufs_qcom_phy_qmp_20nm_init, + .exit = ufs_qcom_phy_qmp_20nm_exit, + .power_on = ufs_qcom_phy_power_on, + .power_off = ufs_qcom_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct ufs_qcom_phy_specific_ops phy_20nm_ops = { + .calibrate_phy = ufs_qcom_phy_qmp_20nm_phy_calibrate, + .start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes, + .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready, + .set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable, + .power_control = ufs_qcom_phy_qmp_20nm_power_control, +}; + +static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy *generic_phy; + struct ufs_qcom_phy_qmp_20nm *phy; + struct ufs_qcom_phy *phy_common; + int err = 0; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) { + err = -ENOMEM; + goto out; + } + phy_common = &phy->common_cfg; + + generic_phy = ufs_qcom_phy_generic_probe(pdev, phy_common, + &ufs_qcom_phy_qmp_20nm_phy_ops, &phy_20nm_ops); + + if (!generic_phy) { + err = -EIO; + goto out; + } + + err = ufs_qcom_phy_init_clks(phy_common); + if (err) + goto out; + + err = ufs_qcom_phy_init_vregulators(phy_common); + if (err) + goto out; + + ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common); + + phy_set_drvdata(generic_phy, phy); + + strlcpy(phy_common->name, UFS_PHY_NAME, sizeof(phy_common->name)); + +out: + return err; +} + +static const struct of_device_id ufs_qcom_phy_qmp_20nm_of_match[] = { + {.compatible = "qcom,ufs-phy-qmp-20nm"}, + {}, +}; +MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_20nm_of_match); + +static struct platform_driver ufs_qcom_phy_qmp_20nm_driver = { + .probe = ufs_qcom_phy_qmp_20nm_probe, + .driver = { + .of_match_table = ufs_qcom_phy_qmp_20nm_of_match, + .name = "ufs_qcom_phy_qmp_20nm", + }, +}; + +module_platform_driver(ufs_qcom_phy_qmp_20nm_driver); + +MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 20nm"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h new file mode 100644 index 000000000000..4f3076bb3d71 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef UFS_QCOM_PHY_QMP_20NM_H_ +#define UFS_QCOM_PHY_QMP_20NM_H_ + +#include "phy-qcom-ufs-i.h" + +/* QCOM UFS PHY control registers */ + +#define COM_OFF(x) (0x000 + x) +#define PHY_OFF(x) (0xC00 + x) +#define TX_OFF(n, x) (0x400 + (0x400 * n) + x) +#define RX_OFF(n, x) (0x600 + (0x400 * n) + x) + +/* UFS PHY PLL block registers */ +#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x0) +#define QSERDES_COM_PLL_VCOTAIL_EN COM_OFF(0x04) +#define QSERDES_COM_PLL_CNTRL COM_OFF(0x14) +#define QSERDES_COM_PLL_IP_SETI COM_OFF(0x24) +#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL COM_OFF(0x28) +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x30) +#define QSERDES_COM_PLL_CP_SETI COM_OFF(0x34) +#define QSERDES_COM_PLL_IP_SETP COM_OFF(0x38) +#define QSERDES_COM_PLL_CP_SETP COM_OFF(0x3C) +#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND COM_OFF(0x48) +#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0x4C) +#define QSERDES_COM_RESETSM_CNTRL2 COM_OFF(0x50) +#define QSERDES_COM_PLLLOCK_CMP1 COM_OFF(0x90) +#define QSERDES_COM_PLLLOCK_CMP2 COM_OFF(0x94) +#define QSERDES_COM_PLLLOCK_CMP3 COM_OFF(0x98) +#define QSERDES_COM_PLLLOCK_CMP_EN COM_OFF(0x9C) +#define QSERDES_COM_BGTC COM_OFF(0xA0) +#define QSERDES_COM_DEC_START1 COM_OFF(0xAC) +#define QSERDES_COM_PLL_AMP_OS COM_OFF(0xB0) +#define QSERDES_COM_RES_CODE_UP_OFFSET COM_OFF(0xD8) +#define QSERDES_COM_RES_CODE_DN_OFFSET COM_OFF(0xDC) +#define QSERDES_COM_DIV_FRAC_START1 COM_OFF(0x100) +#define QSERDES_COM_DIV_FRAC_START2 COM_OFF(0x104) +#define QSERDES_COM_DIV_FRAC_START3 COM_OFF(0x108) +#define QSERDES_COM_DEC_START2 COM_OFF(0x10C) +#define QSERDES_COM_PLL_RXTXEPCLK_EN COM_OFF(0x110) +#define QSERDES_COM_PLL_CRCTRL COM_OFF(0x114) +#define QSERDES_COM_PLL_CLKEPDIV COM_OFF(0x118) + +/* TX LANE n (0, 1) registers */ +#define QSERDES_TX_EMP_POST1_LVL(n) TX_OFF(n, 0x08) +#define QSERDES_TX_DRV_LVL(n) TX_OFF(n, 0x0C) +#define QSERDES_TX_LANE_MODE(n) TX_OFF(n, 0x54) + +/* RX LANE n (0, 1) registers */ +#define QSERDES_RX_CDR_CONTROL1(n) RX_OFF(n, 0x0) +#define QSERDES_RX_CDR_CONTROL_HALF(n) RX_OFF(n, 0x8) +#define QSERDES_RX_RX_EQ_GAIN1_LSB(n) RX_OFF(n, 0xA8) +#define QSERDES_RX_RX_EQ_GAIN1_MSB(n) RX_OFF(n, 0xAC) +#define QSERDES_RX_RX_EQ_GAIN2_LSB(n) RX_OFF(n, 0xB0) +#define QSERDES_RX_RX_EQ_GAIN2_MSB(n) RX_OFF(n, 0xB4) +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(n) RX_OFF(n, 0xBC) +#define QSERDES_RX_CDR_CONTROL_QUARTER(n) RX_OFF(n, 0xC) +#define QSERDES_RX_SIGDET_CNTRL(n) RX_OFF(n, 0x100) + +/* UFS PHY registers */ +#define UFS_PHY_PHY_START PHY_OFF(0x00) +#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x4) +#define UFS_PHY_TX_LANE_ENABLE PHY_OFF(0x44) +#define UFS_PHY_PWM_G1_CLK_DIVIDER PHY_OFF(0x08) +#define UFS_PHY_PWM_G2_CLK_DIVIDER PHY_OFF(0x0C) +#define UFS_PHY_PWM_G3_CLK_DIVIDER PHY_OFF(0x10) +#define UFS_PHY_PWM_G4_CLK_DIVIDER PHY_OFF(0x14) +#define UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER PHY_OFF(0x34) +#define UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER PHY_OFF(0x38) +#define UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER PHY_OFF(0x3C) +#define UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER PHY_OFF(0x40) +#define UFS_PHY_OMC_STATUS_RDVAL PHY_OFF(0x68) +#define UFS_PHY_LINE_RESET_TIME PHY_OFF(0x28) +#define UFS_PHY_LINE_RESET_GRANULARITY PHY_OFF(0x2C) +#define UFS_PHY_TSYNC_RSYNC_CNTL PHY_OFF(0x48) +#define UFS_PHY_PLL_CNTL PHY_OFF(0x50) +#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x54) +#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x5C) +#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL PHY_OFF(0x58) +#define UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL PHY_OFF(0x60) +#define UFS_PHY_CFG_CHANGE_CNT_VAL PHY_OFF(0x64) +#define UFS_PHY_RX_SYNC_WAIT_TIME PHY_OFF(0x6C) +#define UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB4) +#define UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE0) +#define UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB8) +#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE4) +#define UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xBC) +#define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xE8) +#define UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY PHY_OFF(0xFC) +#define UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY PHY_OFF(0x100) +#define UFS_PHY_RX_SIGDET_CTRL3 PHY_OFF(0x14c) +#define UFS_PHY_RMMI_ATTR_CTRL PHY_OFF(0x160) +#define UFS_PHY_RMMI_RX_CFGUPDT_L1 (1 << 7) +#define UFS_PHY_RMMI_TX_CFGUPDT_L1 (1 << 6) +#define UFS_PHY_RMMI_CFGWR_L1 (1 << 5) +#define UFS_PHY_RMMI_CFGRD_L1 (1 << 4) +#define UFS_PHY_RMMI_RX_CFGUPDT_L0 (1 << 3) +#define UFS_PHY_RMMI_TX_CFGUPDT_L0 (1 << 2) +#define UFS_PHY_RMMI_CFGWR_L0 (1 << 1) +#define UFS_PHY_RMMI_CFGRD_L0 (1 << 0) +#define UFS_PHY_RMMI_ATTRID PHY_OFF(0x164) +#define UFS_PHY_RMMI_ATTRWRVAL PHY_OFF(0x168) +#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS PHY_OFF(0x16C) +#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS PHY_OFF(0x170) +#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x174) + +#define UFS_PHY_TX_LANE_ENABLE_MASK 0x3 + +/* + * This structure represents the 20nm specific phy. + * common_cfg MUST remain the first field in this structure + * in case extra fields are added. This way, when calling + * get_ufs_qcom_phy() of generic phy, we can extract the + * common phy structure (struct ufs_qcom_phy) out of it + * regardless of the relevant specific phy. + */ +struct ufs_qcom_phy_qmp_20nm { + struct ufs_qcom_phy common_cfg; +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_2_0[] = { + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x1b), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x0f), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(0), 0x2F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(0), 0x20), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(1), 0x2F), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(1), 0x20), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3), +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_3_0[] = { + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x2b), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x38), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x3c), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_UP_OFFSET, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_DN_OFFSET, 0x02), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x40), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3), +}; + +static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = { + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0x65), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x1e), +}; + +#endif diff --git a/drivers/phy/qualcomm/phy-qcom-ufs.c b/drivers/phy/qualcomm/phy-qcom-ufs.c new file mode 100644 index 000000000000..43865ef340e2 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-ufs.c @@ -0,0 +1,691 @@ +/* + * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include "phy-qcom-ufs-i.h" + +#define MAX_PROP_NAME 32 +#define VDDA_PHY_MIN_UV 1000000 +#define VDDA_PHY_MAX_UV 1000000 +#define VDDA_PLL_MIN_UV 1800000 +#define VDDA_PLL_MAX_UV 1800000 +#define VDDP_REF_CLK_MIN_UV 1200000 +#define VDDP_REF_CLK_MAX_UV 1200000 + +int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, + struct ufs_qcom_phy_calibration *tbl_A, + int tbl_size_A, + struct ufs_qcom_phy_calibration *tbl_B, + int tbl_size_B, bool is_rate_B) +{ + int i; + int ret = 0; + + if (!tbl_A) { + dev_err(ufs_qcom_phy->dev, "%s: tbl_A is NULL", __func__); + ret = EINVAL; + goto out; + } + + for (i = 0; i < tbl_size_A; i++) + writel_relaxed(tbl_A[i].cfg_value, + ufs_qcom_phy->mmio + tbl_A[i].reg_offset); + + /* + * In case we would like to work in rate B, we need + * to override a registers that were configured in rate A table + * with registers of rate B table. + * table. + */ + if (is_rate_B) { + if (!tbl_B) { + dev_err(ufs_qcom_phy->dev, "%s: tbl_B is NULL", + __func__); + ret = EINVAL; + goto out; + } + + for (i = 0; i < tbl_size_B; i++) + writel_relaxed(tbl_B[i].cfg_value, + ufs_qcom_phy->mmio + tbl_B[i].reg_offset); + } + + /* flush buffered writes */ + mb(); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate); + +/* + * This assumes the embedded phy structure inside generic_phy is of type + * struct ufs_qcom_phy. In order to function properly it's crucial + * to keep the embedded struct "struct ufs_qcom_phy common_cfg" + * as the first inside generic_phy. + */ +struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy) +{ + return (struct ufs_qcom_phy *)phy_get_drvdata(generic_phy); +} +EXPORT_SYMBOL_GPL(get_ufs_qcom_phy); + +static +int ufs_qcom_phy_base_init(struct platform_device *pdev, + struct ufs_qcom_phy *phy_common) +{ + struct device *dev = &pdev->dev; + struct resource *res; + int err = 0; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem"); + phy_common->mmio = devm_ioremap_resource(dev, res); + if (IS_ERR((void const *)phy_common->mmio)) { + err = PTR_ERR((void const *)phy_common->mmio); + phy_common->mmio = NULL; + dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n", + __func__, err); + return err; + } + + /* "dev_ref_clk_ctrl_mem" is optional resource */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "dev_ref_clk_ctrl_mem"); + phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res); + if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio)) + phy_common->dev_ref_clk_ctrl_mmio = NULL; + + return 0; +} + +struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, + struct ufs_qcom_phy *common_cfg, + const struct phy_ops *ufs_qcom_phy_gen_ops, + struct ufs_qcom_phy_specific_ops *phy_spec_ops) +{ + int err; + struct device *dev = &pdev->dev; + struct phy *generic_phy = NULL; + struct phy_provider *phy_provider; + + err = ufs_qcom_phy_base_init(pdev, common_cfg); + if (err) { + dev_err(dev, "%s: phy base init failed %d\n", __func__, err); + goto out; + } + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + err = PTR_ERR(phy_provider); + dev_err(dev, "%s: failed to register phy %d\n", __func__, err); + goto out; + } + + generic_phy = devm_phy_create(dev, NULL, ufs_qcom_phy_gen_ops); + if (IS_ERR(generic_phy)) { + err = PTR_ERR(generic_phy); + dev_err(dev, "%s: failed to create phy %d\n", __func__, err); + generic_phy = NULL; + goto out; + } + + common_cfg->phy_spec_ops = phy_spec_ops; + common_cfg->dev = dev; + +out: + return generic_phy; +} +EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe); + +static int __ufs_qcom_phy_clk_get(struct device *dev, + const char *name, struct clk **clk_out, bool err_print) +{ + struct clk *clk; + int err = 0; + + clk = devm_clk_get(dev, name); + if (IS_ERR(clk)) { + err = PTR_ERR(clk); + if (err_print) + dev_err(dev, "failed to get %s err %d", name, err); + } else { + *clk_out = clk; + } + + return err; +} + +static int ufs_qcom_phy_clk_get(struct device *dev, + const char *name, struct clk **clk_out) +{ + return __ufs_qcom_phy_clk_get(dev, name, clk_out, true); +} + +int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common) +{ + int err; + + if (of_device_is_compatible(phy_common->dev->of_node, + "qcom,msm8996-ufs-phy-qmp-14nm")) + goto skip_txrx_clk; + + err = ufs_qcom_phy_clk_get(phy_common->dev, "tx_iface_clk", + &phy_common->tx_iface_clk); + if (err) + goto out; + + err = ufs_qcom_phy_clk_get(phy_common->dev, "rx_iface_clk", + &phy_common->rx_iface_clk); + if (err) + goto out; + +skip_txrx_clk: + err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_src", + &phy_common->ref_clk_src); + if (err) + goto out; + + /* + * "ref_clk_parent" is optional hence don't abort init if it's not + * found. + */ + __ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_parent", + &phy_common->ref_clk_parent, false); + + err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk", + &phy_common->ref_clk); + +out: + return err; +} +EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_clks); + +static int ufs_qcom_phy_init_vreg(struct device *dev, + struct ufs_qcom_phy_vreg *vreg, + const char *name) +{ + int err = 0; + + char prop_name[MAX_PROP_NAME]; + + vreg->name = name; + vreg->reg = devm_regulator_get(dev, name); + if (IS_ERR(vreg->reg)) { + err = PTR_ERR(vreg->reg); + dev_err(dev, "failed to get %s, %d\n", name, err); + goto out; + } + + if (dev->of_node) { + snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name); + err = of_property_read_u32(dev->of_node, + prop_name, &vreg->max_uA); + if (err && err != -EINVAL) { + dev_err(dev, "%s: failed to read %s\n", + __func__, prop_name); + goto out; + } else if (err == -EINVAL || !vreg->max_uA) { + if (regulator_count_voltages(vreg->reg) > 0) { + dev_err(dev, "%s: %s is mandatory\n", + __func__, prop_name); + goto out; + } + err = 0; + } + } + + if (!strcmp(name, "vdda-pll")) { + vreg->max_uV = VDDA_PLL_MAX_UV; + vreg->min_uV = VDDA_PLL_MIN_UV; + } else if (!strcmp(name, "vdda-phy")) { + vreg->max_uV = VDDA_PHY_MAX_UV; + vreg->min_uV = VDDA_PHY_MIN_UV; + } else if (!strcmp(name, "vddp-ref-clk")) { + vreg->max_uV = VDDP_REF_CLK_MAX_UV; + vreg->min_uV = VDDP_REF_CLK_MIN_UV; + } + +out: + return err; +} + +int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common) +{ + int err; + + err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_pll, + "vdda-pll"); + if (err) + goto out; + + err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_phy, + "vdda-phy"); + + if (err) + goto out; + + err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk, + "vddp-ref-clk"); + +out: + return err; +} +EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_vregulators); + +static int ufs_qcom_phy_cfg_vreg(struct device *dev, + struct ufs_qcom_phy_vreg *vreg, bool on) +{ + int ret = 0; + struct regulator *reg = vreg->reg; + const char *name = vreg->name; + int min_uV; + int uA_load; + + if (regulator_count_voltages(reg) > 0) { + min_uV = on ? vreg->min_uV : 0; + ret = regulator_set_voltage(reg, min_uV, vreg->max_uV); + if (ret) { + dev_err(dev, "%s: %s set voltage failed, err=%d\n", + __func__, name, ret); + goto out; + } + uA_load = on ? vreg->max_uA : 0; + ret = regulator_set_load(reg, uA_load); + if (ret >= 0) { + /* + * regulator_set_load() returns new regulator + * mode upon success. + */ + ret = 0; + } else { + dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n", + __func__, name, uA_load, ret); + goto out; + } + } +out: + return ret; +} + +static int ufs_qcom_phy_enable_vreg(struct device *dev, + struct ufs_qcom_phy_vreg *vreg) +{ + int ret = 0; + + if (!vreg || vreg->enabled) + goto out; + + ret = ufs_qcom_phy_cfg_vreg(dev, vreg, true); + if (ret) { + dev_err(dev, "%s: ufs_qcom_phy_cfg_vreg() failed, err=%d\n", + __func__, ret); + goto out; + } + + ret = regulator_enable(vreg->reg); + if (ret) { + dev_err(dev, "%s: enable failed, err=%d\n", + __func__, ret); + goto out; + } + + vreg->enabled = true; +out: + return ret; +} + +static int ufs_qcom_phy_enable_ref_clk(struct ufs_qcom_phy *phy) +{ + int ret = 0; + + if (phy->is_ref_clk_enabled) + goto out; + + /* + * reference clock is propagated in a daisy-chained manner from + * source to phy, so ungate them at each stage. + */ + ret = clk_prepare_enable(phy->ref_clk_src); + if (ret) { + dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n", + __func__, ret); + goto out; + } + + /* + * "ref_clk_parent" is optional clock hence make sure that clk reference + * is available before trying to enable the clock. + */ + if (phy->ref_clk_parent) { + ret = clk_prepare_enable(phy->ref_clk_parent); + if (ret) { + dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n", + __func__, ret); + goto out_disable_src; + } + } + + ret = clk_prepare_enable(phy->ref_clk); + if (ret) { + dev_err(phy->dev, "%s: ref_clk enable failed %d\n", + __func__, ret); + goto out_disable_parent; + } + + phy->is_ref_clk_enabled = true; + goto out; + +out_disable_parent: + if (phy->ref_clk_parent) + clk_disable_unprepare(phy->ref_clk_parent); +out_disable_src: + clk_disable_unprepare(phy->ref_clk_src); +out: + return ret; +} + +static int ufs_qcom_phy_disable_vreg(struct device *dev, + struct ufs_qcom_phy_vreg *vreg) +{ + int ret = 0; + + if (!vreg || !vreg->enabled) + goto out; + + ret = regulator_disable(vreg->reg); + + if (!ret) { + /* ignore errors on applying disable config */ + ufs_qcom_phy_cfg_vreg(dev, vreg, false); + vreg->enabled = false; + } else { + dev_err(dev, "%s: %s disable failed, err=%d\n", + __func__, vreg->name, ret); + } +out: + return ret; +} + +static void ufs_qcom_phy_disable_ref_clk(struct ufs_qcom_phy *phy) +{ + if (phy->is_ref_clk_enabled) { + clk_disable_unprepare(phy->ref_clk); + /* + * "ref_clk_parent" is optional clock hence make sure that clk + * reference is available before trying to disable the clock. + */ + if (phy->ref_clk_parent) + clk_disable_unprepare(phy->ref_clk_parent); + clk_disable_unprepare(phy->ref_clk_src); + phy->is_ref_clk_enabled = false; + } +} + +#define UFS_REF_CLK_EN (1 << 5) + +static void ufs_qcom_phy_dev_ref_clk_ctrl(struct phy *generic_phy, bool enable) +{ + struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy); + + if (phy->dev_ref_clk_ctrl_mmio && + (enable ^ phy->is_dev_ref_clk_enabled)) { + u32 temp = readl_relaxed(phy->dev_ref_clk_ctrl_mmio); + + if (enable) + temp |= UFS_REF_CLK_EN; + else + temp &= ~UFS_REF_CLK_EN; + + /* + * If we are here to disable this clock immediately after + * entering into hibern8, we need to make sure that device + * ref_clk is active atleast 1us after the hibern8 enter. + */ + if (!enable) + udelay(1); + + writel_relaxed(temp, phy->dev_ref_clk_ctrl_mmio); + /* ensure that ref_clk is enabled/disabled before we return */ + wmb(); + /* + * If we call hibern8 exit after this, we need to make sure that + * device ref_clk is stable for atleast 1us before the hibern8 + * exit command. + */ + if (enable) + udelay(1); + + phy->is_dev_ref_clk_enabled = enable; + } +} + +void ufs_qcom_phy_enable_dev_ref_clk(struct phy *generic_phy) +{ + ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, true); +} +EXPORT_SYMBOL_GPL(ufs_qcom_phy_enable_dev_ref_clk); + +void ufs_qcom_phy_disable_dev_ref_clk(struct phy *generic_phy) +{ + ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, false); +} +EXPORT_SYMBOL_GPL(ufs_qcom_phy_disable_dev_ref_clk); + +/* Turn ON M-PHY RMMI interface clocks */ +static int ufs_qcom_phy_enable_iface_clk(struct ufs_qcom_phy *phy) +{ + int ret = 0; + + if (phy->is_iface_clk_enabled) + goto out; + + ret = clk_prepare_enable(phy->tx_iface_clk); + if (ret) { + dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n", + __func__, ret); + goto out; + } + ret = clk_prepare_enable(phy->rx_iface_clk); + if (ret) { + clk_disable_unprepare(phy->tx_iface_clk); + dev_err(phy->dev, "%s: rx_iface_clk enable failed %d. disabling also tx_iface_clk\n", + __func__, ret); + goto out; + } + phy->is_iface_clk_enabled = true; + +out: + return ret; +} + +/* Turn OFF M-PHY RMMI interface clocks */ +void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy) +{ + if (phy->is_iface_clk_enabled) { + clk_disable_unprepare(phy->tx_iface_clk); + clk_disable_unprepare(phy->rx_iface_clk); + phy->is_iface_clk_enabled = false; + } +} + +int ufs_qcom_phy_start_serdes(struct phy *generic_phy) +{ + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); + int ret = 0; + + if (!ufs_qcom_phy->phy_spec_ops->start_serdes) { + dev_err(ufs_qcom_phy->dev, "%s: start_serdes() callback is not supported\n", + __func__); + ret = -ENOTSUPP; + } else { + ufs_qcom_phy->phy_spec_ops->start_serdes(ufs_qcom_phy); + } + + return ret; +} +EXPORT_SYMBOL_GPL(ufs_qcom_phy_start_serdes); + +int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes) +{ + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); + int ret = 0; + + if (!ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable) { + dev_err(ufs_qcom_phy->dev, "%s: set_tx_lane_enable() callback is not supported\n", + __func__); + ret = -ENOTSUPP; + } else { + ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable(ufs_qcom_phy, + tx_lanes); + } + + return ret; +} +EXPORT_SYMBOL_GPL(ufs_qcom_phy_set_tx_lane_enable); + +void ufs_qcom_phy_save_controller_version(struct phy *generic_phy, + u8 major, u16 minor, u16 step) +{ + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); + + ufs_qcom_phy->host_ctrl_rev_major = major; + ufs_qcom_phy->host_ctrl_rev_minor = minor; + ufs_qcom_phy->host_ctrl_rev_step = step; +} +EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version); + +int ufs_qcom_phy_calibrate_phy(struct phy *generic_phy, bool is_rate_B) +{ + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); + int ret = 0; + + if (!ufs_qcom_phy->phy_spec_ops->calibrate_phy) { + dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() callback is not supported\n", + __func__); + ret = -ENOTSUPP; + } else { + ret = ufs_qcom_phy->phy_spec_ops-> + calibrate_phy(ufs_qcom_phy, is_rate_B); + if (ret) + dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() failed %d\n", + __func__, ret); + } + + return ret; +} +EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate_phy); + +int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy) +{ + struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); + + if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) { + dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n", + __func__); + return -ENOTSUPP; + } + + return ufs_qcom_phy->phy_spec_ops-> + is_physical_coding_sublayer_ready(ufs_qcom_phy); +} +EXPORT_SYMBOL_GPL(ufs_qcom_phy_is_pcs_ready); + +int ufs_qcom_phy_power_on(struct phy *generic_phy) +{ + struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); + struct device *dev = phy_common->dev; + int err; + + if (phy_common->is_powered_on) + return 0; + + err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy); + if (err) { + dev_err(dev, "%s enable vdda_phy failed, err=%d\n", + __func__, err); + goto out; + } + + phy_common->phy_spec_ops->power_control(phy_common, true); + + /* vdda_pll also enables ref clock LDOs so enable it first */ + err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_pll); + if (err) { + dev_err(dev, "%s enable vdda_pll failed, err=%d\n", + __func__, err); + goto out_disable_phy; + } + + err = ufs_qcom_phy_enable_iface_clk(phy_common); + if (err) { + dev_err(dev, "%s enable phy iface clock failed, err=%d\n", + __func__, err); + goto out_disable_pll; + } + + err = ufs_qcom_phy_enable_ref_clk(phy_common); + if (err) { + dev_err(dev, "%s enable phy ref clock failed, err=%d\n", + __func__, err); + goto out_disable_iface_clk; + } + + /* enable device PHY ref_clk pad rail */ + if (phy_common->vddp_ref_clk.reg) { + err = ufs_qcom_phy_enable_vreg(dev, + &phy_common->vddp_ref_clk); + if (err) { + dev_err(dev, "%s enable vddp_ref_clk failed, err=%d\n", + __func__, err); + goto out_disable_ref_clk; + } + } + + phy_common->is_powered_on = true; + goto out; + +out_disable_ref_clk: + ufs_qcom_phy_disable_ref_clk(phy_common); +out_disable_iface_clk: + ufs_qcom_phy_disable_iface_clk(phy_common); +out_disable_pll: + ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_pll); +out_disable_phy: + ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_phy); +out: + return err; +} +EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_on); + +int ufs_qcom_phy_power_off(struct phy *generic_phy) +{ + struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); + + if (!phy_common->is_powered_on) + return 0; + + phy_common->phy_spec_ops->power_control(phy_common, false); + + if (phy_common->vddp_ref_clk.reg) + ufs_qcom_phy_disable_vreg(phy_common->dev, + &phy_common->vddp_ref_clk); + ufs_qcom_phy_disable_ref_clk(phy_common); + ufs_qcom_phy_disable_iface_clk(phy_common); + + ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_pll); + ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_phy); + phy_common->is_powered_on = false; + + return 0; +} +EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off); diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs.c b/drivers/phy/qualcomm/phy-qcom-usb-hs.c new file mode 100644 index 000000000000..4b20abc3ae2f --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-usb-hs.c @@ -0,0 +1,295 @@ +/** + * Copyright (C) 2016 Linaro Ltd + * + * 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 + +#define ULPI_PWR_CLK_MNG_REG 0x88 +# define ULPI_PWR_OTG_COMP_DISABLE BIT(0) + +#define ULPI_MISC_A 0x96 +# define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1) +# define ULPI_MISC_A_VBUSVLDEXT BIT(0) + + +struct ulpi_seq { + u8 addr; + u8 val; +}; + +struct qcom_usb_hs_phy { + struct ulpi *ulpi; + struct phy *phy; + struct clk *ref_clk; + struct clk *sleep_clk; + struct regulator *v1p8; + struct regulator *v3p3; + struct reset_control *reset; + struct ulpi_seq *init_seq; + struct extcon_dev *vbus_edev; + struct notifier_block vbus_notify; +}; + +static int qcom_usb_hs_phy_set_mode(struct phy *phy, enum phy_mode mode) +{ + struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); + u8 addr; + int ret; + + if (!uphy->vbus_edev) { + u8 val = 0; + + switch (mode) { + case PHY_MODE_USB_OTG: + case PHY_MODE_USB_HOST: + val |= ULPI_INT_IDGRD; + case PHY_MODE_USB_DEVICE: + val |= ULPI_INT_SESS_VALID; + default: + break; + } + + ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_RISE, val); + if (ret) + return ret; + ret = ulpi_write(uphy->ulpi, ULPI_USB_INT_EN_FALL, val); + } else { + switch (mode) { + case PHY_MODE_USB_OTG: + case PHY_MODE_USB_DEVICE: + addr = ULPI_SET(ULPI_MISC_A); + break; + case PHY_MODE_USB_HOST: + addr = ULPI_CLR(ULPI_MISC_A); + break; + default: + return -EINVAL; + } + + ret = ulpi_write(uphy->ulpi, ULPI_SET(ULPI_PWR_CLK_MNG_REG), + ULPI_PWR_OTG_COMP_DISABLE); + if (ret) + return ret; + ret = ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXTSEL); + } + + return ret; +} + +static int +qcom_usb_hs_phy_vbus_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct qcom_usb_hs_phy *uphy; + u8 addr; + + uphy = container_of(nb, struct qcom_usb_hs_phy, vbus_notify); + + if (event) + addr = ULPI_SET(ULPI_MISC_A); + else + addr = ULPI_CLR(ULPI_MISC_A); + + return ulpi_write(uphy->ulpi, addr, ULPI_MISC_A_VBUSVLDEXT); +} + +static int qcom_usb_hs_phy_power_on(struct phy *phy) +{ + struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); + struct ulpi *ulpi = uphy->ulpi; + const struct ulpi_seq *seq; + int ret, state; + + ret = clk_prepare_enable(uphy->ref_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(uphy->sleep_clk); + if (ret) + goto err_sleep; + + ret = regulator_set_load(uphy->v1p8, 50000); + if (ret < 0) + goto err_1p8; + + ret = regulator_enable(uphy->v1p8); + if (ret) + goto err_1p8; + + ret = regulator_set_voltage_triplet(uphy->v3p3, 3050000, 3300000, + 3300000); + if (ret) + goto err_3p3; + + ret = regulator_set_load(uphy->v3p3, 50000); + if (ret < 0) + goto err_3p3; + + ret = regulator_enable(uphy->v3p3); + if (ret) + goto err_3p3; + + for (seq = uphy->init_seq; seq->addr; seq++) { + ret = ulpi_write(ulpi, ULPI_EXT_VENDOR_SPECIFIC + seq->addr, + seq->val); + if (ret) + goto err_ulpi; + } + + if (uphy->reset) { + ret = reset_control_reset(uphy->reset); + if (ret) + goto err_ulpi; + } + + if (uphy->vbus_edev) { + state = extcon_get_cable_state_(uphy->vbus_edev, EXTCON_USB); + /* setup initial state */ + qcom_usb_hs_phy_vbus_notifier(&uphy->vbus_notify, state, + uphy->vbus_edev); + ret = extcon_register_notifier(uphy->vbus_edev, EXTCON_USB, + &uphy->vbus_notify); + if (ret) + goto err_ulpi; + } + + return 0; +err_ulpi: + regulator_disable(uphy->v3p3); +err_3p3: + regulator_disable(uphy->v1p8); +err_1p8: + clk_disable_unprepare(uphy->sleep_clk); +err_sleep: + clk_disable_unprepare(uphy->ref_clk); + return ret; +} + +static int qcom_usb_hs_phy_power_off(struct phy *phy) +{ + int ret; + struct qcom_usb_hs_phy *uphy = phy_get_drvdata(phy); + + if (uphy->vbus_edev) { + ret = extcon_unregister_notifier(uphy->vbus_edev, EXTCON_USB, + &uphy->vbus_notify); + if (ret) + return ret; + } + + regulator_disable(uphy->v3p3); + regulator_disable(uphy->v1p8); + clk_disable_unprepare(uphy->sleep_clk); + clk_disable_unprepare(uphy->ref_clk); + + return 0; +} + +static const struct phy_ops qcom_usb_hs_phy_ops = { + .power_on = qcom_usb_hs_phy_power_on, + .power_off = qcom_usb_hs_phy_power_off, + .set_mode = qcom_usb_hs_phy_set_mode, + .owner = THIS_MODULE, +}; + +static int qcom_usb_hs_phy_probe(struct ulpi *ulpi) +{ + struct qcom_usb_hs_phy *uphy; + struct phy_provider *p; + struct clk *clk; + struct regulator *reg; + struct reset_control *reset; + int size; + int ret; + + uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); + if (!uphy) + return -ENOMEM; + ulpi_set_drvdata(ulpi, uphy); + uphy->ulpi = ulpi; + + size = of_property_count_u8_elems(ulpi->dev.of_node, "qcom,init-seq"); + if (size < 0) + size = 0; + uphy->init_seq = devm_kmalloc_array(&ulpi->dev, (size / 2) + 1, + sizeof(*uphy->init_seq), GFP_KERNEL); + if (!uphy->init_seq) + return -ENOMEM; + ret = of_property_read_u8_array(ulpi->dev.of_node, "qcom,init-seq", + (u8 *)uphy->init_seq, size); + if (ret && size) + return ret; + /* NUL terminate */ + uphy->init_seq[size / 2].addr = uphy->init_seq[size / 2].val = 0; + + uphy->ref_clk = clk = devm_clk_get(&ulpi->dev, "ref"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->sleep_clk = clk = devm_clk_get(&ulpi->dev, "sleep"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->v1p8 = reg = devm_regulator_get(&ulpi->dev, "v1p8"); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + uphy->v3p3 = reg = devm_regulator_get(&ulpi->dev, "v3p3"); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + uphy->reset = reset = devm_reset_control_get(&ulpi->dev, "por"); + if (IS_ERR(reset)) { + if (PTR_ERR(reset) == -EPROBE_DEFER) + return PTR_ERR(reset); + uphy->reset = NULL; + } + + uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node, + &qcom_usb_hs_phy_ops); + if (IS_ERR(uphy->phy)) + return PTR_ERR(uphy->phy); + + uphy->vbus_edev = extcon_get_edev_by_phandle(&ulpi->dev, 0); + if (IS_ERR(uphy->vbus_edev)) { + if (PTR_ERR(uphy->vbus_edev) != -ENODEV) + return PTR_ERR(uphy->vbus_edev); + uphy->vbus_edev = NULL; + } + + uphy->vbus_notify.notifier_call = qcom_usb_hs_phy_vbus_notifier; + phy_set_drvdata(uphy->phy, uphy); + + p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(p); +} + +static const struct of_device_id qcom_usb_hs_phy_match[] = { + { .compatible = "qcom,usb-hs-phy", }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_usb_hs_phy_match); + +static struct ulpi_driver qcom_usb_hs_phy_driver = { + .probe = qcom_usb_hs_phy_probe, + .driver = { + .name = "qcom_usb_hs_phy", + .of_match_table = qcom_usb_hs_phy_match, + }, +}; +module_ulpi_driver(qcom_usb_hs_phy_driver); + +MODULE_DESCRIPTION("Qualcomm USB HS phy"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hsic.c b/drivers/phy/qualcomm/phy-qcom-usb-hsic.c new file mode 100644 index 000000000000..c110563a73cb --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-usb-hsic.c @@ -0,0 +1,159 @@ +/** + * Copyright (C) 2016 Linaro Ltd + * + * 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 + +#define ULPI_HSIC_CFG 0x30 +#define ULPI_HSIC_IO_CAL 0x33 + +struct qcom_usb_hsic_phy { + struct ulpi *ulpi; + struct phy *phy; + struct pinctrl *pctl; + struct clk *phy_clk; + struct clk *cal_clk; + struct clk *cal_sleep_clk; +}; + +static int qcom_usb_hsic_phy_power_on(struct phy *phy) +{ + struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); + struct ulpi *ulpi = uphy->ulpi; + struct pinctrl_state *pins_default; + int ret; + + ret = clk_prepare_enable(uphy->phy_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(uphy->cal_clk); + if (ret) + goto err_cal; + + ret = clk_prepare_enable(uphy->cal_sleep_clk); + if (ret) + goto err_sleep; + + /* Set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */ + ret = ulpi_write(ulpi, ULPI_HSIC_IO_CAL, 0xff); + if (ret) + goto err_ulpi; + + /* Enable periodic IO calibration in HSIC_CFG register */ + ret = ulpi_write(ulpi, ULPI_HSIC_CFG, 0xa8); + if (ret) + goto err_ulpi; + + /* Configure pins for HSIC functionality */ + pins_default = pinctrl_lookup_state(uphy->pctl, PINCTRL_STATE_DEFAULT); + if (IS_ERR(pins_default)) + return PTR_ERR(pins_default); + + ret = pinctrl_select_state(uphy->pctl, pins_default); + if (ret) + goto err_ulpi; + + /* Enable HSIC mode in HSIC_CFG register */ + ret = ulpi_write(ulpi, ULPI_SET(ULPI_HSIC_CFG), 0x01); + if (ret) + goto err_ulpi; + + /* Disable auto-resume */ + ret = ulpi_write(ulpi, ULPI_CLR(ULPI_IFC_CTRL), + ULPI_IFC_CTRL_AUTORESUME); + if (ret) + goto err_ulpi; + + return ret; +err_ulpi: + clk_disable_unprepare(uphy->cal_sleep_clk); +err_sleep: + clk_disable_unprepare(uphy->cal_clk); +err_cal: + clk_disable_unprepare(uphy->phy_clk); + return ret; +} + +static int qcom_usb_hsic_phy_power_off(struct phy *phy) +{ + struct qcom_usb_hsic_phy *uphy = phy_get_drvdata(phy); + + clk_disable_unprepare(uphy->cal_sleep_clk); + clk_disable_unprepare(uphy->cal_clk); + clk_disable_unprepare(uphy->phy_clk); + + return 0; +} + +static const struct phy_ops qcom_usb_hsic_phy_ops = { + .power_on = qcom_usb_hsic_phy_power_on, + .power_off = qcom_usb_hsic_phy_power_off, + .owner = THIS_MODULE, +}; + +static int qcom_usb_hsic_phy_probe(struct ulpi *ulpi) +{ + struct qcom_usb_hsic_phy *uphy; + struct phy_provider *p; + struct clk *clk; + + uphy = devm_kzalloc(&ulpi->dev, sizeof(*uphy), GFP_KERNEL); + if (!uphy) + return -ENOMEM; + ulpi_set_drvdata(ulpi, uphy); + + uphy->ulpi = ulpi; + uphy->pctl = devm_pinctrl_get(&ulpi->dev); + if (IS_ERR(uphy->pctl)) + return PTR_ERR(uphy->pctl); + + uphy->phy_clk = clk = devm_clk_get(&ulpi->dev, "phy"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->cal_clk = clk = devm_clk_get(&ulpi->dev, "cal"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->cal_sleep_clk = clk = devm_clk_get(&ulpi->dev, "cal_sleep"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + uphy->phy = devm_phy_create(&ulpi->dev, ulpi->dev.of_node, + &qcom_usb_hsic_phy_ops); + if (IS_ERR(uphy->phy)) + return PTR_ERR(uphy->phy); + phy_set_drvdata(uphy->phy, uphy); + + p = devm_of_phy_provider_register(&ulpi->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(p); +} + +static const struct of_device_id qcom_usb_hsic_phy_match[] = { + { .compatible = "qcom,usb-hsic-phy", }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_usb_hsic_phy_match); + +static struct ulpi_driver qcom_usb_hsic_phy_driver = { + .probe = qcom_usb_hsic_phy_probe, + .driver = { + .name = "qcom_usb_hsic_phy", + .of_match_table = qcom_usb_hsic_phy_match, + }, +}; +module_ulpi_driver(qcom_usb_hsic_phy_driver); + +MODULE_DESCRIPTION("Qualcomm USB HSIC phy"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/renesas/Kconfig b/drivers/phy/renesas/Kconfig new file mode 100644 index 000000000000..432e2715e9c4 --- /dev/null +++ b/drivers/phy/renesas/Kconfig @@ -0,0 +1,17 @@ +# +# Phy drivers for Renesas platforms +# +config PHY_RCAR_GEN2 + tristate "Renesas R-Car generation 2 USB PHY driver" + depends on ARCH_RENESAS + depends on GENERIC_PHY + help + Support for USB PHY found on Renesas R-Car generation 2 SoCs. + +config PHY_RCAR_GEN3_USB2 + tristate "Renesas R-Car generation 3 USB 2.0 PHY driver" + depends on ARCH_RENESAS + depends on EXTCON + select GENERIC_PHY + help + Support for USB 2.0 PHY found on Renesas R-Car generation 3 SoCs. diff --git a/drivers/phy/renesas/Makefile b/drivers/phy/renesas/Makefile new file mode 100644 index 000000000000..695241aebf69 --- /dev/null +++ b/drivers/phy/renesas/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o +obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o diff --git a/drivers/phy/renesas/phy-rcar-gen2.c b/drivers/phy/renesas/phy-rcar-gen2.c new file mode 100644 index 000000000000..97d4dd6ea924 --- /dev/null +++ b/drivers/phy/renesas/phy-rcar-gen2.c @@ -0,0 +1,337 @@ +/* + * Renesas R-Car Gen2 PHY driver + * + * Copyright (C) 2014 Renesas Solutions Corp. + * Copyright (C) 2014 Cogent Embedded, Inc. + * + * 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 + +#define USBHS_LPSTS 0x02 +#define USBHS_UGCTRL 0x80 +#define USBHS_UGCTRL2 0x84 +#define USBHS_UGSTS 0x88 /* From technical update */ + +/* Low Power Status register (LPSTS) */ +#define USBHS_LPSTS_SUSPM 0x4000 + +/* USB General control register (UGCTRL) */ +#define USBHS_UGCTRL_CONNECT 0x00000004 +#define USBHS_UGCTRL_PLLRESET 0x00000001 + +/* USB General control register 2 (UGCTRL2) */ +#define USBHS_UGCTRL2_USB2SEL 0x80000000 +#define USBHS_UGCTRL2_USB2SEL_PCI 0x00000000 +#define USBHS_UGCTRL2_USB2SEL_USB30 0x80000000 +#define USBHS_UGCTRL2_USB0SEL 0x00000030 +#define USBHS_UGCTRL2_USB0SEL_PCI 0x00000010 +#define USBHS_UGCTRL2_USB0SEL_HS_USB 0x00000030 + +/* USB General status register (UGSTS) */ +#define USBHS_UGSTS_LOCK 0x00000100 /* From technical update */ + +#define PHYS_PER_CHANNEL 2 + +struct rcar_gen2_phy { + struct phy *phy; + struct rcar_gen2_channel *channel; + int number; + u32 select_value; +}; + +struct rcar_gen2_channel { + struct device_node *of_node; + struct rcar_gen2_phy_driver *drv; + struct rcar_gen2_phy phys[PHYS_PER_CHANNEL]; + int selected_phy; + u32 select_mask; +}; + +struct rcar_gen2_phy_driver { + void __iomem *base; + struct clk *clk; + spinlock_t lock; + int num_channels; + struct rcar_gen2_channel *channels; +}; + +static int rcar_gen2_phy_init(struct phy *p) +{ + struct rcar_gen2_phy *phy = phy_get_drvdata(p); + struct rcar_gen2_channel *channel = phy->channel; + struct rcar_gen2_phy_driver *drv = channel->drv; + unsigned long flags; + u32 ugctrl2; + + /* + * Try to acquire exclusive access to PHY. The first driver calling + * phy_init() on a given channel wins, and all attempts to use another + * PHY on this channel will fail until phy_exit() is called by the first + * driver. Achieving this with cmpxcgh() should be SMP-safe. + */ + if (cmpxchg(&channel->selected_phy, -1, phy->number) != -1) + return -EBUSY; + + clk_prepare_enable(drv->clk); + + spin_lock_irqsave(&drv->lock, flags); + ugctrl2 = readl(drv->base + USBHS_UGCTRL2); + ugctrl2 &= ~channel->select_mask; + ugctrl2 |= phy->select_value; + writel(ugctrl2, drv->base + USBHS_UGCTRL2); + spin_unlock_irqrestore(&drv->lock, flags); + return 0; +} + +static int rcar_gen2_phy_exit(struct phy *p) +{ + struct rcar_gen2_phy *phy = phy_get_drvdata(p); + struct rcar_gen2_channel *channel = phy->channel; + + clk_disable_unprepare(channel->drv->clk); + + channel->selected_phy = -1; + + return 0; +} + +static int rcar_gen2_phy_power_on(struct phy *p) +{ + struct rcar_gen2_phy *phy = phy_get_drvdata(p); + struct rcar_gen2_phy_driver *drv = phy->channel->drv; + void __iomem *base = drv->base; + unsigned long flags; + u32 value; + int err = 0, i; + + /* Skip if it's not USBHS */ + if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB) + return 0; + + spin_lock_irqsave(&drv->lock, flags); + + /* Power on USBHS PHY */ + value = readl(base + USBHS_UGCTRL); + value &= ~USBHS_UGCTRL_PLLRESET; + writel(value, base + USBHS_UGCTRL); + + value = readw(base + USBHS_LPSTS); + value |= USBHS_LPSTS_SUSPM; + writew(value, base + USBHS_LPSTS); + + for (i = 0; i < 20; i++) { + value = readl(base + USBHS_UGSTS); + if ((value & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) { + value = readl(base + USBHS_UGCTRL); + value |= USBHS_UGCTRL_CONNECT; + writel(value, base + USBHS_UGCTRL); + goto out; + } + udelay(1); + } + + /* Timed out waiting for the PLL lock */ + err = -ETIMEDOUT; + +out: + spin_unlock_irqrestore(&drv->lock, flags); + + return err; +} + +static int rcar_gen2_phy_power_off(struct phy *p) +{ + struct rcar_gen2_phy *phy = phy_get_drvdata(p); + struct rcar_gen2_phy_driver *drv = phy->channel->drv; + void __iomem *base = drv->base; + unsigned long flags; + u32 value; + + /* Skip if it's not USBHS */ + if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB) + return 0; + + spin_lock_irqsave(&drv->lock, flags); + + /* Power off USBHS PHY */ + value = readl(base + USBHS_UGCTRL); + value &= ~USBHS_UGCTRL_CONNECT; + writel(value, base + USBHS_UGCTRL); + + value = readw(base + USBHS_LPSTS); + value &= ~USBHS_LPSTS_SUSPM; + writew(value, base + USBHS_LPSTS); + + value = readl(base + USBHS_UGCTRL); + value |= USBHS_UGCTRL_PLLRESET; + writel(value, base + USBHS_UGCTRL); + + spin_unlock_irqrestore(&drv->lock, flags); + + return 0; +} + +static const struct phy_ops rcar_gen2_phy_ops = { + .init = rcar_gen2_phy_init, + .exit = rcar_gen2_phy_exit, + .power_on = rcar_gen2_phy_power_on, + .power_off = rcar_gen2_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct of_device_id rcar_gen2_phy_match_table[] = { + { .compatible = "renesas,usb-phy-r8a7790" }, + { .compatible = "renesas,usb-phy-r8a7791" }, + { .compatible = "renesas,usb-phy-r8a7794" }, + { .compatible = "renesas,rcar-gen2-usb-phy" }, + { } +}; +MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table); + +static struct phy *rcar_gen2_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct rcar_gen2_phy_driver *drv; + struct device_node *np = args->np; + int i; + + drv = dev_get_drvdata(dev); + if (!drv) + return ERR_PTR(-EINVAL); + + for (i = 0; i < drv->num_channels; i++) { + if (np == drv->channels[i].of_node) + break; + } + + if (i >= drv->num_channels || args->args[0] >= 2) + return ERR_PTR(-ENODEV); + + return drv->channels[i].phys[args->args[0]].phy; +} + +static const u32 select_mask[] = { + [0] = USBHS_UGCTRL2_USB0SEL, + [2] = USBHS_UGCTRL2_USB2SEL, +}; + +static const u32 select_value[][PHYS_PER_CHANNEL] = { + [0] = { USBHS_UGCTRL2_USB0SEL_PCI, USBHS_UGCTRL2_USB0SEL_HS_USB }, + [2] = { USBHS_UGCTRL2_USB2SEL_PCI, USBHS_UGCTRL2_USB2SEL_USB30 }, +}; + +static int rcar_gen2_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rcar_gen2_phy_driver *drv; + struct phy_provider *provider; + struct device_node *np; + struct resource *res; + void __iomem *base; + struct clk *clk; + int i = 0; + + if (!dev->of_node) { + dev_err(dev, + "This driver is required to be instantiated from device tree\n"); + return -EINVAL; + } + + clk = devm_clk_get(dev, "usbhs"); + if (IS_ERR(clk)) { + dev_err(dev, "Can't get USBHS clock\n"); + return PTR_ERR(clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + spin_lock_init(&drv->lock); + + drv->clk = clk; + drv->base = base; + + drv->num_channels = of_get_child_count(dev->of_node); + drv->channels = devm_kcalloc(dev, drv->num_channels, + sizeof(struct rcar_gen2_channel), + GFP_KERNEL); + if (!drv->channels) + return -ENOMEM; + + for_each_child_of_node(dev->of_node, np) { + struct rcar_gen2_channel *channel = drv->channels + i; + u32 channel_num; + int error, n; + + channel->of_node = np; + channel->drv = drv; + channel->selected_phy = -1; + + error = of_property_read_u32(np, "reg", &channel_num); + if (error || channel_num > 2) { + dev_err(dev, "Invalid \"reg\" property\n"); + return error; + } + channel->select_mask = select_mask[channel_num]; + + for (n = 0; n < PHYS_PER_CHANNEL; n++) { + struct rcar_gen2_phy *phy = &channel->phys[n]; + + phy->channel = channel; + phy->number = n; + phy->select_value = select_value[channel_num][n]; + + phy->phy = devm_phy_create(dev, NULL, + &rcar_gen2_phy_ops); + if (IS_ERR(phy->phy)) { + dev_err(dev, "Failed to create PHY\n"); + return PTR_ERR(phy->phy); + } + phy_set_drvdata(phy->phy, phy); + } + + i++; + } + + provider = devm_of_phy_provider_register(dev, rcar_gen2_phy_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "Failed to register PHY provider\n"); + return PTR_ERR(provider); + } + + dev_set_drvdata(dev, drv); + + return 0; +} + +static struct platform_driver rcar_gen2_phy_driver = { + .driver = { + .name = "phy_rcar_gen2", + .of_match_table = rcar_gen2_phy_match_table, + }, + .probe = rcar_gen2_phy_probe, +}; + +module_platform_driver(rcar_gen2_phy_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Renesas R-Car Gen2 PHY"); +MODULE_AUTHOR("Sergei Shtylyov "); diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c new file mode 100644 index 000000000000..54c34298a000 --- /dev/null +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -0,0 +1,507 @@ +/* + * Renesas R-Car Gen3 for USB2.0 PHY driver + * + * Copyright (C) 2015 Renesas Electronics Corporation + * + * This is based on the phy-rcar-gen2 driver: + * Copyright (C) 2014 Renesas Solutions Corp. + * Copyright (C) 2014 Cogent Embedded, Inc. + * + * 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 +#include + +/******* USB2.0 Host registers (original offset is +0x200) *******/ +#define USB2_INT_ENABLE 0x000 +#define USB2_USBCTR 0x00c +#define USB2_SPD_RSM_TIMSET 0x10c +#define USB2_OC_TIMSET 0x110 +#define USB2_COMMCTRL 0x600 +#define USB2_OBINTSTA 0x604 +#define USB2_OBINTEN 0x608 +#define USB2_VBCTRL 0x60c +#define USB2_LINECTRL1 0x610 +#define USB2_ADPCTRL 0x630 + +/* INT_ENABLE */ +#define USB2_INT_ENABLE_UCOM_INTEN BIT(3) +#define USB2_INT_ENABLE_USBH_INTB_EN BIT(2) +#define USB2_INT_ENABLE_USBH_INTA_EN BIT(1) +#define USB2_INT_ENABLE_INIT (USB2_INT_ENABLE_UCOM_INTEN | \ + USB2_INT_ENABLE_USBH_INTB_EN | \ + USB2_INT_ENABLE_USBH_INTA_EN) + +/* USBCTR */ +#define USB2_USBCTR_DIRPD BIT(2) +#define USB2_USBCTR_PLL_RST BIT(1) + +/* SPD_RSM_TIMSET */ +#define USB2_SPD_RSM_TIMSET_INIT 0x014e029b + +/* OC_TIMSET */ +#define USB2_OC_TIMSET_INIT 0x000209ab + +/* COMMCTRL */ +#define USB2_COMMCTRL_OTG_PERI BIT(31) /* 1 = Peripheral mode */ + +/* OBINTSTA and OBINTEN */ +#define USB2_OBINT_SESSVLDCHG BIT(12) +#define USB2_OBINT_IDDIGCHG BIT(11) +#define USB2_OBINT_BITS (USB2_OBINT_SESSVLDCHG | \ + USB2_OBINT_IDDIGCHG) + +/* VBCTRL */ +#define USB2_VBCTRL_DRVVBUSSEL BIT(8) + +/* LINECTRL1 */ +#define USB2_LINECTRL1_DPRPD_EN BIT(19) +#define USB2_LINECTRL1_DP_RPD BIT(18) +#define USB2_LINECTRL1_DMRPD_EN BIT(17) +#define USB2_LINECTRL1_DM_RPD BIT(16) +#define USB2_LINECTRL1_OPMODE_NODRV BIT(6) + +/* ADPCTRL */ +#define USB2_ADPCTRL_OTGSESSVLD BIT(20) +#define USB2_ADPCTRL_IDDIG BIT(19) +#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ +#define USB2_ADPCTRL_DRVVBUS BIT(4) + +struct rcar_gen3_chan { + void __iomem *base; + struct extcon_dev *extcon; + struct phy *phy; + struct regulator *vbus; + struct work_struct work; + bool extcon_host; + bool has_otg; +}; + +static void rcar_gen3_phy_usb2_work(struct work_struct *work) +{ + struct rcar_gen3_chan *ch = container_of(work, struct rcar_gen3_chan, + work); + + if (ch->extcon_host) { + extcon_set_state_sync(ch->extcon, EXTCON_USB_HOST, true); + extcon_set_state_sync(ch->extcon, EXTCON_USB, false); + } else { + extcon_set_state_sync(ch->extcon, EXTCON_USB_HOST, false); + extcon_set_state_sync(ch->extcon, EXTCON_USB, true); + } +} + +static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host) +{ + void __iomem *usb2_base = ch->base; + u32 val = readl(usb2_base + USB2_COMMCTRL); + + dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, host); + if (host) + val &= ~USB2_COMMCTRL_OTG_PERI; + else + val |= USB2_COMMCTRL_OTG_PERI; + writel(val, usb2_base + USB2_COMMCTRL); +} + +static void rcar_gen3_set_linectrl(struct rcar_gen3_chan *ch, int dp, int dm) +{ + void __iomem *usb2_base = ch->base; + u32 val = readl(usb2_base + USB2_LINECTRL1); + + dev_vdbg(&ch->phy->dev, "%s: %08x, %d, %d\n", __func__, val, dp, dm); + val &= ~(USB2_LINECTRL1_DP_RPD | USB2_LINECTRL1_DM_RPD); + if (dp) + val |= USB2_LINECTRL1_DP_RPD; + if (dm) + val |= USB2_LINECTRL1_DM_RPD; + writel(val, usb2_base + USB2_LINECTRL1); +} + +static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus) +{ + void __iomem *usb2_base = ch->base; + u32 val = readl(usb2_base + USB2_ADPCTRL); + + dev_vdbg(&ch->phy->dev, "%s: %08x, %d\n", __func__, val, vbus); + if (vbus) + val |= USB2_ADPCTRL_DRVVBUS; + else + val &= ~USB2_ADPCTRL_DRVVBUS; + writel(val, usb2_base + USB2_ADPCTRL); +} + +static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch) +{ + rcar_gen3_set_linectrl(ch, 1, 1); + rcar_gen3_set_host_mode(ch, 1); + rcar_gen3_enable_vbus_ctrl(ch, 1); + + ch->extcon_host = true; + schedule_work(&ch->work); +} + +static void rcar_gen3_init_for_peri(struct rcar_gen3_chan *ch) +{ + rcar_gen3_set_linectrl(ch, 0, 1); + rcar_gen3_set_host_mode(ch, 0); + rcar_gen3_enable_vbus_ctrl(ch, 0); + + ch->extcon_host = false; + schedule_work(&ch->work); +} + +static void rcar_gen3_init_for_b_host(struct rcar_gen3_chan *ch) +{ + void __iomem *usb2_base = ch->base; + u32 val; + + val = readl(usb2_base + USB2_LINECTRL1); + writel(val | USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1); + + rcar_gen3_set_linectrl(ch, 1, 1); + rcar_gen3_set_host_mode(ch, 1); + rcar_gen3_enable_vbus_ctrl(ch, 0); + + val = readl(usb2_base + USB2_LINECTRL1); + writel(val & ~USB2_LINECTRL1_OPMODE_NODRV, usb2_base + USB2_LINECTRL1); +} + +static void rcar_gen3_init_for_a_peri(struct rcar_gen3_chan *ch) +{ + rcar_gen3_set_linectrl(ch, 0, 1); + rcar_gen3_set_host_mode(ch, 0); + rcar_gen3_enable_vbus_ctrl(ch, 1); +} + +static void rcar_gen3_init_from_a_peri_to_a_host(struct rcar_gen3_chan *ch) +{ + void __iomem *usb2_base = ch->base; + u32 val; + + val = readl(usb2_base + USB2_OBINTEN); + writel(val & ~USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); + + rcar_gen3_enable_vbus_ctrl(ch, 0); + rcar_gen3_init_for_host(ch); + + writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); +} + +static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch) +{ + return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG); +} + +static void rcar_gen3_device_recognition(struct rcar_gen3_chan *ch) +{ + if (!rcar_gen3_check_id(ch)) + rcar_gen3_init_for_host(ch); + else + rcar_gen3_init_for_peri(ch); +} + +static bool rcar_gen3_is_host(struct rcar_gen3_chan *ch) +{ + return !(readl(ch->base + USB2_COMMCTRL) & USB2_COMMCTRL_OTG_PERI); +} + +static ssize_t role_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rcar_gen3_chan *ch = dev_get_drvdata(dev); + bool is_b_device, is_host, new_mode_is_host; + + if (!ch->has_otg || !ch->phy->init_count) + return -EIO; + + /* + * is_b_device: true is B-Device. false is A-Device. + * If {new_mode_}is_host: true is Host mode. false is Peripheral mode. + */ + is_b_device = rcar_gen3_check_id(ch); + is_host = rcar_gen3_is_host(ch); + if (!strncmp(buf, "host", strlen("host"))) + new_mode_is_host = true; + else if (!strncmp(buf, "peripheral", strlen("peripheral"))) + new_mode_is_host = false; + else + return -EINVAL; + + /* If current and new mode is the same, this returns the error */ + if (is_host == new_mode_is_host) + return -EINVAL; + + if (new_mode_is_host) { /* And is_host must be false */ + if (!is_b_device) /* A-Peripheral */ + rcar_gen3_init_from_a_peri_to_a_host(ch); + else /* B-Peripheral */ + rcar_gen3_init_for_b_host(ch); + } else { /* And is_host must be true */ + if (!is_b_device) /* A-Host */ + rcar_gen3_init_for_a_peri(ch); + else /* B-Host */ + rcar_gen3_init_for_peri(ch); + } + + return count; +} + +static ssize_t role_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct rcar_gen3_chan *ch = dev_get_drvdata(dev); + + if (!ch->has_otg || !ch->phy->init_count) + return -EIO; + + return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" : + "peripheral"); +} +static DEVICE_ATTR_RW(role); + +static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) +{ + void __iomem *usb2_base = ch->base; + u32 val; + + val = readl(usb2_base + USB2_VBCTRL); + writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL); + writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); + val = readl(usb2_base + USB2_OBINTEN); + writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN); + val = readl(usb2_base + USB2_ADPCTRL); + writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL); + val = readl(usb2_base + USB2_LINECTRL1); + rcar_gen3_set_linectrl(ch, 0, 0); + writel(val | USB2_LINECTRL1_DPRPD_EN | USB2_LINECTRL1_DMRPD_EN, + usb2_base + USB2_LINECTRL1); + + rcar_gen3_device_recognition(ch); +} + +static int rcar_gen3_phy_usb2_init(struct phy *p) +{ + struct rcar_gen3_chan *channel = phy_get_drvdata(p); + void __iomem *usb2_base = channel->base; + + /* Initialize USB2 part */ + writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE); + writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET); + writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET); + + /* Initialize otg part */ + if (channel->has_otg) + rcar_gen3_init_otg(channel); + + return 0; +} + +static int rcar_gen3_phy_usb2_exit(struct phy *p) +{ + struct rcar_gen3_chan *channel = phy_get_drvdata(p); + + writel(0, channel->base + USB2_INT_ENABLE); + + return 0; +} + +static int rcar_gen3_phy_usb2_power_on(struct phy *p) +{ + struct rcar_gen3_chan *channel = phy_get_drvdata(p); + void __iomem *usb2_base = channel->base; + u32 val; + int ret; + + if (channel->vbus) { + ret = regulator_enable(channel->vbus); + if (ret) + return ret; + } + + val = readl(usb2_base + USB2_USBCTR); + val |= USB2_USBCTR_PLL_RST; + writel(val, usb2_base + USB2_USBCTR); + val &= ~USB2_USBCTR_PLL_RST; + writel(val, usb2_base + USB2_USBCTR); + + return 0; +} + +static int rcar_gen3_phy_usb2_power_off(struct phy *p) +{ + struct rcar_gen3_chan *channel = phy_get_drvdata(p); + int ret = 0; + + if (channel->vbus) + ret = regulator_disable(channel->vbus); + + return ret; +} + +static const struct phy_ops rcar_gen3_phy_usb2_ops = { + .init = rcar_gen3_phy_usb2_init, + .exit = rcar_gen3_phy_usb2_exit, + .power_on = rcar_gen3_phy_usb2_power_on, + .power_off = rcar_gen3_phy_usb2_power_off, + .owner = THIS_MODULE, +}; + +static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) +{ + struct rcar_gen3_chan *ch = _ch; + void __iomem *usb2_base = ch->base; + u32 status = readl(usb2_base + USB2_OBINTSTA); + irqreturn_t ret = IRQ_NONE; + + if (status & USB2_OBINT_BITS) { + dev_vdbg(&ch->phy->dev, "%s: %08x\n", __func__, status); + writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA); + rcar_gen3_device_recognition(ch); + ret = IRQ_HANDLED; + } + + return ret; +} + +static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { + { .compatible = "renesas,usb2-phy-r8a7795" }, + { .compatible = "renesas,usb2-phy-r8a7796" }, + { .compatible = "renesas,rcar-gen3-usb2-phy" }, + { } +}; +MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table); + +static const unsigned int rcar_gen3_phy_cable[] = { + EXTCON_USB, + EXTCON_USB_HOST, + EXTCON_NONE, +}; + +static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rcar_gen3_chan *channel; + struct phy_provider *provider; + struct resource *res; + int irq, ret = 0; + + if (!dev->of_node) { + dev_err(dev, "This driver needs device tree\n"); + return -EINVAL; + } + + channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL); + if (!channel) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + channel->base = devm_ioremap_resource(dev, res); + if (IS_ERR(channel->base)) + return PTR_ERR(channel->base); + + /* call request_irq for OTG */ + irq = platform_get_irq(pdev, 0); + if (irq >= 0) { + int ret; + + INIT_WORK(&channel->work, rcar_gen3_phy_usb2_work); + irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq, + IRQF_SHARED, dev_name(dev), channel); + if (irq < 0) + dev_err(dev, "No irq handler (%d)\n", irq); + channel->has_otg = true; + channel->extcon = devm_extcon_dev_allocate(dev, + rcar_gen3_phy_cable); + if (IS_ERR(channel->extcon)) + return PTR_ERR(channel->extcon); + + ret = devm_extcon_dev_register(dev, channel->extcon); + if (ret < 0) { + dev_err(dev, "Failed to register extcon\n"); + return ret; + } + } + + /* + * devm_phy_create() will call pm_runtime_enable(&phy->dev); + * And then, phy-core will manage runtime pm for this device. + */ + pm_runtime_enable(dev); + channel->phy = devm_phy_create(dev, NULL, &rcar_gen3_phy_usb2_ops); + if (IS_ERR(channel->phy)) { + dev_err(dev, "Failed to create USB2 PHY\n"); + ret = PTR_ERR(channel->phy); + goto error; + } + + channel->vbus = devm_regulator_get_optional(dev, "vbus"); + if (IS_ERR(channel->vbus)) { + if (PTR_ERR(channel->vbus) == -EPROBE_DEFER) { + ret = PTR_ERR(channel->vbus); + goto error; + } + channel->vbus = NULL; + } + + platform_set_drvdata(pdev, channel); + phy_set_drvdata(channel->phy, channel); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "Failed to register PHY provider\n"); + ret = PTR_ERR(provider); + goto error; + } else if (channel->has_otg) { + int ret; + + ret = device_create_file(dev, &dev_attr_role); + if (ret < 0) + goto error; + } + + return 0; + +error: + pm_runtime_disable(dev); + + return ret; +} + +static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev) +{ + struct rcar_gen3_chan *channel = platform_get_drvdata(pdev); + + if (channel->has_otg) + device_remove_file(&pdev->dev, &dev_attr_role); + + pm_runtime_disable(&pdev->dev); + + return 0; +}; + +static struct platform_driver rcar_gen3_phy_usb2_driver = { + .driver = { + .name = "phy_rcar_gen3_usb2", + .of_match_table = rcar_gen3_phy_usb2_match_table, + }, + .probe = rcar_gen3_phy_usb2_probe, + .remove = rcar_gen3_phy_usb2_remove, +}; +module_platform_driver(rcar_gen3_phy_usb2_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Renesas R-Car Gen3 USB 2.0 PHY"); +MODULE_AUTHOR("Yoshihiro Shimoda "); diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig new file mode 100644 index 000000000000..f5325b2b679e --- /dev/null +++ b/drivers/phy/rockchip/Kconfig @@ -0,0 +1,51 @@ +# +# Phy drivers for Rockchip platforms +# +config PHY_ROCKCHIP_DP + tristate "Rockchip Display Port PHY Driver" + depends on ARCH_ROCKCHIP && OF + select GENERIC_PHY + help + Enable this to support the Rockchip Display Port PHY. + +config PHY_ROCKCHIP_EMMC + tristate "Rockchip EMMC PHY Driver" + depends on ARCH_ROCKCHIP && OF + select GENERIC_PHY + help + Enable this to support the Rockchip EMMC PHY. + +config PHY_ROCKCHIP_INNO_USB2 + tristate "Rockchip INNO USB2PHY Driver" + depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF + depends on COMMON_CLK + depends on EXTCON + depends on USB_SUPPORT + select GENERIC_PHY + select USB_COMMON + help + Support for Rockchip USB2.0 PHY with Innosilicon IP block. + +config PHY_ROCKCHIP_PCIE + tristate "Rockchip PCIe PHY Driver" + depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST + select GENERIC_PHY + select MFD_SYSCON + help + Enable this to support the Rockchip PCIe PHY. + +config PHY_ROCKCHIP_TYPEC + tristate "Rockchip TYPEC PHY Driver" + depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST) + select EXTCON + select GENERIC_PHY + select RESET_CONTROLLER + help + Enable this to support the Rockchip USB TYPEC PHY. + +config PHY_ROCKCHIP_USB + tristate "Rockchip USB2 PHY Driver" + depends on ARCH_ROCKCHIP && OF + select GENERIC_PHY + help + Enable this to support the Rockchip USB 2.0 PHY. diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile new file mode 100644 index 000000000000..bd0acdf38e0f --- /dev/null +++ b/drivers/phy/rockchip/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o +obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o +obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o +obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o +obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o +obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o diff --git a/drivers/phy/rockchip/phy-rockchip-dp.c b/drivers/phy/rockchip/phy-rockchip-dp.c new file mode 100644 index 000000000000..8b267a746576 --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-dp.c @@ -0,0 +1,154 @@ +/* + * Rockchip DP PHY driver + * + * Copyright (C) 2016 FuZhou Rockchip Co., Ltd. + * Author: Yakir Yang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define GRF_SOC_CON12 0x0274 + +#define GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK BIT(20) +#define GRF_EDP_REF_CLK_SEL_INTER BIT(4) + +#define GRF_EDP_PHY_SIDDQ_HIWORD_MASK BIT(21) +#define GRF_EDP_PHY_SIDDQ_ON 0 +#define GRF_EDP_PHY_SIDDQ_OFF BIT(5) + +struct rockchip_dp_phy { + struct device *dev; + struct regmap *grf; + struct clk *phy_24m; +}; + +static int rockchip_set_phy_state(struct phy *phy, bool enable) +{ + struct rockchip_dp_phy *dp = phy_get_drvdata(phy); + int ret; + + if (enable) { + ret = regmap_write(dp->grf, GRF_SOC_CON12, + GRF_EDP_PHY_SIDDQ_HIWORD_MASK | + GRF_EDP_PHY_SIDDQ_ON); + if (ret < 0) { + dev_err(dp->dev, "Can't enable PHY power %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(dp->phy_24m); + } else { + clk_disable_unprepare(dp->phy_24m); + + ret = regmap_write(dp->grf, GRF_SOC_CON12, + GRF_EDP_PHY_SIDDQ_HIWORD_MASK | + GRF_EDP_PHY_SIDDQ_OFF); + } + + return ret; +} + +static int rockchip_dp_phy_power_on(struct phy *phy) +{ + return rockchip_set_phy_state(phy, true); +} + +static int rockchip_dp_phy_power_off(struct phy *phy) +{ + return rockchip_set_phy_state(phy, false); +} + +static const struct phy_ops rockchip_dp_phy_ops = { + .power_on = rockchip_dp_phy_power_on, + .power_off = rockchip_dp_phy_power_off, + .owner = THIS_MODULE, +}; + +static int rockchip_dp_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct phy_provider *phy_provider; + struct rockchip_dp_phy *dp; + struct phy *phy; + int ret; + + if (!np) + return -ENODEV; + + if (!dev->parent || !dev->parent->of_node) + return -ENODEV; + + dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); + if (!dp) + return -ENOMEM; + + dp->dev = dev; + + dp->phy_24m = devm_clk_get(dev, "24m"); + if (IS_ERR(dp->phy_24m)) { + dev_err(dev, "cannot get clock 24m\n"); + return PTR_ERR(dp->phy_24m); + } + + ret = clk_set_rate(dp->phy_24m, 24000000); + if (ret < 0) { + dev_err(dp->dev, "cannot set clock phy_24m %d\n", ret); + return ret; + } + + dp->grf = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(dp->grf)) { + dev_err(dev, "rk3288-dp needs the General Register Files syscon\n"); + return PTR_ERR(dp->grf); + } + + ret = regmap_write(dp->grf, GRF_SOC_CON12, GRF_EDP_REF_CLK_SEL_INTER | + GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK); + if (ret != 0) { + dev_err(dp->dev, "Could not config GRF edp ref clk: %d\n", ret); + return ret; + } + + phy = devm_phy_create(dev, np, &rockchip_dp_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create phy\n"); + return PTR_ERR(phy); + } + phy_set_drvdata(phy, dp); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id rockchip_dp_phy_dt_ids[] = { + { .compatible = "rockchip,rk3288-dp-phy" }, + {} +}; + +MODULE_DEVICE_TABLE(of, rockchip_dp_phy_dt_ids); + +static struct platform_driver rockchip_dp_phy_driver = { + .probe = rockchip_dp_phy_probe, + .driver = { + .name = "rockchip-dp-phy", + .of_match_table = rockchip_dp_phy_dt_ids, + }, +}; + +module_platform_driver(rockchip_dp_phy_driver); + +MODULE_AUTHOR("Yakir Yang "); +MODULE_DESCRIPTION("Rockchip DP PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/rockchip/phy-rockchip-emmc.c b/drivers/phy/rockchip/phy-rockchip-emmc.c new file mode 100644 index 000000000000..f1b24f18e9b2 --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-emmc.c @@ -0,0 +1,383 @@ +/* + * Rockchip emmc PHY driver + * + * Copyright (C) 2016 Shawn Lin + * Copyright (C) 2016 ROCKCHIP, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The higher 16-bit of this register is used for write protection + * only if BIT(x + 16) set to 1 the BIT(x) can be written. + */ +#define HIWORD_UPDATE(val, mask, shift) \ + ((val) << (shift) | (mask) << ((shift) + 16)) + +/* Register definition */ +#define GRF_EMMCPHY_CON0 0x0 +#define GRF_EMMCPHY_CON1 0x4 +#define GRF_EMMCPHY_CON2 0x8 +#define GRF_EMMCPHY_CON3 0xc +#define GRF_EMMCPHY_CON4 0x10 +#define GRF_EMMCPHY_CON5 0x14 +#define GRF_EMMCPHY_CON6 0x18 +#define GRF_EMMCPHY_STATUS 0x20 + +#define PHYCTRL_PDB_MASK 0x1 +#define PHYCTRL_PDB_SHIFT 0x0 +#define PHYCTRL_PDB_PWR_ON 0x1 +#define PHYCTRL_PDB_PWR_OFF 0x0 +#define PHYCTRL_ENDLL_MASK 0x1 +#define PHYCTRL_ENDLL_SHIFT 0x1 +#define PHYCTRL_ENDLL_ENABLE 0x1 +#define PHYCTRL_ENDLL_DISABLE 0x0 +#define PHYCTRL_CALDONE_MASK 0x1 +#define PHYCTRL_CALDONE_SHIFT 0x6 +#define PHYCTRL_CALDONE_DONE 0x1 +#define PHYCTRL_CALDONE_GOING 0x0 +#define PHYCTRL_DLLRDY_MASK 0x1 +#define PHYCTRL_DLLRDY_SHIFT 0x5 +#define PHYCTRL_DLLRDY_DONE 0x1 +#define PHYCTRL_DLLRDY_GOING 0x0 +#define PHYCTRL_FREQSEL_200M 0x0 +#define PHYCTRL_FREQSEL_50M 0x1 +#define PHYCTRL_FREQSEL_100M 0x2 +#define PHYCTRL_FREQSEL_150M 0x3 +#define PHYCTRL_FREQSEL_MASK 0x3 +#define PHYCTRL_FREQSEL_SHIFT 0xc +#define PHYCTRL_DR_MASK 0x7 +#define PHYCTRL_DR_SHIFT 0x4 +#define PHYCTRL_DR_50OHM 0x0 +#define PHYCTRL_DR_33OHM 0x1 +#define PHYCTRL_DR_66OHM 0x2 +#define PHYCTRL_DR_100OHM 0x3 +#define PHYCTRL_DR_40OHM 0x4 +#define PHYCTRL_OTAPDLYENA 0x1 +#define PHYCTRL_OTAPDLYENA_MASK 0x1 +#define PHYCTRL_OTAPDLYENA_SHIFT 0xb +#define PHYCTRL_OTAPDLYSEL_MASK 0xf +#define PHYCTRL_OTAPDLYSEL_SHIFT 0x7 + +struct rockchip_emmc_phy { + unsigned int reg_offset; + struct regmap *reg_base; + struct clk *emmcclk; +}; + +static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) +{ + struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); + unsigned int caldone; + unsigned int dllrdy; + unsigned int freqsel = PHYCTRL_FREQSEL_200M; + unsigned long rate; + unsigned long timeout; + + /* + * Keep phyctrl_pdb and phyctrl_endll low to allow + * initialization of CALIO state M/C DFFs + */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON6, + HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF, + PHYCTRL_PDB_MASK, + PHYCTRL_PDB_SHIFT)); + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON6, + HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE, + PHYCTRL_ENDLL_MASK, + PHYCTRL_ENDLL_SHIFT)); + + /* Already finish power_off above */ + if (on_off == PHYCTRL_PDB_PWR_OFF) + return 0; + + rate = clk_get_rate(rk_phy->emmcclk); + + if (rate != 0) { + unsigned long ideal_rate; + unsigned long diff; + + switch (rate) { + case 1 ... 74999999: + ideal_rate = 50000000; + freqsel = PHYCTRL_FREQSEL_50M; + break; + case 75000000 ... 124999999: + ideal_rate = 100000000; + freqsel = PHYCTRL_FREQSEL_100M; + break; + case 125000000 ... 174999999: + ideal_rate = 150000000; + freqsel = PHYCTRL_FREQSEL_150M; + break; + default: + ideal_rate = 200000000; + break; + } + + diff = (rate > ideal_rate) ? + rate - ideal_rate : ideal_rate - rate; + + /* + * In order for tuning delays to be accurate we need to be + * pretty spot on for the DLL range, so warn if we're too + * far off. Also warn if we're above the 200 MHz max. Don't + * warn for really slow rates since we won't be tuning then. + */ + if ((rate > 50000000 && diff > 15000000) || (rate > 200000000)) + dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate); + } + + /* + * According to the user manual, calpad calibration + * cycle takes more than 2us without the minimal recommended + * value, so we may need a little margin here + */ + udelay(3); + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON6, + HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON, + PHYCTRL_PDB_MASK, + PHYCTRL_PDB_SHIFT)); + + /* + * According to the user manual, it asks driver to + * wait 5us for calpad busy trimming + */ + udelay(5); + regmap_read(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_STATUS, + &caldone); + caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK; + if (caldone != PHYCTRL_CALDONE_DONE) { + pr_err("rockchip_emmc_phy_power: caldone timeout.\n"); + return -ETIMEDOUT; + } + + /* Set the frequency of the DLL operation */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(freqsel, PHYCTRL_FREQSEL_MASK, + PHYCTRL_FREQSEL_SHIFT)); + + /* Turn on the DLL */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON6, + HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE, + PHYCTRL_ENDLL_MASK, + PHYCTRL_ENDLL_SHIFT)); + + /* + * We turned on the DLL even though the rate was 0 because we the + * clock might be turned on later. ...but we can't wait for the DLL + * to lock when the rate is 0 because it will never lock with no + * input clock. + * + * Technically we should be checking the lock later when the clock + * is turned on, but for now we won't. + */ + if (rate == 0) + return 0; + + /* + * After enabling analog DLL circuits docs say that we need 10.2 us if + * our source clock is at 50 MHz and that lock time scales linearly + * with clock speed. If we are powering on the PHY and the card clock + * is super slow (like 100 kHZ) this could take as long as 5.1 ms as + * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms + * Hopefully we won't be running at 100 kHz, but we should still make + * sure we wait long enough. + * + * NOTE: There appear to be corner cases where the DLL seems to take + * extra long to lock for reasons that aren't understood. In some + * extreme cases we've seen it take up to over 10ms (!). We'll be + * generous and give it 50ms. We still busy wait here because: + * - In most cases it should be super fast. + * - This is not called lots during normal operation so it shouldn't + * be a power or performance problem to busy wait. We expect it + * only at boot / resume. In both cases, eMMC is probably on the + * critical path so busy waiting a little extra time should be OK. + */ + timeout = jiffies + msecs_to_jiffies(50); + do { + udelay(1); + + regmap_read(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_STATUS, + &dllrdy); + dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; + if (dllrdy == PHYCTRL_DLLRDY_DONE) + break; + } while (!time_after(jiffies, timeout)); + + if (dllrdy != PHYCTRL_DLLRDY_DONE) { + pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int rockchip_emmc_phy_init(struct phy *phy) +{ + struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); + int ret = 0; + + /* + * We purposely get the clock here and not in probe to avoid the + * circular dependency problem. We expect: + * - PHY driver to probe + * - SDHCI driver to start probe + * - SDHCI driver to register it's clock + * - SDHCI driver to get the PHY + * - SDHCI driver to init the PHY + * + * The clock is optional, so upon any error we just set to NULL. + * + * NOTE: we don't do anything special for EPROBE_DEFER here. Given the + * above expected use case, EPROBE_DEFER isn't sensible to expect, so + * it's just like any other error. + */ + rk_phy->emmcclk = clk_get(&phy->dev, "emmcclk"); + if (IS_ERR(rk_phy->emmcclk)) { + dev_dbg(&phy->dev, "Error getting emmcclk: %d\n", ret); + rk_phy->emmcclk = NULL; + } + + return ret; +} + +static int rockchip_emmc_phy_exit(struct phy *phy) +{ + struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); + + clk_put(rk_phy->emmcclk); + + return 0; +} + +static int rockchip_emmc_phy_power_off(struct phy *phy) +{ + /* Power down emmc phy analog blocks */ + return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_OFF); +} + +static int rockchip_emmc_phy_power_on(struct phy *phy) +{ + struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); + + /* Drive impedance: 50 Ohm */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON6, + HIWORD_UPDATE(PHYCTRL_DR_50OHM, + PHYCTRL_DR_MASK, + PHYCTRL_DR_SHIFT)); + + /* Output tap delay: enable */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(PHYCTRL_OTAPDLYENA, + PHYCTRL_OTAPDLYENA_MASK, + PHYCTRL_OTAPDLYENA_SHIFT)); + + /* Output tap delay */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(4, + PHYCTRL_OTAPDLYSEL_MASK, + PHYCTRL_OTAPDLYSEL_SHIFT)); + + /* Power up emmc phy analog blocks */ + return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON); +} + +static const struct phy_ops ops = { + .init = rockchip_emmc_phy_init, + .exit = rockchip_emmc_phy_exit, + .power_on = rockchip_emmc_phy_power_on, + .power_off = rockchip_emmc_phy_power_off, + .owner = THIS_MODULE, +}; + +static int rockchip_emmc_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rockchip_emmc_phy *rk_phy; + struct phy *generic_phy; + struct phy_provider *phy_provider; + struct regmap *grf; + unsigned int reg_offset; + + if (!dev->parent || !dev->parent->of_node) + return -ENODEV; + + grf = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(grf)) { + dev_err(dev, "Missing rockchip,grf property\n"); + return PTR_ERR(grf); + } + + rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL); + if (!rk_phy) + return -ENOMEM; + + if (of_property_read_u32(dev->of_node, "reg", ®_offset)) { + dev_err(dev, "missing reg property in node %s\n", + dev->of_node->name); + return -EINVAL; + } + + rk_phy->reg_offset = reg_offset; + rk_phy->reg_base = grf; + + generic_phy = devm_phy_create(dev, dev->of_node, &ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, rk_phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id rockchip_emmc_phy_dt_ids[] = { + { .compatible = "rockchip,rk3399-emmc-phy" }, + {} +}; + +MODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids); + +static struct platform_driver rockchip_emmc_driver = { + .probe = rockchip_emmc_phy_probe, + .driver = { + .name = "rockchip-emmc-phy", + .of_match_table = rockchip_emmc_phy_dt_ids, + }, +}; + +module_platform_driver(rockchip_emmc_driver); + +MODULE_AUTHOR("Shawn Lin "); +MODULE_DESCRIPTION("Rockchip EMMC PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c new file mode 100644 index 000000000000..8efe78a49916 --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -0,0 +1,1284 @@ +/* + * Rockchip USB2.0 PHY with Innosilicon IP block driver + * + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BIT_WRITEABLE_SHIFT 16 +#define SCHEDULE_DELAY (60 * HZ) +#define OTG_SCHEDULE_DELAY (2 * HZ) + +enum rockchip_usb2phy_port_id { + USB2PHY_PORT_OTG, + USB2PHY_PORT_HOST, + USB2PHY_NUM_PORTS, +}; + +enum rockchip_usb2phy_host_state { + PHY_STATE_HS_ONLINE = 0, + PHY_STATE_DISCONNECT = 1, + PHY_STATE_CONNECT = 2, + PHY_STATE_FS_LS_ONLINE = 4, +}; + +/** + * Different states involved in USB charger detection. + * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection + * process is not yet started. + * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact. + * USB_CHG_STATE_DCD_DONE Data pin contact is detected. + * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects + * between SDP and DCP/CDP). + * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects + * between DCP and CDP). + * USB_CHG_STATE_DETECTED USB charger type is determined. + */ +enum usb_chg_state { + USB_CHG_STATE_UNDEFINED = 0, + USB_CHG_STATE_WAIT_FOR_DCD, + USB_CHG_STATE_DCD_DONE, + USB_CHG_STATE_PRIMARY_DONE, + USB_CHG_STATE_SECONDARY_DONE, + USB_CHG_STATE_DETECTED, +}; + +static const unsigned int rockchip_usb2phy_extcon_cable[] = { + EXTCON_USB, + EXTCON_USB_HOST, + EXTCON_CHG_USB_SDP, + EXTCON_CHG_USB_CDP, + EXTCON_CHG_USB_DCP, + EXTCON_CHG_USB_SLOW, + EXTCON_NONE, +}; + +struct usb2phy_reg { + unsigned int offset; + unsigned int bitend; + unsigned int bitstart; + unsigned int disable; + unsigned int enable; +}; + +/** + * struct rockchip_chg_det_reg: usb charger detect registers + * @cp_det: charging port detected successfully. + * @dcp_det: dedicated charging port detected successfully. + * @dp_det: assert data pin connect successfully. + * @idm_sink_en: open dm sink curren. + * @idp_sink_en: open dp sink current. + * @idp_src_en: open dm source current. + * @rdm_pdwn_en: open dm pull down resistor. + * @vdm_src_en: open dm voltage source. + * @vdp_src_en: open dp voltage source. + * @opmode: utmi operational mode. + */ +struct rockchip_chg_det_reg { + struct usb2phy_reg cp_det; + struct usb2phy_reg dcp_det; + struct usb2phy_reg dp_det; + struct usb2phy_reg idm_sink_en; + struct usb2phy_reg idp_sink_en; + struct usb2phy_reg idp_src_en; + struct usb2phy_reg rdm_pdwn_en; + struct usb2phy_reg vdm_src_en; + struct usb2phy_reg vdp_src_en; + struct usb2phy_reg opmode; +}; + +/** + * struct rockchip_usb2phy_port_cfg: usb-phy port configuration. + * @phy_sus: phy suspend register. + * @bvalid_det_en: vbus valid rise detection enable register. + * @bvalid_det_st: vbus valid rise detection status register. + * @bvalid_det_clr: vbus valid rise detection clear register. + * @ls_det_en: linestate detection enable register. + * @ls_det_st: linestate detection state register. + * @ls_det_clr: linestate detection clear register. + * @utmi_avalid: utmi vbus avalid status register. + * @utmi_bvalid: utmi vbus bvalid status register. + * @utmi_ls: utmi linestate state register. + * @utmi_hstdet: utmi host disconnect register. + */ +struct rockchip_usb2phy_port_cfg { + struct usb2phy_reg phy_sus; + struct usb2phy_reg bvalid_det_en; + struct usb2phy_reg bvalid_det_st; + struct usb2phy_reg bvalid_det_clr; + struct usb2phy_reg ls_det_en; + struct usb2phy_reg ls_det_st; + struct usb2phy_reg ls_det_clr; + struct usb2phy_reg utmi_avalid; + struct usb2phy_reg utmi_bvalid; + struct usb2phy_reg utmi_ls; + struct usb2phy_reg utmi_hstdet; +}; + +/** + * struct rockchip_usb2phy_cfg: usb-phy configuration. + * @reg: the address offset of grf for usb-phy config. + * @num_ports: specify how many ports that the phy has. + * @clkout_ctl: keep on/turn off output clk of phy. + * @chg_det: charger detection registers. + */ +struct rockchip_usb2phy_cfg { + unsigned int reg; + unsigned int num_ports; + struct usb2phy_reg clkout_ctl; + const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS]; + const struct rockchip_chg_det_reg chg_det; +}; + +/** + * struct rockchip_usb2phy_port: usb-phy port data. + * @port_id: flag for otg port or host port. + * @suspended: phy suspended flag. + * @utmi_avalid: utmi avalid status usage flag. + * true - use avalid to get vbus status + * flase - use bvalid to get vbus status + * @vbus_attached: otg device vbus status. + * @bvalid_irq: IRQ number assigned for vbus valid rise detection. + * @ls_irq: IRQ number assigned for linestate detection. + * @mutex: for register updating in sm_work. + * @chg_work: charge detect work. + * @otg_sm_work: OTG state machine work. + * @sm_work: HOST state machine work. + * @phy_cfg: port register configuration, assigned by driver data. + * @event_nb: hold event notification callback. + * @state: define OTG enumeration states before device reset. + * @mode: the dr_mode of the controller. + */ +struct rockchip_usb2phy_port { + struct phy *phy; + unsigned int port_id; + bool suspended; + bool utmi_avalid; + bool vbus_attached; + int bvalid_irq; + int ls_irq; + struct mutex mutex; + struct delayed_work chg_work; + struct delayed_work otg_sm_work; + struct delayed_work sm_work; + const struct rockchip_usb2phy_port_cfg *port_cfg; + struct notifier_block event_nb; + enum usb_otg_state state; + enum usb_dr_mode mode; +}; + +/** + * struct rockchip_usb2phy: usb2.0 phy driver data. + * @grf: General Register Files regmap. + * @clk: clock struct of phy input clk. + * @clk480m: clock struct of phy output clk. + * @clk_hw: clock struct of phy output clk management. + * @chg_state: states involved in USB charger detection. + * @chg_type: USB charger types. + * @dcd_retries: The retry count used to track Data contact + * detection process. + * @edev: extcon device for notification registration + * @phy_cfg: phy register configuration, assigned by driver data. + * @ports: phy port instance. + */ +struct rockchip_usb2phy { + struct device *dev; + struct regmap *grf; + struct clk *clk; + struct clk *clk480m; + struct clk_hw clk480m_hw; + enum usb_chg_state chg_state; + enum power_supply_type chg_type; + u8 dcd_retries; + struct extcon_dev *edev; + const struct rockchip_usb2phy_cfg *phy_cfg; + struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS]; +}; + +static inline int property_enable(struct rockchip_usb2phy *rphy, + const struct usb2phy_reg *reg, bool en) +{ + unsigned int val, mask, tmp; + + tmp = en ? reg->enable : reg->disable; + mask = GENMASK(reg->bitend, reg->bitstart); + val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT); + + return regmap_write(rphy->grf, reg->offset, val); +} + +static inline bool property_enabled(struct rockchip_usb2phy *rphy, + const struct usb2phy_reg *reg) +{ + int ret; + unsigned int tmp, orig; + unsigned int mask = GENMASK(reg->bitend, reg->bitstart); + + ret = regmap_read(rphy->grf, reg->offset, &orig); + if (ret) + return false; + + tmp = (orig & mask) >> reg->bitstart; + return tmp == reg->enable; +} + +static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) +{ + struct rockchip_usb2phy *rphy = + container_of(hw, struct rockchip_usb2phy, clk480m_hw); + int ret; + + /* turn on 480m clk output if it is off */ + if (!property_enabled(rphy, &rphy->phy_cfg->clkout_ctl)) { + ret = property_enable(rphy, &rphy->phy_cfg->clkout_ctl, true); + if (ret) + return ret; + + /* waiting for the clk become stable */ + usleep_range(1200, 1300); + } + + return 0; +} + +static void rockchip_usb2phy_clk480m_unprepare(struct clk_hw *hw) +{ + struct rockchip_usb2phy *rphy = + container_of(hw, struct rockchip_usb2phy, clk480m_hw); + + /* turn off 480m clk output */ + property_enable(rphy, &rphy->phy_cfg->clkout_ctl, false); +} + +static int rockchip_usb2phy_clk480m_prepared(struct clk_hw *hw) +{ + struct rockchip_usb2phy *rphy = + container_of(hw, struct rockchip_usb2phy, clk480m_hw); + + return property_enabled(rphy, &rphy->phy_cfg->clkout_ctl); +} + +static unsigned long +rockchip_usb2phy_clk480m_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 480000000; +} + +static const struct clk_ops rockchip_usb2phy_clkout_ops = { + .prepare = rockchip_usb2phy_clk480m_prepare, + .unprepare = rockchip_usb2phy_clk480m_unprepare, + .is_prepared = rockchip_usb2phy_clk480m_prepared, + .recalc_rate = rockchip_usb2phy_clk480m_recalc_rate, +}; + +static void rockchip_usb2phy_clk480m_unregister(void *data) +{ + struct rockchip_usb2phy *rphy = data; + + of_clk_del_provider(rphy->dev->of_node); + clk_unregister(rphy->clk480m); +} + +static int +rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy) +{ + struct device_node *node = rphy->dev->of_node; + struct clk_init_data init; + const char *clk_name; + int ret; + + init.flags = 0; + init.name = "clk_usbphy_480m"; + init.ops = &rockchip_usb2phy_clkout_ops; + + /* optional override of the clockname */ + of_property_read_string(node, "clock-output-names", &init.name); + + if (rphy->clk) { + clk_name = __clk_get_name(rphy->clk); + init.parent_names = &clk_name; + init.num_parents = 1; + } else { + init.parent_names = NULL; + init.num_parents = 0; + } + + rphy->clk480m_hw.init = &init; + + /* register the clock */ + rphy->clk480m = clk_register(rphy->dev, &rphy->clk480m_hw); + if (IS_ERR(rphy->clk480m)) { + ret = PTR_ERR(rphy->clk480m); + goto err_ret; + } + + ret = of_clk_add_provider(node, of_clk_src_simple_get, rphy->clk480m); + if (ret < 0) + goto err_clk_provider; + + ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister, + rphy); + if (ret < 0) + goto err_unreg_action; + + return 0; + +err_unreg_action: + of_clk_del_provider(node); +err_clk_provider: + clk_unregister(rphy->clk480m); +err_ret: + return ret; +} + +static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy) +{ + int ret; + struct device_node *node = rphy->dev->of_node; + struct extcon_dev *edev; + + if (of_property_read_bool(node, "extcon")) { + edev = extcon_get_edev_by_phandle(rphy->dev, 0); + if (IS_ERR(edev)) { + if (PTR_ERR(edev) != -EPROBE_DEFER) + dev_err(rphy->dev, "Invalid or missing extcon\n"); + return PTR_ERR(edev); + } + } else { + /* Initialize extcon device */ + edev = devm_extcon_dev_allocate(rphy->dev, + rockchip_usb2phy_extcon_cable); + + if (IS_ERR(edev)) + return -ENOMEM; + + ret = devm_extcon_dev_register(rphy->dev, edev); + if (ret) { + dev_err(rphy->dev, "failed to register extcon device\n"); + return ret; + } + } + + rphy->edev = edev; + + return 0; +} + +static int rockchip_usb2phy_init(struct phy *phy) +{ + struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); + struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); + int ret = 0; + + mutex_lock(&rport->mutex); + + if (rport->port_id == USB2PHY_PORT_OTG) { + if (rport->mode != USB_DR_MODE_HOST) { + /* clear bvalid status and enable bvalid detect irq */ + ret = property_enable(rphy, + &rport->port_cfg->bvalid_det_clr, + true); + if (ret) + goto out; + + ret = property_enable(rphy, + &rport->port_cfg->bvalid_det_en, + true); + if (ret) + goto out; + + schedule_delayed_work(&rport->otg_sm_work, + OTG_SCHEDULE_DELAY); + } else { + /* If OTG works in host only mode, do nothing. */ + dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode); + } + } else if (rport->port_id == USB2PHY_PORT_HOST) { + /* clear linestate and enable linestate detect irq */ + ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true); + if (ret) + goto out; + + ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true); + if (ret) + goto out; + + schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY); + } + +out: + mutex_unlock(&rport->mutex); + return ret; +} + +static int rockchip_usb2phy_power_on(struct phy *phy) +{ + struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); + struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); + int ret; + + dev_dbg(&rport->phy->dev, "port power on\n"); + + if (!rport->suspended) + return 0; + + ret = clk_prepare_enable(rphy->clk480m); + if (ret) + return ret; + + ret = property_enable(rphy, &rport->port_cfg->phy_sus, false); + if (ret) + return ret; + + rport->suspended = false; + return 0; +} + +static int rockchip_usb2phy_power_off(struct phy *phy) +{ + struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); + struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent); + int ret; + + dev_dbg(&rport->phy->dev, "port power off\n"); + + if (rport->suspended) + return 0; + + ret = property_enable(rphy, &rport->port_cfg->phy_sus, true); + if (ret) + return ret; + + rport->suspended = true; + clk_disable_unprepare(rphy->clk480m); + + return 0; +} + +static int rockchip_usb2phy_exit(struct phy *phy) +{ + struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); + + if (rport->port_id == USB2PHY_PORT_OTG && + rport->mode != USB_DR_MODE_HOST) { + cancel_delayed_work_sync(&rport->otg_sm_work); + cancel_delayed_work_sync(&rport->chg_work); + } else if (rport->port_id == USB2PHY_PORT_HOST) + cancel_delayed_work_sync(&rport->sm_work); + + return 0; +} + +static const struct phy_ops rockchip_usb2phy_ops = { + .init = rockchip_usb2phy_init, + .exit = rockchip_usb2phy_exit, + .power_on = rockchip_usb2phy_power_on, + .power_off = rockchip_usb2phy_power_off, + .owner = THIS_MODULE, +}; + +static void rockchip_usb2phy_otg_sm_work(struct work_struct *work) +{ + struct rockchip_usb2phy_port *rport = + container_of(work, struct rockchip_usb2phy_port, + otg_sm_work.work); + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + static unsigned int cable; + unsigned long delay; + bool vbus_attach, sch_work, notify_charger; + + if (rport->utmi_avalid) + vbus_attach = + property_enabled(rphy, &rport->port_cfg->utmi_avalid); + else + vbus_attach = + property_enabled(rphy, &rport->port_cfg->utmi_bvalid); + + sch_work = false; + notify_charger = false; + delay = OTG_SCHEDULE_DELAY; + dev_dbg(&rport->phy->dev, "%s otg sm work\n", + usb_otg_state_string(rport->state)); + + switch (rport->state) { + case OTG_STATE_UNDEFINED: + rport->state = OTG_STATE_B_IDLE; + if (!vbus_attach) + rockchip_usb2phy_power_off(rport->phy); + /* fall through */ + case OTG_STATE_B_IDLE: + if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) > 0) { + dev_dbg(&rport->phy->dev, "usb otg host connect\n"); + rport->state = OTG_STATE_A_HOST; + rockchip_usb2phy_power_on(rport->phy); + return; + } else if (vbus_attach) { + dev_dbg(&rport->phy->dev, "vbus_attach\n"); + switch (rphy->chg_state) { + case USB_CHG_STATE_UNDEFINED: + schedule_delayed_work(&rport->chg_work, 0); + return; + case USB_CHG_STATE_DETECTED: + switch (rphy->chg_type) { + case POWER_SUPPLY_TYPE_USB: + dev_dbg(&rport->phy->dev, "sdp cable is connected\n"); + rockchip_usb2phy_power_on(rport->phy); + rport->state = OTG_STATE_B_PERIPHERAL; + notify_charger = true; + sch_work = true; + cable = EXTCON_CHG_USB_SDP; + break; + case POWER_SUPPLY_TYPE_USB_DCP: + dev_dbg(&rport->phy->dev, "dcp cable is connected\n"); + rockchip_usb2phy_power_off(rport->phy); + notify_charger = true; + sch_work = true; + cable = EXTCON_CHG_USB_DCP; + break; + case POWER_SUPPLY_TYPE_USB_CDP: + dev_dbg(&rport->phy->dev, "cdp cable is connected\n"); + rockchip_usb2phy_power_on(rport->phy); + rport->state = OTG_STATE_B_PERIPHERAL; + notify_charger = true; + sch_work = true; + cable = EXTCON_CHG_USB_CDP; + break; + default: + break; + } + break; + default: + break; + } + } else { + notify_charger = true; + rphy->chg_state = USB_CHG_STATE_UNDEFINED; + rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; + } + + if (rport->vbus_attached != vbus_attach) { + rport->vbus_attached = vbus_attach; + + if (notify_charger && rphy->edev) { + extcon_set_cable_state_(rphy->edev, + cable, vbus_attach); + if (cable == EXTCON_CHG_USB_SDP) + extcon_set_state_sync(rphy->edev, + EXTCON_USB, + vbus_attach); + } + } + break; + case OTG_STATE_B_PERIPHERAL: + if (!vbus_attach) { + dev_dbg(&rport->phy->dev, "usb disconnect\n"); + rphy->chg_state = USB_CHG_STATE_UNDEFINED; + rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; + rport->state = OTG_STATE_B_IDLE; + delay = 0; + rockchip_usb2phy_power_off(rport->phy); + } + sch_work = true; + break; + case OTG_STATE_A_HOST: + if (extcon_get_cable_state_(rphy->edev, EXTCON_USB_HOST) == 0) { + dev_dbg(&rport->phy->dev, "usb otg host disconnect\n"); + rport->state = OTG_STATE_B_IDLE; + rockchip_usb2phy_power_off(rport->phy); + } + break; + default: + break; + } + + if (sch_work) + schedule_delayed_work(&rport->otg_sm_work, delay); +} + +static const char *chg_to_string(enum power_supply_type chg_type) +{ + switch (chg_type) { + case POWER_SUPPLY_TYPE_USB: + return "USB_SDP_CHARGER"; + case POWER_SUPPLY_TYPE_USB_DCP: + return "USB_DCP_CHARGER"; + case POWER_SUPPLY_TYPE_USB_CDP: + return "USB_CDP_CHARGER"; + default: + return "INVALID_CHARGER"; + } +} + +static void rockchip_chg_enable_dcd(struct rockchip_usb2phy *rphy, + bool en) +{ + property_enable(rphy, &rphy->phy_cfg->chg_det.rdm_pdwn_en, en); + property_enable(rphy, &rphy->phy_cfg->chg_det.idp_src_en, en); +} + +static void rockchip_chg_enable_primary_det(struct rockchip_usb2phy *rphy, + bool en) +{ + property_enable(rphy, &rphy->phy_cfg->chg_det.vdp_src_en, en); + property_enable(rphy, &rphy->phy_cfg->chg_det.idm_sink_en, en); +} + +static void rockchip_chg_enable_secondary_det(struct rockchip_usb2phy *rphy, + bool en) +{ + property_enable(rphy, &rphy->phy_cfg->chg_det.vdm_src_en, en); + property_enable(rphy, &rphy->phy_cfg->chg_det.idp_sink_en, en); +} + +#define CHG_DCD_POLL_TIME (100 * HZ / 1000) +#define CHG_DCD_MAX_RETRIES 6 +#define CHG_PRIMARY_DET_TIME (40 * HZ / 1000) +#define CHG_SECONDARY_DET_TIME (40 * HZ / 1000) +static void rockchip_chg_detect_work(struct work_struct *work) +{ + struct rockchip_usb2phy_port *rport = + container_of(work, struct rockchip_usb2phy_port, chg_work.work); + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + bool is_dcd, tmout, vout; + unsigned long delay; + + dev_dbg(&rport->phy->dev, "chg detection work state = %d\n", + rphy->chg_state); + switch (rphy->chg_state) { + case USB_CHG_STATE_UNDEFINED: + if (!rport->suspended) + rockchip_usb2phy_power_off(rport->phy); + /* put the controller in non-driving mode */ + property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, false); + /* Start DCD processing stage 1 */ + rockchip_chg_enable_dcd(rphy, true); + rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; + rphy->dcd_retries = 0; + delay = CHG_DCD_POLL_TIME; + break; + case USB_CHG_STATE_WAIT_FOR_DCD: + /* get data contact detection status */ + is_dcd = property_enabled(rphy, &rphy->phy_cfg->chg_det.dp_det); + tmout = ++rphy->dcd_retries == CHG_DCD_MAX_RETRIES; + /* stage 2 */ + if (is_dcd || tmout) { + /* stage 4 */ + /* Turn off DCD circuitry */ + rockchip_chg_enable_dcd(rphy, false); + /* Voltage Source on DP, Probe on DM */ + rockchip_chg_enable_primary_det(rphy, true); + delay = CHG_PRIMARY_DET_TIME; + rphy->chg_state = USB_CHG_STATE_DCD_DONE; + } else { + /* stage 3 */ + delay = CHG_DCD_POLL_TIME; + } + break; + case USB_CHG_STATE_DCD_DONE: + vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.cp_det); + rockchip_chg_enable_primary_det(rphy, false); + if (vout) { + /* Voltage Source on DM, Probe on DP */ + rockchip_chg_enable_secondary_det(rphy, true); + delay = CHG_SECONDARY_DET_TIME; + rphy->chg_state = USB_CHG_STATE_PRIMARY_DONE; + } else { + if (rphy->dcd_retries == CHG_DCD_MAX_RETRIES) { + /* floating charger found */ + rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; + rphy->chg_state = USB_CHG_STATE_DETECTED; + delay = 0; + } else { + rphy->chg_type = POWER_SUPPLY_TYPE_USB; + rphy->chg_state = USB_CHG_STATE_DETECTED; + delay = 0; + } + } + break; + case USB_CHG_STATE_PRIMARY_DONE: + vout = property_enabled(rphy, &rphy->phy_cfg->chg_det.dcp_det); + /* Turn off voltage source */ + rockchip_chg_enable_secondary_det(rphy, false); + if (vout) + rphy->chg_type = POWER_SUPPLY_TYPE_USB_DCP; + else + rphy->chg_type = POWER_SUPPLY_TYPE_USB_CDP; + /* fall through */ + case USB_CHG_STATE_SECONDARY_DONE: + rphy->chg_state = USB_CHG_STATE_DETECTED; + delay = 0; + /* fall through */ + case USB_CHG_STATE_DETECTED: + /* put the controller in normal mode */ + property_enable(rphy, &rphy->phy_cfg->chg_det.opmode, true); + rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); + dev_info(&rport->phy->dev, "charger = %s\n", + chg_to_string(rphy->chg_type)); + return; + default: + return; + } + + schedule_delayed_work(&rport->chg_work, delay); +} + +/* + * The function manage host-phy port state and suspend/resume phy port + * to save power. + * + * we rely on utmi_linestate and utmi_hostdisconnect to identify whether + * devices is disconnect or not. Besides, we do not need care it is FS/LS + * disconnected or HS disconnected, actually, we just only need get the + * device is disconnected at last through rearm the delayed work, + * to suspend the phy port in _PHY_STATE_DISCONNECT_ case. + * + * NOTE: It may invoke *phy_powr_off or *phy_power_on which will invoke + * some clk related APIs, so do not invoke it from interrupt context directly. + */ +static void rockchip_usb2phy_sm_work(struct work_struct *work) +{ + struct rockchip_usb2phy_port *rport = + container_of(work, struct rockchip_usb2phy_port, sm_work.work); + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + unsigned int sh = rport->port_cfg->utmi_hstdet.bitend - + rport->port_cfg->utmi_hstdet.bitstart + 1; + unsigned int ul, uhd, state; + unsigned int ul_mask, uhd_mask; + int ret; + + mutex_lock(&rport->mutex); + + ret = regmap_read(rphy->grf, rport->port_cfg->utmi_ls.offset, &ul); + if (ret < 0) + goto next_schedule; + + ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, + &uhd); + if (ret < 0) + goto next_schedule; + + uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend, + rport->port_cfg->utmi_hstdet.bitstart); + ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend, + rport->port_cfg->utmi_ls.bitstart); + + /* stitch on utmi_ls and utmi_hstdet as phy state */ + state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) | + (((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh); + + switch (state) { + case PHY_STATE_HS_ONLINE: + dev_dbg(&rport->phy->dev, "HS online\n"); + break; + case PHY_STATE_FS_LS_ONLINE: + /* + * For FS/LS device, the online state share with connect state + * from utmi_ls and utmi_hstdet register, so we distinguish + * them via suspended flag. + * + * Plus, there are two cases, one is D- Line pull-up, and D+ + * line pull-down, the state is 4; another is D+ line pull-up, + * and D- line pull-down, the state is 2. + */ + if (!rport->suspended) { + /* D- line pull-up, D+ line pull-down */ + dev_dbg(&rport->phy->dev, "FS/LS online\n"); + break; + } + /* fall through */ + case PHY_STATE_CONNECT: + if (rport->suspended) { + dev_dbg(&rport->phy->dev, "Connected\n"); + rockchip_usb2phy_power_on(rport->phy); + rport->suspended = false; + } else { + /* D+ line pull-up, D- line pull-down */ + dev_dbg(&rport->phy->dev, "FS/LS online\n"); + } + break; + case PHY_STATE_DISCONNECT: + if (!rport->suspended) { + dev_dbg(&rport->phy->dev, "Disconnected\n"); + rockchip_usb2phy_power_off(rport->phy); + rport->suspended = true; + } + + /* + * activate the linestate detection to get the next device + * plug-in irq. + */ + property_enable(rphy, &rport->port_cfg->ls_det_clr, true); + property_enable(rphy, &rport->port_cfg->ls_det_en, true); + + /* + * we don't need to rearm the delayed work when the phy port + * is suspended. + */ + mutex_unlock(&rport->mutex); + return; + default: + dev_dbg(&rport->phy->dev, "unknown phy state\n"); + break; + } + +next_schedule: + mutex_unlock(&rport->mutex); + schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY); +} + +static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data) +{ + struct rockchip_usb2phy_port *rport = data; + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + + if (!property_enabled(rphy, &rport->port_cfg->ls_det_st)) + return IRQ_NONE; + + mutex_lock(&rport->mutex); + + /* disable linestate detect irq and clear its status */ + property_enable(rphy, &rport->port_cfg->ls_det_en, false); + property_enable(rphy, &rport->port_cfg->ls_det_clr, true); + + mutex_unlock(&rport->mutex); + + /* + * In this case for host phy port, a new device is plugged in, + * meanwhile, if the phy port is suspended, we need rearm the work to + * resume it and mange its states; otherwise, we do nothing about that. + */ + if (rport->suspended && rport->port_id == USB2PHY_PORT_HOST) + rockchip_usb2phy_sm_work(&rport->sm_work.work); + + return IRQ_HANDLED; +} + +static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data) +{ + struct rockchip_usb2phy_port *rport = data; + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + + if (!property_enabled(rphy, &rport->port_cfg->bvalid_det_st)) + return IRQ_NONE; + + mutex_lock(&rport->mutex); + + /* clear bvalid detect irq pending status */ + property_enable(rphy, &rport->port_cfg->bvalid_det_clr, true); + + mutex_unlock(&rport->mutex); + + rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work); + + return IRQ_HANDLED; +} + +static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, + struct rockchip_usb2phy_port *rport, + struct device_node *child_np) +{ + int ret; + + rport->port_id = USB2PHY_PORT_HOST; + rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_HOST]; + rport->suspended = true; + + mutex_init(&rport->mutex); + INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work); + + rport->ls_irq = of_irq_get_byname(child_np, "linestate"); + if (rport->ls_irq < 0) { + dev_err(rphy->dev, "no linestate irq provided\n"); + return rport->ls_irq; + } + + ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL, + rockchip_usb2phy_linestate_irq, + IRQF_ONESHOT, + "rockchip_usb2phy", rport); + if (ret) { + dev_err(rphy->dev, "failed to request linestate irq handle\n"); + return ret; + } + + return 0; +} + +static int rockchip_otg_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct rockchip_usb2phy_port *rport = + container_of(nb, struct rockchip_usb2phy_port, event_nb); + + schedule_delayed_work(&rport->otg_sm_work, OTG_SCHEDULE_DELAY); + + return NOTIFY_DONE; +} + +static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, + struct rockchip_usb2phy_port *rport, + struct device_node *child_np) +{ + int ret; + + rport->port_id = USB2PHY_PORT_OTG; + rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG]; + rport->state = OTG_STATE_UNDEFINED; + + /* + * set suspended flag to true, but actually don't + * put phy in suspend mode, it aims to enable usb + * phy and clock in power_on() called by usb controller + * driver during probe. + */ + rport->suspended = true; + rport->vbus_attached = false; + + mutex_init(&rport->mutex); + + rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1); + if (rport->mode == USB_DR_MODE_HOST) { + ret = 0; + goto out; + } + + INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work); + INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work); + + rport->utmi_avalid = + of_property_read_bool(child_np, "rockchip,utmi-avalid"); + + rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid"); + if (rport->bvalid_irq < 0) { + dev_err(rphy->dev, "no vbus valid irq provided\n"); + ret = rport->bvalid_irq; + goto out; + } + + ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, NULL, + rockchip_usb2phy_bvalid_irq, + IRQF_ONESHOT, + "rockchip_usb2phy_bvalid", rport); + if (ret) { + dev_err(rphy->dev, "failed to request otg-bvalid irq handle\n"); + goto out; + } + + if (!IS_ERR(rphy->edev)) { + rport->event_nb.notifier_call = rockchip_otg_event; + + ret = extcon_register_notifier(rphy->edev, EXTCON_USB_HOST, + &rport->event_nb); + if (ret) + dev_err(rphy->dev, "register USB HOST notifier failed\n"); + } + +out: + return ret; +} + +static int rockchip_usb2phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child_np; + struct phy_provider *provider; + struct rockchip_usb2phy *rphy; + const struct rockchip_usb2phy_cfg *phy_cfgs; + const struct of_device_id *match; + unsigned int reg; + int index, ret; + + rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL); + if (!rphy) + return -ENOMEM; + + match = of_match_device(dev->driver->of_match_table, dev); + if (!match || !match->data) { + dev_err(dev, "phy configs are not assigned!\n"); + return -EINVAL; + } + + if (!dev->parent || !dev->parent->of_node) + return -EINVAL; + + rphy->grf = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(rphy->grf)) + return PTR_ERR(rphy->grf); + + if (of_property_read_u32(np, "reg", ®)) { + dev_err(dev, "the reg property is not assigned in %s node\n", + np->name); + return -EINVAL; + } + + rphy->dev = dev; + phy_cfgs = match->data; + rphy->chg_state = USB_CHG_STATE_UNDEFINED; + rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; + platform_set_drvdata(pdev, rphy); + + ret = rockchip_usb2phy_extcon_register(rphy); + if (ret) + return ret; + + /* find out a proper config which can be matched with dt. */ + index = 0; + while (phy_cfgs[index].reg) { + if (phy_cfgs[index].reg == reg) { + rphy->phy_cfg = &phy_cfgs[index]; + break; + } + + ++index; + } + + if (!rphy->phy_cfg) { + dev_err(dev, "no phy-config can be matched with %s node\n", + np->name); + return -EINVAL; + } + + rphy->clk = of_clk_get_by_name(np, "phyclk"); + if (!IS_ERR(rphy->clk)) { + clk_prepare_enable(rphy->clk); + } else { + dev_info(&pdev->dev, "no phyclk specified\n"); + rphy->clk = NULL; + } + + ret = rockchip_usb2phy_clk480m_register(rphy); + if (ret) { + dev_err(dev, "failed to register 480m output clock\n"); + goto disable_clks; + } + + index = 0; + for_each_available_child_of_node(np, child_np) { + struct rockchip_usb2phy_port *rport = &rphy->ports[index]; + struct phy *phy; + + /* This driver aims to support both otg-port and host-port */ + if (of_node_cmp(child_np->name, "host-port") && + of_node_cmp(child_np->name, "otg-port")) + goto next_child; + + phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create phy\n"); + ret = PTR_ERR(phy); + goto put_child; + } + + rport->phy = phy; + phy_set_drvdata(rport->phy, rport); + + /* initialize otg/host port separately */ + if (!of_node_cmp(child_np->name, "host-port")) { + ret = rockchip_usb2phy_host_port_init(rphy, rport, + child_np); + if (ret) + goto put_child; + } else { + ret = rockchip_usb2phy_otg_port_init(rphy, rport, + child_np); + if (ret) + goto put_child; + } + +next_child: + /* to prevent out of boundary */ + if (++index >= rphy->phy_cfg->num_ports) + break; + } + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(provider); + +put_child: + of_node_put(child_np); +disable_clks: + if (rphy->clk) { + clk_disable_unprepare(rphy->clk); + clk_put(rphy->clk); + } + return ret; +} + +static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = { + { + .reg = 0x100, + .num_ports = 2, + .clkout_ctl = { 0x108, 4, 4, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x0100, 15, 0, 0, 0x1d1 }, + .bvalid_det_en = { 0x0110, 2, 2, 0, 1 }, + .bvalid_det_st = { 0x0114, 2, 2, 0, 1 }, + .bvalid_det_clr = { 0x0118, 2, 2, 0, 1 }, + .ls_det_en = { 0x0110, 0, 0, 0, 1 }, + .ls_det_st = { 0x0114, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0118, 0, 0, 0, 1 }, + .utmi_avalid = { 0x0120, 10, 10, 0, 1 }, + .utmi_bvalid = { 0x0120, 9, 9, 0, 1 }, + .utmi_ls = { 0x0120, 5, 4, 0, 1 }, + }, + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x104, 15, 0, 0, 0x1d1 }, + .ls_det_en = { 0x110, 1, 1, 0, 1 }, + .ls_det_st = { 0x114, 1, 1, 0, 1 }, + .ls_det_clr = { 0x118, 1, 1, 0, 1 }, + .utmi_ls = { 0x120, 17, 16, 0, 1 }, + .utmi_hstdet = { 0x120, 19, 19, 0, 1 } + } + }, + .chg_det = { + .opmode = { 0x0100, 3, 0, 5, 1 }, + .cp_det = { 0x0120, 24, 24, 0, 1 }, + .dcp_det = { 0x0120, 23, 23, 0, 1 }, + .dp_det = { 0x0120, 25, 25, 0, 1 }, + .idm_sink_en = { 0x0108, 8, 8, 0, 1 }, + .idp_sink_en = { 0x0108, 7, 7, 0, 1 }, + .idp_src_en = { 0x0108, 9, 9, 0, 1 }, + .rdm_pdwn_en = { 0x0108, 10, 10, 0, 1 }, + .vdm_src_en = { 0x0108, 12, 12, 0, 1 }, + .vdp_src_en = { 0x0108, 11, 11, 0, 1 }, + }, + }, + { /* sentinel */ } +}; + +static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = { + { + .reg = 0x700, + .num_ports = 2, + .clkout_ctl = { 0x0724, 15, 15, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x0728, 15, 0, 0, 0x1d1 }, + .ls_det_en = { 0x0680, 4, 4, 0, 1 }, + .ls_det_st = { 0x0690, 4, 4, 0, 1 }, + .ls_det_clr = { 0x06a0, 4, 4, 0, 1 }, + .utmi_ls = { 0x049c, 14, 13, 0, 1 }, + .utmi_hstdet = { 0x049c, 12, 12, 0, 1 } + } + }, + }, + { /* sentinel */ } +}; + +static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { + { + .reg = 0xe450, + .num_ports = 2, + .clkout_ctl = { 0xe450, 4, 4, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0xe454, 1, 0, 2, 1 }, + .bvalid_det_en = { 0xe3c0, 3, 3, 0, 1 }, + .bvalid_det_st = { 0xe3e0, 3, 3, 0, 1 }, + .bvalid_det_clr = { 0xe3d0, 3, 3, 0, 1 }, + .utmi_avalid = { 0xe2ac, 7, 7, 0, 1 }, + .utmi_bvalid = { 0xe2ac, 12, 12, 0, 1 }, + }, + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0xe458, 1, 0, 0x2, 0x1 }, + .ls_det_en = { 0xe3c0, 6, 6, 0, 1 }, + .ls_det_st = { 0xe3e0, 6, 6, 0, 1 }, + .ls_det_clr = { 0xe3d0, 6, 6, 0, 1 }, + .utmi_ls = { 0xe2ac, 22, 21, 0, 1 }, + .utmi_hstdet = { 0xe2ac, 23, 23, 0, 1 } + } + }, + .chg_det = { + .opmode = { 0xe454, 3, 0, 5, 1 }, + .cp_det = { 0xe2ac, 2, 2, 0, 1 }, + .dcp_det = { 0xe2ac, 1, 1, 0, 1 }, + .dp_det = { 0xe2ac, 0, 0, 0, 1 }, + .idm_sink_en = { 0xe450, 8, 8, 0, 1 }, + .idp_sink_en = { 0xe450, 7, 7, 0, 1 }, + .idp_src_en = { 0xe450, 9, 9, 0, 1 }, + .rdm_pdwn_en = { 0xe450, 10, 10, 0, 1 }, + .vdm_src_en = { 0xe450, 12, 12, 0, 1 }, + .vdp_src_en = { 0xe450, 11, 11, 0, 1 }, + }, + }, + { + .reg = 0xe460, + .num_ports = 2, + .clkout_ctl = { 0xe460, 4, 4, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0xe464, 1, 0, 2, 1 }, + .bvalid_det_en = { 0xe3c0, 8, 8, 0, 1 }, + .bvalid_det_st = { 0xe3e0, 8, 8, 0, 1 }, + .bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 }, + .utmi_avalid = { 0xe2ac, 10, 10, 0, 1 }, + .utmi_bvalid = { 0xe2ac, 16, 16, 0, 1 }, + }, + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0xe468, 1, 0, 0x2, 0x1 }, + .ls_det_en = { 0xe3c0, 11, 11, 0, 1 }, + .ls_det_st = { 0xe3e0, 11, 11, 0, 1 }, + .ls_det_clr = { 0xe3d0, 11, 11, 0, 1 }, + .utmi_ls = { 0xe2ac, 26, 25, 0, 1 }, + .utmi_hstdet = { 0xe2ac, 27, 27, 0, 1 } + } + }, + }, + { /* sentinel */ } +}; + +static const struct of_device_id rockchip_usb2phy_dt_match[] = { + { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, + { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, + { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, + {} +}; +MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match); + +static struct platform_driver rockchip_usb2phy_driver = { + .probe = rockchip_usb2phy_probe, + .driver = { + .name = "rockchip-usb2phy", + .of_match_table = rockchip_usb2phy_dt_match, + }, +}; +module_platform_driver(rockchip_usb2phy_driver); + +MODULE_AUTHOR("Frank Wang "); +MODULE_DESCRIPTION("Rockchip USB2.0 PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/rockchip/phy-rockchip-pcie.c b/drivers/phy/rockchip/phy-rockchip-pcie.c new file mode 100644 index 000000000000..6904633cad68 --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-pcie.c @@ -0,0 +1,346 @@ +/* + * Rockchip PCIe PHY driver + * + * Copyright (C) 2016 Shawn Lin + * Copyright (C) 2016 ROCKCHIP, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The higher 16-bit of this register is used for write protection + * only if BIT(x + 16) set to 1 the BIT(x) can be written. + */ +#define HIWORD_UPDATE(val, mask, shift) \ + ((val) << (shift) | (mask) << ((shift) + 16)) + +#define PHY_MAX_LANE_NUM 4 +#define PHY_CFG_DATA_SHIFT 7 +#define PHY_CFG_ADDR_SHIFT 1 +#define PHY_CFG_DATA_MASK 0xf +#define PHY_CFG_ADDR_MASK 0x3f +#define PHY_CFG_RD_MASK 0x3ff +#define PHY_CFG_WR_ENABLE 1 +#define PHY_CFG_WR_DISABLE 1 +#define PHY_CFG_WR_SHIFT 0 +#define PHY_CFG_WR_MASK 1 +#define PHY_CFG_PLL_LOCK 0x10 +#define PHY_CFG_CLK_TEST 0x10 +#define PHY_CFG_CLK_SCC 0x12 +#define PHY_CFG_SEPE_RATE BIT(3) +#define PHY_CFG_PLL_100M BIT(3) +#define PHY_PLL_LOCKED BIT(9) +#define PHY_PLL_OUTPUT BIT(10) +#define PHY_LANE_A_STATUS 0x30 +#define PHY_LANE_B_STATUS 0x31 +#define PHY_LANE_C_STATUS 0x32 +#define PHY_LANE_D_STATUS 0x33 +#define PHY_LANE_RX_DET_SHIFT 11 +#define PHY_LANE_RX_DET_TH 0x1 +#define PHY_LANE_IDLE_OFF 0x1 +#define PHY_LANE_IDLE_MASK 0x1 +#define PHY_LANE_IDLE_A_SHIFT 3 +#define PHY_LANE_IDLE_B_SHIFT 4 +#define PHY_LANE_IDLE_C_SHIFT 5 +#define PHY_LANE_IDLE_D_SHIFT 6 + +struct rockchip_pcie_data { + unsigned int pcie_conf; + unsigned int pcie_status; + unsigned int pcie_laneoff; +}; + +struct rockchip_pcie_phy { + struct rockchip_pcie_data *phy_data; + struct regmap *reg_base; + struct reset_control *phy_rst; + struct clk *clk_pciephy_ref; +}; + +static inline void phy_wr_cfg(struct rockchip_pcie_phy *rk_phy, + u32 addr, u32 data) +{ + regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, + HIWORD_UPDATE(data, + PHY_CFG_DATA_MASK, + PHY_CFG_DATA_SHIFT) | + HIWORD_UPDATE(addr, + PHY_CFG_ADDR_MASK, + PHY_CFG_ADDR_SHIFT)); + udelay(1); + regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, + HIWORD_UPDATE(PHY_CFG_WR_ENABLE, + PHY_CFG_WR_MASK, + PHY_CFG_WR_SHIFT)); + udelay(1); + regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, + HIWORD_UPDATE(PHY_CFG_WR_DISABLE, + PHY_CFG_WR_MASK, + PHY_CFG_WR_SHIFT)); +} + +static inline u32 phy_rd_cfg(struct rockchip_pcie_phy *rk_phy, + u32 addr) +{ + u32 val; + + regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, + HIWORD_UPDATE(addr, + PHY_CFG_RD_MASK, + PHY_CFG_ADDR_SHIFT)); + regmap_read(rk_phy->reg_base, + rk_phy->phy_data->pcie_status, + &val); + return val; +} + +static int rockchip_pcie_phy_power_off(struct phy *phy) +{ + struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); + int err = 0; + + err = reset_control_assert(rk_phy->phy_rst); + if (err) { + dev_err(&phy->dev, "assert phy_rst err %d\n", err); + return err; + } + + return 0; +} + +static int rockchip_pcie_phy_power_on(struct phy *phy) +{ + struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); + int err = 0; + u32 status; + unsigned long timeout; + + err = reset_control_deassert(rk_phy->phy_rst); + if (err) { + dev_err(&phy->dev, "deassert phy_rst err %d\n", err); + return err; + } + + regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, + HIWORD_UPDATE(PHY_CFG_PLL_LOCK, + PHY_CFG_ADDR_MASK, + PHY_CFG_ADDR_SHIFT)); + + /* + * No documented timeout value for phy operation below, + * so we make it large enough here. And we use loop-break + * method which should not be harmful. + */ + timeout = jiffies + msecs_to_jiffies(1000); + + err = -EINVAL; + while (time_before(jiffies, timeout)) { + regmap_read(rk_phy->reg_base, + rk_phy->phy_data->pcie_status, + &status); + if (status & PHY_PLL_LOCKED) { + dev_dbg(&phy->dev, "pll locked!\n"); + err = 0; + break; + } + msleep(20); + } + + if (err) { + dev_err(&phy->dev, "pll lock timeout!\n"); + goto err_pll_lock; + } + + phy_wr_cfg(rk_phy, PHY_CFG_CLK_TEST, PHY_CFG_SEPE_RATE); + phy_wr_cfg(rk_phy, PHY_CFG_CLK_SCC, PHY_CFG_PLL_100M); + + err = -ETIMEDOUT; + while (time_before(jiffies, timeout)) { + regmap_read(rk_phy->reg_base, + rk_phy->phy_data->pcie_status, + &status); + if (!(status & PHY_PLL_OUTPUT)) { + dev_dbg(&phy->dev, "pll output enable done!\n"); + err = 0; + break; + } + msleep(20); + } + + if (err) { + dev_err(&phy->dev, "pll output enable timeout!\n"); + goto err_pll_lock; + } + + regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf, + HIWORD_UPDATE(PHY_CFG_PLL_LOCK, + PHY_CFG_ADDR_MASK, + PHY_CFG_ADDR_SHIFT)); + err = -EINVAL; + while (time_before(jiffies, timeout)) { + regmap_read(rk_phy->reg_base, + rk_phy->phy_data->pcie_status, + &status); + if (status & PHY_PLL_LOCKED) { + dev_dbg(&phy->dev, "pll relocked!\n"); + err = 0; + break; + } + msleep(20); + } + + if (err) { + dev_err(&phy->dev, "pll relock timeout!\n"); + goto err_pll_lock; + } + + return 0; + +err_pll_lock: + reset_control_assert(rk_phy->phy_rst); + return err; +} + +static int rockchip_pcie_phy_init(struct phy *phy) +{ + struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); + int err = 0; + + err = clk_prepare_enable(rk_phy->clk_pciephy_ref); + if (err) { + dev_err(&phy->dev, "Fail to enable pcie ref clock.\n"); + goto err_refclk; + } + + err = reset_control_assert(rk_phy->phy_rst); + if (err) { + dev_err(&phy->dev, "assert phy_rst err %d\n", err); + goto err_reset; + } + + return err; + +err_reset: + clk_disable_unprepare(rk_phy->clk_pciephy_ref); +err_refclk: + return err; +} + +static int rockchip_pcie_phy_exit(struct phy *phy) +{ + struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy); + + clk_disable_unprepare(rk_phy->clk_pciephy_ref); + + return 0; +} + +static const struct phy_ops ops = { + .init = rockchip_pcie_phy_init, + .exit = rockchip_pcie_phy_exit, + .power_on = rockchip_pcie_phy_power_on, + .power_off = rockchip_pcie_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct rockchip_pcie_data rk3399_pcie_data = { + .pcie_conf = 0xe220, + .pcie_status = 0xe2a4, + .pcie_laneoff = 0xe214, +}; + +static const struct of_device_id rockchip_pcie_phy_dt_ids[] = { + { + .compatible = "rockchip,rk3399-pcie-phy", + .data = &rk3399_pcie_data, + }, + {} +}; + +MODULE_DEVICE_TABLE(of, rockchip_pcie_phy_dt_ids); + +static int rockchip_pcie_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rockchip_pcie_phy *rk_phy; + struct phy *generic_phy; + struct phy_provider *phy_provider; + struct regmap *grf; + const struct of_device_id *of_id; + + grf = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(grf)) { + dev_err(dev, "Cannot find GRF syscon\n"); + return PTR_ERR(grf); + } + + rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL); + if (!rk_phy) + return -ENOMEM; + + of_id = of_match_device(rockchip_pcie_phy_dt_ids, &pdev->dev); + if (!of_id) + return -EINVAL; + + rk_phy->phy_data = (struct rockchip_pcie_data *)of_id->data; + rk_phy->reg_base = grf; + + rk_phy->phy_rst = devm_reset_control_get(dev, "phy"); + if (IS_ERR(rk_phy->phy_rst)) { + if (PTR_ERR(rk_phy->phy_rst) != -EPROBE_DEFER) + dev_err(dev, + "missing phy property for reset controller\n"); + return PTR_ERR(rk_phy->phy_rst); + } + + rk_phy->clk_pciephy_ref = devm_clk_get(dev, "refclk"); + if (IS_ERR(rk_phy->clk_pciephy_ref)) { + dev_err(dev, "refclk not found.\n"); + return PTR_ERR(rk_phy->clk_pciephy_ref); + } + + generic_phy = devm_phy_create(dev, dev->of_node, &ops); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, rk_phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver rockchip_pcie_driver = { + .probe = rockchip_pcie_phy_probe, + .driver = { + .name = "rockchip-pcie-phy", + .of_match_table = rockchip_pcie_phy_dt_ids, + }, +}; + +module_platform_driver(rockchip_pcie_driver); + +MODULE_AUTHOR("Shawn Lin "); +MODULE_DESCRIPTION("Rockchip PCIe PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c new file mode 100644 index 000000000000..7cfb0f8995de --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -0,0 +1,1023 @@ +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Chris Zhong + * Kever Yang + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * The ROCKCHIP Type-C PHY has two PLL clocks. The first PLL clock + * is used for USB3, the second PLL clock is used for DP. This Type-C PHY has + * 3 working modes: USB3 only mode, DP only mode, and USB3+DP mode. + * At USB3 only mode, both PLL clocks need to be initialized, this allows the + * PHY to switch mode between USB3 and USB3+DP, without disconnecting the USB + * device. + * In The DP only mode, only the DP PLL needs to be powered on, and the 4 lanes + * are all used for DP. + * + * This driver gets extcon cable state and property, then decides which mode to + * select: + * + * 1. USB3 only mode: + * EXTCON_USB or EXTCON_USB_HOST state is true, and + * EXTCON_PROP_USB_SS property is true. + * EXTCON_DISP_DP state is false. + * + * 2. DP only mode: + * EXTCON_DISP_DP state is true, and + * EXTCON_PROP_USB_SS property is false. + * If EXTCON_USB_HOST state is true, it is DP + USB2 mode, since the USB2 phy + * is a separate phy, so this case is still DP only mode. + * + * 3. USB3+DP mode: + * EXTCON_USB_HOST and EXTCON_DISP_DP are both true, and + * EXTCON_PROP_USB_SS property is true. + * + * This Type-C PHY driver supports normal and flip orientation. The orientation + * is reported by the EXTCON_PROP_USB_TYPEC_POLARITY property: true is flip + * orientation, false is normal orientation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define CMN_SSM_BANDGAP (0x21 << 2) +#define CMN_SSM_BIAS (0x22 << 2) +#define CMN_PLLSM0_PLLEN (0x29 << 2) +#define CMN_PLLSM0_PLLPRE (0x2a << 2) +#define CMN_PLLSM0_PLLVREF (0x2b << 2) +#define CMN_PLLSM0_PLLLOCK (0x2c << 2) +#define CMN_PLLSM1_PLLEN (0x31 << 2) +#define CMN_PLLSM1_PLLPRE (0x32 << 2) +#define CMN_PLLSM1_PLLVREF (0x33 << 2) +#define CMN_PLLSM1_PLLLOCK (0x34 << 2) +#define CMN_PLLSM1_USER_DEF_CTRL (0x37 << 2) +#define CMN_ICAL_OVRD (0xc1 << 2) +#define CMN_PLL0_VCOCAL_OVRD (0x83 << 2) +#define CMN_PLL0_VCOCAL_INIT (0x84 << 2) +#define CMN_PLL0_VCOCAL_ITER (0x85 << 2) +#define CMN_PLL0_LOCK_REFCNT_START (0x90 << 2) +#define CMN_PLL0_LOCK_PLLCNT_START (0x92 << 2) +#define CMN_PLL0_LOCK_PLLCNT_THR (0x93 << 2) +#define CMN_PLL0_INTDIV (0x94 << 2) +#define CMN_PLL0_FRACDIV (0x95 << 2) +#define CMN_PLL0_HIGH_THR (0x96 << 2) +#define CMN_PLL0_DSM_DIAG (0x97 << 2) +#define CMN_PLL0_SS_CTRL1 (0x98 << 2) +#define CMN_PLL0_SS_CTRL2 (0x99 << 2) +#define CMN_PLL1_VCOCAL_START (0xa1 << 2) +#define CMN_PLL1_VCOCAL_OVRD (0xa3 << 2) +#define CMN_PLL1_VCOCAL_INIT (0xa4 << 2) +#define CMN_PLL1_VCOCAL_ITER (0xa5 << 2) +#define CMN_PLL1_LOCK_REFCNT_START (0xb0 << 2) +#define CMN_PLL1_LOCK_PLLCNT_START (0xb2 << 2) +#define CMN_PLL1_LOCK_PLLCNT_THR (0xb3 << 2) +#define CMN_PLL1_INTDIV (0xb4 << 2) +#define CMN_PLL1_FRACDIV (0xb5 << 2) +#define CMN_PLL1_HIGH_THR (0xb6 << 2) +#define CMN_PLL1_DSM_DIAG (0xb7 << 2) +#define CMN_PLL1_SS_CTRL1 (0xb8 << 2) +#define CMN_PLL1_SS_CTRL2 (0xb9 << 2) +#define CMN_RXCAL_OVRD (0xd1 << 2) +#define CMN_TXPUCAL_CTRL (0xe0 << 2) +#define CMN_TXPUCAL_OVRD (0xe1 << 2) +#define CMN_TXPDCAL_OVRD (0xf1 << 2) +#define CMN_DIAG_PLL0_FBH_OVRD (0x1c0 << 2) +#define CMN_DIAG_PLL0_FBL_OVRD (0x1c1 << 2) +#define CMN_DIAG_PLL0_OVRD (0x1c2 << 2) +#define CMN_DIAG_PLL0_V2I_TUNE (0x1c5 << 2) +#define CMN_DIAG_PLL0_CP_TUNE (0x1c6 << 2) +#define CMN_DIAG_PLL0_LF_PROG (0x1c7 << 2) +#define CMN_DIAG_PLL1_FBH_OVRD (0x1d0 << 2) +#define CMN_DIAG_PLL1_FBL_OVRD (0x1d1 << 2) +#define CMN_DIAG_PLL1_OVRD (0x1d2 << 2) +#define CMN_DIAG_PLL1_V2I_TUNE (0x1d5 << 2) +#define CMN_DIAG_PLL1_CP_TUNE (0x1d6 << 2) +#define CMN_DIAG_PLL1_LF_PROG (0x1d7 << 2) +#define CMN_DIAG_PLL1_PTATIS_TUNE1 (0x1d8 << 2) +#define CMN_DIAG_PLL1_PTATIS_TUNE2 (0x1d9 << 2) +#define CMN_DIAG_PLL1_INCLK_CTRL (0x1da << 2) +#define CMN_DIAG_HSCLK_SEL (0x1e0 << 2) + +#define XCVR_PSM_RCTRL(n) ((0x4001 | ((n) << 9)) << 2) +#define XCVR_PSM_CAL_TMR(n) ((0x4002 | ((n) << 9)) << 2) +#define XCVR_PSM_A0IN_TMR(n) ((0x4003 | ((n) << 9)) << 2) +#define TX_TXCC_CAL_SCLR_MULT(n) ((0x4047 | ((n) << 9)) << 2) +#define TX_TXCC_CPOST_MULT_00(n) ((0x404c | ((n) << 9)) << 2) +#define TX_TXCC_CPOST_MULT_01(n) ((0x404d | ((n) << 9)) << 2) +#define TX_TXCC_CPOST_MULT_10(n) ((0x404e | ((n) << 9)) << 2) +#define TX_TXCC_CPOST_MULT_11(n) ((0x404f | ((n) << 9)) << 2) +#define TX_TXCC_MGNFS_MULT_000(n) ((0x4050 | ((n) << 9)) << 2) +#define TX_TXCC_MGNFS_MULT_001(n) ((0x4051 | ((n) << 9)) << 2) +#define TX_TXCC_MGNFS_MULT_010(n) ((0x4052 | ((n) << 9)) << 2) +#define TX_TXCC_MGNFS_MULT_011(n) ((0x4053 | ((n) << 9)) << 2) +#define TX_TXCC_MGNFS_MULT_100(n) ((0x4054 | ((n) << 9)) << 2) +#define TX_TXCC_MGNFS_MULT_101(n) ((0x4055 | ((n) << 9)) << 2) +#define TX_TXCC_MGNFS_MULT_110(n) ((0x4056 | ((n) << 9)) << 2) +#define TX_TXCC_MGNFS_MULT_111(n) ((0x4057 | ((n) << 9)) << 2) +#define XCVR_DIAG_PLLDRC_CTRL(n) ((0x40e0 | ((n) << 9)) << 2) +#define XCVR_DIAG_BIDI_CTRL(n) ((0x40e8 | ((n) << 9)) << 2) +#define XCVR_DIAG_LANE_FCM_EN_MGN(n) ((0x40f2 | ((n) << 9)) << 2) +#define TX_PSC_A0(n) ((0x4100 | ((n) << 9)) << 2) +#define TX_PSC_A1(n) ((0x4101 | ((n) << 9)) << 2) +#define TX_PSC_A2(n) ((0x4102 | ((n) << 9)) << 2) +#define TX_PSC_A3(n) ((0x4103 | ((n) << 9)) << 2) +#define TX_RCVDET_CTRL(n) ((0x4120 | ((n) << 9)) << 2) +#define TX_RCVDET_EN_TMR(n) ((0x4122 | ((n) << 9)) << 2) +#define TX_RCVDET_ST_TMR(n) ((0x4123 | ((n) << 9)) << 2) +#define TX_DIAG_TX_DRV(n) ((0x41e1 | ((n) << 9)) << 2) +#define TX_DIAG_BGREF_PREDRV_DELAY (0x41e7 << 2) +#define TX_ANA_CTRL_REG_1 (0x5020 << 2) +#define TX_ANA_CTRL_REG_2 (0x5021 << 2) +#define TXDA_COEFF_CALC_CTRL (0x5022 << 2) +#define TX_DIG_CTRL_REG_2 (0x5024 << 2) +#define TXDA_CYA_AUXDA_CYA (0x5025 << 2) +#define TX_ANA_CTRL_REG_3 (0x5026 << 2) +#define TX_ANA_CTRL_REG_4 (0x5027 << 2) +#define TX_ANA_CTRL_REG_5 (0x5029 << 2) + +#define RX_PSC_A0(n) ((0x8000 | ((n) << 9)) << 2) +#define RX_PSC_A1(n) ((0x8001 | ((n) << 9)) << 2) +#define RX_PSC_A2(n) ((0x8002 | ((n) << 9)) << 2) +#define RX_PSC_A3(n) ((0x8003 | ((n) << 9)) << 2) +#define RX_PSC_CAL(n) ((0x8006 | ((n) << 9)) << 2) +#define RX_PSC_RDY(n) ((0x8007 | ((n) << 9)) << 2) +#define RX_IQPI_ILL_CAL_OVRD (0x8023 << 2) +#define RX_EPI_ILL_CAL_OVRD (0x8033 << 2) +#define RX_SDCAL0_OVRD (0x8041 << 2) +#define RX_SDCAL1_OVRD (0x8049 << 2) +#define RX_SLC_INIT (0x806d << 2) +#define RX_SLC_RUN (0x806e << 2) +#define RX_CDRLF_CNFG2 (0x8081 << 2) +#define RX_SIGDET_HL_FILT_TMR(n) ((0x8090 | ((n) << 9)) << 2) +#define RX_SLC_IOP0_OVRD (0x8101 << 2) +#define RX_SLC_IOP1_OVRD (0x8105 << 2) +#define RX_SLC_QOP0_OVRD (0x8109 << 2) +#define RX_SLC_QOP1_OVRD (0x810d << 2) +#define RX_SLC_EOP0_OVRD (0x8111 << 2) +#define RX_SLC_EOP1_OVRD (0x8115 << 2) +#define RX_SLC_ION0_OVRD (0x8119 << 2) +#define RX_SLC_ION1_OVRD (0x811d << 2) +#define RX_SLC_QON0_OVRD (0x8121 << 2) +#define RX_SLC_QON1_OVRD (0x8125 << 2) +#define RX_SLC_EON0_OVRD (0x8129 << 2) +#define RX_SLC_EON1_OVRD (0x812d << 2) +#define RX_SLC_IEP0_OVRD (0x8131 << 2) +#define RX_SLC_IEP1_OVRD (0x8135 << 2) +#define RX_SLC_QEP0_OVRD (0x8139 << 2) +#define RX_SLC_QEP1_OVRD (0x813d << 2) +#define RX_SLC_EEP0_OVRD (0x8141 << 2) +#define RX_SLC_EEP1_OVRD (0x8145 << 2) +#define RX_SLC_IEN0_OVRD (0x8149 << 2) +#define RX_SLC_IEN1_OVRD (0x814d << 2) +#define RX_SLC_QEN0_OVRD (0x8151 << 2) +#define RX_SLC_QEN1_OVRD (0x8155 << 2) +#define RX_SLC_EEN0_OVRD (0x8159 << 2) +#define RX_SLC_EEN1_OVRD (0x815d << 2) +#define RX_REE_CTRL_DATA_MASK(n) ((0x81bb | ((n) << 9)) << 2) +#define RX_DIAG_SIGDET_TUNE(n) ((0x81dc | ((n) << 9)) << 2) +#define RX_DIAG_SC2C_DELAY (0x81e1 << 2) + +#define PMA_LANE_CFG (0xc000 << 2) +#define PIPE_CMN_CTRL1 (0xc001 << 2) +#define PIPE_CMN_CTRL2 (0xc002 << 2) +#define PIPE_COM_LOCK_CFG1 (0xc003 << 2) +#define PIPE_COM_LOCK_CFG2 (0xc004 << 2) +#define PIPE_RCV_DET_INH (0xc005 << 2) +#define DP_MODE_CTL (0xc008 << 2) +#define DP_CLK_CTL (0xc009 << 2) +#define STS (0xc00F << 2) +#define PHY_ISO_CMN_CTRL (0xc010 << 2) +#define PHY_DP_TX_CTL (0xc408 << 2) +#define PMA_CMN_CTRL1 (0xc800 << 2) +#define PHY_PMA_ISO_CMN_CTRL (0xc810 << 2) +#define PHY_ISOLATION_CTRL (0xc81f << 2) +#define PHY_PMA_ISO_XCVR_CTRL(n) ((0xcc11 | ((n) << 6)) << 2) +#define PHY_PMA_ISO_LINK_MODE(n) ((0xcc12 | ((n) << 6)) << 2) +#define PHY_PMA_ISO_PWRST_CTRL(n) ((0xcc13 | ((n) << 6)) << 2) +#define PHY_PMA_ISO_TX_DATA_LO(n) ((0xcc14 | ((n) << 6)) << 2) +#define PHY_PMA_ISO_TX_DATA_HI(n) ((0xcc15 | ((n) << 6)) << 2) +#define PHY_PMA_ISO_RX_DATA_LO(n) ((0xcc16 | ((n) << 6)) << 2) +#define PHY_PMA_ISO_RX_DATA_HI(n) ((0xcc17 | ((n) << 6)) << 2) +#define TX_BIST_CTRL(n) ((0x4140 | ((n) << 9)) << 2) +#define TX_BIST_UDDWR(n) ((0x4141 | ((n) << 9)) << 2) + +/* + * Selects which PLL clock will be driven on the analog high speed + * clock 0: PLL 0 div 1 + * clock 1: PLL 1 div 2 + */ +#define CLK_PLL_CONFIG 0X30 +#define CLK_PLL_MASK 0x33 + +#define CMN_READY BIT(0) + +#define DP_PLL_CLOCK_ENABLE BIT(2) +#define DP_PLL_ENABLE BIT(0) +#define DP_PLL_DATA_RATE_RBR ((2 << 12) | (4 << 8)) +#define DP_PLL_DATA_RATE_HBR ((2 << 12) | (4 << 8)) +#define DP_PLL_DATA_RATE_HBR2 ((1 << 12) | (2 << 8)) + +#define DP_MODE_A0 BIT(4) +#define DP_MODE_A2 BIT(6) +#define DP_MODE_ENTER_A0 0xc101 +#define DP_MODE_ENTER_A2 0xc104 + +#define PHY_MODE_SET_TIMEOUT 100000 + +#define PIN_ASSIGN_C_E 0x51d9 +#define PIN_ASSIGN_D_F 0x5100 + +#define MODE_DISCONNECT 0 +#define MODE_UFP_USB BIT(0) +#define MODE_DFP_USB BIT(1) +#define MODE_DFP_DP BIT(2) + +struct usb3phy_reg { + u32 offset; + u32 enable_bit; + u32 write_enable; +}; + +struct rockchip_usb3phy_port_cfg { + struct usb3phy_reg typec_conn_dir; + struct usb3phy_reg usb3tousb2_en; + struct usb3phy_reg external_psm; + struct usb3phy_reg pipe_status; +}; + +struct rockchip_typec_phy { + struct device *dev; + void __iomem *base; + struct extcon_dev *extcon; + struct regmap *grf_regs; + struct clk *clk_core; + struct clk *clk_ref; + struct reset_control *uphy_rst; + struct reset_control *pipe_rst; + struct reset_control *tcphy_rst; + struct rockchip_usb3phy_port_cfg port_cfgs; + /* mutex to protect access to individual PHYs */ + struct mutex lock; + + bool flip; + u8 mode; +}; + +struct phy_reg { + u16 value; + u32 addr; +}; + +struct phy_reg usb3_pll_cfg[] = { + { 0xf0, CMN_PLL0_VCOCAL_INIT }, + { 0x18, CMN_PLL0_VCOCAL_ITER }, + { 0xd0, CMN_PLL0_INTDIV }, + { 0x4a4a, CMN_PLL0_FRACDIV }, + { 0x34, CMN_PLL0_HIGH_THR }, + { 0x1ee, CMN_PLL0_SS_CTRL1 }, + { 0x7f03, CMN_PLL0_SS_CTRL2 }, + { 0x20, CMN_PLL0_DSM_DIAG }, + { 0, CMN_DIAG_PLL0_OVRD }, + { 0, CMN_DIAG_PLL0_FBH_OVRD }, + { 0, CMN_DIAG_PLL0_FBL_OVRD }, + { 0x7, CMN_DIAG_PLL0_V2I_TUNE }, + { 0x45, CMN_DIAG_PLL0_CP_TUNE }, + { 0x8, CMN_DIAG_PLL0_LF_PROG }, +}; + +struct phy_reg dp_pll_cfg[] = { + { 0xf0, CMN_PLL1_VCOCAL_INIT }, + { 0x18, CMN_PLL1_VCOCAL_ITER }, + { 0x30b9, CMN_PLL1_VCOCAL_START }, + { 0x21c, CMN_PLL1_INTDIV }, + { 0, CMN_PLL1_FRACDIV }, + { 0x5, CMN_PLL1_HIGH_THR }, + { 0x35, CMN_PLL1_SS_CTRL1 }, + { 0x7f1e, CMN_PLL1_SS_CTRL2 }, + { 0x20, CMN_PLL1_DSM_DIAG }, + { 0, CMN_PLLSM1_USER_DEF_CTRL }, + { 0, CMN_DIAG_PLL1_OVRD }, + { 0, CMN_DIAG_PLL1_FBH_OVRD }, + { 0, CMN_DIAG_PLL1_FBL_OVRD }, + { 0x6, CMN_DIAG_PLL1_V2I_TUNE }, + { 0x45, CMN_DIAG_PLL1_CP_TUNE }, + { 0x8, CMN_DIAG_PLL1_LF_PROG }, + { 0x100, CMN_DIAG_PLL1_PTATIS_TUNE1 }, + { 0x7, CMN_DIAG_PLL1_PTATIS_TUNE2 }, + { 0x4, CMN_DIAG_PLL1_INCLK_CTRL }, +}; + +static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy) +{ + u32 i, rdata; + + /* + * cmn_ref_clk_sel = 3, select the 24Mhz for clk parent + * cmn_psm_clk_dig_div = 2, set the clk division to 2 + */ + writel(0x830, tcphy->base + PMA_CMN_CTRL1); + for (i = 0; i < 4; i++) { + /* + * The following PHY configuration assumes a 24 MHz reference + * clock. + */ + writel(0x90, tcphy->base + XCVR_DIAG_LANE_FCM_EN_MGN(i)); + writel(0x960, tcphy->base + TX_RCVDET_EN_TMR(i)); + writel(0x30, tcphy->base + TX_RCVDET_ST_TMR(i)); + } + + rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL); + rdata &= ~CLK_PLL_MASK; + rdata |= CLK_PLL_CONFIG; + writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL); +} + +static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy) +{ + u32 i; + + /* load the configuration of PLL0 */ + for (i = 0; i < ARRAY_SIZE(usb3_pll_cfg); i++) + writel(usb3_pll_cfg[i].value, + tcphy->base + usb3_pll_cfg[i].addr); +} + +static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy) +{ + u32 i; + + /* set the default mode to RBR */ + writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR, + tcphy->base + DP_CLK_CTL); + + /* load the configuration of PLL1 */ + for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++) + writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr); +} + +static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane) +{ + writel(0x7799, tcphy->base + TX_PSC_A0(lane)); + writel(0x7798, tcphy->base + TX_PSC_A1(lane)); + writel(0x5098, tcphy->base + TX_PSC_A2(lane)); + writel(0x5098, tcphy->base + TX_PSC_A3(lane)); + writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane)); + writel(0xbf, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane)); +} + +static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane) +{ + writel(0xa6fd, tcphy->base + RX_PSC_A0(lane)); + writel(0xa6fd, tcphy->base + RX_PSC_A1(lane)); + writel(0xa410, tcphy->base + RX_PSC_A2(lane)); + writel(0x2410, tcphy->base + RX_PSC_A3(lane)); + writel(0x23ff, tcphy->base + RX_PSC_CAL(lane)); + writel(0x13, tcphy->base + RX_SIGDET_HL_FILT_TMR(lane)); + writel(0x03e7, tcphy->base + RX_REE_CTRL_DATA_MASK(lane)); + writel(0x1004, tcphy->base + RX_DIAG_SIGDET_TUNE(lane)); + writel(0x2010, tcphy->base + RX_PSC_RDY(lane)); + writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane)); +} + +static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane) +{ + u16 rdata; + + writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane)); + writel(0x6799, tcphy->base + TX_PSC_A0(lane)); + writel(0x6798, tcphy->base + TX_PSC_A1(lane)); + writel(0x98, tcphy->base + TX_PSC_A2(lane)); + writel(0x98, tcphy->base + TX_PSC_A3(lane)); + + writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane)); + writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane)); + writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane)); + writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane)); + writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane)); + writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane)); + writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane)); + writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane)); + writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane)); + writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane)); + writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane)); + writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane)); + + writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane)); + writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane)); + + rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane)); + rdata = (rdata & 0x8fff) | 0x6000; + writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane)); +} + +static inline int property_enable(struct rockchip_typec_phy *tcphy, + const struct usb3phy_reg *reg, bool en) +{ + u32 mask = 1 << reg->write_enable; + u32 val = en << reg->enable_bit; + + return regmap_write(tcphy->grf_regs, reg->offset, val | mask); +} + +static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) +{ + u16 rdata, rdata2, val; + + /* disable txda_cal_latch_en for rewrite the calibration values */ + rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1); + val = rdata & 0xdfff; + writel(val, tcphy->base + TX_ANA_CTRL_REG_1); + + /* + * read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and + * write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it + * works. + */ + rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2); + rdata = rdata & 0xffc0; + + rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL); + rdata2 = rdata2 & 0x3f; + + val = rdata | rdata2; + writel(val, tcphy->base + TX_DIG_CTRL_REG_2); + usleep_range(1000, 1050); + + /* + * Enable signal for latch that sample and holds calibration values. + * Activate this signal for 1 clock cycle to sample new calibration + * values. + */ + rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1); + val = rdata | 0x2000; + writel(val, tcphy->base + TX_ANA_CTRL_REG_1); + usleep_range(150, 200); + + /* set TX Voltage Level and TX Deemphasis to 0 */ + writel(0, tcphy->base + PHY_DP_TX_CTL); + /* re-enable decap */ + writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2); + writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2); + writel(0x2008, tcphy->base + TX_ANA_CTRL_REG_1); + writel(0x2018, tcphy->base + TX_ANA_CTRL_REG_1); + + writel(0, tcphy->base + TX_ANA_CTRL_REG_5); + + /* + * Programs txda_drv_ldo_prog[15:0], Sets driver LDO + * voltage 16'h1001 for DP-AUX-TX and RX + */ + writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4); + + /* re-enables Bandgap reference for LDO */ + writel(0x2098, tcphy->base + TX_ANA_CTRL_REG_1); + writel(0x2198, tcphy->base + TX_ANA_CTRL_REG_1); + + /* + * re-enables the transmitter pre-driver, driver data selection MUX, + * and receiver detect circuits. + */ + writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2); + writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2); + + /* + * BIT 12: Controls auxda_polarity, which selects the polarity of the + * xcvr: + * 1, Reverses the polarity (If TYPEC, Pulls ups aux_p and pull + * down aux_m) + * 0, Normal polarity (if TYPE_C, pulls up aux_m and pulls down + * aux_p) + */ + val = 0xa078; + if (!tcphy->flip) + val |= BIT(12); + writel(val, tcphy->base + TX_ANA_CTRL_REG_1); + + writel(0, tcphy->base + TX_ANA_CTRL_REG_3); + writel(0, tcphy->base + TX_ANA_CTRL_REG_4); + writel(0, tcphy->base + TX_ANA_CTRL_REG_5); + + /* + * Controls low_power_swing_en, set the voltage swing of the driver + * to 400mv. The values below are peak to peak (differential) values. + */ + writel(4, tcphy->base + TXDA_COEFF_CALC_CTRL); + writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA); + + /* Controls tx_high_z_tm_en */ + val = readl(tcphy->base + TX_DIG_CTRL_REG_2); + val |= BIT(15); + writel(val, tcphy->base + TX_DIG_CTRL_REG_2); +} + +static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode) +{ + struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; + int ret, i; + u32 val; + + ret = clk_prepare_enable(tcphy->clk_core); + if (ret) { + dev_err(tcphy->dev, "Failed to prepare_enable core clock\n"); + return ret; + } + + ret = clk_prepare_enable(tcphy->clk_ref); + if (ret) { + dev_err(tcphy->dev, "Failed to prepare_enable ref clock\n"); + goto err_clk_core; + } + + reset_control_deassert(tcphy->tcphy_rst); + + property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip); + + tcphy_cfg_24m(tcphy); + + if (mode == MODE_DFP_DP) { + tcphy_cfg_dp_pll(tcphy); + for (i = 0; i < 4; i++) + tcphy_dp_cfg_lane(tcphy, i); + + writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG); + } else { + tcphy_cfg_usb3_pll(tcphy); + tcphy_cfg_dp_pll(tcphy); + if (tcphy->flip) { + tcphy_tx_usb3_cfg_lane(tcphy, 3); + tcphy_rx_usb3_cfg_lane(tcphy, 2); + tcphy_dp_cfg_lane(tcphy, 0); + tcphy_dp_cfg_lane(tcphy, 1); + } else { + tcphy_tx_usb3_cfg_lane(tcphy, 0); + tcphy_rx_usb3_cfg_lane(tcphy, 1); + tcphy_dp_cfg_lane(tcphy, 2); + tcphy_dp_cfg_lane(tcphy, 3); + } + + writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG); + } + + writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL); + + reset_control_deassert(tcphy->uphy_rst); + + ret = readx_poll_timeout(readl, tcphy->base + PMA_CMN_CTRL1, + val, val & CMN_READY, 10, + PHY_MODE_SET_TIMEOUT); + if (ret < 0) { + dev_err(tcphy->dev, "wait pma ready timeout\n"); + ret = -ETIMEDOUT; + goto err_wait_pma; + } + + reset_control_deassert(tcphy->pipe_rst); + + return 0; + +err_wait_pma: + reset_control_assert(tcphy->uphy_rst); + reset_control_assert(tcphy->tcphy_rst); + clk_disable_unprepare(tcphy->clk_ref); +err_clk_core: + clk_disable_unprepare(tcphy->clk_core); + return ret; +} + +static void tcphy_phy_deinit(struct rockchip_typec_phy *tcphy) +{ + reset_control_assert(tcphy->tcphy_rst); + reset_control_assert(tcphy->uphy_rst); + reset_control_assert(tcphy->pipe_rst); + clk_disable_unprepare(tcphy->clk_core); + clk_disable_unprepare(tcphy->clk_ref); +} + +static int tcphy_get_mode(struct rockchip_typec_phy *tcphy) +{ + struct extcon_dev *edev = tcphy->extcon; + union extcon_property_value property; + unsigned int id; + bool dfp, ufp, dp; + u8 mode; + int ret; + + ufp = extcon_get_state(edev, EXTCON_USB); + dfp = extcon_get_state(edev, EXTCON_USB_HOST); + dp = extcon_get_state(edev, EXTCON_DISP_DP); + + mode = MODE_DFP_USB; + id = EXTCON_USB_HOST; + + if (ufp) { + mode = MODE_UFP_USB; + id = EXTCON_USB; + } else if (dp) { + mode = MODE_DFP_DP; + id = EXTCON_DISP_DP; + + ret = extcon_get_property(edev, id, EXTCON_PROP_USB_SS, + &property); + if (ret) { + dev_err(tcphy->dev, "get superspeed property failed\n"); + return ret; + } + + if (property.intval) + mode |= MODE_DFP_USB; + } + + ret = extcon_get_property(edev, id, EXTCON_PROP_USB_TYPEC_POLARITY, + &property); + if (ret) { + dev_err(tcphy->dev, "get polarity property failed\n"); + return ret; + } + + tcphy->flip = property.intval ? 1 : 0; + + return mode; +} + +static int rockchip_usb3_phy_power_on(struct phy *phy) +{ + struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); + struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; + const struct usb3phy_reg *reg = &cfg->pipe_status; + int timeout, new_mode, ret = 0; + u32 val; + + mutex_lock(&tcphy->lock); + + new_mode = tcphy_get_mode(tcphy); + if (new_mode < 0) { + ret = new_mode; + goto unlock_ret; + } + + /* DP-only mode; fall back to USB2 */ + if (!(new_mode & (MODE_DFP_USB | MODE_UFP_USB))) + goto unlock_ret; + + if (tcphy->mode == new_mode) + goto unlock_ret; + + if (tcphy->mode == MODE_DISCONNECT) + tcphy_phy_init(tcphy, new_mode); + + /* wait TCPHY for pipe ready */ + for (timeout = 0; timeout < 100; timeout++) { + regmap_read(tcphy->grf_regs, reg->offset, &val); + if (!(val & BIT(reg->enable_bit))) { + tcphy->mode |= new_mode & (MODE_DFP_USB | MODE_UFP_USB); + goto unlock_ret; + } + usleep_range(10, 20); + } + + if (tcphy->mode == MODE_DISCONNECT) + tcphy_phy_deinit(tcphy); + + ret = -ETIMEDOUT; + +unlock_ret: + mutex_unlock(&tcphy->lock); + return ret; +} + +static int rockchip_usb3_phy_power_off(struct phy *phy) +{ + struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); + + mutex_lock(&tcphy->lock); + + if (tcphy->mode == MODE_DISCONNECT) + goto unlock; + + tcphy->mode &= ~(MODE_UFP_USB | MODE_DFP_USB); + if (tcphy->mode == MODE_DISCONNECT) + tcphy_phy_deinit(tcphy); + +unlock: + mutex_unlock(&tcphy->lock); + return 0; +} + +static const struct phy_ops rockchip_usb3_phy_ops = { + .power_on = rockchip_usb3_phy_power_on, + .power_off = rockchip_usb3_phy_power_off, + .owner = THIS_MODULE, +}; + +static int rockchip_dp_phy_power_on(struct phy *phy) +{ + struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); + int new_mode, ret = 0; + u32 val; + + mutex_lock(&tcphy->lock); + + new_mode = tcphy_get_mode(tcphy); + if (new_mode < 0) { + ret = new_mode; + goto unlock_ret; + } + + if (!(new_mode & MODE_DFP_DP)) { + ret = -ENODEV; + goto unlock_ret; + } + + if (tcphy->mode == new_mode) + goto unlock_ret; + + /* + * If the PHY has been power on, but the mode is not DP only mode, + * re-init the PHY for setting all of 4 lanes to DP. + */ + if (new_mode == MODE_DFP_DP && tcphy->mode != MODE_DISCONNECT) { + tcphy_phy_deinit(tcphy); + tcphy_phy_init(tcphy, new_mode); + } else if (tcphy->mode == MODE_DISCONNECT) { + tcphy_phy_init(tcphy, new_mode); + } + + ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL, + val, val & DP_MODE_A2, 1000, + PHY_MODE_SET_TIMEOUT); + if (ret < 0) { + dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n"); + goto power_on_finish; + } + + tcphy_dp_aux_calibration(tcphy); + + writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL); + + ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL, + val, val & DP_MODE_A0, 1000, + PHY_MODE_SET_TIMEOUT); + if (ret < 0) { + writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL); + dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n"); + goto power_on_finish; + } + + tcphy->mode |= MODE_DFP_DP; + +power_on_finish: + if (tcphy->mode == MODE_DISCONNECT) + tcphy_phy_deinit(tcphy); +unlock_ret: + mutex_unlock(&tcphy->lock); + return ret; +} + +static int rockchip_dp_phy_power_off(struct phy *phy) +{ + struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy); + + mutex_lock(&tcphy->lock); + + if (tcphy->mode == MODE_DISCONNECT) + goto unlock; + + tcphy->mode &= ~MODE_DFP_DP; + + writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL); + + if (tcphy->mode == MODE_DISCONNECT) + tcphy_phy_deinit(tcphy); + +unlock: + mutex_unlock(&tcphy->lock); + return 0; +} + +static const struct phy_ops rockchip_dp_phy_ops = { + .power_on = rockchip_dp_phy_power_on, + .power_off = rockchip_dp_phy_power_off, + .owner = THIS_MODULE, +}; + +static int tcphy_get_param(struct device *dev, + struct usb3phy_reg *reg, + const char *name) +{ + u32 buffer[3]; + int ret; + + ret = of_property_read_u32_array(dev->of_node, name, buffer, 3); + if (ret) { + dev_err(dev, "Can not parse %s\n", name); + return ret; + } + + reg->offset = buffer[0]; + reg->enable_bit = buffer[1]; + reg->write_enable = buffer[2]; + return 0; +} + +static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy, + struct device *dev) +{ + struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; + int ret; + + ret = tcphy_get_param(dev, &cfg->typec_conn_dir, + "rockchip,typec-conn-dir"); + if (ret) + return ret; + + ret = tcphy_get_param(dev, &cfg->usb3tousb2_en, + "rockchip,usb3tousb2-en"); + if (ret) + return ret; + + ret = tcphy_get_param(dev, &cfg->external_psm, + "rockchip,external-psm"); + if (ret) + return ret; + + ret = tcphy_get_param(dev, &cfg->pipe_status, + "rockchip,pipe-status"); + if (ret) + return ret; + + tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,grf"); + if (IS_ERR(tcphy->grf_regs)) { + dev_err(dev, "could not find grf dt node\n"); + return PTR_ERR(tcphy->grf_regs); + } + + tcphy->clk_core = devm_clk_get(dev, "tcpdcore"); + if (IS_ERR(tcphy->clk_core)) { + dev_err(dev, "could not get uphy core clock\n"); + return PTR_ERR(tcphy->clk_core); + } + + tcphy->clk_ref = devm_clk_get(dev, "tcpdphy-ref"); + if (IS_ERR(tcphy->clk_ref)) { + dev_err(dev, "could not get uphy ref clock\n"); + return PTR_ERR(tcphy->clk_ref); + } + + tcphy->uphy_rst = devm_reset_control_get(dev, "uphy"); + if (IS_ERR(tcphy->uphy_rst)) { + dev_err(dev, "no uphy_rst reset control found\n"); + return PTR_ERR(tcphy->uphy_rst); + } + + tcphy->pipe_rst = devm_reset_control_get(dev, "uphy-pipe"); + if (IS_ERR(tcphy->pipe_rst)) { + dev_err(dev, "no pipe_rst reset control found\n"); + return PTR_ERR(tcphy->pipe_rst); + } + + tcphy->tcphy_rst = devm_reset_control_get(dev, "uphy-tcphy"); + if (IS_ERR(tcphy->tcphy_rst)) { + dev_err(dev, "no tcphy_rst reset control found\n"); + return PTR_ERR(tcphy->tcphy_rst); + } + + return 0; +} + +static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy) +{ + struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs; + + reset_control_assert(tcphy->tcphy_rst); + reset_control_assert(tcphy->uphy_rst); + reset_control_assert(tcphy->pipe_rst); + + /* select external psm clock */ + property_enable(tcphy, &cfg->external_psm, 1); + property_enable(tcphy, &cfg->usb3tousb2_en, 0); + + tcphy->mode = MODE_DISCONNECT; +} + +static int rockchip_typec_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child_np; + struct rockchip_typec_phy *tcphy; + struct phy_provider *phy_provider; + struct resource *res; + int ret; + + tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL); + if (!tcphy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tcphy->base = devm_ioremap_resource(dev, res); + if (IS_ERR(tcphy->base)) + return PTR_ERR(tcphy->base); + + ret = tcphy_parse_dt(tcphy, dev); + if (ret) + return ret; + + tcphy->dev = dev; + platform_set_drvdata(pdev, tcphy); + mutex_init(&tcphy->lock); + + typec_phy_pre_init(tcphy); + + tcphy->extcon = extcon_get_edev_by_phandle(dev, 0); + if (IS_ERR(tcphy->extcon)) { + if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER) + dev_err(dev, "Invalid or missing extcon\n"); + return PTR_ERR(tcphy->extcon); + } + + pm_runtime_enable(dev); + + for_each_available_child_of_node(np, child_np) { + struct phy *phy; + + if (!of_node_cmp(child_np->name, "dp-port")) + phy = devm_phy_create(dev, child_np, + &rockchip_dp_phy_ops); + else if (!of_node_cmp(child_np->name, "usb3-port")) + phy = devm_phy_create(dev, child_np, + &rockchip_usb3_phy_ops); + else + continue; + + if (IS_ERR(phy)) { + dev_err(dev, "failed to create phy: %s\n", + child_np->name); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, tcphy); + } + + 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); + } + + return 0; +} + +static int rockchip_typec_phy_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id rockchip_typec_phy_dt_ids[] = { + { .compatible = "rockchip,rk3399-typec-phy" }, + {} +}; + +MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids); + +static struct platform_driver rockchip_typec_phy_driver = { + .probe = rockchip_typec_phy_probe, + .remove = rockchip_typec_phy_remove, + .driver = { + .name = "rockchip-typec-phy", + .of_match_table = rockchip_typec_phy_dt_ids, + }, +}; + +module_platform_driver(rockchip_typec_phy_driver); + +MODULE_AUTHOR("Chris Zhong "); +MODULE_AUTHOR("Kever Yang "); +MODULE_DESCRIPTION("Rockchip USB TYPE-C PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/rockchip/phy-rockchip-usb.c b/drivers/phy/rockchip/phy-rockchip-usb.c new file mode 100644 index 000000000000..3378eeb7a562 --- /dev/null +++ b/drivers/phy/rockchip/phy-rockchip-usb.c @@ -0,0 +1,540 @@ +/* + * Rockchip usb PHY driver + * + * Copyright (C) 2014 Yunzhi Li + * Copyright (C) 2014 ROCKCHIP, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int enable_usb_uart; + +#define HIWORD_UPDATE(val, mask) \ + ((val) | (mask) << 16) + +#define UOC_CON0_SIDDQ BIT(13) + +struct rockchip_usb_phys { + int reg; + const char *pll_name; +}; + +struct rockchip_usb_phy_base; +struct rockchip_usb_phy_pdata { + struct rockchip_usb_phys *phys; + int (*init_usb_uart)(struct regmap *grf); + int usb_uart_phy; +}; + +struct rockchip_usb_phy_base { + struct device *dev; + struct regmap *reg_base; + const struct rockchip_usb_phy_pdata *pdata; +}; + +struct rockchip_usb_phy { + struct rockchip_usb_phy_base *base; + struct device_node *np; + unsigned int reg_offset; + struct clk *clk; + struct clk *clk480m; + struct clk_hw clk480m_hw; + struct phy *phy; + bool uart_enabled; + struct reset_control *reset; + struct regulator *vbus; +}; + +static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy, + bool siddq) +{ + u32 val = HIWORD_UPDATE(siddq ? UOC_CON0_SIDDQ : 0, UOC_CON0_SIDDQ); + + return regmap_write(phy->base->reg_base, phy->reg_offset, val); +} + +static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 480000000; +} + +static void rockchip_usb_phy480m_disable(struct clk_hw *hw) +{ + struct rockchip_usb_phy *phy = container_of(hw, + struct rockchip_usb_phy, + clk480m_hw); + + if (phy->vbus) + regulator_disable(phy->vbus); + + /* Power down usb phy analog blocks by set siddq 1 */ + rockchip_usb_phy_power(phy, 1); +} + +static int rockchip_usb_phy480m_enable(struct clk_hw *hw) +{ + struct rockchip_usb_phy *phy = container_of(hw, + struct rockchip_usb_phy, + clk480m_hw); + + /* Power up usb phy analog blocks by set siddq 0 */ + return rockchip_usb_phy_power(phy, 0); +} + +static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw) +{ + struct rockchip_usb_phy *phy = container_of(hw, + struct rockchip_usb_phy, + clk480m_hw); + int ret; + u32 val; + + ret = regmap_read(phy->base->reg_base, phy->reg_offset, &val); + if (ret < 0) + return ret; + + return (val & UOC_CON0_SIDDQ) ? 0 : 1; +} + +static const struct clk_ops rockchip_usb_phy480m_ops = { + .enable = rockchip_usb_phy480m_enable, + .disable = rockchip_usb_phy480m_disable, + .is_enabled = rockchip_usb_phy480m_is_enabled, + .recalc_rate = rockchip_usb_phy480m_recalc_rate, +}; + +static int rockchip_usb_phy_power_off(struct phy *_phy) +{ + struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); + + if (phy->uart_enabled) + return -EBUSY; + + clk_disable_unprepare(phy->clk480m); + + return 0; +} + +static int rockchip_usb_phy_power_on(struct phy *_phy) +{ + struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); + + if (phy->uart_enabled) + return -EBUSY; + + if (phy->vbus) { + int ret; + + ret = regulator_enable(phy->vbus); + if (ret) + return ret; + } + + return clk_prepare_enable(phy->clk480m); +} + +static int rockchip_usb_phy_reset(struct phy *_phy) +{ + struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); + + if (phy->reset) { + reset_control_assert(phy->reset); + udelay(10); + reset_control_deassert(phy->reset); + } + + return 0; +} + +static const struct phy_ops ops = { + .power_on = rockchip_usb_phy_power_on, + .power_off = rockchip_usb_phy_power_off, + .reset = rockchip_usb_phy_reset, + .owner = THIS_MODULE, +}; + +static void rockchip_usb_phy_action(void *data) +{ + struct rockchip_usb_phy *rk_phy = data; + + if (!rk_phy->uart_enabled) { + of_clk_del_provider(rk_phy->np); + clk_unregister(rk_phy->clk480m); + } + + if (rk_phy->clk) + clk_put(rk_phy->clk); +} + +static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base, + struct device_node *child) +{ + struct rockchip_usb_phy *rk_phy; + unsigned int reg_offset; + const char *clk_name; + struct clk_init_data init; + int err, i; + + rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL); + if (!rk_phy) + return -ENOMEM; + + rk_phy->base = base; + rk_phy->np = child; + + if (of_property_read_u32(child, "reg", ®_offset)) { + dev_err(base->dev, "missing reg property in node %s\n", + child->name); + return -EINVAL; + } + + rk_phy->reset = of_reset_control_get(child, "phy-reset"); + if (IS_ERR(rk_phy->reset)) + rk_phy->reset = NULL; + + rk_phy->reg_offset = reg_offset; + + rk_phy->clk = of_clk_get_by_name(child, "phyclk"); + if (IS_ERR(rk_phy->clk)) + rk_phy->clk = NULL; + + i = 0; + init.name = NULL; + while (base->pdata->phys[i].reg) { + if (base->pdata->phys[i].reg == reg_offset) { + init.name = base->pdata->phys[i].pll_name; + break; + } + i++; + } + + if (!init.name) { + dev_err(base->dev, "phy data not found\n"); + return -EINVAL; + } + + if (enable_usb_uart && base->pdata->usb_uart_phy == i) { + dev_dbg(base->dev, "phy%d used as uart output\n", i); + rk_phy->uart_enabled = true; + } else { + if (rk_phy->clk) { + clk_name = __clk_get_name(rk_phy->clk); + init.flags = 0; + init.parent_names = &clk_name; + init.num_parents = 1; + } else { + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + } + + init.ops = &rockchip_usb_phy480m_ops; + rk_phy->clk480m_hw.init = &init; + + rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw); + if (IS_ERR(rk_phy->clk480m)) { + err = PTR_ERR(rk_phy->clk480m); + goto err_clk; + } + + err = of_clk_add_provider(child, of_clk_src_simple_get, + rk_phy->clk480m); + if (err < 0) + goto err_clk_prov; + } + + err = devm_add_action_or_reset(base->dev, rockchip_usb_phy_action, + rk_phy); + if (err) + return err; + + rk_phy->phy = devm_phy_create(base->dev, child, &ops); + if (IS_ERR(rk_phy->phy)) { + dev_err(base->dev, "failed to create PHY\n"); + return PTR_ERR(rk_phy->phy); + } + phy_set_drvdata(rk_phy->phy, rk_phy); + + rk_phy->vbus = devm_regulator_get_optional(&rk_phy->phy->dev, "vbus"); + if (IS_ERR(rk_phy->vbus)) { + if (PTR_ERR(rk_phy->vbus) == -EPROBE_DEFER) + return PTR_ERR(rk_phy->vbus); + rk_phy->vbus = NULL; + } + + /* + * When acting as uart-pipe, just keep clock on otherwise + * only power up usb phy when it use, so disable it when init + */ + if (rk_phy->uart_enabled) + return clk_prepare_enable(rk_phy->clk); + else + return rockchip_usb_phy_power(rk_phy, 1); + +err_clk_prov: + if (!rk_phy->uart_enabled) + clk_unregister(rk_phy->clk480m); +err_clk: + if (rk_phy->clk) + clk_put(rk_phy->clk); + return err; +} + +static const struct rockchip_usb_phy_pdata rk3066a_pdata = { + .phys = (struct rockchip_usb_phys[]){ + { .reg = 0x17c, .pll_name = "sclk_otgphy0_480m" }, + { .reg = 0x188, .pll_name = "sclk_otgphy1_480m" }, + { /* sentinel */ } + }, +}; + +static const struct rockchip_usb_phy_pdata rk3188_pdata = { + .phys = (struct rockchip_usb_phys[]){ + { .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" }, + { .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" }, + { /* sentinel */ } + }, +}; + +#define RK3288_UOC0_CON0 0x320 +#define RK3288_UOC0_CON0_COMMON_ON_N BIT(0) +#define RK3288_UOC0_CON0_DISABLE BIT(4) + +#define RK3288_UOC0_CON2 0x328 +#define RK3288_UOC0_CON2_SOFT_CON_SEL BIT(2) + +#define RK3288_UOC0_CON3 0x32c +#define RK3288_UOC0_CON3_UTMI_SUSPENDN BIT(0) +#define RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING (1 << 1) +#define RK3288_UOC0_CON3_UTMI_OPMODE_MASK (3 << 1) +#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC (1 << 3) +#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK (3 << 3) +#define RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED BIT(5) +#define RK3288_UOC0_CON3_BYPASSDMEN BIT(6) +#define RK3288_UOC0_CON3_BYPASSSEL BIT(7) + +/* + * Enable the bypass of uart2 data through the otg usb phy. + * Original description in the TRM. + * 1. Disable the OTG block by setting OTGDISABLE0 to 1’b1. + * 2. Disable the pull-up resistance on the D+ line by setting + * OPMODE0[1:0] to 2’b01. + * 3. To ensure that the XO, Bias, and PLL blocks are powered down in Suspend + * mode, set COMMONONN to 1’b1. + * 4. Place the USB PHY in Suspend mode by setting SUSPENDM0 to 1’b0. + * 5. Set BYPASSSEL0 to 1’b1. + * 6. To transmit data, controls BYPASSDMEN0, and BYPASSDMDATA0. + * To receive data, monitor FSVPLUS0. + * + * The actual code in the vendor kernel does some things differently. + */ +static int __init rk3288_init_usb_uart(struct regmap *grf) +{ + u32 val; + int ret; + + /* + * COMMON_ON and DISABLE settings are described in the TRM, + * but were not present in the original code. + * Also disable the analog phy components to save power. + */ + val = HIWORD_UPDATE(RK3288_UOC0_CON0_COMMON_ON_N + | RK3288_UOC0_CON0_DISABLE + | UOC_CON0_SIDDQ, + RK3288_UOC0_CON0_COMMON_ON_N + | RK3288_UOC0_CON0_DISABLE + | UOC_CON0_SIDDQ); + ret = regmap_write(grf, RK3288_UOC0_CON0, val); + if (ret) + return ret; + + val = HIWORD_UPDATE(RK3288_UOC0_CON2_SOFT_CON_SEL, + RK3288_UOC0_CON2_SOFT_CON_SEL); + ret = regmap_write(grf, RK3288_UOC0_CON2, val); + if (ret) + return ret; + + val = HIWORD_UPDATE(RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING + | RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC + | RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED, + RK3288_UOC0_CON3_UTMI_SUSPENDN + | RK3288_UOC0_CON3_UTMI_OPMODE_MASK + | RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK + | RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED); + ret = regmap_write(grf, RK3288_UOC0_CON3, val); + if (ret) + return ret; + + val = HIWORD_UPDATE(RK3288_UOC0_CON3_BYPASSSEL + | RK3288_UOC0_CON3_BYPASSDMEN, + RK3288_UOC0_CON3_BYPASSSEL + | RK3288_UOC0_CON3_BYPASSDMEN); + ret = regmap_write(grf, RK3288_UOC0_CON3, val); + if (ret) + return ret; + + return 0; +} + +static const struct rockchip_usb_phy_pdata rk3288_pdata = { + .phys = (struct rockchip_usb_phys[]){ + { .reg = 0x320, .pll_name = "sclk_otgphy0_480m" }, + { .reg = 0x334, .pll_name = "sclk_otgphy1_480m" }, + { .reg = 0x348, .pll_name = "sclk_otgphy2_480m" }, + { /* sentinel */ } + }, + .init_usb_uart = rk3288_init_usb_uart, + .usb_uart_phy = 0, +}; + +static int rockchip_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rockchip_usb_phy_base *phy_base; + struct phy_provider *phy_provider; + const struct of_device_id *match; + struct device_node *child; + int err; + + phy_base = devm_kzalloc(dev, sizeof(*phy_base), GFP_KERNEL); + if (!phy_base) + return -ENOMEM; + + match = of_match_device(dev->driver->of_match_table, dev); + if (!match || !match->data) { + dev_err(dev, "missing phy data\n"); + return -EINVAL; + } + + phy_base->pdata = match->data; + + phy_base->dev = dev; + phy_base->reg_base = ERR_PTR(-ENODEV); + if (dev->parent && dev->parent->of_node) + phy_base->reg_base = syscon_node_to_regmap( + dev->parent->of_node); + if (IS_ERR(phy_base->reg_base)) + phy_base->reg_base = syscon_regmap_lookup_by_phandle( + dev->of_node, "rockchip,grf"); + if (IS_ERR(phy_base->reg_base)) { + dev_err(&pdev->dev, "Missing rockchip,grf property\n"); + return PTR_ERR(phy_base->reg_base); + } + + for_each_available_child_of_node(dev->of_node, child) { + err = rockchip_usb_phy_init(phy_base, child); + if (err) { + of_node_put(child); + return err; + } + } + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id rockchip_usb_phy_dt_ids[] = { + { .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata }, + { .compatible = "rockchip,rk3188-usb-phy", .data = &rk3188_pdata }, + { .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata }, + {} +}; + +MODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids); + +static struct platform_driver rockchip_usb_driver = { + .probe = rockchip_usb_phy_probe, + .driver = { + .name = "rockchip-usb-phy", + .of_match_table = rockchip_usb_phy_dt_ids, + }, +}; + +module_platform_driver(rockchip_usb_driver); + +#ifndef MODULE +static int __init rockchip_init_usb_uart(void) +{ + const struct of_device_id *match; + const struct rockchip_usb_phy_pdata *data; + struct device_node *np; + struct regmap *grf; + int ret; + + if (!enable_usb_uart) + return 0; + + np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids, + &match); + if (!np) { + pr_err("%s: failed to find usbphy node\n", __func__); + return -ENOTSUPP; + } + + pr_debug("%s: using settings for %s\n", __func__, match->compatible); + data = match->data; + + if (!data->init_usb_uart) { + pr_err("%s: usb-uart not available on %s\n", + __func__, match->compatible); + return -ENOTSUPP; + } + + grf = ERR_PTR(-ENODEV); + if (np->parent) + grf = syscon_node_to_regmap(np->parent); + if (IS_ERR(grf)) + grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(grf)) { + pr_err("%s: Missing rockchip,grf property, %lu\n", + __func__, PTR_ERR(grf)); + return PTR_ERR(grf); + } + + ret = data->init_usb_uart(grf); + if (ret) { + pr_err("%s: could not init usb_uart, %d\n", __func__, ret); + enable_usb_uart = 0; + return ret; + } + + return 0; +} +early_initcall(rockchip_init_usb_uart); + +static int __init rockchip_usb_uart(char *buf) +{ + enable_usb_uart = true; + return 0; +} +early_param("rockchip.usb_uart", rockchip_usb_uart); +#endif + +MODULE_AUTHOR("Yunzhi Li "); +MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig new file mode 100644 index 000000000000..b7e0645a7bd9 --- /dev/null +++ b/drivers/phy/samsung/Kconfig @@ -0,0 +1,95 @@ +# +# Phy drivers for Samsung platforms +# +config PHY_EXYNOS_DP_VIDEO + tristate "EXYNOS SoC series Display Port PHY driver" + depends on OF + depends on ARCH_EXYNOS || COMPILE_TEST + default ARCH_EXYNOS + select GENERIC_PHY + help + Support for Display Port PHY found on Samsung EXYNOS SoCs. + +config PHY_EXYNOS_MIPI_VIDEO + tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" + depends on HAS_IOMEM + depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST + select GENERIC_PHY + default y if ARCH_S5PV210 || ARCH_EXYNOS + help + Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P + and EXYNOS SoCs. + +config PHY_EXYNOS_PCIE + bool "Exynos PCIe PHY driver" + depends on OF && (ARCH_EXYNOS || COMPILE_TEST) + select GENERIC_PHY + help + Enable PCIe PHY support for Exynos SoC series. + This driver provides PHY interface for Exynos PCIe controller. + +config PHY_SAMSUNG_USB2 + tristate "Samsung USB 2.0 PHY driver" + depends on HAS_IOMEM + depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2 + select GENERIC_PHY + select MFD_SYSCON + default ARCH_EXYNOS + help + Enable this to support the Samsung USB 2.0 PHY driver for Samsung + SoCs. This driver provides the interface for USB 2.0 PHY. Support + for particular PHYs will be enabled based on the SoC type in addition + to this driver. + +config PHY_EXYNOS4210_USB2 + bool + depends on PHY_SAMSUNG_USB2 + default CPU_EXYNOS4210 + +config PHY_EXYNOS4X12_USB2 + bool + depends on PHY_SAMSUNG_USB2 + default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412 + +config PHY_EXYNOS5250_USB2 + bool + depends on PHY_SAMSUNG_USB2 + default SOC_EXYNOS5250 || SOC_EXYNOS5420 + +config PHY_S5PV210_USB2 + bool "Support for S5PV210" + depends on PHY_SAMSUNG_USB2 + depends on ARCH_S5PV210 + help + Enable USB PHY support for S5PV210. This option requires that Samsung + USB 2.0 PHY driver is enabled and means that support for this + particular SoC is compiled in the driver. In case of S5PV210 two phys + are available - device and host. + +config PHY_EXYNOS5_USBDRD + tristate "Exynos5 SoC series USB DRD PHY driver" + depends on ARCH_EXYNOS && OF + depends on HAS_IOMEM + depends on USB_DWC3_EXYNOS + select GENERIC_PHY + select MFD_SYSCON + default y + help + Enable USB DRD PHY support for Exynos 5 SoC series. + This driver provides PHY interface for USB 3.0 DRD controller + present on Exynos5 SoC series. + +config PHY_EXYNOS5250_SATA + tristate "Exynos5250 Sata SerDes/PHY driver" + depends on SOC_EXYNOS5250 + depends on HAS_IOMEM + depends on OF + select GENERIC_PHY + select I2C + select I2C_S3C2410 + select MFD_SYSCON + help + Enable this to support SATA SerDes/Phy found on Samsung's + Exynos5250 based SoCs.This SerDes/Phy supports SATA 1.5 Gb/s, + SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host + port to accept one SATA device. diff --git a/drivers/phy/samsung/Makefile b/drivers/phy/samsung/Makefile new file mode 100644 index 000000000000..20d7f2424772 --- /dev/null +++ b/drivers/phy/samsung/Makefile @@ -0,0 +1,11 @@ +obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o +obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o +obj-$(CONFIG_PHY_EXYNOS_PCIE) += phy-exynos-pcie.o +obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o +phy-exynos-usb2-y += phy-samsung-usb2.o +phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o +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_EXYNOS5250_SATA) += phy-exynos5250-sata.o diff --git a/drivers/phy/samsung/phy-exynos-dp-video.c b/drivers/phy/samsung/phy-exynos-dp-video.c new file mode 100644 index 000000000000..bb3279dbf88c --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-dp-video.c @@ -0,0 +1,122 @@ +/* + * Samsung EXYNOS SoC series Display Port PHY driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Author: Jingoo Han + * + * 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 +#include + +struct exynos_dp_video_phy_drvdata { + u32 phy_ctrl_offset; +}; + +struct exynos_dp_video_phy { + struct regmap *regs; + const struct exynos_dp_video_phy_drvdata *drvdata; +}; + +static int exynos_dp_video_phy_power_on(struct phy *phy) +{ + struct exynos_dp_video_phy *state = phy_get_drvdata(phy); + + /* Disable power isolation on DP-PHY */ + return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset, + EXYNOS4_PHY_ENABLE, EXYNOS4_PHY_ENABLE); +} + +static int exynos_dp_video_phy_power_off(struct phy *phy) +{ + struct exynos_dp_video_phy *state = phy_get_drvdata(phy); + + /* Enable power isolation on DP-PHY */ + return regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset, + EXYNOS4_PHY_ENABLE, 0); +} + +static const struct phy_ops exynos_dp_video_phy_ops = { + .power_on = exynos_dp_video_phy_power_on, + .power_off = exynos_dp_video_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct exynos_dp_video_phy_drvdata exynos5250_dp_video_phy = { + .phy_ctrl_offset = EXYNOS5_DPTX_PHY_CONTROL, +}; + +static const struct exynos_dp_video_phy_drvdata exynos5420_dp_video_phy = { + .phy_ctrl_offset = EXYNOS5420_DPTX_PHY_CONTROL, +}; + +static const struct of_device_id exynos_dp_video_phy_of_match[] = { + { + .compatible = "samsung,exynos5250-dp-video-phy", + .data = &exynos5250_dp_video_phy, + }, { + .compatible = "samsung,exynos5420-dp-video-phy", + .data = &exynos5420_dp_video_phy, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, exynos_dp_video_phy_of_match); + +static int exynos_dp_video_phy_probe(struct platform_device *pdev) +{ + struct exynos_dp_video_phy *state; + struct device *dev = &pdev->dev; + const struct of_device_id *match; + struct phy_provider *phy_provider; + struct phy *phy; + + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + state->regs = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,pmu-syscon"); + if (IS_ERR(state->regs)) { + dev_err(dev, "Failed to lookup PMU regmap\n"); + return PTR_ERR(state->regs); + } + + match = of_match_node(exynos_dp_video_phy_of_match, dev->of_node); + state->drvdata = match->data; + + phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create Display Port PHY\n"); + return PTR_ERR(phy); + } + phy_set_drvdata(phy, state); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver exynos_dp_video_phy_driver = { + .probe = exynos_dp_video_phy_probe, + .driver = { + .name = "exynos-dp-video-phy", + .of_match_table = exynos_dp_video_phy_of_match, + } +}; +module_platform_driver(exynos_dp_video_phy_driver); + +MODULE_AUTHOR("Jingoo Han "); +MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/samsung/phy-exynos-mipi-video.c b/drivers/phy/samsung/phy-exynos-mipi-video.c new file mode 100644 index 000000000000..c198886f80a3 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-mipi-video.c @@ -0,0 +1,377 @@ +/* + * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver + * + * Copyright (C) 2013,2016 Samsung Electronics Co., Ltd. + * Author: Sylwester Nawrocki + * + * 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 +#include +#include + +enum exynos_mipi_phy_id { + EXYNOS_MIPI_PHY_ID_NONE = -1, + EXYNOS_MIPI_PHY_ID_CSIS0, + EXYNOS_MIPI_PHY_ID_DSIM0, + EXYNOS_MIPI_PHY_ID_CSIS1, + EXYNOS_MIPI_PHY_ID_DSIM1, + EXYNOS_MIPI_PHY_ID_CSIS2, + EXYNOS_MIPI_PHYS_NUM +}; + +enum exynos_mipi_phy_regmap_id { + EXYNOS_MIPI_REGMAP_PMU, + EXYNOS_MIPI_REGMAP_DISP, + EXYNOS_MIPI_REGMAP_CAM0, + EXYNOS_MIPI_REGMAP_CAM1, + EXYNOS_MIPI_REGMAPS_NUM +}; + +struct mipi_phy_device_desc { + int num_phys; + int num_regmaps; + const char *regmap_names[EXYNOS_MIPI_REGMAPS_NUM]; + struct exynos_mipi_phy_desc { + enum exynos_mipi_phy_id coupled_phy_id; + u32 enable_val; + unsigned int enable_reg; + enum exynos_mipi_phy_regmap_id enable_map; + u32 resetn_val; + unsigned int resetn_reg; + enum exynos_mipi_phy_regmap_id resetn_map; + } phys[EXYNOS_MIPI_PHYS_NUM]; +}; + +static const struct mipi_phy_device_desc s5pv210_mipi_phy = { + .num_regmaps = 1, + .regmap_names = {"syscon"}, + .num_phys = 4, + .phys = { + { + /* EXYNOS_MIPI_PHY_ID_CSIS0 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, + .enable_val = EXYNOS4_PHY_ENABLE, + .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, + .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0), + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, { + /* EXYNOS_MIPI_PHY_ID_DSIM0 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, + .enable_val = EXYNOS4_PHY_ENABLE, + .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, + .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(0), + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, { + /* EXYNOS_MIPI_PHY_ID_CSIS1 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1, + .enable_val = EXYNOS4_PHY_ENABLE, + .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, + .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1), + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, { + /* EXYNOS_MIPI_PHY_ID_DSIM1 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1, + .enable_val = EXYNOS4_PHY_ENABLE, + .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, + .resetn_reg = EXYNOS4_MIPI_PHY_CONTROL(1), + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, + }, +}; + +static const struct mipi_phy_device_desc exynos5420_mipi_phy = { + .num_regmaps = 1, + .regmap_names = {"syscon"}, + .num_phys = 5, + .phys = { + { + /* EXYNOS_MIPI_PHY_ID_CSIS0 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, + .enable_val = EXYNOS4_PHY_ENABLE, + .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, + .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, { + /* EXYNOS_MIPI_PHY_ID_DSIM0 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, + .enable_val = EXYNOS4_PHY_ENABLE, + .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, + .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(0), + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, { + /* EXYNOS_MIPI_PHY_ID_CSIS1 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM1, + .enable_val = EXYNOS4_PHY_ENABLE, + .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, + .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, { + /* EXYNOS_MIPI_PHY_ID_DSIM1 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS1, + .enable_val = EXYNOS4_PHY_ENABLE, + .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS4_MIPI_PHY_MRESETN, + .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(1), + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, { + /* EXYNOS_MIPI_PHY_ID_CSIS2 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, + .enable_val = EXYNOS4_PHY_ENABLE, + .enable_reg = EXYNOS5420_MIPI_PHY_CONTROL(2), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = EXYNOS4_MIPI_PHY_SRESETN, + .resetn_reg = EXYNOS5420_MIPI_PHY_CONTROL(2), + .resetn_map = EXYNOS_MIPI_REGMAP_PMU, + }, + }, +}; + +#define EXYNOS5433_SYSREG_DISP_MIPI_PHY 0x100C +#define EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON 0x1014 +#define EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON 0x1020 + +static const struct mipi_phy_device_desc exynos5433_mipi_phy = { + .num_regmaps = 4, + .regmap_names = { + "samsung,pmu-syscon", + "samsung,disp-sysreg", + "samsung,cam0-sysreg", + "samsung,cam1-sysreg" + }, + .num_phys = 5, + .phys = { + { + /* EXYNOS_MIPI_PHY_ID_CSIS0 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_DSIM0, + .enable_val = EXYNOS4_PHY_ENABLE, + .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = BIT(0), + .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON, + .resetn_map = EXYNOS_MIPI_REGMAP_CAM0, + }, { + /* EXYNOS_MIPI_PHY_ID_DSIM0 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_CSIS0, + .enable_val = EXYNOS4_PHY_ENABLE, + .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(0), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = BIT(0), + .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY, + .resetn_map = EXYNOS_MIPI_REGMAP_DISP, + }, { + /* EXYNOS_MIPI_PHY_ID_CSIS1 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, + .enable_val = EXYNOS4_PHY_ENABLE, + .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = BIT(1), + .resetn_reg = EXYNOS5433_SYSREG_CAM0_MIPI_DPHY_CON, + .resetn_map = EXYNOS_MIPI_REGMAP_CAM0, + }, { + /* EXYNOS_MIPI_PHY_ID_DSIM1 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, + .enable_val = EXYNOS4_PHY_ENABLE, + .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(1), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = BIT(1), + .resetn_reg = EXYNOS5433_SYSREG_DISP_MIPI_PHY, + .resetn_map = EXYNOS_MIPI_REGMAP_DISP, + }, { + /* EXYNOS_MIPI_PHY_ID_CSIS2 */ + .coupled_phy_id = EXYNOS_MIPI_PHY_ID_NONE, + .enable_val = EXYNOS4_PHY_ENABLE, + .enable_reg = EXYNOS4_MIPI_PHY_CONTROL(2), + .enable_map = EXYNOS_MIPI_REGMAP_PMU, + .resetn_val = BIT(0), + .resetn_reg = EXYNOS5433_SYSREG_CAM1_MIPI_DPHY_CON, + .resetn_map = EXYNOS_MIPI_REGMAP_CAM1, + }, + }, +}; + +struct exynos_mipi_video_phy { + struct regmap *regmaps[EXYNOS_MIPI_REGMAPS_NUM]; + int num_phys; + struct video_phy_desc { + struct phy *phy; + unsigned int index; + const struct exynos_mipi_phy_desc *data; + } phys[EXYNOS_MIPI_PHYS_NUM]; + spinlock_t slock; +}; + +static int __set_phy_state(const struct exynos_mipi_phy_desc *data, + struct exynos_mipi_video_phy *state, unsigned int on) +{ + u32 val; + + spin_lock(&state->slock); + + /* disable in PMU sysreg */ + if (!on && data->coupled_phy_id >= 0 && + state->phys[data->coupled_phy_id].phy->power_count == 0) { + regmap_read(state->regmaps[data->enable_map], data->enable_reg, + &val); + val &= ~data->enable_val; + regmap_write(state->regmaps[data->enable_map], data->enable_reg, + val); + } + + /* PHY reset */ + regmap_read(state->regmaps[data->resetn_map], data->resetn_reg, &val); + val = on ? (val | data->resetn_val) : (val & ~data->resetn_val); + regmap_write(state->regmaps[data->resetn_map], data->resetn_reg, val); + + /* enable in PMU sysreg */ + if (on) { + regmap_read(state->regmaps[data->enable_map], data->enable_reg, + &val); + val |= data->enable_val; + regmap_write(state->regmaps[data->enable_map], data->enable_reg, + val); + } + + spin_unlock(&state->slock); + + return 0; +} + +#define to_mipi_video_phy(desc) \ + container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]) + +static int exynos_mipi_video_phy_power_on(struct phy *phy) +{ + struct video_phy_desc *phy_desc = phy_get_drvdata(phy); + struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); + + return __set_phy_state(phy_desc->data, state, 1); +} + +static int exynos_mipi_video_phy_power_off(struct phy *phy) +{ + struct video_phy_desc *phy_desc = phy_get_drvdata(phy); + struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc); + + return __set_phy_state(phy_desc->data, state, 0); +} + +static struct phy *exynos_mipi_video_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct exynos_mipi_video_phy *state = dev_get_drvdata(dev); + + if (WARN_ON(args->args[0] >= state->num_phys)) + return ERR_PTR(-ENODEV); + + return state->phys[args->args[0]].phy; +} + +static const struct phy_ops exynos_mipi_video_phy_ops = { + .power_on = exynos_mipi_video_phy_power_on, + .power_off = exynos_mipi_video_phy_power_off, + .owner = THIS_MODULE, +}; + +static int exynos_mipi_video_phy_probe(struct platform_device *pdev) +{ + const struct mipi_phy_device_desc *phy_dev; + struct exynos_mipi_video_phy *state; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct phy_provider *phy_provider; + unsigned int i; + + phy_dev = of_device_get_match_data(dev); + if (!phy_dev) + return -ENODEV; + + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + for (i = 0; i < phy_dev->num_regmaps; i++) { + state->regmaps[i] = syscon_regmap_lookup_by_phandle(np, + phy_dev->regmap_names[i]); + if (IS_ERR(state->regmaps[i])) + return PTR_ERR(state->regmaps[i]); + } + state->num_phys = phy_dev->num_phys; + spin_lock_init(&state->slock); + + dev_set_drvdata(dev, state); + + for (i = 0; i < state->num_phys; i++) { + struct phy *phy = devm_phy_create(dev, NULL, + &exynos_mipi_video_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create PHY %d\n", i); + return PTR_ERR(phy); + } + + state->phys[i].phy = phy; + state->phys[i].index = i; + state->phys[i].data = &phy_dev->phys[i]; + phy_set_drvdata(phy, &state->phys[i]); + } + + phy_provider = devm_of_phy_provider_register(dev, + exynos_mipi_video_phy_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id exynos_mipi_video_phy_of_match[] = { + { + .compatible = "samsung,s5pv210-mipi-video-phy", + .data = &s5pv210_mipi_phy, + }, { + .compatible = "samsung,exynos5420-mipi-video-phy", + .data = &exynos5420_mipi_phy, + }, { + .compatible = "samsung,exynos5433-mipi-video-phy", + .data = &exynos5433_mipi_phy, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match); + +static struct platform_driver exynos_mipi_video_phy_driver = { + .probe = exynos_mipi_video_phy_probe, + .driver = { + .of_match_table = exynos_mipi_video_phy_of_match, + .name = "exynos-mipi-video-phy", + } +}; +module_platform_driver(exynos_mipi_video_phy_driver); + +MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver"); +MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/samsung/phy-exynos-pcie.c b/drivers/phy/samsung/phy-exynos-pcie.c new file mode 100644 index 000000000000..a89c12faff39 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos-pcie.c @@ -0,0 +1,281 @@ +/* + * Samsung EXYNOS SoC series PCIe PHY driver + * + * Phy provider for PCIe controller on Exynos SoC series + * + * Copyright (C) 2017 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 +#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 { + const 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; + + if (readl_poll_timeout(ep->phy_base + PCIE_PHY_PLL_LOCKED, val, + (val != 0), 1, 500)) { + dev_err(&phy->dev, "PLL Locked: 0x%x\n", val); + return -ETIMEDOUT; + } + + 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 const 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, + .owner = THIS_MODULE, +}; + +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, + }, + {}, +}; + +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->blk_base)) + return PTR_ERR(exynos_phy->blk_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", + } +}; + +builtin_platform_driver(exynos_pcie_phy_driver); diff --git a/drivers/phy/samsung/phy-exynos4210-usb2.c b/drivers/phy/samsung/phy-exynos4210-usb2.c new file mode 100644 index 000000000000..1f50e1004828 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos4210-usb2.c @@ -0,0 +1,260 @@ +/* + * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Author: Kamil Debski + * + * 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 "phy-samsung-usb2.h" + +/* Exynos USB PHY registers */ + +/* PHY power control */ +#define EXYNOS_4210_UPHYPWR 0x0 + +#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND BIT(0) +#define EXYNOS_4210_UPHYPWR_PHY0_PWR BIT(3) +#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR BIT(4) +#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP BIT(5) +#define EXYNOS_4210_UPHYPWR_PHY0 ( \ + EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \ + EXYNOS_4210_UPHYPWR_PHY0_PWR | \ + EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \ + EXYNOS_4210_UPHYPWR_PHY0_SLEEP) + +#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND BIT(6) +#define EXYNOS_4210_UPHYPWR_PHY1_PWR BIT(7) +#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP BIT(8) +#define EXYNOS_4210_UPHYPWR_PHY1 ( \ + EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \ + EXYNOS_4210_UPHYPWR_PHY1_PWR | \ + EXYNOS_4210_UPHYPWR_PHY1_SLEEP) + +#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND BIT(9) +#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP BIT(10) +#define EXYNOS_4210_UPHYPWR_HSIC0 ( \ + EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \ + EXYNOS_4210_UPHYPWR_HSIC0_SLEEP) + +#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND BIT(11) +#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP BIT(12) +#define EXYNOS_4210_UPHYPWR_HSIC1 ( \ + EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \ + EXYNOS_4210_UPHYPWR_HSIC1_SLEEP) + +/* PHY clock control */ +#define EXYNOS_4210_UPHYCLK 0x4 + +#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) +#define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET 0 +#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) +#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) +#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) + +#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP BIT(2) +#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON BIT(4) +#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON BIT(7) + +/* PHY reset control */ +#define EXYNOS_4210_UPHYRST 0x8 + +#define EXYNOS_4210_URSTCON_PHY0 BIT(0) +#define EXYNOS_4210_URSTCON_OTG_HLINK BIT(1) +#define EXYNOS_4210_URSTCON_OTG_PHYLINK BIT(2) +#define EXYNOS_4210_URSTCON_PHY1_ALL BIT(3) +#define EXYNOS_4210_URSTCON_PHY1_P0 BIT(4) +#define EXYNOS_4210_URSTCON_PHY1_P1P2 BIT(5) +#define EXYNOS_4210_URSTCON_HOST_LINK_ALL BIT(6) +#define EXYNOS_4210_URSTCON_HOST_LINK_P0 BIT(7) +#define EXYNOS_4210_URSTCON_HOST_LINK_P1 BIT(8) +#define EXYNOS_4210_URSTCON_HOST_LINK_P2 BIT(9) + +/* Isolation, configured in the power management unit */ +#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET 0x704 +#define EXYNOS_4210_USB_ISOL_DEVICE BIT(0) +#define EXYNOS_4210_USB_ISOL_HOST_OFFSET 0x708 +#define EXYNOS_4210_USB_ISOL_HOST BIT(0) + +/* USBYPHY1 Floating prevention */ +#define EXYNOS_4210_UPHY1CON 0x34 +#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION 0x1 + +/* Mode switching SUB Device <-> Host */ +#define EXYNOS_4210_MODE_SWITCH_OFFSET 0x21c +#define EXYNOS_4210_MODE_SWITCH_MASK 1 +#define EXYNOS_4210_MODE_SWITCH_DEVICE 0 +#define EXYNOS_4210_MODE_SWITCH_HOST 1 + +enum exynos4210_phy_id { + EXYNOS4210_DEVICE, + EXYNOS4210_HOST, + EXYNOS4210_HSIC0, + EXYNOS4210_HSIC1, + EXYNOS4210_NUM_PHYS, +}; + +/* + * exynos4210_rate_to_clk() converts the supplied clock rate to the value that + * can be written to the phy register. + */ +static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg) +{ + switch (rate) { + case 12 * MHZ: + *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ; + break; + case 24 * MHZ: + *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ; + break; + case 48 * MHZ: + *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + u32 offset; + u32 mask; + + switch (inst->cfg->id) { + case EXYNOS4210_DEVICE: + offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET; + mask = EXYNOS_4210_USB_ISOL_DEVICE; + break; + case EXYNOS4210_HOST: + offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET; + mask = EXYNOS_4210_USB_ISOL_HOST; + break; + default: + return; + } + + regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); +} + +static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + u32 rstbits = 0; + u32 phypwr = 0; + u32 rst; + u32 pwr; + u32 clk; + + switch (inst->cfg->id) { + case EXYNOS4210_DEVICE: + phypwr = EXYNOS_4210_UPHYPWR_PHY0; + rstbits = EXYNOS_4210_URSTCON_PHY0; + break; + case EXYNOS4210_HOST: + phypwr = EXYNOS_4210_UPHYPWR_PHY1; + rstbits = EXYNOS_4210_URSTCON_PHY1_ALL | + EXYNOS_4210_URSTCON_PHY1_P0 | + EXYNOS_4210_URSTCON_PHY1_P1P2 | + EXYNOS_4210_URSTCON_HOST_LINK_ALL | + EXYNOS_4210_URSTCON_HOST_LINK_P0; + writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON); + break; + case EXYNOS4210_HSIC0: + phypwr = EXYNOS_4210_UPHYPWR_HSIC0; + rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | + EXYNOS_4210_URSTCON_HOST_LINK_P1; + break; + case EXYNOS4210_HSIC1: + phypwr = EXYNOS_4210_UPHYPWR_HSIC1; + rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 | + EXYNOS_4210_URSTCON_HOST_LINK_P2; + break; + } + + if (on) { + clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK); + clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK; + clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET; + writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK); + + pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR); + pwr &= ~phypwr; + writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR); + + rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST); + rst |= rstbits; + writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST); + udelay(10); + rst &= ~rstbits; + writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST); + /* The following delay is necessary for the reset sequence to be + * completed */ + udelay(80); + } else { + pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR); + pwr |= phypwr; + writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR); + } +} + +static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst) +{ + /* Order of initialisation is important - first power then isolation */ + exynos4210_phy_pwr(inst, 1); + exynos4210_isol(inst, 0); + + return 0; +} + +static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst) +{ + exynos4210_isol(inst, 1); + exynos4210_phy_pwr(inst, 0); + + return 0; +} + + +static const struct samsung_usb2_common_phy exynos4210_phys[] = { + { + .label = "device", + .id = EXYNOS4210_DEVICE, + .power_on = exynos4210_power_on, + .power_off = exynos4210_power_off, + }, + { + .label = "host", + .id = EXYNOS4210_HOST, + .power_on = exynos4210_power_on, + .power_off = exynos4210_power_off, + }, + { + .label = "hsic0", + .id = EXYNOS4210_HSIC0, + .power_on = exynos4210_power_on, + .power_off = exynos4210_power_off, + }, + { + .label = "hsic1", + .id = EXYNOS4210_HSIC1, + .power_on = exynos4210_power_on, + .power_off = exynos4210_power_off, + }, +}; + +const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = { + .has_mode_switch = 0, + .num_phys = EXYNOS4210_NUM_PHYS, + .phys = exynos4210_phys, + .rate_to_clk = exynos4210_rate_to_clk, +}; diff --git a/drivers/phy/samsung/phy-exynos4x12-usb2.c b/drivers/phy/samsung/phy-exynos4x12-usb2.c new file mode 100644 index 000000000000..7f27a91acf87 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos4x12-usb2.c @@ -0,0 +1,378 @@ +/* + * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Author: Kamil Debski + * + * 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 "phy-samsung-usb2.h" + +/* Exynos USB PHY registers */ + +/* PHY power control */ +#define EXYNOS_4x12_UPHYPWR 0x0 + +#define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND BIT(0) +#define EXYNOS_4x12_UPHYPWR_PHY0_PWR BIT(3) +#define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR BIT(4) +#define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP BIT(5) +#define EXYNOS_4x12_UPHYPWR_PHY0 ( \ + EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \ + EXYNOS_4x12_UPHYPWR_PHY0_PWR | \ + EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \ + EXYNOS_4x12_UPHYPWR_PHY0_SLEEP) + +#define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND BIT(6) +#define EXYNOS_4x12_UPHYPWR_PHY1_PWR BIT(7) +#define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP BIT(8) +#define EXYNOS_4x12_UPHYPWR_PHY1 ( \ + EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \ + EXYNOS_4x12_UPHYPWR_PHY1_PWR | \ + EXYNOS_4x12_UPHYPWR_PHY1_SLEEP) + +#define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND BIT(9) +#define EXYNOS_4x12_UPHYPWR_HSIC0_PWR BIT(10) +#define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP BIT(11) +#define EXYNOS_4x12_UPHYPWR_HSIC0 ( \ + EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \ + EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \ + EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP) + +#define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND BIT(12) +#define EXYNOS_4x12_UPHYPWR_HSIC1_PWR BIT(13) +#define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP BIT(14) +#define EXYNOS_4x12_UPHYPWR_HSIC1 ( \ + EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \ + EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \ + EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP) + +/* PHY clock control */ +#define EXYNOS_4x12_UPHYCLK 0x4 + +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK (0x7 << 0) +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET 0 +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6 (0x0 << 0) +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ (0x1 << 0) +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2 (0x3 << 0) +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ (0x4 << 0) +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0) +#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 0) + +#define EXYNOS_3250_UPHYCLK_REFCLKSEL (0x2 << 8) + +#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP BIT(3) +#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4) +#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7) + +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK (0x7f << 10) +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_OFFSET 10 +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ (0x24 << 10) +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ (0x1c << 10) +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ (0x1a << 10) +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2 (0x15 << 10) +#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ (0x14 << 10) + +/* PHY reset control */ +#define EXYNOS_4x12_UPHYRST 0x8 + +#define EXYNOS_4x12_URSTCON_PHY0 BIT(0) +#define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1) +#define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2) +#define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3) +/* The following bit defines are presented in the + * order taken from the Exynos4412 reference manual. + * + * During experiments with the hardware and debugging + * it was determined that the hardware behaves contrary + * to the manual. + * + * The following bit values were chaned accordingly to the + * results of real hardware experiments. + */ +#define EXYNOS_4x12_URSTCON_PHY1 BIT(4) +#define EXYNOS_4x12_URSTCON_HSIC0 BIT(6) +#define EXYNOS_4x12_URSTCON_HSIC1 BIT(5) +#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7) +#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(10) +#define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9) +#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(8) + +/* Isolation, configured in the power management unit */ +#define EXYNOS_4x12_USB_ISOL_OFFSET 0x704 +#define EXYNOS_4x12_USB_ISOL_OTG BIT(0) +#define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET 0x708 +#define EXYNOS_4x12_USB_ISOL_HSIC0 BIT(0) +#define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET 0x70c +#define EXYNOS_4x12_USB_ISOL_HSIC1 BIT(0) + +/* Mode switching SUB Device <-> Host */ +#define EXYNOS_4x12_MODE_SWITCH_OFFSET 0x21c +#define EXYNOS_4x12_MODE_SWITCH_MASK 1 +#define EXYNOS_4x12_MODE_SWITCH_DEVICE 0 +#define EXYNOS_4x12_MODE_SWITCH_HOST 1 + +enum exynos4x12_phy_id { + EXYNOS4x12_DEVICE, + EXYNOS4x12_HOST, + EXYNOS4x12_HSIC0, + EXYNOS4x12_HSIC1, + EXYNOS4x12_NUM_PHYS, +}; + +/* + * exynos4x12_rate_to_clk() converts the supplied clock rate to the value that + * can be written to the phy register. + */ +static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg) +{ + /* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */ + + switch (rate) { + case 9600 * KHZ: + *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6; + break; + case 10 * MHZ: + *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ; + break; + case 12 * MHZ: + *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ; + break; + case 19200 * KHZ: + *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2; + break; + case 20 * MHZ: + *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ; + break; + case 24 * MHZ: + *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ; + break; + case 50 * MHZ: + *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + u32 offset; + u32 mask; + + switch (inst->cfg->id) { + case EXYNOS4x12_DEVICE: + case EXYNOS4x12_HOST: + offset = EXYNOS_4x12_USB_ISOL_OFFSET; + mask = EXYNOS_4x12_USB_ISOL_OTG; + break; + case EXYNOS4x12_HSIC0: + offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET; + mask = EXYNOS_4x12_USB_ISOL_HSIC0; + break; + case EXYNOS4x12_HSIC1: + offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET; + mask = EXYNOS_4x12_USB_ISOL_HSIC1; + break; + default: + return; + } + + regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); +} + +static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + u32 clk; + + clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK); + clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK; + + if (drv->cfg->has_refclk_sel) + clk = EXYNOS_3250_UPHYCLK_REFCLKSEL; + + clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET; + clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON; + writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK); +} + +static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + u32 rstbits = 0; + u32 phypwr = 0; + u32 rst; + u32 pwr; + + switch (inst->cfg->id) { + case EXYNOS4x12_DEVICE: + phypwr = EXYNOS_4x12_UPHYPWR_PHY0; + rstbits = EXYNOS_4x12_URSTCON_PHY0; + break; + case EXYNOS4x12_HOST: + phypwr = EXYNOS_4x12_UPHYPWR_PHY1; + rstbits = EXYNOS_4x12_URSTCON_HOST_PHY | + EXYNOS_4x12_URSTCON_PHY1 | + EXYNOS_4x12_URSTCON_HOST_LINK_P0; + break; + case EXYNOS4x12_HSIC0: + phypwr = EXYNOS_4x12_UPHYPWR_HSIC0; + rstbits = EXYNOS_4x12_URSTCON_HSIC0 | + EXYNOS_4x12_URSTCON_HOST_LINK_P1; + break; + case EXYNOS4x12_HSIC1: + phypwr = EXYNOS_4x12_UPHYPWR_HSIC1; + rstbits = EXYNOS_4x12_URSTCON_HSIC1 | + EXYNOS_4x12_URSTCON_HOST_LINK_P1; + break; + } + + if (on) { + pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); + pwr &= ~phypwr; + writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); + + rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST); + rst |= rstbits; + writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST); + udelay(10); + rst &= ~rstbits; + writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST); + /* The following delay is necessary for the reset sequence to be + * completed */ + udelay(80); + } else { + pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR); + pwr |= phypwr; + writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR); + } +} + +static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst) +{ + if (inst->int_cnt++ > 0) + return; + + exynos4x12_setup_clk(inst); + exynos4x12_isol(inst, 0); + exynos4x12_phy_pwr(inst, 1); +} + +static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + + if (inst->ext_cnt++ > 0) + return 0; + + if (inst->cfg->id == EXYNOS4x12_HOST) { + regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, + EXYNOS_4x12_MODE_SWITCH_MASK, + EXYNOS_4x12_MODE_SWITCH_HOST); + exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); + } + + if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch) + regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, + EXYNOS_4x12_MODE_SWITCH_MASK, + EXYNOS_4x12_MODE_SWITCH_DEVICE); + + if (inst->cfg->id == EXYNOS4x12_HSIC0 || + inst->cfg->id == EXYNOS4x12_HSIC1) { + exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]); + exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_HOST]); + } + + exynos4x12_power_on_int(inst); + + return 0; +} + +static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst) +{ + if (inst->int_cnt-- > 1) + return; + + exynos4x12_isol(inst, 1); + exynos4x12_phy_pwr(inst, 0); +} + +static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + + if (inst->ext_cnt-- > 1) + return 0; + + if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch) + regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET, + EXYNOS_4x12_MODE_SWITCH_MASK, + EXYNOS_4x12_MODE_SWITCH_HOST); + + if (inst->cfg->id == EXYNOS4x12_HOST) + exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); + + if (inst->cfg->id == EXYNOS4x12_HSIC0 || + inst->cfg->id == EXYNOS4x12_HSIC1) { + exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]); + exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]); + } + + exynos4x12_power_off_int(inst); + + return 0; +} + + +static const struct samsung_usb2_common_phy exynos4x12_phys[] = { + { + .label = "device", + .id = EXYNOS4x12_DEVICE, + .power_on = exynos4x12_power_on, + .power_off = exynos4x12_power_off, + }, + { + .label = "host", + .id = EXYNOS4x12_HOST, + .power_on = exynos4x12_power_on, + .power_off = exynos4x12_power_off, + }, + { + .label = "hsic0", + .id = EXYNOS4x12_HSIC0, + .power_on = exynos4x12_power_on, + .power_off = exynos4x12_power_off, + }, + { + .label = "hsic1", + .id = EXYNOS4x12_HSIC1, + .power_on = exynos4x12_power_on, + .power_off = exynos4x12_power_off, + }, +}; + +const struct samsung_usb2_phy_config exynos3250_usb2_phy_config = { + .has_refclk_sel = 1, + .num_phys = 1, + .phys = exynos4x12_phys, + .rate_to_clk = exynos4x12_rate_to_clk, +}; + +const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = { + .has_mode_switch = 1, + .num_phys = EXYNOS4x12_NUM_PHYS, + .phys = exynos4x12_phys, + .rate_to_clk = exynos4x12_rate_to_clk, +}; diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c new file mode 100644 index 000000000000..7c41daa2c625 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -0,0 +1,782 @@ +/* + * Samsung EXYNOS5 SoC series USB DRD PHY driver + * + * Phy provider for USB 3.0 DRD controller on Exynos5 SoC series + * + * Copyright (C) 2014 Samsung Electronics Co., Ltd. + * Author: Vivek Gautam + * + * 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 +#include +#include +#include +#include + +/* Exynos USB PHY registers */ +#define EXYNOS5_FSEL_9MHZ6 0x0 +#define EXYNOS5_FSEL_10MHZ 0x1 +#define EXYNOS5_FSEL_12MHZ 0x2 +#define EXYNOS5_FSEL_19MHZ2 0x3 +#define EXYNOS5_FSEL_20MHZ 0x4 +#define EXYNOS5_FSEL_24MHZ 0x5 +#define EXYNOS5_FSEL_50MHZ 0x7 + +/* EXYNOS5: USB 3.0 DRD PHY registers */ +#define EXYNOS5_DRD_LINKSYSTEM 0x04 + +#define LINKSYSTEM_FLADJ_MASK (0x3f << 1) +#define LINKSYSTEM_FLADJ(_x) ((_x) << 1) +#define LINKSYSTEM_XHCI_VERSION_CONTROL BIT(27) + +#define EXYNOS5_DRD_PHYUTMI 0x08 + +#define PHYUTMI_OTGDISABLE BIT(6) +#define PHYUTMI_FORCESUSPEND BIT(1) +#define PHYUTMI_FORCESLEEP BIT(0) + +#define EXYNOS5_DRD_PHYPIPE 0x0c + +#define EXYNOS5_DRD_PHYCLKRST 0x10 + +#define PHYCLKRST_EN_UTMISUSPEND BIT(31) + +#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) +#define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23) + +#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) +#define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21) + +#define PHYCLKRST_SSC_EN BIT(20) +#define PHYCLKRST_REF_SSP_EN BIT(19) +#define PHYCLKRST_REF_CLKDIV2 BIT(18) + +#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF (0x32 << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF (0x68 << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF (0x7d << 11) +#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11) + +#define PHYCLKRST_FSEL_UTMI_MASK (0x7 << 5) +#define PHYCLKRST_FSEL_PIPE_MASK (0x7 << 8) +#define PHYCLKRST_FSEL(_x) ((_x) << 5) +#define PHYCLKRST_FSEL_PAD_100MHZ (0x27 << 5) +#define PHYCLKRST_FSEL_PAD_24MHZ (0x2a << 5) +#define PHYCLKRST_FSEL_PAD_20MHZ (0x31 << 5) +#define PHYCLKRST_FSEL_PAD_19_2MHZ (0x38 << 5) + +#define PHYCLKRST_RETENABLEN BIT(4) + +#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) +#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2) +#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2) + +#define PHYCLKRST_PORTRESET BIT(1) +#define PHYCLKRST_COMMONONN BIT(0) + +#define EXYNOS5_DRD_PHYREG0 0x14 +#define EXYNOS5_DRD_PHYREG1 0x18 + +#define EXYNOS5_DRD_PHYPARAM0 0x1c + +#define PHYPARAM0_REF_USE_PAD BIT(31) +#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) +#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) + +#define EXYNOS5_DRD_PHYPARAM1 0x20 + +#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) +#define PHYPARAM1_PCS_TXDEEMPH (0x1c) + +#define EXYNOS5_DRD_PHYTERM 0x24 + +#define EXYNOS5_DRD_PHYTEST 0x28 + +#define PHYTEST_POWERDOWN_SSP BIT(3) +#define PHYTEST_POWERDOWN_HSP BIT(2) + +#define EXYNOS5_DRD_PHYADP 0x2c + +#define EXYNOS5_DRD_PHYUTMICLKSEL 0x30 + +#define PHYUTMICLKSEL_UTMI_CLKSEL BIT(2) + +#define EXYNOS5_DRD_PHYRESUME 0x34 +#define EXYNOS5_DRD_LINKPORT 0x44 + +#define KHZ 1000 +#define MHZ (KHZ * KHZ) + +enum exynos5_usbdrd_phy_id { + EXYNOS5_DRDPHY_UTMI, + EXYNOS5_DRDPHY_PIPE3, + EXYNOS5_DRDPHYS_NUM, +}; + +struct phy_usb_instance; +struct exynos5_usbdrd_phy; + +struct exynos5_usbdrd_phy_config { + u32 id; + void (*phy_isol)(struct phy_usb_instance *inst, u32 on); + void (*phy_init)(struct exynos5_usbdrd_phy *phy_drd); + unsigned int (*set_refclk)(struct phy_usb_instance *inst); +}; + +struct exynos5_usbdrd_phy_drvdata { + const struct exynos5_usbdrd_phy_config *phy_cfg; + u32 pmu_offset_usbdrd0_phy; + u32 pmu_offset_usbdrd1_phy; + bool has_common_clk_gate; +}; + +/** + * struct exynos5_usbdrd_phy - driver data for USB 3.0 PHY + * @dev: pointer to device instance of this platform device + * @reg_phy: usb phy controller register memory base + * @clk: phy clock for register access + * @pipeclk: clock for pipe3 phy + * @utmiclk: clock for utmi+ phy + * @itpclk: clock for ITP generation + * @drv_data: pointer to SoC level driver data structure + * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY + * instances each with its 'phy' and 'phy_cfg'. + * @extrefclk: frequency select settings when using 'separate + * reference clocks' for SS and HS operations + * @ref_clk: reference clock to PHY block from which PHY's + * operational clocks are derived + * vbus: VBUS regulator for phy + * vbus_boost: Boost regulator for VBUS present on few Exynos boards + */ +struct exynos5_usbdrd_phy { + struct device *dev; + void __iomem *reg_phy; + struct clk *clk; + struct clk *pipeclk; + struct clk *utmiclk; + struct clk *itpclk; + const struct exynos5_usbdrd_phy_drvdata *drv_data; + struct phy_usb_instance { + struct phy *phy; + u32 index; + struct regmap *reg_pmu; + u32 pmu_offset; + const struct exynos5_usbdrd_phy_config *phy_cfg; + } phys[EXYNOS5_DRDPHYS_NUM]; + u32 extrefclk; + struct clk *ref_clk; + struct regulator *vbus; + struct regulator *vbus_boost; +}; + +static inline +struct exynos5_usbdrd_phy *to_usbdrd_phy(struct phy_usb_instance *inst) +{ + return container_of((inst), struct exynos5_usbdrd_phy, + phys[(inst)->index]); +} + +/* + * exynos5_rate_to_clk() converts the supplied clock rate to the value that + * can be written to the phy register. + */ +static unsigned int exynos5_rate_to_clk(unsigned long rate, u32 *reg) +{ + /* EXYNOS5_FSEL_MASK */ + + switch (rate) { + case 9600 * KHZ: + *reg = EXYNOS5_FSEL_9MHZ6; + break; + case 10 * MHZ: + *reg = EXYNOS5_FSEL_10MHZ; + break; + case 12 * MHZ: + *reg = EXYNOS5_FSEL_12MHZ; + break; + case 19200 * KHZ: + *reg = EXYNOS5_FSEL_19MHZ2; + break; + case 20 * MHZ: + *reg = EXYNOS5_FSEL_20MHZ; + break; + case 24 * MHZ: + *reg = EXYNOS5_FSEL_24MHZ; + break; + case 50 * MHZ: + *reg = EXYNOS5_FSEL_50MHZ; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void exynos5_usbdrd_phy_isol(struct phy_usb_instance *inst, + unsigned int on) +{ + unsigned int val; + + if (!inst->reg_pmu) + return; + + val = on ? 0 : EXYNOS4_PHY_ENABLE; + + regmap_update_bits(inst->reg_pmu, inst->pmu_offset, + EXYNOS4_PHY_ENABLE, val); +} + +/* + * Sets the pipe3 phy's clk as EXTREFCLK (XXTI) which is internal clock + * from clock core. Further sets multiplier values and spread spectrum + * clock settings for SuperSpeed operations. + */ +static unsigned int +exynos5_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst) +{ + u32 reg; + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + + /* restore any previous reference clock settings */ + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); + + /* Use EXTREFCLK as ref clock */ + reg &= ~PHYCLKRST_REFCLKSEL_MASK; + reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; + + /* FSEL settings corresponding to reference clock */ + reg &= ~PHYCLKRST_FSEL_PIPE_MASK | + PHYCLKRST_MPLL_MULTIPLIER_MASK | + PHYCLKRST_SSC_REFCLKSEL_MASK; + switch (phy_drd->extrefclk) { + case EXYNOS5_FSEL_50MHZ: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF | + PHYCLKRST_SSC_REFCLKSEL(0x00)); + break; + case EXYNOS5_FSEL_24MHZ: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x88)); + break; + case EXYNOS5_FSEL_20MHZ: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x00)); + break; + case EXYNOS5_FSEL_19MHZ2: + reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF | + PHYCLKRST_SSC_REFCLKSEL(0x88)); + break; + default: + dev_dbg(phy_drd->dev, "unsupported ref clk\n"); + break; + } + + return reg; +} + +/* + * Sets the utmi phy's clk as EXTREFCLK (XXTI) which is internal clock + * from clock core. Further sets the FSEL values for HighSpeed operations. + */ +static unsigned int +exynos5_usbdrd_utmi_set_refclk(struct phy_usb_instance *inst) +{ + u32 reg; + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + + /* restore any previous reference clock settings */ + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); + + reg &= ~PHYCLKRST_REFCLKSEL_MASK; + reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; + + reg &= ~PHYCLKRST_FSEL_UTMI_MASK | + PHYCLKRST_MPLL_MULTIPLIER_MASK | + PHYCLKRST_SSC_REFCLKSEL_MASK; + reg |= PHYCLKRST_FSEL(phy_drd->extrefclk); + + return reg; +} + +static void exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd) +{ + u32 reg; + + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); + /* Set Tx De-Emphasis level */ + reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; + reg |= PHYPARAM1_PCS_TXDEEMPH; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); + + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); + reg &= ~PHYTEST_POWERDOWN_SSP; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); +} + +static void exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) +{ + u32 reg; + + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); + /* Set Loss-of-Signal Detector sensitivity */ + reg &= ~PHYPARAM0_REF_LOSLEVEL_MASK; + reg |= PHYPARAM0_REF_LOSLEVEL; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); + + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); + /* Set Tx De-Emphasis level */ + reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; + reg |= PHYPARAM1_PCS_TXDEEMPH; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); + + /* UTMI Power Control */ + writel(PHYUTMI_OTGDISABLE, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); + + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); + reg &= ~PHYTEST_POWERDOWN_HSP; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); +} + +static int exynos5_usbdrd_phy_init(struct phy *phy) +{ + int ret; + u32 reg; + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + + ret = clk_prepare_enable(phy_drd->clk); + if (ret) + return ret; + + /* Reset USB 3.0 PHY */ + writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); + writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYRESUME); + + /* + * Setting the Frame length Adj value[6:1] to default 0x20 + * See xHCI 1.0 spec, 5.2.4 + */ + reg = LINKSYSTEM_XHCI_VERSION_CONTROL | + LINKSYSTEM_FLADJ(0x20); + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_LINKSYSTEM); + + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); + /* Select PHY CLK source */ + reg &= ~PHYPARAM0_REF_USE_PAD; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); + + /* This bit must be set for both HS and SS operations */ + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); + reg |= PHYUTMICLKSEL_UTMI_CLKSEL; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); + + /* UTMI or PIPE3 specific init */ + inst->phy_cfg->phy_init(phy_drd); + + /* reference clock settings */ + reg = inst->phy_cfg->set_refclk(inst); + + /* Digital power supply in normal operating mode */ + reg |= PHYCLKRST_RETENABLEN | + /* Enable ref clock for SS function */ + PHYCLKRST_REF_SSP_EN | + /* Enable spread spectrum */ + PHYCLKRST_SSC_EN | + /* Power down HS Bias and PLL blocks in suspend mode */ + PHYCLKRST_COMMONONN | + /* Reset the port */ + PHYCLKRST_PORTRESET; + + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); + + udelay(10); + + reg &= ~PHYCLKRST_PORTRESET; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); + + clk_disable_unprepare(phy_drd->clk); + + return 0; +} + +static int exynos5_usbdrd_phy_exit(struct phy *phy) +{ + int ret; + u32 reg; + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + + ret = clk_prepare_enable(phy_drd->clk); + if (ret) + return ret; + + reg = PHYUTMI_OTGDISABLE | + PHYUTMI_FORCESUSPEND | + PHYUTMI_FORCESLEEP; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); + + /* Resetting the PHYCLKRST enable bits to reduce leakage current */ + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); + reg &= ~(PHYCLKRST_REF_SSP_EN | + PHYCLKRST_SSC_EN | + PHYCLKRST_COMMONONN); + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); + + /* Control PHYTEST to remove leakage current */ + reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); + reg |= PHYTEST_POWERDOWN_SSP | + PHYTEST_POWERDOWN_HSP; + writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); + + clk_disable_unprepare(phy_drd->clk); + + return 0; +} + +static int exynos5_usbdrd_phy_power_on(struct phy *phy) +{ + int ret; + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + + dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n"); + + clk_prepare_enable(phy_drd->ref_clk); + if (!phy_drd->drv_data->has_common_clk_gate) { + clk_prepare_enable(phy_drd->pipeclk); + clk_prepare_enable(phy_drd->utmiclk); + clk_prepare_enable(phy_drd->itpclk); + } + + /* Enable VBUS supply */ + if (phy_drd->vbus_boost) { + ret = regulator_enable(phy_drd->vbus_boost); + if (ret) { + dev_err(phy_drd->dev, + "Failed to enable VBUS boost supply\n"); + goto fail_vbus; + } + } + + if (phy_drd->vbus) { + ret = regulator_enable(phy_drd->vbus); + if (ret) { + dev_err(phy_drd->dev, "Failed to enable VBUS supply\n"); + goto fail_vbus_boost; + } + } + + /* Power-on PHY*/ + inst->phy_cfg->phy_isol(inst, 0); + + return 0; + +fail_vbus_boost: + if (phy_drd->vbus_boost) + regulator_disable(phy_drd->vbus_boost); + +fail_vbus: + clk_disable_unprepare(phy_drd->ref_clk); + if (!phy_drd->drv_data->has_common_clk_gate) { + clk_disable_unprepare(phy_drd->itpclk); + clk_disable_unprepare(phy_drd->utmiclk); + clk_disable_unprepare(phy_drd->pipeclk); + } + + return ret; +} + +static int exynos5_usbdrd_phy_power_off(struct phy *phy) +{ + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + + dev_dbg(phy_drd->dev, "Request to power_off usbdrd_phy phy\n"); + + /* Power-off the PHY */ + inst->phy_cfg->phy_isol(inst, 1); + + /* Disable VBUS supply */ + if (phy_drd->vbus) + regulator_disable(phy_drd->vbus); + if (phy_drd->vbus_boost) + regulator_disable(phy_drd->vbus_boost); + + clk_disable_unprepare(phy_drd->ref_clk); + if (!phy_drd->drv_data->has_common_clk_gate) { + clk_disable_unprepare(phy_drd->itpclk); + clk_disable_unprepare(phy_drd->pipeclk); + clk_disable_unprepare(phy_drd->utmiclk); + } + + return 0; +} + +static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct exynos5_usbdrd_phy *phy_drd = dev_get_drvdata(dev); + + if (WARN_ON(args->args[0] >= EXYNOS5_DRDPHYS_NUM)) + return ERR_PTR(-ENODEV); + + return phy_drd->phys[args->args[0]].phy; +} + +static const struct phy_ops exynos5_usbdrd_phy_ops = { + .init = exynos5_usbdrd_phy_init, + .exit = exynos5_usbdrd_phy_exit, + .power_on = exynos5_usbdrd_phy_power_on, + .power_off = exynos5_usbdrd_phy_power_off, + .owner = THIS_MODULE, +}; + +static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd) +{ + unsigned long ref_rate; + int ret; + + phy_drd->clk = devm_clk_get(phy_drd->dev, "phy"); + if (IS_ERR(phy_drd->clk)) { + dev_err(phy_drd->dev, "Failed to get phy clock\n"); + return PTR_ERR(phy_drd->clk); + } + + phy_drd->ref_clk = devm_clk_get(phy_drd->dev, "ref"); + if (IS_ERR(phy_drd->ref_clk)) { + dev_err(phy_drd->dev, "Failed to get phy reference clock\n"); + return PTR_ERR(phy_drd->ref_clk); + } + ref_rate = clk_get_rate(phy_drd->ref_clk); + + ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); + if (ret) { + dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n", + ref_rate); + return ret; + } + + if (!phy_drd->drv_data->has_common_clk_gate) { + phy_drd->pipeclk = devm_clk_get(phy_drd->dev, "phy_pipe"); + if (IS_ERR(phy_drd->pipeclk)) { + dev_info(phy_drd->dev, + "PIPE3 phy operational clock not specified\n"); + phy_drd->pipeclk = NULL; + } + + phy_drd->utmiclk = devm_clk_get(phy_drd->dev, "phy_utmi"); + if (IS_ERR(phy_drd->utmiclk)) { + dev_info(phy_drd->dev, + "UTMI phy operational clock not specified\n"); + phy_drd->utmiclk = NULL; + } + + phy_drd->itpclk = devm_clk_get(phy_drd->dev, "itp"); + if (IS_ERR(phy_drd->itpclk)) { + dev_info(phy_drd->dev, + "ITP clock from main OSC not specified\n"); + phy_drd->itpclk = NULL; + } + } + + return 0; +} + +static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { + { + .id = EXYNOS5_DRDPHY_UTMI, + .phy_isol = exynos5_usbdrd_phy_isol, + .phy_init = exynos5_usbdrd_utmi_init, + .set_refclk = exynos5_usbdrd_utmi_set_refclk, + }, + { + .id = EXYNOS5_DRDPHY_PIPE3, + .phy_isol = exynos5_usbdrd_phy_isol, + .phy_init = exynos5_usbdrd_pipe3_init, + .set_refclk = exynos5_usbdrd_pipe3_set_refclk, + }, +}; + +static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { + .phy_cfg = phy_cfg_exynos5, + .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, + .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL, + .has_common_clk_gate = true, +}; + +static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { + .phy_cfg = phy_cfg_exynos5, + .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, + .has_common_clk_gate = true, +}; + +static const struct exynos5_usbdrd_phy_drvdata exynos5433_usbdrd_phy = { + .phy_cfg = phy_cfg_exynos5, + .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, + .pmu_offset_usbdrd1_phy = EXYNOS5433_USBHOST30_PHY_CONTROL, + .has_common_clk_gate = false, +}; + +static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = { + .phy_cfg = phy_cfg_exynos5, + .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, + .has_common_clk_gate = false, +}; + +static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { + { + .compatible = "samsung,exynos5250-usbdrd-phy", + .data = &exynos5250_usbdrd_phy + }, { + .compatible = "samsung,exynos5420-usbdrd-phy", + .data = &exynos5420_usbdrd_phy + }, { + .compatible = "samsung,exynos5433-usbdrd-phy", + .data = &exynos5433_usbdrd_phy + }, { + .compatible = "samsung,exynos7-usbdrd-phy", + .data = &exynos7_usbdrd_phy + }, + { }, +}; +MODULE_DEVICE_TABLE(of, exynos5_usbdrd_phy_of_match); + +static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct exynos5_usbdrd_phy *phy_drd; + struct phy_provider *phy_provider; + struct resource *res; + const struct of_device_id *match; + const struct exynos5_usbdrd_phy_drvdata *drv_data; + struct regmap *reg_pmu; + u32 pmu_offset; + int i, ret; + int channel; + + phy_drd = devm_kzalloc(dev, sizeof(*phy_drd), GFP_KERNEL); + if (!phy_drd) + return -ENOMEM; + + dev_set_drvdata(dev, phy_drd); + phy_drd->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy_drd->reg_phy = devm_ioremap_resource(dev, res); + if (IS_ERR(phy_drd->reg_phy)) + return PTR_ERR(phy_drd->reg_phy); + + match = of_match_node(exynos5_usbdrd_phy_of_match, pdev->dev.of_node); + + drv_data = match->data; + phy_drd->drv_data = drv_data; + + ret = exynos5_usbdrd_phy_clk_handle(phy_drd); + if (ret) { + dev_err(dev, "Failed to initialize clocks\n"); + return ret; + } + + reg_pmu = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,pmu-syscon"); + if (IS_ERR(reg_pmu)) { + dev_err(dev, "Failed to lookup PMU regmap\n"); + return PTR_ERR(reg_pmu); + } + + /* + * Exynos5420 SoC has multiple channels for USB 3.0 PHY, with + * each having separate power control registers. + * 'channel' facilitates to set such registers. + */ + channel = of_alias_get_id(node, "usbdrdphy"); + if (channel < 0) + dev_dbg(dev, "Not a multi-controller usbdrd phy\n"); + + switch (channel) { + case 1: + pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd1_phy; + break; + case 0: + default: + pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd0_phy; + break; + } + + /* Get Vbus regulators */ + phy_drd->vbus = devm_regulator_get(dev, "vbus"); + if (IS_ERR(phy_drd->vbus)) { + ret = PTR_ERR(phy_drd->vbus); + if (ret == -EPROBE_DEFER) + return ret; + + dev_warn(dev, "Failed to get VBUS supply regulator\n"); + phy_drd->vbus = NULL; + } + + phy_drd->vbus_boost = devm_regulator_get(dev, "vbus-boost"); + if (IS_ERR(phy_drd->vbus_boost)) { + ret = PTR_ERR(phy_drd->vbus_boost); + if (ret == -EPROBE_DEFER) + return ret; + + dev_warn(dev, "Failed to get VBUS boost supply regulator\n"); + phy_drd->vbus_boost = NULL; + } + + dev_vdbg(dev, "Creating usbdrd_phy phy\n"); + + for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { + struct phy *phy = devm_phy_create(dev, NULL, + &exynos5_usbdrd_phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "Failed to create usbdrd_phy phy\n"); + return PTR_ERR(phy); + } + + phy_drd->phys[i].phy = phy; + phy_drd->phys[i].index = i; + phy_drd->phys[i].reg_pmu = reg_pmu; + phy_drd->phys[i].pmu_offset = pmu_offset; + phy_drd->phys[i].phy_cfg = &drv_data->phy_cfg[i]; + phy_set_drvdata(phy, &phy_drd->phys[i]); + } + + phy_provider = devm_of_phy_provider_register(dev, + exynos5_usbdrd_phy_xlate); + if (IS_ERR(phy_provider)) { + dev_err(phy_drd->dev, "Failed to register phy provider\n"); + return PTR_ERR(phy_provider); + } + + return 0; +} + +static struct platform_driver exynos5_usb3drd_phy = { + .probe = exynos5_usbdrd_phy_probe, + .driver = { + .of_match_table = exynos5_usbdrd_phy_of_match, + .name = "exynos5_usb3drd_phy", + } +}; + +module_platform_driver(exynos5_usb3drd_phy); +MODULE_DESCRIPTION("Samsung EXYNOS5 SoCs USB 3.0 DRD controller PHY driver"); +MODULE_AUTHOR("Vivek Gautam "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:exynos5_usb3drd_phy"); diff --git a/drivers/phy/samsung/phy-exynos5250-sata.c b/drivers/phy/samsung/phy-exynos5250-sata.c new file mode 100644 index 000000000000..60e13afcd9b8 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos5250-sata.c @@ -0,0 +1,250 @@ +/* + * Samsung SATA SerDes(PHY) driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Authors: Girish K S + * Yuvaraj Kumar C D + * + * 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 +#include +#include +#include + +#define SATAPHY_CONTROL_OFFSET 0x0724 +#define EXYNOS5_SATAPHY_PMU_ENABLE BIT(0) +#define EXYNOS5_SATA_RESET 0x4 +#define RESET_GLOBAL_RST_N BIT(0) +#define RESET_CMN_RST_N BIT(1) +#define RESET_CMN_BLOCK_RST_N BIT(2) +#define RESET_CMN_I2C_RST_N BIT(3) +#define RESET_TX_RX_PIPE_RST_N BIT(4) +#define RESET_TX_RX_BLOCK_RST_N BIT(5) +#define RESET_TX_RX_I2C_RST_N (BIT(6) | BIT(7)) +#define LINK_RESET 0xf0000 +#define EXYNOS5_SATA_MODE0 0x10 +#define SATA_SPD_GEN3 BIT(1) +#define EXYNOS5_SATA_CTRL0 0x14 +#define CTRL0_P0_PHY_CALIBRATED_SEL BIT(9) +#define CTRL0_P0_PHY_CALIBRATED BIT(8) +#define EXYNOS5_SATA_PHSATA_CTRLM 0xe0 +#define PHCTRLM_REF_RATE BIT(1) +#define PHCTRLM_HIGH_SPEED BIT(0) +#define EXYNOS5_SATA_PHSATA_STATM 0xf0 +#define PHSTATM_PLL_LOCKED BIT(0) + +#define PHY_PLL_TIMEOUT (usecs_to_jiffies(1000)) + +struct exynos_sata_phy { + struct phy *phy; + struct clk *phyclk; + void __iomem *regs; + struct regmap *pmureg; + struct i2c_client *client; +}; + +static int wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit, + u32 status) +{ + unsigned long timeout = jiffies + PHY_PLL_TIMEOUT; + + while (time_before(jiffies, timeout)) { + if ((readl(base + reg) & checkbit) == status) + return 0; + } + + return -EFAULT; +} + +static int exynos_sata_phy_power_on(struct phy *phy) +{ + struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy); + + return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET, + EXYNOS5_SATAPHY_PMU_ENABLE, true); + +} + +static int exynos_sata_phy_power_off(struct phy *phy) +{ + struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy); + + return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET, + EXYNOS5_SATAPHY_PMU_ENABLE, false); + +} + +static int exynos_sata_phy_init(struct phy *phy) +{ + u32 val = 0; + int ret = 0; + u8 buf[] = { 0x3a, 0x0b }; + struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy); + + ret = regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET, + EXYNOS5_SATAPHY_PMU_ENABLE, true); + if (ret != 0) + dev_err(&sata_phy->phy->dev, "phy init failed\n"); + + writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); + val |= RESET_GLOBAL_RST_N | RESET_CMN_RST_N | RESET_CMN_BLOCK_RST_N + | RESET_CMN_I2C_RST_N | RESET_TX_RX_PIPE_RST_N + | RESET_TX_RX_BLOCK_RST_N | RESET_TX_RX_I2C_RST_N; + writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); + val |= LINK_RESET; + writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); + val |= RESET_CMN_RST_N; + writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); + val &= ~PHCTRLM_REF_RATE; + writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); + + /* High speed enable for Gen3 */ + val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); + val |= PHCTRLM_HIGH_SPEED; + writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM); + + val = readl(sata_phy->regs + EXYNOS5_SATA_CTRL0); + val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED; + writel(val, sata_phy->regs + EXYNOS5_SATA_CTRL0); + + val = readl(sata_phy->regs + EXYNOS5_SATA_MODE0); + val |= SATA_SPD_GEN3; + writel(val, sata_phy->regs + EXYNOS5_SATA_MODE0); + + ret = i2c_master_send(sata_phy->client, buf, sizeof(buf)); + if (ret < 0) + return ret; + + /* release cmu reset */ + val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); + val &= ~RESET_CMN_RST_N; + writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); + + val = readl(sata_phy->regs + EXYNOS5_SATA_RESET); + val |= RESET_CMN_RST_N; + writel(val, sata_phy->regs + EXYNOS5_SATA_RESET); + + ret = wait_for_reg_status(sata_phy->regs, + EXYNOS5_SATA_PHSATA_STATM, + PHSTATM_PLL_LOCKED, 1); + if (ret < 0) + dev_err(&sata_phy->phy->dev, + "PHY PLL locking failed\n"); + return ret; +} + +static const struct phy_ops exynos_sata_phy_ops = { + .init = exynos_sata_phy_init, + .power_on = exynos_sata_phy_power_on, + .power_off = exynos_sata_phy_power_off, + .owner = THIS_MODULE, +}; + +static int exynos_sata_phy_probe(struct platform_device *pdev) +{ + struct exynos_sata_phy *sata_phy; + struct device *dev = &pdev->dev; + struct resource *res; + struct phy_provider *phy_provider; + struct device_node *node; + int ret = 0; + + sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL); + if (!sata_phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + sata_phy->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(sata_phy->regs)) + return PTR_ERR(sata_phy->regs); + + sata_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node, + "samsung,syscon-phandle"); + if (IS_ERR(sata_phy->pmureg)) { + dev_err(dev, "syscon regmap lookup failed.\n"); + return PTR_ERR(sata_phy->pmureg); + } + + node = of_parse_phandle(dev->of_node, + "samsung,exynos-sataphy-i2c-phandle", 0); + if (!node) + return -EINVAL; + + sata_phy->client = of_find_i2c_device_by_node(node); + if (!sata_phy->client) + return -EPROBE_DEFER; + + dev_set_drvdata(dev, sata_phy); + + sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl"); + if (IS_ERR(sata_phy->phyclk)) { + dev_err(dev, "failed to get clk for PHY\n"); + return PTR_ERR(sata_phy->phyclk); + } + + ret = clk_prepare_enable(sata_phy->phyclk); + if (ret < 0) { + dev_err(dev, "failed to enable source clk\n"); + return ret; + } + + sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops); + if (IS_ERR(sata_phy->phy)) { + clk_disable_unprepare(sata_phy->phyclk); + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(sata_phy->phy); + } + + phy_set_drvdata(sata_phy->phy, sata_phy); + + phy_provider = devm_of_phy_provider_register(dev, + of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + clk_disable_unprepare(sata_phy->phyclk); + return PTR_ERR(phy_provider); + } + + return 0; +} + +static const struct of_device_id exynos_sata_phy_of_match[] = { + { .compatible = "samsung,exynos5250-sata-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, exynos_sata_phy_of_match); + +static struct platform_driver exynos_sata_phy_driver = { + .probe = exynos_sata_phy_probe, + .driver = { + .of_match_table = exynos_sata_phy_of_match, + .name = "samsung,sata-phy", + } +}; +module_platform_driver(exynos_sata_phy_driver); + +MODULE_DESCRIPTION("Samsung SerDes PHY driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Girish K S "); +MODULE_AUTHOR("Yuvaraj C D "); diff --git a/drivers/phy/samsung/phy-exynos5250-usb2.c b/drivers/phy/samsung/phy-exynos5250-usb2.c new file mode 100644 index 000000000000..aad806272305 --- /dev/null +++ b/drivers/phy/samsung/phy-exynos5250-usb2.c @@ -0,0 +1,401 @@ +/* + * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 5250 support + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Author: Kamil Debski + * + * 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 "phy-samsung-usb2.h" + +/* Exynos USB PHY registers */ +#define EXYNOS_5250_REFCLKSEL_CRYSTAL 0x0 +#define EXYNOS_5250_REFCLKSEL_XO 0x1 +#define EXYNOS_5250_REFCLKSEL_CLKCORE 0x2 + +#define EXYNOS_5250_FSEL_9MHZ6 0x0 +#define EXYNOS_5250_FSEL_10MHZ 0x1 +#define EXYNOS_5250_FSEL_12MHZ 0x2 +#define EXYNOS_5250_FSEL_19MHZ2 0x3 +#define EXYNOS_5250_FSEL_20MHZ 0x4 +#define EXYNOS_5250_FSEL_24MHZ 0x5 +#define EXYNOS_5250_FSEL_50MHZ 0x7 + +/* Normal host */ +#define EXYNOS_5250_HOSTPHYCTRL0 0x0 + +#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL BIT(31) +#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT 19 +#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_MASK \ + (0x3 << EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT) +#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT 16 +#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK \ + (0x7 << EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT) +#define EXYNOS_5250_HOSTPHYCTRL0_TESTBURNIN BIT(11) +#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE BIT(10) +#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N BIT(9) +#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_MASK (0x3 << 7) +#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_DUAL (0x0 << 7) +#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ID0 (0x1 << 7) +#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ANALOGTEST (0x2 << 7) +#define EXYNOS_5250_HOSTPHYCTRL0_SIDDQ BIT(6) +#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP BIT(5) +#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND BIT(4) +#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE BIT(3) +#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST BIT(2) +#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST BIT(1) +#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST BIT(0) + +/* HSIC0 & HSIC1 */ +#define EXYNOS_5250_HSICPHYCTRL1 0x10 +#define EXYNOS_5250_HSICPHYCTRL2 0x20 + +#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_MASK (0x3 << 23) +#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT (0x2 << 23) +#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_MASK (0x7f << 16) +#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 (0x24 << 16) +#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_15 (0x1c << 16) +#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_16 (0x1a << 16) +#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_19_2 (0x15 << 16) +#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_20 (0x14 << 16) +#define EXYNOS_5250_HSICPHYCTRLX_SIDDQ BIT(6) +#define EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP BIT(5) +#define EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND BIT(4) +#define EXYNOS_5250_HSICPHYCTRLX_WORDINTERFACE BIT(3) +#define EXYNOS_5250_HSICPHYCTRLX_UTMISWRST BIT(2) +#define EXYNOS_5250_HSICPHYCTRLX_PHYSWRST BIT(0) + +/* EHCI control */ +#define EXYNOS_5250_HOSTEHCICTRL 0x30 +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN BIT(29) +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 BIT(28) +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 BIT(27) +#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16 BIT(26) +#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN BIT(25) +#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT 19 +#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK \ + (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT) +#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT 13 +#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_MASK \ + (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT) +#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL2_SHIFT 7 +#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK \ + (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT) +#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT 1 +#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_MASK \ + (0x1 << EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT) +#define EXYNOS_5250_HOSTEHCICTRL_SIMULATIONMODE BIT(0) + +/* OHCI control */ +#define EXYNOS_5250_HOSTOHCICTRL 0x34 +#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT 1 +#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_MASK \ + (0x3ff << EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT) +#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVALEN BIT(0) + +/* USBOTG */ +#define EXYNOS_5250_USBOTGSYS 0x38 +#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET BIT(14) +#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG BIT(13) +#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST BIT(12) +#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT 9 +#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK \ + (0x3 << EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT) +#define EXYNOS_5250_USBOTGSYS_ID_PULLUP BIT(8) +#define EXYNOS_5250_USBOTGSYS_COMMON_ON BIT(7) +#define EXYNOS_5250_USBOTGSYS_FSEL_SHIFT 4 +#define EXYNOS_5250_USBOTGSYS_FSEL_MASK \ + (0x3 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT) +#define EXYNOS_5250_USBOTGSYS_FORCE_SLEEP BIT(3) +#define EXYNOS_5250_USBOTGSYS_OTGDISABLE BIT(2) +#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG BIT(1) +#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND BIT(0) + +/* Isolation, configured in the power management unit */ +#define EXYNOS_5250_USB_ISOL_OTG_OFFSET 0x704 +#define EXYNOS_5250_USB_ISOL_OTG BIT(0) +#define EXYNOS_5250_USB_ISOL_HOST_OFFSET 0x708 +#define EXYNOS_5250_USB_ISOL_HOST BIT(0) + +/* Mode swtich register */ +#define EXYNOS_5250_MODE_SWITCH_OFFSET 0x230 +#define EXYNOS_5250_MODE_SWITCH_MASK 1 +#define EXYNOS_5250_MODE_SWITCH_DEVICE 0 +#define EXYNOS_5250_MODE_SWITCH_HOST 1 + +enum exynos4x12_phy_id { + EXYNOS5250_DEVICE, + EXYNOS5250_HOST, + EXYNOS5250_HSIC0, + EXYNOS5250_HSIC1, + EXYNOS5250_NUM_PHYS, +}; + +/* + * exynos5250_rate_to_clk() converts the supplied clock rate to the value that + * can be written to the phy register. + */ +static int exynos5250_rate_to_clk(unsigned long rate, u32 *reg) +{ + /* EXYNOS_5250_FSEL_MASK */ + + switch (rate) { + case 9600 * KHZ: + *reg = EXYNOS_5250_FSEL_9MHZ6; + break; + case 10 * MHZ: + *reg = EXYNOS_5250_FSEL_10MHZ; + break; + case 12 * MHZ: + *reg = EXYNOS_5250_FSEL_12MHZ; + break; + case 19200 * KHZ: + *reg = EXYNOS_5250_FSEL_19MHZ2; + break; + case 20 * MHZ: + *reg = EXYNOS_5250_FSEL_20MHZ; + break; + case 24 * MHZ: + *reg = EXYNOS_5250_FSEL_24MHZ; + break; + case 50 * MHZ: + *reg = EXYNOS_5250_FSEL_50MHZ; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void exynos5250_isol(struct samsung_usb2_phy_instance *inst, bool on) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + u32 offset; + u32 mask; + + switch (inst->cfg->id) { + case EXYNOS5250_DEVICE: + offset = EXYNOS_5250_USB_ISOL_OTG_OFFSET; + mask = EXYNOS_5250_USB_ISOL_OTG; + break; + case EXYNOS5250_HOST: + offset = EXYNOS_5250_USB_ISOL_HOST_OFFSET; + mask = EXYNOS_5250_USB_ISOL_HOST; + break; + default: + return; + } + + regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask); +} + +static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + u32 ctrl0; + u32 otg; + u32 ehci; + u32 ohci; + u32 hsic; + + switch (inst->cfg->id) { + case EXYNOS5250_DEVICE: + regmap_update_bits(drv->reg_sys, + EXYNOS_5250_MODE_SWITCH_OFFSET, + EXYNOS_5250_MODE_SWITCH_MASK, + EXYNOS_5250_MODE_SWITCH_DEVICE); + + /* OTG configuration */ + otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS); + /* The clock */ + otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK; + otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT; + /* Reset */ + otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND | + EXYNOS_5250_USBOTGSYS_FORCE_SLEEP | + EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG); + otg |= EXYNOS_5250_USBOTGSYS_PHY_SW_RST | + EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET | + EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | + EXYNOS_5250_USBOTGSYS_OTGDISABLE; + /* Ref clock */ + otg &= ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK; + otg |= EXYNOS_5250_REFCLKSEL_CLKCORE << + EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT; + writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); + udelay(100); + otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST | + EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | + EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET | + EXYNOS_5250_USBOTGSYS_OTGDISABLE); + writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); + + + break; + case EXYNOS5250_HOST: + case EXYNOS5250_HSIC0: + case EXYNOS5250_HSIC1: + /* Host registers configuration */ + ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); + /* The clock */ + ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK; + ctrl0 |= drv->ref_reg_val << + EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT; + + /* Reset */ + ctrl0 &= ~(EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST | + EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL | + EXYNOS_5250_HOSTPHYCTRL0_SIDDQ | + EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND | + EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP); + ctrl0 |= EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST | + EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST | + EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N; + writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); + udelay(10); + ctrl0 &= ~(EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST | + EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST); + writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); + + /* OTG configuration */ + otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS); + /* The clock */ + otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK; + otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT; + /* Reset */ + otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND | + EXYNOS_5250_USBOTGSYS_FORCE_SLEEP | + EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG); + otg |= EXYNOS_5250_USBOTGSYS_PHY_SW_RST | + EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET | + EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | + EXYNOS_5250_USBOTGSYS_OTGDISABLE; + /* Ref clock */ + otg &= ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK; + otg |= EXYNOS_5250_REFCLKSEL_CLKCORE << + EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT; + writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); + udelay(10); + otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST | + EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG | + EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET); + + /* HSIC phy configuration */ + hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 | + EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT | + EXYNOS_5250_HSICPHYCTRLX_PHYSWRST); + writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1); + writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2); + udelay(10); + hsic &= ~EXYNOS_5250_HSICPHYCTRLX_PHYSWRST; + writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1); + writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2); + /* The following delay is necessary for the reset sequence to be + * completed */ + udelay(80); + + /* Enable EHCI DMA burst */ + ehci = readl(drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL); + ehci |= EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN | + EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 | + EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 | + EXYNOS_5250_HOSTEHCICTRL_ENAINCR16; + writel(ehci, drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL); + + /* OHCI settings */ + ohci = readl(drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL); + /* Following code is based on the old driver */ + ohci |= 0x1 << 3; + writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL); + + break; + } + exynos5250_isol(inst, 0); + + return 0; +} + +static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + u32 ctrl0; + u32 otg; + u32 hsic; + + exynos5250_isol(inst, 1); + + switch (inst->cfg->id) { + case EXYNOS5250_DEVICE: + otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS); + otg |= (EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND | + EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG | + EXYNOS_5250_USBOTGSYS_FORCE_SLEEP); + writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS); + break; + case EXYNOS5250_HOST: + ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); + ctrl0 |= (EXYNOS_5250_HOSTPHYCTRL0_SIDDQ | + EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND | + EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP | + EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST | + EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL); + writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0); + break; + case EXYNOS5250_HSIC0: + case EXYNOS5250_HSIC1: + hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 | + EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT | + EXYNOS_5250_HSICPHYCTRLX_SIDDQ | + EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP | + EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND + ); + writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1); + writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2); + break; + } + + return 0; +} + + +static const struct samsung_usb2_common_phy exynos5250_phys[] = { + { + .label = "device", + .id = EXYNOS5250_DEVICE, + .power_on = exynos5250_power_on, + .power_off = exynos5250_power_off, + }, + { + .label = "host", + .id = EXYNOS5250_HOST, + .power_on = exynos5250_power_on, + .power_off = exynos5250_power_off, + }, + { + .label = "hsic0", + .id = EXYNOS5250_HSIC0, + .power_on = exynos5250_power_on, + .power_off = exynos5250_power_off, + }, + { + .label = "hsic1", + .id = EXYNOS5250_HSIC1, + .power_on = exynos5250_power_on, + .power_off = exynos5250_power_off, + }, +}; + +const struct samsung_usb2_phy_config exynos5250_usb2_phy_config = { + .has_mode_switch = 1, + .num_phys = EXYNOS5250_NUM_PHYS, + .phys = exynos5250_phys, + .rate_to_clk = exynos5250_rate_to_clk, +}; diff --git a/drivers/phy/samsung/phy-s5pv210-usb2.c b/drivers/phy/samsung/phy-s5pv210-usb2.c new file mode 100644 index 000000000000..f6f72339bbc3 --- /dev/null +++ b/drivers/phy/samsung/phy-s5pv210-usb2.c @@ -0,0 +1,187 @@ +/* + * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Authors: Kamil Debski + * + * 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 "phy-samsung-usb2.h" + +/* Exynos USB PHY registers */ + +/* PHY power control */ +#define S5PV210_UPHYPWR 0x0 + +#define S5PV210_UPHYPWR_PHY0_SUSPEND BIT(0) +#define S5PV210_UPHYPWR_PHY0_PWR BIT(3) +#define S5PV210_UPHYPWR_PHY0_OTG_PWR BIT(4) +#define S5PV210_UPHYPWR_PHY0 ( \ + S5PV210_UPHYPWR_PHY0_SUSPEND | \ + S5PV210_UPHYPWR_PHY0_PWR | \ + S5PV210_UPHYPWR_PHY0_OTG_PWR) + +#define S5PV210_UPHYPWR_PHY1_SUSPEND BIT(6) +#define S5PV210_UPHYPWR_PHY1_PWR BIT(7) +#define S5PV210_UPHYPWR_PHY1 ( \ + S5PV210_UPHYPWR_PHY1_SUSPEND | \ + S5PV210_UPHYPWR_PHY1_PWR) + +/* PHY clock control */ +#define S5PV210_UPHYCLK 0x4 + +#define S5PV210_UPHYCLK_PHYFSEL_MASK (0x3 << 0) +#define S5PV210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0) +#define S5PV210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0) +#define S5PV210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0) + +#define S5PV210_UPHYCLK_PHY0_ID_PULLUP BIT(2) +#define S5PV210_UPHYCLK_PHY0_COMMON_ON BIT(4) +#define S5PV210_UPHYCLK_PHY1_COMMON_ON BIT(7) + +/* PHY reset control */ +#define S5PV210_UPHYRST 0x8 + +#define S5PV210_URSTCON_PHY0 BIT(0) +#define S5PV210_URSTCON_OTG_HLINK BIT(1) +#define S5PV210_URSTCON_OTG_PHYLINK BIT(2) +#define S5PV210_URSTCON_PHY1_ALL BIT(3) +#define S5PV210_URSTCON_HOST_LINK_ALL BIT(4) + +/* Isolation, configured in the power management unit */ +#define S5PV210_USB_ISOL_OFFSET 0x680c +#define S5PV210_USB_ISOL_DEVICE BIT(0) +#define S5PV210_USB_ISOL_HOST BIT(1) + + +enum s5pv210_phy_id { + S5PV210_DEVICE, + S5PV210_HOST, + S5PV210_NUM_PHYS, +}; + +/* + * s5pv210_rate_to_clk() converts the supplied clock rate to the value that + * can be written to the phy register. + */ +static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg) +{ + switch (rate) { + case 12 * MHZ: + *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ; + break; + case 24 * MHZ: + *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ; + break; + case 48 * MHZ: + *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + u32 mask; + + switch (inst->cfg->id) { + case S5PV210_DEVICE: + mask = S5PV210_USB_ISOL_DEVICE; + break; + case S5PV210_HOST: + mask = S5PV210_USB_ISOL_HOST; + break; + default: + return; + } + + regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET, + mask, on ? 0 : mask); +} + +static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on) +{ + struct samsung_usb2_phy_driver *drv = inst->drv; + u32 rstbits = 0; + u32 phypwr = 0; + u32 rst; + u32 pwr; + + switch (inst->cfg->id) { + case S5PV210_DEVICE: + phypwr = S5PV210_UPHYPWR_PHY0; + rstbits = S5PV210_URSTCON_PHY0; + break; + case S5PV210_HOST: + phypwr = S5PV210_UPHYPWR_PHY1; + rstbits = S5PV210_URSTCON_PHY1_ALL | + S5PV210_URSTCON_HOST_LINK_ALL; + break; + } + + if (on) { + writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK); + + pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); + pwr &= ~phypwr; + writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); + + rst = readl(drv->reg_phy + S5PV210_UPHYRST); + rst |= rstbits; + writel(rst, drv->reg_phy + S5PV210_UPHYRST); + udelay(10); + rst &= ~rstbits; + writel(rst, drv->reg_phy + S5PV210_UPHYRST); + } else { + pwr = readl(drv->reg_phy + S5PV210_UPHYPWR); + pwr |= phypwr; + writel(pwr, drv->reg_phy + S5PV210_UPHYPWR); + } +} + +static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst) +{ + s5pv210_isol(inst, 0); + s5pv210_phy_pwr(inst, 1); + + return 0; +} + +static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst) +{ + s5pv210_phy_pwr(inst, 0); + s5pv210_isol(inst, 1); + + return 0; +} + +static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = { + [S5PV210_DEVICE] = { + .label = "device", + .id = S5PV210_DEVICE, + .power_on = s5pv210_power_on, + .power_off = s5pv210_power_off, + }, + [S5PV210_HOST] = { + .label = "host", + .id = S5PV210_HOST, + .power_on = s5pv210_power_on, + .power_off = s5pv210_power_off, + }, +}; + +const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = { + .num_phys = ARRAY_SIZE(s5pv210_phys), + .phys = s5pv210_phys, + .rate_to_clk = s5pv210_rate_to_clk, +}; diff --git a/drivers/phy/samsung/phy-samsung-usb2.c b/drivers/phy/samsung/phy-samsung-usb2.c new file mode 100644 index 000000000000..1d22d93b552d --- /dev/null +++ b/drivers/phy/samsung/phy-samsung-usb2.c @@ -0,0 +1,267 @@ +/* + * Samsung SoC USB 1.1/2.0 PHY driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Author: Kamil Debski + * + * 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 "phy-samsung-usb2.h" + +static int samsung_usb2_phy_power_on(struct phy *phy) +{ + struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy); + struct samsung_usb2_phy_driver *drv = inst->drv; + int ret; + + dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n", + inst->cfg->label); + + if (drv->vbus) { + ret = regulator_enable(drv->vbus); + if (ret) + goto err_regulator; + } + + ret = clk_prepare_enable(drv->clk); + if (ret) + goto err_main_clk; + ret = clk_prepare_enable(drv->ref_clk); + if (ret) + goto err_instance_clk; + if (inst->cfg->power_on) { + spin_lock(&drv->lock); + ret = inst->cfg->power_on(inst); + spin_unlock(&drv->lock); + if (ret) + goto err_power_on; + } + + return 0; + +err_power_on: + clk_disable_unprepare(drv->ref_clk); +err_instance_clk: + clk_disable_unprepare(drv->clk); +err_main_clk: + if (drv->vbus) + regulator_disable(drv->vbus); +err_regulator: + return ret; +} + +static int samsung_usb2_phy_power_off(struct phy *phy) +{ + struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy); + struct samsung_usb2_phy_driver *drv = inst->drv; + int ret = 0; + + dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n", + inst->cfg->label); + if (inst->cfg->power_off) { + spin_lock(&drv->lock); + ret = inst->cfg->power_off(inst); + spin_unlock(&drv->lock); + if (ret) + return ret; + } + clk_disable_unprepare(drv->ref_clk); + clk_disable_unprepare(drv->clk); + if (drv->vbus) + ret = regulator_disable(drv->vbus); + + return ret; +} + +static const struct phy_ops samsung_usb2_phy_ops = { + .power_on = samsung_usb2_phy_power_on, + .power_off = samsung_usb2_phy_power_off, + .owner = THIS_MODULE, +}; + +static struct phy *samsung_usb2_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct samsung_usb2_phy_driver *drv; + + drv = dev_get_drvdata(dev); + if (!drv) + return ERR_PTR(-EINVAL); + + if (WARN_ON(args->args[0] >= drv->cfg->num_phys)) + return ERR_PTR(-ENODEV); + + return drv->instances[args->args[0]].phy; +} + +static const struct of_device_id samsung_usb2_phy_of_match[] = { +#ifdef CONFIG_PHY_EXYNOS4X12_USB2 + { + .compatible = "samsung,exynos3250-usb2-phy", + .data = &exynos3250_usb2_phy_config, + }, +#endif +#ifdef CONFIG_PHY_EXYNOS4210_USB2 + { + .compatible = "samsung,exynos4210-usb2-phy", + .data = &exynos4210_usb2_phy_config, + }, +#endif +#ifdef CONFIG_PHY_EXYNOS4X12_USB2 + { + .compatible = "samsung,exynos4x12-usb2-phy", + .data = &exynos4x12_usb2_phy_config, + }, +#endif +#ifdef CONFIG_PHY_EXYNOS5250_USB2 + { + .compatible = "samsung,exynos5250-usb2-phy", + .data = &exynos5250_usb2_phy_config, + }, +#endif +#ifdef CONFIG_PHY_S5PV210_USB2 + { + .compatible = "samsung,s5pv210-usb2-phy", + .data = &s5pv210_usb2_phy_config, + }, +#endif + { }, +}; +MODULE_DEVICE_TABLE(of, samsung_usb2_phy_of_match); + +static int samsung_usb2_phy_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + const struct samsung_usb2_phy_config *cfg; + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + struct resource *mem; + struct samsung_usb2_phy_driver *drv; + int i, ret; + + if (!pdev->dev.of_node) { + dev_err(dev, "This driver is required to be instantiated from device tree\n"); + return -EINVAL; + } + + match = of_match_node(samsung_usb2_phy_of_match, pdev->dev.of_node); + if (!match) { + dev_err(dev, "of_match_node() failed\n"); + return -EINVAL; + } + cfg = match->data; + + drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) + + cfg->num_phys * sizeof(struct samsung_usb2_phy_instance), + GFP_KERNEL); + if (!drv) + return -ENOMEM; + + dev_set_drvdata(dev, drv); + spin_lock_init(&drv->lock); + + drv->cfg = cfg; + drv->dev = dev; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + drv->reg_phy = devm_ioremap_resource(dev, mem); + if (IS_ERR(drv->reg_phy)) { + dev_err(dev, "Failed to map register memory (phy)\n"); + return PTR_ERR(drv->reg_phy); + } + + drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "samsung,pmureg-phandle"); + if (IS_ERR(drv->reg_pmu)) { + dev_err(dev, "Failed to map PMU registers (via syscon)\n"); + return PTR_ERR(drv->reg_pmu); + } + + if (drv->cfg->has_mode_switch) { + drv->reg_sys = syscon_regmap_lookup_by_phandle( + pdev->dev.of_node, "samsung,sysreg-phandle"); + if (IS_ERR(drv->reg_sys)) { + dev_err(dev, "Failed to map system registers (via syscon)\n"); + return PTR_ERR(drv->reg_sys); + } + } + + drv->clk = devm_clk_get(dev, "phy"); + if (IS_ERR(drv->clk)) { + dev_err(dev, "Failed to get clock of phy controller\n"); + return PTR_ERR(drv->clk); + } + + drv->ref_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(drv->ref_clk)) { + dev_err(dev, "Failed to get reference clock for the phy controller\n"); + return PTR_ERR(drv->ref_clk); + } + + drv->ref_rate = clk_get_rate(drv->ref_clk); + if (drv->cfg->rate_to_clk) { + ret = drv->cfg->rate_to_clk(drv->ref_rate, &drv->ref_reg_val); + if (ret) + return ret; + } + + drv->vbus = devm_regulator_get(dev, "vbus"); + if (IS_ERR(drv->vbus)) { + ret = PTR_ERR(drv->vbus); + if (ret == -EPROBE_DEFER) + return ret; + drv->vbus = NULL; + } + + for (i = 0; i < drv->cfg->num_phys; i++) { + char *label = drv->cfg->phys[i].label; + struct samsung_usb2_phy_instance *p = &drv->instances[i]; + + dev_dbg(dev, "Creating phy \"%s\"\n", label); + p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops); + if (IS_ERR(p->phy)) { + dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n", + label); + return PTR_ERR(p->phy); + } + + p->cfg = &drv->cfg->phys[i]; + p->drv = drv; + phy_set_bus_width(p->phy, 8); + phy_set_drvdata(p->phy, p); + } + + phy_provider = devm_of_phy_provider_register(dev, + samsung_usb2_phy_xlate); + if (IS_ERR(phy_provider)) { + dev_err(drv->dev, "Failed to register phy provider\n"); + return PTR_ERR(phy_provider); + } + + return 0; +} + +static struct platform_driver samsung_usb2_phy_driver = { + .probe = samsung_usb2_phy_probe, + .driver = { + .of_match_table = samsung_usb2_phy_of_match, + .name = "samsung-usb2-phy", + } +}; + +module_platform_driver(samsung_usb2_phy_driver); +MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver"); +MODULE_AUTHOR("Kamil Debski "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:samsung-usb2-phy"); diff --git a/drivers/phy/samsung/phy-samsung-usb2.h b/drivers/phy/samsung/phy-samsung-usb2.h new file mode 100644 index 000000000000..6563e7ca0ac4 --- /dev/null +++ b/drivers/phy/samsung/phy-samsung-usb2.h @@ -0,0 +1,73 @@ +/* + * Samsung SoC USB 1.1/2.0 PHY driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Author: Kamil Debski + * + * 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. + */ + +#ifndef _PHY_EXYNOS_USB2_H +#define _PHY_EXYNOS_USB2_H + +#include +#include +#include +#include +#include +#include + +#define KHZ 1000 +#define MHZ (KHZ * KHZ) + +struct samsung_usb2_phy_driver; +struct samsung_usb2_phy_instance; +struct samsung_usb2_phy_config; + +struct samsung_usb2_phy_instance { + const struct samsung_usb2_common_phy *cfg; + struct phy *phy; + struct samsung_usb2_phy_driver *drv; + int int_cnt; + int ext_cnt; +}; + +struct samsung_usb2_phy_driver { + const struct samsung_usb2_phy_config *cfg; + struct clk *clk; + struct clk *ref_clk; + struct regulator *vbus; + unsigned long ref_rate; + u32 ref_reg_val; + struct device *dev; + void __iomem *reg_phy; + struct regmap *reg_pmu; + struct regmap *reg_sys; + spinlock_t lock; + struct samsung_usb2_phy_instance instances[0]; +}; + +struct samsung_usb2_common_phy { + int (*power_on)(struct samsung_usb2_phy_instance *); + int (*power_off)(struct samsung_usb2_phy_instance *); + unsigned int id; + char *label; +}; + + +struct samsung_usb2_phy_config { + const struct samsung_usb2_common_phy *phys; + int (*rate_to_clk)(unsigned long, u32 *); + unsigned int num_phys; + bool has_mode_switch; + bool has_refclk_sel; +}; + +extern const struct samsung_usb2_phy_config exynos3250_usb2_phy_config; +extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config; +extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config; +extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config; +extern const struct samsung_usb2_phy_config s5pv210_usb2_phy_config; +#endif diff --git a/drivers/phy/st/Kconfig b/drivers/phy/st/Kconfig new file mode 100644 index 000000000000..0814d3f87ec6 --- /dev/null +++ b/drivers/phy/st/Kconfig @@ -0,0 +1,33 @@ +# +# Phy drivers for STMicro platforms +# +config PHY_MIPHY28LP + tristate "STMicroelectronics MIPHY28LP PHY driver for STiH407" + depends on ARCH_STI + select GENERIC_PHY + help + Enable this to support the miphy transceiver (for SATA/PCIE/USB3) + that is part of STMicroelectronics STiH407 SoC. + +config PHY_ST_SPEAR1310_MIPHY + tristate "ST SPEAR1310-MIPHY driver" + select GENERIC_PHY + depends on MACH_SPEAR1310 || COMPILE_TEST + help + Support for ST SPEAr1310 MIPHY which can be used for PCIe and SATA. + +config PHY_ST_SPEAR1340_MIPHY + tristate "ST SPEAR1340-MIPHY driver" + select GENERIC_PHY + depends on MACH_SPEAR1340 || COMPILE_TEST + help + Support for ST SPEAr1340 MIPHY which can be used for PCIe and SATA. + +config PHY_STIH407_USB + tristate "STMicroelectronics USB2 picoPHY driver for STiH407 family" + depends on RESET_CONTROLLER + depends on ARCH_STI || COMPILE_TEST + select GENERIC_PHY + help + Enable this support to enable the picoPHY device used by USB2 + and USB3 controllers on STMicroelectronics STiH407 SoC families. diff --git a/drivers/phy/st/Makefile b/drivers/phy/st/Makefile new file mode 100644 index 000000000000..e2adfe2166d2 --- /dev/null +++ b/drivers/phy/st/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o +obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o +obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o +obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o diff --git a/drivers/phy/st/phy-miphy28lp.c b/drivers/phy/st/phy-miphy28lp.c new file mode 100644 index 000000000000..213e2e15339c --- /dev/null +++ b/drivers/phy/st/phy-miphy28lp.c @@ -0,0 +1,1286 @@ +/* + * Copyright (C) 2014 STMicroelectronics + * + * STMicroelectronics PHY driver MiPHY28lp (for SoC STiH407). + * + * Author: Alexandre Torgue + * + * 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 +#include +#include +#include + +#include + +/* MiPHY registers */ +#define MIPHY_CONF_RESET 0x00 +#define RST_APPLI_SW BIT(0) +#define RST_CONF_SW BIT(1) +#define RST_MACRO_SW BIT(2) + +#define MIPHY_RESET 0x01 +#define RST_PLL_SW BIT(0) +#define RST_COMP_SW BIT(2) + +#define MIPHY_STATUS_1 0x02 +#define PHY_RDY BIT(0) +#define HFC_RDY BIT(1) +#define HFC_PLL BIT(2) + +#define MIPHY_CONTROL 0x04 +#define TERM_EN_SW BIT(2) +#define DIS_LINK_RST BIT(3) +#define AUTO_RST_RX BIT(4) +#define PX_RX_POL BIT(5) + +#define MIPHY_BOUNDARY_SEL 0x0a +#define TX_SEL BIT(6) +#define SSC_SEL BIT(4) +#define GENSEL_SEL BIT(0) + +#define MIPHY_BOUNDARY_1 0x0b +#define MIPHY_BOUNDARY_2 0x0c +#define SSC_EN_SW BIT(2) + +#define MIPHY_PLL_CLKREF_FREQ 0x0d +#define MIPHY_SPEED 0x0e +#define TX_SPDSEL_80DEC 0 +#define TX_SPDSEL_40DEC 1 +#define TX_SPDSEL_20DEC 2 +#define RX_SPDSEL_80DEC 0 +#define RX_SPDSEL_40DEC (1 << 2) +#define RX_SPDSEL_20DEC (2 << 2) + +#define MIPHY_CONF 0x0f +#define MIPHY_CTRL_TEST_SEL 0x20 +#define MIPHY_CTRL_TEST_1 0x21 +#define MIPHY_CTRL_TEST_2 0x22 +#define MIPHY_CTRL_TEST_3 0x23 +#define MIPHY_CTRL_TEST_4 0x24 +#define MIPHY_FEEDBACK_TEST 0x25 +#define MIPHY_DEBUG_BUS 0x26 +#define MIPHY_DEBUG_STATUS_MSB 0x27 +#define MIPHY_DEBUG_STATUS_LSB 0x28 +#define MIPHY_PWR_RAIL_1 0x29 +#define MIPHY_PWR_RAIL_2 0x2a +#define MIPHY_SYNCHAR_CONTROL 0x30 + +#define MIPHY_COMP_FSM_1 0x3a +#define COMP_START BIT(6) + +#define MIPHY_COMP_FSM_6 0x3f +#define COMP_DONE BIT(7) + +#define MIPHY_COMP_POSTP 0x42 +#define MIPHY_TX_CTRL_1 0x49 +#define TX_REG_STEP_0V 0 +#define TX_REG_STEP_P_25MV 1 +#define TX_REG_STEP_P_50MV 2 +#define TX_REG_STEP_N_25MV 7 +#define TX_REG_STEP_N_50MV 6 +#define TX_REG_STEP_N_75MV 5 + +#define MIPHY_TX_CTRL_2 0x4a +#define TX_SLEW_SW_40_PS 0 +#define TX_SLEW_SW_80_PS 1 +#define TX_SLEW_SW_120_PS 2 + +#define MIPHY_TX_CTRL_3 0x4b +#define MIPHY_TX_CAL_MAN 0x4e +#define TX_SLEW_CAL_MAN_EN BIT(0) + +#define MIPHY_TST_BIAS_BOOST_2 0x62 +#define MIPHY_BIAS_BOOST_1 0x63 +#define MIPHY_BIAS_BOOST_2 0x64 +#define MIPHY_RX_DESBUFF_FDB_2 0x67 +#define MIPHY_RX_DESBUFF_FDB_3 0x68 +#define MIPHY_SIGDET_COMPENS1 0x69 +#define MIPHY_SIGDET_COMPENS2 0x6a +#define MIPHY_JITTER_PERIOD 0x6b +#define MIPHY_JITTER_AMPLITUDE_1 0x6c +#define MIPHY_JITTER_AMPLITUDE_2 0x6d +#define MIPHY_JITTER_AMPLITUDE_3 0x6e +#define MIPHY_RX_K_GAIN 0x78 +#define MIPHY_RX_BUFFER_CTRL 0x7a +#define VGA_GAIN BIT(0) +#define EQ_DC_GAIN BIT(2) +#define EQ_BOOST_GAIN BIT(3) + +#define MIPHY_RX_VGA_GAIN 0x7b +#define MIPHY_RX_EQU_GAIN_1 0x7f +#define MIPHY_RX_EQU_GAIN_2 0x80 +#define MIPHY_RX_EQU_GAIN_3 0x81 +#define MIPHY_RX_CAL_CTRL_1 0x97 +#define MIPHY_RX_CAL_CTRL_2 0x98 + +#define MIPHY_RX_CAL_OFFSET_CTRL 0x99 +#define CAL_OFFSET_VGA_64 (0x03 << 0) +#define CAL_OFFSET_THRESHOLD_64 (0x03 << 2) +#define VGA_OFFSET_POLARITY BIT(4) +#define OFFSET_COMPENSATION_EN BIT(6) + +#define MIPHY_RX_CAL_VGA_STEP 0x9a +#define MIPHY_RX_CAL_EYE_MIN 0x9d +#define MIPHY_RX_CAL_OPT_LENGTH 0x9f +#define MIPHY_RX_LOCK_CTRL_1 0xc1 +#define MIPHY_RX_LOCK_SETTINGS_OPT 0xc2 +#define MIPHY_RX_LOCK_STEP 0xc4 + +#define MIPHY_RX_SIGDET_SLEEP_OA 0xc9 +#define MIPHY_RX_SIGDET_SLEEP_SEL 0xca +#define MIPHY_RX_SIGDET_WAIT_SEL 0xcb +#define MIPHY_RX_SIGDET_DATA_SEL 0xcc +#define EN_ULTRA_LOW_POWER BIT(0) +#define EN_FIRST_HALF BIT(1) +#define EN_SECOND_HALF BIT(2) +#define EN_DIGIT_SIGNAL_CHECK BIT(3) + +#define MIPHY_RX_POWER_CTRL_1 0xcd +#define MIPHY_RX_POWER_CTRL_2 0xce +#define MIPHY_PLL_CALSET_CTRL 0xd3 +#define MIPHY_PLL_CALSET_1 0xd4 +#define MIPHY_PLL_CALSET_2 0xd5 +#define MIPHY_PLL_CALSET_3 0xd6 +#define MIPHY_PLL_CALSET_4 0xd7 +#define MIPHY_PLL_SBR_1 0xe3 +#define SET_NEW_CHANGE BIT(1) + +#define MIPHY_PLL_SBR_2 0xe4 +#define MIPHY_PLL_SBR_3 0xe5 +#define MIPHY_PLL_SBR_4 0xe6 +#define MIPHY_PLL_COMMON_MISC_2 0xe9 +#define START_ACT_FILT BIT(6) + +#define MIPHY_PLL_SPAREIN 0xeb + +/* + * On STiH407 the glue logic can be different among MiPHY devices; for example: + * MiPHY0: OSC_FORCE_EXT means: + * 0: 30MHz crystal clk - 1: 100MHz ext clk routed through MiPHY1 + * MiPHY1: OSC_FORCE_EXT means: + * 1: 30MHz crystal clk - 0: 100MHz ext clk routed through MiPHY1 + * Some devices have not the possibility to check if the osc is ready. + */ +#define MIPHY_OSC_FORCE_EXT BIT(3) +#define MIPHY_OSC_RDY BIT(5) + +#define MIPHY_CTRL_MASK 0x0f +#define MIPHY_CTRL_DEFAULT 0 +#define MIPHY_CTRL_SYNC_D_EN BIT(2) + +/* SATA / PCIe defines */ +#define SATA_CTRL_MASK 0x07 +#define PCIE_CTRL_MASK 0xff +#define SATA_CTRL_SELECT_SATA 1 +#define SATA_CTRL_SELECT_PCIE 0 +#define SYSCFG_PCIE_PCIE_VAL 0x80 +#define SATA_SPDMODE 1 + +#define MIPHY_SATA_BANK_NB 3 +#define MIPHY_PCIE_BANK_NB 2 + +enum { + SYSCFG_CTRL, + SYSCFG_STATUS, + SYSCFG_PCI, + SYSCFG_SATA, + SYSCFG_REG_MAX, +}; + +struct miphy28lp_phy { + struct phy *phy; + struct miphy28lp_dev *phydev; + void __iomem *base; + void __iomem *pipebase; + + bool osc_force_ext; + bool osc_rdy; + bool px_rx_pol_inv; + bool ssc; + bool tx_impedance; + + struct reset_control *miphy_rst; + + u32 sata_gen; + + /* Sysconfig registers offsets needed to configure the device */ + u32 syscfg_reg[SYSCFG_REG_MAX]; + u8 type; +}; + +struct miphy28lp_dev { + struct device *dev; + struct regmap *regmap; + struct mutex miphy_mutex; + struct miphy28lp_phy **phys; + int nphys; +}; + +struct miphy_initval { + u16 reg; + u16 val; +}; + +enum miphy_sata_gen { SATA_GEN1, SATA_GEN2, SATA_GEN3 }; + +static char *PHY_TYPE_name[] = { "sata-up", "pcie-up", "", "usb3-up" }; + +struct pll_ratio { + int clk_ref; + int calset_1; + int calset_2; + int calset_3; + int calset_4; + int cal_ctrl; +}; + +static struct pll_ratio sata_pll_ratio = { + .clk_ref = 0x1e, + .calset_1 = 0xc8, + .calset_2 = 0x00, + .calset_3 = 0x00, + .calset_4 = 0x00, + .cal_ctrl = 0x00, +}; + +static struct pll_ratio pcie_pll_ratio = { + .clk_ref = 0x1e, + .calset_1 = 0xa6, + .calset_2 = 0xaa, + .calset_3 = 0xaa, + .calset_4 = 0x00, + .cal_ctrl = 0x00, +}; + +static struct pll_ratio usb3_pll_ratio = { + .clk_ref = 0x1e, + .calset_1 = 0xa6, + .calset_2 = 0xaa, + .calset_3 = 0xaa, + .calset_4 = 0x04, + .cal_ctrl = 0x00, +}; + +struct miphy28lp_pll_gen { + int bank; + int speed; + int bias_boost_1; + int bias_boost_2; + int tx_ctrl_1; + int tx_ctrl_2; + int tx_ctrl_3; + int rx_k_gain; + int rx_vga_gain; + int rx_equ_gain_1; + int rx_equ_gain_2; + int rx_equ_gain_3; + int rx_buff_ctrl; +}; + +static struct miphy28lp_pll_gen sata_pll_gen[] = { + { + .bank = 0x00, + .speed = TX_SPDSEL_80DEC | RX_SPDSEL_80DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xae, + .tx_ctrl_2 = 0x53, + .tx_ctrl_3 = 0x00, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x7d, + .rx_equ_gain_2 = 0x56, + .rx_equ_gain_3 = 0x00, + }, + { + .bank = 0x01, + .speed = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xae, + .tx_ctrl_2 = 0x72, + .tx_ctrl_3 = 0x20, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x7d, + .rx_equ_gain_2 = 0x56, + .rx_equ_gain_3 = 0x00, + }, + { + .bank = 0x02, + .speed = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xae, + .tx_ctrl_2 = 0xc0, + .tx_ctrl_3 = 0x20, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x7d, + .rx_equ_gain_2 = 0x56, + .rx_equ_gain_3 = 0x00, + }, +}; + +static struct miphy28lp_pll_gen pcie_pll_gen[] = { + { + .bank = 0x00, + .speed = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xa5, + .tx_ctrl_1 = TX_REG_STEP_N_25MV, + .tx_ctrl_2 = 0x71, + .tx_ctrl_3 = 0x60, + .rx_k_gain = 0x98, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x79, + .rx_equ_gain_2 = 0x56, + }, + { + .bank = 0x01, + .speed = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC, + .bias_boost_1 = 0x00, + .bias_boost_2 = 0xa5, + .tx_ctrl_1 = TX_REG_STEP_N_25MV, + .tx_ctrl_2 = 0x70, + .tx_ctrl_3 = 0x60, + .rx_k_gain = 0xcc, + .rx_buff_ctrl = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN, + .rx_vga_gain = 0x00, + .rx_equ_gain_1 = 0x78, + .rx_equ_gain_2 = 0x07, + }, +}; + +static inline void miphy28lp_set_reset(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + u8 val; + + /* Putting Macro in reset */ + writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); + + val = RST_APPLI_SW | RST_CONF_SW; + writeb_relaxed(val, base + MIPHY_CONF_RESET); + + writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); + + /* Bringing the MIPHY-CPU registers out of reset */ + if (miphy_phy->type == PHY_TYPE_PCIE) { + val = AUTO_RST_RX | TERM_EN_SW; + writeb_relaxed(val, base + MIPHY_CONTROL); + } else { + val = AUTO_RST_RX | TERM_EN_SW | DIS_LINK_RST; + writeb_relaxed(val, base + MIPHY_CONTROL); + } +} + +static inline void miphy28lp_pll_calibration(struct miphy28lp_phy *miphy_phy, + struct pll_ratio *pll_ratio) +{ + void __iomem *base = miphy_phy->base; + u8 val; + + /* Applying PLL Settings */ + writeb_relaxed(0x1d, base + MIPHY_PLL_SPAREIN); + writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ); + + /* PLL Ratio */ + writeb_relaxed(pll_ratio->calset_1, base + MIPHY_PLL_CALSET_1); + writeb_relaxed(pll_ratio->calset_2, base + MIPHY_PLL_CALSET_2); + writeb_relaxed(pll_ratio->calset_3, base + MIPHY_PLL_CALSET_3); + writeb_relaxed(pll_ratio->calset_4, base + MIPHY_PLL_CALSET_4); + writeb_relaxed(pll_ratio->cal_ctrl, base + MIPHY_PLL_CALSET_CTRL); + + writeb_relaxed(TX_SEL, base + MIPHY_BOUNDARY_SEL); + + val = (0x68 << 1) | TX_SLEW_CAL_MAN_EN; + writeb_relaxed(val, base + MIPHY_TX_CAL_MAN); + + val = VGA_OFFSET_POLARITY | CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64; + + if (miphy_phy->type != PHY_TYPE_SATA) + val |= OFFSET_COMPENSATION_EN; + + writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL); + + if (miphy_phy->type == PHY_TYPE_USB3) { + writeb_relaxed(0x00, base + MIPHY_CONF); + writeb_relaxed(0x70, base + MIPHY_RX_LOCK_STEP); + writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_OA); + writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_SEL); + writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_WAIT_SEL); + + val = EN_DIGIT_SIGNAL_CHECK | EN_FIRST_HALF; + writeb_relaxed(val, base + MIPHY_RX_SIGDET_DATA_SEL); + } + +} + +static inline void miphy28lp_sata_config_gen(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + int i; + + for (i = 0; i < ARRAY_SIZE(sata_pll_gen); i++) { + struct miphy28lp_pll_gen *gen = &sata_pll_gen[i]; + + /* Banked settings */ + writeb_relaxed(gen->bank, base + MIPHY_CONF); + writeb_relaxed(gen->speed, base + MIPHY_SPEED); + writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1); + writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2); + + /* TX buffer Settings */ + writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2); + writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3); + + /* RX Buffer Settings */ + writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL); + writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN); + writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1); + writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2); + writeb_relaxed(gen->rx_equ_gain_3, base + MIPHY_RX_EQU_GAIN_3); + } +} + +static inline void miphy28lp_pcie_config_gen(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + int i; + + for (i = 0; i < ARRAY_SIZE(pcie_pll_gen); i++) { + struct miphy28lp_pll_gen *gen = &pcie_pll_gen[i]; + + /* Banked settings */ + writeb_relaxed(gen->bank, base + MIPHY_CONF); + writeb_relaxed(gen->speed, base + MIPHY_SPEED); + writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1); + writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2); + + /* TX buffer Settings */ + writeb_relaxed(gen->tx_ctrl_1, base + MIPHY_TX_CTRL_1); + writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2); + writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3); + + writeb_relaxed(gen->rx_k_gain, base + MIPHY_RX_K_GAIN); + + /* RX Buffer Settings */ + writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL); + writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN); + writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1); + writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2); + } +} + +static inline int miphy28lp_wait_compensation(struct miphy28lp_phy *miphy_phy) +{ + unsigned long finish = jiffies + 5 * HZ; + u8 val; + + /* Waiting for Compensation to complete */ + do { + val = readb_relaxed(miphy_phy->base + MIPHY_COMP_FSM_6); + + if (time_after_eq(jiffies, finish)) + return -EBUSY; + cpu_relax(); + } while (!(val & COMP_DONE)); + + return 0; +} + + +static inline int miphy28lp_compensation(struct miphy28lp_phy *miphy_phy, + struct pll_ratio *pll_ratio) +{ + void __iomem *base = miphy_phy->base; + + /* Poll for HFC ready after reset release */ + /* Compensation measurement */ + writeb_relaxed(RST_PLL_SW | RST_COMP_SW, base + MIPHY_RESET); + + writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2); + writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ); + writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1); + + if (miphy_phy->type == PHY_TYPE_PCIE) + writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET); + + writeb_relaxed(0x00, base + MIPHY_RESET); + writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2); + writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); + + /* TX compensation offset to re-center TX impedance */ + writeb_relaxed(0x00, base + MIPHY_COMP_POSTP); + + if (miphy_phy->type == PHY_TYPE_PCIE) + return miphy28lp_wait_compensation(miphy_phy); + + return 0; +} + +static inline void miphy28_usb3_miphy_reset(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + u8 val; + + /* MIPHY Reset */ + writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET); + writeb_relaxed(0x00, base + MIPHY_CONF_RESET); + writeb_relaxed(RST_COMP_SW, base + MIPHY_RESET); + + val = RST_COMP_SW | RST_PLL_SW; + writeb_relaxed(val, base + MIPHY_RESET); + + writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2); + writeb_relaxed(0x1e, base + MIPHY_PLL_CLKREF_FREQ); + writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1); + writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET); + writeb_relaxed(0x00, base + MIPHY_RESET); + writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2); + writeb_relaxed(0x00, base + MIPHY_CONF); + writeb_relaxed(0x00, base + MIPHY_BOUNDARY_1); + writeb_relaxed(0x00, base + MIPHY_TST_BIAS_BOOST_2); + writeb_relaxed(0x00, base + MIPHY_CONF); + writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); + writeb_relaxed(0xa5, base + MIPHY_DEBUG_BUS); + writeb_relaxed(0x00, base + MIPHY_CONF); +} + +static void miphy_sata_tune_ssc(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + u8 val; + + /* Compensate Tx impedance to avoid out of range values */ + /* + * Enable the SSC on PLL for all banks + * SSC Modulation @ 31 KHz and 4000 ppm modulation amp + */ + val = readb_relaxed(base + MIPHY_BOUNDARY_2); + val |= SSC_EN_SW; + writeb_relaxed(val, base + MIPHY_BOUNDARY_2); + + val = readb_relaxed(base + MIPHY_BOUNDARY_SEL); + val |= SSC_SEL; + writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); + + for (val = 0; val < MIPHY_SATA_BANK_NB; val++) { + writeb_relaxed(val, base + MIPHY_CONF); + + /* Add value to each reference clock cycle */ + /* and define the period length of the SSC */ + writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); + writeb_relaxed(0x6c, base + MIPHY_PLL_SBR_3); + writeb_relaxed(0x81, base + MIPHY_PLL_SBR_4); + + /* Clear any previous request */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + + /* requests the PLL to take in account new parameters */ + writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); + + /* To be sure there is no other pending requests */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + } +} + +static void miphy_pcie_tune_ssc(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + u8 val; + + /* Compensate Tx impedance to avoid out of range values */ + /* + * Enable the SSC on PLL for all banks + * SSC Modulation @ 31 KHz and 4000 ppm modulation amp + */ + val = readb_relaxed(base + MIPHY_BOUNDARY_2); + val |= SSC_EN_SW; + writeb_relaxed(val, base + MIPHY_BOUNDARY_2); + + val = readb_relaxed(base + MIPHY_BOUNDARY_SEL); + val |= SSC_SEL; + writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); + + for (val = 0; val < MIPHY_PCIE_BANK_NB; val++) { + writeb_relaxed(val, base + MIPHY_CONF); + + /* Validate Step component */ + writeb_relaxed(0x69, base + MIPHY_PLL_SBR_3); + writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4); + + /* Validate Period component */ + writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); + writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4); + + /* Clear any previous request */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + + /* requests the PLL to take in account new parameters */ + writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1); + + /* To be sure there is no other pending requests */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + } +} + +static inline void miphy_tune_tx_impedance(struct miphy28lp_phy *miphy_phy) +{ + /* Compensate Tx impedance to avoid out of range values */ + writeb_relaxed(0x02, miphy_phy->base + MIPHY_COMP_POSTP); +} + +static inline int miphy28lp_configure_sata(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + int err; + u8 val; + + /* Putting Macro in reset */ + miphy28lp_set_reset(miphy_phy); + + /* PLL calibration */ + miphy28lp_pll_calibration(miphy_phy, &sata_pll_ratio); + + /* Banked settings Gen1/Gen2/Gen3 */ + miphy28lp_sata_config_gen(miphy_phy); + + /* Power control */ + /* Input bridge enable, manual input bridge control */ + writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); + + /* Macro out of reset */ + writeb_relaxed(0x00, base + MIPHY_CONF_RESET); + + /* Poll for HFC ready after reset release */ + /* Compensation measurement */ + err = miphy28lp_compensation(miphy_phy, &sata_pll_ratio); + if (err) + return err; + + if (miphy_phy->px_rx_pol_inv) { + /* Invert Rx polarity */ + val = readb_relaxed(miphy_phy->base + MIPHY_CONTROL); + val |= PX_RX_POL; + writeb_relaxed(val, miphy_phy->base + MIPHY_CONTROL); + } + + if (miphy_phy->ssc) + miphy_sata_tune_ssc(miphy_phy); + + if (miphy_phy->tx_impedance) + miphy_tune_tx_impedance(miphy_phy); + + return 0; +} + +static inline int miphy28lp_configure_pcie(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + int err; + + /* Putting Macro in reset */ + miphy28lp_set_reset(miphy_phy); + + /* PLL calibration */ + miphy28lp_pll_calibration(miphy_phy, &pcie_pll_ratio); + + /* Banked settings Gen1/Gen2 */ + miphy28lp_pcie_config_gen(miphy_phy); + + /* Power control */ + /* Input bridge enable, manual input bridge control */ + writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); + + /* Macro out of reset */ + writeb_relaxed(0x00, base + MIPHY_CONF_RESET); + + /* Poll for HFC ready after reset release */ + /* Compensation measurement */ + err = miphy28lp_compensation(miphy_phy, &pcie_pll_ratio); + if (err) + return err; + + if (miphy_phy->ssc) + miphy_pcie_tune_ssc(miphy_phy); + + if (miphy_phy->tx_impedance) + miphy_tune_tx_impedance(miphy_phy); + + return 0; +} + + +static inline void miphy28lp_configure_usb3(struct miphy28lp_phy *miphy_phy) +{ + void __iomem *base = miphy_phy->base; + u8 val; + + /* Putting Macro in reset */ + miphy28lp_set_reset(miphy_phy); + + /* PLL calibration */ + miphy28lp_pll_calibration(miphy_phy, &usb3_pll_ratio); + + /* Writing The Speed Rate */ + writeb_relaxed(0x00, base + MIPHY_CONF); + + val = RX_SPDSEL_20DEC | TX_SPDSEL_20DEC; + writeb_relaxed(val, base + MIPHY_SPEED); + + /* RX Channel compensation and calibration */ + writeb_relaxed(0x1c, base + MIPHY_RX_LOCK_SETTINGS_OPT); + writeb_relaxed(0x51, base + MIPHY_RX_CAL_CTRL_1); + writeb_relaxed(0x70, base + MIPHY_RX_CAL_CTRL_2); + + val = OFFSET_COMPENSATION_EN | VGA_OFFSET_POLARITY | + CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64; + writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL); + writeb_relaxed(0x22, base + MIPHY_RX_CAL_VGA_STEP); + writeb_relaxed(0x0e, base + MIPHY_RX_CAL_OPT_LENGTH); + + val = EQ_DC_GAIN | VGA_GAIN; + writeb_relaxed(val, base + MIPHY_RX_BUFFER_CTRL); + writeb_relaxed(0x78, base + MIPHY_RX_EQU_GAIN_1); + writeb_relaxed(0x1b, base + MIPHY_SYNCHAR_CONTROL); + + /* TX compensation offset to re-center TX impedance */ + writeb_relaxed(0x02, base + MIPHY_COMP_POSTP); + + /* Enable GENSEL_SEL and SSC */ + /* TX_SEL=0 swing preemp forced by pipe registres */ + val = SSC_SEL | GENSEL_SEL; + writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL); + + /* MIPHY Bias boost */ + writeb_relaxed(0x00, base + MIPHY_BIAS_BOOST_1); + writeb_relaxed(0xa7, base + MIPHY_BIAS_BOOST_2); + + /* SSC modulation */ + writeb_relaxed(SSC_EN_SW, base + MIPHY_BOUNDARY_2); + + /* MIPHY TX control */ + writeb_relaxed(0x00, base + MIPHY_CONF); + + /* Validate Step component */ + writeb_relaxed(0x5a, base + MIPHY_PLL_SBR_3); + writeb_relaxed(0xa0, base + MIPHY_PLL_SBR_4); + + /* Validate Period component */ + writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2); + writeb_relaxed(0xa1, base + MIPHY_PLL_SBR_4); + + /* Clear any previous request */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + + /* requests the PLL to take in account new parameters */ + writeb_relaxed(0x02, base + MIPHY_PLL_SBR_1); + + /* To be sure there is no other pending requests */ + writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1); + + /* Rx PI controller settings */ + writeb_relaxed(0xca, base + MIPHY_RX_K_GAIN); + + /* MIPHY RX input bridge control */ + /* INPUT_BRIDGE_EN_SW=1, manual input bridge control[0]=1 */ + writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1); + writeb_relaxed(0x29, base + MIPHY_RX_POWER_CTRL_1); + writeb_relaxed(0x1a, base + MIPHY_RX_POWER_CTRL_2); + + /* MIPHY Reset for usb3 */ + miphy28_usb3_miphy_reset(miphy_phy); +} + +static inline int miphy_is_ready(struct miphy28lp_phy *miphy_phy) +{ + unsigned long finish = jiffies + 5 * HZ; + u8 mask = HFC_PLL | HFC_RDY; + u8 val; + + /* + * For PCIe and USB3 check only that PLL and HFC are ready + * For SATA check also that phy is ready! + */ + if (miphy_phy->type == PHY_TYPE_SATA) + mask |= PHY_RDY; + + do { + val = readb_relaxed(miphy_phy->base + MIPHY_STATUS_1); + if ((val & mask) != mask) + cpu_relax(); + else + return 0; + } while (!time_after_eq(jiffies, finish)); + + return -EBUSY; +} + +static int miphy_osc_is_ready(struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + unsigned long finish = jiffies + 5 * HZ; + u32 val; + + if (!miphy_phy->osc_rdy) + return 0; + + if (!miphy_phy->syscfg_reg[SYSCFG_STATUS]) + return -EINVAL; + + do { + regmap_read(miphy_dev->regmap, + miphy_phy->syscfg_reg[SYSCFG_STATUS], &val); + + if ((val & MIPHY_OSC_RDY) != MIPHY_OSC_RDY) + cpu_relax(); + else + return 0; + } while (!time_after_eq(jiffies, finish)); + + return -EBUSY; +} + +static int miphy28lp_get_resource_byname(struct device_node *child, + char *rname, struct resource *res) +{ + int index; + + index = of_property_match_string(child, "reg-names", rname); + if (index < 0) + return -ENODEV; + + return of_address_to_resource(child, index, res); +} + +static int miphy28lp_get_one_addr(struct device *dev, + struct device_node *child, char *rname, + void __iomem **base) +{ + struct resource res; + int ret; + + ret = miphy28lp_get_resource_byname(child, rname, &res); + if (!ret) { + *base = devm_ioremap(dev, res.start, resource_size(&res)); + if (!*base) { + dev_err(dev, "failed to ioremap %s address region\n" + , rname); + return -ENOENT; + } + } + + return 0; +} + +/* MiPHY reset and sysconf setup */ +static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val) +{ + int err; + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + + if (!miphy_phy->syscfg_reg[SYSCFG_CTRL]) + return -EINVAL; + + err = reset_control_assert(miphy_phy->miphy_rst); + if (err) { + dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); + return err; + } + + if (miphy_phy->osc_force_ext) + miphy_val |= MIPHY_OSC_FORCE_EXT; + + regmap_update_bits(miphy_dev->regmap, + miphy_phy->syscfg_reg[SYSCFG_CTRL], + MIPHY_CTRL_MASK, miphy_val); + + err = reset_control_deassert(miphy_phy->miphy_rst); + if (err) { + dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); + return err; + } + + return miphy_osc_is_ready(miphy_phy); +} + +static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + int err, sata_conf = SATA_CTRL_SELECT_SATA; + + if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) || + (!miphy_phy->syscfg_reg[SYSCFG_PCI]) || + (!miphy_phy->base)) + return -EINVAL; + + dev_info(miphy_dev->dev, "sata-up mode, addr 0x%p\n", miphy_phy->base); + + /* Configure the glue-logic */ + sata_conf |= ((miphy_phy->sata_gen - SATA_GEN1) << SATA_SPDMODE); + + regmap_update_bits(miphy_dev->regmap, + miphy_phy->syscfg_reg[SYSCFG_SATA], + SATA_CTRL_MASK, sata_conf); + + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI], + PCIE_CTRL_MASK, SATA_CTRL_SELECT_PCIE); + + /* MiPHY path and clocking init */ + err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT); + + if (err) { + dev_err(miphy_dev->dev, "SATA phy setup failed\n"); + return err; + } + + /* initialize miphy */ + miphy28lp_configure_sata(miphy_phy); + + return miphy_is_ready(miphy_phy); +} + +static int miphy28lp_init_pcie(struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + int err; + + if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) || + (!miphy_phy->syscfg_reg[SYSCFG_PCI]) + || (!miphy_phy->base) || (!miphy_phy->pipebase)) + return -EINVAL; + + dev_info(miphy_dev->dev, "pcie-up mode, addr 0x%p\n", miphy_phy->base); + + /* Configure the glue-logic */ + regmap_update_bits(miphy_dev->regmap, + miphy_phy->syscfg_reg[SYSCFG_SATA], + SATA_CTRL_MASK, SATA_CTRL_SELECT_PCIE); + + regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI], + PCIE_CTRL_MASK, SYSCFG_PCIE_PCIE_VAL); + + /* MiPHY path and clocking init */ + err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT); + + if (err) { + dev_err(miphy_dev->dev, "PCIe phy setup failed\n"); + return err; + } + + /* initialize miphy */ + err = miphy28lp_configure_pcie(miphy_phy); + if (err) + return err; + + /* PIPE Wrapper Configuration */ + writeb_relaxed(0x68, miphy_phy->pipebase + 0x104); /* Rise_0 */ + writeb_relaxed(0x61, miphy_phy->pipebase + 0x105); /* Rise_1 */ + writeb_relaxed(0x68, miphy_phy->pipebase + 0x108); /* Fall_0 */ + writeb_relaxed(0x61, miphy_phy->pipebase + 0x109); /* Fall-1 */ + writeb_relaxed(0x68, miphy_phy->pipebase + 0x10c); /* Threshold_0 */ + writeb_relaxed(0x60, miphy_phy->pipebase + 0x10d); /* Threshold_1 */ + + /* Wait for phy_ready */ + return miphy_is_ready(miphy_phy); +} + +static int miphy28lp_init_usb3(struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + int err; + + if ((!miphy_phy->base) || (!miphy_phy->pipebase)) + return -EINVAL; + + dev_info(miphy_dev->dev, "usb3-up mode, addr 0x%p\n", miphy_phy->base); + + /* MiPHY path and clocking init */ + err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_SYNC_D_EN); + if (err) { + dev_err(miphy_dev->dev, "USB3 phy setup failed\n"); + return err; + } + + /* initialize miphy */ + miphy28lp_configure_usb3(miphy_phy); + + /* PIPE Wrapper Configuration */ + writeb_relaxed(0x68, miphy_phy->pipebase + 0x23); + writeb_relaxed(0x61, miphy_phy->pipebase + 0x24); + writeb_relaxed(0x68, miphy_phy->pipebase + 0x26); + writeb_relaxed(0x61, miphy_phy->pipebase + 0x27); + writeb_relaxed(0x18, miphy_phy->pipebase + 0x29); + writeb_relaxed(0x61, miphy_phy->pipebase + 0x2a); + + /* pipe Wrapper usb3 TX swing de-emph margin PREEMPH[7:4], SWING[3:0] */ + writeb_relaxed(0X67, miphy_phy->pipebase + 0x68); + writeb_relaxed(0x0d, miphy_phy->pipebase + 0x69); + writeb_relaxed(0X67, miphy_phy->pipebase + 0x6a); + writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6b); + writeb_relaxed(0X67, miphy_phy->pipebase + 0x6c); + writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6d); + writeb_relaxed(0X67, miphy_phy->pipebase + 0x6e); + writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6f); + + return miphy_is_ready(miphy_phy); +} + +static int miphy28lp_init(struct phy *phy) +{ + struct miphy28lp_phy *miphy_phy = phy_get_drvdata(phy); + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + int ret; + + mutex_lock(&miphy_dev->miphy_mutex); + + switch (miphy_phy->type) { + + case PHY_TYPE_SATA: + ret = miphy28lp_init_sata(miphy_phy); + break; + case PHY_TYPE_PCIE: + ret = miphy28lp_init_pcie(miphy_phy); + break; + case PHY_TYPE_USB3: + ret = miphy28lp_init_usb3(miphy_phy); + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&miphy_dev->miphy_mutex); + + return ret; +} + +static int miphy28lp_get_addr(struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + struct device_node *phynode = miphy_phy->phy->dev.of_node; + int err; + + if ((miphy_phy->type != PHY_TYPE_SATA) && + (miphy_phy->type != PHY_TYPE_PCIE) && + (miphy_phy->type != PHY_TYPE_USB3)) { + return -EINVAL; + } + + err = miphy28lp_get_one_addr(miphy_dev->dev, phynode, + PHY_TYPE_name[miphy_phy->type - PHY_TYPE_SATA], + &miphy_phy->base); + if (err) + return err; + + if ((miphy_phy->type == PHY_TYPE_PCIE) || + (miphy_phy->type == PHY_TYPE_USB3)) { + err = miphy28lp_get_one_addr(miphy_dev->dev, phynode, "pipew", + &miphy_phy->pipebase); + if (err) + return err; + } + + return 0; +} + +static struct phy *miphy28lp_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct miphy28lp_dev *miphy_dev = dev_get_drvdata(dev); + struct miphy28lp_phy *miphy_phy = NULL; + struct device_node *phynode = args->np; + int ret, index = 0; + + if (args->args_count != 1) { + dev_err(dev, "Invalid number of cells in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + for (index = 0; index < miphy_dev->nphys; index++) + if (phynode == miphy_dev->phys[index]->phy->dev.of_node) { + miphy_phy = miphy_dev->phys[index]; + break; + } + + if (!miphy_phy) { + dev_err(dev, "Failed to find appropriate phy\n"); + return ERR_PTR(-EINVAL); + } + + miphy_phy->type = args->args[0]; + + ret = miphy28lp_get_addr(miphy_phy); + if (ret < 0) + return ERR_PTR(ret); + + return miphy_phy->phy; +} + +static const struct phy_ops miphy28lp_ops = { + .init = miphy28lp_init, + .owner = THIS_MODULE, +}; + +static int miphy28lp_probe_resets(struct device_node *node, + struct miphy28lp_phy *miphy_phy) +{ + struct miphy28lp_dev *miphy_dev = miphy_phy->phydev; + int err; + + miphy_phy->miphy_rst = + of_reset_control_get_shared(node, "miphy-sw-rst"); + + if (IS_ERR(miphy_phy->miphy_rst)) { + dev_err(miphy_dev->dev, + "miphy soft reset control not defined\n"); + return PTR_ERR(miphy_phy->miphy_rst); + } + + err = reset_control_deassert(miphy_phy->miphy_rst); + if (err) { + dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n"); + return err; + } + + return 0; +} + +static int miphy28lp_of_probe(struct device_node *np, + struct miphy28lp_phy *miphy_phy) +{ + int i; + u32 ctrlreg; + + miphy_phy->osc_force_ext = + of_property_read_bool(np, "st,osc-force-ext"); + + miphy_phy->osc_rdy = of_property_read_bool(np, "st,osc-rdy"); + + miphy_phy->px_rx_pol_inv = + of_property_read_bool(np, "st,px_rx_pol_inv"); + + miphy_phy->ssc = of_property_read_bool(np, "st,ssc-on"); + + miphy_phy->tx_impedance = + of_property_read_bool(np, "st,tx-impedance-comp"); + + of_property_read_u32(np, "st,sata-gen", &miphy_phy->sata_gen); + if (!miphy_phy->sata_gen) + miphy_phy->sata_gen = SATA_GEN1; + + for (i = 0; i < SYSCFG_REG_MAX; i++) { + if (!of_property_read_u32_index(np, "st,syscfg", i, &ctrlreg)) + miphy_phy->syscfg_reg[i] = ctrlreg; + } + + return 0; +} + +static int miphy28lp_probe(struct platform_device *pdev) +{ + struct device_node *child, *np = pdev->dev.of_node; + struct miphy28lp_dev *miphy_dev; + struct phy_provider *provider; + struct phy *phy; + int ret, port = 0; + + miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL); + if (!miphy_dev) + return -ENOMEM; + + miphy_dev->nphys = of_get_child_count(np); + miphy_dev->phys = devm_kcalloc(&pdev->dev, miphy_dev->nphys, + sizeof(*miphy_dev->phys), GFP_KERNEL); + if (!miphy_dev->phys) + return -ENOMEM; + + miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(miphy_dev->regmap)) { + dev_err(miphy_dev->dev, "No syscfg phandle specified\n"); + return PTR_ERR(miphy_dev->regmap); + } + + miphy_dev->dev = &pdev->dev; + + dev_set_drvdata(&pdev->dev, miphy_dev); + + mutex_init(&miphy_dev->miphy_mutex); + + for_each_child_of_node(np, child) { + struct miphy28lp_phy *miphy_phy; + + miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy), + GFP_KERNEL); + if (!miphy_phy) { + ret = -ENOMEM; + goto put_child; + } + + miphy_dev->phys[port] = miphy_phy; + + phy = devm_phy_create(&pdev->dev, child, &miphy28lp_ops); + if (IS_ERR(phy)) { + dev_err(&pdev->dev, "failed to create PHY\n"); + ret = PTR_ERR(phy); + goto put_child; + } + + miphy_dev->phys[port]->phy = phy; + miphy_dev->phys[port]->phydev = miphy_dev; + + ret = miphy28lp_of_probe(child, miphy_phy); + if (ret) + goto put_child; + + ret = miphy28lp_probe_resets(child, miphy_dev->phys[port]); + if (ret) + goto put_child; + + phy_set_drvdata(phy, miphy_dev->phys[port]); + port++; + + } + + provider = devm_of_phy_provider_register(&pdev->dev, miphy28lp_xlate); + return PTR_ERR_OR_ZERO(provider); +put_child: + of_node_put(child); + return ret; +} + +static const struct of_device_id miphy28lp_of_match[] = { + {.compatible = "st,miphy28lp-phy", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, miphy28lp_of_match); + +static struct platform_driver miphy28lp_driver = { + .probe = miphy28lp_probe, + .driver = { + .name = "miphy28lp-phy", + .of_match_table = miphy28lp_of_match, + } +}; + +module_platform_driver(miphy28lp_driver); + +MODULE_AUTHOR("Alexandre Torgue "); +MODULE_DESCRIPTION("STMicroelectronics miphy28lp driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/st/phy-spear1310-miphy.c b/drivers/phy/st/phy-spear1310-miphy.c new file mode 100644 index 000000000000..ed67e98e54ca --- /dev/null +++ b/drivers/phy/st/phy-spear1310-miphy.c @@ -0,0 +1,261 @@ +/* + * ST SPEAr1310-miphy driver + * + * Copyright (C) 2014 ST Microelectronics + * Pratyush Anand + * Mohit Kumar + * + * 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 + +/* SPEAr1310 Registers */ +#define SPEAR1310_PCIE_SATA_CFG 0x3A4 + #define SPEAR1310_PCIE_SATA2_SEL_PCIE (0 << 31) + #define SPEAR1310_PCIE_SATA1_SEL_PCIE (0 << 30) + #define SPEAR1310_PCIE_SATA0_SEL_PCIE (0 << 29) + #define SPEAR1310_PCIE_SATA2_SEL_SATA BIT(31) + #define SPEAR1310_PCIE_SATA1_SEL_SATA BIT(30) + #define SPEAR1310_PCIE_SATA0_SEL_SATA BIT(29) + #define SPEAR1310_SATA2_CFG_TX_CLK_EN BIT(27) + #define SPEAR1310_SATA2_CFG_RX_CLK_EN BIT(26) + #define SPEAR1310_SATA2_CFG_POWERUP_RESET BIT(25) + #define SPEAR1310_SATA2_CFG_PM_CLK_EN BIT(24) + #define SPEAR1310_SATA1_CFG_TX_CLK_EN BIT(23) + #define SPEAR1310_SATA1_CFG_RX_CLK_EN BIT(22) + #define SPEAR1310_SATA1_CFG_POWERUP_RESET BIT(21) + #define SPEAR1310_SATA1_CFG_PM_CLK_EN BIT(20) + #define SPEAR1310_SATA0_CFG_TX_CLK_EN BIT(19) + #define SPEAR1310_SATA0_CFG_RX_CLK_EN BIT(18) + #define SPEAR1310_SATA0_CFG_POWERUP_RESET BIT(17) + #define SPEAR1310_SATA0_CFG_PM_CLK_EN BIT(16) + #define SPEAR1310_PCIE2_CFG_DEVICE_PRESENT BIT(11) + #define SPEAR1310_PCIE2_CFG_POWERUP_RESET BIT(10) + #define SPEAR1310_PCIE2_CFG_CORE_CLK_EN BIT(9) + #define SPEAR1310_PCIE2_CFG_AUX_CLK_EN BIT(8) + #define SPEAR1310_PCIE1_CFG_DEVICE_PRESENT BIT(7) + #define SPEAR1310_PCIE1_CFG_POWERUP_RESET BIT(6) + #define SPEAR1310_PCIE1_CFG_CORE_CLK_EN BIT(5) + #define SPEAR1310_PCIE1_CFG_AUX_CLK_EN BIT(4) + #define SPEAR1310_PCIE0_CFG_DEVICE_PRESENT BIT(3) + #define SPEAR1310_PCIE0_CFG_POWERUP_RESET BIT(2) + #define SPEAR1310_PCIE0_CFG_CORE_CLK_EN BIT(1) + #define SPEAR1310_PCIE0_CFG_AUX_CLK_EN BIT(0) + + #define SPEAR1310_PCIE_CFG_MASK(x) ((0xF << (x * 4)) | BIT((x + 29))) + #define SPEAR1310_SATA_CFG_MASK(x) ((0xF << (x * 4 + 16)) | \ + BIT((x + 29))) + #define SPEAR1310_PCIE_CFG_VAL(x) \ + (SPEAR1310_PCIE_SATA##x##_SEL_PCIE | \ + SPEAR1310_PCIE##x##_CFG_AUX_CLK_EN | \ + SPEAR1310_PCIE##x##_CFG_CORE_CLK_EN | \ + SPEAR1310_PCIE##x##_CFG_POWERUP_RESET | \ + SPEAR1310_PCIE##x##_CFG_DEVICE_PRESENT) + #define SPEAR1310_SATA_CFG_VAL(x) \ + (SPEAR1310_PCIE_SATA##x##_SEL_SATA | \ + SPEAR1310_SATA##x##_CFG_PM_CLK_EN | \ + SPEAR1310_SATA##x##_CFG_POWERUP_RESET | \ + SPEAR1310_SATA##x##_CFG_RX_CLK_EN | \ + SPEAR1310_SATA##x##_CFG_TX_CLK_EN) + +#define SPEAR1310_PCIE_MIPHY_CFG_1 0x3A8 + #define SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT BIT(31) + #define SPEAR1310_MIPHY_DUAL_CLK_REF_DIV2 BIT(28) + #define SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(x) (x << 16) + #define SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT BIT(15) + #define SPEAR1310_MIPHY_SINGLE_CLK_REF_DIV2 BIT(12) + #define SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(x) (x << 0) + #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA_MASK (0xFFFF) + #define SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK (0xFFFF << 16) + #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA \ + (SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT | \ + SPEAR1310_MIPHY_DUAL_CLK_REF_DIV2 | \ + SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(60) | \ + SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT | \ + SPEAR1310_MIPHY_SINGLE_CLK_REF_DIV2 | \ + SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(60)) + #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \ + (SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(120)) + #define SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE \ + (SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT | \ + SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(25) | \ + SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT | \ + SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(25)) + +#define SPEAR1310_PCIE_MIPHY_CFG_2 0x3AC + +enum spear1310_miphy_mode { + SATA, + PCIE, +}; + +struct spear1310_miphy_priv { + /* instance id of this phy */ + u32 id; + /* phy mode: 0 for SATA 1 for PCIe */ + enum spear1310_miphy_mode mode; + /* regmap for any soc specific misc registers */ + struct regmap *misc; + /* phy struct pointer */ + struct phy *phy; +}; + +static int spear1310_miphy_pcie_init(struct spear1310_miphy_priv *priv) +{ + u32 val; + + regmap_update_bits(priv->misc, SPEAR1310_PCIE_MIPHY_CFG_1, + SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK, + SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE); + + switch (priv->id) { + case 0: + val = SPEAR1310_PCIE_CFG_VAL(0); + break; + case 1: + val = SPEAR1310_PCIE_CFG_VAL(1); + break; + case 2: + val = SPEAR1310_PCIE_CFG_VAL(2); + break; + default: + return -EINVAL; + } + + regmap_update_bits(priv->misc, SPEAR1310_PCIE_SATA_CFG, + SPEAR1310_PCIE_CFG_MASK(priv->id), val); + + return 0; +} + +static int spear1310_miphy_pcie_exit(struct spear1310_miphy_priv *priv) +{ + regmap_update_bits(priv->misc, SPEAR1310_PCIE_SATA_CFG, + SPEAR1310_PCIE_CFG_MASK(priv->id), 0); + + regmap_update_bits(priv->misc, SPEAR1310_PCIE_MIPHY_CFG_1, + SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK, 0); + + return 0; +} + +static int spear1310_miphy_init(struct phy *phy) +{ + struct spear1310_miphy_priv *priv = phy_get_drvdata(phy); + int ret = 0; + + if (priv->mode == PCIE) + ret = spear1310_miphy_pcie_init(priv); + + return ret; +} + +static int spear1310_miphy_exit(struct phy *phy) +{ + struct spear1310_miphy_priv *priv = phy_get_drvdata(phy); + int ret = 0; + + if (priv->mode == PCIE) + ret = spear1310_miphy_pcie_exit(priv); + + return ret; +} + +static const struct of_device_id spear1310_miphy_of_match[] = { + { .compatible = "st,spear1310-miphy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, spear1310_miphy_of_match); + +static const struct phy_ops spear1310_miphy_ops = { + .init = spear1310_miphy_init, + .exit = spear1310_miphy_exit, + .owner = THIS_MODULE, +}; + +static struct phy *spear1310_miphy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct spear1310_miphy_priv *priv = dev_get_drvdata(dev); + + if (args->args_count < 1) { + dev_err(dev, "DT did not pass correct no of args\n"); + return ERR_PTR(-ENODEV); + } + + priv->mode = args->args[0]; + + if (priv->mode != SATA && priv->mode != PCIE) { + dev_err(dev, "DT did not pass correct phy mode\n"); + return ERR_PTR(-ENODEV); + } + + return priv->phy; +} + +static int spear1310_miphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spear1310_miphy_priv *priv; + struct phy_provider *phy_provider; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->misc = + syscon_regmap_lookup_by_phandle(dev->of_node, "misc"); + if (IS_ERR(priv->misc)) { + dev_err(dev, "failed to find misc regmap\n"); + return PTR_ERR(priv->misc); + } + + if (of_property_read_u32(dev->of_node, "phy-id", &priv->id)) { + dev_err(dev, "failed to find phy id\n"); + return -EINVAL; + } + + priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops); + if (IS_ERR(priv->phy)) { + dev_err(dev, "failed to create SATA PCIe PHY\n"); + return PTR_ERR(priv->phy); + } + + dev_set_drvdata(dev, priv); + phy_set_drvdata(priv->phy, priv); + + phy_provider = + devm_of_phy_provider_register(dev, spear1310_miphy_xlate); + if (IS_ERR(phy_provider)) { + dev_err(dev, "failed to register phy provider\n"); + return PTR_ERR(phy_provider); + } + + return 0; +} + +static struct platform_driver spear1310_miphy_driver = { + .probe = spear1310_miphy_probe, + .driver = { + .name = "spear1310-miphy", + .of_match_table = of_match_ptr(spear1310_miphy_of_match), + }, +}; + +module_platform_driver(spear1310_miphy_driver); + +MODULE_DESCRIPTION("ST SPEAR1310-MIPHY driver"); +MODULE_AUTHOR("Pratyush Anand "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/st/phy-spear1340-miphy.c b/drivers/phy/st/phy-spear1340-miphy.c new file mode 100644 index 000000000000..97280c0cf612 --- /dev/null +++ b/drivers/phy/st/phy-spear1340-miphy.c @@ -0,0 +1,294 @@ +/* + * ST spear1340-miphy driver + * + * Copyright (C) 2014 ST Microelectronics + * Pratyush Anand + * Mohit Kumar + * + * 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 + +/* SPEAr1340 Registers */ +/* Power Management Registers */ +#define SPEAR1340_PCM_CFG 0x100 + #define SPEAR1340_PCM_CFG_SATA_POWER_EN BIT(11) +#define SPEAR1340_PCM_WKUP_CFG 0x104 +#define SPEAR1340_SWITCH_CTR 0x108 + +#define SPEAR1340_PERIP1_SW_RST 0x318 + #define SPEAR1340_PERIP1_SW_RSATA BIT(12) +#define SPEAR1340_PERIP2_SW_RST 0x31C +#define SPEAR1340_PERIP3_SW_RST 0x320 + +/* PCIE - SATA configuration registers */ +#define SPEAR1340_PCIE_SATA_CFG 0x424 + /* PCIE CFG MASks */ + #define SPEAR1340_PCIE_CFG_DEVICE_PRESENT BIT(11) + #define SPEAR1340_PCIE_CFG_POWERUP_RESET BIT(10) + #define SPEAR1340_PCIE_CFG_CORE_CLK_EN BIT(9) + #define SPEAR1340_PCIE_CFG_AUX_CLK_EN BIT(8) + #define SPEAR1340_SATA_CFG_TX_CLK_EN BIT(4) + #define SPEAR1340_SATA_CFG_RX_CLK_EN BIT(3) + #define SPEAR1340_SATA_CFG_POWERUP_RESET BIT(2) + #define SPEAR1340_SATA_CFG_PM_CLK_EN BIT(1) + #define SPEAR1340_PCIE_SATA_SEL_PCIE (0) + #define SPEAR1340_PCIE_SATA_SEL_SATA (1) + #define SPEAR1340_PCIE_SATA_CFG_MASK 0xF1F + #define SPEAR1340_PCIE_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_PCIE | \ + SPEAR1340_PCIE_CFG_AUX_CLK_EN | \ + SPEAR1340_PCIE_CFG_CORE_CLK_EN | \ + SPEAR1340_PCIE_CFG_POWERUP_RESET | \ + SPEAR1340_PCIE_CFG_DEVICE_PRESENT) + #define SPEAR1340_SATA_CFG_VAL (SPEAR1340_PCIE_SATA_SEL_SATA | \ + SPEAR1340_SATA_CFG_PM_CLK_EN | \ + SPEAR1340_SATA_CFG_POWERUP_RESET | \ + SPEAR1340_SATA_CFG_RX_CLK_EN | \ + SPEAR1340_SATA_CFG_TX_CLK_EN) + +#define SPEAR1340_PCIE_MIPHY_CFG 0x428 + #define SPEAR1340_MIPHY_OSC_BYPASS_EXT BIT(31) + #define SPEAR1340_MIPHY_CLK_REF_DIV2 BIT(27) + #define SPEAR1340_MIPHY_CLK_REF_DIV4 (2 << 27) + #define SPEAR1340_MIPHY_CLK_REF_DIV8 (3 << 27) + #define SPEAR1340_MIPHY_PLL_RATIO_TOP(x) (x << 0) + #define SPEAR1340_PCIE_MIPHY_CFG_MASK 0xF80000FF + #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \ + (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ + SPEAR1340_MIPHY_CLK_REF_DIV2 | \ + SPEAR1340_MIPHY_PLL_RATIO_TOP(60)) + #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \ + (SPEAR1340_MIPHY_PLL_RATIO_TOP(120)) + #define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \ + (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \ + SPEAR1340_MIPHY_PLL_RATIO_TOP(25)) + +enum spear1340_miphy_mode { + SATA, + PCIE, +}; + +struct spear1340_miphy_priv { + /* phy mode: 0 for SATA 1 for PCIe */ + enum spear1340_miphy_mode mode; + /* regmap for any soc specific misc registers */ + struct regmap *misc; + /* phy struct pointer */ + struct phy *phy; +}; + +static int spear1340_miphy_sata_init(struct spear1340_miphy_priv *priv) +{ + regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, + SPEAR1340_PCIE_SATA_CFG_MASK, + SPEAR1340_SATA_CFG_VAL); + regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, + SPEAR1340_PCIE_MIPHY_CFG_MASK, + SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK); + /* Switch on sata power domain */ + regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG, + SPEAR1340_PCM_CFG_SATA_POWER_EN, + SPEAR1340_PCM_CFG_SATA_POWER_EN); + /* Wait for SATA power domain on */ + msleep(20); + + /* Disable PCIE SATA Controller reset */ + regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST, + SPEAR1340_PERIP1_SW_RSATA, 0); + /* Wait for SATA reset de-assert completion */ + msleep(20); + + return 0; +} + +static int spear1340_miphy_sata_exit(struct spear1340_miphy_priv *priv) +{ + regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, + SPEAR1340_PCIE_SATA_CFG_MASK, 0); + regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, + SPEAR1340_PCIE_MIPHY_CFG_MASK, 0); + + /* Enable PCIE SATA Controller reset */ + regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST, + SPEAR1340_PERIP1_SW_RSATA, + SPEAR1340_PERIP1_SW_RSATA); + /* Wait for SATA power domain off */ + msleep(20); + /* Switch off sata power domain */ + regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG, + SPEAR1340_PCM_CFG_SATA_POWER_EN, 0); + /* Wait for SATA reset assert completion */ + msleep(20); + + return 0; +} + +static int spear1340_miphy_pcie_init(struct spear1340_miphy_priv *priv) +{ + regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, + SPEAR1340_PCIE_MIPHY_CFG_MASK, + SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE); + regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, + SPEAR1340_PCIE_SATA_CFG_MASK, + SPEAR1340_PCIE_CFG_VAL); + + return 0; +} + +static int spear1340_miphy_pcie_exit(struct spear1340_miphy_priv *priv) +{ + regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG, + SPEAR1340_PCIE_MIPHY_CFG_MASK, 0); + regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG, + SPEAR1340_PCIE_SATA_CFG_MASK, 0); + + return 0; +} + +static int spear1340_miphy_init(struct phy *phy) +{ + struct spear1340_miphy_priv *priv = phy_get_drvdata(phy); + int ret = 0; + + if (priv->mode == SATA) + ret = spear1340_miphy_sata_init(priv); + else if (priv->mode == PCIE) + ret = spear1340_miphy_pcie_init(priv); + + return ret; +} + +static int spear1340_miphy_exit(struct phy *phy) +{ + struct spear1340_miphy_priv *priv = phy_get_drvdata(phy); + int ret = 0; + + if (priv->mode == SATA) + ret = spear1340_miphy_sata_exit(priv); + else if (priv->mode == PCIE) + ret = spear1340_miphy_pcie_exit(priv); + + return ret; +} + +static const struct of_device_id spear1340_miphy_of_match[] = { + { .compatible = "st,spear1340-miphy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, spear1340_miphy_of_match); + +static const struct phy_ops spear1340_miphy_ops = { + .init = spear1340_miphy_init, + .exit = spear1340_miphy_exit, + .owner = THIS_MODULE, +}; + +#ifdef CONFIG_PM_SLEEP +static int spear1340_miphy_suspend(struct device *dev) +{ + struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); + int ret = 0; + + if (priv->mode == SATA) + ret = spear1340_miphy_sata_exit(priv); + + return ret; +} + +static int spear1340_miphy_resume(struct device *dev) +{ + struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); + int ret = 0; + + if (priv->mode == SATA) + ret = spear1340_miphy_sata_init(priv); + + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(spear1340_miphy_pm_ops, spear1340_miphy_suspend, + spear1340_miphy_resume); + +static struct phy *spear1340_miphy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct spear1340_miphy_priv *priv = dev_get_drvdata(dev); + + if (args->args_count < 1) { + dev_err(dev, "DT did not pass correct no of args\n"); + return ERR_PTR(-ENODEV); + } + + priv->mode = args->args[0]; + + if (priv->mode != SATA && priv->mode != PCIE) { + dev_err(dev, "DT did not pass correct phy mode\n"); + return ERR_PTR(-ENODEV); + } + + return priv->phy; +} + +static int spear1340_miphy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spear1340_miphy_priv *priv; + struct phy_provider *phy_provider; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->misc = + syscon_regmap_lookup_by_phandle(dev->of_node, "misc"); + if (IS_ERR(priv->misc)) { + dev_err(dev, "failed to find misc regmap\n"); + return PTR_ERR(priv->misc); + } + + priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops); + if (IS_ERR(priv->phy)) { + dev_err(dev, "failed to create SATA PCIe PHY\n"); + return PTR_ERR(priv->phy); + } + + dev_set_drvdata(dev, priv); + phy_set_drvdata(priv->phy, priv); + + phy_provider = + devm_of_phy_provider_register(dev, spear1340_miphy_xlate); + if (IS_ERR(phy_provider)) { + dev_err(dev, "failed to register phy provider\n"); + return PTR_ERR(phy_provider); + } + + return 0; +} + +static struct platform_driver spear1340_miphy_driver = { + .probe = spear1340_miphy_probe, + .driver = { + .name = "spear1340-miphy", + .pm = &spear1340_miphy_pm_ops, + .of_match_table = of_match_ptr(spear1340_miphy_of_match), + }, +}; + +module_platform_driver(spear1340_miphy_driver); + +MODULE_DESCRIPTION("ST SPEAR1340-MIPHY driver"); +MODULE_AUTHOR("Pratyush Anand "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/st/phy-stih407-usb.c b/drivers/phy/st/phy-stih407-usb.c new file mode 100644 index 000000000000..b1f44ab669fb --- /dev/null +++ b/drivers/phy/st/phy-stih407-usb.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2014 STMicroelectronics + * + * STMicroelectronics Generic PHY driver for STiH407 USB2. + * + * Author: Giuseppe Cavallaro + * + * 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 +#include + +#define PHYPARAM_REG 1 +#define PHYCTRL_REG 2 + +/* Default PHY_SEL and REFCLKSEL configuration */ +#define STIH407_USB_PICOPHY_CTRL_PORT_CONF 0x6 +#define STIH407_USB_PICOPHY_CTRL_PORT_MASK 0x1f + +/* ports parameters overriding */ +#define STIH407_USB_PICOPHY_PARAM_DEF 0x39a4dc +#define STIH407_USB_PICOPHY_PARAM_MASK 0xffffffff + +struct stih407_usb2_picophy { + struct phy *phy; + struct regmap *regmap; + struct device *dev; + struct reset_control *rstc; + struct reset_control *rstport; + int ctrl; + int param; +}; + +static int stih407_usb2_pico_ctrl(struct stih407_usb2_picophy *phy_dev) +{ + reset_control_deassert(phy_dev->rstc); + + return regmap_update_bits(phy_dev->regmap, phy_dev->ctrl, + STIH407_USB_PICOPHY_CTRL_PORT_MASK, + STIH407_USB_PICOPHY_CTRL_PORT_CONF); +} + +static int stih407_usb2_init_port(struct phy *phy) +{ + int ret; + struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy); + + stih407_usb2_pico_ctrl(phy_dev); + + ret = regmap_update_bits(phy_dev->regmap, + phy_dev->param, + STIH407_USB_PICOPHY_PARAM_MASK, + STIH407_USB_PICOPHY_PARAM_DEF); + if (ret) + return ret; + + return reset_control_deassert(phy_dev->rstport); +} + +static int stih407_usb2_exit_port(struct phy *phy) +{ + struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy); + + /* + * Only port reset is asserted, phy global reset is kept untouched + * as other ports may still be active. When all ports are in reset + * state, assumption is made that power will be cut off on the phy, in + * case of suspend for instance. Theoretically, asserting individual + * reset (like here) or global reset should be equivalent. + */ + return reset_control_assert(phy_dev->rstport); +} + +static const struct phy_ops stih407_usb2_picophy_data = { + .init = stih407_usb2_init_port, + .exit = stih407_usb2_exit_port, + .owner = THIS_MODULE, +}; + +static int stih407_usb2_picophy_probe(struct platform_device *pdev) +{ + struct stih407_usb2_picophy *phy_dev; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct phy_provider *phy_provider; + struct phy *phy; + int ret; + + phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); + if (!phy_dev) + return -ENOMEM; + + phy_dev->dev = dev; + dev_set_drvdata(dev, phy_dev); + + phy_dev->rstc = devm_reset_control_get_shared(dev, "global"); + if (IS_ERR(phy_dev->rstc)) { + dev_err(dev, "failed to ctrl picoPHY reset\n"); + return PTR_ERR(phy_dev->rstc); + } + + phy_dev->rstport = devm_reset_control_get_exclusive(dev, "port"); + if (IS_ERR(phy_dev->rstport)) { + dev_err(dev, "failed to ctrl picoPHY reset\n"); + return PTR_ERR(phy_dev->rstport); + } + + /* Reset port by default: only deassert it in phy init */ + reset_control_assert(phy_dev->rstport); + + phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(phy_dev->regmap)) { + dev_err(dev, "No syscfg phandle specified\n"); + return PTR_ERR(phy_dev->regmap); + } + + ret = of_property_read_u32_index(np, "st,syscfg", PHYPARAM_REG, + &phy_dev->param); + if (ret) { + dev_err(dev, "can't get phyparam offset (%d)\n", ret); + return ret; + } + + ret = of_property_read_u32_index(np, "st,syscfg", PHYCTRL_REG, + &phy_dev->ctrl); + if (ret) { + dev_err(dev, "can't get phyctrl offset (%d)\n", ret); + return ret; + } + + phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create Display Port PHY\n"); + return PTR_ERR(phy); + } + + phy_dev->phy = phy; + phy_set_drvdata(phy, phy_dev); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + dev_info(dev, "STiH407 USB Generic picoPHY driver probed!"); + + return 0; +} + +static const struct of_device_id stih407_usb2_picophy_of_match[] = { + { .compatible = "st,stih407-usb2-phy" }, + { /*sentinel */ }, +}; + +MODULE_DEVICE_TABLE(of, stih407_usb2_picophy_of_match); + +static struct platform_driver stih407_usb2_picophy_driver = { + .probe = stih407_usb2_picophy_probe, + .driver = { + .name = "stih407-usb-genphy", + .of_match_table = stih407_usb2_picophy_of_match, + } +}; + +module_platform_driver(stih407_usb2_picophy_driver); + +MODULE_AUTHOR("Giuseppe Cavallaro "); +MODULE_DESCRIPTION("STMicroelectronics Generic picoPHY driver for STiH407"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig new file mode 100644 index 000000000000..20503562666c --- /dev/null +++ b/drivers/phy/ti/Kconfig @@ -0,0 +1,78 @@ +# +# Phy drivers for TI platforms +# +config PHY_DA8XX_USB + tristate "TI DA8xx USB PHY Driver" + depends on ARCH_DAVINCI_DA8XX + select GENERIC_PHY + select MFD_SYSCON + help + Enable this to support the USB PHY on DA8xx SoCs. + + This driver controls both the USB 1.1 PHY and the USB 2.0 PHY. + +config PHY_DM816X_USB + tristate "TI dm816x USB PHY driver" + depends on ARCH_OMAP2PLUS + depends on USB_SUPPORT + select GENERIC_PHY + select USB_PHY + help + Enable this for dm816x USB to work. + +config OMAP_CONTROL_PHY + tristate "OMAP CONTROL PHY Driver" + depends on ARCH_OMAP2PLUS || COMPILE_TEST + help + Enable this to add support for the PHY part present in the control + module. This driver has API to power on the USB2 PHY and to write to + the mailbox. The mailbox is present only in omap4 and the register to + power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an + additional register to power on USB3 PHY/SATA PHY/PCIE PHY + (PIPE3 PHY). + +config OMAP_USB2 + tristate "OMAP USB2 PHY Driver" + depends on ARCH_OMAP2PLUS + depends on USB_SUPPORT + select GENERIC_PHY + select USB_PHY + select OMAP_CONTROL_PHY + depends on OMAP_OCP2SCP + help + Enable this to support the transceiver that is part of SOC. This + driver takes care of all the PHY functionality apart from comparator. + The USB OTG controller communicates with the comparator using this + driver. + +config TI_PIPE3 + tristate "TI PIPE3 PHY Driver" + depends on ARCH_OMAP2PLUS || COMPILE_TEST + select GENERIC_PHY + select OMAP_CONTROL_PHY + depends on OMAP_OCP2SCP + help + Enable this to support the PIPE3 PHY that is part of TI SOCs. This + driver takes care of all the PHY functionality apart from comparator. + This driver interacts with the "OMAP Control PHY Driver" to power + on/off the PHY. + +config PHY_TUSB1210 + tristate "TI TUSB1210 ULPI PHY module" + depends on USB_ULPI_BUS + select GENERIC_PHY + help + Support for TI TUSB1210 USB ULPI PHY. + +config TWL4030_USB + tristate "TWL4030 USB Transceiver Driver" + depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS + depends on USB_SUPPORT + depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't 'y' + select GENERIC_PHY + select USB_PHY + help + Enable this to support the USB OTG transceiver on TWL4030 + family chips (including the TWL5030 and TPS659x0 devices). + This transceiver supports high and full speed devices plus, + in host mode, low speed. diff --git a/drivers/phy/ti/Makefile b/drivers/phy/ti/Makefile new file mode 100644 index 000000000000..0cc3a1a557a3 --- /dev/null +++ b/drivers/phy/ti/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o +obj-$(CONFIG_PHY_DM816X_USB) += phy-dm816x-usb.o +obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o +obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o +obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o +obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o +obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o diff --git a/drivers/phy/ti/phy-da8xx-usb.c b/drivers/phy/ti/phy-da8xx-usb.c new file mode 100644 index 000000000000..1b82bff6330f --- /dev/null +++ b/drivers/phy/ti/phy-da8xx-usb.c @@ -0,0 +1,251 @@ +/* + * phy-da8xx-usb - TI DaVinci DA8xx USB PHY driver + * + * Copyright (C) 2016 David Lechner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PHY_INIT_BITS (CFGCHIP2_SESENDEN | CFGCHIP2_VBDTCTEN) + +struct da8xx_usb_phy { + struct phy_provider *phy_provider; + struct phy *usb11_phy; + struct phy *usb20_phy; + struct clk *usb11_clk; + struct clk *usb20_clk; + struct regmap *regmap; +}; + +static int da8xx_usb11_phy_power_on(struct phy *phy) +{ + struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); + int ret; + + ret = clk_prepare_enable(d_phy->usb11_clk); + if (ret) + return ret; + + regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, + CFGCHIP2_USB1SUSPENDM); + + return 0; +} + +static int da8xx_usb11_phy_power_off(struct phy *phy) +{ + struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); + + regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_USB1SUSPENDM, 0); + + clk_disable_unprepare(d_phy->usb11_clk); + + return 0; +} + +static const struct phy_ops da8xx_usb11_phy_ops = { + .power_on = da8xx_usb11_phy_power_on, + .power_off = da8xx_usb11_phy_power_off, + .owner = THIS_MODULE, +}; + +static int da8xx_usb20_phy_power_on(struct phy *phy) +{ + struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); + int ret; + + ret = clk_prepare_enable(d_phy->usb20_clk); + if (ret) + return ret; + + regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, 0); + + return 0; +} + +static int da8xx_usb20_phy_power_off(struct phy *phy) +{ + struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); + + regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGPWRDN, + CFGCHIP2_OTGPWRDN); + + clk_disable_unprepare(d_phy->usb20_clk); + + return 0; +} + +static int da8xx_usb20_phy_set_mode(struct phy *phy, enum phy_mode mode) +{ + struct da8xx_usb_phy *d_phy = phy_get_drvdata(phy); + u32 val; + + switch (mode) { + case PHY_MODE_USB_HOST: /* Force VBUS valid, ID = 0 */ + val = CFGCHIP2_OTGMODE_FORCE_HOST; + break; + case PHY_MODE_USB_DEVICE: /* Force VBUS valid, ID = 1 */ + val = CFGCHIP2_OTGMODE_FORCE_DEVICE; + break; + case PHY_MODE_USB_OTG: /* Don't override the VBUS/ID comparators */ + val = CFGCHIP2_OTGMODE_NO_OVERRIDE; + break; + default: + return -EINVAL; + } + + regmap_write_bits(d_phy->regmap, CFGCHIP(2), CFGCHIP2_OTGMODE_MASK, + val); + + return 0; +} + +static const struct phy_ops da8xx_usb20_phy_ops = { + .power_on = da8xx_usb20_phy_power_on, + .power_off = da8xx_usb20_phy_power_off, + .set_mode = da8xx_usb20_phy_set_mode, + .owner = THIS_MODULE, +}; + +static struct phy *da8xx_usb_phy_of_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct da8xx_usb_phy *d_phy = dev_get_drvdata(dev); + + if (!d_phy) + return ERR_PTR(-ENODEV); + + switch (args->args[0]) { + case 0: + return d_phy->usb20_phy; + case 1: + return d_phy->usb11_phy; + default: + return ERR_PTR(-EINVAL); + } +} + +static int da8xx_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct da8xx_usb_phy *d_phy; + + d_phy = devm_kzalloc(dev, sizeof(*d_phy), GFP_KERNEL); + if (!d_phy) + return -ENOMEM; + + if (node) + d_phy->regmap = syscon_regmap_lookup_by_compatible( + "ti,da830-cfgchip"); + else + d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon"); + if (IS_ERR(d_phy->regmap)) { + dev_err(dev, "Failed to get syscon\n"); + return PTR_ERR(d_phy->regmap); + } + + d_phy->usb11_clk = devm_clk_get(dev, "usb11_phy"); + if (IS_ERR(d_phy->usb11_clk)) { + dev_err(dev, "Failed to get usb11_phy clock\n"); + return PTR_ERR(d_phy->usb11_clk); + } + + d_phy->usb20_clk = devm_clk_get(dev, "usb20_phy"); + if (IS_ERR(d_phy->usb20_clk)) { + dev_err(dev, "Failed to get usb20_phy clock\n"); + return PTR_ERR(d_phy->usb20_clk); + } + + d_phy->usb11_phy = devm_phy_create(dev, node, &da8xx_usb11_phy_ops); + if (IS_ERR(d_phy->usb11_phy)) { + dev_err(dev, "Failed to create usb11 phy\n"); + return PTR_ERR(d_phy->usb11_phy); + } + + d_phy->usb20_phy = devm_phy_create(dev, node, &da8xx_usb20_phy_ops); + if (IS_ERR(d_phy->usb20_phy)) { + dev_err(dev, "Failed to create usb20 phy\n"); + return PTR_ERR(d_phy->usb20_phy); + } + + platform_set_drvdata(pdev, d_phy); + phy_set_drvdata(d_phy->usb11_phy, d_phy); + phy_set_drvdata(d_phy->usb20_phy, d_phy); + + if (node) { + d_phy->phy_provider = devm_of_phy_provider_register(dev, + da8xx_usb_phy_of_xlate); + if (IS_ERR(d_phy->phy_provider)) { + dev_err(dev, "Failed to create phy provider\n"); + return PTR_ERR(d_phy->phy_provider); + } + } else { + int ret; + + ret = phy_create_lookup(d_phy->usb11_phy, "usb-phy", + "ohci-da8xx"); + if (ret) + dev_warn(dev, "Failed to create usb11 phy lookup\n"); + ret = phy_create_lookup(d_phy->usb20_phy, "usb-phy", + "musb-da8xx"); + if (ret) + dev_warn(dev, "Failed to create usb20 phy lookup\n"); + } + + regmap_write_bits(d_phy->regmap, CFGCHIP(2), + PHY_INIT_BITS, PHY_INIT_BITS); + + return 0; +} + +static int da8xx_usb_phy_remove(struct platform_device *pdev) +{ + struct da8xx_usb_phy *d_phy = platform_get_drvdata(pdev); + + if (!pdev->dev.of_node) { + phy_remove_lookup(d_phy->usb20_phy, "usb-phy", "musb-da8xx"); + phy_remove_lookup(d_phy->usb11_phy, "usb-phy", "ohci-da8xx"); + } + + return 0; +} + +static const struct of_device_id da8xx_usb_phy_ids[] = { + { .compatible = "ti,da830-usb-phy" }, + { } +}; +MODULE_DEVICE_TABLE(of, da8xx_usb_phy_ids); + +static struct platform_driver da8xx_usb_phy_driver = { + .probe = da8xx_usb_phy_probe, + .remove = da8xx_usb_phy_remove, + .driver = { + .name = "da8xx-usb-phy", + .of_match_table = da8xx_usb_phy_ids, + }, +}; + +module_platform_driver(da8xx_usb_phy_driver); + +MODULE_ALIAS("platform:da8xx-usb-phy"); +MODULE_AUTHOR("David Lechner "); +MODULE_DESCRIPTION("TI DA8xx USB PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/ti/phy-dm816x-usb.c b/drivers/phy/ti/phy-dm816x-usb.c new file mode 100644 index 000000000000..cbcce7cf0028 --- /dev/null +++ b/drivers/phy/ti/phy-dm816x-usb.c @@ -0,0 +1,290 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * TRM has two sets of USB_CTRL registers.. The correct register bits + * are in TRM section 24.9.8.2 USB_CTRL Register. The TRM documents the + * phy as being SR70LX Synopsys USB 2.0 OTG nanoPHY. It also seems at + * least dm816x rev c ignores writes to USB_CTRL register, but the TI + * kernel is writing to those so it's possible that later revisions + * have worknig USB_CTRL register. + * + * Also note that At least USB_CTRL register seems to be dm816x specific + * according to the TRM. It's possible that USBPHY_CTRL is more generic, + * but that would have to be checked against the SR70LX documentation + * which does not seem to be publicly available. + * + * Finally, the phy on dm814x and am335x is different from dm816x. + */ +#define DM816X_USB_CTRL_PHYCLKSRC BIT(8) /* 1 = PLL ref clock */ +#define DM816X_USB_CTRL_PHYSLEEP1 BIT(1) /* Enable the first phy */ +#define DM816X_USB_CTRL_PHYSLEEP0 BIT(0) /* Enable the second phy */ + +#define DM816X_USBPHY_CTRL_TXRISETUNE 1 +#define DM816X_USBPHY_CTRL_TXVREFTUNE 0xc +#define DM816X_USBPHY_CTRL_TXPREEMTUNE 0x2 + +struct dm816x_usb_phy { + struct regmap *syscon; + struct device *dev; + unsigned int instance; + struct clk *refclk; + struct usb_phy phy; + unsigned int usb_ctrl; /* Shared between phy0 and phy1 */ + unsigned int usbphy_ctrl; +}; + +static int dm816x_usb_phy_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + otg->host = host; + if (!host) + otg->state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int dm816x_usb_phy_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + otg->gadget = gadget; + if (!gadget) + otg->state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int dm816x_usb_phy_init(struct phy *x) +{ + struct dm816x_usb_phy *phy = phy_get_drvdata(x); + unsigned int val; + int error; + + if (clk_get_rate(phy->refclk) != 24000000) + dev_warn(phy->dev, "nonstandard phy refclk\n"); + + /* Set PLL ref clock and put phys to sleep */ + error = regmap_update_bits(phy->syscon, phy->usb_ctrl, + DM816X_USB_CTRL_PHYCLKSRC | + DM816X_USB_CTRL_PHYSLEEP1 | + DM816X_USB_CTRL_PHYSLEEP0, + 0); + regmap_read(phy->syscon, phy->usb_ctrl, &val); + if ((val & 3) != 0) + dev_info(phy->dev, + "Working dm816x USB_CTRL! (0x%08x)\n", + val); + + /* + * TI kernel sets these values for "symmetrical eye diagram and + * better signal quality" so let's assume somebody checked the + * values with a scope and set them here too. + */ + regmap_read(phy->syscon, phy->usbphy_ctrl, &val); + val |= DM816X_USBPHY_CTRL_TXRISETUNE | + DM816X_USBPHY_CTRL_TXVREFTUNE | + DM816X_USBPHY_CTRL_TXPREEMTUNE; + regmap_write(phy->syscon, phy->usbphy_ctrl, val); + + return 0; +} + +static const struct phy_ops ops = { + .init = dm816x_usb_phy_init, + .owner = THIS_MODULE, +}; + +static int __maybe_unused dm816x_usb_phy_runtime_suspend(struct device *dev) +{ + struct dm816x_usb_phy *phy = dev_get_drvdata(dev); + unsigned int mask, val; + int error = 0; + + mask = BIT(phy->instance); + val = ~BIT(phy->instance); + error = regmap_update_bits(phy->syscon, phy->usb_ctrl, + mask, val); + if (error) + dev_err(phy->dev, "phy%i failed to power off\n", + phy->instance); + clk_disable(phy->refclk); + + return 0; +} + +static int __maybe_unused dm816x_usb_phy_runtime_resume(struct device *dev) +{ + struct dm816x_usb_phy *phy = dev_get_drvdata(dev); + unsigned int mask, val; + int error; + + error = clk_enable(phy->refclk); + if (error) + return error; + + /* + * Note that at least dm816x rev c does not seem to do + * anything with the USB_CTRL register. But let's follow + * what the TI tree is doing in case later revisions use + * USB_CTRL. + */ + mask = BIT(phy->instance); + val = BIT(phy->instance); + error = regmap_update_bits(phy->syscon, phy->usb_ctrl, + mask, val); + if (error) { + dev_err(phy->dev, "phy%i failed to power on\n", + phy->instance); + clk_disable(phy->refclk); + return error; + } + + return 0; +} + +static UNIVERSAL_DEV_PM_OPS(dm816x_usb_phy_pm_ops, + dm816x_usb_phy_runtime_suspend, + dm816x_usb_phy_runtime_resume, + NULL); + +#ifdef CONFIG_OF +static const struct of_device_id dm816x_usb_phy_id_table[] = { + { + .compatible = "ti,dm8168-usb-phy", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, dm816x_usb_phy_id_table); +#endif + +static int dm816x_usb_phy_probe(struct platform_device *pdev) +{ + struct dm816x_usb_phy *phy; + struct resource *res; + struct phy *generic_phy; + struct phy_provider *phy_provider; + struct usb_otg *otg; + const struct of_device_id *of_id; + const struct usb_phy_data *phy_data; + int error; + + of_id = of_match_device(of_match_ptr(dm816x_usb_phy_id_table), + &pdev->dev); + if (!of_id) + return -EINVAL; + + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + phy->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "syscon"); + if (IS_ERR(phy->syscon)) + return PTR_ERR(phy->syscon); + + /* + * According to sprs614e.pdf, the first usb_ctrl is shared and + * the second instance for usb_ctrl is reserved.. Also the + * register bits are different from earlier TRMs. + */ + phy->usb_ctrl = 0x20; + phy->usbphy_ctrl = (res->start & 0xff) + 4; + if (phy->usbphy_ctrl == 0x2c) + phy->instance = 1; + + phy_data = of_id->data; + + otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); + if (!otg) + return -ENOMEM; + + phy->dev = &pdev->dev; + phy->phy.dev = phy->dev; + phy->phy.label = "dm8168_usb_phy"; + phy->phy.otg = otg; + phy->phy.type = USB_PHY_TYPE_USB2; + otg->set_host = dm816x_usb_phy_set_host; + otg->set_peripheral = dm816x_usb_phy_set_peripheral; + otg->usb_phy = &phy->phy; + + platform_set_drvdata(pdev, phy); + + phy->refclk = devm_clk_get(phy->dev, "refclk"); + if (IS_ERR(phy->refclk)) + return PTR_ERR(phy->refclk); + error = clk_prepare(phy->refclk); + if (error) + return error; + + pm_runtime_enable(phy->dev); + generic_phy = devm_phy_create(phy->dev, NULL, &ops); + if (IS_ERR(generic_phy)) + return PTR_ERR(generic_phy); + + phy_set_drvdata(generic_phy, phy); + + phy_provider = devm_of_phy_provider_register(phy->dev, + of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + usb_add_phy_dev(&phy->phy); + + return 0; +} + +static int dm816x_usb_phy_remove(struct platform_device *pdev) +{ + struct dm816x_usb_phy *phy = platform_get_drvdata(pdev); + + usb_remove_phy(&phy->phy); + pm_runtime_disable(phy->dev); + clk_unprepare(phy->refclk); + + return 0; +} + +static struct platform_driver dm816x_usb_phy_driver = { + .probe = dm816x_usb_phy_probe, + .remove = dm816x_usb_phy_remove, + .driver = { + .name = "dm816x-usb-phy", + .pm = &dm816x_usb_phy_pm_ops, + .of_match_table = of_match_ptr(dm816x_usb_phy_id_table), + }, +}; + +module_platform_driver(dm816x_usb_phy_driver); + +MODULE_ALIAS("platform:dm816x_usb"); +MODULE_AUTHOR("Tony Lindgren "); +MODULE_DESCRIPTION("dm816x usb phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/ti/phy-omap-control.c b/drivers/phy/ti/phy-omap-control.c new file mode 100644 index 000000000000..e9c41b3fa0ee --- /dev/null +++ b/drivers/phy/ti/phy-omap-control.c @@ -0,0 +1,360 @@ +/* + * omap-control-phy.c - The PHY part of control module. + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Author: Kishon Vijay Abraham I + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * omap_control_pcie_pcs - set the PCS delay count + * @dev: the control module device + * @delay: 8 bit delay value + */ +void omap_control_pcie_pcs(struct device *dev, u8 delay) +{ + u32 val; + struct omap_control_phy *control_phy; + + if (IS_ERR(dev) || !dev) { + pr_err("%s: invalid device\n", __func__); + return; + } + + control_phy = dev_get_drvdata(dev); + if (!control_phy) { + dev_err(dev, "%s: invalid control phy device\n", __func__); + return; + } + + if (control_phy->type != OMAP_CTRL_TYPE_PCIE) { + dev_err(dev, "%s: unsupported operation\n", __func__); + return; + } + + val = readl(control_phy->pcie_pcs); + val &= ~(OMAP_CTRL_PCIE_PCS_MASK << + OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); + val |= (delay << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); + writel(val, control_phy->pcie_pcs); +} +EXPORT_SYMBOL_GPL(omap_control_pcie_pcs); + +/** + * omap_control_phy_power - power on/off the phy using control module reg + * @dev: the control module device + * @on: 0 or 1, based on powering on or off the PHY + */ +void omap_control_phy_power(struct device *dev, int on) +{ + u32 val; + unsigned long rate; + struct omap_control_phy *control_phy; + + if (IS_ERR(dev) || !dev) { + pr_err("%s: invalid device\n", __func__); + return; + } + + control_phy = dev_get_drvdata(dev); + if (!control_phy) { + dev_err(dev, "%s: invalid control phy device\n", __func__); + return; + } + + if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) + return; + + val = readl(control_phy->power); + + switch (control_phy->type) { + case OMAP_CTRL_TYPE_USB2: + if (on) + val &= ~OMAP_CTRL_DEV_PHY_PD; + else + val |= OMAP_CTRL_DEV_PHY_PD; + break; + + case OMAP_CTRL_TYPE_PCIE: + case OMAP_CTRL_TYPE_PIPE3: + rate = clk_get_rate(control_phy->sys_clk); + rate = rate/1000000; + + if (on) { + val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | + OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK); + val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON << + OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; + val |= rate << + OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; + } else { + val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK; + val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF << + OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; + } + break; + + case OMAP_CTRL_TYPE_DRA7USB2: + if (on) + val &= ~OMAP_CTRL_USB2_PHY_PD; + else + val |= OMAP_CTRL_USB2_PHY_PD; + break; + + case OMAP_CTRL_TYPE_AM437USB2: + if (on) { + val &= ~(AM437X_CTRL_USB2_PHY_PD | + AM437X_CTRL_USB2_OTG_PD); + val |= (AM437X_CTRL_USB2_OTGVDET_EN | + AM437X_CTRL_USB2_OTGSESSEND_EN); + } else { + val &= ~(AM437X_CTRL_USB2_OTGVDET_EN | + AM437X_CTRL_USB2_OTGSESSEND_EN); + val |= (AM437X_CTRL_USB2_PHY_PD | + AM437X_CTRL_USB2_OTG_PD); + } + break; + default: + dev_err(dev, "%s: type %d not recognized\n", + __func__, control_phy->type); + break; + } + + writel(val, control_phy->power); +} +EXPORT_SYMBOL_GPL(omap_control_phy_power); + +/** + * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded + * @ctrl_phy: struct omap_control_phy * + * + * Writes to the mailbox register to notify the usb core that a usb + * device has been connected. + */ +static void omap_control_usb_host_mode(struct omap_control_phy *ctrl_phy) +{ + u32 val; + + val = readl(ctrl_phy->otghs_control); + val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND); + val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID; + writel(val, ctrl_phy->otghs_control); +} + +/** + * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high + * impedance + * @ctrl_phy: struct omap_control_phy * + * + * Writes to the mailbox register to notify the usb core that it has been + * connected to a usb host. + */ +static void omap_control_usb_device_mode(struct omap_control_phy *ctrl_phy) +{ + u32 val; + + val = readl(ctrl_phy->otghs_control); + val &= ~OMAP_CTRL_DEV_SESSEND; + val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID | + OMAP_CTRL_DEV_VBUSVALID; + writel(val, ctrl_phy->otghs_control); +} + +/** + * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high + * impedance + * @ctrl_phy: struct omap_control_phy * + * + * Writes to the mailbox register to notify the usb core it's now in + * disconnected state. + */ +static void omap_control_usb_set_sessionend(struct omap_control_phy *ctrl_phy) +{ + u32 val; + + val = readl(ctrl_phy->otghs_control); + val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID); + val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND; + writel(val, ctrl_phy->otghs_control); +} + +/** + * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode + * or device mode or to denote disconnected state + * @dev: the control module device + * @mode: The mode to which usb should be configured + * + * This is an API to write to the mailbox register to notify the usb core that + * a usb device has been connected. + */ +void omap_control_usb_set_mode(struct device *dev, + enum omap_control_usb_mode mode) +{ + struct omap_control_phy *ctrl_phy; + + if (IS_ERR(dev) || !dev) + return; + + ctrl_phy = dev_get_drvdata(dev); + if (!ctrl_phy) { + dev_err(dev, "Invalid control phy device\n"); + return; + } + + if (ctrl_phy->type != OMAP_CTRL_TYPE_OTGHS) + return; + + switch (mode) { + case USB_MODE_HOST: + omap_control_usb_host_mode(ctrl_phy); + break; + case USB_MODE_DEVICE: + omap_control_usb_device_mode(ctrl_phy); + break; + case USB_MODE_DISCONNECT: + omap_control_usb_set_sessionend(ctrl_phy); + break; + default: + dev_vdbg(dev, "invalid omap control usb mode\n"); + } +} +EXPORT_SYMBOL_GPL(omap_control_usb_set_mode); + +static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS; +static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2; +static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3; +static const enum omap_control_phy_type pcie_data = OMAP_CTRL_TYPE_PCIE; +static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2; +static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2; + +static const struct of_device_id omap_control_phy_id_table[] = { + { + .compatible = "ti,control-phy-otghs", + .data = &otghs_data, + }, + { + .compatible = "ti,control-phy-usb2", + .data = &usb2_data, + }, + { + .compatible = "ti,control-phy-pipe3", + .data = &pipe3_data, + }, + { + .compatible = "ti,control-phy-pcie", + .data = &pcie_data, + }, + { + .compatible = "ti,control-phy-usb2-dra7", + .data = &dra7usb2_data, + }, + { + .compatible = "ti,control-phy-usb2-am437", + .data = &am437usb2_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_control_phy_id_table); + +static int omap_control_phy_probe(struct platform_device *pdev) +{ + struct resource *res; + const struct of_device_id *of_id; + struct omap_control_phy *control_phy; + + of_id = of_match_device(omap_control_phy_id_table, &pdev->dev); + if (!of_id) + return -EINVAL; + + control_phy = devm_kzalloc(&pdev->dev, sizeof(*control_phy), + GFP_KERNEL); + if (!control_phy) + return -ENOMEM; + + control_phy->dev = &pdev->dev; + control_phy->type = *(enum omap_control_phy_type *)of_id->data; + + if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "otghs_control"); + control_phy->otghs_control = devm_ioremap_resource( + &pdev->dev, res); + if (IS_ERR(control_phy->otghs_control)) + return PTR_ERR(control_phy->otghs_control); + } else { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "power"); + control_phy->power = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(control_phy->power)) { + dev_err(&pdev->dev, "Couldn't get power register\n"); + return PTR_ERR(control_phy->power); + } + } + + if (control_phy->type == OMAP_CTRL_TYPE_PIPE3 || + control_phy->type == OMAP_CTRL_TYPE_PCIE) { + control_phy->sys_clk = devm_clk_get(control_phy->dev, + "sys_clkin"); + if (IS_ERR(control_phy->sys_clk)) { + pr_err("%s: unable to get sys_clkin\n", __func__); + return -EINVAL; + } + } + + if (control_phy->type == OMAP_CTRL_TYPE_PCIE) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "pcie_pcs"); + control_phy->pcie_pcs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(control_phy->pcie_pcs)) + return PTR_ERR(control_phy->pcie_pcs); + } + + dev_set_drvdata(control_phy->dev, control_phy); + + return 0; +} + +static struct platform_driver omap_control_phy_driver = { + .probe = omap_control_phy_probe, + .driver = { + .name = "omap-control-phy", + .of_match_table = omap_control_phy_id_table, + }, +}; + +static int __init omap_control_phy_init(void) +{ + return platform_driver_register(&omap_control_phy_driver); +} +subsys_initcall(omap_control_phy_init); + +static void __exit omap_control_phy_exit(void) +{ + platform_driver_unregister(&omap_control_phy_driver); +} +module_exit(omap_control_phy_exit); + +MODULE_ALIAS("platform:omap_control_phy"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("OMAP Control Module PHY Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/ti/phy-omap-usb2.c b/drivers/phy/ti/phy-omap-usb2.c new file mode 100644 index 000000000000..fe909fd8144f --- /dev/null +++ b/drivers/phy/ti/phy-omap-usb2.c @@ -0,0 +1,439 @@ +/* + * omap-usb2.c - USB PHY, talking to musb controller in OMAP. + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Author: Kishon Vijay Abraham I + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB2PHY_DISCON_BYP_LATCH (1 << 31) +#define USB2PHY_ANA_CONFIG1 0x4c + +/** + * omap_usb2_set_comparator - links the comparator present in the sytem with + * this phy + * @comparator - the companion phy(comparator) for this phy + * + * The phy companion driver should call this API passing the phy_companion + * filled with set_vbus and start_srp to be used by usb phy. + * + * For use by phy companion driver + */ +int omap_usb2_set_comparator(struct phy_companion *comparator) +{ + struct omap_usb *phy; + struct usb_phy *x = usb_get_phy(USB_PHY_TYPE_USB2); + + if (IS_ERR(x)) + return -ENODEV; + + phy = phy_to_omapusb(x); + phy->comparator = comparator; + return 0; +} +EXPORT_SYMBOL_GPL(omap_usb2_set_comparator); + +static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled) +{ + struct omap_usb *phy = phy_to_omapusb(otg->usb_phy); + + if (!phy->comparator) + return -ENODEV; + + return phy->comparator->set_vbus(phy->comparator, enabled); +} + +static int omap_usb_start_srp(struct usb_otg *otg) +{ + struct omap_usb *phy = phy_to_omapusb(otg->usb_phy); + + if (!phy->comparator) + return -ENODEV; + + return phy->comparator->start_srp(phy->comparator); +} + +static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + otg->host = host; + if (!host) + otg->state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int omap_usb_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + otg->gadget = gadget; + if (!gadget) + otg->state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int omap_usb_phy_power(struct omap_usb *phy, int on) +{ + u32 val; + int ret; + + if (!phy->syscon_phy_power) { + omap_control_phy_power(phy->control_dev, on); + return 0; + } + + if (on) + val = phy->power_on; + else + val = phy->power_off; + + ret = regmap_update_bits(phy->syscon_phy_power, phy->power_reg, + phy->mask, val); + return ret; +} + +static int omap_usb_power_off(struct phy *x) +{ + struct omap_usb *phy = phy_get_drvdata(x); + + return omap_usb_phy_power(phy, false); +} + +static int omap_usb_power_on(struct phy *x) +{ + struct omap_usb *phy = phy_get_drvdata(x); + + return omap_usb_phy_power(phy, true); +} + +static int omap_usb2_disable_clocks(struct omap_usb *phy) +{ + clk_disable(phy->wkupclk); + if (!IS_ERR(phy->optclk)) + clk_disable(phy->optclk); + + return 0; +} + +static int omap_usb2_enable_clocks(struct omap_usb *phy) +{ + int ret; + + ret = clk_enable(phy->wkupclk); + if (ret < 0) { + dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); + goto err0; + } + + if (!IS_ERR(phy->optclk)) { + ret = clk_enable(phy->optclk); + if (ret < 0) { + dev_err(phy->dev, "Failed to enable optclk %d\n", ret); + goto err1; + } + } + + return 0; + +err1: + clk_disable(phy->wkupclk); + +err0: + return ret; +} + +static int omap_usb_init(struct phy *x) +{ + struct omap_usb *phy = phy_get_drvdata(x); + u32 val; + + omap_usb2_enable_clocks(phy); + + if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) { + /* + * + * Reduce the sensitivity of internal PHY by enabling the + * DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This + * resolves issues with certain devices which can otherwise + * be prone to false disconnects. + * + */ + val = omap_usb_readl(phy->phy_base, USB2PHY_ANA_CONFIG1); + val |= USB2PHY_DISCON_BYP_LATCH; + omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val); + } + + return 0; +} + +static int omap_usb_exit(struct phy *x) +{ + struct omap_usb *phy = phy_get_drvdata(x); + + return omap_usb2_disable_clocks(phy); +} + +static const struct phy_ops ops = { + .init = omap_usb_init, + .exit = omap_usb_exit, + .power_on = omap_usb_power_on, + .power_off = omap_usb_power_off, + .owner = THIS_MODULE, +}; + +static const struct usb_phy_data omap_usb2_data = { + .label = "omap_usb2", + .flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS, + .mask = OMAP_DEV_PHY_PD, + .power_off = OMAP_DEV_PHY_PD, +}; + +static const struct usb_phy_data omap5_usb2_data = { + .label = "omap5_usb2", + .flags = 0, + .mask = OMAP_DEV_PHY_PD, + .power_off = OMAP_DEV_PHY_PD, +}; + +static const struct usb_phy_data dra7x_usb2_data = { + .label = "dra7x_usb2", + .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT, + .mask = OMAP_DEV_PHY_PD, + .power_off = OMAP_DEV_PHY_PD, +}; + +static const struct usb_phy_data dra7x_usb2_phy2_data = { + .label = "dra7x_usb2_phy2", + .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT, + .mask = OMAP_USB2_PHY_PD, + .power_off = OMAP_USB2_PHY_PD, +}; + +static const struct usb_phy_data am437x_usb2_data = { + .label = "am437x_usb2", + .flags = 0, + .mask = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD | + AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN, + .power_on = AM437X_USB2_OTGVDET_EN | AM437X_USB2_OTGSESSEND_EN, + .power_off = AM437X_USB2_PHY_PD | AM437X_USB2_OTG_PD, +}; + +static const struct of_device_id omap_usb2_id_table[] = { + { + .compatible = "ti,omap-usb2", + .data = &omap_usb2_data, + }, + { + .compatible = "ti,omap5-usb2", + .data = &omap5_usb2_data, + }, + { + .compatible = "ti,dra7x-usb2", + .data = &dra7x_usb2_data, + }, + { + .compatible = "ti,dra7x-usb2-phy2", + .data = &dra7x_usb2_phy2_data, + }, + { + .compatible = "ti,am437x-usb2", + .data = &am437x_usb2_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_usb2_id_table); + +static int omap_usb2_probe(struct platform_device *pdev) +{ + struct omap_usb *phy; + struct phy *generic_phy; + struct resource *res; + struct phy_provider *phy_provider; + struct usb_otg *otg; + struct device_node *node = pdev->dev.of_node; + struct device_node *control_node; + struct platform_device *control_pdev; + const struct of_device_id *of_id; + struct usb_phy_data *phy_data; + + of_id = of_match_device(omap_usb2_id_table, &pdev->dev); + + if (!of_id) + return -EINVAL; + + phy_data = (struct usb_phy_data *)of_id->data; + + phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); + if (!otg) + return -ENOMEM; + + phy->dev = &pdev->dev; + + phy->phy.dev = phy->dev; + phy->phy.label = phy_data->label; + phy->phy.otg = otg; + phy->phy.type = USB_PHY_TYPE_USB2; + phy->mask = phy_data->mask; + phy->power_on = phy_data->power_on; + phy->power_off = phy_data->power_off; + + if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + phy->phy_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(phy->phy_base)) + return PTR_ERR(phy->phy_base); + phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT; + } + + phy->syscon_phy_power = syscon_regmap_lookup_by_phandle(node, + "syscon-phy-power"); + if (IS_ERR(phy->syscon_phy_power)) { + dev_dbg(&pdev->dev, + "can't get syscon-phy-power, using control device\n"); + phy->syscon_phy_power = NULL; + + control_node = of_parse_phandle(node, "ctrl-module", 0); + if (!control_node) { + dev_err(&pdev->dev, + "Failed to get control device phandle\n"); + return -EINVAL; + } + + control_pdev = of_find_device_by_node(control_node); + if (!control_pdev) { + dev_err(&pdev->dev, "Failed to get control device\n"); + return -EINVAL; + } + phy->control_dev = &control_pdev->dev; + } else { + if (of_property_read_u32_index(node, + "syscon-phy-power", 1, + &phy->power_reg)) { + dev_err(&pdev->dev, + "couldn't get power reg. offset\n"); + return -EINVAL; + } + } + + otg->set_host = omap_usb_set_host; + otg->set_peripheral = omap_usb_set_peripheral; + if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS) + otg->set_vbus = omap_usb_set_vbus; + if (phy_data->flags & OMAP_USB2_HAS_START_SRP) + otg->start_srp = omap_usb_start_srp; + otg->usb_phy = &phy->phy; + + platform_set_drvdata(pdev, phy); + pm_runtime_enable(phy->dev); + + generic_phy = devm_phy_create(phy->dev, NULL, &ops); + if (IS_ERR(generic_phy)) { + pm_runtime_disable(phy->dev); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, phy); + omap_usb_power_off(generic_phy); + + phy_provider = devm_of_phy_provider_register(phy->dev, + of_phy_simple_xlate); + if (IS_ERR(phy_provider)) { + pm_runtime_disable(phy->dev); + return PTR_ERR(phy_provider); + } + + phy->wkupclk = devm_clk_get(phy->dev, "wkupclk"); + if (IS_ERR(phy->wkupclk)) { + dev_warn(&pdev->dev, "unable to get wkupclk, trying old name\n"); + phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k"); + if (IS_ERR(phy->wkupclk)) { + dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n"); + pm_runtime_disable(phy->dev); + return PTR_ERR(phy->wkupclk); + } else { + dev_warn(&pdev->dev, + "found usb_phy_cm_clk32k, please fix DTS\n"); + } + } + clk_prepare(phy->wkupclk); + + phy->optclk = devm_clk_get(phy->dev, "refclk"); + if (IS_ERR(phy->optclk)) { + dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n"); + phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m"); + if (IS_ERR(phy->optclk)) { + dev_dbg(&pdev->dev, + "unable to get usb_otg_ss_refclk960m\n"); + } else { + dev_warn(&pdev->dev, + "found usb_otg_ss_refclk960m, please fix DTS\n"); + } + } + + if (!IS_ERR(phy->optclk)) + clk_prepare(phy->optclk); + + usb_add_phy_dev(&phy->phy); + + return 0; +} + +static int omap_usb2_remove(struct platform_device *pdev) +{ + struct omap_usb *phy = platform_get_drvdata(pdev); + + clk_unprepare(phy->wkupclk); + if (!IS_ERR(phy->optclk)) + clk_unprepare(phy->optclk); + usb_remove_phy(&phy->phy); + pm_runtime_disable(phy->dev); + + return 0; +} + +static struct platform_driver omap_usb2_driver = { + .probe = omap_usb2_probe, + .remove = omap_usb2_remove, + .driver = { + .name = "omap-usb2", + .of_match_table = omap_usb2_id_table, + }, +}; + +module_platform_driver(omap_usb2_driver); + +MODULE_ALIAS("platform:omap_usb2"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("OMAP USB2 phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/ti/phy-ti-pipe3.c b/drivers/phy/ti/phy-ti-pipe3.c new file mode 100644 index 000000000000..9c84d32c6f60 --- /dev/null +++ b/drivers/phy/ti/phy-ti-pipe3.c @@ -0,0 +1,697 @@ +/* + * phy-ti-pipe3 - PIPE3 PHY driver. + * + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Author: Kishon Vijay Abraham I + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PLL_STATUS 0x00000004 +#define PLL_GO 0x00000008 +#define PLL_CONFIGURATION1 0x0000000C +#define PLL_CONFIGURATION2 0x00000010 +#define PLL_CONFIGURATION3 0x00000014 +#define PLL_CONFIGURATION4 0x00000020 + +#define PLL_REGM_MASK 0x001FFE00 +#define PLL_REGM_SHIFT 0x9 +#define PLL_REGM_F_MASK 0x0003FFFF +#define PLL_REGM_F_SHIFT 0x0 +#define PLL_REGN_MASK 0x000001FE +#define PLL_REGN_SHIFT 0x1 +#define PLL_SELFREQDCO_MASK 0x0000000E +#define PLL_SELFREQDCO_SHIFT 0x1 +#define PLL_SD_MASK 0x0003FC00 +#define PLL_SD_SHIFT 10 +#define SET_PLL_GO 0x1 +#define PLL_LDOPWDN BIT(15) +#define PLL_TICOPWDN BIT(16) +#define PLL_LOCK 0x2 +#define PLL_IDLE 0x1 + +#define SATA_PLL_SOFT_RESET BIT(18) + +#define PIPE3_PHY_PWRCTL_CLK_CMD_MASK 0x003FC000 +#define PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT 14 + +#define PIPE3_PHY_PWRCTL_CLK_FREQ_MASK 0xFFC00000 +#define PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT 22 + +#define PIPE3_PHY_TX_RX_POWERON 0x3 +#define PIPE3_PHY_TX_RX_POWEROFF 0x0 + +#define PCIE_PCS_MASK 0xFF0000 +#define PCIE_PCS_DELAY_COUNT_SHIFT 0x10 + +/* + * This is an Empirical value that works, need to confirm the actual + * value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status + * to be correctly reflected in the PIPE3PHY_PLL_STATUS register. + */ +#define PLL_IDLE_TIME 100 /* in milliseconds */ +#define PLL_LOCK_TIME 100 /* in milliseconds */ + +struct pipe3_dpll_params { + u16 m; + u8 n; + u8 freq:3; + u8 sd; + u32 mf; +}; + +struct pipe3_dpll_map { + unsigned long rate; + struct pipe3_dpll_params params; +}; + +struct ti_pipe3 { + void __iomem *pll_ctrl_base; + struct device *dev; + struct device *control_dev; + struct clk *wkupclk; + struct clk *sys_clk; + struct clk *refclk; + struct clk *div_clk; + struct pipe3_dpll_map *dpll_map; + struct regmap *phy_power_syscon; /* ctrl. reg. acces */ + struct regmap *pcs_syscon; /* ctrl. reg. acces */ + struct regmap *dpll_reset_syscon; /* ctrl. reg. acces */ + unsigned int dpll_reset_reg; /* reg. index within syscon */ + unsigned int power_reg; /* power reg. index within syscon */ + unsigned int pcie_pcs_reg; /* pcs reg. index in syscon */ + bool sata_refclk_enabled; +}; + +static struct pipe3_dpll_map dpll_map_usb[] = { + {12000000, {1250, 5, 4, 20, 0} }, /* 12 MHz */ + {16800000, {3125, 20, 4, 20, 0} }, /* 16.8 MHz */ + {19200000, {1172, 8, 4, 20, 65537} }, /* 19.2 MHz */ + {20000000, {1000, 7, 4, 10, 0} }, /* 20 MHz */ + {26000000, {1250, 12, 4, 20, 0} }, /* 26 MHz */ + {38400000, {3125, 47, 4, 20, 92843} }, /* 38.4 MHz */ + { }, /* Terminator */ +}; + +static struct pipe3_dpll_map dpll_map_sata[] = { + {12000000, {1000, 7, 4, 6, 0} }, /* 12 MHz */ + {16800000, {714, 7, 4, 6, 0} }, /* 16.8 MHz */ + {19200000, {625, 7, 4, 6, 0} }, /* 19.2 MHz */ + {20000000, {600, 7, 4, 6, 0} }, /* 20 MHz */ + {26000000, {461, 7, 4, 6, 0} }, /* 26 MHz */ + {38400000, {312, 7, 4, 6, 0} }, /* 38.4 MHz */ + { }, /* Terminator */ +}; + +static inline u32 ti_pipe3_readl(void __iomem *addr, unsigned offset) +{ + return __raw_readl(addr + offset); +} + +static inline void ti_pipe3_writel(void __iomem *addr, unsigned offset, + u32 data) +{ + __raw_writel(data, addr + offset); +} + +static struct pipe3_dpll_params *ti_pipe3_get_dpll_params(struct ti_pipe3 *phy) +{ + unsigned long rate; + struct pipe3_dpll_map *dpll_map = phy->dpll_map; + + rate = clk_get_rate(phy->sys_clk); + + for (; dpll_map->rate; dpll_map++) { + if (rate == dpll_map->rate) + return &dpll_map->params; + } + + dev_err(phy->dev, "No DPLL configuration for %lu Hz SYS CLK\n", rate); + + return NULL; +} + +static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy); +static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy); + +static int ti_pipe3_power_off(struct phy *x) +{ + u32 val; + int ret; + struct ti_pipe3 *phy = phy_get_drvdata(x); + + if (!phy->phy_power_syscon) { + omap_control_phy_power(phy->control_dev, 0); + return 0; + } + + val = PIPE3_PHY_TX_RX_POWEROFF << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; + + ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, + PIPE3_PHY_PWRCTL_CLK_CMD_MASK, val); + return ret; +} + +static int ti_pipe3_power_on(struct phy *x) +{ + u32 val; + u32 mask; + int ret; + unsigned long rate; + struct ti_pipe3 *phy = phy_get_drvdata(x); + + if (!phy->phy_power_syscon) { + omap_control_phy_power(phy->control_dev, 1); + return 0; + } + + rate = clk_get_rate(phy->sys_clk); + if (!rate) { + dev_err(phy->dev, "Invalid clock rate\n"); + return -EINVAL; + } + rate = rate / 1000000; + mask = OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK | + OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK; + val = PIPE3_PHY_TX_RX_POWERON << PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT; + val |= rate << OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT; + + ret = regmap_update_bits(phy->phy_power_syscon, phy->power_reg, + mask, val); + return ret; +} + +static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy) +{ + u32 val; + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(PLL_LOCK_TIME); + do { + cpu_relax(); + val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); + if (val & PLL_LOCK) + return 0; + } while (!time_after(jiffies, timeout)); + + dev_err(phy->dev, "DPLL failed to lock\n"); + return -EBUSY; +} + +static int ti_pipe3_dpll_program(struct ti_pipe3 *phy) +{ + u32 val; + struct pipe3_dpll_params *dpll_params; + + dpll_params = ti_pipe3_get_dpll_params(phy); + if (!dpll_params) + return -EINVAL; + + val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); + val &= ~PLL_REGN_MASK; + val |= dpll_params->n << PLL_REGN_SHIFT; + ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); + + val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); + val &= ~PLL_SELFREQDCO_MASK; + val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT; + ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); + + val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1); + val &= ~PLL_REGM_MASK; + val |= dpll_params->m << PLL_REGM_SHIFT; + ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val); + + val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4); + val &= ~PLL_REGM_F_MASK; + val |= dpll_params->mf << PLL_REGM_F_SHIFT; + ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val); + + val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3); + val &= ~PLL_SD_MASK; + val |= dpll_params->sd << PLL_SD_SHIFT; + ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val); + + ti_pipe3_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO); + + return ti_pipe3_dpll_wait_lock(phy); +} + +static int ti_pipe3_init(struct phy *x) +{ + struct ti_pipe3 *phy = phy_get_drvdata(x); + u32 val; + int ret = 0; + + ti_pipe3_enable_clocks(phy); + /* + * Set pcie_pcs register to 0x96 for proper functioning of phy + * as recommended in AM572x TRM SPRUHZ6, section 18.5.2.2, table + * 18-1804. + */ + if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { + if (!phy->pcs_syscon) { + omap_control_pcie_pcs(phy->control_dev, 0x96); + return 0; + } + + val = 0x96 << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT; + ret = regmap_update_bits(phy->pcs_syscon, phy->pcie_pcs_reg, + PCIE_PCS_MASK, val); + return ret; + } + + /* Bring it out of IDLE if it is IDLE */ + val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); + if (val & PLL_IDLE) { + val &= ~PLL_IDLE; + ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); + ret = ti_pipe3_dpll_wait_lock(phy); + } + + /* SATA has issues if re-programmed when locked */ + val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); + if ((val & PLL_LOCK) && of_device_is_compatible(phy->dev->of_node, + "ti,phy-pipe3-sata")) + return ret; + + /* Program the DPLL */ + ret = ti_pipe3_dpll_program(phy); + if (ret) { + ti_pipe3_disable_clocks(phy); + return -EINVAL; + } + + return ret; +} + +static int ti_pipe3_exit(struct phy *x) +{ + struct ti_pipe3 *phy = phy_get_drvdata(x); + u32 val; + unsigned long timeout; + + /* If dpll_reset_syscon is not present we wont power down SATA DPLL + * due to Errata i783 + */ + if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") && + !phy->dpll_reset_syscon) + return 0; + + /* PCIe doesn't have internal DPLL */ + if (!of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { + /* Put DPLL in IDLE mode */ + val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2); + val |= PLL_IDLE; + ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val); + + /* wait for LDO and Oscillator to power down */ + timeout = jiffies + msecs_to_jiffies(PLL_IDLE_TIME); + do { + cpu_relax(); + val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS); + if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN)) + break; + } while (!time_after(jiffies, timeout)); + + if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) { + dev_err(phy->dev, "Failed to power down: PLL_STATUS 0x%x\n", + val); + return -EBUSY; + } + } + + /* i783: SATA needs control bit toggle after PLL unlock */ + if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata")) { + regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg, + SATA_PLL_SOFT_RESET, SATA_PLL_SOFT_RESET); + regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg, + SATA_PLL_SOFT_RESET, 0); + } + + ti_pipe3_disable_clocks(phy); + + return 0; +} +static const struct phy_ops ops = { + .init = ti_pipe3_init, + .exit = ti_pipe3_exit, + .power_on = ti_pipe3_power_on, + .power_off = ti_pipe3_power_off, + .owner = THIS_MODULE, +}; + +static const struct of_device_id ti_pipe3_id_table[]; + +static int ti_pipe3_get_clk(struct ti_pipe3 *phy) +{ + struct clk *clk; + struct device *dev = phy->dev; + struct device_node *node = dev->of_node; + + phy->refclk = devm_clk_get(dev, "refclk"); + if (IS_ERR(phy->refclk)) { + dev_err(dev, "unable to get refclk\n"); + /* older DTBs have missing refclk in SATA PHY + * so don't bail out in case of SATA PHY. + */ + if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) + return PTR_ERR(phy->refclk); + } + + if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) { + phy->wkupclk = devm_clk_get(dev, "wkupclk"); + if (IS_ERR(phy->wkupclk)) { + dev_err(dev, "unable to get wkupclk\n"); + return PTR_ERR(phy->wkupclk); + } + } else { + phy->wkupclk = ERR_PTR(-ENODEV); + } + + if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie") || + phy->phy_power_syscon) { + phy->sys_clk = devm_clk_get(dev, "sysclk"); + if (IS_ERR(phy->sys_clk)) { + dev_err(dev, "unable to get sysclk\n"); + return -EINVAL; + } + } + + if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { + clk = devm_clk_get(dev, "dpll_ref"); + if (IS_ERR(clk)) { + dev_err(dev, "unable to get dpll ref clk\n"); + return PTR_ERR(clk); + } + clk_set_rate(clk, 1500000000); + + clk = devm_clk_get(dev, "dpll_ref_m2"); + if (IS_ERR(clk)) { + dev_err(dev, "unable to get dpll ref m2 clk\n"); + return PTR_ERR(clk); + } + clk_set_rate(clk, 100000000); + + clk = devm_clk_get(dev, "phy-div"); + if (IS_ERR(clk)) { + dev_err(dev, "unable to get phy-div clk\n"); + return PTR_ERR(clk); + } + clk_set_rate(clk, 100000000); + + phy->div_clk = devm_clk_get(dev, "div-clk"); + if (IS_ERR(phy->div_clk)) { + dev_err(dev, "unable to get div-clk\n"); + return PTR_ERR(phy->div_clk); + } + } else { + phy->div_clk = ERR_PTR(-ENODEV); + } + + return 0; +} + +static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy) +{ + struct device *dev = phy->dev; + struct device_node *node = dev->of_node; + struct device_node *control_node; + struct platform_device *control_pdev; + + phy->phy_power_syscon = syscon_regmap_lookup_by_phandle(node, + "syscon-phy-power"); + if (IS_ERR(phy->phy_power_syscon)) { + dev_dbg(dev, + "can't get syscon-phy-power, using control device\n"); + phy->phy_power_syscon = NULL; + } else { + if (of_property_read_u32_index(node, + "syscon-phy-power", 1, + &phy->power_reg)) { + dev_err(dev, "couldn't get power reg. offset\n"); + return -EINVAL; + } + } + + if (!phy->phy_power_syscon) { + control_node = of_parse_phandle(node, "ctrl-module", 0); + if (!control_node) { + dev_err(dev, "Failed to get control device phandle\n"); + return -EINVAL; + } + + control_pdev = of_find_device_by_node(control_node); + if (!control_pdev) { + dev_err(dev, "Failed to get control device\n"); + return -EINVAL; + } + + phy->control_dev = &control_pdev->dev; + } + + if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { + phy->pcs_syscon = syscon_regmap_lookup_by_phandle(node, + "syscon-pcs"); + if (IS_ERR(phy->pcs_syscon)) { + dev_dbg(dev, + "can't get syscon-pcs, using omap control\n"); + phy->pcs_syscon = NULL; + } else { + if (of_property_read_u32_index(node, + "syscon-pcs", 1, + &phy->pcie_pcs_reg)) { + dev_err(dev, + "couldn't get pcie pcs reg. offset\n"); + return -EINVAL; + } + } + } + + if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) { + phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node, + "syscon-pllreset"); + if (IS_ERR(phy->dpll_reset_syscon)) { + dev_info(dev, + "can't get syscon-pllreset, sata dpll won't idle\n"); + phy->dpll_reset_syscon = NULL; + } else { + if (of_property_read_u32_index(node, + "syscon-pllreset", 1, + &phy->dpll_reset_reg)) { + dev_err(dev, + "couldn't get pllreset reg. offset\n"); + return -EINVAL; + } + } + } + + return 0; +} + +static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy) +{ + struct resource *res; + const struct of_device_id *match; + struct device *dev = phy->dev; + struct device_node *node = dev->of_node; + struct platform_device *pdev = to_platform_device(dev); + + if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) + return 0; + + match = of_match_device(ti_pipe3_id_table, dev); + if (!match) + return -EINVAL; + + phy->dpll_map = (struct pipe3_dpll_map *)match->data; + if (!phy->dpll_map) { + dev_err(dev, "no DPLL data\n"); + return -EINVAL; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "pll_ctrl"); + phy->pll_ctrl_base = devm_ioremap_resource(dev, res); + return PTR_ERR_OR_ZERO(phy->pll_ctrl_base); +} + +static int ti_pipe3_probe(struct platform_device *pdev) +{ + struct ti_pipe3 *phy; + struct phy *generic_phy; + struct phy_provider *phy_provider; + struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->dev = dev; + + ret = ti_pipe3_get_pll_base(phy); + if (ret) + return ret; + + ret = ti_pipe3_get_sysctrl(phy); + if (ret) + return ret; + + ret = ti_pipe3_get_clk(phy); + if (ret) + return ret; + + platform_set_drvdata(pdev, phy); + pm_runtime_enable(dev); + + /* + * Prevent auto-disable of refclk for SATA PHY due to Errata i783 + */ + if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) { + if (!IS_ERR(phy->refclk)) { + clk_prepare_enable(phy->refclk); + phy->sata_refclk_enabled = true; + } + } + + generic_phy = devm_phy_create(dev, NULL, &ops); + if (IS_ERR(generic_phy)) + return PTR_ERR(generic_phy); + + phy_set_drvdata(generic_phy, phy); + + ti_pipe3_power_off(generic_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static int ti_pipe3_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy) +{ + int ret = 0; + + if (!IS_ERR(phy->refclk)) { + ret = clk_prepare_enable(phy->refclk); + if (ret) { + dev_err(phy->dev, "Failed to enable refclk %d\n", ret); + return ret; + } + } + + if (!IS_ERR(phy->wkupclk)) { + ret = clk_prepare_enable(phy->wkupclk); + if (ret) { + dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret); + goto disable_refclk; + } + } + + if (!IS_ERR(phy->div_clk)) { + ret = clk_prepare_enable(phy->div_clk); + if (ret) { + dev_err(phy->dev, "Failed to enable div_clk %d\n", ret); + goto disable_wkupclk; + } + } + + return 0; + +disable_wkupclk: + if (!IS_ERR(phy->wkupclk)) + clk_disable_unprepare(phy->wkupclk); + +disable_refclk: + if (!IS_ERR(phy->refclk)) + clk_disable_unprepare(phy->refclk); + + return ret; +} + +static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy) +{ + if (!IS_ERR(phy->wkupclk)) + clk_disable_unprepare(phy->wkupclk); + if (!IS_ERR(phy->refclk)) { + clk_disable_unprepare(phy->refclk); + /* + * SATA refclk needs an additional disable as we left it + * on in probe to avoid Errata i783 + */ + if (phy->sata_refclk_enabled) { + clk_disable_unprepare(phy->refclk); + phy->sata_refclk_enabled = false; + } + } + + if (!IS_ERR(phy->div_clk)) + clk_disable_unprepare(phy->div_clk); +} + +static const struct of_device_id ti_pipe3_id_table[] = { + { + .compatible = "ti,phy-usb3", + .data = dpll_map_usb, + }, + { + .compatible = "ti,omap-usb3", + .data = dpll_map_usb, + }, + { + .compatible = "ti,phy-pipe3-sata", + .data = dpll_map_sata, + }, + { + .compatible = "ti,phy-pipe3-pcie", + }, + {} +}; +MODULE_DEVICE_TABLE(of, ti_pipe3_id_table); + +static struct platform_driver ti_pipe3_driver = { + .probe = ti_pipe3_probe, + .remove = ti_pipe3_remove, + .driver = { + .name = "ti-pipe3", + .of_match_table = ti_pipe3_id_table, + }, +}; + +module_platform_driver(ti_pipe3_driver); + +MODULE_ALIAS("platform:ti_pipe3"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TI PIPE3 phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/ti/phy-tusb1210.c b/drivers/phy/ti/phy-tusb1210.c new file mode 100644 index 000000000000..bb3fb031c478 --- /dev/null +++ b/drivers/phy/ti/phy-tusb1210.c @@ -0,0 +1,146 @@ +/** + * tusb1210.c - TUSB1210 USB ULPI PHY driver + * + * Copyright (C) 2015 Intel Corporation + * + * Author: Heikki Krogerus + * + * 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 + +#define TUSB1210_VENDOR_SPECIFIC2 0x80 +#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0 +#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT 4 +#define TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT 6 + +struct tusb1210 { + struct ulpi *ulpi; + struct phy *phy; + struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_cs; + u8 vendor_specific2; +}; + +static int tusb1210_power_on(struct phy *phy) +{ + struct tusb1210 *tusb = phy_get_drvdata(phy); + + gpiod_set_value_cansleep(tusb->gpio_reset, 1); + gpiod_set_value_cansleep(tusb->gpio_cs, 1); + + /* Restore the optional eye diagram optimization value */ + if (tusb->vendor_specific2) + ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2, + tusb->vendor_specific2); + + return 0; +} + +static int tusb1210_power_off(struct phy *phy) +{ + struct tusb1210 *tusb = phy_get_drvdata(phy); + + gpiod_set_value_cansleep(tusb->gpio_reset, 0); + gpiod_set_value_cansleep(tusb->gpio_cs, 0); + + return 0; +} + +static const struct phy_ops phy_ops = { + .power_on = tusb1210_power_on, + .power_off = tusb1210_power_off, + .owner = THIS_MODULE, +}; + +static int tusb1210_probe(struct ulpi *ulpi) +{ + struct tusb1210 *tusb; + u8 val, reg; + + tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL); + if (!tusb) + return -ENOMEM; + + tusb->gpio_reset = devm_gpiod_get_optional(&ulpi->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(tusb->gpio_reset)) + return PTR_ERR(tusb->gpio_reset); + + gpiod_set_value_cansleep(tusb->gpio_reset, 1); + + tusb->gpio_cs = devm_gpiod_get_optional(&ulpi->dev, "cs", + GPIOD_OUT_LOW); + if (IS_ERR(tusb->gpio_cs)) + return PTR_ERR(tusb->gpio_cs); + + gpiod_set_value_cansleep(tusb->gpio_cs, 1); + + /* + * VENDOR_SPECIFIC2 register in TUSB1210 can be used for configuring eye + * diagram optimization and DP/DM swap. + */ + + /* High speed output drive strength configuration */ + device_property_read_u8(&ulpi->dev, "ihstx", &val); + reg = val << TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT; + + /* High speed output impedance configuration */ + device_property_read_u8(&ulpi->dev, "zhsdrv", &val); + reg |= val << TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT; + + /* DP/DM swap control */ + device_property_read_u8(&ulpi->dev, "datapolarity", &val); + reg |= val << TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT; + + if (reg) { + ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg); + tusb->vendor_specific2 = reg; + } + + tusb->phy = ulpi_phy_create(ulpi, &phy_ops); + if (IS_ERR(tusb->phy)) + return PTR_ERR(tusb->phy); + + tusb->ulpi = ulpi; + + phy_set_drvdata(tusb->phy, tusb); + ulpi_set_drvdata(ulpi, tusb); + return 0; +} + +static void tusb1210_remove(struct ulpi *ulpi) +{ + struct tusb1210 *tusb = ulpi_get_drvdata(ulpi); + + ulpi_phy_destroy(ulpi, tusb->phy); +} + +#define TI_VENDOR_ID 0x0451 + +static const struct ulpi_device_id tusb1210_ulpi_id[] = { + { TI_VENDOR_ID, 0x1507, }, + { }, +}; +MODULE_DEVICE_TABLE(ulpi, tusb1210_ulpi_id); + +static struct ulpi_driver tusb1210_driver = { + .id_table = tusb1210_ulpi_id, + .probe = tusb1210_probe, + .remove = tusb1210_remove, + .driver = { + .name = "tusb1210", + .owner = THIS_MODULE, + }, +}; + +module_ulpi_driver(tusb1210_driver); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TUSB1210 ULPI PHY driver"); diff --git a/drivers/phy/ti/phy-twl4030-usb.c b/drivers/phy/ti/phy-twl4030-usb.c new file mode 100644 index 000000000000..2990b3965460 --- /dev/null +++ b/drivers/phy/ti/phy-twl4030-usb.c @@ -0,0 +1,839 @@ +/* + * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller + * + * Copyright (C) 2004-2007 Texas Instruments + * Copyright (C) 2008 Nokia Corporation + * Contact: Felipe Balbi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Current status: + * - HS USB ULPI mode works. + * - 3-pin mode support may be added in future. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register defines */ + +#define MCPC_CTRL 0x30 +#define MCPC_CTRL_RTSOL (1 << 7) +#define MCPC_CTRL_EXTSWR (1 << 6) +#define MCPC_CTRL_EXTSWC (1 << 5) +#define MCPC_CTRL_VOICESW (1 << 4) +#define MCPC_CTRL_OUT64K (1 << 3) +#define MCPC_CTRL_RTSCTSSW (1 << 2) +#define MCPC_CTRL_HS_UART (1 << 0) + +#define MCPC_IO_CTRL 0x33 +#define MCPC_IO_CTRL_MICBIASEN (1 << 5) +#define MCPC_IO_CTRL_CTS_NPU (1 << 4) +#define MCPC_IO_CTRL_RXD_PU (1 << 3) +#define MCPC_IO_CTRL_TXDTYP (1 << 2) +#define MCPC_IO_CTRL_CTSTYP (1 << 1) +#define MCPC_IO_CTRL_RTSTYP (1 << 0) + +#define MCPC_CTRL2 0x36 +#define MCPC_CTRL2_MCPC_CK_EN (1 << 0) + +#define OTHER_FUNC_CTRL 0x80 +#define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4) +#define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2) + +#define OTHER_IFC_CTRL 0x83 +#define OTHER_IFC_CTRL_OE_INT_EN (1 << 6) +#define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5) +#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4) +#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT (1 << 3) +#define OTHER_IFC_CTRL_HIZ_ULPI (1 << 2) +#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0) + +#define OTHER_INT_EN_RISE 0x86 +#define OTHER_INT_EN_FALL 0x89 +#define OTHER_INT_STS 0x8C +#define OTHER_INT_LATCH 0x8D +#define OTHER_INT_VB_SESS_VLD (1 << 7) +#define OTHER_INT_DM_HI (1 << 6) /* not valid for "latch" reg */ +#define OTHER_INT_DP_HI (1 << 5) /* not valid for "latch" reg */ +#define OTHER_INT_BDIS_ACON (1 << 3) /* not valid for "fall" regs */ +#define OTHER_INT_MANU (1 << 1) +#define OTHER_INT_ABNORMAL_STRESS (1 << 0) + +#define ID_STATUS 0x96 +#define ID_RES_FLOAT (1 << 4) +#define ID_RES_440K (1 << 3) +#define ID_RES_200K (1 << 2) +#define ID_RES_102K (1 << 1) +#define ID_RES_GND (1 << 0) + +#define POWER_CTRL 0xAC +#define POWER_CTRL_OTG_ENAB (1 << 5) + +#define OTHER_IFC_CTRL2 0xAF +#define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4) +#define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3) +#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2) +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK (3 << 0) /* bits 0 and 1 */ +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N (0 << 0) +#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0) + +#define REG_CTRL_EN 0xB2 +#define REG_CTRL_ERROR 0xB5 +#define ULPI_I2C_CONFLICT_INTEN (1 << 0) + +#define OTHER_FUNC_CTRL2 0xB8 +#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0) + +/* following registers do not have separate _clr and _set registers */ +#define VBUS_DEBOUNCE 0xC0 +#define ID_DEBOUNCE 0xC1 +#define VBAT_TIMER 0xD3 +#define PHY_PWR_CTRL 0xFD +#define PHY_PWR_PHYPWD (1 << 0) +#define PHY_CLK_CTRL 0xFE +#define PHY_CLK_CTRL_CLOCKGATING_EN (1 << 2) +#define PHY_CLK_CTRL_CLK32K_EN (1 << 1) +#define REQ_PHY_DPLL_CLK (1 << 0) +#define PHY_CLK_CTRL_STS 0xFF +#define PHY_DPLL_CLK (1 << 0) + +/* In module TWL_MODULE_PM_MASTER */ +#define STS_HW_CONDITIONS 0x0F + +/* In module TWL_MODULE_PM_RECEIVER */ +#define VUSB_DEDICATED1 0x7D +#define VUSB_DEDICATED2 0x7E +#define VUSB1V5_DEV_GRP 0x71 +#define VUSB1V5_TYPE 0x72 +#define VUSB1V5_REMAP 0x73 +#define VUSB1V8_DEV_GRP 0x74 +#define VUSB1V8_TYPE 0x75 +#define VUSB1V8_REMAP 0x76 +#define VUSB3V1_DEV_GRP 0x77 +#define VUSB3V1_TYPE 0x78 +#define VUSB3V1_REMAP 0x79 + +/* In module TWL4030_MODULE_INTBR */ +#define PMBR1 0x0D +#define GPIO_USB_4PIN_ULPI_2430C (3 << 0) + +/* + * If VBUS is valid or ID is ground, then we know a + * cable is present and we need to be runtime-enabled + */ +static inline bool cable_present(enum musb_vbus_id_status stat) +{ + return stat == MUSB_VBUS_VALID || + stat == MUSB_ID_GROUND; +} + +struct twl4030_usb { + struct usb_phy phy; + struct device *dev; + + /* TWL4030 internal USB regulator supplies */ + struct regulator *usb1v5; + struct regulator *usb1v8; + struct regulator *usb3v1; + + /* for vbus reporting with irqs disabled */ + struct mutex lock; + + /* pin configuration */ + enum twl4030_usb_mode usb_mode; + + int irq; + enum musb_vbus_id_status linkstat; + bool vbus_supplied; + bool musb_mailbox_pending; + + struct delayed_work id_workaround_work; +}; + +/* internal define on top of container_of */ +#define phy_to_twl(x) container_of((x), struct twl4030_usb, phy) + +/*-------------------------------------------------------------------------*/ + +static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl, + u8 module, u8 data, u8 address) +{ + u8 check; + + if ((twl_i2c_write_u8(module, data, address) >= 0) && + (twl_i2c_read_u8(module, &check, address) >= 0) && + (check == data)) + return 0; + dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", + 1, module, address, check, data); + + /* Failed once: Try again */ + if ((twl_i2c_write_u8(module, data, address) >= 0) && + (twl_i2c_read_u8(module, &check, address) >= 0) && + (check == data)) + return 0; + dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", + 2, module, address, check, data); + + /* Failed again: Return error */ + return -EBUSY; +} + +#define twl4030_usb_write_verify(twl, address, data) \ + twl4030_i2c_write_u8_verify(twl, TWL_MODULE_USB, (data), (address)) + +static inline int twl4030_usb_write(struct twl4030_usb *twl, + u8 address, u8 data) +{ + int ret = 0; + + ret = twl_i2c_write_u8(TWL_MODULE_USB, data, address); + if (ret < 0) + dev_dbg(twl->dev, + "TWL4030:USB:Write[0x%x] Error %d\n", address, ret); + return ret; +} + +static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address) +{ + u8 data; + int ret = 0; + + ret = twl_i2c_read_u8(module, &data, address); + if (ret >= 0) + ret = data; + else + dev_dbg(twl->dev, + "TWL4030:readb[0x%x,0x%x] Error %d\n", + module, address, ret); + + return ret; +} + +static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address) +{ + return twl4030_readb(twl, TWL_MODULE_USB, address); +} + +/*-------------------------------------------------------------------------*/ + +static inline int +twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits) +{ + return twl4030_usb_write(twl, ULPI_SET(reg), bits); +} + +static inline int +twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) +{ + return twl4030_usb_write(twl, ULPI_CLR(reg), bits); +} + +/*-------------------------------------------------------------------------*/ + +static bool twl4030_is_driving_vbus(struct twl4030_usb *twl) +{ + int ret; + + ret = twl4030_usb_read(twl, PHY_CLK_CTRL_STS); + if (ret < 0 || !(ret & PHY_DPLL_CLK)) + /* + * if clocks are off, registers are not updated, + * but we can assume we don't drive VBUS in this case + */ + return false; + + ret = twl4030_usb_read(twl, ULPI_OTG_CTRL); + if (ret < 0) + return false; + + return (ret & (ULPI_OTG_DRVVBUS | ULPI_OTG_CHRGVBUS)) ? true : false; +} + +static enum musb_vbus_id_status + twl4030_usb_linkstat(struct twl4030_usb *twl) +{ + int status; + enum musb_vbus_id_status linkstat = MUSB_UNKNOWN; + + twl->vbus_supplied = false; + + /* + * For ID/VBUS sensing, see manual section 15.4.8 ... + * except when using only battery backup power, two + * comparators produce VBUS_PRES and ID_PRES signals, + * which don't match docs elsewhere. But ... BIT(7) + * and BIT(2) of STS_HW_CONDITIONS, respectively, do + * seem to match up. If either is true the USB_PRES + * signal is active, the OTG module is activated, and + * its interrupt may be raised (may wake the system). + */ + status = twl4030_readb(twl, TWL_MODULE_PM_MASTER, STS_HW_CONDITIONS); + if (status < 0) + dev_err(twl->dev, "USB link status err %d\n", status); + else if (status & (BIT(7) | BIT(2))) { + if (status & BIT(7)) { + if (twl4030_is_driving_vbus(twl)) + status &= ~BIT(7); + else + twl->vbus_supplied = true; + } + + if (status & BIT(2)) + linkstat = MUSB_ID_GROUND; + else if (status & BIT(7)) + linkstat = MUSB_VBUS_VALID; + else + linkstat = MUSB_VBUS_OFF; + } else { + if (twl->linkstat != MUSB_UNKNOWN) + linkstat = MUSB_VBUS_OFF; + } + + kobject_uevent(&twl->dev->kobj, linkstat == MUSB_VBUS_VALID + ? KOBJ_ONLINE : KOBJ_OFFLINE); + + dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", + status, status, linkstat); + + /* REVISIT this assumes host and peripheral controllers + * are registered, and that both are active... + */ + + return linkstat; +} + +static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode) +{ + twl->usb_mode = mode; + + switch (mode) { + case T2_USB_MODE_ULPI: + twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL, + ULPI_IFC_CTRL_CARKITMODE); + twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); + twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL, + ULPI_FUNC_CTRL_XCVRSEL_MASK | + ULPI_FUNC_CTRL_OPMODE_MASK); + break; + case -1: + /* FIXME: power on defaults */ + break; + default: + dev_err(twl->dev, "unsupported T2 transceiver mode %d\n", + mode); + break; + } +} + +static void twl4030_i2c_access(struct twl4030_usb *twl, int on) +{ + unsigned long timeout; + int val = twl4030_usb_read(twl, PHY_CLK_CTRL); + + if (val >= 0) { + if (on) { + /* enable DPLL to access PHY registers over I2C */ + val |= REQ_PHY_DPLL_CLK; + WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, + (u8)val) < 0); + + timeout = jiffies + HZ; + while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & + PHY_DPLL_CLK) + && time_before(jiffies, timeout)) + udelay(10); + if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & + PHY_DPLL_CLK)) + dev_err(twl->dev, "Timeout setting T2 HSUSB " + "PHY DPLL clock\n"); + } else { + /* let ULPI control the DPLL clock */ + val &= ~REQ_PHY_DPLL_CLK; + WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, + (u8)val) < 0); + } + } +} + +static void __twl4030_phy_power(struct twl4030_usb *twl, int on) +{ + u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); + + if (on) + pwr &= ~PHY_PWR_PHYPWD; + else + pwr |= PHY_PWR_PHYPWD; + + WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); +} + +static int __maybe_unused twl4030_usb_runtime_suspend(struct device *dev) +{ + struct twl4030_usb *twl = dev_get_drvdata(dev); + + dev_dbg(twl->dev, "%s\n", __func__); + + __twl4030_phy_power(twl, 0); + regulator_disable(twl->usb1v5); + regulator_disable(twl->usb1v8); + regulator_disable(twl->usb3v1); + + return 0; +} + +static int __maybe_unused twl4030_usb_runtime_resume(struct device *dev) +{ + struct twl4030_usb *twl = dev_get_drvdata(dev); + int res; + + dev_dbg(twl->dev, "%s\n", __func__); + + res = regulator_enable(twl->usb3v1); + if (res) + dev_err(twl->dev, "Failed to enable usb3v1\n"); + + res = regulator_enable(twl->usb1v8); + if (res) + dev_err(twl->dev, "Failed to enable usb1v8\n"); + + /* + * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP + * in twl4030) resets the VUSB_DEDICATED2 register. This reset + * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to + * SLEEP. We work around this by clearing the bit after usv3v1 + * is re-activated. This ensures that VUSB3V1 is really active. + */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); + + res = regulator_enable(twl->usb1v5); + if (res) + dev_err(twl->dev, "Failed to enable usb1v5\n"); + + __twl4030_phy_power(twl, 1); + twl4030_usb_write(twl, PHY_CLK_CTRL, + twl4030_usb_read(twl, PHY_CLK_CTRL) | + (PHY_CLK_CTRL_CLOCKGATING_EN | + PHY_CLK_CTRL_CLK32K_EN)); + + twl4030_i2c_access(twl, 1); + twl4030_usb_set_mode(twl, twl->usb_mode); + if (twl->usb_mode == T2_USB_MODE_ULPI) + twl4030_i2c_access(twl, 0); + /* + * According to the TPS65950 TRM, there has to be at least 50ms + * delay between setting POWER_CTRL_OTG_ENAB and enabling charging + * so wait here so that a fully enabled phy can be expected after + * resume + */ + msleep(50); + return 0; +} + +static int twl4030_phy_power_off(struct phy *phy) +{ + struct twl4030_usb *twl = phy_get_drvdata(phy); + + dev_dbg(twl->dev, "%s\n", __func__); + + return 0; +} + +static int twl4030_phy_power_on(struct phy *phy) +{ + struct twl4030_usb *twl = phy_get_drvdata(phy); + + dev_dbg(twl->dev, "%s\n", __func__); + pm_runtime_get_sync(twl->dev); + schedule_delayed_work(&twl->id_workaround_work, HZ); + pm_runtime_mark_last_busy(twl->dev); + pm_runtime_put_autosuspend(twl->dev); + + return 0; +} + +static int twl4030_usb_ldo_init(struct twl4030_usb *twl) +{ + /* Enable writing to power configuration registers */ + twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, + TWL4030_PM_MASTER_PROTECT_KEY); + + twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, + TWL4030_PM_MASTER_PROTECT_KEY); + + /* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/ + /*twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/ + + /* input to VUSB3V1 LDO is from VBAT, not VBUS */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); + + /* Initialize 3.1V regulator */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP); + + twl->usb3v1 = devm_regulator_get(twl->dev, "usb3v1"); + if (IS_ERR(twl->usb3v1)) + return -ENODEV; + + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); + + /* Initialize 1.5V regulator */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP); + + twl->usb1v5 = devm_regulator_get(twl->dev, "usb1v5"); + if (IS_ERR(twl->usb1v5)) + return -ENODEV; + + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); + + /* Initialize 1.8V regulator */ + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP); + + twl->usb1v8 = devm_regulator_get(twl->dev, "usb1v8"); + if (IS_ERR(twl->usb1v8)) + return -ENODEV; + + twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); + + /* disable access to power configuration registers */ + twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, + TWL4030_PM_MASTER_PROTECT_KEY); + + return 0; +} + +static ssize_t twl4030_usb_vbus_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct twl4030_usb *twl = dev_get_drvdata(dev); + int ret = -EINVAL; + + mutex_lock(&twl->lock); + ret = sprintf(buf, "%s\n", + twl->vbus_supplied ? "on" : "off"); + mutex_unlock(&twl->lock); + + return ret; +} +static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); + +static irqreturn_t twl4030_usb_irq(int irq, void *_twl) +{ + struct twl4030_usb *twl = _twl; + enum musb_vbus_id_status status; + bool status_changed = false; + int err; + + status = twl4030_usb_linkstat(twl); + + mutex_lock(&twl->lock); + if (status >= 0 && status != twl->linkstat) { + status_changed = + cable_present(twl->linkstat) != + cable_present(status); + twl->linkstat = status; + } + mutex_unlock(&twl->lock); + + if (status_changed) { + /* FIXME add a set_power() method so that B-devices can + * configure the charger appropriately. It's not always + * correct to consume VBUS power, and how much current to + * consume is a function of the USB configuration chosen + * by the host. + * + * REVISIT usb_gadget_vbus_connect(...) as needed, ditto + * its disconnect() sibling, when changing to/from the + * USB_LINK_VBUS state. musb_hdrc won't care until it + * starts to handle softconnect right. + */ + if (cable_present(status)) { + pm_runtime_get_sync(twl->dev); + } else { + pm_runtime_mark_last_busy(twl->dev); + pm_runtime_put_autosuspend(twl->dev); + } + twl->musb_mailbox_pending = true; + } + if (twl->musb_mailbox_pending) { + err = musb_mailbox(status); + if (!err) + twl->musb_mailbox_pending = false; + } + + /* don't schedule during sleep - irq works right then */ + if (status == MUSB_ID_GROUND && pm_runtime_active(twl->dev)) { + cancel_delayed_work(&twl->id_workaround_work); + schedule_delayed_work(&twl->id_workaround_work, HZ); + } + + if (irq) + sysfs_notify(&twl->dev->kobj, NULL, "vbus"); + + return IRQ_HANDLED; +} + +static void twl4030_id_workaround_work(struct work_struct *work) +{ + struct twl4030_usb *twl = container_of(work, struct twl4030_usb, + id_workaround_work.work); + + twl4030_usb_irq(0, twl); +} + +static int twl4030_phy_init(struct phy *phy) +{ + struct twl4030_usb *twl = phy_get_drvdata(phy); + + pm_runtime_get_sync(twl->dev); + twl->linkstat = MUSB_UNKNOWN; + schedule_delayed_work(&twl->id_workaround_work, HZ); + pm_runtime_mark_last_busy(twl->dev); + pm_runtime_put_autosuspend(twl->dev); + + return 0; +} + +static int twl4030_set_peripheral(struct usb_otg *otg, + struct usb_gadget *gadget) +{ + if (!otg) + return -ENODEV; + + otg->gadget = gadget; + if (!gadget) + otg->state = OTG_STATE_UNDEFINED; + + return 0; +} + +static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host) +{ + if (!otg) + return -ENODEV; + + otg->host = host; + if (!host) + otg->state = OTG_STATE_UNDEFINED; + + return 0; +} + +static const struct phy_ops ops = { + .init = twl4030_phy_init, + .power_on = twl4030_phy_power_on, + .power_off = twl4030_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct dev_pm_ops twl4030_usb_pm_ops = { + SET_RUNTIME_PM_OPS(twl4030_usb_runtime_suspend, + twl4030_usb_runtime_resume, NULL) +}; + +static int twl4030_usb_probe(struct platform_device *pdev) +{ + struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev); + struct twl4030_usb *twl; + struct phy *phy; + int status, err; + struct usb_otg *otg; + struct device_node *np = pdev->dev.of_node; + struct phy_provider *phy_provider; + + twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); + if (!twl) + return -ENOMEM; + + if (np) + of_property_read_u32(np, "usb_mode", + (enum twl4030_usb_mode *)&twl->usb_mode); + else if (pdata) { + twl->usb_mode = pdata->usb_mode; + } else { + dev_err(&pdev->dev, "twl4030 initialized without pdata\n"); + return -EINVAL; + } + + otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); + if (!otg) + return -ENOMEM; + + twl->dev = &pdev->dev; + twl->irq = platform_get_irq(pdev, 0); + twl->vbus_supplied = false; + twl->linkstat = MUSB_UNKNOWN; + twl->musb_mailbox_pending = false; + + twl->phy.dev = twl->dev; + twl->phy.label = "twl4030"; + twl->phy.otg = otg; + twl->phy.type = USB_PHY_TYPE_USB2; + + otg->usb_phy = &twl->phy; + otg->set_host = twl4030_set_host; + otg->set_peripheral = twl4030_set_peripheral; + + phy = devm_phy_create(twl->dev, NULL, &ops); + if (IS_ERR(phy)) { + dev_dbg(&pdev->dev, "Failed to create PHY\n"); + return PTR_ERR(phy); + } + + phy_set_drvdata(phy, twl); + + phy_provider = devm_of_phy_provider_register(twl->dev, + of_phy_simple_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + /* init mutex for workqueue */ + mutex_init(&twl->lock); + + INIT_DELAYED_WORK(&twl->id_workaround_work, twl4030_id_workaround_work); + + err = twl4030_usb_ldo_init(twl); + if (err) { + dev_err(&pdev->dev, "ldo init failed\n"); + return err; + } + usb_add_phy_dev(&twl->phy); + + platform_set_drvdata(pdev, twl); + if (device_create_file(&pdev->dev, &dev_attr_vbus)) + dev_warn(&pdev->dev, "could not create sysfs file\n"); + + ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier); + + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + /* Our job is to use irqs and status from the power module + * to keep the transceiver disabled when nothing's connected. + * + * FIXME we actually shouldn't start enabling it until the + * USB controller drivers have said they're ready, by calling + * set_host() and/or set_peripheral() ... OTG_capable boards + * need both handles, otherwise just one suffices. + */ + status = devm_request_threaded_irq(twl->dev, twl->irq, NULL, + twl4030_usb_irq, IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl4030_usb", twl); + if (status < 0) { + dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n", + twl->irq, status); + return status; + } + + if (pdata) + err = phy_create_lookup(phy, "usb", "musb-hdrc.0"); + if (err) + return err; + + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(twl->dev); + + dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); + return 0; +} + +static int twl4030_usb_remove(struct platform_device *pdev) +{ + struct twl4030_usb *twl = platform_get_drvdata(pdev); + int val; + + usb_remove_phy(&twl->phy); + pm_runtime_get_sync(twl->dev); + cancel_delayed_work(&twl->id_workaround_work); + device_remove_file(twl->dev, &dev_attr_vbus); + + /* set transceiver mode to power on defaults */ + twl4030_usb_set_mode(twl, -1); + + /* idle ulpi before powering off */ + if (cable_present(twl->linkstat)) + pm_runtime_put_noidle(twl->dev); + pm_runtime_mark_last_busy(twl->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(twl->dev); + pm_runtime_disable(twl->dev); + + /* autogate 60MHz ULPI clock, + * clear dpll clock request for i2c access, + * disable 32KHz + */ + val = twl4030_usb_read(twl, PHY_CLK_CTRL); + if (val >= 0) { + val |= PHY_CLK_CTRL_CLOCKGATING_EN; + val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK); + twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val); + } + + /* disable complete OTG block */ + twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id twl4030_usb_id_table[] = { + { .compatible = "ti,twl4030-usb" }, + {} +}; +MODULE_DEVICE_TABLE(of, twl4030_usb_id_table); +#endif + +static struct platform_driver twl4030_usb_driver = { + .probe = twl4030_usb_probe, + .remove = twl4030_usb_remove, + .driver = { + .name = "twl4030_usb", + .pm = &twl4030_usb_pm_ops, + .of_match_table = of_match_ptr(twl4030_usb_id_table), + }, +}; + +static int __init twl4030_usb_init(void) +{ + return platform_driver_register(&twl4030_usb_driver); +} +subsys_initcall(twl4030_usb_init); + +static void __exit twl4030_usb_exit(void) +{ + platform_driver_unregister(&twl4030_usb_driver); +} +module_exit(twl4030_usb_exit); + +MODULE_ALIAS("platform:twl4030_usb"); +MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation"); +MODULE_DESCRIPTION("TWL4030 USB transceiver driver"); +MODULE_LICENSE("GPL");