1 /* 2 * Copyright (c) 2016 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Imre Vadász <imre@vdsz.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 /* Register SMBUS device with ACPICA for ACPI-5.0 GPIO functionality */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/module.h> 41 #include <sys/errno.h> 42 #include <sys/lock.h> 43 #include <sys/bus.h> 44 45 #include "opt_acpi.h" 46 #include "acpi.h" 47 #include <dev/acpica/acpivar.h> 48 #include <contrib/dev/acpica/source/include/amlcode.h> 49 50 #include <bus/smbus/smbconf.h> 51 52 #include "smbus_if.h" 53 54 struct gsb_buffer { 55 UINT8 status; 56 UINT8 len; 57 UINT8 data[]; 58 } __packed; 59 60 struct acpi_i2c_handler_data { 61 struct acpi_connection_info info; 62 device_t dev; 63 }; 64 65 struct smbus_acpi_softc { 66 device_t dev; 67 device_t parent; 68 struct acpi_i2c_handler_data space_handler_data; 69 }; 70 71 static int smbus_acpi_probe(device_t dev); 72 static int smbus_acpi_attach(device_t dev); 73 static int smbus_acpi_detach(device_t dev); 74 75 /* SMBUS Address Space Handler */ 76 static void smbus_acpi_install_address_space_handler( 77 struct smbus_acpi_softc *sc); 78 static void smbus_acpi_remove_address_space_handler( 79 struct smbus_acpi_softc *sc); 80 static ACPI_STATUS smbus_acpi_space_handler(UINT32 Function, 81 ACPI_PHYSICAL_ADDRESS Address, UINT32 BitWidth, 82 UINT64 *Value, void *HandlerContext, 83 void *RegionContext); 84 85 /* 86 * SMBUS Address space handler 87 */ 88 89 static void 90 smbus_acpi_install_address_space_handler(struct smbus_acpi_softc *sc) 91 { 92 ACPI_HANDLE handle; 93 ACPI_STATUS s; 94 95 handle = acpi_get_handle(sc->parent); 96 sc->space_handler_data.dev = sc->parent; 97 s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS, 98 &smbus_acpi_space_handler, NULL, &sc->space_handler_data); 99 if (ACPI_FAILURE(s)) { 100 device_printf(sc->dev, 101 "Failed to install GSBUS Address Space Handler in ACPI\n"); 102 } 103 } 104 105 static void 106 smbus_acpi_remove_address_space_handler(struct smbus_acpi_softc *sc) 107 { 108 ACPI_HANDLE handle; 109 ACPI_STATUS s; 110 111 handle = acpi_get_handle(sc->parent); 112 s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GSBUS, 113 &smbus_acpi_space_handler); 114 if (ACPI_FAILURE(s)) { 115 device_printf(sc->dev, 116 "Failed to remove GSBUS Address Space Handler from ACPI\n"); 117 } 118 } 119 120 static ACPI_STATUS 121 smbus_acpi_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address, 122 UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext) 123 { 124 struct gsb_buffer *gsb = (struct gsb_buffer *)Value; 125 struct acpi_i2c_handler_data *data = HandlerContext; 126 device_t dev = data->dev; 127 struct acpi_connection_info *info = &data->info; 128 struct acpi_resource_i2c_serialbus *sb; 129 ACPI_RESOURCE *Resource; 130 UINT32 accessor_type = Function >> 16; 131 UINT8 action = Function & ACPI_IO_MASK; 132 ACPI_STATUS s = AE_OK; 133 int cnt, val = 0; 134 uint16_t *wdata; 135 short word; 136 char byte; 137 char buf[32]; 138 u_char count; 139 140 if (Value == NULL) 141 return (AE_BAD_PARAMETER); 142 143 s = AcpiBufferToResource(info->Connection, info->Length, 144 &Resource); 145 if (ACPI_FAILURE(s)) 146 return s; 147 if (Resource->Type != ACPI_RESOURCE_TYPE_SERIAL_BUS) { 148 s = AE_BAD_PARAMETER; 149 goto err; 150 } 151 152 sb = &Resource->Data.I2cSerialBus; 153 if (sb->Type != ACPI_RESOURCE_SERIAL_TYPE_I2C) { 154 s = AE_BAD_PARAMETER; 155 goto err; 156 } 157 158 /* XXX Ignore 10bit addressing for now */ 159 if (sb->AccessMode == ACPI_I2C_10BIT_MODE) { 160 s = AE_BAD_PARAMETER; 161 goto err; 162 } 163 164 switch (accessor_type) { 165 case AML_FIELD_ATTRIB_SEND_RECEIVE: 166 if (action == ACPI_READ) { 167 val = SMBUS_RECVB(dev, sb->SlaveAddress, &byte); 168 if (val == 0) 169 gsb->data[0] = byte; 170 } else { 171 val = SMBUS_SENDB(dev, sb->SlaveAddress, 172 gsb->data[0]); 173 } 174 break; 175 case AML_FIELD_ATTRIB_BYTE: 176 if (action == ACPI_READ) { 177 val = SMBUS_READB(dev, sb->SlaveAddress, Address, 178 &byte); 179 if (val == 0) 180 gsb->data[0] = byte; 181 } else { 182 val = SMBUS_WRITEB(dev, sb->SlaveAddress, Address, 183 gsb->data[0]); 184 } 185 break; 186 case AML_FIELD_ATTRIB_WORD: 187 wdata = (uint16_t *)gsb->data; 188 if (action == ACPI_READ) { 189 val = SMBUS_READW(dev, sb->SlaveAddress, Address, 190 &word); 191 if (val == 0) 192 wdata[0] = word; 193 } else { 194 val = SMBUS_WRITEW(dev, sb->SlaveAddress, Address, 195 wdata[0]); 196 } 197 break; 198 case AML_FIELD_ATTRIB_BLOCK: 199 if (action == ACPI_READ) { 200 count = 32; 201 val = SMBUS_BREAD(dev, sb->SlaveAddress, Address, 202 &count, buf); 203 if (val == 0) { 204 gsb->len = count; 205 memcpy(gsb->data, buf, count); 206 } 207 } else { 208 memcpy(buf, gsb->data, gsb->len); 209 count = gsb->len; 210 val = SMBUS_BWRITE(dev, sb->SlaveAddress, Address, 211 count, buf); 212 } 213 break; 214 case AML_FIELD_ATTRIB_BYTES: 215 if (action == ACPI_READ) { 216 cnt = 0; 217 val = SMBUS_TRANS(dev, sb->SlaveAddress, Address, 218 SMB_TRANS_NOCNT | SMB_TRANS_7BIT, NULL, 0, 219 buf, info->AccessLength, &cnt); 220 if (val == 0) 221 memcpy(gsb->data, buf, cnt); 222 } else { 223 memcpy(buf, gsb->data, info->AccessLength); 224 val = SMBUS_TRANS(dev, sb->SlaveAddress, Address, 225 SMB_TRANS_NOCNT | SMB_TRANS_7BIT, 226 buf, info->AccessLength, NULL, 0, NULL); 227 } 228 break; 229 default: 230 device_printf(dev, "protocol(0x%02x) is not supported.\n", 231 accessor_type); 232 s = AE_BAD_PARAMETER; 233 goto err; 234 } 235 236 gsb->status = val; 237 238 err: 239 ACPI_FREE(Resource); 240 241 return (s); 242 } 243 244 static int 245 smbus_acpi_probe(device_t dev) 246 { 247 if (acpi_get_handle(device_get_parent(dev)) == NULL) 248 return (ENXIO); 249 250 device_set_desc(dev, "ACPI I2cSerialBus backend"); 251 252 return (0); 253 } 254 255 static int 256 smbus_acpi_attach(device_t dev) 257 { 258 struct smbus_acpi_softc *sc = device_get_softc(dev); 259 260 sc->dev = dev; 261 sc->parent = device_get_parent(dev); 262 263 smbus_acpi_install_address_space_handler(sc); 264 265 return (0); 266 } 267 268 static int 269 smbus_acpi_detach(device_t dev) 270 { 271 struct smbus_acpi_softc *sc = device_get_softc(dev); 272 273 smbus_acpi_remove_address_space_handler(sc); 274 275 return (0); 276 } 277 278 static device_method_t smbacpi_methods[] = { 279 /* Device interface */ 280 DEVMETHOD(device_probe, smbus_acpi_probe), 281 DEVMETHOD(device_attach, smbus_acpi_attach), 282 DEVMETHOD(device_detach, smbus_acpi_detach), 283 284 DEVMETHOD_END 285 }; 286 287 static driver_t smbacpi_driver = { 288 "smbacpi", 289 smbacpi_methods, 290 sizeof(struct smbus_acpi_softc) 291 }; 292 293 static devclass_t smbacpi_devclass; 294 295 DRIVER_MODULE(smbacpi, ig4iic, smbacpi_driver, smbacpi_devclass, 296 NULL, NULL); 297 MODULE_DEPEND(smbacpi, acpi, 1, 1, 1); 298 MODULE_DEPEND(smbacpi, smbus, 1, 1, 1); 299 MODULE_VERSION(smbacpi, 1); 300