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