dwc3-pci.c 6.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
/**
 * dwc3-pci.c - PCI Specific glue layer
 *
 * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
 *
 * Authors: Felipe Balbi <balbi@ti.com>,
 *	    Sebastian Andrzej Siewior <bigeasy@linutronix.de>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer,
 *    without modification.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The names of the above-listed copyright holders may not be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * ALTERNATIVELY, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") version 2, as published by the Free
 * Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <linux/kernel.h>
40
#include <linux/module.h>
41 42 43 44
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/platform_device.h>

45 46 47
#include <linux/usb/otg.h>
#include <linux/usb/nop-usb-xceiv.h>

48 49 50 51 52 53 54
/* FIXME define these in <linux/pci_ids.h> */
#define PCI_VENDOR_ID_SYNOPSYS		0x16c3
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3	0xabcd

struct dwc3_pci {
	struct device		*dev;
	struct platform_device	*dwc3;
55 56
	struct platform_device	*usb2_phy;
	struct platform_device	*usb3_phy;
57 58
};

B
Bill Pemberton 已提交
59
static int dwc3_pci_register_phys(struct dwc3_pci *glue)
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
{
	struct nop_usb_xceiv_platform_data pdata;
	struct platform_device	*pdev;
	int			ret;

	memset(&pdata, 0x00, sizeof(pdata));

	pdev = platform_device_alloc("nop_usb_xceiv", 0);
	if (!pdev)
		return -ENOMEM;

	glue->usb2_phy = pdev;
	pdata.type = USB_PHY_TYPE_USB2;

	ret = platform_device_add_data(glue->usb2_phy, &pdata, sizeof(pdata));
	if (ret)
		goto err1;

	pdev = platform_device_alloc("nop_usb_xceiv", 1);
	if (!pdev) {
		ret = -ENOMEM;
		goto err1;
	}

	glue->usb3_phy = pdev;
	pdata.type = USB_PHY_TYPE_USB3;

	ret = platform_device_add_data(glue->usb3_phy, &pdata, sizeof(pdata));
	if (ret)
		goto err2;

	ret = platform_device_add(glue->usb2_phy);
	if (ret)
		goto err2;

	ret = platform_device_add(glue->usb3_phy);
	if (ret)
		goto err3;

	return 0;

err3:
	platform_device_del(glue->usb2_phy);

err2:
	platform_device_put(glue->usb3_phy);

err1:
	platform_device_put(glue->usb2_phy);

	return ret;
}

B
Bill Pemberton 已提交
113
static int dwc3_pci_probe(struct pci_dev *pci,
114 115 116 117 118 119
		const struct pci_device_id *id)
{
	struct resource		res[2];
	struct platform_device	*dwc3;
	struct dwc3_pci		*glue;
	int			ret = -ENOMEM;
C
Chanho Park 已提交
120
	struct device		*dev = &pci->dev;
121

C
Chanho Park 已提交
122
	glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
123
	if (!glue) {
C
Chanho Park 已提交
124 125
		dev_err(dev, "not enough memory\n");
		return -ENOMEM;
126 127
	}

128
	glue->dev = dev;
129 130 131

	ret = pci_enable_device(pci);
	if (ret) {
C
Chanho Park 已提交
132 133
		dev_err(dev, "failed to enable pci device\n");
		return -ENODEV;
134 135 136 137 138
	}

	pci_set_power_state(pci, PCI_D0);
	pci_set_master(pci);

139 140 141 142 143 144
	ret = dwc3_pci_register_phys(glue);
	if (ret) {
		dev_err(dev, "couldn't register PHYs\n");
		return ret;
	}

145
	dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
146
	if (!dwc3) {
C
Chanho Park 已提交
147
		dev_err(dev, "couldn't allocate dwc3 device\n");
148
		ret = -ENOMEM;
C
Chanho Park 已提交
149
		goto err1;
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
	}

	memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));

	res[0].start	= pci_resource_start(pci, 0);
	res[0].end	= pci_resource_end(pci, 0);
	res[0].name	= "dwc_usb3";
	res[0].flags	= IORESOURCE_MEM;

	res[1].start	= pci->irq;
	res[1].name	= "dwc_usb3";
	res[1].flags	= IORESOURCE_IRQ;

	ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
	if (ret) {
C
Chanho Park 已提交
165
		dev_err(dev, "couldn't add resources to dwc3 device\n");
166
		goto err1;
167 168 169 170
	}

	pci_set_drvdata(pci, glue);

C
Chanho Park 已提交
171
	dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
172

C
Chanho Park 已提交
173 174 175
	dwc3->dev.dma_mask = dev->dma_mask;
	dwc3->dev.dma_parms = dev->dma_parms;
	dwc3->dev.parent = dev;
176
	glue->dwc3 = dwc3;
177 178 179

	ret = platform_device_add(dwc3);
	if (ret) {
C
Chanho Park 已提交
180 181
		dev_err(dev, "failed to register dwc3 device\n");
		goto err3;
182 183 184 185
	}

	return 0;

C
Chanho Park 已提交
186
err3:
187 188 189
	pci_set_drvdata(pci, NULL);
	platform_device_put(dwc3);
err1:
C
Chanho Park 已提交
190
	pci_disable_device(pci);
191 192 193 194

	return ret;
}

B
Bill Pemberton 已提交
195
static void dwc3_pci_remove(struct pci_dev *pci)
196 197 198
{
	struct dwc3_pci	*glue = pci_get_drvdata(pci);

199
	platform_device_unregister(glue->dwc3);
200 201
	platform_device_unregister(glue->usb2_phy);
	platform_device_unregister(glue->usb3_phy);
202 203 204 205 206 207 208 209 210 211 212 213 214
	pci_set_drvdata(pci, NULL);
	pci_disable_device(pci);
}

static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = {
	{
		PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
				PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3),
	},
	{  }	/* Terminating Entry */
};
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
#ifdef CONFIG_PM
static int dwc3_pci_suspend(struct device *dev)
{
	struct pci_dev	*pci = to_pci_dev(dev);

	pci_disable_device(pci);

	return 0;
}

static int dwc3_pci_resume(struct device *dev)
{
	struct pci_dev	*pci = to_pci_dev(dev);
	int		ret;

	ret = pci_enable_device(pci);
	if (ret) {
		dev_err(dev, "can't re-enable device --> %d\n", ret);
		return ret;
	}

	pci_set_master(pci);

	return 0;
}

static const struct dev_pm_ops dwc3_pci_dev_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_suspend, dwc3_pci_resume)
};

#define DEV_PM_OPS	(&dwc3_pci_dev_pm_ops)
#else
#define DEV_PM_OPS	NULL
#endif /* CONFIG_PM */

250
static struct pci_driver dwc3_pci_driver = {
251
	.name		= "dwc3-pci",
252 253
	.id_table	= dwc3_pci_id_table,
	.probe		= dwc3_pci_probe,
B
Bill Pemberton 已提交
254
	.remove		= dwc3_pci_remove,
255 256 257
	.driver		= {
		.pm	= DEV_PM_OPS,
	},
258 259 260 261 262 263
};

MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer");

G
Greg Kroah-Hartman 已提交
264
module_pci_driver(dwc3_pci_driver);