diff options
Diffstat (limited to 'drivers/bus')
-rw-r--r-- | drivers/bus/Kconfig | 3 | ||||
-rw-r--r-- | drivers/bus/arm-cci.c | 623 | ||||
-rw-r--r-- | drivers/bus/imx-weim.c | 129 | ||||
-rw-r--r-- | drivers/bus/mvebu-mbus.c | 447 |
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 |