]>
Commit | Line | Data |
---|---|---|
d19de0d3 SG |
1 | /* |
2 | * Simulate an I2C port | |
3 | * | |
4 | * Copyright (c) 2014 Google, Inc | |
5 | * | |
6 | * SPDX-License-Identifier: GPL-2.0+ | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
10 | #include <dm.h> | |
11 | #include <errno.h> | |
d19de0d3 SG |
12 | #include <i2c.h> |
13 | #include <asm/test.h> | |
14 | #include <dm/lists.h> | |
15 | #include <dm/device-internal.h> | |
d19de0d3 SG |
16 | |
17 | DECLARE_GLOBAL_DATA_PTR; | |
18 | ||
182bf92d SG |
19 | struct sandbox_i2c_priv { |
20 | bool test_mode; | |
d19de0d3 SG |
21 | }; |
22 | ||
23 | static int get_emul(struct udevice *dev, struct udevice **devp, | |
24 | struct dm_i2c_ops **opsp) | |
25 | { | |
e6f66ec0 | 26 | struct dm_i2c_chip *plat; |
a989ec8d | 27 | struct udevice *child; |
d19de0d3 SG |
28 | int ret; |
29 | ||
30 | *devp = NULL; | |
31 | *opsp = NULL; | |
e6f66ec0 SG |
32 | plat = dev_get_parent_platdata(dev); |
33 | if (!plat->emul) { | |
2e3f1ff6 | 34 | ret = dm_scan_fdt_dev(dev); |
d19de0d3 SG |
35 | if (ret) |
36 | return ret; | |
37 | ||
a989ec8d PM |
38 | for (device_find_first_child(dev, &child); child; |
39 | device_find_next_child(&child)) { | |
40 | if (device_get_uclass_id(child) != UCLASS_I2C_EMUL) | |
41 | continue; | |
42 | ||
43 | ret = device_probe(child); | |
44 | if (ret) | |
45 | return ret; | |
46 | ||
47 | break; | |
48 | } | |
49 | ||
50 | if (child) | |
51 | plat->emul = child; | |
52 | else | |
53 | return -ENODEV; | |
d19de0d3 | 54 | } |
e6f66ec0 SG |
55 | *devp = plat->emul; |
56 | *opsp = i2c_get_ops(plat->emul); | |
d19de0d3 SG |
57 | |
58 | return 0; | |
59 | } | |
60 | ||
182bf92d SG |
61 | void sandbox_i2c_set_test_mode(struct udevice *bus, bool test_mode) |
62 | { | |
63 | struct sandbox_i2c_priv *priv = dev_get_priv(bus); | |
64 | ||
65 | priv->test_mode = test_mode; | |
66 | } | |
67 | ||
d19de0d3 SG |
68 | static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, |
69 | int nmsgs) | |
70 | { | |
e564f054 | 71 | struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus); |
182bf92d | 72 | struct sandbox_i2c_priv *priv = dev_get_priv(bus); |
d19de0d3 SG |
73 | struct dm_i2c_ops *ops; |
74 | struct udevice *emul, *dev; | |
75 | bool is_read; | |
76 | int ret; | |
77 | ||
78 | /* Special test code to return success but with no emulation */ | |
182bf92d | 79 | if (priv->test_mode && msg->addr == SANDBOX_I2C_TEST_ADDR) |
d19de0d3 SG |
80 | return 0; |
81 | ||
25ab4b03 | 82 | ret = i2c_get_chip(bus, msg->addr, 1, &dev); |
d19de0d3 SG |
83 | if (ret) |
84 | return ret; | |
85 | ||
86 | ret = get_emul(dev, &emul, &ops); | |
87 | if (ret) | |
88 | return ret; | |
89 | ||
182bf92d SG |
90 | if (priv->test_mode) { |
91 | /* | |
92 | * For testing, don't allow writing above 100KHz for writes and | |
93 | * 400KHz for reads. | |
94 | */ | |
95 | is_read = nmsgs > 1; | |
96 | if (i2c->speed_hz > (is_read ? 400000 : 100000)) { | |
97 | debug("%s: Max speed exceeded\n", __func__); | |
98 | return -EINVAL; | |
99 | } | |
1bde67b1 | 100 | } |
182bf92d | 101 | |
d19de0d3 SG |
102 | return ops->xfer(emul, msg, nmsgs); |
103 | } | |
104 | ||
105 | static const struct dm_i2c_ops sandbox_i2c_ops = { | |
106 | .xfer = sandbox_i2c_xfer, | |
107 | }; | |
108 | ||
d19de0d3 SG |
109 | static const struct udevice_id sandbox_i2c_ids[] = { |
110 | { .compatible = "sandbox,i2c" }, | |
111 | { } | |
112 | }; | |
113 | ||
114 | U_BOOT_DRIVER(i2c_sandbox) = { | |
115 | .name = "i2c_sandbox", | |
116 | .id = UCLASS_I2C, | |
117 | .of_match = sandbox_i2c_ids, | |
d19de0d3 | 118 | .ops = &sandbox_i2c_ops, |
182bf92d | 119 | .priv_auto_alloc_size = sizeof(struct sandbox_i2c_priv), |
d19de0d3 | 120 | }; |