Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
  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
 67struct 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
 76static 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
108static 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
145static 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
170static 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
183static 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
206static 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
218static 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
229static 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
247static 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
275static 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
294static 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
313static 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
342static 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
373static 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
388static 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
404static 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
422static 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
453static 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
469static 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
475static 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
504static 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
513static 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
522static 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
530static 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
577static 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
591static const struct of_device_id scom_of_ids[] = {
592	{ .compatible = "ibm,fsi2pib" },
593	{ }
594};
595MODULE_DEVICE_TABLE(of, scom_of_ids);
596
597static const struct fsi_device_id scom_ids[] = {
598	{
599		.engine_type = FSI_ENGID_SCOM,
600		.version = FSI_VERSION_ANY,
601	},
602	{ 0 }
603};
604
605static 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
616static int scom_init(void)
617{
618	return fsi_driver_register(&scom_drv);
619}
620
621static void scom_exit(void)
622{
623	fsi_driver_unregister(&scom_drv);
624}
625
626module_init(scom_init);
627module_exit(scom_exit);
628MODULE_DESCRIPTION("SCOM FSI Client device driver");
629MODULE_LICENSE("GPL");