提交 69308402 编写于 作者: L Linus Torvalds

Merge tag 'platform-drivers-x86-v6.3-1' of...

Merge tag 'platform-drivers-x86-v6.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86

Pull x86 platform driver updates from Hans de Goede:

 - AMD PMC: Improvements to aid s2idle debugging

 - Dell WMI-DDV: hwmon support

 - INT3472 camera sensor power-management: Improve privacy LED support

 - Intel VSEC: Base TPMI (Topology Aware Register and PM Capsule
   Interface) support

 - Mellanox: SN5600 and Nvidia L1 switch support

 - Microsoft Surface Support: Various cleanups + code improvements

 - tools/intel-speed-select: Various improvements

 - Miscellaneous other cleanups / fixes

* tag 'platform-drivers-x86-v6.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: (80 commits)
  platform/x86: nvidia-wmi-ec-backlight: Add force module parameter
  platform/x86/amd/pmf: Add depends on CONFIG_POWER_SUPPLY
  platform/x86: dell-ddv: Prefer asynchronous probing
  platform/x86: dell-ddv: Add hwmon support
  Documentation/ABI: Add new attribute for mlxreg-io sysfs interfaces
  platform: mellanox: mlx-platform: Move bus shift assignment out of the loop
  platform: mellanox: mlx-platform: Add mux selection register to regmap
  platform_data/mlxreg: Add field with mapped resource address
  platform/mellanox: mlxreg-hotplug: Allow more flexible hotplug events configuration
  platform: mellanox: Extend all systems with I2C notification callback
  platform: mellanox: Split logic in init and exit flow
  platform: mellanox: Split initialization procedure
  platform: mellanox: Introduce support of new Nvidia L1 switch
  platform: mellanox: Introduce support for next-generation 800GB/s switch
  platform: mellanox: Cosmetic changes - rename to more common name
  platform: mellanox: Change "reset_pwr_converter_fail" attribute
  platform: mellanox: Introduce support for rack manager switch
  MAINTAINERS: dell-wmi-sysman: drop Divya Bharathi
  x86/platform/uv: Make kobj_type structure constant
  platform/x86: think-lmi: Make kobj_type structure constant
  ...
......@@ -522,7 +522,6 @@ Description: These files allow to each of ASICs by writing 1.
The files are write only.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/comm_chnl_ready
Date: July 2022
KernelVersion: 5.20
......@@ -542,3 +541,124 @@ Description: The file indicates COME module hardware configuration.
The purpose is to expose some minor BOM changes for the same system SKU.
The file is read only.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_pwr_converter_fail
Date: February 2023
KernelVersion: 6.3
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file shows the system reset cause due to power converter
devices failure.
Value 1 in file means this is reset cause, 0 - otherwise.
The file is read only.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/erot1_ap_reset
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/erot2_ap_reset
Date: February 2023
KernelVersion: 6.3
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files aim to monitor the status of the External Root of Trust (EROT)
processor's RESET output to the Application Processor (AP).
By reading this file, could be determined if the EROT has invalidated or
revoked AP Firmware, at which point it will hold the AP in RESET until a
valid firmware is loaded. This protects the AP from running an
unauthorized firmware. In the normal flow, the AP reset should be released
after the EROT validates the integrity of the FW, and it should be done so
as quickly as possible so that the AP boots before the CPU starts to
communicate to each ASIC.
The files are read only.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/erot1_recovery
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/erot2_recovery
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/erot1_reset
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/erot2_reset
Date: February 2023
KernelVersion: 6.3
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files aim to perform External Root of Trust (EROT) recovery
sequence after EROT device failure.
These EROT devices protect ASICs from unauthorized access and in normal
flow their reset should be released with system power – earliest power
up stage, so that EROTs can begin boot and authentication process before
CPU starts to communicate to ASICs.
Issuing a reset to the EROT while asserting the recovery signal will cause
the EROT Application Processor to enter recovery mode so that the EROT FW
can be updated/recovered.
For reset/recovery the related file should be toggled by 1/0.
The files are read/write.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/erot1_wp
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/erot2_wp
Date: February 2023
KernelVersion: 6.3
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: These files allow access to External Root of Trust (EROT) for reset
and recovery sequence after EROT device failure.
Default is 0 (programming disabled).
If the system is in locked-down mode writing this file will not be allowed.
The files are read/write.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/spi_chnl_select
Date: February 2023
KernelVersion: 6.3
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file allows SPI chip selection for External Root of Trust (EROT)
device Out-of-Band recovery.
File can be written with 0 or with 1. It selects which EROT can be accessed
through SPI device.
The file is read/write.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic_pg_fail
Date: February 2023
KernelVersion: 6.3
Contact: Vadim Pasternak vadimp@nvidia.com
Description: This file shows ASIC Power Good status.
Value 1 in file means ASIC Power Good failed, 0 - otherwise.
The file is read only.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/clk_brd1_boot_fail
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/clk_brd2_boot_fail
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/clk_brd_fail
Date: February 2023
KernelVersion: 6.3
Contact: Vadim Pasternak vadimp@nvidia.com
Description: These files are related to clock boards status in system.
- clk_brd1_boot_fail: warning about 1-st clock board failed to boot from CI.
- clk_brd2_boot_fail: warning about 2-nd clock board failed to boot from CI.
- clk_brd_fail: error about common clock board boot failure.
The files are read only.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/clk_brd_prog_en
Date: February 2023
KernelVersion: 6.3
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file enables programming of clock boards.
Default is 0 (programming disabled).
If the system is in locked-down mode writing this file will not be allowed.
The file is read/write.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pwr_converter_prog_en
Date: February 2023
KernelVersion: 6.3
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file enables programming of power converters.
Default is 0 (programming disabled).
If the system is in locked-down mode writing this file will not be allowed.
The file is read/write.
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_ac_ok_fail
Date: February 2023
KernelVersion: 6.3
Contact: Vadim Pasternak <vadimp@nvidia.com>
Description: This file shows the system reset cause due to AC power failure.
Value 1 in file means this is reset cause, 0 - otherwise.
The file is read only.
......@@ -19,7 +19,7 @@
.. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister`
.. |ssam_device_notifier_register| replace:: :c:func:`ssam_device_notifier_register`
.. |ssam_device_notifier_unregister| replace:: :c:func:`ssam_device_notifier_unregister`
.. |ssam_request_sync| replace:: :c:func:`ssam_request_sync`
.. |ssam_request_do_sync| replace:: :c:func:`ssam_request_do_sync`
.. |ssam_event_mask| replace:: :c:type:`enum ssam_event_mask <ssam_event_mask>`
......@@ -191,7 +191,7 @@ data received from it is converted from little-endian to host endianness.
* they do not correspond to an actual SAM/EC request.
*/
rqst.target_category = SSAM_SSH_TC_SAM;
rqst.target_id = 0x01;
rqst.target_id = SSAM_SSH_TID_SAM;
rqst.command_id = 0x02;
rqst.instance_id = 0x03;
rqst.flags = SSAM_REQUEST_HAS_RESPONSE;
......@@ -209,12 +209,12 @@ data received from it is converted from little-endian to host endianness.
* with the SSAM_REQUEST_HAS_RESPONSE flag set in the specification
* above.
*/
status = ssam_request_sync(ctrl, &rqst, &resp);
status = ssam_request_do_sync(ctrl, &rqst, &resp);
/*
* Alternatively use
*
* ssam_request_sync_onstack(ctrl, &rqst, &resp, sizeof(arg_le));
* ssam_request_do_sync_onstack(ctrl, &rqst, &resp, sizeof(arg_le));
*
* to perform the request, allocating the message buffer directly
* on the stack as opposed to allocation via kzalloc().
......@@ -230,7 +230,7 @@ data received from it is converted from little-endian to host endianness.
return status;
}
Note that |ssam_request_sync| in its essence is a wrapper over lower-level
Note that |ssam_request_do_sync| in its essence is a wrapper over lower-level
request primitives, which may also be used to perform requests. Refer to its
implementation and documentation for more details.
......@@ -241,7 +241,7 @@ one of the generator macros, for example via:
SSAM_DEFINE_SYNC_REQUEST_W(__ssam_tmp_perf_mode_set, __le32, {
.target_category = SSAM_SSH_TC_TMP,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x03,
.instance_id = 0x00,
});
......
......@@ -13,6 +13,7 @@
.. |DATA_NSQ| replace:: ``DATA_NSQ``
.. |TC| replace:: ``TC``
.. |TID| replace:: ``TID``
.. |SID| replace:: ``SID``
.. |IID| replace:: ``IID``
.. |RQID| replace:: ``RQID``
.. |CID| replace:: ``CID``
......@@ -219,13 +220,13 @@ following fields, packed together and in order:
- |u8|
- Target category.
* - |TID| (out)
* - |TID|
- |u8|
- Target ID for outgoing (host to EC) commands.
- Target ID for commands/messages.
* - |TID| (in)
* - |SID|
- |u8|
- Target ID for incoming (EC to host) commands.
- Source ID for commands/messages.
* - |IID|
- |u8|
......@@ -286,19 +287,20 @@ general, however, a single target category should map to a single reserved
event request ID.
Furthermore, requests, responses, and events have an associated target ID
(``TID``). This target ID is split into output (host to EC) and input (EC to
host) fields, with the respecting other field (e.g. output field on incoming
messages) set to zero. Two ``TID`` values are known: Primary (``0x01``) and
secondary (``0x02``). In general, the response to a request should have the
same ``TID`` value, however, the field (output vs. input) should be used in
accordance to the direction in which the response is sent (i.e. on the input
field, as responses are generally sent from the EC to the host).
Note that, even though requests and events should be uniquely identifiable
by target category and command ID alone, the EC may require specific
target ID and instance ID values to accept a command. A command that is
accepted for ``TID=1``, for example, may not be accepted for ``TID=2``
and vice versa.
(``TID``) and source ID (``SID``). These two fields indicate where a message
originates from (``SID``) and what the intended target of the message is
(``TID``). Note that a response to a specific request therefore has the source
and target IDs swapped when compared to the original request (i.e. the request
target is the response source and the request source is the response target).
See (:c:type:`enum ssh_request_id <ssh_request_id>`) for possible values of
both.
Note that, even though requests and events should be uniquely identifiable by
target category and command ID alone, the EC may require specific target ID and
instance ID values to accept a command. A command that is accepted for
``TID=1``, for example, may not be accepted for ``TID=2`` and vice versa. While
this may not always hold in reality, you can think of different target/source
IDs indicating different physical ECs with potentially different feature sets.
Limitations and Observations
......
......@@ -5773,7 +5773,6 @@ F: Documentation/ABI/testing/sysfs-platform-dell-wmi-ddv
F: drivers/platform/x86/dell/dell-wmi-ddv.c
DELL WMI SYSMAN DRIVER
M: Divya Bharathi <divya.bharathi@dell.com>
M: Prasanth Ksr <prasanth.ksr@dell.com>
L: Dell.Client.Kernel@dell.com
L: platform-driver-x86@vger.kernel.org
......@@ -10535,6 +10534,13 @@ S: Maintained
F: arch/x86/include/asm/intel_telemetry.h
F: drivers/platform/x86/intel/telemetry/
INTEL TPMI DRIVER
M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/intel/tpmi.c
F: include/linux/intel_tpmi.h
INTEL UNCORE FREQUENCY CONTROL
M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
L: platform-driver-x86@vger.kernel.org
......@@ -22480,6 +22486,7 @@ S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git
F: drivers/platform/olpc/
F: drivers/platform/x86/
F: include/linux/platform_data/x86/
X86 PLATFORM DRIVERS - ARCH
R: Darren Hart <dvhart@infradead.org>
......
......@@ -80,7 +80,7 @@ static int ssam_hid_get_descriptor(struct surface_hid_device *shid, u8 entry, u8
rsp.length = 0;
status = ssam_retry(ssam_request_sync_onstack, shid->ctrl, &rqst, &rsp,
status = ssam_retry(ssam_request_do_sync_onstack, shid->ctrl, &rqst, &rsp,
sizeof(*slice));
if (status)
return status;
......@@ -131,7 +131,7 @@ static int ssam_hid_set_raw_report(struct surface_hid_device *shid, u8 rprt_id,
buf[0] = rprt_id;
return ssam_retry(ssam_request_sync, shid->ctrl, &rqst, NULL);
return ssam_retry(ssam_request_do_sync, shid->ctrl, &rqst, NULL);
}
static int ssam_hid_get_raw_report(struct surface_hid_device *shid, u8 rprt_id, u8 *buf, size_t len)
......@@ -151,7 +151,7 @@ static int ssam_hid_get_raw_report(struct surface_hid_device *shid, u8 rprt_id,
rsp.length = 0;
rsp.pointer = buf;
return ssam_retry(ssam_request_sync_onstack, shid->ctrl, &rqst, &rsp, sizeof(rprt_id));
return ssam_retry(ssam_request_do_sync_onstack, shid->ctrl, &rqst, &rsp, sizeof(rprt_id));
}
static u32 ssam_hid_event_fn(struct ssam_event_notifier *nf, const struct ssam_event *event)
......@@ -230,7 +230,7 @@ static void surface_hid_remove(struct ssam_device *sdev)
}
static const struct ssam_device_id surface_hid_match[] = {
{ SSAM_SDEV(HID, SSAM_ANY_TID, SSAM_ANY_IID, 0x00) },
{ SSAM_SDEV(HID, ANY, SSAM_SSH_IID_ANY, 0x00) },
{ },
};
MODULE_DEVICE_TABLE(ssam, surface_hid_match);
......
......@@ -49,7 +49,7 @@ static int ssam_kbd_get_descriptor(struct surface_hid_device *shid, u8 entry, u8
rsp.length = 0;
rsp.pointer = buf;
status = ssam_retry(ssam_request_sync_onstack, shid->ctrl, &rqst, &rsp, sizeof(entry));
status = ssam_retry(ssam_request_do_sync_onstack, shid->ctrl, &rqst, &rsp, sizeof(entry));
if (status)
return status;
......@@ -75,7 +75,7 @@ static int ssam_kbd_set_caps_led(struct surface_hid_device *shid, bool value)
rqst.length = sizeof(value_u8);
rqst.payload = &value_u8;
return ssam_retry(ssam_request_sync_onstack, shid->ctrl, &rqst, NULL, sizeof(value_u8));
return ssam_retry(ssam_request_do_sync_onstack, shid->ctrl, &rqst, NULL, sizeof(value_u8));
}
static int ssam_kbd_get_feature_report(struct surface_hid_device *shid, u8 *buf, size_t len)
......@@ -97,7 +97,7 @@ static int ssam_kbd_get_feature_report(struct surface_hid_device *shid, u8 *buf,
rsp.length = 0;
rsp.pointer = buf;
status = ssam_retry(ssam_request_sync_onstack, shid->ctrl, &rqst, &rsp, sizeof(payload));
status = ssam_retry(ssam_request_do_sync_onstack, shid->ctrl, &rqst, &rsp, sizeof(payload));
if (status)
return status;
......@@ -250,7 +250,7 @@ static int surface_kbd_probe(struct platform_device *pdev)
shid->uid.domain = SSAM_DOMAIN_SERIALHUB;
shid->uid.category = SSAM_SSH_TC_KBD;
shid->uid.target = 2;
shid->uid.target = SSAM_SSH_TID_KIP;
shid->uid.instance = 0;
shid->uid.function = 0;
......
......@@ -23,6 +23,8 @@
#include "leds.h"
static struct class *leds_class;
static DEFINE_MUTEX(leds_lookup_lock);
static LIST_HEAD(leds_lookup_list);
static ssize_t brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
......@@ -215,6 +217,23 @@ static int led_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
static struct led_classdev *led_module_get(struct device *led_dev)
{
struct led_classdev *led_cdev;
if (!led_dev)
return ERR_PTR(-EPROBE_DEFER);
led_cdev = dev_get_drvdata(led_dev);
if (!try_module_get(led_cdev->dev->parent->driver->owner)) {
put_device(led_cdev->dev);
return ERR_PTR(-ENODEV);
}
return led_cdev;
}
/**
* of_led_get() - request a LED device via the LED framework
* @np: device node to get the LED device from
......@@ -226,7 +245,6 @@ static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
struct led_classdev *of_led_get(struct device_node *np, int index)
{
struct device *led_dev;
struct led_classdev *led_cdev;
struct device_node *led_node;
led_node = of_parse_phandle(np, "leds", index);
......@@ -236,15 +254,7 @@ struct led_classdev *of_led_get(struct device_node *np, int index)
led_dev = class_find_device_by_of_node(leds_class, led_node);
of_node_put(led_node);
if (!led_dev)
return ERR_PTR(-EPROBE_DEFER);
led_cdev = dev_get_drvdata(led_dev);
if (!try_module_get(led_cdev->dev->parent->driver->owner))
return ERR_PTR(-ENODEV);
return led_cdev;
return led_module_get(led_dev);
}
EXPORT_SYMBOL_GPL(of_led_get);
......@@ -255,6 +265,7 @@ EXPORT_SYMBOL_GPL(of_led_get);
void led_put(struct led_classdev *led_cdev)
{
module_put(led_cdev->dev->parent->driver->owner);
put_device(led_cdev->dev);
}
EXPORT_SYMBOL_GPL(led_put);
......@@ -265,6 +276,22 @@ static void devm_led_release(struct device *dev, void *res)
led_put(*p);
}
static struct led_classdev *__devm_led_get(struct device *dev, struct led_classdev *led)
{
struct led_classdev **dr;
dr = devres_alloc(devm_led_release, sizeof(struct led_classdev *), GFP_KERNEL);
if (!dr) {
led_put(led);
return ERR_PTR(-ENOMEM);
}
*dr = led;
devres_add(dev, dr);
return led;
}
/**
* devm_of_led_get - Resource-managed request of a LED device
* @dev: LED consumer
......@@ -280,7 +307,6 @@ struct led_classdev *__must_check devm_of_led_get(struct device *dev,
int index)
{
struct led_classdev *led;
struct led_classdev **dr;
if (!dev)
return ERR_PTR(-EINVAL);
......@@ -289,19 +315,91 @@ struct led_classdev *__must_check devm_of_led_get(struct device *dev,
if (IS_ERR(led))
return led;
dr = devres_alloc(devm_led_release, sizeof(struct led_classdev *),
GFP_KERNEL);
if (!dr) {
led_put(led);
return ERR_PTR(-ENOMEM);
return __devm_led_get(dev, led);
}
EXPORT_SYMBOL_GPL(devm_of_led_get);
/**
* led_get() - request a LED device via the LED framework
* @dev: device for which to get the LED device
* @con_id: name of the LED from the device's point of view
*
* @return a pointer to a LED device or ERR_PTR(errno) on failure.
*/
struct led_classdev *led_get(struct device *dev, char *con_id)
{
struct led_lookup_data *lookup;
const char *provider = NULL;
struct device *led_dev;
mutex_lock(&leds_lookup_lock);
list_for_each_entry(lookup, &leds_lookup_list, list) {
if (!strcmp(lookup->dev_id, dev_name(dev)) &&
!strcmp(lookup->con_id, con_id)) {
provider = kstrdup_const(lookup->provider, GFP_KERNEL);
break;
}
}
mutex_unlock(&leds_lookup_lock);
*dr = led;
devres_add(dev, dr);
if (!provider)
return ERR_PTR(-ENOENT);
led_dev = class_find_device_by_name(leds_class, provider);
kfree_const(provider);
return led_module_get(led_dev);
}
EXPORT_SYMBOL_GPL(led_get);
/**
* devm_led_get() - request a LED device via the LED framework
* @dev: device for which to get the LED device
* @con_id: name of the LED from the device's point of view
*
* The LED device returned from this function is automatically released
* on driver detach.
*
* @return a pointer to a LED device or ERR_PTR(errno) on failure.
*/
struct led_classdev *devm_led_get(struct device *dev, char *con_id)
{
struct led_classdev *led;
led = led_get(dev, con_id);
if (IS_ERR(led))
return led;
return __devm_led_get(dev, led);
}
EXPORT_SYMBOL_GPL(devm_of_led_get);
EXPORT_SYMBOL_GPL(devm_led_get);
/**
* led_add_lookup() - Add a LED lookup table entry
* @led_lookup: the lookup table entry to add
*
* Add a LED lookup table entry. On systems without devicetree the lookup table
* is used by led_get() to find LEDs.
*/
void led_add_lookup(struct led_lookup_data *led_lookup)
{
mutex_lock(&leds_lookup_lock);
list_add_tail(&led_lookup->list, &leds_lookup_list);
mutex_unlock(&leds_lookup_lock);
}
EXPORT_SYMBOL_GPL(led_add_lookup);
/**
* led_remove_lookup() - Remove a LED lookup table entry
* @led_lookup: the lookup table entry to remove
*/
void led_remove_lookup(struct led_lookup_data *led_lookup)
{
mutex_lock(&leds_lookup_lock);
list_del(&led_lookup->list);
mutex_unlock(&leds_lookup_lock);
}
EXPORT_SYMBOL_GPL(led_remove_lookup);
static int led_classdev_next_name(const char *init_name, char *name,
size_t len)
......
......@@ -24,6 +24,8 @@
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
#include "v4l2-subdev-priv.h"
static int v4l2_async_nf_call_bound(struct v4l2_async_notifier *n,
struct v4l2_subdev *subdev,
struct v4l2_async_subdev *asd)
......@@ -822,6 +824,8 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
if (!sd->async_list.next)
return;
v4l2_subdev_put_privacy_led(sd);
mutex_lock(&list_lock);
__v4l2_async_nf_unregister(sd->subdev_notifier);
......
......@@ -28,6 +28,8 @@
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
#include "v4l2-subdev-priv.h"
static const struct v4l2_fwnode_bus_conv {
enum v4l2_fwnode_bus_type fwnode_bus_type;
enum v4l2_mbus_type mbus_type;
......@@ -1302,6 +1304,10 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
v4l2_async_nf_init(notifier);
ret = v4l2_subdev_get_privacy_led(sd);
if (ret < 0)
goto out_cleanup;
ret = v4l2_async_nf_parse_fwnode_sensor(sd->dev, notifier);
if (ret < 0)
goto out_cleanup;
......@@ -1322,6 +1328,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
v4l2_async_nf_unregister(notifier);
out_cleanup:
v4l2_subdev_put_privacy_led(sd);
v4l2_async_nf_cleanup(notifier);
kfree(notifier);
......
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* V4L2 sub-device pivate header.
*
* Copyright (C) 2023 Hans de Goede <hdegoede@redhat.com>
*/
#ifndef _V4L2_SUBDEV_PRIV_H_
#define _V4L2_SUBDEV_PRIV_H_
int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd);
void v4l2_subdev_put_privacy_led(struct v4l2_subdev *sd);
#endif
......@@ -9,6 +9,7 @@
*/
#include <linux/ioctl.h>
#include <linux/leds.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/slab.h>
......@@ -23,6 +24,8 @@
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
#include "v4l2-subdev-priv.h"
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd)
{
......@@ -322,6 +325,15 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable)
{
int ret;
#if IS_REACHABLE(CONFIG_LEDS_CLASS)
if (!IS_ERR_OR_NULL(sd->privacy_led)) {
if (enable)
led_set_brightness(sd->privacy_led,
sd->privacy_led->max_brightness);
else
led_set_brightness(sd->privacy_led, 0);
}
#endif
ret = sd->ops->video->s_stream(sd, enable);
if (!enable && ret < 0) {
......@@ -1090,6 +1102,7 @@ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
sd->grp_id = 0;
sd->dev_priv = NULL;
sd->host_priv = NULL;
sd->privacy_led = NULL;
#if defined(CONFIG_MEDIA_CONTROLLER)
sd->entity.name = sd->name;
sd->entity.obj_type = MEDIA_ENTITY_TYPE_V4L2_SUBDEV;
......@@ -1105,3 +1118,36 @@ void v4l2_subdev_notify_event(struct v4l2_subdev *sd,
v4l2_subdev_notify(sd, V4L2_DEVICE_NOTIFY_EVENT, (void *)ev);
}
EXPORT_SYMBOL_GPL(v4l2_subdev_notify_event);
int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd)
{
#if IS_REACHABLE(CONFIG_LEDS_CLASS)
sd->privacy_led = led_get(sd->dev, "privacy-led");
if (IS_ERR(sd->privacy_led) && PTR_ERR(sd->privacy_led) != -ENOENT)
return dev_err_probe(sd->dev, PTR_ERR(sd->privacy_led),
"getting privacy LED\n");
if (!IS_ERR_OR_NULL(sd->privacy_led)) {
mutex_lock(&sd->privacy_led->led_access);
led_sysfs_disable(sd->privacy_led);
led_trigger_remove(sd->privacy_led);
led_set_brightness(sd->privacy_led, 0);
mutex_unlock(&sd->privacy_led->led_access);
}
#endif
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_subdev_get_privacy_led);
void v4l2_subdev_put_privacy_led(struct v4l2_subdev *sd)
{
#if IS_REACHABLE(CONFIG_LEDS_CLASS)
if (!IS_ERR_OR_NULL(sd->privacy_led)) {
mutex_lock(&sd->privacy_led->led_access);
led_sysfs_enable(sd->privacy_led);
mutex_unlock(&sd->privacy_led->led_access);
led_put(sd->privacy_led);
}
#endif
}
EXPORT_SYMBOL_GPL(v4l2_subdev_put_privacy_led);
......@@ -239,6 +239,17 @@ static ssize_t mlxreg_hotplug_attr_show(struct device *dev,
#define PRIV_ATTR(i) priv->mlxreg_hotplug_attr[i]
#define PRIV_DEV_ATTR(i) priv->mlxreg_hotplug_dev_attr[i]
static int mlxreg_hotplug_item_label_index_get(u32 mask, u32 bit)
{
int i, j;
for (i = 0, j = -1; i <= bit; i++) {
if (mask & BIT(i))
j++;
}
return j;
}
static int mlxreg_hotplug_attr_init(struct mlxreg_hotplug_priv_data *priv)
{
struct mlxreg_core_hotplug_platform_data *pdata;
......@@ -246,7 +257,7 @@ static int mlxreg_hotplug_attr_init(struct mlxreg_hotplug_priv_data *priv)
struct mlxreg_core_data *data;
unsigned long mask;
u32 regval;
int num_attrs = 0, id = 0, i, j, k, ret;
int num_attrs = 0, id = 0, i, j, k, count, ret;
pdata = dev_get_platdata(&priv->pdev->dev);
item = pdata->items;
......@@ -272,7 +283,8 @@ static int mlxreg_hotplug_attr_init(struct mlxreg_hotplug_priv_data *priv)
/* Go over all unmasked units within item. */
mask = item->mask;
k = 0;
for_each_set_bit(j, &mask, item->count) {
count = item->ind ? item->ind : item->count;
for_each_set_bit(j, &mask, count) {
if (data->capability) {
/*
* Read capability register and skip non
......@@ -282,16 +294,17 @@ static int mlxreg_hotplug_attr_init(struct mlxreg_hotplug_priv_data *priv)
data->capability, &regval);
if (ret)
return ret;
if (!(regval & data->bit)) {
data++;
continue;
}
}
PRIV_ATTR(id) = &PRIV_DEV_ATTR(id).dev_attr.attr;
PRIV_ATTR(id)->name = devm_kasprintf(&priv->pdev->dev,
GFP_KERNEL,
data->label);
if (!PRIV_ATTR(id)->name) {
dev_err(priv->dev, "Memory allocation failed for attr %d.\n",
id);
......@@ -365,9 +378,14 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
regval &= item->mask;
asserted = item->cache ^ regval;
item->cache = regval;
for_each_set_bit(bit, &asserted, 8) {
data = item->data + bit;
int pos;
pos = mlxreg_hotplug_item_label_index_get(item->mask, bit);
if (pos < 0)
goto out;
data = item->data + pos;
if (regval & BIT(bit)) {
if (item->inversed)
mlxreg_hotplug_device_destroy(priv, data, item->kind);
......
......@@ -136,9 +136,9 @@ int ssam_device_add(struct ssam_device *sdev)
* is always valid and can be used for requests as long as the client
* device we add here is registered as child under it. This essentially
* guarantees that the client driver can always expect the preconditions
* for functions like ssam_request_sync (controller has to be started
* and is not suspended) to hold and thus does not have to check for
* them.
* for functions like ssam_request_do_sync() (controller has to be
* started and is not suspended) to hold and thus does not have to check
* for them.
*
* Note that for this to work, the controller has to be a parent device.
* If it is not a direct parent, care has to be taken that the device is
......
......@@ -994,7 +994,7 @@ static void ssam_handle_event(struct ssh_rtl *rtl,
item->rqid = get_unaligned_le16(&cmd->rqid);
item->event.target_category = cmd->tc;
item->event.target_id = cmd->tid_in;
item->event.target_id = cmd->sid;
item->event.command_id = cmd->cid;
item->event.instance_id = cmd->iid;
memcpy(&item->event.data[0], data->ptr, data->len);
......@@ -1674,7 +1674,7 @@ int ssam_request_sync_submit(struct ssam_controller *ctrl,
EXPORT_SYMBOL_GPL(ssam_request_sync_submit);
/**
* ssam_request_sync() - Execute a synchronous request.
* ssam_request_do_sync() - Execute a synchronous request.
* @ctrl: The controller via which the request will be submitted.
* @spec: The request specification and payload.
* @rsp: The response buffer.
......@@ -1686,7 +1686,7 @@ EXPORT_SYMBOL_GPL(ssam_request_sync_submit);
*
* Return: Returns the status of the request or any failure during setup.
*/
int ssam_request_sync(struct ssam_controller *ctrl,
int ssam_request_do_sync(struct ssam_controller *ctrl,
const struct ssam_request *spec,
struct ssam_response *rsp)
{
......@@ -1722,10 +1722,10 @@ int ssam_request_sync(struct ssam_controller *ctrl,
ssam_request_sync_free(rqst);
return status;
}
EXPORT_SYMBOL_GPL(ssam_request_sync);
EXPORT_SYMBOL_GPL(ssam_request_do_sync);
/**
* ssam_request_sync_with_buffer() - Execute a synchronous request with the
* ssam_request_do_sync_with_buffer() - Execute a synchronous request with the
* provided buffer as back-end for the message buffer.
* @ctrl: The controller via which the request will be submitted.
* @spec: The request specification and payload.
......@@ -1738,14 +1738,14 @@ EXPORT_SYMBOL_GPL(ssam_request_sync);
* SSH_COMMAND_MESSAGE_LENGTH() macro can be used to compute the required
* message buffer size.
*
* This function does essentially the same as ssam_request_sync(), but instead
* of dynamically allocating the request and message data buffer, it uses the
* provided message data buffer and stores the (small) request struct on the
* heap.
* This function does essentially the same as ssam_request_do_sync(), but
* instead of dynamically allocating the request and message data buffer, it
* uses the provided message data buffer and stores the (small) request struct
* on the heap.
*
* Return: Returns the status of the request or any failure during setup.
*/
int ssam_request_sync_with_buffer(struct ssam_controller *ctrl,
int ssam_request_do_sync_with_buffer(struct ssam_controller *ctrl,
const struct ssam_request *spec,
struct ssam_response *rsp,
struct ssam_span *buf)
......@@ -1772,42 +1772,42 @@ int ssam_request_sync_with_buffer(struct ssam_controller *ctrl,
return status;
}
EXPORT_SYMBOL_GPL(ssam_request_sync_with_buffer);
EXPORT_SYMBOL_GPL(ssam_request_do_sync_with_buffer);
/* -- Internal SAM requests. ------------------------------------------------ */
SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_get_firmware_version, __le32, {
.target_category = SSAM_SSH_TC_SAM,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x13,
.instance_id = 0x00,
});
SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_display_off, u8, {
.target_category = SSAM_SSH_TC_SAM,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x15,
.instance_id = 0x00,
});
SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_display_on, u8, {
.target_category = SSAM_SSH_TC_SAM,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x16,
.instance_id = 0x00,
});
SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_d0_exit, u8, {
.target_category = SSAM_SSH_TC_SAM,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x33,
.instance_id = 0x00,
});
SSAM_DEFINE_SYNC_REQUEST_R(ssam_ssh_notif_d0_entry, u8, {
.target_category = SSAM_SSH_TC_SAM,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x34,
.instance_id = 0x00,
});
......@@ -1864,7 +1864,7 @@ static int __ssam_ssh_event_request(struct ssam_controller *ctrl,
result.length = 0;
result.pointer = &buf;
status = ssam_retry(ssam_request_sync_onstack, ctrl, &rqst, &result,
status = ssam_retry(ssam_request_do_sync_onstack, ctrl, &rqst, &result,
sizeof(params));
return status < 0 ? status : buf;
......
......@@ -189,8 +189,8 @@ static inline void msgb_push_cmd(struct msgbuf *msgb, u8 seq, u16 rqid,
__msgb_push_u8(msgb, SSH_PLD_TYPE_CMD); /* Payload type. */
__msgb_push_u8(msgb, rqst->target_category); /* Target category. */
__msgb_push_u8(msgb, rqst->target_id); /* Target ID (out). */
__msgb_push_u8(msgb, 0x00); /* Target ID (in). */
__msgb_push_u8(msgb, rqst->target_id); /* Target ID. */
__msgb_push_u8(msgb, SSAM_SSH_TID_HOST); /* Source ID. */
__msgb_push_u8(msgb, rqst->instance_id); /* Instance ID. */
__msgb_push_u16(msgb, rqid); /* Request ID. */
__msgb_push_u8(msgb, rqst->command_id); /* Command ID. */
......
......@@ -920,13 +920,14 @@ static void ssh_rtl_rx_command(struct ssh_ptl *p, const struct ssam_span *data)
* Check if the message was intended for us. If not, drop it.
*
* Note: We will need to change this to handle debug messages. On newer
* generation devices, these seem to be sent to tid_out=0x03. We as
* host can still receive them as they can be forwarded via an override
* option on SAM, but doing so does not change tid_out=0x00.
* generation devices, these seem to be sent to SSAM_SSH_TID_DEBUG. We
* as host can still receive them as they can be forwarded via an
* override option on SAM, but doing so does not change the target ID
* to SSAM_SSH_TID_HOST.
*/
if (command->tid_out != 0x00) {
if (command->tid != SSAM_SSH_TID_HOST) {
rtl_warn(rtl, "rtl: dropping message not intended for us (tid = %#04x)\n",
command->tid_out);
command->tid);
return;
}
......
......@@ -96,6 +96,7 @@ TRACE_DEFINE_ENUM(SSAM_SSH_TC_POS);
#define SSAM_SEQ_NOT_APPLICABLE ((u16)-1)
#define SSAM_RQID_NOT_APPLICABLE ((u32)-1)
#define SSAM_SSH_TC_NOT_APPLICABLE 0
#define SSAM_SSH_TID_NOT_APPLICABLE ((u8)-1)
#ifndef _SURFACE_AGGREGATOR_TRACE_HELPERS
#define _SURFACE_AGGREGATOR_TRACE_HELPERS
......@@ -150,12 +151,44 @@ static inline u32 ssam_trace_get_request_id(const struct ssh_packet *p)
return get_unaligned_le16(&p->data.ptr[SSH_MSGOFFSET_COMMAND(rqid)]);
}
/**
* ssam_trace_get_request_tid() - Read the packet's request target ID.
* @p: The packet.
*
* Return: Returns the packet's request target ID (TID) field if the packet
* represents a request with command data, or %SSAM_SSH_TID_NOT_APPLICABLE
* if not (e.g. flush request, control packet).
*/
static inline u32 ssam_trace_get_request_tid(const struct ssh_packet *p)
{
if (!p->data.ptr || p->data.len < SSH_COMMAND_MESSAGE_LENGTH(0))
return SSAM_SSH_TID_NOT_APPLICABLE;
return get_unaligned_le16(&p->data.ptr[SSH_MSGOFFSET_COMMAND(tid)]);
}
/**
* ssam_trace_get_request_sid() - Read the packet's request source ID.
* @p: The packet.
*
* Return: Returns the packet's request source ID (SID) field if the packet
* represents a request with command data, or %SSAM_SSH_TID_NOT_APPLICABLE
* if not (e.g. flush request, control packet).
*/
static inline u32 ssam_trace_get_request_sid(const struct ssh_packet *p)
{
if (!p->data.ptr || p->data.len < SSH_COMMAND_MESSAGE_LENGTH(0))
return SSAM_SSH_TID_NOT_APPLICABLE;
return get_unaligned_le16(&p->data.ptr[SSH_MSGOFFSET_COMMAND(sid)]);
}
/**
* ssam_trace_get_request_tc() - Read the packet's request target category.
* @p: The packet.
*
* Return: Returns the packet's request target category (TC) field if the
* packet represents a request with command data, or %SSAM_TC_NOT_APPLICABLE
* packet represents a request with command data, or %SSAM_SSH_TC_NOT_APPLICABLE
* if not (e.g. flush request, control packet).
*/
static inline u32 ssam_trace_get_request_tc(const struct ssh_packet *p)
......@@ -232,8 +265,18 @@ static inline u32 ssam_trace_get_request_tc(const struct ssh_packet *p)
{ SSAM_RQID_NOT_APPLICABLE, "N/A" } \
)
#define ssam_show_ssh_tc(rqid) \
__print_symbolic(rqid, \
#define ssam_show_ssh_tid(tid) \
__print_symbolic(tid, \
{ SSAM_SSH_TID_NOT_APPLICABLE, "N/A" }, \
{ SSAM_SSH_TID_HOST, "Host" }, \
{ SSAM_SSH_TID_SAM, "SAM" }, \
{ SSAM_SSH_TID_KIP, "KIP" }, \
{ SSAM_SSH_TID_DEBUG, "Debug" }, \
{ SSAM_SSH_TID_SURFLINK, "SurfLink" } \
)
#define ssam_show_ssh_tc(tc) \
__print_symbolic(tc, \
{ SSAM_SSH_TC_NOT_APPLICABLE, "N/A" }, \
{ SSAM_SSH_TC_SAM, "SAM" }, \
{ SSAM_SSH_TC_BAT, "BAT" }, \
......@@ -313,6 +356,8 @@ DECLARE_EVENT_CLASS(ssam_command_class,
TP_STRUCT__entry(
__field(u16, rqid)
__field(u16, len)
__field(u8, tid)
__field(u8, sid)
__field(u8, tc)
__field(u8, cid)
__field(u8, iid)
......@@ -320,14 +365,18 @@ DECLARE_EVENT_CLASS(ssam_command_class,
TP_fast_assign(
__entry->rqid = get_unaligned_le16(&cmd->rqid);
__entry->tid = cmd->tid;
__entry->sid = cmd->sid;
__entry->tc = cmd->tc;
__entry->cid = cmd->cid;
__entry->iid = cmd->iid;
__entry->len = len;
),
TP_printk("rqid=%#06x, tc=%s, cid=%#04x, iid=%#04x, len=%u",
TP_printk("rqid=%#06x, tid=%s, sid=%s, tc=%s, cid=%#04x, iid=%#04x, len=%u",
__entry->rqid,
ssam_show_ssh_tid(__entry->tid),
ssam_show_ssh_tid(__entry->sid),
ssam_show_ssh_tc(__entry->tc),
__entry->cid,
__entry->iid,
......@@ -430,6 +479,8 @@ DECLARE_EVENT_CLASS(ssam_request_class,
__field(u8, tc)
__field(u16, cid)
__field(u16, iid)
__field(u8, tid)
__field(u8, sid)
),
TP_fast_assign(
......@@ -439,16 +490,20 @@ DECLARE_EVENT_CLASS(ssam_request_class,
__entry->state = READ_ONCE(request->state);
__entry->rqid = ssam_trace_get_request_id(p);
ssam_trace_ptr_uid(p, __entry->uid);
__entry->tid = ssam_trace_get_request_tid(p);
__entry->sid = ssam_trace_get_request_sid(p);
__entry->tc = ssam_trace_get_request_tc(p);
__entry->cid = ssam_trace_get_command_field_u8(p, cid);
__entry->iid = ssam_trace_get_command_field_u8(p, iid);
),
TP_printk("uid=%s, rqid=%s, ty=%s, sta=%s, tc=%s, cid=%s, iid=%s",
TP_printk("uid=%s, rqid=%s, ty=%s, sta=%s, tid=%s, sid=%s, tc=%s, cid=%s, iid=%s",
__entry->uid,
ssam_show_request_id(__entry->rqid),
ssam_show_request_type(__entry->state),
ssam_show_request_state(__entry->state),
ssam_show_ssh_tid(__entry->tid),
ssam_show_ssh_tid(__entry->sid),
ssam_show_ssh_tc(__entry->tc),
ssam_show_generic_u8_field(__entry->cid),
ssam_show_generic_u8_field(__entry->iid)
......@@ -474,6 +529,8 @@ DECLARE_EVENT_CLASS(ssam_request_status_class,
__field(u8, tc)
__field(u16, cid)
__field(u16, iid)
__field(u8, tid)
__field(u8, sid)
),
TP_fast_assign(
......@@ -484,16 +541,20 @@ DECLARE_EVENT_CLASS(ssam_request_status_class,
__entry->rqid = ssam_trace_get_request_id(p);
__entry->status = status;
ssam_trace_ptr_uid(p, __entry->uid);
__entry->tid = ssam_trace_get_request_tid(p);
__entry->sid = ssam_trace_get_request_sid(p);
__entry->tc = ssam_trace_get_request_tc(p);
__entry->cid = ssam_trace_get_command_field_u8(p, cid);
__entry->iid = ssam_trace_get_command_field_u8(p, iid);
),
TP_printk("uid=%s, rqid=%s, ty=%s, sta=%s, tc=%s, cid=%s, iid=%s, status=%d",
TP_printk("uid=%s, rqid=%s, ty=%s, sta=%s, tid=%s, sid=%s, tc=%s, cid=%s, iid=%s, status=%d",
__entry->uid,
ssam_show_request_id(__entry->rqid),
ssam_show_request_type(__entry->state),
ssam_show_request_state(__entry->state),
ssam_show_ssh_tid(__entry->tid),
ssam_show_ssh_tid(__entry->sid),
ssam_show_ssh_tc(__entry->tc),
ssam_show_generic_u8_field(__entry->cid),
ssam_show_generic_u8_field(__entry->iid),
......
......@@ -590,7 +590,7 @@ static acpi_status san_rqst(struct san_data *d, struct gsb_buffer *buffer)
return san_rqst_fixup_suspended(d, &rqst, buffer);
}
status = __ssam_retry(ssam_request_sync_onstack, SAN_REQUEST_NUM_TRIES,
status = __ssam_retry(ssam_request_do_sync_onstack, SAN_REQUEST_NUM_TRIES,
d->ctrl, &rqst, &rsp, SAN_GSB_MAX_RQSX_PAYLOAD);
if (!status) {
......
......@@ -302,8 +302,8 @@ static long ssam_cdev_request(struct ssam_cdev_client *client, struct ssam_cdev_
* theoretical maximum (SSH_COMMAND_MAX_PAYLOAD_SIZE) of the
* underlying protocol (note that nothing remotely this size
* should ever be allocated in any normal case). This size is
* validated later in ssam_request_sync(), for allocation the
* bound imposed by u16 should be enough.
* validated later in ssam_request_do_sync(), for allocation
* the bound imposed by u16 should be enough.
*/
spec.payload = kzalloc(spec.length, GFP_KERNEL);
if (!spec.payload) {
......@@ -342,7 +342,7 @@ static long ssam_cdev_request(struct ssam_cdev_client *client, struct ssam_cdev_
}
/* Perform request. */
status = ssam_request_sync(client->cdev->ctrl, &spec, &rsp);
status = ssam_request_do_sync(client->cdev->ctrl, &spec, &rsp);
if (status)
goto out;
......
......@@ -214,7 +214,7 @@ static void ssam_hub_remove(struct ssam_device *sdev)
SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_query_opmode, u8, {
.target_category = SSAM_SSH_TC_BAS,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x0d,
.instance_id = 0x00,
});
......@@ -292,7 +292,7 @@ static const struct ssam_hub_desc base_hub = {
SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_query_state, u8, {
.target_category = SSAM_SSH_TC_KIP,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x2c,
.instance_id = 0x00,
});
......@@ -348,8 +348,8 @@ static const struct ssam_hub_desc kip_hub = {
/* -- Driver registration. -------------------------------------------------- */
static const struct ssam_device_id ssam_hub_match[] = {
{ SSAM_VDEV(HUB, 0x01, SSAM_SSH_TC_KIP, 0x00), (unsigned long)&kip_hub },
{ SSAM_VDEV(HUB, 0x02, SSAM_SSH_TC_BAS, 0x00), (unsigned long)&base_hub },
{ SSAM_VDEV(HUB, SAM, SSAM_SSH_TC_KIP, 0x00), (unsigned long)&kip_hub },
{ SSAM_VDEV(HUB, SAM, SSAM_SSH_TC_BAS, 0x00), (unsigned long)&base_hub },
{ }
};
MODULE_DEVICE_TABLE(ssam, ssam_hub_match);
......
......@@ -46,7 +46,7 @@ static const struct software_node ssam_node_hub_kip = {
/* Base device hub (devices attached to Surface Book 3 base). */
static const struct software_node ssam_node_hub_base = {
.name = "ssam:00:00:02:11:00",
.name = "ssam:00:00:01:11:00",
.parent = &ssam_node_root,
};
......
......@@ -247,7 +247,7 @@ static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 s
SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_get_cover_state, u8, {
.target_category = SSAM_SSH_TC_KIP,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x1d,
.instance_id = 0x00,
});
......@@ -371,7 +371,7 @@ static int ssam_pos_get_sources_list(struct ssam_tablet_sw *sw, struct ssam_sour
int status;
rqst.target_category = SSAM_SSH_TC_POS;
rqst.target_id = 0x01;
rqst.target_id = SSAM_SSH_TID_SAM;
rqst.command_id = 0x01;
rqst.instance_id = 0x00;
rqst.flags = SSAM_REQUEST_HAS_RESPONSE;
......@@ -382,7 +382,7 @@ static int ssam_pos_get_sources_list(struct ssam_tablet_sw *sw, struct ssam_sour
rsp.length = 0;
rsp.pointer = (u8 *)sources;
status = ssam_retry(ssam_request_sync_onstack, sw->sdev->ctrl, &rqst, &rsp, 0);
status = ssam_retry(ssam_request_do_sync_onstack, sw->sdev->ctrl, &rqst, &rsp, 0);
if (status)
return status;
......@@ -430,7 +430,7 @@ static int ssam_pos_get_source(struct ssam_tablet_sw *sw, u32 *source_id)
SSAM_DEFINE_SYNC_REQUEST_WR(__ssam_pos_get_posture_for_source, __le32, __le32, {
.target_category = SSAM_SSH_TC_POS,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x02,
.instance_id = 0x00,
});
......@@ -510,8 +510,8 @@ static const struct ssam_tablet_sw_desc ssam_pos_sw_desc = {
/* -- Driver registration. -------------------------------------------------- */
static const struct ssam_device_id ssam_tablet_sw_match[] = {
{ SSAM_SDEV(KIP, 0x01, 0x00, 0x01), (unsigned long)&ssam_kip_sw_desc },
{ SSAM_SDEV(POS, 0x01, 0x00, 0x01), (unsigned long)&ssam_pos_sw_desc },
{ SSAM_SDEV(KIP, SAM, 0x00, 0x01), (unsigned long)&ssam_kip_sw_desc },
{ SSAM_SDEV(POS, SAM, 0x00, 0x01), (unsigned long)&ssam_pos_sw_desc },
{ },
};
MODULE_DEVICE_TABLE(ssam, ssam_tablet_sw_match);
......
......@@ -71,63 +71,63 @@ static_assert(sizeof(struct ssam_bas_base_info) == 2);
SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_lock, {
.target_category = SSAM_SSH_TC_BAS,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x06,
.instance_id = 0x00,
});
SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_unlock, {
.target_category = SSAM_SSH_TC_BAS,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x07,
.instance_id = 0x00,
});
SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_request, {
.target_category = SSAM_SSH_TC_BAS,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x08,
.instance_id = 0x00,
});
SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_confirm, {
.target_category = SSAM_SSH_TC_BAS,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x09,
.instance_id = 0x00,
});
SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_heartbeat, {
.target_category = SSAM_SSH_TC_BAS,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x0a,
.instance_id = 0x00,
});
SSAM_DEFINE_SYNC_REQUEST_N(ssam_bas_latch_cancel, {
.target_category = SSAM_SSH_TC_BAS,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x0b,
.instance_id = 0x00,
});
SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_get_base, struct ssam_bas_base_info, {
.target_category = SSAM_SSH_TC_BAS,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x0c,
.instance_id = 0x00,
});
SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_get_device_mode, u8, {
.target_category = SSAM_SSH_TC_BAS,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x0d,
.instance_id = 0x00,
});
SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_get_latch_status, u8, {
.target_category = SSAM_SSH_TC_BAS,
.target_id = 0x01,
.target_id = SSAM_SSH_TID_SAM,
.command_id = 0x11,
.instance_id = 0x00,
});
......@@ -1214,7 +1214,7 @@ static void surface_dtx_ssam_remove(struct ssam_device *sdev)
}
static const struct ssam_device_id surface_dtx_ssam_match[] = {
{ SSAM_SDEV(BAS, 0x01, 0x00, 0x00) },
{ SSAM_SDEV(BAS, SAM, 0x00, 0x00) },
{ },
};
MODULE_DEVICE_TABLE(ssam, surface_dtx_ssam_match);
......
......@@ -101,18 +101,12 @@ static void shps_dsm_notify_irq(struct platform_device *pdev, enum shps_irq_type
param.type = ACPI_TYPE_INTEGER;
param.integer.value = value;
result = acpi_evaluate_dsm(handle, &shps_dsm_guid, SHPS_DSM_REVISION,
shps_dsm_fn_for_irq(type), &param);
result = acpi_evaluate_dsm_typed(handle, &shps_dsm_guid, SHPS_DSM_REVISION,
shps_dsm_fn_for_irq(type), &param, ACPI_TYPE_BUFFER);
if (!result) {
dev_err(&pdev->dev, "IRQ notification via DSM failed (irq=%d, gpio=%d)\n",
type, value);
} else if (result->type != ACPI_TYPE_BUFFER) {
dev_err(&pdev->dev,
"IRQ notification via DSM failed: unexpected result type (irq=%d, gpio=%d)\n",
type, value);
} else if (result->buffer.length != 1 || result->buffer.pointer[0] != 0) {
dev_err(&pdev->dev,
"IRQ notification via DSM failed: unexpected result value (irq=%d, gpio=%d)\n",
......@@ -121,7 +115,6 @@ static void shps_dsm_notify_irq(struct platform_device *pdev, enum shps_irq_type
mutex_unlock(&sdev->lock[type]);
if (result)
ACPI_FREE(result);
}
......
......@@ -169,7 +169,7 @@ static void surface_platform_profile_remove(struct ssam_device *sdev)
}
static const struct ssam_device_id ssam_platform_profile_match[] = {
{ SSAM_SDEV(TMP, 0x01, 0x00, 0x01) },
{ SSAM_SDEV(TMP, SAM, 0x00, 0x01) },
{ },
};
MODULE_DEVICE_TABLE(ssam, ssam_platform_profile_match);
......
......@@ -214,7 +214,6 @@ config APPLE_GMUX
depends on PNP
depends on BACKLIGHT_CLASS_DEVICE
depends on BACKLIGHT_APPLE=n || BACKLIGHT_APPLE
depends on ACPI_VIDEO=n || ACPI_VIDEO
help
This driver provides support for the gmux device found on many
Apple laptops, which controls the display mux for the hybrid
......
......@@ -544,11 +544,6 @@ static int acerhdf_probe(struct platform_device *device)
return 0;
}
static int acerhdf_remove(struct platform_device *device)
{
return 0;
}
static const struct dev_pm_ops acerhdf_pm_ops = {
.suspend = acerhdf_suspend,
.freeze = acerhdf_suspend,
......@@ -560,7 +555,6 @@ static struct platform_driver acerhdf_driver = {
.pm = &acerhdf_pm_ops,
},
.probe = acerhdf_probe,
.remove = acerhdf_remove,
};
/* check hardware */
......
......@@ -43,6 +43,7 @@
#define AMD_PMC_STB_S2IDLE_PREPARE 0xC6000001
#define AMD_PMC_STB_S2IDLE_RESTORE 0xC6000002
#define AMD_PMC_STB_S2IDLE_CHECK 0xC6000003
#define AMD_PMC_STB_DUMMY_PC 0xC6000007
/* STB S2D(Spill to DRAM) has different message port offset */
#define STB_SPILL_TO_DRAM 0xBE
......@@ -104,6 +105,7 @@
#define DELAY_MIN_US 2000
#define DELAY_MAX_US 3000
#define FIFO_SIZE 4096
enum amd_pmc_def {
MSG_TEST = 0x01,
MSG_OS_HINT_PCO,
......@@ -114,6 +116,7 @@ enum s2d_arg {
S2D_TELEMETRY_SIZE = 0x01,
S2D_PHYS_ADDR_LOW,
S2D_PHYS_ADDR_HIGH,
S2D_NUM_SAMPLES,
};
struct amd_pmc_bit_map {
......@@ -246,13 +249,40 @@ static const struct file_operations amd_pmc_stb_debugfs_fops = {
static int amd_pmc_stb_debugfs_open_v2(struct inode *inode, struct file *filp)
{
struct amd_pmc_dev *dev = filp->f_inode->i_private;
u32 *buf;
u32 *buf, fsize, num_samples, stb_rdptr_offset = 0;
int ret;
/* Write dummy postcode while reading the STB buffer */
ret = amd_pmc_write_stb(dev, AMD_PMC_STB_DUMMY_PC);
if (ret)
dev_err(dev->dev, "error writing to STB: %d\n", ret);
buf = kzalloc(S2D_TELEMETRY_BYTES_MAX, GFP_KERNEL);
if (!buf)
return -ENOMEM;
memcpy_fromio(buf, dev->stb_virt_addr, S2D_TELEMETRY_BYTES_MAX);
/* Spill to DRAM num_samples uses separate SMU message port */
dev->msg_port = 1;
/* Get the num_samples to calculate the last push location */
ret = amd_pmc_send_cmd(dev, S2D_NUM_SAMPLES, &num_samples, STB_SPILL_TO_DRAM, 1);
/* Clear msg_port for other SMU operation */
dev->msg_port = 0;
if (ret) {
dev_err(dev->dev, "error: S2D_NUM_SAMPLES not supported : %d\n", ret);
return ret;
}
/* Start capturing data from the last push location */
if (num_samples > S2D_TELEMETRY_BYTES_MAX) {
fsize = S2D_TELEMETRY_BYTES_MAX;
stb_rdptr_offset = num_samples - fsize;
} else {
fsize = num_samples;
stb_rdptr_offset = 0;
}
memcpy_fromio(buf, dev->stb_virt_addr + stb_rdptr_offset, fsize);
filp->private_data = buf;
return 0;
......@@ -560,13 +590,13 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev)
}
value = amd_pmc_reg_read(dev, response);
dev_dbg(dev->dev, "AMD_PMC_REGISTER_RESPONSE:%x\n", value);
dev_dbg(dev->dev, "AMD_%s_REGISTER_RESPONSE:%x\n", dev->msg_port ? "S2D" : "PMC", value);
value = amd_pmc_reg_read(dev, argument);
dev_dbg(dev->dev, "AMD_PMC_REGISTER_ARGUMENT:%x\n", value);
dev_dbg(dev->dev, "AMD_%s_REGISTER_ARGUMENT:%x\n", dev->msg_port ? "S2D" : "PMC", value);
value = amd_pmc_reg_read(dev, message);
dev_dbg(dev->dev, "AMD_PMC_REGISTER_MESSAGE:%x\n", value);
dev_dbg(dev->dev, "AMD_%s_REGISTER_MESSAGE:%x\n", dev->msg_port ? "S2D" : "PMC", value);
}
static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret)
......
......@@ -6,6 +6,7 @@
config AMD_PMF
tristate "AMD Platform Management Framework"
depends on ACPI && PCI
depends on POWER_SUPPLY
select ACPI_PLATFORM_PROFILE
help
This driver provides support for the AMD Platform Management Framework.
......
......@@ -192,12 +192,12 @@ config DELL_WMI_DESCRIPTOR
config DELL_WMI_DDV
tristate "Dell WMI sensors Support"
default m
depends on ACPI_BATTERY
depends on ACPI_WMI
depends on ACPI_BATTERY || HWMON
help
This option adds support for WMI-based sensors like
battery temperature sensors found on some Dell notebooks.
It also supports reading of the battery ePPID.
This option adds support for WMI-based fan and thermal sensors
found on some Dell notebooks. It also supports various WMI-based battery
extras like reading of the battery temperature and ePPID.
To compile this drivers as a module, choose M here: the module will
be called dell-wmi-ddv.
......
......@@ -67,10 +67,7 @@ static ssize_t smo8800_misc_read(struct file *file, char __user *buf,
retval = 1;
if (data < 255)
byte_data = data;
else
byte_data = 255;
byte_data = min_t(u32, data, 255);
if (put_user(byte_data, buf))
retval = -EFAULT;
......
......@@ -10,28 +10,43 @@
#include <linux/acpi.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/device/driver.h>
#include <linux/dev_printk.h>
#include <linux/errno.h>
#include <linux/kconfig.h>
#include <linux/kernel.h>
#include <linux/hwmon.h>
#include <linux/kstrtox.h>
#include <linux/math.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/limits.h>
#include <linux/pm.h>
#include <linux/power_supply.h>
#include <linux/printk.h>
#include <linux/seq_file.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#include <linux/wmi.h>
#include <acpi/battery.h>
#include <asm/unaligned.h>
#define DRIVER_NAME "dell-wmi-ddv"
#define DELL_DDV_SUPPORTED_INTERFACE 2
#define DELL_DDV_SUPPORTED_VERSION_MIN 2
#define DELL_DDV_SUPPORTED_VERSION_MAX 3
#define DELL_DDV_GUID "8A42EA14-4F2A-FD45-6422-0087F7A7E608"
#define DELL_EPPID_LENGTH 20
#define DELL_EPPID_EXT_LENGTH 23
static bool force;
module_param_unsafe(force, bool, 0);
MODULE_PARM_DESC(force, "Force loading without checking for supported WMI interface versions");
enum dell_ddv_method {
DELL_DDV_BATTERY_DESIGN_CAPACITY = 0x01,
DELL_DDV_BATTERY_FULL_CHARGE_CAPACITY = 0x02,
......@@ -49,6 +64,7 @@ enum dell_ddv_method {
DELL_DDV_BATTERY_RAW_ANALYTICS_START = 0x0E,
DELL_DDV_BATTERY_RAW_ANALYTICS = 0x0F,
DELL_DDV_BATTERY_DESIGN_VOLTAGE = 0x10,
DELL_DDV_BATTERY_RAW_ANALYTICS_A_BLOCK = 0x11, /* version 3 */
DELL_DDV_INTERFACE_VERSION = 0x12,
......@@ -56,13 +72,63 @@ enum dell_ddv_method {
DELL_DDV_THERMAL_SENSOR_INFORMATION = 0x22,
};
struct fan_sensor_entry {
u8 type;
__le16 rpm;
} __packed;
struct thermal_sensor_entry {
u8 type;
s8 now;
s8 min;
s8 max;
u8 unknown;
} __packed;
struct combined_channel_info {
struct hwmon_channel_info info;
u32 config[];
};
struct combined_chip_info {
struct hwmon_chip_info chip;
const struct hwmon_channel_info *info[];
};
struct dell_wmi_ddv_sensors {
struct mutex lock; /* protect caching */
unsigned long timestamp;
union acpi_object *obj;
u64 entries;
};
struct dell_wmi_ddv_data {
struct acpi_battery_hook hook;
struct device_attribute temp_attr;
struct device_attribute eppid_attr;
struct dell_wmi_ddv_sensors fans;
struct dell_wmi_ddv_sensors temps;
struct wmi_device *wdev;
};
static const char * const fan_labels[] = {
"CPU Fan",
"Chassis Motherboard Fan",
"Video Fan",
"Power Supply Fan",
"Chipset Fan",
"Memory Fan",
"PCI Fan",
"HDD Fan",
};
static const char * const fan_dock_labels[] = {
"Docking Chassis/Motherboard Fan",
"Docking Video Fan",
"Docking Power Supply Fan",
"Docking Chipset Fan",
};
static int dell_wmi_ddv_query_type(struct wmi_device *wdev, enum dell_ddv_method method, u32 arg,
union acpi_object **result, acpi_object_type type)
{
......@@ -84,7 +150,7 @@ static int dell_wmi_ddv_query_type(struct wmi_device *wdev, enum dell_ddv_method
if (obj->type != type) {
kfree(obj);
return -EIO;
return -ENOMSG;
}
*result = obj;
......@@ -123,21 +189,27 @@ static int dell_wmi_ddv_query_buffer(struct wmi_device *wdev, enum dell_ddv_meth
if (ret < 0)
return ret;
if (obj->package.count != 2)
goto err_free;
if (obj->package.count != 2 ||
obj->package.elements[0].type != ACPI_TYPE_INTEGER ||
obj->package.elements[1].type != ACPI_TYPE_BUFFER) {
ret = -ENOMSG;
if (obj->package.elements[0].type != ACPI_TYPE_INTEGER)
goto err_free;
}
buffer_size = obj->package.elements[0].integer.value;
if (obj->package.elements[1].type != ACPI_TYPE_BUFFER)
if (!buffer_size) {
ret = -ENODATA;
goto err_free;
}
if (buffer_size > obj->package.elements[1].buffer.length) {
dev_warn(&wdev->dev,
FW_WARN "WMI buffer size (%llu) exceeds ACPI buffer size (%d)\n",
buffer_size, obj->package.elements[1].buffer.length);
ret = -EMSGSIZE;
goto err_free;
}
......@@ -149,7 +221,7 @@ static int dell_wmi_ddv_query_buffer(struct wmi_device *wdev, enum dell_ddv_meth
err_free:
kfree(obj);
return -EIO;
return ret;
}
static int dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_method method,
......@@ -158,6 +230,410 @@ static int dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_meth
return dell_wmi_ddv_query_type(wdev, method, arg, result, ACPI_TYPE_STRING);
}
/*
* Needs to be called with lock held, except during initialization.
*/
static int dell_wmi_ddv_update_sensors(struct wmi_device *wdev, enum dell_ddv_method method,
struct dell_wmi_ddv_sensors *sensors, size_t entry_size)
{
u64 buffer_size, rem, entries;
union acpi_object *obj;
u8 *buffer;
int ret;
if (sensors->obj) {
if (time_before(jiffies, sensors->timestamp + HZ))
return 0;
kfree(sensors->obj);
sensors->obj = NULL;
}
ret = dell_wmi_ddv_query_buffer(wdev, method, 0, &obj);
if (ret < 0)
return ret;
/* buffer format sanity check */
buffer_size = obj->package.elements[0].integer.value;
buffer = obj->package.elements[1].buffer.pointer;
entries = div64_u64_rem(buffer_size, entry_size, &rem);
if (rem != 1 || buffer[buffer_size - 1] != 0xff) {
ret = -ENOMSG;
goto err_free;
}
if (!entries) {
ret = -ENODATA;
goto err_free;
}
sensors->obj = obj;
sensors->entries = entries;
sensors->timestamp = jiffies;
return 0;
err_free:
kfree(obj);
return ret;
}
static umode_t dell_wmi_ddv_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
int channel)
{
return 0444;
}
static int dell_wmi_ddv_fan_read_channel(struct dell_wmi_ddv_data *data, u32 attr, int channel,
long *val)
{
struct fan_sensor_entry *entry;
int ret;
ret = dell_wmi_ddv_update_sensors(data->wdev, DELL_DDV_FAN_SENSOR_INFORMATION,
&data->fans, sizeof(*entry));
if (ret < 0)
return ret;
if (channel >= data->fans.entries)
return -ENXIO;
entry = (struct fan_sensor_entry *)data->fans.obj->package.elements[1].buffer.pointer;
switch (attr) {
case hwmon_fan_input:
*val = get_unaligned_le16(&entry[channel].rpm);
return 0;
default:
break;
}
return -EOPNOTSUPP;
}
static int dell_wmi_ddv_temp_read_channel(struct dell_wmi_ddv_data *data, u32 attr, int channel,
long *val)
{
struct thermal_sensor_entry *entry;
int ret;
ret = dell_wmi_ddv_update_sensors(data->wdev, DELL_DDV_THERMAL_SENSOR_INFORMATION,
&data->temps, sizeof(*entry));
if (ret < 0)
return ret;
if (channel >= data->temps.entries)
return -ENXIO;
entry = (struct thermal_sensor_entry *)data->temps.obj->package.elements[1].buffer.pointer;
switch (attr) {
case hwmon_temp_input:
*val = entry[channel].now * 1000;
return 0;
case hwmon_temp_min:
*val = entry[channel].min * 1000;
return 0;
case hwmon_temp_max:
*val = entry[channel].max * 1000;
return 0;
default:
break;
}
return -EOPNOTSUPP;
}
static int dell_wmi_ddv_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long *val)
{
struct dell_wmi_ddv_data *data = dev_get_drvdata(dev);
int ret;
switch (type) {
case hwmon_fan:
mutex_lock(&data->fans.lock);
ret = dell_wmi_ddv_fan_read_channel(data, attr, channel, val);
mutex_unlock(&data->fans.lock);
return ret;
case hwmon_temp:
mutex_lock(&data->temps.lock);
ret = dell_wmi_ddv_temp_read_channel(data, attr, channel, val);
mutex_unlock(&data->temps.lock);
return ret;
default:
break;
}
return -EOPNOTSUPP;
}
static int dell_wmi_ddv_fan_read_string(struct dell_wmi_ddv_data *data, int channel,
const char **str)
{
struct fan_sensor_entry *entry;
int ret;
u8 type;
ret = dell_wmi_ddv_update_sensors(data->wdev, DELL_DDV_FAN_SENSOR_INFORMATION,
&data->fans, sizeof(*entry));
if (ret < 0)
return ret;
if (channel >= data->fans.entries)
return -ENXIO;
entry = (struct fan_sensor_entry *)data->fans.obj->package.elements[1].buffer.pointer;
type = entry[channel].type;
switch (type) {
case 0x00 ... 0x07:
*str = fan_labels[type];
break;
case 0x11 ... 0x14:
*str = fan_dock_labels[type - 0x11];
break;
default:
*str = "Unknown Fan";
break;
}
return 0;
}
static int dell_wmi_ddv_temp_read_string(struct dell_wmi_ddv_data *data, int channel,
const char **str)
{
struct thermal_sensor_entry *entry;
int ret;
ret = dell_wmi_ddv_update_sensors(data->wdev, DELL_DDV_THERMAL_SENSOR_INFORMATION,
&data->temps, sizeof(*entry));
if (ret < 0)
return ret;
if (channel >= data->temps.entries)
return -ENXIO;
entry = (struct thermal_sensor_entry *)data->temps.obj->package.elements[1].buffer.pointer;
switch (entry[channel].type) {
case 0x00:
*str = "CPU";
break;
case 0x11:
*str = "Video";
break;
case 0x22:
*str = "Memory"; /* sometimes called DIMM */
break;
case 0x33:
*str = "Other";
break;
case 0x44:
*str = "Ambient"; /* sometimes called SKIN */
break;
case 0x52:
*str = "SODIMM";
break;
case 0x55:
*str = "HDD";
break;
case 0x62:
*str = "SODIMM 2";
break;
case 0x73:
*str = "NB";
break;
case 0x83:
*str = "Charger";
break;
case 0xbb:
*str = "Memory 3";
break;
default:
*str = "Unknown";
break;
}
return 0;
}
static int dell_wmi_ddv_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, const char **str)
{
struct dell_wmi_ddv_data *data = dev_get_drvdata(dev);
int ret;
switch (type) {
case hwmon_fan:
switch (attr) {
case hwmon_fan_label:
mutex_lock(&data->fans.lock);
ret = dell_wmi_ddv_fan_read_string(data, channel, str);
mutex_unlock(&data->fans.lock);
return ret;
default:
break;
}
break;
case hwmon_temp:
switch (attr) {
case hwmon_temp_label:
mutex_lock(&data->temps.lock);
ret = dell_wmi_ddv_temp_read_string(data, channel, str);
mutex_unlock(&data->temps.lock);
return ret;
default:
break;
}
break;
default:
break;
}
return -EOPNOTSUPP;
}
static const struct hwmon_ops dell_wmi_ddv_ops = {
.is_visible = dell_wmi_ddv_is_visible,
.read = dell_wmi_ddv_read,
.read_string = dell_wmi_ddv_read_string,
};
static struct hwmon_channel_info *dell_wmi_ddv_channel_create(struct device *dev, u64 count,
enum hwmon_sensor_types type,
u32 config)
{
struct combined_channel_info *cinfo;
int i;
cinfo = devm_kzalloc(dev, struct_size(cinfo, config, count + 1), GFP_KERNEL);
if (!cinfo)
return ERR_PTR(-ENOMEM);
cinfo->info.type = type;
cinfo->info.config = cinfo->config;
for (i = 0; i < count; i++)
cinfo->config[i] = config;
return &cinfo->info;
}
static void dell_wmi_ddv_hwmon_cache_invalidate(struct dell_wmi_ddv_sensors *sensors)
{
mutex_lock(&sensors->lock);
kfree(sensors->obj);
sensors->obj = NULL;
mutex_unlock(&sensors->lock);
}
static void dell_wmi_ddv_hwmon_cache_destroy(void *data)
{
struct dell_wmi_ddv_sensors *sensors = data;
mutex_destroy(&sensors->lock);
kfree(sensors->obj);
}
static struct hwmon_channel_info *dell_wmi_ddv_channel_init(struct wmi_device *wdev,
enum dell_ddv_method method,
struct dell_wmi_ddv_sensors *sensors,
size_t entry_size,
enum hwmon_sensor_types type,
u32 config)
{
struct hwmon_channel_info *info;
int ret;
ret = dell_wmi_ddv_update_sensors(wdev, method, sensors, entry_size);
if (ret < 0)
return ERR_PTR(ret);
mutex_init(&sensors->lock);
ret = devm_add_action_or_reset(&wdev->dev, dell_wmi_ddv_hwmon_cache_destroy, sensors);
if (ret < 0)
return ERR_PTR(ret);
info = dell_wmi_ddv_channel_create(&wdev->dev, sensors->entries, type, config);
if (IS_ERR(info))
devm_release_action(&wdev->dev, dell_wmi_ddv_hwmon_cache_destroy, sensors);
return info;
}
static int dell_wmi_ddv_hwmon_add(struct dell_wmi_ddv_data *data)
{
struct wmi_device *wdev = data->wdev;
struct combined_chip_info *cinfo;
struct hwmon_channel_info *info;
struct device *hdev;
int index = 0;
int ret;
if (!devres_open_group(&wdev->dev, dell_wmi_ddv_hwmon_add, GFP_KERNEL))
return -ENOMEM;
cinfo = devm_kzalloc(&wdev->dev, struct_size(cinfo, info, 4), GFP_KERNEL);
if (!cinfo) {
ret = -ENOMEM;
goto err_release;
}
cinfo->chip.ops = &dell_wmi_ddv_ops;
cinfo->chip.info = cinfo->info;
info = dell_wmi_ddv_channel_create(&wdev->dev, 1, hwmon_chip, HWMON_C_REGISTER_TZ);
if (IS_ERR(info)) {
ret = PTR_ERR(info);
goto err_release;
}
cinfo->info[index] = info;
index++;
info = dell_wmi_ddv_channel_init(wdev, DELL_DDV_FAN_SENSOR_INFORMATION, &data->fans,
sizeof(struct fan_sensor_entry), hwmon_fan,
(HWMON_F_INPUT | HWMON_F_LABEL));
if (!IS_ERR(info)) {
cinfo->info[index] = info;
index++;
}
info = dell_wmi_ddv_channel_init(wdev, DELL_DDV_THERMAL_SENSOR_INFORMATION, &data->temps,
sizeof(struct thermal_sensor_entry), hwmon_temp,
(HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
HWMON_T_LABEL));
if (!IS_ERR(info)) {
cinfo->info[index] = info;
index++;
}
if (index < 2) {
ret = -ENODEV;
goto err_release;
}
hdev = devm_hwmon_device_register_with_info(&wdev->dev, "dell_ddv", data, &cinfo->chip,
NULL);
if (IS_ERR(hdev)) {
ret = PTR_ERR(hdev);
goto err_release;
}
devres_close_group(&wdev->dev, dell_wmi_ddv_hwmon_add);
return 0;
err_release:
devres_release_group(&wdev->dev, dell_wmi_ddv_hwmon_add);
return ret;
}
static int dell_wmi_ddv_battery_index(struct acpi_device *acpi_dev, u32 *index)
{
const char *uid_str;
......@@ -340,9 +816,14 @@ static int dell_wmi_ddv_probe(struct wmi_device *wdev, const void *context)
return ret;
dev_dbg(&wdev->dev, "WMI interface version: %d\n", version);
if (version != DELL_DDV_SUPPORTED_INTERFACE)
if (version < DELL_DDV_SUPPORTED_VERSION_MIN || version > DELL_DDV_SUPPORTED_VERSION_MAX) {
if (!force)
return -ENODEV;
dev_warn(&wdev->dev, "Loading despite unsupported WMI interface version (%u)\n",
version);
}
data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
......@@ -352,9 +833,34 @@ static int dell_wmi_ddv_probe(struct wmi_device *wdev, const void *context)
dell_wmi_ddv_debugfs_init(wdev);
return dell_wmi_ddv_battery_add(data);
if (IS_REACHABLE(CONFIG_ACPI_BATTERY)) {
ret = dell_wmi_ddv_battery_add(data);
if (ret < 0 && ret != -ENODEV)
dev_warn(&wdev->dev, "Unable to register ACPI battery hook: %d\n", ret);
}
if (IS_REACHABLE(CONFIG_HWMON)) {
ret = dell_wmi_ddv_hwmon_add(data);
if (ret < 0 && ret != -ENODEV)
dev_warn(&wdev->dev, "Unable to register hwmon interface: %d\n", ret);
}
return 0;
}
static int dell_wmi_ddv_resume(struct device *dev)
{
struct dell_wmi_ddv_data *data = dev_get_drvdata(dev);
/* Force re-reading of all sensors */
dell_wmi_ddv_hwmon_cache_invalidate(&data->fans);
dell_wmi_ddv_hwmon_cache_invalidate(&data->temps);
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(dell_wmi_ddv_dev_pm_ops, NULL, dell_wmi_ddv_resume);
static const struct wmi_device_id dell_wmi_ddv_id_table[] = {
{ DELL_DDV_GUID, NULL },
{ }
......@@ -364,6 +870,8 @@ MODULE_DEVICE_TABLE(wmi, dell_wmi_ddv_id_table);
static struct wmi_driver dell_wmi_ddv_driver = {
.driver = {
.name = DRIVER_NAME,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = pm_sleep_ptr(&dell_wmi_ddv_dev_pm_ops),
},
.id_table = dell_wmi_ddv_id_table,
.probe = dell_wmi_ddv_probe,
......
......@@ -255,7 +255,7 @@ static void attr_name_release(struct kobject *kobj)
kfree(kobj);
}
static struct kobj_type attr_name_ktype = {
static const struct kobj_type attr_name_ktype = {
.release = attr_name_release,
.sysfs_ops = &wmi_sysman_kobj_sysfs_ops,
};
......
......@@ -217,6 +217,8 @@ static const struct key_entry hp_wmi_keymap[] = {
{ KE_KEY, 0x213b, { KEY_INFO } },
{ KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } },
{ KE_KEY, 0x216a, { KEY_SETUP } },
{ KE_IGNORE, 0x21a4, }, /* Win Lock On */
{ KE_IGNORE, 0x121a4, }, /* Win Lock Off */
{ KE_KEY, 0x21a5, { KEY_PROG2 } }, /* HP Omen Key */
{ KE_KEY, 0x21a7, { KEY_FN_ESC } },
{ KE_KEY, 0x21a9, { KEY_TOUCHPAD_OFF } },
......
......@@ -182,6 +182,19 @@ config INTEL_SMARTCONNECT
This driver checks to determine whether the device has Intel Smart
Connect enabled, and if so disables it.
config INTEL_TPMI
tristate "Intel Topology Aware Register and PM Capsule Interface (TPMI)"
depends on INTEL_VSEC
depends on X86_64
help
The Intel Topology Aware Register and PM Capsule Interface (TPMI),
provides enumerable MMIO interface for power management features.
This driver creates devices, so that other PM feature driver can
be loaded for PM specific feature operation.
To compile this driver as a module, choose M here: the module will
be called intel_vsec_tpmi.
config INTEL_TURBO_MAX_3
bool "Intel Turbo Boost Max Technology 3.0 enumeration driver"
depends on X86_64 && SCHED_MC_PRIO
......
......@@ -47,6 +47,10 @@ obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.o
intel_punit_ipc-y := punit_ipc.o
obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o
# TPMI drivers
intel_vsec_tpmi-y := tpmi.o
obj-$(CONFIG_INTEL_TPMI) += intel_vsec_tpmi.o
# Intel Uncore drivers
intel-rst-y := rst.o
obj-$(CONFIG_INTEL_RST) += intel-rst.o
......
......@@ -131,16 +131,15 @@ static acpi_status sar_get_device_mode(struct platform_device *device)
acpi_status status = AE_OK;
union acpi_object *out;
u32 rev = 0;
int value;
out = acpi_evaluate_dsm(context->handle, &context->guid, rev,
COMMAND_ID_DEV_MODE, NULL);
if (get_int_value(out, &value)) {
out = acpi_evaluate_dsm_typed(context->handle, &context->guid, rev,
COMMAND_ID_DEV_MODE, NULL, ACPI_TYPE_INTEGER);
if (!out) {
dev_err(&device->dev, "DSM cmd:%d Failed to retrieve value\n", COMMAND_ID_DEV_MODE);
status = AE_ERROR;
goto dev_mode_error;
}
context->sar_data.device_mode = value;
context->sar_data.device_mode = out->integer.value;
update_sar_data(context);
sysfs_notify(&device->dev.kobj, NULL, SYSFS_DATANAME);
......@@ -221,11 +220,11 @@ static void sar_get_data(int reg, struct wwan_sar_context *context)
req.type = ACPI_TYPE_INTEGER;
req.integer.value = reg;
out = acpi_evaluate_dsm(context->handle, &context->guid, rev,
COMMAND_ID_CONFIG_TABLE, &req);
out = acpi_evaluate_dsm_typed(context->handle, &context->guid, rev,
COMMAND_ID_CONFIG_TABLE, &req, ACPI_TYPE_PACKAGE);
if (!out)
return;
if (out->type == ACPI_TYPE_PACKAGE && out->package.count >= 3 &&
if (out->package.count >= 3 &&
out->package.elements[0].type == ACPI_TYPE_INTEGER &&
out->package.elements[1].type == ACPI_TYPE_INTEGER &&
out->package.elements[2].type == ACPI_TYPE_PACKAGE &&
......
......@@ -4,6 +4,7 @@ config INTEL_SKL_INT3472
depends on COMMON_CLK
depends on I2C
depends on GPIOLIB
depends on LEDS_CLASS
depends on REGULATOR
select MFD_CORE
select REGMAP_I2C
......
obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472_discrete.o \
intel_skl_int3472_tps68470.o
intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o common.o
intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o led.o common.o
intel_skl_int3472_tps68470-y := tps68470.o tps68470_board_data.o common.o
......@@ -23,8 +23,6 @@ static int skl_int3472_clk_prepare(struct clk_hw *hw)
struct int3472_gpio_clock *clk = to_int3472_clk(hw);
gpiod_set_value_cansleep(clk->ena_gpio, 1);
gpiod_set_value_cansleep(clk->led_gpio, 1);
return 0;
}
......@@ -33,7 +31,6 @@ static void skl_int3472_clk_unprepare(struct clk_hw *hw)
struct int3472_gpio_clock *clk = to_int3472_clk(hw);
gpiod_set_value_cansleep(clk->ena_gpio, 0);
gpiod_set_value_cansleep(clk->led_gpio, 0);
}
static int skl_int3472_clk_enable(struct clk_hw *hw)
......@@ -89,18 +86,37 @@ static const struct clk_ops skl_int3472_clock_ops = {
.recalc_rate = skl_int3472_clk_recalc_rate,
};
int skl_int3472_register_clock(struct int3472_discrete_device *int3472)
int skl_int3472_register_clock(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio, u32 polarity)
{
char *path = agpio->resource_source.string_ptr;
struct clk_init_data init = {
.ops = &skl_int3472_clock_ops,
.flags = CLK_GET_RATE_NOCACHE,
};
int ret;
if (int3472->clock.cl)
return -EBUSY;
int3472->clock.ena_gpio = acpi_get_and_request_gpiod(path, agpio->pin_table[0],
"int3472,clk-enable");
if (IS_ERR(int3472->clock.ena_gpio))
return dev_err_probe(int3472->dev, PTR_ERR(int3472->clock.ena_gpio),
"getting clk-enable GPIO\n");
if (polarity == GPIO_ACTIVE_LOW)
gpiod_toggle_active_low(int3472->clock.ena_gpio);
/* Ensure the pin is in output mode and non-active state */
gpiod_direction_output(int3472->clock.ena_gpio, 0);
init.name = kasprintf(GFP_KERNEL, "%s-clk",
acpi_dev_name(int3472->adev));
if (!init.name)
return -ENOMEM;
if (!init.name) {
ret = -ENOMEM;
goto out_put_gpio;
}
int3472->clock.frequency = skl_int3472_get_clk_frequency(int3472);
......@@ -126,14 +142,20 @@ int skl_int3472_register_clock(struct int3472_discrete_device *int3472)
clk_unregister(int3472->clock.clk);
out_free_init_name:
kfree(init.name);
out_put_gpio:
gpiod_put(int3472->clock.ena_gpio);
return ret;
}
void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472)
{
if (!int3472->clock.cl)
return;
clkdev_drop(int3472->clock.cl);
clk_unregister(int3472->clock.clk);
gpiod_put(int3472->clock.ena_gpio);
}
int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
......
......@@ -6,6 +6,7 @@
#include <linux/clk-provider.h>
#include <linux/gpio/machine.h>
#include <linux/leds.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/types.h>
......@@ -28,6 +29,8 @@
#define GPIO_REGULATOR_NAME_LENGTH 21
#define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9
#define INT3472_LED_MAX_NAME_LEN 32
#define CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET 86
#define INT3472_REGULATOR(_name, _supply, _ops) \
......@@ -96,10 +99,16 @@ struct int3472_discrete_device {
struct clk_hw clk_hw;
struct clk_lookup *cl;
struct gpio_desc *ena_gpio;
struct gpio_desc *led_gpio;
u32 frequency;
} clock;
struct int3472_pled {
struct led_classdev classdev;
struct led_lookup_data lookup;
char name[INT3472_LED_MAX_NAME_LEN];
struct gpio_desc *gpio;
} pled;
unsigned int ngpios; /* how many GPIOs have we seen */
unsigned int n_sensor_gpios; /* how many have we mapped to sensor */
struct gpiod_lookup_table gpios;
......@@ -112,11 +121,16 @@ int skl_int3472_get_sensor_adev_and_name(struct device *dev,
struct acpi_device **sensor_adev_ret,
const char **name_ret);
int skl_int3472_register_clock(struct int3472_discrete_device *int3472);
int skl_int3472_register_clock(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio, u32 polarity);
void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472);
int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio);
void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472);
int skl_int3472_register_pled(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio, u32 polarity);
void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472);
#endif
......@@ -2,8 +2,6 @@
/* Author: Dan Scally <djrscally@gmail.com> */
#include <linux/acpi.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
......@@ -80,14 +78,6 @@ skl_int3472_get_sensor_module_config(struct int3472_discrete_device *int3472)
return ERR_PTR(-ENODEV);
}
if (obj->string.type != ACPI_TYPE_STRING) {
dev_err(int3472->dev,
"Sensor _DSM returned a non-string value\n");
ACPI_FREE(obj);
return ERR_PTR(-EINVAL);
}
for (i = 0; i < ARRAY_SIZE(int3472_sensor_configs); i++) {
if (!strcmp(int3472_sensor_configs[i].sensor_module_name,
obj->string.pointer))
......@@ -154,38 +144,34 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
return 0;
}
static int skl_int3472_map_gpio_to_clk(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio, u8 type)
static void int3472_get_func_and_polarity(u8 type, const char **func, u32 *polarity)
{
char *path = agpio->resource_source.string_ptr;
u16 pin = agpio->pin_table[0];
struct gpio_desc *gpio;
switch (type) {
case INT3472_GPIO_TYPE_RESET:
*func = "reset";
*polarity = GPIO_ACTIVE_LOW;
break;
case INT3472_GPIO_TYPE_POWERDOWN:
*func = "powerdown";
*polarity = GPIO_ACTIVE_LOW;
break;
case INT3472_GPIO_TYPE_CLK_ENABLE:
gpio = acpi_get_and_request_gpiod(path, pin, "int3472,clk-enable");
if (IS_ERR(gpio))
return (PTR_ERR(gpio));
int3472->clock.ena_gpio = gpio;
/* Ensure the pin is in output mode and non-active state */
gpiod_direction_output(int3472->clock.ena_gpio, 0);
*func = "clk-enable";
*polarity = GPIO_ACTIVE_HIGH;
break;
case INT3472_GPIO_TYPE_PRIVACY_LED:
gpio = acpi_get_and_request_gpiod(path, pin, "int3472,privacy-led");
if (IS_ERR(gpio))
return (PTR_ERR(gpio));
int3472->clock.led_gpio = gpio;
/* Ensure the pin is in output mode and non-active state */
gpiod_direction_output(int3472->clock.led_gpio, 0);
*func = "privacy-led";
*polarity = GPIO_ACTIVE_HIGH;
break;
case INT3472_GPIO_TYPE_POWER_ENABLE:
*func = "power-enable";
*polarity = GPIO_ACTIVE_HIGH;
break;
default:
dev_err(int3472->dev, "Invalid GPIO type 0x%02x for clock\n", type);
*func = "unknown";
*polarity = GPIO_ACTIVE_HIGH;
break;
}
return 0;
}
/**
......@@ -226,9 +212,11 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
struct int3472_discrete_device *int3472 = data;
struct acpi_resource_gpio *agpio;
union acpi_object *obj;
u8 active_value, type;
const char *err_msg;
const char *func;
u32 polarity;
int ret;
u8 type;
if (!acpi_gpio_get_io_resource(ares, &agpio))
return 1;
......@@ -250,26 +238,35 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
type = obj->integer.value & 0xff;
int3472_get_func_and_polarity(type, &func, &polarity);
/* If bits 31-24 of the _DSM entry are all 0 then the signal is inverted */
active_value = obj->integer.value >> 24;
if (!active_value)
polarity ^= GPIO_ACTIVE_LOW;
dev_dbg(int3472->dev, "%s %s pin %d active-%s\n", func,
agpio->resource_source.string_ptr, agpio->pin_table[0],
(polarity == GPIO_ACTIVE_HIGH) ? "high" : "low");
switch (type) {
case INT3472_GPIO_TYPE_RESET:
ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "reset",
GPIO_ACTIVE_LOW);
case INT3472_GPIO_TYPE_POWERDOWN:
ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, func, polarity);
if (ret)
err_msg = "Failed to map reset pin to sensor\n";
err_msg = "Failed to map GPIO pin to sensor\n";
break;
case INT3472_GPIO_TYPE_POWERDOWN:
ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "powerdown",
GPIO_ACTIVE_LOW);
case INT3472_GPIO_TYPE_CLK_ENABLE:
ret = skl_int3472_register_clock(int3472, agpio, polarity);
if (ret)
err_msg = "Failed to map powerdown pin to sensor\n";
err_msg = "Failed to register clock\n";
break;
case INT3472_GPIO_TYPE_CLK_ENABLE:
case INT3472_GPIO_TYPE_PRIVACY_LED:
ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type);
ret = skl_int3472_register_pled(int3472, agpio, polarity);
if (ret)
err_msg = "Failed to map GPIO to clock\n";
err_msg = "Failed to register LED\n";
break;
case INT3472_GPIO_TYPE_POWER_ENABLE:
......@@ -314,21 +311,6 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472)
acpi_dev_free_resource_list(&resource_list);
/*
* If we find no clock enable GPIO pin then the privacy LED won't work.
* We've never seen that situation, but it's possible. Warn the user so
* it's clear what's happened.
*/
if (int3472->clock.ena_gpio) {
ret = skl_int3472_register_clock(int3472);
if (ret)
return ret;
} else {
if (int3472->clock.led_gpio)
dev_warn(int3472->dev,
"No clk GPIO. The privacy LED won't work\n");
}
int3472->gpios.dev_id = int3472->sensor_name;
gpiod_add_lookup_table(&int3472->gpios);
......@@ -341,12 +323,8 @@ static int skl_int3472_discrete_remove(struct platform_device *pdev)
gpiod_remove_lookup_table(&int3472->gpios);
if (int3472->clock.cl)
skl_int3472_unregister_clock(int3472);
gpiod_put(int3472->clock.ena_gpio);
gpiod_put(int3472->clock.led_gpio);
skl_int3472_unregister_pled(int3472);
skl_int3472_unregister_regulator(int3472);
return 0;
......
// SPDX-License-Identifier: GPL-2.0
/* Author: Hans de Goede <hdegoede@redhat.com> */
#include <linux/acpi.h>
#include <linux/gpio/consumer.h>
#include <linux/leds.h>
#include "common.h"
static int int3472_pled_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct int3472_discrete_device *int3472 =
container_of(led_cdev, struct int3472_discrete_device, pled.classdev);
gpiod_set_value_cansleep(int3472->pled.gpio, brightness);
return 0;
}
int skl_int3472_register_pled(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio, u32 polarity)
{
char *p, *path = agpio->resource_source.string_ptr;
int ret;
if (int3472->pled.classdev.dev)
return -EBUSY;
int3472->pled.gpio = acpi_get_and_request_gpiod(path, agpio->pin_table[0],
"int3472,privacy-led");
if (IS_ERR(int3472->pled.gpio))
return dev_err_probe(int3472->dev, PTR_ERR(int3472->pled.gpio),
"getting privacy LED GPIO\n");
if (polarity == GPIO_ACTIVE_LOW)
gpiod_toggle_active_low(int3472->pled.gpio);
/* Ensure the pin is in output mode and non-active state */
gpiod_direction_output(int3472->pled.gpio, 0);
/* Generate the name, replacing the ':' in the ACPI devname with '_' */
snprintf(int3472->pled.name, sizeof(int3472->pled.name),
"%s::privacy_led", acpi_dev_name(int3472->sensor));
p = strchr(int3472->pled.name, ':');
if (p)
*p = '_';
int3472->pled.classdev.name = int3472->pled.name;
int3472->pled.classdev.max_brightness = 1;
int3472->pled.classdev.brightness_set_blocking = int3472_pled_set;
ret = led_classdev_register(int3472->dev, &int3472->pled.classdev);
if (ret)
goto err_free_gpio;
int3472->pled.lookup.provider = int3472->pled.name;
int3472->pled.lookup.dev_id = int3472->sensor_name;
int3472->pled.lookup.con_id = "privacy-led";
led_add_lookup(&int3472->pled.lookup);
return 0;
err_free_gpio:
gpiod_put(int3472->pled.gpio);
return ret;
}
void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472)
{
if (IS_ERR_OR_NULL(int3472->pled.classdev.dev))
return;
led_remove_lookup(&int3472->pled.lookup);
led_classdev_unregister(&int3472->pled.classdev);
gpiod_put(int3472->pled.gpio);
}
......@@ -266,17 +266,11 @@ static int oaktrail_probe(struct platform_device *pdev)
return 0;
}
static int oaktrail_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver oaktrail_driver = {
.driver = {
.name = DRIVER_NAME,
},
.probe = oaktrail_probe,
.remove = oaktrail_remove,
};
static int dmi_check_cb(const struct dmi_system_id *id)
......
......@@ -221,9 +221,9 @@ void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev)
guid_parse(ACPI_S0IX_DSM_UUID, &s0ix_dsm_guid);
out_obj = acpi_evaluate_dsm(adev->handle, &s0ix_dsm_guid, 0,
ACPI_GET_LOW_MODE_REGISTERS, NULL);
if (out_obj && out_obj->type == ACPI_TYPE_BUFFER) {
out_obj = acpi_evaluate_dsm_typed(adev->handle, &s0ix_dsm_guid, 0,
ACPI_GET_LOW_MODE_REGISTERS, NULL, ACPI_TYPE_BUFFER);
if (out_obj) {
u32 size = out_obj->buffer.length;
if (size != lpm_size) {
......
......@@ -302,11 +302,6 @@ static int intel_punit_ipc_probe(struct platform_device *pdev)
return 0;
}
static int intel_punit_ipc_remove(struct platform_device *pdev)
{
return 0;
}
static const struct acpi_device_id punit_ipc_acpi_ids[] = {
{ "INT34D4", 0 },
{ }
......@@ -315,7 +310,6 @@ MODULE_DEVICE_TABLE(acpi, punit_ipc_acpi_ids);
static struct platform_driver intel_punit_ipc_driver = {
.probe = intel_punit_ipc_probe,
.remove = intel_punit_ipc_remove,
.driver = {
.name = "intel_punit_ipc",
.acpi_match_table = punit_ipc_acpi_ids,
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* intel-tpmi : Driver to enumerate TPMI features and create devices
*
* Copyright (c) 2023, Intel Corporation.
* All Rights Reserved.
*
* The TPMI (Topology Aware Register and PM Capsule Interface) provides a
* flexible, extendable and PCIe enumerable MMIO interface for PM features.
*
* For example Intel RAPL (Running Average Power Limit) provides a MMIO
* interface using TPMI. This has advantage over traditional MSR
* (Model Specific Register) interface, where a thread needs to be scheduled
* on the target CPU to read or write. Also the RAPL features vary between
* CPU models, and hence lot of model specific code. Here TPMI provides an
* architectural interface by providing hierarchical tables and fields,
* which will not need any model specific implementation.
*
* The TPMI interface uses a PCI VSEC structure to expose the location of
* MMIO region.
*
* This VSEC structure is present in the PCI configuration space of the
* Intel Out-of-Band (OOB) device, which is handled by the Intel VSEC
* driver. The Intel VSEC driver parses VSEC structures present in the PCI
* configuration space of the given device and creates an auxiliary device
* object for each of them. In particular, it creates an auxiliary device
* object representing TPMI that can be bound by an auxiliary driver.
*
* This TPMI driver will bind to the TPMI auxiliary device object created
* by the Intel VSEC driver.
*
* The TPMI specification defines a PFS (PM Feature Structure) table.
* This table is present in the TPMI MMIO region. The starting address
* of PFS is derived from the tBIR (Bar Indicator Register) and "Address"
* field from the VSEC header.
*
* Each TPMI PM feature has one entry in the PFS with a unique TPMI
* ID and its access details. The TPMI driver creates device nodes
* for the supported PM features.
*
* The names of the devices created by the TPMI driver start with the
* "intel_vsec.tpmi-" prefix which is followed by a specific name of the
* given PM feature (for example, "intel_vsec.tpmi-rapl.0").
*
* The device nodes are create by using interface "intel_vsec_add_aux()"
* provided by the Intel VSEC driver.
*/
#include <linux/auxiliary_bus.h>
#include <linux/intel_tpmi.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pci.h>
#include "vsec.h"
/**
* struct intel_tpmi_pfs_entry - TPMI PM Feature Structure (PFS) entry
* @tpmi_id: TPMI feature identifier (what the feature is and its data format).
* @num_entries: Number of feature interface instances present in the PFS.
* This represents the maximum number of Power domains in the SoC.
* @entry_size: Interface instance entry size in 32-bit words.
* @cap_offset: Offset from the PM_Features base address to the base of the PM VSEC
* register bank in KB.
* @attribute: Feature attribute: 0=BIOS. 1=OS. 2-3=Reserved.
* @reserved: Bits for use in the future.
*
* Represents one TPMI feature entry data in the PFS retrieved as is
* from the hardware.
*/
struct intel_tpmi_pfs_entry {
u64 tpmi_id:8;
u64 num_entries:8;
u64 entry_size:16;
u64 cap_offset:16;
u64 attribute:2;
u64 reserved:14;
} __packed;
/**
* struct intel_tpmi_pm_feature - TPMI PM Feature information for a TPMI ID
* @pfs_header: PFS header retireved from the hardware.
* @vsec_offset: Starting MMIO address for this feature in bytes. Essentially
* this offset = "Address" from VSEC header + PFS Capability
* offset for this feature entry.
*
* Represents TPMI instance information for one TPMI ID.
*/
struct intel_tpmi_pm_feature {
struct intel_tpmi_pfs_entry pfs_header;
unsigned int vsec_offset;
};
/**
* struct intel_tpmi_info - TPMI information for all IDs in an instance
* @tpmi_features: Pointer to a list of TPMI feature instances
* @vsec_dev: Pointer to intel_vsec_device structure for this TPMI device
* @feature_count: Number of TPMI of TPMI instances pointed by tpmi_features
* @pfs_start: Start of PFS offset for the TPMI instances in this device
* @plat_info: Stores platform info which can be used by the client drivers
*
* Stores the information for all TPMI devices enumerated from a single PCI device.
*/
struct intel_tpmi_info {
struct intel_tpmi_pm_feature *tpmi_features;
struct intel_vsec_device *vsec_dev;
int feature_count;
u64 pfs_start;
struct intel_tpmi_plat_info plat_info;
};
/**
* struct tpmi_info_header - CPU package ID to PCI device mapping information
* @fn: PCI function number
* @dev: PCI device number
* @bus: PCI bus number
* @pkg: CPU Package id
* @reserved: Reserved for future use
* @lock: When set to 1 the register is locked and becomes read-only
* until next reset. Not for use by the OS driver.
*
* The structure to read hardware provided mapping information.
*/
struct tpmi_info_header {
u64 fn:3;
u64 dev:5;
u64 bus:8;
u64 pkg:8;
u64 reserved:39;
u64 lock:1;
} __packed;
/*
* List of supported TMPI IDs.
* Some TMPI IDs are not used by Linux, so the numbers are not consecutive.
*/
enum intel_tpmi_id {
TPMI_ID_RAPL = 0, /* Running Average Power Limit */
TPMI_ID_PEM = 1, /* Power and Perf excursion Monitor */
TPMI_ID_UNCORE = 2, /* Uncore Frequency Scaling */
TPMI_ID_SST = 5, /* Speed Select Technology */
TPMI_INFO_ID = 0x81, /* Special ID for PCI BDF and Package ID information */
};
/* Used during auxbus device creation */
static DEFINE_IDA(intel_vsec_tpmi_ida);
struct intel_tpmi_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev)
{
struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev);
return vsec_dev->priv_data;
}
EXPORT_SYMBOL_NS_GPL(tpmi_get_platform_data, INTEL_TPMI);
int tpmi_get_resource_count(struct auxiliary_device *auxdev)
{
struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev);
if (vsec_dev)
return vsec_dev->num_resources;
return 0;
}
EXPORT_SYMBOL_NS_GPL(tpmi_get_resource_count, INTEL_TPMI);
struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int index)
{
struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev);
if (vsec_dev && index < vsec_dev->num_resources)
return &vsec_dev->resource[index];
return NULL;
}
EXPORT_SYMBOL_NS_GPL(tpmi_get_resource_at_index, INTEL_TPMI);
static const char *intel_tpmi_name(enum intel_tpmi_id id)
{
switch (id) {
case TPMI_ID_RAPL:
return "rapl";
case TPMI_ID_PEM:
return "pem";
case TPMI_ID_UNCORE:
return "uncore";
case TPMI_ID_SST:
return "sst";
default:
return NULL;
}
}
/* String Length for tpmi-"feature_name(upto 8 bytes)" */
#define TPMI_FEATURE_NAME_LEN 14
static int tpmi_create_device(struct intel_tpmi_info *tpmi_info,
struct intel_tpmi_pm_feature *pfs,
u64 pfs_start)
{
struct intel_vsec_device *vsec_dev = tpmi_info->vsec_dev;
char feature_id_name[TPMI_FEATURE_NAME_LEN];
struct intel_vsec_device *feature_vsec_dev;
struct resource *res, *tmp;
const char *name;
int ret, i;
name = intel_tpmi_name(pfs->pfs_header.tpmi_id);
if (!name)
return -EOPNOTSUPP;
feature_vsec_dev = kzalloc(sizeof(*feature_vsec_dev), GFP_KERNEL);
if (!feature_vsec_dev)
return -ENOMEM;
res = kcalloc(pfs->pfs_header.num_entries, sizeof(*res), GFP_KERNEL);
if (!res) {
ret = -ENOMEM;
goto free_vsec;
}
snprintf(feature_id_name, sizeof(feature_id_name), "tpmi-%s", name);
for (i = 0, tmp = res; i < pfs->pfs_header.num_entries; i++, tmp++) {
u64 entry_size_bytes = pfs->pfs_header.entry_size * 4;
tmp->start = pfs->vsec_offset + entry_size_bytes * i;
tmp->end = tmp->start + entry_size_bytes - 1;
tmp->flags = IORESOURCE_MEM;
}
feature_vsec_dev->pcidev = vsec_dev->pcidev;
feature_vsec_dev->resource = res;
feature_vsec_dev->num_resources = pfs->pfs_header.num_entries;
feature_vsec_dev->priv_data = &tpmi_info->plat_info;
feature_vsec_dev->priv_data_size = sizeof(tpmi_info->plat_info);
feature_vsec_dev->ida = &intel_vsec_tpmi_ida;
/*
* intel_vsec_add_aux() is resource managed, no explicit
* delete is required on error or on module unload.
*/
ret = intel_vsec_add_aux(vsec_dev->pcidev, &vsec_dev->auxdev.dev,
feature_vsec_dev, feature_id_name);
if (ret)
goto free_res;
return 0;
free_res:
kfree(res);
free_vsec:
kfree(feature_vsec_dev);
return ret;
}
static int tpmi_create_devices(struct intel_tpmi_info *tpmi_info)
{
struct intel_vsec_device *vsec_dev = tpmi_info->vsec_dev;
int ret, i;
for (i = 0; i < vsec_dev->num_resources; i++) {
ret = tpmi_create_device(tpmi_info, &tpmi_info->tpmi_features[i],
tpmi_info->pfs_start);
/*
* Fail, if the supported features fails to create device,
* otherwise, continue. Even if one device failed to create,
* fail the loading of driver. Since intel_vsec_add_aux()
* is resource managed, no clean up is required for the
* successfully created devices.
*/
if (ret && ret != -EOPNOTSUPP)
return ret;
}
return 0;
}
#define TPMI_INFO_BUS_INFO_OFFSET 0x08
static int tpmi_process_info(struct intel_tpmi_info *tpmi_info,
struct intel_tpmi_pm_feature *pfs)
{
struct tpmi_info_header header;
void __iomem *info_mem;
info_mem = ioremap(pfs->vsec_offset + TPMI_INFO_BUS_INFO_OFFSET,
pfs->pfs_header.entry_size * 4 - TPMI_INFO_BUS_INFO_OFFSET);
if (!info_mem)
return -ENOMEM;
memcpy_fromio(&header, info_mem, sizeof(header));
tpmi_info->plat_info.package_id = header.pkg;
tpmi_info->plat_info.bus_number = header.bus;
tpmi_info->plat_info.device_number = header.dev;
tpmi_info->plat_info.function_number = header.fn;
iounmap(info_mem);
return 0;
}
static int tpmi_fetch_pfs_header(struct intel_tpmi_pm_feature *pfs, u64 start, int size)
{
void __iomem *pfs_mem;
pfs_mem = ioremap(start, size);
if (!pfs_mem)
return -ENOMEM;
memcpy_fromio(&pfs->pfs_header, pfs_mem, sizeof(pfs->pfs_header));
iounmap(pfs_mem);
return 0;
}
static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev)
{
struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev);
struct pci_dev *pci_dev = vsec_dev->pcidev;
struct intel_tpmi_info *tpmi_info;
u64 pfs_start = 0;
int i;
tpmi_info = devm_kzalloc(&auxdev->dev, sizeof(*tpmi_info), GFP_KERNEL);
if (!tpmi_info)
return -ENOMEM;
tpmi_info->vsec_dev = vsec_dev;
tpmi_info->feature_count = vsec_dev->num_resources;
tpmi_info->plat_info.bus_number = pci_dev->bus->number;
tpmi_info->tpmi_features = devm_kcalloc(&auxdev->dev, vsec_dev->num_resources,
sizeof(*tpmi_info->tpmi_features),
GFP_KERNEL);
if (!tpmi_info->tpmi_features)
return -ENOMEM;
for (i = 0; i < vsec_dev->num_resources; i++) {
struct intel_tpmi_pm_feature *pfs;
struct resource *res;
u64 res_start;
int size, ret;
pfs = &tpmi_info->tpmi_features[i];
res = &vsec_dev->resource[i];
if (!res)
continue;
res_start = res->start;
size = resource_size(res);
if (size < 0)
continue;
ret = tpmi_fetch_pfs_header(pfs, res_start, size);
if (ret)
continue;
if (!pfs_start)
pfs_start = res_start;
pfs->pfs_header.cap_offset *= 1024;
pfs->vsec_offset = pfs_start + pfs->pfs_header.cap_offset;
/*
* Process TPMI_INFO to get PCI device to CPU package ID.
* Device nodes for TPMI features are not created in this
* for loop. So, the mapping information will be available
* when actual device nodes created outside this
* loop via tpmi_create_devices().
*/
if (pfs->pfs_header.tpmi_id == TPMI_INFO_ID)
tpmi_process_info(tpmi_info, pfs);
}
tpmi_info->pfs_start = pfs_start;
auxiliary_set_drvdata(auxdev, tpmi_info);
return tpmi_create_devices(tpmi_info);
}
static int tpmi_probe(struct auxiliary_device *auxdev,
const struct auxiliary_device_id *id)
{
return intel_vsec_tpmi_init(auxdev);
}
/*
* Remove callback is not needed currently as there is no
* cleanup required. All memory allocs are device managed. All
* devices created by this modules are also device managed.
*/
static const struct auxiliary_device_id tpmi_id_table[] = {
{ .name = "intel_vsec.tpmi" },
{}
};
MODULE_DEVICE_TABLE(auxiliary, tpmi_id_table);
static struct auxiliary_driver tpmi_aux_driver = {
.id_table = tpmi_id_table,
.probe = tpmi_probe,
};
module_auxiliary_driver(tpmi_aux_driver);
MODULE_IMPORT_NS(INTEL_VSEC);
MODULE_DESCRIPTION("Intel TPMI enumeration module");
MODULE_LICENSE("GPL");
......@@ -64,6 +64,7 @@ enum intel_vsec_id {
VSEC_ID_WATCHER = 3,
VSEC_ID_CRASHLOG = 4,
VSEC_ID_SDSI = 65,
VSEC_ID_TPMI = 66,
};
static enum intel_vsec_id intel_vsec_allow_list[] = {
......@@ -71,6 +72,7 @@ static enum intel_vsec_id intel_vsec_allow_list[] = {
VSEC_ID_WATCHER,
VSEC_ID_CRASHLOG,
VSEC_ID_SDSI,
VSEC_ID_TPMI,
};
static const char *intel_vsec_name(enum intel_vsec_id id)
......@@ -88,6 +90,9 @@ static const char *intel_vsec_name(enum intel_vsec_id id)
case VSEC_ID_SDSI:
return "sdsi";
case VSEC_ID_TPMI:
return "tpmi";
default:
return NULL;
}
......@@ -124,35 +129,48 @@ static void intel_vsec_remove_aux(void *data)
auxiliary_device_uninit(data);
}
static DEFINE_MUTEX(vsec_ida_lock);
static void intel_vsec_dev_release(struct device *dev)
{
struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(dev);
mutex_lock(&vsec_ida_lock);
ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id);
mutex_unlock(&vsec_ida_lock);
kfree(intel_vsec_dev->resource);
kfree(intel_vsec_dev);
}
static int intel_vsec_add_aux(struct pci_dev *pdev, struct intel_vsec_device *intel_vsec_dev,
int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent,
struct intel_vsec_device *intel_vsec_dev,
const char *name)
{
struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev;
int ret, id;
mutex_lock(&vsec_ida_lock);
ret = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL);
mutex_unlock(&vsec_ida_lock);
if (ret < 0) {
kfree(intel_vsec_dev);
return ret;
}
if (!parent)
parent = &pdev->dev;
auxdev->id = ret;
auxdev->name = name;
auxdev->dev.parent = &pdev->dev;
auxdev->dev.parent = parent;
auxdev->dev.release = intel_vsec_dev_release;
ret = auxiliary_device_init(auxdev);
if (ret < 0) {
mutex_lock(&vsec_ida_lock);
ida_free(intel_vsec_dev->ida, auxdev->id);
mutex_unlock(&vsec_ida_lock);
kfree(intel_vsec_dev->resource);
kfree(intel_vsec_dev);
return ret;
......@@ -164,7 +182,7 @@ static int intel_vsec_add_aux(struct pci_dev *pdev, struct intel_vsec_device *in
return ret;
}
ret = devm_add_action_or_reset(&pdev->dev, intel_vsec_remove_aux,
ret = devm_add_action_or_reset(parent, intel_vsec_remove_aux,
auxdev);
if (ret < 0)
return ret;
......@@ -177,6 +195,7 @@ static int intel_vsec_add_aux(struct pci_dev *pdev, struct intel_vsec_device *in
return 0;
}
EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, INTEL_VSEC);
static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header,
struct intel_vsec_platform_info *info)
......@@ -234,7 +253,8 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he
else
intel_vsec_dev->ida = &intel_vsec_ida;
return intel_vsec_add_aux(pdev, intel_vsec_dev, intel_vsec_name(header->id));
return intel_vsec_add_aux(pdev, NULL, intel_vsec_dev,
intel_vsec_name(header->id));
}
static bool intel_vsec_walk_header(struct pci_dev *pdev,
......
......@@ -38,8 +38,14 @@ struct intel_vsec_device {
struct ida *ida;
struct intel_vsec_platform_info *info;
int num_resources;
void *priv_data;
size_t priv_data_size;
};
int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent,
struct intel_vsec_device *intel_vsec_dev,
const char *name);
static inline struct intel_vsec_device *dev_to_ivdev(struct device *dev)
{
return container_of(dev, struct intel_vsec_device, auxdev.dev);
......
此差异已折叠。
......@@ -12,6 +12,10 @@
#include <linux/wmi.h>
#include <acpi/video.h>
static bool force;
module_param(force, bool, 0444);
MODULE_PARM_DESC(force, "Force loading (disable acpi_backlight=xxx checks");
/**
* wmi_brightness_notify() - helper function for calling WMI-wrapped ACPI method
* @w: Pointer to the struct wmi_device identified by %WMI_BRIGHTNESS_GUID
......@@ -91,7 +95,7 @@ static int nvidia_wmi_ec_backlight_probe(struct wmi_device *wdev, const void *ct
int ret;
/* drivers/acpi/video_detect.c also checks that SOURCE == EC */
if (acpi_video_get_backlight_type() != acpi_backlight_nvidia_wmi_ec)
if (!force && acpi_video_get_backlight_type() != acpi_backlight_nvidia_wmi_ec)
return -ENODEV;
/*
......
......@@ -317,8 +317,8 @@ static int tlmi_get_pwd_settings(struct tlmi_pwdcfg *pwdcfg)
return -EIO;
}
copy_size = obj->buffer.length < sizeof(struct tlmi_pwdcfg) ?
obj->buffer.length : sizeof(struct tlmi_pwdcfg);
copy_size = min_t(size_t, obj->buffer.length, sizeof(struct tlmi_pwdcfg));
memcpy(pwdcfg, obj->buffer.pointer, copy_size);
kfree(obj);
......@@ -1089,12 +1089,12 @@ static void tlmi_pwd_setting_release(struct kobject *kobj)
kfree(setting);
}
static struct kobj_type tlmi_attr_setting_ktype = {
static const struct kobj_type tlmi_attr_setting_ktype = {
.release = &tlmi_attr_setting_release,
.sysfs_ops = &tlmi_kobj_sysfs_ops,
};
static struct kobj_type tlmi_pwd_setting_ktype = {
static const struct kobj_type tlmi_pwd_setting_ktype = {
.release = &tlmi_pwd_setting_release,
.sysfs_ops = &tlmi_kobj_sysfs_ops,
};
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册