00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034 #include <types.h>
00035 #include <kern/errno.h>
00036 #include <lib.h>
00037 #include <uio.h>
00038 #include <synch.h>
00039 #include <platform/bus.h>
00040 #include <vfs.h>
00041 #include <lamebus/lhd.h>
00042 #include "autoconf.h"
00043
00044
00045 #define LHD_REG_NSECT 0
00046 #define LHD_REG_STAT 4
00047 #define LHD_REG_SECT 8
00048 #define LHD_REG_RPM 12
00049
00050
00051 #define LHD_IDLE 0
00052 #define LHD_WORKING 1
00053 #define LHD_OK 4
00054 #define LHD_INVSECT 12
00055 #define LHD_MEDIA 20
00056 #define LHD_ISWRITE 2
00057 #define LHD_STATEMASK 0x1d
00058
00059
00060 #define LHD_BUFFER 32768
00061
00062
00063
00064
00065 static
00066 inline
00067 uint32_t lhd_rdreg(struct lhd_softc *lh, uint32_t reg)
00068 {
00069 return bus_read_register(lh->lh_busdata, lh->lh_buspos, reg);
00070 }
00071
00072
00073
00074
00075 static
00076 inline
00077 void lhd_wreg(struct lhd_softc *lh, uint32_t reg, uint32_t val)
00078 {
00079 bus_write_register(lh->lh_busdata, lh->lh_buspos, reg, val);
00080 }
00081
00082
00083
00084
00085 static
00086 int lhd_code_to_errno(struct lhd_softc *lh, int code)
00087 {
00088 switch (code & LHD_STATEMASK) {
00089 case LHD_OK: return 0;
00090 case LHD_INVSECT: return EINVAL;
00091 case LHD_MEDIA: return EIO;
00092 }
00093 kprintf("lhd%d: Unknown result code %d\n", lh->lh_unit, code);
00094 return EAGAIN;
00095 }
00096
00097
00098
00099
00100
00101 static
00102 void
00103 lhd_iodone(struct lhd_softc *lh, int err)
00104 {
00105 lh->lh_result = err;
00106 V(lh->lh_done);
00107 }
00108
00109
00110
00111
00112
00113
00114 void
00115 lhd_irq(void *vlh)
00116 {
00117 struct lhd_softc *lh = vlh;
00118 uint32_t val;
00119
00120 val = lhd_rdreg(lh, LHD_REG_STAT);
00121
00122 switch (val & LHD_STATEMASK) {
00123 case LHD_IDLE:
00124 case LHD_WORKING:
00125 break;
00126 case LHD_OK:
00127 case LHD_INVSECT:
00128 case LHD_MEDIA:
00129 lhd_wreg(lh, LHD_REG_STAT, 0);
00130 lhd_iodone(lh, lhd_code_to_errno(lh, val));
00131 break;
00132 }
00133 }
00134
00135
00136
00137
00138 static
00139 int
00140 lhd_open(struct device *d, int openflags)
00141 {
00142
00143
00144
00145 (void)d;
00146 (void)openflags;
00147
00148 return 0;
00149 }
00150
00151
00152
00153
00154 static
00155 int
00156 lhd_close(struct device *d)
00157 {
00158
00159
00160
00161 (void)d;
00162
00163 return 0;
00164 }
00165
00166
00167
00168
00169 static
00170 int
00171 lhd_ioctl(struct device *d, int op, userptr_t data)
00172 {
00173
00174
00175
00176 (void)d;
00177 (void)op;
00178 (void)data;
00179 return EIOCTL;
00180 }
00181
00182 #if 0
00183
00184
00185
00186
00187
00188 static
00189 void
00190 lhd_reset(struct lhd_softc *lh)
00191 {
00192 lhd_wreg(lh, LHD_REG_STAT, 0);
00193 }
00194 #endif
00195
00196
00197
00198
00199 static
00200 int
00201 lhd_io(struct device *d, struct uio *uio)
00202 {
00203 struct lhd_softc *lh = d->d_data;
00204
00205 uint32_t sector = uio->uio_offset / LHD_SECTSIZE;
00206 uint32_t sectoff = uio->uio_offset % LHD_SECTSIZE;
00207 uint32_t len = uio->uio_resid / LHD_SECTSIZE;
00208 uint32_t lenoff = uio->uio_resid % LHD_SECTSIZE;
00209 uint32_t i;
00210 uint32_t statval = LHD_WORKING;
00211 int result;
00212
00213
00214 if (sectoff != 0 || lenoff != 0) {
00215 return EINVAL;
00216 }
00217
00218
00219 if (sector+len > lh->lh_dev.d_blocks) {
00220 return EINVAL;
00221 }
00222
00223
00224 if (uio->uio_rw==UIO_WRITE) {
00225 statval |= LHD_ISWRITE;
00226 }
00227
00228
00229 for (i=0; i<len; i++) {
00230
00231
00232 P(lh->lh_clear);
00233
00234
00235
00236
00237
00238 if (uio->uio_rw == UIO_WRITE) {
00239 result = uiomove(lh->lh_buf, LHD_SECTSIZE, uio);
00240 if (result) {
00241 V(lh->lh_clear);
00242 return result;
00243 }
00244 }
00245
00246
00247 lhd_wreg(lh, LHD_REG_SECT, sector+i);
00248
00249
00250 lhd_wreg(lh, LHD_REG_STAT, statval);
00251
00252
00253 P(lh->lh_done);
00254
00255
00256 result = lh->lh_result;
00257
00258
00259
00260
00261
00262 if (result==0 && uio->uio_rw==UIO_READ) {
00263 result = uiomove(lh->lh_buf, LHD_SECTSIZE, uio);
00264 }
00265
00266
00267 V(lh->lh_clear);
00268
00269
00270 if (result) {
00271 return result;
00272 }
00273 }
00274
00275 return 0;
00276 }
00277
00278
00279
00280
00281 int
00282 config_lhd(struct lhd_softc *lh, int lhdno)
00283 {
00284 char name[32];
00285
00286
00287 snprintf(name, sizeof(name), "lhd%d", lhdno);
00288
00289
00290 lh->lh_buf = bus_map_area(lh->lh_busdata, lh->lh_buspos, LHD_BUFFER);
00291
00292
00293 lh->lh_clear = sem_create("lhd-clear", 1);
00294 if (lh->lh_clear == NULL) {
00295 return ENOMEM;
00296 }
00297 lh->lh_done = sem_create("lhd-done", 0);
00298 if (lh->lh_done == NULL) {
00299 sem_destroy(lh->lh_clear);
00300 lh->lh_clear = NULL;
00301 return ENOMEM;
00302 }
00303
00304
00305 lh->lh_dev.d_open = lhd_open;
00306 lh->lh_dev.d_close = lhd_close;
00307 lh->lh_dev.d_io = lhd_io;
00308 lh->lh_dev.d_ioctl = lhd_ioctl;
00309 lh->lh_dev.d_blocks = bus_read_register(lh->lh_busdata, lh->lh_buspos,
00310 LHD_REG_NSECT);
00311 lh->lh_dev.d_blocksize = LHD_SECTSIZE;
00312 lh->lh_dev.d_data = lh;
00313
00314
00315 return vfs_adddev(name, &lh->lh_dev, 1);
00316 }