blob: 70a11f2650fa9244f0cf71cf589cd985d495e3c9 [file] [log] [blame]
Edward A. James7bd86372017-05-15 12:28:44 -05001From patchwork Fri May 12 19:38:18 2017
2Content-Type: text/plain; charset="utf-8"
3MIME-Version: 1.0
4Content-Transfer-Encoding: 7bit
5Subject: [linux,dev-4.10,1/3] drivers: fsi: sbefifo: Add in-kernel API
6From: eajames@linux.vnet.ibm.com
7X-Patchwork-Id: 761836
8Message-Id: <1494617900-32369-2-git-send-email-eajames@linux.vnet.ibm.com>
9To: openbmc@lists.ozlabs.org
10Cc: "Edward A. James" <eajames@us.ibm.com>, bradleyb@fuzziesquirrel.com,
11 cbostic@linux.vnet.ibm.com
12Date: Fri, 12 May 2017 14:38:18 -0500
13
14From: "Edward A. James" <eajames@us.ibm.com>
15
16Add exported functions to the SBEFIFO driver to open/write/read/close
17from within the kernel.
18
19Signed-off-by: Edward A. James <eajames@us.ibm.com>
20---
21 drivers/fsi/fsi-sbefifo.c | 161 +++++++++++++++++++++++++++++++++++---------
22 include/linux/fsi-sbefifo.h | 30 +++++++++
23 2 files changed, 161 insertions(+), 30 deletions(-)
24 create mode 100644 include/linux/fsi-sbefifo.h
25
26diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c
27index b49aec2..56e6331 100644
28--- a/drivers/fsi/fsi-sbefifo.c
29+++ b/drivers/fsi/fsi-sbefifo.c
30@@ -15,9 +15,12 @@
31 #include <linux/errno.h>
32 #include <linux/idr.h>
33 #include <linux/fsi.h>
34+#include <linux/fsi-sbefifo.h>
35 #include <linux/list.h>
36 #include <linux/miscdevice.h>
37 #include <linux/module.h>
38+#include <linux/of.h>
39+#include <linux/of_platform.h>
40 #include <linux/poll.h>
41 #include <linux/sched.h>
42 #include <linux/slab.h>
43@@ -82,6 +85,7 @@ struct sbefifo_client {
44 struct list_head xfrs;
45 struct sbefifo *dev;
46 struct kref kref;
47+ unsigned long f_flags;
48 };
49
50 static struct list_head sbefifo_fifos;
51@@ -506,6 +510,7 @@ static int sbefifo_open(struct inode *inode, struct file *file)
52 return -ENOMEM;
53
54 file->private_data = client;
55+ client->f_flags = file->f_flags;
56
57 return 0;
58 }
59@@ -530,24 +535,18 @@ static unsigned int sbefifo_poll(struct file *file, poll_table *wait)
60 return mask;
61 }
62
63-static ssize_t sbefifo_read(struct file *file, char __user *buf,
64- size_t len, loff_t *offset)
65+static ssize_t sbefifo_read_common(struct sbefifo_client *client,
66+ char __user *ubuf, char *kbuf, size_t len)
67 {
68- struct sbefifo_client *client = file->private_data;
69 struct sbefifo *sbefifo = client->dev;
70 struct sbefifo_xfr *xfr;
71- ssize_t ret = 0;
72 size_t n;
73-
74- WARN_ON(*offset);
75-
76- if (!access_ok(VERIFY_WRITE, buf, len))
77- return -EFAULT;
78+ ssize_t ret = 0;
79
80 if ((len >> 2) << 2 != len)
81 return -EINVAL;
82
83- if ((file->f_flags & O_NONBLOCK) && !sbefifo_xfr_rsp_pending(client))
84+ if ((client->f_flags & O_NONBLOCK) && !sbefifo_xfr_rsp_pending(client))
85 return -EAGAIN;
86
87 sbefifo_get_client(client);
88@@ -566,10 +565,13 @@ static ssize_t sbefifo_read(struct file *file, char __user *buf,
89
90 n = min_t(size_t, n, len);
91
92- if (copy_to_user(buf, READ_ONCE(client->rbuf.rpos), n)) {
93- sbefifo_put_client(client);
94- return -EFAULT;
95- }
96+ if (ubuf) {
97+ if (copy_to_user(ubuf, READ_ONCE(client->rbuf.rpos), n)) {
98+ sbefifo_put_client(client);
99+ return -EFAULT;
100+ }
101+ } else
102+ memcpy(kbuf, READ_ONCE(client->rbuf.rpos), n);
103
104 if (sbefifo_buf_readnb(&client->rbuf, n)) {
105 xfr = sbefifo_client_next_xfr(client);
106@@ -592,20 +594,28 @@ static ssize_t sbefifo_read(struct file *file, char __user *buf,
107 return n;
108 }
109
110-static ssize_t sbefifo_write(struct file *file, const char __user *buf,
111+static ssize_t sbefifo_read(struct file *file, char __user *buf,
112 size_t len, loff_t *offset)
113 {
114 struct sbefifo_client *client = file->private_data;
115- struct sbefifo *sbefifo = client->dev;
116- struct sbefifo_xfr *xfr;
117- ssize_t ret = 0;
118- size_t n;
119
120 WARN_ON(*offset);
121
122- if (!access_ok(VERIFY_READ, buf, len))
123+ if (!access_ok(VERIFY_WRITE, buf, len))
124 return -EFAULT;
125
126+ return sbefifo_read_common(client, buf, NULL, len);
127+}
128+
129+static ssize_t sbefifo_write_common(struct sbefifo_client *client,
130+ const char __user *ubuf, const char *kbuf,
131+ size_t len)
132+{
133+ struct sbefifo *sbefifo = client->dev;
134+ struct sbefifo_xfr *xfr;
135+ ssize_t ret = 0;
136+ size_t n;
137+
138 if ((len >> 2) << 2 != len)
139 return -EINVAL;
140
141@@ -617,7 +627,7 @@ static ssize_t sbefifo_write(struct file *file, const char __user *buf,
142 spin_lock_irq(&sbefifo->lock);
143 xfr = sbefifo_next_xfr(sbefifo);
144
145- if ((file->f_flags & O_NONBLOCK) && xfr && n < len) {
146+ if ((client->f_flags & O_NONBLOCK) && xfr && n < len) {
147 spin_unlock_irq(&sbefifo->lock);
148 return -EAGAIN;
149 }
150@@ -657,18 +667,25 @@ static ssize_t sbefifo_write(struct file *file, const char __user *buf,
151
152 n = min_t(size_t, n, len);
153
154- if (copy_from_user(READ_ONCE(client->wbuf.wpos), buf, n)) {
155- set_bit(SBEFIFO_XFR_CANCEL, &xfr->flags);
156- sbefifo_get(sbefifo);
157- if (mod_timer(&sbefifo->poll_timer, jiffies))
158- sbefifo_put(sbefifo);
159- sbefifo_put_client(client);
160- return -EFAULT;
161+ if (ubuf) {
162+ if (copy_from_user(READ_ONCE(client->wbuf.wpos), ubuf,
163+ n)) {
164+ set_bit(SBEFIFO_XFR_CANCEL, &xfr->flags);
165+ sbefifo_get(sbefifo);
166+ if (mod_timer(&sbefifo->poll_timer, jiffies))
167+ sbefifo_put(sbefifo);
168+ sbefifo_put_client(client);
169+ return -EFAULT;
170+ }
171+
172+ ubuf += n;
173+ } else {
174+ memcpy(READ_ONCE(client->wbuf.wpos), kbuf, n);
175+ kbuf += n;
176 }
177
178 sbefifo_buf_wrotenb(&client->wbuf, n);
179 len -= n;
180- buf += n;
181 ret += n;
182
183 /*
184@@ -685,6 +702,19 @@ static ssize_t sbefifo_write(struct file *file, const char __user *buf,
185 return ret;
186 }
187
188+static ssize_t sbefifo_write(struct file *file, const char __user *buf,
189+ size_t len, loff_t *offset)
190+{
191+ struct sbefifo_client *client = file->private_data;
192+
193+ WARN_ON(*offset);
194+
195+ if (!access_ok(VERIFY_READ, buf, len))
196+ return -EFAULT;
197+
198+ return sbefifo_write_common(client, buf, NULL, len);
199+}
200+
201 static int sbefifo_release(struct inode *inode, struct file *file)
202 {
203 struct sbefifo_client *client = file->private_data;
204@@ -704,12 +734,68 @@ static int sbefifo_release(struct inode *inode, struct file *file)
205 .release = sbefifo_release,
206 };
207
208+struct sbefifo_client *sbefifo_drv_open(struct device *dev,
209+ unsigned long flags)
210+{
211+ struct sbefifo_client *client = NULL;
212+ struct sbefifo *sbefifo;
213+ struct fsi_device *fsi_dev = to_fsi_dev(dev);
214+
215+ list_for_each_entry(sbefifo, &sbefifo_fifos, link) {
216+ if (sbefifo->fsi_dev != fsi_dev)
217+ continue;
218+
219+ client = sbefifo_new_client(sbefifo);
220+ if (client)
221+ client->f_flags = flags;
222+ }
223+
224+ return client;
225+}
226+EXPORT_SYMBOL_GPL(sbefifo_drv_open);
227+
228+int sbefifo_drv_read(struct sbefifo_client *client, char *buf, size_t len)
229+{
230+ return sbefifo_read_common(client, NULL, buf, len);
231+}
232+EXPORT_SYMBOL_GPL(sbefifo_drv_read);
233+
234+int sbefifo_drv_write(struct sbefifo_client *client, const char *buf,
235+ size_t len)
236+{
237+ return sbefifo_write_common(client, NULL, buf, len);
238+}
239+EXPORT_SYMBOL_GPL(sbefifo_drv_write);
240+
241+void sbefifo_drv_release(struct sbefifo_client *client)
242+{
243+ if (!client)
244+ return;
245+
246+ sbefifo_put_client(client);
247+}
248+EXPORT_SYMBOL_GPL(sbefifo_drv_release);
249+
250+static int sbefifo_unregister_child(struct device *dev, void *data)
251+{
252+ struct platform_device *child = to_platform_device(dev);
253+
254+ of_device_unregister(child);
255+ if (dev->of_node)
256+ of_node_clear_flag(dev->of_node, OF_POPULATED);
257+
258+ return 0;
259+}
260+
261 static int sbefifo_probe(struct device *dev)
262 {
263 struct fsi_device *fsi_dev = to_fsi_dev(dev);
264 struct sbefifo *sbefifo;
265+ struct device_node *np;
266+ struct platform_device *child;
267+ char child_name[32];
268 u32 sts;
269- int ret;
270+ int ret, child_idx = 0;
271
272 dev_info(dev, "Found sbefifo device\n");
273 sbefifo = kzalloc(sizeof(*sbefifo), GFP_KERNEL);
274@@ -750,6 +836,18 @@ static int sbefifo_probe(struct device *dev)
275 init_waitqueue_head(&sbefifo->wait);
276 INIT_LIST_HEAD(&sbefifo->xfrs);
277
278+ if (dev->of_node) {
279+ /* create platform devs for dts child nodes (occ, etc) */
280+ for_each_child_of_node(dev->of_node, np) {
281+ snprintf(child_name, sizeof(child_name), "%s-dev%d",
282+ sbefifo->name, child_idx++);
283+ child = of_platform_device_create(np, child_name, dev);
284+ if (!child)
285+ dev_warn(&sbefifo->fsi_dev->dev,
286+ "failed to create child node dev\n");
287+ }
288+ }
289+
290 /* This bit of silicon doesn't offer any interrupts... */
291 setup_timer(&sbefifo->poll_timer, sbefifo_poll_timer,
292 (unsigned long)sbefifo);
293@@ -767,6 +865,9 @@ static int sbefifo_remove(struct device *dev)
294 list_for_each_entry_safe(sbefifo, sbefifo_tmp, &sbefifo_fifos, link) {
295 if (sbefifo->fsi_dev != fsi_dev)
296 continue;
297+
298+ device_for_each_child(dev, NULL, sbefifo_unregister_child);
299+
300 misc_deregister(&sbefifo->mdev);
301 list_del(&sbefifo->link);
302 ida_simple_remove(&sbefifo_ida, sbefifo->idx);
303diff --git a/include/linux/fsi-sbefifo.h b/include/linux/fsi-sbefifo.h
304new file mode 100644
305index 0000000..1b46c63
306--- /dev/null
307+++ b/include/linux/fsi-sbefifo.h
308@@ -0,0 +1,30 @@
309+/*
310+ * SBEFIFO FSI Client device driver
311+ *
312+ * Copyright (C) IBM Corporation 2017
313+ *
314+ * This program is free software; you can redistribute it and/or modify
315+ * it under the terms of the GNU General Public License version 2 as
316+ * published by the Free Software Foundation.
317+ *
318+ * This program is distributed in the hope that it will be useful,
319+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
320+ * MERGCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
321+ * GNU General Public License for more details.
322+ */
323+
324+#ifndef __FSI_SBEFIFO_H__
325+#define __FSI_SBEFIFO_H__
326+
327+struct device;
328+struct sbefifo_client;
329+
330+extern struct sbefifo_client *sbefifo_drv_open(struct device *dev,
331+ unsigned long flags);
332+extern int sbefifo_drv_read(struct sbefifo_client *client, char *buf,
333+ size_t len);
334+extern int sbefifo_drv_write(struct sbefifo_client *client, const char *buf,
335+ size_t len);
336+extern void sbefifo_drv_release(struct sbefifo_client *client);
337+
338+#endif /* __FSI_SBEFIFO_H__ */