xref: /openbmc/linux/drivers/fsi/fsi-core.c (revision 014c2abc)
10508ad1fSJeremy Kerr /*
20508ad1fSJeremy Kerr  * FSI core driver
30508ad1fSJeremy Kerr  *
40508ad1fSJeremy Kerr  * Copyright (C) IBM Corporation 2016
50508ad1fSJeremy Kerr  *
60508ad1fSJeremy Kerr  * This program is free software; you can redistribute it and/or modify
70508ad1fSJeremy Kerr  * it under the terms of the GNU General Public License version 2 as
80508ad1fSJeremy Kerr  * published by the Free Software Foundation.
90508ad1fSJeremy Kerr  *
100508ad1fSJeremy Kerr  * This program is distributed in the hope that it will be useful,
110508ad1fSJeremy Kerr  * but WITHOUT ANY WARRANTY; without even the implied warranty of
120508ad1fSJeremy Kerr  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
130508ad1fSJeremy Kerr  * GNU General Public License for more details.
140508ad1fSJeremy Kerr  */
150508ad1fSJeremy Kerr 
160508ad1fSJeremy Kerr #include <linux/device.h>
170508ad1fSJeremy Kerr #include <linux/fsi.h>
1809aecfabSJeremy Kerr #include <linux/idr.h>
190508ad1fSJeremy Kerr #include <linux/module.h>
200508ad1fSJeremy Kerr 
2109aecfabSJeremy Kerr #include "fsi-master.h"
2209aecfabSJeremy Kerr 
2309aecfabSJeremy Kerr static DEFINE_IDA(master_ida);
2409aecfabSJeremy Kerr 
25faf0b116SJeremy Kerr struct fsi_slave {
26faf0b116SJeremy Kerr 	struct device		dev;
27faf0b116SJeremy Kerr 	struct fsi_master	*master;
28faf0b116SJeremy Kerr 	int			id;
29faf0b116SJeremy Kerr 	int			link;
30faf0b116SJeremy Kerr 	uint32_t		size;	/* size of slave address space */
31faf0b116SJeremy Kerr };
32faf0b116SJeremy Kerr 
33faf0b116SJeremy Kerr #define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
34faf0b116SJeremy Kerr 
35014c2abcSJeremy Kerr static int fsi_master_read(struct fsi_master *master, int link,
36014c2abcSJeremy Kerr 		uint8_t slave_id, uint32_t addr, void *val, size_t size);
37014c2abcSJeremy Kerr static int fsi_master_write(struct fsi_master *master, int link,
38014c2abcSJeremy Kerr 		uint8_t slave_id, uint32_t addr, const void *val, size_t size);
39014c2abcSJeremy Kerr 
40414c1026SJeremy Kerr /* FSI slave support */
41014c2abcSJeremy Kerr static int fsi_slave_calc_addr(struct fsi_slave *slave, uint32_t *addrp,
42014c2abcSJeremy Kerr 		uint8_t *idp)
43014c2abcSJeremy Kerr {
44014c2abcSJeremy Kerr 	uint32_t addr = *addrp;
45014c2abcSJeremy Kerr 	uint8_t id = *idp;
46014c2abcSJeremy Kerr 
47014c2abcSJeremy Kerr 	if (addr > slave->size)
48014c2abcSJeremy Kerr 		return -EINVAL;
49014c2abcSJeremy Kerr 
50014c2abcSJeremy Kerr 	/* For 23 bit addressing, we encode the extra two bits in the slave
51014c2abcSJeremy Kerr 	 * id (and the slave's actual ID needs to be 0).
52014c2abcSJeremy Kerr 	 */
53014c2abcSJeremy Kerr 	if (addr > 0x1fffff) {
54014c2abcSJeremy Kerr 		if (slave->id != 0)
55014c2abcSJeremy Kerr 			return -EINVAL;
56014c2abcSJeremy Kerr 		id = (addr >> 21) & 0x3;
57014c2abcSJeremy Kerr 		addr &= 0x1fffff;
58014c2abcSJeremy Kerr 	}
59014c2abcSJeremy Kerr 
60014c2abcSJeremy Kerr 	*addrp = addr;
61014c2abcSJeremy Kerr 	*idp = id;
62014c2abcSJeremy Kerr 	return 0;
63014c2abcSJeremy Kerr }
64014c2abcSJeremy Kerr 
65014c2abcSJeremy Kerr static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
66014c2abcSJeremy Kerr 			void *val, size_t size)
67014c2abcSJeremy Kerr {
68014c2abcSJeremy Kerr 	uint8_t id = slave->id;
69014c2abcSJeremy Kerr 	int rc;
70014c2abcSJeremy Kerr 
71014c2abcSJeremy Kerr 	rc = fsi_slave_calc_addr(slave, &addr, &id);
72014c2abcSJeremy Kerr 	if (rc)
73014c2abcSJeremy Kerr 		return rc;
74014c2abcSJeremy Kerr 
75014c2abcSJeremy Kerr 	return fsi_master_read(slave->master, slave->link, id,
76014c2abcSJeremy Kerr 			addr, val, size);
77014c2abcSJeremy Kerr }
78014c2abcSJeremy Kerr 
79014c2abcSJeremy Kerr static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
80014c2abcSJeremy Kerr 			const void *val, size_t size)
81014c2abcSJeremy Kerr {
82014c2abcSJeremy Kerr 	uint8_t id = slave->id;
83014c2abcSJeremy Kerr 	int rc;
84014c2abcSJeremy Kerr 
85014c2abcSJeremy Kerr 	rc = fsi_slave_calc_addr(slave, &addr, &id);
86014c2abcSJeremy Kerr 	if (rc)
87014c2abcSJeremy Kerr 		return rc;
88014c2abcSJeremy Kerr 
89014c2abcSJeremy Kerr 	return fsi_master_write(slave->master, slave->link, id,
90014c2abcSJeremy Kerr 			addr, val, size);
91014c2abcSJeremy Kerr }
92014c2abcSJeremy Kerr 
93414c1026SJeremy Kerr static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
94414c1026SJeremy Kerr {
95414c1026SJeremy Kerr 	/* todo: initialise slave device, perform engine scan */
96414c1026SJeremy Kerr 
97414c1026SJeremy Kerr 	return -ENODEV;
98414c1026SJeremy Kerr }
99414c1026SJeremy Kerr 
10009aecfabSJeremy Kerr /* FSI master support */
101014c2abcSJeremy Kerr static int fsi_check_access(uint32_t addr, size_t size)
102014c2abcSJeremy Kerr {
103014c2abcSJeremy Kerr 	if (size != 1 && size != 2 && size != 4)
104014c2abcSJeremy Kerr 		return -EINVAL;
105014c2abcSJeremy Kerr 
106014c2abcSJeremy Kerr 	if ((addr & 0x3) != (size & 0x3))
107014c2abcSJeremy Kerr 		return -EINVAL;
108014c2abcSJeremy Kerr 
109014c2abcSJeremy Kerr 	return 0;
110014c2abcSJeremy Kerr }
111014c2abcSJeremy Kerr 
112014c2abcSJeremy Kerr static int fsi_master_read(struct fsi_master *master, int link,
113014c2abcSJeremy Kerr 		uint8_t slave_id, uint32_t addr, void *val, size_t size)
114014c2abcSJeremy Kerr {
115014c2abcSJeremy Kerr 	int rc;
116014c2abcSJeremy Kerr 
117014c2abcSJeremy Kerr 	rc = fsi_check_access(addr, size);
118014c2abcSJeremy Kerr 	if (rc)
119014c2abcSJeremy Kerr 		return rc;
120014c2abcSJeremy Kerr 
121014c2abcSJeremy Kerr 	return master->read(master, link, slave_id, addr, val, size);
122014c2abcSJeremy Kerr }
123014c2abcSJeremy Kerr 
124014c2abcSJeremy Kerr static int fsi_master_write(struct fsi_master *master, int link,
125014c2abcSJeremy Kerr 		uint8_t slave_id, uint32_t addr, const void *val, size_t size)
126014c2abcSJeremy Kerr {
127014c2abcSJeremy Kerr 	int rc;
128014c2abcSJeremy Kerr 
129014c2abcSJeremy Kerr 	rc = fsi_check_access(addr, size);
130014c2abcSJeremy Kerr 	if (rc)
131014c2abcSJeremy Kerr 		return rc;
132014c2abcSJeremy Kerr 
133014c2abcSJeremy Kerr 	return master->write(master, link, slave_id, addr, val, size);
134014c2abcSJeremy Kerr }
135014c2abcSJeremy Kerr 
136414c1026SJeremy Kerr static int fsi_master_scan(struct fsi_master *master)
137414c1026SJeremy Kerr {
138414c1026SJeremy Kerr 	int link;
139414c1026SJeremy Kerr 
140414c1026SJeremy Kerr 	for (link = 0; link < master->n_links; link++)
141414c1026SJeremy Kerr 		fsi_slave_init(master, link, 0);
142414c1026SJeremy Kerr 
143414c1026SJeremy Kerr 	return 0;
144414c1026SJeremy Kerr }
145414c1026SJeremy Kerr 
14609aecfabSJeremy Kerr int fsi_master_register(struct fsi_master *master)
14709aecfabSJeremy Kerr {
14809aecfabSJeremy Kerr 	int rc;
14909aecfabSJeremy Kerr 
15009aecfabSJeremy Kerr 	if (!master)
15109aecfabSJeremy Kerr 		return -EINVAL;
15209aecfabSJeremy Kerr 
15309aecfabSJeremy Kerr 	master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL);
15409aecfabSJeremy Kerr 	dev_set_name(&master->dev, "fsi%d", master->idx);
15509aecfabSJeremy Kerr 
15609aecfabSJeremy Kerr 	rc = device_register(&master->dev);
157414c1026SJeremy Kerr 	if (rc) {
15809aecfabSJeremy Kerr 		ida_simple_remove(&master_ida, master->idx);
15909aecfabSJeremy Kerr 		return rc;
16009aecfabSJeremy Kerr 	}
161414c1026SJeremy Kerr 
162414c1026SJeremy Kerr 	fsi_master_scan(master);
163414c1026SJeremy Kerr 	return 0;
164414c1026SJeremy Kerr }
16509aecfabSJeremy Kerr EXPORT_SYMBOL_GPL(fsi_master_register);
16609aecfabSJeremy Kerr 
16709aecfabSJeremy Kerr void fsi_master_unregister(struct fsi_master *master)
16809aecfabSJeremy Kerr {
16909aecfabSJeremy Kerr 	if (master->idx >= 0) {
17009aecfabSJeremy Kerr 		ida_simple_remove(&master_ida, master->idx);
17109aecfabSJeremy Kerr 		master->idx = -1;
17209aecfabSJeremy Kerr 	}
17309aecfabSJeremy Kerr 
17409aecfabSJeremy Kerr 	device_unregister(&master->dev);
17509aecfabSJeremy Kerr }
17609aecfabSJeremy Kerr EXPORT_SYMBOL_GPL(fsi_master_unregister);
17709aecfabSJeremy Kerr 
1780508ad1fSJeremy Kerr /* FSI core & Linux bus type definitions */
1790508ad1fSJeremy Kerr 
180dd37eed7SJeremy Kerr static int fsi_bus_match(struct device *dev, struct device_driver *drv)
181dd37eed7SJeremy Kerr {
182dd37eed7SJeremy Kerr 	struct fsi_device *fsi_dev = to_fsi_dev(dev);
183dd37eed7SJeremy Kerr 	struct fsi_driver *fsi_drv = to_fsi_drv(drv);
184dd37eed7SJeremy Kerr 	const struct fsi_device_id *id;
185dd37eed7SJeremy Kerr 
186dd37eed7SJeremy Kerr 	if (!fsi_drv->id_table)
187dd37eed7SJeremy Kerr 		return 0;
188dd37eed7SJeremy Kerr 
189dd37eed7SJeremy Kerr 	for (id = fsi_drv->id_table; id->engine_type; id++) {
190dd37eed7SJeremy Kerr 		if (id->engine_type != fsi_dev->engine_type)
191dd37eed7SJeremy Kerr 			continue;
192dd37eed7SJeremy Kerr 		if (id->version == FSI_VERSION_ANY ||
193dd37eed7SJeremy Kerr 				id->version == fsi_dev->version)
194dd37eed7SJeremy Kerr 			return 1;
195dd37eed7SJeremy Kerr 	}
196dd37eed7SJeremy Kerr 
197dd37eed7SJeremy Kerr 	return 0;
198dd37eed7SJeremy Kerr }
199dd37eed7SJeremy Kerr 
2000508ad1fSJeremy Kerr struct bus_type fsi_bus_type = {
2010508ad1fSJeremy Kerr 	.name		= "fsi",
202dd37eed7SJeremy Kerr 	.match		= fsi_bus_match,
2030508ad1fSJeremy Kerr };
2040508ad1fSJeremy Kerr EXPORT_SYMBOL_GPL(fsi_bus_type);
2050508ad1fSJeremy Kerr 
2060508ad1fSJeremy Kerr static int fsi_init(void)
2070508ad1fSJeremy Kerr {
2080508ad1fSJeremy Kerr 	return bus_register(&fsi_bus_type);
2090508ad1fSJeremy Kerr }
2100508ad1fSJeremy Kerr 
2110508ad1fSJeremy Kerr static void fsi_exit(void)
2120508ad1fSJeremy Kerr {
2130508ad1fSJeremy Kerr 	bus_unregister(&fsi_bus_type);
2140508ad1fSJeremy Kerr }
2150508ad1fSJeremy Kerr 
2160508ad1fSJeremy Kerr module_init(fsi_init);
2170508ad1fSJeremy Kerr module_exit(fsi_exit);
218