1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Device physical location support
4  *
5  * Author: Won Chung <wonchung@google.com>
6  */
7 
8 #include <linux/acpi.h>
9 #include <linux/sysfs.h>
10 
11 #include "physical_location.h"
12 
13 bool dev_add_physical_location(struct device *dev)
14 {
15 	struct acpi_pld_info *pld;
16 	acpi_status status;
17 
18 	if (!has_acpi_companion(dev))
19 		return false;
20 
21 	status = acpi_get_physical_device_location(ACPI_HANDLE(dev), &pld);
22 	if (ACPI_FAILURE(status))
23 		return false;
24 
25 	dev->physical_location =
26 		kzalloc(sizeof(*dev->physical_location), GFP_KERNEL);
27 	if (!dev->physical_location) {
28 		ACPI_FREE(pld);
29 		return false;
30 	}
31 
32 	dev->physical_location->panel = pld->panel;
33 	dev->physical_location->vertical_position = pld->vertical_position;
34 	dev->physical_location->horizontal_position = pld->horizontal_position;
35 	dev->physical_location->dock = pld->dock;
36 	dev->physical_location->lid = pld->lid;
37 
38 	ACPI_FREE(pld);
39 	return true;
40 }
41 
42 static ssize_t panel_show(struct device *dev, struct device_attribute *attr,
43 	char *buf)
44 {
45 	const char *panel;
46 
47 	switch (dev->physical_location->panel) {
48 	case DEVICE_PANEL_TOP:
49 		panel = "top";
50 		break;
51 	case DEVICE_PANEL_BOTTOM:
52 		panel = "bottom";
53 		break;
54 	case DEVICE_PANEL_LEFT:
55 		panel = "left";
56 		break;
57 	case DEVICE_PANEL_RIGHT:
58 		panel = "right";
59 		break;
60 	case DEVICE_PANEL_FRONT:
61 		panel = "front";
62 		break;
63 	case DEVICE_PANEL_BACK:
64 		panel = "back";
65 		break;
66 	default:
67 		panel = "unknown";
68 	}
69 	return sysfs_emit(buf, "%s\n", panel);
70 }
71 static DEVICE_ATTR_RO(panel);
72 
73 static ssize_t vertical_position_show(struct device *dev,
74 	struct device_attribute *attr, char *buf)
75 {
76 	const char *vertical_position;
77 
78 	switch (dev->physical_location->vertical_position) {
79 	case DEVICE_VERT_POS_UPPER:
80 		vertical_position = "upper";
81 		break;
82 	case DEVICE_VERT_POS_CENTER:
83 		vertical_position = "center";
84 		break;
85 	case DEVICE_VERT_POS_LOWER:
86 		vertical_position = "lower";
87 		break;
88 	default:
89 		vertical_position = "unknown";
90 	}
91 	return sysfs_emit(buf, "%s\n", vertical_position);
92 }
93 static DEVICE_ATTR_RO(vertical_position);
94 
95 static ssize_t horizontal_position_show(struct device *dev,
96 	struct device_attribute *attr, char *buf)
97 {
98 	const char *horizontal_position;
99 
100 	switch (dev->physical_location->horizontal_position) {
101 	case DEVICE_HORI_POS_LEFT:
102 		horizontal_position = "left";
103 		break;
104 	case DEVICE_HORI_POS_CENTER:
105 		horizontal_position = "center";
106 		break;
107 	case DEVICE_HORI_POS_RIGHT:
108 		horizontal_position = "right";
109 		break;
110 	default:
111 		horizontal_position = "unknown";
112 	}
113 	return sysfs_emit(buf, "%s\n", horizontal_position);
114 }
115 static DEVICE_ATTR_RO(horizontal_position);
116 
117 static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
118 	char *buf)
119 {
120 	return sysfs_emit(buf, "%s\n",
121 		dev->physical_location->dock ? "yes" : "no");
122 }
123 static DEVICE_ATTR_RO(dock);
124 
125 static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
126 	char *buf)
127 {
128 	return sysfs_emit(buf, "%s\n",
129 		dev->physical_location->lid ? "yes" : "no");
130 }
131 static DEVICE_ATTR_RO(lid);
132 
133 static struct attribute *dev_attr_physical_location[] = {
134 	&dev_attr_panel.attr,
135 	&dev_attr_vertical_position.attr,
136 	&dev_attr_horizontal_position.attr,
137 	&dev_attr_dock.attr,
138 	&dev_attr_lid.attr,
139 	NULL,
140 };
141 
142 const struct attribute_group dev_attr_physical_location_group = {
143 	.name = "physical_location",
144 	.attrs = dev_attr_physical_location,
145 };
146 
147