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
	}

	pci_set_master(pci);

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

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

	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 已提交
164
		dev_err(dev, "couldn't add resources to dwc3 device\n");
165
		goto err1;
166 167 168 169
	}

	pci_set_drvdata(pci, glue);

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

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

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

	return 0;

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

	return ret;
}

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

198
	platform_device_unregister(glue->dwc3);
199 200
	platform_device_unregister(glue->usb2_phy);
	platform_device_unregister(glue->usb3_phy);
201 202 203 204 205 206 207 208 209 210 211 212 213
	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);

214 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
#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 */

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

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

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