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