2017-11-03 10:28:30 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2011-03-10 00:28:55 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2010 Google, Inc.
|
2013-05-16 14:13:02 +00:00
|
|
|
* Copyright (C) 2013 NVIDIA Corporation
|
2011-03-10 00:28:55 +00:00
|
|
|
*
|
|
|
|
* Author:
|
|
|
|
* Erik Gilling <konkers@google.com>
|
|
|
|
* Benoit Goby <benoit@android.com>
|
2013-05-16 14:13:02 +00:00
|
|
|
* Venu Byravarasu <vbyravarasu@nvidia.com>
|
2011-03-10 00:28:55 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/err.h>
|
2012-03-02 20:58:42 +00:00
|
|
|
#include <linux/export.h>
|
2011-03-10 00:28:55 +00:00
|
|
|
#include <linux/gpio.h>
|
2020-01-06 01:34:03 +00:00
|
|
|
#include <linux/iopoll.h>
|
|
|
|
#include <linux/module.h>
|
2013-01-16 03:30:20 +00:00
|
|
|
#include <linux/of.h>
|
2013-08-12 13:06:51 +00:00
|
|
|
#include <linux/of_device.h>
|
2012-04-12 21:46:49 +00:00
|
|
|
#include <linux/of_gpio.h>
|
2020-01-06 01:34:03 +00:00
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/resource.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
|
|
|
|
#include <linux/regulator/consumer.h>
|
|
|
|
|
USB: EHCI: tegra: fix circular module dependencies
The Tegra EHCI driver directly calls various functions in the Tegra USB
PHY driver. The reverse is also true; the PHY driver calls into the EHCI
driver. This is problematic when the two are built as modules.
The calls from the PHY to EHCI driver were originally added in commit
bbdabdb "usb: add APIs to access host registers from Tegra PHY", for the
following reasons:
1) The register being touched is an EHCI register, so logically only the
EHCI driver should touch it.
2) (1) implies that some locking may be needed to correctly implement the
r/m/w access to this shared register.
3) We were expecting to pass only the PHY register space to the Tegra PHY
driver, and hence it would not have access to touch the shared
registers.
To solve this, that commit added functions in the EHCI driver to touch the
shared register on behalf of the PHY driver.
In practice, we ended up not having any locking in the implementaiton of
those functions, and I've been led to believe this is safe. Equally, (3)
did not happen either. Hence, it is possible for the PHY driver to touch
the shared register directly.
Given that, this patch moves the code to touch the shared register back
into the PHY driver, to eliminate the module problems. If we actually
need locking or co-ordination in the future, I propose we put the lock
support into some pre-existing core module, or into a third separate
module, in order to avoid the circular dependencies.
I apologize for my contribution to code churn here.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-06-13 17:24:11 +00:00
|
|
|
#include <linux/usb/ehci_def.h>
|
2020-01-06 01:34:03 +00:00
|
|
|
#include <linux/usb/of.h>
|
2012-09-05 13:20:23 +00:00
|
|
|
#include <linux/usb/tegra_usb_phy.h>
|
2020-01-06 01:34:03 +00:00
|
|
|
#include <linux/usb/ulpi.h>
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define ULPI_VIEWPORT 0x170
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2013-08-12 13:06:51 +00:00
|
|
|
/* PORTSC PTS/PHCD bits, Tegra20 only */
|
2020-01-06 01:34:02 +00:00
|
|
|
#define TEGRA_USB_PORTSC1 0x184
|
|
|
|
#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30)
|
|
|
|
#define TEGRA_USB_PORTSC1_PHCD BIT(23)
|
USB: EHCI: tegra: fix circular module dependencies
The Tegra EHCI driver directly calls various functions in the Tegra USB
PHY driver. The reverse is also true; the PHY driver calls into the EHCI
driver. This is problematic when the two are built as modules.
The calls from the PHY to EHCI driver were originally added in commit
bbdabdb "usb: add APIs to access host registers from Tegra PHY", for the
following reasons:
1) The register being touched is an EHCI register, so logically only the
EHCI driver should touch it.
2) (1) implies that some locking may be needed to correctly implement the
r/m/w access to this shared register.
3) We were expecting to pass only the PHY register space to the Tegra PHY
driver, and hence it would not have access to touch the shared
registers.
To solve this, that commit added functions in the EHCI driver to touch the
shared register on behalf of the PHY driver.
In practice, we ended up not having any locking in the implementaiton of
those functions, and I've been led to believe this is safe. Equally, (3)
did not happen either. Hence, it is possible for the PHY driver to touch
the shared register directly.
Given that, this patch moves the code to touch the shared register back
into the PHY driver, to eliminate the module problems. If we actually
need locking or co-ordination in the future, I propose we put the lock
support into some pre-existing core module, or into a third separate
module, in order to avoid the circular dependencies.
I apologize for my contribution to code churn here.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-06-13 17:24:11 +00:00
|
|
|
|
2013-08-12 13:06:51 +00:00
|
|
|
/* HOSTPC1 PTS/PHCD bits, Tegra30 and above */
|
2020-01-06 01:34:02 +00:00
|
|
|
#define TEGRA_USB_HOSTPC1_DEVLC 0x1b4
|
|
|
|
#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29)
|
|
|
|
#define TEGRA_USB_HOSTPC1_DEVLC_PHCD BIT(22)
|
2013-08-12 13:06:51 +00:00
|
|
|
|
USB: EHCI: tegra: fix circular module dependencies
The Tegra EHCI driver directly calls various functions in the Tegra USB
PHY driver. The reverse is also true; the PHY driver calls into the EHCI
driver. This is problematic when the two are built as modules.
The calls from the PHY to EHCI driver were originally added in commit
bbdabdb "usb: add APIs to access host registers from Tegra PHY", for the
following reasons:
1) The register being touched is an EHCI register, so logically only the
EHCI driver should touch it.
2) (1) implies that some locking may be needed to correctly implement the
r/m/w access to this shared register.
3) We were expecting to pass only the PHY register space to the Tegra PHY
driver, and hence it would not have access to touch the shared
registers.
To solve this, that commit added functions in the EHCI driver to touch the
shared register on behalf of the PHY driver.
In practice, we ended up not having any locking in the implementaiton of
those functions, and I've been led to believe this is safe. Equally, (3)
did not happen either. Hence, it is possible for the PHY driver to touch
the shared register directly.
Given that, this patch moves the code to touch the shared register back
into the PHY driver, to eliminate the module problems. If we actually
need locking or co-ordination in the future, I propose we put the lock
support into some pre-existing core module, or into a third separate
module, in order to avoid the circular dependencies.
I apologize for my contribution to code churn here.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-06-13 17:24:11 +00:00
|
|
|
/* Bits of PORTSC1, which will get cleared by writing 1 into them */
|
|
|
|
#define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
|
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define USB_SUSP_CTRL 0x400
|
|
|
|
#define USB_WAKE_ON_CNNT_EN_DEV BIT(3)
|
|
|
|
#define USB_WAKE_ON_DISCON_EN_DEV BIT(4)
|
|
|
|
#define USB_SUSP_CLR BIT(5)
|
|
|
|
#define USB_PHY_CLK_VALID BIT(7)
|
|
|
|
#define UTMIP_RESET BIT(11)
|
|
|
|
#define UHSIC_RESET BIT(11)
|
|
|
|
#define UTMIP_PHY_ENABLE BIT(12)
|
|
|
|
#define ULPI_PHY_ENABLE BIT(13)
|
|
|
|
#define USB_SUSP_SET BIT(14)
|
|
|
|
#define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16)
|
|
|
|
|
|
|
|
#define USB1_LEGACY_CTRL 0x410
|
|
|
|
#define USB1_NO_LEGACY_MODE BIT(0)
|
2011-03-10 00:28:55 +00:00
|
|
|
#define USB1_VBUS_SENSE_CTL_MASK (3 << 1)
|
|
|
|
#define USB1_VBUS_SENSE_CTL_VBUS_WAKEUP (0 << 1)
|
|
|
|
#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD_OR_VBUS_WAKEUP \
|
|
|
|
(1 << 1)
|
|
|
|
#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD (2 << 1)
|
|
|
|
#define USB1_VBUS_SENSE_CTL_A_SESS_VLD (3 << 1)
|
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define ULPI_TIMING_CTRL_0 0x424
|
|
|
|
#define ULPI_OUTPUT_PINMUX_BYP BIT(10)
|
|
|
|
#define ULPI_CLKOUT_PINMUX_BYP BIT(11)
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define ULPI_TIMING_CTRL_1 0x428
|
|
|
|
#define ULPI_DATA_TRIMMER_LOAD BIT(0)
|
|
|
|
#define ULPI_DATA_TRIMMER_SEL(x) (((x) & 0x7) << 1)
|
|
|
|
#define ULPI_STPDIRNXT_TRIMMER_LOAD BIT(16)
|
|
|
|
#define ULPI_STPDIRNXT_TRIMMER_SEL(x) (((x) & 0x7) << 17)
|
|
|
|
#define ULPI_DIR_TRIMMER_LOAD BIT(24)
|
|
|
|
#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25)
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define UTMIP_PLL_CFG1 0x804
|
2011-03-10 00:28:55 +00:00
|
|
|
#define UTMIP_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
|
|
|
|
#define UTMIP_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27)
|
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define UTMIP_XCVR_CFG0 0x808
|
2011-03-10 00:28:55 +00:00
|
|
|
#define UTMIP_XCVR_SETUP(x) (((x) & 0xf) << 0)
|
2013-08-12 13:06:50 +00:00
|
|
|
#define UTMIP_XCVR_SETUP_MSB(x) ((((x) & 0x70) >> 4) << 22)
|
2011-03-10 00:28:55 +00:00
|
|
|
#define UTMIP_XCVR_LSRSLEW(x) (((x) & 0x3) << 8)
|
|
|
|
#define UTMIP_XCVR_LSFSLEW(x) (((x) & 0x3) << 10)
|
2020-01-06 01:34:02 +00:00
|
|
|
#define UTMIP_FORCE_PD_POWERDOWN BIT(14)
|
|
|
|
#define UTMIP_FORCE_PD2_POWERDOWN BIT(16)
|
|
|
|
#define UTMIP_FORCE_PDZI_POWERDOWN BIT(18)
|
|
|
|
#define UTMIP_XCVR_LSBIAS_SEL BIT(21)
|
2013-08-12 13:06:53 +00:00
|
|
|
#define UTMIP_XCVR_HSSLEW(x) (((x) & 0x3) << 4)
|
|
|
|
#define UTMIP_XCVR_HSSLEW_MSB(x) ((((x) & 0x1fc) >> 2) << 25)
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define UTMIP_BIAS_CFG0 0x80c
|
|
|
|
#define UTMIP_OTGPD BIT(11)
|
|
|
|
#define UTMIP_BIASPD BIT(10)
|
|
|
|
#define UTMIP_HSSQUELCH_LEVEL(x) (((x) & 0x3) << 0)
|
|
|
|
#define UTMIP_HSDISCON_LEVEL(x) (((x) & 0x3) << 2)
|
|
|
|
#define UTMIP_HSDISCON_LEVEL_MSB(x) ((((x) & 0x4) >> 2) << 24)
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define UTMIP_HSRX_CFG0 0x810
|
|
|
|
#define UTMIP_ELASTIC_LIMIT(x) (((x) & 0x1f) << 10)
|
|
|
|
#define UTMIP_IDLE_WAIT(x) (((x) & 0x1f) << 15)
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define UTMIP_HSRX_CFG1 0x814
|
|
|
|
#define UTMIP_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1)
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define UTMIP_TX_CFG0 0x820
|
|
|
|
#define UTMIP_FS_PREABMLE_J BIT(19)
|
|
|
|
#define UTMIP_HS_DISCON_DISABLE BIT(8)
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define UTMIP_MISC_CFG0 0x824
|
|
|
|
#define UTMIP_DPDM_OBSERVE BIT(26)
|
|
|
|
#define UTMIP_DPDM_OBSERVE_SEL(x) (((x) & 0xf) << 27)
|
|
|
|
#define UTMIP_DPDM_OBSERVE_SEL_FS_J UTMIP_DPDM_OBSERVE_SEL(0xf)
|
|
|
|
#define UTMIP_DPDM_OBSERVE_SEL_FS_K UTMIP_DPDM_OBSERVE_SEL(0xe)
|
|
|
|
#define UTMIP_DPDM_OBSERVE_SEL_FS_SE1 UTMIP_DPDM_OBSERVE_SEL(0xd)
|
|
|
|
#define UTMIP_DPDM_OBSERVE_SEL_FS_SE0 UTMIP_DPDM_OBSERVE_SEL(0xc)
|
|
|
|
#define UTMIP_SUSPEND_EXIT_ON_EDGE BIT(22)
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define UTMIP_MISC_CFG1 0x828
|
|
|
|
#define UTMIP_PLL_ACTIVE_DLY_COUNT(x) (((x) & 0x1f) << 18)
|
|
|
|
#define UTMIP_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 6)
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define UTMIP_DEBOUNCE_CFG0 0x82c
|
|
|
|
#define UTMIP_BIAS_DEBOUNCE_A(x) (((x) & 0xffff) << 0)
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define UTMIP_BAT_CHRG_CFG0 0x830
|
|
|
|
#define UTMIP_PD_CHRG BIT(0)
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define UTMIP_SPARE_CFG0 0x834
|
|
|
|
#define FUSE_SETUP_SEL BIT(3)
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define UTMIP_XCVR_CFG1 0x838
|
|
|
|
#define UTMIP_FORCE_PDDISC_POWERDOWN BIT(0)
|
|
|
|
#define UTMIP_FORCE_PDCHRP_POWERDOWN BIT(2)
|
|
|
|
#define UTMIP_FORCE_PDDR_POWERDOWN BIT(4)
|
|
|
|
#define UTMIP_XCVR_TERM_RANGE_ADJ(x) (((x) & 0xf) << 18)
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
#define UTMIP_BIAS_CFG1 0x83c
|
|
|
|
#define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3)
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2013-08-12 13:06:51 +00:00
|
|
|
/* For Tegra30 and above only, the address is different in Tegra20 */
|
2020-01-06 01:34:02 +00:00
|
|
|
#define USB_USBMODE 0x1f8
|
|
|
|
#define USB_USBMODE_MASK (3 << 0)
|
|
|
|
#define USB_USBMODE_HOST (3 << 0)
|
|
|
|
#define USB_USBMODE_DEVICE (2 << 0)
|
2013-08-12 13:06:51 +00:00
|
|
|
|
2011-03-10 00:28:55 +00:00
|
|
|
static DEFINE_SPINLOCK(utmip_pad_lock);
|
2020-01-06 01:34:02 +00:00
|
|
|
static unsigned int utmip_pad_count;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
|
|
|
struct tegra_xtal_freq {
|
2020-01-06 01:34:02 +00:00
|
|
|
unsigned int freq;
|
2011-03-10 00:28:55 +00:00
|
|
|
u8 enable_delay;
|
|
|
|
u8 stable_count;
|
|
|
|
u8 active_delay;
|
|
|
|
u8 xtal_freq_count;
|
|
|
|
u16 debounce;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct tegra_xtal_freq tegra_freq_table[] = {
|
|
|
|
{
|
|
|
|
.freq = 12000000,
|
|
|
|
.enable_delay = 0x02,
|
|
|
|
.stable_count = 0x2F,
|
|
|
|
.active_delay = 0x04,
|
|
|
|
.xtal_freq_count = 0x76,
|
|
|
|
.debounce = 0x7530,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.freq = 13000000,
|
|
|
|
.enable_delay = 0x02,
|
|
|
|
.stable_count = 0x33,
|
|
|
|
.active_delay = 0x05,
|
|
|
|
.xtal_freq_count = 0x7F,
|
|
|
|
.debounce = 0x7EF4,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.freq = 19200000,
|
|
|
|
.enable_delay = 0x03,
|
|
|
|
.stable_count = 0x4B,
|
|
|
|
.active_delay = 0x06,
|
|
|
|
.xtal_freq_count = 0xBB,
|
|
|
|
.debounce = 0xBB80,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.freq = 26000000,
|
|
|
|
.enable_delay = 0x04,
|
|
|
|
.stable_count = 0x66,
|
|
|
|
.active_delay = 0x09,
|
|
|
|
.xtal_freq_count = 0xFE,
|
|
|
|
.debounce = 0xFDE8,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
static inline struct tegra_usb_phy *to_tegra_usb_phy(struct usb_phy *u_phy)
|
|
|
|
{
|
|
|
|
return container_of(u_phy, struct tegra_usb_phy, u_phy);
|
|
|
|
}
|
|
|
|
|
USB: EHCI: tegra: fix circular module dependencies
The Tegra EHCI driver directly calls various functions in the Tegra USB
PHY driver. The reverse is also true; the PHY driver calls into the EHCI
driver. This is problematic when the two are built as modules.
The calls from the PHY to EHCI driver were originally added in commit
bbdabdb "usb: add APIs to access host registers from Tegra PHY", for the
following reasons:
1) The register being touched is an EHCI register, so logically only the
EHCI driver should touch it.
2) (1) implies that some locking may be needed to correctly implement the
r/m/w access to this shared register.
3) We were expecting to pass only the PHY register space to the Tegra PHY
driver, and hence it would not have access to touch the shared
registers.
To solve this, that commit added functions in the EHCI driver to touch the
shared register on behalf of the PHY driver.
In practice, we ended up not having any locking in the implementaiton of
those functions, and I've been led to believe this is safe. Equally, (3)
did not happen either. Hence, it is possible for the PHY driver to touch
the shared register directly.
Given that, this patch moves the code to touch the shared register back
into the PHY driver, to eliminate the module problems. If we actually
need locking or co-ordination in the future, I propose we put the lock
support into some pre-existing core module, or into a third separate
module, in order to avoid the circular dependencies.
I apologize for my contribution to code churn here.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-06-13 17:24:11 +00:00
|
|
|
static void set_pts(struct tegra_usb_phy *phy, u8 pts_val)
|
|
|
|
{
|
|
|
|
void __iomem *base = phy->regs;
|
2020-01-06 01:34:08 +00:00
|
|
|
u32 val;
|
USB: EHCI: tegra: fix circular module dependencies
The Tegra EHCI driver directly calls various functions in the Tegra USB
PHY driver. The reverse is also true; the PHY driver calls into the EHCI
driver. This is problematic when the two are built as modules.
The calls from the PHY to EHCI driver were originally added in commit
bbdabdb "usb: add APIs to access host registers from Tegra PHY", for the
following reasons:
1) The register being touched is an EHCI register, so logically only the
EHCI driver should touch it.
2) (1) implies that some locking may be needed to correctly implement the
r/m/w access to this shared register.
3) We were expecting to pass only the PHY register space to the Tegra PHY
driver, and hence it would not have access to touch the shared
registers.
To solve this, that commit added functions in the EHCI driver to touch the
shared register on behalf of the PHY driver.
In practice, we ended up not having any locking in the implementaiton of
those functions, and I've been led to believe this is safe. Equally, (3)
did not happen either. Hence, it is possible for the PHY driver to touch
the shared register directly.
Given that, this patch moves the code to touch the shared register back
into the PHY driver, to eliminate the module problems. If we actually
need locking or co-ordination in the future, I propose we put the lock
support into some pre-existing core module, or into a third separate
module, in order to avoid the circular dependencies.
I apologize for my contribution to code churn here.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-06-13 17:24:11 +00:00
|
|
|
|
2013-08-12 13:06:51 +00:00
|
|
|
if (phy->soc_config->has_hostpc) {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + TEGRA_USB_HOSTPC1_DEVLC);
|
2013-08-12 13:06:51 +00:00
|
|
|
val &= ~TEGRA_USB_HOSTPC1_DEVLC_PTS(~0);
|
|
|
|
val |= TEGRA_USB_HOSTPC1_DEVLC_PTS(pts_val);
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + TEGRA_USB_HOSTPC1_DEVLC);
|
2013-08-12 13:06:51 +00:00
|
|
|
} else {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + TEGRA_USB_PORTSC1);
|
|
|
|
val &= ~TEGRA_PORTSC1_RWC_BITS;
|
2013-08-12 13:06:51 +00:00
|
|
|
val &= ~TEGRA_USB_PORTSC1_PTS(~0);
|
|
|
|
val |= TEGRA_USB_PORTSC1_PTS(pts_val);
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + TEGRA_USB_PORTSC1);
|
2013-08-12 13:06:51 +00:00
|
|
|
}
|
USB: EHCI: tegra: fix circular module dependencies
The Tegra EHCI driver directly calls various functions in the Tegra USB
PHY driver. The reverse is also true; the PHY driver calls into the EHCI
driver. This is problematic when the two are built as modules.
The calls from the PHY to EHCI driver were originally added in commit
bbdabdb "usb: add APIs to access host registers from Tegra PHY", for the
following reasons:
1) The register being touched is an EHCI register, so logically only the
EHCI driver should touch it.
2) (1) implies that some locking may be needed to correctly implement the
r/m/w access to this shared register.
3) We were expecting to pass only the PHY register space to the Tegra PHY
driver, and hence it would not have access to touch the shared
registers.
To solve this, that commit added functions in the EHCI driver to touch the
shared register on behalf of the PHY driver.
In practice, we ended up not having any locking in the implementaiton of
those functions, and I've been led to believe this is safe. Equally, (3)
did not happen either. Hence, it is possible for the PHY driver to touch
the shared register directly.
Given that, this patch moves the code to touch the shared register back
into the PHY driver, to eliminate the module problems. If we actually
need locking or co-ordination in the future, I propose we put the lock
support into some pre-existing core module, or into a third separate
module, in order to avoid the circular dependencies.
I apologize for my contribution to code churn here.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-06-13 17:24:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void set_phcd(struct tegra_usb_phy *phy, bool enable)
|
|
|
|
{
|
|
|
|
void __iomem *base = phy->regs;
|
2020-01-06 01:34:08 +00:00
|
|
|
u32 val;
|
USB: EHCI: tegra: fix circular module dependencies
The Tegra EHCI driver directly calls various functions in the Tegra USB
PHY driver. The reverse is also true; the PHY driver calls into the EHCI
driver. This is problematic when the two are built as modules.
The calls from the PHY to EHCI driver were originally added in commit
bbdabdb "usb: add APIs to access host registers from Tegra PHY", for the
following reasons:
1) The register being touched is an EHCI register, so logically only the
EHCI driver should touch it.
2) (1) implies that some locking may be needed to correctly implement the
r/m/w access to this shared register.
3) We were expecting to pass only the PHY register space to the Tegra PHY
driver, and hence it would not have access to touch the shared
registers.
To solve this, that commit added functions in the EHCI driver to touch the
shared register on behalf of the PHY driver.
In practice, we ended up not having any locking in the implementaiton of
those functions, and I've been led to believe this is safe. Equally, (3)
did not happen either. Hence, it is possible for the PHY driver to touch
the shared register directly.
Given that, this patch moves the code to touch the shared register back
into the PHY driver, to eliminate the module problems. If we actually
need locking or co-ordination in the future, I propose we put the lock
support into some pre-existing core module, or into a third separate
module, in order to avoid the circular dependencies.
I apologize for my contribution to code churn here.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-06-13 17:24:11 +00:00
|
|
|
|
2013-08-12 13:06:51 +00:00
|
|
|
if (phy->soc_config->has_hostpc) {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + TEGRA_USB_HOSTPC1_DEVLC);
|
2013-08-12 13:06:51 +00:00
|
|
|
if (enable)
|
|
|
|
val |= TEGRA_USB_HOSTPC1_DEVLC_PHCD;
|
|
|
|
else
|
|
|
|
val &= ~TEGRA_USB_HOSTPC1_DEVLC_PHCD;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + TEGRA_USB_HOSTPC1_DEVLC);
|
2013-08-12 13:06:51 +00:00
|
|
|
} else {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + TEGRA_USB_PORTSC1) & ~PORT_RWC_BITS;
|
2013-08-12 13:06:51 +00:00
|
|
|
if (enable)
|
|
|
|
val |= TEGRA_USB_PORTSC1_PHCD;
|
|
|
|
else
|
|
|
|
val &= ~TEGRA_USB_PORTSC1_PHCD;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + TEGRA_USB_PORTSC1);
|
2013-08-12 13:06:51 +00:00
|
|
|
}
|
USB: EHCI: tegra: fix circular module dependencies
The Tegra EHCI driver directly calls various functions in the Tegra USB
PHY driver. The reverse is also true; the PHY driver calls into the EHCI
driver. This is problematic when the two are built as modules.
The calls from the PHY to EHCI driver were originally added in commit
bbdabdb "usb: add APIs to access host registers from Tegra PHY", for the
following reasons:
1) The register being touched is an EHCI register, so logically only the
EHCI driver should touch it.
2) (1) implies that some locking may be needed to correctly implement the
r/m/w access to this shared register.
3) We were expecting to pass only the PHY register space to the Tegra PHY
driver, and hence it would not have access to touch the shared
registers.
To solve this, that commit added functions in the EHCI driver to touch the
shared register on behalf of the PHY driver.
In practice, we ended up not having any locking in the implementaiton of
those functions, and I've been led to believe this is safe. Equally, (3)
did not happen either. Hence, it is possible for the PHY driver to touch
the shared register directly.
Given that, this patch moves the code to touch the shared register back
into the PHY driver, to eliminate the module problems. If we actually
need locking or co-ordination in the future, I propose we put the lock
support into some pre-existing core module, or into a third separate
module, in order to avoid the circular dependencies.
I apologize for my contribution to code churn here.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-06-13 17:24:11 +00:00
|
|
|
}
|
|
|
|
|
2011-03-10 00:28:55 +00:00
|
|
|
static int utmip_pad_open(struct tegra_usb_phy *phy)
|
|
|
|
{
|
2018-04-09 22:02:58 +00:00
|
|
|
int ret;
|
2018-04-09 22:02:57 +00:00
|
|
|
|
2018-04-09 22:02:58 +00:00
|
|
|
ret = clk_prepare_enable(phy->pad_clk);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(phy->u_phy.dev,
|
|
|
|
"Failed to enable UTMI-pads clock: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock(&utmip_pad_lock);
|
|
|
|
|
|
|
|
ret = reset_control_deassert(phy->pad_rst);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(phy->u_phy.dev,
|
|
|
|
"Failed to initialize UTMI-pads reset: %d\n", ret);
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = reset_control_assert(phy->pad_rst);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(phy->u_phy.dev,
|
|
|
|
"Failed to assert UTMI-pads reset: %d\n", ret);
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
udelay(1);
|
|
|
|
|
|
|
|
ret = reset_control_deassert(phy->pad_rst);
|
|
|
|
if (ret)
|
|
|
|
dev_err(phy->u_phy.dev,
|
|
|
|
"Failed to deassert UTMI-pads reset: %d\n", ret);
|
|
|
|
unlock:
|
|
|
|
spin_unlock(&utmip_pad_lock);
|
|
|
|
|
|
|
|
clk_disable_unprepare(phy->pad_clk);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int utmip_pad_close(struct tegra_usb_phy *phy)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = clk_prepare_enable(phy->pad_clk);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(phy->u_phy.dev,
|
|
|
|
"Failed to enable UTMI-pads clock: %d\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = reset_control_assert(phy->pad_rst);
|
|
|
|
if (ret)
|
|
|
|
dev_err(phy->u_phy.dev,
|
|
|
|
"Failed to assert UTMI-pads reset: %d\n", ret);
|
|
|
|
|
|
|
|
udelay(1);
|
|
|
|
|
|
|
|
clk_disable_unprepare(phy->pad_clk);
|
|
|
|
|
|
|
|
return ret;
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
static int utmip_pad_power_on(struct tegra_usb_phy *phy)
|
2011-03-10 00:28:55 +00:00
|
|
|
{
|
2013-08-12 13:06:53 +00:00
|
|
|
struct tegra_utmip_config *config = phy->config;
|
2020-01-06 01:34:02 +00:00
|
|
|
void __iomem *base = phy->pad_regs;
|
2020-01-06 01:34:08 +00:00
|
|
|
u32 val;
|
2020-01-06 01:34:02 +00:00
|
|
|
int err;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
err = clk_prepare_enable(phy->pad_clk);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:12 +00:00
|
|
|
spin_lock(&utmip_pad_lock);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
|
|
|
if (utmip_pad_count++ == 0) {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_BIAS_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~(UTMIP_OTGPD | UTMIP_BIASPD);
|
2013-08-12 13:06:53 +00:00
|
|
|
|
|
|
|
if (phy->soc_config->requires_extra_tuning_parameters) {
|
|
|
|
val &= ~(UTMIP_HSSQUELCH_LEVEL(~0) |
|
|
|
|
UTMIP_HSDISCON_LEVEL(~0) |
|
|
|
|
UTMIP_HSDISCON_LEVEL_MSB(~0));
|
|
|
|
|
|
|
|
val |= UTMIP_HSSQUELCH_LEVEL(config->hssquelch_level);
|
|
|
|
val |= UTMIP_HSDISCON_LEVEL(config->hsdiscon_level);
|
|
|
|
val |= UTMIP_HSDISCON_LEVEL_MSB(config->hsdiscon_level);
|
|
|
|
}
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_BIAS_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:12 +00:00
|
|
|
spin_unlock(&utmip_pad_lock);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2012-06-05 04:29:35 +00:00
|
|
|
clk_disable_unprepare(phy->pad_clk);
|
2020-01-06 01:34:02 +00:00
|
|
|
|
|
|
|
return 0;
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int utmip_pad_power_off(struct tegra_usb_phy *phy)
|
|
|
|
{
|
|
|
|
void __iomem *base = phy->pad_regs;
|
2020-01-06 01:34:08 +00:00
|
|
|
u32 val;
|
2020-01-06 01:34:11 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = clk_prepare_enable(phy->pad_clk);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2020-01-06 01:34:12 +00:00
|
|
|
spin_lock(&utmip_pad_lock);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
|
|
|
if (!utmip_pad_count) {
|
2018-04-09 22:02:57 +00:00
|
|
|
dev_err(phy->u_phy.dev, "UTMIP pad already powered off\n");
|
2020-01-06 01:34:11 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto ulock;
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (--utmip_pad_count == 0) {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_BIAS_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= UTMIP_OTGPD | UTMIP_BIASPD;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_BIAS_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
2020-01-06 01:34:11 +00:00
|
|
|
ulock:
|
2020-01-06 01:34:12 +00:00
|
|
|
spin_unlock(&utmip_pad_lock);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2012-06-05 04:29:35 +00:00
|
|
|
clk_disable_unprepare(phy->pad_clk);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:11 +00:00
|
|
|
return ret;
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
|
|
|
|
{
|
2017-12-17 17:02:39 +00:00
|
|
|
u32 tmp;
|
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
return readl_relaxed_poll_timeout(reg, tmp, (tmp & mask) == result,
|
|
|
|
2000, 6000);
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
|
|
|
|
{
|
|
|
|
void __iomem *base = phy->regs;
|
2020-01-06 01:34:08 +00:00
|
|
|
u32 val;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2017-10-02 11:22:53 +00:00
|
|
|
/*
|
|
|
|
* The USB driver may have already initiated the phy clock
|
|
|
|
* disable so wait to see if the clock turns off and if not
|
|
|
|
* then proceed with gating the clock.
|
|
|
|
*/
|
|
|
|
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) == 0)
|
|
|
|
return;
|
|
|
|
|
2013-01-16 03:30:20 +00:00
|
|
|
if (phy->is_legacy_phy) {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= USB_SUSP_SET;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
usleep_range(10, 100);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~USB_SUSP_SET;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2020-01-06 01:34:02 +00:00
|
|
|
} else {
|
USB: EHCI: tegra: fix circular module dependencies
The Tegra EHCI driver directly calls various functions in the Tegra USB
PHY driver. The reverse is also true; the PHY driver calls into the EHCI
driver. This is problematic when the two are built as modules.
The calls from the PHY to EHCI driver were originally added in commit
bbdabdb "usb: add APIs to access host registers from Tegra PHY", for the
following reasons:
1) The register being touched is an EHCI register, so logically only the
EHCI driver should touch it.
2) (1) implies that some locking may be needed to correctly implement the
r/m/w access to this shared register.
3) We were expecting to pass only the PHY register space to the Tegra PHY
driver, and hence it would not have access to touch the shared
registers.
To solve this, that commit added functions in the EHCI driver to touch the
shared register on behalf of the PHY driver.
In practice, we ended up not having any locking in the implementaiton of
those functions, and I've been led to believe this is safe. Equally, (3)
did not happen either. Hence, it is possible for the PHY driver to touch
the shared register directly.
Given that, this patch moves the code to touch the shared register back
into the PHY driver, to eliminate the module problems. If we actually
need locking or co-ordination in the future, I propose we put the lock
support into some pre-existing core module, or into a third separate
module, in order to avoid the circular dependencies.
I apologize for my contribution to code churn here.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-06-13 17:24:11 +00:00
|
|
|
set_phcd(phy, true);
|
2020-01-06 01:34:02 +00:00
|
|
|
}
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0))
|
2018-04-09 22:02:57 +00:00
|
|
|
dev_err(phy->u_phy.dev,
|
|
|
|
"Timeout waiting for PHY to stabilize on disable\n");
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
|
|
|
|
{
|
|
|
|
void __iomem *base = phy->regs;
|
2020-01-06 01:34:08 +00:00
|
|
|
u32 val;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2017-10-02 11:22:53 +00:00
|
|
|
/*
|
|
|
|
* The USB driver may have already initiated the phy clock
|
|
|
|
* enable so wait to see if the clock turns on and if not
|
|
|
|
* then proceed with ungating the clock.
|
|
|
|
*/
|
|
|
|
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
|
|
|
|
USB_PHY_CLK_VALID) == 0)
|
|
|
|
return;
|
|
|
|
|
2013-01-16 03:30:20 +00:00
|
|
|
if (phy->is_legacy_phy) {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= USB_SUSP_CLR;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
usleep_range(10, 100);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~USB_SUSP_CLR;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2020-01-06 01:34:02 +00:00
|
|
|
} else {
|
USB: EHCI: tegra: fix circular module dependencies
The Tegra EHCI driver directly calls various functions in the Tegra USB
PHY driver. The reverse is also true; the PHY driver calls into the EHCI
driver. This is problematic when the two are built as modules.
The calls from the PHY to EHCI driver were originally added in commit
bbdabdb "usb: add APIs to access host registers from Tegra PHY", for the
following reasons:
1) The register being touched is an EHCI register, so logically only the
EHCI driver should touch it.
2) (1) implies that some locking may be needed to correctly implement the
r/m/w access to this shared register.
3) We were expecting to pass only the PHY register space to the Tegra PHY
driver, and hence it would not have access to touch the shared
registers.
To solve this, that commit added functions in the EHCI driver to touch the
shared register on behalf of the PHY driver.
In practice, we ended up not having any locking in the implementaiton of
those functions, and I've been led to believe this is safe. Equally, (3)
did not happen either. Hence, it is possible for the PHY driver to touch
the shared register directly.
Given that, this patch moves the code to touch the shared register back
into the PHY driver, to eliminate the module problems. If we actually
need locking or co-ordination in the future, I propose we put the lock
support into some pre-existing core module, or into a third separate
module, in order to avoid the circular dependencies.
I apologize for my contribution to code churn here.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-06-13 17:24:11 +00:00
|
|
|
set_phcd(phy, false);
|
2020-01-06 01:34:02 +00:00
|
|
|
}
|
2011-03-10 00:28:55 +00:00
|
|
|
|
|
|
|
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
|
2020-01-06 01:34:02 +00:00
|
|
|
USB_PHY_CLK_VALID))
|
2018-04-09 22:02:57 +00:00
|
|
|
dev_err(phy->u_phy.dev,
|
|
|
|
"Timeout waiting for PHY to stabilize on enable\n");
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int utmi_phy_power_on(struct tegra_usb_phy *phy)
|
|
|
|
{
|
|
|
|
struct tegra_utmip_config *config = phy->config;
|
2020-01-06 01:34:02 +00:00
|
|
|
void __iomem *base = phy->regs;
|
2020-01-06 01:34:08 +00:00
|
|
|
u32 val;
|
2020-01-06 01:34:02 +00:00
|
|
|
int err;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= UTMIP_RESET;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2013-01-16 03:30:20 +00:00
|
|
|
if (phy->is_legacy_phy) {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB1_LEGACY_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= USB1_NO_LEGACY_MODE;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB1_LEGACY_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_TX_CFG0);
|
2013-08-12 13:06:50 +00:00
|
|
|
val |= UTMIP_FS_PREABMLE_J;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_TX_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_HSRX_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~(UTMIP_IDLE_WAIT(~0) | UTMIP_ELASTIC_LIMIT(~0));
|
|
|
|
val |= UTMIP_IDLE_WAIT(config->idle_wait_delay);
|
|
|
|
val |= UTMIP_ELASTIC_LIMIT(config->elastic_limit);
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_HSRX_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_HSRX_CFG1);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~UTMIP_HS_SYNC_START_DLY(~0);
|
|
|
|
val |= UTMIP_HS_SYNC_START_DLY(config->hssync_start_delay);
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_HSRX_CFG1);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_DEBOUNCE_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~UTMIP_BIAS_DEBOUNCE_A(~0);
|
|
|
|
val |= UTMIP_BIAS_DEBOUNCE_A(phy->freq->debounce);
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_DEBOUNCE_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_MISC_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~UTMIP_SUSPEND_EXIT_ON_EDGE;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_MISC_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2013-08-12 13:06:51 +00:00
|
|
|
if (!phy->soc_config->utmi_pll_config_in_car_module) {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_MISC_CFG1);
|
2013-08-12 13:06:51 +00:00
|
|
|
val &= ~(UTMIP_PLL_ACTIVE_DLY_COUNT(~0) |
|
|
|
|
UTMIP_PLLU_STABLE_COUNT(~0));
|
|
|
|
val |= UTMIP_PLL_ACTIVE_DLY_COUNT(phy->freq->active_delay) |
|
|
|
|
UTMIP_PLLU_STABLE_COUNT(phy->freq->stable_count);
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_MISC_CFG1);
|
2013-08-12 13:06:51 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_PLL_CFG1);
|
2013-08-12 13:06:51 +00:00
|
|
|
val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) |
|
|
|
|
UTMIP_PLLU_ENABLE_DLY_COUNT(~0));
|
|
|
|
val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) |
|
|
|
|
UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay);
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_PLL_CFG1);
|
2013-08-12 13:06:51 +00:00
|
|
|
}
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2013-07-25 18:38:09 +00:00
|
|
|
if (phy->mode == USB_DR_MODE_PERIPHERAL) {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~(USB_WAKE_ON_CNNT_EN_DEV | USB_WAKE_ON_DISCON_EN_DEV);
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2013-08-12 13:06:50 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_BAT_CHRG_CFG0);
|
2013-08-12 13:06:50 +00:00
|
|
|
val &= ~UTMIP_PD_CHRG;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_BAT_CHRG_CFG0);
|
2013-08-12 13:06:50 +00:00
|
|
|
} else {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_BAT_CHRG_CFG0);
|
2013-08-12 13:06:50 +00:00
|
|
|
val |= UTMIP_PD_CHRG;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_BAT_CHRG_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
err = utmip_pad_power_on(phy);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_XCVR_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~(UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
|
2013-08-12 13:06:50 +00:00
|
|
|
UTMIP_FORCE_PDZI_POWERDOWN | UTMIP_XCVR_LSBIAS_SEL |
|
|
|
|
UTMIP_XCVR_SETUP(~0) | UTMIP_XCVR_SETUP_MSB(~0) |
|
2013-08-12 13:06:53 +00:00
|
|
|
UTMIP_XCVR_LSFSLEW(~0) | UTMIP_XCVR_LSRSLEW(~0));
|
|
|
|
|
|
|
|
if (!config->xcvr_setup_use_fuses) {
|
|
|
|
val |= UTMIP_XCVR_SETUP(config->xcvr_setup);
|
|
|
|
val |= UTMIP_XCVR_SETUP_MSB(config->xcvr_setup);
|
|
|
|
}
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= UTMIP_XCVR_LSFSLEW(config->xcvr_lsfslew);
|
|
|
|
val |= UTMIP_XCVR_LSRSLEW(config->xcvr_lsrslew);
|
2013-08-12 13:06:53 +00:00
|
|
|
|
|
|
|
if (phy->soc_config->requires_extra_tuning_parameters) {
|
|
|
|
val &= ~(UTMIP_XCVR_HSSLEW(~0) | UTMIP_XCVR_HSSLEW_MSB(~0));
|
|
|
|
val |= UTMIP_XCVR_HSSLEW(config->xcvr_hsslew);
|
|
|
|
val |= UTMIP_XCVR_HSSLEW_MSB(config->xcvr_hsslew);
|
|
|
|
}
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_XCVR_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_XCVR_CFG1);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~(UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
|
|
|
|
UTMIP_FORCE_PDDR_POWERDOWN | UTMIP_XCVR_TERM_RANGE_ADJ(~0));
|
|
|
|
val |= UTMIP_XCVR_TERM_RANGE_ADJ(config->term_range_adj);
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_XCVR_CFG1);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_BIAS_CFG1);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~UTMIP_BIAS_PDTRK_COUNT(~0);
|
|
|
|
val |= UTMIP_BIAS_PDTRK_COUNT(0x5);
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_BIAS_CFG1);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_SPARE_CFG0);
|
2013-08-12 13:06:53 +00:00
|
|
|
if (config->xcvr_setup_use_fuses)
|
|
|
|
val |= FUSE_SETUP_SEL;
|
|
|
|
else
|
|
|
|
val &= ~FUSE_SETUP_SEL;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_SPARE_CFG0);
|
2013-08-12 13:06:53 +00:00
|
|
|
|
|
|
|
if (!phy->is_legacy_phy) {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= UTMIP_PHY_ENABLE;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~UTMIP_RESET;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2013-01-16 03:30:20 +00:00
|
|
|
if (phy->is_legacy_phy) {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB1_LEGACY_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~USB1_VBUS_SENSE_CTL_MASK;
|
|
|
|
val |= USB1_VBUS_SENSE_CTL_A_SESS_VLD;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB1_LEGACY_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~USB_SUSP_SET;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
utmi_phy_clk_enable(phy);
|
|
|
|
|
2013-08-12 13:06:51 +00:00
|
|
|
if (phy->soc_config->requires_usbmode_setup) {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_USBMODE);
|
2013-08-12 13:06:51 +00:00
|
|
|
val &= ~USB_USBMODE_MASK;
|
|
|
|
if (phy->mode == USB_DR_MODE_HOST)
|
|
|
|
val |= USB_USBMODE_HOST;
|
|
|
|
else
|
|
|
|
val |= USB_USBMODE_DEVICE;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_USBMODE);
|
2013-08-12 13:06:51 +00:00
|
|
|
}
|
|
|
|
|
2013-01-17 20:15:37 +00:00
|
|
|
if (!phy->is_legacy_phy)
|
USB: EHCI: tegra: fix circular module dependencies
The Tegra EHCI driver directly calls various functions in the Tegra USB
PHY driver. The reverse is also true; the PHY driver calls into the EHCI
driver. This is problematic when the two are built as modules.
The calls from the PHY to EHCI driver were originally added in commit
bbdabdb "usb: add APIs to access host registers from Tegra PHY", for the
following reasons:
1) The register being touched is an EHCI register, so logically only the
EHCI driver should touch it.
2) (1) implies that some locking may be needed to correctly implement the
r/m/w access to this shared register.
3) We were expecting to pass only the PHY register space to the Tegra PHY
driver, and hence it would not have access to touch the shared
registers.
To solve this, that commit added functions in the EHCI driver to touch the
shared register on behalf of the PHY driver.
In practice, we ended up not having any locking in the implementaiton of
those functions, and I've been led to believe this is safe. Equally, (3)
did not happen either. Hence, it is possible for the PHY driver to touch
the shared register directly.
Given that, this patch moves the code to touch the shared register back
into the PHY driver, to eliminate the module problems. If we actually
need locking or co-ordination in the future, I propose we put the lock
support into some pre-existing core module, or into a third separate
module, in order to avoid the circular dependencies.
I apologize for my contribution to code churn here.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-06-13 17:24:11 +00:00
|
|
|
set_pts(phy, 0);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-05 13:20:23 +00:00
|
|
|
static int utmi_phy_power_off(struct tegra_usb_phy *phy)
|
2011-03-10 00:28:55 +00:00
|
|
|
{
|
|
|
|
void __iomem *base = phy->regs;
|
2020-01-06 01:34:08 +00:00
|
|
|
u32 val;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
|
|
|
utmi_phy_clk_disable(phy);
|
|
|
|
|
2013-07-25 18:38:09 +00:00
|
|
|
if (phy->mode == USB_DR_MODE_PERIPHERAL) {
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~USB_WAKEUP_DEBOUNCE_COUNT(~0);
|
|
|
|
val |= USB_WAKE_ON_CNNT_EN_DEV | USB_WAKEUP_DEBOUNCE_COUNT(5);
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= UTMIP_RESET;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_BAT_CHRG_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= UTMIP_PD_CHRG;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_BAT_CHRG_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_XCVR_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= UTMIP_FORCE_PD_POWERDOWN | UTMIP_FORCE_PD2_POWERDOWN |
|
|
|
|
UTMIP_FORCE_PDZI_POWERDOWN;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_XCVR_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_XCVR_CFG1);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= UTMIP_FORCE_PDDISC_POWERDOWN | UTMIP_FORCE_PDCHRP_POWERDOWN |
|
|
|
|
UTMIP_FORCE_PDDR_POWERDOWN;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_XCVR_CFG1);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2012-09-05 13:20:23 +00:00
|
|
|
return utmip_pad_power_off(phy);
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void utmi_phy_preresume(struct tegra_usb_phy *phy)
|
|
|
|
{
|
|
|
|
void __iomem *base = phy->regs;
|
2020-01-06 01:34:08 +00:00
|
|
|
u32 val;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_TX_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= UTMIP_HS_DISCON_DISABLE;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_TX_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void utmi_phy_postresume(struct tegra_usb_phy *phy)
|
|
|
|
{
|
|
|
|
void __iomem *base = phy->regs;
|
2020-01-06 01:34:08 +00:00
|
|
|
u32 val;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_TX_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~UTMIP_HS_DISCON_DISABLE;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_TX_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void utmi_phy_restore_start(struct tegra_usb_phy *phy,
|
|
|
|
enum tegra_usb_phy_port_speed port_speed)
|
|
|
|
{
|
|
|
|
void __iomem *base = phy->regs;
|
2020-01-06 01:34:08 +00:00
|
|
|
u32 val;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_MISC_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~UTMIP_DPDM_OBSERVE_SEL(~0);
|
|
|
|
if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW)
|
|
|
|
val |= UTMIP_DPDM_OBSERVE_SEL_FS_K;
|
|
|
|
else
|
|
|
|
val |= UTMIP_DPDM_OBSERVE_SEL_FS_J;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_MISC_CFG0);
|
2020-01-06 01:34:02 +00:00
|
|
|
usleep_range(1, 10);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_MISC_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= UTMIP_DPDM_OBSERVE;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_MISC_CFG0);
|
2020-01-06 01:34:02 +00:00
|
|
|
usleep_range(10, 100);
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void utmi_phy_restore_end(struct tegra_usb_phy *phy)
|
|
|
|
{
|
|
|
|
void __iomem *base = phy->regs;
|
2020-01-06 01:34:08 +00:00
|
|
|
u32 val;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + UTMIP_MISC_CFG0);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~UTMIP_DPDM_OBSERVE;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + UTMIP_MISC_CFG0);
|
2020-01-06 01:34:02 +00:00
|
|
|
usleep_range(10, 100);
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
|
|
|
|
{
|
|
|
|
void __iomem *base = phy->regs;
|
2020-01-06 01:34:08 +00:00
|
|
|
u32 val;
|
2020-01-06 01:34:02 +00:00
|
|
|
int err;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:09 +00:00
|
|
|
gpiod_set_value_cansleep(phy->reset_gpio, 1);
|
2020-01-06 01:34:02 +00:00
|
|
|
|
|
|
|
err = clk_prepare_enable(phy->clk);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
usleep_range(5000, 6000);
|
|
|
|
|
2020-01-06 01:34:09 +00:00
|
|
|
gpiod_set_value_cansleep(phy->reset_gpio, 0);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
usleep_range(1000, 2000);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= UHSIC_RESET;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + ULPI_TIMING_CTRL_0);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + ULPI_TIMING_CTRL_0);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= ULPI_PHY_ENABLE;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
|
|
|
val = 0;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + ULPI_TIMING_CTRL_1);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
|
|
|
val |= ULPI_DATA_TRIMMER_SEL(4);
|
|
|
|
val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
|
|
|
|
val |= ULPI_DIR_TRIMMER_SEL(4);
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + ULPI_TIMING_CTRL_1);
|
2020-01-06 01:34:02 +00:00
|
|
|
usleep_range(10, 100);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
|
|
|
val |= ULPI_DATA_TRIMMER_LOAD;
|
|
|
|
val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
|
|
|
|
val |= ULPI_DIR_TRIMMER_LOAD;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + ULPI_TIMING_CTRL_1);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
|
|
|
/* Fix VbusInvalid due to floating VBUS */
|
2020-01-06 01:34:02 +00:00
|
|
|
err = usb_phy_io_write(phy->ulpi, 0x40, 0x08);
|
|
|
|
if (err) {
|
|
|
|
dev_err(phy->u_phy.dev, "ULPI write failed: %d\n", err);
|
|
|
|
goto disable_clk;
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
err = usb_phy_io_write(phy->ulpi, 0x80, 0x0B);
|
|
|
|
if (err) {
|
|
|
|
dev_err(phy->u_phy.dev, "ULPI write failed: %d\n", err);
|
|
|
|
goto disable_clk;
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val |= USB_SUSP_CLR;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2020-01-06 01:34:02 +00:00
|
|
|
usleep_range(100, 1000);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:04 +00:00
|
|
|
val = readl_relaxed(base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
val &= ~USB_SUSP_CLR;
|
2020-01-06 01:34:04 +00:00
|
|
|
writel_relaxed(val, base + USB_SUSP_CTRL);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
|
|
|
return 0;
|
2020-01-06 01:34:02 +00:00
|
|
|
|
|
|
|
disable_clk:
|
|
|
|
clk_disable_unprepare(phy->clk);
|
|
|
|
|
|
|
|
return err;
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
2012-09-05 13:20:23 +00:00
|
|
|
static int ulpi_phy_power_off(struct tegra_usb_phy *phy)
|
2011-03-10 00:28:55 +00:00
|
|
|
{
|
2020-01-06 01:34:09 +00:00
|
|
|
gpiod_set_value_cansleep(phy->reset_gpio, 1);
|
2020-01-06 01:33:59 +00:00
|
|
|
usleep_range(5000, 6000);
|
|
|
|
clk_disable_unprepare(phy->clk);
|
|
|
|
|
|
|
|
return 0;
|
2012-09-05 13:20:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
|
|
|
|
{
|
2020-01-06 01:34:00 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if (phy->powered_on)
|
|
|
|
return 0;
|
|
|
|
|
2013-01-16 03:30:21 +00:00
|
|
|
if (phy->is_ulpi_phy)
|
2020-01-06 01:34:00 +00:00
|
|
|
err = ulpi_phy_power_on(phy);
|
2012-09-05 13:20:23 +00:00
|
|
|
else
|
2020-01-06 01:34:00 +00:00
|
|
|
err = utmi_phy_power_on(phy);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
phy->powered_on = true;
|
|
|
|
|
|
|
|
return 0;
|
2012-09-05 13:20:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
|
|
|
|
{
|
2020-01-06 01:34:00 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!phy->powered_on)
|
|
|
|
return 0;
|
|
|
|
|
2013-01-16 03:30:21 +00:00
|
|
|
if (phy->is_ulpi_phy)
|
2020-01-06 01:34:00 +00:00
|
|
|
err = ulpi_phy_power_off(phy);
|
2012-09-05 13:20:23 +00:00
|
|
|
else
|
2020-01-06 01:34:00 +00:00
|
|
|
err = utmi_phy_power_off(phy);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
phy->powered_on = false;
|
|
|
|
|
|
|
|
return 0;
|
2012-09-05 13:20:23 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:01 +00:00
|
|
|
static void tegra_usb_phy_shutdown(struct usb_phy *u_phy)
|
|
|
|
{
|
2020-01-06 01:34:02 +00:00
|
|
|
struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
|
2020-01-06 01:34:01 +00:00
|
|
|
|
|
|
|
if (WARN_ON(!phy->freq))
|
|
|
|
return;
|
|
|
|
|
|
|
|
tegra_usb_phy_power_off(phy);
|
|
|
|
|
|
|
|
if (!phy->is_ulpi_phy)
|
|
|
|
utmip_pad_close(phy);
|
|
|
|
|
2020-01-06 01:34:05 +00:00
|
|
|
regulator_disable(phy->vbus);
|
2020-01-06 01:34:01 +00:00
|
|
|
clk_disable_unprepare(phy->pll_u);
|
|
|
|
|
|
|
|
phy->freq = NULL;
|
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
static int tegra_usb_phy_set_suspend(struct usb_phy *u_phy, int suspend)
|
2012-09-05 13:20:23 +00:00
|
|
|
{
|
2020-01-06 01:34:02 +00:00
|
|
|
struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
|
2020-01-06 01:34:01 +00:00
|
|
|
|
|
|
|
if (WARN_ON(!phy->freq))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-09-05 13:20:23 +00:00
|
|
|
if (suspend)
|
|
|
|
return tegra_usb_phy_power_off(phy);
|
|
|
|
else
|
|
|
|
return tegra_usb_phy_power_on(phy);
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:01 +00:00
|
|
|
static int tegra_usb_phy_init(struct usb_phy *u_phy)
|
2013-05-16 14:13:02 +00:00
|
|
|
{
|
2020-01-06 01:34:02 +00:00
|
|
|
struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
|
2013-05-16 14:13:02 +00:00
|
|
|
unsigned long parent_rate;
|
2020-01-06 01:34:02 +00:00
|
|
|
unsigned int i;
|
2013-05-16 14:13:02 +00:00
|
|
|
int err;
|
|
|
|
|
2020-01-06 01:34:01 +00:00
|
|
|
if (WARN_ON(phy->freq))
|
|
|
|
return 0;
|
2013-05-16 14:13:02 +00:00
|
|
|
|
|
|
|
err = clk_prepare_enable(phy->pll_u);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
|
|
|
parent_rate = clk_get_rate(clk_get_parent(phy->pll_u));
|
|
|
|
for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) {
|
|
|
|
if (tegra_freq_table[i].freq == parent_rate) {
|
|
|
|
phy->freq = &tegra_freq_table[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!phy->freq) {
|
2018-04-09 22:02:57 +00:00
|
|
|
dev_err(phy->u_phy.dev, "Invalid pll_u parent rate %ld\n",
|
|
|
|
parent_rate);
|
2011-03-10 00:28:55 +00:00
|
|
|
err = -EINVAL;
|
2020-01-06 01:34:10 +00:00
|
|
|
goto disable_clk;
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:05 +00:00
|
|
|
err = regulator_enable(phy->vbus);
|
|
|
|
if (err) {
|
|
|
|
dev_err(phy->u_phy.dev,
|
|
|
|
"Failed to enable USB VBUS regulator: %d\n", err);
|
2020-01-06 01:34:10 +00:00
|
|
|
goto disable_clk;
|
2013-07-17 07:37:49 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:09 +00:00
|
|
|
if (!phy->is_ulpi_phy) {
|
2013-05-16 14:13:02 +00:00
|
|
|
err = utmip_pad_open(phy);
|
2020-01-06 01:34:09 +00:00
|
|
|
if (err)
|
2020-01-06 01:34:10 +00:00
|
|
|
goto disable_vbus;
|
2020-01-06 01:34:09 +00:00
|
|
|
}
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:01 +00:00
|
|
|
err = tegra_usb_phy_power_on(phy);
|
|
|
|
if (err)
|
|
|
|
goto close_phy;
|
|
|
|
|
2013-05-16 14:13:02 +00:00
|
|
|
return 0;
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:01 +00:00
|
|
|
close_phy:
|
|
|
|
if (!phy->is_ulpi_phy)
|
|
|
|
utmip_pad_close(phy);
|
2020-01-06 01:34:10 +00:00
|
|
|
|
|
|
|
disable_vbus:
|
|
|
|
regulator_disable(phy->vbus);
|
|
|
|
|
|
|
|
disable_clk:
|
2012-06-05 04:29:35 +00:00
|
|
|
clk_disable_unprepare(phy->pll_u);
|
2020-01-06 01:34:01 +00:00
|
|
|
|
|
|
|
phy->freq = NULL;
|
|
|
|
|
2013-05-16 14:13:02 +00:00
|
|
|
return err;
|
2011-03-10 00:28:55 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
void tegra_usb_phy_preresume(struct usb_phy *u_phy)
|
2011-03-10 00:28:55 +00:00
|
|
|
{
|
2020-01-06 01:34:02 +00:00
|
|
|
struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
|
2013-01-24 10:27:03 +00:00
|
|
|
|
2013-01-16 03:30:21 +00:00
|
|
|
if (!phy->is_ulpi_phy)
|
2011-03-10 00:28:55 +00:00
|
|
|
utmi_phy_preresume(phy);
|
|
|
|
}
|
2012-03-02 20:58:42 +00:00
|
|
|
EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
void tegra_usb_phy_postresume(struct usb_phy *u_phy)
|
2011-03-10 00:28:55 +00:00
|
|
|
{
|
2020-01-06 01:34:02 +00:00
|
|
|
struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
|
2013-01-24 10:27:03 +00:00
|
|
|
|
2013-01-16 03:30:21 +00:00
|
|
|
if (!phy->is_ulpi_phy)
|
2011-03-10 00:28:55 +00:00
|
|
|
utmi_phy_postresume(phy);
|
|
|
|
}
|
2012-03-02 20:58:42 +00:00
|
|
|
EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
void tegra_ehci_phy_restore_start(struct usb_phy *u_phy,
|
|
|
|
enum tegra_usb_phy_port_speed port_speed)
|
2011-03-10 00:28:55 +00:00
|
|
|
{
|
2020-01-06 01:34:02 +00:00
|
|
|
struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
|
2013-01-24 10:27:03 +00:00
|
|
|
|
2013-01-16 03:30:21 +00:00
|
|
|
if (!phy->is_ulpi_phy)
|
2011-03-10 00:28:55 +00:00
|
|
|
utmi_phy_restore_start(phy, port_speed);
|
|
|
|
}
|
2012-03-02 20:58:42 +00:00
|
|
|
EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
void tegra_ehci_phy_restore_end(struct usb_phy *u_phy)
|
2011-03-10 00:28:55 +00:00
|
|
|
{
|
2020-01-06 01:34:02 +00:00
|
|
|
struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy);
|
2013-01-24 10:27:03 +00:00
|
|
|
|
2013-01-16 03:30:21 +00:00
|
|
|
if (!phy->is_ulpi_phy)
|
2011-03-10 00:28:55 +00:00
|
|
|
utmi_phy_restore_end(phy);
|
|
|
|
}
|
2012-03-02 20:58:42 +00:00
|
|
|
EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end);
|
2011-03-10 00:28:55 +00:00
|
|
|
|
2013-07-17 06:31:01 +00:00
|
|
|
static int read_utmi_param(struct platform_device *pdev, const char *param,
|
|
|
|
u8 *dest)
|
|
|
|
{
|
|
|
|
u32 value;
|
2020-01-06 01:34:02 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
err = of_property_read_u32(pdev->dev.of_node, param, &value);
|
|
|
|
if (err)
|
2018-04-09 22:02:57 +00:00
|
|
|
dev_err(&pdev->dev,
|
|
|
|
"Failed to read USB UTMI parameter %s: %d\n",
|
2013-07-17 06:31:01 +00:00
|
|
|
param, err);
|
2020-01-06 01:34:02 +00:00
|
|
|
else
|
|
|
|
*dest = value;
|
|
|
|
|
2013-07-17 06:31:01 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy,
|
|
|
|
struct platform_device *pdev)
|
|
|
|
{
|
2020-01-06 01:34:02 +00:00
|
|
|
struct tegra_utmip_config *config;
|
2013-07-17 06:31:01 +00:00
|
|
|
struct resource *res;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
tegra_phy->is_ulpi_phy = false;
|
|
|
|
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
|
|
if (!res) {
|
2018-04-09 22:02:57 +00:00
|
|
|
dev_err(&pdev->dev, "Failed to get UTMI pad regs\n");
|
2013-07-17 06:31:01 +00:00
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
|
2020-02-02 22:42:59 +00:00
|
|
|
/*
|
|
|
|
* Note that UTMI pad registers are shared by all PHYs, therefore
|
|
|
|
* devm_platform_ioremap_resource() can't be used here.
|
|
|
|
*/
|
2013-07-17 06:31:01 +00:00
|
|
|
tegra_phy->pad_regs = devm_ioremap(&pdev->dev, res->start,
|
2020-01-06 01:34:02 +00:00
|
|
|
resource_size(res));
|
2013-12-04 02:02:44 +00:00
|
|
|
if (!tegra_phy->pad_regs) {
|
2018-04-09 22:02:57 +00:00
|
|
|
dev_err(&pdev->dev, "Failed to remap UTMI pad regs\n");
|
2013-07-17 06:31:01 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2014-07-21 11:37:37 +00:00
|
|
|
tegra_phy->config = devm_kzalloc(&pdev->dev, sizeof(*config),
|
|
|
|
GFP_KERNEL);
|
2014-10-14 07:56:13 +00:00
|
|
|
if (!tegra_phy->config)
|
2013-07-17 06:31:01 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
config = tegra_phy->config;
|
|
|
|
|
|
|
|
err = read_utmi_param(pdev, "nvidia,hssync-start-delay",
|
2020-01-06 01:34:02 +00:00
|
|
|
&config->hssync_start_delay);
|
|
|
|
if (err)
|
2013-07-17 06:31:01 +00:00
|
|
|
return err;
|
|
|
|
|
|
|
|
err = read_utmi_param(pdev, "nvidia,elastic-limit",
|
2020-01-06 01:34:02 +00:00
|
|
|
&config->elastic_limit);
|
|
|
|
if (err)
|
2013-07-17 06:31:01 +00:00
|
|
|
return err;
|
|
|
|
|
|
|
|
err = read_utmi_param(pdev, "nvidia,idle-wait-delay",
|
2020-01-06 01:34:02 +00:00
|
|
|
&config->idle_wait_delay);
|
|
|
|
if (err)
|
2013-07-17 06:31:01 +00:00
|
|
|
return err;
|
|
|
|
|
|
|
|
err = read_utmi_param(pdev, "nvidia,term-range-adj",
|
2020-01-06 01:34:02 +00:00
|
|
|
&config->term_range_adj);
|
|
|
|
if (err)
|
2013-07-17 06:31:01 +00:00
|
|
|
return err;
|
|
|
|
|
|
|
|
err = read_utmi_param(pdev, "nvidia,xcvr-lsfslew",
|
2020-01-06 01:34:02 +00:00
|
|
|
&config->xcvr_lsfslew);
|
|
|
|
if (err)
|
2013-07-17 06:31:01 +00:00
|
|
|
return err;
|
|
|
|
|
|
|
|
err = read_utmi_param(pdev, "nvidia,xcvr-lsrslew",
|
2020-01-06 01:34:02 +00:00
|
|
|
&config->xcvr_lsrslew);
|
|
|
|
if (err)
|
2013-07-17 06:31:01 +00:00
|
|
|
return err;
|
|
|
|
|
2013-08-12 13:06:53 +00:00
|
|
|
if (tegra_phy->soc_config->requires_extra_tuning_parameters) {
|
|
|
|
err = read_utmi_param(pdev, "nvidia,xcvr-hsslew",
|
2020-01-06 01:34:02 +00:00
|
|
|
&config->xcvr_hsslew);
|
|
|
|
if (err)
|
2013-08-12 13:06:53 +00:00
|
|
|
return err;
|
|
|
|
|
|
|
|
err = read_utmi_param(pdev, "nvidia,hssquelch-level",
|
2020-01-06 01:34:02 +00:00
|
|
|
&config->hssquelch_level);
|
|
|
|
if (err)
|
2013-08-12 13:06:53 +00:00
|
|
|
return err;
|
|
|
|
|
|
|
|
err = read_utmi_param(pdev, "nvidia,hsdiscon-level",
|
2020-01-06 01:34:02 +00:00
|
|
|
&config->hsdiscon_level);
|
|
|
|
if (err)
|
2013-08-12 13:06:53 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
config->xcvr_setup_use_fuses = of_property_read_bool(
|
|
|
|
pdev->dev.of_node, "nvidia,xcvr-setup-use-fuses");
|
|
|
|
|
|
|
|
if (!config->xcvr_setup_use_fuses) {
|
|
|
|
err = read_utmi_param(pdev, "nvidia,xcvr-setup",
|
2020-01-06 01:34:02 +00:00
|
|
|
&config->xcvr_setup);
|
|
|
|
if (err)
|
2013-08-12 13:06:53 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-07-17 06:31:01 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-08-12 13:06:51 +00:00
|
|
|
static const struct tegra_phy_soc_config tegra20_soc_config = {
|
|
|
|
.utmi_pll_config_in_car_module = false,
|
|
|
|
.has_hostpc = false,
|
|
|
|
.requires_usbmode_setup = false,
|
|
|
|
.requires_extra_tuning_parameters = false,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct tegra_phy_soc_config tegra30_soc_config = {
|
|
|
|
.utmi_pll_config_in_car_module = true,
|
|
|
|
.has_hostpc = true,
|
|
|
|
.requires_usbmode_setup = true,
|
|
|
|
.requires_extra_tuning_parameters = true,
|
|
|
|
};
|
|
|
|
|
2014-06-18 04:43:50 +00:00
|
|
|
static const struct of_device_id tegra_usb_phy_id_table[] = {
|
2013-08-12 13:06:51 +00:00
|
|
|
{ .compatible = "nvidia,tegra30-usb-phy", .data = &tegra30_soc_config },
|
|
|
|
{ .compatible = "nvidia,tegra20-usb-phy", .data = &tegra20_soc_config },
|
|
|
|
{ },
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, tegra_usb_phy_id_table);
|
|
|
|
|
2013-05-16 14:13:02 +00:00
|
|
|
static int tegra_usb_phy_probe(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct device_node *np = pdev->dev.of_node;
|
2020-01-06 01:34:02 +00:00
|
|
|
struct tegra_usb_phy *tegra_phy;
|
2013-07-25 18:38:08 +00:00
|
|
|
enum usb_phy_interface phy_type;
|
2020-01-06 01:34:02 +00:00
|
|
|
struct reset_control *reset;
|
2020-01-06 01:34:09 +00:00
|
|
|
struct gpio_desc *gpiod;
|
2020-01-06 01:34:02 +00:00
|
|
|
struct resource *res;
|
2020-01-06 01:34:07 +00:00
|
|
|
struct usb_phy *phy;
|
2013-05-16 14:13:02 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
tegra_phy = devm_kzalloc(&pdev->dev, sizeof(*tegra_phy), GFP_KERNEL);
|
2014-10-14 07:56:13 +00:00
|
|
|
if (!tegra_phy)
|
2013-05-16 14:13:02 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
tegra_phy->soc_config = of_device_get_match_data(&pdev->dev);
|
2013-08-12 13:06:51 +00:00
|
|
|
|
2013-05-16 14:13:02 +00:00
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
if (!res) {
|
|
|
|
dev_err(&pdev->dev, "Failed to get I/O memory\n");
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
|
2020-02-02 22:42:59 +00:00
|
|
|
/*
|
|
|
|
* Note that PHY and USB controller are using shared registers,
|
|
|
|
* therefore devm_platform_ioremap_resource() can't be used here.
|
|
|
|
*/
|
2013-05-16 14:13:02 +00:00
|
|
|
tegra_phy->regs = devm_ioremap(&pdev->dev, res->start,
|
2020-01-06 01:34:02 +00:00
|
|
|
resource_size(res));
|
2013-05-16 14:13:02 +00:00
|
|
|
if (!tegra_phy->regs) {
|
|
|
|
dev_err(&pdev->dev, "Failed to remap I/O memory\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
tegra_phy->is_legacy_phy =
|
|
|
|
of_property_read_bool(np, "nvidia,has-legacy-mode");
|
|
|
|
|
2013-07-25 18:38:09 +00:00
|
|
|
if (of_find_property(np, "dr_mode", NULL))
|
2015-09-21 08:14:34 +00:00
|
|
|
tegra_phy->mode = usb_get_dr_mode(&pdev->dev);
|
2013-07-25 18:38:09 +00:00
|
|
|
else
|
|
|
|
tegra_phy->mode = USB_DR_MODE_HOST;
|
|
|
|
|
|
|
|
if (tegra_phy->mode == USB_DR_MODE_UNKNOWN) {
|
|
|
|
dev_err(&pdev->dev, "dr_mode is invalid\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2013-05-16 14:13:02 +00:00
|
|
|
|
2013-07-17 07:37:49 +00:00
|
|
|
/* On some boards, the VBUS regulator doesn't need to be controlled */
|
2020-01-06 01:34:05 +00:00
|
|
|
tegra_phy->vbus = devm_regulator_get(&pdev->dev, "vbus");
|
|
|
|
if (IS_ERR(tegra_phy->vbus))
|
|
|
|
return PTR_ERR(tegra_phy->vbus);
|
2013-07-17 07:37:49 +00:00
|
|
|
|
2020-01-06 01:34:01 +00:00
|
|
|
tegra_phy->pll_u = devm_clk_get(&pdev->dev, "pll_u");
|
|
|
|
err = PTR_ERR_OR_ZERO(tegra_phy->pll_u);
|
|
|
|
if (err) {
|
|
|
|
dev_err(&pdev->dev, "Failed to get pll_u clock: %d\n", err);
|
2013-05-16 14:13:02 +00:00
|
|
|
return err;
|
2020-01-06 01:34:01 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
phy_type = of_usb_get_phy_mode(np);
|
|
|
|
switch (phy_type) {
|
|
|
|
case USBPHY_INTERFACE_MODE_UTMI:
|
|
|
|
err = utmi_phy_probe(tegra_phy, pdev);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
tegra_phy->pad_clk = devm_clk_get(&pdev->dev, "utmi-pads");
|
|
|
|
err = PTR_ERR_OR_ZERO(tegra_phy->pad_clk);
|
|
|
|
if (err) {
|
|
|
|
dev_err(&pdev->dev,
|
|
|
|
"Failed to get UTMIP pad clock: %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
reset = devm_reset_control_get_optional_shared(&pdev->dev,
|
|
|
|
"utmi-pads");
|
|
|
|
err = PTR_ERR_OR_ZERO(reset);
|
|
|
|
if (err) {
|
|
|
|
dev_err(&pdev->dev,
|
|
|
|
"Failed to get UTMI-pads reset: %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
tegra_phy->pad_rst = reset;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case USBPHY_INTERFACE_MODE_ULPI:
|
|
|
|
tegra_phy->is_ulpi_phy = true;
|
|
|
|
|
2020-01-06 01:34:01 +00:00
|
|
|
tegra_phy->clk = devm_clk_get(&pdev->dev, "ulpi-link");
|
|
|
|
err = PTR_ERR_OR_ZERO(tegra_phy->clk);
|
|
|
|
if (err) {
|
|
|
|
dev_err(&pdev->dev,
|
|
|
|
"Failed to get ULPI clock: %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
2013-05-16 14:13:02 +00:00
|
|
|
|
2020-01-06 01:34:09 +00:00
|
|
|
gpiod = devm_gpiod_get_from_of_node(&pdev->dev, np,
|
|
|
|
"nvidia,phy-reset-gpio",
|
|
|
|
0, GPIOD_OUT_HIGH,
|
|
|
|
"ulpi_phy_reset_b");
|
|
|
|
err = PTR_ERR_OR_ZERO(gpiod);
|
2020-01-06 01:34:02 +00:00
|
|
|
if (err) {
|
2020-01-06 01:34:09 +00:00
|
|
|
dev_err(&pdev->dev,
|
|
|
|
"Request failed for reset GPIO: %d\n", err);
|
2020-01-06 01:34:01 +00:00
|
|
|
return err;
|
|
|
|
}
|
2020-01-06 01:34:09 +00:00
|
|
|
tegra_phy->reset_gpio = gpiod;
|
2020-01-06 01:34:01 +00:00
|
|
|
|
2020-01-06 01:34:07 +00:00
|
|
|
phy = devm_otg_ulpi_create(&pdev->dev,
|
|
|
|
&ulpi_viewport_access_ops, 0);
|
|
|
|
if (!phy) {
|
2020-01-06 01:34:01 +00:00
|
|
|
dev_err(&pdev->dev, "Failed to create ULPI OTG\n");
|
2020-01-06 01:34:02 +00:00
|
|
|
return -ENOMEM;
|
2020-01-06 01:34:01 +00:00
|
|
|
}
|
|
|
|
|
2020-01-06 01:34:07 +00:00
|
|
|
tegra_phy->ulpi = phy;
|
2020-01-06 01:34:01 +00:00
|
|
|
tegra_phy->ulpi->io_priv = tegra_phy->regs + ULPI_VIEWPORT;
|
2020-01-06 01:34:02 +00:00
|
|
|
break;
|
2020-01-06 01:34:01 +00:00
|
|
|
|
2020-01-06 01:34:02 +00:00
|
|
|
default:
|
|
|
|
dev_err(&pdev->dev, "phy_type %u is invalid or unsupported\n",
|
|
|
|
phy_type);
|
|
|
|
return -EINVAL;
|
2020-01-06 01:34:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tegra_phy->u_phy.dev = &pdev->dev;
|
|
|
|
tegra_phy->u_phy.init = tegra_usb_phy_init;
|
|
|
|
tegra_phy->u_phy.shutdown = tegra_usb_phy_shutdown;
|
|
|
|
tegra_phy->u_phy.set_suspend = tegra_usb_phy_set_suspend;
|
2013-05-16 14:13:02 +00:00
|
|
|
|
2013-08-30 03:23:32 +00:00
|
|
|
platform_set_drvdata(pdev, tegra_phy);
|
2013-07-25 18:38:05 +00:00
|
|
|
|
|
|
|
err = usb_add_phy_dev(&tegra_phy->u_phy);
|
2020-01-06 01:34:02 +00:00
|
|
|
if (err)
|
2020-01-06 01:34:07 +00:00
|
|
|
return err;
|
2013-07-25 18:38:05 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tegra_usb_phy_remove(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct tegra_usb_phy *tegra_phy = platform_get_drvdata(pdev);
|
|
|
|
|
|
|
|
usb_remove_phy(&tegra_phy->u_phy);
|
2020-01-06 01:34:01 +00:00
|
|
|
|
2013-05-16 14:13:02 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct platform_driver tegra_usb_phy_driver = {
|
|
|
|
.probe = tegra_usb_phy_probe,
|
2013-07-25 18:38:05 +00:00
|
|
|
.remove = tegra_usb_phy_remove,
|
2013-05-16 14:13:02 +00:00
|
|
|
.driver = {
|
|
|
|
.name = "tegra-phy",
|
2013-09-30 04:14:47 +00:00
|
|
|
.of_match_table = tegra_usb_phy_id_table,
|
2013-05-16 14:13:02 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
module_platform_driver(tegra_usb_phy_driver);
|
|
|
|
|
2013-06-13 17:24:08 +00:00
|
|
|
MODULE_DESCRIPTION("Tegra USB PHY driver");
|
|
|
|
MODULE_LICENSE("GPL v2");
|