Edward A. James | 7bd8637 | 2017-05-15 12:28:44 -0500 | [diff] [blame] | 1 | From patchwork Fri May 12 19:38:18 2017 |
| 2 | Content-Type: text/plain; charset="utf-8" |
| 3 | MIME-Version: 1.0 |
| 4 | Content-Transfer-Encoding: 7bit |
| 5 | Subject: [linux,dev-4.10,1/3] drivers: fsi: sbefifo: Add in-kernel API |
| 6 | From: eajames@linux.vnet.ibm.com |
| 7 | X-Patchwork-Id: 761836 |
| 8 | Message-Id: <1494617900-32369-2-git-send-email-eajames@linux.vnet.ibm.com> |
| 9 | To: openbmc@lists.ozlabs.org |
| 10 | Cc: "Edward A. James" <eajames@us.ibm.com>, bradleyb@fuzziesquirrel.com, |
| 11 | cbostic@linux.vnet.ibm.com |
| 12 | Date: Fri, 12 May 2017 14:38:18 -0500 |
| 13 | |
| 14 | From: "Edward A. James" <eajames@us.ibm.com> |
| 15 | |
| 16 | Add exported functions to the SBEFIFO driver to open/write/read/close |
| 17 | from within the kernel. |
| 18 | |
| 19 | Signed-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 | |
| 26 | diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c |
| 27 | index 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); |
| 303 | diff --git a/include/linux/fsi-sbefifo.h b/include/linux/fsi-sbefifo.h |
| 304 | new file mode 100644 |
| 305 | index 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__ */ |