diff options
-rw-r--r-- | drivers/platform/surface/surface_aggregator_registry.c | 98 |
1 files changed, 40 insertions, 58 deletions
diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index eccb9d1007cd..685d37a7add1 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -13,10 +13,10 @@ #include <linux/kernel.h> #include <linux/limits.h> #include <linux/module.h> -#include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/types.h> +#include <linux/workqueue.h> #include <linux/surface_aggregator/controller.h> #include <linux/surface_aggregator/device.h> @@ -287,6 +287,13 @@ err: /* -- SSAM base-hub driver. ------------------------------------------------- */ +/* + * Some devices (especially battery) may need a bit of time to be fully usable + * after being (re-)connected. This delay has been determined via + * experimentation. + */ +#define SSAM_BASE_UPDATE_CONNECT_DELAY msecs_to_jiffies(2500) + enum ssam_base_hub_state { SSAM_BASE_HUB_UNINITIALIZED, SSAM_BASE_HUB_CONNECTED, @@ -296,8 +303,8 @@ enum ssam_base_hub_state { struct ssam_base_hub { struct ssam_device *sdev; - struct mutex lock; /* Guards state update checks and transitions. */ enum ssam_base_hub_state state; + struct delayed_work update_work; struct ssam_event_notifier notif; }; @@ -335,11 +342,7 @@ static ssize_t ssam_base_hub_state_show(struct device *dev, struct device_attrib char *buf) { struct ssam_base_hub *hub = dev_get_drvdata(dev); - bool connected; - - mutex_lock(&hub->lock); - connected = hub->state == SSAM_BASE_HUB_CONNECTED; - mutex_unlock(&hub->lock); + bool connected = hub->state == SSAM_BASE_HUB_CONNECTED; return sysfs_emit(buf, "%d\n", connected); } @@ -356,16 +359,20 @@ static const struct attribute_group ssam_base_hub_group = { .attrs = ssam_base_hub_attrs, }; -static int __ssam_base_hub_update(struct ssam_base_hub *hub, enum ssam_base_hub_state new) +static void ssam_base_hub_update_workfn(struct work_struct *work) { + struct ssam_base_hub *hub = container_of(work, struct ssam_base_hub, update_work.work); struct fwnode_handle *node = dev_fwnode(&hub->sdev->dev); + enum ssam_base_hub_state state; int status = 0; - lockdep_assert_held(&hub->lock); + status = ssam_base_hub_query_state(hub, &state); + if (status) + return; - if (hub->state == new) - return 0; - hub->state = new; + if (hub->state == state) + return; + hub->state = state; if (hub->state == SSAM_BASE_HUB_CONNECTED) status = ssam_hub_add_devices(&hub->sdev->dev, hub->sdev->ctrl, node); @@ -374,51 +381,28 @@ static int __ssam_base_hub_update(struct ssam_base_hub *hub, enum ssam_base_hub_ if (status) dev_err(&hub->sdev->dev, "failed to update base-hub devices: %d\n", status); - - return status; -} - -static int ssam_base_hub_update(struct ssam_base_hub *hub) -{ - enum ssam_base_hub_state state; - int status; - - mutex_lock(&hub->lock); - - status = ssam_base_hub_query_state(hub, &state); - if (!status) - status = __ssam_base_hub_update(hub, state); - - mutex_unlock(&hub->lock); - return status; } static u32 ssam_base_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event) { - struct ssam_base_hub *hub; - struct ssam_device *sdev; - enum ssam_base_hub_state new; - - hub = container_of(nf, struct ssam_base_hub, notif); - sdev = hub->sdev; + struct ssam_base_hub *hub = container_of(nf, struct ssam_base_hub, notif); + unsigned long delay; if (event->command_id != SSAM_EVENT_BAS_CID_CONNECTION) return 0; if (event->length < 1) { - dev_err(&sdev->dev, "unexpected payload size: %u\n", - event->length); + dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length); return 0; } - if (event->data[0]) - new = SSAM_BASE_HUB_CONNECTED; - else - new = SSAM_BASE_HUB_DISCONNECTED; + /* + * Delay update when the base is being connected to give devices/EC + * some time to set up. + */ + delay = event->data[0] ? SSAM_BASE_UPDATE_CONNECT_DELAY : 0; - mutex_lock(&hub->lock); - __ssam_base_hub_update(hub, new); - mutex_unlock(&hub->lock); + schedule_delayed_work(&hub->update_work, delay); /* * Do not return SSAM_NOTIF_HANDLED: The event should be picked up and @@ -430,7 +414,10 @@ static u32 ssam_base_hub_notif(struct ssam_event_notifier *nf, const struct ssam static int __maybe_unused ssam_base_hub_resume(struct device *dev) { - return ssam_base_hub_update(dev_get_drvdata(dev)); + struct ssam_base_hub *hub = dev_get_drvdata(dev); + + schedule_delayed_work(&hub->update_work, 0); + return 0; } static SIMPLE_DEV_PM_OPS(ssam_base_hub_pm_ops, NULL, ssam_base_hub_resume); @@ -443,8 +430,6 @@ static int ssam_base_hub_probe(struct ssam_device *sdev) if (!hub) return -ENOMEM; - mutex_init(&hub->lock); - hub->sdev = sdev; hub->state = SSAM_BASE_HUB_UNINITIALIZED; @@ -456,27 +441,25 @@ static int ssam_base_hub_probe(struct ssam_device *sdev) hub->notif.event.mask = SSAM_EVENT_MASK_NONE; hub->notif.event.flags = SSAM_EVENT_SEQUENCED; + INIT_DELAYED_WORK(&hub->update_work, ssam_base_hub_update_workfn); + ssam_device_set_drvdata(sdev, hub); status = ssam_notifier_register(sdev->ctrl, &hub->notif); if (status) - goto err_register; - - status = ssam_base_hub_update(hub); - if (status) - goto err_update; + return status; status = sysfs_create_group(&sdev->dev.kobj, &ssam_base_hub_group); if (status) - goto err_update; + goto err; + schedule_delayed_work(&hub->update_work, 0); return 0; -err_update: +err: ssam_notifier_unregister(sdev->ctrl, &hub->notif); + cancel_delayed_work_sync(&hub->update_work); ssam_hub_remove_devices(&sdev->dev); -err_register: - mutex_destroy(&hub->lock); return status; } @@ -487,9 +470,8 @@ static void ssam_base_hub_remove(struct ssam_device *sdev) sysfs_remove_group(&sdev->dev.kobj, &ssam_base_hub_group); ssam_notifier_unregister(sdev->ctrl, &hub->notif); + cancel_delayed_work_sync(&hub->update_work); ssam_hub_remove_devices(&sdev->dev); - - mutex_destroy(&hub->lock); } static const struct ssam_device_id ssam_base_hub_match[] = { |