]>
Commit | Line | Data |
---|---|---|
34f1c9fe SW |
1 | /* |
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 | ||
13 | DECLARE_GLOBAL_DATA_PTR; | |
14 | ||
15 | struct tegra186_bpmp_i2c { | |
16 | uint32_t bpmp_bus_id; | |
17 | }; | |
18 | ||
19 | static 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 | ||
37 | static 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 | ||
89 | static int tegra186_bpmp_i2c_probe(struct udevice *dev) | |
90 | { | |
91 | struct tegra186_bpmp_i2c *priv = dev_get_priv(dev); | |
34f1c9fe | 92 | |
e160f7d4 | 93 | priv->bpmp_bus_id = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), |
34f1c9fe SW |
94 | "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__); | |
7c84319a | 97 | return -EINVAL; |
34f1c9fe SW |
98 | } |
99 | ||
100 | return 0; | |
101 | } | |
102 | ||
103 | static const struct dm_i2c_ops tegra186_bpmp_i2c_ops = { | |
104 | .xfer = tegra186_bpmp_i2c_xfer, | |
105 | }; | |
106 | ||
107 | static const struct udevice_id tegra186_bpmp_i2c_ids[] = { | |
108 | { .compatible = "nvidia,tegra186-bpmp-i2c" }, | |
109 | { } | |
110 | }; | |
111 | ||
112 | U_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 | }; |