xref: /openbmc/linux/drivers/fsi/fsi-core.c (revision f7ade2a6)
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 
162b545cd8SJeremy Kerr #include <linux/crc4.h>
170508ad1fSJeremy Kerr #include <linux/device.h>
180508ad1fSJeremy Kerr #include <linux/fsi.h>
1909aecfabSJeremy Kerr #include <linux/idr.h>
200508ad1fSJeremy Kerr #include <linux/module.h>
212b545cd8SJeremy Kerr #include <linux/slab.h>
22f7ade2a6SJeremy Kerr #include <linux/bitops.h>
230508ad1fSJeremy Kerr 
2409aecfabSJeremy Kerr #include "fsi-master.h"
2509aecfabSJeremy Kerr 
26f7ade2a6SJeremy Kerr #define FSI_SLAVE_CONF_NEXT_MASK	GENMASK(31, 31)
27f7ade2a6SJeremy Kerr #define FSI_SLAVE_CONF_SLOTS_MASK	GENMASK(23, 16)
28f7ade2a6SJeremy Kerr #define FSI_SLAVE_CONF_SLOTS_SHIFT	16
29f7ade2a6SJeremy Kerr #define FSI_SLAVE_CONF_VERSION_MASK	GENMASK(15, 12)
30f7ade2a6SJeremy Kerr #define FSI_SLAVE_CONF_VERSION_SHIFT	12
31f7ade2a6SJeremy Kerr #define FSI_SLAVE_CONF_TYPE_MASK	GENMASK(11, 4)
32f7ade2a6SJeremy Kerr #define FSI_SLAVE_CONF_TYPE_SHIFT	4
33f7ade2a6SJeremy Kerr #define FSI_SLAVE_CONF_CRC_SHIFT	4
34f7ade2a6SJeremy Kerr #define FSI_SLAVE_CONF_CRC_MASK		GENMASK(3, 0)
35f7ade2a6SJeremy Kerr #define FSI_SLAVE_CONF_DATA_BITS	28
36f7ade2a6SJeremy Kerr 
37f7ade2a6SJeremy Kerr static const int engine_page_size = 0x400;
38f7ade2a6SJeremy Kerr 
392b37c3e2SChristopher Bostic #define FSI_SLAVE_BASE			0x800
402b37c3e2SChristopher Bostic 
412b37c3e2SChristopher Bostic /*
422b37c3e2SChristopher Bostic  * FSI slave engine control register offsets
432b37c3e2SChristopher Bostic  */
442b37c3e2SChristopher Bostic #define FSI_SMODE			0x0	/* R/W: Mode register */
452b37c3e2SChristopher Bostic 
462b37c3e2SChristopher Bostic /*
472b37c3e2SChristopher Bostic  * SMODE fields
482b37c3e2SChristopher Bostic  */
492b37c3e2SChristopher Bostic #define FSI_SMODE_WSC		0x80000000	/* Warm start done */
502b37c3e2SChristopher Bostic #define FSI_SMODE_ECRC		0x20000000	/* Hw CRC check */
512b37c3e2SChristopher Bostic #define FSI_SMODE_SID_SHIFT	24		/* ID shift */
522b37c3e2SChristopher Bostic #define FSI_SMODE_SID_MASK	3		/* ID Mask */
532b37c3e2SChristopher Bostic #define FSI_SMODE_ED_SHIFT	20		/* Echo delay shift */
542b37c3e2SChristopher Bostic #define FSI_SMODE_ED_MASK	0xf		/* Echo delay mask */
552b37c3e2SChristopher Bostic #define FSI_SMODE_SD_SHIFT	16		/* Send delay shift */
562b37c3e2SChristopher Bostic #define FSI_SMODE_SD_MASK	0xf		/* Send delay mask */
572b37c3e2SChristopher Bostic #define FSI_SMODE_LBCRR_SHIFT	8		/* Clk ratio shift */
582b37c3e2SChristopher Bostic #define FSI_SMODE_LBCRR_MASK	0xf		/* Clk ratio mask */
592b37c3e2SChristopher Bostic 
602b545cd8SJeremy Kerr #define FSI_SLAVE_SIZE_23b		0x800000
612b545cd8SJeremy Kerr 
6209aecfabSJeremy Kerr static DEFINE_IDA(master_ida);
6309aecfabSJeremy Kerr 
64faf0b116SJeremy Kerr struct fsi_slave {
65faf0b116SJeremy Kerr 	struct device		dev;
66faf0b116SJeremy Kerr 	struct fsi_master	*master;
67faf0b116SJeremy Kerr 	int			id;
68faf0b116SJeremy Kerr 	int			link;
69faf0b116SJeremy Kerr 	uint32_t		size;	/* size of slave address space */
70faf0b116SJeremy Kerr };
71faf0b116SJeremy Kerr 
72faf0b116SJeremy Kerr #define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
73faf0b116SJeremy Kerr 
74014c2abcSJeremy Kerr static int fsi_master_read(struct fsi_master *master, int link,
75014c2abcSJeremy Kerr 		uint8_t slave_id, uint32_t addr, void *val, size_t size);
76014c2abcSJeremy Kerr static int fsi_master_write(struct fsi_master *master, int link,
77014c2abcSJeremy Kerr 		uint8_t slave_id, uint32_t addr, const void *val, size_t size);
78014c2abcSJeremy Kerr 
79f7ade2a6SJeremy Kerr /* FSI endpoint-device support */
80f7ade2a6SJeremy Kerr 
81f7ade2a6SJeremy Kerr static void fsi_device_release(struct device *_device)
82f7ade2a6SJeremy Kerr {
83f7ade2a6SJeremy Kerr 	struct fsi_device *device = to_fsi_dev(_device);
84f7ade2a6SJeremy Kerr 
85f7ade2a6SJeremy Kerr 	kfree(device);
86f7ade2a6SJeremy Kerr }
87f7ade2a6SJeremy Kerr 
88f7ade2a6SJeremy Kerr static struct fsi_device *fsi_create_device(struct fsi_slave *slave)
89f7ade2a6SJeremy Kerr {
90f7ade2a6SJeremy Kerr 	struct fsi_device *dev;
91f7ade2a6SJeremy Kerr 
92f7ade2a6SJeremy Kerr 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
93f7ade2a6SJeremy Kerr 	if (!dev)
94f7ade2a6SJeremy Kerr 		return NULL;
95f7ade2a6SJeremy Kerr 
96f7ade2a6SJeremy Kerr 	dev->dev.parent = &slave->dev;
97f7ade2a6SJeremy Kerr 	dev->dev.bus = &fsi_bus_type;
98f7ade2a6SJeremy Kerr 	dev->dev.release = fsi_device_release;
99f7ade2a6SJeremy Kerr 
100f7ade2a6SJeremy Kerr 	return dev;
101f7ade2a6SJeremy Kerr }
102f7ade2a6SJeremy Kerr 
103414c1026SJeremy Kerr /* FSI slave support */
104014c2abcSJeremy Kerr static int fsi_slave_calc_addr(struct fsi_slave *slave, uint32_t *addrp,
105014c2abcSJeremy Kerr 		uint8_t *idp)
106014c2abcSJeremy Kerr {
107014c2abcSJeremy Kerr 	uint32_t addr = *addrp;
108014c2abcSJeremy Kerr 	uint8_t id = *idp;
109014c2abcSJeremy Kerr 
110014c2abcSJeremy Kerr 	if (addr > slave->size)
111014c2abcSJeremy Kerr 		return -EINVAL;
112014c2abcSJeremy Kerr 
113014c2abcSJeremy Kerr 	/* For 23 bit addressing, we encode the extra two bits in the slave
114014c2abcSJeremy Kerr 	 * id (and the slave's actual ID needs to be 0).
115014c2abcSJeremy Kerr 	 */
116014c2abcSJeremy Kerr 	if (addr > 0x1fffff) {
117014c2abcSJeremy Kerr 		if (slave->id != 0)
118014c2abcSJeremy Kerr 			return -EINVAL;
119014c2abcSJeremy Kerr 		id = (addr >> 21) & 0x3;
120014c2abcSJeremy Kerr 		addr &= 0x1fffff;
121014c2abcSJeremy Kerr 	}
122014c2abcSJeremy Kerr 
123014c2abcSJeremy Kerr 	*addrp = addr;
124014c2abcSJeremy Kerr 	*idp = id;
125014c2abcSJeremy Kerr 	return 0;
126014c2abcSJeremy Kerr }
127014c2abcSJeremy Kerr 
128014c2abcSJeremy Kerr static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
129014c2abcSJeremy Kerr 			void *val, size_t size)
130014c2abcSJeremy Kerr {
131014c2abcSJeremy Kerr 	uint8_t id = slave->id;
132014c2abcSJeremy Kerr 	int rc;
133014c2abcSJeremy Kerr 
134014c2abcSJeremy Kerr 	rc = fsi_slave_calc_addr(slave, &addr, &id);
135014c2abcSJeremy Kerr 	if (rc)
136014c2abcSJeremy Kerr 		return rc;
137014c2abcSJeremy Kerr 
138014c2abcSJeremy Kerr 	return fsi_master_read(slave->master, slave->link, id,
139014c2abcSJeremy Kerr 			addr, val, size);
140014c2abcSJeremy Kerr }
141014c2abcSJeremy Kerr 
142014c2abcSJeremy Kerr static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
143014c2abcSJeremy Kerr 			const void *val, size_t size)
144014c2abcSJeremy Kerr {
145014c2abcSJeremy Kerr 	uint8_t id = slave->id;
146014c2abcSJeremy Kerr 	int rc;
147014c2abcSJeremy Kerr 
148014c2abcSJeremy Kerr 	rc = fsi_slave_calc_addr(slave, &addr, &id);
149014c2abcSJeremy Kerr 	if (rc)
150014c2abcSJeremy Kerr 		return rc;
151014c2abcSJeremy Kerr 
152014c2abcSJeremy Kerr 	return fsi_master_write(slave->master, slave->link, id,
153014c2abcSJeremy Kerr 			addr, val, size);
154014c2abcSJeremy Kerr }
155014c2abcSJeremy Kerr 
156f7ade2a6SJeremy Kerr static int fsi_slave_scan(struct fsi_slave *slave)
157f7ade2a6SJeremy Kerr {
158f7ade2a6SJeremy Kerr 	uint32_t engine_addr;
159f7ade2a6SJeremy Kerr 	uint32_t conf;
160f7ade2a6SJeremy Kerr 	int rc, i;
161f7ade2a6SJeremy Kerr 
162f7ade2a6SJeremy Kerr 	/*
163f7ade2a6SJeremy Kerr 	 * scan engines
164f7ade2a6SJeremy Kerr 	 *
165f7ade2a6SJeremy Kerr 	 * We keep the peek mode and slave engines for the core; so start
166f7ade2a6SJeremy Kerr 	 * at the third slot in the configuration table. We also need to
167f7ade2a6SJeremy Kerr 	 * skip the chip ID entry at the start of the address space.
168f7ade2a6SJeremy Kerr 	 */
169f7ade2a6SJeremy Kerr 	engine_addr = engine_page_size * 3;
170f7ade2a6SJeremy Kerr 	for (i = 2; i < engine_page_size / sizeof(uint32_t); i++) {
171f7ade2a6SJeremy Kerr 		uint8_t slots, version, type, crc;
172f7ade2a6SJeremy Kerr 		struct fsi_device *dev;
173f7ade2a6SJeremy Kerr 
174f7ade2a6SJeremy Kerr 		rc = fsi_slave_read(slave, (i + 1) * sizeof(conf),
175f7ade2a6SJeremy Kerr 				&conf, sizeof(conf));
176f7ade2a6SJeremy Kerr 		if (rc) {
177f7ade2a6SJeremy Kerr 			dev_warn(&slave->dev,
178f7ade2a6SJeremy Kerr 				"error reading slave registers\n");
179f7ade2a6SJeremy Kerr 			return -1;
180f7ade2a6SJeremy Kerr 		}
181f7ade2a6SJeremy Kerr 		conf = be32_to_cpu(conf);
182f7ade2a6SJeremy Kerr 
183f7ade2a6SJeremy Kerr 		crc = crc4(0, conf, 32);
184f7ade2a6SJeremy Kerr 		if (crc) {
185f7ade2a6SJeremy Kerr 			dev_warn(&slave->dev,
186f7ade2a6SJeremy Kerr 				"crc error in slave register at 0x%04x\n",
187f7ade2a6SJeremy Kerr 				i);
188f7ade2a6SJeremy Kerr 			return -1;
189f7ade2a6SJeremy Kerr 		}
190f7ade2a6SJeremy Kerr 
191f7ade2a6SJeremy Kerr 		slots = (conf & FSI_SLAVE_CONF_SLOTS_MASK)
192f7ade2a6SJeremy Kerr 			>> FSI_SLAVE_CONF_SLOTS_SHIFT;
193f7ade2a6SJeremy Kerr 		version = (conf & FSI_SLAVE_CONF_VERSION_MASK)
194f7ade2a6SJeremy Kerr 			>> FSI_SLAVE_CONF_VERSION_SHIFT;
195f7ade2a6SJeremy Kerr 		type = (conf & FSI_SLAVE_CONF_TYPE_MASK)
196f7ade2a6SJeremy Kerr 			>> FSI_SLAVE_CONF_TYPE_SHIFT;
197f7ade2a6SJeremy Kerr 
198f7ade2a6SJeremy Kerr 		/*
199f7ade2a6SJeremy Kerr 		 * Unused address areas are marked by a zero type value; this
200f7ade2a6SJeremy Kerr 		 * skips the defined address areas
201f7ade2a6SJeremy Kerr 		 */
202f7ade2a6SJeremy Kerr 		if (type != 0 && slots != 0) {
203f7ade2a6SJeremy Kerr 
204f7ade2a6SJeremy Kerr 			/* create device */
205f7ade2a6SJeremy Kerr 			dev = fsi_create_device(slave);
206f7ade2a6SJeremy Kerr 			if (!dev)
207f7ade2a6SJeremy Kerr 				return -ENOMEM;
208f7ade2a6SJeremy Kerr 
209f7ade2a6SJeremy Kerr 			dev->slave = slave;
210f7ade2a6SJeremy Kerr 			dev->engine_type = type;
211f7ade2a6SJeremy Kerr 			dev->version = version;
212f7ade2a6SJeremy Kerr 			dev->unit = i;
213f7ade2a6SJeremy Kerr 			dev->addr = engine_addr;
214f7ade2a6SJeremy Kerr 			dev->size = slots * engine_page_size;
215f7ade2a6SJeremy Kerr 
216f7ade2a6SJeremy Kerr 			dev_dbg(&slave->dev,
217f7ade2a6SJeremy Kerr 			"engine[%i]: type %x, version %x, addr %x size %x\n",
218f7ade2a6SJeremy Kerr 					dev->unit, dev->engine_type, version,
219f7ade2a6SJeremy Kerr 					dev->addr, dev->size);
220f7ade2a6SJeremy Kerr 
221f7ade2a6SJeremy Kerr 			dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x",
222f7ade2a6SJeremy Kerr 					slave->master->idx, slave->link,
223f7ade2a6SJeremy Kerr 					slave->id, i - 2);
224f7ade2a6SJeremy Kerr 
225f7ade2a6SJeremy Kerr 			rc = device_register(&dev->dev);
226f7ade2a6SJeremy Kerr 			if (rc) {
227f7ade2a6SJeremy Kerr 				dev_warn(&slave->dev, "add failed: %d\n", rc);
228f7ade2a6SJeremy Kerr 				put_device(&dev->dev);
229f7ade2a6SJeremy Kerr 			}
230f7ade2a6SJeremy Kerr 		}
231f7ade2a6SJeremy Kerr 
232f7ade2a6SJeremy Kerr 		engine_addr += slots * engine_page_size;
233f7ade2a6SJeremy Kerr 
234f7ade2a6SJeremy Kerr 		if (!(conf & FSI_SLAVE_CONF_NEXT_MASK))
235f7ade2a6SJeremy Kerr 			break;
236f7ade2a6SJeremy Kerr 	}
237f7ade2a6SJeremy Kerr 
238f7ade2a6SJeremy Kerr 	return 0;
239f7ade2a6SJeremy Kerr }
240f7ade2a6SJeremy Kerr 
2412b37c3e2SChristopher Bostic /* Encode slave local bus echo delay */
2422b37c3e2SChristopher Bostic static inline uint32_t fsi_smode_echodly(int x)
2432b37c3e2SChristopher Bostic {
2442b37c3e2SChristopher Bostic 	return (x & FSI_SMODE_ED_MASK) << FSI_SMODE_ED_SHIFT;
2452b37c3e2SChristopher Bostic }
2462b37c3e2SChristopher Bostic 
2472b37c3e2SChristopher Bostic /* Encode slave local bus send delay */
2482b37c3e2SChristopher Bostic static inline uint32_t fsi_smode_senddly(int x)
2492b37c3e2SChristopher Bostic {
2502b37c3e2SChristopher Bostic 	return (x & FSI_SMODE_SD_MASK) << FSI_SMODE_SD_SHIFT;
2512b37c3e2SChristopher Bostic }
2522b37c3e2SChristopher Bostic 
2532b37c3e2SChristopher Bostic /* Encode slave local bus clock rate ratio */
2542b37c3e2SChristopher Bostic static inline uint32_t fsi_smode_lbcrr(int x)
2552b37c3e2SChristopher Bostic {
2562b37c3e2SChristopher Bostic 	return (x & FSI_SMODE_LBCRR_MASK) << FSI_SMODE_LBCRR_SHIFT;
2572b37c3e2SChristopher Bostic }
2582b37c3e2SChristopher Bostic 
2592b37c3e2SChristopher Bostic /* Encode slave ID */
2602b37c3e2SChristopher Bostic static inline uint32_t fsi_smode_sid(int x)
2612b37c3e2SChristopher Bostic {
2622b37c3e2SChristopher Bostic 	return (x & FSI_SMODE_SID_MASK) << FSI_SMODE_SID_SHIFT;
2632b37c3e2SChristopher Bostic }
2642b37c3e2SChristopher Bostic 
2652b37c3e2SChristopher Bostic static const uint32_t fsi_slave_smode(int id)
2662b37c3e2SChristopher Bostic {
2672b37c3e2SChristopher Bostic 	return FSI_SMODE_WSC | FSI_SMODE_ECRC
2682b37c3e2SChristopher Bostic 		| fsi_smode_sid(id)
2692b37c3e2SChristopher Bostic 		| fsi_smode_echodly(0xf) | fsi_smode_senddly(0xf)
2702b37c3e2SChristopher Bostic 		| fsi_smode_lbcrr(0x8);
2712b37c3e2SChristopher Bostic }
2722b37c3e2SChristopher Bostic 
2732b37c3e2SChristopher Bostic static int fsi_slave_set_smode(struct fsi_master *master, int link, int id)
2742b37c3e2SChristopher Bostic {
2752b37c3e2SChristopher Bostic 	uint32_t smode;
2762b37c3e2SChristopher Bostic 
2772b37c3e2SChristopher Bostic 	/* set our smode register with the slave ID field to 0; this enables
2782b37c3e2SChristopher Bostic 	 * extended slave addressing
2792b37c3e2SChristopher Bostic 	 */
2802b37c3e2SChristopher Bostic 	smode = fsi_slave_smode(id);
2812b37c3e2SChristopher Bostic 	smode = cpu_to_be32(smode);
2822b37c3e2SChristopher Bostic 
2832b37c3e2SChristopher Bostic 	return fsi_master_write(master, link, id, FSI_SLAVE_BASE + FSI_SMODE,
2842b37c3e2SChristopher Bostic 			&smode, sizeof(smode));
2852b37c3e2SChristopher Bostic }
2862b37c3e2SChristopher Bostic 
2872b545cd8SJeremy Kerr static void fsi_slave_release(struct device *dev)
2882b545cd8SJeremy Kerr {
2892b545cd8SJeremy Kerr 	struct fsi_slave *slave = to_fsi_slave(dev);
2902b545cd8SJeremy Kerr 
2912b545cd8SJeremy Kerr 	kfree(slave);
2922b545cd8SJeremy Kerr }
2932b545cd8SJeremy Kerr 
294414c1026SJeremy Kerr static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
295414c1026SJeremy Kerr {
2962b545cd8SJeremy Kerr 	struct fsi_slave *slave;
2972b545cd8SJeremy Kerr 	uint32_t chip_id;
2982b545cd8SJeremy Kerr 	uint8_t crc;
2992b545cd8SJeremy Kerr 	int rc;
300414c1026SJeremy Kerr 
3012b545cd8SJeremy Kerr 	/* Currently, we only support single slaves on a link, and use the
3022b545cd8SJeremy Kerr 	 * full 23-bit address range
3032b545cd8SJeremy Kerr 	 */
3042b545cd8SJeremy Kerr 	if (id != 0)
3052b545cd8SJeremy Kerr 		return -EINVAL;
3062b545cd8SJeremy Kerr 
3072b545cd8SJeremy Kerr 	rc = fsi_master_read(master, link, id, 0, &chip_id, sizeof(chip_id));
3082b545cd8SJeremy Kerr 	if (rc) {
3092b545cd8SJeremy Kerr 		dev_dbg(&master->dev, "can't read slave %02x:%02x %d\n",
3102b545cd8SJeremy Kerr 				link, id, rc);
311414c1026SJeremy Kerr 		return -ENODEV;
312414c1026SJeremy Kerr 	}
3132b545cd8SJeremy Kerr 	chip_id = be32_to_cpu(chip_id);
3142b545cd8SJeremy Kerr 
3152b545cd8SJeremy Kerr 	crc = crc4(0, chip_id, 32);
3162b545cd8SJeremy Kerr 	if (crc) {
3172b545cd8SJeremy Kerr 		dev_warn(&master->dev, "slave %02x:%02x invalid chip id CRC!\n",
3182b545cd8SJeremy Kerr 				link, id);
3192b545cd8SJeremy Kerr 		return -EIO;
3202b545cd8SJeremy Kerr 	}
3212b545cd8SJeremy Kerr 
3222b545cd8SJeremy Kerr 	dev_info(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n",
3232b545cd8SJeremy Kerr 			chip_id, master->idx, link, id);
3242b545cd8SJeremy Kerr 
3252b37c3e2SChristopher Bostic 	rc = fsi_slave_set_smode(master, link, id);
3262b37c3e2SChristopher Bostic 	if (rc) {
3272b37c3e2SChristopher Bostic 		dev_warn(&master->dev,
3282b37c3e2SChristopher Bostic 				"can't set smode on slave:%02x:%02x %d\n",
3292b37c3e2SChristopher Bostic 				link, id, rc);
3302b37c3e2SChristopher Bostic 		return -ENODEV;
3312b37c3e2SChristopher Bostic 	}
3322b37c3e2SChristopher Bostic 
3332b545cd8SJeremy Kerr 	/* We can communicate with a slave; create the slave device and
3342b545cd8SJeremy Kerr 	 * register.
3352b545cd8SJeremy Kerr 	 */
3362b545cd8SJeremy Kerr 	slave = kzalloc(sizeof(*slave), GFP_KERNEL);
3372b545cd8SJeremy Kerr 	if (!slave)
3382b545cd8SJeremy Kerr 		return -ENOMEM;
3392b545cd8SJeremy Kerr 
3402b545cd8SJeremy Kerr 	slave->master = master;
3412b545cd8SJeremy Kerr 	slave->dev.parent = &master->dev;
3422b545cd8SJeremy Kerr 	slave->dev.release = fsi_slave_release;
3432b545cd8SJeremy Kerr 	slave->link = link;
3442b545cd8SJeremy Kerr 	slave->id = id;
3452b545cd8SJeremy Kerr 	slave->size = FSI_SLAVE_SIZE_23b;
3462b545cd8SJeremy Kerr 
3472b545cd8SJeremy Kerr 	dev_set_name(&slave->dev, "slave@%02x:%02x", link, id);
3482b545cd8SJeremy Kerr 	rc = device_register(&slave->dev);
3492b545cd8SJeremy Kerr 	if (rc < 0) {
3502b545cd8SJeremy Kerr 		dev_warn(&master->dev, "failed to create slave device: %d\n",
3512b545cd8SJeremy Kerr 				rc);
3522b545cd8SJeremy Kerr 		put_device(&slave->dev);
3532b545cd8SJeremy Kerr 		return rc;
3542b545cd8SJeremy Kerr 	}
3552b545cd8SJeremy Kerr 
356f7ade2a6SJeremy Kerr 	rc = fsi_slave_scan(slave);
357f7ade2a6SJeremy Kerr 	if (rc)
358f7ade2a6SJeremy Kerr 		dev_dbg(&master->dev, "failed during slave scan with: %d\n",
359f7ade2a6SJeremy Kerr 				rc);
3602b545cd8SJeremy Kerr 
3612b545cd8SJeremy Kerr 	return rc;
3622b545cd8SJeremy Kerr }
363414c1026SJeremy Kerr 
36409aecfabSJeremy Kerr /* FSI master support */
365014c2abcSJeremy Kerr static int fsi_check_access(uint32_t addr, size_t size)
366014c2abcSJeremy Kerr {
367014c2abcSJeremy Kerr 	if (size != 1 && size != 2 && size != 4)
368014c2abcSJeremy Kerr 		return -EINVAL;
369014c2abcSJeremy Kerr 
370014c2abcSJeremy Kerr 	if ((addr & 0x3) != (size & 0x3))
371014c2abcSJeremy Kerr 		return -EINVAL;
372014c2abcSJeremy Kerr 
373014c2abcSJeremy Kerr 	return 0;
374014c2abcSJeremy Kerr }
375014c2abcSJeremy Kerr 
376014c2abcSJeremy Kerr static int fsi_master_read(struct fsi_master *master, int link,
377014c2abcSJeremy Kerr 		uint8_t slave_id, uint32_t addr, void *val, size_t size)
378014c2abcSJeremy Kerr {
379014c2abcSJeremy Kerr 	int rc;
380014c2abcSJeremy Kerr 
381014c2abcSJeremy Kerr 	rc = fsi_check_access(addr, size);
382014c2abcSJeremy Kerr 	if (rc)
383014c2abcSJeremy Kerr 		return rc;
384014c2abcSJeremy Kerr 
385014c2abcSJeremy Kerr 	return master->read(master, link, slave_id, addr, val, size);
386014c2abcSJeremy Kerr }
387014c2abcSJeremy Kerr 
388014c2abcSJeremy Kerr static int fsi_master_write(struct fsi_master *master, int link,
389014c2abcSJeremy Kerr 		uint8_t slave_id, uint32_t addr, const void *val, size_t size)
390014c2abcSJeremy Kerr {
391014c2abcSJeremy Kerr 	int rc;
392014c2abcSJeremy Kerr 
393014c2abcSJeremy Kerr 	rc = fsi_check_access(addr, size);
394014c2abcSJeremy Kerr 	if (rc)
395014c2abcSJeremy Kerr 		return rc;
396014c2abcSJeremy Kerr 
397014c2abcSJeremy Kerr 	return master->write(master, link, slave_id, addr, val, size);
398014c2abcSJeremy Kerr }
399014c2abcSJeremy Kerr 
40026095282SChristopher Bostic static int fsi_master_link_enable(struct fsi_master *master, int link)
40126095282SChristopher Bostic {
40226095282SChristopher Bostic 	if (master->link_enable)
40326095282SChristopher Bostic 		return master->link_enable(master, link);
40426095282SChristopher Bostic 
40526095282SChristopher Bostic 	return 0;
40626095282SChristopher Bostic }
40726095282SChristopher Bostic 
40826095282SChristopher Bostic /*
40926095282SChristopher Bostic  * Issue a break command on this link
41026095282SChristopher Bostic  */
41126095282SChristopher Bostic static int fsi_master_break(struct fsi_master *master, int link)
41226095282SChristopher Bostic {
41326095282SChristopher Bostic 	if (master->send_break)
41426095282SChristopher Bostic 		return master->send_break(master, link);
41526095282SChristopher Bostic 
41626095282SChristopher Bostic 	return 0;
41726095282SChristopher Bostic }
41826095282SChristopher Bostic 
419414c1026SJeremy Kerr static int fsi_master_scan(struct fsi_master *master)
420414c1026SJeremy Kerr {
42126095282SChristopher Bostic 	int link, rc;
422414c1026SJeremy Kerr 
42326095282SChristopher Bostic 	for (link = 0; link < master->n_links; link++) {
42426095282SChristopher Bostic 		rc = fsi_master_link_enable(master, link);
42526095282SChristopher Bostic 		if (rc) {
42626095282SChristopher Bostic 			dev_dbg(&master->dev,
42726095282SChristopher Bostic 				"enable link %d failed: %d\n", link, rc);
42826095282SChristopher Bostic 			continue;
42926095282SChristopher Bostic 		}
43026095282SChristopher Bostic 		rc = fsi_master_break(master, link);
43126095282SChristopher Bostic 		if (rc) {
43226095282SChristopher Bostic 			dev_dbg(&master->dev,
43326095282SChristopher Bostic 				"break to link %d failed: %d\n", link, rc);
43426095282SChristopher Bostic 			continue;
43526095282SChristopher Bostic 		}
43626095282SChristopher Bostic 
437414c1026SJeremy Kerr 		fsi_slave_init(master, link, 0);
43826095282SChristopher Bostic 	}
439414c1026SJeremy Kerr 
440414c1026SJeremy Kerr 	return 0;
441414c1026SJeremy Kerr }
442414c1026SJeremy Kerr 
44309aecfabSJeremy Kerr int fsi_master_register(struct fsi_master *master)
44409aecfabSJeremy Kerr {
44509aecfabSJeremy Kerr 	int rc;
44609aecfabSJeremy Kerr 
44709aecfabSJeremy Kerr 	if (!master)
44809aecfabSJeremy Kerr 		return -EINVAL;
44909aecfabSJeremy Kerr 
45009aecfabSJeremy Kerr 	master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL);
45109aecfabSJeremy Kerr 	dev_set_name(&master->dev, "fsi%d", master->idx);
45209aecfabSJeremy Kerr 
45309aecfabSJeremy Kerr 	rc = device_register(&master->dev);
454414c1026SJeremy Kerr 	if (rc) {
45509aecfabSJeremy Kerr 		ida_simple_remove(&master_ida, master->idx);
45609aecfabSJeremy Kerr 		return rc;
45709aecfabSJeremy Kerr 	}
458414c1026SJeremy Kerr 
459414c1026SJeremy Kerr 	fsi_master_scan(master);
460414c1026SJeremy Kerr 	return 0;
461414c1026SJeremy Kerr }
46209aecfabSJeremy Kerr EXPORT_SYMBOL_GPL(fsi_master_register);
46309aecfabSJeremy Kerr 
46409aecfabSJeremy Kerr void fsi_master_unregister(struct fsi_master *master)
46509aecfabSJeremy Kerr {
46609aecfabSJeremy Kerr 	if (master->idx >= 0) {
46709aecfabSJeremy Kerr 		ida_simple_remove(&master_ida, master->idx);
46809aecfabSJeremy Kerr 		master->idx = -1;
46909aecfabSJeremy Kerr 	}
47009aecfabSJeremy Kerr 
47109aecfabSJeremy Kerr 	device_unregister(&master->dev);
47209aecfabSJeremy Kerr }
47309aecfabSJeremy Kerr EXPORT_SYMBOL_GPL(fsi_master_unregister);
47409aecfabSJeremy Kerr 
4750508ad1fSJeremy Kerr /* FSI core & Linux bus type definitions */
4760508ad1fSJeremy Kerr 
477dd37eed7SJeremy Kerr static int fsi_bus_match(struct device *dev, struct device_driver *drv)
478dd37eed7SJeremy Kerr {
479dd37eed7SJeremy Kerr 	struct fsi_device *fsi_dev = to_fsi_dev(dev);
480dd37eed7SJeremy Kerr 	struct fsi_driver *fsi_drv = to_fsi_drv(drv);
481dd37eed7SJeremy Kerr 	const struct fsi_device_id *id;
482dd37eed7SJeremy Kerr 
483dd37eed7SJeremy Kerr 	if (!fsi_drv->id_table)
484dd37eed7SJeremy Kerr 		return 0;
485dd37eed7SJeremy Kerr 
486dd37eed7SJeremy Kerr 	for (id = fsi_drv->id_table; id->engine_type; id++) {
487dd37eed7SJeremy Kerr 		if (id->engine_type != fsi_dev->engine_type)
488dd37eed7SJeremy Kerr 			continue;
489dd37eed7SJeremy Kerr 		if (id->version == FSI_VERSION_ANY ||
490dd37eed7SJeremy Kerr 				id->version == fsi_dev->version)
491dd37eed7SJeremy Kerr 			return 1;
492dd37eed7SJeremy Kerr 	}
493dd37eed7SJeremy Kerr 
494dd37eed7SJeremy Kerr 	return 0;
495dd37eed7SJeremy Kerr }
496dd37eed7SJeremy Kerr 
4970508ad1fSJeremy Kerr struct bus_type fsi_bus_type = {
4980508ad1fSJeremy Kerr 	.name		= "fsi",
499dd37eed7SJeremy Kerr 	.match		= fsi_bus_match,
5000508ad1fSJeremy Kerr };
5010508ad1fSJeremy Kerr EXPORT_SYMBOL_GPL(fsi_bus_type);
5020508ad1fSJeremy Kerr 
5030508ad1fSJeremy Kerr static int fsi_init(void)
5040508ad1fSJeremy Kerr {
5050508ad1fSJeremy Kerr 	return bus_register(&fsi_bus_type);
5060508ad1fSJeremy Kerr }
5070508ad1fSJeremy Kerr 
5080508ad1fSJeremy Kerr static void fsi_exit(void)
5090508ad1fSJeremy Kerr {
5100508ad1fSJeremy Kerr 	bus_unregister(&fsi_bus_type);
5110508ad1fSJeremy Kerr }
5120508ad1fSJeremy Kerr 
5130508ad1fSJeremy Kerr module_init(fsi_init);
5140508ad1fSJeremy Kerr module_exit(fsi_exit);
515