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