mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-24 01:41:39 +00:00
mvebu drivers changes for v3.13
- irqchip - add MSI support for armada-370/XP - pci - add MSI support - add support for Marvell Dove SoCs - mvebu (soc changes depending on the pci and irq changes) - probe mbus windows via DT - probe pcie and clock via DT - docs for mvebu - update gated clock documentation -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.20 (GNU/Linux) iQEcBAABAgAGBQJSVCLqAAoJEAi3KVZQDZAe7WgH/RI+S0rSXcIy6vdF3vAweQhF NNjNKi6ZHsQHAJwva62lzS+zNMLLGpG0NAZ8h+o0I+kFJ8cTYoUDFSiszcVuDKz2 LM/0+JZiMTUmKV8D/XLqYZuQSBgQvxEyxwpcsTOZ2K0q8VQbXAdXt+uY9DUlg9sm /VqiAJzV5I7+V+iStQ/zLSYO9NVWe5TZOGjQiqkZIJa23tZ1mOLXsyWXw6SEZBNk ZbFjwQ6jicE/4fRvcbZk+4EeePI66PrUC1X0MoYQ3TLiHepWbkFdpV7LIBlOedcf 0Feq+C1J6DKfSvuCocfTkjygOpcMieyZ+h512NKRry86sWOq1cuZC7MFqvNrMzg= =AiMP -----END PGP SIGNATURE----- Merge tag 'drivers-3.13' of git://git.infradead.org/linux-mvebu into next/drivers From Jason Cooper: mvebu drivers changes for v3.13 - irqchip - add MSI support for armada-370/XP - pci - add MSI support - add support for Marvell Dove SoCs - mvebu (soc changes depending on the pci and irq changes) - probe mbus windows via DT - probe pcie and clock via DT - docs for mvebu - update gated clock documentation * tag 'drivers-3.13' of git://git.infradead.org/linux-mvebu: ARM: mvebu: fix gated clock documentation ARM: dove: remove legacy pcie and clock init ARM: dove: switch to DT probed mbus address windows PCI: mvebu: add support for Marvell Dove SoCs PCI: mvebu: add support for reset on GPIO PCI: mvebu: remove subsys_initcall PCI: mvebu: increment nports only for registered ports PCI: mvebu: move clock enable before register access PCI: mvebu: add support for MSI irqchip: armada-370-xp: implement MSI support irqchip: armada-370-xp: properly request resources Signed-off-by: Kevin Hilman <khilman@linaro.org>
This commit is contained in:
commit
0397bb85b5
7 changed files with 307 additions and 84 deletions
|
@ -4,6 +4,8 @@ Marvell Armada 370 and Armada XP Interrupt Controller
|
|||
Required properties:
|
||||
- compatible: Should be "marvell,mpic"
|
||||
- interrupt-controller: Identifies the node as an interrupt controller.
|
||||
- msi-controller: Identifies the node as an PCI Message Signaled
|
||||
Interrupt controller.
|
||||
- #interrupt-cells: The number of cells to define the interrupts. Should be 1.
|
||||
The cell is the IRQ number
|
||||
|
||||
|
@ -24,6 +26,7 @@ Example:
|
|||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
interrupt-controller;
|
||||
msi-controller;
|
||||
reg = <0xd0020a00 0x1d0>,
|
||||
<0xd0021070 0x58>;
|
||||
};
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
* Gated Clock bindings for Marvell Orion SoCs
|
||||
* Gated Clock bindings for Marvell EBU SoCs
|
||||
|
||||
Marvell Dove and Kirkwood allow some peripheral clocks to be gated to save
|
||||
some power. The clock consumer should specify the desired clock by having
|
||||
the clock ID in its "clocks" phandle cell. The clock ID is directly mapped to
|
||||
the corresponding clock gating control bit in HW to ease manual clock lookup
|
||||
in datasheet.
|
||||
Marvell Armada 370/XP, Dove and Kirkwood allow some peripheral clocks to be
|
||||
gated to save some power. The clock consumer should specify the desired clock
|
||||
by having the clock ID in its "clocks" phandle cell. The clock ID is directly
|
||||
mapped to the corresponding clock gating control bit in HW to ease manual clock
|
||||
lookup in datasheet.
|
||||
|
||||
The following is a list of provided IDs for Armada 370:
|
||||
ID Clock Peripheral
|
||||
|
@ -94,6 +94,8 @@ ID Clock Peripheral
|
|||
|
||||
Required properties:
|
||||
- compatible : shall be one of the following:
|
||||
"marvell,armada-370-gating-clock" - for Armada 370 SoC clock gating
|
||||
"marvell,armada-xp-gating-clock" - for Armada XP SoC clock gating
|
||||
"marvell,dove-gating-clock" - for Dove SoC clock gating
|
||||
"marvell,kirkwood-gating-clock" - for Kirkwood SoC clock gating
|
||||
- reg : shall be the register address of the Clock Gating Control register
|
||||
|
|
|
@ -5,6 +5,7 @@ Mandatory properties:
|
|||
- compatible: one of the following values:
|
||||
marvell,armada-370-pcie
|
||||
marvell,armada-xp-pcie
|
||||
marvell,dove-pcie
|
||||
marvell,kirkwood-pcie
|
||||
- #address-cells, set to <3>
|
||||
- #size-cells, set to <2>
|
||||
|
@ -14,6 +15,8 @@ Mandatory properties:
|
|||
- ranges: ranges describing the MMIO registers to control the PCIe
|
||||
interfaces, and ranges describing the MBus windows needed to access
|
||||
the memory and I/O regions of each PCIe interface.
|
||||
- msi-parent: Link to the hardware entity that serves as the Message
|
||||
Signaled Interrupt controller for this PCI controller.
|
||||
|
||||
The ranges describing the MMIO registers have the following layout:
|
||||
|
||||
|
@ -74,6 +77,8 @@ and the following optional properties:
|
|||
- marvell,pcie-lane: the physical PCIe lane number, for ports having
|
||||
multiple lanes. If this property is not found, we assume that the
|
||||
value is 0.
|
||||
- reset-gpios: optional gpio to PERST#
|
||||
- reset-delay-us: delay in us to wait after reset de-assertion
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -86,6 +91,7 @@ pcie-controller {
|
|||
#size-cells = <2>;
|
||||
|
||||
bus-range = <0x00 0xff>;
|
||||
msi-parent = <&mpic>;
|
||||
|
||||
ranges =
|
||||
<0x82000000 0 0x40000 MBUS_ID(0xf0, 0x01) 0x40000 0 0x00002000 /* Port 0.0 registers */
|
||||
|
@ -135,6 +141,10 @@ pcie-controller {
|
|||
interrupt-map = <0 0 0 0 &mpic 58>;
|
||||
marvell,pcie-port = <0>;
|
||||
marvell,pcie-lane = <0>;
|
||||
/* low-active PERST# reset on GPIO 25 */
|
||||
reset-gpios = <&gpio0 25 1>;
|
||||
/* wait 20ms for device settle after reset deassertion */
|
||||
reset-delay-us = <20000>;
|
||||
clocks = <&gateclk 5>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
|
|
@ -23,41 +23,12 @@
|
|||
#include <plat/irq.h>
|
||||
#include "common.h"
|
||||
|
||||
/*
|
||||
* There are still devices that doesn't even know about DT,
|
||||
* get clock gates here and add a clock lookup.
|
||||
*/
|
||||
static void __init dove_legacy_clk_init(void)
|
||||
{
|
||||
struct device_node *np = of_find_compatible_node(NULL, NULL,
|
||||
"marvell,dove-gating-clock");
|
||||
struct of_phandle_args clkspec;
|
||||
|
||||
clkspec.np = np;
|
||||
clkspec.args_count = 1;
|
||||
|
||||
clkspec.args[0] = CLOCK_GATING_BIT_PCIE0;
|
||||
orion_clkdev_add("0", "pcie",
|
||||
of_clk_get_from_provider(&clkspec));
|
||||
|
||||
clkspec.args[0] = CLOCK_GATING_BIT_PCIE1;
|
||||
orion_clkdev_add("1", "pcie",
|
||||
of_clk_get_from_provider(&clkspec));
|
||||
}
|
||||
|
||||
static void __init dove_dt_time_init(void)
|
||||
{
|
||||
of_clk_init(NULL);
|
||||
clocksource_of_init();
|
||||
}
|
||||
|
||||
static void __init dove_dt_init_early(void)
|
||||
{
|
||||
mvebu_mbus_init("marvell,dove-mbus",
|
||||
BRIDGE_WINS_BASE, BRIDGE_WINS_SZ,
|
||||
DOVE_MC_WINS_BASE, DOVE_MC_WINS_SZ);
|
||||
}
|
||||
|
||||
static void __init dove_dt_init(void)
|
||||
{
|
||||
pr_info("Dove 88AP510 SoC\n");
|
||||
|
@ -65,14 +36,7 @@ static void __init dove_dt_init(void)
|
|||
#ifdef CONFIG_CACHE_TAUROS2
|
||||
tauros2_init(0);
|
||||
#endif
|
||||
dove_setup_cpu_wins();
|
||||
|
||||
/* Setup clocks for legacy devices */
|
||||
dove_legacy_clk_init();
|
||||
|
||||
/* Internal devices not ported to DT yet */
|
||||
dove_pcie_init(1, 1);
|
||||
|
||||
BUG_ON(mvebu_mbus_dt_init());
|
||||
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
|
||||
}
|
||||
|
||||
|
@ -83,7 +47,6 @@ static const char * const dove_dt_board_compat[] = {
|
|||
|
||||
DT_MACHINE_START(DOVE_DT, "Marvell Dove (Flattened Device Tree)")
|
||||
.map_io = dove_map_io,
|
||||
.init_early = dove_dt_init_early,
|
||||
.init_time = dove_dt_time_init,
|
||||
.init_machine = dove_dt_init,
|
||||
.restart = dove_restart,
|
||||
|
|
|
@ -21,7 +21,10 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/msi.h>
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/exception.h>
|
||||
#include <asm/smp_plat.h>
|
||||
|
@ -51,12 +54,22 @@
|
|||
#define IPI_DOORBELL_START (0)
|
||||
#define IPI_DOORBELL_END (8)
|
||||
#define IPI_DOORBELL_MASK 0xFF
|
||||
#define PCI_MSI_DOORBELL_START (16)
|
||||
#define PCI_MSI_DOORBELL_NR (16)
|
||||
#define PCI_MSI_DOORBELL_END (32)
|
||||
#define PCI_MSI_DOORBELL_MASK 0xFFFF0000
|
||||
|
||||
static DEFINE_RAW_SPINLOCK(irq_controller_lock);
|
||||
|
||||
static void __iomem *per_cpu_int_base;
|
||||
static void __iomem *main_int_base;
|
||||
static struct irq_domain *armada_370_xp_mpic_domain;
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
static struct irq_domain *armada_370_xp_msi_domain;
|
||||
static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
|
||||
static DEFINE_MUTEX(msi_used_lock);
|
||||
static phys_addr_t msi_doorbell_addr;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* In SMP mode:
|
||||
|
@ -87,6 +100,144 @@ static void armada_370_xp_irq_unmask(struct irq_data *d)
|
|||
ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
|
||||
static int armada_370_xp_alloc_msi(void)
|
||||
{
|
||||
int hwirq;
|
||||
|
||||
mutex_lock(&msi_used_lock);
|
||||
hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR);
|
||||
if (hwirq >= PCI_MSI_DOORBELL_NR)
|
||||
hwirq = -ENOSPC;
|
||||
else
|
||||
set_bit(hwirq, msi_used);
|
||||
mutex_unlock(&msi_used_lock);
|
||||
|
||||
return hwirq;
|
||||
}
|
||||
|
||||
static void armada_370_xp_free_msi(int hwirq)
|
||||
{
|
||||
mutex_lock(&msi_used_lock);
|
||||
if (!test_bit(hwirq, msi_used))
|
||||
pr_err("trying to free unused MSI#%d\n", hwirq);
|
||||
else
|
||||
clear_bit(hwirq, msi_used);
|
||||
mutex_unlock(&msi_used_lock);
|
||||
}
|
||||
|
||||
static int armada_370_xp_setup_msi_irq(struct msi_chip *chip,
|
||||
struct pci_dev *pdev,
|
||||
struct msi_desc *desc)
|
||||
{
|
||||
struct msi_msg msg;
|
||||
irq_hw_number_t hwirq;
|
||||
int virq;
|
||||
|
||||
hwirq = armada_370_xp_alloc_msi();
|
||||
if (hwirq < 0)
|
||||
return hwirq;
|
||||
|
||||
virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq);
|
||||
if (!virq) {
|
||||
armada_370_xp_free_msi(hwirq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq_set_msi_desc(virq, desc);
|
||||
|
||||
msg.address_lo = msi_doorbell_addr;
|
||||
msg.address_hi = 0;
|
||||
msg.data = 0xf00 | (hwirq + 16);
|
||||
|
||||
write_msi_msg(virq, &msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip,
|
||||
unsigned int irq)
|
||||
{
|
||||
struct irq_data *d = irq_get_irq_data(irq);
|
||||
irq_dispose_mapping(irq);
|
||||
armada_370_xp_free_msi(d->hwirq);
|
||||
}
|
||||
|
||||
static struct irq_chip armada_370_xp_msi_irq_chip = {
|
||||
.name = "armada_370_xp_msi_irq",
|
||||
.irq_enable = unmask_msi_irq,
|
||||
.irq_disable = mask_msi_irq,
|
||||
.irq_mask = mask_msi_irq,
|
||||
.irq_unmask = unmask_msi_irq,
|
||||
};
|
||||
|
||||
static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip,
|
||||
handle_simple_irq);
|
||||
set_irq_flags(virq, IRQF_VALID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops armada_370_xp_msi_irq_ops = {
|
||||
.map = armada_370_xp_msi_map,
|
||||
};
|
||||
|
||||
static int armada_370_xp_msi_init(struct device_node *node,
|
||||
phys_addr_t main_int_phys_base)
|
||||
{
|
||||
struct msi_chip *msi_chip;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
msi_doorbell_addr = main_int_phys_base +
|
||||
ARMADA_370_XP_SW_TRIG_INT_OFFS;
|
||||
|
||||
msi_chip = kzalloc(sizeof(*msi_chip), GFP_KERNEL);
|
||||
if (!msi_chip)
|
||||
return -ENOMEM;
|
||||
|
||||
msi_chip->setup_irq = armada_370_xp_setup_msi_irq;
|
||||
msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq;
|
||||
msi_chip->of_node = node;
|
||||
|
||||
armada_370_xp_msi_domain =
|
||||
irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
|
||||
&armada_370_xp_msi_irq_ops,
|
||||
NULL);
|
||||
if (!armada_370_xp_msi_domain) {
|
||||
kfree(msi_chip);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = of_pci_msi_chip_add(msi_chip);
|
||||
if (ret < 0) {
|
||||
irq_domain_remove(armada_370_xp_msi_domain);
|
||||
kfree(msi_chip);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS)
|
||||
| PCI_MSI_DOORBELL_MASK;
|
||||
|
||||
writel(reg, per_cpu_int_base +
|
||||
ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
|
||||
|
||||
/* Unmask IPI interrupt */
|
||||
writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int armada_370_xp_msi_init(struct device_node *node,
|
||||
phys_addr_t main_int_phys_base)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static int armada_xp_set_affinity(struct irq_data *d,
|
||||
const struct cpumask *mask_val, bool force)
|
||||
|
@ -214,12 +365,39 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
|
|||
if (irqnr > 1022)
|
||||
break;
|
||||
|
||||
if (irqnr > 0) {
|
||||
if (irqnr > 1) {
|
||||
irqnr = irq_find_mapping(armada_370_xp_mpic_domain,
|
||||
irqnr);
|
||||
handle_IRQ(irqnr, regs);
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI_MSI
|
||||
/* MSI handling */
|
||||
if (irqnr == 1) {
|
||||
u32 msimask, msinr;
|
||||
|
||||
msimask = readl_relaxed(per_cpu_int_base +
|
||||
ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
|
||||
& PCI_MSI_DOORBELL_MASK;
|
||||
|
||||
writel(~PCI_MSI_DOORBELL_MASK, per_cpu_int_base +
|
||||
ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
|
||||
|
||||
for (msinr = PCI_MSI_DOORBELL_START;
|
||||
msinr < PCI_MSI_DOORBELL_END; msinr++) {
|
||||
int irq;
|
||||
|
||||
if (!(msimask & BIT(msinr)))
|
||||
continue;
|
||||
|
||||
irq = irq_find_mapping(armada_370_xp_msi_domain,
|
||||
msinr - 16);
|
||||
handle_IRQ(irq, regs);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* IPI Handling */
|
||||
if (irqnr == 0) {
|
||||
|
@ -248,12 +426,25 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
|
|||
static int __init armada_370_xp_mpic_of_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
struct resource main_int_res, per_cpu_int_res;
|
||||
u32 control;
|
||||
|
||||
main_int_base = of_iomap(node, 0);
|
||||
per_cpu_int_base = of_iomap(node, 1);
|
||||
BUG_ON(of_address_to_resource(node, 0, &main_int_res));
|
||||
BUG_ON(of_address_to_resource(node, 1, &per_cpu_int_res));
|
||||
|
||||
BUG_ON(!request_mem_region(main_int_res.start,
|
||||
resource_size(&main_int_res),
|
||||
node->full_name));
|
||||
BUG_ON(!request_mem_region(per_cpu_int_res.start,
|
||||
resource_size(&per_cpu_int_res),
|
||||
node->full_name));
|
||||
|
||||
main_int_base = ioremap(main_int_res.start,
|
||||
resource_size(&main_int_res));
|
||||
BUG_ON(!main_int_base);
|
||||
|
||||
per_cpu_int_base = ioremap(per_cpu_int_res.start,
|
||||
resource_size(&per_cpu_int_res));
|
||||
BUG_ON(!per_cpu_int_base);
|
||||
|
||||
control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL);
|
||||
|
@ -262,8 +453,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
|
|||
irq_domain_add_linear(node, (control >> 2) & 0x3ff,
|
||||
&armada_370_xp_mpic_irq_ops, NULL);
|
||||
|
||||
if (!armada_370_xp_mpic_domain)
|
||||
panic("Unable to add Armada_370_Xp MPIC irq domain (DT)\n");
|
||||
BUG_ON(!armada_370_xp_mpic_domain);
|
||||
|
||||
irq_set_default_host(armada_370_xp_mpic_domain);
|
||||
|
||||
|
@ -280,6 +470,8 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
|
|||
|
||||
#endif
|
||||
|
||||
armada_370_xp_msi_init(node, main_int_res.start);
|
||||
|
||||
set_handle_irq(armada_370_xp_handle_irq);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -3,7 +3,7 @@ menu "PCI host controller drivers"
|
|||
|
||||
config PCI_MVEBU
|
||||
bool "Marvell EBU PCIe controller"
|
||||
depends on ARCH_MVEBU || ARCH_KIRKWOOD
|
||||
depends on ARCH_MVEBU || ARCH_DOVE || ARCH_KIRKWOOD
|
||||
depends on OF
|
||||
|
||||
config PCIE_DW
|
||||
|
|
|
@ -9,13 +9,17 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mbus.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
/*
|
||||
|
@ -103,6 +107,7 @@ struct mvebu_pcie_port;
|
|||
struct mvebu_pcie {
|
||||
struct platform_device *pdev;
|
||||
struct mvebu_pcie_port *ports;
|
||||
struct msi_chip *msi;
|
||||
struct resource io;
|
||||
struct resource realio;
|
||||
struct resource mem;
|
||||
|
@ -124,6 +129,9 @@ struct mvebu_pcie_port {
|
|||
unsigned int io_target;
|
||||
unsigned int io_attr;
|
||||
struct clk *clk;
|
||||
int reset_gpio;
|
||||
int reset_active_low;
|
||||
char *reset_name;
|
||||
struct mvebu_sw_pci_bridge bridge;
|
||||
struct device_node *dn;
|
||||
struct mvebu_pcie *pcie;
|
||||
|
@ -163,7 +171,7 @@ static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr)
|
|||
* BAR[0,2] -> disabled, BAR[1] -> covers all DRAM banks
|
||||
* WIN[0-3] -> DRAM bank[0-3]
|
||||
*/
|
||||
static void __init mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
|
||||
static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
|
||||
{
|
||||
const struct mbus_dram_target_info *dram;
|
||||
u32 size;
|
||||
|
@ -215,7 +223,7 @@ static void __init mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
|
|||
port->base + PCIE_BAR_CTRL_OFF(1));
|
||||
}
|
||||
|
||||
static void __init mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
|
||||
static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
|
||||
{
|
||||
u16 cmd;
|
||||
u32 mask;
|
||||
|
@ -626,7 +634,7 @@ static struct pci_ops mvebu_pcie_ops = {
|
|||
.write = mvebu_pcie_wr_conf,
|
||||
};
|
||||
|
||||
static int __init mvebu_pcie_setup(int nr, struct pci_sys_data *sys)
|
||||
static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys)
|
||||
{
|
||||
struct mvebu_pcie *pcie = sys_to_pcie(sys);
|
||||
int i;
|
||||
|
@ -645,7 +653,7 @@ static int __init mvebu_pcie_setup(int nr, struct pci_sys_data *sys)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int __init mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
|
||||
static int mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
|
||||
{
|
||||
struct of_irq oirq;
|
||||
int ret;
|
||||
|
@ -673,6 +681,12 @@ static struct pci_bus *mvebu_pcie_scan_bus(int nr, struct pci_sys_data *sys)
|
|||
return bus;
|
||||
}
|
||||
|
||||
void mvebu_pcie_add_bus(struct pci_bus *bus)
|
||||
{
|
||||
struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata);
|
||||
bus->msi = pcie->msi;
|
||||
}
|
||||
|
||||
resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev,
|
||||
const struct resource *res,
|
||||
resource_size_t start,
|
||||
|
@ -696,7 +710,7 @@ resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev,
|
|||
return start;
|
||||
}
|
||||
|
||||
static void __init mvebu_pcie_enable(struct mvebu_pcie *pcie)
|
||||
static void mvebu_pcie_enable(struct mvebu_pcie *pcie)
|
||||
{
|
||||
struct hw_pci hw;
|
||||
|
||||
|
@ -709,6 +723,7 @@ static void __init mvebu_pcie_enable(struct mvebu_pcie *pcie)
|
|||
hw.map_irq = mvebu_pcie_map_irq;
|
||||
hw.ops = &mvebu_pcie_ops;
|
||||
hw.align_resource = mvebu_pcie_align_resource;
|
||||
hw.add_bus = mvebu_pcie_add_bus;
|
||||
|
||||
pci_common_init(&hw);
|
||||
}
|
||||
|
@ -718,10 +733,8 @@ static void __init mvebu_pcie_enable(struct mvebu_pcie *pcie)
|
|||
* <...> property for one that matches the given port/lane. Once
|
||||
* found, maps it.
|
||||
*/
|
||||
static void __iomem * __init
|
||||
mvebu_pcie_map_registers(struct platform_device *pdev,
|
||||
struct device_node *np,
|
||||
struct mvebu_pcie_port *port)
|
||||
static void __iomem *mvebu_pcie_map_registers(struct platform_device *pdev,
|
||||
struct device_node *np, struct mvebu_pcie_port *port)
|
||||
{
|
||||
struct resource regs;
|
||||
int ret = 0;
|
||||
|
@ -777,7 +790,22 @@ static int mvebu_get_tgt_attr(struct device_node *np, int devfn,
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int __init mvebu_pcie_probe(struct platform_device *pdev)
|
||||
static void mvebu_pcie_msi_enable(struct mvebu_pcie *pcie)
|
||||
{
|
||||
struct device_node *msi_node;
|
||||
|
||||
msi_node = of_parse_phandle(pcie->pdev->dev.of_node,
|
||||
"msi-parent", 0);
|
||||
if (!msi_node)
|
||||
return;
|
||||
|
||||
pcie->msi = of_pci_find_msi_chip_by_node(msi_node);
|
||||
|
||||
if (pcie->msi)
|
||||
pcie->msi->dev = &pcie->pdev->dev;
|
||||
}
|
||||
|
||||
static int mvebu_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mvebu_pcie *pcie;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
|
@ -790,6 +818,7 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
pcie->pdev = pdev;
|
||||
platform_set_drvdata(pdev, pcie);
|
||||
|
||||
/* Get the PCIe memory and I/O aperture */
|
||||
mvebu_mbus_get_pcie_mem_aperture(&pcie->mem);
|
||||
|
@ -818,13 +847,14 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for_each_child_of_node(pdev->dev.of_node, child) {
|
||||
if (!of_device_is_available(child))
|
||||
continue;
|
||||
pcie->nports++;
|
||||
i++;
|
||||
}
|
||||
|
||||
pcie->ports = devm_kzalloc(&pdev->dev, pcie->nports *
|
||||
pcie->ports = devm_kzalloc(&pdev->dev, i *
|
||||
sizeof(struct mvebu_pcie_port),
|
||||
GFP_KERNEL);
|
||||
if (!pcie->ports)
|
||||
|
@ -833,6 +863,7 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
|
|||
i = 0;
|
||||
for_each_child_of_node(pdev->dev.of_node, child) {
|
||||
struct mvebu_pcie_port *port = &pcie->ports[i];
|
||||
enum of_gpio_flags flags;
|
||||
|
||||
if (!of_device_is_available(child))
|
||||
continue;
|
||||
|
@ -873,11 +904,47 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
|
|||
continue;
|
||||
}
|
||||
|
||||
port->reset_gpio = of_get_named_gpio_flags(child,
|
||||
"reset-gpios", 0, &flags);
|
||||
if (gpio_is_valid(port->reset_gpio)) {
|
||||
u32 reset_udelay = 20000;
|
||||
|
||||
port->reset_active_low = flags & OF_GPIO_ACTIVE_LOW;
|
||||
port->reset_name = kasprintf(GFP_KERNEL,
|
||||
"pcie%d.%d-reset", port->port, port->lane);
|
||||
of_property_read_u32(child, "reset-delay-us",
|
||||
&reset_udelay);
|
||||
|
||||
ret = devm_gpio_request_one(&pdev->dev,
|
||||
port->reset_gpio, GPIOF_DIR_OUT, port->reset_name);
|
||||
if (ret) {
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
continue;
|
||||
}
|
||||
|
||||
gpio_set_value(port->reset_gpio,
|
||||
(port->reset_active_low) ? 1 : 0);
|
||||
msleep(reset_udelay/1000);
|
||||
}
|
||||
|
||||
port->clk = of_clk_get_by_name(child, NULL);
|
||||
if (IS_ERR(port->clk)) {
|
||||
dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n",
|
||||
port->port, port->lane);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(port->clk);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
port->base = mvebu_pcie_map_registers(pdev, child, port);
|
||||
if (IS_ERR(port->base)) {
|
||||
dev_err(&pdev->dev, "PCIe%d.%d: cannot map registers\n",
|
||||
port->port, port->lane);
|
||||
port->base = NULL;
|
||||
clk_disable_unprepare(port->clk);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -893,25 +960,14 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
|
|||
port->port, port->lane);
|
||||
}
|
||||
|
||||
port->clk = of_clk_get_by_name(child, NULL);
|
||||
if (IS_ERR(port->clk)) {
|
||||
dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n",
|
||||
port->port, port->lane);
|
||||
iounmap(port->base);
|
||||
port->haslink = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
port->dn = child;
|
||||
|
||||
clk_prepare_enable(port->clk);
|
||||
spin_lock_init(&port->conf_lock);
|
||||
|
||||
mvebu_sw_pci_bridge_init(port);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
pcie->nports = i;
|
||||
mvebu_pcie_msi_enable(pcie);
|
||||
mvebu_pcie_enable(pcie);
|
||||
|
||||
return 0;
|
||||
|
@ -920,6 +976,7 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
|
|||
static const struct of_device_id mvebu_pcie_of_match_table[] = {
|
||||
{ .compatible = "marvell,armada-xp-pcie", },
|
||||
{ .compatible = "marvell,armada-370-pcie", },
|
||||
{ .compatible = "marvell,dove-pcie", },
|
||||
{ .compatible = "marvell,kirkwood-pcie", },
|
||||
{},
|
||||
};
|
||||
|
@ -931,16 +988,12 @@ static struct platform_driver mvebu_pcie_driver = {
|
|||
.name = "mvebu-pcie",
|
||||
.of_match_table =
|
||||
of_match_ptr(mvebu_pcie_of_match_table),
|
||||
/* driver unloading/unbinding currently not supported */
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = mvebu_pcie_probe,
|
||||
};
|
||||
|
||||
static int __init mvebu_pcie_init(void)
|
||||
{
|
||||
return platform_driver_probe(&mvebu_pcie_driver,
|
||||
mvebu_pcie_probe);
|
||||
}
|
||||
|
||||
subsys_initcall(mvebu_pcie_init);
|
||||
module_platform_driver(mvebu_pcie_driver);
|
||||
|
||||
MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
|
||||
MODULE_DESCRIPTION("Marvell EBU PCIe driver");
|
||||
|
|
Loading…
Reference in a new issue