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