brintos

brintos / linux-shallow public Read only

0
0
Text · 6.9 KiB · 40f1f4d Raw
317 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright (C) IBM Corporation 2023 */3 4#include <linux/device.h>5#include <linux/fsi.h>6#include <linux/i2c.h>7#include <linux/module.h>8#include <linux/mod_devicetable.h>9#include <linux/mutex.h>10 11#include "fsi-master-i2cr.h"12 13#define CREATE_TRACE_POINTS14#include <trace/events/fsi_master_i2cr.h>15 16#define I2CR_ADDRESS_CFAM(a)	((a) >> 2)17#define I2CR_INITIAL_PARITY	true18 19#define I2CR_STATUS_CMD		0x6000220#define  I2CR_STATUS_ERR	 BIT_ULL(61)21#define I2CR_ERROR_CMD		0x6000422#define I2CR_LOG_CMD		0x6000823 24static const u8 i2cr_cfam[] = {25	0xc0, 0x02, 0x0d, 0xa6,26	0x80, 0x01, 0x10, 0x02,27	0x80, 0x01, 0x10, 0x02,28	0x80, 0x01, 0x10, 0x02,29	0x80, 0x01, 0x80, 0x52,30	0x80, 0x01, 0x10, 0x02,31	0x80, 0x01, 0x10, 0x02,32	0x80, 0x01, 0x10, 0x02,33	0x80, 0x01, 0x10, 0x02,34	0x80, 0x01, 0x22, 0x2d,35	0x00, 0x00, 0x00, 0x00,36	0xde, 0xad, 0xc0, 0xde37};38 39static bool i2cr_check_parity32(u32 v, bool parity)40{41	u32 i;42 43	for (i = 0; i < 32; ++i) {44		if (v & (1u << i))45			parity = !parity;46	}47 48	return parity;49}50 51static bool i2cr_check_parity64(u64 v)52{53	u32 i;54	bool parity = I2CR_INITIAL_PARITY;55 56	for (i = 0; i < 64; ++i) {57		if (v & (1llu << i))58			parity = !parity;59	}60 61	return parity;62}63 64static u32 i2cr_get_command(u32 address, bool parity)65{66	address <<= 1;67 68	if (i2cr_check_parity32(address, parity))69		address |= 1;70 71	return address;72}73 74static int i2cr_transfer(struct i2c_client *client, u32 command, u64 *data)75{76	struct i2c_msg msgs[2];77	int ret;78 79	msgs[0].addr = client->addr;80	msgs[0].flags = 0;81	msgs[0].len = sizeof(command);82	msgs[0].buf = (__u8 *)&command;83	msgs[1].addr = client->addr;84	msgs[1].flags = I2C_M_RD;85	msgs[1].len = sizeof(*data);86	msgs[1].buf = (__u8 *)data;87 88	ret = i2c_transfer(client->adapter, msgs, 2);89	if (ret == 2)90		return 0;91 92	trace_i2cr_i2c_error(client, command, ret);93 94	if (ret < 0)95		return ret;96 97	return -EIO;98}99 100static int i2cr_check_status(struct i2c_client *client)101{102	u64 status;103	int ret;104 105	ret = i2cr_transfer(client, I2CR_STATUS_CMD, &status);106	if (ret)107		return ret;108 109	if (status & I2CR_STATUS_ERR) {110		u32 buf[3] = { 0, 0, 0 };111		u64 error;112		u64 log;113 114		i2cr_transfer(client, I2CR_ERROR_CMD, &error);115		i2cr_transfer(client, I2CR_LOG_CMD, &log);116 117		trace_i2cr_status_error(client, status, error, log);118 119		buf[0] = I2CR_STATUS_CMD;120		i2c_master_send(client, (const char *)buf, sizeof(buf));121 122		buf[0] = I2CR_ERROR_CMD;123		i2c_master_send(client, (const char *)buf, sizeof(buf));124 125		buf[0] = I2CR_LOG_CMD;126		i2c_master_send(client, (const char *)buf, sizeof(buf));127 128		dev_err(&client->dev, "status:%016llx error:%016llx log:%016llx\n", status, error,129			log);130		return -EREMOTEIO;131	}132 133	trace_i2cr_status(client, status);134	return 0;135}136 137int fsi_master_i2cr_read(struct fsi_master_i2cr *i2cr, u32 addr, u64 *data)138{139	u32 command = i2cr_get_command(addr, I2CR_INITIAL_PARITY);140	int ret;141 142	mutex_lock(&i2cr->lock);143 144	ret = i2cr_transfer(i2cr->client, command, data);145	if (ret)146		goto unlock;147 148	ret = i2cr_check_status(i2cr->client);149	if (ret)150		goto unlock;151 152	trace_i2cr_read(i2cr->client, command, data);153 154unlock:155	mutex_unlock(&i2cr->lock);156	return ret;157}158EXPORT_SYMBOL_GPL(fsi_master_i2cr_read);159 160int fsi_master_i2cr_write(struct fsi_master_i2cr *i2cr, u32 addr, u64 data)161{162	u32 buf[3] = { 0 };163	int ret;164 165	buf[0] = i2cr_get_command(addr, i2cr_check_parity64(data));166	memcpy(&buf[1], &data, sizeof(data));167 168	mutex_lock(&i2cr->lock);169 170	ret = i2c_master_send(i2cr->client, (const char *)buf, sizeof(buf));171	if (ret == sizeof(buf)) {172		ret = i2cr_check_status(i2cr->client);173		if (!ret)174			trace_i2cr_write(i2cr->client, buf[0], data);175	} else {176		trace_i2cr_i2c_error(i2cr->client, buf[0], ret);177 178		if (ret >= 0)179			ret = -EIO;180	}181 182	mutex_unlock(&i2cr->lock);183	return ret;184}185EXPORT_SYMBOL_GPL(fsi_master_i2cr_write);186 187static int i2cr_read(struct fsi_master *master, int link, uint8_t id, uint32_t addr, void *val,188		     size_t size)189{190	struct fsi_master_i2cr *i2cr = container_of(master, struct fsi_master_i2cr, master);191	u64 data;192	size_t i;193	int ret;194 195	if (link || id || (addr & 0xffff0000) || !(size == 1 || size == 2 || size == 4))196		return -EINVAL;197 198	/*199	 * The I2CR doesn't have CFAM or FSI slave address space - only the200	 * engines. In order for this to work with the FSI core, we need to201	 * emulate at minimum the CFAM config table so that the appropriate202	 * engines are discovered.203	 */204	if (addr < 0xc00) {205		if (addr > sizeof(i2cr_cfam) - 4)206			addr = (addr & 0x3) + (sizeof(i2cr_cfam) - 4);207 208		memcpy(val, &i2cr_cfam[addr], size);209		return 0;210	}211 212	ret = fsi_master_i2cr_read(i2cr, I2CR_ADDRESS_CFAM(addr), &data);213	if (ret)214		return ret;215 216	/*217	 * FSI core expects up to 4 bytes BE back, while I2CR replied with LE218	 * bytes on the wire.219	 */220	for (i = 0; i < size; ++i)221		((u8 *)val)[i] = ((u8 *)&data)[7 - i];222 223	return 0;224}225 226static int i2cr_write(struct fsi_master *master, int link, uint8_t id, uint32_t addr,227		      const void *val, size_t size)228{229	struct fsi_master_i2cr *i2cr = container_of(master, struct fsi_master_i2cr, master);230	u64 data = 0;231	size_t i;232 233	if (link || id || (addr & 0xffff0000) || !(size == 1 || size == 2 || size == 4))234		return -EINVAL;235 236	/* I2CR writes to CFAM or FSI slave address are a successful no-op. */237	if (addr < 0xc00)238		return 0;239 240	/*241	 * FSI core passes up to 4 bytes BE, while the I2CR expects LE bytes on242	 * the wire.243	 */244	for (i = 0; i < size; ++i)245		((u8 *)&data)[7 - i] = ((u8 *)val)[i];246 247	return fsi_master_i2cr_write(i2cr, I2CR_ADDRESS_CFAM(addr), data);248}249 250static void i2cr_release(struct device *dev)251{252	struct fsi_master_i2cr *i2cr = to_fsi_master_i2cr(to_fsi_master(dev));253 254	of_node_put(dev->of_node);255 256	kfree(i2cr);257}258 259static int i2cr_probe(struct i2c_client *client)260{261	struct fsi_master_i2cr *i2cr;262	int ret;263 264	i2cr = kzalloc(sizeof(*i2cr), GFP_KERNEL);265	if (!i2cr)266		return -ENOMEM;267 268	/* Only one I2CR on any given I2C bus (fixed I2C device address) */269	i2cr->master.idx = client->adapter->nr;270	dev_set_name(&i2cr->master.dev, "i2cr%d", i2cr->master.idx);271	i2cr->master.dev.parent = &client->dev;272	i2cr->master.dev.of_node = of_node_get(dev_of_node(&client->dev));273	i2cr->master.dev.release = i2cr_release;274 275	i2cr->master.n_links = 1;276	i2cr->master.read = i2cr_read;277	i2cr->master.write = i2cr_write;278 279	mutex_init(&i2cr->lock);280	i2cr->client = client;281 282	ret = fsi_master_register(&i2cr->master);283	if (ret)284		return ret;285 286	i2c_set_clientdata(client, i2cr);287	return 0;288}289 290static void i2cr_remove(struct i2c_client *client)291{292	struct fsi_master_i2cr *i2cr = i2c_get_clientdata(client);293 294	fsi_master_unregister(&i2cr->master);295}296 297static const struct of_device_id i2cr_ids[] = {298	{ .compatible = "ibm,i2cr-fsi-master" },299	{ }300};301MODULE_DEVICE_TABLE(of, i2cr_ids);302 303static struct i2c_driver i2cr_driver = {304	.probe = i2cr_probe,305	.remove = i2cr_remove,306	.driver = {307		.name = "fsi-master-i2cr",308		.of_match_table = i2cr_ids,309	},310};311 312module_i2c_driver(i2cr_driver)313 314MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");315MODULE_DESCRIPTION("IBM I2C Responder virtual FSI master driver");316MODULE_LICENSE("GPL");317