#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <machine/intr.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdivar.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_clock.h>
#include <dev/ofw/ofw_gpio.h>
#include <dev/ofw/ofw_misc.h>
#include <dev/ofw/fdt.h>
#include <dev/usb/ehcireg.h>
#include <dev/usb/ehcivar.h>
#define USBPHY_CTRL0 0x00
#define USBPHY_TUNE0 0x04
#define HSICPHY_CTRL1 0x10
#define HSICPHY_TUNE1 0x14
#define HSICPHY_CTRL2 0x20
#define HSICPHY_TUNE2 0x24
#define EHCI_CTRL 0x30
#define OHCI_CTRL 0x34
#define USBOTG_SYS 0x38
#define USBOTG_TUNE 0x40
#define CLK_24MHZ 5
#define HOST_CTRL0_PHYSWRSTALL (1U << 31)
#define HOST_CTRL0_COMMONON_N (1 << 9)
#define HOST_CTRL0_SIDDQ (1 << 6)
#define HOST_CTRL0_FORCESLEEP (1 << 5)
#define HOST_CTRL0_FORCESUSPEND (1 << 4)
#define HOST_CTRL0_WORDINTERFACE (1 << 3)
#define HOST_CTRL0_UTMISWRST (1 << 2)
#define HOST_CTRL0_LINKSWRST (1 << 1)
#define HOST_CTRL0_PHYSWRST (1 << 0)
#define HOST_CTRL0_FSEL_MASK (7 << 16)
#define EHCI_CTRL_ENAINCRXALIGN (1 << 29)
#define EHCI_CTRL_ENAINCR4 (1 << 28)
#define EHCI_CTRL_ENAINCR8 (1 << 27)
#define EHCI_CTRL_ENAINCR16 (1 << 26)
#define USB20PHY_CFG 0x230
#define USB20PHY_CFG_HOST_LINK_EN (1 << 0)
#define USB_HOST_POWER_5250 0x708
#define USB_HOST_POWER_54XX 0x70c
#define USB_HOST_POWER_EN (1 << 0)
int exehci_match(struct device *, void *, void *);
void exehci_attach(struct device *, struct device *, void *);
int exehci_detach(struct device *, int);
struct exehci_softc {
struct ehci_softc sc;
void *sc_ih;
int sc_phy;
bus_space_handle_t ph_ioh;
};
const struct cfattach exehci_ca = {
sizeof (struct exehci_softc), exehci_match, exehci_attach,
exehci_detach
};
struct cfdriver exehci_cd = {
NULL, "exehci", DV_DULL
};
void exehci_setup(struct exehci_softc *);
int
exehci_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return OF_is_compatible(faa->fa_node, "samsung,exynos4210-ehci");
}
void
exehci_attach(struct device *parent, struct device *self, void *aux)
{
struct exehci_softc *sc = (struct exehci_softc *)self;
struct fdt_attach_args *faa = aux;
usbd_status r;
char *devname = sc->sc.sc_bus.bdev.dv_xname;
uint32_t phys[2];
uint32_t phy_reg[2];
int node;
if (faa->fa_nreg < 1)
return;
node = OF_child(faa->fa_node);
if (node == 0)
node = faa->fa_node;
if (OF_getpropintarray(node, "phys", phys,
sizeof(phys)) != sizeof(phys))
return;
sc->sc_phy = OF_getnodebyphandle(phys[0]);
if (sc->sc_phy == 0)
return;
if (OF_getpropintarray(sc->sc_phy, "reg", phy_reg,
sizeof(phy_reg)) != sizeof(phy_reg))
return;
sc->sc.iot = faa->fa_iot;
sc->sc.sc_bus.dmatag = faa->fa_dmat;
sc->sc.sc_size = faa->fa_reg[0].size;
if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr,
faa->fa_reg[0].size, 0, &sc->sc.ioh)) {
printf(": cannot map mem space\n");
goto out;
}
if (bus_space_map(sc->sc.iot, phy_reg[0],
phy_reg[1], 0, &sc->ph_ioh)) {
printf(": cannot map mem space\n");
goto mem0;
}
printf("\n");
clock_enable_all(faa->fa_node);
exehci_setup(sc);
sc->sc_ih = arm_intr_establish_fdt(faa->fa_node, IPL_USB,
ehci_intr, &sc->sc, devname);
if (sc->sc_ih == NULL) {
printf(": unable to establish interrupt\n");
goto mem1;
}
strlcpy(sc->sc.sc_vendor, "Exynos 5", sizeof(sc->sc.sc_vendor));
r = ehci_init(&sc->sc);
if (r != USBD_NORMAL_COMPLETION) {
printf("%s: init failed, error=%d\n", devname, r);
goto intr;
}
config_found(self, &sc->sc.sc_bus, usbctlprint);
goto out;
intr:
arm_intr_disestablish(sc->sc_ih);
sc->sc_ih = NULL;
mem1:
bus_space_unmap(sc->sc.iot, sc->ph_ioh, phy_reg[1]);
mem0:
bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
sc->sc.sc_size = 0;
out:
return;
}
int
exehci_detach(struct device *self, int flags)
{
struct exehci_softc *sc = (struct exehci_softc *)self;
int rv;
rv = ehci_detach(self, flags);
if (rv)
return rv;
if (sc->sc_ih != NULL) {
arm_intr_disestablish(sc->sc_ih);
sc->sc_ih = NULL;
}
if (sc->sc.sc_size) {
bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
sc->sc.sc_size = 0;
}
return 0;
}
void
exehci_setup(struct exehci_softc *sc)
{
struct regmap *pmurm, *sysrm;
uint32_t pmureg, sysreg;
bus_size_t offset;
uint32_t val;
int node;
#if 0
exgpio_set_dir(0xa9, EXGPIO_DIR_OUT);
exgpio_set_bit(0xa9);
delay(3000);
#endif
sysreg = OF_getpropint(sc->sc_phy, "samsung,sysreg-phandle", 0);
sysrm = regmap_byphandle(sysreg);
if (sysrm) {
val = regmap_read_4(sysrm, USB20PHY_CFG);
val |= USB20PHY_CFG_HOST_LINK_EN;
regmap_write_4(sysrm, USB20PHY_CFG, val);
}
pmureg = OF_getpropint(sc->sc_phy, "samsung,pmureg-phandle", 0);
pmurm = regmap_byphandle(pmureg);
if (pmurm) {
node = OF_getnodebyphandle(pmureg);
if (OF_is_compatible(node, "samsung,exynos5250-pmu"))
offset = USB_HOST_POWER_5250;
else
offset = USB_HOST_POWER_54XX;
val = regmap_read_4(pmurm, offset);
val |= USB_HOST_POWER_EN;
regmap_write_4(pmurm, offset, val);
}
delay(10000);
val = bus_space_read_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0);
val &= ~(HOST_CTRL0_FSEL_MASK |
HOST_CTRL0_PHYSWRST |
HOST_CTRL0_PHYSWRSTALL |
HOST_CTRL0_SIDDQ |
HOST_CTRL0_FORCESUSPEND |
HOST_CTRL0_FORCESLEEP);
val |= (
CLK_24MHZ << 16 |
HOST_CTRL0_COMMONON_N |
HOST_CTRL0_LINKSWRST |
HOST_CTRL0_UTMISWRST);
bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0, val);
delay(10000);
bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0,
bus_space_read_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0) &
~(HOST_CTRL0_LINKSWRST | HOST_CTRL0_UTMISWRST));
delay(20000);
bus_space_write_4(sc->sc.iot, sc->ph_ioh, EHCI_CTRL,
bus_space_read_4(sc->sc.iot, sc->ph_ioh, EHCI_CTRL) |
EHCI_CTRL_ENAINCRXALIGN |
EHCI_CTRL_ENAINCR4 |
EHCI_CTRL_ENAINCR8 |
EHCI_CTRL_ENAINCR16);
#if 0
if (1) {
exgpio_set_dir(0xc8, EXGPIO_DIR_OUT);
exgpio_clear_bit(0xc8);
delay(1000);
exgpio_set_bit(0xc8);
delay(5000);
val = bus_space_read_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1);
val &= ~(HOST_CTRL0_SIDDQ |
HOST_CTRL0_FORCESLEEP |
HOST_CTRL0_FORCESUSPEND);
bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val);
val |= HOST_CTRL0_PHYSWRST;
bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val);
delay(1000);
val &= ~HOST_CTRL0_PHYSWRST;
bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val);
}
#endif
delay(50000);
}