From 49859e55e364b9e6a53ae8f80318a2e7bd35ef37 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 22 Sep 2017 15:34:01 -0400 Subject: phy: usb: phy-brcm-usb: Add Broadcom STB USB phy driver Add a new USB Phy driver for Broadcom STB SoCs. This driver supports Broadcom STB ARM SoCs. This driver in combination with the Broadcom STB ohci, ehci and xhci drivers will enable USB1.1, USB2.0 and USB3.0 support. This Phy driver also supports the Broadcom BDC gadget driver. Signed-off-by: Al Cooper Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/Kconfig | 13 + drivers/phy/broadcom/Makefile | 3 + drivers/phy/broadcom/phy-brcm-usb-init.c | 1017 ++++++++++++++++++++++++++++++ drivers/phy/broadcom/phy-brcm-usb-init.h | 50 ++ drivers/phy/broadcom/phy-brcm-usb.c | 374 +++++++++++ 5 files changed, 1457 insertions(+) create mode 100644 drivers/phy/broadcom/phy-brcm-usb-init.c create mode 100644 drivers/phy/broadcom/phy-brcm-usb-init.h create mode 100644 drivers/phy/broadcom/phy-brcm-usb.c (limited to 'drivers/phy') diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig index 64fc59c3ae6d..97d27b0d5cc7 100644 --- a/drivers/phy/broadcom/Kconfig +++ b/drivers/phy/broadcom/Kconfig @@ -67,3 +67,16 @@ config PHY_BRCM_SATA help Enable this to support the Broadcom SATA PHY. If unsure, say N. + +config PHY_BRCM_USB + tristate "Broadcom STB USB PHY driver" + depends on ARCH_BRCMSTB + depends on OF + select GENERIC_PHY + select SOC_BRCMSTB + default ARCH_BRCMSTB + help + Enable this to support the Broadcom STB USB PHY. + This driver is required by the USB XHCI, EHCI and OHCI + drivers. + If unsure, say N. diff --git a/drivers/phy/broadcom/Makefile b/drivers/phy/broadcom/Makefile index 4eb82ec8d491..7c37ba651440 100644 --- a/drivers/phy/broadcom/Makefile +++ b/drivers/phy/broadcom/Makefile @@ -5,3 +5,6 @@ obj-$(CONFIG_PHY_BCM_NS_USB3) += phy-bcm-ns-usb3.o obj-$(CONFIG_PHY_NS2_PCIE) += phy-bcm-ns2-pcie.o obj-$(CONFIG_PHY_NS2_USB_DRD) += phy-bcm-ns2-usbdrd.o obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o +obj-$(CONFIG_PHY_BRCM_USB) += phy-brcm-usb-dvr.o + +phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c new file mode 100644 index 000000000000..1e7ce0b6f299 --- /dev/null +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -0,0 +1,1017 @@ +/* + * phy-brcm-usb-init.c - Broadcom USB Phy chip specific init functions + * + * Copyright (C) 2014-2017 Broadcom + * + * 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. + */ + +/* + * This module contains USB PHY initialization for power up and S3 resume + */ + +#include +#include + +#include +#include "phy-brcm-usb-init.h" + +#define PHY_PORTS 2 +#define PHY_PORT_SELECT_0 0 +#define PHY_PORT_SELECT_1 0x1000 + +/* Register definitions for the USB CTRL block */ +#define USB_CTRL_SETUP 0x00 +#define USB_CTRL_SETUP_IOC_MASK 0x00000010 +#define USB_CTRL_SETUP_IPP_MASK 0x00000020 +#define USB_CTRL_SETUP_BABO_MASK 0x00000001 +#define USB_CTRL_SETUP_FNHW_MASK 0x00000002 +#define USB_CTRL_SETUP_FNBO_MASK 0x00000004 +#define USB_CTRL_SETUP_WABO_MASK 0x00000008 +#define USB_CTRL_SETUP_SCB_CLIENT_SWAP_MASK 0x00002000 /* option */ +#define USB_CTRL_SETUP_SCB1_EN_MASK 0x00004000 /* option */ +#define USB_CTRL_SETUP_SCB2_EN_MASK 0x00008000 /* option */ +#define USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK 0X00020000 /* option */ +#define USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK 0x00010000 /* option */ +#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK 0x02000000 /* option */ +#define USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK 0x04000000 /* option */ +#define USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK 0x08000000 /* opt */ +#define USB_CTRL_SETUP_OC3_DISABLE_MASK 0xc0000000 /* option */ +#define USB_CTRL_PLL_CTL 0x04 +#define USB_CTRL_PLL_CTL_PLL_SUSPEND_EN_MASK 0x08000000 +#define USB_CTRL_PLL_CTL_PLL_RESETB_MASK 0x40000000 +#define USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK 0x80000000 /* option */ +#define USB_CTRL_EBRIDGE 0x0c +#define USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK 0x00020000 /* option */ +#define USB_CTRL_MDIO 0x14 +#define USB_CTRL_MDIO2 0x18 +#define USB_CTRL_UTMI_CTL_1 0x2c +#define USB_CTRL_UTMI_CTL_1_POWER_UP_FSM_EN_MASK 0x00000800 +#define USB_CTRL_UTMI_CTL_1_POWER_UP_FSM_EN_P1_MASK 0x08000000 +#define USB_CTRL_USB_PM 0x34 +#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000 /* option */ +#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000 /* option */ +#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK 0x40000000 /* option */ +#define USB_CTRL_USB_PM_USB_PWRDN_MASK 0x80000000 /* option */ +#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000 /* option */ +#define USB_CTRL_USB_PM_USB20_HC_RESETB_MASK 0x30000000 /* option */ +#define USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK 0x00300000 /* option */ +#define USB_CTRL_USB30_CTL1 0x60 +#define USB_CTRL_USB30_CTL1_PHY3_PLL_SEQ_START_MASK 0x00000010 +#define USB_CTRL_USB30_CTL1_PHY3_RESETB_MASK 0x00010000 +#define USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK 0x00020000 /* option */ +#define USB_CTRL_USB30_CTL1_USB3_IOC_MASK 0x10000000 /* option */ +#define USB_CTRL_USB30_CTL1_USB3_IPP_MASK 0x20000000 /* option */ +#define USB_CTRL_USB30_PCTL 0x70 +#define USB_CTRL_USB30_PCTL_PHY3_SOFT_RESETB_MASK 0x00000002 +#define USB_CTRL_USB30_PCTL_PHY3_SOFT_RESETB_P1_MASK 0x00020000 +#define USB_CTRL_USB_DEVICE_CTL1 0x90 +#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003 /* option */ + +/* Register definitions for the XHCI EC block */ +#define USB_XHCI_EC_IRAADR 0x658 +#define USB_XHCI_EC_IRADAT 0x65c + +enum brcm_family_type { + BRCM_FAMILY_3390A0, + BRCM_FAMILY_7250B0, + BRCM_FAMILY_7271A0, + BRCM_FAMILY_7364A0, + BRCM_FAMILY_7366C0, + BRCM_FAMILY_74371A0, + BRCM_FAMILY_7439B0, + BRCM_FAMILY_7445D0, + BRCM_FAMILY_7260A0, + BRCM_FAMILY_7278A0, + BRCM_FAMILY_COUNT, +}; + +#define USB_BRCM_FAMILY(chip) \ + [BRCM_FAMILY_##chip] = __stringify(chip) + +static const char *family_names[BRCM_FAMILY_COUNT] = { + USB_BRCM_FAMILY(3390A0), + USB_BRCM_FAMILY(7250B0), + USB_BRCM_FAMILY(7271A0), + USB_BRCM_FAMILY(7364A0), + USB_BRCM_FAMILY(7366C0), + USB_BRCM_FAMILY(74371A0), + USB_BRCM_FAMILY(7439B0), + USB_BRCM_FAMILY(7445D0), + USB_BRCM_FAMILY(7260A0), + USB_BRCM_FAMILY(7278A0), +}; + +enum { + USB_CTRL_SETUP_SCB1_EN_SELECTOR, + USB_CTRL_SETUP_SCB2_EN_SELECTOR, + USB_CTRL_SETUP_SS_EHCI64BIT_EN_SELECTOR, + USB_CTRL_SETUP_STRAP_IPP_SEL_SELECTOR, + USB_CTRL_SETUP_OC3_DISABLE_SELECTOR, + USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_SELECTOR, + USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_SELECTOR, + USB_CTRL_USB_PM_BDC_SOFT_RESETB_SELECTOR, + USB_CTRL_USB_PM_XHC_SOFT_RESETB_SELECTOR, + USB_CTRL_USB_PM_USB_PWRDN_SELECTOR, + USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_SELECTOR, + USB_CTRL_USB30_CTL1_USB3_IOC_SELECTOR, + USB_CTRL_USB30_CTL1_USB3_IPP_SELECTOR, + USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_SELECTOR, + USB_CTRL_USB_PM_SOFT_RESET_SELECTOR, + USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_SELECTOR, + USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_SELECTOR, + USB_CTRL_USB_PM_USB20_HC_RESETB_SELECTOR, + USB_CTRL_SETUP_ENDIAN_SELECTOR, + USB_CTRL_SELECTOR_COUNT, +}; + +#define USB_CTRL_REG(base, reg) ((void *)base + USB_CTRL_##reg) +#define USB_XHCI_EC_REG(base, reg) ((void *)base + USB_XHCI_EC_##reg) +#define USB_CTRL_MASK(reg, field) \ + USB_CTRL_##reg##_##field##_MASK +#define USB_CTRL_MASK_FAMILY(params, reg, field) \ + (params->usb_reg_bits_map[USB_CTRL_##reg##_##field##_SELECTOR]) + +#define USB_CTRL_SET_FAMILY(params, reg, field) \ + usb_ctrl_set_family(params, USB_CTRL_##reg, \ + USB_CTRL_##reg##_##field##_SELECTOR) +#define USB_CTRL_UNSET_FAMILY(params, reg, field) \ + usb_ctrl_unset_family(params, USB_CTRL_##reg, \ + USB_CTRL_##reg##_##field##_SELECTOR) + +#define USB_CTRL_SET(base, reg, field) \ + usb_ctrl_set(USB_CTRL_REG(base, reg), \ + USB_CTRL_##reg##_##field##_MASK) +#define USB_CTRL_UNSET(base, reg, field) \ + usb_ctrl_unset(USB_CTRL_REG(base, reg), \ + USB_CTRL_##reg##_##field##_MASK) + +#define MDIO_USB2 0 +#define MDIO_USB3 BIT(31) + +#define USB_CTRL_SETUP_ENDIAN_BITS ( \ + USB_CTRL_MASK(SETUP, BABO) | \ + USB_CTRL_MASK(SETUP, FNHW) | \ + USB_CTRL_MASK(SETUP, FNBO) | \ + USB_CTRL_MASK(SETUP, WABO)) + +#ifdef __LITTLE_ENDIAN +#define ENDIAN_SETTINGS ( \ + USB_CTRL_MASK(SETUP, BABO) | \ + USB_CTRL_MASK(SETUP, FNHW)) +#else +#define ENDIAN_SETTINGS ( \ + USB_CTRL_MASK(SETUP, FNHW) | \ + USB_CTRL_MASK(SETUP, FNBO) | \ + USB_CTRL_MASK(SETUP, WABO)) +#endif + +struct id_to_type { + u32 id; + int type; +}; + +static const struct id_to_type id_to_type_table[] = { + { 0x33900000, BRCM_FAMILY_3390A0 }, + { 0x72500010, BRCM_FAMILY_7250B0 }, + { 0x72600000, BRCM_FAMILY_7260A0 }, + { 0x72680000, BRCM_FAMILY_7271A0 }, + { 0x72710000, BRCM_FAMILY_7271A0 }, + { 0x73640000, BRCM_FAMILY_7364A0 }, + { 0x73660020, BRCM_FAMILY_7366C0 }, + { 0x07437100, BRCM_FAMILY_74371A0 }, + { 0x74390010, BRCM_FAMILY_7439B0 }, + { 0x74450030, BRCM_FAMILY_7445D0 }, + { 0x72780000, BRCM_FAMILY_7278A0 }, + { 0, BRCM_FAMILY_7271A0 }, /* default */ +}; + +static const u32 +usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { + /* 3390B0 */ + [BRCM_FAMILY_3390A0] = { + USB_CTRL_SETUP_SCB1_EN_MASK, + USB_CTRL_SETUP_SCB2_EN_MASK, + USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, + USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_MASK, + 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ + USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK, + 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ + USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, + USB_CTRL_USB_PM_USB_PWRDN_MASK, + 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ + USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, + 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ + 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ + 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ + USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, + ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + }, + /* 7250b0 */ + [BRCM_FAMILY_7250B0] = { + USB_CTRL_SETUP_SCB1_EN_MASK, + USB_CTRL_SETUP_SCB2_EN_MASK, + USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, + 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + USB_CTRL_SETUP_OC3_DISABLE_MASK, + USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, + USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK, + 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ + USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK, + 0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */ + 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ + 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */ + 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ + 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ + 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ + USB_CTRL_USB_PM_USB20_HC_RESETB_MASK, + ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + }, + /* 7271a0 */ + [BRCM_FAMILY_7271A0] = { + 0, /* USB_CTRL_SETUP_SCB1_EN_MASK */ + 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ + USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, + USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_MASK, + 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ + USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK, + USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, + USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, + USB_CTRL_USB_PM_USB_PWRDN_MASK, + 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ + USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, + USB_CTRL_USB_PM_SOFT_RESET_MASK, + USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK, + USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK, + USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, + ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + }, + /* 7364a0 */ + [BRCM_FAMILY_7364A0] = { + USB_CTRL_SETUP_SCB1_EN_MASK, + USB_CTRL_SETUP_SCB2_EN_MASK, + USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, + 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + USB_CTRL_SETUP_OC3_DISABLE_MASK, + USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, + USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK, + 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ + USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK, + 0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */ + 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ + 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */ + 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ + 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ + 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ + USB_CTRL_USB_PM_USB20_HC_RESETB_MASK, + ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + }, + /* 7366c0 */ + [BRCM_FAMILY_7366C0] = { + USB_CTRL_SETUP_SCB1_EN_MASK, + USB_CTRL_SETUP_SCB2_EN_MASK, + USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, + 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + USB_CTRL_SETUP_OC3_DISABLE_MASK, + 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ + USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK, + 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ + USB_CTRL_USB_PM_XHC_SOFT_RESETB_VAR_MASK, + USB_CTRL_USB_PM_USB_PWRDN_MASK, + 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ + 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */ + 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ + 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ + 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ + USB_CTRL_USB_PM_USB20_HC_RESETB_MASK, + ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + }, + /* 74371A0 */ + [BRCM_FAMILY_74371A0] = { + USB_CTRL_SETUP_SCB1_EN_MASK, + USB_CTRL_SETUP_SCB2_EN_MASK, + USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK, + 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + 0, /* USB_CTRL_SETUP_OC3_DISABLE_MASK */ + USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, + 0, /* USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK */ + 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ + 0, /* USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK */ + 0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */ + USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK, + USB_CTRL_USB30_CTL1_USB3_IOC_MASK, + USB_CTRL_USB30_CTL1_USB3_IPP_MASK, + 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */ + 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ + 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ + 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ + 0, /* USB_CTRL_USB_PM_USB20_HC_RESETB_MASK */ + ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + }, + /* 7439B0 */ + [BRCM_FAMILY_7439B0] = { + USB_CTRL_SETUP_SCB1_EN_MASK, + USB_CTRL_SETUP_SCB2_EN_MASK, + USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, + USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_MASK, + 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ + 0, /* USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK */ + USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, + USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, + USB_CTRL_USB_PM_USB_PWRDN_MASK, + 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ + USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, + 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ + 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ + 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ + USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, + ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + }, + /* 7445d0 */ + [BRCM_FAMILY_7445D0] = { + USB_CTRL_SETUP_SCB1_EN_MASK, + USB_CTRL_SETUP_SCB2_EN_MASK, + USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK, + 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + USB_CTRL_SETUP_OC3_DISABLE_MASK, + USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, + 0, /* USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK */ + 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ + 0, /* USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK */ + 0, /* USB_CTRL_USB_PM_USB_PWRDN_MASK */ + USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK, + 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ + 0, /* USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK */ + 0, /* USB_CTRL_USB_PM_SOFT_RESET_MASK */ + 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ + 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ + USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, + ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + }, + /* 7260a0 */ + [BRCM_FAMILY_7260A0] = { + 0, /* USB_CTRL_SETUP_SCB1_EN_MASK */ + 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ + USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, + USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_MASK, + 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ + USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK, + USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, + USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, + USB_CTRL_USB_PM_USB_PWRDN_MASK, + 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ + USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, + USB_CTRL_USB_PM_SOFT_RESET_MASK, + USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK, + USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK, + USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK, + ENDIAN_SETTINGS, /* USB_CTRL_SETUP ENDIAN bits */ + }, + /* 7278a0 */ + [BRCM_FAMILY_7278A0] = { + 0, /* USB_CTRL_SETUP_SCB1_EN_MASK */ + 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ + 0, /*USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK */ + USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_MASK, + 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ + USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK, + USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, + USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK, + USB_CTRL_USB_PM_USB_PWRDN_MASK, + 0, /* USB_CTRL_USB30_CTL1_XHC_SOFT_RESETB_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IOC_MASK */ + 0, /* USB_CTRL_USB30_CTL1_USB3_IPP_MASK */ + USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK, + USB_CTRL_USB_PM_SOFT_RESET_MASK, + 0, /* USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK */ + 0, /* USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK */ + 0, /* USB_CTRL_USB_PM_USB20_HC_RESETB_MASK */ + 0, /* USB_CTRL_SETUP ENDIAN bits */ + }, +}; + +static inline u32 brcmusb_readl(void __iomem *addr) +{ + return readl(addr); +} + +static inline void brcmusb_writel(u32 val, void __iomem *addr) +{ + writel(val, addr); +} + +static inline +void usb_ctrl_unset_family(struct brcm_usb_init_params *params, + u32 reg_offset, u32 field) +{ + u32 mask; + void *reg; + + mask = params->usb_reg_bits_map[field]; + reg = params->ctrl_regs + reg_offset; + brcmusb_writel(brcmusb_readl(reg) & ~mask, reg); +}; + +static inline +void usb_ctrl_set_family(struct brcm_usb_init_params *params, + u32 reg_offset, u32 field) +{ + u32 mask; + void *reg; + + mask = params->usb_reg_bits_map[field]; + reg = params->ctrl_regs + reg_offset; + brcmusb_writel(brcmusb_readl(reg) | mask, reg); +}; + +static inline void usb_ctrl_set(void __iomem *reg, u32 field) +{ + u32 value; + + value = brcmusb_readl(reg); + brcmusb_writel(value | field, reg); +} + +static inline void usb_ctrl_unset(void __iomem *reg, u32 field) +{ + u32 value; + + value = brcmusb_readl(reg); + brcmusb_writel(value & ~field, reg); +} + +static u32 brcmusb_usb_mdio_read(void __iomem *ctrl_base, u32 reg, int mode) +{ + u32 data; + + data = (reg << 16) | mode; + brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); + data |= (1 << 24); + brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); + data &= ~(1 << 24); + /* wait for the 60MHz parallel to serial shifter */ + usleep_range(10, 20); + brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); + /* wait for the 60MHz parallel to serial shifter */ + usleep_range(10, 20); + + return brcmusb_readl(USB_CTRL_REG(ctrl_base, MDIO2)) & 0xffff; +} + +static void brcmusb_usb_mdio_write(void __iomem *ctrl_base, u32 reg, + u32 val, int mode) +{ + u32 data; + + data = (reg << 16) | val | mode; + brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); + data |= (1 << 25); + brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); + data &= ~(1 << 25); + + /* wait for the 60MHz parallel to serial shifter */ + usleep_range(10, 20); + brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO)); + /* wait for the 60MHz parallel to serial shifter */ + usleep_range(10, 20); +} + +static void brcmusb_usb_phy_ldo_fix(void __iomem *ctrl_base) +{ + /* first disable FSM but also leave it that way */ + /* to allow normal suspend/resume */ + USB_CTRL_UNSET(ctrl_base, UTMI_CTL_1, POWER_UP_FSM_EN); + USB_CTRL_UNSET(ctrl_base, UTMI_CTL_1, POWER_UP_FSM_EN_P1); + + /* reset USB 2.0 PLL */ + USB_CTRL_UNSET(ctrl_base, PLL_CTL, PLL_RESETB); + /* PLL reset period */ + udelay(1); + USB_CTRL_SET(ctrl_base, PLL_CTL, PLL_RESETB); + /* Give PLL enough time to lock */ + usleep_range(1000, 2000); +} + +static void brcmusb_usb2_eye_fix(void __iomem *ctrl_base) +{ + /* Increase USB 2.0 TX level to meet spec requirement */ + brcmusb_usb_mdio_write(ctrl_base, 0x1f, 0x80a0, MDIO_USB2); + brcmusb_usb_mdio_write(ctrl_base, 0x0a, 0xc6a0, MDIO_USB2); +} + +static void brcmusb_usb3_pll_fix(void __iomem *ctrl_base) +{ + /* Set correct window for PLL lock detect */ + brcmusb_usb_mdio_write(ctrl_base, 0x1f, 0x8000, MDIO_USB3); + brcmusb_usb_mdio_write(ctrl_base, 0x07, 0x1503, MDIO_USB3); +} + +static void brcmusb_usb3_enable_pipe_reset(void __iomem *ctrl_base) +{ + u32 val; + + /* Re-enable USB 3.0 pipe reset */ + brcmusb_usb_mdio_write(ctrl_base, 0x1f, 0x8000, MDIO_USB3); + val = brcmusb_usb_mdio_read(ctrl_base, 0x0f, MDIO_USB3) | 0x200; + brcmusb_usb_mdio_write(ctrl_base, 0x0f, val, MDIO_USB3); +} + +static void brcmusb_usb3_enable_sigdet(void __iomem *ctrl_base) +{ + u32 val, ofs; + int ii; + + ofs = 0; + for (ii = 0; ii < PHY_PORTS; ++ii) { + /* Set correct default for sigdet */ + brcmusb_usb_mdio_write(ctrl_base, 0x1f, (0x8080 + ofs), + MDIO_USB3); + val = brcmusb_usb_mdio_read(ctrl_base, 0x05, MDIO_USB3); + val = (val & ~0x800f) | 0x800d; + brcmusb_usb_mdio_write(ctrl_base, 0x05, val, MDIO_USB3); + ofs = PHY_PORT_SELECT_1; + } +} + +static void brcmusb_usb3_enable_skip_align(void __iomem *ctrl_base) +{ + u32 val, ofs; + int ii; + + ofs = 0; + for (ii = 0; ii < PHY_PORTS; ++ii) { + /* Set correct default for SKIP align */ + brcmusb_usb_mdio_write(ctrl_base, 0x1f, (0x8060 + ofs), + MDIO_USB3); + val = brcmusb_usb_mdio_read(ctrl_base, 0x01, MDIO_USB3) | 0x200; + brcmusb_usb_mdio_write(ctrl_base, 0x01, val, MDIO_USB3); + ofs = PHY_PORT_SELECT_1; + } +} + +static void brcmusb_usb3_unfreeze_aeq(void __iomem *ctrl_base) +{ + u32 val, ofs; + int ii; + + ofs = 0; + for (ii = 0; ii < PHY_PORTS; ++ii) { + /* Let EQ freeze after TSEQ */ + brcmusb_usb_mdio_write(ctrl_base, 0x1f, (0x80e0 + ofs), + MDIO_USB3); + val = brcmusb_usb_mdio_read(ctrl_base, 0x01, MDIO_USB3); + val &= ~0x0008; + brcmusb_usb_mdio_write(ctrl_base, 0x01, val, MDIO_USB3); + ofs = PHY_PORT_SELECT_1; + } +} + +static void brcmusb_usb3_pll_54mhz(struct brcm_usb_init_params *params) +{ + u32 ofs; + int ii; + void __iomem *ctrl_base = params->ctrl_regs; + + /* + * On newer B53 based SoC's, the reference clock for the + * 3.0 PLL has been changed from 50MHz to 54MHz so the + * PLL needs to be reprogrammed. + * See SWLINUX-4006. + * + * On the 7364C0, the reference clock for the + * 3.0 PLL has been changed from 50MHz to 54MHz to + * work around a MOCA issue. + * See SWLINUX-4169. + */ + switch (params->selected_family) { + case BRCM_FAMILY_3390A0: + case BRCM_FAMILY_7250B0: + case BRCM_FAMILY_7366C0: + case BRCM_FAMILY_74371A0: + case BRCM_FAMILY_7439B0: + case BRCM_FAMILY_7445D0: + case BRCM_FAMILY_7260A0: + return; + case BRCM_FAMILY_7364A0: + if (BRCM_REV(params->family_id) < 0x20) + return; + break; + } + + /* set USB 3.0 PLL to accept 54Mhz reference clock */ + USB_CTRL_UNSET(ctrl_base, USB30_CTL1, PHY3_PLL_SEQ_START); + + brcmusb_usb_mdio_write(ctrl_base, 0x1f, 0x8000, MDIO_USB3); + brcmusb_usb_mdio_write(ctrl_base, 0x10, 0x5784, MDIO_USB3); + brcmusb_usb_mdio_write(ctrl_base, 0x11, 0x01d0, MDIO_USB3); + brcmusb_usb_mdio_write(ctrl_base, 0x12, 0x1DE8, MDIO_USB3); + brcmusb_usb_mdio_write(ctrl_base, 0x13, 0xAA80, MDIO_USB3); + brcmusb_usb_mdio_write(ctrl_base, 0x14, 0x8826, MDIO_USB3); + brcmusb_usb_mdio_write(ctrl_base, 0x15, 0x0044, MDIO_USB3); + brcmusb_usb_mdio_write(ctrl_base, 0x16, 0x8000, MDIO_USB3); + brcmusb_usb_mdio_write(ctrl_base, 0x17, 0x0851, MDIO_USB3); + brcmusb_usb_mdio_write(ctrl_base, 0x18, 0x0000, MDIO_USB3); + + /* both ports */ + ofs = 0; + for (ii = 0; ii < PHY_PORTS; ++ii) { + brcmusb_usb_mdio_write(ctrl_base, 0x1f, (0x8040 + ofs), + MDIO_USB3); + brcmusb_usb_mdio_write(ctrl_base, 0x03, 0x0090, MDIO_USB3); + brcmusb_usb_mdio_write(ctrl_base, 0x04, 0x0134, MDIO_USB3); + brcmusb_usb_mdio_write(ctrl_base, 0x1f, (0x8020 + ofs), + MDIO_USB3); + brcmusb_usb_mdio_write(ctrl_base, 0x01, 0x00e2, MDIO_USB3); + ofs = PHY_PORT_SELECT_1; + } + + /* restart PLL sequence */ + USB_CTRL_SET(ctrl_base, USB30_CTL1, PHY3_PLL_SEQ_START); + /* Give PLL enough time to lock */ + usleep_range(1000, 2000); +} + +static void brcmusb_usb3_ssc_enable(void __iomem *ctrl_base) +{ + u32 val; + + /* Enable USB 3.0 TX spread spectrum */ + brcmusb_usb_mdio_write(ctrl_base, 0x1f, 0x8040, MDIO_USB3); + val = brcmusb_usb_mdio_read(ctrl_base, 0x01, MDIO_USB3) | 0xf; + brcmusb_usb_mdio_write(ctrl_base, 0x01, val, MDIO_USB3); + + /* Currently, USB 3.0 SSC is enabled via port 0 MDIO registers, + * which should have been adequate. However, due to a bug in the + * USB 3.0 PHY, it must be enabled via both ports (HWUSB3DVT-26). + */ + brcmusb_usb_mdio_write(ctrl_base, 0x1f, 0x9040, MDIO_USB3); + val = brcmusb_usb_mdio_read(ctrl_base, 0x01, MDIO_USB3) | 0xf; + brcmusb_usb_mdio_write(ctrl_base, 0x01, val, MDIO_USB3); +} + +static void brcmusb_usb3_phy_workarounds(struct brcm_usb_init_params *params) +{ + void __iomem *ctrl_base = params->ctrl_regs; + + brcmusb_usb3_pll_fix(ctrl_base); + brcmusb_usb3_pll_54mhz(params); + brcmusb_usb3_ssc_enable(ctrl_base); + brcmusb_usb3_enable_pipe_reset(ctrl_base); + brcmusb_usb3_enable_sigdet(ctrl_base); + brcmusb_usb3_enable_skip_align(ctrl_base); + brcmusb_usb3_unfreeze_aeq(ctrl_base); +} + +static void brcmusb_memc_fix(struct brcm_usb_init_params *params) +{ + u32 prid; + + if (params->selected_family != BRCM_FAMILY_7445D0) + return; + /* + * This is a workaround for HW7445-1869 where a DMA write ends up + * doing a read pre-fetch after the end of the DMA buffer. This + * causes a problem when the DMA buffer is at the end of physical + * memory, causing the pre-fetch read to access non-existent memory, + * and the chip bondout has MEMC2 disabled. When the pre-fetch read + * tries to use the disabled MEMC2, it hangs the bus. The workaround + * is to disable MEMC2 access in the usb controller which avoids + * the hang. + */ + + prid = params->product_id & 0xfffff000; + switch (prid) { + case 0x72520000: + case 0x74480000: + case 0x74490000: + case 0x07252000: + case 0x07448000: + case 0x07449000: + USB_CTRL_UNSET_FAMILY(params, SETUP, SCB2_EN); + } +} + +static void brcmusb_usb3_otp_fix(struct brcm_usb_init_params *params) +{ + void __iomem *xhci_ec_base = params->xhci_ec_regs; + u32 val; + + if (params->family_id != 0x74371000 || xhci_ec_base == 0) + return; + brcmusb_writel(0xa20c, USB_XHCI_EC_REG(xhci_ec_base, IRAADR)); + val = brcmusb_readl(USB_XHCI_EC_REG(xhci_ec_base, IRADAT)); + + /* set cfg_pick_ss_lock */ + val |= (1 << 27); + brcmusb_writel(val, USB_XHCI_EC_REG(xhci_ec_base, IRADAT)); + + /* Reset USB 3.0 PHY for workaround to take effect */ + USB_CTRL_UNSET(params->ctrl_regs, USB30_CTL1, PHY3_RESETB); + USB_CTRL_SET(params->ctrl_regs, USB30_CTL1, PHY3_RESETB); +} + +static void brcmusb_xhci_soft_reset(struct brcm_usb_init_params *params, + int on_off) +{ + /* Assert reset */ + if (on_off) { + if (USB_CTRL_MASK_FAMILY(params, USB_PM, XHC_SOFT_RESETB)) + USB_CTRL_UNSET_FAMILY(params, USB_PM, XHC_SOFT_RESETB); + else + USB_CTRL_UNSET_FAMILY(params, + USB30_CTL1, XHC_SOFT_RESETB); + } else { /* De-assert reset */ + if (USB_CTRL_MASK_FAMILY(params, USB_PM, XHC_SOFT_RESETB)) + USB_CTRL_SET_FAMILY(params, USB_PM, XHC_SOFT_RESETB); + else + USB_CTRL_SET_FAMILY(params, USB30_CTL1, + XHC_SOFT_RESETB); + } +} + +/* + * Return the best map table family. The order is: + * - exact match of chip and major rev + * - exact match of chip and closest older major rev + * - default chip/rev. + * NOTE: The minor rev is always ignored. + */ +static enum brcm_family_type brcmusb_get_family_type( + struct brcm_usb_init_params *params) +{ + int last_type = -1; + u32 last_family = 0; + u32 family_no_major; + unsigned int x; + u32 family; + + family = params->family_id & 0xfffffff0; + family_no_major = params->family_id & 0xffffff00; + for (x = 0; id_to_type_table[x].id; x++) { + if (family == id_to_type_table[x].id) + return id_to_type_table[x].type; + if (family_no_major == (id_to_type_table[x].id & 0xffffff00)) + if (family > id_to_type_table[x].id && + last_family < id_to_type_table[x].id) { + last_family = id_to_type_table[x].id; + last_type = id_to_type_table[x].type; + } + } + + /* If no match, return the default family */ + if (last_type == -1) + return id_to_type_table[x].type; + return last_type; +} + +void brcm_usb_init_ipp(struct brcm_usb_init_params *params) +{ + void __iomem *ctrl = params->ctrl_regs; + u32 reg; + u32 orig_reg; + + /* Starting with the 7445d0, there are no longer separate 3.0 + * versions of IOC and IPP. + */ + if (USB_CTRL_MASK_FAMILY(params, USB30_CTL1, USB3_IOC)) { + if (params->ioc) + USB_CTRL_SET_FAMILY(params, USB30_CTL1, USB3_IOC); + if (params->ipp == 1) + USB_CTRL_SET_FAMILY(params, USB30_CTL1, USB3_IPP); + } + + reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP)); + orig_reg = reg; + if (USB_CTRL_MASK_FAMILY(params, SETUP, STRAP_CC_DRD_MODE_ENABLE_SEL)) + /* Never use the strap, it's going away. */ + reg &= ~(USB_CTRL_MASK_FAMILY(params, + SETUP, + STRAP_CC_DRD_MODE_ENABLE_SEL)); + if (USB_CTRL_MASK_FAMILY(params, SETUP, STRAP_IPP_SEL)) + if (params->ipp != 2) + /* override ipp strap pin (if it exits) */ + reg &= ~(USB_CTRL_MASK_FAMILY(params, SETUP, + STRAP_IPP_SEL)); + + /* Override the default OC and PP polarity */ + reg &= ~(USB_CTRL_MASK(SETUP, IPP) | USB_CTRL_MASK(SETUP, IOC)); + if (params->ioc) + reg |= USB_CTRL_MASK(SETUP, IOC); + if (params->ipp == 1 && ((reg & USB_CTRL_MASK(SETUP, IPP)) == 0)) + reg |= USB_CTRL_MASK(SETUP, IPP); + brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP)); + + /* + * If we're changing IPP, make sure power is off long enough + * to turn off any connected devices. + */ + if (reg != orig_reg) + msleep(50); +} + +int brcm_usb_init_get_dual_select(struct brcm_usb_init_params *params) +{ + void __iomem *ctrl = params->ctrl_regs; + u32 reg = 0; + + if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) { + reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); + reg &= USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, + PORT_MODE); + } + return reg; +} + +void brcm_usb_init_set_dual_select(struct brcm_usb_init_params *params, + int mode) +{ + void __iomem *ctrl = params->ctrl_regs; + u32 reg; + + if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) { + reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); + reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, + PORT_MODE); + reg |= mode; + brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); + } +} + +void brcm_usb_init_common(struct brcm_usb_init_params *params) +{ + u32 reg; + void __iomem *ctrl = params->ctrl_regs; + + /* Take USB out of power down */ + if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN)) { + USB_CTRL_UNSET_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN); + /* 1 millisecond - for USB clocks to settle down */ + usleep_range(1000, 2000); + } + + if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB_PWRDN)) { + USB_CTRL_UNSET_FAMILY(params, USB_PM, USB_PWRDN); + /* 1 millisecond - for USB clocks to settle down */ + usleep_range(1000, 2000); + } + + if (params->selected_family != BRCM_FAMILY_74371A0 && + (BRCM_ID(params->family_id) != 0x7364)) + /* + * HW7439-637: 7439a0 and its derivatives do not have large + * enough descriptor storage for this. + */ + USB_CTRL_SET_FAMILY(params, SETUP, SS_EHCI64BIT_EN); + + /* Block auto PLL suspend by USB2 PHY (Sasi) */ + USB_CTRL_SET(ctrl, PLL_CTL, PLL_SUSPEND_EN); + + reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP)); + if (params->selected_family == BRCM_FAMILY_7364A0) + /* Suppress overcurrent indication from USB30 ports for A0 */ + reg |= USB_CTRL_MASK_FAMILY(params, SETUP, OC3_DISABLE); + + brcmusb_usb_phy_ldo_fix(ctrl); + brcmusb_usb2_eye_fix(ctrl); + + /* + * Make sure the the second and third memory controller + * interfaces are enabled if they exist. + */ + if (USB_CTRL_MASK_FAMILY(params, SETUP, SCB1_EN)) + reg |= USB_CTRL_MASK_FAMILY(params, SETUP, SCB1_EN); + if (USB_CTRL_MASK_FAMILY(params, SETUP, SCB2_EN)) + reg |= USB_CTRL_MASK_FAMILY(params, SETUP, SCB2_EN); + brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP)); + + brcmusb_memc_fix(params); + + if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) { + reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); + reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, + PORT_MODE); + reg |= params->mode; + brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); + } + if (USB_CTRL_MASK_FAMILY(params, USB_PM, BDC_SOFT_RESETB)) { + switch (params->mode) { + case USB_CTLR_MODE_HOST: + USB_CTRL_UNSET_FAMILY(params, USB_PM, BDC_SOFT_RESETB); + break; + default: + USB_CTRL_SET_FAMILY(params, USB_PM, BDC_SOFT_RESETB); + break; + } + } + if (USB_CTRL_MASK_FAMILY(params, SETUP, CC_DRD_MODE_ENABLE)) { + if (params->mode == USB_CTLR_MODE_TYPEC_PD) + USB_CTRL_SET_FAMILY(params, SETUP, CC_DRD_MODE_ENABLE); + else + USB_CTRL_UNSET_FAMILY(params, SETUP, + CC_DRD_MODE_ENABLE); + } +} + +void brcm_usb_init_eohci(struct brcm_usb_init_params *params) +{ + u32 reg; + void __iomem *ctrl = params->ctrl_regs; + + if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB20_HC_RESETB)) + USB_CTRL_SET_FAMILY(params, USB_PM, USB20_HC_RESETB); + + if (params->selected_family == BRCM_FAMILY_7366C0) + /* + * Don't enable this so the memory controller doesn't read + * into memory holes. NOTE: This bit is low true on 7366C0. + */ + USB_CTRL_SET_FAMILY(params, EBRIDGE, ESTOP_SCB_REQ); + + /* Setup the endian bits */ + reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP)); + reg &= ~USB_CTRL_SETUP_ENDIAN_BITS; + reg |= USB_CTRL_MASK_FAMILY(params, SETUP, ENDIAN); + brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP)); +} + +void brcm_usb_init_xhci(struct brcm_usb_init_params *params) +{ + void __iomem *ctrl = params->ctrl_regs; + + if (BRCM_ID(params->family_id) == 0x7366) { + /* + * The PHY3_SOFT_RESETB bits default to the wrong state. + */ + USB_CTRL_SET(ctrl, USB30_PCTL, PHY3_SOFT_RESETB); + USB_CTRL_SET(ctrl, USB30_PCTL, PHY3_SOFT_RESETB_P1); + } + + /* + * Kick start USB3 PHY + * Make sure it's low to insure a rising edge. + */ + USB_CTRL_UNSET(ctrl, USB30_CTL1, PHY3_PLL_SEQ_START); + USB_CTRL_SET(ctrl, USB30_CTL1, PHY3_PLL_SEQ_START); + + brcmusb_usb3_phy_workarounds(params); + brcmusb_xhci_soft_reset(params, 0); + brcmusb_usb3_otp_fix(params); +} + +void brcm_usb_uninit_common(struct brcm_usb_init_params *params) +{ + if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB_PWRDN)) + USB_CTRL_SET_FAMILY(params, USB_PM, USB_PWRDN); + + if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN)) + USB_CTRL_SET_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN); +} + +void brcm_usb_uninit_eohci(struct brcm_usb_init_params *params) +{ + if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB20_HC_RESETB)) + USB_CTRL_UNSET_FAMILY(params, USB_PM, USB20_HC_RESETB); +} + +void brcm_usb_uninit_xhci(struct brcm_usb_init_params *params) +{ + brcmusb_xhci_soft_reset(params, 1); +} + +void brcm_usb_set_family_map(struct brcm_usb_init_params *params) +{ + int fam; + + fam = brcmusb_get_family_type(params); + params->selected_family = fam; + params->usb_reg_bits_map = + &usb_reg_bits_map_table[fam][0]; + params->family_name = family_names[fam]; +} diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h new file mode 100644 index 000000000000..bb77b863885e --- /dev/null +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014-2017 Broadcom + * + * 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. + */ + +#ifndef _USB_BRCM_COMMON_INIT_H +#define _USB_BRCM_COMMON_INIT_H + +#define USB_CTLR_MODE_HOST 0 +#define USB_CTLR_MODE_DEVICE 1 +#define USB_CTLR_MODE_DRD 2 +#define USB_CTLR_MODE_TYPEC_PD 3 + +struct brcm_usb_init_params; + +struct brcm_usb_init_params { + void __iomem *ctrl_regs; + void __iomem *xhci_ec_regs; + int ioc; + int ipp; + int mode; + u32 family_id; + u32 product_id; + int selected_family; + const char *family_name; + const u32 *usb_reg_bits_map; +}; + +void brcm_usb_set_family_map(struct brcm_usb_init_params *params); +int brcm_usb_init_get_dual_select(struct brcm_usb_init_params *params); +void brcm_usb_init_set_dual_select(struct brcm_usb_init_params *params, + int mode); + +void brcm_usb_init_ipp(struct brcm_usb_init_params *ini); +void brcm_usb_init_common(struct brcm_usb_init_params *ini); +void brcm_usb_init_eohci(struct brcm_usb_init_params *ini); +void brcm_usb_init_xhci(struct brcm_usb_init_params *ini); +void brcm_usb_uninit_common(struct brcm_usb_init_params *ini); +void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini); +void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini); + +#endif /* _USB_BRCM_COMMON_INIT_H */ diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c new file mode 100644 index 000000000000..8ffd0f14f809 --- /dev/null +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -0,0 +1,374 @@ +/* + * phy-brcm-usb.c - Broadcom USB Phy Driver + * + * Copyright (C) 2015-2017 Broadcom + * + * 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 +#include + +#include "phy-brcm-usb-init.h" + +enum brcm_usb_phy_id { + BRCM_USB_PHY_2_0 = 0, + BRCM_USB_PHY_3_0, + BRCM_USB_PHY_ID_MAX +}; + +struct value_to_name_map { + int value; + const char *name; +}; + +static struct value_to_name_map brcm_dr_mode_to_name[] = { + { USB_CTLR_MODE_HOST, "host" }, + { USB_CTLR_MODE_DEVICE, "peripheral" }, + { USB_CTLR_MODE_DRD, "drd" }, + { USB_CTLR_MODE_TYPEC_PD, "typec-pd" } +}; + +struct brcm_usb_phy { + struct phy *phy; + unsigned int id; + bool inited; +}; + +struct brcm_usb_phy_data { + struct brcm_usb_init_params ini; + bool has_eohci; + bool has_xhci; + struct clk *usb_20_clk; + struct clk *usb_30_clk; + struct mutex mutex; /* serialize phy init */ + int init_count; + struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX]; +}; + +static int brcm_usb_phy_init(struct phy *gphy) +{ + struct brcm_usb_phy *phy = phy_get_drvdata(gphy); + struct brcm_usb_phy_data *priv = + container_of(phy, struct brcm_usb_phy_data, phys[phy->id]); + + /* + * Use a lock to make sure a second caller waits until + * the base phy is inited before using it. + */ + mutex_lock(&priv->mutex); + if (priv->init_count++ == 0) { + clk_enable(priv->usb_20_clk); + clk_enable(priv->usb_30_clk); + brcm_usb_init_common(&priv->ini); + } + mutex_unlock(&priv->mutex); + if (phy->id == BRCM_USB_PHY_2_0) + brcm_usb_init_eohci(&priv->ini); + else if (phy->id == BRCM_USB_PHY_3_0) + brcm_usb_init_xhci(&priv->ini); + phy->inited = true; + dev_dbg(&gphy->dev, "INIT, id: %d, total: %d\n", phy->id, + priv->init_count); + + return 0; +} + +static int brcm_usb_phy_exit(struct phy *gphy) +{ + struct brcm_usb_phy *phy = phy_get_drvdata(gphy); + struct brcm_usb_phy_data *priv = + container_of(phy, struct brcm_usb_phy_data, phys[phy->id]); + + dev_dbg(&gphy->dev, "EXIT\n"); + if (phy->id == BRCM_USB_PHY_2_0) + brcm_usb_uninit_eohci(&priv->ini); + if (phy->id == BRCM_USB_PHY_3_0) + brcm_usb_uninit_xhci(&priv->ini); + + /* If both xhci and eohci are gone, reset everything else */ + mutex_lock(&priv->mutex); + if (--priv->init_count == 0) { + brcm_usb_uninit_common(&priv->ini); + clk_disable(priv->usb_20_clk); + clk_disable(priv->usb_30_clk); + } + mutex_unlock(&priv->mutex); + phy->inited = false; + return 0; +} + +static struct phy_ops brcm_usb_phy_ops = { + .init = brcm_usb_phy_init, + .exit = brcm_usb_phy_exit, + .owner = THIS_MODULE, +}; + +static struct phy *brcm_usb_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct brcm_usb_phy_data *data = dev_get_drvdata(dev); + + /* + * values 0 and 1 are for backward compatibility with + * device tree nodes from older bootloaders. + */ + switch (args->args[0]) { + case 0: + case PHY_TYPE_USB2: + if (data->phys[BRCM_USB_PHY_2_0].phy) + return data->phys[BRCM_USB_PHY_2_0].phy; + dev_warn(dev, "Error, 2.0 Phy not found\n"); + break; + case 1: + case PHY_TYPE_USB3: + if (data->phys[BRCM_USB_PHY_3_0].phy) + return data->phys[BRCM_USB_PHY_3_0].phy; + dev_warn(dev, "Error, 3.0 Phy not found\n"); + break; + } + return ERR_PTR(-ENODEV); +} + +static int name_to_value(struct value_to_name_map *table, int count, + const char *name, int *value) +{ + int x; + + *value = 0; + for (x = 0; x < count; x++) { + if (sysfs_streq(name, table[x].name)) { + *value = x; + return 0; + } + } + return -EINVAL; +} + +static int brcm_usb_phy_dvr_init(struct device *dev, + struct brcm_usb_phy_data *priv, + struct device_node *dn) +{ + struct phy *gphy; + int err; + + priv->usb_20_clk = of_clk_get_by_name(dn, "sw_usb"); + if (IS_ERR(priv->usb_20_clk)) { + dev_info(dev, "Clock not found in Device Tree\n"); + priv->usb_20_clk = NULL; + } + err = clk_prepare_enable(priv->usb_20_clk); + if (err) + return err; + + if (priv->has_eohci) { + gphy = devm_phy_create(dev, NULL, &brcm_usb_phy_ops); + if (IS_ERR(gphy)) { + dev_err(dev, "failed to create EHCI/OHCI PHY\n"); + return PTR_ERR(gphy); + } + priv->phys[BRCM_USB_PHY_2_0].phy = gphy; + priv->phys[BRCM_USB_PHY_2_0].id = BRCM_USB_PHY_2_0; + phy_set_drvdata(gphy, &priv->phys[BRCM_USB_PHY_2_0]); + } + + if (priv->has_xhci) { + gphy = devm_phy_create(dev, NULL, &brcm_usb_phy_ops); + if (IS_ERR(gphy)) { + dev_err(dev, "failed to create XHCI PHY\n"); + return PTR_ERR(gphy); + } + priv->phys[BRCM_USB_PHY_3_0].phy = gphy; + priv->phys[BRCM_USB_PHY_3_0].id = BRCM_USB_PHY_3_0; + phy_set_drvdata(gphy, &priv->phys[BRCM_USB_PHY_3_0]); + + priv->usb_30_clk = of_clk_get_by_name(dn, "sw_usb3"); + if (IS_ERR(priv->usb_30_clk)) { + dev_info(dev, + "USB3.0 clock not found in Device Tree\n"); + priv->usb_30_clk = NULL; + } + err = clk_prepare_enable(priv->usb_30_clk); + if (err) + return err; + } + return 0; +} + +static int brcm_usb_phy_probe(struct platform_device *pdev) +{ + struct resource *res; + struct device *dev = &pdev->dev; + struct brcm_usb_phy_data *priv; + struct phy_provider *phy_provider; + struct device_node *dn = pdev->dev.of_node; + int err; + const char *mode; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + priv->ini.family_id = brcmstb_get_family_id(); + priv->ini.product_id = brcmstb_get_product_id(); + brcm_usb_set_family_map(&priv->ini); + dev_dbg(dev, "Best mapping table is for %s\n", + priv->ini.family_name); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "can't get USB_CTRL base address\n"); + return -EINVAL; + } + priv->ini.ctrl_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->ini.ctrl_regs)) { + dev_err(dev, "can't map CTRL register space\n"); + return -EINVAL; + } + + /* The XHCI EC registers are optional */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + priv->ini.xhci_ec_regs = + devm_ioremap_resource(dev, res); + if (IS_ERR(priv->ini.xhci_ec_regs)) { + dev_err(dev, "can't map XHCI EC register space\n"); + return -EINVAL; + } + } + + of_property_read_u32(dn, "brcm,ipp", &priv->ini.ipp); + of_property_read_u32(dn, "brcm,ioc", &priv->ini.ioc); + + priv->ini.mode = USB_CTLR_MODE_HOST; + err = of_property_read_string(dn, "dr_mode", &mode); + if (err == 0) { + name_to_value(&brcm_dr_mode_to_name[0], + ARRAY_SIZE(brcm_dr_mode_to_name), + mode, &priv->ini.mode); + } + if (of_property_read_bool(dn, "brcm,has_xhci")) + priv->has_xhci = true; + if (of_property_read_bool(dn, "brcm,has_eohci")) + priv->has_eohci = true; + + err = brcm_usb_phy_dvr_init(dev, priv, dn); + if (err) + return err; + + mutex_init(&priv->mutex); + + /* make sure invert settings are correct */ + brcm_usb_init_ipp(&priv->ini); + + /* start with everything off */ + if (priv->has_xhci) + brcm_usb_uninit_xhci(&priv->ini); + if (priv->has_eohci) + brcm_usb_uninit_eohci(&priv->ini); + brcm_usb_uninit_common(&priv->ini); + clk_disable(priv->usb_20_clk); + clk_disable(priv->usb_30_clk); + + phy_provider = devm_of_phy_provider_register(dev, brcm_usb_phy_xlate); + if (IS_ERR(phy_provider)) + return PTR_ERR(phy_provider); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int brcm_usb_phy_suspend(struct device *dev) +{ + struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); + + if (priv->init_count) { + clk_disable(priv->usb_20_clk); + clk_disable(priv->usb_30_clk); + } + return 0; +} + +static int brcm_usb_phy_resume(struct device *dev) +{ + struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); + + clk_enable(priv->usb_20_clk); + clk_enable(priv->usb_30_clk); + brcm_usb_init_ipp(&priv->ini); + + /* + * Initialize anything that was previously initialized. + * Uninitialize anything that wasn't previously initialized. + */ + if (priv->init_count) { + brcm_usb_init_common(&priv->ini); + if (priv->phys[BRCM_USB_PHY_2_0].inited) { + brcm_usb_init_eohci(&priv->ini); + } else if (priv->has_eohci) { + brcm_usb_uninit_eohci(&priv->ini); + clk_disable(priv->usb_20_clk); + } + if (priv->phys[BRCM_USB_PHY_3_0].inited) { + brcm_usb_init_xhci(&priv->ini); + } else if (priv->has_xhci) { + brcm_usb_uninit_xhci(&priv->ini); + clk_disable(priv->usb_30_clk); + } + } else { + if (priv->has_xhci) + brcm_usb_uninit_xhci(&priv->ini); + if (priv->has_eohci) + brcm_usb_uninit_eohci(&priv->ini); + brcm_usb_uninit_common(&priv->ini); + clk_disable(priv->usb_20_clk); + clk_disable(priv->usb_30_clk); + } + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops brcm_usb_phy_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(brcm_usb_phy_suspend, brcm_usb_phy_resume) +}; + +static const struct of_device_id brcm_usb_dt_ids[] = { + { .compatible = "brcm,brcmstb-usb-phy" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, brcm_usb_dt_ids); + +static struct platform_driver brcm_usb_driver = { + .probe = brcm_usb_phy_probe, + .driver = { + .name = "brcmstb-usb-phy", + .owner = THIS_MODULE, + .pm = &brcm_usb_phy_pm_ops, + .of_match_table = brcm_usb_dt_ids, + }, +}; + +module_platform_driver(brcm_usb_driver); + +MODULE_ALIAS("platform:brcmstb-usb-phy"); +MODULE_AUTHOR("Al Cooper "); +MODULE_DESCRIPTION("BRCM USB PHY driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 415060b21f318e009d865b4bcbf8f220ebc36964 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 22 Sep 2017 15:34:02 -0400 Subject: phy: usb: phy-brcm-usb: Add ability to force DRD mode to host or device When the usb phy device mode is set to "drd", the USB port will switch between device and host modes depending on what's plugged into the port. Customers have asked for the ability to force host or device mode from software. This commit adds sysfs entries to the phy device that allow this. The sysfs for the phy device can be found at: /sys/bus/platform/drivers/brcmstb-usb-phy/*.usb-phy The following sysfs entries were added: - "dr_mode" (RO) - The current phy "dr_mode" setting. It will be set to one of the following values: - "host" - host mode - "peripheral " - device mode - "drd" - switch between device and host mode based on installed device - "typec-pd" - device/host mode is controller by the USB Type-C PD protocol. If "dr_mode" is "drd" - "drd_select" (RW) - It will be set to one of the following values: - "host" - force host mode - "device" - force device mode - "auto" - allow normal auto selection of host/device based on inserted USB device Signed-off-by: Al Cooper Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-usb.c | 85 +++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) (limited to 'drivers/phy') diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index 8ffd0f14f809..195b98139e5f 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -27,6 +27,8 @@ #include "phy-brcm-usb-init.h" +static DEFINE_MUTEX(sysfs_lock); + enum brcm_usb_phy_id { BRCM_USB_PHY_2_0 = 0, BRCM_USB_PHY_3_0, @@ -45,6 +47,12 @@ static struct value_to_name_map brcm_dr_mode_to_name[] = { { USB_CTLR_MODE_TYPEC_PD, "typec-pd" } }; +static struct value_to_name_map brcm_dual_mode_to_name[] = { + { 0, "host" }, + { 1, "device" }, + { 2, "auto" }, +}; + struct brcm_usb_phy { struct phy *phy; unsigned int id; @@ -161,6 +169,73 @@ static int name_to_value(struct value_to_name_map *table, int count, return -EINVAL; } +static const char *value_to_name(struct value_to_name_map *table, int count, + int value) +{ + if (value >= count) + return "unknown"; + return table[value].name; +} + +static ssize_t dr_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", + value_to_name(&brcm_dr_mode_to_name[0], + ARRAY_SIZE(brcm_dr_mode_to_name), + priv->ini.mode)); +} +static DEVICE_ATTR_RO(dr_mode); + +static ssize_t dual_select_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); + int value; + int res; + + mutex_lock(&sysfs_lock); + res = name_to_value(&brcm_dual_mode_to_name[0], + ARRAY_SIZE(brcm_dual_mode_to_name), buf, &value); + if (!res) { + brcm_usb_init_set_dual_select(&priv->ini, value); + res = len; + } + mutex_unlock(&sysfs_lock); + return res; +} + +static ssize_t dual_select_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); + int value; + + mutex_lock(&sysfs_lock); + value = brcm_usb_init_get_dual_select(&priv->ini); + mutex_unlock(&sysfs_lock); + return sprintf(buf, "%s\n", + value_to_name(&brcm_dual_mode_to_name[0], + ARRAY_SIZE(brcm_dual_mode_to_name), + value)); +} +static DEVICE_ATTR_RW(dual_select); + +static struct attribute *brcm_usb_phy_attrs[] = { + &dev_attr_dr_mode.attr, + &dev_attr_dual_select.attr, + NULL +}; + +static const struct attribute_group brcm_usb_phy_group = { + .attrs = brcm_usb_phy_attrs, +}; + static int brcm_usb_phy_dvr_init(struct device *dev, struct brcm_usb_phy_data *priv, struct device_node *dn) @@ -277,6 +352,16 @@ static int brcm_usb_phy_probe(struct platform_device *pdev) /* make sure invert settings are correct */ brcm_usb_init_ipp(&priv->ini); + /* + * Create sysfs entries for mode. + * Remove "dual_select" attribute if not in dual mode + */ + if (priv->ini.mode != USB_CTLR_MODE_DRD) + brcm_usb_phy_attrs[1] = NULL; + err = sysfs_create_group(&dev->kobj, &brcm_usb_phy_group); + if (err) + dev_warn(dev, "Error creating sysfs attributes\n"); + /* start with everything off */ if (priv->has_xhci) brcm_usb_uninit_xhci(&priv->ini); -- cgit v1.2.3 From 176aa36012135d172394a928a03fb03dfecd83f9 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 21 Sep 2017 12:11:24 +0900 Subject: extcon: Split out extcon header file for consumer and provider device The extcon has two type of extcon devices as following. - 'extcon provider deivce' adds new extcon device and detect the state/properties of external connector. Also, it notifies the state/properties to the extcon consumer device. - 'extcon consumer device' gets the change state/properties from extcon provider device. Prior to that, include/linux/extcon.h contains all exported API for both provider and consumer device driver. To clarify the meaning of header file and to remove the wrong use-case on consumer device, this patch separates into extcon.h and extcon-provider.h. [Description for include/linux/{extcon.h|extcon-provider.h}] - extcon.h includes the extcon API and data structure for extcon consumer device driver. This header file contains the following APIs: : Register/unregister the notifier to catch the change of extcon device : Get the extcon device instance : Get the extcon device name : Get the state of each external connector : Get the property value of each external connector : Get the property capability of each external connector - extcon-provider.h includes the extcon API and data structure for extcon provider device driver. This header file contains the following APIs: : Include 'include/linux/extcon.h' : Allocate the memory for extcon device instance : Register/unregister extcon device : Set the state of each external connector : Set the property value of each external connector : Set the property capability of each external connector Signed-off-by: Chanwoo Choi Acked-by: Sebastian Reichel Acked-by: Chen-Yu Tsai Acked-by: Charles Keepax Acked-by: Lee Jones Acked-by: Felipe Balbi Acked-by: Yoshihiro Shimoda Acked-by: Kishon Vijay Abraham I --- drivers/phy/allwinner/phy-sun4i-usb.c | 2 +- drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c | 2 +- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 2 +- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/phy') diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 1161e11fb3cf..ef34f97f214b 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c index d099a0c8cee5..7ceea5ae2704 100644 --- a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c +++ b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c @@ -12,7 +12,7 @@ */ #include -#include +#include #include #include #include diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 54c34298a000..b33e2994ccce 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -12,7 +12,7 @@ * published by the Free Software Foundation. */ -#include +#include #include #include #include diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index ee7ce5ee53f9..5049dac79bd0 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.3 From 30dbc0415f7c6e6d4fa9cdc8098c27fd8cf74f82 Mon Sep 17 00:00:00 2001 From: Antoine Tenart Date: Mon, 18 Sep 2017 10:04:21 +0200 Subject: phy: mvebu-cp110-comphy: remove unused member in private struct The 'modes' member of the mvebu_comphy_priv structure is not used. Remove it. Signed-off-by: Antoine Tenart Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/marvell/phy-mvebu-cp110-comphy.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/phy') diff --git a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c index 89c887ea5557..a0d522154cdf 100644 --- a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c +++ b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c @@ -154,7 +154,6 @@ struct mvebu_comphy_priv { void __iomem *base; struct regmap *regmap; struct device *dev; - int modes[MVEBU_COMPHY_LANES]; }; struct mvebu_comphy_lane { -- cgit v1.2.3 From 5954a10e8e12ddffd54bfb6b3365aec2c31aa615 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 21 Sep 2017 18:31:49 +0800 Subject: phy: phy-mtk-tphy: add set_mode callback This is used to force PHY with USB OTG function to enter a specific mode, and override OTG IDPIN(or IDDIG) signal. Signed-off-by: Chunfeng Yun Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/mediatek/phy-mtk-tphy.c | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'drivers/phy') diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c index 721a2a1c97ef..402385f2562a 100644 --- a/drivers/phy/mediatek/phy-mtk-tphy.c +++ b/drivers/phy/mediatek/phy-mtk-tphy.c @@ -96,9 +96,11 @@ #define U3P_U2PHYDTM1 0x06C #define P2C_RG_UART_EN BIT(16) +#define P2C_FORCE_IDDIG BIT(9) #define P2C_RG_VBUSVALID BIT(5) #define P2C_RG_SESSEND BIT(4) #define P2C_RG_AVALID BIT(2) +#define P2C_RG_IDDIG BIT(1) #define U3P_U3_CHIP_GPIO_CTLD 0x0c #define P3C_REG_IP_SW_RST BIT(31) @@ -585,6 +587,31 @@ static void u2_phy_instance_exit(struct mtk_tphy *tphy, } } +static void u2_phy_instance_set_mode(struct mtk_tphy *tphy, + struct mtk_phy_instance *instance, + enum phy_mode mode) +{ + struct u2phy_banks *u2_banks = &instance->u2_banks; + u32 tmp; + + tmp = readl(u2_banks->com + U3P_U2PHYDTM1); + switch (mode) { + case PHY_MODE_USB_DEVICE: + tmp |= P2C_FORCE_IDDIG | P2C_RG_IDDIG; + break; + case PHY_MODE_USB_HOST: + tmp |= P2C_FORCE_IDDIG; + tmp &= ~P2C_RG_IDDIG; + break; + case PHY_MODE_USB_OTG: + tmp &= ~(P2C_FORCE_IDDIG | P2C_RG_IDDIG); + break; + default: + return; + } + writel(tmp, u2_banks->com + U3P_U2PHYDTM1); +} + static void pcie_phy_instance_init(struct mtk_tphy *tphy, struct mtk_phy_instance *instance) { @@ -881,6 +908,17 @@ static int mtk_phy_exit(struct phy *phy) return 0; } +static int mtk_phy_set_mode(struct phy *phy, enum phy_mode mode) +{ + struct mtk_phy_instance *instance = phy_get_drvdata(phy); + struct mtk_tphy *tphy = dev_get_drvdata(phy->dev.parent); + + if (instance->type == PHY_TYPE_USB2) + u2_phy_instance_set_mode(tphy, instance, mode); + + return 0; +} + static struct phy *mtk_phy_xlate(struct device *dev, struct of_phandle_args *args) { @@ -931,6 +969,7 @@ static const struct phy_ops mtk_tphy_ops = { .exit = mtk_phy_exit, .power_on = mtk_phy_power_on, .power_off = mtk_phy_power_off, + .set_mode = mtk_phy_set_mode, .owner = THIS_MODULE, }; -- cgit v1.2.3 From f85fd4c909565892a49337ef482090e446c4e5bd Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 22 Sep 2017 09:44:05 -0700 Subject: phy: rockchip-typec: Avoid magic numbers + add delays in aux calib NOTE: nothing is known to be fixed by this change, but it does enforce some delays that are documented to be necessary. Possibly this could fix some corner cases. The function tcphy_dp_aux_calibration(), like most of the functions in the type C PHY, is mostly undocumented and filled with mysterious, hardcoded numbers. Let's attempt to try to document some of these numbers and clean the function up a little bit. Here's the actual cleanup that happened here: 1. All magic numbers were replaced with bit definitions. 2. For registers that we modify multiple times I now keep track of the value of the register rather than randomly doing a read/modify/write or just hardcoding a new number based on knowing what the old number was. 3. Delay 10 ms (vs 1 ms) after writing the calibration code. No idea if this is important but it matches the example in the docs. 4. Whenever setting a "delayed" version of a signal always put an explicit delay in the code. No known problems were seen without this delay but it seems wise to have it. Whenever a delay of "at least 100 ns" was specified I used a delay of 1 us. 5. Added comments to some of the bits of code. 6. Removed duplicate setting of TX_ANA_CTRL_REG_5 (to 0) 7. Moved setting of TX_ANA_CTRL_REG_3 to the same place it was in the sample code. Note that TX_ANA_CTRL_REG_3 ought to be initted to 0 (and elsewhere we assume that we just got a reset), but it seems fine to be explicit. 8. Treats the calibration code as a 7-bit two's complement number. This isn't strictly required, but seems slightly cleaner. The docs say "treat this as a two's complement number, but it should never be negative". If we ever read the "adjustment" codes as documented then perhaps the two's complement bit will matter more. There are still a few weird / mysterious things around aux init and this doesn't attempt to fix all of them. Mostly it's aimed at doing changes that should be _very_ safe and add a lot of clarity. Things specifically not done: A) Resolve the fact that some registers are read/modify/write and others are explicitly initted to a value. We always call tcphy_dp_aux_calibration() right after resetting the PHY so it's probably not critical, but it's a little weird that the code is inconsistent. B) Fully resolve the documented init sequence with the current one. We still have a few mystery steps and we also leave out turning on TXDA_DRV_LDO_BG_FB_EN and TXDA_DRV_LDO_BG_REF_EN, which is in the sample code. C) Clean things up to read all the bits of the calibration code. This will hopefully come in a followup change. This also doesn't attempt to document any of the other parts of the PHY--just the aux init which is all I got docs for. Reviewed-by: Chris Zhong Signed-off-by: Douglas Anderson Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/rockchip/phy-rockchip-typec.c | 202 ++++++++++++++++++++++++------ 1 file changed, 163 insertions(+), 39 deletions(-) (limited to 'drivers/phy') diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index a958c9bced01..e5454eccb57d 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -102,9 +102,40 @@ #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_CTRL (0xf0 << 2) #define CMN_TXPDCAL_OVRD (0xf1 << 2) + +/* For CMN_TXPUCAL_CTRL, CMN_TXPDCAL_CTRL */ +#define CMN_TXPXCAL_START BIT(15) +#define CMN_TXPXCAL_DONE BIT(14) +#define CMN_TXPXCAL_NO_RESPONSE BIT(13) +#define CMN_TXPXCAL_CURRENT_RESPONSE BIT(12) + +#define CMN_TXPU_ADJ_CTRL (0x108 << 2) +#define CMN_TXPD_ADJ_CTRL (0x10c << 2) + +/* + * For CMN_TXPUCAL_CTRL, CMN_TXPDCAL_CTRL, + * CMN_TXPU_ADJ_CTRL, CMN_TXPDCAL_CTRL + * + * NOTE: some of these registers are documented to be 2's complement + * signed numbers, but then documented to be always positive. Weird. + * In such a case, using CMN_CALIB_CODE_POS() avoids the unnecessary + * sign extension. + */ +#define CMN_CALIB_CODE_WIDTH 7 +#define CMN_CALIB_CODE_OFFSET 0 +#define CMN_CALIB_CODE_MASK GENMASK(CMN_CALIB_CODE_WIDTH, 0) +#define CMN_CALIB_CODE(x) \ + sign_extend32((x) >> CMN_CALIB_CODE_OFFSET, CMN_CALIB_CODE_WIDTH) + +#define CMN_CALIB_CODE_POS_MASK GENMASK(CMN_CALIB_CODE_WIDTH - 1, 0) +#define CMN_CALIB_CODE_POS(x) \ + (((x) >> CMN_CALIB_CODE_OFFSET) & CMN_CALIB_CODE_POS_MASK) + #define CMN_DIAG_PLL0_FBH_OVRD (0x1c0 << 2) #define CMN_DIAG_PLL0_FBL_OVRD (0x1c1 << 2) #define CMN_DIAG_PLL0_OVRD (0x1c2 << 2) @@ -138,6 +169,15 @@ #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 TX_TXCC_MGNLS_MULT_000(n) ((0x4058 | ((n) << 9)) << 2) +#define TX_TXCC_MGNLS_MULT_001(n) ((0x4059 | ((n) << 9)) << 2) +#define TX_TXCC_MGNLS_MULT_010(n) ((0x405a | ((n) << 9)) << 2) +#define TX_TXCC_MGNLS_MULT_011(n) ((0x405b | ((n) << 9)) << 2) +#define TX_TXCC_MGNLS_MULT_100(n) ((0x405c | ((n) << 9)) << 2) +#define TX_TXCC_MGNLS_MULT_101(n) ((0x405d | ((n) << 9)) << 2) +#define TX_TXCC_MGNLS_MULT_110(n) ((0x405e | ((n) << 9)) << 2) +#define TX_TXCC_MGNLS_MULT_111(n) ((0x405f | ((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) @@ -150,10 +190,63 @@ #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) + +/* Use this for "n" in macros like "_MULT_XXX" to target the aux channel */ +#define AUX_CH_LANE 8 + #define TX_ANA_CTRL_REG_1 (0x5020 << 2) + +#define TXDA_DP_AUX_EN BIT(15) +#define AUXDA_SE_EN BIT(14) +#define TXDA_CAL_LATCH_EN BIT(13) +#define AUXDA_POLARITY BIT(12) +#define TXDA_DRV_POWER_ISOLATION_EN BIT(11) +#define TXDA_DRV_POWER_EN_PH_2_N BIT(10) +#define TXDA_DRV_POWER_EN_PH_1_N BIT(9) +#define TXDA_BGREF_EN BIT(8) +#define TXDA_DRV_LDO_EN BIT(7) +#define TXDA_DECAP_EN_DEL BIT(6) +#define TXDA_DECAP_EN BIT(5) +#define TXDA_UPHY_SUPPLY_EN_DEL BIT(4) +#define TXDA_UPHY_SUPPLY_EN BIT(3) +#define TXDA_LOW_LEAKAGE_EN BIT(2) +#define TXDA_DRV_IDLE_LOWI_EN BIT(1) +#define TXDA_DRV_CMN_MODE_EN BIT(0) + #define TX_ANA_CTRL_REG_2 (0x5021 << 2) + +#define AUXDA_DEBOUNCING_CLK BIT(15) +#define TXDA_LPBK_RECOVERED_CLK_EN BIT(14) +#define TXDA_LPBK_ISI_GEN_EN BIT(13) +#define TXDA_LPBK_SERIAL_EN BIT(12) +#define TXDA_LPBK_LINE_EN BIT(11) +#define TXDA_DRV_LDO_REDC_SINKIQ BIT(10) +#define XCVR_DECAP_EN_DEL BIT(9) +#define XCVR_DECAP_EN BIT(8) +#define TXDA_MPHY_ENABLE_HS_NT BIT(7) +#define TXDA_MPHY_SA_MODE BIT(6) +#define TXDA_DRV_LDO_RBYR_FB_EN BIT(5) +#define TXDA_DRV_RST_PULL_DOWN BIT(4) +#define TXDA_DRV_LDO_BG_FB_EN BIT(3) +#define TXDA_DRV_LDO_BG_REF_EN BIT(2) +#define TXDA_DRV_PREDRV_EN_DEL BIT(1) +#define TXDA_DRV_PREDRV_EN BIT(0) + #define TXDA_COEFF_CALC_CTRL (0x5022 << 2) + +#define TX_HIGH_Z BIT(6) +#define TX_VMARGIN_OFFSET 3 +#define TX_VMARGIN_MASK 0x7 +#define LOW_POWER_SWING_EN BIT(2) +#define TX_FCM_DRV_MAIN_EN BIT(1) +#define TX_FCM_FULL_MARGIN BIT(0) + #define TX_DIG_CTRL_REG_2 (0x5024 << 2) + +#define TX_HIGH_Z_TM_EN BIT(15) +#define TX_RESCAL_CODE_OFFSET 0 +#define TX_RESCAL_CODE_MASK 0x3f + #define TXDA_CYA_AUXDA_CYA (0x5025 << 2) #define TX_ANA_CTRL_REG_3 (0x5026 << 2) #define TX_ANA_CTRL_REG_4 (0x5027 << 2) @@ -456,54 +549,63 @@ static void tcphy_dp_aux_set_flip(struct rockchip_typec_phy *tcphy) */ tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1); if (!tcphy->flip) - tx_ana_ctrl_reg_1 |= BIT(12); + tx_ana_ctrl_reg_1 |= AUXDA_POLARITY; else - tx_ana_ctrl_reg_1 &= ~BIT(12); + tx_ana_ctrl_reg_1 &= ~AUXDA_POLARITY; writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); } static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) { + u16 val; u16 tx_ana_ctrl_reg_1; - u16 rdata, rdata2, val; + u16 tx_ana_ctrl_reg_2; + s32 pu_calib_code; /* disable txda_cal_latch_en for rewrite the calibration values */ tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1); - tx_ana_ctrl_reg_1 &= ~BIT(13); + tx_ana_ctrl_reg_1 &= ~TXDA_CAL_LATCH_EN; writel(tx_ana_ctrl_reg_1, 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. + * read a resistor calibration code from CMN_TXPUCAL_CTRL[5:0] and + * write it to TX_DIG_CTRL_REG_2[5:0]. */ - rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2); - rdata = rdata & 0xffc0; - - rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL); - rdata2 = rdata2 & 0x3f; + val = readl(tcphy->base + CMN_TXPUCAL_CTRL); + pu_calib_code = CMN_CALIB_CODE_POS(val); - val = rdata | rdata2; + /* write the calibration, then delay 10 ms as sample in docs */ + val = readl(tcphy->base + TX_DIG_CTRL_REG_2); + val &= ~(TX_RESCAL_CODE_MASK << TX_RESCAL_CODE_OFFSET); + val |= pu_calib_code << TX_RESCAL_CODE_OFFSET; writel(val, tcphy->base + TX_DIG_CTRL_REG_2); - usleep_range(1000, 1050); + usleep_range(10000, 10050); /* * Enable signal for latch that sample and holds calibration values. * Activate this signal for 1 clock cycle to sample new calibration * values. */ - tx_ana_ctrl_reg_1 |= BIT(13); + tx_ana_ctrl_reg_1 |= TXDA_CAL_LATCH_EN; writel(tx_ana_ctrl_reg_1, 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); - tx_ana_ctrl_reg_1 |= BIT(3); + tx_ana_ctrl_reg_2 = XCVR_DECAP_EN; + writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2); + udelay(1); + tx_ana_ctrl_reg_2 |= XCVR_DECAP_EN_DEL; + writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2); + + writel(0, tcphy->base + TX_ANA_CTRL_REG_3); + + tx_ana_ctrl_reg_1 |= TXDA_UPHY_SUPPLY_EN; writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); - tx_ana_ctrl_reg_1 |= BIT(4); + udelay(1); + tx_ana_ctrl_reg_1 |= TXDA_UPHY_SUPPLY_EN_DEL; writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); writel(0, tcphy->base + TX_ANA_CTRL_REG_5); @@ -515,44 +617,66 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4); /* re-enables Bandgap reference for LDO */ - tx_ana_ctrl_reg_1 |= BIT(7); + tx_ana_ctrl_reg_1 |= TXDA_DRV_LDO_EN; writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); - tx_ana_ctrl_reg_1 |= BIT(8); + udelay(5); + tx_ana_ctrl_reg_1 |= TXDA_BGREF_EN; writel(tx_ana_ctrl_reg_1, 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); + tx_ana_ctrl_reg_2 |= TXDA_DRV_PREDRV_EN; + writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2); + udelay(1); + tx_ana_ctrl_reg_2 |= TXDA_DRV_PREDRV_EN_DEL; + writel(tx_ana_ctrl_reg_2, tcphy->base + TX_ANA_CTRL_REG_2); /* - * Do some magic undocumented stuff, some of which appears to - * undo the "re-enables Bandgap reference for LDO" above. + * Do all the undocumented magic: + * - Turn on TXDA_DP_AUX_EN, whatever that is, even though sample + * never shows this going on. + * - Turn on TXDA_DECAP_EN (and TXDA_DECAP_EN_DEL) even though + * docs say for aux it's always 0. + * - Turn off the LDO and BGREF, which we just spent time turning + * on above (???). + * + * Without this magic, things seem worse. */ - tx_ana_ctrl_reg_1 |= BIT(15); - tx_ana_ctrl_reg_1 &= ~BIT(8); - tx_ana_ctrl_reg_1 &= ~BIT(7); - tx_ana_ctrl_reg_1 |= BIT(6); - tx_ana_ctrl_reg_1 |= BIT(5); + tx_ana_ctrl_reg_1 |= TXDA_DP_AUX_EN; + tx_ana_ctrl_reg_1 |= TXDA_DECAP_EN; + tx_ana_ctrl_reg_1 &= ~TXDA_DRV_LDO_EN; + tx_ana_ctrl_reg_1 &= ~TXDA_BGREF_EN; + writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); + udelay(1); + tx_ana_ctrl_reg_1 |= TXDA_DECAP_EN_DEL; writel(tx_ana_ctrl_reg_1, 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, don't set the voltage swing of the - * driver to 400mv. The values below are peak to peak (differential) - * values. + * Undo the work we did to set the LDO voltage. + * This doesn't seem to help nor hurt, but it kinda goes with the + * undocumented magic above. */ + writel(0, tcphy->base + TX_ANA_CTRL_REG_4); + + /* Don't set voltage swing to 400 mV peak to peak (differential) */ writel(0, tcphy->base + TXDA_COEFF_CALC_CTRL); + + /* Init TXDA_CYA_AUXDA_CYA for unknown magic reasons */ writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA); - /* Controls tx_high_z_tm_en */ + /* + * More undocumented magic, presumably the goal of which is to + * make the "auxda_source_aux_oen" be ignored and instead to decide + * about "high impedance state" based on what software puts in the + * register TXDA_COEFF_CALC_CTRL (see TX_HIGH_Z). Since we only + * program that register once and we don't set the bit TX_HIGH_Z, + * presumably the goal here is that we should never put the analog + * driver in high impedance state. + */ val = readl(tcphy->base + TX_DIG_CTRL_REG_2); - val |= BIT(15); + val |= TX_HIGH_Z_TM_EN; writel(val, tcphy->base + TX_DIG_CTRL_REG_2); } -- cgit v1.2.3 From e023b1fb5286e23c224f2ae312831a4fce258c02 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 22 Sep 2017 09:44:06 -0700 Subject: phy: rockchip-typec: Do the calibration more correctly Calculate the calibration code as per the docs. The docs talk about reading and averaging the pullup and pulldown calibration codes. They also talk about adding in some adjustment codes. Let's do what the docs say. In practice this doesn't seem to matter a whole lot. On a device I tested the pullup and pulldown codes were nearly the same (0x23 and 0x24) and the adjustment codes were 0. Reviewed-by: Chris Zhong Signed-off-by: Douglas Anderson Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/rockchip/phy-rockchip-typec.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'drivers/phy') diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index e5454eccb57d..ee85fa0ca4b0 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -560,24 +560,33 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy) u16 val; u16 tx_ana_ctrl_reg_1; u16 tx_ana_ctrl_reg_2; - s32 pu_calib_code; - - /* disable txda_cal_latch_en for rewrite the calibration values */ - tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1); - tx_ana_ctrl_reg_1 &= ~TXDA_CAL_LATCH_EN; - writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); + s32 pu_calib_code, pd_calib_code; + s32 pu_adj, pd_adj; + u16 calib; /* - * read a resistor calibration code from CMN_TXPUCAL_CTRL[5:0] and - * write it to TX_DIG_CTRL_REG_2[5:0]. + * Calculate calibration code as per docs: use an average of the + * pull down and pull up. Then add in adjustments. */ val = readl(tcphy->base + CMN_TXPUCAL_CTRL); pu_calib_code = CMN_CALIB_CODE_POS(val); + val = readl(tcphy->base + CMN_TXPDCAL_CTRL); + pd_calib_code = CMN_CALIB_CODE_POS(val); + val = readl(tcphy->base + CMN_TXPU_ADJ_CTRL); + pu_adj = CMN_CALIB_CODE(val); + val = readl(tcphy->base + CMN_TXPD_ADJ_CTRL); + pd_adj = CMN_CALIB_CODE(val); + calib = (pu_calib_code + pd_calib_code) / 2 + pu_adj + pd_adj; + + /* disable txda_cal_latch_en for rewrite the calibration values */ + tx_ana_ctrl_reg_1 = readl(tcphy->base + TX_ANA_CTRL_REG_1); + tx_ana_ctrl_reg_1 &= ~TXDA_CAL_LATCH_EN; + writel(tx_ana_ctrl_reg_1, tcphy->base + TX_ANA_CTRL_REG_1); /* write the calibration, then delay 10 ms as sample in docs */ val = readl(tcphy->base + TX_DIG_CTRL_REG_2); val &= ~(TX_RESCAL_CODE_MASK << TX_RESCAL_CODE_OFFSET); - val |= pu_calib_code << TX_RESCAL_CODE_OFFSET; + val |= calib << TX_RESCAL_CODE_OFFSET; writel(val, tcphy->base + TX_DIG_CTRL_REG_2); usleep_range(10000, 10050); -- cgit v1.2.3 From a06173badf697b1aad51d9fc280b520b38715c95 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 28 Sep 2017 17:33:49 +0800 Subject: phy: sun4i-usb: enable PHY0 dual route for V3s SoC Allwinner V3s SoC also features the dual route of the first USB PHY. Enable it. Signed-off-by: Icenowy Zheng Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/allwinner/phy-sun4i-usb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/phy') diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 1161e11fb3cf..a2fbc7b00b9a 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -926,6 +926,7 @@ static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, .enable_pmu_unk1 = true, + .phy0_dual_route = true, }; static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { -- cgit v1.2.3 From 7e0540f41332cb07055c5fe6629dc83c71974c82 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Thu, 12 Oct 2017 15:34:45 +0900 Subject: phy: rcar-gen3-usb2: check dr_mode for otg mode The previous code assumed a channel has otg capability if a channel has interrupt property. But, it is not good because: - Battery charging feature also needs interrupt property. - Some R-Car Gen3 SoCs (e.g. R-Car D3) don't have OTG capability. So, this patch checks whether usb 2.0 host node has dr_mode property or not. If it has 'dr_mode = "otg";', this driver enables otg capability. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Simon Horman Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/phy') diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 54c34298a000..e00e99a96b68 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -1,7 +1,7 @@ /* * Renesas R-Car Gen3 for USB2.0 PHY driver * - * Copyright (C) 2015 Renesas Electronics Corporation + * Copyright (C) 2015-2017 Renesas Electronics Corporation * * This is based on the phy-rcar-gen2 driver: * Copyright (C) 2014 Renesas Solutions Corp. @@ -22,6 +22,7 @@ #include #include #include +#include #include /******* USB2.0 Host registers (original offset is +0x200) *******/ @@ -415,13 +416,16 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) /* 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); + } + + if (of_usb_get_dr_mode_by_phy(dev->of_node, 0) == USB_DR_MODE_OTG) { + int ret; + channel->has_otg = true; channel->extcon = devm_extcon_dev_allocate(dev, rcar_gen3_phy_cable); -- cgit v1.2.3 From b56acc82f9719d6aa1c1003ac7e34391da85a824 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Thu, 12 Oct 2017 15:34:46 +0900 Subject: phy: rcar-gen3-usb2: use enum phy_mode in the role_store() This patch modifies the role_store() to use "enum phy_mode" instead of the local "bool" for host/device mode selection. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Simon Horman Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'drivers/phy') diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index e00e99a96b68..00a830344d6c 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -219,33 +219,40 @@ static bool rcar_gen3_is_host(struct rcar_gen3_chan *ch) return !(readl(ch->base + USB2_COMMCTRL) & USB2_COMMCTRL_OTG_PERI); } +static enum phy_mode rcar_gen3_get_phy_mode(struct rcar_gen3_chan *ch) +{ + if (rcar_gen3_is_host(ch)) + return PHY_MODE_USB_HOST; + + return PHY_MODE_USB_DEVICE; +} + 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; + bool is_b_device; + enum phy_mode cur_mode, new_mode; 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; + new_mode = PHY_MODE_USB_HOST; else if (!strncmp(buf, "peripheral", strlen("peripheral"))) - new_mode_is_host = false; + new_mode = PHY_MODE_USB_DEVICE; else return -EINVAL; + /* is_b_device: true is B-Device. false is A-Device. */ + is_b_device = rcar_gen3_check_id(ch); + cur_mode = rcar_gen3_get_phy_mode(ch); + /* If current and new mode is the same, this returns the error */ - if (is_host == new_mode_is_host) + if (cur_mode == new_mode) return -EINVAL; - if (new_mode_is_host) { /* And is_host must be false */ + if (new_mode == PHY_MODE_USB_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 */ -- cgit v1.2.3 From 9adaaa9e4517afb8c5cb8931cc4ea0f81f54d396 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Thu, 12 Oct 2017 15:34:47 +0900 Subject: phy: rcar-gen3-usb2: add SoC-specific parameter for dedicated pins This patch adds SoC-specific parameter to avoid reading/writing specific registers wrongly if this driver runs on a SoC which doesn't have dedicated pins (e.g. R-Car D3). This patch also changes the value "has_otg" to "has_otg_pins" for slightly easier reading of the code. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Simon Horman Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'drivers/phy') diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 00a830344d6c..479ae94b976e 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -80,6 +81,8 @@ #define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ #define USB2_ADPCTRL_DRVVBUS BIT(4) +#define RCAR_GEN3_PHY_HAS_DEDICATED_PINS 1 + struct rcar_gen3_chan { void __iomem *base; struct extcon_dev *extcon; @@ -87,7 +90,7 @@ struct rcar_gen3_chan { struct regulator *vbus; struct work_struct work; bool extcon_host; - bool has_otg; + bool has_otg_pins; }; static void rcar_gen3_phy_usb2_work(struct work_struct *work) @@ -234,7 +237,7 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr, bool is_b_device; enum phy_mode cur_mode, new_mode; - if (!ch->has_otg || !ch->phy->init_count) + if (!ch->has_otg_pins || !ch->phy->init_count) return -EIO; if (!strncmp(buf, "host", strlen("host"))) @@ -272,7 +275,7 @@ static ssize_t role_show(struct device *dev, struct device_attribute *attr, { struct rcar_gen3_chan *ch = dev_get_drvdata(dev); - if (!ch->has_otg || !ch->phy->init_count) + if (!ch->has_otg_pins || !ch->phy->init_count) return -EIO; return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" : @@ -311,7 +314,7 @@ static int rcar_gen3_phy_usb2_init(struct phy *p) writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET); /* Initialize otg part */ - if (channel->has_otg) + if (channel->has_otg_pins) rcar_gen3_init_otg(channel); return 0; @@ -385,9 +388,17 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) } 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" }, + { + .compatible = "renesas,usb2-phy-r8a7795", + .data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS, + }, + { + .compatible = "renesas,usb2-phy-r8a7796", + .data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS, + }, + { + .compatible = "renesas,rcar-gen3-usb2-phy", + }, { } }; MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table); @@ -433,7 +444,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) if (of_usb_get_dr_mode_by_phy(dev->of_node, 0) == USB_DR_MODE_OTG) { int ret; - channel->has_otg = true; + channel->has_otg_pins = (uintptr_t)of_device_get_match_data(dev); channel->extcon = devm_extcon_dev_allocate(dev, rcar_gen3_phy_cable); if (IS_ERR(channel->extcon)) @@ -475,7 +486,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) dev_err(dev, "Failed to register PHY provider\n"); ret = PTR_ERR(provider); goto error; - } else if (channel->has_otg) { + } else if (channel->has_otg_pins) { int ret; ret = device_create_file(dev, &dev_attr_role); @@ -495,7 +506,7 @@ static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev) { struct rcar_gen3_chan *channel = platform_get_drvdata(pdev); - if (channel->has_otg) + if (channel->has_otg_pins) device_remove_file(&pdev->dev, &dev_attr_role); pm_runtime_disable(&pdev->dev); -- cgit v1.2.3 From 6ec248fed585d88873c79088cba45918f7724eff Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 11 Oct 2017 17:53:11 -0700 Subject: phy: brcm-sata: Prepare for doing more tuning Split the functional code in brcm_stb_sata_init() to a separate function that actually does configure spread spectrum: brcm_stb_sata_ssc_init() and make that function return void, since that function cannot fail. Signed-off-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-sata.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/phy') diff --git a/drivers/phy/broadcom/phy-brcm-sata.c b/drivers/phy/broadcom/phy-brcm-sata.c index 9d7f74fe3d7c..0152d0b32dae 100644 --- a/drivers/phy/broadcom/phy-brcm-sata.c +++ b/drivers/phy/broadcom/phy-brcm-sata.c @@ -190,7 +190,7 @@ static u32 brcm_sata_phy_rd(void __iomem *pcb_base, u32 bank, u32 ofs) #define STB_FMAX_VAL_DEFAULT 0x3df #define STB_FMAX_VAL_SSC 0x83 -static int brcm_stb_sata_init(struct brcm_sata_port *port) +static void brcm_stb_sata_ssc_init(struct brcm_sata_port *port) { void __iomem *base = brcm_sata_pcb_base(port); struct brcm_sata_phy *priv = port->phy_priv; @@ -215,6 +215,11 @@ static int brcm_stb_sata_init(struct brcm_sata_port *port) brcm_sata_phy_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3, ~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp); +} + +static int brcm_stb_sata_init(struct brcm_sata_port *port) +{ + brcm_stb_sata_ssc_init(port); return 0; } -- cgit v1.2.3 From af174c49564a6fcdb2d10516307e6b28d213b807 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 11 Oct 2017 17:53:12 -0700 Subject: phy: brcm-sata: Allow RX equalizer tuning Parse the DT properties brcm,rxaeq-mode and brcm,rxaeq-value to correctly configure the RX equalizer of the PHY. This may be required to resolve specific signal integrity issues. Signed-off-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/broadcom/phy-brcm-sata.c | 69 +++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) (limited to 'drivers/phy') diff --git a/drivers/phy/broadcom/phy-brcm-sata.c b/drivers/phy/broadcom/phy-brcm-sata.c index 0152d0b32dae..3f953db70288 100644 --- a/drivers/phy/broadcom/phy-brcm-sata.c +++ b/drivers/phy/broadcom/phy-brcm-sata.c @@ -49,11 +49,29 @@ enum brcm_sata_phy_version { BRCM_SATA_PHY_IPROC_SR, }; +enum brcm_sata_phy_rxaeq_mode { + RXAEQ_MODE_OFF = 0, + RXAEQ_MODE_AUTO, + RXAEQ_MODE_MANUAL, +}; + +static enum brcm_sata_phy_rxaeq_mode rxaeq_to_val(const char *m) +{ + if (!strcmp(m, "auto")) + return RXAEQ_MODE_AUTO; + else if (!strcmp(m, "manual")) + return RXAEQ_MODE_MANUAL; + else + return RXAEQ_MODE_OFF; +} + struct brcm_sata_port { int portnum; struct phy *phy; struct brcm_sata_phy *phy_priv; bool ssc_en; + enum brcm_sata_phy_rxaeq_mode rxaeq_mode; + u32 rxaeq_val; }; struct brcm_sata_phy { @@ -93,6 +111,15 @@ enum sata_phy_regs { TX_ACTRL0 = 0x80, TX_ACTRL0_TXPOL_FLIP = BIT(6), + AEQRX_REG_BANK_0 = 0xd0, + AEQ_CONTROL1 = 0x81, + AEQ_CONTROL1_ENABLE = BIT(2), + AEQ_CONTROL1_FREEZE = BIT(3), + AEQ_FRC_EQ = 0x83, + AEQ_FRC_EQ_FORCE = BIT(0), + AEQ_FRC_EQ_FORCE_VAL = BIT(1), + AEQRX_REG_BANK_1 = 0xe0, + OOB_REG_BANK = 0x150, OOB1_REG_BANK = 0x160, OOB_CTRL1 = 0x80, @@ -217,11 +244,43 @@ static void brcm_stb_sata_ssc_init(struct brcm_sata_port *port) ~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp); } +#define AEQ_FRC_EQ_VAL_SHIFT 2 +#define AEQ_FRC_EQ_VAL_MASK 0x3f + +static int brcm_stb_sata_rxaeq_init(struct brcm_sata_port *port) +{ + void __iomem *base = brcm_sata_pcb_base(port); + u32 tmp = 0, reg = 0; + + switch (port->rxaeq_mode) { + case RXAEQ_MODE_OFF: + return 0; + + case RXAEQ_MODE_AUTO: + reg = AEQ_CONTROL1; + tmp = AEQ_CONTROL1_ENABLE | AEQ_CONTROL1_FREEZE; + break; + + case RXAEQ_MODE_MANUAL: + reg = AEQ_FRC_EQ; + tmp = AEQ_FRC_EQ_FORCE | AEQ_FRC_EQ_FORCE_VAL; + if (port->rxaeq_val > AEQ_FRC_EQ_VAL_MASK) + return -EINVAL; + tmp |= port->rxaeq_val << AEQ_FRC_EQ_VAL_SHIFT; + break; + } + + brcm_sata_phy_wr(base, AEQRX_REG_BANK_0, reg, ~tmp, tmp); + brcm_sata_phy_wr(base, AEQRX_REG_BANK_1, reg, ~tmp, tmp); + + return 0; +} + static int brcm_stb_sata_init(struct brcm_sata_port *port) { brcm_stb_sata_ssc_init(port); - return 0; + return brcm_stb_sata_rxaeq_init(port); } /* NS2 SATA PLL1 defaults were characterized by H/W group */ @@ -468,6 +527,7 @@ MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match); static int brcm_sata_phy_probe(struct platform_device *pdev) { + const char *rxaeq_mode; struct device *dev = &pdev->dev; struct device_node *dn = dev->of_node, *child; const struct of_device_id *of_id; @@ -530,6 +590,13 @@ static int brcm_sata_phy_probe(struct platform_device *pdev) port->portnum = id; port->phy_priv = priv; port->phy = devm_phy_create(dev, child, &phy_ops); + port->rxaeq_mode = RXAEQ_MODE_OFF; + if (!of_property_read_string(child, "brcm,rxaeq-mode", + &rxaeq_mode)) + port->rxaeq_mode = rxaeq_to_val(rxaeq_mode); + if (port->rxaeq_mode == RXAEQ_MODE_MANUAL) + of_property_read_u32(child, "brcm,rxaeq-value", + &port->rxaeq_val); port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc"); if (IS_ERR(port->phy)) { dev_err(dev, "failed to create PHY\n"); -- cgit v1.2.3 From 2796ceb0c18a5a0ee96f36d4c3b3773df74c560e Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 9 Oct 2017 14:33:38 +0530 Subject: phy: ti-pipe3: Update pcie phy settings Update the PCIe phy settings based on new settings available in AM572x Technical Reference Manual[1] Revision I, revised April 2017 in Table 26-62 "Preferred PCIe_PHY_RX SCP Register Settings". [1] http://www.ti.com/lit/ug/spruhz6i/spruhz6i.pdf Cc: Vignesh R Signed-off-by: Kishon Vijay Abraham I [nsekhar@ti.com: commit message updates] Signed-off-by: Sekhar Nori Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/ti/phy-ti-pipe3.c | 101 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) (limited to 'drivers/phy') diff --git a/drivers/phy/ti/phy-ti-pipe3.c b/drivers/phy/ti/phy-ti-pipe3.c index 0e564f32749f..68ce4a082b9b 100644 --- a/drivers/phy/ti/phy-ti-pipe3.c +++ b/drivers/phy/ti/phy-ti-pipe3.c @@ -68,6 +68,40 @@ #define PCIE_PCS_MASK 0xFF0000 #define PCIE_PCS_DELAY_COUNT_SHIFT 0x10 +#define PCIEPHYRX_ANA_PROGRAMMABILITY 0x0000000C +#define INTERFACE_MASK GENMASK(31, 27) +#define INTERFACE_SHIFT 27 +#define LOSD_MASK GENMASK(17, 14) +#define LOSD_SHIFT 14 +#define MEM_PLLDIV GENMASK(6, 5) + +#define PCIEPHYRX_TRIM 0x0000001C +#define MEM_DLL_TRIM_SEL GENMASK(31, 30) +#define MEM_DLL_TRIM_SHIFT 30 + +#define PCIEPHYRX_DLL 0x00000024 +#define MEM_DLL_PHINT_RATE GENMASK(31, 30) + +#define PCIEPHYRX_DIGITAL_MODES 0x00000028 +#define MEM_CDR_FASTLOCK BIT(23) +#define MEM_CDR_LBW GENMASK(22, 21) +#define MEM_CDR_STEPCNT GENMASK(20, 19) +#define MEM_CDR_STL_MASK GENMASK(18, 16) +#define MEM_CDR_STL_SHIFT 16 +#define MEM_CDR_THR_MASK GENMASK(15, 13) +#define MEM_CDR_THR_SHIFT 13 +#define MEM_CDR_THR_MODE BIT(12) +#define MEM_CDR_CDR_2NDO_SDM_MODE BIT(11) +#define MEM_OVRD_HS_RATE BIT(26) + +#define PCIEPHYRX_EQUALIZER 0x00000038 +#define MEM_EQLEV GENMASK(31, 16) +#define MEM_EQFTC GENMASK(15, 11) +#define MEM_EQCTL GENMASK(10, 7) +#define MEM_EQCTL_SHIFT 7 +#define MEM_OVRD_EQLEV BIT(2) +#define MEM_OVRD_EQFTC BIT(1) + /* * This is an Empirical value that works, need to confirm the actual * value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status @@ -91,6 +125,8 @@ struct pipe3_dpll_map { struct ti_pipe3 { void __iomem *pll_ctrl_base; + void __iomem *phy_rx; + void __iomem *phy_tx; struct device *dev; struct device *control_dev; struct clk *wkupclk; @@ -261,6 +297,37 @@ static int ti_pipe3_dpll_program(struct ti_pipe3 *phy) return ti_pipe3_dpll_wait_lock(phy); } +static void ti_pipe3_calibrate(struct ti_pipe3 *phy) +{ + u32 val; + + val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_ANA_PROGRAMMABILITY); + val &= ~(INTERFACE_MASK | LOSD_MASK | MEM_PLLDIV); + val = (0x1 << INTERFACE_SHIFT | 0xA << LOSD_SHIFT); + ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_ANA_PROGRAMMABILITY, val); + + val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_DIGITAL_MODES); + val &= ~(MEM_CDR_STEPCNT | MEM_CDR_STL_MASK | MEM_CDR_THR_MASK | + MEM_CDR_CDR_2NDO_SDM_MODE | MEM_OVRD_HS_RATE); + val |= (MEM_CDR_FASTLOCK | MEM_CDR_LBW | 0x3 << MEM_CDR_STL_SHIFT | + 0x1 << MEM_CDR_THR_SHIFT | MEM_CDR_THR_MODE); + ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_DIGITAL_MODES, val); + + val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_TRIM); + val &= ~MEM_DLL_TRIM_SEL; + val |= 0x2 << MEM_DLL_TRIM_SHIFT; + ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_TRIM, val); + + val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_DLL); + val |= MEM_DLL_PHINT_RATE; + ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_DLL, val); + + val = ti_pipe3_readl(phy->phy_rx, PCIEPHYRX_EQUALIZER); + val &= ~(MEM_EQLEV | MEM_EQCTL | MEM_OVRD_EQLEV | MEM_OVRD_EQFTC); + val |= MEM_EQFTC | 0x1 << MEM_EQCTL_SHIFT; + ti_pipe3_writel(phy->phy_rx, PCIEPHYRX_EQUALIZER, val); +} + static int ti_pipe3_init(struct phy *x) { struct ti_pipe3 *phy = phy_get_drvdata(x); @@ -282,7 +349,12 @@ static int ti_pipe3_init(struct phy *x) 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; + if (ret) + return ret; + + ti_pipe3_calibrate(phy); + + return 0; } /* Bring it out of IDLE if it is IDLE */ @@ -513,6 +585,29 @@ static int ti_pipe3_get_sysctrl(struct ti_pipe3 *phy) return 0; } +static int ti_pipe3_get_tx_rx_base(struct ti_pipe3 *phy) +{ + struct resource *res; + 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; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "phy_rx"); + phy->phy_rx = devm_ioremap_resource(dev, res); + if (IS_ERR(phy->phy_rx)) + return PTR_ERR(phy->phy_rx); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "phy_tx"); + phy->phy_tx = devm_ioremap_resource(dev, res); + + return PTR_ERR_OR_ZERO(phy->phy_tx); +} + static int ti_pipe3_get_pll_base(struct ti_pipe3 *phy) { struct resource *res; @@ -559,6 +654,10 @@ static int ti_pipe3_probe(struct platform_device *pdev) if (ret) return ret; + ret = ti_pipe3_get_tx_rx_base(phy); + if (ret) + return ret; + ret = ti_pipe3_get_sysctrl(phy); if (ret) return ret; -- cgit v1.2.3 From 3d741ff44edc05cd313ddc1b3c1c1ab0f41722f4 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 12 Oct 2017 11:49:34 +0530 Subject: phy: qcom-ufs: Add support to set phy mode Adding support to set desired UFS phy mode that can be set from the host controller. Signed-off-by: Vivek Gautam Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-ufs-i.h | 2 ++ drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c | 14 ++++++++++++++ drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c | 14 ++++++++++++++ 3 files changed, 30 insertions(+) (limited to 'drivers/phy') diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h index 13b02b7de30b..94326ed107c3 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-i.h +++ b/drivers/phy/qualcomm/phy-qcom-ufs-i.h @@ -115,6 +115,8 @@ struct ufs_qcom_phy { int cached_regs_table_size; bool is_powered_on; struct ufs_qcom_phy_specific_ops *phy_spec_ops; + + enum phy_mode mode; }; /** diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c index 12a1b498dc4b..af65785230b5 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c @@ -52,6 +52,19 @@ static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy) return 0; } +static +int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy, enum phy_mode mode) +{ + struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); + + phy_common->mode = PHY_MODE_INVALID; + + if (mode > 0) + phy_common->mode = mode; + + return 0; +} + static void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val) { @@ -102,6 +115,7 @@ static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { .exit = ufs_qcom_phy_qmp_14nm_exit, .power_on = ufs_qcom_phy_power_on, .power_off = ufs_qcom_phy_power_off, + .set_mode = ufs_qcom_phy_qmp_14nm_set_mode, .owner = THIS_MODULE, }; diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c index 4f68acb58b73..5c18c41dbdb4 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c @@ -71,6 +71,19 @@ static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy) return 0; } +static +int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy, enum phy_mode mode) +{ + struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); + + phy_common->mode = PHY_MODE_INVALID; + + if (mode > 0) + phy_common->mode = mode; + + return 0; +} + static void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy *phy, bool val) { @@ -160,6 +173,7 @@ static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { .exit = ufs_qcom_phy_qmp_20nm_exit, .power_on = ufs_qcom_phy_power_on, .power_off = ufs_qcom_phy_power_off, + .set_mode = ufs_qcom_phy_qmp_20nm_set_mode, .owner = THIS_MODULE, }; -- cgit v1.2.3 From 052553af6a31b459adfdc6fd1eebced75de332fc Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 12 Oct 2017 11:49:36 +0530 Subject: ufs/phy: qcom: Refactor to use phy_init call Refactor ufs_qcom_power_up_sequence() to get rid of ugly exported phy APIs and use the phy_init() and phy_power_on() to do the phy initialization. Signed-off-by: Vivek Gautam Reviewed-by: Subhash Jadavani Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-ufs-i.h | 3 +- drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c | 15 ++++++++-- drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c | 15 ++++++++-- drivers/phy/qualcomm/phy-qcom-ufs.c | 42 ++++++++++------------------ 4 files changed, 41 insertions(+), 34 deletions(-) (limited to 'drivers/phy') diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h index 94326ed107c3..822c83b8efcd 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-i.h +++ b/drivers/phy/qualcomm/phy-qcom-ufs-i.h @@ -114,6 +114,7 @@ struct ufs_qcom_phy { struct ufs_qcom_phy_calibration *cached_regs; int cached_regs_table_size; bool is_powered_on; + bool is_started; struct ufs_qcom_phy_specific_ops *phy_spec_ops; enum phy_mode mode; @@ -123,7 +124,6 @@ struct ufs_qcom_phy { * 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. @@ -132,7 +132,6 @@ struct ufs_qcom_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); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c index af65785230b5..ba1895b76a5d 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c @@ -44,7 +44,19 @@ void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common) static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy) { - return 0; + struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); + bool is_rate_B = false; + int ret; + + if (phy_common->mode == PHY_MODE_UFS_HS_B) + is_rate_B = true; + + ret = ufs_qcom_phy_qmp_14nm_phy_calibrate(phy_common, is_rate_B); + if (!ret) + /* phy calibrated, but yet to be started */ + phy_common->is_started = false; + + return ret; } static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy) @@ -120,7 +132,6 @@ static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { }; 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, diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c index 5c18c41dbdb4..49f435c71147 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c @@ -63,7 +63,19 @@ void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common) static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy) { - return 0; + struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); + bool is_rate_B = false; + int ret; + + if (phy_common->mode == PHY_MODE_UFS_HS_B) + is_rate_B = true; + + ret = ufs_qcom_phy_qmp_20nm_phy_calibrate(phy_common, is_rate_B); + if (!ret) + /* phy calibrated, but yet to be started */ + phy_common->is_started = false; + + return ret; } static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy) @@ -178,7 +190,6 @@ static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { }; 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, diff --git a/drivers/phy/qualcomm/phy-qcom-ufs.c b/drivers/phy/qualcomm/phy-qcom-ufs.c index 43865ef340e2..c5ff4525edef 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs.c @@ -518,9 +518,8 @@ void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy) } } -int ufs_qcom_phy_start_serdes(struct phy *generic_phy) +static int ufs_qcom_phy_start_serdes(struct ufs_qcom_phy *ufs_qcom_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) { @@ -533,7 +532,6 @@ int ufs_qcom_phy_start_serdes(struct phy *generic_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) { @@ -564,31 +562,8 @@ void ufs_qcom_phy_save_controller_version(struct phy *generic_phy, } 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) +static int ufs_qcom_phy_is_pcs_ready(struct ufs_qcom_phy *ufs_qcom_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__); @@ -598,7 +573,6 @@ int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy) 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) { @@ -609,6 +583,18 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy) if (phy_common->is_powered_on) return 0; + if (!phy_common->is_started) { + err = ufs_qcom_phy_start_serdes(phy_common); + if (err) + return err; + + err = ufs_qcom_phy_is_pcs_ready(phy_common); + if (err) + return err; + + phy_common->is_started = true; + } + err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy); if (err) { dev_err(dev, "%s enable vdda_phy failed, err=%d\n", -- cgit v1.2.3 From 36914111e6829be36b23d1109214250b5ee1ee9c Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 9 Oct 2017 14:00:50 +0200 Subject: drivers: phy: add calibrate method Some quirky UDCs (like dwc3 on Exynos) need to have their phys calibrated e.g. for using super speed. This patch adds a new phy_calibrate() method. When the calibration should be used is dependent on actual chip. In case of dwc3 on Exynos the calibration must happen after usb_add_hcd() (while in host mode), because certain phy parameters like Tx LOS levels and boost levels need to be calibrated further post initialization of xHCI controller, to get SuperSpeed operations working. But an hcd must be prepared first in order to pass it to usb_add_hcd(), so, in particular, dwc3 registers must be available first, and in order for the latter to happen the phys must be initialized. This poses a chicken and egg problem if the calibration were to be performed in phy_init(). To break the circular dependency a separate method is added which can be called at a desired moment after phy intialization. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-core.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/phy') diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index a268f4d6f3e9..b4964b067aec 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -372,6 +372,21 @@ int phy_reset(struct phy *phy) } EXPORT_SYMBOL_GPL(phy_reset); +int phy_calibrate(struct phy *phy) +{ + int ret; + + if (!phy || !phy->ops->calibrate) + return 0; + + mutex_lock(&phy->mutex); + ret = phy->ops->calibrate(phy); + mutex_unlock(&phy->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(phy_calibrate); + /** * _of_phy_get() - lookup and obtain a reference to a phy by phandle * @np: device_node for which to get the phy -- cgit v1.2.3