blob: f3ae2e9fc1bbc0d1ada70c9365a330a2098c592c [file] [log] [blame]
Alexandre Bailon355a7052015-03-31 09:51:59 +02001/*
2 * Loopback bridge driver for the Greybus loopback module.
3 *
4 * Copyright 2014 Google Inc.
5 * Copyright 2014 Linaro Ltd.
6 *
7 * Released under the GPLv2 only.
8 */
Johan Hovold8923c5b2016-02-11 13:52:49 +01009
10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11
Alexandre Bailon355a7052015-03-31 09:51:59 +020012#include <linux/kernel.h>
13#include <linux/module.h>
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +010014#include <linux/mutex.h>
Alexandre Bailon355a7052015-03-31 09:51:59 +020015#include <linux/slab.h>
16#include <linux/kthread.h>
17#include <linux/delay.h>
18#include <linux/random.h>
Greg Kroah-Hartman5679f782015-03-31 23:01:45 +020019#include <linux/sizes.h>
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +010020#include <linux/cdev.h>
21#include <linux/fs.h>
22#include <linux/kfifo.h>
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +010023#include <linux/debugfs.h>
Bryan O'Donoghue50151152015-09-14 10:48:46 +010024#include <linux/list_sort.h>
Bryan O'Donoghue2e238d712015-12-07 01:59:05 +000025#include <linux/spinlock.h>
Bryan O'Donoghue12927832015-12-07 01:59:06 +000026#include <linux/workqueue.h>
Bryan O'Donoghue36f241ff2015-12-11 13:46:52 +000027#include <linux/atomic.h>
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +010028
Johan Hovold1ffc12b2015-04-16 09:53:59 +020029#include <asm/div64.h>
30
Alexandre Bailon355a7052015-03-31 09:51:59 +020031#include "greybus.h"
Bryan O'Donoghuefd589262015-10-15 16:10:43 +010032#include "connection.h"
Alexandre Bailon355a7052015-03-31 09:51:59 +020033
Bryan O'Donoghuefd489e12015-08-11 13:50:54 +010034#define NSEC_PER_DAY 86400000000000ULL
35
Alexandre Bailon355a7052015-03-31 09:51:59 +020036struct gb_loopback_stats {
Alex Eldere051c822015-08-03 12:57:14 -050037 u32 min;
38 u32 max;
Bryan O'Donoghue00af6582015-07-21 23:50:08 +010039 u64 sum;
Alex Eldere13fc282015-08-03 12:57:15 -050040 u32 count;
Alexandre Bailon355a7052015-03-31 09:51:59 +020041};
42
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +010043struct gb_loopback_device {
44 struct dentry *root;
45 u32 count;
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +000046 size_t size_max;
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +010047
Bryan O'Donoghue2e238d712015-12-07 01:59:05 +000048 /* We need to take a lock in atomic context */
49 spinlock_t lock;
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +010050 struct list_head list;
Bryan O'Donoghue12927832015-12-07 01:59:06 +000051 struct list_head list_op_async;
52 wait_queue_head_t wq;
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +010053};
54
55static struct gb_loopback_device gb_dev;
56
Bryan O'Donoghue12927832015-12-07 01:59:06 +000057struct gb_loopback_async_operation {
58 struct gb_loopback *gb;
59 struct gb_operation *operation;
60 struct timeval ts;
61 struct timer_list timer;
62 struct list_head entry;
63 struct work_struct work;
64 struct kref kref;
65 bool pending;
66 int (*completion)(struct gb_loopback_async_operation *op_async);
67};
68
Alexandre Bailon355a7052015-03-31 09:51:59 +020069struct gb_loopback {
70 struct gb_connection *connection;
Alexandre Bailon355a7052015-03-31 09:51:59 +020071
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +010072 struct dentry *file;
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +010073 struct kfifo kfifo_lat;
74 struct kfifo kfifo_ts;
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +010075 struct mutex mutex;
Alexandre Bailon355a7052015-03-31 09:51:59 +020076 struct task_struct *task;
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +010077 struct list_head entry;
Axel Haslam079fa322015-12-11 11:49:45 +010078 struct device *dev;
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +000079 wait_queue_head_t wq;
Bryan O'Donoghue36f241ff2015-12-11 13:46:52 +000080 wait_queue_head_t wq_completion;
81 atomic_t outstanding_operations;
Alexandre Bailon355a7052015-03-31 09:51:59 +020082
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +010083 /* Per connection stats */
Alexandre Bailon355a7052015-03-31 09:51:59 +020084 struct gb_loopback_stats latency;
85 struct gb_loopback_stats throughput;
Bryan O'Donoghue583cbf52015-07-21 23:50:10 +010086 struct gb_loopback_stats requests_per_second;
Bryan O'Donoghue1ec58432015-10-15 16:10:45 +010087 struct gb_loopback_stats apbridge_unipro_latency;
88 struct gb_loopback_stats gpbridge_firmware_latency;
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +010089
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +000090 int type;
Bryan O'Donoghue12927832015-12-07 01:59:06 +000091 int async;
Axel Haslam079fa322015-12-11 11:49:45 +010092 int id;
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +000093 u32 size;
94 u32 iteration_max;
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +010095 u32 iteration_count;
Bryan O'Donoghueb36f04f2015-12-07 01:59:07 +000096 int us_wait;
Alexandre Bailon355a7052015-03-31 09:51:59 +020097 u32 error;
Bryan O'Donoghue12927832015-12-07 01:59:06 +000098 u32 requests_completed;
99 u32 requests_timedout;
100 u32 timeout;
101 u32 jiffy_timeout;
102 u32 timeout_min;
103 u32 timeout_max;
Bryan O'Donoghue8e3fba52015-12-11 13:46:53 +0000104 u32 outstanding_operations_max;
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000105 u32 lbid;
106 u64 elapsed_nsecs;
Bryan O'Donoghuee6227ee2015-10-15 16:10:44 +0100107 u32 apbridge_latency_ts;
108 u32 gpbridge_latency_ts;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200109};
110
Axel Haslam079fa322015-12-11 11:49:45 +0100111static struct class loopback_class = {
112 .name = "gb_loopback",
113 .owner = THIS_MODULE,
114};
115static DEFINE_IDA(loopback_ida);
116
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000117/* Min/max values in jiffies */
118#define GB_LOOPBACK_TIMEOUT_MIN 1
119#define GB_LOOPBACK_TIMEOUT_MAX 10000
120
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100121#define GB_LOOPBACK_FIFO_DEFAULT 8192
122
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100123static unsigned kfifo_depth = GB_LOOPBACK_FIFO_DEFAULT;
124module_param(kfifo_depth, uint, 0444);
125
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +0100126/* Maximum size of any one send data buffer we support */
127#define MAX_PACKET_SIZE (PAGE_SIZE * 2)
128
Bryan O'Donoghueb36f04f2015-12-07 01:59:07 +0000129#define GB_LOOPBACK_US_WAIT_MAX 1000000
Alexandre Bailon355a7052015-03-31 09:51:59 +0200130
Alexandre Bailon355a7052015-03-31 09:51:59 +0200131/* interface sysfs attributes */
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000132#define gb_loopback_ro_attr(field) \
133static ssize_t field##_show(struct device *dev, \
Alexandre Bailon355a7052015-03-31 09:51:59 +0200134 struct device_attribute *attr, \
135 char *buf) \
136{ \
Axel Haslam079fa322015-12-11 11:49:45 +0100137 struct gb_loopback *gb = dev_get_drvdata(dev); \
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000138 return sprintf(buf, "%u\n", gb->field); \
Alexandre Bailon355a7052015-03-31 09:51:59 +0200139} \
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000140static DEVICE_ATTR_RO(field)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200141
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000142#define gb_loopback_ro_stats_attr(name, field, type) \
143static ssize_t name##_##field##_show(struct device *dev, \
Alexandre Bailon355a7052015-03-31 09:51:59 +0200144 struct device_attribute *attr, \
145 char *buf) \
146{ \
Axel Haslam079fa322015-12-11 11:49:45 +0100147 struct gb_loopback *gb = dev_get_drvdata(dev); \
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000148 return sprintf(buf, "%"#type"\n", gb->name.field); \
Alexandre Bailon355a7052015-03-31 09:51:59 +0200149} \
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000150static DEVICE_ATTR_RO(name##_##field)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200151
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000152#define gb_loopback_ro_avg_attr(name) \
153static ssize_t name##_avg_show(struct device *dev, \
Alex Elder7a135a92015-08-03 12:57:18 -0500154 struct device_attribute *attr, \
155 char *buf) \
156{ \
Bryan O'Donoghuef06272b2015-08-17 00:55:11 +0100157 struct gb_loopback_stats *stats; \
Bryan O'Donoghuef06272b2015-08-17 00:55:11 +0100158 struct gb_loopback *gb; \
159 u64 avg; \
160 u32 count, rem; \
Axel Haslam079fa322015-12-11 11:49:45 +0100161 gb = dev_get_drvdata(dev); \
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000162 stats = &gb->name; \
Bryan O'Donoghuef06272b2015-08-17 00:55:11 +0100163 count = stats->count ? stats->count : 1; \
164 avg = stats->sum + count / 2; /* round closest */ \
165 rem = do_div(avg, count); \
Alex Elderff71d392015-08-03 12:57:19 -0500166 return sprintf(buf, "%llu.%06u\n", avg, 1000000 * rem / count); \
Alex Elder7a135a92015-08-03 12:57:18 -0500167} \
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000168static DEVICE_ATTR_RO(name##_avg)
Alex Elder7a135a92015-08-03 12:57:18 -0500169
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000170#define gb_loopback_stats_attrs(field) \
171 gb_loopback_ro_stats_attr(field, min, u); \
172 gb_loopback_ro_stats_attr(field, max, u); \
173 gb_loopback_ro_avg_attr(field)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200174
175#define gb_loopback_attr(field, type) \
176static ssize_t field##_show(struct device *dev, \
177 struct device_attribute *attr, \
178 char *buf) \
179{ \
Axel Haslam079fa322015-12-11 11:49:45 +0100180 struct gb_loopback *gb = dev_get_drvdata(dev); \
Alexandre Bailon355a7052015-03-31 09:51:59 +0200181 return sprintf(buf, "%"#type"\n", gb->field); \
182} \
183static ssize_t field##_store(struct device *dev, \
184 struct device_attribute *attr, \
185 const char *buf, \
186 size_t len) \
187{ \
188 int ret; \
Axel Haslam079fa322015-12-11 11:49:45 +0100189 struct gb_loopback *gb = dev_get_drvdata(dev); \
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000190 mutex_lock(&gb->mutex); \
Alexandre Bailon355a7052015-03-31 09:51:59 +0200191 ret = sscanf(buf, "%"#type, &gb->field); \
Alexandre Bailon355a7052015-03-31 09:51:59 +0200192 if (ret != 1) \
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +0100193 len = -EINVAL; \
194 else \
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000195 gb_loopback_check_attr(gb, bundle); \
196 mutex_unlock(&gb->mutex); \
Alexandre Bailon355a7052015-03-31 09:51:59 +0200197 return len; \
198} \
199static DEVICE_ATTR_RW(field)
200
Bryan O'Donoghuef06272b2015-08-17 00:55:11 +0100201#define gb_dev_loopback_ro_attr(field, conn) \
202static ssize_t field##_show(struct device *dev, \
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100203 struct device_attribute *attr, \
204 char *buf) \
205{ \
Axel Haslam079fa322015-12-11 11:49:45 +0100206 struct gb_loopback *gb = dev_get_drvdata(dev); \
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000207 return sprintf(buf, "%u\n", gb->field); \
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100208} \
209static DEVICE_ATTR_RO(field)
Bryan O'Donoghue3dfe8aa2015-07-24 10:02:56 +0100210
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100211#define gb_dev_loopback_rw_attr(field, type) \
212static ssize_t field##_show(struct device *dev, \
213 struct device_attribute *attr, \
214 char *buf) \
215{ \
Axel Haslam079fa322015-12-11 11:49:45 +0100216 struct gb_loopback *gb = dev_get_drvdata(dev); \
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000217 return sprintf(buf, "%"#type"\n", gb->field); \
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100218} \
219static ssize_t field##_store(struct device *dev, \
220 struct device_attribute *attr, \
221 const char *buf, \
222 size_t len) \
223{ \
224 int ret; \
Axel Haslam079fa322015-12-11 11:49:45 +0100225 struct gb_loopback *gb = dev_get_drvdata(dev); \
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000226 mutex_lock(&gb->mutex); \
227 ret = sscanf(buf, "%"#type, &gb->field); \
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100228 if (ret != 1) \
229 len = -EINVAL; \
230 else \
Axel Haslam079fa322015-12-11 11:49:45 +0100231 gb_loopback_check_attr(gb); \
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000232 mutex_unlock(&gb->mutex); \
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100233 return len; \
234} \
235static DEVICE_ATTR_RW(field)
236
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000237static void gb_loopback_reset_stats(struct gb_loopback *gb);
Axel Haslam079fa322015-12-11 11:49:45 +0100238static void gb_loopback_check_attr(struct gb_loopback *gb)
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100239{
Bryan O'Donoghueb36f04f2015-12-07 01:59:07 +0000240 if (gb->us_wait > GB_LOOPBACK_US_WAIT_MAX)
241 gb->us_wait = GB_LOOPBACK_US_WAIT_MAX;
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000242 if (gb->size > gb_dev.size_max)
243 gb->size = gb_dev.size_max;
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000244 gb->requests_timedout = 0;
245 gb->requests_completed = 0;
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000246 gb->iteration_count = 0;
247 gb->error = 0;
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100248
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000249 if (kfifo_depth < gb->iteration_max) {
Axel Haslam079fa322015-12-11 11:49:45 +0100250 dev_warn(gb->dev,
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000251 "cannot log bytes %u kfifo_depth %u\n",
252 gb->iteration_max, kfifo_depth);
Bryan O'Donoghuecb60f492015-07-28 18:34:39 +0100253 }
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000254 kfifo_reset_out(&gb->kfifo_lat);
255 kfifo_reset_out(&gb->kfifo_ts);
Bryan O'Donoghuecb60f492015-07-28 18:34:39 +0100256
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000257 switch (gb->type) {
Bryan O'Donoghue3dfe8aa2015-07-24 10:02:56 +0100258 case GB_LOOPBACK_TYPE_PING:
259 case GB_LOOPBACK_TYPE_TRANSFER:
260 case GB_LOOPBACK_TYPE_SINK:
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000261 gb->jiffy_timeout = usecs_to_jiffies(gb->timeout);
262 if (!gb->jiffy_timeout)
263 gb->jiffy_timeout = GB_LOOPBACK_TIMEOUT_MIN;
264 else if (gb->jiffy_timeout > GB_LOOPBACK_TIMEOUT_MAX)
265 gb->jiffy_timeout = GB_LOOPBACK_TIMEOUT_MAX;
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000266 gb_loopback_reset_stats(gb);
267 wake_up(&gb->wq);
Bryan O'Donoghue3dfe8aa2015-07-24 10:02:56 +0100268 break;
269 default:
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000270 gb->type = 0;
Bryan O'Donoghue3dfe8aa2015-07-24 10:02:56 +0100271 break;
272 }
Alexandre Bailon355a7052015-03-31 09:51:59 +0200273}
274
275/* Time to send and receive one message */
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000276gb_loopback_stats_attrs(latency);
Bryan O'Donoghue583cbf52015-07-21 23:50:10 +0100277/* Number of requests sent per second on this cport */
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000278gb_loopback_stats_attrs(requests_per_second);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200279/* Quantity of data sent and received on this cport */
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000280gb_loopback_stats_attrs(throughput);
Bryan O'Donoghue1ec58432015-10-15 16:10:45 +0100281/* Latency across the UniPro link from APBridge's perspective */
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000282gb_loopback_stats_attrs(apbridge_unipro_latency);
Bryan O'Donoghue1ec58432015-10-15 16:10:45 +0100283/* Firmware induced overhead in the GPBridge */
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000284gb_loopback_stats_attrs(gpbridge_firmware_latency);
285
Bryan O'Donoghuee140c752015-07-21 23:50:09 +0100286/* Number of errors encountered during loop */
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000287gb_loopback_ro_attr(error);
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000288/* Number of requests successfully completed async */
289gb_loopback_ro_attr(requests_completed);
290/* Number of requests timed out async */
291gb_loopback_ro_attr(requests_timedout);
292/* Timeout minimum in useconds */
293gb_loopback_ro_attr(timeout_min);
294/* Timeout minimum in useconds */
295gb_loopback_ro_attr(timeout_max);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200296
297/*
Bryan O'Donoghue799a3f02015-07-21 23:50:07 +0100298 * Type of loopback message to send based on protocol type definitions
Alexandre Bailon355a7052015-03-31 09:51:59 +0200299 * 0 => Don't send message
Bryan O'Donoghue799a3f02015-07-21 23:50:07 +0100300 * 2 => Send ping message continuously (message without payload)
Alex Elder006335a2015-08-03 12:57:10 -0500301 * 3 => Send transfer message continuously (message with payload,
Bryan O'Donoghue799a3f02015-07-21 23:50:07 +0100302 * payload returned in response)
303 * 4 => Send a sink message (message with payload, no payload in response)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200304 */
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100305gb_dev_loopback_rw_attr(type, d);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200306/* Size of transfer message payload: 0-4096 bytes */
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100307gb_dev_loopback_rw_attr(size, u);
Alex Elder48f19ee2015-05-11 21:16:36 -0500308/* Time to wait between two messages: 0-1000 ms */
Bryan O'Donoghueb36f04f2015-12-07 01:59:07 +0000309gb_dev_loopback_rw_attr(us_wait, d);
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100310/* Maximum iterations for a given operation: 1-(2^32-1), 0 implies infinite */
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100311gb_dev_loopback_rw_attr(iteration_max, u);
312/* The current index of the for (i = 0; i < iteration_max; i++) loop */
Bryan O'Donoghuef06272b2015-08-17 00:55:11 +0100313gb_dev_loopback_ro_attr(iteration_count, false);
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000314/* A flag to indicate synchronous or asynchronous operations */
315gb_dev_loopback_rw_attr(async, u);
316/* Timeout of an individual asynchronous request */
317gb_dev_loopback_rw_attr(timeout, u);
Bryan O'Donoghue8e3fba52015-12-11 13:46:53 +0000318/* Maximum number of in-flight operations before back-off */
319gb_dev_loopback_rw_attr(outstanding_operations_max, u);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200320
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000321static struct attribute *loopback_attrs[] = {
322 &dev_attr_latency_min.attr,
323 &dev_attr_latency_max.attr,
324 &dev_attr_latency_avg.attr,
325 &dev_attr_requests_per_second_min.attr,
326 &dev_attr_requests_per_second_max.attr,
327 &dev_attr_requests_per_second_avg.attr,
328 &dev_attr_throughput_min.attr,
329 &dev_attr_throughput_max.attr,
330 &dev_attr_throughput_avg.attr,
331 &dev_attr_apbridge_unipro_latency_min.attr,
332 &dev_attr_apbridge_unipro_latency_max.attr,
333 &dev_attr_apbridge_unipro_latency_avg.attr,
334 &dev_attr_gpbridge_firmware_latency_min.attr,
335 &dev_attr_gpbridge_firmware_latency_max.attr,
336 &dev_attr_gpbridge_firmware_latency_avg.attr,
Alexandre Bailon355a7052015-03-31 09:51:59 +0200337 &dev_attr_type.attr,
338 &dev_attr_size.attr,
Bryan O'Donoghueb36f04f2015-12-07 01:59:07 +0000339 &dev_attr_us_wait.attr,
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100340 &dev_attr_iteration_count.attr,
341 &dev_attr_iteration_max.attr,
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000342 &dev_attr_async.attr,
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000343 &dev_attr_error.attr,
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000344 &dev_attr_requests_completed.attr,
345 &dev_attr_requests_timedout.attr,
346 &dev_attr_timeout.attr,
Bryan O'Donoghue8e3fba52015-12-11 13:46:53 +0000347 &dev_attr_outstanding_operations_max.attr,
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000348 &dev_attr_timeout_min.attr,
349 &dev_attr_timeout_max.attr,
Alexandre Bailon355a7052015-03-31 09:51:59 +0200350 NULL,
351};
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000352ATTRIBUTE_GROUPS(loopback);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200353
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000354static void gb_loopback_calculate_stats(struct gb_loopback *gb);
355
Bryan O'Donoghuebd416102015-08-17 00:55:05 +0100356static u32 gb_loopback_nsec_to_usec_latency(u64 elapsed_nsecs)
357{
358 u32 lat;
359
360 do_div(elapsed_nsecs, NSEC_PER_USEC);
361 lat = elapsed_nsecs;
362 return lat;
363}
364
Bryan O'Donoghue7c985352015-08-17 00:55:06 +0100365static u64 __gb_loopback_calc_latency(u64 t1, u64 t2)
366{
367 if (t2 > t1)
368 return t2 - t1;
369 else
370 return NSEC_PER_DAY - t2 + t1;
371}
372
Bryan O'Donoghue2f842302015-08-17 00:55:02 +0100373static u64 gb_loopback_calc_latency(struct timeval *ts, struct timeval *te)
Bryan O'Donoghue4c192662015-08-11 13:50:53 +0100374{
375 u64 t1, t2;
376
377 t1 = timeval_to_ns(ts);
378 t2 = timeval_to_ns(te);
Bryan O'Donoghue7c985352015-08-17 00:55:06 +0100379
380 return __gb_loopback_calc_latency(t1, t2);
Bryan O'Donoghue4c192662015-08-11 13:50:53 +0100381}
382
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +0100383static void gb_loopback_push_latency_ts(struct gb_loopback *gb,
384 struct timeval *ts, struct timeval *te)
385{
386 kfifo_in(&gb->kfifo_ts, (unsigned char *)ts, sizeof(*ts));
387 kfifo_in(&gb->kfifo_ts, (unsigned char *)te, sizeof(*te));
388}
389
Bryan O'Donoghuefbb8edb2015-09-14 10:48:47 +0100390static int gb_loopback_operation_sync(struct gb_loopback *gb, int type,
391 void *request, int request_size,
392 void *response, int response_size)
Bryan O'Donoghue384a7a32015-07-13 20:20:49 +0100393{
Bryan O'Donoghuefbb8edb2015-09-14 10:48:47 +0100394 struct gb_operation *operation;
Bryan O'Donoghue384a7a32015-07-13 20:20:49 +0100395 struct timeval ts, te;
Bryan O'Donoghuefbb8edb2015-09-14 10:48:47 +0100396 int ret;
Bryan O'Donoghue384a7a32015-07-13 20:20:49 +0100397
398 do_gettimeofday(&ts);
Bryan O'Donoghuefbb8edb2015-09-14 10:48:47 +0100399 operation = gb_operation_create(gb->connection, type, request_size,
400 response_size, GFP_KERNEL);
401 if (!operation) {
402 ret = -ENOMEM;
403 goto error;
404 }
Bryan O'Donoghuec3bba872015-07-13 20:20:50 +0100405
Bryan O'Donoghuefbb8edb2015-09-14 10:48:47 +0100406 if (request_size)
407 memcpy(operation->request->payload, request, request_size);
408
409 ret = gb_operation_request_send_sync(operation);
410 if (ret) {
Greg Kroah-Hartmand9a9ea12015-10-16 16:55:46 -0700411 dev_err(&gb->connection->bundle->dev,
Bryan O'Donoghuefbb8edb2015-09-14 10:48:47 +0100412 "synchronous operation failed: %d\n", ret);
413 } else {
414 if (response_size == operation->response->payload_size) {
415 memcpy(response, operation->response->payload,
416 response_size);
417 } else {
Greg Kroah-Hartmand9a9ea12015-10-16 16:55:46 -0700418 dev_err(&gb->connection->bundle->dev,
Bryan O'Donoghuefbb8edb2015-09-14 10:48:47 +0100419 "response size %zu expected %d\n",
420 operation->response->payload_size,
421 response_size);
422 }
423 }
Johan Hovold6ab1ce42015-09-26 17:59:15 -0700424
425 gb_operation_put(operation);
Bryan O'Donoghuefbb8edb2015-09-14 10:48:47 +0100426
427error:
Bryan O'Donoghue384a7a32015-07-13 20:20:49 +0100428 do_gettimeofday(&te);
Bryan O'Donoghue2f842302015-08-17 00:55:02 +0100429
430 /* Calculate the total time the message took */
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +0100431 gb_loopback_push_latency_ts(gb, &ts, &te);
Bryan O'Donoghue2f842302015-08-17 00:55:02 +0100432 gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te);
433
Bryan O'Donoghuefbb8edb2015-09-14 10:48:47 +0100434 return ret;
435}
Bryan O'Donoghue384a7a32015-07-13 20:20:49 +0100436
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000437static void __gb_loopback_async_operation_destroy(struct kref *kref)
438{
439 struct gb_loopback_async_operation *op_async;
440
441 op_async = container_of(kref, struct gb_loopback_async_operation, kref);
442
443 list_del(&op_async->entry);
444 if (op_async->operation)
445 gb_operation_put(op_async->operation);
Bryan O'Donoghue36f241ff2015-12-11 13:46:52 +0000446 atomic_dec(&op_async->gb->outstanding_operations);
447 wake_up(&op_async->gb->wq_completion);
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000448 kfree(op_async);
449}
450
451static void gb_loopback_async_operation_get(struct gb_loopback_async_operation
452 *op_async)
453{
454 kref_get(&op_async->kref);
455}
456
457static void gb_loopback_async_operation_put(struct gb_loopback_async_operation
458 *op_async)
459{
460 unsigned long flags;
461
462 spin_lock_irqsave(&gb_dev.lock, flags);
463 kref_put(&op_async->kref, __gb_loopback_async_operation_destroy);
464 spin_unlock_irqrestore(&gb_dev.lock, flags);
465}
466
467static struct gb_loopback_async_operation *
468 gb_loopback_operation_find(u16 id)
469{
470 struct gb_loopback_async_operation *op_async;
471 bool found = false;
472 unsigned long flags;
473
474 spin_lock_irqsave(&gb_dev.lock, flags);
475 list_for_each_entry(op_async, &gb_dev.list_op_async, entry) {
476 if (op_async->operation->id == id) {
477 gb_loopback_async_operation_get(op_async);
478 found = true;
479 break;
480 }
481 }
482 spin_unlock_irqrestore(&gb_dev.lock, flags);
483
484 return found ? op_async : NULL;
485}
486
Bryan O'Donoghue36f241ff2015-12-11 13:46:52 +0000487static void gb_loopback_async_wait_all(struct gb_loopback *gb)
488{
489 wait_event(gb->wq_completion,
490 !atomic_read(&gb->outstanding_operations));
491}
492
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000493static void gb_loopback_async_operation_callback(struct gb_operation *operation)
494{
495 struct gb_loopback_async_operation *op_async;
496 struct gb_loopback *gb;
497 struct timeval te;
498 bool err = false;
499
500 do_gettimeofday(&te);
501 op_async = gb_loopback_operation_find(operation->id);
502 if (!op_async)
503 return;
504
505 gb = op_async->gb;
506 mutex_lock(&gb->mutex);
507
508 if (!op_async->pending || gb_operation_result(operation)) {
509 err = true;
510 } else {
511 if (op_async->completion)
512 if (op_async->completion(op_async))
513 err = true;
514 }
515
516 if (err) {
517 gb->error++;
518 } else {
519 gb->requests_completed++;
520 gb_loopback_push_latency_ts(gb, &op_async->ts, &te);
521 gb->elapsed_nsecs = gb_loopback_calc_latency(&op_async->ts,
522 &te);
Greg Kroah-Hartman2422d362016-02-17 16:30:38 -0800523 gb_loopback_calculate_stats(gb);
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000524 }
525
526 if (op_async->pending) {
527 gb->iteration_count++;
528 op_async->pending = false;
529 del_timer_sync(&op_async->timer);
530 gb_loopback_async_operation_put(op_async);
531 }
532 mutex_unlock(&gb->mutex);
533
534 dev_dbg(&gb->connection->bundle->dev, "complete operation %d\n",
535 operation->id);
536
537 gb_loopback_async_operation_put(op_async);
538}
539
540static void gb_loopback_async_operation_work(struct work_struct *work)
541{
542 struct gb_loopback *gb;
543 struct gb_operation *operation;
544 struct gb_loopback_async_operation *op_async;
545
546 op_async = container_of(work, struct gb_loopback_async_operation, work);
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000547 gb = op_async->gb;
548 operation = op_async->operation;
549
550 mutex_lock(&gb->mutex);
551 if (op_async->pending) {
552 gb->requests_timedout++;
553 gb->error++;
554 gb->iteration_count++;
555 op_async->pending = false;
556 gb_loopback_async_operation_put(op_async);
557 }
558 mutex_unlock(&gb->mutex);
559
560 dev_dbg(&gb->connection->bundle->dev, "timeout operation %d\n",
561 operation->id);
562
563 gb_operation_cancel(operation, -ETIMEDOUT);
564 gb_loopback_async_operation_put(op_async);
565}
566
567static void gb_loopback_async_operation_timeout(unsigned long data)
568{
569 struct gb_loopback_async_operation *op_async;
570 u16 id = data;
571
572 op_async = gb_loopback_operation_find(id);
573 if (!op_async) {
574 pr_err("operation %d not found - time out ?\n", id);
575 return;
576 }
577 schedule_work(&op_async->work);
578}
579
580static int gb_loopback_async_operation(struct gb_loopback *gb, int type,
581 void *request, int request_size,
582 int response_size,
583 void *completion)
584{
585 struct gb_loopback_async_operation *op_async;
586 struct gb_operation *operation;
587 int ret;
588 unsigned long flags;
589
590 op_async = kzalloc(sizeof(*op_async), GFP_KERNEL);
591 if (!op_async)
592 return -ENOMEM;
593
594 INIT_WORK(&op_async->work, gb_loopback_async_operation_work);
595 init_timer(&op_async->timer);
596 kref_init(&op_async->kref);
597
598 operation = gb_operation_create(gb->connection, type, request_size,
599 response_size, GFP_KERNEL);
600 if (!operation) {
Bryan O'Donoghuec7aae4e2015-12-11 13:46:51 +0000601 kfree(op_async);
602 return -ENOMEM;
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000603 }
604
605 if (request_size)
606 memcpy(operation->request->payload, request, request_size);
607
608 op_async->gb = gb;
609 op_async->operation = operation;
610 op_async->completion = completion;
611
612 spin_lock_irqsave(&gb_dev.lock, flags);
613 list_add_tail(&op_async->entry, &gb_dev.list_op_async);
614 spin_unlock_irqrestore(&gb_dev.lock, flags);
615
616 do_gettimeofday(&op_async->ts);
617 op_async->pending = true;
Bryan O'Donoghue36f241ff2015-12-11 13:46:52 +0000618 atomic_inc(&gb->outstanding_operations);
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000619 ret = gb_operation_request_send(operation,
620 gb_loopback_async_operation_callback,
621 GFP_KERNEL);
622 if (ret)
623 goto error;
624
625 op_async->timer.function = gb_loopback_async_operation_timeout;
626 op_async->timer.expires = jiffies + gb->jiffy_timeout;
627 op_async->timer.data = (unsigned long)operation->id;
628 add_timer(&op_async->timer);
629
630 return ret;
631error:
632 gb_loopback_async_operation_put(op_async);
633 return ret;
634}
635
636static int gb_loopback_sync_sink(struct gb_loopback *gb, u32 len)
Bryan O'Donoghuefbb8edb2015-09-14 10:48:47 +0100637{
638 struct gb_loopback_transfer_request *request;
639 int retval;
640
641 request = kmalloc(len + sizeof(*request), GFP_KERNEL);
642 if (!request)
643 return -ENOMEM;
644
645 request->len = cpu_to_le32(len);
646 retval = gb_loopback_operation_sync(gb, GB_LOOPBACK_TYPE_SINK,
647 request, len + sizeof(*request),
648 NULL, 0);
Bryan O'Donoghue384a7a32015-07-13 20:20:49 +0100649 kfree(request);
650 return retval;
651}
652
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000653static int gb_loopback_sync_transfer(struct gb_loopback *gb, u32 len)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200654{
Alexandre Bailon355a7052015-03-31 09:51:59 +0200655 struct gb_loopback_transfer_request *request;
656 struct gb_loopback_transfer_response *response;
657 int retval;
658
Bryan O'Donoghued6a1a3b2015-12-03 17:29:39 +0000659 gb->apbridge_latency_ts = 0;
660 gb->gpbridge_latency_ts = 0;
661
Alexandre Bailon355a7052015-03-31 09:51:59 +0200662 request = kmalloc(len + sizeof(*request), GFP_KERNEL);
663 if (!request)
664 return -ENOMEM;
665 response = kmalloc(len + sizeof(*response), GFP_KERNEL);
666 if (!response) {
667 kfree(request);
668 return -ENOMEM;
669 }
670
Viresh Kumardc4a1062015-08-14 17:07:10 +0530671 memset(request->data, 0x5A, len);
672
Alexandre Bailon355a7052015-03-31 09:51:59 +0200673 request->len = cpu_to_le32(len);
Bryan O'Donoghuefbb8edb2015-09-14 10:48:47 +0100674 retval = gb_loopback_operation_sync(gb, GB_LOOPBACK_TYPE_TRANSFER,
675 request, len + sizeof(*request),
676 response, len + sizeof(*response));
Alexandre Bailon355a7052015-03-31 09:51:59 +0200677 if (retval)
678 goto gb_error;
679
Viresh Kumardc366f82015-08-14 17:07:11 +0530680 if (memcmp(request->data, response->data, len)) {
Greg Kroah-Hartmand9a9ea12015-10-16 16:55:46 -0700681 dev_err(&gb->connection->bundle->dev,
682 "Loopback Data doesn't match\n");
Alexandre Bailon355a7052015-03-31 09:51:59 +0200683 retval = -EREMOTEIO;
Viresh Kumardc366f82015-08-14 17:07:11 +0530684 }
Bryan O'Donoghue1ec58432015-10-15 16:10:45 +0100685 gb->apbridge_latency_ts = (u32)__le32_to_cpu(response->reserved0);
686 gb->gpbridge_latency_ts = (u32)__le32_to_cpu(response->reserved1);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200687
688gb_error:
689 kfree(request);
690 kfree(response);
691
692 return retval;
693}
694
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000695static int gb_loopback_sync_ping(struct gb_loopback *gb)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200696{
Bryan O'Donoghuefbb8edb2015-09-14 10:48:47 +0100697 return gb_loopback_operation_sync(gb, GB_LOOPBACK_TYPE_PING,
698 NULL, 0, NULL, 0);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200699}
700
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000701static int gb_loopback_async_sink(struct gb_loopback *gb, u32 len)
702{
703 struct gb_loopback_transfer_request *request;
704 int retval;
705
706 request = kmalloc(len + sizeof(*request), GFP_KERNEL);
707 if (!request)
708 return -ENOMEM;
709
710 request->len = cpu_to_le32(len);
711 retval = gb_loopback_async_operation(gb, GB_LOOPBACK_TYPE_SINK,
712 request, len + sizeof(*request),
713 0, NULL);
714 kfree(request);
715 return retval;
716}
717
718static int gb_loopback_async_transfer_complete(
719 struct gb_loopback_async_operation *op_async)
720{
721 struct gb_loopback *gb;
722 struct gb_operation *operation;
723 struct gb_loopback_transfer_request *request;
724 struct gb_loopback_transfer_response *response;
725 size_t len;
726 int retval = 0;
727
728 gb = op_async->gb;
729 operation = op_async->operation;
730 request = operation->request->payload;
731 response = operation->response->payload;
732 len = le32_to_cpu(request->len);
733
734 if (memcmp(request->data, response->data, len)) {
735 dev_err(&gb->connection->bundle->dev,
736 "Loopback Data doesn't match operation id %d\n",
737 operation->id);
738 retval = -EREMOTEIO;
739 } else {
740 gb->apbridge_latency_ts =
741 (u32)__le32_to_cpu(response->reserved0);
742 gb->gpbridge_latency_ts =
743 (u32)__le32_to_cpu(response->reserved1);
744 }
745
746 return retval;
747}
748
749static int gb_loopback_async_transfer(struct gb_loopback *gb, u32 len)
750{
751 struct gb_loopback_transfer_request *request;
752 int retval, response_len;
753
754 request = kmalloc(len + sizeof(*request), GFP_KERNEL);
755 if (!request)
756 return -ENOMEM;
757
758 memset(request->data, 0x5A, len);
759
760 request->len = cpu_to_le32(len);
761 response_len = sizeof(struct gb_loopback_transfer_response);
762 retval = gb_loopback_async_operation(gb, GB_LOOPBACK_TYPE_TRANSFER,
763 request, len + sizeof(*request),
764 len + response_len,
765 gb_loopback_async_transfer_complete);
766 if (retval)
767 goto gb_error;
768
769gb_error:
770 kfree(request);
771 return retval;
772}
773
774static int gb_loopback_async_ping(struct gb_loopback *gb)
775{
776 return gb_loopback_async_operation(gb, GB_LOOPBACK_TYPE_PING,
777 NULL, 0, 0, NULL);
778}
779
Viresh Kumare82a11d2016-02-12 16:08:29 +0530780static int gb_loopback_request_handler(struct gb_operation *operation)
Alex Elderac1c2842015-05-11 21:16:39 -0500781{
782 struct gb_connection *connection = operation->connection;
783 struct gb_loopback_transfer_request *request;
784 struct gb_loopback_transfer_response *response;
Greg Kroah-Hartmand9a9ea12015-10-16 16:55:46 -0700785 struct device *dev = &connection->bundle->dev;
Bryan O'Donoghuee51eafe2015-07-14 00:53:13 +0100786 size_t len;
Alex Elderac1c2842015-05-11 21:16:39 -0500787
788 /* By convention, the AP initiates the version operation */
Viresh Kumare82a11d2016-02-12 16:08:29 +0530789 switch (operation->type) {
Viresh Kumar0e2462d2015-08-14 07:57:38 +0530790 case GB_REQUEST_TYPE_PROTOCOL_VERSION:
Greg Kroah-Hartmand9a9ea12015-10-16 16:55:46 -0700791 dev_err(dev, "module-initiated version operation\n");
Alex Elderac1c2842015-05-11 21:16:39 -0500792 return -EINVAL;
793 case GB_LOOPBACK_TYPE_PING:
Bryan O'Donoghue384a7a32015-07-13 20:20:49 +0100794 case GB_LOOPBACK_TYPE_SINK:
Alex Elderac1c2842015-05-11 21:16:39 -0500795 return 0;
796 case GB_LOOPBACK_TYPE_TRANSFER:
797 if (operation->request->payload_size < sizeof(*request)) {
Greg Kroah-Hartmand9a9ea12015-10-16 16:55:46 -0700798 dev_err(dev, "transfer request too small (%zu < %zu)\n",
Alex Elderac1c2842015-05-11 21:16:39 -0500799 operation->request->payload_size,
800 sizeof(*request));
801 return -EINVAL; /* -EMSGSIZE */
802 }
803 request = operation->request->payload;
804 len = le32_to_cpu(request->len);
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100805 if (len > gb_dev.size_max) {
Greg Kroah-Hartmand9a9ea12015-10-16 16:55:46 -0700806 dev_err(dev, "transfer request too large (%zu > %zu)\n",
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100807 len, gb_dev.size_max);
Bryan O'Donoghuec3bba872015-07-13 20:20:50 +0100808 return -EINVAL;
809 }
810
Bartosz Golaszewski81ad6992015-11-19 13:46:43 +0100811 if (!gb_operation_response_alloc(operation,
812 len + sizeof(*response), GFP_KERNEL)) {
813 dev_err(dev, "error allocating response\n");
814 return -ENOMEM;
Alex Elderac1c2842015-05-11 21:16:39 -0500815 }
Bartosz Golaszewski81ad6992015-11-19 13:46:43 +0100816 response = operation->response->payload;
817 response->len = cpu_to_le32(len);
818 if (len)
819 memcpy(response->data, request->data, len);
820
Alex Elderac1c2842015-05-11 21:16:39 -0500821 return 0;
822 default:
Viresh Kumare82a11d2016-02-12 16:08:29 +0530823 dev_err(dev, "unsupported request: %u\n", operation->type);
Alex Elderac1c2842015-05-11 21:16:39 -0500824 return -EINVAL;
825 }
826}
827
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000828static void gb_loopback_reset_stats(struct gb_loopback *gb)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200829{
830 struct gb_loopback_stats reset = {
Alex Eldere051c822015-08-03 12:57:14 -0500831 .min = U32_MAX,
Alexandre Bailon355a7052015-03-31 09:51:59 +0200832 };
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100833
834 /* Reset per-connection stats */
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000835 memcpy(&gb->latency, &reset,
836 sizeof(struct gb_loopback_stats));
837 memcpy(&gb->throughput, &reset,
838 sizeof(struct gb_loopback_stats));
839 memcpy(&gb->requests_per_second, &reset,
840 sizeof(struct gb_loopback_stats));
841 memcpy(&gb->apbridge_unipro_latency, &reset,
842 sizeof(struct gb_loopback_stats));
843 memcpy(&gb->gpbridge_firmware_latency, &reset,
844 sizeof(struct gb_loopback_stats));
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100845
Bryan O'Donoghuef42a6892015-12-07 01:59:08 +0000846 /* Should be initialized at least once per transaction set */
847 gb->apbridge_latency_ts = 0;
848 gb->gpbridge_latency_ts = 0;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200849}
850
Alex Eldera6e7e532015-08-03 12:57:13 -0500851static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u32 val)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200852{
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100853 if (stats->min > val)
854 stats->min = val;
855 if (stats->max < val)
856 stats->max = val;
857 stats->sum += val;
858 stats->count++;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200859}
860
Bryan O'Donoghue583cbf52015-07-21 23:50:10 +0100861static void gb_loopback_requests_update(struct gb_loopback *gb, u32 latency)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200862{
Greg Kroah-Hartman2422d362016-02-17 16:30:38 -0800863 u32 req = USEC_PER_SEC;
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100864
Bryan O'Donoghue583cbf52015-07-21 23:50:10 +0100865 do_div(req, latency);
Greg Kroah-Hartman2422d362016-02-17 16:30:38 -0800866 gb_loopback_update_stats(&gb->requests_per_second, req);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200867}
868
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100869static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200870{
Greg Kroah-Hartman2422d362016-02-17 16:30:38 -0800871 u32 throughput;
Bryan O'Donoghuef7908e42015-07-13 20:20:51 +0100872 u32 aggregate_size = sizeof(struct gb_operation_msg_hdr) * 2;
873
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000874 switch (gb->type) {
Bryan O'Donoghuef7908e42015-07-13 20:20:51 +0100875 case GB_LOOPBACK_TYPE_PING:
876 break;
877 case GB_LOOPBACK_TYPE_SINK:
878 aggregate_size += sizeof(struct gb_loopback_transfer_request) +
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000879 gb->size;
Bryan O'Donoghuef7908e42015-07-13 20:20:51 +0100880 break;
881 case GB_LOOPBACK_TYPE_TRANSFER:
882 aggregate_size += sizeof(struct gb_loopback_transfer_request) +
883 sizeof(struct gb_loopback_transfer_response) +
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000884 gb->size * 2;
Bryan O'Donoghuef7908e42015-07-13 20:20:51 +0100885 break;
886 default:
887 return;
888 }
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100889
Greg Kroah-Hartman2422d362016-02-17 16:30:38 -0800890 /* Calculate bytes per second */
891 throughput = USEC_PER_SEC;
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100892 do_div(throughput, latency);
Greg Kroah-Hartman2422d362016-02-17 16:30:38 -0800893 throughput *= aggregate_size;
894 gb_loopback_update_stats(&gb->throughput, throughput);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200895}
896
Greg Kroah-Hartman2422d362016-02-17 16:30:38 -0800897static void gb_loopback_calculate_stats(struct gb_loopback *gb)
Alexandre Bailon355a7052015-03-31 09:51:59 +0200898{
899 u32 lat;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200900
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100901 /* Express latency in terms of microseconds */
Bryan O'Donoghuebd416102015-08-17 00:55:05 +0100902 lat = gb_loopback_nsec_to_usec_latency(gb->elapsed_nsecs);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200903
Bryan O'Donoghue98676ca2015-08-17 00:55:12 +0100904 /* Log latency stastic */
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100905 gb_loopback_update_stats(&gb->latency, lat);
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100906
907 /* Raw latency log on a per thread basis */
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +0100908 kfifo_in(&gb->kfifo_lat, (unsigned char *)&lat, sizeof(lat));
Bryan O'Donoghue00af6582015-07-21 23:50:08 +0100909
Greg Kroah-Hartman2422d362016-02-17 16:30:38 -0800910 /* Log throughput and requests using latency as benchmark */
911 gb_loopback_throughput_update(gb, lat);
912 gb_loopback_requests_update(gb, lat);
913
Bryan O'Donoghue1ec58432015-10-15 16:10:45 +0100914 /* Log the firmware supplied latency values */
Bryan O'Donoghue1ec58432015-10-15 16:10:45 +0100915 gb_loopback_update_stats(&gb->apbridge_unipro_latency,
916 gb->apbridge_latency_ts);
Bryan O'Donoghue1ec58432015-10-15 16:10:45 +0100917 gb_loopback_update_stats(&gb->gpbridge_firmware_latency,
918 gb->gpbridge_latency_ts);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200919}
920
Bryan O'Donoghue8e3fba52015-12-11 13:46:53 +0000921static void gb_loopback_async_wait_to_send(struct gb_loopback *gb)
922{
923 if (!(gb->async && gb->outstanding_operations_max))
924 return;
925 wait_event_interruptible(gb->wq_completion,
926 (atomic_read(&gb->outstanding_operations) <
927 gb->outstanding_operations_max) ||
928 kthread_should_stop());
929}
930
Alexandre Bailon355a7052015-03-31 09:51:59 +0200931static int gb_loopback_fn(void *data)
932{
933 int error = 0;
Bryan O'Donoghueb36f04f2015-12-07 01:59:07 +0000934 int us_wait = 0;
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100935 int type;
936 u32 size;
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000937 u32 send_count = 0;
Alex Elder3f12c3e2015-08-03 12:57:11 -0500938 struct gb_loopback *gb = data;
Alexandre Bailon355a7052015-03-31 09:51:59 +0200939
Bryan O'Donoghue3dfe8aa2015-07-24 10:02:56 +0100940 while (1) {
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000941 if (!gb->type)
942 wait_event_interruptible(gb->wq, gb->type ||
Bryan O'Donoghue3dfe8aa2015-07-24 10:02:56 +0100943 kthread_should_stop());
Bryan O'Donoghue8e3fba52015-12-11 13:46:53 +0000944 if (kthread_should_stop())
945 break;
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000946
Bryan O'Donoghue8e3fba52015-12-11 13:46:53 +0000947 /* Limit the maximum number of in-flight async operations */
948 gb_loopback_async_wait_to_send(gb);
Bryan O'Donoghue3dfe8aa2015-07-24 10:02:56 +0100949 if (kthread_should_stop())
950 break;
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +0100951
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100952 mutex_lock(&gb->mutex);
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000953 sysfs_notify(&gb->connection->bundle->dev.kobj,
954 NULL, "iteration_count");
955
956 /* Optionally terminate */
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000957 if (send_count == gb->iteration_max) {
Greg Kroah-Hartman2422d362016-02-17 16:30:38 -0800958 gb->type = 0;
959 send_count = 0;
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100960 mutex_unlock(&gb->mutex);
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000961 continue;
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100962 }
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000963 size = gb->size;
Bryan O'Donoghueb36f04f2015-12-07 01:59:07 +0000964 us_wait = gb->us_wait;
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000965 type = gb->type;
Bryan O'Donoghued9fb3752015-12-03 17:29:38 +0000966 mutex_unlock(&gb->mutex);
967
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100968 /* Else operations to perform */
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000969 if (gb->async) {
970 if (type == GB_LOOPBACK_TYPE_PING) {
971 error = gb_loopback_async_ping(gb);
972 gb_loopback_calculate_stats(gb);
973 } else if (type == GB_LOOPBACK_TYPE_TRANSFER) {
974 error = gb_loopback_async_transfer(gb, size);
975 } else if (type == GB_LOOPBACK_TYPE_SINK) {
976 error = gb_loopback_async_sink(gb, size);
977 }
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +0100978
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000979 if (error)
980 gb->error++;
981 } else {
982 /* We are effectively single threaded here */
983 if (type == GB_LOOPBACK_TYPE_PING)
984 error = gb_loopback_sync_ping(gb);
985 else if (type == GB_LOOPBACK_TYPE_TRANSFER)
986 error = gb_loopback_sync_transfer(gb, size);
987 else if (type == GB_LOOPBACK_TYPE_SINK)
988 error = gb_loopback_sync_sink(gb, size);
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +0000989
Bryan O'Donoghue12927832015-12-07 01:59:06 +0000990 if (error)
991 gb->error++;
992 gb->iteration_count++;
993 gb_loopback_calculate_stats(gb);
994 }
995 send_count++;
Bryan O'Donoghueb36f04f2015-12-07 01:59:07 +0000996 if (us_wait)
997 udelay(us_wait);
Alexandre Bailon355a7052015-03-31 09:51:59 +0200998 }
999 return 0;
1000}
1001
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +01001002static int gb_loopback_dbgfs_latency_show_common(struct seq_file *s,
1003 struct kfifo *kfifo,
1004 struct mutex *mutex)
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +01001005{
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +01001006 u32 latency;
1007 int retval;
1008
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +01001009 if (kfifo_len(kfifo) == 0) {
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +01001010 retval = -EAGAIN;
1011 goto done;
1012 }
1013
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +01001014 mutex_lock(mutex);
1015 retval = kfifo_out(kfifo, &latency, sizeof(latency));
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +01001016 if (retval > 0) {
1017 seq_printf(s, "%u", latency);
1018 retval = 0;
1019 }
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +01001020 mutex_unlock(mutex);
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +01001021done:
1022 return retval;
1023}
1024
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +01001025static int gb_loopback_dbgfs_latency_show(struct seq_file *s, void *unused)
1026{
1027 struct gb_loopback *gb = s->private;
1028
1029 return gb_loopback_dbgfs_latency_show_common(s, &gb->kfifo_lat,
1030 &gb->mutex);
1031}
1032
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +01001033static int gb_loopback_latency_open(struct inode *inode, struct file *file)
1034{
1035 return single_open(file, gb_loopback_dbgfs_latency_show,
1036 inode->i_private);
1037}
1038
1039static const struct file_operations gb_loopback_debugfs_latency_ops = {
1040 .open = gb_loopback_latency_open,
1041 .read = seq_read,
1042 .llseek = seq_lseek,
1043 .release = single_release,
1044};
1045
Bryan O'Donoghue50151152015-09-14 10:48:46 +01001046static int gb_loopback_bus_id_compare(void *priv, struct list_head *lha,
1047 struct list_head *lhb)
1048{
1049 struct gb_loopback *a = list_entry(lha, struct gb_loopback, entry);
1050 struct gb_loopback *b = list_entry(lhb, struct gb_loopback, entry);
1051 struct gb_connection *ca = a->connection;
1052 struct gb_connection *cb = b->connection;
1053
Bryan O'Donoghue50151152015-09-14 10:48:46 +01001054 if (ca->bundle->intf->interface_id < cb->bundle->intf->interface_id)
1055 return -1;
1056 if (cb->bundle->intf->interface_id < ca->bundle->intf->interface_id)
1057 return 1;
1058 if (ca->bundle->id < cb->bundle->id)
1059 return -1;
1060 if (cb->bundle->id < ca->bundle->id)
1061 return 1;
1062 if (ca->intf_cport_id < cb->intf_cport_id)
1063 return -1;
1064 else if (cb->intf_cport_id < ca->intf_cport_id)
1065 return 1;
1066
1067 return 0;
1068}
1069
1070static void gb_loopback_insert_id(struct gb_loopback *gb)
1071{
1072 struct gb_loopback *gb_list;
1073 u32 new_lbid = 0;
1074
1075 /* perform an insertion sort */
1076 list_add_tail(&gb->entry, &gb_dev.list);
1077 list_sort(NULL, &gb_dev.list, gb_loopback_bus_id_compare);
1078 list_for_each_entry(gb_list, &gb_dev.list, entry) {
1079 gb_list->lbid = 1 << new_lbid;
1080 new_lbid++;
1081 }
1082}
1083
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +01001084#define DEBUGFS_NAMELEN 32
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +01001085
Viresh Kumare82a11d2016-02-12 16:08:29 +05301086static int gb_loopback_probe(struct gb_bundle *bundle,
1087 const struct greybus_bundle_id *id)
Alexandre Bailon355a7052015-03-31 09:51:59 +02001088{
Viresh Kumare82a11d2016-02-12 16:08:29 +05301089 struct greybus_descriptor_cport *cport_desc;
1090 struct gb_connection *connection;
Alexandre Bailon355a7052015-03-31 09:51:59 +02001091 struct gb_loopback *gb;
Axel Haslam079fa322015-12-11 11:49:45 +01001092 struct device *dev;
Alexandre Bailon355a7052015-03-31 09:51:59 +02001093 int retval;
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +01001094 char name[DEBUGFS_NAMELEN];
Bryan O'Donoghue2e238d712015-12-07 01:59:05 +00001095 unsigned long flags;
Alexandre Bailon355a7052015-03-31 09:51:59 +02001096
Viresh Kumare82a11d2016-02-12 16:08:29 +05301097 if (bundle->num_cports != 1)
1098 return -ENODEV;
1099
1100 cport_desc = &bundle->cport_desc[0];
1101 if (cport_desc->protocol_id != GREYBUS_PROTOCOL_LOOPBACK)
1102 return -ENODEV;
1103
Alexandre Bailon355a7052015-03-31 09:51:59 +02001104 gb = kzalloc(sizeof(*gb), GFP_KERNEL);
1105 if (!gb)
1106 return -ENOMEM;
1107
Viresh Kumare82a11d2016-02-12 16:08:29 +05301108 connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id),
1109 gb_loopback_request_handler);
1110 if (IS_ERR(connection)) {
1111 retval = PTR_ERR(connection);
1112 goto out_kzalloc;
1113 }
1114
1115 gb->connection = connection;
1116 greybus_set_drvdata(bundle, gb);
1117
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +00001118 init_waitqueue_head(&gb->wq);
Bryan O'Donoghue36f241ff2015-12-11 13:46:52 +00001119 init_waitqueue_head(&gb->wq_completion);
1120 atomic_set(&gb->outstanding_operations, 0);
Bryan O'Donoghue8e1d6c32015-12-03 17:29:41 +00001121 gb_loopback_reset_stats(gb);
1122
Bryan O'Donoghue12927832015-12-07 01:59:06 +00001123 /* Reported values to user-space for min/max timeouts */
1124 gb->timeout_min = jiffies_to_usecs(GB_LOOPBACK_TIMEOUT_MIN);
1125 gb->timeout_max = jiffies_to_usecs(GB_LOOPBACK_TIMEOUT_MAX);
1126
Bryan O'Donoghuef06272b2015-08-17 00:55:11 +01001127 if (!gb_dev.count) {
Bryan O'Donoghuef06272b2015-08-17 00:55:11 +01001128 /* Calculate maximum payload */
1129 gb_dev.size_max = gb_operation_get_payload_size_max(connection);
1130 if (gb_dev.size_max <=
1131 sizeof(struct gb_loopback_transfer_request)) {
1132 retval = -EINVAL;
Viresh Kumare82a11d2016-02-12 16:08:29 +05301133 goto out_connection_destroy;
Bryan O'Donoghuef06272b2015-08-17 00:55:11 +01001134 }
1135 gb_dev.size_max -= sizeof(struct gb_loopback_transfer_request);
1136 }
Bryan O'Donoghuef06272b2015-08-17 00:55:11 +01001137
1138 /* Create per-connection sysfs and debugfs data-points */
Bryan O'Donoghue8d8d36d2015-09-23 09:31:33 -07001139 snprintf(name, sizeof(name), "raw_latency_%s",
Greg Kroah-Hartmand9a9ea12015-10-16 16:55:46 -07001140 dev_name(&connection->bundle->dev));
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +01001141 gb->file = debugfs_create_file(name, S_IFREG | S_IRUGO, gb_dev.root, gb,
1142 &gb_loopback_debugfs_latency_ops);
Axel Haslam079fa322015-12-11 11:49:45 +01001143
1144 gb->id = ida_simple_get(&loopback_ida, 0, 0, GFP_KERNEL);
1145 if (gb->id < 0) {
1146 retval = gb->id;
Viresh Kumare82a11d2016-02-12 16:08:29 +05301147 goto out_debugfs_remove;
Axel Haslam079fa322015-12-11 11:49:45 +01001148 }
1149
Viresh Kumare82a11d2016-02-12 16:08:29 +05301150 retval = gb_connection_enable(connection);
1151 if (retval)
1152 goto out_ida_remove;
1153
Axel Haslam079fa322015-12-11 11:49:45 +01001154 dev = device_create_with_groups(&loopback_class,
1155 &connection->bundle->dev,
1156 MKDEV(0, 0), gb, loopback_groups,
1157 "gb_loopback%d", gb->id);
1158 if (IS_ERR(dev)) {
1159 retval = PTR_ERR(dev);
Viresh Kumare82a11d2016-02-12 16:08:29 +05301160 goto out_connection_disable;
Axel Haslam079fa322015-12-11 11:49:45 +01001161 }
1162 gb->dev = dev;
Bryan O'Donoghuec3bba872015-07-13 20:20:50 +01001163
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +01001164 /* Allocate kfifo */
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +01001165 if (kfifo_alloc(&gb->kfifo_lat, kfifo_depth * sizeof(u32),
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +01001166 GFP_KERNEL)) {
1167 retval = -ENOMEM;
Axel Haslam079fa322015-12-11 11:49:45 +01001168 goto out_conn;
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +01001169 }
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +01001170 if (kfifo_alloc(&gb->kfifo_ts, kfifo_depth * sizeof(struct timeval) * 2,
1171 GFP_KERNEL)) {
1172 retval = -ENOMEM;
1173 goto out_kfifo0;
1174 }
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +01001175
1176 /* Fork worker thread */
Bryan O'Donoghue85d678c2015-07-28 18:34:36 +01001177 mutex_init(&gb->mutex);
Alexandre Bailon355a7052015-03-31 09:51:59 +02001178 gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback");
1179 if (IS_ERR(gb->task)) {
Alex Elder69f60342015-05-11 21:16:35 -05001180 retval = PTR_ERR(gb->task);
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +01001181 goto out_kfifo1;
Alexandre Bailon355a7052015-03-31 09:51:59 +02001182 }
1183
Bryan O'Donoghue2e238d712015-12-07 01:59:05 +00001184 spin_lock_irqsave(&gb_dev.lock, flags);
Bryan O'Donoghue50151152015-09-14 10:48:46 +01001185 gb_loopback_insert_id(gb);
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +01001186 gb_dev.count++;
Bryan O'Donoghue2e238d712015-12-07 01:59:05 +00001187 spin_unlock_irqrestore(&gb_dev.lock, flags);
1188
1189 gb_connection_latency_tag_enable(connection);
Alexandre Bailon355a7052015-03-31 09:51:59 +02001190 return 0;
1191
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +01001192out_kfifo1:
1193 kfifo_free(&gb->kfifo_ts);
1194out_kfifo0:
1195 kfifo_free(&gb->kfifo_lat);
Axel Haslam079fa322015-12-11 11:49:45 +01001196out_conn:
1197 device_unregister(dev);
Viresh Kumare82a11d2016-02-12 16:08:29 +05301198out_connection_disable:
1199 gb_connection_disable(connection);
1200out_ida_remove:
Axel Haslam079fa322015-12-11 11:49:45 +01001201 ida_simple_remove(&loopback_ida, gb->id);
Viresh Kumare82a11d2016-02-12 16:08:29 +05301202out_debugfs_remove:
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +01001203 debugfs_remove(gb->file);
Viresh Kumare82a11d2016-02-12 16:08:29 +05301204out_connection_destroy:
1205 gb_connection_destroy(connection);
Bryan O'Donoghue2e238d712015-12-07 01:59:05 +00001206out_kzalloc:
Alexandre Bailon355a7052015-03-31 09:51:59 +02001207 kfree(gb);
Alex Elder1fb807c2015-08-03 12:57:20 -05001208
Alexandre Bailon355a7052015-03-31 09:51:59 +02001209 return retval;
1210}
1211
Viresh Kumare82a11d2016-02-12 16:08:29 +05301212static void gb_loopback_disconnect(struct gb_bundle *bundle)
Alexandre Bailon355a7052015-03-31 09:51:59 +02001213{
Viresh Kumare82a11d2016-02-12 16:08:29 +05301214 struct gb_loopback *gb = greybus_get_drvdata(bundle);
Bryan O'Donoghue2e238d712015-12-07 01:59:05 +00001215 unsigned long flags;
Alexandre Bailon355a7052015-03-31 09:51:59 +02001216
Viresh Kumare82a11d2016-02-12 16:08:29 +05301217 gb_connection_disable(gb->connection);
1218
Alexandre Bailon355a7052015-03-31 09:51:59 +02001219 if (!IS_ERR_OR_NULL(gb->task))
1220 kthread_stop(gb->task);
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +01001221
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +01001222 kfifo_free(&gb->kfifo_lat);
1223 kfifo_free(&gb->kfifo_ts);
Viresh Kumare82a11d2016-02-12 16:08:29 +05301224 gb_connection_latency_tag_disable(gb->connection);
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +01001225 debugfs_remove(gb->file);
Viresh Kumare82a11d2016-02-12 16:08:29 +05301226
1227 /*
1228 * FIXME: gb_loopback_async_wait_all() is redundant now, as connection
1229 * is disabled at the beginning and so we can't have any more
1230 * incoming/outgoing requests.
1231 */
Bryan O'Donoghue36f241ff2015-12-11 13:46:52 +00001232 gb_loopback_async_wait_all(gb);
Bryan O'Donoghue2e238d712015-12-07 01:59:05 +00001233
1234 spin_lock_irqsave(&gb_dev.lock, flags);
1235 gb_dev.count--;
Bryan O'Donoghueff477d02015-09-04 16:53:31 +01001236 list_del(&gb->entry);
Bryan O'Donoghue2e238d712015-12-07 01:59:05 +00001237 spin_unlock_irqrestore(&gb_dev.lock, flags);
1238
Axel Haslam079fa322015-12-11 11:49:45 +01001239 device_unregister(gb->dev);
1240 ida_simple_remove(&loopback_ida, gb->id);
1241
Viresh Kumare82a11d2016-02-12 16:08:29 +05301242 gb_connection_destroy(gb->connection);
Bryan O'Donoghue5f3e0d12015-09-14 10:48:41 +01001243 kfree(gb);
Alexandre Bailon355a7052015-03-31 09:51:59 +02001244}
1245
Viresh Kumare82a11d2016-02-12 16:08:29 +05301246static const struct greybus_bundle_id gb_loopback_id_table[] = {
1247 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOOPBACK) },
1248 { }
1249};
1250MODULE_DEVICE_TABLE(greybus, gb_loopback_id_table);
1251
1252static struct greybus_driver gb_loopback_driver = {
1253 .name = "loopback",
1254 .probe = gb_loopback_probe,
1255 .disconnect = gb_loopback_disconnect,
1256 .id_table = gb_loopback_id_table,
Alexandre Bailon355a7052015-03-31 09:51:59 +02001257};
1258
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +01001259static int loopback_init(void)
1260{
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +01001261 int retval;
1262
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +01001263 INIT_LIST_HEAD(&gb_dev.list);
Bryan O'Donoghue12927832015-12-07 01:59:06 +00001264 INIT_LIST_HEAD(&gb_dev.list_op_async);
Bryan O'Donoghue2e238d712015-12-07 01:59:05 +00001265 spin_lock_init(&gb_dev.lock);
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +01001266 gb_dev.root = debugfs_create_dir("gb_loopback", NULL);
Bryan O'Donoghue67d1eec2015-08-17 00:55:04 +01001267
Axel Haslam079fa322015-12-11 11:49:45 +01001268 retval = class_register(&loopback_class);
1269 if (retval)
1270 goto err;
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +01001271
Viresh Kumare82a11d2016-02-12 16:08:29 +05301272 retval = greybus_register(&gb_loopback_driver);
Axel Haslam079fa322015-12-11 11:49:45 +01001273 if (retval)
1274 goto err_unregister;
1275
1276 return 0;
1277
1278err_unregister:
1279 class_unregister(&loopback_class);
1280err:
Bryan O'Donoghue4b0ea00c2015-08-17 00:55:07 +01001281 debugfs_remove_recursive(gb_dev.root);
1282 return retval;
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +01001283}
1284module_init(loopback_init);
1285
1286static void __exit loopback_exit(void)
1287{
Bryan O'Donoghueaa27bf82015-08-17 00:55:03 +01001288 debugfs_remove_recursive(gb_dev.root);
Viresh Kumare82a11d2016-02-12 16:08:29 +05301289 greybus_deregister(&gb_loopback_driver);
Axel Haslam079fa322015-12-11 11:49:45 +01001290 class_unregister(&loopback_class);
1291 ida_destroy(&loopback_ida);
Bryan O'Donoghuecbd204b2015-07-28 18:34:38 +01001292}
1293module_exit(loopback_exit);
Alexandre Bailon355a7052015-03-31 09:51:59 +02001294
1295MODULE_LICENSE("GPL v2");