intel/wifi: Add WGDS ACPI method for Geo Aware SAR

To comply with all relevant bodies throughout the world, SAR settings
take into account the lowest common denominator Tx power settings. This
setup may lead to non-optimal performance when the user location is in a
country that may allow higher power setting. The purpose of Wireless Geo
Delta Settings (WGDS) is to provide offset settings for FCC, Europe,
Japan and Rest of the world. These offsets would be added (by Intel wifi
driver) to the base SAR Tx Power as defined in WRDS and EWRD

BUG=b:65155728
BRANCH=none
TEST=WGDS ACPI table gets created as expected.

Change-Id: I4f602e3f95ff3545db6cc6e428beb9a36abd9296
Signed-off-by: Pratik Prajapati <pratikkumar.v.prajapati@intel.com>
Reviewed-on: https://review.coreboot.org/21098
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
This commit is contained in:
Pratik Prajapati 2017-08-11 14:06:57 -07:00 committed by Patrick Georgi
parent 8919ac726b
commit 7fd1e4b9b1
6 changed files with 191 additions and 28 deletions

View File

@ -11,7 +11,9 @@ config USE_SAR
default n default n
help help
Enable it when wifi driver uses SAR configuration feature. Enable it when wifi driver uses SAR configuration feature.
VPD entry "wifi_sar" is required to support it. VPD entry "wifi_sar" is read to get SAR settings, if its
not found driver may look into CBFS for default settigs.
WIFI_SAR_CBFS is option to enable CBFS lookup.
config SAR_ENABLE config SAR_ENABLE
bool bool
@ -23,6 +25,26 @@ config DSAR_ENABLE
default n default n
depends on USE_SAR depends on USE_SAR
config GEO_SAR_ENABLE
bool
default n
depends on USE_SAR
config WIFI_SAR_CBFS
bool
default n
depends on USE_SAR
help
wifi driver would look for "wifi_sar" vpd key and load SAR settings from
it, if the vpd key is not found then the driver tries to look for sar
settings from CBFS with file name wifi_sar_defaults.hex.
So OEM/ODM can override wifi sar with VPD.
config WIFI_SAR_CBFS_FILEPATH
string "The cbfs file which has WIFI SAR defaults"
depends on WIFI_SAR_CBFS
default "src/mainboard/$(MAINBOARDDIR)/wifi_sar_defaults.hex"
config DSAR_SET_NUM config DSAR_SET_NUM
hex "Number of SAR sets when D-SAR is enabled" hex "Number of SAR sets when D-SAR is enabled"
default 0x3 default 0x3

View File

@ -12,3 +12,7 @@
# #
ramstage-$(CONFIG_DRIVERS_INTEL_WIFI) += wifi.c ramstage-$(CONFIG_DRIVERS_INTEL_WIFI) += wifi.c
cbfs-files-$(CONFIG_WIFI_SAR_CBFS) += wifi_sar_defaults.hex
wifi_sar_defaults.hex-file := $(call strip_quotes,$(CONFIG_WIFI_SAR_CBFS_FILEPATH))
wifi_sar_defaults.hex-type := raw

View File

@ -28,6 +28,9 @@
/* EWRD Domain type */ /* EWRD Domain type */
#define EWRD_DOMAIN_TYPE_WIFI 0x7 #define EWRD_DOMAIN_TYPE_WIFI 0x7
/* WGDS Domain type */
#define WGDS_DOMAIN_TYPE_WIFI 0x7
struct drivers_intel_wifi_config { struct drivers_intel_wifi_config {
unsigned wake; /* Wake pin for ACPI _PRW */ unsigned wake; /* Wake pin for ACPI _PRW */
}; };

View File

@ -2,6 +2,7 @@
* This file is part of the coreboot project. * This file is part of the coreboot project.
* *
* Copyright (C) 2014 Vladimir Serbinenko * Copyright (C) 2014 Vladimir Serbinenko
* Copyright (C) 2018 Intel Corp.
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -75,6 +76,7 @@ static void emit_sar_acpi_structures(void)
{ {
int i, j, package_size; int i, j, package_size;
struct wifi_sar_limits sar_limits; struct wifi_sar_limits sar_limits;
struct wifi_sar_delta_table *wgds;
/* Retrieve the sar limits data */ /* Retrieve the sar limits data */
if (get_wifi_sar_limits(&sar_limits) < 0) { if (get_wifi_sar_limits(&sar_limits) < 0) {
@ -135,6 +137,59 @@ static void emit_sar_acpi_structures(void)
acpigen_write_byte(sar_limits.sar_limit[i][j]); acpigen_write_byte(sar_limits.sar_limit[i][j]);
acpigen_pop_len(); acpigen_pop_len();
acpigen_pop_len(); acpigen_pop_len();
if (!IS_ENABLED(CONFIG_GEO_SAR_ENABLE))
return;
/*
* Name ("WGDS", Package() {
* Revision,
* Package() {
* DomainType, // 0x7:WiFi
* WgdsWiFiSarDeltaGroup1PowerMax1, // Group 1 FCC 2400 Max
* WgdsWiFiSarDeltaGroup1PowerChainA1, // Group 1 FCC 2400 A Offset
* WgdsWiFiSarDeltaGroup1PowerChainB1, // Group 1 FCC 2400 B Offset
* WgdsWiFiSarDeltaGroup1PowerMax2, // Group 1 FCC 5200 Max
* WgdsWiFiSarDeltaGroup1PowerChainA2, // Group 1 FCC 5200 A Offset
* WgdsWiFiSarDeltaGroup1PowerChainB2, // Group 1 FCC 5200 B Offset
* WgdsWiFiSarDeltaGroup2PowerMax1, // Group 2 EC Jap 2400 Max
* WgdsWiFiSarDeltaGroup2PowerChainA1, // Group 2 EC Jap 2400 A Offset
* WgdsWiFiSarDeltaGroup2PowerChainB1, // Group 2 EC Jap 2400 B Offset
* WgdsWiFiSarDeltaGroup2PowerMax2, // Group 2 EC Jap 5200 Max
* WgdsWiFiSarDeltaGroup2PowerChainA2, // Group 2 EC Jap 5200 A Offset
* WgdsWiFiSarDeltaGroup2PowerChainB2, // Group 2 EC Jap 5200 B Offset
* WgdsWiFiSarDeltaGroup3PowerMax1, // Group 3 ROW 2400 Max
* WgdsWiFiSarDeltaGroup3PowerChainA1, // Group 3 ROW 2400 A Offset
* WgdsWiFiSarDeltaGroup3PowerChainB1, // Group 3 ROW 2400 B Offset
* WgdsWiFiSarDeltaGroup3PowerMax2, // Group 3 ROW 5200 Max
* WgdsWiFiSarDeltaGroup3PowerChainA2, // Group 3 ROW 5200 A Offset
* WgdsWiFiSarDeltaGroup3PowerChainB2, // Group 3 ROW 5200 B Offset
* }
* })
*/
wgds = &sar_limits.wgds;
acpigen_write_name("WGDS");
acpigen_write_package(2);
acpigen_write_dword(wgds->version);
/* Emit 'Domain Type' +
* Group specific delta of power ( 6 bytes * NUM_WGDS_SAR_GROUPS )
*/
package_size = sizeof(sar_limits.wgds.group) + 1;
acpigen_write_package(package_size);
acpigen_write_dword(WGDS_DOMAIN_TYPE_WIFI);
for (i = 0; i < SAR_NUM_WGDS_GROUPS; i++) {
acpigen_write_byte(wgds->group[i].power_max_2400mhz);
acpigen_write_byte(wgds->group[i].power_chain_a_2400mhz);
acpigen_write_byte(wgds->group[i].power_chain_b_2400mhz);
acpigen_write_byte(wgds->group[i].power_max_5200mhz);
acpigen_write_byte(wgds->group[i].power_chain_a_5200mhz);
acpigen_write_byte(wgds->group[i].power_chain_b_5200mhz);
}
acpigen_pop_len();
acpigen_pop_len();
} }
static void intel_wifi_fill_ssdt(struct device *dev) static void intel_wifi_fill_ssdt(struct device *dev)

View File

@ -1,7 +1,7 @@
/* /*
* This file is part of the coreboot project. * This file is part of the coreboot project.
* *
* Copyright (C) 2017 Intel Corp. * Copyright (C) 2017-2018 Intel Corp.
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -15,22 +15,42 @@
#ifndef _SAR_H_ #ifndef _SAR_H_
#define _SAR_H_ #define _SAR_H_
#include <compiler.h>
#include <stdint.h> #include <stdint.h>
#define NUM_SAR_LIMITS 4 #define NUM_SAR_LIMITS 4
#define BYTES_PER_SAR_LIMIT 10 #define BYTES_PER_SAR_LIMIT 10
enum {
SAR_FCC,
SAR_EUROPE_JAPAN,
SAR_REST_OF_WORLD,
SAR_NUM_WGDS_GROUPS
};
struct wifi_sar_delta_table {
uint8_t version;
struct {
uint8_t power_max_2400mhz;
uint8_t power_chain_a_2400mhz;
uint8_t power_chain_b_2400mhz;
uint8_t power_max_5200mhz;
uint8_t power_chain_a_5200mhz;
uint8_t power_chain_b_5200mhz;
} __packed group[SAR_NUM_WGDS_GROUPS];
} __packed;
/* Wifi SAR limit table structure */ /* Wifi SAR limit table structure */
struct wifi_sar_limits { struct wifi_sar_limits {
/* Total 4 SAR limit sets, each has 10 bytes */ /* Total 4 SAR limit sets, each has 10 bytes */
uint8_t sar_limit[NUM_SAR_LIMITS][BYTES_PER_SAR_LIMIT]; uint8_t sar_limit[NUM_SAR_LIMITS][BYTES_PER_SAR_LIMIT];
}; struct wifi_sar_delta_table wgds;
} __packed;
/* /*
* Retrieve the SAR limits data from VPD and decode it. * Retrieve the SAR limits data from VPD and decode it.
* sar_limits: Pointer to wifi_sar_limits where the resulted data is stored * sar_limits: Pointer to wifi_sar_limits where the resulted data is stored
* *
* Returns: 0 on success, -1 on errors (The VPD entry doesn't exist, or the * Returns: 0 on success, -1 on errors (The VPD entry doesn't exist, or the
* VPD entry contains non-heximal value.) * VPD entry contains non-heximal value.)
*/ */
int get_wifi_sar_limits(struct wifi_sar_limits *sar_limits); int get_wifi_sar_limits(struct wifi_sar_limits *sar_limits);

View File

@ -1,7 +1,7 @@
/* /*
* This file is part of the coreboot project. * This file is part of the coreboot project.
* *
* Copyright (C) 2017 Intel Corp. * Copyright (C) 2017-2018 Intel Corp.
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -12,6 +12,8 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#include <cbfs.h>
#include <console/console.h> #include <console/console.h>
#include <lib.h> #include <lib.h>
#include <types.h> #include <types.h>
@ -19,44 +21,101 @@
#include <sar.h> #include <sar.h>
#include "cros_vpd.h" #include "cros_vpd.h"
/* Retrieve the wifi SAR limits data from VPD and decode it */ #define WIFI_SAR_CBFS_FILENAME "wifi_sar_defaults.hex"
static int load_sar_file_from_cbfs(void *buf, size_t buffer_size)
{
return cbfs_boot_load_file(WIFI_SAR_CBFS_FILENAME, buf,
buffer_size, CBFS_TYPE_RAW);
}
/* Retrieve the wifi SAR limits data from VPD and decode it
For VPD: key,value pair is in this format
"wifi_sar"=[<WRDD><EWRD>][WGDS]
WIFI SAR data in CBFS file is expected in same format: [<WRDD><EWRD>][WGDS]
[<WRDD><EWRD>] = NUM_SAR_LIMITS * BYTES_PER_SAR_LIMIT bytes.
[WGDS]=[WGDS_VERSION][WGDS_DATA]
For [WGDS_VERSION] 0x00,
[WGDS_DATA] = [GROUP#0][GROUP#1][GROUP#2]
[GROUP#<i>] =
[2.4Ghz Max Allowed][2.4Ghz Chain A Offset]
[2.4Ghz Chain B Offset][5Ghz Max Allowed]
[5Ghz Chain A Offset][5Ghz Chain B Offset]
[GROUP#0] is for FCC
[GROUP#1] is for Europe/Japan
[GROUP#2] is for ROW
*/
int get_wifi_sar_limits(struct wifi_sar_limits *sar_limits) int get_wifi_sar_limits(struct wifi_sar_limits *sar_limits)
{ {
const char *wifi_sar_limit_key = CROS_VPD_WIFI_SAR_NAME; const char *wifi_sar_limit_key = CROS_VPD_WIFI_SAR_NAME;
/* /* cros_vpd_gets() reads in one less than size characters from the VPD
* cros_vpd_gets() reads in one less than size characters from the VPD
* with a terminating null byte ('\0') stored as the last character into * with a terminating null byte ('\0') stored as the last character into
* the buffer, thus the increasing by 1 for buffer_size. * the buffer, thus the increasing by 1 for buffer_size. */
*/
const size_t buffer_size = (sizeof(struct wifi_sar_limits) / const size_t buffer_size = (sizeof(struct wifi_sar_limits) /
sizeof(uint8_t)) * 2 + 1; sizeof(uint8_t)) * 2 + 1;
char wifi_sar_limit_str[buffer_size]; char wifi_sar_limit_str[buffer_size];
uint8_t bin_buffer[sizeof(struct wifi_sar_limits)]; uint8_t bin_buffer[sizeof(struct wifi_sar_limits)];
int i; size_t sar_cbfs_len, sar_expected_len, bin_buff_adjusted_size;
/* keep it backward compatible. Some older platform are shipping
without GEO SAR and so older wifi_sar VPD key */
sar_expected_len = buffer_size;
bin_buff_adjusted_size = sizeof(struct wifi_sar_limits);
if (!IS_ENABLED(CONFIG_GEO_SAR_ENABLE)) {
sar_expected_len = buffer_size -
sizeof(struct wifi_sar_delta_table) *
sizeof(uint8_t) * 2;
bin_buff_adjusted_size = sizeof(struct wifi_sar_limits) -
sizeof(struct wifi_sar_delta_table);
}
/* Try to read the SAR limit entry from VPD */ /* Try to read the SAR limit entry from VPD */
if (!cros_vpd_gets(wifi_sar_limit_key, wifi_sar_limit_str, if (!cros_vpd_gets(wifi_sar_limit_key, wifi_sar_limit_str,
ARRAY_SIZE(wifi_sar_limit_str))) { buffer_size)) {
printk(BIOS_ERR, printk(BIOS_ERR, "Error: Could not locate '%s' in VPD.\n",
"Error: Could not locate '%s' in VPD\n", wifi_sar_limit_key);
wifi_sar_limit_key);
return -1; if (!IS_ENABLED(CONFIG_WIFI_SAR_CBFS))
return -1;
printk(BIOS_DEBUG, "Checking CBFS for default SAR values\n");
sar_cbfs_len = load_sar_file_from_cbfs(
(void *) wifi_sar_limit_str,
sar_expected_len);
if (sar_cbfs_len != sar_expected_len) {
printk(BIOS_ERR, "%s has bad len in CBFS\n",
WIFI_SAR_CBFS_FILENAME);
return -1;
}
} else {
/* VPD key "wifi_sar" found. strlen is checked with addition of
* 1 as we have created buffer size 1 char larger for the reason
* mentioned at start of this function itself */
if (strlen(wifi_sar_limit_str) + 1 != sar_expected_len) {
printk(BIOS_ERR, "WIFI SAR key has bad len in VPD\n");
return -1;
}
} }
printk(BIOS_DEBUG, "VPD wifi_sar = %s\n", wifi_sar_limit_str);
/* Decode the heximal encoded string to binary values */ /* Decode the heximal encoded string to binary values */
if (hexstrtobin(wifi_sar_limit_str, bin_buffer, if (hexstrtobin(wifi_sar_limit_str, bin_buffer, bin_buff_adjusted_size)
sizeof(struct wifi_sar_limits)) < bin_buff_adjusted_size) {
< sizeof(struct wifi_sar_limits)) { printk(BIOS_ERR, "Error: wifi_sar contains non-hex value!\n");
printk(BIOS_ERR,
"Error: VPD wifi_sar contains non-heximal value!\n");
return -1; return -1;
} }
/* Fill the sar_limits structure with the decoded data */ memset(sar_limits, 0, sizeof(*sar_limits));
for (i = 0; i < NUM_SAR_LIMITS; i++) memcpy(sar_limits, bin_buffer, bin_buff_adjusted_size);
memcpy(sar_limits->sar_limit[i],
&bin_buffer[BYTES_PER_SAR_LIMIT * i],
BYTES_PER_SAR_LIMIT);
return 0; return 0;
} }