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