17d380b98SVladimir Kondratyev /*-
27d380b98SVladimir Kondratyev * SPDX-License-Identifier: BSD-2-Clause
37d380b98SVladimir Kondratyev *
47d380b98SVladimir Kondratyev * Copyright (c) 2023 Vladimir Kondratyev <wulf@FreeBSD.org>
57d380b98SVladimir Kondratyev *
67d380b98SVladimir Kondratyev * Redistribution and use in source and binary forms, with or without
77d380b98SVladimir Kondratyev * modification, are permitted provided that the following conditions
87d380b98SVladimir Kondratyev * are met:
97d380b98SVladimir Kondratyev * 1. Redistributions of source code must retain the above copyright
107d380b98SVladimir Kondratyev * notice, this list of conditions and the following disclaimer.
117d380b98SVladimir Kondratyev * 2. Redistributions in binary form must reproduce the above copyright
127d380b98SVladimir Kondratyev * notice, this list of conditions and the following disclaimer in the
137d380b98SVladimir Kondratyev * documentation and/or other materials provided with the distribution.
147d380b98SVladimir Kondratyev *
157d380b98SVladimir Kondratyev * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
167d380b98SVladimir Kondratyev * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
177d380b98SVladimir Kondratyev * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
187d380b98SVladimir Kondratyev * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
197d380b98SVladimir Kondratyev * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
207d380b98SVladimir Kondratyev * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
217d380b98SVladimir Kondratyev * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
227d380b98SVladimir Kondratyev * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
237d380b98SVladimir Kondratyev * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
247d380b98SVladimir Kondratyev * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
257d380b98SVladimir Kondratyev * SUCH DAMAGE.
267d380b98SVladimir Kondratyev */
277d380b98SVladimir Kondratyev
287d380b98SVladimir Kondratyev #include <sys/param.h>
297d380b98SVladimir Kondratyev #include <sys/bus.h>
307d380b98SVladimir Kondratyev #include <sys/endian.h>
317d380b98SVladimir Kondratyev #include <sys/kernel.h>
327d380b98SVladimir Kondratyev #include <sys/malloc.h>
337d380b98SVladimir Kondratyev #include <sys/module.h>
347d380b98SVladimir Kondratyev #include <sys/rman.h>
357d380b98SVladimir Kondratyev #include <sys/sbuf.h>
367d380b98SVladimir Kondratyev #include <sys/systm.h>
377d380b98SVladimir Kondratyev
387d380b98SVladimir Kondratyev #include <machine/resource.h>
397d380b98SVladimir Kondratyev
407d380b98SVladimir Kondratyev #include <contrib/dev/acpica/include/acpi.h>
417d380b98SVladimir Kondratyev #include <contrib/dev/acpica/include/accommon.h>
427d380b98SVladimir Kondratyev #include <contrib/dev/acpica/include/amlcode.h>
437d380b98SVladimir Kondratyev #include <dev/acpica/acpivar.h>
447d380b98SVladimir Kondratyev
457d380b98SVladimir Kondratyev #include <dev/spibus/spibusvar.h>
467d380b98SVladimir Kondratyev
477d380b98SVladimir Kondratyev /*
487d380b98SVladimir Kondratyev * Make a copy of ACPI_RESOURCE_SPI_SERIALBUS type and replace "pointer to ACPI
497d380b98SVladimir Kondratyev * object name string" field with pointer to ACPI object itself.
507d380b98SVladimir Kondratyev * This saves us extra strdup()/free() pair on acpi_spibus_get_acpi_res call.
517d380b98SVladimir Kondratyev */
527d380b98SVladimir Kondratyev typedef ACPI_RESOURCE_SPI_SERIALBUS ACPI_SPIBUS_RESOURCE_SPI_SERIALBUS;
537d380b98SVladimir Kondratyev #define ResourceSource_Handle ResourceSource.StringPtr
547d380b98SVladimir Kondratyev
557d380b98SVladimir Kondratyev /* Hooks for the ACPI CA debugging infrastructure. */
567d380b98SVladimir Kondratyev #define _COMPONENT ACPI_BUS
577d380b98SVladimir Kondratyev ACPI_MODULE_NAME("SPI")
587d380b98SVladimir Kondratyev
597d380b98SVladimir Kondratyev #if defined (__amd64__) || defined (__i386__)
607d380b98SVladimir Kondratyev static bool is_apple;
617d380b98SVladimir Kondratyev #endif
627d380b98SVladimir Kondratyev
637d380b98SVladimir Kondratyev struct acpi_spibus_ivar {
647d380b98SVladimir Kondratyev struct spibus_ivar super_ivar;
657d380b98SVladimir Kondratyev ACPI_HANDLE handle;
667d380b98SVladimir Kondratyev };
677d380b98SVladimir Kondratyev
687d380b98SVladimir Kondratyev static inline bool
acpi_resource_is_spi_serialbus(ACPI_RESOURCE * res)697d380b98SVladimir Kondratyev acpi_resource_is_spi_serialbus(ACPI_RESOURCE *res)
707d380b98SVladimir Kondratyev {
717d380b98SVladimir Kondratyev return (res->Type == ACPI_RESOURCE_TYPE_SERIAL_BUS &&
727d380b98SVladimir Kondratyev res->Data.CommonSerialBus.Type == ACPI_RESOURCE_SERIAL_TYPE_SPI);
737d380b98SVladimir Kondratyev }
747d380b98SVladimir Kondratyev
757d380b98SVladimir Kondratyev static ACPI_STATUS
acpi_spibus_get_acpi_res_cb(ACPI_RESOURCE * res,void * context)767d380b98SVladimir Kondratyev acpi_spibus_get_acpi_res_cb(ACPI_RESOURCE *res, void *context)
777d380b98SVladimir Kondratyev {
787d380b98SVladimir Kondratyev ACPI_SPIBUS_RESOURCE_SPI_SERIALBUS *sb = context;
797d380b98SVladimir Kondratyev ACPI_STATUS status;
807d380b98SVladimir Kondratyev ACPI_HANDLE handle;
817d380b98SVladimir Kondratyev
827d380b98SVladimir Kondratyev if (acpi_resource_is_spi_serialbus(res)) {
837d380b98SVladimir Kondratyev status = AcpiGetHandle(ACPI_ROOT_OBJECT,
847d380b98SVladimir Kondratyev res->Data.SpiSerialBus.ResourceSource.StringPtr, &handle);
857d380b98SVladimir Kondratyev if (ACPI_FAILURE(status))
867d380b98SVladimir Kondratyev return (status);
877d380b98SVladimir Kondratyev memcpy(sb, &res->Data.SpiSerialBus,
887d380b98SVladimir Kondratyev sizeof(ACPI_SPIBUS_RESOURCE_SPI_SERIALBUS));
897d380b98SVladimir Kondratyev /*
907d380b98SVladimir Kondratyev * replace "pointer to ACPI object name string" field
917d380b98SVladimir Kondratyev * with pointer to ACPI object itself.
927d380b98SVladimir Kondratyev */
937d380b98SVladimir Kondratyev sb->ResourceSource_Handle = handle;
947d380b98SVladimir Kondratyev return (AE_CTRL_TERMINATE);
957d380b98SVladimir Kondratyev } else if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
967d380b98SVladimir Kondratyev return (AE_NOT_FOUND);
977d380b98SVladimir Kondratyev
987d380b98SVladimir Kondratyev return (AE_OK);
997d380b98SVladimir Kondratyev }
1007d380b98SVladimir Kondratyev
1017d380b98SVladimir Kondratyev static void
acpi_spibus_dump_res(device_t dev,ACPI_SPIBUS_RESOURCE_SPI_SERIALBUS * sb)1027d380b98SVladimir Kondratyev acpi_spibus_dump_res(device_t dev, ACPI_SPIBUS_RESOURCE_SPI_SERIALBUS *sb)
1037d380b98SVladimir Kondratyev {
1047d380b98SVladimir Kondratyev device_printf(dev, "found ACPI child\n");
1057d380b98SVladimir Kondratyev printf(" DeviceSelection: 0x%04hx\n", sb->DeviceSelection);
1067d380b98SVladimir Kondratyev printf(" ConnectionSpeed: %uHz\n", sb->ConnectionSpeed);
1077d380b98SVladimir Kondratyev printf(" WireMode: %s\n",
1087d380b98SVladimir Kondratyev sb->WireMode == ACPI_SPI_4WIRE_MODE ?
1097d380b98SVladimir Kondratyev "FourWireMode" : "ThreeWireMode");
1107d380b98SVladimir Kondratyev printf(" DevicePolarity: %s\n",
1117d380b98SVladimir Kondratyev sb->DevicePolarity == ACPI_SPI_ACTIVE_LOW ?
1127d380b98SVladimir Kondratyev "PolarityLow" : "PolarityHigh");
1137d380b98SVladimir Kondratyev printf(" DataBitLength: %uBit\n", sb->DataBitLength);
1147d380b98SVladimir Kondratyev printf(" ClockPhase: %s\n",
1157d380b98SVladimir Kondratyev sb->ClockPhase == ACPI_SPI_FIRST_PHASE ?
1167d380b98SVladimir Kondratyev "ClockPhaseFirst" : "ClockPhaseSecond");
1177d380b98SVladimir Kondratyev printf(" ClockPolarity: %s\n",
1187d380b98SVladimir Kondratyev sb->ClockPolarity == ACPI_SPI_START_LOW ?
1197d380b98SVladimir Kondratyev "ClockPolarityLow" : "ClockPolarityHigh");
1207d380b98SVladimir Kondratyev printf(" SlaveMode: %s\n",
1217d380b98SVladimir Kondratyev sb->SlaveMode == ACPI_CONTROLLER_INITIATED ?
1227d380b98SVladimir Kondratyev "ControllerInitiated" : "DeviceInitiated");
1237d380b98SVladimir Kondratyev printf(" ConnectionSharing: %s\n", sb->ConnectionSharing == 0 ?
1247d380b98SVladimir Kondratyev "Exclusive" : "Shared");
1257d380b98SVladimir Kondratyev }
1267d380b98SVladimir Kondratyev
1277d380b98SVladimir Kondratyev static int
acpi_spibus_get_acpi_res(device_t spibus,ACPI_HANDLE dev,struct spibus_ivar * res)1287d380b98SVladimir Kondratyev acpi_spibus_get_acpi_res(device_t spibus, ACPI_HANDLE dev,
1297d380b98SVladimir Kondratyev struct spibus_ivar *res)
1307d380b98SVladimir Kondratyev {
1317d380b98SVladimir Kondratyev ACPI_SPIBUS_RESOURCE_SPI_SERIALBUS sb;
1327d380b98SVladimir Kondratyev
1337d380b98SVladimir Kondratyev /*
1347d380b98SVladimir Kondratyev * Read "SPI Serial Bus Connection Resource Descriptor"
1357d380b98SVladimir Kondratyev * described in p.19.6.126 of ACPI specification.
1367d380b98SVladimir Kondratyev */
1377d380b98SVladimir Kondratyev bzero(&sb, sizeof(ACPI_SPIBUS_RESOURCE_SPI_SERIALBUS));
1387d380b98SVladimir Kondratyev if (ACPI_FAILURE(AcpiWalkResources(dev, "_CRS",
1397d380b98SVladimir Kondratyev acpi_spibus_get_acpi_res_cb, &sb)))
1407d380b98SVladimir Kondratyev return (ENXIO);
1417d380b98SVladimir Kondratyev if (sb.ResourceSource_Handle !=
1427d380b98SVladimir Kondratyev acpi_get_handle(device_get_parent(spibus)))
1437d380b98SVladimir Kondratyev return (ENXIO);
1447d380b98SVladimir Kondratyev if (bootverbose)
1457d380b98SVladimir Kondratyev acpi_spibus_dump_res(spibus, &sb);
1467d380b98SVladimir Kondratyev /*
1477d380b98SVladimir Kondratyev * The Windows Baytrail and Braswell SPI host controller
1487d380b98SVladimir Kondratyev * drivers uses 1 as the first (and only) value for ACPI
1497d380b98SVladimir Kondratyev * DeviceSelection.
1507d380b98SVladimir Kondratyev */
1517d380b98SVladimir Kondratyev if (sb.DeviceSelection != 0 &&
1527d380b98SVladimir Kondratyev (acpi_MatchHid(sb.ResourceSource_Handle, "80860F0E") ||
1537d380b98SVladimir Kondratyev acpi_MatchHid(sb.ResourceSource_Handle, "8086228E")))
1547d380b98SVladimir Kondratyev res->cs = sb.DeviceSelection - 1;
1557d380b98SVladimir Kondratyev else
1567d380b98SVladimir Kondratyev res->cs = sb.DeviceSelection;
1577d380b98SVladimir Kondratyev res->mode =
1587d380b98SVladimir Kondratyev (sb.ClockPhase != ACPI_SPI_FIRST_PHASE ? SPIBUS_MODE_CPHA : 0) |
1597d380b98SVladimir Kondratyev (sb.ClockPolarity != ACPI_SPI_START_LOW ? SPIBUS_MODE_CPOL : 0);
1607d380b98SVladimir Kondratyev res->clock = sb.ConnectionSpeed;
1617d380b98SVladimir Kondratyev
1627d380b98SVladimir Kondratyev return (0);
1637d380b98SVladimir Kondratyev }
1647d380b98SVladimir Kondratyev
1657d380b98SVladimir Kondratyev #if defined (__amd64__) || defined (__i386__)
1667d380b98SVladimir Kondratyev static int
acpi_spibus_get_apple_res(device_t spibus,ACPI_HANDLE dev,struct spibus_ivar * ivar)1677d380b98SVladimir Kondratyev acpi_spibus_get_apple_res(device_t spibus, ACPI_HANDLE dev,
1687d380b98SVladimir Kondratyev struct spibus_ivar *ivar)
1697d380b98SVladimir Kondratyev {
1707d380b98SVladimir Kondratyev /* a0b5b7c6-1318-441c-b0c9-fe695eaf949b */
1717d380b98SVladimir Kondratyev static const uint8_t apple_guid[ACPI_UUID_LENGTH] = {
1727d380b98SVladimir Kondratyev 0xC6, 0xB7, 0xB5, 0xA0, 0x18, 0x13, 0x1C, 0x44,
1737d380b98SVladimir Kondratyev 0xB0, 0xC9, 0xFE, 0x69, 0x5E, 0xAF, 0x94, 0x9B,
1747d380b98SVladimir Kondratyev };
1757d380b98SVladimir Kondratyev ACPI_BUFFER buf;
1767d380b98SVladimir Kondratyev ACPI_OBJECT *pkg, *comp;
1777d380b98SVladimir Kondratyev ACPI_HANDLE parent;
1787d380b98SVladimir Kondratyev char *k;
1797d380b98SVladimir Kondratyev uint64_t val;
1807d380b98SVladimir Kondratyev
1817d380b98SVladimir Kondratyev /* Apple does not use _CRS but nested devices for SPI slaves */
1827d380b98SVladimir Kondratyev if (ACPI_FAILURE(AcpiGetParent(dev, &parent)))
1837d380b98SVladimir Kondratyev return (ENXIO);
1847d380b98SVladimir Kondratyev if (parent != acpi_get_handle(device_get_parent(spibus)))
1857d380b98SVladimir Kondratyev return (ENXIO);
1867d380b98SVladimir Kondratyev if (ACPI_FAILURE(acpi_EvaluateDSMTyped(dev, apple_guid,
1877d380b98SVladimir Kondratyev 1, 1, NULL, &buf, ACPI_TYPE_PACKAGE)))
1887d380b98SVladimir Kondratyev return (ENXIO);
1897d380b98SVladimir Kondratyev
1907d380b98SVladimir Kondratyev pkg = ((ACPI_OBJECT *)buf.Pointer);
1917d380b98SVladimir Kondratyev if (pkg->Package.Count % 2 != 0) {
1927d380b98SVladimir Kondratyev device_printf(spibus, "_DSM length %d not even\n",
1937d380b98SVladimir Kondratyev pkg->Package.Count);
1947d380b98SVladimir Kondratyev AcpiOsFree(pkg);
1957d380b98SVladimir Kondratyev return (ENXIO);
1967d380b98SVladimir Kondratyev }
1977d380b98SVladimir Kondratyev
1987d380b98SVladimir Kondratyev if (bootverbose)
1997d380b98SVladimir Kondratyev device_printf(spibus, "found ACPI child\n");
2007d380b98SVladimir Kondratyev
2017d380b98SVladimir Kondratyev for (comp = pkg->Package.Elements;
2027d380b98SVladimir Kondratyev comp < pkg->Package.Elements + pkg->Package.Count;
2037d380b98SVladimir Kondratyev comp += 2) {
2047d380b98SVladimir Kondratyev
2057d380b98SVladimir Kondratyev if (comp[0].Type != ACPI_TYPE_STRING ||
2067d380b98SVladimir Kondratyev comp[1].Type != ACPI_TYPE_BUFFER) {
2077d380b98SVladimir Kondratyev device_printf(spibus, "expected string+buffer, "
2087d380b98SVladimir Kondratyev "got %d+%d\n", comp[0].Type, comp[1].Type);
2097d380b98SVladimir Kondratyev continue;
2107d380b98SVladimir Kondratyev }
2117d380b98SVladimir Kondratyev k = comp[0].String.Pointer;
2127d380b98SVladimir Kondratyev val = comp[1].Buffer.Length >= 8 ?
2137d380b98SVladimir Kondratyev *(uint64_t *)comp[1].Buffer.Pointer : 0;
2147d380b98SVladimir Kondratyev
2157d380b98SVladimir Kondratyev if (bootverbose)
216676f02a4SKonstantin Belousov printf(" %s: %ju\n", k, (intmax_t)val);
2177d380b98SVladimir Kondratyev
2187d380b98SVladimir Kondratyev if (strcmp(k, "spiSclkPeriod") == 0) {
2197d380b98SVladimir Kondratyev if (val != 0)
2207d380b98SVladimir Kondratyev ivar->clock = 1000000000 / val;
2217d380b98SVladimir Kondratyev } else if (strcmp(k, "spiSPO") == 0) {
2227d380b98SVladimir Kondratyev if (val != 0)
2237d380b98SVladimir Kondratyev ivar->mode |= SPIBUS_MODE_CPOL;
2247d380b98SVladimir Kondratyev } else if (strcmp(k, "spiSPH") == 0) {
2257d380b98SVladimir Kondratyev if (val != 0)
2267d380b98SVladimir Kondratyev ivar->mode |= SPIBUS_MODE_CPHA;
2277d380b98SVladimir Kondratyev } else if (strcmp(k, "spiCSDelay") == 0) {
2287d380b98SVladimir Kondratyev ivar->cs_delay = val;
2297d380b98SVladimir Kondratyev }
2307d380b98SVladimir Kondratyev }
2317d380b98SVladimir Kondratyev
2327d380b98SVladimir Kondratyev AcpiOsFree(pkg);
2337d380b98SVladimir Kondratyev
2347d380b98SVladimir Kondratyev return (0);
2357d380b98SVladimir Kondratyev }
2367d380b98SVladimir Kondratyev #endif
2377d380b98SVladimir Kondratyev
2387d380b98SVladimir Kondratyev static int
acpi_spibus_delete_acpi_child(ACPI_HANDLE handle)2397d380b98SVladimir Kondratyev acpi_spibus_delete_acpi_child(ACPI_HANDLE handle)
2407d380b98SVladimir Kondratyev {
2417d380b98SVladimir Kondratyev device_t acpi_child, acpi0;
2427d380b98SVladimir Kondratyev
2437d380b98SVladimir Kondratyev /* Delete existing child of acpi bus */
2447d380b98SVladimir Kondratyev acpi_child = acpi_get_device(handle);
2457d380b98SVladimir Kondratyev if (acpi_child != NULL) {
2467d380b98SVladimir Kondratyev acpi0 = devclass_get_device(devclass_find("acpi"), 0);
2477d380b98SVladimir Kondratyev if (device_get_parent(acpi_child) != acpi0)
2487d380b98SVladimir Kondratyev return (ENXIO);
2497d380b98SVladimir Kondratyev
2507d380b98SVladimir Kondratyev if (device_is_attached(acpi_child))
2517d380b98SVladimir Kondratyev return (ENXIO);
2527d380b98SVladimir Kondratyev
2537d380b98SVladimir Kondratyev if (device_delete_child(acpi0, acpi_child) != 0)
2547d380b98SVladimir Kondratyev return (ENXIO);
2557d380b98SVladimir Kondratyev }
2567d380b98SVladimir Kondratyev
2577d380b98SVladimir Kondratyev return (0);
2587d380b98SVladimir Kondratyev }
2597d380b98SVladimir Kondratyev
2607d380b98SVladimir Kondratyev static device_t
acpi_spibus_add_child(device_t dev,u_int order,const char * name,int unit)2617d380b98SVladimir Kondratyev acpi_spibus_add_child(device_t dev, u_int order, const char *name, int unit)
2627d380b98SVladimir Kondratyev {
2637d380b98SVladimir Kondratyev return (spibus_add_child_common(
2647d380b98SVladimir Kondratyev dev, order, name, unit, sizeof(struct acpi_spibus_ivar)));
2657d380b98SVladimir Kondratyev }
2667d380b98SVladimir Kondratyev
2677d380b98SVladimir Kondratyev static ACPI_STATUS
acpi_spibus_enumerate_child(ACPI_HANDLE handle,UINT32 level,void * context,void ** result)2687d380b98SVladimir Kondratyev acpi_spibus_enumerate_child(ACPI_HANDLE handle, UINT32 level,
2697d380b98SVladimir Kondratyev void *context, void **result)
2707d380b98SVladimir Kondratyev {
2717d380b98SVladimir Kondratyev device_t spibus, child;
2727d380b98SVladimir Kondratyev struct spibus_ivar res;
2737d380b98SVladimir Kondratyev ACPI_STATUS status;
2747d380b98SVladimir Kondratyev UINT32 sta;
2757d380b98SVladimir Kondratyev bool found = false;
2767d380b98SVladimir Kondratyev
2777d380b98SVladimir Kondratyev spibus = context;
2787d380b98SVladimir Kondratyev
2797d380b98SVladimir Kondratyev /*
2807d380b98SVladimir Kondratyev * If no _STA method or if it failed, then assume that
2817d380b98SVladimir Kondratyev * the device is present.
2827d380b98SVladimir Kondratyev */
2837d380b98SVladimir Kondratyev if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) &&
2847d380b98SVladimir Kondratyev !ACPI_DEVICE_PRESENT(sta))
2857d380b98SVladimir Kondratyev return (AE_OK);
2867d380b98SVladimir Kondratyev
2877d380b98SVladimir Kondratyev if (!acpi_has_hid(handle))
2887d380b98SVladimir Kondratyev return (AE_OK);
2897d380b98SVladimir Kondratyev
2907d380b98SVladimir Kondratyev bzero(&res, sizeof(res));
2917d380b98SVladimir Kondratyev if (acpi_spibus_get_acpi_res(spibus, handle, &res) == 0)
2927d380b98SVladimir Kondratyev found = true;
2937d380b98SVladimir Kondratyev #if defined (__amd64__) || defined (__i386__)
2947d380b98SVladimir Kondratyev if (!found && is_apple &&
2957d380b98SVladimir Kondratyev acpi_spibus_get_apple_res(spibus, handle, &res) == 0)
2967d380b98SVladimir Kondratyev found = true;
2977d380b98SVladimir Kondratyev #endif
2987d380b98SVladimir Kondratyev if (!found || res.clock == 0)
2997d380b98SVladimir Kondratyev return (AE_OK);
3007d380b98SVladimir Kondratyev
3017d380b98SVladimir Kondratyev /* Delete existing child of acpi bus */
3027d380b98SVladimir Kondratyev if (acpi_spibus_delete_acpi_child(handle) != 0)
3037d380b98SVladimir Kondratyev return (AE_OK);
3047d380b98SVladimir Kondratyev
305*b670c9baSAhmad Khalifa child = BUS_ADD_CHILD(spibus, 0, NULL, DEVICE_UNIT_ANY);
3067d380b98SVladimir Kondratyev if (child == NULL) {
3077d380b98SVladimir Kondratyev device_printf(spibus, "add child failed\n");
3087d380b98SVladimir Kondratyev return (AE_OK);
3097d380b98SVladimir Kondratyev }
3107d380b98SVladimir Kondratyev
3117d380b98SVladimir Kondratyev spibus_set_cs(child, res.cs);
3127d380b98SVladimir Kondratyev spibus_set_mode(child, res.mode);
3137d380b98SVladimir Kondratyev spibus_set_clock(child, res.clock);
3147d380b98SVladimir Kondratyev spibus_set_cs_delay(child, res.cs_delay);
3157d380b98SVladimir Kondratyev acpi_set_handle(child, handle);
3167d380b98SVladimir Kondratyev acpi_parse_resources(child, handle, &acpi_res_parse_set, NULL);
3177d380b98SVladimir Kondratyev
3187d380b98SVladimir Kondratyev /*
3197d380b98SVladimir Kondratyev * Update ACPI-CA to use the IIC enumerated device_t for this handle.
3207d380b98SVladimir Kondratyev */
3217d380b98SVladimir Kondratyev status = AcpiAttachData(handle, acpi_fake_objhandler, child);
3227d380b98SVladimir Kondratyev if (ACPI_FAILURE(status))
3237d380b98SVladimir Kondratyev printf("WARNING: Unable to attach object data to %s - %s\n",
3247d380b98SVladimir Kondratyev acpi_name(handle), AcpiFormatException(status));
3257d380b98SVladimir Kondratyev
3267d380b98SVladimir Kondratyev return (AE_OK);
3277d380b98SVladimir Kondratyev }
3287d380b98SVladimir Kondratyev
3297d380b98SVladimir Kondratyev static ACPI_STATUS
acpi_spibus_enumerate_children(device_t dev)3307d380b98SVladimir Kondratyev acpi_spibus_enumerate_children(device_t dev)
3317d380b98SVladimir Kondratyev {
3327d380b98SVladimir Kondratyev return (AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
3337d380b98SVladimir Kondratyev ACPI_UINT32_MAX, acpi_spibus_enumerate_child, NULL, dev, NULL));
3347d380b98SVladimir Kondratyev }
3357d380b98SVladimir Kondratyev
3367d380b98SVladimir Kondratyev static void
acpi_spibus_set_power_children(device_t dev,int state,bool all_children)3377d380b98SVladimir Kondratyev acpi_spibus_set_power_children(device_t dev, int state, bool all_children)
3387d380b98SVladimir Kondratyev {
3397d380b98SVladimir Kondratyev device_t *devlist;
3407d380b98SVladimir Kondratyev int i, numdevs;
3417d380b98SVladimir Kondratyev
3427d380b98SVladimir Kondratyev if (device_get_children(dev, &devlist, &numdevs) != 0)
3437d380b98SVladimir Kondratyev return;
3447d380b98SVladimir Kondratyev
3457d380b98SVladimir Kondratyev for (i = 0; i < numdevs; i++)
3467d380b98SVladimir Kondratyev if (all_children || device_is_attached(devlist[i]) != 0)
3477d380b98SVladimir Kondratyev acpi_set_powerstate(devlist[i], state);
3487d380b98SVladimir Kondratyev
3497d380b98SVladimir Kondratyev free(devlist, M_TEMP);
3507d380b98SVladimir Kondratyev }
3517d380b98SVladimir Kondratyev
3527d380b98SVladimir Kondratyev static int
acpi_spibus_probe(device_t dev)3537d380b98SVladimir Kondratyev acpi_spibus_probe(device_t dev)
3547d380b98SVladimir Kondratyev {
3557d380b98SVladimir Kondratyev ACPI_HANDLE handle;
3567d380b98SVladimir Kondratyev device_t controller;
3577d380b98SVladimir Kondratyev
3587d380b98SVladimir Kondratyev if (acpi_disabled("spibus"))
3597d380b98SVladimir Kondratyev return (ENXIO);
3607d380b98SVladimir Kondratyev
3617d380b98SVladimir Kondratyev controller = device_get_parent(dev);
3627d380b98SVladimir Kondratyev if (controller == NULL)
3637d380b98SVladimir Kondratyev return (ENXIO);
3647d380b98SVladimir Kondratyev
3657d380b98SVladimir Kondratyev handle = acpi_get_handle(controller);
3667d380b98SVladimir Kondratyev if (handle == NULL)
3677d380b98SVladimir Kondratyev return (ENXIO);
3687d380b98SVladimir Kondratyev
3697d380b98SVladimir Kondratyev device_set_desc(dev, "SPI bus (ACPI-hinted)");
3707d380b98SVladimir Kondratyev return (BUS_PROBE_DEFAULT + 1);
3717d380b98SVladimir Kondratyev }
3727d380b98SVladimir Kondratyev
3737d380b98SVladimir Kondratyev static int
acpi_spibus_attach(device_t dev)3747d380b98SVladimir Kondratyev acpi_spibus_attach(device_t dev)
3757d380b98SVladimir Kondratyev {
3767d380b98SVladimir Kondratyev
3777d380b98SVladimir Kondratyev #if defined (__amd64__) || defined (__i386__)
3787d380b98SVladimir Kondratyev char *vendor = kern_getenv("smbios.bios.vendor");
3797d380b98SVladimir Kondratyev if (vendor != NULL &&
3807d380b98SVladimir Kondratyev (strcmp(vendor, "Apple Inc.") == 0 ||
3817d380b98SVladimir Kondratyev strcmp(vendor, "Apple Computer, Inc.") == 0))
3827d380b98SVladimir Kondratyev is_apple = true;
3837d380b98SVladimir Kondratyev #endif
3847d380b98SVladimir Kondratyev
3857d380b98SVladimir Kondratyev if (ACPI_FAILURE(acpi_spibus_enumerate_children(dev)))
3867d380b98SVladimir Kondratyev device_printf(dev, "children enumeration failed\n");
3877d380b98SVladimir Kondratyev
3887d380b98SVladimir Kondratyev acpi_spibus_set_power_children(dev, ACPI_STATE_D0, true);
3897d380b98SVladimir Kondratyev return (spibus_attach(dev));
3907d380b98SVladimir Kondratyev }
3917d380b98SVladimir Kondratyev
3927d380b98SVladimir Kondratyev static int
acpi_spibus_detach(device_t dev)3937d380b98SVladimir Kondratyev acpi_spibus_detach(device_t dev)
3947d380b98SVladimir Kondratyev {
3957d380b98SVladimir Kondratyev acpi_spibus_set_power_children(dev, ACPI_STATE_D3, false);
3967d380b98SVladimir Kondratyev
3970ac15e47SJohn Baldwin return (bus_generic_detach(dev));
3987d380b98SVladimir Kondratyev }
3997d380b98SVladimir Kondratyev
4007d380b98SVladimir Kondratyev static int
acpi_spibus_suspend(device_t dev)4017d380b98SVladimir Kondratyev acpi_spibus_suspend(device_t dev)
4027d380b98SVladimir Kondratyev {
4037d380b98SVladimir Kondratyev acpi_spibus_set_power_children(dev, ACPI_STATE_D3, false);
4047d380b98SVladimir Kondratyev
4057d380b98SVladimir Kondratyev return (bus_generic_suspend(dev));
4067d380b98SVladimir Kondratyev }
4077d380b98SVladimir Kondratyev
4087d380b98SVladimir Kondratyev static int
acpi_spibus_resume(device_t dev)4097d380b98SVladimir Kondratyev acpi_spibus_resume(device_t dev)
4107d380b98SVladimir Kondratyev {
4117d380b98SVladimir Kondratyev acpi_spibus_set_power_children(dev, ACPI_STATE_D0, false);
4127d380b98SVladimir Kondratyev
4137d380b98SVladimir Kondratyev return (bus_generic_resume(dev));
4147d380b98SVladimir Kondratyev }
4157d380b98SVladimir Kondratyev
4167d380b98SVladimir Kondratyev #ifndef INTRNG
4177d380b98SVladimir Kondratyev /* Mostly copy of acpi_alloc_resource() */
4187d380b98SVladimir Kondratyev static struct resource *
acpi_spibus_alloc_resource(device_t dev,device_t child,int type,int * rid,rman_res_t start,rman_res_t end,rman_res_t count,u_int flags)4197d380b98SVladimir Kondratyev acpi_spibus_alloc_resource(device_t dev, device_t child, int type, int *rid,
4207d380b98SVladimir Kondratyev rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
4217d380b98SVladimir Kondratyev {
4227d380b98SVladimir Kondratyev ACPI_RESOURCE ares;
4237d380b98SVladimir Kondratyev struct resource_list *rl;
4247d380b98SVladimir Kondratyev struct resource *res;
4257d380b98SVladimir Kondratyev
4267d380b98SVladimir Kondratyev if (device_get_parent(child) != dev)
4277d380b98SVladimir Kondratyev return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child,
4287d380b98SVladimir Kondratyev type, rid, start, end, count, flags));
4297d380b98SVladimir Kondratyev
4307d380b98SVladimir Kondratyev rl = BUS_GET_RESOURCE_LIST(dev, child);
4317d380b98SVladimir Kondratyev if (rl == NULL)
4327d380b98SVladimir Kondratyev return (NULL);
4337d380b98SVladimir Kondratyev
4347d380b98SVladimir Kondratyev res = resource_list_alloc(rl, dev, child, type, rid,
4357d380b98SVladimir Kondratyev start, end, count, flags);
4367d380b98SVladimir Kondratyev if (res != NULL && type == SYS_RES_IRQ &&
4377d380b98SVladimir Kondratyev ACPI_SUCCESS(acpi_lookup_irq_resource(child, *rid, res, &ares)))
4387d380b98SVladimir Kondratyev acpi_config_intr(child, &ares);
4397d380b98SVladimir Kondratyev
4407d380b98SVladimir Kondratyev return (res);
4417d380b98SVladimir Kondratyev }
4427d380b98SVladimir Kondratyev #endif
4437d380b98SVladimir Kondratyev
4447d380b98SVladimir Kondratyev /*
4457d380b98SVladimir Kondratyev * If this device is an ACPI child but no one claimed it, attempt
4467d380b98SVladimir Kondratyev * to power it off. We'll power it back up when a driver is added.
4477d380b98SVladimir Kondratyev */
4487d380b98SVladimir Kondratyev static void
acpi_spibus_probe_nomatch(device_t bus,device_t child)4497d380b98SVladimir Kondratyev acpi_spibus_probe_nomatch(device_t bus, device_t child)
4507d380b98SVladimir Kondratyev {
4517d380b98SVladimir Kondratyev spibus_probe_nomatch(bus, child);
4527d380b98SVladimir Kondratyev acpi_set_powerstate(child, ACPI_STATE_D3);
4537d380b98SVladimir Kondratyev }
4547d380b98SVladimir Kondratyev
4557d380b98SVladimir Kondratyev /*
4567d380b98SVladimir Kondratyev * If a new driver has a chance to probe a child, first power it up.
4577d380b98SVladimir Kondratyev */
4587d380b98SVladimir Kondratyev static void
acpi_spibus_driver_added(device_t dev,driver_t * driver)4597d380b98SVladimir Kondratyev acpi_spibus_driver_added(device_t dev, driver_t *driver)
4607d380b98SVladimir Kondratyev {
4617d380b98SVladimir Kondratyev device_t child, *devlist;
4627d380b98SVladimir Kondratyev int i, numdevs;
4637d380b98SVladimir Kondratyev
4647d380b98SVladimir Kondratyev DEVICE_IDENTIFY(driver, dev);
4657d380b98SVladimir Kondratyev if (device_get_children(dev, &devlist, &numdevs) != 0)
4667d380b98SVladimir Kondratyev return;
4677d380b98SVladimir Kondratyev
4687d380b98SVladimir Kondratyev for (i = 0; i < numdevs; i++) {
4697d380b98SVladimir Kondratyev child = devlist[i];
4707d380b98SVladimir Kondratyev if (device_get_state(child) == DS_NOTPRESENT) {
4717d380b98SVladimir Kondratyev acpi_set_powerstate(child, ACPI_STATE_D0);
4727d380b98SVladimir Kondratyev if (device_probe_and_attach(child) != 0)
4737d380b98SVladimir Kondratyev acpi_set_powerstate(child, ACPI_STATE_D3);
4747d380b98SVladimir Kondratyev }
4757d380b98SVladimir Kondratyev }
4767d380b98SVladimir Kondratyev free(devlist, M_TEMP);
4777d380b98SVladimir Kondratyev }
4787d380b98SVladimir Kondratyev
4797d380b98SVladimir Kondratyev static void
acpi_spibus_child_deleted(device_t bus,device_t child)4807d380b98SVladimir Kondratyev acpi_spibus_child_deleted(device_t bus, device_t child)
4817d380b98SVladimir Kondratyev {
4827d380b98SVladimir Kondratyev struct acpi_spibus_ivar *devi = device_get_ivars(child);
4837d380b98SVladimir Kondratyev
4847d380b98SVladimir Kondratyev if (acpi_get_device(devi->handle) == child)
4857d380b98SVladimir Kondratyev AcpiDetachData(devi->handle, acpi_fake_objhandler);
4867d380b98SVladimir Kondratyev }
4877d380b98SVladimir Kondratyev
4887d380b98SVladimir Kondratyev static int
acpi_spibus_read_ivar(device_t bus,device_t child,int which,uintptr_t * res)4897d380b98SVladimir Kondratyev acpi_spibus_read_ivar(device_t bus, device_t child, int which, uintptr_t *res)
4907d380b98SVladimir Kondratyev {
4917d380b98SVladimir Kondratyev struct acpi_spibus_ivar *devi = device_get_ivars(child);
4927d380b98SVladimir Kondratyev
4937d380b98SVladimir Kondratyev switch (which) {
4947d380b98SVladimir Kondratyev case ACPI_IVAR_HANDLE:
4957d380b98SVladimir Kondratyev *res = (uintptr_t)devi->handle;
4967d380b98SVladimir Kondratyev break;
4977d380b98SVladimir Kondratyev default:
4987d380b98SVladimir Kondratyev return (spibus_read_ivar(bus, child, which, res));
4997d380b98SVladimir Kondratyev }
5007d380b98SVladimir Kondratyev
5017d380b98SVladimir Kondratyev return (0);
5027d380b98SVladimir Kondratyev }
5037d380b98SVladimir Kondratyev
5047d380b98SVladimir Kondratyev static int
acpi_spibus_write_ivar(device_t bus,device_t child,int which,uintptr_t val)5057d380b98SVladimir Kondratyev acpi_spibus_write_ivar(device_t bus, device_t child, int which, uintptr_t val)
5067d380b98SVladimir Kondratyev {
5077d380b98SVladimir Kondratyev struct acpi_spibus_ivar *devi = device_get_ivars(child);
5087d380b98SVladimir Kondratyev
5097d380b98SVladimir Kondratyev switch (which) {
5107d380b98SVladimir Kondratyev case ACPI_IVAR_HANDLE:
5117d380b98SVladimir Kondratyev if (devi->handle != NULL)
5127d380b98SVladimir Kondratyev return (EINVAL);
5137d380b98SVladimir Kondratyev devi->handle = (ACPI_HANDLE)val;
5147d380b98SVladimir Kondratyev break;
5157d380b98SVladimir Kondratyev default:
5167d380b98SVladimir Kondratyev return (spibus_write_ivar(bus, child, which, val));
5177d380b98SVladimir Kondratyev }
5187d380b98SVladimir Kondratyev
5197d380b98SVladimir Kondratyev return (0);
5207d380b98SVladimir Kondratyev }
5217d380b98SVladimir Kondratyev
5227d380b98SVladimir Kondratyev /* Location hint for devctl(8). Concatenate IIC and ACPI hints. */
5237d380b98SVladimir Kondratyev static int
acpi_spibus_child_location(device_t bus,device_t child,struct sbuf * sb)5247d380b98SVladimir Kondratyev acpi_spibus_child_location(device_t bus, device_t child, struct sbuf *sb)
5257d380b98SVladimir Kondratyev {
5267d380b98SVladimir Kondratyev struct acpi_spibus_ivar *devi = device_get_ivars(child);
5277d380b98SVladimir Kondratyev int error;
5287d380b98SVladimir Kondratyev
5297d380b98SVladimir Kondratyev /* read SPI location hint string into the buffer. */
5307d380b98SVladimir Kondratyev error = spibus_child_location(bus, child, sb);
5317d380b98SVladimir Kondratyev if (error != 0)
5327d380b98SVladimir Kondratyev return (error);
5337d380b98SVladimir Kondratyev
5347d380b98SVladimir Kondratyev /* Place ACPI string right after IIC one's terminating NUL. */
5357d380b98SVladimir Kondratyev if (devi->handle != NULL)
5367d380b98SVladimir Kondratyev sbuf_printf(sb, " handle=%s", acpi_name(devi->handle));
5377d380b98SVladimir Kondratyev
5387d380b98SVladimir Kondratyev return (0);
5397d380b98SVladimir Kondratyev }
5407d380b98SVladimir Kondratyev
5417d380b98SVladimir Kondratyev /* PnP information for devctl(8). */
5427d380b98SVladimir Kondratyev static int
acpi_spibus_child_pnpinfo(device_t bus,device_t child,struct sbuf * sb)5437d380b98SVladimir Kondratyev acpi_spibus_child_pnpinfo(device_t bus, device_t child, struct sbuf *sb)
5447d380b98SVladimir Kondratyev {
5457d380b98SVladimir Kondratyev struct acpi_spibus_ivar *devi = device_get_ivars(child);
5467d380b98SVladimir Kondratyev
5477d380b98SVladimir Kondratyev return (
5487d380b98SVladimir Kondratyev devi->handle == NULL ? ENOTSUP : acpi_pnpinfo(devi->handle, sb));
5497d380b98SVladimir Kondratyev }
5507d380b98SVladimir Kondratyev
5517d380b98SVladimir Kondratyev static device_method_t acpi_spibus_methods[] = {
5527d380b98SVladimir Kondratyev /* Device interface */
5537d380b98SVladimir Kondratyev DEVMETHOD(device_probe, acpi_spibus_probe),
5547d380b98SVladimir Kondratyev DEVMETHOD(device_attach, acpi_spibus_attach),
5557d380b98SVladimir Kondratyev DEVMETHOD(device_detach, acpi_spibus_detach),
5567d380b98SVladimir Kondratyev DEVMETHOD(device_suspend, acpi_spibus_suspend),
5577d380b98SVladimir Kondratyev DEVMETHOD(device_resume, acpi_spibus_resume),
5587d380b98SVladimir Kondratyev
5597d380b98SVladimir Kondratyev /* Bus interface */
5607d380b98SVladimir Kondratyev #ifndef INTRNG
5617d380b98SVladimir Kondratyev DEVMETHOD(bus_alloc_resource, acpi_spibus_alloc_resource),
5627d380b98SVladimir Kondratyev #endif
5637d380b98SVladimir Kondratyev DEVMETHOD(bus_add_child, acpi_spibus_add_child),
56484553ccdSJohn Baldwin DEVMETHOD(bus_child_deleted, spibus_child_deleted),
5657d380b98SVladimir Kondratyev DEVMETHOD(bus_probe_nomatch, acpi_spibus_probe_nomatch),
5667d380b98SVladimir Kondratyev DEVMETHOD(bus_driver_added, acpi_spibus_driver_added),
5677d380b98SVladimir Kondratyev DEVMETHOD(bus_child_deleted, acpi_spibus_child_deleted),
5687d380b98SVladimir Kondratyev DEVMETHOD(bus_read_ivar, acpi_spibus_read_ivar),
5697d380b98SVladimir Kondratyev DEVMETHOD(bus_write_ivar, acpi_spibus_write_ivar),
5707d380b98SVladimir Kondratyev DEVMETHOD(bus_child_location, acpi_spibus_child_location),
5717d380b98SVladimir Kondratyev DEVMETHOD(bus_child_pnpinfo, acpi_spibus_child_pnpinfo),
5727d380b98SVladimir Kondratyev DEVMETHOD(bus_get_device_path, acpi_get_acpi_device_path),
5737d380b98SVladimir Kondratyev
5747d380b98SVladimir Kondratyev DEVMETHOD_END,
5757d380b98SVladimir Kondratyev };
5767d380b98SVladimir Kondratyev
5777d380b98SVladimir Kondratyev DEFINE_CLASS_1(spibus, acpi_spibus_driver, acpi_spibus_methods,
5787d380b98SVladimir Kondratyev sizeof(struct spibus_softc), spibus_driver);
5797d380b98SVladimir Kondratyev DRIVER_MODULE(acpi_spibus, spi, acpi_spibus_driver, NULL, NULL);
5807d380b98SVladimir Kondratyev MODULE_VERSION(acpi_spibus, 1);
5817d380b98SVladimir Kondratyev MODULE_DEPEND(acpi_spibus, acpi, 1, 1, 1);
582