/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- lhd_rdreg
- lhd_wreg
- lhd_code_to_errno
- lhd_iodone
- lhd_irq
- lhd_open
- lhd_close
- lhd_ioctl
- lhd_reset
- lhd_io
- config_lhd
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 /*
31 * LAMEbus hard disk (lhd) driver.
32 */
33
34 #include <types.h>
35 #include <kern/errno.h>
36 #include <lib.h>
37 #include <uio.h>
38 #include <synch.h>
39 #include <platform/bus.h>
40 #include <vfs.h>
41 #include <lamebus/lhd.h>
42 #include "autoconf.h"
43
44 /* Registers (offsets within slot) */
45 #define LHD_REG_NSECT 0 /* Number of sectors */
46 #define LHD_REG_STAT 4 /* Status */
47 #define LHD_REG_SECT 8 /* Sector for I/O */
48 #define LHD_REG_RPM 12 /* Disk rotation speed (revs per minute) */
49
50 /* Status codes */
51 #define LHD_IDLE 0 /* Device idle */
52 #define LHD_WORKING 1 /* Operation in progress */
53 #define LHD_OK 4 /* Operation succeeded */
54 #define LHD_INVSECT 12 /* Invalid sector requested */
55 #define LHD_MEDIA 20 /* Media error */
56 #define LHD_ISWRITE 2 /* OR with above: I/O is a write */
57 #define LHD_STATEMASK 0x1d /* mask for masking out LHD_ISWRITE */
58
59 /* Buffer (offset within slot) */
60 #define LHD_BUFFER 32768
61
62 /*
63 * Shortcut for reading a register.
64 */
65 static
66 inline
67 uint32_t lhd_rdreg(struct lhd_softc *lh, uint32_t reg)
68 {
69 return bus_read_register(lh->lh_busdata, lh->lh_buspos, reg);
70 }
71
72 /*
73 * Shortcut for writing a register.
74 */
75 static
76 inline
77 void lhd_wreg(struct lhd_softc *lh, uint32_t reg, uint32_t val)
78 {
79 bus_write_register(lh->lh_busdata, lh->lh_buspos, reg, val);
80 }
81
82 /*
83 * Convert a result code from the hardware to an errno value.
84 */
85 static
86 int lhd_code_to_errno(struct lhd_softc *lh, int code)
87 {
88 switch (code & LHD_STATEMASK) {
89 case LHD_OK: return 0;
90 case LHD_INVSECT: return EINVAL;
91 case LHD_MEDIA: return EIO;
92 }
93 kprintf("lhd%d: Unknown result code %d\n", lh->lh_unit, code);
94 return EAGAIN;
95 }
96
97 /*
98 * Record that an I/O has completed: save the result and poke the
99 * completion semaphore.
100 */
101 static
102 void
103 lhd_iodone(struct lhd_softc *lh, int err)
104 {
105 lh->lh_result = err;
106 V(lh->lh_done);
107 }
108
109 /*
110 * Interrupt handler for lhd.
111 * Read the status register; if an operation finished, clear the status
112 * register and report completion.
113 */
114 void
115 lhd_irq(void *vlh)
116 {
117 struct lhd_softc *lh = vlh;
118 uint32_t val;
119
120 val = lhd_rdreg(lh, LHD_REG_STAT);
121
122 switch (val & LHD_STATEMASK) {
123 case LHD_IDLE:
124 case LHD_WORKING:
125 break;
126 case LHD_OK:
127 case LHD_INVSECT:
128 case LHD_MEDIA:
129 lhd_wreg(lh, LHD_REG_STAT, 0);
130 lhd_iodone(lh, lhd_code_to_errno(lh, val));
131 break;
132 }
133 }
134
135 /*
136 * Function called when we are open()'d.
137 */
138 static
139 int
140 lhd_open(struct device *d, int openflags)
141 {
142 /*
143 * Don't need to do anything.
144 */
145 (void)d;
146 (void)openflags;
147
148 return 0;
149 }
150
151 /*
152 * Function called when we are close()'d.
153 */
154 static
155 int
156 lhd_close(struct device *d)
157 {
158 /*
159 * Don't need to do anything.
160 */
161 (void)d;
162
163 return 0;
164 }
165
166 /*
167 * Function for handling ioctls.
168 */
169 static
170 int
171 lhd_ioctl(struct device *d, int op, userptr_t data)
172 {
173 /*
174 * We don't support any ioctls.
175 */
176 (void)d;
177 (void)op;
178 (void)data;
179 return EIOCTL;
180 }
181
182 #if 0
183 /*
184 * Reset the device.
185 * This could be used, for instance, on timeout, if you implement suitable
186 * facilities.
187 */
188 static
189 void
190 lhd_reset(struct lhd_softc *lh)
191 {
192 lhd_wreg(lh, LHD_REG_STAT, 0);
193 }
194 #endif
195
196 /*
197 * I/O function (for both reads and writes)
198 */
199 static
200 int
201 lhd_io(struct device *d, struct uio *uio)
202 {
203 struct lhd_softc *lh = d->d_data;
204
205 uint32_t sector = uio->uio_offset / LHD_SECTSIZE;
206 uint32_t sectoff = uio->uio_offset % LHD_SECTSIZE;
207 uint32_t len = uio->uio_resid / LHD_SECTSIZE;
208 uint32_t lenoff = uio->uio_resid % LHD_SECTSIZE;
209 uint32_t i;
210 uint32_t statval = LHD_WORKING;
211 int result;
212
213 /* Don't allow I/O that isn't sector-aligned. */
214 if (sectoff != 0 || lenoff != 0) {
215 return EINVAL;
216 }
217
218 /* Don't allow I/O past the end of the disk. */
219 if (sector+len > lh->lh_dev.d_blocks) {
220 return EINVAL;
221 }
222
223 /* Set up the value to write into the status register. */
224 if (uio->uio_rw==UIO_WRITE) {
225 statval |= LHD_ISWRITE;
226 }
227
228 /* Loop over all the sectors we were asked to do. */
229 for (i=0; i<len; i++) {
230
231 /* Wait until nobody else is using the device. */
232 P(lh->lh_clear);
233
234 /*
235 * Are we writing? If so, transfer the data to the
236 * on-card buffer.
237 */
238 if (uio->uio_rw == UIO_WRITE) {
239 result = uiomove(lh->lh_buf, LHD_SECTSIZE, uio);
240 if (result) {
241 V(lh->lh_clear);
242 return result;
243 }
244 }
245
246 /* Tell it what sector we want... */
247 lhd_wreg(lh, LHD_REG_SECT, sector+i);
248
249 /* and start the operation. */
250 lhd_wreg(lh, LHD_REG_STAT, statval);
251
252 /* Now wait until the interrupt handler tells us we're done. */
253 P(lh->lh_done);
254
255 /* Get the result value saved by the interrupt handler. */
256 result = lh->lh_result;
257
258 /*
259 * Are we reading? If so, and if we succeeded,
260 * transfer the data out of the on-card buffer.
261 */
262 if (result==0 && uio->uio_rw==UIO_READ) {
263 result = uiomove(lh->lh_buf, LHD_SECTSIZE, uio);
264 }
265
266 /* Tell another thread it's cleared to go ahead. */
267 V(lh->lh_clear);
268
269 /* If we failed, return the error. */
270 if (result) {
271 return result;
272 }
273 }
274
275 return 0;
276 }
277
278 /*
279 * Setup routine called by autoconf.c when an lhd is found.
280 */
281 int
282 config_lhd(struct lhd_softc *lh, int lhdno)
283 {
284 char name[32];
285
286 /* Figure out what our name is. */
287 snprintf(name, sizeof(name), "lhd%d", lhdno);
288
289 /* Get a pointer to the on-chip buffer. */
290 lh->lh_buf = bus_map_area(lh->lh_busdata, lh->lh_buspos, LHD_BUFFER);
291
292 /* Create the semaphores. */
293 lh->lh_clear = sem_create("lhd-clear", 1);
294 if (lh->lh_clear == NULL) {
295 return ENOMEM;
296 }
297 lh->lh_done = sem_create("lhd-done", 0);
298 if (lh->lh_done == NULL) {
299 sem_destroy(lh->lh_clear);
300 lh->lh_clear = NULL;
301 return ENOMEM;
302 }
303
304 /* Set up the VFS device structure. */
305 lh->lh_dev.d_open = lhd_open;
306 lh->lh_dev.d_close = lhd_close;
307 lh->lh_dev.d_io = lhd_io;
308 lh->lh_dev.d_ioctl = lhd_ioctl;
309 lh->lh_dev.d_blocks = bus_read_register(lh->lh_busdata, lh->lh_buspos,
310 LHD_REG_NSECT);
311 lh->lh_dev.d_blocksize = LHD_SECTSIZE;
312 lh->lh_dev.d_data = lh;
313
314 /* Add the VFS device structure to the VFS device list. */
315 return vfs_adddev(name, &lh->lh_dev, 1);
316 }