/* * Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef VERBOSE_DEBUG #ifdef TRACE #undef TRACE #endif #ifdef VERBOSE_DEBUG #define TRACE(dev, fmt, args...) \ dev_dbg(dev, "%s(%d) " fmt "\n", __func__, __LINE__, ## args) #else #define TRACE(dev, fmt, args...) \ do { \ if (0) \ dev_dbg(dev, "%s(%d) " fmt "\n", \ __func__, __LINE__, ## args); \ } while (0) #endif #include "core.h" #include "pinctrl-utils.h" #define TEGRA_USB3_PHYS (3) #define TEGRA_UTMI_PHYS (3) #define TEGRA_HSIC_PHYS (1) #define TEGRA_CDP_PHYS (3) /* FUSE USB_CALIB registers */ /* FUSE_USB_CALIB_0 */ #define HS_CURR_LEVEL_PADX_SHIFT(x) ((x) ? (11 + (x - 1) * 6) : 0) #define HS_CURR_LEVEL_PAD_MASK (0x3f) /* TODO: HS_TERM_RANGE_ADJ has bits overlap, check with hardware team */ #define HS_TERM_RANGE_ADJ_SHIFT (7) #define HS_TERM_RANGE_ADJ_MASK (0xf) #define HS_SQUELCH_SHIFT (29) #define HS_SQUELCH_MASK (0x7) /* FUSE_USB_CALIB_EXT_0 */ #define RPD_CTRL_SHIFT (0) #define RPD_CTRL_MASK (0x1f) /* Data contact detection timeout */ #define TDCD_TIMEOUT_MS 400 /* XUSB PADCTL registers */ #define XUSB_PADCTL_USB2_PAD_MUX (0x4) #define PORT_HSIC (0) #define PORT_XUSB (1) #define XUSB_PADCTL_USB2_PORT_CAP (0x8) #define XUSB_PADCTL_SS_PORT_CAP (0xc) #define PORTX_CAP_SHIFT(x) ((x) * 4) #define PORT_CAP_MASK (0x3) #define PORT_CAP_DISABLED (0x0) #define PORT_CAP_HOST (0x1) #define PORT_CAP_DEVICE (0x2) #define PORT_CAP_OTG (0x3) #define PORT_REVERSE_ID(x) (1 << ((x) * 4 + 3)) #define XUSB_PADCTL_USB2_OC_MAP (0x10) #define XUSB_PADCTL_SS_OC_MAP (0x14) #define PORTX_OC_PIN_SHIFT(x) ((x) * 4) #define PORT_OC_PIN_MASK (0xf) #define OC_PIN_DETECTION_DISABLED (0xf) #define OC_PIN_DETECTED(x) (x) #define OC_PIN_DETECTED_VBUS_PAD(x) ((x) + 4) #define XUSB_PADCTL_VBUS_OC_MAP (0x18) #define VBUS_OC_MAP_SHIFT(x) ((x) * 5 + 1) #define VBUS_OC_MAP_MASK (0xf) #define VBUS_OC_DETECTION_DISABLED (0xf) #define VBUS_OC_DETECTED(x) (x) #define VBUS_OC_DETECTED_VBUS_PAD(x) ((x) + 4) #define VBUS_ENABLE(x) (1 << (x) * 5) #define XUSB_PADCTL_OC_DET (0x1c) #define SET_OC_DETECTED(x) (1 << (x)) #define OC_DETECTED(x) (1 << (8 + (x))) #define OC_DETECTED_VBUS_PAD(x) (1 << (12 + (x))) #define OC_DETECTED_VBUS_PAD_MASK (0xf << 12) #define OC_DETECTED_INT_EN (1 << (20 + (x))) #define OC_DETECTED_INT_EN_VBUS_PAD(x) (1 << (24 + (x))) #define XUSB_PADCTL_ELPG_PROGRAM (0x20) #define USB2_PORT_WAKE_INTERRUPT_ENABLE(x) (1 << (x)) #define USB2_PORT_WAKEUP_EVENT(x) (1 << ((x) + 7)) #define SS_PORT_WAKE_INTERRUPT_ENABLE(x) (1 << ((x) + 14)) #define SS_PORT_WAKEUP_EVENT(x) (1 << ((x) + 21)) #define USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(x) (1 << ((x) + 28)) #define USB2_HSIC_PORT_WAKEUP_EVENT(x) (1 << ((x) + 30)) #define ALL_WAKE_EVENTS \ (USB2_PORT_WAKEUP_EVENT(0) | USB2_PORT_WAKEUP_EVENT(1) | \ USB2_PORT_WAKEUP_EVENT(2) | SS_PORT_WAKEUP_EVENT(0) | \ SS_PORT_WAKEUP_EVENT(1) | SS_PORT_WAKEUP_EVENT(2) | \ USB2_HSIC_PORT_WAKEUP_EVENT(0)) #define XUSB_PADCTL_ELPG_PROGRAM_1 (0x24) #define SSPX_ELPG_CLAMP_EN(x) (1 << (0 + (x) * 3)) #define SSPX_ELPG_CLAMP_EN_EARLY(x) (1 << (1 + (x) * 3)) #define SSPX_ELPG_VCORE_DOWN(x) (1 << (2 + (x) * 3)) #define USB2_BATTERY_CHRG_OTGPADX_CTL0(x) (0x80 + (x) * 0x40) #define PD_CHG (1 << 0) #define VDCD_DET_FILTER_EN (1 << 4) #define VDAT_DET (1 << 5) #define VDAT_DET_FILTER_EN (1 << 8) #define OP_SINK_EN (1 << 9) #define OP_SRC_EN (1 << 10) #define ON_SINK_EN (1 << 11) #define ON_SRC_EN (1 << 12) #define OP_I_SRC_EN (1 << 13) #define ZIP_FILTER_EN (1 << 21) #define ZIN_FILTER_EN (1 << 25) #define DCD_DETECTED (1 << 26) #define SRP_DETECT_EN (1 << 28) #define SRP_DETECTED (1 << 29) #define SRP_INTR_EN (1 << 30) #define GENERATE_SRP (1 << 31) #define USB2_BATTERY_CHRG_OTGPADX_CTL1(x) (0x84 + (x) * 0x40) #define DIV_DET_EN (1 << 4) #define PD_VREG (1 << 6) #define VREG_LEV(x) (((x) & 0x3) << 7) #define VREG_DIR(x) (((x) & 0x3) << 11) #define VREG_DIR_IN VREG_DIR(1) #define VREG_DIR_OUT VREG_DIR(2) #define USBOP_RPD_OVRD (1 << 16) #define USBOP_RPD_OVRD_VAL (1 << 17) #define USBOP_RPU_OVRD (1 << 18) #define USBOP_RPU_OVRD_VAL (1 << 19) #define USBON_RPD_OVRD (1 << 20) #define USBON_RPD_OVRD_VAL (1 << 21) #define USBON_RPU_OVRD (1 << 22) #define USBON_RPU_OVRD_VAL (1 << 23) #define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x88 + (x) * 0x40) #define HS_CURR_LEVEL(x) ((x) & 0x3f) #define TERM_SEL (1 << 25) #define USB2_OTG_PD (1 << 26) #define USB2_OTG_PD2 (1 << 27) #define USB2_OTG_PD2_OVRD_EN (1 << 28) #define USB2_OTG_PD_ZI (1 << 29) #define XUSB_PADCTL_USB2_OTG_PADX_CTL1(x) (0x8c + (x) * 0x40) #define USB2_OTG_PD_DR (1 << 2) #define TERM_RANGE_ADJ(x) (((x) & 0xf) << 3) #define RPD_CTRL(x) (((x) & 0x1f) << 26) #define XUSB_PADCTL_USB2_BATTERY_CHRG_TDCD_DBNC_TIMER_0 (0x280) #define TDCD_DBNC(x) (((x) & 0x7ff) << 0) #define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 (0x284) #define BIAS_PAD_PD (1 << 11) #define HS_SQUELCH_LEVEL(x) (((x) & 0x7) << 0) #define XUSB_PADCTL_USB2_BIAS_PAD_CTL1 (0x288) #define USB2_TRK_START_TIMER(x) (((x) & 0x7f) << 12) #define USB2_TRK_DONE_RESET_TIMER(x) (((x) & 0x7f) << 19) #define USB2_PD_TRK (1 << 26) #define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20) #define HSIC_PD_TX_DATA0 (1 << 1) #define HSIC_PD_TX_STROBE (1 << 3) #define HSIC_PD_RX_DATA0 (1 << 4) #define HSIC_PD_RX_STROBE (1 << 6) #define HSIC_PD_ZI_DATA0 (1 << 7) #define HSIC_PD_ZI_STROBE (1 << 9) #define HSIC_RPD_DATA0 (1 << 13) #define HSIC_RPD_STROBE (1 << 15) #define HSIC_RPU_DATA0 (1 << 16) #define HSIC_RPU_STROBE (1 << 18) #define XUSB_PADCTL_HSIC_PAD_TRK_CTL0 (0x340) #define HSIC_TRK_START_TIMER(x) (((x) & 0x7f) << 5) #define HSIC_TRK_DONE_RESET_TIMER(x) (((x) & 0x7f) << 12) #define HSIC_PD_TRK (1 << 19) #define USB2_VBUS_ID (0x360) #define OTG_VBUS_SESS_VLD (1 << 0) #define OTG_VBUS_SESS_VLD_ST_CHNG (1 << 1) #define OTG_VBUS_SESS_VLD_CHNG_INTR_EN (1 << 2) #define VBUS_VALID (1 << 3) #define VBUS_VALID_ST_CHNG (1 << 4) #define VBUS_VALID_CHNG_INTR_EN (1 << 5) #define IDDIG (1 << 6) #define IDDIG_A (1 << 7) #define IDDIG_B (1 << 8) #define IDDIG_C (1 << 9) #define RID_MASK (0xf << 6) #define IDDIG_ST_CHNG (1 << 10) #define IDDIG_CHNG_INTR_EN (1 << 11) #define VBUS_OVERRIDE (1 << 14) #define ID_OVERRIDE_SHIFT 18 #define ID_OVERRIDE_MASK 0xf #define ID_OVERRIDE(x) (((x) & 0xf) << 18) #define ID_OVERRIDE_FLOATING ID_OVERRIDE(8) #define ID_OVERRIDE_GROUNDED ID_OVERRIDE(0) #define VBUS_WAKEUP (1 << 22) #define VBUS_WAKEUP_ST_CHNG (1 << 23) #define VBUS_WAKEUP_CHNG_INTR_EN (1 << 24) /* XUSB AO registers */ #define XUSB_AO_USB_DEBOUNCE_DEL (0x4) #define UHSIC_LINE_DEB_CNT(x) (((x) & 0xf) << 4) #define UTMIP_LINE_DEB_CNT(x) ((x) & 0xf) #define XUSB_AO_UTMIP_TRIGGERS(x) (0x40 + (x) * 4) #define CLR_WALK_PTR (1 << 0) #define CAP_CFG (1 << 1) #define CLR_WAKE_ALARM (1 << 3) #define XUSB_AO_UHSIC_TRIGGERS(x) (0x60 + (x) * 4) #define HSIC_CLR_WALK_PTR (1 << 0) #define HSIC_CLR_WAKE_ALARM (1 << 3) #define HSIC_CAP_CFG (1 << 4) #define XUSB_AO_UTMIP_SAVED_STATE(x) (0x70 + (x) * 4) #define SPEED(x) ((x) & 0x3) #define UTMI_HS SPEED(0) #define UTMI_FS SPEED(1) #define UTMI_LS SPEED(2) #define UTMI_RST SPEED(3) #define XUSB_AO_UHSIC_SAVED_STATE(x) (0x90 + (x) * 4) #define MODE(x) ((x) & 0x1) #define MODE_HS MODE(1) #define MODE_RST MODE(0) #define XUSB_AO_UTMIP_SLEEPWALK_CFG(x) (0xd0 + (x) * 4) #define XUSB_AO_UHSIC_SLEEPWALK_CFG(x) (0xf0 + (x) * 4) #define FAKE_USBOP_VAL (1 << 0) #define FAKE_USBON_VAL (1 << 1) #define FAKE_USBOP_EN (1 << 2) #define FAKE_USBON_EN (1 << 3) #define FAKE_STROBE_VAL (1 << 0) #define FAKE_DATA_VAL (1 << 1) #define FAKE_STROBE_EN (1 << 2) #define FAKE_DATA_EN (1 << 3) #define WAKE_WALK_EN (1 << 14) #define MASTER_ENABLE (1 << 15) #define LINEVAL_WALK_EN (1 << 16) #define WAKE_VAL(x) (((x) & 0xf) << 17) #define WAKE_VAL_NONE WAKE_VAL(12) #define WAKE_VAL_ANY WAKE_VAL(15) #define WAKE_VAL_DS10 WAKE_VAL(2) #define LINE_WAKEUP_EN (1 << 21) #define MASTER_CFG_SEL (1 << 22) #define XUSB_AO_UTMIP_SLEEPWALK(x) (0x100 + (x) * 4) /* phase A */ #define USBOP_RPD_A (1 << 0) #define USBON_RPD_A (1 << 1) #define AP_A (1 << 4) #define AN_A (1 << 5) #define HIGHZ_A (1 << 6) /* phase B */ #define USBOP_RPD_B (1 << 8) #define USBON_RPD_B (1 << 9) #define AP_B (1 << 12) #define AN_B (1 << 13) #define HIGHZ_B (1 << 14) /* phase C */ #define USBOP_RPD_C (1 << 16) #define USBON_RPD_C (1 << 17) #define AP_C (1 << 20) #define AN_C (1 << 21) #define HIGHZ_C (1 << 22) /* phase D */ #define USBOP_RPD_D (1 << 24) #define USBON_RPD_D (1 << 25) #define AP_D (1 << 28) #define AN_D (1 << 29) #define HIGHZ_D (1 << 30) #define XUSB_AO_UHSIC_SLEEPWALK(x) (0x120 + (x) * 4) /* phase A */ #define RPD_STROBE_A (1 << 0) #define RPD_DATA0_A (1 << 1) #define RPU_STROBE_A (1 << 2) #define RPU_DATA0_A (1 << 3) /* phase B */ #define RPD_STROBE_B (1 << 8) #define RPD_DATA0_B (1 << 9) #define RPU_STROBE_B (1 << 10) #define RPU_DATA0_B (1 << 11) /* phase C */ #define RPD_STROBE_C (1 << 16) #define RPD_DATA0_C (1 << 17) #define RPU_STROBE_C (1 << 18) #define RPU_DATA0_C (1 << 19) /* phase D */ #define RPD_STROBE_D (1 << 24) #define RPD_DATA0_D (1 << 25) #define RPU_STROBE_D (1 << 26) #define RPU_DATA0_D (1 << 27) #define XUSB_AO_UTMIP_PAD_CFG(x) (0x130 + (x) * 4) #define FSLS_USE_XUSB_AO (1 << 3) #define TRK_CTRL_USE_XUSB_AO (1 << 4) #define RPD_CTRL_USE_XUSB_AO (1 << 5) #define RPU_USE_XUSB_AO (1 << 6) #define VREG_USE_XUSB_AO (1 << 7) #define USBOP_VAL_PD (1 << 8) #define USBON_VAL_PD (1 << 9) #define E_DPD_OVRD_EN (1 << 10) #define E_DPD_OVRD_VAL (1 << 11) #define XUSB_AO_UHSIC_PAD_CFG(x) (0x150 + (x) * 4) #define STROBE_VAL_PD (1 << 0) #define DATA0_VAL_PD (1 << 1) #define USE_XUSB_AO (1 << 4) enum tegra186_function { TEGRA186_FUNC_HSIC = 0, TEGRA186_FUNC_XUSB, }; struct tegra_padctl_function { const char *name; const char * const *groups; unsigned int num_groups; }; struct tegra_padctl_group { const unsigned int *funcs; unsigned int num_funcs; }; struct tegra_padctl_soc { const struct pinctrl_pin_desc *pins; unsigned int num_pins; const struct tegra_padctl_function *functions; unsigned int num_functions; const struct tegra_padctl_pad *pads; unsigned int num_pads; unsigned int hsic_port_offset; const char * const *supply_names; unsigned int num_supplies; unsigned int num_oc_pins; }; struct tegra_padctl_pad { const char *name; unsigned int offset; unsigned int shift; unsigned int mask; unsigned int iddq; const unsigned int *funcs; unsigned int num_funcs; }; struct tegra_xusb_fuse_calibration { u32 hs_curr_level[TEGRA_UTMI_PHYS]; u32 hs_squelch; u32 hs_term_range_adj; u32 rpd_ctrl; }; enum xusb_port_cap { CAP_DISABLED = TEGRA_PADCTL_PORT_DISABLED, HOST_ONLY = TEGRA_PADCTL_PORT_HOST_ONLY, DEVICE_ONLY = TEGRA_PADCTL_PORT_DEVICE_ONLY, OTG = TEGRA_PADCTL_PORT_OTG_CAP, }; struct tegra_xusb_usb3_port { enum xusb_port_cap port_cap; int oc_pin; }; struct tegra_xusb_utmi_port { enum xusb_port_cap port_cap; int hs_curr_level_offset; /* deal with platform design deviation */ bool poweron; int oc_pin; }; struct tegra_xusb_hsic_port { bool pretend_connected; }; struct padctl_context { u32 vbus_id; u32 usb2_pad_mux; u32 usb2_port_cap; u32 ss_port_cap; }; struct tegra_padctl { struct device *dev; void __iomem *padctl_regs; void __iomem *ao_regs; struct reset_control *padctl_rst; struct clk *xusb_clk; /* xusb main clock */ struct clk *utmipll; /* utmi pads */ struct clk *usb2_trk_clk; /* utmi tracking circuit clock */ struct clk *hsic_trk_clk; /* hsic tracking circuit clock */ struct mutex lock; const struct tegra_padctl_soc *soc; struct tegra_xusb_fuse_calibration calib; struct tegra_prod *prod_list; struct pinctrl_dev *pinctrl; struct pinctrl_desc desc; struct phy_provider *provider; struct phy *usb3_phys[TEGRA_USB3_PHYS]; struct phy *utmi_phys[TEGRA_UTMI_PHYS]; struct phy *hsic_phys[TEGRA_HSIC_PHYS]; struct phy *cdp_phys[TEGRA_CDP_PHYS]; struct tegra_xusb_hsic_port hsic_ports[TEGRA_HSIC_PHYS]; struct tegra_xusb_utmi_port utmi_ports[TEGRA_UTMI_PHYS]; int utmi_otg_port_base_1; /* one based utmi port number */ struct tegra_xusb_usb3_port usb3_ports[TEGRA_USB3_PHYS]; int usb3_otg_port_base_1; /* one based usb3 port number */ struct work_struct mbox_req_work; struct tegra_xusb_mbox_msg mbox_req; struct mbox_client mbox_client; struct mbox_chan *mbox_chan; bool host_mode_phy_disabled; /* set true if mailbox is not available */ unsigned int bias_pad_enable; /* TODO: should move to host controller driver? */ struct regulator *vbus[TEGRA_UTMI_PHYS]; struct regulator *vddio_hsic; bool otg_vbus_alwayson; struct regulator_bulk_data *supplies; struct padctl_context padctl_context; bool cdp_used; struct pinctrl *oc_pinctrl; /* * array of pinctrl_state (of number num_oc_pins) * for different OC states */ struct pinctrl_state **oc_tristate_enable; struct pinctrl_state **oc_passthrough_enable; struct pinctrl_state **oc_disable; }; #ifdef VERBOSE_DEBUG #define ao_writel(_padctl, _value, _offset) \ { \ unsigned long v = _value, o = _offset; \ pr_debug("%s ao_writel %s(@0x%lx) with 0x%lx\n", __func__, \ #_offset, o, v); \ writel(v, _padctl->ao_regs + o); \ } #define ao_readl(_padctl, _offset) \ ({ \ unsigned long v, o = _offset; \ v = readl(_padctl->ao_regs + o); \ pr_debug("%s ao_readl %s(@0x%lx) = 0x%lx\n", __func__, \ #_offset, o, v); \ v; \ }) #else static inline void ao_writel(struct tegra_padctl *padctl, u32 value, unsigned long offset) { writel(value, padctl->ao_regs + offset); } static inline u32 ao_readl(struct tegra_padctl *padctl, unsigned long offset) { return readl(padctl->ao_regs + offset); } #endif #ifdef VERBOSE_DEBUG #define padctl_writel(_padctl, _value, _offset) \ { \ unsigned long v = _value, o = _offset; \ pr_debug("%s padctl_write %s(@0x%lx) with 0x%lx\n", __func__, \ #_offset, o, v); \ writel(v, _padctl->padctl_regs + o); \ } #define padctl_readl(_padctl, _offset) \ ({ \ unsigned long v, o = _offset; \ v = readl(_padctl->padctl_regs + o); \ pr_debug("%s padctl_read %s(@0x%lx) = 0x%lx\n", __func__, \ #_offset, o, v); \ v; \ }) #else static inline void padctl_writel(struct tegra_padctl *padctl, u32 value, unsigned long offset) { writel(value, padctl->padctl_regs + offset); } static inline u32 padctl_readl(struct tegra_padctl *padctl, unsigned long offset) { return readl(padctl->padctl_regs + offset); } #endif static int tegra186_padctl_regulators_init(struct tegra_padctl *padctl) { struct device *dev = padctl->dev; size_t size; int err; int i; size = padctl->soc->num_supplies * sizeof(struct regulator_bulk_data); padctl->supplies = devm_kzalloc(dev, size, GFP_ATOMIC); if (!padctl->supplies) { dev_err(dev, "failed to alloc memory for regulators\n"); return -ENOMEM; } for (i = 0; i < padctl->soc->num_supplies; i++) padctl->supplies[i].supply = padctl->soc->supply_names[i]; err = devm_regulator_bulk_get(dev, padctl->soc->num_supplies, padctl->supplies); if (err) { dev_err(dev, "failed to request regulators %d\n", err); return err; } return 0; } static inline struct tegra_padctl *mbox_work_to_padctl(struct work_struct *work) { return container_of(work, struct tegra_padctl, mbox_req_work); } #define PIN_OTG_0 0 #define PIN_OTG_1 1 #define PIN_OTG_2 2 #define PIN_HSIC_0 3 #define PIN_USB3_0 4 #define PIN_USB3_1 5 #define PIN_USB3_2 6 #define PIN_CDP_0 7 #define PIN_CDP_1 8 #define PIN_CDP_2 9 static inline bool pad_is_otg(unsigned int pad) { return pad >= PIN_OTG_0 && pad <= PIN_OTG_2; } static inline bool pad_is_hsic(unsigned int pad) { return pad == PIN_HSIC_0; } static inline bool pad_is_usb3(unsigned int pad) { return pad >= PIN_USB3_0 && pad <= PIN_USB3_2; } static inline bool pad_is_cdp(unsigned int pad) { return pad >= PIN_CDP_0 && pad <= PIN_CDP_2; } static int tegra_padctl_get_groups_count(struct pinctrl_dev *pinctrl) { struct tegra_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl); TRACE(padctl->dev, "num_pins %u", padctl->soc->num_pins); return padctl->soc->num_pins; } static const char *tegra_padctl_get_group_name(struct pinctrl_dev *pinctrl, unsigned int group) { struct tegra_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl); struct device *dev = padctl->dev; TRACE(dev, "group %u name %s", group, padctl->soc->pins[group].name); return padctl->soc->pins[group].name; } static int tegra_padctl_get_group_pins(struct pinctrl_dev *pinctrl, unsigned group, const unsigned **pins, unsigned *num_pins) { struct tegra_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl); struct device *d = padctl->dev; *pins = &padctl->soc->pins[group].number; *num_pins = 1; /* one pin per group */ TRACE(d, "group %u num_pins %u pins[0] %u", group, *num_pins, *pins[0]); return 0; } enum tegra_xusb_padctl_param { TEGRA_PADCTL_PORT_CAP, TEGRA_PADCTL_HSIC_PRETEND_CONNECTED, TEGRA_PADCTL_UTMI_HS_CURR_LEVEL_OFFSET, TEGRA_PADCTL_OC_PIN, }; static const struct tegra_padctl_property { const char *name; enum tegra_xusb_padctl_param param; } properties[] = { {"nvidia,port-cap", TEGRA_PADCTL_PORT_CAP}, {"nvidia,pretend-connected", TEGRA_PADCTL_HSIC_PRETEND_CONNECTED}, {"nvidia,hs_curr_level_offset", TEGRA_PADCTL_UTMI_HS_CURR_LEVEL_OFFSET}, {"nvidia,oc-pin", TEGRA_PADCTL_OC_PIN}, }; #define TEGRA_XUSB_PADCTL_PACK(param, value) ((param) << 16 | (value & 0xffff)) #define TEGRA_XUSB_PADCTL_UNPACK_PARAM(config) ((config) >> 16) #define TEGRA_XUSB_PADCTL_UNPACK_VALUE(config) ((config) & 0xffff) static int tegra186_padctl_parse_subnode(struct tegra_padctl *padctl, struct device_node *np, struct pinctrl_map **maps, unsigned int *reserved_maps, unsigned int *num_maps) { unsigned int i, reserve = 0, num_configs = 0; unsigned long config, *configs = NULL; const char *function, *group; struct property *prop; int err = 0; u32 value; err = of_property_read_string(np, "nvidia,function", &function); if (err < 0) { if (err != -EINVAL) goto out; function = NULL; } for (i = 0; i < ARRAY_SIZE(properties); i++) { err = of_property_read_u32(np, properties[i].name, &value); if (err < 0) { if (err == -EINVAL) continue; goto out; } config = TEGRA_XUSB_PADCTL_PACK(properties[i].param, value); err = pinctrl_utils_add_config(padctl->pinctrl, &configs, &num_configs, config); if (err < 0) goto out; } if (function) reserve++; if (num_configs) reserve++; err = of_property_count_strings(np, "nvidia,lanes"); if (err < 0) goto out; reserve *= err; err = pinctrl_utils_reserve_map(padctl->pinctrl, maps, reserved_maps, num_maps, reserve); if (err < 0) goto out; of_property_for_each_string(np, "nvidia,lanes", prop, group) { if (function) { err = pinctrl_utils_add_map_mux(padctl->pinctrl, maps, reserved_maps, num_maps, group, function); if (err < 0) goto out; } if (num_configs) { err = pinctrl_utils_add_map_configs(padctl->pinctrl, maps, reserved_maps, num_maps, group, configs, num_configs, PIN_MAP_TYPE_CONFIGS_GROUP); if (err < 0) goto out; } } err = 0; out: kfree(configs); return err; } static int tegra_padctl_dt_node_to_map(struct pinctrl_dev *pinctrl, struct device_node *parent, struct pinctrl_map **maps, unsigned int *num_maps) { struct tegra_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl); unsigned int reserved_maps = 0; struct device_node *np; int err; *num_maps = 0; *maps = NULL; for_each_child_of_node(parent, np) { /* If node status is disabled then ignore the node */ if (!of_device_is_available(np)) continue; err = tegra186_padctl_parse_subnode(padctl, np, maps, &reserved_maps, num_maps); if (err < 0) { pr_info("%s %d err %d\n", __func__, __LINE__, err); return err; } } return 0; } static const struct pinctrl_ops tegra_xusb_padctl_pinctrl_ops = { .get_groups_count = tegra_padctl_get_groups_count, .get_group_name = tegra_padctl_get_group_name, .get_group_pins = tegra_padctl_get_group_pins, .dt_node_to_map = tegra_padctl_dt_node_to_map, /* API changed after 4.6.0-rc1 */ #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) .dt_free_map = pinctrl_utils_dt_free_map, #else .dt_free_map = pinctrl_utils_free_map, #endif }; static int tegra186_padctl_get_functions_count(struct pinctrl_dev *pinctrl) { struct tegra_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl); TRACE(padctl->dev, "num_functions %u", padctl->soc->num_functions); return padctl->soc->num_functions; } static const char * tegra186_padctl_get_function_name(struct pinctrl_dev *pinctrl, unsigned int function) { struct tegra_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl); TRACE(padctl->dev, "function %u name %s", function, padctl->soc->functions[function].name); return padctl->soc->functions[function].name; } static int tegra186_padctl_get_function_groups(struct pinctrl_dev *pinctrl, unsigned int function, const char * const **groups, unsigned * const num_groups) { struct tegra_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl); *num_groups = padctl->soc->functions[function].num_groups; *groups = padctl->soc->functions[function].groups; TRACE(padctl->dev, "function %u *num_groups %u groups %s", function, *num_groups, *groups[0]); return 0; } static int tegra186_padctl_pinmux_set(struct pinctrl_dev *pinctrl, unsigned int function, unsigned int group) { struct tegra_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl); const struct tegra_padctl_pad *pad; unsigned int i; u32 value; pad = &padctl->soc->pads[group]; TRACE(padctl->dev, "group %u (%s) function %u num_funcs %d", group, pad->name, function, pad->num_funcs); for (i = 0; i < pad->num_funcs; i++) { TRACE(padctl->dev, "lane->funcs[%d] %d\n", i, pad->funcs[i]); if (pad->funcs[i] == function) break; } if (i >= pad->num_funcs) return -EINVAL; TRACE(padctl->dev, "group %s set to function %s", pad->name, padctl->soc->functions[function].name); if (pad_is_otg(group)) { value = padctl_readl(padctl, pad->offset); value &= ~(pad->mask << pad->shift); value |= (PORT_XUSB << pad->shift); padctl_writel(padctl, value, pad->offset); } else if (pad_is_hsic(group)) { value = padctl_readl(padctl, pad->offset); value &= ~(pad->mask << pad->shift); value |= (PORT_HSIC << pad->shift); padctl_writel(padctl, value, pad->offset); } else if (pad_is_cdp(group)) { if (function != TEGRA186_FUNC_XUSB) dev_warn(padctl->dev, "group %s isn't for xusb!", pad->name); } else return -EINVAL; return 0; } static const struct pinmux_ops tegra186_padctl_pinmux_ops = { .get_functions_count = tegra186_padctl_get_functions_count, .get_function_name = tegra186_padctl_get_function_name, .get_function_groups = tegra186_padctl_get_function_groups, .set_mux = tegra186_padctl_pinmux_set, }; static int tegra_padctl_pinconf_group_get(struct pinctrl_dev *pinctrl, unsigned int group, unsigned long *config) { struct tegra_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl); enum tegra_xusb_padctl_param param; int value = 0; param = TEGRA_XUSB_PADCTL_UNPACK_PARAM(*config); TRACE(padctl->dev, "group %u param 0x%x\n", group, param); switch (param) { default: dev_err(padctl->dev, "invalid configuration parameter: %04x\n", param); return -ENOTSUPP; } *config = TEGRA_XUSB_PADCTL_PACK(param, value); return 0; } static int tegra_padctl_pinconf_group_set(struct pinctrl_dev *pinctrl, unsigned int group, unsigned long *configs, unsigned num_configs) { struct tegra_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl); struct device *dev = padctl->dev; enum tegra_xusb_padctl_param param; unsigned long value; int port; s16 offset; int i; for (i = 0; i < num_configs; i++) { param = TEGRA_XUSB_PADCTL_UNPACK_PARAM(configs[i]); value = TEGRA_XUSB_PADCTL_UNPACK_VALUE(configs[i]); TRACE(dev, "group %u config 0x%lx param 0x%x value 0x%lx", group, configs[i], param, value); switch (param) { case TEGRA_PADCTL_PORT_CAP: if (value > TEGRA_PADCTL_PORT_OTG_CAP) { dev_err(dev, "Invalid port-cap: %lu\n", value); return -EINVAL; } if (pad_is_usb3(group)) { int port = group - PIN_USB3_0; padctl->usb3_ports[port].port_cap = value; TRACE(dev, "USB3 port %d cap %lu", port, value); if (value == OTG) { if (padctl->usb3_otg_port_base_1) dev_warn(dev, "enabling OTG on multiple USB3 ports\n"); dev_info(dev, "using USB3 port %d for otg\n", port); padctl->usb3_otg_port_base_1 = port + 1; } } else if (pad_is_otg(group)) { int port = group - PIN_OTG_0; padctl->utmi_ports[port].port_cap = value; TRACE(dev, "UTMI port %d cap %lu", port, value); if (value == OTG) { if (padctl->utmi_otg_port_base_1) dev_warn(dev, "enabling OTG on multiple UTMI ports\n"); dev_info(dev, "using UTMI port %d for otg\n", port); padctl->utmi_otg_port_base_1 = port + 1; } } else { dev_err(dev, "port-cap not applicable for pin %d\n", group); return -EINVAL; } break; case TEGRA_PADCTL_HSIC_PRETEND_CONNECTED: if (!pad_is_hsic(group)) { dev_err(dev, "pretend-connected is not applicable for pin %d\n", group); return -EINVAL; } port = group - PIN_HSIC_0; padctl->hsic_ports[port].pretend_connected = value; TRACE(dev, "HSIC port %d pretend-connected %ld", port, value); break; case TEGRA_PADCTL_UTMI_HS_CURR_LEVEL_OFFSET: if (!pad_is_otg(group)) { dev_err(dev, "hs_curr_level_offset is not applicable for pin %d\n", group); return -EINVAL; } port = group - PIN_OTG_0; offset = TEGRA_XUSB_PADCTL_UNPACK_VALUE(configs[i]); padctl->utmi_ports[port].hs_curr_level_offset = offset; TRACE(dev, "UTMI port %d hs_curr_level_offset %d", port, offset); break; case TEGRA_PADCTL_OC_PIN: if (pad_is_usb3(group)) { int port = group - PIN_USB3_0; if (value >= padctl->soc->num_oc_pins) { dev_err(dev, "Invalid OC pin: %lu\n", value); return -EINVAL; } TRACE(dev, "USB3 port %d OC pin %lu", port, value); padctl->usb3_ports[port].oc_pin = (int) value; } else if (pad_is_otg(group)) { int port = group - PIN_OTG_0; if (value >= padctl->soc->num_oc_pins) { dev_err(dev, "Invalid OC pin: %lu\n", value); return -EINVAL; } TRACE(dev, "USB2 port %d OC pin %lu", port, value); padctl->utmi_ports[port].oc_pin = (int) value; } break; default: dev_err(dev, "invalid configuration parameter: %04x\n", param); return -ENOTSUPP; } } return 0; } #ifdef CONFIG_DEBUG_FS static const char *strip_prefix(const char *s) { const char *comma = strchr(s, ','); if (!comma) return s; return comma + 1; } static void tegra_padctl_pinconf_group_dbg_show(struct pinctrl_dev *pinctrl, struct seq_file *s, unsigned int group) { unsigned int i; for (i = 0; i < ARRAY_SIZE(properties); i++) { unsigned long config, value; int err; config = TEGRA_XUSB_PADCTL_PACK(properties[i].param, 0); err = tegra_padctl_pinconf_group_get(pinctrl, group, &config); if (err < 0) continue; value = TEGRA_XUSB_PADCTL_UNPACK_VALUE(config); seq_printf(s, "\n\t%s=%lu\n", strip_prefix(properties[i].name), value); } } static void tegra_padctl_pinconf_config_dbg_show(struct pinctrl_dev *pinctrl, struct seq_file *s, unsigned long config) { enum tegra_xusb_padctl_param param; const char *name = "unknown"; unsigned long value; unsigned int i; param = TEGRA_XUSB_PADCTL_UNPACK_PARAM(config); value = TEGRA_XUSB_PADCTL_UNPACK_VALUE(config); for (i = 0; i < ARRAY_SIZE(properties); i++) { if (properties[i].param == param) { name = properties[i].name; break; } } seq_printf(s, "%s=%lu", strip_prefix(name), value); } #endif static const struct pinconf_ops tegra_padctl_pinconf_ops = { .pin_config_group_get = tegra_padctl_pinconf_group_get, .pin_config_group_set = tegra_padctl_pinconf_group_set, #ifdef CONFIG_DEBUG_FS .pin_config_group_dbg_show = tegra_padctl_pinconf_group_dbg_show, .pin_config_config_dbg_show = tegra_padctl_pinconf_config_dbg_show, #endif }; static int usb3_phy_to_port(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); unsigned int i; for (i = 0; i < TEGRA_USB3_PHYS; i++) { if (phy == padctl->usb3_phys[i]) return i; } WARN_ON(1); return -EINVAL; } static int tegra186_usb3_phy_power_on(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); int port = usb3_phy_to_port(phy); int pin; u32 reg; if (port < 0) return port; pin = padctl->usb3_ports[port].oc_pin; mutex_lock(&padctl->lock); dev_dbg(padctl->dev, "power on USB3 port %d\n", port); reg = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CAP); reg &= ~(PORT_CAP_MASK << PORTX_CAP_SHIFT(port)); if (padctl->usb3_ports[port].port_cap == CAP_DISABLED) reg |= (PORT_CAP_DISABLED << PORTX_CAP_SHIFT(port)); else if (padctl->usb3_ports[port].port_cap == DEVICE_ONLY) reg |= (PORT_CAP_DEVICE << PORTX_CAP_SHIFT(port)); else if (padctl->usb3_ports[port].port_cap == HOST_ONLY) reg |= (PORT_CAP_HOST << PORTX_CAP_SHIFT(port)); else if (padctl->usb3_ports[port].port_cap == OTG) reg |= (PORT_CAP_OTG << PORTX_CAP_SHIFT(port)); padctl_writel(padctl, reg, XUSB_PADCTL_SS_PORT_CAP); /* setting SS OC map */ if (pin >= 0) { reg = padctl_readl(padctl, XUSB_PADCTL_SS_OC_MAP); reg &= ~(PORT_OC_PIN_MASK << PORTX_OC_PIN_SHIFT(port)); reg |= (OC_PIN_DETECTED_VBUS_PAD(pin) & PORT_OC_PIN_MASK) << PORTX_OC_PIN_SHIFT(port); padctl_writel(padctl, reg, XUSB_PADCTL_SS_OC_MAP); } reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); reg &= ~SSPX_ELPG_VCORE_DOWN(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM_1); usleep_range(100, 200); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); reg &= ~SSPX_ELPG_CLAMP_EN_EARLY(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM_1); usleep_range(100, 200); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); reg &= ~SSPX_ELPG_CLAMP_EN(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM_1); mutex_unlock(&padctl->lock); return 0; } static int tegra186_usb3_phy_power_off(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); struct device *dev = padctl->dev; int port = usb3_phy_to_port(phy); u32 reg; if (port < 0) return port; mutex_lock(&padctl->lock); dev_dbg(dev, "power off USB3 port %d\n", port); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); reg |= SSPX_ELPG_CLAMP_EN_EARLY(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM_1); usleep_range(100, 200); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); reg |= SSPX_ELPG_CLAMP_EN(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM_1); usleep_range(250, 350); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); reg |= SSPX_ELPG_VCORE_DOWN(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM_1); mutex_unlock(&padctl->lock); return 0; } static int tegra186_usb3_phy_enable_wakelogic(struct tegra_padctl *padctl, int port) { struct device *dev = padctl->dev; u32 reg; dev_dbg(dev, "enable wakelogic USB3 port %d\n", port); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); reg |= SSPX_ELPG_CLAMP_EN_EARLY(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM_1); usleep_range(100, 200); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); reg |= SSPX_ELPG_CLAMP_EN(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM_1); usleep_range(250, 350); return 0; } static int tegra186_usb3_phy_disable_wakelogic(struct tegra_padctl *padctl, int port) { struct device *dev = padctl->dev; u32 reg; dev_dbg(dev, "disable wakelogic USB3 port %d\n", port); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); reg &= ~SSPX_ELPG_CLAMP_EN_EARLY(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM_1); usleep_range(100, 200); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1); reg &= ~SSPX_ELPG_CLAMP_EN(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM_1); return 0; } static int tegra186_usb3_phy_init(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); int port = usb3_phy_to_port(phy); if (port < 0) return port; mutex_lock(&padctl->lock); dev_dbg(padctl->dev, "phy init USB3 port %d\n", port); mutex_unlock(&padctl->lock); return 0; } static int tegra186_usb3_phy_exit(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); struct device *dev = padctl->dev; int port = usb3_phy_to_port(phy); if (port < 0) return port; mutex_lock(&padctl->lock); dev_dbg(dev, "phy exit USB3 port %d\n", port); mutex_unlock(&padctl->lock); return 0; } static const struct phy_ops usb3_phy_ops = { .init = tegra186_usb3_phy_init, .exit = tegra186_usb3_phy_exit, .power_on = tegra186_usb3_phy_power_on, .power_off = tegra186_usb3_phy_power_off, .owner = THIS_MODULE, }; static inline bool is_usb3_phy(struct phy *phy) { return phy->ops == &usb3_phy_ops; } static int utmi_phy_to_port(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); unsigned int i; for (i = 0; i < TEGRA_UTMI_PHYS; i++) { if (phy == padctl->utmi_phys[i]) return i; } WARN_ON(1); return -EINVAL; } static int cdp_phy_to_port(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); unsigned int i; for (i = 0; i < TEGRA_CDP_PHYS; i++) { if (phy == padctl->cdp_phys[i]) return i; } WARN_ON(1); return -EINVAL; } static int tegra186_utmi_phy_enable_sleepwalk(struct tegra_padctl *padctl, int port, enum usb_device_speed speed) { u32 reg; dev_dbg(padctl->dev, "enable sleepwalk UTMI port %d speed %d\n", port, speed); /* ensure sleepwalk logic is disabled */ reg = ao_readl(padctl, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); reg &= ~MASTER_ENABLE; ao_writel(padctl, reg, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); /* ensure sleepwalk logics are in low power mode */ reg = ao_readl(padctl, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); reg |= MASTER_CFG_SEL; ao_writel(padctl, reg, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); /* set debounce time */ reg = ao_readl(padctl, XUSB_AO_USB_DEBOUNCE_DEL); reg &= ~UTMIP_LINE_DEB_CNT(~0); reg |= UTMIP_LINE_DEB_CNT(1); ao_writel(padctl, reg, XUSB_AO_USB_DEBOUNCE_DEL); /* ensure fake events of sleepwalk logic are desiabled */ reg = ao_readl(padctl, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); reg &= ~(FAKE_USBOP_VAL | FAKE_USBON_VAL | FAKE_USBOP_EN | FAKE_USBON_EN); ao_writel(padctl, reg, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); /* ensure wake events of sleepwalk logic are not latched */ reg = ao_readl(padctl, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); reg &= ~LINE_WAKEUP_EN; ao_writel(padctl, reg, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); /* disable wake event triggers of sleepwalk logic */ reg = ao_readl(padctl, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); reg &= ~WAKE_VAL(~0); reg |= WAKE_VAL_NONE; ao_writel(padctl, reg, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); /* power down the line state detectors of the pad */ reg = ao_readl(padctl, XUSB_AO_UTMIP_PAD_CFG(port)); reg |= (USBOP_VAL_PD | USBON_VAL_PD); ao_writel(padctl, reg, XUSB_AO_UTMIP_PAD_CFG(port)); /* save state per speed */ reg = ao_readl(padctl, XUSB_AO_UTMIP_SAVED_STATE(port)); reg &= ~SPEED(~0); if (speed == USB_SPEED_HIGH) reg |= UTMI_HS; else if (speed == USB_SPEED_FULL) reg |= UTMI_FS; else if (speed == USB_SPEED_LOW) reg |= UTMI_LS; else reg |= UTMI_RST; ao_writel(padctl, reg, XUSB_AO_UTMIP_SAVED_STATE(port)); /* enable the trigger of the sleepwalk logic */ reg = ao_readl(padctl, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); reg |= LINEVAL_WALK_EN; reg &= ~WAKE_WALK_EN; ao_writel(padctl, reg, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); /* reset the walk pointer and clear the alarm of the sleepwalk logic, * as well as capture the configuration of the USB2.0 pad */ reg = ao_readl(padctl, XUSB_AO_UTMIP_TRIGGERS(port)); reg |= (CLR_WALK_PTR | CLR_WAKE_ALARM | CAP_CFG); ao_writel(padctl, reg, XUSB_AO_UTMIP_TRIGGERS(port)); /* setup the pull-ups and pull-downs of the signals during the four * stages of sleepwalk. * if device is connected, program sleepwalk logic to maintain a J and * keep driving K upon seeing remote wake. */ reg = (USBOP_RPD_A | USBOP_RPD_B | USBOP_RPD_C | USBOP_RPD_D); reg |= (USBON_RPD_A | USBON_RPD_B | USBON_RPD_C | USBON_RPD_D); if (speed == USB_SPEED_UNKNOWN) { reg |= (HIGHZ_A | HIGHZ_B | HIGHZ_C | HIGHZ_D); } else if ((speed == USB_SPEED_HIGH) || (speed == USB_SPEED_FULL)) { /* J state: D+/D- = high/low, K state: D+/D- = low/high */ reg |= HIGHZ_A; reg |= (AP_A); reg |= (AN_B | AN_C | AN_D); } else if (speed == USB_SPEED_LOW) { /* J state: D+/D- = low/high, K state: D+/D- = high/low */ reg |= HIGHZ_A; reg |= AN_A; reg |= (AP_B | AP_C | AP_D); } ao_writel(padctl, reg, XUSB_AO_UTMIP_SLEEPWALK(port)); /* power up the line state detectors of the pad */ reg = ao_readl(padctl, XUSB_AO_UTMIP_PAD_CFG(port)); reg &= ~(USBOP_VAL_PD | USBON_VAL_PD); ao_writel(padctl, reg, XUSB_AO_UTMIP_PAD_CFG(port)); usleep_range(150, 200); /* switch the electric control of the USB2.0 pad to XUSB_AO */ reg = ao_readl(padctl, XUSB_AO_UTMIP_PAD_CFG(port)); reg |= (FSLS_USE_XUSB_AO | TRK_CTRL_USE_XUSB_AO | RPD_CTRL_USE_XUSB_AO | RPU_USE_XUSB_AO | VREG_USE_XUSB_AO); ao_writel(padctl, reg, XUSB_AO_UTMIP_PAD_CFG(port)); /* set the wake signaling trigger events */ reg = ao_readl(padctl, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); reg &= ~WAKE_VAL(~0); reg |= WAKE_VAL_ANY; ao_writel(padctl, reg, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); /* enable the wake detection */ reg = ao_readl(padctl, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); reg |= (MASTER_ENABLE | LINE_WAKEUP_EN); ao_writel(padctl, reg, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); return 0; } static int tegra186_utmi_phy_disable_sleepwalk(struct tegra_padctl *padctl, int port) { u32 reg; dev_dbg(padctl->dev, "disable sleepwalk UTMI port %d\n", port); /* disable the wake detection */ reg = ao_readl(padctl, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); reg &= ~(MASTER_ENABLE | LINE_WAKEUP_EN); ao_writel(padctl, reg, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); /* switch the electric control of the USB2.0 pad to XUSB vcore logic */ reg = ao_readl(padctl, XUSB_AO_UTMIP_PAD_CFG(port)); reg &= ~(FSLS_USE_XUSB_AO | TRK_CTRL_USE_XUSB_AO | RPD_CTRL_USE_XUSB_AO | RPU_USE_XUSB_AO | VREG_USE_XUSB_AO); ao_writel(padctl, reg, XUSB_AO_UTMIP_PAD_CFG(port)); /* disable wake event triggers of sleepwalk logic */ reg = ao_readl(padctl, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); reg &= ~WAKE_VAL(~0); reg |= WAKE_VAL_NONE; ao_writel(padctl, reg, XUSB_AO_UTMIP_SLEEPWALK_CFG(port)); /* power down the line state detectors of the port */ reg = ao_readl(padctl, XUSB_AO_UTMIP_PAD_CFG(port)); reg |= (USBOP_VAL_PD | USBON_VAL_PD); ao_writel(padctl, reg, XUSB_AO_UTMIP_PAD_CFG(port)); /* clear alarm of the sleepwalk logic */ reg = ao_readl(padctl, XUSB_AO_UTMIP_TRIGGERS(port)); reg |= CLR_WAKE_ALARM; ao_writel(padctl, reg, XUSB_AO_UTMIP_TRIGGERS(port)); return 0; } static void tegra186_utmi_bias_pad_power_on(struct tegra_padctl *padctl) { u32 reg; if (padctl->bias_pad_enable++ > 0) return; reg = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); reg &= ~USB2_TRK_START_TIMER(~0); reg |= USB2_TRK_START_TIMER(0x1e); reg &= ~USB2_TRK_DONE_RESET_TIMER(~0); reg |= USB2_TRK_DONE_RESET_TIMER(0xa); padctl_writel(padctl, reg, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); reg &= ~BIAS_PAD_PD; reg &= ~HS_SQUELCH_LEVEL(~0); reg |= HS_SQUELCH_LEVEL(padctl->calib.hs_squelch); padctl_writel(padctl, reg, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); udelay(1); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); reg &= ~USB2_PD_TRK; padctl_writel(padctl, reg, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); } static void tegra186_utmi_bias_pad_power_off(struct tegra_padctl *padctl) { u32 reg; if (WARN_ON(padctl->bias_pad_enable == 0)) return; if (--padctl->bias_pad_enable > 0) return; if (!padctl->cdp_used) { /* only turn BIAS pad off when host CDP isn't enabled */ reg = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); reg |= BIAS_PAD_PD; padctl_writel(padctl, reg, XUSB_PADCTL_USB2_BIAS_PAD_CTL0); } reg = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); reg |= USB2_PD_TRK; padctl_writel(padctl, reg, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); } void tegra18x_phy_xusb_utmi_pad_power_on(struct phy *phy) { struct tegra_padctl *padctl; int port; u32 reg; if (!phy) return; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); if (padctl->utmi_ports[port].poweron) return; tegra186_utmi_bias_pad_power_on(padctl); udelay(2); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg &= ~USB2_OTG_PD; padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port)); reg &= ~USB2_OTG_PD_DR; padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port)); padctl->utmi_ports[port].poweron = true; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_utmi_pad_power_on); void tegra18x_phy_xusb_utmi_pad_power_down(struct phy *phy) { struct tegra_padctl *padctl; int port; u32 reg; if (!phy) return; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); if (!padctl->utmi_ports[port].poweron) return; reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg |= USB2_OTG_PD; padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port)); reg |= USB2_OTG_PD_DR; padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port)); udelay(2); tegra186_utmi_bias_pad_power_off(padctl); padctl->utmi_ports[port].poweron = false; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_utmi_pad_power_down); #define oc_debug(u) \ dev_dbg(u->dev, "%s(%d):OC_DET %#x, VBUS_OC_MAP %#x, "\ "USB2_OC_MAP %#x, SS_OC_MAP %#x\n",\ __func__, __LINE__,\ padctl_readl(u, XUSB_PADCTL_OC_DET), \ padctl_readl(u, XUSB_PADCTL_VBUS_OC_MAP), \ padctl_readl(u, XUSB_PADCTL_USB2_OC_MAP), \ padctl_readl(u, XUSB_PADCTL_SS_OC_MAP)); /* should only be called with a UTMI phy and with padctl->lock held */ static void tegra186_enable_vbus_oc(struct phy *phy) { struct tegra_padctl *padctl; int port, pin; u32 reg; if (!phy) return; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); if (!padctl->oc_pinctrl) { dev_dbg(padctl->dev, "%s no OC pinctrl device\n", __func__); return; } if (port < 0) { dev_warn(padctl->dev, "%s wrong port %d\n", __func__, port); return; } pin = padctl->utmi_ports[port].oc_pin; if (pin < 0) { dev_dbg(padctl->dev, "%s no OC support for port %d\n", __func__, port); return; } dev_dbg(padctl->dev, "enable VBUS/OC on UTMI port %d, pin %d\n", port, pin); /* initialize OC: step 7 in PG p.1272 */ reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OC_MAP); reg &= ~(PORT_OC_PIN_MASK << PORTX_OC_PIN_SHIFT(port)); reg |= OC_PIN_DETECTION_DISABLED << PORTX_OC_PIN_SHIFT(port); padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OC_MAP); /* need to disable VBUS_ENABLEx_OC_MAP before enabling VBUS */ reg = padctl_readl(padctl, XUSB_PADCTL_VBUS_OC_MAP); reg &= ~(VBUS_OC_MAP_MASK << VBUS_OC_MAP_SHIFT(pin)); reg |= VBUS_OC_DETECTION_DISABLED << VBUS_OC_MAP_SHIFT(pin); padctl_writel(padctl, reg, XUSB_PADCTL_VBUS_OC_MAP); /* WAR: disable UTMIPLL power down, not needed for current clk * framework */ /* clear false OC_DETECTED VBUS_PADx */ reg = padctl_readl(padctl, XUSB_PADCTL_OC_DET); reg &= ~OC_DETECTED_VBUS_PAD_MASK; reg |= OC_DETECTED_VBUS_PAD(pin); padctl_writel(padctl, reg, XUSB_PADCTL_OC_DET); udelay(100); /* WAR: enable UTMIPLL power down, not needed for current clk * framework */ /* Enable VBUS */ reg = padctl_readl(padctl, XUSB_PADCTL_VBUS_OC_MAP); reg |= VBUS_ENABLE(pin); padctl_writel(padctl, reg, XUSB_PADCTL_VBUS_OC_MAP); /* vbus has been supplied to device. A finite time (>10ms) for OC * detection pin to be pulled-up */ msleep(20); /* check and clear if there is any stray OC */ reg = padctl_readl(padctl, XUSB_PADCTL_OC_DET); if (reg & OC_DETECTED_VBUS_PAD(pin)) { /* clear stray OC */ dev_dbg(padctl->dev, "clear stray OC on port %d pin %d, OC_DET=%#x\n", port, pin, reg); reg = padctl_readl(padctl, XUSB_PADCTL_VBUS_OC_MAP); reg &= ~VBUS_ENABLE(pin); reg = padctl_readl(padctl, XUSB_PADCTL_OC_DET); reg &= ~OC_DETECTED_VBUS_PAD_MASK; reg |= OC_DETECTED_VBUS_PAD(pin); padctl_writel(padctl, reg, XUSB_PADCTL_OC_DET); /* Enable VBUS back after clearing stray OC */ reg = padctl_readl(padctl, XUSB_PADCTL_VBUS_OC_MAP); reg |= VBUS_ENABLE(pin); padctl_writel(padctl, reg, XUSB_PADCTL_VBUS_OC_MAP); } /* change the OC_MAP source and enable OC interrupt */ reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OC_MAP); reg &= ~(PORT_OC_PIN_MASK << PORTX_OC_PIN_SHIFT(port)); reg |= (OC_PIN_DETECTED_VBUS_PAD(pin) & PORT_OC_PIN_MASK) << PORTX_OC_PIN_SHIFT(port); padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OC_MAP); reg = padctl_readl(padctl, XUSB_PADCTL_OC_DET); reg &= ~OC_DETECTED_VBUS_PAD_MASK; reg |= OC_DETECTED_INT_EN_VBUS_PAD(pin); padctl_writel(padctl, reg, XUSB_PADCTL_OC_DET); reg = padctl_readl(padctl, XUSB_PADCTL_VBUS_OC_MAP); reg &= ~(VBUS_OC_MAP_MASK << VBUS_OC_MAP_SHIFT(pin)); reg |= (VBUS_OC_DETECTED_VBUS_PAD(pin) & VBUS_OC_MAP_MASK) << VBUS_OC_MAP_SHIFT(pin); padctl_writel(padctl, reg, XUSB_PADCTL_VBUS_OC_MAP); oc_debug(padctl); } /* should only be called with a UTMI phy and with padctl->lock held */ static void tegra186_disable_vbus_oc(struct phy *phy) { struct tegra_padctl *padctl; int port, pin; u32 reg; if (!phy) return; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); if (!padctl->oc_pinctrl || port < 0) return; pin = padctl->utmi_ports[port].oc_pin; if (pin < 0) return; dev_dbg(padctl->dev, "disable VBUS/OC on UTMI port %d, pin %d\n", port, pin); /* disable VBUS PAD interrupt for this port */ reg = padctl_readl(padctl, XUSB_PADCTL_OC_DET); reg &= ~OC_DETECTED_INT_EN_VBUS_PAD(pin); padctl_writel(padctl, reg, XUSB_PADCTL_OC_DET); /* clear VBUS OC MAP, disable VBUS. Skip doing so if it's OTG port and * OTG vbus always on is set. */ reg = padctl_readl(padctl, XUSB_PADCTL_VBUS_OC_MAP); reg &= ~(VBUS_OC_MAP_MASK << VBUS_OC_MAP_SHIFT(pin)); reg |= VBUS_OC_DETECTION_DISABLED << VBUS_OC_MAP_SHIFT(pin); reg &= ~VBUS_ENABLE(pin); padctl_writel(padctl, reg, XUSB_PADCTL_VBUS_OC_MAP); } static int tegra186_utmi_phy_power_on(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); struct device *dev = padctl->dev; int port = utmi_phy_to_port(phy); char prod_name[] = "prod_c_utmiX"; int err; u32 reg; if (port < 0) return port; dev_dbg(dev, "power on UTMI port %d\n", port); sprintf(prod_name, "prod_c_utmi%d", port); err = tegra_prod_set_by_name(&padctl->padctl_regs, prod_name, padctl->prod_list); if (err) { dev_info(dev, "failed to apply prod for utmi pad%d (%d)\n", port, err); } sprintf(prod_name, "prod_c_bias"); err = tegra_prod_set_by_name(&padctl->padctl_regs, prod_name, padctl->prod_list); if (err) dev_info(dev, "failed to apply prod for bias pad (%d)\n", err); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP); reg &= ~(PORT_CAP_MASK << PORTX_CAP_SHIFT(port)); if (padctl->utmi_ports[port].port_cap == CAP_DISABLED) reg |= (PORT_CAP_DISABLED << PORTX_CAP_SHIFT(port)); else if (padctl->utmi_ports[port].port_cap == DEVICE_ONLY) reg |= (PORT_CAP_DEVICE << PORTX_CAP_SHIFT(port)); else if (padctl->utmi_ports[port].port_cap == HOST_ONLY) reg |= (PORT_CAP_HOST << PORTX_CAP_SHIFT(port)); else if (padctl->utmi_ports[port].port_cap == OTG) reg |= (PORT_CAP_OTG << PORTX_CAP_SHIFT(port)); padctl_writel(padctl, reg, XUSB_PADCTL_USB2_PORT_CAP); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg &= ~USB2_OTG_PD_ZI; reg |= TERM_SEL; reg &= ~HS_CURR_LEVEL(~0); if (padctl->utmi_ports[port].hs_curr_level_offset) { int hs_current_level; dev_dbg(dev, "UTMI port %d apply hs_curr_level_offset %d\n", port, padctl->utmi_ports[port].hs_curr_level_offset); hs_current_level = (int) padctl->calib.hs_curr_level[port] + padctl->utmi_ports[port].hs_curr_level_offset; if (hs_current_level < 0) hs_current_level = 0; if (hs_current_level > 0x3f) hs_current_level = 0x3f; reg |= HS_CURR_LEVEL(hs_current_level); } else reg |= HS_CURR_LEVEL(padctl->calib.hs_curr_level[port]); padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port)); reg &= ~TERM_RANGE_ADJ(~0); reg |= TERM_RANGE_ADJ(padctl->calib.hs_term_range_adj); reg &= ~RPD_CTRL(~0); reg |= RPD_CTRL(padctl->calib.rpd_ctrl); padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL1(port)); /* enable VBUS OC support only on non-OTG port */ if (port != padctl->utmi_otg_port_base_1 - 1) { mutex_lock(&padctl->lock); tegra186_enable_vbus_oc(phy); mutex_unlock(&padctl->lock); } return 0; } static int tegra186_utmi_phy_power_off(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); int port = utmi_phy_to_port(phy); if (port < 0) return port; dev_dbg(padctl->dev, "power off UTMI port %d\n", port); return 0; } int tegra18x_utmi_vbus_enable(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); int port = utmi_phy_to_port(phy); int rc; if (port < 0) return port; dev_dbg(padctl->dev, "enable vbus-%d\n", port); mutex_lock(&padctl->lock); /* only enable regulator when OC is disabled for host only ports */ /* OC is disabled when either oc_pinctrl is NULL or oc_pin is not * defined (-1) */ if (padctl->vbus[port] && (!padctl->oc_pinctrl || padctl->utmi_ports[port].oc_pin < 0) && padctl->utmi_ports[port].port_cap == HOST_ONLY) { rc = regulator_enable(padctl->vbus[port]); if (rc) { dev_err(padctl->dev, "enable port %d vbus failed %d\n", port, rc); mutex_unlock(&padctl->lock); return rc; } } mutex_unlock(&padctl->lock); return 0; } EXPORT_SYMBOL_GPL(tegra18x_utmi_vbus_enable); static int tegra186_utmi_phy_init(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); int port = utmi_phy_to_port(phy); if (port < 0) return port; mutex_lock(&padctl->lock); dev_dbg(padctl->dev, "phy init UTMI port %d\n", port); mutex_unlock(&padctl->lock); return 0; } static int tegra186_utmi_phy_exit(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); int port = utmi_phy_to_port(phy); int rc; if (port < 0) return port; dev_dbg(padctl->dev, "phy exit UTMI port %d\n", port); mutex_lock(&padctl->lock); if (padctl->vbus[port] && regulator_is_enabled(padctl->vbus[port]) && padctl->utmi_ports[port].port_cap == HOST_ONLY) { rc = regulator_disable(padctl->vbus[port]); if (rc) { dev_err(padctl->dev, "disable port %d vbus failed %d\n", port, rc); mutex_unlock(&padctl->lock); return rc; } } mutex_unlock(&padctl->lock); return 0; } static const struct phy_ops utmi_phy_ops = { .init = tegra186_utmi_phy_init, .exit = tegra186_utmi_phy_exit, .power_on = tegra186_utmi_phy_power_on, .power_off = tegra186_utmi_phy_power_off, .owner = THIS_MODULE, }; static int tegra186_cdp_phy_set_cdp(struct phy *phy, bool enable) { struct tegra_padctl *padctl = phy_get_drvdata(phy); int port = cdp_phy_to_port(phy); u32 reg; dev_info(padctl->dev, "%sable UTMI port %d Tegra CDP\n", enable ? "en" : "dis", port); if (enable) { reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg &= ~PD_CHG; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg |= (USB2_OTG_PD2 | USB2_OTG_PD2_OVRD_EN); padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg |= ON_SRC_EN; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); } else { reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg |= PD_CHG; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg &= ~(USB2_OTG_PD2 | USB2_OTG_PD2_OVRD_EN); padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg &= ~ON_SRC_EN; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); } return 0; } static int tegra186_cdp_phy_power_on(struct phy *phy) { return tegra186_cdp_phy_set_cdp(phy, true); } static int tegra186_cdp_phy_power_off(struct phy *phy) { return tegra186_cdp_phy_set_cdp(phy, false); } static const struct phy_ops cdp_phy_ops = { .power_on = tegra186_cdp_phy_power_on, .power_off = tegra186_cdp_phy_power_off, .owner = THIS_MODULE, }; static inline bool is_utmi_phy(struct phy *phy) { return phy->ops == &utmi_phy_ops; } static int hsic_phy_to_port(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); unsigned int i; for (i = 0; i < TEGRA_HSIC_PHYS; i++) { if (phy == padctl->hsic_phys[i]) return i; } WARN_ON(1); return -EINVAL; } enum hsic_pad_pupd { PUPD_DISABLE = 0, PUPD_IDLE, PUPD_RESET }; static int tegra186_hsic_phy_pupd_set(struct tegra_padctl *padctl, int pad, enum hsic_pad_pupd pupd) { struct device *dev = padctl->dev; u32 reg; if (pad >= 1) { dev_err(dev, "%s invalid HSIC pad number %u\n", __func__, pad); return -EINVAL; } dev_dbg(dev, "%s pad %u pupd %d\n", __func__, pad, pupd); reg = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(pad)); reg &= ~(HSIC_RPD_DATA0 | HSIC_RPU_DATA0); reg &= ~(HSIC_RPU_STROBE | HSIC_RPD_STROBE); if (pupd == PUPD_IDLE) { reg |= (HSIC_RPD_DATA0 | HSIC_RPU_STROBE); } else if (pupd == PUPD_RESET) { reg |= (HSIC_RPD_DATA0 | HSIC_RPD_STROBE); } else if (pupd != PUPD_DISABLE) { dev_err(dev, "%s invalid pupd %d\n", __func__, pupd); return -EINVAL; } padctl_writel(padctl, reg, XUSB_PADCTL_HSIC_PADX_CTL0(pad)); return 0; } static ssize_t hsic_power_show(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); struct tegra_padctl *padctl = platform_get_drvdata(pdev); int pad = 0; u32 reg; int on; reg = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(pad)); if (reg & (HSIC_RPD_DATA0 | HSIC_RPD_STROBE)) on = 0; /* bus in reset */ else on = 1; return sprintf(buf, "%d\n", on); } static ssize_t hsic_power_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { struct platform_device *pdev = to_platform_device(dev); struct tegra_padctl *padctl = platform_get_drvdata(pdev); struct tegra_xusb_mbox_msg msg; unsigned int on; int port; int rc; if (kstrtouint(buf, 10, &on)) return -EINVAL; if (padctl->host_mode_phy_disabled) { dev_err(dev, "doesn't support HSIC PHY because mailbox is not available\n"); return -EINVAL; } if (on) msg.cmd = MBOX_CMD_AIRPLANE_MODE_DISABLED; else msg.cmd = MBOX_CMD_AIRPLANE_MODE_ENABLED; port = padctl->soc->hsic_port_offset; msg.data = BIT(port + 1); rc = mbox_send_message(padctl->mbox_chan, &msg); if (rc < 0) dev_err(dev, "failed to send message to firmware %d\n", rc); if (on) rc = tegra186_hsic_phy_pupd_set(padctl, 0, PUPD_IDLE); else rc = tegra186_hsic_phy_pupd_set(padctl, 0, PUPD_RESET); return n; } static DEVICE_ATTR(hsic_power, S_IRUGO | S_IWUSR, hsic_power_show, hsic_power_store); static ssize_t otg_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) { struct platform_device *pdev = to_platform_device(dev); struct tegra_padctl *padctl = platform_get_drvdata(pdev); int port = padctl->utmi_otg_port_base_1 - 1; if (!padctl->utmi_otg_port_base_1) return sprintf(buf, "No UTMI OTG port\n"); return sprintf(buf, "OTG port %d vbus always-on: %s\n", port, padctl->otg_vbus_alwayson ? "yes" : "no"); } static ssize_t otg_vbus_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { struct platform_device *pdev = to_platform_device(dev); struct tegra_padctl *padctl = platform_get_drvdata(pdev); int port = padctl->utmi_otg_port_base_1 - 1; unsigned int on; int err = 0; if (kstrtouint(buf, 10, &on)) return -EINVAL; if (!padctl->utmi_otg_port_base_1) { dev_err(dev, "No UTMI OTG port\n"); return -EINVAL; } if (on && !padctl->otg_vbus_alwayson) { err = tegra18x_phy_xusb_utmi_vbus_power_on( padctl->utmi_phys[port]); if (!err) padctl->otg_vbus_alwayson = true; } else if (!on && padctl->otg_vbus_alwayson) { /* pre-set this to make vbus power off really work */ padctl->otg_vbus_alwayson = false; err = tegra18x_phy_xusb_utmi_vbus_power_off( padctl->utmi_phys[port]); if (!err) padctl->otg_vbus_alwayson = false; else padctl->otg_vbus_alwayson = true; } if (err) dev_err(dev, "failed to %s OTG port %d vbus always-on: %d\n", on ? "enable" : "disable", port, err); return n; } static DEVICE_ATTR(otg_vbus, S_IRUGO | S_IWUSR, otg_vbus_show, otg_vbus_store); static struct attribute *padctl_attrs[] = { &dev_attr_hsic_power.attr, &dev_attr_otg_vbus.attr, NULL, }; static struct attribute_group padctl_attr_group = { .attrs = padctl_attrs, }; static int tegra186_hsic_phy_pretend_connected(struct tegra_padctl *padctl, int port) { struct device *dev = padctl->dev; struct tegra_xusb_mbox_msg msg; int rc; if (!padctl->hsic_ports[port].pretend_connected) return 0; /* pretend-connected is not enabled */ msg.cmd = MBOX_CMD_HSIC_PRETEND_CONNECT; msg.data = BIT(padctl->soc->hsic_port_offset + port + 1); rc = mbox_send_message(padctl->mbox_chan, &msg); if (rc < 0) dev_err(dev, "failed to send message to firmware %d\n", rc); return rc; } static int tegra186_hsic_phy_enable_sleepwalk(struct tegra_padctl *padctl, int port) { u32 reg; dev_dbg(padctl->dev, "enable sleepwalk HSIC port %d\n", port); /* ensure sleepwalk logic is disabled */ reg = ao_readl(padctl, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); reg &= ~MASTER_ENABLE; ao_writel(padctl, reg, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); /* ensure sleepwalk logics are in low power mode */ reg = ao_readl(padctl, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); reg |= MASTER_CFG_SEL; ao_writel(padctl, reg, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); /* set debounce time */ reg = ao_readl(padctl, XUSB_AO_USB_DEBOUNCE_DEL); reg &= ~UHSIC_LINE_DEB_CNT(~0); reg |= UHSIC_LINE_DEB_CNT(1); ao_writel(padctl, reg, XUSB_AO_USB_DEBOUNCE_DEL); /* ensure fake events of sleepwalk logic are desiabled */ reg = ao_readl(padctl, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); reg &= ~(FAKE_STROBE_VAL | FAKE_DATA_VAL | FAKE_STROBE_EN | FAKE_DATA_EN); ao_writel(padctl, reg, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); /* ensure wake events of sleepwalk logic are not latched */ reg = ao_readl(padctl, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); reg &= ~LINE_WAKEUP_EN; ao_writel(padctl, reg, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); /* disable wake event triggers of sleepwalk logic */ reg = ao_readl(padctl, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); reg &= ~WAKE_VAL(~0); reg |= WAKE_VAL_NONE; ao_writel(padctl, reg, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); /* power down the line state detectors of the port */ reg = ao_readl(padctl, XUSB_AO_UHSIC_PAD_CFG(port)); reg |= (STROBE_VAL_PD | DATA0_VAL_PD); ao_writel(padctl, reg, XUSB_AO_UHSIC_PAD_CFG(port)); /* save state, HSIC always comes up as HS */ reg = ao_readl(padctl, XUSB_AO_UHSIC_SAVED_STATE(port)); reg &= ~MODE(~0); reg |= MODE_HS; ao_writel(padctl, reg, XUSB_AO_UHSIC_SAVED_STATE(port)); /* enable the trigger of the sleepwalk logic */ reg = ao_readl(padctl, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); reg |= (WAKE_WALK_EN | LINEVAL_WALK_EN); ao_writel(padctl, reg, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); /* reset the walk pointer and clear the alarm of the sleepwalk logic, * as well as capture the configuration of the USB2.0 port */ reg = ao_readl(padctl, XUSB_AO_UHSIC_TRIGGERS(port)); reg |= (HSIC_CLR_WALK_PTR | HSIC_CLR_WAKE_ALARM | HSIC_CAP_CFG); ao_writel(padctl, reg, XUSB_AO_UHSIC_TRIGGERS(port)); /* setup the pull-ups and pull-downs of the signals during the four * stages of sleepwalk. * maintain a HSIC IDLE and keep driving HSIC RESUME upon remote wake */ reg = (RPD_DATA0_A | RPU_DATA0_B | RPU_DATA0_C | RPU_DATA0_D); reg |= (RPU_STROBE_A | RPD_STROBE_B | RPD_STROBE_C | RPD_STROBE_D); ao_writel(padctl, reg, XUSB_AO_UHSIC_SLEEPWALK(port)); /* power up the line state detectors of the port */ reg = ao_readl(padctl, XUSB_AO_UHSIC_PAD_CFG(port)); reg &= ~(DATA0_VAL_PD | STROBE_VAL_PD); ao_writel(padctl, reg, XUSB_AO_UHSIC_PAD_CFG(port)); usleep_range(150, 200); /* switch the electric control of the USB2.0 pad to XUSB_AO */ reg = ao_readl(padctl, XUSB_AO_UHSIC_PAD_CFG(port)); reg |= USE_XUSB_AO; ao_writel(padctl, reg, XUSB_AO_UHSIC_PAD_CFG(port)); /* set the wake signaling trigger events */ reg = ao_readl(padctl, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); reg &= ~WAKE_VAL(~0); reg |= WAKE_VAL_DS10; ao_writel(padctl, reg, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); /* enable the wake detection */ reg = ao_readl(padctl, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); reg |= (MASTER_ENABLE | LINE_WAKEUP_EN); ao_writel(padctl, reg, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); return 0; } static int tegra186_hsic_phy_disable_sleepwalk(struct tegra_padctl *padctl, int port) { u32 reg; dev_dbg(padctl->dev, "disable sleepwalk HSIC port %d\n", port); /* disable the wake detection */ reg = ao_readl(padctl, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); reg &= ~(MASTER_ENABLE | LINE_WAKEUP_EN); ao_writel(padctl, reg, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); /* switch the electric control of the USB2.0 pad to XUSB vcore logic */ reg = ao_readl(padctl, XUSB_AO_UHSIC_PAD_CFG(port)); reg &= ~USE_XUSB_AO; ao_writel(padctl, reg, XUSB_AO_UHSIC_PAD_CFG(port)); /* disable wake event triggers of sleepwalk logic */ reg = ao_readl(padctl, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); reg &= ~WAKE_VAL(~0); reg |= WAKE_VAL_NONE; ao_writel(padctl, reg, XUSB_AO_UHSIC_SLEEPWALK_CFG(port)); /* power down the line state detectors of the port */ reg = ao_readl(padctl, XUSB_AO_UHSIC_PAD_CFG(port)); reg |= (STROBE_VAL_PD | DATA0_VAL_PD); ao_writel(padctl, reg, XUSB_AO_UHSIC_PAD_CFG(port)); /* clear alarm of the sleepwalk logic */ reg = ao_readl(padctl, XUSB_AO_UHSIC_TRIGGERS(port)); reg |= HSIC_CLR_WAKE_ALARM; ao_writel(padctl, reg, XUSB_AO_UHSIC_TRIGGERS(port)); return 0; } static int tegra186_hsic_phy_power_on(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); struct device *dev = padctl->dev; int port = hsic_phy_to_port(phy); char prod_name[] = "prod_c_hsicX"; int rc; u32 reg; dev_dbg(dev, "power on HSIC port %d\n", port); if (port < 0) return port; sprintf(prod_name, "prod_c_hsic%d", port); rc = tegra_prod_set_by_name(&padctl->padctl_regs, prod_name, padctl->prod_list); if (rc) { dev_info(dev, "failed to apply prod for hsic pad%d (%d)\n", port, rc); } rc = regulator_enable(padctl->vddio_hsic); if (rc) { dev_err(dev, "enable hsic %d power failed %d\n", port, rc); return rc; } rc = clk_prepare_enable(padctl->hsic_trk_clk); if (rc) { dev_err(dev, "failed to enable HSIC tracking clock %d\n", rc); } reg = padctl_readl(padctl, XUSB_PADCTL_HSIC_PAD_TRK_CTL0); reg &= ~HSIC_TRK_START_TIMER(~0); reg |= HSIC_TRK_START_TIMER(0x1e); reg &= ~HSIC_TRK_DONE_RESET_TIMER(~0); reg |= HSIC_TRK_DONE_RESET_TIMER(0xa); padctl_writel(padctl, reg, XUSB_PADCTL_HSIC_PAD_TRK_CTL0); reg = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(port)); reg &= ~(HSIC_PD_TX_DATA0 | HSIC_PD_TX_STROBE | HSIC_PD_RX_DATA0 | HSIC_PD_RX_STROBE | HSIC_PD_ZI_DATA0 | HSIC_PD_ZI_STROBE); padctl_writel(padctl, reg, XUSB_PADCTL_HSIC_PADX_CTL0(port)); udelay(1); reg = padctl_readl(padctl, XUSB_PADCTL_HSIC_PAD_TRK_CTL0); reg &= ~HSIC_PD_TRK; padctl_writel(padctl, reg, XUSB_PADCTL_HSIC_PAD_TRK_CTL0); usleep_range(50, 60); clk_disable_unprepare(padctl->hsic_trk_clk); return 0; } static int tegra186_hsic_phy_power_off(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); int port = hsic_phy_to_port(phy); int rc; u32 reg; dev_dbg(padctl->dev, "power off HSIC port %d\n", port); if (port < 0) return port; rc = regulator_disable(padctl->vddio_hsic); if (rc) { dev_err(padctl->dev, "disable hsic %d power failed %d\n", port, rc); } reg = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(port)); reg |= (HSIC_PD_TX_DATA0 | HSIC_PD_TX_STROBE | HSIC_PD_RX_DATA0 | HSIC_PD_RX_STROBE | HSIC_PD_ZI_DATA0 | HSIC_PD_ZI_STROBE); padctl_writel(padctl, reg, XUSB_PADCTL_HSIC_PADX_CTL0(port)); reg = padctl_readl(padctl, XUSB_PADCTL_HSIC_PAD_TRK_CTL0); reg |= HSIC_PD_TRK; padctl_writel(padctl, reg, XUSB_PADCTL_HSIC_PAD_TRK_CTL0); return 0; } static int tegra186_hsic_phy_init(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); int port = hsic_phy_to_port(phy); mutex_lock(&padctl->lock); dev_dbg(padctl->dev, "phy init HSIC port %d\n", port); mutex_unlock(&padctl->lock); return 0; } static int tegra186_hsic_phy_exit(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); int port = hsic_phy_to_port(phy); mutex_lock(&padctl->lock); dev_dbg(padctl->dev, "phy exit HSIC port %d\n", port); mutex_unlock(&padctl->lock); return 0; } static const struct phy_ops hsic_phy_ops = { .init = tegra186_hsic_phy_init, .exit = tegra186_hsic_phy_exit, .power_on = tegra186_hsic_phy_power_on, .power_off = tegra186_hsic_phy_power_off, .owner = THIS_MODULE, }; static inline bool is_hsic_phy(struct phy *phy) { return phy->ops == &hsic_phy_ops; } static void tegra_xusb_phy_mbox_work(struct work_struct *work) { struct tegra_padctl *padctl = mbox_work_to_padctl(work); struct tegra_xusb_mbox_msg *msg = &padctl->mbox_req; struct tegra_xusb_mbox_msg resp; u32 ports; int ret = 0; dev_dbg(padctl->dev, "mailbox command %d\n", msg->cmd); resp.cmd = 0; switch (msg->cmd) { case MBOX_CMD_START_HSIC_IDLE: case MBOX_CMD_STOP_HSIC_IDLE: ports = msg->data >> (padctl->soc->hsic_port_offset + 1); resp.data = msg->data; resp.cmd = MBOX_CMD_ACK; if (msg->cmd == MBOX_CMD_START_HSIC_IDLE) tegra186_hsic_phy_pupd_set(padctl, 0, PUPD_IDLE); else tegra186_hsic_phy_pupd_set(padctl, 0, PUPD_DISABLE); break; default: break; } if (resp.cmd) { ret = mbox_send_message(padctl->mbox_chan, &resp); if (ret < 0) dev_err(padctl->dev, "mbox_send_message failed\n"); } } static bool is_phy_mbox_message(u32 cmd) { switch (cmd) { case MBOX_CMD_START_HSIC_IDLE: case MBOX_CMD_STOP_HSIC_IDLE: return true; default: return false; } } static void tegra_xusb_phy_mbox_rx(struct mbox_client *cl, void *data) { struct tegra_padctl *padctl = dev_get_drvdata(cl->dev); struct tegra_xusb_mbox_msg *msg = data; if (is_phy_mbox_message(msg->cmd)) { padctl->mbox_req = *msg; schedule_work(&padctl->mbox_req_work); } } static struct phy *tegra186_padctl_xlate(struct device *dev, struct of_phandle_args *args) { struct tegra_padctl *padctl = dev_get_drvdata(dev); unsigned int index = args->args[0]; unsigned int phy_index; struct phy *phy = NULL; if (args->args_count <= 0) return ERR_PTR(-EINVAL); dev_dbg(dev, "%s index %d\n", __func__, index); if ((index >= TEGRA_PADCTL_PHY_USB3_BASE) && (index < TEGRA_PADCTL_PHY_USB3_BASE + 16)) { phy_index = index - TEGRA_PADCTL_PHY_USB3_BASE; if (phy_index < TEGRA_USB3_PHYS) phy = padctl->usb3_phys[phy_index]; } else if ((index >= TEGRA_PADCTL_PHY_UTMI_BASE) && (index < TEGRA_PADCTL_PHY_UTMI_BASE + 16)) { phy_index = index - TEGRA_PADCTL_PHY_UTMI_BASE; if (phy_index < TEGRA_UTMI_PHYS) phy = padctl->utmi_phys[phy_index]; } else if ((index >= TEGRA_PADCTL_PHY_HSIC_BASE) && (index < TEGRA_PADCTL_PHY_HSIC_BASE + 16)) { phy_index = index - TEGRA_PADCTL_PHY_HSIC_BASE; if (phy_index < TEGRA_HSIC_PHYS) phy = padctl->hsic_phys[phy_index]; } else if ((index >= TEGRA_PADCTL_PHY_CDP_BASE) && (index < TEGRA_PADCTL_PHY_CDP_BASE + 16)) { phy_index = index - TEGRA_PADCTL_PHY_CDP_BASE; if (phy_index < TEGRA_CDP_PHYS) phy = padctl->cdp_phys[phy_index]; padctl->cdp_used = true; } return (phy) ? phy : ERR_PTR(-EINVAL); } static const struct pinctrl_pin_desc tegra186_pins[] = { PINCTRL_PIN(PIN_OTG_0, "otg-0"), PINCTRL_PIN(PIN_OTG_1, "otg-1"), PINCTRL_PIN(PIN_OTG_2, "otg-2"), PINCTRL_PIN(PIN_HSIC_0, "hsic-0"), PINCTRL_PIN(PIN_USB3_0, "usb3-0"), PINCTRL_PIN(PIN_USB3_1, "usb3-1"), PINCTRL_PIN(PIN_USB3_2, "usb3-2"), PINCTRL_PIN(PIN_CDP_0, "cdp-0"), PINCTRL_PIN(PIN_CDP_1, "cdp-1"), PINCTRL_PIN(PIN_CDP_2, "cdp-2"), }; static const char * const tegra186_hsic_groups[] = { "hsic-0", }; static const char * const tegra186_xusb_groups[] = { "otg-0", "otg-1", "otg-2", "cdp-0", "cdp-1", "cdp-2", }; #define TEGRA186_FUNCTION(_name) \ { \ .name = #_name, \ .num_groups = ARRAY_SIZE(tegra186_##_name##_groups), \ .groups = tegra186_##_name##_groups, \ } static struct tegra_padctl_function tegra186_functions[] = { TEGRA186_FUNCTION(hsic), TEGRA186_FUNCTION(xusb), }; static const unsigned int tegra186_otg_functions[] = { TEGRA186_FUNC_XUSB }; static const unsigned int tegra186_hsic_functions[] = { TEGRA186_FUNC_HSIC, }; #define TEGRA186_PAD(_name, _offset, _shift, _mask, _funcs) \ { \ .name = _name, \ .offset = _offset, \ .shift = _shift, \ .mask = _mask, \ .num_funcs = ARRAY_SIZE(tegra186_##_funcs##_functions), \ .funcs = tegra186_##_funcs##_functions, \ } static const struct tegra_padctl_pad tegra186_pads[] = { TEGRA186_PAD("otg-0", 0x004, 0, 0x3, otg), TEGRA186_PAD("otg-1", 0x004, 2, 0x3, otg), TEGRA186_PAD("otg-2", 0x004, 4, 0x3, otg), TEGRA186_PAD("hsic-0", 0x004, 20, 0x1, hsic), }; static const char * const tegra186_supply_names[] = { "avdd_usb", /* 3.3V, vddp_usb */ "vclamp_usb", /* 1.8V, vclamp_usb_init */ "avdd_pll_erefeut", /* 1.8V, pll_utmip_avdd */ }; static const struct tegra_padctl_soc tegra186_soc = { .num_pins = ARRAY_SIZE(tegra186_pins), .pins = tegra186_pins, .num_functions = ARRAY_SIZE(tegra186_functions), .functions = tegra186_functions, .num_pads = ARRAY_SIZE(tegra186_pads), .pads = tegra186_pads, .hsic_port_offset = 6, .supply_names = tegra186_supply_names, .num_supplies = ARRAY_SIZE(tegra186_supply_names), .num_oc_pins = 2, }; static const struct of_device_id tegra_padctl_of_match[] = { {.compatible = "nvidia,tegra186-xusb-padctl", .data = &tegra186_soc}, { } }; MODULE_DEVICE_TABLE(of, tegra_padctl_of_match); static int tegra_xusb_read_fuse_calibration(struct tegra_padctl *padctl) { unsigned int i; u32 reg; tegra_fuse_readl(FUSE_SKU_USB_CALIB_0, ®); dev_info(padctl->dev, "FUSE_SKU_USB_CALIB_0 0x%x\n", reg); for (i = 0; i < TEGRA_UTMI_PHYS; i++) { padctl->calib.hs_curr_level[i] = (reg >> HS_CURR_LEVEL_PADX_SHIFT(i)) & HS_CURR_LEVEL_PAD_MASK; } padctl->calib.hs_squelch = (reg >> HS_SQUELCH_SHIFT) & HS_SQUELCH_MASK; padctl->calib.hs_term_range_adj = (reg >> HS_TERM_RANGE_ADJ_SHIFT) & HS_TERM_RANGE_ADJ_MASK; tegra_fuse_readl(FUSE_USB_CALIB_EXT_0, ®); dev_info(padctl->dev, "FUSE_USB_CALIB_EXT_0 0x%x\n", reg); padctl->calib.rpd_ctrl = (reg >> RPD_CTRL_SHIFT) & RPD_CTRL_MASK; return 0; } static int tegra_xusb_select_vbus_en_state(struct tegra_padctl *padctl, int pin, bool tristate) { int err; if (tristate) err = pinctrl_select_state( padctl->oc_pinctrl, padctl->oc_tristate_enable[pin]); else err = pinctrl_select_state( padctl->oc_pinctrl, padctl->oc_passthrough_enable[pin]); if (err < 0) { dev_err(padctl->dev, "setting pin %d OC state failed: %d\n", pin, err); } return err; } static int tegra_xusb_setup_usb(struct tegra_padctl *padctl) { struct phy *phy; unsigned int i; for (i = 0; i < TEGRA_USB3_PHYS; i++) { if (padctl->usb3_ports[i].port_cap == CAP_DISABLED) continue; if (padctl->host_mode_phy_disabled && (padctl->usb3_ports[i].port_cap == HOST_ONLY)) continue; /* no mailbox support */ phy = devm_phy_create(padctl->dev, NULL, &usb3_phy_ops); if (IS_ERR(phy)) return PTR_ERR(phy); padctl->usb3_phys[i] = phy; phy_set_drvdata(phy, padctl); } for (i = 0; i < TEGRA_UTMI_PHYS; i++) { char reg_name[sizeof("vbus-N")]; if (padctl->host_mode_phy_disabled && (padctl->utmi_ports[i].port_cap == HOST_ONLY)) continue; /* no mailbox support */ sprintf(reg_name, "vbus-%d", i); padctl->vbus[i] = devm_regulator_get_optional(padctl->dev, reg_name); if (IS_ERR(padctl->vbus[i])) { if (PTR_ERR(padctl->vbus[i]) == -EPROBE_DEFER) return -EPROBE_DEFER; padctl->vbus[i] = NULL; } phy = devm_phy_create(padctl->dev, NULL, &utmi_phy_ops); if (IS_ERR(phy)) return PTR_ERR(phy); padctl->utmi_phys[i] = phy; phy_set_drvdata(phy, padctl); } if (padctl->host_mode_phy_disabled) goto skip_hsic; /* no mailbox support */ padctl->vddio_hsic = devm_regulator_get(padctl->dev, "vddio-hsic"); if (IS_ERR(padctl->vddio_hsic)) return PTR_ERR(padctl->vddio_hsic); for (i = 0; i < TEGRA_HSIC_PHYS; i++) { phy = devm_phy_create(padctl->dev, NULL, &hsic_phy_ops); if (IS_ERR(phy)) return PTR_ERR(phy); padctl->hsic_phys[i] = phy; phy_set_drvdata(phy, padctl); } for (i = 0; i < TEGRA_CDP_PHYS; i++) { phy = devm_phy_create(padctl->dev, NULL, &cdp_phy_ops); if (IS_ERR(phy)) return PTR_ERR(phy); padctl->cdp_phys[i] = phy; phy_set_drvdata(phy, padctl); } skip_hsic: return 0; } static int tegra_xusb_setup_oc(struct tegra_padctl *padctl) { int i; bool oc_enabled = false; /* check oc_pin properties from USB3 and UTMI phy */ for (i = 0; i < TEGRA_USB3_PHYS; i++) { if (padctl->usb3_ports[i].oc_pin >= 0) { oc_enabled = true; break; } } for (i = 0; i < TEGRA_UTMI_PHYS; i++) { if (padctl->utmi_ports[i].oc_pin >= 0) { oc_enabled = true; break; } } if (!oc_enabled) { dev_dbg(padctl->dev, "No OC pin defined for USB3/UTMI phys\n"); return -EINVAL; } /* getting pinctrl for controlling OC pins */ padctl->oc_pinctrl = devm_pinctrl_get(padctl->dev); if (IS_ERR_OR_NULL(padctl->oc_pinctrl)) { dev_info(padctl->dev, "Missing OC pinctrl device: %ld\n", PTR_ERR(padctl->oc_pinctrl)); return PTR_ERR(padctl->oc_pinctrl); } /* OC enable state */ padctl->oc_tristate_enable = devm_kcalloc(padctl->dev, padctl->soc->num_oc_pins, sizeof(struct pinctrl_state *), GFP_KERNEL); if (!padctl->oc_tristate_enable) return -ENOMEM; for (i = 0; i < padctl->soc->num_oc_pins; i++) { char state_name[sizeof("vbus_enX_sfio_tristate")]; sprintf(state_name, "vbus_en%d_sfio_tristate", i); padctl->oc_tristate_enable[i] = pinctrl_lookup_state( padctl->oc_pinctrl, state_name); if (IS_ERR(padctl->oc_tristate_enable[i])) { dev_info(padctl->dev, "Missing OC pin %d pinctrl state %s: %ld\n", i, state_name, PTR_ERR(padctl->oc_tristate_enable[i])); return PTR_ERR(padctl->oc_tristate_enable[i]); } } /* OC enable passthrough state */ padctl->oc_passthrough_enable = devm_kcalloc(padctl->dev, padctl->soc->num_oc_pins, sizeof(struct pinctrl_state *), GFP_KERNEL); if (!padctl->oc_passthrough_enable) return -ENOMEM; for (i = 0; i < padctl->soc->num_oc_pins; i++) { char state_name[sizeof("vbus_enX_sfio_passthrough")]; sprintf(state_name, "vbus_en%d_sfio_passthrough", i); padctl->oc_passthrough_enable[i] = pinctrl_lookup_state( padctl->oc_pinctrl, state_name); if (IS_ERR(padctl->oc_passthrough_enable[i])) { dev_info(padctl->dev, "Missing OC pin %d pinctrl state %s: %ld\n", i, state_name, PTR_ERR(padctl->oc_passthrough_enable[i])); return PTR_ERR(padctl->oc_passthrough_enable[i]); } } /* OC disable state */ padctl->oc_disable = devm_kcalloc(padctl->dev, padctl->soc->num_oc_pins, sizeof(struct pinctrl_state *), GFP_KERNEL); if (!padctl->oc_disable) return -ENOMEM; for (i = 0; i < padctl->soc->num_oc_pins; i++) { char state_name[sizeof("vbus_enX_default")]; sprintf(state_name, "vbus_en%d_default", i); padctl->oc_disable[i] = pinctrl_lookup_state( padctl->oc_pinctrl, state_name); if (IS_ERR(padctl->oc_disable[i])) { dev_info(padctl->dev, "Missing OC pin %d pinctrl state %s: %ld\n", i, state_name, PTR_ERR(padctl->oc_disable[i])); return PTR_ERR(padctl->oc_disable[i]); } } return 0; } #ifdef DEBUG #define reg_dump(_dev, _base, _reg) \ dev_dbg(_dev, "%s @%x = 0x%x\n", #_reg, _reg, ioread32(_base + _reg)) #else #define reg_dump(_dev, _base, _reg) do {} while (0) #endif /* initializations to be done at cold boot and SC7 exit */ static void tegra186_padctl_init(struct tegra_padctl *padctl) { int i; u32 reg; for (i = 0; i < TEGRA_UTMI_PHYS; i++) { reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL1(i)); reg |= PD_VREG; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL1(i)); if (padctl->utmi_ports[i].port_cap == CAP_DISABLED) { reg = ao_readl(padctl, XUSB_AO_UTMIP_PAD_CFG(i)); reg |= (E_DPD_OVRD_EN | E_DPD_OVRD_VAL); ao_writel(padctl, reg, XUSB_AO_UTMIP_PAD_CFG(i)); } } } static void tegra186_padctl_save(struct tegra_padctl *padctl) { padctl->padctl_context.vbus_id = padctl_readl(padctl, USB2_VBUS_ID); padctl->padctl_context.usb2_pad_mux = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX); padctl->padctl_context.usb2_port_cap = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP); padctl->padctl_context.ss_port_cap = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CAP); } static void tegra186_padctl_restore(struct tegra_padctl *padctl) { padctl_writel(padctl, padctl->padctl_context.usb2_pad_mux, XUSB_PADCTL_USB2_PAD_MUX); padctl_writel(padctl, padctl->padctl_context.usb2_port_cap, XUSB_PADCTL_USB2_PORT_CAP); padctl_writel(padctl, padctl->padctl_context.ss_port_cap, XUSB_PADCTL_SS_PORT_CAP); padctl_writel(padctl, padctl->padctl_context.vbus_id, USB2_VBUS_ID); } static int tegra186_padctl_suspend(struct device *dev) { struct tegra_padctl *padctl = dev_get_drvdata(dev); dev_dbg(dev, "%s\n", __func__); tegra186_padctl_save(padctl); return 0; } static int tegra186_padctl_resume(struct device *dev) { struct tegra_padctl *padctl = dev_get_drvdata(dev); dev_dbg(dev, "%s\n", __func__); tegra186_padctl_init(padctl); tegra186_padctl_restore(padctl); return 0; } static const struct dev_pm_ops tegra186_padctl_pm_ops = { .suspend_noirq = tegra186_padctl_suspend, .resume_noirq = tegra186_padctl_resume, }; static int tegra186_padctl_probe(struct platform_device *pdev) { struct tegra_padctl *padctl; const struct of_device_id *match; struct device *dev = &pdev->dev; struct resource *res; int err; int i; padctl = devm_kzalloc(dev, sizeof(*padctl), GFP_KERNEL); if (!padctl) return -ENOMEM; platform_set_drvdata(pdev, padctl); mutex_init(&padctl->lock); padctl->dev = dev; match = of_match_node(tegra_padctl_of_match, pdev->dev.of_node); if (!match) return -ENODEV; padctl->soc = match->data; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "padctl"); padctl->padctl_regs = devm_ioremap_resource(dev, res); if (IS_ERR(padctl->padctl_regs)) return PTR_ERR(padctl->padctl_regs); dev_info(dev, "padctl mmio start %pa end %pa\n", &res->start, &res->end); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ao"); padctl->ao_regs = devm_ioremap_resource(dev, res); if (IS_ERR(padctl->ao_regs)) return PTR_ERR(padctl->ao_regs); dev_info(dev, "ao mmio start %pa end %pa\n", &res->start, &res->end); padctl->prod_list = devm_tegra_prod_get(&pdev->dev); if (IS_ERR(padctl->prod_list)) { dev_warn(&pdev->dev, "Prod-settings not available\n"); padctl->prod_list = NULL; } if (tegra_platform_is_silicon()) { err = tegra_xusb_read_fuse_calibration(padctl); if (err < 0) return err; } /* overcurrent disabled by default */ for (i = 0; i < TEGRA_USB3_PHYS; i++) padctl->usb3_ports[i].oc_pin = -1; for (i = 0; i < TEGRA_UTMI_PHYS; i++) padctl->utmi_ports[i].oc_pin = -1; padctl->padctl_rst = devm_reset_control_get(dev, "padctl_rst"); if (IS_ERR(padctl->padctl_rst)) { dev_err(padctl->dev, "failed to get padctl reset\n"); return PTR_ERR(padctl->padctl_rst); } padctl->xusb_clk = devm_clk_get(dev, "xusb_clk"); if (IS_ERR(padctl->xusb_clk)) { dev_err(dev, "failed to get xusb_clk clock\n"); return PTR_ERR(padctl->xusb_clk); } padctl->utmipll = devm_clk_get(dev, "utmipll"); if (IS_ERR(padctl->utmipll)) { dev_err(dev, "failed to get utmipll clock\n"); return PTR_ERR(padctl->utmipll); } padctl->usb2_trk_clk = devm_clk_get(dev, "usb2_trk"); if (IS_ERR(padctl->usb2_trk_clk)) { dev_err(dev, "failed to get usb2_trk clock\n"); return PTR_ERR(padctl->usb2_trk_clk); } padctl->hsic_trk_clk = devm_clk_get(dev, "hsic_trk"); if (IS_ERR(padctl->hsic_trk_clk)) { dev_err(dev, "failed to get hsic_trk clock\n"); return PTR_ERR(padctl->hsic_trk_clk); } err = tegra186_padctl_regulators_init(padctl); if (err < 0) return err; err = regulator_bulk_enable(padctl->soc->num_supplies, padctl->supplies); if (err) { dev_err(dev, "failed to enable regulators %d\n", err); return err; } err = clk_prepare_enable(padctl->xusb_clk); if (err) { dev_err(dev, "failed to enable xusb_clk %d\n", err); goto disable_regulators; } err = clk_prepare_enable(padctl->utmipll); if (err) { dev_err(dev, "failed to enable UTMIPLL %d\n", err); goto disable_xusb_clk; } err = clk_prepare_enable(padctl->usb2_trk_clk); if (err) { dev_err(dev, "failed to enable USB2 tracking clock %d\n", err); goto disable_utmipll; } err = reset_control_deassert(padctl->padctl_rst); if (err) { dev_err(dev, "failed to deassert padctl_rst %d\n", err); goto disable_usb2_trk; } memset(&padctl->desc, 0, sizeof(padctl->desc)); padctl->desc.name = dev_name(dev); padctl->desc.pins = padctl->soc->pins; padctl->desc.npins = padctl->soc->num_pins; padctl->desc.pctlops = &tegra_xusb_padctl_pinctrl_ops; padctl->desc.pmxops = &tegra186_padctl_pinmux_ops; padctl->desc.confops = &tegra_padctl_pinconf_ops; padctl->desc.owner = THIS_MODULE; padctl->pinctrl = pinctrl_register(&padctl->desc, &pdev->dev, padctl); if (!padctl->pinctrl) { dev_err(&pdev->dev, "failed to register pinctrl\n"); err = -ENODEV; goto assert_padctl_rst; } INIT_WORK(&padctl->mbox_req_work, tegra_xusb_phy_mbox_work); padctl->mbox_client.dev = dev; padctl->mbox_client.tx_block = true; padctl->mbox_client.tx_tout = 0; padctl->mbox_client.rx_callback = tegra_xusb_phy_mbox_rx; padctl->mbox_chan = mbox_request_channel(&padctl->mbox_client, 0); if (IS_ERR(padctl->mbox_chan)) { err = PTR_ERR(padctl->mbox_chan); if (err == -EPROBE_DEFER) { dev_info(&pdev->dev, "mailbox is not ready yet\n"); goto unregister; } else { dev_warn(&pdev->dev, "failed to get mailbox, USB Host PHY support disabled\n"); padctl->host_mode_phy_disabled = true; } } err = tegra_xusb_setup_usb(padctl); if (err) goto free_mailbox; tegra186_padctl_init(padctl); err = tegra_xusb_setup_oc(padctl); if (err) padctl->oc_pinctrl = NULL; else dev_info(&pdev->dev, "VBUS over-current detection enabled\n"); /* when oc_pinctrl is not NULL, over-current detection is enabled * for at least one port */ if (padctl->oc_pinctrl) /* switch VBUS pin states to enable OC */ for (i = 0; i < TEGRA_UTMI_PHYS; i++) { int ocpin = padctl->utmi_ports[i].oc_pin; bool isotg = (padctl->utmi_ports[i].port_cap == OTG); if (ocpin >= 0) { /* this OC pin is in use, enable the pin * as SFIO input pin for OC detection, * for OTG port, the default state is * device mode and VBUS off. */ err = tegra_xusb_select_vbus_en_state( padctl, ocpin, !isotg); if (err < 0) goto restore_oc_pin; } } padctl->provider = devm_of_phy_provider_register(dev, tegra186_padctl_xlate); if (IS_ERR(padctl->provider)) { err = PTR_ERR(padctl->provider); dev_err(&pdev->dev, "failed to register PHYs: %d\n", err); goto restore_oc_pin; } err = sysfs_create_group(&pdev->dev.kobj, &padctl_attr_group); if (err) { dev_err(&pdev->dev, "cannot create sysfs group: %d\n", err); goto restore_oc_pin; } return 0; restore_oc_pin: if (padctl->oc_pinctrl) for (i--; i >= 0; i--) { err = pinctrl_select_state(padctl->oc_pinctrl, padctl->oc_disable[i]); if (err < 0) dev_err(dev, "set pin %d OC disable failed: %d\n", i, err); } free_mailbox: if (!IS_ERR(padctl->mbox_chan)) { cancel_work_sync(&padctl->mbox_req_work); mbox_free_channel(padctl->mbox_chan); } unregister: pinctrl_unregister(padctl->pinctrl); assert_padctl_rst: reset_control_assert(padctl->padctl_rst); disable_usb2_trk: clk_disable_unprepare(padctl->usb2_trk_clk); disable_utmipll: clk_disable_unprepare(padctl->utmipll); disable_xusb_clk: clk_disable_unprepare(padctl->xusb_clk); disable_regulators: regulator_bulk_disable(padctl->soc->num_supplies, padctl->supplies); return err; } static int tegra186_padctl_remove(struct platform_device *pdev) { struct tegra_padctl *padctl = platform_get_drvdata(pdev); int i; int err; /* switch all VBUS_ENx pins back to default state */ if (padctl->oc_pinctrl) for (i = 0; i < padctl->soc->num_oc_pins; i++) { err = pinctrl_select_state(padctl->oc_pinctrl, padctl->oc_disable[i]); if (err < 0) dev_err(&pdev->dev, "set pin %d OC disable failed: %d\n", i, err); } sysfs_remove_group(&pdev->dev.kobj, &padctl_attr_group); if (!IS_ERR(padctl->mbox_chan)) { cancel_work_sync(&padctl->mbox_req_work); mbox_free_channel(padctl->mbox_chan); } pinctrl_unregister(padctl->pinctrl); reset_control_assert(padctl->padctl_rst); clk_disable_unprepare(padctl->usb2_trk_clk); clk_disable_unprepare(padctl->utmipll); clk_disable_unprepare(padctl->xusb_clk); regulator_bulk_disable(padctl->soc->num_supplies, padctl->supplies); padctl->cdp_used = false; return 0; } static struct platform_driver tegra186_padctl_driver = { .driver = { .name = "tegra186-padctl", .of_match_table = tegra_padctl_of_match, .pm = &tegra186_padctl_pm_ops, }, .probe = tegra186_padctl_probe, .remove = tegra186_padctl_remove, }; module_platform_driver(tegra186_padctl_driver); /* Tegra Generic PHY Extensions */ int tegra18x_phy_xusb_enable_sleepwalk(struct phy *phy, enum usb_device_speed speed) { struct tegra_padctl *padctl = phy_get_drvdata(phy); int port; if (is_utmi_phy(phy)) { port = utmi_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_utmi_phy_enable_sleepwalk(padctl, port, speed); } else if (is_hsic_phy(phy)) { port = hsic_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_hsic_phy_enable_sleepwalk(padctl, port); } else if (is_usb3_phy(phy)) { port = usb3_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_usb3_phy_enable_wakelogic(padctl, port); } else return -EINVAL; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_enable_sleepwalk); int tegra18x_phy_xusb_disable_sleepwalk(struct phy *phy) { struct tegra_padctl *padctl = phy_get_drvdata(phy); int port; if (is_utmi_phy(phy)) { port = utmi_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_utmi_phy_disable_sleepwalk(padctl, port); } else if (is_hsic_phy(phy)) { port = hsic_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_hsic_phy_disable_sleepwalk(padctl, port); } else if (is_usb3_phy(phy)) { port = usb3_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_usb3_phy_disable_wakelogic(padctl, port); } else return -EINVAL; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_disable_sleepwalk); static int tegra186_padctl_vbus_override(struct tegra_padctl *padctl, bool on) { u32 reg; reg = padctl_readl(padctl, USB2_VBUS_ID); if (on) reg |= VBUS_OVERRIDE; else reg &= ~VBUS_OVERRIDE; padctl_writel(padctl, reg, USB2_VBUS_ID); return 0; } int tegra18x_phy_xusb_set_vbus_override(struct phy *phy) { struct tegra_padctl *padctl; if (!phy) return 0; padctl = phy_get_drvdata(phy); return tegra186_padctl_vbus_override(padctl, true); } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_set_vbus_override); int tegra18x_phy_xusb_clear_vbus_override(struct phy *phy) { struct tegra_padctl *padctl; if (!phy) return 0; padctl = phy_get_drvdata(phy); return tegra186_padctl_vbus_override(padctl, false); } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_clear_vbus_override); static int tegra186_padctl_id_override(struct tegra_padctl *padctl, bool grounded) { u32 reg; reg = padctl_readl(padctl, USB2_VBUS_ID); if (grounded) { reg &= ~ID_OVERRIDE(~0); reg |= ID_OVERRIDE_GROUNDED; } else { reg &= ~ID_OVERRIDE(~0); reg |= ID_OVERRIDE_FLOATING; } padctl_writel(padctl, reg, USB2_VBUS_ID); return 0; } int tegra18x_phy_xusb_set_id_override(struct phy *phy) { struct tegra_padctl *padctl; if (!phy) return 0; padctl = phy_get_drvdata(phy); return tegra186_padctl_id_override(padctl, true); } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_set_id_override); int tegra18x_phy_xusb_clear_id_override(struct phy *phy) { struct tegra_padctl *padctl; if (!phy) return 0; padctl = phy_get_drvdata(phy); return tegra186_padctl_id_override(padctl, false); } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_clear_id_override); static enum tegra_xusb_vbus_rid tegra_phy_xusb_parse_rid(u32 rid_value) { rid_value &= RID_MASK; if (rid_value == IDDIG) return VBUS_ID_RID_FLOAT; else if (rid_value == IDDIG_A) return VBUS_ID_RID_A; else if (rid_value == IDDIG_B) return VBUS_ID_RID_B; else if (rid_value == IDDIG_C) return VBUS_ID_RID_C; else if (rid_value == 0) return VBUS_ID_RID_GND; return VBUS_ID_RID_UNDEFINED; } bool tegra18x_phy_xusb_has_otg_cap(struct phy *phy) { struct tegra_padctl *padctl; if (!phy) return false; padctl = phy_get_drvdata(phy); if (is_utmi_phy(phy)) { if ((padctl->utmi_otg_port_base_1) && padctl->utmi_phys[padctl->utmi_otg_port_base_1 - 1] == phy) return true; } else if (is_usb3_phy(phy)) { if ((padctl->usb3_otg_port_base_1) && padctl->usb3_phys[padctl->usb3_otg_port_base_1 - 1] == phy) return true; } return false; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_has_otg_cap); static int tegra186_usb3_phy_set_wake(struct tegra_padctl *padctl, int port, bool enable) { u32 reg; mutex_lock(&padctl->lock); if (enable) { dev_dbg(padctl->dev, "enable USB3 port %d wake\n", port); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ALL_WAKE_EVENTS; reg |= SS_PORT_WAKEUP_EVENT(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM); usleep_range(10, 20); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ALL_WAKE_EVENTS; reg |= SS_PORT_WAKE_INTERRUPT_ENABLE(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM); } else { dev_dbg(padctl->dev, "disable USB3 port %d wake\n", port); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ALL_WAKE_EVENTS; reg &= ~SS_PORT_WAKE_INTERRUPT_ENABLE(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM); usleep_range(10, 20); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ALL_WAKE_EVENTS; reg |= SS_PORT_WAKEUP_EVENT(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM); } mutex_unlock(&padctl->lock); return 0; } static int tegra186_utmi_phy_set_wake(struct tegra_padctl *padctl, int port, bool enable) { u32 reg; mutex_lock(&padctl->lock); if (enable) { dev_dbg(padctl->dev, "enable UTMI port %d wake\n", port); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ALL_WAKE_EVENTS; reg |= USB2_PORT_WAKEUP_EVENT(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM); usleep_range(10, 20); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ALL_WAKE_EVENTS; reg |= USB2_PORT_WAKE_INTERRUPT_ENABLE(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM); } else { dev_dbg(padctl->dev, "disable UTMI port %d wake\n", port); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ALL_WAKE_EVENTS; reg &= ~USB2_PORT_WAKE_INTERRUPT_ENABLE(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM); usleep_range(10, 20); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ALL_WAKE_EVENTS; reg |= USB2_PORT_WAKEUP_EVENT(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM); } mutex_unlock(&padctl->lock); return 0; } static int tegra186_hsic_phy_set_wake(struct tegra_padctl *padctl, int port, bool enable) { u32 reg; mutex_lock(&padctl->lock); if (enable) { dev_dbg(padctl->dev, "enable HSIC port %d wake\n", port); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ALL_WAKE_EVENTS; reg |= USB2_HSIC_PORT_WAKEUP_EVENT(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM); usleep_range(10, 20); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); reg |= USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM); } else { dev_dbg(padctl->dev, "disable HSIC port %d wake\n", port); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ALL_WAKE_EVENTS; reg &= ~USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM); usleep_range(10, 20); reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); reg &= ~ALL_WAKE_EVENTS; reg |= USB2_HSIC_PORT_WAKEUP_EVENT(port); padctl_writel(padctl, reg, XUSB_PADCTL_ELPG_PROGRAM); } mutex_unlock(&padctl->lock); return 0; } int tegra18x_phy_xusb_enable_wake(struct phy *phy) { struct tegra_padctl *padctl; int port; if (!phy) return 0; padctl = phy_get_drvdata(phy); if (is_utmi_phy(phy)) { port = utmi_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_utmi_phy_set_wake(padctl, port, true); } else if (is_hsic_phy(phy)) { port = hsic_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_hsic_phy_set_wake(padctl, port, true); } else if (is_usb3_phy(phy)) { port = usb3_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_usb3_phy_set_wake(padctl, port, true); } else return -EINVAL; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_enable_wake); int tegra18x_phy_xusb_disable_wake(struct phy *phy) { struct tegra_padctl *padctl; int port; if (!phy) return 0; padctl = phy_get_drvdata(phy); if (is_utmi_phy(phy)) { port = utmi_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_utmi_phy_set_wake(padctl, port, false); } else if (is_hsic_phy(phy)) { port = hsic_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_hsic_phy_set_wake(padctl, port, false); } else if (is_usb3_phy(phy)) { port = usb3_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_usb3_phy_set_wake(padctl, port, false); } return -EINVAL; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_disable_wake); static int tegra186_usb3_phy_remote_wake_detected(struct tegra_padctl *padctl, int port) { u32 reg; reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); if ((reg & SS_PORT_WAKE_INTERRUPT_ENABLE(port)) && (reg & SS_PORT_WAKEUP_EVENT(port))) return true; else return false; } static int tegra186_utmi_phy_remote_wake_detected(struct tegra_padctl *padctl, int port) { u32 reg; reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); if ((reg & USB2_PORT_WAKE_INTERRUPT_ENABLE(port)) && (reg & USB2_PORT_WAKEUP_EVENT(port))) return true; else return false; } static int tegra186_hsic_phy_remote_wake_detected(struct tegra_padctl *padctl, int port) { u32 reg; reg = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM); if ((reg & USB2_HSIC_PORT_WAKE_INTERRUPT_ENABLE(port)) && (reg & USB2_HSIC_PORT_WAKEUP_EVENT(port))) return true; else return false; } int tegra18x_phy_xusb_remote_wake_detected(struct phy *phy) { struct tegra_padctl *padctl; int port; if (!phy) return 0; padctl = phy_get_drvdata(phy); if (is_utmi_phy(phy)) { port = utmi_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_utmi_phy_remote_wake_detected(padctl, port); } else if (is_hsic_phy(phy)) { port = hsic_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_hsic_phy_remote_wake_detected(padctl, port); } else if (is_usb3_phy(phy)) { port = usb3_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_usb3_phy_remote_wake_detected(padctl, port); } return -EINVAL; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_remote_wake_detected); int tegra18x_phy_xusb_pretend_connected(struct phy *phy) { struct tegra_padctl *padctl; int port; if (!phy) return 0; padctl = phy_get_drvdata(phy); /* applicable to HSIC only */ if (is_hsic_phy(phy)) { port = hsic_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_hsic_phy_pretend_connected(padctl, port); } return -EINVAL; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_pretend_connected); void tegra18x_phy_xusb_set_dcd_debounce_time(struct phy *phy, u32 val) { struct tegra_padctl *padctl; u32 reg; if (!phy) return; padctl = phy_get_drvdata(phy); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_BATTERY_CHRG_TDCD_DBNC_TIMER_0); reg &= ~TDCD_DBNC(~0); reg |= TDCD_DBNC(val); padctl_writel(padctl, reg, XUSB_PADCTL_USB2_BATTERY_CHRG_TDCD_DBNC_TIMER_0); } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_set_dcd_debounce_time); void tegra18x_phy_xusb_utmi_pad_charger_detect_on(struct phy *phy) { struct tegra_padctl *padctl; int port; u32 reg; if (!phy) return; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); /* power up necessary stuff */ tegra18x_phy_xusb_utmi_pad_power_on(phy); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg &= ~USB2_OTG_PD_ZI; padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg |= (USB2_OTG_PD2 | USB2_OTG_PD2_OVRD_EN); padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg &= ~PD_CHG; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); /* Set DP/DN Pull up/down to zero by default */ reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL1(port)); reg &= ~(USBOP_RPD_OVRD_VAL | USBOP_RPU_OVRD_VAL | USBON_RPD_OVRD_VAL | USBON_RPU_OVRD_VAL); reg |= (USBOP_RPD_OVRD | USBOP_RPU_OVRD | USBON_RPD_OVRD | USBON_RPU_OVRD); padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL1(port)); /* Disable DP/DN as src/sink */ reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg &= ~(OP_SRC_EN | ON_SINK_EN | ON_SRC_EN | OP_SINK_EN); padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_utmi_pad_charger_detect_on); void tegra18x_phy_xusb_utmi_pad_charger_detect_off(struct phy *phy) { struct tegra_padctl *padctl; int port; u32 reg; if (!phy) return; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL1(port)); reg &= ~(USBOP_RPD_OVRD | USBOP_RPU_OVRD | USBON_RPD_OVRD | USBON_RPU_OVRD); padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL1(port)); /* power down necessary stuff */ reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg |= PD_CHG; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); reg &= ~(USB2_OTG_PD2 | USB2_OTG_PD2_OVRD_EN); padctl_writel(padctl, reg, XUSB_PADCTL_USB2_OTG_PADX_CTL0(port)); tegra18x_phy_xusb_utmi_pad_power_down(phy); } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_utmi_pad_charger_detect_off); void tegra18x_phy_xusb_utmi_pad_enable_detect_filters(struct phy *phy) { struct tegra_padctl *padctl; int port; u32 reg; if (!phy) return; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg |= (VDCD_DET_FILTER_EN | VDAT_DET_FILTER_EN | ZIP_FILTER_EN | ZIN_FILTER_EN); padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_utmi_pad_enable_detect_filters); void tegra18x_phy_xusb_utmi_pad_disable_detect_filters(struct phy *phy) { struct tegra_padctl *padctl; int port; u32 reg; if (!phy) return; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg &= ~(VDCD_DET_FILTER_EN | VDAT_DET_FILTER_EN | ZIP_FILTER_EN | ZIN_FILTER_EN); padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_utmi_pad_disable_detect_filters); void tegra18x_phy_xusb_utmi_pad_set_protection_level(struct phy *phy, int level, enum tegra_vbus_dir dir) { struct tegra_padctl *padctl; int port; u32 reg; if (!phy) return; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL1(port)); if (level < 0) { /* disable pad protection */ reg |= PD_VREG; reg &= ~VREG_LEV(~0); reg &= ~VREG_DIR(~0); } else { reg &= ~PD_VREG; reg &= ~VREG_DIR(~0); if (padctl->utmi_ports[port].port_cap == HOST_ONLY || dir == TEGRA_VBUS_SOURCE) reg |= VREG_DIR_OUT; else if (padctl->utmi_ports[port].port_cap == DEVICE_ONLY || dir == TEGRA_VBUS_SINK) reg |= VREG_DIR_IN; reg &= ~VREG_LEV(~0); reg |= VREG_LEV(level); } padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL1(port)); } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_utmi_pad_set_protection_level); bool tegra18x_phy_xusb_utmi_pad_dcd(struct phy *phy) { struct tegra_padctl *padctl; int port; u32 reg; int dcd_timeout_ms = 0; bool ret = false; if (!phy) return false; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); /* data contact detection */ /* Turn on IDP_SRC */ reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg |= OP_I_SRC_EN; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); /* Turn on D- pull-down resistor */ reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL1(port)); reg |= USBON_RPD_OVRD_VAL; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL1(port)); /* Wait for TDCD_DBNC */ usleep_range(10000, 120000); while (dcd_timeout_ms < TDCD_TIMEOUT_MS) { reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); if (reg & DCD_DETECTED) { dev_dbg(padctl->dev, "USB2 port %d DCD successful\n", port); ret = true; break; } usleep_range(20000, 22000); dcd_timeout_ms += 22; } if (!ret) dev_info(padctl->dev, "%s: DCD timeout %d ms\n", __func__, dcd_timeout_ms); /* Turn off IP_SRC, clear DCD DETECTED*/ reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg &= ~OP_I_SRC_EN; reg |= DCD_DETECTED; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); /* Turn off D- pull-down resistor */ reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL1(port)); reg &= ~USBON_RPD_OVRD_VAL; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL1(port)); return ret; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_utmi_pad_dcd); u32 tegra18x_phy_xusb_noncompliant_div_detect(struct phy *phy) { struct tegra_padctl *padctl; int port; u32 reg; if (!phy) return 0; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL1(port)); reg |= DIV_DET_EN; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL1(port)); udelay(10); reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL1(port)); reg &= ~DIV_DET_EN; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL1(port)); return reg; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_noncompliant_div_detect); bool tegra18x_phy_xusb_utmi_pad_primary_charger_detect(struct phy *phy) { struct tegra_padctl *padctl; int port; u32 reg; bool ret; if (!phy) return false; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); /* Source D+ to D- */ reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg |= OP_SRC_EN | ON_SINK_EN; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); /* Wait for TVDPSRC_ON */ msleep(40); reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); ret = !!(reg & VDAT_DET); /* Turn off OP_SRC, ON_SINK, clear VDAT, ZIN status change */ reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg &= ~(OP_SRC_EN | ON_SINK_EN); padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); return ret; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_utmi_pad_primary_charger_detect); bool tegra18x_phy_xusb_utmi_pad_secondary_charger_detect(struct phy *phy) { struct tegra_padctl *padctl; int port; u32 reg; bool ret; if (!phy) return false; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); /* Source D- to D+ */ reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg |= ON_SRC_EN | OP_SINK_EN; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); /* Wait for TVDPSRC_ON */ msleep(40); reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); ret = !(reg & VDAT_DET); /* Turn off ON_SRC, OP_SINK, clear VDAT, ZIP status change */ reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg &= ~(ON_SRC_EN | OP_SINK_EN); padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); return ret; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_utmi_pad_secondary_charger_detect); /* * This function will fource vbus on whatever under * over-current SFIO or regulator GPIO control, * and also without caring about regulator refcnt. */ int tegra18x_phy_xusb_utmi_vbus_power_on(struct phy *phy) { struct tegra_padctl *padctl; int port; int rc = 0; int status; if (!phy) return -EINVAL; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); mutex_lock(&padctl->lock); if (padctl->oc_pinctrl && padctl->utmi_ports[port].oc_pin >= 0) { tegra_xusb_select_vbus_en_state(padctl, padctl->utmi_ports[port].oc_pin, true); tegra186_enable_vbus_oc(padctl->utmi_phys[port]); } else { status = regulator_is_enabled(padctl->vbus[port]); if (padctl->vbus[port] && !status) { rc = regulator_enable(padctl->vbus[port]); if (rc) dev_err(padctl->dev, "enable port %d vbus failed %d\n", port, rc); } dev_dbg(padctl->dev, "%s: port %d regulator status: %d->%d\n", __func__, port, status, regulator_is_enabled(padctl->vbus[port])); } mutex_unlock(&padctl->lock); return rc; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_utmi_vbus_power_on); /* * This function will fource vbus off whatever under * over-current SFIO or regulator GPIO control, * and also without caring about regulator refcnt; * the only exception is for 'otg vbus always on' case. */ int tegra18x_phy_xusb_utmi_vbus_power_off(struct phy *phy) { struct tegra_padctl *padctl; int port; int rc = 0; int status; if (!phy) return -EINVAL; padctl = phy_get_drvdata(phy); port = utmi_phy_to_port(phy); if (port == padctl->utmi_otg_port_base_1 - 1 && padctl->otg_vbus_alwayson) { dev_dbg(padctl->dev, "%s: port %d vbus cannot off due to alwayson\n", __func__, port); return -EINVAL; } mutex_lock(&padctl->lock); if (padctl->oc_pinctrl && padctl->utmi_ports[port].oc_pin >= 0) { tegra_xusb_select_vbus_en_state(padctl, padctl->utmi_ports[port].oc_pin, false); tegra186_disable_vbus_oc(padctl->utmi_phys[port]); } else { status = regulator_is_enabled(padctl->vbus[port]); if (padctl->vbus[port] && status) { rc = regulator_disable(padctl->vbus[port]); if (rc) dev_err(padctl->dev, "disable port %d vbus failed %d\n", port, rc); } dev_dbg(padctl->dev, "%s: port %d regulator status: %d->%d\n", __func__, port, status, regulator_is_enabled(padctl->vbus[port])); } mutex_unlock(&padctl->lock); return rc; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_utmi_vbus_power_off); int tegra18x_phy_xusb_overcurrent_detected(struct phy *phy) { struct tegra_padctl *padctl; int port; bool detected = false; u32 reg; int pin; if (!phy) return 0; padctl = phy_get_drvdata(phy); if (!is_utmi_phy(phy)) return -EINVAL; port = utmi_phy_to_port(phy); if (port < 0) return -EINVAL; pin = padctl->utmi_ports[port].oc_pin; if (pin < 0) return -EINVAL; reg = padctl_readl(padctl, XUSB_PADCTL_OC_DET); detected = !!(reg & OC_DETECTED_VBUS_PAD(pin)); if (detected) { reg &= ~OC_DETECTED_VBUS_PAD_MASK; reg &= ~OC_DETECTED_INT_EN_VBUS_PAD(pin); padctl_writel(padctl, reg, XUSB_PADCTL_OC_DET); } return detected; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_overcurrent_detected); void tegra18x_phy_xusb_handle_overcurrent(struct phy *phy) { struct tegra_padctl *padctl; u32 reg; unsigned i; int pin; if (!phy) return; padctl = phy_get_drvdata(phy); if (!is_utmi_phy(phy)) return; oc_debug(padctl); mutex_lock(&padctl->lock); reg = padctl_readl(padctl, XUSB_PADCTL_OC_DET); for (i = 0; i < TEGRA_UTMI_PHYS; i++) { pin = padctl->utmi_ports[i].oc_pin; if (pin < 0) continue; if (reg & OC_DETECTED_VBUS_PAD(pin)) { dev_info(padctl->dev, "%s: clear port %d pin %d OC\n", __func__, i, pin); tegra186_enable_vbus_oc(padctl->utmi_phys[i]); } } mutex_unlock(&padctl->lock); } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_handle_overcurrent); static int tegra186_usb3_phy_reverse_id(struct tegra_padctl *padctl, int port, bool enable) { u32 reg; mutex_lock(&padctl->lock); reg = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CAP); if (enable) reg |= PORT_REVERSE_ID(port); else reg &= ~PORT_REVERSE_ID(port); padctl_writel(padctl, reg, XUSB_PADCTL_SS_PORT_CAP); mutex_unlock(&padctl->lock); return 0; } static int tegra186_utmi_phy_reverse_id(struct tegra_padctl *padctl, int port, bool enable) { u32 reg; mutex_lock(&padctl->lock); reg = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP); if (enable) reg |= PORT_REVERSE_ID(port); else reg &= ~PORT_REVERSE_ID(port); padctl_writel(padctl, reg, XUSB_PADCTL_USB2_PORT_CAP); mutex_unlock(&padctl->lock); return 0; } int tegra18x_phy_xusb_set_reverse_id(struct phy *phy) { struct tegra_padctl *padctl; int port; if (!phy) return -EINVAL; padctl = phy_get_drvdata(phy); /* applicable to SS/UTMI only */ if (is_utmi_phy(phy)) { port = utmi_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_utmi_phy_reverse_id(padctl, port, true); } else if (is_usb3_phy(phy)) { port = usb3_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_usb3_phy_reverse_id(padctl, port, true); } return -EINVAL; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_set_reverse_id); int tegra18x_phy_xusb_clear_reverse_id(struct phy *phy) { struct tegra_padctl *padctl; int port; if (!phy) return -EINVAL; padctl = phy_get_drvdata(phy); /* applicable to SS/UTMI only */ if (is_utmi_phy(phy)) { port = utmi_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_utmi_phy_reverse_id(padctl, port, false); } else if (is_usb3_phy(phy)) { port = usb3_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_usb3_phy_reverse_id(padctl, port, false); } return -EINVAL; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_clear_reverse_id); int tegra18x_phy_xusb_generate_srp(struct phy *phy) { struct tegra_padctl *padctl; u32 reg; int port; if (!phy) return -EINVAL; padctl = phy_get_drvdata(phy); /* applicable only to UTMI */ if (is_utmi_phy(phy)) { port = utmi_phy_to_port(phy); if (port < 0) return -EINVAL; mutex_lock(&padctl->lock); reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); reg |= GENERATE_SRP; padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); mutex_unlock(&padctl->lock); return 0; } return -EINVAL; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_generate_srp); static int tegra186_utmi_phy_srp_detect(struct tegra_padctl *padctl, int port, bool enable) { u32 reg; reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); if (enable) reg |= SRP_DETECT_EN | SRP_INTR_EN; else reg &= ~(SRP_DETECT_EN | SRP_INTR_EN); padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); return 0; } int tegra18x_phy_xusb_enable_srp_detect(struct phy *phy) { struct tegra_padctl *padctl; int port; if (!phy) return -EINVAL; padctl = phy_get_drvdata(phy); /* applicable only to UTMI */ if (is_utmi_phy(phy)) { port = utmi_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_utmi_phy_srp_detect(padctl, port, true); } return -EINVAL; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_enable_srp_detect); int tegra18x_phy_xusb_disable_srp_detect(struct phy *phy) { struct tegra_padctl *padctl; int port; if (!phy) return -EINVAL; padctl = phy_get_drvdata(phy); /* applicable only to UTMI */ if (is_utmi_phy(phy)) { port = utmi_phy_to_port(phy); if (port < 0) return -EINVAL; return tegra186_utmi_phy_srp_detect(padctl, port, false); } return -EINVAL; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_disable_srp_detect); bool tegra18x_phy_xusb_srp_detected(struct phy *phy) { struct tegra_padctl *padctl; u32 reg; int port; if (!phy) return false; padctl = phy_get_drvdata(phy); /* applicable only to UTMI */ if (is_utmi_phy(phy)) { port = utmi_phy_to_port(phy); if (port < 0) return false; reg = padctl_readl(padctl, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); dev_dbg(padctl->dev, "USB2_BATTERY_CHRG_OTGPADX_CTL0:%#x\n", reg); if (reg & SRP_DETECTED) { padctl_writel(padctl, reg, USB2_BATTERY_CHRG_OTGPADX_CTL0(port)); return true; } } return false; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_srp_detected); int tegra18x_phy_xusb_enable_otg_int(struct phy *phy) { struct tegra_padctl *padctl; u32 reg; if (!phy) return -EINVAL; padctl = phy_get_drvdata(phy); mutex_lock(&padctl->lock); reg = padctl_readl(padctl, USB2_VBUS_ID); reg |= VBUS_VALID_CHNG_INTR_EN | OTG_VBUS_SESS_VLD_CHNG_INTR_EN | IDDIG_CHNG_INTR_EN | VBUS_WAKEUP_CHNG_INTR_EN; padctl_writel(padctl, reg, USB2_VBUS_ID); mutex_unlock(&padctl->lock); return 0; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_enable_otg_int); int tegra18x_phy_xusb_disable_otg_int(struct phy *phy) { struct tegra_padctl *padctl; u32 reg; if (!phy) return -EINVAL; padctl = phy_get_drvdata(phy); mutex_lock(&padctl->lock); reg = padctl_readl(padctl, USB2_VBUS_ID); reg &= ~(VBUS_VALID_CHNG_INTR_EN | OTG_VBUS_SESS_VLD_CHNG_INTR_EN | IDDIG_CHNG_INTR_EN | VBUS_WAKEUP_CHNG_INTR_EN); padctl_writel(padctl, reg, USB2_VBUS_ID); mutex_unlock(&padctl->lock); return 0; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_disable_otg_int); int tegra18x_phy_xusb_ack_otg_int(struct phy *phy) { struct tegra_padctl *padctl; u32 reg; if (!phy) return -EINVAL; padctl = phy_get_drvdata(phy); reg = padctl_readl(padctl, USB2_VBUS_ID); padctl_writel(padctl, reg, USB2_VBUS_ID); return 0; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_ack_otg_int); int tegra18x_phy_xusb_get_otg_vbus_id(struct phy *phy, struct tegra_xusb_otg_vbus_id *info) { struct tegra_padctl *padctl; u32 reg; if (!phy || !info) return -EINVAL; padctl = phy_get_drvdata(phy); reg = padctl_readl(padctl, USB2_VBUS_ID); info->iddig_chg = !!(reg & IDDIG_ST_CHNG); info->iddig = tegra_phy_xusb_parse_rid(reg); dev_dbg(padctl->dev, "%s: iddig_chg=%d, iddig=%d\n", __func__, info->iddig_chg, info->iddig); info->vbus_sess_vld_chg = !!(reg & OTG_VBUS_SESS_VLD_ST_CHNG); info->vbus_sess_vld = !!(reg & OTG_VBUS_SESS_VLD); dev_dbg(padctl->dev, "%s: vbus_sess_vld_chg=%d, vbus_sess_vld=%d\n", __func__, info->vbus_sess_vld_chg, info->vbus_sess_vld); info->vbus_vld_chg = !!(reg & VBUS_VALID_ST_CHNG); info->vbus_vld = !!(reg & VBUS_VALID); dev_dbg(padctl->dev, "%s: vbus_vld_chg=%d, vbus_vld=%d\n", __func__, info->vbus_vld_chg, info->vbus_vld); info->vbus_wakeup_chg = !!(reg & VBUS_WAKEUP_ST_CHNG); info->vbus_wakeup = !!(reg & VBUS_WAKEUP); dev_dbg(padctl->dev, "%s: vbus_wakeup_chg=%d, vbus_wakeup=%d\n", __func__, info->vbus_wakeup_chg, info->vbus_wakeup); info->vbus_override = !!(reg & VBUS_OVERRIDE); info->id_override = (reg >> ID_OVERRIDE_SHIFT) & ID_OVERRIDE_MASK; dev_dbg(padctl->dev, "%s: vbus_override=%d, id_override=%d\n", __func__, info->vbus_override, info->id_override); return 0; } EXPORT_SYMBOL_GPL(tegra18x_phy_xusb_get_otg_vbus_id); MODULE_AUTHOR("JC Kuo "); MODULE_DESCRIPTION("Tegra 186 XUSB PADCTL driver"); MODULE_LICENSE("GPL v2");