1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2021, Intel Corporation.
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"
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
17 * Function is used to retrieve integer value from acpi object.
21 * * -EIO if there is an issue in acpi_object passed.
23 static int get_int_value(union acpi_object
*obj
, int *out
)
25 if (!obj
|| obj
->type
!= ACPI_TYPE_INTEGER
)
27 *out
= (int)obj
->integer
.value
;
32 * update_sar_data: sar data is updated based on regulatory mode
33 * @context: pointer to driver context structure
35 * sar_data is updated based on regulatory value
36 * context->reg_value will never exceed MAX_REGULATORY
38 static void update_sar_data(struct wwan_sar_context
*context
)
40 struct wwan_device_mode_configuration
*config
=
41 &context
->config_data
[context
->reg_value
];
43 if (config
->device_mode_info
&&
44 context
->sar_data
.device_mode
< config
->total_dev_mode
) {
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
];
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
;
63 * parse_package: parse acpi package for retrieving SAR information
64 * @context: pointer to driver context structure
65 * @item : acpi_object pointer
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.
77 static acpi_status
parse_package(struct wwan_sar_context
*context
, union acpi_object
*item
)
79 struct wwan_device_mode_configuration
*data
;
81 union acpi_object
*num
;
83 num
= &item
->package
.elements
[0];
84 if (get_int_value(num
, &value
) || value
< 0 || value
>= MAX_REGULATORY
)
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
)
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
)
99 for (itr
= 0; itr
< data
->total_dev_mode
; itr
++) {
100 struct wwan_device_mode_info temp
= { 0 };
102 num
= &item
->package
.elements
[itr
+ 1];
103 if (num
->type
!= ACPI_TYPE_PACKAGE
|| num
->package
.count
< TOTAL_DATA
)
105 if (get_int_value(&num
->package
.elements
[0], &temp
.device_mode
))
107 if (get_int_value(&num
->package
.elements
[1], &temp
.bandtable_index
))
109 if (get_int_value(&num
->package
.elements
[2], &temp
.antennatable_index
))
111 if (get_int_value(&num
->package
.elements
[3], &temp
.sartable_index
))
113 data
->device_mode_info
[itr
] = temp
;
119 * sar_get_device_mode: Extraction of information from BIOS via DSM calls
120 * @device: ACPI device for which to retrieve the data
122 * Retrieve the current device mode information from the BIOS.
128 static acpi_status
sar_get_device_mode(struct platform_device
*device
)
130 struct wwan_sar_context
*context
= dev_get_drvdata(&device
->dev
);
131 acpi_status status
= AE_OK
;
132 union acpi_object
*out
;
135 out
= acpi_evaluate_dsm_typed(context
->handle
, &context
->guid
, rev
,
136 COMMAND_ID_DEV_MODE
, NULL
, ACPI_TYPE_INTEGER
);
138 dev_err(&device
->dev
, "DSM cmd:%d Failed to retrieve value\n", COMMAND_ID_DEV_MODE
);
142 context
->sar_data
.device_mode
= out
->integer
.value
;
143 update_sar_data(context
);
144 sysfs_notify(&device
->dev
.kobj
, NULL
, SYSFS_DATANAME
);
151 static const struct acpi_device_id sar_device_ids
[] = {
155 MODULE_DEVICE_TABLE(acpi
, sar_device_ids
);
157 static ssize_t
intc_data_show(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
159 struct wwan_sar_context
*context
= dev_get_drvdata(dev
);
161 return sysfs_emit(buf
, "%d %d %d %d\n", context
->sar_data
.device_mode
,
162 context
->sar_data
.bandtable_index
,
163 context
->sar_data
.antennatable_index
,
164 context
->sar_data
.sartable_index
);
166 static DEVICE_ATTR_RO(intc_data
);
168 static ssize_t
intc_reg_show(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
170 struct wwan_sar_context
*context
= dev_get_drvdata(dev
);
172 return sysfs_emit(buf
, "%d\n", context
->reg_value
);
175 static ssize_t
intc_reg_store(struct device
*dev
, struct device_attribute
*attr
,
176 const char *buf
, size_t count
)
178 struct wwan_sar_context
*context
= dev_get_drvdata(dev
);
184 read
= kstrtouint(buf
, 10, &value
);
187 if (value
>= MAX_REGULATORY
)
189 context
->reg_value
= value
;
190 update_sar_data(context
);
191 sysfs_notify(&dev
->kobj
, NULL
, SYSFS_DATANAME
);
194 static DEVICE_ATTR_RW(intc_reg
);
196 static struct attribute
*intcsar_attrs
[] = {
197 &dev_attr_intc_data
.attr
,
198 &dev_attr_intc_reg
.attr
,
202 static struct attribute_group intcsar_group
= {
203 .attrs
= intcsar_attrs
,
206 static void sar_notify(acpi_handle handle
, u32 event
, void *data
)
208 struct platform_device
*device
= data
;
210 if (event
== SAR_EVENT
) {
211 if (sar_get_device_mode(device
) != AE_OK
)
212 dev_err(&device
->dev
, "sar_get_device_mode error");
216 static void sar_get_data(int reg
, struct wwan_sar_context
*context
)
218 union acpi_object
*out
, req
;
221 req
.type
= ACPI_TYPE_INTEGER
;
222 req
.integer
.value
= reg
;
223 out
= acpi_evaluate_dsm_typed(context
->handle
, &context
->guid
, rev
,
224 COMMAND_ID_CONFIG_TABLE
, &req
, ACPI_TYPE_PACKAGE
);
227 if (out
->package
.count
>= 3 &&
228 out
->package
.elements
[0].type
== ACPI_TYPE_INTEGER
&&
229 out
->package
.elements
[1].type
== ACPI_TYPE_INTEGER
&&
230 out
->package
.elements
[2].type
== ACPI_TYPE_PACKAGE
&&
231 out
->package
.elements
[2].package
.count
> 0) {
232 context
->config_data
[reg
].version
= out
->package
.elements
[0].integer
.value
;
233 context
->config_data
[reg
].total_dev_mode
=
234 out
->package
.elements
[1].integer
.value
;
235 if (context
->config_data
[reg
].total_dev_mode
<= 0 ||
236 context
->config_data
[reg
].total_dev_mode
> MAX_DEV_MODES
) {
240 parse_package(context
, &out
->package
.elements
[2]);
245 static int sar_probe(struct platform_device
*device
)
247 struct wwan_sar_context
*context
;
251 context
= kzalloc(sizeof(*context
), GFP_KERNEL
);
255 context
->sar_device
= device
;
256 context
->handle
= ACPI_HANDLE(&device
->dev
);
257 dev_set_drvdata(&device
->dev
, context
);
259 result
= guid_parse(SAR_DSM_UUID
, &context
->guid
);
261 dev_err(&device
->dev
, "SAR UUID parse error: %d\n", result
);
265 for (reg
= 0; reg
< MAX_REGULATORY
; reg
++)
266 sar_get_data(reg
, context
);
268 if (sar_get_device_mode(device
) != AE_OK
) {
269 dev_err(&device
->dev
, "Failed to get device mode\n");
274 result
= sysfs_create_group(&device
->dev
.kobj
, &intcsar_group
);
276 dev_err(&device
->dev
, "sysfs creation failed\n");
280 if (acpi_install_notify_handler(ACPI_HANDLE(&device
->dev
), ACPI_DEVICE_NOTIFY
,
281 sar_notify
, (void *)device
) != AE_OK
) {
282 dev_err(&device
->dev
, "Failed acpi_install_notify_handler\n");
289 sysfs_remove_group(&device
->dev
.kobj
, &intcsar_group
);
295 static void sar_remove(struct platform_device
*device
)
297 struct wwan_sar_context
*context
= dev_get_drvdata(&device
->dev
);
300 acpi_remove_notify_handler(ACPI_HANDLE(&device
->dev
),
301 ACPI_DEVICE_NOTIFY
, sar_notify
);
302 sysfs_remove_group(&device
->dev
.kobj
, &intcsar_group
);
303 for (reg
= 0; reg
< MAX_REGULATORY
; reg
++)
304 kfree(context
->config_data
[reg
].device_mode_info
);
309 static struct platform_driver sar_driver
= {
311 .remove
= sar_remove
,
314 .acpi_match_table
= ACPI_PTR(sar_device_ids
)
317 module_platform_driver(sar_driver
);
319 MODULE_LICENSE("GPL v2");
320 MODULE_DESCRIPTION("Platform device driver for INTEL MODEM BIOS SAR");
321 MODULE_AUTHOR("Shravan Sudhakar <s.shravan@intel.com>");