Index: sys/arm/freescale/vybrid/vf_i2c.c =================================================================== --- sys/arm/freescale/vybrid/vf_i2c.c +++ sys/arm/freescale/vybrid/vf_i2c.c @@ -57,6 +57,10 @@ #include #include +#ifdef EXT_RESOURCES +#include +#endif + #include #include #include @@ -93,20 +97,35 @@ #define vf_i2c_dbg(_sc, fmt, args...) #endif +#define HW_UNKNOWN 0x00 +#define HW_MVF600 0x01 +#define HW_VF610 0x02 + static int i2c_repeated_start(device_t, u_char, int); static int i2c_start(device_t, u_char, int); static int i2c_stop(device_t); static int i2c_reset(device_t, u_char, u_char, u_char *); static int i2c_read(device_t, char *, int, int *, int, int); static int i2c_write(device_t, const char *, int, int *, int); +static phandle_t i2c_get_node(device_t, device_t); + +struct i2c_div_type { + uint32_t reg_val; + uint32_t div; +}; struct i2c_softc { struct resource *res[2]; bus_space_tag_t bst; bus_space_handle_t bsh; +#ifdef EXT_RESOURCES + clk_t clock; + uint32_t freq; +#endif device_t dev; device_t iicbus; struct mtx mutex; + uintptr_t hwtype; }; static struct resource_spec i2c_spec[] = { @@ -115,6 +134,29 @@ { -1, 0 } }; +#ifdef EXT_RESOURCES +static struct i2c_div_type vf610_div_table[] = { + { 0x00, 20 }, { 0x01, 22 }, { 0x02, 24 }, { 0x03, 26 }, + { 0x04, 28 }, { 0x05, 30 }, { 0x09, 32 }, { 0x06, 34 }, + { 0x0A, 36 }, { 0x0B, 40 }, { 0x0C, 44 }, { 0x0D, 48 }, + { 0x0E, 56 }, { 0x12, 64 }, { 0x13, 72 }, { 0x14, 80 }, + { 0x15, 88 }, { 0x19, 96 }, { 0x16, 104 }, { 0x1A, 112 }, + { 0x17, 128 }, { 0x1D, 160 }, { 0x1E, 192 }, { 0x22, 224 }, + { 0x1F, 240 }, { 0x23, 256 }, { 0x24, 288 }, { 0x25, 320 }, + { 0x26, 384 }, { 0x2A, 448 }, { 0x27, 480 }, { 0x2B, 512 }, + { 0x2C, 576 }, { 0x2D, 640 }, { 0x2E, 768 }, { 0x32, 896 }, + { 0x2F, 960 }, { 0x33, 1024 }, { 0x34, 1152 }, { 0x35, 1280 }, + { 0x36, 1536 }, { 0x3A, 1792 }, { 0x37, 1920 }, { 0x3B, 2048 }, + { 0x3C, 2304 }, { 0x3D, 2560 }, { 0x3E, 3072 }, { 0x3F, 3840 } +}; +#endif + +static const struct ofw_compat_data i2c_compat_data[] = { + {"fsl,mvf600-i2c", HW_MVF600}, + {"fsl,vf610-i2c", HW_VF610}, + {NULL, HW_UNKNOWN} +}; + static int i2c_probe(device_t dev) { @@ -122,7 +164,7 @@ if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "fsl,mvf600-i2c")) + if (!ofw_bus_search_compatible(dev, i2c_compat_data)->ocd_data) return (ENXIO); device_set_desc(dev, "Vybrid Family Inter-Integrated Circuit (I2C)"); @@ -133,13 +175,35 @@ i2c_attach(device_t dev) { struct i2c_softc *sc; +#ifdef EXT_RESOURCES + phandle_t node; +#endif + int error; sc = device_get_softc(dev); sc->dev = dev; + sc->hwtype = ofw_bus_search_compatible(dev, i2c_compat_data)->ocd_data; +#ifdef EXT_RESOURCES + node = ofw_bus_get_node(dev); + + error = clk_get_by_ofw_index(dev, node, 0, &sc->clock); + if (error != 0) { + sc->freq = 0; + device_printf(dev, "Parent clock not found.\n"); + } else { + if (OF_hasprop(node, "clock-frequency")) + OF_getencprop(node, "clock-frequency", &sc->freq, + sizeof(sc->freq)); + else + sc->freq = 100000; + } +#endif mtx_init(&sc->mutex, device_get_nameunit(dev), "I2C", MTX_DEF); - if (bus_alloc_resources(dev, i2c_spec, sc->res)) { + error = bus_alloc_resources(dev, i2c_spec, sc->res); + if (error != 0) { + mtx_destroy(&sc->mutex); device_printf(dev, "could not allocate resources\n"); return (ENXIO); } @@ -154,6 +218,7 @@ if (sc->iicbus == NULL) { device_printf(dev, "could not add iicbus child"); mtx_destroy(&sc->mutex); + bus_release_resources(dev, i2c_spec, sc->res); return (ENXIO); } @@ -162,6 +227,33 @@ return (0); } +static int +i2c_detach(device_t dev) +{ + struct i2c_softc *sc; + int error = 0; + + sc = device_get_softc(dev); + + error = bus_generic_detach(dev); + if (error != 0) { + device_printf(dev, "cannot detach child devices.\n"); + return (error); + } + + error = device_delete_child(dev, sc->iicbus); + if (error != 0) { + device_printf(dev, "could not delete iicbus child.\n"); + return (error); + } + + bus_release_resources(dev, i2c_spec, sc->res); + + mtx_destroy(&sc->mutex); + + return (0); +} + /* Wait for transfer interrupt flag */ static int wait_for_iif(struct i2c_softc *sc) @@ -252,7 +344,7 @@ mtx_unlock(&sc->mutex); - if (error) + if (error != 0) return (error); return (IIC_NOERR); @@ -294,7 +386,7 @@ error = wait_for_iif(sc); mtx_unlock(&sc->mutex); - if (error) { + if (error != 0) { vf_i2c_dbg(sc, "cant i2c start: iif error\n"); return (error); } @@ -328,12 +420,53 @@ return (IIC_NOERR); } +static uint32_t +i2c_get_div_val(device_t dev) +{ + struct i2c_softc *sc; +#ifdef EXT_RESOURCES + uint64_t clk_freq; + int error, i; + + sc = device_get_softc(dev); + + if (sc->hwtype == HW_MVF600) + return 20; + + if (sc->freq == 0) + return vf610_div_table[nitems(vf610_div_table) - 1].reg_val; + + error = clk_get_freq(sc->clock, &clk_freq); + if (error != 0) { + device_printf(dev, "Could not get parent clock frequency. " + "Using default divider.\n"); + return vf610_div_table[nitems(vf610_div_table) - 1].reg_val; + } + + for (i = 0; i < nitems(vf610_div_table) - 1; i++) + if ((clk_freq / vf610_div_table[i].div) <= sc->freq) + break; + + return vf610_div_table[i].reg_val; +#else + sc = device_get_softc(dev); + + if (sc->hwtype == HW_VF610) + return 0x3F; + else + return 20; +#endif +} + static int i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldadr) { struct i2c_softc *sc; + uint32_t div; sc = device_get_softc(dev); + div = i2c_get_div_val(dev); + vf_i2c_dbg(sc, "Div val: %02x\n", div); vf_i2c_dbg(sc, "i2c reset\n"); @@ -351,7 +484,7 @@ DELAY(1000); - WRITE1(sc, I2C_IBFD, 20); + WRITE1(sc, I2C_IBFD, div); WRITE1(sc, I2C_IBCR, 0x0); /* Enable i2c */ DELAY(1000); @@ -389,7 +522,7 @@ while (*read < len) { error = wait_for_icf(sc); - if (error) { + if (error != 0) { mtx_unlock(&sc->mutex); return (error); } @@ -431,7 +564,7 @@ WRITE1(sc, I2C_IBDR, *buf++); error = wait_for_iif(sc); - if (error) { + if (error != 0) { mtx_unlock(&sc->mutex); return (error); } @@ -443,9 +576,19 @@ return (IIC_NOERR); } +static phandle_t +i2c_get_node(device_t bus, device_t dev) +{ + + return ofw_bus_get_node(bus); +} + static device_method_t i2c_methods[] = { - DEVMETHOD(device_probe, i2c_probe), - DEVMETHOD(device_attach, i2c_attach), + DEVMETHOD(device_probe, i2c_probe), + DEVMETHOD(device_attach, i2c_attach), + DEVMETHOD(device_detach, i2c_detach), + + DEVMETHOD(ofw_bus_get_node, i2c_get_node), DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_repeated_start, i2c_repeated_start), @@ -469,3 +612,4 @@ DRIVER_MODULE(i2c, simplebus, i2c_driver, i2c_devclass, 0, 0); DRIVER_MODULE(iicbus, i2c, iicbus_driver, iicbus_devclass, 0, 0); +DRIVER_MODULE(ofw_iicbus, i2c, ofw_iicbus_driver, ofw_iicbus_devclass, 0, 0); Index: sys/conf/files.arm64 =================================================================== --- sys/conf/files.arm64 +++ sys/conf/files.arm64 @@ -106,6 +106,7 @@ arm/broadcom/bcm2835/bcm2835_wdog.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm2836.c optional soc_brcm_bcm2837 fdt | soc_brcm_bcm2838 fdt arm/broadcom/bcm2835/bcm283x_dwc_fdt.c optional dwcotg fdt soc_brcm_bcm2837 | dwcotg fdt soc_brcm_bcm2838 +arm/freescale/vybrid/vf_i2c.c optional iicbus SOC_NXP_LS1046A arm/mv/a37x0_gpio.c optional a37x0_gpio gpio fdt arm/mv/a37x0_iic.c optional a37x0_iic iicbus fdt arm/mv/a37x0_spi.c optional a37x0_spi spibus fdt