greybus: hd: initialise device last
[cascardo/linux.git] / drivers / staging / greybus / hd.c
1 /*
2  * Greybus Host Device
3  *
4  * Copyright 2014-2015 Google Inc.
5  * Copyright 2014-2015 Linaro Ltd.
6  *
7  * Released under the GPLv2 only.
8  */
9
10 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11
12 #include <linux/kernel.h>
13 #include <linux/slab.h>
14
15 #include "greybus.h"
16
17
18 static struct ida gb_hd_bus_id_map;
19
20 static void gb_hd_release(struct device *dev)
21 {
22         struct gb_host_device *hd = to_gb_host_device(dev);
23
24         ida_simple_remove(&gb_hd_bus_id_map, hd->bus_id);
25         ida_destroy(&hd->cport_id_map);
26         kfree(hd);
27 }
28
29 struct device_type greybus_hd_type = {
30         .name           = "greybus_host_device",
31         .release        = gb_hd_release,
32 };
33
34 struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver,
35                                         struct device *parent,
36                                         size_t buffer_size_max,
37                                         size_t num_cports)
38 {
39         struct gb_host_device *hd;
40         int ret;
41
42         /*
43          * Validate that the driver implements all of the callbacks
44          * so that we don't have to every time we make them.
45          */
46         if ((!driver->message_send) || (!driver->message_cancel)) {
47                 pr_err("Must implement all gb_hd_driver callbacks!\n");
48                 return ERR_PTR(-EINVAL);
49         }
50
51         if (buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN) {
52                 dev_err(parent, "greybus host-device buffers too small\n");
53                 return ERR_PTR(-EINVAL);
54         }
55
56         if (num_cports == 0 || num_cports > CPORT_ID_MAX + 1) {
57                 dev_err(parent, "Invalid number of CPorts: %zu\n", num_cports);
58                 return ERR_PTR(-EINVAL);
59         }
60
61         /*
62          * Make sure to never allocate messages larger than what the Greybus
63          * protocol supports.
64          */
65         if (buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) {
66                 dev_warn(parent, "limiting buffer size to %u\n",
67                          GB_OPERATION_MESSAGE_SIZE_MAX);
68                 buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX;
69         }
70
71         hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
72         if (!hd)
73                 return ERR_PTR(-ENOMEM);
74
75         ret = ida_simple_get(&gb_hd_bus_id_map, 1, 0, GFP_KERNEL);
76         if (ret < 0) {
77                 kfree(hd);
78                 return ERR_PTR(ret);
79         }
80         hd->bus_id = ret;
81
82         hd->driver = driver;
83         INIT_LIST_HEAD(&hd->interfaces);
84         INIT_LIST_HEAD(&hd->connections);
85         ida_init(&hd->cport_id_map);
86         hd->buffer_size_max = buffer_size_max;
87         hd->num_cports = num_cports;
88
89         hd->dev.parent = parent;
90         hd->dev.bus = &greybus_bus_type;
91         hd->dev.type = &greybus_hd_type;
92         hd->dev.dma_mask = hd->dev.parent->dma_mask;
93         device_initialize(&hd->dev);
94         dev_set_name(&hd->dev, "greybus%d", hd->bus_id);
95
96         return hd;
97 }
98 EXPORT_SYMBOL_GPL(gb_hd_create);
99
100 static int gb_hd_create_svc_connection(struct gb_host_device *hd)
101 {
102         hd->svc_connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID,
103                                                         GREYBUS_PROTOCOL_SVC);
104         if (!hd->svc_connection) {
105                 dev_err(&hd->dev, "failed to create svc connection\n");
106                 return -ENOMEM;
107         }
108
109         return 0;
110 }
111
112 int gb_hd_add(struct gb_host_device *hd)
113 {
114         int ret;
115
116         ret = device_add(&hd->dev);
117         if (ret)
118                 return ret;
119
120         ret = gb_hd_create_svc_connection(hd);
121         if (ret) {
122                 device_del(&hd->dev);
123                 return ret;
124         }
125
126         ret = gb_connection_init(hd->svc_connection);
127         if (ret) {
128                 gb_connection_destroy(hd->svc_connection);
129                 device_del(&hd->dev);
130                 return ret;
131         }
132
133         return 0;
134 }
135 EXPORT_SYMBOL_GPL(gb_hd_add);
136
137 void gb_hd_del(struct gb_host_device *hd)
138 {
139         gb_interfaces_remove(hd);
140
141         gb_connection_destroy(hd->svc_connection);
142
143         device_del(&hd->dev);
144 }
145 EXPORT_SYMBOL_GPL(gb_hd_del);
146
147 void gb_hd_put(struct gb_host_device *hd)
148 {
149         put_device(&hd->dev);
150 }
151 EXPORT_SYMBOL_GPL(gb_hd_put);
152
153 int __init gb_hd_init(void)
154 {
155         ida_init(&gb_hd_bus_id_map);
156
157         return 0;
158 }
159
160 void gb_hd_exit(void)
161 {
162         ida_destroy(&gb_hd_bus_id_map);
163 }