Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
ffa2d04
Documentation
LICENSES
arch
block
certs
crypto
drivers
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cdrom
char
clk
actions
analogbits
at91
axis
axs10x
baikal-t1
bcm
berlin
davinci
h8300
hisilicon
imgtec
imx
ingenic
keystone
loongson1
mediatek
meson
microchip
mmp
mstar
mvebu
mxs
nxp
pistachio
pxa
qcom
ralink
renesas
rockchip
samsung
sifive
socfpga
spear
sprd
st
starfive
stm32
Makefile
clk-stm32-core.c
clk-stm32-core.h
clk-stm32mp13.c
reset-stm32.c
reset-stm32.h
stm32mp13_rcc.h
sunxi-ng
sunxi
tegra
ti
uniphier
ux500
versatile
visconti
x86
xilinx
zynq
zynqmp
.kunitconfig
Kconfig
Makefile
clk-apple-nco.c
clk-asm9260.c
clk-aspeed.c
clk-aspeed.h
clk-ast2600.c
clk-axi-clkgen.c
clk-axm5516.c
clk-bd718x7.c
clk-bm1880.c
clk-bulk.c
clk-cdce706.c
clk-cdce925.c
clk-clps711x.c
clk-composite.c
clk-conf.c
clk-cs2000-cp.c
clk-devres.c
clk-divider.c
clk-fixed-factor.c
clk-fixed-mmio.c
clk-fixed-rate.c
clk-fractional-divider.c
clk-fractional-divider.h
clk-fsl-flexspi.c
clk-fsl-sai.c
clk-gate.c
clk-gate_test.c
clk-gemini.c
clk-gpio.c
clk-hi655x.c
clk-highbank.c
clk-hsdk-pll.c
clk-k210.c
clk-lan966x.c
clk-lmk04832.c
clk-lochnagar.c
clk-max77686.c
clk-max9485.c
clk-milbeaut.c
clk-moxart.c
clk-multiplier.c
clk-mux.c
clk-nomadik.c
clk-npcm7xx.c
clk-nspire.c
clk-oxnas.c
clk-palmas.c
clk-plldig.c
clk-pwm.c
clk-qoriq.c
clk-renesas-pcie.c
clk-rk808.c
clk-s2mps11.c
clk-scmi.c
clk-scpi.c
clk-si514.c
clk-si5341.c
clk-si5351.c
clk-si5351.h
clk-si544.c
clk-si570.c
clk-sparx5.c
clk-stm32f4.c
clk-stm32h7.c
clk-stm32mp1.c
clk-tps68470.c
clk-twl6040.c
clk-versaclock5.c
clk-vt8500.c
clk-wm831x.c
clk-xgene.c
clk.c
clk.h
clk_test.c
clkdev.c
clocksource
comedi
connector
counter
cpufreq
cpuidle
crypto
cxl
dax
dca
devfreq
dio
dma-buf
dma
edac
eisa
extcon
firewire
firmware
fpga
fsi
gnss
gpio
gpu
greybus
hid
hsi
hv
hwmon
hwspinlock
hwtracing
i2c
i3c
idle
iio
infiniband
input
interconnect
iommu
ipack
irqchip
isdn
leds
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
most
mtd
mux
net
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
opp
parisc
parport
pci
pcmcia
peci
perf
phy
pinctrl
platform
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sh
siox
slimbus
soc
soundwire
spi
spmi
ssb
staging
target
tc
tee
thermal
thunderbolt
tty
uio
usb
vdpa
vfio
vhost
video
virt
virtio
visorbus
vlynq
vme
w1
watchdog
xen
zorro
Kconfig
Makefile
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
drivers
/
clk
/
stm32
/
clk-stm32-core.c
Blame
Blame
Latest commit
History
History
641 lines (489 loc) · 15.7 KB
Breadcrumbs
linux
/
drivers
/
clk
/
stm32
/
clk-stm32-core.c
Top
File metadata and controls
Code
Blame
641 lines (489 loc) · 15.7 KB
Raw
// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics 2022 - All Rights Reserved * Author: Gabriel Fernandez <gabriel.fernandez@foss.st.com> for STMicroelectronics. */ #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> #include <linux/io.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/slab.h> #include <linux/spinlock.h> #include "clk-stm32-core.h" #include "reset-stm32.h" static DEFINE_SPINLOCK(rlock); static int stm32_rcc_clock_init(struct device *dev, const struct of_device_id *match, void __iomem *base) { const struct stm32_rcc_match_data *data = match->data; struct clk_hw_onecell_data *clk_data = data->hw_clks; struct device_node *np = dev_of_node(dev); struct clk_hw **hws; int n, max_binding; max_binding = data->maxbinding; clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, max_binding), GFP_KERNEL); if (!clk_data) return -ENOMEM; clk_data->num = max_binding; hws = clk_data->hws; for (n = 0; n < max_binding; n++) hws[n] = ERR_PTR(-ENOENT); for (n = 0; n < data->num_clocks; n++) { const struct clock_config *cfg_clock = &data->tab_clocks[n]; struct clk_hw *hw = ERR_PTR(-ENOENT); if (data->check_security && data->check_security(base, cfg_clock)) continue; if (cfg_clock->func) hw = (*cfg_clock->func)(dev, data, base, &rlock, cfg_clock); if (IS_ERR(hw)) { dev_err(dev, "Can't register clk %d: %ld\n", n, PTR_ERR(hw)); return PTR_ERR(hw); } if (cfg_clock->id != NO_ID) hws[cfg_clock->id] = hw; } return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); } int stm32_rcc_init(struct device *dev, const struct of_device_id *match_data, void __iomem *base) { const struct of_device_id *match; int err; match = of_match_node(match_data, dev_of_node(dev)); if (!match) { dev_err(dev, "match data not found\n"); return -ENODEV; } /* RCC Reset Configuration */ err = stm32_rcc_reset_init(dev, match, base); if (err) { pr_err("stm32 reset failed to initialize\n"); return err; } /* RCC Clock Configuration */ err = stm32_rcc_clock_init(dev, match, base); if (err) { pr_err("stm32 clock failed to initialize\n"); return err; } return 0; } static u8 stm32_mux_get_parent(void __iomem *base, struct clk_stm32_clock_data *data, u16 mux_id) { const struct stm32_mux_cfg *mux = &data->muxes[mux_id]; u32 mask = BIT(mux->width) - 1; u32 val; val = readl(base + mux->offset) >> mux->shift; val &= mask; return val; } static int stm32_mux_set_parent(void __iomem *base, struct clk_stm32_clock_data *data, u16 mux_id, u8 index) { const struct stm32_mux_cfg *mux = &data->muxes[mux_id]; u32 mask = BIT(mux->width) - 1; u32 reg = readl(base + mux->offset); u32 val = index << mux->shift; reg &= ~(mask << mux->shift); reg |= val; writel(reg, base + mux->offset); return 0; } static void stm32_gate_endisable(void __iomem *base, struct clk_stm32_clock_data *data, u16 gate_id, int enable) { const struct stm32_gate_cfg *gate = &data->gates[gate_id]; void __iomem *addr = base + gate->offset; if (enable) { if (data->gate_cpt[gate_id]++ > 0) return; if (gate->set_clr != 0) writel(BIT(gate->bit_idx), addr); else writel(readl(addr) | BIT(gate->bit_idx), addr); } else { if (--data->gate_cpt[gate_id] > 0) return; if (gate->set_clr != 0) writel(BIT(gate->bit_idx), addr + gate->set_clr); else writel(readl(addr) & ~BIT(gate->bit_idx), addr); } } static void stm32_gate_disable_unused(void __iomem *base, struct clk_stm32_clock_data *data, u16 gate_id) { const struct stm32_gate_cfg *gate = &data->gates[gate_id]; void __iomem *addr = base + gate->offset; if (data->gate_cpt[gate_id] > 0) return; if (gate->set_clr != 0) writel(BIT(gate->bit_idx), addr + gate->set_clr); else writel(readl(addr) & ~BIT(gate->bit_idx), addr); } static int stm32_gate_is_enabled(void __iomem *base, struct clk_stm32_clock_data *data, u16 gate_id) { const struct stm32_gate_cfg *gate = &data->gates[gate_id]; return (readl(base + gate->offset) & BIT(gate->bit_idx)) != 0; } static unsigned int _get_table_div(const struct clk_div_table *table, unsigned int val) { const struct clk_div_table *clkt; for (clkt = table; clkt->div; clkt++) if (clkt->val == val) return clkt->div; return 0; } static unsigned int _get_div(const struct clk_div_table *table, unsigned int val, unsigned long flags, u8 width) { if (flags & CLK_DIVIDER_ONE_BASED) return val; if (flags & CLK_DIVIDER_POWER_OF_TWO) return 1 << val; if (table) return _get_table_div(table, val); return val + 1; } static unsigned long stm32_divider_get_rate(void __iomem *base, struct clk_stm32_clock_data *data, u16 div_id, unsigned long parent_rate) { const struct stm32_div_cfg *divider = &data->dividers[div_id]; unsigned int val; unsigned int div; val = readl(base + divider->offset) >> divider->shift; val &= clk_div_mask(divider->width); div = _get_div(divider->table, val, divider->flags, divider->width); if (!div) { WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO), "%d: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", div_id); return parent_rate; } return DIV_ROUND_UP_ULL((u64)parent_rate, div); } static int stm32_divider_set_rate(void __iomem *base, struct clk_stm32_clock_data *data, u16 div_id, unsigned long rate, unsigned long parent_rate) { const struct stm32_div_cfg *divider = &data->dividers[div_id]; int value; u32 val; value = divider_get_val(rate, parent_rate, divider->table, divider->width, divider->flags); if (value < 0) return value; if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { val = clk_div_mask(divider->width) << (divider->shift + 16); } else { val = readl(base + divider->offset); val &= ~(clk_div_mask(divider->width) << divider->shift); } val |= (u32)value << divider->shift; writel(val, base + divider->offset); return 0; } static u8 clk_stm32_mux_get_parent(struct clk_hw *hw) { struct clk_stm32_mux *mux = to_clk_stm32_mux(hw); return stm32_mux_get_parent(mux->base, mux->clock_data, mux->mux_id); } static int clk_stm32_mux_set_parent(struct clk_hw *hw, u8 index) { struct clk_stm32_mux *mux = to_clk_stm32_mux(hw); unsigned long flags = 0; spin_lock_irqsave(mux->lock, flags); stm32_mux_set_parent(mux->base, mux->clock_data, mux->mux_id, index); spin_unlock_irqrestore(mux->lock, flags); return 0; } const struct clk_ops clk_stm32_mux_ops = { .get_parent = clk_stm32_mux_get_parent, .set_parent = clk_stm32_mux_set_parent, }; static void clk_stm32_gate_endisable(struct clk_hw *hw, int enable) { struct clk_stm32_gate *gate = to_clk_stm32_gate(hw); unsigned long flags = 0; spin_lock_irqsave(gate->lock, flags); stm32_gate_endisable(gate->base, gate->clock_data, gate->gate_id, enable); spin_unlock_irqrestore(gate->lock, flags); } static int clk_stm32_gate_enable(struct clk_hw *hw) { clk_stm32_gate_endisable(hw, 1); return 0; } static void clk_stm32_gate_disable(struct clk_hw *hw) { clk_stm32_gate_endisable(hw, 0); } static int clk_stm32_gate_is_enabled(struct clk_hw *hw) { struct clk_stm32_gate *gate = to_clk_stm32_gate(hw); return stm32_gate_is_enabled(gate->base, gate->clock_data, gate->gate_id); } static void clk_stm32_gate_disable_unused(struct clk_hw *hw) { struct clk_stm32_gate *gate = to_clk_stm32_gate(hw); unsigned long flags = 0; spin_lock_irqsave(gate->lock, flags); stm32_gate_disable_unused(gate->base, gate->clock_data, gate->gate_id); spin_unlock_irqrestore(gate->lock, flags); } const struct clk_ops clk_stm32_gate_ops = { .enable = clk_stm32_gate_enable, .disable = clk_stm32_gate_disable, .is_enabled = clk_stm32_gate_is_enabled, .disable_unused = clk_stm32_gate_disable_unused, }; static int clk_stm32_divider_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_stm32_div *div = to_clk_stm32_divider(hw); unsigned long flags = 0; int ret; if (div->div_id == NO_STM32_DIV) return rate; spin_lock_irqsave(div->lock, flags); ret = stm32_divider_set_rate(div->base, div->clock_data, div->div_id, rate, parent_rate); spin_unlock_irqrestore(div->lock, flags); return ret; } static long clk_stm32_divider_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { struct clk_stm32_div *div = to_clk_stm32_divider(hw); const struct stm32_div_cfg *divider; if (div->div_id == NO_STM32_DIV) return rate; divider = &div->clock_data->dividers[div->div_id]; /* if read only, just return current value */ if (divider->flags & CLK_DIVIDER_READ_ONLY) { u32 val; val = readl(div->base + divider->offset) >> divider->shift; val &= clk_div_mask(divider->width); return divider_ro_round_rate(hw, rate, prate, divider->table, divider->width, divider->flags, val); } return divider_round_rate_parent(hw, clk_hw_get_parent(hw), rate, prate, divider->table, divider->width, divider->flags); } static unsigned long clk_stm32_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_stm32_div *div = to_clk_stm32_divider(hw); if (div->div_id == NO_STM32_DIV) return parent_rate; return stm32_divider_get_rate(div->base, div->clock_data, div->div_id, parent_rate); } const struct clk_ops clk_stm32_divider_ops = { .recalc_rate = clk_stm32_divider_recalc_rate, .round_rate = clk_stm32_divider_round_rate, .set_rate = clk_stm32_divider_set_rate, }; static int clk_stm32_composite_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); unsigned long flags = 0; int ret; if (composite->div_id == NO_STM32_DIV) return rate; spin_lock_irqsave(composite->lock, flags); ret = stm32_divider_set_rate(composite->base, composite->clock_data, composite->div_id, rate, parent_rate); spin_unlock_irqrestore(composite->lock, flags); return ret; } static unsigned long clk_stm32_composite_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); if (composite->div_id == NO_STM32_DIV) return parent_rate; return stm32_divider_get_rate(composite->base, composite->clock_data, composite->div_id, parent_rate); } static long clk_stm32_composite_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); const struct stm32_div_cfg *divider; if (composite->div_id == NO_STM32_DIV) return rate; divider = &composite->clock_data->dividers[composite->div_id]; /* if read only, just return current value */ if (divider->flags & CLK_DIVIDER_READ_ONLY) { u32 val; val = readl(composite->base + divider->offset) >> divider->shift; val &= clk_div_mask(divider->width); return divider_ro_round_rate(hw, rate, prate, divider->table, divider->width, divider->flags, val); } return divider_round_rate_parent(hw, clk_hw_get_parent(hw), rate, prate, divider->table, divider->width, divider->flags); } static u8 clk_stm32_composite_get_parent(struct clk_hw *hw) { struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); return stm32_mux_get_parent(composite->base, composite->clock_data, composite->mux_id); } static int clk_stm32_composite_set_parent(struct clk_hw *hw, u8 index) { struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); unsigned long flags = 0; spin_lock_irqsave(composite->lock, flags); stm32_mux_set_parent(composite->base, composite->clock_data, composite->mux_id, index); spin_unlock_irqrestore(composite->lock, flags); if (composite->clock_data->is_multi_mux) { struct clk_hw *other_mux_hw = composite->clock_data->is_multi_mux(hw); if (other_mux_hw) { struct clk_hw *hwp = clk_hw_get_parent_by_index(hw, index); clk_hw_reparent(other_mux_hw, hwp); } } return 0; } static int clk_stm32_composite_is_enabled(struct clk_hw *hw) { struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); if (composite->gate_id == NO_STM32_GATE) return (__clk_get_enable_count(hw->clk) > 0); return stm32_gate_is_enabled(composite->base, composite->clock_data, composite->gate_id); } static void clk_stm32_composite_gate_endisable(struct clk_hw *hw, int enable) { struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); unsigned long flags = 0; spin_lock_irqsave(composite->lock, flags); stm32_gate_endisable(composite->base, composite->clock_data, composite->gate_id, enable); spin_unlock_irqrestore(composite->lock, flags); } static int clk_stm32_composite_gate_enable(struct clk_hw *hw) { struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); if (composite->gate_id == NO_STM32_GATE) return 0; clk_stm32_composite_gate_endisable(hw, 1); return 0; } static void clk_stm32_composite_gate_disable(struct clk_hw *hw) { struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); if (composite->gate_id == NO_STM32_GATE) return; clk_stm32_composite_gate_endisable(hw, 0); } static void clk_stm32_composite_disable_unused(struct clk_hw *hw) { struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); unsigned long flags = 0; if (composite->gate_id == NO_STM32_GATE) return; spin_lock_irqsave(composite->lock, flags); stm32_gate_disable_unused(composite->base, composite->clock_data, composite->gate_id); spin_unlock_irqrestore(composite->lock, flags); } const struct clk_ops clk_stm32_composite_ops = { .set_rate = clk_stm32_composite_set_rate, .recalc_rate = clk_stm32_composite_recalc_rate, .round_rate = clk_stm32_composite_round_rate, .get_parent = clk_stm32_composite_get_parent, .set_parent = clk_stm32_composite_set_parent, .enable = clk_stm32_composite_gate_enable, .disable = clk_stm32_composite_gate_disable, .is_enabled = clk_stm32_composite_is_enabled, .disable_unused = clk_stm32_composite_disable_unused, }; struct clk_hw *clk_stm32_mux_register(struct device *dev, const struct stm32_rcc_match_data *data, void __iomem *base, spinlock_t *lock, const struct clock_config *cfg) { struct clk_stm32_mux *mux = cfg->clock_cfg; struct clk_hw *hw = &mux->hw; int err; mux->base = base; mux->lock = lock; mux->clock_data = data->clock_data; err = clk_hw_register(dev, hw); if (err) return ERR_PTR(err); return hw; } struct clk_hw *clk_stm32_gate_register(struct device *dev, const struct stm32_rcc_match_data *data, void __iomem *base, spinlock_t *lock, const struct clock_config *cfg) { struct clk_stm32_gate *gate = cfg->clock_cfg; struct clk_hw *hw = &gate->hw; int err; gate->base = base; gate->lock = lock; gate->clock_data = data->clock_data; err = clk_hw_register(dev, hw); if (err) return ERR_PTR(err); return hw; } struct clk_hw *clk_stm32_div_register(struct device *dev, const struct stm32_rcc_match_data *data, void __iomem *base, spinlock_t *lock, const struct clock_config *cfg) { struct clk_stm32_div *div = cfg->clock_cfg; struct clk_hw *hw = &div->hw; int err; div->base = base; div->lock = lock; div->clock_data = data->clock_data; err = clk_hw_register(dev, hw); if (err) return ERR_PTR(err); return hw; } struct clk_hw *clk_stm32_composite_register(struct device *dev, const struct stm32_rcc_match_data *data, void __iomem *base, spinlock_t *lock, const struct clock_config *cfg) { struct clk_stm32_composite *composite = cfg->clock_cfg; struct clk_hw *hw = &composite->hw; int err; composite->base = base; composite->lock = lock; composite->clock_data = data->clock_data; err = clk_hw_register(dev, hw); if (err) return ERR_PTR(err); return hw; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
You can’t perform that action at this time.