1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Simulate an I2C port
4 *
5 * Copyright (c) 2014 Google, Inc
6 */
7
8#include <common.h>
9#include <dm.h>
10#include <errno.h>
11#include <i2c.h>
12#include <log.h>
13#include <asm/i2c.h>
14#include <asm/test.h>
15#include <dm/acpi.h>
16#include <dm/lists.h>
17#include <dm/device-internal.h>
18
19static int get_emul(struct udevice *dev, struct udevice **devp,
20		    struct dm_i2c_ops **opsp)
21{
22	struct dm_i2c_chip *plat;
23	int ret;
24
25	*devp = NULL;
26	*opsp = NULL;
27	plat = dev_get_parent_plat(dev);
28	if (!plat->emul) {
29		ret = i2c_emul_find(dev, &plat->emul);
30		if (ret)
31			return ret;
32	}
33	*devp = plat->emul;
34	*opsp = i2c_get_ops(plat->emul);
35
36	return 0;
37}
38
39void sandbox_i2c_set_test_mode(struct udevice *bus, bool test_mode)
40{
41	struct sandbox_i2c_priv *priv = dev_get_priv(bus);
42
43	priv->test_mode = test_mode;
44}
45
46static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
47			    int nmsgs)
48{
49	struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus);
50	struct sandbox_i2c_priv *priv = dev_get_priv(bus);
51	struct dm_i2c_ops *ops;
52	struct udevice *emul, *dev;
53	bool is_read;
54	int ret;
55
56	/* Special test code to return success but with no emulation */
57	if (priv->test_mode && msg->addr == SANDBOX_I2C_TEST_ADDR)
58		return 0;
59
60	ret = i2c_get_chip(bus, msg->addr, 1, &dev);
61	if (ret)
62		return ret;
63
64	ret = get_emul(dev, &emul, &ops);
65	if (ret)
66		return ret;
67
68	if (priv->test_mode) {
69		/*
70		* For testing, don't allow writing above 100KHz for writes and
71		* 400KHz for reads.
72		*/
73		is_read = nmsgs > 1;
74		if (i2c->speed_hz > (is_read ? I2C_SPEED_FAST_RATE :
75				I2C_SPEED_STANDARD_RATE)) {
76			debug("%s: Max speed exceeded\n", __func__);
77			return -EINVAL;
78		}
79	}
80
81	return ops->xfer(emul, msg, nmsgs);
82}
83
84static const struct dm_i2c_ops sandbox_i2c_ops = {
85	.xfer		= sandbox_i2c_xfer,
86};
87
88static const struct udevice_id sandbox_i2c_ids[] = {
89	{ .compatible = "sandbox,i2c" },
90	{ }
91};
92
93U_BOOT_DRIVER(sandbox_i2c) = {
94	.name	= "sandbox_i2c",
95	.id	= UCLASS_I2C,
96	.of_match = sandbox_i2c_ids,
97	.ops	= &sandbox_i2c_ops,
98	.priv_auto	= sizeof(struct sandbox_i2c_priv),
99};
100