diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-21 17:32:50 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-21 17:32:50 -0800 |
commit | 69308402ca6f5b80a5a090ade0b13bd146891420 (patch) | |
tree | 5e220abeed09416d93773824f5d2cf84c017859a /drivers | |
parent | 5f5ce6bcfcc3abbaf690fca30a22d0dcf6f36d32 (diff) | |
parent | 0d9bdd8a550170306c2021b8d6766c5343b870c2 (diff) |
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
...
Diffstat (limited to 'drivers')
51 files changed, 2682 insertions, 378 deletions
diff --git a/drivers/hid/surface-hid/surface_hid.c b/drivers/hid/surface-hid/surface_hid.c index d4aa8c81903a..61e5814b0ad7 100644 --- a/drivers/hid/surface-hid/surface_hid.c +++ b/drivers/hid/surface-hid/surface_hid.c @@ -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); diff --git a/drivers/hid/surface-hid/surface_kbd.c b/drivers/hid/surface-hid/surface_kbd.c index 0635341bc517..4fbce201db6a 100644 --- a/drivers/hid/surface-hid/surface_kbd.c +++ b/drivers/hid/surface-hid/surface_kbd.c @@ -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; diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 6a8ea94834fa..0c4b8d8d2b4f 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -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); - return led; + led_dev = class_find_device_by_name(leds_class, provider); + kfree_const(provider); + + return led_module_get(led_dev); } -EXPORT_SYMBOL_GPL(devm_of_led_get); +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_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) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 2f1b718a9189..d7e9ffc7aa23 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -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); diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 3d9533c1b202..049c2f2001ea 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -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 @@ out_unregister: v4l2_async_nf_unregister(notifier); out_cleanup: + v4l2_subdev_put_privacy_led(sd); v4l2_async_nf_cleanup(notifier); kfree(notifier); diff --git a/drivers/media/v4l2-core/v4l2-subdev-priv.h b/drivers/media/v4l2-core/v4l2-subdev-priv.h new file mode 100644 index 000000000000..52391d6d8ab7 --- /dev/null +++ b/drivers/media/v4l2-core/v4l2-subdev-priv.h @@ -0,0 +1,14 @@ +/* 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 diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 4988a25bd8f4..6630fb30bc7d 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -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); diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c index 117bc3f395fd..b7dcc64cd238 100644 --- a/drivers/platform/mellanox/mlxreg-hotplug.c +++ b/drivers/platform/mellanox/mlxreg-hotplug.c @@ -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, ®val); 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); diff --git a/drivers/platform/surface/aggregator/bus.c b/drivers/platform/surface/aggregator/bus.c index de539938896e..7004eb4a63a2 100644 --- a/drivers/platform/surface/aggregator/bus.c +++ b/drivers/platform/surface/aggregator/bus.c @@ -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 diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c index c6537a1b3a2e..535581c0471c 100644 --- a/drivers/platform/surface/aggregator/controller.c +++ b/drivers/platform/surface/aggregator/controller.c @@ -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,9 +1686,9 @@ 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, - const struct ssam_request *spec, - struct ssam_response *rsp) +int ssam_request_do_sync(struct ssam_controller *ctrl, + const struct ssam_request *spec, + struct ssam_response *rsp) { struct ssam_request_sync *rqst; struct ssam_span buf; @@ -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,17 +1738,17 @@ 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, - const struct ssam_request *spec, - struct ssam_response *rsp, - struct ssam_span *buf) +int ssam_request_do_sync_with_buffer(struct ssam_controller *ctrl, + const struct ssam_request *spec, + struct ssam_response *rsp, + struct ssam_span *buf) { struct ssam_request_sync rqst; ssize_t len; @@ -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; diff --git a/drivers/platform/surface/aggregator/ssh_msgb.h b/drivers/platform/surface/aggregator/ssh_msgb.h index f3ecad92eefd..438873e06098 100644 --- a/drivers/platform/surface/aggregator/ssh_msgb.h +++ b/drivers/platform/surface/aggregator/ssh_msgb.h @@ -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. */ diff --git a/drivers/platform/surface/aggregator/ssh_request_layer.c b/drivers/platform/surface/aggregator/ssh_request_layer.c index 69132976d297..90634dcacabf 100644 --- a/drivers/platform/surface/aggregator/ssh_request_layer.c +++ b/drivers/platform/surface/aggregator/ssh_request_layer.c @@ -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; } diff --git a/drivers/platform/surface/aggregator/trace.h b/drivers/platform/surface/aggregator/trace.h index 2a2c17771d01..55cc61bba1da 100644 --- a/drivers/platform/surface/aggregator/trace.h +++ b/drivers/platform/surface/aggregator/trace.h @@ -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 @@ -151,11 +152,43 @@ static inline u32 ssam_trace_get_request_id(const struct ssh_packet *p) } /** + * 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), diff --git a/drivers/platform/surface/surface_acpi_notify.c b/drivers/platform/surface/surface_acpi_notify.c index 50500e562963..897cdd9c3aae 100644 --- a/drivers/platform/surface/surface_acpi_notify.c +++ b/drivers/platform/surface/surface_acpi_notify.c @@ -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) { diff --git a/drivers/platform/surface/surface_aggregator_cdev.c b/drivers/platform/surface/surface_aggregator_cdev.c index 492c82e69182..07f0ed658369 100644 --- a/drivers/platform/surface/surface_aggregator_cdev.c +++ b/drivers/platform/surface/surface_aggregator_cdev.c @@ -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; diff --git a/drivers/platform/surface/surface_aggregator_hub.c b/drivers/platform/surface/surface_aggregator_hub.c index 43061514be38..8b8b80228c14 100644 --- a/drivers/platform/surface/surface_aggregator_hub.c +++ b/drivers/platform/surface/surface_aggregator_hub.c @@ -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); diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index 023f126121d7..296f72d52e6a 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -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, }; diff --git a/drivers/platform/surface/surface_aggregator_tabletsw.c b/drivers/platform/surface/surface_aggregator_tabletsw.c index 27d95a6a7851..9fed800c7cc0 100644 --- a/drivers/platform/surface/surface_aggregator_tabletsw.c +++ b/drivers/platform/surface/surface_aggregator_tabletsw.c @@ -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); diff --git a/drivers/platform/surface/surface_dtx.c b/drivers/platform/surface/surface_dtx.c index ed36944467f9..30cbde278c59 100644 --- a/drivers/platform/surface/surface_dtx.c +++ b/drivers/platform/surface/surface_dtx.c @@ -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); diff --git a/drivers/platform/surface/surface_hotplug.c b/drivers/platform/surface/surface_hotplug.c index f004a2495201..7b6d887dccdb 100644 --- a/drivers/platform/surface/surface_hotplug.c +++ b/drivers/platform/surface/surface_hotplug.c @@ -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), ¶m); - + result = acpi_evaluate_dsm_typed(handle, &shps_dsm_guid, SHPS_DSM_REVISION, + shps_dsm_fn_for_irq(type), ¶m, 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,8 +115,7 @@ static void shps_dsm_notify_irq(struct platform_device *pdev, enum shps_irq_type mutex_unlock(&sdev->lock[type]); - if (result) - ACPI_FREE(result); + ACPI_FREE(result); } static irqreturn_t shps_handle_irq(int irq, void *data) diff --git a/drivers/platform/surface/surface_platform_profile.c b/drivers/platform/surface/surface_platform_profile.c index fbf2e11fd6ce..f433a13c3689 100644 --- a/drivers/platform/surface/surface_platform_profile.c +++ b/drivers/platform/surface/surface_platform_profile.c @@ -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); diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 5692385e2d26..ec7c2b4e1721 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -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 diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 1956469c3457..a48638ad2a8a 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -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 */ diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index 3cbb01ec10e3..ab05b9ee6655 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc.c @@ -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) diff --git a/drivers/platform/x86/amd/pmf/Kconfig b/drivers/platform/x86/amd/pmf/Kconfig index c375498c4071..6d89528c3177 100644 --- a/drivers/platform/x86/amd/pmf/Kconfig +++ b/drivers/platform/x86/amd/pmf/Kconfig @@ -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. diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index d319de8f2132..bdd78076b1d7 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -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. diff --git a/drivers/platform/x86/dell/dell-smo8800.c b/drivers/platform/x86/dell/dell-smo8800.c index 3385e852104c..8d6b7a83cf24 100644 --- a/drivers/platform/x86/dell/dell-smo8800.c +++ b/drivers/platform/x86/dell/dell-smo8800.c @@ -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; diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c index 2bb449845d14..d547c9d09725 100644 --- a/drivers/platform/x86/dell/dell-wmi-ddv.c +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -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,8 +816,13 @@ 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) - return -ENODEV; + 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) @@ -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, diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c index 0a6411a8a104..0285b47d99d1 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c @@ -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, }; diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 2ef201b625b3..873f59c3e280 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -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 } }, diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index d5a33473e838..bbbd9e54e9ee 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -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 diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile index 717933dd0cfd..411df4040427 100644 --- a/drivers/platform/x86/intel/Makefile +++ b/drivers/platform/x86/intel/Makefile @@ -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 diff --git a/drivers/platform/x86/intel/int1092/intel_sar.c b/drivers/platform/x86/intel/int1092/intel_sar.c index e03943e6380a..352fc4596494 100644 --- a/drivers/platform/x86/intel/int1092/intel_sar.c +++ b/drivers/platform/x86/intel/int1092/intel_sar.c @@ -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 && diff --git a/drivers/platform/x86/intel/int3472/Kconfig b/drivers/platform/x86/intel/int3472/Kconfig index 62e5d4cf9ee5..17ae997f93ea 100644 --- a/drivers/platform/x86/intel/int3472/Kconfig +++ b/drivers/platform/x86/intel/int3472/Kconfig @@ -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 diff --git a/drivers/platform/x86/intel/int3472/Makefile b/drivers/platform/x86/intel/int3472/Makefile index cfec7784c5c9..9f16cb514397 100644 --- a/drivers/platform/x86/intel/int3472/Makefile +++ b/drivers/platform/x86/intel/int3472/Makefile @@ -1,4 +1,4 @@ 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 diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c index 74dc2cff799e..1086c3d83494 100644 --- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c +++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c @@ -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 @@ err_unregister_clk: 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, diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h index 53270d19c73a..61688e450ce5 100644 --- a/drivers/platform/x86/intel/int3472/common.h +++ b/drivers/platform/x86/intel/int3472/common.h @@ -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 diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index c42c3faa2c32..f064da74f50a 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -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_clock(int3472); + skl_int3472_unregister_pled(int3472); skl_int3472_unregister_regulator(int3472); return 0; diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c new file mode 100644 index 000000000000..bca1ce7d0d0c --- /dev/null +++ b/drivers/platform/x86/intel/int3472/led.c @@ -0,0 +1,75 @@ +// 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); +} diff --git a/drivers/platform/x86/intel/oaktrail.c b/drivers/platform/x86/intel/oaktrail.c index 7c5c623630c1..fa720967e69b 100644 --- a/drivers/platform/x86/intel/oaktrail.c +++ b/drivers/platform/x86/intel/oaktrail.c @@ -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) diff --git a/drivers/platform/x86/intel/pmc/tgl.c b/drivers/platform/x86/intel/pmc/tgl.c index e3e50538465d..c245ada849d0 100644 --- a/drivers/platform/x86/intel/pmc/tgl.c +++ b/drivers/platform/x86/intel/pmc/tgl.c @@ -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) { diff --git a/drivers/platform/x86/intel/punit_ipc.c b/drivers/platform/x86/intel/punit_ipc.c index 66bb39fd0ef9..cd0ba84cc8e4 100644 --- a/drivers/platform/x86/intel/punit_ipc.c +++ b/drivers/platform/x86/intel/punit_ipc.c @@ -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, diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/tpmi.c new file mode 100644 index 000000000000..c60733261c89 --- /dev/null +++ b/drivers/platform/x86/intel/tpmi.c @@ -0,0 +1,415 @@ +// 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"); diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 89c5374e33b3..13decf36c6de 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -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, - const char *name) +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, diff --git a/drivers/platform/x86/intel/vsec.h b/drivers/platform/x86/intel/vsec.h index 3deeb05cf394..ae8fe92c5595 100644 --- a/drivers/platform/x86/intel/vsec.h +++ b/drivers/platform/x86/intel/vsec.h @@ -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); diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 2fac05a17a5c..7b6779cdb134 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -15,6 +15,7 @@ #include <linux/platform_device.h> #include <linux/platform_data/i2c-mux-reg.h> #include <linux/platform_data/mlxreg.h> +#include <linux/reboot.h> #include <linux/regmap.h> #define MLX_PLAT_DEVICE_NAME "mlxplat" @@ -61,12 +62,19 @@ #define MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET 0x37 #define MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET 0x3a #define MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET 0x3b +#define MLXPLAT_CPLD_LPC_REG_DBG1_OFFSET 0x3c +#define MLXPLAT_CPLD_LPC_REG_DBG2_OFFSET 0x3d +#define MLXPLAT_CPLD_LPC_REG_DBG3_OFFSET 0x3e +#define MLXPLAT_CPLD_LPC_REG_DBG4_OFFSET 0x3f #define MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET 0x40 #define MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET 0x41 #define MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET 0x42 #define MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET 0x43 #define MLXPLAT_CPLD_LPC_REG_AGGRCX_OFFSET 0x44 #define MLXPLAT_CPLD_LPC_REG_AGGRCX_MASK_OFFSET 0x45 +#define MLXPLAT_CPLD_LPC_REG_BRD_OFFSET 0x47 +#define MLXPLAT_CPLD_LPC_REG_BRD_EVENT_OFFSET 0x48 +#define MLXPLAT_CPLD_LPC_REG_BRD_MASK_OFFSET 0x49 #define MLXPLAT_CPLD_LPC_REG_GWP_OFFSET 0x4a #define MLXPLAT_CPLD_LPC_REG_GWP_EVENT_OFFSET 0x4b #define MLXPLAT_CPLD_LPC_REG_GWP_MASK_OFFSET 0x4c @@ -90,6 +98,15 @@ #define MLXPLAT_CPLD_LPC_REG_FAN_OFFSET 0x88 #define MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET 0x89 #define MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET 0x8a +#define MLXPLAT_CPLD_LPC_REG_EROT_OFFSET 0x91 +#define MLXPLAT_CPLD_LPC_REG_EROT_EVENT_OFFSET 0x92 +#define MLXPLAT_CPLD_LPC_REG_EROT_MASK_OFFSET 0x93 +#define MLXPLAT_CPLD_LPC_REG_EROTE_OFFSET 0x94 +#define MLXPLAT_CPLD_LPC_REG_EROTE_EVENT_OFFSET 0x95 +#define MLXPLAT_CPLD_LPC_REG_EROTE_MASK_OFFSET 0x96 +#define MLXPLAT_CPLD_LPC_REG_PWRB_OFFSET 0x97 +#define MLXPLAT_CPLD_LPC_REG_PWRB_EVENT_OFFSET 0x98 +#define MLXPLAT_CPLD_LPC_REG_PWRB_MASK_OFFSET 0x99 #define MLXPLAT_CPLD_LPC_REG_LC_VR_OFFSET 0x9a #define MLXPLAT_CPLD_LPC_REG_LC_VR_EVENT_OFFSET 0x9b #define MLXPLAT_CPLD_LPC_REG_LC_VR_MASK_OFFSET 0x9c @@ -109,6 +126,8 @@ #define MLXPLAT_CPLD_LPC_REG_LC_SD_EVENT_OFFSET 0xaa #define MLXPLAT_CPLD_LPC_REG_LC_SD_MASK_OFFSET 0xab #define MLXPLAT_CPLD_LPC_REG_LC_PWR_ON 0xb2 +#define MLXPLAT_CPLD_LPC_REG_GP4_RO_OFFSET 0xc2 +#define MLXPLAT_CPLD_LPC_REG_SPI_CHNL_SELECT 0xc3 #define MLXPLAT_CPLD_LPC_REG_WD_CLEAR_OFFSET 0xc7 #define MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET 0xc8 #define MLXPLAT_CPLD_LPC_REG_WD1_TMR_OFFSET 0xc9 @@ -119,6 +138,11 @@ #define MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET 0xd1 #define MLXPLAT_CPLD_LPC_REG_WD3_TLEFT_OFFSET 0xd2 #define MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET 0xd3 +#define MLXPLAT_CPLD_LPC_REG_DBG_CTRL_OFFSET 0xd9 +#define MLXPLAT_CPLD_LPC_REG_I2C_CH1_OFFSET 0xdb +#define MLXPLAT_CPLD_LPC_REG_I2C_CH2_OFFSET 0xda +#define MLXPLAT_CPLD_LPC_REG_I2C_CH3_OFFSET 0xdc +#define MLXPLAT_CPLD_LPC_REG_I2C_CH4_OFFSET 0xdd #define MLXPLAT_CPLD_LPC_REG_CPLD1_MVER_OFFSET 0xde #define MLXPLAT_CPLD_LPC_REG_CPLD2_MVER_OFFSET 0xdf #define MLXPLAT_CPLD_LPC_REG_CPLD3_MVER_OFFSET 0xe0 @@ -152,23 +176,19 @@ #define MLXPLAT_CPLD_LPC_REG_CONFIG2_OFFSET 0xfc #define MLXPLAT_CPLD_LPC_REG_CONFIG3_OFFSET 0xfd #define MLXPLAT_CPLD_LPC_IO_RANGE 0x100 -#define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb -#define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda -#define MLXPLAT_CPLD_LPC_I2C_CH3_OFF 0xdc -#define MLXPLAT_CPLD_LPC_I2C_CH4_OFF 0xdd #define MLXPLAT_CPLD_LPC_PIO_OFFSET 0x10000UL #define MLXPLAT_CPLD_LPC_REG1 ((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \ - MLXPLAT_CPLD_LPC_I2C_CH1_OFF) | \ + MLXPLAT_CPLD_LPC_REG_I2C_CH1_OFFSET) | \ MLXPLAT_CPLD_LPC_PIO_OFFSET) #define MLXPLAT_CPLD_LPC_REG2 ((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \ - MLXPLAT_CPLD_LPC_I2C_CH2_OFF) | \ + MLXPLAT_CPLD_LPC_REG_I2C_CH2_OFFSET) | \ MLXPLAT_CPLD_LPC_PIO_OFFSET) #define MLXPLAT_CPLD_LPC_REG3 ((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \ - MLXPLAT_CPLD_LPC_I2C_CH3_OFF) | \ + MLXPLAT_CPLD_LPC_REG_I2C_CH3_OFFSET) | \ MLXPLAT_CPLD_LPC_PIO_OFFSET) #define MLXPLAT_CPLD_LPC_REG4 ((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \ - MLXPLAT_CPLD_LPC_I2C_CH4_OFF) | \ + MLXPLAT_CPLD_LPC_REG_I2C_CH4_OFFSET) | \ MLXPLAT_CPLD_LPC_PIO_OFFSET) /* Masks for aggregation, psu, pwr and fan event in CPLD related registers. */ @@ -202,6 +222,7 @@ MLXPLAT_CPLD_AGGR_MASK_LC_SDWN) #define MLXPLAT_CPLD_LOW_AGGR_MASK_LOW 0xc1 #define MLXPLAT_CPLD_LOW_AGGR_MASK_ASIC2 BIT(2) +#define MLXPLAT_CPLD_LOW_AGGR_MASK_PWR_BUT BIT(4) #define MLXPLAT_CPLD_LOW_AGGR_MASK_I2C BIT(6) #define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0) #define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0) @@ -214,6 +235,17 @@ #define MLXPLAT_CPLD_LED_HI_NIBBLE_MASK GENMASK(3, 0) #define MLXPLAT_CPLD_VOLTREG_UPD_MASK GENMASK(5, 4) #define MLXPLAT_CPLD_GWP_MASK GENMASK(0, 0) +#define MLXPLAT_CPLD_EROT_MASK GENMASK(1, 0) +#define MLXPLAT_CPLD_PWR_BUTTON_MASK BIT(0) +#define MLXPLAT_CPLD_LATCH_RST_MASK BIT(5) +#define MLXPLAT_CPLD_THERMAL1_PDB_MASK BIT(3) +#define MLXPLAT_CPLD_THERMAL2_PDB_MASK BIT(4) +#define MLXPLAT_CPLD_INTRUSION_MASK BIT(6) +#define MLXPLAT_CPLD_PWM_PG_MASK BIT(7) +#define MLXPLAT_CPLD_L1_CHA_HEALTH_MASK (MLXPLAT_CPLD_THERMAL1_PDB_MASK | \ + MLXPLAT_CPLD_THERMAL2_PDB_MASK | \ + MLXPLAT_CPLD_INTRUSION_MASK |\ + MLXPLAT_CPLD_PWM_PG_MASK) #define MLXPLAT_CPLD_I2C_CAP_BIT 0x04 #define MLXPLAT_CPLD_I2C_CAP_MASK GENMASK(5, MLXPLAT_CPLD_I2C_CAP_BIT) @@ -226,6 +258,8 @@ /* Masks for aggregation for modular systems */ #define MLXPLAT_CPLD_LPC_LC_MASK GENMASK(7, 0) +#define MLXPLAT_CPLD_HALT_MASK BIT(3) + /* Default I2C parent bus number */ #define MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR 1 @@ -243,6 +277,8 @@ #define MLXPLAT_CPLD_CH2_ETH_MODULAR 3 #define MLXPLAT_CPLD_CH3_ETH_MODULAR 43 #define MLXPLAT_CPLD_CH4_ETH_MODULAR 51 +#define MLXPLAT_CPLD_CH2_RACK_SWITCH 18 +#define MLXPLAT_CPLD_CH2_NG800 34 /* Number of LPC attached MUX platform devices */ #define MLXPLAT_CPLD_LPC_MUX_DEVS 4 @@ -280,6 +316,12 @@ /* Minimum power required for turning on Ethernet modular system (WATT) */ #define MLXPLAT_CPLD_ETH_MODULAR_PWR_MIN 50 +/* Default value for PWM control register for rack switch system */ +#define MLXPLAT_REGMAP_NVSWITCH_PWM_DEFAULT 0xf4 + +#define MLXPLAT_I2C_MAIN_BUS_NOTIFIED 0x01 +#define MLXPLAT_I2C_MAIN_BUS_HANDLE_CREATED 0x02 + /* mlxplat_priv - platform private data * @pdev_i2c - i2c controller platform device * @pdev_mux - array of mux platform devices @@ -289,6 +331,9 @@ * @pdev_fan - FAN platform devices * @pdev_wd - array of watchdog platform devices * @regmap: device register map + * @hotplug_resources: system hotplug resources + * @hotplug_resources_size: size of system hotplug resources + * @hi2c_main_init_status: init status of I2C main bus */ struct mlxplat_priv { struct platform_device *pdev_i2c; @@ -299,8 +344,14 @@ struct mlxplat_priv { struct platform_device *pdev_fan; struct platform_device *pdev_wd[MLXPLAT_CPLD_WD_MAX_DEVS]; void *regmap; + struct resource *hotplug_resources; + unsigned int hotplug_resources_size; + u8 i2c_main_init_status; }; +static struct platform_device *mlxplat_dev; +static int mlxplat_i2c_main_complition_notify(void *handle, int id); + /* Regions for LPC I2C controller and LPC base register space */ static const struct resource mlxplat_lpc_resources[] = { [0] = DEFINE_RES_NAMED(MLXPLAT_CPLD_LPC_I2C_BASE_ADRR, @@ -312,6 +363,11 @@ static const struct resource mlxplat_lpc_resources[] = { IORESOURCE_IO), }; +/* Platform systems default i2c data */ +static struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_i2c_default_data = { + .completion_notify = mlxplat_i2c_main_complition_notify, +}; + /* Platform i2c next generation systems data */ static struct mlxreg_core_data mlxplat_mlxcpld_i2c_ng_items_data[] = { { @@ -334,6 +390,7 @@ static struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_i2c_ng_data = { .mask = MLXPLAT_CPLD_AGGR_MASK_COMEX, .cell_low = MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET, .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_I2C, + .completion_notify = mlxplat_i2c_main_complition_notify, }; /* Platform default channels */ @@ -460,6 +517,67 @@ static struct i2c_mux_reg_platform_data mlxplat_modular_mux_data[] = { }, }; +/* Platform channels for rack switch system family */ +static const int mlxplat_rack_switch_channels[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, +}; + +/* Platform rack switch mux data */ +static struct i2c_mux_reg_platform_data mlxplat_rack_switch_mux_data[] = { + { + .parent = 1, + .base_nr = MLXPLAT_CPLD_CH1, + .write_only = 1, + .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG1, + .reg_size = 1, + .idle_in_use = 1, + .values = mlxplat_rack_switch_channels, + .n_values = ARRAY_SIZE(mlxplat_rack_switch_channels), + }, + { + .parent = 1, + .base_nr = MLXPLAT_CPLD_CH2_RACK_SWITCH, + .write_only = 1, + .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG2, + .reg_size = 1, + .idle_in_use = 1, + .values = mlxplat_msn21xx_channels, + .n_values = ARRAY_SIZE(mlxplat_msn21xx_channels), + }, + +}; + +/* Platform channels for ng800 system family */ +static const int mlxplat_ng800_channels[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 +}; + +/* Platform ng800 mux data */ +static struct i2c_mux_reg_platform_data mlxplat_ng800_mux_data[] = { + { + .parent = 1, + .base_nr = MLXPLAT_CPLD_CH1, + .write_only = 1, + .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG1, + .reg_size = 1, + .idle_in_use = 1, + .values = mlxplat_ng800_channels, + .n_values = ARRAY_SIZE(mlxplat_ng800_channels), + }, + { + .parent = 1, + .base_nr = MLXPLAT_CPLD_CH2_NG800, + .write_only = 1, + .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG2, + .reg_size = 1, + .idle_in_use = 1, + .values = mlxplat_msn21xx_channels, + .n_values = ARRAY_SIZE(mlxplat_msn21xx_channels), + }, + +}; + /* Platform hotplug devices */ static struct i2c_board_info mlxplat_mlxcpld_pwr[] = { { @@ -479,6 +597,15 @@ static struct i2c_board_info mlxplat_mlxcpld_ext_pwr[] = { }, }; +static struct i2c_board_info mlxplat_mlxcpld_pwr_ng800[] = { + { + I2C_BOARD_INFO("dps460", 0x59), + }, + { + I2C_BOARD_INFO("dps460", 0x5a), + }, +}; + static struct i2c_board_info mlxplat_mlxcpld_fan[] = { { I2C_BOARD_INFO("24c32", 0x50), @@ -558,6 +685,23 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_wc_items_data[] = { }, }; +static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_ng800_items_data[] = { + { + .label = "pwr1", + .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, + .mask = BIT(0), + .hpdev.brdinfo = &mlxplat_mlxcpld_pwr_ng800[0], + .hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR, + }, + { + .label = "pwr2", + .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, + .mask = BIT(1), + .hpdev.brdinfo = &mlxplat_mlxcpld_pwr_ng800[1], + .hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR, + }, +}; + static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_items_data[] = { { .label = "fan1", @@ -1181,6 +1325,47 @@ static struct mlxreg_core_item mlxplat_mlxcpld_ext_items[] = { } }; +static struct mlxreg_core_item mlxplat_mlxcpld_ng800_items[] = { + { + .data = mlxplat_mlxcpld_default_ng_psu_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET, + .mask = MLXPLAT_CPLD_PSU_EXT_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_PSU_I2C_CAP_OFFSET, + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_psu_items_data), + .inversed = 1, + .health = false, + }, + { + .data = mlxplat_mlxcpld_default_pwr_ng800_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, + .mask = MLXPLAT_CPLD_PWR_EXT_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_PSU_I2C_CAP_OFFSET, + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_pwr_ng800_items_data), + .inversed = 0, + .health = false, + }, + { + .data = mlxplat_mlxcpld_default_ng_fan_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, + .mask = MLXPLAT_CPLD_FAN_NG_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_fan_items_data), + .inversed = 1, + .health = false, + }, + { + .data = mlxplat_mlxcpld_default_asic_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET, + .mask = MLXPLAT_CPLD_ASIC_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_asic_items_data), + .inversed = 0, + .health = true, + }, +}; + static struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_ext_data = { .items = mlxplat_mlxcpld_ext_items, @@ -1191,6 +1376,16 @@ struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_ext_data = { .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW | MLXPLAT_CPLD_LOW_AGGR_MASK_ASIC2, }; +static +struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_ng800_data = { + .items = mlxplat_mlxcpld_ng800_items, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_ng800_items), + .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET, + .mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF | MLXPLAT_CPLD_AGGR_MASK_COMEX, + .cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET, + .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW | MLXPLAT_CPLD_LOW_AGGR_MASK_ASIC2, +}; + static struct mlxreg_core_data mlxplat_mlxcpld_modular_pwr_items_data[] = { { .label = "pwr1", @@ -2042,7 +2237,7 @@ static struct mlxreg_core_data mlxplat_mlxcpld_global_wp_items_data[] = { }, }; -static struct mlxreg_core_item mlxplat_mlxcpld_nvlink_blade_items[] = { +static struct mlxreg_core_item mlxplat_mlxcpld_chassis_blade_items[] = { { .data = mlxplat_mlxcpld_global_wp_items_data, .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, @@ -2055,15 +2250,256 @@ static struct mlxreg_core_item mlxplat_mlxcpld_nvlink_blade_items[] = { }; static -struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_nvlink_blade_data = { - .items = mlxplat_mlxcpld_nvlink_blade_items, - .counter = ARRAY_SIZE(mlxplat_mlxcpld_nvlink_blade_items), +struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_chassis_blade_data = { + .items = mlxplat_mlxcpld_chassis_blade_items, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_chassis_blade_items), .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET, .mask = MLXPLAT_CPLD_AGGR_MASK_COMEX, .cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET, .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW, }; +/* Platform hotplug for switch systems family data */ +static struct mlxreg_core_data mlxplat_mlxcpld_erot_ap_items_data[] = { + { + .label = "erot1_ap", + .reg = MLXPLAT_CPLD_LPC_REG_EROT_OFFSET, + .mask = BIT(0), + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + }, + { + .label = "erot2_ap", + .reg = MLXPLAT_CPLD_LPC_REG_EROT_OFFSET, + .mask = BIT(1), + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + }, +}; + +static struct mlxreg_core_data mlxplat_mlxcpld_erot_error_items_data[] = { + { + .label = "erot1_error", + .reg = MLXPLAT_CPLD_LPC_REG_EROTE_OFFSET, + .mask = BIT(0), + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + }, + { + .label = "erot2_error", + .reg = MLXPLAT_CPLD_LPC_REG_EROTE_OFFSET, + .mask = BIT(1), + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + }, +}; + +static struct mlxreg_core_item mlxplat_mlxcpld_rack_switch_items[] = { + { + .data = mlxplat_mlxcpld_ext_psu_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET, + .mask = MLXPLAT_CPLD_PSU_EXT_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_PSU_I2C_CAP_OFFSET, + .count = ARRAY_SIZE(mlxplat_mlxcpld_ext_psu_items_data), + .inversed = 1, + .health = false, + }, + { + .data = mlxplat_mlxcpld_ext_pwr_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, + .mask = MLXPLAT_CPLD_PWR_EXT_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_PSU_I2C_CAP_OFFSET, + .count = ARRAY_SIZE(mlxplat_mlxcpld_ext_pwr_items_data), + .inversed = 0, + .health = false, + }, + { + .data = mlxplat_mlxcpld_default_ng_fan_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, + .mask = MLXPLAT_CPLD_FAN_NG_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_fan_items_data), + .inversed = 1, + .health = false, + }, + { + .data = mlxplat_mlxcpld_erot_ap_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_EROT_OFFSET, + .mask = MLXPLAT_CPLD_EROT_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_erot_ap_items_data), + .inversed = 1, + .health = false, + }, + { + .data = mlxplat_mlxcpld_erot_error_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_EROTE_OFFSET, + .mask = MLXPLAT_CPLD_EROT_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_erot_error_items_data), + .inversed = 1, + .health = false, + }, +}; + +static +struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_rack_switch_data = { + .items = mlxplat_mlxcpld_rack_switch_items, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_rack_switch_items), + .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET, + .mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF | MLXPLAT_CPLD_AGGR_MASK_COMEX, + .cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET, + .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW, +}; + +/* Callback performs graceful shutdown after notification about power button event */ +static int +mlxplat_mlxcpld_l1_switch_pwr_events_handler(void *handle, enum mlxreg_hotplug_kind kind, + u8 action) +{ + dev_info(&mlxplat_dev->dev, "System shutdown due to short press of power button"); + kernel_halt(); + return 0; +} + +static struct mlxreg_core_hotplug_notifier mlxplat_mlxcpld_l1_switch_pwr_events_notifier = { + .user_handler = mlxplat_mlxcpld_l1_switch_pwr_events_handler, +}; + +/* Platform hotplug for l1 switch systems family data */ +static struct mlxreg_core_data mlxplat_mlxcpld_l1_switch_pwr_events_items_data[] = { + { + .label = "power_button", + .reg = MLXPLAT_CPLD_LPC_REG_PWRB_OFFSET, + .mask = MLXPLAT_CPLD_PWR_BUTTON_MASK, + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + .hpdev.notifier = &mlxplat_mlxcpld_l1_switch_pwr_events_notifier, + }, +}; + +/* Callback activates latch reset flow after notification about intrusion event */ +static int +mlxplat_mlxcpld_l1_switch_intrusion_events_handler(void *handle, enum mlxreg_hotplug_kind kind, + u8 action) +{ + struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev); + u32 regval; + int err; + + err = regmap_read(priv->regmap, MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, ®val); + if (err) + goto fail_regmap_read; + + if (action) { + dev_info(&mlxplat_dev->dev, "Detected intrusion - system latch is opened"); + err = regmap_write(priv->regmap, MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, + regval | MLXPLAT_CPLD_LATCH_RST_MASK); + } else { + dev_info(&mlxplat_dev->dev, "System latch is properly closed"); + err = regmap_write(priv->regmap, MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, + regval & ~MLXPLAT_CPLD_LATCH_RST_MASK); + } + + if (err) + goto fail_regmap_write; + + return 0; + +fail_regmap_read: +fail_regmap_write: + dev_err(&mlxplat_dev->dev, "Register access failed"); + return err; +} + +static struct mlxreg_core_hotplug_notifier mlxplat_mlxcpld_l1_switch_intrusion_events_notifier = { + .user_handler = mlxplat_mlxcpld_l1_switch_intrusion_events_handler, +}; + +static struct mlxreg_core_data mlxplat_mlxcpld_l1_switch_health_events_items_data[] = { + { + .label = "thermal1_pdb", + .reg = MLXPLAT_CPLD_LPC_REG_BRD_OFFSET, + .mask = MLXPLAT_CPLD_THERMAL1_PDB_MASK, + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + }, + { + .label = "thermal2_pdb", + .reg = MLXPLAT_CPLD_LPC_REG_BRD_OFFSET, + .mask = MLXPLAT_CPLD_THERMAL2_PDB_MASK, + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + }, + { + .label = "intrusion", + .reg = MLXPLAT_CPLD_LPC_REG_BRD_OFFSET, + .mask = MLXPLAT_CPLD_INTRUSION_MASK, + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + .hpdev.notifier = &mlxplat_mlxcpld_l1_switch_intrusion_events_notifier, + }, + { + .label = "pwm_pg", + .reg = MLXPLAT_CPLD_LPC_REG_BRD_OFFSET, + .mask = MLXPLAT_CPLD_PWM_PG_MASK, + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + }, +}; + +static struct mlxreg_core_item mlxplat_mlxcpld_l1_switch_events_items[] = { + { + .data = mlxplat_mlxcpld_default_ng_fan_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, + .mask = MLXPLAT_CPLD_FAN_NG_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_fan_items_data), + .inversed = 1, + .health = false, + }, + { + .data = mlxplat_mlxcpld_erot_ap_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_EROT_OFFSET, + .mask = MLXPLAT_CPLD_EROT_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_erot_ap_items_data), + .inversed = 1, + .health = false, + }, + { + .data = mlxplat_mlxcpld_erot_error_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_EROTE_OFFSET, + .mask = MLXPLAT_CPLD_EROT_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_erot_error_items_data), + .inversed = 1, + .health = false, + }, + { + .data = mlxplat_mlxcpld_l1_switch_pwr_events_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_PWRB_OFFSET, + .mask = MLXPLAT_CPLD_PWR_BUTTON_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_l1_switch_pwr_events_items_data), + .inversed = 0, + .health = false, + }, + { + .data = mlxplat_mlxcpld_l1_switch_health_events_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_BRD_OFFSET, + .mask = MLXPLAT_CPLD_L1_CHA_HEALTH_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_l1_switch_health_events_items_data), + .inversed = 0, + .health = false, + .ind = 8, + }, +}; + +static +struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_l1_switch_data = { + .items = mlxplat_mlxcpld_l1_switch_events_items, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_l1_switch_events_items), + .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET, + .mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF | MLXPLAT_CPLD_AGGR_MASK_COMEX, + .cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET, + .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW | MLXPLAT_CPLD_LOW_AGGR_MASK_PWR_BUT, +}; + /* Platform led default data */ static struct mlxreg_core_data mlxplat_mlxcpld_default_led_data[] = { { @@ -2593,6 +3029,114 @@ static struct mlxreg_core_platform_data mlxplat_modular_led_data = { .counter = ARRAY_SIZE(mlxplat_mlxcpld_modular_led_data), }; +/* Platform led data for chassis system */ +static struct mlxreg_core_data mlxplat_mlxcpld_l1_switch_led_data[] = { + { + .label = "status:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + }, + { + .label = "status:orange", + .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK + }, + { + .label = "fan1:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(0), + }, + { + .label = "fan1:orange", + .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(0), + }, + { + .label = "fan2:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET, + .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(1), + }, + { + .label = "fan2:orange", + .reg = MLXPLAT_CPLD_LPC_REG_LED2_OFFSET, + .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(1), + }, + { + .label = "fan3:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(2), + }, + { + .label = "fan3:orange", + .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(2), + }, + { + .label = "fan4:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET, + .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(3), + }, + { + .label = "fan4:orange", + .reg = MLXPLAT_CPLD_LPC_REG_LED3_OFFSET, + .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(3), + }, + { + .label = "fan5:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(4), + }, + { + .label = "fan5:orange", + .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(4), + }, + { + .label = "fan6:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET, + .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(5), + }, + { + .label = "fan6:orange", + .reg = MLXPLAT_CPLD_LPC_REG_LED4_OFFSET, + .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(5), + }, + { + .label = "uid:blue", + .reg = MLXPLAT_CPLD_LPC_REG_LED5_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + }, +}; + +static struct mlxreg_core_platform_data mlxplat_l1_switch_led_data = { + .data = mlxplat_mlxcpld_l1_switch_led_data, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_l1_switch_led_data), +}; + /* Platform register access default */ static struct mlxreg_core_data mlxplat_mlxcpld_default_regs_io_data[] = { { @@ -2948,6 +3492,51 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = { .mode = 0200, }, { + .label = "erot1_reset", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_GP2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0644, + }, + { + .label = "erot2_reset", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_GP2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0644, + }, + { + .label = "clk_brd_prog_en", + .reg = MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0644, + .secured = 1, + }, + { + .label = "erot1_recovery", + .reg = MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0644, + }, + { + .label = "erot2_recovery", + .reg = MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0644, + }, + { + .label = "erot1_wp", + .reg = MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0644, + .secured = 1, + }, + { + .label = "erot2_wp", + .reg = MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0644, + .secured = 1, + }, + { .label = "reset_long_pb", .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET, .mask = GENMASK(7, 0) & ~BIT(0), @@ -3014,7 +3603,7 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = { .mode = 0444, }, { - .label = "reset_voltmon_upgrade_fail", + .label = "reset_pwr_converter_fail", .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, .mask = GENMASK(7, 0) & ~BIT(0), .mode = 0444, @@ -3050,6 +3639,12 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = { .mode = 0444, }, { + .label = "reset_ac_ok_fail", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0444, + }, + { .label = "psu1_on", .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, .mask = GENMASK(7, 0) & ~BIT(0), @@ -3074,12 +3669,48 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = { .mode = 0200, }, { + .label = "deep_pwr_cycle", + .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0200, + }, + { + .label = "latch_reset", + .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0200, + }, + { .label = "jtag_enable", .reg = MLXPLAT_CPLD_LPC_REG_GP2_OFFSET, .mask = GENMASK(7, 0) & ~BIT(4), .mode = 0644, }, { + .label = "dbg1", + .reg = MLXPLAT_CPLD_LPC_REG_DBG1_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0644, + }, + { + .label = "dbg2", + .reg = MLXPLAT_CPLD_LPC_REG_DBG2_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0644, + }, + { + .label = "dbg3", + .reg = MLXPLAT_CPLD_LPC_REG_DBG3_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0644, + }, + { + .label = "dbg4", + .reg = MLXPLAT_CPLD_LPC_REG_DBG4_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0644, + }, + { .label = "asic_health", .reg = MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET, .mask = MLXPLAT_CPLD_ASIC_MASK, @@ -3131,6 +3762,13 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = { .mode = 0444, }, { + .label = "pwr_converter_prog_en", + .reg = MLXPLAT_CPLD_LPC_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0644, + .secured = 1, + }, + { .label = "vpd_wp", .reg = MLXPLAT_CPLD_LPC_REG_GP0_OFFSET, .mask = GENMASK(7, 0) & ~BIT(3), @@ -3143,6 +3781,49 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = { .mode = 0644, }, { + .label = "erot1_ap_reset", + .reg = MLXPLAT_CPLD_LPC_REG_GP4_RO_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0444, + }, + { + .label = "erot2_ap_reset", + .reg = MLXPLAT_CPLD_LPC_REG_GP4_RO_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0444, + }, + { + .label = "clk_brd1_boot_fail", + .reg = MLXPLAT_CPLD_LPC_REG_GP4_RO_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0444, + }, + { + .label = "clk_brd2_boot_fail", + .reg = MLXPLAT_CPLD_LPC_REG_GP4_RO_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0444, + }, + { + .label = "clk_brd_fail", + .reg = MLXPLAT_CPLD_LPC_REG_GP4_RO_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0444, + }, + { + .label = "asic_pg_fail", + .reg = MLXPLAT_CPLD_LPC_REG_GP4_RO_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0444, + }, + { + .label = "spi_chnl_select", + .reg = MLXPLAT_CPLD_LPC_REG_SPI_CHNL_SELECT, + .mask = GENMASK(7, 0), + .bit = 1, + .mode = 0644, + }, + { .label = "config1", .reg = MLXPLAT_CPLD_LPC_REG_CONFIG1_OFFSET, .bit = GENMASK(7, 0), @@ -3657,8 +4338,8 @@ static struct mlxreg_core_platform_data mlxplat_modular_regs_io_data = { .counter = ARRAY_SIZE(mlxplat_mlxcpld_modular_regs_io_data), }; -/* Platform register access for NVLink blade systems family data */ -static struct mlxreg_core_data mlxplat_mlxcpld_nvlink_blade_regs_io_data[] = { +/* Platform register access for chassis blade systems family data */ +static struct mlxreg_core_data mlxplat_mlxcpld_chassis_blade_regs_io_data[] = { { .label = "cpld1_version", .reg = MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFFSET, @@ -3855,9 +4536,9 @@ static struct mlxreg_core_data mlxplat_mlxcpld_nvlink_blade_regs_io_data[] = { }, }; -static struct mlxreg_core_platform_data mlxplat_nvlink_blade_regs_io_data = { - .data = mlxplat_mlxcpld_nvlink_blade_regs_io_data, - .counter = ARRAY_SIZE(mlxplat_mlxcpld_nvlink_blade_regs_io_data), +static struct mlxreg_core_platform_data mlxplat_chassis_blade_regs_io_data = { + .data = mlxplat_mlxcpld_chassis_blade_regs_io_data, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_chassis_blade_regs_io_data), }; /* Platform FAN default */ @@ -4242,11 +4923,18 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_SAFE_BIOS_OFFSET: case MLXPLAT_CPLD_LPC_SAFE_BIOS_WP_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG1_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG3_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG4_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRCX_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_GWP_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_GWP_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_BRD_OFFSET: + case MLXPLAT_CPLD_LPC_REG_BRD_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_BRD_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC2_EVENT_OFFSET: @@ -4257,6 +4945,12 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROT_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROT_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROTE_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROTE_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWRB_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWRB_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLC_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_LC_IN_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_LC_IN_MASK_OFFSET: @@ -4274,6 +4968,7 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_LC_SD_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_LC_SD_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_LC_PWR_ON: + case MLXPLAT_CPLD_LPC_REG_SPI_CHNL_SELECT: case MLXPLAT_CPLD_LPC_REG_WD_CLEAR_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD1_TMR_OFFSET: @@ -4284,6 +4979,11 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD3_TLEFT_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG_CTRL_OFFSET: + case MLXPLAT_CPLD_LPC_REG_I2C_CH1_OFFSET: + case MLXPLAT_CPLD_LPC_REG_I2C_CH2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_I2C_CH3_OFFSET: + case MLXPLAT_CPLD_LPC_REG_I2C_CH4_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM1_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM2_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM3_OFFSET: @@ -4334,6 +5034,10 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_SAFE_BIOS_WP_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG1_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG3_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG4_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET: @@ -4343,6 +5047,9 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_GWP_OFFSET: case MLXPLAT_CPLD_LPC_REG_GWP_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_GWP_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_BRD_OFFSET: + case MLXPLAT_CPLD_LPC_REG_BRD_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_BRD_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET: @@ -4358,6 +5065,15 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_FAN_OFFSET: case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROT_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROT_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROTE_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROTE_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROTE_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWRB_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWRB_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWRB_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLC_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLC_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_LC_IN_OFFSET: @@ -4382,6 +5098,8 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_LC_SD_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_LC_SD_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_LC_PWR_ON: + case MLXPLAT_CPLD_LPC_REG_GP4_RO_OFFSET: + case MLXPLAT_CPLD_LPC_REG_SPI_CHNL_SELECT: case MLXPLAT_CPLD_LPC_REG_WD_CLEAR_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD1_TMR_OFFSET: @@ -4392,6 +5110,11 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD3_TLEFT_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG_CTRL_OFFSET: + case MLXPLAT_CPLD_LPC_REG_I2C_CH1_OFFSET: + case MLXPLAT_CPLD_LPC_REG_I2C_CH2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_I2C_CH3_OFFSET: + case MLXPLAT_CPLD_LPC_REG_I2C_CH4_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD1_MVER_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD2_MVER_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD3_MVER_OFFSET: @@ -4468,6 +5191,10 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_SAFE_BIOS_WP_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGR_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG1_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG3_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG4_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRCO_OFFSET: @@ -4477,6 +5204,9 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_GWP_OFFSET: case MLXPLAT_CPLD_LPC_REG_GWP_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_GWP_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_BRD_OFFSET: + case MLXPLAT_CPLD_LPC_REG_BRD_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_BRD_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET: @@ -4492,6 +5222,15 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_FAN_OFFSET: case MLXPLAT_CPLD_LPC_REG_FAN_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_FAN_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROT_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROT_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROTE_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROTE_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_EROTE_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWRB_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWRB_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWRB_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLC_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRLC_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_LC_IN_OFFSET: @@ -4516,10 +5255,17 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_LC_SD_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_LC_SD_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_LC_PWR_ON: + case MLXPLAT_CPLD_LPC_REG_GP4_RO_OFFSET: + case MLXPLAT_CPLD_LPC_REG_SPI_CHNL_SELECT: case MLXPLAT_CPLD_LPC_REG_WD2_TMR_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD2_TLEFT_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD3_TLEFT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_DBG_CTRL_OFFSET: + case MLXPLAT_CPLD_LPC_REG_I2C_CH1_OFFSET: + case MLXPLAT_CPLD_LPC_REG_I2C_CH2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_I2C_CH3_OFFSET: + case MLXPLAT_CPLD_LPC_REG_I2C_CH4_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD1_MVER_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD2_MVER_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD3_MVER_OFFSET: @@ -4583,6 +5329,13 @@ static const struct reg_default mlxplat_mlxcpld_regmap_ng400[] = { { MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET, 0x00 }, }; +static const struct reg_default mlxplat_mlxcpld_regmap_rack_switch[] = { + { MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET, MLXPLAT_REGMAP_NVSWITCH_PWM_DEFAULT }, + { MLXPLAT_CPLD_LPC_REG_WD1_ACT_OFFSET, 0x00 }, + { MLXPLAT_CPLD_LPC_REG_WD2_ACT_OFFSET, 0x00 }, + { MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET, 0x00 }, +}; + static const struct reg_default mlxplat_mlxcpld_regmap_eth_modular[] = { { MLXPLAT_CPLD_LPC_REG_GP2_OFFSET, 0x61 }, { MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET, 0x00 }, @@ -4676,6 +5429,20 @@ static const struct regmap_config mlxplat_mlxcpld_regmap_config_ng400 = { .reg_write = mlxplat_mlxcpld_reg_write, }; +static const struct regmap_config mlxplat_mlxcpld_regmap_config_rack_switch = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 255, + .cache_type = REGCACHE_FLAT, + .writeable_reg = mlxplat_mlxcpld_writeable_reg, + .readable_reg = mlxplat_mlxcpld_readable_reg, + .volatile_reg = mlxplat_mlxcpld_volatile_reg, + .reg_defaults = mlxplat_mlxcpld_regmap_rack_switch, + .num_reg_defaults = ARRAY_SIZE(mlxplat_mlxcpld_regmap_rack_switch), + .reg_read = mlxplat_mlxcpld_reg_read, + .reg_write = mlxplat_mlxcpld_reg_write, +}; + static const struct regmap_config mlxplat_mlxcpld_regmap_config_eth_modular = { .reg_bits = 8, .val_bits = 8, @@ -4694,7 +5461,6 @@ static struct resource mlxplat_mlxcpld_resources[] = { [0] = DEFINE_RES_IRQ_NAMED(MLXPLAT_CPLD_LPC_SYSIRQ, "mlxreg-hotplug"), }; -static struct platform_device *mlxplat_dev; static struct mlxreg_core_hotplug_platform_data *mlxplat_i2c; static struct mlxreg_core_hotplug_platform_data *mlxplat_hotplug; static struct mlxreg_core_platform_data *mlxplat_led; @@ -4704,6 +5470,14 @@ static struct mlxreg_core_platform_data *mlxplat_wd_data[MLXPLAT_CPLD_WD_MAX_DEVS]; static const struct regmap_config *mlxplat_regmap_config; +/* Platform default poweroff function */ +static void mlxplat_poweroff(void) +{ + struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev); + + regmap_write(priv->regmap, MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, MLXPLAT_CPLD_HALT_MASK); +} + static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) { int i; @@ -4722,6 +5496,7 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) mlxplat_led = &mlxplat_default_led_data; mlxplat_regs_io = &mlxplat_default_regs_io_data; mlxplat_wd_data[0] = &mlxplat_mlxcpld_wd_set_type1[0]; + mlxplat_i2c = &mlxplat_mlxcpld_i2c_default_data; return 1; } @@ -4744,6 +5519,7 @@ static int __init mlxplat_dmi_default_wc_matched(const struct dmi_system_id *dmi mlxplat_led = &mlxplat_default_led_wc_data; mlxplat_regs_io = &mlxplat_default_regs_io_data; mlxplat_wd_data[0] = &mlxplat_mlxcpld_wd_set_type1[0]; + mlxplat_i2c = &mlxplat_mlxcpld_i2c_default_data; return 1; } @@ -4791,6 +5567,7 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) mlxplat_led = &mlxplat_msn21xx_led_data; mlxplat_regs_io = &mlxplat_msn21xx_regs_io_data; mlxplat_wd_data[0] = &mlxplat_mlxcpld_wd_set_type1[0]; + mlxplat_i2c = &mlxplat_mlxcpld_i2c_default_data; return 1; } @@ -4813,6 +5590,7 @@ static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi) mlxplat_led = &mlxplat_default_led_data; mlxplat_regs_io = &mlxplat_msn21xx_regs_io_data; mlxplat_wd_data[0] = &mlxplat_mlxcpld_wd_set_type1[0]; + mlxplat_i2c = &mlxplat_mlxcpld_i2c_default_data; return 1; } @@ -4835,6 +5613,7 @@ static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi) mlxplat_led = &mlxplat_msn21xx_led_data; mlxplat_regs_io = &mlxplat_msn21xx_regs_io_data; mlxplat_wd_data[0] = &mlxplat_mlxcpld_wd_set_type1[0]; + mlxplat_i2c = &mlxplat_mlxcpld_i2c_default_data; return 1; } @@ -4884,6 +5663,7 @@ static int __init mlxplat_dmi_comex_matched(const struct dmi_system_id *dmi) mlxplat_fan = &mlxplat_default_fan_data; for (i = 0; i < ARRAY_SIZE(mlxplat_mlxcpld_wd_set_type2); i++) mlxplat_wd_data[i] = &mlxplat_mlxcpld_wd_set_type2[i]; + mlxplat_i2c = &mlxplat_mlxcpld_i2c_default_data; mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_comex; return 1; @@ -4935,14 +5715,14 @@ static int __init mlxplat_dmi_modular_matched(const struct dmi_system_id *dmi) return 1; } -static int __init mlxplat_dmi_nvlink_blade_matched(const struct dmi_system_id *dmi) +static int __init mlxplat_dmi_chassis_blade_matched(const struct dmi_system_id *dmi) { int i; mlxplat_max_adap_num = MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM; mlxplat_mux_num = ARRAY_SIZE(mlxplat_default_mux_data); mlxplat_mux_data = mlxplat_default_mux_data; - mlxplat_hotplug = &mlxplat_mlxcpld_nvlink_blade_data; + mlxplat_hotplug = &mlxplat_mlxcpld_chassis_blade_data; mlxplat_hotplug->deferred_nr = mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; for (i = 0; i < mlxplat_mux_num; i++) { @@ -4950,13 +5730,77 @@ static int __init mlxplat_dmi_nvlink_blade_matched(const struct dmi_system_id *d mlxplat_mux_data[i].n_values = ARRAY_SIZE(mlxplat_msn21xx_channels); } - mlxplat_regs_io = &mlxplat_nvlink_blade_regs_io_data; + mlxplat_regs_io = &mlxplat_chassis_blade_regs_io_data; + mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data; + mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng400; + + return 1; +} + +static int __init mlxplat_dmi_rack_switch_matched(const struct dmi_system_id *dmi) +{ + int i; + + mlxplat_max_adap_num = MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM; + mlxplat_mux_num = ARRAY_SIZE(mlxplat_rack_switch_mux_data); + mlxplat_mux_data = mlxplat_rack_switch_mux_data; + mlxplat_hotplug = &mlxplat_mlxcpld_rack_switch_data; + mlxplat_hotplug->deferred_nr = + mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; + mlxplat_led = &mlxplat_default_ng_led_data; + mlxplat_regs_io = &mlxplat_default_ng_regs_io_data; + mlxplat_fan = &mlxplat_default_fan_data; + for (i = 0; i < ARRAY_SIZE(mlxplat_mlxcpld_wd_set_type2); i++) + mlxplat_wd_data[i] = &mlxplat_mlxcpld_wd_set_type2[i]; + mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data; + mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_rack_switch; + + return 1; +} + +static int __init mlxplat_dmi_ng800_matched(const struct dmi_system_id *dmi) +{ + int i; + + mlxplat_max_adap_num = MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM; + mlxplat_mux_num = ARRAY_SIZE(mlxplat_ng800_mux_data); + mlxplat_mux_data = mlxplat_ng800_mux_data; + mlxplat_hotplug = &mlxplat_mlxcpld_ng800_data; + mlxplat_hotplug->deferred_nr = + mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; + mlxplat_led = &mlxplat_default_ng_led_data; + mlxplat_regs_io = &mlxplat_default_ng_regs_io_data; + mlxplat_fan = &mlxplat_default_fan_data; + for (i = 0; i < ARRAY_SIZE(mlxplat_mlxcpld_wd_set_type2); i++) + mlxplat_wd_data[i] = &mlxplat_mlxcpld_wd_set_type2[i]; mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data; mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng400; return 1; } +static int __init mlxplat_dmi_l1_switch_matched(const struct dmi_system_id *dmi) +{ + int i; + + mlxplat_max_adap_num = MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM; + mlxplat_mux_num = ARRAY_SIZE(mlxplat_rack_switch_mux_data); + mlxplat_mux_data = mlxplat_rack_switch_mux_data; + mlxplat_hotplug = &mlxplat_mlxcpld_l1_switch_data; + mlxplat_hotplug->deferred_nr = + mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; + mlxplat_led = &mlxplat_l1_switch_led_data; + mlxplat_regs_io = &mlxplat_default_ng_regs_io_data; + mlxplat_fan = &mlxplat_default_fan_data; + for (i = 0; i < ARRAY_SIZE(mlxplat_mlxcpld_wd_set_type2); i++) + mlxplat_wd_data[i] = &mlxplat_mlxcpld_wd_set_type2[i]; + mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data; + mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_rack_switch; + pm_power_off = mlxplat_poweroff; + + return 1; +} + static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { { .callback = mlxplat_dmi_default_wc_matched, @@ -5015,6 +5859,13 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { }, }, { + .callback = mlxplat_dmi_rack_switch_matched, + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "VMOD0010"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI142"), + }, + }, + { .callback = mlxplat_dmi_ng400_matched, .matches = { DMI_MATCH(DMI_BOARD_NAME, "VMOD0010"), @@ -5027,12 +5878,24 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { }, }, { - .callback = mlxplat_dmi_nvlink_blade_matched, + .callback = mlxplat_dmi_ng800_matched, + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "VMOD0013"), + }, + }, + { + .callback = mlxplat_dmi_chassis_blade_matched, .matches = { DMI_MATCH(DMI_BOARD_NAME, "VMOD0015"), }, }, { + .callback = mlxplat_dmi_l1_switch_matched, + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "VMOD0017"), + }, + }, + { .callback = mlxplat_dmi_msn274x_matched, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), @@ -5145,10 +6008,11 @@ static int mlxplat_mlxcpld_verify_bus_topology(int *nr) shift = *nr - mlxplat_mux_data[i].parent; mlxplat_mux_data[i].parent = *nr; mlxplat_mux_data[i].base_nr += shift; - if (shift > 0) - mlxplat_hotplug->shift_nr = shift; } + if (shift > 0) + mlxplat_hotplug->shift_nr = shift; + return 0; } @@ -5173,73 +6037,53 @@ static int mlxplat_mlxcpld_check_wd_capability(void *regmap) return 0; } -static int __init mlxplat_init(void) +static int mlxplat_lpc_cpld_device_init(struct resource **hotplug_resources, + unsigned int *hotplug_resources_size) { - struct mlxplat_priv *priv; - int i, j, nr, err; - - if (!dmi_check_system(mlxplat_dmi_table)) - return -ENODEV; + int err; mlxplat_dev = platform_device_register_simple(MLX_PLAT_DEVICE_NAME, PLATFORM_DEVID_NONE, - mlxplat_lpc_resources, - ARRAY_SIZE(mlxplat_lpc_resources)); - + mlxplat_lpc_resources, + ARRAY_SIZE(mlxplat_lpc_resources)); if (IS_ERR(mlxplat_dev)) return PTR_ERR(mlxplat_dev); - priv = devm_kzalloc(&mlxplat_dev->dev, sizeof(struct mlxplat_priv), - GFP_KERNEL); - if (!priv) { - err = -ENOMEM; - goto fail_alloc; - } - platform_set_drvdata(mlxplat_dev, priv); - mlxplat_mlxcpld_regmap_ctx.base = devm_ioport_map(&mlxplat_dev->dev, - mlxplat_lpc_resources[1].start, 1); + mlxplat_lpc_resources[1].start, 1); if (!mlxplat_mlxcpld_regmap_ctx.base) { err = -ENOMEM; - goto fail_alloc; + goto fail_devm_ioport_map; } - if (!mlxplat_regmap_config) - mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config; + *hotplug_resources = mlxplat_mlxcpld_resources; + *hotplug_resources_size = ARRAY_SIZE(mlxplat_mlxcpld_resources); - priv->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL, - &mlxplat_mlxcpld_regmap_ctx, - mlxplat_regmap_config); - if (IS_ERR(priv->regmap)) { - err = PTR_ERR(priv->regmap); - goto fail_alloc; - } + return 0; - err = mlxplat_mlxcpld_verify_bus_topology(&nr); - if (nr < 0) - goto fail_alloc; +fail_devm_ioport_map: + platform_device_unregister(mlxplat_dev); + return err; +} - nr = (nr == mlxplat_max_adap_num) ? -1 : nr; - if (mlxplat_i2c) - mlxplat_i2c->regmap = priv->regmap; - priv->pdev_i2c = platform_device_register_resndata(&mlxplat_dev->dev, "i2c_mlxcpld", - nr, mlxplat_mlxcpld_resources, - ARRAY_SIZE(mlxplat_mlxcpld_resources), - mlxplat_i2c, sizeof(*mlxplat_i2c)); - if (IS_ERR(priv->pdev_i2c)) { - err = PTR_ERR(priv->pdev_i2c); - goto fail_alloc; - } +static void mlxplat_lpc_cpld_device_exit(void) +{ + platform_device_unregister(mlxplat_dev); +} - for (i = 0; i < mlxplat_mux_num; i++) { - priv->pdev_mux[i] = platform_device_register_resndata(&priv->pdev_i2c->dev, - "i2c-mux-reg", i, NULL, 0, - &mlxplat_mux_data[i], - sizeof(mlxplat_mux_data[i])); - if (IS_ERR(priv->pdev_mux[i])) { - err = PTR_ERR(priv->pdev_mux[i]); - goto fail_platform_mux_register; - } - } +static int +mlxplat_pre_init(struct resource **hotplug_resources, unsigned int *hotplug_resources_size) +{ + return mlxplat_lpc_cpld_device_init(hotplug_resources, hotplug_resources_size); +} + +static void mlxplat_post_exit(void) +{ + mlxplat_lpc_cpld_device_exit(); +} + +static int mlxplat_post_init(struct mlxplat_priv *priv) +{ + int i = 0, err; /* Add hotplug driver */ if (mlxplat_hotplug) { @@ -5247,24 +6091,15 @@ static int __init mlxplat_init(void) priv->pdev_hotplug = platform_device_register_resndata(&mlxplat_dev->dev, "mlxreg-hotplug", PLATFORM_DEVID_NONE, - mlxplat_mlxcpld_resources, - ARRAY_SIZE(mlxplat_mlxcpld_resources), + priv->hotplug_resources, + priv->hotplug_resources_size, mlxplat_hotplug, sizeof(*mlxplat_hotplug)); if (IS_ERR(priv->pdev_hotplug)) { err = PTR_ERR(priv->pdev_hotplug); - goto fail_platform_mux_register; + goto fail_platform_hotplug_register; } } - /* Set default registers. */ - for (j = 0; j < mlxplat_regmap_config->num_reg_defaults; j++) { - err = regmap_write(priv->regmap, - mlxplat_regmap_config->reg_defaults[j].reg, - mlxplat_regmap_config->reg_defaults[j].def); - if (err) - goto fail_platform_mux_register; - } - /* Add LED driver. */ if (mlxplat_led) { mlxplat_led->regmap = priv->regmap; @@ -5274,7 +6109,7 @@ static int __init mlxplat_init(void) sizeof(*mlxplat_led)); if (IS_ERR(priv->pdev_led)) { err = PTR_ERR(priv->pdev_led); - goto fail_platform_hotplug_register; + goto fail_platform_leds_register; } } @@ -5288,7 +6123,7 @@ static int __init mlxplat_init(void) sizeof(*mlxplat_regs_io)); if (IS_ERR(priv->pdev_io_regs)) { err = PTR_ERR(priv->pdev_io_regs); - goto fail_platform_led_register; + goto fail_platform_io_register; } } @@ -5301,7 +6136,7 @@ static int __init mlxplat_init(void) sizeof(*mlxplat_fan)); if (IS_ERR(priv->pdev_fan)) { err = PTR_ERR(priv->pdev_fan); - goto fail_platform_io_regs_register; + goto fail_platform_fan_register; } } @@ -5309,56 +6144,40 @@ static int __init mlxplat_init(void) err = mlxplat_mlxcpld_check_wd_capability(priv->regmap); if (err) goto fail_platform_wd_register; - for (j = 0; j < MLXPLAT_CPLD_WD_MAX_DEVS; j++) { - if (mlxplat_wd_data[j]) { - mlxplat_wd_data[j]->regmap = priv->regmap; - priv->pdev_wd[j] = - platform_device_register_resndata(&mlxplat_dev->dev, "mlx-wdt", j, - NULL, 0, mlxplat_wd_data[j], - sizeof(*mlxplat_wd_data[j])); - if (IS_ERR(priv->pdev_wd[j])) { - err = PTR_ERR(priv->pdev_wd[j]); + for (i = 0; i < MLXPLAT_CPLD_WD_MAX_DEVS; i++) { + if (mlxplat_wd_data[i]) { + mlxplat_wd_data[i]->regmap = priv->regmap; + priv->pdev_wd[i] = + platform_device_register_resndata(&mlxplat_dev->dev, "mlx-wdt", i, + NULL, 0, mlxplat_wd_data[i], + sizeof(*mlxplat_wd_data[i])); + if (IS_ERR(priv->pdev_wd[i])) { + err = PTR_ERR(priv->pdev_wd[i]); goto fail_platform_wd_register; } } } - /* Sync registers with hardware. */ - regcache_mark_dirty(priv->regmap); - err = regcache_sync(priv->regmap); - if (err) - goto fail_platform_wd_register; - return 0; fail_platform_wd_register: - while (--j >= 0) - platform_device_unregister(priv->pdev_wd[j]); - if (mlxplat_fan) - platform_device_unregister(priv->pdev_fan); -fail_platform_io_regs_register: + while (--i >= 0) + platform_device_unregister(priv->pdev_wd[i]); +fail_platform_fan_register: if (mlxplat_regs_io) platform_device_unregister(priv->pdev_io_regs); -fail_platform_led_register: +fail_platform_io_register: if (mlxplat_led) platform_device_unregister(priv->pdev_led); -fail_platform_hotplug_register: +fail_platform_leds_register: if (mlxplat_hotplug) platform_device_unregister(priv->pdev_hotplug); -fail_platform_mux_register: - while (--i >= 0) - platform_device_unregister(priv->pdev_mux[i]); - platform_device_unregister(priv->pdev_i2c); -fail_alloc: - platform_device_unregister(mlxplat_dev); - +fail_platform_hotplug_register: return err; } -module_init(mlxplat_init); -static void __exit mlxplat_exit(void) +static void mlxplat_pre_exit(struct mlxplat_priv *priv) { - struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev); int i; for (i = MLXPLAT_CPLD_WD_MAX_DEVS - 1; i >= 0 ; i--) @@ -5371,12 +6190,185 @@ static void __exit mlxplat_exit(void) platform_device_unregister(priv->pdev_led); if (priv->pdev_hotplug) platform_device_unregister(priv->pdev_hotplug); +} + +static int +mlxplat_i2c_mux_complition_notify(void *handle, struct i2c_adapter *parent, + struct i2c_adapter *adapters[]) +{ + struct mlxplat_priv *priv = handle; + + return mlxplat_post_init(priv); +} + +static int mlxplat_i2c_mux_topolgy_init(struct mlxplat_priv *priv) +{ + int i, err; + + if (!priv->pdev_i2c) { + priv->i2c_main_init_status = MLXPLAT_I2C_MAIN_BUS_NOTIFIED; + return 0; + } + + priv->i2c_main_init_status = MLXPLAT_I2C_MAIN_BUS_HANDLE_CREATED; + for (i = 0; i < mlxplat_mux_num; i++) { + priv->pdev_mux[i] = platform_device_register_resndata(&priv->pdev_i2c->dev, + "i2c-mux-reg", i, NULL, 0, + &mlxplat_mux_data[i], + sizeof(mlxplat_mux_data[i])); + if (IS_ERR(priv->pdev_mux[i])) { + err = PTR_ERR(priv->pdev_mux[i]); + goto fail_platform_mux_register; + } + } - for (i = mlxplat_mux_num - 1; i >= 0 ; i--) + return mlxplat_i2c_mux_complition_notify(priv, NULL, NULL); + +fail_platform_mux_register: + while (--i >= 0) platform_device_unregister(priv->pdev_mux[i]); + return err; +} - platform_device_unregister(priv->pdev_i2c); - platform_device_unregister(mlxplat_dev); +static void mlxplat_i2c_mux_topolgy_exit(struct mlxplat_priv *priv) +{ + int i; + + for (i = mlxplat_mux_num - 1; i >= 0 ; i--) { + if (priv->pdev_mux[i]) + platform_device_unregister(priv->pdev_mux[i]); + } + + mlxplat_post_exit(); +} + +static int mlxplat_i2c_main_complition_notify(void *handle, int id) +{ + struct mlxplat_priv *priv = handle; + + return mlxplat_i2c_mux_topolgy_init(priv); +} + +static int mlxplat_i2c_main_init(struct mlxplat_priv *priv) +{ + int nr, err; + + if (!mlxplat_i2c) + return 0; + + err = mlxplat_mlxcpld_verify_bus_topology(&nr); + if (nr < 0) + goto fail_mlxplat_mlxcpld_verify_bus_topology; + + nr = (nr == mlxplat_max_adap_num) ? -1 : nr; + mlxplat_i2c->regmap = priv->regmap; + mlxplat_i2c->handle = priv; + + priv->pdev_i2c = platform_device_register_resndata(&mlxplat_dev->dev, "i2c_mlxcpld", + nr, priv->hotplug_resources, + priv->hotplug_resources_size, + mlxplat_i2c, sizeof(*mlxplat_i2c)); + if (IS_ERR(priv->pdev_i2c)) { + err = PTR_ERR(priv->pdev_i2c); + goto fail_platform_i2c_register; + } + + if (priv->i2c_main_init_status == MLXPLAT_I2C_MAIN_BUS_NOTIFIED) { + err = mlxplat_i2c_mux_topolgy_init(priv); + if (err) + goto fail_mlxplat_i2c_mux_topolgy_init; + } + + return 0; + +fail_mlxplat_i2c_mux_topolgy_init: +fail_platform_i2c_register: +fail_mlxplat_mlxcpld_verify_bus_topology: + return err; +} + +static void mlxplat_i2c_main_exit(struct mlxplat_priv *priv) +{ + mlxplat_i2c_mux_topolgy_exit(priv); + if (priv->pdev_i2c) + platform_device_unregister(priv->pdev_i2c); +} + +static int __init mlxplat_init(void) +{ + unsigned int hotplug_resources_size; + struct resource *hotplug_resources; + struct mlxplat_priv *priv; + int i, err; + + if (!dmi_check_system(mlxplat_dmi_table)) + return -ENODEV; + + err = mlxplat_pre_init(&hotplug_resources, &hotplug_resources_size); + if (err) + return err; + + priv = devm_kzalloc(&mlxplat_dev->dev, sizeof(struct mlxplat_priv), + GFP_KERNEL); + if (!priv) { + err = -ENOMEM; + goto fail_alloc; + } + platform_set_drvdata(mlxplat_dev, priv); + priv->hotplug_resources = hotplug_resources; + priv->hotplug_resources_size = hotplug_resources_size; + + if (!mlxplat_regmap_config) + mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config; + + priv->regmap = devm_regmap_init(&mlxplat_dev->dev, NULL, + &mlxplat_mlxcpld_regmap_ctx, + mlxplat_regmap_config); + if (IS_ERR(priv->regmap)) { + err = PTR_ERR(priv->regmap); + goto fail_alloc; + } + + /* Set default registers. */ + for (i = 0; i < mlxplat_regmap_config->num_reg_defaults; i++) { + err = regmap_write(priv->regmap, + mlxplat_regmap_config->reg_defaults[i].reg, + mlxplat_regmap_config->reg_defaults[i].def); + if (err) + goto fail_regmap_write; + } + + err = mlxplat_i2c_main_init(priv); + if (err) + goto fail_mlxplat_i2c_main_init; + + /* Sync registers with hardware. */ + regcache_mark_dirty(priv->regmap); + err = regcache_sync(priv->regmap); + if (err) + goto fail_regcache_sync; + + return 0; + +fail_regcache_sync: + mlxplat_pre_exit(priv); +fail_mlxplat_i2c_main_init: +fail_regmap_write: +fail_alloc: + mlxplat_post_exit(); + + return err; +} +module_init(mlxplat_init); + +static void __exit mlxplat_exit(void) +{ + struct mlxplat_priv *priv = platform_get_drvdata(mlxplat_dev); + + if (pm_power_off) + pm_power_off = NULL; + mlxplat_pre_exit(priv); + mlxplat_i2c_main_exit(priv); } module_exit(mlxplat_exit); diff --git a/drivers/platform/x86/nvidia-wmi-ec-backlight.c b/drivers/platform/x86/nvidia-wmi-ec-backlight.c index baccdf658538..1b572c90c76e 100644 --- a/drivers/platform/x86/nvidia-wmi-ec-backlight.c +++ b/drivers/platform/x86/nvidia-wmi-ec-backlight.c @@ -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; /* diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index a01a92769c1a..86b33b74519b 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -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, }; diff --git a/drivers/platform/x86/uv_sysfs.c b/drivers/platform/x86/uv_sysfs.c index 73fc38ee7430..38d1b692d3c0 100644 --- a/drivers/platform/x86/uv_sysfs.c +++ b/drivers/platform/x86/uv_sysfs.c @@ -203,7 +203,7 @@ static const struct sysfs_ops hub_sysfs_ops = { .show = hub_type_show, }; -static struct kobj_type hub_attr_type = { +static const struct kobj_type hub_attr_type = { .release = hub_release, .sysfs_ops = &hub_sysfs_ops, .default_groups = uv_hub_groups, @@ -356,7 +356,7 @@ static const struct sysfs_ops uv_port_sysfs_ops = { .show = uv_port_type_show, }; -static struct kobj_type uv_port_attr_type = { +static const struct kobj_type uv_port_attr_type = { .release = uv_port_release, .sysfs_ops = &uv_port_sysfs_ops, .default_groups = uv_port_groups, @@ -528,7 +528,7 @@ static const struct sysfs_ops uv_pci_top_sysfs_ops = { .show = pci_top_type_show, }; -static struct kobj_type uv_pci_top_attr_type = { +static const struct kobj_type uv_pci_top_attr_type = { .release = uv_pci_top_release, .sysfs_ops = &uv_pci_top_sysfs_ops, }; diff --git a/drivers/power/supply/surface_battery.c b/drivers/power/supply/surface_battery.c index 540707882bb0..19d2f8834e56 100644 --- a/drivers/power/supply/surface_battery.c +++ b/drivers/power/supply/surface_battery.c @@ -852,8 +852,8 @@ static const struct spwr_psy_properties spwr_psy_props_bat2_sb3 = { }; static const struct ssam_device_id surface_battery_match[] = { - { SSAM_SDEV(BAT, 0x01, 0x01, 0x00), (unsigned long)&spwr_psy_props_bat1 }, - { SSAM_SDEV(BAT, 0x02, 0x01, 0x00), (unsigned long)&spwr_psy_props_bat2_sb3 }, + { SSAM_SDEV(BAT, SAM, 0x01, 0x00), (unsigned long)&spwr_psy_props_bat1 }, + { SSAM_SDEV(BAT, KIP, 0x01, 0x00), (unsigned long)&spwr_psy_props_bat2_sb3 }, { }, }; MODULE_DEVICE_TABLE(ssam, surface_battery_match); diff --git a/drivers/power/supply/surface_charger.c b/drivers/power/supply/surface_charger.c index 59182d55742d..cabdd8da12d0 100644 --- a/drivers/power/supply/surface_charger.c +++ b/drivers/power/supply/surface_charger.c @@ -260,7 +260,7 @@ static const struct spwr_psy_properties spwr_psy_props_adp1 = { }; static const struct ssam_device_id surface_ac_match[] = { - { SSAM_SDEV(BAT, 0x01, 0x01, 0x01), (unsigned long)&spwr_psy_props_adp1 }, + { SSAM_SDEV(BAT, SAM, 0x01, 0x01), (unsigned long)&spwr_psy_props_adp1 }, { }, }; MODULE_DEVICE_TABLE(ssam, surface_ac_match); |