Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -76,6 +76,16 @@ compile-with "${AWK} -f $S/tools/usbdevs2h.awk $S/dev/usb/usbdevs -d" \ no-obj no-implicit-rule before-depend \ clean "usbdevs_data.h" +sdiodevs.h optional mmccam \ + dependency "$S/tools/usbdevs2h.awk $S/dev/sdio/sdiodevs" \ + compile-with "${AWK} -f $S/tools/usbdevs2h.awk $S/dev/sdio/sdiodevs -s" \ + no-obj no-implicit-rule before-depend \ + clean "sdiodevs.h" +sdiodevs_data.h optional mmccam \ + dependency "$S/tools/usbdevs2h.awk $S/dev/sdio/sdiodevs" \ + compile-with "${AWK} -f $S/tools/usbdevs2h.awk $S/dev/sdio/sdiodevs -S" \ + no-obj no-implicit-rule before-depend \ + clean "sdiodevs_data.h" cam/cam.c optional scbus cam/cam_compat.c optional scbus cam/cam_iosched.c optional scbus @@ -3028,6 +3038,9 @@ dev/sdhci/sdhci_if.m optional sdhci dev/sdhci/sdhci_acpi.c optional sdhci acpi dev/sdhci/sdhci_pci.c optional sdhci pci +dev/sdio/sdio_if.m optional mmccam +dev/sdio/sdio_subr.c optional mmccam +dev/sdio/sdiob.c optional mmccam dev/sf/if_sf.c optional sf pci dev/sge/if_sge.c optional sge pci dev/siis/siis.c optional siis pci Index: sys/conf/kmod.mk =================================================================== --- sys/conf/kmod.mk +++ sys/conf/kmod.mk @@ -480,6 +480,18 @@ ${AWK} -f ${SYSDIR}/tools/usbdevs2h.awk ${SYSDIR}/dev/usb/usbdevs -d .endif +.if !empty(SRCS:Msdiodevs.h) +CLEANFILES+= sdiodevs.h +sdiodevs.h: ${SYSDIR}/tools/usbdevs2h.awk ${SYSDIR}/dev/sdio/sdiodevs + ${AWK} -f ${SYSDIR}/tools/usbdevs2h.awk ${SYSDIR}/dev/sdio/sdiodevs -s +.endif + +.if !empty(SRCS:Msdiodevs_data.h) +CLEANFILES+= sdiodevs_data.h +sdiodevs_data.h: ${SYSDIR}/tools/usbdevs2h.awk ${SYSDIR}/dev/sdio/sdiodevs + ${AWK} -f ${SYSDIR}/tools/usbdevs2h.awk ${SYSDIR}/dev/sdio/sdiodevs -S +.endif + .if !empty(SRCS:Macpi_quirks.h) CLEANFILES+= acpi_quirks.h acpi_quirks.h: ${SYSDIR}/tools/acpi_quirks2h.awk ${SYSDIR}/dev/acpica/acpi_quirks Index: sys/dev/sdio/sdio_if.m =================================================================== --- /dev/null +++ sys/dev/sdio/sdio_if.m @@ -0,0 +1,105 @@ +#- +# Copyright (c) 2019 The FreeBSD Foundation +# +# Portions of this software were developed by Björn Zeeb +# under sponsorship from the FreeBSD Foundation. +# +# 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# $FreeBSD$ +# + +#include +#include + +INTERFACE sdio; + +# +# Default implementations of some methods. +# +CODE { + static int + null_sdio_read_direct(device_t dev __unused, uint8_t fn __unused, uint32_t addr __unused, uint8_t *val __unused) + { + + return (ENXIO); + } + + static int + null_sdio_write_direct(device_t dev __unused, uint8_t fn __unused, uint32_t addr __unused, uint8_t val __unused) + { + + return (ENXIO); + } + + static int + null_sdio_rw_extended(device_t dev __unused, uint8_t fn __unused, uint32_t addr __unused, uint32_t size __unused, uint8_t *buffer __unused, bool inaddr __unused) + { + + return (ENXIO); + } +}; + +# +# READ DIRECT (1byte) +# +METHOD int read_direct { + device_t dev; + uint8_t fn; + uint32_t addr; + uint8_t *val; +} DEFAULT null_sdio_read_direct; + +# +# WRITE DIRECT (1byte) +# +METHOD int write_direct { + device_t dev; + uint8_t fn; + uint32_t addr; + uint8_t val; +} DEFAULT null_sdio_write_direct; + +# +# READ EXTENDED +# +METHOD int read_extended { + device_t dev; + uint8_t fn; + uint32_t addr; + uint32_t size; + uint8_t *buffer; + bool incaddr; +} DEFAULT null_sdio_rw_extended; + +# +# WRITE EXTENDED +# +METHOD int write_extended { + device_t dev; + uint8_t fn; + uint32_t addr; + uint32_t size; + uint8_t *buffer; + bool incaddr; +} DEFAULT null_sdio_rw_extended; + +# end Index: sys/dev/sdio/sdio_subr.h =================================================================== --- /dev/null +++ sys/dev/sdio/sdio_subr.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 2017 Ilya Bakulin. All rights reserved. + * Copyright (c) 2018-2019 The FreeBSD Foundation + * + * Portions of this software were developed by Björn Zeeb + * under sponsorship from the FreeBSD Foundation. + * + * 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SDIO_SUBR_H_ +#define _SDIO_SUBR_H_ + +/* + * This file contains structures and functions to work with SDIO cards. + */ + +struct sdio_func { + device_t dev; /* The device to talk to CAM. */ + + uint8_t fn; /* Function number. */ + + uint8_t class; /* Class of function. */ + uint16_t vendor; /* Manufacturer ID. */ + uint16_t device; /* Card ID. */ + + uint16_t max_blksize; /* Maximum block size of function. */ + uint16_t cur_blksize; /* Current block size of function. */ + + uint16_t retries; /* Retires for CAM operations. */ + uint32_t timeout; /* Timeout. */ +}; + +struct card_info { + struct sdio_func f[8]; + + /* Compared to R4 Number of I/O Functions we DO count F0 here. */ + uint8_t num_funcs; + + bool smb; /* Support Multiple Block Transfer */ +}; + +#ifdef _KERNEL +int sdio_enable_func(struct sdio_func *); +int sdio_disable_func(struct sdio_func *); +int sdio_set_blocksize(struct sdio_func *, uint16_t); + +uint8_t sdio_readb(struct sdio_func *, uint32_t, int *); +void sdio_writeb(struct sdio_func *, uint8_t, uint32_t, int *); +uint32_t sdio_readl(struct sdio_func *, uint32_t, int *); +void sdio_writel(struct sdio_func *, uint32_t, uint32_t, int *); + +uint8_t sdio_f0_readb(struct sdio_func *, uint32_t, int *); +void sdio_f0_writeb(struct sdio_func *, uint8_t, uint32_t, int *); +#endif /* _KERNEL */ + +#endif /* _SDIO_SUBR_H_ */ Index: sys/dev/sdio/sdio_subr.c =================================================================== --- /dev/null +++ sys/dev/sdio/sdio_subr.c @@ -0,0 +1,188 @@ +/*- + * Copyright (c) 2017 Ilya Bakulin. All rights reserved. + * Copyright (c) 2018-2019 The FreeBSD Foundation + * + * Portions of this software were developed by Björn Zeeb + * under sponsorship from the FreeBSD Foundation. + * + * 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sdio_if.h" + +/* Works on F0. */ +static int +sdio_set_bool_for_func(device_t dev, uint32_t addr, uint8_t fn, bool enable) +{ + int error; + uint8_t val; + bool enabled; + + error = SDIO_READ_DIRECT(dev, 0, addr, &val); + if (error != 0) + return (error); + + enabled = (val & (1 << fn)) ? true : false; + if (enabled == enable) + return (0); + + if (enable) + val |= (1 << fn); + else + val &= ~(1 << fn); + error = SDIO_WRITE_DIRECT(dev, 0, addr, val); + return (error); +} + +int +sdio_enable_func(struct sdio_func *f) +{ + + return (sdio_set_bool_for_func(f->dev, SD_IO_CCCR_FN_ENABLE, + f->fn, true)); +} + +int +sdio_disable_func(struct sdio_func *f) +{ + + return (sdio_set_bool_for_func(f->dev, SD_IO_CCCR_FN_ENABLE, + f->fn, false)); +} + +int +sdio_set_blocksize(struct sdio_func *f, uint16_t bs) +{ + int error; + uint32_t addr; + uint16_t v; + + addr = SD_IO_FBR_START * f->fn + SD_IO_FBR_IOBLKSZ; + v = htole16(bs); + error = SDIO_WRITE_EXTENDED(f->dev, f->fn, addr, + sizeof(v), (uint8_t *)&v, false); + if (error == 0) + f->cur_blksize = bs; + + return (error); +} + +uint8_t +sdio_readb(struct sdio_func *f, uint32_t addr, int *err) +{ + int error; + uint8_t v; + + error = SDIO_READ_DIRECT(f->dev, f->fn, addr, &v); + if (error) { + if (err != NULL) + *err = error; + return (0xff); + } else { + if (err != NULL) + *err = 0; + return (v); + } +} + +void +sdio_writeb(struct sdio_func *f, uint8_t val, uint32_t addr, int *err) +{ + int error; + + error = SDIO_WRITE_DIRECT(f->dev, f->fn, addr, val); + if (err != NULL) + *err = error; +} + +uint32_t +sdio_readl(struct sdio_func *f, uint32_t addr, int *err) +{ + int error; + uint32_t v; + + error = SDIO_READ_EXTENDED(f->dev, f->fn, addr, sizeof(v), + (uint8_t *)&v, false); + if (error) { + if (err != NULL) + *err = error; + return (0xffffffff); + } else { + if (err != NULL) + *err = 0; + return (le32toh(v)); + } +} + +void +sdio_writel(struct sdio_func *f, uint32_t val, uint32_t addr, int *err) +{ + int error; + + error = SDIO_WRITE_EXTENDED(f->dev, f->fn, addr, sizeof(val), + (uint8_t *)&val, false); + if (err != NULL) + *err = error; +} + +uint8_t +sdio_f0_readb(struct sdio_func *f, uint32_t addr, int *err) +{ + int error; + uint8_t v; + + error = SDIO_READ_DIRECT(f->dev, 0, addr, &v); + if (error) { + if (err != NULL) + *err = error; + return (0xff); + } else { + if (err != NULL) + *err = 0; + return (v); + } +} + +void +sdio_f0_writeb(struct sdio_func *f, uint8_t val, uint32_t addr, int *err) +{ + int error; + + error = SDIO_WRITE_DIRECT(f->dev, 0, addr, val); + if (err != NULL) + *err = error; +} + +/* end */ Index: sys/dev/sdio/sdiob.h =================================================================== --- /dev/null +++ sys/dev/sdio/sdiob.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2019 The FreeBSD Foundation + * + * Portions of this software were developed by Björn Zeeb + * under sponsorship from the FreeBSD Foundation. + * + * 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 ``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 BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SDIOB_H +#define _SDIOB_H + +#define SDIOB_NAME sdiob +#define _SDIOB_NAME_S(x) __STRING(x) +#define SDIOB_NAME_S _SDIOB_NAME_S(SDIOB_NAME) + +#define SDIO_VENDOR_ANY (0x0000) +#define SDIO_DEVICE_ANY (0x0000) + +uint16_t sdio_get_vendor(device_t); +uint16_t sdio_get_device(device_t); +struct sdio_func *sdio_get_function(device_t, uint8_t); + +#endif /* _SDIOB_H */ Index: sys/dev/sdio/sdiob.c =================================================================== --- /dev/null +++ sys/dev/sdio/sdiob.c @@ -0,0 +1,1190 @@ +/*- + * Copyright (c) 2017 Ilya Bakulin. All rights reserved. + * Copyright (c) 2018-2019 The FreeBSD Foundation + * + * Portions of this software were developed by Björn Zeeb + * under sponsorship from the FreeBSD Foundation. + * + * 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 ``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 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. + */ +/* + * Implements the (kernel specific) SDIO parts. + * This will hide all cam(4) functionality from the SDIO driver implementations + * which will just be newbus/device(9) and hence look like any other driver for, + * e.g., PCI. + * The sdiob(4) parts effetively "translate" between the two worlds "bridging" + * messages from MMCCAM to newbus and back. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_cam.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include /* for cam_path */ +#include + +#include +#include + +#include "sdio_if.h" + +#ifdef DEBUG +#define DPRINTF(...) printf(__VA_ARGS__) +#define DPRINTFDEV(_dev, ...) device_printf((_dev), __VA_ARGS__) +#else +#define DPRINTF(...) +#define DPRINTFDEV(_dev, ...) +#endif + +struct sdiob_softc { + uint32_t sdio_state; +#define SDIO_STATE_DEAD 0x0001 +#define SDIO_STATE_INITIALIZING 0x0002 +#define SDIO_STATE_READY 0x0004 + uint32_t nb_state; +#define NB_STATE_DEAD 0x0001 +#define NB_STATE_SIM_ADDED 0x0002 +#define NB_STATE_READY 0x0004 + + /* CAM side (including sim_dev). */ + struct card_info cardinfo; + struct cam_periph *periph; + union ccb *ccb; + struct task discover_task; + + /* Newbus side. */ + device_t dev; /* Ourselves. */ + device_t child; +}; + + +/* -------------------------------------------------------------------------- */ +/* + * Helper functions for driver probe/attach to get the "vendor" and "device" + * information from F0 to be able to decide whether to attach or not. + * Also an accessor function to return the sdio_func * by number. + * + * Given these functions operate on the softc their implementation is here and + * not in in sdio_subr.c. + */ + +uint16_t +sdio_get_vendor(device_t dev) +{ + device_t pdev; + struct sdiob_softc *sc; + + if (dev == NULL) + return (SDIO_VENDOR_ANY); + + pdev = device_get_parent(dev); + if (pdev == NULL) + return (SDIO_VENDOR_ANY); + + sc = device_get_softc(pdev); + if (sc == NULL) + return (SDIO_VENDOR_ANY); + + if (sc->cardinfo.num_funcs < 1) + return (SDIO_VENDOR_ANY); + + return (sc->cardinfo.f[0].vendor); +} + +uint16_t +sdio_get_device(device_t dev) +{ + device_t pdev; + struct sdiob_softc *sc; + + if (dev == NULL) + return (SDIO_VENDOR_ANY); + + pdev = device_get_parent(dev); + if (pdev == NULL) + return (SDIO_VENDOR_ANY); + + sc = device_get_softc(pdev); + if (sc == NULL) + return (SDIO_VENDOR_ANY); + + if (sc->cardinfo.num_funcs < 1) + return (SDIO_VENDOR_ANY); + + return (sc->cardinfo.f[0].device); +} + +struct sdio_func * +sdio_get_function(device_t dev, uint8_t fn) +{ + struct sdiob_softc *sc; + + if (dev == NULL) + return (NULL); + + sc = device_get_softc(dev); + if (sc == NULL) + return (NULL); + + if (sc->cardinfo.num_funcs < fn) + return (NULL); + + return (&sc->cardinfo.f[fn]); +} + + +/* -------------------------------------------------------------------------- */ +/* + * SDIO CMD52 and CM53 implementations along with wrapper functions for + * read/write and a CAM periph helper function. + * These are the backend implementations of the sdio_if.m framework talking + * through CAM to sdhci. + * Note: these functions are also called during early discovery stage when + * we are not a device(9) yet. Hence they cannot always use device_printf() + * to log errors and have to call CAM_DEBUG() during these early stages. + */ + +static int +sdioerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) +{ + + return (cam_periph_error(ccb, cam_flags, sense_flags)); +} + +/* CMD52: direct byte access. */ +static int +sdio_rw_direct_sc(struct sdiob_softc *sc, uint8_t fn, uint32_t addr, bool wr, + uint8_t *val) +{ + uint32_t arg, flags; + int error; + + KASSERT((val != NULL), ("%s val passed as NULL\n", __func__)); + + if (sc->ccb == NULL) + sc->ccb = xpt_alloc_ccb(); + else + memset(sc->ccb, 0, sizeof(*sc->ccb)); + xpt_setup_ccb(&sc->ccb->ccb_h, sc->periph->path, CAM_PRIORITY_NONE); + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("%s(fn=%d, addr=%#02x, wr=%d, *val=%#02x)\n", __func__, + fn, addr, wr, *val)); + + flags = MMC_RSP_R5 | MMC_CMD_AC; + arg = SD_IO_RW_FUNC(fn) | SD_IO_RW_ADR(addr); + if (wr) + arg |= SD_IO_RW_WR | SD_IO_RW_RAW | SD_IO_RW_DAT(*val); + + cam_fill_mmcio(&sc->ccb->mmcio, + /*retries*/ 0, + /*cbfcnp*/ NULL, + /*flags*/ CAM_DIR_NONE, + /*mmc_opcode*/ SD_IO_RW_DIRECT, + /*mmc_arg*/ arg, + /*mmc_flags*/ flags, + /*mmc_data*/ 0, + /*timeout*/ 5000); + error = cam_periph_runccb(sc->ccb, sdioerror, CAM_FLAG_NONE, 0, NULL); + if (error != 0) { + if (sc->dev != NULL) + device_printf(sc->dev, + "%s: Failed to %s address %#10x error=%d\n", + __func__, (wr) ? "write" : "read", addr, error); + else + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_INFO, + ("%s: Failed to %s address: %#10x error=%d\n", + __func__, (wr) ? "write" : "read", addr, error)); + return (error); + } + + /* TODO: Add handling of MMC errors */ + /* ccb->mmcio.cmd.error ? */ + if (wr == false) + *val = sc->ccb->mmcio.cmd.resp[0] & 0xff; + + return (0); +} + +static int +sdio_rw_direct(device_t dev, uint8_t fn, uint32_t addr, bool wr, + uint8_t *val) +{ + struct sdiob_softc *sc; + int error; + + sc = device_get_softc(dev); + cam_periph_lock(sc->periph); + error = sdio_rw_direct_sc(sc, fn, addr, wr, val); + cam_periph_unlock(sc->periph); + return (error); +} + +static int +sdiob_read_direct(device_t dev, uint8_t fn, uint32_t addr, uint8_t *val) +{ + int error; + uint8_t v; + + error = sdio_rw_direct(dev, fn, addr, false, &v); + /* Be polite and do not touch the value on read error. */ + if (error == 0 && val != NULL) + *val = v; + return (error); +} + +static int +sdiob_write_direct(device_t dev, uint8_t fn, uint32_t addr, uint8_t val) +{ + + return (sdio_rw_direct(dev, fn, addr, true, &val)); +} + +/* + * CMD53: IO_RW_EXTENDED, read and write multiple I/O registers. + * Increment false gets FIFO mode (single register address). + */ +static int +sdio_rw_extended_sc(struct sdiob_softc *sc, uint8_t fn, uint32_t addr, + bool wr, uint32_t size, uint8_t *buffer, bool incaddr) +{ + struct mmc_data mmcd; + uint32_t arg, cam_flags, flags, len; + int error; + uint16_t b_count, l; + + if (sc->ccb == NULL) + sc->ccb = xpt_alloc_ccb(); + else + memset(sc->ccb, 0, sizeof(*sc->ccb)); + xpt_setup_ccb(&sc->ccb->ccb_h, sc->periph->path, CAM_PRIORITY_NONE); + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("%s(fn=%d, addr=%#0x, wr=%d, size=%u buf=%p incr=%d)\n", + __func__, fn, addr, wr, size, buffer, incaddr)); + + /* + * If block mode is supported and we have at least 4 bytes to write and + * the size is at least one block, then start doing blk transfers. + */ + while (sc->cardinfo.smb && +#ifndef __notyet__ + /* For now disable blk writes as they lead to CRC Dat errors on RPi. */ + !wr && +#endif + size > 4 && size >= sc->cardinfo.f[fn].cur_blksize) { + + b_count = size / sc->cardinfo.f[fn].cur_blksize; + KASSERT(b_count >= 1, ("%s: block count to small %u size %u " + "cur_blksize %u\n", __func__, b_count, size, + sc->cardinfo.f[fn].cur_blksize)); + +#ifdef __notyet__ + /* XXX support inifinite transfer with b_count = 0. */ +#else + if (b_count > 511) + b_count = 511; + /* Force to 1 blk to see if that works. */ + b_count = 1; + len = b_count * sc->cardinfo.f[fn].cur_blksize; +#endif + flags = MMC_RSP_R5 | MMC_CMD_ADTC; + arg = SD_IOE_RW_FUNC(fn) | SD_IOE_RW_BLK | + SD_IOE_RW_ADR(addr) | SD_IOE_RW_LEN(b_count); + if (incaddr) + arg |= SD_IOE_RW_INCR; + + memset(&mmcd, 0, sizeof(mmcd)); + mmcd.data = buffer; + mmcd.len = len; + /* That's just silly as those are both known from elsewhere, aren't they? */ + mmcd.block_size = sc->cardinfo.f[fn].cur_blksize; + mmcd.block_count = b_count; + + if (wr) { + arg |= SD_IOE_RW_WR; + cam_flags = CAM_DIR_OUT; + mmcd.flags = MMC_DATA_WRITE; + } else { + cam_flags = CAM_DIR_IN; + mmcd.flags = MMC_DATA_READ; + } +#ifdef __notyet__ + if (b_count == 0) + /* XXX. Cancel I/O: CCCR -> ASx */ + /* Stop cmd. */ +#endif + cam_fill_mmcio(&sc->ccb->mmcio, + /*retries*/ 0, + /*cbfcnp*/ NULL, + /*flags*/ cam_flags, + /*mmc_opcode*/ SD_IO_RW_EXTENDED, + /*mmc_arg*/ arg, + /*mmc_flags*/ flags, + /*mmc_data*/ &mmcd, + /*timeout*/ b_count * 5000); /* XXX retries/timeout from func. */ + + if (b_count > 1) { + sc->ccb->mmcio.cmd.data->flags |= MMC_DATA_MULTI; +#if 0 + /* Stop command? */ +#endif + } + error = cam_periph_runccb(sc->ccb, sdioerror, CAM_FLAG_NONE, 0, NULL); + if (error != 0) { + if (sc->dev != NULL) + device_printf(sc->dev, + "%s: Failed to %s address %#10x buffer %p size %u " + "%s b_count %u error=%d\n", + __func__, (wr) ? "write to" : "read from", addr, + buffer, len, (incaddr) ? "incr" : "fixed", + b_count, error); + else + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_INFO, + ("%s: Failed to %s address %#10x buffer %p size %u " + "%s b_count %u error=%d\n", + __func__, (wr) ? "write to" : "read from", addr, + buffer, len, (incaddr) ? "incr" : "fixed", + b_count, error)); + return (error); + } + + /* TODO: Add handling of MMC errors */ + /* ccb->mmcio.cmd.error ? */ + error = sc->ccb->mmcio.cmd.resp[0] & 0xff; + if (error != 0) { + if (sc->dev != NULL) + device_printf(sc->dev, + "%s: Failed to %s address %#10x buffer %p size %u " + "%s b_count %u mmcio resp error=%d\n", + __func__, (wr) ? "write to" : "read from", addr, + buffer, len, (incaddr) ? "incr" : "fixed", + b_count, error); + else + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_INFO, + ("%s: Failed to %s address %#10x buffer %p size %u " + "%s b_count %u mmcio resp error=%d\n", + __func__, (wr) ? "write to" : "read from", addr, + buffer, len, (incaddr) ? "incr" : "fixed", + b_count, error)); + return (error); + } + + size -= len; + buffer += len; + if (incaddr) + addr += len; + } + + len = size; + while (len > 0) { + l = MIN(len, sc->cardinfo.f[fn].cur_blksize); + + flags = MMC_RSP_R5 | MMC_CMD_ADTC; + arg = SD_IOE_RW_FUNC(fn) | SD_IOE_RW_ADR(addr) | SD_IOE_RW_LEN(l); + if (incaddr) + arg |= SD_IOE_RW_INCR; + + memset(&mmcd, 0, sizeof(mmcd)); + mmcd.data = buffer; + mmcd.len = l; + + if (wr) { + arg |= SD_IO_RW_WR; + cam_flags = CAM_DIR_OUT; + mmcd.flags = MMC_DATA_WRITE; + } else { + cam_flags = CAM_DIR_IN; + mmcd.flags = MMC_DATA_READ; + } + cam_fill_mmcio(&sc->ccb->mmcio, + /*retries*/ 0, + /*cbfcnp*/ NULL, + /*flags*/ cam_flags, + /*mmc_opcode*/ SD_IO_RW_EXTENDED, + /*mmc_arg*/ arg, + /*mmc_flags*/ flags, + /*mmc_data*/ &mmcd, + /*timeout*/ 5000); /* XXX retries/timeout from func. */ + error = cam_periph_runccb(sc->ccb, sdioerror, CAM_FLAG_NONE, 0, NULL); + if (error != 0) { + if (sc->dev != NULL) + device_printf(sc->dev, + "%s: Failed to %s address %#10x buffer %p size %u " + "%s error=%d\n", + __func__, (wr) ? "write to" : "read from", addr, + buffer, l, (incaddr) ? "incr" : "fixed", + error); + else + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_INFO, + ("%s: Failed to %s address %#10x buffer %p size %u " + "%s error=%d\n", + __func__, (wr) ? "write to" : "read from", addr, + buffer, l, (incaddr) ? "incr" : "fixed", + error)); + return (error); + } + + /* TODO: Add handling of MMC errors */ + /* ccb->mmcio.cmd.error ? */ + error = sc->ccb->mmcio.cmd.resp[0] & 0xff; + if (error != 0) { + if (sc->dev != NULL) + device_printf(sc->dev, + "%s: Failed to %s address %#10x buffer %p size %u " + "%s mmcio resp error=%d\n", + __func__, (wr) ? "write to" : "read from", addr, + buffer, l, (incaddr) ? "incr" : "fixed", + error); + else + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_INFO, + ("%s: Failed to %s address %#10x buffer %p size %u " + "%s mmcio resp error=%d\n", + __func__, (wr) ? "write to" : "read from", addr, + buffer, l, (incaddr) ? "incr" : "fixed", + error)); + return (error); + } + + /* Prepare for next iteration. */ + len -= l; + buffer += l; + if (incaddr) + addr += l; + } + + return (error); +} + +static int +sdio_rw_extended(device_t dev, uint8_t fn, uint32_t addr, bool wr, + uint32_t size, uint8_t *buffer, bool incaddr) +{ + struct sdiob_softc *sc; + int error; + + sc = device_get_softc(dev); + cam_periph_lock(sc->periph); + error = sdio_rw_extended_sc(sc, fn, addr, wr, size, buffer, incaddr); + cam_periph_unlock(sc->periph); + return (error); +} + +static int +sdiob_read_extended(device_t dev, uint8_t fn, uint32_t addr, uint32_t size, + uint8_t *buffer, bool incaddr) +{ + + return (sdio_rw_extended(dev, fn, addr, false, size, buffer, incaddr)); +} + +static int +sdiob_write_extended(device_t dev, uint8_t fn, uint32_t addr, uint32_t size, + uint8_t *buffer, bool incaddr) +{ + + return (sdio_rw_extended(dev, fn, addr, true, size, buffer, incaddr)); +} + + +/* -------------------------------------------------------------------------- */ +/* + * Newbus functions for ourselves to probe/attach/detach and become a proper + * device(9). Attach will also probe for child devices (another driver + * implementing SDIO). + */ + +static int +sdiob_probe(device_t dev) +{ + + device_set_desc(dev, "SDIO CAM-Newbus bridge"); + return (BUS_PROBE_DEFAULT); +} + +static int +sdiob_attach(device_t dev) +{ + struct sdiob_softc *sc; + int error, i; + + sc = device_get_softc(dev); + if (sc == NULL) + return (ENXIO); + + /* + * Now that we are a dev, initialize the backpointer in the + * sdio function structures so we can pass them around and + * directly call CAM operations. + * Do this before any child gets a chance to attach. + */ + for (i = 0; i < sc->cardinfo.num_funcs; i++) + sc->cardinfo.f[i].dev = dev; + + /* + * XXX-BZ we might want to add one child per valid function later? + * While other implementations seem to do this it only makes sense if + * we discover split-personality cards as otherwise device drivers + * will see n probe/attach cycles and have to independently claim + * functions and then glue them back together to one device, like: + * / -- F0 -- \ + * sdiob - -- F1 -- - foo_dev(9) + * \ -- F2 -- / + * \ -- F3 + * This seems to complex for now. If we will be forced to do this + * at a later time, we shall not probe/attach for F0 but make it + * available through F using special accessor function as we + * already starting to have, e.g., sdio_f0_readb(). + * For now it is all-or-nothing. + */ + sc->child = device_add_child(dev, NULL, -1); + if (sc->child == NULL) { + device_printf(dev, "%s: failed to add child\n", __func__); + return (ENXIO); + } + error = device_probe_and_attach(sc->child); + if (error != 0) { + DPRINTFDEV(dev, "%s: device_probe_and_attach(%p %s) " + "failed %d, no child yet.\n", __func__, sc->child, + device_get_nameunit(sc->child), error); + /* + * Do not delete the child or a driver loading later + * will have nothing to attach to! + */ +#if 0 + if (device_delete_child(dev, sc->child) == 0) + sc->child = NULL; + return (ENXIO); +#endif + } + + sc->nb_state = NB_STATE_READY; + + cam_periph_lock(sc->periph); + xpt_announce_periph(sc->periph, NULL); + cam_periph_unlock(sc->periph); + + return (0); +} + +static int +sdiob_detach(device_t dev) +{ + + /* XXX TODO? */ + return (EOPNOTSUPP); +} + + +/* -------------------------------------------------------------------------- */ +/* + * driver(9) and device(9) "control plane". + * This is what we use when we are making ourselves a device(9) in order to + * provide a newbus interface again, as well as the implementation of the + * SDIO interface. + */ + +static device_method_t sdiob_methods[] = { + + /* Device interface. */ + DEVMETHOD(device_probe, sdiob_probe), + DEVMETHOD(device_attach, sdiob_attach), + DEVMETHOD(device_detach, sdiob_detach), + + /* Bus interface. */ + DEVMETHOD(bus_add_child, bus_generic_add_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* SDIO interface. */ + DEVMETHOD(sdio_read_direct, sdiob_read_direct), + DEVMETHOD(sdio_write_direct, sdiob_write_direct), + DEVMETHOD(sdio_read_extended, sdiob_read_extended), + DEVMETHOD(sdio_write_extended, sdiob_write_extended), + + DEVMETHOD_END +}; + +static devclass_t sdiob_devclass; +static driver_t sdiob_driver = { + SDIOB_NAME_S, + sdiob_methods, + 0 +}; + + +/* -------------------------------------------------------------------------- */ +/* + * CIS related. + * Read card and function information and populate the cardinfo structure. + */ + +static int +sdio_read_direct_sc(struct sdiob_softc *sc, uint8_t fn, uint32_t addr, + uint8_t *val) +{ + int error; + uint8_t v; + + error = sdio_rw_direct_sc(sc, fn, addr, false, &v); + if (error == 0 && val != NULL) + *val = v; + return (error); +} + +static int +sdio_func_read_cis(struct sdiob_softc *sc, uint8_t fn, uint32_t cis_addr) +{ + char cis1_info_buf[256]; + char *cis1_info[4]; + int start, i, count, ret; + uint32_t addr; + uint8_t ch, tuple_id, tuple_len, tuple_count, v; + + /* If we encounter any read errors, abort and return. */ +#define ERR_OUT(ret) \ + if (ret != 0) \ + goto err; + ret = 0; + /* Use to prevent infinite loop in case of parse errors. */ + tuple_count = 0; + memset(cis1_info_buf, 0, 256); + do { + addr = cis_addr; + ret = sdio_read_direct_sc(sc, 0, addr++, &tuple_id); + ERR_OUT(ret); + if (tuple_id == SD_IO_CISTPL_END) + break; + if (tuple_id == 0) { + cis_addr++; + continue; + } + ret = sdio_read_direct_sc(sc, 0, addr++, &tuple_len); + ERR_OUT(ret); + if (tuple_len == 0) { + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_PERIPH, + ("%s: parse error: 0-length tuple %#02x\n", + __func__, tuple_id)); + return (EIO); + } + + switch (tuple_id) { + case SD_IO_CISTPL_VERS_1: + addr += 2; + for (count = 0, start = 0, i = 0; + (count < 4) && ((i + 4) < 256); i++) { + ret = sdio_read_direct_sc(sc, 0, addr + i, &ch); + ERR_OUT(ret); + DPRINTF("%s: count=%d, start=%d, i=%d, got " + "(%#02x)\n", __func__, count, start, i, ch); + if (ch == 0xff) + break; + cis1_info_buf[i] = ch; + if (ch == 0) { + cis1_info[count] = + cis1_info_buf + start; + start = i + 1; + count++; + } + } + /* XXX printfs as part of a parsing API seem odd. */ + printf("Card info: "); + for (i=0; i < 4; i++) + if (cis1_info[i]) + printf(" %s", cis1_info[i]); + printf("\n"); + break; + case SD_IO_CISTPL_MANFID: + /* TPLMID_MANF */ + ret = sdio_read_direct_sc(sc, 0, addr++, &v); + ERR_OUT(ret); + sc->cardinfo.f[fn].vendor = v; + ret = sdio_read_direct_sc(sc, 0, addr++, &v); + ERR_OUT(ret); + sc->cardinfo.f[fn].vendor |= (v << 8); + /* TPLMID_CARD */ + ret = sdio_read_direct_sc(sc, 0, addr++, &v); + ERR_OUT(ret); + sc->cardinfo.f[fn].device = v; + ret = sdio_read_direct_sc(sc, 0, addr, &v); + ERR_OUT(ret); + sc->cardinfo.f[fn].device |= (v << 8); + break; + case SD_IO_CISTPL_FUNCID: + /* not sure if we need to parse it? */ + break; + case SD_IO_CISTPL_FUNCE: + if (tuple_len < 4) { + printf("%s: FUNCE is too short: %d\n", + __func__, tuple_len); + break; + } + /* TPLFE_TYPE (Extended Data) */ + ret = sdio_read_direct_sc(sc, 0, addr++, &v); + ERR_OUT(ret); + if (fn == 0) { + if (v != 0x00) + break; + } else { + if (v != 0x01) + break; + addr += 0x0b; + } + ret = sdio_read_direct_sc(sc, 0, addr, &v); + ERR_OUT(ret); + sc->cardinfo.f[fn].max_blksize = v; + ret = sdio_read_direct_sc(sc, 0, addr+1, &v); + ERR_OUT(ret); + sc->cardinfo.f[fn].max_blksize |= (v << 8); + break; + default: + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_PERIPH, + ("%s: Skipping fn %d tuple %d ID %#02x " + "len %#02x\n", __func__, fn, tuple_count, + tuple_id, tuple_len)); + } + if (tuple_len == 0xff) { + /* Also marks the end of a tuple chain (E1 16.2) */ + /* The tuple is valid, hence this going at the end. */ + break; + } + cis_addr += 2 + tuple_len; + tuple_count++; + } while (tuple_count < 20); +err: +#undef ERR_OUT + return (ret); +} + +static uint32_t +sdio_get_common_cis_addr(struct sdiob_softc *sc) +{ + uint32_t addr; + int error; + uint8_t val; + + error = sdio_read_direct_sc(sc, 0, SD_IO_CCCR_CISPTR + 0, &val); + if (error != 0) { + /* XXX print error */ + return (0xffffffff); + } + addr = val; + error = sdio_read_direct_sc(sc, 0, SD_IO_CCCR_CISPTR + 1, &val); + if (error != 0) { + /* XXX print error */ + return (0xffffffff); + } + addr |= (val << 8); + error = sdio_read_direct_sc(sc, 0, SD_IO_CCCR_CISPTR + 2, &val); + if (error != 0) { + /* XXX print error */ + return (0xffffffff); + } + addr |= (val << 16); + + if (addr < SD_IO_CIS_START || addr > SD_IO_CIS_START + SD_IO_CIS_SIZE) { + CAM_DEBUG(sc->ccb->ccb_h.path, CAM_DEBUG_PERIPH, + ("%s: bad CIS address: %#04x\n", __func__, addr)); + addr = 0xffffffff; + } + + return (addr); +} + +static int +sdiob_get_card_info(struct sdiob_softc *sc) +{ + struct mmc_params *mmcp; + uint32_t cis_addr, fbr_addr; + int fn, error; + uint8_t fn_max, val; + + cis_addr = sdio_get_common_cis_addr(sc); + if (cis_addr == 0xff) + return (-1); + + memset(&sc->cardinfo, 0, sizeof(sc->cardinfo)); + + /* F0 must always be present. */ + fn = 0; + error = sdio_func_read_cis(sc, fn, cis_addr); + if (error != 0) + return (error); + sc->cardinfo.num_funcs++; + /* Read CCCR Card Capability. */ + error = sdio_read_direct_sc(sc, 0, SD_IO_CCCR_CARDCAP, &val); + if (error != 0) { + /* XXX print error */ + return (error); + } + sc->cardinfo.smb = (val & CCCR_CC_SMB) ? true : false; + DPRINTF("%s: F%d: Vendor %#04x product %#04x max block size %d bytes " + "smb %s\n", + __func__, fn, sc->cardinfo.f[fn].vendor, sc->cardinfo.f[fn].device, + sc->cardinfo.f[fn].max_blksize, sc->cardinfo.smb ? "yes" : "no"); + + /* mmcp->sdio_func_count contains the number of functions w/o F0. */ + mmcp = &sc->ccb->ccb_h.path->device->mmc_ident_data; + fn_max = MIN(mmcp->sdio_func_count + 1, nitems(sc->cardinfo.f)); + for (fn = 1; fn < fn_max; fn++) { + + fbr_addr = SD_IO_FBR_START * fn + SD_IO_FBR_CIS_OFFSET; + + error = sdio_read_direct_sc(sc, 0, fbr_addr++, &val); + if (error != 0) + break; + cis_addr = val; + error = sdio_read_direct_sc(sc, 0, fbr_addr++, &val); + if (error != 0) + break; + cis_addr |= (val << 8); + error = sdio_read_direct_sc(sc, 0, fbr_addr++, &val); + if (error != 0) + break; + cis_addr |= (val << 16); + + error = sdio_func_read_cis(sc, fn, cis_addr); + if (error != 0) + break; + + /* Read the Standard SDIO Function Interface Code. */ + fbr_addr = SD_IO_FBR_START * fn; + error = sdio_read_direct_sc(sc, 0, fbr_addr++, &val); + if (error != 0) + break; + sc->cardinfo.f[fn].class = (val & 0x0f); + if (sc->cardinfo.f[fn].class == 0x0f) { + error = sdio_read_direct_sc(sc, 0, fbr_addr, &val); + if (error != 0) + break; + sc->cardinfo.f[fn].class = val; + } + + sc->cardinfo.f[fn].fn = fn; + sc->cardinfo.f[fn].cur_blksize = sc->cardinfo.f[fn].max_blksize; + sc->cardinfo.f[fn].retries = 0; + sc->cardinfo.f[fn].timeout = 5000; + + DPRINTF("%s: F%d: Class %d Vendor %#04x product %#04x " + "max_blksize %d bytes\n", __func__, fn, + sc->cardinfo.f[fn].class, + sc->cardinfo.f[fn].vendor, sc->cardinfo.f[fn].device, + sc->cardinfo.f[fn].max_blksize); + if (sc->cardinfo.f[fn].vendor == 0) { + DPRINTF("%s: F%d doesn't exist\n", __func__, fn); + break; + } + sc->cardinfo.num_funcs++; + } + return (error); +} + + +/* -------------------------------------------------------------------------- */ +/* + * CAM periph registration, allocation, and detached from that a discovery + * task, which goes off reads cardinfo, and then adds ourselves to our SIM's + * device addingt he devclass and registering the driver. This keeps the + * newbus chain connected though we will talk CAM in the middle (until one + * day CAM might be newbusyfied). + */ + +static int +sdio_newbus_sim_add(struct sdiob_softc *sc) +{ + device_t pdev; + devclass_t bus_devclass; + int error; + + /* Add ourselves to our parent (SIM) device. */ + + /* Add ourselves to our parent. That way we can become a parent. */ + KASSERT(sc->periph->sim->sim_dev != NULL, ("%s: sim_dev is NULL, sc %p " + "periph %p sim %p\n", __func__, sc, sc->periph, sc->periph->sim)); + + if (sc->dev == NULL) + sc->dev = BUS_ADD_CHILD(sc->periph->sim->sim_dev, 0, + SDIOB_NAME_S, -1); + if (sc->dev == NULL) + return (ENXIO); + device_set_softc(sc->dev, sc); + /* + * Don't set description here; devclass_add_driver() -> + * device_probe_child() -> device_set_driver() will nuke it again. + */ + /* XXX ivars? */ + + pdev = device_get_parent(sc->dev); + KASSERT(pdev != NULL, ("%s: sc %p dev %p (%s) parent is NULL\n", + __func__, sc, sc->dev, device_get_nameunit(sc->dev))); + bus_devclass = device_get_devclass(pdev); + if (bus_devclass == NULL) { + printf("%s: Failed to get devclass from %s.\n", __func__, + device_get_nameunit(pdev)); + return (ENXIO); + } + + mtx_lock(&Giant); + error = devclass_add_driver(bus_devclass, &sdiob_driver, + BUS_PASS_DEFAULT, &sdiob_devclass); + mtx_unlock(&Giant); + if (error != 0) { + printf("%s: Failed to add driver to devclass: %d.\n", + __func__, error); + return (error); + } + + /* Done. */ + sc->nb_state = NB_STATE_SIM_ADDED; + + return (0); +} + +static void +sdiobdiscover(void *context, int pending) +{ + struct cam_periph *periph; + struct sdiob_softc *sc; + int error; + + KASSERT(context != NULL, ("%s: context is NULL\n", __func__)); + periph = (struct cam_periph *)context; + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("%s\n", __func__)); + + /* Periph was held for us when this task was enqueued. */ + if ((periph->flags & CAM_PERIPH_INVALID) != 0) { + cam_periph_release(periph); + return; + } + + sc = periph->softc; + sc->sdio_state = SDIO_STATE_INITIALIZING; + + if (sc->ccb == NULL) + sc->ccb = xpt_alloc_ccb(); + else + memset(sc->ccb, 0, sizeof(*sc->ccb)); + xpt_setup_ccb(&sc->ccb->ccb_h, periph->path, CAM_PRIORITY_NONE); + + /* + * Read CCCR and FBR of each function, get manufacturer and device IDs, + * max block size, and whatever else we deem necessary. + */ + cam_periph_lock(periph); + error = sdiob_get_card_info(sc); + if (error == 0) + sc->sdio_state = SDIO_STATE_READY; + else + sc->sdio_state = SDIO_STATE_DEAD; + cam_periph_unlock(periph); + + if (error) + return; + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("%s: num_func %d\n", + __func__, sc->cardinfo.num_funcs)); + + /* + * Now CAM portion of the driver has been initialized and + * we know VID/PID of all the functions on the card. + * Time to hook into the newbus. + */ + error = sdio_newbus_sim_add(sc); + if (error != 0) + sc->nb_state = NB_STATE_DEAD; + + return; +} + +/* Called at the end of cam_periph_alloc() for us to finish allocation. */ +static cam_status +sdiobregister(struct cam_periph *periph, void *arg) +{ + struct sdiob_softc *sc; + int error; + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("%s: arg %p\n", __func__, arg)); + if (arg == NULL) { + printf("%s: no getdev CCB, can't register device pariph %p\n", + __func__, periph); + return(CAM_REQ_CMP_ERR); + } + if (periph->sim == NULL || periph->sim->sim_dev == NULL) { + printf("%s: no sim %p or sim_dev %p\n", __func__, periph->sim, + (periph->sim != NULL) ? periph->sim->sim_dev : NULL); + return(CAM_REQ_CMP_ERR); + } + + sc = (struct sdiob_softc *) malloc(sizeof(*sc), M_DEVBUF, + M_NOWAIT|M_ZERO); + if (sc == NULL) { + printf("%s: unable to allocate sc\n", __func__); + return (CAM_REQ_CMP_ERR); + } + sc->sdio_state = SDIO_STATE_DEAD; + sc->nb_state = NB_STATE_DEAD; + TASK_INIT(&sc->discover_task, 0, sdiobdiscover, periph); + + /* Refcount until we are setup. Can't block. */ + error = cam_periph_hold(periph, PRIBIO); + if (error != 0) { + printf("%s: lost periph during registration!\n", __func__); + free(sc, M_DEVBUF); + return(CAM_REQ_CMP_ERR); + } + periph->softc = sc; + sc->periph = periph; + cam_periph_unlock(periph); + + error = taskqueue_enqueue(taskqueue_thread, &sc->discover_task); + + cam_periph_lock(periph); + /* We will continue to hold a refcount for discover_task. */ + /* cam_periph_unhold(periph); */ + + xpt_schedule(periph, CAM_PRIORITY_XPT); + + return (CAM_REQ_CMP); +} + +static void +sdioboninvalidate(struct cam_periph *periph) +{ + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("%s:\n", __func__)); + + return; +} + +static void +sdiobcleanup(struct cam_periph *periph) +{ + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("%s:\n", __func__)); + + return; +} + +static void +sdiobstart(struct cam_periph *periph, union ccb *ccb) +{ + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("%s: ccb %p\n", __func__, ccb)); + + return; +} + +static void +sdiobasync(void *softc, uint32_t code, struct cam_path *path, void *arg) +{ + struct cam_periph *periph; + struct ccb_getdev *cgd; + cam_status status; + + periph = (struct cam_periph *)softc; + + CAM_DEBUG(path, CAM_DEBUG_TRACE, ("%s(code=%d)\n", __func__, code)); + switch (code) { + case AC_FOUND_DEVICE: + if (arg == NULL) + break; + cgd = (struct ccb_getdev *)arg; + if (cgd->protocol != PROTO_MMCSD) + break; + + /* We do not support SD memory (Combo) Cards. */ + if ((path->device->mmc_ident_data.card_features & + CARD_FEATURE_MEMORY)) { + CAM_DEBUG(path, CAM_DEBUG_TRACE, + ("Memory card, not interested\n")); + break; + } + + /* + * Allocate a peripheral instance for this device which starts + * the probe process. + */ + status = cam_periph_alloc(sdiobregister, sdioboninvalidate, + sdiobcleanup, sdiobstart, SDIOB_NAME_S, CAM_PERIPH_BIO, path, + sdiobasync, AC_FOUND_DEVICE, cgd); + if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) + CAM_DEBUG(path, CAM_DEBUG_PERIPH, + ("%s: Unable to attach to new device due to " + "status %#02x\n", __func__, status)); + break; + default: + CAM_DEBUG(path, CAM_DEBUG_PERIPH, + ("%s: cannot handle async code %#02x\n", __func__, code)); + cam_periph_async(periph, code, path, arg); + break; + } +} + +static void +sdiobinit(void) +{ + cam_status status; + + /* + * Register for new device notification. We will be notified for all + * already existing ones. + */ + status = xpt_register_async(AC_FOUND_DEVICE, sdiobasync, NULL, NULL); + if (status != CAM_REQ_CMP) + printf("%s: Failed to attach async callback, statux %#02x", + __func__, status); +} + +/* This function will allow unloading the KLD. */ +static int +sdiobdeinit(void) +{ + + return (EOPNOTSUPP); +} + +static struct periph_driver sdiobdriver = +{ + .init = sdiobinit, + .driver_name = SDIOB_NAME_S, + .units = TAILQ_HEAD_INITIALIZER(sdiobdriver.units), + .generation = 0, + .flags = 0, + .deinit = sdiobdeinit, +}; + +PERIPHDRIVER_DECLARE(SDIOB_NAME, sdiobdriver); +MODULE_VERSION(SDIOB_NAME, 1); Index: sys/dev/sdio/sdiodevs =================================================================== --- /dev/null +++ sys/dev/sdio/sdiodevs @@ -0,0 +1,74 @@ +$FreeBSD$ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 The FreeBSD Foundation + * + * Portions of this software were developed by Björn Zeeb + * under sponsorship from the FreeBSD Foundation. + * + * 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. + */ +/* + * Take SDIO CIS, CISTPL_MANFID, TPLMID_MANF and TPLMID_CARD information and + * present them as vendor and device IDs (a terminology we understand for + * other parts) and run them through the usbdevs2h.awk script to generate + * a header file with #defines for them in sdiodevs.h and sdiodevs_data.h + * which provides a structure with a description as well. + * The format of this file is modelled after sys/dev/usb/usbdevs. + * For more details see there. + */ + +/* + * -------------------------------------------------------------------------- + * List of TPLMID_MANF "vendor ID"s. + * Please sort by vendor ID ascending. + */ +vendor BROADCOM 0x02d0 Broadcom +vendor CYPRESS 0x02d0 Cypress/Broadcom + +/* + * -------------------------------------------------------------------------- + * List of TPLMID_CARD "product ID"s. + * Please group by vendor in same order as above. + */ + +/* Broadcom products */ +product BROADCOM 43241 0x4324 BCM43241 fullmac SDIO WiFi +product BROADCOM 4329 0x4329 BCM4329 fullmac SDIO WiFi +product BROADCOM 4330 0x4330 BCM4330 fullmac SDIO WiFi +product BROADCOM 4334 0x4334 BCM4334 fullmac SDIO WiFi +product BROADCOM 4335_4339 0x4335 BCM4335_4339 fullmac SDIO WiFi +product BROADCOM 4339 0x4339 BCM4339 fullmac SDIO WiFi +product BROADCOM 4345 0x4345 BCM4345 fullmac SDIO WiFi +product BROADCOM 4354 0x4354 BCM4354 fullmac SDIO WiFi +product BROADCOM 4356 0x4356 BCM4356 fullmac SDIO WiFi +product BROADCOM 43143 0xa887 BCM43143 fullmac SDIO WiFi +product BROADCOM 43340 0xa94c BCM43340 fullmac SDIO WiFi +product BROADCOM 43341 0xa94d BCM43341 fullmac SDIO WiFi +product BROADCOM 43362 0xa962 BCM43362 fullmac SDIO WiFi +product BROADCOM 43364 0xa9a4 BCM43364 fullmac SDIO WiFi +product BROADCOM 43430 0xa9a6 BCM43430 fullmac SDIO WiFi +product BROADCOM 43455 0xa9bf BCM43455 fullmac SDIO WiFi +product CYPRESS 4373 0x4373 CY4373 fullmac SDIO WiFi + +/* end */ Index: sys/modules/Makefile =================================================================== --- sys/modules/Makefile +++ sys/modules/Makefile @@ -331,6 +331,7 @@ sdhci \ ${_sdhci_acpi} \ sdhci_pci \ + sdio \ sem \ send \ ${_sf} \ Index: sys/modules/sdio/Makefile =================================================================== --- /dev/null +++ sys/modules/sdio/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/sdio + +KMOD= sdio +SRCS= sdiob.c sdio_subr.c +SRCS+= sdio_if.c +SRCS+= device_if.h bus_if.h sdio_if.h + +.include Index: sys/tools/usbdevs2h.awk =================================================================== --- sys/tools/usbdevs2h.awk +++ sys/tools/usbdevs2h.awk @@ -36,7 +36,7 @@ function usage() { - print "usage: usbdevs2h.awk [-d|-h]"; + print "usage: usbdevs2h.awk [-d|-h|-s|-S]"; exit 1; } @@ -51,7 +51,7 @@ printf(" */\n") > file } -function vendor(hfile) +function vendor(hfile, type) { nvendors++ @@ -59,8 +59,8 @@ vendors[nvendors, 1] = $2; # name vendors[nvendors, 2] = $3; # id if (hfile) - printf("#define\tUSB_VENDOR_%s\t%s\t", vendors[nvendors, 1], - vendors[nvendors, 2]) > hfile + printf("#define\t%s_VENDOR_%s\t%s\t", toupper(type), + vendors[nvendors, 1], vendors[nvendors, 2]) > hfile i = 3; f = 4; # comments @@ -101,7 +101,7 @@ printf("\n") > hfile } -function product(hfile) +function product(hfile, type) { nproducts++ @@ -109,8 +109,9 @@ products[nproducts, 2] = $3; # product id products[nproducts, 3] = $4; # id if (hfile) - printf("#define\tUSB_PRODUCT_%s_%s\t%s\t", \ - products[nproducts, 1], products[nproducts, 2], \ + printf("#define\t%s_PRODUCT_%s_%s\t%s\t", + toupper(type), + products[nproducts, 1], products[nproducts, 2], products[nproducts, 3]) > hfile i=4; f = 5; @@ -153,14 +154,16 @@ printf("\n") > hfile } -function dump_dfile(dfile) +function dump_dfile(dfile, type) { printf("\n") > dfile - printf("const struct usb_knowndev usb_knowndevs[] = {\n") > dfile + printf("const struct %s_knowndev %s_knowndevs[] = {\n", + tolower(type), tolower(type)) > dfile for (i = 1; i <= nproducts; i++) { printf("\t{\n") > dfile - printf("\t USB_VENDOR_%s, USB_PRODUCT_%s_%s,\n", - products[i, 1], products[i, 1], products[i, 2]) > dfile + printf("\t %s_VENDOR_%s, %s_PRODUCT_%s_%s,\n", + toupper(type), products[i, 1], + toupper(type), products[i, 1], products[i, 2]) > dfile printf("\t ") > dfile printf("0") > dfile printf(",\n") > dfile @@ -193,8 +196,9 @@ } for (i = 1; i <= nvendors; i++) { printf("\t{\n") > dfile - printf("\t USB_VENDOR_%s, 0,\n", vendors[i, 1]) > dfile - printf("\t USB_KNOWNDEV_NOPROD,\n") > dfile + printf("\t %s_VENDOR_%s, 0,\n", + toupper(type), vendors[i, 1]) > dfile + printf("\t %s_KNOWNDEV_NOPROD,\n", toupper(type)) > dfile printf("\t \"") > dfile j = 3; needspace = 0; @@ -216,15 +220,29 @@ BEGIN { nproducts = nvendors = 0 +# Having a "/" in the #define should lead to an error later on. +type="N/A" # Process the command line for (i = 1; i < ARGC; i++) { arg = ARGV[i]; - if (arg !~ /^-[dh]+$/ && arg !~ /devs$/) + if (arg !~ /^-[dhsS]+$/ && arg !~ /devs$/) usage(); - if (arg ~ /^-.*d/) + if (arg ~ /^-.*d/) { dfile="usbdevs_data.h" - if (arg ~ /^-.*h/) + type="USB" + } + if (arg ~ /^-.*h/) { hfile="usbdevs.h" + type="USB" + } + if (arg ~ /^-.*S/) { + dfile="sdiodevs_data.h" + type="SDIO" + } + if (arg ~ /^-.*s/) { + hfile="sdiodevs.h" + type="SDIO" + } if (arg ~ /devs$/) srcfile = arg; } @@ -243,11 +261,11 @@ continue; } if ($1 == "vendor") { - vendor(hfile) + vendor(hfile, type) continue } if ($1 == "product") { - product(hfile) + product(hfile, type) continue } if ($0 == "") @@ -261,5 +279,5 @@ # print out the match tables if (dfile) - dump_dfile(dfile) + dump_dfile(dfile, type) }