aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorNeelesh Gupta <neelegup@linux.vnet.ibm.com>2014-12-13 13:01:05 -0500
committerMichael Ellerman <mpe@ellerman.id.au>2014-12-13 20:44:46 -0500
commit470834508f87877f680738a10a305280582c7aed (patch)
tree62cf343202cd77e25285d164bad5c5f6c0bc4877 /drivers/i2c
parent63f13448d81c910a284b096149411a719cbed501 (diff)
i2c: Driver to expose PowerNV platform i2c busses
The patch exposes the available i2c busses on the PowerNV platform to the kernel and implements the bus driver to support i2c and smbus commands. The driver uses the platform device infrastructure to probe the busses on the platform and registers them with the i2c driver framework. Signed-off-by: Neelesh Gupta <neelegup@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Acked-by: Wolfram Sang <wsa@the-dreams.de> (I2C part, excluding the bindings) Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/Kconfig11
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-opal.c294
3 files changed, 306 insertions, 0 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 917c3585f45b..71ad6e11efb7 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -1044,4 +1044,15 @@ config SCx200_ACB
1044 This support is also available as a module. If so, the module 1044 This support is also available as a module. If so, the module
1045 will be called scx200_acb. 1045 will be called scx200_acb.
1046 1046
1047config I2C_OPAL
1048 tristate "IBM OPAL I2C driver"
1049 depends on PPC_POWERNV
1050 default y
1051 help
1052 This exposes the PowerNV platform i2c busses to the linux i2c layer,
1053 the driver is based on the OPAL interfaces.
1054
1055 This driver can also be built as a module. If so, the module will be
1056 called as i2c-opal.
1057
1047endmenu 1058endmenu
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 78d56c54ba2b..e23ec815e21f 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -99,6 +99,7 @@ obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o
99obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o 99obj-$(CONFIG_I2C_BCM_KONA) += i2c-bcm-kona.o
100obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o 100obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o
101obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o 101obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
102obj-$(CONFIG_I2C_OPAL) += i2c-opal.o
102obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o 103obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
103obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o 104obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
104obj-$(CONFIG_SCx200_ACB) += scx200_acb.o 105obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
diff --git a/drivers/i2c/busses/i2c-opal.c b/drivers/i2c/busses/i2c-opal.c
new file mode 100644
index 000000000000..16f90b1a7508
--- /dev/null
+++ b/drivers/i2c/busses/i2c-opal.c
@@ -0,0 +1,294 @@
1/*
2 * IBM OPAL I2C driver
3 * Copyright (C) 2014 IBM
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.
17 */
18
19#include <linux/device.h>
20#include <linux/i2c.h>
21#include <linux/kernel.h>
22#include <linux/mm.h>
23#include <linux/module.h>
24#include <linux/of.h>
25#include <linux/platform_device.h>
26#include <linux/slab.h>
27
28#include <asm/firmware.h>
29#include <asm/opal.h>
30
31static int i2c_opal_translate_error(int rc)
32{
33 switch (rc) {
34 case OPAL_NO_MEM:
35 return -ENOMEM;
36 case OPAL_PARAMETER:
37 return -EINVAL;
38 case OPAL_I2C_ARBT_LOST:
39 return -EAGAIN;
40 case OPAL_I2C_TIMEOUT:
41 return -ETIMEDOUT;
42 case OPAL_I2C_NACK_RCVD:
43 return -ENXIO;
44 case OPAL_I2C_STOP_ERR:
45 return -EBUSY;
46 default:
47 return -EIO;
48 }
49}
50
51static int i2c_opal_send_request(u32 bus_id, struct opal_i2c_request *req)
52{
53 struct opal_msg msg;
54 int token, rc;
55
56 token = opal_async_get_token_interruptible();
57 if (token < 0) {
58 if (token != -ERESTARTSYS)
59 pr_err("Failed to get the async token\n");
60
61 return token;
62 }
63
64 rc = opal_i2c_request(token, bus_id, req);
65 if (rc != OPAL_ASYNC_COMPLETION) {
66 rc = i2c_opal_translate_error(rc);
67 goto exit;
68 }
69
70 rc = opal_async_wait_response(token, &msg);
71 if (rc)
72 goto exit;
73
74 rc = be64_to_cpu(msg.params[1]);
75 if (rc != OPAL_SUCCESS) {
76 rc = i2c_opal_translate_error(rc);
77 goto exit;
78 }
79
80exit:
81 opal_async_release_token(token);
82 return rc;
83}
84
85static int i2c_opal_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
86 int num)
87{
88 unsigned long opal_id = (unsigned long)adap->algo_data;
89 struct opal_i2c_request req;
90 int rc, i;
91
92 /* We only support fairly simple combinations here of one
93 * or two messages
94 */
95 memset(&req, 0, sizeof(req));
96 switch(num) {
97 case 0:
98 return 0;
99 case 1:
100 req.type = (msgs[0].flags & I2C_M_RD) ?
101 OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE;
102 req.addr = cpu_to_be16(msgs[0].addr);
103 req.size = cpu_to_be32(msgs[0].len);
104 req.buffer_ra = cpu_to_be64(__pa(msgs[0].buf));
105 break;
106 case 2:
107 /* For two messages, we basically support only simple
108 * smbus transactions of a write plus a read. We might
109 * want to allow also two writes but we'd have to bounce
110 * the data into a single buffer.
111 */
112 if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD))
113 return -EOPNOTSUPP;
114 if (msgs[0].len > 4)
115 return -EOPNOTSUPP;
116 if (msgs[0].addr != msgs[1].addr)
117 return -EOPNOTSUPP;
118 req.type = OPAL_I2C_SM_READ;
119 req.addr = cpu_to_be16(msgs[0].addr);
120 req.subaddr_sz = msgs[0].len;
121 for (i = 0; i < msgs[0].len; i++)
122 req.subaddr = (req.subaddr << 8) | msgs[0].buf[i];
123 req.subaddr = cpu_to_be32(req.subaddr);
124 req.size = cpu_to_be32(msgs[1].len);
125 req.buffer_ra = cpu_to_be64(__pa(msgs[1].buf));
126 break;
127 default:
128 return -EOPNOTSUPP;
129 }
130
131 rc = i2c_opal_send_request(opal_id, &req);
132 if (rc)
133 return rc;
134
135 return num;
136}
137
138static int i2c_opal_smbus_xfer(struct i2c_adapter *adap, u16 addr,
139 unsigned short flags, char read_write,
140 u8 command, int size, union i2c_smbus_data *data)
141{
142 unsigned long opal_id = (unsigned long)adap->algo_data;
143 struct opal_i2c_request req;
144 u8 local[2];
145 int rc;
146
147 memset(&req, 0, sizeof(req));
148
149 req.addr = cpu_to_be16(addr);
150 switch (size) {
151 case I2C_SMBUS_BYTE:
152 req.buffer_ra = cpu_to_be64(__pa(&data->byte));
153 req.size = cpu_to_be32(1);
154 /* Fall through */
155 case I2C_SMBUS_QUICK:
156 req.type = (read_write == I2C_SMBUS_READ) ?
157 OPAL_I2C_RAW_READ : OPAL_I2C_RAW_WRITE;
158 break;
159 case I2C_SMBUS_BYTE_DATA:
160 req.buffer_ra = cpu_to_be64(__pa(&data->byte));
161 req.size = cpu_to_be32(1);
162 req.subaddr = cpu_to_be32(command);
163 req.subaddr_sz = 1;
164 req.type = (read_write == I2C_SMBUS_READ) ?
165 OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;
166 break;
167 case I2C_SMBUS_WORD_DATA:
168 if (!read_write) {
169 local[0] = data->word & 0xff;
170 local[1] = (data->word >> 8) & 0xff;
171 }
172 req.buffer_ra = cpu_to_be64(__pa(local));
173 req.size = cpu_to_be32(2);
174 req.subaddr = cpu_to_be32(command);
175 req.subaddr_sz = 1;
176 req.type = (read_write == I2C_SMBUS_READ) ?
177 OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;
178 break;
179 case I2C_SMBUS_I2C_BLOCK_DATA:
180 req.buffer_ra = cpu_to_be64(__pa(&data->block[1]));
181 req.size = cpu_to_be32(data->block[0]);
182 req.subaddr = cpu_to_be32(command);
183 req.subaddr_sz = 1;
184 req.type = (read_write == I2C_SMBUS_READ) ?
185 OPAL_I2C_SM_READ : OPAL_I2C_SM_WRITE;
186 break;
187 default:
188 return -EINVAL;
189 }
190
191 rc = i2c_opal_send_request(opal_id, &req);
192 if (!rc && read_write && size == I2C_SMBUS_WORD_DATA) {
193 data->word = ((u16)local[1]) << 8;
194 data->word |= local[0];
195 }
196
197 return rc;
198}
199
200static u32 i2c_opal_func(struct i2c_adapter *adapter)
201{
202 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
203 I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
204 I2C_FUNC_SMBUS_I2C_BLOCK;
205}
206
207static const struct i2c_algorithm i2c_opal_algo = {
208 .master_xfer = i2c_opal_master_xfer,
209 .smbus_xfer = i2c_opal_smbus_xfer,
210 .functionality = i2c_opal_func,
211};
212
213static int i2c_opal_probe(struct platform_device *pdev)
214{
215 struct i2c_adapter *adapter;
216 const char *pname;
217 u32 opal_id;
218 int rc;
219
220 if (!pdev->dev.of_node)
221 return -ENODEV;
222
223 rc = of_property_read_u32(pdev->dev.of_node, "ibm,opal-id", &opal_id);
224 if (rc) {
225 dev_err(&pdev->dev, "Missing ibm,opal-id property !\n");
226 return -EIO;
227 }
228
229 adapter = devm_kzalloc(&pdev->dev, sizeof(*adapter), GFP_KERNEL);
230 if (!adapter)
231 return -ENOMEM;
232
233 adapter->algo = &i2c_opal_algo;
234 adapter->algo_data = (void *)(unsigned long)opal_id;
235 adapter->dev.parent = &pdev->dev;
236 adapter->dev.of_node = of_node_get(pdev->dev.of_node);
237 pname = of_get_property(pdev->dev.of_node, "ibm,port-name", NULL);
238 if (pname)
239 strlcpy(adapter->name, pname, sizeof(adapter->name));
240 else
241 strlcpy(adapter->name, "opal", sizeof(adapter->name));
242
243 platform_set_drvdata(pdev, adapter);
244 rc = i2c_add_adapter(adapter);
245 if (rc)
246 dev_err(&pdev->dev, "Failed to register the i2c adapter\n");
247
248 return rc;
249}
250
251static int i2c_opal_remove(struct platform_device *pdev)
252{
253 struct i2c_adapter *adapter = platform_get_drvdata(pdev);
254
255 i2c_del_adapter(adapter);
256
257 return 0;
258}
259
260static const struct of_device_id i2c_opal_of_match[] = {
261 {
262 .compatible = "ibm,opal-i2c",
263 },
264 { }
265};
266MODULE_DEVICE_TABLE(of, i2c_opal_of_match);
267
268static struct platform_driver i2c_opal_driver = {
269 .probe = i2c_opal_probe,
270 .remove = i2c_opal_remove,
271 .driver = {
272 .name = "i2c-opal",
273 .of_match_table = i2c_opal_of_match,
274 },
275};
276
277static int __init i2c_opal_init(void)
278{
279 if (!firmware_has_feature(FW_FEATURE_OPAL))
280 return -ENODEV;
281
282 return platform_driver_register(&i2c_opal_driver);
283}
284module_init(i2c_opal_init);
285
286static void __exit i2c_opal_exit(void)
287{
288 return platform_driver_unregister(&i2c_opal_driver);
289}
290module_exit(i2c_opal_exit);
291
292MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");
293MODULE_DESCRIPTION("IBM OPAL I2C driver");
294MODULE_LICENSE("GPL");