blob: 41ef14a9235489c7ae9eb6d22315781efcdea97f [file] [log] [blame]
Angel Ponsc74dae92020-04-02 23:48:16 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Nico Huber58173862017-08-01 17:09:35 +02002
Elyes Haouas04c3b5a2022-10-07 10:08:05 +02003#include <commonlib/bsd/helpers.h>
Nico Huber58173862017-08-01 17:09:35 +02004#include <console/console.h>
Elyes Haouas04c3b5a2022-10-07 10:08:05 +02005#include <device/device.h>
Nico Huber58173862017-08-01 17:09:35 +02006#include <device/smbus.h>
7#include <device/i2c_bus.h>
Nico Huber9734af62017-11-01 13:15:33 +01008#include <commonlib/endian.h>
Elyes Haouas04c3b5a2022-10-07 10:08:05 +02009#include <types.h>
Nico Huber58173862017-08-01 17:09:35 +020010
Matt DeVillieree849ba2022-05-19 19:03:07 -050011bool i2c_dev_detect(struct device *dev, unsigned int addr)
12{
13 struct i2c_msg seg = { .flags = 0, .slave = addr, .buf = NULL, .len = 0 };
Martin Rothfab89b32023-10-23 17:47:03 -060014 if (!dev)
15 return false;
Nico Huber51a357642022-08-01 19:32:37 +020016 return dev->ops->ops_i2c_bus->transfer(dev, &seg, 1) == 0;
Matt DeVillieree849ba2022-05-19 19:03:07 -050017}
18
Matt DeVillier190086e2022-03-28 22:24:30 -050019struct bus *i2c_link(const struct device *const dev)
Nico Huber58173862017-08-01 17:09:35 +020020{
Arthur Heymans7fcd4d52023-08-24 15:12:19 +020021 if (!dev || !dev->upstream)
Nico Huber58173862017-08-01 17:09:35 +020022 return NULL;
23
Arthur Heymans7fcd4d52023-08-24 15:12:19 +020024 struct bus *link = dev->upstream;
Nico Huber58173862017-08-01 17:09:35 +020025 while (link) {
26 struct device *const parent = link->dev;
27
28 if (parent && parent->ops &&
29 (parent->ops->ops_i2c_bus || parent->ops->ops_smbus_bus))
30 break;
31
Arthur Heymans7fcd4d52023-08-24 15:12:19 +020032 if (parent && parent->upstream && link != parent->upstream)
33 link = parent->upstream;
Nico Huber58173862017-08-01 17:09:35 +020034 else
35 link = NULL;
36 }
37
Frans Hendriks68dc3692021-02-01 11:52:51 +010038 if (!link)
Matt DeVilliere97eb8f2022-06-18 15:28:00 -050039 printk(BIOS_ALERT, "%s Cannot find I2C or SMBus bus operations\n",
40 dev_path(dev));
Nico Huber58173862017-08-01 17:09:35 +020041
42 return link;
43}
44
Aaron Durbin439cee92018-01-22 21:24:35 -070045int i2c_dev_readb(struct device *const dev)
Nico Huber58173862017-08-01 17:09:35 +020046{
47 struct device *const busdev = i2c_busdev(dev);
48 if (!busdev)
49 return -1;
50
51 if (busdev->ops->ops_i2c_bus) {
52 uint8_t val;
53 const struct i2c_msg msg = {
54 .flags = I2C_M_RD,
55 .slave = dev->path.i2c.device,
56 .buf = &val,
57 .len = sizeof(val),
58 };
59
Frans Hendriksa9caa502021-02-01 11:44:37 +010060 const int ret = busdev->ops->ops_i2c_bus->transfer(busdev, &msg, 1);
Nico Huber58173862017-08-01 17:09:35 +020061 if (ret)
62 return ret;
63 else
64 return val;
65 } else if (busdev->ops->ops_smbus_bus->recv_byte) {
66 return busdev->ops->ops_smbus_bus->recv_byte(dev);
Nico Huber58173862017-08-01 17:09:35 +020067 }
Frans Hendriksa9caa502021-02-01 11:44:37 +010068
69 printk(BIOS_ERR, "%s Missing ops_smbus_bus->recv_byte", dev_path(busdev));
70 return -1;
Nico Huber58173862017-08-01 17:09:35 +020071}
72
Aaron Durbin439cee92018-01-22 21:24:35 -070073int i2c_dev_writeb(struct device *const dev, uint8_t val)
Nico Huber58173862017-08-01 17:09:35 +020074{
75 struct device *const busdev = i2c_busdev(dev);
76 if (!busdev)
77 return -1;
78
79 if (busdev->ops->ops_i2c_bus) {
80 const struct i2c_msg msg = {
81 .flags = 0,
82 .slave = dev->path.i2c.device,
83 .buf = &val,
84 .len = sizeof(val),
85 };
86 return busdev->ops->ops_i2c_bus->transfer(busdev, &msg, 1);
87 } else if (busdev->ops->ops_smbus_bus->send_byte) {
88 return busdev->ops->ops_smbus_bus->send_byte(dev, val);
Nico Huber58173862017-08-01 17:09:35 +020089 }
Frans Hendriksa9caa502021-02-01 11:44:37 +010090
Frans Hendriks68dc3692021-02-01 11:52:51 +010091 printk(BIOS_ERR, "%s Missing ops_smbus_bus->send_byte", dev_path(busdev));
Frans Hendriksa9caa502021-02-01 11:44:37 +010092 return -1;
Nico Huber58173862017-08-01 17:09:35 +020093}
94
Aaron Durbin439cee92018-01-22 21:24:35 -070095int i2c_dev_readb_at(struct device *const dev, uint8_t off)
Nico Huber58173862017-08-01 17:09:35 +020096{
97 struct device *const busdev = i2c_busdev(dev);
98 if (!busdev)
99 return -1;
100
101 if (busdev->ops->ops_i2c_bus) {
102 uint8_t val;
103 const struct i2c_msg msg[] = {
104 {
105 .flags = 0,
106 .slave = dev->path.i2c.device,
107 .buf = &off,
108 .len = sizeof(off),
109 },
110 {
111 .flags = I2C_M_RD,
112 .slave = dev->path.i2c.device,
113 .buf = &val,
114 .len = sizeof(val),
115 },
116 };
117
Frans Hendriksa9caa502021-02-01 11:44:37 +0100118 const int ret = busdev->ops->ops_i2c_bus->transfer(busdev, msg,
119 ARRAY_SIZE(msg));
Nico Huber58173862017-08-01 17:09:35 +0200120 if (ret)
121 return ret;
122 else
123 return val;
124 } else if (busdev->ops->ops_smbus_bus->read_byte) {
125 return busdev->ops->ops_smbus_bus->read_byte(dev, off);
Nico Huber58173862017-08-01 17:09:35 +0200126 }
Frans Hendriksa9caa502021-02-01 11:44:37 +0100127
128 printk(BIOS_ERR, "%s Missing ops_smbus_bus->read_byte", dev_path(busdev));
129 return -1;
Nico Huber58173862017-08-01 17:09:35 +0200130}
131
Frans Hendriksa9caa502021-02-01 11:44:37 +0100132int i2c_dev_writeb_at(struct device *const dev, const uint8_t off, const uint8_t val)
Nico Huber58173862017-08-01 17:09:35 +0200133{
134 struct device *const busdev = i2c_busdev(dev);
135 if (!busdev)
136 return -1;
137
138 if (busdev->ops->ops_i2c_bus) {
139 uint8_t buf[] = { off, val };
140 const struct i2c_msg msg = {
141 .flags = 0,
142 .slave = dev->path.i2c.device,
143 .buf = buf,
144 .len = sizeof(buf),
145 };
146 return busdev->ops->ops_i2c_bus->transfer(busdev, &msg, 1);
147 } else if (busdev->ops->ops_smbus_bus->write_byte) {
148 return busdev->ops->ops_smbus_bus->write_byte(dev, off, val);
Nico Huber58173862017-08-01 17:09:35 +0200149 }
Frans Hendriksa9caa502021-02-01 11:44:37 +0100150
Frans Hendriks68dc3692021-02-01 11:52:51 +0100151 printk(BIOS_ERR, "%s Missing ops_smbus_bus->write_byte", dev_path(busdev));
Frans Hendriksa9caa502021-02-01 11:44:37 +0100152 return -1;
Nico Huber58173862017-08-01 17:09:35 +0200153}
Nico Huber9734af62017-11-01 13:15:33 +0100154
Frans Hendriksa9caa502021-02-01 11:44:37 +0100155int i2c_dev_read_at16(struct device *const dev, uint8_t *const buf, const size_t len,
156 uint16_t off)
Nico Huber9734af62017-11-01 13:15:33 +0100157{
158 struct device *const busdev = i2c_busdev(dev);
159 if (!busdev)
160 return -1;
161
162 if (busdev->ops->ops_i2c_bus) {
163 const struct i2c_msg msg[] = {
164 {
165 .flags = 0,
166 .slave = dev->path.i2c.device,
167 .buf = (uint8_t *)&off,
168 .len = sizeof(off),
169 },
170 {
171 .flags = I2C_M_RD,
172 .slave = dev->path.i2c.device,
173 .buf = buf,
174 .len = len,
175 },
176 };
177
178 write_be16(&off, off);
Frans Hendriksa9caa502021-02-01 11:44:37 +0100179 const int ret = busdev->ops->ops_i2c_bus->transfer(busdev, msg,
180 ARRAY_SIZE(msg));
Nico Huber9734af62017-11-01 13:15:33 +0100181 if (ret)
182 return ret;
183 else
184 return len;
185 } else {
186 printk(BIOS_ERR, "%s Missing ops_i2c_bus->transfer", dev_path(busdev));
187 return -1;
188 }
189}
Werner Zeh63f72f02022-08-26 13:17:52 +0200190
191int i2c_dev_read_at(struct device *const dev, uint8_t *const buf, const size_t len,
192 uint8_t off)
193{
194 struct device *const busdev = i2c_busdev(dev);
195 if (!busdev)
196 return -1;
197
198 if (busdev->ops->ops_i2c_bus) {
199 const struct i2c_msg msg[] = {
200 {
201 .flags = 0,
202 .slave = dev->path.i2c.device,
203 .buf = &off,
204 .len = sizeof(off),
205 },
206 {
207 .flags = I2C_M_RD,
208 .slave = dev->path.i2c.device,
209 .buf = buf,
210 .len = len,
211 },
212 };
213
214 const int ret = busdev->ops->ops_i2c_bus->transfer(busdev, msg,
215 ARRAY_SIZE(msg));
216 if (ret)
217 return ret;
218 else
219 return len;
220 } else {
221 printk(BIOS_ERR, "%s Missing ops_i2c_bus->transfer", dev_path(busdev));
222 return -1;
223 }
224}
225
226int i2c_dev_write_at(struct device *const dev, uint8_t *const buf, const size_t len,
227 uint8_t off)
228{
229 struct device *const busdev = i2c_busdev(dev);
230 if (!busdev)
231 return -1;
232
233 if (busdev->ops->ops_i2c_bus) {
234 const struct i2c_msg msg[] = {
235 {
236 .flags = 0,
237 .slave = dev->path.i2c.device,
238 .buf = &off,
239 .len = sizeof(off),
240 },
241 {
242 .flags = I2C_M_NOSTART,
243 .slave = dev->path.i2c.device,
244 .buf = buf,
245 .len = len,
246 },
247 };
248
249 const int ret = busdev->ops->ops_i2c_bus->transfer(busdev, msg,
250 ARRAY_SIZE(msg));
251 if (ret)
252 return ret;
253 else
254 return len;
255 } else {
256 printk(BIOS_ERR, "%s Missing ops_i2c_bus->transfer", dev_path(busdev));
257 return -1;
258 }
259}