Author: mw
Date: Tue Sep  1 16:17:21 2020
New Revision: 365054
URL: https://svnweb.freebsd.org/changeset/base/365054

Log:
  Introduce the SDHCI driver for NXP QorIQ Layerscape SoCs
  
  Implement support for an eSDHC controller found in NXP QorIQ Layerscape SoCs.
  
  This driver has been tested with NXP LS1046A and LX2160A (Honeycomb board),
  which is incompatible with the existing sdhci_fsl driver (aiming at older
  chips from this family). As such, it is not intended as replacement for
  the old driver, but rather serves as an improved alternative for SoCs that
  support it.
  It comes with support for both PIO and Single DMA modes and samples the
  clock from the extres clk API.
  
  Submitted by: Artur Rojek <a...@semihalf.com>
  Reviewed by: manu, mmel, kibab
  Obtained from: Semihalf
  Sponsored by: Alstom Group
  Differential Revision: https://reviews.freebsd.org/D26153

Added:
  head/sys/dev/sdhci/sdhci_fsl_fdt.c   (contents, props changed)
Modified:
  head/sys/conf/files

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files Tue Sep  1 16:13:09 2020        (r365053)
+++ head/sys/conf/files Tue Sep  1 16:17:21 2020        (r365054)
@@ -3058,6 +3058,7 @@ dev/scc/scc_dev_z8530.c           optional scc
 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

Added: head/sys/dev/sdhci/sdhci_fsl_fdt.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/dev/sdhci/sdhci_fsl_fdt.c  Tue Sep  1 16:17:21 2020        
(r365054)
@@ -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
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to