Index: sys/arm/ti/clk/ti_clk_clkctrl.c =================================================================== --- sys/arm/ti/clk/ti_clk_clkctrl.c +++ sys/arm/ti/clk/ti_clk_clkctrl.c @@ -51,12 +51,9 @@ * clknode for clkctrl, implements gate and mux (for gpioc) */ -#define GPIO_X_GDBCLK_MASK 0x00040000 #define IDLEST_MASK 0x00030000 #define MODULEMODE_MASK 0x00000003 -#define GPIOX_GDBCLK_ENABLE 0x00040000 -#define GPIOX_GDBCLK_DISABLE 0x00000000 #define IDLEST_FUNC 0x00000000 #define IDLEST_TRANS 0x00010000 #define IDLEST_IDLE 0x00020000 @@ -67,7 +64,6 @@ struct ti_clkctrl_clknode_sc { device_t dev; - bool gdbclk; /* omap4-cm range.host + ti,clkctrl reg[0] */ uint32_t register_offset; }; @@ -94,64 +90,14 @@ } static int -ti_clkctrl_set_gdbclk_gate(struct clknode *clk, bool enable) -{ - struct ti_clkctrl_clknode_sc *sc; - uint32_t val, gpio_x_gdbclk; - uint32_t timeout = 100; - - sc = clknode_get_softc(clk); - - READ4(clk, sc->register_offset, &val); - DPRINTF(sc->dev, "val(%x) & (%x | %x = %x)\n", - val, GPIO_X_GDBCLK_MASK, MODULEMODE_MASK, - GPIO_X_GDBCLK_MASK | MODULEMODE_MASK); - - if (enable) { - val = val & MODULEMODE_MASK; - val |= GPIOX_GDBCLK_ENABLE; - } else { - val = val & MODULEMODE_MASK; - val |= GPIOX_GDBCLK_DISABLE; - } - - DPRINTF(sc->dev, "val %x\n", val); - WRITE4(clk, sc->register_offset, val); - - /* Wait */ - while (timeout) { - READ4(clk, sc->register_offset, &val); - gpio_x_gdbclk = val & GPIO_X_GDBCLK_MASK; - if (enable && (gpio_x_gdbclk == GPIOX_GDBCLK_ENABLE)) - break; - else if (!enable && (gpio_x_gdbclk == GPIOX_GDBCLK_DISABLE)) - break; - DELAY(10); - timeout--; - } - if (timeout == 0) { - device_printf(sc->dev, "ti_clkctrl_set_gdbclk_gate: Timeout\n"); - return (1); - } - - return (0); -} - -static int ti_clkctrl_set_gate(struct clknode *clk, bool enable) { struct ti_clkctrl_clknode_sc *sc; uint32_t val, idlest, module; uint32_t timeout=100; - int err; sc = clknode_get_softc(clk); - if (sc->gdbclk) { - err = ti_clkctrl_set_gdbclk_gate(clk, enable); - return (err); - } - READ4(clk, sc->register_offset, &val); if (enable) @@ -210,7 +156,6 @@ sc = clknode_get_softc(clk); sc->register_offset = clkdef->register_offset; - sc->gdbclk = clkdef->gdbclk; if (clknode_register(clkdom, clk) == NULL) { return (2); Index: sys/arm/ti/clk/ti_clkctrl.c =================================================================== --- sys/arm/ti/clk/ti_clkctrl.c +++ sys/arm/ti/clk/ti_clkctrl.c @@ -46,6 +46,7 @@ #include #include +#include #include #include @@ -54,19 +55,16 @@ #include #include #include +#include +/* Documentation/devicetree/bindings/clock/ti-clkctrl.txt */ + #if 0 #define DPRINTF(dev, msg...) device_printf(dev, msg) #else #define DPRINTF(dev, msg...) #endif -#define L4LS_CLKCTRL_38 2 -#define L4_WKUP_CLKCTRL_0 1 -#define NO_SPECIAL_REG 0 - -/* Documentation/devicetree/bindings/clock/ti-clkctrl.txt */ - #define TI_CLKCTRL_L4_WKUP 5 #define TI_CLKCTRL_L4_SECURE 4 #define TI_CLKCTRL_L4_PER 3 @@ -85,18 +83,115 @@ struct ti_clkctrl_softc { device_t dev; - + bool attach_done; + cell_t *reg; + uint32_t num_reg; + const char *org_name; struct clkdom *clkdom; + uint8_t special_reg; + uint64_t parent_offset; }; +struct ti_clkctrl_special_clock_names { + const char *name; + uint8_t name_id; +}; + +/* sub clock types */ +#define TI_CLKCTRL_GATE 1 + +/* Name_id defines - used to map char strings to integer IDs in ti_clkctrl_special_clock_names */ +#define NO_SPECIAL_REG 0 + +#ifdef SOC_OMAP4 + /* TODO */ +#endif /* SOC_OMAP4 */ + +#ifdef SOC_TI_AM335X +#define L4LS_CLKCTRL_38 2 +#define L4_WKUP_CLKCTRL_0 1 +#endif /* SOC_TI_AM335X */ + +struct ti_clkctrl_special_clocks { + uint8_t name_id; + uint32_t offset_reg_address; + uint8_t clk_type; + /* clk_gate_def */ + uint32_t shift; + uint32_t mask; + /* clknode_init_def */ + int id; + const char **parents; + int parent_cnt; +}; + +/* Map up clkctrl with sub clocks */ +#ifdef SOC_OMAP4 +#endif /* SOC_OMAP4 */ + +#ifdef SOC_TI_AM335X +struct ti_clkctrl_special_clock_names am335x_special_clocks_name[] = { + { "l4ls-clkctrl@38", L4LS_CLKCTRL_38 }, + { "l4-wkup-clkctrl@0", L4_WKUP_CLKCTRL_0 }, + { NULL, NO_SPECIAL_REG } +}; +#endif /* SOC_TI_AM335X */ + +#ifdef SOC_OMAP4 + /* TODO */ +#endif /* SOC_OMAP4 */ + +/* Map up the sub clocks */ +#ifdef SOC_TI_AM335X + +/* + * TRM Rev Q. 8.1.12.1.29 - 8.1.12.31 and 8.1.12.2.3 + * + * OPTFCLKEN_GPIOX_GDBCLK bit 18 gate + * + * Figure 8-22 describes the debounce clock selection. + * CLK_32KHZ from PLL: dts label clk_24mhz_clkctrl + * + */ +/* + * TRM Rev Q. 8.1.12.2.3 + * + * OPTFCLKEN_GPIO0_GDBCLK bit 18 gate + * + * Figure 8-22 describes the debounce clock selection. + * + * 0x1 CLK_32K_RTC: dts label clk_32768_ck + * 0x2 CLK_32KHZ from PLL: dts label clk_24mhz_clkctrl + * 0x0 CLK_RC32K: dts label clk_rc32k_ck + * + * 8.1.12.3.14 - CLKSEL_GPIO0_DBCLK mux bit 1:0 + * + */ +/* clkctrl name scheme is [dts-label@baseaddress]_[offset] to the name */ +static const char *gpiox_dbclk_parent[] = { "clk-24mhz-clkctrl@14c_0" }; + +static const char *gpio0_dbclk_parents[] = { "gpio0_dbclk_mux_ck@53c" }; + +static struct ti_clkctrl_special_clocks am335x_special_clocks[] = { + { L4LS_CLKCTRL_38, AM3_L4LS_GPIO2_CLKCTRL, TI_CLKCTRL_GATE, 18, 0x400000, 0x75, gpiox_dbclk_parent, nitems(gpiox_dbclk_parent)}, + { L4LS_CLKCTRL_38, AM3_L4LS_GPIO3_CLKCTRL, TI_CLKCTRL_GATE, 18, 0x400000, 0x79, gpiox_dbclk_parent, nitems(gpiox_dbclk_parent)}, + { L4LS_CLKCTRL_38, AM3_L4LS_GPIO4_CLKCTRL, TI_CLKCTRL_GATE, 18, 0x400000, 0x7D, gpiox_dbclk_parent, nitems(gpiox_dbclk_parent)}, + { L4_WKUP_CLKCTRL_0, AM3_L4_WKUP_GPIO1_CLKCTRL, TI_CLKCTRL_GATE, 18, 0x400000, 0x9, gpio0_dbclk_parents, nitems(gpio0_dbclk_parents)}, + { NO_SPECIAL_REG, 0, 0, 0, 0, 0, NULL, 0} +}; +#endif /* SOC_TI_AM335X */ + static int ti_clkctrl_probe(device_t dev); static int ti_clkctrl_attach(device_t dev); static int ti_clkctrl_detach(device_t dev); +static void ti_clkctrl_new_pass(device_t dev); + int clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells, phandle_t *cells, struct clknode **clk); static int -create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset, - uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg); +create_clkctrl(struct ti_clkctrl_softc *sc, uint32_t index, uint32_t reg_offset); +static int +create_special_clock(struct ti_clkctrl_softc *sc, uint32_t index, uint32_t reg_offset, struct ti_clkctrl_special_clocks *special_clock); static int ti_clkctrl_probe(device_t dev) @@ -117,16 +212,13 @@ { struct ti_clkctrl_softc *sc; phandle_t node; - cell_t *reg; ssize_t numbytes_reg; - int num_reg, err, ti_clock_cells; - uint32_t index, reg_offset, reg_address; - const char *org_name; - uint64_t parent_offset; - uint8_t special_reg = NO_SPECIAL_REG; + int err=0, ti_clock_cells, i; + uint32_t index, reg_offset; sc = device_get_softc(dev); sc->dev = dev; + sc->attach_done = false; node = ofw_bus_get_node(dev); /* Sanity check */ @@ -149,26 +241,26 @@ device_printf(sc->dev, "reg property empty - check your devicetree\n"); return (ENXIO); } - num_reg = numbytes_reg / sizeof(cell_t); + sc->num_reg = numbytes_reg / sizeof(cell_t); - reg = malloc(numbytes_reg, M_DEVBUF, M_WAITOK); - OF_getencprop(node, "reg", reg, numbytes_reg); + sc->reg = malloc(numbytes_reg, M_DEVBUF, M_WAITOK); + OF_getencprop(node, "reg", sc->reg, numbytes_reg); /* Create clock domain */ sc->clkdom = clkdom_create(sc->dev); if (sc->clkdom == NULL) { - free(reg, M_DEVBUF); + free(sc->reg, M_DEVBUF); DPRINTF(sc->dev, "Failed to create clkdom\n"); return (ENXIO); } clkdom_set_ofw_mapper(sc->clkdom, clkctrl_ofw_map); - + /* Create clock nodes */ /* name */ - clk_parse_ofw_clk_name(sc->dev, node, &org_name); + clk_parse_ofw_clk_name(sc->dev, node, &sc->org_name); /* Get parent range */ - parent_offset = ti_omap4_cm_get_simplebus_base_host(device_get_parent(dev)); + sc->parent_offset = ti_omap4_cm_get_simplebus_base_host(device_get_parent(dev)); /* Check if this is a clkctrl with special registers like gpio */ switch (ti_chip()) { @@ -179,74 +271,124 @@ #endif /* SOC_OMAP4 */ #ifdef SOC_TI_AM335X - /* Checkout TRM 8.1.12.1.29 - 8.1.12.31 and 8.1.12.2.3 + /* Checkout TRM 8.1.12.1.29 - 8.1.12.31 and 8.1.12.2.3 bit 18 gate * and the DTS. */ case CHIP_AM335X: - if (strcmp(org_name, "l4ls-clkctrl@38") == 0) - special_reg = L4LS_CLKCTRL_38; - else if (strcmp(org_name, "l4-wkup-clkctrl@0") == 0) - special_reg = L4_WKUP_CLKCTRL_0; - break; + for (i = 0; am335x_special_clocks_name[i].name != NULL; i++) { + if (strcmp(am335x_special_clocks_name[i].name, sc->org_name) == 0) { + sc->special_reg = am335x_special_clocks_name[i].name_id; + break; + } + } /* for */ + #endif /* SOC_TI_AM335X */ default: break; } /* reg property has a pair of (base address, length) */ - for (index = 0; index < num_reg; index += 2) { - for (reg_offset = 0; reg_offset < reg[index+1]; reg_offset += sizeof(cell_t)) { - err = create_clkctrl(sc, reg, index, reg_offset, parent_offset, - org_name, false); + for (index = 0; index < sc->num_reg; index += 2) { + for (reg_offset = 0; reg_offset < sc->reg[index+1]; reg_offset += sizeof(cell_t)) { + /* + * Create clkctrl clk node. + * special sub clocks will be created in ti_clkctrl_new_pass due to + * its parent clocks are not created at this time. + */ + err = create_clkctrl(sc, index, reg_offset); if (err) goto cleanup; + } /* inner for */ + } /* for */ + if (sc->special_reg != NO_SPECIAL_REG) { + /* need a new pass for the subclocks */ + return (bus_generic_attach(sc->dev)); + } + + err = clkdom_finit(sc->clkdom); + if (err) { + DPRINTF(sc->dev, "Clk domain finit fails %x.\n", err); + err = ENXIO; + goto cleanup; + } + + sc->attach_done = true; + +cleanup: + OF_prop_free(__DECONST(char *, sc->org_name)); + + free(sc->reg, M_DEVBUF); + + if (err) + return (err); + + return (bus_generic_attach(dev)); +} + +static void +ti_clkctrl_new_pass(device_t dev) +{ + struct ti_clkctrl_softc *sc; + int err=0, i; + uint32_t index, reg_offset, reg_address; + + sc = device_get_softc(dev); + if (sc->attach_done) { + bus_generic_new_pass(sc->dev); + return; + } + + /* reg property has a pair of (base address, length) */ + for (index = 0; index < sc->num_reg; index += 2) { + for (reg_offset = 0; reg_offset < sc->reg[index+1]; reg_offset += sizeof(cell_t)) { /* Create special clkctrl for GDBCLK in GPIO registers */ - switch (special_reg) { + reg_address = sc->reg[index] + reg_offset - sc->reg[0]; + switch (sc->special_reg) { case NO_SPECIAL_REG: break; +#ifdef SOC_TI_AM335X case L4LS_CLKCTRL_38: - reg_address = reg[index] + reg_offset-reg[0]; - if (reg_address == 0x74 || - reg_address == 0x78 || - reg_address == 0x7C) - { - err = create_clkctrl(sc, reg, index, reg_offset, - parent_offset, org_name, true); - if (err) - goto cleanup; - } - break; case L4_WKUP_CLKCTRL_0: - reg_address = reg[index] + reg_offset - reg[0]; - if (reg_address == 0x8) - { - err = create_clkctrl(sc, reg, index, reg_offset, - parent_offset, org_name, true); + for (i = 0; am335x_special_clocks[i].name_id != NO_SPECIAL_REG && err == 0; i++) { + if (am335x_special_clocks[i].name_id != sc->special_reg) + continue; + + if (am335x_special_clocks[i].offset_reg_address != reg_address) + continue; + + err = create_special_clock(sc, index, reg_offset, &am335x_special_clocks[i]); if (err) - goto cleanup; + goto cleanup_new_pass; + /* Dont break - a single clkctrl can have multiple subclocks */ } break; - } /* switch (special_reg) */ +#endif + } /* switch (sc->special_reg) */ + + /* handle errors in above switch-case */ + if (err) + goto cleanup_new_pass; } /* inner for */ } /* for */ - + err = clkdom_finit(sc->clkdom); if (err) { DPRINTF(sc->dev, "Clk domain finit fails %x.\n", err); err = ENXIO; - goto cleanup; + goto cleanup_new_pass; } -cleanup: - OF_prop_free(__DECONST(char *, org_name)); + sc->attach_done = true; - free(reg, M_DEVBUF); +cleanup_new_pass: + OF_prop_free(__DECONST(char *, sc->org_name)); + free(sc->reg, M_DEVBUF); if (err) - return (err); + return; - return (bus_generic_attach(dev)); + bus_generic_attach(dev); } static int @@ -259,17 +401,25 @@ int clkctrl_ofw_map(struct clkdom *clkdom, uint32_t ncells, phandle_t *cells, struct clknode **clk) { + struct clknode *parent_clk; + char *name; + size_t name_len; + if (ncells == 0) *clk = clknode_find_by_id(clkdom, 1); else if (ncells == 1) *clk = clknode_find_by_id(clkdom, cells[0]); else if (ncells == 2) { - /* To avoid collision with other IDs just add one. - * All other registers has an offset of 4 from each other. - */ - if (cells[1]) - *clk = clknode_find_by_id(clkdom, cells[0]+1); - else + if (cells[1]) { + parent_clk = clknode_find_by_id(clkdom, cells[0]); + if (parent_clk == NULL) + return (ENXIO); + name_len = strlen(clknode_get_name(parent_clk)) + 1 + 3; /* _xx */ + name = malloc(name_len, M_OFWPROP, M_WAITOK); + snprintf(name, name_len, "%s_%x", clknode_get_name(parent_clk), cells[1]); + *clk = clknode_find_by_name(name); + OF_prop_free(name); + } else *clk = clknode_find_by_id(clkdom, cells[0]); } else @@ -282,14 +432,13 @@ } static int -create_clkctrl(struct ti_clkctrl_softc *sc, cell_t *reg, uint32_t index, uint32_t reg_offset, - uint64_t parent_offset, const char *org_name, bool special_gdbclk_reg) { +create_clkctrl(struct ti_clkctrl_softc *sc, uint32_t index, uint32_t reg_offset) { struct ti_clk_clkctrl_def def; char *name; size_t name_len; - int err; + int err=0; - name_len = strlen(org_name) + 1 + 5; /* 5 = _xxxx */ + name_len = strlen(sc->org_name) + 1 + 5; /* 5 = _xxxx */ name = malloc(name_len, M_OFWPROP, M_WAITOK); /* @@ -299,16 +448,12 @@ * sys/gnu/dts/dts/include/dt-bindings/clock/dra7.h * reg[0] are in practice the same as the offset described in the dts. */ - /* special_gdbclk_reg are 0 or 1 */ - def.clkdef.id = reg[index] + reg_offset - reg[0] + special_gdbclk_reg; - def.register_offset = parent_offset + reg[index] + reg_offset; + def.clkdef.id = sc->reg[index] + reg_offset - sc->reg[0]; + def.register_offset = sc->parent_offset + sc->reg[index] + reg_offset; - /* Indicate this clkctrl is special and dont use IDLEST/MODULEMODE */ - def.gdbclk = special_gdbclk_reg; - /* Make up an uniq name in the namespace for each clkctrl */ snprintf(name, name_len, "%s_%x", - org_name, def.clkdef.id); + sc->org_name, def.clkdef.id); def.clkdef.name = (const char *) name; DPRINTF(sc->dev, "ti_clkctrl_attach: reg[%d]: %s %x\n", @@ -332,11 +477,57 @@ return (err); } +static int +create_special_clock(struct ti_clkctrl_softc *sc, uint32_t index, uint32_t reg_offset, struct ti_clkctrl_special_clocks *special_clock) { + struct clk_gate_def gate; + int err=0; + char *name; + size_t name_len; + intptr_t parent_id; + + parent_id = sc->reg[index] + reg_offset - sc->reg[0]; + name_len = strlen(sc->org_name) + 1 + 8; /* 8 = _xxxx_xx */ + name = malloc(name_len, M_OFWPROP, M_WAITOK); + /* Make up an uniq name in the namespace for each clkctrl */ + snprintf(name, name_len, "%s_%x_%x", + sc->org_name, parent_id, special_clock->shift); + + switch (special_clock->clk_type) { + case TI_CLKCTRL_GATE: + gate.clkdef.name = name; + gate.clkdef.id = special_clock->id; + gate.clkdef.parent_names = special_clock->parents; + gate.clkdef.parent_cnt = special_clock->parent_cnt; + gate.clkdef.flags = 0; + + gate.offset = sc->parent_offset + sc->reg[index] + reg_offset; + gate.shift = special_clock->shift; + gate.mask = special_clock->mask; + gate.on_value = 1; + gate.off_value = 0; + gate.gate_flags = 0; + + err = clknode_gate_register(sc->clkdom, &gate); + break; + default: + DPRINTF(sc->dev, "clk_type %x not implemented\n", + special_clock->clk_type); + break; + } /* switch */ + + OF_prop_free(name); + + return (err); +} + static device_method_t ti_clkctrl_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ti_clkctrl_probe), DEVMETHOD(device_attach, ti_clkctrl_attach), DEVMETHOD(device_detach, ti_clkctrl_detach), + + /* Bus interface */ + DEVMETHOD(bus_new_pass, ti_clkctrl_new_pass), DEVMETHOD_END };