Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Lad Prabhakar | 2751 | 100.00% | 1 | 100.00% |
Total | 2751 | 1 |
// SPDX-License-Identifier: GPL-2.0 /* * PCIe endpoint driver for Renesas R-Car SoCs * Copyright (c) 2020 Renesas Electronics Europe GmbH * * Author: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> */ #include <linux/clk.h> #include <linux/delay.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/of_pci.h> #include <linux/of_platform.h> #include <linux/pci.h> #include <linux/pci-epc.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> #include "pcie-rcar.h" #define RCAR_EPC_MAX_FUNCTIONS 1 /* Structure representing the PCIe interface */ struct rcar_pcie_endpoint { struct rcar_pcie pcie; phys_addr_t *ob_mapped_addr; struct pci_epc_mem_window *ob_window; u8 max_functions; unsigned int bar_to_atu[MAX_NR_INBOUND_MAPS]; unsigned long *ib_window_map; u32 num_ib_windows; u32 num_ob_windows; }; static void rcar_pcie_ep_hw_init(struct rcar_pcie *pcie) { u32 val; rcar_pci_write_reg(pcie, 0, PCIETCTLR); /* Set endpoint mode */ rcar_pci_write_reg(pcie, 0, PCIEMSR); /* Initialize default capabilities. */ rcar_rmw32(pcie, REXPCAP(0), 0xff, PCI_CAP_ID_EXP); rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS), PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ENDPOINT << 4); rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f, PCI_HEADER_TYPE_NORMAL); /* Write out the physical slot number = 0 */ rcar_rmw32(pcie, REXPCAP(PCI_EXP_SLTCAP), PCI_EXP_SLTCAP_PSN, 0); val = rcar_pci_read_reg(pcie, EXPCAP(1)); /* device supports fixed 128 bytes MPSS */ val &= ~GENMASK(2, 0); rcar_pci_write_reg(pcie, val, EXPCAP(1)); val = rcar_pci_read_reg(pcie, EXPCAP(2)); /* read requests size 128 bytes */ val &= ~GENMASK(14, 12); /* payload size 128 bytes */ val &= ~GENMASK(7, 5); rcar_pci_write_reg(pcie, val, EXPCAP(2)); /* Set target link speed to 5.0 GT/s */ rcar_rmw32(pcie, EXPCAP(12), PCI_EXP_LNKSTA_CLS, PCI_EXP_LNKSTA_CLS_5_0GB); /* Set the completion timer timeout to the maximum 50ms. */ rcar_rmw32(pcie, TLCTLR + 1, 0x3f, 50); /* Terminate list of capabilities (Next Capability Offset=0) */ rcar_rmw32(pcie, RVCCAP(0), 0xfff00000, 0); /* flush modifications */ wmb(); } static int rcar_pcie_ep_get_window(struct rcar_pcie_endpoint *ep, phys_addr_t addr) { int i; for (i = 0; i < ep->num_ob_windows; i++) if (ep->ob_window[i].phys_base == addr) return i; return -EINVAL; } static int rcar_pcie_parse_outbound_ranges(struct rcar_pcie_endpoint *ep, struct platform_device *pdev) { struct rcar_pcie *pcie = &ep->pcie; char outbound_name[10]; struct resource *res; unsigned int i = 0; ep->num_ob_windows = 0; for (i = 0; i < RCAR_PCI_MAX_RESOURCES; i++) { sprintf(outbound_name, "memory%u", i); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, outbound_name); if (!res) { dev_err(pcie->dev, "missing outbound window %u\n", i); return -EINVAL; } if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), outbound_name)) { dev_err(pcie->dev, "Cannot request memory region %s.\n", outbound_name); return -EIO; } ep->ob_window[i].phys_base = res->start; ep->ob_window[i].size = resource_size(res); /* controller doesn't support multiple allocation * from same window, so set page_size to window size */ ep->ob_window[i].page_size = resource_size(res); } ep->num_ob_windows = i; return 0; } static int rcar_pcie_ep_get_pdata(struct rcar_pcie_endpoint *ep, struct platform_device *pdev) { struct rcar_pcie *pcie = &ep->pcie; struct pci_epc_mem_window *window; struct device *dev = pcie->dev; struct resource res; int err; err = of_address_to_resource(dev->of_node, 0, &res); if (err) return err; pcie->base = devm_ioremap_resource(dev, &res); if (IS_ERR(pcie->base)) return PTR_ERR(pcie->base); ep->ob_window = devm_kcalloc(dev, RCAR_PCI_MAX_RESOURCES, sizeof(*window), GFP_KERNEL); if (!ep->ob_window) return -ENOMEM; rcar_pcie_parse_outbound_ranges(ep, pdev); err = of_property_read_u8(dev->of_node, "max-functions", &ep->max_functions); if (err < 0 || ep->max_functions > RCAR_EPC_MAX_FUNCTIONS) ep->max_functions = RCAR_EPC_MAX_FUNCTIONS; return 0; } static int rcar_pcie_ep_write_header(struct pci_epc *epc, u8 fn, struct pci_epf_header *hdr) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); struct rcar_pcie *pcie = &ep->pcie; u32 val; if (!fn) val = hdr->vendorid; else val = rcar_pci_read_reg(pcie, IDSETR0); val |= hdr->deviceid << 16; rcar_pci_write_reg(pcie, val, IDSETR0); val = hdr->revid; val |= hdr->progif_code << 8; val |= hdr->subclass_code << 16; val |= hdr->baseclass_code << 24; rcar_pci_write_reg(pcie, val, IDSETR1); if (!fn) val = hdr->subsys_vendor_id; else val = rcar_pci_read_reg(pcie, SUBIDSETR); val |= hdr->subsys_id << 16; rcar_pci_write_reg(pcie, val, SUBIDSETR); if (hdr->interrupt_pin > PCI_INTERRUPT_INTA) return -EINVAL; val = rcar_pci_read_reg(pcie, PCICONF(15)); val |= (hdr->interrupt_pin << 8); rcar_pci_write_reg(pcie, val, PCICONF(15)); return 0; } static int rcar_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, struct pci_epf_bar *epf_bar) { int flags = epf_bar->flags | LAR_ENABLE | LAM_64BIT; struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); u64 size = 1ULL << fls64(epf_bar->size - 1); dma_addr_t cpu_addr = epf_bar->phys_addr; enum pci_barno bar = epf_bar->barno; struct rcar_pcie *pcie = &ep->pcie; u32 mask; int idx; int err; idx = find_first_zero_bit(ep->ib_window_map, ep->num_ib_windows); if (idx >= ep->num_ib_windows) { dev_err(pcie->dev, "no free inbound window\n"); return -EINVAL; } if ((flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) flags |= IO_SPACE; ep->bar_to_atu[bar] = idx; /* use 64-bit BARs */ set_bit(idx, ep->ib_window_map); set_bit(idx + 1, ep->ib_window_map); if (cpu_addr > 0) { unsigned long nr_zeros = __ffs64(cpu_addr); u64 alignment = 1ULL << nr_zeros; size = min(size, alignment); } size = min(size, 1ULL << 32); mask = roundup_pow_of_two(size) - 1; mask &= ~0xf; rcar_pcie_set_inbound(pcie, cpu_addr, 0x0, mask | flags, idx, false); err = rcar_pcie_wait_for_phyrdy(pcie); if (err) { dev_err(pcie->dev, "phy not ready\n"); return -EINVAL; } return 0; } static void rcar_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, struct pci_epf_bar *epf_bar) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); enum pci_barno bar = epf_bar->barno; u32 atu_index = ep->bar_to_atu[bar]; rcar_pcie_set_inbound(&ep->pcie, 0x0, 0x0, 0x0, bar, false); clear_bit(atu_index, ep->ib_window_map); clear_bit(atu_index + 1, ep->ib_window_map); } static int rcar_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 interrupts) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); struct rcar_pcie *pcie = &ep->pcie; u32 flags; flags = rcar_pci_read_reg(pcie, MSICAP(fn)); flags |= interrupts << MSICAP0_MMESCAP_OFFSET; rcar_pci_write_reg(pcie, flags, MSICAP(fn)); return 0; } static int rcar_pcie_ep_get_msi(struct pci_epc *epc, u8 fn) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); struct rcar_pcie *pcie = &ep->pcie; u32 flags; flags = rcar_pci_read_reg(pcie, MSICAP(fn)); if (!(flags & MSICAP0_MSIE)) return -EINVAL; return ((flags & MSICAP0_MMESE_MASK) >> MSICAP0_MMESE_OFFSET); } static int rcar_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, phys_addr_t addr, u64 pci_addr, size_t size) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); struct rcar_pcie *pcie = &ep->pcie; struct resource_entry win; struct resource res; int window; int err; /* check if we have a link. */ err = rcar_pcie_wait_for_dl(pcie); if (err) { dev_err(pcie->dev, "link not up\n"); return err; } window = rcar_pcie_ep_get_window(ep, addr); if (window < 0) { dev_err(pcie->dev, "failed to get corresponding window\n"); return -EINVAL; } memset(&win, 0x0, sizeof(win)); memset(&res, 0x0, sizeof(res)); res.start = pci_addr; res.end = pci_addr + size - 1; res.flags = IORESOURCE_MEM; win.res = &res; rcar_pcie_set_outbound(pcie, window, &win); ep->ob_mapped_addr[window] = addr; return 0; } static void rcar_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, phys_addr_t addr) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); struct resource_entry win; struct resource res; int idx; for (idx = 0; idx < ep->num_ob_windows; idx++) if (ep->ob_mapped_addr[idx] == addr) break; if (idx >= ep->num_ob_windows) return; memset(&win, 0x0, sizeof(win)); memset(&res, 0x0, sizeof(res)); win.res = &res; rcar_pcie_set_outbound(&ep->pcie, idx, &win); ep->ob_mapped_addr[idx] = 0; } static int rcar_pcie_ep_assert_intx(struct rcar_pcie_endpoint *ep, u8 fn, u8 intx) { struct rcar_pcie *pcie = &ep->pcie; u32 val; val = rcar_pci_read_reg(pcie, PCIEMSITXR); if ((val & PCI_MSI_FLAGS_ENABLE)) { dev_err(pcie->dev, "MSI is enabled, cannot assert INTx\n"); return -EINVAL; } val = rcar_pci_read_reg(pcie, PCICONF(1)); if ((val & INTDIS)) { dev_err(pcie->dev, "INTx message transmission is disabled\n"); return -EINVAL; } val = rcar_pci_read_reg(pcie, PCIEINTXR); if ((val & ASTINTX)) { dev_err(pcie->dev, "INTx is already asserted\n"); return -EINVAL; } val |= ASTINTX; rcar_pci_write_reg(pcie, val, PCIEINTXR); usleep_range(1000, 1001); val = rcar_pci_read_reg(pcie, PCIEINTXR); val &= ~ASTINTX; rcar_pci_write_reg(pcie, val, PCIEINTXR); return 0; } static int rcar_pcie_ep_assert_msi(struct rcar_pcie *pcie, u8 fn, u8 interrupt_num) { u16 msi_count; u32 val; /* Check MSI enable bit */ val = rcar_pci_read_reg(pcie, MSICAP(fn)); if (!(val & MSICAP0_MSIE)) return -EINVAL; /* Get MSI numbers from MME */ msi_count = ((val & MSICAP0_MMESE_MASK) >> MSICAP0_MMESE_OFFSET); msi_count = 1 << msi_count; if (!interrupt_num || interrupt_num > msi_count) return -EINVAL; val = rcar_pci_read_reg(pcie, PCIEMSITXR); rcar_pci_write_reg(pcie, val | (interrupt_num - 1), PCIEMSITXR); return 0; } static int rcar_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, enum pci_epc_irq_type type, u16 interrupt_num) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); switch (type) { case PCI_EPC_IRQ_LEGACY: return rcar_pcie_ep_assert_intx(ep, fn, 0); case PCI_EPC_IRQ_MSI: return rcar_pcie_ep_assert_msi(&ep->pcie, fn, interrupt_num); default: return -EINVAL; } } static int rcar_pcie_ep_start(struct pci_epc *epc) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); rcar_pci_write_reg(&ep->pcie, MACCTLR_INIT_VAL, MACCTLR); rcar_pci_write_reg(&ep->pcie, CFINIT, PCIETCTLR); return 0; } static void rcar_pcie_ep_stop(struct pci_epc *epc) { struct rcar_pcie_endpoint *ep = epc_get_drvdata(epc); rcar_pci_write_reg(&ep->pcie, 0, PCIETCTLR); } static const struct pci_epc_features rcar_pcie_epc_features = { .linkup_notifier = false, .msi_capable = true, .msix_capable = false, /* use 64-bit BARs so mark BAR[1,3,5] as reserved */ .reserved_bar = 1 << BAR_1 | 1 << BAR_3 | 1 << BAR_5, .bar_fixed_64bit = 1 << BAR_0 | 1 << BAR_2 | 1 << BAR_4, .bar_fixed_size[0] = 128, .bar_fixed_size[2] = 256, .bar_fixed_size[4] = 256, }; static const struct pci_epc_features* rcar_pcie_ep_get_features(struct pci_epc *epc, u8 func_no) { return &rcar_pcie_epc_features; } static const struct pci_epc_ops rcar_pcie_epc_ops = { .write_header = rcar_pcie_ep_write_header, .set_bar = rcar_pcie_ep_set_bar, .clear_bar = rcar_pcie_ep_clear_bar, .set_msi = rcar_pcie_ep_set_msi, .get_msi = rcar_pcie_ep_get_msi, .map_addr = rcar_pcie_ep_map_addr, .unmap_addr = rcar_pcie_ep_unmap_addr, .raise_irq = rcar_pcie_ep_raise_irq, .start = rcar_pcie_ep_start, .stop = rcar_pcie_ep_stop, .get_features = rcar_pcie_ep_get_features, }; static const struct of_device_id rcar_pcie_ep_of_match[] = { { .compatible = "renesas,r8a774c0-pcie-ep", }, { .compatible = "renesas,rcar-gen3-pcie-ep" }, { }, }; static int rcar_pcie_ep_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rcar_pcie_endpoint *ep; struct rcar_pcie *pcie; struct pci_epc *epc; int err; ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); if (!ep) return -ENOMEM; pcie = &ep->pcie; pcie->dev = dev; pm_runtime_enable(dev); err = pm_runtime_get_sync(dev); if (err < 0) { dev_err(dev, "pm_runtime_get_sync failed\n"); goto err_pm_disable; } err = rcar_pcie_ep_get_pdata(ep, pdev); if (err < 0) { dev_err(dev, "failed to request resources: %d\n", err); goto err_pm_put; } ep->num_ib_windows = MAX_NR_INBOUND_MAPS; ep->ib_window_map = devm_kcalloc(dev, BITS_TO_LONGS(ep->num_ib_windows), sizeof(long), GFP_KERNEL); if (!ep->ib_window_map) { err = -ENOMEM; dev_err(dev, "failed to allocate memory for inbound map\n"); goto err_pm_put; } ep->ob_mapped_addr = devm_kcalloc(dev, ep->num_ob_windows, sizeof(*ep->ob_mapped_addr), GFP_KERNEL); if (!ep->ob_mapped_addr) { err = -ENOMEM; dev_err(dev, "failed to allocate memory for outbound memory pointers\n"); goto err_pm_put; } epc = devm_pci_epc_create(dev, &rcar_pcie_epc_ops); if (IS_ERR(epc)) { dev_err(dev, "failed to create epc device\n"); err = PTR_ERR(epc); goto err_pm_put; } epc->max_functions = ep->max_functions; epc_set_drvdata(epc, ep); rcar_pcie_ep_hw_init(pcie); err = pci_epc_multi_mem_init(epc, ep->ob_window, ep->num_ob_windows); if (err < 0) { dev_err(dev, "failed to initialize the epc memory space\n"); goto err_pm_put; } return 0; err_pm_put: pm_runtime_put(dev); err_pm_disable: pm_runtime_disable(dev); return err; } static struct platform_driver rcar_pcie_ep_driver = { .driver = { .name = "rcar-pcie-ep", .of_match_table = rcar_pcie_ep_of_match, .suppress_bind_attrs = true, }, .probe = rcar_pcie_ep_probe, }; builtin_platform_driver(rcar_pcie_ep_driver);
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1