greybus: hd: generalise cport allocation
[cascardo/linux.git] / drivers / staging / greybus / hd.c
index 762cc03..fba6d76 100644 (file)
@@ -24,6 +24,71 @@ int gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd,
 }
 EXPORT_SYMBOL_GPL(gb_hd_output);
 
+static ssize_t bus_id_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct gb_host_device *hd = to_gb_host_device(dev);
+
+       return sprintf(buf, "%d\n", hd->bus_id);
+}
+static DEVICE_ATTR_RO(bus_id);
+
+static struct attribute *bus_attrs[] = {
+       &dev_attr_bus_id.attr,
+       NULL
+};
+ATTRIBUTE_GROUPS(bus);
+
+int gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id)
+{
+       struct ida *id_map = &hd->cport_id_map;
+       int ret;
+
+       ret = ida_simple_get(id_map, cport_id, cport_id + 1, GFP_KERNEL);
+       if (ret < 0) {
+               dev_err(&hd->dev, "failed to reserve cport %u\n", cport_id);
+               return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(gb_hd_cport_reserve);
+
+/* Locking: Caller guarantees serialisation */
+int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id,
+                               unsigned long flags)
+{
+       struct ida *id_map = &hd->cport_id_map;
+       int ida_start, ida_end;
+
+       if (hd->driver->cport_allocate)
+               return hd->driver->cport_allocate(hd, cport_id, flags);
+
+       if (cport_id < 0) {
+               ida_start = 0;
+               ida_end = hd->num_cports;
+       } else if (cport_id < hd->num_cports) {
+               ida_start = cport_id;
+               ida_end = cport_id + 1;
+       } else {
+               dev_err(&hd->dev, "cport %d not available\n", cport_id);
+               return -EINVAL;
+       }
+
+       return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL);
+}
+
+/* Locking: Caller guarantees serialisation */
+void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id)
+{
+       if (hd->driver->cport_release) {
+               hd->driver->cport_release(hd, cport_id);
+               return;
+       }
+
+       ida_simple_remove(&hd->cport_id_map, cport_id);
+}
+
 static void gb_hd_release(struct device *dev)
 {
        struct gb_host_device *hd = to_gb_host_device(dev);
@@ -98,6 +163,7 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver,
        hd->dev.parent = parent;
        hd->dev.bus = &greybus_bus_type;
        hd->dev.type = &greybus_hd_type;
+       hd->dev.groups = bus_groups;
        hd->dev.dma_mask = hd->dev.parent->dma_mask;
        device_initialize(&hd->dev);
        dev_set_name(&hd->dev, "greybus%d", hd->bus_id);