xref: /netbsd-src/sys/dev/marvell/mvspi.c (revision 100a3398b8d3c64e571cff36b46c23431b410e09)
11ed09d9cSrkujawa /*******************************************************************************
21ed09d9cSrkujawa Copyright (C) Marvell International Ltd. and its affiliates
31ed09d9cSrkujawa 
41ed09d9cSrkujawa Developed by Semihalf
51ed09d9cSrkujawa 
61ed09d9cSrkujawa ********************************************************************************
71ed09d9cSrkujawa Marvell BSD License
81ed09d9cSrkujawa 
91ed09d9cSrkujawa If you received this File from Marvell, you may opt to use, redistribute and/or
101ed09d9cSrkujawa modify this File under the following licensing terms.
111ed09d9cSrkujawa Redistribution and use in source and binary forms, with or without modification,
121ed09d9cSrkujawa are permitted provided that the following conditions are met:
131ed09d9cSrkujawa 
141ed09d9cSrkujawa     *   Redistributions of source code must retain the above copyright notice,
151ed09d9cSrkujawa             this list of conditions and the following disclaimer.
161ed09d9cSrkujawa 
171ed09d9cSrkujawa     *   Redistributions in binary form must reproduce the above copyright
181ed09d9cSrkujawa         notice, this list of conditions and the following disclaimer in the
191ed09d9cSrkujawa         documentation and/or other materials provided with the distribution.
201ed09d9cSrkujawa 
211ed09d9cSrkujawa     *   Neither the name of Marvell nor the names of its contributors may be
221ed09d9cSrkujawa         used to endorse or promote products derived from this software without
231ed09d9cSrkujawa         specific prior written permission.
241ed09d9cSrkujawa 
251ed09d9cSrkujawa THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
261ed09d9cSrkujawa ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
271ed09d9cSrkujawa WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
281ed09d9cSrkujawa DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
291ed09d9cSrkujawa ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
301ed09d9cSrkujawa (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
311ed09d9cSrkujawa LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
321ed09d9cSrkujawa ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
331ed09d9cSrkujawa (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
341ed09d9cSrkujawa SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
351ed09d9cSrkujawa 
361ed09d9cSrkujawa *******************************************************************************/
371ed09d9cSrkujawa 
381ed09d9cSrkujawa /*
391ed09d9cSrkujawa  * Transfer mechanism extracted from arspi.c corresponding with the lines
401ed09d9cSrkujawa  * 254-262 in this file.
411ed09d9cSrkujawa  */
421ed09d9cSrkujawa 
431ed09d9cSrkujawa #include <sys/param.h>
441ed09d9cSrkujawa #include <sys/device.h>
451ed09d9cSrkujawa 
461ed09d9cSrkujawa #include <dev/spi/spivar.h>
471ed09d9cSrkujawa 
481ed09d9cSrkujawa #include <dev/marvell/mvspireg.h>
491ed09d9cSrkujawa #include <dev/marvell/marvellvar.h>
501ed09d9cSrkujawa 
511ed09d9cSrkujawa #include "locators.h"
521ed09d9cSrkujawa 
531ed09d9cSrkujawa extern uint32_t mvTclk;
541ed09d9cSrkujawa 
551ed09d9cSrkujawa struct mvspi_softc {
561ed09d9cSrkujawa 	struct spi_controller	sc_spi;
571ed09d9cSrkujawa 	void			*sc_ih;
581ed09d9cSrkujawa 	bool			sc_interrupts;
591ed09d9cSrkujawa 
601ed09d9cSrkujawa 	struct spi_transfer	*sc_transfer;
611ed09d9cSrkujawa 	struct spi_chunk	*sc_wchunk;	/* For partial writes */
621ed09d9cSrkujawa 	struct spi_transq	sc_transq;
631ed09d9cSrkujawa 	bus_space_tag_t		sc_st;
641ed09d9cSrkujawa 	bus_space_handle_t	sc_sh;
651ed09d9cSrkujawa 	bus_size_t		sc_size;
661ed09d9cSrkujawa };
671ed09d9cSrkujawa 
681ed09d9cSrkujawa int mvspi_match(struct device *, struct cfdata *, void *);
691ed09d9cSrkujawa void mvspi_attach(struct device *, struct device *, void *);
701ed09d9cSrkujawa /* SPI service routines */
711ed09d9cSrkujawa int mvspi_configure(void *, int, int, int);
721ed09d9cSrkujawa int mvspi_transfer(void *, struct spi_transfer *);
731ed09d9cSrkujawa /* Internal support */
741ed09d9cSrkujawa void mvspi_sched(struct mvspi_softc *);
751ed09d9cSrkujawa void mvspi_assert(struct mvspi_softc *sc);
761ed09d9cSrkujawa void mvspi_deassert(struct mvspi_softc *sc);
771ed09d9cSrkujawa 
781ed09d9cSrkujawa #define	GETREG(sc, x)					\
791ed09d9cSrkujawa 	bus_space_read_4(sc->sc_st, sc->sc_sh, x)
801ed09d9cSrkujawa #define	PUTREG(sc, x, v)				\
811ed09d9cSrkujawa 	bus_space_write_4(sc->sc_st, sc->sc_sh, x, v)
821ed09d9cSrkujawa 
831ed09d9cSrkujawa /* Attach structure */
841ed09d9cSrkujawa CFATTACH_DECL_NEW(mvspi_mbus, sizeof(struct mvspi_softc),
851ed09d9cSrkujawa     mvspi_match, mvspi_attach, NULL, NULL);
861ed09d9cSrkujawa 
871ed09d9cSrkujawa int
mvspi_match(struct device * parent,struct cfdata * cf,void * aux)881ed09d9cSrkujawa mvspi_match(struct device *parent, struct cfdata *cf, void *aux)
891ed09d9cSrkujawa {
901ed09d9cSrkujawa 	struct marvell_attach_args *mva = aux;
911ed09d9cSrkujawa 
921ed09d9cSrkujawa 	if (strcmp(mva->mva_name, cf->cf_name) != 0)
931ed09d9cSrkujawa 		return 0;
941ed09d9cSrkujawa 	if (mva->mva_offset == MVA_OFFSET_DEFAULT ||
951ed09d9cSrkujawa 	    mva->mva_irq == MVA_IRQ_DEFAULT)
961ed09d9cSrkujawa 		return 0;
971ed09d9cSrkujawa 
981ed09d9cSrkujawa 	mva->mva_size = MVSPI_SIZE;
991ed09d9cSrkujawa 	return 1;
1001ed09d9cSrkujawa }
1011ed09d9cSrkujawa 
1021ed09d9cSrkujawa void
mvspi_attach(struct device * parent,struct device * self,void * aux)1031ed09d9cSrkujawa mvspi_attach(struct device *parent, struct device *self, void *aux)
1041ed09d9cSrkujawa {
1051ed09d9cSrkujawa 	struct mvspi_softc *sc =  device_private(self);
1061ed09d9cSrkujawa   	struct marvell_attach_args *mva = aux;
1071ed09d9cSrkujawa 	struct spibus_attach_args sba;
1081ed09d9cSrkujawa 	int ctl;
1091ed09d9cSrkujawa 
1101ed09d9cSrkujawa 	aprint_normal(": Marvell SPI controller\n");
1111ed09d9cSrkujawa 
1121ed09d9cSrkujawa 	/*
1131ed09d9cSrkujawa 	 * Map registers.
1141ed09d9cSrkujawa 	 */
1151ed09d9cSrkujawa 	sc->sc_st = mva->mva_iot;
1161ed09d9cSrkujawa 	sc->sc_size = mva->mva_size;
1171ed09d9cSrkujawa 
1181ed09d9cSrkujawa 	if (bus_space_subregion(sc->sc_st, mva->mva_ioh, mva->mva_offset,
1191ed09d9cSrkujawa 	    mva->mva_size, &sc->sc_sh)) {
1201ed09d9cSrkujawa 		aprint_error_dev(self, "Cannot map registers\n");
1211ed09d9cSrkujawa 		return;
1221ed09d9cSrkujawa 	}
1231ed09d9cSrkujawa 
1241ed09d9cSrkujawa 	/*
1251ed09d9cSrkujawa 	 * Initialize hardware.
1261ed09d9cSrkujawa 	 */
1271ed09d9cSrkujawa 	ctl = GETREG(sc, MVSPI_INTCONF_REG);
1281ed09d9cSrkujawa 
1291ed09d9cSrkujawa 	ctl &= MVSPI_DIRHS_MASK;
1301ed09d9cSrkujawa 	ctl &= MVSPI_1BYTE_MASK;
1311ed09d9cSrkujawa 
1329015c01fSchristos 	PUTREG(sc, MVSPI_INTCONF_REG, ctl);
1331ed09d9cSrkujawa 
1341ed09d9cSrkujawa 	/*
1351ed09d9cSrkujawa 	 * Initialize SPI controller.
1361ed09d9cSrkujawa 	 */
1371ed09d9cSrkujawa 	sc->sc_spi.sct_cookie = sc;
1381ed09d9cSrkujawa 	sc->sc_spi.sct_configure = mvspi_configure;
1391ed09d9cSrkujawa 	sc->sc_spi.sct_transfer = mvspi_transfer;
1401ed09d9cSrkujawa 	sc->sc_spi.sct_nslaves = 1;
1411ed09d9cSrkujawa 
1421ed09d9cSrkujawa 	/*
1431ed09d9cSrkujawa 	 * Initialize the queue.
1441ed09d9cSrkujawa 	 */
1451ed09d9cSrkujawa 	spi_transq_init(&sc->sc_transq);
1461ed09d9cSrkujawa 
1471ed09d9cSrkujawa 	/*
1481ed09d9cSrkujawa 	 * Initialize and attach bus attach.
1491ed09d9cSrkujawa 	 */
1509555f417Stnn 	memset(&sba, 0, sizeof(sba));
1511ed09d9cSrkujawa 	sba.sba_controller = &sc->sc_spi;
152c7fb772bSthorpej 	config_found(self, &sba, spibus_print, CFARGS_NONE);
1531ed09d9cSrkujawa }
1541ed09d9cSrkujawa 
1551ed09d9cSrkujawa int
mvspi_configure(void * cookie,int slave,int mode,int speed)1561ed09d9cSrkujawa mvspi_configure(void *cookie, int slave, int mode, int speed)
1571ed09d9cSrkujawa {
1581ed09d9cSrkujawa 	struct mvspi_softc *sc = cookie;
1591ed09d9cSrkujawa 	uint32_t ctl = 0, spr, sppr;
1601ed09d9cSrkujawa 	uint32_t divider;
1611ed09d9cSrkujawa 	uint32_t best_spr = 0, best_sppr = 0;
1621ed09d9cSrkujawa 	uint32_t best_sppr0, best_spprhi;
1631ed09d9cSrkujawa 	uint8_t exact_match = 0;
1641ed09d9cSrkujawa 	uint32_t min_baud_offset = 0xFFFFFFFF;
1651ed09d9cSrkujawa 
1661ed09d9cSrkujawa 	if (slave < 0 || slave > 7)
1671ed09d9cSrkujawa 		return EINVAL;
1681ed09d9cSrkujawa 
1691ed09d9cSrkujawa 	switch(mode) {
1701ed09d9cSrkujawa 		case SPI_MODE_0:
1711ed09d9cSrkujawa 			ctl &= ~(MVSPI_CPOL_MASK);
1721ed09d9cSrkujawa 			/* In boards documentation, CPHA is inverted */
1731ed09d9cSrkujawa 			ctl &= MVSPI_CPHA_MASK;
1741ed09d9cSrkujawa 			break;
1751ed09d9cSrkujawa 		case SPI_MODE_1:
1761ed09d9cSrkujawa 			ctl |= MVSPI_CPOL_MASK;
1771ed09d9cSrkujawa 			ctl &= MVSPI_CPHA_MASK;
1781ed09d9cSrkujawa 			break;
1791ed09d9cSrkujawa 		case SPI_MODE_2:
1801ed09d9cSrkujawa 			ctl &= ~(MVSPI_CPOL_MASK);
1811ed09d9cSrkujawa 			ctl |= ~(MVSPI_CPHA_MASK);
1821ed09d9cSrkujawa 			break;
1831ed09d9cSrkujawa 		case SPI_MODE_3:
1841ed09d9cSrkujawa 			ctl |= MVSPI_CPOL_MASK;
1851ed09d9cSrkujawa 			ctl |= ~(MVSPI_CPHA_MASK);
1861ed09d9cSrkujawa 			break;
1871ed09d9cSrkujawa 		default:
1881ed09d9cSrkujawa 			return EINVAL;
1891ed09d9cSrkujawa 	}
1901ed09d9cSrkujawa 
1911ed09d9cSrkujawa 	/* Find the best prescale configuration - less or equal:
192*100a3398Sandvar 	 * SPI actual frequency = core_clk / (SPR * (2 ^ SPPR))
1931ed09d9cSrkujawa 	 * Try to find the minimal SPR and SPPR values that offer
1941ed09d9cSrkujawa 	 * the best prescale config.
1951ed09d9cSrkujawa 	 *
1961ed09d9cSrkujawa 	 */
1971ed09d9cSrkujawa 	for (spr = 1; spr <= MVSPI_SPR_MAXVALUE; spr++) {
1981ed09d9cSrkujawa 		for (sppr = 0; sppr <= MVSPI_SPPR_MAXVALUE; sppr++) {
1991ed09d9cSrkujawa 			divider = spr * (1 << sppr);
2001ed09d9cSrkujawa 			/* Check for higher - irrelevant */
2011ed09d9cSrkujawa 			if ((mvTclk / divider) > speed)
2021ed09d9cSrkujawa 				continue;
2031ed09d9cSrkujawa 
2041ed09d9cSrkujawa 			/* Check for exact fit */
2051ed09d9cSrkujawa 			if ((mvTclk / divider) == speed) {
2061ed09d9cSrkujawa 				best_spr = spr;
2071ed09d9cSrkujawa 				best_sppr = sppr;
2081ed09d9cSrkujawa 				exact_match = 1;
2091ed09d9cSrkujawa 				break;
2101ed09d9cSrkujawa 			}
2111ed09d9cSrkujawa 
2121ed09d9cSrkujawa 			/* Check if this is better than the previous one */
2131ed09d9cSrkujawa 			if ((speed - (mvTclk / divider)) < min_baud_offset) {
2141ed09d9cSrkujawa 				min_baud_offset = (speed - (mvTclk / divider));
2151ed09d9cSrkujawa 				best_spr = spr;
2161ed09d9cSrkujawa 				best_sppr = sppr;
2171ed09d9cSrkujawa 			}
2181ed09d9cSrkujawa 		}
2191ed09d9cSrkujawa 
2201ed09d9cSrkujawa 		if (exact_match == 1)
2211ed09d9cSrkujawa 			break;
2221ed09d9cSrkujawa 	}
2231ed09d9cSrkujawa 
2241ed09d9cSrkujawa 	if (best_spr == 0) {
2251ed09d9cSrkujawa 		printf("%s ERROR: SPI baud rate prescale error!\n", __func__);
2261ed09d9cSrkujawa 		return -1;
2271ed09d9cSrkujawa 	}
2281ed09d9cSrkujawa 
2291ed09d9cSrkujawa 	ctl &= ~(MVSPI_SPR_MASK);
2301ed09d9cSrkujawa 	ctl &= ~(MVSPI_SPPR_MASK);
2311ed09d9cSrkujawa 	ctl |= best_spr;
2321ed09d9cSrkujawa 
2331ed09d9cSrkujawa 	best_spprhi = best_sppr & MVSPI_SPPRHI_MASK;
2341ed09d9cSrkujawa 	best_spprhi = best_spprhi << 5;
2351ed09d9cSrkujawa 
2361ed09d9cSrkujawa 	ctl |= best_spprhi;
2371ed09d9cSrkujawa 
2381ed09d9cSrkujawa 	best_sppr0 = best_sppr & MVSPI_SPPR0_MASK;
2391ed09d9cSrkujawa 	best_sppr0 = best_sppr0 << 4;
2401ed09d9cSrkujawa 
2411ed09d9cSrkujawa 	ctl |= best_sppr0;
2421ed09d9cSrkujawa 
2431ed09d9cSrkujawa 	PUTREG(sc, MVSPI_INTCONF_REG, ctl);
2441ed09d9cSrkujawa 
2451ed09d9cSrkujawa 	return 0;
2461ed09d9cSrkujawa }
2471ed09d9cSrkujawa 
2481ed09d9cSrkujawa int
mvspi_transfer(void * cookie,struct spi_transfer * st)2491ed09d9cSrkujawa mvspi_transfer(void *cookie, struct spi_transfer *st)
2501ed09d9cSrkujawa {
2511ed09d9cSrkujawa 	struct mvspi_softc *sc = cookie;
252706bb852Skhorben 	int s;
2531ed09d9cSrkujawa 
2541ed09d9cSrkujawa 	s = splbio();
2551ed09d9cSrkujawa 	spi_transq_enqueue(&sc->sc_transq, st);
2561ed09d9cSrkujawa 	if (sc->sc_transfer == NULL) {
2571ed09d9cSrkujawa 		mvspi_sched(sc);
2581ed09d9cSrkujawa 	}
2591ed09d9cSrkujawa 	splx(s);
2601ed09d9cSrkujawa 	return 0;
2611ed09d9cSrkujawa }
2621ed09d9cSrkujawa 
2631ed09d9cSrkujawa void
mvspi_assert(struct mvspi_softc * sc)2641ed09d9cSrkujawa mvspi_assert(struct mvspi_softc *sc)
2651ed09d9cSrkujawa {
2661ed09d9cSrkujawa 	int ctl;
2671ed09d9cSrkujawa 
2685d545ea0Schristos 	if (sc->sc_transfer->st_slave < 0 || sc->sc_transfer->st_slave > 7) {
2691ed09d9cSrkujawa 		printf("%s ERROR: Slave number %d not valid!\n",  __func__, sc->sc_transfer->st_slave);
2701ed09d9cSrkujawa 		return;
2711ed09d9cSrkujawa 	} else
2721ed09d9cSrkujawa 		/* Enable appropriate CSn according to its slave number */
2731ed09d9cSrkujawa 		PUTREG(sc, MVSPI_CTRL_REG, (sc->sc_transfer->st_slave << 2));
2741ed09d9cSrkujawa 
2751ed09d9cSrkujawa 	/* Enable CSnAct */
2761ed09d9cSrkujawa 	ctl = GETREG(sc, MVSPI_CTRL_REG);
2771ed09d9cSrkujawa 	ctl |= MVSPI_CSNACT_MASK;
2781ed09d9cSrkujawa 	PUTREG(sc, MVSPI_CTRL_REG, ctl);
2791ed09d9cSrkujawa }
2801ed09d9cSrkujawa 
2811ed09d9cSrkujawa void
mvspi_deassert(struct mvspi_softc * sc)2821ed09d9cSrkujawa mvspi_deassert(struct mvspi_softc *sc)
2831ed09d9cSrkujawa {
2841ed09d9cSrkujawa 	int ctl = GETREG(sc, MVSPI_CTRL_REG);
2851ed09d9cSrkujawa 	ctl &= ~(MVSPI_CSNACT_MASK);
2861ed09d9cSrkujawa 	PUTREG(sc, MVSPI_CTRL_REG, ctl);
2871ed09d9cSrkujawa }
2881ed09d9cSrkujawa 
2891ed09d9cSrkujawa void
mvspi_sched(struct mvspi_softc * sc)2901ed09d9cSrkujawa mvspi_sched(struct mvspi_softc *sc)
2911ed09d9cSrkujawa {
2921ed09d9cSrkujawa 	struct spi_transfer *st;
2931ed09d9cSrkujawa 	struct spi_chunk *chunk;
2941ed09d9cSrkujawa 	int i, j, ctl;
2951ed09d9cSrkujawa 	uint8_t byte;
2961ed09d9cSrkujawa 	int ready = FALSE;
2971ed09d9cSrkujawa 
2981ed09d9cSrkujawa 	for (;;) {
2991ed09d9cSrkujawa 		if ((st = sc->sc_transfer) == NULL) {
3001ed09d9cSrkujawa 			if ((st = spi_transq_first(&sc->sc_transq)) == NULL) {
3011ed09d9cSrkujawa 				/* No work left to do */
3021ed09d9cSrkujawa 				break;
3031ed09d9cSrkujawa 			}
3041ed09d9cSrkujawa 			spi_transq_dequeue(&sc->sc_transq);
3051ed09d9cSrkujawa 			sc->sc_transfer = st;
3061ed09d9cSrkujawa 		}
3071ed09d9cSrkujawa 
3081ed09d9cSrkujawa 		chunk = st->st_chunks;
3091ed09d9cSrkujawa 
3101ed09d9cSrkujawa 		mvspi_assert(sc);
3111ed09d9cSrkujawa 
3121ed09d9cSrkujawa 		do {
3131ed09d9cSrkujawa 			for (i = chunk->chunk_wresid; i > 0; i--) {
3141ed09d9cSrkujawa 				/* First clear the ready bit */
3151ed09d9cSrkujawa 				ctl = GETREG(sc, MVSPI_CTRL_REG);
3161ed09d9cSrkujawa 				ctl &= ~(MVSPI_CR_SMEMRDY);
3171ed09d9cSrkujawa 				PUTREG(sc, MVSPI_CTRL_REG, ctl);
3181ed09d9cSrkujawa 
3191ed09d9cSrkujawa 				if (chunk->chunk_wptr){
3201ed09d9cSrkujawa 					byte = *chunk->chunk_wptr;
3211ed09d9cSrkujawa 					chunk->chunk_wptr++;
3221ed09d9cSrkujawa 				} else
3231ed09d9cSrkujawa 					byte = MVSPI_DUMMY_BYTE;
3241ed09d9cSrkujawa 
3251ed09d9cSrkujawa 				/* Transmit data */
3261ed09d9cSrkujawa 				PUTREG(sc, MVSPI_DATAOUT_REG, byte);
3271ed09d9cSrkujawa 
3281ed09d9cSrkujawa 				/* Wait with timeout for memory ready */
3291ed09d9cSrkujawa 				for (j = 0; j < MVSPI_WAIT_RDY_MAX_LOOP; j++) {
3301ed09d9cSrkujawa 					if (GETREG(sc, MVSPI_CTRL_REG) &
3311ed09d9cSrkujawa 						MVSPI_CR_SMEMRDY) {
3321ed09d9cSrkujawa 						ready = TRUE;
3331ed09d9cSrkujawa 						break;
3341ed09d9cSrkujawa 					}
3351ed09d9cSrkujawa 
3361ed09d9cSrkujawa 				}
3371ed09d9cSrkujawa 
3381ed09d9cSrkujawa 				if (!ready) {
3391ed09d9cSrkujawa 					mvspi_deassert(sc);
3401ed09d9cSrkujawa 					spi_done(st, EBUSY);
3411ed09d9cSrkujawa 					return;
3421ed09d9cSrkujawa 				}
3431ed09d9cSrkujawa 
3441ed09d9cSrkujawa 				/* Check that the RX data is needed */
3451ed09d9cSrkujawa 				if (chunk->chunk_rptr) {
3461ed09d9cSrkujawa 					*chunk->chunk_rptr =
3471ed09d9cSrkujawa 						GETREG(sc, MVSPI_DATAIN_REG);
3481ed09d9cSrkujawa 					chunk->chunk_rptr++;
3491ed09d9cSrkujawa 
3501ed09d9cSrkujawa 				}
3511ed09d9cSrkujawa 
3521ed09d9cSrkujawa 			}
3531ed09d9cSrkujawa 
3541ed09d9cSrkujawa 			chunk = chunk->chunk_next;
3551ed09d9cSrkujawa 
3561ed09d9cSrkujawa 		} while (chunk != NULL);
3571ed09d9cSrkujawa 
3581ed09d9cSrkujawa 		mvspi_deassert(sc);
3591ed09d9cSrkujawa 
3601ed09d9cSrkujawa 		spi_done(st, 0);
3611ed09d9cSrkujawa 		sc->sc_transfer = NULL;
3621ed09d9cSrkujawa 
3631ed09d9cSrkujawa 
3641ed09d9cSrkujawa 		break;
3651ed09d9cSrkujawa 	}
3661ed09d9cSrkujawa }
367