123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609 |
- /*
- * Copyright (c) 2006-2022, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2022-10-24 GuEe-GUI first version
- */
- #include <rthw.h>
- #include <rtthread.h>
- #define DBG_TAG "pci.ofw"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- #include <drivers/pci.h>
- #include <drivers/ofw.h>
- #include <drivers/ofw_io.h>
- #include <drivers/ofw_irq.h>
- #include <drivers/ofw_fdt.h>
- static rt_err_t pci_ofw_irq_parse(struct rt_pci_device *pdev, struct rt_ofw_cell_args *out_irq)
- {
- rt_err_t err = RT_EOK;
- rt_uint8_t pin;
- fdt32_t map_addr[4];
- struct rt_pci_device *p2pdev;
- struct rt_ofw_node *dev_np, *p2pnode = RT_NULL;
- /* Parse device tree if dev have a device node */
- dev_np = pdev->parent.ofw_node;
- if (dev_np)
- {
- err = rt_ofw_parse_irq_cells(dev_np, 0, out_irq);
- if (err)
- {
- return err;
- }
- }
- /* Assume #interrupt-cells is 1 */
- if ((err = rt_pci_read_config_u8(pdev, PCIR_INTPIN, &pin)))
- {
- goto _err;
- }
- /* No pin, exit with no error message. */
- if (pin == 0)
- {
- return -RT_ENOSYS;
- }
- /* Try local interrupt-map in the device node */
- if (rt_ofw_prop_read_raw(dev_np, "interrupt-map", RT_NULL))
- {
- pin = rt_pci_irq_intx(pdev, pin);
- p2pnode = dev_np;
- }
- /* Walk up the PCI tree */
- while (!p2pnode)
- {
- p2pdev = pdev->bus->self;
- /* Is the root bus -> host bridge */
- if (rt_pci_is_root_bus(pdev->bus))
- {
- struct rt_pci_host_bridge *host_bridge = pdev->bus->host_bridge;
- p2pnode = host_bridge->parent.ofw_node;
- if (!p2pnode)
- {
- err = -RT_EINVAL;
- goto _err;
- }
- }
- else
- {
- /* Is P2P bridge */
- p2pnode = p2pdev->parent.ofw_node;
- }
- if (p2pnode)
- {
- break;
- }
- /* Try get INTx in P2P */
- pin = rt_pci_irq_intx(pdev, pin);
- pdev = p2pdev;
- }
- /* For more format detail, please read `components/drivers/ofw/irq.c:ofw_parse_irq_map` */
- out_irq->data = map_addr;
- out_irq->args_count = 2;
- out_irq->args[0] = 3;
- out_irq->args[1] = 1;
- /* In addr cells */
- map_addr[0] = cpu_to_fdt32((pdev->bus->number << 16) | (pdev->devfn << 8));
- map_addr[1] = cpu_to_fdt32(0);
- map_addr[2] = cpu_to_fdt32(0);
- /* In pin cells */
- map_addr[3] = cpu_to_fdt32(pin);
- err = rt_ofw_parse_irq_map(p2pnode, out_irq);
- _err:
- if (err == -RT_EEMPTY)
- {
- LOG_W("PCI-Device<%s> no interrupt-map found, INTx interrupts not available",
- rt_dm_dev_get_name(&pdev->parent));
- LOG_W("PCI-Device<%s> possibly some PCI slots don't have level triggered interrupts capability",
- rt_dm_dev_get_name(&pdev->parent));
- }
- else if (err && err != -RT_ENOSYS)
- {
- LOG_E("PCI-Device<%s> irq parse failed with err = %s",
- rt_dm_dev_get_name(&pdev->parent), rt_strerror(err));
- }
- return err;
- }
- int rt_pci_ofw_irq_parse_and_map(struct rt_pci_device *pdev,
- rt_uint8_t slot, rt_uint8_t pin)
- {
- int irq = -1;
- rt_err_t status;
- struct rt_ofw_cell_args irq_args;
- if (!pdev)
- {
- goto _end;
- }
- status = pci_ofw_irq_parse(pdev, &irq_args);
- if (status)
- {
- goto _end;
- }
- irq = rt_ofw_map_irq(&irq_args);
- if (irq >= 0)
- {
- pdev->intx_pic = rt_pic_dynamic_cast(rt_ofw_data(irq_args.data));
- }
- _end:
- return irq;
- }
- static rt_err_t pci_ofw_parse_ranges(struct rt_ofw_node *dev_np, const char *propname,
- int phy_addr_cells, int phy_size_cells, int cpu_addr_cells,
- struct rt_pci_bus_region **out_regions, rt_size_t *out_regions_nr)
- {
- const fdt32_t *cell;
- rt_ssize_t total_cells;
- int groups, space_code;
- rt_uint32_t phy_addr[3];
- rt_uint64_t cpu_addr, phy_addr_size;
- *out_regions = RT_NULL;
- *out_regions_nr = 0;
- cell = rt_ofw_prop_read_raw(dev_np, propname, &total_cells);
- if (!cell)
- {
- return -RT_EEMPTY;
- }
- groups = total_cells / sizeof(*cell) / (phy_addr_cells + phy_size_cells + cpu_addr_cells);
- *out_regions = rt_malloc(groups * sizeof(struct rt_pci_bus_region));
- if (!*out_regions)
- {
- return -RT_ENOMEM;
- }
- for (int i = 0; i < groups; ++i)
- {
- /*
- * ranges:
- * phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr
- * phys.low cell: llllllll llllllll llllllll llllllll
- * phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh
- *
- * n: relocatable region flag (doesn't play a role here)
- * p: prefetchable (cacheable) region flag
- * t: aliased address flag (doesn't play a role here)
- * ss: space code
- * 00: configuration space
- * 01: I/O space
- * 10: 32 bit memory space
- * 11: 64 bit memory space
- * bbbbbbbb: The PCI bus number
- * ddddd: The device number
- * fff: The function number. Used for multifunction PCI devices.
- * rrrrrrrr: Register number; used for configuration cycles.
- */
- for (int j = 0; j < phy_addr_cells; ++j)
- {
- phy_addr[j] = rt_fdt_read_number(cell++, 1);
- }
- space_code = (phy_addr[0] >> 24) & 0x3;
- cpu_addr = rt_fdt_read_number(cell, cpu_addr_cells);
- cell += cpu_addr_cells;
- phy_addr_size = rt_fdt_read_number(cell, phy_size_cells);
- cell += phy_size_cells;
- (*out_regions)[i].phy_addr = ((rt_uint64_t)phy_addr[1] << 32) | phy_addr[2];
- (*out_regions)[i].cpu_addr = cpu_addr;
- (*out_regions)[i].size = phy_addr_size;
- (*out_regions)[i].bus_start = (*out_regions)[i].phy_addr;
- if (space_code & 2)
- {
- (*out_regions)[i].flags = phy_addr[0] & (1U << 30) ?
- PCI_BUS_REGION_F_PREFETCH : PCI_BUS_REGION_F_MEM;
- }
- else if (space_code & 1)
- {
- (*out_regions)[i].flags = PCI_BUS_REGION_F_IO;
- }
- else
- {
- (*out_regions)[i].flags = PCI_BUS_REGION_F_NONE;
- }
- ++*out_regions_nr;
- }
- return RT_EOK;
- }
- rt_err_t rt_pci_ofw_parse_ranges(struct rt_ofw_node *dev_np,
- struct rt_pci_host_bridge *host_bridge)
- {
- rt_err_t err;
- int phy_addr_cells = -1, phy_size_cells = -1, cpu_addr_cells;
- if (!dev_np || !host_bridge)
- {
- return -RT_EINVAL;
- }
- cpu_addr_cells = rt_ofw_io_addr_cells(dev_np);
- rt_ofw_prop_read_s32(dev_np, "#address-cells", &phy_addr_cells);
- rt_ofw_prop_read_s32(dev_np, "#size-cells", &phy_size_cells);
- if (phy_addr_cells != 3 || phy_size_cells < 1 || cpu_addr_cells < 1)
- {
- return -RT_EINVAL;
- }
- if (pci_ofw_parse_ranges(dev_np, "ranges",
- phy_addr_cells, phy_size_cells, cpu_addr_cells,
- &host_bridge->bus_regions, &host_bridge->bus_regions_nr))
- {
- return -RT_EINVAL;
- }
- if ((err = rt_pci_region_setup(host_bridge)))
- {
- rt_free(host_bridge->bus_regions);
- host_bridge->bus_regions_nr = 0;
- return err;
- }
- err = pci_ofw_parse_ranges(dev_np, "dma-ranges",
- phy_addr_cells, phy_size_cells, cpu_addr_cells,
- &host_bridge->dma_regions, &host_bridge->dma_regions_nr);
- if (err != -RT_EEMPTY)
- {
- rt_free(host_bridge->bus_regions);
- host_bridge->bus_regions_nr = 0;
- LOG_E("%s: Read dma-ranges error = %s", rt_ofw_node_full_name(dev_np),
- rt_strerror(err));
- return err;
- }
- return RT_EOK;
- }
- rt_err_t rt_pci_ofw_host_bridge_init(struct rt_ofw_node *dev_np,
- struct rt_pci_host_bridge *host_bridge)
- {
- rt_err_t err;
- const char *propname;
- if (!dev_np || !host_bridge)
- {
- return -RT_EINVAL;
- }
- host_bridge->irq_slot = rt_pci_irq_slot;
- host_bridge->irq_map = rt_pci_ofw_irq_parse_and_map;
- if (rt_ofw_prop_read_u32_array_index(dev_np, "bus-range", 0, 2, host_bridge->bus_range) < 0)
- {
- return -RT_EIO;
- }
- propname = rt_ofw_get_prop_fuzzy_name(dev_np, ",pci-domain$");
- rt_ofw_prop_read_u32(dev_np, propname, &host_bridge->domain);
- err = rt_pci_ofw_parse_ranges(dev_np, host_bridge);
- return err;
- }
- rt_err_t rt_pci_ofw_bus_init(struct rt_pci_bus *bus)
- {
- rt_err_t err = RT_EOK;
- return err;
- }
- rt_err_t rt_pci_ofw_bus_free(struct rt_pci_bus *bus)
- {
- rt_err_t err = RT_EOK;
- return err;
- }
- /*
- * RID (Requester ID) is formatted such that:
- * Bits [15:8] are the Bus number.
- * Bits [7:3] are the Device number.
- * Bits [2:0] are the Function number.
- *
- * msi-map: Maps a Requester ID to an MSI controller and associated
- * msi-specifier data. The property is an arbitrary number of tuples of
- * (rid-base,msi-controller,msi-base,length), where:
- *
- * - rid-base is a single cell describing the first RID matched by the entry.
- *
- * - msi-controller is a single phandle to an MSI controller
- *
- * - msi-base is an msi-specifier describing the msi-specifier produced for
- * the first RID matched by the entry.
- *
- * - length is a single cell describing how many consecutive RIDs are matched
- * following the rid-base.
- *
- * Any RID r in the interval [rid-base, rid-base + length) is associated with
- * the listed msi-controller, with the msi-specifier (r - rid-base + msi-base).
- *
- * msi-map-mask: A mask to be applied to each Requester ID prior to being mapped
- * to an msi-specifier per the msi-map property.
- *
- * msi-parent: Describes the MSI parent of the root complex itself. Where
- * the root complex and MSI controller do not pass sideband data with MSI
- * writes, this property may be used to describe the MSI controller(s)
- * used by PCI devices under the root complex, if defined as such in the
- * binding for the root complex.
- *
- * / {
- * #address-cells = <1>;
- * #size-cells = <1>;
- *
- * msi_a: msi-controller@a {
- * reg = <0xa 0x1>;
- * msi-controller;
- * #msi-cells = <1>;
- * };
- *
- * msi_b: msi-controller@b {
- * reg = <0xb 0x1>;
- * msi-controller;
- * #msi-cells = <1>;
- * };
- *
- * msi_c: msi-controller@c {
- * reg = <0xc 0x1>;
- * msi-controller;
- * #msi-cells = <1>;
- * };
- *
- * Example (1)
- * ===========
- * pci: pci@f {
- * reg = <0xf 0x1>;
- * device_type = "pci";
- *
- * // The sideband data provided to the MSI controller is
- * // the RID, identity-mapped.
- * msi-map = <0x0 &msi_a 0x0 0x10000>;
- * };
- *
- * Example (2)
- * ===========
- * pci: pci@ff {
- * reg = <0xff 0x1>;
- * device_type = "pci";
- *
- * // The sideband data provided to the MSI controller is
- * // the RID, masked to only the device and function bits.
- * msi-map = <0x0 &msi_a 0x0 0x100>;
- * msi-map-mask = <0xff>
- * };
- *
- * Example (3)
- * ===========
- * pci: pci@fff {
- * reg = <0xfff 0x1>;
- * device_type = "pci";
- *
- * // The sideband data provided to the MSI controller is
- * // the RID, but the high bit of the bus number is ignored.
- * msi-map = <0x0000 &msi_a 0x0000 0x8000>,
- * <0x8000 &msi_a 0x0000 0x8000>;
- * };
- *
- * Example (4)
- * ===========
- * pci: pci@f {
- * reg = <0xf 0x1>;
- * device_type = "pci";
- *
- * // The sideband data provided to the MSI controller is
- * // the RID, but the high bit of the bus number is negated.
- * msi-map = <0x0000 &msi 0x8000 0x8000>,
- * <0x8000 &msi 0x0000 0x8000>;
- * };
- *
- * Example (5)
- * ===========
- * pci: pci@f {
- * reg = <0xf 0x1>;
- * device_type = "pci";
- *
- * // The sideband data provided to MSI controller a is the
- * // RID, but the high bit of the bus number is negated.
- * // The sideband data provided to MSI controller b is the
- * // RID, identity-mapped.
- * // MSI controller c is not addressable.
- * msi-map = <0x0000 &msi_a 0x8000 0x08000>,
- * <0x8000 &msi_a 0x0000 0x08000>,
- * <0x0000 &msi_b 0x0000 0x10000>;
- * };
- * };
- */
- static void ofw_msi_pic_init(struct rt_pci_device *pdev)
- {
- #ifdef RT_PCI_MSI
- rt_uint32_t rid;
- struct rt_pci_host_bridge *bridge;
- struct rt_ofw_node *np, *msi_ic_np = RT_NULL;
- /*
- * NOTE: Typically, a device's RID is equal to the PCI device's ID.
- * However, in complex bus management scenarios such as servers and PCs,
- * the RID needs to be associated with DMA. In these cases,
- * the RID should be equal to the DMA alias assigned to the
- * PCI device by the system bus.
- */
- rid = rt_pci_dev_id(pdev);
- bridge = rt_pci_find_host_bridge(pdev->bus);
- RT_ASSERT(bridge != RT_NULL);
- np = bridge->parent.ofw_node;
- if (!(msi_ic_np = rt_ofw_parse_phandle(np, "msi-parent", 0)))
- {
- rt_ofw_map_id(np, rid, "msi-map", "msi-map-mask", &msi_ic_np, RT_NULL);
- }
- if (!msi_ic_np)
- {
- LOG_W("%s: MSI PIC not found", rt_dm_dev_get_name(&pdev->parent));
- return;
- }
- pdev->msi_pic = rt_pic_dynamic_cast(rt_ofw_data(msi_ic_np));
- if (!pdev->msi_pic)
- {
- LOG_W("%s: '%s' not supported", rt_dm_dev_get_name(&pdev->parent), "msi-parent");
- goto _out_put_msi_parent_node;
- }
- if (!pdev->msi_pic->ops->irq_compose_msi_msg)
- {
- LOG_E("%s: MSI pic MUST implemented %s",
- rt_ofw_node_full_name(msi_ic_np), "irq_compose_msi_msg");
- RT_ASSERT(0);
- }
- if (!pdev->msi_pic->ops->irq_alloc_msi)
- {
- LOG_E("%s: MSI pic MUST implemented %s",
- rt_ofw_node_full_name(msi_ic_np), "irq_alloc_msi");
- RT_ASSERT(0);
- }
- if (!pdev->msi_pic->ops->irq_free_msi)
- {
- LOG_E("%s: MSI pic MUST implemented %s",
- rt_ofw_node_full_name(msi_ic_np), "irq_free_msi");
- RT_ASSERT(0);
- }
- _out_put_msi_parent_node:
- rt_ofw_node_put(msi_ic_np);
- #endif
- }
- static rt_int32_t ofw_pci_devfn(struct rt_ofw_node *np)
- {
- rt_int32_t res;
- rt_uint32_t reg[5];
- res = rt_ofw_prop_read_u32_array_index(np, "reg", 0, RT_ARRAY_SIZE(reg), reg);
- return res > 0 ? ((reg[0] >> 8) & 0xff) : res;
- }
- static struct rt_ofw_node *ofw_find_device(struct rt_ofw_node *np, rt_uint32_t devfn)
- {
- struct rt_ofw_node *dev_np, *mfd_np;
- rt_ofw_foreach_child_node(np, dev_np)
- {
- if (ofw_pci_devfn(dev_np) == devfn)
- {
- return dev_np;
- }
- if (rt_ofw_node_tag_equ(dev_np, "multifunc-device"))
- {
- rt_ofw_foreach_child_node(dev_np, mfd_np)
- {
- if (ofw_pci_devfn(mfd_np) == devfn)
- {
- rt_ofw_node_put(dev_np);
- return mfd_np;
- }
- }
- }
- }
- return RT_NULL;
- }
- rt_err_t rt_pci_ofw_device_init(struct rt_pci_device *pdev)
- {
- struct rt_ofw_node *np = RT_NULL;
- if (!pdev)
- {
- return -RT_EINVAL;
- }
- ofw_msi_pic_init(pdev);
- if (rt_pci_is_root_bus(pdev->bus) || !pdev->bus->self)
- {
- struct rt_pci_host_bridge *host_bridge;
- host_bridge = rt_pci_find_host_bridge(pdev->bus);
- RT_ASSERT(host_bridge != RT_NULL);
- np = host_bridge->parent.ofw_node;
- }
- else
- {
- np = pdev->bus->self->parent.ofw_node;
- }
- if (np)
- {
- pdev->parent.ofw_node = ofw_find_device(np, pdev->devfn);
- }
- return RT_EOK;
- }
- rt_err_t rt_pci_ofw_device_free(struct rt_pci_device *pdev)
- {
- if (!pdev)
- {
- return -RT_EINVAL;
- }
- rt_ofw_node_put(pdev->parent.ofw_node);
- return RT_EOK;
- }
|