1d19de0d3SSimon Glass /*
2d19de0d3SSimon Glass * Simulate an I2C port
3d19de0d3SSimon Glass *
4d19de0d3SSimon Glass * Copyright (c) 2014 Google, Inc
5d19de0d3SSimon Glass *
6d19de0d3SSimon Glass * SPDX-License-Identifier: GPL-2.0+
7d19de0d3SSimon Glass */
8d19de0d3SSimon Glass
9d19de0d3SSimon Glass #include <common.h>
10d19de0d3SSimon Glass #include <dm.h>
11d19de0d3SSimon Glass #include <errno.h>
12d19de0d3SSimon Glass #include <i2c.h>
13d19de0d3SSimon Glass #include <asm/test.h>
14d19de0d3SSimon Glass #include <dm/lists.h>
15d19de0d3SSimon Glass #include <dm/device-internal.h>
16d19de0d3SSimon Glass
17d19de0d3SSimon Glass DECLARE_GLOBAL_DATA_PTR;
18d19de0d3SSimon Glass
19182bf92dSSimon Glass struct sandbox_i2c_priv {
20182bf92dSSimon Glass bool test_mode;
21d19de0d3SSimon Glass };
22d19de0d3SSimon Glass
get_emul(struct udevice * dev,struct udevice ** devp,struct dm_i2c_ops ** opsp)23d19de0d3SSimon Glass static int get_emul(struct udevice *dev, struct udevice **devp,
24d19de0d3SSimon Glass struct dm_i2c_ops **opsp)
25d19de0d3SSimon Glass {
26e6f66ec0SSimon Glass struct dm_i2c_chip *plat;
27a989ec8dSPrzemyslaw Marczak struct udevice *child;
28d19de0d3SSimon Glass int ret;
29d19de0d3SSimon Glass
30d19de0d3SSimon Glass *devp = NULL;
31d19de0d3SSimon Glass *opsp = NULL;
32e6f66ec0SSimon Glass plat = dev_get_parent_platdata(dev);
33e6f66ec0SSimon Glass if (!plat->emul) {
34*2e3f1ff6SSimon Glass ret = dm_scan_fdt_dev(dev);
35d19de0d3SSimon Glass if (ret)
36d19de0d3SSimon Glass return ret;
37d19de0d3SSimon Glass
38a989ec8dSPrzemyslaw Marczak for (device_find_first_child(dev, &child); child;
39a989ec8dSPrzemyslaw Marczak device_find_next_child(&child)) {
40a989ec8dSPrzemyslaw Marczak if (device_get_uclass_id(child) != UCLASS_I2C_EMUL)
41a989ec8dSPrzemyslaw Marczak continue;
42a989ec8dSPrzemyslaw Marczak
43a989ec8dSPrzemyslaw Marczak ret = device_probe(child);
44d19de0d3SSimon Glass if (ret)
45d19de0d3SSimon Glass return ret;
46a989ec8dSPrzemyslaw Marczak
47a989ec8dSPrzemyslaw Marczak break;
48a989ec8dSPrzemyslaw Marczak }
49a989ec8dSPrzemyslaw Marczak
50a989ec8dSPrzemyslaw Marczak if (child)
51a989ec8dSPrzemyslaw Marczak plat->emul = child;
52a989ec8dSPrzemyslaw Marczak else
53a989ec8dSPrzemyslaw Marczak return -ENODEV;
54d19de0d3SSimon Glass }
55e6f66ec0SSimon Glass *devp = plat->emul;
56e6f66ec0SSimon Glass *opsp = i2c_get_ops(plat->emul);
57d19de0d3SSimon Glass
58d19de0d3SSimon Glass return 0;
59d19de0d3SSimon Glass }
60d19de0d3SSimon Glass
sandbox_i2c_set_test_mode(struct udevice * bus,bool test_mode)61182bf92dSSimon Glass void sandbox_i2c_set_test_mode(struct udevice *bus, bool test_mode)
62182bf92dSSimon Glass {
63182bf92dSSimon Glass struct sandbox_i2c_priv *priv = dev_get_priv(bus);
64182bf92dSSimon Glass
65182bf92dSSimon Glass priv->test_mode = test_mode;
66182bf92dSSimon Glass }
67182bf92dSSimon Glass
sandbox_i2c_xfer(struct udevice * bus,struct i2c_msg * msg,int nmsgs)68d19de0d3SSimon Glass static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
69d19de0d3SSimon Glass int nmsgs)
70d19de0d3SSimon Glass {
71e564f054SSimon Glass struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus);
72182bf92dSSimon Glass struct sandbox_i2c_priv *priv = dev_get_priv(bus);
73d19de0d3SSimon Glass struct dm_i2c_ops *ops;
74d19de0d3SSimon Glass struct udevice *emul, *dev;
75d19de0d3SSimon Glass bool is_read;
76d19de0d3SSimon Glass int ret;
77d19de0d3SSimon Glass
78d19de0d3SSimon Glass /* Special test code to return success but with no emulation */
79182bf92dSSimon Glass if (priv->test_mode && msg->addr == SANDBOX_I2C_TEST_ADDR)
80d19de0d3SSimon Glass return 0;
81d19de0d3SSimon Glass
8225ab4b03SSimon Glass ret = i2c_get_chip(bus, msg->addr, 1, &dev);
83d19de0d3SSimon Glass if (ret)
84d19de0d3SSimon Glass return ret;
85d19de0d3SSimon Glass
86d19de0d3SSimon Glass ret = get_emul(dev, &emul, &ops);
87d19de0d3SSimon Glass if (ret)
88d19de0d3SSimon Glass return ret;
89d19de0d3SSimon Glass
90182bf92dSSimon Glass if (priv->test_mode) {
91d19de0d3SSimon Glass /*
92d19de0d3SSimon Glass * For testing, don't allow writing above 100KHz for writes and
93182bf92dSSimon Glass * 400KHz for reads.
94d19de0d3SSimon Glass */
95d19de0d3SSimon Glass is_read = nmsgs > 1;
961bde67b1SSimon Glass if (i2c->speed_hz > (is_read ? 400000 : 100000)) {
971bde67b1SSimon Glass debug("%s: Max speed exceeded\n", __func__);
98d19de0d3SSimon Glass return -EINVAL;
991bde67b1SSimon Glass }
100182bf92dSSimon Glass }
101182bf92dSSimon Glass
102d19de0d3SSimon Glass return ops->xfer(emul, msg, nmsgs);
103d19de0d3SSimon Glass }
104d19de0d3SSimon Glass
105d19de0d3SSimon Glass static const struct dm_i2c_ops sandbox_i2c_ops = {
106d19de0d3SSimon Glass .xfer = sandbox_i2c_xfer,
107d19de0d3SSimon Glass };
108d19de0d3SSimon Glass
109d19de0d3SSimon Glass static const struct udevice_id sandbox_i2c_ids[] = {
110d19de0d3SSimon Glass { .compatible = "sandbox,i2c" },
111d19de0d3SSimon Glass { }
112d19de0d3SSimon Glass };
113d19de0d3SSimon Glass
114d19de0d3SSimon Glass U_BOOT_DRIVER(i2c_sandbox) = {
115d19de0d3SSimon Glass .name = "i2c_sandbox",
116d19de0d3SSimon Glass .id = UCLASS_I2C,
117d19de0d3SSimon Glass .of_match = sandbox_i2c_ids,
118d19de0d3SSimon Glass .ops = &sandbox_i2c_ops,
119182bf92dSSimon Glass .priv_auto_alloc_size = sizeof(struct sandbox_i2c_priv),
120d19de0d3SSimon Glass };
121