firmware: ti_sci: Add a command for releasing all exclusive devices
authorLokesh Vutla <lokeshvutla@ti.com>
Fri, 7 Jun 2019 13:54:41 +0000 (19:24 +0530)
committerTom Rini <trini@konsulko.com>
Sat, 27 Jul 2019 01:49:22 +0000 (21:49 -0400)
Any host while requesting for a device can request for its exclusive
access. If an exclusive permission is obtained then it is the host's
responsibility to release the device before the software entity on
the host completes its execution. Else any other host's request for
the device will be nacked. So add a command that releases all the
exclusive devices that is acquired by the current host. This should
be used with utmost care and can be called only at the end of the
execution.

Signed-off-by: Lokesh Vutla <lokeshvutla@ti.com>
drivers/firmware/ti_sci.c
include/linux/soc/ti/ti_sci_protocol.h

index 8c68f987882b576e8a017ee1d8ce491db8a32a84..1fd29f2cdf55f9f0faa14b8ae36d08a6d6fc9533 100644 (file)
@@ -87,11 +87,18 @@ struct ti_sci_info {
        struct mbox_chan chan_notify;
        struct ti_sci_xfer xfer;
        struct list_head list;
+       struct list_head dev_list;
        bool is_secure;
        u8 host_id;
        u8 seq;
 };
 
+struct ti_sci_exclusive_dev {
+       u32 id;
+       u32 count;
+       struct list_head list;
+};
+
 #define handle_to_ti_sci_info(h) container_of(h, struct ti_sci_info, handle)
 
 /**
@@ -427,6 +434,47 @@ static int ti_sci_cmd_set_board_config_pm(const struct ti_sci_handle *handle,
                                              addr, size);
 }
 
+static struct ti_sci_exclusive_dev
+*ti_sci_get_exclusive_dev(struct list_head *dev_list, u32 id)
+{
+       struct ti_sci_exclusive_dev *dev;
+
+       list_for_each_entry(dev, dev_list, list)
+               if (dev->id == id)
+                       return dev;
+
+       return NULL;
+}
+
+static void ti_sci_add_exclusive_dev(struct ti_sci_info *info, u32 id)
+{
+       struct ti_sci_exclusive_dev *dev;
+
+       dev = ti_sci_get_exclusive_dev(&info->dev_list, id);
+       if (dev) {
+               dev->count++;
+               return;
+       }
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       dev->id = id;
+       dev->count = 1;
+       INIT_LIST_HEAD(&dev->list);
+       list_add_tail(&dev->list, &info->dev_list);
+}
+
+static void ti_sci_delete_exclusive_dev(struct ti_sci_info *info, u32 id)
+{
+       struct ti_sci_exclusive_dev *dev;
+
+       dev = ti_sci_get_exclusive_dev(&info->dev_list, id);
+       if (!dev)
+               return;
+
+       if (dev->count > 0)
+               dev->count--;
+}
+
 /**
  * ti_sci_set_device_state() - Set device state helper
  * @handle:    pointer to TI SCI handle
@@ -474,6 +522,11 @@ static int ti_sci_set_device_state(const struct ti_sci_handle *handle,
        if (!ti_sci_is_response_ack(resp))
                return -ENODEV;
 
+       if (state == MSG_DEVICE_SW_STATE_AUTO_OFF)
+               ti_sci_delete_exclusive_dev(info, id);
+       else if (flags & MSG_FLAG_DEVICE_EXCLUSIVE)
+               ti_sci_add_exclusive_dev(info, id);
+
        return ret;
 }
 
@@ -651,6 +704,25 @@ static int ti_sci_cmd_put_device(const struct ti_sci_handle *handle, u32 id)
                                       MSG_DEVICE_SW_STATE_AUTO_OFF);
 }
 
+static
+int ti_sci_cmd_release_exclusive_devices(const struct ti_sci_handle *handle)
+{
+       struct ti_sci_exclusive_dev *dev, *tmp;
+       struct ti_sci_info *info;
+       int i, cnt;
+
+       info = handle_to_ti_sci_info(handle);
+
+       list_for_each_entry_safe(dev, tmp, &info->dev_list, list) {
+               cnt = dev->count;
+               debug("%s: id = %d, cnt = %d\n", __func__, dev->id, cnt);
+               for (i = 0; i < cnt; i++)
+                       ti_sci_cmd_put_device(handle, dev->id);
+       }
+
+       return 0;
+}
+
 /**
  * ti_sci_cmd_dev_is_valid() - Is the device valid
  * @handle:    Pointer to TISCI handle as retrieved by *ti_sci_get_handle
@@ -2839,6 +2911,7 @@ static void ti_sci_setup_ops(struct ti_sci_info *info)
        dops->is_transitioning = ti_sci_cmd_dev_is_trans;
        dops->set_device_resets = ti_sci_cmd_set_device_resets;
        dops->get_device_resets = ti_sci_cmd_get_device_resets;
+       dops->release_exclusive_devices = ti_sci_cmd_release_exclusive_devices;
 
        cops->get_clock = ti_sci_cmd_get_clock;
        cops->idle_clock = ti_sci_cmd_idle_clock;
@@ -3033,6 +3106,8 @@ static int ti_sci_probe(struct udevice *dev)
 
        ret = ti_sci_cmd_get_revision(&info->handle);
 
+       INIT_LIST_HEAD(&info->dev_list);
+
        return ret;
 }
 
index cd6e5853b43f11c0b781b0716ba896c5645bb357..1cba8d9b790406231089a41daac28254ee3acbde 100644 (file)
@@ -105,6 +105,9 @@ struct ti_sci_board_ops {
  *             -reset_state: pointer to u32 which will retrieve resets
  *             Returns 0 for successful request, else returns
  *             corresponding error message.
+ * @release_exclusive_devices: Command to release all the exclusive devices
+ *             attached to this host. This should be used very carefully
+ *             and only at the end of execution of your software.
  *
  * NOTE: for all these functions, the following parameters are generic in
  * nature:
@@ -137,6 +140,7 @@ struct ti_sci_dev_ops {
                                 u32 reset_state);
        int (*get_device_resets)(const struct ti_sci_handle *handle, u32 id,
                                 u32 *reset_state);
+       int (*release_exclusive_devices)(const struct ti_sci_handle *handle);
 };
 
 /**