18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * FSI core driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) IBM Corporation 2016
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * TODO:
88c2ecf20Sopenharmony_ci *  - Rework topology
98c2ecf20Sopenharmony_ci *  - s/chip_id/chip_loc
108c2ecf20Sopenharmony_ci *  - s/cfam/chip (cfam_id -> chip_id etc...)
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/crc4.h>
148c2ecf20Sopenharmony_ci#include <linux/device.h>
158c2ecf20Sopenharmony_ci#include <linux/fsi.h>
168c2ecf20Sopenharmony_ci#include <linux/idr.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/of.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/bitops.h>
218c2ecf20Sopenharmony_ci#include <linux/cdev.h>
228c2ecf20Sopenharmony_ci#include <linux/fs.h>
238c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "fsi-master.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS
288c2ecf20Sopenharmony_ci#include <trace/events/fsi.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_NEXT_MASK	GENMASK(31, 31)
318c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_SLOTS_MASK	GENMASK(23, 16)
328c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_SLOTS_SHIFT	16
338c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_VERSION_MASK	GENMASK(15, 12)
348c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_VERSION_SHIFT	12
358c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_TYPE_MASK	GENMASK(11, 4)
368c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_TYPE_SHIFT	4
378c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_CRC_SHIFT	4
388c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_CRC_MASK		GENMASK(3, 0)
398c2ecf20Sopenharmony_ci#define FSI_SLAVE_CONF_DATA_BITS	28
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define FSI_PEEK_BASE			0x410
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic const int engine_page_size = 0x400;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define FSI_SLAVE_BASE			0x800
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/*
488c2ecf20Sopenharmony_ci * FSI slave engine control register offsets
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_ci#define FSI_SMODE		0x0	/* R/W: Mode register */
518c2ecf20Sopenharmony_ci#define FSI_SISC		0x8	/* R/W: Interrupt condition */
528c2ecf20Sopenharmony_ci#define FSI_SSTAT		0x14	/* R  : Slave status */
538c2ecf20Sopenharmony_ci#define FSI_SLBUS		0x30	/* W  : LBUS Ownership */
548c2ecf20Sopenharmony_ci#define FSI_LLMODE		0x100	/* R/W: Link layer mode register */
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * SMODE fields
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_ci#define FSI_SMODE_WSC		0x80000000	/* Warm start done */
608c2ecf20Sopenharmony_ci#define FSI_SMODE_ECRC		0x20000000	/* Hw CRC check */
618c2ecf20Sopenharmony_ci#define FSI_SMODE_SID_SHIFT	24		/* ID shift */
628c2ecf20Sopenharmony_ci#define FSI_SMODE_SID_MASK	3		/* ID Mask */
638c2ecf20Sopenharmony_ci#define FSI_SMODE_ED_SHIFT	20		/* Echo delay shift */
648c2ecf20Sopenharmony_ci#define FSI_SMODE_ED_MASK	0xf		/* Echo delay mask */
658c2ecf20Sopenharmony_ci#define FSI_SMODE_SD_SHIFT	16		/* Send delay shift */
668c2ecf20Sopenharmony_ci#define FSI_SMODE_SD_MASK	0xf		/* Send delay mask */
678c2ecf20Sopenharmony_ci#define FSI_SMODE_LBCRR_SHIFT	8		/* Clk ratio shift */
688c2ecf20Sopenharmony_ci#define FSI_SMODE_LBCRR_MASK	0xf		/* Clk ratio mask */
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/*
718c2ecf20Sopenharmony_ci * SLBUS fields
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_ci#define FSI_SLBUS_FORCE		0x80000000	/* Force LBUS ownership */
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/*
768c2ecf20Sopenharmony_ci * LLMODE fields
778c2ecf20Sopenharmony_ci */
788c2ecf20Sopenharmony_ci#define FSI_LLMODE_ASYNC	0x1
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define FSI_SLAVE_SIZE_23b		0x800000
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic DEFINE_IDA(master_ida);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistruct fsi_slave {
858c2ecf20Sopenharmony_ci	struct device		dev;
868c2ecf20Sopenharmony_ci	struct fsi_master	*master;
878c2ecf20Sopenharmony_ci	struct cdev		cdev;
888c2ecf20Sopenharmony_ci	int			cdev_idx;
898c2ecf20Sopenharmony_ci	int			id;	/* FSI address */
908c2ecf20Sopenharmony_ci	int			link;	/* FSI link# */
918c2ecf20Sopenharmony_ci	u32			cfam_id;
928c2ecf20Sopenharmony_ci	int			chip_id;
938c2ecf20Sopenharmony_ci	uint32_t		size;	/* size of slave address space */
948c2ecf20Sopenharmony_ci	u8			t_send_delay;
958c2ecf20Sopenharmony_ci	u8			t_echo_delay;
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci#define to_fsi_master(d) container_of(d, struct fsi_master, dev)
998c2ecf20Sopenharmony_ci#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic const int slave_retries = 2;
1028c2ecf20Sopenharmony_cistatic int discard_errors;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic dev_t fsi_base_dev;
1058c2ecf20Sopenharmony_cistatic DEFINE_IDA(fsi_minor_ida);
1068c2ecf20Sopenharmony_ci#define FSI_CHAR_MAX_DEVICES	0x1000
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/* Legacy /dev numbering: 4 devices per chip, 16 chips */
1098c2ecf20Sopenharmony_ci#define FSI_CHAR_LEGACY_TOP	64
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int fsi_master_read(struct fsi_master *master, int link,
1128c2ecf20Sopenharmony_ci		uint8_t slave_id, uint32_t addr, void *val, size_t size);
1138c2ecf20Sopenharmony_cistatic int fsi_master_write(struct fsi_master *master, int link,
1148c2ecf20Sopenharmony_ci		uint8_t slave_id, uint32_t addr, const void *val, size_t size);
1158c2ecf20Sopenharmony_cistatic int fsi_master_break(struct fsi_master *master, int link);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/*
1188c2ecf20Sopenharmony_ci * fsi_device_read() / fsi_device_write() / fsi_device_peek()
1198c2ecf20Sopenharmony_ci *
1208c2ecf20Sopenharmony_ci * FSI endpoint-device support
1218c2ecf20Sopenharmony_ci *
1228c2ecf20Sopenharmony_ci * Read / write / peek accessors for a client
1238c2ecf20Sopenharmony_ci *
1248c2ecf20Sopenharmony_ci * Parameters:
1258c2ecf20Sopenharmony_ci * dev:  Structure passed to FSI client device drivers on probe().
1268c2ecf20Sopenharmony_ci * addr: FSI address of given device.  Client should pass in its base address
1278c2ecf20Sopenharmony_ci *       plus desired offset to access its register space.
1288c2ecf20Sopenharmony_ci * val:  For read/peek this is the value read at the specified address. For
1298c2ecf20Sopenharmony_ci *       write this is value to write to the specified address.
1308c2ecf20Sopenharmony_ci *       The data in val must be FSI bus endian (big endian).
1318c2ecf20Sopenharmony_ci * size: Size in bytes of the operation.  Sizes supported are 1, 2 and 4 bytes.
1328c2ecf20Sopenharmony_ci *       Addresses must be aligned on size boundaries or an error will result.
1338c2ecf20Sopenharmony_ci */
1348c2ecf20Sopenharmony_ciint fsi_device_read(struct fsi_device *dev, uint32_t addr, void *val,
1358c2ecf20Sopenharmony_ci		size_t size)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	if (addr > dev->size || size > dev->size || addr > dev->size - size)
1388c2ecf20Sopenharmony_ci		return -EINVAL;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return fsi_slave_read(dev->slave, dev->addr + addr, val, size);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_device_read);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ciint fsi_device_write(struct fsi_device *dev, uint32_t addr, const void *val,
1458c2ecf20Sopenharmony_ci		size_t size)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	if (addr > dev->size || size > dev->size || addr > dev->size - size)
1488c2ecf20Sopenharmony_ci		return -EINVAL;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return fsi_slave_write(dev->slave, dev->addr + addr, val, size);
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_device_write);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ciint fsi_device_peek(struct fsi_device *dev, void *val)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	uint32_t addr = FSI_PEEK_BASE + ((dev->unit - 2) * sizeof(uint32_t));
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	return fsi_slave_read(dev->slave, addr, val, sizeof(uint32_t));
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic void fsi_device_release(struct device *_device)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	struct fsi_device *device = to_fsi_dev(_device);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	of_node_put(device->dev.of_node);
1668c2ecf20Sopenharmony_ci	kfree(device);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic struct fsi_device *fsi_create_device(struct fsi_slave *slave)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct fsi_device *dev;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1748c2ecf20Sopenharmony_ci	if (!dev)
1758c2ecf20Sopenharmony_ci		return NULL;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	dev->dev.parent = &slave->dev;
1788c2ecf20Sopenharmony_ci	dev->dev.bus = &fsi_bus_type;
1798c2ecf20Sopenharmony_ci	dev->dev.release = fsi_device_release;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return dev;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci/* FSI slave support */
1858c2ecf20Sopenharmony_cistatic int fsi_slave_calc_addr(struct fsi_slave *slave, uint32_t *addrp,
1868c2ecf20Sopenharmony_ci		uint8_t *idp)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	uint32_t addr = *addrp;
1898c2ecf20Sopenharmony_ci	uint8_t id = *idp;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (addr > slave->size)
1928c2ecf20Sopenharmony_ci		return -EINVAL;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/* For 23 bit addressing, we encode the extra two bits in the slave
1958c2ecf20Sopenharmony_ci	 * id (and the slave's actual ID needs to be 0).
1968c2ecf20Sopenharmony_ci	 */
1978c2ecf20Sopenharmony_ci	if (addr > 0x1fffff) {
1988c2ecf20Sopenharmony_ci		if (slave->id != 0)
1998c2ecf20Sopenharmony_ci			return -EINVAL;
2008c2ecf20Sopenharmony_ci		id = (addr >> 21) & 0x3;
2018c2ecf20Sopenharmony_ci		addr &= 0x1fffff;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	*addrp = addr;
2058c2ecf20Sopenharmony_ci	*idp = id;
2068c2ecf20Sopenharmony_ci	return 0;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic int fsi_slave_report_and_clear_errors(struct fsi_slave *slave)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct fsi_master *master = slave->master;
2128c2ecf20Sopenharmony_ci	__be32 irq, stat;
2138c2ecf20Sopenharmony_ci	int rc, link;
2148c2ecf20Sopenharmony_ci	uint8_t id;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	link = slave->link;
2178c2ecf20Sopenharmony_ci	id = slave->id;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	rc = fsi_master_read(master, link, id, FSI_SLAVE_BASE + FSI_SISC,
2208c2ecf20Sopenharmony_ci			&irq, sizeof(irq));
2218c2ecf20Sopenharmony_ci	if (rc)
2228c2ecf20Sopenharmony_ci		return rc;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	rc =  fsi_master_read(master, link, id, FSI_SLAVE_BASE + FSI_SSTAT,
2258c2ecf20Sopenharmony_ci			&stat, sizeof(stat));
2268c2ecf20Sopenharmony_ci	if (rc)
2278c2ecf20Sopenharmony_ci		return rc;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	dev_dbg(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n",
2308c2ecf20Sopenharmony_ci			be32_to_cpu(stat), be32_to_cpu(irq));
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/* clear interrupts */
2338c2ecf20Sopenharmony_ci	return fsi_master_write(master, link, id, FSI_SLAVE_BASE + FSI_SISC,
2348c2ecf20Sopenharmony_ci			&irq, sizeof(irq));
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/* Encode slave local bus echo delay */
2388c2ecf20Sopenharmony_cistatic inline uint32_t fsi_smode_echodly(int x)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	return (x & FSI_SMODE_ED_MASK) << FSI_SMODE_ED_SHIFT;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci/* Encode slave local bus send delay */
2448c2ecf20Sopenharmony_cistatic inline uint32_t fsi_smode_senddly(int x)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	return (x & FSI_SMODE_SD_MASK) << FSI_SMODE_SD_SHIFT;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci/* Encode slave local bus clock rate ratio */
2508c2ecf20Sopenharmony_cistatic inline uint32_t fsi_smode_lbcrr(int x)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	return (x & FSI_SMODE_LBCRR_MASK) << FSI_SMODE_LBCRR_SHIFT;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci/* Encode slave ID */
2568c2ecf20Sopenharmony_cistatic inline uint32_t fsi_smode_sid(int x)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	return (x & FSI_SMODE_SID_MASK) << FSI_SMODE_SID_SHIFT;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic uint32_t fsi_slave_smode(int id, u8 t_senddly, u8 t_echodly)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	return FSI_SMODE_WSC | FSI_SMODE_ECRC
2648c2ecf20Sopenharmony_ci		| fsi_smode_sid(id)
2658c2ecf20Sopenharmony_ci		| fsi_smode_echodly(t_echodly - 1) | fsi_smode_senddly(t_senddly - 1)
2668c2ecf20Sopenharmony_ci		| fsi_smode_lbcrr(0x8);
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int fsi_slave_set_smode(struct fsi_slave *slave)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	uint32_t smode;
2728c2ecf20Sopenharmony_ci	__be32 data;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/* set our smode register with the slave ID field to 0; this enables
2758c2ecf20Sopenharmony_ci	 * extended slave addressing
2768c2ecf20Sopenharmony_ci	 */
2778c2ecf20Sopenharmony_ci	smode = fsi_slave_smode(slave->id, slave->t_send_delay, slave->t_echo_delay);
2788c2ecf20Sopenharmony_ci	data = cpu_to_be32(smode);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return fsi_master_write(slave->master, slave->link, slave->id,
2818c2ecf20Sopenharmony_ci				FSI_SLAVE_BASE + FSI_SMODE,
2828c2ecf20Sopenharmony_ci				&data, sizeof(data));
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic int fsi_slave_handle_error(struct fsi_slave *slave, bool write,
2868c2ecf20Sopenharmony_ci				  uint32_t addr, size_t size)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	struct fsi_master *master = slave->master;
2898c2ecf20Sopenharmony_ci	int rc, link;
2908c2ecf20Sopenharmony_ci	uint32_t reg;
2918c2ecf20Sopenharmony_ci	uint8_t id, send_delay, echo_delay;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	if (discard_errors)
2948c2ecf20Sopenharmony_ci		return -1;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	link = slave->link;
2978c2ecf20Sopenharmony_ci	id = slave->id;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	dev_dbg(&slave->dev, "handling error on %s to 0x%08x[%zd]",
3008c2ecf20Sopenharmony_ci			write ? "write" : "read", addr, size);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* try a simple clear of error conditions, which may fail if we've lost
3038c2ecf20Sopenharmony_ci	 * communication with the slave
3048c2ecf20Sopenharmony_ci	 */
3058c2ecf20Sopenharmony_ci	rc = fsi_slave_report_and_clear_errors(slave);
3068c2ecf20Sopenharmony_ci	if (!rc)
3078c2ecf20Sopenharmony_ci		return 0;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* send a TERM and retry */
3108c2ecf20Sopenharmony_ci	if (master->term) {
3118c2ecf20Sopenharmony_ci		rc = master->term(master, link, id);
3128c2ecf20Sopenharmony_ci		if (!rc) {
3138c2ecf20Sopenharmony_ci			rc = fsi_master_read(master, link, id, 0,
3148c2ecf20Sopenharmony_ci					&reg, sizeof(reg));
3158c2ecf20Sopenharmony_ci			if (!rc)
3168c2ecf20Sopenharmony_ci				rc = fsi_slave_report_and_clear_errors(slave);
3178c2ecf20Sopenharmony_ci			if (!rc)
3188c2ecf20Sopenharmony_ci				return 0;
3198c2ecf20Sopenharmony_ci		}
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	send_delay = slave->t_send_delay;
3238c2ecf20Sopenharmony_ci	echo_delay = slave->t_echo_delay;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	/* getting serious, reset the slave via BREAK */
3268c2ecf20Sopenharmony_ci	rc = fsi_master_break(master, link);
3278c2ecf20Sopenharmony_ci	if (rc)
3288c2ecf20Sopenharmony_ci		return rc;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	slave->t_send_delay = send_delay;
3318c2ecf20Sopenharmony_ci	slave->t_echo_delay = echo_delay;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	rc = fsi_slave_set_smode(slave);
3348c2ecf20Sopenharmony_ci	if (rc)
3358c2ecf20Sopenharmony_ci		return rc;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (master->link_config)
3388c2ecf20Sopenharmony_ci		master->link_config(master, link,
3398c2ecf20Sopenharmony_ci				    slave->t_send_delay,
3408c2ecf20Sopenharmony_ci				    slave->t_echo_delay);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	return fsi_slave_report_and_clear_errors(slave);
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ciint fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
3468c2ecf20Sopenharmony_ci			void *val, size_t size)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	uint8_t id = slave->id;
3498c2ecf20Sopenharmony_ci	int rc, err_rc, i;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	rc = fsi_slave_calc_addr(slave, &addr, &id);
3528c2ecf20Sopenharmony_ci	if (rc)
3538c2ecf20Sopenharmony_ci		return rc;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	for (i = 0; i < slave_retries; i++) {
3568c2ecf20Sopenharmony_ci		rc = fsi_master_read(slave->master, slave->link,
3578c2ecf20Sopenharmony_ci				id, addr, val, size);
3588c2ecf20Sopenharmony_ci		if (!rc)
3598c2ecf20Sopenharmony_ci			break;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		err_rc = fsi_slave_handle_error(slave, false, addr, size);
3628c2ecf20Sopenharmony_ci		if (err_rc)
3638c2ecf20Sopenharmony_ci			break;
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	return rc;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_slave_read);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ciint fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
3718c2ecf20Sopenharmony_ci			const void *val, size_t size)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	uint8_t id = slave->id;
3748c2ecf20Sopenharmony_ci	int rc, err_rc, i;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	rc = fsi_slave_calc_addr(slave, &addr, &id);
3778c2ecf20Sopenharmony_ci	if (rc)
3788c2ecf20Sopenharmony_ci		return rc;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	for (i = 0; i < slave_retries; i++) {
3818c2ecf20Sopenharmony_ci		rc = fsi_master_write(slave->master, slave->link,
3828c2ecf20Sopenharmony_ci				id, addr, val, size);
3838c2ecf20Sopenharmony_ci		if (!rc)
3848c2ecf20Sopenharmony_ci			break;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		err_rc = fsi_slave_handle_error(slave, true, addr, size);
3878c2ecf20Sopenharmony_ci		if (err_rc)
3888c2ecf20Sopenharmony_ci			break;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return rc;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_slave_write);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ciextern int fsi_slave_claim_range(struct fsi_slave *slave,
3968c2ecf20Sopenharmony_ci		uint32_t addr, uint32_t size)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	if (addr + size < addr)
3998c2ecf20Sopenharmony_ci		return -EINVAL;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	if (addr + size > slave->size)
4028c2ecf20Sopenharmony_ci		return -EINVAL;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/* todo: check for overlapping claims */
4058c2ecf20Sopenharmony_ci	return 0;
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_slave_claim_range);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ciextern void fsi_slave_release_range(struct fsi_slave *slave,
4108c2ecf20Sopenharmony_ci		uint32_t addr, uint32_t size)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_slave_release_range);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic bool fsi_device_node_matches(struct device *dev, struct device_node *np,
4168c2ecf20Sopenharmony_ci		uint32_t addr, uint32_t size)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	unsigned int len, na, ns;
4198c2ecf20Sopenharmony_ci	const __be32 *prop;
4208c2ecf20Sopenharmony_ci	uint32_t psize;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	na = of_n_addr_cells(np);
4238c2ecf20Sopenharmony_ci	ns = of_n_size_cells(np);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (na != 1 || ns != 1)
4268c2ecf20Sopenharmony_ci		return false;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	prop = of_get_property(np, "reg", &len);
4298c2ecf20Sopenharmony_ci	if (!prop || len != 8)
4308c2ecf20Sopenharmony_ci		return false;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	if (of_read_number(prop, 1) != addr)
4338c2ecf20Sopenharmony_ci		return false;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	psize = of_read_number(prop + 1, 1);
4368c2ecf20Sopenharmony_ci	if (psize != size) {
4378c2ecf20Sopenharmony_ci		dev_warn(dev,
4388c2ecf20Sopenharmony_ci			"node %s matches probed address, but not size (got 0x%x, expected 0x%x)",
4398c2ecf20Sopenharmony_ci			of_node_full_name(np), psize, size);
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	return true;
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci/* Find a matching node for the slave engine at @address, using @size bytes
4468c2ecf20Sopenharmony_ci * of space. Returns NULL if not found, or a matching node with refcount
4478c2ecf20Sopenharmony_ci * already incremented.
4488c2ecf20Sopenharmony_ci */
4498c2ecf20Sopenharmony_cistatic struct device_node *fsi_device_find_of_node(struct fsi_device *dev)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	struct device_node *parent, *np;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	parent = dev_of_node(&dev->slave->dev);
4548c2ecf20Sopenharmony_ci	if (!parent)
4558c2ecf20Sopenharmony_ci		return NULL;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	for_each_child_of_node(parent, np) {
4588c2ecf20Sopenharmony_ci		if (fsi_device_node_matches(&dev->dev, np,
4598c2ecf20Sopenharmony_ci					dev->addr, dev->size))
4608c2ecf20Sopenharmony_ci			return np;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	return NULL;
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic int fsi_slave_scan(struct fsi_slave *slave)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	uint32_t engine_addr;
4698c2ecf20Sopenharmony_ci	int rc, i;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/*
4728c2ecf20Sopenharmony_ci	 * scan engines
4738c2ecf20Sopenharmony_ci	 *
4748c2ecf20Sopenharmony_ci	 * We keep the peek mode and slave engines for the core; so start
4758c2ecf20Sopenharmony_ci	 * at the third slot in the configuration table. We also need to
4768c2ecf20Sopenharmony_ci	 * skip the chip ID entry at the start of the address space.
4778c2ecf20Sopenharmony_ci	 */
4788c2ecf20Sopenharmony_ci	engine_addr = engine_page_size * 3;
4798c2ecf20Sopenharmony_ci	for (i = 2; i < engine_page_size / sizeof(uint32_t); i++) {
4808c2ecf20Sopenharmony_ci		uint8_t slots, version, type, crc;
4818c2ecf20Sopenharmony_ci		struct fsi_device *dev;
4828c2ecf20Sopenharmony_ci		uint32_t conf;
4838c2ecf20Sopenharmony_ci		__be32 data;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci		rc = fsi_slave_read(slave, (i + 1) * sizeof(data),
4868c2ecf20Sopenharmony_ci				&data, sizeof(data));
4878c2ecf20Sopenharmony_ci		if (rc) {
4888c2ecf20Sopenharmony_ci			dev_warn(&slave->dev,
4898c2ecf20Sopenharmony_ci				"error reading slave registers\n");
4908c2ecf20Sopenharmony_ci			return -1;
4918c2ecf20Sopenharmony_ci		}
4928c2ecf20Sopenharmony_ci		conf = be32_to_cpu(data);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci		crc = crc4(0, conf, 32);
4958c2ecf20Sopenharmony_ci		if (crc) {
4968c2ecf20Sopenharmony_ci			dev_warn(&slave->dev,
4978c2ecf20Sopenharmony_ci				"crc error in slave register at 0x%04x\n",
4988c2ecf20Sopenharmony_ci				i);
4998c2ecf20Sopenharmony_ci			return -1;
5008c2ecf20Sopenharmony_ci		}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci		slots = (conf & FSI_SLAVE_CONF_SLOTS_MASK)
5038c2ecf20Sopenharmony_ci			>> FSI_SLAVE_CONF_SLOTS_SHIFT;
5048c2ecf20Sopenharmony_ci		version = (conf & FSI_SLAVE_CONF_VERSION_MASK)
5058c2ecf20Sopenharmony_ci			>> FSI_SLAVE_CONF_VERSION_SHIFT;
5068c2ecf20Sopenharmony_ci		type = (conf & FSI_SLAVE_CONF_TYPE_MASK)
5078c2ecf20Sopenharmony_ci			>> FSI_SLAVE_CONF_TYPE_SHIFT;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci		/*
5108c2ecf20Sopenharmony_ci		 * Unused address areas are marked by a zero type value; this
5118c2ecf20Sopenharmony_ci		 * skips the defined address areas
5128c2ecf20Sopenharmony_ci		 */
5138c2ecf20Sopenharmony_ci		if (type != 0 && slots != 0) {
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci			/* create device */
5168c2ecf20Sopenharmony_ci			dev = fsi_create_device(slave);
5178c2ecf20Sopenharmony_ci			if (!dev)
5188c2ecf20Sopenharmony_ci				return -ENOMEM;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci			dev->slave = slave;
5218c2ecf20Sopenharmony_ci			dev->engine_type = type;
5228c2ecf20Sopenharmony_ci			dev->version = version;
5238c2ecf20Sopenharmony_ci			dev->unit = i;
5248c2ecf20Sopenharmony_ci			dev->addr = engine_addr;
5258c2ecf20Sopenharmony_ci			dev->size = slots * engine_page_size;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci			dev_dbg(&slave->dev,
5288c2ecf20Sopenharmony_ci			"engine[%i]: type %x, version %x, addr %x size %x\n",
5298c2ecf20Sopenharmony_ci					dev->unit, dev->engine_type, version,
5308c2ecf20Sopenharmony_ci					dev->addr, dev->size);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci			dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x",
5338c2ecf20Sopenharmony_ci					slave->master->idx, slave->link,
5348c2ecf20Sopenharmony_ci					slave->id, i - 2);
5358c2ecf20Sopenharmony_ci			dev->dev.of_node = fsi_device_find_of_node(dev);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci			rc = device_register(&dev->dev);
5388c2ecf20Sopenharmony_ci			if (rc) {
5398c2ecf20Sopenharmony_ci				dev_warn(&slave->dev, "add failed: %d\n", rc);
5408c2ecf20Sopenharmony_ci				put_device(&dev->dev);
5418c2ecf20Sopenharmony_ci			}
5428c2ecf20Sopenharmony_ci		}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci		engine_addr += slots * engine_page_size;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci		if (!(conf & FSI_SLAVE_CONF_NEXT_MASK))
5478c2ecf20Sopenharmony_ci			break;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	return 0;
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cistatic unsigned long aligned_access_size(size_t offset, size_t count)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	unsigned long offset_unit, count_unit;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	/* Criteria:
5588c2ecf20Sopenharmony_ci	 *
5598c2ecf20Sopenharmony_ci	 * 1. Access size must be less than or equal to the maximum access
5608c2ecf20Sopenharmony_ci	 *    width or the highest power-of-two factor of offset
5618c2ecf20Sopenharmony_ci	 * 2. Access size must be less than or equal to the amount specified by
5628c2ecf20Sopenharmony_ci	 *    count
5638c2ecf20Sopenharmony_ci	 *
5648c2ecf20Sopenharmony_ci	 * The access width is optimal if we can calculate 1 to be strictly
5658c2ecf20Sopenharmony_ci	 * equal while still satisfying 2.
5668c2ecf20Sopenharmony_ci	 */
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	/* Find 1 by the bottom bit of offset (with a 4 byte access cap) */
5698c2ecf20Sopenharmony_ci	offset_unit = BIT(__builtin_ctzl(offset | 4));
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	/* Find 2 by the top bit of count */
5728c2ecf20Sopenharmony_ci	count_unit = BIT(8 * sizeof(unsigned long) - 1 - __builtin_clzl(count));
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	/* Constrain the maximum access width to the minimum of both criteria */
5758c2ecf20Sopenharmony_ci	return BIT(__builtin_ctzl(offset_unit | count_unit));
5768c2ecf20Sopenharmony_ci}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_cistatic ssize_t fsi_slave_sysfs_raw_read(struct file *file,
5798c2ecf20Sopenharmony_ci		struct kobject *kobj, struct bin_attribute *attr, char *buf,
5808c2ecf20Sopenharmony_ci		loff_t off, size_t count)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj));
5838c2ecf20Sopenharmony_ci	size_t total_len, read_len;
5848c2ecf20Sopenharmony_ci	int rc;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	if (off < 0)
5878c2ecf20Sopenharmony_ci		return -EINVAL;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	if (off > 0xffffffff || count > 0xffffffff || off + count > 0xffffffff)
5908c2ecf20Sopenharmony_ci		return -EINVAL;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	for (total_len = 0; total_len < count; total_len += read_len) {
5938c2ecf20Sopenharmony_ci		read_len = aligned_access_size(off, count - total_len);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci		rc = fsi_slave_read(slave, off, buf + total_len, read_len);
5968c2ecf20Sopenharmony_ci		if (rc)
5978c2ecf20Sopenharmony_ci			return rc;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci		off += read_len;
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	return count;
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic ssize_t fsi_slave_sysfs_raw_write(struct file *file,
6068c2ecf20Sopenharmony_ci		struct kobject *kobj, struct bin_attribute *attr,
6078c2ecf20Sopenharmony_ci		char *buf, loff_t off, size_t count)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj));
6108c2ecf20Sopenharmony_ci	size_t total_len, write_len;
6118c2ecf20Sopenharmony_ci	int rc;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	if (off < 0)
6148c2ecf20Sopenharmony_ci		return -EINVAL;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	if (off > 0xffffffff || count > 0xffffffff || off + count > 0xffffffff)
6178c2ecf20Sopenharmony_ci		return -EINVAL;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	for (total_len = 0; total_len < count; total_len += write_len) {
6208c2ecf20Sopenharmony_ci		write_len = aligned_access_size(off, count - total_len);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci		rc = fsi_slave_write(slave, off, buf + total_len, write_len);
6238c2ecf20Sopenharmony_ci		if (rc)
6248c2ecf20Sopenharmony_ci			return rc;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci		off += write_len;
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	return count;
6308c2ecf20Sopenharmony_ci}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_cistatic const struct bin_attribute fsi_slave_raw_attr = {
6338c2ecf20Sopenharmony_ci	.attr = {
6348c2ecf20Sopenharmony_ci		.name = "raw",
6358c2ecf20Sopenharmony_ci		.mode = 0600,
6368c2ecf20Sopenharmony_ci	},
6378c2ecf20Sopenharmony_ci	.size = 0,
6388c2ecf20Sopenharmony_ci	.read = fsi_slave_sysfs_raw_read,
6398c2ecf20Sopenharmony_ci	.write = fsi_slave_sysfs_raw_write,
6408c2ecf20Sopenharmony_ci};
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cistatic void fsi_slave_release(struct device *dev)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	struct fsi_slave *slave = to_fsi_slave(dev);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	fsi_free_minor(slave->dev.devt);
6478c2ecf20Sopenharmony_ci	of_node_put(dev->of_node);
6488c2ecf20Sopenharmony_ci	kfree(slave);
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cistatic bool fsi_slave_node_matches(struct device_node *np,
6528c2ecf20Sopenharmony_ci		int link, uint8_t id)
6538c2ecf20Sopenharmony_ci{
6548c2ecf20Sopenharmony_ci	unsigned int len, na, ns;
6558c2ecf20Sopenharmony_ci	const __be32 *prop;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	na = of_n_addr_cells(np);
6588c2ecf20Sopenharmony_ci	ns = of_n_size_cells(np);
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	/* Ensure we have the correct format for addresses and sizes in
6618c2ecf20Sopenharmony_ci	 * reg properties
6628c2ecf20Sopenharmony_ci	 */
6638c2ecf20Sopenharmony_ci	if (na != 2 || ns != 0)
6648c2ecf20Sopenharmony_ci		return false;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	prop = of_get_property(np, "reg", &len);
6678c2ecf20Sopenharmony_ci	if (!prop || len != 8)
6688c2ecf20Sopenharmony_ci		return false;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	return (of_read_number(prop, 1) == link) &&
6718c2ecf20Sopenharmony_ci		(of_read_number(prop + 1, 1) == id);
6728c2ecf20Sopenharmony_ci}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci/* Find a matching node for the slave at (link, id). Returns NULL if none
6758c2ecf20Sopenharmony_ci * found, or a matching node with refcount already incremented.
6768c2ecf20Sopenharmony_ci */
6778c2ecf20Sopenharmony_cistatic struct device_node *fsi_slave_find_of_node(struct fsi_master *master,
6788c2ecf20Sopenharmony_ci		int link, uint8_t id)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	struct device_node *parent, *np;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	parent = dev_of_node(&master->dev);
6838c2ecf20Sopenharmony_ci	if (!parent)
6848c2ecf20Sopenharmony_ci		return NULL;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	for_each_child_of_node(parent, np) {
6878c2ecf20Sopenharmony_ci		if (fsi_slave_node_matches(np, link, id))
6888c2ecf20Sopenharmony_ci			return np;
6898c2ecf20Sopenharmony_ci	}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	return NULL;
6928c2ecf20Sopenharmony_ci}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cistatic ssize_t cfam_read(struct file *filep, char __user *buf, size_t count,
6958c2ecf20Sopenharmony_ci			 loff_t *offset)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	struct fsi_slave *slave = filep->private_data;
6988c2ecf20Sopenharmony_ci	size_t total_len, read_len;
6998c2ecf20Sopenharmony_ci	loff_t off = *offset;
7008c2ecf20Sopenharmony_ci	ssize_t rc;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	if (off < 0)
7038c2ecf20Sopenharmony_ci		return -EINVAL;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	if (off > 0xffffffff || count > 0xffffffff || off + count > 0xffffffff)
7068c2ecf20Sopenharmony_ci		return -EINVAL;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	for (total_len = 0; total_len < count; total_len += read_len) {
7098c2ecf20Sopenharmony_ci		__be32 data;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci		read_len = min_t(size_t, count, 4);
7128c2ecf20Sopenharmony_ci		read_len -= off & 0x3;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci		rc = fsi_slave_read(slave, off, &data, read_len);
7158c2ecf20Sopenharmony_ci		if (rc)
7168c2ecf20Sopenharmony_ci			goto fail;
7178c2ecf20Sopenharmony_ci		rc = copy_to_user(buf + total_len, &data, read_len);
7188c2ecf20Sopenharmony_ci		if (rc) {
7198c2ecf20Sopenharmony_ci			rc = -EFAULT;
7208c2ecf20Sopenharmony_ci			goto fail;
7218c2ecf20Sopenharmony_ci		}
7228c2ecf20Sopenharmony_ci		off += read_len;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci	rc = count;
7258c2ecf20Sopenharmony_ci fail:
7268c2ecf20Sopenharmony_ci	*offset = off;
7278c2ecf20Sopenharmony_ci	return rc;
7288c2ecf20Sopenharmony_ci}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_cistatic ssize_t cfam_write(struct file *filep, const char __user *buf,
7318c2ecf20Sopenharmony_ci			  size_t count, loff_t *offset)
7328c2ecf20Sopenharmony_ci{
7338c2ecf20Sopenharmony_ci	struct fsi_slave *slave = filep->private_data;
7348c2ecf20Sopenharmony_ci	size_t total_len, write_len;
7358c2ecf20Sopenharmony_ci	loff_t off = *offset;
7368c2ecf20Sopenharmony_ci	ssize_t rc;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	if (off < 0)
7408c2ecf20Sopenharmony_ci		return -EINVAL;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	if (off > 0xffffffff || count > 0xffffffff || off + count > 0xffffffff)
7438c2ecf20Sopenharmony_ci		return -EINVAL;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	for (total_len = 0; total_len < count; total_len += write_len) {
7468c2ecf20Sopenharmony_ci		__be32 data;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci		write_len = min_t(size_t, count, 4);
7498c2ecf20Sopenharmony_ci		write_len -= off & 0x3;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci		rc = copy_from_user(&data, buf + total_len, write_len);
7528c2ecf20Sopenharmony_ci		if (rc) {
7538c2ecf20Sopenharmony_ci			rc = -EFAULT;
7548c2ecf20Sopenharmony_ci			goto fail;
7558c2ecf20Sopenharmony_ci		}
7568c2ecf20Sopenharmony_ci		rc = fsi_slave_write(slave, off, &data, write_len);
7578c2ecf20Sopenharmony_ci		if (rc)
7588c2ecf20Sopenharmony_ci			goto fail;
7598c2ecf20Sopenharmony_ci		off += write_len;
7608c2ecf20Sopenharmony_ci	}
7618c2ecf20Sopenharmony_ci	rc = count;
7628c2ecf20Sopenharmony_ci fail:
7638c2ecf20Sopenharmony_ci	*offset = off;
7648c2ecf20Sopenharmony_ci	return rc;
7658c2ecf20Sopenharmony_ci}
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_cistatic loff_t cfam_llseek(struct file *file, loff_t offset, int whence)
7688c2ecf20Sopenharmony_ci{
7698c2ecf20Sopenharmony_ci	switch (whence) {
7708c2ecf20Sopenharmony_ci	case SEEK_CUR:
7718c2ecf20Sopenharmony_ci		break;
7728c2ecf20Sopenharmony_ci	case SEEK_SET:
7738c2ecf20Sopenharmony_ci		file->f_pos = offset;
7748c2ecf20Sopenharmony_ci		break;
7758c2ecf20Sopenharmony_ci	default:
7768c2ecf20Sopenharmony_ci		return -EINVAL;
7778c2ecf20Sopenharmony_ci	}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	return offset;
7808c2ecf20Sopenharmony_ci}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_cistatic int cfam_open(struct inode *inode, struct file *file)
7838c2ecf20Sopenharmony_ci{
7848c2ecf20Sopenharmony_ci	struct fsi_slave *slave = container_of(inode->i_cdev, struct fsi_slave, cdev);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	file->private_data = slave;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	return 0;
7898c2ecf20Sopenharmony_ci}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_cistatic const struct file_operations cfam_fops = {
7928c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
7938c2ecf20Sopenharmony_ci	.open		= cfam_open,
7948c2ecf20Sopenharmony_ci	.llseek		= cfam_llseek,
7958c2ecf20Sopenharmony_ci	.read		= cfam_read,
7968c2ecf20Sopenharmony_ci	.write		= cfam_write,
7978c2ecf20Sopenharmony_ci};
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_cistatic ssize_t send_term_store(struct device *dev,
8008c2ecf20Sopenharmony_ci			       struct device_attribute *attr,
8018c2ecf20Sopenharmony_ci			       const char *buf, size_t count)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	struct fsi_slave *slave = to_fsi_slave(dev);
8048c2ecf20Sopenharmony_ci	struct fsi_master *master = slave->master;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	if (!master->term)
8078c2ecf20Sopenharmony_ci		return -ENODEV;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	master->term(master, slave->link, slave->id);
8108c2ecf20Sopenharmony_ci	return count;
8118c2ecf20Sopenharmony_ci}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(send_term);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_cistatic ssize_t slave_send_echo_show(struct device *dev,
8168c2ecf20Sopenharmony_ci				    struct device_attribute *attr,
8178c2ecf20Sopenharmony_ci				    char *buf)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	struct fsi_slave *slave = to_fsi_slave(dev);
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", slave->t_send_delay);
8228c2ecf20Sopenharmony_ci}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_cistatic ssize_t slave_send_echo_store(struct device *dev,
8258c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	struct fsi_slave *slave = to_fsi_slave(dev);
8288c2ecf20Sopenharmony_ci	struct fsi_master *master = slave->master;
8298c2ecf20Sopenharmony_ci	unsigned long val;
8308c2ecf20Sopenharmony_ci	int rc;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	if (kstrtoul(buf, 0, &val) < 0)
8338c2ecf20Sopenharmony_ci		return -EINVAL;
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	if (val < 1 || val > 16)
8368c2ecf20Sopenharmony_ci		return -EINVAL;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	if (!master->link_config)
8398c2ecf20Sopenharmony_ci		return -ENXIO;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	/* Current HW mandates that send and echo delay are identical */
8428c2ecf20Sopenharmony_ci	slave->t_send_delay = val;
8438c2ecf20Sopenharmony_ci	slave->t_echo_delay = val;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	rc = fsi_slave_set_smode(slave);
8468c2ecf20Sopenharmony_ci	if (rc < 0)
8478c2ecf20Sopenharmony_ci		return rc;
8488c2ecf20Sopenharmony_ci	if (master->link_config)
8498c2ecf20Sopenharmony_ci		master->link_config(master, slave->link,
8508c2ecf20Sopenharmony_ci				    slave->t_send_delay,
8518c2ecf20Sopenharmony_ci				    slave->t_echo_delay);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	return count;
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_cistatic DEVICE_ATTR(send_echo_delays, 0600,
8578c2ecf20Sopenharmony_ci		   slave_send_echo_show, slave_send_echo_store);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic ssize_t chip_id_show(struct device *dev,
8608c2ecf20Sopenharmony_ci			    struct device_attribute *attr,
8618c2ecf20Sopenharmony_ci			    char *buf)
8628c2ecf20Sopenharmony_ci{
8638c2ecf20Sopenharmony_ci	struct fsi_slave *slave = to_fsi_slave(dev);
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", slave->chip_id);
8668c2ecf20Sopenharmony_ci}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(chip_id);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_cistatic ssize_t cfam_id_show(struct device *dev,
8718c2ecf20Sopenharmony_ci			    struct device_attribute *attr,
8728c2ecf20Sopenharmony_ci			    char *buf)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci	struct fsi_slave *slave = to_fsi_slave(dev);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%x\n", slave->cfam_id);
8778c2ecf20Sopenharmony_ci}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(cfam_id);
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cistatic struct attribute *cfam_attr[] = {
8828c2ecf20Sopenharmony_ci	&dev_attr_send_echo_delays.attr,
8838c2ecf20Sopenharmony_ci	&dev_attr_chip_id.attr,
8848c2ecf20Sopenharmony_ci	&dev_attr_cfam_id.attr,
8858c2ecf20Sopenharmony_ci	&dev_attr_send_term.attr,
8868c2ecf20Sopenharmony_ci	NULL,
8878c2ecf20Sopenharmony_ci};
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_cistatic const struct attribute_group cfam_attr_group = {
8908c2ecf20Sopenharmony_ci	.attrs = cfam_attr,
8918c2ecf20Sopenharmony_ci};
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_cistatic const struct attribute_group *cfam_attr_groups[] = {
8948c2ecf20Sopenharmony_ci	&cfam_attr_group,
8958c2ecf20Sopenharmony_ci	NULL,
8968c2ecf20Sopenharmony_ci};
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_cistatic char *cfam_devnode(struct device *dev, umode_t *mode,
8998c2ecf20Sopenharmony_ci			  kuid_t *uid, kgid_t *gid)
9008c2ecf20Sopenharmony_ci{
9018c2ecf20Sopenharmony_ci	struct fsi_slave *slave = to_fsi_slave(dev);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci#ifdef CONFIG_FSI_NEW_DEV_NODE
9048c2ecf20Sopenharmony_ci	return kasprintf(GFP_KERNEL, "fsi/cfam%d", slave->cdev_idx);
9058c2ecf20Sopenharmony_ci#else
9068c2ecf20Sopenharmony_ci	return kasprintf(GFP_KERNEL, "cfam%d", slave->cdev_idx);
9078c2ecf20Sopenharmony_ci#endif
9088c2ecf20Sopenharmony_ci}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_cistatic const struct device_type cfam_type = {
9118c2ecf20Sopenharmony_ci	.name = "cfam",
9128c2ecf20Sopenharmony_ci	.devnode = cfam_devnode,
9138c2ecf20Sopenharmony_ci	.groups = cfam_attr_groups
9148c2ecf20Sopenharmony_ci};
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_cistatic char *fsi_cdev_devnode(struct device *dev, umode_t *mode,
9178c2ecf20Sopenharmony_ci			      kuid_t *uid, kgid_t *gid)
9188c2ecf20Sopenharmony_ci{
9198c2ecf20Sopenharmony_ci#ifdef CONFIG_FSI_NEW_DEV_NODE
9208c2ecf20Sopenharmony_ci	return kasprintf(GFP_KERNEL, "fsi/%s", dev_name(dev));
9218c2ecf20Sopenharmony_ci#else
9228c2ecf20Sopenharmony_ci	return kasprintf(GFP_KERNEL, "%s", dev_name(dev));
9238c2ecf20Sopenharmony_ci#endif
9248c2ecf20Sopenharmony_ci}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ciconst struct device_type fsi_cdev_type = {
9278c2ecf20Sopenharmony_ci	.name = "fsi-cdev",
9288c2ecf20Sopenharmony_ci	.devnode = fsi_cdev_devnode,
9298c2ecf20Sopenharmony_ci};
9308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_cdev_type);
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci/* Backward compatible /dev/ numbering in "old style" mode */
9338c2ecf20Sopenharmony_cistatic int fsi_adjust_index(int index)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci#ifdef CONFIG_FSI_NEW_DEV_NODE
9368c2ecf20Sopenharmony_ci	return index;
9378c2ecf20Sopenharmony_ci#else
9388c2ecf20Sopenharmony_ci	return index + 1;
9398c2ecf20Sopenharmony_ci#endif
9408c2ecf20Sopenharmony_ci}
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_cistatic int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,
9438c2ecf20Sopenharmony_ci			       dev_t *out_dev, int *out_index)
9448c2ecf20Sopenharmony_ci{
9458c2ecf20Sopenharmony_ci	int cid = slave->chip_id;
9468c2ecf20Sopenharmony_ci	int id;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	/* Check if we qualify for legacy numbering */
9498c2ecf20Sopenharmony_ci	if (cid >= 0 && cid < 16 && type < 4) {
9508c2ecf20Sopenharmony_ci		/* Try reserving the legacy number */
9518c2ecf20Sopenharmony_ci		id = (cid << 4) | type;
9528c2ecf20Sopenharmony_ci		id = ida_simple_get(&fsi_minor_ida, id, id + 1, GFP_KERNEL);
9538c2ecf20Sopenharmony_ci		if (id >= 0) {
9548c2ecf20Sopenharmony_ci			*out_index = fsi_adjust_index(cid);
9558c2ecf20Sopenharmony_ci			*out_dev = fsi_base_dev + id;
9568c2ecf20Sopenharmony_ci			return 0;
9578c2ecf20Sopenharmony_ci		}
9588c2ecf20Sopenharmony_ci		/* Other failure */
9598c2ecf20Sopenharmony_ci		if (id != -ENOSPC)
9608c2ecf20Sopenharmony_ci			return id;
9618c2ecf20Sopenharmony_ci		/* Fallback to non-legacy allocation */
9628c2ecf20Sopenharmony_ci	}
9638c2ecf20Sopenharmony_ci	id = ida_simple_get(&fsi_minor_ida, FSI_CHAR_LEGACY_TOP,
9648c2ecf20Sopenharmony_ci			    FSI_CHAR_MAX_DEVICES, GFP_KERNEL);
9658c2ecf20Sopenharmony_ci	if (id < 0)
9668c2ecf20Sopenharmony_ci		return id;
9678c2ecf20Sopenharmony_ci	*out_index = fsi_adjust_index(id);
9688c2ecf20Sopenharmony_ci	*out_dev = fsi_base_dev + id;
9698c2ecf20Sopenharmony_ci	return 0;
9708c2ecf20Sopenharmony_ci}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ciint fsi_get_new_minor(struct fsi_device *fdev, enum fsi_dev_type type,
9738c2ecf20Sopenharmony_ci		      dev_t *out_dev, int *out_index)
9748c2ecf20Sopenharmony_ci{
9758c2ecf20Sopenharmony_ci	return __fsi_get_new_minor(fdev->slave, type, out_dev, out_index);
9768c2ecf20Sopenharmony_ci}
9778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_get_new_minor);
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_civoid fsi_free_minor(dev_t dev)
9808c2ecf20Sopenharmony_ci{
9818c2ecf20Sopenharmony_ci	ida_simple_remove(&fsi_minor_ida, MINOR(dev));
9828c2ecf20Sopenharmony_ci}
9838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_free_minor);
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_cistatic int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
9868c2ecf20Sopenharmony_ci{
9878c2ecf20Sopenharmony_ci	uint32_t cfam_id;
9888c2ecf20Sopenharmony_ci	struct fsi_slave *slave;
9898c2ecf20Sopenharmony_ci	uint8_t crc;
9908c2ecf20Sopenharmony_ci	__be32 data, llmode, slbus;
9918c2ecf20Sopenharmony_ci	int rc;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	/* Currently, we only support single slaves on a link, and use the
9948c2ecf20Sopenharmony_ci	 * full 23-bit address range
9958c2ecf20Sopenharmony_ci	 */
9968c2ecf20Sopenharmony_ci	if (id != 0)
9978c2ecf20Sopenharmony_ci		return -EINVAL;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	rc = fsi_master_read(master, link, id, 0, &data, sizeof(data));
10008c2ecf20Sopenharmony_ci	if (rc) {
10018c2ecf20Sopenharmony_ci		dev_dbg(&master->dev, "can't read slave %02x:%02x %d\n",
10028c2ecf20Sopenharmony_ci				link, id, rc);
10038c2ecf20Sopenharmony_ci		return -ENODEV;
10048c2ecf20Sopenharmony_ci	}
10058c2ecf20Sopenharmony_ci	cfam_id = be32_to_cpu(data);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	crc = crc4(0, cfam_id, 32);
10088c2ecf20Sopenharmony_ci	if (crc) {
10098c2ecf20Sopenharmony_ci		dev_warn(&master->dev, "slave %02x:%02x invalid cfam id CRC!\n",
10108c2ecf20Sopenharmony_ci				link, id);
10118c2ecf20Sopenharmony_ci		return -EIO;
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	dev_dbg(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n",
10158c2ecf20Sopenharmony_ci			cfam_id, master->idx, link, id);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	/* If we're behind a master that doesn't provide a self-running bus
10188c2ecf20Sopenharmony_ci	 * clock, put the slave into async mode
10198c2ecf20Sopenharmony_ci	 */
10208c2ecf20Sopenharmony_ci	if (master->flags & FSI_MASTER_FLAG_SWCLOCK) {
10218c2ecf20Sopenharmony_ci		llmode = cpu_to_be32(FSI_LLMODE_ASYNC);
10228c2ecf20Sopenharmony_ci		rc = fsi_master_write(master, link, id,
10238c2ecf20Sopenharmony_ci				FSI_SLAVE_BASE + FSI_LLMODE,
10248c2ecf20Sopenharmony_ci				&llmode, sizeof(llmode));
10258c2ecf20Sopenharmony_ci		if (rc)
10268c2ecf20Sopenharmony_ci			dev_warn(&master->dev,
10278c2ecf20Sopenharmony_ci				"can't set llmode on slave:%02x:%02x %d\n",
10288c2ecf20Sopenharmony_ci				link, id, rc);
10298c2ecf20Sopenharmony_ci	}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	/* We can communicate with a slave; create the slave device and
10328c2ecf20Sopenharmony_ci	 * register.
10338c2ecf20Sopenharmony_ci	 */
10348c2ecf20Sopenharmony_ci	slave = kzalloc(sizeof(*slave), GFP_KERNEL);
10358c2ecf20Sopenharmony_ci	if (!slave)
10368c2ecf20Sopenharmony_ci		return -ENOMEM;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	dev_set_name(&slave->dev, "slave@%02x:%02x", link, id);
10398c2ecf20Sopenharmony_ci	slave->dev.type = &cfam_type;
10408c2ecf20Sopenharmony_ci	slave->dev.parent = &master->dev;
10418c2ecf20Sopenharmony_ci	slave->dev.of_node = fsi_slave_find_of_node(master, link, id);
10428c2ecf20Sopenharmony_ci	slave->dev.release = fsi_slave_release;
10438c2ecf20Sopenharmony_ci	device_initialize(&slave->dev);
10448c2ecf20Sopenharmony_ci	slave->cfam_id = cfam_id;
10458c2ecf20Sopenharmony_ci	slave->master = master;
10468c2ecf20Sopenharmony_ci	slave->link = link;
10478c2ecf20Sopenharmony_ci	slave->id = id;
10488c2ecf20Sopenharmony_ci	slave->size = FSI_SLAVE_SIZE_23b;
10498c2ecf20Sopenharmony_ci	slave->t_send_delay = 16;
10508c2ecf20Sopenharmony_ci	slave->t_echo_delay = 16;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	/* Get chip ID if any */
10538c2ecf20Sopenharmony_ci	slave->chip_id = -1;
10548c2ecf20Sopenharmony_ci	if (slave->dev.of_node) {
10558c2ecf20Sopenharmony_ci		uint32_t prop;
10568c2ecf20Sopenharmony_ci		if (!of_property_read_u32(slave->dev.of_node, "chip-id", &prop))
10578c2ecf20Sopenharmony_ci			slave->chip_id = prop;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	}
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	slbus = cpu_to_be32(FSI_SLBUS_FORCE);
10628c2ecf20Sopenharmony_ci	rc = fsi_master_write(master, link, id, FSI_SLAVE_BASE + FSI_SLBUS,
10638c2ecf20Sopenharmony_ci			      &slbus, sizeof(slbus));
10648c2ecf20Sopenharmony_ci	if (rc)
10658c2ecf20Sopenharmony_ci		dev_warn(&master->dev,
10668c2ecf20Sopenharmony_ci			 "can't set slbus on slave:%02x:%02x %d\n", link, id,
10678c2ecf20Sopenharmony_ci			 rc);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	rc = fsi_slave_set_smode(slave);
10708c2ecf20Sopenharmony_ci	if (rc) {
10718c2ecf20Sopenharmony_ci		dev_warn(&master->dev,
10728c2ecf20Sopenharmony_ci				"can't set smode on slave:%02x:%02x %d\n",
10738c2ecf20Sopenharmony_ci				link, id, rc);
10748c2ecf20Sopenharmony_ci		goto err_free;
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	/* Allocate a minor in the FSI space */
10788c2ecf20Sopenharmony_ci	rc = __fsi_get_new_minor(slave, fsi_dev_cfam, &slave->dev.devt,
10798c2ecf20Sopenharmony_ci				 &slave->cdev_idx);
10808c2ecf20Sopenharmony_ci	if (rc)
10818c2ecf20Sopenharmony_ci		goto err_free;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	/* Create chardev for userspace access */
10848c2ecf20Sopenharmony_ci	cdev_init(&slave->cdev, &cfam_fops);
10858c2ecf20Sopenharmony_ci	rc = cdev_device_add(&slave->cdev, &slave->dev);
10868c2ecf20Sopenharmony_ci	if (rc) {
10878c2ecf20Sopenharmony_ci		dev_err(&slave->dev, "Error %d creating slave device\n", rc);
10888c2ecf20Sopenharmony_ci		goto err_free_ida;
10898c2ecf20Sopenharmony_ci	}
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	/* Now that we have the cdev registered with the core, any fatal
10928c2ecf20Sopenharmony_ci	 * failures beyond this point will need to clean up through
10938c2ecf20Sopenharmony_ci	 * cdev_device_del(). Fortunately though, nothing past here is fatal.
10948c2ecf20Sopenharmony_ci	 */
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	if (master->link_config)
10978c2ecf20Sopenharmony_ci		master->link_config(master, link,
10988c2ecf20Sopenharmony_ci				    slave->t_send_delay,
10998c2ecf20Sopenharmony_ci				    slave->t_echo_delay);
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	/* Legacy raw file -> to be removed */
11028c2ecf20Sopenharmony_ci	rc = device_create_bin_file(&slave->dev, &fsi_slave_raw_attr);
11038c2ecf20Sopenharmony_ci	if (rc)
11048c2ecf20Sopenharmony_ci		dev_warn(&slave->dev, "failed to create raw attr: %d\n", rc);
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	rc = fsi_slave_scan(slave);
11088c2ecf20Sopenharmony_ci	if (rc)
11098c2ecf20Sopenharmony_ci		dev_dbg(&master->dev, "failed during slave scan with: %d\n",
11108c2ecf20Sopenharmony_ci				rc);
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	return 0;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_cierr_free_ida:
11158c2ecf20Sopenharmony_ci	fsi_free_minor(slave->dev.devt);
11168c2ecf20Sopenharmony_cierr_free:
11178c2ecf20Sopenharmony_ci	of_node_put(slave->dev.of_node);
11188c2ecf20Sopenharmony_ci	kfree(slave);
11198c2ecf20Sopenharmony_ci	return rc;
11208c2ecf20Sopenharmony_ci}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci/* FSI master support */
11238c2ecf20Sopenharmony_cistatic int fsi_check_access(uint32_t addr, size_t size)
11248c2ecf20Sopenharmony_ci{
11258c2ecf20Sopenharmony_ci	if (size == 4) {
11268c2ecf20Sopenharmony_ci		if (addr & 0x3)
11278c2ecf20Sopenharmony_ci			return -EINVAL;
11288c2ecf20Sopenharmony_ci	} else if (size == 2) {
11298c2ecf20Sopenharmony_ci		if (addr & 0x1)
11308c2ecf20Sopenharmony_ci			return -EINVAL;
11318c2ecf20Sopenharmony_ci	} else if (size != 1)
11328c2ecf20Sopenharmony_ci		return -EINVAL;
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	return 0;
11358c2ecf20Sopenharmony_ci}
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_cistatic int fsi_master_read(struct fsi_master *master, int link,
11388c2ecf20Sopenharmony_ci		uint8_t slave_id, uint32_t addr, void *val, size_t size)
11398c2ecf20Sopenharmony_ci{
11408c2ecf20Sopenharmony_ci	int rc;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	trace_fsi_master_read(master, link, slave_id, addr, size);
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	rc = fsi_check_access(addr, size);
11458c2ecf20Sopenharmony_ci	if (!rc)
11468c2ecf20Sopenharmony_ci		rc = master->read(master, link, slave_id, addr, val, size);
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	trace_fsi_master_rw_result(master, link, slave_id, addr, size,
11498c2ecf20Sopenharmony_ci			false, val, rc);
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	return rc;
11528c2ecf20Sopenharmony_ci}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_cistatic int fsi_master_write(struct fsi_master *master, int link,
11558c2ecf20Sopenharmony_ci		uint8_t slave_id, uint32_t addr, const void *val, size_t size)
11568c2ecf20Sopenharmony_ci{
11578c2ecf20Sopenharmony_ci	int rc;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	trace_fsi_master_write(master, link, slave_id, addr, size, val);
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	rc = fsi_check_access(addr, size);
11628c2ecf20Sopenharmony_ci	if (!rc)
11638c2ecf20Sopenharmony_ci		rc = master->write(master, link, slave_id, addr, val, size);
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	trace_fsi_master_rw_result(master, link, slave_id, addr, size,
11668c2ecf20Sopenharmony_ci			true, val, rc);
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	return rc;
11698c2ecf20Sopenharmony_ci}
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_cistatic int fsi_master_link_disable(struct fsi_master *master, int link)
11728c2ecf20Sopenharmony_ci{
11738c2ecf20Sopenharmony_ci	if (master->link_enable)
11748c2ecf20Sopenharmony_ci		return master->link_enable(master, link, false);
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	return 0;
11778c2ecf20Sopenharmony_ci}
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_cistatic int fsi_master_link_enable(struct fsi_master *master, int link)
11808c2ecf20Sopenharmony_ci{
11818c2ecf20Sopenharmony_ci	if (master->link_enable)
11828c2ecf20Sopenharmony_ci		return master->link_enable(master, link, true);
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	return 0;
11858c2ecf20Sopenharmony_ci}
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci/*
11888c2ecf20Sopenharmony_ci * Issue a break command on this link
11898c2ecf20Sopenharmony_ci */
11908c2ecf20Sopenharmony_cistatic int fsi_master_break(struct fsi_master *master, int link)
11918c2ecf20Sopenharmony_ci{
11928c2ecf20Sopenharmony_ci	int rc = 0;
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	trace_fsi_master_break(master, link);
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_ci	if (master->send_break)
11978c2ecf20Sopenharmony_ci		rc = master->send_break(master, link);
11988c2ecf20Sopenharmony_ci	if (master->link_config)
11998c2ecf20Sopenharmony_ci		master->link_config(master, link, 16, 16);
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	return rc;
12028c2ecf20Sopenharmony_ci}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_cistatic int fsi_master_scan(struct fsi_master *master)
12058c2ecf20Sopenharmony_ci{
12068c2ecf20Sopenharmony_ci	int link, rc;
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	for (link = 0; link < master->n_links; link++) {
12098c2ecf20Sopenharmony_ci		rc = fsi_master_link_enable(master, link);
12108c2ecf20Sopenharmony_ci		if (rc) {
12118c2ecf20Sopenharmony_ci			dev_dbg(&master->dev,
12128c2ecf20Sopenharmony_ci				"enable link %d failed: %d\n", link, rc);
12138c2ecf20Sopenharmony_ci			continue;
12148c2ecf20Sopenharmony_ci		}
12158c2ecf20Sopenharmony_ci		rc = fsi_master_break(master, link);
12168c2ecf20Sopenharmony_ci		if (rc) {
12178c2ecf20Sopenharmony_ci			fsi_master_link_disable(master, link);
12188c2ecf20Sopenharmony_ci			dev_dbg(&master->dev,
12198c2ecf20Sopenharmony_ci				"break to link %d failed: %d\n", link, rc);
12208c2ecf20Sopenharmony_ci			continue;
12218c2ecf20Sopenharmony_ci		}
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci		rc = fsi_slave_init(master, link, 0);
12248c2ecf20Sopenharmony_ci		if (rc)
12258c2ecf20Sopenharmony_ci			fsi_master_link_disable(master, link);
12268c2ecf20Sopenharmony_ci	}
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	return 0;
12298c2ecf20Sopenharmony_ci}
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_cistatic int fsi_slave_remove_device(struct device *dev, void *arg)
12328c2ecf20Sopenharmony_ci{
12338c2ecf20Sopenharmony_ci	device_unregister(dev);
12348c2ecf20Sopenharmony_ci	return 0;
12358c2ecf20Sopenharmony_ci}
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_cistatic int fsi_master_remove_slave(struct device *dev, void *arg)
12388c2ecf20Sopenharmony_ci{
12398c2ecf20Sopenharmony_ci	struct fsi_slave *slave = to_fsi_slave(dev);
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	device_for_each_child(dev, NULL, fsi_slave_remove_device);
12428c2ecf20Sopenharmony_ci	cdev_device_del(&slave->cdev, &slave->dev);
12438c2ecf20Sopenharmony_ci	put_device(dev);
12448c2ecf20Sopenharmony_ci	return 0;
12458c2ecf20Sopenharmony_ci}
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_cistatic void fsi_master_unscan(struct fsi_master *master)
12488c2ecf20Sopenharmony_ci{
12498c2ecf20Sopenharmony_ci	device_for_each_child(&master->dev, NULL, fsi_master_remove_slave);
12508c2ecf20Sopenharmony_ci}
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ciint fsi_master_rescan(struct fsi_master *master)
12538c2ecf20Sopenharmony_ci{
12548c2ecf20Sopenharmony_ci	int rc;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	mutex_lock(&master->scan_lock);
12578c2ecf20Sopenharmony_ci	fsi_master_unscan(master);
12588c2ecf20Sopenharmony_ci	rc = fsi_master_scan(master);
12598c2ecf20Sopenharmony_ci	mutex_unlock(&master->scan_lock);
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	return rc;
12628c2ecf20Sopenharmony_ci}
12638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_master_rescan);
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_cistatic ssize_t master_rescan_store(struct device *dev,
12668c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
12678c2ecf20Sopenharmony_ci{
12688c2ecf20Sopenharmony_ci	struct fsi_master *master = to_fsi_master(dev);
12698c2ecf20Sopenharmony_ci	int rc;
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	rc = fsi_master_rescan(master);
12728c2ecf20Sopenharmony_ci	if (rc < 0)
12738c2ecf20Sopenharmony_ci		return rc;
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	return count;
12768c2ecf20Sopenharmony_ci}
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_cistatic DEVICE_ATTR(rescan, 0200, NULL, master_rescan_store);
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_cistatic ssize_t master_break_store(struct device *dev,
12818c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
12828c2ecf20Sopenharmony_ci{
12838c2ecf20Sopenharmony_ci	struct fsi_master *master = to_fsi_master(dev);
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	fsi_master_break(master, 0);
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	return count;
12888c2ecf20Sopenharmony_ci}
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_cistatic DEVICE_ATTR(break, 0200, NULL, master_break_store);
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_cistatic struct attribute *master_attrs[] = {
12938c2ecf20Sopenharmony_ci	&dev_attr_break.attr,
12948c2ecf20Sopenharmony_ci	&dev_attr_rescan.attr,
12958c2ecf20Sopenharmony_ci	NULL
12968c2ecf20Sopenharmony_ci};
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(master);
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_cistatic struct class fsi_master_class = {
13018c2ecf20Sopenharmony_ci	.name = "fsi-master",
13028c2ecf20Sopenharmony_ci	.dev_groups = master_groups,
13038c2ecf20Sopenharmony_ci};
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ciint fsi_master_register(struct fsi_master *master)
13068c2ecf20Sopenharmony_ci{
13078c2ecf20Sopenharmony_ci	int rc;
13088c2ecf20Sopenharmony_ci	struct device_node *np;
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	mutex_init(&master->scan_lock);
13118c2ecf20Sopenharmony_ci	master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL);
13128c2ecf20Sopenharmony_ci	if (master->idx < 0)
13138c2ecf20Sopenharmony_ci		return master->idx;
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	dev_set_name(&master->dev, "fsi%d", master->idx);
13168c2ecf20Sopenharmony_ci	master->dev.class = &fsi_master_class;
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	rc = device_register(&master->dev);
13198c2ecf20Sopenharmony_ci	if (rc) {
13208c2ecf20Sopenharmony_ci		ida_simple_remove(&master_ida, master->idx);
13218c2ecf20Sopenharmony_ci		return rc;
13228c2ecf20Sopenharmony_ci	}
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	np = dev_of_node(&master->dev);
13258c2ecf20Sopenharmony_ci	if (!of_property_read_bool(np, "no-scan-on-init")) {
13268c2ecf20Sopenharmony_ci		mutex_lock(&master->scan_lock);
13278c2ecf20Sopenharmony_ci		fsi_master_scan(master);
13288c2ecf20Sopenharmony_ci		mutex_unlock(&master->scan_lock);
13298c2ecf20Sopenharmony_ci	}
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	return 0;
13328c2ecf20Sopenharmony_ci}
13338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_master_register);
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_civoid fsi_master_unregister(struct fsi_master *master)
13368c2ecf20Sopenharmony_ci{
13378c2ecf20Sopenharmony_ci	if (master->idx >= 0) {
13388c2ecf20Sopenharmony_ci		ida_simple_remove(&master_ida, master->idx);
13398c2ecf20Sopenharmony_ci		master->idx = -1;
13408c2ecf20Sopenharmony_ci	}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	mutex_lock(&master->scan_lock);
13438c2ecf20Sopenharmony_ci	fsi_master_unscan(master);
13448c2ecf20Sopenharmony_ci	mutex_unlock(&master->scan_lock);
13458c2ecf20Sopenharmony_ci	device_unregister(&master->dev);
13468c2ecf20Sopenharmony_ci}
13478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_master_unregister);
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci/* FSI core & Linux bus type definitions */
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_cistatic int fsi_bus_match(struct device *dev, struct device_driver *drv)
13528c2ecf20Sopenharmony_ci{
13538c2ecf20Sopenharmony_ci	struct fsi_device *fsi_dev = to_fsi_dev(dev);
13548c2ecf20Sopenharmony_ci	struct fsi_driver *fsi_drv = to_fsi_drv(drv);
13558c2ecf20Sopenharmony_ci	const struct fsi_device_id *id;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	if (!fsi_drv->id_table)
13588c2ecf20Sopenharmony_ci		return 0;
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	for (id = fsi_drv->id_table; id->engine_type; id++) {
13618c2ecf20Sopenharmony_ci		if (id->engine_type != fsi_dev->engine_type)
13628c2ecf20Sopenharmony_ci			continue;
13638c2ecf20Sopenharmony_ci		if (id->version == FSI_VERSION_ANY ||
13648c2ecf20Sopenharmony_ci				id->version == fsi_dev->version)
13658c2ecf20Sopenharmony_ci			return 1;
13668c2ecf20Sopenharmony_ci	}
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	return 0;
13698c2ecf20Sopenharmony_ci}
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ciint fsi_driver_register(struct fsi_driver *fsi_drv)
13728c2ecf20Sopenharmony_ci{
13738c2ecf20Sopenharmony_ci	if (!fsi_drv)
13748c2ecf20Sopenharmony_ci		return -EINVAL;
13758c2ecf20Sopenharmony_ci	if (!fsi_drv->id_table)
13768c2ecf20Sopenharmony_ci		return -EINVAL;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	return driver_register(&fsi_drv->drv);
13798c2ecf20Sopenharmony_ci}
13808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_driver_register);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_civoid fsi_driver_unregister(struct fsi_driver *fsi_drv)
13838c2ecf20Sopenharmony_ci{
13848c2ecf20Sopenharmony_ci	driver_unregister(&fsi_drv->drv);
13858c2ecf20Sopenharmony_ci}
13868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_driver_unregister);
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_cistruct bus_type fsi_bus_type = {
13898c2ecf20Sopenharmony_ci	.name		= "fsi",
13908c2ecf20Sopenharmony_ci	.match		= fsi_bus_match,
13918c2ecf20Sopenharmony_ci};
13928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fsi_bus_type);
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_cistatic int __init fsi_init(void)
13958c2ecf20Sopenharmony_ci{
13968c2ecf20Sopenharmony_ci	int rc;
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	rc = alloc_chrdev_region(&fsi_base_dev, 0, FSI_CHAR_MAX_DEVICES, "fsi");
13998c2ecf20Sopenharmony_ci	if (rc)
14008c2ecf20Sopenharmony_ci		return rc;
14018c2ecf20Sopenharmony_ci	rc = bus_register(&fsi_bus_type);
14028c2ecf20Sopenharmony_ci	if (rc)
14038c2ecf20Sopenharmony_ci		goto fail_bus;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	rc = class_register(&fsi_master_class);
14068c2ecf20Sopenharmony_ci	if (rc)
14078c2ecf20Sopenharmony_ci		goto fail_class;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	return 0;
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci fail_class:
14128c2ecf20Sopenharmony_ci	bus_unregister(&fsi_bus_type);
14138c2ecf20Sopenharmony_ci fail_bus:
14148c2ecf20Sopenharmony_ci	unregister_chrdev_region(fsi_base_dev, FSI_CHAR_MAX_DEVICES);
14158c2ecf20Sopenharmony_ci	return rc;
14168c2ecf20Sopenharmony_ci}
14178c2ecf20Sopenharmony_cipostcore_initcall(fsi_init);
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_cistatic void fsi_exit(void)
14208c2ecf20Sopenharmony_ci{
14218c2ecf20Sopenharmony_ci	class_unregister(&fsi_master_class);
14228c2ecf20Sopenharmony_ci	bus_unregister(&fsi_bus_type);
14238c2ecf20Sopenharmony_ci	unregister_chrdev_region(fsi_base_dev, FSI_CHAR_MAX_DEVICES);
14248c2ecf20Sopenharmony_ci	ida_destroy(&fsi_minor_ida);
14258c2ecf20Sopenharmony_ci}
14268c2ecf20Sopenharmony_cimodule_exit(fsi_exit);
14278c2ecf20Sopenharmony_cimodule_param(discard_errors, int, 0664);
14288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
14298c2ecf20Sopenharmony_ciMODULE_PARM_DESC(discard_errors, "Don't invoke error handling on bus accesses");
1430