提交 0d573c6a 编写于 作者: R Rafael J. Wysocki

Merge branches 'acpi-x86', 'acpi-cppc' and 'acpi-soc'

* acpi-x86:
  x86: ACPI: make variable names clearer in acpi_parse_madt_lapic_entries()
  x86: ACPI: remove extraneous white space after semicolon

* acpi-cppc:
  ACPI / CPPC: Support PCC with interrupt flag
  ACPI / CPPC: Add prefix cppc to cpudata structure name
  ACPI / CPPC: Add support for functional fixed hardware address
  ACPI / CPPC: Don't return on CPPC probe failure
  ACPI / CPPC: Allow build with ACPI_CPU_FREQ_PSS config
  ACPI / CPPC: check for error bit in PCC status field
  ACPI / CPPC: move all PCC related information into pcc_data
  ACPI / CPPC: add sysfs support to compute delivered performance
  ACPI / CPPC: set a non-zero value for transition_latency
  ACPI / CPPC: support for batching CPPC requests
  ACPI / CPPC: acquire pcc_lock only while accessing PCC subspace
  ACPI / CPPC: restructure read/writes for efficient sys mapped reg ops
  mailbox: pcc: Support HW-Reduced Communication Subspace type 2

* acpi-soc:
  ACPI / APD: constify local structures
  ACPI / APD: Add device HID for Vulcan SPI controller
obj-$(CONFIG_ACPI) += boot.o obj-$(CONFIG_ACPI) += boot.o
obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o
obj-$(CONFIG_ACPI_APEI) += apei.o obj-$(CONFIG_ACPI_APEI) += apei.o
obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_msr.o
ifneq ($(CONFIG_ACPI_PROCESSOR),) ifneq ($(CONFIG_ACPI_PROCESSOR),)
obj-y += cstate.o obj-y += cstate.o
......
...@@ -1031,8 +1031,8 @@ static int __init acpi_parse_madt_lapic_entries(void) ...@@ -1031,8 +1031,8 @@ static int __init acpi_parse_madt_lapic_entries(void)
return ret; return ret;
} }
x2count = madt_proc[0].count; count = madt_proc[0].count;
count = madt_proc[1].count; x2count = madt_proc[1].count;
} }
if (!count && !x2count) { if (!count && !x2count) {
printk(KERN_ERR PREFIX "No LAPIC entries present\n"); printk(KERN_ERR PREFIX "No LAPIC entries present\n");
......
/*
* cppc_msr.c: MSR Interface for CPPC
* Copyright (c) 2016, Intel Corporation.
*
* 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.
*
*/
#include <acpi/cppc_acpi.h>
#include <asm/msr.h>
/* Refer to drivers/acpi/cppc_acpi.c for the description of functions */
bool cpc_ffh_supported(void)
{
return true;
}
int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
{
int err;
err = rdmsrl_safe_on_cpu(cpunum, reg->address, val);
if (!err) {
u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
reg->bit_offset);
*val &= mask;
*val >>= reg->bit_offset;
}
return err;
}
int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
{
u64 rd_val;
int err;
err = rdmsrl_safe_on_cpu(cpunum, reg->address, &rd_val);
if (!err) {
u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
reg->bit_offset);
val <<= reg->bit_offset;
val &= mask;
rd_val &= ~mask;
rd_val |= val;
err = wrmsrl_safe_on_cpu(cpunum, reg->address, rd_val);
}
return err;
}
...@@ -227,7 +227,6 @@ config ACPI_MCFG ...@@ -227,7 +227,6 @@ config ACPI_MCFG
config ACPI_CPPC_LIB config ACPI_CPPC_LIB
bool bool
depends on ACPI_PROCESSOR depends on ACPI_PROCESSOR
depends on !ACPI_CPU_FREQ_PSS
select MAILBOX select MAILBOX
select PCC select PCC
help help
......
...@@ -72,7 +72,7 @@ static int acpi_apd_setup(struct apd_private_data *pdata) ...@@ -72,7 +72,7 @@ static int acpi_apd_setup(struct apd_private_data *pdata)
} }
#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE #ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
static struct apd_device_desc cz_i2c_desc = { static const struct apd_device_desc cz_i2c_desc = {
.setup = acpi_apd_setup, .setup = acpi_apd_setup,
.fixed_clk_rate = 133000000, .fixed_clk_rate = 133000000,
}; };
...@@ -84,7 +84,7 @@ static struct property_entry uart_properties[] = { ...@@ -84,7 +84,7 @@ static struct property_entry uart_properties[] = {
{ }, { },
}; };
static struct apd_device_desc cz_uart_desc = { static const struct apd_device_desc cz_uart_desc = {
.setup = acpi_apd_setup, .setup = acpi_apd_setup,
.fixed_clk_rate = 48000000, .fixed_clk_rate = 48000000,
.properties = uart_properties, .properties = uart_properties,
...@@ -92,10 +92,15 @@ static struct apd_device_desc cz_uart_desc = { ...@@ -92,10 +92,15 @@ static struct apd_device_desc cz_uart_desc = {
#endif #endif
#ifdef CONFIG_ARM64 #ifdef CONFIG_ARM64
static struct apd_device_desc xgene_i2c_desc = { static const struct apd_device_desc xgene_i2c_desc = {
.setup = acpi_apd_setup, .setup = acpi_apd_setup,
.fixed_clk_rate = 100000000, .fixed_clk_rate = 100000000,
}; };
static const struct apd_device_desc vulcan_spi_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 133000000,
};
#endif #endif
#else #else
...@@ -164,6 +169,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = { ...@@ -164,6 +169,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = {
#endif #endif
#ifdef CONFIG_ARM64 #ifdef CONFIG_ARM64
{ "APMC0D0F", APD_ADDR(xgene_i2c_desc) }, { "APMC0D0F", APD_ADDR(xgene_i2c_desc) },
{ "BRCM900D", APD_ADDR(vulcan_spi_desc) },
#endif #endif
{ } { }
}; };
......
此差异已折叠。
...@@ -245,8 +245,8 @@ static int __acpi_processor_start(struct acpi_device *device) ...@@ -245,8 +245,8 @@ static int __acpi_processor_start(struct acpi_device *device)
return 0; return 0;
result = acpi_cppc_processor_probe(pr); result = acpi_cppc_processor_probe(pr);
if (result) if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS))
return -ENODEV; dev_warn(&device->dev, "CPPC data invalid or not present\n");
if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
acpi_processor_power_init(pr); acpi_processor_power_init(pr);
......
...@@ -30,13 +30,13 @@ ...@@ -30,13 +30,13 @@
* performance capabilities, desired performance level * performance capabilities, desired performance level
* requested etc. * requested etc.
*/ */
static struct cpudata **all_cpu_data; static struct cppc_cpudata **all_cpu_data;
static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int target_freq,
unsigned int relation) unsigned int relation)
{ {
struct cpudata *cpu; struct cppc_cpudata *cpu;
struct cpufreq_freqs freqs; struct cpufreq_freqs freqs;
int ret = 0; int ret = 0;
...@@ -66,7 +66,7 @@ static int cppc_verify_policy(struct cpufreq_policy *policy) ...@@ -66,7 +66,7 @@ static int cppc_verify_policy(struct cpufreq_policy *policy)
static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy) static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy)
{ {
int cpu_num = policy->cpu; int cpu_num = policy->cpu;
struct cpudata *cpu = all_cpu_data[cpu_num]; struct cppc_cpudata *cpu = all_cpu_data[cpu_num];
int ret; int ret;
cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf; cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf;
...@@ -79,7 +79,7 @@ static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy) ...@@ -79,7 +79,7 @@ static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy)
static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
{ {
struct cpudata *cpu; struct cppc_cpudata *cpu;
unsigned int cpu_num = policy->cpu; unsigned int cpu_num = policy->cpu;
int ret = 0; int ret = 0;
...@@ -98,6 +98,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) ...@@ -98,6 +98,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
policy->max = cpu->perf_caps.highest_perf; policy->max = cpu->perf_caps.highest_perf;
policy->cpuinfo.min_freq = policy->min; policy->cpuinfo.min_freq = policy->min;
policy->cpuinfo.max_freq = policy->max; policy->cpuinfo.max_freq = policy->max;
policy->cpuinfo.transition_latency = cppc_get_transition_latency(cpu_num);
policy->shared_type = cpu->shared_type; policy->shared_type = cpu->shared_type;
if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
...@@ -134,7 +135,7 @@ static struct cpufreq_driver cppc_cpufreq_driver = { ...@@ -134,7 +135,7 @@ static struct cpufreq_driver cppc_cpufreq_driver = {
static int __init cppc_cpufreq_init(void) static int __init cppc_cpufreq_init(void)
{ {
int i, ret = 0; int i, ret = 0;
struct cpudata *cpu; struct cppc_cpudata *cpu;
if (acpi_disabled) if (acpi_disabled)
return -ENODEV; return -ENODEV;
...@@ -144,7 +145,7 @@ static int __init cppc_cpufreq_init(void) ...@@ -144,7 +145,7 @@ static int __init cppc_cpufreq_init(void)
return -ENOMEM; return -ENOMEM;
for_each_possible_cpu(i) { for_each_possible_cpu(i) {
all_cpu_data[i] = kzalloc(sizeof(struct cpudata), GFP_KERNEL); all_cpu_data[i] = kzalloc(sizeof(struct cppc_cpudata), GFP_KERNEL);
if (!all_cpu_data[i]) if (!all_cpu_data[i])
goto out; goto out;
...@@ -175,7 +176,7 @@ static int __init cppc_cpufreq_init(void) ...@@ -175,7 +176,7 @@ static int __init cppc_cpufreq_init(void)
static void __exit cppc_cpufreq_exit(void) static void __exit cppc_cpufreq_exit(void)
{ {
struct cpudata *cpu; struct cppc_cpudata *cpu;
int i; int i;
cpufreq_unregister_driver(&cppc_cpufreq_driver); cpufreq_unregister_driver(&cppc_cpufreq_driver);
......
...@@ -59,6 +59,7 @@ ...@@ -59,6 +59,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mailbox_controller.h> #include <linux/mailbox_controller.h>
...@@ -68,11 +69,16 @@ ...@@ -68,11 +69,16 @@
#include "mailbox.h" #include "mailbox.h"
#define MAX_PCC_SUBSPACES 256 #define MAX_PCC_SUBSPACES 256
#define MBOX_IRQ_NAME "pcc-mbox"
static struct mbox_chan *pcc_mbox_channels; static struct mbox_chan *pcc_mbox_channels;
/* Array of cached virtual address for doorbell registers */ /* Array of cached virtual address for doorbell registers */
static void __iomem **pcc_doorbell_vaddr; static void __iomem **pcc_doorbell_vaddr;
/* Array of cached virtual address for doorbell ack registers */
static void __iomem **pcc_doorbell_ack_vaddr;
/* Array of doorbell interrupts */
static int *pcc_doorbell_irq;
static struct mbox_controller pcc_mbox_ctrl = {}; static struct mbox_controller pcc_mbox_ctrl = {};
/** /**
...@@ -91,6 +97,132 @@ static struct mbox_chan *get_pcc_channel(int id) ...@@ -91,6 +97,132 @@ static struct mbox_chan *get_pcc_channel(int id)
return &pcc_mbox_channels[id]; return &pcc_mbox_channels[id];
} }
/*
* PCC can be used with perf critical drivers such as CPPC
* So it makes sense to locally cache the virtual address and
* use it to read/write to PCC registers such as doorbell register
*
* The below read_register and write_registers are used to read and
* write from perf critical registers such as PCC doorbell register
*/
static int read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width)
{
int ret_val = 0;
switch (bit_width) {
case 8:
*val = readb(vaddr);
break;
case 16:
*val = readw(vaddr);
break;
case 32:
*val = readl(vaddr);
break;
case 64:
*val = readq(vaddr);
break;
default:
pr_debug("Error: Cannot read register of %u bit width",
bit_width);
ret_val = -EFAULT;
break;
}
return ret_val;
}
static int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width)
{
int ret_val = 0;
switch (bit_width) {
case 8:
writeb(val, vaddr);
break;
case 16:
writew(val, vaddr);
break;
case 32:
writel(val, vaddr);
break;
case 64:
writeq(val, vaddr);
break;
default:
pr_debug("Error: Cannot write register of %u bit width",
bit_width);
ret_val = -EFAULT;
break;
}
return ret_val;
}
/**
* pcc_map_interrupt - Map a PCC subspace GSI to a linux IRQ number
* @interrupt: GSI number.
* @flags: interrupt flags
*
* Returns: a valid linux IRQ number on success
* 0 or -EINVAL on failure
*/
static int pcc_map_interrupt(u32 interrupt, u32 flags)
{
int trigger, polarity;
if (!interrupt)
return 0;
trigger = (flags & ACPI_PCCT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE
: ACPI_LEVEL_SENSITIVE;
polarity = (flags & ACPI_PCCT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW
: ACPI_ACTIVE_HIGH;
return acpi_register_gsi(NULL, interrupt, trigger, polarity);
}
/**
* pcc_mbox_irq - PCC mailbox interrupt handler
*/
static irqreturn_t pcc_mbox_irq(int irq, void *p)
{
struct acpi_generic_address *doorbell_ack;
struct acpi_pcct_hw_reduced *pcct_ss;
struct mbox_chan *chan = p;
u64 doorbell_ack_preserve;
u64 doorbell_ack_write;
u64 doorbell_ack_val;
int ret;
pcct_ss = chan->con_priv;
mbox_chan_received_data(chan, NULL);
if (pcct_ss->header.type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
struct acpi_pcct_hw_reduced_type2 *pcct2_ss = chan->con_priv;
u32 id = chan - pcc_mbox_channels;
doorbell_ack = &pcct2_ss->doorbell_ack_register;
doorbell_ack_preserve = pcct2_ss->ack_preserve_mask;
doorbell_ack_write = pcct2_ss->ack_write_mask;
ret = read_register(pcc_doorbell_ack_vaddr[id],
&doorbell_ack_val,
doorbell_ack->bit_width);
if (ret)
return IRQ_NONE;
ret = write_register(pcc_doorbell_ack_vaddr[id],
(doorbell_ack_val & doorbell_ack_preserve)
| doorbell_ack_write,
doorbell_ack->bit_width);
if (ret)
return IRQ_NONE;
}
return IRQ_HANDLED;
}
/** /**
* pcc_mbox_request_channel - PCC clients call this function to * pcc_mbox_request_channel - PCC clients call this function to
* request a pointer to their PCC subspace, from which they * request a pointer to their PCC subspace, from which they
...@@ -135,6 +267,18 @@ struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, ...@@ -135,6 +267,18 @@ struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl,
if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone) if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone)
chan->txdone_method |= TXDONE_BY_ACK; chan->txdone_method |= TXDONE_BY_ACK;
if (pcc_doorbell_irq[subspace_id] > 0) {
int rc;
rc = devm_request_irq(dev, pcc_doorbell_irq[subspace_id],
pcc_mbox_irq, 0, MBOX_IRQ_NAME, chan);
if (unlikely(rc)) {
dev_err(dev, "failed to register PCC interrupt %d\n",
pcc_doorbell_irq[subspace_id]);
chan = ERR_PTR(rc);
}
}
spin_unlock_irqrestore(&chan->lock, flags); spin_unlock_irqrestore(&chan->lock, flags);
return chan; return chan;
...@@ -149,80 +293,30 @@ EXPORT_SYMBOL_GPL(pcc_mbox_request_channel); ...@@ -149,80 +293,30 @@ EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
*/ */
void pcc_mbox_free_channel(struct mbox_chan *chan) void pcc_mbox_free_channel(struct mbox_chan *chan)
{ {
u32 id = chan - pcc_mbox_channels;
unsigned long flags; unsigned long flags;
if (!chan || !chan->cl) if (!chan || !chan->cl)
return; return;
if (id >= pcc_mbox_ctrl.num_chans) {
pr_debug("pcc_mbox_free_channel: Invalid mbox_chan passed\n");
return;
}
spin_lock_irqsave(&chan->lock, flags); spin_lock_irqsave(&chan->lock, flags);
chan->cl = NULL; chan->cl = NULL;
chan->active_req = NULL; chan->active_req = NULL;
if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK)) if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK))
chan->txdone_method = TXDONE_BY_POLL; chan->txdone_method = TXDONE_BY_POLL;
if (pcc_doorbell_irq[id] > 0)
devm_free_irq(chan->mbox->dev, pcc_doorbell_irq[id], chan);
spin_unlock_irqrestore(&chan->lock, flags); spin_unlock_irqrestore(&chan->lock, flags);
} }
EXPORT_SYMBOL_GPL(pcc_mbox_free_channel); EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);
/*
* PCC can be used with perf critical drivers such as CPPC
* So it makes sense to locally cache the virtual address and
* use it to read/write to PCC registers such as doorbell register
*
* The below read_register and write_registers are used to read and
* write from perf critical registers such as PCC doorbell register
*/
static int read_register(void __iomem *vaddr, u64 *val, unsigned int bit_width)
{
int ret_val = 0;
switch (bit_width) {
case 8:
*val = readb(vaddr);
break;
case 16:
*val = readw(vaddr);
break;
case 32:
*val = readl(vaddr);
break;
case 64:
*val = readq(vaddr);
break;
default:
pr_debug("Error: Cannot read register of %u bit width",
bit_width);
ret_val = -EFAULT;
break;
}
return ret_val;
}
static int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width)
{
int ret_val = 0;
switch (bit_width) {
case 8:
writeb(val, vaddr);
break;
case 16:
writew(val, vaddr);
break;
case 32:
writel(val, vaddr);
break;
case 64:
writeq(val, vaddr);
break;
default:
pr_debug("Error: Cannot write register of %u bit width",
bit_width);
ret_val = -EFAULT;
break;
}
return ret_val;
}
/** /**
* pcc_send_data - Called from Mailbox Controller code. Used * pcc_send_data - Called from Mailbox Controller code. Used
...@@ -296,8 +390,10 @@ static int parse_pcc_subspace(struct acpi_subtable_header *header, ...@@ -296,8 +390,10 @@ static int parse_pcc_subspace(struct acpi_subtable_header *header,
if (pcc_mbox_ctrl.num_chans <= MAX_PCC_SUBSPACES) { if (pcc_mbox_ctrl.num_chans <= MAX_PCC_SUBSPACES) {
pcct_ss = (struct acpi_pcct_hw_reduced *) header; pcct_ss = (struct acpi_pcct_hw_reduced *) header;
if (pcct_ss->header.type != if ((pcct_ss->header.type !=
ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE) { ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE)
&& (pcct_ss->header.type !=
ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2)) {
pr_err("Incorrect PCC Subspace type detected\n"); pr_err("Incorrect PCC Subspace type detected\n");
return -EINVAL; return -EINVAL;
} }
...@@ -306,6 +402,43 @@ static int parse_pcc_subspace(struct acpi_subtable_header *header, ...@@ -306,6 +402,43 @@ static int parse_pcc_subspace(struct acpi_subtable_header *header,
return 0; return 0;
} }
/**
* pcc_parse_subspace_irq - Parse the PCC IRQ and PCC ACK register
* There should be one entry per PCC client.
* @id: PCC subspace index.
* @pcct_ss: Pointer to the ACPI subtable header under the PCCT.
*
* Return: 0 for Success, else errno.
*
* This gets called for each entry in the PCC table.
*/
static int pcc_parse_subspace_irq(int id,
struct acpi_pcct_hw_reduced *pcct_ss)
{
pcc_doorbell_irq[id] = pcc_map_interrupt(pcct_ss->doorbell_interrupt,
(u32)pcct_ss->flags);
if (pcc_doorbell_irq[id] <= 0) {
pr_err("PCC GSI %d not registered\n",
pcct_ss->doorbell_interrupt);
return -EINVAL;
}
if (pcct_ss->header.type
== ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
struct acpi_pcct_hw_reduced_type2 *pcct2_ss = (void *)pcct_ss;
pcc_doorbell_ack_vaddr[id] = acpi_os_ioremap(
pcct2_ss->doorbell_ack_register.address,
pcct2_ss->doorbell_ack_register.bit_width / 8);
if (!pcc_doorbell_ack_vaddr[id]) {
pr_err("Failed to ioremap PCC ACK register\n");
return -ENOMEM;
}
}
return 0;
}
/** /**
* acpi_pcc_probe - Parse the ACPI tree for the PCCT. * acpi_pcc_probe - Parse the ACPI tree for the PCCT.
* *
...@@ -316,7 +449,9 @@ static int __init acpi_pcc_probe(void) ...@@ -316,7 +449,9 @@ static int __init acpi_pcc_probe(void)
acpi_size pcct_tbl_header_size; acpi_size pcct_tbl_header_size;
struct acpi_table_header *pcct_tbl; struct acpi_table_header *pcct_tbl;
struct acpi_subtable_header *pcct_entry; struct acpi_subtable_header *pcct_entry;
int count, i; struct acpi_table_pcct *acpi_pcct_tbl;
int count, i, rc;
int sum = 0;
acpi_status status = AE_OK; acpi_status status = AE_OK;
/* Search for PCCT */ /* Search for PCCT */
...@@ -333,37 +468,66 @@ static int __init acpi_pcc_probe(void) ...@@ -333,37 +468,66 @@ static int __init acpi_pcc_probe(void)
sizeof(struct acpi_table_pcct), sizeof(struct acpi_table_pcct),
ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE, ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE,
parse_pcc_subspace, MAX_PCC_SUBSPACES); parse_pcc_subspace, MAX_PCC_SUBSPACES);
sum += (count > 0) ? count : 0;
count = acpi_table_parse_entries(ACPI_SIG_PCCT,
sizeof(struct acpi_table_pcct),
ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2,
parse_pcc_subspace, MAX_PCC_SUBSPACES);
sum += (count > 0) ? count : 0;
if (count <= 0) { if (sum == 0 || sum >= MAX_PCC_SUBSPACES) {
pr_err("Error parsing PCC subspaces from PCCT\n"); pr_err("Error parsing PCC subspaces from PCCT\n");
return -EINVAL; return -EINVAL;
} }
pcc_mbox_channels = kzalloc(sizeof(struct mbox_chan) * pcc_mbox_channels = kzalloc(sizeof(struct mbox_chan) *
count, GFP_KERNEL); sum, GFP_KERNEL);
if (!pcc_mbox_channels) { if (!pcc_mbox_channels) {
pr_err("Could not allocate space for PCC mbox channels\n"); pr_err("Could not allocate space for PCC mbox channels\n");
return -ENOMEM; return -ENOMEM;
} }
pcc_doorbell_vaddr = kcalloc(count, sizeof(void *), GFP_KERNEL); pcc_doorbell_vaddr = kcalloc(sum, sizeof(void *), GFP_KERNEL);
if (!pcc_doorbell_vaddr) { if (!pcc_doorbell_vaddr) {
kfree(pcc_mbox_channels); rc = -ENOMEM;
return -ENOMEM; goto err_free_mbox;
}
pcc_doorbell_ack_vaddr = kcalloc(sum, sizeof(void *), GFP_KERNEL);
if (!pcc_doorbell_ack_vaddr) {
rc = -ENOMEM;
goto err_free_db_vaddr;
}
pcc_doorbell_irq = kcalloc(sum, sizeof(int), GFP_KERNEL);
if (!pcc_doorbell_irq) {
rc = -ENOMEM;
goto err_free_db_ack_vaddr;
} }
/* Point to the first PCC subspace entry */ /* Point to the first PCC subspace entry */
pcct_entry = (struct acpi_subtable_header *) ( pcct_entry = (struct acpi_subtable_header *) (
(unsigned long) pcct_tbl + sizeof(struct acpi_table_pcct)); (unsigned long) pcct_tbl + sizeof(struct acpi_table_pcct));
for (i = 0; i < count; i++) { acpi_pcct_tbl = (struct acpi_table_pcct *) pcct_tbl;
if (acpi_pcct_tbl->flags & ACPI_PCCT_DOORBELL)
pcc_mbox_ctrl.txdone_irq = true;
for (i = 0; i < sum; i++) {
struct acpi_generic_address *db_reg; struct acpi_generic_address *db_reg;
struct acpi_pcct_hw_reduced *pcct_ss; struct acpi_pcct_hw_reduced *pcct_ss;
pcc_mbox_channels[i].con_priv = pcct_entry; pcc_mbox_channels[i].con_priv = pcct_entry;
pcct_ss = (struct acpi_pcct_hw_reduced *) pcct_entry;
if (pcc_mbox_ctrl.txdone_irq) {
rc = pcc_parse_subspace_irq(i, pcct_ss);
if (rc < 0)
goto err;
}
/* If doorbell is in system memory cache the virt address */ /* If doorbell is in system memory cache the virt address */
pcct_ss = (struct acpi_pcct_hw_reduced *)pcct_entry;
db_reg = &pcct_ss->doorbell_register; db_reg = &pcct_ss->doorbell_register;
if (db_reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) if (db_reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
pcc_doorbell_vaddr[i] = acpi_os_ioremap(db_reg->address, pcc_doorbell_vaddr[i] = acpi_os_ioremap(db_reg->address,
...@@ -372,11 +536,21 @@ static int __init acpi_pcc_probe(void) ...@@ -372,11 +536,21 @@ static int __init acpi_pcc_probe(void)
((unsigned long) pcct_entry + pcct_entry->length); ((unsigned long) pcct_entry + pcct_entry->length);
} }
pcc_mbox_ctrl.num_chans = count; pcc_mbox_ctrl.num_chans = sum;
pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl.num_chans); pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl.num_chans);
return 0; return 0;
err:
kfree(pcc_doorbell_irq);
err_free_db_ack_vaddr:
kfree(pcc_doorbell_ack_vaddr);
err_free_db_vaddr:
kfree(pcc_doorbell_vaddr);
err_free_mbox:
kfree(pcc_mbox_channels);
return rc;
} }
/** /**
......
...@@ -24,7 +24,9 @@ ...@@ -24,7 +24,9 @@
#define CPPC_NUM_ENT 21 #define CPPC_NUM_ENT 21
#define CPPC_REV 2 #define CPPC_REV 2
#define PCC_CMD_COMPLETE 1 #define PCC_CMD_COMPLETE_MASK (1 << 0)
#define PCC_ERROR_MASK (1 << 2)
#define MAX_CPC_REG_ENT 19 #define MAX_CPC_REG_ENT 19
/* CPPC specific PCC commands. */ /* CPPC specific PCC commands. */
...@@ -49,6 +51,7 @@ struct cpc_reg { ...@@ -49,6 +51,7 @@ struct cpc_reg {
*/ */
struct cpc_register_resource { struct cpc_register_resource {
acpi_object_type type; acpi_object_type type;
u64 __iomem *sys_mem_vaddr;
union { union {
struct cpc_reg reg; struct cpc_reg reg;
u64 int_value; u64 int_value;
...@@ -60,8 +63,11 @@ struct cpc_desc { ...@@ -60,8 +63,11 @@ struct cpc_desc {
int num_entries; int num_entries;
int version; int version;
int cpu_id; int cpu_id;
int write_cmd_status;
int write_cmd_id;
struct cpc_register_resource cpc_regs[MAX_CPC_REG_ENT]; struct cpc_register_resource cpc_regs[MAX_CPC_REG_ENT];
struct acpi_psd_package domain_info; struct acpi_psd_package domain_info;
struct kobject kobj;
}; };
/* These are indexes into the per-cpu cpc_regs[]. Order is important. */ /* These are indexes into the per-cpu cpc_regs[]. Order is important. */
...@@ -96,7 +102,6 @@ enum cppc_regs { ...@@ -96,7 +102,6 @@ enum cppc_regs {
struct cppc_perf_caps { struct cppc_perf_caps {
u32 highest_perf; u32 highest_perf;
u32 nominal_perf; u32 nominal_perf;
u32 reference_perf;
u32 lowest_perf; u32 lowest_perf;
}; };
...@@ -108,13 +113,13 @@ struct cppc_perf_ctrls { ...@@ -108,13 +113,13 @@ struct cppc_perf_ctrls {
struct cppc_perf_fb_ctrs { struct cppc_perf_fb_ctrs {
u64 reference; u64 reference;
u64 prev_reference;
u64 delivered; u64 delivered;
u64 prev_delivered; u64 reference_perf;
u64 ctr_wrap_time;
}; };
/* Per CPU container for runtime CPPC management. */ /* Per CPU container for runtime CPPC management. */
struct cpudata { struct cppc_cpudata {
int cpu; int cpu;
struct cppc_perf_caps perf_caps; struct cppc_perf_caps perf_caps;
struct cppc_perf_ctrls perf_ctrls; struct cppc_perf_ctrls perf_ctrls;
...@@ -127,6 +132,7 @@ struct cpudata { ...@@ -127,6 +132,7 @@ struct cpudata {
extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs); extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs);
extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls); extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls);
extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps); extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps);
extern int acpi_get_psd_map(struct cpudata **); extern int acpi_get_psd_map(struct cppc_cpudata **);
extern unsigned int cppc_get_transition_latency(int cpu);
#endif /* _CPPC_ACPI_H*/ #endif /* _CPPC_ACPI_H*/
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册