/*- * Copyright 2015 Alexander Kabaev * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. */ /* * Ingenic JZ4780 CGU driver. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clkdev_if.h" #include /********************************************************************** * JZ4780 CGU clock domain **********************************************************************/ struct jz4780_clock_softc { device_t dev; struct resource *res[1]; struct mtx mtx; struct clkdom *clkdom; }; static struct resource_spec jz4780_clock_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; struct jz4780_clk_pll_def { uint16_t clk_id; uint16_t clk_reg; const char *clk_name; const char *clk_pname[1]; }; #define PLL(_id, cname, pname, reg) { \ .clk_id = _id, \ .clk_reg = reg, \ .clk_name = cname, \ .clk_pname[0] = pname, \ } struct jz4780_clk_gate_def { uint16_t clk_id; uint16_t clk_bit; const char *clk_name; const char *clk_pname[1]; }; #define GATE(_id, cname, pname, bit) { \ .clk_id = _id, \ .clk_bit = bit, \ .clk_name = cname, \ .clk_pname[0] = pname, \ } #define MUX(reg, shift, bits, map) \ .clk_mux.mux_reg = (reg), \ .clk_mux.mux_shift = (shift), \ .clk_mux.mux_bits = (bits), \ .clk_mux.mux_map = (map), #define NO_MUX #define DIV(reg, shift, lg, bits, ce, st, bb) \ .clk_div.div_reg = (reg), \ .clk_div.div_shift = (shift), \ .clk_div.div_bits = (bits), \ .clk_div.div_lg = (lg), \ .clk_div.div_ce_bit = (ce), \ .clk_div.div_st_bit = (st), \ .clk_div.div_busy_bit = (bb), #define NO_DIV \ #define GATEBIT(bit) \ .clk_gate_bit = (bit), #define NO_GATE \ .clk_gate_bit = (-1), #define PLIST(pnames...) \ .clk_pnames = { pnames }, #define GENCLK(id, name, type, parents, mux, div, gt) { \ .clk_id = id, \ .clk_type = type, \ .clk_name = name, \ parents \ mux \ div \ gt \ } /* PLL definitions */ static struct jz4780_clk_pll_def pll_clks[] = { PLL(JZ4780_CLK_APLL, "apll", "ext", JZ_CPAPCR), PLL(JZ4780_CLK_MPLL, "mpll", "ext", JZ_CPMPCR), PLL(JZ4780_CLK_EPLL, "epll", "ext", JZ_CPEPCR), PLL(JZ4780_CLK_VPLL, "vpll", "ext", JZ_CPVPCR), }; /* OTG PHY clock (reuse gate def structure */ static struct jz4780_clk_gate_def otg_clks[] = { GATE(JZ4780_CLK_OTGPHY, "otg_phy", "ext", 0), }; static const struct jz4780_clk_descr gen_clks[] = { GENCLK(JZ4780_CLK_SCLKA, "sclk_a", CLK_MASK_MUX, PLIST("apll", "ext", "rtc"), MUX(JZ_CPCCR, 30, 2, 0x7), NO_DIV, NO_GATE ), GENCLK(JZ4780_CLK_CPUMUX, "cpumux", CLK_MASK_MUX, PLIST("sclk_a", "mpll", "epll"), MUX(JZ_CPCCR, 28, 2, 0x7), NO_DIV, NO_GATE ), GENCLK(JZ4780_CLK_CPU, "cpu", CLK_MASK_DIV, PLIST("cpumux"), NO_MUX, DIV(JZ_CPCCR, 0, 0, 4, 22, -1, -1), NO_GATE ), GENCLK(JZ4780_CLK_L2CACHE, "l2cache", CLK_MASK_DIV, PLIST("cpumux"), NO_MUX, DIV(JZ_CPCCR, 4, 0, 4, -1, -1, -1), NO_GATE ), GENCLK(JZ4780_CLK_AHB0, "ahb0", CLK_MASK_MUX | CLK_MASK_DIV, PLIST("sclk_a", "mpll", "epll"), MUX(JZ_CPCCR, 26, 2, 0x7), DIV(JZ_CPCCR, 8, 0, 4, 21, -1, -1), NO_GATE ), GENCLK(JZ4780_CLK_AHB2PMUX, "ahb2_apb_mux", CLK_MASK_MUX, PLIST("sclk_a", "mpll", "rtc"), MUX(JZ_CPCCR, 24, 2, 0x7), NO_DIV, NO_GATE ), GENCLK(JZ4780_CLK_AHB2, "ahb2", CLK_MASK_DIV, PLIST("ahb2_apb_mux"), NO_MUX, DIV(JZ_CPCCR, 12, 0, 4, 20, -1, -1), NO_GATE ), GENCLK(JZ4780_CLK_PCLK, "pclk", CLK_MASK_DIV, PLIST("ahb2_apb_mux"), NO_MUX, DIV(JZ_CPCCR, 16, 0, 4, 20, -1, -1), NO_GATE ), GENCLK(JZ4780_CLK_DDR, "ddr", CLK_MASK_MUX | CLK_MASK_DIV, PLIST("sclk_a", "mpll"), MUX(JZ_DDCDR, 30, 2, 0x6), DIV(JZ_DDCDR, 0, 0, 4, 29, 28, 27), NO_GATE ), GENCLK(JZ4780_CLK_VPU, "vpu", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE, PLIST("sclk_a", "mpll", "epll"), MUX(JZ_VPUCDR, 30, 2, 0xe), DIV(JZ_VPUCDR, 0, 0, 4, 29, 28, 27), GATEBIT(32 + 2) ), GENCLK(JZ4780_CLK_I2SPLL, "i2s_pll", CLK_MASK_MUX | CLK_MASK_DIV, PLIST("sclk_a", "epll"), MUX(JZ_I2SCDR, 30, 1, 0xc), DIV(JZ_I2SCDR, 0, 0, 8, 29, 28, 27), NO_GATE ), GENCLK(JZ4780_CLK_I2S, "i2s", CLK_MASK_MUX, PLIST("ext", "i2s_pll"), MUX(JZ_I2SCDR, 31, 1, 0xc), NO_DIV, NO_GATE ), GENCLK(JZ4780_CLK_LCD0PIXCLK, "lcd0pixclk", CLK_MASK_MUX | CLK_MASK_DIV, PLIST("sclk_a", "mpll", "vpll"), MUX(JZ_LP0CDR, 30, 2, 0xe), DIV(JZ_LP0CDR, 0, 0, 8, 28, 27, 26), NO_GATE ), GENCLK(JZ4780_CLK_LCD1PIXCLK, "lcd1pixclk", CLK_MASK_MUX | CLK_MASK_DIV, PLIST("sclk_a", "mpll", "vpll"), MUX(JZ_LP1CDR, 30, 2, 0xe), DIV(JZ_LP1CDR, 0, 0, 8, 28, 27, 26), NO_GATE ), GENCLK(JZ4780_CLK_MSCMUX, "msc_mux", CLK_MASK_MUX, PLIST("sclk_a", "mpll"), MUX(JZ_MSC0CDR, 30, 2, 0x6), NO_DIV, NO_GATE ), GENCLK(JZ4780_CLK_MSC0, "msc0", CLK_MASK_DIV | CLK_MASK_GATE, PLIST("msc_mux"), NO_MUX, DIV(JZ_MSC0CDR, 0, 1, 8, 29, 28, 27), GATEBIT(3) ), GENCLK(JZ4780_CLK_MSC1, "msc1", CLK_MASK_DIV | CLK_MASK_GATE, PLIST("msc_mux"), NO_MUX, DIV(JZ_MSC1CDR, 0, 1, 8, 29, 28, 27), GATEBIT(11) ), GENCLK(JZ4780_CLK_MSC2, "msc2", CLK_MASK_DIV | CLK_MASK_GATE, PLIST("msc_mux"), NO_MUX, DIV(JZ_MSC2CDR, 0, 1, 8, 29, 28, 27), GATEBIT(12) ), GENCLK(JZ4780_CLK_UHC, "uhc", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE, PLIST("sclk_a", "mpll", "epll", "otg_phy"), MUX(JZ_UHCCDR, 30, 2, 0xf), DIV(JZ_UHCCDR, 0, 0, 8, 29, 28, 27), GATEBIT(24) ), GENCLK(JZ4780_CLK_SSIPLL, "ssi_pll", CLK_MASK_MUX | CLK_MASK_DIV, PLIST("sclk_a", "mpll"), MUX(JZ_SSICDR, 30, 1, 0xc), DIV(JZ_SSICDR, 0, 0, 8, 29, 28, 27), NO_GATE ), GENCLK(JZ4780_CLK_SSI, "ssi", CLK_MASK_MUX, PLIST("ext", "ssi_pll"), MUX(JZ_SSICDR, 31, 1, 0xc), NO_DIV, NO_GATE ), GENCLK(JZ4780_CLK_CIMMCLK, "cim_mclk", CLK_MASK_MUX | CLK_MASK_DIV, PLIST("sclk_a", "mpll"), MUX(JZ_CIMCDR, 31, 1, 0xc), DIV(JZ_CIMCDR, 0, 0, 8, 30, 29, 28), NO_GATE ), GENCLK(JZ4780_CLK_PCMPLL, "pcm_pll", CLK_MASK_MUX | CLK_MASK_DIV, PLIST("sclk_a", "mpll", "epll", "vpll"), MUX(JZ_PCMCDR, 29, 2, 0xf), DIV(JZ_PCMCDR, 0, 0, 8, 28, 27, 26), NO_GATE ), GENCLK(JZ4780_CLK_PCM, "pcm", CLK_MASK_MUX | CLK_MASK_GATE, PLIST("ext", "pcm_pll"), MUX(JZ_PCMCDR, 31, 1, 0xc), NO_DIV, GATEBIT(32 + 3) ), GENCLK(JZ4780_CLK_GPU, "gpu", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE, PLIST("sclk_a", "mpll", "epll"), MUX(JZ_GPUCDR, 30, 2, 0x7), DIV(JZ_GPUCDR, 0, 0, 4, 29, 28, 27), GATEBIT(32 + 4) ), GENCLK(JZ4780_CLK_HDMI, "hdmi", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE, PLIST("sclk_a", "mpll", "vpll"), MUX(JZ_HDMICDR, 30, 2, 0xe), DIV(JZ_HDMICDR, 0, 0, 8, 29, 28, 26), GATEBIT(32 + 9) ), GENCLK(JZ4780_CLK_BCH, "bch", CLK_MASK_MUX | CLK_MASK_DIV | CLK_MASK_GATE, PLIST("sclk_a", "mpll", "epll"), MUX(JZ_BCHCDR, 30, 2, 0x7), DIV(JZ_BCHCDR, 0, 0, 4, 29, 28, 27), GATEBIT(1) ), }; static struct jz4780_clk_gate_def gate_clks[] = { GATE(JZ4780_CLK_NEMC, "nemc", "ahb2", 0), GATE(JZ4780_CLK_OTG0, "otg0", "ext", 2), GATE(JZ4780_CLK_SSI0, "ssi0", "ssi", 4), GATE(JZ4780_CLK_SMB0, "smb0", "pclk", 5), GATE(JZ4780_CLK_SMB1, "smb1", "pclk", 6), GATE(JZ4780_CLK_SCC, "scc", "ext", 7), GATE(JZ4780_CLK_AIC, "aic", "ext", 8), GATE(JZ4780_CLK_TSSI0, "tssi0", "ext", 9), GATE(JZ4780_CLK_OWI, "owi", "ext", 10), GATE(JZ4780_CLK_KBC, "kbc", "ext", 13), GATE(JZ4780_CLK_SADC, "sadc", "ext", 14), GATE(JZ4780_CLK_UART0, "uart0", "ext", 15), GATE(JZ4780_CLK_UART1, "uart1", "ext", 16), GATE(JZ4780_CLK_UART2, "uart2", "ext", 17), GATE(JZ4780_CLK_UART3, "uart3", "ext", 18), GATE(JZ4780_CLK_SSI1, "ssi1", "ssi", 19), GATE(JZ4780_CLK_SSI2, "ssi2", "ssi", 20), GATE(JZ4780_CLK_PDMA, "pdma", "ext", 21), GATE(JZ4780_CLK_GPS, "gps", "ext", 22), GATE(JZ4780_CLK_MAC, "mac", "ext", 23), GATE(JZ4780_CLK_SMB2, "smb2", "pclk", 25), GATE(JZ4780_CLK_CIM, "cim", "ext", 26), GATE(JZ4780_CLK_LCD, "lcd", "ext", 28), GATE(JZ4780_CLK_TVE, "tve", "lcd", 27), GATE(JZ4780_CLK_IPU, "ipu", "ext", 29), GATE(JZ4780_CLK_DDR0, "ddr0", "ddr", 30), GATE(JZ4780_CLK_DDR1, "ddr1", "ddr", 31), GATE(JZ4780_CLK_SMB3, "smb3", "pclk", 32 + 0), GATE(JZ4780_CLK_TSSI1, "tssi1", "ext", 32 + 1), GATE(JZ4780_CLK_COMPRESS, "compress", "ext", 32 + 5), GATE(JZ4780_CLK_AIC1, "aic1", "ext", 32 + 6), GATE(JZ4780_CLK_GPVLC, "gpvlc", "ext", 32 + 7), GATE(JZ4780_CLK_OTG1, "otg1", "ext", 32 + 8), GATE(JZ4780_CLK_UART4, "uart4", "ext", 32 + 10), GATE(JZ4780_CLK_AHBMON, "ahb_mon", "ext", 32 + 11), GATE(JZ4780_CLK_SMB4, "smb4", "pclk", 32 + 12), GATE(JZ4780_CLK_DES, "des", "ext", 32 + 13), GATE(JZ4780_CLK_X2D, "x2d", "ext", 32 + 14), GATE(JZ4780_CLK_CORE1, "core1", "cpu", 32 + 15), }; static int jz4780_clock_register(struct jz4780_clock_softc *sc) { int i, ret; /* Register PLLs */ for (i = 0; i < nitems(pll_clks); i++) { struct clknode_init_def clkdef; clkdef.id = pll_clks[i].clk_id; clkdef.name = __DECONST(char *, pll_clks[i].clk_name); clkdef.parent_names = pll_clks[i].clk_pname; clkdef.parent_cnt = 1; clkdef.flags = CLK_NODE_STATIC_STRINGS; ret = jz4780_clk_pll_register(sc->clkdom, &clkdef, &sc->mtx, sc->res[0], pll_clks[i].clk_reg); if (ret != 0) return (ret); } /* Register OTG clock */ for (i = 0; i < nitems(otg_clks); i++) { struct clknode_init_def clkdef; clkdef.id = otg_clks[i].clk_id; clkdef.name = __DECONST(char *, otg_clks[i].clk_name); clkdef.parent_names = otg_clks[i].clk_pname; clkdef.parent_cnt = 1; clkdef.flags = CLK_NODE_STATIC_STRINGS; ret = jz4780_clk_otg_register(sc->clkdom, &clkdef, &sc->mtx, sc->res[0]); if (ret != 0) return (ret); } /* Register muxes and divisors */ for (i = 0; i < nitems(gen_clks); i++) { ret = jz4780_clk_gen_register(sc->clkdom, &gen_clks[i], &sc->mtx, sc->res[0]); if (ret != 0) return (ret); } /* Register simple gates */ for (i = 0; i < nitems(gate_clks); i++) { struct clk_gate_def gatedef; gatedef.clkdef.id = gate_clks[i].clk_id; gatedef.clkdef.name = __DECONST(char *, gate_clks[i].clk_name); gatedef.clkdef.parent_names = gate_clks[i].clk_pname; gatedef.clkdef.parent_cnt = 1; gatedef.clkdef.flags = CLK_NODE_STATIC_STRINGS; if (gate_clks[i].clk_bit < 32) { gatedef.offset = JZ_CLKGR0; gatedef.shift = gate_clks[i].clk_bit; } else { gatedef.offset = JZ_CLKGR1; gatedef.shift = gate_clks[i].clk_bit - 32; } gatedef.mask = 1; gatedef.on_value = 0; gatedef.off_value = 1; gatedef.gate_flags = 0; ret = clknode_gate_register(sc->clkdom, &gatedef); if (ret != 0) return (ret); } return (0); } static int jz4780_clock_fixup(struct jz4780_clock_softc *sc) { struct clknode *clk_uhc; int ret; /* * Make UHC mux use MPLL as the source. It defaults to OTG_PHY * and that somehow just does not work. */ clkdom_xlock(sc->clkdom); /* Assume the worst */ ret = ENXIO; clk_uhc = clknode_find_by_id(sc->clkdom, JZ4780_CLK_UHC); if (clk_uhc != NULL) { ret = clknode_set_parent_by_name(clk_uhc, "mpll"); if (ret != 0) device_printf(sc->dev, "unable to reparent uhc clock\n"); else ret = clknode_set_freq(clk_uhc, 48000000, 0, 0); if (ret != 0) device_printf(sc->dev, "unable to init uhc clock\n"); } else device_printf(sc->dev, "unable to lookup uhc clock\n"); clkdom_unlock(sc->clkdom); return (ret); } #define CGU_LOCK(sc) mtx_lock(&(sc)->mtx) #define CGU_UNLOCK(sc) mtx_unlock(&(sc)->mtx) #define CGU_LOCK_INIT(sc) \ mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \ "jz4780-cgu", MTX_DEF) #define CGU_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx); #define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) #define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg)) static int jz4780_clock_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-cgu")) return (ENXIO); device_set_desc(dev, "Ingenic jz4780 CGU"); return (BUS_PROBE_DEFAULT); } static int jz4780_clock_attach(device_t dev) { struct jz4780_clock_softc *sc; sc = device_get_softc(dev); if (bus_alloc_resources(dev, jz4780_clock_spec, sc->res)) { device_printf(dev, "could not allocate resources for device\n"); return (ENXIO); } sc->dev = dev; CGU_LOCK_INIT(sc); sc->clkdom = clkdom_create(dev); if (sc->clkdom == NULL) goto fail; if (jz4780_clock_register(sc) != 0) goto fail; if (clkdom_finit(sc->clkdom) != 0) goto fail; if (jz4780_clock_fixup(sc) != 0) goto fail; if (bootverbose) clkdom_dump(sc->clkdom); return (0); fail: bus_release_resources(dev, jz4780_clock_spec, sc->res); CGU_LOCK_DESTROY(sc); return (ENXIO); } static int jz4780_clock_detach(device_t dev) { struct jz4780_clock_softc *sc; sc = device_get_softc(dev); bus_release_resources(dev, jz4780_clock_spec, sc->res); CGU_LOCK_DESTROY(sc); return (0); } static int jz4780_clock_write_4(device_t dev, bus_addr_t addr, uint32_t val) { struct jz4780_clock_softc *sc; sc = device_get_softc(dev); CSR_WRITE_4(sc, addr, val); return (0); } static int jz4780_clock_read_4(device_t dev, bus_addr_t addr, uint32_t *val) { struct jz4780_clock_softc *sc; sc = device_get_softc(dev); *val = CSR_READ_4(sc, addr); return (0); } static int jz4780_clock_modify_4(device_t dev, bus_addr_t addr, uint32_t clear_mask, uint32_t set_mask) { struct jz4780_clock_softc *sc; uint32_t val; sc = device_get_softc(dev); val = CSR_READ_4(sc, addr); val &= ~clear_mask; val |= set_mask; CSR_WRITE_4(sc, addr, val); return (0); } static void jz4780_clock_device_lock(device_t dev) { struct jz4780_clock_softc *sc; sc = device_get_softc(dev); CGU_LOCK(sc); } static void jz4780_clock_device_unlock(device_t dev) { struct jz4780_clock_softc *sc; sc = device_get_softc(dev); CGU_UNLOCK(sc); } static device_method_t jz4780_clock_methods[] = { /* Device interface */ DEVMETHOD(device_probe, jz4780_clock_probe), DEVMETHOD(device_attach, jz4780_clock_attach), DEVMETHOD(device_detach, jz4780_clock_detach), /* Clock device interface */ DEVMETHOD(clkdev_write_4, jz4780_clock_write_4), DEVMETHOD(clkdev_read_4, jz4780_clock_read_4), DEVMETHOD(clkdev_modify_4, jz4780_clock_modify_4), DEVMETHOD(clkdev_device_lock, jz4780_clock_device_lock), DEVMETHOD(clkdev_device_unlock, jz4780_clock_device_unlock), DEVMETHOD_END }; static driver_t jz4780_clock_driver = { "cgu", jz4780_clock_methods, sizeof(struct jz4780_clock_softc), }; static devclass_t jz4780_clock_devclass; EARLY_DRIVER_MODULE(jz4780_clock, simplebus, jz4780_clock_driver, jz4780_clock_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE); static int jz4780_ehci_clk_config(struct jz4780_clock_softc *sc) { clk_t phy_clk, ext_clk; uint64_t phy_freq; int err; phy_clk = NULL; ext_clk = NULL; err = -1; /* Set phy timing by copying it from ext */ if (clk_get_by_id(sc->dev, sc->clkdom, JZ4780_CLK_OTGPHY, &phy_clk) != 0) goto done; if (clk_get_parent(phy_clk, &ext_clk) != 0) goto done; if (clk_get_freq(ext_clk, &phy_freq) != 0) goto done; if (clk_set_freq(phy_clk, phy_freq, 0) != 0) goto done; err = 0; done: clk_release(ext_clk); clk_release(phy_clk); return (err); } int jz4780_ohci_enable(void) { device_t dev; struct jz4780_clock_softc *sc; uint32_t reg; dev = devclass_get_device(jz4780_clock_devclass, 0); if (dev == NULL) return (-1); sc = device_get_softc(dev); CGU_LOCK(sc); /* Do not force port1 to suspend mode */ reg = CSR_READ_4(sc, JZ_OPCR); reg |= OPCR_SPENDN1; CSR_WRITE_4(sc, JZ_OPCR, reg); CGU_UNLOCK(sc); return (0); } int jz4780_ehci_enable(void) { device_t dev; struct jz4780_clock_softc *sc; uint32_t reg; dev = devclass_get_device(jz4780_clock_devclass, 0); if (dev == NULL) return (-1); sc = device_get_softc(dev); /* * EHCI should use MPPL as a parent, but Linux configures OTG * clock anyway. Follow their lead blindly. */ if (jz4780_ehci_clk_config(sc) != 0) return (-1); CGU_LOCK(sc); /* Enable OTG, should not be necessary since we use PLL clock */ reg = CSR_READ_4(sc, JZ_USBPCR); reg &= ~(PCR_OTG_DISABLE); CSR_WRITE_4(sc, JZ_USBPCR, reg); /* Do not force port1 to suspend mode */ reg = CSR_READ_4(sc, JZ_OPCR); reg |= OPCR_SPENDN1; CSR_WRITE_4(sc, JZ_OPCR, reg); /* D- pulldown */ reg = CSR_READ_4(sc, JZ_USBPCR1); reg |= PCR_DMPD1; CSR_WRITE_4(sc, JZ_USBPCR1, reg); /* D+ pulldown */ reg = CSR_READ_4(sc, JZ_USBPCR1); reg |= PCR_DPPD1; CSR_WRITE_4(sc, JZ_USBPCR1, reg); /* 16 bit bus witdth for port 1*/ reg = CSR_READ_4(sc, JZ_USBPCR1); reg |= PCR_WORD_I_F1 | PCR_WORD_I_F0; CSR_WRITE_4(sc, JZ_USBPCR1, reg); /* Reset USB */ reg = CSR_READ_4(sc, JZ_USBPCR); reg |= PCR_POR; CSR_WRITE_4(sc, JZ_USBPCR, reg); DELAY(1); reg = CSR_READ_4(sc, JZ_USBPCR); reg &= ~(PCR_POR); CSR_WRITE_4(sc, JZ_USBPCR, reg); /* Soft-reset USB */ reg = CSR_READ_4(sc, JZ_SRBC); reg |= SRBC_UHC_SR; CSR_WRITE_4(sc, JZ_SRBC, reg); /* 300ms */ DELAY(300*hz/1000); reg = CSR_READ_4(sc, JZ_SRBC); reg &= ~(SRBC_UHC_SR); CSR_WRITE_4(sc, JZ_SRBC, reg); /* 300ms */ DELAY(300*hz/1000); CGU_UNLOCK(sc); return (0); } #define USBRESET_DETECT_TIME 0x96 int jz4780_otg_enable(void) { device_t dev; struct jz4780_clock_softc *sc; uint32_t reg; dev = devclass_get_device(jz4780_clock_devclass, 0); if (dev == NULL) return (-1); sc = device_get_softc(dev); CGU_LOCK(sc); /* Select Synopsys OTG mode */ reg = CSR_READ_4(sc, JZ_USBPCR1); reg |= PCR_SYNOPSYS; /* Set UTMI bus width to 16 bit */ reg |= PCR_WORD_I_F0 | PCR_WORD_I_F1; CSR_WRITE_4(sc, JZ_USBPCR1, reg); /* Blah */ reg = CSR_READ_4(sc, JZ_USBVBFIL); reg = REG_SET(reg, USBVBFIL_IDDIGFIL, 0); reg = REG_SET(reg, USBVBFIL_USBVBFIL, 0); CSR_WRITE_4(sc, JZ_USBVBFIL, reg); /* Setup reset detect time */ reg = CSR_READ_4(sc, JZ_USBRDT); reg = REG_SET(reg, USBRDT_USBRDT, USBRESET_DETECT_TIME); reg |= USBRDT_VBFIL_LD_EN; CSR_WRITE_4(sc, JZ_USBRDT, reg); /* Setup USBPCR bits */ reg = CSR_READ_4(sc, JZ_USBPCR); reg |= PCR_USB_MODE; reg |= PCR_COMMONONN; reg |= PCR_VBUSVLDEXT; reg |= PCR_VBUSVLDEXTSEL; reg &= ~(PCR_OTG_DISABLE); CSR_WRITE_4(sc, JZ_USBPCR, reg); /* Reset USB */ reg = CSR_READ_4(sc, JZ_USBPCR); reg |= PCR_POR; CSR_WRITE_4(sc, JZ_USBPCR, reg); DELAY(1000); reg = CSR_READ_4(sc, JZ_USBPCR); reg &= ~(PCR_POR); CSR_WRITE_4(sc, JZ_USBPCR, reg); /* Unsuspend OTG port */ reg = CSR_READ_4(sc, JZ_OPCR); reg |= OPCR_SPENDN0; CSR_WRITE_4(sc, JZ_OPCR, reg); CGU_UNLOCK(sc); return (0); }