Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2e036f0d authored by zhaochen's avatar zhaochen
Browse files

usb: xhci: Add support UPD720201K8 PCI to USB



The µPD720201 is a USB host controller LSI compatible with the USB 3.0
and xHCI (eXtensible Host Controller Interface) 1.0 specifications.The
system bus is compatible with the PCIe Gen2 specification.The controller
provides four USB ports with LS (low-Speed) / FS (full-Speed) /
HS (high-Speed) / SS (SuperSpeed) support. The µPD720201 comes in a
68-pin QFN package and has the following improvements over previous
products.

Change-Id: Ie77c2d6d57e88c91d493f5055bef15a7d6b537a8
Signed-off-by: default avatarzhaochen <zhaochen@codeaurora.org>
parent be918bf5
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -804,3 +804,9 @@ config USB_HCD_TEST_MODE
	  This option is of interest only to developers who need to validate
	  their USB hardware designs.  It is not needed for normal use.  If
	  unsure, say N.

config USB_UPD720X
	tristate "Support for Renesas UPD720X XHCI USB controller"
	---help---
	  Enables support for the USB Host controller present on the
	  Renesas UPD720X chipsets.
+1 −0
Original line number Diff line number Diff line
@@ -82,3 +82,4 @@ obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o
obj-$(CONFIG_USB_HCD_SSB)	+= ssb-hcd.o
obj-$(CONFIG_USB_FOTG210_HCD)	+= fotg210-hcd.o
obj-$(CONFIG_USB_MAX3421_HCD)	+= max3421-hcd.o
obj-$(CONFIG_USB_UPD720X)	+= xhci-fwdload.o
+6 −0
Original line number Diff line number Diff line
@@ -15,6 +15,11 @@ void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev);
void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev);
void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
void sb800_prefetch(struct device *dev, int on);
#ifdef CONFIG_USB_UPD720X
void upd720x_finish_download(struct pci_dev *pDev);
#else
#define upd720x_finish_download(pDev)
#endif
#else
struct pci_dev;
static inline void usb_amd_quirk_pll_disable(void) {}
@@ -23,6 +28,7 @@ static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {}
static inline void usb_amd_dev_put(void) {}
static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {}
static inline void sb800_prefetch(struct device *dev, int on) {}
#define upd720x_finish_download(pDev)
#endif  /* CONFIG_PCI */

#endif  /*  __LINUX_USB_PCI_QUIRKS_H  */
+228 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2019 The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#include <linux/errno.h>
#include <linux/time.h>
#include <linux/firmware.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <asm/dma-iommu.h>
#include <linux/msm_dma_iommu_mapping.h>

#define upd720x_firmware  "K2026090.mem"

#define UPD_PCI_F4  0xF4

#define UPD_PCI_F4_FWDOWNLOADENABLE  (0x0001)
#define UPD_PCI_F4_FWDOWNLOADLOCK    (0x0002)
#define UPD_PCI_F4_SETDATA0          (0x0100)
#define UPD_PCI_F4_SETDATA1          (0x0200)
#define UPD_PCI_F4_RESULT            (0x0070)

#define UPD_PCI_F8  0xF8

#define UPD_PCI_FC  0xFC

enum SET_DATA {
	SET_DATA_PAGE0,
	SET_DATA_PAGE1
};

#define SMMU_BASE 0x10000000
#define SMMU_SIZE 0x40000000

struct firmware *fw_pointer;
static struct dma_iommu_mapping *upd720x_smmu_init(struct device *dev)
{
	int rc = 0;
	int atomic_ctx = 1;
	int bypass_enable = 1;
	struct dma_iommu_mapping *mapping = NULL;

	mapping = arm_iommu_create_mapping(&platform_bus_type,
		SMMU_BASE, SMMU_SIZE);
	if (IS_ERR(mapping)) {
		rc = PTR_ERR(mapping);
		dev_err(dev, "create mapping failed, err = %d\n", rc);
		return NULL;
	}

	rc = iommu_domain_set_attr(mapping->domain,
		DOMAIN_ATTR_ATOMIC, &atomic_ctx);
	if (rc) {
		dev_err(dev, "Set atomic attribute to SMMU failed (%d)\n", rc);
		arm_iommu_release_mapping(mapping);
		return NULL;
	}

	rc = iommu_domain_set_attr(mapping->domain,
		DOMAIN_ATTR_S1_BYPASS, &bypass_enable);
	if (rc) {
		dev_err(dev, "Set bypass attribute to SMMU failed (%d)\n", rc);
		arm_iommu_release_mapping(mapping);
		return NULL;
	}

	rc = arm_iommu_attach_device(dev, mapping);
	if (rc) {
		dev_err(dev, "arm_iommu_attach_device failed (%d)\n", rc);
		arm_iommu_release_mapping(mapping);
		return NULL;
	}

	pr_info("attached to IOMMU\n");
	return mapping;
}

static void upd720x_smmu_deinit(struct device *dev)
{
	arm_iommu_detach_device(dev);
	arm_iommu_release_mapping(to_dma_iommu_mapping(dev));
}


static int upd720x_download_enable(struct pci_dev *pDev)
{
	unsigned int read_data;
	int result;

	result = pci_read_config_dword(pDev, UPD_PCI_F4, &read_data);
	pr_info("Set FW Download enable\n");
	result = pci_write_config_dword(pDev, UPD_PCI_F4, read_data |
		UPD_PCI_F4_FWDOWNLOADENABLE);
	return result;
}

static int upd720x_download_lock(struct pci_dev *pDev)
{
	unsigned int read_data;
	int result;

	result = pci_read_config_dword(pDev, UPD_PCI_F4, &read_data);
	pr_info("Set FW Download lock\n");
	result = pci_write_config_dword(pDev, UPD_PCI_F4, read_data |
		UPD_PCI_F4_FWDOWNLOADLOCK);
	return result;
}

static int upd720x_set_data0(struct pci_dev *pDev)
{
	unsigned int read_data;
	int result;

	result = pci_read_config_dword(pDev, UPD_PCI_F4, &read_data);
	result = pci_write_config_dword(pDev, UPD_PCI_F4,
		(read_data & ~UPD_PCI_F4_SETDATA1) |
		UPD_PCI_F4_SETDATA0);

	return result;
}

static int upd720x_set_data1(struct pci_dev *pDev)
{
	unsigned int read_data;
	int result;

	result = pci_read_config_dword(pDev, UPD_PCI_F4, &read_data);
	result = pci_write_config_dword(pDev, UPD_PCI_F4,
		(read_data & ~UPD_PCI_F4_SETDATA0) |
		UPD_PCI_F4_SETDATA1);

	return result;
}

static int upd720x_download_clearcontrol(struct pci_dev *pDev)
{
	int read_buf;
	int rc;

	rc = pci_read_config_dword(pDev, UPD_PCI_F4, &read_buf);
	if (rc == 0) {
		rc = pci_write_config_dword(pDev, UPD_PCI_F4, read_buf &
		~UPD_PCI_F4_FWDOWNLOADENABLE);
	}
	return rc;
}
int upd720x_firmware_download(struct pci_dev  *pDev,
	unsigned char *pFWImage, unsigned int firmware_size)
{
	enum SET_DATA page = SET_DATA_PAGE0;
	int offset;
	unsigned int *image = (unsigned int *)pFWImage;
	unsigned int fw_dwordsize   = firmware_size /
		(sizeof(unsigned int) / sizeof(unsigned char));

	if ((firmware_size %
	(sizeof(unsigned int) / sizeof(unsigned char))) != 0)
		fw_dwordsize++;

	if (upd720x_download_enable(pDev) == -EFAULT) {
		pr_info("Set FW Download Enable is timeout");
		return -EFAULT;
	}

	for (offset = 0; offset < fw_dwordsize; offset++) {
		switch (page) {
		case SET_DATA_PAGE0:
			pci_write_config_dword(pDev, UPD_PCI_F8, image[offset]);

			if (upd720x_set_data0(pDev) == -EFAULT)
				return -EFAULT;
			page = SET_DATA_PAGE1;
			break;

		case SET_DATA_PAGE1:
			pci_write_config_dword(pDev, UPD_PCI_FC, image[offset]);
			if (upd720x_set_data1(pDev) == -EFAULT)
				return -EFAULT;
			page = SET_DATA_PAGE0;
			break;
		default:
			break;
		}
	}

	if (upd720x_download_clearcontrol(pDev) == -EFAULT)
		return -EFAULT;

	if (upd720x_download_lock(pDev) == -EFAULT)
		return -EFAULT;

	return 0;
}

int upd720x_finish_download(struct pci_dev *pDev)
{
	int result = -EFAULT;
	char *firmwarename = upd720x_firmware;
	int lc;
	int ret;

	if (!upd720x_smmu_init(&pDev->dev))
		return -EFAULT;

	for (lc = 0; (lc < 2) && (fw_pointer == NULL); lc++) {
		ret = request_firmware((const struct firmware **)&fw_pointer,
			firmwarename, &pDev->bus->dev);
		if (ret == 0) {
			result = upd720x_firmware_download(pDev,
				(unsigned char *)fw_pointer->data,
				fw_pointer->size);
			break;
		}
		result = ret;
	}
	return result;
}


+3 −1
Original line number Diff line number Diff line
@@ -192,8 +192,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
		xhci->quirks |= XHCI_BROKEN_STREAMS;
	}
	if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
			pdev->device == 0x0014)
			pdev->device == 0x0014) {
		xhci->quirks |= XHCI_TRUST_TX_LENGTH;
		upd720x_finish_download(pdev);
	}
	if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
			pdev->device == 0x0015)
		xhci->quirks |= XHCI_RESET_ON_RESUME;