File: [local] / sys / arch / hp300 / dev / spc.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:05:42 2008 UTC (16 years, 4 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD Changes since 1.1: +0 -0 lines
Import of OpenBSD 4.2 release kernel tree with initial code to support
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO
Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)
|
/* $OpenBSD: spc.c,v 1.12 2005/11/14 21:51:55 miod Exp $ */
/* $NetBSD: spc.c,v 1.2 2003/11/17 14:37:59 tsutsui Exp $ */
/*
* Copyright (c) 2003 Izumi Tsutsui.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/buf.h>
#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/intr.h>
#include <hp300/dev/dioreg.h>
#include <hp300/dev/diovar.h>
#include <hp300/dev/diodevs.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_message.h>
#include <scsi/scsiconf.h>
#include <hp300/dev/mb89352reg.h>
#include <hp300/dev/mb89352var.h>
#include <hp300/dev/hp98265reg.h>
#include <hp300/dev/dmareg.h>
#include <hp300/dev/dmavar.h>
#ifdef USELEDS
#include <hp300/hp300/leds.h>
#endif
int spc_dio_match(struct device *, void *, void *);
void spc_dio_attach(struct device *, struct device *, void *);
int spc_dio_dmastart(struct spc_softc *, void *, size_t, int);
int spc_dio_dmadone(struct spc_softc *);
void spc_dio_dmago(void *);
void spc_dio_dmastop(void *);
int spc_dio_intr(void *);
void spc_dio_reset(struct spc_softc *);
#define HPSPC_ADDRESS(o) (dsc->sc_dregs + ((o) << 1) + 1)
#define hpspc_read(o) *(volatile u_int8_t *)(HPSPC_ADDRESS(o))
#define hpspc_write(o, v) *(volatile u_int8_t *)(HPSPC_ADDRESS(o)) = (v)
struct spc_dio_softc {
struct spc_softc sc_spc; /* MI spc softc */
struct isr sc_isr;
volatile u_int8_t *sc_dregs; /* Complete registers */
struct dmaqueue sc_dq; /* DMA job queue */
u_int sc_dflags; /* DMA flags */
#define SCSI_DMA32 0x01 /* 32-bit DMA should be used */
#define SCSI_HAVEDMA 0x02 /* controller has DMA channel */
#define SCSI_DATAIN 0x04 /* DMA direction */
};
struct cfattach spc_ca = {
sizeof(struct spc_dio_softc), spc_dio_match, spc_dio_attach
};
struct cfdriver spc_cd = {
NULL, "spc", DV_DULL
};
/* cf_flags */
#define SPC_NODMA 0x01
int
spc_dio_match(struct device *parent, void *vcf, void *aux)
{
struct dio_attach_args *da = aux;
switch (da->da_id) {
case DIO_DEVICE_ID_SCSI0:
case DIO_DEVICE_ID_SCSI1:
case DIO_DEVICE_ID_SCSI2:
case DIO_DEVICE_ID_SCSI3:
return 1;
}
return 0;
}
void
spc_dio_attach(struct device *parent, struct device *self, void *aux)
{
struct spc_dio_softc *dsc = (struct spc_dio_softc *)self;
struct spc_softc *sc = &dsc->sc_spc;
struct dio_attach_args *da = aux;
int ipl;
u_int8_t id, hconf;
dsc->sc_dregs = (u_int8_t *)iomap(dio_scodetopa(da->da_scode),
da->da_size);
if (dsc->sc_dregs == NULL) {
printf(": can't map SCSI registers\n");
return;
}
sc->sc_regs = dsc->sc_dregs + SPC_OFFSET;
ipl = DIO_IPL(sc->sc_regs);
printf(" ipl %d: 98265A SCSI", ipl);
hpspc_write(HPSCSI_ID, 0xff);
DELAY(100);
id = hpspc_read(HPSCSI_ID);
hconf = hpspc_read(HPSCSI_HCONF);
if ((id & ID_WORD_DMA) == 0) {
printf(", 32-bit DMA");
dsc->sc_dflags |= SCSI_DMA32;
}
if ((hconf & HCONF_PARITY) == 0)
printf(", no parity");
id &= ID_MASK;
printf(", SCSI ID %d\n", id);
if ((hconf & HCONF_PARITY) != 0)
sc->sc_ctlflags = SCTL_PARITY_ENAB;
sc->sc_initiator = id;
if ((sc->sc_dev.dv_cfdata->cf_flags & SPC_NODMA) == 0) {
sc->sc_dma_start = spc_dio_dmastart;
sc->sc_dma_done = spc_dio_dmadone;
}
sc->sc_reset = spc_dio_reset;
dsc->sc_dq.dq_softc = dsc;
dsc->sc_dq.dq_start = spc_dio_dmago;
dsc->sc_dq.dq_done = spc_dio_dmastop;
hpspc_write(HPSCSI_CSR, 0x00);
hpspc_write(HPSCSI_HCONF, 0x00);
dsc->sc_isr.isr_func = spc_dio_intr;
dsc->sc_isr.isr_arg = dsc;
dsc->sc_isr.isr_ipl = ipl;
dsc->sc_isr.isr_priority = IPL_BIO;
dio_intr_establish(&dsc->sc_isr, self->dv_xname);
spc_attach(sc);
/* Enable SPC interrupts. */
hpspc_write(HPSCSI_CSR, CSR_IE);
}
int
spc_dio_dmastart(struct spc_softc *sc, void *addr, size_t size, int datain)
{
struct spc_dio_softc *dsc = (struct spc_dio_softc *)sc;
/*
* The HP98658 hardware cannot do odd length transfers, the
* last byte of data will always be 0x00.
*/
if ((size & 1) != 0)
return (EINVAL);
dsc->sc_dq.dq_chan = DMA0 | DMA1;
dsc->sc_dflags |= SCSI_HAVEDMA;
if (datain)
dsc->sc_dflags |= SCSI_DATAIN;
else
dsc->sc_dflags &= ~SCSI_DATAIN;
if (dmareq(&dsc->sc_dq) != 0)
/* DMA channel is available, so start DMA immediately */
spc_dio_dmago((void *)dsc);
/* else dma start function will be called later from dmafree(). */
return (0);
}
void
spc_dio_dmago(void *arg)
{
struct spc_dio_softc *dsc = (struct spc_dio_softc *)arg;
struct spc_softc *sc = &dsc->sc_spc;
int len, chan;
u_int32_t dmaflags;
u_int8_t cmd;
hpspc_write(HPSCSI_HCONF, 0);
cmd = CSR_IE;
dmaflags = DMAGO_NOINT;
chan = dsc->sc_dq.dq_chan;
if ((dsc->sc_dflags & SCSI_DATAIN) != 0) {
cmd |= CSR_DMAIN;
dmaflags |= DMAGO_READ;
}
if ((dsc->sc_dflags & SCSI_DMA32) != 0 &&
((u_int)sc->sc_dp & 3) == 0 &&
(sc->sc_dleft & 3) == 0) {
cmd |= CSR_DMA32;
dmaflags |= DMAGO_LWORD;
} else {
dmaflags |= DMAGO_WORD;
}
sc->sc_flags |= SPC_DOINGDMA;
dmago(chan, sc->sc_dp, sc->sc_dleft, dmaflags);
hpspc_write(HPSCSI_CSR, cmd);
cmd |= (chan == 0) ? CSR_DE0 : CSR_DE1;
hpspc_write(HPSCSI_CSR, cmd);
cmd = SCMD_XFR;
len = sc->sc_dleft;
if ((len & (DEV_BSIZE -1)) != 0) {
cmd |= SCMD_PAD;
#if 0
/*
* XXX - If we don't do this, the last 2 or 4 bytes
* (depending on word/lword DMA) of a read get trashed.
* It looks like it is necessary for the DMA to complete
* before the SPC goes into "pad mode"??? Note: if we
* also do this on a write, the request never completes.
*/
if ((dsc->sc_dflags & SCSI_DATAIN) != 0)
len += 2;
#endif
}
spc_write(TCH, len >> 16);
spc_write(TCM, len >> 8);
spc_write(TCL, len);
spc_write(PCTL, sc->sc_phase | PCTL_BFINT_ENAB);
spc_write(SCMD, cmd);
}
int
spc_dio_dmadone(struct spc_softc *sc)
{
struct spc_dio_softc *dsc = (struct spc_dio_softc *)sc;
int resid, trans;
u_int8_t cmd;
/* Check if the DMA operation is finished. */
if ((spc_read(SSTS) & SSTS_BUSY) != 0)
return (0);
sc->sc_flags &= ~SPC_DOINGDMA;
if ((dsc->sc_dflags & SCSI_HAVEDMA) != 0) {
dsc->sc_dflags &= ~SCSI_HAVEDMA;
dmafree(&dsc->sc_dq);
}
cmd = hpspc_read(HPSCSI_CSR);
cmd &= ~(CSR_DE1 | CSR_DE0);
hpspc_write(HPSCSI_CSR, cmd);
resid = spc_read(TCH) << 16 |
spc_read(TCM) << 8 |
spc_read(TCL);
trans = sc->sc_dleft - resid;
sc->sc_dp += trans;
sc->sc_dleft -= trans;
return (1);
}
void
spc_dio_dmastop(void *arg)
{
struct spc_dio_softc *dsc = (struct spc_dio_softc *)arg;
struct spc_softc *sc = &dsc->sc_spc;
u_int8_t cmd;
cmd = hpspc_read(HPSCSI_CSR);
cmd &= ~(CSR_DE1 | CSR_DE0);
hpspc_write(HPSCSI_CSR, cmd);
dsc->sc_dflags &= ~SCSI_HAVEDMA;
sc->sc_flags &= ~SPC_DOINGDMA;
}
int
spc_dio_intr(void *arg)
{
struct spc_dio_softc *dsc = (struct spc_dio_softc *)arg;
/* if we are sharing the ipl level, this interrupt may not be for us. */
if ((hpspc_read(HPSCSI_CSR) & (CSR_IE | CSR_IR)) != (CSR_IE | CSR_IR))
return (0);
#ifdef USELEDS
ledcontrol(0, 0, LED_DISK);
#endif
return (spc_intr(arg));
}
void
spc_dio_reset(struct spc_softc *sc)
{
struct spc_dio_softc *dsc = (struct spc_dio_softc *)sc;
spc_reset(sc);
hpspc_write(HPSCSI_HCONF, 0x00);
}