Page MenuHomeFreeBSD

D26153.id.diff
No OneTemporary

D26153.id.diff

Index: head/sys/conf/files
===================================================================
--- head/sys/conf/files
+++ head/sys/conf/files
@@ -3058,6 +3058,7 @@
dev/sdhci/sdhci.c optional sdhci
dev/sdhci/sdhci_fdt.c optional sdhci fdt
dev/sdhci/sdhci_fdt_gpio.c optional sdhci fdt gpio
+dev/sdhci/sdhci_fsl_fdt.c optional sdhci fdt gpio
dev/sdhci/sdhci_if.m optional sdhci
dev/sdhci/sdhci_acpi.c optional sdhci acpi
dev/sdhci/sdhci_pci.c optional sdhci pci
Index: head/sys/dev/sdhci/sdhci_fsl_fdt.c
===================================================================
--- head/sys/dev/sdhci/sdhci_fsl_fdt.c
+++ head/sys/dev/sdhci/sdhci_fsl_fdt.c
@@ -0,0 +1,680 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Alstom Group.
+ * Copyright (c) 2020 Semihalf.
+ *
+ * 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.
+ */
+
+/* eSDHC controller driver for NXP QorIQ Layerscape SoCs. */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcbrvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/sdhci/sdhci.h>
+#include <dev/sdhci/sdhci_fdt_gpio.h>
+
+#include "mmcbr_if.h"
+#include "sdhci_if.h"
+
+#define RD4 (sc->read)
+#define WR4 (sc->write)
+
+#define SDHCI_FSL_PRES_STATE 0x24
+#define SDHCI_FSL_PRES_SDSTB (1 << 3)
+#define SDHCI_FSL_PRES_COMPAT_MASK 0x000f0f07
+
+#define SDHCI_FSL_PROT_CTRL 0x28
+#define SDHCI_FSL_PROT_CTRL_WIDTH_1BIT (0 << 1)
+#define SDHCI_FSL_PROT_CTRL_WIDTH_4BIT (1 << 1)
+#define SDHCI_FSL_PROT_CTRL_WIDTH_8BIT (2 << 1)
+#define SDHCI_FSL_PROT_CTRL_WIDTH_MASK (3 << 1)
+#define SDHCI_FSL_PROT_CTRL_BYTE_SWAP (0 << 4)
+#define SDHCI_FSL_PROT_CTRL_BYTE_NATIVE (2 << 4)
+#define SDHCI_FSL_PROT_CTRL_BYTE_MASK (3 << 4)
+#define SDHCI_FSL_PROT_CTRL_DMA_MASK (3 << 8)
+
+#define SDHCI_FSL_SYS_CTRL 0x2c
+#define SDHCI_FSL_CLK_IPGEN (1 << 0)
+#define SDHCI_FSL_CLK_SDCLKEN (1 << 3)
+#define SDHCI_FSL_CLK_DIVIDER_MASK 0x000000f0
+#define SDHCI_FSL_CLK_DIVIDER_SHIFT 4
+#define SDHCI_FSL_CLK_PRESCALE_MASK 0x0000ff00
+#define SDHCI_FSL_CLK_PRESCALE_SHIFT 8
+
+#define SDHCI_FSL_WTMK_LVL 0x44
+#define SDHCI_FSL_WTMK_RD_512B (0 << 0)
+#define SDHCI_FSL_WTMK_WR_512B (0 << 15)
+
+#define SDHCI_FSL_HOST_VERSION 0xfc
+#define SDHCI_FSL_CAPABILITIES2 0x114
+
+#define SDHCI_FSL_ESDHC_CTRL 0x40c
+#define SDHCI_FSL_ESDHC_CTRL_SNOOP (1 << 6)
+#define SDHCI_FSL_ESDHC_CTRL_CLK_DIV2 (1 << 19)
+
+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;
+ struct sdhci_fdt_gpio *gpio;
+ struct sdhci_slot slot;
+ bool slot_init_done;
+ uint32_t cmd_and_mode;
+ uint16_t sdclk_bits;
+
+ 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;
+};
+
+static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_ls1046a_soc_data = {
+ .quirks = SDHCI_QUIRK_DONT_SET_HISPD_BIT | SDHCI_QUIRK_BROKEN_AUTO_STOP
+};
+
+static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_gen_data = {
+ .quirks = 0,
+};
+
+static const struct ofw_compat_data sdhci_fsl_fdt_compat_data[] = {
+ {"fsl,ls1046a-esdhc", (uintptr_t)&sdhci_fsl_fdt_ls1046a_soc_data},
+ {"fsl,esdhc", (uintptr_t)&sdhci_fsl_fdt_gen_data},
+ {NULL, 0}
+};
+
+static uint32_t
+read_be(struct sdhci_fsl_fdt_softc *sc, bus_size_t off)
+{
+
+ return (be32toh(bus_read_4(sc->mem_res, off)));
+}
+
+static void
+write_be(struct sdhci_fsl_fdt_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_write_4(sc->mem_res, off, htobe32(val));
+}
+
+static uint32_t
+read_le(struct sdhci_fsl_fdt_softc *sc, bus_size_t off)
+{
+
+ return (bus_read_4(sc->mem_res, off));
+}
+
+static void
+write_le(struct sdhci_fsl_fdt_softc *sc, bus_size_t off, uint32_t val)
+{
+
+ bus_write_4(sc->mem_res, off, val);
+}
+
+
+static uint16_t
+sdhci_fsl_fdt_get_clock(struct sdhci_fsl_fdt_softc *sc)
+{
+ uint16_t val;
+
+ val = sc->sdclk_bits | SDHCI_CLOCK_INT_EN;
+ if (RD4(sc, SDHCI_FSL_PRES_STATE) & SDHCI_FSL_PRES_SDSTB)
+ val |= SDHCI_CLOCK_INT_STABLE;
+ if (RD4(sc, SDHCI_FSL_SYS_CTRL) & SDHCI_FSL_CLK_SDCLKEN)
+ val |= SDHCI_CLOCK_CARD_EN;
+
+ return (val);
+}
+
+static void
+fsl_sdhc_fdt_set_clock(struct sdhci_fsl_fdt_softc *sc, uint16_t val)
+{
+ uint32_t div, freq, prescale, val32;
+
+ sc->sdclk_bits = val & SDHCI_DIVIDERS_MASK;
+ val32 = RD4(sc, SDHCI_CLOCK_CONTROL);
+
+ if ((val & SDHCI_CLOCK_CARD_EN) == 0) {
+ WR4(sc, SDHCI_CLOCK_CONTROL, val32 & ~SDHCI_FSL_CLK_SDCLKEN);
+ return;
+ }
+
+ div = ((val >> SDHCI_DIVIDER_SHIFT) & SDHCI_DIVIDER_MASK) |
+ ((val >> SDHCI_DIVIDER_HI_SHIFT) & SDHCI_DIVIDER_HI_MASK) <<
+ SDHCI_DIVIDER_MASK_LEN;
+ if (div == 0)
+ freq = sc->baseclk_hz;
+ else
+ freq = sc->baseclk_hz / (2 * div);
+
+ for (prescale = 2; freq < sc->baseclk_hz / (prescale * 16); )
+ prescale <<= 1;
+ for (div = 1; freq < sc->baseclk_hz / (prescale * div); )
+ ++div;
+
+#ifdef DEBUG
+ device_printf(sc->dev,
+ "Desired SD/MMC freq: %d, actual: %d; base %d prescale %d divisor %d\n",
+ freq, sc->baseclk_hz / (prescale * div),
+ sc->baseclk_hz, prescale, div);
+#endif
+
+ prescale >>= 1;
+ div -= 1;
+
+ val32 &= ~(SDHCI_FSL_CLK_DIVIDER_MASK | SDHCI_FSL_CLK_PRESCALE_MASK);
+ val32 |= div << SDHCI_FSL_CLK_DIVIDER_SHIFT;
+ val32 |= prescale << SDHCI_FSL_CLK_PRESCALE_SHIFT;
+ val32 |= SDHCI_FSL_CLK_IPGEN | SDHCI_FSL_CLK_SDCLKEN;
+ WR4(sc, SDHCI_CLOCK_CONTROL, val32);
+}
+
+static uint8_t
+sdhci_fsl_fdt_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct sdhci_fsl_fdt_softc *sc;
+ uint32_t wrk32, val32;
+
+ sc = device_get_softc(dev);
+
+ switch (off) {
+ case SDHCI_HOST_CONTROL:
+ wrk32 = RD4(sc, SDHCI_FSL_PROT_CTRL);
+ val32 = wrk32 & (SDHCI_CTRL_LED | SDHCI_CTRL_CARD_DET |
+ SDHCI_CTRL_FORCE_CARD);
+ if (wrk32 & SDHCI_FSL_PROT_CTRL_WIDTH_4BIT)
+ val32 |= SDHCI_CTRL_4BITBUS;
+ else if (wrk32 & SDHCI_FSL_PROT_CTRL_WIDTH_8BIT)
+ val32 |= SDHCI_CTRL_8BITBUS;
+ return (val32);
+ case SDHCI_POWER_CONTROL:
+ return (SDHCI_POWER_ON | SDHCI_POWER_300);
+ default:
+ break;
+ }
+
+ return ((RD4(sc, off & ~3) >> (off & 3) * 8) & UINT8_MAX);
+}
+
+static uint16_t
+sdhci_fsl_fdt_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct sdhci_fsl_fdt_softc *sc;
+ uint32_t val32;
+
+ sc = device_get_softc(dev);
+
+ switch (off) {
+ case SDHCI_CLOCK_CONTROL:
+ return (sdhci_fsl_fdt_get_clock(sc));
+ case SDHCI_HOST_VERSION:
+ return (RD4(sc, SDHCI_FSL_HOST_VERSION) & UINT16_MAX);
+ case SDHCI_TRANSFER_MODE:
+ return (sc->cmd_and_mode & UINT16_MAX);
+ case SDHCI_COMMAND_FLAGS:
+ return (sc->cmd_and_mode >> 16);
+ case SDHCI_SLOT_INT_STATUS:
+ /*
+ * eSDHC hardware manages only a single slot.
+ * Synthesize a slot interrupt status register for slot 1 below.
+ */
+ val32 = RD4(sc, SDHCI_INT_STATUS);
+ val32 &= RD4(sc, SDHCI_SIGNAL_ENABLE);
+ return (!!val32);
+ default:
+ return ((RD4(sc, off & ~3) >> (off & 3) * 8) & UINT16_MAX);
+ }
+}
+
+static uint32_t
+sdhci_fsl_fdt_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+ struct sdhci_fsl_fdt_softc *sc;
+ uint32_t wrk32, val32;
+
+ sc = device_get_softc(dev);
+
+ if (off == SDHCI_BUFFER)
+ return (bus_read_4(sc->mem_res, off));
+ if (off == SDHCI_CAPABILITIES2)
+ off = SDHCI_FSL_CAPABILITIES2;
+
+ val32 = RD4(sc, off);
+
+ switch (off) {
+ case SDHCI_CAPABILITIES:
+ val32 &= ~(SDHCI_CAN_DO_SUSPEND | SDHCI_CAN_VDD_180);
+ break;
+ case SDHCI_PRESENT_STATE:
+ wrk32 = val32;
+ val32 &= SDHCI_FSL_PRES_COMPAT_MASK;
+ val32 |= (wrk32 >> 4) & SDHCI_STATE_DAT_MASK;
+ val32 |= (wrk32 << 1) & SDHCI_STATE_CMD;
+ break;
+ default:
+ break;
+ }
+
+ return (val32);
+}
+
+static void
+sdhci_fsl_fdt_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint32_t *data, bus_size_t count)
+{
+ struct sdhci_fsl_fdt_softc *sc;
+
+ sc = device_get_softc(dev);
+ bus_read_multi_4(sc->mem_res, off, data, count);
+}
+
+static void
+sdhci_fsl_fdt_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint8_t val)
+{
+ struct sdhci_fsl_fdt_softc *sc;
+ uint32_t val32;
+
+ sc = device_get_softc(dev);
+
+ switch (off) {
+ case SDHCI_HOST_CONTROL:
+ val32 = RD4(sc, SDHCI_FSL_PROT_CTRL);
+ val32 &= ~SDHCI_FSL_PROT_CTRL_WIDTH_MASK;
+ val32 |= (val & SDHCI_CTRL_LED);
+
+ if (val & SDHCI_CTRL_8BITBUS)
+ val32 |= SDHCI_FSL_PROT_CTRL_WIDTH_8BIT;
+ else
+ /* Bus width is 1-bit when this flag is not set. */
+ val32 |= (val & SDHCI_CTRL_4BITBUS);
+ /* Enable SDMA by masking out this field. */
+ val32 &= ~SDHCI_FSL_PROT_CTRL_DMA_MASK;
+ val32 &= ~(SDHCI_CTRL_CARD_DET | SDHCI_CTRL_FORCE_CARD);
+ val32 |= (val & (SDHCI_CTRL_CARD_DET |
+ SDHCI_CTRL_FORCE_CARD));
+ WR4(sc, SDHCI_FSL_PROT_CTRL, val32);
+ return;
+ case SDHCI_POWER_CONTROL:
+ return;
+ case SDHCI_SOFTWARE_RESET:
+ val &= ~SDHCI_RESET_ALL;
+ /* FALLTHROUGH. */
+ default:
+ val32 = RD4(sc, off & ~3);
+ val32 &= ~(UINT8_MAX << (off & 3) * 8);
+ val32 |= (val << (off & 3) * 8);
+ WR4(sc, off & ~3, val32);
+ return;
+ }
+}
+
+static void
+sdhci_fsl_fdt_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint16_t val)
+{
+ struct sdhci_fsl_fdt_softc *sc;
+ uint32_t val32;
+
+ sc = device_get_softc(dev);
+
+ switch (off) {
+ case SDHCI_CLOCK_CONTROL:
+ fsl_sdhc_fdt_set_clock(sc, val);
+ return;
+ /*
+ * eSDHC hardware combines command and mode into a single
+ * register. Cache it here, so that command isn't written
+ * until after mode.
+ */
+ case SDHCI_TRANSFER_MODE:
+ sc->cmd_and_mode = val;
+ return;
+ case SDHCI_COMMAND_FLAGS:
+ sc->cmd_and_mode =
+ (sc->cmd_and_mode & UINT16_MAX) | (val << 16);
+ WR4(sc, SDHCI_TRANSFER_MODE, sc->cmd_and_mode);
+ sc->cmd_and_mode = 0;
+ return;
+ default:
+ val32 = RD4(sc, off & ~3);
+ val32 &= ~(UINT16_MAX << (off & 3) * 8);
+ val32 |= ((val & UINT16_MAX) << (off & 3) * 8);
+ WR4(sc, off & ~3, val32);
+ return;
+ }
+}
+
+static void
+sdhci_fsl_fdt_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
+ uint32_t val)
+{
+ struct sdhci_fsl_fdt_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ switch (off) {
+ case SDHCI_BUFFER:
+ bus_write_4(sc->mem_res, off, val);
+ return;
+ /*
+ * eSDHC hardware lacks support for the SDMA buffer boundary
+ * feature and instead generates SDHCI_INT_DMA_END interrupts
+ * after each completed DMA data transfer.
+ * Since this duplicates the SDHCI_INT_DATA_END functionality,
+ * mask out the unneeded SDHCI_INT_DMA_END interrupt.
+ */
+ case SDHCI_INT_ENABLE:
+ case SDHCI_SIGNAL_ENABLE:
+ val &= ~SDHCI_INT_DMA_END;
+ /* FALLTHROUGH. */
+ default:
+ WR4(sc, off, val);
+ return;
+ }
+}
+
+static void
+sdhci_fsl_fdt_write_multi_4(device_t dev, struct sdhci_slot *slot,
+ bus_size_t off, uint32_t *data, bus_size_t count)
+{
+ struct sdhci_fsl_fdt_softc *sc;
+
+ sc = device_get_softc(dev);
+ bus_write_multi_4(sc->mem_res, off, data, count);
+}
+
+static void
+sdhci_fsl_fdt_irq(void *arg)
+{
+ struct sdhci_fsl_fdt_softc *sc;
+
+ sc = arg;
+ sdhci_generic_intr(&sc->slot);
+ return;
+}
+
+static int
+sdhci_fsl_fdt_get_ro(device_t bus, device_t child)
+{
+ struct sdhci_fsl_fdt_softc *sc;
+
+ sc = device_get_softc(bus);
+ return (sdhci_fdt_gpio_get_readonly(sc->gpio));
+}
+
+static bool
+sdhci_fsl_fdt_get_card_present(device_t dev, struct sdhci_slot *slot)
+{
+ struct sdhci_fsl_fdt_softc *sc;
+
+ sc = device_get_softc(dev);
+ return (sdhci_fdt_gpio_get_present(sc->gpio));
+}
+
+static int
+sdhci_fsl_fdt_attach(device_t dev)
+{
+ struct sdhci_fsl_fdt_softc *sc;
+ 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->soc_data = (struct sdhci_fsl_fdt_soc_data *)ocd_data;
+ sc->dev = dev;
+ sc->slot.quirks = sc->soc_data->quirks;
+
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev,
+ "Could not allocate resources for controller\n");
+ return (ENOMEM);
+ }
+
+ rid = 0;
+ sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->irq_res == NULL) {
+ device_printf(dev,
+ "Could not allocate irq resources for controller\n");
+ ret = ENOMEM;
+ goto err_free_mem;
+ }
+
+ ret = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, sdhci_fsl_fdt_irq, sc, &sc->irq_cookie);
+ if (ret != 0) {
+ device_printf(dev, "Could not setup IRQ handler\n");
+ 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 / 2;
+
+ /* 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;
+ }
+
+ /*
+ * Setting this register affects byte order in SDHCI_BUFFER only.
+ * If the eSDHC block is connected over a big-endian bus, the data
+ * read from/written to the buffer will be already byte swapped.
+ * In such a case, setting SDHCI_FSL_PROT_CTRL_BYTE_SWAP will convert
+ * the byte order again, resulting in a native byte order.
+ * The read/write callbacks accommodate for this behavior.
+ */
+ val = RD4(sc, SDHCI_FSL_PROT_CTRL);
+ val &= ~SDHCI_FSL_PROT_CTRL_BYTE_MASK;
+ WR4(sc, SDHCI_FSL_PROT_CTRL, val | buf_order);
+
+ /*
+ * Gate the SD clock and set its source to peripheral clock / 2.
+ * The frequency in baseclk_hz is set to match this.
+ */
+ val = RD4(sc, SDHCI_CLOCK_CONTROL);
+ WR4(sc, SDHCI_CLOCK_CONTROL, val & ~SDHCI_FSL_CLK_SDCLKEN);
+ 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->baseclk_hz;
+ sc->gpio = sdhci_fdt_gpio_setup(dev, &sc->slot);
+
+ /*
+ * Set the buffer watermark level to 128 words (512 bytes) for both
+ * read and write. The hardware has a restriction that when the read or
+ * write ready status is asserted, that means you can read exactly the
+ * number of words set in the watermark register before you have to
+ * re-check the status and potentially wait for more data. The main
+ * sdhci driver provides no hook for doing status checking on less than
+ * a full block boundary, so we set the watermark level to be a full
+ * block. Reads and writes where the block size is less than the
+ * watermark size will work correctly too, no need to change the
+ * watermark for different size blocks. However, 128 is the maximum
+ * allowed for the watermark, so PIO is limitted to 512 byte blocks.
+ */
+ WR4(sc, SDHCI_FSL_WTMK_LVL, SDHCI_FSL_WTMK_WR_512B |
+ SDHCI_FSL_WTMK_RD_512B);
+
+ ret = sdhci_init_slot(dev, &sc->slot, 0);
+ if (ret != 0)
+ goto err_free_gpio;
+ sc->slot_init_done = true;
+ sdhci_start_slot(&sc->slot);
+
+ return (bus_generic_attach(dev));
+
+err_free_gpio:
+ sdhci_fdt_gpio_teardown(sc->gpio);
+err_free_irq:
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_cookie);
+err_free_irq_res:
+ bus_free_resource(dev, SYS_RES_IRQ, sc->irq_res);
+err_free_mem:
+ bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
+ return (ret);
+}
+
+static int
+sdhci_fsl_fdt_detach(device_t dev)
+{
+ struct sdhci_fsl_fdt_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (sc->slot_init_done)
+ sdhci_cleanup_slot(&sc->slot);
+ if (sc->gpio != NULL)
+ sdhci_fdt_gpio_teardown(sc->gpio);
+ if (sc->irq_cookie != NULL)
+ bus_teardown_intr(dev, sc->irq_res, sc->irq_cookie);
+ if (sc->irq_res != NULL)
+ bus_free_resource(dev, SYS_RES_IRQ, sc->irq_res);
+ if (sc->mem_res != NULL)
+ bus_free_resource(dev, SYS_RES_MEMORY, sc->mem_res);
+ return (0);
+}
+
+static int
+sdhci_fsl_fdt_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_search_compatible(dev,
+ sdhci_fsl_fdt_compat_data)->ocd_data)
+ return (ENXIO);
+
+ device_set_desc(dev, "NXP QorIQ Layerscape eSDHC controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+sdhci_fsl_fdt_read_ivar(device_t bus, device_t child, int which,
+ uintptr_t *result)
+{
+ struct sdhci_slot *slot = device_get_ivars(child);
+
+ if (which == MMCBR_IVAR_MAX_DATA && (slot->opt & SDHCI_HAVE_DMA)) {
+ /*
+ * In the absence of SDMA buffer boundary functionality,
+ * limit the maximum data length per read/write command
+ * to bounce buffer size.
+ */
+ *result = howmany(slot->sdma_bbufsz, 512);
+ return (0);
+ }
+ return (sdhci_generic_read_ivar(bus, child, which, result));
+}
+
+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 interface. */
+ DEVMETHOD(bus_read_ivar, sdhci_fsl_fdt_read_ivar),
+ DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar),
+
+ /* MMC bridge interface. */
+ DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios),
+ DEVMETHOD(mmcbr_request, sdhci_generic_request),
+ DEVMETHOD(mmcbr_get_ro, sdhci_fsl_fdt_get_ro),
+ DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host),
+ DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host),
+
+ /* SDHCI accessors. */
+ DEVMETHOD(sdhci_read_1, sdhci_fsl_fdt_read_1),
+ DEVMETHOD(sdhci_read_2, sdhci_fsl_fdt_read_2),
+ DEVMETHOD(sdhci_read_4, sdhci_fsl_fdt_read_4),
+ DEVMETHOD(sdhci_read_multi_4, sdhci_fsl_fdt_read_multi_4),
+ DEVMETHOD(sdhci_write_1, sdhci_fsl_fdt_write_1),
+ DEVMETHOD(sdhci_write_2, sdhci_fsl_fdt_write_2),
+ DEVMETHOD(sdhci_write_4, sdhci_fsl_fdt_write_4),
+ DEVMETHOD(sdhci_write_multi_4, sdhci_fsl_fdt_write_multi_4),
+ DEVMETHOD(sdhci_get_card_present, sdhci_fsl_fdt_get_card_present),
+ DEVMETHOD_END
+};
+
+static devclass_t sdhci_fsl_fdt_devclass;
+static driver_t sdhci_fsl_fdt_driver = {
+ "sdhci_fsl_fdt",
+ sdhci_fsl_fdt_methods,
+ sizeof(struct sdhci_fsl_fdt_softc),
+};
+
+DRIVER_MODULE(sdhci_fsl_fdt, simplebus, sdhci_fsl_fdt_driver,
+ sdhci_fsl_fdt_devclass, NULL, NULL);
+SDHCI_DEPEND(sdhci_fsl_fdt);
+
+#ifndef MMCCAM
+MMC_DECLARE_BRIDGE(sdhci_fsl_fdt);
+#endif

File Metadata

Mime Type
text/plain
Expires
Thu, Mar 5, 12:25 AM (5 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29262810
Default Alt Text
D26153.id.diff (19 KB)

Event Timeline