NetBSD/sys/dev/pci/mvsata_pci.c

517 lines
16 KiB
C

/* $NetBSD: mvsata_pci.c,v 1.8 2014/03/29 19:28:25 christos Exp $ */
/*
* Copyright (c) 2008 KIYOHARA Takashi
* 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.
*
* 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/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mvsata_pci.c,v 1.8 2014/03/29 19:28:25 christos Exp $");
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/device.h>
#include <sys/errno.h>
#include <sys/pmf.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/pciidereg.h>
#include <dev/pci/pciidevar.h>
#include <dev/ic/mvsatareg.h>
#include <dev/ic/mvsatavar.h>
#define MVSATA_PCI_HCARBITER_SPACE_OFFSET 0x20000
#define MVSATA_PCI_COMMAND 0x00c00
#define MVSATA_PCI_COMMAND_MWRITECOMBINE (1 << 4)
#define MVSATA_PCI_COMMAND_MREADCOMBINE (1 << 5)
#define MVSATA_PCI_SERRMASK 0x00c28
#define MVSATA_PCI_MSITRIGGER 0x00c38
#define MVSATA_PCI_MODE 0x00d00
#define MVSATA_PCI_DISCTIMER 0x00d04
#define MVSATA_PCI_EROMBAR 0x00d2c
#define MVSATA_PCI_MAINCS 0x00d30
#define MVSATA_PCI_MAINCS_SPM (1 << 2) /* stop pci master */
#define MVSATA_PCI_MAINCS_PME (1 << 3) /* pci master empty */
#define MVSATA_PCI_MAINCS_GSR (1 << 4) /* glab soft reset */
#define MVSATA_PCI_E_IRQCAUSE 0x01900
#define MVSATA_PCI_E_IRQMASK 0x01910
#define MVSATA_PCI_XBARTIMEOUT 0x01d04
#define MVSATA_PCI_ERRLOWADDR 0x01d40
#define MVSATA_PCI_ERRHIGHADDR 0x01d44
#define MVSATA_PCI_ERRATTRIBUTE 0x01d48
#define MVSATA_PCI_ERRCOMMAND 0x01d50
#define MVSATA_PCI_IRQCAUSE 0x01d58
#define MVSATA_PCI_IRQMASK 0x01d5c
#define MVSATA_PCI_MAINIRQCAUSE 0x01d60
#define MVSATA_PCI_MAINIRQMASK 0x01d64
#define MVSATA_PCI_MAINIRQ_SATAERR(hc, port) \
(1 << (((port) << 1) + (hc) * 9))
#define MVSATA_PCI_MAINIRQ_SATADONE(hc, port) \
(1 << (((port) << 1) + (hc) * 9 + 1))
#define MVSATA_PCI_MAINIRQ_SATACOALDONE(hc) (1 << ((hc) * 9 + 8))
#define MVSATA_PCI_MAINIRQ_PCI (1 << 18)
#define MVSATA_PCI_FLASHCTL 0x1046c
#define MVSATA_PCI_GPIOPORTCTL 0x104f0
#define MVSATA_PCI_RESETCFG 0x180d8
#define MVSATA_PCI_DEV(psc) (psc->psc_sc.sc_wdcdev.sc_atac.atac_dev)
struct mvsata_pci_softc {
struct mvsata_softc psc_sc;
pci_chipset_tag_t psc_pc;
pcitag_t psc_tag;
bus_space_tag_t psc_iot;
bus_space_handle_t psc_ioh;
void *psc_ih;
};
static int mvsata_pci_match(device_t, struct cfdata *, void *);
static void mvsata_pci_attach(device_t, device_t, void *);
static int mvsata_pci_detach(device_t, int);
static int mvsata_pci_intr(void *);
static bool mvsata_pci_resume(device_t, const pmf_qual_t *qual);
static int mvsata_pci_sreset(struct mvsata_softc *);
static int mvsata_pci_misc_reset(struct mvsata_softc *);
static void mvsata_pci_enable_intr(struct mvsata_port *, int);
CFATTACH_DECL_NEW(mvsata_pci, sizeof(struct mvsata_pci_softc),
mvsata_pci_match, mvsata_pci_attach, mvsata_pci_detach, NULL);
struct mvsata_product mvsata_pci_products[] = {
#define PCI_VP(v, p) PCI_VENDOR_ ## v, PCI_PRODUCT_ ## v ## _ ## p
{ PCI_VP(MARVELL, 88SX5040), 1, 4, gen1, 0 },
{ PCI_VP(MARVELL, 88SX5041), 1, 4, gen1, 0 },
{ PCI_VP(MARVELL, 88SX5080), 2, 4, gen1, 0 },
{ PCI_VP(MARVELL, 88SX5081), 2, 4, gen1, 0 },
{ PCI_VP(MARVELL, 88SX6040), 1, 4, gen2, 0 },
{ PCI_VP(MARVELL, 88SX6041), 1, 4, gen2, 0 },
{ PCI_VP(ADP2, 1420SA), 1, 4, gen2, 0 }, /* 88SX6041 */
{ PCI_VP(MARVELL, 88SX6042), 1, 4, gen2e, 0 },
{ PCI_VP(MARVELL, 88SX6080), 2, 4, gen2, MVSATA_FLAGS_PCIE },
{ PCI_VP(MARVELL, 88SX6081), 2, 4, gen2, MVSATA_FLAGS_PCIE },
{ PCI_VP(MARVELL, 88SX7042), 1, 4, gen2e, 0 },
{ PCI_VP(ADP2, 1430SA), 1, 4, gen2e, 0 }, /* 88SX7042 */
{ PCI_VP(TRIONES, ROCKETRAID_2310), 1, 4, gen2e, 0 },
#undef PCI_VP
};
/*
* mvsata_pci_match()
* This function returns 2, because mvsata is high priority more than pciide.
*/
static int
mvsata_pci_match(device_t parent, struct cfdata *match, void *aux)
{
struct pci_attach_args *pa = aux;
int i;
for (i = 0; i < __arraycount(mvsata_pci_products); i++)
if (PCI_VENDOR(pa->pa_id) == mvsata_pci_products[i].vendor &&
PCI_PRODUCT(pa->pa_id) == mvsata_pci_products[i].model)
return 2;
return 0;
}
static void
mvsata_pci_attach(device_t parent, device_t self, void *aux)
{
struct pci_attach_args *pa = aux;
struct mvsata_pci_softc *psc = device_private(self);
struct mvsata_softc *sc = &psc->psc_sc;
pci_intr_handle_t intrhandle;
pcireg_t csr;
bus_size_t size;
uint32_t reg, mask;
int read_pre_amps, hc, port, rv, i;
const char *intrstr;
char intrbuf[PCI_INTRSTR_LEN];
sc->sc_wdcdev.sc_atac.atac_dev = self;
sc->sc_model = PCI_PRODUCT(pa->pa_id);
sc->sc_rev = PCI_REVISION(pa->pa_class);
sc->sc_dmat = pa->pa_dmat;
sc->sc_enable_intr = mvsata_pci_enable_intr;
pci_aprint_devinfo(pa, "Marvell Serial-ATA Host Controller");
/* Map I/O register */
if (pci_mapreg_map(pa, PCI_MAPREG_START,
PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 0,
&psc->psc_iot, &psc->psc_ioh, NULL, &size) != 0) {
aprint_error_dev(self, "can't map registers\n");
return;
}
psc->psc_pc = pa->pa_pc;
psc->psc_tag = pa->pa_tag;
if (bus_space_subregion(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_HCARBITER_SPACE_OFFSET,
size - MVSATA_PCI_HCARBITER_SPACE_OFFSET, &sc->sc_ioh)) {
aprint_error_dev(self, "can't subregion registers\n");
return;
}
sc->sc_iot = psc->psc_iot;
/* Enable device */
csr = pci_conf_read(psc->psc_pc, psc->psc_tag, PCI_COMMAND_STATUS_REG);
csr |= PCI_COMMAND_MASTER_ENABLE;
pci_conf_write(psc->psc_pc, psc->psc_tag, PCI_COMMAND_STATUS_REG, csr);
if (pci_intr_map(pa, &intrhandle) != 0) {
aprint_error_dev(self, "couldn't map interrupt\n");
return;
}
intrstr = pci_intr_string(psc->psc_pc, intrhandle, intrbuf, sizeof(intrbuf));
psc->psc_ih = pci_intr_establish(psc->psc_pc, intrhandle, IPL_BIO,
mvsata_pci_intr, sc);
if (psc->psc_ih == NULL) {
aprint_error_dev(self, "couldn't establish interrupt\n");
return;
}
aprint_normal_dev(self, "interrupting at %s\n",
intrstr ? intrstr : "unknown interrupt");
/*
* Check if TWSI serial ROM initialization was triggered.
* If so, then PRE/AMP configuration probably are set after
* reset by serial ROM. If not then override the PRE/AMP
* values.
*/
reg = bus_space_read_4(psc->psc_iot, psc->psc_ioh, MVSATA_PCI_RESETCFG);
read_pre_amps = (reg & 0x00000001) ? 1 : 0;
for (i = 0; i < __arraycount(mvsata_pci_products); i++)
if (PCI_VENDOR(pa->pa_id) == mvsata_pci_products[i].vendor &&
PCI_PRODUCT(pa->pa_id) == mvsata_pci_products[i].model)
break;
KASSERT(i < __arraycount(mvsata_pci_products));
rv = mvsata_attach(sc, &mvsata_pci_products[i],
mvsata_pci_sreset, mvsata_pci_misc_reset, read_pre_amps);
if (rv != 0) {
pci_intr_disestablish(psc->psc_pc, psc->psc_ih);
return;
}
mask = MVSATA_PCI_MAINIRQ_PCI;
for (hc = 0; hc < sc->sc_hc; hc++)
for (port = 0; port < sc->sc_port; port++)
mask |=
MVSATA_PCI_MAINIRQ_SATAERR(hc, port) |
MVSATA_PCI_MAINIRQ_SATADONE(hc, port);
bus_space_write_4(psc->psc_iot, psc->psc_ioh, MVSATA_PCI_MAINIRQMASK,
mask);
if (!pmf_device_register(self, NULL, mvsata_pci_resume))
aprint_error_dev(self, "couldn't establish power handler\n");
}
static int
mvsata_pci_detach(device_t self, int flags)
{
struct mvsata_pci_softc *psc = device_private(self);
/* XXXX: needs reset ? */
pci_intr_disestablish(psc->psc_pc, psc->psc_ih);
pmf_device_deregister(self);
return 0;
}
static int
mvsata_pci_intr(void *arg)
{
struct mvsata_pci_softc *psc = (struct mvsata_pci_softc *)arg;
struct mvsata_softc *sc = &psc->psc_sc;
uint32_t cause;
int hc, port, handled = 0;
cause = bus_space_read_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_MAINIRQCAUSE);
for (hc = 0; hc < sc->sc_hc; hc++)
for (port = 0; port < sc->sc_port; port++)
if (cause & MVSATA_PCI_MAINIRQ_SATAERR(hc, port)) {
struct mvsata_port *mvport;
mvport = sc->sc_hcs[hc].hc_ports[port];
handled |= mvsata_error(mvport);
}
for (hc = 0; hc < sc->sc_hc; hc++)
if (cause &
(MVSATA_PCI_MAINIRQ_SATADONE(hc, 0) |
MVSATA_PCI_MAINIRQ_SATADONE(hc, 1) |
MVSATA_PCI_MAINIRQ_SATADONE(hc, 2) |
MVSATA_PCI_MAINIRQ_SATADONE(hc, 3)))
handled |= mvsata_intr(&sc->sc_hcs[hc]);
if (cause & MVSATA_PCI_MAINIRQ_PCI) {
uint32_t pe_cause;
if (sc->sc_flags & MVSATA_FLAGS_PCIE) {
pe_cause = bus_space_read_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_E_IRQCAUSE);
aprint_error_dev(MVSATA_PCI_DEV(psc),
"PCIe error: 0x%x\n", pe_cause);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_E_IRQCAUSE, ~pe_cause);
} else {
pe_cause = bus_space_read_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_IRQCAUSE);
aprint_error_dev(MVSATA_PCI_DEV(psc),
"PCI error: 0x%x\n", pe_cause);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_IRQCAUSE, ~pe_cause);
}
handled = 1; /* XXXXX */
}
return handled;
}
static bool
mvsata_pci_resume(device_t dev, const pmf_qual_t *qual)
{
/* not yet... */
return true;
}
static int
mvsata_pci_sreset(struct mvsata_softc *sc)
{
struct mvsata_pci_softc *psc = (struct mvsata_pci_softc *)sc;
uint32_t val;
int i;
val = bus_space_read_4(psc->psc_iot, psc->psc_ioh, MVSATA_PCI_MAINCS);
val |= MVSATA_PCI_MAINCS_SPM;
bus_space_write_4(psc->psc_iot, psc->psc_ioh, MVSATA_PCI_MAINCS, val);
for (i = 0; i < 1000; i++) {
delay(1);
val = bus_space_read_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_MAINCS);
if (val & MVSATA_PCI_MAINCS_PME)
break;
}
if (!(val & MVSATA_PCI_MAINCS_PME)) {
aprint_error_dev(MVSATA_PCI_DEV(psc),
"PCI master won't flush\n");
return -1;
}
/* reset */
bus_space_write_4(psc->psc_iot, psc->psc_ioh, MVSATA_PCI_MAINCS,
val | MVSATA_PCI_MAINCS_GSR);
val = bus_space_read_4(psc->psc_iot, psc->psc_ioh, MVSATA_PCI_MAINCS);
delay(5);
if (!(val & MVSATA_PCI_MAINCS_GSR)) {
aprint_error_dev(MVSATA_PCI_DEV(psc),
"can't set global reset\n");
return -1;
}
/* clear reset and *reenable the PCI master* (not mentioned in spec) */
val &= ~(MVSATA_PCI_MAINCS_GSR | MVSATA_PCI_MAINCS_SPM);
bus_space_write_4(psc->psc_iot, psc->psc_ioh, MVSATA_PCI_MAINCS, val);
val = bus_space_read_4(psc->psc_iot, psc->psc_ioh, MVSATA_PCI_MAINCS);
delay(5);
if (val & MVSATA_PCI_MAINCS_GSR) {
aprint_error_dev(MVSATA_PCI_DEV(psc),
"can't set global reset\n");
return -1;
}
return 0;
}
static int
mvsata_pci_misc_reset(struct mvsata_softc *sc)
{
struct mvsata_pci_softc *psc = (struct mvsata_pci_softc *)sc;
#define MVSATA_PCI_COMMAND_DEFAULT 0x0107e371
#define MVSATA_PCI_COMMAND_PCI_CONVENTIONAL_ONLY 0x800003e0
uint32_t val, pci_command = MVSATA_PCI_COMMAND_DEFAULT;
bus_space_write_4(psc->psc_iot, psc->psc_ioh, MVSATA_PCI_FLASHCTL,
0x0fcfffff);
if (sc->sc_gen == gen2 || sc->sc_gen == gen2e) {
val = bus_space_read_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_GPIOPORTCTL);
val &= 0x3;
#if 0
val |= 0x00000060;
#else /* XXXX */
val |= 0x00000070;
#endif
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_GPIOPORTCTL, val);
}
if (sc->sc_gen == gen1) {
/* Expansion ROM BAR Enable */
val = bus_space_read_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_EROMBAR);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_EROMBAR, val | 0x00000001);
}
if (sc->sc_flags & MVSATA_FLAGS_PCIE) {
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_MAINIRQMASK, 0);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_E_IRQCAUSE, 0);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_E_IRQMASK, 0);
} else {
val = bus_space_read_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_MODE);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_MODE, val & 0xff00ffff);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_DISCTIMER, 0);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_MSITRIGGER, 0);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_XBARTIMEOUT, 0x000100ff);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_MAINIRQMASK, 0);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_SERRMASK, 0);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_IRQCAUSE, 0);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_IRQMASK, 0);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_ERRLOWADDR, 0);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_ERRHIGHADDR, 0);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_ERRATTRIBUTE, 0);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_ERRCOMMAND, 0);
}
/* Enable LED */
if (sc->sc_gen == gen1) {
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_GPIOPORTCTL, 0);
/* XXXX: 50xxB2 errata ? */
#if 0
if (sc->sc_rev == 3) {
int port;
val = bus_space_read_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_GPIOPORTCTL);
/* XXXX: check HDD connected */
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_GPIOPORTCTL, val);
}
#endif
/* Disable Flash controller clock */
val = bus_space_read_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_EROMBAR);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_EROMBAR, val & ~0x00000001);
} else
#if 0
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_GPIOPORTCTL, 0x00000060);
#else /* XXXX */
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_GPIOPORTCTL, 0x00000070);
#endif
if (sc->sc_flags & MVSATA_FLAGS_PCIE)
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_E_IRQMASK, 0x0000070a);
else {
val = bus_space_read_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_MODE);
if ((val & 0x30) >> 4) { /* PCI-X */
int mv60x1b2 =
((sc->sc_model == PCI_PRODUCT_MARVELL_88SX6041 ||
sc->sc_model == PCI_PRODUCT_MARVELL_88SX6081) &&
sc->sc_rev == 7);
pci_command &=
~MVSATA_PCI_COMMAND_PCI_CONVENTIONAL_ONLY;
if (sc->sc_gen == gen1 || mv60x1b2)
pci_command &=
~MVSATA_PCI_COMMAND_MWRITECOMBINE;
} else
if (sc->sc_gen == gen1)
pci_command &=
~(MVSATA_PCI_COMMAND_MWRITECOMBINE |
MVSATA_PCI_COMMAND_MREADCOMBINE);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_COMMAND, pci_command);
#define MVSATA_PCI_INTERRUPT_MASK 0x00d77fe6
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_SERRMASK, MVSATA_PCI_INTERRUPT_MASK);
bus_space_write_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_IRQMASK, MVSATA_PCI_INTERRUPT_MASK);
}
return 0;
}
static void
mvsata_pci_enable_intr(struct mvsata_port *mvport, int on)
{
struct mvsata_pci_softc *psc =
device_private(mvport->port_ata_channel.ch_atac->atac_dev);
uint32_t mask;
int hc = mvport->port_hc->hc, port = mvport->port;
mask = bus_space_read_4(psc->psc_iot, psc->psc_ioh,
MVSATA_PCI_MAINIRQMASK);
if (on)
mask |= MVSATA_PCI_MAINIRQ_SATADONE(hc, port);
else
mask &= ~MVSATA_PCI_MAINIRQ_SATADONE(hc, port);
bus_space_write_4(psc->psc_iot, psc->psc_ioh, MVSATA_PCI_MAINIRQMASK,
mask);
}