[PATCH] sdhci_fsl: add ACPI front-end for NXP Layerscape eSDHC (NXP0003)
yarshure <[email protected]> Thu, 9 Jul 2026 23:22:35 +0000
| Newsgroups | gmane.os.freebsd.devel.arm |
|---|---|
| Message-ID | <[email protected]> |
Refactor the FDT-only sdhci_fsl driver into a bus-agnostic base class (DEFINE_CLASS_0 "sdhci_fsl") plus FDT and ACPI subclasses, mirroring sdhci_xenon. A shared sdhci_fsl_attach_common() does the controller bring-up; each front-end supplies the bus-specific bits (soc_data, base clock, endianness, mmc host properties, card-detect). The new ACPI front-end (sdhci_fsl_acpi.c) matches _HID NXP0003, reads the base clock from the _DSD "clock-frequency" property (there is no FDT clock framework under ACPI), and brings up the eSDHC controllers described by UEFI/EDK2 firmware. On a SolidRun LX2160A CEX7 booting via EDK2 this exposes the SD/TF card (/dev/mmcsd0) and eMMC (/dev/mmcsd1) that were previously invisible because no driver matched NXP0003. - New sdhci_fsl.h: shared softc, soc_data and DECLARE_CLASS(sdhci_fsl_driver). - sdhci_fsl_fdt.c: split attach into an FDT prologue + bus-agnostic sdhci_fsl_attach_common(); base class + FDT subclass; declare the mmc bridge on the shared "sdhci_fsl" devclass; guard get_ro for the no-GPIO (ACPI) case. - sdhci_fsl_acpi.c: NXP0003 ACPI binding; base clock from _DSD; generic card-present; mark the slot non-removable (eSDHC present-state card detect is unreliable); cap to high speed pending UHS tuning support. MFC after: 2 weeks Signed-off-by: yarshure <[email protected]> --- Notes for review (not part of the commit message): * Verification. Hardware-tested on a SolidRun HoneyComb / CEX7 (LX2160A) booting via EDK2/UEFI (ACPI). Before this change nothing matched _HID NXP0003, so the eSDHC controllers were unattached; after it both slots probe and come up: mmc0: <MMC/SD bus> ... on sdhci_fsl_acpi0 mmcsd0: <MMCHC ...> ... (SD/TF card) mmc1: <MMC/SD bus> ... on sdhci_fsl_acpi1 mmcsd1: <MMCHC ...> ... (eMMC) /dev/mmcsd0 and /dev/mmcsd1 read and write correctly. * The FDT path is preserved unchanged -- the refactor only splits the existing sdhci_fsl_attach() into an FDT prologue plus a bus-agnostic sdhci_fsl_attach_common(), mirroring sdhci_xenon's base/FDT/ACPI split, so existing FDT (device-tree) boots behave exactly as before. It has not been re-tested on an FDT-booted board; a smoke test there is welcome. * Two ACPI-specific choices worth a look: the base clock is read from the _DSD "clock-frequency" property (no FDT clock framework under ACPI), and the slot is marked non-removable + capped to high speed (eSDHC present-state card detect is unreliable; UHS tuning is future work). * Disclosure: developed with AI assistance; the hardware bring-up above was done by hand. sys/conf/files | 1 + sys/dev/sdhci/sdhci_fsl.h | 61 +++++++++ sys/dev/sdhci/sdhci_fsl_acpi.c | 124 +++++++++++++++++ sys/dev/sdhci/sdhci_fsl_fdt.c | 237 +++++++++++++++++++-------------- 4 files changed, 321 insertions(+), 102 deletions(-) create mode 100644 sys/dev/sdhci/sdhci_fsl.h create mode 100644 sys/dev/sdhci/sdhci_fsl_acpi.c diff --git a/sys/conf/files b/sys/conf/files index cc18ecbb9..face1c207 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -3138,6 +3138,7 @@ dev/sdhci/sdhci.c optional sdhci dev/sdhci/sdhci_fdt.c optional sdhci fdt regulator clk dev/sdhci/sdhci_fdt_gpio.c optional sdhci fdt gpio dev/sdhci/sdhci_fsl_fdt.c optional sdhci fdt gpio regulator clk +dev/sdhci/sdhci_fsl_acpi.c optional sdhci fdt gpio regulator clk acpi dev/sdhci/sdhci_if.m optional sdhci dev/sdhci/sdhci_acpi.c optional sdhci acpi dev/sdhci/sdhci_pci.c optional sdhci pci diff --git a/sys/dev/sdhci/sdhci_fsl.h b/sys/dev/sdhci/sdhci_fsl.h new file mode 100644 index 000000000..a623b3b96 --- /dev/null +++ b/sys/dev/sdhci/sdhci_fsl.h @@ -0,0 +1,61 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Shared definitions for the NXP QorIQ Layerscape eSDHC controller, used by + * both the FDT (sdhci_fsl_fdt.c) and ACPI (sdhci_fsl_acpi.c) bus front-ends. + */ + +#ifndef _SDHCI_FSL_H_ +#define _SDHCI_FSL_H_ + +#include <dev/mmc/mmc_helpers.h> +#include <dev/sdhci/sdhci.h> + +struct sdhci_fdt_gpio; + +struct sdhci_fsl_fdt_soc_data { + int quirks; + int baseclk_div; + uint8_t errata; + char *syscon_compat; +}; + +extern const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_lx2160a_soc_data; + +struct sdhci_fsl_fdt_softc { + device_t dev; + const struct sdhci_fsl_fdt_soc_data *soc_data; + struct resource *mem_res; + struct resource *irq_res; + void *irq_cookie; + uint32_t baseclk_hz; + uint32_t maxclk_hz; + struct sdhci_fdt_gpio *gpio; + struct sdhci_slot slot; + bool slot_init_done; + uint32_t cmd_and_mode; + uint16_t sdclk_bits; + struct mmc_helper fdt_helper; + uint32_t div_ratio; + uint8_t vendor_ver; + uint32_t flags; + + /* Set by the bus front-end before calling sdhci_fsl_attach_common(). */ + bool little_endian; + bool acpi; /* ACPI front-end (no FDT node) */ + + uint32_t (* read)(struct sdhci_fsl_fdt_softc *, bus_size_t); + void (* write)(struct sdhci_fsl_fdt_softc *, bus_size_t, uint32_t); +}; + +DECLARE_CLASS(sdhci_fsl_driver); + +/* + * Bus-agnostic attach: allocate resources, configure the eSDHC block and bring + * up the slot. The caller must first fill soc_data, baseclk_hz, little_endian, + * acpi and the parsed mmc host properties (slot.host) in the softc. + */ +int sdhci_fsl_attach_common(device_t dev); +int sdhci_fsl_detach(device_t dev); + +#endif /* _SDHCI_FSL_H_ */ diff --git a/sys/dev/sdhci/sdhci_fsl_acpi.c b/sys/dev/sdhci/sdhci_fsl_acpi.c new file mode 100644 index 000000000..b9abfdfa9 --- /dev/null +++ b/sys/dev/sdhci/sdhci_fsl_acpi.c @@ -0,0 +1,124 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * ACPI front-end for the NXP QorIQ Layerscape eSDHC controller (_HID NXP0003). + * + * Under UEFI/ACPI the eSDHC is described in the DSDT (MMIO + IRQ in _CRS, base + * clock and bus properties in _DSD) but there is no FDT clock framework. This + * binding reads the firmware-provided properties and hands off to the shared + * sdhci_fsl_attach_common(), letting the OS access the SD/TF card (e.g. to + * rewrite the boot firmware) without booting under u-boot/FDT. + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/taskqueue.h> + +#include <machine/bus.h> +#include <machine/resource.h> + +#include <dev/mmc/bridge.h> +#include <dev/mmc/mmcbrvar.h> +#include <dev/mmc/mmc_helpers.h> + +#include <contrib/dev/acpica/include/acpi.h> +#include <contrib/dev/acpica/include/accommon.h> +#include <dev/acpica/acpivar.h> + +#include <dev/sdhci/sdhci.h> +#include <dev/sdhci/sdhci_fsl.h> + +#include "mmcbr_if.h" +#include "sdhci_if.h" + +#include "opt_mmccam.h" + +static char *sdhci_fsl_acpi_hids[] = { + "NXP0003", + NULL +}; + +static int +sdhci_fsl_acpi_probe(device_t dev) +{ + int err; + + err = ACPI_ID_PROBE(device_get_parent(dev), dev, sdhci_fsl_acpi_hids, + NULL); + if (err <= 0) { + device_set_desc(dev, + "NXP QorIQ Layerscape eSDHC controller"); + return (err); + } + + return (ENXIO); +} + +static int +sdhci_fsl_acpi_attach(device_t dev) +{ + struct sdhci_fsl_fdt_softc *sc; + struct mmc_helper mmc_helper; + uint32_t clk_hz; + + sc = device_get_softc(dev); + + /* + * NXP0003 identifies the LX2160A-class eSDHC; use its SoC data for the + * quirks/errata and the base-clock divider. + */ + sc->soc_data = &sdhci_fsl_fdt_lx2160a_soc_data; + sc->acpi = true; + sc->little_endian = true; /* _DSD "little-endian"; LX2160A eSDHC */ + + /* Base (peripheral) clock from the _DSD "clock-frequency" property. */ + clk_hz = 0; + if (device_get_property(dev, "clock-frequency", &clk_hz, sizeof(clk_hz), + DEVICE_PROP_UINT32) <= 0 || clk_hz == 0) { + device_printf(dev, "missing 'clock-frequency' property\n"); + return (ENXIO); + } + sc->baseclk_hz = clk_hz / sc->soc_data->baseclk_div; + + /* mmc host properties (bus-width, max-frequency, ...) from _DSD. */ + memset(&mmc_helper, 0, sizeof(mmc_helper)); + if (mmc_parse(dev, &mmc_helper, &sc->slot.host) != 0) + return (ENXIO); + + /* + * Cap to high speed (25 MHz). UHS modes (SDR50/SDR104) require a tuning + * sequence with voltage switching that is not yet wired up on the ACPI + * front-end, so restrict to a non-tuned speed for reliable enumeration. + */ + if (sc->slot.host.f_max > 25000000) + sc->slot.host.f_max = 25000000; + + return (sdhci_fsl_attach_common(dev)); +} + +static const device_method_t sdhci_fsl_acpi_methods[] = { + DEVMETHOD(device_probe, sdhci_fsl_acpi_probe), + DEVMETHOD(device_attach, sdhci_fsl_acpi_attach), + DEVMETHOD(device_detach, sdhci_fsl_detach), + + /* + * No FDT GPIO line under ACPI; use the controller's own card-detect + * (present-state register) instead. + */ + DEVMETHOD(sdhci_get_card_present, sdhci_generic_get_card_present), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(sdhci_fsl, sdhci_fsl_acpi_driver, sdhci_fsl_acpi_methods, + sizeof(struct sdhci_fsl_fdt_softc), sdhci_fsl_driver); + +DRIVER_MODULE(sdhci_fsl_acpi, acpi, sdhci_fsl_acpi_driver, NULL, NULL); +SDHCI_DEPEND(sdhci_fsl_acpi); + +#ifndef MMCCAM +MMC_DECLARE_BRIDGE(sdhci_fsl_acpi); +#endif diff --git a/sys/dev/sdhci/sdhci_fsl_fdt.c b/sys/dev/sdhci/sdhci_fsl_fdt.c index 185b53a6d..185c31e50 100644 --- a/sys/dev/sdhci/sdhci_fsl_fdt.c +++ b/sys/dev/sdhci/sdhci_fsl_fdt.c @@ -48,6 +48,7 @@ #include <dev/ofw/ofw_bus_subr.h> #include <dev/sdhci/sdhci.h> #include <dev/sdhci/sdhci_fdt_gpio.h> +#include <dev/sdhci/sdhci_fsl.h> #include "mmcbr_if.h" #include "sdhci_if.h" @@ -183,34 +184,10 @@ #define SDHCI_FSL_MAX_RETRIES 20000 /* DELAY(10) * this = 200ms */ -struct sdhci_fsl_fdt_softc { - device_t dev; - const struct sdhci_fsl_fdt_soc_data *soc_data; - struct resource *mem_res; - struct resource *irq_res; - void *irq_cookie; - uint32_t baseclk_hz; - uint32_t maxclk_hz; - struct sdhci_fdt_gpio *gpio; - struct sdhci_slot slot; - bool slot_init_done; - uint32_t cmd_and_mode; - uint16_t sdclk_bits; - struct mmc_helper fdt_helper; - uint32_t div_ratio; - uint8_t vendor_ver; - uint32_t flags; - - uint32_t (* read)(struct sdhci_fsl_fdt_softc *, bus_size_t); - void (* write)(struct sdhci_fsl_fdt_softc *, bus_size_t, uint32_t); -}; - -struct sdhci_fsl_fdt_soc_data { - int quirks; - int baseclk_div; - uint8_t errata; - char *syscon_compat; -}; +/* + * struct sdhci_fsl_fdt_softc and struct sdhci_fsl_fdt_soc_data now live in + * <dev/sdhci/sdhci_fsl.h> so they can be shared with the ACPI front-end. + */ static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_ls1012a_soc_data = { .quirks = 0, @@ -234,7 +211,8 @@ static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_ls1046a_soc_data = { .syscon_compat = "fsl,ls1046a-scfg", }; -static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_lx2160a_soc_data = { +/* Shared with the ACPI front-end (declared in sdhci_fsl.h). */ +const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_lx2160a_soc_data = { .quirks = 0, .baseclk_div = 2, .errata = SDHCI_FSL_UNRELIABLE_PULSE_DET | @@ -755,6 +733,9 @@ sdhci_fsl_fdt_get_ro(device_t bus, device_t child) struct sdhci_fsl_fdt_softc *sc; sc = device_get_softc(bus); + /* No FDT write-protect GPIO (e.g. ACPI front-end): use the std reg. */ + if (sc->gpio == NULL) + return (sdhci_generic_get_ro(bus, child)); return (sdhci_fdt_gpio_get_readonly(sc->gpio)); } @@ -803,8 +784,12 @@ sdhci_fsl_fdt_vddrange_to_mask(device_t dev, uint32_t *vdd_ranges, int len) return (vdd_mask); } +/* + * Apply the FDT "voltage-ranges" property over the capabilities that + * sdhci_fsl_attach_common() already read from hardware. FDT front-end only. + */ static void -sdhci_fsl_fdt_of_parse(device_t dev) +sdhci_fsl_fdt_voltage_fixup(device_t dev) { struct sdhci_fsl_fdt_softc *sc; phandle_t node; @@ -815,16 +800,6 @@ sdhci_fsl_fdt_of_parse(device_t dev) sc = device_get_softc(dev); node = ofw_bus_get_node(dev); - /* Call mmc_fdt_parse in order to get mmc related properties. */ - mmc_fdt_parse(dev, node, &sc->fdt_helper, &sc->slot.host); - - sc->slot.quirks |= SDHCI_QUIRK_MISSING_CAPS; - sc->slot.caps = sdhci_fsl_fdt_read_4(dev, &sc->slot, - SDHCI_CAPABILITIES) & ~(SDHCI_CAN_DO_SUSPEND); - sc->slot.caps2 = sdhci_fsl_fdt_read_4(dev, &sc->slot, - SDHCI_CAPABILITIES2); - - /* Parse the "voltage-ranges" dts property. */ num_ranges = OF_getencprop_alloc(node, "voltage-ranges", (void **) &voltage_ranges); if (num_ranges <= 0) @@ -859,39 +834,37 @@ sdhci_fsl_poll_register(struct sdhci_fsl_fdt_softc *sc, return (0); } -static int -sdhci_fsl_fdt_attach(device_t dev) +/* + * Bus-agnostic attach. The bus front-end must have filled soc_data, + * baseclk_hz, little_endian, acpi and the parsed mmc host properties + * (slot.host) in the softc before calling this. + */ +int +sdhci_fsl_attach_common(device_t dev) { struct sdhci_fsl_fdt_softc *sc; struct mmc_host *host; uint32_t val, buf_order; - uintptr_t ocd_data; - uint64_t clk_hz; - phandle_t node; int rid, ret; - clk_t clk; - node = ofw_bus_get_node(dev); sc = device_get_softc(dev); - ocd_data = ofw_bus_search_compatible(dev, - sdhci_fsl_fdt_compat_data)->ocd_data; sc->dev = dev; sc->flags = 0; host = &sc->slot.host; - rid = 0; - - /* - * LX2160A needs its own soc_data in order to apply SoC - * specific quriks. Since the controller is identified - * only with a generic compatible string we need to do this dance here. - */ - if (ofw_bus_node_is_compatible(OF_finddevice("/"), "fsl,lx2160a")) - sc->soc_data = &sdhci_fsl_fdt_lx2160a_soc_data; - else - sc->soc_data = (struct sdhci_fsl_fdt_soc_data *)ocd_data; - sc->slot.quirks = sc->soc_data->quirks; + /* eSDHC block endianness; chosen by the bus front-end. */ + if (sc->little_endian) { + sc->read = read_le; + sc->write = write_le; + buf_order = SDHCI_FSL_PROT_CTRL_BYTE_NATIVE; + } else { + sc->read = read_be; + sc->write = write_be; + buf_order = SDHCI_FSL_PROT_CTRL_BYTE_SWAP; + } + + rid = 0; sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem_res == NULL) { @@ -917,36 +890,20 @@ sdhci_fsl_fdt_attach(device_t dev) goto err_free_irq_res; } - ret = clk_get_by_ofw_index(dev, node, 0, &clk); - if (ret != 0) { - device_printf(dev, "Parent clock not found\n"); - goto err_free_irq; - } - - ret = clk_get_freq(clk, &clk_hz); - if (ret != 0) { - device_printf(dev, - "Could not get parent clock frequency\n"); - goto err_free_irq; - } - - sc->baseclk_hz = clk_hz / sc->soc_data->baseclk_div; - - /* Figure out eSDHC block endianness before we touch any HW regs. */ - if (OF_hasprop(node, "little-endian")) { - sc->read = read_le; - sc->write = write_le; - buf_order = SDHCI_FSL_PROT_CTRL_BYTE_NATIVE; - } else { - sc->read = read_be; - sc->write = write_be; - buf_order = SDHCI_FSL_PROT_CTRL_BYTE_SWAP; - } - sc->vendor_ver = (RD4(sc, SDHCI_FSL_HOST_VERSION) & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; - sdhci_fsl_fdt_of_parse(dev); + /* Capabilities are missing from the standard registers; read them. */ + sc->slot.quirks |= SDHCI_QUIRK_MISSING_CAPS; + sc->slot.caps = sdhci_fsl_fdt_read_4(dev, &sc->slot, + SDHCI_CAPABILITIES) & ~(SDHCI_CAN_DO_SUSPEND); + sc->slot.caps2 = sdhci_fsl_fdt_read_4(dev, &sc->slot, + SDHCI_CAPABILITIES2); + + /* FDT may override the voltage caps via "voltage-ranges". */ + if (!sc->acpi) + sdhci_fsl_fdt_voltage_fixup(dev); + sc->maxclk_hz = host->f_max ? host->f_max : sc->baseclk_hz; /* @@ -971,7 +928,10 @@ sdhci_fsl_fdt_attach(device_t dev) val = RD4(sc, SDHCI_FSL_ESDHC_CTRL); WR4(sc, SDHCI_FSL_ESDHC_CTRL, val | SDHCI_FSL_ESDHC_CTRL_CLK_DIV2); sc->slot.max_clk = sc->maxclk_hz; - sc->gpio = sdhci_fdt_gpio_setup(dev, &sc->slot); + + /* GPIO card-detect is an FDT-only facility. */ + if (!sc->acpi) + sc->gpio = sdhci_fdt_gpio_setup(dev, &sc->slot); /* * Set the buffer watermark level to 128 words (512 bytes) for both @@ -992,6 +952,16 @@ sdhci_fsl_fdt_attach(device_t dev) ret = sdhci_init_slot(dev, &sc->slot, 0); if (ret != 0) goto err_free_gpio; + + /* + * The eSDHC's SDHCI present-state card-detect bits are unreliable; the + * FDT path works around this with a GPIO card-detect line, which is not + * available under ACPI. Mark the slot non-removable so sdhci attaches + * the mmc bus without waiting for a (never-stable) card-detect. + */ + if (sc->acpi) + sc->slot.opt |= SDHCI_NON_REMOVABLE; + sc->slot_init_done = true; sdhci_start_slot(&sc->slot); @@ -999,8 +969,8 @@ sdhci_fsl_fdt_attach(device_t dev) return (0); err_free_gpio: - sdhci_fdt_gpio_teardown(sc->gpio); -err_free_irq: + if (sc->gpio != NULL) + sdhci_fdt_gpio_teardown(sc->gpio); bus_teardown_intr(dev, sc->irq_res, sc->irq_cookie); err_free_irq_res: bus_free_resource(dev, SYS_RES_IRQ, sc->irq_res); @@ -1010,7 +980,54 @@ sdhci_fsl_fdt_attach(device_t dev) } static int -sdhci_fsl_fdt_detach(device_t dev) +sdhci_fsl_fdt_attach(device_t dev) +{ + struct sdhci_fsl_fdt_softc *sc; + uintptr_t ocd_data; + uint64_t clk_hz; + phandle_t node; + clk_t clk; + int ret; + + node = ofw_bus_get_node(dev); + sc = device_get_softc(dev); + ocd_data = ofw_bus_search_compatible(dev, + sdhci_fsl_fdt_compat_data)->ocd_data; + + /* + * LX2160A needs its own soc_data in order to apply SoC + * specific quriks. Since the controller is identified + * only with a generic compatible string we need to do this dance here. + */ + if (ofw_bus_node_is_compatible(OF_finddevice("/"), "fsl,lx2160a")) + sc->soc_data = &sdhci_fsl_fdt_lx2160a_soc_data; + else + sc->soc_data = (const struct sdhci_fsl_fdt_soc_data *)ocd_data; + + /* Parent (peripheral) clock comes from the FDT clock framework. */ + ret = clk_get_by_ofw_index(dev, node, 0, &clk); + if (ret != 0) { + device_printf(dev, "Parent clock not found\n"); + return (ret); + } + ret = clk_get_freq(clk, &clk_hz); + if (ret != 0) { + device_printf(dev, "Could not get parent clock frequency\n"); + return (ret); + } + sc->baseclk_hz = clk_hz / sc->soc_data->baseclk_div; + + sc->little_endian = OF_hasprop(node, "little-endian"); + sc->acpi = false; + + /* mmc host properties from the FDT node. */ + mmc_fdt_parse(dev, node, &sc->fdt_helper, &sc->slot.host); + + return (sdhci_fsl_attach_common(dev)); +} + +int +sdhci_fsl_detach(device_t dev) { struct sdhci_fsl_fdt_softc *sc; @@ -1514,12 +1531,11 @@ sdhci_fsl_fdt_set_uhs_timing(device_t dev, struct sdhci_slot *slot) } } -static const device_method_t sdhci_fsl_fdt_methods[] = { - /* Device interface. */ - DEVMETHOD(device_probe, sdhci_fsl_fdt_probe), - DEVMETHOD(device_attach, sdhci_fsl_fdt_attach), - DEVMETHOD(device_detach, sdhci_fsl_fdt_detach), - +/* + * Bus-agnostic base class. The FDT and ACPI front-ends subclass this and + * override only device_probe/attach/detach (and, for ACPI, card-present). + */ +static const device_method_t sdhci_fsl_methods[] = { /* Bus interface. */ DEVMETHOD(bus_read_ivar, sdhci_fsl_fdt_read_ivar), DEVMETHOD(bus_write_ivar, sdhci_fsl_fdt_write_ivar), @@ -1549,12 +1565,29 @@ static const device_method_t sdhci_fsl_fdt_methods[] = { DEVMETHOD_END }; -static driver_t sdhci_fsl_fdt_driver = { - "sdhci_fsl_fdt", - sdhci_fsl_fdt_methods, - sizeof(struct sdhci_fsl_fdt_softc), +DEFINE_CLASS_0(sdhci_fsl, sdhci_fsl_driver, sdhci_fsl_methods, + sizeof(struct sdhci_fsl_fdt_softc)); + +/* + * Attach the mmc bus to the shared "sdhci_fsl" devclass (both the FDT and ACPI + * subclasses create devices of this class). Without this the controller + * attaches but the mmc bridge never does, so no card enumerates. + */ +#ifndef MMCCAM +MMC_DECLARE_BRIDGE(sdhci_fsl); +#endif + +/* FDT front-end. */ +static const device_method_t sdhci_fsl_fdt_methods[] = { + DEVMETHOD(device_probe, sdhci_fsl_fdt_probe), + DEVMETHOD(device_attach, sdhci_fsl_fdt_attach), + DEVMETHOD(device_detach, sdhci_fsl_detach), + DEVMETHOD_END }; +DEFINE_CLASS_1(sdhci_fsl, sdhci_fsl_fdt_driver, sdhci_fsl_fdt_methods, + sizeof(struct sdhci_fsl_fdt_softc), sdhci_fsl_driver); + DRIVER_MODULE(sdhci_fsl_fdt, simplebus, sdhci_fsl_fdt_driver, NULL, NULL); SDHCI_DEPEND(sdhci_fsl_fdt); -- 2.39.3 (Apple Git-146)