xref: /openbmc/linux/drivers/i2c/busses/i2c-opal.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1d5bb994bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
247083450SNeelesh Gupta /*
347083450SNeelesh Gupta  * IBM OPAL I2C driver
447083450SNeelesh Gupta  * Copyright (C) 2014 IBM
547083450SNeelesh Gupta  */
647083450SNeelesh Gupta 
747083450SNeelesh Gupta #include <linux/device.h>
847083450SNeelesh Gupta #include <linux/i2c.h>
947083450SNeelesh Gupta #include <linux/kernel.h>
1047083450SNeelesh Gupta #include <linux/mm.h>
1147083450SNeelesh Gupta #include <linux/module.h>
1247083450SNeelesh Gupta #include <linux/of.h>
1347083450SNeelesh Gupta #include <linux/platform_device.h>
1447083450SNeelesh Gupta #include <linux/slab.h>
1547083450SNeelesh Gupta 
1647083450SNeelesh Gupta #include <asm/firmware.h>
1747083450SNeelesh Gupta #include <asm/opal.h>
1847083450SNeelesh Gupta 
i2c_opal_translate_error(int rc)1947083450SNeelesh Gupta static int i2c_opal_translate_error(int rc)
2047083450SNeelesh Gupta {
2147083450SNeelesh Gupta 	switch (rc) {
2247083450SNeelesh Gupta 	case OPAL_NO_MEM:
2347083450SNeelesh Gupta 		return -ENOMEM;
2447083450SNeelesh Gupta 	case OPAL_PARAMETER:
2547083450SNeelesh Gupta 		return -EINVAL;
2647083450SNeelesh Gupta 	case OPAL_I2C_ARBT_LOST:
2747083450SNeelesh Gupta 		return -EAGAIN;
2847083450SNeelesh Gupta 	case OPAL_I2C_TIMEOUT:
2947083450SNeelesh Gupta 		return -ETIMEDOUT;
3047083450SNeelesh Gupta 	case OPAL_I2C_NACK_RCVD:
3147083450SNeelesh Gupta 		return -ENXIO;
3247083450SNeelesh Gupta 	case OPAL_I2C_STOP_ERR:
3347083450SNeelesh Gupta 		return -EBUSY;
3447083450SNeelesh Gupta 	default:
3547083450SNeelesh Gupta 		return -EIO;
3647083450SNeelesh Gupta 	}
3747083450SNeelesh Gupta }
3847083450SNeelesh Gupta 
i2c_opal_send_request(u32 bus_id,struct opal_i2c_request * req)3947083450SNeelesh Gupta static int i2c_opal_send_request(u32 bus_id, struct opal_i2c_request *req)
4047083450SNeelesh Gupta {
4147083450SNeelesh Gupta 	struct opal_msg msg;
4247083450SNeelesh Gupta 	int token, rc;
4347083450SNeelesh Gupta 
4447083450SNeelesh Gupta 	token = opal_async_get_token_interruptible();
4547083450SNeelesh Gupta 	if (token < 0) {
4647083450SNeelesh Gupta 		if (token != -ERESTARTSYS)
4747083450SNeelesh Gupta 			pr_err("Failed to get the async token\n");
4847083450SNeelesh Gupta 
4947083450SNeelesh Gupta 		return token;
5047083450SNeelesh Gupta 	}
5147083450SNeelesh Gupta 
5247083450SNeelesh Gupta 	rc = opal_i2c_request(token, bus_id, req);
5347083450SNeelesh Gupta 	if (rc != OPAL_ASYNC_COMPLETION) {
5447083450SNeelesh Gupta 		rc = i2c_opal_translate_error(rc);
5547083450SNeelesh Gupta 		goto exit;
5647083450SNeelesh Gupta 	}
5747083450SNeelesh Gupta 
5847083450SNeelesh Gupta 	rc = opal_async_wait_response(token, &msg);
5947083450SNeelesh Gupta 	if (rc)
6047083450SNeelesh Gupta 		goto exit;
6147083450SNeelesh Gupta 
62d0226d31SSuraj Jitindar Singh 	rc = opal_get_async_rc(msg);
6347083450SNeelesh Gupta 	if (rc != OPAL_SUCCESS) {
6447083450SNeelesh Gupta 		rc = i2c_opal_translate_error(rc);
6547083450SNeelesh Gupta 		goto exit;
6647083450SNeelesh Gupta 	}
6747083450SNeelesh Gupta 
6847083450SNeelesh Gupta exit:
6947083450SNeelesh Gupta 	opal_async_release_token(token);
7047083450SNeelesh Gupta 	return rc;
7147083450SNeelesh Gupta }
7247083450SNeelesh Gupta 
i2c_opal_master_xfer(struct i2c_adapter * adap,struct i2c_msg * msgs,int num)7347083450SNeelesh Gupta static int i2c_opal_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
7447083450SNeelesh Gupta 				int num)
7547083450SNeelesh Gupta {
7647083450SNeelesh Gupta 	unsigned long opal_id = (unsigned long)adap->algo_data;
7747083450SNeelesh Gupta 	struct opal_i2c_request req;
7847083450SNeelesh Gupta 	int rc, i;
7947083450SNeelesh Gupta 
8047083450SNeelesh Gupta 	/* We only support fairly simple combinations here of one
8147083450SNeelesh Gupta 	 * or two messages
8247083450SNeelesh Gupta 	 */
8347083450SNeelesh Gupta 	memset(&req, 0, sizeof(req));
8447083450SNeelesh Gupta 	switch(num) {
8547083450SNeelesh Gupta 	case 1:
8647083450SNeelesh Gupta 		req.type = (msgs[0].flags & I2C_M_RD) ?
8747083450SNeelesh Gupta 			OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE;
8847083450SNeelesh Gupta 		req.addr = cpu_to_be16(msgs[0].addr);
8947083450SNeelesh Gupta 		req.size = cpu_to_be32(msgs[0].len);
9047083450SNeelesh Gupta 		req.buffer_ra = cpu_to_be64(__pa(msgs[0].buf));
9147083450SNeelesh Gupta 		break;
9247083450SNeelesh Gupta 	case 2:
93bf070380SNeelesh Gupta 		req.type = (msgs[1].flags & I2C_M_RD) ?
94bf070380SNeelesh Gupta 			OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;
9547083450SNeelesh Gupta 		req.addr = cpu_to_be16(msgs[0].addr);
9647083450SNeelesh Gupta 		req.subaddr_sz = msgs[0].len;
9747083450SNeelesh Gupta 		for (i = 0; i < msgs[0].len; i++)
9847083450SNeelesh Gupta 			req.subaddr = (req.subaddr << 8) | msgs[0].buf[i];
9947083450SNeelesh Gupta 		req.subaddr = cpu_to_be32(req.subaddr);
10047083450SNeelesh Gupta 		req.size = cpu_to_be32(msgs[1].len);
10147083450SNeelesh Gupta 		req.buffer_ra = cpu_to_be64(__pa(msgs[1].buf));
10247083450SNeelesh Gupta 		break;
10347083450SNeelesh Gupta 	}
10447083450SNeelesh Gupta 
10547083450SNeelesh Gupta 	rc = i2c_opal_send_request(opal_id, &req);
10647083450SNeelesh Gupta 	if (rc)
10747083450SNeelesh Gupta 		return rc;
10847083450SNeelesh Gupta 
10947083450SNeelesh Gupta 	return num;
11047083450SNeelesh Gupta }
11147083450SNeelesh Gupta 
i2c_opal_smbus_xfer(struct i2c_adapter * adap,u16 addr,unsigned short flags,char read_write,u8 command,int size,union i2c_smbus_data * data)11247083450SNeelesh Gupta static int i2c_opal_smbus_xfer(struct i2c_adapter *adap, u16 addr,
11347083450SNeelesh Gupta 			       unsigned short flags, char read_write,
11447083450SNeelesh Gupta 			       u8 command, int size, union i2c_smbus_data *data)
11547083450SNeelesh Gupta {
11647083450SNeelesh Gupta 	unsigned long opal_id = (unsigned long)adap->algo_data;
11747083450SNeelesh Gupta 	struct opal_i2c_request req;
11847083450SNeelesh Gupta 	u8 local[2];
11947083450SNeelesh Gupta 	int rc;
12047083450SNeelesh Gupta 
12147083450SNeelesh Gupta 	memset(&req, 0, sizeof(req));
12247083450SNeelesh Gupta 
12347083450SNeelesh Gupta 	req.addr = cpu_to_be16(addr);
12447083450SNeelesh Gupta 	switch (size) {
12547083450SNeelesh Gupta 	case I2C_SMBUS_BYTE:
12647083450SNeelesh Gupta 		req.buffer_ra = cpu_to_be64(__pa(&data->byte));
12747083450SNeelesh Gupta 		req.size = cpu_to_be32(1);
128df561f66SGustavo A. R. Silva 		fallthrough;
12947083450SNeelesh Gupta 	case I2C_SMBUS_QUICK:
13047083450SNeelesh Gupta 		req.type = (read_write == I2C_SMBUS_READ) ?
13147083450SNeelesh Gupta 			OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE;
13247083450SNeelesh Gupta 		break;
13347083450SNeelesh Gupta 	case I2C_SMBUS_BYTE_DATA:
13447083450SNeelesh Gupta 		req.buffer_ra = cpu_to_be64(__pa(&data->byte));
13547083450SNeelesh Gupta 		req.size = cpu_to_be32(1);
13647083450SNeelesh Gupta 		req.subaddr = cpu_to_be32(command);
13747083450SNeelesh Gupta 		req.subaddr_sz = 1;
13847083450SNeelesh Gupta 		req.type = (read_write == I2C_SMBUS_READ) ?
13947083450SNeelesh Gupta 			OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;
14047083450SNeelesh Gupta 		break;
14147083450SNeelesh Gupta 	case I2C_SMBUS_WORD_DATA:
14247083450SNeelesh Gupta 		if (!read_write) {
14347083450SNeelesh Gupta 			local[0] = data->word & 0xff;
14447083450SNeelesh Gupta 			local[1] = (data->word >> 8) & 0xff;
14547083450SNeelesh Gupta 		}
14647083450SNeelesh Gupta 		req.buffer_ra = cpu_to_be64(__pa(local));
14747083450SNeelesh Gupta 		req.size = cpu_to_be32(2);
14847083450SNeelesh Gupta 		req.subaddr = cpu_to_be32(command);
14947083450SNeelesh Gupta 		req.subaddr_sz = 1;
15047083450SNeelesh Gupta 		req.type = (read_write == I2C_SMBUS_READ) ?
15147083450SNeelesh Gupta 			OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;
15247083450SNeelesh Gupta 		break;
15347083450SNeelesh Gupta 	case I2C_SMBUS_I2C_BLOCK_DATA:
15447083450SNeelesh Gupta 		req.buffer_ra = cpu_to_be64(__pa(&data->block[1]));
15547083450SNeelesh Gupta 		req.size = cpu_to_be32(data->block[0]);
15647083450SNeelesh Gupta 		req.subaddr = cpu_to_be32(command);
15747083450SNeelesh Gupta 		req.subaddr_sz = 1;
15847083450SNeelesh Gupta 		req.type = (read_write == I2C_SMBUS_READ) ?
15947083450SNeelesh Gupta 			OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;
16047083450SNeelesh Gupta 		break;
16147083450SNeelesh Gupta 	default:
16247083450SNeelesh Gupta 		return -EINVAL;
16347083450SNeelesh Gupta 	}
16447083450SNeelesh Gupta 
16547083450SNeelesh Gupta 	rc = i2c_opal_send_request(opal_id, &req);
16647083450SNeelesh Gupta 	if (!rc && read_write && size == I2C_SMBUS_WORD_DATA) {
16747083450SNeelesh Gupta 		data->word = ((u16)local[1]) << 8;
16847083450SNeelesh Gupta 		data->word |= local[0];
16947083450SNeelesh Gupta 	}
17047083450SNeelesh Gupta 
17147083450SNeelesh Gupta 	return rc;
17247083450SNeelesh Gupta }
17347083450SNeelesh Gupta 
i2c_opal_func(struct i2c_adapter * adapter)17447083450SNeelesh Gupta static u32 i2c_opal_func(struct i2c_adapter *adapter)
17547083450SNeelesh Gupta {
17647083450SNeelesh Gupta 	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
17747083450SNeelesh Gupta 	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
17847083450SNeelesh Gupta 	       I2C_FUNC_SMBUS_I2C_BLOCK;
17947083450SNeelesh Gupta }
18047083450SNeelesh Gupta 
18147083450SNeelesh Gupta static const struct i2c_algorithm i2c_opal_algo = {
18247083450SNeelesh Gupta 	.master_xfer	= i2c_opal_master_xfer,
18347083450SNeelesh Gupta 	.smbus_xfer	= i2c_opal_smbus_xfer,
18447083450SNeelesh Gupta 	.functionality	= i2c_opal_func,
18547083450SNeelesh Gupta };
18647083450SNeelesh Gupta 
187bf070380SNeelesh Gupta /*
188bf070380SNeelesh Gupta  * For two messages, we basically support simple smbus transactions of a
189bf070380SNeelesh Gupta  * write-then-anything.
190a531afdfSWolfram Sang  */
191ae3923a2SBhumika Goyal static const struct i2c_adapter_quirks i2c_opal_quirks = {
192bf070380SNeelesh Gupta 	.flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | I2C_AQ_COMB_SAME_ADDR,
193a531afdfSWolfram Sang 	.max_comb_1st_msg_len = 4,
194a531afdfSWolfram Sang };
195a531afdfSWolfram Sang 
i2c_opal_probe(struct platform_device * pdev)19647083450SNeelesh Gupta static int i2c_opal_probe(struct platform_device *pdev)
19747083450SNeelesh Gupta {
19847083450SNeelesh Gupta 	struct i2c_adapter	*adapter;
19947083450SNeelesh Gupta 	const char		*pname;
20047083450SNeelesh Gupta 	u32			opal_id;
20147083450SNeelesh Gupta 	int			rc;
20247083450SNeelesh Gupta 
20347083450SNeelesh Gupta 	if (!pdev->dev.of_node)
20447083450SNeelesh Gupta 		return -ENODEV;
20547083450SNeelesh Gupta 
20647083450SNeelesh Gupta 	rc = of_property_read_u32(pdev->dev.of_node, "ibm,opal-id", &opal_id);
20747083450SNeelesh Gupta 	if (rc) {
20847083450SNeelesh Gupta 		dev_err(&pdev->dev, "Missing ibm,opal-id property !\n");
20947083450SNeelesh Gupta 		return -EIO;
21047083450SNeelesh Gupta 	}
21147083450SNeelesh Gupta 
21247083450SNeelesh Gupta 	adapter = devm_kzalloc(&pdev->dev, sizeof(*adapter), GFP_KERNEL);
21347083450SNeelesh Gupta 	if (!adapter)
21447083450SNeelesh Gupta 		return -ENOMEM;
21547083450SNeelesh Gupta 
21647083450SNeelesh Gupta 	adapter->algo = &i2c_opal_algo;
21747083450SNeelesh Gupta 	adapter->algo_data = (void *)(unsigned long)opal_id;
218a531afdfSWolfram Sang 	adapter->quirks = &i2c_opal_quirks;
21947083450SNeelesh Gupta 	adapter->dev.parent = &pdev->dev;
22047083450SNeelesh Gupta 	adapter->dev.of_node = of_node_get(pdev->dev.of_node);
22147083450SNeelesh Gupta 	pname = of_get_property(pdev->dev.of_node, "ibm,port-name", NULL);
22247083450SNeelesh Gupta 	if (pname)
223ea1558ceSWolfram Sang 		strscpy(adapter->name, pname, sizeof(adapter->name));
22447083450SNeelesh Gupta 	else
225ea1558ceSWolfram Sang 		strscpy(adapter->name, "opal", sizeof(adapter->name));
22647083450SNeelesh Gupta 
22747083450SNeelesh Gupta 	platform_set_drvdata(pdev, adapter);
22847083450SNeelesh Gupta 	rc = i2c_add_adapter(adapter);
22947083450SNeelesh Gupta 	if (rc)
23047083450SNeelesh Gupta 		dev_err(&pdev->dev, "Failed to register the i2c adapter\n");
23147083450SNeelesh Gupta 
23247083450SNeelesh Gupta 	return rc;
23347083450SNeelesh Gupta }
23447083450SNeelesh Gupta 
i2c_opal_remove(struct platform_device * pdev)235*e190a0c3SUwe Kleine-König static void i2c_opal_remove(struct platform_device *pdev)
23647083450SNeelesh Gupta {
23747083450SNeelesh Gupta 	struct i2c_adapter *adapter = platform_get_drvdata(pdev);
23847083450SNeelesh Gupta 
23947083450SNeelesh Gupta 	i2c_del_adapter(adapter);
24047083450SNeelesh Gupta }
24147083450SNeelesh Gupta 
24247083450SNeelesh Gupta static const struct of_device_id i2c_opal_of_match[] = {
24347083450SNeelesh Gupta 	{
24447083450SNeelesh Gupta 		.compatible = "ibm,opal-i2c",
24547083450SNeelesh Gupta 	},
24647083450SNeelesh Gupta 	{ }
24747083450SNeelesh Gupta };
24847083450SNeelesh Gupta MODULE_DEVICE_TABLE(of, i2c_opal_of_match);
24947083450SNeelesh Gupta 
25047083450SNeelesh Gupta static struct platform_driver i2c_opal_driver = {
25147083450SNeelesh Gupta 	.probe	= i2c_opal_probe,
252*e190a0c3SUwe Kleine-König 	.remove_new = i2c_opal_remove,
25347083450SNeelesh Gupta 	.driver	= {
25447083450SNeelesh Gupta 		.name		= "i2c-opal",
25547083450SNeelesh Gupta 		.of_match_table	= i2c_opal_of_match,
25647083450SNeelesh Gupta 	},
25747083450SNeelesh Gupta };
25847083450SNeelesh Gupta 
i2c_opal_init(void)25947083450SNeelesh Gupta static int __init i2c_opal_init(void)
26047083450SNeelesh Gupta {
26147083450SNeelesh Gupta 	if (!firmware_has_feature(FW_FEATURE_OPAL))
26247083450SNeelesh Gupta 		return -ENODEV;
26347083450SNeelesh Gupta 
26447083450SNeelesh Gupta 	return platform_driver_register(&i2c_opal_driver);
26547083450SNeelesh Gupta }
26647083450SNeelesh Gupta module_init(i2c_opal_init);
26747083450SNeelesh Gupta 
i2c_opal_exit(void)26847083450SNeelesh Gupta static void __exit i2c_opal_exit(void)
26947083450SNeelesh Gupta {
27047083450SNeelesh Gupta 	return platform_driver_unregister(&i2c_opal_driver);
27147083450SNeelesh Gupta }
27247083450SNeelesh Gupta module_exit(i2c_opal_exit);
27347083450SNeelesh Gupta 
27447083450SNeelesh Gupta MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");
27547083450SNeelesh Gupta MODULE_DESCRIPTION("IBM OPAL I2C driver");
27647083450SNeelesh Gupta MODULE_LICENSE("GPL");
277