1*dcfbd31eSShravan S // SPDX-License-Identifier: GPL-2.0-only 2*dcfbd31eSShravan S /* 3*dcfbd31eSShravan S * Copyright (c) 2021, Intel Corporation. 4*dcfbd31eSShravan S */ 5*dcfbd31eSShravan S 6*dcfbd31eSShravan S #include <linux/acpi.h> 7*dcfbd31eSShravan S #include <linux/kobject.h> 8*dcfbd31eSShravan S #include <linux/platform_device.h> 9*dcfbd31eSShravan S #include <linux/sysfs.h> 10*dcfbd31eSShravan S #include "intel_sar.h" 11*dcfbd31eSShravan S 12*dcfbd31eSShravan S /** 13*dcfbd31eSShravan S * get_int_value: Retrieve integer values from ACPI Object 14*dcfbd31eSShravan S * @obj: acpi_object pointer which has the integer value 15*dcfbd31eSShravan S * @out: output pointer will get integer value 16*dcfbd31eSShravan S * 17*dcfbd31eSShravan S * Function is used to retrieve integer value from acpi object. 18*dcfbd31eSShravan S * 19*dcfbd31eSShravan S * Return: 20*dcfbd31eSShravan S * * 0 on success 21*dcfbd31eSShravan S * * -EIO if there is an issue in acpi_object passed. 22*dcfbd31eSShravan S */ 23*dcfbd31eSShravan S static int get_int_value(union acpi_object *obj, int *out) 24*dcfbd31eSShravan S { 25*dcfbd31eSShravan S if (!obj || obj->type != ACPI_TYPE_INTEGER) 26*dcfbd31eSShravan S return -EIO; 27*dcfbd31eSShravan S *out = (int)obj->integer.value; 28*dcfbd31eSShravan S return 0; 29*dcfbd31eSShravan S } 30*dcfbd31eSShravan S 31*dcfbd31eSShravan S /** 32*dcfbd31eSShravan S * update_sar_data: sar data is updated based on regulatory mode 33*dcfbd31eSShravan S * @context: pointer to driver context structure 34*dcfbd31eSShravan S * 35*dcfbd31eSShravan S * sar_data is updated based on regulatory value 36*dcfbd31eSShravan S * context->reg_value will never exceed MAX_REGULATORY 37*dcfbd31eSShravan S */ 38*dcfbd31eSShravan S static void update_sar_data(struct wwan_sar_context *context) 39*dcfbd31eSShravan S { 40*dcfbd31eSShravan S struct wwan_device_mode_configuration *config = 41*dcfbd31eSShravan S &context->config_data[context->reg_value]; 42*dcfbd31eSShravan S 43*dcfbd31eSShravan S if (config->device_mode_info && 44*dcfbd31eSShravan S context->sar_data.device_mode < config->total_dev_mode) { 45*dcfbd31eSShravan S struct wwan_device_mode_info *dev_mode = 46*dcfbd31eSShravan S &config->device_mode_info[context->sar_data.device_mode]; 47*dcfbd31eSShravan S 48*dcfbd31eSShravan S context->sar_data.antennatable_index = dev_mode->antennatable_index; 49*dcfbd31eSShravan S context->sar_data.bandtable_index = dev_mode->bandtable_index; 50*dcfbd31eSShravan S context->sar_data.sartable_index = dev_mode->sartable_index; 51*dcfbd31eSShravan S } 52*dcfbd31eSShravan S } 53*dcfbd31eSShravan S 54*dcfbd31eSShravan S /** 55*dcfbd31eSShravan S * parse_package: parse acpi package for retrieving SAR information 56*dcfbd31eSShravan S * @context: pointer to driver context structure 57*dcfbd31eSShravan S * @item : acpi_object pointer 58*dcfbd31eSShravan S * 59*dcfbd31eSShravan S * Given acpi_object is iterated to retrieve information for each device mode. 60*dcfbd31eSShravan S * If a given package corresponding to a specific device mode is faulty, it is 61*dcfbd31eSShravan S * skipped and the specific entry in context structure will have the default value 62*dcfbd31eSShravan S * of zero. Decoding of subsequent device modes is realized by having "continue" 63*dcfbd31eSShravan S * statements in the for loop on encountering error in parsing given device mode. 64*dcfbd31eSShravan S * 65*dcfbd31eSShravan S * Return: 66*dcfbd31eSShravan S * AE_OK if success 67*dcfbd31eSShravan S * AE_ERROR on error 68*dcfbd31eSShravan S */ 69*dcfbd31eSShravan S static acpi_status parse_package(struct wwan_sar_context *context, union acpi_object *item) 70*dcfbd31eSShravan S { 71*dcfbd31eSShravan S struct wwan_device_mode_configuration *data; 72*dcfbd31eSShravan S int value, itr, reg; 73*dcfbd31eSShravan S union acpi_object *num; 74*dcfbd31eSShravan S 75*dcfbd31eSShravan S num = &item->package.elements[0]; 76*dcfbd31eSShravan S if (get_int_value(num, &value) || value < 0 || value >= MAX_REGULATORY) 77*dcfbd31eSShravan S return AE_ERROR; 78*dcfbd31eSShravan S 79*dcfbd31eSShravan S reg = value; 80*dcfbd31eSShravan S 81*dcfbd31eSShravan S data = &context->config_data[reg]; 82*dcfbd31eSShravan S if (data->total_dev_mode > MAX_DEV_MODES || data->total_dev_mode == 0 || 83*dcfbd31eSShravan S item->package.count <= data->total_dev_mode) 84*dcfbd31eSShravan S return AE_ERROR; 85*dcfbd31eSShravan S 86*dcfbd31eSShravan S data->device_mode_info = kmalloc_array(data->total_dev_mode, 87*dcfbd31eSShravan S sizeof(struct wwan_device_mode_info), GFP_KERNEL); 88*dcfbd31eSShravan S if (!data->device_mode_info) 89*dcfbd31eSShravan S return AE_ERROR; 90*dcfbd31eSShravan S 91*dcfbd31eSShravan S for (itr = 0; itr < data->total_dev_mode; itr++) { 92*dcfbd31eSShravan S struct wwan_device_mode_info temp = { 0 }; 93*dcfbd31eSShravan S 94*dcfbd31eSShravan S num = &item->package.elements[itr + 1]; 95*dcfbd31eSShravan S if (num->type != ACPI_TYPE_PACKAGE || num->package.count < TOTAL_DATA) 96*dcfbd31eSShravan S continue; 97*dcfbd31eSShravan S if (get_int_value(&num->package.elements[0], &temp.device_mode)) 98*dcfbd31eSShravan S continue; 99*dcfbd31eSShravan S if (get_int_value(&num->package.elements[1], &temp.bandtable_index)) 100*dcfbd31eSShravan S continue; 101*dcfbd31eSShravan S if (get_int_value(&num->package.elements[2], &temp.antennatable_index)) 102*dcfbd31eSShravan S continue; 103*dcfbd31eSShravan S if (get_int_value(&num->package.elements[3], &temp.sartable_index)) 104*dcfbd31eSShravan S continue; 105*dcfbd31eSShravan S data->device_mode_info[itr] = temp; 106*dcfbd31eSShravan S } 107*dcfbd31eSShravan S return AE_OK; 108*dcfbd31eSShravan S } 109*dcfbd31eSShravan S 110*dcfbd31eSShravan S /** 111*dcfbd31eSShravan S * sar_get_device_mode: Extraction of information from BIOS via DSM calls 112*dcfbd31eSShravan S * @device: ACPI device for which to retrieve the data 113*dcfbd31eSShravan S * 114*dcfbd31eSShravan S * Retrieve the current device mode information from the BIOS. 115*dcfbd31eSShravan S * 116*dcfbd31eSShravan S * Return: 117*dcfbd31eSShravan S * AE_OK on success 118*dcfbd31eSShravan S * AE_ERROR on error 119*dcfbd31eSShravan S */ 120*dcfbd31eSShravan S static acpi_status sar_get_device_mode(struct platform_device *device) 121*dcfbd31eSShravan S { 122*dcfbd31eSShravan S struct wwan_sar_context *context = dev_get_drvdata(&device->dev); 123*dcfbd31eSShravan S acpi_status status = AE_OK; 124*dcfbd31eSShravan S union acpi_object *out; 125*dcfbd31eSShravan S u32 rev = 0; 126*dcfbd31eSShravan S int value; 127*dcfbd31eSShravan S 128*dcfbd31eSShravan S out = acpi_evaluate_dsm(context->handle, &context->guid, rev, 129*dcfbd31eSShravan S COMMAND_ID_DEV_MODE, NULL); 130*dcfbd31eSShravan S if (get_int_value(out, &value)) { 131*dcfbd31eSShravan S dev_err(&device->dev, "DSM cmd:%d Failed to retrieve value\n", COMMAND_ID_DEV_MODE); 132*dcfbd31eSShravan S status = AE_ERROR; 133*dcfbd31eSShravan S goto dev_mode_error; 134*dcfbd31eSShravan S } 135*dcfbd31eSShravan S context->sar_data.device_mode = value; 136*dcfbd31eSShravan S update_sar_data(context); 137*dcfbd31eSShravan S sysfs_notify(&device->dev.kobj, NULL, SYSFS_DATANAME); 138*dcfbd31eSShravan S 139*dcfbd31eSShravan S dev_mode_error: 140*dcfbd31eSShravan S ACPI_FREE(out); 141*dcfbd31eSShravan S return status; 142*dcfbd31eSShravan S } 143*dcfbd31eSShravan S 144*dcfbd31eSShravan S static const struct acpi_device_id sar_device_ids[] = { 145*dcfbd31eSShravan S { "INTC1092", 0}, 146*dcfbd31eSShravan S {} 147*dcfbd31eSShravan S }; 148*dcfbd31eSShravan S MODULE_DEVICE_TABLE(acpi, sar_device_ids); 149*dcfbd31eSShravan S 150*dcfbd31eSShravan S static ssize_t intc_data_show(struct device *dev, struct device_attribute *attr, char *buf) 151*dcfbd31eSShravan S { 152*dcfbd31eSShravan S struct wwan_sar_context *context = dev_get_drvdata(dev); 153*dcfbd31eSShravan S 154*dcfbd31eSShravan S return sysfs_emit(buf, "%d %d %d %d\n", context->sar_data.device_mode, 155*dcfbd31eSShravan S context->sar_data.bandtable_index, 156*dcfbd31eSShravan S context->sar_data.antennatable_index, 157*dcfbd31eSShravan S context->sar_data.sartable_index); 158*dcfbd31eSShravan S } 159*dcfbd31eSShravan S static DEVICE_ATTR_RO(intc_data); 160*dcfbd31eSShravan S 161*dcfbd31eSShravan S static ssize_t intc_reg_show(struct device *dev, struct device_attribute *attr, char *buf) 162*dcfbd31eSShravan S { 163*dcfbd31eSShravan S struct wwan_sar_context *context = dev_get_drvdata(dev); 164*dcfbd31eSShravan S 165*dcfbd31eSShravan S return sysfs_emit(buf, "%d\n", context->reg_value); 166*dcfbd31eSShravan S } 167*dcfbd31eSShravan S 168*dcfbd31eSShravan S static ssize_t intc_reg_store(struct device *dev, struct device_attribute *attr, 169*dcfbd31eSShravan S const char *buf, size_t count) 170*dcfbd31eSShravan S { 171*dcfbd31eSShravan S struct wwan_sar_context *context = dev_get_drvdata(dev); 172*dcfbd31eSShravan S unsigned int value; 173*dcfbd31eSShravan S int read; 174*dcfbd31eSShravan S 175*dcfbd31eSShravan S if (!count) 176*dcfbd31eSShravan S return -EINVAL; 177*dcfbd31eSShravan S read = kstrtouint(buf, 10, &value); 178*dcfbd31eSShravan S if (read < 0) 179*dcfbd31eSShravan S return read; 180*dcfbd31eSShravan S if (value >= MAX_REGULATORY) 181*dcfbd31eSShravan S return -EOVERFLOW; 182*dcfbd31eSShravan S context->reg_value = value; 183*dcfbd31eSShravan S update_sar_data(context); 184*dcfbd31eSShravan S sysfs_notify(&dev->kobj, NULL, SYSFS_DATANAME); 185*dcfbd31eSShravan S return count; 186*dcfbd31eSShravan S } 187*dcfbd31eSShravan S static DEVICE_ATTR_RW(intc_reg); 188*dcfbd31eSShravan S 189*dcfbd31eSShravan S static struct attribute *intcsar_attrs[] = { 190*dcfbd31eSShravan S &dev_attr_intc_data.attr, 191*dcfbd31eSShravan S &dev_attr_intc_reg.attr, 192*dcfbd31eSShravan S NULL 193*dcfbd31eSShravan S }; 194*dcfbd31eSShravan S 195*dcfbd31eSShravan S static struct attribute_group intcsar_group = { 196*dcfbd31eSShravan S .attrs = intcsar_attrs, 197*dcfbd31eSShravan S }; 198*dcfbd31eSShravan S 199*dcfbd31eSShravan S static void sar_notify(acpi_handle handle, u32 event, void *data) 200*dcfbd31eSShravan S { 201*dcfbd31eSShravan S struct platform_device *device = data; 202*dcfbd31eSShravan S 203*dcfbd31eSShravan S if (event == SAR_EVENT) { 204*dcfbd31eSShravan S if (sar_get_device_mode(device) != AE_OK) 205*dcfbd31eSShravan S dev_err(&device->dev, "sar_get_device_mode error"); 206*dcfbd31eSShravan S } 207*dcfbd31eSShravan S } 208*dcfbd31eSShravan S 209*dcfbd31eSShravan S static void sar_get_data(int reg, struct wwan_sar_context *context) 210*dcfbd31eSShravan S { 211*dcfbd31eSShravan S union acpi_object *out, req; 212*dcfbd31eSShravan S u32 rev = 0; 213*dcfbd31eSShravan S 214*dcfbd31eSShravan S req.type = ACPI_TYPE_INTEGER; 215*dcfbd31eSShravan S req.integer.value = reg; 216*dcfbd31eSShravan S out = acpi_evaluate_dsm(context->handle, &context->guid, rev, 217*dcfbd31eSShravan S COMMAND_ID_CONFIG_TABLE, &req); 218*dcfbd31eSShravan S if (!out) 219*dcfbd31eSShravan S return; 220*dcfbd31eSShravan S if (out->type == ACPI_TYPE_PACKAGE && out->package.count >= 3 && 221*dcfbd31eSShravan S out->package.elements[0].type == ACPI_TYPE_INTEGER && 222*dcfbd31eSShravan S out->package.elements[1].type == ACPI_TYPE_INTEGER && 223*dcfbd31eSShravan S out->package.elements[2].type == ACPI_TYPE_PACKAGE && 224*dcfbd31eSShravan S out->package.elements[2].package.count > 0) { 225*dcfbd31eSShravan S context->config_data[reg].version = out->package.elements[0].integer.value; 226*dcfbd31eSShravan S context->config_data[reg].total_dev_mode = 227*dcfbd31eSShravan S out->package.elements[1].integer.value; 228*dcfbd31eSShravan S if (context->config_data[reg].total_dev_mode <= 0 || 229*dcfbd31eSShravan S context->config_data[reg].total_dev_mode > MAX_DEV_MODES) { 230*dcfbd31eSShravan S ACPI_FREE(out); 231*dcfbd31eSShravan S return; 232*dcfbd31eSShravan S } 233*dcfbd31eSShravan S parse_package(context, &out->package.elements[2]); 234*dcfbd31eSShravan S } 235*dcfbd31eSShravan S ACPI_FREE(out); 236*dcfbd31eSShravan S } 237*dcfbd31eSShravan S 238*dcfbd31eSShravan S static int sar_probe(struct platform_device *device) 239*dcfbd31eSShravan S { 240*dcfbd31eSShravan S struct wwan_sar_context *context; 241*dcfbd31eSShravan S int reg; 242*dcfbd31eSShravan S int result; 243*dcfbd31eSShravan S 244*dcfbd31eSShravan S context = kzalloc(sizeof(*context), GFP_KERNEL); 245*dcfbd31eSShravan S if (!context) 246*dcfbd31eSShravan S return -ENOMEM; 247*dcfbd31eSShravan S 248*dcfbd31eSShravan S context->sar_device = device; 249*dcfbd31eSShravan S context->handle = ACPI_HANDLE(&device->dev); 250*dcfbd31eSShravan S dev_set_drvdata(&device->dev, context); 251*dcfbd31eSShravan S 252*dcfbd31eSShravan S result = guid_parse(SAR_DSM_UUID, &context->guid); 253*dcfbd31eSShravan S if (result) { 254*dcfbd31eSShravan S dev_err(&device->dev, "SAR UUID parse error: %d\n", result); 255*dcfbd31eSShravan S goto r_free; 256*dcfbd31eSShravan S } 257*dcfbd31eSShravan S 258*dcfbd31eSShravan S for (reg = 0; reg < MAX_REGULATORY; reg++) 259*dcfbd31eSShravan S sar_get_data(reg, context); 260*dcfbd31eSShravan S 261*dcfbd31eSShravan S if (sar_get_device_mode(device) != AE_OK) { 262*dcfbd31eSShravan S dev_err(&device->dev, "Failed to get device mode\n"); 263*dcfbd31eSShravan S result = -EIO; 264*dcfbd31eSShravan S goto r_free; 265*dcfbd31eSShravan S } 266*dcfbd31eSShravan S 267*dcfbd31eSShravan S result = sysfs_create_group(&device->dev.kobj, &intcsar_group); 268*dcfbd31eSShravan S if (result) { 269*dcfbd31eSShravan S dev_err(&device->dev, "sysfs creation failed\n"); 270*dcfbd31eSShravan S goto r_free; 271*dcfbd31eSShravan S } 272*dcfbd31eSShravan S 273*dcfbd31eSShravan S if (acpi_install_notify_handler(ACPI_HANDLE(&device->dev), ACPI_DEVICE_NOTIFY, 274*dcfbd31eSShravan S sar_notify, (void *)device) != AE_OK) { 275*dcfbd31eSShravan S dev_err(&device->dev, "Failed acpi_install_notify_handler\n"); 276*dcfbd31eSShravan S result = -EIO; 277*dcfbd31eSShravan S goto r_sys; 278*dcfbd31eSShravan S } 279*dcfbd31eSShravan S return 0; 280*dcfbd31eSShravan S 281*dcfbd31eSShravan S r_sys: 282*dcfbd31eSShravan S sysfs_remove_group(&device->dev.kobj, &intcsar_group); 283*dcfbd31eSShravan S r_free: 284*dcfbd31eSShravan S kfree(context); 285*dcfbd31eSShravan S return result; 286*dcfbd31eSShravan S } 287*dcfbd31eSShravan S 288*dcfbd31eSShravan S static int sar_remove(struct platform_device *device) 289*dcfbd31eSShravan S { 290*dcfbd31eSShravan S struct wwan_sar_context *context = dev_get_drvdata(&device->dev); 291*dcfbd31eSShravan S int reg; 292*dcfbd31eSShravan S 293*dcfbd31eSShravan S acpi_remove_notify_handler(ACPI_HANDLE(&device->dev), 294*dcfbd31eSShravan S ACPI_DEVICE_NOTIFY, sar_notify); 295*dcfbd31eSShravan S sysfs_remove_group(&device->dev.kobj, &intcsar_group); 296*dcfbd31eSShravan S for (reg = 0; reg < MAX_REGULATORY; reg++) 297*dcfbd31eSShravan S kfree(context->config_data[reg].device_mode_info); 298*dcfbd31eSShravan S 299*dcfbd31eSShravan S kfree(context); 300*dcfbd31eSShravan S return 0; 301*dcfbd31eSShravan S } 302*dcfbd31eSShravan S 303*dcfbd31eSShravan S static struct platform_driver sar_driver = { 304*dcfbd31eSShravan S .probe = sar_probe, 305*dcfbd31eSShravan S .remove = sar_remove, 306*dcfbd31eSShravan S .driver = { 307*dcfbd31eSShravan S .name = DRVNAME, 308*dcfbd31eSShravan S .owner = THIS_MODULE, 309*dcfbd31eSShravan S .acpi_match_table = ACPI_PTR(sar_device_ids) 310*dcfbd31eSShravan S } 311*dcfbd31eSShravan S }; 312*dcfbd31eSShravan S module_platform_driver(sar_driver); 313*dcfbd31eSShravan S 314*dcfbd31eSShravan S MODULE_LICENSE("GPL v2"); 315*dcfbd31eSShravan S MODULE_DESCRIPTION("Platform device driver for INTEL MODEM BIOS SAR"); 316*dcfbd31eSShravan S MODULE_AUTHOR("Shravan S <s.shravan@intel.com>"); 317