summaryrefslogtreecommitdiff
path: root/sys/dev/ic/ncr5380.doc
blob: 18ce9bcd442dd9f0e8bb19b59a6178163b9977fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
MI 5380 driver
==============

(What?  Documentation?  Is this guy nuts? :-)

Reselection
-----------

This driver will permit reselection on non-polled commands if
sc->sc_flags & NCR5380_PERMIT_RESELECT is 1.  This permits enabling of
reselection on a per-device basis.

Disconnect/reselect is never permitted for polled commands.



Interfacing the driver to MD code
---------------------------------

/sys/dev/ic/ncr5380.c is now stand-alone.  DON'T include it after your
MD stuff!

This allows for more than one 5380-based SCSI board in your system.  This is
a real possibility for Amiga generic kernels.

Your driver's softc structure must have an instance of struct ncr5380_softc
as the first thing in the structure.  The MD code must initialize the
following:

sci_*: pointers to the 5380 registers.  All accesses are done through
  these pointers.  This indirection allows the driver to work with
  boards that map the 5380 on even addresses only or do other
  weirdnesses.

int (*sc_pio_out)(sc, phase, datalen, data)
int (*sc_pio_in)(sc, phase, datalen, data)
  These point to functions that do programmed I/O transfers to the bus and
  from the bus, respectively.  Arguments:

  sc            points to the softc
  phase         the current SCSI bus phase
  datalen       length of data to transfer
  data          pointer to the buffer

  Both functions must return the number of bytes successfully transferred.
  A transfer operation must be aborted if the target requests a different
  phase before the transfer completes.

  If you have no special requirements, you can point these to
  ncr5380_pio_out() and ncr5380_pio_in() respectively.  If your board
  can do pseudo-DMA, then you might want to point these to functions
  that use this feature.

void (*sc_dma_alloc)(sc)
  This function is called to set up a DMA transfer.  You must create and
  return a "DMA handle" in sc->sc_dma_hand which identifies the DMA transfer.
  The driver will pass you your DMA handle in sc->sc_dma_hand for future
  operations.  The contents of the DMA handle are immaterial to the MI
  code - the DMA handle is for your bookkeeping only.  Usually, you
  create a structure and point to it here.

  For example, you can record the mapped and unmapped addresses of the
  buffer.  The Sun driver places an Am9516 UDC control block in the DMA
  handle.

  If for some reason you decide not to do DMA for the transfer, make
  sc->sc_dma_hand NULL.  This might happen if the proposed transfer is
  misaligned, or in the wrong type of memory, or...

void (*sc_dma_start)(sc)
  This function starts the transfer.

void (*sc_dma_stop)(sc)
  This function stops a transfer.  sc->sc_datalen and sc->sc_dataptr must
  be updated to reflect the portion of the DMA already done.

void (*sc_dma_eop)(sc)
  This function is called when the 5380 signals EOP.  Either continue
  the DMA or stop the DMA.

void (*sc_dma_free)(sc)
  This function frees the current DMA handle.

u_char *sc_dataptr;
int sc_datalen;
  These variables form the active SCSI data pointer.  DMA code must start
  DMA at the location given, and update the pointer/length in response to
  DMA operations.

u_short sc_dma_flags;
  See ncr5380var.h



Writing your DMA code
---------------------

DMA on a system with protected or virtual memory is always a problem.  Even
though a disk transfer may be logically contiguous, the physical pages backing
the transfer may not be.  There are two common solutions to this problem:

DMA chains: the DMA is broken up into a list of contiguous segments.  The first
segment is submitted to the DMA controller, and when it completes, the second
segment is submitted, without stopping the 5380.  This is what the sc_dma_eop()
function can do efficiently - if you have a DMA chain, it can quickly load up
the next link in the chain.  The sc_dma_alloc() function builds the chain and
sc_dma_free() releases any resources you used to build it.

DVMA: Direct Virtual Memory Access.  In this scheme, DMA requests go through
the MMU.  Although you can't page fault, you can program the MMU to remap
things so the DMA controller sees contiguous data.  In this mode, sc_dma_alloc()
is used to map the transfer into the address space reserved for DVMA and
sc_dma_free() is used to unmap it.


Interrupts
----------

ncr5380_sbc_intr() must be called when the 5380 interrupts the host.

You must write an interrupt routine pretty much from scratch to check for
things generated by MD hardware.


Known problems
--------------

I'm getting this out now so that other ports can hack on it and integrate it.

The sun3, DMA/Interrupt appears to be working now, but needs testing.

Polled commands submitted while non-polled commands are in progress are not
handled correctly.  This can happen if reselection is enabled and a new disk
is mounted while an I/O is in progress on another disk. 

The problem is: what to do if you get reselected while doing the selection
for the polled command?  Currently, the driver busy waits for the non-polled
command to complete, but this is bogus.  I need to complete the non-polled
command in polled mode, then do the polled command.


Timeouts in the driver are EXTREMELY sensitive to the characteristics of the
local implementation of delay().  The Sun3 version delays for a minimum of 5us.
However, the driver must assume that delay(1) will delay only 1us.  For this
reason, performance on the Sun3 sucks in some places.