|
@@ -0,0 +1,200 @@
|
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
|
+/*
|
|
|
+ * Phytium PCIE host driver
|
|
|
+ *
|
|
|
+ * Heavily based on drivers/pci/pcie_xilinx.c
|
|
|
+ *
|
|
|
+ * Copyright (C) 2019
|
|
|
+ */
|
|
|
+
|
|
|
+#include <common.h>
|
|
|
+#include <dm.h>
|
|
|
+#include <pci.h>
|
|
|
+#include <asm/io.h>
|
|
|
+
|
|
|
+/**
|
|
|
+ * struct phytium_pcie - phytium PCIe controller state
|
|
|
+ * @cfg_base: The base address of memory mapped configuration space
|
|
|
+ */
|
|
|
+struct phytium_pcie {
|
|
|
+ void *cfg_base;
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * phytium_pci_skip_dev()
|
|
|
+ * @parent: Identifies the PCIe device to access
|
|
|
+ *
|
|
|
+ * Checks whether the parent of the PCIe device is bridge
|
|
|
+ *
|
|
|
+ * Return: true if it is bridge, else false.
|
|
|
+ */
|
|
|
+static int phytium_pci_skip_dev(pci_dev_t parent)
|
|
|
+{
|
|
|
+ unsigned char pos, id;
|
|
|
+ unsigned long addr = 0x40000000;
|
|
|
+ unsigned short capreg;
|
|
|
+ unsigned char port_type;
|
|
|
+
|
|
|
+ addr += PCI_BUS(parent) << 20;
|
|
|
+ addr += PCI_DEV(parent) << 15;
|
|
|
+ addr += PCI_FUNC(parent) << 12;
|
|
|
+
|
|
|
+ pos = 0x34;
|
|
|
+ while (1) {
|
|
|
+ pos = readb(addr + pos);
|
|
|
+ if (pos < 0x40)
|
|
|
+ break;
|
|
|
+ pos &= ~3;
|
|
|
+ id = readb(addr + pos);
|
|
|
+ if (id == 0xff)
|
|
|
+ break;
|
|
|
+ if (id == 0x10) {
|
|
|
+ capreg = readw(addr + pos + 2);
|
|
|
+ port_type = (capreg >> 4) & 0xf;
|
|
|
+ if (port_type == 0x6 || port_type == 0x4)
|
|
|
+ return 1;
|
|
|
+ else
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ pos += 1;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pci_phytium_conf_address() - Calculate the address of a config access
|
|
|
+ * @bus: Pointer to the PCI bus
|
|
|
+ * @bdf: Identifies the PCIe device to access
|
|
|
+ * @offset: The offset into the device's configuration space
|
|
|
+ * @paddress: Pointer to the pointer to write the calculates address to
|
|
|
+ *
|
|
|
+ * Calculates the address that should be accessed to perform a PCIe
|
|
|
+ * configuration space access for a given device identified by the PCIe
|
|
|
+ * controller device @pcie and the bus, device & function numbers in @bdf. If
|
|
|
+ * access to the device is not valid then the function will return an error
|
|
|
+ * code. Otherwise the address to access will be written to the pointer pointed
|
|
|
+ * to by @paddress.
|
|
|
+ */
|
|
|
+static int pci_phytium_conf_address(struct udevice *bus, pci_dev_t bdf,
|
|
|
+ uint offset,
|
|
|
+ void **paddress)
|
|
|
+{
|
|
|
+ struct phytium_pcie *pcie = dev_get_priv(bus);
|
|
|
+ void *addr;
|
|
|
+ pci_dev_t bdf_parent;
|
|
|
+
|
|
|
+ unsigned int bus_no = PCI_BUS(bdf);
|
|
|
+ unsigned int dev_no = PCI_DEV(bdf);
|
|
|
+
|
|
|
+ bdf_parent = PCI_BDF((bus_no - 1), 0, 0);
|
|
|
+
|
|
|
+ addr = pcie->cfg_base;
|
|
|
+ addr += PCI_BUS(bdf) << 20;
|
|
|
+ addr += PCI_DEV(bdf) << 15;
|
|
|
+ addr += PCI_FUNC(bdf) << 12;
|
|
|
+
|
|
|
+ if (bus_no > 0 && dev_no > 0) {
|
|
|
+ if ((readb(addr + PCI_HEADER_TYPE) & 0x7f) !=
|
|
|
+ PCI_HEADER_TYPE_BRIDGE)
|
|
|
+ return -ENODEV;
|
|
|
+ if (phytium_pci_skip_dev(bdf_parent))
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+
|
|
|
+ addr += offset;
|
|
|
+ *paddress = addr;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pci_phytium_read_config() - Read from configuration space
|
|
|
+ * @bus: Pointer to the PCI bus
|
|
|
+ * @bdf: Identifies the PCIe device to access
|
|
|
+ * @offset: The offset into the device's configuration space
|
|
|
+ * @valuep: A pointer at which to store the read value
|
|
|
+ * @size: Indicates the size of access to perform
|
|
|
+ *
|
|
|
+ * Read a value of size @size from offset @offset within the configuration
|
|
|
+ * space of the device identified by the bus, device & function numbers in @bdf
|
|
|
+ * on the PCI bus @bus.
|
|
|
+ */
|
|
|
+static int pci_phytium_read_config(struct udevice *bus, pci_dev_t bdf,
|
|
|
+ uint offset, ulong *valuep,
|
|
|
+ enum pci_size_t size)
|
|
|
+{
|
|
|
+ return pci_generic_mmap_read_config(bus, pci_phytium_conf_address,
|
|
|
+ bdf, offset, valuep, size);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pci_phytium_write_config() - Write to configuration space
|
|
|
+ * @bus: Pointer to the PCI bus
|
|
|
+ * @bdf: Identifies the PCIe device to access
|
|
|
+ * @offset: The offset into the device's configuration space
|
|
|
+ * @value: The value to write
|
|
|
+ * @size: Indicates the size of access to perform
|
|
|
+ *
|
|
|
+ * Write the value @value of size @size from offset @offset within the
|
|
|
+ * configuration space of the device identified by the bus, device & function
|
|
|
+ * numbers in @bdf on the PCI bus @bus.
|
|
|
+ */
|
|
|
+static int pci_phytium_write_config(struct udevice *bus, pci_dev_t bdf,
|
|
|
+ uint offset, ulong value,
|
|
|
+ enum pci_size_t size)
|
|
|
+{
|
|
|
+ return pci_generic_mmap_write_config(bus, pci_phytium_conf_address,
|
|
|
+ bdf, offset, value, size);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pci_phytium_ofdata_to_platdata() - Translate from DT to device state
|
|
|
+ * @dev: A pointer to the device being operated on
|
|
|
+ *
|
|
|
+ * Translate relevant data from the device tree pertaining to device @dev into
|
|
|
+ * state that the driver will later make use of. This state is stored in the
|
|
|
+ * device's private data structure.
|
|
|
+ *
|
|
|
+ * Return: 0 on success, else -EINVAL
|
|
|
+ */
|
|
|
+static int pci_phytium_ofdata_to_platdata(struct udevice *dev)
|
|
|
+{
|
|
|
+ struct phytium_pcie *pcie = dev_get_priv(dev);
|
|
|
+ struct fdt_resource reg_res;
|
|
|
+
|
|
|
+ DECLARE_GLOBAL_DATA_PTR;
|
|
|
+
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), "reg",
|
|
|
+ 0, ®_res);
|
|
|
+ if (err < 0) {
|
|
|
+ pr_err("\"reg\" resource not found\n");
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ pcie->cfg_base = map_physmem(reg_res.start,
|
|
|
+ fdt_resource_size(®_res),
|
|
|
+ MAP_NOCACHE);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct dm_pci_ops pci_phytium_ops = {
|
|
|
+ .read_config = pci_phytium_read_config,
|
|
|
+ .write_config = pci_phytium_write_config,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct udevice_id pci_phytium_ids[] = {
|
|
|
+ { .compatible = "phytium,pcie-host-1.0" },
|
|
|
+ { }
|
|
|
+};
|
|
|
+
|
|
|
+U_BOOT_DRIVER(pci_phytium) = {
|
|
|
+ .name = "pci_phytium",
|
|
|
+ .id = UCLASS_PCI,
|
|
|
+ .of_match = pci_phytium_ids,
|
|
|
+ .ops = &pci_phytium_ops,
|
|
|
+ .ofdata_to_platdata = pci_phytium_ofdata_to_platdata,
|
|
|
+ .priv_auto_alloc_size = sizeof(struct phytium_pcie),
|
|
|
+};
|