Home | History | Annotate | Line # | Download | only in acpi
      1 /* $NetBSD: smbus_acpi.c,v 1.18 2022/10/24 10:17:27 riastradh Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2009 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Paul Goyette
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * ACPI SMBus Controller driver
     34  *
     35  * See http://smbus.org/specs/smbus_cmi10.pdf for specifications
     36  */
     37 
     38 #include <sys/cdefs.h>
     39 __KERNEL_RCSID(0, "$NetBSD: smbus_acpi.c,v 1.18 2022/10/24 10:17:27 riastradh Exp $");
     40 
     41 #include <sys/param.h>
     42 #include <sys/device.h>
     43 #include <sys/callout.h>
     44 #include <sys/kernel.h>
     45 #include <sys/mutex.h>
     46 #include <sys/systm.h>
     47 
     48 #include <dev/acpi/acpireg.h>
     49 #include <dev/acpi/acpivar.h>
     50 
     51 #include <dev/i2c/i2cvar.h>
     52 
     53 #define _COMPONENT		ACPI_BUS_COMPONENT
     54 ACPI_MODULE_NAME		("smbus_acpi")
     55 
     56 /*
     57  * ACPI SMBus CMI protocol codes.
     58  */
     59 #define	ACPI_SMBUS_RD_QUICK	0x03
     60 #define	ACPI_SMBUS_RCV_BYTE	0x05
     61 #define	ACPI_SMBUS_RD_BYTE	0x07
     62 #define	ACPI_SMBUS_RD_WORD	0x09
     63 #define	ACPI_SMBUS_RD_BLOCK	0x0B
     64 #define	ACPI_SMBUS_WR_QUICK	0x02
     65 #define	ACPI_SMBUS_SND_BYTE	0x04
     66 #define	ACPI_SMBUS_WR_BYTE	0x06
     67 #define	ACPI_SMBUS_WR_WORD	0x08
     68 #define	ACPI_SMBUS_WR_BLOCK	0x0A
     69 #define	ACPI_SMBUS_PROCESS_CALL	0x0C
     70 
     71 struct acpi_smbus_softc {
     72 	struct acpi_devnode 	*sc_devnode;
     73 	struct callout		sc_callout;
     74 	struct i2c_controller	sc_i2c_tag;
     75 	device_t		sc_dv;
     76 	int			sc_poll_alert;
     77 };
     78 
     79 static int	acpi_smbus_match(device_t, cfdata_t, void *);
     80 static void	acpi_smbus_attach(device_t, device_t, void *);
     81 static int	acpi_smbus_detach(device_t, int);
     82 static int	acpi_smbus_poll_alert(ACPI_HANDLE, int *);
     83 static int	acpi_smbus_exec(void *, i2c_op_t, i2c_addr_t, const void *,
     84 				size_t, void *, size_t, int);
     85 static void	acpi_smbus_alerts(struct acpi_smbus_softc *);
     86 static void	acpi_smbus_tick(void *);
     87 static void	acpi_smbus_notify_handler(ACPI_HANDLE, uint32_t, void *);
     88 
     89 struct SMB_UDID {
     90 	uint8_t		dev_cap;
     91 	uint8_t		vers_rev;
     92 	uint16_t	vendor;
     93 	uint16_t	device;
     94 	uint16_t	interface;
     95 	uint16_t	subsys_vendor;
     96 	uint16_t	subsys_device;
     97 	uint8_t		reserved[4];
     98 };
     99 
    100 struct SMB_DEVICE {
    101 	uint8_t		slave_addr;
    102 	uint8_t		reserved;
    103 	struct SMB_UDID	dev_id;
    104 };
    105 
    106 struct SMB_INFO {
    107 	uint8_t		struct_ver;
    108 	uint8_t		spec_ver;
    109 	uint8_t		hw_cap;
    110 	uint8_t		poll_int;
    111 	uint8_t		dev_count;
    112 	struct SMB_DEVICE device[1];
    113 };
    114 
    115 static const struct device_compatible_entry compat_data[] = {
    116 	{ .compat = "SMBUS01" },	/* SMBus CMI v1.0 */
    117 	DEVICE_COMPAT_EOL
    118 };
    119 
    120 CFATTACH_DECL_NEW(acpismbus, sizeof(struct acpi_smbus_softc),
    121     acpi_smbus_match, acpi_smbus_attach, acpi_smbus_detach, NULL);
    122 
    123 static int
    124 acpi_smbus_match(device_t parent, cfdata_t match, void *aux)
    125 {
    126 	struct acpi_attach_args *aa = aux;
    127 	int ret;
    128 
    129 	ret = acpi_compatible_match(aa, compat_data);
    130 	if (ret == 0)
    131 		return 0;
    132 
    133 	return acpi_smbus_poll_alert(aa->aa_node->ad_handle, NULL) ? ret : 0;
    134 }
    135 
    136 static void
    137 acpi_smbus_attach(device_t parent, device_t self, void *aux)
    138 {
    139 	struct acpi_smbus_softc *sc = device_private(self);
    140 	struct acpi_attach_args *aa = aux;
    141 	struct i2cbus_attach_args iba;
    142 
    143 	aprint_naive("\n");
    144 
    145 	sc->sc_devnode = aa->aa_node;
    146 	sc->sc_dv = self;
    147 	sc->sc_poll_alert = 2;
    148 
    149 	/* Attach I2C bus. */
    150 	iic_tag_init(&sc->sc_i2c_tag);
    151 	sc->sc_i2c_tag.ic_cookie = sc;
    152 	sc->sc_i2c_tag.ic_exec = acpi_smbus_exec;
    153 
    154 	(void)acpi_smbus_poll_alert(aa->aa_node->ad_handle,&sc->sc_poll_alert);
    155 
    156 	/* If failed, fall-back to polling. */
    157 	if (acpi_register_notify(sc->sc_devnode,
    158 		acpi_smbus_notify_handler) != true)
    159 		sc->sc_poll_alert = 2;
    160 
    161 	callout_init(&sc->sc_callout, 0);
    162 	callout_setfunc(&sc->sc_callout, acpi_smbus_tick, self);
    163 
    164 	if (sc->sc_poll_alert != 0) {
    165 		aprint_debug(": alert_poll %d sec", sc->sc_poll_alert);
    166 		callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz);
    167 	}
    168 
    169 	aprint_normal("\n");
    170 
    171 	(void)memset(&iba, 0, sizeof(iba));
    172 	(void)pmf_device_register(self, NULL, NULL);
    173 
    174 	iba.iba_tag = &sc->sc_i2c_tag;
    175 
    176 	config_found(self, &iba, iicbus_print, CFARGS_NONE);
    177 }
    178 
    179 static int
    180 acpi_smbus_detach(device_t self, int flags)
    181 {
    182 	struct acpi_smbus_softc *sc = device_private(self);
    183 
    184 	pmf_device_deregister(self);
    185 	acpi_deregister_notify(sc->sc_devnode);
    186 
    187 	callout_halt(&sc->sc_callout, NULL);
    188 	callout_destroy(&sc->sc_callout);
    189 
    190 	iic_tag_fini(&sc->sc_i2c_tag);
    191 
    192 	return 0;
    193 }
    194 
    195 static int
    196 acpi_smbus_poll_alert(ACPI_HANDLE hdl, int *alert)
    197 {
    198 	struct SMB_INFO *info;
    199 	ACPI_BUFFER smi_buf;
    200 	ACPI_OBJECT *e, *p;
    201 	ACPI_STATUS rv;
    202 
    203 	/*
    204 	 * Retrieve polling interval for SMBus Alerts.
    205 	 */
    206 	rv = acpi_eval_struct(hdl, "_SBI", &smi_buf);
    207 
    208 	if (ACPI_FAILURE(rv))
    209 		return 0;
    210 
    211 	p = smi_buf.Pointer;
    212 
    213 	if (p->Type != ACPI_TYPE_PACKAGE) {
    214 		rv = AE_TYPE;
    215 		goto out;
    216 	}
    217 
    218 	if (p->Package.Count == 0) {
    219 		rv = AE_LIMIT;
    220 		goto out;
    221 	}
    222 
    223 	e = p->Package.Elements;
    224 
    225 	if (e[0].Type != ACPI_TYPE_INTEGER) {
    226 		rv = AE_TYPE;
    227 		goto out;
    228 	}
    229 
    230 	/* Verify CMI version. */
    231 	if (e[0].Integer.Value != 0x10) {
    232 		rv = AE_SUPPORT;
    233 		goto out;
    234 	}
    235 
    236 	if (alert != NULL) {
    237 
    238 		if (p->Package.Count < 2)
    239 			goto out;
    240 
    241 		if (e[1].Type != ACPI_TYPE_BUFFER)
    242 			goto out;
    243 
    244 		info = (struct SMB_INFO *)(e[1].Buffer.Pointer);
    245 		*alert = info->poll_int;
    246 	}
    247 
    248 out:
    249 	if (smi_buf.Pointer != NULL)
    250 		ACPI_FREE(smi_buf.Pointer);
    251 
    252 	return (ACPI_FAILURE(rv)) ? 0 : 1;
    253 }
    254 
    255 static int
    256 acpi_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
    257 	const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
    258 {
    259         struct acpi_smbus_softc *sc = cookie;
    260 	const uint8_t *c = cmdbuf;
    261 	uint8_t *b = buf, *xb;
    262 	const char *path;
    263 	ACPI_OBJECT_LIST args;
    264 	ACPI_OBJECT arg[5];
    265 	ACPI_OBJECT *p, *e;
    266 	ACPI_BUFFER smbuf;
    267 	ACPI_STATUS rv;
    268 	int i, r, xlen;
    269 
    270 	/*
    271 	 *	arg[0] : protocol
    272 	 *	arg[1] : slave address
    273 	 *	arg[2] : command
    274 	 *	arg[3] : data length
    275 	 *	arg[4] : data
    276 	 */
    277 	for (i = r = 0; i < __arraycount(arg); i++)
    278 		arg[i].Type = ACPI_TYPE_INTEGER;
    279 
    280 	args.Pointer = arg;
    281 
    282 	smbuf.Pointer = NULL;
    283 	smbuf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
    284 
    285 	arg[1].Integer.Value = addr;
    286 
    287 	if (I2C_OP_READ_P(op)) {
    288 
    289 		path = "_SBR";
    290 		args.Count = 3;
    291 
    292 		switch (len) {
    293 
    294 		case 0:
    295 			arg[0].Integer.Value = (cmdlen != 0) ?
    296 			    ACPI_SMBUS_RCV_BYTE : ACPI_SMBUS_RD_QUICK;
    297 
    298 			arg[2].Integer.Value = 0;
    299 			break;
    300 
    301 		case 1:
    302 			arg[0].Integer.Value = ACPI_SMBUS_RD_BYTE;
    303 			arg[2].Integer.Value = *c;
    304 			break;
    305 
    306 		case 2:
    307 			arg[0].Integer.Value = ACPI_SMBUS_RD_WORD;
    308 			arg[2].Integer.Value = *c;
    309 			break;
    310 
    311 		default:
    312 			arg[0].Integer.Value = ACPI_SMBUS_RD_BLOCK;
    313 			arg[2].Integer.Value = *c;
    314 			break;
    315 		}
    316 
    317 	} else {
    318 
    319 		path = "_SBW";
    320 		args.Count = 5;
    321 
    322 		arg[3].Integer.Value = len;
    323 
    324 		switch (len) {
    325 
    326 		case 0:
    327 			if (cmdlen == 0) {
    328 				arg[2].Integer.Value = 0;
    329 				arg[0].Integer.Value = ACPI_SMBUS_WR_QUICK;
    330 			} else {
    331 				arg[2].Integer.Value = *c;
    332 				arg[0].Integer.Value = ACPI_SMBUS_SND_BYTE;
    333 			}
    334 
    335 			arg[4].Integer.Value = 0;
    336 			break;
    337 
    338 		case 1:
    339 			arg[0].Integer.Value = ACPI_SMBUS_WR_BYTE;
    340 			arg[2].Integer.Value = *c;
    341 			arg[4].Integer.Value = *b;
    342 			break;
    343 
    344 		case 2:
    345 			arg[0].Integer.Value = ACPI_SMBUS_WR_WORD;
    346 			arg[2].Integer.Value = *c;
    347 			arg[4].Integer.Value = *b++;
    348 			arg[4].Integer.Value += (*b--) << 8;
    349 			break;
    350 
    351 		default:
    352 			arg[0].Integer.Value = ACPI_SMBUS_WR_BLOCK;
    353 			arg[2].Integer.Value = *c;
    354 			arg[4].Type = ACPI_TYPE_BUFFER;
    355 			arg[4].Buffer.Pointer = buf;
    356 			arg[4].Buffer.Length = (len < 32) ? len : 32;
    357 			break;
    358 		}
    359 	}
    360 
    361 	rv = AcpiEvaluateObject(sc->sc_devnode->ad_handle, path, &args,&smbuf);
    362 
    363 	if (ACPI_FAILURE(rv))
    364 		goto out;
    365 
    366 	p = smbuf.Pointer;
    367 
    368 	if (p->Type != ACPI_TYPE_PACKAGE) {
    369 		rv = AE_TYPE;
    370 		goto out;
    371 	}
    372 
    373 	if (p->Package.Count < 1) {
    374 		rv = AE_LIMIT;
    375 		goto out;
    376 	}
    377 
    378 	e = p->Package.Elements;
    379 
    380 	if (e->Type != ACPI_TYPE_INTEGER) {
    381 		rv = AE_TYPE;
    382 		goto out;
    383 	}
    384 
    385 	ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT,
    386 		"return status: %"PRIu64"\n", e[0].Integer.Value));
    387 
    388 	if (e[0].Integer.Value != 0) {
    389 		rv = AE_BAD_VALUE;
    390 		goto out;
    391 	}
    392 
    393 	/*
    394 	 * For read operations, copy data to user buffer.
    395 	 */
    396 	if (I2C_OP_READ_P(op)) {
    397 
    398 		if (p->Package.Count < 3) {
    399 			rv = AE_LIMIT;
    400 			goto out;
    401 		}
    402 
    403 		if (e[1].Type != ACPI_TYPE_INTEGER) {
    404 			rv = AE_TYPE;
    405 			goto out;
    406 		}
    407 
    408 		xlen = e[1].Integer.Value;
    409 
    410 		if (xlen > len)
    411 			xlen = len;
    412 
    413 		switch (e[2].Type) {
    414 
    415 		case ACPI_TYPE_BUFFER:
    416 
    417 			if (xlen == 0) {
    418 				rv = AE_LIMIT;
    419 				goto out;
    420 			}
    421 
    422 			xb = e[2].Buffer.Pointer;
    423 
    424 			if (xb == NULL) {
    425 				rv = AE_NULL_OBJECT;
    426 				goto out;
    427 			}
    428 
    429 			(void)memcpy(b, xb, xlen);
    430 			break;
    431 
    432 		case ACPI_TYPE_INTEGER:
    433 
    434 			if (xlen > 0)
    435 				*b++ = e[2].Integer.Value & 0xff;
    436 
    437 			if (xlen > 1)
    438 				*b = e[2].Integer.Value >> 8;
    439 
    440 			break;
    441 
    442 		default:
    443 			rv = AE_TYPE;
    444 			goto out;
    445 		}
    446 	}
    447 
    448 out:
    449 	if (smbuf.Pointer != NULL)
    450 		ACPI_FREE(smbuf.Pointer);
    451 
    452 	if (ACPI_SUCCESS(rv))
    453 		return 0;
    454 
    455 	ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, "failed to "
    456 		"evaluate %s: %s\n", path, AcpiFormatException(rv)));
    457 
    458 	return 1;
    459 }
    460 
    461 /*
    462  * Whether triggered by periodic polling or a Notify(),
    463  * retrieve all pending SMBus device alerts.
    464  */
    465 static void
    466 acpi_smbus_alerts(struct acpi_smbus_softc *sc)
    467 {
    468 	const ACPI_HANDLE hdl = sc->sc_devnode->ad_handle;
    469 	ACPI_OBJECT *e, *p;
    470 	ACPI_BUFFER alert;
    471 	ACPI_STATUS rv;
    472 	int status = 0;
    473 	uint8_t addr;
    474 
    475 	do {
    476 		rv = acpi_eval_struct(hdl, "_SBA", &alert);
    477 
    478 		if (ACPI_FAILURE(rv)) {
    479 			status = 1;
    480 			goto done;
    481 		}
    482 
    483 		p = alert.Pointer;
    484 
    485 		if (p->Type == ACPI_TYPE_PACKAGE && p->Package.Count >= 2) {
    486 
    487 			status = 1;
    488 
    489 			e = p->Package.Elements;
    490 
    491 			if (e[0].Type == ACPI_TYPE_INTEGER)
    492 				status = e[0].Integer.Value;
    493 
    494 			if (status == 0 && e[1].Type == ACPI_TYPE_INTEGER) {
    495 				addr = e[1].Integer.Value;
    496 
    497 				aprint_debug_dev(sc->sc_dv,
    498 				    "alert for 0x%x\n", addr);
    499 			}
    500 		}
    501 done:
    502 		if (alert.Pointer != NULL)
    503 			ACPI_FREE(alert.Pointer);
    504 
    505 	} while (status == 0);
    506 }
    507 
    508 static void
    509 acpi_smbus_tick(void *opaque)
    510 {
    511 	device_t dv = opaque;
    512 	struct acpi_smbus_softc *sc = device_private(dv);
    513 
    514 	acpi_smbus_alerts(sc);
    515 
    516 	callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz);
    517 }
    518 
    519 static void
    520 acpi_smbus_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque)
    521 {
    522 	device_t dv = opaque;
    523 	struct acpi_smbus_softc *sc = device_private(dv);
    524 
    525 	aprint_debug_dev(dv, "received notify message 0x%x\n", notify);
    526 
    527 	acpi_smbus_alerts(sc);
    528 }
    529