svn commit: r365054 - in head/sys: conf dev/sdhci
Marcin Wojtas
mw at semihalf.com
Wed Sep 2 10:16:02 UTC 2020
wt., 1 wrz 2020 o 22:37 Mateusz Guzik <mjguzik at gmail.com> napisał(a):
> This commit breaks numerous kernels, e.g. _.arm.RPI-B:
>
> In file included from /usr/src/sys/dev/sdhci/sdhci_fsl_fdt.c:45:
> /usr/src/sys/dev/extres/clk/clk.h:37:10: fatal error: 'clknode_if.h'
> file not found
> #include "clknode_if.h"
>
>
Unfortunately yes, fixing it.
> On 9/1/20, Marcin Wojtas <mw at freebsd.org> wrote:
> > 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 <ar at 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 at freebsd.org mailing list
> > https://lists.freebsd.org/mailman/listinfo/svn-src-all
> > To unsubscribe, send any mail to "svn-src-all-unsubscribe at freebsd.org"
> >
>
>
> --
> Mateusz Guzik <mjguzik gmail.com>
>
More information about the svn-src-head
mailing list