diff options
author | Thomas Zimmermann | 2021-11-18 09:36:39 +0100 |
---|---|---|
committer | Thomas Zimmermann | 2021-11-18 09:36:39 +0100 |
commit | a713ca234ea9d946235ac7248995c5fddfd9e523 (patch) | |
tree | 708f72ee1c76360aa80c926f1defc8301aef1a23 /drivers/platform | |
parent | 37fe0cf5fb803d98efd7feb64b408c9b029c1085 (diff) | |
parent | fa55b7dcdc43c1aa1ba12bca9d2dd4318c2a0dbf (diff) |
Merge drm/drm-next into drm-misc-next
Backmerging from drm/drm-next for v5.16-rc1.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Diffstat (limited to 'drivers/platform')
45 files changed, 5931 insertions, 674 deletions
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index 1f7861944044..d6306d2a096f 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -156,7 +156,7 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum); if (ec_response_timed_out()) { - dev_warn(ec->dev, "EC responsed timed out\n"); + dev_warn(ec->dev, "EC response timed out\n"); ret = -EIO; goto done; } @@ -238,7 +238,7 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, cros_ec_lpc_ops.write(EC_LPC_ADDR_HOST_CMD, 1, &sum); if (ec_response_timed_out()) { - dev_warn(ec->dev, "EC responsed timed out\n"); + dev_warn(ec->dev, "EC response timed out\n"); ret = -EIO; goto done; } diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index a7404d69b2d3..c4caf2e2de82 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -808,38 +808,27 @@ EXPORT_SYMBOL(cros_ec_get_host_event); * * Call this function to test whether the ChromeOS EC supports a feature. * - * Return: 1 if supported, 0 if not + * Return: true if supported, false if not (or if an error was encountered). */ -int cros_ec_check_features(struct cros_ec_dev *ec, int feature) +bool cros_ec_check_features(struct cros_ec_dev *ec, int feature) { - struct cros_ec_command *msg; + struct ec_response_get_features *features = &ec->features; int ret; - if (ec->features[0] == -1U && ec->features[1] == -1U) { + if (features->flags[0] == -1U && features->flags[1] == -1U) { /* features bitmap not read yet */ - msg = kzalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL); - if (!msg) - return -ENOMEM; - - msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset; - msg->insize = sizeof(ec->features); - - ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + ret = cros_ec_command(ec->ec_dev, 0, EC_CMD_GET_FEATURES + ec->cmd_offset, + NULL, 0, features, sizeof(*features)); if (ret < 0) { - dev_warn(ec->dev, "cannot get EC features: %d/%d\n", - ret, msg->result); - memset(ec->features, 0, sizeof(ec->features)); - } else { - memcpy(ec->features, msg->data, sizeof(ec->features)); + dev_warn(ec->dev, "cannot get EC features: %d\n", ret); + memset(features, 0, sizeof(*features)); } dev_dbg(ec->dev, "EC features %08x %08x\n", - ec->features[0], ec->features[1]); - - kfree(msg); + features->flags[0], features->flags[1]); } - return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature); + return !!(features->flags[feature / 32] & EC_FEATURE_MASK_0(feature)); } EXPORT_SYMBOL_GPL(cros_ec_check_features); @@ -908,3 +897,51 @@ int cros_ec_get_sensor_count(struct cros_ec_dev *ec) return sensor_count; } EXPORT_SYMBOL_GPL(cros_ec_get_sensor_count); + +/** + * cros_ec_command - Send a command to the EC. + * + * @ec_dev: EC device + * @version: EC command version + * @command: EC command + * @outdata: EC command output data + * @outsize: Size of outdata + * @indata: EC command input data + * @insize: Size of indata + * + * Return: >= 0 on success, negative error number on failure. + */ +int cros_ec_command(struct cros_ec_device *ec_dev, + unsigned int version, + int command, + void *outdata, + int outsize, + void *indata, + int insize) +{ + struct cros_ec_command *msg; + int ret; + + msg = kzalloc(sizeof(*msg) + max(insize, outsize), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->version = version; + msg->command = command; + msg->outsize = outsize; + msg->insize = insize; + + if (outsize) + memcpy(msg->data, outdata, outsize); + + ret = cros_ec_cmd_xfer_status(ec_dev, msg); + if (ret < 0) + goto error; + + if (insize) + memcpy(indata, msg->data, insize); +error: + kfree(msg); + return ret; +} +EXPORT_SYMBOL_GPL(cros_ec_command); diff --git a/drivers/platform/chrome/cros_ec_sensorhub.c b/drivers/platform/chrome/cros_ec_sensorhub.c index 9c4af76a9956..31fb8bdaad5a 100644 --- a/drivers/platform/chrome/cros_ec_sensorhub.c +++ b/drivers/platform/chrome/cros_ec_sensorhub.c @@ -224,8 +224,7 @@ static int cros_ec_sensorhub_probe(struct platform_device *pdev) */ static int cros_ec_sensorhub_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct cros_ec_sensorhub *sensorhub = platform_get_drvdata(pdev); + struct cros_ec_sensorhub *sensorhub = dev_get_drvdata(dev); struct cros_ec_dev *ec = sensorhub->ec; if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) @@ -235,8 +234,7 @@ static int cros_ec_sensorhub_suspend(struct device *dev) static int cros_ec_sensorhub_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct cros_ec_sensorhub *sensorhub = platform_get_drvdata(pdev); + struct cros_ec_sensorhub *sensorhub = dev_get_drvdata(dev); struct cros_ec_dev *ec = sensorhub->ec; if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 262a891eded3..5de0bfb0bc4d 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -379,37 +379,6 @@ unregister_ports: return ret; } -static int cros_typec_ec_command(struct cros_typec_data *typec, - unsigned int version, - unsigned int command, - void *outdata, - unsigned int outsize, - void *indata, - unsigned int insize) -{ - struct cros_ec_command *msg; - int ret; - - msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL); - if (!msg) - return -ENOMEM; - - msg->version = version; - msg->command = command; - msg->outsize = outsize; - msg->insize = insize; - - if (outsize) - memcpy(msg->data, outdata, outsize); - - ret = cros_ec_cmd_xfer_status(typec->ec, msg); - if (ret >= 0 && insize) - memcpy(indata, msg->data, insize); - - kfree(msg); - return ret; -} - static int cros_typec_usb_safe_state(struct cros_typec_port *port) { port->state.mode = TYPEC_STATE_SAFE; @@ -596,8 +565,8 @@ mux_ack: /* Sending Acknowledgment to EC */ mux_ack.port = port_num; - if (cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_MUX_ACK, &mux_ack, - sizeof(mux_ack), NULL, 0) < 0) + if (cros_ec_command(typec->ec, 0, EC_CMD_USB_PD_MUX_ACK, &mux_ack, + sizeof(mux_ack), NULL, 0) < 0) dev_warn(typec->dev, "Failed to send Mux ACK to EC for port: %d\n", port_num); @@ -668,8 +637,8 @@ static int cros_typec_get_mux_info(struct cros_typec_data *typec, int port_num, .port = port_num, }; - return cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_MUX_INFO, &req, - sizeof(req), resp, sizeof(*resp)); + return cros_ec_command(typec->ec, 0, EC_CMD_USB_PD_MUX_INFO, &req, + sizeof(req), resp, sizeof(*resp)); } /* @@ -776,8 +745,8 @@ static int cros_typec_handle_sop_prime_disc(struct cros_typec_data *typec, int p int ret = 0; memset(disc, 0, EC_PROTO2_MAX_RESPONSE_SIZE); - ret = cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req), - disc, EC_PROTO2_MAX_RESPONSE_SIZE); + ret = cros_ec_command(typec->ec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req), + disc, EC_PROTO2_MAX_RESPONSE_SIZE); if (ret < 0) { dev_err(typec->dev, "Failed to get SOP' discovery data for port: %d\n", port_num); goto sop_prime_disc_exit; @@ -859,8 +828,8 @@ static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_nu typec_partner_set_pd_revision(port->partner, pd_revision); memset(sop_disc, 0, EC_PROTO2_MAX_RESPONSE_SIZE); - ret = cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req), - sop_disc, EC_PROTO2_MAX_RESPONSE_SIZE); + ret = cros_ec_command(typec->ec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req), + sop_disc, EC_PROTO2_MAX_RESPONSE_SIZE); if (ret < 0) { dev_err(typec->dev, "Failed to get SOP discovery data for port: %d\n", port_num); goto disc_exit; @@ -892,8 +861,8 @@ static int cros_typec_send_clear_event(struct cros_typec_data *typec, int port_n .clear_events_mask = events_mask, }; - return cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_CONTROL, &req, - sizeof(req), NULL, 0); + return cros_ec_command(typec->ec, 0, EC_CMD_TYPEC_CONTROL, &req, + sizeof(req), NULL, 0); } static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num) @@ -904,8 +873,8 @@ static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num }; int ret; - ret = cros_typec_ec_command(typec, 0, EC_CMD_TYPEC_STATUS, &req, sizeof(req), - &resp, sizeof(resp)); + ret = cros_ec_command(typec->ec, 0, EC_CMD_TYPEC_STATUS, &req, sizeof(req), + &resp, sizeof(resp)); if (ret < 0) { dev_warn(typec->dev, "EC_CMD_TYPEC_STATUS failed for port: %d\n", port_num); return; @@ -983,9 +952,9 @@ static int cros_typec_port_update(struct cros_typec_data *typec, int port_num) req.mux = USB_PD_CTRL_MUX_NO_CHANGE; req.swap = USB_PD_CTRL_SWAP_NONE; - ret = cros_typec_ec_command(typec, typec->pd_ctrl_ver, - EC_CMD_USB_PD_CONTROL, &req, sizeof(req), - &resp, sizeof(resp)); + ret = cros_ec_command(typec->ec, typec->pd_ctrl_ver, + EC_CMD_USB_PD_CONTROL, &req, sizeof(req), + &resp, sizeof(resp)); if (ret < 0) return ret; @@ -1035,8 +1004,8 @@ static int cros_typec_get_cmd_version(struct cros_typec_data *typec) /* We're interested in the PD control command version. */ req_v1.cmd = EC_CMD_USB_PD_CONTROL; - ret = cros_typec_ec_command(typec, 1, EC_CMD_GET_CMD_VERSIONS, - &req_v1, sizeof(req_v1), &resp, + ret = cros_ec_command(typec->ec, 1, EC_CMD_GET_CMD_VERSIONS, + &req_v1, sizeof(req_v1), &resp, sizeof(resp)); if (ret < 0) return ret; @@ -1116,12 +1085,11 @@ static int cros_typec_probe(struct platform_device *pdev) } ec_dev = dev_get_drvdata(&typec->ec->ec->dev); - typec->typec_cmd_supported = !!cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_CMD); - typec->needs_mux_ack = !!cros_ec_check_features(ec_dev, - EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK); + typec->typec_cmd_supported = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_CMD); + typec->needs_mux_ack = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK); - ret = cros_typec_ec_command(typec, 0, EC_CMD_USB_PD_PORTS, NULL, 0, - &resp, sizeof(resp)); + ret = cros_ec_command(typec->ec, 0, EC_CMD_USB_PD_PORTS, NULL, 0, + &resp, sizeof(resp)); if (ret < 0) return ret; diff --git a/drivers/platform/chrome/cros_usbpd_notify.c b/drivers/platform/chrome/cros_usbpd_notify.c index 48a6617aa12f..91ce6be91aac 100644 --- a/drivers/platform/chrome/cros_usbpd_notify.c +++ b/drivers/platform/chrome/cros_usbpd_notify.c @@ -53,50 +53,6 @@ void cros_usbpd_unregister_notify(struct notifier_block *nb) } EXPORT_SYMBOL_GPL(cros_usbpd_unregister_notify); -/** - * cros_ec_pd_command - Send a command to the EC. - * - * @ec_dev: EC device - * @command: EC command - * @outdata: EC command output data - * @outsize: Size of outdata - * @indata: EC command input data - * @insize: Size of indata - * - * Return: >= 0 on success, negative error number on failure. - */ -static int cros_ec_pd_command(struct cros_ec_device *ec_dev, - int command, - uint8_t *outdata, - int outsize, - uint8_t *indata, - int insize) -{ - struct cros_ec_command *msg; - int ret; - - msg = kzalloc(sizeof(*msg) + max(insize, outsize), GFP_KERNEL); - if (!msg) - return -ENOMEM; - - msg->command = command; - msg->outsize = outsize; - msg->insize = insize; - - if (outsize) - memcpy(msg->data, outdata, outsize); - - ret = cros_ec_cmd_xfer_status(ec_dev, msg); - if (ret < 0) - goto error; - - if (insize) - memcpy(indata, msg->data, insize); -error: - kfree(msg); - return ret; -} - static void cros_usbpd_get_event_and_notify(struct device *dev, struct cros_ec_device *ec_dev) { @@ -115,10 +71,8 @@ static void cros_usbpd_get_event_and_notify(struct device *dev, } /* Check for PD host events on EC. */ - ret = cros_ec_pd_command(ec_dev, EC_CMD_PD_HOST_EVENT_STATUS, - NULL, 0, - (uint8_t *)&host_event_status, - sizeof(host_event_status)); + ret = cros_ec_command(ec_dev, 0, EC_CMD_PD_HOST_EVENT_STATUS, + NULL, 0, &host_event_status, sizeof(host_event_status)); if (ret < 0) { dev_warn(dev, "Can't get host event status (err: %d)\n", ret); goto send_notify; diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig index edd17e1a1f88..d4c5c170bca0 100644 --- a/drivers/platform/mellanox/Kconfig +++ b/drivers/platform/mellanox/Kconfig @@ -34,6 +34,18 @@ config MLXREG_IO to system resets operation, system reset causes monitoring and some kinds of mux selection. +config MLXREG_LC + tristate "Mellanox line card platform driver support" + depends on REGMAP + depends on HWMON + depends on I2C + help + This driver provides support for the Mellanox MSN4800-XX line cards, + which are the part of MSN4800 Ethernet modular switch systems + providing a high performance switching solution for Enterprise Data + Centers (EDC) for building Ethernet based clusters, High-Performance + Computing (HPC) and embedded environments. + config MLXBF_TMFIFO tristate "Mellanox BlueField SoC TmFifo platform driver" depends on ARM64 diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile index 000ddaa74c98..a4868366ff18 100644 --- a/drivers/platform/mellanox/Makefile +++ b/drivers/platform/mellanox/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_MLXBF_PMC) += mlxbf-pmc.o obj-$(CONFIG_MLXBF_TMFIFO) += mlxbf-tmfifo.o obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o +obj-$(CONFIG_MLXREG_LC) += mlxreg-lc.o diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c index b013445147dd..117bc3f395fd 100644 --- a/drivers/platform/mellanox/mlxreg-hotplug.c +++ b/drivers/platform/mellanox/mlxreg-hotplug.c @@ -28,7 +28,7 @@ /* ASIC good health mask. */ #define MLXREG_HOTPLUG_GOOD_HEALTH_MASK 0x02 -#define MLXREG_HOTPLUG_ATTRS_MAX 24 +#define MLXREG_HOTPLUG_ATTRS_MAX 128 #define MLXREG_HOTPLUG_NOT_ASSERT 3 /** @@ -89,9 +89,20 @@ mlxreg_hotplug_udev_event_send(struct kobject *kobj, return kobject_uevent_env(kobj, KOBJ_CHANGE, mlxreg_hotplug_udev_envp); } +static void +mlxreg_hotplug_pdata_export(void *pdata, void *regmap) +{ + struct mlxreg_core_hotplug_platform_data *dev_pdata = pdata; + + /* Export regmap to underlying device. */ + dev_pdata->regmap = regmap; +} + static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv, - struct mlxreg_core_data *data) + struct mlxreg_core_data *data, + enum mlxreg_hotplug_kind kind) { + struct i2c_board_info *brdinfo = data->hpdev.brdinfo; struct mlxreg_core_hotplug_platform_data *pdata; struct i2c_client *client; @@ -106,46 +117,88 @@ static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv, return 0; pdata = dev_get_platdata(&priv->pdev->dev); - data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr + - pdata->shift_nr); - if (!data->hpdev.adapter) { - dev_err(priv->dev, "Failed to get adapter for bus %d\n", - data->hpdev.nr + pdata->shift_nr); - return -EFAULT; - } + switch (data->hpdev.action) { + case MLXREG_HOTPLUG_DEVICE_DEFAULT_ACTION: + data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr + + pdata->shift_nr); + if (!data->hpdev.adapter) { + dev_err(priv->dev, "Failed to get adapter for bus %d\n", + data->hpdev.nr + pdata->shift_nr); + return -EFAULT; + } - client = i2c_new_client_device(data->hpdev.adapter, - data->hpdev.brdinfo); - if (IS_ERR(client)) { - dev_err(priv->dev, "Failed to create client %s at bus %d at addr 0x%02x\n", - data->hpdev.brdinfo->type, data->hpdev.nr + - pdata->shift_nr, data->hpdev.brdinfo->addr); + /* Export platform data to underlying device. */ + if (brdinfo->platform_data) + mlxreg_hotplug_pdata_export(brdinfo->platform_data, pdata->regmap); - i2c_put_adapter(data->hpdev.adapter); - data->hpdev.adapter = NULL; - return PTR_ERR(client); + client = i2c_new_client_device(data->hpdev.adapter, + brdinfo); + if (IS_ERR(client)) { + dev_err(priv->dev, "Failed to create client %s at bus %d at addr 0x%02x\n", + brdinfo->type, data->hpdev.nr + + pdata->shift_nr, brdinfo->addr); + + i2c_put_adapter(data->hpdev.adapter); + data->hpdev.adapter = NULL; + return PTR_ERR(client); + } + + data->hpdev.client = client; + break; + case MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION: + /* Export platform data to underlying device. */ + if (data->hpdev.brdinfo && data->hpdev.brdinfo->platform_data) + mlxreg_hotplug_pdata_export(data->hpdev.brdinfo->platform_data, + pdata->regmap); + /* Pass parent hotplug device handle to underlying device. */ + data->notifier = data->hpdev.notifier; + data->hpdev.pdev = platform_device_register_resndata(&priv->pdev->dev, + brdinfo->type, + data->hpdev.nr, + NULL, 0, data, + sizeof(*data)); + if (IS_ERR(data->hpdev.pdev)) + return PTR_ERR(data->hpdev.pdev); + + break; + default: + break; } - data->hpdev.client = client; + if (data->hpdev.notifier && data->hpdev.notifier->user_handler) + return data->hpdev.notifier->user_handler(data->hpdev.notifier->handle, kind, 1); return 0; } static void mlxreg_hotplug_device_destroy(struct mlxreg_hotplug_priv_data *priv, - struct mlxreg_core_data *data) + struct mlxreg_core_data *data, + enum mlxreg_hotplug_kind kind) { /* Notify user by sending hwmon uevent. */ mlxreg_hotplug_udev_event_send(&priv->hwmon->kobj, data, false); + if (data->hpdev.notifier && data->hpdev.notifier->user_handler) + data->hpdev.notifier->user_handler(data->hpdev.notifier->handle, kind, 0); + + switch (data->hpdev.action) { + case MLXREG_HOTPLUG_DEVICE_DEFAULT_ACTION: + if (data->hpdev.client) { + i2c_unregister_device(data->hpdev.client); + data->hpdev.client = NULL; + } - if (data->hpdev.client) { - i2c_unregister_device(data->hpdev.client); - data->hpdev.client = NULL; - } - - if (data->hpdev.adapter) { - i2c_put_adapter(data->hpdev.adapter); - data->hpdev.adapter = NULL; + if (data->hpdev.adapter) { + i2c_put_adapter(data->hpdev.adapter); + data->hpdev.adapter = NULL; + } + break; + case MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION: + if (data->hpdev.pdev) + platform_device_unregister(data->hpdev.pdev); + break; + default: + break; } } @@ -317,14 +370,14 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv, data = item->data + bit; if (regval & BIT(bit)) { if (item->inversed) - mlxreg_hotplug_device_destroy(priv, data); + mlxreg_hotplug_device_destroy(priv, data, item->kind); else - mlxreg_hotplug_device_create(priv, data); + mlxreg_hotplug_device_create(priv, data, item->kind); } else { if (item->inversed) - mlxreg_hotplug_device_create(priv, data); + mlxreg_hotplug_device_create(priv, data, item->kind); else - mlxreg_hotplug_device_destroy(priv, data); + mlxreg_hotplug_device_destroy(priv, data, item->kind); } } @@ -381,7 +434,7 @@ mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv, * ASIC is in steady state. Connect associated * device, if configured. */ - mlxreg_hotplug_device_create(priv, data); + mlxreg_hotplug_device_create(priv, data, item->kind); data->attached = true; } } else { @@ -391,7 +444,7 @@ mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv, * in steady state. Disconnect associated * device, if it has been connected. */ - mlxreg_hotplug_device_destroy(priv, data); + mlxreg_hotplug_device_destroy(priv, data, item->kind); data->attached = false; data->health_cntr = 0; } @@ -630,7 +683,7 @@ static void mlxreg_hotplug_unset_irq(struct mlxreg_hotplug_priv_data *priv) /* Remove all the attached devices in group. */ count = item->count; for (j = 0; j < count; j++, data++) - mlxreg_hotplug_device_destroy(priv, data); + mlxreg_hotplug_device_destroy(priv, data, item->kind); } } diff --git a/drivers/platform/mellanox/mlxreg-io.c b/drivers/platform/mellanox/mlxreg-io.c index 7646708d57e4..2c2686d5c2fc 100644 --- a/drivers/platform/mellanox/mlxreg-io.c +++ b/drivers/platform/mellanox/mlxreg-io.c @@ -18,7 +18,7 @@ /* Attribute parameters. */ #define MLXREG_IO_ATT_SIZE 10 -#define MLXREG_IO_ATT_NUM 48 +#define MLXREG_IO_ATT_NUM 96 /** * struct mlxreg_io_priv_data - driver's private data: @@ -98,7 +98,7 @@ mlxreg_io_get_reg(void *regmap, struct mlxreg_core_data *data, u32 in_val, if (ret) goto access_error; - *regval |= rol32(val, regsize * i); + *regval |= rol32(val, regsize * i * 8); } } @@ -141,7 +141,7 @@ mlxreg_io_attr_store(struct device *dev, struct device_attribute *attr, return -EINVAL; /* Convert buffer to input value. */ - ret = kstrtou32(buf, len, &input_val); + ret = kstrtou32(buf, 0, &input_val); if (ret) return ret; diff --git a/drivers/platform/mellanox/mlxreg-lc.c b/drivers/platform/mellanox/mlxreg-lc.c new file mode 100644 index 000000000000..0b7f58feb701 --- /dev/null +++ b/drivers/platform/mellanox/mlxreg-lc.c @@ -0,0 +1,906 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Nvidia line card driver + * + * Copyright (C) 2020 Nvidia Technologies Ltd. + */ + +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/platform_data/mlxcpld.h> +#include <linux/platform_data/mlxreg.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +/* I2C bus IO offsets */ +#define MLXREG_LC_REG_CPLD1_VER_OFFSET 0x2500 +#define MLXREG_LC_REG_FPGA1_VER_OFFSET 0x2501 +#define MLXREG_LC_REG_CPLD1_PN_OFFSET 0x2504 +#define MLXREG_LC_REG_FPGA1_PN_OFFSET 0x2506 +#define MLXREG_LC_REG_RESET_CAUSE_OFFSET 0x251d +#define MLXREG_LC_REG_LED1_OFFSET 0x2520 +#define MLXREG_LC_REG_GP0_OFFSET 0x252e +#define MLXREG_LC_REG_FIELD_UPGRADE 0x2534 +#define MLXREG_LC_CHANNEL_I2C_REG 0x25dc +#define MLXREG_LC_REG_CPLD1_MVER_OFFSET 0x25de +#define MLXREG_LC_REG_FPGA1_MVER_OFFSET 0x25df +#define MLXREG_LC_REG_MAX_POWER_OFFSET 0x25f1 +#define MLXREG_LC_REG_CONFIG_OFFSET 0x25fb +#define MLXREG_LC_REG_MAX 0x3fff + +/** + * enum mlxreg_lc_type - line cards types + * + * @MLXREG_LC_SN4800_C16: 100GbE line card with 16 QSFP28 ports; + */ +enum mlxreg_lc_type { + MLXREG_LC_SN4800_C16 = 0x0000, +}; + +/** + * enum mlxreg_lc_state - line cards state + * + * @MLXREG_LC_INITIALIZED: line card is initialized; + * @MLXREG_LC_POWERED: line card is powered; + * @MLXREG_LC_SYNCED: line card is synchronized between hardware and firmware; + */ +enum mlxreg_lc_state { + MLXREG_LC_INITIALIZED = BIT(0), + MLXREG_LC_POWERED = BIT(1), + MLXREG_LC_SYNCED = BIT(2), +}; + +#define MLXREG_LC_CONFIGURED (MLXREG_LC_INITIALIZED | MLXREG_LC_POWERED | MLXREG_LC_SYNCED) + +/* mlxreg_lc - device private data + * @dev: platform device; + * @lock: line card lock; + * @par_regmap: parent device regmap handle; + * @data: pltaform core data; + * @io_data: register access platform data; + * @led_data: LED platform data ; + * @mux_data: MUX platform data; + * @led: LED device; + * @io_regs: register access device; + * @mux_brdinfo: mux configuration; + * @mux: mux devices; + * @aux_devs: I2C devices feeding by auxiliary power; + * @aux_devs_num: number of I2C devices feeding by auxiliary power; + * @main_devs: I2C devices feeding by main power; + * @main_devs_num: number of I2C devices feeding by main power; + * @state: line card state; + */ +struct mlxreg_lc { + struct device *dev; + struct mutex lock; /* line card access lock */ + void *par_regmap; + struct mlxreg_core_data *data; + struct mlxreg_core_platform_data *io_data; + struct mlxreg_core_platform_data *led_data; + struct mlxcpld_mux_plat_data *mux_data; + struct platform_device *led; + struct platform_device *io_regs; + struct i2c_board_info *mux_brdinfo; + struct platform_device *mux; + struct mlxreg_hotplug_device *aux_devs; + int aux_devs_num; + struct mlxreg_hotplug_device *main_devs; + int main_devs_num; + enum mlxreg_lc_state state; +}; + +static bool mlxreg_lc_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MLXREG_LC_REG_LED1_OFFSET: + case MLXREG_LC_REG_GP0_OFFSET: + case MLXREG_LC_REG_FIELD_UPGRADE: + case MLXREG_LC_CHANNEL_I2C_REG: + return true; + } + return false; +} + +static bool mlxreg_lc_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MLXREG_LC_REG_CPLD1_VER_OFFSET: + case MLXREG_LC_REG_FPGA1_VER_OFFSET: + case MLXREG_LC_REG_CPLD1_PN_OFFSET: + case MLXREG_LC_REG_FPGA1_PN_OFFSET: + case MLXREG_LC_REG_RESET_CAUSE_OFFSET: + case MLXREG_LC_REG_LED1_OFFSET: + case MLXREG_LC_REG_GP0_OFFSET: + case MLXREG_LC_REG_FIELD_UPGRADE: + case MLXREG_LC_CHANNEL_I2C_REG: + case MLXREG_LC_REG_CPLD1_MVER_OFFSET: + case MLXREG_LC_REG_FPGA1_MVER_OFFSET: + case MLXREG_LC_REG_MAX_POWER_OFFSET: + case MLXREG_LC_REG_CONFIG_OFFSET: + return true; + } + return false; +} + +static bool mlxreg_lc_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MLXREG_LC_REG_CPLD1_VER_OFFSET: + case MLXREG_LC_REG_FPGA1_VER_OFFSET: + case MLXREG_LC_REG_CPLD1_PN_OFFSET: + case MLXREG_LC_REG_FPGA1_PN_OFFSET: + case MLXREG_LC_REG_RESET_CAUSE_OFFSET: + case MLXREG_LC_REG_LED1_OFFSET: + case MLXREG_LC_REG_GP0_OFFSET: + case MLXREG_LC_REG_FIELD_UPGRADE: + case MLXREG_LC_CHANNEL_I2C_REG: + case MLXREG_LC_REG_CPLD1_MVER_OFFSET: + case MLXREG_LC_REG_FPGA1_MVER_OFFSET: + case MLXREG_LC_REG_MAX_POWER_OFFSET: + case MLXREG_LC_REG_CONFIG_OFFSET: + return true; + } + return false; +} + +static const struct reg_default mlxreg_lc_regmap_default[] = { + { MLXREG_LC_CHANNEL_I2C_REG, 0x00 }, +}; + +/* Configuration for the register map of a device with 2 bytes address space. */ +static const struct regmap_config mlxreg_lc_regmap_conf = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MLXREG_LC_REG_MAX, + .cache_type = REGCACHE_FLAT, + .writeable_reg = mlxreg_lc_writeable_reg, + .readable_reg = mlxreg_lc_readable_reg, + .volatile_reg = mlxreg_lc_volatile_reg, + .reg_defaults = mlxreg_lc_regmap_default, + .num_reg_defaults = ARRAY_SIZE(mlxreg_lc_regmap_default), +}; + +/* Default channels vector. + * It contains only the channels, which physically connected to the devices, + * empty channels are skipped. + */ +static int mlxreg_lc_chan[] = { + 0x04, 0x05, 0x06, 0x07, 0x08, 0x10, 0x20, 0x21, 0x22, 0x23, 0x40, 0x41, + 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, + 0x4e, 0x4f +}; + +/* Defaul mux configuration. */ +static struct mlxcpld_mux_plat_data mlxreg_lc_mux_data[] = { + { + .chan_ids = mlxreg_lc_chan, + .num_adaps = ARRAY_SIZE(mlxreg_lc_chan), + .sel_reg_addr = MLXREG_LC_CHANNEL_I2C_REG, + .reg_size = 2, + }, +}; + +/* Defaul mux board info. */ +static struct i2c_board_info mlxreg_lc_mux_brdinfo = { + I2C_BOARD_INFO("i2c-mux-mlxcpld", 0x32), +}; + +/* Line card default auxiliary power static devices. */ +static struct i2c_board_info mlxreg_lc_aux_pwr_devices[] = { + { + I2C_BOARD_INFO("24c32", 0x51), + }, + { + I2C_BOARD_INFO("24c32", 0x51), + }, +}; + +/* Line card default auxiliary power board info. */ +static struct mlxreg_hotplug_device mlxreg_lc_aux_pwr_brdinfo[] = { + { + .brdinfo = &mlxreg_lc_aux_pwr_devices[0], + .nr = 3, + }, + { + .brdinfo = &mlxreg_lc_aux_pwr_devices[1], + .nr = 4, + }, +}; + +/* Line card default main power static devices. */ +static struct i2c_board_info mlxreg_lc_main_pwr_devices[] = { + { + I2C_BOARD_INFO("mp2975", 0x62), + }, + { + I2C_BOARD_INFO("mp2975", 0x64), + }, + { + I2C_BOARD_INFO("max11603", 0x6d), + }, + { + I2C_BOARD_INFO("lm25066", 0x15), + }, +}; + +/* Line card default main power board info. */ +static struct mlxreg_hotplug_device mlxreg_lc_main_pwr_brdinfo[] = { + { + .brdinfo = &mlxreg_lc_main_pwr_devices[0], + .nr = 0, + }, + { + .brdinfo = &mlxreg_lc_main_pwr_devices[1], + .nr = 0, + }, + { + .brdinfo = &mlxreg_lc_main_pwr_devices[2], + .nr = 1, + }, + { + .brdinfo = &mlxreg_lc_main_pwr_devices[3], + .nr = 2, + }, +}; + +/* LED default data. */ +static struct mlxreg_core_data mlxreg_lc_led_data[] = { + { + .label = "status:green", + .reg = MLXREG_LC_REG_LED1_OFFSET, + .mask = GENMASK(7, 4), + }, + { + .label = "status:orange", + .reg = MLXREG_LC_REG_LED1_OFFSET, + .mask = GENMASK(7, 4), + }, +}; + +static struct mlxreg_core_platform_data mlxreg_lc_led = { + .identity = "pci", + .data = mlxreg_lc_led_data, + .counter = ARRAY_SIZE(mlxreg_lc_led_data), +}; + +/* Default register access data. */ +static struct mlxreg_core_data mlxreg_lc_io_data[] = { + { + .label = "cpld1_version", + .reg = MLXREG_LC_REG_CPLD1_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "fpga1_version", + .reg = MLXREG_LC_REG_FPGA1_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld1_pn", + .reg = MLXREG_LC_REG_CPLD1_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "fpga1_pn", + .reg = MLXREG_LC_REG_FPGA1_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld1_version_min", + .reg = MLXREG_LC_REG_CPLD1_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "fpga1_version_min", + .reg = MLXREG_LC_REG_FPGA1_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "reset_fpga_not_done", + .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0444, + }, + { + .label = "reset_aux_pwr_or_ref", + .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0444, + }, + { + .label = "reset_dc_dc_pwr_fail", + .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0444, + }, + { + .label = "reset_from_chassis", + .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0444, + }, + { + .label = "reset_pwr_off_from_chassis", + .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0444, + }, + { + .label = "reset_line_card", + .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0444, + }, + { + .label = "reset_line_card_pwr_en", + .reg = MLXREG_LC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0444, + }, + { + .label = "cpld_upgrade_en", + .reg = MLXREG_LC_REG_FIELD_UPGRADE, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0644, + .secured = 1, + }, + { + .label = "fpga_upgrade_en", + .reg = MLXREG_LC_REG_FIELD_UPGRADE, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0644, + .secured = 1, + }, + { + .label = "qsfp_pwr_en", + .reg = MLXREG_LC_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0644, + }, + { + .label = "vpd_wp", + .reg = MLXREG_LC_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0644, + .secured = 1, + }, + { + .label = "agb_spi_burn_en", + .reg = MLXREG_LC_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0644, + .secured = 1, + }, + { + .label = "fpga_spi_burn_en", + .reg = MLXREG_LC_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0644, + .secured = 1, + }, + { + .label = "max_power", + .reg = MLXREG_LC_REG_MAX_POWER_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "config", + .reg = MLXREG_LC_REG_CONFIG_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, +}; + +static struct mlxreg_core_platform_data mlxreg_lc_regs_io = { + .data = mlxreg_lc_io_data, + .counter = ARRAY_SIZE(mlxreg_lc_io_data), +}; + +static int +mlxreg_lc_create_static_devices(struct mlxreg_lc *mlxreg_lc, struct mlxreg_hotplug_device *devs, + int size) +{ + struct mlxreg_hotplug_device *dev = devs; + int i; + + /* Create static I2C device feeding by auxiliary or main power. */ + for (i = 0; i < size; i++, dev++) { + dev->client = i2c_new_client_device(dev->adapter, dev->brdinfo); + if (IS_ERR(dev->client)) { + dev_err(mlxreg_lc->dev, "Failed to create client %s at bus %d at addr 0x%02x\n", + dev->brdinfo->type, dev->nr, dev->brdinfo->addr); + + dev->adapter = NULL; + goto fail_create_static_devices; + } + } + + return 0; + +fail_create_static_devices: + while (--i >= 0) { + dev = devs + i; + i2c_unregister_device(dev->client); + dev->client = NULL; + } + return IS_ERR(dev->client); +} + +static void +mlxreg_lc_destroy_static_devices(struct mlxreg_lc *mlxreg_lc, struct mlxreg_hotplug_device *devs, + int size) +{ + struct mlxreg_hotplug_device *dev = devs; + int i; + + /* Destroy static I2C device feeding by auxiliary or main power. */ + for (i = 0; i < size; i++, dev++) { + if (dev->client) { + i2c_unregister_device(dev->client); + dev->client = NULL; + } + } +} + +static int mlxreg_lc_power_on_off(struct mlxreg_lc *mlxreg_lc, u8 action) +{ + u32 regval; + int err; + + mutex_lock(&mlxreg_lc->lock); + + err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_pwr, ®val); + if (err) + goto regmap_read_fail; + + if (action) + regval |= BIT(mlxreg_lc->data->slot - 1); + else + regval &= ~BIT(mlxreg_lc->data->slot - 1); + + err = regmap_write(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_pwr, regval); + +regmap_read_fail: + mutex_unlock(&mlxreg_lc->lock); + return err; +} + +static int mlxreg_lc_enable_disable(struct mlxreg_lc *mlxreg_lc, bool action) +{ + u32 regval; + int err; + + /* + * Hardware holds the line card after powering on in the disabled state. Holding line card + * in disabled state protects access to the line components, like FPGA and gearboxes. + * Line card should be enabled in order to get it in operational state. Line card could be + * disabled for moving it to non-operational state. Enabling line card does not affect the + * line card which is already has been enabled. Disabling does not affect the disabled line + * card. + */ + mutex_lock(&mlxreg_lc->lock); + + err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_ena, ®val); + if (err) + goto regmap_read_fail; + + if (action) + regval |= BIT(mlxreg_lc->data->slot - 1); + else + regval &= ~BIT(mlxreg_lc->data->slot - 1); + + err = regmap_write(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_ena, regval); + +regmap_read_fail: + mutex_unlock(&mlxreg_lc->lock); + return err; +} + +static int +mlxreg_lc_sn4800_c16_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap, + struct mlxreg_core_data *data) +{ + struct device *dev = &data->hpdev.client->dev; + + /* Set line card configuration according to the type. */ + mlxreg_lc->mux_data = mlxreg_lc_mux_data; + mlxreg_lc->io_data = &mlxreg_lc_regs_io; + mlxreg_lc->led_data = &mlxreg_lc_led; + mlxreg_lc->mux_brdinfo = &mlxreg_lc_mux_brdinfo; + + mlxreg_lc->aux_devs = devm_kmemdup(dev, mlxreg_lc_aux_pwr_brdinfo, + sizeof(mlxreg_lc_aux_pwr_brdinfo), GFP_KERNEL); + if (!mlxreg_lc->aux_devs) + return -ENOMEM; + mlxreg_lc->aux_devs_num = ARRAY_SIZE(mlxreg_lc_aux_pwr_brdinfo); + mlxreg_lc->main_devs = devm_kmemdup(dev, mlxreg_lc_main_pwr_brdinfo, + sizeof(mlxreg_lc_main_pwr_brdinfo), GFP_KERNEL); + if (!mlxreg_lc->main_devs) + return -ENOMEM; + mlxreg_lc->main_devs_num = ARRAY_SIZE(mlxreg_lc_main_pwr_brdinfo); + + return 0; +} + +static void +mlxreg_lc_state_update(struct mlxreg_lc *mlxreg_lc, enum mlxreg_lc_state state, u8 action) +{ + mutex_lock(&mlxreg_lc->lock); + + if (action) + mlxreg_lc->state |= state; + else + mlxreg_lc->state &= ~state; + + mutex_unlock(&mlxreg_lc->lock); +} + +/* + * Callback is to be called from mlxreg-hotplug driver to notify about line card about received + * event. + */ +static int mlxreg_lc_event_handler(void *handle, enum mlxreg_hotplug_kind kind, u8 action) +{ + struct mlxreg_lc *mlxreg_lc = handle; + int err = 0; + + dev_info(mlxreg_lc->dev, "linecard#%d state %d event kind %d action %d\n", + mlxreg_lc->data->slot, mlxreg_lc->state, kind, action); + + if (!(mlxreg_lc->state & MLXREG_LC_INITIALIZED)) + return 0; + + switch (kind) { + case MLXREG_HOTPLUG_LC_SYNCED: + /* + * Synchronization event - hardware and firmware are synchronized. Power on/off + * line card - to allow/disallow main power source. + */ + mlxreg_lc_state_update(mlxreg_lc, MLXREG_LC_SYNCED, action); + /* Power line card if it is not powered yet. */ + if (!(mlxreg_lc->state & MLXREG_LC_POWERED) && action) { + err = mlxreg_lc_power_on_off(mlxreg_lc, 1); + if (err) + return err; + } + /* In case line card is configured - enable it. */ + if (mlxreg_lc->state & MLXREG_LC_CONFIGURED && action) + err = mlxreg_lc_enable_disable(mlxreg_lc, 1); + break; + case MLXREG_HOTPLUG_LC_POWERED: + /* Power event - attach or de-attach line card device feeding by the main power. */ + if (action) { + /* Do not create devices, if line card is already powered. */ + if (mlxreg_lc->state & MLXREG_LC_POWERED) { + /* In case line card is configured - enable it. */ + if (mlxreg_lc->state & MLXREG_LC_CONFIGURED) + err = mlxreg_lc_enable_disable(mlxreg_lc, 1); + return err; + } + err = mlxreg_lc_create_static_devices(mlxreg_lc, mlxreg_lc->main_devs, + mlxreg_lc->main_devs_num); + if (err) + return err; + + /* In case line card is already in ready state - enable it. */ + if (mlxreg_lc->state & MLXREG_LC_CONFIGURED) + err = mlxreg_lc_enable_disable(mlxreg_lc, 1); + } else { + mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->main_devs, + mlxreg_lc->main_devs_num); + } + mlxreg_lc_state_update(mlxreg_lc, MLXREG_LC_POWERED, action); + break; + case MLXREG_HOTPLUG_LC_READY: + /* + * Ready event – enable line card by releasing it from reset or disable it by put + * to reset state. + */ + err = mlxreg_lc_enable_disable(mlxreg_lc, !!action); + break; + case MLXREG_HOTPLUG_LC_THERMAL: + /* Thermal shutdown event – power off line card. */ + if (action) + err = mlxreg_lc_power_on_off(mlxreg_lc, 0); + break; + default: + break; + } + + return err; +} + +/* + * Callback is to be called from i2c-mux-mlxcpld driver to indicate that all adapter devices has + * been created. + */ +static int mlxreg_lc_completion_notify(void *handle, struct i2c_adapter *parent, + struct i2c_adapter *adapters[]) +{ + struct mlxreg_hotplug_device *main_dev, *aux_dev; + struct mlxreg_lc *mlxreg_lc = handle; + u32 regval; + int i, err; + + /* Update I2C devices feeding by auxiliary power. */ + aux_dev = mlxreg_lc->aux_devs; + for (i = 0; i < mlxreg_lc->aux_devs_num; i++, aux_dev++) { + aux_dev->adapter = adapters[aux_dev->nr]; + aux_dev->nr = adapters[aux_dev->nr]->nr; + } + + err = mlxreg_lc_create_static_devices(mlxreg_lc, mlxreg_lc->aux_devs, + mlxreg_lc->aux_devs_num); + if (err) + return err; + + /* Update I2C devices feeding by main power. */ + main_dev = mlxreg_lc->main_devs; + for (i = 0; i < mlxreg_lc->main_devs_num; i++, main_dev++) { + main_dev->adapter = adapters[main_dev->nr]; + main_dev->nr = adapters[main_dev->nr]->nr; + } + + /* Verify if line card is powered. */ + err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_pwr, ®val); + if (err) + goto mlxreg_lc_regmap_read_power_fail; + + if (regval & mlxreg_lc->data->mask) { + err = mlxreg_lc_create_static_devices(mlxreg_lc, mlxreg_lc->main_devs, + mlxreg_lc->main_devs_num); + if (err) + goto mlxreg_lc_create_static_devices_failed; + + mlxreg_lc_state_update(mlxreg_lc, MLXREG_LC_POWERED, 1); + } + + /* Verify if line card is synchronized. */ + err = regmap_read(mlxreg_lc->par_regmap, mlxreg_lc->data->reg_sync, ®val); + if (err) + goto mlxreg_lc_regmap_read_sync_fail; + + /* Power on line card if necessary. */ + if (regval & mlxreg_lc->data->mask) { + mlxreg_lc->state |= MLXREG_LC_SYNCED; + mlxreg_lc_state_update(mlxreg_lc, MLXREG_LC_SYNCED, 1); + if (mlxreg_lc->state & ~MLXREG_LC_POWERED) { + err = mlxreg_lc_power_on_off(mlxreg_lc, 1); + if (err) + goto mlxreg_lc_regmap_power_on_off_fail; + } + } + + mlxreg_lc_state_update(mlxreg_lc, MLXREG_LC_INITIALIZED, 1); + + return 0; + +mlxreg_lc_regmap_power_on_off_fail: +mlxreg_lc_regmap_read_sync_fail: + if (mlxreg_lc->state & MLXREG_LC_POWERED) + mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->main_devs, + mlxreg_lc->main_devs_num); +mlxreg_lc_create_static_devices_failed: + mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->aux_devs, mlxreg_lc->aux_devs_num); +mlxreg_lc_regmap_read_power_fail: + return err; +} + +static int +mlxreg_lc_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap, + struct mlxreg_core_data *data) +{ + struct device *dev = &data->hpdev.client->dev; + int lsb, err; + u32 regval; + + /* Validate line card type. */ + err = regmap_read(regmap, MLXREG_LC_REG_CONFIG_OFFSET, &lsb); + err = (!err) ? regmap_read(regmap, MLXREG_LC_REG_CONFIG_OFFSET, ®val) : err; + if (err) + return err; + regval = (regval & GENMASK(7, 0)) << 8 | (lsb & GENMASK(7, 0)); + switch (regval) { + case MLXREG_LC_SN4800_C16: + err = mlxreg_lc_sn4800_c16_config_init(mlxreg_lc, regmap, data); + if (err) + return err; + break; + default: + return -ENODEV; + } + + /* Create mux infrastructure. */ + mlxreg_lc->mux_data->handle = mlxreg_lc; + mlxreg_lc->mux_data->completion_notify = mlxreg_lc_completion_notify; + mlxreg_lc->mux_brdinfo->platform_data = mlxreg_lc->mux_data; + mlxreg_lc->mux = platform_device_register_resndata(dev, "i2c-mux-mlxcpld", data->hpdev.nr, + NULL, 0, mlxreg_lc->mux_data, + sizeof(*mlxreg_lc->mux_data)); + if (IS_ERR(mlxreg_lc->mux)) + return PTR_ERR(mlxreg_lc->mux); + + /* Register IO access driver. */ + if (mlxreg_lc->io_data) { + mlxreg_lc->io_data->regmap = regmap; + mlxreg_lc->io_regs = + platform_device_register_resndata(dev, "mlxreg-io", data->hpdev.nr, NULL, 0, + mlxreg_lc->io_data, sizeof(*mlxreg_lc->io_data)); + if (IS_ERR(mlxreg_lc->io_regs)) { + err = PTR_ERR(mlxreg_lc->io_regs); + goto fail_register_io; + } + } + + /* Register LED driver. */ + if (mlxreg_lc->led_data) { + mlxreg_lc->led_data->regmap = regmap; + mlxreg_lc->led = + platform_device_register_resndata(dev, "leds-mlxreg", data->hpdev.nr, NULL, 0, + mlxreg_lc->led_data, + sizeof(*mlxreg_lc->led_data)); + if (IS_ERR(mlxreg_lc->led)) { + err = PTR_ERR(mlxreg_lc->led); + goto fail_register_led; + } + } + + return 0; + +fail_register_led: + if (mlxreg_lc->io_regs) + platform_device_unregister(mlxreg_lc->io_regs); +fail_register_io: + if (mlxreg_lc->mux) + platform_device_unregister(mlxreg_lc->mux); + + return err; +} + +static void mlxreg_lc_config_exit(struct mlxreg_lc *mlxreg_lc) +{ + /* Unregister LED driver. */ + if (mlxreg_lc->led) + platform_device_unregister(mlxreg_lc->led); + /* Unregister IO access driver. */ + if (mlxreg_lc->io_regs) + platform_device_unregister(mlxreg_lc->io_regs); + /* Remove mux infrastructure. */ + if (mlxreg_lc->mux) + platform_device_unregister(mlxreg_lc->mux); +} + +static int mlxreg_lc_probe(struct platform_device *pdev) +{ + struct mlxreg_core_hotplug_platform_data *par_pdata; + struct mlxreg_core_data *data; + struct mlxreg_lc *mlxreg_lc; + void *regmap; + int i, err; + + data = dev_get_platdata(&pdev->dev); + if (!data) + return -EINVAL; + + mlxreg_lc = devm_kzalloc(&pdev->dev, sizeof(*mlxreg_lc), GFP_KERNEL); + if (!mlxreg_lc) + return -ENOMEM; + + mutex_init(&mlxreg_lc->lock); + /* Set event notification callback. */ + if (data->notifier) { + data->notifier->user_handler = mlxreg_lc_event_handler; + data->notifier->handle = mlxreg_lc; + } + data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr); + if (!data->hpdev.adapter) { + dev_err(&pdev->dev, "Failed to get adapter for bus %d\n", + data->hpdev.nr); + return -EFAULT; + } + + /* Create device at the top of line card I2C tree.*/ + data->hpdev.client = i2c_new_client_device(data->hpdev.adapter, + data->hpdev.brdinfo); + if (IS_ERR(data->hpdev.client)) { + dev_err(&pdev->dev, "Failed to create client %s at bus %d at addr 0x%02x\n", + data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); + + i2c_put_adapter(data->hpdev.adapter); + data->hpdev.adapter = NULL; + return PTR_ERR(data->hpdev.client); + } + + regmap = devm_regmap_init_i2c(data->hpdev.client, + &mlxreg_lc_regmap_conf); + if (IS_ERR(regmap)) { + err = PTR_ERR(regmap); + goto mlxreg_lc_probe_fail; + } + + /* Set default registers. */ + for (i = 0; i < mlxreg_lc_regmap_conf.num_reg_defaults; i++) { + err = regmap_write(regmap, mlxreg_lc_regmap_default[i].reg, + mlxreg_lc_regmap_default[i].def); + if (err) + goto mlxreg_lc_probe_fail; + } + + /* Sync registers with hardware. */ + regcache_mark_dirty(regmap); + err = regcache_sync(regmap); + if (err) + goto mlxreg_lc_probe_fail; + + par_pdata = data->hpdev.brdinfo->platform_data; + mlxreg_lc->par_regmap = par_pdata->regmap; + mlxreg_lc->data = data; + mlxreg_lc->dev = &pdev->dev; + platform_set_drvdata(pdev, mlxreg_lc); + + /* Configure line card. */ + err = mlxreg_lc_config_init(mlxreg_lc, regmap, data); + if (err) + goto mlxreg_lc_probe_fail; + + return err; + +mlxreg_lc_probe_fail: + i2c_put_adapter(data->hpdev.adapter); + return err; +} + +static int mlxreg_lc_remove(struct platform_device *pdev) +{ + struct mlxreg_core_data *data = dev_get_platdata(&pdev->dev); + struct mlxreg_lc *mlxreg_lc = platform_get_drvdata(pdev); + + /* Clear event notification callback. */ + if (data->notifier) { + data->notifier->user_handler = NULL; + data->notifier->handle = NULL; + } + + /* Destroy static I2C device feeding by main power. */ + mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->main_devs, + mlxreg_lc->main_devs_num); + /* Destroy static I2C device feeding by auxiliary power. */ + mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->aux_devs, mlxreg_lc->aux_devs_num); + /* Unregister underlying drivers. */ + mlxreg_lc_config_exit(mlxreg_lc); + if (data->hpdev.client) { + i2c_unregister_device(data->hpdev.client); + data->hpdev.client = NULL; + i2c_put_adapter(data->hpdev.adapter); + data->hpdev.adapter = NULL; + } + + return 0; +} + +static struct platform_driver mlxreg_lc_driver = { + .probe = mlxreg_lc_probe, + .remove = mlxreg_lc_remove, + .driver = { + .name = "mlxreg-lc", + }, +}; + +module_platform_driver(mlxreg_lc_driver); + +MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>"); +MODULE_DESCRIPTION("Nvidia line card platform driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("platform:mlxreg-lc"); diff --git a/drivers/platform/surface/surface3-wmi.c b/drivers/platform/surface/surface3-wmi.c index fcd1d4fb94d5..09ac9cfc40d8 100644 --- a/drivers/platform/surface/surface3-wmi.c +++ b/drivers/platform/surface/surface3-wmi.c @@ -139,13 +139,12 @@ static acpi_status s3_wmi_attach_spi_device(acpi_handle handle, static int s3_wmi_check_platform_device(struct device *dev, void *data) { - struct acpi_device *adev, *ts_adev = NULL; - acpi_handle handle; + struct acpi_device *adev = ACPI_COMPANION(dev); + struct acpi_device *ts_adev = NULL; acpi_status status; /* ignore non ACPI devices */ - handle = ACPI_HANDLE(dev); - if (!handle || acpi_bus_get_device(handle, &adev)) + if (!adev) return 0; /* check for LID ACPI switch */ @@ -159,7 +158,7 @@ static int s3_wmi_check_platform_device(struct device *dev, void *data) strlen(SPI_CTL_OBJ_NAME))) return 0; - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, adev->handle, 1, s3_wmi_attach_spi_device, NULL, &ts_adev, NULL); if (ACPI_FAILURE(status)) diff --git a/drivers/platform/surface/surface3_power.c b/drivers/platform/surface/surface3_power.c index 90c1568ea4e0..abac3eec565e 100644 --- a/drivers/platform/surface/surface3_power.c +++ b/drivers/platform/surface/surface3_power.c @@ -159,12 +159,11 @@ mshw0011_notify(struct mshw0011_data *cdata, u8 arg1, u8 arg2, unsigned int *ret_value) { union acpi_object *obj; - struct acpi_device *adev; acpi_handle handle; unsigned int i; handle = ACPI_HANDLE(&cdata->adp1->dev); - if (!handle || acpi_bus_get_device(handle, &adev)) + if (!handle) return -ENODEV; obj = acpi_evaluate_dsm_typed(handle, &mshw0011_guid, arg1, arg2, NULL, diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index 4428c4330229..e70f4c63554e 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -77,6 +77,42 @@ static const struct software_node ssam_node_bas_dtx = { .parent = &ssam_node_root, }; +/* HID keyboard (TID1). */ +static const struct software_node ssam_node_hid_tid1_keyboard = { + .name = "ssam:01:15:01:01:00", + .parent = &ssam_node_root, +}; + +/* HID pen stash (TID1; pen taken / stashed away evens). */ +static const struct software_node ssam_node_hid_tid1_penstash = { + .name = "ssam:01:15:01:02:00", + .parent = &ssam_node_root, +}; + +/* HID touchpad (TID1). */ +static const struct software_node ssam_node_hid_tid1_touchpad = { + .name = "ssam:01:15:01:03:00", + .parent = &ssam_node_root, +}; + +/* HID device instance 6 (TID1, unknown HID device). */ +static const struct software_node ssam_node_hid_tid1_iid6 = { + .name = "ssam:01:15:01:06:00", + .parent = &ssam_node_root, +}; + +/* HID device instance 7 (TID1, unknown HID device). */ +static const struct software_node ssam_node_hid_tid1_iid7 = { + .name = "ssam:01:15:01:07:00", + .parent = &ssam_node_root, +}; + +/* HID system controls (TID1). */ +static const struct software_node ssam_node_hid_tid1_sysctrl = { + .name = "ssam:01:15:01:08:00", + .parent = &ssam_node_root, +}; + /* HID keyboard. */ static const struct software_node ssam_node_hid_main_keyboard = { .name = "ssam:01:15:02:01:00", @@ -159,6 +195,21 @@ static const struct software_node *ssam_node_group_sl3[] = { NULL, }; +/* Devices for Surface Laptop Studio. */ +static const struct software_node *ssam_node_group_sls[] = { + &ssam_node_root, + &ssam_node_bat_ac, + &ssam_node_bat_main, + &ssam_node_tmp_pprof, + &ssam_node_hid_tid1_keyboard, + &ssam_node_hid_tid1_penstash, + &ssam_node_hid_tid1_touchpad, + &ssam_node_hid_tid1_iid6, + &ssam_node_hid_tid1_iid7, + &ssam_node_hid_tid1_sysctrl, + NULL, +}; + /* Devices for Surface Laptop Go. */ static const struct software_node *ssam_node_group_slg1[] = { &ssam_node_root, @@ -177,6 +228,15 @@ static const struct software_node *ssam_node_group_sp7[] = { NULL, }; +static const struct software_node *ssam_node_group_sp8[] = { + &ssam_node_root, + &ssam_node_bat_ac, + &ssam_node_bat_main, + &ssam_node_tmp_pprof, + /* TODO: Add support for keyboard cover. */ + NULL, +}; + /* -- Device registry helper functions. ------------------------------------- */ @@ -483,6 +543,9 @@ static const struct acpi_device_id ssam_platform_hub_match[] = { /* Surface Pro 7+ */ { "MSHW0119", (unsigned long)ssam_node_group_sp7 }, + /* Surface Pro 8 */ + { "MSHW0263", (unsigned long)ssam_node_group_sp8 }, + /* Surface Book 2 */ { "MSHW0107", (unsigned long)ssam_node_group_gen5 }, @@ -507,6 +570,9 @@ static const struct acpi_device_id ssam_platform_hub_match[] = { /* Surface Laptop Go 1 */ { "MSHW0118", (unsigned long)ssam_node_group_slg1 }, + /* Surface Laptop Studio */ + { "MSHW0123", (unsigned long)ssam_node_group_sls }, + { }, }; MODULE_DEVICE_TABLE(acpi, ssam_platform_hub_match); diff --git a/drivers/platform/surface/surface_gpe.c b/drivers/platform/surface/surface_gpe.c index 86f6991b1215..c1775db29efb 100644 --- a/drivers/platform/surface/surface_gpe.c +++ b/drivers/platform/surface/surface_gpe.c @@ -26,6 +26,11 @@ static const struct property_entry lid_device_props_l17[] = { {}, }; +static const struct property_entry lid_device_props_l4B[] = { + PROPERTY_ENTRY_U32("gpe", 0x4B), + {}, +}; + static const struct property_entry lid_device_props_l4D[] = { PROPERTY_ENTRY_U32("gpe", 0x4D), {}, @@ -158,6 +163,14 @@ static const struct dmi_system_id dmi_lid_device_table[] = { }, .driver_data = (void *)lid_device_props_l4D, }, + { + .ident = "Surface Laptop Studio", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop Studio"), + }, + .driver_data = (void *)lid_device_props_l4B, + }, { } }; diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 20208207e366..b110a2e6b8f3 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -91,6 +91,21 @@ config PEAQ_WMI help Say Y here if you want to support WMI-based hotkeys on PEAQ 2-in-1s. +config NVIDIA_WMI_EC_BACKLIGHT + tristate "EC Backlight Driver for Hybrid Graphics Notebook Systems" + depends on ACPI_WMI + depends on BACKLIGHT_CLASS_DEVICE + help + This driver provides a sysfs backlight interface for notebook systems + which are equipped with NVIDIA hybrid graphics and drive LCD backlight + levels through the Embedded Controller (EC). + + Say Y or M here if you want to control the backlight on a notebook + system with an EC-driven backlight. + + If you choose to compile this driver as a module the module will be + called nvidia-wmi-ec-backlight. + config XIAOMI_WMI tristate "Xiaomi WMI key driver" depends on ACPI_WMI @@ -426,6 +441,7 @@ config HP_WMI depends on RFKILL || RFKILL = n select INPUT_SPARSEKMAP select ACPI_PLATFORM_PROFILE + select HWMON help Say Y here if you want to support WMI-based hotkeys on HP laptops and to read data from WMI such as docking or ambient light sensor state. @@ -715,6 +731,16 @@ config PCENGINES_APU2 To compile this driver as a module, choose M here: the module will be called pcengines-apuv2. +config BARCO_P50_GPIO + tristate "Barco P50 GPIO driver for identify LED/button" + depends on GPIOLIB + help + This driver provides access to the GPIOs for the identify button + and led present on Barco P50 board. + + To compile this driver as a module, choose M here: the module + will be called barco-p50-gpio. + config SAMSUNG_LAPTOP tristate "Samsung Laptop driver" depends on RFKILL || RFKILL = n @@ -907,6 +933,9 @@ config SONYPI_COMPAT config SYSTEM76_ACPI tristate "System76 ACPI Driver" depends on ACPI + depends on ACPI_BATTERY + depends on HWMON + depends on INPUT select NEW_LEDS select LEDS_CLASS select LEDS_TRIGGERS diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 69690e26bb6d..219478061683 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o # WMI drivers obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o obj-$(CONFIG_MXM_WMI) += mxm-wmi.o +obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o @@ -80,6 +81,9 @@ obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o # PC Engines obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o +# Barco +obj-$(CONFIG_BARCO_P50_GPIO) += barco-p50-gpio.o + # Samsung obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 694b45ed06a2..9c6943e401a6 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -138,7 +138,7 @@ struct event_return_value { u16 reserved1; u8 kbd_dock_state; u8 reserved2; -} __attribute__((packed)); +} __packed; /* * GUID3 Get Device Status device flags @@ -172,33 +172,33 @@ struct func_input_params { u8 app_status; /* Acer Device Status. LM, ePM, RF Button... */ u8 app_mask; /* Bit mask to app_status */ u8 reserved; -} __attribute__((packed)); +} __packed; struct func_return_value { u8 error_code; /* Error Code */ u8 ec_return_value; /* EC Return Value */ u16 reserved; -} __attribute__((packed)); +} __packed; struct wmid3_gds_set_input_param { /* Set Device Status input parameter */ u8 function_num; /* Function Number */ u8 hotkey_number; /* Hotkey Number */ u16 devices; /* Set Device */ u8 volume_value; /* Volume Value */ -} __attribute__((packed)); +} __packed; struct wmid3_gds_get_input_param { /* Get Device Status input parameter */ u8 function_num; /* Function Number */ u8 hotkey_number; /* Hotkey Number */ u16 devices; /* Get Device */ -} __attribute__((packed)); +} __packed; struct wmid3_gds_return_value { /* Get Device Status return value*/ u8 error_code; /* Error Code */ u8 ec_return_value; /* EC Return Value */ u16 devices; /* Current Device Status */ u32 reserved; -} __attribute__((packed)); +} __packed; struct hotkey_function_type_aa { u8 type; @@ -210,7 +210,7 @@ struct hotkey_function_type_aa { u16 display_func_bitmap; u16 others_func_bitmap; u8 commun_fn_key_number; -} __attribute__((packed)); +} __packed; /* * Interface capability flags diff --git a/drivers/platform/x86/amd-pmc.c b/drivers/platform/x86/amd-pmc.c index 3481479a2942..b7e50ed050a8 100644 --- a/drivers/platform/x86/amd-pmc.c +++ b/drivers/platform/x86/amd-pmc.c @@ -17,9 +17,11 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/iopoll.h> +#include <linux/limits.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/platform_device.h> +#include <linux/rtc.h> #include <linux/suspend.h> #include <linux/seq_file.h> #include <linux/uaccess.h> @@ -29,6 +31,10 @@ #define AMD_PMC_REGISTER_RESPONSE 0x980 #define AMD_PMC_REGISTER_ARGUMENT 0x9BC +/* PMC Scratch Registers */ +#define AMD_PMC_SCRATCH_REG_CZN 0x94 +#define AMD_PMC_SCRATCH_REG_YC 0xD14 + /* Base address of SMU for mapping physical address to virtual address */ #define AMD_PMC_SMU_INDEX_ADDRESS 0xB8 #define AMD_PMC_SMU_INDEX_DATA 0xBC @@ -71,7 +77,7 @@ #define AMD_CPU_ID_YC 0x14B5 #define PMC_MSG_DELAY_MIN_US 100 -#define RESPONSE_REGISTER_LOOP_MAX 200 +#define RESPONSE_REGISTER_LOOP_MAX 20000 #define SOC_SUBSYSTEM_IP_MAX 12 #define DELAY_MIN_US 2000 @@ -110,6 +116,10 @@ struct amd_pmc_dev { u32 base_addr; u32 cpu_id; u32 active_ips; +/* SMU version information */ + u16 major; + u16 minor; + u16 rev; struct device *dev; struct mutex lock; /* generic mutex lock */ #if IS_ENABLED(CONFIG_DEBUG_FS) @@ -118,7 +128,7 @@ struct amd_pmc_dev { }; static struct amd_pmc_dev pmc; -static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, bool set, u32 *data, u8 msg, bool ret); +static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret); static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset) { @@ -133,7 +143,7 @@ static inline void amd_pmc_reg_write(struct amd_pmc_dev *dev, int reg_offset, u3 struct smu_metrics { u32 table_version; u32 hint_count; - u32 s0i3_cyclecount; + u32 s0i3_last_entry_status; u32 timein_s0i2; u64 timeentering_s0i3_lastcapture; u64 timeentering_s0i3_totaltime; @@ -147,6 +157,49 @@ struct smu_metrics { u64 timecondition_notmet_totaltime[SOC_SUBSYSTEM_IP_MAX]; } __packed; +static int amd_pmc_get_smu_version(struct amd_pmc_dev *dev) +{ + int rc; + u32 val; + + rc = amd_pmc_send_cmd(dev, 0, &val, SMU_MSG_GETSMUVERSION, 1); + if (rc) + return rc; + + dev->major = (val >> 16) & GENMASK(15, 0); + dev->minor = (val >> 8) & GENMASK(7, 0); + dev->rev = (val >> 0) & GENMASK(7, 0); + + dev_dbg(dev->dev, "SMU version is %u.%u.%u\n", dev->major, dev->minor, dev->rev); + + return 0; +} + +static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev, + struct seq_file *s) +{ + u32 val; + + switch (pdev->cpu_id) { + case AMD_CPU_ID_CZN: + val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN); + break; + case AMD_CPU_ID_YC: + val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC); + break; + default: + return -EINVAL; + } + + if (dev) + dev_dbg(pdev->dev, "SMU idlemask s0i3: 0x%x\n", val); + + if (s) + seq_printf(s, "SMU idlemask : 0x%x\n", val); + + return 0; +} + #ifdef CONFIG_DEBUG_FS static int smu_fw_info_show(struct seq_file *s, void *unused) { @@ -162,9 +215,12 @@ static int smu_fw_info_show(struct seq_file *s, void *unused) seq_puts(s, "\n=== SMU Statistics ===\n"); seq_printf(s, "Table Version: %d\n", table.table_version); seq_printf(s, "Hint Count: %d\n", table.hint_count); - seq_printf(s, "S0i3 Cycle Count: %d\n", table.s0i3_cyclecount); + seq_printf(s, "Last S0i3 Status: %s\n", table.s0i3_last_entry_status ? "Success" : + "Unknown/Fail"); seq_printf(s, "Time (in us) to S0i3: %lld\n", table.timeentering_s0i3_lastcapture); seq_printf(s, "Time (in us) in S0i3: %lld\n", table.timein_s0i3_lastcapture); + seq_printf(s, "Time (in us) to resume from S0i3: %lld\n", + table.timeto_resume_to_os_lastcapture); seq_puts(s, "\n=== Active time (in us) ===\n"); for (idx = 0 ; idx < SOC_SUBSYSTEM_IP_MAX ; idx++) { @@ -201,6 +257,23 @@ static int s0ix_stats_show(struct seq_file *s, void *unused) } DEFINE_SHOW_ATTRIBUTE(s0ix_stats); +static int amd_pmc_idlemask_show(struct seq_file *s, void *unused) +{ + struct amd_pmc_dev *dev = s->private; + int rc; + + if (dev->major > 56 || (dev->major >= 55 && dev->minor >= 37)) { + rc = amd_pmc_idlemask_read(dev, NULL, s); + if (rc) + return rc; + } else { + seq_puts(s, "Unsupported SMU version for Idlemask\n"); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(amd_pmc_idlemask); + static void amd_pmc_dbgfs_unregister(struct amd_pmc_dev *dev) { debugfs_remove_recursive(dev->dbgfs_dir); @@ -213,6 +286,8 @@ static void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev) &smu_fw_info_fops); debugfs_create_file("s0ix_stats", 0644, dev->dbgfs_dir, dev, &s0ix_stats_fops); + debugfs_create_file("amd_pmc_idlemask", 0644, dev->dbgfs_dir, dev, + &amd_pmc_idlemask_fops); } #else static inline void amd_pmc_dbgfs_register(struct amd_pmc_dev *dev) @@ -264,7 +339,7 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev) dev_dbg(dev->dev, "AMD_PMC_REGISTER_MESSAGE:%x\n", value); } -static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, bool set, u32 *data, u8 msg, bool ret) +static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret) { int rc; u32 val; @@ -283,7 +358,7 @@ static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, bool set, u32 *data, u8 msg amd_pmc_reg_write(dev, AMD_PMC_REGISTER_RESPONSE, 0); /* Write argument into response register */ - amd_pmc_reg_write(dev, AMD_PMC_REGISTER_ARGUMENT, set); + amd_pmc_reg_write(dev, AMD_PMC_REGISTER_ARGUMENT, arg); /* Write message ID to message ID register */ amd_pmc_reg_write(dev, AMD_PMC_REGISTER_MESSAGE, msg); @@ -339,18 +414,73 @@ static int amd_pmc_get_os_hint(struct amd_pmc_dev *dev) return -EINVAL; } +static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg) +{ + struct rtc_device *rtc_device; + time64_t then, now, duration; + struct rtc_wkalrm alarm; + struct rtc_time tm; + int rc; + + if (pdev->major < 64 || (pdev->major == 64 && pdev->minor < 53)) + return 0; + + rtc_device = rtc_class_open("rtc0"); + if (!rtc_device) + return 0; + rc = rtc_read_alarm(rtc_device, &alarm); + if (rc) + return rc; + if (!alarm.enabled) { + dev_dbg(pdev->dev, "alarm not enabled\n"); + return 0; + } + rc = rtc_read_time(rtc_device, &tm); + if (rc) + return rc; + then = rtc_tm_to_time64(&alarm.time); + now = rtc_tm_to_time64(&tm); + duration = then-now; + + /* in the past */ + if (then < now) + return 0; + + /* will be stored in upper 16 bits of s0i3 hint argument, + * so timer wakeup from s0i3 is limited to ~18 hours or less + */ + if (duration <= 4 || duration > U16_MAX) + return -EINVAL; + + *arg |= (duration << 16); + rc = rtc_alarm_irq_enable(rtc_device, 0); + dev_dbg(pdev->dev, "wakeup timer programmed for %lld seconds\n", duration); + + return rc; +} + static int __maybe_unused amd_pmc_suspend(struct device *dev) { struct amd_pmc_dev *pdev = dev_get_drvdata(dev); int rc; u8 msg; + u32 arg = 1; /* Reset and Start SMU logging - to monitor the s0i3 stats */ amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_RESET, 0); amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_START, 0); + /* Activate CZN specific RTC functionality */ + if (pdev->cpu_id == AMD_CPU_ID_CZN) { + rc = amd_pmc_verify_czn_rtc(pdev, &arg); + if (rc < 0) + return rc; + } + + /* Dump the IdleMask before we send hint to SMU */ + amd_pmc_idlemask_read(pdev, dev, NULL); msg = amd_pmc_get_os_hint(pdev); - rc = amd_pmc_send_cmd(pdev, 1, NULL, msg, 0); + rc = amd_pmc_send_cmd(pdev, arg, NULL, msg, 0); if (rc) dev_err(pdev->dev, "suspend failed\n"); @@ -363,14 +493,17 @@ static int __maybe_unused amd_pmc_resume(struct device *dev) int rc; u8 msg; - /* Let SMU know that we are looking for stats */ - amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0); - msg = amd_pmc_get_os_hint(pdev); rc = amd_pmc_send_cmd(pdev, 0, NULL, msg, 0); if (rc) dev_err(pdev->dev, "resume failed\n"); + /* Let SMU know that we are looking for stats */ + amd_pmc_send_cmd(pdev, 0, NULL, SMU_MSG_LOG_DUMP_DATA, 0); + + /* Dump the IdleMask to see the blockers */ + amd_pmc_idlemask_read(pdev, dev, NULL); + return 0; } @@ -457,6 +590,7 @@ static int amd_pmc_probe(struct platform_device *pdev) if (err) dev_err(dev->dev, "SMU debugging info not supported on this platform\n"); + amd_pmc_get_smu_version(dev); platform_set_drvdata(pdev, dev); amd_pmc_dbgfs_register(dev); return 0; @@ -476,6 +610,7 @@ static const struct acpi_device_id amd_pmc_acpi_ids[] = { {"AMDI0006", 0}, {"AMDI0007", 0}, {"AMD0004", 0}, + {"AMD0005", 0}, { } }; MODULE_DEVICE_TABLE(acpi, amd_pmc_acpi_ids); diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index e14fb5fa7324..8f067ac4e952 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -2169,8 +2169,8 @@ static ssize_t throttle_thermal_policy_store(struct device *dev, static DEVICE_ATTR_RW(throttle_thermal_policy); /* Platform profile ***********************************************************/ -static int platform_profile_get(struct platform_profile_handler *pprof, - enum platform_profile_option *profile) +static int asus_wmi_platform_profile_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) { struct asus_wmi *asus; int tp; @@ -2196,8 +2196,8 @@ static int platform_profile_get(struct platform_profile_handler *pprof, return 0; } -static int platform_profile_set(struct platform_profile_handler *pprof, - enum platform_profile_option profile) +static int asus_wmi_platform_profile_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) { struct asus_wmi *asus; int tp; @@ -2236,8 +2236,8 @@ static int platform_profile_setup(struct asus_wmi *asus) dev_info(dev, "Using throttle_thermal_policy for platform_profile support\n"); - asus->platform_profile_handler.profile_get = platform_profile_get; - asus->platform_profile_handler.profile_set = platform_profile_set; + asus->platform_profile_handler.profile_get = asus_wmi_platform_profile_get; + asus->platform_profile_handler.profile_set = asus_wmi_platform_profile_set; set_bit(PLATFORM_PROFILE_QUIET, asus->platform_profile_handler.choices); set_bit(PLATFORM_PROFILE_BALANCED, diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c new file mode 100644 index 000000000000..f5c72e33f9ae --- /dev/null +++ b/drivers/platform/x86/barco-p50-gpio.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Support for EC-connected GPIOs for identify + * LED/button on Barco P50 board + * + * Copyright (C) 2021 Barco NV + * Author: Santosh Kumar Yadav <santoshkumar.yadav@barco.com> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/dmi.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio_keys.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/machine.h> +#include <linux/input.h> + + +#define DRIVER_NAME "barco-p50-gpio" + +/* GPIO lines */ +#define P50_GPIO_LINE_LED 0 +#define P50_GPIO_LINE_BTN 1 + +/* GPIO IO Ports */ +#define P50_GPIO_IO_PORT_BASE 0x299 + +#define P50_PORT_DATA 0x00 +#define P50_PORT_CMD 0x01 + +#define P50_STATUS_OBF 0x01 /* EC output buffer full */ +#define P50_STATUS_IBF 0x02 /* EC input buffer full */ + +#define P50_CMD_READ 0xa0 +#define P50_CMD_WRITE 0x50 + +/* EC mailbox registers */ +#define P50_MBOX_REG_CMD 0x00 +#define P50_MBOX_REG_STATUS 0x01 +#define P50_MBOX_REG_PARAM 0x02 +#define P50_MBOX_REG_DATA 0x03 + +#define P50_MBOX_CMD_READ_GPIO 0x11 +#define P50_MBOX_CMD_WRITE_GPIO 0x12 +#define P50_MBOX_CMD_CLEAR 0xff + +#define P50_MBOX_STATUS_SUCCESS 0x01 + +#define P50_MBOX_PARAM_LED 0x12 +#define P50_MBOX_PARAM_BTN 0x13 + + +struct p50_gpio { + struct gpio_chip gc; + struct mutex lock; + unsigned long base; + struct platform_device *leds_pdev; + struct platform_device *keys_pdev; +}; + +static struct platform_device *gpio_pdev; + +static int gpio_params[] = { + [P50_GPIO_LINE_LED] = P50_MBOX_PARAM_LED, + [P50_GPIO_LINE_BTN] = P50_MBOX_PARAM_BTN, +}; + +static const char * const gpio_names[] = { + [P50_GPIO_LINE_LED] = "identify-led", + [P50_GPIO_LINE_BTN] = "identify-button", +}; + + +static struct gpiod_lookup_table p50_gpio_led_table = { + .dev_id = "leds-gpio", + .table = { + GPIO_LOOKUP_IDX(DRIVER_NAME, P50_GPIO_LINE_LED, NULL, 0, GPIO_ACTIVE_HIGH), + {} + } +}; + +/* GPIO LEDs */ +static struct gpio_led leds[] = { + { .name = "identify" } +}; + +static struct gpio_led_platform_data leds_pdata = { + .num_leds = ARRAY_SIZE(leds), + .leds = leds, +}; + +/* GPIO keyboard */ +static struct gpio_keys_button buttons[] = { + { + .code = KEY_VENDOR, + .gpio = P50_GPIO_LINE_BTN, + .active_low = 1, + .type = EV_KEY, + .value = 1, + }, +}; + +static struct gpio_keys_platform_data keys_pdata = { + .buttons = buttons, + .nbuttons = ARRAY_SIZE(buttons), + .poll_interval = 100, + .rep = 0, + .name = "identify", +}; + + +/* low level access routines */ + +static int p50_wait_ec(struct p50_gpio *p50, int mask, int expected) +{ + int i, val; + + for (i = 0; i < 100; i++) { + val = inb(p50->base + P50_PORT_CMD) & mask; + if (val == expected) + return 0; + usleep_range(500, 2000); + } + + dev_err(p50->gc.parent, "Timed out waiting for EC (0x%x)\n", val); + return -ETIMEDOUT; +} + + +static int p50_read_mbox_reg(struct p50_gpio *p50, int reg) +{ + int ret; + + ret = p50_wait_ec(p50, P50_STATUS_IBF, 0); + if (ret) + return ret; + + /* clear output buffer flag, prevent unfinished commands */ + inb(p50->base + P50_PORT_DATA); + + /* cmd/address */ + outb(P50_CMD_READ | reg, p50->base + P50_PORT_CMD); + + ret = p50_wait_ec(p50, P50_STATUS_OBF, P50_STATUS_OBF); + if (ret) + return ret; + + return inb(p50->base + P50_PORT_DATA); +} + +static int p50_write_mbox_reg(struct p50_gpio *p50, int reg, int val) +{ + int ret; + + ret = p50_wait_ec(p50, P50_STATUS_IBF, 0); + if (ret) + return ret; + + /* cmd/address */ + outb(P50_CMD_WRITE | reg, p50->base + P50_PORT_CMD); + + ret = p50_wait_ec(p50, P50_STATUS_IBF, 0); + if (ret) + return ret; + + /* data */ + outb(val, p50->base + P50_PORT_DATA); + + return 0; +} + + +/* mbox routines */ + +static int p50_wait_mbox_idle(struct p50_gpio *p50) +{ + int i, val; + + for (i = 0; i < 1000; i++) { + val = p50_read_mbox_reg(p50, P50_MBOX_REG_CMD); + /* cmd is 0 when idle */ + if (val <= 0) + return val; + + usleep_range(500, 2000); + } + + dev_err(p50->gc.parent, "Timed out waiting for EC mbox idle (CMD: 0x%x)\n", val); + + return -ETIMEDOUT; +} + +static int p50_send_mbox_cmd(struct p50_gpio *p50, int cmd, int param, int data) +{ + int ret; + + ret = p50_wait_mbox_idle(p50); + if (ret) + return ret; + + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_DATA, data); + if (ret) + return ret; + + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_PARAM, param); + if (ret) + return ret; + + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, cmd); + if (ret) + return ret; + + ret = p50_wait_mbox_idle(p50); + if (ret) + return ret; + + ret = p50_read_mbox_reg(p50, P50_MBOX_REG_STATUS); + if (ret < 0) + return ret; + + if (ret == P50_MBOX_STATUS_SUCCESS) + return 0; + + dev_err(p50->gc.parent, "Mbox command failed (CMD=0x%x STAT=0x%x PARAM=0x%x DATA=0x%x)\n", + cmd, ret, param, data); + + return -EIO; +} + + +/* gpio routines */ + +static int p50_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + switch (offset) { + case P50_GPIO_LINE_BTN: + return GPIO_LINE_DIRECTION_IN; + + case P50_GPIO_LINE_LED: + return GPIO_LINE_DIRECTION_OUT; + + default: + return -EINVAL; + } +} + +static int p50_gpio_get(struct gpio_chip *gc, unsigned int offset) +{ + struct p50_gpio *p50 = gpiochip_get_data(gc); + int ret; + + mutex_lock(&p50->lock); + + ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_READ_GPIO, gpio_params[offset], 0); + if (ret == 0) + ret = p50_read_mbox_reg(p50, P50_MBOX_REG_DATA); + + mutex_unlock(&p50->lock); + + return ret; +} + +static void p50_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct p50_gpio *p50 = gpiochip_get_data(gc); + + mutex_lock(&p50->lock); + + p50_send_mbox_cmd(p50, P50_MBOX_CMD_WRITE_GPIO, gpio_params[offset], value); + + mutex_unlock(&p50->lock); +} + +static int p50_gpio_probe(struct platform_device *pdev) +{ + struct p50_gpio *p50; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "Cannot get I/O ports\n"); + return -ENODEV; + } + + if (!devm_request_region(&pdev->dev, res->start, resource_size(res), pdev->name)) { + dev_err(&pdev->dev, "Unable to reserve I/O region\n"); + return -EBUSY; + } + + p50 = devm_kzalloc(&pdev->dev, sizeof(*p50), GFP_KERNEL); + if (!p50) + return -ENOMEM; + + platform_set_drvdata(pdev, p50); + mutex_init(&p50->lock); + p50->base = res->start; + p50->gc.owner = THIS_MODULE; + p50->gc.parent = &pdev->dev; + p50->gc.label = dev_name(&pdev->dev); + p50->gc.ngpio = ARRAY_SIZE(gpio_names); + p50->gc.names = gpio_names; + p50->gc.can_sleep = true; + p50->gc.base = -1; + p50->gc.get_direction = p50_gpio_get_direction; + p50->gc.get = p50_gpio_get; + p50->gc.set = p50_gpio_set; + + + /* reset mbox */ + ret = p50_wait_mbox_idle(p50); + if (ret) + return ret; + + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, P50_MBOX_CMD_CLEAR); + if (ret) + return ret; + + ret = p50_wait_mbox_idle(p50); + if (ret) + return ret; + + + ret = devm_gpiochip_add_data(&pdev->dev, &p50->gc, p50); + if (ret < 0) { + dev_err(&pdev->dev, "Could not register gpiochip: %d\n", ret); + return ret; + } + + gpiod_add_lookup_table(&p50_gpio_led_table); + + p50->leds_pdev = platform_device_register_data(&pdev->dev, + "leds-gpio", PLATFORM_DEVID_NONE, &leds_pdata, sizeof(leds_pdata)); + + if (IS_ERR(p50->leds_pdev)) { + ret = PTR_ERR(p50->leds_pdev); + dev_err(&pdev->dev, "Could not register leds-gpio: %d\n", ret); + goto err_leds; + } + + /* gpio-keys-polled uses old-style gpio interface, pass the right identifier */ + buttons[0].gpio += p50->gc.base; + + p50->keys_pdev = + platform_device_register_data(&pdev->dev, "gpio-keys-polled", + PLATFORM_DEVID_NONE, + &keys_pdata, sizeof(keys_pdata)); + + if (IS_ERR(p50->keys_pdev)) { + ret = PTR_ERR(p50->keys_pdev); + dev_err(&pdev->dev, "Could not register gpio-keys-polled: %d\n", ret); + goto err_keys; + } + + return 0; + +err_keys: + platform_device_unregister(p50->leds_pdev); +err_leds: + gpiod_remove_lookup_table(&p50_gpio_led_table); + + return ret; +} + +static int p50_gpio_remove(struct platform_device *pdev) +{ + struct p50_gpio *p50 = platform_get_drvdata(pdev); + + platform_device_unregister(p50->keys_pdev); + platform_device_unregister(p50->leds_pdev); + + gpiod_remove_lookup_table(&p50_gpio_led_table); + + return 0; +} + +static struct platform_driver p50_gpio_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = p50_gpio_probe, + .remove = p50_gpio_remove, +}; + +/* Board setup */ +static const struct dmi_system_id dmi_ids[] __initconst = { + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Barco"), + DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "P50") + }, + }, + {} +}; +MODULE_DEVICE_TABLE(dmi, dmi_ids); + +static int __init p50_module_init(void) +{ + struct resource res = DEFINE_RES_IO(P50_GPIO_IO_PORT_BASE, P50_PORT_CMD + 1); + + if (!dmi_first_match(dmi_ids)) + return -ENODEV; + + platform_driver_register(&p50_gpio_driver); + + gpio_pdev = platform_device_register_simple(DRIVER_NAME, PLATFORM_DEVID_NONE, &res, 1); + if (IS_ERR(gpio_pdev)) { + pr_err("failed registering %s: %ld\n", DRIVER_NAME, PTR_ERR(gpio_pdev)); + platform_driver_unregister(&p50_gpio_driver); + return PTR_ERR(gpio_pdev); + } + + return 0; +} + +static void __exit p50_module_exit(void) +{ + platform_device_unregister(gpio_pdev); + platform_driver_unregister(&p50_gpio_driver); +} + +module_init(p50_module_init); +module_exit(p50_module_exit); + +MODULE_AUTHOR("Santosh Kumar Yadav, Barco NV <santoshkumar.yadav@barco.com>"); +MODULE_DESCRIPTION("Barco P50 identify GPIOs driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index 821aba31821c..2fffa57e596e 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -166,8 +166,8 @@ config DELL_WMI config DELL_WMI_PRIVACY bool "Dell WMI Hardware Privacy Support" + depends on LEDS_TRIGGER_AUDIO = y || DELL_WMI = LEDS_TRIGGER_AUDIO depends on DELL_WMI - depends on LEDS_TRIGGER_AUDIO help This option adds integration with the "Dell Hardware Privacy" feature of Dell laptops to the dell-wmi driver. diff --git a/drivers/platform/x86/dell/dell-wmi-base.c b/drivers/platform/x86/dell/dell-wmi-base.c index 089c125e18f7..e07d3ba85a3f 100644 --- a/drivers/platform/x86/dell/dell-wmi-base.c +++ b/drivers/platform/x86/dell/dell-wmi-base.c @@ -40,6 +40,7 @@ static bool wmi_requires_smbios_request; struct dell_wmi_priv { struct input_dev *input_dev; + struct input_dev *tabletswitch_dev; u32 interface_version; }; @@ -309,6 +310,9 @@ static const struct key_entry dell_wmi_keymap_type_0010[] = { * Keymap for WMI events of type 0x0011 */ static const struct key_entry dell_wmi_keymap_type_0011[] = { + /* Reflex keyboard switch on 2n1 devices */ + { KE_IGNORE, 0xe070, { KEY_RESERVED } }, + /* Battery unplugged */ { KE_IGNORE, 0xfff0, { KEY_RESERVED } }, @@ -340,21 +344,55 @@ static const struct key_entry dell_wmi_keymap_type_0011[] = { * They are events with extended data */ static const struct key_entry dell_wmi_keymap_type_0012[] = { + /* Ultra-performance mode switch request */ + { KE_IGNORE, 0x000d, { KEY_RESERVED } }, + /* Fn-lock button pressed */ { KE_IGNORE, 0xe035, { KEY_RESERVED } }, }; -static void dell_wmi_process_key(struct wmi_device *wdev, int type, int code) +static void dell_wmi_switch_event(struct input_dev **subdev, + const char *devname, + int switchid, + int value) +{ + if (!*subdev) { + struct input_dev *dev = input_allocate_device(); + + if (!dev) { + pr_warn("could not allocate device for %s\n", devname); + return; + } + __set_bit(EV_SW, (dev)->evbit); + __set_bit(switchid, (dev)->swbit); + + (dev)->name = devname; + (dev)->id.bustype = BUS_HOST; + if (input_register_device(dev)) { + input_free_device(dev); + pr_warn("could not register device for %s\n", devname); + return; + } + *subdev = dev; + } + + input_report_switch(*subdev, switchid, value); + input_sync(*subdev); +} + +static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, u16 *buffer, int remaining) { struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); const struct key_entry *key; + int used = 0; + int value = 1; key = sparse_keymap_entry_from_scancode(priv->input_dev, (type << 16) | code); if (!key) { pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n", type, code); - return; + return 0; } pr_debug("Key with type 0x%04x and code 0x%04x pressed\n", type, code); @@ -363,16 +401,27 @@ static void dell_wmi_process_key(struct wmi_device *wdev, int type, int code) if ((key->keycode == KEY_BRIGHTNESSUP || key->keycode == KEY_BRIGHTNESSDOWN) && acpi_video_handles_brightness_key_presses()) - return; + return 0; if (type == 0x0000 && code == 0xe025 && !wmi_requires_smbios_request) - return; + return 0; - if (key->keycode == KEY_KBDILLUMTOGGLE) + if (key->keycode == KEY_KBDILLUMTOGGLE) { dell_laptop_call_notifier( DELL_LAPTOP_KBD_BACKLIGHT_BRIGHTNESS_CHANGED, NULL); + } else if (type == 0x0011 && code == 0xe070 && remaining > 0) { + dell_wmi_switch_event(&priv->tabletswitch_dev, + "Dell tablet mode switch", + SW_TABLET_MODE, !buffer[0]); + return 1; + } else if (type == 0x0012 && code == 0x000d && remaining > 0) { + value = (buffer[2] == 2); + used = 1; + } - sparse_keymap_report_entry(priv->input_dev, key, 1, true); + sparse_keymap_report_entry(priv->input_dev, key, value, true); + + return used; } static void dell_wmi_notify(struct wmi_device *wdev, @@ -430,21 +479,26 @@ static void dell_wmi_notify(struct wmi_device *wdev, case 0x0000: /* One key pressed or event occurred */ if (len > 2) dell_wmi_process_key(wdev, buffer_entry[1], - buffer_entry[2]); + buffer_entry[2], + buffer_entry + 3, + len - 3); /* Extended data is currently ignored */ break; case 0x0010: /* Sequence of keys pressed */ case 0x0011: /* Sequence of events occurred */ for (i = 2; i < len; ++i) - dell_wmi_process_key(wdev, buffer_entry[1], - buffer_entry[i]); + i += dell_wmi_process_key(wdev, buffer_entry[1], + buffer_entry[i], + buffer_entry + i, + len - i - 1); break; case 0x0012: if ((len > 4) && dell_privacy_process_event(buffer_entry[1], buffer_entry[3], buffer_entry[4])) /* dell_privacy_process_event has handled the event */; else if (len > 2) - dell_wmi_process_key(wdev, buffer_entry[1], buffer_entry[2]); + dell_wmi_process_key(wdev, buffer_entry[1], buffer_entry[2], + buffer_entry + 3, len - 3); break; default: /* Unknown event */ pr_info("Unknown WMI event type 0x%x\n", @@ -661,6 +715,8 @@ static void dell_wmi_input_destroy(struct wmi_device *wdev) struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); input_unregister_device(priv->input_dev); + if (priv->tabletswitch_dev) + input_unregister_device(priv->tabletswitch_dev); } /* diff --git a/drivers/platform/x86/gigabyte-wmi.c b/drivers/platform/x86/gigabyte-wmi.c index 7f3a03f937f6..658bab4b7964 100644 --- a/drivers/platform/x86/gigabyte-wmi.c +++ b/drivers/platform/x86/gigabyte-wmi.c @@ -141,9 +141,11 @@ static u8 gigabyte_wmi_detect_sensor_usability(struct wmi_device *wdev) static const struct dmi_system_id gigabyte_wmi_known_working_platforms[] = { DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B450M S2H V2"), + DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 AORUS ELITE AX V2"), DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 AORUS ELITE"), DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 AORUS ELITE V2"), DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 GAMING X V2"), + DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550I AORUS PRO AX"), DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550M AORUS PRO-P"), DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550M DS3H"), DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("Z390 I AORUS PRO WIFI-CF"), diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 027a1467d009..48a46466f086 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -22,9 +22,11 @@ #include <linux/input/sparse-keymap.h> #include <linux/platform_device.h> #include <linux/platform_profile.h> +#include <linux/hwmon.h> #include <linux/acpi.h> #include <linux/rfkill.h> #include <linux/string.h> +#include <linux/dmi.h> MODULE_AUTHOR("Matthew Garrett <mjg59@srcf.ucam.org>"); MODULE_DESCRIPTION("HP laptop WMI hotkeys driver"); @@ -39,6 +41,25 @@ MODULE_PARM_DESC(enable_tablet_mode_sw, "Enable SW_TABLET_MODE reporting (-1=aut #define HPWMI_EVENT_GUID "95F24279-4D7B-4334-9387-ACCDC67EF61C" #define HPWMI_BIOS_GUID "5FB7F034-2C63-45e9-BE91-3D44E2C707E4" +#define HP_OMEN_EC_THERMAL_PROFILE_OFFSET 0x95 + +/* DMI board names of devices that should use the omen specific path for + * thermal profiles. + * This was obtained by taking a look in the windows omen command center + * app and parsing a json file that they use to figure out what capabilities + * the device should have. + * A device is considered an omen if the DisplayName in that list contains + * "OMEN", and it can use the thermal profile stuff if the "Feature" array + * contains "PerformanceControl". + */ +static const char * const omen_thermal_profile_boards[] = { + "84DA", "84DB", "84DC", "8574", "8575", "860A", "87B5", "8572", "8573", + "8600", "8601", "8602", "8605", "8606", "8607", "8746", "8747", "8749", + "874A", "8603", "8604", "8748", "886B", "886C", "878A", "878B", "878C", + "88C8", "88CB", "8786", "8787", "8788", "88D1", "88D2", "88F4", "88FD", + "88F5", "88F6", "88F7", "88FE", "88FF", "8900", "8901", "8902", "8912", + "8917", "8918", "8949", "894A", "89EB" +}; enum hp_wmi_radio { HPWMI_WIFI = 0x0, @@ -89,10 +110,18 @@ enum hp_wmi_commandtype { HPWMI_THERMAL_PROFILE_QUERY = 0x4c, }; +enum hp_wmi_gm_commandtype { + HPWMI_FAN_SPEED_GET_QUERY = 0x11, + HPWMI_SET_PERFORMANCE_MODE = 0x1A, + HPWMI_FAN_SPEED_MAX_GET_QUERY = 0x26, + HPWMI_FAN_SPEED_MAX_SET_QUERY = 0x27, +}; + enum hp_wmi_command { HPWMI_READ = 0x01, HPWMI_WRITE = 0x02, HPWMI_ODM = 0x03, + HPWMI_GM = 0x20008, }; enum hp_wmi_hardware_mask { @@ -120,6 +149,12 @@ enum hp_wireless2_bits { HPWMI_POWER_FW_OR_HW = HPWMI_POWER_BIOS | HPWMI_POWER_HARD, }; +enum hp_thermal_profile_omen { + HP_OMEN_THERMAL_PROFILE_DEFAULT = 0x00, + HP_OMEN_THERMAL_PROFILE_PERFORMANCE = 0x01, + HP_OMEN_THERMAL_PROFILE_COOL = 0x02, +}; + enum hp_thermal_profile { HP_THERMAL_PROFILE_PERFORMANCE = 0x00, HP_THERMAL_PROFILE_DEFAULT = 0x01, @@ -279,6 +314,24 @@ out_free: return ret; } +static int hp_wmi_get_fan_speed(int fan) +{ + u8 fsh, fsl; + char fan_data[4] = { fan, 0, 0, 0 }; + + int ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_GET_QUERY, HPWMI_GM, + &fan_data, sizeof(fan_data), + sizeof(fan_data)); + + if (ret != 0) + return -EINVAL; + + fsh = fan_data[2]; + fsl = fan_data[3]; + + return (fsh << 8) | fsl; +} + static int hp_wmi_read_int(int query) { int val = 0, ret; @@ -302,6 +355,73 @@ static int hp_wmi_hw_state(int mask) return !!(state & mask); } +static int omen_thermal_profile_set(int mode) +{ + char buffer[2] = {0, mode}; + int ret; + + if (mode < 0 || mode > 2) + return -EINVAL; + + ret = hp_wmi_perform_query(HPWMI_SET_PERFORMANCE_MODE, HPWMI_GM, + &buffer, sizeof(buffer), sizeof(buffer)); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return mode; +} + +static bool is_omen_thermal_profile(void) +{ + const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); + + if (!board_name) + return false; + + return match_string(omen_thermal_profile_boards, + ARRAY_SIZE(omen_thermal_profile_boards), + board_name) >= 0; +} + +static int omen_thermal_profile_get(void) +{ + u8 data; + + int ret = ec_read(HP_OMEN_EC_THERMAL_PROFILE_OFFSET, &data); + + if (ret) + return ret; + + return data; +} + +static int hp_wmi_fan_speed_max_set(int enabled) +{ + int ret; + + ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_SET_QUERY, HPWMI_GM, + &enabled, sizeof(enabled), sizeof(enabled)); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return enabled; +} + +static int hp_wmi_fan_speed_max_get(void) +{ + int val = 0, ret; + + ret = hp_wmi_perform_query(HPWMI_FAN_SPEED_MAX_GET_QUERY, HPWMI_GM, + &val, sizeof(val), sizeof(val)); + + if (ret) + return ret < 0 ? ret : -EINVAL; + + return val; +} + static int __init hp_wmi_bios_2008_later(void) { int state = 0; @@ -878,6 +998,58 @@ fail: return err; } +static int platform_profile_omen_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) +{ + int tp; + + tp = omen_thermal_profile_get(); + if (tp < 0) + return tp; + + switch (tp) { + case HP_OMEN_THERMAL_PROFILE_PERFORMANCE: + *profile = PLATFORM_PROFILE_PERFORMANCE; + break; + case HP_OMEN_THERMAL_PROFILE_DEFAULT: + *profile = PLATFORM_PROFILE_BALANCED; + break; + case HP_OMEN_THERMAL_PROFILE_COOL: + *profile = PLATFORM_PROFILE_COOL; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int platform_profile_omen_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) +{ + int err, tp; + + switch (profile) { + case PLATFORM_PROFILE_PERFORMANCE: + tp = HP_OMEN_THERMAL_PROFILE_PERFORMANCE; + break; + case PLATFORM_PROFILE_BALANCED: + tp = HP_OMEN_THERMAL_PROFILE_DEFAULT; + break; + case PLATFORM_PROFILE_COOL: + tp = HP_OMEN_THERMAL_PROFILE_COOL; + break; + default: + return -EOPNOTSUPP; + } + + err = omen_thermal_profile_set(tp); + if (err < 0) + return err; + + return 0; +} + static int thermal_profile_get(void) { return hp_wmi_read_int(HPWMI_THERMAL_PROFILE_QUERY); @@ -889,8 +1061,8 @@ static int thermal_profile_set(int thermal_profile) sizeof(thermal_profile), 0); } -static int platform_profile_get(struct platform_profile_handler *pprof, - enum platform_profile_option *profile) +static int hp_wmi_platform_profile_get(struct platform_profile_handler *pprof, + enum platform_profile_option *profile) { int tp; @@ -915,8 +1087,8 @@ static int platform_profile_get(struct platform_profile_handler *pprof, return 0; } -static int platform_profile_set(struct platform_profile_handler *pprof, - enum platform_profile_option profile) +static int hp_wmi_platform_profile_set(struct platform_profile_handler *pprof, + enum platform_profile_option profile) { int err, tp; @@ -945,20 +1117,39 @@ static int thermal_profile_setup(void) { int err, tp; - tp = thermal_profile_get(); - if (tp < 0) - return tp; + if (is_omen_thermal_profile()) { + tp = omen_thermal_profile_get(); + if (tp < 0) + return tp; - /* - * call thermal profile write command to ensure that the firmware correctly - * sets the OEM variables for the DPTF - */ - err = thermal_profile_set(tp); - if (err) - return err; + /* + * call thermal profile write command to ensure that the + * firmware correctly sets the OEM variables + */ + + err = omen_thermal_profile_set(tp); + if (err < 0) + return err; - platform_profile_handler.profile_get = platform_profile_get, - platform_profile_handler.profile_set = platform_profile_set, + platform_profile_handler.profile_get = platform_profile_omen_get; + platform_profile_handler.profile_set = platform_profile_omen_set; + } else { + tp = thermal_profile_get(); + + if (tp < 0) + return tp; + + /* + * call thermal profile write command to ensure that the + * firmware correctly sets the OEM variables for the DPTF + */ + err = thermal_profile_set(tp); + if (err) + return err; + + platform_profile_handler.profile_get = hp_wmi_platform_profile_get; + platform_profile_handler.profile_set = hp_wmi_platform_profile_set; + } set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices); set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices); @@ -973,8 +1164,11 @@ static int thermal_profile_setup(void) return 0; } +static int hp_wmi_hwmon_init(void); + static int __init hp_wmi_bios_setup(struct platform_device *device) { + int err; /* clear detected rfkill devices */ wifi_rfkill = NULL; bluetooth_rfkill = NULL; @@ -984,6 +1178,11 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) if (hp_wmi_rfkill_setup(device)) hp_wmi_rfkill2_setup(device); + err = hp_wmi_hwmon_init(); + + if (err < 0) + return err; + thermal_profile_setup(); return 0; @@ -1068,6 +1267,112 @@ static struct platform_driver hp_wmi_driver = { .remove = __exit_p(hp_wmi_bios_remove), }; +static umode_t hp_wmi_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_pwm: + return 0644; + case hwmon_fan: + if (hp_wmi_get_fan_speed(channel) >= 0) + return 0444; + break; + default: + return 0; + } + + return 0; +} + +static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + int ret; + + switch (type) { + case hwmon_fan: + ret = hp_wmi_get_fan_speed(channel); + + if (ret < 0) + return ret; + *val = ret; + return 0; + case hwmon_pwm: + switch (hp_wmi_fan_speed_max_get()) { + case 0: + /* 0 is automatic fan, which is 2 for hwmon */ + *val = 2; + return 0; + case 1: + /* 1 is max fan, which is 0 + * (no fan speed control) for hwmon + */ + *val = 0; + return 0; + default: + /* shouldn't happen */ + return -ENODATA; + } + default: + return -EINVAL; + } +} + +static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_pwm: + switch (val) { + case 0: + /* 0 is no fan speed control (max), which is 1 for us */ + return hp_wmi_fan_speed_max_set(1); + case 2: + /* 2 is automatic speed control, which is 0 for us */ + return hp_wmi_fan_speed_max_set(0); + default: + /* we don't support manual fan speed control */ + return -EINVAL; + } + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_channel_info *info[] = { + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT), + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE), + NULL +}; + +static const struct hwmon_ops ops = { + .is_visible = hp_wmi_hwmon_is_visible, + .read = hp_wmi_hwmon_read, + .write = hp_wmi_hwmon_write, +}; + +static const struct hwmon_chip_info chip_info = { + .ops = &ops, + .info = info, +}; + +static int hp_wmi_hwmon_init(void) +{ + struct device *dev = &hp_wmi_platform_dev->dev; + struct device *hwmon; + + hwmon = devm_hwmon_device_register_with_info(dev, "hp", &hp_wmi_driver, + &chip_info, NULL); + + if (IS_ERR(hwmon)) { + dev_err(dev, "Could not register hp hwmon device\n"); + return PTR_ERR(hwmon); + } + + return 0; +} + static int __init hp_wmi_init(void) { int event_capable = wmi_has_guid(HPWMI_EVENT_GUID); diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index cc53f725c041..b183967ecfb7 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -349,7 +349,8 @@ static int lis3lv02d_remove(struct platform_device *device) led_classdev_unregister(&hpled_led.led_classdev); flush_work(&hpled_led.work); - return lis3lv02d_remove_fs(&lis3_dev); + lis3lv02d_remove_fs(&lis3_dev); + return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/platform/x86/i2c-multi-instantiate.c b/drivers/platform/x86/i2c-multi-instantiate.c index a50153ecd560..4956a1df5b90 100644 --- a/drivers/platform/x86/i2c-multi-instantiate.c +++ b/drivers/platform/x86/i2c-multi-instantiate.c @@ -139,29 +139,13 @@ static const struct i2c_inst_data bsg2150_data[] = { {} }; -/* - * Device with _HID INT3515 (TI PD controllers) has some unresolved interrupt - * issues. The most common problem seen is interrupt flood. - * - * There are at least two known causes. Firstly, on some boards, the - * I2CSerialBus resource index does not match the Interrupt resource, i.e. they - * are not one-to-one mapped like in the array below. Secondly, on some boards - * the IRQ line from the PD controller is not actually connected at all. But the - * interrupt flood is also seen on some boards where those are not a problem, so - * there are some other problems as well. - * - * Because of the issues with the interrupt, the device is disabled for now. If - * you wish to debug the issues, uncomment the below, and add an entry for the - * INT3515 device to the i2c_multi_instance_ids table. - * - * static const struct i2c_inst_data int3515_data[] = { - * { "tps6598x", IRQ_RESOURCE_APIC, 0 }, - * { "tps6598x", IRQ_RESOURCE_APIC, 1 }, - * { "tps6598x", IRQ_RESOURCE_APIC, 2 }, - * { "tps6598x", IRQ_RESOURCE_APIC, 3 }, - * { } - * }; - */ +static const struct i2c_inst_data int3515_data[] = { + { "tps6598x", IRQ_RESOURCE_APIC, 0 }, + { "tps6598x", IRQ_RESOURCE_APIC, 1 }, + { "tps6598x", IRQ_RESOURCE_APIC, 2 }, + { "tps6598x", IRQ_RESOURCE_APIC, 3 }, + {} +}; /* * Note new device-ids must also be added to i2c_multi_instantiate_ids in @@ -170,6 +154,7 @@ static const struct i2c_inst_data bsg2150_data[] = { static const struct acpi_device_id i2c_multi_inst_acpi_ids[] = { { "BSG1160", (unsigned long)bsg1160_data }, { "BSG2150", (unsigned long)bsg2150_data }, + { "INT3515", (unsigned long)int3515_data }, { } }; MODULE_DEVICE_TABLE(acpi, i2c_multi_inst_acpi_ids); diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index e7a1299e3776..3ccb7b71dfb1 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -868,6 +868,18 @@ static void dytc_profile_refresh(struct ideapad_private *priv) } } +static const struct dmi_system_id ideapad_dytc_v4_allow_table[] = { + { + /* Ideapad 5 Pro 16ACH6 */ + .ident = "LENOVO 82L5", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "82L5") + } + }, + {} +}; + static int ideapad_dytc_profile_init(struct ideapad_private *priv) { int err, dytc_version; @@ -882,12 +894,21 @@ static int ideapad_dytc_profile_init(struct ideapad_private *priv) return err; /* Check DYTC is enabled and supports mode setting */ - if (!test_bit(DYTC_QUERY_ENABLE_BIT, &output)) + if (!test_bit(DYTC_QUERY_ENABLE_BIT, &output)) { + dev_info(&priv->platform_device->dev, "DYTC_QUERY_ENABLE_BIT returned false\n"); return -ENODEV; + } dytc_version = (output >> DYTC_QUERY_REV_BIT) & 0xF; - if (dytc_version < 5) - return -ENODEV; + + if (dytc_version < 5) { + if (dytc_version < 4 || !dmi_check_system(ideapad_dytc_v4_allow_table)) { + dev_info(&priv->platform_device->dev, + "DYTC_VERSION is less than 4 or is not allowed: %d\n", + dytc_version); + return -ENODEV; + } + } priv->dytc = kzalloc(sizeof(*priv->dytc), GFP_KERNEL); if (!priv->dytc) @@ -1534,17 +1555,13 @@ static void ideapad_check_features(struct ideapad_private *priv) static int ideapad_acpi_add(struct platform_device *pdev) { + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); struct ideapad_private *priv; - struct acpi_device *adev; acpi_status status; unsigned long cfg; int err, i; - err = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev); - if (err) - return -ENODEV; - - if (eval_int(adev->handle, "_CFG", &cfg)) + if (!adev || eval_int(adev->handle, "_CFG", &cfg)) return -ENODEV; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index 0b21468e1bd0..38ce3e344589 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -102,6 +102,22 @@ config INTEL_CHTDC_TI_PWRBTN To compile this driver as a module, choose M here: the module will be called intel_chtdc_ti_pwrbtn. +config INTEL_ISHTP_ECLITE + tristate "Intel ISHTP eclite controller Driver" + depends on INTEL_ISH_HID + depends on ACPI + help + This driver is for accessing the PSE (Programmable Service Engine) - + an Embedded Controller like IP - using ISHTP (Integrated Sensor Hub + Transport Protocol) to get battery, thermal and UCSI (USB Type-C + Connector System Software Interface) related data from the platform. + Users who don't want to use discrete Embedded Controller on Intel's + Elkhartlake platform can leverage this integrated solution of + ECLite which is part of PSE subsystem. + + To compile this driver as a module, choose M here: the module + will be called intel_ishtp_eclite. + config INTEL_MRFLD_PWRBTN tristate "Intel Merrifield Basin Cove power button driver" depends on INTEL_SOC_PMIC_MRFLD diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile index 8b3a3f7bab49..7c24be2423d8 100644 --- a/drivers/platform/x86/intel/Makefile +++ b/drivers/platform/x86/intel/Makefile @@ -21,6 +21,7 @@ intel-vbtn-y := vbtn.o obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o # Intel miscellaneous drivers +obj-$(CONFIG_INTEL_ISHTP_ECLITE) += ishtp_eclite.o intel_int0002_vgpio-y := int0002_vgpio.o obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o intel_oaktrail-y := oaktrail.o diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index a33a5826e81a..08598942a6d7 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -118,12 +118,30 @@ static const struct dmi_system_id dmi_vgbs_allow_list[] = { { } }; +/* + * Some devices, even non convertible ones, can send incorrect SW_TABLET_MODE + * reports. Accept such reports only from devices in this list. + */ +static const struct dmi_system_id dmi_auto_add_switch[] = { + { + .matches = { + DMI_EXACT_MATCH(DMI_CHASSIS_TYPE, "31" /* Convertible */), + }, + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_CHASSIS_TYPE, "32" /* Detachable */), + }, + }, + {} /* Array terminator */ +}; + struct intel_hid_priv { struct input_dev *input_dev; struct input_dev *array; struct input_dev *switches; bool wakeup_mode; - bool dual_accel; + bool auto_add_switch; }; #define HID_EVENT_FILTER_UUID "eeec56b3-4442-408f-a792-4edd4d758054" @@ -452,10 +470,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) * Some convertible have unreliable VGBS return which could cause incorrect * SW_TABLET_MODE report, in these cases we enable support when receiving * the first event instead of during driver setup. - * - * See dual_accel_detect.h for more info on the dual_accel check. */ - if (!priv->switches && !priv->dual_accel && (event == 0xcc || event == 0xcd)) { + if (!priv->switches && priv->auto_add_switch && (event == 0xcc || event == 0xcd)) { dev_info(&device->dev, "switch event received, enable switches supports\n"); err = intel_hid_switches_setup(device); if (err) @@ -596,7 +612,8 @@ static int intel_hid_probe(struct platform_device *device) return -ENOMEM; dev_set_drvdata(&device->dev, priv); - priv->dual_accel = dual_accel_detect(); + /* See dual_accel_detect.h for more info on the dual_accel check. */ + priv->auto_add_switch = dmi_check_system(dmi_auto_add_switch) && !dual_accel_detect(); err = intel_hid_input_setup(device); if (err) { diff --git a/drivers/platform/x86/intel/int0002_vgpio.c b/drivers/platform/x86/intel/int0002_vgpio.c index 569342aa8926..617dbf98980e 100644 --- a/drivers/platform/x86/intel/int0002_vgpio.c +++ b/drivers/platform/x86/intel/int0002_vgpio.c @@ -34,13 +34,11 @@ #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/platform_data/x86/soc.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/suspend.h> -#include <asm/cpu_device_id.h> -#include <asm/intel-family.h> - #define DRV_NAME "INT0002 Virtual GPIO" /* For some reason the virtual GPIO pin tied to the GPE is numbered pin 2 */ @@ -151,12 +149,6 @@ static struct irq_chip int0002_irqchip = { .irq_set_wake = int0002_irq_set_wake, }; -static const struct x86_cpu_id int0002_cpu_ids[] = { - X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT, NULL), - X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT, NULL), - {} -}; - static void int0002_init_irq_valid_mask(struct gpio_chip *chip, unsigned long *valid_mask, unsigned int ngpios) @@ -167,15 +159,13 @@ static void int0002_init_irq_valid_mask(struct gpio_chip *chip, static int int0002_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct x86_cpu_id *cpu_id; struct int0002_data *int0002; struct gpio_irq_chip *girq; struct gpio_chip *chip; int irq, ret; /* Menlow has a different INT0002 device? <sigh> */ - cpu_id = x86_match_cpu(int0002_cpu_ids); - if (!cpu_id) + if (!soc_intel_is_byt() && !soc_intel_is_cht()) return -ENODEV; irq = platform_get_irq(pdev, 0); diff --git a/drivers/platform/x86/intel/int1092/intel_sar.c b/drivers/platform/x86/intel/int1092/intel_sar.c index 379560fe5df9..e03943e6380a 100644 --- a/drivers/platform/x86/intel/int1092/intel_sar.c +++ b/drivers/platform/x86/intel/int1092/intel_sar.c @@ -42,12 +42,20 @@ static void update_sar_data(struct wwan_sar_context *context) if (config->device_mode_info && context->sar_data.device_mode < config->total_dev_mode) { - struct wwan_device_mode_info *dev_mode = - &config->device_mode_info[context->sar_data.device_mode]; - - context->sar_data.antennatable_index = dev_mode->antennatable_index; - context->sar_data.bandtable_index = dev_mode->bandtable_index; - context->sar_data.sartable_index = dev_mode->sartable_index; + int itr = 0; + + for (itr = 0; itr < config->total_dev_mode; itr++) { + if (context->sar_data.device_mode == + config->device_mode_info[itr].device_mode) { + struct wwan_device_mode_info *dev_mode = + &config->device_mode_info[itr]; + + context->sar_data.antennatable_index = dev_mode->antennatable_index; + context->sar_data.bandtable_index = dev_mode->bandtable_index; + context->sar_data.sartable_index = dev_mode->sartable_index; + break; + } + } } } @@ -305,7 +313,6 @@ static struct platform_driver sar_driver = { .remove = sar_remove, .driver = { .name = DRVNAME, - .owner = THIS_MODULE, .acpi_match_table = ACPI_PTR(sar_device_ids) } }; @@ -313,4 +320,4 @@ module_platform_driver(sar_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Platform device driver for INTEL MODEM BIOS SAR"); -MODULE_AUTHOR("Shravan S <s.shravan@intel.com>"); +MODULE_AUTHOR("Shravan Sudhakar <s.shravan@intel.com>"); diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c b/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c index 9fe0a2527e1c..e59d79c7e82f 100644 --- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c +++ b/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c @@ -401,7 +401,7 @@ int skl_int3472_discrete_remove(struct platform_device *pdev) gpiod_remove_lookup_table(&int3472->gpios); - if (int3472->clock.ena_gpio) + if (int3472->clock.cl) skl_int3472_unregister_clock(int3472); gpiod_put(int3472->clock.ena_gpio); diff --git a/drivers/platform/x86/intel/ishtp_eclite.c b/drivers/platform/x86/intel/ishtp_eclite.c new file mode 100644 index 000000000000..12fc98a48657 --- /dev/null +++ b/drivers/platform/x86/intel/ishtp_eclite.c @@ -0,0 +1,701 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel ECLite opregion driver for talking to ECLite firmware running on + * Intel Integrated Sensor Hub (ISH) using ISH Transport Protocol (ISHTP) + * + * Copyright (c) 2021, Intel Corporation. + */ + +#include <linux/acpi.h> +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/intel-ish-client-if.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/suspend.h> +#include <linux/types.h> +#include <linux/uuid.h> +#include <linux/uaccess.h> + +#define ECLITE_DATA_OPREGION_ID 0x9E +#define ECLITE_CMD_OPREGION_ID 0x9F + +#define ECL_MSG_DATA 0x1 +#define ECL_MSG_EVENT 0x2 + +#define ECL_ISH_READ 0x1 +#define ECL_ISH_WRITE 0x2 +#define ECL_ISH_HEADER_VERSION 0 + +#define ECL_CL_RX_RING_SIZE 16 +#define ECL_CL_TX_RING_SIZE 8 + +#define ECL_DATA_OPR_BUFLEN 384 +#define ECL_EVENTS_NOTIFY 333 + +#define cmd_opr_offsetof(element) offsetof(struct opregion_cmd, element) +#define cl_data_to_dev(opr_dev) ishtp_device((opr_dev)->cl_device) + +#ifndef BITS_TO_BYTES +#define BITS_TO_BYTES(x) ((x) / 8) +#endif + +struct opregion_cmd { + unsigned int command; + unsigned int offset; + unsigned int length; + unsigned int event_id; +}; + +struct opregion_data { + char data[ECL_DATA_OPR_BUFLEN]; +}; + +struct opregion_context { + struct opregion_cmd cmd_area; + struct opregion_data data_area; +}; + +struct ecl_message_header { + unsigned int version:2; + unsigned int data_type:2; + unsigned int request_type:2; + unsigned int offset:9; + unsigned int data_len:9; + unsigned int event:8; +}; + +struct ecl_message { + struct ecl_message_header header; + char payload[ECL_DATA_OPR_BUFLEN]; +}; + +struct ishtp_opregion_dev { + struct opregion_context opr_context; + struct ishtp_cl *ecl_ishtp_cl; + struct ishtp_cl_device *cl_device; + struct ishtp_fw_client *fw_client; + struct ishtp_cl_rb *rb; + struct acpi_device *adev; + unsigned int dsm_event_id; + unsigned int ish_link_ready; + unsigned int ish_read_done; + unsigned int acpi_init_done; + wait_queue_head_t read_wait; + struct work_struct event_work; + struct work_struct reset_work; + /* lock for opregion context */ + struct mutex lock; + +}; + +/* eclite ishtp client UUID: 6a19cc4b-d760-4de3-b14d-f25ebd0fbcd9 */ +static const guid_t ecl_ishtp_guid = + GUID_INIT(0x6a19cc4b, 0xd760, 0x4de3, + 0xb1, 0x4d, 0xf2, 0x5e, 0xbd, 0xf, 0xbc, 0xd9); + +/* ACPI DSM UUID: 91d936a7-1f01-49c6-a6b4-72f00ad8d8a5 */ +static const guid_t ecl_acpi_guid = + GUID_INIT(0x91d936a7, 0x1f01, 0x49c6, 0xa6, + 0xb4, 0x72, 0xf0, 0x0a, 0xd8, 0xd8, 0xa5); + +/** + * ecl_ish_cl_read() - Read data from eclite FW + * + * @opr_dev: pointer to opregion device + * + * This function issues a read request to eclite FW and waits until it + * receives a response. When response is received the read data is copied to + * opregion buffer. + */ +static int ecl_ish_cl_read(struct ishtp_opregion_dev *opr_dev) +{ + struct ecl_message_header header; + int len, rv; + + if (!opr_dev->ish_link_ready) + return -EIO; + + if ((opr_dev->opr_context.cmd_area.offset + + opr_dev->opr_context.cmd_area.length) > ECL_DATA_OPR_BUFLEN) { + return -EINVAL; + } + + header.version = ECL_ISH_HEADER_VERSION; + header.data_type = ECL_MSG_DATA; + header.request_type = ECL_ISH_READ; + header.offset = opr_dev->opr_context.cmd_area.offset; + header.data_len = opr_dev->opr_context.cmd_area.length; + header.event = opr_dev->opr_context.cmd_area.event_id; + len = sizeof(header); + + opr_dev->ish_read_done = false; + rv = ishtp_cl_send(opr_dev->ecl_ishtp_cl, (uint8_t *)&header, len); + if (rv) { + dev_err(cl_data_to_dev(opr_dev), "ish-read : send failed\n"); + return -EIO; + } + + dev_dbg(cl_data_to_dev(opr_dev), + "[ish_rd] Req: off : %x, len : %x\n", + header.offset, + header.data_len); + + rv = wait_event_interruptible_timeout(opr_dev->read_wait, + opr_dev->ish_read_done, + 2 * HZ); + if (!rv) { + dev_err(cl_data_to_dev(opr_dev), + "[ish_rd] No response from firmware\n"); + return -EIO; + } + + return 0; +} + +/** + * ecl_ish_cl_write() - This function writes data to eclite FW. + * + * @opr_dev: pointer to opregion device + * + * This function writes data to eclite FW. + */ +static int ecl_ish_cl_write(struct ishtp_opregion_dev *opr_dev) +{ + struct ecl_message message; + int len; + + if (!opr_dev->ish_link_ready) + return -EIO; + + if ((opr_dev->opr_context.cmd_area.offset + + opr_dev->opr_context.cmd_area.length) > ECL_DATA_OPR_BUFLEN) { + return -EINVAL; + } + + message.header.version = ECL_ISH_HEADER_VERSION; + message.header.data_type = ECL_MSG_DATA; + message.header.request_type = ECL_ISH_WRITE; + message.header.offset = opr_dev->opr_context.cmd_area.offset; + message.header.data_len = opr_dev->opr_context.cmd_area.length; + message.header.event = opr_dev->opr_context.cmd_area.event_id; + len = sizeof(struct ecl_message_header) + message.header.data_len; + + memcpy(message.payload, + opr_dev->opr_context.data_area.data + message.header.offset, + message.header.data_len); + + dev_dbg(cl_data_to_dev(opr_dev), + "[ish_wr] off : %x, len : %x\n", + message.header.offset, + message.header.data_len); + + return ishtp_cl_send(opr_dev->ecl_ishtp_cl, (uint8_t *)&message, len); +} + +static acpi_status +ecl_opregion_cmd_handler(u32 function, acpi_physical_address address, + u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + struct ishtp_opregion_dev *opr_dev; + struct opregion_cmd *cmd; + acpi_status status = AE_OK; + + if (!region_context || !value64) + return AE_BAD_PARAMETER; + + if (function == ACPI_READ) + return AE_ERROR; + + opr_dev = (struct ishtp_opregion_dev *)region_context; + + mutex_lock(&opr_dev->lock); + + cmd = &opr_dev->opr_context.cmd_area; + + switch (address) { + case cmd_opr_offsetof(command): + cmd->command = (u32)*value64; + + if (cmd->command == ECL_ISH_READ) + status = ecl_ish_cl_read(opr_dev); + else if (cmd->command == ECL_ISH_WRITE) + status = ecl_ish_cl_write(opr_dev); + else + status = AE_ERROR; + break; + case cmd_opr_offsetof(offset): + cmd->offset = (u32)*value64; + break; + case cmd_opr_offsetof(length): + cmd->length = (u32)*value64; + break; + case cmd_opr_offsetof(event_id): + cmd->event_id = (u32)*value64; + break; + default: + status = AE_ERROR; + } + + mutex_unlock(&opr_dev->lock); + + return status; +} + +static acpi_status +ecl_opregion_data_handler(u32 function, acpi_physical_address address, + u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + struct ishtp_opregion_dev *opr_dev; + unsigned int bytes = BITS_TO_BYTES(bits); + void *data_addr; + + if (!region_context || !value64) + return AE_BAD_PARAMETER; + + if (address + bytes > ECL_DATA_OPR_BUFLEN) + return AE_BAD_PARAMETER; + + opr_dev = (struct ishtp_opregion_dev *)region_context; + + mutex_lock(&opr_dev->lock); + + data_addr = &opr_dev->opr_context.data_area.data[address]; + + if (function == ACPI_READ) { + memcpy(value64, data_addr, bytes); + } else if (function == ACPI_WRITE) { + memcpy(data_addr, value64, bytes); + } else { + mutex_unlock(&opr_dev->lock); + return AE_BAD_PARAMETER; + } + + mutex_unlock(&opr_dev->lock); + + return AE_OK; +} + +static int acpi_find_eclite_device(struct ishtp_opregion_dev *opr_dev) +{ + struct acpi_device *adev; + + /* Find ECLite device and save reference */ + adev = acpi_dev_get_first_match_dev("INTC1035", NULL, -1); + if (!adev) { + dev_err(cl_data_to_dev(opr_dev), "eclite ACPI device not found\n"); + return -ENODEV; + } + + opr_dev->adev = adev; + + return 0; +} + +static int acpi_opregion_init(struct ishtp_opregion_dev *opr_dev) +{ + acpi_status status; + + status = acpi_install_address_space_handler(opr_dev->adev->handle, + ECLITE_CMD_OPREGION_ID, + ecl_opregion_cmd_handler, + NULL, opr_dev); + if (ACPI_FAILURE(status)) { + dev_err(cl_data_to_dev(opr_dev), + "cmd space handler install failed\n"); + return -ENODEV; + } + + status = acpi_install_address_space_handler(opr_dev->adev->handle, + ECLITE_DATA_OPREGION_ID, + ecl_opregion_data_handler, + NULL, opr_dev); + if (ACPI_FAILURE(status)) { + dev_err(cl_data_to_dev(opr_dev), + "data space handler install failed\n"); + + acpi_remove_address_space_handler(opr_dev->adev->handle, + ECLITE_CMD_OPREGION_ID, + ecl_opregion_cmd_handler); + return -ENODEV; + } + opr_dev->acpi_init_done = true; + + dev_dbg(cl_data_to_dev(opr_dev), "Opregion handlers are installed\n"); + + return 0; +} + +static void acpi_opregion_deinit(struct ishtp_opregion_dev *opr_dev) +{ + acpi_remove_address_space_handler(opr_dev->adev->handle, + ECLITE_CMD_OPREGION_ID, + ecl_opregion_cmd_handler); + + acpi_remove_address_space_handler(opr_dev->adev->handle, + ECLITE_DATA_OPREGION_ID, + ecl_opregion_data_handler); + opr_dev->acpi_init_done = false; +} + +static void ecl_acpi_invoke_dsm(struct work_struct *work) +{ + struct ishtp_opregion_dev *opr_dev; + union acpi_object *obj; + + opr_dev = container_of(work, struct ishtp_opregion_dev, event_work); + if (!opr_dev->acpi_init_done) + return; + + obj = acpi_evaluate_dsm(opr_dev->adev->handle, &ecl_acpi_guid, 0, + opr_dev->dsm_event_id, NULL); + if (!obj) { + dev_warn(cl_data_to_dev(opr_dev), "_DSM fn call failed\n"); + return; + } + + dev_dbg(cl_data_to_dev(opr_dev), "Exec DSM function code: %d success\n", + opr_dev->dsm_event_id); + + ACPI_FREE(obj); +} + +static void ecl_ish_process_rx_data(struct ishtp_opregion_dev *opr_dev) +{ + struct ecl_message *message = + (struct ecl_message *)opr_dev->rb->buffer.data; + + dev_dbg(cl_data_to_dev(opr_dev), + "[ish_rd] Resp: off : %x, len : %x\n", + message->header.offset, + message->header.data_len); + + if ((message->header.offset + message->header.data_len) > + ECL_DATA_OPR_BUFLEN) { + return; + } + + memcpy(opr_dev->opr_context.data_area.data + message->header.offset, + message->payload, message->header.data_len); + + opr_dev->ish_read_done = true; + wake_up_interruptible(&opr_dev->read_wait); +} + +static void ecl_ish_process_rx_event(struct ishtp_opregion_dev *opr_dev) +{ + struct ecl_message_header *header = + (struct ecl_message_header *)opr_dev->rb->buffer.data; + + dev_dbg(cl_data_to_dev(opr_dev), + "[ish_ev] Evt received: %8x\n", header->event); + + opr_dev->dsm_event_id = header->event; + schedule_work(&opr_dev->event_work); +} + +static int ecl_ish_cl_enable_events(struct ishtp_opregion_dev *opr_dev, + bool config_enable) +{ + struct ecl_message message; + int len; + + message.header.version = ECL_ISH_HEADER_VERSION; + message.header.data_type = ECL_MSG_DATA; + message.header.request_type = ECL_ISH_WRITE; + message.header.offset = ECL_EVENTS_NOTIFY; + message.header.data_len = 1; + message.payload[0] = config_enable; + + len = sizeof(struct ecl_message_header) + message.header.data_len; + + return ishtp_cl_send(opr_dev->ecl_ishtp_cl, (uint8_t *)&message, len); +} + +static void ecl_ishtp_cl_event_cb(struct ishtp_cl_device *cl_device) +{ + struct ishtp_cl *ecl_ishtp_cl = ishtp_get_drvdata(cl_device); + struct ishtp_opregion_dev *opr_dev; + struct ecl_message_header *header; + struct ishtp_cl_rb *rb; + + opr_dev = ishtp_get_client_data(ecl_ishtp_cl); + while ((rb = ishtp_cl_rx_get_rb(opr_dev->ecl_ishtp_cl)) != NULL) { + opr_dev->rb = rb; + header = (struct ecl_message_header *)rb->buffer.data; + + if (header->data_type == ECL_MSG_DATA) + ecl_ish_process_rx_data(opr_dev); + else if (header->data_type == ECL_MSG_EVENT) + ecl_ish_process_rx_event(opr_dev); + else + /* Got an event with wrong data_type, ignore it */ + dev_err(cl_data_to_dev(opr_dev), + "[ish_cb] Received wrong data_type\n"); + + ishtp_cl_io_rb_recycle(rb); + } +} + +static int ecl_ishtp_cl_init(struct ishtp_cl *ecl_ishtp_cl) +{ + struct ishtp_opregion_dev *opr_dev = + ishtp_get_client_data(ecl_ishtp_cl); + struct ishtp_fw_client *fw_client; + struct ishtp_device *dev; + int rv; + + rv = ishtp_cl_link(ecl_ishtp_cl); + if (rv) { + dev_err(cl_data_to_dev(opr_dev), "ishtp_cl_link failed\n"); + return rv; + } + + dev = ishtp_get_ishtp_device(ecl_ishtp_cl); + + /* Connect to FW client */ + ishtp_set_tx_ring_size(ecl_ishtp_cl, ECL_CL_TX_RING_SIZE); + ishtp_set_rx_ring_size(ecl_ishtp_cl, ECL_CL_RX_RING_SIZE); + + fw_client = ishtp_fw_cl_get_client(dev, &ecl_ishtp_guid); + if (!fw_client) { + dev_err(cl_data_to_dev(opr_dev), "fw client not found\n"); + return -ENOENT; + } + + ishtp_cl_set_fw_client_id(ecl_ishtp_cl, + ishtp_get_fw_client_id(fw_client)); + + ishtp_set_connection_state(ecl_ishtp_cl, ISHTP_CL_CONNECTING); + + rv = ishtp_cl_connect(ecl_ishtp_cl); + if (rv) { + dev_err(cl_data_to_dev(opr_dev), "client connect failed\n"); + + ishtp_cl_unlink(ecl_ishtp_cl); + return rv; + } + + dev_dbg(cl_data_to_dev(opr_dev), "Host connected to fw client\n"); + + return 0; +} + +static void ecl_ishtp_cl_deinit(struct ishtp_cl *ecl_ishtp_cl) +{ + ishtp_cl_unlink(ecl_ishtp_cl); + ishtp_cl_flush_queues(ecl_ishtp_cl); + ishtp_cl_free(ecl_ishtp_cl); +} + +static void ecl_ishtp_cl_reset_handler(struct work_struct *work) +{ + struct ishtp_opregion_dev *opr_dev; + struct ishtp_cl_device *cl_device; + struct ishtp_cl *ecl_ishtp_cl; + int rv; + int retry; + + opr_dev = container_of(work, struct ishtp_opregion_dev, reset_work); + + opr_dev->ish_link_ready = false; + + cl_device = opr_dev->cl_device; + ecl_ishtp_cl = opr_dev->ecl_ishtp_cl; + + ecl_ishtp_cl_deinit(ecl_ishtp_cl); + + ecl_ishtp_cl = ishtp_cl_allocate(cl_device); + if (!ecl_ishtp_cl) + return; + + ishtp_set_drvdata(cl_device, ecl_ishtp_cl); + ishtp_set_client_data(ecl_ishtp_cl, opr_dev); + + opr_dev->ecl_ishtp_cl = ecl_ishtp_cl; + + for (retry = 0; retry < 3; ++retry) { + rv = ecl_ishtp_cl_init(ecl_ishtp_cl); + if (!rv) + break; + } + if (rv) { + ishtp_cl_free(ecl_ishtp_cl); + opr_dev->ecl_ishtp_cl = NULL; + dev_err(cl_data_to_dev(opr_dev), + "[ish_rst] Reset failed. Link not ready.\n"); + return; + } + + ishtp_register_event_cb(cl_device, ecl_ishtp_cl_event_cb); + dev_info(cl_data_to_dev(opr_dev), + "[ish_rst] Reset Success. Link ready.\n"); + + opr_dev->ish_link_ready = true; + + if (opr_dev->acpi_init_done) + return; + + rv = acpi_opregion_init(opr_dev); + if (rv) { + dev_err(cl_data_to_dev(opr_dev), + "ACPI opregion init failed\n"); + } +} + +static int ecl_ishtp_cl_probe(struct ishtp_cl_device *cl_device) +{ + struct ishtp_cl *ecl_ishtp_cl; + struct ishtp_opregion_dev *opr_dev; + int rv; + + opr_dev = devm_kzalloc(ishtp_device(cl_device), sizeof(*opr_dev), + GFP_KERNEL); + if (!opr_dev) + return -ENOMEM; + + ecl_ishtp_cl = ishtp_cl_allocate(cl_device); + if (!ecl_ishtp_cl) + return -ENOMEM; + + ishtp_set_drvdata(cl_device, ecl_ishtp_cl); + ishtp_set_client_data(ecl_ishtp_cl, opr_dev); + opr_dev->ecl_ishtp_cl = ecl_ishtp_cl; + opr_dev->cl_device = cl_device; + + init_waitqueue_head(&opr_dev->read_wait); + INIT_WORK(&opr_dev->event_work, ecl_acpi_invoke_dsm); + INIT_WORK(&opr_dev->reset_work, ecl_ishtp_cl_reset_handler); + + /* Initialize ish client device */ + rv = ecl_ishtp_cl_init(ecl_ishtp_cl); + if (rv) { + dev_err(cl_data_to_dev(opr_dev), "Client init failed\n"); + goto err_exit; + } + + dev_dbg(cl_data_to_dev(opr_dev), "eclite-ishtp client initialised\n"); + + opr_dev->ish_link_ready = true; + mutex_init(&opr_dev->lock); + + rv = acpi_find_eclite_device(opr_dev); + if (rv) { + dev_err(cl_data_to_dev(opr_dev), "ECLite ACPI ID not found\n"); + goto err_exit; + } + + /* Register a handler for eclite fw events */ + ishtp_register_event_cb(cl_device, ecl_ishtp_cl_event_cb); + + /* Now init opregion handlers */ + rv = acpi_opregion_init(opr_dev); + if (rv) { + dev_err(cl_data_to_dev(opr_dev), "ACPI opregion init failed\n"); + goto err_exit; + } + + /* Reprobe devices depending on ECLite - battery, fan, etc. */ + acpi_dev_clear_dependencies(opr_dev->adev); + + return 0; +err_exit: + ishtp_set_connection_state(ecl_ishtp_cl, ISHTP_CL_DISCONNECTING); + ishtp_cl_disconnect(ecl_ishtp_cl); + ecl_ishtp_cl_deinit(ecl_ishtp_cl); + + return rv; +} + +static void ecl_ishtp_cl_remove(struct ishtp_cl_device *cl_device) +{ + struct ishtp_cl *ecl_ishtp_cl = ishtp_get_drvdata(cl_device); + struct ishtp_opregion_dev *opr_dev = + ishtp_get_client_data(ecl_ishtp_cl); + + if (opr_dev->acpi_init_done) + acpi_opregion_deinit(opr_dev); + + acpi_dev_put(opr_dev->adev); + + ishtp_set_connection_state(ecl_ishtp_cl, ISHTP_CL_DISCONNECTING); + ishtp_cl_disconnect(ecl_ishtp_cl); + ecl_ishtp_cl_deinit(ecl_ishtp_cl); + + cancel_work_sync(&opr_dev->reset_work); + cancel_work_sync(&opr_dev->event_work); +} + +static int ecl_ishtp_cl_reset(struct ishtp_cl_device *cl_device) +{ + struct ishtp_cl *ecl_ishtp_cl = ishtp_get_drvdata(cl_device); + struct ishtp_opregion_dev *opr_dev = + ishtp_get_client_data(ecl_ishtp_cl); + + schedule_work(&opr_dev->reset_work); + + return 0; +} + +static int ecl_ishtp_cl_suspend(struct device *device) +{ + struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device); + struct ishtp_cl *ecl_ishtp_cl = ishtp_get_drvdata(cl_device); + struct ishtp_opregion_dev *opr_dev = + ishtp_get_client_data(ecl_ishtp_cl); + + if (acpi_target_system_state() == ACPI_STATE_S0) + return 0; + + acpi_opregion_deinit(opr_dev); + ecl_ish_cl_enable_events(opr_dev, false); + + return 0; +} + +static int ecl_ishtp_cl_resume(struct device *device) +{ + /* A reset is expected to call after an Sx. At this point + * we are not sure if the link is up or not to restore anything, + * so do nothing in resume path + */ + return 0; +} + +static const struct dev_pm_ops ecl_ishtp_pm_ops = { + .suspend = ecl_ishtp_cl_suspend, + .resume = ecl_ishtp_cl_resume, +}; + +static struct ishtp_cl_driver ecl_ishtp_cl_driver = { + .name = "ishtp-eclite", + .guid = &ecl_ishtp_guid, + .probe = ecl_ishtp_cl_probe, + .remove = ecl_ishtp_cl_remove, + .reset = ecl_ishtp_cl_reset, + .driver.pm = &ecl_ishtp_pm_ops, +}; + +static int __init ecl_ishtp_init(void) +{ + return ishtp_cl_driver_register(&ecl_ishtp_cl_driver, THIS_MODULE); +} + +static void __exit ecl_ishtp_exit(void) +{ + return ishtp_cl_driver_unregister(&ecl_ishtp_cl_driver); +} + +late_initcall(ecl_ishtp_init); +module_exit(ecl_ishtp_exit); + +MODULE_DESCRIPTION("ISH ISHTP eclite client opregion driver"); +MODULE_AUTHOR("K Naduvalath, Sumesh <sumesh.k.naduvalath@intel.com>"); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("ishtp:*"); diff --git a/drivers/platform/x86/intel/punit_ipc.c b/drivers/platform/x86/intel/punit_ipc.c index f58b8543f6ac..66bb39fd0ef9 100644 --- a/drivers/platform/x86/intel/punit_ipc.c +++ b/drivers/platform/x86/intel/punit_ipc.c @@ -8,7 +8,6 @@ * which provide mailbox interface for power management usage. */ -#include <linux/acpi.h> #include <linux/bitops.h> #include <linux/delay.h> #include <linux/device.h> @@ -319,7 +318,7 @@ static struct platform_driver intel_punit_ipc_driver = { .remove = intel_punit_ipc_remove, .driver = { .name = "intel_punit_ipc", - .acpi_match_table = ACPI_PTR(punit_ipc_acpi_ids), + .acpi_match_table = punit_ipc_acpi_ids, }, }; diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index bfa0cc20750d..7cc9089d1e14 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -75,7 +75,7 @@ struct intel_scu_ipc_dev { #define IPC_READ_BUFFER 0x90 /* Timeout in jiffies */ -#define IPC_TIMEOUT (5 * HZ) +#define IPC_TIMEOUT (10 * HZ) static struct intel_scu_ipc_dev *ipcdev; /* Only one for now */ static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */ @@ -232,7 +232,7 @@ static inline u32 ipc_data_readl(struct intel_scu_ipc_dev *scu, u32 offset) /* Wait till scu status is busy */ static inline int busy_loop(struct intel_scu_ipc_dev *scu) { - unsigned long end = jiffies + msecs_to_jiffies(IPC_TIMEOUT); + unsigned long end = jiffies + IPC_TIMEOUT; do { u32 status; @@ -247,7 +247,7 @@ static inline int busy_loop(struct intel_scu_ipc_dev *scu) return -ETIMEDOUT; } -/* Wait till ipc ioc interrupt is received or timeout in 3 HZ */ +/* Wait till ipc ioc interrupt is received or timeout in 10 HZ */ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu) { int status; diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c index 3e520d5bca07..ae9293024c77 100644 --- a/drivers/platform/x86/lg-laptop.c +++ b/drivers/platform/x86/lg-laptop.c @@ -60,7 +60,6 @@ MODULE_ALIAS("wmi:" WMI_EVENT_GUID2); MODULE_ALIAS("wmi:" WMI_EVENT_GUID3); MODULE_ALIAS("wmi:" WMI_METHOD_WMAB); MODULE_ALIAS("wmi:" WMI_METHOD_WMBB); -MODULE_ALIAS("acpi*:LGEX0815:*"); static struct platform_device *pf_device; static struct input_dev *wmi_input_dev; @@ -331,7 +330,7 @@ static ssize_t fan_mode_show(struct device *dev, status = r->integer.value & 0x01; kfree(r); - return snprintf(buffer, PAGE_SIZE, "%d\n", status); + return sysfs_emit(buffer, "%d\n", status); } static ssize_t usb_charge_store(struct device *dev, @@ -373,7 +372,7 @@ static ssize_t usb_charge_show(struct device *dev, kfree(r); - return snprintf(buffer, PAGE_SIZE, "%d\n", status); + return sysfs_emit(buffer, "%d\n", status); } static ssize_t reader_mode_store(struct device *dev, @@ -415,7 +414,7 @@ static ssize_t reader_mode_show(struct device *dev, kfree(r); - return snprintf(buffer, PAGE_SIZE, "%d\n", status); + return sysfs_emit(buffer, "%d\n", status); } static ssize_t fn_lock_store(struct device *dev, @@ -456,7 +455,7 @@ static ssize_t fn_lock_show(struct device *dev, status = !!r->buffer.pointer[0]; kfree(r); - return snprintf(buffer, PAGE_SIZE, "%d\n", status); + return sysfs_emit(buffer, "%d\n", status); } static ssize_t battery_care_limit_store(struct device *dev, @@ -521,7 +520,7 @@ static ssize_t battery_care_limit_show(struct device *dev, if (status != 80 && status != 100) status = 0; - return snprintf(buffer, PAGE_SIZE, "%d\n", status); + return sysfs_emit(buffer, "%d\n", status); } static DEVICE_ATTR_RW(fan_mode); @@ -655,7 +654,7 @@ static int acpi_add(struct acpi_device *device) goto out_platform_registered; } product = dmi_get_system_info(DMI_PRODUCT_NAME); - if (strlen(product) > 4) + if (product && strlen(product) > 4) switch (product[4]) { case '5': case '6': diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 8bce3da32a42..447044fdcb77 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -27,9 +27,14 @@ #define MLXPLAT_CPLD_LPC_REG_CPLD3_VER_OFFSET 0x02 #define MLXPLAT_CPLD_LPC_REG_CPLD4_VER_OFFSET 0x03 #define MLXPLAT_CPLD_LPC_REG_CPLD1_PN_OFFSET 0x04 +#define MLXPLAT_CPLD_LPC_REG_CPLD1_PN1_OFFSET 0x05 #define MLXPLAT_CPLD_LPC_REG_CPLD2_PN_OFFSET 0x06 +#define MLXPLAT_CPLD_LPC_REG_CPLD2_PN1_OFFSET 0x07 #define MLXPLAT_CPLD_LPC_REG_CPLD3_PN_OFFSET 0x08 +#define MLXPLAT_CPLD_LPC_REG_CPLD3_PN1_OFFSET 0x09 #define MLXPLAT_CPLD_LPC_REG_CPLD4_PN_OFFSET 0x0a +#define MLXPLAT_CPLD_LPC_REG_CPLD4_PN1_OFFSET 0x0b +#define MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET 0x1c #define MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET 0x1d #define MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET 0x1e #define MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET 0x1f @@ -38,13 +43,20 @@ #define MLXPLAT_CPLD_LPC_REG_LED3_OFFSET 0x22 #define MLXPLAT_CPLD_LPC_REG_LED4_OFFSET 0x23 #define MLXPLAT_CPLD_LPC_REG_LED5_OFFSET 0x24 +#define MLXPLAT_CPLD_LPC_REG_LED6_OFFSET 0x25 +#define MLXPLAT_CPLD_LPC_REG_LED7_OFFSET 0x26 #define MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION 0x2a #define MLXPLAT_CPLD_LPC_REG_GP0_RO_OFFSET 0x2b +#define MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET 0x2d #define MLXPLAT_CPLD_LPC_REG_GP0_OFFSET 0x2e +#define MLXPLAT_CPLD_LPC_REG_GP_RST_OFFSET 0x2f #define MLXPLAT_CPLD_LPC_REG_GP1_OFFSET 0x30 #define MLXPLAT_CPLD_LPC_REG_WP1_OFFSET 0x31 #define MLXPLAT_CPLD_LPC_REG_GP2_OFFSET 0x32 #define MLXPLAT_CPLD_LPC_REG_WP2_OFFSET 0x33 +#define MLXPLAT_CPLD_LPC_REG_FIELD_UPGRADE 0x34 +#define MLXPLAT_CPLD_LPC_SAFE_BIOS_OFFSET 0x35 +#define MLXPLAT_CPLD_LPC_SAFE_BIOS_WP_OFFSET 0x36 #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 @@ -57,15 +69,39 @@ #define MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET 0x50 #define MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET 0x51 #define MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET 0x52 +#define MLXPLAT_CPLD_LPC_REG_AGGRLC_OFFSET 0x56 +#define MLXPLAT_CPLD_LPC_REG_AGGRLC_MASK_OFFSET 0x57 #define MLXPLAT_CPLD_LPC_REG_PSU_OFFSET 0x58 #define MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET 0x59 #define MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET 0x5a #define MLXPLAT_CPLD_LPC_REG_PWR_OFFSET 0x64 #define MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET 0x65 #define MLXPLAT_CPLD_LPC_REG_PWR_MASK_OFFSET 0x66 +#define MLXPLAT_CPLD_LPC_REG_LC_IN_OFFSET 0x70 +#define MLXPLAT_CPLD_LPC_REG_LC_IN_EVENT_OFFSET 0x71 +#define MLXPLAT_CPLD_LPC_REG_LC_IN_MASK_OFFSET 0x72 #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_LC_VR_OFFSET 0x9a +#define MLXPLAT_CPLD_LPC_REG_LC_VR_EVENT_OFFSET 0x9b +#define MLXPLAT_CPLD_LPC_REG_LC_VR_MASK_OFFSET 0x9c +#define MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET 0x9d +#define MLXPLAT_CPLD_LPC_REG_LC_PG_EVENT_OFFSET 0x9e +#define MLXPLAT_CPLD_LPC_REG_LC_PG_MASK_OFFSET 0x9f +#define MLXPLAT_CPLD_LPC_REG_LC_RD_OFFSET 0xa0 +#define MLXPLAT_CPLD_LPC_REG_LC_RD_EVENT_OFFSET 0xa1 +#define MLXPLAT_CPLD_LPC_REG_LC_RD_MASK_OFFSET 0xa2 +#define MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET 0xa3 +#define MLXPLAT_CPLD_LPC_REG_LC_SN_EVENT_OFFSET 0xa4 +#define MLXPLAT_CPLD_LPC_REG_LC_SN_MASK_OFFSET 0xa5 +#define MLXPLAT_CPLD_LPC_REG_LC_OK_OFFSET 0xa6 +#define MLXPLAT_CPLD_LPC_REG_LC_OK_EVENT_OFFSET 0xa7 +#define MLXPLAT_CPLD_LPC_REG_LC_OK_MASK_OFFSET 0xa8 +#define MLXPLAT_CPLD_LPC_REG_LC_SD_OFFSET 0xa9 +#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_WD_CLEAR_OFFSET 0xc7 #define MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET 0xc8 #define MLXPLAT_CPLD_LPC_REG_WD1_TMR_OFFSET 0xc9 @@ -88,23 +124,30 @@ #define MLXPLAT_CPLD_LPC_REG_TACHO4_OFFSET 0xe7 #define MLXPLAT_CPLD_LPC_REG_TACHO5_OFFSET 0xe8 #define MLXPLAT_CPLD_LPC_REG_TACHO6_OFFSET 0xe9 +#define MLXPLAT_CPLD_LPC_REG_PWM2_OFFSET 0xea #define MLXPLAT_CPLD_LPC_REG_TACHO7_OFFSET 0xeb #define MLXPLAT_CPLD_LPC_REG_TACHO8_OFFSET 0xec #define MLXPLAT_CPLD_LPC_REG_TACHO9_OFFSET 0xed #define MLXPLAT_CPLD_LPC_REG_TACHO10_OFFSET 0xee #define MLXPLAT_CPLD_LPC_REG_TACHO11_OFFSET 0xef #define MLXPLAT_CPLD_LPC_REG_TACHO12_OFFSET 0xf0 +#define MLXPLAT_CPLD_LPC_REG_TACHO13_OFFSET 0xf1 +#define MLXPLAT_CPLD_LPC_REG_TACHO14_OFFSET 0xf2 +#define MLXPLAT_CPLD_LPC_REG_PWM3_OFFSET 0xf3 +#define MLXPLAT_CPLD_LPC_REG_PWM4_OFFSET 0xf4 #define MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET 0xf5 #define MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET 0xf6 #define MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET 0xf7 #define MLXPLAT_CPLD_LPC_REG_TACHO_SPEED_OFFSET 0xf8 #define MLXPLAT_CPLD_LPC_REG_PSU_I2C_CAP_OFFSET 0xf9 +#define MLXPLAT_CPLD_LPC_REG_SLOT_QTY_OFFSET 0xfa #define MLXPLAT_CPLD_LPC_REG_CONFIG1_OFFSET 0xfb #define MLXPLAT_CPLD_LPC_REG_CONFIG2_OFFSET 0xfc #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 + \ @@ -116,6 +159,9 @@ #define MLXPLAT_CPLD_LPC_REG3 ((MLXPLAT_CPLD_LPC_REG_BASE_ADRR + \ MLXPLAT_CPLD_LPC_I2C_CH3_OFF) | \ 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_PIO_OFFSET) /* Masks for aggregation, psu, pwr and fan event in CPLD related registers. */ #define MLXPLAT_CPLD_AGGR_ASIC_MASK_DEF 0x04 @@ -128,6 +174,24 @@ #define MLXPLAT_CPLD_AGGR_ASIC_MASK_NG 0x01 #define MLXPLAT_CPLD_AGGR_MASK_NG_DEF 0x04 #define MLXPLAT_CPLD_AGGR_MASK_COMEX BIT(0) +#define MLXPLAT_CPLD_AGGR_MASK_LC BIT(3) +#define MLXPLAT_CPLD_AGGR_MASK_MODULAR (MLXPLAT_CPLD_AGGR_MASK_NG_DEF | \ + MLXPLAT_CPLD_AGGR_MASK_COMEX | \ + MLXPLAT_CPLD_AGGR_MASK_LC) +#define MLXPLAT_CPLD_AGGR_MASK_LC_PRSNT BIT(0) +#define MLXPLAT_CPLD_AGGR_MASK_LC_RDY BIT(1) +#define MLXPLAT_CPLD_AGGR_MASK_LC_PG BIT(2) +#define MLXPLAT_CPLD_AGGR_MASK_LC_SCRD BIT(3) +#define MLXPLAT_CPLD_AGGR_MASK_LC_SYNC BIT(4) +#define MLXPLAT_CPLD_AGGR_MASK_LC_ACT BIT(5) +#define MLXPLAT_CPLD_AGGR_MASK_LC_SDWN BIT(6) +#define MLXPLAT_CPLD_AGGR_MASK_LC_LOW (MLXPLAT_CPLD_AGGR_MASK_LC_PRSNT | \ + MLXPLAT_CPLD_AGGR_MASK_LC_RDY | \ + MLXPLAT_CPLD_AGGR_MASK_LC_PG | \ + MLXPLAT_CPLD_AGGR_MASK_LC_SCRD | \ + MLXPLAT_CPLD_AGGR_MASK_LC_SYNC | \ + MLXPLAT_CPLD_AGGR_MASK_LC_ACT | \ + MLXPLAT_CPLD_AGGR_MASK_LC_SDWN) #define MLXPLAT_CPLD_LOW_AGGR_MASK_LOW 0xc1 #define MLXPLAT_CPLD_LOW_AGGR_MASK_I2C BIT(6) #define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0) @@ -136,7 +200,7 @@ #define MLXPLAT_CPLD_PWR_EXT_MASK GENMASK(3, 0) #define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0) #define MLXPLAT_CPLD_ASIC_MASK GENMASK(1, 0) -#define MLXPLAT_CPLD_FAN_NG_MASK GENMASK(5, 0) +#define MLXPLAT_CPLD_FAN_NG_MASK GENMASK(6, 0) #define MLXPLAT_CPLD_LED_LO_NIBBLE_MASK GENMASK(7, 4) #define MLXPLAT_CPLD_LED_HI_NIBBLE_MASK GENMASK(3, 0) #define MLXPLAT_CPLD_VOLTREG_UPD_MASK GENMASK(5, 4) @@ -149,6 +213,9 @@ MLXPLAT_CPLD_AGGR_MASK_CARRIER) #define MLXPLAT_CPLD_LOW_AGGRCX_MASK 0xc1 +/* Masks for aggregation for modular systems */ +#define MLXPLAT_CPLD_LPC_LC_MASK GENMASK(7, 0) + /* Default I2C parent bus number */ #define MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR 1 @@ -163,9 +230,12 @@ #define MLXPLAT_CPLD_CH1 2 #define MLXPLAT_CPLD_CH2 10 #define MLXPLAT_CPLD_CH3 18 +#define MLXPLAT_CPLD_CH2_ETH_MODULAR 3 +#define MLXPLAT_CPLD_CH3_ETH_MODULAR 43 +#define MLXPLAT_CPLD_CH4_ETH_MODULAR 51 /* Number of LPC attached MUX platform devices */ -#define MLXPLAT_CPLD_LPC_MUX_DEVS 3 +#define MLXPLAT_CPLD_LPC_MUX_DEVS 4 /* Hotplug devices adapter numbers */ #define MLXPLAT_CPLD_NR_NONE -1 @@ -175,6 +245,11 @@ #define MLXPLAT_CPLD_FAN2_DEFAULT_NR 12 #define MLXPLAT_CPLD_FAN3_DEFAULT_NR 13 #define MLXPLAT_CPLD_FAN4_DEFAULT_NR 14 +#define MLXPLAT_CPLD_NR_ASIC 3 +#define MLXPLAT_CPLD_NR_LC_BASE 34 + +#define MLXPLAT_CPLD_NR_LC_SET(nr) (MLXPLAT_CPLD_NR_LC_BASE + (nr)) +#define MLXPLAT_CPLD_LC_ADDR 0x32 /* Masks and default values for watchdogs */ #define MLXPLAT_CPLD_WD1_CLEAR_MASK GENMASK(7, 1) @@ -190,6 +265,11 @@ #define MLXPLAT_CPLD_WD3_DFLT_TIMEOUT 600 #define MLXPLAT_CPLD_WD_MAX_DEVS 2 +#define MLXPLAT_CPLD_LPC_SYSIRQ 17 + +/* Minimum power required for turning on Ethernet modular system (WATT) */ +#define MLXPLAT_CPLD_ETH_MODULAR_PWR_MIN 50 + /* mlxplat_priv - platform private data * @pdev_i2c - i2c controller platform device * @pdev_mux - array of mux platform devices @@ -318,6 +398,58 @@ static struct i2c_mux_reg_platform_data mlxplat_extended_mux_data[] = { }; +/* Platform channels for modular system family */ +static const int mlxplat_modular_upper_channel[] = { 1 }; +static const int mlxplat_modular_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, 33, 34, 35, 36, 37, + 38, 39, 40 +}; + +/* Platform modular mux data */ +static struct i2c_mux_reg_platform_data mlxplat_modular_mux_data[] = { + { + .parent = 1, + .base_nr = MLXPLAT_CPLD_CH1, + .write_only = 1, + .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG4, + .reg_size = 1, + .idle_in_use = 1, + .values = mlxplat_modular_upper_channel, + .n_values = ARRAY_SIZE(mlxplat_modular_upper_channel), + }, + { + .parent = 1, + .base_nr = MLXPLAT_CPLD_CH2_ETH_MODULAR, + .write_only = 1, + .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG1, + .reg_size = 1, + .idle_in_use = 1, + .values = mlxplat_modular_channels, + .n_values = ARRAY_SIZE(mlxplat_modular_channels), + }, + { + .parent = MLXPLAT_CPLD_CH1, + .base_nr = MLXPLAT_CPLD_CH3_ETH_MODULAR, + .write_only = 1, + .reg = (void __iomem *)MLXPLAT_CPLD_LPC_REG3, + .reg_size = 1, + .idle_in_use = 1, + .values = mlxplat_msn21xx_channels, + .n_values = ARRAY_SIZE(mlxplat_msn21xx_channels), + }, + { + .parent = 1, + .base_nr = MLXPLAT_CPLD_CH4_ETH_MODULAR, + .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[] = { { @@ -401,6 +533,21 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_items_data[] = { }, }; +static struct mlxreg_core_data mlxplat_mlxcpld_default_pwr_wc_items_data[] = { + { + .label = "pwr1", + .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, + .mask = BIT(0), + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + }, + { + .label = "pwr2", + .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, + .mask = BIT(1), + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + }, +}; + static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_items_data[] = { { .label = "fan1", @@ -529,6 +676,46 @@ struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_default_data = { .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW, }; +static struct mlxreg_core_item mlxplat_mlxcpld_default_wc_items[] = { + { + .data = mlxplat_mlxcpld_comex_psu_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_CARRIER, + .reg = MLXPLAT_CPLD_LPC_REG_PSU_OFFSET, + .mask = MLXPLAT_CPLD_PSU_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_psu_items_data), + .inversed = 1, + .health = false, + }, + { + .data = mlxplat_mlxcpld_default_pwr_wc_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_CARRIER, + .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, + .mask = MLXPLAT_CPLD_PWR_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_pwr_items_data), + .inversed = 0, + .health = false, + }, + { + .data = mlxplat_mlxcpld_default_asic_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_ASIC_MASK_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_default_wc_data = { + .items = mlxplat_mlxcpld_default_wc_items, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_wc_items), + .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET, + .mask = MLXPLAT_CPLD_AGGR_MASK_DEF, + .cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET, + .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW, +}; + static struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_comex_data = { .items = mlxplat_mlxcpld_comex_items, @@ -807,6 +994,14 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_fan_items_data[] = { .bit = BIT(5), .hpdev.nr = MLXPLAT_CPLD_NR_NONE, }, + { + .label = "fan7", + .reg = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, + .mask = BIT(6), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(6), + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + }, }; static struct mlxreg_core_item mlxplat_mlxcpld_default_ng_items[] = { @@ -968,6 +1163,847 @@ struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_ext_data = { .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW, }; +static struct mlxreg_core_data mlxplat_mlxcpld_modular_pwr_items_data[] = { + { + .label = "pwr1", + .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, + .mask = BIT(0), + .hpdev.brdinfo = &mlxplat_mlxcpld_pwr[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[1], + .hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR, + }, + { + .label = "pwr3", + .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, + .mask = BIT(2), + .hpdev.brdinfo = &mlxplat_mlxcpld_ext_pwr[0], + .hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR, + }, + { + .label = "pwr4", + .reg = MLXPLAT_CPLD_LPC_REG_PWR_OFFSET, + .mask = BIT(3), + .hpdev.brdinfo = &mlxplat_mlxcpld_ext_pwr[1], + .hpdev.nr = MLXPLAT_CPLD_PSU_MSNXXXX_NR, + }, +}; + +static +struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_lc_act = { + .irq = MLXPLAT_CPLD_LPC_SYSIRQ, +}; + +static struct mlxreg_core_data mlxplat_mlxcpld_modular_asic_items_data[] = { + { + .label = "asic1", + .reg = MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET, + .mask = MLXPLAT_CPLD_ASIC_MASK, + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + }, +}; + +static struct i2c_board_info mlxplat_mlxcpld_lc_i2c_dev[] = { + { + I2C_BOARD_INFO("mlxreg-lc", MLXPLAT_CPLD_LC_ADDR), + .platform_data = &mlxplat_mlxcpld_lc_act, + }, + { + I2C_BOARD_INFO("mlxreg-lc", MLXPLAT_CPLD_LC_ADDR), + .platform_data = &mlxplat_mlxcpld_lc_act, + }, + { + I2C_BOARD_INFO("mlxreg-lc", MLXPLAT_CPLD_LC_ADDR), + .platform_data = &mlxplat_mlxcpld_lc_act, + }, + { + I2C_BOARD_INFO("mlxreg-lc", MLXPLAT_CPLD_LC_ADDR), + .platform_data = &mlxplat_mlxcpld_lc_act, + }, + { + I2C_BOARD_INFO("mlxreg-lc", MLXPLAT_CPLD_LC_ADDR), + .platform_data = &mlxplat_mlxcpld_lc_act, + }, + { + I2C_BOARD_INFO("mlxreg-lc", MLXPLAT_CPLD_LC_ADDR), + .platform_data = &mlxplat_mlxcpld_lc_act, + }, + { + I2C_BOARD_INFO("mlxreg-lc", MLXPLAT_CPLD_LC_ADDR), + .platform_data = &mlxplat_mlxcpld_lc_act, + }, + { + I2C_BOARD_INFO("mlxreg-lc", MLXPLAT_CPLD_LC_ADDR), + .platform_data = &mlxplat_mlxcpld_lc_act, + }, +}; + +static struct mlxreg_core_hotplug_notifier mlxplat_mlxcpld_modular_lc_notifier[] = { + { + .identity = "lc1", + }, + { + .identity = "lc2", + }, + { + .identity = "lc3", + }, + { + .identity = "lc4", + }, + { + .identity = "lc5", + }, + { + .identity = "lc6", + }, + { + .identity = "lc7", + }, + { + .identity = "lc8", + }, +}; + +static struct mlxreg_core_data mlxplat_mlxcpld_modular_lc_pr_items_data[] = { + { + .label = "lc1_present", + .reg = MLXPLAT_CPLD_LPC_REG_LC_IN_OFFSET, + .mask = BIT(0), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[0], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(0), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[0], + .slot = 1, + }, + { + .label = "lc2_present", + .reg = MLXPLAT_CPLD_LPC_REG_LC_IN_OFFSET, + .mask = BIT(1), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[1], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(1), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[1], + .slot = 2, + }, + { + .label = "lc3_present", + .reg = MLXPLAT_CPLD_LPC_REG_LC_IN_OFFSET, + .mask = BIT(2), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[2], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(2), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[2], + .slot = 3, + }, + { + .label = "lc4_present", + .reg = MLXPLAT_CPLD_LPC_REG_LC_IN_OFFSET, + .mask = BIT(3), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[3], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(3), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[3], + .slot = 4, + }, + { + .label = "lc5_present", + .reg = MLXPLAT_CPLD_LPC_REG_LC_IN_OFFSET, + .mask = BIT(4), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[4], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(4), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[4], + .slot = 5, + }, + { + .label = "lc6_present", + .reg = MLXPLAT_CPLD_LPC_REG_LC_IN_OFFSET, + .mask = BIT(5), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[5], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(5), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[5], + .slot = 6, + }, + { + .label = "lc7_present", + .reg = MLXPLAT_CPLD_LPC_REG_LC_IN_OFFSET, + .mask = BIT(6), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[6], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(6), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[6], + .slot = 7, + }, + { + .label = "lc8_present", + .reg = MLXPLAT_CPLD_LPC_REG_LC_IN_OFFSET, + .mask = BIT(7), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[7], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(7), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[7], + .slot = 8, + }, +}; + +static struct mlxreg_core_data mlxplat_mlxcpld_modular_lc_ver_items_data[] = { + { + .label = "lc1_verified", + .reg = MLXPLAT_CPLD_LPC_REG_LC_VR_OFFSET, + .mask = BIT(0), + .reg_prsnt = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .reg_sync = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .reg_pwr = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .reg_ena = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[0], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(0), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[0], + .slot = 1, + }, + { + .label = "lc2_verified", + .reg = MLXPLAT_CPLD_LPC_REG_LC_VR_OFFSET, + .mask = BIT(1), + .reg_prsnt = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .reg_sync = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .reg_pwr = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .reg_ena = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[1], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(1), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[1], + .slot = 2, + }, + { + .label = "lc3_verified", + .reg = MLXPLAT_CPLD_LPC_REG_LC_VR_OFFSET, + .mask = BIT(2), + .reg_prsnt = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .reg_sync = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .reg_pwr = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .reg_ena = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[2], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(2), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[2], + .slot = 3, + }, + { + .label = "lc4_verified", + .reg = MLXPLAT_CPLD_LPC_REG_LC_VR_OFFSET, + .mask = BIT(3), + .reg_prsnt = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .reg_sync = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .reg_pwr = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .reg_ena = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[3], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(3), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[3], + .slot = 4, + }, + { + .label = "lc5_verified", + .reg = MLXPLAT_CPLD_LPC_REG_LC_VR_OFFSET, + .mask = BIT(4), + .reg_prsnt = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .reg_sync = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .reg_pwr = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .reg_ena = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[4], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(4), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[4], + .slot = 5, + }, + { + .label = "lc6_verified", + .reg = MLXPLAT_CPLD_LPC_REG_LC_VR_OFFSET, + .mask = BIT(5), + .reg_prsnt = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .reg_sync = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .reg_pwr = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .reg_ena = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[5], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(5), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[5], + .slot = 6, + }, + { + .label = "lc7_verified", + .reg = MLXPLAT_CPLD_LPC_REG_LC_VR_OFFSET, + .mask = BIT(6), + .reg_prsnt = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .reg_sync = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .reg_pwr = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .reg_ena = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[6], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(6), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[6], + .slot = 7, + }, + { + .label = "lc8_verified", + .reg = MLXPLAT_CPLD_LPC_REG_LC_VR_OFFSET, + .mask = BIT(7), + .reg_prsnt = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .reg_sync = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .reg_pwr = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .reg_ena = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[7], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(7), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_PLATFORM_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[7], + .slot = 8, + }, +}; + +static struct mlxreg_core_data mlxplat_mlxcpld_modular_lc_pg_data[] = { + { + .label = "lc1_powered", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .mask = BIT(0), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[0], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(0), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[0], + .slot = 1, + }, + { + .label = "lc2_powered", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .mask = BIT(1), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[1], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(1), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[1], + .slot = 2, + }, + { + .label = "lc3_powered", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .mask = BIT(2), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[2], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(2), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[2], + .slot = 3, + }, + { + .label = "lc4_powered", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .mask = BIT(3), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[3], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(3), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[3], + .slot = 4, + }, + { + .label = "lc5_powered", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .mask = BIT(4), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[4], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(4), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[4], + .slot = 5, + }, + { + .label = "lc6_powered", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .mask = BIT(5), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[5], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(5), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[5], + .slot = 6, + }, + { + .label = "lc7_powered", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .mask = BIT(6), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[6], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(6), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[6], + .slot = 7, + }, + { + .label = "lc8_powered", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .mask = BIT(7), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[7], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(7), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[7], + .slot = 8, + }, +}; + +static struct mlxreg_core_data mlxplat_mlxcpld_modular_lc_ready_data[] = { + { + .label = "lc1_ready", + .reg = MLXPLAT_CPLD_LPC_REG_LC_RD_OFFSET, + .mask = BIT(0), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[0], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(0), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[0], + .slot = 1, + }, + { + .label = "lc2_ready", + .reg = MLXPLAT_CPLD_LPC_REG_LC_RD_OFFSET, + .mask = BIT(1), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[1], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(1), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[1], + .slot = 2, + }, + { + .label = "lc3_ready", + .reg = MLXPLAT_CPLD_LPC_REG_LC_RD_OFFSET, + .mask = BIT(2), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[2], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(2), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[2], + .slot = 3, + }, + { + .label = "lc4_ready", + .reg = MLXPLAT_CPLD_LPC_REG_LC_RD_OFFSET, + .mask = BIT(3), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[3], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(3), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[3], + .slot = 4, + }, + { + .label = "lc5_ready", + .reg = MLXPLAT_CPLD_LPC_REG_LC_RD_OFFSET, + .mask = BIT(4), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[4], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(4), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[4], + .slot = 5, + }, + { + .label = "lc6_ready", + .reg = MLXPLAT_CPLD_LPC_REG_LC_RD_OFFSET, + .mask = BIT(5), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[5], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(5), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[5], + .slot = 6, + }, + { + .label = "lc7_ready", + .reg = MLXPLAT_CPLD_LPC_REG_LC_RD_OFFSET, + .mask = BIT(6), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[6], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(6), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[6], + .slot = 7, + }, + { + .label = "lc8_ready", + .reg = MLXPLAT_CPLD_LPC_REG_LC_RD_OFFSET, + .mask = BIT(7), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[7], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(7), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[7], + .slot = 8, + }, +}; + +static struct mlxreg_core_data mlxplat_mlxcpld_modular_lc_synced_data[] = { + { + .label = "lc1_synced", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .mask = BIT(0), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[0], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(0), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[0], + .slot = 1, + }, + { + .label = "lc2_synced", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .mask = BIT(1), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[1], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(1), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[1], + .slot = 2, + }, + { + .label = "lc3_synced", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .mask = BIT(2), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[2], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(2), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[2], + .slot = 3, + }, + { + .label = "lc4_synced", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .mask = BIT(3), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[3], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(3), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[3], + .slot = 4, + }, + { + .label = "lc5_synced", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .mask = BIT(4), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[4], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(4), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[4], + .slot = 5, + }, + { + .label = "lc6_synced", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .mask = BIT(5), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[5], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(5), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[5], + .slot = 6, + }, + { + .label = "lc7_synced", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .mask = BIT(6), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[6], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(6), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[6], + .slot = 7, + }, + { + .label = "lc8_synced", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .mask = BIT(7), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[7], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(7), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[7], + .slot = 8, + }, +}; + +static struct mlxreg_core_data mlxplat_mlxcpld_modular_lc_act_data[] = { + { + .label = "lc1_active", + .reg = MLXPLAT_CPLD_LPC_REG_LC_OK_OFFSET, + .mask = BIT(0), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[0], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(0), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[0], + .slot = 1, + }, + { + .label = "lc2_active", + .reg = MLXPLAT_CPLD_LPC_REG_LC_OK_OFFSET, + .mask = BIT(1), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[1], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(1), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[1], + .slot = 2, + }, + { + .label = "lc3_active", + .reg = MLXPLAT_CPLD_LPC_REG_LC_OK_OFFSET, + .mask = BIT(2), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[2], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(2), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[2], + .slot = 3, + }, + { + .label = "lc4_active", + .reg = MLXPLAT_CPLD_LPC_REG_LC_OK_OFFSET, + .mask = BIT(3), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[3], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(3), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[3], + .slot = 4, + }, + { + .label = "lc5_active", + .reg = MLXPLAT_CPLD_LPC_REG_LC_OK_OFFSET, + .mask = BIT(4), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[4], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(4), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[4], + .slot = 5, + }, + { + .label = "lc6_active", + .reg = MLXPLAT_CPLD_LPC_REG_LC_OK_OFFSET, + .mask = BIT(5), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[5], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(5), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[5], + .slot = 6, + }, + { + .label = "lc7_active", + .reg = MLXPLAT_CPLD_LPC_REG_LC_OK_OFFSET, + .mask = BIT(6), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[6], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(6), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[6], + .slot = 7, + }, + { + .label = "lc8_active", + .reg = MLXPLAT_CPLD_LPC_REG_LC_OK_OFFSET, + .mask = BIT(7), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[7], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(7), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[7], + .slot = 8, + }, +}; + +static struct mlxreg_core_data mlxplat_mlxcpld_modular_lc_sd_data[] = { + { + .label = "lc1_shutdown", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SD_OFFSET, + .mask = BIT(0), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[0], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(0), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[0], + .slot = 1, + }, + { + .label = "lc2_shutdown", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SD_OFFSET, + .mask = BIT(1), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[1], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(1), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[1], + .slot = 2, + }, + { + .label = "lc3_shutdown", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SD_OFFSET, + .mask = BIT(2), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[2], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(2), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[2], + .slot = 3, + }, + { + .label = "lc4_shutdown", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SD_OFFSET, + .mask = BIT(3), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[3], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(3), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[3], + .slot = 4, + }, + { + .label = "lc5_shutdown", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SD_OFFSET, + .mask = BIT(4), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[4], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(4), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[4], + .slot = 5, + }, + { + .label = "lc6_shutdown", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SD_OFFSET, + .mask = BIT(5), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[5], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(5), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[5], + .slot = 6, + }, + { + .label = "lc7_shutdown", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SD_OFFSET, + .mask = BIT(6), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[6], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(6), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[6], + .slot = 7, + }, + { + .label = "lc8_shutdown", + .reg = MLXPLAT_CPLD_LPC_REG_LC_SD_OFFSET, + .mask = BIT(7), + .hpdev.brdinfo = &mlxplat_mlxcpld_lc_i2c_dev[7], + .hpdev.nr = MLXPLAT_CPLD_NR_LC_SET(7), + .hpdev.action = MLXREG_HOTPLUG_DEVICE_NO_ACTION, + .hpdev.notifier = &mlxplat_mlxcpld_modular_lc_notifier[7], + .slot = 8, + }, +}; + +static struct mlxreg_core_item mlxplat_mlxcpld_modular_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_modular_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_modular_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_modular_asic_items_data), + .inversed = 0, + .health = true, + }, + { + .data = mlxplat_mlxcpld_modular_lc_pr_items_data, + .kind = MLXREG_HOTPLUG_LC_PRESENT, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_LC, + .reg = MLXPLAT_CPLD_LPC_REG_LC_IN_OFFSET, + .mask = MLXPLAT_CPLD_LPC_LC_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_modular_lc_pr_items_data), + .inversed = 1, + .health = false, + }, + { + .data = mlxplat_mlxcpld_modular_lc_ver_items_data, + .kind = MLXREG_HOTPLUG_LC_VERIFIED, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_LC, + .reg = MLXPLAT_CPLD_LPC_REG_LC_VR_OFFSET, + .mask = MLXPLAT_CPLD_LPC_LC_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_modular_lc_ver_items_data), + .inversed = 0, + .health = false, + }, + { + .data = mlxplat_mlxcpld_modular_lc_pg_data, + .kind = MLXREG_HOTPLUG_LC_POWERED, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_LC, + .reg = MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET, + .mask = MLXPLAT_CPLD_LPC_LC_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_modular_lc_pg_data), + .inversed = 0, + .health = false, + }, + { + .data = mlxplat_mlxcpld_modular_lc_ready_data, + .kind = MLXREG_HOTPLUG_LC_READY, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_LC, + .reg = MLXPLAT_CPLD_LPC_REG_LC_RD_OFFSET, + .mask = MLXPLAT_CPLD_LPC_LC_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_modular_lc_ready_data), + .inversed = 0, + .health = false, + }, + { + .data = mlxplat_mlxcpld_modular_lc_synced_data, + .kind = MLXREG_HOTPLUG_LC_SYNCED, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_LC, + .reg = MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET, + .mask = MLXPLAT_CPLD_LPC_LC_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_modular_lc_synced_data), + .inversed = 0, + .health = false, + }, + { + .data = mlxplat_mlxcpld_modular_lc_act_data, + .kind = MLXREG_HOTPLUG_LC_ACTIVE, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_LC, + .reg = MLXPLAT_CPLD_LPC_REG_LC_OK_OFFSET, + .mask = MLXPLAT_CPLD_LPC_LC_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_modular_lc_act_data), + .inversed = 0, + .health = false, + }, + { + .data = mlxplat_mlxcpld_modular_lc_sd_data, + .kind = MLXREG_HOTPLUG_LC_THERMAL, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_LC, + .reg = MLXPLAT_CPLD_LPC_REG_LC_SD_OFFSET, + .mask = MLXPLAT_CPLD_LPC_LC_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_modular_lc_sd_data), + .inversed = 0, + .health = false, + }, +}; + +static +struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_modular_data = { + .items = mlxplat_mlxcpld_modular_items, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_modular_items), + .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET, + .mask = MLXPLAT_CPLD_AGGR_MASK_MODULAR, + .cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET, + .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW, +}; + /* Platform led default data */ static struct mlxreg_core_data mlxplat_mlxcpld_default_led_data[] = { { @@ -1037,6 +2073,35 @@ static struct mlxreg_core_platform_data mlxplat_default_led_data = { .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_led_data), }; +/* Platform led default data for water cooling */ +static struct mlxreg_core_data mlxplat_mlxcpld_default_led_wc_data[] = { + { + .label = "status:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + }, + { + .label = "status:red", + .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK + }, + { + .label = "psu:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET, + .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + }, + { + .label = "psu:red", + .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET, + .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + }, +}; + +static struct mlxreg_core_platform_data mlxplat_default_led_wc_data = { + .data = mlxplat_mlxcpld_default_led_wc_data, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_led_wc_data), +}; + /* Platform led MSN21xx system family data */ static struct mlxreg_core_data mlxplat_mlxcpld_msn21xx_led_data[] = { { @@ -1198,6 +2263,20 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_led_data[] = { .bit = BIT(5), }, { + .label = "fan7:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED6_OFFSET, + .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(6), + }, + { + .label = "fan7:orange", + .reg = MLXPLAT_CPLD_LPC_REG_LED6_OFFSET, + .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(6), + }, + { .label = "uid:blue", .reg = MLXPLAT_CPLD_LPC_REG_LED5_OFFSET, .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, @@ -1283,6 +2362,158 @@ static struct mlxreg_core_platform_data mlxplat_comex_100G_led_data = { .counter = ARRAY_SIZE(mlxplat_mlxcpld_comex_100G_led_data), }; +/* Platform led for data for modular systems */ +static struct mlxreg_core_data mlxplat_mlxcpld_modular_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 = "psu:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET, + .mask = MLXPLAT_CPLD_LED_HI_NIBBLE_MASK, + }, + { + .label = "psu:orange", + .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET, + .mask = MLXPLAT_CPLD_LED_HI_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 = "fan7:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED6_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(6), + }, + { + .label = "fan7:orange", + .reg = MLXPLAT_CPLD_LPC_REG_LED6_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + .capability = MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET, + .bit = BIT(6), + }, + { + .label = "uid:blue", + .reg = MLXPLAT_CPLD_LPC_REG_LED5_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + }, + { + .label = "fan_front:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED6_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + }, + { + .label = "fan_front:orange", + .reg = MLXPLAT_CPLD_LPC_REG_LED6_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + }, + { + .label = "mgmt:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED7_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + }, + { + .label = "mgmt:orange", + .reg = MLXPLAT_CPLD_LPC_REG_LED7_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + }, +}; + +static struct mlxreg_core_platform_data mlxplat_modular_led_data = { + .data = mlxplat_mlxcpld_modular_led_data, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_modular_led_data), +}; + /* Platform register access default */ static struct mlxreg_core_data mlxplat_mlxcpld_default_regs_io_data[] = { { @@ -1771,6 +3002,30 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = { .mode = 0444, }, { + .label = "bios_safe_mode", + .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0444, + }, + { + .label = "bios_active_image", + .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0444, + }, + { + .label = "bios_auth_fail", + .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0444, + }, + { + .label = "bios_upgrade_fail", + .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0444, + }, + { .label = "voltreg_update_status", .reg = MLXPLAT_CPLD_LPC_REG_GP0_RO_OFFSET, .mask = MLXPLAT_CPLD_VOLTREG_UPD_MASK, @@ -1814,6 +3069,484 @@ static struct mlxreg_core_platform_data mlxplat_default_ng_regs_io_data = { .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_ng_regs_io_data), }; +/* Platform register access for modular systems families data */ +static struct mlxreg_core_data mlxplat_mlxcpld_modular_regs_io_data[] = { + { + .label = "cpld1_version", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld2_version", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD2_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld3_version", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD3_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld4_version", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD4_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld1_pn", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD1_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld2_pn", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD2_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld3_pn", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD3_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld4_pn", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD4_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld1_version_min", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD1_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld2_version_min", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD2_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld3_version_min", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD3_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld4_version_min", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD4_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "lc1_enable", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0644, + }, + { + .label = "lc2_enable", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0644, + }, + { + .label = "lc3_enable", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0644, + }, + { + .label = "lc4_enable", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0644, + }, + { + .label = "lc5_enable", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0644, + }, + { + .label = "lc6_enable", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0644, + }, + { + .label = "lc7_enable", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0644, + }, + { + .label = "lc8_enable", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0644, + }, + { + .label = "reset_long_pb", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0444, + }, + { + .label = "reset_short_pb", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0444, + }, + { + .label = "reset_aux_pwr_or_fu", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0444, + }, + { + .label = "reset_mgmt_dc_dc_pwr_fail", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0444, + }, + { + .label = "reset_sys_comex_bios", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0444, + }, + { + .label = "reset_sw_reset", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0444, + }, + { + .label = "reset_aux_pwr_or_reload", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0444, + }, + { + .label = "reset_comex_pwr_fail", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0444, + }, + { + .label = "reset_platform", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0444, + }, + { + .label = "reset_soc", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0444, + }, + { + .label = "reset_pwr_off_from_carrier", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0444, + }, + { + .label = "reset_swb_wd", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0444, + }, + { + .label = "reset_swb_aux_pwr_or_fu", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0444, + }, + { + .label = "reset_swb_dc_dc_pwr_fail", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0444, + }, + { + .label = "reset_swb_12v_fail", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0444, + }, + { + .label = "reset_system", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0444, + }, + { + .label = "reset_thermal_spc_or_pciesw", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0444, + }, + { + .label = "bios_safe_mode", + .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0444, + }, + { + .label = "bios_active_image", + .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0444, + }, + { + .label = "bios_auth_fail", + .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0444, + }, + { + .label = "bios_upgrade_fail", + .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0444, + }, + { + .label = "voltreg_update_status", + .reg = MLXPLAT_CPLD_LPC_REG_GP0_RO_OFFSET, + .mask = MLXPLAT_CPLD_VOLTREG_UPD_MASK, + .bit = 5, + .mode = 0444, + }, + { + .label = "vpd_wp", + .reg = MLXPLAT_CPLD_LPC_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0644, + }, + { + .label = "pcie_asic_reset_dis", + .reg = MLXPLAT_CPLD_LPC_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0644, + }, + { + .label = "shutdown_unlock", + .reg = MLXPLAT_CPLD_LPC_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0644, + }, + { + .label = "lc1_rst_mask", + .reg = MLXPLAT_CPLD_LPC_REG_GP_RST_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0200, + }, + { + .label = "lc2_rst_mask", + .reg = MLXPLAT_CPLD_LPC_REG_GP_RST_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0200, + }, + { + .label = "lc3_rst_mask", + .reg = MLXPLAT_CPLD_LPC_REG_GP_RST_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0200, + }, + { + .label = "lc4_rst_mask", + .reg = MLXPLAT_CPLD_LPC_REG_GP_RST_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0200, + }, + { + .label = "lc5_rst_mask", + .reg = MLXPLAT_CPLD_LPC_REG_GP_RST_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0200, + }, + { + .label = "lc6_rst_mask", + .reg = MLXPLAT_CPLD_LPC_REG_GP_RST_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0200, + }, + { + .label = "lc7_rst_mask", + .reg = MLXPLAT_CPLD_LPC_REG_GP_RST_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0200, + }, + { + .label = "lc8_rst_mask", + .reg = MLXPLAT_CPLD_LPC_REG_GP_RST_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0200, + }, + { + .label = "psu1_on", + .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0200, + }, + { + .label = "psu2_on", + .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0200, + }, + { + .label = "pwr_cycle", + .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0200, + }, + { + .label = "pwr_down", + .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0200, + }, + { + .label = "psu3_on", + .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0200, + }, + { + .label = "psu4_on", + .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0200, + }, + { + .label = "auto_power_mode", + .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0644, + }, + { + .label = "pm_mgmt_en", + .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0644, + }, + { + .label = "jtag_enable", + .reg = MLXPLAT_CPLD_LPC_REG_FIELD_UPGRADE, + .mask = GENMASK(3, 0), + .bit = 1, + .mode = 0644, + }, + { + .label = "safe_bios_dis", + .reg = MLXPLAT_CPLD_LPC_SAFE_BIOS_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0644, + }, + { + .label = "safe_bios_dis_wp", + .reg = MLXPLAT_CPLD_LPC_SAFE_BIOS_WP_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0644, + }, + { + .label = "asic_health", + .reg = MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET, + .mask = MLXPLAT_CPLD_ASIC_MASK, + .bit = 1, + .mode = 0444, + }, + { + .label = "fan_dir", + .reg = MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "lc1_pwr", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0644, + }, + { + .label = "lc2_pwr", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0644, + }, + { + .label = "lc3_pwr", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0644, + }, + { + .label = "lc4_pwr", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0644, + }, + { + .label = "lc5_pwr", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0644, + }, + { + .label = "lc6_pwr", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0644, + }, + { + .label = "lc7_pwr", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0644, + }, + { + .label = "lc8_pwr", + .reg = MLXPLAT_CPLD_LPC_REG_LC_PWR_ON, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0644, + }, + { + .label = "config1", + .reg = MLXPLAT_CPLD_LPC_REG_CONFIG1_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "config2", + .reg = MLXPLAT_CPLD_LPC_REG_CONFIG2_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "ufm_version", + .reg = MLXPLAT_CPLD_LPC_REG_UFM_VERSION_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, +}; + +static struct mlxreg_core_platform_data mlxplat_modular_regs_io_data = { + .data = mlxplat_mlxcpld_modular_regs_io_data, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_modular_regs_io_data), +}; + /* Platform FAN default */ static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_data[] = { { @@ -1821,6 +3554,18 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_data[] = { .reg = MLXPLAT_CPLD_LPC_REG_PWM1_OFFSET, }, { + .label = "pwm2", + .reg = MLXPLAT_CPLD_LPC_REG_PWM2_OFFSET, + }, + { + .label = "pwm3", + .reg = MLXPLAT_CPLD_LPC_REG_PWM3_OFFSET, + }, + { + .label = "pwm4", + .reg = MLXPLAT_CPLD_LPC_REG_PWM4_OFFSET, + }, + { .label = "tacho1", .reg = MLXPLAT_CPLD_LPC_REG_TACHO1_OFFSET, .mask = GENMASK(7, 0), @@ -1918,6 +3663,20 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_data[] = { .reg_prsnt = MLXPLAT_CPLD_LPC_REG_FAN_OFFSET, }, { + .label = "tacho13", + .reg = MLXPLAT_CPLD_LPC_REG_TACHO13_OFFSET, + .mask = GENMASK(7, 0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET, + .bit = BIT(4), + }, + { + .label = "tacho14", + .reg = MLXPLAT_CPLD_LPC_REG_TACHO14_OFFSET, + .mask = GENMASK(7, 0), + .capability = MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET, + .bit = BIT(5), + }, + { .label = "conf", .capability = MLXPLAT_CPLD_LPC_REG_TACHO_SPEED_OFFSET, }, @@ -2152,16 +3911,23 @@ static struct mlxreg_core_platform_data mlxplat_mlxcpld_wd_set_type3[] = { static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { + case MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET: case MLXPLAT_CPLD_LPC_REG_LED1_OFFSET: case MLXPLAT_CPLD_LPC_REG_LED2_OFFSET: case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET: case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET: case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LED6_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LED7_OFFSET: case MLXPLAT_CPLD_LPC_REG_GP0_OFFSET: + case MLXPLAT_CPLD_LPC_REG_GP_RST_OFFSET: case MLXPLAT_CPLD_LPC_REG_GP1_OFFSET: case MLXPLAT_CPLD_LPC_REG_WP1_OFFSET: case MLXPLAT_CPLD_LPC_REG_GP2_OFFSET: case MLXPLAT_CPLD_LPC_REG_WP2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_FIELD_UPGRADE: + 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_AGGRLO_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET: @@ -2174,6 +3940,23 @@ 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_AGGRLC_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_IN_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_IN_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_VR_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_VR_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_PG_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_PG_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_RD_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_RD_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_OK_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_OK_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_SN_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_SN_MASK_OFFSET: + 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_WD_CLEAR_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD1_TMR_OFFSET: @@ -2185,6 +3968,9 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_WD3_TLEFT_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM1_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWM2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWM3_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWM4_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET: return true; } @@ -2199,9 +3985,14 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_CPLD3_VER_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD4_VER_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD1_PN_OFFSET: + case MLXPLAT_CPLD_LPC_REG_CPLD1_PN1_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD2_PN_OFFSET: + case MLXPLAT_CPLD_LPC_REG_CPLD2_PN1_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD3_PN_OFFSET: + case MLXPLAT_CPLD_LPC_REG_CPLD3_PN1_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD4_PN_OFFSET: + case MLXPLAT_CPLD_LPC_REG_CPLD4_PN1_OFFSET: + case MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET: case MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET: case MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET: case MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET: @@ -2210,13 +4001,20 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET: case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET: case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LED6_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LED7_OFFSET: case MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION: case MLXPLAT_CPLD_LPC_REG_GP0_RO_OFFSET: + case MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET: case MLXPLAT_CPLD_LPC_REG_GP0_OFFSET: + case MLXPLAT_CPLD_LPC_REG_GP_RST_OFFSET: case MLXPLAT_CPLD_LPC_REG_GP1_OFFSET: case MLXPLAT_CPLD_LPC_REG_WP1_OFFSET: case MLXPLAT_CPLD_LPC_REG_GP2_OFFSET: case MLXPLAT_CPLD_LPC_REG_WP2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_FIELD_UPGRADE: + case MLXPLAT_CPLD_LPC_SAFE_BIOS_OFFSET: + 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_AGGRLO_OFFSET: @@ -2237,6 +4035,30 @@ 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_AGGRLC_OFFSET: + case MLXPLAT_CPLD_LPC_REG_AGGRLC_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_IN_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_IN_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_IN_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_VR_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_VR_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_VR_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_PG_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_PG_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_RD_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_RD_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_RD_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_OK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_OK_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_OK_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_SN_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_SN_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_SD_OFFSET: + 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_WD_CLEAR_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD_CLEAR_WP_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD1_TMR_OFFSET: @@ -2252,6 +4074,9 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_CPLD3_MVER_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD4_MVER_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM1_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWM2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWM3_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWM4_OFFSET: case MLXPLAT_CPLD_LPC_REG_TACHO1_OFFSET: case MLXPLAT_CPLD_LPC_REG_TACHO2_OFFSET: case MLXPLAT_CPLD_LPC_REG_TACHO3_OFFSET: @@ -2264,12 +4089,15 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_TACHO10_OFFSET: case MLXPLAT_CPLD_LPC_REG_TACHO11_OFFSET: case MLXPLAT_CPLD_LPC_REG_TACHO12_OFFSET: + case MLXPLAT_CPLD_LPC_REG_TACHO13_OFFSET: + case MLXPLAT_CPLD_LPC_REG_TACHO14_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET: case MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET: case MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET: case MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET: case MLXPLAT_CPLD_LPC_REG_TACHO_SPEED_OFFSET: case MLXPLAT_CPLD_LPC_REG_PSU_I2C_CAP_OFFSET: + case MLXPLAT_CPLD_LPC_REG_SLOT_QTY_OFFSET: case MLXPLAT_CPLD_LPC_REG_CONFIG1_OFFSET: case MLXPLAT_CPLD_LPC_REG_CONFIG2_OFFSET: case MLXPLAT_CPLD_LPC_REG_UFM_VERSION_OFFSET: @@ -2286,9 +4114,14 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_CPLD3_VER_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD4_VER_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD1_PN_OFFSET: + case MLXPLAT_CPLD_LPC_REG_CPLD1_PN1_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD2_PN_OFFSET: + case MLXPLAT_CPLD_LPC_REG_CPLD2_PN1_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD3_PN_OFFSET: + case MLXPLAT_CPLD_LPC_REG_CPLD3_PN1_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD4_PN_OFFSET: + case MLXPLAT_CPLD_LPC_REG_CPLD4_PN1_OFFSET: + case MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET: case MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET: case MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET: case MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET: @@ -2297,11 +4130,18 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_LED3_OFFSET: case MLXPLAT_CPLD_LPC_REG_LED4_OFFSET: case MLXPLAT_CPLD_LPC_REG_LED5_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LED6_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LED7_OFFSET: case MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION: case MLXPLAT_CPLD_LPC_REG_GP0_RO_OFFSET: + case MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET: case MLXPLAT_CPLD_LPC_REG_GP0_OFFSET: + case MLXPLAT_CPLD_LPC_REG_GP_RST_OFFSET: case MLXPLAT_CPLD_LPC_REG_GP1_OFFSET: case MLXPLAT_CPLD_LPC_REG_GP2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_FIELD_UPGRADE: + case MLXPLAT_CPLD_LPC_SAFE_BIOS_OFFSET: + 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_AGGRLO_OFFSET: @@ -2322,6 +4162,30 @@ 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_AGGRLC_OFFSET: + case MLXPLAT_CPLD_LPC_REG_AGGRLC_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_IN_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_IN_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_IN_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_VR_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_VR_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_VR_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_PG_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_PG_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_PG_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_RD_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_RD_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_RD_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_OK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_OK_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_OK_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_SN_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_SN_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_SN_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_LC_SD_OFFSET: + 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_WD2_TMR_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD2_TLEFT_OFFSET: case MLXPLAT_CPLD_LPC_REG_WD3_TMR_OFFSET: @@ -2331,6 +4195,9 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_CPLD3_MVER_OFFSET: case MLXPLAT_CPLD_LPC_REG_CPLD4_MVER_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM1_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWM2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWM3_OFFSET: + case MLXPLAT_CPLD_LPC_REG_PWM4_OFFSET: case MLXPLAT_CPLD_LPC_REG_TACHO1_OFFSET: case MLXPLAT_CPLD_LPC_REG_TACHO2_OFFSET: case MLXPLAT_CPLD_LPC_REG_TACHO3_OFFSET: @@ -2343,12 +4210,15 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_TACHO10_OFFSET: case MLXPLAT_CPLD_LPC_REG_TACHO11_OFFSET: case MLXPLAT_CPLD_LPC_REG_TACHO12_OFFSET: + case MLXPLAT_CPLD_LPC_REG_TACHO13_OFFSET: + case MLXPLAT_CPLD_LPC_REG_TACHO14_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET: case MLXPLAT_CPLD_LPC_REG_FAN_CAP1_OFFSET: case MLXPLAT_CPLD_LPC_REG_FAN_CAP2_OFFSET: case MLXPLAT_CPLD_LPC_REG_FAN_DRW_CAP_OFFSET: case MLXPLAT_CPLD_LPC_REG_TACHO_SPEED_OFFSET: case MLXPLAT_CPLD_LPC_REG_PSU_I2C_CAP_OFFSET: + case MLXPLAT_CPLD_LPC_REG_SLOT_QTY_OFFSET: case MLXPLAT_CPLD_LPC_REG_CONFIG1_OFFSET: case MLXPLAT_CPLD_LPC_REG_CONFIG2_OFFSET: case MLXPLAT_CPLD_LPC_REG_UFM_VERSION_OFFSET: @@ -2382,6 +4252,19 @@ 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_eth_modular[] = { + { MLXPLAT_CPLD_LPC_REG_GP2_OFFSET, 0x61 }, + { MLXPLAT_CPLD_LPC_REG_PWM_CONTROL_OFFSET, 0x00 }, + { MLXPLAT_CPLD_LPC_REG_PWM2_OFFSET, 0x00 }, + { MLXPLAT_CPLD_LPC_REG_PWM3_OFFSET, 0x00 }, + { MLXPLAT_CPLD_LPC_REG_PWM4_OFFSET, 0x00 }, + { MLXPLAT_CPLD_LPC_REG_WD1_ACT_OFFSET, 0x00 }, + { MLXPLAT_CPLD_LPC_REG_WD2_ACT_OFFSET, 0x00 }, + { MLXPLAT_CPLD_LPC_REG_WD3_ACT_OFFSET, 0x00 }, + { MLXPLAT_CPLD_LPC_REG_AGGRLC_MASK_OFFSET, + MLXPLAT_CPLD_AGGR_MASK_LC_LOW }, +}; + struct mlxplat_mlxcpld_regmap_context { void __iomem *base; }; @@ -2462,8 +4345,22 @@ 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_eth_modular = { + .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_eth_modular, + .num_reg_defaults = ARRAY_SIZE(mlxplat_mlxcpld_regmap_eth_modular), + .reg_read = mlxplat_mlxcpld_reg_read, + .reg_write = mlxplat_mlxcpld_reg_write, +}; + static struct resource mlxplat_mlxcpld_resources[] = { - [0] = DEFINE_RES_IRQ_NAMED(17, "mlxreg-hotplug"), + [0] = DEFINE_RES_IRQ_NAMED(MLXPLAT_CPLD_LPC_SYSIRQ, "mlxreg-hotplug"), }; static struct platform_device *mlxplat_dev; @@ -2498,6 +4395,28 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) return 1; } +static int __init mlxplat_dmi_default_wc_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; + for (i = 0; i < mlxplat_mux_num; i++) { + mlxplat_mux_data[i].values = mlxplat_default_channels[i]; + mlxplat_mux_data[i].n_values = + ARRAY_SIZE(mlxplat_default_channels[i]); + } + mlxplat_hotplug = &mlxplat_mlxcpld_default_wc_data; + mlxplat_hotplug->deferred_nr = + mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; + 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]; + + return 1; +} + static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) { int i; @@ -2640,8 +4559,35 @@ static int __init mlxplat_dmi_ng400_matched(const struct dmi_system_id *dmi) return 1; } +static int __init mlxplat_dmi_modular_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_modular_mux_data); + mlxplat_mux_data = mlxplat_modular_mux_data; + mlxplat_hotplug = &mlxplat_mlxcpld_modular_data; + mlxplat_hotplug->deferred_nr = MLXPLAT_CPLD_CH4_ETH_MODULAR; + mlxplat_led = &mlxplat_modular_led_data; + mlxplat_regs_io = &mlxplat_modular_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_eth_modular; + + return 1; +} + static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { { + .callback = mlxplat_dmi_default_wc_matched, + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "VMOD0001"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI138"), + }, + }, + { .callback = mlxplat_dmi_default_matched, .matches = { DMI_MATCH(DMI_BOARD_NAME, "VMOD0001"), @@ -2690,6 +4636,12 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { }, }, { + .callback = mlxplat_dmi_modular_matched, + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "VMOD0011"), + }, + }, + { .callback = mlxplat_dmi_msn274x_matched, .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Mellanox Technologies"), diff --git a/drivers/platform/x86/nvidia-wmi-ec-backlight.c b/drivers/platform/x86/nvidia-wmi-ec-backlight.c new file mode 100644 index 000000000000..61e37194df70 --- /dev/null +++ b/drivers/platform/x86/nvidia-wmi-ec-backlight.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + */ + +#include <linux/acpi.h> +#include <linux/backlight.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/wmi.h> + +/** + * enum wmi_brightness_method - WMI method IDs + * @WMI_BRIGHTNESS_METHOD_LEVEL: Get/Set EC brightness level status + * @WMI_BRIGHTNESS_METHOD_SOURCE: Get/Set EC Brightness Source + */ +enum wmi_brightness_method { + WMI_BRIGHTNESS_METHOD_LEVEL = 1, + WMI_BRIGHTNESS_METHOD_SOURCE = 2, + WMI_BRIGHTNESS_METHOD_MAX +}; + +/** + * enum wmi_brightness_mode - Operation mode for WMI-wrapped method + * @WMI_BRIGHTNESS_MODE_GET: Get the current brightness level/source. + * @WMI_BRIGHTNESS_MODE_SET: Set the brightness level. + * @WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL: Get the maximum brightness level. This + * is only valid when the WMI method is + * %WMI_BRIGHTNESS_METHOD_LEVEL. + */ +enum wmi_brightness_mode { + WMI_BRIGHTNESS_MODE_GET = 0, + WMI_BRIGHTNESS_MODE_SET = 1, + WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL = 2, + WMI_BRIGHTNESS_MODE_MAX +}; + +/** + * enum wmi_brightness_source - Backlight brightness control source selection + * @WMI_BRIGHTNESS_SOURCE_GPU: Backlight brightness is controlled by the GPU. + * @WMI_BRIGHTNESS_SOURCE_EC: Backlight brightness is controlled by the + * system's Embedded Controller (EC). + * @WMI_BRIGHTNESS_SOURCE_AUX: Backlight brightness is controlled over the + * DisplayPort AUX channel. + */ +enum wmi_brightness_source { + WMI_BRIGHTNESS_SOURCE_GPU = 1, + WMI_BRIGHTNESS_SOURCE_EC = 2, + WMI_BRIGHTNESS_SOURCE_AUX = 3, + WMI_BRIGHTNESS_SOURCE_MAX +}; + +/** + * struct wmi_brightness_args - arguments for the WMI-wrapped ACPI method + * @mode: Pass in an &enum wmi_brightness_mode value to select between + * getting or setting a value. + * @val: In parameter for value to set when using %WMI_BRIGHTNESS_MODE_SET + * mode. Not used in conjunction with %WMI_BRIGHTNESS_MODE_GET or + * %WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL mode. + * @ret: Out parameter returning retrieved value when operating in + * %WMI_BRIGHTNESS_MODE_GET or %WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL + * mode. Not used in %WMI_BRIGHTNESS_MODE_SET mode. + * @ignored: Padding; not used. The ACPI method expects a 24 byte params struct. + * + * This is the parameters structure for the WmiBrightnessNotify ACPI method as + * wrapped by WMI. The value passed in to @val or returned by @ret will be a + * brightness value when the WMI method ID is %WMI_BRIGHTNESS_METHOD_LEVEL, or + * an &enum wmi_brightness_source value with %WMI_BRIGHTNESS_METHOD_SOURCE. + */ +struct wmi_brightness_args { + u32 mode; + u32 val; + u32 ret; + u32 ignored[3]; +}; + +/** + * wmi_brightness_notify() - helper function for calling WMI-wrapped ACPI method + * @w: Pointer to the struct wmi_device identified by %WMI_BRIGHTNESS_GUID + * @id: The WMI method ID to call (e.g. %WMI_BRIGHTNESS_METHOD_LEVEL or + * %WMI_BRIGHTNESS_METHOD_SOURCE) + * @mode: The operation to perform on the method (e.g. %WMI_BRIGHTNESS_MODE_SET + * or %WMI_BRIGHTNESS_MODE_GET) + * @val: Pointer to a value passed in by the caller when @mode is + * %WMI_BRIGHTNESS_MODE_SET, or a value passed out to caller when @mode + * is %WMI_BRIGHTNESS_MODE_GET or %WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL. + * + * Returns 0 on success, or a negative error number on failure. + */ +static int wmi_brightness_notify(struct wmi_device *w, enum wmi_brightness_method id, enum wmi_brightness_mode mode, u32 *val) +{ + struct wmi_brightness_args args = { + .mode = mode, + .val = 0, + .ret = 0, + }; + struct acpi_buffer buf = { (acpi_size)sizeof(args), &args }; + acpi_status status; + + if (id < WMI_BRIGHTNESS_METHOD_LEVEL || + id >= WMI_BRIGHTNESS_METHOD_MAX || + mode < WMI_BRIGHTNESS_MODE_GET || mode >= WMI_BRIGHTNESS_MODE_MAX) + return -EINVAL; + + if (mode == WMI_BRIGHTNESS_MODE_SET) + args.val = *val; + + status = wmidev_evaluate_method(w, 0, id, &buf, &buf); + if (ACPI_FAILURE(status)) { + dev_err(&w->dev, "EC backlight control failed: %s\n", + acpi_format_exception(status)); + return -EIO; + } + + if (mode != WMI_BRIGHTNESS_MODE_SET) + *val = args.ret; + + return 0; +} + +static int nvidia_wmi_ec_backlight_update_status(struct backlight_device *bd) +{ + struct wmi_device *wdev = bl_get_data(bd); + + return wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL, + WMI_BRIGHTNESS_MODE_SET, + &bd->props.brightness); +} + +static int nvidia_wmi_ec_backlight_get_brightness(struct backlight_device *bd) +{ + struct wmi_device *wdev = bl_get_data(bd); + u32 level; + int ret; + + ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL, + WMI_BRIGHTNESS_MODE_GET, &level); + if (ret < 0) + return ret; + + return level; +} + +static const struct backlight_ops nvidia_wmi_ec_backlight_ops = { + .update_status = nvidia_wmi_ec_backlight_update_status, + .get_brightness = nvidia_wmi_ec_backlight_get_brightness, +}; + +static int nvidia_wmi_ec_backlight_probe(struct wmi_device *wdev, const void *ctx) +{ + struct backlight_properties props = {}; + struct backlight_device *bdev; + u32 source; + int ret; + + ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_SOURCE, + WMI_BRIGHTNESS_MODE_GET, &source); + if (ret) + return ret; + + /* + * This driver is only to be used when brightness control is handled + * by the EC; otherwise, the GPU driver(s) should control brightness. + */ + if (source != WMI_BRIGHTNESS_SOURCE_EC) + return -ENODEV; + + /* + * Identify this backlight device as a firmware device so that it can + * be prioritized over any exposed GPU-driven raw device(s). + */ + props.type = BACKLIGHT_FIRMWARE; + + ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL, + WMI_BRIGHTNESS_MODE_GET_MAX_LEVEL, + &props.max_brightness); + if (ret) + return ret; + + ret = wmi_brightness_notify(wdev, WMI_BRIGHTNESS_METHOD_LEVEL, + WMI_BRIGHTNESS_MODE_GET, &props.brightness); + if (ret) + return ret; + + bdev = devm_backlight_device_register(&wdev->dev, + "nvidia_wmi_ec_backlight", + &wdev->dev, wdev, + &nvidia_wmi_ec_backlight_ops, + &props); + return PTR_ERR_OR_ZERO(bdev); +} + +#define WMI_BRIGHTNESS_GUID "603E9613-EF25-4338-A3D0-C46177516DB7" + +static const struct wmi_device_id nvidia_wmi_ec_backlight_id_table[] = { + { .guid_string = WMI_BRIGHTNESS_GUID }, + { } +}; +MODULE_DEVICE_TABLE(wmi, nvidia_wmi_ec_backlight_id_table); + +static struct wmi_driver nvidia_wmi_ec_backlight_driver = { + .driver = { + .name = "nvidia-wmi-ec-backlight", + }, + .probe = nvidia_wmi_ec_backlight_probe, + .id_table = nvidia_wmi_ec_backlight_id_table, +}; +module_wmi_driver(nvidia_wmi_ec_backlight_driver); + +MODULE_AUTHOR("Daniel Dadap <ddadap@nvidia.com>"); +MODULE_DESCRIPTION("NVIDIA WMI EC Backlight driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index d4f444401496..37850d07987d 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -470,7 +470,7 @@ static ssize_t numbatt_show(struct device *dev, struct device_attribute *attr, if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; - return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]); + return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_NUM_BATTERIES]); } static ssize_t lcdtype_show(struct device *dev, struct device_attribute *attr, @@ -482,7 +482,7 @@ static ssize_t lcdtype_show(struct device *dev, struct device_attribute *attr, if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; - return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_LCD_TYPE]); + return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_LCD_TYPE]); } static ssize_t mute_show(struct device *dev, struct device_attribute *attr, @@ -494,7 +494,7 @@ static ssize_t mute_show(struct device *dev, struct device_attribute *attr, if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; - return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_MUTE]); + return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_MUTE]); } static ssize_t mute_store(struct device *dev, struct device_attribute *attr, @@ -524,7 +524,7 @@ static ssize_t sticky_key_show(struct device *dev, struct device_attribute *attr if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; - return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sticky_key); + return sysfs_emit(buf, "%u\n", pcc->sticky_key); } static ssize_t sticky_key_store(struct device *dev, struct device_attribute *attr, @@ -566,7 +566,7 @@ static ssize_t eco_mode_show(struct device *dev, struct device_attribute *attr, result = -EIO; break; } - return snprintf(buf, PAGE_SIZE, "%u\n", result); + return sysfs_emit(buf, "%u\n", result); } static ssize_t eco_mode_store(struct device *dev, struct device_attribute *attr, @@ -625,7 +625,7 @@ static ssize_t ac_brightness_show(struct device *dev, struct device_attribute *a if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; - return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_AC_CUR_BRIGHT]); + return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_AC_CUR_BRIGHT]); } static ssize_t ac_brightness_store(struct device *dev, struct device_attribute *attr, @@ -655,7 +655,7 @@ static ssize_t dc_brightness_show(struct device *dev, struct device_attribute *a if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; - return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_DC_CUR_BRIGHT]); + return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_DC_CUR_BRIGHT]); } static ssize_t dc_brightness_store(struct device *dev, struct device_attribute *attr, @@ -685,7 +685,7 @@ static ssize_t current_brightness_show(struct device *dev, struct device_attribu if (!acpi_pcc_retrieve_biosdata(pcc)) return -EIO; - return snprintf(buf, PAGE_SIZE, "%u\n", pcc->sinf[SINF_CUR_BRIGHT]); + return sysfs_emit(buf, "%u\n", pcc->sinf[SINF_CUR_BRIGHT]); } static ssize_t current_brightness_store(struct device *dev, struct device_attribute *attr, @@ -710,7 +710,7 @@ static ssize_t current_brightness_store(struct device *dev, struct device_attrib static ssize_t cdpower_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", get_optd_power_state()); + return sysfs_emit(buf, "%d\n", get_optd_power_state()); } static ssize_t cdpower_store(struct device *dev, struct device_attribute *attr, diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 704813374922..d8d0c0bed5e9 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -964,7 +964,7 @@ static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *a if (item->validate) value = item->validate(SNC_VALIDATE_OUT, value); - return snprintf(buffer, PAGE_SIZE, "%d\n", value); + return sysfs_emit(buffer, "%d\n", value); } static ssize_t sony_nc_sysfs_store(struct device *dev, @@ -1811,9 +1811,7 @@ static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev, static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev, struct device_attribute *attr, char *buffer) { - ssize_t count = 0; - count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_ctl->mode); - return count; + return sysfs_emit(buffer, "%d\n", kbdbl_ctl->mode); } static int __sony_nc_kbd_backlight_timeout_set(u8 value) @@ -1855,9 +1853,7 @@ static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev, static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev, struct device_attribute *attr, char *buffer) { - ssize_t count = 0; - count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_ctl->timeout); - return count; + return sysfs_emit(buffer, "%d\n", kbdbl_ctl->timeout); } static int sony_nc_kbd_backlight_setup(struct platform_device *pd, @@ -2051,21 +2047,18 @@ static ssize_t sony_nc_battery_care_limit_show(struct device *dev, break; } - return snprintf(buffer, PAGE_SIZE, "%d\n", status); + return sysfs_emit(buffer, "%d\n", status); } static ssize_t sony_nc_battery_care_health_show(struct device *dev, struct device_attribute *attr, char *buffer) { - ssize_t count = 0; unsigned int health; if (sony_call_snc_handle(bcare_ctl->handle, 0x0200, &health)) return -EIO; - count = snprintf(buffer, PAGE_SIZE, "%d\n", health & 0xff); - - return count; + return sysfs_emit(buffer, "%d\n", health & 0xff); } static int sony_nc_battery_care_setup(struct platform_device *pd, @@ -2215,15 +2208,12 @@ static ssize_t sony_nc_thermal_mode_store(struct device *dev, static ssize_t sony_nc_thermal_mode_show(struct device *dev, struct device_attribute *attr, char *buffer) { - ssize_t count = 0; int mode = sony_nc_thermal_mode_get(); if (mode < 0) return mode; - count = snprintf(buffer, PAGE_SIZE, "%s\n", snc_thermal_profiles[mode]); - - return count; + return sysfs_emit(buffer, "%s\n", snc_thermal_profiles[mode]); } static int sony_nc_thermal_setup(struct platform_device *pd) @@ -2361,7 +2351,7 @@ static ssize_t sony_nc_lid_resume_show(struct device *dev, while (pos < LID_RESUME_MAX) { if (&lid_ctl->attrs[pos].attr == &attr->attr) - return snprintf(buffer, PAGE_SIZE, "%d\n", + return sysfs_emit(buffer, "%d\n", (lid_ctl->status >> pos) & 0x01); pos++; } @@ -2493,7 +2483,7 @@ static ssize_t sony_nc_gfx_switch_status_show(struct device *dev, if (pos < 0) return pos; - return snprintf(buffer, PAGE_SIZE, "%s\n", + return sysfs_emit(buffer, "%s\n", pos == SPEED ? "speed" : pos == STAMINA ? "stamina" : pos == AUTO ? "auto" : "unknown"); @@ -2568,7 +2558,7 @@ static ssize_t sony_nc_highspeed_charging_show(struct device *dev, if (sony_call_snc_handle(0x0131, 0x0100, &result)) return -EIO; - return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); + return sysfs_emit(buffer, "%d\n", result & 0x01); } static int sony_nc_highspeed_charging_setup(struct platform_device *pd) @@ -2642,7 +2632,7 @@ static ssize_t sony_nc_lowbatt_show(struct device *dev, if (sony_call_snc_handle(0x0121, 0x0200, &result)) return -EIO; - return snprintf(buffer, PAGE_SIZE, "%d\n", result & 1); + return sysfs_emit(buffer, "%d\n", result & 1); } static int sony_nc_lowbatt_setup(struct platform_device *pd) @@ -2708,7 +2698,7 @@ static ssize_t sony_nc_hsfan_show(struct device *dev, if (sony_call_snc_handle(0x0149, 0x0100, &result)) return -EIO; - return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); + return sysfs_emit(buffer, "%d\n", result & 0x01); } static ssize_t sony_nc_fanspeed_show(struct device *dev, @@ -2719,7 +2709,7 @@ static ssize_t sony_nc_fanspeed_show(struct device *dev, if (sony_call_snc_handle(0x0149, 0x0300, &result)) return -EIO; - return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0xff); + return sysfs_emit(buffer, "%d\n", result & 0xff); } static int sony_nc_fanspeed_setup(struct platform_device *pd) @@ -2815,7 +2805,7 @@ static ssize_t sony_nc_usb_charge_show(struct device *dev, if (sony_call_snc_handle(0x0155, 0x0000, &result)) return -EIO; - return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); + return sysfs_emit(buffer, "%d\n", result & 0x01); } static int sony_nc_usb_charge_setup(struct platform_device *pd) @@ -2870,7 +2860,7 @@ static ssize_t sony_nc_panelid_show(struct device *dev, if (sony_call_snc_handle(0x011D, 0x0000, &result)) return -EIO; - return snprintf(buffer, PAGE_SIZE, "%d\n", result); + return sysfs_emit(buffer, "%d\n", result); } static int sony_nc_panelid_setup(struct platform_device *pd) @@ -2998,7 +2988,7 @@ static ssize_t sony_nc_touchpad_show(struct device *dev, if (sony_call_snc_handle(tp_ctl->handle, 0x000, &result)) return -EINVAL; - return snprintf(buffer, PAGE_SIZE, "%d\n", !(result & 0x01)); + return sysfs_emit(buffer, "%d\n", !(result & 0x01)); } static int sony_nc_touchpad_setup(struct platform_device *pd, @@ -3915,7 +3905,7 @@ static ssize_t sony_pic_wwanpower_show(struct device *dev, { ssize_t count; mutex_lock(&spic_dev.lock); - count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.wwan_power); + count = sysfs_emit(buffer, "%d\n", spic_dev.wwan_power); mutex_unlock(&spic_dev.lock); return count; } @@ -3954,7 +3944,7 @@ static ssize_t sony_pic_bluetoothpower_show(struct device *dev, { ssize_t count = 0; mutex_lock(&spic_dev.lock); - count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.bluetooth_power); + count = sysfs_emit(buffer, "%d\n", spic_dev.bluetooth_power); mutex_unlock(&spic_dev.lock); return count; } @@ -3996,7 +3986,7 @@ static ssize_t sony_pic_fanspeed_show(struct device *dev, if (sony_pic_get_fanspeed(&value)) return -EIO; - return snprintf(buffer, PAGE_SIZE, "%d\n", value); + return sysfs_emit(buffer, "%d\n", value); } #define SPIC_ATTR(_name, _mode) \ diff --git a/drivers/platform/x86/system76_acpi.c b/drivers/platform/x86/system76_acpi.c index c14fd22ba196..8b292ee95a14 100644 --- a/drivers/platform/x86/system76_acpi.c +++ b/drivers/platform/x86/system76_acpi.c @@ -10,13 +10,20 @@ */ #include <linux/acpi.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> #include <linux/init.h> +#include <linux/input.h> #include <linux/kernel.h> #include <linux/leds.h> #include <linux/module.h> #include <linux/pci_ids.h> +#include <linux/power_supply.h> +#include <linux/sysfs.h> #include <linux/types.h> +#include <acpi/battery.h> + struct system76_data { struct acpi_device *acpi_dev; struct led_classdev ap_led; @@ -24,6 +31,10 @@ struct system76_data { enum led_brightness kb_brightness; enum led_brightness kb_toggle_brightness; int kb_color; + struct device *therm; + union acpi_object *nfan; + union acpi_object *ntmp; + struct input_dev *input; }; static const struct acpi_device_id device_ids[] = { @@ -63,9 +74,57 @@ static int system76_get(struct system76_data *data, char *method) handle = acpi_device_handle(data->acpi_dev); status = acpi_evaluate_integer(handle, method, NULL, &ret); if (ACPI_SUCCESS(status)) - return (int)ret; - else - return -1; + return ret; + return -ENODEV; +} + +// Get a System76 ACPI device value by name with index +static int system76_get_index(struct system76_data *data, char *method, int index) +{ + union acpi_object obj; + struct acpi_object_list obj_list; + acpi_handle handle; + acpi_status status; + unsigned long long ret = 0; + + obj.type = ACPI_TYPE_INTEGER; + obj.integer.value = index; + obj_list.count = 1; + obj_list.pointer = &obj; + + handle = acpi_device_handle(data->acpi_dev); + status = acpi_evaluate_integer(handle, method, &obj_list, &ret); + if (ACPI_SUCCESS(status)) + return ret; + return -ENODEV; +} + +// Get a System76 ACPI device object by name +static int system76_get_object(struct system76_data *data, char *method, union acpi_object **obj) +{ + acpi_handle handle; + acpi_status status; + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + + handle = acpi_device_handle(data->acpi_dev); + status = acpi_evaluate_object(handle, method, NULL, &buf); + if (ACPI_SUCCESS(status)) { + *obj = buf.pointer; + return 0; + } + + return -ENODEV; +} + +// Get a name from a System76 ACPI device object +static char *system76_name(union acpi_object *obj, int index) +{ + if (obj && obj->type == ACPI_TYPE_PACKAGE && index <= obj->package.count) { + if (obj->package.elements[index].type == ACPI_TYPE_STRING) + return obj->package.elements[index].string.pointer; + } + + return NULL; } // Set a System76 ACPI device value by name @@ -88,6 +147,154 @@ static int system76_set(struct system76_data *data, char *method, int value) return -1; } +#define BATTERY_THRESHOLD_INVALID 0xFF + +enum { + THRESHOLD_START, + THRESHOLD_END, +}; + +static ssize_t battery_get_threshold(int which, char *buf) +{ + struct acpi_object_list input; + union acpi_object param; + acpi_handle handle; + acpi_status status; + unsigned long long ret = BATTERY_THRESHOLD_INVALID; + + handle = ec_get_handle(); + if (!handle) + return -ENODEV; + + input.count = 1; + input.pointer = ¶m; + // Start/stop selection + param.type = ACPI_TYPE_INTEGER; + param.integer.value = which; + + status = acpi_evaluate_integer(handle, "GBCT", &input, &ret); + if (ACPI_FAILURE(status)) + return -EIO; + if (ret == BATTERY_THRESHOLD_INVALID) + return -EINVAL; + + return sysfs_emit(buf, "%d\n", (int)ret); +} + +static ssize_t battery_set_threshold(int which, const char *buf, size_t count) +{ + struct acpi_object_list input; + union acpi_object params[2]; + acpi_handle handle; + acpi_status status; + unsigned int value; + int ret; + + handle = ec_get_handle(); + if (!handle) + return -ENODEV; + + ret = kstrtouint(buf, 10, &value); + if (ret) + return ret; + + if (value > 100) + return -EINVAL; + + input.count = 2; + input.pointer = params; + // Start/stop selection + params[0].type = ACPI_TYPE_INTEGER; + params[0].integer.value = which; + // Threshold value + params[1].type = ACPI_TYPE_INTEGER; + params[1].integer.value = value; + + status = acpi_evaluate_object(handle, "SBCT", &input, NULL); + if (ACPI_FAILURE(status)) + return -EIO; + + return count; +} + +static ssize_t charge_control_start_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return battery_get_threshold(THRESHOLD_START, buf); +} + +static ssize_t charge_control_start_threshold_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return battery_set_threshold(THRESHOLD_START, buf, count); +} + +static DEVICE_ATTR_RW(charge_control_start_threshold); + +static ssize_t charge_control_end_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return battery_get_threshold(THRESHOLD_END, buf); +} + +static ssize_t charge_control_end_threshold_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return battery_set_threshold(THRESHOLD_END, buf, count); +} + +static DEVICE_ATTR_RW(charge_control_end_threshold); + +static struct attribute *system76_battery_attrs[] = { + &dev_attr_charge_control_start_threshold.attr, + &dev_attr_charge_control_end_threshold.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(system76_battery); + +static int system76_battery_add(struct power_supply *battery) +{ + // System76 EC only supports 1 battery + if (strcmp(battery->desc->name, "BAT0") != 0) + return -ENODEV; + + if (device_add_groups(&battery->dev, system76_battery_groups)) + return -ENODEV; + + return 0; +} + +static int system76_battery_remove(struct power_supply *battery) +{ + device_remove_groups(&battery->dev, system76_battery_groups); + return 0; +} + +static struct acpi_battery_hook system76_battery_hook = { + .add_battery = system76_battery_add, + .remove_battery = system76_battery_remove, + .name = "System76 Battery Extension", +}; + +static void system76_battery_init(void) +{ + acpi_handle handle; + + handle = ec_get_handle(); + if (handle && acpi_has_method(handle, "GBCT")) + battery_hook_register(&system76_battery_hook); +} + +static void system76_battery_exit(void) +{ + acpi_handle handle; + + handle = ec_get_handle(); + if (handle && acpi_has_method(handle, "GBCT")) + battery_hook_unregister(&system76_battery_hook); +} + // Get the airplane mode LED brightness static enum led_brightness ap_led_get(struct led_classdev *led) { @@ -141,7 +348,7 @@ static ssize_t kb_led_color_show( led = (struct led_classdev *)dev->driver_data; data = container_of(led, struct system76_data, kb_led); - return sprintf(buf, "%06X\n", data->kb_color); + return sysfs_emit(buf, "%06X\n", data->kb_color); } // Set the keyboard LED color @@ -169,7 +376,7 @@ static ssize_t kb_led_color_store( return size; } -static const struct device_attribute kb_led_color_dev_attr = { +static struct device_attribute dev_attr_kb_led_color = { .attr = { .name = "color", .mode = 0644, @@ -178,6 +385,13 @@ static const struct device_attribute kb_led_color_dev_attr = { .store = kb_led_color_store, }; +static struct attribute *system76_kb_led_color_attrs[] = { + &dev_attr_kb_led_color.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(system76_kb_led_color); + // Notify that the keyboard LED was changed by hardware static void kb_led_notify(struct system76_data *data) { @@ -270,6 +484,155 @@ static void kb_led_hotkey_color(struct system76_data *data) kb_led_notify(data); } +static umode_t thermal_is_visible(const void *drvdata, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct system76_data *data = drvdata; + + switch (type) { + case hwmon_fan: + case hwmon_pwm: + if (system76_name(data->nfan, channel)) + return 0444; + break; + + case hwmon_temp: + if (system76_name(data->ntmp, channel)) + return 0444; + break; + + default: + return 0; + } + + return 0; +} + +static int thermal_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + struct system76_data *data = dev_get_drvdata(dev); + int raw; + + switch (type) { + case hwmon_fan: + if (attr == hwmon_fan_input) { + raw = system76_get_index(data, "GFAN", channel); + if (raw < 0) + return raw; + *val = (raw >> 8) & 0xFFFF; + return 0; + } + break; + + case hwmon_pwm: + if (attr == hwmon_pwm_input) { + raw = system76_get_index(data, "GFAN", channel); + if (raw < 0) + return raw; + *val = raw & 0xFF; + return 0; + } + break; + + case hwmon_temp: + if (attr == hwmon_temp_input) { + raw = system76_get_index(data, "GTMP", channel); + if (raw < 0) + return raw; + *val = raw * 1000; + return 0; + } + break; + + default: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} + +static int thermal_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) +{ + struct system76_data *data = dev_get_drvdata(dev); + + switch (type) { + case hwmon_fan: + if (attr == hwmon_fan_label) { + *str = system76_name(data->nfan, channel); + if (*str) + return 0; + } + break; + + case hwmon_temp: + if (attr == hwmon_temp_label) { + *str = system76_name(data->ntmp, channel); + if (*str) + return 0; + } + break; + + default: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} + +static const struct hwmon_ops thermal_ops = { + .is_visible = thermal_is_visible, + .read = thermal_read, + .read_string = thermal_read_string, +}; + +// Allocate up to 8 fans and temperatures +static const struct hwmon_channel_info *thermal_channel_info[] = { + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL), + NULL +}; + +static const struct hwmon_chip_info thermal_chip_info = { + .ops = &thermal_ops, + .info = thermal_channel_info, +}; + +static void input_key(struct system76_data *data, unsigned int code) +{ + input_report_key(data->input, code, 1); + input_sync(data->input); + + input_report_key(data->input, code, 0); + input_sync(data->input); +} + // Handle ACPI notification static void system76_notify(struct acpi_device *acpi_dev, u32 event) { @@ -292,6 +655,9 @@ static void system76_notify(struct acpi_device *acpi_dev, u32 event) case 0x84: kb_led_hotkey_color(data); break; + case 0x85: + input_key(data, KEY_SCREENLOCK); + break; } } @@ -326,6 +692,7 @@ static int system76_add(struct acpi_device *acpi_dev) data->kb_led.brightness_set_blocking = kb_led_set; if (acpi_has_method(acpi_device_handle(data->acpi_dev), "SKBC")) { data->kb_led.max_brightness = 255; + data->kb_led.groups = system76_kb_led_color_groups; data->kb_toggle_brightness = 72; data->kb_color = 0xffffff; system76_set(data, "SKBC", data->kb_color); @@ -337,16 +704,42 @@ static int system76_add(struct acpi_device *acpi_dev) if (err) return err; - if (data->kb_color >= 0) { - err = device_create_file( - data->kb_led.dev, - &kb_led_color_dev_attr - ); - if (err) - return err; - } + data->input = devm_input_allocate_device(&acpi_dev->dev); + if (!data->input) + return -ENOMEM; + + data->input->name = "System76 ACPI Hotkeys"; + data->input->phys = "system76_acpi/input0"; + data->input->id.bustype = BUS_HOST; + data->input->dev.parent = &acpi_dev->dev; + input_set_capability(data->input, EV_KEY, KEY_SCREENLOCK); + + err = input_register_device(data->input); + if (err) + goto error; + + err = system76_get_object(data, "NFAN", &data->nfan); + if (err) + goto error; + + err = system76_get_object(data, "NTMP", &data->ntmp); + if (err) + goto error; + + data->therm = devm_hwmon_device_register_with_info(&acpi_dev->dev, + "system76_acpi", data, &thermal_chip_info, NULL); + err = PTR_ERR_OR_ZERO(data->therm); + if (err) + goto error; + + system76_battery_init(); return 0; + +error: + kfree(data->ntmp); + kfree(data->nfan); + return err; } // Remove a System76 ACPI device @@ -355,13 +748,15 @@ static int system76_remove(struct acpi_device *acpi_dev) struct system76_data *data; data = acpi_driver_data(acpi_dev); - if (data->kb_color >= 0) - device_remove_file(data->kb_led.dev, &kb_led_color_dev_attr); - devm_led_classdev_unregister(&acpi_dev->dev, &data->ap_led); + system76_battery_exit(); + devm_led_classdev_unregister(&acpi_dev->dev, &data->ap_led); devm_led_classdev_unregister(&acpi_dev->dev, &data->kb_led); + kfree(data->nfan); + kfree(data->ntmp); + system76_get(data, "FINI"); return 0; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 291cd18c9c8f..b39dbc2fe45b 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -1003,79 +1003,6 @@ static struct platform_driver tpacpi_hwmon_pdriver = { * sysfs support helpers */ -struct attribute_set { - unsigned int members, max_members; - struct attribute_group group; -}; - -struct attribute_set_obj { - struct attribute_set s; - struct attribute *a; -} __attribute__((packed)); - -static struct attribute_set *create_attr_set(unsigned int max_members, - const char *name) -{ - struct attribute_set_obj *sobj; - - if (max_members == 0) - return NULL; - - /* Allocates space for implicit NULL at the end too */ - sobj = kzalloc(sizeof(struct attribute_set_obj) + - max_members * sizeof(struct attribute *), - GFP_KERNEL); - if (!sobj) - return NULL; - sobj->s.max_members = max_members; - sobj->s.group.attrs = &sobj->a; - sobj->s.group.name = name; - - return &sobj->s; -} - -#define destroy_attr_set(_set) \ - kfree(_set) - -/* not multi-threaded safe, use it in a single thread per set */ -static int add_to_attr_set(struct attribute_set *s, struct attribute *attr) -{ - if (!s || !attr) - return -EINVAL; - - if (s->members >= s->max_members) - return -ENOMEM; - - s->group.attrs[s->members] = attr; - s->members++; - - return 0; -} - -static int add_many_to_attr_set(struct attribute_set *s, - struct attribute **attr, - unsigned int count) -{ - int i, res; - - for (i = 0; i < count; i++) { - res = add_to_attr_set(s, attr[i]); - if (res) - return res; - } - - return 0; -} - -static void delete_attr_set(struct attribute_set *s, struct kobject *kobj) -{ - sysfs_remove_group(kobj, &s->group); - destroy_attr_set(s); -} - -#define register_attr_set_with_sysfs(_attr_set, _kobj) \ - sysfs_create_group(_kobj, &_attr_set->group) - static int parse_strtoul(const char *buf, unsigned long max, unsigned long *value) { @@ -1350,7 +1277,7 @@ static ssize_t tpacpi_rfk_sysfs_enable_show(const enum tpacpi_rfk_id id, return status; } - return snprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", (status == TPACPI_RFK_RADIO_ON) ? 1 : 0); } @@ -1443,14 +1370,14 @@ static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf) /* interface_version --------------------------------------------------- */ static ssize_t interface_version_show(struct device_driver *drv, char *buf) { - return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION); + return sysfs_emit(buf, "0x%08x\n", TPACPI_SYSFS_VERSION); } static DRIVER_ATTR_RO(interface_version); /* debug_level --------------------------------------------------------- */ static ssize_t debug_level_show(struct device_driver *drv, char *buf) { - return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level); + return sysfs_emit(buf, "0x%04x\n", dbg_level); } static ssize_t debug_level_store(struct device_driver *drv, const char *buf, @@ -1470,7 +1397,7 @@ static DRIVER_ATTR_RW(debug_level); /* version ------------------------------------------------------------- */ static ssize_t version_show(struct device_driver *drv, char *buf) { - return snprintf(buf, PAGE_SIZE, "%s v%s\n", + return sysfs_emit(buf, "%s v%s\n", TPACPI_DESC, TPACPI_VERSION); } static DRIVER_ATTR_RO(version); @@ -1482,7 +1409,7 @@ static DRIVER_ATTR_RO(version); /* wlsw_emulstate ------------------------------------------------------ */ static ssize_t wlsw_emulstate_show(struct device_driver *drv, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_wlsw_emulstate); + return sysfs_emit(buf, "%d\n", !!tpacpi_wlsw_emulstate); } static ssize_t wlsw_emulstate_store(struct device_driver *drv, const char *buf, @@ -1505,7 +1432,7 @@ static DRIVER_ATTR_RW(wlsw_emulstate); /* bluetooth_emulstate ------------------------------------------------- */ static ssize_t bluetooth_emulstate_show(struct device_driver *drv, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_bluetooth_emulstate); + return sysfs_emit(buf, "%d\n", !!tpacpi_bluetooth_emulstate); } static ssize_t bluetooth_emulstate_store(struct device_driver *drv, @@ -1525,7 +1452,7 @@ static DRIVER_ATTR_RW(bluetooth_emulstate); /* wwan_emulstate ------------------------------------------------- */ static ssize_t wwan_emulstate_show(struct device_driver *drv, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_wwan_emulstate); + return sysfs_emit(buf, "%d\n", !!tpacpi_wwan_emulstate); } static ssize_t wwan_emulstate_store(struct device_driver *drv, const char *buf, @@ -1545,7 +1472,7 @@ static DRIVER_ATTR_RW(wwan_emulstate); /* uwb_emulstate ------------------------------------------------- */ static ssize_t uwb_emulstate_show(struct device_driver *drv, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_uwb_emulstate); + return sysfs_emit(buf, "%d\n", !!tpacpi_uwb_emulstate); } static ssize_t uwb_emulstate_store(struct device_driver *drv, const char *buf, @@ -2044,8 +1971,6 @@ static u32 hotkey_acpi_mask; /* events enabled in firmware */ static u16 *hotkey_keycode_map; -static struct attribute_set *hotkey_dev_attributes; - static void tpacpi_driver_event(const unsigned int hkey_event); static void hotkey_driver_event(const unsigned int scancode); static void hotkey_poll_setup(const bool may_warn); @@ -2755,7 +2680,7 @@ static ssize_t hotkey_enable_show(struct device *dev, if (res) return res; - return snprintf(buf, PAGE_SIZE, "%d\n", status); + return sysfs_emit(buf, "%d\n", status); } static ssize_t hotkey_enable_store(struct device *dev, @@ -2783,7 +2708,7 @@ static ssize_t hotkey_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_user_mask); + return sysfs_emit(buf, "0x%08x\n", hotkey_user_mask); } static ssize_t hotkey_mask_store(struct device *dev, @@ -2831,7 +2756,7 @@ static ssize_t hotkey_bios_mask_show(struct device *dev, { printk_deprecated_attribute("hotkey_bios_mask", "This attribute is useless."); - return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_orig_mask); + return sysfs_emit(buf, "0x%08x\n", hotkey_orig_mask); } static DEVICE_ATTR_RO(hotkey_bios_mask); @@ -2841,7 +2766,7 @@ static ssize_t hotkey_all_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "0x%08x\n", + return sysfs_emit(buf, "0x%08x\n", hotkey_all_mask | hotkey_source_mask); } @@ -2852,7 +2777,7 @@ static ssize_t hotkey_adaptive_all_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "0x%08x\n", + return sysfs_emit(buf, "0x%08x\n", hotkey_adaptive_all_mask | hotkey_source_mask); } @@ -2863,7 +2788,7 @@ static ssize_t hotkey_recommended_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "0x%08x\n", + return sysfs_emit(buf, "0x%08x\n", (hotkey_all_mask | hotkey_source_mask) & ~hotkey_reserved_mask); } @@ -2877,7 +2802,7 @@ static ssize_t hotkey_source_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "0x%08x\n", hotkey_source_mask); + return sysfs_emit(buf, "0x%08x\n", hotkey_source_mask); } static ssize_t hotkey_source_mask_store(struct device *dev, @@ -2928,7 +2853,7 @@ static ssize_t hotkey_poll_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_poll_freq); + return sysfs_emit(buf, "%d\n", hotkey_poll_freq); } static ssize_t hotkey_poll_freq_store(struct device *dev, @@ -2970,7 +2895,7 @@ static ssize_t hotkey_radio_sw_show(struct device *dev, /* Opportunistic update */ tpacpi_rfk_update_hwblock_state((res == TPACPI_RFK_RADIO_OFF)); - return snprintf(buf, PAGE_SIZE, "%d\n", + return sysfs_emit(buf, "%d\n", (res == TPACPI_RFK_RADIO_OFF) ? 0 : 1); } @@ -2993,7 +2918,7 @@ static ssize_t hotkey_tablet_mode_show(struct device *dev, if (res < 0) return res; - return snprintf(buf, PAGE_SIZE, "%d\n", !!s); + return sysfs_emit(buf, "%d\n", !!s); } static DEVICE_ATTR_RO(hotkey_tablet_mode); @@ -3010,7 +2935,7 @@ static ssize_t hotkey_wakeup_reason_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_wakeup_reason); + return sysfs_emit(buf, "%d\n", hotkey_wakeup_reason); } static DEVICE_ATTR(wakeup_reason, S_IRUGO, hotkey_wakeup_reason_show, NULL); @@ -3026,7 +2951,7 @@ static ssize_t hotkey_wakeup_hotunplug_complete_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_autosleep_ack); + return sysfs_emit(buf, "%d\n", hotkey_autosleep_ack); } static DEVICE_ATTR(wakeup_hotunplug_complete, S_IRUGO, @@ -3061,7 +2986,7 @@ static ssize_t adaptive_kbd_mode_show(struct device *dev, if (current_mode < 0) return current_mode; - return snprintf(buf, PAGE_SIZE, "%d\n", current_mode); + return sysfs_emit(buf, "%d\n", current_mode); } static ssize_t adaptive_kbd_mode_store(struct device *dev, @@ -3091,7 +3016,7 @@ static const struct attribute_group adaptive_kbd_attr_group = { /* --------------------------------------------------------------------- */ -static struct attribute *hotkey_attributes[] __initdata = { +static struct attribute *hotkey_attributes[] = { &dev_attr_hotkey_enable.attr, &dev_attr_hotkey_bios_enabled.attr, &dev_attr_hotkey_bios_mask.attr, @@ -3105,6 +3030,26 @@ static struct attribute *hotkey_attributes[] __initdata = { &dev_attr_hotkey_source_mask.attr, &dev_attr_hotkey_poll_freq.attr, #endif + NULL +}; + +static umode_t hotkey_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + if (attr == &dev_attr_hotkey_tablet_mode.attr) { + if (!tp_features.hotkey_tablet) + return 0; + } else if (attr == &dev_attr_hotkey_radio_sw.attr) { + if (!tp_features.hotkey_wlsw) + return 0; + } + + return attr->mode; +} + +static const struct attribute_group hotkey_attr_group = { + .is_visible = hotkey_attr_is_visible, + .attrs = hotkey_attributes, }; /* @@ -3163,9 +3108,7 @@ static void hotkey_exit(void) hotkey_poll_stop_sync(); mutex_unlock(&hotkey_mutex); #endif - - if (hotkey_dev_attributes) - delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); + sysfs_remove_group(&tpacpi_pdev->dev.kobj, &hotkey_attr_group); dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY, "restoring original HKEY status and mask\n"); @@ -3251,11 +3194,6 @@ static int hotkey_init_tablet_mode(void) pr_info("Tablet mode switch found (type: %s), currently in %s mode\n", type, in_tablet_mode ? "tablet" : "laptop"); - res = add_to_attr_set(hotkey_dev_attributes, - &dev_attr_hotkey_tablet_mode.attr); - if (res) - return -1; - return in_tablet_mode; } @@ -3517,19 +3455,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) tpacpi_disable_brightness_delay(); - /* MUST have enough space for all attributes to be added to - * hotkey_dev_attributes */ - hotkey_dev_attributes = create_attr_set( - ARRAY_SIZE(hotkey_attributes) + 2, - NULL); - if (!hotkey_dev_attributes) - return -ENOMEM; - res = add_many_to_attr_set(hotkey_dev_attributes, - hotkey_attributes, - ARRAY_SIZE(hotkey_attributes)); - if (res) - goto err_exit; - /* mask not supported on 600e/x, 770e, 770x, A21e, A2xm/p, A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking for HKEY interface version 0x100 */ @@ -3638,18 +3563,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) pr_info("radio switch found; radios are %s\n", enabled(status, 0)); } - if (tp_features.hotkey_wlsw) - res = add_to_attr_set(hotkey_dev_attributes, - &dev_attr_hotkey_radio_sw.attr); - - res = hotkey_init_tablet_mode(); - if (res < 0) - goto err_exit; - tabletsw_state = res; - - res = register_attr_set_with_sysfs(hotkey_dev_attributes, - &tpacpi_pdev->dev.kobj); + tabletsw_state = hotkey_init_tablet_mode(); + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, &hotkey_attr_group); if (res) goto err_exit; @@ -3748,11 +3664,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) return 0; err_exit: - delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); - sysfs_remove_group(&tpacpi_pdev->dev.kobj, - &adaptive_kbd_attr_group); - - hotkey_dev_attributes = NULL; + sysfs_remove_group(&tpacpi_pdev->dev.kobj, &hotkey_attr_group); + sysfs_remove_group(&tpacpi_pdev->dev.kobj, &adaptive_kbd_attr_group); return (res < 0) ? res : 1; } @@ -6437,7 +6350,7 @@ static ssize_t thermal_temp_input_show(struct device *dev, if (value == TPACPI_THERMAL_SENSOR_NA) return -ENXIO; - return snprintf(buf, PAGE_SIZE, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } #define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \ @@ -8670,7 +8583,7 @@ static ssize_t fan_pwm1_enable_show(struct device *dev, } else mode = 1; - return snprintf(buf, PAGE_SIZE, "%d\n", mode); + return sysfs_emit(buf, "%d\n", mode); } static ssize_t fan_pwm1_enable_store(struct device *dev, @@ -8736,7 +8649,7 @@ static ssize_t fan_pwm1_show(struct device *dev, if (status > 7) status = 7; - return snprintf(buf, PAGE_SIZE, "%u\n", (status * 255) / 7); + return sysfs_emit(buf, "%u\n", (status * 255) / 7); } static ssize_t fan_pwm1_store(struct device *dev, @@ -8789,7 +8702,7 @@ static ssize_t fan_fan1_input_show(struct device *dev, if (res < 0) return res; - return snprintf(buf, PAGE_SIZE, "%u\n", speed); + return sysfs_emit(buf, "%u\n", speed); } static DEVICE_ATTR(fan1_input, S_IRUGO, fan_fan1_input_show, NULL); @@ -8806,7 +8719,7 @@ static ssize_t fan_fan2_input_show(struct device *dev, if (res < 0) return res; - return snprintf(buf, PAGE_SIZE, "%u\n", speed); + return sysfs_emit(buf, "%u\n", speed); } static DEVICE_ATTR(fan2_input, S_IRUGO, fan_fan2_input_show, NULL); @@ -8814,7 +8727,7 @@ static DEVICE_ATTR(fan2_input, S_IRUGO, fan_fan2_input_show, NULL); /* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */ static ssize_t fan_watchdog_show(struct device_driver *drv, char *buf) { - return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval); + return sysfs_emit(buf, "%u\n", fan_watchdog_maxinterval); } static ssize_t fan_watchdog_store(struct device_driver *drv, const char *buf, @@ -9161,7 +9074,7 @@ static int fan_write_cmd_level(const char *cmd, int *rc) if (strlencmp(cmd, "level auto") == 0) level = TP_EC_FAN_AUTO; - else if ((strlencmp(cmd, "level disengaged") == 0) | + else if ((strlencmp(cmd, "level disengaged") == 0) || (strlencmp(cmd, "level full-speed") == 0)) level = TP_EC_FAN_FULLSPEED; else if (sscanf(cmd, "level %d", &level) != 1) diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 0e1451b1d9c6..fa8812039b82 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -100,10 +100,10 @@ static const struct ts_dmi_data chuwi_hi10_air_data = { }; static const struct property_entry chuwi_hi10_plus_props[] = { - PROPERTY_ENTRY_U32("touchscreen-min-x", 0), - PROPERTY_ENTRY_U32("touchscreen-min-y", 5), - PROPERTY_ENTRY_U32("touchscreen-size-x", 1914), - PROPERTY_ENTRY_U32("touchscreen-size-y", 1283), + PROPERTY_ENTRY_U32("touchscreen-min-x", 12), + PROPERTY_ENTRY_U32("touchscreen-min-y", 10), + PROPERTY_ENTRY_U32("touchscreen-size-x", 1908), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1270), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hi10plus.fw"), PROPERTY_ENTRY_U32("silead,max-fingers", 10), PROPERTY_ENTRY_BOOL("silead,home-button"), @@ -111,6 +111,15 @@ static const struct property_entry chuwi_hi10_plus_props[] = { }; static const struct ts_dmi_data chuwi_hi10_plus_data = { + .embedded_fw = { + .name = "silead/gsl1680-chuwi-hi10plus.fw", + .prefix = { 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }, + .length = 34056, + .sha256 = { 0xfd, 0x0a, 0x08, 0x08, 0x3c, 0xa6, 0x34, 0x4e, + 0x2c, 0x49, 0x9c, 0xcd, 0x7d, 0x44, 0x9d, 0x38, + 0x10, 0x68, 0xb5, 0xbd, 0xb7, 0x2a, 0x63, 0xb5, + 0x67, 0x0b, 0x96, 0xbd, 0x89, 0x67, 0x85, 0x09 }, + }, .acpi_name = "MSSL0017:00", .properties = chuwi_hi10_plus_props, }; @@ -141,6 +150,33 @@ static const struct ts_dmi_data chuwi_hi10_pro_data = { .properties = chuwi_hi10_pro_props, }; +static const struct property_entry chuwi_hibook_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 30), + PROPERTY_ENTRY_U32("touchscreen-min-y", 4), + PROPERTY_ENTRY_U32("touchscreen-size-x", 1892), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1276), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-hibook.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct ts_dmi_data chuwi_hibook_data = { + .embedded_fw = { + .name = "silead/gsl1680-chuwi-hibook.fw", + .prefix = { 0xf0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }, + .length = 40392, + .sha256 = { 0xf7, 0xc0, 0xe8, 0x5a, 0x6c, 0xf2, 0xeb, 0x8d, + 0x12, 0xc4, 0x45, 0xbf, 0x55, 0x13, 0x4c, 0x1a, + 0x13, 0x04, 0x31, 0x08, 0x65, 0x73, 0xf7, 0xa8, + 0x1b, 0x7d, 0x59, 0xc9, 0xe6, 0x97, 0xf7, 0x38 }, + }, + .acpi_name = "MSSL0017:00", + .properties = chuwi_hibook_props, +}; + static const struct property_entry chuwi_vi8_props[] = { PROPERTY_ENTRY_U32("touchscreen-min-x", 4), PROPERTY_ENTRY_U32("touchscreen-min-y", 6), @@ -902,6 +938,23 @@ static const struct ts_dmi_data trekstor_surftab_wintron70_data = { .properties = trekstor_surftab_wintron70_props, }; +static const struct property_entry viglen_connect_10_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1890), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), + PROPERTY_ENTRY_U32("touchscreen-fuzz-x", 6), + PROPERTY_ENTRY_U32("touchscreen-fuzz-y", 6), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-viglen-connect-10.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct ts_dmi_data viglen_connect_10_data = { + .acpi_name = "MSSL1680:00", + .properties = viglen_connect_10_props, +}; + static const struct property_entry vinga_twizzle_j116_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1920), PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), @@ -980,6 +1033,16 @@ const struct dmi_system_id touchscreen_dmi_table[] = { }, }, { + /* Chuwi HiBook (CWI514) */ + .driver_data = (void *)&chuwi_hibook_data, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), + DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), + /* Above matches are too generic, add bios-date match */ + DMI_MATCH(DMI_BIOS_DATE, "05/07/2016"), + }, + }, + { /* Chuwi Vi8 (CWI506) */ .driver_data = (void *)&chuwi_vi8_data, .matches = { @@ -1476,6 +1539,14 @@ const struct dmi_system_id touchscreen_dmi_table[] = { }, }, { + /* Viglen Connect 10 */ + .driver_data = (void *)&viglen_connect_10_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Viglen Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Connect 10'' Tablet PC"), + }, + }, + { /* Vinga Twizzle J116 */ .driver_data = (void *)&vinga_twizzle_j116_data, .matches = { diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index a76313006bdc..c34341f4da76 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -17,6 +17,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/acpi.h> +#include <linux/bits.h> +#include <linux/build_bug.h> #include <linux/device.h> #include <linux/init.h> #include <linux/kernel.h> @@ -25,6 +27,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/types.h> #include <linux/uaccess.h> #include <linux/uuid.h> @@ -39,7 +42,7 @@ MODULE_LICENSE("GPL"); static LIST_HEAD(wmi_block_list); struct guid_block { - char guid[16]; + guid_t guid; union { char object_id[2]; struct { @@ -49,7 +52,10 @@ struct guid_block { }; u8 instance_count; u8 flags; -}; +} __packed; +static_assert(sizeof(typeof_member(struct guid_block, guid)) == 16); +static_assert(sizeof(struct guid_block) == 20); +static_assert(__alignof__(struct guid_block) == 1); struct wmi_block { struct wmi_device dev; @@ -70,10 +76,10 @@ struct wmi_block { * If the GUID data block is marked as expensive, we must enable and * explicitily disable data collection. */ -#define ACPI_WMI_EXPENSIVE 0x1 -#define ACPI_WMI_METHOD 0x2 /* GUID is a method */ -#define ACPI_WMI_STRING 0x4 /* GUID takes & returns a string */ -#define ACPI_WMI_EVENT 0x8 /* GUID is an event */ +#define ACPI_WMI_EXPENSIVE BIT(0) +#define ACPI_WMI_METHOD BIT(1) /* GUID is a method */ +#define ACPI_WMI_STRING BIT(2) /* GUID takes & returns a string */ +#define ACPI_WMI_EVENT BIT(3) /* GUID is an event */ static bool debug_event; module_param(debug_event, bool, 0444); @@ -91,7 +97,7 @@ static int acpi_wmi_probe(struct platform_device *device); static const struct acpi_device_id wmi_device_ids[] = { {"PNP0C14", 0}, {"pnp0c14", 0}, - {"", 0}, + { } }; MODULE_DEVICE_TABLE(acpi, wmi_device_ids); @@ -108,43 +114,44 @@ static struct platform_driver acpi_wmi_driver = { * GUID parsing functions */ -static bool find_guid(const char *guid_string, struct wmi_block **out) +static acpi_status find_guid(const char *guid_string, struct wmi_block **out) { guid_t guid_input; struct wmi_block *wblock; - struct guid_block *block; + + if (!guid_string) + return AE_BAD_PARAMETER; if (guid_parse(guid_string, &guid_input)) - return false; + return AE_BAD_PARAMETER; list_for_each_entry(wblock, &wmi_block_list, list) { - block = &wblock->gblock; - - if (memcmp(block->guid, &guid_input, 16) == 0) { + if (guid_equal(&wblock->gblock.guid, &guid_input)) { if (out) *out = wblock; - return true; + + return AE_OK; } } - return false; + + return AE_NOT_FOUND; } static const void *find_guid_context(struct wmi_block *wblock, - struct wmi_driver *wdriver) + struct wmi_driver *wdriver) { const struct wmi_device_id *id; - guid_t guid_input; - if (wblock == NULL || wdriver == NULL) - return NULL; - if (wdriver->id_table == NULL) + id = wdriver->id_table; + if (!id) return NULL; - id = wdriver->id_table; while (*id->guid_string) { + guid_t guid_input; + if (guid_parse(id->guid_string, &guid_input)) continue; - if (!memcmp(wblock->gblock.guid, &guid_input, 16)) + if (guid_equal(&wblock->gblock.guid, &guid_input)) return id->context; id++; } @@ -175,9 +182,9 @@ static int get_subobj_info(acpi_handle handle, const char *pathname, return 0; } -static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) +static acpi_status wmi_method_enable(struct wmi_block *wblock, bool enable) { - struct guid_block *block = NULL; + struct guid_block *block; char method[5]; acpi_status status; acpi_handle handle; @@ -187,11 +194,50 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) snprintf(method, 5, "WE%02X", block->notify_id); status = acpi_execute_simple_method(handle, method, enable); + if (status == AE_NOT_FOUND) + return AE_OK; - if (status != AE_OK && status != AE_NOT_FOUND) - return status; + return status; +} + +#define WMI_ACPI_METHOD_NAME_SIZE 5 + +static inline void get_acpi_method_name(const struct wmi_block *wblock, + const char method, + char buffer[static WMI_ACPI_METHOD_NAME_SIZE]) +{ + static_assert(ARRAY_SIZE(wblock->gblock.object_id) == 2); + static_assert(WMI_ACPI_METHOD_NAME_SIZE >= 5); + + buffer[0] = 'W'; + buffer[1] = method; + buffer[2] = wblock->gblock.object_id[0]; + buffer[3] = wblock->gblock.object_id[1]; + buffer[4] = '\0'; +} + +static inline acpi_object_type get_param_acpi_type(const struct wmi_block *wblock) +{ + if (wblock->gblock.flags & ACPI_WMI_STRING) + return ACPI_TYPE_STRING; else - return AE_OK; + return ACPI_TYPE_BUFFER; +} + +static acpi_status get_event_data(const struct wmi_block *wblock, struct acpi_buffer *out) +{ + union acpi_object param = { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = wblock->gblock.notify_id, + } + }; + struct acpi_object_list input = { + .count = 1, + .pointer = ¶m, + }; + + return acpi_evaluate_object(wblock->acpi_device->handle, "_WED", &input, out); } /* @@ -226,13 +272,16 @@ EXPORT_SYMBOL_GPL(set_required_buffer_size); * * Call an ACPI-WMI method */ -acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, -u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) +acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, u32 method_id, + const struct acpi_buffer *in, struct acpi_buffer *out) { struct wmi_block *wblock = NULL; + acpi_status status; + + status = find_guid(guid_string, &wblock); + if (ACPI_FAILURE(status)) + return status; - if (!find_guid(guid_string, &wblock)) - return AE_ERROR; return wmidev_evaluate_method(&wblock->dev, instance, method_id, in, out); } @@ -248,16 +297,15 @@ EXPORT_SYMBOL_GPL(wmi_evaluate_method); * * Call an ACPI-WMI method */ -acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, - u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) +acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 method_id, + const struct acpi_buffer *in, struct acpi_buffer *out) { - struct guid_block *block = NULL; - struct wmi_block *wblock = NULL; + struct guid_block *block; + struct wmi_block *wblock; acpi_handle handle; - acpi_status status; struct acpi_object_list input; union acpi_object params[3]; - char method[5] = "WM"; + char method[WMI_ACPI_METHOD_NAME_SIZE]; wblock = container_of(wdev, struct wmi_block, dev); block = &wblock->gblock; @@ -279,33 +327,27 @@ acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, if (in) { input.count = 3; - if (block->flags & ACPI_WMI_STRING) { - params[2].type = ACPI_TYPE_STRING; - } else { - params[2].type = ACPI_TYPE_BUFFER; - } + params[2].type = get_param_acpi_type(wblock); params[2].buffer.length = in->length; params[2].buffer.pointer = in->pointer; } - strncat(method, block->object_id, 2); + get_acpi_method_name(wblock, 'M', method); - status = acpi_evaluate_object(handle, method, &input, out); - - return status; + return acpi_evaluate_object(handle, method, &input, out); } EXPORT_SYMBOL_GPL(wmidev_evaluate_method); static acpi_status __query_block(struct wmi_block *wblock, u8 instance, struct acpi_buffer *out) { - struct guid_block *block = NULL; + struct guid_block *block; acpi_handle handle; acpi_status status, wc_status = AE_ERROR; struct acpi_object_list input; union acpi_object wq_params[1]; - char method[5]; - char wc_method[5] = "WC"; + char wc_method[WMI_ACPI_METHOD_NAME_SIZE]; + char method[WMI_ACPI_METHOD_NAME_SIZE]; if (!out) return AE_BAD_PARAMETER; @@ -333,7 +375,7 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance, * enable collection. */ if (block->flags & ACPI_WMI_EXPENSIVE) { - strncat(wc_method, block->object_id, 2); + get_acpi_method_name(wblock, 'C', wc_method); /* * Some GUIDs break the specification by declaring themselves @@ -343,9 +385,7 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance, wc_status = acpi_execute_simple_method(handle, wc_method, 1); } - strcpy(method, "WQ"); - strncat(method, block->object_id, 2); - + get_acpi_method_name(wblock, 'Q', method); status = acpi_evaluate_object(handle, method, &input, out); /* @@ -353,7 +393,14 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance, * the WQxx method failed - we should disable collection anyway. */ if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { - status = acpi_execute_simple_method(handle, wc_method, 0); + /* + * Ignore whether this WCxx call succeeds or not since + * the previously executed WQxx method call might have + * succeeded, and returning the failing status code + * of this call would throw away the result of the WQxx + * call, potentially leaking memory. + */ + acpi_execute_simple_method(handle, wc_method, 0); } return status; @@ -371,12 +418,11 @@ acpi_status wmi_query_block(const char *guid_string, u8 instance, struct acpi_buffer *out) { struct wmi_block *wblock; + acpi_status status; - if (!guid_string) - return AE_BAD_PARAMETER; - - if (!find_guid(guid_string, &wblock)) - return AE_ERROR; + status = find_guid(guid_string, &wblock); + if (ACPI_FAILURE(status)) + return status; return __query_block(wblock, instance, out); } @@ -390,7 +436,7 @@ union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance) if (ACPI_FAILURE(__query_block(wblock, instance, &out))) return NULL; - return (union acpi_object *)out.pointer; + return out.pointer; } EXPORT_SYMBOL_GPL(wmidev_block_query); @@ -405,18 +451,20 @@ EXPORT_SYMBOL_GPL(wmidev_block_query); acpi_status wmi_set_block(const char *guid_string, u8 instance, const struct acpi_buffer *in) { - struct guid_block *block = NULL; struct wmi_block *wblock = NULL; + struct guid_block *block; acpi_handle handle; struct acpi_object_list input; union acpi_object params[2]; - char method[5] = "WS"; + char method[WMI_ACPI_METHOD_NAME_SIZE]; + acpi_status status; - if (!guid_string || !in) + if (!in) return AE_BAD_DATA; - if (!find_guid(guid_string, &wblock)) - return AE_ERROR; + status = find_guid(guid_string, &wblock); + if (ACPI_FAILURE(status)) + return status; block = &wblock->gblock; handle = wblock->acpi_device->handle; @@ -432,16 +480,11 @@ acpi_status wmi_set_block(const char *guid_string, u8 instance, input.pointer = params; params[0].type = ACPI_TYPE_INTEGER; params[0].integer.value = instance; - - if (block->flags & ACPI_WMI_STRING) { - params[1].type = ACPI_TYPE_STRING; - } else { - params[1].type = ACPI_TYPE_BUFFER; - } + params[1].type = get_param_acpi_type(wblock); params[1].buffer.length = in->length; params[1].buffer.pointer = in->pointer; - strncat(method, block->object_id, 2); + get_acpi_method_name(wblock, 'S', method); return acpi_evaluate_object(handle, method, &input, NULL); } @@ -449,7 +492,7 @@ EXPORT_SYMBOL_GPL(wmi_set_block); static void wmi_dump_wdg(const struct guid_block *g) { - pr_info("%pUL:\n", g->guid); + pr_info("%pUL:\n", &g->guid); if (g->flags & ACPI_WMI_EVENT) pr_info("\tnotify_id: 0x%02X\n", g->notify_id); else @@ -482,15 +525,14 @@ static void wmi_notify_debug(u32 value, void *context) return; } - obj = (union acpi_object *)response.pointer; - + obj = response.pointer; if (!obj) return; - pr_info("DEBUG Event "); - switch(obj->type) { + pr_info("DEBUG: event 0x%02X ", value); + switch (obj->type) { case ACPI_TYPE_BUFFER: - pr_cont("BUFFER_TYPE - length %d\n", obj->buffer.length); + pr_cont("BUFFER_TYPE - length %u\n", obj->buffer.length); break; case ACPI_TYPE_STRING: pr_cont("STRING_TYPE - %s\n", obj->string.pointer); @@ -499,7 +541,7 @@ static void wmi_notify_debug(u32 value, void *context) pr_cont("INTEGER_TYPE - %llu\n", obj->integer.value); break; case ACPI_TYPE_PACKAGE: - pr_cont("PACKAGE_TYPE - %d elements\n", obj->package.count); + pr_cont("PACKAGE_TYPE - %u elements\n", obj->package.count); break; default: pr_cont("object type 0x%X\n", obj->type); @@ -516,7 +558,8 @@ static void wmi_notify_debug(u32 value, void *context) * Register a handler for events sent to the ACPI-WMI mapper device. */ acpi_status wmi_install_notify_handler(const char *guid, -wmi_notify_handler handler, void *data) + wmi_notify_handler handler, + void *data) { struct wmi_block *block; acpi_status status = AE_NOT_EXIST; @@ -531,7 +574,7 @@ wmi_notify_handler handler, void *data) list_for_each_entry(block, &wmi_block_list, list) { acpi_status wmi_status; - if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { + if (guid_equal(&block->gblock.guid, &guid_input)) { if (block->handler && block->handler != wmi_notify_debug) return AE_ALREADY_ACQUIRED; @@ -539,7 +582,7 @@ wmi_notify_handler handler, void *data) block->handler = handler; block->handler_data = data; - wmi_status = wmi_method_enable(block, 1); + wmi_status = wmi_method_enable(block, true); if ((wmi_status != AE_OK) || ((wmi_status == AE_OK) && (status == AE_NOT_EXIST))) status = wmi_status; @@ -551,7 +594,7 @@ wmi_notify_handler handler, void *data) EXPORT_SYMBOL_GPL(wmi_install_notify_handler); /** - * wmi_uninstall_notify_handler - Unregister handler for WMI events + * wmi_remove_notify_handler - Unregister handler for WMI events * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba * * Unregister handler for events sent to the ACPI-WMI mapper device. @@ -571,7 +614,7 @@ acpi_status wmi_remove_notify_handler(const char *guid) list_for_each_entry(block, &wmi_block_list, list) { acpi_status wmi_status; - if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { + if (guid_equal(&block->gblock.guid, &guid_input)) { if (!block->handler || block->handler == wmi_notify_debug) return AE_NULL_ENTRY; @@ -580,7 +623,7 @@ acpi_status wmi_remove_notify_handler(const char *guid) block->handler = wmi_notify_debug; status = AE_OK; } else { - wmi_status = wmi_method_enable(block, 0); + wmi_status = wmi_method_enable(block, false); block->handler = NULL; block->handler_data = NULL; if ((wmi_status != AE_OK) || @@ -605,23 +648,13 @@ EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); */ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) { - struct acpi_object_list input; - union acpi_object params[1]; - struct guid_block *gblock; struct wmi_block *wblock; - input.count = 1; - input.pointer = params; - params[0].type = ACPI_TYPE_INTEGER; - params[0].integer.value = event; - list_for_each_entry(wblock, &wmi_block_list, list) { - gblock = &wblock->gblock; + struct guid_block *gblock = &wblock->gblock; - if ((gblock->flags & ACPI_WMI_EVENT) && - (gblock->notify_id == event)) - return acpi_evaluate_object(wblock->acpi_device->handle, - "_WED", &input, out); + if ((gblock->flags & ACPI_WMI_EVENT) && gblock->notify_id == event) + return get_event_data(wblock, out); } return AE_NOT_FOUND; @@ -636,7 +669,7 @@ EXPORT_SYMBOL_GPL(wmi_get_event_data); */ bool wmi_has_guid(const char *guid_string) { - return find_guid(guid_string, NULL); + return ACPI_SUCCESS(find_guid(guid_string, NULL)); } EXPORT_SYMBOL_GPL(wmi_has_guid); @@ -651,8 +684,10 @@ EXPORT_SYMBOL_GPL(wmi_has_guid); char *wmi_get_acpi_device_uid(const char *guid_string) { struct wmi_block *wblock = NULL; + acpi_status status; - if (!find_guid(guid_string, &wblock)) + status = find_guid(guid_string, &wblock); + if (ACPI_FAILURE(status)) return NULL; return acpi_device_uid(wblock->acpi_device); @@ -669,6 +704,11 @@ static struct wmi_device *dev_to_wdev(struct device *dev) return container_of(dev, struct wmi_device, dev); } +static inline struct wmi_driver *drv_to_wdrv(struct device_driver *drv) +{ + return container_of(drv, struct wmi_driver, driver); +} + /* * sysfs interface */ @@ -677,7 +717,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, { struct wmi_block *wblock = dev_to_wblock(dev); - return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid); + return sysfs_emit(buf, "wmi:%pUL\n", &wblock->gblock.guid); } static DEVICE_ATTR_RO(modalias); @@ -686,7 +726,7 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr, { struct wmi_block *wblock = dev_to_wblock(dev); - return sprintf(buf, "%pUL\n", wblock->gblock.guid); + return sysfs_emit(buf, "%pUL\n", &wblock->gblock.guid); } static DEVICE_ATTR_RO(guid); @@ -695,7 +735,7 @@ static ssize_t instance_count_show(struct device *dev, { struct wmi_block *wblock = dev_to_wblock(dev); - return sprintf(buf, "%d\n", (int)wblock->gblock.instance_count); + return sysfs_emit(buf, "%d\n", (int)wblock->gblock.instance_count); } static DEVICE_ATTR_RO(instance_count); @@ -704,8 +744,8 @@ static ssize_t expensive_show(struct device *dev, { struct wmi_block *wblock = dev_to_wblock(dev); - return sprintf(buf, "%d\n", - (wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0); + return sysfs_emit(buf, "%d\n", + (wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0); } static DEVICE_ATTR_RO(expensive); @@ -714,7 +754,7 @@ static struct attribute *wmi_attrs[] = { &dev_attr_guid.attr, &dev_attr_instance_count.attr, &dev_attr_expensive.attr, - NULL, + NULL }; ATTRIBUTE_GROUPS(wmi); @@ -723,13 +763,13 @@ static ssize_t notify_id_show(struct device *dev, struct device_attribute *attr, { struct wmi_block *wblock = dev_to_wblock(dev); - return sprintf(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id); + return sysfs_emit(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id); } static DEVICE_ATTR_RO(notify_id); static struct attribute *wmi_event_attrs[] = { &dev_attr_notify_id.attr, - NULL, + NULL }; ATTRIBUTE_GROUPS(wmi_event); @@ -738,8 +778,8 @@ static ssize_t object_id_show(struct device *dev, struct device_attribute *attr, { struct wmi_block *wblock = dev_to_wblock(dev); - return sprintf(buf, "%c%c\n", wblock->gblock.object_id[0], - wblock->gblock.object_id[1]); + return sysfs_emit(buf, "%c%c\n", wblock->gblock.object_id[0], + wblock->gblock.object_id[1]); } static DEVICE_ATTR_RO(object_id); @@ -748,20 +788,20 @@ static ssize_t setable_show(struct device *dev, struct device_attribute *attr, { struct wmi_device *wdev = dev_to_wdev(dev); - return sprintf(buf, "%d\n", (int)wdev->setable); + return sysfs_emit(buf, "%d\n", (int)wdev->setable); } static DEVICE_ATTR_RO(setable); static struct attribute *wmi_data_attrs[] = { &dev_attr_object_id.attr, &dev_attr_setable.attr, - NULL, + NULL }; ATTRIBUTE_GROUPS(wmi_data); static struct attribute *wmi_method_attrs[] = { &dev_attr_object_id.attr, - NULL, + NULL }; ATTRIBUTE_GROUPS(wmi_method); @@ -769,10 +809,10 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env) { struct wmi_block *wblock = dev_to_wblock(dev); - if (add_uevent_var(env, "MODALIAS=wmi:%pUL", wblock->gblock.guid)) + if (add_uevent_var(env, "MODALIAS=wmi:%pUL", &wblock->gblock.guid)) return -ENOMEM; - if (add_uevent_var(env, "WMI_GUID=%pUL", wblock->gblock.guid)) + if (add_uevent_var(env, "WMI_GUID=%pUL", &wblock->gblock.guid)) return -ENOMEM; return 0; @@ -787,8 +827,7 @@ static void wmi_dev_release(struct device *dev) static int wmi_dev_match(struct device *dev, struct device_driver *driver) { - struct wmi_driver *wmi_driver = - container_of(driver, struct wmi_driver, driver); + struct wmi_driver *wmi_driver = drv_to_wdrv(driver); struct wmi_block *wblock = dev_to_wblock(dev); const struct wmi_device_id *id = wmi_driver->id_table; @@ -800,7 +839,7 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver) if (WARN_ON(guid_parse(id->guid_string, &driver_guid))) continue; - if (!memcmp(&driver_guid, wblock->gblock.guid, 16)) + if (guid_equal(&driver_guid, &wblock->gblock.guid)) return 1; id++; @@ -811,8 +850,8 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver) static int wmi_char_open(struct inode *inode, struct file *filp) { const char *driver_name = filp->f_path.dentry->d_iname; - struct wmi_block *wblock = NULL; - struct wmi_block *next = NULL; + struct wmi_block *wblock; + struct wmi_block *next; list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { if (!wblock->dev.dev.driver) @@ -830,7 +869,7 @@ static int wmi_char_open(struct inode *inode, struct file *filp) } static ssize_t wmi_char_read(struct file *filp, char __user *buffer, - size_t length, loff_t *offset) + size_t length, loff_t *offset) { struct wmi_block *wblock = filp->private_data; @@ -844,8 +883,8 @@ static long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct wmi_ioctl_buffer __user *input = (struct wmi_ioctl_buffer __user *) arg; struct wmi_block *wblock = filp->private_data; - struct wmi_ioctl_buffer *buf = NULL; - struct wmi_driver *wdriver = NULL; + struct wmi_ioctl_buffer *buf; + struct wmi_driver *wdriver; int ret; if (_IOC_TYPE(cmd) != WMI_IOC) @@ -885,8 +924,7 @@ static long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } /* let the driver do any filtering and do the call */ - wdriver = container_of(wblock->dev.dev.driver, - struct wmi_driver, driver); + wdriver = drv_to_wdrv(wblock->dev.dev.driver); if (!try_module_get(wdriver->driver.owner)) { ret = -EBUSY; goto out_ioctl; @@ -919,12 +957,11 @@ static const struct file_operations wmi_fops = { static int wmi_dev_probe(struct device *dev) { struct wmi_block *wblock = dev_to_wblock(dev); - struct wmi_driver *wdriver = - container_of(dev->driver, struct wmi_driver, driver); + struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); int ret = 0; char *buf; - if (ACPI_FAILURE(wmi_method_enable(wblock, 1))) + if (ACPI_FAILURE(wmi_method_enable(wblock, true))) dev_warn(dev, "failed to enable device -- probing anyway\n"); if (wdriver->probe) { @@ -975,7 +1012,7 @@ probe_misc_failure: probe_string_failure: kfree(wblock->handler_data); probe_failure: - if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) + if (ACPI_FAILURE(wmi_method_enable(wblock, false))) dev_warn(dev, "failed to disable device\n"); return ret; } @@ -983,8 +1020,7 @@ probe_failure: static void wmi_dev_remove(struct device *dev) { struct wmi_block *wblock = dev_to_wblock(dev); - struct wmi_driver *wdriver = - container_of(dev->driver, struct wmi_driver, driver); + struct wmi_driver *wdriver = drv_to_wdrv(dev->driver); if (wdriver->filter_callback) { misc_deregister(&wblock->char_dev); @@ -995,7 +1031,7 @@ static void wmi_dev_remove(struct device *dev) if (wdriver->remove) wdriver->remove(dev_to_wdev(dev)); - if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) + if (ACPI_FAILURE(wmi_method_enable(wblock, false))) dev_warn(dev, "failed to disable device\n"); } @@ -1031,20 +1067,19 @@ static const struct device_type wmi_type_data = { }; static int wmi_create_device(struct device *wmi_bus_dev, - const struct guid_block *gblock, struct wmi_block *wblock, struct acpi_device *device) { struct acpi_device_info *info; - char method[5]; + char method[WMI_ACPI_METHOD_NAME_SIZE]; int result; - if (gblock->flags & ACPI_WMI_EVENT) { + if (wblock->gblock.flags & ACPI_WMI_EVENT) { wblock->dev.dev.type = &wmi_type_event; goto out_init; } - if (gblock->flags & ACPI_WMI_METHOD) { + if (wblock->gblock.flags & ACPI_WMI_METHOD) { wblock->dev.dev.type = &wmi_type_method; mutex_init(&wblock->char_mutex); goto out_init; @@ -1055,8 +1090,7 @@ static int wmi_create_device(struct device *wmi_bus_dev, * required per the WMI documentation. If it is not present, * we ignore this data block. */ - strcpy(method, "WQ"); - strncat(method, wblock->gblock.object_id, 2); + get_acpi_method_name(wblock, 'Q', method); result = get_subobj_info(device->handle, method, &info); if (result) { @@ -1083,8 +1117,7 @@ static int wmi_create_device(struct device *wmi_bus_dev, kfree(info); - strcpy(method, "WS"); - strncat(method, wblock->gblock.object_id, 2); + get_acpi_method_name(wblock, 'S', method); result = get_subobj_info(device->handle, method, NULL); if (result == 0) @@ -1094,7 +1127,7 @@ static int wmi_create_device(struct device *wmi_bus_dev, wblock->dev.dev.bus = &wmi_bus_type; wblock->dev.dev.parent = wmi_bus_dev; - dev_set_name(&wblock->dev.dev, "%pUL", gblock->guid); + dev_set_name(&wblock->dev.dev, "%pUL", &wblock->gblock.guid); device_initialize(&wblock->dev.dev); @@ -1114,12 +1147,12 @@ static void wmi_free_devices(struct acpi_device *device) } } -static bool guid_already_parsed(struct acpi_device *device, const u8 *guid) +static bool guid_already_parsed(struct acpi_device *device, const guid_t *guid) { struct wmi_block *wblock; list_for_each_entry(wblock, &wmi_block_list, list) { - if (memcmp(wblock->gblock.guid, guid, 16) == 0) { + if (guid_equal(&wblock->gblock.guid, guid)) { /* * Because we historically didn't track the relationship * between GUIDs and ACPI nodes, we don't know whether @@ -1152,7 +1185,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) if (ACPI_FAILURE(status)) return -ENXIO; - obj = (union acpi_object *) out.pointer; + obj = out.pointer; if (!obj) return -ENXIO; @@ -1174,10 +1207,10 @@ static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) * case yet, so for now, we'll just ignore the duplicate * for device creation. */ - if (guid_already_parsed(device, gblock[i].guid)) + if (guid_already_parsed(device, &gblock[i].guid)) continue; - wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL); + wblock = kzalloc(sizeof(*wblock), GFP_KERNEL); if (!wblock) { retval = -ENOMEM; break; @@ -1186,7 +1219,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) wblock->acpi_device = device; wblock->gblock = gblock[i]; - retval = wmi_create_device(wmi_bus_dev, &gblock[i], wblock, device); + retval = wmi_create_device(wmi_bus_dev, wblock, device); if (retval) { kfree(wblock); continue; @@ -1196,7 +1229,7 @@ static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) if (debug_event) { wblock->handler = wmi_notify_debug; - wmi_method_enable(wblock, 1); + wmi_method_enable(wblock, true); } } @@ -1211,9 +1244,9 @@ static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device) retval = device_add(&wblock->dev.dev); if (retval) { dev_err(wmi_bus_dev, "failed to register %pUL\n", - wblock->gblock.guid); + &wblock->gblock.guid); if (debug_event) - wmi_method_enable(wblock, 0); + wmi_method_enable(wblock, false); list_del(&wblock->list); put_device(&wblock->dev.dev); } @@ -1230,8 +1263,8 @@ out_free_pointer: */ static acpi_status acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, - u32 bits, u64 *value, - void *handler_context, void *region_context) + u32 bits, u64 *value, + void *handler_context, void *region_context) { int result = 0, i = 0; u8 temp = 0; @@ -1268,17 +1301,15 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address, static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, void *context) { - struct guid_block *block; struct wmi_block *wblock; bool found_it = false; list_for_each_entry(wblock, &wmi_block_list, list) { - block = &wblock->gblock; + struct guid_block *block = &wblock->gblock; if (wblock->acpi_device->handle == handle && (block->flags & ACPI_WMI_EVENT) && - (block->notify_id == event)) - { + (block->notify_id == event)) { found_it = true; break; } @@ -1289,31 +1320,18 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, /* If a driver is bound, then notify the driver. */ if (wblock->dev.dev.driver) { - struct wmi_driver *driver; - struct acpi_object_list input; - union acpi_object params[1]; + struct wmi_driver *driver = drv_to_wdrv(wblock->dev.dev.driver); struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_status status; - driver = container_of(wblock->dev.dev.driver, - struct wmi_driver, driver); - - input.count = 1; - input.pointer = params; - params[0].type = ACPI_TYPE_INTEGER; - params[0].integer.value = event; - - status = acpi_evaluate_object(wblock->acpi_device->handle, - "_WED", &input, &evdata); + status = get_event_data(wblock, &evdata); if (ACPI_FAILURE(status)) { - dev_warn(&wblock->dev.dev, - "failed to get event data\n"); + dev_warn(&wblock->dev.dev, "failed to get event data\n"); return; } if (driver->notify) - driver->notify(&wblock->dev, - (union acpi_object *)evdata.pointer); + driver->notify(&wblock->dev, evdata.pointer); kfree(evdata.pointer); } else if (wblock->handler) { @@ -1322,25 +1340,24 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, } if (debug_event) - pr_info("DEBUG Event GUID: %pUL\n", wblock->gblock.guid); + pr_info("DEBUG: GUID %pUL event 0x%02X\n", &wblock->gblock.guid, event); acpi_bus_generate_netlink_event( wblock->acpi_device->pnp.device_class, dev_name(&wblock->dev.dev), event, 0); - } static int acpi_wmi_remove(struct platform_device *device) { struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev); - acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, + acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, acpi_wmi_notify_handler); acpi_remove_address_space_handler(acpi_device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); wmi_free_devices(acpi_device); - device_unregister((struct device *)dev_get_drvdata(&device->dev)); + device_unregister(dev_get_drvdata(&device->dev)); return 0; } @@ -1368,7 +1385,7 @@ static int acpi_wmi_probe(struct platform_device *device) } status = acpi_install_notify_handler(acpi_device->handle, - ACPI_DEVICE_NOTIFY, + ACPI_ALL_NOTIFY, acpi_wmi_notify_handler, NULL); if (ACPI_FAILURE(status)) { @@ -1397,7 +1414,7 @@ err_remove_busdev: device_unregister(wmi_bus_dev); err_remove_notify_handler: - acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, + acpi_remove_notify_handler(acpi_device->handle, ACPI_ALL_NOTIFY, acpi_wmi_notify_handler); err_remove_ec_handler: |