xref: /kernel/linux/linux-5.10/drivers/dma/dw/pci.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PCI driver for the Synopsys DesignWare DMA Controller
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Intel Corporation
68c2ecf20Sopenharmony_ci * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/pci.h>
118c2ecf20Sopenharmony_ci#include <linux/device.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "internal.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic int dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
168c2ecf20Sopenharmony_ci{
178c2ecf20Sopenharmony_ci	const struct dw_dma_chip_pdata *drv_data = (void *)pid->driver_data;
188c2ecf20Sopenharmony_ci	struct dw_dma_chip_pdata *data;
198c2ecf20Sopenharmony_ci	struct dw_dma_chip *chip;
208c2ecf20Sopenharmony_ci	int ret;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	ret = pcim_enable_device(pdev);
238c2ecf20Sopenharmony_ci	if (ret)
248c2ecf20Sopenharmony_ci		return ret;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	ret = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
278c2ecf20Sopenharmony_ci	if (ret) {
288c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "I/O memory remapping failed\n");
298c2ecf20Sopenharmony_ci		return ret;
308c2ecf20Sopenharmony_ci	}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	pci_set_master(pdev);
338c2ecf20Sopenharmony_ci	pci_try_set_mwi(pdev);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
368c2ecf20Sopenharmony_ci	if (ret)
378c2ecf20Sopenharmony_ci		return ret;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
408c2ecf20Sopenharmony_ci	if (ret)
418c2ecf20Sopenharmony_ci		return ret;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	data = devm_kmemdup(&pdev->dev, drv_data, sizeof(*drv_data), GFP_KERNEL);
448c2ecf20Sopenharmony_ci	if (!data)
458c2ecf20Sopenharmony_ci		return -ENOMEM;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
488c2ecf20Sopenharmony_ci	if (!chip)
498c2ecf20Sopenharmony_ci		return -ENOMEM;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	chip->dev = &pdev->dev;
528c2ecf20Sopenharmony_ci	chip->id = pdev->devfn;
538c2ecf20Sopenharmony_ci	chip->regs = pcim_iomap_table(pdev)[0];
548c2ecf20Sopenharmony_ci	chip->irq = pdev->irq;
558c2ecf20Sopenharmony_ci	chip->pdata = data->pdata;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	data->chip = chip;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	ret = data->probe(chip);
608c2ecf20Sopenharmony_ci	if (ret)
618c2ecf20Sopenharmony_ci		return ret;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	dw_dma_acpi_controller_register(chip->dw);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, data);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	return 0;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic void dw_pci_remove(struct pci_dev *pdev)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	struct dw_dma_chip_pdata *data = pci_get_drvdata(pdev);
738c2ecf20Sopenharmony_ci	struct dw_dma_chip *chip = data->chip;
748c2ecf20Sopenharmony_ci	int ret;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	dw_dma_acpi_controller_free(chip->dw);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	ret = data->remove(chip);
798c2ecf20Sopenharmony_ci	if (ret)
808c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "can't remove device properly: %d\n", ret);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int dw_pci_suspend_late(struct device *dev)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct dw_dma_chip_pdata *data = dev_get_drvdata(dev);
888c2ecf20Sopenharmony_ci	struct dw_dma_chip *chip = data->chip;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	return do_dw_dma_disable(chip);
918c2ecf20Sopenharmony_ci};
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int dw_pci_resume_early(struct device *dev)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct dw_dma_chip_pdata *data = dev_get_drvdata(dev);
968c2ecf20Sopenharmony_ci	struct dw_dma_chip *chip = data->chip;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return do_dw_dma_enable(chip);
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic const struct dev_pm_ops dw_pci_dev_pm_ops = {
1048c2ecf20Sopenharmony_ci	SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_pci_suspend_late, dw_pci_resume_early)
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic const struct pci_device_id dw_pci_id_table[] = {
1088c2ecf20Sopenharmony_ci	/* Medfield (GPDMA) */
1098c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x0827), (kernel_ulong_t)&dw_dma_chip_pdata },
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* BayTrail */
1128c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x0f06), (kernel_ulong_t)&dw_dma_chip_pdata },
1138c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x0f40), (kernel_ulong_t)&dw_dma_chip_pdata },
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* Merrifield */
1168c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x11a2), (kernel_ulong_t)&idma32_chip_pdata },
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* Braswell */
1198c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x2286), (kernel_ulong_t)&dw_dma_chip_pdata },
1208c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x22c0), (kernel_ulong_t)&dw_dma_chip_pdata },
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/* Elkhart Lake iDMA 32-bit (PSE DMA) */
1238c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x4bb4), (kernel_ulong_t)&idma32_chip_pdata },
1248c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x4bb5), (kernel_ulong_t)&idma32_chip_pdata },
1258c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x4bb6), (kernel_ulong_t)&idma32_chip_pdata },
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* Haswell */
1288c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x9c60), (kernel_ulong_t)&dw_dma_chip_pdata },
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* Broadwell */
1318c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(INTEL, 0x9ce0), (kernel_ulong_t)&dw_dma_chip_pdata },
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	{ }
1348c2ecf20Sopenharmony_ci};
1358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, dw_pci_id_table);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic struct pci_driver dw_pci_driver = {
1388c2ecf20Sopenharmony_ci	.name		= "dw_dmac_pci",
1398c2ecf20Sopenharmony_ci	.id_table	= dw_pci_id_table,
1408c2ecf20Sopenharmony_ci	.probe		= dw_pci_probe,
1418c2ecf20Sopenharmony_ci	.remove		= dw_pci_remove,
1428c2ecf20Sopenharmony_ci	.driver	= {
1438c2ecf20Sopenharmony_ci		.pm	= &dw_pci_dev_pm_ops,
1448c2ecf20Sopenharmony_ci	},
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cimodule_pci_driver(dw_pci_driver);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Synopsys DesignWare DMA Controller PCI driver");
1518c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
152