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