Linux Audio

Check our new training course

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