18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ci_hdrc_pci.c - MIPS USB IP core family device controller
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: David Lopo
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/pci.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/usb/gadget.h>
158c2ecf20Sopenharmony_ci#include <linux/usb/chipidea.h>
168c2ecf20Sopenharmony_ci#include <linux/usb/usb_phy_generic.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/* driver name */
198c2ecf20Sopenharmony_ci#define UDC_DRIVER_NAME   "ci_hdrc_pci"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistruct ci_hdrc_pci {
228c2ecf20Sopenharmony_ci	struct platform_device	*ci;
238c2ecf20Sopenharmony_ci	struct platform_device	*phy;
248c2ecf20Sopenharmony_ci};
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/******************************************************************************
278c2ecf20Sopenharmony_ci * PCI block
288c2ecf20Sopenharmony_ci *****************************************************************************/
298c2ecf20Sopenharmony_cistatic struct ci_hdrc_platform_data pci_platdata = {
308c2ecf20Sopenharmony_ci	.name		= UDC_DRIVER_NAME,
318c2ecf20Sopenharmony_ci	.capoffset	= DEF_CAPOFFSET,
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic struct ci_hdrc_platform_data langwell_pci_platdata = {
358c2ecf20Sopenharmony_ci	.name		= UDC_DRIVER_NAME,
368c2ecf20Sopenharmony_ci	.capoffset	= 0,
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic struct ci_hdrc_platform_data penwell_pci_platdata = {
408c2ecf20Sopenharmony_ci	.name		= UDC_DRIVER_NAME,
418c2ecf20Sopenharmony_ci	.capoffset	= 0,
428c2ecf20Sopenharmony_ci	.power_budget	= 200,
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/**
468c2ecf20Sopenharmony_ci * ci_hdrc_pci_probe: PCI probe
478c2ecf20Sopenharmony_ci * @pdev: USB device controller being probed
488c2ecf20Sopenharmony_ci * @id:   PCI hotplug ID connecting controller to UDC framework
498c2ecf20Sopenharmony_ci *
508c2ecf20Sopenharmony_ci * This function returns an error code
518c2ecf20Sopenharmony_ci * Allocates basic PCI resources for this USB device controller, and then
528c2ecf20Sopenharmony_ci * invokes the udc_probe() method to start the UDC associated with it
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_cistatic int ci_hdrc_pci_probe(struct pci_dev *pdev,
558c2ecf20Sopenharmony_ci				       const struct pci_device_id *id)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	struct ci_hdrc_platform_data *platdata = (void *)id->driver_data;
588c2ecf20Sopenharmony_ci	struct ci_hdrc_pci *ci;
598c2ecf20Sopenharmony_ci	struct resource res[3];
608c2ecf20Sopenharmony_ci	int retval = 0, nres = 2;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (!platdata) {
638c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "device doesn't provide driver data\n");
648c2ecf20Sopenharmony_ci		return -ENODEV;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
688c2ecf20Sopenharmony_ci	if (!ci)
698c2ecf20Sopenharmony_ci		return -ENOMEM;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	retval = pcim_enable_device(pdev);
728c2ecf20Sopenharmony_ci	if (retval)
738c2ecf20Sopenharmony_ci		return retval;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (!pdev->irq) {
768c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "No IRQ, check BIOS/PCI setup!");
778c2ecf20Sopenharmony_ci		return -ENODEV;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	pci_set_master(pdev);
818c2ecf20Sopenharmony_ci	pci_try_set_mwi(pdev);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/* register a nop PHY */
848c2ecf20Sopenharmony_ci	ci->phy = usb_phy_generic_register();
858c2ecf20Sopenharmony_ci	if (IS_ERR(ci->phy))
868c2ecf20Sopenharmony_ci		return PTR_ERR(ci->phy);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	memset(res, 0, sizeof(res));
898c2ecf20Sopenharmony_ci	res[0].start	= pci_resource_start(pdev, 0);
908c2ecf20Sopenharmony_ci	res[0].end	= pci_resource_end(pdev, 0);
918c2ecf20Sopenharmony_ci	res[0].flags	= IORESOURCE_MEM;
928c2ecf20Sopenharmony_ci	res[1].start	= pdev->irq;
938c2ecf20Sopenharmony_ci	res[1].flags	= IORESOURCE_IRQ;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	ci->ci = ci_hdrc_add_device(&pdev->dev, res, nres, platdata);
968c2ecf20Sopenharmony_ci	if (IS_ERR(ci->ci)) {
978c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
988c2ecf20Sopenharmony_ci		usb_phy_generic_unregister(ci->phy);
998c2ecf20Sopenharmony_ci		return PTR_ERR(ci->ci);
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, ci);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return 0;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/**
1088c2ecf20Sopenharmony_ci * ci_hdrc_pci_remove: PCI remove
1098c2ecf20Sopenharmony_ci * @pdev: USB Device Controller being removed
1108c2ecf20Sopenharmony_ci *
1118c2ecf20Sopenharmony_ci * Reverses the effect of ci_hdrc_pci_probe(),
1128c2ecf20Sopenharmony_ci * first invoking the udc_remove() and then releases
1138c2ecf20Sopenharmony_ci * all PCI resources allocated for this USB device controller
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_cistatic void ci_hdrc_pci_remove(struct pci_dev *pdev)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct ci_hdrc_pci *ci = pci_get_drvdata(pdev);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	ci_hdrc_remove_device(ci->ci);
1208c2ecf20Sopenharmony_ci	usb_phy_generic_unregister(ci->phy);
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/*
1248c2ecf20Sopenharmony_ci * PCI device table
1258c2ecf20Sopenharmony_ci * PCI device structure
1268c2ecf20Sopenharmony_ci *
1278c2ecf20Sopenharmony_ci * Check "pci.h" for details
1288c2ecf20Sopenharmony_ci *
1298c2ecf20Sopenharmony_ci * Note: ehci-pci driver may try to probe the device first. You have to add an
1308c2ecf20Sopenharmony_ci * ID to the bypass_pci_id_table in ehci-pci driver to prevent this.
1318c2ecf20Sopenharmony_ci */
1328c2ecf20Sopenharmony_cistatic const struct pci_device_id ci_hdrc_pci_id_table[] = {
1338c2ecf20Sopenharmony_ci	{
1348c2ecf20Sopenharmony_ci		PCI_DEVICE(0x153F, 0x1004),
1358c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t)&pci_platdata,
1368c2ecf20Sopenharmony_ci	},
1378c2ecf20Sopenharmony_ci	{
1388c2ecf20Sopenharmony_ci		PCI_DEVICE(0x153F, 0x1006),
1398c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t)&pci_platdata,
1408c2ecf20Sopenharmony_ci	},
1418c2ecf20Sopenharmony_ci	{
1428c2ecf20Sopenharmony_ci		PCI_VDEVICE(INTEL, 0x0811),
1438c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t)&langwell_pci_platdata,
1448c2ecf20Sopenharmony_ci	},
1458c2ecf20Sopenharmony_ci	{
1468c2ecf20Sopenharmony_ci		PCI_VDEVICE(INTEL, 0x0829),
1478c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t)&penwell_pci_platdata,
1488c2ecf20Sopenharmony_ci	},
1498c2ecf20Sopenharmony_ci	{
1508c2ecf20Sopenharmony_ci		/* Intel Clovertrail */
1518c2ecf20Sopenharmony_ci		PCI_VDEVICE(INTEL, 0xe006),
1528c2ecf20Sopenharmony_ci		.driver_data = (kernel_ulong_t)&penwell_pci_platdata,
1538c2ecf20Sopenharmony_ci	},
1548c2ecf20Sopenharmony_ci	{ 0 } /* end: all zeroes */
1558c2ecf20Sopenharmony_ci};
1568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ci_hdrc_pci_id_table);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic struct pci_driver ci_hdrc_pci_driver = {
1598c2ecf20Sopenharmony_ci	.name         =	UDC_DRIVER_NAME,
1608c2ecf20Sopenharmony_ci	.id_table     =	ci_hdrc_pci_id_table,
1618c2ecf20Sopenharmony_ci	.probe        =	ci_hdrc_pci_probe,
1628c2ecf20Sopenharmony_ci	.remove       =	ci_hdrc_pci_remove,
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cimodule_pci_driver(ci_hdrc_pci_driver);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ciMODULE_AUTHOR("MIPS - David Lopo <dlopo@chipidea.mips.com>");
1688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller");
1698c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1708c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ci13xxx_pci");
171