Home | History | Annotate | Line # | Download | only in acpi
smbus_acpi.c revision 1.14
      1 /* $NetBSD: smbus_acpi.c,v 1.14 2019/12/22 23:23:32 thorpej 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.14 2019/12/22 23:23:32 thorpej 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 char * const smbus_acpi_ids[] = {
    116 	"SMBUS01",	/* SMBus CMI v1.0 */
    117 	NULL
    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 
    128 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
    129 		return 0;
    130 
    131 	if (acpi_match_hid(aa->aa_node->ad_devinfo, smbus_acpi_ids) == 0)
    132 		return 0;
    133 
    134 	return acpi_smbus_poll_alert(aa->aa_node->ad_handle, NULL);
    135 }
    136 
    137 static void
    138 acpi_smbus_attach(device_t parent, device_t self, void *aux)
    139 {
    140 	struct acpi_smbus_softc *sc = device_private(self);
    141 	struct acpi_attach_args *aa = aux;
    142 	struct i2cbus_attach_args iba;
    143 
    144 	aprint_naive("\n");
    145 
    146 	sc->sc_devnode = aa->aa_node;
    147 	sc->sc_dv = self;
    148 	sc->sc_poll_alert = 2;
    149 
    150 	/* Attach I2C bus. */
    151 	iic_tag_init(&sc->sc_i2c_tag);
    152 	sc->sc_i2c_tag.ic_cookie = sc;
    153 	sc->sc_i2c_tag.ic_exec = acpi_smbus_exec;
    154 
    155 	(void)acpi_smbus_poll_alert(aa->aa_node->ad_handle,&sc->sc_poll_alert);
    156 
    157 	/* If failed, fall-back to polling. */
    158 	if (acpi_register_notify(sc->sc_devnode,
    159 		acpi_smbus_notify_handler) != true)
    160 		sc->sc_poll_alert = 2;
    161 
    162 	callout_init(&sc->sc_callout, 0);
    163 	callout_setfunc(&sc->sc_callout, acpi_smbus_tick, self);
    164 
    165 	if (sc->sc_poll_alert != 0) {
    166 		aprint_debug(": alert_poll %d sec", sc->sc_poll_alert);
    167 		callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz);
    168 	}
    169 
    170 	aprint_normal("\n");
    171 
    172 	(void)memset(&iba, 0, sizeof(iba));
    173 	(void)pmf_device_register(self, NULL, NULL);
    174 
    175 	iba.iba_tag = &sc->sc_i2c_tag;
    176 
    177 	(void)config_found_ia(self, "i2cbus", &iba, iicbus_print);
    178 }
    179 
    180 static int
    181 acpi_smbus_detach(device_t self, int flags)
    182 {
    183 	struct acpi_smbus_softc *sc = device_private(self);
    184 
    185 	pmf_device_deregister(self);
    186 	acpi_deregister_notify(sc->sc_devnode);
    187 
    188 	callout_halt(&sc->sc_callout, NULL);
    189 	callout_destroy(&sc->sc_callout);
    190 
    191 	iic_tag_fini(&sc->sc_i2c_tag);
    192 
    193 	return 0;
    194 }
    195 
    196 static int
    197 acpi_smbus_poll_alert(ACPI_HANDLE hdl, int *alert)
    198 {
    199 	struct SMB_INFO *info;
    200 	ACPI_BUFFER smi_buf;
    201 	ACPI_OBJECT *e, *p;
    202 	ACPI_STATUS rv;
    203 
    204 	/*
    205 	 * Retrieve polling interval for SMBus Alerts.
    206 	 */
    207 	rv = acpi_eval_struct(hdl, "_SBI", &smi_buf);
    208 
    209 	if (ACPI_FAILURE(rv))
    210 		return 0;
    211 
    212 	p = smi_buf.Pointer;
    213 
    214 	if (p->Type != ACPI_TYPE_PACKAGE) {
    215 		rv = AE_TYPE;
    216 		goto out;
    217 	}
    218 
    219 	if (p->Package.Count == 0) {
    220 		rv = AE_LIMIT;
    221 		goto out;
    222 	}
    223 
    224 	e = p->Package.Elements;
    225 
    226 	if (e[0].Type != ACPI_TYPE_INTEGER) {
    227 		rv = AE_TYPE;
    228 		goto out;
    229 	}
    230 
    231 	/* Verify CMI version. */
    232 	if (e[0].Integer.Value != 0x10) {
    233 		rv = AE_SUPPORT;
    234 		goto out;
    235 	}
    236 
    237 	if (alert != NULL) {
    238 
    239 		if (p->Package.Count < 2)
    240 			goto out;
    241 
    242 		if (e[1].Type != ACPI_TYPE_BUFFER)
    243 			goto out;
    244 
    245 		info = (struct SMB_INFO *)(e[1].Buffer.Pointer);
    246 		*alert = info->poll_int;
    247 	}
    248 
    249 out:
    250 	if (smi_buf.Pointer != NULL)
    251 		ACPI_FREE(smi_buf.Pointer);
    252 
    253 	return (ACPI_FAILURE(rv)) ? 0 : 1;
    254 }
    255 
    256 static int
    257 acpi_smbus_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
    258 	const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
    259 {
    260         struct acpi_smbus_softc *sc = cookie;
    261 	const uint8_t *c = cmdbuf;
    262 	uint8_t *b = buf, *xb;
    263 	const char *path;
    264 	ACPI_OBJECT_LIST args;
    265 	ACPI_OBJECT arg[5];
    266 	ACPI_OBJECT *p, *e;
    267 	ACPI_BUFFER smbuf;
    268 	ACPI_STATUS rv;
    269 	int i, r, xlen;
    270 
    271 	/*
    272 	 *	arg[0] : protocol
    273 	 *	arg[1] : slave address
    274 	 *	arg[2] : command
    275 	 *	arg[3] : data length
    276 	 *	arg[4] : data
    277 	 */
    278 	for (i = r = 0; i < __arraycount(arg); i++)
    279 		arg[i].Type = ACPI_TYPE_INTEGER;
    280 
    281 	args.Pointer = arg;
    282 
    283 	smbuf.Pointer = NULL;
    284 	smbuf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;
    285 
    286 	arg[1].Integer.Value = addr;
    287 
    288 	if (I2C_OP_READ_P(op)) {
    289 
    290 		path = "_SBR";
    291 		args.Count = 3;
    292 
    293 		switch (len) {
    294 
    295 		case 0:
    296 			arg[0].Integer.Value = (cmdlen != 0) ?
    297 			    ACPI_SMBUS_RCV_BYTE : ACPI_SMBUS_RD_QUICK;
    298 
    299 			arg[2].Integer.Value = 0;
    300 			break;
    301 
    302 		case 1:
    303 			arg[0].Integer.Value = ACPI_SMBUS_RD_BYTE;
    304 			arg[2].Integer.Value = *c;
    305 			break;
    306 
    307 		case 2:
    308 			arg[0].Integer.Value = ACPI_SMBUS_RD_WORD;
    309 			arg[2].Integer.Value = *c;
    310 			break;
    311 
    312 		default:
    313 			arg[0].Integer.Value = ACPI_SMBUS_RD_BLOCK;
    314 			arg[2].Integer.Value = *c;
    315 			break;
    316 		}
    317 
    318 	} else {
    319 
    320 		path = "_SBW";
    321 		args.Count = 5;
    322 
    323 		arg[3].Integer.Value = len;
    324 
    325 		switch (len) {
    326 
    327 		case 0:
    328 			if (cmdlen == 0) {
    329 				arg[2].Integer.Value = 0;
    330 				arg[0].Integer.Value = ACPI_SMBUS_WR_QUICK;
    331 			} else {
    332 				arg[2].Integer.Value = *c;
    333 				arg[0].Integer.Value = ACPI_SMBUS_SND_BYTE;
    334 			}
    335 
    336 			arg[4].Integer.Value = 0;
    337 			break;
    338 
    339 		case 1:
    340 			arg[0].Integer.Value = ACPI_SMBUS_WR_BYTE;
    341 			arg[2].Integer.Value = *c;
    342 			arg[4].Integer.Value = *b;
    343 			break;
    344 
    345 		case 2:
    346 			arg[0].Integer.Value = ACPI_SMBUS_WR_WORD;
    347 			arg[2].Integer.Value = *c;
    348 			arg[4].Integer.Value = *b++;
    349 			arg[4].Integer.Value += (*b--) << 8;
    350 			break;
    351 
    352 		default:
    353 			arg[0].Integer.Value = ACPI_SMBUS_WR_BLOCK;
    354 			arg[2].Integer.Value = *c;
    355 			arg[4].Type = ACPI_TYPE_BUFFER;
    356 			arg[4].Buffer.Pointer = buf;
    357 			arg[4].Buffer.Length = (len < 32) ? len : 32;
    358 			break;
    359 		}
    360 	}
    361 
    362 	rv = AcpiEvaluateObject(sc->sc_devnode->ad_handle, path, &args,&smbuf);
    363 
    364 	if (ACPI_FAILURE(rv))
    365 		goto out;
    366 
    367 	p = smbuf.Pointer;
    368 
    369 	if (p->Type != ACPI_TYPE_PACKAGE) {
    370 		rv = AE_TYPE;
    371 		goto out;
    372 	}
    373 
    374 	if (p->Package.Count < 1) {
    375 		rv = AE_LIMIT;
    376 		goto out;
    377 	}
    378 
    379 	e = p->Package.Elements;
    380 
    381 	if (e->Type != ACPI_TYPE_INTEGER) {
    382 		rv = AE_TYPE;
    383 		goto out;
    384 	}
    385 
    386 	ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT,
    387 		"return status: %"PRIu64"\n", e[0].Integer.Value));
    388 
    389 	if (e[0].Integer.Value != 0) {
    390 		rv = AE_BAD_VALUE;
    391 		goto out;
    392 	}
    393 
    394 	/*
    395 	 * For read operations, copy data to user buffer.
    396 	 */
    397 	if (I2C_OP_READ_P(op)) {
    398 
    399 		if (p->Package.Count < 3) {
    400 			rv = AE_LIMIT;
    401 			goto out;
    402 		}
    403 
    404 		if (e[1].Type != ACPI_TYPE_INTEGER) {
    405 			rv = AE_TYPE;
    406 			goto out;
    407 		}
    408 
    409 		xlen = e[1].Integer.Value;
    410 
    411 		if (xlen > len)
    412 			xlen = len;
    413 
    414 		switch (e[2].Type) {
    415 
    416 		case ACPI_TYPE_BUFFER:
    417 
    418 			if (xlen == 0) {
    419 				rv = AE_LIMIT;
    420 				goto out;
    421 			}
    422 
    423 			xb = e[2].Buffer.Pointer;
    424 
    425 			if (xb == NULL) {
    426 				rv = AE_NULL_OBJECT;
    427 				goto out;
    428 			}
    429 
    430 			(void)memcpy(b, xb, xlen);
    431 			break;
    432 
    433 		case ACPI_TYPE_INTEGER:
    434 
    435 			if (xlen > 0)
    436 				*b++ = e[2].Integer.Value & 0xff;
    437 
    438 			if (xlen > 1)
    439 				*b = e[2].Integer.Value >> 8;
    440 
    441 			break;
    442 
    443 		default:
    444 			rv = AE_TYPE;
    445 			goto out;
    446 		}
    447 	}
    448 
    449 out:
    450 	if (smbuf.Pointer != NULL)
    451 		ACPI_FREE(smbuf.Pointer);
    452 
    453 	if (ACPI_SUCCESS(rv))
    454 		return 0;
    455 
    456 	ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, "failed to "
    457 		"evaluate %s: %s\n", path, AcpiFormatException(rv)));
    458 
    459 	return 1;
    460 }
    461 
    462 /*
    463  * Whether triggered by periodic polling or a Notify(),
    464  * retrieve all pending SMBus device alerts.
    465  */
    466 static void
    467 acpi_smbus_alerts(struct acpi_smbus_softc *sc)
    468 {
    469 	const ACPI_HANDLE hdl = sc->sc_devnode->ad_handle;
    470 	ACPI_OBJECT *e, *p;
    471 	ACPI_BUFFER alert;
    472 	ACPI_STATUS rv;
    473 	int status = 0;
    474 	uint8_t addr;
    475 
    476 	do {
    477 		rv = acpi_eval_struct(hdl, "_SBA", &alert);
    478 
    479 		if (ACPI_FAILURE(rv)) {
    480 			status = 1;
    481 			goto done;
    482 		}
    483 
    484 		p = alert.Pointer;
    485 
    486 		if (p->Type == ACPI_TYPE_PACKAGE && p->Package.Count >= 2) {
    487 
    488 			status = 1;
    489 
    490 			e = p->Package.Elements;
    491 
    492 			if (e[0].Type == ACPI_TYPE_INTEGER)
    493 				status = e[0].Integer.Value;
    494 
    495 			if (status == 0 && e[1].Type == ACPI_TYPE_INTEGER) {
    496 				addr = e[1].Integer.Value;
    497 
    498 				aprint_debug_dev(sc->sc_dv,
    499 				    "alert for 0x%x\n", addr);
    500 
    501 				(void)iic_smbus_intr(&sc->sc_i2c_tag);
    502 			}
    503 		}
    504 done:
    505 		if (alert.Pointer != NULL)
    506 			ACPI_FREE(alert.Pointer);
    507 
    508 	} while (status == 0);
    509 }
    510 
    511 static void
    512 acpi_smbus_tick(void *opaque)
    513 {
    514 	device_t dv = opaque;
    515 	struct acpi_smbus_softc *sc = device_private(dv);
    516 
    517 	acpi_smbus_alerts(sc);
    518 
    519 	callout_schedule(&sc->sc_callout, sc->sc_poll_alert * hz);
    520 }
    521 
    522 static void
    523 acpi_smbus_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque)
    524 {
    525 	device_t dv = opaque;
    526 	struct acpi_smbus_softc *sc = device_private(dv);
    527 
    528 	aprint_debug_dev(dv, "received notify message 0x%x\n", notify);
    529 
    530 	acpi_smbus_alerts(sc);
    531 }
    532