1/*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023 Vladimir Kondratyev <wulf@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/param.h> 29#include <sys/bus.h> 30#include <sys/endian.h> 31#include <sys/kernel.h> 32#include <sys/malloc.h> 33#include <sys/module.h> 34#include <sys/rman.h> 35#include <sys/sbuf.h> 36#include <sys/systm.h> 37 38#include <machine/resource.h> 39 40#include <contrib/dev/acpica/include/acpi.h> 41#include <contrib/dev/acpica/include/accommon.h> 42#include <contrib/dev/acpica/include/amlcode.h> 43#include <dev/acpica/acpivar.h> 44 45#include <dev/spibus/spibusvar.h> 46 47/* 48 * Make a copy of ACPI_RESOURCE_SPI_SERIALBUS type and replace "pointer to ACPI 49 * object name string" field with pointer to ACPI object itself. 50 * This saves us extra strdup()/free() pair on acpi_spibus_get_acpi_res call. 51 */ 52typedef ACPI_RESOURCE_SPI_SERIALBUS ACPI_SPIBUS_RESOURCE_SPI_SERIALBUS; 53#define ResourceSource_Handle ResourceSource.StringPtr 54 55/* Hooks for the ACPI CA debugging infrastructure. */ 56#define _COMPONENT ACPI_BUS 57ACPI_MODULE_NAME("SPI") 58 59#if defined (__amd64__) || defined (__i386__) 60static bool is_apple; 61#endif 62 63struct acpi_spibus_ivar { 64 struct spibus_ivar super_ivar; 65 ACPI_HANDLE handle; 66}; 67 68static inline bool 69acpi_resource_is_spi_serialbus(ACPI_RESOURCE *res) 70{ 71 return (res->Type == ACPI_RESOURCE_TYPE_SERIAL_BUS && 72 res->Data.CommonSerialBus.Type == ACPI_RESOURCE_SERIAL_TYPE_SPI); 73} 74 75static ACPI_STATUS 76acpi_spibus_get_acpi_res_cb(ACPI_RESOURCE *res, void *context) 77{ 78 ACPI_SPIBUS_RESOURCE_SPI_SERIALBUS *sb = context; 79 ACPI_STATUS status; 80 ACPI_HANDLE handle; 81 82 if (acpi_resource_is_spi_serialbus(res)) { 83 status = AcpiGetHandle(ACPI_ROOT_OBJECT, 84 res->Data.SpiSerialBus.ResourceSource.StringPtr, &handle); 85 if (ACPI_FAILURE(status)) 86 return (status); 87 memcpy(sb, &res->Data.SpiSerialBus, 88 sizeof(ACPI_SPIBUS_RESOURCE_SPI_SERIALBUS)); 89 /* 90 * replace "pointer to ACPI object name string" field 91 * with pointer to ACPI object itself. 92 */ 93 sb->ResourceSource_Handle = handle; 94 return (AE_CTRL_TERMINATE); 95 } else if (res->Type == ACPI_RESOURCE_TYPE_END_TAG) 96 return (AE_NOT_FOUND); 97 98 return (AE_OK); 99} 100 101static void 102acpi_spibus_dump_res(device_t dev, ACPI_SPIBUS_RESOURCE_SPI_SERIALBUS *sb) 103{ 104 device_printf(dev, "found ACPI child\n"); 105 printf(" DeviceSelection: 0x%04hx\n", sb->DeviceSelection); 106 printf(" ConnectionSpeed: %uHz\n", sb->ConnectionSpeed); 107 printf(" WireMode: %s\n", 108 sb->WireMode == ACPI_SPI_4WIRE_MODE ? 109 "FourWireMode" : "ThreeWireMode"); 110 printf(" DevicePolarity: %s\n", 111 sb->DevicePolarity == ACPI_SPI_ACTIVE_LOW ? 112 "PolarityLow" : "PolarityHigh"); 113 printf(" DataBitLength: %uBit\n", sb->DataBitLength); 114 printf(" ClockPhase: %s\n", 115 sb->ClockPhase == ACPI_SPI_FIRST_PHASE ? 116 "ClockPhaseFirst" : "ClockPhaseSecond"); 117 printf(" ClockPolarity: %s\n", 118 sb->ClockPolarity == ACPI_SPI_START_LOW ? 119 "ClockPolarityLow" : "ClockPolarityHigh"); 120 printf(" SlaveMode: %s\n", 121 sb->SlaveMode == ACPI_CONTROLLER_INITIATED ? 122 "ControllerInitiated" : "DeviceInitiated"); 123 printf(" ConnectionSharing: %s\n", sb->ConnectionSharing == 0 ? 124 "Exclusive" : "Shared"); 125} 126 127static int 128acpi_spibus_get_acpi_res(device_t spibus, ACPI_HANDLE dev, 129 struct spibus_ivar *res) 130{ 131 ACPI_SPIBUS_RESOURCE_SPI_SERIALBUS sb; 132 133 /* 134 * Read "SPI Serial Bus Connection Resource Descriptor" 135 * described in p.19.6.126 of ACPI specification. 136 */ 137 bzero(&sb, sizeof(ACPI_SPIBUS_RESOURCE_SPI_SERIALBUS)); 138 if (ACPI_FAILURE(AcpiWalkResources(dev, "_CRS", 139 acpi_spibus_get_acpi_res_cb, &sb))) 140 return (ENXIO); 141 if (sb.ResourceSource_Handle != 142 acpi_get_handle(device_get_parent(spibus))) 143 return (ENXIO); 144 if (bootverbose) 145 acpi_spibus_dump_res(spibus, &sb); 146 /* 147 * The Windows Baytrail and Braswell SPI host controller 148 * drivers uses 1 as the first (and only) value for ACPI 149 * DeviceSelection. 150 */ 151 if (sb.DeviceSelection != 0 && 152 (acpi_MatchHid(sb.ResourceSource_Handle, "80860F0E") || 153 acpi_MatchHid(sb.ResourceSource_Handle, "8086228E"))) 154 res->cs = sb.DeviceSelection - 1; 155 else 156 res->cs = sb.DeviceSelection; 157 res->mode = 158 (sb.ClockPhase != ACPI_SPI_FIRST_PHASE ? SPIBUS_MODE_CPHA : 0) | 159 (sb.ClockPolarity != ACPI_SPI_START_LOW ? SPIBUS_MODE_CPOL : 0); 160 res->clock = sb.ConnectionSpeed; 161 162 return (0); 163} 164 165#if defined (__amd64__) || defined (__i386__) 166static int 167acpi_spibus_get_apple_res(device_t spibus, ACPI_HANDLE dev, 168 struct spibus_ivar *ivar) 169{ 170 /* a0b5b7c6-1318-441c-b0c9-fe695eaf949b */ 171 static const uint8_t apple_guid[ACPI_UUID_LENGTH] = { 172 0xC6, 0xB7, 0xB5, 0xA0, 0x18, 0x13, 0x1C, 0x44, 173 0xB0, 0xC9, 0xFE, 0x69, 0x5E, 0xAF, 0x94, 0x9B, 174 }; 175 ACPI_BUFFER buf; 176 ACPI_OBJECT *pkg, *comp; 177 ACPI_HANDLE parent; 178 char *k; 179 uint64_t val; 180 181 /* Apple does not use _CRS but nested devices for SPI slaves */ 182 if (ACPI_FAILURE(AcpiGetParent(dev, &parent))) 183 return (ENXIO); 184 if (parent != acpi_get_handle(device_get_parent(spibus))) 185 return (ENXIO); 186 if (ACPI_FAILURE(acpi_EvaluateDSMTyped(dev, apple_guid, 187 1, 1, NULL, &buf, ACPI_TYPE_PACKAGE))) 188 return (ENXIO); 189 190 pkg = ((ACPI_OBJECT *)buf.Pointer); 191 if (pkg->Package.Count % 2 != 0) { 192 device_printf(spibus, "_DSM length %d not even\n", 193 pkg->Package.Count); 194 AcpiOsFree(pkg); 195 return (ENXIO); 196 } 197 198 if (bootverbose) 199 device_printf(spibus, "found ACPI child\n"); 200 201 for (comp = pkg->Package.Elements; 202 comp < pkg->Package.Elements + pkg->Package.Count; 203 comp += 2) { 204 205 if (comp[0].Type != ACPI_TYPE_STRING || 206 comp[1].Type != ACPI_TYPE_BUFFER) { 207 device_printf(spibus, "expected string+buffer, " 208 "got %d+%d\n", comp[0].Type, comp[1].Type); 209 continue; 210 } 211 k = comp[0].String.Pointer; 212 val = comp[1].Buffer.Length >= 8 ? 213 *(uint64_t *)comp[1].Buffer.Pointer : 0; 214 215 if (bootverbose) 216 printf(" %s: %ju\n", k, (intmax_t)val); 217 218 if (strcmp(k, "spiSclkPeriod") == 0) { 219 if (val != 0) 220 ivar->clock = 1000000000 / val; 221 } else if (strcmp(k, "spiSPO") == 0) { 222 if (val != 0) 223 ivar->mode |= SPIBUS_MODE_CPOL; 224 } else if (strcmp(k, "spiSPH") == 0) { 225 if (val != 0) 226 ivar->mode |= SPIBUS_MODE_CPHA; 227 } else if (strcmp(k, "spiCSDelay") == 0) { 228 ivar->cs_delay = val; 229 } 230 } 231 232 AcpiOsFree(pkg); 233 234 return (0); 235} 236#endif 237 238static int 239acpi_spibus_delete_acpi_child(ACPI_HANDLE handle) 240{ 241 device_t acpi_child, acpi0; 242 243 /* Delete existing child of acpi bus */ 244 acpi_child = acpi_get_device(handle); 245 if (acpi_child != NULL) { 246 acpi0 = devclass_get_device(devclass_find("acpi"), 0); 247 if (device_get_parent(acpi_child) != acpi0) 248 return (ENXIO); 249 250 if (device_is_attached(acpi_child)) 251 return (ENXIO); 252 253 if (device_delete_child(acpi0, acpi_child) != 0) 254 return (ENXIO); 255 } 256 257 return (0); 258} 259 260static device_t 261acpi_spibus_add_child(device_t dev, u_int order, const char *name, int unit) 262{ 263 return (spibus_add_child_common( 264 dev, order, name, unit, sizeof(struct acpi_spibus_ivar))); 265} 266 267static ACPI_STATUS 268acpi_spibus_enumerate_child(ACPI_HANDLE handle, UINT32 level, 269 void *context, void **result) 270{ 271 device_t spibus, child; 272 struct spibus_ivar res; 273 ACPI_STATUS status; 274 UINT32 sta; 275 bool found = false; 276 277 spibus = context; 278 279 /* 280 * If no _STA method or if it failed, then assume that 281 * the device is present. 282 */ 283 if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) && 284 !ACPI_DEVICE_PRESENT(sta)) 285 return (AE_OK); 286 287 if (!acpi_has_hid(handle)) 288 return (AE_OK); 289 290 bzero(&res, sizeof(res)); 291 if (acpi_spibus_get_acpi_res(spibus, handle, &res) == 0) 292 found = true; 293#if defined (__amd64__) || defined (__i386__) 294 if (!found && is_apple && 295 acpi_spibus_get_apple_res(spibus, handle, &res) == 0) 296 found = true; 297#endif 298 if (!found || res.clock == 0) 299 return (AE_OK); 300 301 /* Delete existing child of acpi bus */ 302 if (acpi_spibus_delete_acpi_child(handle) != 0) 303 return (AE_OK); 304 305 child = BUS_ADD_CHILD(spibus, 0, NULL, -1); 306 if (child == NULL) { 307 device_printf(spibus, "add child failed\n"); 308 return (AE_OK); 309 } 310 311 spibus_set_cs(child, res.cs); 312 spibus_set_mode(child, res.mode); 313 spibus_set_clock(child, res.clock); 314 spibus_set_cs_delay(child, res.cs_delay); 315 acpi_set_handle(child, handle); 316 acpi_parse_resources(child, handle, &acpi_res_parse_set, NULL); 317 318 /* 319 * Update ACPI-CA to use the IIC enumerated device_t for this handle. 320 */ 321 status = AcpiAttachData(handle, acpi_fake_objhandler, child); 322 if (ACPI_FAILURE(status)) 323 printf("WARNING: Unable to attach object data to %s - %s\n", 324 acpi_name(handle), AcpiFormatException(status)); 325 326 return (AE_OK); 327} 328 329static ACPI_STATUS 330acpi_spibus_enumerate_children(device_t dev) 331{ 332 return (AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 333 ACPI_UINT32_MAX, acpi_spibus_enumerate_child, NULL, dev, NULL)); 334} 335 336static void 337acpi_spibus_set_power_children(device_t dev, int state, bool all_children) 338{ 339 device_t *devlist; 340 int i, numdevs; 341 342 if (device_get_children(dev, &devlist, &numdevs) != 0) 343 return; 344 345 for (i = 0; i < numdevs; i++) 346 if (all_children || device_is_attached(devlist[i]) != 0) 347 acpi_set_powerstate(devlist[i], state); 348 349 free(devlist, M_TEMP); 350} 351 352static int 353acpi_spibus_probe(device_t dev) 354{ 355 ACPI_HANDLE handle; 356 device_t controller; 357 358 if (acpi_disabled("spibus")) 359 return (ENXIO); 360 361 controller = device_get_parent(dev); 362 if (controller == NULL) 363 return (ENXIO); 364 365 handle = acpi_get_handle(controller); 366 if (handle == NULL) 367 return (ENXIO); 368 369 device_set_desc(dev, "SPI bus (ACPI-hinted)"); 370 return (BUS_PROBE_DEFAULT + 1); 371} 372 373static int 374acpi_spibus_attach(device_t dev) 375{ 376 377#if defined (__amd64__) || defined (__i386__) 378 char *vendor = kern_getenv("smbios.bios.vendor"); 379 if (vendor != NULL && 380 (strcmp(vendor, "Apple Inc.") == 0 || 381 strcmp(vendor, "Apple Computer, Inc.") == 0)) 382 is_apple = true; 383#endif 384 385 if (ACPI_FAILURE(acpi_spibus_enumerate_children(dev))) 386 device_printf(dev, "children enumeration failed\n"); 387 388 acpi_spibus_set_power_children(dev, ACPI_STATE_D0, true); 389 return (spibus_attach(dev)); 390} 391 392static int 393acpi_spibus_detach(device_t dev) 394{ 395 acpi_spibus_set_power_children(dev, ACPI_STATE_D3, false); 396 397 return (spibus_detach(dev)); 398} 399 400static int 401acpi_spibus_suspend(device_t dev) 402{ 403 acpi_spibus_set_power_children(dev, ACPI_STATE_D3, false); 404 405 return (bus_generic_suspend(dev)); 406} 407 408static int 409acpi_spibus_resume(device_t dev) 410{ 411 acpi_spibus_set_power_children(dev, ACPI_STATE_D0, false); 412 413 return (bus_generic_resume(dev)); 414} 415 416#ifndef INTRNG 417/* Mostly copy of acpi_alloc_resource() */ 418static struct resource * 419acpi_spibus_alloc_resource(device_t dev, device_t child, int type, int *rid, 420 rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 421{ 422 ACPI_RESOURCE ares; 423 struct resource_list *rl; 424 struct resource *res; 425 426 if (device_get_parent(child) != dev) 427 return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child, 428 type, rid, start, end, count, flags)); 429 430 rl = BUS_GET_RESOURCE_LIST(dev, child); 431 if (rl == NULL) 432 return (NULL); 433 434 res = resource_list_alloc(rl, dev, child, type, rid, 435 start, end, count, flags); 436 if (res != NULL && type == SYS_RES_IRQ && 437 ACPI_SUCCESS(acpi_lookup_irq_resource(child, *rid, res, &ares))) 438 acpi_config_intr(child, &ares); 439 440 return (res); 441} 442#endif 443 444/* 445 * If this device is an ACPI child but no one claimed it, attempt 446 * to power it off. We'll power it back up when a driver is added. 447 */ 448static void 449acpi_spibus_probe_nomatch(device_t bus, device_t child) 450{ 451 spibus_probe_nomatch(bus, child); 452 acpi_set_powerstate(child, ACPI_STATE_D3); 453} 454 455/* 456 * If a new driver has a chance to probe a child, first power it up. 457 */ 458static void 459acpi_spibus_driver_added(device_t dev, driver_t *driver) 460{ 461 device_t child, *devlist; 462 int i, numdevs; 463 464 DEVICE_IDENTIFY(driver, dev); 465 if (device_get_children(dev, &devlist, &numdevs) != 0) 466 return; 467 468 for (i = 0; i < numdevs; i++) { 469 child = devlist[i]; 470 if (device_get_state(child) == DS_NOTPRESENT) { 471 acpi_set_powerstate(child, ACPI_STATE_D0); 472 if (device_probe_and_attach(child) != 0) 473 acpi_set_powerstate(child, ACPI_STATE_D3); 474 } 475 } 476 free(devlist, M_TEMP); 477} 478 479static void 480acpi_spibus_child_deleted(device_t bus, device_t child) 481{ 482 struct acpi_spibus_ivar *devi = device_get_ivars(child); 483 484 if (acpi_get_device(devi->handle) == child) 485 AcpiDetachData(devi->handle, acpi_fake_objhandler); 486} 487 488static int 489acpi_spibus_read_ivar(device_t bus, device_t child, int which, uintptr_t *res) 490{ 491 struct acpi_spibus_ivar *devi = device_get_ivars(child); 492 493 switch (which) { 494 case ACPI_IVAR_HANDLE: 495 *res = (uintptr_t)devi->handle; 496 break; 497 default: 498 return (spibus_read_ivar(bus, child, which, res)); 499 } 500 501 return (0); 502} 503 504static int 505acpi_spibus_write_ivar(device_t bus, device_t child, int which, uintptr_t val) 506{ 507 struct acpi_spibus_ivar *devi = device_get_ivars(child); 508 509 switch (which) { 510 case ACPI_IVAR_HANDLE: 511 if (devi->handle != NULL) 512 return (EINVAL); 513 devi->handle = (ACPI_HANDLE)val; 514 break; 515 default: 516 return (spibus_write_ivar(bus, child, which, val)); 517 } 518 519 return (0); 520} 521 522/* Location hint for devctl(8). Concatenate IIC and ACPI hints. */ 523static int 524acpi_spibus_child_location(device_t bus, device_t child, struct sbuf *sb) 525{ 526 struct acpi_spibus_ivar *devi = device_get_ivars(child); 527 int error; 528 529 /* read SPI location hint string into the buffer. */ 530 error = spibus_child_location(bus, child, sb); 531 if (error != 0) 532 return (error); 533 534 /* Place ACPI string right after IIC one's terminating NUL. */ 535 if (devi->handle != NULL) 536 sbuf_printf(sb, " handle=%s", acpi_name(devi->handle)); 537 538 return (0); 539} 540 541/* PnP information for devctl(8). */ 542static int 543acpi_spibus_child_pnpinfo(device_t bus, device_t child, struct sbuf *sb) 544{ 545 struct acpi_spibus_ivar *devi = device_get_ivars(child); 546 547 return ( 548 devi->handle == NULL ? ENOTSUP : acpi_pnpinfo(devi->handle, sb)); 549} 550 551static device_method_t acpi_spibus_methods[] = { 552 /* Device interface */ 553 DEVMETHOD(device_probe, acpi_spibus_probe), 554 DEVMETHOD(device_attach, acpi_spibus_attach), 555 DEVMETHOD(device_detach, acpi_spibus_detach), 556 DEVMETHOD(device_suspend, acpi_spibus_suspend), 557 DEVMETHOD(device_resume, acpi_spibus_resume), 558 559 /* Bus interface */ 560#ifndef INTRNG 561 DEVMETHOD(bus_alloc_resource, acpi_spibus_alloc_resource), 562#endif 563 DEVMETHOD(bus_add_child, acpi_spibus_add_child), 564 DEVMETHOD(bus_probe_nomatch, acpi_spibus_probe_nomatch), 565 DEVMETHOD(bus_driver_added, acpi_spibus_driver_added), 566 DEVMETHOD(bus_child_deleted, acpi_spibus_child_deleted), 567 DEVMETHOD(bus_read_ivar, acpi_spibus_read_ivar), 568 DEVMETHOD(bus_write_ivar, acpi_spibus_write_ivar), 569 DEVMETHOD(bus_child_location, acpi_spibus_child_location), 570 DEVMETHOD(bus_child_pnpinfo, acpi_spibus_child_pnpinfo), 571 DEVMETHOD(bus_get_device_path, acpi_get_acpi_device_path), 572 573 DEVMETHOD_END, 574}; 575 576DEFINE_CLASS_1(spibus, acpi_spibus_driver, acpi_spibus_methods, 577 sizeof(struct spibus_softc), spibus_driver); 578DRIVER_MODULE(acpi_spibus, spi, acpi_spibus_driver, NULL, NULL); 579MODULE_VERSION(acpi_spibus, 1); 580MODULE_DEPEND(acpi_spibus, acpi, 1, 1, 1); 581