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