Changeset View
Changeset View
Standalone View
Standalone View
sys/arm/ti/ti_sysc.c
Show All 40 Lines | |||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <dev/fdt/simplebus.h> | #include <dev/fdt/simplebus.h> | ||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#include <dev/clk/clk.h> | #include <dev/clk/clk.h> | ||||
#include <dev/clk/clk_link.h> | |||||
#include <arm/ti/ti_sysc.h> | #include <arm/ti/ti_sysc.h> | ||||
#include <arm/ti/clk/clock_common.h> | #include <arm/ti/clk/am33xx.h> | ||||
#include <arm/ti/clk/ti_clock_common.h> | |||||
#include <arm/ti/ti_cpuid.h> | |||||
#define DEBUG_SYSC 0 | #define DEBUG_SYSC 0 | ||||
#if DEBUG_SYSC | #if DEBUG_SYSC | ||||
#define DPRINTF(dev, msg...) device_printf(dev, msg) | #define DPRINTF(dev, msg...) device_printf(dev, msg) | ||||
#else | #else | ||||
#define DPRINTF(dev, msg...) | #define DPRINTF(dev, msg...) | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | |||||
struct clk_list { | struct clk_list { | ||||
TAILQ_ENTRY(clk_list) next; | TAILQ_ENTRY(clk_list) next; | ||||
clk_t clk; | clk_t clk; | ||||
}; | }; | ||||
struct ti_sysc_softc { | struct ti_sysc_softc { | ||||
struct simplebus_softc sc; | struct simplebus_softc sc; | ||||
bool attach_done; | |||||
device_t dev; | device_t dev; | ||||
int device_type; | int device_type; | ||||
struct sysc_reg reg[REG_MAX]; | struct sysc_reg reg[REG_MAX]; | ||||
/* Offset from host base address */ | /* Offset from host base address */ | ||||
uint64_t offset_reg[REG_MAX]; | uint64_t offset_reg[REG_MAX]; | ||||
▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | |||||
ti_sysc_clock_enable(device_t dev) { | ti_sysc_clock_enable(device_t dev) { | ||||
struct clk_list *clkp, *clkp_tmp; | struct clk_list *clkp, *clkp_tmp; | ||||
struct ti_sysc_softc *sc = device_get_softc(dev); | struct ti_sysc_softc *sc = device_get_softc(dev); | ||||
int err; | int err; | ||||
TAILQ_FOREACH_SAFE(clkp, &sc->clk_list, next, clkp_tmp) { | TAILQ_FOREACH_SAFE(clkp, &sc->clk_list, next, clkp_tmp) { | ||||
err = clk_enable(clkp->clk); | err = clk_enable(clkp->clk); | ||||
if (err) { | if (err != 0) { | ||||
DPRINTF(sc->dev, "clk_enable %s failed %d\n", | DPRINTF(sc->dev, "clk_enable %s failed %d\n", | ||||
clk_get_name(clkp->clk), err); | clk_get_name(clkp->clk), err); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
return (err); | return (err); | ||||
} | } | ||||
int | int | ||||
ti_sysc_clock_disable(device_t dev) { | ti_sysc_clock_disable(device_t dev) { | ||||
struct clk_list *clkp, *clkp_tmp; | struct clk_list *clkp, *clkp_tmp; | ||||
struct ti_sysc_softc *sc = device_get_softc(dev); | struct ti_sysc_softc *sc = device_get_softc(dev); | ||||
int err = 0; | int err = 0; | ||||
TAILQ_FOREACH_SAFE(clkp, &sc->clk_list, next, clkp_tmp) { | TAILQ_FOREACH_SAFE(clkp, &sc->clk_list, next, clkp_tmp) { | ||||
err = clk_disable(clkp->clk); | err = clk_disable(clkp->clk); | ||||
if (err) { | if (err != 0) { | ||||
DPRINTF(sc->dev, "clk_enable %s failed %d\n", | DPRINTF(sc->dev, "clk_enable %s failed %d\n", | ||||
clk_get_name(clkp->clk), err); | clk_get_name(clkp->clk), err); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
return (err); | return (err); | ||||
} | } | ||||
static int | static int | ||||
parse_regfields(struct ti_sysc_softc *sc) { | parse_regfields(struct ti_sysc_softc *sc) { | ||||
phandle_t node; | phandle_t node; | ||||
uint32_t parent_address_cells; | uint32_t parent_address_cells; | ||||
uint32_t parent_size_cells; | uint32_t parent_size_cells; | ||||
cell_t *reg; | cell_t *reg; | ||||
ssize_t nreg; | ssize_t nreg; | ||||
int err, k, reg_i, prop_idx; | int err, k, reg_i, prop_idx; | ||||
uint32_t idx; | uint32_t idx; | ||||
/* Make sure address & size are 0 */ | |||||
for (idx = 0; idx < REG_MAX; idx++) { | |||||
sc->reg[idx].address = 0; | |||||
sc->reg[idx].size = 0; | |||||
} | |||||
node = ofw_bus_get_node(sc->dev); | node = ofw_bus_get_node(sc->dev); | ||||
/* Ensure its a reg node at all */ | |||||
if (!ofw_bus_has_prop(sc->dev, "reg")) | |||||
/* Silently ignore for now */ | |||||
return (0); | |||||
/* Get parents address and size properties */ | /* Get parents address and size properties */ | ||||
err = OF_searchencprop(OF_parent(node), "#address-cells", | err = OF_searchencprop(OF_parent(node), "#address-cells", | ||||
&parent_address_cells, sizeof(parent_address_cells)); | &parent_address_cells, sizeof(parent_address_cells)); | ||||
if (err == -1) | if (err == -1) | ||||
return (ENXIO); | return (ENXIO); | ||||
if (!(parent_address_cells == 1 || parent_address_cells == 2)) { | if (!(parent_address_cells == 1 || parent_address_cells == 2)) { | ||||
DPRINTF(sc->dev, "Expect parent #address-cells=[1||2]\n"); | DPRINTF(sc->dev, "Expect parent #address-cells=[1||2]\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
Show All 12 Lines | parse_regfields(struct ti_sysc_softc *sc) { | ||||
/* Grab the content of reg properties */ | /* Grab the content of reg properties */ | ||||
nreg = OF_getproplen(node, "reg"); | nreg = OF_getproplen(node, "reg"); | ||||
if (nreg <= 0) | if (nreg <= 0) | ||||
return (ENXIO); | return (ENXIO); | ||||
reg = malloc(nreg, M_DEVBUF, M_WAITOK); | reg = malloc(nreg, M_DEVBUF, M_WAITOK); | ||||
OF_getencprop(node, "reg", reg, nreg); | OF_getencprop(node, "reg", reg, nreg); | ||||
/* Make sure address & size are 0 */ | |||||
for (idx = 0; idx < REG_MAX; idx++) { | |||||
sc->reg[idx].address = 0; | |||||
sc->reg[idx].size = 0; | |||||
} | |||||
/* Loop through reg-names and figure out which reg-name corresponds to | /* Loop through reg-names and figure out which reg-name corresponds to | ||||
* index populate the values into the reg array. | * index populate the values into the reg array. | ||||
*/ | */ | ||||
for (idx = 0, reg_i = 0; idx < REG_MAX && reg_i < nreg; idx++) { | for (idx = 0, reg_i = 0; idx < REG_MAX && reg_i < nreg; idx++) { | ||||
err = ofw_bus_find_string_index(node, "reg-names", | err = ofw_bus_find_string_index(node, "reg-names", | ||||
reg_names[idx], &prop_idx); | reg_names[idx], &prop_idx); | ||||
if (err != 0) | if (err != 0) | ||||
continue; | continue; | ||||
Show All 15 Lines | else | ||||
sc->sc.ranges[REG_REV].host; | sc->sc.ranges[REG_REV].host; | ||||
DPRINTF(sc->dev, "reg[%s] address %#jx size %#jx\n", | DPRINTF(sc->dev, "reg[%s] address %#jx size %#jx\n", | ||||
reg_names[idx], | reg_names[idx], | ||||
sc->reg[prop_idx].address, | sc->reg[prop_idx].address, | ||||
sc->reg[prop_idx].size); | sc->reg[prop_idx].size); | ||||
} | } | ||||
free(reg, M_DEVBUF); | free(reg, M_DEVBUF); | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
parse_idle(struct ti_sysc_softc *sc, const char *name, uint32_t *idle) { | parse_idle(struct ti_sysc_softc *sc, const char *name, uint32_t *idle) { | ||||
phandle_t node; | phandle_t node; | ||||
cell_t value[SYSC_IDLE_MAX]; | cell_t value[SYSC_IDLE_MAX]; | ||||
int len, no, i; | int len, no, i; | ||||
node = ofw_bus_get_node(sc->dev); | node = ofw_bus_get_node(sc->dev); | ||||
if (!OF_hasprop(node, name)) { | if (OF_hasprop(node, name) == 0) | ||||
return; | return; | ||||
} | |||||
len = OF_getproplen(node, name); | len = OF_getproplen(node, name); | ||||
no = len / sizeof(cell_t); | no = len / sizeof(cell_t); | ||||
if (no >= SYSC_IDLE_MAX) { | if (no >= SYSC_IDLE_MAX) { | ||||
DPRINTF(sc->dev, "Limit %s\n", name); | DPRINTF(sc->dev, "Limit %s\n", name); | ||||
no = SYSC_IDLE_MAX-1; | no = SYSC_IDLE_MAX-1; | ||||
len = no * sizeof(cell_t); | len = no * sizeof(cell_t); | ||||
} | } | ||||
Show All 20 Lines | #if DEBUG_SYSC | ||||
} | } | ||||
#endif | #endif | ||||
} | } | ||||
for ( ; i < SYSC_IDLE_MAX; i++) | for ( ; i < SYSC_IDLE_MAX; i++) | ||||
idle[i] = -1; | idle[i] = -1; | ||||
} | } | ||||
static int | static int | ||||
ti_sysc_attach_clocks(struct ti_sysc_softc *sc) { | |||||
clk_t *clk; | |||||
struct clk_list *clkp; | |||||
int index, err; | |||||
clk = malloc(sc->num_clocks*sizeof(clk_t), M_DEVBUF, M_WAITOK | M_ZERO); | |||||
/* Check if all clocks can be found */ | |||||
for (index = 0; index < sc->num_clocks; index++) { | |||||
err = clk_get_by_ofw_index(sc->dev, 0, index, &clk[index]); | |||||
if (err != 0) { | |||||
free(clk, M_DEVBUF); | |||||
return (1); | |||||
} | |||||
} | |||||
/* All clocks are found, add to list */ | |||||
for (index = 0; index < sc->num_clocks; index++) { | |||||
clkp = malloc(sizeof(*clkp), M_DEVBUF, M_WAITOK | M_ZERO); | |||||
clkp->clk = clk[index]; | |||||
TAILQ_INSERT_TAIL(&sc->clk_list, clkp, next); | |||||
} | |||||
/* Release the clk array */ | |||||
free(clk, M_DEVBUF); | |||||
return (0); | |||||
} | |||||
static int | |||||
ti_sysc_simplebus_attach_child(device_t dev) { | ti_sysc_simplebus_attach_child(device_t dev) { | ||||
device_t cdev; | device_t cdev; | ||||
phandle_t node, child; | phandle_t node, child; | ||||
struct ti_sysc_softc *sc = device_get_softc(dev); | struct ti_sysc_softc *sc = device_get_softc(dev); | ||||
node = ofw_bus_get_node(sc->dev); | node = ofw_bus_get_node(sc->dev); | ||||
for (child = OF_child(node); child > 0; child = OF_peer(child)) { | for (child = OF_child(node); child > 0; child = OF_peer(child)) { | ||||
if (!ofw_bus_node_status_okay(child)) | |||||
continue; | |||||
cdev = simplebus_add_device(sc->dev, child, 0, NULL, -1, NULL); | cdev = simplebus_add_device(sc->dev, child, 0, NULL, -1, NULL); | ||||
if (cdev != NULL) | if (cdev != NULL) | ||||
device_probe_and_attach(cdev); | device_probe_and_attach(cdev); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
/* Device interface */ | /* Device interface */ | ||||
static int | static int | ||||
ti_sysc_probe(device_t dev) | ti_sysc_probe(device_t dev) | ||||
{ | { | ||||
if (!ofw_bus_status_okay(dev)) | if (!ofw_bus_status_okay(dev)) | ||||
return (ENXIO); | return (ENXIO); | ||||
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) | if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) | ||||
return (ENXIO); | return (ENXIO); | ||||
device_set_desc(dev, "TI SYSC Interconnect"); | device_set_desc(dev, "TI SYSC Interconnect"); | ||||
if (bootverbose == 0) | |||||
device_quiet(dev); | |||||
return (BUS_PROBE_DEFAULT); | return (BUS_PROBE_DEFAULT); | ||||
} | } | ||||
static void | |||||
ti_sysc_init_clk_links(device_t dev) { | |||||
int chip_id; | |||||
int i, err; | |||||
struct clkdom *clkdom; | |||||
device_t child; | |||||
device_t root_dev = dev; | |||||
device_t tmp_dev; | |||||
/* find device root to attach the imaginary ti_clkinit. | |||||
* Its used to create clk_link for future clocks. | |||||
*/ | |||||
while (1) { | |||||
tmp_dev = device_get_parent(root_dev); | |||||
if (tmp_dev == NULL) | |||||
break; | |||||
root_dev = tmp_dev; | |||||
} | |||||
/* If ti_clkinitX already exist the links are already created */ | |||||
if (device_find_child(root_dev, "ti_clkinit", -1) != NULL) { | |||||
return; | |||||
} | |||||
/* otherwise add ti_clkinit as a child to the root */ | |||||
child = device_add_child(root_dev, "ti_clkinit", -1); | |||||
if (child == NULL) | |||||
panic("Cant add ti_clkinit\n"); | |||||
clkdom = clkdom_create(dev); | |||||
if (clkdom == NULL) | |||||
panic("Cannot create clkdom\n"); | |||||
chip_id = ti_chip(); | |||||
if (chip_id == CHIP_AM335X) { | |||||
/* | |||||
* pre-create clocks to get handles to parents | |||||
* before they actually exists | |||||
*/ | |||||
for (i = 0; i < nitems(scm_clocks); i++) { | |||||
err = clknode_link_register(clkdom, | |||||
&scm_clocks[i]); | |||||
if (err != 0) | |||||
device_printf(dev, "Cant create clock link %s\n", | |||||
scm_clocks[i].clkdef.name); | |||||
} | |||||
for (i = 0; i < nitems(per_cm_0); i++) { | |||||
err = clknode_link_register(clkdom, | |||||
&per_cm_0[i]); | |||||
if (err != 0) | |||||
device_printf(dev, "Cant create clock link %s\n", | |||||
per_cm_0[i].clkdef.name); | |||||
} | |||||
} | |||||
else if (chip_id == CHIP_OMAP_4) | |||||
device_printf(dev, "Todo - add clock support"); | |||||
if (clkdom_finit(clkdom) != 0) | |||||
panic("cannot finalize clkdom initialization\n"); | |||||
} | |||||
static int | static int | ||||
ti_sysc_attach(device_t dev) | ti_sysc_attach(device_t dev) | ||||
{ | { | ||||
struct ti_sysc_softc *sc; | struct ti_sysc_softc *sc; | ||||
phandle_t node; | phandle_t node; | ||||
int err; | int err; | ||||
cell_t value; | cell_t value; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
sc->dev = dev; | sc->dev = dev; | ||||
sc->device_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; | sc->device_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; | ||||
node = ofw_bus_get_node(sc->dev); | node = ofw_bus_get_node(sc->dev); | ||||
ti_sysc_init_clk_links(dev); | |||||
/* ranges - use simplebus */ | /* ranges - use simplebus */ | ||||
simplebus_init(sc->dev, node); | simplebus_init(sc->dev, node); | ||||
if (simplebus_fill_ranges(node, &sc->sc) < 0) { | if (simplebus_fill_ranges(node, &sc->sc) < 0) { | ||||
DPRINTF(sc->dev, "could not get ranges\n"); | DPRINTF(sc->dev, "could not get ranges\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
if (sc->sc.nranges == 0) { | if (sc->sc.nranges == 0) { | ||||
DPRINTF(sc->dev, "nranges == 0\n"); | DPRINTF(sc->dev, "nranges == 0\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
/* Required field reg & reg-names - assume at least "rev" exists */ | /* Required field reg & reg-names - assume at least "rev" exists */ | ||||
err = parse_regfields(sc); | err = parse_regfields(sc); | ||||
if (err) { | if (err != 0) { | ||||
DPRINTF(sc->dev, "parse_regfields failed %d\n", err); | DPRINTF(sc->dev, "parse_regfields failed %d\n", err); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
/* Optional */ | /* Optional */ | ||||
if (OF_hasprop(node, "ti,sysc-mask")) { | if (OF_hasprop(node, "ti,sysc-mask") == 1) { | ||||
OF_getencprop(node, "ti,sysc-mask", &value, sizeof(cell_t)); | OF_getencprop(node, "ti,sysc-mask", &value, sizeof(cell_t)); | ||||
sc->ti_sysc_mask = value; | sc->ti_sysc_mask = value; | ||||
} | } | ||||
if (OF_hasprop(node, "ti,syss-mask")) { | if (OF_hasprop(node, "ti,syss-mask") == 1) { | ||||
OF_getencprop(node, "ti,syss-mask", &value, sizeof(cell_t)); | OF_getencprop(node, "ti,syss-mask", &value, sizeof(cell_t)); | ||||
sc->ti_syss_mask = value; | sc->ti_syss_mask = value; | ||||
} | } | ||||
if (OF_hasprop(node, "ti,sysc-delay-us")) { | if (OF_hasprop(node, "ti,sysc-delay-us") == 1) { | ||||
OF_getencprop(node, "ti,sysc-delay-us", &value, sizeof(cell_t)); | OF_getencprop(node, "ti,sysc-delay-us", &value, sizeof(cell_t)); | ||||
sc->ti_sysc_delay_us = value; | sc->ti_sysc_delay_us = value; | ||||
} | } | ||||
DPRINTF(sc->dev, "sysc_mask %x syss_mask %x delay_us %x\n", | DPRINTF(sc->dev, "sysc_mask %x syss_mask %x delay_us %x\n", | ||||
sc->ti_sysc_mask, sc->ti_syss_mask, sc->ti_sysc_delay_us); | sc->ti_sysc_mask, sc->ti_syss_mask, sc->ti_sysc_delay_us); | ||||
parse_idle(sc, "ti,sysc-midle", sc->ti_sysc_midle); | parse_idle(sc, "ti,sysc-midle", sc->ti_sysc_midle); | ||||
parse_idle(sc, "ti,sysc-sidle", sc->ti_sysc_sidle); | parse_idle(sc, "ti,sysc-sidle", sc->ti_sysc_sidle); | ||||
if (OF_hasprop(node, "ti,no-reset-on-init")) | if (OF_hasprop(node, "ti,no-reset-on-init") == 1) | ||||
sc->ti_no_reset_on_init = true; | sc->ti_no_reset_on_init = true; | ||||
else | else | ||||
sc->ti_no_reset_on_init = false; | sc->ti_no_reset_on_init = false; | ||||
if (OF_hasprop(node, "ti,no-idle-on-init")) | if (OF_hasprop(node, "ti,no-idle-on-init") == 1) | ||||
sc->ti_no_idle_on_init = true; | sc->ti_no_idle_on_init = true; | ||||
else | else | ||||
sc->ti_no_idle_on_init = false; | sc->ti_no_idle_on_init = false; | ||||
if (OF_hasprop(node, "ti,no-idle")) | if (OF_hasprop(node, "ti,no-idle") == 1) | ||||
sc->ti_no_idle = true; | sc->ti_no_idle = true; | ||||
else | else | ||||
sc->ti_no_idle = false; | sc->ti_no_idle = false; | ||||
DPRINTF(sc->dev, | DPRINTF(sc->dev, | ||||
"no-reset-on-init %d, no-idle-on-init %d, no-idle %d\n", | "no-reset-on-init %d, no-idle-on-init %d, no-idle %d\n", | ||||
sc->ti_no_reset_on_init, | sc->ti_no_reset_on_init, | ||||
sc->ti_no_idle_on_init, | sc->ti_no_idle_on_init, | ||||
sc->ti_no_idle); | sc->ti_no_idle); | ||||
if (OF_hasprop(node, "clocks")) { | if (OF_hasprop(node, "clocks") == 1) { | ||||
struct clock_cell_info cell_info; | int idx, clk_idx; | ||||
read_clock_cells(sc->dev, &cell_info); | const char *name = ofw_bus_get_name(dev); | ||||
free(cell_info.clock_cells, M_DEVBUF); | clk_t clkk; | ||||
free(cell_info.clock_cells_ncells, M_DEVBUF); | |||||
sc->num_clocks = cell_info.num_real_clocks; | DPRINTF(dev, "ti_sysc_attach bus name %s\n", | ||||
name); | |||||
err = clk_get_by_ofw_index(dev, node, 0, &clkk); | |||||
if (err == 0) { | |||||
/* TODO: support multiple clocks.... */ | |||||
struct clk_list *clkp; | |||||
clkp = malloc(sizeof(*clkp), M_DEVBUF, | |||||
M_WAITOK | M_ZERO); | |||||
err = clk_get_by_ofw_index(dev, node, 0, &clkp->clk); | |||||
DPRINTF(dev, "err %d clk_get_name: %s\n", | |||||
err, clk_get_name(clkk)); | |||||
TAILQ_INIT(&sc->clk_list); | TAILQ_INIT(&sc->clk_list); | ||||
sc->num_clocks = 1; //sysc_clock_table[idx].parent_cnt; | |||||
TAILQ_INSERT_TAIL(&sc->clk_list, clkp, next); | |||||
} else { | |||||
DPRINTF(dev, "USE LOOKUP TABLE\n"); | |||||
for (idx = 0; idx < nitems(sysc_clock_table); idx++) { | |||||
if (strcmp(sysc_clock_table[idx].node_name, name)==0) | |||||
break; | |||||
} | |||||
err = ti_sysc_attach_clocks(sc); | if (idx == nitems(sysc_clock_table)) | ||||
if (err) { | panic("Cant find clocks for node %s\n", name); | ||||
DPRINTF(sc->dev, "Failed to attach clocks\n"); | |||||
return (bus_generic_attach(sc->dev)); | DPRINTF(dev, "%s at sysc_clock_table[%d]\n", name, idx); | ||||
/* | |||||
* Found the correct place in the lookup table. | |||||
* Loop through and get the clocks | |||||
*/ | |||||
TAILQ_INIT(&sc->clk_list); | |||||
sc->num_clocks = sysc_clock_table[idx].parent_cnt; | |||||
clk_idx = 0; | |||||
for (; clk_idx < sysc_clock_table[idx].parent_cnt; clk_idx++) { | |||||
struct clk_list *clkp; | |||||
clkp = malloc(sizeof(*clkp), M_DEVBUF, | |||||
M_WAITOK | M_ZERO); | |||||
err = clk_get_by_name(dev, | |||||
sysc_clock_table[idx].parent_names[clk_idx], | |||||
&clkp->clk); | |||||
if (err != 0) | |||||
panic("Cant get clock %s err %d", | |||||
sysc_clock_table[idx].parent_names[clk_idx], | |||||
err); | |||||
TAILQ_INSERT_TAIL(&sc->clk_list, clkp, next); | |||||
} /* for */ | |||||
} /* else */ | |||||
} | } | ||||
} | |||||
err = ti_sysc_simplebus_attach_child(sc->dev); | err = ti_sysc_simplebus_attach_child(sc->dev); | ||||
if (err) { | if (err != 0) { | ||||
DPRINTF(sc->dev, "ti_sysc_simplebus_attach_child %d\n", | DPRINTF(sc->dev, "ti_sysc_simplebus_attach_child %d\n", | ||||
err); | err); | ||||
return (err); | return (err); | ||||
} | } | ||||
sc->attach_done = true; | |||||
return (bus_generic_attach(sc->dev)); | return (bus_generic_attach(sc->dev)); | ||||
} | } | ||||
static int | static int | ||||
ti_sysc_detach(device_t dev) | ti_sysc_detach(device_t dev) | ||||
{ | { | ||||
return (EBUSY); | return (EBUSY); | ||||
} | } | ||||
/* Bus interface */ | |||||
static void | |||||
ti_sysc_new_pass(device_t dev) | |||||
{ | |||||
struct ti_sysc_softc *sc; | |||||
int err; | |||||
phandle_t node; | |||||
sc = device_get_softc(dev); | |||||
if (sc->attach_done) { | |||||
bus_generic_new_pass(sc->dev); | |||||
return; | |||||
} | |||||
node = ofw_bus_get_node(sc->dev); | |||||
if (OF_hasprop(node, "clocks")) { | |||||
err = ti_sysc_attach_clocks(sc); | |||||
if (err) { | |||||
DPRINTF(sc->dev, "Failed to attach clocks\n"); | |||||
return; | |||||
} | |||||
} | |||||
err = ti_sysc_simplebus_attach_child(sc->dev); | |||||
if (err) { | |||||
DPRINTF(sc->dev, | |||||
"ti_sysc_simplebus_attach_child failed %d\n", err); | |||||
return; | |||||
} | |||||
sc->attach_done = true; | |||||
bus_generic_attach(sc->dev); | |||||
} | |||||
static device_method_t ti_sysc_methods[] = { | static device_method_t ti_sysc_methods[] = { | ||||
/* Device interface */ | /* Device interface */ | ||||
DEVMETHOD(device_probe, ti_sysc_probe), | DEVMETHOD(device_probe, ti_sysc_probe), | ||||
DEVMETHOD(device_attach, ti_sysc_attach), | DEVMETHOD(device_attach, ti_sysc_attach), | ||||
DEVMETHOD(device_detach, ti_sysc_detach), | DEVMETHOD(device_detach, ti_sysc_detach), | ||||
/* Bus interface */ | |||||
DEVMETHOD(bus_new_pass, ti_sysc_new_pass), | |||||
DEVMETHOD_END | DEVMETHOD_END | ||||
}; | }; | ||||
DEFINE_CLASS_1(ti_sysc, ti_sysc_driver, ti_sysc_methods, | DEFINE_CLASS_1(ti_sysc, ti_sysc_driver, ti_sysc_methods, | ||||
sizeof(struct ti_sysc_softc), simplebus_driver); | sizeof(struct ti_sysc_softc), simplebus_driver); | ||||
EARLY_DRIVER_MODULE(ti_sysc, simplebus, ti_sysc_driver, 0, 0, | EARLY_DRIVER_MODULE(ti_sysc, simplebus, ti_sysc_driver, 0, 0, | ||||
BUS_PASS_BUS + BUS_PASS_ORDER_FIRST); | BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); |