diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-07 13:17:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-07 13:17:46 +0000 |
commit | 7f3a4257159dea8e7ef66d1a539dc6df708b8ed3 (patch) | |
tree | bcc69b5f4609f348fac49e2f59e210b29eaea783 /drivers/usb/typec | |
parent | Adding upstream version 6.9.12. (diff) | |
download | linux-7f3a4257159dea8e7ef66d1a539dc6df708b8ed3.tar.xz linux-7f3a4257159dea8e7ef66d1a539dc6df708b8ed3.zip |
Adding upstream version 6.10.3.upstream/6.10.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/usb/typec')
-rw-r--r-- | drivers/usb/typec/altmodes/displayport.c | 1 | ||||
-rw-r--r-- | drivers/usb/typec/altmodes/nvidia.c | 1 | ||||
-rw-r--r-- | drivers/usb/typec/mux/Kconfig | 2 | ||||
-rw-r--r-- | drivers/usb/typec/mux/gpio-sbu-mux.c | 8 | ||||
-rw-r--r-- | drivers/usb/typec/mux/nb7vpq904m.c | 7 | ||||
-rw-r--r-- | drivers/usb/typec/mux/ptn36502.c | 55 | ||||
-rw-r--r-- | drivers/usb/typec/stusb160x.c | 2 | ||||
-rw-r--r-- | drivers/usb/typec/tipd/core.c | 5 | ||||
-rw-r--r-- | drivers/usb/typec/ucsi/ucsi.c | 162 | ||||
-rw-r--r-- | drivers/usb/typec/ucsi/ucsi.h | 8 | ||||
-rw-r--r-- | drivers/usb/typec/ucsi/ucsi_acpi.c | 87 | ||||
-rw-r--r-- | drivers/usb/typec/ucsi/ucsi_glink.c | 78 | ||||
-rw-r--r-- | drivers/usb/typec/ucsi/ucsi_stm32g0.c | 1 |
13 files changed, 215 insertions, 202 deletions
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 038dc51f42..596cd48060 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -802,7 +802,6 @@ static struct typec_altmode_driver dp_altmode_driver = { .remove = dp_altmode_remove, .driver = { .name = "typec_displayport", - .owner = THIS_MODULE, .dev_groups = displayport_groups, }, }; diff --git a/drivers/usb/typec/altmodes/nvidia.c b/drivers/usb/typec/altmodes/nvidia.c index c367697364..fe70b36f07 100644 --- a/drivers/usb/typec/altmodes/nvidia.c +++ b/drivers/usb/typec/altmodes/nvidia.c @@ -35,7 +35,6 @@ static struct typec_altmode_driver nvidia_altmode_driver = { .remove = nvidia_altmode_remove, .driver = { .name = "typec_nvidia", - .owner = THIS_MODULE, }, }; module_typec_altmode_driver(nvidia_altmode_driver); diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index 399c7b0983..ce7db6ad30 100644 --- a/drivers/usb/typec/mux/Kconfig +++ b/drivers/usb/typec/mux/Kconfig @@ -60,7 +60,7 @@ config TYPEC_MUX_PTN36502 tristate "NXP PTN36502 Type-C redriver driver" depends on I2C depends on DRM || DRM=n - select DRM_PANEL_BRIDGE if DRM + select DRM_AUX_BRIDGE if DRM_BRIDGE && OF select REGMAP_I2C help Say Y or M if your system has a NXP PTN36502 Type-C redriver chip diff --git a/drivers/usb/typec/mux/gpio-sbu-mux.c b/drivers/usb/typec/mux/gpio-sbu-mux.c index ad60fd4e84..374168482d 100644 --- a/drivers/usb/typec/mux/gpio-sbu-mux.c +++ b/drivers/usb/typec/mux/gpio-sbu-mux.c @@ -48,10 +48,10 @@ static int gpio_sbu_switch_set(struct typec_switch_dev *sw, } if (enabled != sbu_mux->enabled) - gpiod_set_value(sbu_mux->enable_gpio, enabled); + gpiod_set_value_cansleep(sbu_mux->enable_gpio, enabled); if (swapped != sbu_mux->swapped) - gpiod_set_value(sbu_mux->select_gpio, swapped); + gpiod_set_value_cansleep(sbu_mux->select_gpio, swapped); sbu_mux->enabled = enabled; sbu_mux->swapped = swapped; @@ -82,7 +82,7 @@ static int gpio_sbu_mux_set(struct typec_mux_dev *mux, break; } - gpiod_set_value(sbu_mux->enable_gpio, sbu_mux->enabled); + gpiod_set_value_cansleep(sbu_mux->enable_gpio, sbu_mux->enabled); mutex_unlock(&sbu_mux->lock); @@ -141,7 +141,7 @@ static void gpio_sbu_mux_remove(struct platform_device *pdev) { struct gpio_sbu_mux *sbu_mux = platform_get_drvdata(pdev); - gpiod_set_value(sbu_mux->enable_gpio, 0); + gpiod_set_value_cansleep(sbu_mux->enable_gpio, 0); typec_mux_unregister(sbu_mux->mux); typec_switch_unregister(sbu_mux->sw); diff --git a/drivers/usb/typec/mux/nb7vpq904m.c b/drivers/usb/typec/mux/nb7vpq904m.c index b178267137..9fe4ce6f62 100644 --- a/drivers/usb/typec/mux/nb7vpq904m.c +++ b/drivers/usb/typec/mux/nb7vpq904m.c @@ -413,7 +413,7 @@ static int nb7vpq904m_probe(struct i2c_client *client) ret = nb7vpq904m_parse_data_lanes_mapping(nb7); if (ret) - return ret; + goto err_switch_put; ret = regulator_enable(nb7->vcc_supply); if (ret) @@ -456,6 +456,9 @@ err_disable_gpio: gpiod_set_value(nb7->enable_gpio, 0); regulator_disable(nb7->vcc_supply); +err_switch_put: + typec_switch_put(nb7->typec_switch); + return ret; } @@ -469,6 +472,8 @@ static void nb7vpq904m_remove(struct i2c_client *client) gpiod_set_value(nb7->enable_gpio, 0); regulator_disable(nb7->vcc_supply); + + typec_switch_put(nb7->typec_switch); } static const struct i2c_device_id nb7vpq904m_table[] = { diff --git a/drivers/usb/typec/mux/ptn36502.c b/drivers/usb/typec/mux/ptn36502.c index 72ae38a1b2..88136a6d6f 100644 --- a/drivers/usb/typec/mux/ptn36502.c +++ b/drivers/usb/typec/mux/ptn36502.c @@ -8,7 +8,7 @@ * Copyright (C) 2023 Dmitry Baryshkov <dmitry.baryshkov@linaro.org> */ -#include <drm/drm_bridge.h> +#include <drm/bridge/aux-bridge.h> #include <linux/bitfield.h> #include <linux/i2c.h> #include <linux/kernel.h> @@ -68,8 +68,6 @@ struct ptn36502 { struct typec_switch *typec_switch; - struct drm_bridge bridge; - struct mutex lock; /* protect non-concurrent retimer & switch */ enum typec_orientation orientation; @@ -283,44 +281,6 @@ static int ptn36502_detect(struct ptn36502 *ptn) return 0; } -#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DRM_PANEL_BRIDGE) -static int ptn36502_bridge_attach(struct drm_bridge *bridge, - enum drm_bridge_attach_flags flags) -{ - struct ptn36502 *ptn = container_of(bridge, struct ptn36502, bridge); - struct drm_bridge *next_bridge; - - if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) - return -EINVAL; - - next_bridge = devm_drm_of_get_bridge(&ptn->client->dev, ptn->client->dev.of_node, 0, 0); - if (IS_ERR(next_bridge)) { - dev_err(&ptn->client->dev, "failed to acquire drm_bridge: %pe\n", next_bridge); - return PTR_ERR(next_bridge); - } - - return drm_bridge_attach(bridge->encoder, next_bridge, bridge, - DRM_BRIDGE_ATTACH_NO_CONNECTOR); -} - -static const struct drm_bridge_funcs ptn36502_bridge_funcs = { - .attach = ptn36502_bridge_attach, -}; - -static int ptn36502_register_bridge(struct ptn36502 *ptn) -{ - ptn->bridge.funcs = &ptn36502_bridge_funcs; - ptn->bridge.of_node = ptn->client->dev.of_node; - - return devm_drm_bridge_add(&ptn->client->dev, &ptn->bridge); -} -#else -static int ptn36502_register_bridge(struct ptn36502 *ptn) -{ - return 0; -} -#endif - static const struct regmap_config ptn36502_regmap = { .max_register = 0x0d, .reg_bits = 8, @@ -362,14 +322,16 @@ static int ptn36502_probe(struct i2c_client *client) "Failed to acquire orientation-switch\n"); ret = regulator_enable(ptn->vdd18_supply); - if (ret) - return dev_err_probe(dev, ret, "Failed to enable vdd18\n"); + if (ret) { + ret = dev_err_probe(dev, ret, "Failed to enable vdd18\n"); + goto err_switch_put; + } ret = ptn36502_detect(ptn); if (ret) goto err_disable_regulator; - ret = ptn36502_register_bridge(ptn); + ret = drm_aux_bridge_register(dev); if (ret) goto err_disable_regulator; @@ -403,6 +365,9 @@ err_switch_unregister: err_disable_regulator: regulator_disable(ptn->vdd18_supply); +err_switch_put: + typec_switch_put(ptn->typec_switch); + return ret; } @@ -414,6 +379,8 @@ static void ptn36502_remove(struct i2c_client *client) typec_switch_unregister(ptn->sw); regulator_disable(ptn->vdd18_supply); + + typec_switch_put(ptn->typec_switch); } static const struct i2c_device_id ptn36502_table[] = { diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c index 3ab118df1b..f3140fc04c 100644 --- a/drivers/usb/typec/stusb160x.c +++ b/drivers/usb/typec/stusb160x.c @@ -234,7 +234,7 @@ static const struct regmap_config stusb1600_regmap_config = { .readable_reg = stusb160x_reg_readable, .volatile_reg = stusb160x_reg_volatile, .precious_reg = stusb160x_reg_precious, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static bool stusb160x_get_vconn(struct stusb160x *chip) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 191f86da28..ad76dbd20e 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -1365,10 +1365,7 @@ static int tps6598x_probe(struct i2c_client *client) TPS_REG_INT_PLUG_EVENT; } - if (dev_fwnode(tps->dev)) - tps->data = device_get_match_data(tps->dev); - else - tps->data = i2c_get_match_data(client); + tps->data = i2c_get_match_data(client); if (!tps->data) return -EINVAL; diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 911d774c98..2cc7aedd49 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -625,7 +625,9 @@ static int ucsi_read_pdos(struct ucsi_connector *con, int ret; if (is_partner && - ucsi->quirks & UCSI_NO_PARTNER_PDOS) + ucsi->quirks & UCSI_NO_PARTNER_PDOS && + ((con->status.flags & UCSI_CONSTAT_PWR_DIR) || + !is_source(role))) return 0; command = UCSI_COMMAND(UCSI_GET_PDOS) | UCSI_CONNECTOR_NUMBER(con->num); @@ -680,6 +682,26 @@ static int ucsi_get_src_pdos(struct ucsi_connector *con) return ret; } +static struct usb_power_delivery_capabilities *ucsi_get_pd_caps(struct ucsi_connector *con, + enum typec_role role, + bool is_partner) +{ + struct usb_power_delivery_capabilities_desc pd_caps; + int ret; + + ret = ucsi_get_pdos(con, role, is_partner, pd_caps.pdo); + if (ret <= 0) + return ERR_PTR(ret); + + if (ret < PDO_MAX_OBJECTS) + pd_caps.pdo[ret] = 0; + + pd_caps.role = role; + + return usb_power_delivery_register_capabilities(is_partner ? con->partner_pd : con->pd, + &pd_caps); +} + static int ucsi_read_identity(struct ucsi_connector *con, u8 recipient, u8 offset, u8 bytes, void *resp) { @@ -804,46 +826,51 @@ static int ucsi_check_altmodes(struct ucsi_connector *con) return ret; } +static void ucsi_register_device_pdos(struct ucsi_connector *con) +{ + struct ucsi *ucsi = con->ucsi; + struct usb_power_delivery_desc desc = { ucsi->cap.pd_version }; + struct usb_power_delivery_capabilities *pd_cap; + + if (con->pd) + return; + + con->pd = usb_power_delivery_register(ucsi->dev, &desc); + + pd_cap = ucsi_get_pd_caps(con, TYPEC_SOURCE, false); + if (!IS_ERR(pd_cap)) + con->port_source_caps = pd_cap; + + pd_cap = ucsi_get_pd_caps(con, TYPEC_SINK, false); + if (!IS_ERR(pd_cap)) + con->port_sink_caps = pd_cap; + + typec_port_set_usb_power_delivery(con->port, con->pd); +} + static int ucsi_register_partner_pdos(struct ucsi_connector *con) { struct usb_power_delivery_desc desc = { con->ucsi->cap.pd_version }; - struct usb_power_delivery_capabilities_desc caps; struct usb_power_delivery_capabilities *cap; - int ret; if (con->partner_pd) return 0; - con->partner_pd = usb_power_delivery_register(NULL, &desc); + con->partner_pd = typec_partner_usb_power_delivery_register(con->partner, &desc); if (IS_ERR(con->partner_pd)) return PTR_ERR(con->partner_pd); - ret = ucsi_get_pdos(con, TYPEC_SOURCE, 1, caps.pdo); - if (ret > 0) { - if (ret < PDO_MAX_OBJECTS) - caps.pdo[ret] = 0; - - caps.role = TYPEC_SOURCE; - cap = usb_power_delivery_register_capabilities(con->partner_pd, &caps); - if (IS_ERR(cap)) - return PTR_ERR(cap); - - con->partner_source_caps = cap; - } - - ret = ucsi_get_pdos(con, TYPEC_SINK, 1, caps.pdo); - if (ret > 0) { - if (ret < PDO_MAX_OBJECTS) - caps.pdo[ret] = 0; + cap = ucsi_get_pd_caps(con, TYPEC_SOURCE, true); + if (IS_ERR(cap)) + return PTR_ERR(cap); - caps.role = TYPEC_SINK; + con->partner_source_caps = cap; - cap = usb_power_delivery_register_capabilities(con->partner_pd, &caps); - if (IS_ERR(cap)) - return PTR_ERR(cap); + cap = ucsi_get_pd_caps(con, TYPEC_SINK, true); + if (IS_ERR(cap)) + return PTR_ERR(cap); - con->partner_sink_caps = cap; - } + con->partner_sink_caps = cap; return typec_partner_set_usb_power_delivery(con->partner, con->partner_pd); } @@ -981,6 +1008,9 @@ static int ucsi_register_partner(struct ucsi_connector *con) break; } + if (pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD) + ucsi_register_device_pdos(con); + desc.identity = &con->partner_identity; desc.usb_pd = pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD; desc.pd_revision = UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV_AS_BCD(con->cap.flags); @@ -1174,6 +1204,9 @@ static void ucsi_handle_connector_change(struct work_struct *work) trace_ucsi_connector_change(con->num, &con->status); + if (ucsi->ops->connector_status) + ucsi->ops->connector_status(con); + role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) { @@ -1312,6 +1345,9 @@ static int ucsi_reset_ppm(struct ucsi *ucsi) goto out; } + /* Give the PPM time to process a reset before reading CCI */ + msleep(20); + ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci)); if (ret) goto out; @@ -1325,7 +1361,6 @@ static int ucsi_reset_ppm(struct ucsi *ucsi) goto out; } - msleep(20); } while (!(cci & UCSI_CCI_RESET_COMPLETE)); out: @@ -1465,9 +1500,6 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con) static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) { - struct usb_power_delivery_desc desc = { ucsi->cap.pd_version}; - struct usb_power_delivery_capabilities_desc pd_caps; - struct usb_power_delivery_capabilities *pd_cap; struct typec_capability *cap = &con->typec_cap; enum typec_accessory *accessory = cap->accessory; enum usb_role u_role = USB_ROLE_NONE; @@ -1534,6 +1566,9 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) cap->driver_data = con; cap->ops = &ucsi_ops; + if (ucsi->ops->update_connector) + ucsi->ops->update_connector(con); + ret = ucsi_register_port_psy(con); if (ret) goto out; @@ -1545,40 +1580,8 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) goto out; } - con->pd = usb_power_delivery_register(ucsi->dev, &desc); - - ret = ucsi_get_pdos(con, TYPEC_SOURCE, 0, pd_caps.pdo); - if (ret > 0) { - if (ret < PDO_MAX_OBJECTS) - pd_caps.pdo[ret] = 0; - - pd_caps.role = TYPEC_SOURCE; - pd_cap = usb_power_delivery_register_capabilities(con->pd, &pd_caps); - if (IS_ERR(pd_cap)) { - ret = PTR_ERR(pd_cap); - goto out; - } - - con->port_source_caps = pd_cap; - } - - memset(&pd_caps, 0, sizeof(pd_caps)); - ret = ucsi_get_pdos(con, TYPEC_SINK, 0, pd_caps.pdo); - if (ret > 0) { - if (ret < PDO_MAX_OBJECTS) - pd_caps.pdo[ret] = 0; - - pd_caps.role = TYPEC_SINK; - pd_cap = usb_power_delivery_register_capabilities(con->pd, &pd_caps); - if (IS_ERR(pd_cap)) { - ret = PTR_ERR(pd_cap); - goto out; - } - - con->port_sink_caps = pd_cap; - } - - typec_port_set_usb_power_delivery(con->port, con->pd); + if (!(ucsi->quirks & UCSI_DELAY_DEVICE_PDOS)) + ucsi_register_device_pdos(con); /* Alternate modes */ ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_CON); @@ -1598,6 +1601,9 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) } ret = 0; /* ucsi_send_command() returns length on success */ + if (ucsi->ops->connector_status) + ucsi->ops->connector_status(con); + switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { case UCSI_CONSTAT_PARTNER_TYPE_UFP: case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: @@ -1641,6 +1647,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) if (con->partner && UCSI_CONSTAT_PWR_OPMODE(con->status.flags) == UCSI_CONSTAT_PWR_OPMODE_PD) { + ucsi_register_device_pdos(con); ucsi_get_src_pdos(con); ucsi_check_altmodes(con); } @@ -1660,6 +1667,27 @@ out_unlock: return ret; } +static u64 ucsi_get_supported_notifications(struct ucsi *ucsi) +{ + u8 features = ucsi->cap.features; + u64 ntfy = UCSI_ENABLE_NTFY_ALL; + + if (!(features & UCSI_CAP_ALT_MODE_DETAILS)) + ntfy &= ~UCSI_ENABLE_NTFY_CAM_CHANGE; + + if (!(features & UCSI_CAP_PDO_DETAILS)) + ntfy &= ~(UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE | + UCSI_ENABLE_NTFY_CAP_CHANGE); + + if (!(features & UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS)) + ntfy &= ~UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE; + + if (!(features & UCSI_CAP_PD_RESET)) + ntfy &= ~UCSI_ENABLE_NTFY_PD_RESET_COMPLETE; + + return ntfy; +} + /** * ucsi_init - Initialize UCSI interface * @ucsi: UCSI to be initialized @@ -1714,8 +1742,8 @@ static int ucsi_init(struct ucsi *ucsi) goto err_unregister; } - /* Enable all notifications */ - ntfy = UCSI_ENABLE_NTFY_ALL; + /* Enable all supported notifications */ + ntfy = ucsi_get_supported_notifications(ucsi); command = UCSI_SET_NOTIFICATION_ENABLE | ntfy; ret = ucsi_send_command(ucsi, command, NULL, 0); if (ret < 0) diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 0e7c92eb1b..c4d103db9d 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -16,6 +16,7 @@ struct ucsi; struct ucsi_altmode; +struct ucsi_connector; struct dentry; /* UCSI offsets (Bytes) */ @@ -59,6 +60,8 @@ struct dentry; * @sync_write: Blocking write operation * @async_write: Non-blocking write operation * @update_altmodes: Squashes duplicate DP altmodes + * @update_connector: Update connector capabilities before registering + * @connector_status: Updates connector status, called holding connector lock * * Read and write routines for UCSI interface. @sync_write must wait for the * Command Completion Event from the PPM before returning, and @async_write must @@ -73,6 +76,8 @@ struct ucsi_operations { const void *val, size_t val_len); bool (*update_altmodes)(struct ucsi *ucsi, struct ucsi_altmode *orig, struct ucsi_altmode *updated); + void (*update_connector)(struct ucsi_connector *con); + void (*connector_status)(struct ucsi_connector *con); }; struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops); @@ -403,11 +408,10 @@ struct ucsi { /* PPM communication flags */ unsigned long flags; #define EVENT_PENDING 0 -#define COMMAND_PENDING 1 -#define ACK_PENDING 2 unsigned long quirks; #define UCSI_NO_PARTNER_PDOS BIT(0) /* Don't read partner's PDOs */ +#define UCSI_DELAY_DEVICE_PDOS BIT(1) /* Reading PDOs fails until the parter is in PD mode */ }; #define UCSI_MAX_SVID 5 diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 7b3ac133ef..adf32ca0f7 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -23,9 +23,9 @@ struct ucsi_acpi { void *base; struct completion complete; unsigned long flags; -#define UCSI_ACPI_SUPPRESS_EVENT 0 #define UCSI_ACPI_COMMAND_PENDING 1 #define UCSI_ACPI_ACK_PENDING 2 +#define UCSI_ACPI_CHECK_BOGUS_EVENT 3 guid_t guid; u64 cmd; }; @@ -129,46 +129,55 @@ static const struct ucsi_operations ucsi_zenbook_ops = { .async_write = ucsi_acpi_async_write }; -/* - * Some Dell laptops don't like ACK commands with the - * UCSI_ACK_CONNECTOR_CHANGE but not the UCSI_ACK_COMMAND_COMPLETE - * bit set. To work around this send a dummy command and bundle the - * UCSI_ACK_CONNECTOR_CHANGE with the UCSI_ACK_COMMAND_COMPLETE - * for the dummy command. - */ -static int -ucsi_dell_sync_write(struct ucsi *ucsi, unsigned int offset, - const void *val, size_t val_len) +static int ucsi_gram_read(struct ucsi *ucsi, unsigned int offset, + void *val, size_t val_len) { + u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE | + UCSI_CONSTAT_PDOS_CHANGE; struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - u64 cmd = *(u64 *)val; - u64 dummycmd = UCSI_GET_CAPABILITY; + struct ucsi_connector_status *status; int ret; - if (cmd == (UCSI_ACK_CC_CI | UCSI_ACK_CONNECTOR_CHANGE)) { - cmd |= UCSI_ACK_COMMAND_COMPLETE; - - /* - * The UCSI core thinks it is sending a connector change ack - * and will accept new connector change events. We don't want - * this to happen for the dummy command as its response will - * still report the very event that the core is trying to clear. - */ - set_bit(UCSI_ACPI_SUPPRESS_EVENT, &ua->flags); - ret = ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &dummycmd, - sizeof(dummycmd)); - clear_bit(UCSI_ACPI_SUPPRESS_EVENT, &ua->flags); - - if (ret < 0) - return ret; + ret = ucsi_acpi_read(ucsi, offset, val, val_len); + if (ret < 0) + return ret; + + if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS && + test_bit(UCSI_ACPI_CHECK_BOGUS_EVENT, &ua->flags) && + offset == UCSI_MESSAGE_IN) { + status = (struct ucsi_connector_status *)val; + + /* Clear the bogus change */ + if (status->change == bogus_change) + status->change = 0; + + clear_bit(UCSI_ACPI_CHECK_BOGUS_EVENT, &ua->flags); } - return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd)); + return ret; } -static const struct ucsi_operations ucsi_dell_ops = { - .read = ucsi_acpi_read, - .sync_write = ucsi_dell_sync_write, +static int ucsi_gram_sync_write(struct ucsi *ucsi, unsigned int offset, + const void *val, size_t val_len) +{ + struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); + int ret; + + ret = ucsi_acpi_sync_write(ucsi, offset, val, val_len); + if (ret < 0) + return ret; + + if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS && + ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) && + ua->cmd & UCSI_GET_PDOS_SRC_PDOS) + set_bit(UCSI_ACPI_CHECK_BOGUS_EVENT, &ua->flags); + + return ret; +} + +static const struct ucsi_operations ucsi_gram_ops = { + .read = ucsi_gram_read, + .sync_write = ucsi_gram_sync_write, .async_write = ucsi_acpi_async_write }; @@ -182,9 +191,11 @@ static const struct dmi_system_id ucsi_acpi_quirks[] = { }, { .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), + DMI_MATCH(DMI_PRODUCT_FAMILY, "LG gram PC"), + DMI_MATCH(DMI_PRODUCT_NAME, "90Q"), }, - .driver_data = (void *)&ucsi_dell_ops, + .driver_data = (void *)&ucsi_gram_ops, }, { } }; @@ -199,11 +210,11 @@ static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data) if (ret) return; - if (UCSI_CCI_CONNECTOR(cci) && - !test_bit(UCSI_ACPI_SUPPRESS_EVENT, &ua->flags)) + if (UCSI_CCI_CONNECTOR(cci)) ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci)); - if (cci & UCSI_CCI_ACK_COMPLETE && test_bit(ACK_PENDING, &ua->flags)) + if (cci & UCSI_CCI_ACK_COMPLETE && + test_bit(UCSI_ACPI_ACK_PENDING, &ua->flags)) complete(&ua->complete); if (cci & UCSI_CCI_COMMAND_COMPLETE && test_bit(UCSI_ACPI_COMMAND_PENDING, &ua->flags)) diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 4763bd6e55..2fa973afe4 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -14,7 +14,7 @@ #include <linux/soc/qcom/pmic_glink.h> #include "ucsi.h" -#define PMIC_GLINK_MAX_PORTS 2 +#define PMIC_GLINK_MAX_PORTS 3 #define UCSI_BUF_SIZE 48 @@ -58,7 +58,6 @@ struct pmic_glink_ucsi { struct device *dev; struct gpio_desc *port_orientation[PMIC_GLINK_MAX_PORTS]; - struct typec_switch *port_switch[PMIC_GLINK_MAX_PORTS]; struct pmic_glink_client *client; @@ -187,10 +186,41 @@ static int pmic_glink_ucsi_sync_write(struct ucsi *__ucsi, unsigned int offset, return ret; } +static void pmic_glink_ucsi_update_connector(struct ucsi_connector *con) +{ + struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(con->ucsi); + int i; + + for (i = 0; i < PMIC_GLINK_MAX_PORTS; i++) { + if (ucsi->port_orientation[i]) + con->typec_cap.orientation_aware = true; + } +} + +static void pmic_glink_ucsi_connector_status(struct ucsi_connector *con) +{ + struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(con->ucsi); + int orientation; + + if (con->num >= PMIC_GLINK_MAX_PORTS || + !ucsi->port_orientation[con->num - 1]) + return; + + orientation = gpiod_get_value(ucsi->port_orientation[con->num - 1]); + if (orientation >= 0) { + typec_set_orientation(con->port, + orientation ? + TYPEC_ORIENTATION_REVERSE : + TYPEC_ORIENTATION_NORMAL); + } +} + static const struct ucsi_operations pmic_glink_ucsi_ops = { .read = pmic_glink_ucsi_read, .sync_write = pmic_glink_ucsi_sync_write, - .async_write = pmic_glink_ucsi_async_write + .async_write = pmic_glink_ucsi_async_write, + .update_connector = pmic_glink_ucsi_update_connector, + .connector_status = pmic_glink_ucsi_connector_status, }; static void pmic_glink_ucsi_read_ack(struct pmic_glink_ucsi *ucsi, const void *data, int len) @@ -229,20 +259,8 @@ static void pmic_glink_ucsi_notify(struct work_struct *work) } con_num = UCSI_CCI_CONNECTOR(cci); - if (con_num) { - if (con_num <= PMIC_GLINK_MAX_PORTS && - ucsi->port_orientation[con_num - 1]) { - int orientation = gpiod_get_value(ucsi->port_orientation[con_num - 1]); - - if (orientation >= 0) { - typec_switch_set(ucsi->port_switch[con_num - 1], - orientation ? TYPEC_ORIENTATION_REVERSE - : TYPEC_ORIENTATION_NORMAL); - } - } - + if (con_num) ucsi_connector_change(ucsi->ucsi, con_num); - } if (ucsi->sync_pending && (cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))) { @@ -253,20 +271,6 @@ static void pmic_glink_ucsi_notify(struct work_struct *work) static void pmic_glink_ucsi_register(struct work_struct *work) { struct pmic_glink_ucsi *ucsi = container_of(work, struct pmic_glink_ucsi, register_work); - int orientation; - int i; - - for (i = 0; i < PMIC_GLINK_MAX_PORTS; i++) { - if (!ucsi->port_orientation[i]) - continue; - orientation = gpiod_get_value(ucsi->port_orientation[i]); - - if (orientation >= 0) { - typec_switch_set(ucsi->port_switch[i], - orientation ? TYPEC_ORIENTATION_REVERSE - : TYPEC_ORIENTATION_NORMAL); - } - } ucsi_register(ucsi->ucsi); } @@ -310,13 +314,16 @@ static void pmic_glink_ucsi_destroy(void *data) } static unsigned long quirk_sc8180x = UCSI_NO_PARTNER_PDOS; +static unsigned long quirk_sc8280xp = UCSI_NO_PARTNER_PDOS | UCSI_DELAY_DEVICE_PDOS; +static unsigned long quirk_sm8450 = UCSI_DELAY_DEVICE_PDOS; static const struct of_device_id pmic_glink_ucsi_of_quirks[] = { - { .compatible = "qcom,qcm6490-pmic-glink", .data = &quirk_sc8180x, }, + { .compatible = "qcom,qcm6490-pmic-glink", .data = &quirk_sc8280xp, }, { .compatible = "qcom,sc8180x-pmic-glink", .data = &quirk_sc8180x, }, - { .compatible = "qcom,sc8280xp-pmic-glink", .data = &quirk_sc8180x, }, + { .compatible = "qcom,sc8280xp-pmic-glink", .data = &quirk_sc8280xp, }, { .compatible = "qcom,sm8350-pmic-glink", .data = &quirk_sc8180x, }, - { .compatible = "qcom,sm8550-pmic-glink", .data = &quirk_sc8180x, }, + { .compatible = "qcom,sm8450-pmic-glink", .data = &quirk_sm8450, }, + { .compatible = "qcom,sm8550-pmic-glink", .data = &quirk_sm8450, }, {} }; @@ -386,11 +393,6 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev, "unable to acquire orientation gpio\n"); } ucsi->port_orientation[port] = desc; - - ucsi->port_switch[port] = fwnode_typec_switch_get(fwnode); - if (IS_ERR(ucsi->port_switch[port])) - return dev_err_probe(dev, PTR_ERR(ucsi->port_switch[port]), - "failed to acquire orientation-switch\n"); } ucsi->client = devm_pmic_glink_register_client(dev, diff --git a/drivers/usb/typec/ucsi/ucsi_stm32g0.c b/drivers/usb/typec/ucsi/ucsi_stm32g0.c index 1d7ee833eb..ac69288e8b 100644 --- a/drivers/usb/typec/ucsi/ucsi_stm32g0.c +++ b/drivers/usb/typec/ucsi/ucsi_stm32g0.c @@ -64,6 +64,7 @@ struct ucsi_stm32g0 { struct completion complete; struct device *dev; unsigned long flags; +#define COMMAND_PENDING 1 #define ACK_PENDING 2 const char *fw_name; struct ucsi *ucsi; |