Commit 6423d295 authored by Won Chung's avatar Won Chung Committed by Greg Kroah-Hartman
Browse files

driver core: Add sysfs support for physical location of a device



When ACPI table includes _PLD fields for a device, create a new
directory (physical_location) in sysfs to share _PLD fields.

Currently without PLD information, when there are multiple of same
devices, it is hard to distinguish which device corresponds to which
physical device at which location. For example, when there are two Type
C connectors, it is hard to find out which connector corresponds to the
Type C port on the left panel versus the Type C port on the right panel.
With PLD information provided, we can determine which specific device at
which location is doing what.

_PLD output includes much more fields, but only generic fields are added
and exposed to sysfs, so that non-ACPI devices can also support it in
the future. The minimal generic fields needed for locating a device are
the following.
- panel
- vertical_position
- horizontal_position
- dock
- lid

Signed-off-by: default avatarWon Chung <wonchung@google.com>
Link: https://lore.kernel.org/r/20220314195458.271430-1-wonchung@google.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ce753ad1
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
What:		/sys/devices/.../physical_location
Date:		March 2022
Contact:	Won Chung <wonchung@google.com>
Description:
		This directory contains information on physical location of
		the device connection point with respect to the system's
		housing.

What:		/sys/devices/.../physical_location/panel
Date:		March 2022
Contact:	Won Chung <wonchung@google.com>
Description:
		Describes which panel surface of the system’s housing the
		device connection point resides on.

What:		/sys/devices/.../physical_location/vertical_position
Date:		March 2022
Contact:	Won Chung <wonchung@google.com>
Description:
		Describes vertical position of the device connection point on
		the panel surface.

What:		/sys/devices/.../physical_location/horizontal_position
Date:		March 2022
Contact:	Won Chung <wonchung@google.com>
Description:
		Describes horizontal position of the device connection point on
		the panel surface.

What:		/sys/devices/.../physical_location/dock
Date:		March 2022
Contact:	Won Chung <wonchung@google.com>
Description:
		"Yes" if the device connection point resides in a docking
		station or a port replicator. "No" otherwise.

What:		/sys/devices/.../physical_location/lid
Date:		March 2022
Contact:	Won Chung <wonchung@google.com>
Description:
		"Yes" if the device connection point resides on the lid of
		laptop system. "No" otherwise.
+1 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ obj-$(CONFIG_DEV_COREDUMP) += devcoredump.o
obj-$(CONFIG_GENERIC_MSI_IRQ_DOMAIN) += platform-msi.o
obj-$(CONFIG_GENERIC_ARCH_TOPOLOGY) += arch_topology.o
obj-$(CONFIG_GENERIC_ARCH_NUMA) += arch_numa.o
obj-$(CONFIG_ACPI) += physical_location.o

obj-y			+= test/

+15 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include <linux/dma-map-ops.h> /* for dma_default_coherent */

#include "base.h"
#include "physical_location.h"
#include "power/power.h"

#ifdef CONFIG_SYSFS_DEPRECATED
@@ -2649,8 +2650,17 @@ static int device_add_attrs(struct device *dev)
			goto err_remove_dev_waiting_for_supplier;
	}

	if (dev_add_physical_location(dev)) {
		error = device_add_group(dev,
			&dev_attr_physical_location_group);
		if (error)
			goto err_remove_dev_removable;
	}

	return 0;

 err_remove_dev_removable:
	device_remove_file(dev, &dev_attr_removable);
 err_remove_dev_waiting_for_supplier:
	device_remove_file(dev, &dev_attr_waiting_for_supplier);
 err_remove_dev_online:
@@ -2672,6 +2682,11 @@ static void device_remove_attrs(struct device *dev)
	struct class *class = dev->class;
	const struct device_type *type = dev->type;

	if (dev->physical_location) {
		device_remove_group(dev, &dev_attr_physical_location_group);
		kfree(dev->physical_location);
	}

	device_remove_file(dev, &dev_attr_removable);
	device_remove_file(dev, &dev_attr_waiting_for_supplier);
	device_remove_file(dev, &dev_attr_online);
+137 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Device physical location support
 *
 * Author: Won Chung <wonchung@google.com>
 */

#include <linux/acpi.h>
#include <linux/sysfs.h>

#include "physical_location.h"

bool dev_add_physical_location(struct device *dev)
{
	struct acpi_pld_info *pld;
	acpi_status status;

	if (!has_acpi_companion(dev))
		return false;

	status = acpi_get_physical_device_location(ACPI_HANDLE(dev), &pld);
	if (ACPI_FAILURE(status))
		return false;

	dev->physical_location =
		kzalloc(sizeof(*dev->physical_location), GFP_KERNEL);
	dev->physical_location->panel = pld->panel;
	dev->physical_location->vertical_position = pld->vertical_position;
	dev->physical_location->horizontal_position = pld->horizontal_position;
	dev->physical_location->dock = pld->dock;
	dev->physical_location->lid = pld->lid;

	return true;
}

static ssize_t panel_show(struct device *dev, struct device_attribute *attr,
	char *buf)
{
	const char *panel;

	switch (dev->physical_location->panel) {
	case DEVICE_PANEL_TOP:
		panel = "top";
		break;
	case DEVICE_PANEL_BOTTOM:
		panel = "bottom";
		break;
	case DEVICE_PANEL_LEFT:
		panel = "left";
		break;
	case DEVICE_PANEL_RIGHT:
		panel = "right";
		break;
	case DEVICE_PANEL_FRONT:
		panel = "front";
		break;
	default:
		panel = "unknown";
	}
	return sysfs_emit(buf, "%s\n", panel);
}
static DEVICE_ATTR_RO(panel);

static ssize_t vertical_position_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	const char *vertical_position;

	switch (dev->physical_location->vertical_position) {
	case DEVICE_VERT_POS_UPPER:
		vertical_position = "upper";
		break;
	case DEVICE_VERT_POS_CENTER:
		vertical_position = "center";
		break;
	case DEVICE_VERT_POS_LOWER:
		vertical_position = "lower";
		break;
	default:
		vertical_position = "unknown";
	}
	return sysfs_emit(buf, "%s\n", vertical_position);
}
static DEVICE_ATTR_RO(vertical_position);

static ssize_t horizontal_position_show(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	const char *horizontal_position;

	switch (dev->physical_location->horizontal_position) {
	case DEVICE_HORI_POS_LEFT:
		horizontal_position = "left";
		break;
	case DEVICE_HORI_POS_CENTER:
		horizontal_position = "center";
		break;
	case DEVICE_HORI_POS_RIGHT:
		horizontal_position = "right";
		break;
	default:
		horizontal_position = "unknown";
	}
	return sysfs_emit(buf, "%s\n", horizontal_position);
}
static DEVICE_ATTR_RO(horizontal_position);

static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
	char *buf)
{
	return sysfs_emit(buf, "%s\n",
		dev->physical_location->dock ? "yes" : "no");
}
static DEVICE_ATTR_RO(dock);

static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
	char *buf)
{
	return sysfs_emit(buf, "%s\n",
		dev->physical_location->lid ? "yes" : "no");
}
static DEVICE_ATTR_RO(lid);

static struct attribute *dev_attr_physical_location[] = {
	&dev_attr_panel.attr,
	&dev_attr_vertical_position.attr,
	&dev_attr_horizontal_position.attr,
	&dev_attr_dock.attr,
	&dev_attr_lid.attr,
	NULL,
};

const struct attribute_group dev_attr_physical_location_group = {
	.name = "physical_location",
	.attrs = dev_attr_physical_location,
};
+16 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Device physical location support
 *
 * Author: Won Chung <wonchung@google.com>
 */

#include <linux/device.h>

#ifdef CONFIG_ACPI
extern bool dev_add_physical_location(struct device *dev);
extern const struct attribute_group dev_attr_physical_location_group;
#else
static inline bool dev_add_physical_location(struct device *dev) { return false; };
static const struct attribute_group dev_attr_physical_location_group = {};
#endif
Loading