Blame view

drivers/greybus/bundle.c 5.4 KB
eb50fd3a2   Greg Kroah-Hartman   staging: greybus:...
1
  // SPDX-License-Identifier: GPL-2.0
8c12cde3c   Alex Elder   greybus: define g...
2
  /*
1db0a5ff3   Greg Kroah-Hartman   greybus: bundle: ...
3
   * Greybus bundles
8c12cde3c   Alex Elder   greybus: define g...
4
   *
75052a550   Greg Kroah-Hartman   greybus: bundle: ...
5
6
   * Copyright 2014-2015 Google Inc.
   * Copyright 2014-2015 Linaro Ltd.
8c12cde3c   Alex Elder   greybus: define g...
7
   */
ec0ad8681   Greg Kroah-Hartman   staging: greybus:...
8
  #include <linux/greybus.h>
4f9c5c0bb   Alex Elder   greybus: tracing:...
9
  #include "greybus_trace.h"
8c12cde3c   Alex Elder   greybus: define g...
10

4396c00b7   Johan Hovold   greybus: bundle: ...
11
12
  static ssize_t bundle_class_show(struct device *dev,
  				 struct device_attribute *attr, char *buf)
9f5f30e71   Viresh Kumar   greybus: driver c...
13
14
  {
  	struct gb_bundle *bundle = to_gb_bundle(dev);
2b14daba1   Johan Hovold   greybus: bundle: ...
15
16
  	return sprintf(buf, "0x%02x
  ", bundle->class);
9f5f30e71   Viresh Kumar   greybus: driver c...
17
  }
4396c00b7   Johan Hovold   greybus: bundle: ...
18
  static DEVICE_ATTR_RO(bundle_class);
9f5f30e71   Viresh Kumar   greybus: driver c...
19

a97015c9e   Johan Hovold   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   Greg Kroah-Hartman   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   Nishka Dasgupta   staging: greybus:...
33
  	if (!bundle->state)
75052a550   Greg Kroah-Hartman   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   Alex Elder   greybus: bundle: ...
47
  	bundle->state = kstrdup(buf, GFP_KERNEL);
75052a550   Greg Kroah-Hartman   greybus: bundle: ...
48
49
  	if (!bundle->state)
  		return -ENOMEM;
75052a550   Greg Kroah-Hartman   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   Greg Kroah-Hartman   greybus: bundle: ...
56
  static struct attribute *bundle_attrs[] = {
4396c00b7   Johan Hovold   greybus: bundle: ...
57
  	&dev_attr_bundle_class.attr,
a97015c9e   Johan Hovold   greybus: bundle: ...
58
  	&dev_attr_bundle_id.attr,
75052a550   Greg Kroah-Hartman   greybus: bundle: ...
59
  	&dev_attr_state.attr,
f0f61b904   Greg Kroah-Hartman   greybus: hook up ...
60
61
  	NULL,
  };
1db0a5ff3   Greg Kroah-Hartman   greybus: bundle: ...
62
  ATTRIBUTE_GROUPS(bundle);
f0f61b904   Greg Kroah-Hartman   greybus: hook up ...
63

1db1b2430   Johan Hovold   greybus: bundle: ...
64
  static struct gb_bundle *gb_bundle_find(struct gb_interface *intf,
b7417e3ca   Bhanusree Pola   Staging: greybus:...
65
  					u8 bundle_id)
1db1b2430   Johan Hovold   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   Greg Kroah-Hartman   greybus: bundle: ...
76
  static void gb_bundle_release(struct device *dev)
f0f61b904   Greg Kroah-Hartman   greybus: hook up ...
77
  {
1db0a5ff3   Greg Kroah-Hartman   greybus: bundle: ...
78
  	struct gb_bundle *bundle = to_gb_bundle(dev);
f0f61b904   Greg Kroah-Hartman   greybus: hook up ...
79

4f9c5c0bb   Alex Elder   greybus: tracing:...
80
  	trace_gb_bundle_release(bundle);
75052a550   Greg Kroah-Hartman   greybus: bundle: ...
81
  	kfree(bundle->state);
98fdf5a03   Johan Hovold   greybus: core: de...
82
  	kfree(bundle->cport_desc);
1db0a5ff3   Greg Kroah-Hartman   greybus: bundle: ...
83
  	kfree(bundle);
f0f61b904   Greg Kroah-Hartman   greybus: hook up ...
84
  }
948c6227e   Greg Kroah-Hartman   staging: greybus:...
85
  #ifdef CONFIG_PM
61e13db9c   David Lin   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   Greg Kroah-Hartman   greybus: bundle: ...
162
163
164
  struct device_type greybus_bundle_type = {
  	.name =		"greybus_bundle",
  	.release =	gb_bundle_release,
61e13db9c   David Lin   greybus: bundle: ...
165
  	.pm =		&gb_bundle_pm_ops,
f0f61b904   Greg Kroah-Hartman   greybus: hook up ...
166
  };
8c12cde3c   Alex Elder   greybus: define g...
167
  /*
1db0a5ff3   Greg Kroah-Hartman   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   Alex Elder   greybus: define g...
170
171
   * pointer if a failure occurs due to memory exhaustion.
   */
7c183f70e   Viresh Kumar   greybus: bundle: ...
172
  struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id,
88e6d37c4   Viresh Kumar   greybus: bundle: ...
173
  				   u8 class)
8c12cde3c   Alex Elder   greybus: define g...
174
  {
1db0a5ff3   Greg Kroah-Hartman   greybus: bundle: ...
175
  	struct gb_bundle *bundle;
8c12cde3c   Alex Elder   greybus: define g...
176

76639ef57   Alex Elder   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   Alex Elder   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   Johan Hovold   greybus: bundle: ...
188
189
  		dev_err(&intf->dev, "duplicate bundle id %u
  ", bundle_id);
8267616b3   Alex Elder   greybus: bundle: ...
190
191
  		return NULL;
  	}
1db0a5ff3   Greg Kroah-Hartman   greybus: bundle: ...
192
193
  	bundle = kzalloc(sizeof(*bundle), GFP_KERNEL);
  	if (!bundle)
8c12cde3c   Alex Elder   greybus: define g...
194
  		return NULL;
4ab9b3c24   Greg Kroah-Hartman   greybus: interfac...
195
  	bundle->intf = intf;
7c183f70e   Viresh Kumar   greybus: bundle: ...
196
  	bundle->id = bundle_id;
88e6d37c4   Viresh Kumar   greybus: bundle: ...
197
  	bundle->class = class;
1db0a5ff3   Greg Kroah-Hartman   greybus: bundle: ...
198
  	INIT_LIST_HEAD(&bundle->connections);
8c12cde3c   Alex Elder   greybus: define g...
199

4ab9b3c24   Greg Kroah-Hartman   greybus: interfac...
200
  	bundle->dev.parent = &intf->dev;
1db0a5ff3   Greg Kroah-Hartman   greybus: bundle: ...
201
202
203
  	bundle->dev.bus = &greybus_bus_type;
  	bundle->dev.type = &greybus_bundle_type;
  	bundle->dev.groups = bundle_groups;
dc0f0285f   Viresh Kumar   greybus: Bundle: ...
204
  	bundle->dev.dma_mask = intf->dev.dma_mask;
1db0a5ff3   Greg Kroah-Hartman   greybus: bundle: ...
205
  	device_initialize(&bundle->dev);
f0172c704   Johan Hovold   greybus: bundle: ...
206
  	dev_set_name(&bundle->dev, "%s.%d", dev_name(&intf->dev), bundle_id);
1db0a5ff3   Greg Kroah-Hartman   greybus: bundle: ...
207

928f2abd5   Viresh Kumar   greybus: Tear dow...
208
  	list_add(&bundle->links, &intf->bundles);
8c12cde3c   Alex Elder   greybus: define g...
209

4f9c5c0bb   Alex Elder   greybus: tracing:...
210
  	trace_gb_bundle_create(bundle);
1db0a5ff3   Greg Kroah-Hartman   greybus: bundle: ...
211
  	return bundle;
8c12cde3c   Alex Elder   greybus: define g...
212
  }
a7e36d0ea   Johan Hovold   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   Alex Elder   greybus: tracing:...
223
  	trace_gb_bundle_add(bundle);
a7e36d0ea   Johan Hovold   greybus: bundle: ...
224
225
  	return 0;
  }
8c12cde3c   Alex Elder   greybus: define g...
226
  /*
1db0a5ff3   Greg Kroah-Hartman   greybus: bundle: ...
227
   * Tear down a previously set up bundle.
8c12cde3c   Alex Elder   greybus: define g...
228
   */
fe53b45ca   Alex Elder   greybus: bundle: ...
229
  void gb_bundle_destroy(struct gb_bundle *bundle)
8c12cde3c   Alex Elder   greybus: define g...
230
  {
4f9c5c0bb   Alex Elder   greybus: tracing:...
231
  	trace_gb_bundle_destroy(bundle);
a7e36d0ea   Johan Hovold   greybus: bundle: ...
232
233
  	if (device_is_registered(&bundle->dev))
  		device_del(&bundle->dev);
1a4c013a4   Matt Porter   greybus: interfac...
234

a7e36d0ea   Johan Hovold   greybus: bundle: ...
235
236
237
238
  	list_del(&bundle->links);
  
  	put_device(&bundle->dev);
  }