1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Greybus bundles |
4 | * |
5 | * Copyright 2014-2015 Google Inc. |
6 | * Copyright 2014-2015 Linaro Ltd. |
7 | */ |
8 | |
9 | #include <linux/greybus.h> |
10 | #include "greybus_trace.h" |
11 | |
12 | static ssize_t bundle_class_show(struct device *dev, |
13 | struct device_attribute *attr, char *buf) |
14 | { |
15 | struct gb_bundle *bundle = to_gb_bundle(dev); |
16 | |
17 | return sprintf(buf, fmt: "0x%02x\n" , bundle->class); |
18 | } |
19 | static DEVICE_ATTR_RO(bundle_class); |
20 | |
21 | static ssize_t bundle_id_show(struct device *dev, |
22 | struct device_attribute *attr, char *buf) |
23 | { |
24 | struct gb_bundle *bundle = to_gb_bundle(dev); |
25 | |
26 | return sprintf(buf, fmt: "%u\n" , bundle->id); |
27 | } |
28 | static DEVICE_ATTR_RO(bundle_id); |
29 | |
30 | static ssize_t state_show(struct device *dev, struct device_attribute *attr, |
31 | char *buf) |
32 | { |
33 | struct gb_bundle *bundle = to_gb_bundle(dev); |
34 | |
35 | if (!bundle->state) |
36 | return sprintf(buf, fmt: "\n" ); |
37 | |
38 | return sprintf(buf, fmt: "%s\n" , bundle->state); |
39 | } |
40 | |
41 | static ssize_t state_store(struct device *dev, struct device_attribute *attr, |
42 | const char *buf, size_t size) |
43 | { |
44 | struct gb_bundle *bundle = to_gb_bundle(dev); |
45 | |
46 | kfree(objp: bundle->state); |
47 | bundle->state = kstrdup(s: buf, GFP_KERNEL); |
48 | if (!bundle->state) |
49 | return -ENOMEM; |
50 | |
51 | /* Tell userspace that the file contents changed */ |
52 | sysfs_notify(kobj: &bundle->dev.kobj, NULL, attr: "state" ); |
53 | |
54 | return size; |
55 | } |
56 | static DEVICE_ATTR_RW(state); |
57 | |
58 | static struct attribute *bundle_attrs[] = { |
59 | &dev_attr_bundle_class.attr, |
60 | &dev_attr_bundle_id.attr, |
61 | &dev_attr_state.attr, |
62 | NULL, |
63 | }; |
64 | |
65 | ATTRIBUTE_GROUPS(bundle); |
66 | |
67 | static struct gb_bundle *gb_bundle_find(struct gb_interface *intf, |
68 | u8 bundle_id) |
69 | { |
70 | struct gb_bundle *bundle; |
71 | |
72 | list_for_each_entry(bundle, &intf->bundles, links) { |
73 | if (bundle->id == bundle_id) |
74 | return bundle; |
75 | } |
76 | |
77 | return NULL; |
78 | } |
79 | |
80 | static void gb_bundle_release(struct device *dev) |
81 | { |
82 | struct gb_bundle *bundle = to_gb_bundle(dev); |
83 | |
84 | trace_gb_bundle_release(bundle); |
85 | |
86 | kfree(objp: bundle->state); |
87 | kfree(objp: bundle->cport_desc); |
88 | kfree(objp: bundle); |
89 | } |
90 | |
91 | #ifdef CONFIG_PM |
92 | static void gb_bundle_disable_all_connections(struct gb_bundle *bundle) |
93 | { |
94 | struct gb_connection *connection; |
95 | |
96 | list_for_each_entry(connection, &bundle->connections, bundle_links) |
97 | gb_connection_disable(connection); |
98 | } |
99 | |
100 | static void gb_bundle_enable_all_connections(struct gb_bundle *bundle) |
101 | { |
102 | struct gb_connection *connection; |
103 | |
104 | list_for_each_entry(connection, &bundle->connections, bundle_links) |
105 | gb_connection_enable(connection); |
106 | } |
107 | |
108 | static int gb_bundle_suspend(struct device *dev) |
109 | { |
110 | struct gb_bundle *bundle = to_gb_bundle(dev); |
111 | const struct dev_pm_ops *pm = dev->driver->pm; |
112 | int ret; |
113 | |
114 | if (pm && pm->runtime_suspend) { |
115 | ret = pm->runtime_suspend(&bundle->dev); |
116 | if (ret) |
117 | return ret; |
118 | } else { |
119 | gb_bundle_disable_all_connections(bundle); |
120 | } |
121 | |
122 | ret = gb_control_bundle_suspend(control: bundle->intf->control, bundle_id: bundle->id); |
123 | if (ret) { |
124 | if (pm && pm->runtime_resume) |
125 | ret = pm->runtime_resume(dev); |
126 | else |
127 | gb_bundle_enable_all_connections(bundle); |
128 | |
129 | return ret; |
130 | } |
131 | |
132 | return 0; |
133 | } |
134 | |
135 | static int gb_bundle_resume(struct device *dev) |
136 | { |
137 | struct gb_bundle *bundle = to_gb_bundle(dev); |
138 | const struct dev_pm_ops *pm = dev->driver->pm; |
139 | int ret; |
140 | |
141 | ret = gb_control_bundle_resume(control: bundle->intf->control, bundle_id: bundle->id); |
142 | if (ret) |
143 | return ret; |
144 | |
145 | if (pm && pm->runtime_resume) { |
146 | ret = pm->runtime_resume(dev); |
147 | if (ret) |
148 | return ret; |
149 | } else { |
150 | gb_bundle_enable_all_connections(bundle); |
151 | } |
152 | |
153 | return 0; |
154 | } |
155 | |
156 | static int gb_bundle_idle(struct device *dev) |
157 | { |
158 | pm_runtime_mark_last_busy(dev); |
159 | pm_request_autosuspend(dev); |
160 | |
161 | return 0; |
162 | } |
163 | #endif |
164 | |
165 | static const struct dev_pm_ops gb_bundle_pm_ops = { |
166 | SET_RUNTIME_PM_OPS(gb_bundle_suspend, gb_bundle_resume, gb_bundle_idle) |
167 | }; |
168 | |
169 | struct device_type greybus_bundle_type = { |
170 | .name = "greybus_bundle" , |
171 | .release = gb_bundle_release, |
172 | .pm = &gb_bundle_pm_ops, |
173 | }; |
174 | |
175 | /* |
176 | * Create a gb_bundle structure to represent a discovered |
177 | * bundle. Returns a pointer to the new bundle or a null |
178 | * pointer if a failure occurs due to memory exhaustion. |
179 | */ |
180 | struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, |
181 | u8 class) |
182 | { |
183 | struct gb_bundle *bundle; |
184 | |
185 | if (bundle_id == BUNDLE_ID_NONE) { |
186 | dev_err(&intf->dev, "can't use bundle id %u\n" , bundle_id); |
187 | return NULL; |
188 | } |
189 | |
190 | /* |
191 | * Reject any attempt to reuse a bundle id. We initialize |
192 | * these serially, so there's no need to worry about keeping |
193 | * the interface bundle list locked here. |
194 | */ |
195 | if (gb_bundle_find(intf, bundle_id)) { |
196 | dev_err(&intf->dev, "duplicate bundle id %u\n" , bundle_id); |
197 | return NULL; |
198 | } |
199 | |
200 | bundle = kzalloc(size: sizeof(*bundle), GFP_KERNEL); |
201 | if (!bundle) |
202 | return NULL; |
203 | |
204 | bundle->intf = intf; |
205 | bundle->id = bundle_id; |
206 | bundle->class = class; |
207 | INIT_LIST_HEAD(list: &bundle->connections); |
208 | |
209 | bundle->dev.parent = &intf->dev; |
210 | bundle->dev.bus = &greybus_bus_type; |
211 | bundle->dev.type = &greybus_bundle_type; |
212 | bundle->dev.groups = bundle_groups; |
213 | bundle->dev.dma_mask = intf->dev.dma_mask; |
214 | device_initialize(dev: &bundle->dev); |
215 | dev_set_name(dev: &bundle->dev, name: "%s.%d" , dev_name(dev: &intf->dev), bundle_id); |
216 | |
217 | list_add(new: &bundle->links, head: &intf->bundles); |
218 | |
219 | trace_gb_bundle_create(bundle); |
220 | |
221 | return bundle; |
222 | } |
223 | |
224 | int gb_bundle_add(struct gb_bundle *bundle) |
225 | { |
226 | int ret; |
227 | |
228 | ret = device_add(dev: &bundle->dev); |
229 | if (ret) { |
230 | dev_err(&bundle->dev, "failed to register bundle: %d\n" , ret); |
231 | return ret; |
232 | } |
233 | |
234 | trace_gb_bundle_add(bundle); |
235 | |
236 | return 0; |
237 | } |
238 | |
239 | /* |
240 | * Tear down a previously set up bundle. |
241 | */ |
242 | void gb_bundle_destroy(struct gb_bundle *bundle) |
243 | { |
244 | trace_gb_bundle_destroy(bundle); |
245 | |
246 | if (device_is_registered(dev: &bundle->dev)) |
247 | device_del(dev: &bundle->dev); |
248 | |
249 | list_del(entry: &bundle->links); |
250 | |
251 | put_device(dev: &bundle->dev); |
252 | } |
253 | |