xref: /openbmc/linux/drivers/fsi/i2cr-scom.c (revision c0b34bed)
1*c0b34bedSEddie James // SPDX-License-Identifier: GPL-2.0
2*c0b34bedSEddie James /* Copyright (C) IBM Corporation 2023 */
3*c0b34bedSEddie James 
4*c0b34bedSEddie James #include <linux/cdev.h>
5*c0b34bedSEddie James #include <linux/device.h>
6*c0b34bedSEddie James #include <linux/fs.h>
7*c0b34bedSEddie James #include <linux/fsi.h>
8*c0b34bedSEddie James #include <linux/module.h>
9*c0b34bedSEddie James #include <linux/mod_devicetable.h>
10*c0b34bedSEddie James 
11*c0b34bedSEddie James #include "fsi-master-i2cr.h"
12*c0b34bedSEddie James #include "fsi-slave.h"
13*c0b34bedSEddie James 
14*c0b34bedSEddie James struct i2cr_scom {
15*c0b34bedSEddie James 	struct device dev;
16*c0b34bedSEddie James 	struct cdev cdev;
17*c0b34bedSEddie James 	struct fsi_master_i2cr *i2cr;
18*c0b34bedSEddie James };
19*c0b34bedSEddie James 
i2cr_scom_llseek(struct file * file,loff_t offset,int whence)20*c0b34bedSEddie James static loff_t i2cr_scom_llseek(struct file *file, loff_t offset, int whence)
21*c0b34bedSEddie James {
22*c0b34bedSEddie James 	switch (whence) {
23*c0b34bedSEddie James 	case SEEK_CUR:
24*c0b34bedSEddie James 		break;
25*c0b34bedSEddie James 	case SEEK_SET:
26*c0b34bedSEddie James 		file->f_pos = offset;
27*c0b34bedSEddie James 		break;
28*c0b34bedSEddie James 	default:
29*c0b34bedSEddie James 		return -EINVAL;
30*c0b34bedSEddie James 	}
31*c0b34bedSEddie James 
32*c0b34bedSEddie James 	return offset;
33*c0b34bedSEddie James }
34*c0b34bedSEddie James 
i2cr_scom_read(struct file * filep,char __user * buf,size_t len,loff_t * offset)35*c0b34bedSEddie James static ssize_t i2cr_scom_read(struct file *filep, char __user *buf, size_t len, loff_t *offset)
36*c0b34bedSEddie James {
37*c0b34bedSEddie James 	struct i2cr_scom *scom = filep->private_data;
38*c0b34bedSEddie James 	u64 data;
39*c0b34bedSEddie James 	int ret;
40*c0b34bedSEddie James 
41*c0b34bedSEddie James 	if (len != sizeof(data))
42*c0b34bedSEddie James 		return -EINVAL;
43*c0b34bedSEddie James 
44*c0b34bedSEddie James 	ret = fsi_master_i2cr_read(scom->i2cr, (u32)*offset, &data);
45*c0b34bedSEddie James 	if (ret)
46*c0b34bedSEddie James 		return ret;
47*c0b34bedSEddie James 
48*c0b34bedSEddie James 	ret = copy_to_user(buf, &data, len);
49*c0b34bedSEddie James 	if (ret)
50*c0b34bedSEddie James 		return ret;
51*c0b34bedSEddie James 
52*c0b34bedSEddie James 	return len;
53*c0b34bedSEddie James }
54*c0b34bedSEddie James 
i2cr_scom_write(struct file * filep,const char __user * buf,size_t len,loff_t * offset)55*c0b34bedSEddie James static ssize_t i2cr_scom_write(struct file *filep, const char __user *buf, size_t len,
56*c0b34bedSEddie James 			       loff_t *offset)
57*c0b34bedSEddie James {
58*c0b34bedSEddie James 	struct i2cr_scom *scom = filep->private_data;
59*c0b34bedSEddie James 	u64 data;
60*c0b34bedSEddie James 	int ret;
61*c0b34bedSEddie James 
62*c0b34bedSEddie James 	if (len != sizeof(data))
63*c0b34bedSEddie James 		return -EINVAL;
64*c0b34bedSEddie James 
65*c0b34bedSEddie James 	ret = copy_from_user(&data, buf, len);
66*c0b34bedSEddie James 	if (ret)
67*c0b34bedSEddie James 		return ret;
68*c0b34bedSEddie James 
69*c0b34bedSEddie James 	ret = fsi_master_i2cr_write(scom->i2cr, (u32)*offset, data);
70*c0b34bedSEddie James 	if (ret)
71*c0b34bedSEddie James 		return ret;
72*c0b34bedSEddie James 
73*c0b34bedSEddie James 	return len;
74*c0b34bedSEddie James }
75*c0b34bedSEddie James 
76*c0b34bedSEddie James static const struct file_operations i2cr_scom_fops = {
77*c0b34bedSEddie James 	.owner		= THIS_MODULE,
78*c0b34bedSEddie James 	.open		= simple_open,
79*c0b34bedSEddie James 	.llseek		= i2cr_scom_llseek,
80*c0b34bedSEddie James 	.read		= i2cr_scom_read,
81*c0b34bedSEddie James 	.write		= i2cr_scom_write,
82*c0b34bedSEddie James };
83*c0b34bedSEddie James 
i2cr_scom_probe(struct device * dev)84*c0b34bedSEddie James static int i2cr_scom_probe(struct device *dev)
85*c0b34bedSEddie James {
86*c0b34bedSEddie James 	struct fsi_device *fsi_dev = to_fsi_dev(dev);
87*c0b34bedSEddie James 	struct i2cr_scom *scom;
88*c0b34bedSEddie James 	int didx;
89*c0b34bedSEddie James 	int ret;
90*c0b34bedSEddie James 
91*c0b34bedSEddie James 	if (!is_fsi_master_i2cr(fsi_dev->slave->master))
92*c0b34bedSEddie James 		return -ENODEV;
93*c0b34bedSEddie James 
94*c0b34bedSEddie James 	scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
95*c0b34bedSEddie James 	if (!scom)
96*c0b34bedSEddie James 		return -ENOMEM;
97*c0b34bedSEddie James 
98*c0b34bedSEddie James 	scom->i2cr = to_fsi_master_i2cr(fsi_dev->slave->master);
99*c0b34bedSEddie James 	dev_set_drvdata(dev, scom);
100*c0b34bedSEddie James 
101*c0b34bedSEddie James 	scom->dev.type = &fsi_cdev_type;
102*c0b34bedSEddie James 	scom->dev.parent = dev;
103*c0b34bedSEddie James 	device_initialize(&scom->dev);
104*c0b34bedSEddie James 
105*c0b34bedSEddie James 	ret = fsi_get_new_minor(fsi_dev, fsi_dev_scom, &scom->dev.devt, &didx);
106*c0b34bedSEddie James 	if (ret)
107*c0b34bedSEddie James 		return ret;
108*c0b34bedSEddie James 
109*c0b34bedSEddie James 	dev_set_name(&scom->dev, "scom%d", didx);
110*c0b34bedSEddie James 	cdev_init(&scom->cdev, &i2cr_scom_fops);
111*c0b34bedSEddie James 	ret = cdev_device_add(&scom->cdev, &scom->dev);
112*c0b34bedSEddie James 	if (ret)
113*c0b34bedSEddie James 		fsi_free_minor(scom->dev.devt);
114*c0b34bedSEddie James 
115*c0b34bedSEddie James 	return ret;
116*c0b34bedSEddie James }
117*c0b34bedSEddie James 
i2cr_scom_remove(struct device * dev)118*c0b34bedSEddie James static int i2cr_scom_remove(struct device *dev)
119*c0b34bedSEddie James {
120*c0b34bedSEddie James 	struct i2cr_scom *scom = dev_get_drvdata(dev);
121*c0b34bedSEddie James 
122*c0b34bedSEddie James 	cdev_device_del(&scom->cdev, &scom->dev);
123*c0b34bedSEddie James 	fsi_free_minor(scom->dev.devt);
124*c0b34bedSEddie James 
125*c0b34bedSEddie James 	return 0;
126*c0b34bedSEddie James }
127*c0b34bedSEddie James 
128*c0b34bedSEddie James static const struct of_device_id i2cr_scom_of_ids[] = {
129*c0b34bedSEddie James 	{ .compatible = "ibm,i2cr-scom" },
130*c0b34bedSEddie James 	{ }
131*c0b34bedSEddie James };
132*c0b34bedSEddie James MODULE_DEVICE_TABLE(of, i2cr_scom_of_ids);
133*c0b34bedSEddie James 
134*c0b34bedSEddie James static const struct fsi_device_id i2cr_scom_ids[] = {
135*c0b34bedSEddie James 	{ 0x5, FSI_VERSION_ANY },
136*c0b34bedSEddie James 	{ }
137*c0b34bedSEddie James };
138*c0b34bedSEddie James 
139*c0b34bedSEddie James static struct fsi_driver i2cr_scom_driver = {
140*c0b34bedSEddie James 	.id_table = i2cr_scom_ids,
141*c0b34bedSEddie James 	.drv = {
142*c0b34bedSEddie James 		.name = "i2cr_scom",
143*c0b34bedSEddie James 		.bus = &fsi_bus_type,
144*c0b34bedSEddie James 		.of_match_table = i2cr_scom_of_ids,
145*c0b34bedSEddie James 		.probe = i2cr_scom_probe,
146*c0b34bedSEddie James 		.remove = i2cr_scom_remove,
147*c0b34bedSEddie James 	}
148*c0b34bedSEddie James };
149*c0b34bedSEddie James 
150*c0b34bedSEddie James module_fsi_driver(i2cr_scom_driver);
151*c0b34bedSEddie James 
152*c0b34bedSEddie James MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
153*c0b34bedSEddie James MODULE_DESCRIPTION("IBM I2C Responder SCOM driver");
154*c0b34bedSEddie James MODULE_LICENSE("GPL");
155