ata_dbdma.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. /*-
  2. * SPDX-License-Identifier: BSD-3-Clause
  3. *
  4. * Copyright 2008 by Nathan Whitehorn. All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. * 1. Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * 2. Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * 3. The name of the author may not be used to endorse or promote products
  15. * derived from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  22. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  24. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  25. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27. * SUCH DAMAGE.
  28. */
  29. #include <sys/cdefs.h>
  30. __FBSDID("* $FreeBSD$");
  31. /*
  32. * Common routines for the DMA engine on both the Apple Kauai and MacIO
  33. * ATA controllers.
  34. */
  35. #include <sys/param.h>
  36. #include <sys/systm.h>
  37. #include <sys/kernel.h>
  38. #include <sys/module.h>
  39. #include <sys/bus.h>
  40. #include <sys/malloc.h>
  41. #include <sys/sema.h>
  42. #include <sys/taskqueue.h>
  43. #include <vm/uma.h>
  44. #include <machine/stdarg.h>
  45. #include <machine/resource.h>
  46. #include <machine/bus.h>
  47. #include <sys/rman.h>
  48. #include <sys/ata.h>
  49. #include <dev/ata/ata-all.h>
  50. #include <dev/ata/ata-pci.h>
  51. #include <ata_if.h>
  52. #include "ata_dbdma.h"
  53. struct ata_dbdma_dmaload_args {
  54. struct ata_dbdma_channel *sc;
  55. int write;
  56. int nsegs;
  57. };
  58. static void
  59. ata_dbdma_setprd(void *xarg, bus_dma_segment_t *segs, int nsegs, int error)
  60. {
  61. struct ata_dbdma_dmaload_args *arg = xarg;
  62. struct ata_dbdma_channel *sc = arg->sc;
  63. int branch_type, command;
  64. int prev_stop;
  65. int i;
  66. mtx_lock(&sc->dbdma_mtx);
  67. prev_stop = sc->next_dma_slot-1;
  68. if (prev_stop < 0)
  69. prev_stop = 0xff;
  70. for (i = 0; i < nsegs; i++) {
  71. /* Loop back to the beginning if this is our last slot */
  72. if (sc->next_dma_slot == 0xff)
  73. branch_type = DBDMA_ALWAYS;
  74. else
  75. branch_type = DBDMA_NEVER;
  76. if (arg->write) {
  77. command = (i + 1 < nsegs) ? DBDMA_OUTPUT_MORE :
  78. DBDMA_OUTPUT_LAST;
  79. } else {
  80. command = (i + 1 < nsegs) ? DBDMA_INPUT_MORE :
  81. DBDMA_INPUT_LAST;
  82. }
  83. dbdma_insert_command(sc->dbdma, sc->next_dma_slot++,
  84. command, 0, segs[i].ds_addr, segs[i].ds_len,
  85. DBDMA_NEVER, branch_type, DBDMA_NEVER, 0);
  86. if (branch_type == DBDMA_ALWAYS)
  87. sc->next_dma_slot = 0;
  88. }
  89. /* We have a corner case where the STOP command is the last slot,
  90. * but you can't branch in STOP commands. So add a NOP branch here
  91. * and the STOP in slot 0. */
  92. if (sc->next_dma_slot == 0xff) {
  93. dbdma_insert_branch(sc->dbdma, sc->next_dma_slot, 0);
  94. sc->next_dma_slot = 0;
  95. }
  96. #if 0
  97. dbdma_insert_command(sc->dbdma, sc->next_dma_slot++,
  98. DBDMA_NOP, 0, 0, 0, DBDMA_ALWAYS, DBDMA_NEVER, DBDMA_NEVER, 0);
  99. #endif
  100. dbdma_insert_stop(sc->dbdma, sc->next_dma_slot++);
  101. dbdma_insert_nop(sc->dbdma, prev_stop);
  102. dbdma_sync_commands(sc->dbdma, BUS_DMASYNC_PREWRITE);
  103. mtx_unlock(&sc->dbdma_mtx);
  104. arg->nsegs = nsegs;
  105. }
  106. static int
  107. ata_dbdma_status(device_t dev)
  108. {
  109. struct ata_dbdma_channel *sc = device_get_softc(dev);
  110. struct ata_channel *ch = device_get_softc(dev);
  111. if (sc->sc_ch.dma.flags & ATA_DMA_ACTIVE) {
  112. return (!(dbdma_get_chan_status(sc->dbdma) &
  113. DBDMA_STATUS_ACTIVE));
  114. }
  115. if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
  116. DELAY(100);
  117. if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY)
  118. return 0;
  119. }
  120. return 1;
  121. }
  122. static int
  123. ata_dbdma_start(struct ata_request *request)
  124. {
  125. struct ata_dbdma_channel *sc = device_get_softc(request->parent);
  126. sc->sc_ch.dma.flags |= ATA_DMA_ACTIVE;
  127. dbdma_wake(sc->dbdma);
  128. return 0;
  129. }
  130. static void
  131. ata_dbdma_reset(device_t dev)
  132. {
  133. struct ata_dbdma_channel *sc = device_get_softc(dev);
  134. mtx_lock(&sc->dbdma_mtx);
  135. dbdma_stop(sc->dbdma);
  136. dbdma_insert_stop(sc->dbdma, 0);
  137. sc->next_dma_slot=1;
  138. dbdma_set_current_cmd(sc->dbdma, 0);
  139. sc->sc_ch.dma.flags &= ~ATA_DMA_ACTIVE;
  140. mtx_unlock(&sc->dbdma_mtx);
  141. }
  142. static int
  143. ata_dbdma_stop(struct ata_request *request)
  144. {
  145. struct ata_dbdma_channel *sc = device_get_softc(request->parent);
  146. uint16_t status;
  147. status = dbdma_get_chan_status(sc->dbdma);
  148. dbdma_pause(sc->dbdma);
  149. sc->sc_ch.dma.flags &= ~ATA_DMA_ACTIVE;
  150. if (status & DBDMA_STATUS_DEAD) {
  151. device_printf(request->parent,"DBDMA dead, resetting "
  152. "channel...\n");
  153. ata_dbdma_reset(request->parent);
  154. return ATA_S_ERROR;
  155. }
  156. if (!(status & DBDMA_STATUS_RUN)) {
  157. device_printf(request->parent,"DBDMA confused, stop called "
  158. "when channel is not running!\n");
  159. return ATA_S_ERROR;
  160. }
  161. if (status & DBDMA_STATUS_ACTIVE) {
  162. device_printf(request->parent,"DBDMA channel stopped "
  163. "prematurely\n");
  164. return ATA_S_ERROR;
  165. }
  166. return 0;
  167. }
  168. static int
  169. ata_dbdma_load(struct ata_request *request, void *addr, int *entries)
  170. {
  171. struct ata_channel *ch = device_get_softc(request->parent);
  172. struct ata_dbdma_dmaload_args args;
  173. int error;
  174. args.sc = device_get_softc(request->parent);
  175. args.write = !(request->flags & ATA_R_READ);
  176. if (!request->bytecount) {
  177. device_printf(request->dev,
  178. "FAILURE - zero length DMA transfer attempted\n");
  179. return EIO;
  180. }
  181. if (((uintptr_t)(request->data) & (ch->dma.alignment - 1)) ||
  182. (request->bytecount & (ch->dma.alignment - 1))) {
  183. device_printf(request->dev,
  184. "FAILURE - non aligned DMA transfer attempted\n");
  185. return EIO;
  186. }
  187. if (request->bytecount > ch->dma.max_iosize) {
  188. device_printf(request->dev,
  189. "FAILURE - oversized DMA transfer attempt %d > %d\n",
  190. request->bytecount, ch->dma.max_iosize);
  191. return EIO;
  192. }
  193. request->dma = &ch->dma.slot[0];
  194. if ((error = bus_dmamap_load(request->dma->data_tag,
  195. request->dma->data_map, request->data, request->bytecount,
  196. &ata_dbdma_setprd, &args, BUS_DMA_NOWAIT))) {
  197. device_printf(request->dev, "FAILURE - load data\n");
  198. goto error;
  199. }
  200. if (entries)
  201. *entries = args.nsegs;
  202. bus_dmamap_sync(request->dma->sg_tag, request->dma->sg_map,
  203. BUS_DMASYNC_PREWRITE);
  204. bus_dmamap_sync(request->dma->data_tag, request->dma->data_map,
  205. (request->flags & ATA_R_READ) ?
  206. BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
  207. return 0;
  208. error:
  209. ch->dma.unload(request);
  210. return EIO;
  211. }
  212. void
  213. ata_dbdma_dmainit(device_t dev)
  214. {
  215. struct ata_dbdma_channel *sc = device_get_softc(dev);
  216. int error;
  217. error = dbdma_allocate_channel(sc->dbdma_regs, sc->dbdma_offset,
  218. bus_get_dma_tag(dev), 256, &sc->dbdma);
  219. dbdma_set_wait_selector(sc->dbdma,1 << 7, 1 << 7);
  220. dbdma_insert_stop(sc->dbdma,0);
  221. sc->next_dma_slot=1;
  222. sc->sc_ch.dma.start = ata_dbdma_start;
  223. sc->sc_ch.dma.stop = ata_dbdma_stop;
  224. sc->sc_ch.dma.load = ata_dbdma_load;
  225. sc->sc_ch.dma.reset = ata_dbdma_reset;
  226. /*
  227. * DBDMA's field for transfer size is 16 bits. This will overflow
  228. * if we try to do a 64K transfer, so stop short of 64K.
  229. */
  230. sc->sc_ch.dma.segsize = 126 * DEV_BSIZE;
  231. ata_dmainit(dev);
  232. sc->sc_ch.hw.status = ata_dbdma_status;
  233. mtx_init(&sc->dbdma_mtx, "ATA DBDMA", NULL, MTX_DEF);
  234. }