svn commit: r365054 - in head/sys: conf dev/sdhci

Mateusz Guzik mjguzik at gmail.com
Tue Sep 1 20:37:24 UTC 2020


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"

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-all mailing list