Hello,
This patchset adds initial support for the PCC (Platform Communication Channel) interface as described in the current ACPI 5.0a spec. See Section 14 of the ACPI spec - http://acpi.info/DOWNLOADS/ACPI_5_Errata%20A.pdf for more details on how PCC works.
In brief PCC (Platform Communication Channel) is a generic means for PCC clients, to talk to the firmware. The PCC register space is typically memory mapped IO and uses a doorbell mechanism to communicate synchronously from the OS to the firmware. The PCC driver is completely agnostic to the protocol implemented by the PCC clients. It only implements the enumeration of PCC channels and the low level transport mechanism and leaves the rest to the PCC clients.
The PCC is meant to be useable in the future by clients such as CPPC (Collaborative Processor Performance Control), RAS (Reliability, Availability and Serviceability) and MPST (Memory Power State Tables) and possibly others.
While the PCC clients will come as following patches whenever they're ready, I wanted to get feedback on this common driver and hope that it could be merged upstream. This should hopefully help various folks that are working on drivers that rely on the PCC interface.
Cheers, Ashwin
=== Testing Methodology ===
The PCC test driver in [2/2] is a simple driver that was used to demonstrate how PCC clients would use the PCC driver. The PCC driver was tested by sending multiple PCC READS and PCC WRITES across a shared memory region on an MSM ARMv7 platform. This memory is shared between an apps processor and a power controller processor. So, one end of the PCC channel is the PCC test driver running on the apps processor and the other end is a debugger script (running on a JTAG debugger) thats attached to the power processor. The debugger script is busy looping on the doorbell address waiting for a bit to flip. This bit indicates a synchronous communication from the apps processor. The doorbell is rung when the OS sends a PCC READ or PCC WRITE command. The PCC communication channel is simply a few fake 32 bit registers that are incremented by the OS and the debugger script. The OS increments the registers and then sends the PCC WRITE command. On a PCC READ, the script increments these registers and then OS reads them back. The values are always incremented by 1 by either end, so we know what value to expect for each PCC READ/WRITE.
Ashwin Chaugule (2): ACPI: Add support for Platform Communication Channel PCC Test driver
drivers/acpi/Kconfig | 10 +++ drivers/acpi/Makefile | 2 +- drivers/acpi/pcc-test.c | 172 +++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/pcc.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 drivers/acpi/pcc-test.c create mode 100644 drivers/acpi/pcc.c
ACPI 5.0a+ spec defines a generic mode of communication between the OS and a platform such as the BMC. This medium (PCC) is typically used by CPPC (ACPI CPU Performance management), RAS (ACPI reliability protocol) and MPST (ACPI Memory power states).
This patch adds initial support for PCC to be usable by the aforementioned PCC clients.
Signed-off-by: Ashwin Chaugule ashwin.chaugule@linaro.org --- drivers/acpi/Kconfig | 10 +++ drivers/acpi/Makefile | 2 +- drivers/acpi/pcc.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 drivers/acpi/pcc.c
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index ff5c83c..8f40e52 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -352,6 +352,16 @@ config ACPI_REDUCED_HARDWARE_ONLY
If you are unsure what to do, do not enable this option.
+config ACPI_PCC + bool "ACPI Platform Communication Channel" + def_bool n + depends on ACPI + help + Enable this option if your platform supports PCC as defined in the + ACPI spec 5.0a+. PCC is a generic mechanism for the OS to communicate + with a platform such as a BMC. PCC is typically used by CPPC, RAS + and MPST. + source "drivers/acpi/apei/Kconfig"
config ACPI_EXTLOG diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index fc133d4..d8aa613 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -74,7 +74,7 @@ obj-$(CONFIG_ACPI_HED) += hed.o obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o - +obj-$(CONFIG_ACPI_PCC) += pcc.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o processor_throttling.o processor-y += processor_idle.o processor_thermal.o diff --git a/drivers/acpi/pcc.c b/drivers/acpi/pcc.c new file mode 100644 index 0000000..328ca93 --- /dev/null +++ b/drivers/acpi/pcc.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2014 Linaro Ltd. + * Author: Ashwin Chaugule ashwin.chaugule@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + */ + +#include <linux/acpi.h> +#include <linux/io.h> +#include <linux/uaccess.h> +#include <linux/init.h> +#include <linux/cpufreq.h> +#include <linux/delay.h> +#include <linux/ioctl.h> +#include <linux/vmalloc.h> + +#include <acpi/actbl.h> + +#define MAX_PCC_SUBSPACES 256 +#define PCCS_SS_SIG_MAGIC 0x50434300 +#define PCC_CMD_COMPLETE 0x1 +#define PCC_VERSION "0.1" + +struct pcc_ss_desc { + struct acpi_pcct_subspace *pcc_ss_ptr; + raw_spinlock_t lock; +}; + +/* Array of Type 0 PCC Subspace descriptors */ +struct pcc_ss_desc pcc_ss_arr[MAX_PCC_SUBSPACES]; + +/* Total number of subspaces detected in PCCT. */ +static int total_ss; + +/* + * PCC clients call this function to get a base address of their + * Communication channel + */ +int get_pcc_comm_channel(u32 ss_idx, u64 __iomem *addr, int *len) +{ + struct acpi_pcct_subspace *pcct_subspace = + pcc_ss_arr[ss_idx].pcc_ss_ptr; + + if (pcct_subspace) { + *addr = pcct_subspace->base_address; + *len = pcct_subspace->length; + } else + return -EINVAL; + + return 0; +} + +/* Send PCC cmd on behalf of this (subspace id) PCC client */ +u16 send_pcc_cmd(u8 cmd, u8 sci, u32 ss_idx, u64 __iomem *base_addr) +{ + struct acpi_pcct_subspace *pcct_subspace = + pcc_ss_arr[ss_idx].pcc_ss_ptr; + struct acpi_pcct_shared_memory *generic_comm_base = + (struct acpi_pcct_shared_memory *)base_addr; + struct acpi_generic_address doorbell; + u64 doorbell_preserve; + u64 doorbell_val; + u64 doorbell_write; + /* + * Min time in usec that OSPM is expected to wait + * before sending the next PCC cmd. + */ + u16 cmd_delay = pcct_subspace->min_turnaround_time; + + if (!generic_comm_base) { + pr_err("No Generic Communication Channel provided.\n"); + return -EINVAL; + } + + raw_spin_lock(&pcc_ss_arr[ss_idx].lock); + + /* Get doorbell details for this subspace. */ + doorbell = pcct_subspace->doorbell_register; + doorbell_preserve = pcct_subspace->preserve_mask; + doorbell_write = pcct_subspace->write_mask; + + /* Loop until CMD complete bit is set. For prev cmds. */ + while (!(ioread16(&generic_comm_base->status) & PCC_CMD_COMPLETE)) + udelay(cmd_delay); + + /* Write to the shared comm region. */ + iowrite16(cmd, &generic_comm_base->command); + + /* Write Subspace MAGIC value so platform can identify destination. */ + iowrite32((PCCS_SS_SIG_MAGIC | ss_idx), &generic_comm_base->signature); + + /* Flip CMD COMPLETE bit */ + iowrite16(0, &generic_comm_base->status); + + /* Sync notification from OSPM to Platform. */ + acpi_read(&doorbell_val, &doorbell); + acpi_write((doorbell_val & doorbell_preserve) | doorbell_write, + &doorbell); + + /* Wait for Platform to consume. */ + while (!(ioread16(&generic_comm_base->status) & PCC_CMD_COMPLETE)) + udelay(cmd_delay); + + raw_spin_unlock(&pcc_ss_arr[ss_idx].lock); + + return generic_comm_base->status; +} + +static int parse_pcc_subspace(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_pcct_subspace *pcct_ss; + + if (total_ss <= MAX_PCC_SUBSPACES) { + pcct_ss = (struct acpi_pcct_subspace *) header; + + if (pcct_ss->header.type != ACPI_PCCT_TYPE_GENERIC_SUBSPACE) { + pr_err("Incorrect PCC Subspace type detected\n"); + return -EINVAL; + } + + pcc_ss_arr[total_ss].pcc_ss_ptr = pcct_ss; + raw_spin_lock_init(&pcc_ss_arr[total_ss].lock); + + total_ss++; + } else { + pr_err("No more space for PCC subspaces.\n"); + return -ENOSPC; + } + + return 0; +} + +static int __init pcc_probe(void) +{ + acpi_status status = AE_OK; + acpi_size pcct_tbl_header_size; + struct acpi_table_pcct *pcct_tbl; + + /* Search for PCCT */ + status = acpi_get_table_with_size(ACPI_SIG_PCCT, 0, + (struct acpi_table_header **)&pcct_tbl, + &pcct_tbl_header_size); + + if (ACPI_SUCCESS(status) && !pcct_tbl) { + pr_warn("PCCT header not found.\n"); + status = AE_NOT_FOUND; + goto out_err; + } + + status = acpi_table_parse_entries(ACPI_SIG_PCCT, + sizeof(struct acpi_table_pcct), + ACPI_PCCT_TYPE_GENERIC_SUBSPACE, + parse_pcc_subspace, MAX_PCC_SUBSPACES); + + if (ACPI_SUCCESS(status)) + pr_err("Error parsing PCC subspaces from PCCT\n"); + + pr_info("Detected %d PCC Subspaces\n", total_ss); + +out_err: + return ACPI_SUCCESS(status) ? 1 : 0; +} + +static int __init pcc_init(void) +{ + int ret; + + if (acpi_disabled) + return -ENODEV; + + /* Check if PCC support is available. */ + ret = pcc_probe(); + + if (ret) { + pr_debug("PCC probe failed.\n"); + return -EINVAL; + } + + return ret; +} + +device_initcall(pcc_init);
On Tue, May 27, 2014 at 04:29:58PM -0400, Ashwin Chaugule wrote:
ACPI 5.0a+ spec defines a generic mode of communication between the OS and a platform such as the BMC. This medium (PCC) is typically used by CPPC (ACPI CPU Performance management), RAS (ACPI reliability protocol) and MPST (ACPI Memory power states).
This patch adds initial support for PCC to be usable by the aforementioned PCC clients.
This looks an awful lot like a mailbox - should it be integrated with the mailbox system that Jassi (CCed) has been working on getting upstream? Not deleting any context for Jassi's benefit.
Signed-off-by: Ashwin Chaugule ashwin.chaugule@linaro.org
drivers/acpi/Kconfig | 10 +++ drivers/acpi/Makefile | 2 +- drivers/acpi/pcc.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 drivers/acpi/pcc.c
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index ff5c83c..8f40e52 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -352,6 +352,16 @@ config ACPI_REDUCED_HARDWARE_ONLY If you are unsure what to do, do not enable this option. +config ACPI_PCC
- bool "ACPI Platform Communication Channel"
- def_bool n
- depends on ACPI
- help
- Enable this option if your platform supports PCC as defined in the
- ACPI spec 5.0a+. PCC is a generic mechanism for the OS to communicate
- with a platform such as a BMC. PCC is typically used by CPPC, RAS
- and MPST.
source "drivers/acpi/apei/Kconfig" config ACPI_EXTLOG diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index fc133d4..d8aa613 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -74,7 +74,7 @@ obj-$(CONFIG_ACPI_HED) += hed.o obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o
+obj-$(CONFIG_ACPI_PCC) += pcc.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o processor_throttling.o processor-y += processor_idle.o processor_thermal.o diff --git a/drivers/acpi/pcc.c b/drivers/acpi/pcc.c new file mode 100644 index 0000000..328ca93 --- /dev/null +++ b/drivers/acpi/pcc.c @@ -0,0 +1,192 @@ +/*
- Copyright (C) 2014 Linaro Ltd.
- Author: Ashwin Chaugule ashwin.chaugule@linaro.org
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that 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.
- */
+#include <linux/acpi.h> +#include <linux/io.h> +#include <linux/uaccess.h> +#include <linux/init.h> +#include <linux/cpufreq.h> +#include <linux/delay.h> +#include <linux/ioctl.h> +#include <linux/vmalloc.h>
+#include <acpi/actbl.h>
+#define MAX_PCC_SUBSPACES 256 +#define PCCS_SS_SIG_MAGIC 0x50434300 +#define PCC_CMD_COMPLETE 0x1 +#define PCC_VERSION "0.1"
+struct pcc_ss_desc {
- struct acpi_pcct_subspace *pcc_ss_ptr;
- raw_spinlock_t lock;
+};
+/* Array of Type 0 PCC Subspace descriptors */ +struct pcc_ss_desc pcc_ss_arr[MAX_PCC_SUBSPACES];
+/* Total number of subspaces detected in PCCT. */ +static int total_ss;
+/*
- PCC clients call this function to get a base address of their
- Communication channel
- */
+int get_pcc_comm_channel(u32 ss_idx, u64 __iomem *addr, int *len) +{
- struct acpi_pcct_subspace *pcct_subspace =
pcc_ss_arr[ss_idx].pcc_ss_ptr;
- if (pcct_subspace) {
*addr = pcct_subspace->base_address;
*len = pcct_subspace->length;
- } else
return -EINVAL;
- return 0;
+}
+/* Send PCC cmd on behalf of this (subspace id) PCC client */ +u16 send_pcc_cmd(u8 cmd, u8 sci, u32 ss_idx, u64 __iomem *base_addr) +{
- struct acpi_pcct_subspace *pcct_subspace =
pcc_ss_arr[ss_idx].pcc_ss_ptr;
- struct acpi_pcct_shared_memory *generic_comm_base =
(struct acpi_pcct_shared_memory *)base_addr;
- struct acpi_generic_address doorbell;
- u64 doorbell_preserve;
- u64 doorbell_val;
- u64 doorbell_write;
- /*
* Min time in usec that OSPM is expected to wait
* before sending the next PCC cmd.
*/
- u16 cmd_delay = pcct_subspace->min_turnaround_time;
- if (!generic_comm_base) {
pr_err("No Generic Communication Channel provided.\n");
return -EINVAL;
- }
- raw_spin_lock(&pcc_ss_arr[ss_idx].lock);
- /* Get doorbell details for this subspace. */
- doorbell = pcct_subspace->doorbell_register;
- doorbell_preserve = pcct_subspace->preserve_mask;
- doorbell_write = pcct_subspace->write_mask;
- /* Loop until CMD complete bit is set. For prev cmds. */
- while (!(ioread16(&generic_comm_base->status) & PCC_CMD_COMPLETE))
udelay(cmd_delay);
- /* Write to the shared comm region. */
- iowrite16(cmd, &generic_comm_base->command);
- /* Write Subspace MAGIC value so platform can identify destination. */
- iowrite32((PCCS_SS_SIG_MAGIC | ss_idx), &generic_comm_base->signature);
- /* Flip CMD COMPLETE bit */
- iowrite16(0, &generic_comm_base->status);
- /* Sync notification from OSPM to Platform. */
- acpi_read(&doorbell_val, &doorbell);
- acpi_write((doorbell_val & doorbell_preserve) | doorbell_write,
&doorbell);
- /* Wait for Platform to consume. */
- while (!(ioread16(&generic_comm_base->status) & PCC_CMD_COMPLETE))
udelay(cmd_delay);
- raw_spin_unlock(&pcc_ss_arr[ss_idx].lock);
- return generic_comm_base->status;
+}
+static int parse_pcc_subspace(struct acpi_subtable_header *header,
const unsigned long end)
+{
- struct acpi_pcct_subspace *pcct_ss;
- if (total_ss <= MAX_PCC_SUBSPACES) {
pcct_ss = (struct acpi_pcct_subspace *) header;
if (pcct_ss->header.type != ACPI_PCCT_TYPE_GENERIC_SUBSPACE) {
pr_err("Incorrect PCC Subspace type detected\n");
return -EINVAL;
}
pcc_ss_arr[total_ss].pcc_ss_ptr = pcct_ss;
raw_spin_lock_init(&pcc_ss_arr[total_ss].lock);
total_ss++;
- } else {
pr_err("No more space for PCC subspaces.\n");
return -ENOSPC;
- }
- return 0;
+}
+static int __init pcc_probe(void) +{
- acpi_status status = AE_OK;
- acpi_size pcct_tbl_header_size;
- struct acpi_table_pcct *pcct_tbl;
- /* Search for PCCT */
- status = acpi_get_table_with_size(ACPI_SIG_PCCT, 0,
(struct acpi_table_header **)&pcct_tbl,
&pcct_tbl_header_size);
- if (ACPI_SUCCESS(status) && !pcct_tbl) {
pr_warn("PCCT header not found.\n");
status = AE_NOT_FOUND;
goto out_err;
- }
- status = acpi_table_parse_entries(ACPI_SIG_PCCT,
sizeof(struct acpi_table_pcct),
ACPI_PCCT_TYPE_GENERIC_SUBSPACE,
parse_pcc_subspace, MAX_PCC_SUBSPACES);
- if (ACPI_SUCCESS(status))
pr_err("Error parsing PCC subspaces from PCCT\n");
- pr_info("Detected %d PCC Subspaces\n", total_ss);
+out_err:
- return ACPI_SUCCESS(status) ? 1 : 0;
+}
+static int __init pcc_init(void) +{
- int ret;
- if (acpi_disabled)
return -ENODEV;
- /* Check if PCC support is available. */
- ret = pcc_probe();
- if (ret) {
pr_debug("PCC probe failed.\n");
return -EINVAL;
- }
- return ret;
+}
+device_initcall(pcc_init);
1.8.3.2
linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Fri, May 30, 2014 at 3:47 PM, Mark Brown broonie@kernel.org wrote:
On Tue, May 27, 2014 at 04:29:58PM -0400, Ashwin Chaugule wrote:
ACPI 5.0a+ spec defines a generic mode of communication between the OS and a platform such as the BMC. This medium (PCC) is typically used by CPPC (ACPI CPU Performance management), RAS (ACPI reliability protocol) and MPST (ACPI Memory power states).
This patch adds initial support for PCC to be usable by the aforementioned PCC clients.
This looks an awful lot like a mailbox - should it be integrated with the mailbox system that Jassi (CCed) has been working on getting upstream? Not deleting any context for Jassi's benefit.
Yeah, the mailbox api should support PCC as such, with pcc-test.c as the client above and pcc.c as the controller driver below it.
Thanks, Jassi
Hello,
On 30 May 2014 09:03, Jassi Brar jassisinghbrar@gmail.com wrote:
On Fri, May 30, 2014 at 3:47 PM, Mark Brown broonie@kernel.org wrote:
On Tue, May 27, 2014 at 04:29:58PM -0400, Ashwin Chaugule wrote:
ACPI 5.0a+ spec defines a generic mode of communication between the OS and a platform such as the BMC. This medium (PCC) is typically used by CPPC (ACPI CPU Performance management), RAS (ACPI reliability protocol) and MPST (ACPI Memory power states).
This patch adds initial support for PCC to be usable by the aforementioned PCC clients.
This looks an awful lot like a mailbox - should it be integrated with the mailbox system that Jassi (CCed) has been working on getting upstream? Not deleting any context for Jassi's benefit.
Yeah, the mailbox api should support PCC as such, with pcc-test.c as the client above and pcc.c as the controller driver below it.
Interesting. Do you have a git repo with this framework where I could have a look?
Cheers, Ashwin
On 30 May 2014 18:58, Ashwin Chaugule ashwin.chaugule@linaro.org wrote:
Hello,
On 30 May 2014 09:03, Jassi Brar jassisinghbrar@gmail.com wrote:
On Fri, May 30, 2014 at 3:47 PM, Mark Brown broonie@kernel.org wrote:
On Tue, May 27, 2014 at 04:29:58PM -0400, Ashwin Chaugule wrote:
ACPI 5.0a+ spec defines a generic mode of communication between the OS and a platform such as the BMC. This medium (PCC) is typically used by CPPC (ACPI CPU Performance management), RAS (ACPI reliability protocol) and MPST (ACPI Memory power states).
This patch adds initial support for PCC to be usable by the aforementioned PCC clients.
This looks an awful lot like a mailbox - should it be integrated with the mailbox system that Jassi (CCed) has been working on getting upstream? Not deleting any context for Jassi's benefit.
Yeah, the mailbox api should support PCC as such, with pcc-test.c as the client above and pcc.c as the controller driver below it.
Interesting. Do you have a git repo with this framework where I could have a look?
Please scrape it from https://lkml.org/lkml/2014/5/15/49 My platform also uses the doorbell mechanism with request and response written in shared-memory, so I am confident the mailbox api should work for PCC.
Regards, -Jassi
Hi Jassi,
On 30 May 2014 09:40, Jassi Brar jaswinder.singh@linaro.org wrote:
Please scrape it from https://lkml.org/lkml/2014/5/15/49 My platform also uses the doorbell mechanism with request and response written in shared-memory, so I am confident the mailbox api should work for PCC.
Just had a quick look. PCC integration appears doable and the queuing and async notification stuff also looks useful. I'll play around with it a bit more to see if it really works. Meanwhile, the one thing that comes to mind is the DT bindings. For an ACPI-only kernel, we'll probably need another function (at least) to register mbox controllers and clients.
Cheers, Ashwin
Opens a node in /proc/pcc_test.
Echo 0 for PCC read Echo 1 for PCC write Echo 2 for read base addr of PCC channel
Signed-off-by: Ashwin Chaugule ashwin.chaugule@linaro.org --- drivers/acpi/Makefile | 2 +- drivers/acpi/pcc-test.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 drivers/acpi/pcc-test.c
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index d8aa613..f7bbbe9 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -74,7 +74,7 @@ obj-$(CONFIG_ACPI_HED) += hed.o obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o -obj-$(CONFIG_ACPI_PCC) += pcc.o +obj-$(CONFIG_ACPI_PCC) += pcc.o pcc-test.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o processor_throttling.o processor-y += processor_idle.o processor_thermal.o diff --git a/drivers/acpi/pcc-test.c b/drivers/acpi/pcc-test.c new file mode 100644 index 0000000..cb6ee10 --- /dev/null +++ b/drivers/acpi/pcc-test.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2014 Linaro Ltd. + * Author: Ashwin Chaugule ashwin.chaugule@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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. + * + */ + +#include <linux/acpi.h> +#include <linux/io.h> +#include <linux/uaccess.h> +#include <linux/init.h> +#include <linux/cpufreq.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <asm/uaccess.h> + +#include <acpi/actbl.h> + +static u64 pcc_comm_base_addr; /* Returned by the Subspace structure */ +static u64 *comm_base_addr; /* For use after ioremap */ + +extern int get_pcc_comm_channel(u32 ss_idx, u64* addr, int *len); +extern u16 send_pcc_cmd(u8 cmd, u8 sci, u32 ss_idx, u64 * __iomem base_addr); + +/* XXX: The PCC Subspace id is hard coded here only for test purposes. + * In reality it should be parsed from the PCC package as described + * in the PCC client table. e.g. CPC for CPPC + */ +#define PCC_SUBSPACE_IDX 0 +#define CMD_COMPLETE 1 + +enum ppc_cmds { + CMD_READ, + CMD_WRITE, + RESERVED, +}; + +static u64 reg1, reg2; + +static void run_pcc_test_read(void) +{ + u16 pcc_status; + u64 reg1_addr = comm_base_addr + 8; + u64 reg2_addr = comm_base_addr + 16; + + /* READ part of the test */ + pr_info("Sending PCC read req from Channel base addr: %llx\n", pcc_comm_base_addr); + pcc_status = send_pcc_cmd(CMD_READ, 0, PCC_SUBSPACE_IDX, &pcc_comm_base_addr); + if (pcc_status & CMD_COMPLETE) { + pr_info("Read updated values from Platform.\n"); + reg1 = iorea32(®1_addr); + reg2 = ioread32(®2_addr); + pr_info("updated value of reg1:%llx\n", reg1); + pr_info("updated value of reg2:%llx\n", reg2); + } else + pr_err("Failed to read PCC parameters\n"); +} + +static void run_pcc_test_write(void) +{ + u16 pcc_status; + u64 reg1_addr = comm_base_addr + 8; + u64 reg2_addr = comm_base_addr + 16; + + /* WRITE part of the test */ + reg1++; + reg2++; + + iowrite32(reg1, ®1_addr); + iowrite32(reg2, ®2_addr); + + pr_info("Sending PCC write req from Channel base addr: %p\n", comm_base_addr); + pcc_status = send_pcc_cmd(CMD_WRITE, 0, PCC_SUBSPACE_IDX, comm_base_addr); + if (pcc_status & CMD_COMPLETE) + pr_info("OSPM successfully sent PCC read cmd to platform\n"); + else + pr_err("Failed to write PCC parameters. \n"); +} + +void get_pcc_comm(void) +{ + int ret, len; + + ret = get_pcc_comm_channel(PCC_SUBSPACE_IDX, &pcc_comm_base_addr, &len); + + if (ret) { + pr_err("No PCC Communication channel found\n"); + goto out_err; + } + + comm_base_addr = ioremap_nocache(pcc_comm_base_addr, len); + + if (!comm_base_addr) + pr_err("Could not map PCC communicate channel\n"); + + pr_info("Acquired PCC comm channel base addr: %llx\n", pcc_comm_base_addr); + +out_err: + return; +} + +static ssize_t pcc_test_write(struct file *file, const char __user *buf, + size_t count, loff_t *offs) +{ + char ctl[2]; + + if (count != 2 || *offs) + return -EINVAL; + + if (copy_from_user(ctl, buf, count)) + return -EFAULT; + + switch (ctl[0]) { + case '0': + /* PCC read */ + run_pcc_test_read(); + break; + case '1': + /* PCC write */ + run_pcc_test_write(); + break; + case '2': + /* Get PCC channel */ + get_pcc_comm(); + break; + default: + pr_err("Unknown val\n"); + break; + } + + return count; +} + +static int pcc_test_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int pcc_test_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +static const struct file_operations pcc_test_fops = { + .open = pcc_test_open, +// .read = seq_read, + .write = pcc_test_write, + .release = pcc_test_release, +}; + +static int __init pcc_test(void) +{ + struct proc_dir_entry *pe; + + pe = proc_create("pcc_test", 0644, NULL, &pcc_test_fops); + + if (!pe) + return -ENOMEM; + + return 0; +} + +late_initcall(pcc_test);