mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 02:32:09 -04:00
ARM: SoC driver updates for v4.1
Driver updates for v4.1. Some of these are for drivers/soc, where we find more
and more SoC-specific drivers these days. Some are for other driver subsystems
where we have received acks from the appropriate maintainers.
The larger parts of this branch are:
- MediaTek support for their PMIC wrapper interface, a high-level interface
for talking to the system PMIC over a dedicated I2C interface.
- Qualcomm SCM driver has been moved to drivers/firmware. It's used for CPU
up/down and needs to be in a shared location for arm/arm64 common code.
- Cleanup of ARM-CCI PMU code.
- Anoter set of cleanusp to the OMAP GPMC code.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQIcBAABAgAGBQJVNzKYAAoJEIwa5zzehBx3UJ8P/37OA1Qc5vR/Kyc8WlhPlNFV
MRE7ajM+FMd/Islt4nVNKATK2o3peCTPrqcniDfdPmN2dM1l4LvdeIvkIhKpB09h
ovmYTZLI/AIbbkttWybGO4lVpFeATxX5N91XXBHvbqkMh6N6ppiYWZUYeJs9EhAw
2YKykfDCTjKykS+m4YThXw9SCF/6mkCvaBL2VAuKnoV0ygjQD109Fce/irKaAoyw
L2w4PXOimUk8RshTx3afKCgTotMS0e9JWjKjvDO5M2KAD8DHm7PDRMmRVzA2sSFG
E2BCfh2DTjzJjdfRYsTd1bYnWzvakX1CzLjiFv+Sb0ctanoZdiDtJIDpX+vSXZ+D
W7i0yhEWIrr2qaZOyXR8znw8BhzdVZhmT+O76N47HvzMb5JUkaZBhEUBfUyeDbk+
YhEwz1G+YxT+fg7bBrxa3vLzOJ2pUwVJPITNuPKr6eZJmaBqgx2M7xXs3KyIJX1Y
AV9eUs2uNCyonawO6xXRhlUKREKL1TftqNXfLj7MYg6AaoVqK0qNRfuCdhMFhMdN
88QCl/qpPtIDL6ZnxlOejs30+DCH1QPx6+GhxkUlVlF1j/2ZM0cK2cj3tggDbNvN
uSy/g5pYDp62Az8UCoNXmKdnb4UONigb7k49naZbW/9CGRp1rrmUdzPUAm0tHMBT
HsH7ms+nHAZHrAlNvvtP
=/sWo
-----END PGP SIGNATURE-----
Merge tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver updates from Olof Johansson:
"Driver updates for v4.1. Some of these are for drivers/soc, where we
find more and more SoC-specific drivers these days. Some are for
other driver subsystems where we have received acks from the
appropriate maintainers.
The larger parts of this branch are:
- MediaTek support for their PMIC wrapper interface, a high-level
interface for talking to the system PMIC over a dedicated I2C
interface.
- Qualcomm SCM driver has been moved to drivers/firmware. It's used
for CPU up/down and needs to be in a shared location for arm/arm64
common code.
- cleanup of ARM-CCI PMU code.
- another set of cleanusp to the OMAP GPMC code"
* tag 'armsoc-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (43 commits)
soc/mediatek: Remove unused variables
clocksource: atmel-st: select MFD_SYSCON
soc: mediatek: Add PMIC wrapper for MT8135 and MT8173 SoCs
arm-cci: Fix CCI PMU event validation
arm-cci: Split the code for PMU vs driver support
arm-cci: Get rid of secure transactions for PMU driver
arm-cci: Abstract the CCI400 PMU specific definitions
arm-cci: Rearrange code for splitting PMU vs driver code
drivers: cci: reject groups spanning multiple HW PMUs
ARM: at91: remove useless include
clocksource: atmel-st: remove mach/hardware dependency
clocksource: atmel-st: use syscon/regmap
ARM: at91: time: move the system timer driver to drivers/clocksource
ARM: at91: properly initialize timer
ARM: at91: at91rm9200: remove deprecated arm_pm_restart
watchdog: at91rm9200: implement restart handler
watchdog: at91rm9200: use the system timer syscon
mfd: syscon: Add atmel system timer registers definition
ARM: at91/dt: declare atmel,at91rm9200-st as a syscon
soc: qcom: gsbi: Add support for ADM CRCI muxing
...
This commit is contained in:
commit
7d2b6ef19c
52 changed files with 2753 additions and 960 deletions
|
|
@ -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,6 +75,13 @@ config MVEBU_MBUS
|
|||
Driver needed for the MBus configuration on Marvell EBU SoCs
|
||||
(Kirkwood, Dove, Orion5x, MV78XX0 and Armada 370/XP).
|
||||
|
||||
config OMAP_INTERCONNECT
|
||||
tristate "OMAP INTERCONNECT DRIVER"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
|
||||
help
|
||||
Driver to enable OMAP interconnect error handling driver.
|
||||
|
||||
config OMAP_OCP2SCP
|
||||
tristate "OMAP OCP2SCP DRIVER"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
|
|
@ -49,27 +91,18 @@ config OMAP_OCP2SCP
|
|||
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
|
||||
|
||||
config SIMPLE_PM_BUS
|
||||
bool "Simple Power-Managed Bus Driver"
|
||||
depends on OF && PM
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
help
|
||||
Driver to enable OMAP interconnect error handling driver.
|
||||
|
||||
config ARM_CCI
|
||||
bool "ARM CCI driver support"
|
||||
depends on ARM && OF && CPU_V7
|
||||
help
|
||||
Driver supporting the CCI 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.
|
||||
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"
|
||||
|
|
|
|||
|
|
@ -2,17 +2,18 @@
|
|||
# Makefile for the bus drivers.
|
||||
#
|
||||
|
||||
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
|
||||
|
||||
# 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_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
|
||||
|
||||
# Interconnect bus driver for OMAP SoCs.
|
||||
obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
|
||||
|
||||
obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
|
||||
obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
|
||||
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
|
||||
|
|
|
|||
|
|
@ -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 const struct of_device_id arm_cci_matches[] = {
|
||||
#ifdef CONFIG_ARM_CCI400_COMMON
|
||||
{.compatible = "arm,cci-400", .data = CCI400_PORTS_DATA },
|
||||
#endif
|
||||
{},
|
||||
};
|
||||
|
||||
static struct cci_ace_port *ports;
|
||||
static unsigned int nb_cci_ports;
|
||||
#ifdef CONFIG_ARM_CCI400_PMU
|
||||
|
||||
static void __iomem *cci_ctrl_base;
|
||||
static unsigned long cci_ctrl_phys;
|
||||
|
||||
#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,6 +70,66 @@ static unsigned long cci_ctrl_phys;
|
|||
#define CCI_PID2_REV_MASK 0xf0
|
||||
#define CCI_PID2_REV_SHIFT 4
|
||||
|
||||
#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)
|
||||
|
||||
#define CCI_PMU_CNTR_MASK ((1ULL << 32) -1)
|
||||
|
||||
#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 */
|
||||
|
||||
/* Types of interfaces that can generate events */
|
||||
enum {
|
||||
CCI_IF_SLAVE,
|
||||
CCI_IF_MASTER,
|
||||
CCI_IF_MAX,
|
||||
};
|
||||
|
||||
struct event_range {
|
||||
u32 min;
|
||||
u32 max;
|
||||
};
|
||||
|
||||
struct cci_pmu_hw_events {
|
||||
struct perf_event *events[CCI_PMU_MAX_HW_EVENTS];
|
||||
unsigned long used_mask[BITS_TO_LONGS(CCI_PMU_MAX_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;
|
||||
const struct cci_pmu_model *model;
|
||||
struct cci_pmu_hw_events hw_events;
|
||||
struct platform_device *plat_device;
|
||||
int num_events;
|
||||
atomic_t active_events;
|
||||
struct mutex reserve_mutex;
|
||||
cpumask_t cpus;
|
||||
};
|
||||
static struct cci_pmu *pmu;
|
||||
|
||||
#define to_cci_pmu(c) (container_of(c, struct cci_pmu, pmu))
|
||||
|
||||
/* Port ids */
|
||||
#define CCI_PORT_S0 0
|
||||
#define CCI_PORT_S1 1
|
||||
|
|
@ -89,17 +144,6 @@ static unsigned long cci_ctrl_phys;
|
|||
#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
|
||||
#define CCI_PMU_OVRFLW 0x00c
|
||||
|
||||
#define CCI_PMU_OVRFLW_FLAG 1
|
||||
|
||||
#define CCI_PMU_CNTR_BASE(idx) ((idx) * SZ_4K)
|
||||
|
||||
#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
|
||||
|
|
@ -109,12 +153,6 @@ 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)
|
||||
|
|
@ -141,70 +179,39 @@ enum cci400_perf_events {
|
|||
#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,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* 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 cci_pmu_hw_events {
|
||||
struct perf_event *events[CCI_PMU_MAX_HW_EVENTS];
|
||||
unsigned long used_mask[BITS_TO_LONGS(CCI_PMU_MAX_HW_EVENTS)];
|
||||
raw_spinlock_t pmu_lock;
|
||||
};
|
||||
|
||||
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;
|
||||
struct cci_pmu_hw_events hw_events;
|
||||
struct platform_device *plat_device;
|
||||
int num_events;
|
||||
atomic_t active_events;
|
||||
struct mutex reserve_mutex;
|
||||
cpumask_t cpus;
|
||||
};
|
||||
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)
|
||||
static int pmu_validate_hw_event(unsigned long hw_event)
|
||||
{
|
||||
int i;
|
||||
u8 ev_source = CCI_PMU_EVENT_SOURCE(hw_event);
|
||||
u8 ev_code = CCI_PMU_EVENT_CODE(hw_event);
|
||||
int if_type;
|
||||
|
||||
for (i = 0; i < nr_irqs; i++)
|
||||
if (irq == irqs[i])
|
||||
return true;
|
||||
if (hw_event & ~CCI_PMU_EVENT_MASK)
|
||||
return -ENOENT;
|
||||
|
||||
return false;
|
||||
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_type = CCI_IF_SLAVE;
|
||||
break;
|
||||
case CCI_PORT_M0:
|
||||
case CCI_PORT_M1:
|
||||
case CCI_PORT_M2:
|
||||
/* Master Interface */
|
||||
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)
|
||||
|
|
@ -219,50 +226,11 @@ static int probe_cci_revision(void)
|
|||
return CCI_REV_R1;
|
||||
}
|
||||
|
||||
static struct pmu_port_event_ranges *port_range_by_rev(void)
|
||||
static const struct cci_pmu_model *probe_cci_model(struct platform_device *pdev)
|
||||
{
|
||||
int rev = probe_cci_revision();
|
||||
|
||||
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;
|
||||
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)
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -687,15 +663,15 @@ validate_group(struct perf_event *event)
|
|||
.used_mask = CPU_BITS_NONE,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
kfree(ports);
|
||||
return ret;
|
||||
np = of_find_matching_node(NULL, arm_cci_matches);
|
||||
if(!np || !of_device_is_available(np))
|
||||
return -ENODEV;
|
||||
|
||||
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
|
||||
|
|
|
|||
58
drivers/bus/simple-pm-bus.c
Normal file
58
drivers/bus/simple-pm-bus.c
Normal file
|
|
@ -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 <[email protected]>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
Loading…
Reference in a new issue