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