From 201c3a81a345f24c9ce5424cd1bb72559921e5c2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 2 May 2014 13:38:31 -0700 Subject: arm64: topology: Tell the scheduler about the relative power of cores In heterogeneous systems like big.LITTLE systems the scheduler will be able to make better use of the available cores if we provide power numbers to it indicating their relative performance. Do this by parsing the CPU nodes in the DT. This code currently has no effect as no information on the relative performance of the cores is provided. Signed-off-by: Mark Brown Signed-off-by: Jon Medhurst --- arch/arm64/kernel/topology.c | 153 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index fcb8f7b42271..20434005b0fa 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -19,10 +19,34 @@ #include #include #include +#include #include #include +/* + * cpu power table + * This per cpu data structure describes the relative capacity of each core. + * On a heteregenous system, cores don't have the same computation capacity + * and we reflect that difference in the cpu_power field so the scheduler can + * take this difference into account during load balance. A per cpu structure + * is preferred because each CPU updates its own cpu_power field during the + * load balance except for idle cores. One idle core is selected to run the + * rebalance_domains for all idle cores and the cpu_power can be updated + * during this sequence. + */ +static DEFINE_PER_CPU(unsigned long, cpu_scale); + +unsigned long arch_scale_freq_power(struct sched_domain *sd, int cpu) +{ + return per_cpu(cpu_scale, cpu); +} + +static void set_power_scale(unsigned int cpu, unsigned long power) +{ + per_cpu(cpu_scale, cpu) = power; +} + static int __init get_cpu_for_node(struct device_node *node) { struct device_node *cpu_node; @@ -161,6 +185,38 @@ static int __init parse_cluster(struct device_node *cluster, int depth) return 0; } +struct cpu_efficiency { + const char *compatible; + unsigned long efficiency; +}; + +/* + * Table of relative efficiency of each processors + * The efficiency value must fit in 20bit and the final + * cpu_scale value must be in the range + * 0 < cpu_scale < 3*SCHED_POWER_SCALE/2 + * in order to return at most 1 when DIV_ROUND_CLOSEST + * is used to compute the capacity of a CPU. + * Processors that are not defined in the table, + * use the default SCHED_POWER_SCALE value for cpu_scale. + */ +static const struct cpu_efficiency table_efficiency[] = { + { NULL, }, +}; + +static unsigned long *__cpu_capacity; +#define cpu_capacity(cpu) __cpu_capacity[cpu] + +static unsigned long middle_capacity = 1; + +/* + * Iterate all CPUs' descriptor in DT and compute the efficiency + * (as per table_efficiency). Also calculate a middle efficiency + * as close as possible to (max{eff_i} - min{eff_i}) / 2 + * This is later used to scale the cpu_power field such that an + * 'average' CPU is of middle power. Also see the comments near + * table_efficiency[] and update_cpu_power(). + */ static int __init parse_dt_topology(void) { struct device_node *cn, *map; @@ -200,6 +256,91 @@ out: return ret; } +static void __init parse_dt_cpu_power(void) +{ + const struct cpu_efficiency *cpu_eff; + struct device_node *cn; + unsigned long min_capacity = ULONG_MAX; + unsigned long max_capacity = 0; + unsigned long capacity = 0; + int cpu; + + __cpu_capacity = kcalloc(nr_cpu_ids, sizeof(*__cpu_capacity), + GFP_NOWAIT); + + for_each_possible_cpu(cpu) { + const u32 *rate; + int len; + + /* Too early to use cpu->of_node */ + cn = of_get_cpu_node(cpu, NULL); + if (!cn) { + pr_err("Missing device node for CPU %d\n", cpu); + continue; + } + + for (cpu_eff = table_efficiency; cpu_eff->compatible; cpu_eff++) + if (of_device_is_compatible(cn, cpu_eff->compatible)) + break; + + if (cpu_eff->compatible == NULL) { + pr_warn("%s: Unknown CPU type\n", cn->full_name); + continue; + } + + rate = of_get_property(cn, "clock-frequency", &len); + if (!rate || len != 4) { + pr_err("%s: Missing clock-frequency property\n", + cn->full_name); + continue; + } + + capacity = ((be32_to_cpup(rate)) >> 20) * cpu_eff->efficiency; + + /* Save min capacity of the system */ + if (capacity < min_capacity) + min_capacity = capacity; + + /* Save max capacity of the system */ + if (capacity > max_capacity) + max_capacity = capacity; + + cpu_capacity(cpu) = capacity; + } + + /* If min and max capacities are equal we bypass the update of the + * cpu_scale because all CPUs have the same capacity. Otherwise, we + * compute a middle_capacity factor that will ensure that the capacity + * of an 'average' CPU of the system will be as close as possible to + * SCHED_POWER_SCALE, which is the default value, but with the + * constraint explained near table_efficiency[]. + */ + if (min_capacity == max_capacity) + return; + else if (4 * max_capacity < (3 * (max_capacity + min_capacity))) + middle_capacity = (min_capacity + max_capacity) + >> (SCHED_POWER_SHIFT+1); + else + middle_capacity = ((max_capacity / 3) + >> (SCHED_POWER_SHIFT-1)) + 1; +} + +/* + * Look for a customed capacity of a CPU in the cpu_topo_data table during the + * boot. The update of all CPUs is in O(n^2) for heteregeneous system but the + * function returns directly for SMP system. + */ +static void update_cpu_power(unsigned int cpu) +{ + if (!cpu_capacity(cpu)) + return; + + set_power_scale(cpu, cpu_capacity(cpu) / middle_capacity); + + pr_info("CPU%u: update cpu_power %lu\n", + cpu, arch_scale_freq_power(NULL, cpu)); +} + /* * cpu topology table */ @@ -272,6 +413,7 @@ void store_cpu_topology(unsigned int cpuid) topology_populated: update_siblings_masks(cpuid); + update_cpu_power(cpuid); } static void __init reset_cpu_topology(void) @@ -292,6 +434,14 @@ static void __init reset_cpu_topology(void) } } +static void __init reset_cpu_power(void) +{ + unsigned int cpu; + + for_each_possible_cpu(cpu) + set_power_scale(cpu, SCHED_POWER_SCALE); +} + void __init init_cpu_topology(void) { reset_cpu_topology(); @@ -302,4 +452,7 @@ void __init init_cpu_topology(void) */ if (parse_dt_topology()) reset_cpu_topology(); + + reset_cpu_power(); + parse_dt_cpu_power(); } -- cgit v1.2.3 From b305937675a8ebac6525aa6b3fe14903606d5984 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 2 May 2014 13:38:32 -0700 Subject: arm64: topology: Provide relative power numbers for cores Provide performance numbers to the scheduler to help it fill the cores in the system on big.LITTLE systems. With the current scheduler this may perform poorly for applications that try to do OpenMP style work over all cores but should help for more common workloads. The current 32 bit ARM implementation provides a similar estimate so this helps ensure that work to improve big.LITTLE systems on ARMv7 systems performs similarly on ARMv8 systems. The power numbers are the same as for ARMv7 since it seems that the expected differential between the big and little cores is very similar on both ARMv7 and ARMv8. In both ARMv7 and ARMv8 cases the numbers were based on the published DMIPS numbers. These numbers are just an initial and basic approximation for use with the current scheduler, it is likely that both experience with silicon and ongoing work on improving the scheduler will lead to further tuning or will tune automatically at runtime and so make the specific choice of numbers here less critical. Signed-off-by: Mark Brown Signed-off-by: Jon Medhurst --- arch/arm64/kernel/topology.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index 20434005b0fa..45e805b357eb 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -201,6 +201,8 @@ struct cpu_efficiency { * use the default SCHED_POWER_SCALE value for cpu_scale. */ static const struct cpu_efficiency table_efficiency[] = { + { "arm,cortex-a57", 3891 }, + { "arm,cortex-a53", 2048 }, { NULL, }, }; -- cgit v1.2.3 From a1078367c8bdf00bc56e2edde3aeb326970f639b Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 15 Apr 2014 15:14:27 +0100 Subject: mailbox: add support for ARM Message Handling Unit(MHU) controller This patch adds support for ARM Message Handling Unit(MHU) controller that provides control logic and interrupt generation to support inter-processor communication between the Application Processor and the System Control Processor(SCP). This support is built on the existing common mailbox framework for client/protocol drivers and controller drivers of Inter Processor Communication(IPC). SCP controls most of the power managament on the Application Processors. Signed-off-by: Sudeep Holla Signed-off-by: Jon Medhurst --- drivers/mailbox/Kconfig | 14 ++ drivers/mailbox/Makefile | 2 + drivers/mailbox/arm_mhu.c | 336 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/mailbox/arm_mhu.h | 31 +++++ 4 files changed, 383 insertions(+) create mode 100644 drivers/mailbox/arm_mhu.c create mode 100644 drivers/mailbox/arm_mhu.h diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index c04fed9eb15d..d61fac9572d4 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -6,6 +6,20 @@ menuconfig MAILBOX signals. Say Y if your platform supports hardware mailboxes. if MAILBOX +config ARM_MHU_MBOX + bool "ARM Message Handling Unit (MHU) Mailbox" + help + This driver provides support for inter-processor communication + between System Control Processor (SCP) with Cortex-M3 processor + and Application Processors (AP) on some ARM based systems with + MHU peripheral. + + SCP controls most of the power managament on the Application + Processors. It offers control and management of: the core/cluster + power states, various power domain DVFS including the core/cluster, + certain system clocks configuration, thermal sensors and many + others. + config PL320_MBOX bool "ARM PL320 Mailbox" depends on ARM_AMBA diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index dd412c22208b..867b57313794 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -2,6 +2,8 @@ obj-$(CONFIG_MAILBOX) += mailbox.o +obj-$(CONFIG_ARM_MHU_MBOX) += arm_mhu.o + obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c new file mode 100644 index 000000000000..841b0cb1b710 --- /dev/null +++ b/drivers/mailbox/arm_mhu.c @@ -0,0 +1,336 @@ +/* + * Driver for the Message Handling Unit (MHU) which is the peripheral in + * the Compute SubSystem (CSS) providing a mechanism for inter-processor + * communication between System Control Processor (SCP) with Cortex-M3 + * processor and Application Processors (AP). + * + * The MHU peripheral provides a mechanism to assert interrupt signals to + * facilitate inter-processor message passing between the SCP and the AP. + * The message payload can be deposited into main memory or on-chip memories. + * The MHU supports three bi-directional channels - low priority, high + * priority and secure(can't be used in non-secure execution modes) + * + * Copyright (C) 2014 ARM Ltd. + * + * Author: Sudeep Holla + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arm_mhu.h" + +#define DRIVER_NAME CONTROLLER_NAME"_drv" + +/* + * +--------------------+-------+---------------+ + * | Hardware Register | Offset| Driver View | + * +--------------------+-------+---------------+ + * | SCP_INTR_L_STAT | 0x000 | RX_STATUS(L) | + * | SCP_INTR_L_SET | 0x008 | RX_SET(L) | + * | SCP_INTR_L_CLEAR | 0x010 | RX_CLEAR(L) | + * +--------------------+-------+---------------+ + * | SCP_INTR_H_STAT | 0x020 | RX_STATUS(H) | + * | SCP_INTR_H_SET | 0x028 | RX_SET(H) | + * | SCP_INTR_H_CLEAR | 0x030 | RX_CLEAR(H) | + * +--------------------+-------+---------------+ + * | CPU_INTR_L_STAT | 0x100 | TX_STATUS(L) | + * | CPU_INTR_L_SET | 0x108 | TX_SET(L) | + * | CPU_INTR_L_CLEAR | 0x110 | TX_CLEAR(L) | + * +--------------------+-------+---------------+ + * | CPU_INTR_H_STAT | 0x120 | TX_STATUS(H) | + * | CPU_INTR_H_SET | 0x128 | TX_SET(H) | + * | CPU_INTR_H_CLEAR | 0x130 | TX_CLEAR(H) | + * +--------------------+-------+---------------+ +*/ +#define RX_OFFSET(chan) ((idx) * 0x20) +#define RX_STATUS(chan) RX_OFFSET(chan) +#define RX_SET(chan) (RX_OFFSET(chan) + 0x8) +#define RX_CLEAR(chan) (RX_OFFSET(chan) + 0x10) + +#define TX_OFFSET(chan) (0x100 + (idx) * 0x20) +#define TX_STATUS(chan) TX_OFFSET(chan) +#define TX_SET(chan) (TX_OFFSET(chan) + 0x8) +#define TX_CLEAR(chan) (TX_OFFSET(chan) + 0x10) + +/* + * +---------------+-------+----------------+ + * | Payload | Offset| Driver View | + * +---------------+-------+----------------+ + * | SCP->AP Low | 0x000 | RX_PAYLOAD(L) | + * | SCP->AP High | 0x400 | RX_PAYLOAD(H) | + * +---------------+-------+----------------+ + * | AP->SCP Low | 0x200 | TX_PAYLOAD(H) | + * | AP->SCP High | 0x600 | TX_PAYLOAD(H) | + * +---------------+-------+----------------+ +*/ +#define PAYLOAD_MAX_SIZE 0x200 +#define PAYLOAD_OFFSET 0x400 +#define RX_PAYLOAD(chan) ((chan) * PAYLOAD_OFFSET) +#define TX_PAYLOAD(chan) ((chan) * PAYLOAD_OFFSET + PAYLOAD_MAX_SIZE) + +struct mhu_chan { + int index; + int rx_irq; + struct mbox_link link; + struct mhu_ctlr *ctlr; + struct mhu_data_buf *data; +}; + +struct mhu_ctlr { + struct device *dev; + void __iomem *mbox_base; + void __iomem *payload_base; + struct mbox_controller mbox_con; + struct mhu_chan channels[CHANNEL_MAX]; +}; + +static inline struct mhu_chan *to_mhu_chan(struct mbox_link *lnk) +{ + if (!lnk) + return NULL; + + return container_of(lnk, struct mhu_chan, link); +} + +static irqreturn_t mbox_handler(int irq, void *p) +{ + struct mbox_link *link = (struct mbox_link *)p; + struct mhu_chan *chan = to_mhu_chan(link); + struct mhu_ctlr *ctlr = chan->ctlr; + void __iomem *mbox_base = ctlr->mbox_base; + void __iomem *payload = ctlr->payload_base; + int idx = chan->index; + u32 status = readl(mbox_base + RX_STATUS(idx)); + + if (status && irq == chan->rx_irq) { + struct mhu_data_buf *data = chan->data; + if (!data) + return IRQ_NONE; /* spurious */ + if (data->rx_buf) + memcpy(data->rx_buf, payload + RX_PAYLOAD(idx), + data->rx_size); + chan->data = NULL; + writel(~0, mbox_base + RX_CLEAR(idx)); + mbox_link_received_data(link, data); + } + + return IRQ_HANDLED; +} + +static int mhu_send_data(struct mbox_link *link, void *msg) +{ + struct mhu_chan *chan = to_mhu_chan(link); + struct mhu_ctlr *ctlr = chan->ctlr; + void __iomem *mbox_base = ctlr->mbox_base; + void __iomem *payload = ctlr->payload_base; + struct mhu_data_buf *data = (struct mhu_data_buf *)msg; + int idx = chan->index; + + if (!data) + return -EINVAL; + + chan->data = data; + if (data->tx_buf) + memcpy(payload + TX_PAYLOAD(idx), data->tx_buf, data->tx_size); + writel(data->cmd, mbox_base + TX_SET(idx)); + + return 0; +} + +static int mhu_startup(struct mbox_link *link, void *ignored) +{ + struct mhu_chan *chan = to_mhu_chan(link); + int err, mbox_irq = chan->rx_irq; + + err = request_threaded_irq(mbox_irq, NULL, mbox_handler, IRQF_ONESHOT, + link->link_name, link); + if (err) + return err; + + chan->data = NULL; + return 0; +} + +static void mhu_shutdown(struct mbox_link *link) +{ + struct mhu_chan *chan = to_mhu_chan(link); + + chan->data = NULL; + free_irq(chan->rx_irq, link); +} + +static bool mhu_last_tx_done(struct mbox_link *link) +{ + struct mhu_chan *chan = to_mhu_chan(link); + struct mhu_ctlr *ctlr = chan->ctlr; + void __iomem *mbox_base = ctlr->mbox_base; + int idx = chan->index; + + return !readl(mbox_base + TX_STATUS(idx)); +} + +static struct mbox_link_ops mhu_ops = { + .send_data = mhu_send_data, + .startup = mhu_startup, + .shutdown = mhu_shutdown, + .last_tx_done = mhu_last_tx_done, +}; + +static int mhu_probe(struct platform_device *pdev) +{ + struct mhu_ctlr *ctlr; + struct mhu_chan *chan; + struct device *dev = &pdev->dev; + struct mbox_link **l; + struct resource *res; + int idx; + static const char * const channel_names[] = { + CHANNEL_LOW_PRIORITY, + CHANNEL_HIGH_PRIORITY + }; + + ctlr = devm_kzalloc(dev, sizeof(*ctlr), GFP_KERNEL); + if (!ctlr) { + dev_err(dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "failed to get mailbox memory resource\n"); + return -ENXIO; + } + + ctlr->mbox_base = devm_request_and_ioremap(dev, res); + if (!ctlr->mbox_base) { + dev_err(dev, "failed to request or ioremap mailbox control\n"); + return -EADDRNOTAVAIL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "failed to get payload memory resource\n"); + return -ENXIO; + } + + ctlr->payload_base = devm_request_and_ioremap(dev, res); + if (!ctlr->payload_base) { + dev_err(dev, "failed to request or ioremap mailbox payload\n"); + return -EADDRNOTAVAIL; + } + + ctlr->dev = dev; + platform_set_drvdata(pdev, ctlr); + + l = devm_kzalloc(dev, sizeof(*l) * (CHANNEL_MAX + 1), GFP_KERNEL); + if (!l) { + dev_err(dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + ctlr->mbox_con.links = l; + ctlr->mbox_con.txdone_poll = true; + ctlr->mbox_con.txpoll_period = 10; + ctlr->mbox_con.ops = &mhu_ops; + snprintf(ctlr->mbox_con.controller_name, 16, CONTROLLER_NAME); + ctlr->mbox_con.dev = dev; + + for (idx = 0; idx < CHANNEL_MAX; idx++) { + chan = &ctlr->channels[idx]; + chan->index = idx; + chan->ctlr = ctlr; + chan->rx_irq = platform_get_irq(pdev, idx); + if (chan->rx_irq < 0) { + dev_err(dev, "failed to get interrupt for %s\n", + channel_names[idx]); + return -ENXIO; + } + l[idx] = &chan->link; + snprintf(l[idx]->link_name, 16, channel_names[idx]); + } + l[idx] = NULL; + + if (mbox_controller_register(&ctlr->mbox_con)) { + dev_err(dev, "failed to register mailbox controller\n"); + return -ENOMEM; + } + _dev_info(dev, "registered mailbox controller %s\n", + ctlr->mbox_con.controller_name); + return 0; +} + +static int mhu_remove(struct platform_device *pdev) +{ + struct mhu_ctlr *ctlr = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + mbox_controller_unregister(&ctlr->mbox_con); + _dev_info(dev, "unregistered mailbox controller %s\n", + ctlr->mbox_con.controller_name); + devm_kfree(dev, ctlr->mbox_con.links); + + devm_iounmap(dev, ctlr->payload_base); + devm_iounmap(dev, ctlr->mbox_base); + + platform_set_drvdata(pdev, NULL); + devm_kfree(dev, ctlr); + return 0; +} + +static struct of_device_id mhu_of_match[] = { + { .compatible = "arm,mhu" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mhu_of_match); + +static struct platform_driver mhu_driver = { + .probe = mhu_probe, + .remove = mhu_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = mhu_of_match, + }, +}; + +static int __init mhu_init(void) +{ + return platform_driver_register(&mhu_driver); +} +core_initcall(mhu_init); + +static void __exit mhu_exit(void) +{ + platform_driver_unregister(&mhu_driver); +} +module_exit(mhu_exit); + +MODULE_AUTHOR("Sudeep Holla "); +MODULE_DESCRIPTION("ARM MHU mailbox driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mailbox/arm_mhu.h b/drivers/mailbox/arm_mhu.h new file mode 100644 index 000000000000..3b5343375c43 --- /dev/null +++ b/drivers/mailbox/arm_mhu.h @@ -0,0 +1,31 @@ +/* + * ARM Message Handling Unit (MHU) driver header + * + * Copyright (C) 2014 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#define CONTROLLER_NAME "mhu_ctlr" + +#define CHANNEL_MAX 2 +#define CHANNEL_LOW_PRIORITY "cpu_to_scp_low" +#define CHANNEL_HIGH_PRIORITY "cpu_to_scp_high" + +struct mhu_data_buf { + u32 cmd; + int tx_size; + void *tx_buf; + int rx_size; + void *rx_buf; + void *cl_data; +}; -- cgit v1.2.3 From 5fe4d7825157a2a771da01e3424a675291bf7300 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 15 Apr 2014 16:09:42 +0100 Subject: mailbox: add support for System Control and Power Interface(SCPI) protocol This patch add supports for System Control and Power Interface (SCPI) Message Protocol used between the Application Cores(AP) and the System Control Processor(SCP). The MHU peripheral provides a mechanism for inter-processor communication between SCP's M3 processor and AP. SCP offers control and management of the core/cluster power states, various power domain DVFS including the core/cluster, certain system clocks configuration, thermal sensors and many others. This protocol library provides interface for all the client drivers using SCPI to make use of the features offered by the SCP. Signed-off-by: Sudeep Holla Signed-off-by: Jon Medhurst --- drivers/mailbox/Kconfig | 13 ++ drivers/mailbox/Makefile | 1 + drivers/mailbox/scpi_protocol.c | 354 ++++++++++++++++++++++++++++++++++++++++ include/linux/scpi_protocol.h | 30 ++++ 4 files changed, 398 insertions(+) create mode 100644 drivers/mailbox/scpi_protocol.c create mode 100644 include/linux/scpi_protocol.h diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index d61fac9572d4..59171d428600 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -20,6 +20,19 @@ config ARM_MHU_MBOX certain system clocks configuration, thermal sensors and many others. +config ARM_SCPI_PROTOCOL + bool "ARM System Control and Power Interface (SCPI) Message Protocol" + select ARM_MHU_MBOX + help + System Control and Power Interface (SCPI) Message Protocol is + defined for the purpose of communication between the Application + Cores(AP) and the System Control Processor(SCP). The MHU peripheral + provides a mechanism for inter-processor communication between SCP + and AP. + + This protocol library provides interface for all the client drivers + making use of the features offered by the SCP. + config PL320_MBOX bool "ARM PL320 Mailbox" depends on ARM_AMBA diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 867b57313794..526c7c8505f4 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_MAILBOX) += mailbox.o obj-$(CONFIG_ARM_MHU_MBOX) += arm_mhu.o +obj-$(CONFIG_ARM_SCPI_PROTOCOL) += scpi_protocol.o obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o diff --git a/drivers/mailbox/scpi_protocol.c b/drivers/mailbox/scpi_protocol.c new file mode 100644 index 000000000000..7a1ff2dd2687 --- /dev/null +++ b/drivers/mailbox/scpi_protocol.c @@ -0,0 +1,354 @@ +/* + * System Control and Power Interface (SCPI) Message Protocol driver + * + * Copyright (C) 2014 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "arm_mhu.h" + +#define CMD_ID_SHIFT 0 +#define CMD_ID_MASK 0xff +#define CMD_SENDER_ID_SHIFT 8 +#define CMD_SENDER_ID_MASK 0xff +#define CMD_DATA_SIZE_SHIFT 20 +#define CMD_DATA_SIZE_MASK 0x1ff +#define PACK_SCPI_CMD(cmd, sender, txsz) \ + ((((cmd) & CMD_ID_MASK) << CMD_ID_SHIFT) | \ + (((sender) & CMD_SENDER_ID_MASK) << CMD_SENDER_ID_SHIFT) | \ + (((txsz) & CMD_DATA_SIZE_MASK) << CMD_DATA_SIZE_SHIFT)) + +#define MAX_DVFS_DOMAINS 3 +#define MAX_DVFS_OPPS 4 +#define DVFS_LATENCY(hdr) ((hdr) >> 16) +#define DVFS_OPP_COUNT(hdr) (((hdr) >> 8) & 0xff) + +enum scpi_error_codes { + SCPI_SUCCESS = 0, /* Success */ + SCPI_ERR_PARAM = 1, /* Invalid parameter(s) */ + SCPI_ERR_ALIGN = 2, /* Invalid alignment */ + SCPI_ERR_SIZE = 3, /* Invalid size */ + SCPI_ERR_HANDLER = 4, /* Invalid handler/callback */ + SCPI_ERR_ACCESS = 5, /* Invalid access/permission denied */ + SCPI_ERR_RANGE = 6, /* Value out of range */ + SCPI_ERR_TIMEOUT = 7, /* Timeout has occurred */ + SCPI_ERR_NOMEM = 8, /* Invalid memory area or pointer */ + SCPI_ERR_PWRSTATE = 9, /* Invalid power state */ + SCPI_ERR_SUPPORT = 10, /* Not supported or disabled */ + SCPI_ERR_DEVICE = 11, /* Device error */ + SCPI_ERR_MAX +}; + +enum scpi_client_id { + SCPI_CL_NONE, + SCPI_CL_CLOCKS, + SCPI_CL_DVFS, + SCPI_CL_POWER, + SCPI_MAX, +}; + +enum scpi_std_cmd { + SCPI_CMD_INVALID = 0x00, + SCPI_CMD_SCPI_READY = 0x01, + SCPI_CMD_SCPI_CAPABILITIES = 0x02, + SCPI_CMD_EVENT = 0x03, + SCPI_CMD_SET_CSS_PWR_STATE = 0x04, + SCPI_CMD_GET_CSS_PWR_STATE = 0x05, + SCPI_CMD_CFG_PWR_STATE_STAT = 0x06, + SCPI_CMD_GET_PWR_STATE_STAT = 0x07, + SCPI_CMD_SYS_PWR_STATE = 0x08, + SCPI_CMD_L2_READY = 0x09, + SCPI_CMD_SET_AP_TIMER = 0x0a, + SCPI_CMD_CANCEL_AP_TIME = 0x0b, + SCPI_CMD_DVFS_CAPABILITIES = 0x0c, + SCPI_CMD_GET_DVFS_INFO = 0x0d, + SCPI_CMD_SET_DVFS = 0x0e, + SCPI_CMD_GET_DVFS = 0x0f, + SCPI_CMD_GET_DVFS_STAT = 0x10, + SCPI_CMD_SET_RTC = 0x11, + SCPI_CMD_GET_RTC = 0x12, + SCPI_CMD_CLOCK_CAPABILITIES = 0x13, + SCPI_CMD_SET_CLOCK_INDEX = 0x14, + SCPI_CMD_SET_CLOCK_VALUE = 0x15, + SCPI_CMD_GET_CLOCK_VALUE = 0x16, + SCPI_CMD_PSU_CAPABILITIES = 0x17, + SCPI_CMD_SET_PSU = 0x18, + SCPI_CMD_GET_PSU = 0x19, + SCPI_CMD_SENSOR_CAPABILITIES = 0x1a, + SCPI_CMD_SENSOR_INFO = 0x1b, + SCPI_CMD_SENSOR_VALUE = 0x1c, + SCPI_CMD_SENSOR_CFG_PERIODIC = 0x1d, + SCPI_CMD_SENSOR_CFG_BOUNDS = 0x1e, + SCPI_CMD_SENSOR_ASYNC_VALUE = 0x1f, + SCPI_CMD_COUNT +}; + +struct scpi_data_buf { + int client_id; + struct mhu_data_buf *data; + struct completion complete; +}; + +static int high_priority_cmds[] = { + SCPI_CMD_GET_CSS_PWR_STATE, + SCPI_CMD_CFG_PWR_STATE_STAT, + SCPI_CMD_GET_PWR_STATE_STAT, + SCPI_CMD_SET_DVFS, + SCPI_CMD_GET_DVFS, + SCPI_CMD_SET_RTC, + SCPI_CMD_GET_RTC, + SCPI_CMD_SET_CLOCK_INDEX, + SCPI_CMD_SET_CLOCK_VALUE, + SCPI_CMD_GET_CLOCK_VALUE, + SCPI_CMD_SET_PSU, + SCPI_CMD_GET_PSU, + SCPI_CMD_SENSOR_VALUE, + SCPI_CMD_SENSOR_CFG_PERIODIC, + SCPI_CMD_SENSOR_CFG_BOUNDS, +}; + +static struct scpi_opp *scpi_opps[MAX_DVFS_DOMAINS]; + +static int scpi_linux_errmap[SCPI_ERR_MAX] = { + 0, -EINVAL, -ENOEXEC, -EMSGSIZE, + -EINVAL, -EACCES, -ERANGE, -ETIMEDOUT, + -ENOMEM, -EINVAL, -EOPNOTSUPP, -EIO, +}; + +static inline int scpi_to_linux_errno(int errno) +{ + if (errno >= SCPI_SUCCESS && errno < SCPI_ERR_MAX) + return scpi_linux_errmap[errno]; + return -EIO; +} + +static bool high_priority_chan_supported(int cmd) +{ + int idx; + for (idx = 0; idx < ARRAY_SIZE(high_priority_cmds); idx++) + if (cmd == high_priority_cmds[idx]) + return true; + return false; +} + +static void scpi_rx_callback(struct mbox_client *cl, void *msg) +{ + struct mhu_data_buf *data = (struct mhu_data_buf *)msg; + struct scpi_data_buf *scpi_buf = data->cl_data; + complete(&scpi_buf->complete); +} + +static int send_scpi_cmd(struct scpi_data_buf *scpi_buf, bool high_priority) +{ + struct mbox_chan *chan; + struct mbox_client cl; + struct mhu_data_buf *data = scpi_buf->data; + u32 status; + + cl.rx_callback = scpi_rx_callback; + cl.tx_done = NULL; + cl.tx_block = true; + cl.tx_tout = 50; /* 50 msec */ + cl.link_data = NULL; + cl.knows_txdone = false; + cl.chan_name = high_priority ? + CONTROLLER_NAME":"CHANNEL_HIGH_PRIORITY : + CONTROLLER_NAME":"CHANNEL_LOW_PRIORITY; + + chan = mbox_request_channel(&cl); + if (IS_ERR(chan)) + return PTR_ERR(chan); + + init_completion(&scpi_buf->complete); + if (mbox_send_message(chan, (void *)data)) + return -EIO; + + if (!wait_for_completion_timeout(&scpi_buf->complete, + msecs_to_jiffies(50))) + status = SCPI_ERR_TIMEOUT; + else + status = *(u32 *)(data->rx_buf); /* read first word */ + + mbox_free_channel(chan); + + return scpi_to_linux_errno(status); +} + +#define SCPI_SETUP_DBUF(scpi_buf, mhu_buf, _client_id,\ + _cmd, _tx_buf, _rx_buf) \ +do { \ + struct mhu_data_buf *pdata = &mhu_buf; \ + pdata->cmd = _cmd; \ + pdata->tx_buf = &_tx_buf; \ + pdata->tx_size = sizeof(_tx_buf); \ + pdata->rx_buf = &_rx_buf; \ + pdata->rx_size = sizeof(_rx_buf); \ + scpi_buf.client_id = _client_id; \ + scpi_buf.data = pdata; \ +} while (0) + +static int scpi_execute_cmd(struct scpi_data_buf *scpi_buf) +{ + struct mhu_data_buf *data; + bool high_priority; + + if (!scpi_buf || !scpi_buf->data) + return -EINVAL; + + data = scpi_buf->data; + high_priority = high_priority_chan_supported(data->cmd); + data->cmd = PACK_SCPI_CMD(data->cmd, scpi_buf->client_id, + data->tx_size); + data->cl_data = scpi_buf; + + return send_scpi_cmd(scpi_buf, high_priority); +} + +unsigned long scpi_clk_get_val(u16 clk_id) +{ + struct scpi_data_buf sdata; + struct mhu_data_buf mdata; + struct { + u32 status; + u32 clk_rate; + } buf; + + SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_CLOCKS, + SCPI_CMD_GET_CLOCK_VALUE, clk_id, buf); + if (scpi_execute_cmd(&sdata)) + return 0; + + return buf.clk_rate; +} +EXPORT_SYMBOL_GPL(scpi_clk_get_val); + +int scpi_clk_set_val(u16 clk_id, unsigned long rate) +{ + struct scpi_data_buf sdata; + struct mhu_data_buf mdata; + int stat; + struct { + u32 clk_rate; + u16 clk_id; + } buf; + + buf.clk_rate = (u32)rate; + buf.clk_id = clk_id; + + SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_CLOCKS, + SCPI_CMD_SET_CLOCK_VALUE, buf, stat); + return scpi_execute_cmd(&sdata); +} +EXPORT_SYMBOL_GPL(scpi_clk_set_val); + +struct scpi_opp *scpi_dvfs_get_opps(u8 domain) +{ + struct scpi_data_buf sdata; + struct mhu_data_buf mdata; + struct { + u32 status; + u32 header; + u32 freqs[MAX_DVFS_OPPS]; + } buf; + struct scpi_opp *opp; + size_t freqs_sz; + int count, ret; + + if (domain >= MAX_DVFS_DOMAINS) + return ERR_PTR(-EINVAL); + + if (scpi_opps[domain]) /* data already populated */ + return scpi_opps[domain]; + + SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DVFS, + SCPI_CMD_GET_DVFS_INFO, domain, buf); + ret = scpi_execute_cmd(&sdata); + if (ret) + return ERR_PTR(ret); + + opp = kmalloc(sizeof(*opp), GFP_KERNEL); + if (!opp) + return ERR_PTR(-ENOMEM); + + count = DVFS_OPP_COUNT(buf.header); + freqs_sz = count * sizeof(*(opp->freqs)); + + opp->count = count; + opp->latency = DVFS_LATENCY(buf.header); + opp->freqs = kmalloc(freqs_sz, GFP_KERNEL); + if (!opp->freqs) { + kfree(opp); + return ERR_PTR(-ENOMEM); + } + + memcpy(opp->freqs, &buf.freqs[0], freqs_sz); + scpi_opps[domain] = opp; + + return opp; +} +EXPORT_SYMBOL_GPL(scpi_dvfs_get_opps); + +int scpi_dvfs_get_idx(u8 domain) +{ + struct scpi_data_buf sdata; + struct mhu_data_buf mdata; + struct { + u32 status; + u8 dvfs_idx; + } buf; + int ret; + + if (domain >= MAX_DVFS_DOMAINS) + return -EINVAL; + + SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DVFS, + SCPI_CMD_GET_DVFS, domain, buf); + ret = scpi_execute_cmd(&sdata); + + if (!ret) + ret = buf.dvfs_idx; + return ret; +} +EXPORT_SYMBOL_GPL(scpi_dvfs_get_idx); + +int scpi_dvfs_set_idx(u8 domain, u8 idx) +{ + struct scpi_data_buf sdata; + struct mhu_data_buf mdata; + struct { + u8 dvfs_domain; + u8 dvfs_idx; + } buf; + int stat; + + buf.dvfs_idx = idx; + buf.dvfs_domain = domain; + + if (domain >= MAX_DVFS_DOMAINS) + return -EINVAL; + + SCPI_SETUP_DBUF(sdata, mdata, SCPI_CL_DVFS, + SCPI_CMD_SET_DVFS, buf, stat); + return scpi_execute_cmd(&sdata); +} +EXPORT_SYMBOL_GPL(scpi_dvfs_set_idx); diff --git a/include/linux/scpi_protocol.h b/include/linux/scpi_protocol.h new file mode 100644 index 000000000000..66e5eb3710ab --- /dev/null +++ b/include/linux/scpi_protocol.h @@ -0,0 +1,30 @@ +/* + * SCPI Message Protocol driver header + * + * Copyright (C) 2014 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#include + +struct scpi_opp { + u32 *freqs; + u32 latency; /* in usecs */ + int count; +}; + +unsigned long scpi_clk_get_val(u16 clk_id); +int scpi_clk_set_val(u16 clk_id, unsigned long rate); +int scpi_dvfs_get_idx(u8 domain); +int scpi_dvfs_set_idx(u8 domain, u8 idx); +struct scpi_opp *scpi_dvfs_get_opps(u8 domain); -- cgit v1.2.3 From 95a0577a28e64ec32adfefa1b6745d560052a40b Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 27 Jun 2014 16:54:07 +0100 Subject: mailbox: get mhu driver working with new (v7) mailbox framework Signed-off-by: Jon Medhurst --- drivers/mailbox/arm_mhu.c | 65 ++++++++++++++++------------------------- drivers/mailbox/arm_mhu.h | 2 ++ drivers/mailbox/scpi_protocol.c | 6 ++-- 3 files changed, 30 insertions(+), 43 deletions(-) diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c index 841b0cb1b710..6256caae9133 100644 --- a/drivers/mailbox/arm_mhu.c +++ b/drivers/mailbox/arm_mhu.c @@ -46,7 +46,9 @@ #include "arm_mhu.h" -#define DRIVER_NAME CONTROLLER_NAME"_drv" +struct device* the_scpi_device; + +#define DRIVER_NAME "arm_mhu" /* * +--------------------+-------+---------------+ @@ -98,7 +100,6 @@ struct mhu_chan { int index; int rx_irq; - struct mbox_link link; struct mhu_ctlr *ctlr; struct mhu_data_buf *data; }; @@ -111,18 +112,10 @@ struct mhu_ctlr { struct mhu_chan channels[CHANNEL_MAX]; }; -static inline struct mhu_chan *to_mhu_chan(struct mbox_link *lnk) -{ - if (!lnk) - return NULL; - - return container_of(lnk, struct mhu_chan, link); -} - static irqreturn_t mbox_handler(int irq, void *p) { - struct mbox_link *link = (struct mbox_link *)p; - struct mhu_chan *chan = to_mhu_chan(link); + struct mbox_chan *link = (struct mbox_chan *)p; + struct mhu_chan *chan = link->con_priv; struct mhu_ctlr *ctlr = chan->ctlr; void __iomem *mbox_base = ctlr->mbox_base; void __iomem *payload = ctlr->payload_base; @@ -138,15 +131,15 @@ static irqreturn_t mbox_handler(int irq, void *p) data->rx_size); chan->data = NULL; writel(~0, mbox_base + RX_CLEAR(idx)); - mbox_link_received_data(link, data); + mbox_chan_received_data(link, data); } return IRQ_HANDLED; } -static int mhu_send_data(struct mbox_link *link, void *msg) +static int mhu_send_data(struct mbox_chan *link, void *msg) { - struct mhu_chan *chan = to_mhu_chan(link); + struct mhu_chan *chan = link->con_priv; struct mhu_ctlr *ctlr = chan->ctlr; void __iomem *mbox_base = ctlr->mbox_base; void __iomem *payload = ctlr->payload_base; @@ -164,31 +157,27 @@ static int mhu_send_data(struct mbox_link *link, void *msg) return 0; } -static int mhu_startup(struct mbox_link *link, void *ignored) +static int mhu_startup(struct mbox_chan *link) { - struct mhu_chan *chan = to_mhu_chan(link); + struct mhu_chan *chan = link->con_priv; int err, mbox_irq = chan->rx_irq; err = request_threaded_irq(mbox_irq, NULL, mbox_handler, IRQF_ONESHOT, - link->link_name, link); - if (err) - return err; - - chan->data = NULL; - return 0; + DRIVER_NAME, link); + return err; } -static void mhu_shutdown(struct mbox_link *link) +static void mhu_shutdown(struct mbox_chan *link) { - struct mhu_chan *chan = to_mhu_chan(link); + struct mhu_chan *chan = link->con_priv; chan->data = NULL; free_irq(chan->rx_irq, link); } -static bool mhu_last_tx_done(struct mbox_link *link) +static bool mhu_last_tx_done(struct mbox_chan *link) { - struct mhu_chan *chan = to_mhu_chan(link); + struct mhu_chan *chan = link->con_priv; struct mhu_ctlr *ctlr = chan->ctlr; void __iomem *mbox_base = ctlr->mbox_base; int idx = chan->index; @@ -196,7 +185,7 @@ static bool mhu_last_tx_done(struct mbox_link *link) return !readl(mbox_base + TX_STATUS(idx)); } -static struct mbox_link_ops mhu_ops = { +static struct mbox_chan_ops mhu_ops = { .send_data = mhu_send_data, .startup = mhu_startup, .shutdown = mhu_shutdown, @@ -208,7 +197,7 @@ static int mhu_probe(struct platform_device *pdev) struct mhu_ctlr *ctlr; struct mhu_chan *chan; struct device *dev = &pdev->dev; - struct mbox_link **l; + struct mbox_chan *l; struct resource *res; int idx; static const char * const channel_names[] = { @@ -249,17 +238,17 @@ static int mhu_probe(struct platform_device *pdev) ctlr->dev = dev; platform_set_drvdata(pdev, ctlr); - l = devm_kzalloc(dev, sizeof(*l) * (CHANNEL_MAX + 1), GFP_KERNEL); + l = devm_kzalloc(dev, sizeof(*l) * CHANNEL_MAX, GFP_KERNEL); if (!l) { dev_err(dev, "failed to allocate memory\n"); return -ENOMEM; } - ctlr->mbox_con.links = l; + ctlr->mbox_con.chans = l; + ctlr->mbox_con.num_chans = CHANNEL_MAX; ctlr->mbox_con.txdone_poll = true; ctlr->mbox_con.txpoll_period = 10; ctlr->mbox_con.ops = &mhu_ops; - snprintf(ctlr->mbox_con.controller_name, 16, CONTROLLER_NAME); ctlr->mbox_con.dev = dev; for (idx = 0; idx < CHANNEL_MAX; idx++) { @@ -272,17 +261,15 @@ static int mhu_probe(struct platform_device *pdev) channel_names[idx]); return -ENXIO; } - l[idx] = &chan->link; - snprintf(l[idx]->link_name, 16, channel_names[idx]); + l[idx].con_priv = chan; } - l[idx] = NULL; if (mbox_controller_register(&ctlr->mbox_con)) { dev_err(dev, "failed to register mailbox controller\n"); return -ENOMEM; } - _dev_info(dev, "registered mailbox controller %s\n", - ctlr->mbox_con.controller_name); + + the_scpi_device = dev; return 0; } @@ -292,9 +279,7 @@ static int mhu_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; mbox_controller_unregister(&ctlr->mbox_con); - _dev_info(dev, "unregistered mailbox controller %s\n", - ctlr->mbox_con.controller_name); - devm_kfree(dev, ctlr->mbox_con.links); + devm_kfree(dev, ctlr->mbox_con.chans); devm_iounmap(dev, ctlr->payload_base); devm_iounmap(dev, ctlr->mbox_base); diff --git a/drivers/mailbox/arm_mhu.h b/drivers/mailbox/arm_mhu.h index 3b5343375c43..f78c1fa369c8 100644 --- a/drivers/mailbox/arm_mhu.h +++ b/drivers/mailbox/arm_mhu.h @@ -29,3 +29,5 @@ struct mhu_data_buf { void *rx_buf; void *cl_data; }; + +extern struct device* the_scpi_device; diff --git a/drivers/mailbox/scpi_protocol.c b/drivers/mailbox/scpi_protocol.c index 7a1ff2dd2687..c8a824a60a43 100644 --- a/drivers/mailbox/scpi_protocol.c +++ b/drivers/mailbox/scpi_protocol.c @@ -165,15 +165,15 @@ static int send_scpi_cmd(struct scpi_data_buf *scpi_buf, bool high_priority) struct mhu_data_buf *data = scpi_buf->data; u32 status; + cl.dev = the_scpi_device; cl.rx_callback = scpi_rx_callback; cl.tx_done = NULL; cl.tx_block = true; cl.tx_tout = 50; /* 50 msec */ - cl.link_data = NULL; cl.knows_txdone = false; cl.chan_name = high_priority ? - CONTROLLER_NAME":"CHANNEL_HIGH_PRIORITY : - CONTROLLER_NAME":"CHANNEL_LOW_PRIORITY; + CHANNEL_HIGH_PRIORITY : + CHANNEL_LOW_PRIORITY; chan = mbox_request_channel(&cl); if (IS_ERR(chan)) -- cgit v1.2.3 From 88afd03a534e75558c3e388d0f816f7c6e1ce0e8 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 24 Apr 2014 16:58:11 +0100 Subject: clk: add support for clocks provided by system control processor On some ARM based systems, a separate Cortex-M based System Control Processor(SCP) provides the overall power, clock, reset and system control. System Control and Power Interface(SCPI) Message Protocol is defined for the communication between the Application Cores(AP) and the SCP. This patch adds support for the clocks provided by SCP using SCPI protocol. Signed-off-by: Sudeep Holla Signed-off-by: Jon Medhurst --- Documentation/devicetree/bindings/clock/scpi.txt | 34 +++ drivers/clk/Kconfig | 10 + drivers/clk/Makefile | 1 + drivers/clk/clk-scpi.c | 309 +++++++++++++++++++++++ 4 files changed, 354 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/scpi.txt create mode 100644 drivers/clk/clk-scpi.c diff --git a/Documentation/devicetree/bindings/clock/scpi.txt b/Documentation/devicetree/bindings/clock/scpi.txt new file mode 100644 index 000000000000..b2b7035018f4 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/scpi.txt @@ -0,0 +1,34 @@ +Device Tree Clock bindings for the clocks based on +System Control and Power Interface (SCPI) Message Protocol + +This binding uses the common clock binding[1]. + +Required properties: +- compatible : shall be one of the following: + "arm,scpi-clks" - for the container node with all the clocks + based on the SCPI protocol + "arm,scpi-clk-indexed" - all the clocks that are variable and index + based. These clocks don't provide the full range between the + limits but only discrete points within the range. The firmware + provides the mapping for each such operating frequency and the + index associated with it. + "arm,scpi-clk-range" - all the clocks that are variable and provide + full range within the specified range + +Required properties for all clocks(all from common clock binding): +- #clock-cells : ; shall be set to 0 or 1 depending on whether it has single + or multiple clock outputs. +- clock-output-names : shall be the corresponding names of the outputs. +- clock-indices: The identifyng number for the clocks in the node as expected + by the firmware. It can be non linear and hence provide the mapping + of identifiers into the clock-output-names array. +- frequency-range: The allowed range of clock frequency supported specified + in the form of minimum and maximum limits(two u32 fields) + This is required only if compatible is "arm,scpi-clk-range" + +Clock consumers should specify the desired clocks they use with a +"clocks" phandle cell. Consumers should also provide an additional ID +in their clock property. This ID refers to the specific clock in the clock +provider list. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 3f44f292d066..2b6aab6460e0 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -32,6 +32,16 @@ config COMMON_CLK_WM831X source "drivers/clk/versatile/Kconfig" +config COMMON_CLK_SCPI + bool "Clock driver controlled via SCPI interface" + depends on ARM_SCPI_PROTOCOL + ---help--- + This driver provides support for clocks that are controlled + by firmware that implements the SCPI interface. + + This driver uses SCPI Message Protocol to interact with the + firware providing all the clock controls. + config COMMON_CLK_MAX_GEN bool diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d5fba5bc6e1b..589b7732e5b6 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_COMMON_CLK_PALMAS) += clk-palmas.o obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o +obj-$(CONFIG_COMMON_CLK_SCPI) += clk-scpi.o obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c new file mode 100644 index 000000000000..2d707663542f --- /dev/null +++ b/drivers/clk/clk-scpi.c @@ -0,0 +1,309 @@ +/* + * System Control and Power Interface (SCPI) Protocol based clock driver + * + * Copyright (C) 2014 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct scpi_clk { + u32 id; + const char *name; + struct clk_hw hw; + struct scpi_opp *opps; + unsigned long rate_min; + unsigned long rate_max; +}; + +#define to_scpi_clk(clk) container_of(clk, struct scpi_clk, hw) + +static unsigned long scpi_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct scpi_clk *clk = to_scpi_clk(hw); + return scpi_clk_get_val(clk->id); +} + +static long scpi_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct scpi_clk *clk = to_scpi_clk(hw); + if (clk->rate_min && rate < clk->rate_min) + rate = clk->rate_min; + if (clk->rate_max && rate > clk->rate_max) + rate = clk->rate_max; + + return rate; +} + +static int scpi_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct scpi_clk *clk = to_scpi_clk(hw); + return scpi_clk_set_val(clk->id, rate); +} + +static struct clk_ops scpi_clk_ops = { + .recalc_rate = scpi_clk_recalc_rate, + .round_rate = scpi_clk_round_rate, + .set_rate = scpi_clk_set_rate, +}; + +/* find closest match to given frequency in OPP table */ +static int __scpi_dvfs_round_rate(struct scpi_clk *clk, unsigned long rate) +{ + int idx, max_opp = clk->opps->count; + u32 *freqs = clk->opps->freqs; + u32 fmin = 0, fmax = ~0, ftmp; + + for (idx = 0; idx < max_opp; idx++, freqs++) { + ftmp = *freqs; + if (ftmp >= (u32)rate) { + if (ftmp <= fmax) + fmax = ftmp; + } else { + if (ftmp >= fmin) + fmin = ftmp; + } + } + if (fmax != ~0) + return fmax; + else + return fmin; +} + +static unsigned long scpi_dvfs_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct scpi_clk *clk = to_scpi_clk(hw); + int idx = scpi_dvfs_get_idx(clk->id); + u32 *freqs = clk->opps->freqs; + + if (idx < 0) + return 0; + else + return *(freqs + idx); +} + +static long scpi_dvfs_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct scpi_clk *clk = to_scpi_clk(hw); + return __scpi_dvfs_round_rate(clk, rate); +} + +static int __scpi_find_dvfs_index(struct scpi_clk *clk, unsigned long rate) +{ + int idx, max_opp = clk->opps->count; + u32 *freqs = clk->opps->freqs; + + for (idx = 0; idx < max_opp; idx++, freqs++) + if (*freqs == (u32)rate) + break; + return (idx == max_opp) ? -EINVAL : idx; +} + +static int scpi_dvfs_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct scpi_clk *clk = to_scpi_clk(hw); + int ret = __scpi_find_dvfs_index(clk, rate); + + if (ret < 0) + return ret; + else + return scpi_dvfs_set_idx(clk->id, (u8)ret); +} + +static struct clk_ops scpi_dvfs_ops = { + .recalc_rate = scpi_dvfs_recalc_rate, + .round_rate = scpi_dvfs_round_rate, + .set_rate = scpi_dvfs_set_rate, +}; + +static struct clk * +scpi_dvfs_ops_init(struct device *dev, struct device_node *np, + struct scpi_clk *sclk) +{ + struct clk_init_data init; + struct scpi_opp *opp; + + init.name = sclk->name; + init.flags = CLK_IS_ROOT; + init.num_parents = 0; + init.ops = &scpi_dvfs_ops; + sclk->hw.init = &init; + + opp = scpi_dvfs_get_opps(sclk->id); + if (IS_ERR(opp)) + return (struct clk *)opp; + + sclk->opps = opp; + + return devm_clk_register(dev, &sclk->hw); +} + +static struct clk * +scpi_clk_ops_init(struct device *dev, struct device_node *np, + struct scpi_clk *sclk) +{ + struct clk_init_data init; + u32 range[2]; + int ret; + + init.name = sclk->name; + init.flags = CLK_IS_ROOT; + init.num_parents = 0; + init.ops = &scpi_clk_ops; + sclk->hw.init = &init; + + ret = of_property_read_u32_array(np, "frequency-range", range, + ARRAY_SIZE(range)); + if (ret) + return ERR_PTR(ret); + sclk->rate_min = range[0]; + sclk->rate_max = range[1]; + + return devm_clk_register(dev, &sclk->hw); +} + +static int scpi_clk_setup(struct device *dev, struct device_node *np, + const void *data) +{ + struct clk *(*setup_ops)(struct device *, struct device_node *, + struct scpi_clk *) = data; + struct clk_onecell_data *clk_data; + struct clk **clks; + size_t count; + int idx; + + count = of_property_count_strings(np, "clock-output-names"); + if (count < 0) { + dev_err(dev, "%s: invalid clock output count\n", np->name); + return -EINVAL; + } + + clk_data = devm_kmalloc(dev, sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) { + dev_err(dev, "failed to allocate clock provider data\n"); + return -ENOMEM; + } + + clks = devm_kmalloc(dev, count * sizeof(*clks), GFP_KERNEL); + if (!clks) { + dev_err(dev, "failed to allocate clock providers\n"); + return -ENOMEM; + } + + for (idx = 0; idx < count; idx++) { + struct scpi_clk *sclk; + u32 val; + + sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL); + if (!sclk) { + dev_err(dev, "failed to allocate scpi clocks\n"); + return -ENOMEM; + } + + if (of_property_read_string_index(np, "clock-output-names", + idx, &sclk->name)) { + dev_err(dev, "invalid clock name @ %s\n", np->name); + return -EINVAL; + } + + if (of_property_read_u32_index(np, "clock-indices", + idx, &val)) { + dev_err(dev, "invalid clock index @ %s\n", np->name); + return -EINVAL; + } + + sclk->id = val; + + clks[idx] = setup_ops(dev, np, sclk); + if (IS_ERR(clks[idx])) { + dev_err(dev, "failed to register clock '%s'\n", + sclk->name); + return PTR_ERR(clks[idx]); + } + + dev_dbg(dev, "Registered clock '%s'\n", sclk->name); + } + + clk_data->clks = clks; + clk_data->clk_num = count; + of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); + + return 0; +} + +static const struct of_device_id clk_match[] = { + { .compatible = "arm,scpi-clk-indexed", .data = scpi_dvfs_ops_init, }, + { .compatible = "arm,scpi-clk-range", .data = &scpi_clk_ops_init, }, + {} +}; + +static int scpi_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node, *child; + const struct of_device_id *match; + int ret; + + for_each_child_of_node(np, child) { + match = of_match_node(clk_match, child); + if (!match) + continue; + ret = scpi_clk_setup(dev, child, match->data); + if (ret) + return ret; + } + return 0; +} + +static struct of_device_id scpi_clk_ids[] = { + { .compatible = "arm,scpi-clks", }, + {} +}; + +static struct platform_driver scpi_clk_driver = { + .driver = { + .name = "scpi_clocks", + .of_match_table = scpi_clk_ids, + }, + .probe = scpi_clk_probe, +}; + +static int __init scpi_clk_init(void) +{ + return platform_driver_register(&scpi_clk_driver); +} +postcore_initcall(scpi_clk_init); + +static void __exit scpi_clk_exit(void) +{ + platform_driver_unregister(&scpi_clk_driver); +} +module_exit(scpi_clk_exit); + +MODULE_AUTHOR("Sudeep Holla "); +MODULE_DESCRIPTION("ARM SCPI clock driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 9f73cf08e1439885f3858dc6f052cab8cb9d53d6 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 8 May 2014 17:47:48 +0100 Subject: cpufreq: arm_big_little: add SPCI interface driver On some ARM based systems, a separate Cortex-M based System Control Processor(SCP) provides the overall power, clock, reset and system control including CPU DVFS. SCPI Message Protocol is used to communicate with the SCPI. This patch adds a interface driver for adding OPPs and registering the arm_big_little cpufreq driver for such systems. Signed-off-by: Sudeep Holla Signed-off-by: Jon Medhurst --- drivers/cpufreq/Kconfig.arm | 9 ++++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/scpi-cpufreq.c | 99 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 drivers/cpufreq/scpi-cpufreq.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 0f9a2c3c0e0d..056fb4edeb15 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -24,6 +24,15 @@ config ARM_VEXPRESS_SPC_CPUFREQ This add the CPUfreq driver support for Versatile Express big.LITTLE platforms using SPC for power management. +config ARM_SPCI_CPUFREQ + tristate "SPCI based CPUfreq driver" + depends on ARM_BIG_LITTLE_CPUFREQ && ARM_SCPI_PROTOCOL + help + This add the CPUfreq driver support for ARM big.LITTLE platforms + using SCPI interface for CPU power management. + + This driver works only if firmware the supporting CPU DVFS adhere + to SCPI protocol. config ARM_EXYNOS_CPUFREQ bool diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index b3ca7b0b2c33..86d1c29199ae 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o obj-$(CONFIG_ARM_TEGRA_CPUFREQ) += tegra-cpufreq.o obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o +obj-$(CONFIG_ARM_SPCI_CPUFREQ) += scpi-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c new file mode 100644 index 000000000000..60725199b9aa --- /dev/null +++ b/drivers/cpufreq/scpi-cpufreq.c @@ -0,0 +1,99 @@ +/* + * SCPI CPUFreq Interface driver + * + * It provides necessary ops to arm_big_little cpufreq driver. + * + * Copyright (C) 2014 ARM Ltd. + * Sudeep Holla + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +#include "arm_big_little.h" + +static int scpi_init_opp_table(struct device *cpu_dev) +{ + u8 domain = topology_physical_package_id(cpu_dev->id); + struct scpi_opp *opp; + int idx, ret = 0, max_opp; + u32 *freqs; + + opp = scpi_dvfs_get_opps(domain); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + freqs = opp->freqs; + max_opp = opp->count; + for (idx = 0; idx < max_opp; idx++, freqs++) { + ret = dev_pm_opp_add(cpu_dev, *freqs, 900000000 /* TODO */); + if (ret) { + dev_warn(cpu_dev, "failed to add opp %u\n", *freqs); + return ret; + } + } + return ret; +} + +static int scpi_get_transition_latency(struct device *cpu_dev) +{ + u8 domain = topology_physical_package_id(cpu_dev->id); + struct scpi_opp *opp; + + opp = scpi_dvfs_get_opps(domain); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + return opp->latency * 1000; /* SCPI returns in uS */ +} + +static struct cpufreq_arm_bL_ops scpi_cpufreq_ops = { + .name = "scpi", + .get_transition_latency = scpi_get_transition_latency, + .init_opp_table = scpi_init_opp_table, +}; + +static int scpi_cpufreq_probe(struct platform_device *pdev) +{ + return bL_cpufreq_register(&scpi_cpufreq_ops); +} + +static int scpi_cpufreq_remove(struct platform_device *pdev) +{ + bL_cpufreq_unregister(&scpi_cpufreq_ops); + return 0; +} + +static struct of_device_id scpi_cpufreq_of_match[] = { + { .compatible = "arm,scpi-cpufreq" }, + {}, +}; +MODULE_DEVICE_TABLE(of, scpi_cpufreq_of_match); + +static struct platform_driver scpi_cpufreq_platdrv = { + .driver = { + .name = "scpi-cpufreq", + .owner = THIS_MODULE, + .of_match_table = scpi_cpufreq_of_match, + }, + .probe = scpi_cpufreq_probe, + .remove = scpi_cpufreq_remove, +}; +module_platform_driver(scpi_cpufreq_platdrv); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 2a94b1be206a67f0e28a018ddb0d3ad959397a18 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Wed, 28 May 2014 15:15:49 +0100 Subject: [HACK] cpufreq: arm_big_little: Fall back to getting clock from cpu device The driver in LSK assumes a hard-coded name for cluster clock, as used by vexpress TC2. Modify this to allow also clocks to be obtained from the cpu device; as Juno requires and as seems more like the correct way. Signed-off-by: Jon Medhurst --- drivers/cpufreq/arm_big_little.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index e1a6ba66a7f5..61171d5d8726 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -343,7 +343,9 @@ static int _get_cluster_clk_and_freq_table(struct device *cpu_dev) } name[12] = cluster + '0'; - clk[cluster] = clk_get(cpu_dev, name); + clk[cluster] = clk_get_sys(name, NULL); + if (IS_ERR(clk[cluster])) + clk[cluster] = clk_get(cpu_dev, NULL); if (!IS_ERR(clk[cluster])) { dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n", __func__, clk[cluster], freq_table[cluster], -- cgit v1.2.3 From f89928d01e2f9e569c6a19e3f3c52f3403fb5ac6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 9 May 2014 17:24:17 +0100 Subject: arm64: Add big.LITTLE switcher stub The big.LITTLE cpufreq driver is useful on arm64 big.LITTLE systems even without IKS support since it implements support for clusters with shared clocks (a common big.LITTLE configuration). In order to allow it to be built provide the non-IKS stubs for arm64, enabling cpufreq with all the cores available. It may make sense to make an asm-generic version of these stubs instead but given that there's only likely to be these two architectures using the code and asm-generic stubs also need per architecture updates it's probably more trouble than it's worth. Signed-off-by: Mark Brown --- arch/arm64/include/asm/bL_switcher.h | 54 ++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 arch/arm64/include/asm/bL_switcher.h diff --git a/arch/arm64/include/asm/bL_switcher.h b/arch/arm64/include/asm/bL_switcher.h new file mode 100644 index 000000000000..2bee500b7f54 --- /dev/null +++ b/arch/arm64/include/asm/bL_switcher.h @@ -0,0 +1,54 @@ +/* + * Based on the stubs for the ARM implementation which is: + * + * Created by: Nicolas Pitre, April 2012 + * Copyright: (C) 2012-2013 Linaro Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef ASM_BL_SWITCHER_H +#define ASM_BL_SWITCHER_H + +#include +#include + +typedef void (*bL_switch_completion_handler)(void *cookie); + +static inline int bL_switch_request(unsigned int cpu, + unsigned int new_cluster_id) +{ + return -ENOTSUPP; +} + +/* + * Register here to be notified about runtime enabling/disabling of + * the switcher. + * + * The notifier chain is called with the switcher activation lock held: + * the switcher will not be enabled or disabled during callbacks. + * Callbacks must not call bL_switcher_{get,put}_enabled(). + */ +#define BL_NOTIFY_PRE_ENABLE 0 +#define BL_NOTIFY_POST_ENABLE 1 +#define BL_NOTIFY_PRE_DISABLE 2 +#define BL_NOTIFY_POST_DISABLE 3 + +static inline int bL_switcher_register_notifier(struct notifier_block *nb) +{ + return 0; +} + +static inline int bL_switcher_unregister_notifier(struct notifier_block *nb) +{ + return 0; +} + +static inline bool bL_switcher_get_enabled(void) { return false; } +static inline void bL_switcher_put_enabled(void) { } +static inline int bL_switcher_trace_trigger(void) { return 0; } +static inline int bL_switcher_get_logical_index(u32 mpidr) { return -EUNATCH; } + +#endif -- cgit v1.2.3 From c66d0fffea4d9d1f161c5d45f8d2d0330d4d255f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 9 May 2014 17:40:31 +0100 Subject: cpufreq: Enable big.LITTLE cpufreq driver on arm64 There are arm64 big.LITTLE systems so enable the big.LITTLE cpufreq driver. While IKS is not available for these systems the driver is still useful since it manages clusters with shared frequencies which is the common case for these systems. Long term combining the cpufreq-cpu0 and big.LITTLE drivers may be a more sensible option but that is substantially more complex especially in the case of IKS. Signed-off-by: Mark Brown Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki (cherry picked from commit 4920ab84979d8cd2eb7e3c4fefcc924efabf1cb2) Signed-off-by: Mark Brown Signed-off-by: Jon Medhurst --- drivers/cpufreq/Kconfig.arm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 056fb4edeb15..4da070e91b32 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -5,7 +5,8 @@ # big LITTLE core layer and glue drivers config ARM_BIG_LITTLE_CPUFREQ tristate "Generic ARM big LITTLE CPUfreq driver" - depends on ARM && BIG_LITTLE && ARM_CPU_TOPOLOGY && HAVE_CLK + depends on ARM_CPU_TOPOLOGY || (ARM64 && SMP) + depends on HAVE_CLK select PM_OPP help This enables the Generic CPUfreq driver for ARM big.LITTLE platforms. -- cgit v1.2.3 From 39ad25f80e406bce0214f461cca46d1963be0d15 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Mon, 23 Jun 2014 11:39:09 +0100 Subject: arm64: topology: Use new name for SCHED_POWER_SHIFT Commit ca8ce3d0b1 (sched: Final power vs. capacity cleanups) renamed SCHED_POWER_SHIFT to SCHED_CAPACITY_SHIFT so we need to use that name. Signed-off-by: Jon Medhurst --- arch/arm64/kernel/topology.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index 45e805b357eb..417555806149 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -321,10 +321,10 @@ static void __init parse_dt_cpu_power(void) return; else if (4 * max_capacity < (3 * (max_capacity + min_capacity))) middle_capacity = (min_capacity + max_capacity) - >> (SCHED_POWER_SHIFT+1); + >> (SCHED_CAPACITY_SHIFT+1); else middle_capacity = ((max_capacity / 3) - >> (SCHED_POWER_SHIFT-1)) + 1; + >> (SCHED_CAPACITY_SHIFT-1)) + 1; } /* @@ -441,7 +441,7 @@ static void __init reset_cpu_power(void) unsigned int cpu; for_each_possible_cpu(cpu) - set_power_scale(cpu, SCHED_POWER_SCALE); + set_power_scale(cpu, SCHED_CAPACITY_SCALE); } void __init init_cpu_topology(void) -- cgit v1.2.3 From 9499cfe8886d82b311e775145724e6d7eee40894 Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Tue, 22 Jul 2014 18:34:54 +0100 Subject: mailbox: Pack SCPI structures used for messages. The System Control Processor expects data sent in the messages to be contiguos. When using unpacked structures to describe the data being transmitted we increase the general size of the message which leads to SCP rejecting our request. Signed-off-by: Liviu Dudau Signed-off-by: Jon Medhurst --- drivers/mailbox/scpi_protocol.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/mailbox/scpi_protocol.c b/drivers/mailbox/scpi_protocol.c index c8a824a60a43..4e442667ed7f 100644 --- a/drivers/mailbox/scpi_protocol.c +++ b/drivers/mailbox/scpi_protocol.c @@ -228,7 +228,7 @@ unsigned long scpi_clk_get_val(u16 clk_id) { struct scpi_data_buf sdata; struct mhu_data_buf mdata; - struct { + struct __packed { u32 status; u32 clk_rate; } buf; @@ -247,7 +247,7 @@ int scpi_clk_set_val(u16 clk_id, unsigned long rate) struct scpi_data_buf sdata; struct mhu_data_buf mdata; int stat; - struct { + struct __packed { u32 clk_rate; u16 clk_id; } buf; @@ -265,7 +265,7 @@ struct scpi_opp *scpi_dvfs_get_opps(u8 domain) { struct scpi_data_buf sdata; struct mhu_data_buf mdata; - struct { + struct __packed { u32 status; u32 header; u32 freqs[MAX_DVFS_OPPS]; @@ -312,7 +312,7 @@ int scpi_dvfs_get_idx(u8 domain) { struct scpi_data_buf sdata; struct mhu_data_buf mdata; - struct { + struct __packed { u32 status; u8 dvfs_idx; } buf; @@ -335,7 +335,7 @@ int scpi_dvfs_set_idx(u8 domain, u8 idx) { struct scpi_data_buf sdata; struct mhu_data_buf mdata; - struct { + struct __packed { u8 dvfs_domain; u8 dvfs_idx; } buf; -- cgit v1.2.3 From f5c0df9207051dc53a1e6c7fdf98954604c4749d Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Thu, 24 Jul 2014 11:14:21 +0100 Subject: mailbox: scpi: Free the mailbox channel when we fail to queue a message. When sending an SCPI command we aquire a channel and queue the message in the mailbox. If the queuing failed we were not releasing the channel hence preventing everyone else from using it. Signed-off-by: Punit Agrawal Signed-off-by: Liviu Dudau Signed-off-by: Jon Medhurst --- drivers/mailbox/scpi_protocol.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/mailbox/scpi_protocol.c b/drivers/mailbox/scpi_protocol.c index 4e442667ed7f..195f86c6fd58 100644 --- a/drivers/mailbox/scpi_protocol.c +++ b/drivers/mailbox/scpi_protocol.c @@ -180,8 +180,10 @@ static int send_scpi_cmd(struct scpi_data_buf *scpi_buf, bool high_priority) return PTR_ERR(chan); init_completion(&scpi_buf->complete); - if (mbox_send_message(chan, (void *)data)) - return -EIO; + if (mbox_send_message(chan, (void *)data) < 0) { + status = SCPI_ERR_TIMEOUT; + goto free_channel; + } if (!wait_for_completion_timeout(&scpi_buf->complete, msecs_to_jiffies(50))) @@ -189,6 +191,7 @@ static int send_scpi_cmd(struct scpi_data_buf *scpi_buf, bool high_priority) else status = *(u32 *)(data->rx_buf); /* read first word */ +free_channel: mbox_free_channel(chan); return scpi_to_linux_errno(status); -- cgit v1.2.3 From a1f3ee46de704984a62c5bd319c640a92990f235 Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Thu, 24 Jul 2014 12:21:17 +0100 Subject: mailbox: mhu: Acknowledge the interrupt only after data is pushed According to the mailbox documentation the controller should ACK the RX only after it has finished pushing the data up the link. Signed-off-by: Punit Agrawal Signed-off-by: Liviu Dudau Signed-off-by: Jon Medhurst --- drivers/mailbox/arm_mhu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c index 6256caae9133..5029af71780d 100644 --- a/drivers/mailbox/arm_mhu.c +++ b/drivers/mailbox/arm_mhu.c @@ -130,8 +130,8 @@ static irqreturn_t mbox_handler(int irq, void *p) memcpy(data->rx_buf, payload + RX_PAYLOAD(idx), data->rx_size); chan->data = NULL; - writel(~0, mbox_base + RX_CLEAR(idx)); mbox_chan_received_data(link, data); + writel(~0, mbox_base + RX_CLEAR(idx)); } return IRQ_HANDLED; -- cgit v1.2.3 From c5b3f616c2f81589d3d8959f8169abe1336ce681 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Tue, 29 Jul 2014 14:02:26 +0100 Subject: mailbox: Remove all message timeouts and block until they complete Neither the mailbox framework nor the scpi_protocol code correctly handle timeouts if a message is subsequently completed by the SCP, in that case they end up accessing no-longer live stack based objects. Even if the code was reworked to fix those issues, we are still left with problems with the scpi protocol because a delayed message response may look like a reply to a later message. To hopefully avoid all these problems this patch removes all timeouts and forces things block until each message completes. Signed-off-by: Jon Medhurst --- drivers/mailbox/arm_mhu.c | 3 +-- drivers/mailbox/scpi_protocol.c | 10 +++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c index 5029af71780d..c6842784e410 100644 --- a/drivers/mailbox/arm_mhu.c +++ b/drivers/mailbox/arm_mhu.c @@ -246,8 +246,7 @@ static int mhu_probe(struct platform_device *pdev) ctlr->mbox_con.chans = l; ctlr->mbox_con.num_chans = CHANNEL_MAX; - ctlr->mbox_con.txdone_poll = true; - ctlr->mbox_con.txpoll_period = 10; + ctlr->mbox_con.txdone_irq = true; ctlr->mbox_con.ops = &mhu_ops; ctlr->mbox_con.dev = dev; diff --git a/drivers/mailbox/scpi_protocol.c b/drivers/mailbox/scpi_protocol.c index 195f86c6fd58..edcf47ea06ab 100644 --- a/drivers/mailbox/scpi_protocol.c +++ b/drivers/mailbox/scpi_protocol.c @@ -168,8 +168,7 @@ static int send_scpi_cmd(struct scpi_data_buf *scpi_buf, bool high_priority) cl.dev = the_scpi_device; cl.rx_callback = scpi_rx_callback; cl.tx_done = NULL; - cl.tx_block = true; - cl.tx_tout = 50; /* 50 msec */ + cl.tx_block = false; cl.knows_txdone = false; cl.chan_name = high_priority ? CHANNEL_HIGH_PRIORITY : @@ -185,11 +184,8 @@ static int send_scpi_cmd(struct scpi_data_buf *scpi_buf, bool high_priority) goto free_channel; } - if (!wait_for_completion_timeout(&scpi_buf->complete, - msecs_to_jiffies(50))) - status = SCPI_ERR_TIMEOUT; - else - status = *(u32 *)(data->rx_buf); /* read first word */ + wait_for_completion(&scpi_buf->complete); + status = *(u32 *)(data->rx_buf); /* read first word */ free_channel: mbox_free_channel(chan); -- cgit v1.2.3 From 14c5a81134c58c5e5b9acff55861604c517437f8 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 22 Aug 2014 14:59:45 +0100 Subject: mailbox: mhu: Replace use of devm_request_and_ioremap() This deprecated API is removed in Linux 3.17 Signed-off-by: Jon Medhurst --- drivers/mailbox/arm_mhu.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c index c6842784e410..28fb4f01b413 100644 --- a/drivers/mailbox/arm_mhu.c +++ b/drivers/mailbox/arm_mhu.c @@ -217,10 +217,10 @@ static int mhu_probe(struct platform_device *pdev) return -ENXIO; } - ctlr->mbox_base = devm_request_and_ioremap(dev, res); - if (!ctlr->mbox_base) { + ctlr->mbox_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctlr->mbox_base)) { dev_err(dev, "failed to request or ioremap mailbox control\n"); - return -EADDRNOTAVAIL; + return PTR_ERR(ctlr->mbox_base); } res = platform_get_resource(pdev, IORESOURCE_MEM, 1); @@ -229,10 +229,10 @@ static int mhu_probe(struct platform_device *pdev) return -ENXIO; } - ctlr->payload_base = devm_request_and_ioremap(dev, res); - if (!ctlr->payload_base) { + ctlr->payload_base = devm_ioremap_resource(dev, res); + if (IS_ERR(ctlr->payload_base)) { dev_err(dev, "failed to request or ioremap mailbox payload\n"); - return -EADDRNOTAVAIL; + return PTR_ERR(ctlr->payload_base); } ctlr->dev = dev; -- cgit v1.2.3 From e9d5402b599eb3e5a0b99062d354f328d1fd56bf Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Fri, 22 Aug 2014 13:07:50 +0100 Subject: mailbox: mhu: Update for new version of mailbox patches Signed-off-by: Jon Medhurst --- drivers/mailbox/scpi_protocol.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/mailbox/scpi_protocol.c b/drivers/mailbox/scpi_protocol.c index edcf47ea06ab..49b500cd87ef 100644 --- a/drivers/mailbox/scpi_protocol.c +++ b/drivers/mailbox/scpi_protocol.c @@ -170,11 +170,8 @@ static int send_scpi_cmd(struct scpi_data_buf *scpi_buf, bool high_priority) cl.tx_done = NULL; cl.tx_block = false; cl.knows_txdone = false; - cl.chan_name = high_priority ? - CHANNEL_HIGH_PRIORITY : - CHANNEL_LOW_PRIORITY; - chan = mbox_request_channel(&cl); + chan = mbox_request_channel(&cl, high_priority); if (IS_ERR(chan)) return PTR_ERR(chan); -- cgit v1.2.3 From 3bbaf1350c7a83ad507c94e2cbe8f344dc474206 Mon Sep 17 00:00:00 2001 From: Filipe Rinaldi Date: Fri, 20 Jun 2014 15:25:38 +0100 Subject: scpi: Add voltage on the DVFS Info command Newer versions of SCP added voltage as one of the parameters in the DVFS Info command. This patch reads the voltage which can be used by CPUFreq and Devfreq. Signed-off-by: Filipe Rinaldi Signed-off-by: Jon Medhurst --- drivers/clk/clk-scpi.c | 26 +++++++++++++------------- drivers/cpufreq/scpi-cpufreq.c | 21 +++++++++++---------- drivers/mailbox/scpi_protocol.c | 28 ++++++++++++++-------------- include/linux/scpi_protocol.h | 9 +++++++-- 4 files changed, 45 insertions(+), 39 deletions(-) diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c index 2d707663542f..e4874bfeaae5 100644 --- a/drivers/clk/clk-scpi.c +++ b/drivers/clk/clk-scpi.c @@ -72,11 +72,11 @@ static struct clk_ops scpi_clk_ops = { static int __scpi_dvfs_round_rate(struct scpi_clk *clk, unsigned long rate) { int idx, max_opp = clk->opps->count; - u32 *freqs = clk->opps->freqs; + struct scpi_opp_entry *opp = clk->opps->opp; u32 fmin = 0, fmax = ~0, ftmp; - for (idx = 0; idx < max_opp; idx++, freqs++) { - ftmp = *freqs; + for (idx = 0; idx < max_opp; idx++, opp++) { + ftmp = opp->freq_hz; if (ftmp >= (u32)rate) { if (ftmp <= fmax) fmax = ftmp; @@ -96,12 +96,12 @@ static unsigned long scpi_dvfs_recalc_rate(struct clk_hw *hw, { struct scpi_clk *clk = to_scpi_clk(hw); int idx = scpi_dvfs_get_idx(clk->id); - u32 *freqs = clk->opps->freqs; + struct scpi_opp_entry *opp = clk->opps->opp; if (idx < 0) return 0; else - return *(freqs + idx); + return opp[idx].freq_hz; } static long scpi_dvfs_round_rate(struct clk_hw *hw, unsigned long rate, @@ -114,10 +114,10 @@ static long scpi_dvfs_round_rate(struct clk_hw *hw, unsigned long rate, static int __scpi_find_dvfs_index(struct scpi_clk *clk, unsigned long rate) { int idx, max_opp = clk->opps->count; - u32 *freqs = clk->opps->freqs; + struct scpi_opp_entry *opp = clk->opps->opp; - for (idx = 0; idx < max_opp; idx++, freqs++) - if (*freqs == (u32)rate) + for (idx = 0; idx < max_opp; idx++, opp++) + if (opp->freq_hz == (u32)rate) break; return (idx == max_opp) ? -EINVAL : idx; } @@ -145,7 +145,7 @@ scpi_dvfs_ops_init(struct device *dev, struct device_node *np, struct scpi_clk *sclk) { struct clk_init_data init; - struct scpi_opp *opp; + struct scpi_opp *opps; init.name = sclk->name; init.flags = CLK_IS_ROOT; @@ -153,11 +153,11 @@ scpi_dvfs_ops_init(struct device *dev, struct device_node *np, init.ops = &scpi_dvfs_ops; sclk->hw.init = &init; - opp = scpi_dvfs_get_opps(sclk->id); - if (IS_ERR(opp)) - return (struct clk *)opp; + opps = scpi_dvfs_get_opps(sclk->id); + if (IS_ERR(opps)) + return (struct clk *)opps; - sclk->opps = opp; + sclk->opps = opps; return devm_clk_register(dev, &sclk->hw); } diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c index 60725199b9aa..65482c6e9c03 100644 --- a/drivers/cpufreq/scpi-cpufreq.c +++ b/drivers/cpufreq/scpi-cpufreq.c @@ -30,20 +30,21 @@ static int scpi_init_opp_table(struct device *cpu_dev) { u8 domain = topology_physical_package_id(cpu_dev->id); - struct scpi_opp *opp; + struct scpi_opp *opps; int idx, ret = 0, max_opp; - u32 *freqs; + struct scpi_opp_entry *opp; - opp = scpi_dvfs_get_opps(domain); - if (IS_ERR(opp)) - return PTR_ERR(opp); + opps = scpi_dvfs_get_opps(domain); + if (IS_ERR(opps)) + return PTR_ERR(opps); - freqs = opp->freqs; - max_opp = opp->count; - for (idx = 0; idx < max_opp; idx++, freqs++) { - ret = dev_pm_opp_add(cpu_dev, *freqs, 900000000 /* TODO */); + opp = opps->opp; + max_opp = opps->count; + for (idx = 0; idx < max_opp; idx++, opp++) { + ret = dev_pm_opp_add(cpu_dev, opp->freq_hz, opp->volt_mv*1000); if (ret) { - dev_warn(cpu_dev, "failed to add opp %u\n", *freqs); + dev_warn(cpu_dev, "failed to add opp %uHz %umV\n", + opp->freq_hz, opp->volt_mv); return ret; } } diff --git a/drivers/mailbox/scpi_protocol.c b/drivers/mailbox/scpi_protocol.c index 49b500cd87ef..d3fcc8144ced 100644 --- a/drivers/mailbox/scpi_protocol.c +++ b/drivers/mailbox/scpi_protocol.c @@ -264,10 +264,10 @@ struct scpi_opp *scpi_dvfs_get_opps(u8 domain) struct __packed { u32 status; u32 header; - u32 freqs[MAX_DVFS_OPPS]; + struct scpi_opp_entry opp[MAX_DVFS_OPPS]; } buf; - struct scpi_opp *opp; - size_t freqs_sz; + struct scpi_opp *opps; + size_t opps_sz; int count, ret; if (domain >= MAX_DVFS_DOMAINS) @@ -282,25 +282,25 @@ struct scpi_opp *scpi_dvfs_get_opps(u8 domain) if (ret) return ERR_PTR(ret); - opp = kmalloc(sizeof(*opp), GFP_KERNEL); - if (!opp) + opps = kmalloc(sizeof(*opps), GFP_KERNEL); + if (!opps) return ERR_PTR(-ENOMEM); count = DVFS_OPP_COUNT(buf.header); - freqs_sz = count * sizeof(*(opp->freqs)); + opps_sz = count * sizeof(*(opps->opp)); - opp->count = count; - opp->latency = DVFS_LATENCY(buf.header); - opp->freqs = kmalloc(freqs_sz, GFP_KERNEL); - if (!opp->freqs) { - kfree(opp); + opps->count = count; + opps->latency = DVFS_LATENCY(buf.header); + opps->opp = kmalloc(opps_sz, GFP_KERNEL); + if (!opps->opp) { + kfree(opps); return ERR_PTR(-ENOMEM); } - memcpy(opp->freqs, &buf.freqs[0], freqs_sz); - scpi_opps[domain] = opp; + memcpy(opps->opp, &buf.opp[0], opps_sz); + scpi_opps[domain] = opps; - return opp; + return opps; } EXPORT_SYMBOL_GPL(scpi_dvfs_get_opps); diff --git a/include/linux/scpi_protocol.h b/include/linux/scpi_protocol.h index 66e5eb3710ab..0fdc969d8b9e 100644 --- a/include/linux/scpi_protocol.h +++ b/include/linux/scpi_protocol.h @@ -17,11 +17,16 @@ */ #include +struct scpi_opp_entry { + u32 freq_hz; + u32 volt_mv; +} __packed; + struct scpi_opp { - u32 *freqs; + struct scpi_opp_entry *opp; u32 latency; /* in usecs */ int count; -}; +} __packed; unsigned long scpi_clk_get_val(u16 clk_id); int scpi_clk_set_val(u16 clk_id, unsigned long rate); -- cgit v1.2.3 From 8bea7add6c6fca1c56fa52bfb5e191dd430dba11 Mon Sep 17 00:00:00 2001 From: Filipe Rinaldi Date: Fri, 20 Jun 2014 11:38:35 +0100 Subject: scpi: Increase the maximum number of DVFS OPPs Signed-off-by: Filipe Rinaldi Signed-off-by: Jon Medhurst --- drivers/mailbox/scpi_protocol.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mailbox/scpi_protocol.c b/drivers/mailbox/scpi_protocol.c index d3fcc8144ced..16daf3c16d75 100644 --- a/drivers/mailbox/scpi_protocol.c +++ b/drivers/mailbox/scpi_protocol.c @@ -39,7 +39,7 @@ (((txsz) & CMD_DATA_SIZE_MASK) << CMD_DATA_SIZE_SHIFT)) #define MAX_DVFS_DOMAINS 3 -#define MAX_DVFS_OPPS 4 +#define MAX_DVFS_OPPS 8 #define DVFS_LATENCY(hdr) ((hdr) >> 16) #define DVFS_OPP_COUNT(hdr) (((hdr) >> 8) & 0xff) -- cgit v1.2.3 From 67b126a317577c58bc75cc2cea8593b1af22b7df Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Mon, 12 Jan 2015 12:06:24 +0000 Subject: dts: juno: Add cpu-map nodes Signed-off-by: Jon Medhurst --- arch/arm64/boot/dts/arm/juno.dts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts index cb3073e4e7a8..a44b543325d6 100644 --- a/arch/arm64/boot/dts/arm/juno.dts +++ b/arch/arm64/boot/dts/arm/juno.dts @@ -34,6 +34,32 @@ #address-cells = <2>; #size-cells = <0>; + cpu-map { + cluster1 { + core0 { + cpu = <&A53_0>; + }; + core1 { + cpu = <&A53_1>; + }; + core2 { + cpu = <&A53_2>; + }; + core3 { + cpu = <&A53_3>; + }; + }; + + cluster0 { + core0 { + cpu = <&A57_0>; + }; + core1 { + cpu = <&A57_1>; + }; + }; + }; + A57_0: cpu@0 { compatible = "arm,cortex-a57","arm,armv8"; reg = <0x0 0x0>; -- cgit v1.2.3 From f597b2a2f5b006c05f2ab36caafbada509266f79 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Mon, 12 Jan 2015 12:07:03 +0000 Subject: dts: juno: Add mailbox nodes Signed-off-by: Jon Medhurst --- arch/arm64/boot/dts/arm/juno.dts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts index a44b543325d6..b9777f38568d 100644 --- a/arch/arm64/boot/dts/arm/juno.dts +++ b/arch/arm64/boot/dts/arm/juno.dts @@ -140,6 +140,17 @@ ; }; + mailbox: mhu@2b1f0000 { + compatible = "arm,mhu"; + reg = <0x0 0x2b1f0000 0x0 0x10000>, /* MHU registers */ + <0x0 0x2e000000 0x0 0x10000>; /* Payload area */ + interrupts = <0 36 4>, /* low priority interrupt */ + <0 35 4>; /* high priority interrupt */ + #mbox-cells = <1>; + mbox-names = "cpu_to_scp_low", "cpu_to_scp_high"; + mboxes = <&mailbox 0 &mailbox 1>; + }; + /include/ "juno-clocks.dtsi" dma@7ff00000 { -- cgit v1.2.3 From b97f2d7053a0daaf6b4736c944fb9ab32805f97f Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Mon, 12 Jan 2015 12:07:32 +0000 Subject: dts: juno: Add SCPI and cpufreq nodes Signed-off-by: Jon Medhurst --- arch/arm64/boot/dts/arm/juno.dts | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts index b9777f38568d..523bb0e83001 100644 --- a/arch/arm64/boot/dts/arm/juno.dts +++ b/arch/arm64/boot/dts/arm/juno.dts @@ -65,6 +65,8 @@ reg = <0x0 0x0>; device_type = "cpu"; enable-method = "psci"; + clocks = <&scpi_dvfs 0>; + clock-names = "vbig"; }; A57_1: cpu@1 { @@ -72,6 +74,8 @@ reg = <0x0 0x1>; device_type = "cpu"; enable-method = "psci"; + clocks = <&scpi_dvfs 0>; + clock-names = "vbig"; }; A53_0: cpu@100 { @@ -79,6 +83,8 @@ reg = <0x0 0x100>; device_type = "cpu"; enable-method = "psci"; + clocks = <&scpi_dvfs 1>; + clock-names = "vlittle"; }; A53_1: cpu@101 { @@ -86,6 +92,8 @@ reg = <0x0 0x101>; device_type = "cpu"; enable-method = "psci"; + clocks = <&scpi_dvfs 1>; + clock-names = "vlittle"; }; A53_2: cpu@102 { @@ -93,6 +101,8 @@ reg = <0x0 0x102>; device_type = "cpu"; enable-method = "psci"; + clocks = <&scpi_dvfs 1>; + clock-names = "vlittle"; }; A53_3: cpu@103 { @@ -100,6 +110,8 @@ reg = <0x0 0x103>; device_type = "cpu"; enable-method = "psci"; + clocks = <&scpi_dvfs 1>; + clock-names = "vlittle"; }; }; @@ -151,6 +163,30 @@ mboxes = <&mailbox 0 &mailbox 1>; }; + clocks { + compatible = "arm,scpi-clks"; + + scpi_dvfs: scpi_clocks@0 { + compatible = "arm,scpi-clk-indexed"; + #clock-cells = <1>; + clock-indices = <0>, <1>, <2>; + clock-output-names = "vbig", "vlittle", "vgpu"; + }; + + scpi_clk: scpi_clocks@3 { + compatible = "arm,scpi-clk-range"; + #clock-cells = <1>; + clock-indices = <3>, <4>; + frequency-range = <23000000 210000000>; + clock-output-names = "pxlclk0", "pxlclk1"; + }; + + }; + + cpufreq { + compatible = "arm,scpi-cpufreq"; + }; + /include/ "juno-clocks.dtsi" dma@7ff00000 { -- cgit v1.2.3 From 5338f9876b1ecfd4cb43181a948c2ad9474fb4be Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Mon, 12 Jan 2015 16:35:08 +0000 Subject: mailbox: mhu: Initialise all members of struct mbox_client Failure to do so means means we may end up using uninitialised values if new members are added, leading to bugs; as happens with commit 97b0c7bd2e86 (mailbox: add tx_prepare client callback). Signed-off-by: Jon Medhurst --- drivers/mailbox/scpi_protocol.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/mailbox/scpi_protocol.c b/drivers/mailbox/scpi_protocol.c index 16daf3c16d75..0388733c3c22 100644 --- a/drivers/mailbox/scpi_protocol.c +++ b/drivers/mailbox/scpi_protocol.c @@ -161,15 +161,12 @@ static void scpi_rx_callback(struct mbox_client *cl, void *msg) static int send_scpi_cmd(struct scpi_data_buf *scpi_buf, bool high_priority) { struct mbox_chan *chan; - struct mbox_client cl; + struct mbox_client cl = {0}; struct mhu_data_buf *data = scpi_buf->data; u32 status; cl.dev = the_scpi_device; cl.rx_callback = scpi_rx_callback; - cl.tx_done = NULL; - cl.tx_block = false; - cl.knows_txdone = false; chan = mbox_request_channel(&cl, high_priority); if (IS_ERR(chan)) -- cgit v1.2.3