lhd.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /*
  2. * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
  3. * The President and Fellows of Harvard College.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of the University nor the names of its contributors
  14. * may be used to endorse or promote products derived from this software
  15. * without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
  18. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
  21. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25. * LIABILITY, 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. /*
  30. * LAMEbus hard disk (lhd) driver.
  31. */
  32. #include <types.h>
  33. #include <kern/errno.h>
  34. #include <lib.h>
  35. #include <uio.h>
  36. #include <synch.h>
  37. #include <platform/bus.h>
  38. #include <vfs.h>
  39. #include <lamebus/lhd.h>
  40. #include "autoconf.h"
  41. /* Registers (offsets within slot) */
  42. #define LHD_REG_NSECT 0 /* Number of sectors */
  43. #define LHD_REG_STAT 4 /* Status */
  44. #define LHD_REG_SECT 8 /* Sector for I/O */
  45. #define LHD_REG_RPM 12 /* Disk rotation speed (revs per minute) */
  46. /* Status codes */
  47. #define LHD_IDLE 0 /* Device idle */
  48. #define LHD_WORKING 1 /* Operation in progress */
  49. #define LHD_OK 4 /* Operation succeeded */
  50. #define LHD_INVSECT 12 /* Invalid sector requested */
  51. #define LHD_MEDIA 20 /* Media error */
  52. #define LHD_ISWRITE 2 /* OR with above: I/O is a write */
  53. #define LHD_STATEMASK 0x1d /* mask for masking out LHD_ISWRITE */
  54. /* Buffer (offset within slot) */
  55. #define LHD_BUFFER 32768
  56. /*
  57. * Shortcut for reading a register.
  58. */
  59. static
  60. inline
  61. uint32_t lhd_rdreg(struct lhd_softc *lh, uint32_t reg)
  62. {
  63. return bus_read_register(lh->lh_busdata, lh->lh_buspos, reg);
  64. }
  65. /*
  66. * Shortcut for writing a register.
  67. */
  68. static
  69. inline
  70. void lhd_wreg(struct lhd_softc *lh, uint32_t reg, uint32_t val)
  71. {
  72. bus_write_register(lh->lh_busdata, lh->lh_buspos, reg, val);
  73. }
  74. /*
  75. * Convert a result code from the hardware to an errno value.
  76. */
  77. static
  78. int lhd_code_to_errno(struct lhd_softc *lh, int code)
  79. {
  80. switch (code & LHD_STATEMASK) {
  81. case LHD_OK: return 0;
  82. case LHD_INVSECT: return EINVAL;
  83. case LHD_MEDIA: return EIO;
  84. }
  85. kprintf("lhd%d: Unknown result code %d\n", lh->lh_unit, code);
  86. return EAGAIN;
  87. }
  88. /*
  89. * Record that an I/O has completed: save the result and poke the
  90. * completion semaphore.
  91. */
  92. static
  93. void
  94. lhd_iodone(struct lhd_softc *lh, int err)
  95. {
  96. lh->lh_result = err;
  97. V(lh->lh_done);
  98. }
  99. /*
  100. * Interrupt handler for lhd.
  101. * Read the status register; if an operation finished, clear the status
  102. * register and report completion.
  103. */
  104. void
  105. lhd_irq(void *vlh)
  106. {
  107. struct lhd_softc *lh = vlh;
  108. uint32_t val;
  109. val = lhd_rdreg(lh, LHD_REG_STAT);
  110. switch (val & LHD_STATEMASK) {
  111. case LHD_IDLE:
  112. case LHD_WORKING:
  113. break;
  114. case LHD_OK:
  115. case LHD_INVSECT:
  116. case LHD_MEDIA:
  117. lhd_wreg(lh, LHD_REG_STAT, 0);
  118. lhd_iodone(lh, lhd_code_to_errno(lh, val));
  119. break;
  120. }
  121. }
  122. /*
  123. * Function called when we are open()'d.
  124. */
  125. static
  126. int
  127. lhd_open(struct device *d, int openflags)
  128. {
  129. /*
  130. * Don't need to do anything.
  131. */
  132. (void)d;
  133. (void)openflags;
  134. return 0;
  135. }
  136. /*
  137. * Function called when we are close()'d.
  138. */
  139. static
  140. int
  141. lhd_close(struct device *d)
  142. {
  143. /*
  144. * Don't need to do anything.
  145. */
  146. (void)d;
  147. return 0;
  148. }
  149. /*
  150. * Function for handling ioctls.
  151. */
  152. static
  153. int
  154. lhd_ioctl(struct device *d, int op, userptr_t data)
  155. {
  156. /*
  157. * We don't support any ioctls.
  158. */
  159. (void)d;
  160. (void)op;
  161. (void)data;
  162. return EIOCTL;
  163. }
  164. #if 0
  165. /*
  166. * Reset the device.
  167. * This could be used, for instance, on timeout, if you implement suitable
  168. * facilities.
  169. */
  170. static
  171. void
  172. lhd_reset(struct lhd_softc *lh)
  173. {
  174. lhd_wreg(lh, LHD_REG_STAT, 0);
  175. }
  176. #endif
  177. /*
  178. * I/O function (for both reads and writes)
  179. */
  180. static
  181. int
  182. lhd_io(struct device *d, struct uio *uio)
  183. {
  184. struct lhd_softc *lh = d->d_data;
  185. uint32_t sector = uio->uio_offset / LHD_SECTSIZE;
  186. uint32_t sectoff = uio->uio_offset % LHD_SECTSIZE;
  187. uint32_t len = uio->uio_resid / LHD_SECTSIZE;
  188. uint32_t lenoff = uio->uio_resid % LHD_SECTSIZE;
  189. uint32_t i;
  190. uint32_t statval = LHD_WORKING;
  191. int result;
  192. /* Don't allow I/O that isn't sector-aligned. */
  193. if (sectoff != 0 || lenoff != 0) {
  194. return EINVAL;
  195. }
  196. /* Don't allow I/O past the end of the disk. */
  197. if (sector+len > lh->lh_dev.d_blocks) {
  198. return EINVAL;
  199. }
  200. /* Set up the value to write into the status register. */
  201. if (uio->uio_rw==UIO_WRITE) {
  202. statval |= LHD_ISWRITE;
  203. }
  204. /* Loop over all the sectors we were asked to do. */
  205. for (i=0; i<len; i++) {
  206. /* Wait until nobody else is using the device. */
  207. P(lh->lh_clear);
  208. /*
  209. * Are we writing? If so, transfer the data to the
  210. * on-card buffer.
  211. */
  212. if (uio->uio_rw == UIO_WRITE) {
  213. result = uiomove(lh->lh_buf, LHD_SECTSIZE, uio);
  214. if (result) {
  215. V(lh->lh_clear);
  216. return result;
  217. }
  218. }
  219. /* Tell it what sector we want... */
  220. lhd_wreg(lh, LHD_REG_SECT, sector+i);
  221. /* and start the operation. */
  222. lhd_wreg(lh, LHD_REG_STAT, statval);
  223. /* Now wait until the interrupt handler tells us we're done. */
  224. P(lh->lh_done);
  225. /* Get the result value saved by the interrupt handler. */
  226. result = lh->lh_result;
  227. /*
  228. * Are we reading? If so, and if we succeeded,
  229. * transfer the data out of the on-card buffer.
  230. */
  231. if (result==0 && uio->uio_rw==UIO_READ) {
  232. result = uiomove(lh->lh_buf, LHD_SECTSIZE, uio);
  233. }
  234. /* Tell another thread it's cleared to go ahead. */
  235. V(lh->lh_clear);
  236. /* If we failed, return the error. */
  237. if (result) {
  238. return result;
  239. }
  240. }
  241. return 0;
  242. }
  243. /*
  244. * Setup routine called by autoconf.c when an lhd is found.
  245. */
  246. int
  247. config_lhd(struct lhd_softc *lh, int lhdno)
  248. {
  249. char name[32];
  250. /* Figure out what our name is. */
  251. snprintf(name, sizeof(name), "lhd%d", lhdno);
  252. /* Get a pointer to the on-chip buffer. */
  253. lh->lh_buf = bus_map_area(lh->lh_busdata, lh->lh_buspos, LHD_BUFFER);
  254. /* Create the semaphores. */
  255. lh->lh_clear = sem_create("lhd-clear", 1);
  256. if (lh->lh_clear == NULL) {
  257. return ENOMEM;
  258. }
  259. lh->lh_done = sem_create("lhd-done", 0);
  260. if (lh->lh_done == NULL) {
  261. sem_destroy(lh->lh_clear);
  262. lh->lh_clear = NULL;
  263. return ENOMEM;
  264. }
  265. /* Set up the VFS device structure. */
  266. lh->lh_dev.d_open = lhd_open;
  267. lh->lh_dev.d_close = lhd_close;
  268. lh->lh_dev.d_io = lhd_io;
  269. lh->lh_dev.d_ioctl = lhd_ioctl;
  270. lh->lh_dev.d_blocks = bus_read_register(lh->lh_busdata, lh->lh_buspos,
  271. LHD_REG_NSECT);
  272. lh->lh_dev.d_blocksize = LHD_SECTSIZE;
  273. lh->lh_dev.d_data = lh;
  274. /* Add the VFS device structure to the VFS device list. */
  275. return vfs_adddev(name, &lh->lh_dev, 1);
  276. }