imx23_mmc.c revision 1.1
1/* $Id: imx23_mmc.c,v 1.1 2026/02/01 11:31:28 yurix Exp $ */
2
3/*
4 * Copyright (c) 2012 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Petri Laakso.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/param.h>
33#include <sys/types.h>
34#include <sys/bus.h>
35#include <sys/cdefs.h>
36#include <sys/condvar.h>
37#include <sys/device.h>
38#include <sys/errno.h>
39#include <sys/mutex.h>
40#include <sys/systm.h>
41
42#include <dev/fdt/fdtvar.h>
43#include <dev/sdmmc/sdmmcchip.h>
44#include <dev/sdmmc/sdmmcreg.h>
45#include <dev/sdmmc/sdmmcvar.h>
46
47#include <arm/imx/imx23_icollreg.h>
48#include <arm/imx/imx23_mmcreg.h>
49#include <arm/imx/imx23var.h>
50#include <arm/pic/picvar.h>
51
52/*
53 * SD/MMC host controller driver for i.MX23.
54 *
55 * TODO:
56 *
57 * - Add support for SMC_CAPS_AUTO_STOP.
58 * - Uset GPIO for SD card detection.
59 */
60
61struct imx23_mmc_softc {
62	device_t sc_dev;
63	struct fdtbus_dma *dma_channel;
64	bus_space_handle_t sc_hdl;
65	bus_space_tag_t sc_iot;
66	device_t sc_sdmmc;
67	kmutex_t sc_lock;
68	struct kcondvar sc_intr_cv;
69	uint32_t sc_irq_error;
70	uint8_t sc_state;
71	uint8_t sc_bus_width;
72	uint32_t pio_words[3];
73};
74
75
76static int	imx23_mmc_match(device_t, cfdata_t, void *);
77static void	imx23_mmc_attach(device_t, device_t, void *);
78
79static void	imx23_mmc_reset(struct imx23_mmc_softc *);
80static void	imx23_mmc_init(struct imx23_mmc_softc *);
81static uint32_t	imx23_mmc_set_sck(struct imx23_mmc_softc *, uint32_t);
82static void	imx23_mmc_dma_intr(void *);
83static int	imx23_mmc_error_intr(void *);
84static void 	imx23_mmc_prepare_data_command(struct imx23_mmc_softc *,
85			       struct fdtbus_dma_req *, struct sdmmc_command *);
86static void 	imx23_mmc_prepare_command(struct imx23_mmc_softc *,
87			  struct fdtbus_dma_req *, struct sdmmc_command *);
88
89/* sdmmc(4) driver chip function prototypes. */
90static int	imx23_mmc_host_reset(sdmmc_chipset_handle_t);
91static uint32_t	imx23_mmc_host_ocr(sdmmc_chipset_handle_t);
92static int	imx23_mmc_host_maxblklen(sdmmc_chipset_handle_t);
93static int	imx23_mmc_card_detect(sdmmc_chipset_handle_t);
94static int	imx23_mmc_write_protect(sdmmc_chipset_handle_t);
95static int	imx23_mmc_bus_power(sdmmc_chipset_handle_t, uint32_t);
96static int	imx23_mmc_bus_clock(sdmmc_chipset_handle_t, int);
97static int	imx23_mmc_bus_width(sdmmc_chipset_handle_t, int);
98static int	imx23_mmc_bus_rod(sdmmc_chipset_handle_t, int);
99static void	imx23_mmc_exec_command(sdmmc_chipset_handle_t,
100		struct sdmmc_command *);
101static void	imx23_mmc_card_enable_intr(sdmmc_chipset_handle_t, int);
102static void	imx23_mmc_card_intr_ack(sdmmc_chipset_handle_t);
103
104static struct sdmmc_chip_functions imx23_mmc_functions = {
105	.host_reset	= imx23_mmc_host_reset,
106	.host_ocr	= imx23_mmc_host_ocr,
107	.host_maxblklen	= imx23_mmc_host_maxblklen,
108	.card_detect	= imx23_mmc_card_detect,
109	.write_protect	= imx23_mmc_write_protect,
110	.bus_power	= imx23_mmc_bus_power,
111	.bus_clock	= imx23_mmc_bus_clock,
112	.bus_width	= imx23_mmc_bus_width,
113	.bus_rod	= imx23_mmc_bus_rod,
114	.exec_command	= imx23_mmc_exec_command,
115	.card_enable_intr = imx23_mmc_card_enable_intr,
116	.card_intr_ack	= imx23_mmc_card_intr_ack
117};
118
119CFATTACH_DECL_NEW(imx23mmc, sizeof(struct imx23_mmc_softc), imx23_mmc_match,
120		  imx23_mmc_attach, NULL, NULL);
121
122#define SSP_SOFT_RST_LOOP 455	/* At least 1 us ... */
123
124#define SSP_RD(sc, reg)							\
125	bus_space_read_4(sc->sc_iot, sc->sc_hdl, (reg))
126#define SSP_WR(sc, reg, val)						\
127	bus_space_write_4(sc->sc_iot, sc->sc_hdl, (reg), (val))
128
129#define SSP_CLK		160000000	/* CLK_SSP from PLL in Hz */
130#define SSP_CLK_MIN	400		/* 400 kHz */
131#define SSP_CLK_MAX	48000		/* 48 MHz */
132
133/* DATA_TIMEOUT is calculated as: * (1 / SSP_CLK) * (DATA_TIMEOUT * 4096) */
134#define DATA_TIMEOUT 0x4240
135
136#define BUS_WIDTH_1_BIT 0x0
137#define BUS_WIDTH_4_BIT 0x1
138#define BUS_WIDTH_8_BIT 0x2
139
140/* Flags for sc_state. */
141#define SSP_STATE_IDLE	0
142#define SSP_STATE_DMA	1
143
144#define HW_SSP_CTRL1_IRQ_MASK (						\
145    HW_SSP_CTRL1_SDIO_IRQ |						\
146    HW_SSP_CTRL1_RESP_ERR_IRQ |						\
147    HW_SSP_CTRL1_RESP_TIMEOUT_IRQ |					\
148    HW_SSP_CTRL1_DATA_TIMEOUT_IRQ |					\
149    HW_SSP_CTRL1_DATA_CRC_IRQ |						\
150    HW_SSP_CTRL1_FIFO_UNDERRUN_IRQ |					\
151    HW_SSP_CTRL1_RECV_TIMEOUT_IRQ |					\
152    HW_SSP_CTRL1_FIFO_OVERRUN_IRQ)
153
154/* SSP does not support over 64k transfer size. */
155#define MAX_TRANSFER_SIZE 65536
156
157/* Offsets of pio words in pio array */
158#define PIO_WORD_CTRL0	0
159#define PIO_WORD_CMD0	1
160#define PIO_WORD_CMD1	2
161
162static const struct device_compatible_entry compat_data[] = {
163	{ .compat = "fsl,imx23-mmc" },
164	DEVICE_COMPAT_EOL
165};
166
167static int
168imx23_mmc_match(device_t parent, cfdata_t match, void *aux)
169{
170	struct fdt_attach_args *const faa = aux;
171
172	return of_compatible_match(faa->faa_phandle, compat_data);
173}
174
175static void
176imx23_mmc_attach(device_t parent, device_t self, void *aux)
177{
178	struct imx23_mmc_softc *const sc = device_private(self);
179	struct fdt_attach_args *const faa = aux;
180	const int phandle = faa->faa_phandle;
181	struct sdmmcbus_attach_args saa;
182	char intrstr[128];
183
184	sc->sc_dev = self;
185	sc->sc_iot = faa->faa_bst;
186
187	/* map ssp control registers */
188	bus_addr_t addr;
189	bus_size_t size;
190	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
191		aprint_error(": couldn't get register address\n");
192		return;
193	}
194	if (bus_space_map(faa->faa_bst, addr, size, 0, &sc->sc_hdl)) {
195		aprint_error(": couldn't map registers\n");
196		return;
197	}
198
199	/* Initialize lock. */
200	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SCHED);
201
202	/* Condvar to wait interrupt complete. */
203	cv_init(&sc->sc_intr_cv, "ssp_intr");
204
205	/* acquire DMA channel */
206	sc->dma_channel = fdtbus_dma_get(phandle,"rx-tx", imx23_mmc_dma_intr,
207					 sc);
208	if(sc->dma_channel == NULL) {
209		aprint_error(": couldn't map registers\n");
210		return;
211	}
212
213	/* establish error interrupt */
214	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
215		aprint_error(": failed to decode interrupt\n");
216		return;
217	}
218	void *ih = fdtbus_intr_establish_xname(phandle, 0, IPL_SDMMC, IST_LEVEL,
219					       imx23_mmc_error_intr, sc,
220					 device_xname(self));
221	if (ih == NULL) {
222		aprint_error_dev(self, "couldn't establish error interrupt\n");
223		return;
224	}
225
226	imx23_mmc_reset(sc);
227	imx23_mmc_init(sc);
228
229	uint32_t imx23_mmc_vers = SSP_RD(sc, HW_SSP_VERSION);
230	aprint_normal(": SSP Block v%" __PRIuBIT ".%" __PRIuBIT "\n",
231	    __SHIFTOUT(imx23_mmc_vers, HW_SSP_VERSION_MAJOR),
232	    __SHIFTOUT(imx23_mmc_vers, HW_SSP_VERSION_MINOR));
233
234	/* Attach sdmmc to ssp bus. */
235	memset(&saa, 0, sizeof(saa));
236	saa.saa_busname = "sdmmc";
237	saa.saa_sct	= &imx23_mmc_functions;
238	saa.saa_spi_sct	= NULL;
239	saa.saa_sch	= sc;
240	saa.saa_dmat	= faa->faa_dmat;
241	saa.saa_clkmin	= SSP_CLK_MIN;
242	saa.saa_clkmax	= SSP_CLK_MAX;
243	saa.saa_caps	= SMC_CAPS_DMA | SMC_CAPS_4BIT_MODE;
244
245	sc->sc_sdmmc = config_found(sc->sc_dev, &saa, NULL, CFARGS_NONE);
246	if (sc->sc_sdmmc == NULL) {
247		aprint_error_dev(sc->sc_dev, "unable to attach sdmmc\n");
248		return;
249	}
250
251	return;
252}
253
254/*
255 * sdmmc chip functions.
256 */
257static int
258imx23_mmc_host_reset(sdmmc_chipset_handle_t sch)
259{
260	struct imx23_mmc_softc *sc = sch;
261	imx23_mmc_reset(sc);
262	return 0;
263}
264
265static uint32_t
266imx23_mmc_host_ocr(sdmmc_chipset_handle_t sch)
267{
268	/* SSP supports at least 3.2 - 3.3v */
269	return MMC_OCR_3_2V_3_3V;
270}
271
272static int
273imx23_mmc_host_maxblklen(sdmmc_chipset_handle_t sch)
274{
275	return 512;
276}
277
278/*
279 * Called at the beginning of sdmmc_task_thread to detect the presence
280 * of the SD card.
281 */
282static int
283imx23_mmc_card_detect(sdmmc_chipset_handle_t sch)
284{
285	return 1; /* the olinuxino has no card detection */
286}
287
288static int
289imx23_mmc_write_protect(sdmmc_chipset_handle_t sch)
290{
291	/* The device is not write protected. */
292	return 0;
293}
294
295static int
296imx23_mmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
297{
298	/* i.MX23 SSP does not support setting bus power. */
299	return 0;
300}
301
302static int
303imx23_mmc_bus_clock(sdmmc_chipset_handle_t sch, int clock)
304{
305	struct imx23_mmc_softc *sc = sch;
306	uint32_t sck;
307
308	if (clock < SSP_CLK_MIN)
309		sck = imx23_mmc_set_sck(sc, SSP_CLK_MIN * 1000);
310	else
311		sck = imx23_mmc_set_sck(sc, clock * 1000);
312
313	/* Notify user if we didn't get the exact clock rate from SSP that was
314	 * requested from the SDMMC subsystem. */
315	if (sck != clock * 1000) {
316		sck = sck / 1000;
317		if (((sck) / 1000) != 0)
318			aprint_normal_dev(sc->sc_dev, "bus clock @ %u.%03u "
319			    "MHz\n", sck / 1000, sck % 1000);
320		else
321			aprint_normal_dev(sc->sc_dev, "bus clock @ %u KHz\n",
322			    sck % 1000);
323	}
324
325	return 0;
326}
327
328static int
329imx23_mmc_bus_width(sdmmc_chipset_handle_t sch, int width)
330{
331	struct imx23_mmc_softc *sc = sch;
332
333	switch(width) {
334	case(1):
335		sc->sc_bus_width = BUS_WIDTH_1_BIT;
336		break;
337	case(4):
338		sc->sc_bus_width = BUS_WIDTH_4_BIT;
339		break;
340	case(8):
341		sc->sc_bus_width = BUS_WIDTH_8_BIT;
342		break;
343	default:
344		return 1;
345	}
346
347	return 0;
348}
349
350static int
351imx23_mmc_bus_rod(sdmmc_chipset_handle_t sch, int rod)
352{
353	/* Go to data transfer mode. */
354	return 0;
355}
356
357static void
358imx23_mmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
359{
360	struct imx23_mmc_softc *sc = sch;
361	struct fdtbus_dma_req req;
362
363	/* SSP does not support over 64k transfer size. */
364	if (cmd->c_data != NULL && cmd->c_datalen > MAX_TRANSFER_SIZE) {
365		aprint_error_dev(sc->sc_dev, "transfer size over %d: %d\n",
366		    MAX_TRANSFER_SIZE, cmd->c_datalen);
367		cmd->c_error = ENODEV;
368		return;
369	}
370
371	mutex_enter(&sc->sc_lock);
372
373	/* Setup DMA command chain.*/
374	if (cmd->c_data != NULL && cmd->c_datalen) {
375		/* command with data */
376		imx23_mmc_prepare_data_command(sc, &req, cmd);
377	} else {
378		/* Only command, no data. */
379		imx23_mmc_prepare_command(sc, &req, cmd);
380	}
381
382
383	sc->sc_state = SSP_STATE_DMA;
384	sc->sc_irq_error = 0;
385	cmd->c_error = 0;
386
387	/* Run DMA */
388	if(fdtbus_dma_transfer(sc->dma_channel, &req)) {
389		aprint_error_dev(sc->sc_dev, "dma transfer error\n");
390		goto out;
391	}
392
393	/* Wait DMA to complete. */
394	while (sc->sc_state == SSP_STATE_DMA)
395		cv_wait(&sc->sc_intr_cv, &sc->sc_lock);
396
397	if (sc->sc_irq_error) {
398		/* Do not log RESP_TIMEOUT_IRQ error if bus width is 0 as it is
399		 * expected during SD card initialization phase. */
400		if (sc->sc_bus_width) {
401			aprint_error_dev(sc->sc_dev, "SSP_ERROR_IRQ: %d\n",
402			    sc->sc_irq_error);
403		}
404		else if(!(sc->sc_irq_error & HW_SSP_CTRL1_RESP_TIMEOUT_IRQ)) {
405			aprint_error_dev(sc->sc_dev, "SSP_ERROR_IRQ: %d\n",
406			    sc->sc_irq_error);
407		}
408
409		/* Shift unsigned error code so it fits nicely to signed int. */
410		cmd->c_error = sc->sc_irq_error >> 8;
411	}
412
413	/* Check response from the card if such was requested. */
414	if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
415		cmd->c_resp[0] = SSP_RD(sc, HW_SSP_SDRESP0);
416		if (ISSET(cmd->c_flags, SCF_RSP_136)) {
417			cmd->c_resp[1] = SSP_RD(sc, HW_SSP_SDRESP1);
418			cmd->c_resp[2] = SSP_RD(sc, HW_SSP_SDRESP2);
419			cmd->c_resp[3] = SSP_RD(sc, HW_SSP_SDRESP3);
420			/*
421			 * Remove CRC7 + LSB by rotating all bits right by 8 to
422			 * make sdmmc __bitfield() happy.
423			 */
424			cmd->c_resp[0] >>= 8; /* Remove CRC7 + LSB. */
425			cmd->c_resp[0] |= (0x000000FF & cmd->c_resp[1]) << 24;
426			cmd->c_resp[1] >>= 8;
427			cmd->c_resp[1] |= (0x000000FF & cmd->c_resp[2]) << 24;
428			cmd->c_resp[2] >>= 8;
429			cmd->c_resp[2] |= (0x000000FF & cmd->c_resp[3]) << 24;
430			cmd->c_resp[3] >>= 8;
431		}
432	}
433
434out:
435	mutex_exit(&sc->sc_lock);
436
437	return;
438}
439
440static void
441imx23_mmc_card_enable_intr(sdmmc_chipset_handle_t sch, int irq)
442{
443	struct imx23_mmc_softc *sc = sch;
444	aprint_error_dev(sc->sc_dev, "issp_card_enable_intr not implemented\n");
445	return;
446}
447
448static void
449imx23_mmc_card_intr_ack(sdmmc_chipset_handle_t sch)
450{
451	struct imx23_mmc_softc *sc = sch;
452	aprint_error_dev(sc->sc_dev, "issp_card_intr_ack not implemented\n");
453	return;
454}
455
456/*
457 * Reset the SSP block.
458 *
459 * Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block"
460 */
461static void
462imx23_mmc_reset(struct imx23_mmc_softc *sc)
463{
464	unsigned int loop;
465
466	/* Prepare for soft-reset by making sure that SFTRST is not currently
467	 * asserted. Also clear CLKGATE so we can wait for its assertion below.
468	 */
469	SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_SFTRST);
470
471	/* Wait at least a microsecond for SFTRST to deassert. */
472	loop = 0;
473	while ((SSP_RD(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_SFTRST) ||
474	    (loop < SSP_SOFT_RST_LOOP))
475		loop++;
476
477	/* Clear CLKGATE so we can wait for its assertion below. */
478	SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_CLKGATE);
479
480	/* Soft-reset the block. */
481	SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_SFTRST);
482
483	/* Wait until clock is in the gated state. */
484	while (!(SSP_RD(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_CLKGATE));
485
486	/* Bring block out of reset. */
487	SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_SFTRST);
488
489	loop = 0;
490	while ((SSP_RD(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_SFTRST) ||
491	    (loop < SSP_SOFT_RST_LOOP))
492		loop++;
493
494	SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_CLKGATE);
495
496	/* Wait until clock is in the NON-gated state. */
497	while (SSP_RD(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_CLKGATE);
498
499	return;
500}
501
502/*
503 * Initialize SSP controller to SD/MMC mode.
504 */
505static void
506imx23_mmc_init(struct imx23_mmc_softc *sc)
507{
508	uint32_t reg;
509
510	reg = SSP_RD(sc, HW_SSP_CTRL0);
511	reg |= HW_SSP_CTRL0_ENABLE;
512
513	/* Initial data bus width is 1-bit. */
514	reg &= ~(HW_SSP_CTRL0_BUS_WIDTH);
515	reg |= __SHIFTIN(BUS_WIDTH_1_BIT, HW_SSP_CTRL0_BUS_WIDTH) |
516	    HW_SSP_CTRL0_WAIT_FOR_IRQ | HW_SSP_CTRL0_ENABLE;
517	SSP_WR(sc, HW_SSP_CTRL0, reg);
518	sc->sc_bus_width = BUS_WIDTH_1_BIT;
519
520	/* Set data timeout. */
521	reg = SSP_RD(sc, HW_SSP_TIMING);
522	reg &= ~(HW_SSP_TIMING_TIMEOUT);
523	reg |= __SHIFTIN(DATA_TIMEOUT, HW_SSP_TIMING_TIMEOUT);
524	SSP_WR(sc, HW_SSP_TIMING, reg);
525
526	/* Set initial clock rate to minimum. */
527	imx23_mmc_set_sck(sc, SSP_CLK_MIN * 1000);
528
529	reg = SSP_RD(sc, HW_SSP_CTRL1);
530	/* Enable all but SDIO IRQ's. */
531	reg |= HW_SSP_CTRL1_RESP_ERR_IRQ_EN |
532	    HW_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN |
533	    HW_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN |
534	    HW_SSP_CTRL1_DATA_CRC_IRQ_EN |
535	    HW_SSP_CTRL1_FIFO_UNDERRUN_EN |
536	    HW_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN |
537	    HW_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN;
538	reg |= HW_SSP_CTRL1_DMA_ENABLE;
539	reg |= HW_SSP_CTRL1_POLARITY;
540	/* Set SD/MMC mode and use use 8-bits per word. */
541	reg &= ~(HW_SSP_CTRL1_WORD_LENGTH | HW_SSP_CTRL1_SSP_MODE);
542	reg |= __SHIFTIN(0x7, HW_SSP_CTRL1_WORD_LENGTH) |
543	    __SHIFTIN(0x3, HW_SSP_CTRL1_SSP_MODE);
544	SSP_WR(sc, HW_SSP_CTRL1, reg);
545
546	return;
547}
548
549/*
550 * Set SSP_SCK clock rate to the value specified in target.
551 *
552 * SSP_SCK is calculated as: SSP_CLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE))
553 *
554 * imx23_mmc_set_sck finds the most suitable CLOCK_DIVIDE and CLOCK_RATE
555 * register values for the target clock rate by iterating through all possible
556 * register values.
557 */
558static uint32_t
559imx23_mmc_set_sck(struct imx23_mmc_softc *sc, uint32_t target)
560{
561	uint32_t newclk, found, reg;
562	uint8_t div, rate, d, r;
563
564	found = div = rate = 0;
565
566	for (d = 2; d < 254; d++) {
567		for (r = 0; r < 255; r++) {
568			newclk = SSP_CLK / (d * (1 + r));
569			if (newclk == target) {
570				found = newclk;
571				div = d;
572				rate = r;
573				goto out;
574			}
575			if (newclk < target && newclk > found) {
576				found = newclk;
577				div = d;
578				rate = r;
579			}
580		}
581	}
582out:
583	reg = SSP_RD(sc, HW_SSP_TIMING);
584	reg &= ~(HW_SSP_TIMING_CLOCK_DIVIDE | HW_SSP_TIMING_CLOCK_RATE);
585	reg |= __SHIFTIN(div, HW_SSP_TIMING_CLOCK_DIVIDE) |
586	    __SHIFTIN(rate, HW_SSP_TIMING_CLOCK_RATE);
587	SSP_WR(sc, HW_SSP_TIMING, reg);
588
589	return SSP_CLK / (div * (1 + rate));
590}
591
592/*
593 * IRQ from DMA.
594 */
595static void
596imx23_mmc_dma_intr(void *arg)
597{
598	struct imx23_mmc_softc *sc = arg;
599
600	mutex_enter(&sc->sc_lock);
601
602	sc->sc_state = SSP_STATE_IDLE;
603
604	/* Signal thread that interrupt was handled. */
605	cv_signal(&sc->sc_intr_cv);
606
607	mutex_exit(&sc->sc_lock);
608}
609
610/*
611 * IRQ from SSP block.
612 *
613 * When SSP receives IRQ it terminates ongoing DMA transfer by issuing DMATERM
614 * signal to DMA block.
615 */
616static int
617imx23_mmc_error_intr(void *arg)
618{
619	struct imx23_mmc_softc *sc = arg;
620
621	mutex_enter(&sc->sc_lock);
622
623	sc->sc_irq_error =
624	    SSP_RD(sc, HW_SSP_CTRL1) & HW_SSP_CTRL1_IRQ_MASK;
625
626	/* Acknowledge all IRQ's. */
627	SSP_WR(sc, HW_SSP_CTRL1_CLR, HW_SSP_CTRL1_IRQ_MASK);
628
629	mutex_exit(&sc->sc_lock);
630
631	/* Return 1 to acknowledge IRQ. */
632	return 1;
633}
634
635/*
636 * Set up a dma transfer for a block with data.
637 */
638static void
639imx23_mmc_prepare_data_command(struct imx23_mmc_softc *sc,
640	struct fdtbus_dma_req *req, struct sdmmc_command *cmd)
641{
642	int block_count = cmd->c_datalen / cmd->c_blklen;
643
644	/* prepare DMA request */
645	req->dreq_segs = cmd->c_dmamap->dm_segs;
646	req->dreq_nsegs = cmd->c_dmamap->dm_nsegs;
647	req->dreq_block_irq = 1;
648	req->dreq_block_multi = 0;
649	req->dreq_datalen = 3;
650	req->dreq_data = sc->pio_words;
651
652	/* prepare CTRL0 register*/
653	sc->pio_words[PIO_WORD_CTRL0] =
654	    HW_SSP_CTRL0_DATA_XFER |
655	    __SHIFTIN(sc->sc_bus_width, HW_SSP_CTRL0_BUS_WIDTH) |
656	    HW_SSP_CTRL0_WAIT_FOR_IRQ |
657	    __SHIFTIN(cmd->c_datalen, HW_SSP_CTRL0_XFER_COUNT) |
658	    HW_SSP_CTRL0_ENABLE;
659	if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
660		req->dreq_dir = FDT_DMA_READ;
661		sc->pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_READ;
662	} else {
663		req->dreq_dir = FDT_DMA_WRITE;
664	}
665	if (!ISSET(cmd->c_flags, SCF_RSP_CRC)) {
666		sc->pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_IGNORE_CRC;
667	}
668	if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
669		sc->pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_GET_RESP;
670		if (ISSET(cmd->c_flags, SCF_RSP_136)) {
671			sc->pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_LONG_RESP;
672		}
673	}
674
675	/* prepare CMD0 register */
676	sc->pio_words[PIO_WORD_CMD0] =
677	    HW_SSP_CMD0_APPEND_8CYC |
678	    __SHIFTIN(ffs(cmd->c_blklen) - 1, HW_SSP_CMD0_BLOCK_SIZE) |
679	    __SHIFTIN(block_count - 1, HW_SSP_CMD0_BLOCK_COUNT) |
680	    __SHIFTIN(cmd->c_opcode, HW_SSP_CMD0_CMD);
681
682	/* prepare CMD1 register */
683	sc->pio_words[PIO_WORD_CMD1] = cmd->c_arg;
684}
685
686/*
687 * Setup a dma transfer for a command without data (PIO only)
688 */
689static void
690imx23_mmc_prepare_command(struct imx23_mmc_softc *sc,
691	struct fdtbus_dma_req *req, struct sdmmc_command *cmd)
692{
693	/* prepare DMA */
694	req->dreq_nsegs = 0;
695	req->dreq_block_irq = 1;
696	req->dreq_block_multi = 0;
697	req->dreq_dir = FDT_DMA_NO_XFER;
698	req->dreq_datalen = 3;
699	req->dreq_data = sc->pio_words;
700
701	/* prepare CTRL0 register*/
702	sc->pio_words[PIO_WORD_CTRL0] =
703	    __SHIFTIN(sc->sc_bus_width, HW_SSP_CTRL0_BUS_WIDTH) |
704	    HW_SSP_CTRL0_WAIT_FOR_IRQ | HW_SSP_CTRL0_ENABLE;
705	if (!ISSET(cmd->c_flags, SCF_RSP_CRC)) {
706		sc->pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_IGNORE_CRC;
707	}
708	if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
709		sc->pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_GET_RESP;
710		if (ISSET(cmd->c_flags, SCF_RSP_136)) {
711			sc->pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_LONG_RESP;
712		}
713	}
714
715	/* prepare CMD0 register */
716	sc->pio_words[PIO_WORD_CMD0] =
717	    __SHIFTIN(cmd->c_opcode, HW_SSP_CMD0_CMD);
718
719	/* prepare CMD1 register */
720	sc->pio_words[PIO_WORD_CMD1] = cmd->c_arg;
721}
722