#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <dev/ic/ahcireg.h>
#include <dev/ic/ahcivar.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_clock.h>
#include <dev/ofw/ofw_misc.h>
#include <dev/ofw/fdt.h>
#define SATA_CAP 0x000
#define SATA_GHC 0x004
#define SATA_IS 0x008
#define SATA_PI 0x00C
#define SATA_VS 0x010
#define SATA_CCC_CTL 0x014
#define SATA_CCC_PORTS 0x018
#define SATA_CAP2 0x024
#define SATA_BISTAFR 0x0A0
#define SATA_BISTCR 0x0A4
#define SATA_BISTFCTR 0x0A8
#define SATA_BSTSR 0x0AC
#define SATA_OOBR 0x0BC
#define SATA_GPCR 0x0D0
#define SATA_GPSR 0x0D4
#define SATA_TIMER1MS 0x0E0
#define SATA_TESTR 0x0F4
#define SATA_VERSIONR 0x0F8
#define SATA_P0CLB 0x100
#define SATA_P0FB 0x108
#define SATA_P0IS 0x110
#define SATA_P0IE 0x114
#define SATA_P0CMD 0x118
#define SATA_P0TFD 0x120
#define SATA_P0SIG 0x124
#define SATA_P0SSTS 0x128
#define SATA_P0SCTL 0x12C
#define SATA_P0SERR 0x130
#define SATA_P0SACT 0x134
#define SATA_P0CI 0x138
#define SATA_P0SNTF 0x13C
#define SATA_P0DMACR 0x170
#define SATA_P0PHYCR 0x178
#define SATA_P0PHYSR 0x17C
#define SATA_CAP_SSS (1 << 27)
#define SATA_GHC_HR (1 << 0)
#define SATA_P0PHYCR_TEST_PDDQ (1 << 20)
#define IOMUXC_GPR13 0x034
#define IOMUXC_GPR13_SATA_PHY_1_TX_EDGE_RATE (1 << 0)
#define IOMUXC_GPR13_SATA_PHY_1_MPLL_CLK_EN (1 << 1)
#define IOMUXC_GPR13_SATA_PHY_2_1104V (0x11 << 2)
#define IOMUXC_GPR13_SATA_PHY_3_333DB (0x00 << 7)
#define IOMUXC_GPR13_SATA_PHY_4_9_16 (0x04 << 11)
#define IOMUXC_GPR13_SATA_PHY_5_SS (0x01 << 14)
#define IOMUXC_GPR13_SATA_SPEED_3G (0x01 << 15)
#define IOMUXC_GPR13_SATA_PHY_6 (0x03 << 16)
#define IOMUXC_GPR13_SATA_PHY_7_SATA2M (0x12 << 19)
#define IOMUXC_GPR13_SATA_PHY_8_30DB (0x05 << 24)
#define IOMUXC_GPR13_SATA_MASK 0x07FFFFFF
int imxahci_match(struct device *, void *, void *);
void imxahci_attach(struct device *, struct device *, void *);
int imxahci_detach(struct device *, int);
int imxahci_activate(struct device *, int);
extern int ahci_intr(void *);
struct imxahci_softc {
struct ahci_softc sc;
};
const struct cfattach imxahci_ca = {
sizeof(struct imxahci_softc),
imxahci_match,
imxahci_attach,
imxahci_detach,
imxahci_activate
};
struct cfdriver imxahci_cd = {
NULL, "imxahci", DV_DULL
};
int
imxahci_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return OF_is_compatible(faa->fa_node, "fsl,imx6q-ahci");
}
void
imxahci_attach(struct device *parent, struct device *self, void *aux)
{
struct imxahci_softc *imxsc = (struct imxahci_softc *) self;
struct ahci_softc *sc = &imxsc->sc;
struct fdt_attach_args *faa = aux;
uint32_t timeout = 0x100000;
struct regmap *rm;
uint32_t reg;
if (faa->fa_nreg < 1)
return;
sc->sc_iot = faa->fa_iot;
sc->sc_ios = faa->fa_reg[0].size;
sc->sc_dmat = faa->fa_dmat;
if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
faa->fa_reg[0].size, 0, &sc->sc_ioh))
panic("imxahci_attach: bus_space_map failed!");
sc->sc_ih = arm_intr_establish_fdt(faa->fa_node, IPL_BIO,
ahci_intr, sc, sc->sc_dev.dv_xname);
if (sc->sc_ih == NULL) {
printf(": unable to establish interrupt\n");
goto unmap;
}
clock_enable(faa->fa_node, "sata_ref");
clock_enable(faa->fa_node, "sata");
delay(100);
rm = regmap_bycompatible("fsl,imx6q-iomuxc-gpr");
if (rm != NULL) {
reg = regmap_read_4(rm, IOMUXC_GPR13);
reg &= ~IOMUXC_GPR13_SATA_MASK;
reg |= IOMUXC_GPR13_SATA_PHY_2_1104V |
IOMUXC_GPR13_SATA_PHY_3_333DB |
IOMUXC_GPR13_SATA_PHY_4_9_16 |
IOMUXC_GPR13_SATA_SPEED_3G |
IOMUXC_GPR13_SATA_PHY_6 |
IOMUXC_GPR13_SATA_PHY_7_SATA2M |
IOMUXC_GPR13_SATA_PHY_8_30DB;
regmap_write_4(rm, IOMUXC_GPR13, reg);
reg = regmap_read_4(rm, IOMUXC_GPR13);
reg |= IOMUXC_GPR13_SATA_PHY_1_MPLL_CLK_EN;
regmap_write_4(rm, IOMUXC_GPR13, reg);
}
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_P0PHYCR,
bus_space_read_4(sc->sc_iot, sc->sc_ioh, SATA_P0PHYCR) & ~SATA_P0PHYCR_TEST_PDDQ);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_GHC, SATA_GHC_HR);
while (!bus_space_read_4(sc->sc_iot, sc->sc_ioh, SATA_VERSIONR));
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_CAP,
bus_space_read_4(sc->sc_iot, sc->sc_ioh, SATA_CAP) | SATA_CAP_SSS);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_PI, 1);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_TIMER1MS,
clock_get_frequency(faa->fa_node, "ahb"));
while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SATA_P0SSTS) & 0xF) && timeout--);
printf(":");
if (ahci_attach(sc) != 0) {
goto irq;
}
return;
irq:
arm_intr_disestablish(sc->sc_ih);
unmap:
bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
}
int
imxahci_detach(struct device *self, int flags)
{
struct imxahci_softc *imxsc = (struct imxahci_softc *) self;
struct ahci_softc *sc = &imxsc->sc;
ahci_detach(sc, flags);
bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
return 0;
}
int
imxahci_activate(struct device *self, int act)
{
struct imxahci_softc *imxsc = (struct imxahci_softc *) self;
struct ahci_softc *sc = &imxsc->sc;
return ahci_activate((struct device *)sc, act);
}