1 /****************************************************************************** 2 * 3 * This file is provided under a dual BSD/GPLv2 license. When using or 4 * redistributing this file, you may do so under either license. 5 * 6 * GPL LICENSE SUMMARY 7 * 8 * Copyright(c) 2017 Intel Deutschland GmbH 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of version 2 of the GNU General Public License as 12 * published by the Free Software Foundation. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; 21 * 22 * The full GNU General Public License is included in this distribution 23 * in the file called COPYING. 24 * 25 * Contact Information: 26 * Intel Linux Wireless <linuxwifi@intel.com> 27 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 28 * 29 * BSD LICENSE 30 * 31 * Copyright(c) 2017 Intel Deutschland GmbH 32 * All rights reserved. 33 * 34 * Redistribution and use in source and binary forms, with or without 35 * modification, are permitted provided that the following conditions 36 * are met: 37 * 38 * * Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * * Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in 42 * the documentation and/or other materials provided with the 43 * distribution. 44 * * Neither the name Intel Corporation nor the names of its 45 * contributors may be used to endorse or promote products derived 46 * from this software without specific prior written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 49 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 50 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 51 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 52 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 53 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 54 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 55 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 56 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 57 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 58 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 59 * 60 *****************************************************************************/ 61 62 #include "iwl-drv.h" 63 #include "iwl-debug.h" 64 #include "acpi.h" 65 66 void *iwl_acpi_get_object(struct device *dev, acpi_string method) 67 { 68 acpi_handle root_handle; 69 acpi_handle handle; 70 struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; 71 acpi_status status; 72 73 root_handle = ACPI_HANDLE(dev); 74 if (!root_handle) { 75 IWL_DEBUG_DEV_RADIO(dev, 76 "Could not retrieve root port ACPI handle\n"); 77 return ERR_PTR(-ENOENT); 78 } 79 80 /* Get the method's handle */ 81 status = acpi_get_handle(root_handle, method, &handle); 82 if (ACPI_FAILURE(status)) { 83 IWL_DEBUG_DEV_RADIO(dev, "%s method not found\n", method); 84 return ERR_PTR(-ENOENT); 85 } 86 87 /* Call the method with no arguments */ 88 status = acpi_evaluate_object(handle, NULL, NULL, &buf); 89 if (ACPI_FAILURE(status)) { 90 IWL_DEBUG_DEV_RADIO(dev, "%s invocation failed (0x%x)\n", 91 method, status); 92 return ERR_PTR(-ENOENT); 93 } 94 95 return buf.pointer; 96 } 97 IWL_EXPORT_SYMBOL(iwl_acpi_get_object); 98 99 union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev, 100 union acpi_object *data, 101 int data_size) 102 { 103 int i; 104 union acpi_object *wifi_pkg; 105 106 /* 107 * We need at least one entry in the wifi package that 108 * describes the domain, and one more entry, otherwise there's 109 * no point in reading it. 110 */ 111 if (WARN_ON_ONCE(data_size < 2)) 112 return ERR_PTR(-EINVAL); 113 114 /* 115 * We need at least two packages, one for the revision and one 116 * for the data itself. Also check that the revision is valid 117 * (i.e. it is an integer set to 0). 118 */ 119 if (data->type != ACPI_TYPE_PACKAGE || 120 data->package.count < 2 || 121 data->package.elements[0].type != ACPI_TYPE_INTEGER || 122 data->package.elements[0].integer.value != 0) { 123 IWL_DEBUG_DEV_RADIO(dev, "Unsupported packages structure\n"); 124 return ERR_PTR(-EINVAL); 125 } 126 127 /* loop through all the packages to find the one for WiFi */ 128 for (i = 1; i < data->package.count; i++) { 129 union acpi_object *domain; 130 131 wifi_pkg = &data->package.elements[i]; 132 133 /* skip entries that are not a package with the right size */ 134 if (wifi_pkg->type != ACPI_TYPE_PACKAGE || 135 wifi_pkg->package.count != data_size) 136 continue; 137 138 domain = &wifi_pkg->package.elements[0]; 139 if (domain->type == ACPI_TYPE_INTEGER && 140 domain->integer.value == ACPI_WIFI_DOMAIN) 141 goto found; 142 } 143 144 return ERR_PTR(-ENOENT); 145 146 found: 147 return wifi_pkg; 148 } 149 IWL_EXPORT_SYMBOL(iwl_acpi_get_wifi_pkg); 150 151 int iwl_acpi_get_mcc(struct device *dev, char *mcc) 152 { 153 union acpi_object *wifi_pkg, *data; 154 u32 mcc_val; 155 int ret; 156 157 data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD); 158 if (IS_ERR(data)) 159 return PTR_ERR(data); 160 161 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE); 162 if (IS_ERR(wifi_pkg)) { 163 ret = PTR_ERR(wifi_pkg); 164 goto out_free; 165 } 166 167 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) { 168 ret = -EINVAL; 169 goto out_free; 170 } 171 172 mcc_val = wifi_pkg->package.elements[1].integer.value; 173 174 mcc[0] = (mcc_val >> 8) & 0xff; 175 mcc[1] = mcc_val & 0xff; 176 mcc[2] = '\0'; 177 178 ret = 0; 179 out_free: 180 kfree(data); 181 return ret; 182 } 183 IWL_EXPORT_SYMBOL(iwl_acpi_get_mcc); 184 185 u64 iwl_acpi_get_pwr_limit(struct device *dev) 186 { 187 union acpi_object *data, *wifi_pkg; 188 u64 dflt_pwr_limit; 189 190 data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD); 191 if (IS_ERR(data)) { 192 dflt_pwr_limit = 0; 193 goto out; 194 } 195 196 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, 197 ACPI_SPLC_WIFI_DATA_SIZE); 198 if (IS_ERR(wifi_pkg) || 199 wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) { 200 dflt_pwr_limit = 0; 201 goto out_free; 202 } 203 204 dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value; 205 out_free: 206 kfree(data); 207 out: 208 return dflt_pwr_limit; 209 } 210 IWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit); 211