]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
abb0b01e SG |
2 | /* |
3 | * Copyright (c) 2015 Google, Inc | |
4 | * Written by Simon Glass <[email protected]> | |
5 | * | |
ca6c5e03 SR |
6 | * SMBus block read/write support added by Stefan Roese: |
7 | * Copyright (C) 2016 Stefan Roese <[email protected]> | |
abb0b01e SG |
8 | */ |
9 | ||
10 | #include <common.h> | |
11 | #include <dm.h> | |
12 | #include <i2c.h> | |
f7ae49fc | 13 | #include <log.h> |
ca6c5e03 | 14 | #include <pci.h> |
abb0b01e SG |
15 | #include <asm/io.h> |
16 | ||
ca6c5e03 SR |
17 | /* PCI Configuration Space (D31:F3): SMBus */ |
18 | #define SMB_BASE 0x20 | |
19 | #define HOSTC 0x40 | |
20 | #define HST_EN (1 << 0) | |
21 | #define SMB_RCV_SLVA 0x09 | |
22 | ||
23 | /* SMBus I/O bits. */ | |
24 | #define SMBHSTSTAT 0x0 | |
25 | #define SMBHSTCTL 0x2 | |
26 | #define SMBHSTCMD 0x3 | |
27 | #define SMBXMITADD 0x4 | |
28 | #define SMBHSTDAT0 0x5 | |
29 | #define SMBHSTDAT1 0x6 | |
30 | #define SMBBLKDAT 0x7 | |
31 | #define SMBTRNSADD 0x9 | |
32 | #define SMBSLVDATA 0xa | |
33 | #define SMBAUXCTL 0xd | |
34 | #define SMLINK_PIN_CTL 0xe | |
35 | #define SMBUS_PIN_CTL 0xf | |
36 | ||
37 | /* I801 Hosts Status register bits */ | |
38 | #define SMBHSTSTS_BYTE_DONE 0x80 | |
39 | #define SMBHSTSTS_INUSE_STS 0x40 | |
40 | #define SMBHSTSTS_SMBALERT_STS 0x20 | |
41 | #define SMBHSTSTS_FAILED 0x10 | |
42 | #define SMBHSTSTS_BUS_ERR 0x08 | |
43 | #define SMBHSTSTS_DEV_ERR 0x04 | |
44 | #define SMBHSTSTS_INTR 0x02 | |
45 | #define SMBHSTSTS_HOST_BUSY 0x01 | |
46 | ||
47 | /* I801 Host Control register bits */ | |
48 | #define SMBHSTCNT_INTREN 0x01 | |
49 | #define SMBHSTCNT_KILL 0x02 | |
50 | #define SMBHSTCNT_LAST_BYTE 0x20 | |
51 | #define SMBHSTCNT_START 0x40 | |
52 | #define SMBHSTCNT_PEC_EN 0x80 /* ICH3 and later */ | |
53 | ||
54 | /* Auxiliary control register bits, ICH4+ only */ | |
55 | #define SMBAUXCTL_CRC 1 | |
56 | #define SMBAUXCTL_E32B 2 | |
57 | ||
58 | #define SMBUS_TIMEOUT 100 /* 100 ms */ | |
59 | ||
60 | struct intel_i2c { | |
61 | u32 base; | |
62 | int running; | |
63 | }; | |
64 | ||
65 | static int smbus_wait_until_ready(u32 base) | |
abb0b01e | 66 | { |
ca6c5e03 SR |
67 | unsigned long ts; |
68 | u8 byte; | |
69 | ||
70 | ts = get_timer(0); | |
71 | do { | |
72 | byte = inb(base + SMBHSTSTAT); | |
73 | if (!(byte & 1)) | |
74 | return 0; | |
75 | } while (get_timer(ts) < SMBUS_TIMEOUT); | |
76 | ||
77 | return -ETIMEDOUT; | |
abb0b01e SG |
78 | } |
79 | ||
ca6c5e03 | 80 | static int smbus_wait_until_done(u32 base) |
abb0b01e | 81 | { |
ca6c5e03 SR |
82 | unsigned long ts; |
83 | u8 byte; | |
84 | ||
85 | ts = get_timer(0); | |
86 | do { | |
87 | byte = inb(base + SMBHSTSTAT); | |
88 | if (!((byte & 1) || (byte & ~((1 << 6) | (1 << 0))) == 0)) | |
89 | return 0; | |
90 | } while (get_timer(ts) < SMBUS_TIMEOUT); | |
91 | ||
92 | return -ETIMEDOUT; | |
abb0b01e SG |
93 | } |
94 | ||
ca6c5e03 SR |
95 | static int smbus_block_read(u32 base, u8 dev, u8 *buffer, |
96 | int offset, int len) | |
abb0b01e | 97 | { |
ca6c5e03 SR |
98 | u8 buf_temp[32]; |
99 | int count; | |
100 | int i; | |
101 | ||
102 | debug("%s (%d): dev=0x%x offs=0x%x len=0x%x\n", | |
103 | __func__, __LINE__, dev, offset, len); | |
104 | if (smbus_wait_until_ready(base) < 0) | |
105 | return -ETIMEDOUT; | |
106 | ||
107 | /* Setup transaction */ | |
108 | ||
109 | /* Reset the data buffer index */ | |
110 | inb(base + SMBHSTCTL); | |
111 | ||
112 | /* Set the device I'm talking too */ | |
113 | outb(((dev & 0x7f) << 1) | 1, base + SMBXMITADD); | |
114 | /* Set the command/address... */ | |
115 | outb(offset & 0xff, base + SMBHSTCMD); | |
116 | /* Set up for a block read */ | |
117 | outb((inb(base + SMBHSTCTL) & (~(0x7) << 2)) | (0x5 << 2), | |
118 | (base + SMBHSTCTL)); | |
119 | /* Clear any lingering errors, so the transaction will run */ | |
120 | outb(inb(base + SMBHSTSTAT), base + SMBHSTSTAT); | |
121 | ||
122 | /* Start the command */ | |
123 | outb((inb(base + SMBHSTCTL) | SMBHSTCNT_START), base + SMBHSTCTL); | |
124 | ||
125 | /* Poll for transaction completion */ | |
126 | if (smbus_wait_until_done(base) < 0) { | |
127 | printf("SMBUS read transaction timeout (dev=0x%x)\n", dev); | |
128 | return -ETIMEDOUT; | |
129 | } | |
130 | ||
131 | count = inb(base + SMBHSTDAT0); | |
132 | debug("%s (%d): count=%d (len=%d)\n", __func__, __LINE__, count, len); | |
133 | if (count == 0) { | |
134 | debug("ERROR: len=0 on read\n"); | |
135 | return -EIO; | |
136 | } | |
137 | ||
138 | if (count < len) { | |
139 | debug("ERROR: too few bytes read\n"); | |
140 | return -EIO; | |
141 | } | |
142 | ||
143 | if (count > 32) { | |
144 | debug("ERROR: count=%d too high\n", count); | |
145 | return -EIO; | |
146 | } | |
147 | ||
148 | /* Read all available bytes from buffer */ | |
149 | for (i = 0; i < count; i++) | |
150 | buf_temp[i] = inb(base + SMBBLKDAT); | |
151 | ||
152 | memcpy(buffer, buf_temp, len); | |
153 | ||
154 | /* Return results of transaction */ | |
155 | if (!(inb(base + SMBHSTSTAT) & SMBHSTSTS_INTR)) | |
156 | return -EIO; | |
157 | ||
abb0b01e SG |
158 | return 0; |
159 | } | |
160 | ||
ca6c5e03 SR |
161 | static int smbus_block_write(u32 base, u8 dev, u8 *buffer, |
162 | int offset, int len) | |
abb0b01e | 163 | { |
ca6c5e03 SR |
164 | int i; |
165 | ||
166 | debug("%s (%d): dev=0x%x offs=0x%x len=0x%x\n", | |
167 | __func__, __LINE__, dev, offset, len); | |
168 | if (smbus_wait_until_ready(base) < 0) | |
169 | return -ETIMEDOUT; | |
170 | ||
171 | /* Setup transaction */ | |
172 | /* Set the device I'm talking too */ | |
173 | outb(((dev & 0x7f) << 1) & ~0x01, base + SMBXMITADD); | |
174 | /* Set the command/address... */ | |
175 | outb(offset, base + SMBHSTCMD); | |
176 | /* Set up for a block write */ | |
177 | outb((inb(base + SMBHSTCTL) & (~(0x7) << 2)) | (0x5 << 2), | |
178 | (base + SMBHSTCTL)); | |
179 | /* Clear any lingering errors, so the transaction will run */ | |
180 | outb(inb(base + SMBHSTSTAT), base + SMBHSTSTAT); | |
181 | ||
182 | /* Write count in DAT0 register */ | |
183 | outb(len, base + SMBHSTDAT0); | |
184 | ||
185 | /* Write data bytes... */ | |
186 | for (i = 0; i < len; i++) | |
187 | outb(*buffer++, base + SMBBLKDAT); | |
188 | ||
189 | /* Start the command */ | |
190 | outb((inb(base + SMBHSTCTL) | SMBHSTCNT_START), base + SMBHSTCTL); | |
191 | ||
192 | /* Poll for transaction completion */ | |
193 | if (smbus_wait_until_done(base) < 0) { | |
194 | printf("SMBUS write transaction timeout (dev=0x%x)\n", dev); | |
195 | return -ETIMEDOUT; | |
196 | } | |
197 | ||
198 | /* Return results of transaction */ | |
199 | if (!(inb(base + SMBHSTSTAT) & SMBHSTSTS_INTR)) | |
200 | return -EIO; | |
201 | ||
202 | return 0; | |
203 | } | |
204 | ||
205 | static int intel_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) | |
206 | { | |
207 | struct intel_i2c *i2c = dev_get_priv(bus); | |
208 | struct i2c_msg *dmsg, *omsg, dummy; | |
209 | ||
210 | debug("i2c_xfer: %d messages\n", nmsgs); | |
211 | ||
212 | memset(&dummy, 0, sizeof(struct i2c_msg)); | |
213 | ||
0c7645bd | 214 | /* |
ca6c5e03 SR |
215 | * We expect either two messages (one with an offset and one with the |
216 | * actucal data) or one message (just data) | |
0c7645bd | 217 | */ |
ca6c5e03 SR |
218 | if (nmsgs > 2 || nmsgs == 0) { |
219 | debug("%s: Only one or two messages are supported", __func__); | |
220 | return -EIO; | |
221 | } | |
222 | ||
223 | omsg = nmsgs == 1 ? &dummy : msg; | |
224 | dmsg = nmsgs == 1 ? msg : msg + 1; | |
225 | ||
226 | if (dmsg->flags & I2C_M_RD) | |
227 | return smbus_block_read(i2c->base, dmsg->addr, &dmsg->buf[0], | |
228 | omsg->buf[0], dmsg->len); | |
229 | else | |
230 | return smbus_block_write(i2c->base, dmsg->addr, &dmsg->buf[1], | |
231 | dmsg->buf[0], dmsg->len - 1); | |
232 | } | |
233 | ||
234 | static int intel_i2c_probe_chip(struct udevice *bus, uint chip_addr, | |
235 | uint chip_flags) | |
236 | { | |
237 | struct intel_i2c *i2c = dev_get_priv(bus); | |
238 | u8 buf[4]; | |
239 | ||
240 | return smbus_block_read(i2c->base, chip_addr, buf, 0, 1); | |
241 | } | |
242 | ||
243 | static int intel_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) | |
244 | { | |
245 | return 0; | |
246 | } | |
247 | ||
248 | static int intel_i2c_probe(struct udevice *dev) | |
249 | { | |
250 | struct intel_i2c *priv = dev_get_priv(dev); | |
fac3e796 | 251 | ulong base; |
ca6c5e03 SR |
252 | |
253 | /* Save base address from PCI BAR */ | |
fac3e796 SG |
254 | priv->base = (ulong)dm_pci_map_bar(dev, PCI_BASE_ADDRESS_4, |
255 | PCI_REGION_IO); | |
ca6c5e03 | 256 | base = priv->base; |
0c7645bd SG |
257 | |
258 | /* Set SMBus enable. */ | |
259 | dm_pci_write_config8(dev, HOSTC, HST_EN); | |
260 | ||
ca6c5e03 SR |
261 | /* Disable interrupts */ |
262 | outb(inb(base + SMBHSTCTL) & ~SMBHSTCNT_INTREN, base + SMBHSTCTL); | |
0c7645bd | 263 | |
ca6c5e03 SR |
264 | /* Set 32-byte data buffer mode */ |
265 | outb(inb(base + SMBAUXCTL) | SMBAUXCTL_E32B, base + SMBAUXCTL); | |
0c7645bd | 266 | |
ca6c5e03 SR |
267 | return 0; |
268 | } | |
269 | ||
270 | static int intel_i2c_bind(struct udevice *dev) | |
271 | { | |
272 | static int num_cards __attribute__ ((section(".data"))); | |
273 | char name[20]; | |
274 | ||
275 | /* Create a unique device name for PCI type devices */ | |
276 | if (device_is_on_pci_bus(dev)) { | |
277 | /* | |
278 | * ToDo: | |
279 | * Setting req_seq in the driver is probably not recommended. | |
280 | * But without a DT alias the number is not configured. And | |
281 | * using this driver is impossible for PCIe I2C devices. | |
282 | * This can be removed, once a better (correct) way for this | |
283 | * is found and implemented. | |
284 | */ | |
285 | dev->req_seq = num_cards; | |
286 | sprintf(name, "intel_i2c#%u", num_cards++); | |
287 | device_set_name(dev, name); | |
288 | } | |
0c7645bd | 289 | |
abb0b01e SG |
290 | return 0; |
291 | } | |
292 | ||
293 | static const struct dm_i2c_ops intel_i2c_ops = { | |
294 | .xfer = intel_i2c_xfer, | |
295 | .probe_chip = intel_i2c_probe_chip, | |
296 | .set_bus_speed = intel_i2c_set_bus_speed, | |
297 | }; | |
298 | ||
299 | static const struct udevice_id intel_i2c_ids[] = { | |
300 | { .compatible = "intel,ich-i2c" }, | |
301 | { } | |
302 | }; | |
303 | ||
304 | U_BOOT_DRIVER(intel_i2c) = { | |
305 | .name = "i2c_intel", | |
306 | .id = UCLASS_I2C, | |
307 | .of_match = intel_i2c_ids, | |
abb0b01e | 308 | .ops = &intel_i2c_ops, |
41575d8e | 309 | .priv_auto = sizeof(struct intel_i2c), |
ca6c5e03 | 310 | .bind = intel_i2c_bind, |
abb0b01e SG |
311 | .probe = intel_i2c_probe, |
312 | }; | |
ca6c5e03 SR |
313 | |
314 | static struct pci_device_id intel_smbus_pci_supported[] = { | |
315 | /* Intel BayTrail SMBus on the PCI bus */ | |
316 | { PCI_VDEVICE(INTEL, 0x0f12) }, | |
317 | /* Intel IvyBridge (Panther Point PCH) SMBus on the PCI bus */ | |
318 | { PCI_VDEVICE(INTEL, 0x1e22) }, | |
319 | {}, | |
320 | }; | |
321 | ||
322 | U_BOOT_PCI_DEVICE(intel_i2c, intel_smbus_pci_supported); |