Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/fsi/fsi-scom.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* SCOM FSI Client device driver
4
*
5
* Copyright (C) IBM Corporation 2016
6
*/
7
8
#include <linux/fsi.h>
9
#include <linux/module.h>
10
#include <linux/cdev.h>
11
#include <linux/delay.h>
12
#include <linux/fs.h>
13
#include <linux/mod_devicetable.h>
14
#include <linux/uaccess.h>
15
#include <linux/slab.h>
16
#include <linux/list.h>
17
18
#include <uapi/linux/fsi.h>
19
20
#define FSI_ENGID_SCOM 0x5
21
22
/* SCOM engine register set */
23
#define SCOM_DATA0_REG 0x00
24
#define SCOM_DATA1_REG 0x04
25
#define SCOM_CMD_REG 0x08
26
#define SCOM_FSI2PIB_RESET_REG 0x18
27
#define SCOM_STATUS_REG 0x1C /* Read */
28
#define SCOM_PIB_RESET_REG 0x1C /* Write */
29
30
/* Command register */
31
#define SCOM_WRITE_CMD 0x80000000
32
#define SCOM_READ_CMD 0x00000000
33
34
/* Status register bits */
35
#define SCOM_STATUS_ERR_SUMMARY 0x80000000
36
#define SCOM_STATUS_PROTECTION 0x01000000
37
#define SCOM_STATUS_PARITY 0x04000000
38
#define SCOM_STATUS_PIB_ABORT 0x00100000
39
#define SCOM_STATUS_PIB_RESP_MASK 0x00007000
40
#define SCOM_STATUS_PIB_RESP_SHIFT 12
41
42
#define SCOM_STATUS_FSI2PIB_ERROR (SCOM_STATUS_PROTECTION | \
43
SCOM_STATUS_PARITY | \
44
SCOM_STATUS_PIB_ABORT)
45
#define SCOM_STATUS_ANY_ERR (SCOM_STATUS_FSI2PIB_ERROR | \
46
SCOM_STATUS_PIB_RESP_MASK)
47
/* SCOM address encodings */
48
#define XSCOM_ADDR_IND_FLAG BIT_ULL(63)
49
#define XSCOM_ADDR_INF_FORM1 BIT_ULL(60)
50
51
/* SCOM indirect stuff */
52
#define XSCOM_ADDR_DIRECT_PART 0x7fffffffull
53
#define XSCOM_ADDR_INDIRECT_PART 0x000fffff00000000ull
54
#define XSCOM_DATA_IND_READ BIT_ULL(63)
55
#define XSCOM_DATA_IND_COMPLETE BIT_ULL(31)
56
#define XSCOM_DATA_IND_ERR_MASK 0x70000000ull
57
#define XSCOM_DATA_IND_ERR_SHIFT 28
58
#define XSCOM_DATA_IND_DATA 0x0000ffffull
59
#define XSCOM_DATA_IND_FORM1_DATA 0x000fffffffffffffull
60
#define XSCOM_ADDR_FORM1_LOW 0x000ffffffffull
61
#define XSCOM_ADDR_FORM1_HI 0xfff00000000ull
62
#define XSCOM_ADDR_FORM1_HI_SHIFT 20
63
64
/* Retries */
65
#define SCOM_MAX_IND_RETRIES 10 /* Retries indirect not ready */
66
67
struct scom_device {
68
struct list_head link;
69
struct fsi_device *fsi_dev;
70
struct device dev;
71
struct cdev cdev;
72
struct mutex lock;
73
bool dead;
74
};
75
76
static int __put_scom(struct scom_device *scom_dev, uint64_t value,
77
uint32_t addr, uint32_t *status)
78
{
79
__be32 data, raw_status;
80
int rc;
81
82
data = cpu_to_be32((value >> 32) & 0xffffffff);
83
rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
84
sizeof(uint32_t));
85
if (rc)
86
return rc;
87
88
data = cpu_to_be32(value & 0xffffffff);
89
rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
90
sizeof(uint32_t));
91
if (rc)
92
return rc;
93
94
data = cpu_to_be32(SCOM_WRITE_CMD | addr);
95
rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
96
sizeof(uint32_t));
97
if (rc)
98
return rc;
99
rc = fsi_device_read(scom_dev->fsi_dev, SCOM_STATUS_REG, &raw_status,
100
sizeof(uint32_t));
101
if (rc)
102
return rc;
103
*status = be32_to_cpu(raw_status);
104
105
return 0;
106
}
107
108
static int __get_scom(struct scom_device *scom_dev, uint64_t *value,
109
uint32_t addr, uint32_t *status)
110
{
111
__be32 data, raw_status;
112
int rc;
113
114
115
*value = 0ULL;
116
data = cpu_to_be32(SCOM_READ_CMD | addr);
117
rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
118
sizeof(uint32_t));
119
if (rc)
120
return rc;
121
rc = fsi_device_read(scom_dev->fsi_dev, SCOM_STATUS_REG, &raw_status,
122
sizeof(uint32_t));
123
if (rc)
124
return rc;
125
126
/*
127
* Read the data registers even on error, so we don't have
128
* to interpret the status register here.
129
*/
130
rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
131
sizeof(uint32_t));
132
if (rc)
133
return rc;
134
*value |= (uint64_t)be32_to_cpu(data) << 32;
135
rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
136
sizeof(uint32_t));
137
if (rc)
138
return rc;
139
*value |= be32_to_cpu(data);
140
*status = be32_to_cpu(raw_status);
141
142
return rc;
143
}
144
145
static int put_indirect_scom_form0(struct scom_device *scom, uint64_t value,
146
uint64_t addr, uint32_t *status)
147
{
148
uint64_t ind_data, ind_addr;
149
int rc, err;
150
151
if (value & ~XSCOM_DATA_IND_DATA)
152
return -EINVAL;
153
154
ind_addr = addr & XSCOM_ADDR_DIRECT_PART;
155
ind_data = (addr & XSCOM_ADDR_INDIRECT_PART) | value;
156
rc = __put_scom(scom, ind_data, ind_addr, status);
157
if (rc || (*status & SCOM_STATUS_ANY_ERR))
158
return rc;
159
160
rc = __get_scom(scom, &ind_data, addr, status);
161
if (rc || (*status & SCOM_STATUS_ANY_ERR))
162
return rc;
163
164
err = (ind_data & XSCOM_DATA_IND_ERR_MASK) >> XSCOM_DATA_IND_ERR_SHIFT;
165
*status = err << SCOM_STATUS_PIB_RESP_SHIFT;
166
167
return 0;
168
}
169
170
static int put_indirect_scom_form1(struct scom_device *scom, uint64_t value,
171
uint64_t addr, uint32_t *status)
172
{
173
uint64_t ind_data, ind_addr;
174
175
if (value & ~XSCOM_DATA_IND_FORM1_DATA)
176
return -EINVAL;
177
178
ind_addr = addr & XSCOM_ADDR_FORM1_LOW;
179
ind_data = value | (addr & XSCOM_ADDR_FORM1_HI) << XSCOM_ADDR_FORM1_HI_SHIFT;
180
return __put_scom(scom, ind_data, ind_addr, status);
181
}
182
183
static int get_indirect_scom_form0(struct scom_device *scom, uint64_t *value,
184
uint64_t addr, uint32_t *status)
185
{
186
uint64_t ind_data, ind_addr;
187
int rc, err;
188
189
ind_addr = addr & XSCOM_ADDR_DIRECT_PART;
190
ind_data = (addr & XSCOM_ADDR_INDIRECT_PART) | XSCOM_DATA_IND_READ;
191
rc = __put_scom(scom, ind_data, ind_addr, status);
192
if (rc || (*status & SCOM_STATUS_ANY_ERR))
193
return rc;
194
195
rc = __get_scom(scom, &ind_data, addr, status);
196
if (rc || (*status & SCOM_STATUS_ANY_ERR))
197
return rc;
198
199
err = (ind_data & XSCOM_DATA_IND_ERR_MASK) >> XSCOM_DATA_IND_ERR_SHIFT;
200
*status = err << SCOM_STATUS_PIB_RESP_SHIFT;
201
*value = ind_data & XSCOM_DATA_IND_DATA;
202
203
return 0;
204
}
205
206
static int raw_put_scom(struct scom_device *scom, uint64_t value,
207
uint64_t addr, uint32_t *status)
208
{
209
if (addr & XSCOM_ADDR_IND_FLAG) {
210
if (addr & XSCOM_ADDR_INF_FORM1)
211
return put_indirect_scom_form1(scom, value, addr, status);
212
else
213
return put_indirect_scom_form0(scom, value, addr, status);
214
} else
215
return __put_scom(scom, value, addr, status);
216
}
217
218
static int raw_get_scom(struct scom_device *scom, uint64_t *value,
219
uint64_t addr, uint32_t *status)
220
{
221
if (addr & XSCOM_ADDR_IND_FLAG) {
222
if (addr & XSCOM_ADDR_INF_FORM1)
223
return -ENXIO;
224
return get_indirect_scom_form0(scom, value, addr, status);
225
} else
226
return __get_scom(scom, value, addr, status);
227
}
228
229
static int handle_fsi2pib_status(struct scom_device *scom, uint32_t status)
230
{
231
uint32_t dummy = -1;
232
233
if (status & SCOM_STATUS_FSI2PIB_ERROR)
234
fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy,
235
sizeof(uint32_t));
236
237
if (status & SCOM_STATUS_PROTECTION)
238
return -EPERM;
239
if (status & SCOM_STATUS_PARITY)
240
return -EIO;
241
242
if (status & SCOM_STATUS_PIB_ABORT)
243
return -EBUSY;
244
return 0;
245
}
246
247
static int handle_pib_status(struct scom_device *scom, uint8_t status)
248
{
249
uint32_t dummy = -1;
250
251
if (status == SCOM_PIB_SUCCESS)
252
return 0;
253
if (status == SCOM_PIB_BLOCKED)
254
return -EBUSY;
255
256
/* Reset the bridge */
257
fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy,
258
sizeof(uint32_t));
259
260
switch(status) {
261
case SCOM_PIB_OFFLINE:
262
return -ENODEV;
263
case SCOM_PIB_BAD_ADDR:
264
return -ENXIO;
265
case SCOM_PIB_TIMEOUT:
266
return -ETIMEDOUT;
267
case SCOM_PIB_PARTIAL:
268
case SCOM_PIB_CLK_ERR:
269
case SCOM_PIB_PARITY_ERR:
270
default:
271
return -EIO;
272
}
273
}
274
275
static int put_scom(struct scom_device *scom, uint64_t value,
276
uint64_t addr)
277
{
278
uint32_t status;
279
int rc;
280
281
rc = raw_put_scom(scom, value, addr, &status);
282
if (rc)
283
return rc;
284
285
rc = handle_fsi2pib_status(scom, status);
286
if (rc)
287
return rc;
288
289
return handle_pib_status(scom,
290
(status & SCOM_STATUS_PIB_RESP_MASK)
291
>> SCOM_STATUS_PIB_RESP_SHIFT);
292
}
293
294
static int get_scom(struct scom_device *scom, uint64_t *value,
295
uint64_t addr)
296
{
297
uint32_t status;
298
int rc;
299
300
rc = raw_get_scom(scom, value, addr, &status);
301
if (rc)
302
return rc;
303
304
rc = handle_fsi2pib_status(scom, status);
305
if (rc)
306
return rc;
307
308
return handle_pib_status(scom,
309
(status & SCOM_STATUS_PIB_RESP_MASK)
310
>> SCOM_STATUS_PIB_RESP_SHIFT);
311
}
312
313
static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
314
loff_t *offset)
315
{
316
struct scom_device *scom = filep->private_data;
317
struct device *dev = &scom->fsi_dev->dev;
318
uint64_t val;
319
int rc;
320
321
if (len != sizeof(uint64_t))
322
return -EINVAL;
323
324
mutex_lock(&scom->lock);
325
if (scom->dead)
326
rc = -ENODEV;
327
else
328
rc = get_scom(scom, &val, *offset);
329
mutex_unlock(&scom->lock);
330
if (rc) {
331
dev_dbg(dev, "get_scom fail:%d\n", rc);
332
return rc;
333
}
334
335
rc = copy_to_user(buf, &val, len);
336
if (rc)
337
dev_dbg(dev, "copy to user failed:%d\n", rc);
338
339
return rc ? rc : len;
340
}
341
342
static ssize_t scom_write(struct file *filep, const char __user *buf,
343
size_t len, loff_t *offset)
344
{
345
int rc;
346
struct scom_device *scom = filep->private_data;
347
struct device *dev = &scom->fsi_dev->dev;
348
uint64_t val;
349
350
if (len != sizeof(uint64_t))
351
return -EINVAL;
352
353
rc = copy_from_user(&val, buf, len);
354
if (rc) {
355
dev_dbg(dev, "copy from user failed:%d\n", rc);
356
return -EINVAL;
357
}
358
359
mutex_lock(&scom->lock);
360
if (scom->dead)
361
rc = -ENODEV;
362
else
363
rc = put_scom(scom, val, *offset);
364
mutex_unlock(&scom->lock);
365
if (rc) {
366
dev_dbg(dev, "put_scom failed with:%d\n", rc);
367
return rc;
368
}
369
370
return len;
371
}
372
373
static loff_t scom_llseek(struct file *file, loff_t offset, int whence)
374
{
375
switch (whence) {
376
case SEEK_CUR:
377
break;
378
case SEEK_SET:
379
file->f_pos = offset;
380
break;
381
default:
382
return -EINVAL;
383
}
384
385
return offset;
386
}
387
388
static void raw_convert_status(struct scom_access *acc, uint32_t status)
389
{
390
acc->pib_status = (status & SCOM_STATUS_PIB_RESP_MASK) >>
391
SCOM_STATUS_PIB_RESP_SHIFT;
392
acc->intf_errors = 0;
393
394
if (status & SCOM_STATUS_PROTECTION)
395
acc->intf_errors |= SCOM_INTF_ERR_PROTECTION;
396
else if (status & SCOM_STATUS_PARITY)
397
acc->intf_errors |= SCOM_INTF_ERR_PARITY;
398
else if (status & SCOM_STATUS_PIB_ABORT)
399
acc->intf_errors |= SCOM_INTF_ERR_ABORT;
400
else if (status & SCOM_STATUS_ERR_SUMMARY)
401
acc->intf_errors |= SCOM_INTF_ERR_UNKNOWN;
402
}
403
404
static int scom_raw_read(struct scom_device *scom, void __user *argp)
405
{
406
struct scom_access acc;
407
uint32_t status;
408
int rc;
409
410
if (copy_from_user(&acc, argp, sizeof(struct scom_access)))
411
return -EFAULT;
412
413
rc = raw_get_scom(scom, &acc.data, acc.addr, &status);
414
if (rc)
415
return rc;
416
raw_convert_status(&acc, status);
417
if (copy_to_user(argp, &acc, sizeof(struct scom_access)))
418
return -EFAULT;
419
return 0;
420
}
421
422
static int scom_raw_write(struct scom_device *scom, void __user *argp)
423
{
424
u64 prev_data, mask, data;
425
struct scom_access acc;
426
uint32_t status;
427
int rc;
428
429
if (copy_from_user(&acc, argp, sizeof(struct scom_access)))
430
return -EFAULT;
431
432
if (acc.mask) {
433
rc = raw_get_scom(scom, &prev_data, acc.addr, &status);
434
if (rc)
435
return rc;
436
if (status & SCOM_STATUS_ANY_ERR)
437
goto fail;
438
mask = acc.mask;
439
} else {
440
prev_data = mask = -1ull;
441
}
442
data = (prev_data & ~mask) | (acc.data & mask);
443
rc = raw_put_scom(scom, data, acc.addr, &status);
444
if (rc)
445
return rc;
446
fail:
447
raw_convert_status(&acc, status);
448
if (copy_to_user(argp, &acc, sizeof(struct scom_access)))
449
return -EFAULT;
450
return 0;
451
}
452
453
static int scom_reset(struct scom_device *scom, void __user *argp)
454
{
455
uint32_t flags, dummy = -1;
456
int rc = 0;
457
458
if (get_user(flags, (__u32 __user *)argp))
459
return -EFAULT;
460
if (flags & SCOM_RESET_PIB)
461
rc = fsi_device_write(scom->fsi_dev, SCOM_PIB_RESET_REG, &dummy,
462
sizeof(uint32_t));
463
if (!rc && (flags & (SCOM_RESET_PIB | SCOM_RESET_INTF)))
464
rc = fsi_device_write(scom->fsi_dev, SCOM_FSI2PIB_RESET_REG, &dummy,
465
sizeof(uint32_t));
466
return rc;
467
}
468
469
static int scom_check(struct scom_device *scom, void __user *argp)
470
{
471
/* Still need to find out how to get "protected" */
472
return put_user(SCOM_CHECK_SUPPORTED, (__u32 __user *)argp);
473
}
474
475
static long scom_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
476
{
477
struct scom_device *scom = file->private_data;
478
void __user *argp = (void __user *)arg;
479
int rc = -ENOTTY;
480
481
mutex_lock(&scom->lock);
482
if (scom->dead) {
483
mutex_unlock(&scom->lock);
484
return -ENODEV;
485
}
486
switch(cmd) {
487
case FSI_SCOM_CHECK:
488
rc = scom_check(scom, argp);
489
break;
490
case FSI_SCOM_READ:
491
rc = scom_raw_read(scom, argp);
492
break;
493
case FSI_SCOM_WRITE:
494
rc = scom_raw_write(scom, argp);
495
break;
496
case FSI_SCOM_RESET:
497
rc = scom_reset(scom, argp);
498
break;
499
}
500
mutex_unlock(&scom->lock);
501
return rc;
502
}
503
504
static int scom_open(struct inode *inode, struct file *file)
505
{
506
struct scom_device *scom = container_of(inode->i_cdev, struct scom_device, cdev);
507
508
file->private_data = scom;
509
510
return 0;
511
}
512
513
static const struct file_operations scom_fops = {
514
.owner = THIS_MODULE,
515
.open = scom_open,
516
.llseek = scom_llseek,
517
.read = scom_read,
518
.write = scom_write,
519
.unlocked_ioctl = scom_ioctl,
520
};
521
522
static void scom_free(struct device *dev)
523
{
524
struct scom_device *scom = container_of(dev, struct scom_device, dev);
525
526
put_device(&scom->fsi_dev->dev);
527
kfree(scom);
528
}
529
530
static int scom_probe(struct device *dev)
531
{
532
struct fsi_device *fsi_dev = to_fsi_dev(dev);
533
struct scom_device *scom;
534
int rc, didx;
535
536
scom = kzalloc(sizeof(*scom), GFP_KERNEL);
537
if (!scom)
538
return -ENOMEM;
539
dev_set_drvdata(dev, scom);
540
mutex_init(&scom->lock);
541
542
/* Grab a reference to the device (parent of our cdev), we'll drop it later */
543
if (!get_device(dev)) {
544
kfree(scom);
545
return -ENODEV;
546
}
547
scom->fsi_dev = fsi_dev;
548
549
/* Create chardev for userspace access */
550
scom->dev.type = &fsi_cdev_type;
551
scom->dev.parent = dev;
552
scom->dev.release = scom_free;
553
device_initialize(&scom->dev);
554
555
/* Allocate a minor in the FSI space */
556
rc = fsi_get_new_minor(fsi_dev, fsi_dev_scom, &scom->dev.devt, &didx);
557
if (rc)
558
goto err;
559
560
dev_set_name(&scom->dev, "scom%d", didx);
561
cdev_init(&scom->cdev, &scom_fops);
562
rc = cdev_device_add(&scom->cdev, &scom->dev);
563
if (rc) {
564
dev_err(dev, "Error %d creating char device %s\n",
565
rc, dev_name(&scom->dev));
566
goto err_free_minor;
567
}
568
569
return 0;
570
err_free_minor:
571
fsi_free_minor(scom->dev.devt);
572
err:
573
put_device(&scom->dev);
574
return rc;
575
}
576
577
static int scom_remove(struct device *dev)
578
{
579
struct scom_device *scom = dev_get_drvdata(dev);
580
581
mutex_lock(&scom->lock);
582
scom->dead = true;
583
mutex_unlock(&scom->lock);
584
cdev_device_del(&scom->cdev, &scom->dev);
585
fsi_free_minor(scom->dev.devt);
586
put_device(&scom->dev);
587
588
return 0;
589
}
590
591
static const struct of_device_id scom_of_ids[] = {
592
{ .compatible = "ibm,fsi2pib" },
593
{ }
594
};
595
MODULE_DEVICE_TABLE(of, scom_of_ids);
596
597
static const struct fsi_device_id scom_ids[] = {
598
{
599
.engine_type = FSI_ENGID_SCOM,
600
.version = FSI_VERSION_ANY,
601
},
602
{ 0 }
603
};
604
605
static struct fsi_driver scom_drv = {
606
.id_table = scom_ids,
607
.drv = {
608
.name = "scom",
609
.bus = &fsi_bus_type,
610
.of_match_table = scom_of_ids,
611
.probe = scom_probe,
612
.remove = scom_remove,
613
}
614
};
615
616
static int scom_init(void)
617
{
618
return fsi_driver_register(&scom_drv);
619
}
620
621
static void scom_exit(void)
622
{
623
fsi_driver_unregister(&scom_drv);
624
}
625
626
module_init(scom_init);
627
module_exit(scom_exit);
628
MODULE_DESCRIPTION("SCOM FSI Client device driver");
629
MODULE_LICENSE("GPL");
630
631