| .. | .. |
|---|
| 11 | 11 | #include <linux/dma-mapping.h> |
|---|
| 12 | 12 | #include <linux/firmware.h> |
|---|
| 13 | 13 | #include <linux/interrupt.h> |
|---|
| 14 | +#include <linux/iopoll.h> |
|---|
| 14 | 15 | #include <linux/kernel.h> |
|---|
| 15 | 16 | #include <linux/module.h> |
|---|
| 16 | 17 | #include <linux/of_device.h> |
|---|
| .. | .. |
|---|
| 18 | 19 | #include <linux/phy/tegra/xusb.h> |
|---|
| 19 | 20 | #include <linux/platform_device.h> |
|---|
| 20 | 21 | #include <linux/pm.h> |
|---|
| 22 | +#include <linux/pm_domain.h> |
|---|
| 21 | 23 | #include <linux/pm_runtime.h> |
|---|
| 22 | 24 | #include <linux/regulator/consumer.h> |
|---|
| 23 | 25 | #include <linux/reset.h> |
|---|
| 24 | 26 | #include <linux/slab.h> |
|---|
| 27 | +#include <linux/usb/otg.h> |
|---|
| 28 | +#include <linux/usb/phy.h> |
|---|
| 29 | +#include <linux/usb/role.h> |
|---|
| 25 | 30 | #include <soc/tegra/pmc.h> |
|---|
| 26 | 31 | |
|---|
| 27 | 32 | #include "xhci.h" |
|---|
| .. | .. |
|---|
| 37 | 42 | #define XUSB_CFG_4 0x010 |
|---|
| 38 | 43 | #define XUSB_BASE_ADDR_SHIFT 15 |
|---|
| 39 | 44 | #define XUSB_BASE_ADDR_MASK 0x1ffff |
|---|
| 45 | +#define XUSB_CFG_16 0x040 |
|---|
| 46 | +#define XUSB_CFG_24 0x060 |
|---|
| 47 | +#define XUSB_CFG_AXI_CFG 0x0f8 |
|---|
| 40 | 48 | #define XUSB_CFG_ARU_C11_CSBRANGE 0x41c |
|---|
| 49 | +#define XUSB_CFG_ARU_CONTEXT 0x43c |
|---|
| 50 | +#define XUSB_CFG_ARU_CONTEXT_HS_PLS 0x478 |
|---|
| 51 | +#define XUSB_CFG_ARU_CONTEXT_FS_PLS 0x47c |
|---|
| 52 | +#define XUSB_CFG_ARU_CONTEXT_HSFS_SPEED 0x480 |
|---|
| 53 | +#define XUSB_CFG_ARU_CONTEXT_HSFS_PP 0x484 |
|---|
| 41 | 54 | #define XUSB_CFG_CSB_BASE_ADDR 0x800 |
|---|
| 42 | 55 | |
|---|
| 43 | 56 | /* FPCI mailbox registers */ |
|---|
| 44 | | -#define XUSB_CFG_ARU_MBOX_CMD 0x0e4 |
|---|
| 57 | +/* XUSB_CFG_ARU_MBOX_CMD */ |
|---|
| 45 | 58 | #define MBOX_DEST_FALC BIT(27) |
|---|
| 46 | 59 | #define MBOX_DEST_PME BIT(28) |
|---|
| 47 | 60 | #define MBOX_DEST_SMI BIT(29) |
|---|
| 48 | 61 | #define MBOX_DEST_XHCI BIT(30) |
|---|
| 49 | 62 | #define MBOX_INT_EN BIT(31) |
|---|
| 50 | | -#define XUSB_CFG_ARU_MBOX_DATA_IN 0x0e8 |
|---|
| 63 | +/* XUSB_CFG_ARU_MBOX_DATA_IN and XUSB_CFG_ARU_MBOX_DATA_OUT */ |
|---|
| 51 | 64 | #define CMD_DATA_SHIFT 0 |
|---|
| 52 | 65 | #define CMD_DATA_MASK 0xffffff |
|---|
| 53 | 66 | #define CMD_TYPE_SHIFT 24 |
|---|
| 54 | 67 | #define CMD_TYPE_MASK 0xff |
|---|
| 55 | | -#define XUSB_CFG_ARU_MBOX_DATA_OUT 0x0ec |
|---|
| 56 | | -#define XUSB_CFG_ARU_MBOX_OWNER 0x0f0 |
|---|
| 68 | +/* XUSB_CFG_ARU_MBOX_OWNER */ |
|---|
| 57 | 69 | #define MBOX_OWNER_NONE 0 |
|---|
| 58 | 70 | #define MBOX_OWNER_FW 1 |
|---|
| 59 | 71 | #define MBOX_OWNER_SW 2 |
|---|
| .. | .. |
|---|
| 62 | 74 | #define MBOX_SMI_INTR_EN BIT(3) |
|---|
| 63 | 75 | |
|---|
| 64 | 76 | /* IPFS registers */ |
|---|
| 77 | +#define IPFS_XUSB_HOST_MSI_BAR_SZ_0 0x0c0 |
|---|
| 78 | +#define IPFS_XUSB_HOST_MSI_AXI_BAR_ST_0 0x0c4 |
|---|
| 79 | +#define IPFS_XUSB_HOST_MSI_FPCI_BAR_ST_0 0x0c8 |
|---|
| 80 | +#define IPFS_XUSB_HOST_MSI_VEC0_0 0x100 |
|---|
| 81 | +#define IPFS_XUSB_HOST_MSI_EN_VEC0_0 0x140 |
|---|
| 65 | 82 | #define IPFS_XUSB_HOST_CONFIGURATION_0 0x180 |
|---|
| 66 | 83 | #define IPFS_EN_FPCI BIT(0) |
|---|
| 84 | +#define IPFS_XUSB_HOST_FPCI_ERROR_MASKS_0 0x184 |
|---|
| 67 | 85 | #define IPFS_XUSB_HOST_INTR_MASK_0 0x188 |
|---|
| 68 | 86 | #define IPFS_IP_INT_MASK BIT(16) |
|---|
| 87 | +#define IPFS_XUSB_HOST_INTR_ENABLE_0 0x198 |
|---|
| 88 | +#define IPFS_XUSB_HOST_UFPCI_CONFIG_0 0x19c |
|---|
| 69 | 89 | #define IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0 0x1bc |
|---|
| 90 | +#define IPFS_XUSB_HOST_MCCIF_FIFOCTRL_0 0x1dc |
|---|
| 70 | 91 | |
|---|
| 71 | 92 | #define CSB_PAGE_SELECT_MASK 0x7fffff |
|---|
| 72 | 93 | #define CSB_PAGE_SELECT_SHIFT 9 |
|---|
| .. | .. |
|---|
| 101 | 122 | #define L2IMEMOP_ACTION_SHIFT 24 |
|---|
| 102 | 123 | #define L2IMEMOP_INVALIDATE_ALL (0x40 << L2IMEMOP_ACTION_SHIFT) |
|---|
| 103 | 124 | #define L2IMEMOP_LOAD_LOCKED_RESULT (0x11 << L2IMEMOP_ACTION_SHIFT) |
|---|
| 125 | +#define XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT 0x101a18 |
|---|
| 126 | +#define L2IMEMOP_RESULT_VLD BIT(31) |
|---|
| 104 | 127 | #define XUSB_CSB_MP_APMAP 0x10181c |
|---|
| 105 | 128 | #define APMAP_BOOTPATH BIT(31) |
|---|
| 106 | 129 | |
|---|
| 107 | 130 | #define IMEM_BLOCK_SIZE 256 |
|---|
| 108 | 131 | |
|---|
| 109 | 132 | struct tegra_xusb_fw_header { |
|---|
| 110 | | - u32 boot_loadaddr_in_imem; |
|---|
| 111 | | - u32 boot_codedfi_offset; |
|---|
| 112 | | - u32 boot_codetag; |
|---|
| 113 | | - u32 boot_codesize; |
|---|
| 114 | | - u32 phys_memaddr; |
|---|
| 115 | | - u16 reqphys_memsize; |
|---|
| 116 | | - u16 alloc_phys_memsize; |
|---|
| 117 | | - u32 rodata_img_offset; |
|---|
| 118 | | - u32 rodata_section_start; |
|---|
| 119 | | - u32 rodata_section_end; |
|---|
| 120 | | - u32 main_fnaddr; |
|---|
| 121 | | - u32 fwimg_cksum; |
|---|
| 122 | | - u32 fwimg_created_time; |
|---|
| 123 | | - u32 imem_resident_start; |
|---|
| 124 | | - u32 imem_resident_end; |
|---|
| 125 | | - u32 idirect_start; |
|---|
| 126 | | - u32 idirect_end; |
|---|
| 127 | | - u32 l2_imem_start; |
|---|
| 128 | | - u32 l2_imem_end; |
|---|
| 129 | | - u32 version_id; |
|---|
| 133 | + __le32 boot_loadaddr_in_imem; |
|---|
| 134 | + __le32 boot_codedfi_offset; |
|---|
| 135 | + __le32 boot_codetag; |
|---|
| 136 | + __le32 boot_codesize; |
|---|
| 137 | + __le32 phys_memaddr; |
|---|
| 138 | + __le16 reqphys_memsize; |
|---|
| 139 | + __le16 alloc_phys_memsize; |
|---|
| 140 | + __le32 rodata_img_offset; |
|---|
| 141 | + __le32 rodata_section_start; |
|---|
| 142 | + __le32 rodata_section_end; |
|---|
| 143 | + __le32 main_fnaddr; |
|---|
| 144 | + __le32 fwimg_cksum; |
|---|
| 145 | + __le32 fwimg_created_time; |
|---|
| 146 | + __le32 imem_resident_start; |
|---|
| 147 | + __le32 imem_resident_end; |
|---|
| 148 | + __le32 idirect_start; |
|---|
| 149 | + __le32 idirect_end; |
|---|
| 150 | + __le32 l2_imem_start; |
|---|
| 151 | + __le32 l2_imem_end; |
|---|
| 152 | + __le32 version_id; |
|---|
| 130 | 153 | u8 init_ddirect; |
|---|
| 131 | 154 | u8 reserved[3]; |
|---|
| 132 | | - u32 phys_addr_log_buffer; |
|---|
| 133 | | - u32 total_log_entries; |
|---|
| 134 | | - u32 dequeue_ptr; |
|---|
| 135 | | - u32 dummy_var[2]; |
|---|
| 136 | | - u32 fwimg_len; |
|---|
| 155 | + __le32 phys_addr_log_buffer; |
|---|
| 156 | + __le32 total_log_entries; |
|---|
| 157 | + __le32 dequeue_ptr; |
|---|
| 158 | + __le32 dummy_var[2]; |
|---|
| 159 | + __le32 fwimg_len; |
|---|
| 137 | 160 | u8 magic[8]; |
|---|
| 138 | | - u32 ss_low_power_entry_timeout; |
|---|
| 161 | + __le32 ss_low_power_entry_timeout; |
|---|
| 139 | 162 | u8 num_hsic_port; |
|---|
| 140 | 163 | u8 padding[139]; /* Pad to 256 bytes */ |
|---|
| 141 | 164 | }; |
|---|
| .. | .. |
|---|
| 145 | 168 | unsigned int num; |
|---|
| 146 | 169 | }; |
|---|
| 147 | 170 | |
|---|
| 171 | +struct tegra_xusb_mbox_regs { |
|---|
| 172 | + u16 cmd; |
|---|
| 173 | + u16 data_in; |
|---|
| 174 | + u16 data_out; |
|---|
| 175 | + u16 owner; |
|---|
| 176 | +}; |
|---|
| 177 | + |
|---|
| 178 | +struct tegra_xusb_context_soc { |
|---|
| 179 | + struct { |
|---|
| 180 | + const unsigned int *offsets; |
|---|
| 181 | + unsigned int num_offsets; |
|---|
| 182 | + } ipfs; |
|---|
| 183 | + |
|---|
| 184 | + struct { |
|---|
| 185 | + const unsigned int *offsets; |
|---|
| 186 | + unsigned int num_offsets; |
|---|
| 187 | + } fpci; |
|---|
| 188 | +}; |
|---|
| 189 | + |
|---|
| 148 | 190 | struct tegra_xusb_soc { |
|---|
| 149 | 191 | const char *firmware; |
|---|
| 150 | 192 | const char * const *supply_names; |
|---|
| 151 | 193 | unsigned int num_supplies; |
|---|
| 152 | 194 | const struct tegra_xusb_phy_type *phy_types; |
|---|
| 153 | 195 | unsigned int num_types; |
|---|
| 196 | + const struct tegra_xusb_context_soc *context; |
|---|
| 154 | 197 | |
|---|
| 155 | 198 | struct { |
|---|
| 156 | 199 | struct { |
|---|
| .. | .. |
|---|
| 159 | 202 | } usb2, ulpi, hsic, usb3; |
|---|
| 160 | 203 | } ports; |
|---|
| 161 | 204 | |
|---|
| 205 | + struct tegra_xusb_mbox_regs mbox; |
|---|
| 206 | + |
|---|
| 162 | 207 | bool scale_ss_clock; |
|---|
| 208 | + bool has_ipfs; |
|---|
| 209 | + bool lpm_support; |
|---|
| 210 | + bool otg_reset_sspi; |
|---|
| 211 | +}; |
|---|
| 212 | + |
|---|
| 213 | +struct tegra_xusb_context { |
|---|
| 214 | + u32 *ipfs; |
|---|
| 215 | + u32 *fpci; |
|---|
| 163 | 216 | }; |
|---|
| 164 | 217 | |
|---|
| 165 | 218 | struct tegra_xusb { |
|---|
| .. | .. |
|---|
| 194 | 247 | struct reset_control *host_rst; |
|---|
| 195 | 248 | struct reset_control *ss_rst; |
|---|
| 196 | 249 | |
|---|
| 250 | + struct device *genpd_dev_host; |
|---|
| 251 | + struct device *genpd_dev_ss; |
|---|
| 252 | + struct device_link *genpd_dl_host; |
|---|
| 253 | + struct device_link *genpd_dl_ss; |
|---|
| 254 | + |
|---|
| 197 | 255 | struct phy **phys; |
|---|
| 198 | 256 | unsigned int num_phys; |
|---|
| 257 | + |
|---|
| 258 | + struct usb_phy **usbphy; |
|---|
| 259 | + unsigned int num_usb_phys; |
|---|
| 260 | + int otg_usb2_port; |
|---|
| 261 | + int otg_usb3_port; |
|---|
| 262 | + bool host_mode; |
|---|
| 263 | + struct notifier_block id_nb; |
|---|
| 264 | + struct work_struct id_work; |
|---|
| 199 | 265 | |
|---|
| 200 | 266 | /* Firmware loading related */ |
|---|
| 201 | 267 | struct { |
|---|
| .. | .. |
|---|
| 203 | 269 | void *virt; |
|---|
| 204 | 270 | dma_addr_t phys; |
|---|
| 205 | 271 | } fw; |
|---|
| 272 | + |
|---|
| 273 | + struct tegra_xusb_context context; |
|---|
| 206 | 274 | }; |
|---|
| 207 | 275 | |
|---|
| 208 | 276 | static struct hc_driver __read_mostly tegra_xhci_hc_driver; |
|---|
| .. | .. |
|---|
| 347 | 415 | MBOX_CMD_NAK |
|---|
| 348 | 416 | }; |
|---|
| 349 | 417 | |
|---|
| 350 | | -static const char * const mbox_cmd_name[] = { |
|---|
| 351 | | - [ 1] = "MSG_ENABLE", |
|---|
| 352 | | - [ 2] = "INC_FALCON_CLOCK", |
|---|
| 353 | | - [ 3] = "DEC_FALCON_CLOCK", |
|---|
| 354 | | - [ 4] = "INC_SSPI_CLOCK", |
|---|
| 355 | | - [ 5] = "DEC_SSPI_CLOCK", |
|---|
| 356 | | - [ 6] = "SET_BW", |
|---|
| 357 | | - [ 7] = "SET_SS_PWR_GATING", |
|---|
| 358 | | - [ 8] = "SET_SS_PWR_UNGATING", |
|---|
| 359 | | - [ 9] = "SAVE_DFE_CTLE_CTX", |
|---|
| 360 | | - [ 10] = "AIRPLANE_MODE_ENABLED", |
|---|
| 361 | | - [ 11] = "AIRPLANE_MODE_DISABLED", |
|---|
| 362 | | - [ 12] = "START_HSIC_IDLE", |
|---|
| 363 | | - [ 13] = "STOP_HSIC_IDLE", |
|---|
| 364 | | - [ 14] = "DBC_WAKE_STACK", |
|---|
| 365 | | - [ 15] = "HSIC_PRETEND_CONNECT", |
|---|
| 366 | | - [ 16] = "RESET_SSPI", |
|---|
| 367 | | - [ 17] = "DISABLE_SS_LFPS_DETECTION", |
|---|
| 368 | | - [ 18] = "ENABLE_SS_LFPS_DETECTION", |
|---|
| 369 | | - [128] = "ACK", |
|---|
| 370 | | - [129] = "NAK", |
|---|
| 371 | | -}; |
|---|
| 372 | | - |
|---|
| 373 | 418 | struct tegra_xusb_mbox_msg { |
|---|
| 374 | 419 | u32 cmd; |
|---|
| 375 | 420 | u32 data; |
|---|
| .. | .. |
|---|
| 411 | 456 | * ACK/NAK messages. |
|---|
| 412 | 457 | */ |
|---|
| 413 | 458 | if (!(msg->cmd == MBOX_CMD_ACK || msg->cmd == MBOX_CMD_NAK)) { |
|---|
| 414 | | - value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER); |
|---|
| 459 | + value = fpci_readl(tegra, tegra->soc->mbox.owner); |
|---|
| 415 | 460 | if (value != MBOX_OWNER_NONE) { |
|---|
| 416 | 461 | dev_err(tegra->dev, "mailbox is busy\n"); |
|---|
| 417 | 462 | return -EBUSY; |
|---|
| 418 | 463 | } |
|---|
| 419 | 464 | |
|---|
| 420 | | - fpci_writel(tegra, MBOX_OWNER_SW, XUSB_CFG_ARU_MBOX_OWNER); |
|---|
| 465 | + fpci_writel(tegra, MBOX_OWNER_SW, tegra->soc->mbox.owner); |
|---|
| 421 | 466 | |
|---|
| 422 | | - value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER); |
|---|
| 467 | + value = fpci_readl(tegra, tegra->soc->mbox.owner); |
|---|
| 423 | 468 | if (value != MBOX_OWNER_SW) { |
|---|
| 424 | 469 | dev_err(tegra->dev, "failed to acquire mailbox\n"); |
|---|
| 425 | 470 | return -EBUSY; |
|---|
| .. | .. |
|---|
| 429 | 474 | } |
|---|
| 430 | 475 | |
|---|
| 431 | 476 | value = tegra_xusb_mbox_pack(msg); |
|---|
| 432 | | - fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_DATA_IN); |
|---|
| 477 | + fpci_writel(tegra, value, tegra->soc->mbox.data_in); |
|---|
| 433 | 478 | |
|---|
| 434 | | - value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_CMD); |
|---|
| 479 | + value = fpci_readl(tegra, tegra->soc->mbox.cmd); |
|---|
| 435 | 480 | value |= MBOX_INT_EN | MBOX_DEST_FALC; |
|---|
| 436 | | - fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_CMD); |
|---|
| 481 | + fpci_writel(tegra, value, tegra->soc->mbox.cmd); |
|---|
| 437 | 482 | |
|---|
| 438 | 483 | if (wait_for_idle) { |
|---|
| 439 | 484 | unsigned long timeout = jiffies + msecs_to_jiffies(250); |
|---|
| 440 | 485 | |
|---|
| 441 | 486 | while (time_before(jiffies, timeout)) { |
|---|
| 442 | | - value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER); |
|---|
| 487 | + value = fpci_readl(tegra, tegra->soc->mbox.owner); |
|---|
| 443 | 488 | if (value == MBOX_OWNER_NONE) |
|---|
| 444 | 489 | break; |
|---|
| 445 | 490 | |
|---|
| .. | .. |
|---|
| 447 | 492 | } |
|---|
| 448 | 493 | |
|---|
| 449 | 494 | if (time_after(jiffies, timeout)) |
|---|
| 450 | | - value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER); |
|---|
| 495 | + value = fpci_readl(tegra, tegra->soc->mbox.owner); |
|---|
| 451 | 496 | |
|---|
| 452 | 497 | if (value != MBOX_OWNER_NONE) |
|---|
| 453 | 498 | return -ETIMEDOUT; |
|---|
| .. | .. |
|---|
| 621 | 666 | |
|---|
| 622 | 667 | mutex_lock(&tegra->lock); |
|---|
| 623 | 668 | |
|---|
| 624 | | - value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_DATA_OUT); |
|---|
| 669 | + value = fpci_readl(tegra, tegra->soc->mbox.data_out); |
|---|
| 625 | 670 | tegra_xusb_mbox_unpack(&msg, value); |
|---|
| 626 | 671 | |
|---|
| 627 | | - value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_CMD); |
|---|
| 672 | + value = fpci_readl(tegra, tegra->soc->mbox.cmd); |
|---|
| 628 | 673 | value &= ~MBOX_DEST_SMI; |
|---|
| 629 | | - fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_CMD); |
|---|
| 674 | + fpci_writel(tegra, value, tegra->soc->mbox.cmd); |
|---|
| 630 | 675 | |
|---|
| 631 | 676 | /* clear mailbox owner if no ACK/NAK is required */ |
|---|
| 632 | 677 | if (!tegra_xusb_mbox_cmd_requires_ack(msg.cmd)) |
|---|
| 633 | | - fpci_writel(tegra, MBOX_OWNER_NONE, XUSB_CFG_ARU_MBOX_OWNER); |
|---|
| 678 | + fpci_writel(tegra, MBOX_OWNER_NONE, tegra->soc->mbox.owner); |
|---|
| 634 | 679 | |
|---|
| 635 | 680 | tegra_xusb_mbox_handle(tegra, &msg); |
|---|
| 636 | 681 | |
|---|
| .. | .. |
|---|
| 638 | 683 | return IRQ_HANDLED; |
|---|
| 639 | 684 | } |
|---|
| 640 | 685 | |
|---|
| 641 | | -static void tegra_xusb_ipfs_config(struct tegra_xusb *tegra, |
|---|
| 642 | | - struct resource *regs) |
|---|
| 686 | +static void tegra_xusb_config(struct tegra_xusb *tegra) |
|---|
| 643 | 687 | { |
|---|
| 688 | + u32 regs = tegra->hcd->rsrc_start; |
|---|
| 644 | 689 | u32 value; |
|---|
| 645 | 690 | |
|---|
| 646 | | - value = ipfs_readl(tegra, IPFS_XUSB_HOST_CONFIGURATION_0); |
|---|
| 647 | | - value |= IPFS_EN_FPCI; |
|---|
| 648 | | - ipfs_writel(tegra, value, IPFS_XUSB_HOST_CONFIGURATION_0); |
|---|
| 691 | + if (tegra->soc->has_ipfs) { |
|---|
| 692 | + value = ipfs_readl(tegra, IPFS_XUSB_HOST_CONFIGURATION_0); |
|---|
| 693 | + value |= IPFS_EN_FPCI; |
|---|
| 694 | + ipfs_writel(tegra, value, IPFS_XUSB_HOST_CONFIGURATION_0); |
|---|
| 649 | 695 | |
|---|
| 650 | | - usleep_range(10, 20); |
|---|
| 696 | + usleep_range(10, 20); |
|---|
| 697 | + } |
|---|
| 651 | 698 | |
|---|
| 652 | 699 | /* Program BAR0 space */ |
|---|
| 653 | 700 | value = fpci_readl(tegra, XUSB_CFG_4); |
|---|
| 654 | 701 | value &= ~(XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT); |
|---|
| 655 | | - value |= regs->start & (XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT); |
|---|
| 702 | + value |= regs & (XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT); |
|---|
| 656 | 703 | fpci_writel(tegra, value, XUSB_CFG_4); |
|---|
| 657 | 704 | |
|---|
| 658 | 705 | usleep_range(100, 200); |
|---|
| .. | .. |
|---|
| 662 | 709 | value |= XUSB_IO_SPACE_EN | XUSB_MEM_SPACE_EN | XUSB_BUS_MASTER_EN; |
|---|
| 663 | 710 | fpci_writel(tegra, value, XUSB_CFG_1); |
|---|
| 664 | 711 | |
|---|
| 665 | | - /* Enable interrupt assertion */ |
|---|
| 666 | | - value = ipfs_readl(tegra, IPFS_XUSB_HOST_INTR_MASK_0); |
|---|
| 667 | | - value |= IPFS_IP_INT_MASK; |
|---|
| 668 | | - ipfs_writel(tegra, value, IPFS_XUSB_HOST_INTR_MASK_0); |
|---|
| 712 | + if (tegra->soc->has_ipfs) { |
|---|
| 713 | + /* Enable interrupt assertion */ |
|---|
| 714 | + value = ipfs_readl(tegra, IPFS_XUSB_HOST_INTR_MASK_0); |
|---|
| 715 | + value |= IPFS_IP_INT_MASK; |
|---|
| 716 | + ipfs_writel(tegra, value, IPFS_XUSB_HOST_INTR_MASK_0); |
|---|
| 669 | 717 | |
|---|
| 670 | | - /* Set hysteresis */ |
|---|
| 671 | | - ipfs_writel(tegra, 0x80, IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0); |
|---|
| 718 | + /* Set hysteresis */ |
|---|
| 719 | + ipfs_writel(tegra, 0x80, IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0); |
|---|
| 720 | + } |
|---|
| 672 | 721 | } |
|---|
| 673 | 722 | |
|---|
| 674 | 723 | static int tegra_xusb_clk_enable(struct tegra_xusb *tegra) |
|---|
| .. | .. |
|---|
| 774 | 823 | { |
|---|
| 775 | 824 | struct tegra_xusb *tegra = dev_get_drvdata(dev); |
|---|
| 776 | 825 | |
|---|
| 777 | | - tegra_xusb_phy_disable(tegra); |
|---|
| 778 | 826 | regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); |
|---|
| 779 | 827 | tegra_xusb_clk_disable(tegra); |
|---|
| 780 | 828 | |
|---|
| .. | .. |
|---|
| 798 | 846 | goto disable_clk; |
|---|
| 799 | 847 | } |
|---|
| 800 | 848 | |
|---|
| 801 | | - err = tegra_xusb_phy_enable(tegra); |
|---|
| 802 | | - if (err < 0) { |
|---|
| 803 | | - dev_err(dev, "failed to enable PHYs: %d\n", err); |
|---|
| 804 | | - goto disable_regulator; |
|---|
| 805 | | - } |
|---|
| 806 | | - |
|---|
| 807 | 849 | return 0; |
|---|
| 808 | 850 | |
|---|
| 809 | | -disable_regulator: |
|---|
| 810 | | - regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); |
|---|
| 811 | 851 | disable_clk: |
|---|
| 812 | 852 | tegra_xusb_clk_disable(tegra); |
|---|
| 813 | 853 | return err; |
|---|
| 814 | 854 | } |
|---|
| 815 | 855 | |
|---|
| 816 | | -static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) |
|---|
| 856 | +#ifdef CONFIG_PM_SLEEP |
|---|
| 857 | +static int tegra_xusb_init_context(struct tegra_xusb *tegra) |
|---|
| 817 | 858 | { |
|---|
| 818 | | - unsigned int code_tag_blocks, code_size_blocks, code_blocks; |
|---|
| 859 | + const struct tegra_xusb_context_soc *soc = tegra->soc->context; |
|---|
| 860 | + |
|---|
| 861 | + tegra->context.ipfs = devm_kcalloc(tegra->dev, soc->ipfs.num_offsets, |
|---|
| 862 | + sizeof(u32), GFP_KERNEL); |
|---|
| 863 | + if (!tegra->context.ipfs) |
|---|
| 864 | + return -ENOMEM; |
|---|
| 865 | + |
|---|
| 866 | + tegra->context.fpci = devm_kcalloc(tegra->dev, soc->fpci.num_offsets, |
|---|
| 867 | + sizeof(u32), GFP_KERNEL); |
|---|
| 868 | + if (!tegra->context.fpci) |
|---|
| 869 | + return -ENOMEM; |
|---|
| 870 | + |
|---|
| 871 | + return 0; |
|---|
| 872 | +} |
|---|
| 873 | +#else |
|---|
| 874 | +static inline int tegra_xusb_init_context(struct tegra_xusb *tegra) |
|---|
| 875 | +{ |
|---|
| 876 | + return 0; |
|---|
| 877 | +} |
|---|
| 878 | +#endif |
|---|
| 879 | + |
|---|
| 880 | +static int tegra_xusb_request_firmware(struct tegra_xusb *tegra) |
|---|
| 881 | +{ |
|---|
| 819 | 882 | struct tegra_xusb_fw_header *header; |
|---|
| 820 | | - struct device *dev = tegra->dev; |
|---|
| 821 | 883 | const struct firmware *fw; |
|---|
| 822 | | - unsigned long timeout; |
|---|
| 823 | | - time64_t timestamp; |
|---|
| 824 | | - struct tm time; |
|---|
| 825 | | - u64 address; |
|---|
| 826 | | - u32 value; |
|---|
| 827 | 884 | int err; |
|---|
| 828 | 885 | |
|---|
| 829 | 886 | err = request_firmware(&fw, tegra->soc->firmware, tegra->dev); |
|---|
| .. | .. |
|---|
| 847 | 904 | header = (struct tegra_xusb_fw_header *)tegra->fw.virt; |
|---|
| 848 | 905 | memcpy(tegra->fw.virt, fw->data, tegra->fw.size); |
|---|
| 849 | 906 | release_firmware(fw); |
|---|
| 907 | + |
|---|
| 908 | + return 0; |
|---|
| 909 | +} |
|---|
| 910 | + |
|---|
| 911 | +static int tegra_xusb_load_firmware(struct tegra_xusb *tegra) |
|---|
| 912 | +{ |
|---|
| 913 | + unsigned int code_tag_blocks, code_size_blocks, code_blocks; |
|---|
| 914 | + struct xhci_cap_regs __iomem *cap = tegra->regs; |
|---|
| 915 | + struct tegra_xusb_fw_header *header; |
|---|
| 916 | + struct device *dev = tegra->dev; |
|---|
| 917 | + struct xhci_op_regs __iomem *op; |
|---|
| 918 | + unsigned long timeout; |
|---|
| 919 | + time64_t timestamp; |
|---|
| 920 | + struct tm time; |
|---|
| 921 | + u64 address; |
|---|
| 922 | + u32 value; |
|---|
| 923 | + int err; |
|---|
| 924 | + |
|---|
| 925 | + header = (struct tegra_xusb_fw_header *)tegra->fw.virt; |
|---|
| 926 | + op = tegra->regs + HC_LENGTH(readl(&cap->hc_capbase)); |
|---|
| 850 | 927 | |
|---|
| 851 | 928 | if (csb_readl(tegra, XUSB_CSB_MP_ILOAD_BASE_LO) != 0) { |
|---|
| 852 | 929 | dev_info(dev, "Firmware already loaded, Falcon state %#x\n", |
|---|
| .. | .. |
|---|
| 902 | 979 | |
|---|
| 903 | 980 | csb_writel(tegra, 0, XUSB_FALC_DMACTL); |
|---|
| 904 | 981 | |
|---|
| 905 | | - msleep(50); |
|---|
| 982 | + /* wait for RESULT_VLD to get set */ |
|---|
| 983 | +#define tegra_csb_readl(offset) csb_readl(tegra, offset) |
|---|
| 984 | + err = readx_poll_timeout(tegra_csb_readl, |
|---|
| 985 | + XUSB_CSB_MEMPOOL_L2IMEMOP_RESULT, value, |
|---|
| 986 | + value & L2IMEMOP_RESULT_VLD, 100, 10000); |
|---|
| 987 | + if (err < 0) { |
|---|
| 988 | + dev_err(dev, "DMA controller not ready %#010x\n", value); |
|---|
| 989 | + return err; |
|---|
| 990 | + } |
|---|
| 991 | +#undef tegra_csb_readl |
|---|
| 906 | 992 | |
|---|
| 907 | 993 | csb_writel(tegra, le32_to_cpu(header->boot_codetag), |
|---|
| 908 | 994 | XUSB_FALC_BOOTVEC); |
|---|
| 909 | 995 | |
|---|
| 910 | | - /* Boot Falcon CPU and wait for it to enter the STOPPED (idle) state. */ |
|---|
| 911 | | - timeout = jiffies + msecs_to_jiffies(5); |
|---|
| 912 | | - |
|---|
| 996 | + /* Boot Falcon CPU and wait for USBSTS_CNR to get cleared. */ |
|---|
| 913 | 997 | csb_writel(tegra, CPUCTL_STARTCPU, XUSB_FALC_CPUCTL); |
|---|
| 914 | 998 | |
|---|
| 915 | | - while (time_before(jiffies, timeout)) { |
|---|
| 916 | | - if (csb_readl(tegra, XUSB_FALC_CPUCTL) == CPUCTL_STATE_STOPPED) |
|---|
| 999 | + timeout = jiffies + msecs_to_jiffies(200); |
|---|
| 1000 | + |
|---|
| 1001 | + do { |
|---|
| 1002 | + value = readl(&op->status); |
|---|
| 1003 | + if ((value & STS_CNR) == 0) |
|---|
| 917 | 1004 | break; |
|---|
| 918 | 1005 | |
|---|
| 919 | | - usleep_range(100, 200); |
|---|
| 920 | | - } |
|---|
| 1006 | + usleep_range(1000, 2000); |
|---|
| 1007 | + } while (time_is_after_jiffies(timeout)); |
|---|
| 921 | 1008 | |
|---|
| 922 | | - if (csb_readl(tegra, XUSB_FALC_CPUCTL) != CPUCTL_STATE_STOPPED) { |
|---|
| 923 | | - dev_err(dev, "Falcon failed to start, state: %#x\n", |
|---|
| 924 | | - csb_readl(tegra, XUSB_FALC_CPUCTL)); |
|---|
| 1009 | + value = readl(&op->status); |
|---|
| 1010 | + if (value & STS_CNR) { |
|---|
| 1011 | + value = csb_readl(tegra, XUSB_FALC_CPUCTL); |
|---|
| 1012 | + dev_err(dev, "XHCI controller not read: %#010x\n", value); |
|---|
| 925 | 1013 | return -EIO; |
|---|
| 926 | 1014 | } |
|---|
| 927 | 1015 | |
|---|
| .. | .. |
|---|
| 935 | 1023 | return 0; |
|---|
| 936 | 1024 | } |
|---|
| 937 | 1025 | |
|---|
| 938 | | -static int tegra_xusb_probe(struct platform_device *pdev) |
|---|
| 1026 | +static void tegra_xusb_powerdomain_remove(struct device *dev, |
|---|
| 1027 | + struct tegra_xusb *tegra) |
|---|
| 1028 | +{ |
|---|
| 1029 | + if (tegra->genpd_dl_ss) |
|---|
| 1030 | + device_link_del(tegra->genpd_dl_ss); |
|---|
| 1031 | + if (tegra->genpd_dl_host) |
|---|
| 1032 | + device_link_del(tegra->genpd_dl_host); |
|---|
| 1033 | + if (!IS_ERR_OR_NULL(tegra->genpd_dev_ss)) |
|---|
| 1034 | + dev_pm_domain_detach(tegra->genpd_dev_ss, true); |
|---|
| 1035 | + if (!IS_ERR_OR_NULL(tegra->genpd_dev_host)) |
|---|
| 1036 | + dev_pm_domain_detach(tegra->genpd_dev_host, true); |
|---|
| 1037 | +} |
|---|
| 1038 | + |
|---|
| 1039 | +static int tegra_xusb_powerdomain_init(struct device *dev, |
|---|
| 1040 | + struct tegra_xusb *tegra) |
|---|
| 1041 | +{ |
|---|
| 1042 | + int err; |
|---|
| 1043 | + |
|---|
| 1044 | + tegra->genpd_dev_host = dev_pm_domain_attach_by_name(dev, "xusb_host"); |
|---|
| 1045 | + if (IS_ERR(tegra->genpd_dev_host)) { |
|---|
| 1046 | + err = PTR_ERR(tegra->genpd_dev_host); |
|---|
| 1047 | + dev_err(dev, "failed to get host pm-domain: %d\n", err); |
|---|
| 1048 | + return err; |
|---|
| 1049 | + } |
|---|
| 1050 | + |
|---|
| 1051 | + tegra->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "xusb_ss"); |
|---|
| 1052 | + if (IS_ERR(tegra->genpd_dev_ss)) { |
|---|
| 1053 | + err = PTR_ERR(tegra->genpd_dev_ss); |
|---|
| 1054 | + dev_err(dev, "failed to get superspeed pm-domain: %d\n", err); |
|---|
| 1055 | + return err; |
|---|
| 1056 | + } |
|---|
| 1057 | + |
|---|
| 1058 | + tegra->genpd_dl_host = device_link_add(dev, tegra->genpd_dev_host, |
|---|
| 1059 | + DL_FLAG_PM_RUNTIME | |
|---|
| 1060 | + DL_FLAG_STATELESS); |
|---|
| 1061 | + if (!tegra->genpd_dl_host) { |
|---|
| 1062 | + dev_err(dev, "adding host device link failed!\n"); |
|---|
| 1063 | + return -ENODEV; |
|---|
| 1064 | + } |
|---|
| 1065 | + |
|---|
| 1066 | + tegra->genpd_dl_ss = device_link_add(dev, tegra->genpd_dev_ss, |
|---|
| 1067 | + DL_FLAG_PM_RUNTIME | |
|---|
| 1068 | + DL_FLAG_STATELESS); |
|---|
| 1069 | + if (!tegra->genpd_dl_ss) { |
|---|
| 1070 | + dev_err(dev, "adding superspeed device link failed!\n"); |
|---|
| 1071 | + return -ENODEV; |
|---|
| 1072 | + } |
|---|
| 1073 | + |
|---|
| 1074 | + return 0; |
|---|
| 1075 | +} |
|---|
| 1076 | + |
|---|
| 1077 | +static int __tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra) |
|---|
| 939 | 1078 | { |
|---|
| 940 | 1079 | struct tegra_xusb_mbox_msg msg; |
|---|
| 941 | | - struct resource *res, *regs; |
|---|
| 1080 | + int err; |
|---|
| 1081 | + |
|---|
| 1082 | + /* Enable firmware messages from controller. */ |
|---|
| 1083 | + msg.cmd = MBOX_CMD_MSG_ENABLED; |
|---|
| 1084 | + msg.data = 0; |
|---|
| 1085 | + |
|---|
| 1086 | + err = tegra_xusb_mbox_send(tegra, &msg); |
|---|
| 1087 | + if (err < 0) |
|---|
| 1088 | + dev_err(tegra->dev, "failed to enable messages: %d\n", err); |
|---|
| 1089 | + |
|---|
| 1090 | + return err; |
|---|
| 1091 | +} |
|---|
| 1092 | + |
|---|
| 1093 | +static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra) |
|---|
| 1094 | +{ |
|---|
| 1095 | + int err; |
|---|
| 1096 | + |
|---|
| 1097 | + mutex_lock(&tegra->lock); |
|---|
| 1098 | + err = __tegra_xusb_enable_firmware_messages(tegra); |
|---|
| 1099 | + mutex_unlock(&tegra->lock); |
|---|
| 1100 | + |
|---|
| 1101 | + return err; |
|---|
| 1102 | +} |
|---|
| 1103 | + |
|---|
| 1104 | +static void tegra_xhci_set_port_power(struct tegra_xusb *tegra, bool main, |
|---|
| 1105 | + bool set) |
|---|
| 1106 | +{ |
|---|
| 1107 | + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); |
|---|
| 1108 | + struct usb_hcd *hcd = main ? xhci->main_hcd : xhci->shared_hcd; |
|---|
| 1109 | + unsigned int wait = (!main && !set) ? 1000 : 10; |
|---|
| 1110 | + u16 typeReq = set ? SetPortFeature : ClearPortFeature; |
|---|
| 1111 | + u16 wIndex = main ? tegra->otg_usb2_port + 1 : tegra->otg_usb3_port + 1; |
|---|
| 1112 | + u32 status; |
|---|
| 1113 | + u32 stat_power = main ? USB_PORT_STAT_POWER : USB_SS_PORT_STAT_POWER; |
|---|
| 1114 | + u32 status_val = set ? stat_power : 0; |
|---|
| 1115 | + |
|---|
| 1116 | + dev_dbg(tegra->dev, "%s():%s %s port power\n", __func__, |
|---|
| 1117 | + set ? "set" : "clear", main ? "HS" : "SS"); |
|---|
| 1118 | + |
|---|
| 1119 | + hcd->driver->hub_control(hcd, typeReq, USB_PORT_FEAT_POWER, wIndex, |
|---|
| 1120 | + NULL, 0); |
|---|
| 1121 | + |
|---|
| 1122 | + do { |
|---|
| 1123 | + tegra_xhci_hc_driver.hub_control(hcd, GetPortStatus, 0, wIndex, |
|---|
| 1124 | + (char *) &status, sizeof(status)); |
|---|
| 1125 | + if (status_val == (status & stat_power)) |
|---|
| 1126 | + break; |
|---|
| 1127 | + |
|---|
| 1128 | + if (!main && !set) |
|---|
| 1129 | + usleep_range(600, 700); |
|---|
| 1130 | + else |
|---|
| 1131 | + usleep_range(10, 20); |
|---|
| 1132 | + } while (--wait > 0); |
|---|
| 1133 | + |
|---|
| 1134 | + if (status_val != (status & stat_power)) |
|---|
| 1135 | + dev_info(tegra->dev, "failed to %s %s PP %d\n", |
|---|
| 1136 | + set ? "set" : "clear", |
|---|
| 1137 | + main ? "HS" : "SS", status); |
|---|
| 1138 | +} |
|---|
| 1139 | + |
|---|
| 1140 | +static struct phy *tegra_xusb_get_phy(struct tegra_xusb *tegra, char *name, |
|---|
| 1141 | + int port) |
|---|
| 1142 | +{ |
|---|
| 1143 | + unsigned int i, phy_count = 0; |
|---|
| 1144 | + |
|---|
| 1145 | + for (i = 0; i < tegra->soc->num_types; i++) { |
|---|
| 1146 | + if (!strncmp(tegra->soc->phy_types[i].name, name, |
|---|
| 1147 | + strlen(name))) |
|---|
| 1148 | + return tegra->phys[phy_count+port]; |
|---|
| 1149 | + |
|---|
| 1150 | + phy_count += tegra->soc->phy_types[i].num; |
|---|
| 1151 | + } |
|---|
| 1152 | + |
|---|
| 1153 | + return NULL; |
|---|
| 1154 | +} |
|---|
| 1155 | + |
|---|
| 1156 | +static void tegra_xhci_id_work(struct work_struct *work) |
|---|
| 1157 | +{ |
|---|
| 1158 | + struct tegra_xusb *tegra = container_of(work, struct tegra_xusb, |
|---|
| 1159 | + id_work); |
|---|
| 1160 | + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); |
|---|
| 1161 | + struct tegra_xusb_mbox_msg msg; |
|---|
| 1162 | + struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", |
|---|
| 1163 | + tegra->otg_usb2_port); |
|---|
| 1164 | + u32 status; |
|---|
| 1165 | + int ret; |
|---|
| 1166 | + |
|---|
| 1167 | + dev_dbg(tegra->dev, "host mode %s\n", tegra->host_mode ? "on" : "off"); |
|---|
| 1168 | + |
|---|
| 1169 | + mutex_lock(&tegra->lock); |
|---|
| 1170 | + |
|---|
| 1171 | + if (tegra->host_mode) |
|---|
| 1172 | + phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST); |
|---|
| 1173 | + else |
|---|
| 1174 | + phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE); |
|---|
| 1175 | + |
|---|
| 1176 | + mutex_unlock(&tegra->lock); |
|---|
| 1177 | + |
|---|
| 1178 | + tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion(tegra->padctl, |
|---|
| 1179 | + tegra->otg_usb2_port); |
|---|
| 1180 | + |
|---|
| 1181 | + if (tegra->host_mode) { |
|---|
| 1182 | + /* switch to host mode */ |
|---|
| 1183 | + if (tegra->otg_usb3_port >= 0) { |
|---|
| 1184 | + if (tegra->soc->otg_reset_sspi) { |
|---|
| 1185 | + /* set PP=0 */ |
|---|
| 1186 | + tegra_xhci_hc_driver.hub_control( |
|---|
| 1187 | + xhci->shared_hcd, GetPortStatus, |
|---|
| 1188 | + 0, tegra->otg_usb3_port+1, |
|---|
| 1189 | + (char *) &status, sizeof(status)); |
|---|
| 1190 | + if (status & USB_SS_PORT_STAT_POWER) |
|---|
| 1191 | + tegra_xhci_set_port_power(tegra, false, |
|---|
| 1192 | + false); |
|---|
| 1193 | + |
|---|
| 1194 | + /* reset OTG port SSPI */ |
|---|
| 1195 | + msg.cmd = MBOX_CMD_RESET_SSPI; |
|---|
| 1196 | + msg.data = tegra->otg_usb3_port+1; |
|---|
| 1197 | + |
|---|
| 1198 | + ret = tegra_xusb_mbox_send(tegra, &msg); |
|---|
| 1199 | + if (ret < 0) { |
|---|
| 1200 | + dev_info(tegra->dev, |
|---|
| 1201 | + "failed to RESET_SSPI %d\n", |
|---|
| 1202 | + ret); |
|---|
| 1203 | + } |
|---|
| 1204 | + } |
|---|
| 1205 | + |
|---|
| 1206 | + tegra_xhci_set_port_power(tegra, false, true); |
|---|
| 1207 | + } |
|---|
| 1208 | + |
|---|
| 1209 | + tegra_xhci_set_port_power(tegra, true, true); |
|---|
| 1210 | + |
|---|
| 1211 | + } else { |
|---|
| 1212 | + if (tegra->otg_usb3_port >= 0) |
|---|
| 1213 | + tegra_xhci_set_port_power(tegra, false, false); |
|---|
| 1214 | + |
|---|
| 1215 | + tegra_xhci_set_port_power(tegra, true, false); |
|---|
| 1216 | + } |
|---|
| 1217 | +} |
|---|
| 1218 | + |
|---|
| 1219 | +static int tegra_xusb_get_usb2_port(struct tegra_xusb *tegra, |
|---|
| 1220 | + struct usb_phy *usbphy) |
|---|
| 1221 | +{ |
|---|
| 1222 | + unsigned int i; |
|---|
| 1223 | + |
|---|
| 1224 | + for (i = 0; i < tegra->num_usb_phys; i++) { |
|---|
| 1225 | + if (tegra->usbphy[i] && usbphy == tegra->usbphy[i]) |
|---|
| 1226 | + return i; |
|---|
| 1227 | + } |
|---|
| 1228 | + |
|---|
| 1229 | + return -1; |
|---|
| 1230 | +} |
|---|
| 1231 | + |
|---|
| 1232 | +static int tegra_xhci_id_notify(struct notifier_block *nb, |
|---|
| 1233 | + unsigned long action, void *data) |
|---|
| 1234 | +{ |
|---|
| 1235 | + struct tegra_xusb *tegra = container_of(nb, struct tegra_xusb, |
|---|
| 1236 | + id_nb); |
|---|
| 1237 | + struct usb_phy *usbphy = (struct usb_phy *)data; |
|---|
| 1238 | + |
|---|
| 1239 | + dev_dbg(tegra->dev, "%s(): action is %d", __func__, usbphy->last_event); |
|---|
| 1240 | + |
|---|
| 1241 | + if ((tegra->host_mode && usbphy->last_event == USB_EVENT_ID) || |
|---|
| 1242 | + (!tegra->host_mode && usbphy->last_event != USB_EVENT_ID)) { |
|---|
| 1243 | + dev_dbg(tegra->dev, "Same role(%d) received. Ignore", |
|---|
| 1244 | + tegra->host_mode); |
|---|
| 1245 | + return NOTIFY_OK; |
|---|
| 1246 | + } |
|---|
| 1247 | + |
|---|
| 1248 | + tegra->otg_usb2_port = tegra_xusb_get_usb2_port(tegra, usbphy); |
|---|
| 1249 | + |
|---|
| 1250 | + tegra->host_mode = (usbphy->last_event == USB_EVENT_ID) ? true : false; |
|---|
| 1251 | + |
|---|
| 1252 | + schedule_work(&tegra->id_work); |
|---|
| 1253 | + |
|---|
| 1254 | + return NOTIFY_OK; |
|---|
| 1255 | +} |
|---|
| 1256 | + |
|---|
| 1257 | +static int tegra_xusb_init_usb_phy(struct tegra_xusb *tegra) |
|---|
| 1258 | +{ |
|---|
| 1259 | + unsigned int i; |
|---|
| 1260 | + |
|---|
| 1261 | + tegra->usbphy = devm_kcalloc(tegra->dev, tegra->num_usb_phys, |
|---|
| 1262 | + sizeof(*tegra->usbphy), GFP_KERNEL); |
|---|
| 1263 | + if (!tegra->usbphy) |
|---|
| 1264 | + return -ENOMEM; |
|---|
| 1265 | + |
|---|
| 1266 | + INIT_WORK(&tegra->id_work, tegra_xhci_id_work); |
|---|
| 1267 | + tegra->id_nb.notifier_call = tegra_xhci_id_notify; |
|---|
| 1268 | + tegra->otg_usb2_port = -EINVAL; |
|---|
| 1269 | + tegra->otg_usb3_port = -EINVAL; |
|---|
| 1270 | + |
|---|
| 1271 | + for (i = 0; i < tegra->num_usb_phys; i++) { |
|---|
| 1272 | + struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", i); |
|---|
| 1273 | + |
|---|
| 1274 | + if (!phy) |
|---|
| 1275 | + continue; |
|---|
| 1276 | + |
|---|
| 1277 | + tegra->usbphy[i] = devm_usb_get_phy_by_node(tegra->dev, |
|---|
| 1278 | + phy->dev.of_node, |
|---|
| 1279 | + &tegra->id_nb); |
|---|
| 1280 | + if (!IS_ERR(tegra->usbphy[i])) { |
|---|
| 1281 | + dev_dbg(tegra->dev, "usbphy-%d registered", i); |
|---|
| 1282 | + otg_set_host(tegra->usbphy[i]->otg, &tegra->hcd->self); |
|---|
| 1283 | + } else { |
|---|
| 1284 | + /* |
|---|
| 1285 | + * usb-phy is optional, continue if its not available. |
|---|
| 1286 | + */ |
|---|
| 1287 | + tegra->usbphy[i] = NULL; |
|---|
| 1288 | + } |
|---|
| 1289 | + } |
|---|
| 1290 | + |
|---|
| 1291 | + return 0; |
|---|
| 1292 | +} |
|---|
| 1293 | + |
|---|
| 1294 | +static void tegra_xusb_deinit_usb_phy(struct tegra_xusb *tegra) |
|---|
| 1295 | +{ |
|---|
| 1296 | + unsigned int i; |
|---|
| 1297 | + |
|---|
| 1298 | + cancel_work_sync(&tegra->id_work); |
|---|
| 1299 | + |
|---|
| 1300 | + for (i = 0; i < tegra->num_usb_phys; i++) |
|---|
| 1301 | + if (tegra->usbphy[i]) |
|---|
| 1302 | + otg_set_host(tegra->usbphy[i]->otg, NULL); |
|---|
| 1303 | +} |
|---|
| 1304 | + |
|---|
| 1305 | +static int tegra_xusb_probe(struct platform_device *pdev) |
|---|
| 1306 | +{ |
|---|
| 942 | 1307 | struct tegra_xusb *tegra; |
|---|
| 1308 | + struct resource *regs; |
|---|
| 943 | 1309 | struct xhci_hcd *xhci; |
|---|
| 944 | 1310 | unsigned int i, j, k; |
|---|
| 945 | 1311 | struct phy *phy; |
|---|
| .. | .. |
|---|
| 955 | 1321 | mutex_init(&tegra->lock); |
|---|
| 956 | 1322 | tegra->dev = &pdev->dev; |
|---|
| 957 | 1323 | |
|---|
| 1324 | + err = tegra_xusb_init_context(tegra); |
|---|
| 1325 | + if (err < 0) |
|---|
| 1326 | + return err; |
|---|
| 1327 | + |
|---|
| 958 | 1328 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 959 | 1329 | tegra->regs = devm_ioremap_resource(&pdev->dev, regs); |
|---|
| 960 | 1330 | if (IS_ERR(tegra->regs)) |
|---|
| 961 | 1331 | return PTR_ERR(tegra->regs); |
|---|
| 962 | 1332 | |
|---|
| 963 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
|---|
| 964 | | - tegra->fpci_base = devm_ioremap_resource(&pdev->dev, res); |
|---|
| 1333 | + tegra->fpci_base = devm_platform_ioremap_resource(pdev, 1); |
|---|
| 965 | 1334 | if (IS_ERR(tegra->fpci_base)) |
|---|
| 966 | 1335 | return PTR_ERR(tegra->fpci_base); |
|---|
| 967 | 1336 | |
|---|
| 968 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 2); |
|---|
| 969 | | - tegra->ipfs_base = devm_ioremap_resource(&pdev->dev, res); |
|---|
| 970 | | - if (IS_ERR(tegra->ipfs_base)) |
|---|
| 971 | | - return PTR_ERR(tegra->ipfs_base); |
|---|
| 1337 | + if (tegra->soc->has_ipfs) { |
|---|
| 1338 | + tegra->ipfs_base = devm_platform_ioremap_resource(pdev, 2); |
|---|
| 1339 | + if (IS_ERR(tegra->ipfs_base)) |
|---|
| 1340 | + return PTR_ERR(tegra->ipfs_base); |
|---|
| 1341 | + } |
|---|
| 972 | 1342 | |
|---|
| 973 | 1343 | tegra->xhci_irq = platform_get_irq(pdev, 0); |
|---|
| 974 | 1344 | if (tegra->xhci_irq < 0) |
|---|
| .. | .. |
|---|
| 1045 | 1415 | goto put_padctl; |
|---|
| 1046 | 1416 | } |
|---|
| 1047 | 1417 | |
|---|
| 1048 | | - if (!pdev->dev.pm_domain) { |
|---|
| 1418 | + if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) { |
|---|
| 1049 | 1419 | tegra->host_rst = devm_reset_control_get(&pdev->dev, |
|---|
| 1050 | 1420 | "xusb_host"); |
|---|
| 1051 | 1421 | if (IS_ERR(tegra->host_rst)) { |
|---|
| .. | .. |
|---|
| 1076 | 1446 | tegra->host_clk, |
|---|
| 1077 | 1447 | tegra->host_rst); |
|---|
| 1078 | 1448 | if (err) { |
|---|
| 1449 | + tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA); |
|---|
| 1079 | 1450 | dev_err(&pdev->dev, |
|---|
| 1080 | 1451 | "failed to enable XUSBC domain: %d\n", err); |
|---|
| 1081 | | - goto disable_xusba; |
|---|
| 1452 | + goto put_padctl; |
|---|
| 1082 | 1453 | } |
|---|
| 1454 | + } else { |
|---|
| 1455 | + err = tegra_xusb_powerdomain_init(&pdev->dev, tegra); |
|---|
| 1456 | + if (err) |
|---|
| 1457 | + goto put_powerdomains; |
|---|
| 1083 | 1458 | } |
|---|
| 1084 | 1459 | |
|---|
| 1085 | 1460 | tegra->supplies = devm_kcalloc(&pdev->dev, tegra->soc->num_supplies, |
|---|
| 1086 | 1461 | sizeof(*tegra->supplies), GFP_KERNEL); |
|---|
| 1087 | 1462 | if (!tegra->supplies) { |
|---|
| 1088 | 1463 | err = -ENOMEM; |
|---|
| 1089 | | - goto disable_xusbc; |
|---|
| 1464 | + goto put_powerdomains; |
|---|
| 1090 | 1465 | } |
|---|
| 1091 | 1466 | |
|---|
| 1092 | | - for (i = 0; i < tegra->soc->num_supplies; i++) |
|---|
| 1093 | | - tegra->supplies[i].supply = tegra->soc->supply_names[i]; |
|---|
| 1467 | + regulator_bulk_set_supply_names(tegra->supplies, |
|---|
| 1468 | + tegra->soc->supply_names, |
|---|
| 1469 | + tegra->soc->num_supplies); |
|---|
| 1094 | 1470 | |
|---|
| 1095 | 1471 | err = devm_regulator_bulk_get(&pdev->dev, tegra->soc->num_supplies, |
|---|
| 1096 | 1472 | tegra->supplies); |
|---|
| 1097 | 1473 | if (err) { |
|---|
| 1098 | 1474 | dev_err(&pdev->dev, "failed to get regulators: %d\n", err); |
|---|
| 1099 | | - goto disable_xusbc; |
|---|
| 1475 | + goto put_powerdomains; |
|---|
| 1100 | 1476 | } |
|---|
| 1101 | 1477 | |
|---|
| 1102 | | - for (i = 0; i < tegra->soc->num_types; i++) |
|---|
| 1478 | + for (i = 0; i < tegra->soc->num_types; i++) { |
|---|
| 1479 | + if (!strncmp(tegra->soc->phy_types[i].name, "usb2", 4)) |
|---|
| 1480 | + tegra->num_usb_phys = tegra->soc->phy_types[i].num; |
|---|
| 1103 | 1481 | tegra->num_phys += tegra->soc->phy_types[i].num; |
|---|
| 1482 | + } |
|---|
| 1104 | 1483 | |
|---|
| 1105 | 1484 | tegra->phys = devm_kcalloc(&pdev->dev, tegra->num_phys, |
|---|
| 1106 | 1485 | sizeof(*tegra->phys), GFP_KERNEL); |
|---|
| 1107 | 1486 | if (!tegra->phys) { |
|---|
| 1108 | 1487 | err = -ENOMEM; |
|---|
| 1109 | | - goto disable_xusbc; |
|---|
| 1488 | + goto put_powerdomains; |
|---|
| 1110 | 1489 | } |
|---|
| 1111 | 1490 | |
|---|
| 1112 | 1491 | for (i = 0, k = 0; i < tegra->soc->num_types; i++) { |
|---|
| .. | .. |
|---|
| 1122 | 1501 | "failed to get PHY %s: %ld\n", prop, |
|---|
| 1123 | 1502 | PTR_ERR(phy)); |
|---|
| 1124 | 1503 | err = PTR_ERR(phy); |
|---|
| 1125 | | - goto disable_xusbc; |
|---|
| 1504 | + goto put_powerdomains; |
|---|
| 1126 | 1505 | } |
|---|
| 1127 | 1506 | |
|---|
| 1128 | 1507 | tegra->phys[k++] = phy; |
|---|
| .. | .. |
|---|
| 1133 | 1512 | dev_name(&pdev->dev)); |
|---|
| 1134 | 1513 | if (!tegra->hcd) { |
|---|
| 1135 | 1514 | err = -ENOMEM; |
|---|
| 1136 | | - goto disable_xusbc; |
|---|
| 1515 | + goto put_powerdomains; |
|---|
| 1137 | 1516 | } |
|---|
| 1517 | + |
|---|
| 1518 | + tegra->hcd->regs = tegra->regs; |
|---|
| 1519 | + tegra->hcd->rsrc_start = regs->start; |
|---|
| 1520 | + tegra->hcd->rsrc_len = resource_size(regs); |
|---|
| 1138 | 1521 | |
|---|
| 1139 | 1522 | /* |
|---|
| 1140 | 1523 | * This must happen after usb_create_hcd(), because usb_create_hcd() |
|---|
| .. | .. |
|---|
| 1142 | 1525 | */ |
|---|
| 1143 | 1526 | platform_set_drvdata(pdev, tegra); |
|---|
| 1144 | 1527 | |
|---|
| 1145 | | - pm_runtime_enable(&pdev->dev); |
|---|
| 1146 | | - if (pm_runtime_enabled(&pdev->dev)) |
|---|
| 1147 | | - err = pm_runtime_get_sync(&pdev->dev); |
|---|
| 1148 | | - else |
|---|
| 1149 | | - err = tegra_xusb_runtime_resume(&pdev->dev); |
|---|
| 1150 | | - |
|---|
| 1528 | + err = tegra_xusb_phy_enable(tegra); |
|---|
| 1151 | 1529 | if (err < 0) { |
|---|
| 1152 | | - dev_err(&pdev->dev, "failed to enable device: %d\n", err); |
|---|
| 1153 | | - goto disable_rpm; |
|---|
| 1530 | + dev_err(&pdev->dev, "failed to enable PHYs: %d\n", err); |
|---|
| 1531 | + goto put_hcd; |
|---|
| 1154 | 1532 | } |
|---|
| 1155 | | - |
|---|
| 1156 | | - tegra_xusb_ipfs_config(tegra, regs); |
|---|
| 1157 | 1533 | |
|---|
| 1158 | 1534 | /* |
|---|
| 1159 | 1535 | * The XUSB Falcon microcontroller can only address 40 bits, so set |
|---|
| .. | .. |
|---|
| 1162 | 1538 | err = dma_set_mask_and_coherent(tegra->dev, DMA_BIT_MASK(40)); |
|---|
| 1163 | 1539 | if (err < 0) { |
|---|
| 1164 | 1540 | dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err); |
|---|
| 1165 | | - goto put_rpm; |
|---|
| 1541 | + goto disable_phy; |
|---|
| 1166 | 1542 | } |
|---|
| 1543 | + |
|---|
| 1544 | + err = tegra_xusb_request_firmware(tegra); |
|---|
| 1545 | + if (err < 0) { |
|---|
| 1546 | + dev_err(&pdev->dev, "failed to request firmware: %d\n", err); |
|---|
| 1547 | + goto disable_phy; |
|---|
| 1548 | + } |
|---|
| 1549 | + |
|---|
| 1550 | + pm_runtime_enable(&pdev->dev); |
|---|
| 1551 | + |
|---|
| 1552 | + if (!pm_runtime_enabled(&pdev->dev)) |
|---|
| 1553 | + err = tegra_xusb_runtime_resume(&pdev->dev); |
|---|
| 1554 | + else |
|---|
| 1555 | + err = pm_runtime_get_sync(&pdev->dev); |
|---|
| 1556 | + |
|---|
| 1557 | + if (err < 0) { |
|---|
| 1558 | + dev_err(&pdev->dev, "failed to enable device: %d\n", err); |
|---|
| 1559 | + goto free_firmware; |
|---|
| 1560 | + } |
|---|
| 1561 | + |
|---|
| 1562 | + tegra_xusb_config(tegra); |
|---|
| 1167 | 1563 | |
|---|
| 1168 | 1564 | err = tegra_xusb_load_firmware(tegra); |
|---|
| 1169 | 1565 | if (err < 0) { |
|---|
| 1170 | 1566 | dev_err(&pdev->dev, "failed to load firmware: %d\n", err); |
|---|
| 1171 | 1567 | goto put_rpm; |
|---|
| 1172 | 1568 | } |
|---|
| 1173 | | - |
|---|
| 1174 | | - tegra->hcd->regs = tegra->regs; |
|---|
| 1175 | | - tegra->hcd->rsrc_start = regs->start; |
|---|
| 1176 | | - tegra->hcd->rsrc_len = resource_size(regs); |
|---|
| 1177 | 1569 | |
|---|
| 1178 | 1570 | err = usb_add_hcd(tegra->hcd, tegra->xhci_irq, IRQF_SHARED); |
|---|
| 1179 | 1571 | if (err < 0) { |
|---|
| .. | .. |
|---|
| 1201 | 1593 | goto put_usb3; |
|---|
| 1202 | 1594 | } |
|---|
| 1203 | 1595 | |
|---|
| 1204 | | - mutex_lock(&tegra->lock); |
|---|
| 1205 | | - |
|---|
| 1206 | | - /* Enable firmware messages from controller. */ |
|---|
| 1207 | | - msg.cmd = MBOX_CMD_MSG_ENABLED; |
|---|
| 1208 | | - msg.data = 0; |
|---|
| 1209 | | - |
|---|
| 1210 | | - err = tegra_xusb_mbox_send(tegra, &msg); |
|---|
| 1596 | + err = tegra_xusb_enable_firmware_messages(tegra); |
|---|
| 1211 | 1597 | if (err < 0) { |
|---|
| 1212 | 1598 | dev_err(&pdev->dev, "failed to enable messages: %d\n", err); |
|---|
| 1213 | | - mutex_unlock(&tegra->lock); |
|---|
| 1214 | 1599 | goto remove_usb3; |
|---|
| 1215 | 1600 | } |
|---|
| 1216 | | - |
|---|
| 1217 | | - mutex_unlock(&tegra->lock); |
|---|
| 1218 | 1601 | |
|---|
| 1219 | 1602 | err = devm_request_threaded_irq(&pdev->dev, tegra->mbox_irq, |
|---|
| 1220 | 1603 | tegra_xusb_mbox_irq, |
|---|
| .. | .. |
|---|
| 1222 | 1605 | dev_name(&pdev->dev), tegra); |
|---|
| 1223 | 1606 | if (err < 0) { |
|---|
| 1224 | 1607 | dev_err(&pdev->dev, "failed to request IRQ: %d\n", err); |
|---|
| 1608 | + goto remove_usb3; |
|---|
| 1609 | + } |
|---|
| 1610 | + |
|---|
| 1611 | + err = tegra_xusb_init_usb_phy(tegra); |
|---|
| 1612 | + if (err < 0) { |
|---|
| 1613 | + dev_err(&pdev->dev, "failed to init USB PHY: %d\n", err); |
|---|
| 1225 | 1614 | goto remove_usb3; |
|---|
| 1226 | 1615 | } |
|---|
| 1227 | 1616 | |
|---|
| .. | .. |
|---|
| 1236 | 1625 | put_rpm: |
|---|
| 1237 | 1626 | if (!pm_runtime_status_suspended(&pdev->dev)) |
|---|
| 1238 | 1627 | tegra_xusb_runtime_suspend(&pdev->dev); |
|---|
| 1239 | | -disable_rpm: |
|---|
| 1240 | | - pm_runtime_disable(&pdev->dev); |
|---|
| 1628 | +put_hcd: |
|---|
| 1241 | 1629 | usb_put_hcd(tegra->hcd); |
|---|
| 1242 | | -disable_xusbc: |
|---|
| 1243 | | - if (!pdev->dev.pm_domain) |
|---|
| 1630 | +free_firmware: |
|---|
| 1631 | + dma_free_coherent(&pdev->dev, tegra->fw.size, tegra->fw.virt, |
|---|
| 1632 | + tegra->fw.phys); |
|---|
| 1633 | +disable_phy: |
|---|
| 1634 | + tegra_xusb_phy_disable(tegra); |
|---|
| 1635 | + pm_runtime_disable(&pdev->dev); |
|---|
| 1636 | +put_powerdomains: |
|---|
| 1637 | + if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) { |
|---|
| 1244 | 1638 | tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC); |
|---|
| 1245 | | -disable_xusba: |
|---|
| 1246 | | - if (!pdev->dev.pm_domain) |
|---|
| 1247 | 1639 | tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA); |
|---|
| 1640 | + } else { |
|---|
| 1641 | + tegra_xusb_powerdomain_remove(&pdev->dev, tegra); |
|---|
| 1642 | + } |
|---|
| 1248 | 1643 | put_padctl: |
|---|
| 1249 | 1644 | tegra_xusb_padctl_put(tegra->padctl); |
|---|
| 1250 | 1645 | return err; |
|---|
| .. | .. |
|---|
| 1254 | 1649 | { |
|---|
| 1255 | 1650 | struct tegra_xusb *tegra = platform_get_drvdata(pdev); |
|---|
| 1256 | 1651 | struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); |
|---|
| 1652 | + |
|---|
| 1653 | + tegra_xusb_deinit_usb_phy(tegra); |
|---|
| 1257 | 1654 | |
|---|
| 1258 | 1655 | usb_remove_hcd(xhci->shared_hcd); |
|---|
| 1259 | 1656 | usb_put_hcd(xhci->shared_hcd); |
|---|
| .. | .. |
|---|
| 1267 | 1664 | pm_runtime_put_sync(&pdev->dev); |
|---|
| 1268 | 1665 | pm_runtime_disable(&pdev->dev); |
|---|
| 1269 | 1666 | |
|---|
| 1667 | + if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) { |
|---|
| 1668 | + tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC); |
|---|
| 1669 | + tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA); |
|---|
| 1670 | + } else { |
|---|
| 1671 | + tegra_xusb_powerdomain_remove(&pdev->dev, tegra); |
|---|
| 1672 | + } |
|---|
| 1673 | + |
|---|
| 1674 | + tegra_xusb_phy_disable(tegra); |
|---|
| 1675 | + |
|---|
| 1270 | 1676 | tegra_xusb_padctl_put(tegra->padctl); |
|---|
| 1271 | 1677 | |
|---|
| 1272 | 1678 | return 0; |
|---|
| 1273 | 1679 | } |
|---|
| 1274 | 1680 | |
|---|
| 1275 | 1681 | #ifdef CONFIG_PM_SLEEP |
|---|
| 1682 | +static bool xhci_hub_ports_suspended(struct xhci_hub *hub) |
|---|
| 1683 | +{ |
|---|
| 1684 | + struct device *dev = hub->hcd->self.controller; |
|---|
| 1685 | + bool status = true; |
|---|
| 1686 | + unsigned int i; |
|---|
| 1687 | + u32 value; |
|---|
| 1688 | + |
|---|
| 1689 | + for (i = 0; i < hub->num_ports; i++) { |
|---|
| 1690 | + value = readl(hub->ports[i]->addr); |
|---|
| 1691 | + if ((value & PORT_PE) == 0) |
|---|
| 1692 | + continue; |
|---|
| 1693 | + |
|---|
| 1694 | + if ((value & PORT_PLS_MASK) != XDEV_U3) { |
|---|
| 1695 | + dev_info(dev, "%u-%u isn't suspended: %#010x\n", |
|---|
| 1696 | + hub->hcd->self.busnum, i + 1, value); |
|---|
| 1697 | + status = false; |
|---|
| 1698 | + } |
|---|
| 1699 | + } |
|---|
| 1700 | + |
|---|
| 1701 | + return status; |
|---|
| 1702 | +} |
|---|
| 1703 | + |
|---|
| 1704 | +static int tegra_xusb_check_ports(struct tegra_xusb *tegra) |
|---|
| 1705 | +{ |
|---|
| 1706 | + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); |
|---|
| 1707 | + unsigned long flags; |
|---|
| 1708 | + int err = 0; |
|---|
| 1709 | + |
|---|
| 1710 | + spin_lock_irqsave(&xhci->lock, flags); |
|---|
| 1711 | + |
|---|
| 1712 | + if (!xhci_hub_ports_suspended(&xhci->usb2_rhub) || |
|---|
| 1713 | + !xhci_hub_ports_suspended(&xhci->usb3_rhub)) |
|---|
| 1714 | + err = -EBUSY; |
|---|
| 1715 | + |
|---|
| 1716 | + spin_unlock_irqrestore(&xhci->lock, flags); |
|---|
| 1717 | + |
|---|
| 1718 | + return err; |
|---|
| 1719 | +} |
|---|
| 1720 | + |
|---|
| 1721 | +static void tegra_xusb_save_context(struct tegra_xusb *tegra) |
|---|
| 1722 | +{ |
|---|
| 1723 | + const struct tegra_xusb_context_soc *soc = tegra->soc->context; |
|---|
| 1724 | + struct tegra_xusb_context *ctx = &tegra->context; |
|---|
| 1725 | + unsigned int i; |
|---|
| 1726 | + |
|---|
| 1727 | + if (soc->ipfs.num_offsets > 0) { |
|---|
| 1728 | + for (i = 0; i < soc->ipfs.num_offsets; i++) |
|---|
| 1729 | + ctx->ipfs[i] = ipfs_readl(tegra, soc->ipfs.offsets[i]); |
|---|
| 1730 | + } |
|---|
| 1731 | + |
|---|
| 1732 | + if (soc->fpci.num_offsets > 0) { |
|---|
| 1733 | + for (i = 0; i < soc->fpci.num_offsets; i++) |
|---|
| 1734 | + ctx->fpci[i] = fpci_readl(tegra, soc->fpci.offsets[i]); |
|---|
| 1735 | + } |
|---|
| 1736 | +} |
|---|
| 1737 | + |
|---|
| 1738 | +static void tegra_xusb_restore_context(struct tegra_xusb *tegra) |
|---|
| 1739 | +{ |
|---|
| 1740 | + const struct tegra_xusb_context_soc *soc = tegra->soc->context; |
|---|
| 1741 | + struct tegra_xusb_context *ctx = &tegra->context; |
|---|
| 1742 | + unsigned int i; |
|---|
| 1743 | + |
|---|
| 1744 | + if (soc->fpci.num_offsets > 0) { |
|---|
| 1745 | + for (i = 0; i < soc->fpci.num_offsets; i++) |
|---|
| 1746 | + fpci_writel(tegra, ctx->fpci[i], soc->fpci.offsets[i]); |
|---|
| 1747 | + } |
|---|
| 1748 | + |
|---|
| 1749 | + if (soc->ipfs.num_offsets > 0) { |
|---|
| 1750 | + for (i = 0; i < soc->ipfs.num_offsets; i++) |
|---|
| 1751 | + ipfs_writel(tegra, ctx->ipfs[i], soc->ipfs.offsets[i]); |
|---|
| 1752 | + } |
|---|
| 1753 | +} |
|---|
| 1754 | + |
|---|
| 1755 | +static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool wakeup) |
|---|
| 1756 | +{ |
|---|
| 1757 | + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); |
|---|
| 1758 | + int err; |
|---|
| 1759 | + |
|---|
| 1760 | + err = tegra_xusb_check_ports(tegra); |
|---|
| 1761 | + if (err < 0) { |
|---|
| 1762 | + dev_err(tegra->dev, "not all ports suspended: %d\n", err); |
|---|
| 1763 | + return err; |
|---|
| 1764 | + } |
|---|
| 1765 | + |
|---|
| 1766 | + err = xhci_suspend(xhci, wakeup); |
|---|
| 1767 | + if (err < 0) { |
|---|
| 1768 | + dev_err(tegra->dev, "failed to suspend XHCI: %d\n", err); |
|---|
| 1769 | + return err; |
|---|
| 1770 | + } |
|---|
| 1771 | + |
|---|
| 1772 | + tegra_xusb_save_context(tegra); |
|---|
| 1773 | + tegra_xusb_phy_disable(tegra); |
|---|
| 1774 | + tegra_xusb_clk_disable(tegra); |
|---|
| 1775 | + |
|---|
| 1776 | + return 0; |
|---|
| 1777 | +} |
|---|
| 1778 | + |
|---|
| 1779 | +static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool wakeup) |
|---|
| 1780 | +{ |
|---|
| 1781 | + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); |
|---|
| 1782 | + int err; |
|---|
| 1783 | + |
|---|
| 1784 | + err = tegra_xusb_clk_enable(tegra); |
|---|
| 1785 | + if (err < 0) { |
|---|
| 1786 | + dev_err(tegra->dev, "failed to enable clocks: %d\n", err); |
|---|
| 1787 | + return err; |
|---|
| 1788 | + } |
|---|
| 1789 | + |
|---|
| 1790 | + err = tegra_xusb_phy_enable(tegra); |
|---|
| 1791 | + if (err < 0) { |
|---|
| 1792 | + dev_err(tegra->dev, "failed to enable PHYs: %d\n", err); |
|---|
| 1793 | + goto disable_clk; |
|---|
| 1794 | + } |
|---|
| 1795 | + |
|---|
| 1796 | + tegra_xusb_config(tegra); |
|---|
| 1797 | + tegra_xusb_restore_context(tegra); |
|---|
| 1798 | + |
|---|
| 1799 | + err = tegra_xusb_load_firmware(tegra); |
|---|
| 1800 | + if (err < 0) { |
|---|
| 1801 | + dev_err(tegra->dev, "failed to load firmware: %d\n", err); |
|---|
| 1802 | + goto disable_phy; |
|---|
| 1803 | + } |
|---|
| 1804 | + |
|---|
| 1805 | + err = __tegra_xusb_enable_firmware_messages(tegra); |
|---|
| 1806 | + if (err < 0) { |
|---|
| 1807 | + dev_err(tegra->dev, "failed to enable messages: %d\n", err); |
|---|
| 1808 | + goto disable_phy; |
|---|
| 1809 | + } |
|---|
| 1810 | + |
|---|
| 1811 | + err = xhci_resume(xhci, true); |
|---|
| 1812 | + if (err < 0) { |
|---|
| 1813 | + dev_err(tegra->dev, "failed to resume XHCI: %d\n", err); |
|---|
| 1814 | + goto disable_phy; |
|---|
| 1815 | + } |
|---|
| 1816 | + |
|---|
| 1817 | + return 0; |
|---|
| 1818 | + |
|---|
| 1819 | +disable_phy: |
|---|
| 1820 | + tegra_xusb_phy_disable(tegra); |
|---|
| 1821 | +disable_clk: |
|---|
| 1822 | + tegra_xusb_clk_disable(tegra); |
|---|
| 1823 | + return err; |
|---|
| 1824 | +} |
|---|
| 1825 | + |
|---|
| 1276 | 1826 | static int tegra_xusb_suspend(struct device *dev) |
|---|
| 1277 | 1827 | { |
|---|
| 1278 | 1828 | struct tegra_xusb *tegra = dev_get_drvdata(dev); |
|---|
| 1279 | | - struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); |
|---|
| 1280 | 1829 | bool wakeup = device_may_wakeup(dev); |
|---|
| 1830 | + int err; |
|---|
| 1281 | 1831 | |
|---|
| 1282 | | - /* TODO: Powergate controller across suspend/resume. */ |
|---|
| 1283 | | - return xhci_suspend(xhci, wakeup); |
|---|
| 1832 | + synchronize_irq(tegra->mbox_irq); |
|---|
| 1833 | + |
|---|
| 1834 | + mutex_lock(&tegra->lock); |
|---|
| 1835 | + err = tegra_xusb_enter_elpg(tegra, wakeup); |
|---|
| 1836 | + mutex_unlock(&tegra->lock); |
|---|
| 1837 | + |
|---|
| 1838 | + return err; |
|---|
| 1284 | 1839 | } |
|---|
| 1285 | 1840 | |
|---|
| 1286 | 1841 | static int tegra_xusb_resume(struct device *dev) |
|---|
| 1287 | 1842 | { |
|---|
| 1288 | 1843 | struct tegra_xusb *tegra = dev_get_drvdata(dev); |
|---|
| 1289 | | - struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); |
|---|
| 1844 | + bool wakeup = device_may_wakeup(dev); |
|---|
| 1845 | + int err; |
|---|
| 1290 | 1846 | |
|---|
| 1291 | | - return xhci_resume(xhci, 0); |
|---|
| 1847 | + mutex_lock(&tegra->lock); |
|---|
| 1848 | + err = tegra_xusb_exit_elpg(tegra, wakeup); |
|---|
| 1849 | + mutex_unlock(&tegra->lock); |
|---|
| 1850 | + |
|---|
| 1851 | + return err; |
|---|
| 1292 | 1852 | } |
|---|
| 1293 | 1853 | #endif |
|---|
| 1294 | 1854 | |
|---|
| .. | .. |
|---|
| 1302 | 1862 | "avddio-pex", |
|---|
| 1303 | 1863 | "dvddio-pex", |
|---|
| 1304 | 1864 | "avdd-usb", |
|---|
| 1305 | | - "avdd-pll-utmip", |
|---|
| 1306 | | - "avdd-pll-erefe", |
|---|
| 1307 | | - "avdd-usb-ss-pll", |
|---|
| 1308 | 1865 | "hvdd-usb-ss", |
|---|
| 1309 | | - "hvdd-usb-ss-pll-e", |
|---|
| 1310 | 1866 | }; |
|---|
| 1311 | 1867 | |
|---|
| 1312 | 1868 | static const struct tegra_xusb_phy_type tegra124_phy_types[] = { |
|---|
| .. | .. |
|---|
| 1315 | 1871 | { .name = "hsic", .num = 2, }, |
|---|
| 1316 | 1872 | }; |
|---|
| 1317 | 1873 | |
|---|
| 1874 | +static const unsigned int tegra124_xusb_context_ipfs[] = { |
|---|
| 1875 | + IPFS_XUSB_HOST_MSI_BAR_SZ_0, |
|---|
| 1876 | + IPFS_XUSB_HOST_MSI_AXI_BAR_ST_0, |
|---|
| 1877 | + IPFS_XUSB_HOST_MSI_FPCI_BAR_ST_0, |
|---|
| 1878 | + IPFS_XUSB_HOST_MSI_VEC0_0, |
|---|
| 1879 | + IPFS_XUSB_HOST_MSI_EN_VEC0_0, |
|---|
| 1880 | + IPFS_XUSB_HOST_FPCI_ERROR_MASKS_0, |
|---|
| 1881 | + IPFS_XUSB_HOST_INTR_MASK_0, |
|---|
| 1882 | + IPFS_XUSB_HOST_INTR_ENABLE_0, |
|---|
| 1883 | + IPFS_XUSB_HOST_UFPCI_CONFIG_0, |
|---|
| 1884 | + IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0, |
|---|
| 1885 | + IPFS_XUSB_HOST_MCCIF_FIFOCTRL_0, |
|---|
| 1886 | +}; |
|---|
| 1887 | + |
|---|
| 1888 | +static const unsigned int tegra124_xusb_context_fpci[] = { |
|---|
| 1889 | + XUSB_CFG_ARU_CONTEXT_HS_PLS, |
|---|
| 1890 | + XUSB_CFG_ARU_CONTEXT_FS_PLS, |
|---|
| 1891 | + XUSB_CFG_ARU_CONTEXT_HSFS_SPEED, |
|---|
| 1892 | + XUSB_CFG_ARU_CONTEXT_HSFS_PP, |
|---|
| 1893 | + XUSB_CFG_ARU_CONTEXT, |
|---|
| 1894 | + XUSB_CFG_AXI_CFG, |
|---|
| 1895 | + XUSB_CFG_24, |
|---|
| 1896 | + XUSB_CFG_16, |
|---|
| 1897 | +}; |
|---|
| 1898 | + |
|---|
| 1899 | +static const struct tegra_xusb_context_soc tegra124_xusb_context = { |
|---|
| 1900 | + .ipfs = { |
|---|
| 1901 | + .num_offsets = ARRAY_SIZE(tegra124_xusb_context_ipfs), |
|---|
| 1902 | + .offsets = tegra124_xusb_context_ipfs, |
|---|
| 1903 | + }, |
|---|
| 1904 | + .fpci = { |
|---|
| 1905 | + .num_offsets = ARRAY_SIZE(tegra124_xusb_context_fpci), |
|---|
| 1906 | + .offsets = tegra124_xusb_context_fpci, |
|---|
| 1907 | + }, |
|---|
| 1908 | +}; |
|---|
| 1909 | + |
|---|
| 1318 | 1910 | static const struct tegra_xusb_soc tegra124_soc = { |
|---|
| 1319 | 1911 | .firmware = "nvidia/tegra124/xusb.bin", |
|---|
| 1320 | 1912 | .supply_names = tegra124_supply_names, |
|---|
| 1321 | 1913 | .num_supplies = ARRAY_SIZE(tegra124_supply_names), |
|---|
| 1322 | 1914 | .phy_types = tegra124_phy_types, |
|---|
| 1323 | 1915 | .num_types = ARRAY_SIZE(tegra124_phy_types), |
|---|
| 1916 | + .context = &tegra124_xusb_context, |
|---|
| 1324 | 1917 | .ports = { |
|---|
| 1325 | 1918 | .usb2 = { .offset = 4, .count = 4, }, |
|---|
| 1326 | 1919 | .hsic = { .offset = 6, .count = 2, }, |
|---|
| 1327 | 1920 | .usb3 = { .offset = 0, .count = 2, }, |
|---|
| 1328 | 1921 | }, |
|---|
| 1329 | 1922 | .scale_ss_clock = true, |
|---|
| 1923 | + .has_ipfs = true, |
|---|
| 1924 | + .otg_reset_sspi = false, |
|---|
| 1925 | + .mbox = { |
|---|
| 1926 | + .cmd = 0xe4, |
|---|
| 1927 | + .data_in = 0xe8, |
|---|
| 1928 | + .data_out = 0xec, |
|---|
| 1929 | + .owner = 0xf0, |
|---|
| 1930 | + }, |
|---|
| 1330 | 1931 | }; |
|---|
| 1331 | 1932 | MODULE_FIRMWARE("nvidia/tegra124/xusb.bin"); |
|---|
| 1332 | 1933 | |
|---|
| .. | .. |
|---|
| 1334 | 1935 | "dvddio-pex", |
|---|
| 1335 | 1936 | "hvddio-pex", |
|---|
| 1336 | 1937 | "avdd-usb", |
|---|
| 1337 | | - "avdd-pll-utmip", |
|---|
| 1338 | | - "avdd-pll-uerefe", |
|---|
| 1339 | | - "dvdd-pex-pll", |
|---|
| 1340 | | - "hvdd-pex-pll-e", |
|---|
| 1341 | 1938 | }; |
|---|
| 1342 | 1939 | |
|---|
| 1343 | 1940 | static const struct tegra_xusb_phy_type tegra210_phy_types[] = { |
|---|
| .. | .. |
|---|
| 1352 | 1949 | .num_supplies = ARRAY_SIZE(tegra210_supply_names), |
|---|
| 1353 | 1950 | .phy_types = tegra210_phy_types, |
|---|
| 1354 | 1951 | .num_types = ARRAY_SIZE(tegra210_phy_types), |
|---|
| 1952 | + .context = &tegra124_xusb_context, |
|---|
| 1355 | 1953 | .ports = { |
|---|
| 1356 | 1954 | .usb2 = { .offset = 4, .count = 4, }, |
|---|
| 1357 | 1955 | .hsic = { .offset = 8, .count = 1, }, |
|---|
| 1358 | 1956 | .usb3 = { .offset = 0, .count = 4, }, |
|---|
| 1359 | 1957 | }, |
|---|
| 1360 | 1958 | .scale_ss_clock = false, |
|---|
| 1959 | + .has_ipfs = true, |
|---|
| 1960 | + .otg_reset_sspi = true, |
|---|
| 1961 | + .mbox = { |
|---|
| 1962 | + .cmd = 0xe4, |
|---|
| 1963 | + .data_in = 0xe8, |
|---|
| 1964 | + .data_out = 0xec, |
|---|
| 1965 | + .owner = 0xf0, |
|---|
| 1966 | + }, |
|---|
| 1361 | 1967 | }; |
|---|
| 1362 | 1968 | MODULE_FIRMWARE("nvidia/tegra210/xusb.bin"); |
|---|
| 1969 | + |
|---|
| 1970 | +static const char * const tegra186_supply_names[] = { |
|---|
| 1971 | +}; |
|---|
| 1972 | +MODULE_FIRMWARE("nvidia/tegra186/xusb.bin"); |
|---|
| 1973 | + |
|---|
| 1974 | +static const struct tegra_xusb_phy_type tegra186_phy_types[] = { |
|---|
| 1975 | + { .name = "usb3", .num = 3, }, |
|---|
| 1976 | + { .name = "usb2", .num = 3, }, |
|---|
| 1977 | + { .name = "hsic", .num = 1, }, |
|---|
| 1978 | +}; |
|---|
| 1979 | + |
|---|
| 1980 | +static const struct tegra_xusb_context_soc tegra186_xusb_context = { |
|---|
| 1981 | + .fpci = { |
|---|
| 1982 | + .num_offsets = ARRAY_SIZE(tegra124_xusb_context_fpci), |
|---|
| 1983 | + .offsets = tegra124_xusb_context_fpci, |
|---|
| 1984 | + }, |
|---|
| 1985 | +}; |
|---|
| 1986 | + |
|---|
| 1987 | +static const struct tegra_xusb_soc tegra186_soc = { |
|---|
| 1988 | + .firmware = "nvidia/tegra186/xusb.bin", |
|---|
| 1989 | + .supply_names = tegra186_supply_names, |
|---|
| 1990 | + .num_supplies = ARRAY_SIZE(tegra186_supply_names), |
|---|
| 1991 | + .phy_types = tegra186_phy_types, |
|---|
| 1992 | + .num_types = ARRAY_SIZE(tegra186_phy_types), |
|---|
| 1993 | + .context = &tegra186_xusb_context, |
|---|
| 1994 | + .ports = { |
|---|
| 1995 | + .usb3 = { .offset = 0, .count = 3, }, |
|---|
| 1996 | + .usb2 = { .offset = 3, .count = 3, }, |
|---|
| 1997 | + .hsic = { .offset = 6, .count = 1, }, |
|---|
| 1998 | + }, |
|---|
| 1999 | + .scale_ss_clock = false, |
|---|
| 2000 | + .has_ipfs = false, |
|---|
| 2001 | + .otg_reset_sspi = false, |
|---|
| 2002 | + .mbox = { |
|---|
| 2003 | + .cmd = 0xe4, |
|---|
| 2004 | + .data_in = 0xe8, |
|---|
| 2005 | + .data_out = 0xec, |
|---|
| 2006 | + .owner = 0xf0, |
|---|
| 2007 | + }, |
|---|
| 2008 | + .lpm_support = true, |
|---|
| 2009 | +}; |
|---|
| 2010 | + |
|---|
| 2011 | +static const char * const tegra194_supply_names[] = { |
|---|
| 2012 | +}; |
|---|
| 2013 | + |
|---|
| 2014 | +static const struct tegra_xusb_phy_type tegra194_phy_types[] = { |
|---|
| 2015 | + { .name = "usb3", .num = 4, }, |
|---|
| 2016 | + { .name = "usb2", .num = 4, }, |
|---|
| 2017 | +}; |
|---|
| 2018 | + |
|---|
| 2019 | +static const struct tegra_xusb_soc tegra194_soc = { |
|---|
| 2020 | + .firmware = "nvidia/tegra194/xusb.bin", |
|---|
| 2021 | + .supply_names = tegra194_supply_names, |
|---|
| 2022 | + .num_supplies = ARRAY_SIZE(tegra194_supply_names), |
|---|
| 2023 | + .phy_types = tegra194_phy_types, |
|---|
| 2024 | + .num_types = ARRAY_SIZE(tegra194_phy_types), |
|---|
| 2025 | + .context = &tegra186_xusb_context, |
|---|
| 2026 | + .ports = { |
|---|
| 2027 | + .usb3 = { .offset = 0, .count = 4, }, |
|---|
| 2028 | + .usb2 = { .offset = 4, .count = 4, }, |
|---|
| 2029 | + }, |
|---|
| 2030 | + .scale_ss_clock = false, |
|---|
| 2031 | + .has_ipfs = false, |
|---|
| 2032 | + .otg_reset_sspi = false, |
|---|
| 2033 | + .mbox = { |
|---|
| 2034 | + .cmd = 0x68, |
|---|
| 2035 | + .data_in = 0x6c, |
|---|
| 2036 | + .data_out = 0x70, |
|---|
| 2037 | + .owner = 0x74, |
|---|
| 2038 | + }, |
|---|
| 2039 | + .lpm_support = true, |
|---|
| 2040 | +}; |
|---|
| 2041 | +MODULE_FIRMWARE("nvidia/tegra194/xusb.bin"); |
|---|
| 1363 | 2042 | |
|---|
| 1364 | 2043 | static const struct of_device_id tegra_xusb_of_match[] = { |
|---|
| 1365 | 2044 | { .compatible = "nvidia,tegra124-xusb", .data = &tegra124_soc }, |
|---|
| 1366 | 2045 | { .compatible = "nvidia,tegra210-xusb", .data = &tegra210_soc }, |
|---|
| 2046 | + { .compatible = "nvidia,tegra186-xusb", .data = &tegra186_soc }, |
|---|
| 2047 | + { .compatible = "nvidia,tegra194-xusb", .data = &tegra194_soc }, |
|---|
| 1367 | 2048 | { }, |
|---|
| 1368 | 2049 | }; |
|---|
| 1369 | 2050 | MODULE_DEVICE_TABLE(of, tegra_xusb_of_match); |
|---|
| .. | .. |
|---|
| 1380 | 2061 | |
|---|
| 1381 | 2062 | static void tegra_xhci_quirks(struct device *dev, struct xhci_hcd *xhci) |
|---|
| 1382 | 2063 | { |
|---|
| 2064 | + struct tegra_xusb *tegra = dev_get_drvdata(dev); |
|---|
| 2065 | + |
|---|
| 1383 | 2066 | xhci->quirks |= XHCI_PLAT; |
|---|
| 2067 | + if (tegra && tegra->soc->lpm_support) |
|---|
| 2068 | + xhci->quirks |= XHCI_LPM_SUPPORT; |
|---|
| 1384 | 2069 | } |
|---|
| 1385 | 2070 | |
|---|
| 1386 | 2071 | static int tegra_xhci_setup(struct usb_hcd *hcd) |
|---|