From c197a2671fbc08ee04c0abf2b9c9234d1068a3a7 Mon Sep 17 00:00:00 2001 From: Jonathan Matthew Date: Tue, 20 Mar 2018 04:18:41 +0000 Subject: Add hibernate IO path for sdmmc(4). This requires some help from the sdmmc chipset driver, currently only implemented in sdhc(4), but mostly uses the regular path. sdhc(4) also needed the ability to perform IO while cold. ok deraadt@ --- sys/arch/amd64/amd64/hibernate_machdep.c | 8 ++- sys/arch/i386/i386/hibernate_machdep.c | 8 ++- sys/dev/sdmmc/files.sdmmc | 4 +- sys/dev/sdmmc/sdhc.c | 72 ++++++++++++++++++++++- sys/dev/sdmmc/sdmmc.c | 5 +- sys/dev/sdmmc/sdmmc_mem.c | 40 ++++++++++++- sys/dev/sdmmc/sdmmc_scsi.c | 99 +++++++++++++++++++++++++++++++- sys/dev/sdmmc/sdmmcchip.h | 4 +- sys/dev/sdmmc/sdmmcvar.h | 7 ++- 9 files changed, 235 insertions(+), 12 deletions(-) (limited to 'sys') diff --git a/sys/arch/amd64/amd64/hibernate_machdep.c b/sys/arch/amd64/amd64/hibernate_machdep.c index f2185eb836d..c1d7b6a8a0e 100644 --- a/sys/arch/amd64/amd64/hibernate_machdep.c +++ b/sys/arch/amd64/amd64/hibernate_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hibernate_machdep.c,v 1.40 2018/02/10 05:11:06 jmatthew Exp $ */ +/* $OpenBSD: hibernate_machdep.c,v 1.41 2018/03/20 04:18:40 jmatthew Exp $ */ /* * Copyright (c) 2012 Mike Larkin @@ -48,6 +48,7 @@ #include "softraid.h" #include "sd.h" #include "nvme.h" +#include "sdmmc.h" /* Hibernate support */ void hibernate_enter_resume_4k_pte(vaddr_t, paddr_t); @@ -94,6 +95,8 @@ get_hibernate_io_function(dev_t dev) vaddr_t addr, size_t size, int op, void *page); extern int sr_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size, int op, void *page); + extern int sdmmc_scsi_hibernate_io(dev_t dev, daddr_t blkno, + vaddr_t addr, size_t size, int op, void *page); struct device *dv = disk_lookup(&sd_cd, DISKUNIT(dev)); struct { const char *driver; @@ -107,6 +110,9 @@ get_hibernate_io_function(dev_t dev) #endif #if NSOFTRAID > 0 { "softraid", sr_hibernate_io }, +#endif +#if NSDMMC > 0 + { "sdmmc", sdmmc_scsi_hibernate_io }, #endif }; diff --git a/sys/arch/i386/i386/hibernate_machdep.c b/sys/arch/i386/i386/hibernate_machdep.c index 5bc05033673..f98d6c5a49e 100644 --- a/sys/arch/i386/i386/hibernate_machdep.c +++ b/sys/arch/i386/i386/hibernate_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: hibernate_machdep.c,v 1.50 2018/02/10 05:11:06 jmatthew Exp $ */ +/* $OpenBSD: hibernate_machdep.c,v 1.51 2018/03/20 04:18:40 jmatthew Exp $ */ /* * Copyright (c) 2011 Mike Larkin @@ -45,6 +45,7 @@ #include "ahci.h" #include "softraid.h" #include "sd.h" +#include "sdmmc.h" /* Hibernate support */ void hibernate_enter_resume_4k_pte(vaddr_t, paddr_t); @@ -97,6 +98,8 @@ get_hibernate_io_function(dev_t dev) vaddr_t addr, size_t size, int op, void *page); extern int sr_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size, int op, void *page); + extern int sdmmc_scsi_hibernate_io(dev_t dev, daddr_t blkno, + vaddr_t addr, size_t size, int op, void *page); struct device *dv = disk_lookup(&sd_cd, DISKUNIT(dev)); struct { const char *driver; @@ -107,6 +110,9 @@ get_hibernate_io_function(dev_t dev) #endif #if NSOFTRAID > 0 { "softraid", sr_hibernate_io }, +#endif +#if SDMMC > 0 + { "sdmmc", sdmmc_scsi_hibernate_io }, #endif }; diff --git a/sys/dev/sdmmc/files.sdmmc b/sys/dev/sdmmc/files.sdmmc index 1b70aa8672f..c9c05242d23 100644 --- a/sys/dev/sdmmc/files.sdmmc +++ b/sys/dev/sdmmc/files.sdmmc @@ -1,4 +1,4 @@ -# $OpenBSD: files.sdmmc,v 1.5 2017/10/11 17:19:50 patrick Exp $ +# $OpenBSD: files.sdmmc,v 1.6 2018/03/20 04:18:40 jmatthew Exp $ # # Config file and device description for machine-independent SD/MMC code. # Included by ports that need it. @@ -6,7 +6,7 @@ define sdmmc {} device sdmmc: scsi attach sdmmc at sdmmcbus -file dev/sdmmc/sdmmc.c sdmmc +file dev/sdmmc/sdmmc.c sdmmc needs-flag file dev/sdmmc/sdmmc_cis.c sdmmc file dev/sdmmc/sdmmc_io.c sdmmc file dev/sdmmc/sdmmc_mem.c sdmmc diff --git a/sys/dev/sdmmc/sdhc.c b/sys/dev/sdmmc/sdhc.c index e7881a1a607..bfb3596bcc3 100644 --- a/sys/dev/sdmmc/sdhc.c +++ b/sys/dev/sdmmc/sdhc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdhc.c,v 1.57 2018/03/19 21:40:32 kettenis Exp $ */ +/* $OpenBSD: sdhc.c,v 1.58 2018/03/20 04:18:40 jmatthew Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler @@ -96,10 +96,12 @@ void sdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); int sdhc_start_command(struct sdhc_host *, struct sdmmc_command *); int sdhc_wait_state(struct sdhc_host *, u_int32_t, u_int32_t); int sdhc_soft_reset(struct sdhc_host *, int); +int sdhc_wait_intr_cold(struct sdhc_host *, int, int); int sdhc_wait_intr(struct sdhc_host *, int, int); void sdhc_transfer_data(struct sdhc_host *, struct sdmmc_command *); void sdhc_read_data(struct sdhc_host *, u_char *, int); void sdhc_write_data(struct sdhc_host *, u_char *, int); +int sdhc_hibernate_init(sdmmc_chipset_handle_t, void *); #ifdef SDHC_DEBUG int sdhcdebug = 0; @@ -127,7 +129,9 @@ struct sdmmc_chip_functions sdhc_functions = { sdhc_card_intr_mask, sdhc_card_intr_ack, /* UHS functions */ - sdhc_signal_voltage + sdhc_signal_voltage, + /* hibernate */ + sdhc_hibernate_init, }; struct cfdriver sdhc_cd = { @@ -1073,12 +1077,65 @@ sdhc_soft_reset(struct sdhc_host *hp, int mask) return (0); } +int +sdhc_wait_intr_cold(struct sdhc_host *hp, int mask, int timo) +{ + int status; + + mask |= SDHC_ERROR_INTERRUPT; + timo = timo * tick; + status = hp->intr_status; + while ((status & mask) == 0) { + + status = HREAD2(hp, SDHC_NINTR_STATUS); + if (ISSET(status, SDHC_NINTR_STATUS_MASK)) { + HWRITE2(hp, SDHC_NINTR_STATUS, status); + if (ISSET(status, SDHC_ERROR_INTERRUPT)) { + uint16_t error; + error = HREAD2(hp, SDHC_EINTR_STATUS); + HWRITE2(hp, SDHC_EINTR_STATUS, error); + hp->intr_status |= status; + + if (ISSET(error, SDHC_CMD_TIMEOUT_ERROR| + SDHC_DATA_TIMEOUT_ERROR)) + break; + } + + if (ISSET(status, SDHC_BUFFER_READ_READY | + SDHC_BUFFER_WRITE_READY | SDHC_COMMAND_COMPLETE | + SDHC_TRANSFER_COMPLETE)) { + hp->intr_status |= status; + break; + } + + if (ISSET(status, SDHC_CARD_INTERRUPT)) { + HSET2(hp, SDHC_NINTR_STATUS_EN, + SDHC_CARD_INTERRUPT); + } + + continue; + } + + delay(1); + if (timo-- == 0) { + status |= SDHC_ERROR_INTERRUPT; + break; + } + } + + hp->intr_status &= ~(status & mask); + return (status & mask); +} + int sdhc_wait_intr(struct sdhc_host *hp, int mask, int timo) { int status; int s; + if (cold) + return (sdhc_wait_intr_cold(hp, mask, timo)); + mask |= SDHC_ERROR_INTERRUPT; s = splsdmmc(); @@ -1222,3 +1279,14 @@ sdhc_dump_regs(struct sdhc_host *hp) HREAD4(hp, SDHC_MAX_CAPABILITIES)); } #endif + +int +sdhc_hibernate_init(sdmmc_chipset_handle_t sch, void *fake_softc) +{ + struct sdhc_host *hp, *fhp; + fhp = fake_softc; + hp = sch; + *fhp = *hp; + + return (0); +} diff --git a/sys/dev/sdmmc/sdmmc.c b/sys/dev/sdmmc/sdmmc.c index bb7d4cc96b6..7a7fc01389e 100644 --- a/sys/dev/sdmmc/sdmmc.c +++ b/sys/dev/sdmmc/sdmmc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmc.c,v 1.49 2018/02/11 20:58:40 patrick Exp $ */ +/* $OpenBSD: sdmmc.c,v 1.50 2018/03/20 04:18:40 jmatthew Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler @@ -630,7 +630,8 @@ sdmmc_mmc_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd) #endif error = cmd->c_error; - wakeup(cmd); + if (!cold) + wakeup(cmd); return error; } diff --git a/sys/dev/sdmmc/sdmmc_mem.c b/sys/dev/sdmmc/sdmmc_mem.c index 4d926ff0f63..8c7f1728e26 100644 --- a/sys/dev/sdmmc/sdmmc_mem.c +++ b/sys/dev/sdmmc/sdmmc_mem.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmc_mem.c,v 1.30 2017/04/06 03:15:29 deraadt Exp $ */ +/* $OpenBSD: sdmmc_mem.c,v 1.31 2018/03/20 04:18:40 jmatthew Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler @@ -28,6 +28,10 @@ #include #include +#ifdef HIBERNATE +#include +#endif + typedef struct { uint32_t _bits[512/32]; } __packed __aligned(4) sdmmc_bitfield512_t; void sdmmc_be512_to_bitfield512(sdmmc_bitfield512_t *); @@ -1104,3 +1108,37 @@ out: rw_exit(&sc->sc_lock); return (error); } + +#ifdef HIBERNATE +int +sdmmc_mem_hibernate_write(struct sdmmc_function *sf, daddr_t blkno, + u_char *data, size_t datalen) +{ + struct sdmmc_softc *sc = sf->sc; + int i, error; + struct bus_dmamap dmamap; + paddr_t phys_addr; + + if (ISSET(sc->sc_caps, SMC_CAPS_SINGLE_ONLY)) { + for (i = 0; i < datalen / sf->csd.sector_size; i++) { + error = sdmmc_mem_write_block_subr(sf, NULL, blkno + i, + data + i * sf->csd.sector_size, + sf->csd.sector_size); + if (error) + return (error); + } + } else if (!ISSET(sc->sc_caps, SMC_CAPS_DMA)) { + return (sdmmc_mem_write_block_subr(sf, NULL, blkno, data, + datalen)); + } + + /* pretend we're bus_dmamap_load */ + bzero(&dmamap, sizeof(dmamap)); + pmap_extract(pmap_kernel(), (vaddr_t)data, &phys_addr); + dmamap.dm_mapsize = datalen; + dmamap.dm_nsegs = 1; + dmamap.dm_segs[0].ds_addr = phys_addr; + dmamap.dm_segs[0].ds_len = datalen; + return (sdmmc_mem_write_block_subr(sf, &dmamap, blkno, data, datalen)); +} +#endif diff --git a/sys/dev/sdmmc/sdmmc_scsi.c b/sys/dev/sdmmc/sdmmc_scsi.c index 7c84610a742..3060a76ffba 100644 --- a/sys/dev/sdmmc/sdmmc_scsi.c +++ b/sys/dev/sdmmc/sdmmc_scsi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmc_scsi.c,v 1.40 2017/04/06 17:00:53 deraadt Exp $ */ +/* $OpenBSD: sdmmc_scsi.c,v 1.41 2018/03/20 04:18:40 jmatthew Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler @@ -32,6 +32,13 @@ #include #include +#ifdef HIBERNATE +#include +#include +#include +#include +#endif + #define SDMMC_SCSIID_HOST 0x00 #define SDMMC_SCSIID_MAX 0x0f @@ -552,3 +559,93 @@ sdmmc_scsi_minphys(struct buf *bp, struct scsi_link *sl) minphys(bp); } + +#ifdef HIBERNATE +int +sdmmc_scsi_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size, + int op, void *page) +{ + struct { + struct sdmmc_softc sdmmc_sc; + struct sdmmc_function sdmmc_sf; + daddr_t poffset; + size_t psize; + struct sdmmc_function *orig_sf; + char chipset_softc[0]; /* size depends on the chipset layer */ + } *state = page; + extern struct cfdriver sd_cd; + struct device *disk, *scsibus, *chip, *sdmmc; + struct scsibus_softc *bus_sc; + struct sdmmc_scsi_softc *scsi_sc; + struct scsi_link *link; + struct sdmmc_function *sf; + struct sdmmc_softc *sc; + int error; + + switch (op) { + case HIB_INIT: + /* find device (sdmmc_softc, sdmmc_function) */ + disk = disk_lookup(&sd_cd, DISKUNIT(dev)); + scsibus = disk->dv_parent; + sdmmc = scsibus->dv_parent; + chip = sdmmc->dv_parent; + + bus_sc = (struct scsibus_softc *)scsibus; + scsi_sc = (struct sdmmc_scsi_softc *)scsibus; + SLIST_FOREACH(link, &bus_sc->sc_link_list, bus_list) { + if (link->device_softc == disk) { + sc = (struct sdmmc_softc *)link->adapter_softc; + scsi_sc = sc->sc_scsibus; + sf = scsi_sc->sc_tgt[link->target].card; + } + } + + /* if the chipset doesn't do hibernate, bail out now */ + sc = (struct sdmmc_softc *)sdmmc; + if (sc->sct->hibernate_init == NULL) + return (ENOTTY); + + state->sdmmc_sc = *sc; + state->sdmmc_sf = *sf; + state->sdmmc_sf.sc = &state->sdmmc_sc; + + /* pretend we own the lock */ + state->sdmmc_sc.sc_lock.rwl_owner = + (((long)curproc) & ~RWLOCK_MASK) | RWLOCK_WRLOCK; + + /* build chip layer fake softc */ + error = state->sdmmc_sc.sct->hibernate_init(state->sdmmc_sc.sch, + &state->chipset_softc); + if (error) + return (error); + state->sdmmc_sc.sch = state->chipset_softc; + + /* make sure we're talking to the right target */ + state->orig_sf = sc->sc_card; + error = sdmmc_select_card(&state->sdmmc_sc, &state->sdmmc_sf); + if (error) + return (error); + + state->poffset = blkno; + state->psize = size; + return (0); + + case HIB_W: + if (blkno > state->psize) + return (E2BIG); + return (sdmmc_mem_hibernate_write(&state->sdmmc_sf, + blkno + state->poffset, (u_char *)addr, size)); + + case HIB_DONE: + /* + * bring the hardware state back into line with the real + * softc by operating on the fake one + */ + sdmmc_select_card(&state->sdmmc_sc, state->orig_sf); + return (0); + } + + return (EINVAL); +} + +#endif diff --git a/sys/dev/sdmmc/sdmmcchip.h b/sys/dev/sdmmc/sdmmcchip.h index 464521602f7..a370bf10638 100644 --- a/sys/dev/sdmmc/sdmmcchip.h +++ b/sys/dev/sdmmc/sdmmcchip.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmcchip.h,v 1.10 2017/12/24 12:55:52 kettenis Exp $ */ +/* $OpenBSD: sdmmcchip.h,v 1.11 2018/03/20 04:18:40 jmatthew Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler @@ -46,6 +46,8 @@ struct sdmmc_chip_functions { void (*card_intr_ack)(sdmmc_chipset_handle_t); /* UHS functions */ int (*signal_voltage)(sdmmc_chipset_handle_t, int); + /* hibernate */ + int (*hibernate_init)(sdmmc_chipset_handle_t, void *); }; /* host controller reset */ diff --git a/sys/dev/sdmmc/sdmmcvar.h b/sys/dev/sdmmc/sdmmcvar.h index 0510bdaa6d1..af8a2f85506 100644 --- a/sys/dev/sdmmc/sdmmcvar.h +++ b/sys/dev/sdmmc/sdmmcvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmcvar.h,v 1.28 2018/02/11 20:58:40 patrick Exp $ */ +/* $OpenBSD: sdmmcvar.h,v 1.29 2018/03/20 04:18:40 jmatthew Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler @@ -280,6 +280,11 @@ int sdmmc_mem_init(struct sdmmc_softc *, struct sdmmc_function *); int sdmmc_mem_read_block(struct sdmmc_function *, int, u_char *, size_t); int sdmmc_mem_write_block(struct sdmmc_function *, int, u_char *, size_t); +#ifdef HIBERNATE +int sdmmc_mem_hibernate_write(struct sdmmc_function *, daddr_t, u_char *, + size_t); +#endif + /* ioctls */ #include -- cgit v1.2.3