aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShardar Shariff Md <smohammed@nvidia.com>2017-02-20 11:52:34 -0500
committerWolfram Sang <wsa@the-dreams.de>2017-02-20 13:12:38 -0500
commit0297ffa69cab614f508ef5d9abdb4c0ffd709795 (patch)
tree7e6808d62e243e5ef2fc356b420faeb43a17ff4a
parent6a405f7c5d0e9d67163f25fad7fde9e70a9837bb (diff)
i2c: Add Tegra BPMP I2C proxy driver
Add Tegra BPMP I2C driver. The BPMP is the boot and power management processor embedded in Tegra SoCs. In newer SoC versions, access to one of the I2C busses goes via the BPMP, requiring a different "proxy" I2C driver that accesses the bus via the real I2C driver embedded in the BPMP firmware. Signed-off-by: Shardar Shariff Md <smohammed@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
-rw-r--r--drivers/i2c/busses/Kconfig11
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-tegra-bpmp.c346
3 files changed, 358 insertions, 0 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index c64221d143ff..8adc0f1d7ad0 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -930,6 +930,17 @@ config I2C_TEGRA
930 If you say yes to this option, support will be included for the 930 If you say yes to this option, support will be included for the
931 I2C controller embedded in NVIDIA Tegra SOCs 931 I2C controller embedded in NVIDIA Tegra SOCs
932 932
933config I2C_TEGRA_BPMP
934 tristate "NVIDIA Tegra BPMP I2C controller"
935 depends on TEGRA_BPMP
936 help
937 If you say yes to this option, support will be included for the I2C
938 controller embedded in NVIDIA Tegra SoCs accessed via the BPMP.
939
940 This I2C driver is a 'virtual' I2C driver. The real driver is part
941 of the BPMP firmware, and this driver merely communicates with that
942 real driver.
943
933config I2C_UNIPHIER 944config I2C_UNIPHIER
934 tristate "UniPhier FIFO-less I2C controller" 945 tristate "UniPhier FIFO-less I2C controller"
935 depends on ARCH_UNIPHIER || COMPILE_TEST 946 depends on ARCH_UNIPHIER || COMPILE_TEST
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index a2c6ff558578..30b60855fbcd 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -89,6 +89,7 @@ obj-$(CONFIG_I2C_STM32F4) += i2c-stm32f4.o
89obj-$(CONFIG_I2C_STU300) += i2c-stu300.o 89obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
90obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o 90obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o
91obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o 91obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
92obj-$(CONFIG_I2C_TEGRA_BPMP) += i2c-tegra-bpmp.o
92obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o 93obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o
93obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o 94obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o
94obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o 95obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
diff --git a/drivers/i2c/busses/i2c-tegra-bpmp.c b/drivers/i2c/busses/i2c-tegra-bpmp.c
new file mode 100644
index 000000000000..9eed69d5e17e
--- /dev/null
+++ b/drivers/i2c/busses/i2c-tegra-bpmp.c
@@ -0,0 +1,346 @@
1/*
2 * drivers/i2c/busses/i2c-tegra-bpmp.c
3 *
4 * Copyright (c) 2016 NVIDIA Corporation. All rights reserved.
5 *
6 * Author: Shardar Shariff Md <smohammed@nvidia.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <linux/err.h>
22#include <linux/i2c.h>
23#include <linux/init.h>
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/of_device.h>
27#include <linux/platform_device.h>
28#include <linux/pm_runtime.h>
29
30#include <soc/tegra/bpmp-abi.h>
31#include <soc/tegra/bpmp.h>
32
33/*
34 * Serialized I2C message header size is 6 bytes and includes address, flags
35 * and length
36 */
37#define SERIALI2C_HDR_SIZE 6
38
39struct tegra_bpmp_i2c {
40 struct i2c_adapter adapter;
41 struct device *dev;
42
43 struct tegra_bpmp *bpmp;
44 unsigned int bus;
45};
46
47/*
48 * Linux flags are translated to BPMP defined I2C flags that are used in BPMP
49 * firmware I2C driver to avoid any issues in future if Linux I2C flags are
50 * changed.
51 */
52static int tegra_bpmp_xlate_flags(u16 flags, u16 *out)
53{
54 if (flags & I2C_M_TEN) {
55 *out |= SERIALI2C_TEN;
56 flags &= ~I2C_M_TEN;
57 }
58
59 if (flags & I2C_M_RD) {
60 *out |= SERIALI2C_RD;
61 flags &= ~I2C_M_RD;
62 }
63
64 if (flags & I2C_M_STOP) {
65 *out |= SERIALI2C_STOP;
66 flags &= ~I2C_M_STOP;
67 }
68
69 if (flags & I2C_M_NOSTART) {
70 *out |= SERIALI2C_NOSTART;
71 flags &= ~I2C_M_NOSTART;
72 }
73
74 if (flags & I2C_M_REV_DIR_ADDR) {
75 *out |= SERIALI2C_REV_DIR_ADDR;
76 flags &= ~I2C_M_REV_DIR_ADDR;
77 }
78
79 if (flags & I2C_M_IGNORE_NAK) {
80 *out |= SERIALI2C_IGNORE_NAK;
81 flags &= ~I2C_M_IGNORE_NAK;
82 }
83
84 if (flags & I2C_M_NO_RD_ACK) {
85 *out |= SERIALI2C_NO_RD_ACK;
86 flags &= ~I2C_M_NO_RD_ACK;
87 }
88
89 if (flags & I2C_M_RECV_LEN) {
90 *out |= SERIALI2C_RECV_LEN;
91 flags &= ~I2C_M_RECV_LEN;
92 }
93
94 return (flags != 0) ? -EINVAL : 0;
95}
96
97/**
98 * The serialized I2C format is simply the following:
99 * [addr little-endian][flags little-endian][len little-endian][data if write]
100 * [addr little-endian][flags little-endian][len little-endian][data if write]
101 * ...
102 *
103 * The flags are translated from Linux kernel representation to seriali2c
104 * representation. Any undefined flag being set causes an error.
105 *
106 * The data is there only for writes. Reads have the data transferred in the
107 * other direction, and thus data is not present.
108 *
109 * See deserialize_i2c documentation for the data format in the other direction.
110 */
111static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
112 struct mrq_i2c_request *request,
113 struct i2c_msg *msgs,
114 unsigned int num)
115{
116 char *buf = request->xfer.data_buf;
117 unsigned int i, j, pos = 0;
118 int err;
119
120 for (i = 0; i < num; i++) {
121 struct i2c_msg *msg = &msgs[i];
122 u16 flags = 0;
123
124 err = tegra_bpmp_xlate_flags(msg->flags, &flags);
125 if (err < 0)
126 return err;
127
128 buf[pos++] = msg->addr & 0xff;
129 buf[pos++] = (msg->addr & 0xff00) >> 8;
130 buf[pos++] = flags & 0xff;
131 buf[pos++] = (flags & 0xff00) >> 8;
132 buf[pos++] = msg->len & 0xff;
133 buf[pos++] = (msg->len & 0xff00) >> 8;
134
135 if ((flags & SERIALI2C_RD) == 0) {
136 for (j = 0; j < msg->len; j++)
137 buf[pos++] = msg->buf[j];
138 }
139 }
140
141 request->xfer.data_size = pos;
142
143 return 0;
144}
145
146/**
147 * The data in the BPMP -> CPU direction is composed of sequential blocks for
148 * those messages that have I2C_M_RD. So, for example, if you have:
149 *
150 * - !I2C_M_RD, len == 5, data == a0 01 02 03 04
151 * - !I2C_M_RD, len == 1, data == a0
152 * - I2C_M_RD, len == 2, data == [uninitialized buffer 1]
153 * - !I2C_M_RD, len == 1, data == a2
154 * - I2C_M_RD, len == 2, data == [uninitialized buffer 2]
155 *
156 * ...then the data in the BPMP -> CPU direction would be 4 bytes total, and
157 * would contain 2 bytes that will go to uninitialized buffer 1, and 2 bytes
158 * that will go to uninitialized buffer 2.
159 */
160static int tegra_bpmp_i2c_deserialize(struct tegra_bpmp_i2c *i2c,
161 struct mrq_i2c_response *response,
162 struct i2c_msg *msgs,
163 unsigned int num)
164{
165 size_t size = response->xfer.data_size, len = 0, pos = 0;
166 char *buf = response->xfer.data_buf;
167 unsigned int i;
168
169 for (i = 0; i < num; i++)
170 if (msgs[i].flags & I2C_M_RD)
171 len += msgs[i].len;
172
173 if (len != size)
174 return -EINVAL;
175
176 for (i = 0; i < num; i++) {
177 if (msgs[i].flags & I2C_M_RD) {
178 memcpy(msgs[i].buf, buf + pos, msgs[i].len);
179 pos += msgs[i].len;
180 }
181 }
182
183 return 0;
184}
185
186static int tegra_bpmp_i2c_msg_len_check(struct i2c_msg *msgs, unsigned int num)
187{
188 size_t tx_len = 0, rx_len = 0;
189 unsigned int i;
190
191 for (i = 0; i < num; i++)
192 if (!(msgs[i].flags & I2C_M_RD))
193 tx_len += SERIALI2C_HDR_SIZE + msgs[i].len;
194
195 if (tx_len > TEGRA_I2C_IPC_MAX_IN_BUF_SIZE)
196 return -EINVAL;
197
198 for (i = 0; i < num; i++)
199 if ((msgs[i].flags & I2C_M_RD))
200 rx_len += msgs[i].len;
201
202 if (rx_len > TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE)
203 return -EINVAL;
204
205 return 0;
206}
207
208static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
209 struct mrq_i2c_request *request,
210 struct mrq_i2c_response *response)
211{
212 struct tegra_bpmp_message msg;
213 int err;
214
215 request->cmd = CMD_I2C_XFER;
216 request->xfer.bus_id = i2c->bus;
217
218 memset(&msg, 0, sizeof(msg));
219 msg.mrq = MRQ_I2C;
220 msg.tx.data = request;
221 msg.tx.size = sizeof(*request);
222 msg.rx.data = response;
223 msg.rx.size = sizeof(*response);
224
225 if (irqs_disabled())
226 err = tegra_bpmp_transfer_atomic(i2c->bpmp, &msg);
227 else
228 err = tegra_bpmp_transfer(i2c->bpmp, &msg);
229
230 return err;
231}
232
233static int tegra_bpmp_i2c_xfer(struct i2c_adapter *adapter,
234 struct i2c_msg *msgs, int num)
235{
236 struct tegra_bpmp_i2c *i2c = i2c_get_adapdata(adapter);
237 struct mrq_i2c_response response;
238 struct mrq_i2c_request request;
239 int err;
240
241 err = tegra_bpmp_i2c_msg_len_check(msgs, num);
242 if (err < 0) {
243 dev_err(i2c->dev, "unsupported message length\n");
244 return err;
245 }
246
247 memset(&request, 0, sizeof(request));
248 memset(&response, 0, sizeof(response));
249
250 err = tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num);
251 if (err < 0) {
252 dev_err(i2c->dev, "failed to serialize message: %d\n", err);
253 return err;
254 }
255
256 err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response);
257 if (err < 0) {
258 dev_err(i2c->dev, "failed to transfer message: %d\n", err);
259 return err;
260 }
261
262 err = tegra_bpmp_i2c_deserialize(i2c, &response, msgs, num);
263 if (err < 0) {
264 dev_err(i2c->dev, "failed to deserialize message: %d\n", err);
265 return err;
266 }
267
268 return num;
269}
270
271static u32 tegra_bpmp_i2c_func(struct i2c_adapter *adapter)
272{
273 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
274 I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART;
275}
276
277static const struct i2c_algorithm tegra_bpmp_i2c_algo = {
278 .master_xfer = tegra_bpmp_i2c_xfer,
279 .functionality = tegra_bpmp_i2c_func,
280};
281
282static int tegra_bpmp_i2c_probe(struct platform_device *pdev)
283{
284 struct tegra_bpmp_i2c *i2c;
285 u32 value;
286 int err;
287
288 i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
289 if (!i2c)
290 return -ENOMEM;
291
292 i2c->dev = &pdev->dev;
293
294 i2c->bpmp = dev_get_drvdata(pdev->dev.parent);
295 if (!i2c->bpmp)
296 return -ENODEV;
297
298 err = of_property_read_u32(pdev->dev.of_node, "nvidia,bpmp-bus-id",
299 &value);
300 if (err < 0)
301 return err;
302
303 i2c->bus = value;
304
305 i2c_set_adapdata(&i2c->adapter, i2c);
306 i2c->adapter.owner = THIS_MODULE;
307 strlcpy(i2c->adapter.name, "Tegra BPMP I2C adapter",
308 sizeof(i2c->adapter.name));
309 i2c->adapter.algo = &tegra_bpmp_i2c_algo;
310 i2c->adapter.dev.parent = &pdev->dev;
311 i2c->adapter.dev.of_node = pdev->dev.of_node;
312
313 platform_set_drvdata(pdev, i2c);
314
315 return i2c_add_adapter(&i2c->adapter);
316}
317
318static int tegra_bpmp_i2c_remove(struct platform_device *pdev)
319{
320 struct tegra_bpmp_i2c *i2c = platform_get_drvdata(pdev);
321
322 i2c_del_adapter(&i2c->adapter);
323
324 return 0;
325}
326
327static const struct of_device_id tegra_bpmp_i2c_of_match[] = {
328 { .compatible = "nvidia,tegra186-bpmp-i2c", },
329 { }
330};
331MODULE_DEVICE_TABLE(of, tegra_bpmp_i2c_of_match);
332
333static struct platform_driver tegra_bpmp_i2c_driver = {
334 .driver = {
335 .name = "tegra-bpmp-i2c",
336 .of_match_table = tegra_bpmp_i2c_of_match,
337 },
338 .probe = tegra_bpmp_i2c_probe,
339 .remove = tegra_bpmp_i2c_remove,
340};
341module_platform_driver(tegra_bpmp_i2c_driver);
342
343MODULE_DESCRIPTION("NVIDIA Tegra BPMP I2C bus contoller driver");
344MODULE_AUTHOR("Shardar Shariff Md <smohammed@nvidia.com>");
345MODULE_AUTHOR("Juha-Matti Tilli");
346MODULE_LICENSE("GPL v2");