root/sys/dev/sdmmc/sdmmcvar.h
/*      $NetBSD: sdmmcvar.h,v 1.40 2026/07/01 19:02:41 yurix Exp $      */
/*      $OpenBSD: sdmmcvar.h,v 1.13 2009/01/09 10:55:22 jsg Exp $       */

/*
 * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _SDMMCVAR_H_
#define _SDMMCVAR_H_

#ifdef _KERNEL_OPT
#include "opt_sdmmc.h"
#endif

#include <sys/queue.h>
#include <sys/mutex.h>
#include <sys/callout.h>
#include <sys/evcnt.h>

#include <sys/bus.h>

#include <dev/sdmmc/sdmmcchip.h>
#include <dev/sdmmc/sdmmcreg.h>

#define SDMMC_SECTOR_SIZE_SB    9
#define SDMMC_SECTOR_SIZE       (1 << SDMMC_SECTOR_SIZE_SB)     /* =512 */

struct sdmmc_csd {
        int     csdver;         /* CSD structure format */
        u_int   mmcver;         /* MMC version (for CID format) */
        int     capacity;       /* total number of sectors */
        int     read_bl_len;    /* block length for reads */
        int     write_bl_len;   /* block length for writes */
        int     r2w_factor;
        int     tran_speed;     /* transfer speed (kbit/s) */
        int     ccc;            /* Card Command Class for SD */
        /* ... */
};

struct sdmmc_ext_csd {
        uint8_t rev;
        uint8_t rst_n_function; /* RST_n_FUNCTION */
        uint32_t cache_size;
};

struct sdmmc_cid {
        int     mid;            /* manufacturer identification number */
        int     oid;            /* OEM/product identification number */
        char    pnm[8];         /* product name (MMC v1 has the longest) */
        int     rev;            /* product revision */
        int     psn;            /* product serial number */
        int     mdt;            /* manufacturing date */
};

struct sdmmc_scr {
        int     sd_spec;
        int     sd_spec3;
        int     sd_spec4;
        int     bus_width;
        bool    support_cmd48;
};

struct sdmmc_ssr {
        bool    cache;          /* cache supported */
};

struct sdmmc_ext_regset {
        bool            valid;
        uint8_t         fno;
        uint32_t        start_addr;
};

struct sdmmc_ext_sd {
        struct sdmmc_ext_regset pef;    /* Performance Enhancement */
};


typedef uint32_t sdmmc_response[4];

struct sdmmc_softc;

struct sdmmc_task {
        void (*func)(void *arg);
        void *arg;
        int onqueue;
        struct sdmmc_softc *sc;
        TAILQ_ENTRY(sdmmc_task) next;
};

#define sdmmc_init_task(xtask, xfunc, xarg)                             \
do {                                                                    \
        (xtask)->func = (xfunc);                                        \
        (xtask)->arg = (xarg);                                          \
        (xtask)->onqueue = 0;                                           \
        (xtask)->sc = NULL;                                             \
} while (/*CONSTCOND*/0)

struct sdmmc_command {
        struct sdmmc_task c_task;       /* task queue entry */
        uint16_t         c_opcode;      /* SD or MMC command index */
        uint32_t         c_arg;         /* SD/MMC command argument */
        sdmmc_response   c_resp;        /* response buffer */
        bus_dmamap_t     c_dmamap;
        void            *c_data;        /* buffer to send or read into */
        int              c_datalen;     /* length of data buffer */
        int              c_blklen;      /* block length */
        int              c_flags;       /* see below */
#define SCF_ITSDONE     (1U << 0)               /* command is complete */
#define SCF_RSP_PRESENT (1U << 1)
#define SCF_RSP_BSY     (1U << 2)
#define SCF_RSP_136     (1U << 3)
#define SCF_RSP_CRC     (1U << 4)
#define SCF_RSP_IDX     (1U << 5)
#define SCF_CMD_READ    (1U << 6)       /* read command (data expected) */
/* non SPI */
#define SCF_CMD_AC      (0U << 8)
#define SCF_CMD_ADTC    (1U << 8)
#define SCF_CMD_BC      (2U << 8)
#define SCF_CMD_BCR     (3U << 8)
#define SCF_CMD_MASK    (3U << 8)
/* SPI */
#define SCF_RSP_SPI_S1  (1U << 10)
#define SCF_RSP_SPI_S2  (1U << 11)
#define SCF_RSP_SPI_B4  (1U << 12)
#define SCF_RSP_SPI_BSY (1U << 13)
/* Probing */
#define SCF_TOUT_OK     (1U << 14)      /* command timeout expected */
/* Command hints */
#define SCF_XFER_SDHC   (1U << 15)      /* card is SDHC */
#define SCF_POLL        (1U << 16)      /* polling required */
#define SCF_NEED_BOUNCE (1U << 17)      /* (driver) transfer requires bounce buffer */
#define SCF_NO_STOP     (1U << 18)      /* don't enable automatic stop CMD12 */
/* response types */
#define SCF_RSP_R0      0       /* none */
#define SCF_RSP_R1      (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
#define SCF_RSP_R1B     (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
#define SCF_RSP_R2      (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_136)
#define SCF_RSP_R3      (SCF_RSP_PRESENT)
#define SCF_RSP_R4      (SCF_RSP_PRESENT)
#define SCF_RSP_R5      (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
#define SCF_RSP_R5B     (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
#define SCF_RSP_R6      (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
#define SCF_RSP_R7      (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
#define SCF_RSP_MASK    (0x1f << 1)
/* SPI */
#define SCF_RSP_SPI_R1  (SCF_RSP_SPI_S1)
#define SCF_RSP_SPI_R1B (SCF_RSP_SPI_S1|SCF_RSP_SPI_BSY)
#define SCF_RSP_SPI_R2  (SCF_RSP_SPI_S1|SCF_RSP_SPI_S2)
#define SCF_RSP_SPI_R3  (SCF_RSP_SPI_S1|SCF_RSP_SPI_B4)
#define SCF_RSP_SPI_R4  (SCF_RSP_SPI_S1|SCF_RSP_SPI_B4)
#define SCF_RSP_SPI_R5  (SCF_RSP_SPI_S1|SCF_RSP_SPI_S2)
#define SCF_RSP_SPI_R7  (SCF_RSP_SPI_S1|SCF_RSP_SPI_B4)
#define SCF_RSP_SPI_MASK (0xf << 10)
        int              c_error;       /* errno value on completion */

        /* Host controller owned fields for data xfer in progress */
        int c_resid;                    /* remaining I/O */
        u_char *c_buf;                  /* remaining data */
};

/*
 * Decoded PC Card 16 based Card Information Structure (CIS),
 * per card (function 0) and per function (1 and greater).
 */
struct sdmmc_cis {
        uint16_t         manufacturer;
#define SDMMC_VENDOR_INVALID    0xffff
        uint16_t         product;
#define SDMMC_PRODUCT_INVALID   0xffff
        uint8_t          function;
#define SDMMC_FUNCTION_INVALID  0xff
        u_char           cis1_major;
        u_char           cis1_minor;
        char             cis1_info_buf[256];
        char            *cis1_info[4];
        uint8_t          lan_nid[6];
};

/*
 * Structure describing either an SD card I/O function or a SD/MMC
 * memory card from a "stack of cards" that responded to CMD2.  For a
 * combo card with one I/O function and one memory card, there will be
 * two of these structures allocated.  Each card slot has such a list
 * of sdmmc_function structures.
 */
struct sdmmc_function {
        /* common members */
        struct sdmmc_softc *sc;         /* card slot softc */
        uint16_t rca;                   /* relative card address */
        int interface;                  /* SD/MMC:0, SDIO:standard interface */
        int width;                      /* bus width */
        u_int blklen;                   /* block length */
        int flags;
#define SFF_ERROR               0x0001  /* function is poo; ignore it */
#define SFF_SDHC                0x0002  /* SD High Capacity card */
#define SFF_CACHE_ENABLED       0x0004  /* cache enabled */
        SIMPLEQ_ENTRY(sdmmc_function) sf_list;
        /* SD card I/O function members */
        int number;                     /* I/O function number or -1 */
        device_t child;                 /* function driver */
        struct sdmmc_cis cis;           /* decoded CIS */
        /* SD/MMC memory card members */
        struct sdmmc_csd csd;           /* decoded CSD value */
        struct sdmmc_ext_csd ext_csd;   /* decoded EXT_CSD value */
        struct sdmmc_cid cid;           /* decoded CID value */
        sdmmc_response raw_cid;         /* temp. storage for decoding */
        uint32_t raw_scr[2];
        struct sdmmc_scr scr;           /* decoded SCR value */
        struct sdmmc_ssr ssr;           /* decoded SSR value */
        struct sdmmc_ext_sd ext_sd;     /* decoded SD extension value */

        void *bbuf;                     /* bounce buffer */
        bus_dmamap_t bbuf_dmap;         /* DMA map for bounce buffer */
        bus_dmamap_t sseg_dmap;         /* DMA map for single segment */
};

/*
 * Structure describing a single SD/MMC/SDIO card slot.
 */
struct sdmmc_softc {
        device_t sc_dev;                /* base device */
#define SDMMCDEVNAME(sc)        (device_xname(sc->sc_dev))

        sdmmc_chipset_tag_t sc_sct;     /* host controller chipset tag */
        sdmmc_spi_chipset_tag_t sc_spi_sct;
        sdmmc_chipset_handle_t sc_sch;  /* host controller chipset handle */
        bus_dma_tag_t sc_dmat;
        bus_dmamap_t sc_dmap;
#define SDMMC_MAXNSEGS          ((MAXPHYS / PAGE_SIZE) + 1)
        bus_addr_t sc_dma_align_mask;   /* DMA alignment required */

        struct kmutex sc_mtx;           /* lock around host controller */
        int sc_dying;                   /* bus driver is shutting down */

        uint32_t sc_flags;
#define SMF_INITED              0x0001
#define SMF_SD_MODE             0x0002  /* host in SD mode (MMC otherwise) */
#define SMF_IO_MODE             0x0004  /* host in I/O mode (SD mode only) */
#define SMF_MEM_MODE            0x0008  /* host in memory mode (SD or MMC) */
#define SMF_CARD_PRESENT        0x4000  /* card presence noticed */
#define SMF_CARD_ATTACHED       0x8000  /* card driver(s) attached */
#define SMF_UHS_MODE            0x10000 /* host in UHS mode */

        uint32_t sc_caps;               /* host capability */
#define SMC_CAPS_AUTO_STOP      __BIT(0)        /* send CMD12 automagically by host */
#define SMC_CAPS_4BIT_MODE      __BIT(1)        /* 4-bits data bus width */
#define SMC_CAPS_DMA            __BIT(2)        /* DMA transfer */
#define SMC_CAPS_SPI_MODE       __BIT(3)        /* SPI mode */
#define SMC_CAPS_POLL_CARD_DET  __BIT(4)        /* Polling card detect */
#define SMC_CAPS_SINGLE_ONLY    __BIT(5)        /* only single read/write */
#define SMC_CAPS_8BIT_MODE      __BIT(6)        /* 8-bits data bus width */
#define SMC_CAPS_MULTI_SEG_DMA  __BIT(7)        /* multiple segment DMA transfer */
#define SMC_CAPS_SD_HIGHSPEED   __BIT(8)        /* SD high-speed timing */
#define SMC_CAPS_MMC_HIGHSPEED  __BIT(9)        /* MMC high-speed timing */
#define SMC_CAPS_MMC_DDR52      __BIT(10)       /* MMC HS DDR52 timing */
/*                              __BIT(11)       */
#define SMC_CAPS_UHS_SDR50      __BIT(12)       /* UHS SDR50 timing */
#define SMC_CAPS_UHS_SDR104     __BIT(13)       /* UHS SDR104 timing */
#define SMC_CAPS_UHS_DDR50      __BIT(14)       /* UHS DDR50 timing */
#define SMC_CAPS_UHS_MASK       (SMC_CAPS_UHS_SDR50 \
                                    | SMC_CAPS_UHS_SDR104 \
                                    | SMC_CAPS_UHS_DDR50)
#define SMC_CAPS_MMC_HS200      __BIT(15)       /* eMMC HS200 timing */
#define SMC_CAPS_POLLING        __BIT(30)       /* driver supports cmd polling */

        /* function */
        int sc_function_count;          /* number of I/O functions (SDIO) */
        struct sdmmc_function *sc_card; /* selected card */
        struct sdmmc_function *sc_fn0;  /* function 0, the card itself */
        SIMPLEQ_HEAD(, sdmmc_function) sf_head; /* list of card functions */

        /* task queue */
        struct lwp *sc_tskq_lwp;        /* asynchronous tasks */
        TAILQ_HEAD(, sdmmc_task) sc_tskq;   /* task thread work queue */
        struct kmutex sc_tskq_mtx;
        struct kcondvar sc_tskq_cv;
        struct sdmmc_task *sc_curtask;

        /* discover task */
        struct sdmmc_task sc_discover_task; /* card attach/detach task */
        struct kmutex sc_discover_task_mtx;

        /* interrupt task */
        struct sdmmc_task sc_intr_task; /* card interrupt task */
        TAILQ_HEAD(, sdmmc_intr_handler) sc_intrq; /* interrupt handlers */

        u_int sc_clkmin;                /* host min bus clock */
        u_int sc_clkmax;                /* host max bus clock */
        u_int sc_busclk;                /* host bus clock */
        bool sc_busddr;                 /* host bus clock is in DDR mode */
        int sc_buswidth;                /* host bus width */
        const char *sc_transfer_mode;   /* current transfer mode */

        callout_t sc_card_detect_ch;    /* polling card insert/remove */

        /* event counters */
        struct evcnt sc_ev_xfer;        /* xfer count */
        struct evcnt sc_ev_xfer_aligned[8]; /* aligned xfer counts */
        struct evcnt sc_ev_xfer_unaligned; /* unaligned xfer count */
        struct evcnt sc_ev_xfer_error;  /* error xfer count */

        uint32_t sc_max_seg;            /* maximum segment size */
};

/*
 * Attach devices at the sdmmc bus.
 */
struct sdmmc_attach_args {
        uint16_t manufacturer;
        uint16_t product;
        int interface;
        struct sdmmc_function *sf;
};

struct sdmmc_product {
        uint16_t        pp_vendor;
        uint16_t        pp_product;
        const char      *pp_cisinfo[4];
};

#ifndef IPL_SDMMC
#define IPL_SDMMC       IPL_BIO
#endif

#ifndef splsdmmc
#define splsdmmc()      splbio()
#endif

#define SDMMC_LOCK(sc)
#define SDMMC_UNLOCK(sc)

#ifdef SDMMC_DEBUG
extern int sdmmcdebug;
#endif

void    sdmmc_add_task(struct sdmmc_softc *, struct sdmmc_task *);
bool    sdmmc_del_task(struct sdmmc_softc *, struct sdmmc_task *, kmutex_t *);

struct  sdmmc_function *sdmmc_function_alloc(struct sdmmc_softc *);
void    sdmmc_function_free(struct sdmmc_function *);
int     sdmmc_set_bus_power(struct sdmmc_softc *, uint32_t, uint32_t);
int     sdmmc_mmc_command(struct sdmmc_softc *, struct sdmmc_command *);
int     sdmmc_app_command(struct sdmmc_softc *, struct sdmmc_function *,
            struct sdmmc_command *);
void    sdmmc_stop_transmission(struct sdmmc_softc *);
void    sdmmc_go_idle_state(struct sdmmc_softc *);
int     sdmmc_select_card(struct sdmmc_softc *, struct sdmmc_function *);
int     sdmmc_set_relative_addr(struct sdmmc_softc *, struct sdmmc_function *);

void    sdmmc_intr_enable(struct sdmmc_function *);
void    sdmmc_intr_disable(struct sdmmc_function *);
void    *sdmmc_intr_establish(device_t, int (*)(void *), void *, const char *);
void    sdmmc_intr_disestablish(void *);
void    sdmmc_intr_task(void *);

int     sdmmc_decode_csd(struct sdmmc_softc *, sdmmc_response,
            struct sdmmc_function *);
int     sdmmc_decode_cid(struct sdmmc_softc *, sdmmc_response,
            struct sdmmc_function *);
void    sdmmc_print_cid(struct sdmmc_cid *);
#ifdef SDMMC_DUMP_CSD
void    sdmmc_print_csd(sdmmc_response, struct sdmmc_csd *);
#endif
void    sdmmc_dump_data(const char *, void *, size_t);

int     sdmmc_io_enable(struct sdmmc_softc *);
void    sdmmc_io_scan(struct sdmmc_softc *);
int     sdmmc_io_init(struct sdmmc_softc *, struct sdmmc_function *);
int     sdmmc_io_set_blocklen(struct sdmmc_function *, int);
uint8_t sdmmc_io_read_1(struct sdmmc_function *, int);
uint16_t sdmmc_io_read_2(struct sdmmc_function *, int);
uint32_t sdmmc_io_read_4(struct sdmmc_function *, int);
int     sdmmc_io_read_multi_1(struct sdmmc_function *, int, u_char *, int);
int     sdmmc_io_read_region_1(struct sdmmc_function *, int, u_char *, int);
void    sdmmc_io_write_1(struct sdmmc_function *, int, uint8_t);
void    sdmmc_io_write_2(struct sdmmc_function *, int, uint16_t);
void    sdmmc_io_write_4(struct sdmmc_function *, int, uint32_t);
int     sdmmc_io_write_multi_1(struct sdmmc_function *, int, u_char *, int);
int     sdmmc_io_write_region_1(struct sdmmc_function *, int, u_char *, int);
int     sdmmc_io_function_enable(struct sdmmc_function *);
void    sdmmc_io_function_disable(struct sdmmc_function *);
int     sdmmc_io_function_abort(struct sdmmc_function *);

uint32_t sdmmc_cisptr(struct sdmmc_function *);
int     sdmmc_read_cis(struct sdmmc_function *, struct sdmmc_cis *);
void    sdmmc_print_cis(struct sdmmc_function *);
void    sdmmc_check_cis_quirks(struct sdmmc_function *);

int     sdmmc_mem_enable(struct sdmmc_softc *);
void    sdmmc_mem_scan(struct sdmmc_softc *);
int     sdmmc_mem_init(struct sdmmc_softc *, struct sdmmc_function *);
int     sdmmc_mem_send_op_cond(struct sdmmc_softc *, uint32_t, uint32_t *);
int     sdmmc_mem_send_if_cond(struct sdmmc_softc *, uint32_t, uint32_t *);
int     sdmmc_mem_set_blocklen(struct sdmmc_softc *, struct sdmmc_function *,
            int);
int     sdmmc_mem_read_block(struct sdmmc_function *, uint32_t, u_char *,
            size_t);
int     sdmmc_mem_write_block(struct sdmmc_function *, uint32_t, u_char *,
            size_t);
int     sdmmc_mem_discard(struct sdmmc_function *, uint32_t, uint32_t);
int     sdmmc_mem_flush_cache(struct sdmmc_function *, bool);

void    sdmmc_pause(u_int, kmutex_t *);

static inline bool
sdmmc_alignment_ok(struct sdmmc_softc *sc, bus_dmamap_t dm)
{
        const bus_addr_t align_mask = sc->sc_dma_align_mask;

        if (align_mask != 0) {
                int n;

                for (n = 0; n < dm->dm_nsegs; n++) {
                        if ((dm->dm_segs[n].ds_addr & align_mask) != 0 ||
                            (dm->dm_segs[n].ds_len & align_mask) != 0) {
                                return false;
                        }
                }
        }
        return true;
}

#endif  /* _SDMMCVAR_H_ */