Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -2454,6 +2454,7 @@ dev/mmc/mmcbus_if.m standard dev/mmc/mmcsd.c optional mmcsd !mmccam dev/mmc/mmc_fdt_helpers.c optional mmc fdt | mmccam fdt +dev/mmc/mmc_fdt_pwrseq.c optional mmc fdt | mmccam fdt dev/mmcnull/mmcnull.c optional mmcnull dev/mn/if_mn.c optional mn pci dev/mpr/mpr.c optional mpr Index: sys/dev/mmc/host/dwmmc.c =================================================================== --- sys/dev/mmc/host/dwmmc.c +++ sys/dev/mmc/host/dwmmc.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -832,6 +833,8 @@ device_printf(sc->dev, "Cannot disable vqmmc regulator\n"); #endif + mmc_fdt_free(&sc->mmc_helper); + return (0); } @@ -933,6 +936,23 @@ return (ret); } +static void +dwmmc_update_power_mode(struct dwmmc_softc *sc, enum mmc_power_mode power_mode) +{ + + switch (power_mode) { + case power_off: + mmc_fdt_pwrseq_off(&sc->mmc_helper); + break; + case power_up: + mmc_fdt_pwrseq_up(&sc->mmc_helper); + break; + case power_on: + mmc_fdt_pwrseq_on(&sc->mmc_helper); + break; + }; +} + static int dma_done(struct dwmmc_softc *sc, struct mmc_command *cmd) { @@ -1581,6 +1601,7 @@ ios->power_mode = new_ios->power_mode; if (bootverbose) device_printf(sc->dev, "Power mode => %d\n", ios->power_mode); + dwmmc_update_power_mode(sc, ios->power_mode); } if (cts->ios_valid & MMC_BT) { ios->timing = new_ios->timing; Index: sys/dev/mmc/mmc_fdt_helpers.h =================================================================== --- sys/dev/mmc/mmc_fdt_helpers.h +++ sys/dev/mmc/mmc_fdt_helpers.h @@ -33,6 +33,8 @@ #include #include +#include + #ifdef EXT_RESOURCES #include #endif @@ -58,7 +60,8 @@ #define MMC_PROP_NO_SDIO (1 << 5) #define MMC_PROP_NO_SD (1 << 6) #define MMC_PROP_NO_MMC (1 << 7) - +#define MMC_PROP_SDIO_IRQ (1 << 8) + struct mmc_pwrseq *pwrseq; #ifdef EXT_RESOURCES regulator_t vmmc_supply; regulator_t vqmmc_supply; @@ -68,6 +71,8 @@ typedef void (*mmc_fdt_cd_handler)(device_t dev, bool present); int mmc_fdt_parse(device_t dev, phandle_t node, struct mmc_fdt_helper *helper, struct mmc_host *host); +void mmc_fdt_free(struct mmc_fdt_helper *); + int mmc_fdt_gpio_setup(device_t dev, phandle_t node, struct mmc_fdt_helper *helper, mmc_fdt_cd_handler handler); void mmc_fdt_gpio_teardown(struct mmc_fdt_helper *helper); bool mmc_fdt_gpio_get_present(struct mmc_fdt_helper *helper); Index: sys/dev/mmc/mmc_fdt_helpers.c =================================================================== --- sys/dev/mmc/mmc_fdt_helpers.c +++ sys/dev/mmc/mmc_fdt_helpers.c @@ -135,6 +135,8 @@ helper->props |= MMC_PROP_NO_SD; if (OF_hasprop(node, "no-mmc")) helper->props |= MMC_PROP_NO_MMC; + if (OF_hasprop(node, "cap-sdio-irq")) + helper->props |= MMC_PROP_SDIO_IRQ; if (!(helper->props & MMC_PROP_NO_SD)) mmc_fdt_parse_sd_speed(node, host); @@ -181,9 +183,25 @@ host->caps |= MMC_CAP_SIGNALING_330; #endif + (void) mmc_fdt_parse_pwrseq(dev, node, helper, host); + return (0); } +void +mmc_fdt_free(struct mmc_fdt_helper *helper) +{ + + if (helper == NULL) + return; + + mmc_fdt_free_pwrseq(helper); + mmc_fdt_gpio_teardown(helper); +#ifdef EXT_RESOURCES + /* Cleanup regulators. */ +#endif +} + /* * Card detect interrupt handler. */ Index: sys/dev/mmc/mmc_fdt_pwrseq.h =================================================================== --- /dev/null +++ sys/dev/mmc/mmc_fdt_pwrseq.h @@ -0,0 +1,55 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Bjoern A. Zeeb + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_MMC_MMC_FDT_PWRSEQ_H +#define _DEV_MMC_MMC_FDT_PWRSEQ_H + +#include + +struct mmc_pwrseq { + phandle_t node; + int (*power_off)(struct mmc_pwrseq *); + int (*power_up)(struct mmc_pwrseq *); + int (*power_on)(struct mmc_pwrseq *); + TAILQ_ENTRY(mmc_pwrseq) mmc_pwrseq_elm; +}; + +struct mmc_fdt_helper; +int mmc_fdt_parse_pwrseq(device_t, phandle_t, struct mmc_fdt_helper *, + struct mmc_host *); +void mmc_fdt_free_pwrseq(struct mmc_fdt_helper *); + +int mmc_fdt_pwrseq_enqueue(struct mmc_pwrseq *); +int mmc_fdt_pwrseq_dequeue(struct mmc_pwrseq *); + +int mmc_fdt_pwrseq_off(struct mmc_fdt_helper *); +int mmc_fdt_pwrseq_up(struct mmc_fdt_helper *); +int mmc_fdt_pwrseq_on(struct mmc_fdt_helper *); + +#endif /* _DEV_MMC_MMC_FDT_PWRSEQ_H */ Index: sys/dev/mmc/mmc_fdt_pwrseq.c =================================================================== --- /dev/null +++ sys/dev/mmc/mmc_fdt_pwrseq.c @@ -0,0 +1,181 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Bjoern A. Zeeb + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + + +/* + * I was wondering how the "power sequence" would call things in sequence + * and realised that turning pins on/off needs to happen at different times + * so it is not really a sequence but a collection of a few helper function + * calls which are always present in the mmc (or rather driver) code. + * Given most people have never had a need for the mmc-pwrseq-simple calls + * try to make NOPs by default. That's where my hackery ends and I have to + * write proper code. + */ + +static TAILQ_HEAD(mmc_pwqseq_head, mmc_pwrseq) mmc_pwqseq_q = + TAILQ_HEAD_INITIALIZER(mmc_pwqseq_q); + +#define PWRSEQ_LOCK() +#define PWRSEQ_UNLOCK() + +int +mmc_fdt_parse_pwrseq(device_t dev, phandle_t node, + struct mmc_fdt_helper *helper, struct mmc_host *host) +{ + phandle_t xref_pwrseq, node_pwrseq; + struct mmc_pwrseq *pwrseq; + int rv; + + rv = OF_getencprop(node, "mmc-pwrseq", &xref_pwrseq, + sizeof(xref_pwrseq)); + if (rv < 0) { + if (bootverbose) + device_printf(dev, "No mmc-pwrseq xref: %d.\n", rv); + return (ENXIO); + } + + /* + * XXX-BZ this is a bit unelegant, but I am not sure how to do OF/dev + * chains and attach the specific pwrseq implementation as a child. + * I wonder if we could do this more newbus like calling + * device_probe_attach_child() somehow. Anyway, first proof of concept. + */ + node_pwrseq = OF_node_from_xref(xref_pwrseq); + if (node_pwrseq == xref_pwrseq) { + device_printf(dev, "Cannot find xref for 'mmc-pwrseq' entry\n"); + return (ENXIO); + } + + PWRSEQ_LOCK(); + TAILQ_FOREACH(pwrseq, &mmc_pwqseq_q, mmc_pwrseq_elm) { + if (pwrseq->node == node_pwrseq) { + helper->pwrseq = pwrseq; + device_printf(dev, "Found mmc-pwrseq handler.\n"); + break; + } + } + PWRSEQ_UNLOCK(); + + return (ENXIO); +} + +void +mmc_fdt_free_pwrseq(struct mmc_fdt_helper *helper) +{ + + if (helper == NULL) + return; + + /* XXX for now just nuke the pointer as we don't do newbus or refcnt. */ + if (helper->pwrseq != NULL) + helper->pwrseq = NULL; +} + +/* -------------------------------------------------------------------------- */ + +int +mmc_fdt_pwrseq_enqueue(struct mmc_pwrseq *pwrseq) +{ + + KASSERT(pwrseq != NULL, ("%s: pwrseq is NULL\n", __func__)); + + PWRSEQ_LOCK(); + TAILQ_INSERT_TAIL(&mmc_pwqseq_q, pwrseq, mmc_pwrseq_elm); + PWRSEQ_UNLOCK(); + + return (0); +} + +int +mmc_fdt_pwrseq_dequeue(struct mmc_pwrseq *pwrseq) +{ + + KASSERT(pwrseq != NULL, ("%s: pwrseq is NULL\n", __func__)); + + PWRSEQ_LOCK(); + TAILQ_REMOVE(&mmc_pwqseq_q, pwrseq, mmc_pwrseq_elm); + PWRSEQ_UNLOCK(); + + return (0); +} + +/* -------------------------------------------------------------------------- */ + +int +mmc_fdt_pwrseq_off(struct mmc_fdt_helper *helper) +{ + + if (helper == NULL || helper->pwrseq == NULL) + return (0); + + if (helper->pwrseq->power_off == NULL) + return (0); + + return (helper->pwrseq->power_off(helper->pwrseq)); +} + +int +mmc_fdt_pwrseq_up(struct mmc_fdt_helper *helper) +{ + + if (helper == NULL || helper->pwrseq == NULL) + return (0); + + if (helper->pwrseq->power_up == NULL) + return (0); + + return (helper->pwrseq->power_up(helper->pwrseq)); +} + +int +mmc_fdt_pwrseq_on(struct mmc_fdt_helper *helper) +{ + + if (helper == NULL || helper->pwrseq == NULL) + return (0); + + if (helper->pwrseq->power_on == NULL) + return (0); + + return (helper->pwrseq->power_on(helper->pwrseq)); +} +