blob: b4fff43269889fec6d52e64eb089e314726a72e3 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0
Stephen Warren67a83482016-08-08 11:28:27 -06002/*
3 * Copyright (c) 2016, NVIDIA CORPORATION.
Stephen Warren67a83482016-08-08 11:28:27 -06004 */
5
6#include <common.h>
7#include <dm.h>
8#include <i2c.h>
9#include <misc.h>
10#include <asm/arch-tegra/bpmp_abi.h>
11
12DECLARE_GLOBAL_DATA_PTR;
13
14struct tegra186_bpmp_i2c {
15 uint32_t bpmp_bus_id;
16};
17
18static inline void serialize_u16(uint8_t **p, uint16_t val)
19{
20 (*p)[0] = val & 0xff;
21 (*p)[1] = val >> 8;
22 (*p) += 2;
23}
24
25/* These just happen to have the same values as I2C_M_* and SERIALI2C_* */
26#define SUPPORTED_FLAGS \
27 (I2C_M_TEN | \
28 I2C_M_RD | \
29 I2C_M_STOP | \
30 I2C_M_NOSTART | \
31 I2C_M_REV_DIR_ADDR | \
32 I2C_M_IGNORE_NAK | \
33 I2C_M_NO_RD_ACK | \
34 I2C_M_RECV_LEN)
35
36static int tegra186_bpmp_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
37 int nmsgs)
38{
39 struct tegra186_bpmp_i2c *priv = dev_get_priv(dev);
40 struct mrq_i2c_request req;
41 struct mrq_i2c_response resp;
42 uint8_t *p;
43 int left, i, ret;
44
45 req.cmd = CMD_I2C_XFER;
46 req.xfer.bus_id = priv->bpmp_bus_id;
47 p = &req.xfer.data_buf[0];
48 left = ARRAY_SIZE(req.xfer.data_buf);
49 for (i = 0; i < nmsgs; i++) {
50 int len = 6;
51 if (!(msg[i].flags & I2C_M_RD))
52 len += msg[i].len;
53 if ((len >= BIT(16)) || (len > left))
54 return -ENOSPC;
55
56 if (msg[i].flags & ~SUPPORTED_FLAGS)
57 return -EINVAL;
58
59 serialize_u16(&p, msg[i].addr);
60 serialize_u16(&p, msg[i].flags);
61 serialize_u16(&p, msg[i].len);
62 if (!(msg[i].flags & I2C_M_RD)) {
63 memcpy(p, msg[i].buf, msg[i].len);
64 p += msg[i].len;
65 }
66 }
67 req.xfer.data_size = p - &req.xfer.data_buf[0];
68
69 ret = misc_call(dev->parent, MRQ_I2C, &req, sizeof(req), &resp,
70 sizeof(resp));
71 if (ret < 0)
72 return ret;
73
74 p = &resp.xfer.data_buf[0];
75 left = resp.xfer.data_size;
76 if (left > ARRAY_SIZE(resp.xfer.data_buf))
77 return -EINVAL;
78 for (i = 0; i < nmsgs; i++) {
79 if (msg[i].flags & I2C_M_RD) {
80 memcpy(msg[i].buf, p, msg[i].len);
81 p += msg[i].len;
82 }
83 }
84
85 return 0;
86}
87
88static int tegra186_bpmp_i2c_probe(struct udevice *dev)
89{
90 struct tegra186_bpmp_i2c *priv = dev_get_priv(dev);
Stephen Warren67a83482016-08-08 11:28:27 -060091
Simon Glassdd79d6e2017-01-17 16:52:55 -070092 priv->bpmp_bus_id = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
Stephen Warren67a83482016-08-08 11:28:27 -060093 "nvidia,bpmp-bus-id", U32_MAX);
94 if (priv->bpmp_bus_id == U32_MAX) {
95 debug("%s: could not parse nvidia,bpmp-bus-id\n", __func__);
Simon Glassf44b4bf2017-09-17 16:54:53 -060096 return -EINVAL;
Stephen Warren67a83482016-08-08 11:28:27 -060097 }
98
99 return 0;
100}
101
102static const struct dm_i2c_ops tegra186_bpmp_i2c_ops = {
103 .xfer = tegra186_bpmp_i2c_xfer,
104};
105
106static const struct udevice_id tegra186_bpmp_i2c_ids[] = {
107 { .compatible = "nvidia,tegra186-bpmp-i2c" },
108 { }
109};
110
111U_BOOT_DRIVER(i2c_gpio) = {
112 .name = "tegra186_bpmp_i2c",
113 .id = UCLASS_I2C,
114 .of_match = tegra186_bpmp_i2c_ids,
115 .probe = tegra186_bpmp_i2c_probe,
116 .priv_auto_alloc_size = sizeof(struct tegra186_bpmp_i2c),
117 .ops = &tegra186_bpmp_i2c_ops,
118};