@@ -167,6 +167,58 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk)
return true;
}
+static bool bus_clk_data_offsets_valid(struct kona_clk *bcm_clk)
+{
+ struct bus_clk_data *bus;
+ struct bcm_clk_policy *policy;
+ struct bcm_clk_gate *gate;
+ struct bcm_clk_hyst *hyst;
+ const char *name;
+ u32 range;
+ u32 limit;
+
+ BUG_ON(bcm_clk->type != bcm_clk_bus);
+ bus = bcm_clk->u.bus;
+ name = bcm_clk->init_data.name;
+ range = bcm_clk->ccu->range;
+
+ limit = range - sizeof(u32);
+ limit = round_down(limit, sizeof(u32));
+
+ policy = &bus->policy;
+ if (policy_exists(policy)) {
+ if (policy->offset > limit) {
+ pr_err("%s: bad policy offset for %s (%u > %u)\n",
+ __func__, name, policy->offset, limit);
+ return false;
+ }
+ }
+
+ gate = &bus->gate;
+ hyst = &bus->hyst;
+ if (gate_exists(gate)) {
+ if (gate->offset > limit) {
+ pr_err("%s: bad gate offset for %s (%u > %u)\n",
+ __func__, name, gate->offset, limit);
+ return false;
+ }
+
+ if (hyst_exists(hyst)) {
+ if (hyst->offset > limit) {
+ pr_err("%s: bad hysteresis offset for %s "
+ "(%u > %u)\n", __func__,
+ name, hyst->offset, limit);
+ return false;
+ }
+ }
+ } else if (hyst_exists(hyst)) {
+ pr_err("%s: hysteresis but no gate for %s\n", __func__, name);
+ return false;
+ }
+
+ return true;
+}
+
/* A bit position must be less than the number of bits in a 32-bit register. */
static bool bit_posn_valid(u32 bit_posn, const char *field_name,
const char *clock_name)
@@ -481,9 +533,46 @@ peri_clk_data_valid(struct kona_clk *bcm_clk)
return kona_dividers_valid(bcm_clk);
}
+/* Determine whether the set of bus clock registers are valid. */
+static bool
+bus_clk_data_valid(struct kona_clk *bcm_clk)
+{
+ struct bus_clk_data *bus;
+ struct bcm_clk_policy *policy;
+ struct bcm_clk_gate *gate;
+ struct bcm_clk_hyst *hyst;
+ const char *name;
+
+ BUG_ON(bcm_clk->type != bcm_clk_bus);
+
+ if (!bus_clk_data_offsets_valid(bcm_clk))
+ return false;
+
+ bus = bcm_clk->u.bus;
+ name = bcm_clk->init_data.name;
+
+ policy = &bus->policy;
+ if (policy_exists(policy) && !policy_valid(policy, name))
+ return false;
+
+ gate = &bus->gate;
+ if (gate_exists(gate) && !gate_valid(gate, "gate", name))
+ return false;
+
+ hyst = &bus->hyst;
+ if (hyst_exists(hyst) && !hyst_valid(hyst, name))
+ return false;
+
+ return true;
+}
+
static bool kona_clk_valid(struct kona_clk *bcm_clk)
{
switch (bcm_clk->type) {
+ case bcm_clk_bus:
+ if (!bus_clk_data_valid(bcm_clk))
+ return false;
+ break;
case bcm_clk_peri:
if (!peri_clk_data_valid(bcm_clk))
return false;
@@ -656,6 +745,14 @@ static void peri_clk_teardown(struct peri_clk_data *data,
clk_sel_teardown(&data->sel, init_data);
}
+static void bus_clk_teardown(struct bus_clk_data *data,
+ struct clk_init_data *init_data)
+{
+ init_data->num_parents = 0;
+ kfree(init_data->parent_names);
+ init_data->parent_names = NULL;
+}
+
/*
* Caller is responsible for freeing the parent_names[] and
* parent_sel[] arrays in the peripheral clock's "data" structure
@@ -670,9 +767,23 @@ peri_clk_setup(struct peri_clk_data *data, struct clk_init_data *init_data)
return clk_sel_setup(data->clocks, &data->sel, init_data);
}
+static int
+bus_clk_setup(struct bus_clk_data *data, struct clk_init_data *init_data)
+{
+ init_data->flags = CLK_IGNORE_UNUSED;
+
+ init_data->parent_names = NULL;
+ init_data->num_parents = 0;
+
+ return 0;
+}
+
static void bcm_clk_teardown(struct kona_clk *bcm_clk)
{
switch (bcm_clk->type) {
+ case bcm_clk_bus:
+ bus_clk_teardown(bcm_clk->u.data, &bcm_clk->init_data);
+ break;
case bcm_clk_peri:
peri_clk_teardown(bcm_clk->u.data, &bcm_clk->init_data);
break;
@@ -702,6 +813,11 @@ static int kona_clk_setup(struct kona_clk *bcm_clk)
struct clk_init_data *init_data = &bcm_clk->init_data;
switch (bcm_clk->type) {
+ case bcm_clk_bus:
+ ret = bus_clk_setup(bcm_clk->u.data, init_data);
+ if (ret)
+ return ret;
+ break;
case bcm_clk_peri:
ret = peri_clk_setup(bcm_clk->u.data, init_data);
if (ret)
@@ -961,7 +961,7 @@ static int selector_write(struct ccu_data *ccu, struct bcm_clk_gate *gate,
return ret;
}
-/* Clock operations */
+/* Peripheral clock operations */
static int kona_peri_clk_enable(struct clk_hw *hw)
{
@@ -1233,9 +1233,69 @@ static bool __peri_clk_init(struct kona_clk *bcm_clk)
return true;
}
+/* Bus clock operations */
+
+static int kona_bus_clk_enable(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct bcm_clk_gate *gate = &bcm_clk->u.bus->gate;
+
+ return clk_gate(bcm_clk->ccu, bcm_clk->init_data.name, gate, true);
+}
+
+static void kona_bus_clk_disable(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct bcm_clk_gate *gate = &bcm_clk->u.bus->gate;
+
+ (void)clk_gate(bcm_clk->ccu, bcm_clk->init_data.name, gate, false);
+}
+
+static int kona_bus_clk_is_enabled(struct clk_hw *hw)
+{
+ struct kona_clk *bcm_clk = to_kona_clk(hw);
+ struct bcm_clk_gate *gate = &bcm_clk->u.bus->gate;
+
+ return is_clk_gate_enabled(bcm_clk->ccu, gate) ? 1 : 0;
+}
+
+struct clk_ops kona_bus_clk_ops = {
+ .enable = kona_bus_clk_enable,
+ .disable = kona_bus_clk_disable,
+ .is_enabled = kona_bus_clk_is_enabled,
+};
+
+/* Put a bus clock into its initial state */
+static bool __bus_clk_init(struct kona_clk *bcm_clk)
+{
+ struct ccu_data *ccu = bcm_clk->ccu;
+ struct bus_clk_data *bus = bcm_clk->u.bus;
+ const char *name = bcm_clk->init_data.name;
+
+ BUG_ON(bcm_clk->type != bcm_clk_bus);
+
+ if (!policy_init(ccu, &bus->policy)) {
+ pr_err("%s: error initializing policy for %s\n",
+ __func__, name);
+ return false;
+ }
+ if (!gate_init(ccu, &bus->gate)) {
+ pr_err("%s: error initializing gate for %s\n", __func__, name);
+ return false;
+ }
+ if (!hyst_init(ccu, &bus->hyst)) {
+ pr_err("%s: error initializing hyst for %s\n", __func__, name);
+ return false;
+ }
+
+ return true;
+}
+
static bool __kona_clk_init(struct kona_clk *bcm_clk)
{
switch (bcm_clk->type) {
+ case bcm_clk_bus:
+ return __bus_clk_init(bcm_clk);
case bcm_clk_peri:
return __peri_clk_init(bcm_clk);
default:
@@ -390,6 +390,14 @@ struct peri_clk_data {
struct bcm_clk_sel sel;
const char *clocks[]; /* must be last; use CLOCKS() to declare */
};
+
+struct bus_clk_data {
+ struct bcm_clk_policy policy;
+ struct bcm_clk_gate gate;
+ struct bcm_clk_hyst hyst;
+ const char *clocks[]; /* must be last; use CLOCKS() to declare */
+};
+
#define CLOCKS(...) { __VA_ARGS__, NULL, }
#define NO_CLOCKS { NULL, } /* Must use of no parent clocks */
@@ -401,6 +409,7 @@ struct kona_clk {
union {
void *data;
struct peri_clk_data *peri;
+ struct bus_clk_data *bus;
} u;
};
#define to_kona_clk(_hw) \
@@ -488,6 +497,7 @@ struct ccu_data {
/* Exported globals */
extern struct clk_ops kona_peri_clk_ops;
+extern struct clk_ops kona_bus_clk_ops;
/* Externally visible functions */