1/*
2 * bdc_pci.c - BRCM BDC USB3.0 device controller PCI interface file.
3 *
4 * Copyright (C) 2014 Broadcom Corporation
5 *
6 * Author: Ashwini Pahuja
7 *
8 * Based on drivers under drivers/usb/
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version.
14 *
15 */
16
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/slab.h>
20#include <linux/pci.h>
21#include <linux/pci_ids.h>
22#include <linux/platform_device.h>
23
24#include "bdc.h"
25
26#define BDC_PCI_PID 0x1570
27
28struct bdc_pci {
29	struct device *dev;
30	struct platform_device *bdc;
31};
32
33static int bdc_setup_msi(struct pci_dev *pci)
34{
35	int ret;
36
37	ret = pci_enable_msi(pci);
38	if (ret) {
39		pr_err("failed to allocate MSI entry\n");
40		return ret;
41	}
42
43	return ret;
44}
45
46static int bdc_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
47{
48	struct resource res[2];
49	struct platform_device *bdc;
50	struct bdc_pci *glue;
51	int ret = -ENOMEM;
52
53	glue = devm_kzalloc(&pci->dev, sizeof(*glue), GFP_KERNEL);
54	if (!glue)
55		return -ENOMEM;
56
57	glue->dev = &pci->dev;
58	ret = pci_enable_device(pci);
59	if (ret) {
60		dev_err(&pci->dev, "failed to enable pci device\n");
61		return -ENODEV;
62	}
63	pci_set_master(pci);
64
65	bdc = platform_device_alloc(BRCM_BDC_NAME, PLATFORM_DEVID_AUTO);
66	if (!bdc)
67		return -ENOMEM;
68
69	memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
70	bdc_setup_msi(pci);
71
72	res[0].start	= pci_resource_start(pci, 0);
73	res[0].end	= pci_resource_end(pci, 0);
74	res[0].name	= BRCM_BDC_NAME;
75	res[0].flags	= IORESOURCE_MEM;
76
77	res[1].start	= pci->irq;
78	res[1].name	= BRCM_BDC_NAME;
79	res[1].flags	= IORESOURCE_IRQ;
80
81	ret = platform_device_add_resources(bdc, res, ARRAY_SIZE(res));
82	if (ret) {
83		dev_err(&pci->dev,
84			"couldn't add resources to bdc device\n");
85		return ret;
86	}
87
88	pci_set_drvdata(pci, glue);
89
90	dma_set_coherent_mask(&bdc->dev, pci->dev.coherent_dma_mask);
91
92	bdc->dev.dma_mask = pci->dev.dma_mask;
93	bdc->dev.dma_parms = pci->dev.dma_parms;
94	bdc->dev.parent = &pci->dev;
95	glue->bdc = bdc;
96
97	ret = platform_device_add(bdc);
98	if (ret) {
99		dev_err(&pci->dev, "failed to register bdc device\n");
100		platform_device_put(bdc);
101		return ret;
102	}
103
104	return 0;
105}
106
107static void bdc_pci_remove(struct pci_dev *pci)
108{
109	struct bdc_pci *glue = pci_get_drvdata(pci);
110
111	platform_device_unregister(glue->bdc);
112	pci_disable_msi(pci);
113}
114
115static struct pci_device_id bdc_pci_id_table[] = {
116	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, BDC_PCI_PID), },
117	{} /* Terminating Entry */
118};
119
120MODULE_DEVICE_TABLE(pci, bdc_pci_id_table);
121
122static struct pci_driver bdc_pci_driver = {
123	.name = "bdc-pci",
124	.id_table = bdc_pci_id_table,
125	.probe = bdc_pci_probe,
126	.remove = bdc_pci_remove,
127};
128
129MODULE_AUTHOR("Ashwini Pahuja <ashwini.linux@gmail.com>");
130MODULE_LICENSE("GPL");
131MODULE_DESCRIPTION("BRCM BDC USB3 PCI Glue layer");
132module_pci_driver(bdc_pci_driver);
133