Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/amdsmb/amdsmb.c
39507 views
1
/*-
2
* Copyright (c) 2005 Ruslan Ermilov
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/param.h>
28
#include <sys/bus.h>
29
#include <sys/kernel.h>
30
#include <sys/lock.h>
31
#include <sys/module.h>
32
#include <sys/mutex.h>
33
#include <sys/systm.h>
34
35
#include <machine/bus.h>
36
#include <machine/resource.h>
37
#include <sys/rman.h>
38
39
#include <dev/pci/pcivar.h>
40
#include <dev/pci/pcireg.h>
41
42
#include <dev/smbus/smbconf.h>
43
#include "smbus_if.h"
44
45
#define AMDSMB_DEBUG(x) if (amdsmb_debug) (x)
46
47
#ifdef DEBUG
48
static int amdsmb_debug = 1;
49
#else
50
static int amdsmb_debug = 0;
51
#endif
52
53
#define AMDSMB_VENDORID_AMD 0x1022
54
#define AMDSMB_DEVICEID_AMD8111_SMB2 0x746a
55
56
/*
57
* ACPI 3.0, Chapter 12, Embedded Controller Interface.
58
*/
59
#define EC_DATA 0x00 /* data register */
60
#define EC_SC 0x04 /* status of controller */
61
#define EC_CMD 0x04 /* command register */
62
63
#define EC_SC_IBF 0x02 /* data ready for embedded controller */
64
#define EC_SC_OBF 0x01 /* data ready for host */
65
#define EC_CMD_WR 0x81 /* write EC */
66
#define EC_CMD_RD 0x80 /* read EC */
67
68
/*
69
* ACPI 3.0, Chapter 12, SMBus Host Controller Interface.
70
*/
71
#define SMB_PRTCL 0x00 /* protocol */
72
#define SMB_STS 0x01 /* status */
73
#define SMB_ADDR 0x02 /* address */
74
#define SMB_CMD 0x03 /* command */
75
#define SMB_DATA 0x04 /* 32 data registers */
76
#define SMB_BCNT 0x24 /* number of data bytes */
77
#define SMB_ALRM_A 0x25 /* alarm address */
78
#define SMB_ALRM_D 0x26 /* 2 bytes alarm data */
79
80
#define SMB_STS_DONE 0x80
81
#define SMB_STS_ALRM 0x40
82
#define SMB_STS_RES 0x20
83
#define SMB_STS_STATUS 0x1f
84
#define SMB_STS_OK 0x00 /* OK */
85
#define SMB_STS_UF 0x07 /* Unknown Failure */
86
#define SMB_STS_DANA 0x10 /* Device Address Not Acknowledged */
87
#define SMB_STS_DED 0x11 /* Device Error Detected */
88
#define SMB_STS_DCAD 0x12 /* Device Command Access Denied */
89
#define SMB_STS_UE 0x13 /* Unknown Error */
90
#define SMB_STS_DAD 0x17 /* Device Access Denied */
91
#define SMB_STS_T 0x18 /* Timeout */
92
#define SMB_STS_HUP 0x19 /* Host Unsupported Protocol */
93
#define SMB_STS_B 0x1a /* Busy */
94
#define SMB_STS_PEC 0x1f /* PEC (CRC-8) Error */
95
96
#define SMB_PRTCL_WRITE 0x00
97
#define SMB_PRTCL_READ 0x01
98
#define SMB_PRTCL_QUICK 0x02
99
#define SMB_PRTCL_BYTE 0x04
100
#define SMB_PRTCL_BYTE_DATA 0x06
101
#define SMB_PRTCL_WORD_DATA 0x08
102
#define SMB_PRTCL_BLOCK_DATA 0x0a
103
#define SMB_PRTCL_PROC_CALL 0x0c
104
#define SMB_PRTCL_BLOCK_PROC_CALL 0x0d
105
#define SMB_PRTCL_PEC 0x80
106
107
struct amdsmb_softc {
108
int rid;
109
struct resource *res;
110
device_t smbus;
111
struct mtx lock;
112
};
113
114
#define AMDSMB_LOCK(amdsmb) mtx_lock(&(amdsmb)->lock)
115
#define AMDSMB_UNLOCK(amdsmb) mtx_unlock(&(amdsmb)->lock)
116
#define AMDSMB_LOCK_ASSERT(amdsmb) mtx_assert(&(amdsmb)->lock, MA_OWNED)
117
118
#define AMDSMB_ECINB(amdsmb, register) \
119
(bus_read_1(amdsmb->res, register))
120
#define AMDSMB_ECOUTB(amdsmb, register, value) \
121
(bus_write_1(amdsmb->res, register, value))
122
123
static int amdsmb_detach(device_t dev);
124
125
struct pci_device_table amdsmb_devs[] = {
126
{ PCI_DEV(AMDSMB_VENDORID_AMD, AMDSMB_DEVICEID_AMD8111_SMB2),
127
PCI_DESCR("AMD-8111 SMBus 2.0 Controller") }
128
};
129
130
static int
131
amdsmb_probe(device_t dev)
132
{
133
const struct pci_device_table *tbl;
134
135
tbl = PCI_MATCH(dev, amdsmb_devs);
136
if (tbl == NULL)
137
return (ENXIO);
138
device_set_desc(dev, tbl->descr);
139
140
return (BUS_PROBE_DEFAULT);
141
}
142
143
static int
144
amdsmb_attach(device_t dev)
145
{
146
struct amdsmb_softc *amdsmb_sc = device_get_softc(dev);
147
148
/* Allocate I/O space */
149
amdsmb_sc->rid = PCIR_BAR(0);
150
151
amdsmb_sc->res = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
152
&amdsmb_sc->rid, RF_ACTIVE);
153
154
if (amdsmb_sc->res == NULL) {
155
device_printf(dev, "could not map i/o space\n");
156
return (ENXIO);
157
}
158
159
mtx_init(&amdsmb_sc->lock, device_get_nameunit(dev), "amdsmb", MTX_DEF);
160
161
/* Allocate a new smbus device */
162
amdsmb_sc->smbus = device_add_child(dev, "smbus", DEVICE_UNIT_ANY);
163
if (!amdsmb_sc->smbus) {
164
amdsmb_detach(dev);
165
return (EINVAL);
166
}
167
168
bus_attach_children(dev);
169
170
return (0);
171
}
172
173
static int
174
amdsmb_detach(device_t dev)
175
{
176
struct amdsmb_softc *amdsmb_sc = device_get_softc(dev);
177
int error;
178
179
error = bus_generic_detach(dev);
180
if (error != 0)
181
return (error);
182
183
mtx_destroy(&amdsmb_sc->lock);
184
if (amdsmb_sc->res)
185
bus_release_resource(dev, SYS_RES_IOPORT, amdsmb_sc->rid,
186
amdsmb_sc->res);
187
188
return (0);
189
}
190
191
static int
192
amdsmb_callback(device_t dev, int index, void *data)
193
{
194
int error = 0;
195
196
switch (index) {
197
case SMB_REQUEST_BUS:
198
case SMB_RELEASE_BUS:
199
break;
200
default:
201
error = EINVAL;
202
}
203
204
return (error);
205
}
206
207
static int
208
amdsmb_ec_wait_write(struct amdsmb_softc *sc)
209
{
210
int timeout = 500;
211
212
while (timeout-- && AMDSMB_ECINB(sc, EC_SC) & EC_SC_IBF)
213
DELAY(1);
214
if (timeout == 0) {
215
device_printf(sc->smbus, "timeout waiting for IBF to clear\n");
216
return (1);
217
}
218
return (0);
219
}
220
221
static int
222
amdsmb_ec_wait_read(struct amdsmb_softc *sc)
223
{
224
int timeout = 500;
225
226
while (timeout-- && ~AMDSMB_ECINB(sc, EC_SC) & EC_SC_OBF)
227
DELAY(1);
228
if (timeout == 0) {
229
device_printf(sc->smbus, "timeout waiting for OBF to set\n");
230
return (1);
231
}
232
return (0);
233
}
234
235
static int
236
amdsmb_ec_read(struct amdsmb_softc *sc, u_char addr, u_char *data)
237
{
238
239
AMDSMB_LOCK_ASSERT(sc);
240
if (amdsmb_ec_wait_write(sc))
241
return (1);
242
AMDSMB_ECOUTB(sc, EC_CMD, EC_CMD_RD);
243
244
if (amdsmb_ec_wait_write(sc))
245
return (1);
246
AMDSMB_ECOUTB(sc, EC_DATA, addr);
247
248
if (amdsmb_ec_wait_read(sc))
249
return (1);
250
*data = AMDSMB_ECINB(sc, EC_DATA);
251
252
return (0);
253
}
254
255
static int
256
amdsmb_ec_write(struct amdsmb_softc *sc, u_char addr, u_char data)
257
{
258
259
AMDSMB_LOCK_ASSERT(sc);
260
if (amdsmb_ec_wait_write(sc))
261
return (1);
262
AMDSMB_ECOUTB(sc, EC_CMD, EC_CMD_WR);
263
264
if (amdsmb_ec_wait_write(sc))
265
return (1);
266
AMDSMB_ECOUTB(sc, EC_DATA, addr);
267
268
if (amdsmb_ec_wait_write(sc))
269
return (1);
270
AMDSMB_ECOUTB(sc, EC_DATA, data);
271
272
return (0);
273
}
274
275
static int
276
amdsmb_wait(struct amdsmb_softc *sc)
277
{
278
u_char sts, temp;
279
int error, count;
280
281
AMDSMB_LOCK_ASSERT(sc);
282
amdsmb_ec_read(sc, SMB_PRTCL, &temp);
283
if (temp != 0)
284
{
285
count = 10000;
286
do {
287
DELAY(500);
288
amdsmb_ec_read(sc, SMB_PRTCL, &temp);
289
} while (temp != 0 && count--);
290
if (count == 0)
291
return (SMB_ETIMEOUT);
292
}
293
294
amdsmb_ec_read(sc, SMB_STS, &sts);
295
sts &= SMB_STS_STATUS;
296
AMDSMB_DEBUG(printf("amdsmb: STS=0x%x\n", sts));
297
298
switch (sts) {
299
case SMB_STS_OK:
300
error = SMB_ENOERR;
301
break;
302
case SMB_STS_DANA:
303
error = SMB_ENOACK;
304
break;
305
case SMB_STS_B:
306
error = SMB_EBUSY;
307
break;
308
case SMB_STS_T:
309
error = SMB_ETIMEOUT;
310
break;
311
case SMB_STS_DCAD:
312
case SMB_STS_DAD:
313
case SMB_STS_HUP:
314
error = SMB_ENOTSUPP;
315
break;
316
default:
317
error = SMB_EBUSERR;
318
break;
319
}
320
321
return (error);
322
}
323
324
static int
325
amdsmb_quick(device_t dev, u_char slave, int how)
326
{
327
struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
328
u_char protocol;
329
int error;
330
331
protocol = SMB_PRTCL_QUICK;
332
333
switch (how) {
334
case SMB_QWRITE:
335
protocol |= SMB_PRTCL_WRITE;
336
AMDSMB_DEBUG(printf("amdsmb: QWRITE to 0x%x", slave));
337
break;
338
case SMB_QREAD:
339
protocol |= SMB_PRTCL_READ;
340
AMDSMB_DEBUG(printf("amdsmb: QREAD to 0x%x", slave));
341
break;
342
default:
343
panic("%s: unknown QUICK command (%x)!", __func__, how);
344
}
345
346
AMDSMB_LOCK(sc);
347
amdsmb_ec_write(sc, SMB_ADDR, slave);
348
amdsmb_ec_write(sc, SMB_PRTCL, protocol);
349
350
error = amdsmb_wait(sc);
351
352
AMDSMB_DEBUG(printf(", error=0x%x\n", error));
353
AMDSMB_UNLOCK(sc);
354
355
return (error);
356
}
357
358
static int
359
amdsmb_sendb(device_t dev, u_char slave, char byte)
360
{
361
struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
362
int error;
363
364
AMDSMB_LOCK(sc);
365
amdsmb_ec_write(sc, SMB_CMD, byte);
366
amdsmb_ec_write(sc, SMB_ADDR, slave);
367
amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE);
368
369
error = amdsmb_wait(sc);
370
371
AMDSMB_DEBUG(printf("amdsmb: SENDB to 0x%x, byte=0x%x, error=0x%x\n",
372
slave, byte, error));
373
AMDSMB_UNLOCK(sc);
374
375
return (error);
376
}
377
378
static int
379
amdsmb_recvb(device_t dev, u_char slave, char *byte)
380
{
381
struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
382
int error;
383
384
AMDSMB_LOCK(sc);
385
amdsmb_ec_write(sc, SMB_ADDR, slave);
386
amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE);
387
388
if ((error = amdsmb_wait(sc)) == SMB_ENOERR)
389
amdsmb_ec_read(sc, SMB_DATA, byte);
390
391
AMDSMB_DEBUG(printf("amdsmb: RECVB from 0x%x, byte=0x%x, error=0x%x\n",
392
slave, *byte, error));
393
AMDSMB_UNLOCK(sc);
394
395
return (error);
396
}
397
398
static int
399
amdsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
400
{
401
struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
402
int error;
403
404
AMDSMB_LOCK(sc);
405
amdsmb_ec_write(sc, SMB_CMD, cmd);
406
amdsmb_ec_write(sc, SMB_DATA, byte);
407
amdsmb_ec_write(sc, SMB_ADDR, slave);
408
amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BYTE_DATA);
409
410
error = amdsmb_wait(sc);
411
412
AMDSMB_DEBUG(printf("amdsmb: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, "
413
"error=0x%x\n", slave, cmd, byte, error));
414
AMDSMB_UNLOCK(sc);
415
416
return (error);
417
}
418
419
static int
420
amdsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
421
{
422
struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
423
int error;
424
425
AMDSMB_LOCK(sc);
426
amdsmb_ec_write(sc, SMB_CMD, cmd);
427
amdsmb_ec_write(sc, SMB_ADDR, slave);
428
amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BYTE_DATA);
429
430
if ((error = amdsmb_wait(sc)) == SMB_ENOERR)
431
amdsmb_ec_read(sc, SMB_DATA, byte);
432
433
AMDSMB_DEBUG(printf("amdsmb: READB from 0x%x, cmd=0x%x, byte=0x%x, "
434
"error=0x%x\n", slave, cmd, (unsigned char)*byte, error));
435
AMDSMB_UNLOCK(sc);
436
437
return (error);
438
}
439
440
static int
441
amdsmb_writew(device_t dev, u_char slave, char cmd, short word)
442
{
443
struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
444
int error;
445
446
AMDSMB_LOCK(sc);
447
amdsmb_ec_write(sc, SMB_CMD, cmd);
448
amdsmb_ec_write(sc, SMB_DATA, word);
449
amdsmb_ec_write(sc, SMB_DATA + 1, word >> 8);
450
amdsmb_ec_write(sc, SMB_ADDR, slave);
451
amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_WORD_DATA);
452
453
error = amdsmb_wait(sc);
454
455
AMDSMB_DEBUG(printf("amdsmb: WRITEW to 0x%x, cmd=0x%x, word=0x%x, "
456
"error=0x%x\n", slave, cmd, word, error));
457
AMDSMB_UNLOCK(sc);
458
459
return (error);
460
}
461
462
static int
463
amdsmb_readw(device_t dev, u_char slave, char cmd, short *word)
464
{
465
struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
466
u_char temp[2];
467
int error;
468
469
AMDSMB_LOCK(sc);
470
amdsmb_ec_write(sc, SMB_CMD, cmd);
471
amdsmb_ec_write(sc, SMB_ADDR, slave);
472
amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_WORD_DATA);
473
474
if ((error = amdsmb_wait(sc)) == SMB_ENOERR) {
475
amdsmb_ec_read(sc, SMB_DATA + 0, &temp[0]);
476
amdsmb_ec_read(sc, SMB_DATA + 1, &temp[1]);
477
*word = temp[0] | (temp[1] << 8);
478
}
479
480
AMDSMB_DEBUG(printf("amdsmb: READW from 0x%x, cmd=0x%x, word=0x%x, "
481
"error=0x%x\n", slave, cmd, (unsigned short)*word, error));
482
AMDSMB_UNLOCK(sc);
483
484
return (error);
485
}
486
487
static int
488
amdsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
489
{
490
struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
491
u_char i;
492
int error;
493
494
if (count < 1 || count > 32)
495
return (SMB_EINVAL);
496
497
AMDSMB_LOCK(sc);
498
amdsmb_ec_write(sc, SMB_CMD, cmd);
499
amdsmb_ec_write(sc, SMB_BCNT, count);
500
for (i = 0; i < count; i++)
501
amdsmb_ec_write(sc, SMB_DATA + i, buf[i]);
502
amdsmb_ec_write(sc, SMB_ADDR, slave);
503
amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_WRITE | SMB_PRTCL_BLOCK_DATA);
504
505
error = amdsmb_wait(sc);
506
507
AMDSMB_DEBUG(printf("amdsmb: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, "
508
"error=0x%x", slave, count, cmd, error));
509
AMDSMB_UNLOCK(sc);
510
511
return (error);
512
}
513
514
static int
515
amdsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
516
{
517
struct amdsmb_softc *sc = (struct amdsmb_softc *)device_get_softc(dev);
518
u_char data, len, i;
519
int error;
520
521
if (*count < 1 || *count > 32)
522
return (SMB_EINVAL);
523
524
AMDSMB_LOCK(sc);
525
amdsmb_ec_write(sc, SMB_CMD, cmd);
526
amdsmb_ec_write(sc, SMB_ADDR, slave);
527
amdsmb_ec_write(sc, SMB_PRTCL, SMB_PRTCL_READ | SMB_PRTCL_BLOCK_DATA);
528
529
if ((error = amdsmb_wait(sc)) == SMB_ENOERR) {
530
amdsmb_ec_read(sc, SMB_BCNT, &len);
531
for (i = 0; i < len; i++) {
532
amdsmb_ec_read(sc, SMB_DATA + i, &data);
533
if (i < *count)
534
buf[i] = data;
535
}
536
*count = len;
537
}
538
539
AMDSMB_DEBUG(printf("amdsmb: READBLK to 0x%x, count=0x%x, cmd=0x%x, "
540
"error=0x%x", slave, *count, cmd, error));
541
AMDSMB_UNLOCK(sc);
542
543
return (error);
544
}
545
546
static device_method_t amdsmb_methods[] = {
547
/* Device interface */
548
DEVMETHOD(device_probe, amdsmb_probe),
549
DEVMETHOD(device_attach, amdsmb_attach),
550
DEVMETHOD(device_detach, amdsmb_detach),
551
552
/* SMBus interface */
553
DEVMETHOD(smbus_callback, amdsmb_callback),
554
DEVMETHOD(smbus_quick, amdsmb_quick),
555
DEVMETHOD(smbus_sendb, amdsmb_sendb),
556
DEVMETHOD(smbus_recvb, amdsmb_recvb),
557
DEVMETHOD(smbus_writeb, amdsmb_writeb),
558
DEVMETHOD(smbus_readb, amdsmb_readb),
559
DEVMETHOD(smbus_writew, amdsmb_writew),
560
DEVMETHOD(smbus_readw, amdsmb_readw),
561
DEVMETHOD(smbus_bwrite, amdsmb_bwrite),
562
DEVMETHOD(smbus_bread, amdsmb_bread),
563
{ 0, 0 }
564
};
565
566
static driver_t amdsmb_driver = {
567
"amdsmb",
568
amdsmb_methods,
569
sizeof(struct amdsmb_softc),
570
};
571
572
DRIVER_MODULE(amdsmb, pci, amdsmb_driver, 0, 0);
573
DRIVER_MODULE(smbus, amdsmb, smbus_driver, 0, 0);
574
575
MODULE_DEPEND(amdsmb, pci, 1, 1, 1);
576
MODULE_DEPEND(amdsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
577
MODULE_VERSION(amdsmb, 1);
578
579