Add bcm281xx architecture support code including a clock framework and chip reset. Define register block base addresses for the bcm281xx architecture and create an empty gpio header file required when CONFIG_CMD_GPIO is set. Signed-off-by: Darwin Rambo <drambo@broadcom.com> Reviewed-by: Steve Rae <srae@broadcom.com> Reviewed-by: Tim Kryger <tkryger@linaro.org>master
parent
b3134fce89
commit
989ce04999
@ -0,0 +1,11 @@ |
||||
#
|
||||
# Copyright 2013 Broadcom Corporation.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
obj-y += reset.o
|
||||
obj-y += clk-core.o
|
||||
obj-y += clk-bcm281xx.o
|
||||
obj-y += clk-sdio.o
|
||||
obj-y += clk-bsc.o
|
@ -0,0 +1,523 @@ |
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
/*
|
||||
* |
||||
* bcm281xx-specific clock tables |
||||
* |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <asm/io.h> |
||||
#include <asm/errno.h> |
||||
#include <asm/arch/sysmap.h> |
||||
#include <asm/kona-common/clk.h> |
||||
#include "clk-core.h" |
||||
|
||||
#define CLOCK_1K 1000 |
||||
#define CLOCK_1M (CLOCK_1K * 1000) |
||||
|
||||
/* declare a reference clock */ |
||||
#define DECLARE_REF_CLK(clk_name, clk_parent, clk_rate, clk_div) \ |
||||
static struct refclk clk_name = { \
|
||||
.clk = { \
|
||||
.name = #clk_name, \
|
||||
.parent = clk_parent, \
|
||||
.rate = clk_rate, \
|
||||
.div = clk_div, \
|
||||
.ops = &ref_clk_ops, \
|
||||
}, \
|
||||
} |
||||
|
||||
/*
|
||||
* Reference clocks |
||||
*/ |
||||
|
||||
/* Declare a list of reference clocks */ |
||||
DECLARE_REF_CLK(ref_crystal, 0, 26 * CLOCK_1M, 1); |
||||
DECLARE_REF_CLK(var_96m, 0, 96 * CLOCK_1M, 1); |
||||
DECLARE_REF_CLK(ref_96m, 0, 96 * CLOCK_1M, 1); |
||||
DECLARE_REF_CLK(ref_312m, 0, 312 * CLOCK_1M, 0); |
||||
DECLARE_REF_CLK(ref_104m, &ref_312m.clk, 104 * CLOCK_1M, 3); |
||||
DECLARE_REF_CLK(ref_52m, &ref_104m.clk, 52 * CLOCK_1M, 2); |
||||
DECLARE_REF_CLK(ref_13m, &ref_52m.clk, 13 * CLOCK_1M, 4); |
||||
DECLARE_REF_CLK(var_312m, 0, 312 * CLOCK_1M, 0); |
||||
DECLARE_REF_CLK(var_104m, &var_312m.clk, 104 * CLOCK_1M, 3); |
||||
DECLARE_REF_CLK(var_52m, &var_104m.clk, 52 * CLOCK_1M, 2); |
||||
DECLARE_REF_CLK(var_13m, &var_52m.clk, 13 * CLOCK_1M, 4); |
||||
|
||||
struct refclk_lkup { |
||||
struct refclk *procclk; |
||||
const char *name; |
||||
}; |
||||
|
||||
/* Lookup table for string to clk tranlation */ |
||||
#define MKSTR(x) {&x, #x} |
||||
static struct refclk_lkup refclk_str_tbl[] = { |
||||
MKSTR(ref_crystal), MKSTR(var_96m), MKSTR(ref_96m), |
||||
MKSTR(ref_312m), MKSTR(ref_104m), MKSTR(ref_52m), |
||||
MKSTR(ref_13m), MKSTR(var_312m), MKSTR(var_104m), |
||||
MKSTR(var_52m), MKSTR(var_13m), |
||||
}; |
||||
|
||||
int refclk_entries = sizeof(refclk_str_tbl)/sizeof(refclk_str_tbl[0]); |
||||
|
||||
/* convert ref clock string to clock structure pointer */ |
||||
struct refclk *refclk_str_to_clk(const char *name) |
||||
{ |
||||
int i; |
||||
struct refclk_lkup *tblp = refclk_str_tbl; |
||||
for (i = 0; i < refclk_entries; i++, tblp++) { |
||||
if (!(strcmp(name, tblp->name))) |
||||
return tblp->procclk; |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
/* frequency tables indexed by freq_id */ |
||||
unsigned long master_axi_freq_tbl[8] = { |
||||
26 * CLOCK_1M, |
||||
52 * CLOCK_1M, |
||||
104 * CLOCK_1M, |
||||
156 * CLOCK_1M, |
||||
156 * CLOCK_1M, |
||||
208 * CLOCK_1M, |
||||
312 * CLOCK_1M, |
||||
312 * CLOCK_1M |
||||
}; |
||||
|
||||
unsigned long master_ahb_freq_tbl[8] = { |
||||
26 * CLOCK_1M, |
||||
52 * CLOCK_1M, |
||||
52 * CLOCK_1M, |
||||
52 * CLOCK_1M, |
||||
78 * CLOCK_1M, |
||||
104 * CLOCK_1M, |
||||
104 * CLOCK_1M, |
||||
156 * CLOCK_1M |
||||
}; |
||||
|
||||
unsigned long slave_axi_freq_tbl[8] = { |
||||
26 * CLOCK_1M, |
||||
52 * CLOCK_1M, |
||||
78 * CLOCK_1M, |
||||
104 * CLOCK_1M, |
||||
156 * CLOCK_1M, |
||||
156 * CLOCK_1M |
||||
}; |
||||
|
||||
unsigned long slave_apb_freq_tbl[8] = { |
||||
26 * CLOCK_1M, |
||||
26 * CLOCK_1M, |
||||
39 * CLOCK_1M, |
||||
52 * CLOCK_1M, |
||||
52 * CLOCK_1M, |
||||
78 * CLOCK_1M |
||||
}; |
||||
|
||||
static struct bus_clk_data bsc1_apb_data = { |
||||
.gate = HW_SW_GATE_AUTO(0x0458, 16, 0, 1), |
||||
}; |
||||
|
||||
static struct bus_clk_data bsc2_apb_data = { |
||||
.gate = HW_SW_GATE_AUTO(0x045c, 16, 0, 1), |
||||
}; |
||||
|
||||
static struct bus_clk_data bsc3_apb_data = { |
||||
.gate = HW_SW_GATE_AUTO(0x0484, 16, 0, 1), |
||||
}; |
||||
|
||||
/* * Master CCU clocks */ |
||||
static struct peri_clk_data sdio1_data = { |
||||
.gate = HW_SW_GATE(0x0358, 18, 2, 3), |
||||
.clocks = CLOCKS("ref_crystal", |
||||
"var_52m", |
||||
"ref_52m", |
||||
"var_96m", |
||||
"ref_96m"), |
||||
.sel = SELECTOR(0x0a28, 0, 3), |
||||
.div = DIVIDER(0x0a28, 4, 14), |
||||
.trig = TRIGGER(0x0afc, 9), |
||||
}; |
||||
|
||||
static struct peri_clk_data sdio2_data = { |
||||
.gate = HW_SW_GATE(0x035c, 18, 2, 3), |
||||
.clocks = CLOCKS("ref_crystal", |
||||
"var_52m", |
||||
"ref_52m", |
||||
"var_96m", |
||||
"ref_96m"), |
||||
.sel = SELECTOR(0x0a2c, 0, 3), |
||||
.div = DIVIDER(0x0a2c, 4, 14), |
||||
.trig = TRIGGER(0x0afc, 10), |
||||
}; |
||||
|
||||
static struct peri_clk_data sdio3_data = { |
||||
.gate = HW_SW_GATE(0x0364, 18, 2, 3), |
||||
.clocks = CLOCKS("ref_crystal", |
||||
"var_52m", |
||||
"ref_52m", |
||||
"var_96m", |
||||
"ref_96m"), |
||||
.sel = SELECTOR(0x0a34, 0, 3), |
||||
.div = DIVIDER(0x0a34, 4, 14), |
||||
.trig = TRIGGER(0x0afc, 12), |
||||
}; |
||||
|
||||
static struct peri_clk_data sdio4_data = { |
||||
.gate = HW_SW_GATE(0x0360, 18, 2, 3), |
||||
.clocks = CLOCKS("ref_crystal", |
||||
"var_52m", |
||||
"ref_52m", |
||||
"var_96m", |
||||
"ref_96m"), |
||||
.sel = SELECTOR(0x0a30, 0, 3), |
||||
.div = DIVIDER(0x0a30, 4, 14), |
||||
.trig = TRIGGER(0x0afc, 11), |
||||
}; |
||||
|
||||
static struct peri_clk_data sdio1_sleep_data = { |
||||
.clocks = CLOCKS("ref_32k"), |
||||
.gate = SW_ONLY_GATE(0x0358, 20, 4), |
||||
}; |
||||
|
||||
static struct peri_clk_data sdio2_sleep_data = { |
||||
.clocks = CLOCKS("ref_32k"), |
||||
.gate = SW_ONLY_GATE(0x035c, 20, 4), |
||||
}; |
||||
|
||||
static struct peri_clk_data sdio3_sleep_data = { |
||||
.clocks = CLOCKS("ref_32k"), |
||||
.gate = SW_ONLY_GATE(0x0364, 20, 4), |
||||
}; |
||||
|
||||
static struct peri_clk_data sdio4_sleep_data = { |
||||
.clocks = CLOCKS("ref_32k"), |
||||
.gate = SW_ONLY_GATE(0x0360, 20, 4), |
||||
}; |
||||
|
||||
static struct bus_clk_data sdio1_ahb_data = { |
||||
.gate = HW_SW_GATE_AUTO(0x0358, 16, 0, 1), |
||||
}; |
||||
|
||||
static struct bus_clk_data sdio2_ahb_data = { |
||||
.gate = HW_SW_GATE_AUTO(0x035c, 16, 0, 1), |
||||
}; |
||||
|
||||
static struct bus_clk_data sdio3_ahb_data = { |
||||
.gate = HW_SW_GATE_AUTO(0x0364, 16, 0, 1), |
||||
}; |
||||
|
||||
static struct bus_clk_data sdio4_ahb_data = { |
||||
.gate = HW_SW_GATE_AUTO(0x0360, 16, 0, 1), |
||||
}; |
||||
|
||||
/* * Slave CCU clocks */ |
||||
static struct peri_clk_data bsc1_data = { |
||||
.gate = HW_SW_GATE(0x0458, 18, 2, 3), |
||||
.clocks = CLOCKS("ref_crystal", |
||||
"var_104m", |
||||
"ref_104m", |
||||
"var_13m", |
||||
"ref_13m"), |
||||
.sel = SELECTOR(0x0a64, 0, 3), |
||||
.trig = TRIGGER(0x0afc, 23), |
||||
}; |
||||
|
||||
static struct peri_clk_data bsc2_data = { |
||||
.gate = HW_SW_GATE(0x045c, 18, 2, 3), |
||||
.clocks = CLOCKS("ref_crystal", |
||||
"var_104m", |
||||
"ref_104m", |
||||
"var_13m", |
||||
"ref_13m"), |
||||
.sel = SELECTOR(0x0a68, 0, 3), |
||||
.trig = TRIGGER(0x0afc, 24), |
||||
}; |
||||
|
||||
static struct peri_clk_data bsc3_data = { |
||||
.gate = HW_SW_GATE(0x0484, 18, 2, 3), |
||||
.clocks = CLOCKS("ref_crystal", |
||||
"var_104m", |
||||
"ref_104m", |
||||
"var_13m", |
||||
"ref_13m"), |
||||
.sel = SELECTOR(0x0a84, 0, 3), |
||||
.trig = TRIGGER(0x0b00, 2), |
||||
}; |
||||
|
||||
/*
|
||||
* CCU clocks |
||||
*/ |
||||
|
||||
static struct ccu_clock kpm_ccu_clk = { |
||||
.clk = { |
||||
.name = "kpm_ccu_clk", |
||||
.ops = &ccu_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR, |
||||
}, |
||||
.num_policy_masks = 1, |
||||
.policy_freq_offset = 0x00000008, |
||||
.freq_bit_shift = 8, |
||||
.policy_ctl_offset = 0x0000000c, |
||||
.policy0_mask_offset = 0x00000010, |
||||
.policy1_mask_offset = 0x00000014, |
||||
.policy2_mask_offset = 0x00000018, |
||||
.policy3_mask_offset = 0x0000001c, |
||||
.lvm_en_offset = 0x00000034, |
||||
.freq_id = 2, |
||||
.freq_tbl = master_axi_freq_tbl, |
||||
}; |
||||
|
||||
static struct ccu_clock kps_ccu_clk = { |
||||
.clk = { |
||||
.name = "kps_ccu_clk", |
||||
.ops = &ccu_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR, |
||||
}, |
||||
.num_policy_masks = 2, |
||||
.policy_freq_offset = 0x00000008, |
||||
.freq_bit_shift = 8, |
||||
.policy_ctl_offset = 0x0000000c, |
||||
.policy0_mask_offset = 0x00000010, |
||||
.policy1_mask_offset = 0x00000014, |
||||
.policy2_mask_offset = 0x00000018, |
||||
.policy3_mask_offset = 0x0000001c, |
||||
.policy0_mask2_offset = 0x00000048, |
||||
.policy1_mask2_offset = 0x0000004c, |
||||
.policy2_mask2_offset = 0x00000050, |
||||
.policy3_mask2_offset = 0x00000054, |
||||
.lvm_en_offset = 0x00000034, |
||||
.freq_id = 2, |
||||
.freq_tbl = slave_axi_freq_tbl, |
||||
}; |
||||
|
||||
/*
|
||||
* Bus clocks |
||||
*/ |
||||
|
||||
/* KPM bus clocks */ |
||||
static struct bus_clock sdio1_ahb_clk = { |
||||
.clk = { |
||||
.name = "sdio1_ahb_clk", |
||||
.parent = &kpm_ccu_clk.clk, |
||||
.ops = &bus_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR, |
||||
}, |
||||
.freq_tbl = master_ahb_freq_tbl, |
||||
.data = &sdio1_ahb_data, |
||||
}; |
||||
|
||||
static struct bus_clock sdio2_ahb_clk = { |
||||
.clk = { |
||||
.name = "sdio2_ahb_clk", |
||||
.parent = &kpm_ccu_clk.clk, |
||||
.ops = &bus_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR, |
||||
}, |
||||
.freq_tbl = master_ahb_freq_tbl, |
||||
.data = &sdio2_ahb_data, |
||||
}; |
||||
|
||||
static struct bus_clock sdio3_ahb_clk = { |
||||
.clk = { |
||||
.name = "sdio3_ahb_clk", |
||||
.parent = &kpm_ccu_clk.clk, |
||||
.ops = &bus_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR, |
||||
}, |
||||
.freq_tbl = master_ahb_freq_tbl, |
||||
.data = &sdio3_ahb_data, |
||||
}; |
||||
|
||||
static struct bus_clock sdio4_ahb_clk = { |
||||
.clk = { |
||||
.name = "sdio4_ahb_clk", |
||||
.parent = &kpm_ccu_clk.clk, |
||||
.ops = &bus_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR, |
||||
}, |
||||
.freq_tbl = master_ahb_freq_tbl, |
||||
.data = &sdio4_ahb_data, |
||||
}; |
||||
|
||||
static struct bus_clock bsc1_apb_clk = { |
||||
.clk = { |
||||
.name = "bsc1_apb_clk", |
||||
.parent = &kps_ccu_clk.clk, |
||||
.ops = &bus_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR, |
||||
}, |
||||
.freq_tbl = slave_apb_freq_tbl, |
||||
.data = &bsc1_apb_data, |
||||
}; |
||||
|
||||
static struct bus_clock bsc2_apb_clk = { |
||||
.clk = { |
||||
.name = "bsc2_apb_clk", |
||||
.parent = &kps_ccu_clk.clk, |
||||
.ops = &bus_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR, |
||||
}, |
||||
.freq_tbl = slave_apb_freq_tbl, |
||||
.data = &bsc2_apb_data, |
||||
}; |
||||
|
||||
static struct bus_clock bsc3_apb_clk = { |
||||
.clk = { |
||||
.name = "bsc3_apb_clk", |
||||
.parent = &kps_ccu_clk.clk, |
||||
.ops = &bus_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR, |
||||
}, |
||||
.freq_tbl = slave_apb_freq_tbl, |
||||
.data = &bsc3_apb_data, |
||||
}; |
||||
|
||||
/* KPM peripheral */ |
||||
static struct peri_clock sdio1_clk = { |
||||
.clk = { |
||||
.name = "sdio1_clk", |
||||
.parent = &ref_52m.clk, |
||||
.ops = &peri_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR, |
||||
}, |
||||
.data = &sdio1_data, |
||||
}; |
||||
|
||||
static struct peri_clock sdio2_clk = { |
||||
.clk = { |
||||
.name = "sdio2_clk", |
||||
.parent = &ref_52m.clk, |
||||
.ops = &peri_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR, |
||||
}, |
||||
.data = &sdio2_data, |
||||
}; |
||||
|
||||
static struct peri_clock sdio3_clk = { |
||||
.clk = { |
||||
.name = "sdio3_clk", |
||||
.parent = &ref_52m.clk, |
||||
.ops = &peri_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR, |
||||
}, |
||||
.data = &sdio3_data, |
||||
}; |
||||
|
||||
static struct peri_clock sdio4_clk = { |
||||
.clk = { |
||||
.name = "sdio4_clk", |
||||
.parent = &ref_52m.clk, |
||||
.ops = &peri_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR, |
||||
}, |
||||
.data = &sdio4_data, |
||||
}; |
||||
|
||||
static struct peri_clock sdio1_sleep_clk = { |
||||
.clk = { |
||||
.name = "sdio1_sleep_clk", |
||||
.parent = &kpm_ccu_clk.clk, |
||||
.ops = &bus_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR, |
||||
}, |
||||
.data = &sdio1_sleep_data, |
||||
}; |
||||
|
||||
static struct peri_clock sdio2_sleep_clk = { |
||||
.clk = { |
||||
.name = "sdio2_sleep_clk", |
||||
.parent = &kpm_ccu_clk.clk, |
||||
.ops = &bus_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR, |
||||
}, |
||||
.data = &sdio2_sleep_data, |
||||
}; |
||||
|
||||
static struct peri_clock sdio3_sleep_clk = { |
||||
.clk = { |
||||
.name = "sdio3_sleep_clk", |
||||
.parent = &kpm_ccu_clk.clk, |
||||
.ops = &bus_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR, |
||||
}, |
||||
.data = &sdio3_sleep_data, |
||||
}; |
||||
|
||||
static struct peri_clock sdio4_sleep_clk = { |
||||
.clk = { |
||||
.name = "sdio4_sleep_clk", |
||||
.parent = &kpm_ccu_clk.clk, |
||||
.ops = &bus_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_MST_CLK_BASE_ADDR, |
||||
}, |
||||
.data = &sdio4_sleep_data, |
||||
}; |
||||
|
||||
/* KPS peripheral clock */ |
||||
static struct peri_clock bsc1_clk = { |
||||
.clk = { |
||||
.name = "bsc1_clk", |
||||
.parent = &ref_13m.clk, |
||||
.rate = 13 * CLOCK_1M, |
||||
.div = 1, |
||||
.ops = &peri_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR, |
||||
}, |
||||
.data = &bsc1_data, |
||||
}; |
||||
|
||||
static struct peri_clock bsc2_clk = { |
||||
.clk = { |
||||
.name = "bsc2_clk", |
||||
.parent = &ref_13m.clk, |
||||
.rate = 13 * CLOCK_1M, |
||||
.div = 1, |
||||
.ops = &peri_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR, |
||||
}, |
||||
.data = &bsc2_data, |
||||
}; |
||||
|
||||
static struct peri_clock bsc3_clk = { |
||||
.clk = { |
||||
.name = "bsc3_clk", |
||||
.parent = &ref_13m.clk, |
||||
.rate = 13 * CLOCK_1M, |
||||
.div = 1, |
||||
.ops = &peri_clk_ops, |
||||
.ccu_clk_mgr_base = KONA_SLV_CLK_BASE_ADDR, |
||||
}, |
||||
.data = &bsc3_data, |
||||
}; |
||||
|
||||
/* public table for registering clocks */ |
||||
struct clk_lookup arch_clk_tbl[] = { |
||||
/* Peripheral clocks */ |
||||
CLK_LK(sdio1), |
||||
CLK_LK(sdio2), |
||||
CLK_LK(sdio3), |
||||
CLK_LK(sdio4), |
||||
CLK_LK(sdio1_sleep), |
||||
CLK_LK(sdio2_sleep), |
||||
CLK_LK(sdio3_sleep), |
||||
CLK_LK(sdio4_sleep), |
||||
CLK_LK(bsc1), |
||||
CLK_LK(bsc2), |
||||
CLK_LK(bsc3), |
||||
/* Bus clocks */ |
||||
CLK_LK(sdio1_ahb), |
||||
CLK_LK(sdio2_ahb), |
||||
CLK_LK(sdio3_ahb), |
||||
CLK_LK(sdio4_ahb), |
||||
CLK_LK(bsc1_apb), |
||||
CLK_LK(bsc2_apb), |
||||
CLK_LK(bsc3_apb), |
||||
}; |
||||
|
||||
/* public array size */ |
||||
unsigned int arch_clk_tbl_array_size = ARRAY_SIZE(arch_clk_tbl); |
@ -0,0 +1,52 @@ |
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <asm/io.h> |
||||
#include <asm/errno.h> |
||||
#include <asm/arch/sysmap.h> |
||||
#include <asm/kona-common/clk.h> |
||||
#include "clk-core.h" |
||||
|
||||
/* Enable appropriate clocks for a BSC/I2C port */ |
||||
int clk_bsc_enable(void *base) |
||||
{ |
||||
int ret; |
||||
char *bscstr, *apbstr; |
||||
|
||||
switch ((u32) base) { |
||||
case PMU_BSC_BASE_ADDR: |
||||
/* PMU clock is always enabled */ |
||||
return 0; |
||||
case BSC1_BASE_ADDR: |
||||
bscstr = "bsc1_clk"; |
||||
apbstr = "bsc1_apb_clk"; |
||||
break; |
||||
case BSC2_BASE_ADDR: |
||||
bscstr = "bsc2_clk"; |
||||
apbstr = "bsc2_apb_clk"; |
||||
break; |
||||
case BSC3_BASE_ADDR: |
||||
bscstr = "bsc3_clk"; |
||||
apbstr = "bsc3_apb_clk"; |
||||
break; |
||||
default: |
||||
printf("%s: base 0x%p not found\n", __func__, base); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
/* Note that the bus clock must be enabled first */ |
||||
|
||||
ret = clk_get_and_enable(apbstr); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
ret = clk_get_and_enable(bscstr); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,513 @@ |
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
/*
|
||||
* |
||||
* bcm281xx architecture clock framework |
||||
* |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <asm/io.h> |
||||
#include <asm/errno.h> |
||||
#include <bitfield.h> |
||||
#include <asm/arch/sysmap.h> |
||||
#include <asm/kona-common/clk.h> |
||||
#include "clk-core.h" |
||||
|
||||
#define CLK_WR_ACCESS_PASSWORD 0x00a5a501 |
||||
#define WR_ACCESS_OFFSET 0 /* common to all clock blocks */ |
||||
#define POLICY_CTL_GO 1 /* Load and refresh policy masks */ |
||||
#define POLICY_CTL_GO_ATL 4 /* Active Load */ |
||||
|
||||
/* Helper function */ |
||||
int clk_get_and_enable(char *clkstr) |
||||
{ |
||||
int ret = 0; |
||||
struct clk *c; |
||||
|
||||
debug("%s: %s\n", __func__, clkstr); |
||||
|
||||
c = clk_get(clkstr); |
||||
if (c) { |
||||
ret = clk_enable(c); |
||||
if (ret) |
||||
return ret; |
||||
} else { |
||||
printf("%s: Couldn't find %s\n", __func__, clkstr); |
||||
return -EINVAL; |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
/*
|
||||
* Poll a register in a CCU's address space, returning when the |
||||
* specified bit in that register's value is set (or clear). Delay |
||||
* a microsecond after each read of the register. Returns true if |
||||
* successful, or false if we gave up trying. |
||||
* |
||||
* Caller must ensure the CCU lock is held. |
||||
*/ |
||||
#define CLK_GATE_DELAY_USEC 2000 |
||||
static inline int wait_bit(void *base, u32 offset, u32 bit, bool want) |
||||
{ |
||||
unsigned int tries; |
||||
u32 bit_mask = 1 << bit; |
||||
|
||||
for (tries = 0; tries < CLK_GATE_DELAY_USEC; tries++) { |
||||
u32 val; |
||||
bool bit_val; |
||||
|
||||
val = readl(base + offset); |
||||
bit_val = (val & bit_mask) ? 1 : 0; |
||||
if (bit_val == want) |
||||
return 0; /* success */ |
||||
udelay(1); |
||||
} |
||||
|
||||
debug("%s: timeout on addr 0x%p, waiting for bit %d to go to %d\n", |
||||
__func__, base + offset, bit, want); |
||||
|
||||
return -ETIMEDOUT; |
||||
} |
||||
|
||||
/* Enable a peripheral clock */ |
||||
static int peri_clk_enable(struct clk *c, int enable) |
||||
{ |
||||
int ret = 0; |
||||
u32 reg; |
||||
struct peri_clock *peri_clk = to_peri_clk(c); |
||||
struct peri_clk_data *cd = peri_clk->data; |
||||
struct bcm_clk_gate *gate = &cd->gate; |
||||
void *base = (void *)c->ccu_clk_mgr_base; |
||||
|
||||
|
||||
debug("%s: %s\n", __func__, c->name); |
||||
|
||||
clk_get_rate(c); /* Make sure rate and sel are filled in */ |
||||
|
||||
/* enable access */ |
||||
writel(CLK_WR_ACCESS_PASSWORD, base + WR_ACCESS_OFFSET); |
||||
|
||||
if (enable) { |
||||
debug("%s %s set rate %lu div %lu sel %d parent %lu\n", |
||||
__func__, c->name, c->rate, c->div, c->sel, |
||||
c->parent->rate); |
||||
|
||||
/*
|
||||
* clkgate - only software controllable gates are |
||||
* supported by u-boot which includes all clocks |
||||
* that matter. This avoids bringing in a lot of extra |
||||
* complexity as done in the kernel framework. |
||||
*/ |
||||
if (gate_exists(gate)) { |
||||
reg = readl(base + cd->gate.offset); |
||||
reg |= (1 << cd->gate.en_bit); |
||||
writel(reg, base + cd->gate.offset); |
||||
} |
||||
|
||||
/* div and pll select */ |
||||
if (divider_exists(&cd->div)) { |
||||
reg = readl(base + cd->div.offset); |
||||
bitfield_replace(reg, cd->div.shift, cd->div.width, |
||||
c->div - 1); |
||||
writel(reg, base + cd->div.offset); |
||||
} |
||||
|
||||
/* frequency selector */ |
||||
if (selector_exists(&cd->sel)) { |
||||
reg = readl(base + cd->sel.offset); |
||||
bitfield_replace(reg, cd->sel.shift, cd->sel.width, |
||||
c->sel); |
||||
writel(reg, base + cd->sel.offset); |
||||
} |
||||
|
||||
/* trigger */ |
||||
if (trigger_exists(&cd->trig)) { |
||||
writel((1 << cd->trig.bit), base + cd->trig.offset); |
||||
|
||||
/* wait for trigger status bit to go to 0 */ |
||||
ret = wait_bit(base, cd->trig.offset, cd->trig.bit, 0); |
||||
if (ret) |
||||
return ret; |
||||
} |
||||
|
||||
/* wait for running (status_bit = 1) */ |
||||
ret = wait_bit(base, cd->gate.offset, cd->gate.status_bit, 1); |
||||
if (ret) |
||||
return ret; |
||||
} else { |
||||
debug("%s disable clock %s\n", __func__, c->name); |
||||
|
||||
/* clkgate */ |
||||
reg = readl(base + cd->gate.offset); |
||||
reg &= ~(1 << cd->gate.en_bit); |
||||
writel(reg, base + cd->gate.offset); |
||||
|
||||
/* wait for stop (status_bit = 0) */ |
||||
ret = wait_bit(base, cd->gate.offset, cd->gate.status_bit, 0); |
||||
} |
||||
|
||||
/* disable access */ |
||||
writel(0, base + WR_ACCESS_OFFSET); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
/* Set the rate of a peripheral clock */ |
||||
static int peri_clk_set_rate(struct clk *c, unsigned long rate) |
||||
{ |
||||
int ret = 0; |
||||
int i; |
||||
unsigned long diff; |
||||
unsigned long new_rate = 0, div = 1; |
||||
struct peri_clock *peri_clk = to_peri_clk(c); |
||||
struct peri_clk_data *cd = peri_clk->data; |
||||
const char **clock; |
||||
|
||||
debug("%s: %s\n", __func__, c->name); |
||||
diff = rate; |
||||
|
||||
i = 0; |
||||
for (clock = cd->clocks; *clock; clock++, i++) { |
||||
struct refclk *ref = refclk_str_to_clk(*clock); |
||||
if (!ref) { |
||||
printf("%s: Lookup of %s failed\n", __func__, *clock); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
/* round to the new rate */ |
||||
div = ref->clk.rate / rate; |
||||
if (div == 0) |
||||
div = 1; |
||||
|
||||
new_rate = ref->clk.rate / div; |
||||
|
||||
/* get the min diff */ |
||||
if (abs(new_rate - rate) < diff) { |
||||
diff = abs(new_rate - rate); |
||||
c->sel = i; |
||||
c->parent = &ref->clk; |
||||
c->rate = new_rate; |
||||
c->div = div; |
||||
} |
||||
} |
||||
|
||||
debug("%s %s set rate %lu div %lu sel %d parent %lu\n", __func__, |
||||
c->name, c->rate, c->div, c->sel, c->parent->rate); |
||||
return ret; |
||||
} |
||||
|
||||
/* Get the rate of a peripheral clock */ |
||||
static unsigned long peri_clk_get_rate(struct clk *c) |
||||
{ |
||||
struct peri_clock *peri_clk = to_peri_clk(c); |
||||
struct peri_clk_data *cd = peri_clk->data; |
||||
void *base = (void *)c->ccu_clk_mgr_base; |
||||
int div = 1; |
||||
const char **clock; |
||||
struct refclk *ref; |
||||
u32 reg; |
||||
|
||||
debug("%s: %s\n", __func__, c->name); |
||||
if (selector_exists(&cd->sel)) { |
||||
reg = readl(base + cd->sel.offset); |
||||
c->sel = bitfield_extract(reg, cd->sel.shift, cd->sel.width); |
||||
} else { |
||||
/*
|
||||
* For peri clocks that don't have a selector, the single |
||||
* reference clock will always exist at index 0. |
||||
*/ |
||||
c->sel = 0; |
||||
} |
||||
|
||||
if (divider_exists(&cd->div)) { |
||||
reg = readl(base + cd->div.offset); |
||||
div = bitfield_extract(reg, cd->div.shift, cd->div.width); |
||||
div += 1; |
||||
} |
||||
|
||||
clock = cd->clocks; |
||||
ref = refclk_str_to_clk(clock[c->sel]); |
||||
if (!ref) { |
||||
printf("%s: Can't lookup %s\n", __func__, clock[c->sel]); |
||||
return 0; |
||||
} |
||||
|
||||
c->parent = &ref->clk; |
||||
c->div = div; |
||||
c->rate = c->parent->rate / c->div; |
||||
debug("%s parent rate %lu div %d sel %d rate %lu\n", __func__, |
||||
c->parent->rate, div, c->sel, c->rate); |
||||
|
||||
return c->rate; |
||||
} |
||||
|
||||
/* Peripheral clock operations */ |
||||
struct clk_ops peri_clk_ops = { |
||||
.enable = peri_clk_enable, |
||||
.set_rate = peri_clk_set_rate, |
||||
.get_rate = peri_clk_get_rate, |
||||
}; |
||||
|
||||
/* Enable a CCU clock */ |
||||
static int ccu_clk_enable(struct clk *c, int enable) |
||||
{ |
||||
struct ccu_clock *ccu_clk = to_ccu_clk(c); |
||||
void *base = (void *)c->ccu_clk_mgr_base; |
||||
int ret = 0; |
||||
u32 reg; |
||||
|
||||
debug("%s: %s\n", __func__, c->name); |
||||
if (!enable) |
||||
return -EINVAL; /* CCU clock cannot shutdown */ |
||||
|
||||
/* enable access */ |
||||
writel(CLK_WR_ACCESS_PASSWORD, base + WR_ACCESS_OFFSET); |
||||
|
||||
/* config enable for policy engine */ |
||||
writel(1, base + ccu_clk->lvm_en_offset); |
||||
|
||||
/* wait for bit to go to 0 */ |
||||
ret = wait_bit(base, ccu_clk->lvm_en_offset, 0, 0); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
/* freq ID */ |
||||
if (!ccu_clk->freq_bit_shift) |
||||
ccu_clk->freq_bit_shift = 8; |
||||
|
||||
/* Set frequency id for each of the 4 policies */ |
||||
reg = ccu_clk->freq_id | |
||||
(ccu_clk->freq_id << (ccu_clk->freq_bit_shift)) | |
||||
(ccu_clk->freq_id << (ccu_clk->freq_bit_shift * 2)) | |
||||
(ccu_clk->freq_id << (ccu_clk->freq_bit_shift * 3)); |
||||
writel(reg, base + ccu_clk->policy_freq_offset); |
||||
|
||||
/* enable all clock mask */ |
||||
writel(0x7fffffff, base + ccu_clk->policy0_mask_offset); |
||||
writel(0x7fffffff, base + ccu_clk->policy1_mask_offset); |
||||
writel(0x7fffffff, base + ccu_clk->policy2_mask_offset); |
||||
writel(0x7fffffff, base + ccu_clk->policy3_mask_offset); |
||||
|
||||
if (ccu_clk->num_policy_masks == 2) { |
||||
writel(0x7fffffff, base + ccu_clk->policy0_mask2_offset); |
||||
writel(0x7fffffff, base + ccu_clk->policy1_mask2_offset); |
||||
writel(0x7fffffff, base + ccu_clk->policy2_mask2_offset); |
||||
writel(0x7fffffff, base + ccu_clk->policy3_mask2_offset); |
||||
} |
||||
|
||||
/* start policy engine */ |
||||
reg = readl(base + ccu_clk->policy_ctl_offset); |
||||
reg |= (POLICY_CTL_GO + POLICY_CTL_GO_ATL); |
||||
writel(reg, base + ccu_clk->policy_ctl_offset); |
||||
|
||||
/* wait till started */ |
||||
ret = wait_bit(base, ccu_clk->policy_ctl_offset, 0, 0); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
/* disable access */ |
||||
writel(0, base + WR_ACCESS_OFFSET); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
/* Get the CCU clock rate */ |
||||
static unsigned long ccu_clk_get_rate(struct clk *c) |
||||
{ |
||||
struct ccu_clock *ccu_clk = to_ccu_clk(c); |
||||
debug("%s: %s\n", __func__, c->name); |
||||
c->rate = ccu_clk->freq_tbl[ccu_clk->freq_id]; |
||||
return c->rate; |
||||
} |
||||
|
||||
/* CCU clock operations */ |
||||
struct clk_ops ccu_clk_ops = { |
||||
.enable = ccu_clk_enable, |
||||
.get_rate = ccu_clk_get_rate, |
||||
}; |
||||
|
||||
/* Enable a bus clock */ |
||||
static int bus_clk_enable(struct clk *c, int enable) |
||||
{ |
||||
struct bus_clock *bus_clk = to_bus_clk(c); |
||||
struct bus_clk_data *cd = bus_clk->data; |
||||
void *base = (void *)c->ccu_clk_mgr_base; |
||||
int ret = 0; |
||||
u32 reg; |
||||
|
||||
debug("%s: %s\n", __func__, c->name); |
||||
/* enable access */ |
||||
writel(CLK_WR_ACCESS_PASSWORD, base + WR_ACCESS_OFFSET); |
||||
|
||||
/* enable gating */ |
||||
reg = readl(base + cd->gate.offset); |
||||
if (!!(reg & (1 << cd->gate.status_bit)) == !!enable) |
||||
debug("%s already %s\n", c->name, |
||||
enable ? "enabled" : "disabled"); |
||||
else { |
||||
int want = (enable) ? 1 : 0; |
||||
reg |= (1 << cd->gate.hw_sw_sel_bit); |
||||
|
||||
if (enable) |
||||
reg |= (1 << cd->gate.en_bit); |
||||
else |
||||
reg &= ~(1 << cd->gate.en_bit); |
||||
|
||||
writel(reg, base + cd->gate.offset); |
||||
ret = wait_bit(base, cd->gate.offset, cd->gate.status_bit, |
||||
want); |
||||
if (ret) |
||||
return ret; |
||||
} |
||||
|
||||
/* disable access */ |
||||
writel(0, base + WR_ACCESS_OFFSET); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
/* Get the rate of a bus clock */ |
||||
static unsigned long bus_clk_get_rate(struct clk *c) |
||||
{ |
||||
struct bus_clock *bus_clk = to_bus_clk(c); |
||||
struct ccu_clock *ccu_clk; |
||||
|
||||
debug("%s: %s\n", __func__, c->name); |
||||
ccu_clk = to_ccu_clk(c->parent); |
||||
|
||||
c->rate = bus_clk->freq_tbl[ccu_clk->freq_id]; |
||||
c->div = ccu_clk->freq_tbl[ccu_clk->freq_id] / c->rate; |
||||
return c->rate; |
||||
} |
||||
|
||||
/* Bus clock operations */ |
||||
struct clk_ops bus_clk_ops = { |
||||
.enable = bus_clk_enable, |
||||
.get_rate = bus_clk_get_rate, |
||||
}; |
||||
|
||||
/* Enable a reference clock */ |
||||
static int ref_clk_enable(struct clk *c, int enable) |
||||
{ |
||||
debug("%s: %s\n", __func__, c->name); |
||||
return 0; |
||||
} |
||||
|
||||
/* Reference clock operations */ |
||||
struct clk_ops ref_clk_ops = { |
||||
.enable = ref_clk_enable, |
||||
}; |
||||
|
||||
/*
|
||||
* clk.h implementation follows |
||||
*/ |
||||
|
||||
/* Initialize the clock framework */ |
||||
int clk_init(void) |
||||
{ |
||||
debug("%s:\n", __func__); |
||||
return 0; |
||||
} |
||||
|
||||
/* Get a clock handle, give a name string */ |
||||
struct clk *clk_get(const char *con_id) |
||||
{ |
||||
int i; |
||||
struct clk_lookup *clk_tblp; |
||||
|
||||
debug("%s: %s\n", __func__, con_id); |
||||
|
||||
clk_tblp = arch_clk_tbl; |
||||
for (i = 0; i < arch_clk_tbl_array_size; i++, clk_tblp++) { |
||||
if (clk_tblp->con_id) { |
||||
if (!con_id || strcmp(clk_tblp->con_id, con_id)) |
||||
continue; |
||||
return clk_tblp->clk; |
||||
} |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
/* Enable a clock */ |
||||
int clk_enable(struct clk *c) |
||||
{ |
||||
int ret = 0; |
||||
|
||||
debug("%s: %s\n", __func__, c->name); |
||||
if (!c->ops || !c->ops->enable) |
||||
return -1; |
||||
|
||||
/* enable parent clock first */ |
||||
if (c->parent) |
||||
ret = clk_enable(c->parent); |
||||
|
||||
if (ret) |
||||
return ret; |
||||
|
||||
if (!c->use_cnt) { |
||||
c->use_cnt++; |
||||
ret = c->ops->enable(c, 1); |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
/* Disable a clock */ |
||||
void clk_disable(struct clk *c) |
||||
{ |
||||
debug("%s: %s\n", __func__, c->name); |
||||
if (!c->ops || !c->ops->enable) |
||||
return; |
||||
|
||||
if (c->use_cnt) { |
||||
c->use_cnt--; |
||||
c->ops->enable(c, 0); |
||||
} |
||||
|
||||
/* disable parent */ |
||||
if (c->parent) |
||||
clk_disable(c->parent); |
||||
} |
||||
|
||||
/* Get the clock rate */ |
||||
unsigned long clk_get_rate(struct clk *c) |
||||
{ |
||||
unsigned long rate; |
||||
|
||||
debug("%s: %s\n", __func__, c->name); |
||||
if (!c || !c->ops || !c->ops->get_rate) |
||||
return 0; |
||||
|
||||
rate = c->ops->get_rate(c); |
||||
debug("%s: rate = %ld\n", __func__, rate); |
||||
return rate; |
||||
} |
||||
|
||||
/* Set the clock rate */ |
||||
int clk_set_rate(struct clk *c, unsigned long rate) |
||||
{ |
||||
int ret; |
||||
|
||||
debug("%s: %s rate=%ld\n", __func__, c->name, rate); |
||||
if (!c || !c->ops || !c->ops->set_rate) |
||||
return -EINVAL; |
||||
|
||||
if (c->use_cnt) |
||||
return -EINVAL; |
||||
|
||||
ret = c->ops->set_rate(c, rate); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
/* Not required for this arch */ |
||||
/*
|
||||
long clk_round_rate(struct clk *clk, unsigned long rate); |
||||
int clk_set_parent(struct clk *clk, struct clk *parent); |
||||
struct clk *clk_get_parent(struct clk *clk); |
||||
*/ |
@ -0,0 +1,495 @@ |
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <linux/stddef.h> |
||||
|
||||
#ifdef CONFIG_CLK_DEBUG |
||||
#undef writel |
||||
#undef readl |
||||
static inline void writel(u32 val, void *addr) |
||||
{ |
||||
printf("Write [0x%p] = 0x%08x\n", addr, val); |
||||
*(u32 *)addr = val; |
||||
} |
||||
|
||||
static inline u32 readl(void *addr) |
||||
{ |
||||
u32 val = *(u32 *)addr; |
||||
printf("Read [0x%p] = 0x%08x\n", addr, val); |
||||
return val; |
||||
} |
||||
#endif |
||||
|
||||
struct clk; |
||||
|
||||
struct clk_lookup { |
||||
const char *dev_id; |
||||
const char *con_id; |
||||
struct clk *clk; |
||||
}; |
||||
|
||||
extern struct clk_lookup arch_clk_tbl[]; |
||||
extern unsigned int arch_clk_tbl_array_size; |
||||
|
||||
/**
|
||||
* struct clk_ops - standard clock operations |
||||
* @enable: enable/disable clock, see clk_enable() and clk_disable() |
||||
* @set_rate: set the clock rate, see clk_set_rate(). |
||||
* @get_rate: get the clock rate, see clk_get_rate(). |
||||
* @round_rate: round a given clock rate, see clk_round_rate(). |
||||
* @set_parent: set the clock's parent, see clk_set_parent(). |
||||
* |
||||
* Group the common clock implementations together so that we |
||||
* don't have to keep setting the same fiels again. We leave |
||||
* enable in struct clk. |
||||
* |
||||
*/ |
||||
struct clk_ops { |
||||
int (*enable) (struct clk *c, int enable); |
||||
int (*set_rate) (struct clk *c, unsigned long rate); |
||||
unsigned long (*get_rate) (struct clk *c); |
||||
unsigned long (*round_rate) (struct clk *c, unsigned long rate); |
||||
int (*set_parent) (struct clk *c, struct clk *parent); |
||||
}; |
||||
|
||||
struct clk { |
||||
struct clk *parent; |
||||
const char *name; |
||||
int use_cnt; |
||||
unsigned long rate; /* in HZ */ |
||||
|
||||
/* programmable divider. 0 means fixed ratio to parent clock */ |
||||
unsigned long div; |
||||
|
||||
struct clk_src *src; |
||||
struct clk_ops *ops; |
||||
|
||||
unsigned long ccu_clk_mgr_base; |
||||
int sel; |
||||
}; |
||||
|
||||
struct refclk *refclk_str_to_clk(const char *name); |
||||
|
||||
#define U8_MAX ((u8)~0U) |
||||
#define U32_MAX ((u32)~0U) |
||||
#define U64_MAX ((u64)~0U) |
||||
|
||||
/* The common clock framework uses u8 to represent a parent index */ |
||||
#define PARENT_COUNT_MAX ((u32)U8_MAX) |
||||
|
||||
#define BAD_CLK_INDEX U8_MAX /* Can't ever be valid */ |
||||
#define BAD_CLK_NAME ((const char *)-1) |
||||
|
||||
#define BAD_SCALED_DIV_VALUE U64_MAX |
||||
|
||||
/*
|
||||
* Utility macros for object flag management. If possible, flags |
||||
* should be defined such that 0 is the desired default value. |
||||
*/ |
||||
#define FLAG(type, flag) BCM_CLK_ ## type ## _FLAGS_ ## flag |
||||
#define FLAG_SET(obj, type, flag) ((obj)->flags |= FLAG(type, flag)) |
||||
#define FLAG_CLEAR(obj, type, flag) ((obj)->flags &= ~(FLAG(type, flag))) |
||||
#define FLAG_FLIP(obj, type, flag) ((obj)->flags ^= FLAG(type, flag)) |
||||
#define FLAG_TEST(obj, type, flag) (!!((obj)->flags & FLAG(type, flag))) |
||||
|
||||
/* Clock field state tests */ |
||||
|
||||
#define gate_exists(gate) FLAG_TEST(gate, GATE, EXISTS) |
||||
#define gate_is_enabled(gate) FLAG_TEST(gate, GATE, ENABLED) |
||||
#define gate_is_hw_controllable(gate) FLAG_TEST(gate, GATE, HW) |
||||
#define gate_is_sw_controllable(gate) FLAG_TEST(gate, GATE, SW) |
||||
#define gate_is_sw_managed(gate) FLAG_TEST(gate, GATE, SW_MANAGED) |
||||
#define gate_is_no_disable(gate) FLAG_TEST(gate, GATE, NO_DISABLE) |
||||
|
||||
#define gate_flip_enabled(gate) FLAG_FLIP(gate, GATE, ENABLED) |
||||
|
||||
#define divider_exists(div) FLAG_TEST(div, DIV, EXISTS) |
||||
#define divider_is_fixed(div) FLAG_TEST(div, DIV, FIXED) |
||||
#define divider_has_fraction(div) (!divider_is_fixed(div) && \ |
||||
(div)->frac_width > 0) |
||||
|
||||
#define selector_exists(sel) ((sel)->width != 0) |
||||
#define trigger_exists(trig) FLAG_TEST(trig, TRIG, EXISTS) |
||||
|
||||
/* Clock type, used to tell common block what it's part of */ |
||||
enum bcm_clk_type { |
||||
bcm_clk_none, /* undefined clock type */ |
||||
bcm_clk_bus, |
||||
bcm_clk_core, |
||||
bcm_clk_peri |
||||
}; |
||||
|
||||
/*
|
||||
* Gating control and status is managed by a 32-bit gate register. |
||||
* |
||||
* There are several types of gating available: |
||||
* - (no gate) |
||||
* A clock with no gate is assumed to be always enabled. |
||||
* - hardware-only gating (auto-gating) |
||||
* Enabling or disabling clocks with this type of gate is |
||||
* managed automatically by the hardware. Such clocks can be |
||||
* considered by the software to be enabled. The current status |
||||
* of auto-gated clocks can be read from the gate status bit. |
||||
* - software-only gating |
||||
* Auto-gating is not available for this type of clock. |
||||
* Instead, software manages whether it's enabled by setting or |
||||
* clearing the enable bit. The current gate status of a gate |
||||
* under software control can be read from the gate status bit. |
||||
* To ensure a change to the gating status is complete, the |
||||
* status bit can be polled to verify that the gate has entered |
||||
* the desired state. |
||||
* - selectable hardware or software gating |
||||
* Gating for this type of clock can be configured to be either |
||||
* under software or hardware control. Which type is in use is |
||||
* determined by the hw_sw_sel bit of the gate register. |
||||
*/ |
||||
struct bcm_clk_gate { |
||||
u32 offset; /* gate register offset */ |
||||
u32 status_bit; /* 0: gate is disabled; 0: gatge is enabled */ |
||||
u32 en_bit; /* 0: disable; 1: enable */ |
||||
u32 hw_sw_sel_bit; /* 0: hardware gating; 1: software gating */ |
||||
u32 flags; /* BCM_CLK_GATE_FLAGS_* below */ |
||||
}; |
||||
|
||||
/*
|
||||
* Gate flags: |
||||
* HW means this gate can be auto-gated |
||||
* SW means the state of this gate can be software controlled |
||||
* NO_DISABLE means this gate is (only) enabled if under software control |
||||
* SW_MANAGED means the status of this gate is under software control |
||||
* ENABLED means this software-managed gate is *supposed* to be enabled |
||||
*/ |
||||
#define BCM_CLK_GATE_FLAGS_EXISTS ((u32)1 << 0) /* Gate is valid */ |
||||
#define BCM_CLK_GATE_FLAGS_HW ((u32)1 << 1) /* Can auto-gate */ |
||||
#define BCM_CLK_GATE_FLAGS_SW ((u32)1 << 2) /* Software control */ |
||||
#define BCM_CLK_GATE_FLAGS_NO_DISABLE ((u32)1 << 3) /* HW or enabled */ |
||||
#define BCM_CLK_GATE_FLAGS_SW_MANAGED ((u32)1 << 4) /* SW now in control */ |
||||
#define BCM_CLK_GATE_FLAGS_ENABLED ((u32)1 << 5) /* If SW_MANAGED */ |
||||
|
||||
/*
|
||||
* Gate initialization macros. |
||||
* |
||||
* Any gate initially under software control will be enabled. |
||||
*/ |
||||
|
||||
/* A hardware/software gate initially under software control */ |
||||
#define HW_SW_GATE(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \ |
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.en_bit = (_en_bit), \
|
||||
.hw_sw_sel_bit = (_hw_sw_sel_bit), \
|
||||
.flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
|
||||
FLAG(GATE, SW_MANAGED)|FLAG(GATE, ENABLED)| \
|
||||
FLAG(GATE, EXISTS), \
|
||||
} |
||||
|
||||
/* A hardware/software gate initially under hardware control */ |
||||
#define HW_SW_GATE_AUTO(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \ |
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.en_bit = (_en_bit), \
|
||||
.hw_sw_sel_bit = (_hw_sw_sel_bit), \
|
||||
.flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
|
||||
FLAG(GATE, EXISTS), \
|
||||
} |
||||
|
||||
/* A hardware-or-enabled gate (enabled if not under hardware control) */ |
||||
#define HW_ENABLE_GATE(_offset, _status_bit, _en_bit, _hw_sw_sel_bit) \ |
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.en_bit = (_en_bit), \
|
||||
.hw_sw_sel_bit = (_hw_sw_sel_bit), \
|
||||
.flags = FLAG(GATE, HW)|FLAG(GATE, SW)| \
|
||||
FLAG(GATE, NO_DISABLE)|FLAG(GATE, EXISTS), \
|
||||
} |
||||
|
||||
/* A software-only gate */ |
||||
#define SW_ONLY_GATE(_offset, _status_bit, _en_bit) \ |
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.en_bit = (_en_bit), \
|
||||
.flags = FLAG(GATE, SW)|FLAG(GATE, SW_MANAGED)| \
|
||||
FLAG(GATE, ENABLED)|FLAG(GATE, EXISTS), \
|
||||
} |
||||
|
||||
/* A hardware-only gate */ |
||||
#define HW_ONLY_GATE(_offset, _status_bit) \ |
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.status_bit = (_status_bit), \
|
||||
.flags = FLAG(GATE, HW)|FLAG(GATE, EXISTS), \
|
||||
} |
||||
|
||||
/*
|
||||
* Each clock can have zero, one, or two dividers which change the |
||||
* output rate of the clock. Each divider can be either fixed or |
||||
* variable. If there are two dividers, they are the "pre-divider" |
||||
* and the "regular" or "downstream" divider. If there is only one, |
||||
* there is no pre-divider. |
||||
* |
||||
* A fixed divider is any non-zero (positive) value, and it |
||||
* indicates how the input rate is affected by the divider. |
||||
* |
||||
* The value of a variable divider is maintained in a sub-field of a |
||||
* 32-bit divider register. The position of the field in the |
||||
* register is defined by its offset and width. The value recorded |
||||
* in this field is always 1 less than the value it represents. |
||||
* |
||||
* In addition, a variable divider can indicate that some subset |
||||
* of its bits represent a "fractional" part of the divider. Such |
||||
* bits comprise the low-order portion of the divider field, and can |
||||
* be viewed as representing the portion of the divider that lies to |
||||
* the right of the decimal point. Most variable dividers have zero |
||||
* fractional bits. Variable dividers with non-zero fraction width |
||||
* still record a value 1 less than the value they represent; the |
||||
* added 1 does *not* affect the low-order bit in this case, it |
||||
* affects the bits above the fractional part only. (Often in this |
||||
* code a divider field value is distinguished from the value it |
||||
* represents by referring to the latter as a "divisor".) |
||||
* |
||||
* In order to avoid dealing with fractions, divider arithmetic is |
||||
* performed using "scaled" values. A scaled value is one that's |
||||
* been left-shifted by the fractional width of a divider. Dividing |
||||
* a scaled value by a scaled divisor produces the desired quotient |
||||
* without loss of precision and without any other special handling |
||||
* for fractions. |
||||
* |
||||
* The recorded value of a variable divider can be modified. To |
||||
* modify either divider (or both), a clock must be enabled (i.e., |
||||
* using its gate). In addition, a trigger register (described |
||||
* below) must be used to commit the change, and polled to verify |
||||
* the change is complete. |
||||
*/ |
||||
struct bcm_clk_div { |
||||
union { |
||||
struct { /* variable divider */ |
||||
u32 offset; /* divider register offset */ |
||||
u32 shift; /* field shift */ |
||||
u32 width; /* field width */ |
||||
u32 frac_width; /* field fraction width */ |
||||
|
||||
u64 scaled_div; /* scaled divider value */ |
||||
}; |
||||
u32 fixed; /* non-zero fixed divider value */ |
||||
}; |
||||
u32 flags; /* BCM_CLK_DIV_FLAGS_* below */ |
||||
}; |
||||
|
||||
/*
|
||||
* Divider flags: |
||||
* EXISTS means this divider exists |
||||
* FIXED means it is a fixed-rate divider |
||||
*/ |
||||
#define BCM_CLK_DIV_FLAGS_EXISTS ((u32)1 << 0) /* Divider is valid */ |
||||
#define BCM_CLK_DIV_FLAGS_FIXED ((u32)1 << 1) /* Fixed-value */ |
||||
|
||||
/* Divider initialization macros */ |
||||
|
||||
/* A fixed (non-zero) divider */ |
||||
#define FIXED_DIVIDER(_value) \ |
||||
{ \
|
||||
.fixed = (_value), \
|
||||
.flags = FLAG(DIV, EXISTS)|FLAG(DIV, FIXED), \
|
||||
} |
||||
|
||||
/* A divider with an integral divisor */ |
||||
#define DIVIDER(_offset, _shift, _width) \ |
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.shift = (_shift), \
|
||||
.width = (_width), \
|
||||
.scaled_div = BAD_SCALED_DIV_VALUE, \
|
||||
.flags = FLAG(DIV, EXISTS), \
|
||||
} |
||||
|
||||
/* A divider whose divisor has an integer and fractional part */ |
||||
#define FRAC_DIVIDER(_offset, _shift, _width, _frac_width) \ |
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.shift = (_shift), \
|
||||
.width = (_width), \
|
||||
.frac_width = (_frac_width), \
|
||||
.scaled_div = BAD_SCALED_DIV_VALUE, \
|
||||
.flags = FLAG(DIV, EXISTS), \
|
||||
} |
||||
|
||||
/*
|
||||
* Clocks may have multiple "parent" clocks. If there is more than |
||||
* one, a selector must be specified to define which of the parent |
||||
* clocks is currently in use. The selected clock is indicated in a |
||||
* sub-field of a 32-bit selector register. The range of |
||||
* representable selector values typically exceeds the number of |
||||
* available parent clocks. Occasionally the reset value of a |
||||
* selector field is explicitly set to a (specific) value that does |
||||
* not correspond to a defined input clock. |
||||
* |
||||
* We register all known parent clocks with the common clock code |
||||
* using a packed array (i.e., no empty slots) of (parent) clock |
||||
* names, and refer to them later using indexes into that array. |
||||
* We maintain an array of selector values indexed by common clock |
||||
* index values in order to map between these common clock indexes |
||||
* and the selector values used by the hardware. |
||||
* |
||||
* Like dividers, a selector can be modified, but to do so a clock |
||||
* must be enabled, and a trigger must be used to commit the change. |
||||
*/ |
||||
struct bcm_clk_sel { |
||||
u32 offset; /* selector register offset */ |
||||
u32 shift; /* field shift */ |
||||
u32 width; /* field width */ |
||||
|
||||
u32 parent_count; /* number of entries in parent_sel[] */ |
||||
u32 *parent_sel; /* array of parent selector values */ |
||||
u8 clk_index; /* current selected index in parent_sel[] */ |
||||
}; |
||||
|
||||
/* Selector initialization macro */ |
||||
#define SELECTOR(_offset, _shift, _width) \ |
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.shift = (_shift), \
|
||||
.width = (_width), \
|
||||
.clk_index = BAD_CLK_INDEX, \
|
||||
} |
||||
|
||||
/*
|
||||
* Making changes to a variable divider or a selector for a clock |
||||
* requires the use of a trigger. A trigger is defined by a single |
||||
* bit within a register. To signal a change, a 1 is written into |
||||
* that bit. To determine when the change has been completed, that |
||||
* trigger bit is polled; the read value will be 1 while the change |
||||
* is in progress, and 0 when it is complete. |
||||
* |
||||
* Occasionally a clock will have more than one trigger. In this |
||||
* case, the "pre-trigger" will be used when changing a clock's |
||||
* selector and/or its pre-divider. |
||||
*/ |
||||
struct bcm_clk_trig { |
||||
u32 offset; /* trigger register offset */ |
||||
u32 bit; /* trigger bit */ |
||||
u32 flags; /* BCM_CLK_TRIG_FLAGS_* below */ |
||||
}; |
||||
|
||||
/*
|
||||
* Trigger flags: |
||||
* EXISTS means this trigger exists |
||||
*/ |
||||
#define BCM_CLK_TRIG_FLAGS_EXISTS ((u32)1 << 0) /* Trigger is valid */ |
||||
|
||||
/* Trigger initialization macro */ |
||||
#define TRIGGER(_offset, _bit) \ |
||||
{ \
|
||||
.offset = (_offset), \
|
||||
.bit = (_bit), \
|
||||
.flags = FLAG(TRIG, EXISTS), \
|
||||
} |
||||
|
||||
struct bus_clk_data { |
||||
struct bcm_clk_gate gate; |
||||
}; |
||||
|
||||
struct core_clk_data { |
||||
struct bcm_clk_gate gate; |
||||
}; |
||||
|
||||
struct peri_clk_data { |
||||
struct bcm_clk_gate gate; |
||||
struct bcm_clk_trig pre_trig; |
||||
struct bcm_clk_div pre_div; |
||||
struct bcm_clk_trig trig; |
||||
struct bcm_clk_div div; |
||||
struct bcm_clk_sel sel; |
||||
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 */ |
||||
|
||||
struct refclk { |
||||
struct clk clk; |
||||
}; |
||||
|
||||
struct peri_clock { |
||||
struct clk clk; |
||||
struct peri_clk_data *data; |
||||
}; |
||||
|
||||
struct ccu_clock { |
||||
struct clk clk; |
||||
|
||||
int num_policy_masks; |
||||
unsigned long policy_freq_offset; |
||||
int freq_bit_shift; /* 8 for most CCUs */ |
||||
unsigned long policy_ctl_offset; |
||||
unsigned long policy0_mask_offset; |
||||
unsigned long policy1_mask_offset; |
||||
unsigned long policy2_mask_offset; |
||||
unsigned long policy3_mask_offset; |
||||
unsigned long policy0_mask2_offset; |
||||
unsigned long policy1_mask2_offset; |
||||
unsigned long policy2_mask2_offset; |
||||
unsigned long policy3_mask2_offset; |
||||
unsigned long lvm_en_offset; |
||||
|
||||
int freq_id; |
||||
unsigned long *freq_tbl; |
||||
}; |
||||
|
||||
struct bus_clock { |
||||
struct clk clk; |
||||
struct bus_clk_data *data; |
||||
unsigned long *freq_tbl; |
||||
}; |
||||
|
||||
struct ref_clock { |
||||
struct clk clk; |
||||
}; |
||||
|
||||
static inline int is_same_clock(struct clk *a, struct clk *b) |
||||
{ |
||||
return (a == b); |
||||
} |
||||
|
||||
#define to_clk(p) (&((p)->clk)) |
||||
#define name_to_clk(name) (&((name##_clk).clk)) |
||||
/* declare a struct clk_lookup */ |
||||
#define CLK_LK(name) \ |
||||
{.con_id = __stringify(name##_clk), .clk = name_to_clk(name),} |
||||
|
||||
static inline struct refclk *to_refclk(struct clk *clock) |
||||
{ |
||||
return container_of(clock, struct refclk, clk); |
||||
} |
||||
|
||||
static inline struct peri_clock *to_peri_clk(struct clk *clock) |
||||
{ |
||||
return container_of(clock, struct peri_clock, clk); |
||||
} |
||||
|
||||
static inline struct ccu_clock *to_ccu_clk(struct clk *clock) |
||||
{ |
||||
return container_of(clock, struct ccu_clock, clk); |
||||
} |
||||
|
||||
static inline struct bus_clock *to_bus_clk(struct clk *clock) |
||||
{ |
||||
return container_of(clock, struct bus_clock, clk); |
||||
} |
||||
|
||||
static inline struct ref_clock *to_ref_clk(struct clk *clock) |
||||
{ |
||||
return container_of(clock, struct ref_clock, clk); |
||||
} |
||||
|
||||
extern struct clk_ops peri_clk_ops; |
||||
extern struct clk_ops ccu_clk_ops; |
||||
extern struct clk_ops bus_clk_ops; |
||||
extern struct clk_ops ref_clk_ops; |
||||
|
||||
extern int clk_get_and_enable(char *clkstr); |
@ -0,0 +1,73 @@ |
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <asm/io.h> |
||||
#include <asm/errno.h> |
||||
#include <asm/arch/sysmap.h> |
||||
#include <asm/kona-common/clk.h> |
||||
#include "clk-core.h" |
||||
|
||||
/* Enable appropriate clocks for an SDIO port */ |
||||
int clk_sdio_enable(void *base, u32 rate, u32 *actual_ratep) |
||||
{ |
||||
int ret; |
||||
struct clk *c; |
||||
|
||||
char *clkstr; |
||||
char *slpstr; |
||||
char *ahbstr; |
||||
|
||||
switch ((u32) base) { |
||||
case CONFIG_SYS_SDIO_BASE0: |
||||
clkstr = CONFIG_SYS_SDIO0 "_clk"; |
||||
ahbstr = CONFIG_SYS_SDIO0 "_ahb_clk"; |
||||
slpstr = CONFIG_SYS_SDIO0 "_sleep_clk"; |
||||
break; |
||||
case CONFIG_SYS_SDIO_BASE1: |
||||
clkstr = CONFIG_SYS_SDIO1 "_clk"; |
||||
ahbstr = CONFIG_SYS_SDIO1 "_ahb_clk"; |
||||
slpstr = CONFIG_SYS_SDIO1 "_sleep_clk"; |
||||
break; |
||||
case CONFIG_SYS_SDIO_BASE2: |
||||
clkstr = CONFIG_SYS_SDIO2 "_clk"; |
||||
ahbstr = CONFIG_SYS_SDIO2 "_ahb_clk"; |
||||
slpstr = CONFIG_SYS_SDIO2 "_sleep_clk"; |
||||
break; |
||||
case CONFIG_SYS_SDIO_BASE3: |
||||
clkstr = CONFIG_SYS_SDIO3 "_clk"; |
||||
ahbstr = CONFIG_SYS_SDIO3 "_ahb_clk"; |
||||
slpstr = CONFIG_SYS_SDIO3 "_sleep_clk"; |
||||
break; |
||||
default: |
||||
printf("%s: base 0x%p not found\n", __func__, base); |
||||
return -EINVAL; |
||||
} |
||||
|
||||
ret = clk_get_and_enable(ahbstr); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
ret = clk_get_and_enable(slpstr); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
c = clk_get(clkstr); |
||||
if (c) { |
||||
ret = clk_set_rate(c, rate); |
||||
if (ret) |
||||
return ret; |
||||
|
||||
ret = clk_enable(c); |
||||
if (ret) |
||||
return ret; |
||||
} else { |
||||
printf("%s: Couldn't find %s\n", __func__, clkstr); |
||||
return -EINVAL; |
||||
} |
||||
*actual_ratep = rate; |
||||
return 0; |
||||
} |
@ -0,0 +1,27 @@ |
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <asm/io.h> |
||||
#include <asm/arch/sysmap.h> |
||||
|
||||
#define EN_MASK 0x08000000 /* Enable timer */ |
||||
#define SRSTEN_MASK 0x04000000 /* Enable soft reset */ |
||||
#define CLKS_SHIFT 20 /* Clock period shift */ |
||||
#define LD_SHIFT 0 /* Reload value shift */ |
||||
|
||||
void reset_cpu(ulong ignored) |
||||
{ |
||||
/*
|
||||
* Set WD enable, RST enable, |
||||
* 3.9 msec clock period (8), reload value (8*3.9ms) |
||||
*/ |
||||
u32 reg = EN_MASK + SRSTEN_MASK + (8 << CLKS_SHIFT) + (8 << LD_SHIFT); |
||||
writel(reg, SECWD2_BASE_ADDR); |
||||
|
||||
while (1) |
||||
; /* loop forever till reset */ |
||||
} |
@ -0,0 +1,15 @@ |
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#ifndef __ARCH_BCM281XX_GPIO_H |
||||
#define __ARCH_BCM281XX_GPIO_H |
||||
|
||||
/*
|
||||
* Empty file - cmd_gpio.c requires this. The implementation |
||||
* is in drivers/gpio/kona_gpio.c instead of inlined here. |
||||
*/ |
||||
|
||||
#endif |
@ -0,0 +1,25 @@ |
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#ifndef __ARCH_BCM281XX_SYSMAP_H |
||||
|
||||
#define BSC1_BASE_ADDR 0x3e016000 |
||||
#define BSC2_BASE_ADDR 0x3e017000 |
||||
#define BSC3_BASE_ADDR 0x3e018000 |
||||
#define GPIO2_BASE_ADDR 0x35003000 |
||||
#define KONA_MST_CLK_BASE_ADDR 0x3f001000 |
||||
#define KONA_SLV_CLK_BASE_ADDR 0x3e011000 |
||||
#define PMU_BSC_BASE_ADDR 0x3500d000 |
||||
#define PWRMGR_BASE_ADDR 0x35010000 |
||||
#define SDIO1_BASE_ADDR 0x3f180000 |
||||
#define SDIO2_BASE_ADDR 0x3f190000 |
||||
#define SDIO3_BASE_ADDR 0x3f1a0000 |
||||
#define SDIO4_BASE_ADDR 0x3f1b0000 |
||||
#define SECWD_BASE_ADDR 0x3500c000 |
||||
#define SECWD2_BASE_ADDR 0x35002f40 |
||||
#define TIMER_BASE_ADDR 0x3e00d000 |
||||
|
||||
#endif |
@ -0,0 +1,58 @@ |
||||
/*
|
||||
* Copyright 2013 Broadcom Corporation. |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
/*
|
||||
* Bitfield operations |
||||
* |
||||
* These are generic bitfield operations which allow manipulation of variable |
||||
* width bitfields within a word. One use of this would be to use data tables |
||||
* to determine how to reprogram fields within R/W hardware registers. |
||||
* |
||||
* Example: |
||||
* |
||||
* old_reg_val |
||||
* +--------+----+---+--+-----+----------+ |
||||
* | | | | | old | | |
||||
* +--------+----+---+--+-----+----------+ |
||||
* |
||||
* new_reg_val |
||||
* +--------+----+---+--+-----+----------+ |
||||
* | | | | | new | | |
||||
* +--------+----+---+--+-----+----------+ |
||||
* |
||||
* mask = bitfield_mask(10, 5); |
||||
* old = bitfield_extract(old_reg_val, 10, 5); |
||||
* new_reg_val = bitfield_replace(old_reg_val, 10, 5, new); |
||||
* |
||||
* The numbers 10 and 5 could for example come from data |
||||
* tables which describe all bitfields in all registers. |
||||
*/ |
||||
|
||||
#include <linux/types.h> |
||||
|
||||
/* Produces a mask of set bits covering a range of a uint value */ |
||||
static inline uint bitfield_mask(uint shift, uint width) |
||||
{ |
||||
return ((1 << width) - 1) << shift; |
||||
} |
||||
|
||||
/* Extract the value of a bitfield found within a given register value */ |
||||
static inline uint bitfield_extract(uint reg_val, uint shift, uint width) |
||||
{ |
||||
return (reg_val & bitfield_mask(shift, width)) >> shift; |
||||
} |
||||
|
||||
/*
|
||||
* Replace the value of a bitfield found within a given register value |
||||
* Returns the newly modified uint value with the replaced field. |
||||
*/ |
||||
static inline uint bitfield_replace(uint reg_val, uint shift, uint width, |
||||
uint bitfield_val) |
||||
{ |
||||
uint mask = bitfield_mask(shift, width); |
||||
|
||||
return (reg_val & ~mask) | (bitfield_val << shift); |
||||
} |
Loading…
Reference in new issue