18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * bdc_pci.c - BRCM BDC USB3.0 device controller PCI interface file.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Broadcom Corporation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Ashwini Pahuja
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Based on drivers under drivers/usb/
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/pci.h>
168c2ecf20Sopenharmony_ci#include <linux/pci_ids.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "bdc.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define BDC_PCI_PID 0x1570
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistruct bdc_pci {
248c2ecf20Sopenharmony_ci	struct device *dev;
258c2ecf20Sopenharmony_ci	struct platform_device *bdc;
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic int bdc_setup_msi(struct pci_dev *pci)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	int ret;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	ret = pci_enable_msi(pci);
338c2ecf20Sopenharmony_ci	if (ret) {
348c2ecf20Sopenharmony_ci		pr_err("failed to allocate MSI entry\n");
358c2ecf20Sopenharmony_ci		return ret;
368c2ecf20Sopenharmony_ci	}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	return ret;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int bdc_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct resource res[2];
448c2ecf20Sopenharmony_ci	struct platform_device *bdc;
458c2ecf20Sopenharmony_ci	struct bdc_pci *glue;
468c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	glue = devm_kzalloc(&pci->dev, sizeof(*glue), GFP_KERNEL);
498c2ecf20Sopenharmony_ci	if (!glue)
508c2ecf20Sopenharmony_ci		return -ENOMEM;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	glue->dev = &pci->dev;
538c2ecf20Sopenharmony_ci	ret = pci_enable_device(pci);
548c2ecf20Sopenharmony_ci	if (ret) {
558c2ecf20Sopenharmony_ci		dev_err(&pci->dev, "failed to enable pci device\n");
568c2ecf20Sopenharmony_ci		return -ENODEV;
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci	pci_set_master(pci);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	bdc = platform_device_alloc(BRCM_BDC_NAME, PLATFORM_DEVID_AUTO);
618c2ecf20Sopenharmony_ci	if (!bdc)
628c2ecf20Sopenharmony_ci		return -ENOMEM;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
658c2ecf20Sopenharmony_ci	bdc_setup_msi(pci);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	res[0].start	= pci_resource_start(pci, 0);
688c2ecf20Sopenharmony_ci	res[0].end	= pci_resource_end(pci, 0);
698c2ecf20Sopenharmony_ci	res[0].name	= BRCM_BDC_NAME;
708c2ecf20Sopenharmony_ci	res[0].flags	= IORESOURCE_MEM;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	res[1].start	= pci->irq;
738c2ecf20Sopenharmony_ci	res[1].name	= BRCM_BDC_NAME;
748c2ecf20Sopenharmony_ci	res[1].flags	= IORESOURCE_IRQ;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	ret = platform_device_add_resources(bdc, res, ARRAY_SIZE(res));
778c2ecf20Sopenharmony_ci	if (ret) {
788c2ecf20Sopenharmony_ci		dev_err(&pci->dev,
798c2ecf20Sopenharmony_ci			"couldn't add resources to bdc device\n");
808c2ecf20Sopenharmony_ci		platform_device_put(bdc);
818c2ecf20Sopenharmony_ci		return ret;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	pci_set_drvdata(pci, glue);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	dma_set_coherent_mask(&bdc->dev, pci->dev.coherent_dma_mask);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	bdc->dev.dma_mask = pci->dev.dma_mask;
898c2ecf20Sopenharmony_ci	bdc->dev.dma_parms = pci->dev.dma_parms;
908c2ecf20Sopenharmony_ci	bdc->dev.parent = &pci->dev;
918c2ecf20Sopenharmony_ci	glue->bdc = bdc;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	ret = platform_device_add(bdc);
948c2ecf20Sopenharmony_ci	if (ret) {
958c2ecf20Sopenharmony_ci		dev_err(&pci->dev, "failed to register bdc device\n");
968c2ecf20Sopenharmony_ci		platform_device_put(bdc);
978c2ecf20Sopenharmony_ci		return ret;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return 0;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void bdc_pci_remove(struct pci_dev *pci)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct bdc_pci *glue = pci_get_drvdata(pci);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	platform_device_unregister(glue->bdc);
1088c2ecf20Sopenharmony_ci	pci_disable_msi(pci);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic struct pci_device_id bdc_pci_id_table[] = {
1128c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, BDC_PCI_PID), },
1138c2ecf20Sopenharmony_ci	{} /* Terminating Entry */
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, bdc_pci_id_table);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic struct pci_driver bdc_pci_driver = {
1198c2ecf20Sopenharmony_ci	.name = "bdc-pci",
1208c2ecf20Sopenharmony_ci	.id_table = bdc_pci_id_table,
1218c2ecf20Sopenharmony_ci	.probe = bdc_pci_probe,
1228c2ecf20Sopenharmony_ci	.remove = bdc_pci_remove,
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ashwini Pahuja <ashwini.linux@gmail.com>");
1268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("BRCM BDC USB3 PCI Glue layer");
1288c2ecf20Sopenharmony_cimodule_pci_driver(bdc_pci_driver);
129