From bf88ab3454e2b8a6c06b73412cfe8d9dffee4ffa Mon Sep 17 00:00:00 2001 From: Artur Weber Date: Sat, 7 Jan 2023 18:02:48 +0100 Subject: [PATCH] (wip) clk: bcm-kona: implement full CCU initialization sequence *Still* doesn't fix the clock issues, actually, now it prints a message about being unable to start the policy engine... Signed-off-by: Artur Weber --- drivers/clk/bcm/clk-bcm21664.c | 85 +++++++++++++++++++++++++ drivers/clk/bcm/clk-kona.c | 110 +++++++++++++++++++++++++++++++++ drivers/clk/bcm/clk-kona.h | 75 ++++++++++++++++++++++ 3 files changed, 270 insertions(+) diff --git a/drivers/clk/bcm/clk-bcm21664.c b/drivers/clk/bcm/clk-bcm21664.c index b3bfdcb33f6d7..afe2519130b49 100644 --- a/drivers/clk/bcm/clk-bcm21664.c +++ b/drivers/clk/bcm/clk-bcm21664.c @@ -55,6 +55,33 @@ static struct ccu_data aon_ccu_data = { .policy = { .enable = CCU_LVM_EN(0x0034, 0), .control = CCU_POLICY_CTL(0x000c, 0, 1, 2), + .mask = CCU_POLICY_MASK(0x0010, 0), + }, + .voltage = { + CCU_VOLTAGE_OFFSET(0x0040, 0x0044), + .voltage_table = { + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + }, + .voltage_table_len = 5, + }, + .peri_volt = { + .offset = 0x0030, + .peri_volt_table = { + CCU_PERI_VOLT_NORMAL, + CCU_PERI_VOLT_HIGH, + }, + .peri_volt_table_len = 2, + }, + .freq_policy = { + .offset = 0x0008, + .freq_policy_table = { + 1, 1, 2, 2 /* ECO, ECO, NORMAL, NORMAL */ + }, + .freq_policy_table_len = 4, }, .kona_clks = { [BCM21664_AON_CCU_HUB_TIMER] = @@ -140,6 +167,36 @@ static struct ccu_data master_ccu_data = { .policy = { .enable = CCU_LVM_EN(0x0034, 0), .control = CCU_POLICY_CTL(0x000c, 0, 1, 2), + .mask = CCU_POLICY_MASK(0x0010, 0), + }, + .voltage = { + CCU_VOLTAGE_OFFSET(0x0040, 0x0044), + .voltage_table = { + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + }, + .voltage_table_len = 8, + }, + .peri_volt = { + .offset = 0x0030, + .peri_volt_table = { + CCU_PERI_VOLT_NORMAL, + CCU_PERI_VOLT_HIGH, + }, + .peri_volt_table_len = 2, + }, + .freq_policy = { + .offset = 0x0008, + .freq_policy_table = { + 2, 2, 3, 3 /* ECO, ECO, NORMAL, NORMAL */ + }, + .freq_policy_table_len = 4, }, .kona_clks = { [BCM21664_MASTER_CCU_SDIO1] = @@ -243,6 +300,34 @@ static struct ccu_data slave_ccu_data = { .policy = { .enable = CCU_LVM_EN(0x0034, 0), .control = CCU_POLICY_CTL(0x000c, 0, 1, 2), + .mask = CCU_POLICY_MASK(0x0010, 0), + }, + .voltage = { + CCU_VOLTAGE_OFFSET(0x0040, 0x0044), + .voltage_table = { + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + CCU_VOLTAGE_ECO, + }, + .voltage_table_len = 6, + }, + .peri_volt = { + .offset = 0x0030, + .peri_volt_table = { + CCU_PERI_VOLT_NORMAL, + CCU_PERI_VOLT_HIGH, + }, + .peri_volt_table_len = 2, + }, + .freq_policy = { + .offset = 0x0008, + .freq_policy_table = { + 1, 1, 3, 3 /* ECO, ECO, NORMAL, NORMAL */ + }, + .freq_policy_table_len = 4, }, .kona_clks = { [BCM21664_SLAVE_CCU_UARTB] = diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index ec5749e301ba8..8c8c61f8e4e86 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -961,6 +961,75 @@ static int selector_write(struct ccu_data *ccu, struct bcm_clk_gate *gate, return ret; } +/* CCU operations */ + +static void kona_ccu_set_voltage(struct ccu_data *ccu, int voltage_reg_num, + u8 voltage_policy_id) +{ + unsigned long flags; + u32 offset; + u32 value; + u8 shift; + + flags = ccu_lock(ccu); + + if (voltage_reg_num <= 3) { + shift = voltage_reg_num << 3; + offset = ccu->voltage.offset1; + } else if ((voltage_reg_num <= 7) && ccu->voltage.offset2) { + shift = (voltage_reg_num - 4) << 3; + offset = ccu->voltage.offset2; + } else { + BUG(); + } + + value = __ccu_read(ccu, offset); + value = (value & ~(0xF << shift)) | + ((voltage_policy_id & 0xF) << shift); + + __ccu_write(ccu, offset, value); + + ccu_unlock(ccu, flags); +} + +static void kona_ccu_set_peri_voltage(struct ccu_data *ccu, u8 peri_volt_reg_num, + u8 peri_volt_policy_id) +{ + unsigned long flags; + u32 value; + u8 shift; + + flags = ccu_lock(ccu); + + shift = peri_volt_reg_num << 3; + value = __ccu_read(ccu, ccu->peri_volt.offset); + value = (value & ~(0xF << shift)) | + ((peri_volt_policy_id & 0xF) << shift); + + __ccu_write(ccu, ccu->peri_volt.offset, value); + + ccu_unlock(ccu, flags); +} + +static void kona_ccu_set_freq_policy(struct ccu_data *ccu, u8 freq_policy_reg_num, + u8 freq_policy_policy_id) +{ + unsigned long flags; + u32 value; + u8 shift; + + flags = ccu_lock(ccu); + + shift = freq_policy_reg_num << 3; + value = __ccu_read(ccu, ccu->freq_policy.offset); + value = (value & ~(0x7 << shift)) | + (freq_policy_policy_id << shift); + + __ccu_write(ccu, ccu->freq_policy.offset, value); + + ccu_unlock(ccu, flags); +} + /* Clock operations */ static int kona_peri_clk_enable(struct clk_hw *hw) @@ -1254,7 +1323,48 @@ bool __init kona_ccu_init(struct ccu_data *ccu) flags = ccu_lock(ccu); __ccu_write_enable(ccu); + if (!__ccu_policy_engine_stop(ccu)) + pr_err("Could not stop policy engine"); + + /* Enable all policies */ + if (ccu_policy_exists(&ccu->policy)) { + for (which = 0; which < CCU_POLICY_MAX; which++) { + if (ccu->policy.mask.mask1_offset != 0) + __ccu_write(ccu, ccu->policy.mask.mask1_offset + (4 * which), + CCU_POLICY_ENABLE_ALL); + + if (ccu->policy.mask.mask2_offset != 0) + __ccu_write(ccu, ccu->policy.mask.mask2_offset + (4 * which), + CCU_POLICY_ENABLE_ALL); + } + } + + /* Set voltage from voltage tables */ + if (ccu_voltage_exists(&ccu->voltage)) { + for (which = 0; which < ccu->voltage.voltage_table_len; which++) { + kona_ccu_set_voltage(ccu, which, ccu->voltage.voltage_table[which]); + } + } + + /* Set peripheral voltage from voltage tables */ + if (ccu_peri_volt_exists(&ccu->peri_volt)) { + for (which = 0; which < ccu->peri_volt.peri_volt_table_len; which++) { + kona_ccu_set_peri_voltage(ccu, which, + ccu->peri_volt.peri_volt_table[which]); + } + } + + /* Set peripheral voltage from voltage tables */ + if (ccu_freq_policy_exists(&ccu->freq_policy)) { + for (which = 0; which < ccu->freq_policy.freq_policy_table_len; which++) { + kona_ccu_set_freq_policy(ccu, which, + ccu->freq_policy.freq_policy_table[which]); + } + } + + __ccu_policy_engine_start(ccu, true); + /* Initialize clocks */ for (which = 0; which < ccu->clk_num; which++) { struct kona_clk *bcm_clk = &kona_clks[which]; diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h index e09655024ac2a..d318626ea7546 100644 --- a/drivers/clk/bcm/clk-kona.h +++ b/drivers/clk/bcm/clk-kona.h @@ -38,6 +38,9 @@ /* CCU field state tests */ #define ccu_policy_exists(ccu_policy) ((ccu_policy)->enable.offset != 0) +#define ccu_voltage_exists(ccu_voltage) ((ccu_voltage)->offset1 != 0) +#define ccu_peri_volt_exists(ccu_peri_volt) ((ccu_peri_volt)->offset != 0) +#define ccu_freq_policy_exists(ccu_freq_policy) ((ccu_freq_policy)->offset != 0) /* Clock field state tests */ @@ -453,9 +456,78 @@ struct bcm_policy_ctl { .atl_bit = (_atl_bit), \ } + +/* CCU policy masks */ +enum { + CCU_POLICY_0, + CCU_POLICY_1, + CCU_POLICY_2, + CCU_POLICY_3, + CCU_POLICY_MAX, +}; + +#define CCU_POLICY_ENABLE_ALL 0x7FFFFFFF + +struct bcm_policy_mask { + u32 mask1_offset; + u32 mask2_offset; +}; + +/* Policy mask initialization macro */ +#define CCU_POLICY_MASK(_mask1_offset, _mask2_offset) \ + { \ + .mask1_offset = (_mask1_offset), \ + .mask2_offset = (_mask2_offset), \ + } + + struct ccu_policy { struct bcm_lvm_en enable; struct bcm_policy_ctl control; + struct bcm_policy_mask mask; +}; + +/* CCU voltage policy IDs */ +#define CCU_VOLATGE_OFF 0x0 +#define CCU_VOLTAGE_RETN 0x1 +#define CCU_VOLTAGE_WAKEUP 0x2 + +#define CCU_VOLTAGE_ECO 0x9 +#define CCU_VOLTAGE_NORMAL 0xB +#define CCU_VOLTAGE_TURBO 0xD +#define CCU_VOLTAGE_SUPER_TURBO 0xF + +#define CCU_VOLTAGE_A9_ECO 0x8 +#define CCU_VOLTAGE_A9_NORMAL 0xA +#define CCU_VOLTAGE_A9_TURBO 0xC +#define CCU_VOLTAGE_A9_SUPER_TURBO 0xE + +#define CCU_VOLTAGE_OFFSET(_offset1, _offset2) \ + .offset1 = _offset1, \ + .offset2 = _offset2 + +struct ccu_voltage { + u32 offset1; /* 0-3 */ + u32 offset2; /* 4-7 */ + u32 voltage_table[8]; /* Array of voltage IDs */ + size_t voltage_table_len; +}; + +/* CCU peripheral voltage policy IDs */ +#define CCU_PERI_VOLT_NORMAL 0 +#define CCU_PERI_VOLT_HIGH 0 + +struct ccu_peri_volt { + u32 offset; + u32 peri_volt_table[2]; + size_t peri_volt_table_len; +}; + +/* CCU frequency policy data */ +struct ccu_freq_policy { + u32 offset; + u32 freq_policy_table[4]; + size_t freq_policy_table_len; }; /* @@ -472,6 +544,9 @@ struct ccu_data { spinlock_t lock; /* serialization lock */ bool write_enabled; /* write access is currently enabled */ struct ccu_policy policy; + struct ccu_voltage voltage; + struct ccu_peri_volt peri_volt; + struct ccu_freq_policy freq_policy; struct device_node *node; size_t clk_num; const char *name;