mirror of
https://github.com/torvalds/linux.git
synced 2026-05-24 15:12:13 +02:00
Merge branch 'pci/controller/apple'
- Skip ports disabled in DT when setting up ports (Janne Grunau)
- Add t6020 compatible string (Alyssa Rosenzweig)
- Extract ECAM bridge creation helper from pci_host_common_probe() to
separate driver-specific things like MSI from PCI things (Marc Zyngier)
- Dynamically allocate RID-to_SID bitmap to prepare for SoCs with varying
capabilities (Marc Zyngier)
- Directly set/clear INTx mask bits because T602x dropped the accessors
that could do this without locking (Marc Zyngier)
- Move port PHY registers to their own reg items to accommodate T602x,
which moves them around; retain default offsets for existing DTs that
lack phy%d entries with the reg offsets (Hector Martin)
- Stop polling for core refclk, which doesn't work on T602x and the
bootloader has already done anyway (Hector Martin)
- Use gpiod_set_value_cansleep() when asserting PERST# in probe because
we're allowed to sleep there (Hector Martin)
- Move register offsets into SoC-specific structure (Hector Martin)
- Add T602x PCIe support (Hector Martin)
* pci/controller/apple:
PCI: apple: Add T602x PCIe support
PCI: apple: Abstract register offsets via a SoC-specific structure
PCI: apple: Use gpiod_set_value_cansleep in probe flow
PCI: apple: Drop poll for CORE_RC_PHYIF_STAT_REFCLK
PCI: apple: Move port PHY registers to their own reg items
PCI: apple: Fix missing OF node reference in apple_pcie_setup_port
PCI: apple: Move away from INTMSK{SET,CLR} for INTx and private interrupts
PCI: apple: Dynamically allocate RID-to_SID bitmap
PCI: apple: Move over to standalone probing
PCI: ecam: Allow cfg->priv to be pre-populated from the root port device
PCI: host-generic: Extract an ECAM bridge creation helper from pci_host_common_probe()
dt-bindings: pci: apple,pcie: Add t6020 compatible string
PCI: apple: Set only available ports up
This commit is contained in:
commit
c3b2f9dccb
|
|
@ -17,6 +17,10 @@ description: |
|
|||
implements its root ports. But the ATU found on most DesignWare
|
||||
PCIe host bridges is absent.
|
||||
|
||||
On systems derived from T602x, the PHY registers are in a region
|
||||
separate from the port registers. In that case, there is one PHY
|
||||
register range per port register range.
|
||||
|
||||
All root ports share a single ECAM space, but separate GPIOs are
|
||||
used to take the PCI devices on those ports out of reset. Therefore
|
||||
the standard "reset-gpios" and "max-link-speed" properties appear on
|
||||
|
|
@ -30,16 +34,18 @@ description: |
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- apple,t8103-pcie
|
||||
- apple,t8112-pcie
|
||||
- apple,t6000-pcie
|
||||
- const: apple,pcie
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- apple,t8103-pcie
|
||||
- apple,t8112-pcie
|
||||
- apple,t6000-pcie
|
||||
- const: apple,pcie
|
||||
- const: apple,t6020-pcie
|
||||
|
||||
reg:
|
||||
minItems: 3
|
||||
maxItems: 6
|
||||
maxItems: 10
|
||||
|
||||
reg-names:
|
||||
minItems: 3
|
||||
|
|
@ -50,6 +56,10 @@ properties:
|
|||
- const: port1
|
||||
- const: port2
|
||||
- const: port3
|
||||
- const: phy0
|
||||
- const: phy1
|
||||
- const: phy2
|
||||
- const: phy3
|
||||
|
||||
ranges:
|
||||
minItems: 2
|
||||
|
|
@ -98,6 +108,15 @@ allOf:
|
|||
maxItems: 5
|
||||
interrupts:
|
||||
maxItems: 3
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: apple,t6020-pcie
|
||||
then:
|
||||
properties:
|
||||
reg-names:
|
||||
minItems: 10
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
|
|
|||
|
|
@ -49,23 +49,17 @@ static struct pci_config_window *gen_pci_init(struct device *dev,
|
|||
return cfg;
|
||||
}
|
||||
|
||||
int pci_host_common_probe(struct platform_device *pdev)
|
||||
int pci_host_common_init(struct platform_device *pdev,
|
||||
const struct pci_ecam_ops *ops)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct pci_host_bridge *bridge;
|
||||
struct pci_config_window *cfg;
|
||||
const struct pci_ecam_ops *ops;
|
||||
|
||||
ops = of_device_get_match_data(&pdev->dev);
|
||||
if (!ops)
|
||||
return -ENODEV;
|
||||
|
||||
bridge = devm_pci_alloc_host_bridge(dev, 0);
|
||||
if (!bridge)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, bridge);
|
||||
|
||||
of_pci_check_probe_only();
|
||||
|
||||
/* Parse and map our Configuration Space windows */
|
||||
|
|
@ -73,6 +67,8 @@ int pci_host_common_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(cfg))
|
||||
return PTR_ERR(cfg);
|
||||
|
||||
platform_set_drvdata(pdev, bridge);
|
||||
|
||||
bridge->sysdata = cfg;
|
||||
bridge->ops = (struct pci_ops *)&ops->pci_ops;
|
||||
bridge->enable_device = ops->enable_device;
|
||||
|
|
@ -81,6 +77,18 @@ int pci_host_common_probe(struct platform_device *pdev)
|
|||
|
||||
return pci_host_probe(bridge);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_host_common_init);
|
||||
|
||||
int pci_host_common_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct pci_ecam_ops *ops;
|
||||
|
||||
ops = of_device_get_match_data(&pdev->dev);
|
||||
if (!ops)
|
||||
return -ENODEV;
|
||||
|
||||
return pci_host_common_init(pdev, ops);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_host_common_probe);
|
||||
|
||||
void pci_host_common_remove(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
* Author: Marc Zyngier <maz@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
|
@ -29,6 +30,7 @@
|
|||
#include <linux/of_irq.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
|
||||
/* T8103 (original M1) and related SoCs */
|
||||
#define CORE_RC_PHYIF_CTL 0x00024
|
||||
#define CORE_RC_PHYIF_CTL_RUN BIT(0)
|
||||
#define CORE_RC_PHYIF_STAT 0x00028
|
||||
|
|
@ -39,14 +41,18 @@
|
|||
#define CORE_RC_STAT_READY BIT(0)
|
||||
#define CORE_FABRIC_STAT 0x04000
|
||||
#define CORE_FABRIC_STAT_MASK 0x001F001F
|
||||
#define CORE_LANE_CFG(port) (0x84000 + 0x4000 * (port))
|
||||
#define CORE_LANE_CFG_REFCLK0REQ BIT(0)
|
||||
#define CORE_LANE_CFG_REFCLK1REQ BIT(1)
|
||||
#define CORE_LANE_CFG_REFCLK0ACK BIT(2)
|
||||
#define CORE_LANE_CFG_REFCLK1ACK BIT(3)
|
||||
#define CORE_LANE_CFG_REFCLKEN (BIT(9) | BIT(10))
|
||||
#define CORE_LANE_CTL(port) (0x84004 + 0x4000 * (port))
|
||||
#define CORE_LANE_CTL_CFGACC BIT(15)
|
||||
|
||||
#define CORE_PHY_DEFAULT_BASE(port) (0x84000 + 0x4000 * (port))
|
||||
|
||||
#define PHY_LANE_CFG 0x00000
|
||||
#define PHY_LANE_CFG_REFCLK0REQ BIT(0)
|
||||
#define PHY_LANE_CFG_REFCLK1REQ BIT(1)
|
||||
#define PHY_LANE_CFG_REFCLK0ACK BIT(2)
|
||||
#define PHY_LANE_CFG_REFCLK1ACK BIT(3)
|
||||
#define PHY_LANE_CFG_REFCLKEN (BIT(9) | BIT(10))
|
||||
#define PHY_LANE_CFG_REFCLKCGEN (BIT(30) | BIT(31))
|
||||
#define PHY_LANE_CTL 0x00004
|
||||
#define PHY_LANE_CTL_CFGACC BIT(15)
|
||||
|
||||
#define PORT_LTSSMCTL 0x00080
|
||||
#define PORT_LTSSMCTL_START BIT(0)
|
||||
|
|
@ -100,7 +106,7 @@
|
|||
#define PORT_REFCLK_CGDIS BIT(8)
|
||||
#define PORT_PERST 0x00814
|
||||
#define PORT_PERST_OFF BIT(0)
|
||||
#define PORT_RID2SID(i16) (0x00828 + 4 * (i16))
|
||||
#define PORT_RID2SID 0x00828
|
||||
#define PORT_RID2SID_VALID BIT(31)
|
||||
#define PORT_RID2SID_SID_SHIFT 16
|
||||
#define PORT_RID2SID_BUS_SHIFT 8
|
||||
|
|
@ -118,7 +124,15 @@
|
|||
#define PORT_TUNSTAT_PERST_ACK_PEND BIT(1)
|
||||
#define PORT_PREFMEM_ENABLE 0x00994
|
||||
|
||||
#define MAX_RID2SID 64
|
||||
/* T602x (M2-pro and co) */
|
||||
#define PORT_T602X_MSIADDR 0x016c
|
||||
#define PORT_T602X_MSIADDR_HI 0x0170
|
||||
#define PORT_T602X_PERST 0x082c
|
||||
#define PORT_T602X_RID2SID 0x3000
|
||||
#define PORT_T602X_MSIMAP 0x3800
|
||||
|
||||
#define PORT_MSIMAP_ENABLE BIT(31)
|
||||
#define PORT_MSIMAP_TARGET GENMASK(7, 0)
|
||||
|
||||
/*
|
||||
* The doorbell address is set to 0xfffff000, which by convention
|
||||
|
|
@ -129,10 +143,45 @@
|
|||
*/
|
||||
#define DOORBELL_ADDR CONFIG_PCIE_APPLE_MSI_DOORBELL_ADDR
|
||||
|
||||
struct hw_info {
|
||||
u32 phy_lane_ctl;
|
||||
u32 port_msiaddr;
|
||||
u32 port_msiaddr_hi;
|
||||
u32 port_refclk;
|
||||
u32 port_perst;
|
||||
u32 port_rid2sid;
|
||||
u32 port_msimap;
|
||||
u32 max_rid2sid;
|
||||
};
|
||||
|
||||
static const struct hw_info t8103_hw = {
|
||||
.phy_lane_ctl = PHY_LANE_CTL,
|
||||
.port_msiaddr = PORT_MSIADDR,
|
||||
.port_msiaddr_hi = 0,
|
||||
.port_refclk = PORT_REFCLK,
|
||||
.port_perst = PORT_PERST,
|
||||
.port_rid2sid = PORT_RID2SID,
|
||||
.port_msimap = 0,
|
||||
.max_rid2sid = 64,
|
||||
};
|
||||
|
||||
static const struct hw_info t602x_hw = {
|
||||
.phy_lane_ctl = 0,
|
||||
.port_msiaddr = PORT_T602X_MSIADDR,
|
||||
.port_msiaddr_hi = PORT_T602X_MSIADDR_HI,
|
||||
.port_refclk = 0,
|
||||
.port_perst = PORT_T602X_PERST,
|
||||
.port_rid2sid = PORT_T602X_RID2SID,
|
||||
.port_msimap = PORT_T602X_MSIMAP,
|
||||
/* 16 on t602x, guess for autodetect on future HW */
|
||||
.max_rid2sid = 512,
|
||||
};
|
||||
|
||||
struct apple_pcie {
|
||||
struct mutex lock;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
const struct hw_info *hw;
|
||||
struct irq_domain *domain;
|
||||
unsigned long *bitmap;
|
||||
struct list_head ports;
|
||||
|
|
@ -142,12 +191,14 @@ struct apple_pcie {
|
|||
};
|
||||
|
||||
struct apple_pcie_port {
|
||||
raw_spinlock_t lock;
|
||||
struct apple_pcie *pcie;
|
||||
struct device_node *np;
|
||||
void __iomem *base;
|
||||
void __iomem *phy;
|
||||
struct irq_domain *domain;
|
||||
struct list_head entry;
|
||||
DECLARE_BITMAP(sid_map, MAX_RID2SID);
|
||||
unsigned long *sid_map;
|
||||
int sid_map_sz;
|
||||
int idx;
|
||||
};
|
||||
|
|
@ -261,14 +312,16 @@ static void apple_port_irq_mask(struct irq_data *data)
|
|||
{
|
||||
struct apple_pcie_port *port = irq_data_get_irq_chip_data(data);
|
||||
|
||||
writel_relaxed(BIT(data->hwirq), port->base + PORT_INTMSKSET);
|
||||
guard(raw_spinlock_irqsave)(&port->lock);
|
||||
rmw_set(BIT(data->hwirq), port->base + PORT_INTMSK);
|
||||
}
|
||||
|
||||
static void apple_port_irq_unmask(struct irq_data *data)
|
||||
{
|
||||
struct apple_pcie_port *port = irq_data_get_irq_chip_data(data);
|
||||
|
||||
writel_relaxed(BIT(data->hwirq), port->base + PORT_INTMSKCLR);
|
||||
guard(raw_spinlock_irqsave)(&port->lock);
|
||||
rmw_clear(BIT(data->hwirq), port->base + PORT_INTMSK);
|
||||
}
|
||||
|
||||
static bool hwirq_is_intx(unsigned int hwirq)
|
||||
|
|
@ -372,7 +425,9 @@ static void apple_port_irq_handler(struct irq_desc *desc)
|
|||
static int apple_pcie_port_setup_irq(struct apple_pcie_port *port)
|
||||
{
|
||||
struct fwnode_handle *fwnode = &port->np->fwnode;
|
||||
struct apple_pcie *pcie = port->pcie;
|
||||
unsigned int irq;
|
||||
u32 val = 0;
|
||||
|
||||
/* FIXME: consider moving each interrupt under each port */
|
||||
irq = irq_of_parse_and_map(to_of_node(dev_fwnode(port->pcie->dev)),
|
||||
|
|
@ -387,20 +442,31 @@ static int apple_pcie_port_setup_irq(struct apple_pcie_port *port)
|
|||
return -ENOMEM;
|
||||
|
||||
/* Disable all interrupts */
|
||||
writel_relaxed(~0, port->base + PORT_INTMSKSET);
|
||||
writel_relaxed(~0, port->base + PORT_INTMSK);
|
||||
writel_relaxed(~0, port->base + PORT_INTSTAT);
|
||||
writel_relaxed(~0, port->base + PORT_LINKCMDSTS);
|
||||
|
||||
irq_set_chained_handler_and_data(irq, apple_port_irq_handler, port);
|
||||
|
||||
/* Configure MSI base address */
|
||||
BUILD_BUG_ON(upper_32_bits(DOORBELL_ADDR));
|
||||
writel_relaxed(lower_32_bits(DOORBELL_ADDR), port->base + PORT_MSIADDR);
|
||||
writel_relaxed(lower_32_bits(DOORBELL_ADDR),
|
||||
port->base + pcie->hw->port_msiaddr);
|
||||
if (pcie->hw->port_msiaddr_hi)
|
||||
writel_relaxed(0, port->base + pcie->hw->port_msiaddr_hi);
|
||||
|
||||
/* Enable MSIs, shared between all ports */
|
||||
writel_relaxed(0, port->base + PORT_MSIBASE);
|
||||
writel_relaxed((ilog2(port->pcie->nvecs) << PORT_MSICFG_L2MSINUM_SHIFT) |
|
||||
PORT_MSICFG_EN, port->base + PORT_MSICFG);
|
||||
if (pcie->hw->port_msimap) {
|
||||
for (int i = 0; i < pcie->nvecs; i++)
|
||||
writel_relaxed(FIELD_PREP(PORT_MSIMAP_TARGET, i) |
|
||||
PORT_MSIMAP_ENABLE,
|
||||
port->base + pcie->hw->port_msimap + 4 * i);
|
||||
} else {
|
||||
writel_relaxed(0, port->base + PORT_MSIBASE);
|
||||
val = ilog2(pcie->nvecs) << PORT_MSICFG_L2MSINUM_SHIFT;
|
||||
}
|
||||
|
||||
writel_relaxed(val | PORT_MSICFG_EN, port->base + PORT_MSICFG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -467,43 +533,47 @@ static int apple_pcie_setup_refclk(struct apple_pcie *pcie,
|
|||
u32 stat;
|
||||
int res;
|
||||
|
||||
res = readl_relaxed_poll_timeout(pcie->base + CORE_RC_PHYIF_STAT, stat,
|
||||
stat & CORE_RC_PHYIF_STAT_REFCLK,
|
||||
if (pcie->hw->phy_lane_ctl)
|
||||
rmw_set(PHY_LANE_CTL_CFGACC, port->phy + pcie->hw->phy_lane_ctl);
|
||||
|
||||
rmw_set(PHY_LANE_CFG_REFCLK0REQ, port->phy + PHY_LANE_CFG);
|
||||
|
||||
res = readl_relaxed_poll_timeout(port->phy + PHY_LANE_CFG,
|
||||
stat, stat & PHY_LANE_CFG_REFCLK0ACK,
|
||||
100, 50000);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
rmw_set(CORE_LANE_CTL_CFGACC, pcie->base + CORE_LANE_CTL(port->idx));
|
||||
rmw_set(CORE_LANE_CFG_REFCLK0REQ, pcie->base + CORE_LANE_CFG(port->idx));
|
||||
|
||||
res = readl_relaxed_poll_timeout(pcie->base + CORE_LANE_CFG(port->idx),
|
||||
stat, stat & CORE_LANE_CFG_REFCLK0ACK,
|
||||
100, 50000);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
rmw_set(CORE_LANE_CFG_REFCLK1REQ, pcie->base + CORE_LANE_CFG(port->idx));
|
||||
res = readl_relaxed_poll_timeout(pcie->base + CORE_LANE_CFG(port->idx),
|
||||
stat, stat & CORE_LANE_CFG_REFCLK1ACK,
|
||||
rmw_set(PHY_LANE_CFG_REFCLK1REQ, port->phy + PHY_LANE_CFG);
|
||||
res = readl_relaxed_poll_timeout(port->phy + PHY_LANE_CFG,
|
||||
stat, stat & PHY_LANE_CFG_REFCLK1ACK,
|
||||
100, 50000);
|
||||
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
rmw_clear(CORE_LANE_CTL_CFGACC, pcie->base + CORE_LANE_CTL(port->idx));
|
||||
if (pcie->hw->phy_lane_ctl)
|
||||
rmw_clear(PHY_LANE_CTL_CFGACC, port->phy + pcie->hw->phy_lane_ctl);
|
||||
|
||||
rmw_set(CORE_LANE_CFG_REFCLKEN, pcie->base + CORE_LANE_CFG(port->idx));
|
||||
rmw_set(PORT_REFCLK_EN, port->base + PORT_REFCLK);
|
||||
rmw_set(PHY_LANE_CFG_REFCLKEN, port->phy + PHY_LANE_CFG);
|
||||
|
||||
if (pcie->hw->port_refclk)
|
||||
rmw_set(PORT_REFCLK_EN, port->base + pcie->hw->port_refclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __iomem *port_rid2sid_addr(struct apple_pcie_port *port, int idx)
|
||||
{
|
||||
return port->base + port->pcie->hw->port_rid2sid + 4 * idx;
|
||||
}
|
||||
|
||||
static u32 apple_pcie_rid2sid_write(struct apple_pcie_port *port,
|
||||
int idx, u32 val)
|
||||
{
|
||||
writel_relaxed(val, port->base + PORT_RID2SID(idx));
|
||||
writel_relaxed(val, port_rid2sid_addr(port, idx));
|
||||
/* Read back to ensure completion of the write */
|
||||
return readl_relaxed(port->base + PORT_RID2SID(idx));
|
||||
return readl_relaxed(port_rid2sid_addr(port, idx));
|
||||
}
|
||||
|
||||
static int apple_pcie_setup_port(struct apple_pcie *pcie,
|
||||
|
|
@ -512,6 +582,8 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
|
|||
struct platform_device *platform = to_platform_device(pcie->dev);
|
||||
struct apple_pcie_port *port;
|
||||
struct gpio_desc *reset;
|
||||
struct resource *res;
|
||||
char name[16];
|
||||
u32 stat, idx;
|
||||
int ret, i;
|
||||
|
||||
|
|
@ -524,6 +596,10 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
|
|||
if (!port)
|
||||
return -ENOMEM;
|
||||
|
||||
port->sid_map = devm_bitmap_zalloc(pcie->dev, pcie->hw->max_rid2sid, GFP_KERNEL);
|
||||
if (!port->sid_map)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = of_property_read_u32_index(np, "reg", 0, &idx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -533,14 +609,28 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
|
|||
port->pcie = pcie;
|
||||
port->np = np;
|
||||
|
||||
port->base = devm_platform_ioremap_resource(platform, port->idx + 2);
|
||||
raw_spin_lock_init(&port->lock);
|
||||
|
||||
snprintf(name, sizeof(name), "port%d", port->idx);
|
||||
res = platform_get_resource_byname(platform, IORESOURCE_MEM, name);
|
||||
if (!res)
|
||||
res = platform_get_resource(platform, IORESOURCE_MEM, port->idx + 2);
|
||||
|
||||
port->base = devm_ioremap_resource(&platform->dev, res);
|
||||
if (IS_ERR(port->base))
|
||||
return PTR_ERR(port->base);
|
||||
|
||||
snprintf(name, sizeof(name), "phy%d", port->idx);
|
||||
res = platform_get_resource_byname(platform, IORESOURCE_MEM, name);
|
||||
if (res)
|
||||
port->phy = devm_ioremap_resource(&platform->dev, res);
|
||||
else
|
||||
port->phy = pcie->base + CORE_PHY_DEFAULT_BASE(port->idx);
|
||||
|
||||
rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK);
|
||||
|
||||
/* Assert PERST# before setting up the clock */
|
||||
gpiod_set_value(reset, 1);
|
||||
gpiod_set_value_cansleep(reset, 1);
|
||||
|
||||
ret = apple_pcie_setup_refclk(pcie, port);
|
||||
if (ret < 0)
|
||||
|
|
@ -550,8 +640,8 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
|
|||
usleep_range(100, 200);
|
||||
|
||||
/* Deassert PERST# */
|
||||
rmw_set(PORT_PERST_OFF, port->base + PORT_PERST);
|
||||
gpiod_set_value(reset, 0);
|
||||
rmw_set(PORT_PERST_OFF, port->base + pcie->hw->port_perst);
|
||||
gpiod_set_value_cansleep(reset, 0);
|
||||
|
||||
/* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */
|
||||
msleep(100);
|
||||
|
|
@ -563,7 +653,11 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
|
|||
return ret;
|
||||
}
|
||||
|
||||
rmw_clear(PORT_REFCLK_CGDIS, port->base + PORT_REFCLK);
|
||||
if (pcie->hw->port_refclk)
|
||||
rmw_clear(PORT_REFCLK_CGDIS, port->base + pcie->hw->port_refclk);
|
||||
else
|
||||
rmw_set(PHY_LANE_CFG_REFCLKCGEN, port->phy + PHY_LANE_CFG);
|
||||
|
||||
rmw_clear(PORT_APPCLK_CGDIS, port->base + PORT_APPCLK);
|
||||
|
||||
ret = apple_pcie_port_setup_irq(port);
|
||||
|
|
@ -571,7 +665,7 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
|
|||
return ret;
|
||||
|
||||
/* Reset all RID/SID mappings, and check for RAZ/WI registers */
|
||||
for (i = 0; i < MAX_RID2SID; i++) {
|
||||
for (i = 0; i < pcie->hw->max_rid2sid; i++) {
|
||||
if (apple_pcie_rid2sid_write(port, i, 0xbad1d) != 0xbad1d)
|
||||
break;
|
||||
apple_pcie_rid2sid_write(port, i, 0);
|
||||
|
|
@ -584,6 +678,9 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
|
|||
list_add_tail(&port->entry, &pcie->ports);
|
||||
init_completion(&pcie->event);
|
||||
|
||||
/* In the success path, we keep a reference to np around */
|
||||
of_node_get(np);
|
||||
|
||||
ret = apple_pcie_port_register_irqs(port);
|
||||
WARN_ON(ret);
|
||||
|
||||
|
|
@ -716,7 +813,7 @@ static void apple_pcie_disable_device(struct pci_host_bridge *bridge, struct pci
|
|||
for_each_set_bit(idx, port->sid_map, port->sid_map_sz) {
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(port->base + PORT_RID2SID(idx));
|
||||
val = readl_relaxed(port_rid2sid_addr(port, idx));
|
||||
if ((val & 0xffff) == rid) {
|
||||
apple_pcie_rid2sid_write(port, idx, 0);
|
||||
bitmap_release_region(port->sid_map, idx, 0);
|
||||
|
|
@ -730,34 +827,14 @@ static void apple_pcie_disable_device(struct pci_host_bridge *bridge, struct pci
|
|||
|
||||
static int apple_pcie_init(struct pci_config_window *cfg)
|
||||
{
|
||||
struct apple_pcie *pcie = cfg->priv;
|
||||
struct device *dev = cfg->parent;
|
||||
struct platform_device *platform = to_platform_device(dev);
|
||||
struct apple_pcie *pcie;
|
||||
int ret;
|
||||
|
||||
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
|
||||
if (!pcie)
|
||||
return -ENOMEM;
|
||||
|
||||
pcie->dev = dev;
|
||||
|
||||
mutex_init(&pcie->lock);
|
||||
|
||||
pcie->base = devm_platform_ioremap_resource(platform, 1);
|
||||
if (IS_ERR(pcie->base))
|
||||
return PTR_ERR(pcie->base);
|
||||
|
||||
cfg->priv = pcie;
|
||||
INIT_LIST_HEAD(&pcie->ports);
|
||||
|
||||
ret = apple_msi_init(pcie);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for_each_child_of_node_scoped(dev->of_node, of_port) {
|
||||
for_each_available_child_of_node_scoped(dev->of_node, of_port) {
|
||||
ret = apple_pcie_setup_port(pcie, of_port);
|
||||
if (ret) {
|
||||
dev_err(pcie->dev, "Port %pOF setup fail: %d\n", of_port, ret);
|
||||
dev_err(dev, "Port %pOF setup fail: %d\n", of_port, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -776,14 +853,44 @@ static const struct pci_ecam_ops apple_pcie_cfg_ecam_ops = {
|
|||
}
|
||||
};
|
||||
|
||||
static int apple_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct apple_pcie *pcie;
|
||||
int ret;
|
||||
|
||||
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
|
||||
if (!pcie)
|
||||
return -ENOMEM;
|
||||
|
||||
pcie->dev = dev;
|
||||
pcie->hw = of_device_get_match_data(dev);
|
||||
if (!pcie->hw)
|
||||
return -ENODEV;
|
||||
pcie->base = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (IS_ERR(pcie->base))
|
||||
return PTR_ERR(pcie->base);
|
||||
|
||||
mutex_init(&pcie->lock);
|
||||
INIT_LIST_HEAD(&pcie->ports);
|
||||
dev_set_drvdata(dev, pcie);
|
||||
|
||||
ret = apple_msi_init(pcie);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return pci_host_common_init(pdev, &apple_pcie_cfg_ecam_ops);
|
||||
}
|
||||
|
||||
static const struct of_device_id apple_pcie_of_match[] = {
|
||||
{ .compatible = "apple,pcie", .data = &apple_pcie_cfg_ecam_ops },
|
||||
{ .compatible = "apple,t6020-pcie", .data = &t602x_hw },
|
||||
{ .compatible = "apple,pcie", .data = &t8103_hw },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, apple_pcie_of_match);
|
||||
|
||||
static struct platform_driver apple_pcie_driver = {
|
||||
.probe = pci_host_common_probe,
|
||||
.probe = apple_pcie_probe,
|
||||
.driver = {
|
||||
.name = "pcie-apple",
|
||||
.of_match_table = apple_pcie_of_match,
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ struct pci_config_window *pci_ecam_create(struct device *dev,
|
|||
goto err_exit_iomap;
|
||||
}
|
||||
|
||||
cfg->priv = dev_get_drvdata(dev);
|
||||
|
||||
if (ops->init) {
|
||||
err = ops->init(cfg);
|
||||
if (err)
|
||||
|
|
|
|||
|
|
@ -97,6 +97,8 @@ extern const struct pci_ecam_ops loongson_pci_ecam_ops; /* Loongson PCIe */
|
|||
#if IS_ENABLED(CONFIG_PCI_HOST_COMMON)
|
||||
/* for DT-based PCI controllers that support ECAM */
|
||||
int pci_host_common_probe(struct platform_device *pdev);
|
||||
int pci_host_common_init(struct platform_device *pdev,
|
||||
const struct pci_ecam_ops *ops);
|
||||
void pci_host_common_remove(struct platform_device *pdev);
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user