In the DZ interface as implemented by the DC7085 gate array the serial
transmitters are double buffered, meaning that at the time a transmitter
is ready to accept the next character there is one in the transmit shift
register still being sent to the line. Issuing a master clear at this
time causes this character to be lost, so wait an extra amount of time
sufficient for the transmit shift register to drain at 9600bps, which is
the baud rate setting used by the firmware console.
Mind the specified 1.4us TRDY recovery time in the course and continue
using iob() as the completion barrier, since the platforms involved use
a write buffer that can delay and combine writes, and reorder them with
respect to reads regardless of the MMIO locations accessed and we still
lack a platform-independent handler for that.
When called from dz_serial_console_init() this is too early for fsleep()
to work and even before lpj has been calculated and therefore the delay
is actually not sufficient for the transmitter to drain and is merely a
placeholder now. This will be addressed in a follow-up change.
Fixes: e6ee512f5a77 ("dz.c: Resource management")
Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
Cc: stable@vger.kernel.org # v2.6.25+
Link: https://patch.msgid.link/alpine.DEB.2.21.2605062259080.46195@angie.orcam.me.uk
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
static void dz_reset(struct dz_port *dport)
{
struct dz_mux *mux = dport->mux;
+ unsigned short tcr;
+ int loops = 10000;
if (mux->initialised)
return;
+ tcr = dz_in(dport, DZ_TCR);
+
+ /* Do not disturb any ongoing transmissions. */
+ if (dz_in(dport, DZ_CSR) & DZ_MSE) {
+ unsigned short csr, mask;
+
+ mask = tcr;
+ while ((mask & DZ_LNENB) && loops--) {
+ csr = dz_in(dport, DZ_CSR);
+ if (!(csr & DZ_TRDY))
+ continue;
+ mask &= ~(1 << ((csr & DZ_TLINE) >> 8));
+ dz_out(dport, DZ_TCR, mask);
+ iob();
+ udelay(2); /* 1.4us TRDY recovery. */
+ }
+ udelay(1200); /* Transmitter drain. */
+ }
+
dz_out(dport, DZ_CSR, DZ_CLR);
while (dz_in(dport, DZ_CSR) & DZ_CLR);
iob();