diff options
Diffstat (limited to 'drivers/bus')
-rw-r--r-- | drivers/bus/Kconfig | 73 | ||||
-rw-r--r-- | drivers/bus/Makefile | 17 | ||||
-rw-r--r-- | drivers/bus/arm-cci.c | 519 | ||||
-rw-r--r-- | drivers/bus/imx-weim.c | 13 | ||||
-rw-r--r-- | drivers/bus/mips_cdmm.c | 4 | ||||
-rw-r--r-- | drivers/bus/mvebu-mbus.c | 109 | ||||
-rw-r--r-- | drivers/bus/omap-ocp2scp.c | 34 | ||||
-rw-r--r-- | drivers/bus/omap_l3_noc.c | 5 | ||||
-rw-r--r-- | drivers/bus/omap_l3_noc.h | 54 | ||||
-rw-r--r-- | drivers/bus/simple-pm-bus.c | 58 |
10 files changed, 516 insertions, 370 deletions
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index cbddbaddb347..a1d4af6df3f5 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -4,6 +4,41 @@ menu "Bus devices" +config ARM_CCI + bool + +config ARM_CCI400_COMMON + bool + select ARM_CCI + +config ARM_CCI400_PMU + bool "ARM CCI400 PMU support" + default y + depends on ARM || ARM64 + depends on HW_PERF_EVENTS + select ARM_CCI400_COMMON + help + Support for PMU events monitoring on the ARM CCI cache coherent + interconnect. + + If unsure, say Y + +config ARM_CCI400_PORT_CTRL + bool + depends on ARM && OF && CPU_V7 + select ARM_CCI400_COMMON + help + Low level power management driver for CCI400 cache coherent + interconnect for ARM platforms. + +config ARM_CCN + bool "ARM CCN driver support" + depends on ARM || ARM64 + depends on PERF_EVENTS + help + PMU (perf) driver supporting the ARM CCN (Cache Coherent Network) + interconnect. + config BRCMSTB_GISB_ARB bool "Broadcom STB GISB bus arbiter" depends on ARM || MIPS @@ -40,15 +75,6 @@ config MVEBU_MBUS Driver needed for the MBus configuration on Marvell EBU SoCs (Kirkwood, Dove, Orion5x, MV78XX0 and Armada 370/XP). -config OMAP_OCP2SCP - tristate "OMAP OCP2SCP DRIVER" - depends on ARCH_OMAP2PLUS - help - Driver to enable ocp2scp module which transforms ocp interface - protocol to scp protocol. In OMAP4, USB PHY is connected via - OCP2SCP and in OMAP5, both USB PHY and SATA PHY is connected via - OCP2SCP. - config OMAP_INTERCONNECT tristate "OMAP INTERCONNECT DRIVER" depends on ARCH_OMAP2PLUS @@ -56,20 +82,27 @@ config OMAP_INTERCONNECT help Driver to enable OMAP interconnect error handling driver. -config ARM_CCI - bool "ARM CCI driver support" - depends on ARM && OF && CPU_V7 +config OMAP_OCP2SCP + tristate "OMAP OCP2SCP DRIVER" + depends on ARCH_OMAP2PLUS help - Driver supporting the CCI cache coherent interconnect for ARM - platforms. + Driver to enable ocp2scp module which transforms ocp interface + protocol to scp protocol. In OMAP4, USB PHY is connected via + OCP2SCP and in OMAP5, both USB PHY and SATA PHY is connected via + OCP2SCP. -config ARM_CCN - bool "ARM CCN driver support" - depends on ARM || ARM64 - depends on PERF_EVENTS +config SIMPLE_PM_BUS + bool "Simple Power-Managed Bus Driver" + depends on OF && PM + depends on ARCH_SHMOBILE || COMPILE_TEST help - PMU (perf) driver supporting the ARM CCN (Cache Coherent Network) - interconnect. + Driver for transparent busses that don't need a real driver, but + where the bus controller is part of a PM domain, or under the control + of a functional clock, and thus relies on runtime PM for managing + this PM domain and/or clock. + An example of such a bus controller is the Renesas Bus State + Controller (BSC, sometimes called "LBSC within Bus Bridge", or + "External Bus Interface") as found on several Renesas ARM SoCs. config VEXPRESS_CONFIG bool "Versatile Express configuration bus" diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 807dd17ef2f8..790e7b933fb2 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -2,17 +2,18 @@ # Makefile for the bus drivers. # +# Interconnect bus drivers for ARM platforms +obj-$(CONFIG_ARM_CCI) += arm-cci.o +obj-$(CONFIG_ARM_CCN) += arm-ccn.o + obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o -obj-$(CONFIG_IMX_WEIM) += imx-weim.o -obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o -obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o -obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o +obj-$(CONFIG_IMX_WEIM) += imx-weim.o +obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o +obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o # Interconnect bus driver for OMAP SoCs. obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o -# Interconnect bus drivers for ARM platforms -obj-$(CONFIG_ARM_CCI) += arm-cci.o -obj-$(CONFIG_ARM_CCN) += arm-ccn.o - +obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o +obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c index 84fd66057dad..5340604b23a4 100644 --- a/drivers/bus/arm-cci.c +++ b/drivers/bus/arm-cci.c @@ -29,41 +29,36 @@ #include <asm/cacheflush.h> #include <asm/smp_plat.h> -#define DRIVER_NAME "CCI-400" -#define DRIVER_NAME_PMU DRIVER_NAME " PMU" - -#define CCI_PORT_CTRL 0x0 -#define CCI_CTRL_STATUS 0xc - -#define CCI_ENABLE_SNOOP_REQ 0x1 -#define CCI_ENABLE_DVM_REQ 0x2 -#define CCI_ENABLE_REQ (CCI_ENABLE_SNOOP_REQ | CCI_ENABLE_DVM_REQ) +static void __iomem *cci_ctrl_base; +static unsigned long cci_ctrl_phys; +#ifdef CONFIG_ARM_CCI400_PORT_CTRL struct cci_nb_ports { unsigned int nb_ace; unsigned int nb_ace_lite; }; -enum cci_ace_port_type { - ACE_INVALID_PORT = 0x0, - ACE_PORT, - ACE_LITE_PORT, +static const struct cci_nb_ports cci400_ports = { + .nb_ace = 2, + .nb_ace_lite = 3 }; -struct cci_ace_port { - void __iomem *base; - unsigned long phys; - enum cci_ace_port_type type; - struct device_node *dn; -}; +#define CCI400_PORTS_DATA (&cci400_ports) +#else +#define CCI400_PORTS_DATA (NULL) +#endif -static struct cci_ace_port *ports; -static unsigned int nb_cci_ports; +static const struct of_device_id arm_cci_matches[] = { +#ifdef CONFIG_ARM_CCI400_COMMON + {.compatible = "arm,cci-400", .data = CCI400_PORTS_DATA }, +#endif + {}, +}; -static void __iomem *cci_ctrl_base; -static unsigned long cci_ctrl_phys; +#ifdef CONFIG_ARM_CCI400_PMU -#ifdef CONFIG_HW_PERF_EVENTS +#define DRIVER_NAME "CCI-400" +#define DRIVER_NAME_PMU DRIVER_NAME " PMU" #define CCI_PMCR 0x0100 #define CCI_PID2 0x0fe8 @@ -75,20 +70,6 @@ static unsigned long cci_ctrl_phys; #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_R1_PX 5 - #define CCI_PMU_EVT_SEL 0x000 #define CCI_PMU_CNTR 0x004 #define CCI_PMU_CNTR_CTRL 0x008 @@ -100,76 +81,22 @@ static unsigned long cci_ctrl_phys; #define CCI_PMU_CNTR_MASK ((1ULL << 32) -1) -/* - * 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_MASK 0xffUL #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, - }, +/* Types of interfaces that can generate events */ +enum { + CCI_IF_SLAVE, + CCI_IF_MASTER, + CCI_IF_MAX, }; -/* - * Export different PMU names for the different revisions so userspace knows - * because the event ids are different - */ -static char *const pmu_names[] = { - [CCI_REV_R0] = "CCI_400", - [CCI_REV_R1] = "CCI_400_r1", +struct event_range { + u32 min; + u32 max; }; struct cci_pmu_hw_events { @@ -178,13 +105,20 @@ struct cci_pmu_hw_events { raw_spinlock_t pmu_lock; }; +struct cci_pmu_model { + char *name; + struct event_range event_ranges[CCI_IF_MAX]; +}; + +static struct cci_pmu_model cci_pmu_models[]; + struct cci_pmu { void __iomem *base; struct pmu pmu; int nr_irqs; int irqs[CCI_PMU_MAX_HW_EVENTS]; unsigned long active_irqs; - struct pmu_port_event_ranges *port_ranges; + const struct cci_pmu_model *model; struct cci_pmu_hw_events hw_events; struct platform_device *plat_device; int num_events; @@ -196,52 +130,63 @@ static struct cci_pmu *pmu; #define to_cci_pmu(c) (container_of(c, struct cci_pmu, 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; -} +/* 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 -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; +#define CCI_REV_R0 0 +#define CCI_REV_R1 1 +#define CCI_REV_R1_PX 5 - if (rev < CCI_REV_R1_PX) - return CCI_REV_R0; - else - return CCI_REV_R1; -} +/* + * 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 +}; -static struct pmu_port_event_ranges *port_range_by_rev(void) -{ - int rev = probe_cci_revision(); +#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) - return &port_event_range[rev]; -} +/* + * 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. + */ -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; -} +#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 -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; -} +#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 -static int pmu_validate_hw_event(u8 hw_event) +static int pmu_validate_hw_event(unsigned long hw_event) { u8 ev_source = CCI_PMU_EVENT_SOURCE(hw_event); u8 ev_code = CCI_PMU_EVENT_CODE(hw_event); + int if_type; + + if (hw_event & ~CCI_PMU_EVENT_MASK) + return -ENOENT; switch (ev_source) { case CCI_PORT_S0: @@ -250,21 +195,44 @@ static int pmu_validate_hw_event(u8 hw_event) case CCI_PORT_S3: case CCI_PORT_S4: /* Slave Interface */ - if (pmu_is_valid_slave_event(ev_code)) - return hw_event; + if_type = CCI_IF_SLAVE; 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; + if_type = CCI_IF_MASTER; break; + default: + return -ENOENT; } + if (ev_code >= pmu->model->event_ranges[if_type].min && + ev_code <= pmu->model->event_ranges[if_type].max) + return hw_event; + return -ENOENT; } +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_R1_PX) + return CCI_REV_R0; + else + return CCI_REV_R1; +} + +static const struct cci_pmu_model *probe_cci_model(struct platform_device *pdev) +{ + if (platform_has_secure_cci_access()) + return &cci_pmu_models[probe_cci_revision()]; + return NULL; +} + static int pmu_is_valid_counter(struct cci_pmu *cci_pmu, int idx) { return CCI_PMU_CYCLE_CNTR_IDX <= idx && @@ -293,7 +261,6 @@ static void pmu_enable_counter(int idx) static void pmu_set_event(int idx, unsigned long event) { - event &= CCI_PMU_EVENT_MASK; pmu_write_register(event, idx, CCI_PMU_EVT_SEL); } @@ -310,7 +277,7 @@ static int pmu_get_event_idx(struct cci_pmu_hw_events *hw, struct perf_event *ev { struct cci_pmu *cci_pmu = to_cci_pmu(event->pmu); struct hw_perf_event *hw_event = &event->hw; - unsigned long cci_event = hw_event->config_base & CCI_PMU_EVENT_MASK; + unsigned long cci_event = hw_event->config_base; int idx; if (cci_event == CCI_PMU_CYCLES) { @@ -331,7 +298,7 @@ static int pmu_get_event_idx(struct cci_pmu_hw_events *hw, struct perf_event *ev static int pmu_map_event(struct perf_event *event) { int mapping; - u8 config = event->attr.config & CCI_PMU_EVENT_MASK; + unsigned long config = event->attr.config; if (event->attr.type < PERF_TYPE_MAX) return -ENOENT; @@ -660,12 +627,21 @@ static void cci_pmu_del(struct perf_event *event, int flags) } static int -validate_event(struct cci_pmu_hw_events *hw_events, - struct perf_event *event) +validate_event(struct pmu *cci_pmu, + struct cci_pmu_hw_events *hw_events, + struct perf_event *event) { if (is_software_event(event)) return 1; + /* + * Reject groups spanning multiple HW PMUs (e.g. CPU + CCI). The + * core perf code won't check that the pmu->ctx == leader->ctx + * until after pmu->event_init(event). + */ + if (event->pmu != cci_pmu) + return 0; + if (event->state < PERF_EVENT_STATE_OFF) return 1; @@ -684,18 +660,18 @@ validate_group(struct perf_event *event) * Initialise the fake PMU. We only need to populate the * used_mask for the purposes of validation. */ - .used_mask = CPU_BITS_NONE, + .used_mask = { 0 }, }; - if (!validate_event(&fake_pmu, leader)) + if (!validate_event(event->pmu, &fake_pmu, leader)) return -EINVAL; list_for_each_entry(sibling, &leader->sibling_list, group_entry) { - if (!validate_event(&fake_pmu, sibling)) + if (!validate_event(event->pmu, &fake_pmu, sibling)) return -EINVAL; } - if (!validate_event(&fake_pmu, event)) + if (!validate_event(event->pmu, &fake_pmu, event)) return -EINVAL; return 0; @@ -831,9 +807,9 @@ static const struct attribute_group *pmu_attr_groups[] = { static int cci_pmu_init(struct cci_pmu *cci_pmu, struct platform_device *pdev) { - char *name = pmu_names[probe_cci_revision()]; + char *name = cci_pmu->model->name; cci_pmu->pmu = (struct pmu) { - .name = pmu_names[probe_cci_revision()], + .name = cci_pmu->model->name, .task_ctx_nr = perf_invalid_context, .pmu_enable = cci_pmu_enable, .pmu_disable = cci_pmu_disable, @@ -886,22 +862,93 @@ static struct notifier_block cci_pmu_cpu_nb = { .priority = CPU_PRI_PERF + 1, }; +static struct cci_pmu_model cci_pmu_models[] = { + [CCI_REV_R0] = { + .name = "CCI_400", + .event_ranges = { + [CCI_IF_SLAVE] = { + CCI_REV_R0_SLAVE_PORT_MIN_EV, + CCI_REV_R0_SLAVE_PORT_MAX_EV, + }, + [CCI_IF_MASTER] = { + CCI_REV_R0_MASTER_PORT_MIN_EV, + CCI_REV_R0_MASTER_PORT_MAX_EV, + }, + }, + }, + [CCI_REV_R1] = { + .name = "CCI_400_r1", + .event_ranges = { + [CCI_IF_SLAVE] = { + CCI_REV_R1_SLAVE_PORT_MIN_EV, + CCI_REV_R1_SLAVE_PORT_MAX_EV, + }, + [CCI_IF_MASTER] = { + CCI_REV_R1_MASTER_PORT_MIN_EV, + CCI_REV_R1_MASTER_PORT_MAX_EV, + }, + }, + }, +}; + static const struct of_device_id arm_cci_pmu_matches[] = { { .compatible = "arm,cci-400-pmu", + .data = NULL, + }, + { + .compatible = "arm,cci-400-pmu,r0", + .data = &cci_pmu_models[CCI_REV_R0], + }, + { + .compatible = "arm,cci-400-pmu,r1", + .data = &cci_pmu_models[CCI_REV_R1], }, {}, }; +static inline const struct cci_pmu_model *get_cci_model(struct platform_device *pdev) +{ + const struct of_device_id *match = of_match_node(arm_cci_pmu_matches, + pdev->dev.of_node); + if (!match) + return NULL; + if (match->data) + return match->data; + + dev_warn(&pdev->dev, "DEPRECATED compatible property," + "requires secure access to CCI registers"); + return probe_cci_model(pdev); +} + +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 cci_pmu_probe(struct platform_device *pdev) { struct resource *res; int i, ret, irq; + const struct cci_pmu_model *model; + + model = get_cci_model(pdev); + if (!model) { + dev_warn(&pdev->dev, "CCI PMU version not supported\n"); + return -ENODEV; + } pmu = devm_kzalloc(&pdev->dev, sizeof(*pmu), GFP_KERNEL); if (!pmu) return -ENOMEM; + pmu->model = model; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); pmu->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(pmu->base)) @@ -933,12 +980,6 @@ static int cci_pmu_probe(struct platform_device *pdev) 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; - } - raw_spin_lock_init(&pmu->hw_events.pmu_lock); mutex_init(&pmu->reserve_mutex); atomic_set(&pmu->active_events, 0); @@ -952,6 +993,7 @@ static int cci_pmu_probe(struct platform_device *pdev) if (ret) return ret; + pr_info("ARM %s PMU driver probed", pmu->model->name); return 0; } @@ -963,7 +1005,66 @@ static int cci_platform_probe(struct platform_device *pdev) return of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); } -#endif /* 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 /* !CONFIG_ARM_CCI400_PMU */ + +static int __init cci_platform_init(void) +{ + return 0; +} + +#endif /* CONFIG_ARM_CCI400_PMU */ + +#ifdef CONFIG_ARM_CCI400_PORT_CTRL + +#define CCI_PORT_CTRL 0x0 +#define CCI_CTRL_STATUS 0xc + +#define CCI_ENABLE_SNOOP_REQ 0x1 +#define CCI_ENABLE_DVM_REQ 0x2 +#define CCI_ENABLE_REQ (CCI_ENABLE_SNOOP_REQ | CCI_ENABLE_DVM_REQ) + +enum cci_ace_port_type { + ACE_INVALID_PORT = 0x0, + ACE_PORT, + ACE_LITE_PORT, +}; + +struct cci_ace_port { + void __iomem *base; + unsigned long phys; + enum cci_ace_port_type type; + struct device_node *dn; +}; + +static struct cci_ace_port *ports; +static unsigned int nb_cci_ports; struct cpu_port { u64 mpidr; @@ -1284,36 +1385,20 @@ int notrace __cci_control_port_by_index(u32 port, bool enable) } EXPORT_SYMBOL_GPL(__cci_control_port_by_index); -static const struct cci_nb_ports cci400_ports = { - .nb_ace = 2, - .nb_ace_lite = 3 -}; - -static const struct of_device_id arm_cci_matches[] = { - {.compatible = "arm,cci-400", .data = &cci400_ports }, - {}, -}; - static const struct of_device_id arm_cci_ctrl_if_matches[] = { {.compatible = "arm,cci-400-ctrl-if", }, {}, }; -static int cci_probe(void) +static int cci_probe_ports(struct device_node *np) { struct cci_nb_ports const *cci_config; int ret, i, nb_ace = 0, nb_ace_lite = 0; - struct device_node *np, *cp; + struct device_node *cp; struct resource res; const char *match_str; bool is_ace; - np = of_find_matching_node(NULL, arm_cci_matches); - if (!np) - return -ENODEV; - - if (!of_device_is_available(np)) - return -ENODEV; cci_config = of_match_node(arm_cci_matches, np)->data; if (!cci_config) @@ -1325,17 +1410,6 @@ static int cci_probe(void) if (!ports) return -ENOMEM; - ret = of_address_to_resource(np, 0, &res); - if (!ret) { - cci_ctrl_base = ioremap(res.start, resource_size(&res)); - cci_ctrl_phys = res.start; - } - if (ret || !cci_ctrl_base) { - WARN(1, "unable to ioremap CCI ctrl\n"); - ret = -ENXIO; - goto memalloc_err; - } - for_each_child_of_node(np, cp) { if (!of_match_node(arm_cci_ctrl_if_matches, cp)) continue; @@ -1395,12 +1469,37 @@ static int cci_probe(void) sync_cache_w(&cpu_port); __sync_cache_range_w(ports, sizeof(*ports) * nb_cci_ports); pr_info("ARM CCI driver probed\n"); + return 0; +} +#else /* !CONFIG_ARM_CCI400_PORT_CTRL */ +static inline int cci_probe_ports(struct device_node *np) +{ + return 0; +} +#endif /* CONFIG_ARM_CCI400_PORT_CTRL */ -memalloc_err: +static int cci_probe(void) +{ + int ret; + struct device_node *np; + struct resource res; + + np = of_find_matching_node(NULL, arm_cci_matches); + if(!np || !of_device_is_available(np)) + return -ENODEV; - kfree(ports); - return ret; + ret = of_address_to_resource(np, 0, &res); + if (!ret) { + cci_ctrl_base = ioremap(res.start, resource_size(&res)); + cci_ctrl_phys = res.start; + } + if (ret || !cci_ctrl_base) { + WARN(1, "unable to ioremap CCI ctrl\n"); + return -ENXIO; + } + + return cci_probe_ports(np); } static int cci_init_status = -EAGAIN; @@ -1418,42 +1517,6 @@ static int 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 diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c index 0958b6981773..e98d15eaa799 100644 --- a/drivers/bus/imx-weim.c +++ b/drivers/bus/imx-weim.c @@ -142,7 +142,7 @@ static int __init weim_parse_dt(struct platform_device *pdev, &pdev->dev); const struct imx_weim_devtype *devtype = of_id->data; struct device_node *child; - int ret; + int ret, have_child = 0; if (devtype == &imx50_weim_devtype) { ret = imx_weim_gpr_setup(pdev); @@ -155,14 +155,15 @@ static int __init weim_parse_dt(struct platform_device *pdev, continue; ret = weim_timing_setup(child, base, devtype); - if (ret) { - dev_err(&pdev->dev, "%s set timing failed.\n", + if (ret) + dev_warn(&pdev->dev, "%s set timing failed.\n", child->full_name); - return ret; - } + else + have_child = 1; } - ret = of_platform_populate(pdev->dev.of_node, + if (have_child) + ret = of_platform_populate(pdev->dev.of_node, of_default_bus_match_table, NULL, &pdev->dev); if (ret) diff --git a/drivers/bus/mips_cdmm.c b/drivers/bus/mips_cdmm.c index 5bd792c68f9b..ab3bde16ecb4 100644 --- a/drivers/bus/mips_cdmm.c +++ b/drivers/bus/mips_cdmm.c @@ -453,7 +453,7 @@ void __iomem *mips_cdmm_early_probe(unsigned int dev_type) /* Look for a specific device type */ for (; drb < bus->drbs; drb += size + 1) { - acsr = readl(cdmm + drb * CDMM_DRB_SIZE); + acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE); type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT; if (type == dev_type) return cdmm + drb * CDMM_DRB_SIZE; @@ -500,7 +500,7 @@ static void mips_cdmm_bus_discover(struct mips_cdmm_bus *bus) bus->discovered = true; pr_info("cdmm%u discovery (%u blocks)\n", cpu, bus->drbs); for (; drb < bus->drbs; drb += size + 1) { - acsr = readl(cdmm + drb * CDMM_DRB_SIZE); + acsr = __raw_readl(cdmm + drb * CDMM_DRB_SIZE); type = (acsr & CDMM_ACSR_DEVTYPE) >> CDMM_ACSR_DEVTYPE_SHIFT; size = (acsr & CDMM_ACSR_DEVSIZE) >> CDMM_ACSR_DEVSIZE_SHIFT; rev = (acsr & CDMM_ACSR_DEVREV) >> CDMM_ACSR_DEVREV_SHIFT; diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index fb9ec6221730..6f047dcb94c2 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -58,7 +58,6 @@ #include <linux/debugfs.h> #include <linux/log2.h> #include <linux/syscore_ops.h> -#include <linux/memblock.h> /* * DDR target is the same on all platforms. @@ -70,6 +69,7 @@ */ #define WIN_CTRL_OFF 0x0000 #define WIN_CTRL_ENABLE BIT(0) +/* Only on HW I/O coherency capable platforms */ #define WIN_CTRL_SYNCBARRIER BIT(1) #define WIN_CTRL_TGT_MASK 0xf0 #define WIN_CTRL_TGT_SHIFT 4 @@ -102,9 +102,7 @@ /* Relative to mbusbridge_base */ #define MBUS_BRIDGE_CTRL_OFF 0x0 -#define MBUS_BRIDGE_SIZE_MASK 0xffff0000 #define MBUS_BRIDGE_BASE_OFF 0x4 -#define MBUS_BRIDGE_BASE_MASK 0xffff0000 /* Maximum number of windows, for all known platforms */ #define MBUS_WINS_MAX 20 @@ -323,8 +321,9 @@ static int mvebu_mbus_setup_window(struct mvebu_mbus_state *mbus, ctrl = ((size - 1) & WIN_CTRL_SIZE_MASK) | (attr << WIN_CTRL_ATTR_SHIFT) | (target << WIN_CTRL_TGT_SHIFT) | - WIN_CTRL_SYNCBARRIER | WIN_CTRL_ENABLE; + if (mbus->hw_io_coherency) + ctrl |= WIN_CTRL_SYNCBARRIER; writel(base & WIN_BASE_LOW, addr + WIN_BASE_OFF); writel(ctrl, addr + WIN_CTRL_OFF); @@ -577,106 +576,36 @@ static unsigned int armada_xp_mbus_win_remap_offset(int win) return MVEBU_MBUS_NO_REMAP; } -/* - * Use the memblock information to find the MBus bridge hole in the - * physical address space. - */ -static void __init -mvebu_mbus_find_bridge_hole(uint64_t *start, uint64_t *end) -{ - struct memblock_region *r; - uint64_t s = 0; - - for_each_memblock(memory, r) { - /* - * This part of the memory is above 4 GB, so we don't - * care for the MBus bridge hole. - */ - if (r->base >= 0x100000000) - continue; - - /* - * The MBus bridge hole is at the end of the RAM under - * the 4 GB limit. - */ - if (r->base + r->size > s) - s = r->base + r->size; - } - - *start = s; - *end = 0x100000000; -} - static void __init mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus) { int i; int cs; - uint64_t mbus_bridge_base, mbus_bridge_end; mvebu_mbus_dram_info.mbus_dram_target_id = TARGET_DDR; - mvebu_mbus_find_bridge_hole(&mbus_bridge_base, &mbus_bridge_end); - for (i = 0, cs = 0; i < 4; i++) { - u64 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i)); - u64 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i)); - u64 end; - struct mbus_dram_window *w; - - /* Ignore entries that are not enabled */ - if (!(size & DDR_SIZE_ENABLED)) - continue; - - /* - * Ignore entries whose base address is above 2^32, - * since devices cannot DMA to such high addresses - */ - if (base & DDR_BASE_CS_HIGH_MASK) - continue; - - base = base & DDR_BASE_CS_LOW_MASK; - size = (size | ~DDR_SIZE_MASK) + 1; - end = base + size; - - /* - * Adjust base/size of the current CS to make sure it - * doesn't overlap with the MBus bridge hole. This is - * particularly important for devices that do DMA from - * DRAM to a SRAM mapped in a MBus window, such as the - * CESA cryptographic engine. - */ + u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i)); + u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i)); /* - * The CS is fully enclosed inside the MBus bridge - * area, so ignore it. + * We only take care of entries for which the chip + * select is enabled, and that don't have high base + * address bits set (devices can only access the first + * 32 bits of the memory). */ - if (base >= mbus_bridge_base && end <= mbus_bridge_end) - continue; + if ((size & DDR_SIZE_ENABLED) && + !(base & DDR_BASE_CS_HIGH_MASK)) { + struct mbus_dram_window *w; - /* - * Beginning of CS overlaps with end of MBus, raise CS - * base address, and shrink its size. - */ - if (base >= mbus_bridge_base && end > mbus_bridge_end) { - size -= mbus_bridge_end - base; - base = mbus_bridge_end; + w = &mvebu_mbus_dram_info.cs[cs++]; + w->cs_index = i; + w->mbus_attr = 0xf & ~(1 << i); + if (mbus->hw_io_coherency) + w->mbus_attr |= ATTR_HW_COHERENCY; + w->base = base & DDR_BASE_CS_LOW_MASK; + w->size = (size | ~DDR_SIZE_MASK) + 1; } - - /* - * End of CS overlaps with beginning of MBus, shrink - * CS size. - */ - if (base < mbus_bridge_base && end > mbus_bridge_base) - size -= end - mbus_bridge_base; - - w = &mvebu_mbus_dram_info.cs[cs++]; - w->cs_index = i; - w->mbus_attr = 0xf & ~(1 << i); - if (mbus->hw_io_coherency) - w->mbus_attr |= ATTR_HW_COHERENCY; - w->base = base; - w->size = size; } mvebu_mbus_dram_info.num_cs = cs; } diff --git a/drivers/bus/omap-ocp2scp.c b/drivers/bus/omap-ocp2scp.c index 723ec06ad2c8..9f1856948758 100644 --- a/drivers/bus/omap-ocp2scp.c +++ b/drivers/bus/omap-ocp2scp.c @@ -16,6 +16,7 @@ * */ +#include <linux/io.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/err.h> @@ -23,6 +24,9 @@ #include <linux/of.h> #include <linux/of_platform.h> +#define OCP2SCP_TIMING 0x18 +#define SYNC2_MASK 0xf + static int ocp2scp_remove_devices(struct device *dev, void *c) { struct platform_device *pdev = to_platform_device(dev); @@ -35,6 +39,9 @@ static int ocp2scp_remove_devices(struct device *dev, void *c) static int omap_ocp2scp_probe(struct platform_device *pdev) { int ret; + u32 reg; + void __iomem *regs; + struct resource *res; struct device_node *np = pdev->dev.of_node; if (np) { @@ -47,6 +54,32 @@ static int omap_ocp2scp_probe(struct platform_device *pdev) } pm_runtime_enable(&pdev->dev); + /* + * As per AM572x TRM: http://www.ti.com/lit/ug/spruhz6/spruhz6.pdf + * under section 26.3.2.2, table 26-26 OCP2SCP TIMING Caution; + * As per OMAP4430 TRM: http://www.ti.com/lit/ug/swpu231ap/swpu231ap.pdf + * under section 23.12.6.2.2 , Table 23-1213 OCP2SCP TIMING Caution; + * As per OMAP4460 TRM: http://www.ti.com/lit/ug/swpu235ab/swpu235ab.pdf + * under section 23.12.6.2.2, Table 23-1213 OCP2SCP TIMING Caution; + * As per OMAP543x TRM http://www.ti.com/lit/pdf/swpu249 + * under section 27.3.2.2, Table 27-27 OCP2SCP TIMING Caution; + * + * Read path of OCP2SCP is not working properly due to low reset value + * of SYNC2 parameter in OCP2SCP. Suggested reset value is 0x6 or more. + */ + if (!of_device_is_compatible(np, "ti,am437x-ocp2scp")) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + goto err0; + + pm_runtime_get_sync(&pdev->dev); + reg = readl_relaxed(regs + OCP2SCP_TIMING); + reg &= ~(SYNC2_MASK); + reg |= 0x6; + writel_relaxed(reg, regs + OCP2SCP_TIMING); + pm_runtime_put_sync(&pdev->dev); + } return 0; @@ -67,6 +100,7 @@ static int omap_ocp2scp_remove(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id omap_ocp2scp_id_table[] = { { .compatible = "ti,omap-ocp2scp" }, + { .compatible = "ti,am437x-ocp2scp" }, {} }; MODULE_DEVICE_TABLE(of, omap_ocp2scp_id_table); diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 11f7982cbdb3..ebee57d715d2 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -1,7 +1,7 @@ /* * OMAP L3 Interconnect error handling driver * - * Copyright (C) 2011-2014 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2011-2015 Texas Instruments Incorporated - http://www.ti.com/ * Santosh Shilimkar <santosh.shilimkar@ti.com> * Sricharan <r.sricharan@ti.com> * @@ -233,7 +233,8 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3) } static const struct of_device_id l3_noc_match[] = { - {.compatible = "ti,omap4-l3-noc", .data = &omap_l3_data}, + {.compatible = "ti,omap4-l3-noc", .data = &omap4_l3_data}, + {.compatible = "ti,omap5-l3-noc", .data = &omap5_l3_data}, {.compatible = "ti,dra7-l3-noc", .data = &dra_l3_data}, {.compatible = "ti,am4372-l3-noc", .data = &am4372_l3_data}, {}, diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index 95254585db86..73431f81da28 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -1,7 +1,7 @@ /* * OMAP L3 Interconnect error handling driver header * - * Copyright (C) 2011-2014 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2011-2015 Texas Instruments Incorporated - http://www.ti.com/ * Santosh Shilimkar <santosh.shilimkar@ti.com> * sricharan <r.sricharan@ti.com> * @@ -175,16 +175,14 @@ static struct l3_flagmux_data omap_l3_flagmux_clk2 = { }; -static struct l3_target_data omap_l3_target_data_clk3[] = { - {0x0100, "EMUSS",}, - {0x0300, "DEBUG SOURCE",}, - {0x0, "HOST CLK3",}, +static struct l3_target_data omap4_l3_target_data_clk3[] = { + {0x0100, "DEBUGSS",}, }; -static struct l3_flagmux_data omap_l3_flagmux_clk3 = { +static struct l3_flagmux_data omap4_l3_flagmux_clk3 = { .offset = 0x0200, - .l3_targ = omap_l3_target_data_clk3, - .num_targ_data = ARRAY_SIZE(omap_l3_target_data_clk3), + .l3_targ = omap4_l3_target_data_clk3, + .num_targ_data = ARRAY_SIZE(omap4_l3_target_data_clk3), }; static struct l3_masters_data omap_l3_masters[] = { @@ -215,21 +213,49 @@ static struct l3_masters_data omap_l3_masters[] = { { 0x32, "USBHOSTFS"} }; -static struct l3_flagmux_data *omap_l3_flagmux[] = { +static struct l3_flagmux_data *omap4_l3_flagmux[] = { &omap_l3_flagmux_clk1, &omap_l3_flagmux_clk2, - &omap_l3_flagmux_clk3, + &omap4_l3_flagmux_clk3, }; -static const struct omap_l3 omap_l3_data = { - .l3_flagmux = omap_l3_flagmux, - .num_modules = ARRAY_SIZE(omap_l3_flagmux), +static const struct omap_l3 omap4_l3_data = { + .l3_flagmux = omap4_l3_flagmux, + .num_modules = ARRAY_SIZE(omap4_l3_flagmux), .l3_masters = omap_l3_masters, .num_masters = ARRAY_SIZE(omap_l3_masters), /* The 6 MSBs of register field used to distinguish initiator */ .mst_addr_mask = 0xFC, }; +/* OMAP5 data */ +static struct l3_target_data omap5_l3_target_data_clk3[] = { + {0x0100, "L3INSTR",}, + {0x0300, "DEBUGSS",}, + {0x0, "HOSTCLK3",}, +}; + +static struct l3_flagmux_data omap5_l3_flagmux_clk3 = { + .offset = 0x0200, + .l3_targ = omap5_l3_target_data_clk3, + .num_targ_data = ARRAY_SIZE(omap5_l3_target_data_clk3), +}; + +static struct l3_flagmux_data *omap5_l3_flagmux[] = { + &omap_l3_flagmux_clk1, + &omap_l3_flagmux_clk2, + &omap5_l3_flagmux_clk3, +}; + +static const struct omap_l3 omap5_l3_data = { + .l3_flagmux = omap5_l3_flagmux, + .num_modules = ARRAY_SIZE(omap5_l3_flagmux), + .l3_masters = omap_l3_masters, + .num_masters = ARRAY_SIZE(omap_l3_masters), + /* The 6 MSBs of register field used to distinguish initiator */ + .mst_addr_mask = 0x7E0, +}; + /* DRA7 data */ static struct l3_target_data dra_l3_target_data_clk1[] = { {0x2a00, "AES1",}, @@ -274,7 +300,7 @@ static struct l3_flagmux_data dra_l3_flagmux_clk1 = { static struct l3_target_data dra_l3_target_data_clk2[] = { {0x0, "HOST CLK1",}, - {0x0, "HOST CLK2",}, + {0x800000, "HOST CLK2",}, {0xdead, L3_TARGET_NOT_SUPPORTED,}, {0x3400, "SHA2_2",}, {0x0900, "BB2D",}, diff --git a/drivers/bus/simple-pm-bus.c b/drivers/bus/simple-pm-bus.c new file mode 100644 index 000000000000..c5eb46cbf388 --- /dev/null +++ b/drivers/bus/simple-pm-bus.c @@ -0,0 +1,58 @@ +/* + * Simple Power-Managed Bus Driver + * + * Copyright (C) 2014-2015 Glider bvba + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + + +static int simple_pm_bus_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + pm_runtime_enable(&pdev->dev); + + if (np) + of_platform_populate(np, NULL, NULL, &pdev->dev); + + return 0; +} + +static int simple_pm_bus_remove(struct platform_device *pdev) +{ + dev_dbg(&pdev->dev, "%s\n", __func__); + + pm_runtime_disable(&pdev->dev); + return 0; +} + +static const struct of_device_id simple_pm_bus_of_match[] = { + { .compatible = "simple-pm-bus", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, simple_pm_bus_of_match); + +static struct platform_driver simple_pm_bus_driver = { + .probe = simple_pm_bus_probe, + .remove = simple_pm_bus_remove, + .driver = { + .name = "simple-pm-bus", + .of_match_table = simple_pm_bus_of_match, + }, +}; + +module_platform_driver(simple_pm_bus_driver); + +MODULE_DESCRIPTION("Simple Power-Managed Bus Driver"); +MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>"); +MODULE_LICENSE("GPL v2"); |