[PATCH v6 5/7] clk: bcm281xx: add bus clock support

From: Alex Elder
Date: Mon Jun 16 2014 - 09:40:59 EST


Add bus clock support. A bus clock has a subset of the components
present in a peripheral clock (again, all optional): a gate; CCU
policy management bits; and if needed, bits to control hysteresis.

Signed-off-by: Alex Elder <elder@xxxxxxxxxx>
---
drivers/clk/bcm/clk-kona-setup.c | 96 ++++++++++++++++++++++++++++++++++++++--
drivers/clk/bcm/clk-kona.c | 61 +++++++++++++++++++++++++
drivers/clk/bcm/clk-kona.h | 8 ++++
3 files changed, 162 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c
index 317f7dd..1dae591 100644
--- a/drivers/clk/bcm/clk-kona-setup.c
+++ b/drivers/clk/bcm/clk-kona-setup.c
@@ -76,6 +76,56 @@ static bool clk_requires_trigger(struct kona_clk *bcm_clk)
return divider_exists(div) && !divider_is_fixed(div);
}

+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 limit;
+
+ BUG_ON(bcm_clk->type != bcm_clk_bus);
+ bus = bcm_clk->u.bus;
+ name = bcm_clk->init_data.name;
+
+ limit = bcm_clk->ccu->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;
+}
+
static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk)
{
struct peri_clk_data *peri;
@@ -86,15 +136,13 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk)
struct bcm_clk_sel *sel;
struct bcm_clk_trig *trig;
const char *name;
- u32 range;
u32 limit;

BUG_ON(bcm_clk->type != bcm_clk_peri);
peri = bcm_clk->u.peri;
name = bcm_clk->init_data.name;
- range = bcm_clk->ccu->range;

- limit = range - sizeof(u32);
+ limit = bcm_clk->ccu->range - sizeof(u32);
limit = round_down(limit, sizeof(u32));

policy = &peri->policy;
@@ -397,6 +445,23 @@ static bool trig_valid(struct bcm_clk_trig *trig, const char *field_name,
return bit_posn_valid(trig->bit, field_name, clock_name);
}

+/* Determine whether the set of bus clock registers are valid. */
+static bool
+bus_clk_data_valid(struct kona_clk *bcm_clk)
+{
+ struct bcm_clk_gate *gate;
+
+ BUG_ON(bcm_clk->type != bcm_clk_bus);
+ if (!bus_clk_data_offsets_valid(bcm_clk))
+ return false;
+
+ gate = &bcm_clk->u.bus->gate;
+ if (!gate_exists(gate))
+ return true;
+
+ return gate_valid(gate, "gate", bcm_clk->init_data.name);
+}
+
/* Determine whether the set of peripheral clock registers are valid. */
static bool
peri_clk_data_valid(struct kona_clk *bcm_clk)
@@ -494,6 +559,10 @@ peri_clk_data_valid(struct kona_clk *bcm_clk)
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;
@@ -664,6 +733,20 @@ static void clk_sel_teardown(struct bcm_clk_sel *sel,
init_data->parent_names = NULL;
}

+static void bus_clk_teardown(struct bus_clk_data *data,
+ struct clk_init_data *init_data)
+{
+ /* Nothing to do */
+}
+
+static int
+bus_clk_setup(struct bus_clk_data *data, struct clk_init_data *init_data)
+{
+ init_data->flags = CLK_IGNORE_UNUSED;
+
+ return 0;
+}
+
static void peri_clk_teardown(struct peri_clk_data *data,
struct clk_init_data *init_data)
{
@@ -687,6 +770,9 @@ peri_clk_setup(struct peri_clk_data *data, struct clk_init_data *init_data)
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;
@@ -722,6 +808,10 @@ struct clk *kona_clk_setup(struct kona_clk *bcm_clk)
struct clk *clk = NULL;

switch (bcm_clk->type) {
+ case bcm_clk_bus:
+ if (bus_clk_setup(bcm_clk->u.data, init_data))
+ return NULL;
+ break;
case bcm_clk_peri:
if (peri_clk_setup(bcm_clk->u.data, init_data))
return NULL;
diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c
index 51b4edb..a938b15 100644
--- a/drivers/clk/bcm/clk-kona.c
+++ b/drivers/clk/bcm/clk-kona.c
@@ -984,6 +984,31 @@ static int selector_write(struct ccu_data *ccu, struct bcm_clk_gate *gate,
return ret;
}

+/* 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;
+}
+
/* Put a peripheral clock into its initial state */
static bool __peri_clk_init(struct kona_clk *bcm_clk)
{
@@ -1095,6 +1120,10 @@ static int kona_clk_prepare(struct clk_hw *hw)
__ccu_write_enable(ccu);

switch (bcm_clk->type) {
+ case bcm_clk_bus:
+ if (!__bus_clk_init(bcm_clk))
+ ret = -EINVAL;
+ break;
case bcm_clk_peri:
if (!__peri_clk_init(bcm_clk))
ret = -EINVAL;
@@ -1143,6 +1172,38 @@ static void kona_clk_unprepare(struct clk_hw *hw)
kona_prereq_disable_unprepare(bcm_clk);
}

+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 = {
+ .prepare = kona_clk_prepare,
+ .unprepare = kona_clk_unprepare,
+ .enable = kona_bus_clk_enable,
+ .disable = kona_bus_clk_disable,
+ .is_enabled = kona_bus_clk_is_enabled,
+};
+
static int kona_peri_clk_enable(struct clk_hw *hw)
{
struct kona_clk *bcm_clk = to_kona_clk(hw);
diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h
index 98b6fa1..46ea11f 100644
--- a/drivers/clk/bcm/clk-kona.h
+++ b/drivers/clk/bcm/clk-kona.h
@@ -387,6 +387,12 @@ struct bcm_clk_trig {
.flags = FLAG(TRIG, EXISTS), \
}

+struct bus_clk_data {
+ struct bcm_clk_policy policy;
+ struct bcm_clk_gate gate;
+ struct bcm_clk_hyst hyst;
+};
+
struct peri_clk_data {
struct bcm_clk_policy policy;
struct bcm_clk_gate gate;
@@ -413,6 +419,7 @@ struct kona_clk {
} prereq;
union {
void *data;
+ struct bus_clk_data *bus;
struct peri_clk_data *peri;
} u;
};
@@ -520,6 +527,7 @@ struct ccu_data {

/* Exported globals */

+extern struct clk_ops kona_bus_clk_ops;
extern struct clk_ops kona_peri_clk_ops;

/* Externally visible functions */
--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/