brintos

brintos / linux-shallow public Read only

0
0
Text · 6.5 KiB · d9dd71c Raw
277 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * IBM OPAL I2C driver4 * Copyright (C) 2014 IBM5 */6 7#include <linux/device.h>8#include <linux/i2c.h>9#include <linux/kernel.h>10#include <linux/mm.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/platform_device.h>14#include <linux/slab.h>15 16#include <asm/firmware.h>17#include <asm/opal.h>18 19static int i2c_opal_translate_error(int rc)20{21	switch (rc) {22	case OPAL_NO_MEM:23		return -ENOMEM;24	case OPAL_PARAMETER:25		return -EINVAL;26	case OPAL_I2C_ARBT_LOST:27		return -EAGAIN;28	case OPAL_I2C_TIMEOUT:29		return -ETIMEDOUT;30	case OPAL_I2C_NACK_RCVD:31		return -ENXIO;32	case OPAL_I2C_STOP_ERR:33		return -EBUSY;34	default:35		return -EIO;36	}37}38 39static int i2c_opal_send_request(u32 bus_id, struct opal_i2c_request *req)40{41	struct opal_msg msg;42	int token, rc;43 44	token = opal_async_get_token_interruptible();45	if (token < 0) {46		if (token != -ERESTARTSYS)47			pr_err("Failed to get the async token\n");48 49		return token;50	}51 52	rc = opal_i2c_request(token, bus_id, req);53	if (rc != OPAL_ASYNC_COMPLETION) {54		rc = i2c_opal_translate_error(rc);55		goto exit;56	}57 58	rc = opal_async_wait_response(token, &msg);59	if (rc)60		goto exit;61 62	rc = opal_get_async_rc(msg);63	if (rc != OPAL_SUCCESS) {64		rc = i2c_opal_translate_error(rc);65		goto exit;66	}67 68exit:69	opal_async_release_token(token);70	return rc;71}72 73static int i2c_opal_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,74			 int num)75{76	unsigned long opal_id = (unsigned long)adap->algo_data;77	struct opal_i2c_request req;78	int rc, i;79 80	/* We only support fairly simple combinations here of one81	 * or two messages82	 */83	memset(&req, 0, sizeof(req));84	switch(num) {85	case 1:86		req.type = (msgs[0].flags & I2C_M_RD) ?87			OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE;88		req.addr = cpu_to_be16(msgs[0].addr);89		req.size = cpu_to_be32(msgs[0].len);90		req.buffer_ra = cpu_to_be64(__pa(msgs[0].buf));91		break;92	case 2:93		req.type = (msgs[1].flags & I2C_M_RD) ?94			OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;95		req.addr = cpu_to_be16(msgs[0].addr);96		req.subaddr_sz = msgs[0].len;97		for (i = 0; i < msgs[0].len; i++)98			req.subaddr = (req.subaddr << 8) | msgs[0].buf[i];99		req.subaddr = cpu_to_be32(req.subaddr);100		req.size = cpu_to_be32(msgs[1].len);101		req.buffer_ra = cpu_to_be64(__pa(msgs[1].buf));102		break;103	}104 105	rc = i2c_opal_send_request(opal_id, &req);106	if (rc)107		return rc;108 109	return num;110}111 112static int i2c_opal_smbus_xfer(struct i2c_adapter *adap, u16 addr,113			       unsigned short flags, char read_write,114			       u8 command, int size, union i2c_smbus_data *data)115{116	unsigned long opal_id = (unsigned long)adap->algo_data;117	struct opal_i2c_request req;118	u8 local[2];119	int rc;120 121	memset(&req, 0, sizeof(req));122 123	req.addr = cpu_to_be16(addr);124	switch (size) {125	case I2C_SMBUS_BYTE:126		req.buffer_ra = cpu_to_be64(__pa(&data->byte));127		req.size = cpu_to_be32(1);128		fallthrough;129	case I2C_SMBUS_QUICK:130		req.type = (read_write == I2C_SMBUS_READ) ?131			OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE;132		break;133	case I2C_SMBUS_BYTE_DATA:134		req.buffer_ra = cpu_to_be64(__pa(&data->byte));135		req.size = cpu_to_be32(1);136		req.subaddr = cpu_to_be32(command);137		req.subaddr_sz = 1;138		req.type = (read_write == I2C_SMBUS_READ) ?139			OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;140		break;141	case I2C_SMBUS_WORD_DATA:142		if (!read_write) {143			local[0] = data->word & 0xff;144			local[1] = (data->word >> 8) & 0xff;145		}146		req.buffer_ra = cpu_to_be64(__pa(local));147		req.size = cpu_to_be32(2);148		req.subaddr = cpu_to_be32(command);149		req.subaddr_sz = 1;150		req.type = (read_write == I2C_SMBUS_READ) ?151			OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;152		break;153	case I2C_SMBUS_I2C_BLOCK_DATA:154		req.buffer_ra = cpu_to_be64(__pa(&data->block[1]));155		req.size = cpu_to_be32(data->block[0]);156		req.subaddr = cpu_to_be32(command);157		req.subaddr_sz = 1;158		req.type = (read_write == I2C_SMBUS_READ) ?159			OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;160		break;161	default:162		return -EINVAL;163	}164 165	rc = i2c_opal_send_request(opal_id, &req);166	if (!rc && read_write && size == I2C_SMBUS_WORD_DATA) {167		data->word = ((u16)local[1]) << 8;168		data->word |= local[0];169	}170 171	return rc;172}173 174static u32 i2c_opal_func(struct i2c_adapter *adapter)175{176	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |177	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |178	       I2C_FUNC_SMBUS_I2C_BLOCK;179}180 181static const struct i2c_algorithm i2c_opal_algo = {182	.xfer = i2c_opal_xfer,183	.smbus_xfer = i2c_opal_smbus_xfer,184	.functionality = i2c_opal_func,185};186 187/*188 * For two messages, we basically support simple smbus transactions of a189 * write-then-anything.190 */191static const struct i2c_adapter_quirks i2c_opal_quirks = {192	.flags = I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | I2C_AQ_COMB_SAME_ADDR,193	.max_comb_1st_msg_len = 4,194};195 196static int i2c_opal_probe(struct platform_device *pdev)197{198	struct i2c_adapter	*adapter;199	const char		*pname;200	u32			opal_id;201	int			rc;202 203	if (!pdev->dev.of_node)204		return -ENODEV;205 206	rc = of_property_read_u32(pdev->dev.of_node, "ibm,opal-id", &opal_id);207	if (rc) {208		dev_err(&pdev->dev, "Missing ibm,opal-id property !\n");209		return -EIO;210	}211 212	adapter = devm_kzalloc(&pdev->dev, sizeof(*adapter), GFP_KERNEL);213	if (!adapter)214		return -ENOMEM;215 216	adapter->algo = &i2c_opal_algo;217	adapter->algo_data = (void *)(unsigned long)opal_id;218	adapter->quirks = &i2c_opal_quirks;219	adapter->dev.parent = &pdev->dev;220	adapter->dev.of_node = of_node_get(pdev->dev.of_node);221	pname = of_get_property(pdev->dev.of_node, "ibm,port-name", NULL);222	if (pname)223		strscpy(adapter->name, pname, sizeof(adapter->name));224	else225		strscpy(adapter->name, "opal", sizeof(adapter->name));226 227	platform_set_drvdata(pdev, adapter);228	rc = i2c_add_adapter(adapter);229	if (rc)230		dev_err(&pdev->dev, "Failed to register the i2c adapter\n");231 232	return rc;233}234 235static void i2c_opal_remove(struct platform_device *pdev)236{237	struct i2c_adapter *adapter = platform_get_drvdata(pdev);238 239	i2c_del_adapter(adapter);240}241 242static const struct of_device_id i2c_opal_of_match[] = {243	{244		.compatible = "ibm,opal-i2c",245	},246	{ }247};248MODULE_DEVICE_TABLE(of, i2c_opal_of_match);249 250static struct platform_driver i2c_opal_driver = {251	.probe	= i2c_opal_probe,252	.remove_new = i2c_opal_remove,253	.driver	= {254		.name		= "i2c-opal",255		.of_match_table	= i2c_opal_of_match,256	},257};258 259static int __init i2c_opal_init(void)260{261	if (!firmware_has_feature(FW_FEATURE_OPAL))262		return -ENODEV;263 264	return platform_driver_register(&i2c_opal_driver);265}266module_init(i2c_opal_init);267 268static void __exit i2c_opal_exit(void)269{270	return platform_driver_unregister(&i2c_opal_driver);271}272module_exit(i2c_opal_exit);273 274MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");275MODULE_DESCRIPTION("IBM OPAL I2C driver");276MODULE_LICENSE("GPL");277