2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 1998, 1999 Nicolas Souchu
5 * Copyright (c) 2001 Alcove - Nicolas Souchu
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
36 * Iomega ZIP+ Matchmaker Parallel Port Interface driver
38 * Thanks to David Campbell work on the Linux driver and the Iomega specs
39 * Thanks to Thiebault Moeglin for the drive
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/module.h>
46 #include <sys/malloc.h>
52 #include <dev/ppbus/ppbio.h>
53 #include <dev/ppbus/ppbconf.h>
54 #include <dev/ppbus/ppb_msq.h>
55 #include <dev/ppbus/vpoio.h>
56 #include <dev/ppbus/ppb_1284.h>
60 #define VP0_SELTMO 5000 /* select timeout */
61 #define VP0_FAST_SPINTMO 500000 /* wait status timeout */
62 #define VP0_LOW_SPINTMO 5000000 /* wait status timeout */
64 #define VP0_SECTOR_SIZE 512
67 * Microcode to execute very fast I/O sequences at the lowest bus level.
70 #define WAIT_RET MS_PARAM(7, 2, MS_TYP_PTR)
71 #define WAIT_TMO MS_PARAM(1, 0, MS_TYP_INT)
73 #define DECLARE_WAIT_MICROSEQUENCE \
74 struct ppb_microseq wait_microseq[] = { \
78 MS_BRSET(nBUSY, 4 /* ready */), \
79 MS_DBRA(-2 /* loop */), \
81 MS_RET(1), /* timed out */ \
84 MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN ), \
85 MS_RET(0) /* no error */ \
88 #define SELECT_TARGET MS_PARAM(6, 1, MS_TYP_CHA)
90 #define DECLARE_SELECT_MICROSEQUENCE \
91 struct ppb_microseq select_microseq[] = { \
93 /* first, check there is nothing holding onto the bus */ \
96 MS_BRCLEAR(0x8, 2 /* _ready */), \
97 MS_DBRA(-2 /* _loop */), \
98 MS_RET(2), /* bus busy */ \
101 MS_DASS(MS_UNKNOWN /* 0x80 | 1 << target */), \
105 /* now, wait until the drive is ready */ \
106 MS_SET(VP0_SELTMO), \
108 MS_BRSET(0x8, 3 /* ready */), \
109 MS_DBRA(-2 /* loop */), \
112 MS_RET(VP0_ESELECT_TIMEOUT), \
118 static struct ppb_microseq transfer_epilog[] = {
126 #define CPP_S1 MS_PARAM(10, 2, MS_TYP_PTR)
127 #define CPP_S2 MS_PARAM(13, 2, MS_TYP_PTR)
128 #define CPP_S3 MS_PARAM(16, 2, MS_TYP_PTR)
129 #define CPP_PARAM MS_PARAM(17, 1, MS_TYP_CHA)
131 #define DECLARE_CPP_MICROSEQ \
132 struct ppb_microseq cpp_microseq[] = { \
133 MS_CASS(0x0c), MS_DELAY(2), \
134 MS_DASS(0xaa), MS_DELAY(10), \
135 MS_DASS(0x55), MS_DELAY(10), \
136 MS_DASS(0x00), MS_DELAY(10), \
137 MS_DASS(0xff), MS_DELAY(10), \
138 MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s1 */), \
139 MS_DASS(0x87), MS_DELAY(10), \
140 MS_RFETCH(MS_REG_STR, 0xb8, MS_UNKNOWN /* &s2 */), \
141 MS_DASS(0x78), MS_DELAY(10), \
142 MS_RFETCH(MS_REG_STR, 0x38, MS_UNKNOWN /* &s3 */), \
143 MS_DASS(MS_UNKNOWN /* param */), \
145 MS_CASS(0x0c), MS_DELAY(10), \
146 MS_CASS(0x0d), MS_DELAY(2), \
147 MS_CASS(0x0c), MS_DELAY(10), \
148 MS_DASS(0xff), MS_DELAY(10), \
152 #define NEGOCIATED_MODE MS_PARAM(2, 1, MS_TYP_CHA)
154 #define DECLARE_NEGOCIATE_MICROSEQ \
155 struct ppb_microseq negociate_microseq[] = { \
158 MS_DASS(MS_UNKNOWN /* mode */), \
162 MS_BRSET(0x20, 5 /* continue */), \
167 MS_RET(VP0_ENEGOCIATE), \
176 #define INB_NIBBLE_L MS_PARAM(3, 2, MS_TYP_PTR)
177 #define INB_NIBBLE_H MS_PARAM(6, 2, MS_TYP_PTR)
178 #define INB_NIBBLE_F MS_PARAM(9, 0, MS_TYP_FUN)
179 #define INB_NIBBLE_P MS_PARAM(9, 1, MS_TYP_PTR)
182 * This is the sub-microseqence for MS_GET in NIBBLE mode
183 * Retrieve the two nibbles and call the C function to generate the character
184 * and store it in the buffer (see nibble_inbyte_hook())
187 #define DECLARE_NIBBLE_INBYTE_SUBMICROSEQ \
188 struct ppb_microseq nibble_inbyte_submicroseq[] = { \
193 MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* low nibble */),\
196 MS_RFETCH(MS_REG_STR, MS_FETCH_ALL, MS_UNKNOWN /* high nibble */),\
199 /* do a C call to format the received nibbles */ \
200 MS_C_CALL(MS_UNKNOWN /* C hook */, MS_UNKNOWN /* param */), \
201 MS_DBRA(-7 /* loop */), \
205 static struct ppb_microseq reset_microseq[] = {
218 * nibble_inbyte_hook()
220 * Formats high and low nibble into a character
223 nibble_inbyte_hook (void *p, char *ptr)
225 struct vpo_nibble *s = (struct vpo_nibble *)p;
227 /* increment the buffer pointer */
228 *ptr = ((s->l >> 4) & 0x0f) + (s->h & 0xf0);
234 * This is the sub-microseqence for MS_GET in PS2 mode
236 static struct ppb_microseq ps2_inbyte_submicroseq[] = {
241 MS_RFETCH_P(1, MS_REG_DTR, MS_FETCH_ALL),
243 MS_DBRA(-4 /* loop */),
249 * This is the sub-microsequence for MS_PUT in both NIBBLE and PS2 modes
251 static struct ppb_microseq spp_outbyte_submicroseq[] = {
255 MS_RASSERT_P(1, MS_REG_DTR),
257 MS_DBRA(0), /* decrement counter */
258 MS_RASSERT_P(1, MS_REG_DTR),
260 MS_DBRA(-6 /* loop */),
262 /* return from the put call */
267 /* EPP 1.7 microsequences, ptr and len set at runtime */
268 static struct ppb_microseq epp17_outstr[] = {
270 MS_RASSERT_P(MS_ACCUM, MS_REG_EPP_D),
275 static struct ppb_microseq epp17_instr[] = {
277 MS_RFETCH_P(MS_ACCUM, MS_REG_EPP_D, MS_FETCH_ALL),
283 imm_disconnect(struct vpoio_data *vpo, int *connected, int release_bus)
285 DECLARE_CPP_MICROSEQ;
287 device_t ppbus = device_get_parent(vpo->vpo_dev);
291 /* all should be ok */
295 ppb_MS_init_msq(cpp_microseq, 4, CPP_S1, (void *)&s1,
296 CPP_S2, (void *)&s2, CPP_S3, (void *)&s3,
299 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
301 if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x38)) {
303 device_printf(vpo->vpo_dev,
304 "(disconnect) s1=0x%x s2=0x%x, s3=0x%x\n",
305 s1 & 0xff, s2 & 0xff, s3 & 0xff);
307 *connected = VP0_ECONNECT;
311 return (ppb_release_bus(ppbus, vpo->vpo_dev));
317 * how : PPB_WAIT or PPB_DONTWAIT
320 imm_connect(struct vpoio_data *vpo, int how, int *disconnected, int request_bus)
322 DECLARE_CPP_MICROSEQ;
324 device_t ppbus = device_get_parent(vpo->vpo_dev);
329 /* all should be ok */
334 if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, how)))
337 ppb_MS_init_msq(cpp_microseq, 3, CPP_S1, (void *)&s1,
338 CPP_S2, (void *)&s2, CPP_S3, (void *)&s3);
340 /* select device 0 in compatible mode */
341 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0);
342 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
344 /* disconnect all devices */
345 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x30);
346 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
348 if (PPB_IN_EPP_MODE(ppbus))
349 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0x28);
351 ppb_MS_init_msq(cpp_microseq, 1, CPP_PARAM, 0xe0);
353 ppb_MS_microseq(ppbus, vpo->vpo_dev, cpp_microseq, &ret);
355 if ((s1 != (char)0xb8 || s2 != (char)0x18 || s3 != (char)0x30)) {
357 device_printf(vpo->vpo_dev,
358 "(connect) s1=0x%x s2=0x%x, s3=0x%x\n",
359 s1 & 0xff, s2 & 0xff, s3 & 0xff);
361 *disconnected = VP0_ECONNECT;
370 * Detect and initialise the VP0 adapter.
373 imm_detect(struct vpoio_data *vpo)
375 device_t ppbus = device_get_parent(vpo->vpo_dev);
378 if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_DONTWAIT)))
381 /* disconnect the drive, keep the bus */
382 imm_disconnect(vpo, NULL, 0);
384 vpo->vpo_mode_found = VP0_MODE_UNDEFINED;
387 /* try to enter EPP mode since vpoio failure put the bus in NIBBLE */
388 if (ppb_set_mode(ppbus, PPB_EPP) != -1) {
389 imm_connect(vpo, PPB_DONTWAIT, &error, 0);
392 /* if connection failed try PS/2 then NIBBLE modes */
394 if (ppb_set_mode(ppbus, PPB_PS2) != -1) {
395 imm_connect(vpo, PPB_DONTWAIT, &error, 0);
398 if (ppb_set_mode(ppbus, PPB_NIBBLE) != -1) {
399 imm_connect(vpo, PPB_DONTWAIT, &error, 0);
402 vpo->vpo_mode_found = VP0_MODE_NIBBLE;
404 device_printf(vpo->vpo_dev,
405 "NIBBLE mode unavailable!\n");
409 vpo->vpo_mode_found = VP0_MODE_PS2;
412 vpo->vpo_mode_found = VP0_MODE_EPP;
415 /* send SCSI reset signal */
416 ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL);
418 /* release the bus now */
419 imm_disconnect(vpo, &error, 1);
421 /* ensure we are disconnected or daisy chained peripheral
422 * may cause serious problem to the disk */
426 device_printf(vpo->vpo_dev,
427 "can't disconnect from the drive\n");
434 ppb_release_bus(ppbus, vpo->vpo_dev);
435 return (VP0_EINITFAILED);
442 imm_outstr(struct vpoio_data *vpo, char *buffer, int size)
444 device_t ppbus = device_get_parent(vpo->vpo_dev);
447 if (PPB_IN_EPP_MODE(ppbus))
448 ppb_reset_epp_timeout(ppbus);
450 ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_PUT, (union ppb_insarg)buffer,
451 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
460 imm_instr(struct vpoio_data *vpo, char *buffer, int size)
462 device_t ppbus = device_get_parent(vpo->vpo_dev);
465 if (PPB_IN_EPP_MODE(ppbus))
466 ppb_reset_epp_timeout(ppbus);
468 ppb_MS_exec(ppbus, vpo->vpo_dev, MS_OP_GET, (union ppb_insarg)buffer,
469 (union ppb_insarg)size, (union ppb_insarg)MS_UNKNOWN, &error);
475 imm_select(struct vpoio_data *vpo, int initiator, int target)
477 DECLARE_SELECT_MICROSEQUENCE;
478 device_t ppbus = device_get_parent(vpo->vpo_dev);
481 /* initialize the select microsequence */
482 ppb_MS_init_msq(select_microseq, 1,
483 SELECT_TARGET, 1 << initiator | 1 << target);
485 ppb_MS_microseq(ppbus, vpo->vpo_dev, select_microseq, &ret);
493 * H_SELIN must be low.
497 imm_wait(struct vpoio_data *vpo, int tmo)
499 DECLARE_WAIT_MICROSEQUENCE;
501 device_t ppbus = device_get_parent(vpo->vpo_dev);
505 * Return some status information.
506 * Semantics : 0x88 = ZIP+ wants more data
507 * 0x98 = ZIP+ wants to send more data
508 * 0xa8 = ZIP+ wants command
509 * 0xb8 = end of transfer, ZIP+ is sending status
512 ppb_MS_init_msq(wait_microseq, 2,
513 WAIT_RET, (void *)&ret,
516 ppb_MS_microseq(ppbus, vpo->vpo_dev, wait_microseq, &err);
519 return (0); /* command timed out */
525 imm_negociate(struct vpoio_data *vpo)
527 DECLARE_NEGOCIATE_MICROSEQ;
528 device_t ppbus = device_get_parent(vpo->vpo_dev);
532 if (PPB_IN_NIBBLE_MODE(ppbus))
534 else if (PPB_IN_PS2_MODE(ppbus))
539 #if 0 /* XXX use standalone code not to depend on ppb_1284 code yet */
540 ret = ppb_1284_negociate(ppbus, negociate_mode);
543 return (VP0_ENEGOCIATE);
546 ppb_MS_init_msq(negociate_microseq, 1,
547 NEGOCIATED_MODE, negociate_mode);
549 ppb_MS_microseq(ppbus, vpo->vpo_dev, negociate_microseq, &ret);
557 * Low level probe of vpo device
561 imm_probe(device_t dev, struct vpoio_data *vpo)
565 /* ppbus dependent initialisation */
568 /* now, try to initialise the drive */
569 if ((error = imm_detect(vpo))) {
579 * Low level attachment of vpo device
583 imm_attach(struct vpoio_data *vpo)
585 DECLARE_NIBBLE_INBYTE_SUBMICROSEQ;
586 device_t ppbus = device_get_parent(vpo->vpo_dev);
590 * Initialize microsequence code
592 vpo->vpo_nibble_inbyte_msq = (struct ppb_microseq *)malloc(
593 sizeof(nibble_inbyte_submicroseq), M_DEVBUF, M_NOWAIT);
595 if (!vpo->vpo_nibble_inbyte_msq)
598 bcopy((void *)nibble_inbyte_submicroseq,
599 (void *)vpo->vpo_nibble_inbyte_msq,
600 sizeof(nibble_inbyte_submicroseq));
602 ppb_MS_init_msq(vpo->vpo_nibble_inbyte_msq, 4,
603 INB_NIBBLE_H, (void *)&(vpo)->vpo_nibble.h,
604 INB_NIBBLE_L, (void *)&(vpo)->vpo_nibble.l,
605 INB_NIBBLE_F, nibble_inbyte_hook,
606 INB_NIBBLE_P, (void *)&(vpo)->vpo_nibble);
609 * Initialize mode dependent in/out microsequences
612 if ((error = ppb_request_bus(ppbus, vpo->vpo_dev, PPB_WAIT)))
615 /* ppbus automatically restore the last mode entered during detection */
616 switch (vpo->vpo_mode_found) {
618 ppb_MS_GET_init(ppbus, vpo->vpo_dev, epp17_instr);
619 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, epp17_outstr);
620 device_printf(vpo->vpo_dev, "EPP mode\n");
623 ppb_MS_GET_init(ppbus, vpo->vpo_dev, ps2_inbyte_submicroseq);
624 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
625 device_printf(vpo->vpo_dev, "PS2 mode\n");
627 case VP0_MODE_NIBBLE:
628 ppb_MS_GET_init(ppbus, vpo->vpo_dev, vpo->vpo_nibble_inbyte_msq);
629 ppb_MS_PUT_init(ppbus, vpo->vpo_dev, spp_outbyte_submicroseq);
630 device_printf(vpo->vpo_dev, "NIBBLE mode\n");
633 panic("imm: unknown mode %d", vpo->vpo_mode_found);
636 ppb_release_bus(ppbus, vpo->vpo_dev);
647 imm_reset_bus(struct vpoio_data *vpo)
649 device_t ppbus = device_get_parent(vpo->vpo_dev);
652 /* first, connect to the drive and request the bus */
653 imm_connect(vpo, PPB_WAIT|PPB_INTR, &disconnected, 1);
657 /* reset the SCSI bus */
658 ppb_MS_microseq(ppbus, vpo->vpo_dev, reset_microseq, NULL);
660 /* then disconnect */
661 imm_disconnect(vpo, NULL, 1);
670 * Send an SCSI command
674 imm_do_scsi(struct vpoio_data *vpo, int host, int target, char *command,
675 int clen, char *buffer, int blen, int *result, int *count,
678 device_t ppbus = device_get_parent(vpo->vpo_dev);
681 int len, error = 0, not_connected = 0;
686 * enter disk state, allocate the ppbus
689 * Should we allow this call to be interruptible?
690 * The only way to report the interruption is to return
691 * EIO to upper SCSI code :^(
693 if ((error = imm_connect(vpo, PPB_WAIT|PPB_INTR, ¬_connected, 1)))
702 * Select the drive ...
704 if ((*ret = imm_select(vpo,host,target)))
708 * Send the command ...
710 for (k = 0; k < clen; k+=2) {
711 if (imm_wait(vpo, VP0_FAST_SPINTMO) != (char)0xa8) {
712 *ret = VP0_ECMD_TIMEOUT;
715 if (imm_outstr(vpo, &command[k], 2)) {
716 *ret = VP0_EPPDATA_TIMEOUT;
721 if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) {
722 *ret = VP0_ESTATUS_TIMEOUT;
726 if ((r & 0x30) == 0x10) {
727 if (imm_negociate(vpo)) {
728 *ret = VP0_ENEGOCIATE;
735 * Complete transfer ...
740 if (!(r = imm_wait(vpo, VP0_LOW_SPINTMO))) {
741 *ret = VP0_ESTATUS_TIMEOUT;
745 /* stop when the ZIP+ wants to send status */
749 if (*count >= blen) {
750 *ret = VP0_EDATA_OVERFLOW;
754 /* ZIP+ wants to send data? */
755 if (r == (char)0x88) {
756 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
759 error = imm_outstr(vpo, &buffer[*count], len);
761 if (!PPB_IN_EPP_MODE(ppbus))
764 len = (((blen - *count) >= VP0_SECTOR_SIZE)) ?
767 error = imm_instr(vpo, &buffer[*count], len);
778 if ((PPB_IN_NIBBLE_MODE(ppbus) ||
779 PPB_IN_PS2_MODE(ppbus)) && negociated)
780 ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL);
783 * Retrieve status ...
785 if (imm_negociate(vpo)) {
786 *ret = VP0_ENEGOCIATE;
791 if (imm_instr(vpo, &l, 1)) {
796 /* check if the ZIP+ wants to send more status */
797 if (imm_wait(vpo, VP0_FAST_SPINTMO) == (char)0xb8)
798 if (imm_instr(vpo, &h, 1)) {
799 *ret = VP0_EOTHER + 2;
803 /* Experience showed that we should discard this */
807 *result = ((int) h << 8) | ((int) l & 0xff);
810 if ((PPB_IN_NIBBLE_MODE(ppbus) ||
811 PPB_IN_PS2_MODE(ppbus)) && negociated)
812 ppb_MS_microseq(ppbus, vpo->vpo_dev, transfer_epilog, NULL);
814 /* return to printer state, release the ppbus */
815 imm_disconnect(vpo, NULL, 1);