/* * PCIe endpoint skeleton driver for IMX6 SOCs * * Copyright (C) 2014 Freescale Semiconductor, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #define DRV_DESCRIPTION "i.MX PCIE endpoint device driver" #define DRV_VERSION "version 0.1" #define DRV_NAME "imx_pcie_ep" struct imx_pcie_ep_priv { struct pci_dev *pci_dev; void __iomem *hw_base; }; /** * imx_pcie_ep_probe - Device Initialization Routine * @pdev: PCI device information struct * @id: entry in id_tbl * * Returns 0 on success, negative on failure **/ static int imx_pcie_ep_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int ret = 0; unsigned int msi_addr = 0; struct device *dev = &pdev->dev; struct imx_pcie_ep_priv *priv; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(dev, "can't alloc imx pcie priv\n"); return -ENOMEM; } priv->pci_dev = pdev; if (pci_enable_device(pdev)) { ret = -ENODEV; goto out; } pci_set_master(pdev); pci_set_drvdata(pdev, priv); priv->hw_base = pci_iomap(pdev, 0, 0); if (!priv->hw_base) { ret = -ENODEV; goto out; } pr_info("pci_resource_len = 0x%08llx\n", (unsigned long long) pci_resource_len(pdev, 0)); pr_info("pci_resource_base = %p\n", priv->hw_base); ret = pci_enable_msi(priv->pci_dev); if (ret < 0) { dev_err(dev, "can't enable msi\n"); return ret; } /* * Force to use 0x01FF8000 on iMX6q SD board and 0x08FF8000 * on iMX6sx SDB board as the MSI address, to do the MSI demo */ if (*(unsigned int *)priv->hw_base > 0x4FFFFFFF) /* iMX6sx SDB board, DDR layout [0x8000_0000 ~ 0xBFFF_FFFF] */ msi_addr = 0x08FF8000; else /* iMX6q SD board, DDR layout [0x1000_0000 ~ 0x4FFF_FFFF] */ msi_addr = 0x01FF8000; pr_info("pci_msi_addr = 0x%08x\n", msi_addr); pci_bus_write_config_dword(pdev->bus, 0, 0x54, msi_addr); pci_bus_write_config_dword(pdev->bus->parent, 0, 0x820, msi_addr); /* configure rc's msi cap */ pci_bus_read_config_dword(pdev->bus->parent, 0, 0x50, &ret); ret |= (PCI_MSI_FLAGS_ENABLE << 16); pci_bus_write_config_dword(pdev->bus->parent, 0, 0x50, ret); pci_bus_write_config_dword(pdev->bus->parent, 0, 0x828, 0x1); pci_bus_write_config_dword(pdev->bus->parent, 0, 0x82C, 0xFFFFFFFE); return 0; out: return ret; } static void imx_pcie_ep_remove(struct pci_dev *pdev) { struct imx_pcie_ep_priv *priv = pci_get_drvdata(pdev); if (!priv) return; pr_info("***imx pcie ep driver unload***\n"); } static struct pci_device_id imx_pcie_ep_ids[] = { { .class = PCI_CLASS_MEMORY_RAM << 8, .class_mask = ~0, .vendor = 0xbeaf, .device = 0xdead, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, }, { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, imx_pcie_ep_ids); static struct pci_driver imx_pcie_ep_driver = { .name = DRV_NAME, .id_table = imx_pcie_ep_ids, .probe = imx_pcie_ep_probe, .remove = imx_pcie_ep_remove, }; static int __init imx_pcie_ep_init(void) { int ret; pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); ret = pci_register_driver(&imx_pcie_ep_driver); if (ret) pr_err("Unable to initialize PCI module\n"); return ret; } static void __exit imx_pcie_ep_exit(void) { pci_unregister_driver(&imx_pcie_ep_driver); } module_exit(imx_pcie_ep_exit); module_init(imx_pcie_ep_init); MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_VERSION(DRV_VERSION); MODULE_LICENSE("GPL"); MODULE_ALIAS("imx_pcie_ep");