xref: /netbsd-src/sys/dev/ic/pl181.c (revision c7fb772b85b2b5d4cfb282f868f454b4701534fd)
1*c7fb772bSthorpej /* $NetBSD: pl181.c,v 1.9 2021/08/07 16:19:12 thorpej Exp $ */
214e804e4Sjmcneill 
314e804e4Sjmcneill /*-
414e804e4Sjmcneill  * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
514e804e4Sjmcneill  * All rights reserved.
614e804e4Sjmcneill  *
714e804e4Sjmcneill  * Redistribution and use in source and binary forms, with or without
814e804e4Sjmcneill  * modification, are permitted provided that the following conditions
914e804e4Sjmcneill  * are met:
1014e804e4Sjmcneill  * 1. Redistributions of source code must retain the above copyright
1114e804e4Sjmcneill  *    notice, this list of conditions and the following disclaimer.
1214e804e4Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
1314e804e4Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
1414e804e4Sjmcneill  *    documentation and/or other materials provided with the distribution.
1514e804e4Sjmcneill  *
1614e804e4Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1714e804e4Sjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1814e804e4Sjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1914e804e4Sjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2014e804e4Sjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2114e804e4Sjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2214e804e4Sjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2314e804e4Sjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2414e804e4Sjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2514e804e4Sjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2614e804e4Sjmcneill  * SUCH DAMAGE.
2714e804e4Sjmcneill  */
2814e804e4Sjmcneill 
2914e804e4Sjmcneill #include <sys/cdefs.h>
30*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: pl181.c,v 1.9 2021/08/07 16:19:12 thorpej Exp $");
3114e804e4Sjmcneill 
3214e804e4Sjmcneill #include <sys/param.h>
3314e804e4Sjmcneill #include <sys/bus.h>
3414e804e4Sjmcneill #include <sys/device.h>
3514e804e4Sjmcneill #include <sys/intr.h>
3614e804e4Sjmcneill #include <sys/systm.h>
3714e804e4Sjmcneill #include <sys/kernel.h>
3814e804e4Sjmcneill 
3914e804e4Sjmcneill #include <dev/sdmmc/sdmmcvar.h>
4014e804e4Sjmcneill #include <dev/sdmmc/sdmmcchip.h>
4114e804e4Sjmcneill #include <dev/sdmmc/sdmmc_ioreg.h>
4214e804e4Sjmcneill 
4314e804e4Sjmcneill #include <dev/ic/pl181reg.h>
4414e804e4Sjmcneill #include <dev/ic/pl181var.h>
4514e804e4Sjmcneill 
460b3c41cbSjmcneill /*
470b3c41cbSjmcneill  * Data length register is 16 bits for a maximum of 65535 bytes. Round
480b3c41cbSjmcneill  * maximum transfer size down to the nearest sector.
490b3c41cbSjmcneill  */
500b3c41cbSjmcneill #define	PLMMC_MAXXFER	rounddown(65535, SDMMC_SECTOR_SIZE)
510b3c41cbSjmcneill 
525dbf83ecSjmcneill /*
535dbf83ecSjmcneill  * PL181 FIFO is 16 words deep (64 bytes)
545dbf83ecSjmcneill  */
555dbf83ecSjmcneill #define	PL181_FIFO_DEPTH	64
565dbf83ecSjmcneill 
575dbf83ecSjmcneill /*
585dbf83ecSjmcneill  * Data transfer IRQ status bits
595dbf83ecSjmcneill  */
605dbf83ecSjmcneill #define	PLMMC_INT_DATA_MASK						\
615dbf83ecSjmcneill 	(MMCI_INT_DATA_TIMEOUT|MMCI_INT_DATA_CRC_FAIL|			\
625dbf83ecSjmcneill 	 MMCI_INT_TX_FIFO_EMPTY|MMCI_INT_TX_FIFO_HALF_EMPTY|		\
635dbf83ecSjmcneill 	 MMCI_INT_RX_FIFO_FULL|MMCI_INT_RX_FIFO_HALF_FULL|		\
645dbf83ecSjmcneill 	 MMCI_INT_DATA_END|MMCI_INT_DATA_BLOCK_END)
655dbf83ecSjmcneill #define	PLMMC_INT_CMD_MASK						\
665dbf83ecSjmcneill 	(MMCI_INT_CMD_TIMEOUT|MMCI_INT_CMD_RESP_END)
675dbf83ecSjmcneill 
6814e804e4Sjmcneill static int	plmmc_host_reset(sdmmc_chipset_handle_t);
6914e804e4Sjmcneill static uint32_t	plmmc_host_ocr(sdmmc_chipset_handle_t);
7014e804e4Sjmcneill static int	plmmc_host_maxblklen(sdmmc_chipset_handle_t);
7114e804e4Sjmcneill static int	plmmc_card_detect(sdmmc_chipset_handle_t);
7214e804e4Sjmcneill static int	plmmc_write_protect(sdmmc_chipset_handle_t);
7314e804e4Sjmcneill static int	plmmc_bus_power(sdmmc_chipset_handle_t, uint32_t);
7414e804e4Sjmcneill static int	plmmc_bus_clock(sdmmc_chipset_handle_t, int);
7514e804e4Sjmcneill static int	plmmc_bus_width(sdmmc_chipset_handle_t, int);
7614e804e4Sjmcneill static int	plmmc_bus_rod(sdmmc_chipset_handle_t, int);
7714e804e4Sjmcneill static void	plmmc_exec_command(sdmmc_chipset_handle_t,
7814e804e4Sjmcneill 				     struct sdmmc_command *);
7914e804e4Sjmcneill static void	plmmc_card_enable_intr(sdmmc_chipset_handle_t, int);
8014e804e4Sjmcneill static void	plmmc_card_intr_ack(sdmmc_chipset_handle_t);
8114e804e4Sjmcneill 
825dbf83ecSjmcneill static int	plmmc_wait_cmd(struct plmmc_softc *);
8314e804e4Sjmcneill static int	plmmc_pio_transfer(struct plmmc_softc *,
840b3c41cbSjmcneill 				     struct sdmmc_command *, int);
8514e804e4Sjmcneill 
8614e804e4Sjmcneill static struct sdmmc_chip_functions plmmc_chip_functions = {
8714e804e4Sjmcneill 	.host_reset = plmmc_host_reset,
8814e804e4Sjmcneill 	.host_ocr = plmmc_host_ocr,
8914e804e4Sjmcneill 	.host_maxblklen = plmmc_host_maxblklen,
9014e804e4Sjmcneill 	.card_detect = plmmc_card_detect,
9114e804e4Sjmcneill 	.write_protect = plmmc_write_protect,
9214e804e4Sjmcneill 	.bus_power = plmmc_bus_power,
9314e804e4Sjmcneill 	.bus_clock = plmmc_bus_clock,
9414e804e4Sjmcneill 	.bus_width = plmmc_bus_width,
9514e804e4Sjmcneill 	.bus_rod = plmmc_bus_rod,
9614e804e4Sjmcneill 	.exec_command = plmmc_exec_command,
9714e804e4Sjmcneill 	.card_enable_intr = plmmc_card_enable_intr,
9814e804e4Sjmcneill 	.card_intr_ack = plmmc_card_intr_ack,
9914e804e4Sjmcneill };
10014e804e4Sjmcneill 
10114e804e4Sjmcneill #define MMCI_WRITE(sc, reg, val) \
10214e804e4Sjmcneill 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
1035dbf83ecSjmcneill #define	MMCI_WRITE_MULTI(sc, reg, datap, cnt) \
1045dbf83ecSjmcneill 	bus_space_write_multi_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (datap), (cnt))
10514e804e4Sjmcneill #define MMCI_READ(sc, reg) \
10614e804e4Sjmcneill 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
1075dbf83ecSjmcneill #define	MMCI_READ_MULTI(sc, reg, datap, cnt) \
1085dbf83ecSjmcneill 	bus_space_read_multi_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (datap), (cnt))
10914e804e4Sjmcneill 
11014e804e4Sjmcneill void
plmmc_init(struct plmmc_softc * sc)11114e804e4Sjmcneill plmmc_init(struct plmmc_softc *sc)
11214e804e4Sjmcneill {
11314e804e4Sjmcneill 	struct sdmmcbus_attach_args saa;
11414e804e4Sjmcneill 
1155dbf83ecSjmcneill 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_BIO);
11614e804e4Sjmcneill 	cv_init(&sc->sc_intr_cv, "plmmcirq");
11714e804e4Sjmcneill 
11814e804e4Sjmcneill #ifdef PLMMC_DEBUG
11914e804e4Sjmcneill 	device_printf(sc->sc_dev, "PeriphID %#x %#x %#x %#x\n",
12014e804e4Sjmcneill 	    MMCI_READ(sc, MMCI_PERIPH_ID0_REG),
12114e804e4Sjmcneill 	    MMCI_READ(sc, MMCI_PERIPH_ID1_REG),
12214e804e4Sjmcneill 	    MMCI_READ(sc, MMCI_PERIPH_ID2_REG),
12314e804e4Sjmcneill 	    MMCI_READ(sc, MMCI_PERIPH_ID3_REG));
12414e804e4Sjmcneill 	device_printf(sc->sc_dev, "PCellID %#x %#x %#x %#x\n",
12514e804e4Sjmcneill 	    MMCI_READ(sc, MMCI_PCELL_ID0_REG),
12614e804e4Sjmcneill 	    MMCI_READ(sc, MMCI_PCELL_ID1_REG),
12714e804e4Sjmcneill 	    MMCI_READ(sc, MMCI_PCELL_ID2_REG),
12814e804e4Sjmcneill 	    MMCI_READ(sc, MMCI_PCELL_ID3_REG));
12914e804e4Sjmcneill #endif
13014e804e4Sjmcneill 
13114e804e4Sjmcneill 	plmmc_bus_clock(sc, 400);
13214e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_POWER_REG, 0);
13314e804e4Sjmcneill 	delay(10000);
13414e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_POWER_REG, MMCI_POWER_CTRL_POWERUP);
13514e804e4Sjmcneill 	delay(10000);
13614e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_POWER_REG, MMCI_POWER_CTRL_POWERON);
13714e804e4Sjmcneill 	plmmc_host_reset(sc);
13814e804e4Sjmcneill 
13914e804e4Sjmcneill 	memset(&saa, 0, sizeof(saa));
14014e804e4Sjmcneill 	saa.saa_busname = "sdmmc";
14114e804e4Sjmcneill 	saa.saa_sct = &plmmc_chip_functions;
14214e804e4Sjmcneill 	saa.saa_sch = sc;
14314e804e4Sjmcneill 	saa.saa_clkmin = 400;
1444060e118Sjmcneill 	saa.saa_clkmax = sc->sc_max_freq > 0 ?
1454060e118Sjmcneill 	    sc->sc_max_freq / 1000 : sc->sc_clock_freq / 1000;
1460b3c41cbSjmcneill 	saa.saa_caps = SMC_CAPS_4BIT_MODE;
14714e804e4Sjmcneill 
148*c7fb772bSthorpej 	sc->sc_sdmmc_dev = config_found(sc->sc_dev, &saa, NULL, CFARGS_NONE);
14914e804e4Sjmcneill }
15014e804e4Sjmcneill 
1515dbf83ecSjmcneill static int
plmmc_intr_xfer(struct plmmc_softc * sc,struct sdmmc_command * cmd)1525dbf83ecSjmcneill plmmc_intr_xfer(struct plmmc_softc *sc, struct sdmmc_command *cmd)
1535dbf83ecSjmcneill {
1545dbf83ecSjmcneill 	uint32_t len;
1555dbf83ecSjmcneill 
1565dbf83ecSjmcneill 	if (cmd == NULL) {
1575dbf83ecSjmcneill 		device_printf(sc->sc_dev, "TX/RX interrupt with no active transfer\n");
1585dbf83ecSjmcneill 		return EINVAL;
1595dbf83ecSjmcneill 	}
1605dbf83ecSjmcneill 
1615dbf83ecSjmcneill 	if (cmd->c_buf == NULL) {
1625dbf83ecSjmcneill 		return EINVAL;
1635dbf83ecSjmcneill 	}
1645dbf83ecSjmcneill 
1655dbf83ecSjmcneill 	const uint32_t fifo_cnt =
1665dbf83ecSjmcneill 	    __SHIFTOUT(MMCI_READ(sc, MMCI_FIFO_CNT_REG), MMCI_FIFO_CNT) * 4;
1675dbf83ecSjmcneill 	if (fifo_cnt > sc->sc_fifo_resid) {
1685dbf83ecSjmcneill 		device_printf(sc->sc_dev, "FIFO counter is out of sync with active transfer\n");
1695dbf83ecSjmcneill 		return EIO;
1705dbf83ecSjmcneill 	}
1715dbf83ecSjmcneill 
1725dbf83ecSjmcneill 	if (cmd->c_flags & SCF_CMD_READ)
1735dbf83ecSjmcneill 		len = sc->sc_fifo_resid - fifo_cnt;
1745dbf83ecSjmcneill 	else
175d1579b2dSriastradh 		len = uimin(sc->sc_fifo_resid, PL181_FIFO_DEPTH);
1765dbf83ecSjmcneill 
1775dbf83ecSjmcneill 	if (len == 0)
1785dbf83ecSjmcneill 		return 0;
1795dbf83ecSjmcneill 
1805dbf83ecSjmcneill 	if (cmd->c_flags & SCF_CMD_READ)
1815dbf83ecSjmcneill 		MMCI_READ_MULTI(sc, MMCI_FIFO_REG, (uint32_t *)cmd->c_buf, len / 4);
1825dbf83ecSjmcneill 	else
1835dbf83ecSjmcneill 		MMCI_WRITE_MULTI(sc, MMCI_FIFO_REG, (uint32_t *)cmd->c_buf, len / 4);
1845dbf83ecSjmcneill 
1855dbf83ecSjmcneill 	sc->sc_fifo_resid -= len;
1865dbf83ecSjmcneill 	cmd->c_resid -= len;
1875dbf83ecSjmcneill 	cmd->c_buf += len;
1885dbf83ecSjmcneill 
1895dbf83ecSjmcneill 	return 0;
1905dbf83ecSjmcneill }
1915dbf83ecSjmcneill 
19214e804e4Sjmcneill int
plmmc_intr(void * priv)19314e804e4Sjmcneill plmmc_intr(void *priv)
19414e804e4Sjmcneill {
19514e804e4Sjmcneill 	struct plmmc_softc *sc = priv;
1965dbf83ecSjmcneill 	uint32_t status, mask;
1975dbf83ecSjmcneill 	int retry = 100000;
19814e804e4Sjmcneill 
1995dbf83ecSjmcneill 	mutex_enter(&sc->sc_lock);
2005dbf83ecSjmcneill 
2015dbf83ecSjmcneill 	while (--retry > 0) {
20214e804e4Sjmcneill 		status = MMCI_READ(sc, MMCI_STATUS_REG);
20314e804e4Sjmcneill #ifdef PLMMC_DEBUG
20414e804e4Sjmcneill 		printf("%s: MMCI_STATUS_REG = %#x\n", __func__, status);
20514e804e4Sjmcneill #endif
2065dbf83ecSjmcneill 		if ((status & sc->sc_status_mask) == 0)
2075dbf83ecSjmcneill 			break;
2085dbf83ecSjmcneill 		MMCI_WRITE(sc, MMCI_CLEAR_REG, status);
2095dbf83ecSjmcneill 		sc->sc_intr_status |= status;
2105dbf83ecSjmcneill 
2115dbf83ecSjmcneill 		if (status & MMCI_INT_CMD_TIMEOUT)
2125dbf83ecSjmcneill 			break;
2135dbf83ecSjmcneill 
2145dbf83ecSjmcneill 		if (status & (MMCI_INT_DATA_TIMEOUT|MMCI_INT_DATA_CRC_FAIL)) {
2155dbf83ecSjmcneill 			device_printf(sc->sc_dev,
2165dbf83ecSjmcneill 			    "data xfer error, status %08x\n", status);
2175dbf83ecSjmcneill 			break;
21814e804e4Sjmcneill 		}
21914e804e4Sjmcneill 
2205dbf83ecSjmcneill 		if (status & (MMCI_INT_TX_FIFO_EMPTY|MMCI_INT_TX_FIFO_HALF_EMPTY|
2215dbf83ecSjmcneill 			      MMCI_INT_RX_FIFO_FULL|MMCI_INT_RX_FIFO_HALF_FULL|
2225dbf83ecSjmcneill 			      MMCI_INT_DATA_END|MMCI_INT_DATA_BLOCK_END)) {
22314e804e4Sjmcneill 
2245dbf83ecSjmcneill 			/* Data transfer in progress */
2255dbf83ecSjmcneill 			if (plmmc_intr_xfer(sc, sc->sc_cmd) == 0 &&
2265dbf83ecSjmcneill 			    sc->sc_fifo_resid == 0) {
2275dbf83ecSjmcneill 				/* Disable data IRQs */
2285dbf83ecSjmcneill 				mask = MMCI_READ(sc, MMCI_MASK0_REG);
2295dbf83ecSjmcneill 				mask &= ~PLMMC_INT_DATA_MASK;
2305dbf83ecSjmcneill 				MMCI_WRITE(sc, MMCI_MASK0_REG, mask);
2315dbf83ecSjmcneill 				/* Ignore data status bits after transfer */
2325dbf83ecSjmcneill 				sc->sc_status_mask &= ~PLMMC_INT_DATA_MASK;
2335dbf83ecSjmcneill 			}
2345dbf83ecSjmcneill 		}
2355dbf83ecSjmcneill 
2365dbf83ecSjmcneill 		if (status & MMCI_INT_CMD_RESP_END)
2375dbf83ecSjmcneill 			cv_broadcast(&sc->sc_intr_cv);
2385dbf83ecSjmcneill 	}
2395dbf83ecSjmcneill 	if (retry == 0) {
2405dbf83ecSjmcneill 		device_printf(sc->sc_dev, "intr handler stuck, fifo resid %d, status %08x\n",
2415dbf83ecSjmcneill 		    sc->sc_fifo_resid, MMCI_READ(sc, MMCI_STATUS_REG));
2425dbf83ecSjmcneill 	}
2435dbf83ecSjmcneill 
2445dbf83ecSjmcneill 	cv_broadcast(&sc->sc_intr_cv);
2455dbf83ecSjmcneill 	mutex_exit(&sc->sc_lock);
24614e804e4Sjmcneill 
24714e804e4Sjmcneill 	return 1;
24814e804e4Sjmcneill }
24914e804e4Sjmcneill 
25014e804e4Sjmcneill static int
plmmc_wait_cmd(struct plmmc_softc * sc)2515dbf83ecSjmcneill plmmc_wait_cmd(struct plmmc_softc *sc)
25214e804e4Sjmcneill {
2535dbf83ecSjmcneill 	int error = 0;
25414e804e4Sjmcneill 
2555dbf83ecSjmcneill 	KASSERT(mutex_owned(&sc->sc_lock));
25614e804e4Sjmcneill 
2575dbf83ecSjmcneill 	while (error == 0) {
2585dbf83ecSjmcneill 		if (sc->sc_intr_status & MMCI_INT_CMD_TIMEOUT) {
2595dbf83ecSjmcneill 			error = ETIMEDOUT;
2605dbf83ecSjmcneill 			break;
2615dbf83ecSjmcneill 		} else if (sc->sc_intr_status & MMCI_INT_CMD_RESP_END) {
2625dbf83ecSjmcneill 			break;
26314e804e4Sjmcneill 		}
26414e804e4Sjmcneill 
2655dbf83ecSjmcneill 		error = cv_timedwait(&sc->sc_intr_cv, &sc->sc_lock, hz * 2);
2665dbf83ecSjmcneill 		if (error != 0)
2675dbf83ecSjmcneill 			break;
26814e804e4Sjmcneill 	}
26914e804e4Sjmcneill 
27014e804e4Sjmcneill 	return error;
27114e804e4Sjmcneill }
27214e804e4Sjmcneill 
27314e804e4Sjmcneill static int
plmmc_pio_transfer(struct plmmc_softc * sc,struct sdmmc_command * cmd,int xferlen)2740b3c41cbSjmcneill plmmc_pio_transfer(struct plmmc_softc *sc, struct sdmmc_command *cmd,
2750b3c41cbSjmcneill     int xferlen)
27614e804e4Sjmcneill {
2775dbf83ecSjmcneill 	int error = 0;
27814e804e4Sjmcneill 
2795dbf83ecSjmcneill 	while (sc->sc_fifo_resid > 0 && error == 0) {
2805dbf83ecSjmcneill 		error = cv_timedwait(&sc->sc_intr_cv,
2815dbf83ecSjmcneill 		    &sc->sc_lock, hz * 5);
2825dbf83ecSjmcneill 		if (error != 0)
2835dbf83ecSjmcneill 			break;
2845dbf83ecSjmcneill 
2855dbf83ecSjmcneill 		if (sc->sc_intr_status & MMCI_INT_DATA_TIMEOUT)
2865dbf83ecSjmcneill 			error = ETIMEDOUT;
2875dbf83ecSjmcneill 		else if (sc->sc_intr_status & MMCI_INT_DATA_CRC_FAIL)
2885dbf83ecSjmcneill 			error = EIO;
28914e804e4Sjmcneill 	}
29014e804e4Sjmcneill 
2915dbf83ecSjmcneill 	return error;
29214e804e4Sjmcneill }
29314e804e4Sjmcneill 
29414e804e4Sjmcneill static int
plmmc_host_reset(sdmmc_chipset_handle_t sch)29514e804e4Sjmcneill plmmc_host_reset(sdmmc_chipset_handle_t sch)
29614e804e4Sjmcneill {
29714e804e4Sjmcneill 	struct plmmc_softc *sc = sch;
29814e804e4Sjmcneill 
29914e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_MASK0_REG, 0);
30014e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_MASK1_REG, 0);
30114e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_CLEAR_REG, 0xffffffff);
30214e804e4Sjmcneill 
30314e804e4Sjmcneill 	return 0;
30414e804e4Sjmcneill }
30514e804e4Sjmcneill 
30614e804e4Sjmcneill static uint32_t
plmmc_host_ocr(sdmmc_chipset_handle_t sch)30714e804e4Sjmcneill plmmc_host_ocr(sdmmc_chipset_handle_t sch)
30814e804e4Sjmcneill {
30914e804e4Sjmcneill 	return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V;
31014e804e4Sjmcneill }
31114e804e4Sjmcneill 
31214e804e4Sjmcneill static int
plmmc_host_maxblklen(sdmmc_chipset_handle_t sch)31314e804e4Sjmcneill plmmc_host_maxblklen(sdmmc_chipset_handle_t sch)
31414e804e4Sjmcneill {
31514e804e4Sjmcneill 	return 2048;
31614e804e4Sjmcneill }
31714e804e4Sjmcneill 
31814e804e4Sjmcneill static int
plmmc_card_detect(sdmmc_chipset_handle_t sch)31914e804e4Sjmcneill plmmc_card_detect(sdmmc_chipset_handle_t sch)
32014e804e4Sjmcneill {
32114e804e4Sjmcneill 	return 1;
32214e804e4Sjmcneill }
32314e804e4Sjmcneill 
32414e804e4Sjmcneill static int
plmmc_write_protect(sdmmc_chipset_handle_t sch)32514e804e4Sjmcneill plmmc_write_protect(sdmmc_chipset_handle_t sch)
32614e804e4Sjmcneill {
32714e804e4Sjmcneill 	return 0;
32814e804e4Sjmcneill }
32914e804e4Sjmcneill 
33014e804e4Sjmcneill static int
plmmc_bus_power(sdmmc_chipset_handle_t sch,uint32_t ocr)33114e804e4Sjmcneill plmmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
33214e804e4Sjmcneill {
33314e804e4Sjmcneill 	return 0;
33414e804e4Sjmcneill }
33514e804e4Sjmcneill 
33614e804e4Sjmcneill static int
plmmc_bus_clock(sdmmc_chipset_handle_t sch,int freq)33714e804e4Sjmcneill plmmc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
33814e804e4Sjmcneill {
33914e804e4Sjmcneill 	struct plmmc_softc *sc = sch;
34014e804e4Sjmcneill 	u_int pll_freq, clk_div;
34114e804e4Sjmcneill 	uint32_t clock;
34214e804e4Sjmcneill 
34314e804e4Sjmcneill 	clock = MMCI_CLOCK_PWRSAVE;
34414e804e4Sjmcneill 	if (freq) {
34514e804e4Sjmcneill 		pll_freq = sc->sc_clock_freq / 1000;
34614e804e4Sjmcneill 		clk_div = (howmany(pll_freq, freq) >> 1) - 1;
34714e804e4Sjmcneill 		clock |= __SHIFTIN(clk_div, MMCI_CLOCK_CLKDIV);
34814e804e4Sjmcneill 		clock |= MMCI_CLOCK_ENABLE;
34914e804e4Sjmcneill 	}
35014e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_CLOCK_REG, clock);
35114e804e4Sjmcneill 
35214e804e4Sjmcneill 	return 0;
35314e804e4Sjmcneill }
35414e804e4Sjmcneill 
35514e804e4Sjmcneill static int
plmmc_bus_width(sdmmc_chipset_handle_t sch,int width)35614e804e4Sjmcneill plmmc_bus_width(sdmmc_chipset_handle_t sch, int width)
35714e804e4Sjmcneill {
35814e804e4Sjmcneill 	return 0;
35914e804e4Sjmcneill }
36014e804e4Sjmcneill 
36114e804e4Sjmcneill static int
plmmc_bus_rod(sdmmc_chipset_handle_t sch,int on)36214e804e4Sjmcneill plmmc_bus_rod(sdmmc_chipset_handle_t sch, int on)
36314e804e4Sjmcneill {
36414e804e4Sjmcneill 	struct plmmc_softc *sc = sch;
36514e804e4Sjmcneill 	uint32_t power;
36614e804e4Sjmcneill 
36714e804e4Sjmcneill 
36814e804e4Sjmcneill 	power = MMCI_READ(sc, MMCI_POWER_REG);
36914e804e4Sjmcneill 	if (on) {
37014e804e4Sjmcneill 		power |= MMCI_POWER_ROD;
37114e804e4Sjmcneill 	} else {
37214e804e4Sjmcneill 		power &= ~MMCI_POWER_ROD;
37314e804e4Sjmcneill 	}
37414e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_POWER_REG, power);
37514e804e4Sjmcneill 
37614e804e4Sjmcneill 	return 0;
37714e804e4Sjmcneill }
37814e804e4Sjmcneill 
37914e804e4Sjmcneill static void
plmmc_do_command(sdmmc_chipset_handle_t sch,struct sdmmc_command * cmd)3800b3c41cbSjmcneill plmmc_do_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
38114e804e4Sjmcneill {
38214e804e4Sjmcneill 	struct plmmc_softc *sc = sch;
38314e804e4Sjmcneill 	uint32_t cmdval = MMCI_COMMAND_ENABLE;
38414e804e4Sjmcneill 
3855dbf83ecSjmcneill 	KASSERT(mutex_owned(&sc->sc_lock));
38614e804e4Sjmcneill 
387d1579b2dSriastradh 	const int xferlen = uimin(cmd->c_resid, PLMMC_MAXXFER);
3880b3c41cbSjmcneill 
3895dbf83ecSjmcneill 	sc->sc_cmd = cmd;
3905dbf83ecSjmcneill 	sc->sc_fifo_resid = xferlen;
3915dbf83ecSjmcneill 	sc->sc_status_mask = ~0U;
3925dbf83ecSjmcneill 	sc->sc_intr_status = 0;
3935dbf83ecSjmcneill 
3940b3c41cbSjmcneill #ifdef PLMMC_DEBUG
3950b3c41cbSjmcneill 	device_printf(sc->sc_dev,
3960b3c41cbSjmcneill 	    "opcode %d flags %#x datalen %d resid %d xferlen %d\n",
3970b3c41cbSjmcneill 	    cmd->c_opcode, cmd->c_flags, cmd->c_datalen, cmd->c_resid, xferlen);
3980b3c41cbSjmcneill #endif
39914e804e4Sjmcneill 
40014e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_COMMAND_REG, 0);
40114e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_MASK0_REG, 0);
40214e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_CLEAR_REG, 0xffffffff);
4035dbf83ecSjmcneill 	MMCI_WRITE(sc, MMCI_MASK0_REG, PLMMC_INT_DATA_MASK | PLMMC_INT_CMD_MASK);
40414e804e4Sjmcneill 
40514e804e4Sjmcneill 	if (cmd->c_flags & SCF_RSP_PRESENT)
40614e804e4Sjmcneill 		cmdval |= MMCI_COMMAND_RESPONSE;
40714e804e4Sjmcneill 	if (cmd->c_flags & SCF_RSP_136)
40814e804e4Sjmcneill 		cmdval |= MMCI_COMMAND_LONGRSP;
40914e804e4Sjmcneill 
4100b3c41cbSjmcneill 	uint32_t arg = cmd->c_arg;
4110b3c41cbSjmcneill 
4120b3c41cbSjmcneill 	if (xferlen > 0) {
4130b3c41cbSjmcneill 		unsigned int nblks = xferlen / cmd->c_blklen;
4140b3c41cbSjmcneill 		if (nblks == 0 || (xferlen % cmd->c_blklen) != 0)
41514e804e4Sjmcneill 			++nblks;
41614e804e4Sjmcneill 
41714e804e4Sjmcneill 		const uint32_t dir = (cmd->c_flags & SCF_CMD_READ) ? 1 : 0;
41814e804e4Sjmcneill 		const uint32_t blksize = ffs(cmd->c_blklen) - 1;
41914e804e4Sjmcneill 
42014e804e4Sjmcneill 		MMCI_WRITE(sc, MMCI_DATA_TIMER_REG, 0xffffffff);
42114e804e4Sjmcneill 		MMCI_WRITE(sc, MMCI_DATA_LENGTH_REG, nblks * cmd->c_blklen);
42214e804e4Sjmcneill 		MMCI_WRITE(sc, MMCI_DATA_CTRL_REG,
42314e804e4Sjmcneill 		    __SHIFTIN(dir, MMCI_DATA_CTRL_DIRECTION) |
42414e804e4Sjmcneill 		    __SHIFTIN(blksize, MMCI_DATA_CTRL_BLOCKSIZE) |
42514e804e4Sjmcneill 		    MMCI_DATA_CTRL_ENABLE);
4260b3c41cbSjmcneill 
4270b3c41cbSjmcneill 		/* Adjust blkno if necessary */
4280b3c41cbSjmcneill 		u_int blkoff =
4290b3c41cbSjmcneill 		    (cmd->c_datalen - cmd->c_resid) / SDMMC_SECTOR_SIZE;
4300b3c41cbSjmcneill 		if (!ISSET(cmd->c_flags, SCF_XFER_SDHC))
4310b3c41cbSjmcneill 			blkoff <<= SDMMC_SECTOR_SIZE_SB;
4320b3c41cbSjmcneill 		arg += blkoff;
43314e804e4Sjmcneill 	}
43414e804e4Sjmcneill 
4350b3c41cbSjmcneill 	MMCI_WRITE(sc, MMCI_ARGUMENT_REG, arg);
43614e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_COMMAND_REG, cmdval | cmd->c_opcode);
43714e804e4Sjmcneill 
4380b3c41cbSjmcneill 	if (xferlen > 0) {
4390b3c41cbSjmcneill 		cmd->c_error = plmmc_pio_transfer(sc, cmd, xferlen);
44014e804e4Sjmcneill 		if (cmd->c_error) {
4415dbf83ecSjmcneill #ifdef PLMMC_DEBUG
4425dbf83ecSjmcneill 			device_printf(sc->sc_dev,
4435dbf83ecSjmcneill 			    "MMCI_STATUS_REG = %08x\n", MMCI_READ(sc, MMCI_STATUS_REG));
4445dbf83ecSjmcneill #endif
44514e804e4Sjmcneill 			device_printf(sc->sc_dev,
44614e804e4Sjmcneill 			    "error (%d) waiting for xfer\n", cmd->c_error);
44714e804e4Sjmcneill 			goto done;
44814e804e4Sjmcneill 		}
44914e804e4Sjmcneill 	}
45014e804e4Sjmcneill 
4510b3c41cbSjmcneill 	if ((cmd->c_flags & SCF_RSP_PRESENT) && cmd->c_resid == 0) {
4525dbf83ecSjmcneill 		cmd->c_error = plmmc_wait_cmd(sc);
45314e804e4Sjmcneill 		if (cmd->c_error) {
45414e804e4Sjmcneill #ifdef PLMMC_DEBUG
45514e804e4Sjmcneill 			device_printf(sc->sc_dev,
45614e804e4Sjmcneill 			    "error (%d) waiting for resp\n", cmd->c_error);
45714e804e4Sjmcneill #endif
45814e804e4Sjmcneill 			goto done;
45914e804e4Sjmcneill 		}
46014e804e4Sjmcneill 
46114e804e4Sjmcneill 		if (cmd->c_flags & SCF_RSP_136) {
46214e804e4Sjmcneill 			cmd->c_resp[3] = MMCI_READ(sc, MMCI_RESP0_REG);
46314e804e4Sjmcneill 			cmd->c_resp[2] = MMCI_READ(sc, MMCI_RESP1_REG);
46414e804e4Sjmcneill 			cmd->c_resp[1] = MMCI_READ(sc, MMCI_RESP2_REG);
46514e804e4Sjmcneill 			cmd->c_resp[0] = MMCI_READ(sc, MMCI_RESP3_REG);
46614e804e4Sjmcneill 			if (cmd->c_flags & SCF_RSP_CRC) {
46714e804e4Sjmcneill 				cmd->c_resp[0] = (cmd->c_resp[0] >> 8) |
46814e804e4Sjmcneill 				    (cmd->c_resp[1] << 24);
46914e804e4Sjmcneill 				cmd->c_resp[1] = (cmd->c_resp[1] >> 8) |
47014e804e4Sjmcneill 				    (cmd->c_resp[2] << 24);
47114e804e4Sjmcneill 				cmd->c_resp[2] = (cmd->c_resp[2] >> 8) |
47214e804e4Sjmcneill 				    (cmd->c_resp[3] << 24);
47314e804e4Sjmcneill 				cmd->c_resp[3] = (cmd->c_resp[3] >> 8);
47414e804e4Sjmcneill 			}
47514e804e4Sjmcneill 		} else {
47614e804e4Sjmcneill 			cmd->c_resp[0] = MMCI_READ(sc, MMCI_RESP0_REG);
47714e804e4Sjmcneill 		}
47814e804e4Sjmcneill 	}
47914e804e4Sjmcneill 
48014e804e4Sjmcneill done:
4815dbf83ecSjmcneill 	sc->sc_cmd = NULL;
4825dbf83ecSjmcneill 
48314e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_COMMAND_REG, 0);
48414e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_MASK0_REG, 0);
48514e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_CLEAR_REG, 0xffffffff);
48614e804e4Sjmcneill 	MMCI_WRITE(sc, MMCI_DATA_CNT_REG, 0);
48714e804e4Sjmcneill 
48814e804e4Sjmcneill #ifdef PLMMC_DEBUG
4895dbf83ecSjmcneill 	device_printf(sc->sc_dev, "status = %#x\n", sc->sc_intr_status);
49014e804e4Sjmcneill #endif
4910b3c41cbSjmcneill }
4920b3c41cbSjmcneill 
4930b3c41cbSjmcneill static void
plmmc_exec_command(sdmmc_chipset_handle_t sch,struct sdmmc_command * cmd)4940b3c41cbSjmcneill plmmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
4950b3c41cbSjmcneill {
4960b3c41cbSjmcneill 	struct plmmc_softc *sc = sch;
4970b3c41cbSjmcneill 
4980b3c41cbSjmcneill #ifdef PLMMC_DEBUG
4990b3c41cbSjmcneill 	device_printf(sc->sc_dev, "opcode %d flags %#x data %p datalen %d\n",
5000b3c41cbSjmcneill 	    cmd->c_opcode, cmd->c_flags, cmd->c_data, cmd->c_datalen);
5010b3c41cbSjmcneill #endif
5020b3c41cbSjmcneill 
5035dbf83ecSjmcneill 	mutex_enter(&sc->sc_lock);
5040b3c41cbSjmcneill 	cmd->c_resid = cmd->c_datalen;
5050b3c41cbSjmcneill 	cmd->c_buf = cmd->c_data;
5060b3c41cbSjmcneill 	do {
5070b3c41cbSjmcneill 		plmmc_do_command(sch, cmd);
5080b3c41cbSjmcneill 
5090b3c41cbSjmcneill 		if (cmd->c_resid > 0 && cmd->c_error == 0) {
5100b3c41cbSjmcneill 			/*
5110b3c41cbSjmcneill 			 * Multi block transfer and there is still data
5120b3c41cbSjmcneill 			 * remaining. Send a stop cmd between transfers.
5130b3c41cbSjmcneill 			 */
5140b3c41cbSjmcneill 			struct sdmmc_command stop_cmd;
5150b3c41cbSjmcneill 			memset(&stop_cmd, 0, sizeof(stop_cmd));
5160b3c41cbSjmcneill 			stop_cmd.c_opcode = MMC_STOP_TRANSMISSION;
5170b3c41cbSjmcneill 			stop_cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1B |
5180b3c41cbSjmcneill 			    SCF_RSP_SPI_R1B;
5190b3c41cbSjmcneill 			plmmc_do_command(sch, &stop_cmd);
5200b3c41cbSjmcneill 		}
5210b3c41cbSjmcneill 	} while (cmd->c_resid > 0 && cmd->c_error == 0);
5220b3c41cbSjmcneill 	cmd->c_flags |= SCF_ITSDONE;
5235dbf83ecSjmcneill 	mutex_exit(&sc->sc_lock);
52414e804e4Sjmcneill }
52514e804e4Sjmcneill 
52614e804e4Sjmcneill static void
plmmc_card_enable_intr(sdmmc_chipset_handle_t sch,int enable)52714e804e4Sjmcneill plmmc_card_enable_intr(sdmmc_chipset_handle_t sch, int enable)
52814e804e4Sjmcneill {
52914e804e4Sjmcneill }
53014e804e4Sjmcneill 
53114e804e4Sjmcneill static void
plmmc_card_intr_ack(sdmmc_chipset_handle_t sch)53214e804e4Sjmcneill plmmc_card_intr_ack(sdmmc_chipset_handle_t sch)
53314e804e4Sjmcneill {
53414e804e4Sjmcneill }
535