1*6f8dc150Sandvar /* $Id: imx23_ssp.c,v 1.8 2021/10/21 13:21:54 andvar Exp $ */
2eba5cacbSjkunz
3eba5cacbSjkunz /*
4eba5cacbSjkunz * Copyright (c) 2012 The NetBSD Foundation, Inc.
5eba5cacbSjkunz * All rights reserved.
6eba5cacbSjkunz *
7eba5cacbSjkunz * This code is derived from software contributed to The NetBSD Foundation
8eba5cacbSjkunz * by Petri Laakso.
9eba5cacbSjkunz *
10eba5cacbSjkunz * Redistribution and use in source and binary forms, with or without
11eba5cacbSjkunz * modification, are permitted provided that the following conditions
12eba5cacbSjkunz * are met:
13eba5cacbSjkunz * 1. Redistributions of source code must retain the above copyright
14eba5cacbSjkunz * notice, this list of conditions and the following disclaimer.
15eba5cacbSjkunz * 2. Redistributions in binary form must reproduce the above copyright
16eba5cacbSjkunz * notice, this list of conditions and the following disclaimer in the
17eba5cacbSjkunz * documentation and/or other materials provided with the distribution.
18eba5cacbSjkunz *
19eba5cacbSjkunz * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20eba5cacbSjkunz * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21eba5cacbSjkunz * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22eba5cacbSjkunz * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23eba5cacbSjkunz * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24eba5cacbSjkunz * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25eba5cacbSjkunz * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26eba5cacbSjkunz * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27eba5cacbSjkunz * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28eba5cacbSjkunz * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29eba5cacbSjkunz * POSSIBILITY OF SUCH DAMAGE.
30eba5cacbSjkunz */
31eba5cacbSjkunz
32eba5cacbSjkunz #include <sys/param.h>
33eba5cacbSjkunz #include <sys/types.h>
34eba5cacbSjkunz #include <sys/bus.h>
35eba5cacbSjkunz #include <sys/cdefs.h>
36aca15765Sjkunz #include <sys/condvar.h>
37eba5cacbSjkunz #include <sys/device.h>
38eba5cacbSjkunz #include <sys/errno.h>
39aca15765Sjkunz #include <sys/mutex.h>
40eba5cacbSjkunz #include <sys/systm.h>
41eba5cacbSjkunz
42aca15765Sjkunz #include <arm/pic/picvar.h>
43aca15765Sjkunz
44aca15765Sjkunz #include <arm/imx/imx23_apbdmavar.h>
45aca15765Sjkunz #include <arm/imx/imx23_icollreg.h>
46eba5cacbSjkunz #include <arm/imx/imx23_sspreg.h>
47eba5cacbSjkunz #include <arm/imx/imx23var.h>
48eba5cacbSjkunz
49eba5cacbSjkunz #include <dev/sdmmc/sdmmcchip.h>
50eba5cacbSjkunz #include <dev/sdmmc/sdmmcreg.h>
51eba5cacbSjkunz #include <dev/sdmmc/sdmmcvar.h>
52eba5cacbSjkunz
53eba5cacbSjkunz /*
54c332c3c0Sjkunz * SD/MMC host controller driver for i.MX23.
55aca15765Sjkunz *
56aca15765Sjkunz * TODO:
57aca15765Sjkunz *
58aca15765Sjkunz * - Add support for SMC_CAPS_AUTO_STOP.
5994de0730Smatt * - Uset GPIO for SD card detection.
60eba5cacbSjkunz */
61eba5cacbSjkunz
62aca15765Sjkunz #define DMA_MAXNSEGS ((MAXPHYS / PAGE_SIZE) + 1)
63aca15765Sjkunz
64aca15765Sjkunz typedef struct issp_softc {
65eba5cacbSjkunz device_t sc_dev;
66aca15765Sjkunz apbdma_softc_t sc_dmac;
67aca15765Sjkunz bus_dma_tag_t sc_dmat;
68aca15765Sjkunz bus_dmamap_t sc_dmamp;
69aca15765Sjkunz bus_size_t sc_chnsiz;
70aca15765Sjkunz bus_dma_segment_t sc_ds[1];
71aca15765Sjkunz int sc_rseg;
72eba5cacbSjkunz bus_space_handle_t sc_hdl;
73aca15765Sjkunz bus_space_tag_t sc_iot;
74eba5cacbSjkunz device_t sc_sdmmc;
75aca15765Sjkunz kmutex_t sc_lock;
76aca15765Sjkunz struct kcondvar sc_intr_cv;
77aca15765Sjkunz unsigned int dma_channel;
78aca15765Sjkunz uint32_t sc_dma_error;
79aca15765Sjkunz uint32_t sc_irq_error;
80aca15765Sjkunz uint8_t sc_state;
81aca15765Sjkunz uint8_t sc_bus_width;
82aca15765Sjkunz } *issp_softc_t;
83eba5cacbSjkunz
84eba5cacbSjkunz static int issp_match(device_t, cfdata_t, void *);
85eba5cacbSjkunz static void issp_attach(device_t, device_t, void *);
86eba5cacbSjkunz static int issp_activate(device_t, enum devact);
87eba5cacbSjkunz
88c332c3c0Sjkunz static void issp_reset(struct issp_softc *);
89c332c3c0Sjkunz static void issp_init(struct issp_softc *);
90aca15765Sjkunz static uint32_t issp_set_sck(struct issp_softc *, uint32_t);
91aca15765Sjkunz static int issp_dma_intr(void *);
92aca15765Sjkunz static int issp_error_intr(void *);
93aca15765Sjkunz static void issp_ack_intr(struct issp_softc *);
94aca15765Sjkunz static void issp_create_dma_cmd_list_multi(issp_softc_t, void *,
95aca15765Sjkunz struct sdmmc_command *);
96aca15765Sjkunz static void issp_create_dma_cmd_list_single(issp_softc_t, void *,
97aca15765Sjkunz struct sdmmc_command *);
98aca15765Sjkunz static void issp_create_dma_cmd_list(issp_softc_t, void *,
99aca15765Sjkunz struct sdmmc_command *);
100c332c3c0Sjkunz
101c332c3c0Sjkunz /* sdmmc(4) driver chip function prototypes. */
102eba5cacbSjkunz static int issp_host_reset(sdmmc_chipset_handle_t);
103eba5cacbSjkunz static uint32_t issp_host_ocr(sdmmc_chipset_handle_t);
104eba5cacbSjkunz static int issp_host_maxblklen(sdmmc_chipset_handle_t);
105eba5cacbSjkunz static int issp_card_detect(sdmmc_chipset_handle_t);
106eba5cacbSjkunz static int issp_write_protect(sdmmc_chipset_handle_t);
107eba5cacbSjkunz static int issp_bus_power(sdmmc_chipset_handle_t, uint32_t);
108eba5cacbSjkunz static int issp_bus_clock(sdmmc_chipset_handle_t, int);
109eba5cacbSjkunz static int issp_bus_width(sdmmc_chipset_handle_t, int);
110eba5cacbSjkunz static int issp_bus_rod(sdmmc_chipset_handle_t, int);
111eba5cacbSjkunz static void issp_exec_command(sdmmc_chipset_handle_t,
112eba5cacbSjkunz struct sdmmc_command *);
113eba5cacbSjkunz static void issp_card_enable_intr(sdmmc_chipset_handle_t, int);
114eba5cacbSjkunz static void issp_card_intr_ack(sdmmc_chipset_handle_t);
115eba5cacbSjkunz
116eba5cacbSjkunz static struct sdmmc_chip_functions issp_functions = {
117eba5cacbSjkunz .host_reset = issp_host_reset,
118eba5cacbSjkunz .host_ocr = issp_host_ocr,
119eba5cacbSjkunz .host_maxblklen = issp_host_maxblklen,
120eba5cacbSjkunz .card_detect = issp_card_detect,
121eba5cacbSjkunz .write_protect = issp_write_protect,
122eba5cacbSjkunz .bus_power = issp_bus_power,
123eba5cacbSjkunz .bus_clock = issp_bus_clock,
124eba5cacbSjkunz .bus_width = issp_bus_width,
125eba5cacbSjkunz .bus_rod = issp_bus_rod,
126eba5cacbSjkunz .exec_command = issp_exec_command,
127eba5cacbSjkunz .card_enable_intr = issp_card_enable_intr,
128eba5cacbSjkunz .card_intr_ack = issp_card_intr_ack
129eba5cacbSjkunz };
130eba5cacbSjkunz
131c332c3c0Sjkunz CFATTACH_DECL3_NEW(ssp,
132c332c3c0Sjkunz sizeof(struct issp_softc),
133c332c3c0Sjkunz issp_match,
134c332c3c0Sjkunz issp_attach,
135c332c3c0Sjkunz NULL,
136c332c3c0Sjkunz issp_activate,
137c332c3c0Sjkunz NULL,
138c332c3c0Sjkunz NULL,
139aca15765Sjkunz 0
140aca15765Sjkunz );
141c332c3c0Sjkunz
142c332c3c0Sjkunz #define SSP_SOFT_RST_LOOP 455 /* At least 1 us ... */
143c332c3c0Sjkunz
144c332c3c0Sjkunz #define SSP_RD(sc, reg) \
145eba5cacbSjkunz bus_space_read_4(sc->sc_iot, sc->sc_hdl, (reg))
146c332c3c0Sjkunz #define SSP_WR(sc, reg, val) \
147eba5cacbSjkunz bus_space_write_4(sc->sc_iot, sc->sc_hdl, (reg), (val))
148eba5cacbSjkunz
14994de0730Smatt #define SSP_CLK 160000000 /* CLK_SSP from PLL in Hz */
150c332c3c0Sjkunz #define SSP_CLK_MIN 400 /* 400 kHz */
151eba5cacbSjkunz #define SSP_CLK_MAX 48000 /* 48 MHz */
152c332c3c0Sjkunz
153aca15765Sjkunz /* DATA_TIMEOUT is calculated as: * (1 / SSP_CLK) * (DATA_TIMEOUT * 4096) */
15494de0730Smatt #define DATA_TIMEOUT 0x4240
155c332c3c0Sjkunz
156c332c3c0Sjkunz #define BUS_WIDTH_1_BIT 0x0
157c332c3c0Sjkunz #define BUS_WIDTH_4_BIT 0x1
158c332c3c0Sjkunz #define BUS_WIDTH_8_BIT 0x2
159c332c3c0Sjkunz
160aca15765Sjkunz #define SSP1_ATTACHED 1
161aca15765Sjkunz #define SSP2_ATTACHED 2
162aca15765Sjkunz
163aca15765Sjkunz /* Flags for sc_state. */
164aca15765Sjkunz #define SSP_STATE_IDLE 0
165aca15765Sjkunz #define SSP_STATE_DMA 1
166aca15765Sjkunz
167aca15765Sjkunz #define PIO_WORD_CTRL0 0
168aca15765Sjkunz #define PIO_WORD_CMD0 1
169aca15765Sjkunz #define PIO_WORD_CMD1 2
170aca15765Sjkunz
171aca15765Sjkunz #define HW_SSP_CTRL1_IRQ_MASK ( \
172aca15765Sjkunz HW_SSP_CTRL1_SDIO_IRQ | \
173aca15765Sjkunz HW_SSP_CTRL1_RESP_ERR_IRQ | \
174aca15765Sjkunz HW_SSP_CTRL1_RESP_TIMEOUT_IRQ | \
175aca15765Sjkunz HW_SSP_CTRL1_DATA_TIMEOUT_IRQ | \
176aca15765Sjkunz HW_SSP_CTRL1_DATA_CRC_IRQ | \
177aca15765Sjkunz HW_SSP_CTRL1_FIFO_UNDERRUN_IRQ | \
178aca15765Sjkunz HW_SSP_CTRL1_RECV_TIMEOUT_IRQ | \
179aca15765Sjkunz HW_SSP_CTRL1_FIFO_OVERRUN_IRQ)
180aca15765Sjkunz
181aca15765Sjkunz /* SSP does not support over 64k transfer size. */
182aca15765Sjkunz #define MAX_TRANSFER_SIZE 65536
183aca15765Sjkunz
184eba5cacbSjkunz static int
issp_match(device_t parent,cfdata_t match,void * aux)185eba5cacbSjkunz issp_match(device_t parent, cfdata_t match, void *aux)
186eba5cacbSjkunz {
187eba5cacbSjkunz struct apb_attach_args *aa = aux;
188eba5cacbSjkunz
189eba5cacbSjkunz if ((aa->aa_addr == HW_SSP1_BASE) && (aa->aa_size == HW_SSP1_SIZE))
190eba5cacbSjkunz return 1;
191eba5cacbSjkunz
192eba5cacbSjkunz if ((aa->aa_addr == HW_SSP2_BASE) && (aa->aa_size == HW_SSP2_SIZE))
193eba5cacbSjkunz return 1;
194eba5cacbSjkunz
195eba5cacbSjkunz return 0;
196eba5cacbSjkunz }
197eba5cacbSjkunz
198eba5cacbSjkunz static void
issp_attach(device_t parent,device_t self,void * aux)199eba5cacbSjkunz issp_attach(device_t parent, device_t self, void *aux)
200eba5cacbSjkunz {
201eba5cacbSjkunz struct issp_softc *sc = device_private(self);
202c332c3c0Sjkunz struct apb_softc *sc_parent = device_private(parent);
203eba5cacbSjkunz struct apb_attach_args *aa = aux;
204eba5cacbSjkunz struct sdmmcbus_attach_args saa;
205aca15765Sjkunz static int ssp_attached = 0;
206aca15765Sjkunz int error;
207aca15765Sjkunz void *intr;
208eba5cacbSjkunz
209eba5cacbSjkunz sc->sc_dev = self;
210eba5cacbSjkunz sc->sc_iot = aa->aa_iot;
211aca15765Sjkunz sc->sc_dmat = aa->aa_dmat;
212eba5cacbSjkunz
213aca15765Sjkunz /* Test if device instance is already attached. */
214aca15765Sjkunz if (aa->aa_addr == HW_SSP1_BASE && ISSET(ssp_attached, SSP1_ATTACHED)) {
215aca15765Sjkunz aprint_error_dev(sc->sc_dev, "SSP1 already attached\n");
216aca15765Sjkunz return;
217aca15765Sjkunz }
218aca15765Sjkunz if (aa->aa_addr == HW_SSP2_BASE && ISSET(ssp_attached, SSP2_ATTACHED)) {
219aca15765Sjkunz aprint_error_dev(sc->sc_dev, "SSP2 already attached\n");
220aca15765Sjkunz return;
221aca15765Sjkunz }
222aca15765Sjkunz
223aca15765Sjkunz if (aa->aa_addr == HW_SSP1_BASE) {
224aca15765Sjkunz sc->dma_channel = APBH_DMA_CHANNEL_SSP1;
225aca15765Sjkunz }
226aca15765Sjkunz if (aa->aa_addr == HW_SSP2_BASE) {
227aca15765Sjkunz sc->dma_channel = APBH_DMA_CHANNEL_SSP2;
228aca15765Sjkunz }
229aca15765Sjkunz
230aca15765Sjkunz /* This driver requires DMA functionality from the bus.
231aca15765Sjkunz * Parent bus passes handle to the DMA controller instance. */
232aca15765Sjkunz if (sc_parent->dmac == NULL) {
233aca15765Sjkunz aprint_error_dev(sc->sc_dev, "DMA functionality missing\n");
234aca15765Sjkunz return;
235aca15765Sjkunz }
236aca15765Sjkunz sc->sc_dmac = device_private(sc_parent->dmac);
237aca15765Sjkunz
238aca15765Sjkunz /* Initialize lock. */
239aca15765Sjkunz mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SDMMC);
240aca15765Sjkunz
241aca15765Sjkunz /* Condvar to wait interrupt complete. */
242aca15765Sjkunz cv_init(&sc->sc_intr_cv, "ssp_intr");
243aca15765Sjkunz
244aca15765Sjkunz /* Establish interrupt handlers for SSP errors and SSP DMA. */
245aca15765Sjkunz if (aa->aa_addr == HW_SSP1_BASE) {
246aca15765Sjkunz intr = intr_establish(IRQ_SSP1_DMA, IPL_SDMMC, IST_LEVEL,
247aca15765Sjkunz issp_dma_intr, sc);
248aca15765Sjkunz if (intr == NULL) {
249aca15765Sjkunz aprint_error_dev(sc->sc_dev, "Unable to establish "
250aca15765Sjkunz "interrupt for SSP1 DMA\n");
251aca15765Sjkunz return;
252aca15765Sjkunz }
253aca15765Sjkunz intr = intr_establish(IRQ_SSP1_ERROR, IPL_SDMMC, IST_LEVEL,
254aca15765Sjkunz issp_error_intr, sc);
255aca15765Sjkunz if (intr == NULL) {
256aca15765Sjkunz aprint_error_dev(sc->sc_dev, "Unable to establish "
257aca15765Sjkunz "interrupt for SSP1 ERROR\n");
258aca15765Sjkunz return;
259aca15765Sjkunz }
260aca15765Sjkunz }
261aca15765Sjkunz
262aca15765Sjkunz if (aa->aa_addr == HW_SSP2_BASE) {
263aca15765Sjkunz intr = intr_establish(IRQ_SSP2_DMA, IPL_SDMMC, IST_LEVEL,
264aca15765Sjkunz issp_dma_intr, sc);
265aca15765Sjkunz if (intr == NULL) {
266aca15765Sjkunz aprint_error_dev(sc->sc_dev, "Unable to establish "
267aca15765Sjkunz "interrupt for SSP2 DMA\n");
268aca15765Sjkunz return;
269aca15765Sjkunz }
270aca15765Sjkunz intr = intr_establish(IRQ_SSP2_ERROR, IPL_SDMMC, IST_LEVEL,
271aca15765Sjkunz issp_error_intr, sc);
272aca15765Sjkunz if (intr == NULL) {
273aca15765Sjkunz aprint_error_dev(sc->sc_dev, "Unable to establish "
274aca15765Sjkunz "interrupt for SSP2 ERROR\n");
275aca15765Sjkunz return;
276aca15765Sjkunz }
277aca15765Sjkunz }
278aca15765Sjkunz
279aca15765Sjkunz /* Allocate DMA handle. */
280aca15765Sjkunz error = bus_dmamap_create(sc->sc_dmat, MAXPHYS, 1, MAXPHYS,
281aca15765Sjkunz 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamp);
282aca15765Sjkunz if (error) {
283aca15765Sjkunz aprint_error_dev(sc->sc_dev,
284aca15765Sjkunz "Unable to allocate DMA handle\n");
285aca15765Sjkunz return;
286aca15765Sjkunz }
287aca15765Sjkunz
288aca15765Sjkunz /* Allocate memory for DMA command chain. */
289aca15765Sjkunz sc->sc_chnsiz = sizeof(struct apbdma_command) *
290aca15765Sjkunz (MAX_TRANSFER_SIZE / SDMMC_SECTOR_SIZE);
291aca15765Sjkunz
292aca15765Sjkunz error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_chnsiz, PAGE_SIZE, 0,
293aca15765Sjkunz sc->sc_ds, 1, &sc->sc_rseg, BUS_DMA_NOWAIT);
294aca15765Sjkunz if (error) {
295aca15765Sjkunz aprint_error_dev(sc->sc_dev,
296aca15765Sjkunz "Unable to allocate DMA memory\n");
297aca15765Sjkunz return;
298aca15765Sjkunz }
299aca15765Sjkunz
300aca15765Sjkunz /* Initialize DMA channel. */
301aca15765Sjkunz apbdma_chan_init(sc->sc_dmac, sc->dma_channel);
302aca15765Sjkunz
303aca15765Sjkunz /* Map SSP bus space. */
304aca15765Sjkunz if (bus_space_map(sc->sc_iot, aa->aa_addr, aa->aa_size, 0,
305aca15765Sjkunz &sc->sc_hdl)) {
306aca15765Sjkunz aprint_error_dev(sc->sc_dev, "Unable to map SSP bus space\n");
307eba5cacbSjkunz return;
308eba5cacbSjkunz }
309eba5cacbSjkunz
310eba5cacbSjkunz issp_reset(sc);
311eba5cacbSjkunz issp_init(sc);
312eba5cacbSjkunz
313c332c3c0Sjkunz uint32_t issp_vers = SSP_RD(sc, HW_SSP_VERSION);
314eba5cacbSjkunz aprint_normal(": SSP Block v%" __PRIuBIT ".%" __PRIuBIT "\n",
315eba5cacbSjkunz __SHIFTOUT(issp_vers, HW_SSP_VERSION_MAJOR),
316eba5cacbSjkunz __SHIFTOUT(issp_vers, HW_SSP_VERSION_MINOR));
317eba5cacbSjkunz
318aca15765Sjkunz /* Attach sdmmc to ssp bus. */
319eba5cacbSjkunz saa.saa_busname = "sdmmc";
320eba5cacbSjkunz saa.saa_sct = &issp_functions;
321eba5cacbSjkunz saa.saa_spi_sct = NULL;
322eba5cacbSjkunz saa.saa_sch = sc;
323eba5cacbSjkunz saa.saa_dmat = aa->aa_dmat;
324eba5cacbSjkunz saa.saa_clkmin = SSP_CLK_MIN;
325eba5cacbSjkunz saa.saa_clkmax = SSP_CLK_MAX;
326aca15765Sjkunz saa.saa_caps = SMC_CAPS_DMA | SMC_CAPS_4BIT_MODE |
327aca15765Sjkunz SMC_CAPS_MULTI_SEG_DMA;
328eba5cacbSjkunz
329c7fb772bSthorpej sc->sc_sdmmc = config_found(sc->sc_dev, &saa, NULL, CFARGS_NONE);
330eba5cacbSjkunz if (sc->sc_sdmmc == NULL) {
331eba5cacbSjkunz aprint_error_dev(sc->sc_dev, "unable to attach sdmmc\n");
332eba5cacbSjkunz return;
333eba5cacbSjkunz }
334eba5cacbSjkunz
33572e44f84Sandvar /* Device instance was successfully attached. */
336aca15765Sjkunz if (aa->aa_addr == HW_SSP1_BASE)
337aca15765Sjkunz ssp_attached |= SSP1_ATTACHED;
338aca15765Sjkunz if (aa->aa_addr == HW_SSP2_BASE)
339aca15765Sjkunz ssp_attached |= SSP2_ATTACHED;
340eba5cacbSjkunz
341eba5cacbSjkunz return;
342eba5cacbSjkunz }
343eba5cacbSjkunz
344eba5cacbSjkunz static int
issp_activate(device_t self,enum devact act)345eba5cacbSjkunz issp_activate(device_t self, enum devact act)
346eba5cacbSjkunz {
347eba5cacbSjkunz return EOPNOTSUPP;
348eba5cacbSjkunz }
349eba5cacbSjkunz
350eba5cacbSjkunz /*
351eba5cacbSjkunz * sdmmc chip functions.
352eba5cacbSjkunz */
353eba5cacbSjkunz static int
issp_host_reset(sdmmc_chipset_handle_t sch)354eba5cacbSjkunz issp_host_reset(sdmmc_chipset_handle_t sch)
355eba5cacbSjkunz {
356eba5cacbSjkunz struct issp_softc *sc = sch;
357eba5cacbSjkunz issp_reset(sc);
358eba5cacbSjkunz return 0;
359eba5cacbSjkunz }
360eba5cacbSjkunz
361eba5cacbSjkunz static uint32_t
issp_host_ocr(sdmmc_chipset_handle_t sch)362eba5cacbSjkunz issp_host_ocr(sdmmc_chipset_handle_t sch)
363eba5cacbSjkunz {
364eba5cacbSjkunz /* SSP supports at least 3.2 - 3.3v */
365eba5cacbSjkunz return MMC_OCR_3_2V_3_3V;
366eba5cacbSjkunz }
367eba5cacbSjkunz
368eba5cacbSjkunz static int
issp_host_maxblklen(sdmmc_chipset_handle_t sch)369eba5cacbSjkunz issp_host_maxblklen(sdmmc_chipset_handle_t sch)
370eba5cacbSjkunz {
371eba5cacbSjkunz return 512;
372eba5cacbSjkunz }
373eba5cacbSjkunz
374eba5cacbSjkunz /*
375eba5cacbSjkunz * Called at the beginning of sdmmc_task_thread to detect the presence
376eba5cacbSjkunz * of the SD card.
377eba5cacbSjkunz */
378eba5cacbSjkunz static int
issp_card_detect(sdmmc_chipset_handle_t sch)379eba5cacbSjkunz issp_card_detect(sdmmc_chipset_handle_t sch)
380eba5cacbSjkunz {
381eba5cacbSjkunz return 1;
382eba5cacbSjkunz }
383eba5cacbSjkunz
384eba5cacbSjkunz static int
issp_write_protect(sdmmc_chipset_handle_t sch)385eba5cacbSjkunz issp_write_protect(sdmmc_chipset_handle_t sch)
386eba5cacbSjkunz {
387eba5cacbSjkunz /* The device is not write protected. */
388eba5cacbSjkunz return 0;
389eba5cacbSjkunz }
390eba5cacbSjkunz
391eba5cacbSjkunz static int
issp_bus_power(sdmmc_chipset_handle_t sch,uint32_t ocr)392c332c3c0Sjkunz issp_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
393eba5cacbSjkunz {
394c332c3c0Sjkunz /* i.MX23 SSP does not support setting bus power. */
395eba5cacbSjkunz return 0;
396eba5cacbSjkunz }
397eba5cacbSjkunz
398eba5cacbSjkunz static int
issp_bus_clock(sdmmc_chipset_handle_t sch,int clock)399eba5cacbSjkunz issp_bus_clock(sdmmc_chipset_handle_t sch, int clock)
400eba5cacbSjkunz {
401eba5cacbSjkunz struct issp_softc *sc = sch;
402eba5cacbSjkunz uint32_t sck;
403eba5cacbSjkunz
404aca15765Sjkunz if (clock < SSP_CLK_MIN)
405aca15765Sjkunz sck = issp_set_sck(sc, SSP_CLK_MIN * 1000);
406aca15765Sjkunz else
407eba5cacbSjkunz sck = issp_set_sck(sc, clock * 1000);
408c332c3c0Sjkunz
409aca15765Sjkunz /* Notify user if we didn't get the exact clock rate from SSP that was
410aca15765Sjkunz * requested from the SDMMC subsystem. */
411aca15765Sjkunz if (sck != clock * 1000) {
412aca15765Sjkunz sck = sck / 1000;
413aca15765Sjkunz if (((sck) / 1000) != 0)
414aca15765Sjkunz aprint_normal_dev(sc->sc_dev, "bus clock @ %u.%03u "
415aca15765Sjkunz "MHz\n", sck / 1000, sck % 1000);
416aca15765Sjkunz else
417aca15765Sjkunz aprint_normal_dev(sc->sc_dev, "bus clock @ %u KHz\n",
418aca15765Sjkunz sck % 1000);
419aca15765Sjkunz }
420eba5cacbSjkunz
421eba5cacbSjkunz return 0;
422eba5cacbSjkunz }
423eba5cacbSjkunz
424eba5cacbSjkunz static int
issp_bus_width(sdmmc_chipset_handle_t sch,int width)425eba5cacbSjkunz issp_bus_width(sdmmc_chipset_handle_t sch, int width)
426eba5cacbSjkunz {
427c332c3c0Sjkunz struct issp_softc *sc = sch;
428c332c3c0Sjkunz
429c332c3c0Sjkunz switch(width) {
430c332c3c0Sjkunz case(1):
431aca15765Sjkunz sc->sc_bus_width = BUS_WIDTH_1_BIT;
432c332c3c0Sjkunz break;
433c332c3c0Sjkunz case(4):
434aca15765Sjkunz sc->sc_bus_width = BUS_WIDTH_4_BIT;
435c332c3c0Sjkunz break;
436c332c3c0Sjkunz case(8):
437aca15765Sjkunz sc->sc_bus_width = BUS_WIDTH_8_BIT;
438c332c3c0Sjkunz break;
439c332c3c0Sjkunz default:
440c332c3c0Sjkunz return 1;
441c332c3c0Sjkunz }
442c332c3c0Sjkunz
443c332c3c0Sjkunz return 0;
444eba5cacbSjkunz }
445eba5cacbSjkunz
446eba5cacbSjkunz static int
issp_bus_rod(sdmmc_chipset_handle_t sch,int rod)447eba5cacbSjkunz issp_bus_rod(sdmmc_chipset_handle_t sch, int rod)
448eba5cacbSjkunz {
449eba5cacbSjkunz /* Go to data transfer mode. */
450eba5cacbSjkunz return 0;
451eba5cacbSjkunz }
452eba5cacbSjkunz
453eba5cacbSjkunz static void
issp_exec_command(sdmmc_chipset_handle_t sch,struct sdmmc_command * cmd)454eba5cacbSjkunz issp_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
455eba5cacbSjkunz {
456aca15765Sjkunz issp_softc_t sc = sch;
457aca15765Sjkunz void *dma_chain;
458aca15765Sjkunz int error;
459eba5cacbSjkunz
460aca15765Sjkunz /* SSP does not support over 64k transfer size. */
461aca15765Sjkunz if (cmd->c_data != NULL && cmd->c_datalen > MAX_TRANSFER_SIZE) {
462aca15765Sjkunz aprint_error_dev(sc->sc_dev, "transfer size over %d: %d\n",
463aca15765Sjkunz MAX_TRANSFER_SIZE, cmd->c_datalen);
464aca15765Sjkunz cmd->c_error = ENODEV;
465aca15765Sjkunz return;
466eba5cacbSjkunz }
467eba5cacbSjkunz
468aca15765Sjkunz /* Map dma_chain to point allocated previously allocated DMA chain. */
469aca15765Sjkunz error = bus_dmamem_map(sc->sc_dmat, sc->sc_ds, 1, sc->sc_chnsiz,
470aca15765Sjkunz &dma_chain, BUS_DMA_NOWAIT);
471aca15765Sjkunz if (error) {
472aca15765Sjkunz aprint_error_dev(sc->sc_dev, "bus_dmamem_map: %d\n", error);
473aca15765Sjkunz cmd->c_error = error;
474aca15765Sjkunz goto out;
475aca15765Sjkunz }
476eba5cacbSjkunz
477aca15765Sjkunz error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamp, dma_chain,
478aca15765Sjkunz sc->sc_chnsiz, NULL, BUS_DMA_NOWAIT|BUS_DMA_WRITE);
479aca15765Sjkunz if (error) {
480aca15765Sjkunz aprint_error_dev(sc->sc_dev, "bus_dmamap_load: %d\n", error);
481aca15765Sjkunz cmd->c_error = error;
482aca15765Sjkunz goto dmamem_unmap;
483aca15765Sjkunz }
484eba5cacbSjkunz
485aca15765Sjkunz memset(dma_chain, 0, sc->sc_chnsiz);
486c332c3c0Sjkunz
487aca15765Sjkunz /* Setup DMA command chain.*/
488aca15765Sjkunz if (cmd->c_data != NULL && (cmd->c_datalen / cmd->c_blklen) > 1) {
489aca15765Sjkunz /* Multi block transfer. */
490aca15765Sjkunz issp_create_dma_cmd_list_multi(sc, dma_chain, cmd);
491aca15765Sjkunz } else if (cmd->c_data != NULL && cmd->c_datalen) {
492aca15765Sjkunz /* Single block transfer. */
493aca15765Sjkunz issp_create_dma_cmd_list_single(sc, dma_chain, cmd);
494c332c3c0Sjkunz } else {
495aca15765Sjkunz /* Only command, no data. */
496aca15765Sjkunz issp_create_dma_cmd_list(sc, dma_chain, cmd);
497c332c3c0Sjkunz }
498eba5cacbSjkunz
499aca15765Sjkunz /* Tell DMA controller where it can find just initialized DMA chain. */
500aca15765Sjkunz apbdma_chan_set_chain(sc->sc_dmac, sc->dma_channel, sc->sc_dmamp);
501eba5cacbSjkunz
502aca15765Sjkunz /* Synchronize command chain before DMA controller accesses it. */
503aca15765Sjkunz bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamp, 0, sc->sc_chnsiz,
504aca15765Sjkunz BUS_DMASYNC_PREWRITE);
505aca15765Sjkunz
506aca15765Sjkunz sc->sc_state = SSP_STATE_DMA;
507aca15765Sjkunz sc->sc_irq_error = 0;
508aca15765Sjkunz sc->sc_dma_error = 0;
509aca15765Sjkunz cmd->c_error = 0;
510aca15765Sjkunz
511aca15765Sjkunz mutex_enter(&sc->sc_lock);
512aca15765Sjkunz
513aca15765Sjkunz /* Run DMA command chain. */
514aca15765Sjkunz apbdma_run(sc->sc_dmac, sc->dma_channel);
515aca15765Sjkunz
516aca15765Sjkunz /* Wait DMA to complete. */
517aca15765Sjkunz while (sc->sc_state == SSP_STATE_DMA)
518aca15765Sjkunz cv_wait(&sc->sc_intr_cv, &sc->sc_lock);
519aca15765Sjkunz
520aca15765Sjkunz mutex_exit(&sc->sc_lock);
521aca15765Sjkunz
522aca15765Sjkunz bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamp, 0, sc->sc_chnsiz,
523aca15765Sjkunz BUS_DMASYNC_POSTWRITE);
524aca15765Sjkunz
525aca15765Sjkunz if (sc->sc_dma_error) {
526aca15765Sjkunz if (sc->sc_dma_error == DMA_IRQ_TERM) {
527aca15765Sjkunz apbdma_chan_reset(sc->sc_dmac, sc->dma_channel);
528aca15765Sjkunz cmd->c_error = sc->sc_dma_error;
529c332c3c0Sjkunz }
530aca15765Sjkunz else if (sc->sc_dma_error == DMA_IRQ_BUS_ERROR) {
531aca15765Sjkunz aprint_error_dev(sc->sc_dev, "DMA_IRQ_BUS_ERROR: %d\n",
532aca15765Sjkunz sc->sc_irq_error);
533aca15765Sjkunz cmd->c_error = sc->sc_dma_error;
534c332c3c0Sjkunz }
535c332c3c0Sjkunz }
536c332c3c0Sjkunz
537aca15765Sjkunz if (sc->sc_irq_error) {
538aca15765Sjkunz /* Do not log RESP_TIMEOUT_IRQ error if bus width is 0 as it is
539aca15765Sjkunz * expected during SD card initialization phase. */
540aca15765Sjkunz if (sc->sc_bus_width) {
541aca15765Sjkunz aprint_error_dev(sc->sc_dev, "SSP_ERROR_IRQ: %d\n",
542aca15765Sjkunz sc->sc_irq_error);
543aca15765Sjkunz }
544aca15765Sjkunz else if(!(sc->sc_irq_error & HW_SSP_CTRL1_RESP_TIMEOUT_IRQ)) {
545aca15765Sjkunz aprint_error_dev(sc->sc_dev, "SSP_ERROR_IRQ: %d\n",
546aca15765Sjkunz sc->sc_irq_error);
547aca15765Sjkunz }
548eba5cacbSjkunz
549aca15765Sjkunz /* Shift unsigned error code so it fits nicely to signed int. */
550aca15765Sjkunz cmd->c_error = sc->sc_irq_error >> 8;
551aca15765Sjkunz }
552eba5cacbSjkunz
553*6f8dc150Sandvar /* Check response from the card if such was requested. */
554eba5cacbSjkunz if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
555c332c3c0Sjkunz cmd->c_resp[0] = SSP_RD(sc, HW_SSP_SDRESP0);
556eba5cacbSjkunz if (ISSET(cmd->c_flags, SCF_RSP_136)) {
557c332c3c0Sjkunz cmd->c_resp[1] = SSP_RD(sc, HW_SSP_SDRESP1);
558c332c3c0Sjkunz cmd->c_resp[2] = SSP_RD(sc, HW_SSP_SDRESP2);
559c332c3c0Sjkunz cmd->c_resp[3] = SSP_RD(sc, HW_SSP_SDRESP3);
560eba5cacbSjkunz /*
561c332c3c0Sjkunz * Remove CRC7 + LSB by rotating all bits right by 8 to
562c332c3c0Sjkunz * make sdmmc __bitfield() happy.
563eba5cacbSjkunz */
564c332c3c0Sjkunz cmd->c_resp[0] >>= 8; /* Remove CRC7 + LSB. */
565c332c3c0Sjkunz cmd->c_resp[0] |= (0x000000FF & cmd->c_resp[1]) << 24;
566c332c3c0Sjkunz cmd->c_resp[1] >>= 8;
567c332c3c0Sjkunz cmd->c_resp[1] |= (0x000000FF & cmd->c_resp[2]) << 24;
568c332c3c0Sjkunz cmd->c_resp[2] >>= 8;
569c332c3c0Sjkunz cmd->c_resp[2] |= (0x000000FF & cmd->c_resp[3]) << 24;
570c332c3c0Sjkunz cmd->c_resp[3] >>= 8;
571c332c3c0Sjkunz }
572c332c3c0Sjkunz }
573aca15765Sjkunz
574aca15765Sjkunz bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamp);
575aca15765Sjkunz dmamem_unmap:
576aca15765Sjkunz bus_dmamem_unmap(sc->sc_dmat, dma_chain, sc->sc_chnsiz);
577aca15765Sjkunz out:
578aca15765Sjkunz
579eba5cacbSjkunz return;
580eba5cacbSjkunz }
581eba5cacbSjkunz
582eba5cacbSjkunz static void
issp_card_enable_intr(sdmmc_chipset_handle_t sch,int irq)583eba5cacbSjkunz issp_card_enable_intr(sdmmc_chipset_handle_t sch, int irq)
584eba5cacbSjkunz {
585eba5cacbSjkunz struct issp_softc *sc = sch;
586aca15765Sjkunz aprint_error_dev(sc->sc_dev, "issp_card_enable_intr not implemented\n");
587eba5cacbSjkunz return;
588eba5cacbSjkunz }
589eba5cacbSjkunz
590eba5cacbSjkunz static void
issp_card_intr_ack(sdmmc_chipset_handle_t sch)591eba5cacbSjkunz issp_card_intr_ack(sdmmc_chipset_handle_t sch)
592eba5cacbSjkunz {
593eba5cacbSjkunz struct issp_softc *sc = sch;
594aca15765Sjkunz aprint_error_dev(sc->sc_dev, "issp_card_intr_ack not implemented\n");
595eba5cacbSjkunz return;
596eba5cacbSjkunz }
597eba5cacbSjkunz
598eba5cacbSjkunz /*
599eba5cacbSjkunz * Reset the SSP block.
600eba5cacbSjkunz *
601c332c3c0Sjkunz * Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block"
602eba5cacbSjkunz */
603eba5cacbSjkunz static void
issp_reset(struct issp_softc * sc)604eba5cacbSjkunz issp_reset(struct issp_softc *sc)
605eba5cacbSjkunz {
606eba5cacbSjkunz unsigned int loop;
607eba5cacbSjkunz
608eba5cacbSjkunz /* Prepare for soft-reset by making sure that SFTRST is not currently
609eba5cacbSjkunz * asserted. Also clear CLKGATE so we can wait for its assertion below.
610eba5cacbSjkunz */
611c332c3c0Sjkunz SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_SFTRST);
612eba5cacbSjkunz
613eba5cacbSjkunz /* Wait at least a microsecond for SFTRST to deassert. */
614eba5cacbSjkunz loop = 0;
615c332c3c0Sjkunz while ((SSP_RD(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_SFTRST) ||
616eba5cacbSjkunz (loop < SSP_SOFT_RST_LOOP))
617eba5cacbSjkunz loop++;
618eba5cacbSjkunz
619eba5cacbSjkunz /* Clear CLKGATE so we can wait for its assertion below. */
620c332c3c0Sjkunz SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_CLKGATE);
621eba5cacbSjkunz
622eba5cacbSjkunz /* Soft-reset the block. */
623c332c3c0Sjkunz SSP_WR(sc, HW_SSP_CTRL0_SET, HW_SSP_CTRL0_SFTRST);
624eba5cacbSjkunz
625eba5cacbSjkunz /* Wait until clock is in the gated state. */
626c332c3c0Sjkunz while (!(SSP_RD(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_CLKGATE));
627eba5cacbSjkunz
628eba5cacbSjkunz /* Bring block out of reset. */
629c332c3c0Sjkunz SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_SFTRST);
630eba5cacbSjkunz
631eba5cacbSjkunz loop = 0;
632c332c3c0Sjkunz while ((SSP_RD(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_SFTRST) ||
633eba5cacbSjkunz (loop < SSP_SOFT_RST_LOOP))
634eba5cacbSjkunz loop++;
635eba5cacbSjkunz
636c332c3c0Sjkunz SSP_WR(sc, HW_SSP_CTRL0_CLR, HW_SSP_CTRL0_CLKGATE);
637eba5cacbSjkunz
638eba5cacbSjkunz /* Wait until clock is in the NON-gated state. */
639c332c3c0Sjkunz while (SSP_RD(sc, HW_SSP_CTRL0) & HW_SSP_CTRL0_CLKGATE);
640eba5cacbSjkunz
641eba5cacbSjkunz return;
642eba5cacbSjkunz }
643eba5cacbSjkunz
644c332c3c0Sjkunz /*
645c332c3c0Sjkunz * Initialize SSP controller to SD/MMC mode.
646eba5cacbSjkunz */
647eba5cacbSjkunz static void
issp_init(struct issp_softc * sc)648eba5cacbSjkunz issp_init(struct issp_softc *sc)
649eba5cacbSjkunz {
650eba5cacbSjkunz uint32_t reg;
651eba5cacbSjkunz
652c332c3c0Sjkunz reg = SSP_RD(sc, HW_SSP_CTRL0);
653aca15765Sjkunz reg |= HW_SSP_CTRL0_ENABLE;
654aca15765Sjkunz
655aca15765Sjkunz /* Initial data bus width is 1-bit. */
656eba5cacbSjkunz reg &= ~(HW_SSP_CTRL0_BUS_WIDTH);
657c332c3c0Sjkunz reg |= __SHIFTIN(BUS_WIDTH_1_BIT, HW_SSP_CTRL0_BUS_WIDTH) |
658c332c3c0Sjkunz HW_SSP_CTRL0_WAIT_FOR_IRQ | HW_SSP_CTRL0_ENABLE;
659c332c3c0Sjkunz SSP_WR(sc, HW_SSP_CTRL0, reg);
660aca15765Sjkunz sc->sc_bus_width = BUS_WIDTH_1_BIT;
661eba5cacbSjkunz
662c332c3c0Sjkunz /* Set data timeout. */
663c332c3c0Sjkunz reg = SSP_RD(sc, HW_SSP_TIMING);
664c332c3c0Sjkunz reg &= ~(HW_SSP_TIMING_TIMEOUT);
665c332c3c0Sjkunz reg |= __SHIFTIN(DATA_TIMEOUT, HW_SSP_TIMING_TIMEOUT);
666aca15765Sjkunz SSP_WR(sc, HW_SSP_TIMING, reg);
667c332c3c0Sjkunz
668c332c3c0Sjkunz /* Set initial clock rate to minimum. */
669c332c3c0Sjkunz issp_set_sck(sc, SSP_CLK_MIN * 1000);
670c332c3c0Sjkunz
671c332c3c0Sjkunz reg = SSP_RD(sc, HW_SSP_CTRL1);
672aca15765Sjkunz /* Enable all but SDIO IRQ's. */
673aca15765Sjkunz reg |= HW_SSP_CTRL1_RESP_ERR_IRQ_EN |
674aca15765Sjkunz HW_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN |
675aca15765Sjkunz HW_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN |
676aca15765Sjkunz HW_SSP_CTRL1_DATA_CRC_IRQ_EN |
677aca15765Sjkunz HW_SSP_CTRL1_FIFO_UNDERRUN_EN |
678aca15765Sjkunz HW_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN |
679aca15765Sjkunz HW_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN;
680aca15765Sjkunz reg |= HW_SSP_CTRL1_DMA_ENABLE;
681aca15765Sjkunz reg |= HW_SSP_CTRL1_POLARITY;
682aca15765Sjkunz /* Set SD/MMC mode and use use 8-bits per word. */
683eba5cacbSjkunz reg &= ~(HW_SSP_CTRL1_WORD_LENGTH | HW_SSP_CTRL1_SSP_MODE);
684aca15765Sjkunz reg |= __SHIFTIN(0x7, HW_SSP_CTRL1_WORD_LENGTH) |
685eba5cacbSjkunz __SHIFTIN(0x3, HW_SSP_CTRL1_SSP_MODE);
686c332c3c0Sjkunz SSP_WR(sc, HW_SSP_CTRL1, reg);
687eba5cacbSjkunz
688eba5cacbSjkunz return;
689eba5cacbSjkunz }
690eba5cacbSjkunz
691eba5cacbSjkunz /*
692eba5cacbSjkunz * Set SSP_SCK clock rate to the value specified in target.
693eba5cacbSjkunz *
694eba5cacbSjkunz * SSP_SCK is calculated as: SSP_CLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE))
695eba5cacbSjkunz *
696aca15765Sjkunz * issp_set_sck finds the most suitable CLOCK_DIVIDE and CLOCK_RATE register
697eba5cacbSjkunz * values for the target clock rate by iterating through all possible register
698eba5cacbSjkunz * values.
699eba5cacbSjkunz */
700eba5cacbSjkunz static uint32_t
issp_set_sck(struct issp_softc * sc,uint32_t target)701eba5cacbSjkunz issp_set_sck(struct issp_softc *sc, uint32_t target)
702eba5cacbSjkunz {
703eba5cacbSjkunz uint32_t newclk, found, reg;
704eba5cacbSjkunz uint8_t div, rate, d, r;
705eba5cacbSjkunz
706eba5cacbSjkunz found = div = rate = 0;
707eba5cacbSjkunz
708eba5cacbSjkunz for (d = 2; d < 254; d++) {
709eba5cacbSjkunz for (r = 0; r < 255; r++) {
710eba5cacbSjkunz newclk = SSP_CLK / (d * (1 + r));
711eba5cacbSjkunz if (newclk == target) {
712eba5cacbSjkunz found = newclk;
713eba5cacbSjkunz div = d;
714eba5cacbSjkunz rate = r;
715eba5cacbSjkunz goto out;
716eba5cacbSjkunz }
717eba5cacbSjkunz if (newclk < target && newclk > found) {
718eba5cacbSjkunz found = newclk;
719eba5cacbSjkunz div = d;
720eba5cacbSjkunz rate = r;
721eba5cacbSjkunz }
722eba5cacbSjkunz }
723eba5cacbSjkunz }
724eba5cacbSjkunz out:
725c332c3c0Sjkunz reg = SSP_RD(sc, HW_SSP_TIMING);
726eba5cacbSjkunz reg &= ~(HW_SSP_TIMING_CLOCK_DIVIDE | HW_SSP_TIMING_CLOCK_RATE);
727eba5cacbSjkunz reg |= __SHIFTIN(div, HW_SSP_TIMING_CLOCK_DIVIDE) |
728eba5cacbSjkunz __SHIFTIN(rate, HW_SSP_TIMING_CLOCK_RATE);
729c332c3c0Sjkunz SSP_WR(sc, HW_SSP_TIMING, reg);
730eba5cacbSjkunz
731c332c3c0Sjkunz return SSP_CLK / (div * (1 + rate));
732eba5cacbSjkunz }
733aca15765Sjkunz
734aca15765Sjkunz /*
735aca15765Sjkunz * IRQ from DMA.
736aca15765Sjkunz */
737aca15765Sjkunz static int
issp_dma_intr(void * arg)738aca15765Sjkunz issp_dma_intr(void *arg)
739aca15765Sjkunz {
740aca15765Sjkunz issp_softc_t sc = arg;
741aca15765Sjkunz unsigned int dma_err;
742aca15765Sjkunz
743aca15765Sjkunz dma_err = apbdma_intr_status(sc->sc_dmac, sc->dma_channel);
744aca15765Sjkunz
745aca15765Sjkunz if (dma_err) {
746aca15765Sjkunz apbdma_ack_error_intr(sc->sc_dmac, sc->dma_channel);
747aca15765Sjkunz } else {
748aca15765Sjkunz apbdma_ack_intr(sc->sc_dmac, sc->dma_channel);
749aca15765Sjkunz }
750aca15765Sjkunz
751aca15765Sjkunz mutex_enter(&sc->sc_lock);
752aca15765Sjkunz
753aca15765Sjkunz sc->sc_dma_error = dma_err;
754aca15765Sjkunz sc->sc_state = SSP_STATE_IDLE;
755aca15765Sjkunz
756aca15765Sjkunz /* Signal thread that interrupt was handled. */
757aca15765Sjkunz cv_signal(&sc->sc_intr_cv);
758aca15765Sjkunz
759aca15765Sjkunz mutex_exit(&sc->sc_lock);
760aca15765Sjkunz
761aca15765Sjkunz /* Return 1 to acknowledge IRQ. */
762aca15765Sjkunz return 1;
763aca15765Sjkunz }
764aca15765Sjkunz
765aca15765Sjkunz /*
766aca15765Sjkunz * IRQ from SSP block.
767aca15765Sjkunz *
768aca15765Sjkunz * When SSP receives IRQ it terminates ongoing DMA transfer by issuing DMATERM
769aca15765Sjkunz * signal to DMA block.
770aca15765Sjkunz */
771aca15765Sjkunz static int
issp_error_intr(void * arg)772aca15765Sjkunz issp_error_intr(void *arg)
773aca15765Sjkunz {
774aca15765Sjkunz issp_softc_t sc = arg;
775aca15765Sjkunz
776aca15765Sjkunz mutex_enter(&sc->sc_lock);
777aca15765Sjkunz
778aca15765Sjkunz sc->sc_irq_error =
779aca15765Sjkunz SSP_RD(sc, HW_SSP_CTRL1) & HW_SSP_CTRL1_IRQ_MASK;
780aca15765Sjkunz
781aca15765Sjkunz issp_ack_intr(sc);
782aca15765Sjkunz
783aca15765Sjkunz mutex_exit(&sc->sc_lock);
784aca15765Sjkunz
785aca15765Sjkunz /* Return 1 to acknowledge IRQ. */
786aca15765Sjkunz return 1;
787aca15765Sjkunz }
788aca15765Sjkunz
789aca15765Sjkunz /*
790aca15765Sjkunz * Acknowledge SSP error IRQ.
791aca15765Sjkunz */
792aca15765Sjkunz static void
issp_ack_intr(struct issp_softc * sc)793aca15765Sjkunz issp_ack_intr(struct issp_softc *sc)
794aca15765Sjkunz {
795aca15765Sjkunz
796aca15765Sjkunz /* Acknowledge all IRQ's. */
797aca15765Sjkunz SSP_WR(sc, HW_SSP_CTRL1_CLR, HW_SSP_CTRL1_IRQ_MASK);
798aca15765Sjkunz
799aca15765Sjkunz return;
800aca15765Sjkunz }
801aca15765Sjkunz
802aca15765Sjkunz /*
803aca15765Sjkunz * Set up multi block DMA transfer.
804aca15765Sjkunz */
805aca15765Sjkunz static void
issp_create_dma_cmd_list_multi(issp_softc_t sc,void * dma_chain,struct sdmmc_command * cmd)806aca15765Sjkunz issp_create_dma_cmd_list_multi(issp_softc_t sc, void *dma_chain,
807aca15765Sjkunz struct sdmmc_command *cmd)
808aca15765Sjkunz {
809aca15765Sjkunz apbdma_command_t dma_cmd;
810aca15765Sjkunz int blocks;
811aca15765Sjkunz int nblk;
812aca15765Sjkunz
813aca15765Sjkunz blocks = cmd->c_datalen / cmd->c_blklen;
814aca15765Sjkunz nblk = 0;
815aca15765Sjkunz dma_cmd = dma_chain;
816aca15765Sjkunz
817aca15765Sjkunz /* HEAD */
818aca15765Sjkunz apbdma_cmd_buf(&dma_cmd[nblk], cmd->c_blklen * nblk, cmd->c_dmamap);
819aca15765Sjkunz apbdma_cmd_chain(&dma_cmd[nblk], &dma_cmd[nblk+1], dma_chain,
820aca15765Sjkunz sc->sc_dmamp);
821aca15765Sjkunz
822aca15765Sjkunz dma_cmd[nblk].control =
823aca15765Sjkunz __SHIFTIN(cmd->c_blklen, APBDMA_CMD_XFER_COUNT) |
824aca15765Sjkunz __SHIFTIN(3, APBDMA_CMD_CMDPIOWORDS) | APBDMA_CMD_HALTONTERMINATE |
825aca15765Sjkunz APBDMA_CMD_CHAIN;
826aca15765Sjkunz
827aca15765Sjkunz if (!ISSET(cmd->c_flags, SCF_RSP_CRC)) {
828aca15765Sjkunz dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |=
829aca15765Sjkunz HW_SSP_CTRL0_IGNORE_CRC;
830aca15765Sjkunz }
831aca15765Sjkunz
832aca15765Sjkunz dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_DATA_XFER |
833aca15765Sjkunz __SHIFTIN(sc->sc_bus_width, HW_SSP_CTRL0_BUS_WIDTH) |
834aca15765Sjkunz HW_SSP_CTRL0_WAIT_FOR_IRQ |
835aca15765Sjkunz __SHIFTIN(cmd->c_datalen, HW_SSP_CTRL0_XFER_COUNT);
836aca15765Sjkunz
837aca15765Sjkunz if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
838aca15765Sjkunz dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |=
839aca15765Sjkunz HW_SSP_CTRL0_GET_RESP;
840aca15765Sjkunz if (ISSET(cmd->c_flags, SCF_RSP_136)) {
841aca15765Sjkunz dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |=
842aca15765Sjkunz HW_SSP_CTRL0_LONG_RESP;
843aca15765Sjkunz }
844aca15765Sjkunz }
845aca15765Sjkunz
846aca15765Sjkunz dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_ENABLE;
847aca15765Sjkunz
848aca15765Sjkunz dma_cmd[nblk].pio_words[PIO_WORD_CMD0] =
849aca15765Sjkunz __SHIFTIN(ffs(cmd->c_blklen) - 1, HW_SSP_CMD0_BLOCK_SIZE) |
850aca15765Sjkunz __SHIFTIN(blocks - 1, HW_SSP_CMD0_BLOCK_COUNT) |
851aca15765Sjkunz __SHIFTIN(cmd->c_opcode, HW_SSP_CMD0_CMD);
852aca15765Sjkunz
853aca15765Sjkunz dma_cmd[nblk].pio_words[PIO_WORD_CMD1] = cmd->c_arg;
854aca15765Sjkunz
855aca15765Sjkunz if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
856aca15765Sjkunz dma_cmd[nblk].control |=
857aca15765Sjkunz __SHIFTIN(APBDMA_CMD_DMA_WRITE, APBDMA_CMD_COMMAND);
858aca15765Sjkunz dma_cmd[nblk].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_READ;
859aca15765Sjkunz } else {
860aca15765Sjkunz dma_cmd[nblk].control |=
861aca15765Sjkunz __SHIFTIN(APBDMA_CMD_DMA_READ, APBDMA_CMD_COMMAND);
862aca15765Sjkunz }
863aca15765Sjkunz
864aca15765Sjkunz nblk++;
865aca15765Sjkunz
866aca15765Sjkunz /* BODY: Build commands for blocks between head and tail, if any. */
867aca15765Sjkunz for (; nblk < blocks - 1; nblk++) {
868aca15765Sjkunz
869aca15765Sjkunz apbdma_cmd_buf(&dma_cmd[nblk], cmd->c_blklen * nblk,
870aca15765Sjkunz cmd->c_dmamap);
871aca15765Sjkunz
872aca15765Sjkunz apbdma_cmd_chain(&dma_cmd[nblk], &dma_cmd[nblk+1], dma_chain,
873aca15765Sjkunz sc->sc_dmamp);
874aca15765Sjkunz
875aca15765Sjkunz dma_cmd[nblk].control =
876aca15765Sjkunz __SHIFTIN(cmd->c_blklen, APBDMA_CMD_XFER_COUNT) |
877aca15765Sjkunz APBDMA_CMD_HALTONTERMINATE | APBDMA_CMD_CHAIN;
878aca15765Sjkunz
879aca15765Sjkunz if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
880aca15765Sjkunz dma_cmd[nblk].control |=
881aca15765Sjkunz __SHIFTIN(APBDMA_CMD_DMA_WRITE,
882aca15765Sjkunz APBDMA_CMD_COMMAND);
883aca15765Sjkunz } else {
884aca15765Sjkunz dma_cmd[nblk].control |=
885aca15765Sjkunz __SHIFTIN(APBDMA_CMD_DMA_READ, APBDMA_CMD_COMMAND);
886aca15765Sjkunz }
887aca15765Sjkunz }
888aca15765Sjkunz
889aca15765Sjkunz /* TAIL
890aca15765Sjkunz *
891aca15765Sjkunz * TODO: Send CMD12/STOP with last DMA command to support
892aca15765Sjkunz * SMC_CAPS_AUTO_STOP.
893aca15765Sjkunz */
894aca15765Sjkunz apbdma_cmd_buf(&dma_cmd[nblk], cmd->c_blklen * nblk, cmd->c_dmamap);
895aca15765Sjkunz /* next = NULL */
896aca15765Sjkunz dma_cmd[nblk].control =
897aca15765Sjkunz __SHIFTIN(cmd->c_blklen, APBDMA_CMD_XFER_COUNT) |
898aca15765Sjkunz APBDMA_CMD_HALTONTERMINATE | APBDMA_CMD_WAIT4ENDCMD |
899aca15765Sjkunz APBDMA_CMD_SEMAPHORE | APBDMA_CMD_IRQONCMPLT;
900aca15765Sjkunz
901aca15765Sjkunz if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
902aca15765Sjkunz dma_cmd[nblk].control |= __SHIFTIN(APBDMA_CMD_DMA_WRITE,
903aca15765Sjkunz APBDMA_CMD_COMMAND);
904aca15765Sjkunz } else {
905aca15765Sjkunz dma_cmd[nblk].control |= __SHIFTIN(APBDMA_CMD_DMA_READ,
906aca15765Sjkunz APBDMA_CMD_COMMAND);
907aca15765Sjkunz }
908aca15765Sjkunz
909aca15765Sjkunz return;
910aca15765Sjkunz }
911aca15765Sjkunz
912aca15765Sjkunz /*
913aca15765Sjkunz * Set up single block DMA transfer.
914aca15765Sjkunz */
915aca15765Sjkunz static void
issp_create_dma_cmd_list_single(issp_softc_t sc,void * dma_chain,struct sdmmc_command * cmd)916aca15765Sjkunz issp_create_dma_cmd_list_single(issp_softc_t sc, void *dma_chain,
917aca15765Sjkunz struct sdmmc_command *cmd)
918aca15765Sjkunz {
919aca15765Sjkunz apbdma_command_t dma_cmd;
920aca15765Sjkunz
921aca15765Sjkunz dma_cmd = dma_chain;
922aca15765Sjkunz
923aca15765Sjkunz dma_cmd[0].control = __SHIFTIN(cmd->c_datalen, APBDMA_CMD_XFER_COUNT) |
924aca15765Sjkunz __SHIFTIN(3, APBDMA_CMD_CMDPIOWORDS) |
925aca15765Sjkunz APBDMA_CMD_HALTONTERMINATE | APBDMA_CMD_WAIT4ENDCMD |
926aca15765Sjkunz APBDMA_CMD_SEMAPHORE | APBDMA_CMD_IRQONCMPLT;
927aca15765Sjkunz
928aca15765Sjkunz /* Transfer single block to the beginning of the DMA buffer. */
929aca15765Sjkunz apbdma_cmd_buf(&dma_cmd[0], 0, cmd->c_dmamap);
930aca15765Sjkunz
931aca15765Sjkunz if (!ISSET(cmd->c_flags, SCF_RSP_CRC)) {
932aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
933aca15765Sjkunz HW_SSP_CTRL0_IGNORE_CRC;
934aca15765Sjkunz }
935aca15765Sjkunz
936aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
937aca15765Sjkunz HW_SSP_CTRL0_DATA_XFER |
938aca15765Sjkunz __SHIFTIN(sc->sc_bus_width, HW_SSP_CTRL0_BUS_WIDTH) |
939aca15765Sjkunz HW_SSP_CTRL0_WAIT_FOR_IRQ |
940aca15765Sjkunz __SHIFTIN(cmd->c_datalen, HW_SSP_CTRL0_XFER_COUNT);
941aca15765Sjkunz
942aca15765Sjkunz if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
943aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_GET_RESP;
944aca15765Sjkunz if (ISSET(cmd->c_flags, SCF_RSP_136)) {
945aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
946aca15765Sjkunz HW_SSP_CTRL0_LONG_RESP;
947aca15765Sjkunz }
948aca15765Sjkunz }
949aca15765Sjkunz
950aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_ENABLE;
951aca15765Sjkunz
952aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CMD0] =
953aca15765Sjkunz HW_SSP_CMD0_APPEND_8CYC |
954aca15765Sjkunz __SHIFTIN(cmd->c_opcode, HW_SSP_CMD0_CMD);
955aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CMD1] = cmd->c_arg;
956aca15765Sjkunz
957aca15765Sjkunz if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
958aca15765Sjkunz dma_cmd[0].control |=
959aca15765Sjkunz __SHIFTIN(APBDMA_CMD_DMA_WRITE, APBDMA_CMD_COMMAND);
960aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_READ;
961aca15765Sjkunz } else {
962aca15765Sjkunz dma_cmd[0].control |=
963aca15765Sjkunz __SHIFTIN(APBDMA_CMD_DMA_READ, APBDMA_CMD_COMMAND);
964aca15765Sjkunz }
965aca15765Sjkunz
966aca15765Sjkunz return;
967aca15765Sjkunz }
968aca15765Sjkunz
969aca15765Sjkunz /*
970aca15765Sjkunz * Do DMA PIO (issue CMD). No block transfers.
971aca15765Sjkunz */
972aca15765Sjkunz static void
issp_create_dma_cmd_list(issp_softc_t sc,void * dma_chain,struct sdmmc_command * cmd)973aca15765Sjkunz issp_create_dma_cmd_list(issp_softc_t sc, void *dma_chain,
974aca15765Sjkunz struct sdmmc_command *cmd)
975aca15765Sjkunz {
976aca15765Sjkunz apbdma_command_t dma_cmd;
977aca15765Sjkunz
978aca15765Sjkunz dma_cmd = dma_chain;
979aca15765Sjkunz
980aca15765Sjkunz dma_cmd[0].control = __SHIFTIN(3, APBDMA_CMD_CMDPIOWORDS) |
981aca15765Sjkunz APBDMA_CMD_HALTONTERMINATE | APBDMA_CMD_WAIT4ENDCMD |
982aca15765Sjkunz APBDMA_CMD_SEMAPHORE | APBDMA_CMD_IRQONCMPLT |
983aca15765Sjkunz __SHIFTIN(APBDMA_CMD_NO_DMA_XFER, APBDMA_CMD_COMMAND);
984aca15765Sjkunz
985aca15765Sjkunz if (!ISSET(cmd->c_flags, SCF_RSP_CRC)) {
986aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
987aca15765Sjkunz HW_SSP_CTRL0_IGNORE_CRC;
988aca15765Sjkunz }
989aca15765Sjkunz
990aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
991aca15765Sjkunz __SHIFTIN(sc->sc_bus_width, HW_SSP_CTRL0_BUS_WIDTH) |
992aca15765Sjkunz HW_SSP_CTRL0_WAIT_FOR_IRQ;
993aca15765Sjkunz
994aca15765Sjkunz if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
995aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_GET_RESP;
996aca15765Sjkunz if (ISSET(cmd->c_flags, SCF_RSP_136)) {
997aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CTRL0] |=
998aca15765Sjkunz HW_SSP_CTRL0_LONG_RESP;
999aca15765Sjkunz }
1000aca15765Sjkunz }
1001aca15765Sjkunz
1002aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CTRL0] |= HW_SSP_CTRL0_ENABLE;
1003aca15765Sjkunz
1004aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CMD0] =
1005aca15765Sjkunz __SHIFTIN(cmd->c_opcode, HW_SSP_CMD0_CMD);
1006aca15765Sjkunz dma_cmd[0].pio_words[PIO_WORD_CMD1] = cmd->c_arg;
1007aca15765Sjkunz
1008aca15765Sjkunz return;
1009aca15765Sjkunz }
1010