xref: /dflybsd-src/sys/bus/smbus/smbacpi/smbacpi.c (revision 806343b9970349b20504d6a3a0ad77008c380d00)
198eefd6fSImre Vadász /*
298eefd6fSImre Vadász  * Copyright (c) 2016 The DragonFly Project.  All rights reserved.
398eefd6fSImre Vadász  *
498eefd6fSImre Vadász  * This code is derived from software contributed to The DragonFly Project
598eefd6fSImre Vadász  * by Imre Vadász <imre@vdsz.com>
698eefd6fSImre Vadász  *
798eefd6fSImre Vadász  * Redistribution and use in source and binary forms, with or without
898eefd6fSImre Vadász  * modification, are permitted provided that the following conditions
998eefd6fSImre Vadász  * are met:
1098eefd6fSImre Vadász  *
1198eefd6fSImre Vadász  * 1. Redistributions of source code must retain the above copyright
1298eefd6fSImre Vadász  *    notice, this list of conditions and the following disclaimer.
1398eefd6fSImre Vadász  * 2. Redistributions in binary form must reproduce the above copyright
1498eefd6fSImre Vadász  *    notice, this list of conditions and the following disclaimer in
1598eefd6fSImre Vadász  *    the documentation and/or other materials provided with the
1698eefd6fSImre Vadász  *    distribution.
1798eefd6fSImre Vadász  * 3. Neither the name of The DragonFly Project nor the names of its
1898eefd6fSImre Vadász  *    contributors may be used to endorse or promote products derived
1998eefd6fSImre Vadász  *    from this software without specific, prior written permission.
2098eefd6fSImre Vadász  *
2198eefd6fSImre Vadász  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2298eefd6fSImre Vadász  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2398eefd6fSImre Vadász  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2498eefd6fSImre Vadász  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
2598eefd6fSImre Vadász  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2698eefd6fSImre Vadász  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2798eefd6fSImre Vadász  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2898eefd6fSImre Vadász  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2998eefd6fSImre Vadász  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3098eefd6fSImre Vadász  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3198eefd6fSImre Vadász  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3298eefd6fSImre Vadász  * SUCH DAMAGE.
3398eefd6fSImre Vadász  */
3498eefd6fSImre Vadász 
3598eefd6fSImre Vadász /* Register SMBUS device with ACPICA for ACPI-5.0 GPIO functionality */
3698eefd6fSImre Vadász 
3798eefd6fSImre Vadász #include <sys/param.h>
3898eefd6fSImre Vadász #include <sys/systm.h>
3998eefd6fSImre Vadász #include <sys/kernel.h>
4098eefd6fSImre Vadász #include <sys/module.h>
4198eefd6fSImre Vadász #include <sys/errno.h>
4298eefd6fSImre Vadász #include <sys/lock.h>
4398eefd6fSImre Vadász #include <sys/bus.h>
4498eefd6fSImre Vadász 
4598eefd6fSImre Vadász #include "opt_acpi.h"
4698eefd6fSImre Vadász #include "acpi.h"
4798eefd6fSImre Vadász #include <dev/acpica/acpivar.h>
4898eefd6fSImre Vadász #include <contrib/dev/acpica/source/include/amlcode.h>
4998eefd6fSImre Vadász 
5098eefd6fSImre Vadász #include <bus/smbus/smbconf.h>
5198eefd6fSImre Vadász 
5298eefd6fSImre Vadász #include "smbus_if.h"
5398eefd6fSImre Vadász 
5498eefd6fSImre Vadász struct gsb_buffer {
5598eefd6fSImre Vadász 	UINT8 status;
5698eefd6fSImre Vadász 	UINT8 len;
5798eefd6fSImre Vadász 	UINT8 data[];
5898eefd6fSImre Vadász } __packed;
5998eefd6fSImre Vadász 
6098eefd6fSImre Vadász struct acpi_i2c_handler_data {
6198eefd6fSImre Vadász 	struct acpi_connection_info info;
6298eefd6fSImre Vadász 	device_t dev;
6398eefd6fSImre Vadász };
6498eefd6fSImre Vadász 
6598eefd6fSImre Vadász struct smbus_acpi_softc {
6698eefd6fSImre Vadász 	device_t dev;
6798eefd6fSImre Vadász 	device_t parent;
6898eefd6fSImre Vadász 	struct acpi_i2c_handler_data space_handler_data;
6998eefd6fSImre Vadász };
7098eefd6fSImre Vadász 
7198eefd6fSImre Vadász static int	smbus_acpi_probe(device_t dev);
7298eefd6fSImre Vadász static int	smbus_acpi_attach(device_t dev);
7398eefd6fSImre Vadász static int	smbus_acpi_detach(device_t dev);
7498eefd6fSImre Vadász 
7598eefd6fSImre Vadász /* SMBUS Address Space Handler */
7698eefd6fSImre Vadász static void		smbus_acpi_install_address_space_handler(
7798eefd6fSImre Vadász 			    struct smbus_acpi_softc *sc);
7898eefd6fSImre Vadász static void		smbus_acpi_remove_address_space_handler(
7998eefd6fSImre Vadász 			    struct smbus_acpi_softc *sc);
8098eefd6fSImre Vadász static ACPI_STATUS	smbus_acpi_space_handler(UINT32 Function,
8198eefd6fSImre Vadász 			    ACPI_PHYSICAL_ADDRESS Address, UINT32 BitWidth,
8298eefd6fSImre Vadász 			    UINT64 *Value, void *HandlerContext,
8398eefd6fSImre Vadász 			    void *RegionContext);
8498eefd6fSImre Vadász 
8598eefd6fSImre Vadász /*
8698eefd6fSImre Vadász  * SMBUS Address space handler
8798eefd6fSImre Vadász  */
8898eefd6fSImre Vadász 
8998eefd6fSImre Vadász static void
smbus_acpi_install_address_space_handler(struct smbus_acpi_softc * sc)9098eefd6fSImre Vadász smbus_acpi_install_address_space_handler(struct smbus_acpi_softc *sc)
9198eefd6fSImre Vadász {
9298eefd6fSImre Vadász 	ACPI_HANDLE handle;
9398eefd6fSImre Vadász 	ACPI_STATUS s;
9498eefd6fSImre Vadász 
9598eefd6fSImre Vadász 	handle = acpi_get_handle(sc->parent);
9698eefd6fSImre Vadász 	sc->space_handler_data.dev = sc->parent;
9798eefd6fSImre Vadász 	s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS,
9898eefd6fSImre Vadász 	    &smbus_acpi_space_handler, NULL, &sc->space_handler_data);
9998eefd6fSImre Vadász 	if (ACPI_FAILURE(s)) {
10098eefd6fSImre Vadász 		device_printf(sc->dev,
10198eefd6fSImre Vadász 		    "Failed to install GSBUS Address Space Handler in ACPI\n");
10298eefd6fSImre Vadász 	}
10398eefd6fSImre Vadász }
10498eefd6fSImre Vadász 
10598eefd6fSImre Vadász static void
smbus_acpi_remove_address_space_handler(struct smbus_acpi_softc * sc)10698eefd6fSImre Vadász smbus_acpi_remove_address_space_handler(struct smbus_acpi_softc *sc)
10798eefd6fSImre Vadász {
10898eefd6fSImre Vadász 	ACPI_HANDLE handle;
10998eefd6fSImre Vadász 	ACPI_STATUS s;
11098eefd6fSImre Vadász 
11198eefd6fSImre Vadász 	handle = acpi_get_handle(sc->parent);
11298eefd6fSImre Vadász 	s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS,
11398eefd6fSImre Vadász 	    &smbus_acpi_space_handler);
11498eefd6fSImre Vadász 	if (ACPI_FAILURE(s)) {
11598eefd6fSImre Vadász 		device_printf(sc->dev,
11698eefd6fSImre Vadász 		    "Failed to remove GSBUS Address Space Handler from ACPI\n");
11798eefd6fSImre Vadász 	}
11898eefd6fSImre Vadász }
11998eefd6fSImre Vadász 
12098eefd6fSImre Vadász static ACPI_STATUS
smbus_acpi_space_handler(UINT32 Function,ACPI_PHYSICAL_ADDRESS Address,UINT32 BitWidth,UINT64 * Value,void * HandlerContext,void * RegionContext)12198eefd6fSImre Vadász smbus_acpi_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address,
12298eefd6fSImre Vadász     UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext)
12398eefd6fSImre Vadász {
12498eefd6fSImre Vadász 	struct gsb_buffer *gsb = (struct gsb_buffer *)Value;
12598eefd6fSImre Vadász 	struct acpi_i2c_handler_data *data = HandlerContext;
12698eefd6fSImre Vadász 	device_t dev = data->dev;
12798eefd6fSImre Vadász 	struct acpi_connection_info *info = &data->info;
12898eefd6fSImre Vadász 	struct acpi_resource_i2c_serialbus *sb;
12998eefd6fSImre Vadász 	ACPI_RESOURCE *Resource;
13098eefd6fSImre Vadász 	UINT32 accessor_type = Function >> 16;
13198eefd6fSImre Vadász 	UINT8 action = Function & ACPI_IO_MASK;
13298eefd6fSImre Vadász 	ACPI_STATUS s = AE_OK;
13398eefd6fSImre Vadász 	int cnt, val = 0;
13498eefd6fSImre Vadász 	uint16_t *wdata;
13598eefd6fSImre Vadász 	short word;
13698eefd6fSImre Vadász 	char byte;
13798eefd6fSImre Vadász 	char buf[32];
13898eefd6fSImre Vadász 	u_char count;
13998eefd6fSImre Vadász 
14098eefd6fSImre Vadász 	if (Value == NULL)
14198eefd6fSImre Vadász 		return (AE_BAD_PARAMETER);
14298eefd6fSImre Vadász 
14398eefd6fSImre Vadász 	s = AcpiBufferToResource(info->Connection, info->Length,
14498eefd6fSImre Vadász 	    &Resource);
14598eefd6fSImre Vadász 	if (ACPI_FAILURE(s))
14698eefd6fSImre Vadász 		return s;
14798eefd6fSImre Vadász 	if (Resource->Type != ACPI_RESOURCE_TYPE_SERIAL_BUS) {
14898eefd6fSImre Vadász 		s = AE_BAD_PARAMETER;
14998eefd6fSImre Vadász 		goto err;
15098eefd6fSImre Vadász 	}
15198eefd6fSImre Vadász 
15298eefd6fSImre Vadász 	sb = &Resource->Data.I2cSerialBus;
15398eefd6fSImre Vadász 	if (sb->Type != ACPI_RESOURCE_SERIAL_TYPE_I2C) {
15498eefd6fSImre Vadász 		s = AE_BAD_PARAMETER;
15598eefd6fSImre Vadász 		goto err;
15698eefd6fSImre Vadász 	}
15798eefd6fSImre Vadász 
15898eefd6fSImre Vadász 	/* XXX Ignore 10bit addressing for now */
15998eefd6fSImre Vadász 	if (sb->AccessMode == ACPI_I2C_10BIT_MODE) {
16098eefd6fSImre Vadász 		s = AE_BAD_PARAMETER;
16198eefd6fSImre Vadász 		goto err;
16298eefd6fSImre Vadász 	}
16398eefd6fSImre Vadász 
16498eefd6fSImre Vadász 	switch (accessor_type) {
165*806343b9SSascha Wildner 	case AML_FIELD_ATTRIB_SEND_RECEIVE:
16698eefd6fSImre Vadász 		if (action == ACPI_READ) {
16798eefd6fSImre Vadász 			val = SMBUS_RECVB(dev, sb->SlaveAddress, &byte);
16898eefd6fSImre Vadász 			if (val == 0)
16998eefd6fSImre Vadász 				gsb->data[0] = byte;
17098eefd6fSImre Vadász 		} else {
17198eefd6fSImre Vadász 			val = SMBUS_SENDB(dev, sb->SlaveAddress,
17298eefd6fSImre Vadász 			    gsb->data[0]);
17398eefd6fSImre Vadász 		}
17498eefd6fSImre Vadász 		break;
17598eefd6fSImre Vadász 	case AML_FIELD_ATTRIB_BYTE:
17698eefd6fSImre Vadász 		if (action == ACPI_READ) {
17798eefd6fSImre Vadász 			val = SMBUS_READB(dev, sb->SlaveAddress, Address,
17898eefd6fSImre Vadász 			    &byte);
17998eefd6fSImre Vadász 			if (val == 0)
18098eefd6fSImre Vadász 				gsb->data[0] = byte;
18198eefd6fSImre Vadász 		} else {
18298eefd6fSImre Vadász 			val = SMBUS_WRITEB(dev, sb->SlaveAddress, Address,
18398eefd6fSImre Vadász 			    gsb->data[0]);
18498eefd6fSImre Vadász 		}
18598eefd6fSImre Vadász 		break;
18698eefd6fSImre Vadász 	case AML_FIELD_ATTRIB_WORD:
18798eefd6fSImre Vadász 		wdata = (uint16_t *)gsb->data;
18898eefd6fSImre Vadász 		if (action == ACPI_READ) {
18998eefd6fSImre Vadász 			val = SMBUS_READW(dev, sb->SlaveAddress, Address,
19098eefd6fSImre Vadász 			    &word);
19198eefd6fSImre Vadász 			if (val == 0)
19298eefd6fSImre Vadász 				wdata[0] = word;
19398eefd6fSImre Vadász 		} else {
19498eefd6fSImre Vadász 			val = SMBUS_WRITEW(dev, sb->SlaveAddress, Address,
19598eefd6fSImre Vadász 			    wdata[0]);
19698eefd6fSImre Vadász 		}
19798eefd6fSImre Vadász 		break;
19898eefd6fSImre Vadász 	case AML_FIELD_ATTRIB_BLOCK:
19998eefd6fSImre Vadász 		if (action == ACPI_READ) {
20098eefd6fSImre Vadász 			count = 32;
20198eefd6fSImre Vadász 			val = SMBUS_BREAD(dev, sb->SlaveAddress, Address,
20298eefd6fSImre Vadász 			    &count, buf);
20398eefd6fSImre Vadász 			if (val == 0) {
20498eefd6fSImre Vadász 				gsb->len = count;
20598eefd6fSImre Vadász 				memcpy(gsb->data, buf, count);
20698eefd6fSImre Vadász 			}
20798eefd6fSImre Vadász 		} else {
20898eefd6fSImre Vadász 			memcpy(buf, gsb->data, gsb->len);
20998eefd6fSImre Vadász 			count = gsb->len;
21098eefd6fSImre Vadász 			val = SMBUS_BWRITE(dev, sb->SlaveAddress, Address,
21198eefd6fSImre Vadász 			    count, buf);
21298eefd6fSImre Vadász 		}
21398eefd6fSImre Vadász 		break;
214*806343b9SSascha Wildner 	case AML_FIELD_ATTRIB_BYTES:
21598eefd6fSImre Vadász 		if (action == ACPI_READ) {
21698eefd6fSImre Vadász 			cnt = 0;
21798eefd6fSImre Vadász 			val = SMBUS_TRANS(dev, sb->SlaveAddress, Address,
21898eefd6fSImre Vadász 			    SMB_TRANS_NOCNT | SMB_TRANS_7BIT, NULL, 0,
21998eefd6fSImre Vadász 			    buf, info->AccessLength, &cnt);
22098eefd6fSImre Vadász 			if (val == 0)
22198eefd6fSImre Vadász 				memcpy(gsb->data, buf, cnt);
22298eefd6fSImre Vadász 		} else {
22398eefd6fSImre Vadász 			memcpy(buf, gsb->data, info->AccessLength);
22498eefd6fSImre Vadász 			val = SMBUS_TRANS(dev, sb->SlaveAddress, Address,
22598eefd6fSImre Vadász 			    SMB_TRANS_NOCNT | SMB_TRANS_7BIT,
22698eefd6fSImre Vadász 			    buf, info->AccessLength, NULL, 0, NULL);
22798eefd6fSImre Vadász 		}
22898eefd6fSImre Vadász 		break;
22998eefd6fSImre Vadász 	default:
23098eefd6fSImre Vadász 		device_printf(dev, "protocol(0x%02x) is not supported.\n",
23198eefd6fSImre Vadász 		    accessor_type);
23298eefd6fSImre Vadász 		s = AE_BAD_PARAMETER;
23398eefd6fSImre Vadász 		goto err;
23498eefd6fSImre Vadász 	}
23598eefd6fSImre Vadász 
23698eefd6fSImre Vadász 	gsb->status = val;
23798eefd6fSImre Vadász 
23898eefd6fSImre Vadász err:
23998eefd6fSImre Vadász 	ACPI_FREE(Resource);
24098eefd6fSImre Vadász 
24198eefd6fSImre Vadász 	return (s);
24298eefd6fSImre Vadász }
24398eefd6fSImre Vadász 
24498eefd6fSImre Vadász static int
smbus_acpi_probe(device_t dev)24598eefd6fSImre Vadász smbus_acpi_probe(device_t dev)
24698eefd6fSImre Vadász {
24798eefd6fSImre Vadász 	if (acpi_get_handle(device_get_parent(dev)) == NULL)
24898eefd6fSImre Vadász 		return (ENXIO);
24998eefd6fSImre Vadász 
25098eefd6fSImre Vadász 	device_set_desc(dev, "ACPI I2cSerialBus backend");
25198eefd6fSImre Vadász 
25298eefd6fSImre Vadász 	return (0);
25398eefd6fSImre Vadász }
25498eefd6fSImre Vadász 
25598eefd6fSImre Vadász static int
smbus_acpi_attach(device_t dev)25698eefd6fSImre Vadász smbus_acpi_attach(device_t dev)
25798eefd6fSImre Vadász {
25898eefd6fSImre Vadász 	struct smbus_acpi_softc *sc = device_get_softc(dev);
25998eefd6fSImre Vadász 
26098eefd6fSImre Vadász 	sc->dev = dev;
26198eefd6fSImre Vadász 	sc->parent = device_get_parent(dev);
26298eefd6fSImre Vadász 
26398eefd6fSImre Vadász 	smbus_acpi_install_address_space_handler(sc);
26498eefd6fSImre Vadász 
26598eefd6fSImre Vadász 	return (0);
26698eefd6fSImre Vadász }
26798eefd6fSImre Vadász 
26898eefd6fSImre Vadász static int
smbus_acpi_detach(device_t dev)26998eefd6fSImre Vadász smbus_acpi_detach(device_t dev)
27098eefd6fSImre Vadász {
27198eefd6fSImre Vadász 	struct smbus_acpi_softc *sc = device_get_softc(dev);
27298eefd6fSImre Vadász 
27398eefd6fSImre Vadász 	smbus_acpi_remove_address_space_handler(sc);
27498eefd6fSImre Vadász 
27598eefd6fSImre Vadász 	return (0);
27698eefd6fSImre Vadász }
27798eefd6fSImre Vadász 
27898eefd6fSImre Vadász static device_method_t smbacpi_methods[] = {
27998eefd6fSImre Vadász 	/* Device interface */
28098eefd6fSImre Vadász 	DEVMETHOD(device_probe, smbus_acpi_probe),
28198eefd6fSImre Vadász 	DEVMETHOD(device_attach, smbus_acpi_attach),
28298eefd6fSImre Vadász 	DEVMETHOD(device_detach, smbus_acpi_detach),
28398eefd6fSImre Vadász 
28498eefd6fSImre Vadász 	DEVMETHOD_END
28598eefd6fSImre Vadász };
28698eefd6fSImre Vadász 
28798eefd6fSImre Vadász static driver_t smbacpi_driver = {
28898eefd6fSImre Vadász 	"smbacpi",
28998eefd6fSImre Vadász 	smbacpi_methods,
29098eefd6fSImre Vadász 	sizeof(struct smbus_acpi_softc)
29198eefd6fSImre Vadász };
29298eefd6fSImre Vadász 
29398eefd6fSImre Vadász static devclass_t smbacpi_devclass;
29498eefd6fSImre Vadász 
29598eefd6fSImre Vadász DRIVER_MODULE(smbacpi, ig4iic, smbacpi_driver, smbacpi_devclass,
29698eefd6fSImre Vadász     NULL, NULL);
29798eefd6fSImre Vadász MODULE_DEPEND(smbacpi, acpi, 1, 1, 1);
29898eefd6fSImre Vadász MODULE_DEPEND(smbacpi, smbus, 1, 1, 1);
29998eefd6fSImre Vadász MODULE_VERSION(smbacpi, 1);
300