summaryrefslogtreecommitdiff
path: root/drivers/bus
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2013-12-19 15:08:03 +0100
committerJiri Kosina <jkosina@suse.cz>2013-12-19 15:08:32 +0100
commite23c34bb41da65f354fb7eee04300c56ee48f60c (patch)
tree549fbe449d55273b81ef104a9755109bf4ae7817 /drivers/bus
parentb481c2cb3534c85dca625973b33eba15f9af3e4c (diff)
parent319e2e3f63c348a9b66db4667efa73178e18b17d (diff)
Merge branch 'master' into for-next
Sync with Linus' tree to be able to apply fixes on top of newer things in tree (efi-stub). Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/bus')
-rw-r--r--drivers/bus/Kconfig3
-rw-r--r--drivers/bus/arm-cci.c623
-rw-r--r--drivers/bus/imx-weim.c129
-rw-r--r--drivers/bus/mvebu-mbus.c447
4 files changed, 952 insertions, 250 deletions
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 1f70e84b442c..552373c4e362 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -8,10 +8,9 @@ config IMX_WEIM
bool "Freescale EIM DRIVER"
depends on ARCH_MXC
help
- Driver for i.MX6 WEIM controller.
+ Driver for i.MX WEIM controller.
The WEIM(Wireless External Interface Module) works like a bus.
You can attach many different devices on it, such as NOR, onenand.
- But now, we only support the Parallel NOR.
config MVEBU_MBUS
bool
diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c
index 200926699778..b6739cb78e32 100644
--- a/drivers/bus/arm-cci.c
+++ b/drivers/bus/arm-cci.c
@@ -18,11 +18,21 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <asm/cacheflush.h>
+#include <asm/irq_regs.h>
+#include <asm/pmu.h>
#include <asm/smp_plat.h>
+#define DRIVER_NAME "CCI-400"
+#define DRIVER_NAME_PMU DRIVER_NAME " PMU"
+#define PMU_NAME "CCI_400"
+
#define CCI_PORT_CTRL 0x0
#define CCI_CTRL_STATUS 0xc
@@ -54,6 +64,568 @@ static unsigned int nb_cci_ports;
static void __iomem *cci_ctrl_base;
static unsigned long cci_ctrl_phys;
+#ifdef CONFIG_HW_PERF_EVENTS
+
+#define CCI_PMCR 0x0100
+#define CCI_PID2 0x0fe8
+
+#define CCI_PMCR_CEN 0x00000001
+#define CCI_PMCR_NCNT_MASK 0x0000f800
+#define CCI_PMCR_NCNT_SHIFT 11
+
+#define CCI_PID2_REV_MASK 0xf0
+#define CCI_PID2_REV_SHIFT 4
+
+/* Port ids */
+#define CCI_PORT_S0 0
+#define CCI_PORT_S1 1
+#define CCI_PORT_S2 2
+#define CCI_PORT_S3 3
+#define CCI_PORT_S4 4
+#define CCI_PORT_M0 5
+#define CCI_PORT_M1 6
+#define CCI_PORT_M2 7
+
+#define CCI_REV_R0 0
+#define CCI_REV_R1 1
+#define CCI_REV_R0_P4 4
+#define CCI_REV_R1_P2 6
+
+#define CCI_PMU_EVT_SEL 0x000
+#define CCI_PMU_CNTR 0x004
+#define CCI_PMU_CNTR_CTRL 0x008
+#define CCI_PMU_OVRFLW 0x00c
+
+#define CCI_PMU_OVRFLW_FLAG 1
+
+#define CCI_PMU_CNTR_BASE(idx) ((idx) * SZ_4K)
+
+/*
+ * Instead of an event id to monitor CCI cycles, a dedicated counter is
+ * provided. Use 0xff to represent CCI cycles and hope that no future revisions
+ * make use of this event in hardware.
+ */
+enum cci400_perf_events {
+ CCI_PMU_CYCLES = 0xff
+};
+
+#define CCI_PMU_EVENT_MASK 0xff
+#define CCI_PMU_EVENT_SOURCE(event) ((event >> 5) & 0x7)
+#define CCI_PMU_EVENT_CODE(event) (event & 0x1f)
+
+#define CCI_PMU_MAX_HW_EVENTS 5 /* CCI PMU has 4 counters + 1 cycle counter */
+
+#define CCI_PMU_CYCLE_CNTR_IDX 0
+#define CCI_PMU_CNTR0_IDX 1
+#define CCI_PMU_CNTR_LAST(cci_pmu) (CCI_PMU_CYCLE_CNTR_IDX + cci_pmu->num_events - 1)
+
+/*
+ * CCI PMU event id is an 8-bit value made of two parts - bits 7:5 for one of 8
+ * ports and bits 4:0 are event codes. There are different event codes
+ * associated with each port type.
+ *
+ * Additionally, the range of events associated with the port types changed
+ * between Rev0 and Rev1.
+ *
+ * The constants below define the range of valid codes for each port type for
+ * the different revisions and are used to validate the event to be monitored.
+ */
+
+#define CCI_REV_R0_SLAVE_PORT_MIN_EV 0x00
+#define CCI_REV_R0_SLAVE_PORT_MAX_EV 0x13
+#define CCI_REV_R0_MASTER_PORT_MIN_EV 0x14
+#define CCI_REV_R0_MASTER_PORT_MAX_EV 0x1a
+
+#define CCI_REV_R1_SLAVE_PORT_MIN_EV 0x00
+#define CCI_REV_R1_SLAVE_PORT_MAX_EV 0x14
+#define CCI_REV_R1_MASTER_PORT_MIN_EV 0x00
+#define CCI_REV_R1_MASTER_PORT_MAX_EV 0x11
+
+struct pmu_port_event_ranges {
+ u8 slave_min;
+ u8 slave_max;
+ u8 master_min;
+ u8 master_max;
+};
+
+static struct pmu_port_event_ranges port_event_range[] = {
+ [CCI_REV_R0] = {
+ .slave_min = CCI_REV_R0_SLAVE_PORT_MIN_EV,
+ .slave_max = CCI_REV_R0_SLAVE_PORT_MAX_EV,
+ .master_min = CCI_REV_R0_MASTER_PORT_MIN_EV,
+ .master_max = CCI_REV_R0_MASTER_PORT_MAX_EV,
+ },
+ [CCI_REV_R1] = {
+ .slave_min = CCI_REV_R1_SLAVE_PORT_MIN_EV,
+ .slave_max = CCI_REV_R1_SLAVE_PORT_MAX_EV,
+ .master_min = CCI_REV_R1_MASTER_PORT_MIN_EV,
+ .master_max = CCI_REV_R1_MASTER_PORT_MAX_EV,
+ },
+};
+
+struct cci_pmu_drv_data {
+ void __iomem *base;
+ struct arm_pmu *cci_pmu;
+ int nr_irqs;
+ int irqs[CCI_PMU_MAX_HW_EVENTS];
+ unsigned long active_irqs;
+ struct perf_event *events[CCI_PMU_MAX_HW_EVENTS];
+ unsigned long used_mask[BITS_TO_LONGS(CCI_PMU_MAX_HW_EVENTS)];
+ struct pmu_port_event_ranges *port_ranges;
+ struct pmu_hw_events hw_events;
+};
+static struct cci_pmu_drv_data *pmu;
+
+static bool is_duplicate_irq(int irq, int *irqs, int nr_irqs)
+{
+ int i;
+
+ for (i = 0; i < nr_irqs; i++)
+ if (irq == irqs[i])
+ return true;
+
+ return false;
+}
+
+static int probe_cci_revision(void)
+{
+ int rev;
+ rev = readl_relaxed(cci_ctrl_base + CCI_PID2) & CCI_PID2_REV_MASK;
+ rev >>= CCI_PID2_REV_SHIFT;
+
+ if (rev <= CCI_REV_R0_P4)
+ return CCI_REV_R0;
+ else if (rev <= CCI_REV_R1_P2)
+ return CCI_REV_R1;
+
+ return -ENOENT;
+}
+
+static struct pmu_port_event_ranges *port_range_by_rev(void)
+{
+ int rev = probe_cci_revision();
+
+ if (rev < 0)
+ return NULL;
+
+ return &port_event_range[rev];
+}
+
+static int pmu_is_valid_slave_event(u8 ev_code)
+{
+ return pmu->port_ranges->slave_min <= ev_code &&
+ ev_code <= pmu->port_ranges->slave_max;
+}
+
+static int pmu_is_valid_master_event(u8 ev_code)
+{
+ return pmu->port_ranges->master_min <= ev_code &&
+ ev_code <= pmu->port_ranges->master_max;
+}
+
+static int pmu_validate_hw_event(u8 hw_event)
+{
+ u8 ev_source = CCI_PMU_EVENT_SOURCE(hw_event);
+ u8 ev_code = CCI_PMU_EVENT_CODE(hw_event);
+
+ switch (ev_source) {
+ case CCI_PORT_S0:
+ case CCI_PORT_S1:
+ case CCI_PORT_S2:
+ case CCI_PORT_S3:
+ case CCI_PORT_S4:
+ /* Slave Interface */
+ if (pmu_is_valid_slave_event(ev_code))
+ return hw_event;
+ break;
+ case CCI_PORT_M0:
+ case CCI_PORT_M1:
+ case CCI_PORT_M2:
+ /* Master Interface */
+ if (pmu_is_valid_master_event(ev_code))
+ return hw_event;
+ break;
+ }
+
+ return -ENOENT;
+}
+
+static int pmu_is_valid_counter(struct arm_pmu *cci_pmu, int idx)
+{
+ return CCI_PMU_CYCLE_CNTR_IDX <= idx &&
+ idx <= CCI_PMU_CNTR_LAST(cci_pmu);
+}
+
+static u32 pmu_read_register(int idx, unsigned int offset)
+{
+ return readl_relaxed(pmu->base + CCI_PMU_CNTR_BASE(idx) + offset);
+}
+
+static void pmu_write_register(u32 value, int idx, unsigned int offset)
+{
+ return writel_relaxed(value, pmu->base + CCI_PMU_CNTR_BASE(idx) + offset);
+}
+
+static void pmu_disable_counter(int idx)
+{
+ pmu_write_register(0, idx, CCI_PMU_CNTR_CTRL);
+}
+
+static void pmu_enable_counter(int idx)
+{
+ pmu_write_register(1, idx, CCI_PMU_CNTR_CTRL);
+}
+
+static void pmu_set_event(int idx, unsigned long event)
+{
+ event &= CCI_PMU_EVENT_MASK;
+ pmu_write_register(event, idx, CCI_PMU_EVT_SEL);
+}
+
+static u32 pmu_get_max_counters(void)
+{
+ u32 n_cnts = (readl_relaxed(cci_ctrl_base + CCI_PMCR) &
+ CCI_PMCR_NCNT_MASK) >> CCI_PMCR_NCNT_SHIFT;
+
+ /* add 1 for cycle counter */
+ return n_cnts + 1;
+}
+
+static struct pmu_hw_events *pmu_get_hw_events(void)
+{
+ return &pmu->hw_events;
+}
+
+static int pmu_get_event_idx(struct pmu_hw_events *hw, struct perf_event *event)
+{
+ struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
+ struct hw_perf_event *hw_event = &event->hw;
+ unsigned long cci_event = hw_event->config_base & CCI_PMU_EVENT_MASK;
+ int idx;
+
+ if (cci_event == CCI_PMU_CYCLES) {
+ if (test_and_set_bit(CCI_PMU_CYCLE_CNTR_IDX, hw->used_mask))
+ return -EAGAIN;
+
+ return CCI_PMU_CYCLE_CNTR_IDX;
+ }
+
+ for (idx = CCI_PMU_CNTR0_IDX; idx <= CCI_PMU_CNTR_LAST(cci_pmu); ++idx)
+ if (!test_and_set_bit(idx, hw->used_mask))
+ return idx;
+
+ /* No counters available */
+ return -EAGAIN;
+}
+
+static int pmu_map_event(struct perf_event *event)
+{
+ int mapping;
+ u8 config = event->attr.config & CCI_PMU_EVENT_MASK;
+
+ if (event->attr.type < PERF_TYPE_MAX)
+ return -ENOENT;
+
+ if (config == CCI_PMU_CYCLES)
+ mapping = config;
+ else
+ mapping = pmu_validate_hw_event(config);
+
+ return mapping;
+}
+
+static int pmu_request_irq(struct arm_pmu *cci_pmu, irq_handler_t handler)
+{
+ int i;
+ struct platform_device *pmu_device = cci_pmu->plat_device;
+
+ if (unlikely(!pmu_device))
+ return -ENODEV;
+
+ if (pmu->nr_irqs < 1) {
+ dev_err(&pmu_device->dev, "no irqs for CCI PMUs defined\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Register all available CCI PMU interrupts. In the interrupt handler
+ * we iterate over the counters checking for interrupt source (the
+ * overflowing counter) and clear it.
+ *
+ * This should allow handling of non-unique interrupt for the counters.
+ */
+ for (i = 0; i < pmu->nr_irqs; i++) {
+ int err = request_irq(pmu->irqs[i], handler, IRQF_SHARED,
+ "arm-cci-pmu", cci_pmu);
+ if (err) {
+ dev_err(&pmu_device->dev, "unable to request IRQ%d for ARM CCI PMU counters\n",
+ pmu->irqs[i]);
+ return err;
+ }
+
+ set_bit(i, &pmu->active_irqs);
+ }
+
+ return 0;
+}
+
+static irqreturn_t pmu_handle_irq(int irq_num, void *dev)
+{
+ unsigned long flags;
+ struct arm_pmu *cci_pmu = (struct arm_pmu *)dev;
+ struct pmu_hw_events *events = cci_pmu->get_hw_events();
+ struct perf_sample_data data;
+ struct pt_regs *regs;
+ int idx, handled = IRQ_NONE;
+
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
+ regs = get_irq_regs();
+ /*
+ * Iterate over counters and update the corresponding perf events.
+ * This should work regardless of whether we have per-counter overflow
+ * interrupt or a combined overflow interrupt.
+ */
+ for (idx = CCI_PMU_CYCLE_CNTR_IDX; idx <= CCI_PMU_CNTR_LAST(cci_pmu); idx++) {
+ struct perf_event *event = events->events[idx];
+ struct hw_perf_event *hw_counter;
+
+ if (!event)
+ continue;
+
+ hw_counter = &event->hw;
+
+ /* Did this counter overflow? */
+ if (!pmu_read_register(idx, CCI_PMU_OVRFLW) & CCI_PMU_OVRFLW_FLAG)
+ continue;
+
+ pmu_write_register(CCI_PMU_OVRFLW_FLAG, idx, CCI_PMU_OVRFLW);
+
+ handled = IRQ_HANDLED;
+
+ armpmu_event_update(event);
+ perf_sample_data_init(&data, 0, hw_counter->last_period);
+ if (!armpmu_event_set_period(event))
+ continue;
+
+ if (perf_event_overflow(event, &data, regs))
+ cci_pmu->disable(event);
+ }
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+
+ return IRQ_RETVAL(handled);
+}
+
+static void pmu_free_irq(struct arm_pmu *cci_pmu)
+{
+ int i;
+
+ for (i = 0; i < pmu->nr_irqs; i++) {
+ if (!test_and_clear_bit(i, &pmu->active_irqs))
+ continue;
+
+ free_irq(pmu->irqs[i], cci_pmu);
+ }
+}
+
+static void pmu_enable_event(struct perf_event *event)
+{
+ unsigned long flags;
+ struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
+ struct pmu_hw_events *events = cci_pmu->get_hw_events();
+ struct hw_perf_event *hw_counter = &event->hw;
+ int idx = hw_counter->idx;
+
+ if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
+ dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
+ return;
+ }
+
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
+
+ /* Configure the event to count, unless you are counting cycles */
+ if (idx != CCI_PMU_CYCLE_CNTR_IDX)
+ pmu_set_event(idx, hw_counter->config_base);
+
+ pmu_enable_counter(idx);
+
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static void pmu_disable_event(struct perf_event *event)
+{
+ struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
+ struct hw_perf_event *hw_counter = &event->hw;
+ int idx = hw_counter->idx;
+
+ if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
+ dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
+ return;
+ }
+
+ pmu_disable_counter(idx);
+}
+
+static void pmu_start(struct arm_pmu *cci_pmu)
+{
+ u32 val;
+ unsigned long flags;
+ struct pmu_hw_events *events = cci_pmu->get_hw_events();
+
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
+
+ /* Enable all the PMU counters. */
+ val = readl_relaxed(cci_ctrl_base + CCI_PMCR) | CCI_PMCR_CEN;
+ writel(val, cci_ctrl_base + CCI_PMCR);
+
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static void pmu_stop(struct arm_pmu *cci_pmu)
+{
+ u32 val;
+ unsigned long flags;
+ struct pmu_hw_events *events = cci_pmu->get_hw_events();
+
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
+
+ /* Disable all the PMU counters. */
+ val = readl_relaxed(cci_ctrl_base + CCI_PMCR) & ~CCI_PMCR_CEN;
+ writel(val, cci_ctrl_base + CCI_PMCR);
+
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static u32 pmu_read_counter(struct perf_event *event)
+{
+ struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
+ struct hw_perf_event *hw_counter = &event->hw;
+ int idx = hw_counter->idx;
+ u32 value;
+
+ if (unlikely(!pmu_is_valid_counter(cci_pmu, idx))) {
+ dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
+ return 0;
+ }
+ value = pmu_read_register(idx, CCI_PMU_CNTR);
+
+ return value;
+}
+
+static void pmu_write_counter(struct perf_event *event, u32 value)
+{
+ struct arm_pmu *cci_pmu = to_arm_pmu(event->pmu);
+ struct hw_perf_event *hw_counter = &event->hw;
+ int idx = hw_counter->idx;
+
+ if (unlikely(!pmu_is_valid_counter(cci_pmu, idx)))
+ dev_err(&cci_pmu->plat_device->dev, "Invalid CCI PMU counter %d\n", idx);
+ else
+ pmu_write_register(value, idx, CCI_PMU_CNTR);
+}
+
+static int cci_pmu_init(struct arm_pmu *cci_pmu, struct platform_device *pdev)
+{
+ *cci_pmu = (struct arm_pmu){
+ .name = PMU_NAME,
+ .max_period = (1LLU << 32) - 1,
+ .get_hw_events = pmu_get_hw_events,
+ .get_event_idx = pmu_get_event_idx,
+ .map_event = pmu_map_event,
+ .request_irq = pmu_request_irq,
+ .handle_irq = pmu_handle_irq,
+ .free_irq = pmu_free_irq,
+ .enable = pmu_enable_event,
+ .disable = pmu_disable_event,
+ .start = pmu_start,
+ .stop = pmu_stop,
+ .read_counter = pmu_read_counter,
+ .write_counter = pmu_write_counter,
+ };
+
+ cci_pmu->plat_device = pdev;
+ cci_pmu->num_events = pmu_get_max_counters();
+
+ return armpmu_register(cci_pmu, -1);
+}
+
+static const struct of_device_id arm_cci_pmu_matches[] = {
+ {
+ .compatible = "arm,cci-400-pmu",
+ },
+ {},
+};
+
+static int cci_pmu_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int i, ret, irq;
+
+ pmu = devm_kzalloc(&pdev->dev, sizeof(*pmu), GFP_KERNEL);
+ if (!pmu)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pmu->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pmu->base))
+ return -ENOMEM;
+
+ /*
+ * CCI PMU has 5 overflow signals - one per counter; but some may be tied
+ * together to a common interrupt.
+ */
+ pmu->nr_irqs = 0;
+ for (i = 0; i < CCI_PMU_MAX_HW_EVENTS; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ break;
+
+ if (is_duplicate_irq(irq, pmu->irqs, pmu->nr_irqs))
+ continue;
+
+ pmu->irqs[pmu->nr_irqs++] = irq;
+ }
+
+ /*
+ * Ensure that the device tree has as many interrupts as the number
+ * of counters.
+ */
+ if (i < CCI_PMU_MAX_HW_EVENTS) {
+ dev_warn(&pdev->dev, "In-correct number of interrupts: %d, should be %d\n",
+ i, CCI_PMU_MAX_HW_EVENTS);
+ return -EINVAL;
+ }
+
+ pmu->port_ranges = port_range_by_rev();
+ if (!pmu->port_ranges) {
+ dev_warn(&pdev->dev, "CCI PMU version not supported\n");
+ return -EINVAL;
+ }
+
+ pmu->cci_pmu = devm_kzalloc(&pdev->dev, sizeof(*(pmu->cci_pmu)), GFP_KERNEL);
+ if (!pmu->cci_pmu)
+ return -ENOMEM;
+
+ pmu->hw_events.events = pmu->events;
+ pmu->hw_events.used_mask = pmu->used_mask;
+ raw_spin_lock_init(&pmu->hw_events.pmu_lock);
+
+ ret = cci_pmu_init(pmu->cci_pmu, pdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int cci_platform_probe(struct platform_device *pdev)
+{
+ if (!cci_probed())
+ return -ENODEV;
+
+ return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+}
+
+#endif /* CONFIG_HW_PERF_EVENTS */
+
struct cpu_port {
u64 mpidr;
u32 port;
@@ -120,7 +692,7 @@ int cci_ace_get_port(struct device_node *dn)
}
EXPORT_SYMBOL_GPL(cci_ace_get_port);
-static void __init cci_ace_init_ports(void)
+static void cci_ace_init_ports(void)
{
int port, cpu;
struct device_node *cpun;
@@ -280,7 +852,7 @@ asmlinkage void __naked cci_enable_port_for_self(void)
/* Enable the CCI port */
" ldr r0, [r0, %[offsetof_port_phys]] \n"
-" mov r3, #"__stringify(CCI_ENABLE_REQ)" \n"
+" mov r3, %[cci_enable_req]\n"
" str r3, [r0, #"__stringify(CCI_PORT_CTRL)"] \n"
/* poll the status reg for completion */
@@ -288,7 +860,7 @@ asmlinkage void __naked cci_enable_port_for_self(void)
" ldr r0, [r1] \n"
" ldr r0, [r0, r1] @ cci_ctrl_base \n"
"4: ldr r1, [r0, #"__stringify(CCI_CTRL_STATUS)"] \n"
-" tst r1, #1 \n"
+" tst r1, %[cci_control_status_bits] \n"
" bne 4b \n"
" mov r0, #0 \n"
@@ -301,6 +873,8 @@ asmlinkage void __naked cci_enable_port_for_self(void)
"7: .word cci_ctrl_phys - . \n"
: :
[sizeof_cpu_port] "i" (sizeof(cpu_port)),
+ [cci_enable_req] "i" cpu_to_le32(CCI_ENABLE_REQ),
+ [cci_control_status_bits] "i" cpu_to_le32(1),
#ifndef __ARMEB__
[offsetof_cpu_port_mpidr_lsb] "i" (offsetof(struct cpu_port, mpidr)),
#else
@@ -386,7 +960,7 @@ static const struct of_device_id arm_cci_ctrl_if_matches[] = {
{},
};
-static int __init cci_probe(void)
+static int cci_probe(void)
{
struct cci_nb_ports const *cci_config;
int ret, i, nb_ace = 0, nb_ace_lite = 0;
@@ -490,7 +1064,7 @@ memalloc_err:
static int cci_init_status = -EAGAIN;
static DEFINE_MUTEX(cci_probing);
-static int __init cci_init(void)
+static int cci_init(void)
{
if (cci_init_status != -EAGAIN)
return cci_init_status;
@@ -502,18 +1076,55 @@ static int __init cci_init(void)
return cci_init_status;
}
+#ifdef CONFIG_HW_PERF_EVENTS
+static struct platform_driver cci_pmu_driver = {
+ .driver = {
+ .name = DRIVER_NAME_PMU,
+ .of_match_table = arm_cci_pmu_matches,
+ },
+ .probe = cci_pmu_probe,
+};
+
+static struct platform_driver cci_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = arm_cci_matches,
+ },
+ .probe = cci_platform_probe,
+};
+
+static int __init cci_platform_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&cci_pmu_driver);
+ if (ret)
+ return ret;
+
+ return platform_driver_register(&cci_platform_driver);
+}
+
+#else
+
+static int __init cci_platform_init(void)
+{
+ return 0;
+}
+
+#endif
/*
* To sort out early init calls ordering a helper function is provided to
* check if the CCI driver has beed initialized. Function check if the driver
* has been initialized, if not it calls the init function that probes
* the driver and updates the return value.
*/
-bool __init cci_probed(void)
+bool cci_probed(void)
{
return cci_init() == 0;
}
EXPORT_SYMBOL_GPL(cci_probed);
early_initcall(cci_init);
+core_initcall(cci_platform_init);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ARM CCI support");
diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c
index 349f14e886b7..3ef58c8dbf11 100644
--- a/drivers/bus/imx-weim.c
+++ b/drivers/bus/imx-weim.c
@@ -12,52 +12,83 @@
#include <linux/io.h>
#include <linux/of_device.h>
-struct imx_weim {
- void __iomem *base;
- struct clk *clk;
+struct imx_weim_devtype {
+ unsigned int cs_count;
+ unsigned int cs_regs_count;
+ unsigned int cs_stride;
+};
+
+static const struct imx_weim_devtype imx1_weim_devtype = {
+ .cs_count = 6,
+ .cs_regs_count = 2,
+ .cs_stride = 0x08,
+};
+
+static const struct imx_weim_devtype imx27_weim_devtype = {
+ .cs_count = 6,
+ .cs_regs_count = 3,
+ .cs_stride = 0x10,
+};
+
+static const struct imx_weim_devtype imx50_weim_devtype = {
+ .cs_count = 4,
+ .cs_regs_count = 6,
+ .cs_stride = 0x18,
+};
+
+static const struct imx_weim_devtype imx51_weim_devtype = {
+ .cs_count = 6,
+ .cs_regs_count = 6,
+ .cs_stride = 0x18,
};
static const struct of_device_id weim_id_table[] = {
- { .compatible = "fsl,imx6q-weim", },
- {}
+ /* i.MX1/21 */
+ { .compatible = "fsl,imx1-weim", .data = &imx1_weim_devtype, },
+ /* i.MX25/27/31/35 */
+ { .compatible = "fsl,imx27-weim", .data = &imx27_weim_devtype, },
+ /* i.MX50/53/6Q */
+ { .compatible = "fsl,imx50-weim", .data = &imx50_weim_devtype, },
+ { .compatible = "fsl,imx6q-weim", .data = &imx50_weim_devtype, },
+ /* i.MX51 */
+ { .compatible = "fsl,imx51-weim", .data = &imx51_weim_devtype, },
+ { }
};
MODULE_DEVICE_TABLE(of, weim_id_table);
-#define CS_TIMING_LEN 6
-#define CS_REG_RANGE 0x18
-
/* Parse and set the timing for this device. */
-static int
-weim_timing_setup(struct platform_device *pdev, struct device_node *np)
+static int __init weim_timing_setup(struct device_node *np, void __iomem *base,
+ const struct imx_weim_devtype *devtype)
{
- struct imx_weim *weim = platform_get_drvdata(pdev);
- u32 value[CS_TIMING_LEN];
- u32 cs_idx;
- int ret;
- int i;
+ u32 cs_idx, value[devtype->cs_regs_count];
+ int i, ret;
/* get the CS index from this child node's "reg" property. */
ret = of_property_read_u32(np, "reg", &cs_idx);
if (ret)
return ret;
- /* The weim has four chip selects. */
- if (cs_idx > 3)
+ if (cs_idx >= devtype->cs_count)
return -EINVAL;
ret = of_property_read_u32_array(np, "fsl,weim-cs-timing",
- value, CS_TIMING_LEN);
+ value, devtype->cs_regs_count);
if (ret)
return ret;
/* set the timing for WEIM */
- for (i = 0; i < CS_TIMING_LEN; i++)
- writel(value[i], weim->base + cs_idx * CS_REG_RANGE + i * 4);
+ for (i = 0; i < devtype->cs_regs_count; i++)
+ writel(value[i], base + cs_idx * devtype->cs_stride + i * 4);
+
return 0;
}
-static int weim_parse_dt(struct platform_device *pdev)
+static int __init weim_parse_dt(struct platform_device *pdev,
+ void __iomem *base)
{
+ const struct of_device_id *of_id = of_match_device(weim_id_table,
+ &pdev->dev);
+ const struct imx_weim_devtype *devtype = of_id->data;
struct device_node *child;
int ret;
@@ -65,7 +96,7 @@ static int weim_parse_dt(struct platform_device *pdev)
if (!child->name)
continue;
- ret = weim_timing_setup(pdev, child);
+ ret = weim_timing_setup(child, base, devtype);
if (ret) {
dev_err(&pdev->dev, "%s set timing failed.\n",
child->full_name);
@@ -80,59 +111,47 @@ static int weim_parse_dt(struct platform_device *pdev)
return ret;
}
-static int weim_probe(struct platform_device *pdev)
+static int __init weim_probe(struct platform_device *pdev)
{
- struct imx_weim *weim;
struct resource *res;
- int ret = -EINVAL;
-
- weim = devm_kzalloc(&pdev->dev, sizeof(*weim), GFP_KERNEL);
- if (!weim) {
- ret = -ENOMEM;
- goto weim_err;
- }
- platform_set_drvdata(pdev, weim);
+ struct clk *clk;
+ void __iomem *base;
+ int ret;
/* get the resource */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- weim->base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(weim->base)) {
- ret = PTR_ERR(weim->base);
- goto weim_err;
- }
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
/* get the clock */
- weim->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(weim->clk))
- goto weim_err;
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
- ret = clk_prepare_enable(weim->clk);
+ ret = clk_prepare_enable(clk);
if (ret)
- goto weim_err;
+ return ret;
/* parse the device node */
- ret = weim_parse_dt(pdev);
- if (ret) {
- clk_disable_unprepare(weim->clk);
- goto weim_err;
- }
-
- dev_info(&pdev->dev, "WEIM driver registered.\n");
- return 0;
+ ret = weim_parse_dt(pdev, base);
+ if (ret)
+ clk_disable_unprepare(clk);
+ else
+ dev_info(&pdev->dev, "Driver registered.\n");
-weim_err:
return ret;
}
static struct platform_driver weim_driver = {
.driver = {
- .name = "imx-weim",
- .of_match_table = weim_id_table,
+ .name = "imx-weim",
+ .owner = THIS_MODULE,
+ .of_match_table = weim_id_table,
},
- .probe = weim_probe,
};
+module_platform_driver_probe(weim_driver, weim_probe);
-module_platform_driver(weim_driver);
MODULE_AUTHOR("Freescale Semiconductor Inc.");
MODULE_DESCRIPTION("i.MX EIM Controller Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index 33c6947eebec..2394e9753ef5 100644
--- a/drivers/bus/mvebu-mbus.c
+++ b/drivers/bus/mvebu-mbus.c
@@ -35,13 +35,9 @@
*
* - Provides an API for platform code or device drivers to
* dynamically add or remove address decoding windows for the CPU ->
- * device accesses. This API is mvebu_mbus_add_window(),
- * mvebu_mbus_add_window_remap_flags() and
- * mvebu_mbus_del_window(). Since the (target, attribute) values
- * differ from one SoC family to another, the API uses a 'const char
- * *' string to identify devices, and this driver is responsible for
- * knowing the mapping between the name of a device and its
- * corresponding (target, attribute) in the current SoC family.
+ * device accesses. This API is mvebu_mbus_add_window_by_id(),
+ * mvebu_mbus_add_window_remap_by_id() and
+ * mvebu_mbus_del_window().
*
* - Provides a debugfs interface in /sys/kernel/debug/mvebu-mbus/ to
* see the list of CPU -> SDRAM windows and their configuration
@@ -97,33 +93,6 @@
#define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4)
-struct mvebu_mbus_mapping {
- const char *name;
- u8 target;
- u8 attr;
- u8 attrmask;
-};
-
-/*
- * Masks used for the 'attrmask' field of mvebu_mbus_mapping. They
- * allow to get the real attribute value, discarding the special bits
- * used to select a PCI MEM region or a PCI WA region. This allows the
- * debugfs code to reverse-match the name of a device from its
- * target/attr values.
- *
- * For all devices except PCI, all bits of 'attr' must be
- * considered. For most SoCs, only bit 3 should be ignored (it allows
- * to select between PCI MEM and PCI I/O). On Orion5x however, there
- * is the special bit 5 to select a PCI WA region.
- */
-#define MAPDEF_NOMASK 0xff
-#define MAPDEF_PCIMASK 0xf7
-#define MAPDEF_ORIONPCIMASK 0xd7
-
-/* Macro used to define one mvebu_mbus_mapping entry */
-#define MAPDEF(__n, __t, __a, __m) \
- { .name = __n, .target = __t, .attr = __a, .attrmask = __m }
-
struct mvebu_mbus_state;
struct mvebu_mbus_soc_data {
@@ -133,7 +102,6 @@ struct mvebu_mbus_soc_data {
void (*setup_cpu_target)(struct mvebu_mbus_state *s);
int (*show_cpu_target)(struct mvebu_mbus_state *s,
struct seq_file *seq, void *v);
- const struct mvebu_mbus_mapping *map;
};
struct mvebu_mbus_state {
@@ -142,6 +110,8 @@ struct mvebu_mbus_state {
struct dentry *debugfs_root;
struct dentry *debugfs_sdram;
struct dentry *debugfs_devs;
+ struct resource pcie_mem_aperture;
+ struct resource pcie_io_aperture;
const struct mvebu_mbus_soc_data *soc;
int hw_io_coherency;
};
@@ -428,8 +398,7 @@ static int mvebu_devs_debug_show(struct seq_file *seq, void *v)
u64 wbase, wremap;
u32 wsize;
u8 wtarget, wattr;
- int enabled, i;
- const char *name;
+ int enabled;
mvebu_mbus_read_window(mbus, win,
&enabled, &wbase, &wsize,
@@ -440,18 +409,9 @@ static int mvebu_devs_debug_show(struct seq_file *seq, void *v)
continue;
}
-
- for (i = 0; mbus->soc->map[i].name; i++)
- if (mbus->soc->map[i].target == wtarget &&
- mbus->soc->map[i].attr ==
- (wattr & mbus->soc->map[i].attrmask))
- break;
-
- name = mbus->soc->map[i].name ?: "unknown";
-
- seq_printf(seq, "[%02d] %016llx - %016llx : %s",
+ seq_printf(seq, "[%02d] %016llx - %016llx : %04x:%04x",
win, (unsigned long long)wbase,
- (unsigned long long)(wbase + wsize), name);
+ (unsigned long long)(wbase + wsize), wtarget, wattr);
if (win < mbus->soc->num_remappable_wins) {
seq_printf(seq, " (remap %016llx)\n",
@@ -576,62 +536,12 @@ mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus)
mvebu_mbus_dram_info.num_cs = cs;
}
-static const struct mvebu_mbus_mapping armada_370_map[] = {
- MAPDEF("bootrom", 1, 0xe0, MAPDEF_NOMASK),
- MAPDEF("devbus-boot", 1, 0x2f, MAPDEF_NOMASK),
- MAPDEF("devbus-cs0", 1, 0x3e, MAPDEF_NOMASK),
- MAPDEF("devbus-cs1", 1, 0x3d, MAPDEF_NOMASK),
- MAPDEF("devbus-cs2", 1, 0x3b, MAPDEF_NOMASK),
- MAPDEF("devbus-cs3", 1, 0x37, MAPDEF_NOMASK),
- MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
- MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK),
- {},
-};
-
-static const struct mvebu_mbus_soc_data armada_370_mbus_data = {
+static const struct mvebu_mbus_soc_data armada_370_xp_mbus_data = {
.num_wins = 20,
.num_remappable_wins = 8,
.win_cfg_offset = armada_370_xp_mbus_win_offset,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
- .map = armada_370_map,
-};
-
-static const struct mvebu_mbus_mapping armada_xp_map[] = {
- MAPDEF("bootrom", 1, 0x1d, MAPDEF_NOMASK),
- MAPDEF("devbus-boot", 1, 0x2f, MAPDEF_NOMASK),
- MAPDEF("devbus-cs0", 1, 0x3e, MAPDEF_NOMASK),
- MAPDEF("devbus-cs1", 1, 0x3d, MAPDEF_NOMASK),
- MAPDEF("devbus-cs2", 1, 0x3b, MAPDEF_NOMASK),
- MAPDEF("devbus-cs3", 1, 0x37, MAPDEF_NOMASK),
- MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
- MAPDEF("pcie0.1", 4, 0xd0, MAPDEF_PCIMASK),
- MAPDEF("pcie0.2", 4, 0xb0, MAPDEF_PCIMASK),
- MAPDEF("pcie0.3", 4, 0x70, MAPDEF_PCIMASK),
- MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK),
- MAPDEF("pcie1.1", 8, 0xd0, MAPDEF_PCIMASK),
- MAPDEF("pcie1.2", 8, 0xb0, MAPDEF_PCIMASK),
- MAPDEF("pcie1.3", 8, 0x70, MAPDEF_PCIMASK),
- MAPDEF("pcie2.0", 4, 0xf0, MAPDEF_PCIMASK),
- MAPDEF("pcie3.0", 8, 0xf0, MAPDEF_PCIMASK),
- {},
-};
-
-static const struct mvebu_mbus_soc_data armada_xp_mbus_data = {
- .num_wins = 20,
- .num_remappable_wins = 8,
- .win_cfg_offset = armada_370_xp_mbus_win_offset,
- .setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
- .show_cpu_target = mvebu_sdram_debug_show_orion,
- .map = armada_xp_map,
-};
-
-static const struct mvebu_mbus_mapping kirkwood_map[] = {
- MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
- MAPDEF("pcie1.0", 4, 0xd0, MAPDEF_PCIMASK),
- MAPDEF("sram", 3, 0x01, MAPDEF_NOMASK),
- MAPDEF("nand", 1, 0x2f, MAPDEF_NOMASK),
- {},
};
static const struct mvebu_mbus_soc_data kirkwood_mbus_data = {
@@ -640,16 +550,6 @@ static const struct mvebu_mbus_soc_data kirkwood_mbus_data = {
.win_cfg_offset = orion_mbus_win_offset,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
- .map = kirkwood_map,
-};
-
-static const struct mvebu_mbus_mapping dove_map[] = {
- MAPDEF("pcie0.0", 0x4, 0xe0, MAPDEF_PCIMASK),
- MAPDEF("pcie1.0", 0x8, 0xe0, MAPDEF_PCIMASK),
- MAPDEF("cesa", 0x3, 0x01, MAPDEF_NOMASK),
- MAPDEF("bootrom", 0x1, 0xfd, MAPDEF_NOMASK),
- MAPDEF("scratchpad", 0xd, 0x0, MAPDEF_NOMASK),
- {},
};
static const struct mvebu_mbus_soc_data dove_mbus_data = {
@@ -658,18 +558,6 @@ static const struct mvebu_mbus_soc_data dove_mbus_data = {
.win_cfg_offset = orion_mbus_win_offset,
.setup_cpu_target = mvebu_mbus_dove_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_dove,
- .map = dove_map,
-};
-
-static const struct mvebu_mbus_mapping orion5x_map[] = {
- MAPDEF("pcie0.0", 4, 0x51, MAPDEF_ORIONPCIMASK),
- MAPDEF("pci0.0", 3, 0x51, MAPDEF_ORIONPCIMASK),
- MAPDEF("devbus-boot", 1, 0x0f, MAPDEF_NOMASK),
- MAPDEF("devbus-cs0", 1, 0x1e, MAPDEF_NOMASK),
- MAPDEF("devbus-cs1", 1, 0x1d, MAPDEF_NOMASK),
- MAPDEF("devbus-cs2", 1, 0x1b, MAPDEF_NOMASK),
- MAPDEF("sram", 0, 0x00, MAPDEF_NOMASK),
- {},
};
/*
@@ -682,7 +570,6 @@ static const struct mvebu_mbus_soc_data orion5x_4win_mbus_data = {
.win_cfg_offset = orion_mbus_win_offset,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
- .map = orion5x_map,
};
static const struct mvebu_mbus_soc_data orion5x_2win_mbus_data = {
@@ -691,21 +578,6 @@ static const struct mvebu_mbus_soc_data orion5x_2win_mbus_data = {
.win_cfg_offset = orion_mbus_win_offset,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
- .map = orion5x_map,
-};
-
-static const struct mvebu_mbus_mapping mv78xx0_map[] = {
- MAPDEF("pcie0.0", 4, 0xe0, MAPDEF_PCIMASK),
- MAPDEF("pcie0.1", 4, 0xd0, MAPDEF_PCIMASK),
- MAPDEF("pcie0.2", 4, 0xb0, MAPDEF_PCIMASK),
- MAPDEF("pcie0.3", 4, 0x70, MAPDEF_PCIMASK),
- MAPDEF("pcie1.0", 8, 0xe0, MAPDEF_PCIMASK),
- MAPDEF("pcie1.1", 8, 0xd0, MAPDEF_PCIMASK),
- MAPDEF("pcie1.2", 8, 0xb0, MAPDEF_PCIMASK),
- MAPDEF("pcie1.3", 8, 0x70, MAPDEF_PCIMASK),
- MAPDEF("pcie2.0", 4, 0xf0, MAPDEF_PCIMASK),
- MAPDEF("pcie3.0", 8, 0xf0, MAPDEF_PCIMASK),
- {},
};
static const struct mvebu_mbus_soc_data mv78xx0_mbus_data = {
@@ -714,7 +586,6 @@ static const struct mvebu_mbus_soc_data mv78xx0_mbus_data = {
.win_cfg_offset = mv78xx0_mbus_win_offset,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
- .map = mv78xx0_map,
};
/*
@@ -725,9 +596,9 @@ static const struct mvebu_mbus_soc_data mv78xx0_mbus_data = {
*/
static const struct of_device_id of_mvebu_mbus_ids[] = {
{ .compatible = "marvell,armada370-mbus",
- .data = &armada_370_mbus_data, },
+ .data = &armada_370_xp_mbus_data, },
{ .compatible = "marvell,armadaxp-mbus",
- .data = &armada_xp_mbus_data, },
+ .data = &armada_370_xp_mbus_data, },
{ .compatible = "marvell,kirkwood-mbus",
.data = &kirkwood_mbus_data, },
{ .compatible = "marvell,dove-mbus",
@@ -748,48 +619,27 @@ static const struct of_device_id of_mvebu_mbus_ids[] = {
/*
* Public API of the driver
*/
-int mvebu_mbus_add_window_remap_flags(const char *devname, phys_addr_t base,
- size_t size, phys_addr_t remap,
- unsigned int flags)
+int mvebu_mbus_add_window_remap_by_id(unsigned int target,
+ unsigned int attribute,
+ phys_addr_t base, size_t size,
+ phys_addr_t remap)
{
struct mvebu_mbus_state *s = &mbus_state;
- u8 target, attr;
- int i;
-
- if (!s->soc->map)
- return -ENODEV;
-
- for (i = 0; s->soc->map[i].name; i++)
- if (!strcmp(s->soc->map[i].name, devname))
- break;
-
- if (!s->soc->map[i].name) {
- pr_err("unknown device '%s'\n", devname);
- return -ENODEV;
- }
-
- target = s->soc->map[i].target;
- attr = s->soc->map[i].attr;
-
- if (flags == MVEBU_MBUS_PCI_MEM)
- attr |= 0x8;
- else if (flags == MVEBU_MBUS_PCI_WA)
- attr |= 0x28;
- if (!mvebu_mbus_window_conflicts(s, base, size, target, attr)) {
- pr_err("cannot add window '%s', conflicts with another window\n",
- devname);
+ if (!mvebu_mbus_window_conflicts(s, base, size, target, attribute)) {
+ pr_err("cannot add window '%x:%x', conflicts with another window\n",
+ target, attribute);
return -EINVAL;
}
- return mvebu_mbus_alloc_window(s, base, size, remap, target, attr);
-
+ return mvebu_mbus_alloc_window(s, base, size, remap, target, attribute);
}
-int mvebu_mbus_add_window(const char *devname, phys_addr_t base, size_t size)
+int mvebu_mbus_add_window_by_id(unsigned int target, unsigned int attribute,
+ phys_addr_t base, size_t size)
{
- return mvebu_mbus_add_window_remap_flags(devname, base, size,
- MVEBU_MBUS_NO_REMAP, 0);
+ return mvebu_mbus_add_window_remap_by_id(target, attribute, base,
+ size, MVEBU_MBUS_NO_REMAP);
}
int mvebu_mbus_del_window(phys_addr_t base, size_t size)
@@ -804,6 +654,20 @@ int mvebu_mbus_del_window(phys_addr_t base, size_t size)
return 0;
}
+void mvebu_mbus_get_pcie_mem_aperture(struct resource *res)
+{
+ if (!res)
+ return;
+ *res = mbus_state.pcie_mem_aperture;
+}
+
+void mvebu_mbus_get_pcie_io_aperture(struct resource *res)
+{
+ if (!res)
+ return;
+ *res = mbus_state.pcie_io_aperture;
+}
+
static __init int mvebu_mbus_debugfs_init(void)
{
struct mvebu_mbus_state *s = &mbus_state;
@@ -830,14 +694,45 @@ static __init int mvebu_mbus_debugfs_init(void)
}
fs_initcall(mvebu_mbus_debugfs_init);
+static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
+ phys_addr_t mbuswins_phys_base,
+ size_t mbuswins_size,
+ phys_addr_t sdramwins_phys_base,
+ size_t sdramwins_size)
+{
+ struct device_node *np;
+ int win;
+
+ mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size);
+ if (!mbus->mbuswins_base)
+ return -ENOMEM;
+
+ mbus->sdramwins_base = ioremap(sdramwins_phys_base, sdramwins_size);
+ if (!mbus->sdramwins_base) {
+ iounmap(mbus_state.mbuswins_base);
+ return -ENOMEM;
+ }
+
+ np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric");
+ if (np) {
+ mbus->hw_io_coherency = 1;
+ of_node_put(np);
+ }
+
+ for (win = 0; win < mbus->soc->num_wins; win++)
+ mvebu_mbus_disable_window(mbus, win);
+
+ mbus->soc->setup_cpu_target(mbus);
+
+ return 0;
+}
+
int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base,
size_t mbuswins_size,
phys_addr_t sdramwins_phys_base,
size_t sdramwins_size)
{
- struct mvebu_mbus_state *mbus = &mbus_state;
const struct of_device_id *of_id;
- int win;
for (of_id = of_mvebu_mbus_ids; of_id->compatible; of_id++)
if (!strcmp(of_id->compatible, soc))
@@ -848,25 +743,203 @@ int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base,
return -ENODEV;
}
- mbus->soc = of_id->data;
+ mbus_state.soc = of_id->data;
- mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size);
- if (!mbus->mbuswins_base)
- return -ENOMEM;
+ return mvebu_mbus_common_init(&mbus_state,
+ mbuswins_phys_base,
+ mbuswins_size,
+ sdramwins_phys_base,
+ sdramwins_size);
+}
- mbus->sdramwins_base = ioremap(sdramwins_phys_base, sdramwins_size);
- if (!mbus->sdramwins_base) {
- iounmap(mbus_state.mbuswins_base);
+#ifdef CONFIG_OF
+/*
+ * The window IDs in the ranges DT property have the following format:
+ * - bits 28 to 31: MBus custom field
+ * - bits 24 to 27: window target ID
+ * - bits 16 to 23: window attribute ID
+ * - bits 0 to 15: unused
+ */
+#define CUSTOM(id) (((id) & 0xF0000000) >> 24)
+#define TARGET(id) (((id) & 0x0F000000) >> 24)
+#define ATTR(id) (((id) & 0x00FF0000) >> 16)
+
+static int __init mbus_dt_setup_win(struct mvebu_mbus_state *mbus,
+ u32 base, u32 size,
+ u8 target, u8 attr)
+{
+ if (!mvebu_mbus_window_conflicts(mbus, base, size, target, attr)) {
+ pr_err("cannot add window '%04x:%04x', conflicts with another window\n",
+ target, attr);
+ return -EBUSY;
+ }
+
+ if (mvebu_mbus_alloc_window(mbus, base, size, MVEBU_MBUS_NO_REMAP,
+ target, attr)) {
+ pr_err("cannot add window '%04x:%04x', too many windows\n",
+ target, attr);
return -ENOMEM;
}
+ return 0;
+}
- if (of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"))
- mbus->hw_io_coherency = 1;
+static int __init
+mbus_parse_ranges(struct device_node *node,
+ int *addr_cells, int *c_addr_cells, int *c_size_cells,
+ int *cell_count, const __be32 **ranges_start,
+ const __be32 **ranges_end)
+{
+ const __be32 *prop;
+ int ranges_len, tuple_len;
+
+ /* Allow a node with no 'ranges' property */
+ *ranges_start = of_get_property(node, "ranges", &ranges_len);
+ if (*ranges_start == NULL) {
+ *addr_cells = *c_addr_cells = *c_size_cells = *cell_count = 0;
+ *ranges_start = *ranges_end = NULL;
+ return 0;
+ }
+ *ranges_end = *ranges_start + ranges_len / sizeof(__be32);
- for (win = 0; win < mbus->soc->num_wins; win++)
- mvebu_mbus_disable_window(mbus, win);
+ *addr_cells = of_n_addr_cells(node);
- mbus->soc->setup_cpu_target(mbus);
+ prop = of_get_property(node, "#address-cells", NULL);
+ *c_addr_cells = be32_to_cpup(prop);
+
+ prop = of_get_property(node, "#size-cells", NULL);
+ *c_size_cells = be32_to_cpup(prop);
+ *cell_count = *addr_cells + *c_addr_cells + *c_size_cells;
+ tuple_len = (*cell_count) * sizeof(__be32);
+
+ if (ranges_len % tuple_len) {
+ pr_warn("malformed ranges entry '%s'\n", node->name);
+ return -EINVAL;
+ }
return 0;
}
+
+static int __init mbus_dt_setup(struct mvebu_mbus_state *mbus,
+ struct device_node *np)
+{
+ int addr_cells, c_addr_cells, c_size_cells;
+ int i, ret, cell_count;
+ const __be32 *r, *ranges_start, *ranges_end;
+
+ ret = mbus_parse_ranges(np, &addr_cells, &c_addr_cells,
+ &c_size_cells, &cell_count,
+ &ranges_start, &ranges_end);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0, r = ranges_start; r < ranges_end; r += cell_count, i++) {
+ u32 windowid, base, size;
+ u8 target, attr;
+
+ /*
+ * An entry with a non-zero custom field do not
+ * correspond to a static window, so skip it.
+ */
+ windowid = of_read_number(r, 1);
+ if (CUSTOM(windowid))
+ continue;
+
+ target = TARGET(windowid);
+ attr = ATTR(windowid);
+
+ base = of_read_number(r + c_addr_cells, addr_cells);
+ size = of_read_number(r + c_addr_cells + addr_cells,
+ c_size_cells);
+ ret = mbus_dt_setup_win(mbus, base, size, target, attr);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static void __init mvebu_mbus_get_pcie_resources(struct device_node *np,
+ struct resource *mem,
+ struct resource *io)
+{
+ u32 reg[2];
+ int ret;
+
+ /*
+ * These are optional, so we make sure that resource_size(x) will
+ * return 0.
+ */
+ memset(mem, 0, sizeof(struct resource));
+ mem->end = -1;
+ memset(io, 0, sizeof(struct resource));
+ io->end = -1;
+
+ ret = of_property_read_u32_array(np, "pcie-mem-aperture", reg, ARRAY_SIZE(reg));
+ if (!ret) {
+ mem->start = reg[0];
+ mem->end = mem->start + reg[1];
+ mem->flags = IORESOURCE_MEM;
+ }
+
+ ret = of_property_read_u32_array(np, "pcie-io-aperture", reg, ARRAY_SIZE(reg));
+ if (!ret) {
+ io->start = reg[0];
+ io->end = io->start + reg[1];
+ io->flags = IORESOURCE_IO;
+ }
+}
+
+int __init mvebu_mbus_dt_init(void)
+{
+ struct resource mbuswins_res, sdramwins_res;
+ struct device_node *np, *controller;
+ const struct of_device_id *of_id;
+ const __be32 *prop;
+ int ret;
+
+ np = of_find_matching_node(NULL, of_mvebu_mbus_ids);
+ if (!np) {
+ pr_err("could not find a matching SoC family\n");
+ return -ENODEV;
+ }
+
+ of_id = of_match_node(of_mvebu_mbus_ids, np);
+ mbus_state.soc = of_id->data;
+
+ prop = of_get_property(np, "controller", NULL);
+ if (!prop) {
+ pr_err("required 'controller' property missing\n");
+ return -EINVAL;
+ }
+
+ controller = of_find_node_by_phandle(be32_to_cpup(prop));
+ if (!controller) {
+ pr_err("could not find an 'mbus-controller' node\n");
+ return -ENODEV;
+ }
+
+ if (of_address_to_resource(controller, 0, &mbuswins_res)) {
+ pr_err("cannot get MBUS register address\n");
+ return -EINVAL;
+ }
+
+ if (of_address_to_resource(controller, 1, &sdramwins_res)) {
+ pr_err("cannot get SDRAM register address\n");
+ return -EINVAL;
+ }
+
+ /* Get optional pcie-{mem,io}-aperture properties */
+ mvebu_mbus_get_pcie_resources(np, &mbus_state.pcie_mem_aperture,
+ &mbus_state.pcie_io_aperture);
+
+ ret = mvebu_mbus_common_init(&mbus_state,
+ mbuswins_res.start,
+ resource_size(&mbuswins_res),
+ sdramwins_res.start,
+ resource_size(&sdramwins_res));
+ if (ret)
+ return ret;
+
+ /* Setup statically declared windows in the DT */
+ return mbus_dt_setup(&mbus_state, np);
+}
+#endif