diff options
246 files changed, 11421 insertions, 2310 deletions
diff --git a/Documentation/conf.py b/Documentation/conf.py index f9054ab60cb1..63857d33778c 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -271,10 +271,29 @@ latex_elements = { # Additional stuff for the LaTeX preamble. 'preamble': ''' - \\usepackage{ifthen} + % Use some font with UTF-8 support with XeLaTeX + \\usepackage{fontspec} + \\setsansfont{DejaVu Serif} + \\setromanfont{DejaVu Sans} + \\setmonofont{DejaVu Sans Mono} + + ''' +} + +# Fix reference escape troubles with Sphinx 1.4.x +if major == 1 and minor > 3: + latex_elements['preamble'] += '\\renewcommand*{\\DUrole}[2]{ #2 }\n' + +if major == 1 and minor <= 4: + latex_elements['preamble'] += '\\usepackage[margin=0.5in, top=1in, bottom=1in]{geometry}' +elif major == 1 and (minor > 5 or (minor == 5 and patch >= 3)): + latex_elements['sphinxsetup'] = 'hmargin=0.5in, vmargin=1in' + latex_elements['preamble'] += '\\fvset{fontsize=auto}\n' - % Allow generate some pages in landscape - \\usepackage{lscape} +# Customize notice background colors on Sphinx < 1.6: +if major == 1 and minor < 6: + latex_elements['preamble'] += ''' + \\usepackage{ifthen} % Put notes in color and let them be inside a table \\definecolor{NoteColor}{RGB}{204,255,255} @@ -325,27 +344,26 @@ latex_elements = { } \\makeatother - % Use some font with UTF-8 support with XeLaTeX - \\usepackage{fontspec} - \\setsansfont{DejaVu Serif} - \\setromanfont{DejaVu Sans} - \\setmonofont{DejaVu Sans Mono} - - % To allow adjusting table sizes - \\usepackage{adjustbox} - ''' -} - -# Fix reference escape troubles with Sphinx 1.4.x -if major == 1 and minor > 3: - latex_elements['preamble'] += '\\renewcommand*{\\DUrole}[2]{ #2 }\n' -if major == 1 and minor <= 4: - latex_elements['preamble'] += '\\usepackage[margin=0.5in, top=1in, bottom=1in]{geometry}' -elif major == 1 and (minor > 5 or (minor == 5 and patch >= 3)): - latex_elements['sphinxsetup'] = 'hmargin=0.5in, vmargin=1in' - latex_elements['preamble'] += '\\fvset{fontsize=auto}\n' +# With Sphinx 1.6, it is possible to change the Bg color directly +# by using: +# \definecolor{sphinxnoteBgColor}{RGB}{204,255,255} +# \definecolor{sphinxwarningBgColor}{RGB}{255,204,204} +# \definecolor{sphinxattentionBgColor}{RGB}{255,255,204} +# \definecolor{sphinximportantBgColor}{RGB}{192,255,204} +# +# However, it require to use sphinx heavy box with: +# +# \renewenvironment{sphinxlightbox} {% +# \\begin{sphinxheavybox} +# } +# \\end{sphinxheavybox} +# } +# +# Unfortunately, the implementation is buggy: if a note is inside a +# table, it isn't displayed well. So, for now, let's use boring +# black and white notes. # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, diff --git a/Documentation/devicetree/bindings/clock/amlogic,gxbb-aoclkc.txt b/Documentation/devicetree/bindings/clock/amlogic,gxbb-aoclkc.txt index faa6d8ac5834..786dc39ca904 100644 --- a/Documentation/devicetree/bindings/clock/amlogic,gxbb-aoclkc.txt +++ b/Documentation/devicetree/bindings/clock/amlogic,gxbb-aoclkc.txt @@ -5,9 +5,11 @@ controllers within the Always-On part of the SoC. Required Properties: -- compatible: should be "amlogic,gxbb-aoclkc" -- reg: physical base address of the clock controller and length of memory - mapped region. +- compatible: value should be different for each SoC family as : + - GXBB (S905) : "amlogic,meson-gxbb-aoclkc" + - GXL (S905X, S905D) : "amlogic,meson-gxl-aoclkc" + - GXM (S912) : "amlogic,meson-gxm-aoclkc" + followed by the common "amlogic,meson-gx-aoclkc" - #clock-cells: should be 1. @@ -23,14 +25,22 @@ to specify the reset which they consume. All available resets are defined as preprocessor macros in the dt-bindings/reset/gxbb-aoclkc.h header and can be used in device tree sources. +Parent node should have the following properties : +- compatible: "amlogic,meson-gx-ao-sysctrl", "syscon", "simple-mfd" +- reg: base address and size of the AO system control register space. + Example: AO Clock controller node: - clkc_AO: clock-controller@040 { - compatible = "amlogic,gxbb-aoclkc"; - reg = <0x0 0x040 0x0 0x4>; +ao_sysctrl: sys-ctrl@0 { + compatible = "amlogic,meson-gx-ao-sysctrl", "syscon", "simple-mfd"; + reg = <0x0 0x0 0x0 0x100>; + + clkc_AO: clock-controller { + compatible = "amlogic,meson-gxbb-aoclkc", "amlogic,meson-gx-aoclkc"; #clock-cells = <1>; #reset-cells = <1>; }; +}; Example: UART controller node that consumes the clock and reset generated by the clock controller: diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt b/Documentation/devicetree/bindings/clock/at91-clock.txt index 5f3ad65daf69..51c259a92d02 100644 --- a/Documentation/devicetree/bindings/clock/at91-clock.txt +++ b/Documentation/devicetree/bindings/clock/at91-clock.txt @@ -81,6 +81,16 @@ Required properties: "atmel,sama5d2-clk-generated": at91 generated clock + "atmel,sama5d2-clk-audio-pll-frac": + at91 audio fractional pll + + "atmel,sama5d2-clk-audio-pll-pad": + at91 audio pll CLK_AUDIO output pin + + "atmel,sama5d2-clk-audio-pll-pmc" + at91 audio pll output on AUDIOPLLCLK that feeds the PMC + and can be used by peripheral clock or generic clock + Required properties for SCKC node: - reg : defines the IO memory reserved for the SCKC. - #size-cells : shall be 0 (reg is used to encode clk id). diff --git a/Documentation/devicetree/bindings/clock/idt,versaclock5.txt b/Documentation/devicetree/bindings/clock/idt,versaclock5.txt index 53d7e50ed875..05a245c9df08 100644 --- a/Documentation/devicetree/bindings/clock/idt,versaclock5.txt +++ b/Documentation/devicetree/bindings/clock/idt,versaclock5.txt @@ -1,24 +1,32 @@ -Binding for IDT VersaClock5 programmable i2c clock generator. +Binding for IDT VersaClock 5,6 programmable i2c clock generators. -The IDT VersaClock5 are programmable i2c clock generators providing -from 3 to 12 output clocks. +The IDT VersaClock 5 and VersaClock 6 are programmable i2c clock +generators providing from 3 to 12 output clocks. ==I2C device node== Required properties: -- compatible: shall be one of "idt,5p49v5923" , "idt,5p49v5933" , - "idt,5p49v5935". +- compatible: shall be one of + "idt,5p49v5923" + "idt,5p49v5925" + "idt,5p49v5933" + "idt,5p49v5935" + "idt,5p49v6901" - reg: i2c device address, shall be 0x68 or 0x6a. - #clock-cells: from common clock binding; shall be set to 1. - clocks: from common clock binding; list of parent clock handles, - - 5p49v5923: (required) either or both of XTAL or CLKIN + - 5p49v5923 and + 5p49v5925 and + 5p49v6901: (required) either or both of XTAL or CLKIN reference clock. - 5p49v5933 and - 5p49v5935: (optional) property not present (internal Xtal used) or CLKIN reference clock. - clock-names: from common clock binding; clock input names, can be - - 5p49v5923: (required) either or both of "xin", "clkin". + - 5p49v5923 and + 5p49v5925 and + 5p49v6901: (required) either or both of "xin", "clkin". - 5p49v5933 and - 5p49v5935: (optional) property not present or "clkin". @@ -37,6 +45,7 @@ clock specifier, the following mapping applies: 1 -- OUT1 2 -- OUT4 +5P49V5925 and 5P49V5935: 0 -- OUT0_SEL_I2CB 1 -- OUT1 @@ -44,6 +53,13 @@ clock specifier, the following mapping applies: 3 -- OUT3 4 -- OUT4 +5P49V6901: + 0 -- OUT0_SEL_I2CB + 1 -- OUT1 + 2 -- OUT2 + 3 -- OUT3 + 4 -- OUT4 + ==Example== /* 25MHz reference crystal */ diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.txt index 707a686d8d3e..316e13686568 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.txt +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.txt @@ -22,6 +22,7 @@ Required Properties: - "renesas,r8a7794-cpg-mssr" for the r8a7794 SoC (R-Car E2) - "renesas,r8a7795-cpg-mssr" for the r8a7795 SoC (R-Car H3) - "renesas,r8a7796-cpg-mssr" for the r8a7796 SoC (R-Car M3-W) + - "renesas,r8a77995-cpg-mssr" for the r8a77995 SoC (R-Car D3) - reg: Base address and length of the memory resource used by the CPG/MSSR block @@ -30,7 +31,7 @@ Required Properties: clock-names - clock-names: List of external parent clock names. Valid names are: - "extal" (r8a7743, r8a7745, r8a7790, r8a7791, r8a7792, r8a7793, r8a7794, - r8a7795, r8a7796) + r8a7795, r8a7796, r8a77995) - "extalr" (r8a7795, r8a7796) - "usb_extal" (r8a7743, r8a7745, r8a7790, r8a7791, r8a7793, r8a7794) diff --git a/Documentation/devicetree/bindings/clock/renesas,rcar-usb2-clock-sel.txt b/Documentation/devicetree/bindings/clock/renesas,rcar-usb2-clock-sel.txt new file mode 100644 index 000000000000..e96e085271c1 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/renesas,rcar-usb2-clock-sel.txt @@ -0,0 +1,55 @@ +* Renesas R-Car USB 2.0 clock selector + +This file provides information on what the device node for the R-Car USB 2.0 +clock selector. + +If you connect an external clock to the USB_EXTAL pin only, you should set +the clock rate to "usb_extal" node only. +If you connect an oscillator to both the USB_XTAL and USB_EXTAL, this module +is not needed because this is default setting. (Of course, you can set the +clock rates to both "usb_extal" and "usb_xtal" nodes. + +Case 1: An external clock connects to R-Car SoC + +----------+ +--- R-Car ---------------------+ + |External |---|USB_EXTAL ---> all usb channels| + |clock | |USB_XTAL | + +----------+ +-------------------------------+ +In this case, we need this driver with "usb_extal" clock. + +Case 2: An oscillator connects to R-Car SoC + +----------+ +--- R-Car ---------------------+ + |Oscillator|---|USB_EXTAL -+-> all usb channels| + | |---|USB_XTAL --+ | + +----------+ +-------------------------------+ +In this case, we don't need this selector. + +Required properties: +- compatible: "renesas,r8a7795-rcar-usb2-clock-sel" if the device is a part of + an R8A7795 SoC. + "renesas,r8a7796-rcar-usb2-clock-sel" if the device if a part of + an R8A7796 SoC. + "renesas,rcar-gen3-usb2-clock-sel" for a generic R-Car Gen3 + compatible device. + + When compatible with the generic version, nodes must list the + SoC-specific version corresponding to the platform first + followed by the generic version. + +- reg: offset and length of the USB 2.0 clock selector register block. +- clocks: A list of phandles and specifier pairs. +- clock-names: Name of the clocks. + - The functional clock must be "ehci_ohci" + - The USB_EXTAL clock pin must be "usb_extal" + - The USB_XTAL clock pin must be "usb_xtal" +- #clock-cells: Must be 0 + +Example (R-Car H3): + + usb2_clksel: clock-controller@e6590630 { + compatible = "renesas,r8a77950-rcar-usb2-clock-sel", + "renesas,rcar-gen3-usb2-clock-sel"; + reg = <0 0xe6590630 0 0x02>; + clocks = <&cpg CPG_MOD 703>, <&usb_extal>, <&usb_xtal>; + clock-names = "ehci_ohci", "usb_extal", "usb_xtal"; + #clock-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/clock/rockchip,rk3128-cru.txt b/Documentation/devicetree/bindings/clock/rockchip,rk3128-cru.txt index 455a9a00a623..6f8744fd301b 100644 --- a/Documentation/devicetree/bindings/clock/rockchip,rk3128-cru.txt +++ b/Documentation/devicetree/bindings/clock/rockchip,rk3128-cru.txt @@ -1,12 +1,14 @@ -* Rockchip RK3128 Clock and Reset Unit +* Rockchip RK3126/RK3128 Clock and Reset Unit -The RK3128 clock controller generates and supplies clock to various +The RK3126/RK3128 clock controller generates and supplies clock to various controllers within the SoC and also implements a reset controller for SoC peripherals. Required Properties: -- compatible: should be "rockchip,rk3128-cru" +- compatible: should be "rockchip,rk3126-cru" or "rockchip,rk3128-cru" + "rockchip,rk3126-cru" - controller compatible with RK3126 SoC. + "rockchip,rk3128-cru" - controller compatible with RK3128 SoC. - reg: physical base address of the controller and length of memory mapped region. - #clock-cells: should be 1. diff --git a/Documentation/devicetree/bindings/clock/snps,hsdk-pll-clock.txt b/Documentation/devicetree/bindings/clock/snps,hsdk-pll-clock.txt new file mode 100644 index 000000000000..c56c7553c730 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/snps,hsdk-pll-clock.txt @@ -0,0 +1,28 @@ +Binding for the HSDK Generic PLL clock + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible: should be "snps,hsdk-<name>-pll-clock" + "snps,hsdk-core-pll-clock" + "snps,hsdk-gp-pll-clock" + "snps,hsdk-hdmi-pll-clock" +- reg : should contain base register location and length. +- clocks: shall be the input parent clock phandle for the PLL. +- #clock-cells: from common clock binding; Should always be set to 0. + +Example: + input_clk: input-clk { + clock-frequency = <33333333>; + compatible = "fixed-clock"; + #clock-cells = <0>; + }; + + cpu_clk: cpu-clk@0 { + compatible = "snps,hsdk-core-pll-clock"; + reg = <0x00 0x10>; + #clock-cells = <0>; + clocks = <&input_clk>; + }; diff --git a/Documentation/devicetree/bindings/clock/snps,pll-clock.txt b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt new file mode 100644 index 000000000000..11fe4876612c --- /dev/null +++ b/Documentation/devicetree/bindings/clock/snps,pll-clock.txt @@ -0,0 +1,28 @@ +Binding for the AXS10X Generic PLL clock + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible: should be "snps,axs10x-<name>-pll-clock" + "snps,axs10x-arc-pll-clock" + "snps,axs10x-pgu-pll-clock" +- reg: should always contain 2 pairs address - length: first for PLL config +registers and second for corresponding LOCK CGU register. +- clocks: shall be the input parent clock phandle for the PLL. +- #clock-cells: from common clock binding; Should always be set to 0. + +Example: + input-clk: input-clk { + clock-frequency = <33333333>; + compatible = "fixed-clock"; + #clock-cells = <0>; + }; + + core-clk: core-clk@80 { + compatible = "snps,axs10x-arc-pll-clock"; + reg = <0x80 0x10>, <0x100 0x10>; + #clock-cells = <0>; + clocks = <&input-clk>; + }; diff --git a/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt new file mode 100644 index 000000000000..a135504c7d57 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt @@ -0,0 +1,71 @@ +STMicroelectronics STM32H7 Reset and Clock Controller +===================================================== + +The RCC IP is both a reset and a clock controller. + +Please refer to clock-bindings.txt for common clock controller binding usage. +Please also refer to reset.txt for common reset controller binding usage. + +Required properties: +- compatible: Should be: + "st,stm32h743-rcc" + +- reg: should be register base and length as documented in the + datasheet + +- #reset-cells: 1, see below + +- #clock-cells : from common clock binding; shall be set to 1 + +- clocks: External oscillator clock phandle + - high speed external clock signal (HSE) + - low speed external clock signal (LSE) + - external I2S clock (I2S_CKIN) + +Optional properties: +- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup domain + write protection (RTC clock). + +Example: + + rcc: reset-clock-controller@58024400 { + compatible = "st,stm32h743-rcc", "st,stm32-rcc"; + reg = <0x58024400 0x400>; + #reset-cells = <1>; + #clock-cells = <2>; + clocks = <&clk_hse>, <&clk_lse>, <&clk_i2s_ckin>; + + st,syscfg = <&pwrcfg>; +}; + +The peripheral clock consumer should specify the desired clock by +having the clock ID in its "clocks" phandle cell. + +Example: + + timer5: timer@40000c00 { + compatible = "st,stm32-timer"; + reg = <0x40000c00 0x400>; + interrupts = <50>; + clocks = <&rcc TIM5_CK>; + }; + +Specifying softreset control of devices +======================================= + +Device nodes should specify the reset channel required in their "resets" +property, containing a phandle to the reset device node and an index specifying +which channel to use. +The index is the bit number within the RCC registers bank, starting from RCC +base address. +It is calculated as: index = register_offset / 4 * 32 + bit_offset. +Where bit_offset is the bit offset within the register. + +For example, for CRC reset: + crc = AHB4RSTR_offset / 4 * 32 + CRCRST_bit_offset = 0x88 / 4 * 32 + 19 = 1107 + +Example: + + timer2 { + resets = <&rcc STM32H7_APB1L_RESET(TIM2)>; + }; diff --git a/Documentation/devicetree/bindings/clock/sunxi-ccu.txt b/Documentation/devicetree/bindings/clock/sunxi-ccu.txt index df9fad58facd..7eda08eb8a1e 100644 --- a/Documentation/devicetree/bindings/clock/sunxi-ccu.txt +++ b/Documentation/devicetree/bindings/clock/sunxi-ccu.txt @@ -3,18 +3,24 @@ Allwinner Clock Control Unit Binding Required properties : - compatible: must contain one of the following compatibles: + - "allwinner,sun4i-a10-ccu" + - "allwinner,sun5i-a10s-ccu" + - "allwinner,sun5i-a13-ccu" - "allwinner,sun6i-a31-ccu" + - "allwinner,sun7i-a20-ccu" - "allwinner,sun8i-a23-ccu" - "allwinner,sun8i-a33-ccu" - "allwinner,sun8i-a83t-ccu" - "allwinner,sun8i-a83t-r-ccu" - "allwinner,sun8i-h3-ccu" - "allwinner,sun8i-h3-r-ccu" ++ - "allwinner,sun8i-r40-ccu" - "allwinner,sun8i-v3s-ccu" - "allwinner,sun9i-a80-ccu" - "allwinner,sun50i-a64-ccu" - "allwinner,sun50i-a64-r-ccu" - "allwinner,sun50i-h5-ccu" + - "nextthing,gr8-ccu" - reg: Must contain the registers base address and length - clocks: phandle to the oscillators feeding the CCU. Two are needed: diff --git a/Documentation/devicetree/bindings/clock/uniphier-clock.txt b/Documentation/devicetree/bindings/clock/uniphier-clock.txt index 812163060fa3..7b5f602765fe 100644 --- a/Documentation/devicetree/bindings/clock/uniphier-clock.txt +++ b/Documentation/devicetree/bindings/clock/uniphier-clock.txt @@ -6,7 +6,6 @@ System clock Required properties: - compatible: should be one of the following: - "socionext,uniphier-sld3-clock" - for sLD3 SoC. "socionext,uniphier-ld4-clock" - for LD4 SoC. "socionext,uniphier-pro4-clock" - for Pro4 SoC. "socionext,uniphier-sld8-clock" - for sLD8 SoC. @@ -14,6 +13,7 @@ Required properties: "socionext,uniphier-pxs2-clock" - for PXs2/LD6b SoC. "socionext,uniphier-ld11-clock" - for LD11 SoC. "socionext,uniphier-ld20-clock" - for LD20 SoC. + "socionext,uniphier-pxs3-clock" - for PXs3 SoC - #clock-cells: should be 1. Example: @@ -48,7 +48,6 @@ Media I/O (MIO) clock, SD clock Required properties: - compatible: should be one of the following: - "socionext,uniphier-sld3-mio-clock" - for sLD3 SoC. "socionext,uniphier-ld4-mio-clock" - for LD4 SoC. "socionext,uniphier-pro4-mio-clock" - for Pro4 SoC. "socionext,uniphier-sld8-mio-clock" - for sLD8 SoC. @@ -56,6 +55,7 @@ Required properties: "socionext,uniphier-pxs2-sd-clock" - for PXs2/LD6b SoC. "socionext,uniphier-ld11-mio-clock" - for LD11 SoC. "socionext,uniphier-ld20-sd-clock" - for LD20 SoC. + "socionext,uniphier-pxs3-sd-clock" - for PXs3 SoC - #clock-cells: should be 1. Example: @@ -82,11 +82,9 @@ Provided clocks: 8: USB2 ch0 host 9: USB2 ch1 host 10: USB2 ch2 host -11: USB2 ch3 host 12: USB2 ch0 PHY 13: USB2 ch1 PHY 14: USB2 ch2 PHY -15: USB2 ch3 PHY Peripheral clock @@ -94,7 +92,6 @@ Peripheral clock Required properties: - compatible: should be one of the following: - "socionext,uniphier-sld3-peri-clock" - for sLD3 SoC. "socionext,uniphier-ld4-peri-clock" - for LD4 SoC. "socionext,uniphier-pro4-peri-clock" - for Pro4 SoC. "socionext,uniphier-sld8-peri-clock" - for sLD8 SoC. @@ -102,6 +99,7 @@ Required properties: "socionext,uniphier-pxs2-peri-clock" - for PXs2/LD6b SoC. "socionext,uniphier-ld11-peri-clock" - for LD11 SoC. "socionext,uniphier-ld20-peri-clock" - for LD20 SoC. + "socionext,uniphier-pxs3-peri-clock" - for PXs3 SoC - #clock-cells: should be 1. Example: diff --git a/Documentation/devicetree/bindings/power/wakeup-source.txt b/Documentation/devicetree/bindings/power/wakeup-source.txt index 963c6dfd484d..3c81f78b5c27 100644 --- a/Documentation/devicetree/bindings/power/wakeup-source.txt +++ b/Documentation/devicetree/bindings/power/wakeup-source.txt @@ -20,13 +20,12 @@ List of legacy properties and respective binding document 1. "enable-sdio-wakeup" Documentation/devicetree/bindings/mmc/mmc.txt 2. "gpio-key,wakeup" Documentation/devicetree/bindings/input/gpio-keys{,-polled}.txt 3. "has-tpo" Documentation/devicetree/bindings/rtc/rtc-opal.txt -4. "isil,irq2-can-wakeup-machine" Documentation/devicetree/bindings/rtc/isil,isl12057.txt -5. "linux,wakeup" Documentation/devicetree/bindings/input/gpio-matrix-keypad.txt +4. "linux,wakeup" Documentation/devicetree/bindings/input/gpio-matrix-keypad.txt Documentation/devicetree/bindings/mfd/tc3589x.txt Documentation/devicetree/bindings/input/ads7846.txt -6. "linux,keypad-wakeup" Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt -7. "linux,input-wakeup" Documentation/devicetree/bindings/input/samsung-keypad.txt -8. "nvidia,wakeup-source" Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt +5. "linux,keypad-wakeup" Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt +6. "linux,input-wakeup" Documentation/devicetree/bindings/input/samsung-keypad.txt +7. "nvidia,wakeup-source" Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt Examples -------- diff --git a/Documentation/devicetree/bindings/rtc/google,goldfish-rtc.txt b/Documentation/devicetree/bindings/rtc/google,goldfish-rtc.txt new file mode 100644 index 000000000000..634312dd95ca --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/google,goldfish-rtc.txt @@ -0,0 +1,17 @@ +Android Goldfish RTC + +Android Goldfish RTC device used by Android emulator. + +Required properties: + +- compatible : should contain "google,goldfish-rtc" +- reg : <registers mapping> +- interrupts : <interrupt mapping> + +Example: + + goldfish_timer@9020000 { + compatible = "google,goldfish-rtc"; + reg = <0x9020000 0x1000>; + interrupts = <0x3>; + }; diff --git a/Documentation/devicetree/bindings/rtc/isil,isl12057.txt b/Documentation/devicetree/bindings/rtc/isil,isl12057.txt index cf83e0940302..fbbdd92e5af9 100644 --- a/Documentation/devicetree/bindings/rtc/isil,isl12057.txt +++ b/Documentation/devicetree/bindings/rtc/isil,isl12057.txt @@ -24,7 +24,6 @@ Optional properties: - "wakeup-source": mark the chip as a wakeup source, independently of the availability of an IRQ line connected to the SoC. - (Legacy property supported: "isil,irq2-can-wakeup-machine") - "interrupt-parent", "interrupts": for passing the interrupt line of the SoC connected to IRQ#2 of the RTC chip. diff --git a/Documentation/devicetree/bindings/rtc/realtek,rtd119x.txt b/Documentation/devicetree/bindings/rtc/realtek,rtd119x.txt new file mode 100644 index 000000000000..bbf1ccb5df31 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/realtek,rtd119x.txt @@ -0,0 +1,16 @@ +Realtek RTD129x Real-Time Clock +=============================== + +Required properties: +- compatible : Should be "realtek,rtd1295-rtc" +- reg : Specifies the physical base address and size +- clocks : Specifies the clock gate + + +Example: + + rtc@9801b600 { + compatible = "realtek,rtd1295-clk"; + reg = <0x9801b600 0x100>; + clocks = <&clkc RTD1295_CLK_EN_MISC_RTC>; + }; diff --git a/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt b/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt index 945934918b71..d5e26d313f62 100644 --- a/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt +++ b/Documentation/devicetree/bindings/rtc/sun6i-rtc.txt @@ -10,7 +10,7 @@ Required properties: Required properties for new device trees - clocks : phandle to the 32kHz external oscillator -- clock-output-names : name of the LOSC clock created +- clock-output-names : names of the LOSC and its external output clocks created - #clock-cells : must be equals to 1. The RTC provides two clocks: the LOSC and its external output, with index 0 and 1 respectively. @@ -21,7 +21,7 @@ rtc: rtc@01f00000 { compatible = "allwinner,sun6i-a31-rtc"; reg = <0x01f00000 0x54>; interrupts = <0 40 4>, <0 41 4>; - clock-output-names = "osc32k"; + clock-output-names = "osc32k", "osc32k-out"; clocks = <&ext_osc32k>; #clock-cells = <1>; }; diff --git a/Documentation/devicetree/bindings/sound/atmel-classd.txt b/Documentation/devicetree/bindings/sound/atmel-classd.txt index 549e701cb7a1..898551076382 100644 --- a/Documentation/devicetree/bindings/sound/atmel-classd.txt +++ b/Documentation/devicetree/bindings/sound/atmel-classd.txt @@ -13,13 +13,11 @@ Required properties: Must be "tx". - clock-names Tuple listing input clock names. - Required elements: "pclk", "gclk" and "aclk". + Required elements: "pclk" and "gclk". - clocks Please refer to clock-bindings.txt. - assigned-clocks Should be <&classd_gclk>. -- assigned-clock-parents - Should be <&audio_pll_pmc>. Optional properties: - pinctrl-names, pinctrl-0 @@ -45,10 +43,9 @@ classd: classd@fc048000 { (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(47))>; dma-names = "tx"; - clocks = <&classd_clk>, <&classd_gclk>, <&audio_pll_pmc>; - clock-names = "pclk", "gclk", "aclk"; + clocks = <&classd_clk>, <&classd_gclk>; + clock-names = "pclk", "gclk"; assigned-clocks = <&classd_gclk>; - assigned-clock-parents = <&audio_pll_pmc>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_classd_default>; diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index fe25787ff6d4..75d2d57e2c44 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -22,7 +22,7 @@ prototypes: struct vfsmount *(*d_automount)(struct path *path); int (*d_manage)(const struct path *, bool); struct dentry *(*d_real)(struct dentry *, const struct inode *, - unsigned int); + unsigned int, unsigned int); locking rules: rename_lock ->d_lock may block rcu-walk diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 405a3df759b3..5fd325df59e2 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -988,7 +988,7 @@ struct dentry_operations { struct vfsmount *(*d_automount)(struct path *); int (*d_manage)(const struct path *, bool); struct dentry *(*d_real)(struct dentry *, const struct inode *, - unsigned int); + unsigned int, unsigned int); }; d_revalidate: called when the VFS needs to revalidate a dentry. This diff --git a/Documentation/translations/ko_KR/memory-barriers.txt b/Documentation/translations/ko_KR/memory-barriers.txt index bc80fc0e210f..a7a813258013 100644 --- a/Documentation/translations/ko_KR/memory-barriers.txt +++ b/Documentation/translations/ko_KR/memory-barriers.txt @@ -523,11 +523,11 @@ CPU ì—게 ê¸°ëŒ€í• ìˆ˜ 있는 ìµœì†Œí•œì˜ ë³´ìž¥ì‚¬í• ëª‡ê°€ì§€ê°€ 있습니 즉, ACQUIRE 는 ìµœì†Œí•œì˜ "ì·¨ë“" ë™ìž‘처럼, ê·¸ë¦¬ê³ RELEASE 는 ìµœì†Œí•œì˜ "공개" 처럼 ë™ìž‘한다는 ì˜ë¯¸ìž…니다. -core-api/atomic_ops.rst ì—ì„œ 설명ë˜ëŠ” ì–´í† ë¯¹ 오í¼ë ˆì´ì…˜ë“¤ 중ì—는 ì™„ì „ížˆ -순서잡힌 것들과 (배리어를 사용하지 않는) ì™„í™”ëœ ìˆœì„œì˜ ê²ƒë“¤ ì™¸ì— ACQUIRE 와 -RELEASE ë¶€ë¥˜ì˜ ê²ƒë“¤ë„ ì¡´ìž¬í•©ë‹ˆë‹¤. 로드와 ìŠ¤í† ì–´ë¥¼ ëª¨ë‘ ìˆ˜í–‰í•˜ëŠ” ì¡°í•©ëœ ì–´í† ë¯¹ -오í¼ë ˆì´ì…˜ì—ì„œ, ACQUIRE 는 해당 오í¼ë ˆì´ì…˜ì˜ 로드 부분ì—만 ì ìš©ë˜ê³ RELEASE 는 -해당 오í¼ë ˆì´ì…˜ì˜ ìŠ¤í† ì–´ 부분ì—만 ì ìš©ë©ë‹ˆë‹¤. +atomic_t.txt ì— ì„¤ëª…ëœ ì–´í† ë¯¹ 오í¼ë ˆì´ì…˜ë“¤ 중 ì¼ë¶€ëŠ” ì™„ì „ížˆ 순서잡힌 것들과 +(배리어를 사용하지 않는) ì™„í™”ëœ ìˆœì„œì˜ ê²ƒë“¤ ì™¸ì— ACQUIRE 와 RELEASE ë¶€ë¥˜ì˜ +ê²ƒë“¤ë„ ì¡´ìž¬í•©ë‹ˆë‹¤. 로드와 ìŠ¤í† ì–´ë¥¼ ëª¨ë‘ ìˆ˜í–‰í•˜ëŠ” ì¡°í•©ëœ ì–´í† ë¯¹ 오í¼ë ˆì´ì…˜ì—ì„œ, +ACQUIRE 는 해당 오í¼ë ˆì´ì…˜ì˜ 로드 부분ì—만 ì ìš©ë˜ê³ RELEASE 는 해당 +오í¼ë ˆì´ì…˜ì˜ ìŠ¤í† ì–´ 부분ì—만 ì ìš©ë©ë‹ˆë‹¤. 메모리 ë°°ë¦¬ì–´ë“¤ì€ ë‘ CPU ê°„, ë˜ëŠ” CPU 와 디바ì´ìŠ¤ ê°„ì— ìƒí˜¸ìž‘ìš©ì˜ ê°€ëŠ¥ì„±ì´ ìžˆì„ ë•Œì—만 필요합니다. 만약 ì–´ë–¤ ì½”ë“œì— ê·¸ëŸ° ìƒí˜¸ìž‘ìš©ì´ ì—†ì„ ê²ƒì´ ë³´ìž¥ëœë‹¤ë©´, 해당 @@ -617,7 +617,22 @@ RELEASE ë¶€ë¥˜ì˜ ê²ƒë“¤ë„ ì¡´ìž¬í•©ë‹ˆë‹¤. 로드와 ìŠ¤í† ì–´ë¥¼ ëª¨ë‘ ìˆ˜í ì´ ë³€ê²½ì€ ì•žì˜ ì²˜ìŒ ë‘가지 ê²°ê³¼ 중 í•˜ë‚˜ë§Œì´ ë°œìƒí• 수 ìžˆê³ , ì„¸ë²ˆì§¸ì˜ ê²°ê³¼ëŠ” ë°œìƒí• 수 ì—†ë„ë¡ í•©ë‹ˆë‹¤. -ë°ì´í„° ì˜ì¡´ì„± 배리어는 ì˜ì¡´ì ì“°ê¸°ì— ëŒ€í•´ì„œë„ ìˆœì„œë¥¼ ìž¡ì•„ì¤ë‹ˆë‹¤: + +[!] ì´ ìƒë‹¹ížˆ ë°˜ì§ê´€ì ì¸ ìƒí™©ì€ ë¶„ë¦¬ëœ ìºì‹œë¥¼ 가지는 기계들ì—ì„œ 가장 잘 +ë°œìƒí•˜ëŠ”ë°, 예를 들면 í•œ ìºì‹œ ë±…í¬ëŠ” ì§ìˆ˜ ë²ˆí˜¸ì˜ ìºì‹œ ë¼ì¸ë“¤ì„ ì²˜ë¦¬í•˜ê³ , 다른 +ë±…í¬ëŠ” 홀수 ë²ˆí˜¸ì˜ ìºì‹œ ë¼ì¸ë“¤ì„ 처리하는 ê²½ìš°ìž„ì„ ì•Œì•„ë‘시기 ë°”ëžë‹ˆë‹¤. í¬ì¸í„° +P 는 ì§ìˆ˜ 번호 ìºì‹œ ë¼ì¸ì— ì €ìž¥ë˜ì–´ ìžˆê³ , 변수 B 는 홀수 번호 ìºì‹œ ë¼ì¸ì— +ì €ìž¥ë˜ì–´ ìžˆì„ ìˆ˜ 있습니다. 여기서 ê°’ì„ ì½ì–´ì˜¤ëŠ” CPU ì˜ ìºì‹œì˜ 홀수 번호 처리 +ë±…í¬ëŠ” 열심히 ì¼ê°ì„ ì²˜ë¦¬ì¤‘ì¸ ë°˜ë©´ 홀수 번호 처리 ë±…í¬ëŠ” í• ì¼ ì—†ì´ í•œê°€í•œ +중ì´ë¼ë©´ í¬ì¸í„° P (&B) ì˜ ìƒˆë¡œìš´ ê°’ê³¼ 변수 B ì˜ ê¸°ì¡´ ê°’ (2) 를 ë³¼ 수 있습니다. + + +ì˜ì¡´ì ì“°ê¸°ë“¤ì˜ ìˆœì„œë¥¼ 맞추는ë°ì—는 ë°ì´í„° ì˜ì¡´ì„± 배리어가 필요치 ì•Šì€ë°, ì´ëŠ” +리눅스 커ë„ì´ ì§€ì›í•˜ëŠ” CPU ë“¤ì€ (1) 쓰기가 ì •ë§ë¡œ ì¼ì–´ë‚ 지, (2) 쓰기가 ì–´ë””ì— +ì´ë£¨ì–´ì§ˆì§€, ê·¸ë¦¬ê³ (3) 쓰여질 ê°’ì„ í™•ì‹¤ížˆ 알기 ì „ê¹Œì§€ëŠ” 쓰기를 수행하지 않기 +때문입니다. 하지만 "컨트롤 ì˜ì¡´ì„±" 섹션과 +Documentation/RCU/rcu_dereference.txt 파ì¼ì„ ì£¼ì˜ ê¹Šê²Œ ì½ì–´ 주시기 ë°”ëžë‹ˆë‹¤: +컴파ì¼ëŸ¬ëŠ” 매우 ì°½ì˜ì ì¸ ë§Žì€ ë°©ë²•ìœ¼ë¡œ 종ì†ì„±ì„ ê¹° 수 있습니다. CPU 1 CPU 2 =============== =============== @@ -626,28 +641,19 @@ RELEASE ë¶€ë¥˜ì˜ ê²ƒë“¤ë„ ì¡´ìž¬í•©ë‹ˆë‹¤. 로드와 ìŠ¤í† ì–´ë¥¼ ëª¨ë‘ ìˆ˜í <쓰기 배리어> WRITE_ONCE(P, &B); Q = READ_ONCE(P); - <ë°ì´í„° ì˜ì¡´ì„± 배리어> - *Q = 5; + WRITE_ONCE(*Q, 5); -ì´ ë°ì´í„° ì˜ì¡´ì„± 배리어는 Q ë¡œì˜ ì½ê¸°ê°€ *Q ë¡œì˜ ìŠ¤í† ì–´ì™€ 순서를 맞추게 -í•´ì¤ë‹ˆë‹¤. ì´ëŠ” 다ìŒê³¼ ê°™ì€ ê²°ê³¼ë¥¼ 막습니다: +ë”°ë¼ì„œ, Q ë¡œì˜ ì½ê¸°ì™€ *Q ë¡œì˜ ì“°ê¸° 사ì´ì—는 ë°ì´í„° 종ì†ì„± 배리어가 필요치 +않습니다. 달리 ë§í•˜ë©´, ë°ì´í„° 종ì†ì„± 배리어가 ì—†ë”ë¼ë„ ë‹¤ìŒ ê²°ê³¼ëŠ” ìƒê¸°ì§€ +않습니다: (Q == &B) && (B == 4) ì´ëŸ° íŒ¨í„´ì€ ë“œë¬¼ê²Œ 사용ë˜ì–´ì•¼ í•¨ì„ ì•Œì•„ ë‘시기 ë°”ëžë‹ˆë‹¤. 무엇보다ë„, ì˜ì¡´ì„± 순서 ê·œì¹™ì˜ ì˜ë„는 쓰기 ìž‘ì—…ì„ -예방- í•´ì„œ 그로 ì¸í•´ ë°œìƒí•˜ëŠ” 비싼 ìºì‹œ ë¯¸ìŠ¤ë„ ì—†ì• ë ¤ëŠ” 것입니다. ì´ íŒ¨í„´ì€ ë“œë¬¼ê²Œ ë°œìƒí•˜ëŠ” ì—러 ì¡°ê±´ ê°™ì€ê²ƒë“¤ì„ 기ë¡í•˜ëŠ”ë° -사용ë 수 ìžˆê³ , ì´ë ‡ê²Œ 배리어를 사용해 순서를 지키게 í•¨ìœ¼ë¡œì¨ ê·¸ëŸ° 기ë¡ì´ -사ë¼ì§€ëŠ” ê²ƒì„ ë§‰ìŠµë‹ˆë‹¤. - - -[!] ìƒë‹¹ížˆ 비ì§ê´€ì ì¸ ì´ ìƒí™©ì€ ë¶„ë¦¬ëœ ìºì‹œë¥¼ 가진 기계, 예를 들어 í•œ ìºì‹œ -ë±…í¬ê°€ ì§ìˆ˜ë²ˆ ìºì‹œ ë¼ì¸ì„ ì²˜ë¦¬í•˜ê³ ë‹¤ë¥¸ ë±…í¬ëŠ” 홀수번 ìºì‹œ ë¼ì¸ì„ 처리하는 기계 -등ì—ì„œ 가장 잘 ë°œìƒí•©ë‹ˆë‹¤. í¬ì¸í„° P 는 홀수 ë²ˆí˜¸ì˜ ìºì‹œ ë¼ì¸ì— ìžˆê³ , 변수 B 는 -ì§ìˆ˜ 번호 ìºì‹œ ë¼ì¸ì— ìžˆë‹¤ê³ ìƒê°í•´ 봅시다. 그런 ìƒíƒœì—ì„œ ì½ê¸° ìž‘ì—…ì„ í•˜ëŠ” CPU -ì˜ ì§ìˆ˜ë²ˆ ë±…í¬ëŠ” í• ì¼ì´ 쌓여 매우 ë°”ì˜ì§€ë§Œ 홀수번 ë±…í¬ëŠ” í• ì¼ì´ 없어 아무 -ì¼ë„ 하지 ì•Šê³ ìžˆì—ˆë‹¤ë©´, í¬ì¸í„° P 는 새 ê°’ (&B) ì„, ê·¸ë¦¬ê³ ë³€ìˆ˜ B 는 ì˜›ë‚ ê°’ -(2) ì„ ê°€ì§€ê³ ìžˆëŠ” ìƒíƒœê°€ 보여질 ìˆ˜ë„ ìžˆìŠµë‹ˆë‹¤. +사용ë 수 있으며, CPUì˜ ìžì—°ì ì¸ ìˆœì„œ ë³´ìž¥ì´ ê·¸ëŸ° 기ë¡ë“¤ì„ 사ë¼ì§€ì§€ 않게 +í•´ì¤ë‹ˆë‹¤. ë°ì´í„° ì˜ì¡´ì„± 배리어는 매우 중요한ë°, 예를 들어 RCU 시스템ì—ì„œ ê·¸ë ‡ìŠµë‹ˆë‹¤. @@ -1848,8 +1854,7 @@ Mandatory ë°°ë¦¬ì–´ë“¤ì€ SMP 시스템ì—ì„œë„ UP 시스템ì—ì„œë„ SMP íš¨ê³ ì´ ì½”ë“œëŠ” ê°ì²´ì˜ ì—…ë°ì´íŠ¸ëœ death 마í¬ê°€ ë ˆí¼ëŸ°ìŠ¤ ì¹´ìš´í„° ê°ì†Œ ë™ìž‘ *ì „ì—* ë³´ì¼ ê²ƒì„ ë³´ìž¥í•©ë‹ˆë‹¤. - ë” ë§Žì€ ì •ë³´ë¥¼ ìœ„í•´ì„ Documentation/core-api/atomic_ops.rst 문서를 ì°¸ê³ í•˜ì„¸ìš”. - 어디서 ì´ê²ƒë“¤ì„ 사용해야 í• ì§€ ê¶ê¸ˆí•˜ë‹¤ë©´ "ì–´í† ë¯¹ 오í¼ë ˆì´ì…˜" ì„œë¸Œì„¹ì…˜ì„ + ë” ë§Žì€ ì •ë³´ë¥¼ ìœ„í•´ì„ Documentation/atomic_{t,bitops}.txt 문서를 ì°¸ê³ í•˜ì„¸ìš”. @@ -2468,86 +2473,7 @@ _않습니다_. ì „ì²´ 메모리 배리어를 ë‚´í¬í•˜ê³ ë˜ ì¼ë¶€ëŠ” ë‚´í¬í•˜ì§€ 않지만, 커ë„ì—ì„œ ìƒë‹¹ížˆ ì˜ì¡´ì 으로 사용하는 기능 중 하나입니다. -ë©”ëª¨ë¦¬ì˜ ì–´ë–¤ ìƒíƒœë¥¼ ìˆ˜ì •í•˜ê³ í•´ë‹¹ ìƒíƒœì— 대한 (ì˜ˆì „ì˜ ë˜ëŠ” ìµœì‹ ì˜) ì •ë³´ë¥¼ -리턴하는 ì–´í† ë¯¹ 오í¼ë ˆì´ì…˜ì€ ëª¨ë‘ SMP-ì¡°ê±´ì 범용 메모리 배리어(smp_mb())를 -ì‹¤ì œ 오í¼ë ˆì´ì…˜ì˜ ì•žê³¼ ë’¤ì— ë‚´í¬í•©ë‹ˆë‹¤. ì´ëŸ° 오í¼ë ˆì´ì…˜ì€ 다ìŒì˜ ê²ƒë“¤ì„ -í¬í•¨í•©ë‹ˆë‹¤: - - xchg(); - atomic_xchg(); atomic_long_xchg(); - atomic_inc_return(); atomic_long_inc_return(); - atomic_dec_return(); atomic_long_dec_return(); - atomic_add_return(); atomic_long_add_return(); - atomic_sub_return(); atomic_long_sub_return(); - atomic_inc_and_test(); atomic_long_inc_and_test(); - atomic_dec_and_test(); atomic_long_dec_and_test(); - atomic_sub_and_test(); atomic_long_sub_and_test(); - atomic_add_negative(); atomic_long_add_negative(); - test_and_set_bit(); - test_and_clear_bit(); - test_and_change_bit(); - - /* exchange ì¡°ê±´ì´ ì„±ê³µí• ë•Œ */ - cmpxchg(); - atomic_cmpxchg(); atomic_long_cmpxchg(); - atomic_add_unless(); atomic_long_add_unless(); - -ì´ê²ƒë“¤ì€ 메모리 배리어 효과가 필요한 ACQUIRE 부류와 RELEASE 부류 오í¼ë ˆì´ì…˜ë“¤ì„ -êµ¬í˜„í• ë•Œ, ê·¸ë¦¬ê³ ê°ì²´ í•´ì œë¥¼ 위해 ë ˆí¼ëŸ°ìŠ¤ 카운터를 ì¡°ì •í• ë•Œ, 암묵ì 메모리 -배리어 효과가 필요한 ê³³ ë“±ì— ì‚¬ìš©ë©ë‹ˆë‹¤. - - -다ìŒì˜ 오í¼ë ˆì´ì…˜ë“¤ì€ 메모리 배리어를 ë‚´í¬í•˜ì§€ _않기_ ë•Œë¬¸ì— ë¬¸ì œê°€ ë 수 -있지만, RELEASE ë¶€ë¥˜ì˜ ì˜¤í¼ë ˆì´ì…˜ë“¤ê³¼ ê°™ì€ ê²ƒë“¤ì„ êµ¬í˜„í• ë•Œ 사용ë ìˆ˜ë„ -있습니다: - - atomic_set(); - set_bit(); - clear_bit(); - change_bit(); - -ì´ê²ƒë“¤ì„ ì‚¬ìš©í• ë•Œì—는 필요하다면 ì ì ˆí•œ (예를 들면 smp_mb__before_atomic() -ê°™ì€) 메모리 배리어가 명시ì 으로 함께 사용ë˜ì–´ì•¼ 합니다. - - -ì•„ëž˜ì˜ ê²ƒë“¤ë„ ë©”ëª¨ë¦¬ 배리어를 ë‚´í¬í•˜ì§€ _않기_ 때문ì—, ì¼ë¶€ 환경ì—서는 (예를 -들면 smp_mb__before_atomic() ê³¼ ê°™ì€) 명시ì ì¸ ë©”ëª¨ë¦¬ 배리어 ì‚¬ìš©ì´ í•„ìš”í•©ë‹ˆë‹¤. - - atomic_add(); - atomic_sub(); - atomic_inc(); - atomic_dec(); - -ì´ê²ƒë“¤ì´ 통계 ìƒì„±ì„ 위해 사용ëœë‹¤ë©´, ê·¸ë¦¬ê³ í†µê³„ ë°ì´í„° 사ì´ì— 관계가 존재하지 -않는다면 메모리 배리어는 필요치 ì•Šì„ ê²ë‹ˆë‹¤. - -ê°ì²´ì˜ ìˆ˜ëª…ì„ ê´€ë¦¬í•˜ê¸° 위해 ë ˆí¼ëŸ°ìŠ¤ 카운팅 목ì 으로 사용ëœë‹¤ë©´, ë ˆí¼ëŸ°ìŠ¤ -카운터는 ë½ìœ¼ë¡œ 보호ë˜ëŠ” 섹션ì—서만 ì¡°ì •ë˜ê±°ë‚˜ 호출하는 ìª½ì´ ì´ë¯¸ 충분한 -ë ˆí¼ëŸ°ìŠ¤ë¥¼ ìž¡ê³ ìžˆì„ ê²ƒì´ê¸° ë•Œë¬¸ì— ë©”ëª¨ë¦¬ 배리어는 아마 í•„ìš” ì—†ì„ ê²ë‹ˆë‹¤. - -만약 ì–´ë–¤ ë½ì„ 구성하기 위해 사용ëœë‹¤ë©´, ë½ ê´€ë ¨ ë™ìž‘ì€ ì¼ë°˜ì 으로 ìž‘ì—…ì„ íŠ¹ì • -순서대로 진행해야 하므로 메모리 배리어가 í•„ìš”í• ìˆ˜ 있습니다. - -기본ì 으로, ê° ì‚¬ìš©ì²˜ì—서는 메모리 배리어가 필요한지 아닌지 충분히 ê³ ë ¤í•´ì•¼ -합니다. - -ì•„ëž˜ì˜ ì˜¤í¼ë ˆì´ì…˜ë“¤ì€ 특별한 ë½ ê´€ë ¨ ë™ìž‘들입니다: - - test_and_set_bit_lock(); - clear_bit_unlock(); - __clear_bit_unlock(); - -ì´ê²ƒë“¤ì€ ACQUIRE 류와 RELEASE ë¥˜ì˜ ì˜¤í¼ë ˆì´ì…˜ë“¤ì„ 구현합니다. ë½ ê´€ë ¨ ë„구를 -êµ¬í˜„í• ë•Œì—는 ì´ê²ƒë“¤ì„ 좀 ë” ì„ í˜¸í•˜ëŠ” íŽ¸ì´ ë‚˜ì€ë°, ì´ê²ƒë“¤ì˜ êµ¬í˜„ì€ ë§Žì€ -아키í…ì³ì—ì„œ 최ì í™” ë 수 있기 때문입니다. - -[!] ì´ëŸ° ìƒí™©ì— ì‚¬ìš©í• ìˆ˜ 있는 특수한 메모리 배리어 ë„êµ¬ë“¤ì´ ìžˆìŠµë‹ˆë‹¤ë§Œ, ì¼ë¶€ -CPU ì—서는 사용ë˜ëŠ” ì–´í† ë¯¹ ì¸ìŠ¤íŠ¸ëŸì…˜ ìžì²´ì— 메모리 배리어가 ë‚´í¬ë˜ì–´ 있어서 -ì–´í† ë¯¹ 오í¼ë ˆì´ì…˜ê³¼ 메모리 배리어를 함께 사용하는 게 불필요한 ì¼ì´ ë 수 -있는ë°, 그런 ê²½ìš°ì— ì´ íŠ¹ìˆ˜ 메모리 배리어 ë„êµ¬ë“¤ì€ no-op ì´ ë˜ì–´ 실질ì 으로 -아무ì¼ë„ 하지 않습니다. - -ë” ë§Žì€ ë‚´ìš©ì„ ìœ„í•´ì„ Documentation/core-api/atomic_ops.rst 를 ì°¸ê³ í•˜ì„¸ìš”. +ë” ë§Žì€ ë‚´ìš©ì„ ìœ„í•´ì„ Documentation/atomic_t.txt 를 ì°¸ê³ í•˜ì„¸ìš”. 디바ì´ìŠ¤ 액세스 diff --git a/MAINTAINERS b/MAINTAINERS index f46a3225e398..209306019483 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -855,6 +855,12 @@ S: Supported F: drivers/android/ F: drivers/staging/android/ +ANDROID GOLDFISH RTC DRIVER +M: Miodrag Dinic <miodrag.dinic@imgtec.com> +S: Supported +F: Documentation/devicetree/bindings/rtc/google,goldfish-rtc.txt +F: drivers/rtc/rtc-goldfish.c + ANDROID ION DRIVER M: Laura Abbott <labbott@redhat.com> M: Sumit Semwal <sumit.semwal@linaro.org> @@ -4385,6 +4391,12 @@ S: Supported F: drivers/gpu/drm/nouveau/ F: include/uapi/drm/nouveau_drm.h +DRM DRIVER FOR PERVASIVE DISPLAYS REPAPER PANELS +M: Noralf Trønnes <noralf@tronnes.org> +S: Maintained +F: drivers/gpu/drm/tinydrm/repaper.c +F: Documentation/devicetree/bindings/display/repaper.txt + DRM DRIVER FOR QEMU'S CIRRUS DEVICE M: Dave Airlie <airlied@redhat.com> M: Gerd Hoffmann <kraxel@redhat.com> @@ -4403,12 +4415,6 @@ S: Maintained F: drivers/gpu/drm/qxl/ F: include/uapi/drm/qxl_drm.h -DRM DRIVER FOR PERVASIVE DISPLAYS REPAPER PANELS -M: Noralf Trønnes <noralf@tronnes.org> -S: Maintained -F: drivers/gpu/drm/tinydrm/repaper.c -F: Documentation/devicetree/bindings/display/repaper.txt - DRM DRIVER FOR RAGE 128 VIDEO CARDS S: Orphan / Obsolete F: drivers/gpu/drm/r128/ @@ -6202,22 +6208,22 @@ F: include/uapi/linux/if_hippi.h F: net/802/hippi.c F: drivers/net/hippi/ -HISILICON NETWORK SUBSYSTEM DRIVER +HISILICON NETWORK SUBSYSTEM 3 DRIVER (HNS3) M: Yisen Zhuang <yisen.zhuang@huawei.com> M: Salil Mehta <salil.mehta@huawei.com> L: netdev@vger.kernel.org W: http://www.hisilicon.com S: Maintained -F: drivers/net/ethernet/hisilicon/ -F: Documentation/devicetree/bindings/net/hisilicon*.txt +F: drivers/net/ethernet/hisilicon/hns3/ -HISILICON NETWORK SUBSYSTEM 3 DRIVER (HNS3) +HISILICON NETWORK SUBSYSTEM DRIVER M: Yisen Zhuang <yisen.zhuang@huawei.com> M: Salil Mehta <salil.mehta@huawei.com> L: netdev@vger.kernel.org W: http://www.hisilicon.com S: Maintained -F: drivers/net/ethernet/hisilicon/hns3/ +F: drivers/net/ethernet/hisilicon/ +F: Documentation/devicetree/bindings/net/hisilicon*.txt HISILICON ROCE DRIVER M: Lijun Ou <oulijun@huawei.com> @@ -6234,6 +6240,13 @@ S: Supported F: drivers/scsi/hisi_sas/ F: Documentation/devicetree/bindings/scsi/hisilicon-sas.txt +HMM - Heterogeneous Memory Management +M: Jérôme Glisse <jglisse@redhat.com> +L: linux-mm@kvack.org +S: Maintained +F: mm/hmm* +F: include/linux/hmm* + HOST AP DRIVER M: Jouni Malinen <j@w1.fi> L: linux-wireless@vger.kernel.org @@ -7500,19 +7513,6 @@ F: include/kvm/iodev.h F: virt/kvm/* F: tools/kvm/ -KERNEL VIRTUAL MACHINE FOR X86 (KVM/x86) -M: Paolo Bonzini <pbonzini@redhat.com> -M: Radim KrÄmář <rkrcmar@redhat.com> -L: kvm@vger.kernel.org -W: http://www.linux-kvm.org -T: git git://git.kernel.org/pub/scm/virt/kvm/kvm.git -S: Supported -F: arch/x86/kvm/ -F: arch/x86/include/uapi/asm/kvm* -F: arch/x86/include/asm/kvm* -F: arch/x86/kernel/kvm.c -F: arch/x86/kernel/kvmclock.c - KERNEL VIRTUAL MACHINE FOR AMD-V (KVM/amd) M: Joerg Roedel <joro@8bytes.org> L: kvm@vger.kernel.org @@ -7535,17 +7535,6 @@ F: arch/arm/kvm/ F: virt/kvm/arm/ F: include/kvm/arm_* -KERNEL VIRTUAL MACHINE FOR POWERPC (KVM/powerpc) -M: Alexander Graf <agraf@suse.com> -L: kvm-ppc@vger.kernel.org -W: http://www.linux-kvm.org/ -T: git git://github.com/agraf/linux-2.6.git -S: Supported -F: arch/powerpc/include/uapi/asm/kvm* -F: arch/powerpc/include/asm/kvm* -F: arch/powerpc/kvm/ -F: arch/powerpc/kernel/kvm* - KERNEL VIRTUAL MACHINE FOR ARM64 (KVM/arm64) M: Christoffer Dall <christoffer.dall@linaro.org> M: Marc Zyngier <marc.zyngier@arm.com> @@ -7564,6 +7553,17 @@ F: arch/mips/include/uapi/asm/kvm* F: arch/mips/include/asm/kvm* F: arch/mips/kvm/ +KERNEL VIRTUAL MACHINE FOR POWERPC (KVM/powerpc) +M: Alexander Graf <agraf@suse.com> +L: kvm-ppc@vger.kernel.org +W: http://www.linux-kvm.org/ +T: git git://github.com/agraf/linux-2.6.git +S: Supported +F: arch/powerpc/include/uapi/asm/kvm* +F: arch/powerpc/include/asm/kvm* +F: arch/powerpc/kvm/ +F: arch/powerpc/kernel/kvm* + KERNEL VIRTUAL MACHINE for s390 (KVM/s390) M: Christian Borntraeger <borntraeger@de.ibm.com> M: Cornelia Huck <cohuck@redhat.com> @@ -7577,6 +7577,19 @@ F: arch/s390/include/asm/kvm* F: arch/s390/kvm/ F: arch/s390/mm/gmap.c +KERNEL VIRTUAL MACHINE FOR X86 (KVM/x86) +M: Paolo Bonzini <pbonzini@redhat.com> +M: Radim KrÄmář <rkrcmar@redhat.com> +L: kvm@vger.kernel.org +W: http://www.linux-kvm.org +T: git git://git.kernel.org/pub/scm/virt/kvm/kvm.git +S: Supported +F: arch/x86/kvm/ +F: arch/x86/include/uapi/asm/kvm* +F: arch/x86/include/asm/kvm* +F: arch/x86/kernel/kvm.c +F: arch/x86/kernel/kvmclock.c + KERNFS M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> M: Tejun Heo <tj@kernel.org> @@ -7816,13 +7829,6 @@ M: Sasha Levin <alexander.levin@verizon.com> S: Maintained F: tools/lib/lockdep/ -HMM - Heterogeneous Memory Management -M: Jérôme Glisse <jglisse@redhat.com> -L: linux-mm@kvack.org -S: Maintained -F: mm/hmm* -F: include/linux/hmm* - LIBNVDIMM BLK: MMIO-APERTURE DRIVER M: Ross Zwisler <ross.zwisler@linux.intel.com> L: linux-nvdimm@lists.01.org @@ -8417,6 +8423,14 @@ T: git git://linuxtv.org/media_tree.git S: Supported F: drivers/media/dvb-frontends/cxd2841er* +MEDIA DRIVERS FOR DIGITAL DEVICES PCIE DEVICES +M: Daniel Scheller <d.scheller.oss@gmail.com> +L: linux-media@vger.kernel.org +W: https://linuxtv.org +T: git git://linuxtv.org/media_tree.git +S: Maintained +F: drivers/media/pci/ddbridge/* + MEDIA DRIVERS FOR FREESCALE IMX M: Steve Longerbeam <slongerbeam@gmail.com> M: Philipp Zabel <p.zabel@pengutronix.de> @@ -8538,14 +8552,6 @@ T: git git://linuxtv.org/media_tree.git S: Maintained F: drivers/media/dvb-frontends/stv6111* -MEDIA DRIVERS FOR DIGITAL DEVICES PCIE DEVICES -M: Daniel Scheller <d.scheller.oss@gmail.com> -L: linux-media@vger.kernel.org -W: https://linuxtv.org -T: git git://linuxtv.org/media_tree.git -S: Maintained -F: drivers/media/pci/ddbridge/* - MEDIA INPUT INFRASTRUCTURE (V4L/DVB) M: Mauro Carvalho Chehab <mchehab@s-opensource.com> M: Mauro Carvalho Chehab <mchehab@kernel.org> @@ -8569,6 +8575,11 @@ F: include/uapi/linux/meye.h F: include/uapi/linux/ivtv* F: include/uapi/linux/uvcvideo.h +MEDIATEK CIR DRIVER +M: Sean Wang <sean.wang@mediatek.com> +S: Maintained +F: drivers/media/rc/mtk-cir.c + MEDIATEK ETHERNET DRIVER M: Felix Fietkau <nbd@openwrt.org> M: John Crispin <john@phrozen.org> @@ -8609,11 +8620,6 @@ L: linux-wireless@vger.kernel.org S: Maintained F: drivers/net/wireless/mediatek/mt7601u/ -MEDIATEK CIR DRIVER -M: Sean Wang <sean.wang@mediatek.com> -S: Maintained -F: drivers/media/rc/mtk-cir.c - MEDIATEK RANDOM NUMBER GENERATOR SUPPORT M: Sean Wang <sean.wang@mediatek.com> S: Maintained @@ -10129,7 +10135,7 @@ F: include/uapi/linux/ppdev.h F: Documentation/parport*.txt PARAVIRT_OPS INTERFACE -M: Jeremy Fitzhardinge <jeremy@goop.org> +M: Juergen Gross <jgross@suse.com> M: Chris Wright <chrisw@sous-sol.org> M: Alok Kataria <akataria@vmware.com> M: Rusty Russell <rusty@rustcorp.com.au> @@ -10137,7 +10143,7 @@ L: virtualization@lists.linux-foundation.org S: Supported F: Documentation/virtual/paravirt_ops.txt F: arch/*/kernel/paravirt* -F: arch/*/include/asm/paravirt.h +F: arch/*/include/asm/paravirt*.h F: include/linux/hypervisor.h PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES @@ -12836,6 +12842,18 @@ F: drivers/clocksource/arc_timer.c F: drivers/tty/serial/arc_uart.c T: git git://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc.git +SYNOPSYS ARC HSDK SDP pll clock driver +M: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> +S: Supported +F: drivers/clk/clk-hsdk-pll.c +F: Documentation/devicetree/bindings/clock/snps,hsdk-pll-clock.txt + +SYNOPSYS ARC SDP clock driver +M: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> +S: Supported +F: drivers/clk/axs10x/* +F: Documentation/devicetree/bindings/clock/snps,pll-clock.txt + SYNOPSYS ARC SDP platform support M: Alexey Brodkin <abrodkin@synopsys.com> S: Supported diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 195da38cb9a2..6d870421a7a6 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -26,6 +26,7 @@ config SOC_SAMA5D2 select HAVE_AT91_USB_CLK select HAVE_AT91_H32MX select HAVE_AT91_GENERATED_CLK + select HAVE_AT91_AUDIO_PLL select PINCTRL_AT91PIO4 help Select this if ou are using one of Atmel's SAMA5D2 family SoC. @@ -125,6 +126,9 @@ config HAVE_AT91_H32MX config HAVE_AT91_GENERATED_CLK bool +config HAVE_AT91_AUDIO_PLL + bool + config SOC_SAM_V4_V5 bool diff --git a/arch/openrisc/include/asm/pgtable.h b/arch/openrisc/include/asm/pgtable.h index ff97374ca069..71a6f08de8f2 100644 --- a/arch/openrisc/include/asm/pgtable.h +++ b/arch/openrisc/include/asm/pgtable.h @@ -414,6 +414,8 @@ static inline void pmd_set(pmd_t *pmdp, pte_t *ptep) extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; /* defined in head.S */ +struct vm_area_struct; + /* * or32 doesn't have any external MMU info: the kernel page * tables contain all the necessary information. diff --git a/arch/x86/include/asm/desc.h b/arch/x86/include/asm/desc.h index 1a2ba368da39..9d0e13738ed3 100644 --- a/arch/x86/include/asm/desc.h +++ b/arch/x86/include/asm/desc.h @@ -121,7 +121,6 @@ static inline int desc_empty(const void *ptr) #define load_ldt(ldt) asm volatile("lldt %0"::"m" (ldt)) #define store_gdt(dtr) native_store_gdt(dtr) -#define store_idt(dtr) native_store_idt(dtr) #define store_tr(tr) (tr = native_store_tr()) #define load_TLS(t, cpu) native_load_tls(t, cpu) @@ -228,7 +227,7 @@ static inline void native_store_gdt(struct desc_ptr *dtr) asm volatile("sgdt %0":"=m" (*dtr)); } -static inline void native_store_idt(struct desc_ptr *dtr) +static inline void store_idt(struct desc_ptr *dtr) { asm volatile("sidt %0":"=m" (*dtr)); } diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h index c25dd22f7c70..12deec722cf0 100644 --- a/arch/x86/include/asm/paravirt.h +++ b/arch/x86/include/asm/paravirt.h @@ -71,11 +71,6 @@ static inline void write_cr3(unsigned long x) PVOP_VCALL1(pv_mmu_ops.write_cr3, x); } -static inline unsigned long __read_cr4(void) -{ - return PVOP_CALL0(unsigned long, pv_cpu_ops.read_cr4); -} - static inline void __write_cr4(unsigned long x) { PVOP_VCALL1(pv_cpu_ops.write_cr4, x); @@ -228,10 +223,6 @@ static inline void set_ldt(const void *addr, unsigned entries) { PVOP_VCALL2(pv_cpu_ops.set_ldt, addr, entries); } -static inline void store_idt(struct desc_ptr *dtr) -{ - PVOP_VCALL1(pv_cpu_ops.store_idt, dtr); -} static inline unsigned long paravirt_store_tr(void) { return PVOP_CALL0(unsigned long, pv_cpu_ops.store_tr); @@ -365,12 +356,6 @@ static inline void paravirt_release_p4d(unsigned long pfn) PVOP_VCALL1(pv_mmu_ops.release_p4d, pfn); } -static inline void pte_update(struct mm_struct *mm, unsigned long addr, - pte_t *ptep) -{ - PVOP_VCALL3(pv_mmu_ops.pte_update, mm, addr, ptep); -} - static inline pte_t __pte(pteval_t val) { pteval_t ret; @@ -472,28 +457,6 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, PVOP_VCALL4(pv_mmu_ops.set_pte_at, mm, addr, ptep, pte.pte); } -static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr, - pmd_t *pmdp, pmd_t pmd) -{ - if (sizeof(pmdval_t) > sizeof(long)) - /* 5 arg words */ - pv_mmu_ops.set_pmd_at(mm, addr, pmdp, pmd); - else - PVOP_VCALL4(pv_mmu_ops.set_pmd_at, mm, addr, pmdp, - native_pmd_val(pmd)); -} - -static inline void set_pud_at(struct mm_struct *mm, unsigned long addr, - pud_t *pudp, pud_t pud) -{ - if (sizeof(pudval_t) > sizeof(long)) - /* 5 arg words */ - pv_mmu_ops.set_pud_at(mm, addr, pudp, pud); - else - PVOP_VCALL4(pv_mmu_ops.set_pud_at, mm, addr, pudp, - native_pud_val(pud)); -} - static inline void set_pmd(pmd_t *pmdp, pmd_t pmd) { pmdval_t val = native_pmd_val(pmd); diff --git a/arch/x86/include/asm/paravirt_types.h b/arch/x86/include/asm/paravirt_types.h index 6b64fc6367f2..42873edd9f9d 100644 --- a/arch/x86/include/asm/paravirt_types.h +++ b/arch/x86/include/asm/paravirt_types.h @@ -107,7 +107,6 @@ struct pv_cpu_ops { unsigned long (*read_cr0)(void); void (*write_cr0)(unsigned long); - unsigned long (*read_cr4)(void); void (*write_cr4)(unsigned long); #ifdef CONFIG_X86_64 @@ -119,8 +118,6 @@ struct pv_cpu_ops { void (*load_tr_desc)(void); void (*load_gdt)(const struct desc_ptr *); void (*load_idt)(const struct desc_ptr *); - /* store_gdt has been removed. */ - void (*store_idt)(struct desc_ptr *); void (*set_ldt)(const void *desc, unsigned entries); unsigned long (*store_tr)(void); void (*load_tls)(struct thread_struct *t, unsigned int cpu); @@ -245,12 +242,6 @@ struct pv_mmu_ops { void (*set_pte_at)(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pteval); void (*set_pmd)(pmd_t *pmdp, pmd_t pmdval); - void (*set_pmd_at)(struct mm_struct *mm, unsigned long addr, - pmd_t *pmdp, pmd_t pmdval); - void (*set_pud_at)(struct mm_struct *mm, unsigned long addr, - pud_t *pudp, pud_t pudval); - void (*pte_update)(struct mm_struct *mm, unsigned long addr, - pte_t *ptep); pte_t (*ptep_modify_prot_start)(struct mm_struct *mm, unsigned long addr, pte_t *ptep); diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index 5b4c44d419c5..b714934512b3 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h @@ -55,8 +55,6 @@ extern pmdval_t early_pmd_flags; #else /* !CONFIG_PARAVIRT */ #define set_pte(ptep, pte) native_set_pte(ptep, pte) #define set_pte_at(mm, addr, ptep, pte) native_set_pte_at(mm, addr, ptep, pte) -#define set_pmd_at(mm, addr, pmdp, pmd) native_set_pmd_at(mm, addr, pmdp, pmd) -#define set_pud_at(mm, addr, pudp, pud) native_set_pud_at(mm, addr, pudp, pud) #define set_pte_atomic(ptep, pte) \ native_set_pte_atomic(ptep, pte) @@ -87,8 +85,6 @@ extern pmdval_t early_pmd_flags; #define pte_clear(mm, addr, ptep) native_pte_clear(mm, addr, ptep) #define pmd_clear(pmd) native_pmd_clear(pmd) -#define pte_update(mm, addr, ptep) do { } while (0) - #define pgd_val(x) native_pgd_val(x) #define __pgd(x) native_make_pgd(x) @@ -979,31 +975,18 @@ static inline void native_set_pte_at(struct mm_struct *mm, unsigned long addr, native_set_pte(ptep, pte); } -static inline void native_set_pmd_at(struct mm_struct *mm, unsigned long addr, - pmd_t *pmdp , pmd_t pmd) +static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr, + pmd_t *pmdp, pmd_t pmd) { native_set_pmd(pmdp, pmd); } -static inline void native_set_pud_at(struct mm_struct *mm, unsigned long addr, - pud_t *pudp, pud_t pud) +static inline void set_pud_at(struct mm_struct *mm, unsigned long addr, + pud_t *pudp, pud_t pud) { native_set_pud(pudp, pud); } -#ifndef CONFIG_PARAVIRT -/* - * Rules for using pte_update - it must be called after any PTE update which - * has not been done using the set_pte / clear_pte interfaces. It is used by - * shadow mode hypervisors to resynchronize the shadow page tables. Kernel PTE - * updates should either be sets, clears, or set_pte_atomic for P->P - * transitions, which means this hook should only be called for user PTEs. - * This hook implies a P->P protection or access change has taken place, which - * requires a subsequent TLB flush. - */ -#define pte_update(mm, addr, ptep) do { } while (0) -#endif - /* * We only update the dirty/accessed state if we set * the dirty bit by hand in the kernel, since the hardware @@ -1031,7 +1014,6 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { pte_t pte = native_ptep_get_and_clear(ptep); - pte_update(mm, addr, ptep); return pte; } @@ -1058,7 +1040,6 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { clear_bit(_PAGE_BIT_RW, (unsigned long *)&ptep->pte); - pte_update(mm, addr, ptep); } #define flush_tlb_fix_spurious_fault(vma, address) do { } while (0) diff --git a/arch/x86/include/asm/special_insns.h b/arch/x86/include/asm/special_insns.h index 9efaabf5b54b..a24dfcf79f4a 100644 --- a/arch/x86/include/asm/special_insns.h +++ b/arch/x86/include/asm/special_insns.h @@ -135,6 +135,11 @@ static inline void native_wbinvd(void) extern asmlinkage void native_load_gs_index(unsigned); +static inline unsigned long __read_cr4(void) +{ + return native_read_cr4(); +} + #ifdef CONFIG_PARAVIRT #include <asm/paravirt.h> #else @@ -173,11 +178,6 @@ static inline void write_cr3(unsigned long x) native_write_cr3(x); } -static inline unsigned long __read_cr4(void) -{ - return native_read_cr4(); -} - static inline void __write_cr4(unsigned long x) { native_write_cr4(x); diff --git a/arch/x86/include/uapi/asm/hyperv.h b/arch/x86/include/uapi/asm/hyperv.h index 7032f4d8dff3..f65d12504e80 100644 --- a/arch/x86/include/uapi/asm/hyperv.h +++ b/arch/x86/include/uapi/asm/hyperv.h @@ -153,12 +153,6 @@ #define HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED (1 << 11) /* - * HV_VP_SET available - */ -#define HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED (1 << 11) - - -/* * Crash notification flag. */ #define HV_CRASH_CTL_CRASH_NOTIFY (1ULL << 63) diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index fb1d3358a4af..775f10100d7f 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -169,21 +169,21 @@ static int __init x86_mpx_setup(char *s) __setup("nompx", x86_mpx_setup); #ifdef CONFIG_X86_64 -static int __init x86_pcid_setup(char *s) +static int __init x86_nopcid_setup(char *s) { - /* require an exact match without trailing characters */ - if (strlen(s)) - return 0; + /* nopcid doesn't accept parameters */ + if (s) + return -EINVAL; /* do not emit a message if the feature is not present */ if (!boot_cpu_has(X86_FEATURE_PCID)) - return 1; + return 0; setup_clear_cpu_cap(X86_FEATURE_PCID); pr_info("nopcid: PCID feature disabled\n"); - return 1; + return 0; } -__setup("nopcid", x86_pcid_setup); +early_param("nopcid", x86_nopcid_setup); #endif static int __init x86_noinvpcid_setup(char *s) @@ -329,38 +329,6 @@ static __always_inline void setup_smap(struct cpuinfo_x86 *c) } } -static void setup_pcid(struct cpuinfo_x86 *c) -{ - if (cpu_has(c, X86_FEATURE_PCID)) { - if (cpu_has(c, X86_FEATURE_PGE)) { - /* - * We'd like to use cr4_set_bits_and_update_boot(), - * but we can't. CR4.PCIDE is special and can only - * be set in long mode, and the early CPU init code - * doesn't know this and would try to restore CR4.PCIDE - * prior to entering long mode. - * - * Instead, we rely on the fact that hotplug, resume, - * etc all fully restore CR4 before they write anything - * that could have nonzero PCID bits to CR3. CR4.PCIDE - * has no effect on the page tables themselves, so we - * don't need it to be restored early. - */ - cr4_set_bits(X86_CR4_PCIDE); - } else { - /* - * flush_tlb_all(), as currently implemented, won't - * work if PCID is on but PGE is not. Since that - * combination doesn't exist on real hardware, there's - * no reason to try to fully support it, but it's - * polite to avoid corrupting data if we're on - * an improperly configured VM. - */ - clear_cpu_cap(c, X86_FEATURE_PCID); - } - } -} - /* * Protection Keys are not available in 32-bit mode. */ @@ -1175,9 +1143,6 @@ static void identify_cpu(struct cpuinfo_x86 *c) setup_smep(c); setup_smap(c); - /* Set up PCID */ - setup_pcid(c); - /* * The vendor-specific functions might have changed features. * Now we do "generic changes." diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index 3b3f713e15e5..236324e83a3a 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -59,8 +59,6 @@ void hyperv_vector_handler(struct pt_regs *regs) void hv_setup_vmbus_irq(void (*handler)(void)) { vmbus_handler = handler; - /* Setup the IDT for hypervisor callback */ - alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR, hyperv_callback_vector); } void hv_remove_vmbus_irq(void) @@ -251,6 +249,8 @@ static void __init ms_hyperv_init_platform(void) */ x86_platform.apic_post_init = hyperv_init; hyperv_setup_mmu_ops(); + /* Setup the IDT for hypervisor callback */ + alloc_intr_gate(HYPERVISOR_CALLBACK_VECTOR, hyperv_callback_vector); #endif } diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c index a14df9eecfed..19a3e8f961c7 100644 --- a/arch/x86/kernel/paravirt.c +++ b/arch/x86/kernel/paravirt.c @@ -327,7 +327,6 @@ __visible struct pv_cpu_ops pv_cpu_ops = { .set_debugreg = native_set_debugreg, .read_cr0 = native_read_cr0, .write_cr0 = native_write_cr0, - .read_cr4 = native_read_cr4, .write_cr4 = native_write_cr4, #ifdef CONFIG_X86_64 .read_cr8 = native_read_cr8, @@ -343,7 +342,6 @@ __visible struct pv_cpu_ops pv_cpu_ops = { .set_ldt = native_set_ldt, .load_gdt = native_load_gdt, .load_idt = native_load_idt, - .store_idt = native_store_idt, .store_tr = native_store_tr, .load_tls = native_load_tls, #ifdef CONFIG_X86_64 @@ -411,8 +409,6 @@ struct pv_mmu_ops pv_mmu_ops __ro_after_init = { .set_pte = native_set_pte, .set_pte_at = native_set_pte_at, .set_pmd = native_set_pmd, - .set_pmd_at = native_set_pmd_at, - .pte_update = paravirt_nop, .ptep_modify_prot_start = __ptep_modify_prot_start, .ptep_modify_prot_commit = __ptep_modify_prot_commit, @@ -424,7 +420,6 @@ struct pv_mmu_ops pv_mmu_ops __ro_after_init = { .pmd_clear = native_pmd_clear, #endif .set_pud = native_set_pud, - .set_pud_at = native_set_pud_at, .pmd_val = PTE_IDENT, .make_pmd = PTE_IDENT, diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index d84afb0a322d..0957dd73d127 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -1178,8 +1178,11 @@ void __init setup_arch(char **cmdline_p) * with the current CR4 value. This may not be necessary, but * auditing all the early-boot CR4 manipulation would be needed to * rule it out. + * + * Mask off features that don't work outside long mode (just + * PCIDE for now). */ - mmu_cr4_features = __read_cr4(); + mmu_cr4_features = __read_cr4() & ~X86_CR4_PCIDE; memblock_set_current_limit(get_max_mapped()); diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index cd6622c3204e..0854ff169274 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -226,10 +226,12 @@ static int enable_start_cpu0; static void notrace start_secondary(void *unused) { /* - * Don't put *anything* before cpu_init(), SMP booting is too - * fragile that we want to limit the things done here to the - * most necessary things. + * Don't put *anything* except direct CPU state initialization + * before cpu_init(), SMP booting is too fragile that we want to + * limit the things done here to the most necessary things. */ + if (boot_cpu_has(X86_FEATURE_PCID)) + __write_cr4(__read_cr4() | X86_CR4_PCIDE); cpu_init(); x86_cpuinit.early_percpu_clock_init(); preempt_disable(); diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 4253adef9044..699704d4bc9e 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -5192,7 +5192,7 @@ static void vmx_set_constant_host_state(struct vcpu_vmx *vmx) vmcs_write16(HOST_SS_SELECTOR, __KERNEL_DS); /* 22.2.4 */ vmcs_write16(HOST_TR_SELECTOR, GDT_ENTRY_TSS*8); /* 22.2.4 */ - native_store_idt(&dt); + store_idt(&dt); vmcs_writel(HOST_IDTR_BASE, dt.address); /* 22.2.4 */ vmx->host_idt_base = dt.address; diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 7777ccc0e9f9..af5c1ed21d43 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -19,6 +19,7 @@ #include <asm/microcode.h> #include <asm/kaslr.h> #include <asm/hypervisor.h> +#include <asm/cpufeature.h> /* * We need to define the tracepoints somewhere, and tlb.c @@ -193,6 +194,38 @@ static void __init probe_page_size_mask(void) } } +static void setup_pcid(void) +{ +#ifdef CONFIG_X86_64 + if (boot_cpu_has(X86_FEATURE_PCID)) { + if (boot_cpu_has(X86_FEATURE_PGE)) { + /* + * This can't be cr4_set_bits_and_update_boot() -- + * the trampoline code can't handle CR4.PCIDE and + * it wouldn't do any good anyway. Despite the name, + * cr4_set_bits_and_update_boot() doesn't actually + * cause the bits in question to remain set all the + * way through the secondary boot asm. + * + * Instead, we brute-force it and set CR4.PCIDE + * manually in start_secondary(). + */ + cr4_set_bits(X86_CR4_PCIDE); + } else { + /* + * flush_tlb_all(), as currently implemented, won't + * work if PCID is on but PGE is not. Since that + * combination doesn't exist on real hardware, there's + * no reason to try to fully support it, but it's + * polite to avoid corrupting data if we're on + * an improperly configured VM. + */ + setup_clear_cpu_cap(X86_FEATURE_PCID); + } + } +#endif +} + #ifdef CONFIG_X86_32 #define NR_RANGE_MR 3 #else /* CONFIG_X86_64 */ @@ -592,6 +625,7 @@ void __init init_mem_mapping(void) unsigned long end; probe_page_size_mask(); + setup_pcid(); #ifdef CONFIG_X86_64 end = max_pfn << PAGE_SHIFT; diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c index 218834a3e9ad..b372f3442bbf 100644 --- a/arch/x86/mm/pgtable.c +++ b/arch/x86/mm/pgtable.c @@ -426,10 +426,8 @@ int ptep_set_access_flags(struct vm_area_struct *vma, { int changed = !pte_same(*ptep, entry); - if (changed && dirty) { + if (changed && dirty) *ptep = entry; - pte_update(vma->vm_mm, address, ptep); - } return changed; } @@ -486,9 +484,6 @@ int ptep_test_and_clear_young(struct vm_area_struct *vma, ret = test_and_clear_bit(_PAGE_BIT_ACCESSED, (unsigned long *) &ptep->pte); - if (ret) - pte_update(vma->vm_mm, addr, ptep); - return ret; } diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c index 37689a7cc03b..1ab3821f9e26 100644 --- a/arch/x86/mm/tlb.c +++ b/arch/x86/mm/tlb.c @@ -121,8 +121,28 @@ void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next, * hypothetical buggy code that directly switches to swapper_pg_dir * without going through leave_mm() / switch_mm_irqs_off() or that * does something like write_cr3(read_cr3_pa()). + * + * Only do this check if CONFIG_DEBUG_VM=y because __read_cr3() + * isn't free. */ - VM_BUG_ON(__read_cr3() != (__sme_pa(real_prev->pgd) | prev_asid)); +#ifdef CONFIG_DEBUG_VM + if (WARN_ON_ONCE(__read_cr3() != + (__sme_pa(real_prev->pgd) | prev_asid))) { + /* + * If we were to BUG here, we'd be very likely to kill + * the system so hard that we don't see the call trace. + * Try to recover instead by ignoring the error and doing + * a global flush to minimize the chance of corruption. + * + * (This is far from being a fully correct recovery. + * Architecturally, the CPU could prefetch something + * back into an incorrect ASID slot and leave it there + * to cause trouble down the road. It's better than + * nothing, though.) + */ + __flush_tlb_all(); + } +#endif if (real_prev == next) { VM_BUG_ON(this_cpu_read(cpu_tlbstate.ctxs[prev_asid].ctx_id) != diff --git a/arch/x86/power/hibernate_64.c b/arch/x86/power/hibernate_64.c index f2598d81cd55..f910c514438f 100644 --- a/arch/x86/power/hibernate_64.c +++ b/arch/x86/power/hibernate_64.c @@ -295,7 +295,26 @@ int arch_hibernation_header_save(void *addr, unsigned int max_size) return -EOVERFLOW; rdr->jump_address = (unsigned long)restore_registers; rdr->jump_address_phys = __pa_symbol(restore_registers); - rdr->cr3 = restore_cr3; + + /* + * The restore code fixes up CR3 and CR4 in the following sequence: + * + * [in hibernation asm] + * 1. CR3 <= temporary page tables + * 2. CR4 <= mmu_cr4_features (from the kernel that restores us) + * 3. CR3 <= rdr->cr3 + * 4. CR4 <= mmu_cr4_features (from us, i.e. the image kernel) + * [in restore_processor_state()] + * 5. CR4 <= saved CR4 + * 6. CR3 <= saved CR3 + * + * Our mmu_cr4_features has CR4.PCIDE=0, and toggling + * CR4.PCIDE while CR3's PCID bits are nonzero is illegal, so + * rdr->cr3 needs to point to valid page tables but must not + * have any of the PCID bits set. + */ + rdr->cr3 = restore_cr3 & ~CR3_PCID_MASK; + rdr->magic = RESTORE_MAGIC; hibernation_e820_save(rdr->e820_digest); diff --git a/arch/x86/xen/enlighten_pv.c b/arch/x86/xen/enlighten_pv.c index ae2a2e2d6362..69b9deff7e5c 100644 --- a/arch/x86/xen/enlighten_pv.c +++ b/arch/x86/xen/enlighten_pv.c @@ -1038,7 +1038,6 @@ static const struct pv_cpu_ops xen_cpu_ops __initconst = { .read_cr0 = xen_read_cr0, .write_cr0 = xen_write_cr0, - .read_cr4 = native_read_cr4, .write_cr4 = xen_write_cr4, #ifdef CONFIG_X86_64 @@ -1073,7 +1072,6 @@ static const struct pv_cpu_ops xen_cpu_ops __initconst = { .alloc_ldt = xen_alloc_ldt, .free_ldt = xen_free_ldt, - .store_idt = native_store_idt, .store_tr = xen_store_tr, .write_ldt_entry = xen_write_ldt_entry, diff --git a/arch/x86/xen/mmu_pv.c b/arch/x86/xen/mmu_pv.c index 6b983b300666..509f560bd0c6 100644 --- a/arch/x86/xen/mmu_pv.c +++ b/arch/x86/xen/mmu_pv.c @@ -2409,8 +2409,6 @@ static const struct pv_mmu_ops xen_mmu_ops __initconst = { .flush_tlb_single = xen_flush_tlb_single, .flush_tlb_others = xen_flush_tlb_others, - .pte_update = paravirt_nop, - .pgd_alloc = xen_pgd_alloc, .pgd_free = xen_pgd_free, diff --git a/block/blk-core.c b/block/blk-core.c index d709c0e3a2ac..aebe676225e6 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2342,7 +2342,12 @@ blk_status_t blk_insert_cloned_request(struct request_queue *q, struct request * if (q->mq_ops) { if (blk_queue_io_stat(q)) blk_account_io_start(rq, true); - blk_mq_sched_insert_request(rq, false, true, false, false); + /* + * Since we have a scheduler attached on the top device, + * bypass a potential scheduler on the bottom device for + * insert. + */ + blk_mq_request_bypass_insert(rq); return BLK_STS_OK; } diff --git a/block/blk-lib.c b/block/blk-lib.c index e01adb5145b3..62240f8832ca 100644 --- a/block/blk-lib.c +++ b/block/blk-lib.c @@ -269,9 +269,9 @@ static int __blkdev_issue_write_zeroes(struct block_device *bdev, */ static unsigned int __blkdev_sectors_to_bio_pages(sector_t nr_sects) { - sector_t bytes = (nr_sects << 9) + PAGE_SIZE - 1; + sector_t pages = DIV_ROUND_UP_SECTOR_T(nr_sects, PAGE_SIZE / 512); - return min(bytes >> PAGE_SHIFT, (sector_t)BIO_MAX_PAGES); + return min(pages, (sector_t)BIO_MAX_PAGES); } /** diff --git a/block/blk-mq.c b/block/blk-mq.c index 3f18cff80050..98a18609755e 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -1401,6 +1401,22 @@ void __blk_mq_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq, blk_mq_hctx_mark_pending(hctx, ctx); } +/* + * Should only be used carefully, when the caller knows we want to + * bypass a potential IO scheduler on the target device. + */ +void blk_mq_request_bypass_insert(struct request *rq) +{ + struct blk_mq_ctx *ctx = rq->mq_ctx; + struct blk_mq_hw_ctx *hctx = blk_mq_map_queue(rq->q, ctx->cpu); + + spin_lock(&hctx->lock); + list_add_tail(&rq->queuelist, &hctx->dispatch); + spin_unlock(&hctx->lock); + + blk_mq_run_hw_queue(hctx, false); +} + void blk_mq_insert_requests(struct blk_mq_hw_ctx *hctx, struct blk_mq_ctx *ctx, struct list_head *list) diff --git a/block/blk-mq.h b/block/blk-mq.h index 98252b79b80b..ef15b3414da5 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -54,6 +54,7 @@ int blk_mq_alloc_rqs(struct blk_mq_tag_set *set, struct blk_mq_tags *tags, */ void __blk_mq_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq, bool at_head); +void blk_mq_request_bypass_insert(struct request *rq); void blk_mq_insert_requests(struct blk_mq_hw_ctx *hctx, struct blk_mq_ctx *ctx, struct list_head *list); diff --git a/block/opal_proto.h b/block/opal_proto.h index f40c9acf8895..e20be8258854 100644 --- a/block/opal_proto.h +++ b/block/opal_proto.h @@ -46,6 +46,7 @@ enum opal_response_token { #define GENERIC_HOST_SESSION_NUM 0x41 #define TPER_SYNC_SUPPORTED 0x01 +#define MBR_ENABLED_MASK 0x10 #define TINY_ATOM_DATA_MASK 0x3F #define TINY_ATOM_SIGNED 0x40 diff --git a/block/sed-opal.c b/block/sed-opal.c index 9b30ae5ab843..9ed51d0c6b1d 100644 --- a/block/sed-opal.c +++ b/block/sed-opal.c @@ -80,6 +80,7 @@ struct parsed_resp { struct opal_dev { bool supported; + bool mbr_enabled; void *data; sec_send_recv *send_recv; @@ -283,6 +284,14 @@ static bool check_tper(const void *data) return true; } +static bool check_mbrenabled(const void *data) +{ + const struct d0_locking_features *lfeat = data; + u8 sup_feat = lfeat->supported_features; + + return !!(sup_feat & MBR_ENABLED_MASK); +} + static bool check_sum(const void *data) { const struct d0_single_user_mode *sum = data; @@ -417,6 +426,7 @@ static int opal_discovery0_end(struct opal_dev *dev) u32 hlen = be32_to_cpu(hdr->length); print_buffer(dev->resp, hlen); + dev->mbr_enabled = false; if (hlen > IO_BUFFER_LENGTH - sizeof(*hdr)) { pr_debug("Discovery length overflows buffer (%zu+%u)/%u\n", @@ -442,6 +452,8 @@ static int opal_discovery0_end(struct opal_dev *dev) check_geometry(dev, body); break; case FC_LOCKING: + dev->mbr_enabled = check_mbrenabled(body->features); + break; case FC_ENTERPRISE: case FC_DATASTORE: /* some ignored properties */ @@ -2190,6 +2202,21 @@ static int __opal_lock_unlock(struct opal_dev *dev, return next(dev); } +static int __opal_set_mbr_done(struct opal_dev *dev, struct opal_key *key) +{ + u8 mbr_done_tf = 1; + const struct opal_step mbrdone_step [] = { + { opal_discovery0, }, + { start_admin1LSP_opal_session, key }, + { set_mbr_done, &mbr_done_tf }, + { end_opal_session, }, + { NULL, } + }; + + dev->steps = mbrdone_step; + return next(dev); +} + static int opal_lock_unlock(struct opal_dev *dev, struct opal_lock_unlock *lk_unlk) { @@ -2345,6 +2372,11 @@ bool opal_unlock_from_suspend(struct opal_dev *dev) suspend->unlk.session.sum); was_failure = true; } + if (dev->mbr_enabled) { + ret = __opal_set_mbr_done(dev, &suspend->unlk.session.opal_key); + if (ret) + pr_debug("Failed to set MBR Done in S3 resume\n"); + } } mutex_unlock(&dev->dev_lock); return was_failure; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 68ca2d9fcd73..1c4e1aa6767e 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -31,6 +31,13 @@ config COMMON_CLK_WM831X source "drivers/clk/versatile/Kconfig" +config CLK_HSDK + bool "PLL Driver for HSDK platform" + depends on OF || COMPILE_TEST + ---help--- + This driver supports the HSDK core, system, ddr, tunnel and hdmi PLLs + control. + config COMMON_CLK_MAX77686 tristate "Clock driver for Maxim 77620/77686/77802 MFD" depends on MFD_MAX77686 || MFD_MAX77620 || COMPILE_TEST @@ -39,10 +46,10 @@ config COMMON_CLK_MAX77686 clock. config COMMON_CLK_RK808 - tristate "Clock driver for RK808/RK818" + tristate "Clock driver for RK805/RK808/RK818" depends on MFD_RK808 ---help--- - This driver supports RK808 and RK818 crystal oscillator clock. These + This driver supports RK805, RK808 and RK818 crystal oscillator clock. These multi-function devices have two fixed-rate oscillators, clocked at 32KHz each. Clkout1 is always on, Clkout2 can off by control register. @@ -210,14 +217,14 @@ config COMMON_CLK_OXNAS Support for the OXNAS SoC Family clocks. config COMMON_CLK_VC5 - tristate "Clock driver for IDT VersaClock5 devices" + tristate "Clock driver for IDT VersaClock 5,6 devices" depends on I2C depends on OF select REGMAP_I2C help ---help--- - This driver supports the IDT VersaClock5 programmable clock - generator. + This driver supports the IDT VersaClock 5 and VersaClock 6 + programmable clock generators. source "drivers/clk/bcm/Kconfig" source "drivers/clk/hisilicon/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index cd376b3fb47a..c99f363826f0 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -27,8 +27,8 @@ obj-$(CONFIG_COMMON_CLK_CS2000_CP) += clk-cs2000-cp.o obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o +obj-$(CONFIG_CLK_HSDK) += clk-hsdk-pll.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o -obj-$(CONFIG_ARCH_MB86S7X) += clk-mb86s7x.o obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o @@ -44,6 +44,7 @@ obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_COMMON_CLK_SI514) += clk-si514.o obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o obj-$(CONFIG_ARCH_STM32) += clk-stm32f4.o +obj-$(CONFIG_ARCH_STM32) += clk-stm32h7.o obj-$(CONFIG_ARCH_TANGO) += clk-tango4.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o obj-$(CONFIG_ARCH_U300) += clk-u300.o diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 13e67bd35cff..c68947b65a4c 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile @@ -6,6 +6,7 @@ obj-y += pmc.o sckc.o obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o obj-y += clk-system.o clk-peripheral.o clk-programmable.o +obj-$(CONFIG_HAVE_AT91_AUDIO_PLL) += clk-audio-pll.o obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c new file mode 100644 index 000000000000..da7bafcfbe70 --- /dev/null +++ b/drivers/clk/at91/clk-audio-pll.c @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2016 Atmel Corporation, + * Songjun Wu <songjun.wu@atmel.com>, + * Nicolas Ferre <nicolas.ferre@atmel.com> + * Copyright (C) 2017 Free Electrons, + * Quentin Schulz <quentin.schulz@free-electrons.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The Sama5d2 SoC has two audio PLLs (PMC and PAD) that shares the same parent + * (FRAC). FRAC can output between 620 and 700MHz and only multiply the rate of + * its own parent. PMC and PAD can then divide the FRAC rate to best match the + * asked rate. + * + * Traits of FRAC clock: + * enable - clk_enable writes nd, fracr parameters and enables PLL + * rate - rate is adjustable. + * clk->rate = parent->rate * ((nd + 1) + (fracr / 2^22)) + * parent - fixed parent. No clk_set_parent support + * + * Traits of PMC clock: + * enable - clk_enable writes qdpmc, and enables PMC output + * rate - rate is adjustable. + * clk->rate = parent->rate / (qdpmc + 1) + * parent - fixed parent. No clk_set_parent support + * + * Traits of PAD clock: + * enable - clk_enable writes divisors and enables PAD output + * rate - rate is adjustable. + * clk->rate = parent->rate / (qdaudio * div)) + * parent - fixed parent. No clk_set_parent support + * + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clk/at91_pmc.h> +#include <linux/of.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define AUDIO_PLL_DIV_FRAC BIT(22) +#define AUDIO_PLL_ND_MAX (AT91_PMC_AUDIO_PLL_ND_MASK >> \ + AT91_PMC_AUDIO_PLL_ND_OFFSET) + +#define AUDIO_PLL_QDPAD(qd, div) ((AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(qd) & \ + AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK) | \ + (AT91_PMC_AUDIO_PLL_QDPAD_DIV(div) & \ + AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK)) + +#define AUDIO_PLL_QDPMC_MAX (AT91_PMC_AUDIO_PLL_QDPMC_MASK >> \ + AT91_PMC_AUDIO_PLL_QDPMC_OFFSET) + +#define AUDIO_PLL_FOUT_MIN 620000000UL +#define AUDIO_PLL_FOUT_MAX 700000000UL + +struct clk_audio_frac { + struct clk_hw hw; + struct regmap *regmap; + u32 fracr; + u8 nd; +}; + +struct clk_audio_pad { + struct clk_hw hw; + struct regmap *regmap; + u8 qdaudio; + u8 div; +}; + +struct clk_audio_pmc { + struct clk_hw hw; + struct regmap *regmap; + u8 qdpmc; +}; + +#define to_clk_audio_frac(hw) container_of(hw, struct clk_audio_frac, hw) +#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw) +#define to_clk_audio_pmc(hw) container_of(hw, struct clk_audio_pmc, hw) + +static int clk_audio_pll_frac_enable(struct clk_hw *hw) +{ + struct clk_audio_frac *frac = to_clk_audio_frac(hw); + + regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_RESETN, 0); + regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_RESETN, + AT91_PMC_AUDIO_PLL_RESETN); + regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL1, + AT91_PMC_AUDIO_PLL_FRACR_MASK, frac->fracr); + + /* + * reset and enable have to be done in 2 separated writes + * for AT91_PMC_AUDIO_PLL0 + */ + regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PLLEN | + AT91_PMC_AUDIO_PLL_ND_MASK, + AT91_PMC_AUDIO_PLL_PLLEN | + AT91_PMC_AUDIO_PLL_ND(frac->nd)); + + return 0; +} + +static int clk_audio_pll_pad_enable(struct clk_hw *hw) +{ + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); + + regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL1, + AT91_PMC_AUDIO_PLL_QDPAD_MASK, + AUDIO_PLL_QDPAD(apad_ck->qdaudio, apad_ck->div)); + regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PADEN, AT91_PMC_AUDIO_PLL_PADEN); + + return 0; +} + +static int clk_audio_pll_pmc_enable(struct clk_hw *hw) +{ + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); + + regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PMCEN | + AT91_PMC_AUDIO_PLL_QDPMC_MASK, + AT91_PMC_AUDIO_PLL_PMCEN | + AT91_PMC_AUDIO_PLL_QDPMC(apmc_ck->qdpmc)); + return 0; +} + +static void clk_audio_pll_frac_disable(struct clk_hw *hw) +{ + struct clk_audio_frac *frac = to_clk_audio_frac(hw); + + regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PLLEN, 0); + /* do it in 2 separated writes */ + regmap_update_bits(frac->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_RESETN, 0); +} + +static void clk_audio_pll_pad_disable(struct clk_hw *hw) +{ + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); + + regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PADEN, 0); +} + +static void clk_audio_pll_pmc_disable(struct clk_hw *hw) +{ + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); + + regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0, + AT91_PMC_AUDIO_PLL_PMCEN, 0); +} + +static unsigned long clk_audio_pll_fout(unsigned long parent_rate, + unsigned long nd, unsigned long fracr) +{ + unsigned long long fr = (unsigned long long)parent_rate * fracr; + + pr_debug("A PLL: %s, fr = %llu\n", __func__, fr); + + fr = DIV_ROUND_CLOSEST_ULL(fr, AUDIO_PLL_DIV_FRAC); + + pr_debug("A PLL: %s, fr = %llu\n", __func__, fr); + + return parent_rate * (nd + 1) + fr; +} + +static unsigned long clk_audio_pll_frac_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_audio_frac *frac = to_clk_audio_frac(hw); + unsigned long fout; + + fout = clk_audio_pll_fout(parent_rate, frac->nd, frac->fracr); + + pr_debug("A PLL: %s, fout = %lu (nd = %u, fracr = %lu)\n", __func__, + fout, frac->nd, (unsigned long)frac->fracr); + + return fout; +} + +static unsigned long clk_audio_pll_pad_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); + unsigned long apad_rate = 0; + + if (apad_ck->qdaudio && apad_ck->div) + apad_rate = parent_rate / (apad_ck->qdaudio * apad_ck->div); + + pr_debug("A PLL/PAD: %s, apad_rate = %lu (div = %u, qdaudio = %u)\n", + __func__, apad_rate, apad_ck->div, apad_ck->qdaudio); + + return apad_rate; +} + +static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); + unsigned long apmc_rate = 0; + + apmc_rate = parent_rate / (apmc_ck->qdpmc + 1); + + pr_debug("A PLL/PMC: %s, apmc_rate = %lu (qdpmc = %u)\n", __func__, + apmc_rate, apmc_ck->qdpmc); + + return apmc_rate; +} + +static int clk_audio_pll_frac_compute_frac(unsigned long rate, + unsigned long parent_rate, + unsigned long *nd, + unsigned long *fracr) +{ + unsigned long long tmp, rem; + + if (!rate) + return -EINVAL; + + tmp = rate; + rem = do_div(tmp, parent_rate); + if (!tmp || tmp >= AUDIO_PLL_ND_MAX) + return -EINVAL; + + *nd = tmp - 1; + + tmp = rem * AUDIO_PLL_DIV_FRAC; + tmp = DIV_ROUND_CLOSEST_ULL(tmp, parent_rate); + if (tmp > AT91_PMC_AUDIO_PLL_FRACR_MASK) + return -EINVAL; + + /* we can cast here as we verified the bounds just above */ + *fracr = (unsigned long)tmp; + + return 0; +} + +static int clk_audio_pll_frac_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + unsigned long fracr, nd; + int ret; + + pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, + req->rate, req->best_parent_rate); + + req->rate = clamp(req->rate, AUDIO_PLL_FOUT_MIN, AUDIO_PLL_FOUT_MAX); + + req->min_rate = max(req->min_rate, AUDIO_PLL_FOUT_MIN); + req->max_rate = min(req->max_rate, AUDIO_PLL_FOUT_MAX); + + ret = clk_audio_pll_frac_compute_frac(req->rate, req->best_parent_rate, + &nd, &fracr); + if (ret) + return ret; + + req->rate = clk_audio_pll_fout(req->best_parent_rate, nd, fracr); + + req->best_parent_hw = clk_hw_get_parent(hw); + + pr_debug("A PLL: %s, best_rate = %lu (nd = %lu, fracr = %lu)\n", + __func__, req->rate, nd, fracr); + + return 0; +} + +static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_hw *pclk = clk_hw_get_parent(hw); + long best_rate = -EINVAL; + unsigned long best_parent_rate; + unsigned long tmp_qd; + u32 div; + long tmp_rate; + int tmp_diff; + int best_diff = -1; + + pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__, + rate, *parent_rate); + + /* + * Rate divisor is actually made of two different divisors, multiplied + * between themselves before dividing the rate. + * tmp_qd goes from 1 to 31 and div is either 2 or 3. + * In order to avoid testing twice the rate divisor (e.g. divisor 12 can + * be found with (tmp_qd, div) = (2, 6) or (3, 4)), we remove any loop + * for a rate divisor when div is 2 and tmp_qd is a multiple of 3. + * We cannot inverse it (condition div is 3 and tmp_qd is even) or we + * would miss some rate divisor that aren't reachable with div being 2 + * (e.g. rate divisor 90 is made with div = 3 and tmp_qd = 30, thus + * tmp_qd is even so we skip it because we think div 2 could make this + * rate divisor which isn't possible since tmp_qd has to be <= 31). + */ + for (tmp_qd = 1; tmp_qd < AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX; tmp_qd++) + for (div = 2; div <= 3; div++) { + if (div == 2 && tmp_qd % 3 == 0) + continue; + + best_parent_rate = clk_hw_round_rate(pclk, + rate * tmp_qd * div); + tmp_rate = best_parent_rate / (div * tmp_qd); + tmp_diff = abs(rate - tmp_rate); + + if (best_diff < 0 || best_diff > tmp_diff) { + *parent_rate = best_parent_rate; + best_rate = tmp_rate; + best_diff = tmp_diff; + } + } + + pr_debug("A PLL/PAD: %s, best_rate = %ld, best_parent_rate = %lu\n", + __func__, best_rate, best_parent_rate); + + return best_rate; +} + +static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_hw *pclk = clk_hw_get_parent(hw); + long best_rate = -EINVAL; + unsigned long best_parent_rate = 0; + u32 tmp_qd = 0, div; + long tmp_rate; + int tmp_diff; + int best_diff = -1; + + pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__, + rate, *parent_rate); + + for (div = 1; div <= AUDIO_PLL_QDPMC_MAX; div++) { + best_parent_rate = clk_round_rate(pclk->clk, rate * div); + tmp_rate = best_parent_rate / div; + tmp_diff = abs(rate - tmp_rate); + + if (best_diff < 0 || best_diff > tmp_diff) { + *parent_rate = best_parent_rate; + best_rate = tmp_rate; + best_diff = tmp_diff; + tmp_qd = div; + } + } + + pr_debug("A PLL/PMC: %s, best_rate = %ld, best_parent_rate = %lu (qd = %d)\n", + __func__, best_rate, *parent_rate, tmp_qd - 1); + + return best_rate; +} + +static int clk_audio_pll_frac_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_audio_frac *frac = to_clk_audio_frac(hw); + unsigned long fracr, nd; + int ret; + + pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate, + parent_rate); + + if (rate < AUDIO_PLL_FOUT_MIN || rate > AUDIO_PLL_FOUT_MAX) + return -EINVAL; + + ret = clk_audio_pll_frac_compute_frac(rate, parent_rate, &nd, &fracr); + if (ret) + return ret; + + frac->nd = nd; + frac->fracr = fracr; + + return 0; +} + +static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw); + u8 tmp_div; + + pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__, + rate, parent_rate); + + if (!rate) + return -EINVAL; + + tmp_div = parent_rate / rate; + if (tmp_div % 3 == 0) { + apad_ck->qdaudio = tmp_div / 3; + apad_ck->div = 3; + } else { + apad_ck->qdaudio = tmp_div / 2; + apad_ck->div = 2; + } + + return 0; +} + +static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw); + + if (!rate) + return -EINVAL; + + pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__, + rate, parent_rate); + + apmc_ck->qdpmc = parent_rate / rate - 1; + + return 0; +} + +static const struct clk_ops audio_pll_frac_ops = { + .enable = clk_audio_pll_frac_enable, + .disable = clk_audio_pll_frac_disable, + .recalc_rate = clk_audio_pll_frac_recalc_rate, + .determine_rate = clk_audio_pll_frac_determine_rate, + .set_rate = clk_audio_pll_frac_set_rate, +}; + +static const struct clk_ops audio_pll_pad_ops = { + .enable = clk_audio_pll_pad_enable, + .disable = clk_audio_pll_pad_disable, + .recalc_rate = clk_audio_pll_pad_recalc_rate, + .round_rate = clk_audio_pll_pad_round_rate, + .set_rate = clk_audio_pll_pad_set_rate, +}; + +static const struct clk_ops audio_pll_pmc_ops = { + .enable = clk_audio_pll_pmc_enable, + .disable = clk_audio_pll_pmc_disable, + .recalc_rate = clk_audio_pll_pmc_recalc_rate, + .round_rate = clk_audio_pll_pmc_round_rate, + .set_rate = clk_audio_pll_pmc_set_rate, +}; + +static int of_sama5d2_clk_audio_pll_setup(struct device_node *np, + struct clk_init_data *init, + struct clk_hw *hw, + struct regmap **clk_audio_regmap) +{ + struct regmap *regmap; + const char *parent_names[1]; + int ret; + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + init->name = np->name; + of_clk_parent_fill(np, parent_names, 1); + init->parent_names = parent_names; + init->num_parents = 1; + + hw->init = init; + *clk_audio_regmap = regmap; + + ret = clk_hw_register(NULL, hw); + if (ret) + return ret; + + return of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); +} + +static void __init of_sama5d2_clk_audio_pll_frac_setup(struct device_node *np) +{ + struct clk_audio_frac *frac_ck; + struct clk_init_data init = {}; + + frac_ck = kzalloc(sizeof(*frac_ck), GFP_KERNEL); + if (!frac_ck) + return; + + init.ops = &audio_pll_frac_ops; + init.flags = CLK_SET_RATE_GATE; + + if (of_sama5d2_clk_audio_pll_setup(np, &init, &frac_ck->hw, + &frac_ck->regmap)) + kfree(frac_ck); +} + +static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np) +{ + struct clk_audio_pad *apad_ck; + struct clk_init_data init = {}; + + apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL); + if (!apad_ck) + return; + + init.ops = &audio_pll_pad_ops; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; + + if (of_sama5d2_clk_audio_pll_setup(np, &init, &apad_ck->hw, + &apad_ck->regmap)) + kfree(apad_ck); +} + +static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np) +{ + struct clk_audio_pad *apmc_ck; + struct clk_init_data init = {}; + + apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL); + if (!apmc_ck) + return; + + init.ops = &audio_pll_pmc_ops; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; + + if (of_sama5d2_clk_audio_pll_setup(np, &init, &apmc_ck->hw, + &apmc_ck->regmap)) + kfree(apmc_ck); +} + +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_frac_setup, + "atmel,sama5d2-clk-audio-pll-frac", + of_sama5d2_clk_audio_pll_frac_setup); +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup, + "atmel,sama5d2-clk-audio-pll-pad", + of_sama5d2_clk_audio_pll_pad_setup); +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup, + "atmel,sama5d2-clk-audio-pll-pmc", + of_sama5d2_clk_audio_pll_pmc_setup); diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c index f0b7ae904ce2..33481368740e 100644 --- a/drivers/clk/at91/clk-generated.c +++ b/drivers/clk/at91/clk-generated.c @@ -26,6 +26,13 @@ #define GENERATED_SOURCE_MAX 6 #define GENERATED_MAX_DIV 255 +#define GCK_ID_SSC0 43 +#define GCK_ID_SSC1 44 +#define GCK_ID_I2S0 54 +#define GCK_ID_I2S1 55 +#define GCK_ID_CLASSD 59 +#define GCK_INDEX_DT_AUDIO_PLL 5 + struct clk_generated { struct clk_hw hw; struct regmap *regmap; @@ -34,6 +41,7 @@ struct clk_generated { u32 id; u32 gckdiv; u8 parent_id; + bool audio_pll_allowed; }; #define to_clk_generated(hw) \ @@ -99,21 +107,41 @@ clk_generated_recalc_rate(struct clk_hw *hw, return DIV_ROUND_CLOSEST(parent_rate, gck->gckdiv + 1); } +static void clk_generated_best_diff(struct clk_rate_request *req, + struct clk_hw *parent, + unsigned long parent_rate, u32 div, + int *best_diff, long *best_rate) +{ + unsigned long tmp_rate; + int tmp_diff; + + if (!div) + tmp_rate = parent_rate; + else + tmp_rate = parent_rate / div; + tmp_diff = abs(req->rate - tmp_rate); + + if (*best_diff < 0 || *best_diff > tmp_diff) { + *best_rate = tmp_rate; + *best_diff = tmp_diff; + req->best_parent_rate = parent_rate; + req->best_parent_hw = parent; + } +} + static int clk_generated_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct clk_generated *gck = to_clk_generated(hw); struct clk_hw *parent = NULL; + struct clk_rate_request req_parent = *req; long best_rate = -EINVAL; - unsigned long tmp_rate, min_rate; + unsigned long min_rate, parent_rate; int best_diff = -1; - int tmp_diff; int i; + u32 div; - for (i = 0; i < clk_hw_get_num_parents(hw); i++) { - u32 div; - unsigned long parent_rate; - + for (i = 0; i < clk_hw_get_num_parents(hw) - 1; i++) { parent = clk_hw_get_parent_by_index(hw, i); if (!parent) continue; @@ -124,25 +152,43 @@ static int clk_generated_determine_rate(struct clk_hw *hw, (gck->range.max && min_rate > gck->range.max)) continue; - for (div = 1; div < GENERATED_MAX_DIV + 2; div++) { - tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div); - tmp_diff = abs(req->rate - tmp_rate); + div = DIV_ROUND_CLOSEST(parent_rate, req->rate); - if (best_diff < 0 || best_diff > tmp_diff) { - best_rate = tmp_rate; - best_diff = tmp_diff; - req->best_parent_rate = parent_rate; - req->best_parent_hw = parent; - } + clk_generated_best_diff(req, parent, parent_rate, div, + &best_diff, &best_rate); - if (!best_diff || tmp_rate < req->rate) - break; - } + if (!best_diff) + break; + } + + /* + * The audio_pll rate can be modified, unlike the five others clocks + * that should never be altered. + * The audio_pll can technically be used by multiple consumers. However, + * with the rate locking, the first consumer to enable to clock will be + * the one definitely setting the rate of the clock. + * Since audio IPs are most likely to request the same rate, we enforce + * that the only clks able to modify gck rate are those of audio IPs. + */ + + if (!gck->audio_pll_allowed) + goto end; + + parent = clk_hw_get_parent_by_index(hw, GCK_INDEX_DT_AUDIO_PLL); + if (!parent) + goto end; + + for (div = 1; div < GENERATED_MAX_DIV + 2; div++) { + req_parent.rate = req->rate * div; + __clk_determine_rate(parent, &req_parent); + clk_generated_best_diff(req, parent, req_parent.rate, div, + &best_diff, &best_rate); if (!best_diff) break; } +end: pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n", __func__, best_rate, __clk_get_name((req->best_parent_hw)->clk), @@ -252,7 +298,8 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, init.ops = &generated_ops; init.parent_names = parent_names; init.num_parents = num_parents; - init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT; gck->id = id; gck->hw.init = &init; @@ -284,6 +331,7 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np) struct device_node *gcknp; struct clk_range range = CLK_RANGE(0, 0); struct regmap *regmap; + struct clk_generated *gck; num_parents = of_clk_get_parent_count(np); if (num_parents == 0 || num_parents > GENERATED_SOURCE_MAX) @@ -315,6 +363,21 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np) hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name, parent_names, num_parents, id, &range); + + gck = to_clk_generated(hw); + + if (of_device_is_compatible(np, + "atmel,sama5d2-clk-generated")) { + if (gck->id == GCK_ID_SSC0 || gck->id == GCK_ID_SSC1 || + gck->id == GCK_ID_I2S0 || gck->id == GCK_ID_I2S1 || + gck->id == GCK_ID_CLASSD) + gck->audio_pll_allowed = true; + else + gck->audio_pll_allowed = false; + } else { + gck->audio_pll_allowed = false; + } + if (IS_ERR(hw)) continue; diff --git a/drivers/clk/axs10x/Makefile b/drivers/clk/axs10x/Makefile index 01996b871b06..d747deafbf1e 100644 --- a/drivers/clk/axs10x/Makefile +++ b/drivers/clk/axs10x/Makefile @@ -1 +1,2 @@ obj-y += i2s_pll_clock.o +obj-y += pll_clock.o diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c new file mode 100644 index 000000000000..25d8c240ddfb --- /dev/null +++ b/drivers/clk/axs10x/pll_clock.c @@ -0,0 +1,346 @@ +/* + * Synopsys AXS10X SDP Generic PLL clock driver + * + * Copyright (C) 2017 Synopsys + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/slab.h> +#include <linux/of.h> + +/* PLL registers addresses */ +#define PLL_REG_IDIV 0x0 +#define PLL_REG_FBDIV 0x4 +#define PLL_REG_ODIV 0x8 + +/* + * Bit fields of the PLL IDIV/FBDIV/ODIV registers: + * ________________________________________________________________________ + * |31 15| 14 | 13 | 12 |11 6|5 0| + * |-------RESRVED------|-NOUPDATE-|-BYPASS-|-EDGE-|--HIGHTIME--|--LOWTIME--| + * |____________________|__________|________|______|____________|___________| + * + * Following macros determine the way of access to these registers + * They should be set up only using the macros. + * reg should be an u32 variable. + */ + +#define PLL_REG_GET_LOW(reg) \ + (((reg) & (0x3F << 0)) >> 0) +#define PLL_REG_GET_HIGH(reg) \ + (((reg) & (0x3F << 6)) >> 6) +#define PLL_REG_GET_EDGE(reg) \ + (((reg) & (BIT(12))) ? 1 : 0) +#define PLL_REG_GET_BYPASS(reg) \ + (((reg) & (BIT(13))) ? 1 : 0) +#define PLL_REG_GET_NOUPD(reg) \ + (((reg) & (BIT(14))) ? 1 : 0) +#define PLL_REG_GET_PAD(reg) \ + (((reg) & (0x1FFFF << 15)) >> 15) + +#define PLL_REG_SET_LOW(reg, value) \ + { reg |= (((value) & 0x3F) << 0); } +#define PLL_REG_SET_HIGH(reg, value) \ + { reg |= (((value) & 0x3F) << 6); } +#define PLL_REG_SET_EDGE(reg, value) \ + { reg |= (((value) & 0x01) << 12); } +#define PLL_REG_SET_BYPASS(reg, value) \ + { reg |= (((value) & 0x01) << 13); } +#define PLL_REG_SET_NOUPD(reg, value) \ + { reg |= (((value) & 0x01) << 14); } +#define PLL_REG_SET_PAD(reg, value) \ + { reg |= (((value) & 0x1FFFF) << 15); } + +#define PLL_LOCK BIT(0) +#define PLL_ERROR BIT(1) +#define PLL_MAX_LOCK_TIME 100 /* 100 us */ + +struct axs10x_pll_cfg { + u32 rate; + u32 idiv; + u32 fbdiv; + u32 odiv; +}; + +static const struct axs10x_pll_cfg arc_pll_cfg[] = { + { 33333333, 1, 1, 1 }, + { 50000000, 1, 30, 20 }, + { 75000000, 2, 45, 10 }, + { 90000000, 2, 54, 10 }, + { 100000000, 1, 30, 10 }, + { 125000000, 2, 45, 6 }, + {} +}; + +static const struct axs10x_pll_cfg pgu_pll_cfg[] = { + { 25200000, 1, 84, 90 }, + { 50000000, 1, 100, 54 }, + { 74250000, 1, 44, 16 }, + {} +}; + +struct axs10x_pll_clk { + struct clk_hw hw; + void __iomem *base; + void __iomem *lock; + const struct axs10x_pll_cfg *pll_cfg; + struct device *dev; +}; + +static inline void axs10x_pll_write(struct axs10x_pll_clk *clk, u32 reg, + u32 val) +{ + iowrite32(val, clk->base + reg); +} + +static inline u32 axs10x_pll_read(struct axs10x_pll_clk *clk, u32 reg) +{ + return ioread32(clk->base + reg); +} + +static inline struct axs10x_pll_clk *to_axs10x_pll_clk(struct clk_hw *hw) +{ + return container_of(hw, struct axs10x_pll_clk, hw); +} + +static inline u32 axs10x_div_get_value(u32 reg) +{ + if (PLL_REG_GET_BYPASS(reg)) + return 1; + + return PLL_REG_GET_HIGH(reg) + PLL_REG_GET_LOW(reg); +} + +static inline u32 axs10x_encode_div(unsigned int id, int upd) +{ + u32 div = 0; + + PLL_REG_SET_LOW(div, (id % 2 == 0) ? id >> 1 : (id >> 1) + 1); + PLL_REG_SET_HIGH(div, id >> 1); + PLL_REG_SET_EDGE(div, id % 2); + PLL_REG_SET_BYPASS(div, id == 1 ? 1 : 0); + PLL_REG_SET_NOUPD(div, upd == 0 ? 1 : 0); + + return div; +} + +static unsigned long axs10x_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u64 rate; + u32 idiv, fbdiv, odiv; + struct axs10x_pll_clk *clk = to_axs10x_pll_clk(hw); + + idiv = axs10x_div_get_value(axs10x_pll_read(clk, PLL_REG_IDIV)); + fbdiv = axs10x_div_get_value(axs10x_pll_read(clk, PLL_REG_FBDIV)); + odiv = axs10x_div_get_value(axs10x_pll_read(clk, PLL_REG_ODIV)); + + rate = (u64)parent_rate * fbdiv; + do_div(rate, idiv * odiv); + + return rate; +} + +static long axs10x_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int i; + long best_rate; + struct axs10x_pll_clk *clk = to_axs10x_pll_clk(hw); + const struct axs10x_pll_cfg *pll_cfg = clk->pll_cfg; + + if (pll_cfg[0].rate == 0) + return -EINVAL; + + best_rate = pll_cfg[0].rate; + + for (i = 1; pll_cfg[i].rate != 0; i++) { + if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate)) + best_rate = pll_cfg[i].rate; + } + + return best_rate; +} + +static int axs10x_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int i; + struct axs10x_pll_clk *clk = to_axs10x_pll_clk(hw); + const struct axs10x_pll_cfg *pll_cfg = clk->pll_cfg; + + for (i = 0; pll_cfg[i].rate != 0; i++) { + if (pll_cfg[i].rate == rate) { + axs10x_pll_write(clk, PLL_REG_IDIV, + axs10x_encode_div(pll_cfg[i].idiv, 0)); + axs10x_pll_write(clk, PLL_REG_FBDIV, + axs10x_encode_div(pll_cfg[i].fbdiv, 0)); + axs10x_pll_write(clk, PLL_REG_ODIV, + axs10x_encode_div(pll_cfg[i].odiv, 1)); + + /* + * Wait until CGU relocks and check error status. + * If after timeout CGU is unlocked yet return error + */ + udelay(PLL_MAX_LOCK_TIME); + if (!(ioread32(clk->lock) & PLL_LOCK)) + return -ETIMEDOUT; + + if (ioread32(clk->lock) & PLL_ERROR) + return -EINVAL; + + return 0; + } + } + + dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate, + parent_rate); + return -EINVAL; +} + +static const struct clk_ops axs10x_pll_ops = { + .recalc_rate = axs10x_pll_recalc_rate, + .round_rate = axs10x_pll_round_rate, + .set_rate = axs10x_pll_set_rate, +}; + +static int axs10x_pll_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const char *parent_name; + struct axs10x_pll_clk *pll_clk; + struct resource *mem; + struct clk_init_data init = { }; + int ret; + + pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL); + if (!pll_clk) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pll_clk->base = devm_ioremap_resource(dev, mem); + if (IS_ERR(pll_clk->base)) + return PTR_ERR(pll_clk->base); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + pll_clk->lock = devm_ioremap_resource(dev, mem); + if (IS_ERR(pll_clk->lock)) + return PTR_ERR(pll_clk->lock); + + init.name = dev->of_node->name; + init.ops = &axs10x_pll_ops; + parent_name = of_clk_get_parent_name(dev->of_node, 0); + init.parent_names = &parent_name; + init.num_parents = 1; + pll_clk->hw.init = &init; + pll_clk->dev = dev; + pll_clk->pll_cfg = of_device_get_match_data(dev); + + if (!pll_clk->pll_cfg) { + dev_err(dev, "No OF match data provided\n"); + return -EINVAL; + } + + ret = devm_clk_hw_register(dev, &pll_clk->hw); + if (ret) { + dev_err(dev, "failed to register %s clock\n", init.name); + return ret; + } + + return of_clk_add_hw_provider(dev->of_node, of_clk_hw_simple_get, + &pll_clk->hw); +} + +static int axs10x_pll_clk_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + return 0; +} + +static void __init of_axs10x_pll_clk_setup(struct device_node *node) +{ + const char *parent_name; + struct axs10x_pll_clk *pll_clk; + struct clk_init_data init = { }; + int ret; + + pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); + if (!pll_clk) + return; + + pll_clk->base = of_iomap(node, 0); + if (!pll_clk->base) { + pr_err("failed to map pll div registers\n"); + goto err_free_pll_clk; + } + + pll_clk->lock = of_iomap(node, 1); + if (!pll_clk->lock) { + pr_err("failed to map pll lock register\n"); + goto err_unmap_base; + } + + init.name = node->name; + init.ops = &axs10x_pll_ops; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = &parent_name; + init.num_parents = parent_name ? 1 : 0; + pll_clk->hw.init = &init; + pll_clk->pll_cfg = arc_pll_cfg; + + ret = clk_hw_register(NULL, &pll_clk->hw); + if (ret) { + pr_err("failed to register %s clock\n", node->name); + goto err_unmap_lock; + } + + ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &pll_clk->hw); + if (ret) { + pr_err("failed to add hw provider for %s clock\n", node->name); + goto err_unregister_clk; + } + + return; + +err_unregister_clk: + clk_hw_unregister(&pll_clk->hw); +err_unmap_lock: + iounmap(pll_clk->lock); +err_unmap_base: + iounmap(pll_clk->base); +err_free_pll_clk: + kfree(pll_clk); +} +CLK_OF_DECLARE(axs10x_pll_clock, "snps,axs10x-arc-pll-clock", + of_axs10x_pll_clk_setup); + +static const struct of_device_id axs10x_pll_clk_id[] = { + { .compatible = "snps,axs10x-pgu-pll-clock", .data = &pgu_pll_cfg}, + { } +}; +MODULE_DEVICE_TABLE(of, axs10x_pll_clk_id); + +static struct platform_driver axs10x_pll_clk_driver = { + .driver = { + .name = "axs10x-pll-clock", + .of_match_table = axs10x_pll_clk_id, + }, + .probe = axs10x_pll_clk_probe, + .remove = axs10x_pll_clk_remove, +}; +builtin_platform_driver(axs10x_pll_clk_driver); + +MODULE_AUTHOR("Vlad Zakharov <vzakhar@synopsys.com>"); +MODULE_DESCRIPTION("Synopsys AXS10X SDP Generic PLL Clock Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/berlin/bg2.c b/drivers/clk/berlin/bg2.c index 1d99292e2039..e7331ace0337 100644 --- a/drivers/clk/berlin/bg2.c +++ b/drivers/clk/berlin/bg2.c @@ -679,8 +679,7 @@ static void __init berlin2_clock_setup(struct device_node *np) if (!IS_ERR(hws[n])) continue; - pr_err("%s: Unable to register leaf clock %d\n", - np->full_name, n); + pr_err("%pOF: Unable to register leaf clock %d\n", np, n); goto bg2_fail; } diff --git a/drivers/clk/berlin/bg2q.c b/drivers/clk/berlin/bg2q.c index 3b784b593afd..67c270b143f7 100644 --- a/drivers/clk/berlin/bg2q.c +++ b/drivers/clk/berlin/bg2q.c @@ -304,14 +304,14 @@ static void __init berlin2q_clock_setup(struct device_node *np) gbase = of_iomap(parent_np, 0); if (!gbase) { - pr_err("%s: Unable to map global base\n", np->full_name); + pr_err("%pOF: Unable to map global base\n", np); return; } /* BG2Q CPU PLL is not part of global registers */ cpupll_base = of_iomap(parent_np, 1); if (!cpupll_base) { - pr_err("%s: Unable to map cpupll base\n", np->full_name); + pr_err("%pOF: Unable to map cpupll base\n", np); iounmap(gbase); return; } @@ -376,8 +376,7 @@ static void __init berlin2q_clock_setup(struct device_node *np) if (!IS_ERR(hws[n])) continue; - pr_err("%s: Unable to register leaf clock %d\n", - np->full_name, n); + pr_err("%pOF: Unable to register leaf clock %d\n", np, n); goto bg2q_fail; } diff --git a/drivers/clk/clk-asm9260.c b/drivers/clk/clk-asm9260.c index ea8568536193..bf0582cbbf38 100644 --- a/drivers/clk/clk-asm9260.c +++ b/drivers/clk/clk-asm9260.c @@ -338,8 +338,8 @@ static void __init asm9260_acc_init(struct device_node *np) if (!IS_ERR(hws[n])) continue; - pr_err("%s: Unable to register leaf clock %d\n", - np->full_name, n); + pr_err("%pOF: Unable to register leaf clock %d\n", + np, n); goto fail; } diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c index 7ec36722f8ab..49819b546134 100644 --- a/drivers/clk/clk-conf.c +++ b/drivers/clk/clk-conf.c @@ -23,8 +23,8 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier) num_parents = of_count_phandle_with_args(node, "assigned-clock-parents", "#clock-cells"); if (num_parents == -EINVAL) - pr_err("clk: invalid value of clock-parents property at %s\n", - node->full_name); + pr_err("clk: invalid value of clock-parents property at %pOF\n", + node); for (index = 0; index < num_parents; index++) { rc = of_parse_phandle_with_args(node, "assigned-clock-parents", @@ -41,8 +41,8 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier) pclk = of_clk_get_from_provider(&clkspec); if (IS_ERR(pclk)) { if (PTR_ERR(pclk) != -EPROBE_DEFER) - pr_warn("clk: couldn't get parent clock %d for %s\n", - index, node->full_name); + pr_warn("clk: couldn't get parent clock %d for %pOF\n", + index, node); return PTR_ERR(pclk); } @@ -57,8 +57,8 @@ static int __set_clk_parents(struct device_node *node, bool clk_supplier) clk = of_clk_get_from_provider(&clkspec); if (IS_ERR(clk)) { if (PTR_ERR(clk) != -EPROBE_DEFER) - pr_warn("clk: couldn't get assigned clock %d for %s\n", - index, node->full_name); + pr_warn("clk: couldn't get assigned clock %d for %pOF\n", + index, node); rc = PTR_ERR(clk); goto err; } @@ -102,8 +102,8 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier) clk = of_clk_get_from_provider(&clkspec); if (IS_ERR(clk)) { if (PTR_ERR(clk) != -EPROBE_DEFER) - pr_warn("clk: couldn't get clock %d for %s\n", - index, node->full_name); + pr_warn("clk: couldn't get clock %d for %pOF\n", + index, node); return PTR_ERR(clk); } diff --git a/drivers/clk/clk-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c index c54baede4d68..e8ea81c30f0c 100644 --- a/drivers/clk/clk-cs2000-cp.c +++ b/drivers/clk/clk-cs2000-cp.c @@ -343,6 +343,15 @@ static int cs2000_set_rate(struct clk_hw *hw, return __cs2000_set_rate(priv, ch, rate, parent_rate); } +static int cs2000_set_saved_rate(struct cs2000_priv *priv) +{ + int ch = 0; /* it uses ch0 only at this point */ + + return __cs2000_set_rate(priv, ch, + priv->saved_rate, + priv->saved_parent_rate); +} + static int cs2000_enable(struct clk_hw *hw) { struct cs2000_priv *priv = hw_to_priv(hw); @@ -535,11 +544,8 @@ probe_err: static int cs2000_resume(struct device *dev) { struct cs2000_priv *priv = dev_get_drvdata(dev); - int ch = 0; /* it uses ch0 only at this point */ - return __cs2000_set_rate(priv, ch, - priv->saved_rate, - priv->saved_parent_rate); + return cs2000_set_saved_rate(priv); } static const struct dev_pm_ops cs2000_pm_ops = { diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 9bb472cccca6..4ed516cb7276 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -385,12 +385,14 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_divider *divider = to_clk_divider(hw); - unsigned int value; + int value; unsigned long flags = 0; u32 val; value = divider_get_val(rate, parent_rate, divider->table, divider->width, divider->flags); + if (value < 0) + return value; if (divider->lock) spin_lock_irqsave(divider->lock, flags); @@ -403,7 +405,7 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, val = clk_readl(divider->reg); val &= ~(div_mask(divider->width) << divider->shift); } - val |= value << divider->shift; + val |= (u32)value << divider->shift; clk_writel(val, divider->reg); if (divider->lock) diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index aab904618eb6..fdf625fb10fa 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -49,16 +49,12 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw, return ret; } -static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static void clk_fd_general_approximation(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate, + unsigned long *m, unsigned long *n) { struct clk_fractional_divider *fd = to_clk_fd(hw); unsigned long scale; - unsigned long m, n; - u64 ret; - - if (!rate || rate >= *parent_rate) - return *parent_rate; /* * Get rate closer to *parent_rate to guarantee there is no overflow @@ -71,7 +67,23 @@ static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, rational_best_approximation(rate, *parent_rate, GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0), - &m, &n); + m, n); +} + +static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_fractional_divider *fd = to_clk_fd(hw); + unsigned long m, n; + u64 ret; + + if (!rate || rate >= *parent_rate) + return *parent_rate; + + if (fd->approximation) + fd->approximation(hw, rate, parent_rate, &m, &n); + else + clk_fd_general_approximation(hw, rate, parent_rate, &m, &n); ret = (u64)*parent_rate * m; do_div(ret, n); diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 4e0c054a787c..dd82485e09a1 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -86,7 +86,7 @@ static void clk_gate_disable(struct clk_hw *hw) clk_gate_endisable(hw, 0); } -static int clk_gate_is_enabled(struct clk_hw *hw) +int clk_gate_is_enabled(struct clk_hw *hw) { u32 reg; struct clk_gate *gate = to_clk_gate(hw); @@ -101,6 +101,7 @@ static int clk_gate_is_enabled(struct clk_hw *hw) return reg ? 1 : 0; } +EXPORT_SYMBOL_GPL(clk_gate_is_enabled); const struct clk_ops clk_gate_ops = { .enable = clk_gate_enable, diff --git a/drivers/clk/clk-gemini.c b/drivers/clk/clk-gemini.c index b4cf2f699a21..f940e5af845b 100644 --- a/drivers/clk/clk-gemini.c +++ b/drivers/clk/clk-gemini.c @@ -37,7 +37,6 @@ static DEFINE_SPINLOCK(gemini_clk_lock); #define GEMINI_GLOBAL_MISC_CONTROL 0x30 #define PCI_CLK_66MHZ BIT(18) -#define PCI_CLK_OE BIT(17) #define GEMINI_GLOBAL_CLOCK_CONTROL 0x34 #define PCI_CLKRUN_EN BIT(16) @@ -159,9 +158,6 @@ static int gemini_pci_enable(struct clk_hw *hw) regmap_update_bits(pciclk->map, GEMINI_GLOBAL_CLOCK_CONTROL, 0, PCI_CLKRUN_EN); - regmap_update_bits(pciclk->map, - GEMINI_GLOBAL_MISC_CONTROL, - 0, PCI_CLK_OE); return 0; } @@ -169,9 +165,6 @@ static void gemini_pci_disable(struct clk_hw *hw) { struct clk_gemini_pci *pciclk = to_pciclk(hw); - regmap_update_bits(pciclk->map, - GEMINI_GLOBAL_MISC_CONTROL, - PCI_CLK_OE, 0); regmap_update_bits(pciclk->map, GEMINI_GLOBAL_CLOCK_CONTROL, PCI_CLKRUN_EN, 0); } diff --git a/drivers/clk/clk-hsdk-pll.c b/drivers/clk/clk-hsdk-pll.c new file mode 100644 index 000000000000..bbf237173b37 --- /dev/null +++ b/drivers/clk/clk-hsdk-pll.c @@ -0,0 +1,431 @@ +/* + * Synopsys HSDK SDP Generic PLL clock driver + * + * Copyright (C) 2017 Synopsys + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define CGU_PLL_CTRL 0x000 /* ARC PLL control register */ +#define CGU_PLL_STATUS 0x004 /* ARC PLL status register */ +#define CGU_PLL_FMEAS 0x008 /* ARC PLL frequency measurement register */ +#define CGU_PLL_MON 0x00C /* ARC PLL monitor register */ + +#define CGU_PLL_CTRL_ODIV_SHIFT 2 +#define CGU_PLL_CTRL_IDIV_SHIFT 4 +#define CGU_PLL_CTRL_FBDIV_SHIFT 9 +#define CGU_PLL_CTRL_BAND_SHIFT 20 + +#define CGU_PLL_CTRL_ODIV_MASK GENMASK(3, CGU_PLL_CTRL_ODIV_SHIFT) +#define CGU_PLL_CTRL_IDIV_MASK GENMASK(8, CGU_PLL_CTRL_IDIV_SHIFT) +#define CGU_PLL_CTRL_FBDIV_MASK GENMASK(15, CGU_PLL_CTRL_FBDIV_SHIFT) + +#define CGU_PLL_CTRL_PD BIT(0) +#define CGU_PLL_CTRL_BYPASS BIT(1) + +#define CGU_PLL_STATUS_LOCK BIT(0) +#define CGU_PLL_STATUS_ERR BIT(1) + +#define HSDK_PLL_MAX_LOCK_TIME 100 /* 100 us */ + +#define CGU_PLL_SOURCE_MAX 1 + +#define CORE_IF_CLK_THRESHOLD_HZ 500000000 +#define CREG_CORE_IF_CLK_DIV_1 0x0 +#define CREG_CORE_IF_CLK_DIV_2 0x1 + +struct hsdk_pll_cfg { + u32 rate; + u32 idiv; + u32 fbdiv; + u32 odiv; + u32 band; +}; + +static const struct hsdk_pll_cfg asdt_pll_cfg[] = { + { 100000000, 0, 11, 3, 0 }, + { 133000000, 0, 15, 3, 0 }, + { 200000000, 1, 47, 3, 0 }, + { 233000000, 1, 27, 2, 0 }, + { 300000000, 1, 35, 2, 0 }, + { 333000000, 1, 39, 2, 0 }, + { 400000000, 1, 47, 2, 0 }, + { 500000000, 0, 14, 1, 0 }, + { 600000000, 0, 17, 1, 0 }, + { 700000000, 0, 20, 1, 0 }, + { 800000000, 0, 23, 1, 0 }, + { 900000000, 1, 26, 0, 0 }, + { 1000000000, 1, 29, 0, 0 }, + { 1100000000, 1, 32, 0, 0 }, + { 1200000000, 1, 35, 0, 0 }, + { 1300000000, 1, 38, 0, 0 }, + { 1400000000, 1, 41, 0, 0 }, + { 1500000000, 1, 44, 0, 0 }, + { 1600000000, 1, 47, 0, 0 }, + {} +}; + +static const struct hsdk_pll_cfg hdmi_pll_cfg[] = { + { 297000000, 0, 21, 2, 0 }, + { 540000000, 0, 19, 1, 0 }, + { 594000000, 0, 21, 1, 0 }, + {} +}; + +struct hsdk_pll_clk { + struct clk_hw hw; + void __iomem *regs; + void __iomem *spec_regs; + const struct hsdk_pll_devdata *pll_devdata; + struct device *dev; +}; + +struct hsdk_pll_devdata { + const struct hsdk_pll_cfg *pll_cfg; + int (*update_rate)(struct hsdk_pll_clk *clk, unsigned long rate, + const struct hsdk_pll_cfg *cfg); +}; + +static int hsdk_pll_core_update_rate(struct hsdk_pll_clk *, unsigned long, + const struct hsdk_pll_cfg *); +static int hsdk_pll_comm_update_rate(struct hsdk_pll_clk *, unsigned long, + const struct hsdk_pll_cfg *); + +static const struct hsdk_pll_devdata core_pll_devdata = { + .pll_cfg = asdt_pll_cfg, + .update_rate = hsdk_pll_core_update_rate, +}; + +static const struct hsdk_pll_devdata sdt_pll_devdata = { + .pll_cfg = asdt_pll_cfg, + .update_rate = hsdk_pll_comm_update_rate, +}; + +static const struct hsdk_pll_devdata hdmi_pll_devdata = { + .pll_cfg = hdmi_pll_cfg, + .update_rate = hsdk_pll_comm_update_rate, +}; + +static inline void hsdk_pll_write(struct hsdk_pll_clk *clk, u32 reg, u32 val) +{ + iowrite32(val, clk->regs + reg); +} + +static inline u32 hsdk_pll_read(struct hsdk_pll_clk *clk, u32 reg) +{ + return ioread32(clk->regs + reg); +} + +static inline void hsdk_pll_set_cfg(struct hsdk_pll_clk *clk, + const struct hsdk_pll_cfg *cfg) +{ + u32 val = 0; + + /* Powerdown and Bypass bits should be cleared */ + val |= cfg->idiv << CGU_PLL_CTRL_IDIV_SHIFT; + val |= cfg->fbdiv << CGU_PLL_CTRL_FBDIV_SHIFT; + val |= cfg->odiv << CGU_PLL_CTRL_ODIV_SHIFT; + val |= cfg->band << CGU_PLL_CTRL_BAND_SHIFT; + + dev_dbg(clk->dev, "write configurarion: %#x\n", val); + + hsdk_pll_write(clk, CGU_PLL_CTRL, val); +} + +static inline bool hsdk_pll_is_locked(struct hsdk_pll_clk *clk) +{ + return !!(hsdk_pll_read(clk, CGU_PLL_STATUS) & CGU_PLL_STATUS_LOCK); +} + +static inline bool hsdk_pll_is_err(struct hsdk_pll_clk *clk) +{ + return !!(hsdk_pll_read(clk, CGU_PLL_STATUS) & CGU_PLL_STATUS_ERR); +} + +static inline struct hsdk_pll_clk *to_hsdk_pll_clk(struct clk_hw *hw) +{ + return container_of(hw, struct hsdk_pll_clk, hw); +} + +static unsigned long hsdk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 val; + u64 rate; + u32 idiv, fbdiv, odiv; + struct hsdk_pll_clk *clk = to_hsdk_pll_clk(hw); + + val = hsdk_pll_read(clk, CGU_PLL_CTRL); + + dev_dbg(clk->dev, "current configurarion: %#x\n", val); + + /* Check if PLL is disabled */ + if (val & CGU_PLL_CTRL_PD) + return 0; + + /* Check if PLL is bypassed */ + if (val & CGU_PLL_CTRL_BYPASS) + return parent_rate; + + /* input divider = reg.idiv + 1 */ + idiv = 1 + ((val & CGU_PLL_CTRL_IDIV_MASK) >> CGU_PLL_CTRL_IDIV_SHIFT); + /* fb divider = 2*(reg.fbdiv + 1) */ + fbdiv = 2 * (1 + ((val & CGU_PLL_CTRL_FBDIV_MASK) >> CGU_PLL_CTRL_FBDIV_SHIFT)); + /* output divider = 2^(reg.odiv) */ + odiv = 1 << ((val & CGU_PLL_CTRL_ODIV_MASK) >> CGU_PLL_CTRL_ODIV_SHIFT); + + rate = (u64)parent_rate * fbdiv; + do_div(rate, idiv * odiv); + + return rate; +} + +static long hsdk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int i; + unsigned long best_rate; + struct hsdk_pll_clk *clk = to_hsdk_pll_clk(hw); + const struct hsdk_pll_cfg *pll_cfg = clk->pll_devdata->pll_cfg; + + if (pll_cfg[0].rate == 0) + return -EINVAL; + + best_rate = pll_cfg[0].rate; + + for (i = 1; pll_cfg[i].rate != 0; i++) { + if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate)) + best_rate = pll_cfg[i].rate; + } + + dev_dbg(clk->dev, "chosen best rate: %lu\n", best_rate); + + return best_rate; +} + +static int hsdk_pll_comm_update_rate(struct hsdk_pll_clk *clk, + unsigned long rate, + const struct hsdk_pll_cfg *cfg) +{ + hsdk_pll_set_cfg(clk, cfg); + + /* + * Wait until CGU relocks and check error status. + * If after timeout CGU is unlocked yet return error. + */ + udelay(HSDK_PLL_MAX_LOCK_TIME); + if (!hsdk_pll_is_locked(clk)) + return -ETIMEDOUT; + + if (hsdk_pll_is_err(clk)) + return -EINVAL; + + return 0; +} + +static int hsdk_pll_core_update_rate(struct hsdk_pll_clk *clk, + unsigned long rate, + const struct hsdk_pll_cfg *cfg) +{ + /* + * When core clock exceeds 500MHz, the divider for the interface + * clock must be programmed to div-by-2. + */ + if (rate > CORE_IF_CLK_THRESHOLD_HZ) + iowrite32(CREG_CORE_IF_CLK_DIV_2, clk->spec_regs); + + hsdk_pll_set_cfg(clk, cfg); + + /* + * Wait until CGU relocks and check error status. + * If after timeout CGU is unlocked yet return error. + */ + udelay(HSDK_PLL_MAX_LOCK_TIME); + if (!hsdk_pll_is_locked(clk)) + return -ETIMEDOUT; + + if (hsdk_pll_is_err(clk)) + return -EINVAL; + + /* + * Program divider to div-by-1 if we succesfuly set core clock below + * 500MHz threshold. + */ + if (rate <= CORE_IF_CLK_THRESHOLD_HZ) + iowrite32(CREG_CORE_IF_CLK_DIV_1, clk->spec_regs); + + return 0; +} + +static int hsdk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int i; + struct hsdk_pll_clk *clk = to_hsdk_pll_clk(hw); + const struct hsdk_pll_cfg *pll_cfg = clk->pll_devdata->pll_cfg; + + for (i = 0; pll_cfg[i].rate != 0; i++) { + if (pll_cfg[i].rate == rate) { + return clk->pll_devdata->update_rate(clk, rate, + &pll_cfg[i]); + } + } + + dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate, + parent_rate); + + return -EINVAL; +} + +static const struct clk_ops hsdk_pll_ops = { + .recalc_rate = hsdk_pll_recalc_rate, + .round_rate = hsdk_pll_round_rate, + .set_rate = hsdk_pll_set_rate, +}; + +static int hsdk_pll_clk_probe(struct platform_device *pdev) +{ + int ret; + struct resource *mem; + const char *parent_name; + unsigned int num_parents; + struct hsdk_pll_clk *pll_clk; + struct clk_init_data init = { }; + struct device *dev = &pdev->dev; + + pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL); + if (!pll_clk) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pll_clk->regs = devm_ioremap_resource(dev, mem); + if (IS_ERR(pll_clk->regs)) + return PTR_ERR(pll_clk->regs); + + init.name = dev->of_node->name; + init.ops = &hsdk_pll_ops; + parent_name = of_clk_get_parent_name(dev->of_node, 0); + init.parent_names = &parent_name; + num_parents = of_clk_get_parent_count(dev->of_node); + if (num_parents == 0 || num_parents > CGU_PLL_SOURCE_MAX) { + dev_err(dev, "wrong clock parents number: %u\n", num_parents); + return -EINVAL; + } + init.num_parents = num_parents; + + pll_clk->hw.init = &init; + pll_clk->dev = dev; + pll_clk->pll_devdata = of_device_get_match_data(dev); + + if (!pll_clk->pll_devdata) { + dev_err(dev, "No OF match data provided\n"); + return -EINVAL; + } + + ret = devm_clk_hw_register(dev, &pll_clk->hw); + if (ret) { + dev_err(dev, "failed to register %s clock\n", init.name); + return ret; + } + + return of_clk_add_hw_provider(dev->of_node, of_clk_hw_simple_get, + &pll_clk->hw); +} + +static int hsdk_pll_clk_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + return 0; +} + +static void __init of_hsdk_pll_clk_setup(struct device_node *node) +{ + int ret; + const char *parent_name; + unsigned int num_parents; + struct hsdk_pll_clk *pll_clk; + struct clk_init_data init = { }; + + pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); + if (!pll_clk) + return; + + pll_clk->regs = of_iomap(node, 0); + if (!pll_clk->regs) { + pr_err("failed to map pll registers\n"); + goto err_free_pll_clk; + } + + pll_clk->spec_regs = of_iomap(node, 1); + if (!pll_clk->spec_regs) { + pr_err("failed to map pll registers\n"); + goto err_unmap_comm_regs; + } + + init.name = node->name; + init.ops = &hsdk_pll_ops; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = &parent_name; + num_parents = of_clk_get_parent_count(node); + if (num_parents > CGU_PLL_SOURCE_MAX) { + pr_err("too much clock parents: %u\n", num_parents); + goto err_unmap_spec_regs; + } + init.num_parents = num_parents; + + pll_clk->hw.init = &init; + pll_clk->pll_devdata = &core_pll_devdata; + + ret = clk_hw_register(NULL, &pll_clk->hw); + if (ret) { + pr_err("failed to register %s clock\n", node->name); + goto err_unmap_spec_regs; + } + + ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &pll_clk->hw); + if (ret) { + pr_err("failed to add hw provider for %s clock\n", node->name); + goto err_unmap_spec_regs; + } + + return; + +err_unmap_spec_regs: + iounmap(pll_clk->spec_regs); +err_unmap_comm_regs: + iounmap(pll_clk->regs); +err_free_pll_clk: + kfree(pll_clk); +} + +/* Core PLL needed early for ARC cpus timers */ +CLK_OF_DECLARE(hsdk_pll_clock, "snps,hsdk-core-pll-clock", +of_hsdk_pll_clk_setup); + +static const struct of_device_id hsdk_pll_clk_id[] = { + { .compatible = "snps,hsdk-gp-pll-clock", .data = &sdt_pll_devdata}, + { .compatible = "snps,hsdk-hdmi-pll-clock", .data = &hdmi_pll_devdata}, + { } +}; + +static struct platform_driver hsdk_pll_clk_driver = { + .driver = { + .name = "hsdk-gp-pll-clock", + .of_match_table = hsdk_pll_clk_id, + }, + .probe = hsdk_pll_clk_probe, + .remove = hsdk_pll_clk_remove, +}; +builtin_platform_driver(hsdk_pll_clk_driver); diff --git a/drivers/clk/clk-mb86s7x.c b/drivers/clk/clk-mb86s7x.c deleted file mode 100644 index 2a83a3ff1d09..000000000000 --- a/drivers/clk/clk-mb86s7x.c +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Copyright (C) 2013-2015 FUJITSU SEMICONDUCTOR LIMITED - * Copyright (C) 2015 Linaro Ltd. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include <linux/clkdev.h> -#include <linux/err.h> -#include <linux/io.h> -#include <linux/of.h> -#include <linux/cpu.h> -#include <linux/clk-provider.h> -#include <linux/spinlock.h> -#include <linux/module.h> -#include <linux/topology.h> -#include <linux/mailbox_client.h> -#include <linux/platform_device.h> - -#include <soc/mb86s7x/scb_mhu.h> - -#define to_crg_clk(p) container_of(p, struct crg_clk, hw) -#define to_clc_clk(p) container_of(p, struct cl_clk, hw) - -struct mb86s7x_peri_clk { - u32 payload_size; - u32 cntrlr; - u32 domain; - u32 port; - u32 en; - u64 frequency; -} __packed __aligned(4); - -struct hack_rate { - unsigned clk_id; - unsigned long rate; - int gated; -}; - -struct crg_clk { - struct clk_hw hw; - u8 cntrlr, domain, port; -}; - -static int crg_gate_control(struct clk_hw *hw, int en) -{ - struct crg_clk *crgclk = to_crg_clk(hw); - struct mb86s7x_peri_clk cmd; - int ret; - - cmd.payload_size = sizeof(cmd); - cmd.cntrlr = crgclk->cntrlr; - cmd.domain = crgclk->domain; - cmd.port = crgclk->port; - cmd.en = en; - - /* Port is UngatedCLK */ - if (cmd.port == 8) - return en ? 0 : -EINVAL; - - pr_debug("%s:%d CMD Cntrlr-%u Dom-%u Port-%u En-%u}\n", - __func__, __LINE__, cmd.cntrlr, - cmd.domain, cmd.port, cmd.en); - - ret = mb86s7x_send_packet(CMD_PERI_CLOCK_GATE_SET_REQ, - &cmd, sizeof(cmd)); - if (ret < 0) { - pr_err("%s:%d failed!\n", __func__, __LINE__); - return ret; - } - - pr_debug("%s:%d REP Cntrlr-%u Dom-%u Port-%u En-%u}\n", - __func__, __LINE__, cmd.cntrlr, - cmd.domain, cmd.port, cmd.en); - - /* If the request was rejected */ - if (cmd.en != en) - ret = -EINVAL; - else - ret = 0; - - return ret; -} - -static int crg_port_prepare(struct clk_hw *hw) -{ - return crg_gate_control(hw, 1); -} - -static void crg_port_unprepare(struct clk_hw *hw) -{ - crg_gate_control(hw, 0); -} - -static int -crg_rate_control(struct clk_hw *hw, int set, unsigned long *rate) -{ - struct crg_clk *crgclk = to_crg_clk(hw); - struct mb86s7x_peri_clk cmd; - int code, ret; - - cmd.payload_size = sizeof(cmd); - cmd.cntrlr = crgclk->cntrlr; - cmd.domain = crgclk->domain; - cmd.port = crgclk->port; - cmd.frequency = *rate; - - if (set) { - code = CMD_PERI_CLOCK_RATE_SET_REQ; - pr_debug("%s:%d CMD Cntrlr-%u Dom-%u Port-%u Rate-SET %lluHz}\n", - __func__, __LINE__, cmd.cntrlr, - cmd.domain, cmd.port, cmd.frequency); - } else { - code = CMD_PERI_CLOCK_RATE_GET_REQ; - pr_debug("%s:%d CMD Cntrlr-%u Dom-%u Port-%u Rate-GET}\n", - __func__, __LINE__, cmd.cntrlr, - cmd.domain, cmd.port); - } - - ret = mb86s7x_send_packet(code, &cmd, sizeof(cmd)); - if (ret < 0) { - pr_err("%s:%d failed!\n", __func__, __LINE__); - return ret; - } - - if (set) - pr_debug("%s:%d REP Cntrlr-%u Dom-%u Port-%u Rate-SET %lluHz}\n", - __func__, __LINE__, cmd.cntrlr, - cmd.domain, cmd.port, cmd.frequency); - else - pr_debug("%s:%d REP Cntrlr-%u Dom-%u Port-%u Rate-GOT %lluHz}\n", - __func__, __LINE__, cmd.cntrlr, - cmd.domain, cmd.port, cmd.frequency); - - *rate = cmd.frequency; - return 0; -} - -static unsigned long -crg_port_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) -{ - unsigned long rate; - - crg_rate_control(hw, 0, &rate); - - return rate; -} - -static long -crg_port_round_rate(struct clk_hw *hw, - unsigned long rate, unsigned long *pr) -{ - return rate; -} - -static int -crg_port_set_rate(struct clk_hw *hw, - unsigned long rate, unsigned long parent_rate) -{ - return crg_rate_control(hw, 1, &rate); -} - -const struct clk_ops crg_port_ops = { - .prepare = crg_port_prepare, - .unprepare = crg_port_unprepare, - .recalc_rate = crg_port_recalc_rate, - .round_rate = crg_port_round_rate, - .set_rate = crg_port_set_rate, -}; - -struct mb86s70_crg11 { - struct mutex lock; /* protects CLK populating and searching */ -}; - -static struct clk *crg11_get(struct of_phandle_args *clkspec, void *data) -{ - struct mb86s70_crg11 *crg11 = data; - struct clk_init_data init; - u32 cntrlr, domain, port; - struct crg_clk *crgclk; - struct clk *clk; - char clkp[20]; - - if (clkspec->args_count != 3) - return ERR_PTR(-EINVAL); - - cntrlr = clkspec->args[0]; - domain = clkspec->args[1]; - port = clkspec->args[2]; - - if (port > 7) - snprintf(clkp, 20, "UngatedCLK%d_%X", cntrlr, domain); - else - snprintf(clkp, 20, "CLK%d_%X_%d", cntrlr, domain, port); - - mutex_lock(&crg11->lock); - - clk = __clk_lookup(clkp); - if (clk) { - mutex_unlock(&crg11->lock); - return clk; - } - - crgclk = kzalloc(sizeof(*crgclk), GFP_KERNEL); - if (!crgclk) { - mutex_unlock(&crg11->lock); - return ERR_PTR(-ENOMEM); - } - - init.name = clkp; - init.num_parents = 0; - init.ops = &crg_port_ops; - init.flags = 0; - crgclk->hw.init = &init; - crgclk->cntrlr = cntrlr; - crgclk->domain = domain; - crgclk->port = port; - clk = clk_register(NULL, &crgclk->hw); - if (IS_ERR(clk)) - pr_err("%s:%d Error!\n", __func__, __LINE__); - else - pr_debug("Registered %s\n", clkp); - - clk_register_clkdev(clk, clkp, NULL); - mutex_unlock(&crg11->lock); - return clk; -} - -static void __init crg_port_init(struct device_node *node) -{ - struct mb86s70_crg11 *crg11; - - crg11 = kzalloc(sizeof(*crg11), GFP_KERNEL); - if (!crg11) - return; - - mutex_init(&crg11->lock); - - of_clk_add_provider(node, crg11_get, crg11); -} -CLK_OF_DECLARE(crg11_gate, "fujitsu,mb86s70-crg11", crg_port_init); - -struct cl_clk { - struct clk_hw hw; - int cluster; -}; - -struct mb86s7x_cpu_freq { - u32 payload_size; - u32 cluster_class; - u32 cluster_id; - u32 cpu_id; - u64 frequency; -}; - -static void mhu_cluster_rate(struct clk_hw *hw, unsigned long *rate, int get) -{ - struct cl_clk *clc = to_clc_clk(hw); - struct mb86s7x_cpu_freq cmd; - int code, ret; - - cmd.payload_size = sizeof(cmd); - cmd.cluster_class = 0; - cmd.cluster_id = clc->cluster; - cmd.cpu_id = 0; - cmd.frequency = *rate; - - if (get) - code = CMD_CPU_CLOCK_RATE_GET_REQ; - else - code = CMD_CPU_CLOCK_RATE_SET_REQ; - - pr_debug("%s:%d CMD Cl_Class-%u CL_ID-%u CPU_ID-%u Freq-%llu}\n", - __func__, __LINE__, cmd.cluster_class, - cmd.cluster_id, cmd.cpu_id, cmd.frequency); - - ret = mb86s7x_send_packet(code, &cmd, sizeof(cmd)); - if (ret < 0) { - pr_err("%s:%d failed!\n", __func__, __LINE__); - return; - } - - pr_debug("%s:%d REP Cl_Class-%u CL_ID-%u CPU_ID-%u Freq-%llu}\n", - __func__, __LINE__, cmd.cluster_class, - cmd.cluster_id, cmd.cpu_id, cmd.frequency); - - *rate = cmd.frequency; -} - -static unsigned long -clc_recalc_rate(struct clk_hw *hw, unsigned long unused) -{ - unsigned long rate; - - mhu_cluster_rate(hw, &rate, 1); - return rate; -} - -static long -clc_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *unused) -{ - return rate; -} - -static int -clc_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long unused) -{ - unsigned long res = rate; - - mhu_cluster_rate(hw, &res, 0); - - return (res == rate) ? 0 : -EINVAL; -} - -static struct clk_ops clk_clc_ops = { - .recalc_rate = clc_recalc_rate, - .round_rate = clc_round_rate, - .set_rate = clc_set_rate, -}; - -static struct clk_hw *mb86s7x_clclk_register(struct device *cpu_dev) -{ - struct clk_init_data init; - struct cl_clk *clc; - int ret; - - clc = kzalloc(sizeof(*clc), GFP_KERNEL); - if (!clc) - return ERR_PTR(-ENOMEM); - - clc->hw.init = &init; - clc->cluster = topology_physical_package_id(cpu_dev->id); - - init.name = dev_name(cpu_dev); - init.ops = &clk_clc_ops; - init.flags = CLK_GET_RATE_NOCACHE; - init.num_parents = 0; - - ret = devm_clk_hw_register(cpu_dev, &clc->hw); - if (ret) - return ERR_PTR(ret); - return &clc->hw; -} - -static int mb86s7x_clclk_of_init(void) -{ - int cpu, ret = -ENODEV; - struct device_node *np; - struct clk_hw *hw; - - np = of_find_compatible_node(NULL, NULL, "fujitsu,mb86s70-scb-1.0"); - if (!np || !of_device_is_available(np)) - goto exit; - - for_each_possible_cpu(cpu) { - struct device *cpu_dev = get_cpu_device(cpu); - - if (!cpu_dev) { - pr_err("failed to get cpu%d device\n", cpu); - continue; - } - - hw = mb86s7x_clclk_register(cpu_dev); - if (IS_ERR(hw)) { - pr_err("failed to register cpu%d clock\n", cpu); - continue; - } - if (clk_hw_register_clkdev(hw, NULL, dev_name(cpu_dev))) { - pr_err("failed to register cpu%d clock lookup\n", cpu); - continue; - } - pr_debug("registered clk for %s\n", dev_name(cpu_dev)); - } - ret = 0; - - platform_device_register_simple("arm-bL-cpufreq-dt", -1, NULL, 0); -exit: - of_node_put(np); - return ret; -} -module_init(mb86s7x_clclk_of_init); diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c index b86dac851116..58428d0043fd 100644 --- a/drivers/clk/clk-moxart.c +++ b/drivers/clk/clk-moxart.c @@ -18,7 +18,7 @@ static void __init moxart_of_pll_clk_init(struct device_node *node) { - static void __iomem *base; + void __iomem *base; struct clk_hw *hw; struct clk *ref_clk; unsigned int mul; @@ -30,7 +30,7 @@ static void __init moxart_of_pll_clk_init(struct device_node *node) base = of_iomap(node, 0); if (!base) { - pr_err("%s: of_iomap failed\n", node->full_name); + pr_err("%pOF: of_iomap failed\n", node); return; } @@ -39,13 +39,13 @@ static void __init moxart_of_pll_clk_init(struct device_node *node) ref_clk = of_clk_get(node, 0); if (IS_ERR(ref_clk)) { - pr_err("%s: of_clk_get failed\n", node->full_name); + pr_err("%pOF: of_clk_get failed\n", node); return; } hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, mul, 1); if (IS_ERR(hw)) { - pr_err("%s: failed to register clock\n", node->full_name); + pr_err("%pOF: failed to register clock\n", node); return; } @@ -57,7 +57,7 @@ CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock", static void __init moxart_of_apb_clk_init(struct device_node *node) { - static void __iomem *base; + void __iomem *base; struct clk_hw *hw; struct clk *pll_clk; unsigned int div, val; @@ -70,7 +70,7 @@ static void __init moxart_of_apb_clk_init(struct device_node *node) base = of_iomap(node, 0); if (!base) { - pr_err("%s: of_iomap failed\n", node->full_name); + pr_err("%pOF: of_iomap failed\n", node); return; } @@ -83,13 +83,13 @@ static void __init moxart_of_apb_clk_init(struct device_node *node) pll_clk = of_clk_get(node, 0); if (IS_ERR(pll_clk)) { - pr_err("%s: of_clk_get failed\n", node->full_name); + pr_err("%pOF: of_clk_get failed\n", node); return; } hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, 1, div); if (IS_ERR(hw)) { - pr_err("%s: failed to register clock\n", node->full_name); + pr_err("%pOF: failed to register clock\n", node); return; } diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c index f3931e38fac0..b0ea753b8709 100644 --- a/drivers/clk/clk-qoriq.c +++ b/drivers/clk/clk-qoriq.c @@ -12,6 +12,7 @@ #include <linux/clk.h> #include <linux/clk-provider.h> +#include <linux/clkdev.h> #include <linux/fsl/guts.h> #include <linux/io.h> #include <linux/kernel.h> @@ -537,6 +538,17 @@ static const struct clockgen_chipinfo chipinfo[] = { .flags = CG_PLL_8BIT, }, { + .compat = "fsl,ls1088a-clockgen", + .cmux_groups = { + &clockgen2_cmux_cga12 + }, + .cmux_to_group = { + 0, 0, -1 + }, + .pll_mask = 0x07, + .flags = CG_VER3 | CG_LITTLE_ENDIAN, + }, + { .compat = "fsl,ls1012a-clockgen", .cmux_groups = { &ls1012a_cmux @@ -1113,6 +1125,7 @@ static void __init create_one_pll(struct clockgen *cg, int idx) for (i = 0; i < ARRAY_SIZE(pll->div); i++) { struct clk *clk; + int ret; snprintf(pll->div[i].name, sizeof(pll->div[i].name), "cg-pll%d-div%d", idx, i + 1); @@ -1126,6 +1139,11 @@ static void __init create_one_pll(struct clockgen *cg, int idx) } pll->div[i].clk = clk; + ret = clk_register_clkdev(clk, pll->div[i].name, NULL); + if (ret != 0) + pr_err("%s: %s: register to lookup table failed %ld\n", + __func__, pll->div[i].name, PTR_ERR(clk)); + } } @@ -1348,8 +1366,7 @@ static void __init clockgen_init(struct device_node *np) } if (i == ARRAY_SIZE(chipinfo)) { - pr_err("%s: unknown clockgen node %s\n", __func__, - np->full_name); + pr_err("%s: unknown clockgen node %pOF\n", __func__, np); goto err; } clockgen.info = chipinfo[i]; @@ -1362,8 +1379,8 @@ static void __init clockgen_init(struct device_node *np) if (guts) { clockgen.guts = of_iomap(guts, 0); if (!clockgen.guts) { - pr_err("%s: Couldn't map %s regs\n", __func__, - guts->full_name); + pr_err("%s: Couldn't map %pOF regs\n", __func__, + guts); } } @@ -1398,6 +1415,7 @@ CLK_OF_DECLARE(qoriq_clockgen_ls1012a, "fsl,ls1012a-clockgen", clockgen_init); CLK_OF_DECLARE(qoriq_clockgen_ls1021a, "fsl,ls1021a-clockgen", clockgen_init); CLK_OF_DECLARE(qoriq_clockgen_ls1043a, "fsl,ls1043a-clockgen", clockgen_init); CLK_OF_DECLARE(qoriq_clockgen_ls1046a, "fsl,ls1046a-clockgen", clockgen_init); +CLK_OF_DECLARE(qoriq_clockgen_ls1088a, "fsl,ls1088a-clockgen", clockgen_init); CLK_OF_DECLARE(qoriq_clockgen_ls2080a, "fsl,ls2080a-clockgen", clockgen_init); /* Legacy nodes */ diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 2492442eea77..20d90769cced 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -519,6 +519,11 @@ static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate, SI5351_CLK_INTEGER_MODE, (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0); + /* Do a pll soft reset on the affected pll */ + si5351_reg_write(hwdata->drvdata, SI5351_PLL_RESET, + hwdata->num == 0 ? SI5351_PLL_RESET_A : + SI5351_PLL_RESET_B); + dev_dbg(&hwdata->drvdata->client->dev, "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n", __func__, clk_hw_get_name(hw), @@ -1091,13 +1096,6 @@ static int si5351_clkout_set_rate(struct clk_hw *hw, unsigned long rate, si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, SI5351_CLK_POWERDOWN, 0); - /* - * Do a pll soft reset on both plls, needed in some cases to get - * all outputs running. - */ - si5351_reg_write(hwdata->drvdata, SI5351_PLL_RESET, - SI5351_PLL_RESET_A | SI5351_PLL_RESET_B); - dev_dbg(&hwdata->drvdata->client->dev, "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n", __func__, clk_hw_get_name(hw), (1 << rdiv), diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c index 68e2a4e499f1..96c6b6bc8f0e 100644 --- a/drivers/clk/clk-stm32f4.c +++ b/drivers/clk/clk-stm32f4.c @@ -1541,8 +1541,8 @@ static void __init stm32f4_rcc_init(struct device_node *np) base + gd->offset, gd->bit_idx, 0, &stm32f4_clk_lock); if (IS_ERR(clks[idx])) { - pr_err("%s: Unable to register leaf clock %s\n", - np->full_name, gd->name); + pr_err("%pOF: Unable to register leaf clock %s\n", + np, gd->name); goto fail; } } diff --git a/drivers/clk/clk-stm32h7.c b/drivers/clk/clk-stm32h7.c new file mode 100644 index 000000000000..a94c3f56c590 --- /dev/null +++ b/drivers/clk/clk-stm32h7.c @@ -0,0 +1,1410 @@ +/* + * Copyright (C) Gabriel Fernandez 2017 + * Author: Gabriel Fernandez <gabriel.fernandez@st.com> + * + * License terms: GPL V2.0. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/stm32h7-clks.h> + +/* Reset Clock Control Registers */ +#define RCC_CR 0x00 +#define RCC_CFGR 0x10 +#define RCC_D1CFGR 0x18 +#define RCC_D2CFGR 0x1C +#define RCC_D3CFGR 0x20 +#define RCC_PLLCKSELR 0x28 +#define RCC_PLLCFGR 0x2C +#define RCC_PLL1DIVR 0x30 +#define RCC_PLL1FRACR 0x34 +#define RCC_PLL2DIVR 0x38 +#define RCC_PLL2FRACR 0x3C +#define RCC_PLL3DIVR 0x40 +#define RCC_PLL3FRACR 0x44 +#define RCC_D1CCIPR 0x4C +#define RCC_D2CCIP1R 0x50 +#define RCC_D2CCIP2R 0x54 +#define RCC_D3CCIPR 0x58 +#define RCC_BDCR 0x70 +#define RCC_CSR 0x74 +#define RCC_AHB3ENR 0xD4 +#define RCC_AHB1ENR 0xD8 +#define RCC_AHB2ENR 0xDC +#define RCC_AHB4ENR 0xE0 +#define RCC_APB3ENR 0xE4 +#define RCC_APB1LENR 0xE8 +#define RCC_APB1HENR 0xEC +#define RCC_APB2ENR 0xF0 +#define RCC_APB4ENR 0xF4 + +static DEFINE_SPINLOCK(stm32rcc_lock); + +static void __iomem *base; +static struct clk_hw **hws; + +/* System clock parent */ +static const char * const sys_src[] = { + "hsi_ck", "csi_ck", "hse_ck", "pll1_p" }; + +static const char * const tracein_src[] = { + "hsi_ck", "csi_ck", "hse_ck", "pll1_r" }; + +static const char * const per_src[] = { + "hsi_ker", "csi_ker", "hse_ck", "disabled" }; + +static const char * const pll_src[] = { + "hsi_ck", "csi_ck", "hse_ck", "no clock" }; + +static const char * const sdmmc_src[] = { "pll1_q", "pll2_r" }; + +static const char * const dsi_src[] = { "ck_dsi_phy", "pll2_q" }; + +static const char * const qspi_src[] = { + "hclk", "pll1_q", "pll2_r", "per_ck" }; + +static const char * const fmc_src[] = { + "hclk", "pll1_q", "pll2_r", "per_ck" }; + +/* Kernel clock parent */ +static const char * const swp_src[] = { "pclk1", "hsi_ker" }; + +static const char * const fdcan_src[] = { "hse_ck", "pll1_q", "pll2_q" }; + +static const char * const dfsdm1_src[] = { "pclk2", "sys_ck" }; + +static const char * const spdifrx_src[] = { + "pll1_q", "pll2_r", "pll3_r", "hsi_ker" }; + +static const char *spi_src1[5] = { + "pll1_q", "pll2_p", "pll3_p", NULL, "per_ck" }; + +static const char * const spi_src2[] = { + "pclk2", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "hse_ck" }; + +static const char * const spi_src3[] = { + "pclk4", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "hse_ck" }; + +static const char * const lptim_src1[] = { + "pclk1", "pll2_p", "pll3_r", "lse_ck", "lsi_ck", "per_ck" }; + +static const char * const lptim_src2[] = { + "pclk4", "pll2_p", "pll3_r", "lse_ck", "lsi_ck", "per_ck" }; + +static const char * const cec_src[] = {"lse_ck", "lsi_ck", "csi_ker_div122" }; + +static const char * const usbotg_src[] = {"pll1_q", "pll3_q", "rc48_ck" }; + +/* i2c 1,2,3 src */ +static const char * const i2c_src1[] = { + "pclk1", "pll3_r", "hsi_ker", "csi_ker" }; + +static const char * const i2c_src2[] = { + "pclk4", "pll3_r", "hsi_ker", "csi_ker" }; + +static const char * const rng_src[] = { + "rc48_ck", "pll1_q", "lse_ck", "lsi_ck" }; + +/* usart 1,6 src */ +static const char * const usart_src1[] = { + "pclk2", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "lse_ck" }; + +/* usart 2,3,4,5,7,8 src */ +static const char * const usart_src2[] = { + "pclk1", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "lse_ck" }; + +static const char *sai_src[5] = { + "pll1_q", "pll2_p", "pll3_p", NULL, "per_ck" }; + +static const char * const adc_src[] = { "pll2_p", "pll3_r", "per_ck" }; + +/* lptim 2,3,4,5 src */ +static const char * const lpuart1_src[] = { + "pclk3", "pll2_q", "pll3_q", "csi_ker", "lse_ck" }; + +static const char * const hrtim_src[] = { "tim2_ker", "d1cpre" }; + +/* RTC clock parent */ +static const char * const rtc_src[] = { "off", "lse_ck", "lsi_ck", "hse_1M" }; + +/* Micro-controller output clock parent */ +static const char * const mco_src1[] = { + "hsi_ck", "lse_ck", "hse_ck", "pll1_q", "rc48_ck" }; + +static const char * const mco_src2[] = { + "sys_ck", "pll2_p", "hse_ck", "pll1_p", "csi_ck", "lsi_ck" }; + +/* LCD clock */ +static const char * const ltdc_src[] = {"pll3_r"}; + +/* Gate clock with ready bit and backup domain management */ +struct stm32_ready_gate { + struct clk_gate gate; + u8 bit_rdy; +}; + +#define to_ready_gate_clk(_rgate) container_of(_rgate, struct stm32_ready_gate,\ + gate) + +#define RGATE_TIMEOUT 10000 + +static int ready_gate_clk_enable(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32_ready_gate *rgate = to_ready_gate_clk(gate); + int bit_status; + unsigned int timeout = RGATE_TIMEOUT; + + if (clk_gate_ops.is_enabled(hw)) + return 0; + + clk_gate_ops.enable(hw); + + /* We can't use readl_poll_timeout() because we can blocked if + * someone enables this clock before clocksource changes. + * Only jiffies counter is available. Jiffies are incremented by + * interruptions and enable op does not allow to be interrupted. + */ + do { + bit_status = !(readl(gate->reg) & BIT(rgate->bit_rdy)); + + if (bit_status) + udelay(100); + + } while (bit_status && --timeout); + + return bit_status; +} + +static void ready_gate_clk_disable(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32_ready_gate *rgate = to_ready_gate_clk(gate); + int bit_status; + unsigned int timeout = RGATE_TIMEOUT; + + if (!clk_gate_ops.is_enabled(hw)) + return; + + clk_gate_ops.disable(hw); + + do { + bit_status = !!(readl(gate->reg) & BIT(rgate->bit_rdy)); + + if (bit_status) + udelay(100); + + } while (bit_status && --timeout); +} + +static const struct clk_ops ready_gate_clk_ops = { + .enable = ready_gate_clk_enable, + .disable = ready_gate_clk_disable, + .is_enabled = clk_gate_is_enabled, +}; + +static struct clk_hw *clk_register_ready_gate(struct device *dev, + const char *name, const char *parent_name, + void __iomem *reg, u8 bit_idx, u8 bit_rdy, + unsigned long flags, spinlock_t *lock) +{ + struct stm32_ready_gate *rgate; + struct clk_init_data init = { NULL }; + struct clk_hw *hw; + int ret; + + rgate = kzalloc(sizeof(*rgate), GFP_KERNEL); + if (!rgate) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &ready_gate_clk_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + rgate->bit_rdy = bit_rdy; + rgate->gate.lock = lock; + rgate->gate.reg = reg; + rgate->gate.bit_idx = bit_idx; + rgate->gate.hw.init = &init; + + hw = &rgate->gate.hw; + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(rgate); + hw = ERR_PTR(ret); + } + + return hw; +} + +struct gate_cfg { + u32 offset; + u8 bit_idx; +}; + +struct muxdiv_cfg { + u32 offset; + u8 shift; + u8 width; +}; + +struct composite_clk_cfg { + struct gate_cfg *gate; + struct muxdiv_cfg *mux; + struct muxdiv_cfg *div; + const char *name; + const char * const *parent_name; + int num_parents; + u32 flags; +}; + +struct composite_clk_gcfg_t { + u8 flags; + const struct clk_ops *ops; +}; + +/* + * General config definition of a composite clock (only clock diviser for rate) + */ +struct composite_clk_gcfg { + struct composite_clk_gcfg_t *mux; + struct composite_clk_gcfg_t *div; + struct composite_clk_gcfg_t *gate; +}; + +#define M_CFG_MUX(_mux_ops, _mux_flags)\ + .mux = &(struct composite_clk_gcfg_t) { _mux_flags, _mux_ops} + +#define M_CFG_DIV(_rate_ops, _rate_flags)\ + .div = &(struct composite_clk_gcfg_t) {_rate_flags, _rate_ops} + +#define M_CFG_GATE(_gate_ops, _gate_flags)\ + .gate = &(struct composite_clk_gcfg_t) { _gate_flags, _gate_ops} + +static struct clk_mux *_get_cmux(void __iomem *reg, u8 shift, u8 width, + u32 flags, spinlock_t *lock) +{ + struct clk_mux *mux; + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = reg; + mux->shift = shift; + mux->mask = (1 << width) - 1; + mux->flags = flags; + mux->lock = lock; + + return mux; +} + +static struct clk_divider *_get_cdiv(void __iomem *reg, u8 shift, u8 width, + u32 flags, spinlock_t *lock) +{ + struct clk_divider *div; + + div = kzalloc(sizeof(*div), GFP_KERNEL); + + if (!div) + return ERR_PTR(-ENOMEM); + + div->reg = reg; + div->shift = shift; + div->width = width; + div->flags = flags; + div->lock = lock; + + return div; +} + +static struct clk_gate *_get_cgate(void __iomem *reg, u8 bit_idx, u32 flags, + spinlock_t *lock) +{ + struct clk_gate *gate; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + gate->reg = reg; + gate->bit_idx = bit_idx; + gate->flags = flags; + gate->lock = lock; + + return gate; +} + +struct composite_cfg { + struct clk_hw *mux_hw; + struct clk_hw *div_hw; + struct clk_hw *gate_hw; + + const struct clk_ops *mux_ops; + const struct clk_ops *div_ops; + const struct clk_ops *gate_ops; +}; + +static void get_cfg_composite_div(const struct composite_clk_gcfg *gcfg, + const struct composite_clk_cfg *cfg, + struct composite_cfg *composite, spinlock_t *lock) +{ + struct clk_mux *mux = NULL; + struct clk_divider *div = NULL; + struct clk_gate *gate = NULL; + const struct clk_ops *mux_ops, *div_ops, *gate_ops; + struct clk_hw *mux_hw; + struct clk_hw *div_hw; + struct clk_hw *gate_hw; + + mux_ops = div_ops = gate_ops = NULL; + mux_hw = div_hw = gate_hw = NULL; + + if (gcfg->mux && gcfg->mux) { + mux = _get_cmux(base + cfg->mux->offset, + cfg->mux->shift, + cfg->mux->width, + gcfg->mux->flags, lock); + + if (!IS_ERR(mux)) { + mux_hw = &mux->hw; + mux_ops = gcfg->mux->ops ? + gcfg->mux->ops : &clk_mux_ops; + } + } + + if (gcfg->div && cfg->div) { + div = _get_cdiv(base + cfg->div->offset, + cfg->div->shift, + cfg->div->width, + gcfg->div->flags, lock); + + if (!IS_ERR(div)) { + div_hw = &div->hw; + div_ops = gcfg->div->ops ? + gcfg->div->ops : &clk_divider_ops; + } + } + + if (gcfg->gate && gcfg->gate) { + gate = _get_cgate(base + cfg->gate->offset, + cfg->gate->bit_idx, + gcfg->gate->flags, lock); + + if (!IS_ERR(gate)) { + gate_hw = &gate->hw; + gate_ops = gcfg->gate->ops ? + gcfg->gate->ops : &clk_gate_ops; + } + } + + composite->mux_hw = mux_hw; + composite->mux_ops = mux_ops; + + composite->div_hw = div_hw; + composite->div_ops = div_ops; + + composite->gate_hw = gate_hw; + composite->gate_ops = gate_ops; +} + +/* Kernel Timer */ +struct timer_ker { + u8 dppre_shift; + struct clk_hw hw; + spinlock_t *lock; +}; + +#define to_timer_ker(_hw) container_of(_hw, struct timer_ker, hw) + +static unsigned long timer_ker_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct timer_ker *clk_elem = to_timer_ker(hw); + u32 timpre; + u32 dppre_shift = clk_elem->dppre_shift; + u32 prescaler; + u32 mul; + + timpre = (readl(base + RCC_CFGR) >> 15) & 0x01; + + prescaler = (readl(base + RCC_D2CFGR) >> dppre_shift) & 0x03; + + mul = 2; + + if (prescaler < 4) + mul = 1; + + else if (timpre && prescaler > 4) + mul = 4; + + return parent_rate * mul; +} + +static const struct clk_ops timer_ker_ops = { + .recalc_rate = timer_ker_recalc_rate, +}; + +static struct clk_hw *clk_register_stm32_timer_ker(struct device *dev, + const char *name, const char *parent_name, + unsigned long flags, + u8 dppre_shift, + spinlock_t *lock) +{ + struct timer_ker *element; + struct clk_init_data init; + struct clk_hw *hw; + int err; + + element = kzalloc(sizeof(*element), GFP_KERNEL); + if (!element) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &timer_ker_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + element->hw.init = &init; + element->lock = lock; + element->dppre_shift = dppre_shift; + + hw = &element->hw; + err = clk_hw_register(dev, hw); + + if (err) { + kfree(element); + return ERR_PTR(err); + } + + return hw; +} + +static const struct clk_div_table d1cpre_div_table[] = { + { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1}, + { 4, 1 }, { 5, 1 }, { 6, 1 }, { 7, 1}, + { 8, 2 }, { 9, 4 }, { 10, 8 }, { 11, 16 }, + { 12, 64 }, { 13, 128 }, { 14, 256 }, + { 15, 512 }, + { 0 }, +}; + +static const struct clk_div_table ppre_div_table[] = { + { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1}, + { 4, 2 }, { 5, 4 }, { 6, 8 }, { 7, 16 }, + { 0 }, +}; + +static void register_core_and_bus_clocks(void) +{ + /* CORE AND BUS */ + hws[SYS_D1CPRE] = clk_hw_register_divider_table(NULL, "d1cpre", + "sys_ck", CLK_IGNORE_UNUSED, base + RCC_D1CFGR, 8, 4, 0, + d1cpre_div_table, &stm32rcc_lock); + + hws[HCLK] = clk_hw_register_divider_table(NULL, "hclk", "d1cpre", + CLK_IGNORE_UNUSED, base + RCC_D1CFGR, 0, 4, 0, + d1cpre_div_table, &stm32rcc_lock); + + /* D1 DOMAIN */ + /* * CPU Systick */ + hws[CPU_SYSTICK] = clk_hw_register_fixed_factor(NULL, "systick", + "d1cpre", 0, 1, 8); + + /* * APB3 peripheral */ + hws[PCLK3] = clk_hw_register_divider_table(NULL, "pclk3", "hclk", 0, + base + RCC_D1CFGR, 4, 3, 0, + ppre_div_table, &stm32rcc_lock); + + /* D2 DOMAIN */ + /* * APB1 peripheral */ + hws[PCLK1] = clk_hw_register_divider_table(NULL, "pclk1", "hclk", 0, + base + RCC_D2CFGR, 4, 3, 0, + ppre_div_table, &stm32rcc_lock); + + /* Timers prescaler clocks */ + clk_register_stm32_timer_ker(NULL, "tim1_ker", "pclk1", 0, + 4, &stm32rcc_lock); + + /* * APB2 peripheral */ + hws[PCLK2] = clk_hw_register_divider_table(NULL, "pclk2", "hclk", 0, + base + RCC_D2CFGR, 8, 3, 0, ppre_div_table, + &stm32rcc_lock); + + clk_register_stm32_timer_ker(NULL, "tim2_ker", "pclk2", 0, 8, + &stm32rcc_lock); + + /* D3 DOMAIN */ + /* * APB4 peripheral */ + hws[PCLK4] = clk_hw_register_divider_table(NULL, "pclk4", "hclk", 0, + base + RCC_D3CFGR, 4, 3, 0, + ppre_div_table, &stm32rcc_lock); +} + +/* MUX clock configuration */ +struct stm32_mux_clk { + const char *name; + const char * const *parents; + u8 num_parents; + u32 offset; + u8 shift; + u8 width; + u32 flags; +}; + +#define M_MCLOCF(_name, _parents, _mux_offset, _mux_shift, _mux_width, _flags)\ +{\ + .name = _name,\ + .parents = _parents,\ + .num_parents = ARRAY_SIZE(_parents),\ + .offset = _mux_offset,\ + .shift = _mux_shift,\ + .width = _mux_width,\ + .flags = _flags,\ +} + +#define M_MCLOC(_name, _parents, _mux_offset, _mux_shift, _mux_width)\ + M_MCLOCF(_name, _parents, _mux_offset, _mux_shift, _mux_width, 0)\ + +static const struct stm32_mux_clk stm32_mclk[] __initconst = { + M_MCLOC("per_ck", per_src, RCC_D1CCIPR, 28, 3), + M_MCLOC("pllsrc", pll_src, RCC_PLLCKSELR, 0, 3), + M_MCLOC("sys_ck", sys_src, RCC_CFGR, 0, 3), + M_MCLOC("tracein_ck", tracein_src, RCC_CFGR, 0, 3), +}; + +/* Oscillary clock configuration */ +struct stm32_osc_clk { + const char *name; + const char *parent; + u32 gate_offset; + u8 bit_idx; + u8 bit_rdy; + u32 flags; +}; + +#define OSC_CLKF(_name, _parent, _gate_offset, _bit_idx, _bit_rdy, _flags)\ +{\ + .name = _name,\ + .parent = _parent,\ + .gate_offset = _gate_offset,\ + .bit_idx = _bit_idx,\ + .bit_rdy = _bit_rdy,\ + .flags = _flags,\ +} + +#define OSC_CLK(_name, _parent, _gate_offset, _bit_idx, _bit_rdy)\ + OSC_CLKF(_name, _parent, _gate_offset, _bit_idx, _bit_rdy, 0) + +static const struct stm32_osc_clk stm32_oclk[] __initconst = { + OSC_CLKF("hsi_ck", "hsidiv", RCC_CR, 0, 2, CLK_IGNORE_UNUSED), + OSC_CLKF("hsi_ker", "hsidiv", RCC_CR, 1, 2, CLK_IGNORE_UNUSED), + OSC_CLKF("csi_ck", "clk-csi", RCC_CR, 7, 8, CLK_IGNORE_UNUSED), + OSC_CLKF("csi_ker", "clk-csi", RCC_CR, 9, 8, CLK_IGNORE_UNUSED), + OSC_CLKF("rc48_ck", "clk-rc48", RCC_CR, 12, 13, CLK_IGNORE_UNUSED), + OSC_CLKF("lsi_ck", "clk-lsi", RCC_CSR, 0, 1, CLK_IGNORE_UNUSED), +}; + +/* PLL configuration */ +struct st32h7_pll_cfg { + u8 bit_idx; + u32 offset_divr; + u8 bit_frac_en; + u32 offset_frac; + u8 divm; +}; + +struct stm32_pll_data { + const char *name; + const char *parent_name; + unsigned long flags; + const struct st32h7_pll_cfg *cfg; +}; + +static const struct st32h7_pll_cfg stm32h7_pll1 = { + .bit_idx = 24, + .offset_divr = RCC_PLL1DIVR, + .bit_frac_en = 0, + .offset_frac = RCC_PLL1FRACR, + .divm = 4, +}; + +static const struct st32h7_pll_cfg stm32h7_pll2 = { + .bit_idx = 26, + .offset_divr = RCC_PLL2DIVR, + .bit_frac_en = 4, + .offset_frac = RCC_PLL2FRACR, + .divm = 12, +}; + +static const struct st32h7_pll_cfg stm32h7_pll3 = { + .bit_idx = 28, + .offset_divr = RCC_PLL3DIVR, + .bit_frac_en = 8, + .offset_frac = RCC_PLL3FRACR, + .divm = 20, +}; + +static const struct stm32_pll_data stm32_pll[] = { + { "vco1", "pllsrc", CLK_IGNORE_UNUSED, &stm32h7_pll1 }, + { "vco2", "pllsrc", 0, &stm32h7_pll2 }, + { "vco3", "pllsrc", 0, &stm32h7_pll3 }, +}; + +struct stm32_fractional_divider { + void __iomem *mreg; + u8 mshift; + u8 mwidth; + u32 mmask; + + void __iomem *nreg; + u8 nshift; + u8 nwidth; + + void __iomem *freg_status; + u8 freg_bit; + void __iomem *freg_value; + u8 fshift; + u8 fwidth; + + u8 flags; + struct clk_hw hw; + spinlock_t *lock; +}; + +struct stm32_pll_obj { + spinlock_t *lock; + struct stm32_fractional_divider div; + struct stm32_ready_gate rgate; + struct clk_hw hw; +}; + +#define to_pll(_hw) container_of(_hw, struct stm32_pll_obj, hw) + +static int pll_is_enabled(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct clk_hw *_hw = &clk_elem->rgate.gate.hw; + + __clk_hw_set_clk(_hw, hw); + + return ready_gate_clk_ops.is_enabled(_hw); +} + +static int pll_enable(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct clk_hw *_hw = &clk_elem->rgate.gate.hw; + + __clk_hw_set_clk(_hw, hw); + + return ready_gate_clk_ops.enable(_hw); +} + +static void pll_disable(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct clk_hw *_hw = &clk_elem->rgate.gate.hw; + + __clk_hw_set_clk(_hw, hw); + + ready_gate_clk_ops.disable(_hw); +} + +static int pll_frac_is_enabled(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct stm32_fractional_divider *fd = &clk_elem->div; + + return (readl(fd->freg_status) >> fd->freg_bit) & 0x01; +} + +static unsigned long pll_read_frac(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct stm32_fractional_divider *fd = &clk_elem->div; + + return (readl(fd->freg_value) >> fd->fshift) & + GENMASK(fd->fwidth - 1, 0); +} + +static unsigned long pll_fd_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct stm32_fractional_divider *fd = &clk_elem->div; + unsigned long m, n; + u32 val, mask; + u64 rate, rate1 = 0; + + val = readl(fd->mreg); + mask = GENMASK(fd->mwidth - 1, 0) << fd->mshift; + m = (val & mask) >> fd->mshift; + + val = readl(fd->nreg); + mask = GENMASK(fd->nwidth - 1, 0) << fd->nshift; + n = ((val & mask) >> fd->nshift) + 1; + + if (!n || !m) + return parent_rate; + + rate = (u64)parent_rate * n; + do_div(rate, m); + + if (pll_frac_is_enabled(hw)) { + val = pll_read_frac(hw); + rate1 = (u64)parent_rate * (u64)val; + do_div(rate1, (m * 8191)); + } + + return rate + rate1; +} + +static const struct clk_ops pll_ops = { + .enable = pll_enable, + .disable = pll_disable, + .is_enabled = pll_is_enabled, + .recalc_rate = pll_fd_recalc_rate, +}; + +static struct clk_hw *clk_register_stm32_pll(struct device *dev, + const char *name, + const char *parent, + unsigned long flags, + const struct st32h7_pll_cfg *cfg, + spinlock_t *lock) +{ + struct stm32_pll_obj *pll; + struct clk_init_data init = { NULL }; + struct clk_hw *hw; + int ret; + struct stm32_fractional_divider *div = NULL; + struct stm32_ready_gate *rgate; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &pll_ops; + init.flags = flags; + init.parent_names = &parent; + init.num_parents = 1; + pll->hw.init = &init; + + hw = &pll->hw; + rgate = &pll->rgate; + + rgate->bit_rdy = cfg->bit_idx + 1; + rgate->gate.lock = lock; + rgate->gate.reg = base + RCC_CR; + rgate->gate.bit_idx = cfg->bit_idx; + + div = &pll->div; + div->flags = 0; + div->mreg = base + RCC_PLLCKSELR; + div->mshift = cfg->divm; + div->mwidth = 6; + div->nreg = base + cfg->offset_divr; + div->nshift = 0; + div->nwidth = 9; + + div->freg_status = base + RCC_PLLCFGR; + div->freg_bit = cfg->bit_frac_en; + div->freg_value = base + cfg->offset_frac; + div->fshift = 3; + div->fwidth = 13; + + div->lock = lock; + + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(pll); + hw = ERR_PTR(ret); + } + + return hw; +} + +/* ODF CLOCKS */ +static unsigned long odf_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return clk_divider_ops.recalc_rate(hw, parent_rate); +} + +static long odf_divider_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + return clk_divider_ops.round_rate(hw, rate, prate); +} + +static int odf_divider_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_hw *hwp; + int pll_status; + int ret; + + hwp = clk_hw_get_parent(hw); + + pll_status = pll_is_enabled(hwp); + + if (pll_status) + pll_disable(hwp); + + ret = clk_divider_ops.set_rate(hw, rate, parent_rate); + + if (pll_status) + pll_enable(hwp); + + return ret; +} + +static const struct clk_ops odf_divider_ops = { + .recalc_rate = odf_divider_recalc_rate, + .round_rate = odf_divider_round_rate, + .set_rate = odf_divider_set_rate, +}; + +static int odf_gate_enable(struct clk_hw *hw) +{ + struct clk_hw *hwp; + int pll_status; + int ret; + + if (clk_gate_ops.is_enabled(hw)) + return 0; + + hwp = clk_hw_get_parent(hw); + + pll_status = pll_is_enabled(hwp); + + if (pll_status) + pll_disable(hwp); + + ret = clk_gate_ops.enable(hw); + + if (pll_status) + pll_enable(hwp); + + return ret; +} + +static void odf_gate_disable(struct clk_hw *hw) +{ + struct clk_hw *hwp; + int pll_status; + + if (!clk_gate_ops.is_enabled(hw)) + return; + + hwp = clk_hw_get_parent(hw); + + pll_status = pll_is_enabled(hwp); + + if (pll_status) + pll_disable(hwp); + + clk_gate_ops.disable(hw); + + if (pll_status) + pll_enable(hwp); +} + +static const struct clk_ops odf_gate_ops = { + .enable = odf_gate_enable, + .disable = odf_gate_disable, + .is_enabled = clk_gate_is_enabled, +}; + +static struct composite_clk_gcfg odf_clk_gcfg = { + M_CFG_DIV(&odf_divider_ops, 0), + M_CFG_GATE(&odf_gate_ops, 0), +}; + +#define M_ODF_F(_name, _parent, _gate_offset, _bit_idx, _rate_offset,\ + _rate_shift, _rate_width, _flags)\ +{\ + .mux = NULL,\ + .div = &(struct muxdiv_cfg) {_rate_offset, _rate_shift, _rate_width},\ + .gate = &(struct gate_cfg) {_gate_offset, _bit_idx },\ + .name = _name,\ + .parent_name = &(const char *) {_parent},\ + .num_parents = 1,\ + .flags = _flags,\ +} + +#define M_ODF(_name, _parent, _gate_offset, _bit_idx, _rate_offset,\ + _rate_shift, _rate_width)\ +M_ODF_F(_name, _parent, _gate_offset, _bit_idx, _rate_offset,\ + _rate_shift, _rate_width, 0)\ + +static const struct composite_clk_cfg stm32_odf[3][3] = { + { + M_ODF_F("pll1_p", "vco1", RCC_PLLCFGR, 16, RCC_PLL1DIVR, 9, 7, + CLK_IGNORE_UNUSED), + M_ODF_F("pll1_q", "vco1", RCC_PLLCFGR, 17, RCC_PLL1DIVR, 16, 7, + CLK_IGNORE_UNUSED), + M_ODF_F("pll1_r", "vco1", RCC_PLLCFGR, 18, RCC_PLL1DIVR, 24, 7, + CLK_IGNORE_UNUSED), + }, + + { + M_ODF("pll2_p", "vco2", RCC_PLLCFGR, 19, RCC_PLL2DIVR, 9, 7), + M_ODF("pll2_q", "vco2", RCC_PLLCFGR, 20, RCC_PLL2DIVR, 16, 7), + M_ODF("pll2_r", "vco2", RCC_PLLCFGR, 21, RCC_PLL2DIVR, 24, 7), + }, + { + M_ODF("pll3_p", "vco3", RCC_PLLCFGR, 22, RCC_PLL3DIVR, 9, 7), + M_ODF("pll3_q", "vco3", RCC_PLLCFGR, 23, RCC_PLL3DIVR, 16, 7), + M_ODF("pll3_r", "vco3", RCC_PLLCFGR, 24, RCC_PLL3DIVR, 24, 7), + } +}; + +/* PERIF CLOCKS */ +struct pclk_t { + u32 gate_offset; + u8 bit_idx; + const char *name; + const char *parent; + u32 flags; +}; + +#define PER_CLKF(_gate_offset, _bit_idx, _name, _parent, _flags)\ +{\ + .gate_offset = _gate_offset,\ + .bit_idx = _bit_idx,\ + .name = _name,\ + .parent = _parent,\ + .flags = _flags,\ +} + +#define PER_CLK(_gate_offset, _bit_idx, _name, _parent)\ + PER_CLKF(_gate_offset, _bit_idx, _name, _parent, 0) + +static const struct pclk_t pclk[] = { + PER_CLK(RCC_AHB3ENR, 31, "d1sram1", "hclk"), + PER_CLK(RCC_AHB3ENR, 30, "itcm", "hclk"), + PER_CLK(RCC_AHB3ENR, 29, "dtcm2", "hclk"), + PER_CLK(RCC_AHB3ENR, 28, "dtcm1", "hclk"), + PER_CLK(RCC_AHB3ENR, 8, "flitf", "hclk"), + PER_CLK(RCC_AHB3ENR, 5, "jpgdec", "hclk"), + PER_CLK(RCC_AHB3ENR, 4, "dma2d", "hclk"), + PER_CLK(RCC_AHB3ENR, 0, "mdma", "hclk"), + PER_CLK(RCC_AHB1ENR, 28, "usb2ulpi", "hclk"), + PER_CLK(RCC_AHB1ENR, 26, "usb1ulpi", "hclk"), + PER_CLK(RCC_AHB1ENR, 17, "eth1rx", "hclk"), + PER_CLK(RCC_AHB1ENR, 16, "eth1tx", "hclk"), + PER_CLK(RCC_AHB1ENR, 15, "eth1mac", "hclk"), + PER_CLK(RCC_AHB1ENR, 14, "art", "hclk"), + PER_CLK(RCC_AHB1ENR, 1, "dma2", "hclk"), + PER_CLK(RCC_AHB1ENR, 0, "dma1", "hclk"), + PER_CLK(RCC_AHB2ENR, 31, "d2sram3", "hclk"), + PER_CLK(RCC_AHB2ENR, 30, "d2sram2", "hclk"), + PER_CLK(RCC_AHB2ENR, 29, "d2sram1", "hclk"), + PER_CLK(RCC_AHB2ENR, 5, "hash", "hclk"), + PER_CLK(RCC_AHB2ENR, 4, "crypt", "hclk"), + PER_CLK(RCC_AHB2ENR, 0, "camitf", "hclk"), + PER_CLK(RCC_AHB4ENR, 28, "bkpram", "hclk"), + PER_CLK(RCC_AHB4ENR, 25, "hsem", "hclk"), + PER_CLK(RCC_AHB4ENR, 21, "bdma", "hclk"), + PER_CLK(RCC_AHB4ENR, 19, "crc", "hclk"), + PER_CLK(RCC_AHB4ENR, 10, "gpiok", "hclk"), + PER_CLK(RCC_AHB4ENR, 9, "gpioj", "hclk"), + PER_CLK(RCC_AHB4ENR, 8, "gpioi", "hclk"), + PER_CLK(RCC_AHB4ENR, 7, "gpioh", "hclk"), + PER_CLK(RCC_AHB4ENR, 6, "gpiog", "hclk"), + PER_CLK(RCC_AHB4ENR, 5, "gpiof", "hclk"), + PER_CLK(RCC_AHB4ENR, 4, "gpioe", "hclk"), + PER_CLK(RCC_AHB4ENR, 3, "gpiod", "hclk"), + PER_CLK(RCC_AHB4ENR, 2, "gpioc", "hclk"), + PER_CLK(RCC_AHB4ENR, 1, "gpiob", "hclk"), + PER_CLK(RCC_AHB4ENR, 0, "gpioa", "hclk"), + PER_CLK(RCC_APB3ENR, 6, "wwdg1", "pclk3"), + PER_CLK(RCC_APB1LENR, 29, "dac12", "pclk1"), + PER_CLK(RCC_APB1LENR, 11, "wwdg2", "pclk1"), + PER_CLK(RCC_APB1LENR, 8, "tim14", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 7, "tim13", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 6, "tim12", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 5, "tim7", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 4, "tim6", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 3, "tim5", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 2, "tim4", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 1, "tim3", "tim1_ker"), + PER_CLK(RCC_APB1LENR, 0, "tim2", "tim1_ker"), + PER_CLK(RCC_APB1HENR, 5, "mdios", "pclk1"), + PER_CLK(RCC_APB1HENR, 4, "opamp", "pclk1"), + PER_CLK(RCC_APB1HENR, 1, "crs", "pclk1"), + PER_CLK(RCC_APB2ENR, 18, "tim17", "tim2_ker"), + PER_CLK(RCC_APB2ENR, 17, "tim16", "tim2_ker"), + PER_CLK(RCC_APB2ENR, 16, "tim15", "tim2_ker"), + PER_CLK(RCC_APB2ENR, 1, "tim8", "tim2_ker"), + PER_CLK(RCC_APB2ENR, 0, "tim1", "tim2_ker"), + PER_CLK(RCC_APB4ENR, 26, "tmpsens", "pclk4"), + PER_CLK(RCC_APB4ENR, 16, "rtcapb", "pclk4"), + PER_CLK(RCC_APB4ENR, 15, "vref", "pclk4"), + PER_CLK(RCC_APB4ENR, 14, "comp12", "pclk4"), + PER_CLK(RCC_APB4ENR, 1, "syscfg", "pclk4"), +}; + +/* KERNEL CLOCKS */ +#define KER_CLKF(_gate_offset, _bit_idx,\ + _mux_offset, _mux_shift, _mux_width,\ + _name, _parent_name,\ + _flags) \ +{ \ + .gate = &(struct gate_cfg) {_gate_offset, _bit_idx},\ + .mux = &(struct muxdiv_cfg) {_mux_offset, _mux_shift, _mux_width },\ + .name = _name, \ + .parent_name = _parent_name, \ + .num_parents = ARRAY_SIZE(_parent_name),\ + .flags = _flags,\ +} + +#define KER_CLK(_gate_offset, _bit_idx, _mux_offset, _mux_shift, _mux_width,\ + _name, _parent_name) \ +KER_CLKF(_gate_offset, _bit_idx, _mux_offset, _mux_shift, _mux_width,\ + _name, _parent_name, 0)\ + +#define KER_CLKF_NOMUX(_gate_offset, _bit_idx,\ + _name, _parent_name,\ + _flags) \ +{ \ + .gate = &(struct gate_cfg) {_gate_offset, _bit_idx},\ + .mux = NULL,\ + .name = _name, \ + .parent_name = _parent_name, \ + .num_parents = 1,\ + .flags = _flags,\ +} + +static const struct composite_clk_cfg kclk[] = { + KER_CLK(RCC_AHB3ENR, 16, RCC_D1CCIPR, 16, 1, "sdmmc1", sdmmc_src), + KER_CLKF(RCC_AHB3ENR, 14, RCC_D1CCIPR, 4, 2, "quadspi", qspi_src, + CLK_IGNORE_UNUSED), + KER_CLKF(RCC_AHB3ENR, 12, RCC_D1CCIPR, 0, 2, "fmc", fmc_src, + CLK_IGNORE_UNUSED), + KER_CLK(RCC_AHB1ENR, 27, RCC_D2CCIP2R, 20, 2, "usb2otg", usbotg_src), + KER_CLK(RCC_AHB1ENR, 25, RCC_D2CCIP2R, 20, 2, "usb1otg", usbotg_src), + KER_CLK(RCC_AHB1ENR, 5, RCC_D3CCIPR, 16, 2, "adc12", adc_src), + KER_CLK(RCC_AHB2ENR, 9, RCC_D1CCIPR, 16, 1, "sdmmc2", sdmmc_src), + KER_CLK(RCC_AHB2ENR, 6, RCC_D2CCIP2R, 8, 2, "rng", rng_src), + KER_CLK(RCC_AHB4ENR, 24, RCC_D3CCIPR, 16, 2, "adc3", adc_src), + KER_CLKF(RCC_APB3ENR, 4, RCC_D1CCIPR, 8, 1, "dsi", dsi_src, + CLK_SET_RATE_PARENT), + KER_CLKF_NOMUX(RCC_APB3ENR, 3, "ltdc", ltdc_src, CLK_SET_RATE_PARENT), + KER_CLK(RCC_APB1LENR, 31, RCC_D2CCIP2R, 0, 3, "usart8", usart_src2), + KER_CLK(RCC_APB1LENR, 30, RCC_D2CCIP2R, 0, 3, "usart7", usart_src2), + KER_CLK(RCC_APB1LENR, 27, RCC_D2CCIP2R, 22, 2, "hdmicec", cec_src), + KER_CLK(RCC_APB1LENR, 23, RCC_D2CCIP2R, 12, 2, "i2c3", i2c_src1), + KER_CLK(RCC_APB1LENR, 22, RCC_D2CCIP2R, 12, 2, "i2c2", i2c_src1), + KER_CLK(RCC_APB1LENR, 21, RCC_D2CCIP2R, 12, 2, "i2c1", i2c_src1), + KER_CLK(RCC_APB1LENR, 20, RCC_D2CCIP2R, 0, 3, "uart5", usart_src2), + KER_CLK(RCC_APB1LENR, 19, RCC_D2CCIP2R, 0, 3, "uart4", usart_src2), + KER_CLK(RCC_APB1LENR, 18, RCC_D2CCIP2R, 0, 3, "usart3", usart_src2), + KER_CLK(RCC_APB1LENR, 17, RCC_D2CCIP2R, 0, 3, "usart2", usart_src2), + KER_CLK(RCC_APB1LENR, 16, RCC_D2CCIP1R, 20, 2, "spdifrx", spdifrx_src), + KER_CLK(RCC_APB1LENR, 15, RCC_D2CCIP1R, 16, 3, "spi3", spi_src1), + KER_CLK(RCC_APB1LENR, 14, RCC_D2CCIP1R, 16, 3, "spi2", spi_src1), + KER_CLK(RCC_APB1LENR, 9, RCC_D2CCIP2R, 28, 3, "lptim1", lptim_src1), + KER_CLK(RCC_APB1HENR, 8, RCC_D2CCIP1R, 28, 2, "fdcan", fdcan_src), + KER_CLK(RCC_APB1HENR, 2, RCC_D2CCIP1R, 31, 1, "swp", swp_src), + KER_CLK(RCC_APB2ENR, 29, RCC_CFGR, 14, 1, "hrtim", hrtim_src), + KER_CLK(RCC_APB2ENR, 28, RCC_D2CCIP1R, 24, 1, "dfsdm1", dfsdm1_src), + KER_CLKF(RCC_APB2ENR, 24, RCC_D2CCIP1R, 6, 3, "sai3", sai_src, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), + KER_CLKF(RCC_APB2ENR, 23, RCC_D2CCIP1R, 6, 3, "sai2", sai_src, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), + KER_CLKF(RCC_APB2ENR, 22, RCC_D2CCIP1R, 0, 3, "sai1", sai_src, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT), + KER_CLK(RCC_APB2ENR, 20, RCC_D2CCIP1R, 16, 3, "spi5", spi_src2), + KER_CLK(RCC_APB2ENR, 13, RCC_D2CCIP1R, 16, 3, "spi4", spi_src2), + KER_CLK(RCC_APB2ENR, 12, RCC_D2CCIP1R, 16, 3, "spi1", spi_src1), + KER_CLK(RCC_APB2ENR, 5, RCC_D2CCIP2R, 3, 3, "usart6", usart_src1), + KER_CLK(RCC_APB2ENR, 4, RCC_D2CCIP2R, 3, 3, "usart1", usart_src1), + KER_CLK(RCC_APB4ENR, 21, RCC_D3CCIPR, 24, 3, "sai4b", sai_src), + KER_CLK(RCC_APB4ENR, 21, RCC_D3CCIPR, 21, 3, "sai4a", sai_src), + KER_CLK(RCC_APB4ENR, 12, RCC_D3CCIPR, 13, 3, "lptim5", lptim_src2), + KER_CLK(RCC_APB4ENR, 11, RCC_D3CCIPR, 13, 3, "lptim4", lptim_src2), + KER_CLK(RCC_APB4ENR, 10, RCC_D3CCIPR, 13, 3, "lptim3", lptim_src2), + KER_CLK(RCC_APB4ENR, 9, RCC_D3CCIPR, 10, 3, "lptim2", lptim_src2), + KER_CLK(RCC_APB4ENR, 7, RCC_D3CCIPR, 8, 2, "i2c4", i2c_src2), + KER_CLK(RCC_APB4ENR, 5, RCC_D3CCIPR, 28, 3, "spi6", spi_src3), + KER_CLK(RCC_APB4ENR, 3, RCC_D3CCIPR, 0, 3, "lpuart1", lpuart1_src), +}; + +static struct composite_clk_gcfg kernel_clk_cfg = { + M_CFG_MUX(NULL, 0), + M_CFG_GATE(NULL, 0), +}; + +/* RTC clock */ +/* + * RTC & LSE registers are protected against parasitic write access. + * PWR_CR_DBP bit must be set to enable write access to RTC registers. + */ +/* STM32_PWR_CR */ +#define PWR_CR 0x00 +/* STM32_PWR_CR bit field */ +#define PWR_CR_DBP BIT(8) + +static struct composite_clk_gcfg rtc_clk_cfg = { + M_CFG_MUX(NULL, 0), + M_CFG_GATE(NULL, 0), +}; + +static const struct composite_clk_cfg rtc_clk = + KER_CLK(RCC_BDCR, 15, RCC_BDCR, 8, 2, "rtc_ck", rtc_src); + +/* Micro-controller output clock */ +static struct composite_clk_gcfg mco_clk_cfg = { + M_CFG_MUX(NULL, 0), + M_CFG_DIV(NULL, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO), +}; + +#define M_MCO_F(_name, _parents, _mux_offset, _mux_shift, _mux_width,\ + _rate_offset, _rate_shift, _rate_width,\ + _flags)\ +{\ + .mux = &(struct muxdiv_cfg) {_mux_offset, _mux_shift, _mux_width },\ + .div = &(struct muxdiv_cfg) {_rate_offset, _rate_shift, _rate_width},\ + .gate = NULL,\ + .name = _name,\ + .parent_name = _parents,\ + .num_parents = ARRAY_SIZE(_parents),\ + .flags = _flags,\ +} + +static const struct composite_clk_cfg mco_clk[] = { + M_MCO_F("mco1", mco_src1, RCC_CFGR, 22, 4, RCC_CFGR, 18, 4, 0), + M_MCO_F("mco2", mco_src2, RCC_CFGR, 29, 3, RCC_CFGR, 25, 4, 0), +}; + +static void __init stm32h7_rcc_init(struct device_node *np) +{ + struct clk_hw_onecell_data *clk_data; + struct composite_cfg c_cfg; + int n; + const char *hse_clk, *lse_clk, *i2s_clk; + struct regmap *pdrm; + + clk_data = kzalloc(sizeof(*clk_data) + + sizeof(*clk_data->hws) * STM32H7_MAX_CLKS, + GFP_KERNEL); + if (!clk_data) + return; + + clk_data->num = STM32H7_MAX_CLKS; + + hws = clk_data->hws; + + for (n = 0; n < STM32H7_MAX_CLKS; n++) + hws[n] = ERR_PTR(-ENOENT); + + /* get RCC base @ from DT */ + base = of_iomap(np, 0); + if (!base) { + pr_err("%s: unable to map resource", np->name); + goto err_free_clks; + } + + pdrm = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(pdrm)) + pr_warn("%s: Unable to get syscfg\n", __func__); + else + /* In any case disable backup domain write protection + * and will never be enabled. + * Needed by LSE & RTC clocks. + */ + regmap_update_bits(pdrm, PWR_CR, PWR_CR_DBP, PWR_CR_DBP); + + /* Put parent names from DT */ + hse_clk = of_clk_get_parent_name(np, 0); + lse_clk = of_clk_get_parent_name(np, 1); + i2s_clk = of_clk_get_parent_name(np, 2); + + sai_src[3] = i2s_clk; + spi_src1[3] = i2s_clk; + + /* Register Internal oscillators */ + clk_hw_register_fixed_rate(NULL, "clk-hsi", NULL, 0, 64000000); + clk_hw_register_fixed_rate(NULL, "clk-csi", NULL, 0, 4000000); + clk_hw_register_fixed_rate(NULL, "clk-lsi", NULL, 0, 32000); + clk_hw_register_fixed_rate(NULL, "clk-rc48", NULL, 0, 48000); + + /* This clock is coming from outside. Frequencies unknown */ + hws[CK_DSI_PHY] = clk_hw_register_fixed_rate(NULL, "ck_dsi_phy", NULL, + 0, 0); + + hws[HSI_DIV] = clk_hw_register_divider(NULL, "hsidiv", "clk-hsi", 0, + base + RCC_CR, 3, 2, CLK_DIVIDER_POWER_OF_TWO, + &stm32rcc_lock); + + hws[HSE_1M] = clk_hw_register_divider(NULL, "hse_1M", "hse_ck", 0, + base + RCC_CFGR, 8, 6, CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO, + &stm32rcc_lock); + + /* Mux system clocks */ + for (n = 0; n < ARRAY_SIZE(stm32_mclk); n++) + hws[MCLK_BANK + n] = clk_hw_register_mux(NULL, + stm32_mclk[n].name, + stm32_mclk[n].parents, + stm32_mclk[n].num_parents, + stm32_mclk[n].flags, + stm32_mclk[n].offset + base, + stm32_mclk[n].shift, + stm32_mclk[n].width, + 0, + &stm32rcc_lock); + + register_core_and_bus_clocks(); + + /* Oscillary clocks */ + for (n = 0; n < ARRAY_SIZE(stm32_oclk); n++) + hws[OSC_BANK + n] = clk_register_ready_gate(NULL, + stm32_oclk[n].name, + stm32_oclk[n].parent, + stm32_oclk[n].gate_offset + base, + stm32_oclk[n].bit_idx, + stm32_oclk[n].bit_rdy, + stm32_oclk[n].flags, + &stm32rcc_lock); + + hws[HSE_CK] = clk_register_ready_gate(NULL, + "hse_ck", + hse_clk, + RCC_CR + base, + 16, 17, + 0, + &stm32rcc_lock); + + hws[LSE_CK] = clk_register_ready_gate(NULL, + "lse_ck", + lse_clk, + RCC_BDCR + base, + 0, 1, + 0, + &stm32rcc_lock); + + hws[CSI_KER_DIV122 + n] = clk_hw_register_fixed_factor(NULL, + "csi_ker_div122", "csi_ker", 0, 1, 122); + + /* PLLs */ + for (n = 0; n < ARRAY_SIZE(stm32_pll); n++) { + int odf; + + /* Register the VCO */ + clk_register_stm32_pll(NULL, stm32_pll[n].name, + stm32_pll[n].parent_name, stm32_pll[n].flags, + stm32_pll[n].cfg, + &stm32rcc_lock); + + /* Register the 3 output dividers */ + for (odf = 0; odf < 3; odf++) { + int idx = n * 3 + odf; + + get_cfg_composite_div(&odf_clk_gcfg, &stm32_odf[n][odf], + &c_cfg, &stm32rcc_lock); + + hws[ODF_BANK + idx] = clk_hw_register_composite(NULL, + stm32_odf[n][odf].name, + stm32_odf[n][odf].parent_name, + stm32_odf[n][odf].num_parents, + c_cfg.mux_hw, c_cfg.mux_ops, + c_cfg.div_hw, c_cfg.div_ops, + c_cfg.gate_hw, c_cfg.gate_ops, + stm32_odf[n][odf].flags); + } + } + + /* Peripheral clocks */ + for (n = 0; n < ARRAY_SIZE(pclk); n++) + hws[PERIF_BANK + n] = clk_hw_register_gate(NULL, pclk[n].name, + pclk[n].parent, + pclk[n].flags, base + pclk[n].gate_offset, + pclk[n].bit_idx, pclk[n].flags, &stm32rcc_lock); + + /* Kernel clocks */ + for (n = 0; n < ARRAY_SIZE(kclk); n++) { + get_cfg_composite_div(&kernel_clk_cfg, &kclk[n], &c_cfg, + &stm32rcc_lock); + + hws[KERN_BANK + n] = clk_hw_register_composite(NULL, + kclk[n].name, + kclk[n].parent_name, + kclk[n].num_parents, + c_cfg.mux_hw, c_cfg.mux_ops, + c_cfg.div_hw, c_cfg.div_ops, + c_cfg.gate_hw, c_cfg.gate_ops, + kclk[n].flags); + } + + /* RTC clock (default state is off) */ + clk_hw_register_fixed_rate(NULL, "off", NULL, 0, 0); + + get_cfg_composite_div(&rtc_clk_cfg, &rtc_clk, &c_cfg, &stm32rcc_lock); + + hws[RTC_CK] = clk_hw_register_composite(NULL, + rtc_clk.name, + rtc_clk.parent_name, + rtc_clk.num_parents, + c_cfg.mux_hw, c_cfg.mux_ops, + c_cfg.div_hw, c_cfg.div_ops, + c_cfg.gate_hw, c_cfg.gate_ops, + rtc_clk.flags); + + /* Micro-controller clocks */ + for (n = 0; n < ARRAY_SIZE(mco_clk); n++) { + get_cfg_composite_div(&mco_clk_cfg, &mco_clk[n], &c_cfg, + &stm32rcc_lock); + + hws[MCO_BANK + n] = clk_hw_register_composite(NULL, + mco_clk[n].name, + mco_clk[n].parent_name, + mco_clk[n].num_parents, + c_cfg.mux_hw, c_cfg.mux_ops, + c_cfg.div_hw, c_cfg.div_ops, + c_cfg.gate_hw, c_cfg.gate_ops, + mco_clk[n].flags); + } + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); + + return; + +err_free_clks: + kfree(clk_data); +} + +/* The RCC node is a clock and reset controller, and these + * functionalities are supported by different drivers that + * matches the same compatible strings. + */ +CLK_OF_DECLARE_DRIVER(stm32h7_rcc, "st,stm32h743-rcc", stm32h7_rcc_init); diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c index ea7d552a2f2b..decffb3826ec 100644 --- a/drivers/clk/clk-versaclock5.c +++ b/drivers/clk/clk-versaclock5.c @@ -57,6 +57,7 @@ #define VC5_PRIM_SRC_SHDN 0x10 #define VC5_PRIM_SRC_SHDN_EN_XTAL BIT(7) #define VC5_PRIM_SRC_SHDN_EN_CLKIN BIT(6) +#define VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ BIT(3) #define VC5_PRIM_SRC_SHDN_SP BIT(1) #define VC5_PRIM_SRC_SHDN_EN_GBL_SHDN BIT(0) @@ -122,12 +123,16 @@ /* flags to describe chip features */ /* chip has built-in oscilator */ #define VC5_HAS_INTERNAL_XTAL BIT(0) +/* chip has PFD requency doubler */ +#define VC5_HAS_PFD_FREQ_DBL BIT(1) /* Supported IDT VC5 models. */ enum vc5_model { IDT_VC5_5P49V5923, + IDT_VC5_5P49V5925, IDT_VC5_5P49V5933, IDT_VC5_5P49V5935, + IDT_VC6_5P49V6901, }; /* Structure to describe features of a particular VC5 model */ @@ -157,6 +162,8 @@ struct vc5_driver_data { struct clk *pin_clkin; unsigned char clk_mux_ins; struct clk_hw clk_mux; + struct clk_hw clk_mul; + struct clk_hw clk_pfd; struct vc5_hw_data clk_pll; struct vc5_hw_data clk_fod[VC5_MAX_FOD_NUM]; struct vc5_hw_data clk_out[VC5_MAX_CLK_OUT_NUM]; @@ -166,6 +173,14 @@ static const char * const vc5_mux_names[] = { "mux" }; +static const char * const vc5_dbl_names[] = { + "dbl" +}; + +static const char * const vc5_pfd_names[] = { + "pfd" +}; + static const char * const vc5_pll_names[] = { "pll" }; @@ -254,11 +269,64 @@ static int vc5_mux_set_parent(struct clk_hw *hw, u8 index) return regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, mask, src); } -static unsigned long vc5_mux_recalc_rate(struct clk_hw *hw, +static const struct clk_ops vc5_mux_ops = { + .set_parent = vc5_mux_set_parent, + .get_parent = vc5_mux_get_parent, +}; + +static unsigned long vc5_dbl_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct vc5_driver_data *vc5 = - container_of(hw, struct vc5_driver_data, clk_mux); + container_of(hw, struct vc5_driver_data, clk_mul); + unsigned int premul; + + regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &premul); + if (premul & VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ) + parent_rate *= 2; + + return parent_rate; +} + +static long vc5_dbl_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + if ((*parent_rate == rate) || ((*parent_rate * 2) == rate)) + return rate; + else + return -EINVAL; +} + +static int vc5_dbl_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct vc5_driver_data *vc5 = + container_of(hw, struct vc5_driver_data, clk_mul); + u32 mask; + + if ((parent_rate * 2) == rate) + mask = VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ; + else + mask = 0; + + regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, + VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ, + mask); + + return 0; +} + +static const struct clk_ops vc5_dbl_ops = { + .recalc_rate = vc5_dbl_recalc_rate, + .round_rate = vc5_dbl_round_rate, + .set_rate = vc5_dbl_set_rate, +}; + +static unsigned long vc5_pfd_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct vc5_driver_data *vc5 = + container_of(hw, struct vc5_driver_data, clk_pfd); unsigned int prediv, div; regmap_read(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, &prediv); @@ -276,7 +344,7 @@ static unsigned long vc5_mux_recalc_rate(struct clk_hw *hw, return parent_rate / VC5_REF_DIVIDER_REF_DIV(div); } -static long vc5_mux_round_rate(struct clk_hw *hw, unsigned long rate, +static long vc5_pfd_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { unsigned long idiv; @@ -296,11 +364,11 @@ static long vc5_mux_round_rate(struct clk_hw *hw, unsigned long rate, return *parent_rate / idiv; } -static int vc5_mux_set_rate(struct clk_hw *hw, unsigned long rate, +static int vc5_pfd_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct vc5_driver_data *vc5 = - container_of(hw, struct vc5_driver_data, clk_mux); + container_of(hw, struct vc5_driver_data, clk_pfd); unsigned long idiv; u8 div; @@ -328,12 +396,10 @@ static int vc5_mux_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static const struct clk_ops vc5_mux_ops = { - .set_parent = vc5_mux_set_parent, - .get_parent = vc5_mux_get_parent, - .recalc_rate = vc5_mux_recalc_rate, - .round_rate = vc5_mux_round_rate, - .set_rate = vc5_mux_set_rate, +static const struct clk_ops vc5_pfd_ops = { + .recalc_rate = vc5_pfd_recalc_rate, + .round_rate = vc5_pfd_round_rate, + .set_rate = vc5_pfd_set_rate, }; /* @@ -426,6 +492,10 @@ static unsigned long vc5_fod_recalc_rate(struct clk_hw *hw, div_frc = (od_frc[0] << 22) | (od_frc[1] << 14) | (od_frc[2] << 6) | (od_frc[3] >> 2); + /* Avoid division by zero if the output is not configured. */ + if (div_int == 0 && div_frc == 0) + return 0; + /* The PLL divider has 12 integer bits and 30 fractional bits */ return div64_u64((u64)f_in << 24ULL, ((u64)div_int << 24ULL) + div_frc); } @@ -503,6 +573,25 @@ static int vc5_clk_out_prepare(struct clk_hw *hw) { struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); struct vc5_driver_data *vc5 = hwdata->vc5; + const u8 mask = VC5_OUT_DIV_CONTROL_SELB_NORM | + VC5_OUT_DIV_CONTROL_SEL_EXT | + VC5_OUT_DIV_CONTROL_EN_FOD; + unsigned int src; + int ret; + + /* + * If the input mux is disabled, enable it first and + * select source from matching FOD. + */ + regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src); + if ((src & mask) == 0) { + src = VC5_OUT_DIV_CONTROL_RESET | VC5_OUT_DIV_CONTROL_EN_FOD; + ret = regmap_update_bits(vc5->regmap, + VC5_OUT_DIV_CONTROL(hwdata->num), + mask | VC5_OUT_DIV_CONTROL_RESET, src); + if (ret) + return ret; + } /* Enable the clock buffer */ regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), @@ -516,7 +605,7 @@ static void vc5_clk_out_unprepare(struct clk_hw *hw) struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); struct vc5_driver_data *vc5 = hwdata->vc5; - /* Enable the clock buffer */ + /* Disable the clock buffer */ regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), VC5_CLK_OUTPUT_CFG1_EN_CLKBUF, 0); } @@ -537,6 +626,9 @@ static unsigned char vc5_clk_out_get_parent(struct clk_hw *hw) regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src); src &= mask; + if (src == 0) /* Input mux set to DISABLED */ + return 0; + if ((src & fodclkmask) == VC5_OUT_DIV_CONTROL_EN_FOD) return 0; @@ -595,7 +687,9 @@ static int vc5_map_index_to_output(const enum vc5_model model, case IDT_VC5_5P49V5933: return (n == 0) ? 0 : 3; case IDT_VC5_5P49V5923: + case IDT_VC5_5P49V5925: case IDT_VC5_5P49V5935: + case IDT_VC6_5P49V6901: default: return n; } @@ -672,12 +766,46 @@ static int vc5_probe(struct i2c_client *client, goto err_clk; } + if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) { + /* Register frequency doubler */ + memset(&init, 0, sizeof(init)); + init.name = vc5_dbl_names[0]; + init.ops = &vc5_dbl_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = vc5_mux_names; + init.num_parents = 1; + vc5->clk_mul.init = &init; + ret = devm_clk_hw_register(&client->dev, &vc5->clk_mul); + if (ret) { + dev_err(&client->dev, "unable to register %s\n", + init.name); + goto err_clk; + } + } + + /* Register PFD */ + memset(&init, 0, sizeof(init)); + init.name = vc5_pfd_names[0]; + init.ops = &vc5_pfd_ops; + init.flags = CLK_SET_RATE_PARENT; + if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) + init.parent_names = vc5_dbl_names; + else + init.parent_names = vc5_mux_names; + init.num_parents = 1; + vc5->clk_pfd.init = &init; + ret = devm_clk_hw_register(&client->dev, &vc5->clk_pfd); + if (ret) { + dev_err(&client->dev, "unable to register %s\n", init.name); + goto err_clk; + } + /* Register PLL */ memset(&init, 0, sizeof(init)); init.name = vc5_pll_names[0]; init.ops = &vc5_pll_ops; init.flags = CLK_SET_RATE_PARENT; - init.parent_names = vc5_mux_names; + init.parent_names = vc5_pfd_names; init.num_parents = 1; vc5->clk_pll.num = 0; vc5->clk_pll.vc5 = vc5; @@ -785,6 +913,13 @@ static const struct vc5_chip_info idt_5p49v5923_info = { .flags = 0, }; +static const struct vc5_chip_info idt_5p49v5925_info = { + .model = IDT_VC5_5P49V5925, + .clk_fod_cnt = 4, + .clk_out_cnt = 5, + .flags = 0, +}; + static const struct vc5_chip_info idt_5p49v5933_info = { .model = IDT_VC5_5P49V5933, .clk_fod_cnt = 2, @@ -799,18 +934,29 @@ static const struct vc5_chip_info idt_5p49v5935_info = { .flags = VC5_HAS_INTERNAL_XTAL, }; +static const struct vc5_chip_info idt_5p49v6901_info = { + .model = IDT_VC6_5P49V6901, + .clk_fod_cnt = 4, + .clk_out_cnt = 5, + .flags = VC5_HAS_PFD_FREQ_DBL, +}; + static const struct i2c_device_id vc5_id[] = { { "5p49v5923", .driver_data = IDT_VC5_5P49V5923 }, + { "5p49v5925", .driver_data = IDT_VC5_5P49V5925 }, { "5p49v5933", .driver_data = IDT_VC5_5P49V5933 }, { "5p49v5935", .driver_data = IDT_VC5_5P49V5935 }, + { "5p49v6901", .driver_data = IDT_VC6_5P49V6901 }, { } }; MODULE_DEVICE_TABLE(i2c, vc5_id); static const struct of_device_id clk_vc5_of_match[] = { { .compatible = "idt,5p49v5923", .data = &idt_5p49v5923_info }, + { .compatible = "idt,5p49v5925", .data = &idt_5p49v5925_info }, { .compatible = "idt,5p49v5933", .data = &idt_5p49v5933_info }, { .compatible = "idt,5p49v5935", .data = &idt_5p49v5935_info }, + { .compatible = "idt,5p49v6901", .data = &idt_5p49v6901_info }, { }, }; MODULE_DEVICE_TABLE(of, clk_vc5_of_match); diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index bc37030e38ba..4c75821a3933 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -192,7 +192,7 @@ static void xgene_pllclk_init(struct device_node *np, enum xgene_pll_type pll_ty reg = of_iomap(np, 0); if (reg == NULL) { - pr_err("Unable to map CSR register for %s\n", np->full_name); + pr_err("Unable to map CSR register for %pOF\n", np); return; } of_property_read_string(np, "clock-output-names", &clk_name); @@ -409,12 +409,12 @@ static void xgene_pmdclk_init(struct device_node *np) /* Parse the DTS register for resource */ rc = of_address_to_resource(np, 0, &res); if (rc != 0) { - pr_err("no DTS register for %s\n", np->full_name); + pr_err("no DTS register for %pOF\n", np); return; } csr_reg = of_iomap(np, 0); if (!csr_reg) { - pr_err("Unable to map resource for %s\n", np->full_name); + pr_err("Unable to map resource for %pOF\n", np); return; } of_property_read_string(np, "clock-output-names", &clk_name); @@ -703,16 +703,14 @@ static void __init xgene_devclk_init(struct device_node *np) rc = of_address_to_resource(np, i, &res); if (rc != 0) { if (i == 0) { - pr_err("no DTS register for %s\n", - np->full_name); + pr_err("no DTS register for %pOF\n", np); return; } break; } map_res = of_iomap(np, i); if (map_res == NULL) { - pr_err("Unable to map resource %d for %s\n", - i, np->full_name); + pr_err("Unable to map resource %d for %pOF\n", i, np); goto err; } if (strcmp(res.name, "div-reg") == 0) @@ -747,8 +745,7 @@ static void __init xgene_devclk_init(struct device_node *np) pr_debug("Add %s clock\n", clk_name); rc = of_clk_add_provider(np, of_clk_src_simple_get, clk); if (rc != 0) - pr_err("%s: could register provider clk %s\n", __func__, - np->full_name); + pr_err("%s: could register provider clk %pOF\n", __func__, np); return; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index fc58c52a26b4..c8d83acda006 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -3132,7 +3132,7 @@ int of_clk_add_provider(struct device_node *np, mutex_lock(&of_clk_mutex); list_add(&cp->link, &of_clk_providers); mutex_unlock(&of_clk_mutex); - pr_debug("Added clock from %s\n", np->full_name); + pr_debug("Added clock from %pOF\n", np); ret = of_clk_set_defaults(np, true); if (ret < 0) @@ -3167,7 +3167,7 @@ int of_clk_add_hw_provider(struct device_node *np, mutex_lock(&of_clk_mutex); list_add(&cp->link, &of_clk_providers); mutex_unlock(&of_clk_mutex); - pr_debug("Added clk_hw provider from %s\n", np->full_name); + pr_debug("Added clk_hw provider from %pOF\n", np); ret = of_clk_set_defaults(np, true); if (ret < 0) diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index bb8a77a5985f..6b2f29df3f70 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -77,8 +77,8 @@ static struct clk *__of_clk_get_by_name(struct device_node *np, break; } else if (name && index >= 0) { if (PTR_ERR(clk) != -EPROBE_DEFER) - pr_err("ERROR: could not get clock %s:%s(%i)\n", - np->full_name, name ? name : "", index); + pr_err("ERROR: could not get clock %pOF:%s(%i)\n", + np, name ? name : "", index); return clk; } diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c index 4181b6808545..e786d717f75d 100644 --- a/drivers/clk/hisilicon/clk-hi6220.c +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -55,9 +55,9 @@ static struct hisi_fixed_factor_clock hi6220_fixed_factor_clks[] __initdata = { }; static struct hisi_gate_clock hi6220_separated_gate_clks_ao[] __initdata = { - { HI6220_WDT0_PCLK, "wdt0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 12, 0, }, - { HI6220_WDT1_PCLK, "wdt1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 13, 0, }, - { HI6220_WDT2_PCLK, "wdt2_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 14, 0, }, + { HI6220_WDT0_PCLK, "wdt0_pclk", "ref32k", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 12, 0, }, + { HI6220_WDT1_PCLK, "wdt1_pclk", "ref32k", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 13, 0, }, + { HI6220_WDT2_PCLK, "wdt2_pclk", "ref32k", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 14, 0, }, { HI6220_TIMER0_PCLK, "timer0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 15, 0, }, { HI6220_TIMER1_PCLK, "timer1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 16, 0, }, { HI6220_TIMER2_PCLK, "timer2_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 17, 0, }, diff --git a/drivers/clk/imx/clk-imx51-imx53.c b/drivers/clk/imx/clk-imx51-imx53.c index 1e3c9ea5f9dc..7bcaf270db11 100644 --- a/drivers/clk/imx/clk-imx51-imx53.c +++ b/drivers/clk/imx/clk-imx51-imx53.c @@ -416,10 +416,10 @@ static void __init mx51_clocks_init(struct device_node *np) clk[IMX5_CLK_LP_APM] = imx_clk_mux("lp_apm", MXC_CCM_CCSR, 9, 1, lp_apm_sel, ARRAY_SIZE(lp_apm_sel)); - clk[IMX5_CLK_IPU_DI0_SEL] = imx_clk_mux("ipu_di0_sel", MXC_CCM_CSCMR2, 26, 3, - mx51_ipu_di0_sel, ARRAY_SIZE(mx51_ipu_di0_sel)); - clk[IMX5_CLK_IPU_DI1_SEL] = imx_clk_mux("ipu_di1_sel", MXC_CCM_CSCMR2, 29, 3, - mx51_ipu_di1_sel, ARRAY_SIZE(mx51_ipu_di1_sel)); + clk[IMX5_CLK_IPU_DI0_SEL] = imx_clk_mux_flags("ipu_di0_sel", MXC_CCM_CSCMR2, 26, 3, + mx51_ipu_di0_sel, ARRAY_SIZE(mx51_ipu_di0_sel), CLK_SET_RATE_PARENT); + clk[IMX5_CLK_IPU_DI1_SEL] = imx_clk_mux_flags("ipu_di1_sel", MXC_CCM_CSCMR2, 29, 3, + mx51_ipu_di1_sel, ARRAY_SIZE(mx51_ipu_di1_sel), CLK_SET_RATE_PARENT); clk[IMX5_CLK_TVE_EXT_SEL] = imx_clk_mux_flags("tve_ext_sel", MXC_CCM_CSCMR1, 6, 1, mx51_tve_ext_sel, ARRAY_SIZE(mx51_tve_ext_sel), CLK_SET_RATE_PARENT); clk[IMX5_CLK_TVE_SEL] = imx_clk_mux("tve_sel", MXC_CCM_CSCMR1, 7, 1, diff --git a/drivers/clk/imx/clk-imx6sl.c b/drivers/clk/imx/clk-imx6sl.c index 5fd4ddac1bf1..9642cdf0fb88 100644 --- a/drivers/clk/imx/clk-imx6sl.c +++ b/drivers/clk/imx/clk-imx6sl.c @@ -71,7 +71,7 @@ static const char *pll5_bypass_sels[] = { "pll5", "pll5_bypass_src", }; static const char *pll6_bypass_sels[] = { "pll6", "pll6_bypass_src", }; static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", }; -static struct clk_div_table clk_enet_ref_table[] = { +static const struct clk_div_table clk_enet_ref_table[] = { { .val = 0, .div = 20, }, { .val = 1, .div = 10, }, { .val = 2, .div = 5, }, @@ -79,14 +79,14 @@ static struct clk_div_table clk_enet_ref_table[] = { { } }; -static struct clk_div_table post_div_table[] = { +static const struct clk_div_table post_div_table[] = { { .val = 2, .div = 1, }, { .val = 1, .div = 2, }, { .val = 0, .div = 4, }, { } }; -static struct clk_div_table video_div_table[] = { +static const struct clk_div_table video_div_table[] = { { .val = 0, .div = 1, }, { .val = 1, .div = 2, }, { .val = 2, .div = 1, }, diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c index b5c96de41ccf..e6d389e333d7 100644 --- a/drivers/clk/imx/clk-imx6sx.c +++ b/drivers/clk/imx/clk-imx6sx.c @@ -105,7 +105,7 @@ static int const clks_init_on[] __initconst = { IMX6SX_CLK_EPIT2, }; -static struct clk_div_table clk_enet_ref_table[] = { +static const struct clk_div_table clk_enet_ref_table[] = { { .val = 0, .div = 20, }, { .val = 1, .div = 10, }, { .val = 2, .div = 5, }, @@ -113,14 +113,14 @@ static struct clk_div_table clk_enet_ref_table[] = { { } }; -static struct clk_div_table post_div_table[] = { +static const struct clk_div_table post_div_table[] = { { .val = 2, .div = 1, }, { .val = 1, .div = 2, }, { .val = 0, .div = 4, }, { } }; -static struct clk_div_table video_div_table[] = { +static const struct clk_div_table video_div_table[] = { { .val = 0, .div = 1, }, { .val = 1, .div = 2, }, { .val = 2, .div = 1, }, diff --git a/drivers/clk/imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c index b4e0dff3c8c2..5e8c18afce9a 100644 --- a/drivers/clk/imx/clk-imx6ul.c +++ b/drivers/clk/imx/clk-imx6ul.c @@ -78,7 +78,7 @@ static int const clks_init_on[] __initconst = { IMX6UL_CLK_MMDC_P0_FAST, IMX6UL_CLK_MMDC_P0_IPG, }; -static struct clk_div_table clk_enet_ref_table[] = { +static const struct clk_div_table clk_enet_ref_table[] = { { .val = 0, .div = 20, }, { .val = 1, .div = 10, }, { .val = 2, .div = 5, }, @@ -86,14 +86,14 @@ static struct clk_div_table clk_enet_ref_table[] = { { } }; -static struct clk_div_table post_div_table[] = { +static const struct clk_div_table post_div_table[] = { { .val = 2, .div = 1, }, { .val = 1, .div = 2, }, { .val = 0, .div = 4, }, { } }; -static struct clk_div_table video_div_table[] = { +static const struct clk_div_table video_div_table[] = { { .val = 0, .div = 1, }, { .val = 1, .div = 2, }, { .val = 2, .div = 1, }, diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c index 3da121826b1b..2305699db467 100644 --- a/drivers/clk/imx/clk-imx7d.c +++ b/drivers/clk/imx/clk-imx7d.c @@ -27,7 +27,7 @@ static u32 share_count_sai2; static u32 share_count_sai3; static u32 share_count_nand; -static struct clk_div_table test_div_table[] = { +static const struct clk_div_table test_div_table[] = { { .val = 3, .div = 1, }, { .val = 2, .div = 1, }, { .val = 1, .div = 2, }, @@ -35,7 +35,7 @@ static struct clk_div_table test_div_table[] = { { } }; -static struct clk_div_table post_div_table[] = { +static const struct clk_div_table post_div_table[] = { { .val = 3, .div = 4, }, { .val = 2, .div = 1, }, { .val = 1, .div = 2, }, diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c index 59b1863deb88..6dae54325a91 100644 --- a/drivers/clk/imx/clk-vf610.c +++ b/drivers/clk/imx/clk-vf610.c @@ -102,7 +102,7 @@ static const char *ftm_ext_sels[] = {"sirc_128k", "sxosc", "fxosc_half", "audio_ static const char *ftm_fix_sels[] = { "sxosc", "ipg_bus", }; -static struct clk_div_table pll4_audio_div_table[] = { +static const struct clk_div_table pll4_audio_div_table[] = { { .val = 0, .div = 1 }, { .val = 1, .div = 2 }, { .val = 2, .div = 6 }, diff --git a/drivers/clk/mediatek/clk-cpumux.c b/drivers/clk/mediatek/clk-cpumux.c index edd8e6918050..16e56772d280 100644 --- a/drivers/clk/mediatek/clk-cpumux.c +++ b/drivers/clk/mediatek/clk-cpumux.c @@ -27,7 +27,6 @@ static inline struct mtk_clk_cpumux *to_mtk_clk_cpumux(struct clk_hw *_hw) static u8 clk_cpumux_get_parent(struct clk_hw *hw) { struct mtk_clk_cpumux *mux = to_mtk_clk_cpumux(hw); - int num_parents = clk_hw_get_num_parents(hw); unsigned int val; regmap_read(mux->regmap, mux->reg, &val); @@ -35,9 +34,6 @@ static u8 clk_cpumux_get_parent(struct clk_hw *hw) val >>= mux->shift; val &= mux->mask; - if (val >= num_parents) - return -EINVAL; - return val; } @@ -98,7 +94,7 @@ int __init mtk_clk_register_cpumuxes(struct device_node *node, regmap = syscon_node_to_regmap(node); if (IS_ERR(regmap)) { - pr_err("Cannot find regmap for %s: %ld\n", node->full_name, + pr_err("Cannot find regmap for %pOF: %ld\n", node, PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c index 0541df78141c..9c0ae4278a94 100644 --- a/drivers/clk/mediatek/clk-mtk.c +++ b/drivers/clk/mediatek/clk-mtk.c @@ -114,7 +114,7 @@ int mtk_clk_register_gates(struct device_node *node, regmap = syscon_node_to_regmap(node); if (IS_ERR(regmap)) { - pr_err("Cannot find regmap for %s: %ld\n", node->full_name, + pr_err("Cannot find regmap for %pOF: %ld\n", node, PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/clk/mediatek/reset.c b/drivers/clk/mediatek/reset.c index 309049d41f1b..d3551d5efef2 100644 --- a/drivers/clk/mediatek/reset.c +++ b/drivers/clk/mediatek/reset.c @@ -72,7 +72,7 @@ void mtk_register_reset_controller(struct device_node *np, regmap = syscon_node_to_regmap(np); if (IS_ERR(regmap)) { - pr_err("Cannot find regmap for %s: %ld\n", np->full_name, + pr_err("Cannot find regmap for %pOF: %ld\n", np, PTR_ERR(regmap)); return; } diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig index 5588f75a8414..d2d0174a6eca 100644 --- a/drivers/clk/meson/Kconfig +++ b/drivers/clk/meson/Kconfig @@ -6,6 +6,7 @@ config COMMON_CLK_AMLOGIC config COMMON_CLK_MESON8B bool depends on COMMON_CLK_AMLOGIC + select RESET_CONTROLLER help Support for the clock controller on AmLogic S802 (Meson8), S805 (Meson8b) and S812 (Meson8m2) devices. Say Y if you diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index 83b6d9d65aa1..b139d41b25da 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -4,4 +4,4 @@ obj-$(CONFIG_COMMON_CLK_AMLOGIC) += clk-pll.o clk-cpu.o clk-mpll.o clk-audio-divider.o obj-$(CONFIG_COMMON_CLK_MESON8B) += meson8b.o -obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o +obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o gxbb-aoclk-regmap.o gxbb-aoclk-32k.o diff --git a/drivers/clk/meson/gxbb-aoclk-32k.c b/drivers/clk/meson/gxbb-aoclk-32k.c new file mode 100644 index 000000000000..491634dbc985 --- /dev/null +++ b/drivers/clk/meson/gxbb-aoclk-32k.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2017 BayLibre, SAS. + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <linux/clk-provider.h> +#include <linux/bitfield.h> +#include <linux/regmap.h> +#include "gxbb-aoclk.h" + +/* + * The AO Domain embeds a dual/divider to generate a more precise + * 32,768KHz clock for low-power suspend mode and CEC. + * ______ ______ + * | | | | + * ______ | Div1 |-| Cnt1 | ______ + * | | /|______| |______|\ | | + * Xtal-->| Gate |---| ______ ______ X-X--| Gate |--> + * |______| | \| | | |/ | |______| + * | | Div2 |-| Cnt2 | | + * | |______| |______| | + * |_______________________| + * + * The dividing can be switched to single or dual, with a counter + * for each divider to set when the switching is done. + * The entire dividing mechanism can be also bypassed. + */ + +#define CLK_CNTL0_N1_MASK GENMASK(11, 0) +#define CLK_CNTL0_N2_MASK GENMASK(23, 12) +#define CLK_CNTL0_DUALDIV_EN BIT(28) +#define CLK_CNTL0_OUT_GATE_EN BIT(30) +#define CLK_CNTL0_IN_GATE_EN BIT(31) + +#define CLK_CNTL1_M1_MASK GENMASK(11, 0) +#define CLK_CNTL1_M2_MASK GENMASK(23, 12) +#define CLK_CNTL1_BYPASS_EN BIT(24) +#define CLK_CNTL1_SELECT_OSC BIT(27) + +#define PWR_CNTL_ALT_32K_SEL GENMASK(13, 10) + +struct cec_32k_freq_table { + unsigned long parent_rate; + unsigned long target_rate; + bool dualdiv; + unsigned int n1; + unsigned int n2; + unsigned int m1; + unsigned int m2; +}; + +static const struct cec_32k_freq_table aoclk_cec_32k_table[] = { + [0] = { + .parent_rate = 24000000, + .target_rate = 32768, + .dualdiv = true, + .n1 = 733, + .n2 = 732, + .m1 = 8, + .m2 = 11, + }, +}; + +/* + * If CLK_CNTL0_DUALDIV_EN == 0 + * - will use N1 divider only + * If CLK_CNTL0_DUALDIV_EN == 1 + * - hold M1 cycles of N1 divider then changes to N2 + * - hold M2 cycles of N2 divider then changes to N1 + * Then we can get more accurate division. + */ +static unsigned long aoclk_cec_32k_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw); + unsigned long n1; + u32 reg0, reg1; + + regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, ®0); + regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, ®1); + + if (reg1 & CLK_CNTL1_BYPASS_EN) + return parent_rate; + + if (reg0 & CLK_CNTL0_DUALDIV_EN) { + unsigned long n2, m1, m2, f1, f2, p1, p2; + + n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1; + n2 = FIELD_GET(CLK_CNTL0_N2_MASK, reg0) + 1; + + m1 = FIELD_GET(CLK_CNTL1_M1_MASK, reg1) + 1; + m2 = FIELD_GET(CLK_CNTL1_M2_MASK, reg1) + 1; + + f1 = DIV_ROUND_CLOSEST(parent_rate, n1); + f2 = DIV_ROUND_CLOSEST(parent_rate, n2); + + p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2)); + p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2)); + + return DIV_ROUND_UP(100000000, p1 + p2); + } + + n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1; + + return DIV_ROUND_CLOSEST(parent_rate, n1); +} + +static const struct cec_32k_freq_table *find_cec_32k_freq(unsigned long rate, + unsigned long prate) +{ + int i; + + for (i = 0 ; i < ARRAY_SIZE(aoclk_cec_32k_table) ; ++i) + if (aoclk_cec_32k_table[i].parent_rate == prate && + aoclk_cec_32k_table[i].target_rate == rate) + return &aoclk_cec_32k_table[i]; + + return NULL; +} + +static long aoclk_cec_32k_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate, + *prate); + + /* If invalid return first one */ + if (!freq) + return aoclk_cec_32k_table[0].target_rate; + + return freq->target_rate; +} + +/* + * From the Amlogic init procedure, the IN and OUT gates needs to be handled + * in the init procedure to avoid any glitches. + */ + +static int aoclk_cec_32k_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate, + parent_rate); + struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw); + u32 reg = 0; + + if (!freq) + return -EINVAL; + + /* Disable clock */ + regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, + CLK_CNTL0_IN_GATE_EN | CLK_CNTL0_OUT_GATE_EN, 0); + + reg = FIELD_PREP(CLK_CNTL0_N1_MASK, freq->n1 - 1); + if (freq->dualdiv) + reg |= CLK_CNTL0_DUALDIV_EN | + FIELD_PREP(CLK_CNTL0_N2_MASK, freq->n2 - 1); + + regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, reg); + + reg = FIELD_PREP(CLK_CNTL1_M1_MASK, freq->m1 - 1); + if (freq->dualdiv) + reg |= FIELD_PREP(CLK_CNTL1_M2_MASK, freq->m2 - 1); + + regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, reg); + + /* Enable clock */ + regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, + CLK_CNTL0_IN_GATE_EN, CLK_CNTL0_IN_GATE_EN); + + udelay(200); + + regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, + CLK_CNTL0_OUT_GATE_EN, CLK_CNTL0_OUT_GATE_EN); + + regmap_update_bits(cec_32k->regmap, AO_CRT_CLK_CNTL1, + CLK_CNTL1_SELECT_OSC, CLK_CNTL1_SELECT_OSC); + + /* Select 32k from XTAL */ + regmap_update_bits(cec_32k->regmap, + AO_RTI_PWR_CNTL_REG0, + PWR_CNTL_ALT_32K_SEL, + FIELD_PREP(PWR_CNTL_ALT_32K_SEL, 4)); + + return 0; +} + +const struct clk_ops meson_aoclk_cec_32k_ops = { + .recalc_rate = aoclk_cec_32k_recalc_rate, + .round_rate = aoclk_cec_32k_round_rate, + .set_rate = aoclk_cec_32k_set_rate, +}; diff --git a/drivers/clk/meson/gxbb-aoclk-regmap.c b/drivers/clk/meson/gxbb-aoclk-regmap.c new file mode 100644 index 000000000000..2515fbfa0467 --- /dev/null +++ b/drivers/clk/meson/gxbb-aoclk-regmap.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 BayLibre, SAS. + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <linux/clk-provider.h> +#include <linux/bitfield.h> +#include <linux/regmap.h> +#include "gxbb-aoclk.h" + +static int aoclk_gate_regmap_enable(struct clk_hw *hw) +{ + struct aoclk_gate_regmap *gate = to_aoclk_gate_regmap(hw); + + return regmap_update_bits(gate->regmap, AO_RTI_GEN_CNTL_REG0, + BIT(gate->bit_idx), BIT(gate->bit_idx)); +} + +static void aoclk_gate_regmap_disable(struct clk_hw *hw) +{ + struct aoclk_gate_regmap *gate = to_aoclk_gate_regmap(hw); + + regmap_update_bits(gate->regmap, AO_RTI_GEN_CNTL_REG0, + BIT(gate->bit_idx), 0); +} + +static int aoclk_gate_regmap_is_enabled(struct clk_hw *hw) +{ + struct aoclk_gate_regmap *gate = to_aoclk_gate_regmap(hw); + unsigned int val; + int ret; + + ret = regmap_read(gate->regmap, AO_RTI_GEN_CNTL_REG0, &val); + if (ret) + return ret; + + return (val & BIT(gate->bit_idx)) != 0; +} + +const struct clk_ops meson_aoclk_gate_regmap_ops = { + .enable = aoclk_gate_regmap_enable, + .disable = aoclk_gate_regmap_disable, + .is_enabled = aoclk_gate_regmap_is_enabled, +}; diff --git a/drivers/clk/meson/gxbb-aoclk.c b/drivers/clk/meson/gxbb-aoclk.c index b45c5fba7e35..6c161e0a8e59 100644 --- a/drivers/clk/meson/gxbb-aoclk.c +++ b/drivers/clk/meson/gxbb-aoclk.c @@ -56,16 +56,20 @@ #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/reset-controller.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> #include <linux/init.h> +#include <linux/delay.h> #include <dt-bindings/clock/gxbb-aoclkc.h> #include <dt-bindings/reset/gxbb-aoclkc.h> +#include "gxbb-aoclk.h" static DEFINE_SPINLOCK(gxbb_aoclk_lock); struct gxbb_aoclk_reset_controller { struct reset_controller_dev reset; unsigned int *data; - void __iomem *base; + struct regmap *regmap; }; static int gxbb_aoclk_do_reset(struct reset_controller_dev *rcdev, @@ -74,9 +78,8 @@ static int gxbb_aoclk_do_reset(struct reset_controller_dev *rcdev, struct gxbb_aoclk_reset_controller *reset = container_of(rcdev, struct gxbb_aoclk_reset_controller, reset); - writel(BIT(reset->data[id]), reset->base); - - return 0; + return regmap_write(reset->regmap, AO_RTI_GEN_CNTL_REG0, + BIT(reset->data[id])); } static const struct reset_control_ops gxbb_aoclk_reset_ops = { @@ -84,13 +87,12 @@ static const struct reset_control_ops gxbb_aoclk_reset_ops = { }; #define GXBB_AO_GATE(_name, _bit) \ -static struct clk_gate _name##_ao = { \ - .reg = (void __iomem *)0, \ +static struct aoclk_gate_regmap _name##_ao = { \ .bit_idx = (_bit), \ .lock = &gxbb_aoclk_lock, \ .hw.init = &(struct clk_init_data) { \ .name = #_name "_ao", \ - .ops = &clk_gate_ops, \ + .ops = &meson_aoclk_gate_regmap_ops, \ .parent_names = (const char *[]){ "clk81" }, \ .num_parents = 1, \ .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \ @@ -104,6 +106,17 @@ GXBB_AO_GATE(uart1, 3); GXBB_AO_GATE(uart2, 5); GXBB_AO_GATE(ir_blaster, 6); +static struct aoclk_cec_32k cec_32k_ao = { + .lock = &gxbb_aoclk_lock, + .hw.init = &(struct clk_init_data) { + .name = "cec_32k_ao", + .ops = &meson_aoclk_cec_32k_ops, + .parent_names = (const char *[]){ "xtal" }, + .num_parents = 1, + .flags = CLK_IGNORE_UNUSED, + }, +}; + static unsigned int gxbb_aoclk_reset[] = { [RESET_AO_REMOTE] = 16, [RESET_AO_I2C_MASTER] = 18, @@ -113,7 +126,7 @@ static unsigned int gxbb_aoclk_reset[] = { [RESET_AO_IR_BLASTER] = 23, }; -static struct clk_gate *gxbb_aoclk_gate[] = { +static struct aoclk_gate_regmap *gxbb_aoclk_gate[] = { [CLKID_AO_REMOTE] = &remote_ao, [CLKID_AO_I2C_MASTER] = &i2c_master_ao, [CLKID_AO_I2C_SLAVE] = &i2c_slave_ao, @@ -130,30 +143,30 @@ static struct clk_hw_onecell_data gxbb_aoclk_onecell_data = { [CLKID_AO_UART1] = &uart1_ao.hw, [CLKID_AO_UART2] = &uart2_ao.hw, [CLKID_AO_IR_BLASTER] = &ir_blaster_ao.hw, + [CLKID_AO_CEC_32K] = &cec_32k_ao.hw, }, - .num = ARRAY_SIZE(gxbb_aoclk_gate), + .num = 7, }; static int gxbb_aoclkc_probe(struct platform_device *pdev) { - struct resource *res; - void __iomem *base; - int ret, clkid; - struct device *dev = &pdev->dev; struct gxbb_aoclk_reset_controller *rstc; + struct device *dev = &pdev->dev; + struct regmap *regmap; + int ret, clkid; rstc = devm_kzalloc(dev, sizeof(*rstc), GFP_KERNEL); if (!rstc) return -ENOMEM; - /* Generic clocks */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); + regmap = syscon_node_to_regmap(of_get_parent(dev->of_node)); + if (IS_ERR(regmap)) { + dev_err(dev, "failed to get regmap\n"); + return -ENODEV; + } /* Reset Controller */ - rstc->base = base; + rstc->regmap = regmap; rstc->data = gxbb_aoclk_reset; rstc->reset.ops = &gxbb_aoclk_reset_ops; rstc->reset.nr_resets = ARRAY_SIZE(gxbb_aoclk_reset); @@ -161,10 +174,10 @@ static int gxbb_aoclkc_probe(struct platform_device *pdev) ret = devm_reset_controller_register(dev, &rstc->reset); /* - * Populate base address and register all clks + * Populate regmap and register all clks */ - for (clkid = 0; clkid < gxbb_aoclk_onecell_data.num; clkid++) { - gxbb_aoclk_gate[clkid]->reg = base; + for (clkid = 0; clkid < ARRAY_SIZE(gxbb_aoclk_gate); clkid++) { + gxbb_aoclk_gate[clkid]->regmap = regmap; ret = devm_clk_hw_register(dev, gxbb_aoclk_onecell_data.hws[clkid]); @@ -172,12 +185,18 @@ static int gxbb_aoclkc_probe(struct platform_device *pdev) return ret; } + /* Specific clocks */ + cec_32k_ao.regmap = regmap; + ret = devm_clk_hw_register(dev, &cec_32k_ao.hw); + if (ret) + return ret; + return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, &gxbb_aoclk_onecell_data); } static const struct of_device_id gxbb_aoclkc_match_table[] = { - { .compatible = "amlogic,gxbb-aoclkc" }, + { .compatible = "amlogic,meson-gx-aoclkc" }, { } }; diff --git a/drivers/clk/meson/gxbb-aoclk.h b/drivers/clk/meson/gxbb-aoclk.h new file mode 100644 index 000000000000..e8604c8f7eee --- /dev/null +++ b/drivers/clk/meson/gxbb-aoclk.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __GXBB_AOCLKC_H +#define __GXBB_AOCLKC_H + +/* AO Configuration Clock registers offsets */ +#define AO_RTI_PWR_CNTL_REG1 0x0c +#define AO_RTI_PWR_CNTL_REG0 0x10 +#define AO_RTI_GEN_CNTL_REG0 0x40 +#define AO_OSCIN_CNTL 0x58 +#define AO_CRT_CLK_CNTL1 0x68 +#define AO_RTC_ALT_CLK_CNTL0 0x94 +#define AO_RTC_ALT_CLK_CNTL1 0x98 + +struct aoclk_gate_regmap { + struct clk_hw hw; + unsigned bit_idx; + struct regmap *regmap; + spinlock_t *lock; +}; + +#define to_aoclk_gate_regmap(_hw) \ + container_of(_hw, struct aoclk_gate_regmap, hw) + +extern const struct clk_ops meson_aoclk_gate_regmap_ops; + +struct aoclk_cec_32k { + struct clk_hw hw; + struct regmap *regmap; + spinlock_t *lock; +}; + +#define to_aoclk_cec_32k(_hw) container_of(_hw, struct aoclk_cec_32k, hw) + +extern const struct clk_ops meson_aoclk_cec_32k_ops; + +#endif /* __GXBB_AOCLKC_H */ diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index 964489b39f6a..b2d1e8ed7152 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -850,13 +850,14 @@ static struct meson_clk_audio_divider gxbb_cts_amclk_div = { .shift = 0, .width = 8, }, + .flags = CLK_DIVIDER_ROUND_CLOSEST, .lock = &clk_lock, .hw.init = &(struct clk_init_data){ .name = "cts_amclk_div", .ops = &meson_clk_audio_divider_ops, .parent_names = (const char *[]){ "cts_amclk_sel" }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT | CLK_DIVIDER_ROUND_CLOSEST, + .flags = CLK_SET_RATE_PARENT, }, }; @@ -880,7 +881,7 @@ static struct clk_mux gxbb_cts_mclk_i958_sel = { /* Default parent unknown (register reset value: 0) */ .table = (u32[]){ 1, 2, 3 }, .lock = &clk_lock, - .hw.init = &(struct clk_init_data){ + .hw.init = &(struct clk_init_data) { .name = "cts_mclk_i958_sel", .ops = &clk_mux_ops, .parent_names = (const char *[]){ "mpll0", "mpll1", "mpll2" }, @@ -894,12 +895,13 @@ static struct clk_divider gxbb_cts_mclk_i958_div = { .shift = 16, .width = 8, .lock = &clk_lock, - .hw.init = &(struct clk_init_data){ + .flags = CLK_DIVIDER_ROUND_CLOSEST, + .hw.init = &(struct clk_init_data) { .name = "cts_mclk_i958_div", .ops = &clk_divider_ops, .parent_names = (const char *[]){ "cts_mclk_i958_sel" }, .num_parents = 1, - .flags = CLK_SET_RATE_PARENT | CLK_DIVIDER_ROUND_CLOSEST, + .flags = CLK_SET_RATE_PARENT, }, }; @@ -979,6 +981,156 @@ static struct clk_mux gxbb_32k_clk_sel = { }, }; +static const char * const gxbb_sd_emmc_clk0_parent_names[] = { + "xtal", "fclk_div2", "fclk_div3", "fclk_div5", "fclk_div7", + + /* + * Following these parent clocks, we should also have had mpll2, mpll3 + * and gp0_pll but these clocks are too precious to be used here. All + * the necessary rates for MMC and NAND operation can be acheived using + * xtal or fclk_div clocks + */ +}; + +/* SDIO clock */ +static struct clk_mux gxbb_sd_emmc_a_clk0_sel = { + .reg = (void *)HHI_SD_EMMC_CLK_CNTL, + .mask = 0x7, + .shift = 9, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_a_clk0_sel", + .ops = &clk_mux_ops, + .parent_names = gxbb_sd_emmc_clk0_parent_names, + .num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parent_names), + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_divider gxbb_sd_emmc_a_clk0_div = { + .reg = (void *)HHI_SD_EMMC_CLK_CNTL, + .shift = 0, + .width = 7, + .lock = &clk_lock, + .flags = CLK_DIVIDER_ROUND_CLOSEST, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_a_clk0_div", + .ops = &clk_divider_ops, + .parent_names = (const char *[]){ "sd_emmc_a_clk0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_gate gxbb_sd_emmc_a_clk0 = { + .reg = (void *)HHI_SD_EMMC_CLK_CNTL, + .bit_idx = 7, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "sd_emmc_a_clk0", + .ops = &clk_gate_ops, + .parent_names = (const char *[]){ "sd_emmc_a_clk0_div" }, + .num_parents = 1, + + /* + * FIXME: + * We need CLK_IGNORE_UNUSED because mmc DT node point to xtal + * instead of this clock. CCF would gate this on boot, killing + * the mmc controller. Please remove this flag once DT properly + * point to this clock instead of xtal + * + * Same goes for emmc B and C clocks + */ + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +/* SDcard clock */ +static struct clk_mux gxbb_sd_emmc_b_clk0_sel = { + .reg = (void *)HHI_SD_EMMC_CLK_CNTL, + .mask = 0x7, + .shift = 25, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_b_clk0_sel", + .ops = &clk_mux_ops, + .parent_names = gxbb_sd_emmc_clk0_parent_names, + .num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parent_names), + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_divider gxbb_sd_emmc_b_clk0_div = { + .reg = (void *)HHI_SD_EMMC_CLK_CNTL, + .shift = 16, + .width = 7, + .lock = &clk_lock, + .flags = CLK_DIVIDER_ROUND_CLOSEST, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_b_clk0_div", + .ops = &clk_divider_ops, + .parent_names = (const char *[]){ "sd_emmc_b_clk0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_gate gxbb_sd_emmc_b_clk0 = { + .reg = (void *)HHI_SD_EMMC_CLK_CNTL, + .bit_idx = 23, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "sd_emmc_b_clk0", + .ops = &clk_gate_ops, + .parent_names = (const char *[]){ "sd_emmc_b_clk0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +/* EMMC/NAND clock */ +static struct clk_mux gxbb_sd_emmc_c_clk0_sel = { + .reg = (void *)HHI_NAND_CLK_CNTL, + .mask = 0x7, + .shift = 9, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_c_clk0_sel", + .ops = &clk_mux_ops, + .parent_names = gxbb_sd_emmc_clk0_parent_names, + .num_parents = ARRAY_SIZE(gxbb_sd_emmc_clk0_parent_names), + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_divider gxbb_sd_emmc_c_clk0_div = { + .reg = (void *)HHI_NAND_CLK_CNTL, + .shift = 0, + .width = 7, + .lock = &clk_lock, + .flags = CLK_DIVIDER_ROUND_CLOSEST, + .hw.init = &(struct clk_init_data) { + .name = "sd_emmc_c_clk0_div", + .ops = &clk_divider_ops, + .parent_names = (const char *[]){ "sd_emmc_c_clk0_sel" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_gate gxbb_sd_emmc_c_clk0 = { + .reg = (void *)HHI_NAND_CLK_CNTL, + .bit_idx = 7, + .lock = &clk_lock, + .hw.init = &(struct clk_init_data){ + .name = "sd_emmc_c_clk0", + .ops = &clk_gate_ops, + .parent_names = (const char *[]){ "sd_emmc_c_clk0_div" }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + /* Everything Else (EE) domain gates */ static MESON_GATE(gxbb_ddr, HHI_GCLK_MPEG0, 0); static MESON_GATE(gxbb_dos, HHI_GCLK_MPEG0, 1); @@ -1188,6 +1340,15 @@ static struct clk_hw_onecell_data gxbb_hw_onecell_data = { [CLKID_32K_CLK] = &gxbb_32k_clk.hw, [CLKID_32K_CLK_SEL] = &gxbb_32k_clk_sel.hw, [CLKID_32K_CLK_DIV] = &gxbb_32k_clk_div.hw, + [CLKID_SD_EMMC_A_CLK0_SEL] = &gxbb_sd_emmc_a_clk0_sel.hw, + [CLKID_SD_EMMC_A_CLK0_DIV] = &gxbb_sd_emmc_a_clk0_div.hw, + [CLKID_SD_EMMC_A_CLK0] = &gxbb_sd_emmc_a_clk0.hw, + [CLKID_SD_EMMC_B_CLK0_SEL] = &gxbb_sd_emmc_b_clk0_sel.hw, + [CLKID_SD_EMMC_B_CLK0_DIV] = &gxbb_sd_emmc_b_clk0_div.hw, + [CLKID_SD_EMMC_B_CLK0] = &gxbb_sd_emmc_b_clk0.hw, + [CLKID_SD_EMMC_C_CLK0_SEL] = &gxbb_sd_emmc_c_clk0_sel.hw, + [CLKID_SD_EMMC_C_CLK0_DIV] = &gxbb_sd_emmc_c_clk0_div.hw, + [CLKID_SD_EMMC_C_CLK0] = &gxbb_sd_emmc_c_clk0.hw, [NR_CLKS] = NULL, }, .num = NR_CLKS, @@ -1311,6 +1472,15 @@ static struct clk_hw_onecell_data gxl_hw_onecell_data = { [CLKID_32K_CLK] = &gxbb_32k_clk.hw, [CLKID_32K_CLK_SEL] = &gxbb_32k_clk_sel.hw, [CLKID_32K_CLK_DIV] = &gxbb_32k_clk_div.hw, + [CLKID_SD_EMMC_A_CLK0_SEL] = &gxbb_sd_emmc_a_clk0_sel.hw, + [CLKID_SD_EMMC_A_CLK0_DIV] = &gxbb_sd_emmc_a_clk0_div.hw, + [CLKID_SD_EMMC_A_CLK0] = &gxbb_sd_emmc_a_clk0.hw, + [CLKID_SD_EMMC_B_CLK0_SEL] = &gxbb_sd_emmc_b_clk0_sel.hw, + [CLKID_SD_EMMC_B_CLK0_DIV] = &gxbb_sd_emmc_b_clk0_div.hw, + [CLKID_SD_EMMC_B_CLK0] = &gxbb_sd_emmc_b_clk0.hw, + [CLKID_SD_EMMC_C_CLK0_SEL] = &gxbb_sd_emmc_c_clk0_sel.hw, + [CLKID_SD_EMMC_C_CLK0_DIV] = &gxbb_sd_emmc_c_clk0_div.hw, + [CLKID_SD_EMMC_C_CLK0] = &gxbb_sd_emmc_c_clk0.hw, [NR_CLKS] = NULL, }, .num = NR_CLKS, @@ -1427,6 +1597,9 @@ static struct clk_gate *const gxbb_clk_gates[] = { &gxbb_cts_amclk, &gxbb_cts_mclk_i958, &gxbb_32k_clk, + &gxbb_sd_emmc_a_clk0, + &gxbb_sd_emmc_b_clk0, + &gxbb_sd_emmc_c_clk0, }; static struct clk_mux *const gxbb_clk_muxes[] = { @@ -1439,6 +1612,9 @@ static struct clk_mux *const gxbb_clk_muxes[] = { &gxbb_cts_mclk_i958_sel, &gxbb_cts_i958, &gxbb_32k_clk_sel, + &gxbb_sd_emmc_a_clk0_sel, + &gxbb_sd_emmc_b_clk0_sel, + &gxbb_sd_emmc_c_clk0_sel, }; static struct clk_divider *const gxbb_clk_dividers[] = { @@ -1448,6 +1624,9 @@ static struct clk_divider *const gxbb_clk_dividers[] = { &gxbb_mali_1_div, &gxbb_cts_mclk_i958_div, &gxbb_32k_clk_div, + &gxbb_sd_emmc_a_clk0_div, + &gxbb_sd_emmc_b_clk0_div, + &gxbb_sd_emmc_c_clk0_div, }; static struct meson_clk_audio_divider *const gxbb_audio_dividers[] = { diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index cb60a516ca82..20ab7190d328 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -25,6 +25,8 @@ #include <linux/clk-provider.h> #include <linux/of_address.h> #include <linux/platform_device.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> #include <linux/init.h> #include "clkc.h" @@ -32,6 +34,13 @@ static DEFINE_SPINLOCK(clk_lock); +static void __iomem *clk_base; + +struct meson8b_clk_reset { + struct reset_controller_dev reset; + void __iomem *base; +}; + static const struct pll_rate_table sys_pll_rate_table[] = { PLL_RATE(312000000, 52, 1, 2), PLL_RATE(336000000, 56, 1, 2), @@ -696,20 +705,114 @@ static struct clk_divider *const meson8b_clk_dividers[] = { &meson8b_mpeg_clk_div, }; +static const struct meson8b_clk_reset_line { + u32 reg; + u8 bit_idx; +} meson8b_clk_reset_bits[] = { + [CLKC_RESET_L2_CACHE_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 30 + }, + [CLKC_RESET_AXI_64_TO_128_BRIDGE_A5_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 29 + }, + [CLKC_RESET_SCU_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 28 + }, + [CLKC_RESET_CPU3_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 27 + }, + [CLKC_RESET_CPU2_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 26 + }, + [CLKC_RESET_CPU1_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 25 + }, + [CLKC_RESET_CPU0_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 24 + }, + [CLKC_RESET_A5_GLOBAL_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 18 + }, + [CLKC_RESET_A5_AXI_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 17 + }, + [CLKC_RESET_A5_ABP_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL0, .bit_idx = 16 + }, + [CLKC_RESET_AXI_64_TO_128_BRIDGE_MMC_SOFT_RESET] = { + .reg = HHI_SYS_CPU_CLK_CNTL1, .bit_idx = 30 + }, + [CLKC_RESET_VID_CLK_CNTL_SOFT_RESET] = { + .reg = HHI_VID_CLK_CNTL, .bit_idx = 15 + }, + [CLKC_RESET_VID_DIVIDER_CNTL_SOFT_RESET_POST] = { + .reg = HHI_VID_DIVIDER_CNTL, .bit_idx = 7 + }, + [CLKC_RESET_VID_DIVIDER_CNTL_SOFT_RESET_PRE] = { + .reg = HHI_VID_DIVIDER_CNTL, .bit_idx = 3 + }, + [CLKC_RESET_VID_DIVIDER_CNTL_RESET_N_POST] = { + .reg = HHI_VID_DIVIDER_CNTL, .bit_idx = 1 + }, + [CLKC_RESET_VID_DIVIDER_CNTL_RESET_N_PRE] = { + .reg = HHI_VID_DIVIDER_CNTL, .bit_idx = 0 + }, +}; + +static int meson8b_clk_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct meson8b_clk_reset *meson8b_clk_reset = + container_of(rcdev, struct meson8b_clk_reset, reset); + unsigned long flags; + const struct meson8b_clk_reset_line *reset; + u32 val; + + if (id >= ARRAY_SIZE(meson8b_clk_reset_bits)) + return -EINVAL; + + reset = &meson8b_clk_reset_bits[id]; + + spin_lock_irqsave(&clk_lock, flags); + + val = readl(meson8b_clk_reset->base + reset->reg); + if (assert) + val |= BIT(reset->bit_idx); + else + val &= ~BIT(reset->bit_idx); + writel(val, meson8b_clk_reset->base + reset->reg); + + spin_unlock_irqrestore(&clk_lock, flags); + + return 0; +} + +static int meson8b_clk_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return meson8b_clk_reset_update(rcdev, id, true); +} + +static int meson8b_clk_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return meson8b_clk_reset_update(rcdev, id, false); +} + +static const struct reset_control_ops meson8b_clk_reset_ops = { + .assert = meson8b_clk_reset_assert, + .deassert = meson8b_clk_reset_deassert, +}; + static int meson8b_clkc_probe(struct platform_device *pdev) { - void __iomem *clk_base; int ret, clkid, i; struct clk_hw *parent_hw; struct clk *parent_clk; struct device *dev = &pdev->dev; - /* Generic clocks and PLLs */ - clk_base = of_iomap(dev->of_node, 1); - if (!clk_base) { - pr_err("%s: Unable to map clk base\n", __func__); + if (!clk_base) return -ENXIO; - } /* Populate base address for PLLs */ for (i = 0; i < ARRAY_SIZE(meson8b_clk_plls); i++) @@ -749,7 +852,7 @@ static int meson8b_clkc_probe(struct platform_device *pdev) /* FIXME convert to devm_clk_register */ ret = devm_clk_hw_register(dev, meson8b_hw_onecell_data.hws[clkid]); if (ret) - goto iounmap; + return ret; } /* @@ -772,15 +875,11 @@ static int meson8b_clkc_probe(struct platform_device *pdev) if (ret) { pr_err("%s: failed to register clock notifier for cpu_clk\n", __func__); - goto iounmap; + return ret; } return of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, &meson8b_hw_onecell_data); - -iounmap: - iounmap(clk_base); - return ret; } static const struct of_device_id meson8b_clkc_match_table[] = { @@ -799,3 +898,39 @@ static struct platform_driver meson8b_driver = { }; builtin_platform_driver(meson8b_driver); + +static void __init meson8b_clkc_reset_init(struct device_node *np) +{ + struct meson8b_clk_reset *rstc; + int ret; + + /* Generic clocks, PLLs and some of the reset-bits */ + clk_base = of_iomap(np, 1); + if (!clk_base) { + pr_err("%s: Unable to map clk base\n", __func__); + return; + } + + rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); + if (!rstc) + return; + + /* Reset Controller */ + rstc->base = clk_base; + rstc->reset.ops = &meson8b_clk_reset_ops; + rstc->reset.nr_resets = ARRAY_SIZE(meson8b_clk_reset_bits); + rstc->reset.of_node = np; + ret = reset_controller_register(&rstc->reset); + if (ret) { + pr_err("%s: Failed to register clkc reset controller: %d\n", + __func__, ret); + return; + } +} + +CLK_OF_DECLARE_DRIVER(meson8_clkc, "amlogic,meson8-clkc", + meson8b_clkc_reset_init); +CLK_OF_DECLARE_DRIVER(meson8b_clkc, "amlogic,meson8b-clkc", + meson8b_clkc_reset_init); +CLK_OF_DECLARE_DRIVER(meson8m2_clkc, "amlogic,meson8m2-clkc", + meson8b_clkc_reset_init); diff --git a/drivers/clk/meson/meson8b.h b/drivers/clk/meson/meson8b.h index c139bb3273ca..2eaf8a52e7dd 100644 --- a/drivers/clk/meson/meson8b.h +++ b/drivers/clk/meson/meson8b.h @@ -37,6 +37,9 @@ #define HHI_GCLK_AO 0x154 /* 0x55 offset in data sheet */ #define HHI_SYS_CPU_CLK_CNTL1 0x15c /* 0x57 offset in data sheet */ #define HHI_MPEG_CLK_CNTL 0x174 /* 0x5d offset in data sheet */ +#define HHI_VID_CLK_CNTL 0x17c /* 0x5f offset in data sheet */ +#define HHI_VID_DIVIDER_CNTL 0x198 /* 0x66 offset in data sheet */ +#define HHI_SYS_CPU_CLK_CNTL0 0x19c /* 0x67 offset in data sheet */ #define HHI_MPLL_CNTL 0x280 /* 0xa0 offset in data sheet */ #define HHI_SYS_PLL_CNTL 0x300 /* 0xc0 offset in data sheet */ #define HHI_VID_PLL_CNTL 0x320 /* 0xc8 offset in data sheet */ @@ -68,7 +71,11 @@ #define CLK_NR_CLKS 96 -/* include the CLKIDs that have been made part of the stable DT binding */ +/* + * include the CLKID and RESETID that have + * been made part of the stable DT binding + */ #include <dt-bindings/clock/meson8b-clkc.h> +#include <dt-bindings/reset/amlogic,meson8b-clkc-reset.h> #endif /* __MESON8B_H */ diff --git a/drivers/clk/mmp/clk.c b/drivers/clk/mmp/clk.c index 61893fe73251..089927e4cda2 100644 --- a/drivers/clk/mmp/clk.c +++ b/drivers/clk/mmp/clk.c @@ -9,7 +9,7 @@ void mmp_clk_init(struct device_node *np, struct mmp_clk_unit *unit, int nr_clks) { - static struct clk **clk_table; + struct clk **clk_table; clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL); if (!clk_table) diff --git a/drivers/clk/nxp/clk-lpc32xx.c b/drivers/clk/nxp/clk-lpc32xx.c index 5b98ff9076f3..7b359afd620e 100644 --- a/drivers/clk/nxp/clk-lpc32xx.c +++ b/drivers/clk/nxp/clk-lpc32xx.c @@ -885,7 +885,7 @@ static const struct clk_ops clk_usb_i2c_ops = { .recalc_rate = clk_usb_i2c_recalc_rate, }; -static int clk_gate_enable(struct clk_hw *hw) +static int lpc32xx_clk_gate_enable(struct clk_hw *hw) { struct lpc32xx_clk_gate *clk = to_lpc32xx_gate(hw); u32 mask = BIT(clk->bit_idx); @@ -894,7 +894,7 @@ static int clk_gate_enable(struct clk_hw *hw) return regmap_update_bits(clk_regmap, clk->reg, mask, val); } -static void clk_gate_disable(struct clk_hw *hw) +static void lpc32xx_clk_gate_disable(struct clk_hw *hw) { struct lpc32xx_clk_gate *clk = to_lpc32xx_gate(hw); u32 mask = BIT(clk->bit_idx); @@ -903,7 +903,7 @@ static void clk_gate_disable(struct clk_hw *hw) regmap_update_bits(clk_regmap, clk->reg, mask, val); } -static int clk_gate_is_enabled(struct clk_hw *hw) +static int lpc32xx_clk_gate_is_enabled(struct clk_hw *hw) { struct lpc32xx_clk_gate *clk = to_lpc32xx_gate(hw); u32 val; @@ -916,9 +916,9 @@ static int clk_gate_is_enabled(struct clk_hw *hw) } static const struct clk_ops lpc32xx_clk_gate_ops = { - .enable = clk_gate_enable, - .disable = clk_gate_disable, - .is_enabled = clk_gate_is_enabled, + .enable = lpc32xx_clk_gate_enable, + .disable = lpc32xx_clk_gate_disable, + .is_enabled = lpc32xx_clk_gate_is_enabled, }; #define div_mask(width) ((1 << (width)) - 1) diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index d990fe44aef3..cc03d5508627 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -412,8 +412,6 @@ static const struct clk_ops clk_smd_rpm_ops = { static const struct clk_ops clk_smd_rpm_branch_ops = { .prepare = clk_smd_rpm_prepare, .unprepare = clk_smd_rpm_unprepare, - .round_rate = clk_smd_rpm_round_rate, - .recalc_rate = clk_smd_rpm_recalc_rate, }; /* msm8916 */ diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c index 2cfe7000fc60..3410ee68d4bc 100644 --- a/drivers/clk/qcom/gcc-msm8916.c +++ b/drivers/clk/qcom/gcc-msm8916.c @@ -1176,7 +1176,7 @@ static struct clk_rcg2 bimc_gpu_clk_src = { .parent_names = gcc_xo_gpll0_bimc, .num_parents = 3, .flags = CLK_GET_RATE_NOCACHE, - .ops = &clk_rcg2_shared_ops, + .ops = &clk_rcg2_ops, }, }; diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c index 8abc200d4fd3..7ddec886fcd3 100644 --- a/drivers/clk/qcom/gcc-msm8996.c +++ b/drivers/clk/qcom/gcc-msm8996.c @@ -2730,6 +2730,32 @@ static struct clk_fixed_factor ufs_rx_cfg_clk_src = { }, }; +static struct clk_branch gcc_hlos1_vote_lpass_core_smmu_clk = { + .halt_reg = 0x7d010, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7d010, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "hlos1_vote_lpass_core_smmu_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_hlos1_vote_lpass_adsp_smmu_clk = { + .halt_reg = 0x7d014, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7d014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "hlos1_vote_lpass_adsp_smmu_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch gcc_ufs_rx_cfg_clk = { .halt_reg = 0x75014, .clkr = { @@ -3307,6 +3333,8 @@ static struct clk_regmap *gcc_msm8996_clocks[] = { [GCC_UFS_AHB_CLK] = &gcc_ufs_ahb_clk.clkr, [GCC_UFS_TX_CFG_CLK] = &gcc_ufs_tx_cfg_clk.clkr, [GCC_UFS_RX_CFG_CLK] = &gcc_ufs_rx_cfg_clk.clkr, + [GCC_HLOS1_VOTE_LPASS_CORE_SMMU_CLK] = &gcc_hlos1_vote_lpass_core_smmu_clk.clkr, + [GCC_HLOS1_VOTE_LPASS_ADSP_SMMU_CLK] = &gcc_hlos1_vote_lpass_adsp_smmu_clk.clkr, [GCC_UFS_TX_SYMBOL_0_CLK] = &gcc_ufs_tx_symbol_0_clk.clkr, [GCC_UFS_RX_SYMBOL_0_CLK] = &gcc_ufs_rx_symbol_0_clk.clkr, [GCC_UFS_RX_SYMBOL_1_CLK] = &gcc_ufs_rx_symbol_1_clk.clkr, diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index 78d1df9112ba..acbb38151ba1 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -15,6 +15,7 @@ config CLK_RENESAS select CLK_R8A7794 if ARCH_R8A7794 select CLK_R8A7795 if ARCH_R8A7795 select CLK_R8A7796 if ARCH_R8A7796 + select CLK_R8A77995 if ARCH_R8A77995 select CLK_SH73A0 if ARCH_SH73A0 if CLK_RENESAS @@ -34,94 +35,103 @@ config CLK_EMEV2 bool "Emma Mobile EV2 clock support" if COMPILE_TEST config CLK_RZA1 - bool + bool "RZ/A1H clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP config CLK_R8A73A4 - bool + bool "R-Mobile APE6 clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP select CLK_RENESAS_DIV6 config CLK_R8A7740 - bool + bool "R-Mobile A1 clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP select CLK_RENESAS_DIV6 config CLK_R8A7743 - bool + bool "RZ/G1M clock support" if COMPILE_TEST select CLK_RCAR_GEN2_CPG config CLK_R8A7745 - bool + bool "RZ/G1E clock support" if COMPILE_TEST select CLK_RCAR_GEN2_CPG config CLK_R8A7778 - bool + bool "R-Car M1A clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP config CLK_R8A7779 - bool + bool "R-Car H1 clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP config CLK_R8A7790 - bool + bool "R-Car H2 clock support" if COMPILE_TEST select CLK_RCAR_GEN2 if CLK_RENESAS_LEGACY select CLK_RCAR_GEN2_CPG select CLK_RENESAS_DIV6 config CLK_R8A7791 - bool + bool "R-Car M2-W/N clock support" if COMPILE_TEST select CLK_RCAR_GEN2 if CLK_RENESAS_LEGACY select CLK_RCAR_GEN2_CPG select CLK_RENESAS_DIV6 config CLK_R8A7792 - bool + bool "R-Car V2H clock support" if COMPILE_TEST select CLK_RCAR_GEN2 if CLK_RENESAS_LEGACY select CLK_RCAR_GEN2_CPG config CLK_R8A7794 - bool + bool "R-Car E2 clock support" if COMPILE_TEST select CLK_RCAR_GEN2 if CLK_RENESAS_LEGACY select CLK_RCAR_GEN2_CPG select CLK_RENESAS_DIV6 config CLK_R8A7795 - bool + bool "R-Car H3 clock support" if COMPILE_TEST select CLK_RCAR_GEN3_CPG config CLK_R8A7796 - bool + bool "R-Car M3-W clock support" if COMPILE_TEST + select CLK_RCAR_GEN3_CPG + +config CLK_R8A77995 + bool "R-Car D3 clock support" if COMPILE_TEST select CLK_RCAR_GEN3_CPG config CLK_SH73A0 - bool + bool "SH-Mobile AG5 clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP select CLK_RENESAS_DIV6 # Family config CLK_RCAR_GEN2 - bool + bool "R-Car Gen2 legacy clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSTP select CLK_RENESAS_DIV6 config CLK_RCAR_GEN2_CPG - bool + bool "R-Car Gen2 CPG clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSSR config CLK_RCAR_GEN3_CPG - bool + bool "R-Car Gen3 CPG clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSSR +config CLK_RCAR_USB2_CLOCK_SEL + bool "Renesas R-Car USB2 clock selector support" + depends on ARCH_RENESAS || COMPILE_TEST + help + This is a driver for R-Car USB2 clock selector # Generic config CLK_RENESAS_CPG_MSSR - bool + bool "CPG/MSSR clock support" if COMPILE_TEST select CLK_RENESAS_DIV6 config CLK_RENESAS_CPG_MSTP - bool + bool "MSTP clock support" if COMPILE_TEST config CLK_RENESAS_DIV6 bool "DIV6 clock support" if COMPILE_TEST diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index 02d04124371f..9bda3ec5b199 100644 --- a/drivers/clk/renesas/Makefile +++ b/drivers/clk/renesas/Makefile @@ -13,12 +13,14 @@ obj-$(CONFIG_CLK_R8A7792) += r8a7792-cpg-mssr.o obj-$(CONFIG_CLK_R8A7794) += r8a7794-cpg-mssr.o obj-$(CONFIG_CLK_R8A7795) += r8a7795-cpg-mssr.o obj-$(CONFIG_CLK_R8A7796) += r8a7796-cpg-mssr.o +obj-$(CONFIG_CLK_R8A77995) += r8a77995-cpg-mssr.o obj-$(CONFIG_CLK_SH73A0) += clk-sh73a0.o # Family obj-$(CONFIG_CLK_RCAR_GEN2) += clk-rcar-gen2.o obj-$(CONFIG_CLK_RCAR_GEN2_CPG) += rcar-gen2-cpg.o obj-$(CONFIG_CLK_RCAR_GEN3_CPG) += rcar-gen3-cpg.o +obj-$(CONFIG_CLK_RCAR_USB2_CLOCK_SEL) += rcar-usb2-clock-sel.o # Generic obj-$(CONFIG_CLK_RENESAS_CPG_MSSR) += renesas-cpg-mssr.o diff --git a/drivers/clk/renesas/clk-div6.c b/drivers/clk/renesas/clk-div6.c index 0627860233cb..3e0040c0ac87 100644 --- a/drivers/clk/renesas/clk-div6.c +++ b/drivers/clk/renesas/clk-div6.c @@ -29,6 +29,9 @@ * @hw: handle between common and hardware-specific interfaces * @reg: IO-remapped register * @div: divisor value (1-64) + * @src_shift: Shift to access the register bits to select the parent clock + * @src_width: Number of register bits to select the parent clock (may be 0) + * @parents: Array to map from valid parent clocks indices to hardware indices */ struct div6_clock { struct clk_hw hw; diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c index f1617dd044cb..500a9e4e03c4 100644 --- a/drivers/clk/renesas/clk-mstp.c +++ b/drivers/clk/renesas/clk-mstp.c @@ -335,7 +335,7 @@ void __init cpg_mstp_add_clk_domain(struct device_node *np) u32 ncells; if (of_property_read_u32(np, "#power-domain-cells", &ncells)) { - pr_warn("%s lacks #power-domain-cells\n", np->full_name); + pr_warn("%pOF lacks #power-domain-cells\n", np); return; } diff --git a/drivers/clk/renesas/clk-rcar-gen2.c b/drivers/clk/renesas/clk-rcar-gen2.c index 51a2479ed5d7..0b2e56d0d94b 100644 --- a/drivers/clk/renesas/clk-rcar-gen2.c +++ b/drivers/clk/renesas/clk-rcar-gen2.c @@ -407,8 +407,7 @@ static void __init rcar_gen2_cpg_clocks_init(struct device_node *np) if (rcar_rst_read_mode_pins(&cpg_mode)) { /* Backward-compatibility with old DT */ - pr_warn("%s: failed to obtain mode pins from RST\n", - np->full_name); + pr_warn("%pOF: failed to obtain mode pins from RST\n", np); cpg_mode = rcar_gen2_read_mode_pins(); } diff --git a/drivers/clk/renesas/r8a7792-cpg-mssr.c b/drivers/clk/renesas/r8a7792-cpg-mssr.c index a832b9b6f7b0..7f85bbf20bf7 100644 --- a/drivers/clk/renesas/r8a7792-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7792-cpg-mssr.c @@ -118,6 +118,13 @@ static const struct mssr_mod_clk r8a7792_mod_clks[] __initconst = { DEF_MOD("vin1", 810, R8A7792_CLK_ZG), DEF_MOD("vin0", 811, R8A7792_CLK_ZG), DEF_MOD("etheravb", 812, R8A7792_CLK_HP), + DEF_MOD("imr-lx3", 821, R8A7792_CLK_ZG), + DEF_MOD("imr-lsx3-1", 822, R8A7792_CLK_ZG), + DEF_MOD("imr-lsx3-0", 823, R8A7792_CLK_ZG), + DEF_MOD("imr-lsx3-5", 825, R8A7792_CLK_ZG), + DEF_MOD("imr-lsx3-4", 826, R8A7792_CLK_ZG), + DEF_MOD("imr-lsx3-3", 827, R8A7792_CLK_ZG), + DEF_MOD("imr-lsx3-2", 828, R8A7792_CLK_ZG), DEF_MOD("gyro-adc", 901, R8A7792_CLK_P), DEF_MOD("gpio7", 904, R8A7792_CLK_CP), DEF_MOD("gpio6", 905, R8A7792_CLK_CP), diff --git a/drivers/clk/renesas/r8a7795-cpg-mssr.c b/drivers/clk/renesas/r8a7795-cpg-mssr.c index c091a8e024b8..762b2f8824f1 100644 --- a/drivers/clk/renesas/r8a7795-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7795-cpg-mssr.c @@ -305,23 +305,23 @@ static const unsigned int r8a7795_crit_mod_clks[] __initconst = { (((md) & BIT(17)) >> 17)) static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[16] __initconst = { - /* EXTAL div PLL1 mult PLL3 mult */ - { 1, 192, 192, }, - { 1, 192, 128, }, - { 0, /* Prohibited setting */ }, - { 1, 192, 192, }, - { 1, 160, 160, }, - { 1, 160, 106, }, - { 0, /* Prohibited setting */ }, - { 1, 160, 160, }, - { 1, 128, 128, }, - { 1, 128, 84, }, - { 0, /* Prohibited setting */ }, - { 1, 128, 128, }, - { 2, 192, 192, }, - { 2, 192, 128, }, - { 0, /* Prohibited setting */ }, - { 2, 192, 192, }, + /* EXTAL div PLL1 mult/div PLL3 mult/div */ + { 1, 192, 1, 192, 1, }, + { 1, 192, 1, 128, 1, }, + { 0, /* Prohibited setting */ }, + { 1, 192, 1, 192, 1, }, + { 1, 160, 1, 160, 1, }, + { 1, 160, 1, 106, 1, }, + { 0, /* Prohibited setting */ }, + { 1, 160, 1, 160, 1, }, + { 1, 128, 1, 128, 1, }, + { 1, 128, 1, 84, 1, }, + { 0, /* Prohibited setting */ }, + { 1, 128, 1, 128, 1, }, + { 2, 192, 1, 192, 1, }, + { 2, 192, 1, 128, 1, }, + { 0, /* Prohibited setting */ }, + { 2, 192, 1, 192, 1, }, }; static const struct soc_device_attribute r8a7795es1[] __initconst = { diff --git a/drivers/clk/renesas/r8a7796-cpg-mssr.c b/drivers/clk/renesas/r8a7796-cpg-mssr.c index acc6d0f153e1..e5e7fb212288 100644 --- a/drivers/clk/renesas/r8a7796-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7796-cpg-mssr.c @@ -138,6 +138,7 @@ static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = { DEF_MOD("sdif0", 314, R8A7796_CLK_SD0), DEF_MOD("pcie1", 318, R8A7796_CLK_S3D1), DEF_MOD("pcie0", 319, R8A7796_CLK_S3D1), + DEF_MOD("usb3-if0", 328, R8A7796_CLK_S3D1), DEF_MOD("usb-dmac0", 330, R8A7796_CLK_S3D1), DEF_MOD("usb-dmac1", 331, R8A7796_CLK_S3D1), DEF_MOD("rwdt", 402, R8A7796_CLK_R), @@ -277,23 +278,23 @@ static const unsigned int r8a7796_crit_mod_clks[] __initconst = { (((md) & BIT(17)) >> 17)) static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[16] __initconst = { - /* EXTAL div PLL1 mult PLL3 mult */ - { 1, 192, 192, }, - { 1, 192, 128, }, - { 0, /* Prohibited setting */ }, - { 1, 192, 192, }, - { 1, 160, 160, }, - { 1, 160, 106, }, - { 0, /* Prohibited setting */ }, - { 1, 160, 160, }, - { 1, 128, 128, }, - { 1, 128, 84, }, - { 0, /* Prohibited setting */ }, - { 1, 128, 128, }, - { 2, 192, 192, }, - { 2, 192, 128, }, - { 0, /* Prohibited setting */ }, - { 2, 192, 192, }, + /* EXTAL div PLL1 mult/div PLL3 mult/div */ + { 1, 192, 1, 192, 1, }, + { 1, 192, 1, 128, 1, }, + { 0, /* Prohibited setting */ }, + { 1, 192, 1, 192, 1, }, + { 1, 160, 1, 160, 1, }, + { 1, 160, 1, 106, 1, }, + { 0, /* Prohibited setting */ }, + { 1, 160, 1, 160, 1, }, + { 1, 128, 1, 128, 1, }, + { 1, 128, 1, 84, 1, }, + { 0, /* Prohibited setting */ }, + { 1, 128, 1, 128, 1, }, + { 2, 192, 1, 192, 1, }, + { 2, 192, 1, 128, 1, }, + { 0, /* Prohibited setting */ }, + { 2, 192, 1, 192, 1, }, }; static int __init r8a7796_cpg_mssr_init(struct device *dev) diff --git a/drivers/clk/renesas/r8a77995-cpg-mssr.c b/drivers/clk/renesas/r8a77995-cpg-mssr.c new file mode 100644 index 000000000000..e594cf8ee63b --- /dev/null +++ b/drivers/clk/renesas/r8a77995-cpg-mssr.c @@ -0,0 +1,236 @@ +/* + * r8a77995 Clock Pulse Generator / Module Standby and Software Reset + * + * Copyright (C) 2017 Glider bvba + * + * Based on r8a7795-cpg-mssr.c + * + * Copyright (C) 2015 Glider bvba + * Copyright (C) 2015 Renesas Electronics Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/soc/renesas/rcar-rst.h> + +#include <dt-bindings/clock/r8a77995-cpg-mssr.h> + +#include "renesas-cpg-mssr.h" +#include "rcar-gen3-cpg.h" + +enum clk_ids { + /* Core Clock Outputs exported to DT */ + LAST_DT_CORE_CLK = R8A77995_CLK_CP, + + /* External Input Clocks */ + CLK_EXTAL, + + /* Internal Core Clocks */ + CLK_MAIN, + CLK_PLL0, + CLK_PLL1, + CLK_PLL3, + CLK_PLL0D2, + CLK_PLL0D3, + CLK_PLL0D5, + CLK_PLL1D2, + CLK_PE, + CLK_S0, + CLK_S1, + CLK_S2, + CLK_S3, + CLK_SDSRC, + CLK_SSPSRC, + + /* Module Clocks */ + MOD_CLK_BASE +}; + +static const struct cpg_core_clk r8a77995_core_clks[] __initconst = { + /* External Clock Inputs */ + DEF_INPUT("extal", CLK_EXTAL), + + /* Internal Core Clocks */ + DEF_BASE(".main", CLK_MAIN, CLK_TYPE_GEN3_MAIN, CLK_EXTAL), + DEF_BASE(".pll1", CLK_PLL1, CLK_TYPE_GEN3_PLL1, CLK_MAIN), + DEF_BASE(".pll3", CLK_PLL3, CLK_TYPE_GEN3_PLL3, CLK_MAIN), + + DEF_FIXED(".pll0", CLK_PLL0, CLK_MAIN, 4, 250), + DEF_FIXED(".pll0d2", CLK_PLL0D2, CLK_PLL0, 2, 1), + DEF_FIXED(".pll0d3", CLK_PLL0D3, CLK_PLL0, 3, 1), + DEF_FIXED(".pll0d5", CLK_PLL0D5, CLK_PLL0, 5, 1), + DEF_FIXED(".pll1d2", CLK_PLL1D2, CLK_PLL1, 2, 1), + DEF_FIXED(".pe", CLK_PE, CLK_PLL0D3, 4, 1), + DEF_FIXED(".s0", CLK_S0, CLK_PLL1, 2, 1), + DEF_FIXED(".s1", CLK_S1, CLK_PLL1, 3, 1), + DEF_FIXED(".s2", CLK_S2, CLK_PLL1, 4, 1), + DEF_FIXED(".s3", CLK_S3, CLK_PLL1, 6, 1), + DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1, 2, 1), + + /* Core Clock Outputs */ + DEF_FIXED("z2", R8A77995_CLK_Z2, CLK_PLL0D3, 1, 1), + DEF_FIXED("ztr", R8A77995_CLK_ZTR, CLK_PLL1, 6, 1), + DEF_FIXED("zt", R8A77995_CLK_ZT, CLK_PLL1, 4, 1), + DEF_FIXED("zx", R8A77995_CLK_ZX, CLK_PLL1, 3, 1), + DEF_FIXED("s0d1", R8A77995_CLK_S0D1, CLK_S0, 1, 1), + DEF_FIXED("s1d1", R8A77995_CLK_S1D1, CLK_S1, 1, 1), + DEF_FIXED("s1d2", R8A77995_CLK_S1D2, CLK_S1, 2, 1), + DEF_FIXED("s1d4", R8A77995_CLK_S1D4, CLK_S1, 4, 1), + DEF_FIXED("s2d1", R8A77995_CLK_S2D1, CLK_S2, 1, 1), + DEF_FIXED("s2d2", R8A77995_CLK_S2D2, CLK_S2, 2, 1), + DEF_FIXED("s2d4", R8A77995_CLK_S2D4, CLK_S2, 4, 1), + DEF_FIXED("s3d1", R8A77995_CLK_S3D1, CLK_S3, 1, 1), + DEF_FIXED("s3d2", R8A77995_CLK_S3D2, CLK_S3, 2, 1), + DEF_FIXED("s3d4", R8A77995_CLK_S3D4, CLK_S3, 4, 1), + + DEF_FIXED("cl", R8A77995_CLK_CL, CLK_PLL1, 48, 1), + DEF_FIXED("cp", R8A77995_CLK_CP, CLK_EXTAL, 2, 1), + DEF_FIXED("osc", R8A77995_CLK_OSC, CLK_EXTAL, 384, 1), + DEF_FIXED("r", R8A77995_CLK_R, CLK_EXTAL, 1536, 1), + + DEF_GEN3_PE("s1d4c", R8A77995_CLK_S1D4C, CLK_S1, 4, CLK_PE, 2), + DEF_GEN3_PE("s3d1c", R8A77995_CLK_S3D1C, CLK_S3, 1, CLK_PE, 1), + DEF_GEN3_PE("s3d2c", R8A77995_CLK_S3D2C, CLK_S3, 2, CLK_PE, 2), + DEF_GEN3_PE("s3d4c", R8A77995_CLK_S3D4C, CLK_S3, 4, CLK_PE, 4), + + DEF_GEN3_SD("sd0", R8A77995_CLK_SD0, CLK_SDSRC, 0x268), + + DEF_DIV6P1("canfd", R8A77995_CLK_CANFD, CLK_PLL0D3, 0x244), + DEF_DIV6P1("mso", R8A77995_CLK_MSO, CLK_PLL1D2, 0x014), +}; + +static const struct mssr_mod_clk r8a77995_mod_clks[] __initconst = { + DEF_MOD("scif5", 202, R8A77995_CLK_S3D4C), + DEF_MOD("scif4", 203, R8A77995_CLK_S3D4C), + DEF_MOD("scif3", 204, R8A77995_CLK_S3D4C), + DEF_MOD("scif1", 206, R8A77995_CLK_S3D4C), + DEF_MOD("scif0", 207, R8A77995_CLK_S3D4C), + DEF_MOD("msiof3", 208, R8A77995_CLK_MSO), + DEF_MOD("msiof2", 209, R8A77995_CLK_MSO), + DEF_MOD("msiof1", 210, R8A77995_CLK_MSO), + DEF_MOD("msiof0", 211, R8A77995_CLK_MSO), + DEF_MOD("sys-dmac2", 217, R8A77995_CLK_S3D1), + DEF_MOD("sys-dmac1", 218, R8A77995_CLK_S3D1), + DEF_MOD("sys-dmac0", 219, R8A77995_CLK_S3D1), + DEF_MOD("cmt3", 300, R8A77995_CLK_R), + DEF_MOD("cmt2", 301, R8A77995_CLK_R), + DEF_MOD("cmt1", 302, R8A77995_CLK_R), + DEF_MOD("cmt0", 303, R8A77995_CLK_R), + DEF_MOD("scif2", 310, R8A77995_CLK_S3D4C), + DEF_MOD("emmc0", 312, R8A77995_CLK_SD0), + DEF_MOD("usb-dmac0", 330, R8A77995_CLK_S3D1), + DEF_MOD("usb-dmac1", 331, R8A77995_CLK_S3D1), + DEF_MOD("rwdt", 402, R8A77995_CLK_R), + DEF_MOD("intc-ex", 407, R8A77995_CLK_CP), + DEF_MOD("intc-ap", 408, R8A77995_CLK_S3D1), + DEF_MOD("audmac0", 502, R8A77995_CLK_S3D1), + DEF_MOD("hscif3", 517, R8A77995_CLK_S3D1C), + DEF_MOD("hscif0", 520, R8A77995_CLK_S3D1C), + DEF_MOD("thermal", 522, R8A77995_CLK_CP), + DEF_MOD("pwm", 523, R8A77995_CLK_S3D4C), + DEF_MOD("fcpvd1", 602, R8A77995_CLK_S1D2), + DEF_MOD("fcpvd0", 603, R8A77995_CLK_S1D2), + DEF_MOD("fcpvbs", 607, R8A77995_CLK_S0D1), + DEF_MOD("vspd1", 622, R8A77995_CLK_S1D2), + DEF_MOD("vspd0", 623, R8A77995_CLK_S1D2), + DEF_MOD("vspbs", 627, R8A77995_CLK_S0D1), + DEF_MOD("ehci0", 703, R8A77995_CLK_S3D2), + DEF_MOD("hsusb", 704, R8A77995_CLK_S3D2), + DEF_MOD("du1", 723, R8A77995_CLK_S2D1), + DEF_MOD("du0", 724, R8A77995_CLK_S2D1), + DEF_MOD("lvds", 727, R8A77995_CLK_S2D1), + DEF_MOD("vin7", 804, R8A77995_CLK_S1D2), + DEF_MOD("vin6", 805, R8A77995_CLK_S1D2), + DEF_MOD("vin5", 806, R8A77995_CLK_S1D2), + DEF_MOD("vin4", 807, R8A77995_CLK_S1D2), + DEF_MOD("etheravb", 812, R8A77995_CLK_S3D2), + DEF_MOD("imr0", 823, R8A77995_CLK_S1D2), + DEF_MOD("gpio6", 906, R8A77995_CLK_S3D4), + DEF_MOD("gpio5", 907, R8A77995_CLK_S3D4), + DEF_MOD("gpio4", 908, R8A77995_CLK_S3D4), + DEF_MOD("gpio3", 909, R8A77995_CLK_S3D4), + DEF_MOD("gpio2", 910, R8A77995_CLK_S3D4), + DEF_MOD("gpio1", 911, R8A77995_CLK_S3D4), + DEF_MOD("gpio0", 912, R8A77995_CLK_S3D4), + DEF_MOD("can-fd", 914, R8A77995_CLK_S3D2), + DEF_MOD("can-if1", 915, R8A77995_CLK_S3D4), + DEF_MOD("can-if0", 916, R8A77995_CLK_S3D4), + DEF_MOD("i2c3", 928, R8A77995_CLK_S3D2), + DEF_MOD("i2c2", 929, R8A77995_CLK_S3D2), + DEF_MOD("i2c1", 930, R8A77995_CLK_S3D2), + DEF_MOD("i2c0", 931, R8A77995_CLK_S3D2), + DEF_MOD("ssi-all", 1005, R8A77995_CLK_S3D4), + DEF_MOD("ssi4", 1011, MOD_CLK_ID(1005)), + DEF_MOD("ssi3", 1012, MOD_CLK_ID(1005)), + DEF_MOD("scu-all", 1017, R8A77995_CLK_S3D4), + DEF_MOD("scu-dvc1", 1018, MOD_CLK_ID(1017)), + DEF_MOD("scu-dvc0", 1019, MOD_CLK_ID(1017)), + DEF_MOD("scu-ctu1-mix1", 1020, MOD_CLK_ID(1017)), + DEF_MOD("scu-ctu0-mix0", 1021, MOD_CLK_ID(1017)), + DEF_MOD("scu-src6", 1025, MOD_CLK_ID(1017)), + DEF_MOD("scu-src5", 1026, MOD_CLK_ID(1017)), +}; + +static const unsigned int r8a77995_crit_mod_clks[] __initconst = { + MOD_CLK_ID(408), /* INTC-AP (GIC) */ +}; + + +/* + * CPG Clock Data + */ + +/* + * MD19 EXTAL (MHz) PLL0 PLL1 PLL3 + *-------------------------------------------------------------------- + * 0 48 x 1 x250/4 x100/3 x100/3 + * 1 48 x 1 x250/4 x100/3 x116/6 + */ +#define CPG_PLL_CONFIG_INDEX(md) (((md) & BIT(19)) >> 19) + +static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[2] __initconst = { + /* EXTAL div PLL1 mult/div PLL3 mult/div */ + { 1, 100, 3, 100, 3, }, + { 1, 100, 3, 116, 6, }, +}; + +static int __init r8a77995_cpg_mssr_init(struct device *dev) +{ + const struct rcar_gen3_cpg_pll_config *cpg_pll_config; + u32 cpg_mode; + int error; + + error = rcar_rst_read_mode_pins(&cpg_mode); + if (error) + return error; + + cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)]; + + return rcar_gen3_cpg_init(cpg_pll_config, 0, cpg_mode); +} + +const struct cpg_mssr_info r8a77995_cpg_mssr_info __initconst = { + /* Core Clocks */ + .core_clks = r8a77995_core_clks, + .num_core_clks = ARRAY_SIZE(r8a77995_core_clks), + .last_dt_core_clk = LAST_DT_CORE_CLK, + .num_total_core_clks = MOD_CLK_BASE, + + /* Module Clocks */ + .mod_clks = r8a77995_mod_clks, + .num_mod_clks = ARRAY_SIZE(r8a77995_mod_clks), + .num_hw_mod_clks = 12 * 32, + + /* Critical Module Clocks */ + .crit_mod_clks = r8a77995_crit_mod_clks, + .num_crit_mod_clks = ARRAY_SIZE(r8a77995_crit_mod_clks), + + /* Callbacks */ + .init = r8a77995_cpg_mssr_init, + .cpg_clk_register = rcar_gen3_cpg_clk_register, +}; diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index 3dee900522b7..951105816547 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c @@ -60,6 +60,7 @@ struct sd_clock { unsigned int div_num; unsigned int div_min; unsigned int div_max; + unsigned int cur_div_idx; }; /* SDn divider @@ -96,21 +97,10 @@ static const struct sd_div_table cpg_sd_div_table[] = { static int cpg_sd_clock_enable(struct clk_hw *hw) { struct sd_clock *clock = to_sd_clock(hw); - u32 val, sd_fc; - unsigned int i; - - val = readl(clock->reg); - - sd_fc = val & CPG_SD_FC_MASK; - for (i = 0; i < clock->div_num; i++) - if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK)) - break; - - if (i >= clock->div_num) - return -EINVAL; + u32 val = readl(clock->reg); val &= ~(CPG_SD_STP_MASK); - val |= clock->div_table[i].val & CPG_SD_STP_MASK; + val |= clock->div_table[clock->cur_div_idx].val & CPG_SD_STP_MASK; writel(val, clock->reg); @@ -135,21 +125,9 @@ static unsigned long cpg_sd_clock_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct sd_clock *clock = to_sd_clock(hw); - unsigned long rate = parent_rate; - u32 val, sd_fc; - unsigned int i; - val = readl(clock->reg); - - sd_fc = val & CPG_SD_FC_MASK; - for (i = 0; i < clock->div_num; i++) - if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK)) - break; - - if (i >= clock->div_num) - return -EINVAL; - - return DIV_ROUND_CLOSEST(rate, clock->div_table[i].div); + return DIV_ROUND_CLOSEST(parent_rate, + clock->div_table[clock->cur_div_idx].div); } static unsigned int cpg_sd_clock_calc_div(struct sd_clock *clock, @@ -190,6 +168,8 @@ static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate, if (i >= clock->div_num) return -EINVAL; + clock->cur_div_idx = i; + val = readl(clock->reg); val &= ~(CPG_SD_STP_MASK | CPG_SD_FC_MASK); val |= clock->div_table[i].val & (CPG_SD_STP_MASK | CPG_SD_FC_MASK); @@ -215,6 +195,7 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core, struct sd_clock *clock; struct clk *clk; unsigned int i; + u32 sd_fc; clock = kzalloc(sizeof(*clock), GFP_KERNEL); if (!clock) @@ -231,6 +212,18 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core, clock->div_table = cpg_sd_div_table; clock->div_num = ARRAY_SIZE(cpg_sd_div_table); + sd_fc = readl(clock->reg) & CPG_SD_FC_MASK; + for (i = 0; i < clock->div_num; i++) + if (sd_fc == (clock->div_table[i].val & CPG_SD_FC_MASK)) + break; + + if (WARN_ON(i >= clock->div_num)) { + kfree(clock); + return ERR_PTR(-EINVAL); + } + + clock->cur_div_idx = i; + clock->div_max = clock->div_table[0].div; clock->div_min = clock->div_max; for (i = 1; i < clock->div_num; i++) { @@ -279,7 +272,7 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, unsigned int div = 1; u32 value; - parent = clks[core->parent]; + parent = clks[core->parent & 0xffff]; /* CLK_TYPE_PE uses high bits */ if (IS_ERR(parent)) return ERR_CAST(parent); @@ -303,6 +296,7 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, case CLK_TYPE_GEN3_PLL1: mult = cpg_pll_config->pll1_mult; + div = cpg_pll_config->pll1_div; break; case CLK_TYPE_GEN3_PLL2: @@ -320,6 +314,7 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, case CLK_TYPE_GEN3_PLL3: mult = cpg_pll_config->pll3_mult; + div = cpg_pll_config->pll3_div; break; case CLK_TYPE_GEN3_PLL4: @@ -360,6 +355,24 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev, parent = clks[cpg_clk_extalr]; break; + case CLK_TYPE_GEN3_PE: + /* + * Peripheral clock with a fixed divider, selectable between + * clean and spread spectrum parents using MD12 + */ + if (cpg_mode & BIT(12)) { + /* Clean */ + div = core->div & 0xffff; + } else { + /* SCCG */ + parent = clks[core->parent >> 16]; + if (IS_ERR(parent)) + return ERR_CAST(parent); + div = core->div >> 16; + } + mult = 1; + break; + default: return ERR_PTR(-EINVAL); } diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h index 073be54b5d03..d756ef8b78eb 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.h +++ b/drivers/clk/renesas/rcar-gen3-cpg.h @@ -20,15 +20,24 @@ enum rcar_gen3_clk_types { CLK_TYPE_GEN3_PLL4, CLK_TYPE_GEN3_SD, CLK_TYPE_GEN3_R, + CLK_TYPE_GEN3_PE, }; #define DEF_GEN3_SD(_name, _id, _parent, _offset) \ DEF_BASE(_name, _id, CLK_TYPE_GEN3_SD, _parent, .offset = _offset) +#define DEF_GEN3_PE(_name, _id, _parent_sscg, _div_sscg, _parent_clean, \ + _div_clean) \ + DEF_BASE(_name, _id, CLK_TYPE_GEN3_PE, \ + (_parent_sscg) << 16 | (_parent_clean), \ + .div = (_div_sscg) << 16 | (_div_clean)) + struct rcar_gen3_cpg_pll_config { - unsigned int extal_div; - unsigned int pll1_mult; - unsigned int pll3_mult; + u8 extal_div; + u8 pll1_mult; + u8 pll1_div; + u8 pll3_mult; + u8 pll3_div; }; #define CPG_RCKCR 0x240 diff --git a/drivers/clk/renesas/rcar-usb2-clock-sel.c b/drivers/clk/renesas/rcar-usb2-clock-sel.c new file mode 100644 index 000000000000..6cd030a58964 --- /dev/null +++ b/drivers/clk/renesas/rcar-usb2-clock-sel.c @@ -0,0 +1,188 @@ +/* + * Renesas R-Car USB2.0 clock selector + * + * Copyright (C) 2017 Renesas Electronics Corp. + * + * Based on renesas-cpg-mssr.c + * + * Copyright (C) 2015 Glider bvba + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> + +#define USB20_CLKSET0 0x00 +#define CLKSET0_INTCLK_EN BIT(11) +#define CLKSET0_PRIVATE BIT(0) +#define CLKSET0_EXTAL_ONLY (CLKSET0_INTCLK_EN | CLKSET0_PRIVATE) + +struct usb2_clock_sel_priv { + void __iomem *base; + struct clk_hw hw; + bool extal; + bool xtal; +}; +#define to_priv(_hw) container_of(_hw, struct usb2_clock_sel_priv, hw) + +static void usb2_clock_sel_enable_extal_only(struct usb2_clock_sel_priv *priv) +{ + u16 val = readw(priv->base + USB20_CLKSET0); + + pr_debug("%s: enter %d %d %x\n", __func__, + priv->extal, priv->xtal, val); + + if (priv->extal && !priv->xtal && val != CLKSET0_EXTAL_ONLY) + writew(CLKSET0_EXTAL_ONLY, priv->base + USB20_CLKSET0); +} + +static void usb2_clock_sel_disable_extal_only(struct usb2_clock_sel_priv *priv) +{ + if (priv->extal && !priv->xtal) + writew(CLKSET0_PRIVATE, priv->base + USB20_CLKSET0); +} + +static int usb2_clock_sel_enable(struct clk_hw *hw) +{ + usb2_clock_sel_enable_extal_only(to_priv(hw)); + + return 0; +} + +static void usb2_clock_sel_disable(struct clk_hw *hw) +{ + usb2_clock_sel_disable_extal_only(to_priv(hw)); +} + +/* + * This module seems a mux, but this driver assumes a gate because + * ehci/ohci platform drivers don't support clk_set_parent() for now. + * If this driver acts as a gate, ehci/ohci-platform drivers don't need + * any modification. + */ +static const struct clk_ops usb2_clock_sel_clock_ops = { + .enable = usb2_clock_sel_enable, + .disable = usb2_clock_sel_disable, +}; + +static const struct of_device_id rcar_usb2_clock_sel_match[] = { + { .compatible = "renesas,rcar-gen3-usb2-clock-sel" }, + { } +}; + +static int rcar_usb2_clock_sel_suspend(struct device *dev) +{ + struct usb2_clock_sel_priv *priv = dev_get_drvdata(dev); + + usb2_clock_sel_disable_extal_only(priv); + pm_runtime_put(dev); + + return 0; +} + +static int rcar_usb2_clock_sel_resume(struct device *dev) +{ + struct usb2_clock_sel_priv *priv = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + usb2_clock_sel_enable_extal_only(priv); + + return 0; +} + +static int rcar_usb2_clock_sel_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb2_clock_sel_priv *priv = platform_get_drvdata(pdev); + + of_clk_del_provider(dev->of_node); + clk_hw_unregister(&priv->hw); + pm_runtime_put(dev); + pm_runtime_disable(dev); + + return 0; +} + +static int rcar_usb2_clock_sel_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct usb2_clock_sel_priv *priv; + struct resource *res; + struct clk *clk; + struct clk_init_data init; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + clk = devm_clk_get(dev, "usb_extal"); + if (!IS_ERR(clk) && !clk_prepare_enable(clk)) { + priv->extal = !!clk_get_rate(clk); + clk_disable_unprepare(clk); + } + clk = devm_clk_get(dev, "usb_xtal"); + if (!IS_ERR(clk) && !clk_prepare_enable(clk)) { + priv->xtal = !!clk_get_rate(clk); + clk_disable_unprepare(clk); + } + + if (!priv->extal && !priv->xtal) { + dev_err(dev, "This driver needs usb_extal or usb_xtal\n"); + return -ENOENT; + } + + platform_set_drvdata(pdev, priv); + dev_set_drvdata(dev, priv); + + init.name = "rcar_usb2_clock_sel"; + init.ops = &usb2_clock_sel_clock_ops; + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + priv->hw.init = &init; + + clk = clk_register(NULL, &priv->hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_hw_provider(np, of_clk_hw_simple_get, &priv->hw); +} + +static const struct dev_pm_ops rcar_usb2_clock_sel_pm_ops = { + .suspend = rcar_usb2_clock_sel_suspend, + .resume = rcar_usb2_clock_sel_resume, +}; + +static struct platform_driver rcar_usb2_clock_sel_driver = { + .driver = { + .name = "rcar-usb2-clock-sel", + .of_match_table = rcar_usb2_clock_sel_match, + .pm = &rcar_usb2_clock_sel_pm_ops, + }, + .probe = rcar_usb2_clock_sel_probe, + .remove = rcar_usb2_clock_sel_remove, +}; +builtin_platform_driver(rcar_usb2_clock_sel_driver); + +MODULE_DESCRIPTION("Renesas R-Car USB2 clock selector Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index 1f607c806f9b..e580a5e6346c 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -680,6 +680,12 @@ static const struct of_device_id cpg_mssr_match[] = { .data = &r8a7796_cpg_mssr_info, }, #endif +#ifdef CONFIG_CLK_R8A77995 + { + .compatible = "renesas,r8a77995-cpg-mssr", + .data = &r8a77995_cpg_mssr_info, + }, +#endif { /* sentinel */ } }; diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h index 43d7c7f6832d..94b9071d1061 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.h +++ b/drivers/clk/renesas/renesas-cpg-mssr.h @@ -138,6 +138,7 @@ extern const struct cpg_mssr_info r8a7792_cpg_mssr_info; extern const struct cpg_mssr_info r8a7794_cpg_mssr_info; extern const struct cpg_mssr_info r8a7795_cpg_mssr_info; extern const struct cpg_mssr_info r8a7796_cpg_mssr_info; +extern const struct cpg_mssr_info r8a77995_cpg_mssr_info; /* diff --git a/drivers/clk/rockchip/clk-rk3128.c b/drivers/clk/rockchip/clk-rk3128.c index e243f2eae68f..62d7854e4b87 100644 --- a/drivers/clk/rockchip/clk-rk3128.c +++ b/drivers/clk/rockchip/clk-rk3128.c @@ -201,7 +201,7 @@ static struct rockchip_clk_branch rk3128_uart2_fracmux __initdata = MUX(SCLK_UART2, "sclk_uart2", mux_uart2_p, CLK_SET_RATE_PARENT, RK2928_CLKSEL_CON(15), 8, 2, MFLAGS); -static struct rockchip_clk_branch rk3128_clk_branches[] __initdata = { +static struct rockchip_clk_branch common_clk_branches[] __initdata = { /* * Clock-Architecture Diagram 1 */ @@ -459,10 +459,6 @@ static struct rockchip_clk_branch rk3128_clk_branches[] __initdata = { RK2928_CLKSEL_CON(2), 14, 2, MFLAGS, 8, 5, DFLAGS, RK2928_CLKGATE_CON(10), 15, GFLAGS), - COMPOSITE(SCLK_SFC, "sclk_sfc", mux_sclk_sfc_src_p, 0, - RK2928_CLKSEL_CON(11), 14, 2, MFLAGS, 8, 5, DFLAGS, - RK2928_CLKGATE_CON(3), 15, GFLAGS), - COMPOSITE_NOMUX(PCLK_PMU_PRE, "pclk_pmu_pre", "cpll", 0, RK2928_CLKSEL_CON(29), 8, 6, DFLAGS, RK2928_CLKGATE_CON(1), 0, GFLAGS), @@ -495,7 +491,6 @@ static struct rockchip_clk_branch rk3128_clk_branches[] __initdata = { GATE(ACLK_DMAC, "aclk_dmac", "aclk_peri", 0, RK2928_CLKGATE_CON(5), 1, GFLAGS), GATE(0, "aclk_peri_niu", "aclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(9), 15, GFLAGS), GATE(0, "aclk_cpu_to_peri", "aclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(4), 2, GFLAGS), - GATE(HCLK_GPS, "hclk_gps", "aclk_peri", 0, RK2928_CLKGATE_CON(3), 14, GFLAGS), GATE(HCLK_I2S_8CH, "hclk_i2s_8ch", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 4, GFLAGS), GATE(0, "hclk_peri_matrix", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(4), 0, GFLAGS), @@ -541,7 +536,6 @@ static struct rockchip_clk_branch rk3128_clk_branches[] __initdata = { GATE(0, "hclk_rom", "hclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(5), 6, GFLAGS), GATE(HCLK_CRYPTO, "hclk_crypto", "hclk_cpu", 0, RK2928_CLKGATE_CON(3), 5, GFLAGS), - GATE(PCLK_HDMI, "pclk_hdmi", "pclk_cpu", 0, RK2928_CLKGATE_CON(3), 8, GFLAGS), GATE(PCLK_ACODEC, "pclk_acodec", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 14, GFLAGS), GATE(0, "pclk_ddrupctl", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(5), 7, GFLAGS), GATE(0, "pclk_grf", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(5), 4, GFLAGS), @@ -561,6 +555,21 @@ static struct rockchip_clk_branch rk3128_clk_branches[] __initdata = { MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "sclk_emmc", RK3228_EMMC_CON1, 0), }; +static struct rockchip_clk_branch rk3126_clk_branches[] __initdata = { + GATE(0, "pclk_stimer", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(3), 15, GFLAGS), + GATE(0, "pclk_s_efuse", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(3), 14, GFLAGS), + GATE(0, "pclk_sgrf", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(3), 8, GFLAGS), +}; + +static struct rockchip_clk_branch rk3128_clk_branches[] __initdata = { + COMPOSITE(SCLK_SFC, "sclk_sfc", mux_sclk_sfc_src_p, 0, + RK2928_CLKSEL_CON(11), 14, 2, MFLAGS, 8, 5, DFLAGS, + RK2928_CLKGATE_CON(3), 15, GFLAGS), + + GATE(HCLK_GPS, "hclk_gps", "aclk_peri", 0, RK2928_CLKGATE_CON(3), 14, GFLAGS), + GATE(PCLK_HDMI, "pclk_hdmi", "pclk_cpu", 0, RK2928_CLKGATE_CON(3), 8, GFLAGS), +}; + static const char *const rk3128_critical_clocks[] __initconst = { "aclk_cpu", "hclk_cpu", @@ -570,7 +579,7 @@ static const char *const rk3128_critical_clocks[] __initconst = { "pclk_peri", }; -static void __init rk3128_clk_init(struct device_node *np) +static struct rockchip_clk_provider *__init rk3128_common_clk_init(struct device_node *np) { struct rockchip_clk_provider *ctx; void __iomem *reg_base; @@ -578,23 +587,21 @@ static void __init rk3128_clk_init(struct device_node *np) reg_base = of_iomap(np, 0); if (!reg_base) { pr_err("%s: could not map cru region\n", __func__); - return; + return ERR_PTR(-ENOMEM); } ctx = rockchip_clk_init(np, reg_base, CLK_NR_CLKS); if (IS_ERR(ctx)) { pr_err("%s: rockchip clk init failed\n", __func__); iounmap(reg_base); - return; + return ERR_PTR(-ENOMEM); } rockchip_clk_register_plls(ctx, rk3128_pll_clks, ARRAY_SIZE(rk3128_pll_clks), RK3128_GRF_SOC_STATUS0); - rockchip_clk_register_branches(ctx, rk3128_clk_branches, - ARRAY_SIZE(rk3128_clk_branches)); - rockchip_clk_protect_critical(rk3128_critical_clocks, - ARRAY_SIZE(rk3128_critical_clocks)); + rockchip_clk_register_branches(ctx, common_clk_branches, + ARRAY_SIZE(common_clk_branches)); rockchip_clk_register_armclk(ctx, ARMCLK, "armclk", mux_armclk_p, ARRAY_SIZE(mux_armclk_p), @@ -606,6 +613,40 @@ static void __init rk3128_clk_init(struct device_node *np) rockchip_register_restart_notifier(ctx, RK2928_GLB_SRST_FST, NULL); + return ctx; +} + +static void __init rk3126_clk_init(struct device_node *np) +{ + struct rockchip_clk_provider *ctx; + + ctx = rk3128_common_clk_init(np); + if (IS_ERR(ctx)) + return; + + rockchip_clk_register_branches(ctx, rk3126_clk_branches, + ARRAY_SIZE(rk3126_clk_branches)); + rockchip_clk_protect_critical(rk3128_critical_clocks, + ARRAY_SIZE(rk3128_critical_clocks)); + + rockchip_clk_of_add_provider(np, ctx); +} + +CLK_OF_DECLARE(rk3126_cru, "rockchip,rk3126-cru", rk3126_clk_init); + +static void __init rk3128_clk_init(struct device_node *np) +{ + struct rockchip_clk_provider *ctx; + + ctx = rk3128_common_clk_init(np); + if (IS_ERR(ctx)) + return; + + rockchip_clk_register_branches(ctx, rk3128_clk_branches, + ARRAY_SIZE(rk3128_clk_branches)); + rockchip_clk_protect_critical(rk3128_critical_clocks, + ARRAY_SIZE(rk3128_critical_clocks)); + rockchip_clk_of_add_provider(np, ctx); } diff --git a/drivers/clk/rockchip/clk-rk3228.c b/drivers/clk/rockchip/clk-rk3228.c index bb405d9044a3..11e7f2d1c054 100644 --- a/drivers/clk/rockchip/clk-rk3228.c +++ b/drivers/clk/rockchip/clk-rk3228.c @@ -391,7 +391,7 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = { RK2928_CLKSEL_CON(11), 8, 2, MFLAGS, 0, 8, DFLAGS, RK2928_CLKGATE_CON(2), 11, GFLAGS), - COMPOSITE_NODIV(0, "sclk_sdio_src", mux_mmc_src_p, 0, + COMPOSITE_NODIV(SCLK_SDIO_SRC, "sclk_sdio_src", mux_mmc_src_p, 0, RK2928_CLKSEL_CON(11), 10, 2, MFLAGS, RK2928_CLKGATE_CON(2), 13, GFLAGS), DIV(SCLK_SDIO, "sclk_sdio", "sclk_sdio_src", 0, diff --git a/drivers/clk/rockchip/clk-rv1108.c b/drivers/clk/rockchip/clk-rv1108.c index 7c05ab366348..089cb17925e5 100644 --- a/drivers/clk/rockchip/clk-rv1108.c +++ b/drivers/clk/rockchip/clk-rv1108.c @@ -93,9 +93,24 @@ static struct rockchip_pll_rate_table rv1108_pll_rates[] = { } static struct rockchip_cpuclk_rate_table rv1108_cpuclk_rates[] __initdata = { - RV1108_CPUCLK_RATE(816000000, 4), - RV1108_CPUCLK_RATE(600000000, 4), - RV1108_CPUCLK_RATE(312000000, 4), + RV1108_CPUCLK_RATE(1608000000, 7), + RV1108_CPUCLK_RATE(1512000000, 7), + RV1108_CPUCLK_RATE(1488000000, 5), + RV1108_CPUCLK_RATE(1416000000, 5), + RV1108_CPUCLK_RATE(1392000000, 5), + RV1108_CPUCLK_RATE(1296000000, 5), + RV1108_CPUCLK_RATE(1200000000, 5), + RV1108_CPUCLK_RATE(1104000000, 5), + RV1108_CPUCLK_RATE(1008000000, 5), + RV1108_CPUCLK_RATE(912000000, 5), + RV1108_CPUCLK_RATE(816000000, 3), + RV1108_CPUCLK_RATE(696000000, 3), + RV1108_CPUCLK_RATE(600000000, 3), + RV1108_CPUCLK_RATE(500000000, 3), + RV1108_CPUCLK_RATE(408000000, 1), + RV1108_CPUCLK_RATE(312000000, 1), + RV1108_CPUCLK_RATE(216000000, 1), + RV1108_CPUCLK_RATE(96000000, 1), }; static const struct rockchip_cpuclk_reg_data rv1108_cpuclk_data = { @@ -105,7 +120,7 @@ static const struct rockchip_cpuclk_reg_data rv1108_cpuclk_data = { .mux_core_alt = 1, .mux_core_main = 0, .mux_core_shift = 8, - .mux_core_mask = 0x1, + .mux_core_mask = 0x3, }; PNAME(mux_pll_p) = { "xin24m", "xin24m"}; @@ -114,30 +129,42 @@ PNAME(mux_armclk_p) = { "apll_core", "gpll_core", "dpll_core" }; PNAME(mux_usb480m_pre_p) = { "usbphy", "xin24m" }; PNAME(mux_hdmiphy_phy_p) = { "hdmiphy", "xin24m" }; PNAME(mux_dclk_hdmiphy_pre_p) = { "dclk_hdmiphy_src_gpll", "dclk_hdmiphy_src_dpll" }; -PNAME(mux_pll_src_4plls_p) = { "dpll", "hdmiphy", "gpll", "usb480m" }; +PNAME(mux_pll_src_4plls_p) = { "dpll", "gpll", "hdmiphy", "usb480m" }; PNAME(mux_pll_src_3plls_p) = { "apll", "gpll", "dpll" }; PNAME(mux_pll_src_2plls_p) = { "dpll", "gpll" }; PNAME(mux_pll_src_apll_gpll_p) = { "apll", "gpll" }; -PNAME(mux_aclk_peri_src_p) = { "aclk_peri_src_dpll", "aclk_peri_src_gpll" }; +PNAME(mux_aclk_peri_src_p) = { "aclk_peri_src_gpll", "aclk_peri_src_dpll" }; PNAME(mux_aclk_bus_src_p) = { "aclk_bus_src_gpll", "aclk_bus_src_apll", "aclk_bus_src_dpll" }; PNAME(mux_mmc_src_p) = { "dpll", "gpll", "xin24m", "usb480m" }; PNAME(mux_pll_src_dpll_gpll_usb480m_p) = { "dpll", "gpll", "usb480m" }; PNAME(mux_uart0_p) = { "uart0_src", "uart0_frac", "xin24m" }; PNAME(mux_uart1_p) = { "uart1_src", "uart1_frac", "xin24m" }; PNAME(mux_uart2_p) = { "uart2_src", "uart2_frac", "xin24m" }; -PNAME(mux_sclk_macphy_p) = { "sclk_macphy_pre", "ext_gmac" }; +PNAME(mux_sclk_mac_p) = { "sclk_mac_pre", "ext_gmac" }; PNAME(mux_i2s0_pre_p) = { "i2s0_src", "i2s0_frac", "ext_i2s", "xin12m" }; PNAME(mux_i2s_out_p) = { "i2s0_pre", "xin12m" }; -PNAME(mux_i2s1_p) = { "i2s1_src", "i2s1_frac", "xin12m" }; -PNAME(mux_i2s2_p) = { "i2s2_src", "i2s2_frac", "xin12m" }; +PNAME(mux_i2s1_p) = { "i2s1_src", "i2s1_frac", "dummy", "xin12m" }; +PNAME(mux_i2s2_p) = { "i2s2_src", "i2s2_frac", "dummy", "xin12m" }; +PNAME(mux_wifi_src_p) = { "gpll", "xin24m" }; +PNAME(mux_cifout_src_p) = { "hdmiphy", "gpll" }; +PNAME(mux_cifout_p) = { "sclk_cifout_src", "xin24m" }; +PNAME(mux_sclk_cif0_src_p) = { "pclk_vip", "clk_cif0_chn_out", "pclkin_cvbs2cif" }; +PNAME(mux_sclk_cif1_src_p) = { "pclk_vip", "clk_cif1_chn_out", "pclkin_cvbs2cif" }; +PNAME(mux_sclk_cif2_src_p) = { "pclk_vip", "clk_cif2_chn_out", "pclkin_cvbs2cif" }; +PNAME(mux_sclk_cif3_src_p) = { "pclk_vip", "clk_cif3_chn_out", "pclkin_cvbs2cif" }; +PNAME(mux_dsp_src_p) = { "dpll", "gpll", "apll", "usb480m" }; +PNAME(mux_dclk_hdmiphy_p) = { "hdmiphy", "xin24m" }; +PNAME(mux_dclk_vop_p) = { "dclk_hdmiphy", "dclk_vop_src" }; +PNAME(mux_hdmi_cec_src_p) = { "dpll", "gpll", "xin24m" }; +PNAME(mux_cvbs_src_p) = { "apll", "io_cvbs_clkin", "hdmiphy", "gpll" }; static struct rockchip_pll_clock rv1108_pll_clks[] __initdata = { [apll] = PLL(pll_rk3399, PLL_APLL, "apll", mux_pll_p, 0, RV1108_PLL_CON(0), - RV1108_PLL_CON(3), 8, 31, 0, rv1108_pll_rates), + RV1108_PLL_CON(3), 8, 0, 0, rv1108_pll_rates), [dpll] = PLL(pll_rk3399, PLL_DPLL, "dpll", mux_pll_p, 0, RV1108_PLL_CON(8), - RV1108_PLL_CON(11), 8, 31, 0, NULL), + RV1108_PLL_CON(11), 8, 1, 0, NULL), [gpll] = PLL(pll_rk3399, PLL_GPLL, "gpll", mux_pll_p, 0, RV1108_PLL_CON(16), - RV1108_PLL_CON(19), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rv1108_pll_rates), + RV1108_PLL_CON(19), 8, 2, 0, rv1108_pll_rates), }; #define MFLAGS CLK_MUX_HIWORD_MASK @@ -170,10 +197,10 @@ static struct rockchip_clk_branch rv1108_i2s2_fracmux __initdata = RV1108_CLKSEL_CON(7), 12, 2, MFLAGS); static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { - MUX(0, "hdmi_phy", mux_hdmiphy_phy_p, CLK_SET_RATE_PARENT, - RV1108_MISC_CON, 13, 2, MFLAGS), + MUX(0, "hdmiphy", mux_hdmiphy_phy_p, CLK_SET_RATE_PARENT, + RV1108_MISC_CON, 13, 1, MFLAGS), MUX(0, "usb480m", mux_usb480m_pre_p, CLK_SET_RATE_PARENT, - RV1108_MISC_CON, 15, 2, MFLAGS), + RV1108_MISC_CON, 15, 1, MFLAGS), /* * Clock-Architecture Diagram 2 */ @@ -197,50 +224,212 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { RV1108_CLKGATE_CON(11), 1, GFLAGS), /* PD_RKVENC */ + COMPOSITE(0, "aclk_rkvenc_pre", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(37), 6, 2, MFLAGS, 0, 5, DFLAGS, + RV1108_CLKGATE_CON(8), 8, GFLAGS), + FACTOR_GATE(0, "hclk_rkvenc_pre", "aclk_rkvenc_pre", 0, 1, 4, + RV1108_CLKGATE_CON(8), 10, GFLAGS), + COMPOSITE(SCLK_VENC_CORE, "clk_venc_core", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(37), 14, 2, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKGATE_CON(8), 9, GFLAGS), + GATE(ACLK_RKVENC, "aclk_rkvenc", "aclk_rkvenc_pre", 0, + RV1108_CLKGATE_CON(19), 8, GFLAGS), + GATE(HCLK_RKVENC, "hclk_rkvenc", "hclk_rkvenc_pre", 0, + RV1108_CLKGATE_CON(19), 9, GFLAGS), + GATE(0, "aclk_rkvenc_niu", "aclk_rkvenc_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(19), 11, GFLAGS), + GATE(0, "hclk_rkvenc_niu", "hclk_rkvenc_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(19), 10, GFLAGS), /* PD_RKVDEC */ + COMPOSITE(SCLK_HEVC_CORE, "sclk_hevc_core", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(36), 6, 2, MFLAGS, 0, 5, DFLAGS, + RV1108_CLKGATE_CON(8), 2, GFLAGS), + FACTOR_GATE(0, "hclk_rkvdec_pre", "sclk_hevc_core", 0, 1, 4, + RV1108_CLKGATE_CON(8), 10, GFLAGS), + COMPOSITE(SCLK_HEVC_CABAC, "clk_hevc_cabac", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(35), 14, 2, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKGATE_CON(8), 1, GFLAGS), + + COMPOSITE(0, "aclk_rkvdec_pre", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(35), 6, 2, MFLAGS, 0, 5, DFLAGS, + RV1108_CLKGATE_CON(8), 0, GFLAGS), + COMPOSITE(0, "aclk_vpu_pre", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(36), 14, 2, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKGATE_CON(8), 3, GFLAGS), + GATE(ACLK_RKVDEC, "aclk_rkvdec", "aclk_rkvdec_pre", 0, + RV1108_CLKGATE_CON(19), 0, GFLAGS), + GATE(ACLK_VPU, "aclk_vpu", "aclk_vpu_pre", 0, + RV1108_CLKGATE_CON(19), 1, GFLAGS), + GATE(HCLK_RKVDEC, "hclk_rkvdec", "hclk_rkvdec_pre", 0, + RV1108_CLKGATE_CON(19), 2, GFLAGS), + GATE(HCLK_VPU, "hclk_vpu", "hclk_rkvdec_pre", 0, + RV1108_CLKGATE_CON(19), 3, GFLAGS), + GATE(0, "aclk_rkvdec_niu", "aclk_rkvdec_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(19), 4, GFLAGS), + GATE(0, "hclk_rkvdec_niu", "hclk_rkvdec_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(19), 5, GFLAGS), + GATE(0, "aclk_vpu_niu", "aclk_vpu_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(19), 6, GFLAGS), /* PD_PMU_wrapper */ COMPOSITE_NOMUX(0, "pmu_24m_ena", "gpll", CLK_IGNORE_UNUSED, RV1108_CLKSEL_CON(38), 0, 5, DFLAGS, RV1108_CLKGATE_CON(8), 12, GFLAGS), - GATE(0, "pmu", "pmu_24m_ena", CLK_IGNORE_UNUSED, + GATE(0, "pclk_pmu", "pmu_24m_ena", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(10), 0, GFLAGS), - GATE(0, "intmem1", "pmu_24m_ena", CLK_IGNORE_UNUSED, + GATE(0, "pclk_intmem1", "pmu_24m_ena", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(10), 1, GFLAGS), - GATE(0, "gpio0_pmu", "pmu_24m_ena", CLK_IGNORE_UNUSED, + GATE(PCLK_GPIO0_PMU, "pclk_gpio0_pmu", "pmu_24m_ena", 0, RV1108_CLKGATE_CON(10), 2, GFLAGS), - GATE(0, "pmugrf", "pmu_24m_ena", CLK_IGNORE_UNUSED, + GATE(0, "pclk_pmugrf", "pmu_24m_ena", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(10), 3, GFLAGS), - GATE(0, "pmu_noc", "pmu_24m_ena", CLK_IGNORE_UNUSED, + GATE(0, "pclk_pmu_niu", "pmu_24m_ena", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(10), 4, GFLAGS), - GATE(0, "i2c0_pmu_pclk", "pmu_24m_ena", CLK_IGNORE_UNUSED, + GATE(PCLK_I2C0_PMU, "pclk_i2c0_pmu", "pmu_24m_ena", 0, RV1108_CLKGATE_CON(10), 5, GFLAGS), - GATE(0, "pwm0_pmu_pclk", "pmu_24m_ena", CLK_IGNORE_UNUSED, + GATE(PCLK_PWM0_PMU, "pclk_pwm0_pmu", "pmu_24m_ena", 0, RV1108_CLKGATE_CON(10), 6, GFLAGS), - COMPOSITE(0, "pwm0_pmu_clk", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, + COMPOSITE(SCLK_PWM0_PMU, "sclk_pwm0_pmu", mux_pll_src_2plls_p, 0, RV1108_CLKSEL_CON(12), 7, 1, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(8), 15, GFLAGS), - COMPOSITE(0, "i2c0_pmu_clk", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, + COMPOSITE(SCLK_I2C0_PMU, "sclk_i2c0_pmu", mux_pll_src_2plls_p, 0, RV1108_CLKSEL_CON(19), 7, 1, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(8), 14, GFLAGS), GATE(0, "pvtm_pmu", "xin24m", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(8), 13, GFLAGS), /* + * Clock-Architecture Diagram 3 + */ + COMPOSITE(SCLK_WIFI, "sclk_wifi", mux_wifi_src_p, 0, + RV1108_CLKSEL_CON(28), 15, 1, MFLAGS, 8, 6, DFLAGS, + RV1108_CLKGATE_CON(9), 8, GFLAGS), + COMPOSITE_NODIV(0, "sclk_cifout_src", mux_cifout_src_p, 0, + RV1108_CLKSEL_CON(40), 8, 1, MFLAGS, + RV1108_CLKGATE_CON(9), 11, GFLAGS), + COMPOSITE_NOGATE(SCLK_CIFOUT, "sclk_cifout", mux_cifout_p, 0, + RV1108_CLKSEL_CON(40), 12, 1, MFLAGS, 0, 5, DFLAGS), + COMPOSITE_NOMUX(SCLK_MIPI_CSI_OUT, "sclk_mipi_csi_out", "xin24m", 0, + RV1108_CLKSEL_CON(41), 0, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 12, GFLAGS), + + GATE(0, "pclk_acodecphy", "pclk_top_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(14), 6, GFLAGS), + GATE(0, "pclk_usbgrf", "pclk_top_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(14), 14, GFLAGS), + + GATE(ACLK_CIF0, "aclk_cif0", "aclk_vio1_pre", 0, + RV1108_CLKGATE_CON(18), 10, GFLAGS), + GATE(HCLK_CIF0, "hclk_cif0", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(18), 10, GFLAGS), + COMPOSITE_NODIV(SCLK_CIF0, "sclk_cif0", mux_sclk_cif0_src_p, 0, + RV1108_CLKSEL_CON(31), 0, 2, MFLAGS, + RV1108_CLKGATE_CON(7), 9, GFLAGS), + GATE(ACLK_CIF1, "aclk_cif1", "aclk_vio1_pre", 0, + RV1108_CLKGATE_CON(17), 6, GFLAGS), + GATE(HCLK_CIF1, "hclk_cif1", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(17), 7, GFLAGS), + COMPOSITE_NODIV(SCLK_CIF1, "sclk_cif1", mux_sclk_cif1_src_p, 0, + RV1108_CLKSEL_CON(31), 2, 2, MFLAGS, + RV1108_CLKGATE_CON(7), 10, GFLAGS), + GATE(ACLK_CIF2, "aclk_cif2", "aclk_vio1_pre", 0, + RV1108_CLKGATE_CON(17), 8, GFLAGS), + GATE(HCLK_CIF2, "hclk_cif2", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(17), 9, GFLAGS), + COMPOSITE_NODIV(SCLK_CIF2, "sclk_cif2", mux_sclk_cif2_src_p, 0, + RV1108_CLKSEL_CON(31), 4, 2, MFLAGS, + RV1108_CLKGATE_CON(7), 11, GFLAGS), + GATE(ACLK_CIF3, "aclk_cif3", "aclk_vio1_pre", 0, + RV1108_CLKGATE_CON(17), 10, GFLAGS), + GATE(HCLK_CIF3, "hclk_cif3", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(17), 11, GFLAGS), + COMPOSITE_NODIV(SCLK_CIF3, "sclk_cif3", mux_sclk_cif3_src_p, 0, + RV1108_CLKSEL_CON(31), 6, 2, MFLAGS, + RV1108_CLKGATE_CON(7), 12, GFLAGS), + GATE(0, "pclk_cif1to4", "pclk_vip", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(7), 8, GFLAGS), + + /* PD_DSP_wrapper */ + COMPOSITE(SCLK_DSP, "sclk_dsp", mux_dsp_src_p, 0, + RV1108_CLKSEL_CON(42), 8, 2, MFLAGS, 0, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 0, GFLAGS), + GATE(0, "clk_dsp_sys_wd", "sclk_dsp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 0, GFLAGS), + GATE(0, "clk_dsp_epp_wd", "sclk_dsp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 1, GFLAGS), + GATE(0, "clk_dsp_edp_wd", "sclk_dsp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 2, GFLAGS), + GATE(0, "clk_dsp_iop_wd", "sclk_dsp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 3, GFLAGS), + GATE(0, "clk_dsp_free", "sclk_dsp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 13, GFLAGS), + COMPOSITE_NOMUX(SCLK_DSP_IOP, "sclk_dsp_iop", "sclk_dsp", 0, + RV1108_CLKSEL_CON(44), 0, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 1, GFLAGS), + COMPOSITE_NOMUX(SCLK_DSP_EPP, "sclk_dsp_epp", "sclk_dsp", 0, + RV1108_CLKSEL_CON(44), 8, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 2, GFLAGS), + COMPOSITE_NOMUX(SCLK_DSP_EDP, "sclk_dsp_edp", "sclk_dsp", 0, + RV1108_CLKSEL_CON(45), 0, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 3, GFLAGS), + COMPOSITE_NOMUX(SCLK_DSP_EDAP, "sclk_dsp_edap", "sclk_dsp", 0, + RV1108_CLKSEL_CON(45), 8, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 4, GFLAGS), + GATE(0, "pclk_dsp_iop_niu", "sclk_dsp_iop", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 4, GFLAGS), + GATE(0, "aclk_dsp_epp_niu", "sclk_dsp_epp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 5, GFLAGS), + GATE(0, "aclk_dsp_edp_niu", "sclk_dsp_edp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 6, GFLAGS), + GATE(0, "pclk_dsp_dbg_niu", "sclk_dsp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 7, GFLAGS), + GATE(0, "aclk_dsp_edap_niu", "sclk_dsp_edap", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 14, GFLAGS), + COMPOSITE_NOMUX(SCLK_DSP_PFM, "sclk_dsp_pfm", "sclk_dsp", 0, + RV1108_CLKSEL_CON(43), 0, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 5, GFLAGS), + COMPOSITE_NOMUX(PCLK_DSP_CFG, "pclk_dsp_cfg", "sclk_dsp", 0, + RV1108_CLKSEL_CON(43), 8, 5, DFLAGS, + RV1108_CLKGATE_CON(9), 6, GFLAGS), + GATE(0, "pclk_dsp_cfg_niu", "pclk_dsp_cfg", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 8, GFLAGS), + GATE(0, "pclk_dsp_pfm_mon", "pclk_dsp_cfg", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 9, GFLAGS), + GATE(0, "pclk_intc", "pclk_dsp_cfg", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 10, GFLAGS), + GATE(0, "pclk_dsp_grf", "pclk_dsp_cfg", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 11, GFLAGS), + GATE(0, "pclk_mailbox", "pclk_dsp_cfg", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 12, GFLAGS), + GATE(0, "aclk_dsp_epp_perf", "sclk_dsp_epp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(16), 15, GFLAGS), + GATE(0, "aclk_dsp_edp_perf", "sclk_dsp_edp", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(11), 8, GFLAGS), + + /* * Clock-Architecture Diagram 4 */ - COMPOSITE(0, "aclk_vio0_2wrap_occ", mux_pll_src_4plls_p, CLK_IGNORE_UNUSED, + COMPOSITE(0, "aclk_vio0_pre", mux_pll_src_4plls_p, CLK_IGNORE_UNUSED, RV1108_CLKSEL_CON(28), 6, 2, MFLAGS, 0, 5, DFLAGS, RV1108_CLKGATE_CON(6), 0, GFLAGS), - GATE(0, "aclk_vio0_pre", "aclk_vio0_2wrap_occ", CLK_IGNORE_UNUSED, + GATE(ACLK_VIO0, "aclk_vio0", "aclk_vio0_pre", 0, RV1108_CLKGATE_CON(17), 0, GFLAGS), COMPOSITE_NOMUX(0, "hclk_vio_pre", "aclk_vio0_pre", 0, RV1108_CLKSEL_CON(29), 0, 5, DFLAGS, RV1108_CLKGATE_CON(7), 2, GFLAGS), + GATE(HCLK_VIO, "hclk_vio", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(17), 2, GFLAGS), COMPOSITE_NOMUX(0, "pclk_vio_pre", "aclk_vio0_pre", 0, RV1108_CLKSEL_CON(29), 8, 5, DFLAGS, RV1108_CLKGATE_CON(7), 3, GFLAGS), + GATE(PCLK_VIO, "pclk_vio", "pclk_vio_pre", 0, + RV1108_CLKGATE_CON(17), 3, GFLAGS), + COMPOSITE(0, "aclk_vio1_pre", mux_pll_src_4plls_p, CLK_IGNORE_UNUSED, + RV1108_CLKSEL_CON(28), 14, 2, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKGATE_CON(6), 1, GFLAGS), + GATE(ACLK_VIO1, "aclk_vio1", "aclk_vio1_pre", 0, + RV1108_CLKGATE_CON(17), 1, GFLAGS), INVERTER(0, "pclk_vip", "ext_vip", RV1108_CLKSEL_CON(31), 8, IFLAGS), @@ -252,8 +441,63 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { RV1108_CLKGATE_CON(6), 5, GFLAGS), GATE(0, "dclk_hdmiphy_src_dpll", "dpll", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(6), 4, GFLAGS), - COMPOSITE_NOGATE(0, "dclk_hdmiphy", mux_dclk_hdmiphy_pre_p, 0, - RV1108_CLKSEL_CON(32), 6, 2, MFLAGS, 8, 6, DFLAGS), + COMPOSITE_NOGATE(0, "dclk_hdmiphy_pre", mux_dclk_hdmiphy_pre_p, 0, + RV1108_CLKSEL_CON(32), 6, 1, MFLAGS, 8, 6, DFLAGS), + COMPOSITE_NOGATE(DCLK_VOP_SRC, "dclk_vop_src", mux_dclk_hdmiphy_pre_p, 0, + RV1108_CLKSEL_CON(32), 6, 1, MFLAGS, 0, 6, DFLAGS), + MUX(DCLK_HDMIPHY, "dclk_hdmiphy", mux_dclk_hdmiphy_p, CLK_SET_RATE_PARENT, + RV1108_CLKSEL_CON(32), 15, 1, MFLAGS), + MUX(DCLK_VOP, "dclk_vop", mux_dclk_vop_p, CLK_SET_RATE_PARENT, + RV1108_CLKSEL_CON(32), 7, 1, MFLAGS), + GATE(ACLK_VOP, "aclk_vop", "aclk_vio0_pre", 0, + RV1108_CLKGATE_CON(18), 0, GFLAGS), + GATE(HCLK_VOP, "hclk_vop", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(18), 1, GFLAGS), + GATE(ACLK_IEP, "aclk_iep", "aclk_vio0_pre", 0, + RV1108_CLKGATE_CON(18), 2, GFLAGS), + GATE(HCLK_IEP, "hclk_iep", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(18), 3, GFLAGS), + + GATE(ACLK_RGA, "aclk_rga", "aclk_vio1_pre", 0, + RV1108_CLKGATE_CON(18), 4, GFLAGS), + GATE(HCLK_RGA, "hclk_rga", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(18), 5, GFLAGS), + COMPOSITE(SCLK_RGA, "sclk_rga", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(33), 6, 2, MFLAGS, 0, 5, DFLAGS, + RV1108_CLKGATE_CON(6), 6, GFLAGS), + + COMPOSITE(SCLK_CVBS_HOST, "sclk_cvbs_host", mux_cvbs_src_p, 0, + RV1108_CLKSEL_CON(33), 13, 2, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKGATE_CON(6), 7, GFLAGS), + FACTOR(0, "sclk_cvbs_27m", "sclk_cvbs_host", 0, 1, 2), + + GATE(SCLK_HDMI_SFR, "sclk_hdmi_sfr", "xin24m", 0, + RV1108_CLKGATE_CON(6), 8, GFLAGS), + + COMPOSITE(SCLK_HDMI_CEC, "sclk_hdmi_cec", mux_hdmi_cec_src_p, 0, + RV1108_CLKSEL_CON(34), 14, 2, MFLAGS, 0, 14, DFLAGS, + RV1108_CLKGATE_CON(6), 9, GFLAGS), + GATE(PCLK_MIPI_DSI, "pclk_mipi_dsi", "pclk_vio_pre", 0, + RV1108_CLKGATE_CON(18), 8, GFLAGS), + GATE(PCLK_HDMI_CTRL, "pclk_hdmi_ctrl", "pclk_vio_pre", 0, + RV1108_CLKGATE_CON(18), 9, GFLAGS), + + GATE(ACLK_ISP, "aclk_isp", "aclk_vio1_pre", 0, + RV1108_CLKGATE_CON(18), 12, GFLAGS), + GATE(HCLK_ISP, "hclk_isp", "hclk_vio_pre", 0, + RV1108_CLKGATE_CON(18), 11, GFLAGS), + COMPOSITE(SCLK_ISP, "sclk_isp", mux_pll_src_4plls_p, 0, + RV1108_CLKSEL_CON(30), 14, 2, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKGATE_CON(6), 3, GFLAGS), + + GATE(0, "clk_dsiphy24m", "xin24m", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(9), 10, GFLAGS), + GATE(0, "pclk_vdacphy", "pclk_top_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(14), 9, GFLAGS), + GATE(0, "pclk_mipi_dsiphy", "pclk_top_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(14), 11, GFLAGS), + GATE(0, "pclk_mipi_csiphy", "pclk_top_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(14), 12, GFLAGS), /* * Clock-Architecture Diagram 5 @@ -261,10 +505,11 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { FACTOR(0, "xin12m", "xin24m", 0, 1, 2), - COMPOSITE(0, "i2s0_src", mux_pll_src_2plls_p, 0, + + COMPOSITE(SCLK_I2S0_SRC, "i2s0_src", mux_pll_src_2plls_p, 0, RV1108_CLKSEL_CON(5), 8, 1, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(2), 0, GFLAGS), - COMPOSITE_FRACMUX(0, "i2s1_frac", "i2s1_src", CLK_SET_RATE_PARENT, + COMPOSITE_FRACMUX(0, "i2s0_frac", "i2s0_src", CLK_SET_RATE_PARENT, RV1108_CLKSEL_CON(8), 0, RV1108_CLKGATE_CON(2), 1, GFLAGS, &rv1108_i2s0_fracmux), @@ -274,7 +519,7 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { RV1108_CLKSEL_CON(5), 15, 1, MFLAGS, RV1108_CLKGATE_CON(2), 3, GFLAGS), - COMPOSITE(0, "i2s1_src", mux_pll_src_2plls_p, 0, + COMPOSITE(SCLK_I2S1_SRC, "i2s1_src", mux_pll_src_2plls_p, 0, RV1108_CLKSEL_CON(6), 8, 1, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(2), 4, GFLAGS), COMPOSITE_FRACMUX(0, "i2s1_frac", "i2s1_src", CLK_SET_RATE_PARENT, @@ -284,7 +529,7 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { GATE(SCLK_I2S1, "sclk_i2s1", "i2s1_pre", CLK_SET_RATE_PARENT, RV1108_CLKGATE_CON(2), 6, GFLAGS), - COMPOSITE(0, "i2s2_src", mux_pll_src_2plls_p, 0, + COMPOSITE(SCLK_I2S2_SRC, "i2s2_src", mux_pll_src_2plls_p, 0, RV1108_CLKSEL_CON(7), 8, 1, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(3), 8, GFLAGS), COMPOSITE_FRACMUX(0, "i2s2_frac", "i2s2_src", CLK_SET_RATE_PARENT, @@ -303,32 +548,53 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { RV1108_CLKGATE_CON(1), 2, GFLAGS), COMPOSITE_NOGATE(ACLK_PRE, "aclk_bus_pre", mux_aclk_bus_src_p, 0, RV1108_CLKSEL_CON(2), 8, 2, MFLAGS, 0, 5, DFLAGS), - COMPOSITE_NOMUX(0, "hclk_bus_pre", "aclk_bus_2wrap_occ", 0, + COMPOSITE_NOMUX(HCLK_BUS, "hclk_bus_pre", "aclk_bus_pre", 0, RV1108_CLKSEL_CON(3), 0, 5, DFLAGS, RV1108_CLKGATE_CON(1), 4, GFLAGS), - COMPOSITE_NOMUX(0, "pclken_bus", "aclk_bus_2wrap_occ", 0, + COMPOSITE_NOMUX(0, "pclk_bus_pre", "aclk_bus_pre", 0, RV1108_CLKSEL_CON(3), 8, 5, DFLAGS, RV1108_CLKGATE_CON(1), 5, GFLAGS), - GATE(0, "pclk_bus_pre", "pclken_bus", CLK_IGNORE_UNUSED, + GATE(PCLK_BUS, "pclk_bus", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(1), 6, GFLAGS), - GATE(0, "pclk_top_pre", "pclken_bus", CLK_IGNORE_UNUSED, + GATE(0, "pclk_top_pre", "pclk_bus_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(1), 7, GFLAGS), - GATE(0, "pclk_ddr_pre", "pclken_bus", CLK_IGNORE_UNUSED, + GATE(0, "pclk_ddr_pre", "pclk_bus_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(1), 8, GFLAGS), - GATE(0, "clk_timer0", "mux_pll_p", CLK_IGNORE_UNUSED, + GATE(SCLK_TIMER0, "clk_timer0", "xin24m", 0, RV1108_CLKGATE_CON(1), 9, GFLAGS), - GATE(0, "clk_timer1", "mux_pll_p", CLK_IGNORE_UNUSED, + GATE(SCLK_TIMER1, "clk_timer1", "xin24m", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(1), 10, GFLAGS), - GATE(0, "pclk_timer", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_TIMER, "pclk_timer", "pclk_bus_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(13), 4, GFLAGS), - COMPOSITE(0, "uart0_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, + GATE(HCLK_I2S0_8CH, "hclk_i2s0_8ch", "hclk_bus_pre", 0, + RV1108_CLKGATE_CON(12), 7, GFLAGS), + GATE(HCLK_I2S1_2CH, "hclk_i2s1_2ch", "hclk_bus_pre", 0, + RV1108_CLKGATE_CON(12), 8, GFLAGS), + GATE(HCLK_I2S2_2CH, "hclk_i2s2_2ch", "hclk_bus_pre", 0, + RV1108_CLKGATE_CON(12), 9, GFLAGS), + + GATE(HCLK_CRYPTO_MST, "hclk_crypto_mst", "hclk_bus_pre", 0, + RV1108_CLKGATE_CON(12), 10, GFLAGS), + GATE(HCLK_CRYPTO_SLV, "hclk_crypto_slv", "hclk_bus_pre", 0, + RV1108_CLKGATE_CON(12), 11, GFLAGS), + COMPOSITE(SCLK_CRYPTO, "sclk_crypto", mux_pll_src_2plls_p, 0, + RV1108_CLKSEL_CON(11), 7, 1, MFLAGS, 0, 5, DFLAGS, + RV1108_CLKGATE_CON(2), 12, GFLAGS), + + COMPOSITE(SCLK_SPI, "sclk_spi", mux_pll_src_2plls_p, 0, + RV1108_CLKSEL_CON(11), 15, 1, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKGATE_CON(3), 0, GFLAGS), + GATE(PCLK_SPI, "pclk_spi", "pclk_bus_pre", 0, + RV1108_CLKGATE_CON(13), 5, GFLAGS), + + COMPOSITE(SCLK_UART0_SRC, "uart0_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, RV1108_CLKSEL_CON(13), 12, 2, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(3), 1, GFLAGS), - COMPOSITE(0, "uart1_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, + COMPOSITE(SCLK_UART1_SRC, "uart1_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, RV1108_CLKSEL_CON(14), 12, 2, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(3), 3, GFLAGS), - COMPOSITE(0, "uart21_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, + COMPOSITE(SCLK_UART2_SRC, "uart2_src", mux_pll_src_dpll_gpll_usb480m_p, CLK_IGNORE_UNUSED, RV1108_CLKSEL_CON(15), 12, 2, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(3), 5, GFLAGS), @@ -344,44 +610,58 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { RV1108_CLKSEL_CON(18), 0, RV1108_CLKGATE_CON(3), 6, GFLAGS, &rv1108_uart2_fracmux), - GATE(PCLK_UART0, "pclk_uart0", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_UART0, "pclk_uart0", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 10, GFLAGS), - GATE(PCLK_UART1, "pclk_uart1", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_UART1, "pclk_uart1", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 11, GFLAGS), - GATE(PCLK_UART2, "pclk_uart2", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_UART2, "pclk_uart2", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 12, GFLAGS), - COMPOSITE(0, "clk_i2c1", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, - RV1108_CLKSEL_CON(19), 15, 2, MFLAGS, 8, 7, DFLAGS, + COMPOSITE(SCLK_I2C1, "clk_i2c1", mux_pll_src_2plls_p, 0, + RV1108_CLKSEL_CON(19), 15, 1, MFLAGS, 8, 7, DFLAGS, RV1108_CLKGATE_CON(3), 7, GFLAGS), - COMPOSITE(0, "clk_i2c2", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, - RV1108_CLKSEL_CON(20), 7, 2, MFLAGS, 0, 7, DFLAGS, + COMPOSITE(SCLK_I2C2, "clk_i2c2", mux_pll_src_2plls_p, 0, + RV1108_CLKSEL_CON(20), 7, 1, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(3), 8, GFLAGS), - COMPOSITE(0, "clk_i2c3", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, - RV1108_CLKSEL_CON(20), 15, 2, MFLAGS, 8, 7, DFLAGS, + COMPOSITE(SCLK_I2C3, "clk_i2c3", mux_pll_src_2plls_p, 0, + RV1108_CLKSEL_CON(20), 15, 1, MFLAGS, 8, 7, DFLAGS, RV1108_CLKGATE_CON(3), 9, GFLAGS), - GATE(0, "pclk_i2c1", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_I2C1, "pclk_i2c1", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 0, GFLAGS), - GATE(0, "pclk_i2c2", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_I2C2, "pclk_i2c2", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 1, GFLAGS), - GATE(0, "pclk_i2c3", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_I2C3, "pclk_i2c3", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 2, GFLAGS), - COMPOSITE(0, "clk_pwm1", mux_pll_src_2plls_p, CLK_IGNORE_UNUSED, + COMPOSITE(SCLK_PWM, "clk_pwm", mux_pll_src_2plls_p, 0, RV1108_CLKSEL_CON(12), 15, 2, MFLAGS, 8, 7, DFLAGS, RV1108_CLKGATE_CON(3), 10, GFLAGS), - GATE(0, "pclk_pwm1", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_PWM, "pclk_pwm", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 6, GFLAGS), - GATE(0, "pclk_wdt", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_WDT, "pclk_wdt", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 3, GFLAGS), - GATE(0, "pclk_gpio1", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 7, GFLAGS), - GATE(0, "pclk_gpio2", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 8, GFLAGS), - GATE(0, "pclk_gpio3", "pclk_bus_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_bus_pre", 0, RV1108_CLKGATE_CON(13), 9, GFLAGS), GATE(0, "pclk_grf", "pclk_bus_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(14), 0, GFLAGS), + GATE(PCLK_EFUSE0, "pclk_efuse0", "pclk_bus_pre", 0, + RV1108_CLKGATE_CON(12), 12, GFLAGS), + GATE(PCLK_EFUSE1, "pclk_efuse1", "pclk_bus_pre", 0, + RV1108_CLKGATE_CON(12), 13, GFLAGS), + GATE(PCLK_TSADC, "pclk_tsadc", "pclk_bus_pre", 0, + RV1108_CLKGATE_CON(13), 13, GFLAGS), + COMPOSITE_NOMUX(SCLK_TSADC, "sclk_tsadc", "xin24m", 0, + RV1108_CLKSEL_CON(21), 0, 10, DFLAGS, + RV1108_CLKGATE_CON(3), 11, GFLAGS), + GATE(PCLK_SARADC, "pclk_saradc", "pclk_bus_pre", 0, + RV1108_CLKGATE_CON(13), 14, GFLAGS), + COMPOSITE_NOMUX(SCLK_SARADC, "sclk_saradc", "xin24m", 0, + RV1108_CLKSEL_CON(22), 0, 10, DFLAGS, + RV1108_CLKGATE_CON(3), 12, GFLAGS), GATE(ACLK_DMAC, "aclk_dmac", "aclk_bus_pre", 0, RV1108_CLKGATE_CON(12), 2, GFLAGS), @@ -397,18 +677,24 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { RV1108_CLKGATE_CON(0), 9, GFLAGS), GATE(0, "gpll_ddr", "gpll", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(0), 10, GFLAGS), - COMPOSITE(0, "ddrphy4x", mux_ddrphy_p, CLK_IGNORE_UNUSED, + COMPOSITE_NOGATE(0, "clk_ddrphy_src", mux_ddrphy_p, CLK_IGNORE_UNUSED, RV1108_CLKSEL_CON(4), 8, 2, MFLAGS, 0, 3, - DFLAGS | CLK_DIVIDER_POWER_OF_TWO, + DFLAGS | CLK_DIVIDER_POWER_OF_TWO), + FACTOR(0, "clk_ddr", "clk_ddrphy_src", 0, 1, 2), + GATE(0, "clk_ddrphy4x", "clk_ddr", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(10), 9, GFLAGS), - GATE(0, "ddrupctl", "ddrphy_pre", CLK_IGNORE_UNUSED, + GATE(0, "pclk_ddrupctl", "pclk_ddr_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(12), 4, GFLAGS), - GATE(0, "ddrc", "ddrphy", CLK_IGNORE_UNUSED, + GATE(0, "nclk_ddrupctl", "clk_ddr", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(12), 5, GFLAGS), - GATE(0, "ddrmon", "ddrphy_pre", CLK_IGNORE_UNUSED, + GATE(0, "pclk_ddrmon", "pclk_ddr_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(12), 6, GFLAGS), GATE(0, "timer_clk", "xin24m", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(0), 11, GFLAGS), + GATE(0, "pclk_mschniu", "pclk_ddr_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(14), 2, GFLAGS), + GATE(0, "pclk_ddrphy", "pclk_ddr_pre", CLK_IGNORE_UNUSED, + RV1108_CLKGATE_CON(14), 4, GFLAGS), /* * Clock-Architecture Diagram 6 @@ -418,23 +704,23 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { COMPOSITE_NOMUX(0, "pclk_periph_pre", "gpll", 0, RV1108_CLKSEL_CON(23), 10, 5, DFLAGS, RV1108_CLKGATE_CON(4), 5, GFLAGS), - GATE(0, "pclk_periph", "pclk_periph_pre", CLK_IGNORE_UNUSED, + GATE(PCLK_PERI, "pclk_periph", "pclk_periph_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(15), 13, GFLAGS), COMPOSITE_NOMUX(0, "hclk_periph_pre", "gpll", 0, RV1108_CLKSEL_CON(23), 5, 5, DFLAGS, RV1108_CLKGATE_CON(4), 4, GFLAGS), - GATE(0, "hclk_periph", "hclk_periph_pre", CLK_IGNORE_UNUSED, + GATE(HCLK_PERI, "hclk_periph", "hclk_periph_pre", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(15), 12, GFLAGS), GATE(0, "aclk_peri_src_dpll", "dpll", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(4), 1, GFLAGS), GATE(0, "aclk_peri_src_gpll", "gpll", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(4), 2, GFLAGS), - COMPOSITE(0, "aclk_periph", mux_aclk_peri_src_p, CLK_IGNORE_UNUSED, - RV1108_CLKSEL_CON(23), 15, 2, MFLAGS, 0, 5, DFLAGS, + COMPOSITE(ACLK_PERI, "aclk_periph", mux_aclk_peri_src_p, 0, + RV1108_CLKSEL_CON(23), 15, 1, MFLAGS, 0, 5, DFLAGS, RV1108_CLKGATE_CON(15), 11, GFLAGS), - COMPOSITE(SCLK_SDMMC, "sclk_sdmmc0", mux_mmc_src_p, 0, + COMPOSITE(SCLK_SDMMC, "sclk_sdmmc", mux_mmc_src_p, 0, RV1108_CLKSEL_CON(25), 8, 2, MFLAGS, 0, 8, DFLAGS, RV1108_CLKGATE_CON(5), 0, GFLAGS), @@ -454,23 +740,31 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { GATE(HCLK_EMMC, "hclk_emmc", "hclk_periph", 0, RV1108_CLKGATE_CON(15), 2, GFLAGS), COMPOSITE(SCLK_NANDC, "sclk_nandc", mux_pll_src_2plls_p, 0, - RV1108_CLKSEL_CON(27), 14, 2, MFLAGS, 8, 5, DFLAGS, + RV1108_CLKSEL_CON(27), 14, 1, MFLAGS, 8, 5, DFLAGS, RV1108_CLKGATE_CON(5), 3, GFLAGS), GATE(HCLK_NANDC, "hclk_nandc", "hclk_periph", 0, RV1108_CLKGATE_CON(15), 3, GFLAGS), + GATE(HCLK_HOST0, "hclk_host0", "hclk_periph", 0, RV1108_CLKGATE_CON(15), 6, GFLAGS), + GATE(0, "hclk_host0_arb", "hclk_periph", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(15), 7, GFLAGS), + GATE(HCLK_OTG, "hclk_otg", "hclk_periph", 0, RV1108_CLKGATE_CON(15), 8, GFLAGS), + GATE(0, "hclk_otg_pmu", "hclk_periph", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(15), 9, GFLAGS), + GATE(SCLK_USBPHY, "clk_usbphy", "xin24m", CLK_IGNORE_UNUSED, RV1108_CLKGATE_CON(5), 5, GFLAGS), + COMPOSITE(SCLK_SFC, "sclk_sfc", mux_pll_src_2plls_p, 0, - RV1108_CLKSEL_CON(27), 7, 2, MFLAGS, 0, 7, DFLAGS, + RV1108_CLKSEL_CON(27), 7, 1, MFLAGS, 0, 7, DFLAGS, RV1108_CLKGATE_CON(5), 4, GFLAGS), GATE(HCLK_SFC, "hclk_sfc", "hclk_periph", 0, RV1108_CLKGATE_CON(15), 10, GFLAGS), - COMPOSITE(0, "sclk_macphy_pre", mux_pll_src_apll_gpll_p, 0, - RV1108_CLKSEL_CON(24), 12, 2, MFLAGS, 0, 5, DFLAGS, + COMPOSITE(SCLK_MAC_PRE, "sclk_mac_pre", mux_pll_src_apll_gpll_p, 0, + RV1108_CLKSEL_CON(24), 12, 1, MFLAGS, 0, 5, DFLAGS, RV1108_CLKGATE_CON(4), 10, GFLAGS), - MUX(0, "sclk_macphy", mux_sclk_macphy_p, CLK_SET_RATE_PARENT, - RV1108_CLKSEL_CON(24), 8, 2, MFLAGS), - GATE(0, "sclk_macphy_rx", "sclk_macphy", 0, RV1108_CLKGATE_CON(4), 8, GFLAGS), - GATE(0, "sclk_mac_ref", "sclk_macphy", 0, RV1108_CLKGATE_CON(4), 6, GFLAGS), - GATE(0, "sclk_mac_refout", "sclk_macphy", 0, RV1108_CLKGATE_CON(4), 7, GFLAGS), + MUX(SCLK_MAC, "sclk_mac", mux_sclk_mac_p, CLK_SET_RATE_PARENT, + RV1108_CLKSEL_CON(24), 8, 1, MFLAGS), + GATE(SCLK_MAC_RX, "sclk_mac_rx", "sclk_mac", 0, RV1108_CLKGATE_CON(4), 8, GFLAGS), + GATE(SCLK_MAC_REF, "sclk_mac_ref", "sclk_mac", 0, RV1108_CLKGATE_CON(4), 6, GFLAGS), + GATE(SCLK_MAC_REFOUT, "sclk_mac_refout", "sclk_mac", 0, RV1108_CLKGATE_CON(4), 7, GFLAGS), + GATE(ACLK_GMAC, "aclk_gmac", "aclk_periph", 0, RV1108_CLKGATE_CON(15), 4, GFLAGS), + GATE(PCLK_GMAC, "pclk_gmac", "pclk_periph", 0, RV1108_CLKGATE_CON(15), 5, GFLAGS), MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "sclk_sdmmc", RV1108_SDMMC_CON0, 1), MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc", RV1108_SDMMC_CON1, 1), @@ -484,10 +778,16 @@ static struct rockchip_clk_branch rv1108_clk_branches[] __initdata = { static const char *const rv1108_critical_clocks[] __initconst = { "aclk_core", - "aclk_bus_src_gpll", + "aclk_bus", + "hclk_bus", + "pclk_bus", "aclk_periph", "hclk_periph", "pclk_periph", + "nclk_ddrupctl", + "pclk_ddrmon", + "pclk_acodecphy", + "pclk_pmu", }; static void __init rv1108_clk_init(struct device_node *np) diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index fe1d393cf678..35dbd63c2f49 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -29,6 +29,7 @@ #include <linux/mfd/syscon.h> #include <linux/regmap.h> #include <linux/reboot.h> +#include <linux/rational.h> #include "clk.h" /** @@ -164,6 +165,40 @@ static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb, return notifier_from_errno(ret); } +/** + * fractional divider must set that denominator is 20 times larger than + * numerator to generate precise clock frequency. + */ +static void rockchip_fractional_approximation(struct clk_hw *hw, + unsigned long rate, unsigned long *parent_rate, + unsigned long *m, unsigned long *n) +{ + struct clk_fractional_divider *fd = to_clk_fd(hw); + unsigned long p_rate, p_parent_rate; + struct clk_hw *p_parent; + unsigned long scale; + + p_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); + if ((rate * 20 > p_rate) && (p_rate % rate != 0)) { + p_parent = clk_hw_get_parent(clk_hw_get_parent(hw)); + p_parent_rate = clk_hw_get_rate(p_parent); + *parent_rate = p_parent_rate; + } + + /* + * Get rate closer to *parent_rate to guarantee there is no overflow + * for m and n. In the result it will be the nearest rate left shifted + * by (scale - fd->nwidth) bits. + */ + scale = fls_long(*parent_rate / rate - 1); + if (scale > fd->nwidth) + rate <<= scale - fd->nwidth; + + rational_best_approximation(rate, *parent_rate, + GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0), + m, n); +} + static struct clk *rockchip_clk_register_frac_branch( struct rockchip_clk_provider *ctx, const char *name, const char *const *parent_names, u8 num_parents, @@ -210,6 +245,7 @@ static struct clk *rockchip_clk_register_frac_branch( div->nwidth = 16; div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift; div->lock = lock; + div->approximation = rockchip_fractional_approximation; div_ops = &clk_fractional_divider_ops; clk = clk_register_composite(NULL, name, parent_names, num_parents, diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c index 1fab56f396d4..b117783ed404 100644 --- a/drivers/clk/samsung/clk-exynos-audss.c +++ b/drivers/clk/samsung/clk-exynos-audss.c @@ -180,7 +180,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) } clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_register_mux(NULL, "mout_audss", mout_audss_p, ARRAY_SIZE(mout_audss_p), - CLK_SET_RATE_NO_REPARENT, + CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, reg_base + ASS_CLK_SRC, 0, 1, 0, &lock); cdclk = devm_clk_get(&pdev->dev, "cdclk"); @@ -195,11 +195,11 @@ static int exynos_audss_clk_probe(struct platform_device *pdev) reg_base + ASS_CLK_SRC, 2, 2, 0, &lock); clk_table[EXYNOS_DOUT_SRP] = clk_hw_register_divider(NULL, "dout_srp", - "mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4, - 0, &lock); + "mout_audss", CLK_SET_RATE_PARENT, + reg_base + ASS_CLK_DIV, 0, 4, 0, &lock); clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(NULL, - "dout_aud_bus", "dout_srp", 0, + "dout_aud_bus", "dout_srp", CLK_SET_RATE_PARENT, reg_base + ASS_CLK_DIV, 4, 4, 0, &lock); clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(NULL, "dout_i2s", diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 9a6476aa7d81..25601967d1cd 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -537,8 +537,8 @@ static const struct samsung_mux_clock exynos5800_mux_clks[] __initconst = { MUX(CLK_MOUT_MX_MSPLL_CCORE, "mout_mx_mspll_ccore", mout_mx_mspll_ccore_p, SRC_TOP7, 16, 2), - MUX(CLK_MOUT_MAU_EPLL, "mout_mau_epll_clk", mout_mau_epll_clk_5800_p, - SRC_TOP7, 20, 2), + MUX_F(CLK_MOUT_MAU_EPLL, "mout_mau_epll_clk", mout_mau_epll_clk_5800_p, + SRC_TOP7, 20, 2, CLK_SET_RATE_PARENT, 0), MUX(0, "sclk_bpll", mout_bpll_p, SRC_TOP7, 24, 1), MUX(0, "mout_epll2", mout_epll2_5800_p, SRC_TOP7, 28, 1), @@ -547,8 +547,8 @@ static const struct samsung_mux_clock exynos5800_mux_clks[] __initconst = { MUX(0, "mout_aclk432_cam", mout_group6_5800_p, SRC_TOP8, 24, 2), MUX(0, "mout_aclk432_scaler", mout_group6_5800_p, SRC_TOP8, 28, 2), - MUX(CLK_MOUT_USER_MAU_EPLL, "mout_user_mau_epll", mout_group16_5800_p, - SRC_TOP9, 8, 1), + MUX_F(CLK_MOUT_USER_MAU_EPLL, "mout_user_mau_epll", mout_group16_5800_p, + SRC_TOP9, 8, 1, CLK_SET_RATE_PARENT, 0), MUX(0, "mout_user_aclk550_cam", mout_group15_5800_p, SRC_TOP9, 16, 1), MUX(0, "mout_user_aclkfl1_550_cam", mout_group13_5800_p, @@ -590,6 +590,8 @@ static const struct samsung_gate_clock exynos5800_gate_clks[] __initconst = { GATE_BUS_TOP, 24, 0, 0), GATE(CLK_ACLK432_SCALER, "aclk432_scaler", "mout_user_aclk432_scaler", GATE_BUS_TOP, 27, CLK_IS_CRITICAL, 0), + GATE(CLK_MAU_EPLL, "mau_epll", "mout_user_mau_epll", + SRC_MASK_TOP7, 20, CLK_SET_RATE_PARENT, 0), }; static const struct samsung_mux_clock exynos5420_mux_clks[] __initconst = { @@ -629,6 +631,11 @@ static const struct samsung_div_clock exynos5420_div_clks[] __initconst = { "mout_aclk400_wcore_bpll", DIV_TOP0, 16, 3), }; +static const struct samsung_gate_clock exynos5420_gate_clks[] __initconst = { + GATE(CLK_MAU_EPLL, "mau_epll", "mout_mau_epll_clk", + SRC_MASK_TOP7, 20, CLK_SET_RATE_PARENT, 0), +}; + static const struct samsung_mux_clock exynos5x_mux_clks[] __initconst = { MUX(0, "mout_user_pclk66_gpio", mout_user_pclk66_gpio_p, SRC_TOP7, 4, 1), @@ -706,7 +713,8 @@ static const struct samsung_mux_clock exynos5x_mux_clks[] __initconst = { MUX(0, "mout_sclk_spll", mout_spll_p, SRC_TOP6, 8, 1), MUX(0, "mout_sclk_ipll", mout_ipll_p, SRC_TOP6, 12, 1), MUX(0, "mout_sclk_rpll", mout_rpll_p, SRC_TOP6, 16, 1), - MUX(CLK_MOUT_EPLL, "mout_sclk_epll", mout_epll_p, SRC_TOP6, 20, 1), + MUX_F(CLK_MOUT_EPLL, "mout_sclk_epll", mout_epll_p, SRC_TOP6, 20, 1, + CLK_SET_RATE_PARENT, 0), MUX(0, "mout_sclk_dpll", mout_dpll_p, SRC_TOP6, 24, 1), MUX(0, "mout_sclk_cpll", mout_cpll_p, SRC_TOP6, 28, 1), @@ -1001,9 +1009,6 @@ static const struct samsung_gate_clock exynos5x_gate_clks[] __initconst = { GATE(0, "aclk300_disp1", "mout_user_aclk300_disp1", SRC_MASK_TOP2, 24, CLK_IS_CRITICAL, 0), - GATE(CLK_MAU_EPLL, "mau_epll", "mout_mau_epll_clk", - SRC_MASK_TOP7, 20, 0, 0), - /* sclk */ GATE(CLK_SCLK_UART0, "sclk_uart0", "dout_uart0", GATE_TOP_SCLK_PERIC, 0, CLK_SET_RATE_PARENT, 0), @@ -1440,6 +1445,8 @@ static void __init exynos5x_clk_init(struct device_node *np, ARRAY_SIZE(exynos5420_mux_clks)); samsung_clk_register_div(ctx, exynos5420_div_clks, ARRAY_SIZE(exynos5420_div_clks)); + samsung_clk_register_gate(ctx, exynos5420_gate_clks, + ARRAY_SIZE(exynos5420_gate_clks)); } else { samsung_clk_register_fixed_factor( ctx, exynos5800_fixed_factor_clks, diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig index 7342928c35cd..6427d0ebe2de 100644 --- a/drivers/clk/sunxi-ng/Kconfig +++ b/drivers/clk/sunxi-ng/Kconfig @@ -11,6 +11,19 @@ config SUN50I_A64_CCU default ARM64 && ARCH_SUNXI depends on (ARM64 && ARCH_SUNXI) || COMPILE_TEST +config SUN4I_A10_CCU + bool "Support for the Allwinner A10/A20 CCU" + select SUNXI_CCU_DIV + select SUNXI_CCU_MULT + select SUNXI_CCU_NK + select SUNXI_CCU_NKM + select SUNXI_CCU_NM + select SUNXI_CCU_MP + select SUNXI_CCU_PHASE + default MACH_SUN4I + default MACH_SUN7I + depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST + config SUN5I_CCU bool "Support for the Allwinner sun5i family CCM" default MACH_SUN5I @@ -48,6 +61,11 @@ config SUN8I_V3S_CCU config SUN8I_DE2_CCU bool "Support for the Allwinner SoCs DE2 CCU" +config SUN8I_R40_CCU + bool "Support for the Allwinner R40 CCU" + default MACH_SUN8I + depends on MACH_SUN8I || COMPILE_TEST + config SUN9I_A80_CCU bool "Support for the Allwinner A80 CCU" default MACH_SUN9I diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile index 45a5910379a5..85a0633c1eac 100644 --- a/drivers/clk/sunxi-ng/Makefile +++ b/drivers/clk/sunxi-ng/Makefile @@ -20,6 +20,7 @@ lib-$(CONFIG_SUNXI_CCU) += ccu_mp.o # SoC support obj-$(CONFIG_SUN50I_A64_CCU) += ccu-sun50i-a64.o +obj-$(CONFIG_SUN4I_A10_CCU) += ccu-sun4i-a10.o obj-$(CONFIG_SUN5I_CCU) += ccu-sun5i.o obj-$(CONFIG_SUN6I_A31_CCU) += ccu-sun6i-a31.o obj-$(CONFIG_SUN8I_A23_CCU) += ccu-sun8i-a23.o @@ -29,6 +30,7 @@ obj-$(CONFIG_SUN8I_H3_CCU) += ccu-sun8i-h3.o obj-$(CONFIG_SUN8I_V3S_CCU) += ccu-sun8i-v3s.o obj-$(CONFIG_SUN8I_DE2_CCU) += ccu-sun8i-de2.o obj-$(CONFIG_SUN8I_R_CCU) += ccu-sun8i-r.o +obj-$(CONFIG_SUN8I_R40_CCU) += ccu-sun8i-r40.o obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80.o obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-de.o obj-$(CONFIG_SUN9I_A80_CCU) += ccu-sun9i-a80-usb.o diff --git a/drivers/clk/sunxi-ng/ccu-sun4i-a10.c b/drivers/clk/sunxi-ng/ccu-sun4i-a10.c new file mode 100644 index 000000000000..286b0049b7b6 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun4i-a10.c @@ -0,0 +1,1456 @@ +/* + * Copyright (c) 2017 Priit Laes <plaes@plaes.org>. + * Copyright (c) 2017 Maxime Ripard. + * Copyright (c) 2017 Jonathan Liu. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of_address.h> + +#include "ccu_common.h" +#include "ccu_reset.h" + +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_mp.h" +#include "ccu_mult.h" +#include "ccu_nk.h" +#include "ccu_nkm.h" +#include "ccu_nkmp.h" +#include "ccu_nm.h" +#include "ccu_phase.h" + +#include "ccu-sun4i-a10.h" + +static struct ccu_nkmp pll_core_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .m = _SUNXI_CCU_DIV(0, 2), + .p = _SUNXI_CCU_DIV(16, 2), + .common = { + .reg = 0x000, + .hw.init = CLK_HW_INIT("pll-core", + "hosc", + &ccu_nkmp_ops, + 0), + }, +}; + +/* + * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from + * the base (2x, 4x and 8x), and one variable divider (the one true + * pll audio). + * + * We don't have any need for the variable divider for now, so we just + * hardcode it to match with the clock names. + */ +#define SUN4I_PLL_AUDIO_REG 0x008 +static struct ccu_nm pll_audio_base_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 7, 0), + .m = _SUNXI_CCU_DIV_OFFSET(0, 5, 0), + .common = { + .reg = 0x008, + .hw.init = CLK_HW_INIT("pll-audio-base", + "hosc", + &ccu_nm_ops, + 0), + }, + +}; + +static struct ccu_mult pll_video0_clk = { + .enable = BIT(31), + .mult = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(0, 7, 0, 9, 127), + .frac = _SUNXI_CCU_FRAC(BIT(15), BIT(14), + 270000000, 297000000), + .common = { + .reg = 0x010, + .features = (CCU_FEATURE_FRACTIONAL | + CCU_FEATURE_ALL_PREDIV), + .prediv = 8, + .hw.init = CLK_HW_INIT("pll-video0", + "hosc", + &ccu_mult_ops, + 0), + }, +}; + +static struct ccu_nkmp pll_ve_sun4i_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .m = _SUNXI_CCU_DIV(0, 2), + .p = _SUNXI_CCU_DIV(16, 2), + .common = { + .reg = 0x018, + .hw.init = CLK_HW_INIT("pll-ve", + "hosc", + &ccu_nkmp_ops, + 0), + }, +}; + +static struct ccu_nk pll_ve_sun7i_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .common = { + .reg = 0x018, + .hw.init = CLK_HW_INIT("pll-ve", + "hosc", + &ccu_nk_ops, + 0), + }, +}; + +static struct ccu_nk pll_ddr_base_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .common = { + .reg = 0x020, + .hw.init = CLK_HW_INIT("pll-ddr-base", + "hosc", + &ccu_nk_ops, + 0), + }, +}; + +static SUNXI_CCU_M(pll_ddr_clk, "pll-ddr", "pll-ddr-base", 0x020, 0, 2, + CLK_IS_CRITICAL); + +static struct ccu_div pll_ddr_other_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(16, 2, CLK_DIVIDER_POWER_OF_TWO), + .common = { + .reg = 0x020, + .hw.init = CLK_HW_INIT("pll-ddr-other", "pll-ddr-base", + &ccu_div_ops, + 0), + }, +}; + +static struct ccu_nk pll_periph_base_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .common = { + .reg = 0x028, + .hw.init = CLK_HW_INIT("pll-periph-base", + "hosc", + &ccu_nk_ops, + 0), + }, +}; + +static CLK_FIXED_FACTOR(pll_periph_clk, "pll-periph", "pll-periph-base", + 2, 1, CLK_SET_RATE_PARENT); + +/* Not documented on A10 */ +static struct ccu_div pll_periph_sata_clk = { + .enable = BIT(14), + .div = _SUNXI_CCU_DIV(0, 2), + .fixed_post_div = 6, + .common = { + .reg = 0x028, + .features = CCU_FEATURE_FIXED_POSTDIV, + .hw.init = CLK_HW_INIT("pll-periph-sata", + "pll-periph-base", + &ccu_div_ops, 0), + }, +}; + +static struct ccu_mult pll_video1_clk = { + .enable = BIT(31), + .mult = _SUNXI_CCU_MULT_OFFSET_MIN_MAX(0, 7, 0, 9, 127), + .frac = _SUNXI_CCU_FRAC(BIT(15), BIT(14), + 270000000, 297000000), + .common = { + .reg = 0x030, + .features = (CCU_FEATURE_FRACTIONAL | + CCU_FEATURE_ALL_PREDIV), + .prediv = 8, + .hw.init = CLK_HW_INIT("pll-video1", + "hosc", + &ccu_mult_ops, + 0), + }, +}; + +/* Not present on A10 */ +static struct ccu_nk pll_gpu_clk = { + .enable = BIT(31), + .n = _SUNXI_CCU_MULT_OFFSET(8, 5, 0), + .k = _SUNXI_CCU_MULT(4, 2), + .common = { + .reg = 0x040, + .hw.init = CLK_HW_INIT("pll-gpu", + "hosc", + &ccu_nk_ops, + 0), + }, +}; + +static SUNXI_CCU_GATE(hosc_clk, "hosc", "osc24M", 0x050, BIT(0), 0); + +static const char *const cpu_parents[] = { "osc32k", "hosc", + "pll-core", "pll-periph" }; +static const struct ccu_mux_fixed_prediv cpu_predivs[] = { + { .index = 3, .div = 3, }, +}; + +#define SUN4I_AHB_REG 0x054 +static struct ccu_mux cpu_clk = { + .mux = { + .shift = 16, + .width = 2, + .fixed_predivs = cpu_predivs, + .n_predivs = ARRAY_SIZE(cpu_predivs), + }, + .common = { + .reg = 0x054, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("cpu", + cpu_parents, + &ccu_mux_ops, + CLK_IS_CRITICAL), + } +}; + +static SUNXI_CCU_M(axi_clk, "axi", "cpu", 0x054, 0, 2, 0); + +static struct ccu_div ahb_sun4i_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), + .common = { + .reg = 0x054, + .hw.init = CLK_HW_INIT("ahb", "axi", &ccu_div_ops, 0), + }, +}; + +static const char *const ahb_sun7i_parents[] = { "axi", "pll-periph", + "pll-periph" }; +static const struct ccu_mux_fixed_prediv ahb_sun7i_predivs[] = { + { .index = 1, .div = 2, }, + { /* Sentinel */ }, +}; +static struct ccu_div ahb_sun7i_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), + .mux = { + .shift = 6, + .width = 2, + .fixed_predivs = ahb_sun7i_predivs, + .n_predivs = ARRAY_SIZE(ahb_sun7i_predivs), + }, + + .common = { + .reg = 0x054, + .hw.init = CLK_HW_INIT_PARENTS("ahb", + ahb_sun7i_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct clk_div_table apb0_div_table[] = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { /* Sentinel */ }, +}; +static SUNXI_CCU_DIV_TABLE(apb0_clk, "apb0", "ahb", + 0x054, 8, 2, apb0_div_table, 0); + +static const char *const apb1_parents[] = { "hosc", "pll-periph", "osc32k" }; +static SUNXI_CCU_MP_WITH_MUX(apb1_clk, "apb1", apb1_parents, 0x058, + 0, 5, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + 0); + +/* Not present on A20 */ +static SUNXI_CCU_GATE(axi_dram_clk, "axi-dram", "ahb", + 0x05c, BIT(31), 0); + +static SUNXI_CCU_GATE(ahb_otg_clk, "ahb-otg", "ahb", + 0x060, BIT(0), 0); +static SUNXI_CCU_GATE(ahb_ehci0_clk, "ahb-ehci0", "ahb", + 0x060, BIT(1), 0); +static SUNXI_CCU_GATE(ahb_ohci0_clk, "ahb-ohci0", "ahb", + 0x060, BIT(2), 0); +static SUNXI_CCU_GATE(ahb_ehci1_clk, "ahb-ehci1", "ahb", + 0x060, BIT(3), 0); +static SUNXI_CCU_GATE(ahb_ohci1_clk, "ahb-ohci1", "ahb", + 0x060, BIT(4), 0); +static SUNXI_CCU_GATE(ahb_ss_clk, "ahb-ss", "ahb", + 0x060, BIT(5), 0); +static SUNXI_CCU_GATE(ahb_dma_clk, "ahb-dma", "ahb", + 0x060, BIT(6), 0); +static SUNXI_CCU_GATE(ahb_bist_clk, "ahb-bist", "ahb", + 0x060, BIT(7), 0); +static SUNXI_CCU_GATE(ahb_mmc0_clk, "ahb-mmc0", "ahb", + 0x060, BIT(8), 0); +static SUNXI_CCU_GATE(ahb_mmc1_clk, "ahb-mmc1", "ahb", + 0x060, BIT(9), 0); +static SUNXI_CCU_GATE(ahb_mmc2_clk, "ahb-mmc2", "ahb", + 0x060, BIT(10), 0); +static SUNXI_CCU_GATE(ahb_mmc3_clk, "ahb-mmc3", "ahb", + 0x060, BIT(11), 0); +static SUNXI_CCU_GATE(ahb_ms_clk, "ahb-ms", "ahb", + 0x060, BIT(12), 0); +static SUNXI_CCU_GATE(ahb_nand_clk, "ahb-nand", "ahb", + 0x060, BIT(13), 0); +static SUNXI_CCU_GATE(ahb_sdram_clk, "ahb-sdram", "ahb", + 0x060, BIT(14), CLK_IS_CRITICAL); + +static SUNXI_CCU_GATE(ahb_ace_clk, "ahb-ace", "ahb", + 0x060, BIT(16), 0); +static SUNXI_CCU_GATE(ahb_emac_clk, "ahb-emac", "ahb", + 0x060, BIT(17), 0); +static SUNXI_CCU_GATE(ahb_ts_clk, "ahb-ts", "ahb", + 0x060, BIT(18), 0); +static SUNXI_CCU_GATE(ahb_spi0_clk, "ahb-spi0", "ahb", + 0x060, BIT(20), 0); +static SUNXI_CCU_GATE(ahb_spi1_clk, "ahb-spi1", "ahb", + 0x060, BIT(21), 0); +static SUNXI_CCU_GATE(ahb_spi2_clk, "ahb-spi2", "ahb", + 0x060, BIT(22), 0); +static SUNXI_CCU_GATE(ahb_spi3_clk, "ahb-spi3", "ahb", + 0x060, BIT(23), 0); +static SUNXI_CCU_GATE(ahb_pata_clk, "ahb-pata", "ahb", + 0x060, BIT(24), 0); +/* Not documented on A20 */ +static SUNXI_CCU_GATE(ahb_sata_clk, "ahb-sata", "ahb", + 0x060, BIT(25), 0); +/* Not present on A20 */ +static SUNXI_CCU_GATE(ahb_gps_clk, "ahb-gps", "ahb", + 0x060, BIT(26), 0); +/* Not present on A10 */ +static SUNXI_CCU_GATE(ahb_hstimer_clk, "ahb-hstimer", "ahb", + 0x060, BIT(28), 0); + +static SUNXI_CCU_GATE(ahb_ve_clk, "ahb-ve", "ahb", + 0x064, BIT(0), 0); +static SUNXI_CCU_GATE(ahb_tvd_clk, "ahb-tvd", "ahb", + 0x064, BIT(1), 0); +static SUNXI_CCU_GATE(ahb_tve0_clk, "ahb-tve0", "ahb", + 0x064, BIT(2), 0); +static SUNXI_CCU_GATE(ahb_tve1_clk, "ahb-tve1", "ahb", + 0x064, BIT(3), 0); +static SUNXI_CCU_GATE(ahb_lcd0_clk, "ahb-lcd0", "ahb", + 0x064, BIT(4), 0); +static SUNXI_CCU_GATE(ahb_lcd1_clk, "ahb-lcd1", "ahb", + 0x064, BIT(5), 0); +static SUNXI_CCU_GATE(ahb_csi0_clk, "ahb-csi0", "ahb", + 0x064, BIT(8), 0); +static SUNXI_CCU_GATE(ahb_csi1_clk, "ahb-csi1", "ahb", + 0x064, BIT(9), 0); +/* Not present on A10 */ +static SUNXI_CCU_GATE(ahb_hdmi1_clk, "ahb-hdmi1", "ahb", + 0x064, BIT(10), 0); +static SUNXI_CCU_GATE(ahb_hdmi0_clk, "ahb-hdmi0", "ahb", + 0x064, BIT(11), 0); +static SUNXI_CCU_GATE(ahb_de_be0_clk, "ahb-de-be0", "ahb", + 0x064, BIT(12), 0); +static SUNXI_CCU_GATE(ahb_de_be1_clk, "ahb-de-be1", "ahb", + 0x064, BIT(13), 0); +static SUNXI_CCU_GATE(ahb_de_fe0_clk, "ahb-de-fe0", "ahb", + 0x064, BIT(14), 0); +static SUNXI_CCU_GATE(ahb_de_fe1_clk, "ahb-de-fe1", "ahb", + 0x064, BIT(15), 0); +/* Not present on A10 */ +static SUNXI_CCU_GATE(ahb_gmac_clk, "ahb-gmac", "ahb", + 0x064, BIT(17), 0); +static SUNXI_CCU_GATE(ahb_mp_clk, "ahb-mp", "ahb", + 0x064, BIT(18), 0); +static SUNXI_CCU_GATE(ahb_gpu_clk, "ahb-gpu", "ahb", + 0x064, BIT(20), 0); + +static SUNXI_CCU_GATE(apb0_codec_clk, "apb0-codec", "apb0", + 0x068, BIT(0), 0); +static SUNXI_CCU_GATE(apb0_spdif_clk, "apb0-spdif", "apb0", + 0x068, BIT(1), 0); +static SUNXI_CCU_GATE(apb0_ac97_clk, "apb0-ac97", "apb0", + 0x068, BIT(2), 0); +static SUNXI_CCU_GATE(apb0_i2s0_clk, "apb0-i2s0", "apb0", + 0x068, BIT(3), 0); +/* Not present on A10 */ +static SUNXI_CCU_GATE(apb0_i2s1_clk, "apb0-i2s1", "apb0", + 0x068, BIT(4), 0); +static SUNXI_CCU_GATE(apb0_pio_clk, "apb0-pio", "apb0", + 0x068, BIT(5), 0); +static SUNXI_CCU_GATE(apb0_ir0_clk, "apb0-ir0", "apb0", + 0x068, BIT(6), 0); +static SUNXI_CCU_GATE(apb0_ir1_clk, "apb0-ir1", "apb0", + 0x068, BIT(7), 0); +/* Not present on A10 */ +static SUNXI_CCU_GATE(apb0_i2s2_clk, "apb0-i2s2", "apb0", + 0x068, BIT(8), 0); +static SUNXI_CCU_GATE(apb0_keypad_clk, "apb0-keypad", "apb0", + 0x068, BIT(10), 0); + +static SUNXI_CCU_GATE(apb1_i2c0_clk, "apb1-i2c0", "apb1", + 0x06c, BIT(0), 0); +static SUNXI_CCU_GATE(apb1_i2c1_clk, "apb1-i2c1", "apb1", + 0x06c, BIT(1), 0); +static SUNXI_CCU_GATE(apb1_i2c2_clk, "apb1-i2c2", "apb1", + 0x06c, BIT(2), 0); +/* Not present on A10 */ +static SUNXI_CCU_GATE(apb1_i2c3_clk, "apb1-i2c3", "apb1", + 0x06c, BIT(3), 0); +static SUNXI_CCU_GATE(apb1_can_clk, "apb1-can", "apb1", + 0x06c, BIT(4), 0); +static SUNXI_CCU_GATE(apb1_scr_clk, "apb1-scr", "apb1", + 0x06c, BIT(5), 0); +static SUNXI_CCU_GATE(apb1_ps20_clk, "apb1-ps20", "apb1", + 0x06c, BIT(6), 0); +static SUNXI_CCU_GATE(apb1_ps21_clk, "apb1-ps21", "apb1", + 0x06c, BIT(7), 0); +/* Not present on A10 */ +static SUNXI_CCU_GATE(apb1_i2c4_clk, "apb1-i2c4", "apb1", + 0x06c, BIT(15), 0); +static SUNXI_CCU_GATE(apb1_uart0_clk, "apb1-uart0", "apb1", + 0x06c, BIT(16), 0); +static SUNXI_CCU_GATE(apb1_uart1_clk, "apb1-uart1", "apb1", + 0x06c, BIT(17), 0); +static SUNXI_CCU_GATE(apb1_uart2_clk, "apb1-uart2", "apb1", + 0x06c, BIT(18), 0); +static SUNXI_CCU_GATE(apb1_uart3_clk, "apb1-uart3", "apb1", + 0x06c, BIT(19), 0); +static SUNXI_CCU_GATE(apb1_uart4_clk, "apb1-uart4", "apb1", + 0x06c, BIT(20), 0); +static SUNXI_CCU_GATE(apb1_uart5_clk, "apb1-uart5", "apb1", + 0x06c, BIT(21), 0); +static SUNXI_CCU_GATE(apb1_uart6_clk, "apb1-uart6", "apb1", + 0x06c, BIT(22), 0); +static SUNXI_CCU_GATE(apb1_uart7_clk, "apb1-uart7", "apb1", + 0x06c, BIT(23), 0); + +static const char *const mod0_default_parents[] = { "hosc", "pll-periph", + "pll-ddr-other" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* Undocumented on A10 */ +static SUNXI_CCU_MP_WITH_MUX_GATE(ms_clk, "ms", mod0_default_parents, 0x084, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, 0x088, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* MMC output and sample clocks are not present on A10 */ +static SUNXI_CCU_PHASE(mmc0_output_clk, "mmc0_output", "mmc0", + 0x088, 8, 3, 0); +static SUNXI_CCU_PHASE(mmc0_sample_clk, "mmc0_sample", "mmc0", + 0x088, 20, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, 0x08c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* MMC output and sample clocks are not present on A10 */ +static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1_output", "mmc1", + 0x08c, 8, 3, 0); +static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1_sample", "mmc1", + 0x08c, 20, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, 0x090, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* MMC output and sample clocks are not present on A10 */ +static SUNXI_CCU_PHASE(mmc2_output_clk, "mmc2_output", "mmc2", + 0x090, 8, 3, 0); +static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2_sample", "mmc2", + 0x090, 20, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc3_clk, "mmc3", mod0_default_parents, 0x094, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* MMC output and sample clocks are not present on A10 */ +static SUNXI_CCU_PHASE(mmc3_output_clk, "mmc3_output", "mmc3", + 0x094, 8, 3, 0); +static SUNXI_CCU_PHASE(mmc3_sample_clk, "mmc3_sample", "mmc3", + 0x094, 20, 3, 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", mod0_default_parents, 0x098, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ss_clk, "ss", mod0_default_parents, 0x09c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi2_clk, "spi2", mod0_default_parents, 0x0a8, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* Undocumented on A10 */ +static SUNXI_CCU_MP_WITH_MUX_GATE(pata_clk, "pata", mod0_default_parents, 0x0ac, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* TODO: Check whether A10 actually supports osc32k as 4th parent? */ +static const char *const ir_parents_sun4i[] = { "hosc", "pll-periph", + "pll-ddr-other" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(ir0_sun4i_clk, "ir0", ir_parents_sun4i, 0x0b0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ir1_sun4i_clk, "ir1", ir_parents_sun4i, 0x0b4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); +static const char *const ir_parents_sun7i[] = { "hosc", "pll-periph", + "pll-ddr-other", "osc32k" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(ir0_sun7i_clk, "ir0", ir_parents_sun7i, 0x0b0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ir1_sun7i_clk, "ir1", ir_parents_sun7i, 0x0b4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char *const audio_parents[] = { "pll-audio-8x", "pll-audio-4x", + "pll-audio-2x", "pll-audio" }; +static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", audio_parents, + 0x0b8, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_MUX_WITH_GATE(ac97_clk, "ac97", audio_parents, + 0x0bc, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +/* Undocumented on A10 */ +static SUNXI_CCU_MUX_WITH_GATE(spdif_clk, "spdif", audio_parents, + 0x0c0, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static const char *const keypad_parents[] = { "hosc", "losc"}; +static const u8 keypad_table[] = { 0, 2 }; +static struct ccu_mp keypad_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(0, 5), + .p = _SUNXI_CCU_DIV(16, 2), + .mux = _SUNXI_CCU_MUX_TABLE(24, 2, keypad_table), + .common = { + .reg = 0x0c4, + .hw.init = CLK_HW_INIT_PARENTS("keypad", + keypad_parents, + &ccu_mp_ops, + 0), + }, +}; + +/* + * SATA supports external clock as parent via BIT(24) and is probably an + * optional crystal or oscillator that can be connected to the + * SATA-CLKM / SATA-CLKP pins. + */ +static const char *const sata_parents[] = {"pll-periph-sata", "sata-ext"}; +static SUNXI_CCU_MUX_WITH_GATE(sata_clk, "sata", sata_parents, + 0x0c8, 24, 1, BIT(31), CLK_SET_RATE_PARENT); + + +static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "pll-periph", + 0x0cc, BIT(6), 0); +static SUNXI_CCU_GATE(usb_ohci1_clk, "usb-ohci1", "pll-periph", + 0x0cc, BIT(7), 0); +static SUNXI_CCU_GATE(usb_phy_clk, "usb-phy", "pll-periph", + 0x0cc, BIT(8), 0); + +/* TODO: GPS CLK 0x0d0 */ + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi3_clk, "spi3", mod0_default_parents, 0x0d4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +/* Not present on A10 */ +static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", audio_parents, + 0x0d8, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +/* Not present on A10 */ +static SUNXI_CCU_MUX_WITH_GATE(i2s2_clk, "i2s2", audio_parents, + 0x0dc, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "pll-ddr", + 0x100, BIT(0), 0); +static SUNXI_CCU_GATE(dram_csi0_clk, "dram-csi0", "pll-ddr", + 0x100, BIT(1), 0); +static SUNXI_CCU_GATE(dram_csi1_clk, "dram-csi1", "pll-ddr", + 0x100, BIT(2), 0); +static SUNXI_CCU_GATE(dram_ts_clk, "dram-ts", "pll-ddr", + 0x100, BIT(3), 0); +static SUNXI_CCU_GATE(dram_tvd_clk, "dram-tvd", "pll-ddr", + 0x100, BIT(4), 0); +static SUNXI_CCU_GATE(dram_tve0_clk, "dram-tve0", "pll-ddr", + 0x100, BIT(5), 0); +static SUNXI_CCU_GATE(dram_tve1_clk, "dram-tve1", "pll-ddr", + 0x100, BIT(6), 0); + +/* Clock seems to be critical only on sun4i */ +static SUNXI_CCU_GATE(dram_out_clk, "dram-out", "pll-ddr", + 0x100, BIT(15), CLK_IS_CRITICAL); +static SUNXI_CCU_GATE(dram_de_fe1_clk, "dram-de-fe1", "pll-ddr", + 0x100, BIT(24), 0); +static SUNXI_CCU_GATE(dram_de_fe0_clk, "dram-de-fe0", "pll-ddr", + 0x100, BIT(25), 0); +static SUNXI_CCU_GATE(dram_de_be0_clk, "dram-de-be0", "pll-ddr", + 0x100, BIT(26), 0); +static SUNXI_CCU_GATE(dram_de_be1_clk, "dram-de-be1", "pll-ddr", + 0x100, BIT(27), 0); +static SUNXI_CCU_GATE(dram_mp_clk, "dram-mp", "pll-ddr", + 0x100, BIT(28), 0); +static SUNXI_CCU_GATE(dram_ace_clk, "dram-ace", "pll-ddr", + 0x100, BIT(29), 0); + +static const char *const de_parents[] = { "pll-video0", "pll-video1", + "pll-ddr-other" }; +static SUNXI_CCU_M_WITH_MUX_GATE(de_be0_clk, "de-be0", de_parents, + 0x104, 0, 4, 24, 2, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(de_be1_clk, "de-be1", de_parents, + 0x108, 0, 4, 24, 2, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(de_fe0_clk, "de-fe0", de_parents, + 0x10c, 0, 4, 24, 2, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(de_fe1_clk, "de-fe1", de_parents, + 0x110, 0, 4, 24, 2, BIT(31), 0); + +/* Undocumented on A10 */ +static SUNXI_CCU_M_WITH_MUX_GATE(de_mp_clk, "de-mp", de_parents, + 0x114, 0, 4, 24, 2, BIT(31), 0); + +static const char *const disp_parents[] = { "pll-video0", "pll-video1", + "pll-video0-2x", "pll-video1-2x" }; +static SUNXI_CCU_MUX_WITH_GATE(tcon0_ch0_clk, "tcon0-ch0-sclk", disp_parents, + 0x118, 24, 2, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_MUX_WITH_GATE(tcon1_ch0_clk, "tcon1-ch0-sclk", disp_parents, + 0x11c, 24, 2, BIT(31), CLK_SET_RATE_PARENT); + +static const char *const csi_sclk_parents[] = { "pll-video0", "pll-ve", + "pll-ddr-other", "pll-periph" }; + +static SUNXI_CCU_M_WITH_MUX_GATE(csi_sclk_clk, "csi-sclk", + csi_sclk_parents, + 0x120, 0, 4, 24, 2, BIT(31), 0); + +/* TVD clock setup for A10 */ +static const char *const tvd_parents[] = { "pll-video0", "pll-video1" }; +static SUNXI_CCU_MUX_WITH_GATE(tvd_sun4i_clk, "tvd", tvd_parents, + 0x128, 24, 1, BIT(31), 0); + +/* TVD clock setup for A20 */ +static SUNXI_CCU_MP_WITH_MUX_GATE(tvd_sclk2_sun7i_clk, + "tvd-sclk2", tvd_parents, + 0x128, + 0, 4, /* M */ + 16, 4, /* P */ + 8, 1, /* mux */ + BIT(15), /* gate */ + 0); + +static SUNXI_CCU_M_WITH_GATE(tvd_sclk1_sun7i_clk, "tvd-sclk1", "tvd-sclk2", + 0x128, 0, 4, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(tcon0_ch1_sclk2_clk, "tcon0-ch1-sclk2", + disp_parents, + 0x12c, 0, 4, 24, 2, BIT(31), + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_GATE(tcon0_ch1_clk, + "tcon0-ch1-sclk1", "tcon0-ch1-sclk2", + 0x12c, 11, 1, BIT(15), + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_MUX_GATE(tcon1_ch1_sclk2_clk, "tcon1-ch1-sclk2", + disp_parents, + 0x130, 0, 4, 24, 2, BIT(31), + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_GATE(tcon1_ch1_clk, + "tcon1-ch1-sclk1", "tcon1-ch1-sclk2", + 0x130, 11, 1, BIT(15), + CLK_SET_RATE_PARENT); + +static const char *const csi_parents[] = { "hosc", "pll-video0", "pll-video1", + "pll-video0-2x", "pll-video1-2x"}; +static const u8 csi_table[] = { 0, 1, 2, 5, 6}; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi0_clk, "csi0", + csi_parents, csi_table, + 0x134, 0, 5, 24, 3, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(csi1_clk, "csi1", + csi_parents, csi_table, + 0x138, 0, 5, 24, 3, BIT(31), 0); + +static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", 0x13c, 16, 8, BIT(31), 0); + +static SUNXI_CCU_GATE(codec_clk, "codec", "pll-audio", + 0x140, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(avs_clk, "avs", "hosc", 0x144, BIT(31), 0); + +static const char *const ace_parents[] = { "pll-ve", "pll-ddr-other" }; +static SUNXI_CCU_M_WITH_MUX_GATE(ace_clk, "ace", ace_parents, + 0x148, 0, 4, 24, 1, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", disp_parents, + 0x150, 0, 4, 24, 2, BIT(31), + CLK_SET_RATE_PARENT); + +static const char *const gpu_parents_sun4i[] = { "pll-video0", "pll-ve", + "pll-ddr-other", + "pll-video1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(gpu_sun4i_clk, "gpu", gpu_parents_sun4i, + 0x154, 0, 4, 24, 2, BIT(31), + CLK_SET_RATE_PARENT); + +static const char *const gpu_parents_sun7i[] = { "pll-video0", "pll-ve", + "pll-ddr-other", "pll-video1", + "pll-gpu" }; +static const u8 gpu_table_sun7i[] = { 0, 1, 2, 3, 4 }; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(gpu_sun7i_clk, "gpu", + gpu_parents_sun7i, gpu_table_sun7i, + 0x154, 0, 4, 24, 3, BIT(31), + CLK_SET_RATE_PARENT); + +static const char *const mbus_sun4i_parents[] = { "hosc", "pll-periph", + "pll-ddr-other" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(mbus_sun4i_clk, "mbus", mbus_sun4i_parents, + 0x15c, 0, 4, 16, 2, 24, 2, BIT(31), + 0); +static const char *const mbus_sun7i_parents[] = { "hosc", "pll-periph-base", + "pll-ddr-other" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(mbus_sun7i_clk, "mbus", mbus_sun7i_parents, + 0x15c, 0, 4, 16, 2, 24, 2, BIT(31), + CLK_IS_CRITICAL); + +static SUNXI_CCU_GATE(hdmi1_slow_clk, "hdmi1-slow", "hosc", 0x178, BIT(31), 0); + +static const char *const hdmi1_parents[] = { "pll-video0", "pll-video1" }; +static const u8 hdmi1_table[] = { 0, 1}; +static SUNXI_CCU_M_WITH_MUX_TABLE_GATE(hdmi1_clk, "hdmi1", + hdmi1_parents, hdmi1_table, + 0x17c, 0, 4, 24, 2, BIT(31), + CLK_SET_RATE_PARENT); + +static const char *const out_parents[] = { "hosc", "osc32k", "hosc" }; +static const struct ccu_mux_fixed_prediv clk_out_predivs[] = { + { .index = 0, .div = 750, }, +}; + +static struct ccu_mp out_a_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(8, 5), + .p = _SUNXI_CCU_DIV(20, 2), + .mux = { + .shift = 24, + .width = 2, + .fixed_predivs = clk_out_predivs, + .n_predivs = ARRAY_SIZE(clk_out_predivs), + }, + .common = { + .reg = 0x1f0, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("out-a", + out_parents, + &ccu_mp_ops, + 0), + }, +}; +static struct ccu_mp out_b_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(8, 5), + .p = _SUNXI_CCU_DIV(20, 2), + .mux = { + .shift = 24, + .width = 2, + .fixed_predivs = clk_out_predivs, + .n_predivs = ARRAY_SIZE(clk_out_predivs), + }, + .common = { + .reg = 0x1f4, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("out-b", + out_parents, + &ccu_mp_ops, + 0), + }, +}; + +static struct ccu_common *sun4i_sun7i_ccu_clks[] = { + &hosc_clk.common, + &pll_core_clk.common, + &pll_audio_base_clk.common, + &pll_video0_clk.common, + &pll_ve_sun4i_clk.common, + &pll_ve_sun7i_clk.common, + &pll_ddr_base_clk.common, + &pll_ddr_clk.common, + &pll_ddr_other_clk.common, + &pll_periph_base_clk.common, + &pll_periph_sata_clk.common, + &pll_video1_clk.common, + &pll_gpu_clk.common, + &cpu_clk.common, + &axi_clk.common, + &axi_dram_clk.common, + &ahb_sun4i_clk.common, + &ahb_sun7i_clk.common, + &apb0_clk.common, + &apb1_clk.common, + &ahb_otg_clk.common, + &ahb_ehci0_clk.common, + &ahb_ohci0_clk.common, + &ahb_ehci1_clk.common, + &ahb_ohci1_clk.common, + &ahb_ss_clk.common, + &ahb_dma_clk.common, + &ahb_bist_clk.common, + &ahb_mmc0_clk.common, + &ahb_mmc1_clk.common, + &ahb_mmc2_clk.common, + &ahb_mmc3_clk.common, + &ahb_ms_clk.common, + &ahb_nand_clk.common, + &ahb_sdram_clk.common, + &ahb_ace_clk.common, + &ahb_emac_clk.common, + &ahb_ts_clk.common, + &ahb_spi0_clk.common, + &ahb_spi1_clk.common, + &ahb_spi2_clk.common, + &ahb_spi3_clk.common, + &ahb_pata_clk.common, + &ahb_sata_clk.common, + &ahb_gps_clk.common, + &ahb_hstimer_clk.common, + &ahb_ve_clk.common, + &ahb_tvd_clk.common, + &ahb_tve0_clk.common, + &ahb_tve1_clk.common, + &ahb_lcd0_clk.common, + &ahb_lcd1_clk.common, + &ahb_csi0_clk.common, + &ahb_csi1_clk.common, + &ahb_hdmi1_clk.common, + &ahb_hdmi0_clk.common, + &ahb_de_be0_clk.common, + &ahb_de_be1_clk.common, + &ahb_de_fe0_clk.common, + &ahb_de_fe1_clk.common, + &ahb_gmac_clk.common, + &ahb_mp_clk.common, + &ahb_gpu_clk.common, + &apb0_codec_clk.common, + &apb0_spdif_clk.common, + &apb0_ac97_clk.common, + &apb0_i2s0_clk.common, + &apb0_i2s1_clk.common, + &apb0_pio_clk.common, + &apb0_ir0_clk.common, + &apb0_ir1_clk.common, + &apb0_i2s2_clk.common, + &apb0_keypad_clk.common, + &apb1_i2c0_clk.common, + &apb1_i2c1_clk.common, + &apb1_i2c2_clk.common, + &apb1_i2c3_clk.common, + &apb1_can_clk.common, + &apb1_scr_clk.common, + &apb1_ps20_clk.common, + &apb1_ps21_clk.common, + &apb1_i2c4_clk.common, + &apb1_uart0_clk.common, + &apb1_uart1_clk.common, + &apb1_uart2_clk.common, + &apb1_uart3_clk.common, + &apb1_uart4_clk.common, + &apb1_uart5_clk.common, + &apb1_uart6_clk.common, + &apb1_uart7_clk.common, + &nand_clk.common, + &ms_clk.common, + &mmc0_clk.common, + &mmc0_output_clk.common, + &mmc0_sample_clk.common, + &mmc1_clk.common, + &mmc1_output_clk.common, + &mmc1_sample_clk.common, + &mmc2_clk.common, + &mmc2_output_clk.common, + &mmc2_sample_clk.common, + &mmc3_clk.common, + &mmc3_output_clk.common, + &mmc3_sample_clk.common, + &ts_clk.common, + &ss_clk.common, + &spi0_clk.common, + &spi1_clk.common, + &spi2_clk.common, + &pata_clk.common, + &ir0_sun4i_clk.common, + &ir1_sun4i_clk.common, + &ir0_sun7i_clk.common, + &ir1_sun7i_clk.common, + &i2s0_clk.common, + &ac97_clk.common, + &spdif_clk.common, + &keypad_clk.common, + &sata_clk.common, + &usb_ohci0_clk.common, + &usb_ohci1_clk.common, + &usb_phy_clk.common, + &spi3_clk.common, + &i2s1_clk.common, + &i2s2_clk.common, + &dram_ve_clk.common, + &dram_csi0_clk.common, + &dram_csi1_clk.common, + &dram_ts_clk.common, + &dram_tvd_clk.common, + &dram_tve0_clk.common, + &dram_tve1_clk.common, + &dram_out_clk.common, + &dram_de_fe1_clk.common, + &dram_de_fe0_clk.common, + &dram_de_be0_clk.common, + &dram_de_be1_clk.common, + &dram_mp_clk.common, + &dram_ace_clk.common, + &de_be0_clk.common, + &de_be1_clk.common, + &de_fe0_clk.common, + &de_fe1_clk.common, + &de_mp_clk.common, + &tcon0_ch0_clk.common, + &tcon1_ch0_clk.common, + &csi_sclk_clk.common, + &tvd_sun4i_clk.common, + &tvd_sclk1_sun7i_clk.common, + &tvd_sclk2_sun7i_clk.common, + &tcon0_ch1_sclk2_clk.common, + &tcon0_ch1_clk.common, + &tcon1_ch1_sclk2_clk.common, + &tcon1_ch1_clk.common, + &csi0_clk.common, + &csi1_clk.common, + &ve_clk.common, + &codec_clk.common, + &avs_clk.common, + &ace_clk.common, + &hdmi_clk.common, + &gpu_sun4i_clk.common, + &gpu_sun7i_clk.common, + &mbus_sun4i_clk.common, + &mbus_sun7i_clk.common, + &hdmi1_slow_clk.common, + &hdmi1_clk.common, + &out_a_clk.common, + &out_b_clk.common +}; + +/* Post-divider for pll-audio is hardcoded to 4 */ +static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", + "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", + "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x", + "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_video0_2x_clk, "pll-video0-2x", + "pll-video0", 1, 2, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_video1_2x_clk, "pll-video1-2x", + "pll-video1", 1, 2, CLK_SET_RATE_PARENT); + + +static struct clk_hw_onecell_data sun4i_a10_hw_clks = { + .hws = { + [CLK_HOSC] = &hosc_clk.common.hw, + [CLK_PLL_CORE] = &pll_core_clk.common.hw, + [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.hw, + [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, + [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, + [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, + [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw, + [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw, + [CLK_PLL_VE] = &pll_ve_sun4i_clk.common.hw, + [CLK_PLL_DDR_BASE] = &pll_ddr_base_clk.common.hw, + [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, + [CLK_PLL_DDR_OTHER] = &pll_ddr_other_clk.common.hw, + [CLK_PLL_PERIPH_BASE] = &pll_periph_base_clk.common.hw, + [CLK_PLL_PERIPH] = &pll_periph_clk.hw, + [CLK_PLL_PERIPH_SATA] = &pll_periph_sata_clk.common.hw, + [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw, + [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw, + [CLK_CPU] = &cpu_clk.common.hw, + [CLK_AXI] = &axi_clk.common.hw, + [CLK_AXI_DRAM] = &axi_dram_clk.common.hw, + [CLK_AHB] = &ahb_sun4i_clk.common.hw, + [CLK_APB0] = &apb0_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_AHB_OTG] = &ahb_otg_clk.common.hw, + [CLK_AHB_EHCI0] = &ahb_ehci0_clk.common.hw, + [CLK_AHB_OHCI0] = &ahb_ohci0_clk.common.hw, + [CLK_AHB_EHCI1] = &ahb_ehci1_clk.common.hw, + [CLK_AHB_OHCI1] = &ahb_ohci1_clk.common.hw, + [CLK_AHB_SS] = &ahb_ss_clk.common.hw, + [CLK_AHB_DMA] = &ahb_dma_clk.common.hw, + [CLK_AHB_BIST] = &ahb_bist_clk.common.hw, + [CLK_AHB_MMC0] = &ahb_mmc0_clk.common.hw, + [CLK_AHB_MMC1] = &ahb_mmc1_clk.common.hw, + [CLK_AHB_MMC2] = &ahb_mmc2_clk.common.hw, + [CLK_AHB_MMC3] = &ahb_mmc3_clk.common.hw, + [CLK_AHB_MS] = &ahb_ms_clk.common.hw, + [CLK_AHB_NAND] = &ahb_nand_clk.common.hw, + [CLK_AHB_SDRAM] = &ahb_sdram_clk.common.hw, + [CLK_AHB_ACE] = &ahb_ace_clk.common.hw, + [CLK_AHB_EMAC] = &ahb_emac_clk.common.hw, + [CLK_AHB_TS] = &ahb_ts_clk.common.hw, + [CLK_AHB_SPI0] = &ahb_spi0_clk.common.hw, + [CLK_AHB_SPI1] = &ahb_spi1_clk.common.hw, + [CLK_AHB_SPI2] = &ahb_spi2_clk.common.hw, + [CLK_AHB_SPI3] = &ahb_spi3_clk.common.hw, + [CLK_AHB_PATA] = &ahb_pata_clk.common.hw, + [CLK_AHB_SATA] = &ahb_sata_clk.common.hw, + [CLK_AHB_GPS] = &ahb_gps_clk.common.hw, + [CLK_AHB_VE] = &ahb_ve_clk.common.hw, + [CLK_AHB_TVD] = &ahb_tvd_clk.common.hw, + [CLK_AHB_TVE0] = &ahb_tve0_clk.common.hw, + [CLK_AHB_TVE1] = &ahb_tve1_clk.common.hw, + [CLK_AHB_LCD0] = &ahb_lcd0_clk.common.hw, + [CLK_AHB_LCD1] = &ahb_lcd1_clk.common.hw, + [CLK_AHB_CSI0] = &ahb_csi0_clk.common.hw, + [CLK_AHB_CSI1] = &ahb_csi1_clk.common.hw, + [CLK_AHB_HDMI0] = &ahb_hdmi0_clk.common.hw, + [CLK_AHB_DE_BE0] = &ahb_de_be0_clk.common.hw, + [CLK_AHB_DE_BE1] = &ahb_de_be1_clk.common.hw, + [CLK_AHB_DE_FE0] = &ahb_de_fe0_clk.common.hw, + [CLK_AHB_DE_FE1] = &ahb_de_fe1_clk.common.hw, + [CLK_AHB_MP] = &ahb_mp_clk.common.hw, + [CLK_AHB_GPU] = &ahb_gpu_clk.common.hw, + [CLK_APB0_CODEC] = &apb0_codec_clk.common.hw, + [CLK_APB0_SPDIF] = &apb0_spdif_clk.common.hw, + [CLK_APB0_AC97] = &apb0_ac97_clk.common.hw, + [CLK_APB0_I2S0] = &apb0_i2s0_clk.common.hw, + [CLK_APB0_PIO] = &apb0_pio_clk.common.hw, + [CLK_APB0_IR0] = &apb0_ir0_clk.common.hw, + [CLK_APB0_IR1] = &apb0_ir1_clk.common.hw, + [CLK_APB0_KEYPAD] = &apb0_keypad_clk.common.hw, + [CLK_APB1_I2C0] = &apb1_i2c0_clk.common.hw, + [CLK_APB1_I2C1] = &apb1_i2c1_clk.common.hw, + [CLK_APB1_I2C2] = &apb1_i2c2_clk.common.hw, + [CLK_APB1_CAN] = &apb1_can_clk.common.hw, + [CLK_APB1_SCR] = &apb1_scr_clk.common.hw, + [CLK_APB1_PS20] = &apb1_ps20_clk.common.hw, + [CLK_APB1_PS21] = &apb1_ps21_clk.common.hw, + [CLK_APB1_UART0] = &apb1_uart0_clk.common.hw, + [CLK_APB1_UART1] = &apb1_uart1_clk.common.hw, + [CLK_APB1_UART2] = &apb1_uart2_clk.common.hw, + [CLK_APB1_UART3] = &apb1_uart3_clk.common.hw, + [CLK_APB1_UART4] = &apb1_uart4_clk.common.hw, + [CLK_APB1_UART5] = &apb1_uart5_clk.common.hw, + [CLK_APB1_UART6] = &apb1_uart6_clk.common.hw, + [CLK_APB1_UART7] = &apb1_uart7_clk.common.hw, + [CLK_NAND] = &nand_clk.common.hw, + [CLK_MS] = &ms_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_MMC3] = &mmc3_clk.common.hw, + [CLK_TS] = &ts_clk.common.hw, + [CLK_SS] = &ss_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_SPI2] = &spi2_clk.common.hw, + [CLK_PATA] = &pata_clk.common.hw, + [CLK_IR0] = &ir0_sun4i_clk.common.hw, + [CLK_IR1] = &ir1_sun4i_clk.common.hw, + [CLK_I2S0] = &i2s0_clk.common.hw, + [CLK_AC97] = &ac97_clk.common.hw, + [CLK_SPDIF] = &spdif_clk.common.hw, + [CLK_KEYPAD] = &keypad_clk.common.hw, + [CLK_SATA] = &sata_clk.common.hw, + [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw, + [CLK_USB_OHCI1] = &usb_ohci1_clk.common.hw, + [CLK_USB_PHY] = &usb_phy_clk.common.hw, + /* CLK_GPS is unimplemented */ + [CLK_SPI3] = &spi3_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI0] = &dram_csi0_clk.common.hw, + [CLK_DRAM_CSI1] = &dram_csi1_clk.common.hw, + [CLK_DRAM_TS] = &dram_ts_clk.common.hw, + [CLK_DRAM_TVD] = &dram_tvd_clk.common.hw, + [CLK_DRAM_TVE0] = &dram_tve0_clk.common.hw, + [CLK_DRAM_TVE1] = &dram_tve1_clk.common.hw, + [CLK_DRAM_OUT] = &dram_out_clk.common.hw, + [CLK_DRAM_DE_FE1] = &dram_de_fe1_clk.common.hw, + [CLK_DRAM_DE_FE0] = &dram_de_fe0_clk.common.hw, + [CLK_DRAM_DE_BE0] = &dram_de_be0_clk.common.hw, + [CLK_DRAM_DE_BE1] = &dram_de_be1_clk.common.hw, + [CLK_DRAM_MP] = &dram_mp_clk.common.hw, + [CLK_DRAM_ACE] = &dram_ace_clk.common.hw, + [CLK_DE_BE0] = &de_be0_clk.common.hw, + [CLK_DE_BE1] = &de_be1_clk.common.hw, + [CLK_DE_FE0] = &de_fe0_clk.common.hw, + [CLK_DE_FE1] = &de_fe1_clk.common.hw, + [CLK_DE_MP] = &de_mp_clk.common.hw, + [CLK_TCON0_CH0] = &tcon0_ch0_clk.common.hw, + [CLK_TCON1_CH0] = &tcon1_ch0_clk.common.hw, + [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw, + [CLK_TVD] = &tvd_sun4i_clk.common.hw, + [CLK_TCON0_CH1_SCLK2] = &tcon0_ch1_sclk2_clk.common.hw, + [CLK_TCON0_CH1] = &tcon0_ch1_clk.common.hw, + [CLK_TCON1_CH1_SCLK2] = &tcon1_ch1_sclk2_clk.common.hw, + [CLK_TCON1_CH1] = &tcon1_ch1_clk.common.hw, + [CLK_CSI0] = &csi0_clk.common.hw, + [CLK_CSI1] = &csi1_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_CODEC] = &codec_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_ACE] = &ace_clk.common.hw, + [CLK_HDMI] = &hdmi_clk.common.hw, + [CLK_GPU] = &gpu_sun7i_clk.common.hw, + [CLK_MBUS] = &mbus_sun4i_clk.common.hw, + }, + .num = CLK_NUMBER_SUN4I, +}; +static struct clk_hw_onecell_data sun7i_a20_hw_clks = { + .hws = { + [CLK_HOSC] = &hosc_clk.common.hw, + [CLK_PLL_CORE] = &pll_core_clk.common.hw, + [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.hw, + [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, + [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, + [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, + [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw, + [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw, + [CLK_PLL_VE] = &pll_ve_sun7i_clk.common.hw, + [CLK_PLL_DDR_BASE] = &pll_ddr_base_clk.common.hw, + [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, + [CLK_PLL_DDR_OTHER] = &pll_ddr_other_clk.common.hw, + [CLK_PLL_PERIPH_BASE] = &pll_periph_base_clk.common.hw, + [CLK_PLL_PERIPH] = &pll_periph_clk.hw, + [CLK_PLL_PERIPH_SATA] = &pll_periph_sata_clk.common.hw, + [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw, + [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw, + [CLK_PLL_GPU] = &pll_gpu_clk.common.hw, + [CLK_CPU] = &cpu_clk.common.hw, + [CLK_AXI] = &axi_clk.common.hw, + [CLK_AHB] = &ahb_sun7i_clk.common.hw, + [CLK_APB0] = &apb0_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_AHB_OTG] = &ahb_otg_clk.common.hw, + [CLK_AHB_EHCI0] = &ahb_ehci0_clk.common.hw, + [CLK_AHB_OHCI0] = &ahb_ohci0_clk.common.hw, + [CLK_AHB_EHCI1] = &ahb_ehci1_clk.common.hw, + [CLK_AHB_OHCI1] = &ahb_ohci1_clk.common.hw, + [CLK_AHB_SS] = &ahb_ss_clk.common.hw, + [CLK_AHB_DMA] = &ahb_dma_clk.common.hw, + [CLK_AHB_BIST] = &ahb_bist_clk.common.hw, + [CLK_AHB_MMC0] = &ahb_mmc0_clk.common.hw, + [CLK_AHB_MMC1] = &ahb_mmc1_clk.common.hw, + [CLK_AHB_MMC2] = &ahb_mmc2_clk.common.hw, + [CLK_AHB_MMC3] = &ahb_mmc3_clk.common.hw, + [CLK_AHB_MS] = &ahb_ms_clk.common.hw, + [CLK_AHB_NAND] = &ahb_nand_clk.common.hw, + [CLK_AHB_SDRAM] = &ahb_sdram_clk.common.hw, + [CLK_AHB_ACE] = &ahb_ace_clk.common.hw, + [CLK_AHB_EMAC] = &ahb_emac_clk.common.hw, + [CLK_AHB_TS] = &ahb_ts_clk.common.hw, + [CLK_AHB_SPI0] = &ahb_spi0_clk.common.hw, + [CLK_AHB_SPI1] = &ahb_spi1_clk.common.hw, + [CLK_AHB_SPI2] = &ahb_spi2_clk.common.hw, + [CLK_AHB_SPI3] = &ahb_spi3_clk.common.hw, + [CLK_AHB_PATA] = &ahb_pata_clk.common.hw, + [CLK_AHB_SATA] = &ahb_sata_clk.common.hw, + [CLK_AHB_HSTIMER] = &ahb_hstimer_clk.common.hw, + [CLK_AHB_VE] = &ahb_ve_clk.common.hw, + [CLK_AHB_TVD] = &ahb_tvd_clk.common.hw, + [CLK_AHB_TVE0] = &ahb_tve0_clk.common.hw, + [CLK_AHB_TVE1] = &ahb_tve1_clk.common.hw, + [CLK_AHB_LCD0] = &ahb_lcd0_clk.common.hw, + [CLK_AHB_LCD1] = &ahb_lcd1_clk.common.hw, + [CLK_AHB_CSI0] = &ahb_csi0_clk.common.hw, + [CLK_AHB_CSI1] = &ahb_csi1_clk.common.hw, + [CLK_AHB_HDMI1] = &ahb_hdmi1_clk.common.hw, + [CLK_AHB_HDMI0] = &ahb_hdmi0_clk.common.hw, + [CLK_AHB_DE_BE0] = &ahb_de_be0_clk.common.hw, + [CLK_AHB_DE_BE1] = &ahb_de_be1_clk.common.hw, + [CLK_AHB_DE_FE0] = &ahb_de_fe0_clk.common.hw, + [CLK_AHB_DE_FE1] = &ahb_de_fe1_clk.common.hw, + [CLK_AHB_GMAC] = &ahb_gmac_clk.common.hw, + [CLK_AHB_MP] = &ahb_mp_clk.common.hw, + [CLK_AHB_GPU] = &ahb_gpu_clk.common.hw, + [CLK_APB0_CODEC] = &apb0_codec_clk.common.hw, + [CLK_APB0_SPDIF] = &apb0_spdif_clk.common.hw, + [CLK_APB0_AC97] = &apb0_ac97_clk.common.hw, + [CLK_APB0_I2S0] = &apb0_i2s0_clk.common.hw, + [CLK_APB0_I2S1] = &apb0_i2s1_clk.common.hw, + [CLK_APB0_PIO] = &apb0_pio_clk.common.hw, + [CLK_APB0_IR0] = &apb0_ir0_clk.common.hw, + [CLK_APB0_IR1] = &apb0_ir1_clk.common.hw, + [CLK_APB0_I2S2] = &apb0_i2s2_clk.common.hw, + [CLK_APB0_KEYPAD] = &apb0_keypad_clk.common.hw, + [CLK_APB1_I2C0] = &apb1_i2c0_clk.common.hw, + [CLK_APB1_I2C1] = &apb1_i2c1_clk.common.hw, + [CLK_APB1_I2C2] = &apb1_i2c2_clk.common.hw, + [CLK_APB1_I2C3] = &apb1_i2c3_clk.common.hw, + [CLK_APB1_CAN] = &apb1_can_clk.common.hw, + [CLK_APB1_SCR] = &apb1_scr_clk.common.hw, + [CLK_APB1_PS20] = &apb1_ps20_clk.common.hw, + [CLK_APB1_PS21] = &apb1_ps21_clk.common.hw, + [CLK_APB1_I2C4] = &apb1_i2c4_clk.common.hw, + [CLK_APB1_UART0] = &apb1_uart0_clk.common.hw, + [CLK_APB1_UART1] = &apb1_uart1_clk.common.hw, + [CLK_APB1_UART2] = &apb1_uart2_clk.common.hw, + [CLK_APB1_UART3] = &apb1_uart3_clk.common.hw, + [CLK_APB1_UART4] = &apb1_uart4_clk.common.hw, + [CLK_APB1_UART5] = &apb1_uart5_clk.common.hw, + [CLK_APB1_UART6] = &apb1_uart6_clk.common.hw, + [CLK_APB1_UART7] = &apb1_uart7_clk.common.hw, + [CLK_NAND] = &nand_clk.common.hw, + [CLK_MS] = &ms_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC0_OUTPUT] = &mmc0_output_clk.common.hw, + [CLK_MMC0_SAMPLE] = &mmc0_sample_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC1_OUTPUT] = &mmc1_output_clk.common.hw, + [CLK_MMC1_SAMPLE] = &mmc1_sample_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_MMC2_OUTPUT] = &mmc2_output_clk.common.hw, + [CLK_MMC2_SAMPLE] = &mmc2_sample_clk.common.hw, + [CLK_MMC3] = &mmc3_clk.common.hw, + [CLK_MMC3_OUTPUT] = &mmc3_output_clk.common.hw, + [CLK_MMC3_SAMPLE] = &mmc3_sample_clk.common.hw, + [CLK_TS] = &ts_clk.common.hw, + [CLK_SS] = &ss_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_SPI2] = &spi2_clk.common.hw, + [CLK_PATA] = &pata_clk.common.hw, + [CLK_IR0] = &ir0_sun7i_clk.common.hw, + [CLK_IR1] = &ir1_sun7i_clk.common.hw, + [CLK_I2S0] = &i2s0_clk.common.hw, + [CLK_AC97] = &ac97_clk.common.hw, + [CLK_SPDIF] = &spdif_clk.common.hw, + [CLK_KEYPAD] = &keypad_clk.common.hw, + [CLK_SATA] = &sata_clk.common.hw, + [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw, + [CLK_USB_OHCI1] = &usb_ohci1_clk.common.hw, + [CLK_USB_PHY] = &usb_phy_clk.common.hw, + /* CLK_GPS is unimplemented */ + [CLK_SPI3] = &spi3_clk.common.hw, + [CLK_I2S1] = &i2s1_clk.common.hw, + [CLK_I2S2] = &i2s2_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI0] = &dram_csi0_clk.common.hw, + [CLK_DRAM_CSI1] = &dram_csi1_clk.common.hw, + [CLK_DRAM_TS] = &dram_ts_clk.common.hw, + [CLK_DRAM_TVD] = &dram_tvd_clk.common.hw, + [CLK_DRAM_TVE0] = &dram_tve0_clk.common.hw, + [CLK_DRAM_TVE1] = &dram_tve1_clk.common.hw, + [CLK_DRAM_OUT] = &dram_out_clk.common.hw, + [CLK_DRAM_DE_FE1] = &dram_de_fe1_clk.common.hw, + [CLK_DRAM_DE_FE0] = &dram_de_fe0_clk.common.hw, + [CLK_DRAM_DE_BE0] = &dram_de_be0_clk.common.hw, + [CLK_DRAM_DE_BE1] = &dram_de_be1_clk.common.hw, + [CLK_DRAM_MP] = &dram_mp_clk.common.hw, + [CLK_DRAM_ACE] = &dram_ace_clk.common.hw, + [CLK_DE_BE0] = &de_be0_clk.common.hw, + [CLK_DE_BE1] = &de_be1_clk.common.hw, + [CLK_DE_FE0] = &de_fe0_clk.common.hw, + [CLK_DE_FE1] = &de_fe1_clk.common.hw, + [CLK_DE_MP] = &de_mp_clk.common.hw, + [CLK_TCON0_CH0] = &tcon0_ch0_clk.common.hw, + [CLK_TCON1_CH0] = &tcon1_ch0_clk.common.hw, + [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw, + [CLK_TVD_SCLK2] = &tvd_sclk2_sun7i_clk.common.hw, + [CLK_TVD] = &tvd_sclk1_sun7i_clk.common.hw, + [CLK_TCON0_CH1_SCLK2] = &tcon0_ch1_sclk2_clk.common.hw, + [CLK_TCON0_CH1] = &tcon0_ch1_clk.common.hw, + [CLK_TCON1_CH1_SCLK2] = &tcon1_ch1_sclk2_clk.common.hw, + [CLK_TCON1_CH1] = &tcon1_ch1_clk.common.hw, + [CLK_CSI0] = &csi0_clk.common.hw, + [CLK_CSI1] = &csi1_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_CODEC] = &codec_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_ACE] = &ace_clk.common.hw, + [CLK_HDMI] = &hdmi_clk.common.hw, + [CLK_GPU] = &gpu_sun7i_clk.common.hw, + [CLK_MBUS] = &mbus_sun7i_clk.common.hw, + [CLK_HDMI1_SLOW] = &hdmi1_slow_clk.common.hw, + [CLK_HDMI1] = &hdmi1_clk.common.hw, + [CLK_OUT_A] = &out_a_clk.common.hw, + [CLK_OUT_B] = &out_b_clk.common.hw, + }, + .num = CLK_NUMBER_SUN7I, +}; + +static struct ccu_reset_map sunxi_a10_a20_ccu_resets[] = { + [RST_USB_PHY0] = { 0x0cc, BIT(0) }, + [RST_USB_PHY1] = { 0x0cc, BIT(1) }, + [RST_USB_PHY2] = { 0x0cc, BIT(2) }, + [RST_GPS] = { 0x0d0, BIT(0) }, + [RST_DE_BE0] = { 0x104, BIT(30) }, + [RST_DE_BE1] = { 0x108, BIT(30) }, + [RST_DE_FE0] = { 0x10c, BIT(30) }, + [RST_DE_FE1] = { 0x110, BIT(30) }, + [RST_DE_MP] = { 0x114, BIT(30) }, + [RST_TVE0] = { 0x118, BIT(29) }, + [RST_TCON0] = { 0x118, BIT(30) }, + [RST_TVE1] = { 0x11c, BIT(29) }, + [RST_TCON1] = { 0x11c, BIT(30) }, + [RST_CSI0] = { 0x134, BIT(30) }, + [RST_CSI1] = { 0x138, BIT(30) }, + [RST_VE] = { 0x13c, BIT(0) }, + [RST_ACE] = { 0x148, BIT(16) }, + [RST_LVDS] = { 0x14c, BIT(0) }, + [RST_GPU] = { 0x154, BIT(30) }, + [RST_HDMI_H] = { 0x170, BIT(0) }, + [RST_HDMI_SYS] = { 0x170, BIT(1) }, + [RST_HDMI_AUDIO_DMA] = { 0x170, BIT(2) }, +}; + +static const struct sunxi_ccu_desc sun4i_a10_ccu_desc = { + .ccu_clks = sun4i_sun7i_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun4i_sun7i_ccu_clks), + + .hw_clks = &sun4i_a10_hw_clks, + + .resets = sunxi_a10_a20_ccu_resets, + .num_resets = ARRAY_SIZE(sunxi_a10_a20_ccu_resets), +}; + +static const struct sunxi_ccu_desc sun7i_a20_ccu_desc = { + .ccu_clks = sun4i_sun7i_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun4i_sun7i_ccu_clks), + + .hw_clks = &sun7i_a20_hw_clks, + + .resets = sunxi_a10_a20_ccu_resets, + .num_resets = ARRAY_SIZE(sunxi_a10_a20_ccu_resets), +}; + +static void __init sun4i_ccu_init(struct device_node *node, + const struct sunxi_ccu_desc *desc) +{ + void __iomem *reg; + u32 val; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("%s: Could not map the clock registers\n", + of_node_full_name(node)); + return; + } + + /* Force the PLL-Audio-1x divider to 4 */ + val = readl(reg + SUN4I_PLL_AUDIO_REG); + val &= ~GENMASK(29, 26); + writel(val | (4 << 26), reg + SUN4I_PLL_AUDIO_REG); + + /* + * Use the peripheral PLL6 as the AHB parent, instead of CPU / + * AXI which have rate changes due to cpufreq. + * + * This is especially a big deal for the HS timer whose parent + * clock is AHB. + * + * NB! These bits are undocumented in A10 manual. + */ + val = readl(reg + SUN4I_AHB_REG); + val &= ~GENMASK(7, 6); + writel(val | (2 << 6), reg + SUN4I_AHB_REG); + + sunxi_ccu_probe(node, reg, desc); +} + +static void __init sun4i_a10_ccu_setup(struct device_node *node) +{ + sun4i_ccu_init(node, &sun4i_a10_ccu_desc); +} +CLK_OF_DECLARE(sun4i_a10_ccu, "allwinner,sun4i-a10-ccu", + sun4i_a10_ccu_setup); + +static void __init sun7i_a20_ccu_setup(struct device_node *node) +{ + sun4i_ccu_init(node, &sun7i_a20_ccu_desc); +} +CLK_OF_DECLARE(sun7i_a20_ccu, "allwinner,sun7i-a20-ccu", + sun7i_a20_ccu_setup); diff --git a/drivers/clk/sunxi-ng/ccu-sun4i-a10.h b/drivers/clk/sunxi-ng/ccu-sun4i-a10.h new file mode 100644 index 000000000000..c5947c7c050e --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun4i-a10.h @@ -0,0 +1,61 @@ +/* + * Copyright 2017 Priit Laes + * + * Priit Laes <plaes@plaes.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CCU_SUN4I_A10_H_ +#define _CCU_SUN4I_A10_H_ + +#include <dt-bindings/clock/sun4i-a10-ccu.h> +#include <dt-bindings/clock/sun7i-a20-ccu.h> +#include <dt-bindings/reset/sun4i-a10-ccu.h> + +/* The HOSC is exported */ +#define CLK_PLL_CORE 2 +#define CLK_PLL_AUDIO_BASE 3 +#define CLK_PLL_AUDIO 4 +#define CLK_PLL_AUDIO_2X 5 +#define CLK_PLL_AUDIO_4X 6 +#define CLK_PLL_AUDIO_8X 7 +#define CLK_PLL_VIDEO0 8 +#define CLK_PLL_VIDEO0_2X 9 +#define CLK_PLL_VE 10 +#define CLK_PLL_DDR_BASE 11 +#define CLK_PLL_DDR 12 +#define CLK_PLL_DDR_OTHER 13 +#define CLK_PLL_PERIPH_BASE 14 +#define CLK_PLL_PERIPH 15 +#define CLK_PLL_PERIPH_SATA 16 +#define CLK_PLL_VIDEO1 17 +#define CLK_PLL_VIDEO1_2X 18 +#define CLK_PLL_GPU 19 + +/* The CPU clock is exported */ +#define CLK_AXI 21 +#define CLK_AXI_DRAM 22 +#define CLK_AHB 23 +#define CLK_APB0 24 +#define CLK_APB1 25 + +/* AHB gates are exported (23..68) */ +/* APB0 gates are exported (69..78) */ +/* APB1 gates are exported (79..95) */ +/* IP module clocks are exported (96..128) */ +/* DRAM gates are exported (129..142)*/ +/* Media (display engine clocks & etc) are exported (143..169) */ + +#define CLK_NUMBER_SUN4I (CLK_MBUS + 1) +#define CLK_NUMBER_SUN7I (CLK_OUT_B + 1) + +#endif /* _CCU_SUN4I_A10_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu-sun5i.c b/drivers/clk/sunxi-ng/ccu-sun5i.c index 31d7ffda9aab..ab9e850b3707 100644 --- a/drivers/clk/sunxi-ng/ccu-sun5i.c +++ b/drivers/clk/sunxi-ng/ccu-sun5i.c @@ -976,8 +976,7 @@ static void __init sun5i_ccu_init(struct device_node *node, reg = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); + pr_err("%pOF: Could not map the clock registers\n", node); return; } diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c index 4d6078fca9ac..8af434815fba 100644 --- a/drivers/clk/sunxi-ng/ccu-sun6i-a31.c +++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.c @@ -1217,8 +1217,7 @@ static void __init sun6i_a31_ccu_setup(struct device_node *node) reg = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); + pr_err("%pOF: Could not map the clock registers\n", node); return; } diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c index 8a753ed0426d..d93b452f0df9 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a23.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a23.c @@ -716,8 +716,7 @@ static void __init sun8i_a23_ccu_setup(struct device_node *node) reg = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); + pr_err("%pOF: Could not map the clock registers\n", node); return; } diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c index 10b38dc46f75..13eb5b23c5e7 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-a33.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-a33.c @@ -777,8 +777,7 @@ static void __init sun8i_a33_ccu_setup(struct device_node *node) reg = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); + pr_err("%pOF: Could not map the clock registers\n", node); return; } diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c index 62e4f0d2b2fc..1729ff6a5aae 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c @@ -135,7 +135,7 @@ static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_de_clk, "pll-de", static const char * const cpux_parents[] = { "osc32k", "osc24M", "pll-cpux" , "pll-cpux" }; static SUNXI_CCU_MUX(cpux_clk, "cpux", cpux_parents, - 0x050, 16, 2, CLK_IS_CRITICAL); + 0x050, 16, 2, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT); static SUNXI_CCU_M(axi_clk, "axi", "cpux", 0x050, 0, 2, 0); @@ -1103,6 +1103,13 @@ static const struct sunxi_ccu_desc sun50i_h5_ccu_desc = { .num_resets = ARRAY_SIZE(sun50i_h5_ccu_resets), }; +static struct ccu_pll_nb sun8i_h3_pll_cpu_nb = { + .common = &pll_cpux_clk.common, + /* copy from pll_cpux_clk */ + .enable = BIT(31), + .lock = BIT(28), +}; + static struct ccu_mux_nb sun8i_h3_cpu_nb = { .common = &cpux_clk.common, .cm = &cpux_clk.mux, @@ -1118,8 +1125,7 @@ static void __init sunxi_h3_h5_ccu_init(struct device_node *node, reg = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); + pr_err("%pOF: Could not map the clock registers\n", node); return; } @@ -1130,6 +1136,10 @@ static void __init sunxi_h3_h5_ccu_init(struct device_node *node, sunxi_ccu_probe(node, reg, desc); + /* Gate then ungate PLL CPU after any rate changes */ + ccu_pll_notifier_register(&sun8i_h3_pll_cpu_nb); + + /* Reparent CPU during PLL CPU rate changes */ ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk, &sun8i_h3_cpu_nb); } diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r.c b/drivers/clk/sunxi-ng/ccu-sun8i-r.c index e54816ec1dbe..71feb7b24e8a 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-r.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-r.c @@ -290,8 +290,7 @@ static void __init sunxi_r_ccu_init(struct device_node *node, reg = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); + pr_err("%pOF: Could not map the clock registers\n", node); return; } diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r.h b/drivers/clk/sunxi-ng/ccu-sun8i-r.h index a7a407f12b56..fb01bffb929d 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-r.h +++ b/drivers/clk/sunxi-ng/ccu-sun8i-r.h @@ -13,7 +13,7 @@ */ #ifndef _CCU_SUN8I_R_H -#define _CCU_SUN8I_R_H_ +#define _CCU_SUN8I_R_H #include <dt-bindings/clock/sun8i-r-ccu.h> #include <dt-bindings/reset/sun8i-r-ccu.h> diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r40.c b/drivers/clk/sunxi-ng/ccu-sun8i-r40.c new file mode 100644 index 000000000000..933f2e68f42a --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-r40.c @@ -0,0 +1,1290 @@ +/* + * Copyright (c) 2017 Icenowy Zheng <icenowy@aosc.io> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/of_address.h> + +#include "ccu_common.h" +#include "ccu_reset.h" + +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_mp.h" +#include "ccu_mult.h" +#include "ccu_nk.h" +#include "ccu_nkm.h" +#include "ccu_nkmp.h" +#include "ccu_nm.h" +#include "ccu_phase.h" + +#include "ccu-sun8i-r40.h" + +/* TODO: The result of N*K is required to be in [10, 88] range. */ +static struct ccu_nkmp pll_cpu_clk = { + .enable = BIT(31), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT(8, 5), + .k = _SUNXI_CCU_MULT(4, 2), + .m = _SUNXI_CCU_DIV(0, 2), + .p = _SUNXI_CCU_DIV_MAX(16, 2, 4), + .common = { + .reg = 0x000, + .hw.init = CLK_HW_INIT("pll-cpu", + "osc24M", + &ccu_nkmp_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +/* + * The Audio PLL is supposed to have 4 outputs: 3 fixed factors from + * the base (2x, 4x and 8x), and one variable divider (the one true + * pll audio). + * + * We don't have any need for the variable divider for now, so we just + * hardcode it to match with the clock names + */ +#define SUN8I_R40_PLL_AUDIO_REG 0x008 + +static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_audio_base_clk, "pll-audio-base", + "osc24M", 0x008, + 8, 7, /* N */ + 0, 5, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* TODO: The result of N/M is required to be in [8, 25] range. */ +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video0_clk, "pll-video0", + "osc24M", 0x0010, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* TODO: The result of N/M is required to be in [8, 25] range. */ +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_ve_clk, "pll-ve", + "osc24M", 0x0018, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* TODO: The result of N*K is required to be in [10, 77] range. */ +static SUNXI_CCU_NKM_WITH_GATE_LOCK(pll_ddr0_clk, "pll-ddr0", + "osc24M", 0x020, + 8, 5, /* N */ + 4, 2, /* K */ + 0, 2, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* TODO: The result of N*K is required to be in [21, 58] range. */ +static struct ccu_nk pll_periph0_clk = { + .enable = BIT(31), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT(8, 5), + .k = _SUNXI_CCU_MULT(4, 2), + .fixed_post_div = 2, + .common = { + .reg = 0x028, + .features = CCU_FEATURE_FIXED_POSTDIV, + .hw.init = CLK_HW_INIT("pll-periph0", "osc24M", + &ccu_nk_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static struct ccu_div pll_periph0_sata_clk = { + .enable = BIT(24), + .div = _SUNXI_CCU_DIV(0, 2), + /* + * The formula of pll-periph0 (1x) is 24MHz*N*K/2, and the formula + * of pll-periph0-sata is 24MHz*N*K/M/6, so the postdiv here is + * 6/2 = 3. + */ + .fixed_post_div = 3, + .common = { + .reg = 0x028, + .features = CCU_FEATURE_FIXED_POSTDIV, + .hw.init = CLK_HW_INIT("pll-periph0-sata", + "pll-periph0", + &ccu_div_ops, 0), + }, +}; + +/* TODO: The result of N*K is required to be in [21, 58] range. */ +static struct ccu_nk pll_periph1_clk = { + .enable = BIT(31), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT(8, 5), + .k = _SUNXI_CCU_MULT(4, 2), + .fixed_post_div = 2, + .common = { + .reg = 0x02c, + .features = CCU_FEATURE_FIXED_POSTDIV, + .hw.init = CLK_HW_INIT("pll-periph1", "osc24M", + &ccu_nk_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +/* TODO: The result of N/M is required to be in [8, 25] range. */ +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_video1_clk, "pll-video1", + "osc24M", 0x030, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static struct ccu_nkm pll_sata_clk = { + .enable = BIT(31), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT(8, 5), + .k = _SUNXI_CCU_MULT(4, 2), + .m = _SUNXI_CCU_DIV(0, 2), + .fixed_post_div = 6, + .common = { + .reg = 0x034, + .features = CCU_FEATURE_FIXED_POSTDIV, + .hw.init = CLK_HW_INIT("pll-sata", "osc24M", + &ccu_nkm_ops, + CLK_SET_RATE_UNGATE), + }, +}; + +static const char * const pll_sata_out_parents[] = { "pll-sata", + "pll-periph0-sata" }; +static SUNXI_CCU_MUX_WITH_GATE(pll_sata_out_clk, "pll-sata-out", + pll_sata_out_parents, 0x034, + 30, 1, /* mux */ + BIT(14), /* gate */ + CLK_SET_RATE_PARENT); + +/* TODO: The result of N/M is required to be in [8, 25] range. */ +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_gpu_clk, "pll-gpu", + "osc24M", 0x038, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* + * The MIPI PLL has 2 modes: "MIPI" and "HDMI". + * + * The MIPI mode is a standard NKM-style clock. The HDMI mode is an + * integer / fractional clock with switchable multipliers and dividers. + * This is not supported here. We hardcode the PLL to MIPI mode. + * + * TODO: In the MIPI mode, M/N is required to be equal or lesser than 3, + * which cannot be implemented now. + */ +#define SUN8I_R40_PLL_MIPI_REG 0x040 + +static const char * const pll_mipi_parents[] = { "pll-video0" }; +static struct ccu_nkm pll_mipi_clk = { + .enable = BIT(31) | BIT(23) | BIT(22), + .lock = BIT(28), + .n = _SUNXI_CCU_MULT(8, 4), + .k = _SUNXI_CCU_MULT_MIN(4, 2, 2), + .m = _SUNXI_CCU_DIV(0, 4), + .mux = _SUNXI_CCU_MUX(21, 1), + .common = { + .reg = 0x040, + .hw.init = CLK_HW_INIT_PARENTS("pll-mipi", + pll_mipi_parents, + &ccu_nkm_ops, + CLK_SET_RATE_UNGATE) + }, +}; + +/* TODO: The result of N/M is required to be in [8, 25] range. */ +static SUNXI_CCU_NM_WITH_FRAC_GATE_LOCK(pll_de_clk, "pll-de", + "osc24M", 0x048, + 8, 7, /* N */ + 0, 4, /* M */ + BIT(24), /* frac enable */ + BIT(25), /* frac select */ + 270000000, /* frac rate 0 */ + 297000000, /* frac rate 1 */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +/* TODO: The N factor is required to be in [16, 75] range. */ +static SUNXI_CCU_NM_WITH_GATE_LOCK(pll_ddr1_clk, "pll-ddr1", + "osc24M", 0x04c, + 8, 7, /* N */ + 0, 2, /* M */ + BIT(31), /* gate */ + BIT(28), /* lock */ + CLK_SET_RATE_UNGATE); + +static const char * const cpu_parents[] = { "osc32k", "osc24M", + "pll-cpu", "pll-cpu" }; +static SUNXI_CCU_MUX(cpu_clk, "cpu", cpu_parents, + 0x050, 16, 2, CLK_IS_CRITICAL | CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M(axi_clk, "axi", "cpu", 0x050, 0, 2, 0); + +static const char * const ahb1_parents[] = { "osc32k", "osc24M", + "axi", "pll-periph0" }; +static const struct ccu_mux_var_prediv ahb1_predivs[] = { + { .index = 3, .shift = 6, .width = 2 }, +}; +static struct ccu_div ahb1_clk = { + .div = _SUNXI_CCU_DIV_FLAGS(4, 2, CLK_DIVIDER_POWER_OF_TWO), + + .mux = { + .shift = 12, + .width = 2, + + .var_predivs = ahb1_predivs, + .n_var_predivs = ARRAY_SIZE(ahb1_predivs), + }, + + .common = { + .reg = 0x054, + .features = CCU_FEATURE_VARIABLE_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("ahb1", + ahb1_parents, + &ccu_div_ops, + 0), + }, +}; + +static struct clk_div_table apb1_div_table[] = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { /* Sentinel */ }, +}; +static SUNXI_CCU_DIV_TABLE(apb1_clk, "apb1", "ahb1", + 0x054, 8, 2, apb1_div_table, 0); + +static const char * const apb2_parents[] = { "osc32k", "osc24M", + "pll-periph0-2x", + "pll-periph0-2x" }; +static SUNXI_CCU_MP_WITH_MUX(apb2_clk, "apb2", apb2_parents, 0x058, + 0, 5, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + 0); + +static SUNXI_CCU_GATE(bus_mipi_dsi_clk, "bus-mipi-dsi", "ahb1", + 0x060, BIT(1), 0); +static SUNXI_CCU_GATE(bus_ce_clk, "bus-ce", "ahb1", + 0x060, BIT(5), 0); +static SUNXI_CCU_GATE(bus_dma_clk, "bus-dma", "ahb1", + 0x060, BIT(6), 0); +static SUNXI_CCU_GATE(bus_mmc0_clk, "bus-mmc0", "ahb1", + 0x060, BIT(8), 0); +static SUNXI_CCU_GATE(bus_mmc1_clk, "bus-mmc1", "ahb1", + 0x060, BIT(9), 0); +static SUNXI_CCU_GATE(bus_mmc2_clk, "bus-mmc2", "ahb1", + 0x060, BIT(10), 0); +static SUNXI_CCU_GATE(bus_mmc3_clk, "bus-mmc3", "ahb1", + 0x060, BIT(11), 0); +static SUNXI_CCU_GATE(bus_nand_clk, "bus-nand", "ahb1", + 0x060, BIT(13), 0); +static SUNXI_CCU_GATE(bus_dram_clk, "bus-dram", "ahb1", + 0x060, BIT(14), 0); +static SUNXI_CCU_GATE(bus_emac_clk, "bus-emac", "ahb1", + 0x060, BIT(17), 0); +static SUNXI_CCU_GATE(bus_ts_clk, "bus-ts", "ahb1", + 0x060, BIT(18), 0); +static SUNXI_CCU_GATE(bus_hstimer_clk, "bus-hstimer", "ahb1", + 0x060, BIT(19), 0); +static SUNXI_CCU_GATE(bus_spi0_clk, "bus-spi0", "ahb1", + 0x060, BIT(20), 0); +static SUNXI_CCU_GATE(bus_spi1_clk, "bus-spi1", "ahb1", + 0x060, BIT(21), 0); +static SUNXI_CCU_GATE(bus_spi2_clk, "bus-spi2", "ahb1", + 0x060, BIT(22), 0); +static SUNXI_CCU_GATE(bus_spi3_clk, "bus-spi3", "ahb1", + 0x060, BIT(23), 0); +static SUNXI_CCU_GATE(bus_sata_clk, "bus-sata", "ahb1", + 0x060, BIT(24), 0); +static SUNXI_CCU_GATE(bus_otg_clk, "bus-otg", "ahb1", + 0x060, BIT(25), 0); +static SUNXI_CCU_GATE(bus_ehci0_clk, "bus-ehci0", "ahb1", + 0x060, BIT(26), 0); +static SUNXI_CCU_GATE(bus_ehci1_clk, "bus-ehci1", "ahb1", + 0x060, BIT(27), 0); +static SUNXI_CCU_GATE(bus_ehci2_clk, "bus-ehci2", "ahb1", + 0x060, BIT(28), 0); +static SUNXI_CCU_GATE(bus_ohci0_clk, "bus-ohci0", "ahb1", + 0x060, BIT(29), 0); +static SUNXI_CCU_GATE(bus_ohci1_clk, "bus-ohci1", "ahb1", + 0x060, BIT(30), 0); +static SUNXI_CCU_GATE(bus_ohci2_clk, "bus-ohci2", "ahb1", + 0x060, BIT(31), 0); + +static SUNXI_CCU_GATE(bus_ve_clk, "bus-ve", "ahb1", + 0x064, BIT(0), 0); +static SUNXI_CCU_GATE(bus_mp_clk, "bus-mp", "ahb1", + 0x064, BIT(2), 0); +static SUNXI_CCU_GATE(bus_deinterlace_clk, "bus-deinterlace", "ahb1", + 0x064, BIT(5), 0); +static SUNXI_CCU_GATE(bus_csi0_clk, "bus-csi0", "ahb1", + 0x064, BIT(8), 0); +static SUNXI_CCU_GATE(bus_csi1_clk, "bus-csi1", "ahb1", + 0x064, BIT(9), 0); +static SUNXI_CCU_GATE(bus_hdmi0_clk, "bus-hdmi0", "ahb1", + 0x064, BIT(10), 0); +static SUNXI_CCU_GATE(bus_hdmi1_clk, "bus-hdmi1", "ahb1", + 0x064, BIT(11), 0); +static SUNXI_CCU_GATE(bus_de_clk, "bus-de", "ahb1", + 0x064, BIT(12), 0); +static SUNXI_CCU_GATE(bus_tve0_clk, "bus-tve0", "ahb1", + 0x064, BIT(13), 0); +static SUNXI_CCU_GATE(bus_tve1_clk, "bus-tve1", "ahb1", + 0x064, BIT(14), 0); +static SUNXI_CCU_GATE(bus_tve_top_clk, "bus-tve-top", "ahb1", + 0x064, BIT(15), 0); +static SUNXI_CCU_GATE(bus_gmac_clk, "bus-gmac", "ahb1", + 0x064, BIT(17), 0); +static SUNXI_CCU_GATE(bus_gpu_clk, "bus-gpu", "ahb1", + 0x064, BIT(20), 0); +static SUNXI_CCU_GATE(bus_tvd0_clk, "bus-tvd0", "ahb1", + 0x064, BIT(21), 0); +static SUNXI_CCU_GATE(bus_tvd1_clk, "bus-tvd1", "ahb1", + 0x064, BIT(22), 0); +static SUNXI_CCU_GATE(bus_tvd2_clk, "bus-tvd2", "ahb1", + 0x064, BIT(23), 0); +static SUNXI_CCU_GATE(bus_tvd3_clk, "bus-tvd3", "ahb1", + 0x064, BIT(24), 0); +static SUNXI_CCU_GATE(bus_tvd_top_clk, "bus-tvd-top", "ahb1", + 0x064, BIT(25), 0); +static SUNXI_CCU_GATE(bus_tcon_lcd0_clk, "bus-tcon-lcd0", "ahb1", + 0x064, BIT(26), 0); +static SUNXI_CCU_GATE(bus_tcon_lcd1_clk, "bus-tcon-lcd1", "ahb1", + 0x064, BIT(27), 0); +static SUNXI_CCU_GATE(bus_tcon_tv0_clk, "bus-tcon-tv0", "ahb1", + 0x064, BIT(28), 0); +static SUNXI_CCU_GATE(bus_tcon_tv1_clk, "bus-tcon-tv1", "ahb1", + 0x064, BIT(29), 0); +static SUNXI_CCU_GATE(bus_tcon_top_clk, "bus-tcon-top", "ahb1", + 0x064, BIT(30), 0); + +static SUNXI_CCU_GATE(bus_codec_clk, "bus-codec", "apb1", + 0x068, BIT(0), 0); +static SUNXI_CCU_GATE(bus_spdif_clk, "bus-spdif", "apb1", + 0x068, BIT(1), 0); +static SUNXI_CCU_GATE(bus_ac97_clk, "bus-ac97", "apb1", + 0x068, BIT(2), 0); +static SUNXI_CCU_GATE(bus_pio_clk, "bus-pio", "apb1", + 0x068, BIT(5), 0); +static SUNXI_CCU_GATE(bus_ir0_clk, "bus-ir0", "apb1", + 0x068, BIT(6), 0); +static SUNXI_CCU_GATE(bus_ir1_clk, "bus-ir1", "apb1", + 0x068, BIT(7), 0); +static SUNXI_CCU_GATE(bus_ths_clk, "bus-ths", "apb1", + 0x068, BIT(8), 0); +static SUNXI_CCU_GATE(bus_keypad_clk, "bus-keypad", "apb1", + 0x068, BIT(10), 0); +static SUNXI_CCU_GATE(bus_i2s0_clk, "bus-i2s0", "apb1", + 0x068, BIT(12), 0); +static SUNXI_CCU_GATE(bus_i2s1_clk, "bus-i2s1", "apb1", + 0x068, BIT(13), 0); +static SUNXI_CCU_GATE(bus_i2s2_clk, "bus-i2s2", "apb1", + 0x068, BIT(14), 0); + +static SUNXI_CCU_GATE(bus_i2c0_clk, "bus-i2c0", "apb2", + 0x06c, BIT(0), 0); +static SUNXI_CCU_GATE(bus_i2c1_clk, "bus-i2c1", "apb2", + 0x06c, BIT(1), 0); +static SUNXI_CCU_GATE(bus_i2c2_clk, "bus-i2c2", "apb2", + 0x06c, BIT(2), 0); +static SUNXI_CCU_GATE(bus_i2c3_clk, "bus-i2c3", "apb2", + 0x06c, BIT(3), 0); +/* + * In datasheet here's "Reserved", however the gate exists in BSP soucre + * code. + */ +static SUNXI_CCU_GATE(bus_can_clk, "bus-can", "apb2", + 0x06c, BIT(4), 0); +static SUNXI_CCU_GATE(bus_scr_clk, "bus-scr", "apb2", + 0x06c, BIT(5), 0); +static SUNXI_CCU_GATE(bus_ps20_clk, "bus-ps20", "apb2", + 0x06c, BIT(6), 0); +static SUNXI_CCU_GATE(bus_ps21_clk, "bus-ps21", "apb2", + 0x06c, BIT(7), 0); +static SUNXI_CCU_GATE(bus_i2c4_clk, "bus-i2c4", "apb2", + 0x06c, BIT(15), 0); +static SUNXI_CCU_GATE(bus_uart0_clk, "bus-uart0", "apb2", + 0x06c, BIT(16), 0); +static SUNXI_CCU_GATE(bus_uart1_clk, "bus-uart1", "apb2", + 0x06c, BIT(17), 0); +static SUNXI_CCU_GATE(bus_uart2_clk, "bus-uart2", "apb2", + 0x06c, BIT(18), 0); +static SUNXI_CCU_GATE(bus_uart3_clk, "bus-uart3", "apb2", + 0x06c, BIT(19), 0); +static SUNXI_CCU_GATE(bus_uart4_clk, "bus-uart4", "apb2", + 0x06c, BIT(20), 0); +static SUNXI_CCU_GATE(bus_uart5_clk, "bus-uart5", "apb2", + 0x06c, BIT(21), 0); +static SUNXI_CCU_GATE(bus_uart6_clk, "bus-uart6", "apb2", + 0x06c, BIT(22), 0); +static SUNXI_CCU_GATE(bus_uart7_clk, "bus-uart7", "apb2", + 0x06c, BIT(23), 0); + +static SUNXI_CCU_GATE(bus_dbg_clk, "bus-dbg", "ahb1", + 0x070, BIT(7), 0); + +static const char * const ths_parents[] = { "osc24M" }; +static struct ccu_div ths_clk = { + .enable = BIT(31), + .div = _SUNXI_CCU_DIV_FLAGS(0, 2, CLK_DIVIDER_POWER_OF_TWO), + .mux = _SUNXI_CCU_MUX(24, 2), + .common = { + .reg = 0x074, + .hw.init = CLK_HW_INIT_PARENTS("ths", + ths_parents, + &ccu_div_ops, + 0), + }, +}; + +static const char * const mod0_default_parents[] = { "osc24M", "pll-periph0", + "pll-periph1" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(nand_clk, "nand", mod0_default_parents, 0x080, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc0_clk, "mmc0", mod0_default_parents, 0x088, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc1_clk, "mmc1", mod0_default_parents, 0x08c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents, 0x090, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(mmc3_clk, "mmc3", mod0_default_parents, 0x094, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const ts_parents[] = { "osc24M", "pll-periph0", }; +static SUNXI_CCU_MP_WITH_MUX_GATE(ts_clk, "ts", ts_parents, 0x098, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 4, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const ce_parents[] = { "osc24M", "pll-periph0-2x", + "pll-periph1-2x" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(ce_clk, "ce", ce_parents, 0x09c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi0_clk, "spi0", mod0_default_parents, 0x0a0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi1_clk, "spi1", mod0_default_parents, 0x0a4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi2_clk, "spi2", mod0_default_parents, 0x0a8, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(spi3_clk, "spi3", mod0_default_parents, 0x0ac, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const i2s_parents[] = { "pll-audio-8x", "pll-audio-4x", + "pll-audio-2x", "pll-audio" }; +static SUNXI_CCU_MUX_WITH_GATE(i2s0_clk, "i2s0", i2s_parents, + 0x0b0, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_MUX_WITH_GATE(i2s1_clk, "i2s1", i2s_parents, + 0x0b4, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_MUX_WITH_GATE(i2s2_clk, "i2s2", i2s_parents, + 0x0b8, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_MUX_WITH_GATE(ac97_clk, "ac97", i2s_parents, + 0x0bc, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_MUX_WITH_GATE(spdif_clk, "spdif", i2s_parents, + 0x0c0, 16, 2, BIT(31), CLK_SET_RATE_PARENT); + +static const char * const keypad_parents[] = { "osc24M", "osc32k" }; +static const u8 keypad_table[] = { 0, 2 }; +static struct ccu_mp keypad_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(0, 5), + .p = _SUNXI_CCU_DIV(16, 2), + .mux = _SUNXI_CCU_MUX_TABLE(24, 2, keypad_table), + .common = { + .reg = 0x0c4, + .hw.init = CLK_HW_INIT_PARENTS("keypad", + keypad_parents, + &ccu_mp_ops, + 0), + } +}; + +static const char * const sata_parents[] = { "pll-sata-out", "sata-ext" }; +static SUNXI_CCU_MUX_WITH_GATE(sata_clk, "sata", sata_parents, + 0x0c8, 24, 1, BIT(31), CLK_SET_RATE_PARENT); + +/* + * There are 3 OHCI 12M clock source selection bits in this register. + * We will force them to 0 (12M divided from 48M). + */ +#define SUN8I_R40_USB_CLK_REG 0x0cc + +static SUNXI_CCU_GATE(usb_phy0_clk, "usb-phy0", "osc24M", + 0x0cc, BIT(8), 0); +static SUNXI_CCU_GATE(usb_phy1_clk, "usb-phy1", "osc24M", + 0x0cc, BIT(9), 0); +static SUNXI_CCU_GATE(usb_phy2_clk, "usb-phy2", "osc24M", + 0x0cc, BIT(10), 0); +static SUNXI_CCU_GATE(usb_ohci0_clk, "usb-ohci0", "osc12M", + 0x0cc, BIT(16), 0); +static SUNXI_CCU_GATE(usb_ohci1_clk, "usb-ohci1", "osc12M", + 0x0cc, BIT(17), 0); +static SUNXI_CCU_GATE(usb_ohci2_clk, "usb-ohci2", "osc12M", + 0x0cc, BIT(18), 0); + +static const char * const ir_parents[] = { "osc24M", "pll-periph0", + "pll-periph1", "osc32k" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(ir0_clk, "ir0", ir_parents, 0x0d0, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static SUNXI_CCU_MP_WITH_MUX_GATE(ir1_clk, "ir1", ir_parents, 0x0d4, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + 0); + +static const char * const dram_parents[] = { "pll-ddr0", "pll-ddr1" }; +static SUNXI_CCU_M_WITH_MUX(dram_clk, "dram", dram_parents, + 0x0f4, 0, 2, 20, 2, CLK_IS_CRITICAL); + +static SUNXI_CCU_GATE(dram_ve_clk, "dram-ve", "dram", + 0x100, BIT(0), 0); +static SUNXI_CCU_GATE(dram_csi0_clk, "dram-csi0", "dram", + 0x100, BIT(1), 0); +static SUNXI_CCU_GATE(dram_csi1_clk, "dram-csi1", "dram", + 0x100, BIT(2), 0); +static SUNXI_CCU_GATE(dram_ts_clk, "dram-ts", "dram", + 0x100, BIT(3), 0); +static SUNXI_CCU_GATE(dram_tvd_clk, "dram-tvd", "dram", + 0x100, BIT(4), 0); +static SUNXI_CCU_GATE(dram_mp_clk, "dram-mp", "dram", + 0x100, BIT(5), 0); +static SUNXI_CCU_GATE(dram_deinterlace_clk, "dram-deinterlace", "dram", + 0x100, BIT(6), 0); + +static const char * const de_parents[] = { "pll-periph0-2x", "pll-de" }; +static SUNXI_CCU_M_WITH_MUX_GATE(de_clk, "de", de_parents, + 0x104, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(mp_clk, "mp", de_parents, + 0x108, 0, 4, 24, 3, BIT(31), 0); + +static const char * const tcon_parents[] = { "pll-video0", "pll-video1", + "pll-video0-2x", "pll-video1-2x", + "pll-mipi" }; +static SUNXI_CCU_MUX_WITH_GATE(tcon_lcd0_clk, "tcon-lcd0", tcon_parents, + 0x110, 24, 3, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_MUX_WITH_GATE(tcon_lcd1_clk, "tcon-lcd1", tcon_parents, + 0x114, 24, 3, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_M_WITH_MUX_GATE(tcon_tv0_clk, "tcon-tv0", tcon_parents, + 0x118, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(tcon_tv1_clk, "tcon-tv1", tcon_parents, + 0x11c, 0, 4, 24, 3, BIT(31), 0); + +static const char * const deinterlace_parents[] = { "pll-periph0", + "pll-periph1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(deinterlace_clk, "deinterlace", + deinterlace_parents, 0x124, 0, 4, 24, 3, + BIT(31), 0); + +static const char * const csi_mclk_parents[] = { "osc24M", "pll-video1", + "pll-periph1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(csi1_mclk_clk, "csi1-mclk", csi_mclk_parents, + 0x130, 0, 5, 8, 3, BIT(15), 0); + +static const char * const csi_sclk_parents[] = { "pll-periph0", "pll-periph1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(csi_sclk_clk, "csi-sclk", csi_sclk_parents, + 0x134, 16, 4, 24, 3, BIT(31), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(csi0_mclk_clk, "csi0-mclk", csi_mclk_parents, + 0x134, 0, 5, 8, 3, BIT(15), 0); + +static SUNXI_CCU_M_WITH_GATE(ve_clk, "ve", "pll-ve", + 0x13c, 16, 3, BIT(31), CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE(codec_clk, "codec", "pll-audio", + 0x140, BIT(31), CLK_SET_RATE_PARENT); +static SUNXI_CCU_GATE(avs_clk, "avs", "osc24M", + 0x144, BIT(31), 0); + +static const char * const hdmi_parents[] = { "pll-video0", "pll-video1" }; +static SUNXI_CCU_M_WITH_MUX_GATE(hdmi_clk, "hdmi", hdmi_parents, + 0x150, 0, 4, 24, 2, BIT(31), 0); + +static SUNXI_CCU_GATE(hdmi_slow_clk, "hdmi-slow", "osc24M", + 0x154, BIT(31), 0); + +/* + * In the SoC's user manual, the P factor is mentioned, but not used in + * the frequency formula. + * + * Here the factor is included, according to the BSP kernel source, + * which contains the P factor of this clock. + */ +static const char * const mbus_parents[] = { "osc24M", "pll-periph0-2x", + "pll-ddr0" }; +static SUNXI_CCU_MP_WITH_MUX_GATE(mbus_clk, "mbus", mbus_parents, 0x15c, + 0, 4, /* M */ + 16, 2, /* P */ + 24, 2, /* mux */ + BIT(31), /* gate */ + CLK_IS_CRITICAL); + +static const char * const dsi_dphy_parents[] = { "pll-video0", "pll-video1", + "pll-periph0" }; +static SUNXI_CCU_M_WITH_MUX_GATE(dsi_dphy_clk, "dsi-dphy", dsi_dphy_parents, + 0x168, 0, 4, 8, 2, BIT(15), 0); + +static SUNXI_CCU_M_WITH_MUX_GATE(tve0_clk, "tve0", tcon_parents, + 0x180, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(tve1_clk, "tve1", tcon_parents, + 0x184, 0, 4, 24, 3, BIT(31), 0); + +static const char * const tvd_parents[] = { "pll-video0", "pll-video1", + "pll-video0-2x", "pll-video1-2x" }; +static SUNXI_CCU_M_WITH_MUX_GATE(tvd0_clk, "tvd0", tvd_parents, + 0x188, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(tvd1_clk, "tvd1", tvd_parents, + 0x18c, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(tvd2_clk, "tvd2", tvd_parents, + 0x190, 0, 4, 24, 3, BIT(31), 0); +static SUNXI_CCU_M_WITH_MUX_GATE(tvd3_clk, "tvd3", tvd_parents, + 0x194, 0, 4, 24, 3, BIT(31), 0); + +static SUNXI_CCU_M_WITH_GATE(gpu_clk, "gpu", "pll-gpu", + 0x1a0, 0, 3, BIT(31), CLK_SET_RATE_PARENT); + +static const char * const out_parents[] = { "osc24M", "osc32k", "osc24M" }; +static const struct ccu_mux_fixed_prediv out_predivs[] = { + { .index = 0, .div = 750, }, +}; + +static struct ccu_mp outa_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(8, 5), + .p = _SUNXI_CCU_DIV(20, 2), + .mux = { + .shift = 24, + .width = 2, + .fixed_predivs = out_predivs, + .n_predivs = ARRAY_SIZE(out_predivs), + }, + .common = { + .reg = 0x1f0, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("outa", out_parents, + &ccu_mp_ops, 0), + } +}; + +static struct ccu_mp outb_clk = { + .enable = BIT(31), + .m = _SUNXI_CCU_DIV(8, 5), + .p = _SUNXI_CCU_DIV(20, 2), + .mux = { + .shift = 24, + .width = 2, + .fixed_predivs = out_predivs, + .n_predivs = ARRAY_SIZE(out_predivs), + }, + .common = { + .reg = 0x1f4, + .features = CCU_FEATURE_FIXED_PREDIV, + .hw.init = CLK_HW_INIT_PARENTS("outb", out_parents, + &ccu_mp_ops, 0), + } +}; + +static struct ccu_common *sun8i_r40_ccu_clks[] = { + &pll_cpu_clk.common, + &pll_audio_base_clk.common, + &pll_video0_clk.common, + &pll_ve_clk.common, + &pll_ddr0_clk.common, + &pll_periph0_clk.common, + &pll_periph0_sata_clk.common, + &pll_periph1_clk.common, + &pll_video1_clk.common, + &pll_sata_clk.common, + &pll_sata_out_clk.common, + &pll_gpu_clk.common, + &pll_mipi_clk.common, + &pll_de_clk.common, + &pll_ddr1_clk.common, + &cpu_clk.common, + &axi_clk.common, + &ahb1_clk.common, + &apb1_clk.common, + &apb2_clk.common, + &bus_mipi_dsi_clk.common, + &bus_ce_clk.common, + &bus_dma_clk.common, + &bus_mmc0_clk.common, + &bus_mmc1_clk.common, + &bus_mmc2_clk.common, + &bus_mmc3_clk.common, + &bus_nand_clk.common, + &bus_dram_clk.common, + &bus_emac_clk.common, + &bus_ts_clk.common, + &bus_hstimer_clk.common, + &bus_spi0_clk.common, + &bus_spi1_clk.common, + &bus_spi2_clk.common, + &bus_spi3_clk.common, + &bus_sata_clk.common, + &bus_otg_clk.common, + &bus_ehci0_clk.common, + &bus_ehci1_clk.common, + &bus_ehci2_clk.common, + &bus_ohci0_clk.common, + &bus_ohci1_clk.common, + &bus_ohci2_clk.common, + &bus_ve_clk.common, + &bus_mp_clk.common, + &bus_deinterlace_clk.common, + &bus_csi0_clk.common, + &bus_csi1_clk.common, + &bus_hdmi0_clk.common, + &bus_hdmi1_clk.common, + &bus_de_clk.common, + &bus_tve0_clk.common, + &bus_tve1_clk.common, + &bus_tve_top_clk.common, + &bus_gmac_clk.common, + &bus_gpu_clk.common, + &bus_tvd0_clk.common, + &bus_tvd1_clk.common, + &bus_tvd2_clk.common, + &bus_tvd3_clk.common, + &bus_tvd_top_clk.common, + &bus_tcon_lcd0_clk.common, + &bus_tcon_lcd1_clk.common, + &bus_tcon_tv0_clk.common, + &bus_tcon_tv1_clk.common, + &bus_tcon_top_clk.common, + &bus_codec_clk.common, + &bus_spdif_clk.common, + &bus_ac97_clk.common, + &bus_pio_clk.common, + &bus_ir0_clk.common, + &bus_ir1_clk.common, + &bus_ths_clk.common, + &bus_keypad_clk.common, + &bus_i2s0_clk.common, + &bus_i2s1_clk.common, + &bus_i2s2_clk.common, + &bus_i2c0_clk.common, + &bus_i2c1_clk.common, + &bus_i2c2_clk.common, + &bus_i2c3_clk.common, + &bus_can_clk.common, + &bus_scr_clk.common, + &bus_ps20_clk.common, + &bus_ps21_clk.common, + &bus_i2c4_clk.common, + &bus_uart0_clk.common, + &bus_uart1_clk.common, + &bus_uart2_clk.common, + &bus_uart3_clk.common, + &bus_uart4_clk.common, + &bus_uart5_clk.common, + &bus_uart6_clk.common, + &bus_uart7_clk.common, + &bus_dbg_clk.common, + &ths_clk.common, + &nand_clk.common, + &mmc0_clk.common, + &mmc1_clk.common, + &mmc2_clk.common, + &mmc3_clk.common, + &ts_clk.common, + &ce_clk.common, + &spi0_clk.common, + &spi1_clk.common, + &spi2_clk.common, + &spi3_clk.common, + &i2s0_clk.common, + &i2s1_clk.common, + &i2s2_clk.common, + &ac97_clk.common, + &spdif_clk.common, + &keypad_clk.common, + &sata_clk.common, + &usb_phy0_clk.common, + &usb_phy1_clk.common, + &usb_phy2_clk.common, + &usb_ohci0_clk.common, + &usb_ohci1_clk.common, + &usb_ohci2_clk.common, + &ir0_clk.common, + &ir1_clk.common, + &dram_clk.common, + &dram_ve_clk.common, + &dram_csi0_clk.common, + &dram_csi1_clk.common, + &dram_ts_clk.common, + &dram_tvd_clk.common, + &dram_mp_clk.common, + &dram_deinterlace_clk.common, + &de_clk.common, + &mp_clk.common, + &tcon_lcd0_clk.common, + &tcon_lcd1_clk.common, + &tcon_tv0_clk.common, + &tcon_tv1_clk.common, + &deinterlace_clk.common, + &csi1_mclk_clk.common, + &csi_sclk_clk.common, + &csi0_mclk_clk.common, + &ve_clk.common, + &codec_clk.common, + &avs_clk.common, + &hdmi_clk.common, + &hdmi_slow_clk.common, + &mbus_clk.common, + &dsi_dphy_clk.common, + &tve0_clk.common, + &tve1_clk.common, + &tvd0_clk.common, + &tvd1_clk.common, + &tvd2_clk.common, + &tvd3_clk.common, + &gpu_clk.common, + &outa_clk.common, + &outb_clk.common, +}; + +/* Fixed Factor clocks */ +static CLK_FIXED_FACTOR(osc12M_clk, "osc12M", "osc24M", 2, 1, 0); + +/* We hardcode the divider to 4 for now */ +static CLK_FIXED_FACTOR(pll_audio_clk, "pll-audio", + "pll-audio-base", 4, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_2x_clk, "pll-audio-2x", + "pll-audio-base", 2, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_4x_clk, "pll-audio-4x", + "pll-audio-base", 1, 1, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_audio_8x_clk, "pll-audio-8x", + "pll-audio-base", 1, 2, CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR(pll_periph0_2x_clk, "pll-periph0-2x", + "pll-periph0", 1, 2, 0); +static CLK_FIXED_FACTOR(pll_periph1_2x_clk, "pll-periph1-2x", + "pll-periph1", 1, 2, 0); +static CLK_FIXED_FACTOR(pll_video0_2x_clk, "pll-video0-2x", + "pll-video0", 1, 2, 0); +static CLK_FIXED_FACTOR(pll_video1_2x_clk, "pll-video1-2x", + "pll-video1", 1, 2, 0); + +static struct clk_hw_onecell_data sun8i_r40_hw_clks = { + .hws = { + [CLK_OSC_12M] = &osc12M_clk.hw, + [CLK_PLL_CPU] = &pll_cpu_clk.common.hw, + [CLK_PLL_AUDIO_BASE] = &pll_audio_base_clk.common.hw, + [CLK_PLL_AUDIO] = &pll_audio_clk.hw, + [CLK_PLL_AUDIO_2X] = &pll_audio_2x_clk.hw, + [CLK_PLL_AUDIO_4X] = &pll_audio_4x_clk.hw, + [CLK_PLL_AUDIO_8X] = &pll_audio_8x_clk.hw, + [CLK_PLL_VIDEO0] = &pll_video0_clk.common.hw, + [CLK_PLL_VIDEO0_2X] = &pll_video0_2x_clk.hw, + [CLK_PLL_VE] = &pll_ve_clk.common.hw, + [CLK_PLL_DDR0] = &pll_ddr0_clk.common.hw, + [CLK_PLL_PERIPH0] = &pll_periph0_clk.common.hw, + [CLK_PLL_PERIPH0_SATA] = &pll_periph0_sata_clk.common.hw, + [CLK_PLL_PERIPH0_2X] = &pll_periph0_2x_clk.hw, + [CLK_PLL_PERIPH1] = &pll_periph1_clk.common.hw, + [CLK_PLL_PERIPH1_2X] = &pll_periph1_2x_clk.hw, + [CLK_PLL_VIDEO1] = &pll_video1_clk.common.hw, + [CLK_PLL_VIDEO1_2X] = &pll_video1_2x_clk.hw, + [CLK_PLL_SATA] = &pll_sata_clk.common.hw, + [CLK_PLL_SATA_OUT] = &pll_sata_out_clk.common.hw, + [CLK_PLL_GPU] = &pll_gpu_clk.common.hw, + [CLK_PLL_MIPI] = &pll_mipi_clk.common.hw, + [CLK_PLL_DE] = &pll_de_clk.common.hw, + [CLK_PLL_DDR1] = &pll_ddr1_clk.common.hw, + [CLK_CPU] = &cpu_clk.common.hw, + [CLK_AXI] = &axi_clk.common.hw, + [CLK_AHB1] = &ahb1_clk.common.hw, + [CLK_APB1] = &apb1_clk.common.hw, + [CLK_APB2] = &apb2_clk.common.hw, + [CLK_BUS_MIPI_DSI] = &bus_mipi_dsi_clk.common.hw, + [CLK_BUS_CE] = &bus_ce_clk.common.hw, + [CLK_BUS_DMA] = &bus_dma_clk.common.hw, + [CLK_BUS_MMC0] = &bus_mmc0_clk.common.hw, + [CLK_BUS_MMC1] = &bus_mmc1_clk.common.hw, + [CLK_BUS_MMC2] = &bus_mmc2_clk.common.hw, + [CLK_BUS_MMC3] = &bus_mmc3_clk.common.hw, + [CLK_BUS_NAND] = &bus_nand_clk.common.hw, + [CLK_BUS_DRAM] = &bus_dram_clk.common.hw, + [CLK_BUS_EMAC] = &bus_emac_clk.common.hw, + [CLK_BUS_TS] = &bus_ts_clk.common.hw, + [CLK_BUS_HSTIMER] = &bus_hstimer_clk.common.hw, + [CLK_BUS_SPI0] = &bus_spi0_clk.common.hw, + [CLK_BUS_SPI1] = &bus_spi1_clk.common.hw, + [CLK_BUS_SPI2] = &bus_spi2_clk.common.hw, + [CLK_BUS_SPI3] = &bus_spi3_clk.common.hw, + [CLK_BUS_SATA] = &bus_sata_clk.common.hw, + [CLK_BUS_OTG] = &bus_otg_clk.common.hw, + [CLK_BUS_EHCI0] = &bus_ehci0_clk.common.hw, + [CLK_BUS_EHCI1] = &bus_ehci1_clk.common.hw, + [CLK_BUS_EHCI2] = &bus_ehci2_clk.common.hw, + [CLK_BUS_OHCI0] = &bus_ohci0_clk.common.hw, + [CLK_BUS_OHCI1] = &bus_ohci1_clk.common.hw, + [CLK_BUS_OHCI2] = &bus_ohci2_clk.common.hw, + [CLK_BUS_VE] = &bus_ve_clk.common.hw, + [CLK_BUS_MP] = &bus_mp_clk.common.hw, + [CLK_BUS_DEINTERLACE] = &bus_deinterlace_clk.common.hw, + [CLK_BUS_CSI0] = &bus_csi0_clk.common.hw, + [CLK_BUS_CSI1] = &bus_csi1_clk.common.hw, + [CLK_BUS_HDMI0] = &bus_hdmi0_clk.common.hw, + [CLK_BUS_HDMI1] = &bus_hdmi1_clk.common.hw, + [CLK_BUS_DE] = &bus_de_clk.common.hw, + [CLK_BUS_TVE0] = &bus_tve0_clk.common.hw, + [CLK_BUS_TVE1] = &bus_tve1_clk.common.hw, + [CLK_BUS_TVE_TOP] = &bus_tve_top_clk.common.hw, + [CLK_BUS_GMAC] = &bus_gmac_clk.common.hw, + [CLK_BUS_GPU] = &bus_gpu_clk.common.hw, + [CLK_BUS_TVD0] = &bus_tvd0_clk.common.hw, + [CLK_BUS_TVD1] = &bus_tvd1_clk.common.hw, + [CLK_BUS_TVD2] = &bus_tvd2_clk.common.hw, + [CLK_BUS_TVD3] = &bus_tvd3_clk.common.hw, + [CLK_BUS_TVD_TOP] = &bus_tvd_top_clk.common.hw, + [CLK_BUS_TCON_LCD0] = &bus_tcon_lcd0_clk.common.hw, + [CLK_BUS_TCON_LCD1] = &bus_tcon_lcd1_clk.common.hw, + [CLK_BUS_TCON_TV0] = &bus_tcon_tv0_clk.common.hw, + [CLK_BUS_TCON_TV1] = &bus_tcon_tv1_clk.common.hw, + [CLK_BUS_TCON_TOP] = &bus_tcon_top_clk.common.hw, + [CLK_BUS_CODEC] = &bus_codec_clk.common.hw, + [CLK_BUS_SPDIF] = &bus_spdif_clk.common.hw, + [CLK_BUS_AC97] = &bus_ac97_clk.common.hw, + [CLK_BUS_PIO] = &bus_pio_clk.common.hw, + [CLK_BUS_IR0] = &bus_ir0_clk.common.hw, + [CLK_BUS_IR1] = &bus_ir1_clk.common.hw, + [CLK_BUS_THS] = &bus_ths_clk.common.hw, + [CLK_BUS_KEYPAD] = &bus_keypad_clk.common.hw, + [CLK_BUS_I2S0] = &bus_i2s0_clk.common.hw, + [CLK_BUS_I2S1] = &bus_i2s1_clk.common.hw, + [CLK_BUS_I2S2] = &bus_i2s2_clk.common.hw, + [CLK_BUS_I2C0] = &bus_i2c0_clk.common.hw, + [CLK_BUS_I2C1] = &bus_i2c1_clk.common.hw, + [CLK_BUS_I2C2] = &bus_i2c2_clk.common.hw, + [CLK_BUS_I2C3] = &bus_i2c3_clk.common.hw, + [CLK_BUS_CAN] = &bus_can_clk.common.hw, + [CLK_BUS_SCR] = &bus_scr_clk.common.hw, + [CLK_BUS_PS20] = &bus_ps20_clk.common.hw, + [CLK_BUS_PS21] = &bus_ps21_clk.common.hw, + [CLK_BUS_I2C4] = &bus_i2c4_clk.common.hw, + [CLK_BUS_UART0] = &bus_uart0_clk.common.hw, + [CLK_BUS_UART1] = &bus_uart1_clk.common.hw, + [CLK_BUS_UART2] = &bus_uart2_clk.common.hw, + [CLK_BUS_UART3] = &bus_uart3_clk.common.hw, + [CLK_BUS_UART4] = &bus_uart4_clk.common.hw, + [CLK_BUS_UART5] = &bus_uart5_clk.common.hw, + [CLK_BUS_UART6] = &bus_uart6_clk.common.hw, + [CLK_BUS_UART7] = &bus_uart7_clk.common.hw, + [CLK_BUS_DBG] = &bus_dbg_clk.common.hw, + [CLK_THS] = &ths_clk.common.hw, + [CLK_NAND] = &nand_clk.common.hw, + [CLK_MMC0] = &mmc0_clk.common.hw, + [CLK_MMC1] = &mmc1_clk.common.hw, + [CLK_MMC2] = &mmc2_clk.common.hw, + [CLK_MMC3] = &mmc3_clk.common.hw, + [CLK_TS] = &ts_clk.common.hw, + [CLK_CE] = &ce_clk.common.hw, + [CLK_SPI0] = &spi0_clk.common.hw, + [CLK_SPI1] = &spi1_clk.common.hw, + [CLK_SPI2] = &spi2_clk.common.hw, + [CLK_SPI3] = &spi3_clk.common.hw, + [CLK_I2S0] = &i2s0_clk.common.hw, + [CLK_I2S1] = &i2s1_clk.common.hw, + [CLK_I2S2] = &i2s2_clk.common.hw, + [CLK_AC97] = &ac97_clk.common.hw, + [CLK_SPDIF] = &spdif_clk.common.hw, + [CLK_KEYPAD] = &keypad_clk.common.hw, + [CLK_SATA] = &sata_clk.common.hw, + [CLK_USB_PHY0] = &usb_phy0_clk.common.hw, + [CLK_USB_PHY1] = &usb_phy1_clk.common.hw, + [CLK_USB_PHY2] = &usb_phy2_clk.common.hw, + [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw, + [CLK_USB_OHCI1] = &usb_ohci1_clk.common.hw, + [CLK_USB_OHCI2] = &usb_ohci2_clk.common.hw, + [CLK_IR0] = &ir0_clk.common.hw, + [CLK_IR1] = &ir1_clk.common.hw, + [CLK_DRAM] = &dram_clk.common.hw, + [CLK_DRAM_VE] = &dram_ve_clk.common.hw, + [CLK_DRAM_CSI0] = &dram_csi0_clk.common.hw, + [CLK_DRAM_CSI1] = &dram_csi1_clk.common.hw, + [CLK_DRAM_TS] = &dram_ts_clk.common.hw, + [CLK_DRAM_TVD] = &dram_tvd_clk.common.hw, + [CLK_DRAM_MP] = &dram_mp_clk.common.hw, + [CLK_DRAM_DEINTERLACE] = &dram_deinterlace_clk.common.hw, + [CLK_DE] = &de_clk.common.hw, + [CLK_MP] = &mp_clk.common.hw, + [CLK_TCON_LCD0] = &tcon_lcd0_clk.common.hw, + [CLK_TCON_LCD1] = &tcon_lcd1_clk.common.hw, + [CLK_TCON_TV0] = &tcon_tv0_clk.common.hw, + [CLK_TCON_TV1] = &tcon_tv1_clk.common.hw, + [CLK_DEINTERLACE] = &deinterlace_clk.common.hw, + [CLK_CSI1_MCLK] = &csi1_mclk_clk.common.hw, + [CLK_CSI_SCLK] = &csi_sclk_clk.common.hw, + [CLK_CSI0_MCLK] = &csi0_mclk_clk.common.hw, + [CLK_VE] = &ve_clk.common.hw, + [CLK_CODEC] = &codec_clk.common.hw, + [CLK_AVS] = &avs_clk.common.hw, + [CLK_HDMI] = &hdmi_clk.common.hw, + [CLK_HDMI_SLOW] = &hdmi_slow_clk.common.hw, + [CLK_MBUS] = &mbus_clk.common.hw, + [CLK_DSI_DPHY] = &dsi_dphy_clk.common.hw, + [CLK_TVE0] = &tve0_clk.common.hw, + [CLK_TVE1] = &tve1_clk.common.hw, + [CLK_TVD0] = &tvd0_clk.common.hw, + [CLK_TVD1] = &tvd1_clk.common.hw, + [CLK_TVD2] = &tvd2_clk.common.hw, + [CLK_TVD3] = &tvd3_clk.common.hw, + [CLK_GPU] = &gpu_clk.common.hw, + [CLK_OUTA] = &outa_clk.common.hw, + [CLK_OUTB] = &outb_clk.common.hw, + }, + .num = CLK_NUMBER, +}; + +static struct ccu_reset_map sun8i_r40_ccu_resets[] = { + [RST_USB_PHY0] = { 0x0cc, BIT(0) }, + [RST_USB_PHY1] = { 0x0cc, BIT(1) }, + [RST_USB_PHY2] = { 0x0cc, BIT(2) }, + + [RST_DRAM] = { 0x0f4, BIT(31) }, + [RST_MBUS] = { 0x0fc, BIT(31) }, + + [RST_BUS_MIPI_DSI] = { 0x2c0, BIT(1) }, + [RST_BUS_CE] = { 0x2c0, BIT(5) }, + [RST_BUS_DMA] = { 0x2c0, BIT(6) }, + [RST_BUS_MMC0] = { 0x2c0, BIT(8) }, + [RST_BUS_MMC1] = { 0x2c0, BIT(9) }, + [RST_BUS_MMC2] = { 0x2c0, BIT(10) }, + [RST_BUS_MMC3] = { 0x2c0, BIT(11) }, + [RST_BUS_NAND] = { 0x2c0, BIT(13) }, + [RST_BUS_DRAM] = { 0x2c0, BIT(14) }, + [RST_BUS_EMAC] = { 0x2c0, BIT(17) }, + [RST_BUS_TS] = { 0x2c0, BIT(18) }, + [RST_BUS_HSTIMER] = { 0x2c0, BIT(19) }, + [RST_BUS_SPI0] = { 0x2c0, BIT(20) }, + [RST_BUS_SPI1] = { 0x2c0, BIT(21) }, + [RST_BUS_SPI2] = { 0x2c0, BIT(22) }, + [RST_BUS_SPI3] = { 0x2c0, BIT(23) }, + [RST_BUS_SATA] = { 0x2c0, BIT(24) }, + [RST_BUS_OTG] = { 0x2c0, BIT(25) }, + [RST_BUS_EHCI0] = { 0x2c0, BIT(26) }, + [RST_BUS_EHCI1] = { 0x2c0, BIT(27) }, + [RST_BUS_EHCI2] = { 0x2c0, BIT(28) }, + [RST_BUS_OHCI0] = { 0x2c0, BIT(29) }, + [RST_BUS_OHCI1] = { 0x2c0, BIT(30) }, + [RST_BUS_OHCI2] = { 0x2c0, BIT(31) }, + + [RST_BUS_VE] = { 0x2c4, BIT(0) }, + [RST_BUS_MP] = { 0x2c4, BIT(2) }, + [RST_BUS_DEINTERLACE] = { 0x2c4, BIT(5) }, + [RST_BUS_CSI0] = { 0x2c4, BIT(8) }, + [RST_BUS_CSI1] = { 0x2c4, BIT(9) }, + [RST_BUS_HDMI0] = { 0x2c4, BIT(10) }, + [RST_BUS_HDMI1] = { 0x2c4, BIT(11) }, + [RST_BUS_DE] = { 0x2c4, BIT(12) }, + [RST_BUS_TVE0] = { 0x2c4, BIT(13) }, + [RST_BUS_TVE1] = { 0x2c4, BIT(14) }, + [RST_BUS_TVE_TOP] = { 0x2c4, BIT(15) }, + [RST_BUS_GMAC] = { 0x2c4, BIT(17) }, + [RST_BUS_GPU] = { 0x2c4, BIT(20) }, + [RST_BUS_TVD0] = { 0x2c4, BIT(21) }, + [RST_BUS_TVD1] = { 0x2c4, BIT(22) }, + [RST_BUS_TVD2] = { 0x2c4, BIT(23) }, + [RST_BUS_TVD3] = { 0x2c4, BIT(24) }, + [RST_BUS_TVD_TOP] = { 0x2c4, BIT(25) }, + [RST_BUS_TCON_LCD0] = { 0x2c4, BIT(26) }, + [RST_BUS_TCON_LCD1] = { 0x2c4, BIT(27) }, + [RST_BUS_TCON_TV0] = { 0x2c4, BIT(28) }, + [RST_BUS_TCON_TV1] = { 0x2c4, BIT(29) }, + [RST_BUS_TCON_TOP] = { 0x2c4, BIT(30) }, + [RST_BUS_DBG] = { 0x2c4, BIT(31) }, + + [RST_BUS_LVDS] = { 0x2c8, BIT(0) }, + + [RST_BUS_CODEC] = { 0x2d0, BIT(0) }, + [RST_BUS_SPDIF] = { 0x2d0, BIT(1) }, + [RST_BUS_AC97] = { 0x2d0, BIT(2) }, + [RST_BUS_IR0] = { 0x2d0, BIT(6) }, + [RST_BUS_IR1] = { 0x2d0, BIT(7) }, + [RST_BUS_THS] = { 0x2d0, BIT(8) }, + [RST_BUS_KEYPAD] = { 0x2d0, BIT(10) }, + [RST_BUS_I2S0] = { 0x2d0, BIT(12) }, + [RST_BUS_I2S1] = { 0x2d0, BIT(13) }, + [RST_BUS_I2S2] = { 0x2d0, BIT(14) }, + + [RST_BUS_I2C0] = { 0x2d8, BIT(0) }, + [RST_BUS_I2C1] = { 0x2d8, BIT(1) }, + [RST_BUS_I2C2] = { 0x2d8, BIT(2) }, + [RST_BUS_I2C3] = { 0x2d8, BIT(3) }, + [RST_BUS_CAN] = { 0x2d8, BIT(4) }, + [RST_BUS_SCR] = { 0x2d8, BIT(5) }, + [RST_BUS_PS20] = { 0x2d8, BIT(6) }, + [RST_BUS_PS21] = { 0x2d8, BIT(7) }, + [RST_BUS_I2C4] = { 0x2d8, BIT(15) }, + [RST_BUS_UART0] = { 0x2d8, BIT(16) }, + [RST_BUS_UART1] = { 0x2d8, BIT(17) }, + [RST_BUS_UART2] = { 0x2d8, BIT(18) }, + [RST_BUS_UART3] = { 0x2d8, BIT(19) }, + [RST_BUS_UART4] = { 0x2d8, BIT(20) }, + [RST_BUS_UART5] = { 0x2d8, BIT(21) }, + [RST_BUS_UART6] = { 0x2d8, BIT(22) }, + [RST_BUS_UART7] = { 0x2d8, BIT(23) }, +}; + +static const struct sunxi_ccu_desc sun8i_r40_ccu_desc = { + .ccu_clks = sun8i_r40_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_r40_ccu_clks), + + .hw_clks = &sun8i_r40_hw_clks, + + .resets = sun8i_r40_ccu_resets, + .num_resets = ARRAY_SIZE(sun8i_r40_ccu_resets), +}; + +static struct ccu_pll_nb sun8i_r40_pll_cpu_nb = { + .common = &pll_cpu_clk.common, + /* copy from pll_cpu_clk */ + .enable = BIT(31), + .lock = BIT(28), +}; + +static struct ccu_mux_nb sun8i_r40_cpu_nb = { + .common = &cpu_clk.common, + .cm = &cpu_clk.mux, + .delay_us = 1, /* > 8 clock cycles at 24 MHz */ + .bypass_index = 1, /* index of 24 MHz oscillator */ +}; + +static void __init sun8i_r40_ccu_setup(struct device_node *node) +{ + void __iomem *reg; + u32 val; + + reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) { + pr_err("%s: Could not map the clock registers\n", + of_node_full_name(node)); + return; + } + + /* Force the PLL-Audio-1x divider to 4 */ + val = readl(reg + SUN8I_R40_PLL_AUDIO_REG); + val &= ~GENMASK(19, 16); + writel(val | (3 << 16), reg + SUN8I_R40_PLL_AUDIO_REG); + + /* Force PLL-MIPI to MIPI mode */ + val = readl(reg + SUN8I_R40_PLL_MIPI_REG); + val &= ~BIT(16); + writel(val, reg + SUN8I_R40_PLL_MIPI_REG); + + /* Force OHCI 12M parent to 12M divided from 48M */ + val = readl(reg + SUN8I_R40_USB_CLK_REG); + val &= ~GENMASK(25, 20); + writel(val, reg + SUN8I_R40_USB_CLK_REG); + + sunxi_ccu_probe(node, reg, &sun8i_r40_ccu_desc); + + /* Gate then ungate PLL CPU after any rate changes */ + ccu_pll_notifier_register(&sun8i_r40_pll_cpu_nb); + + /* Reparent CPU during PLL CPU rate changes */ + ccu_mux_notifier_register(pll_cpu_clk.common.hw.clk, + &sun8i_r40_cpu_nb); +} +CLK_OF_DECLARE(sun8i_r40_ccu, "allwinner,sun8i-r40-ccu", + sun8i_r40_ccu_setup); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r40.h b/drivers/clk/sunxi-ng/ccu-sun8i-r40.h new file mode 100644 index 000000000000..0db8e1e97af8 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun8i-r40.h @@ -0,0 +1,69 @@ +/* + * Copyright 2017 Icenowy Zheng <icenowy@aosc.io> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CCU_SUN8I_R40_H_ +#define _CCU_SUN8I_R40_H_ + +#include <dt-bindings/clock/sun8i-r40-ccu.h> +#include <dt-bindings/reset/sun8i-r40-ccu.h> + +#define CLK_OSC_12M 0 +#define CLK_PLL_CPU 1 +#define CLK_PLL_AUDIO_BASE 2 +#define CLK_PLL_AUDIO 3 +#define CLK_PLL_AUDIO_2X 4 +#define CLK_PLL_AUDIO_4X 5 +#define CLK_PLL_AUDIO_8X 6 +#define CLK_PLL_VIDEO0 7 +#define CLK_PLL_VIDEO0_2X 8 +#define CLK_PLL_VE 9 +#define CLK_PLL_DDR0 10 +#define CLK_PLL_PERIPH0 11 +#define CLK_PLL_PERIPH0_SATA 12 +#define CLK_PLL_PERIPH0_2X 13 +#define CLK_PLL_PERIPH1 14 +#define CLK_PLL_PERIPH1_2X 15 +#define CLK_PLL_VIDEO1 16 +#define CLK_PLL_VIDEO1_2X 17 +#define CLK_PLL_SATA 18 +#define CLK_PLL_SATA_OUT 19 +#define CLK_PLL_GPU 20 +#define CLK_PLL_MIPI 21 +#define CLK_PLL_DE 22 +#define CLK_PLL_DDR1 23 + +/* The CPU clock is exported */ + +#define CLK_AXI 25 +#define CLK_AHB1 26 +#define CLK_APB1 27 +#define CLK_APB2 28 + +/* All the bus gates are exported */ + +/* The first bunch of module clocks are exported */ + +#define CLK_DRAM 132 + +/* All the DRAM gates are exported */ + +/* Some more module clocks are exported */ + +#define CLK_MBUS 155 + +/* Another bunch of module clocks are exported */ + +#define CLK_NUMBER (CLK_OUTB + 1) + +#endif /* _CCU_SUN8I_R40_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c index a34a78d7fb28..621b1cd996db 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c @@ -575,8 +575,7 @@ static void __init sun8i_v3s_ccu_setup(struct device_node *node) reg = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(reg)) { - pr_err("%s: Could not map the clock registers\n", - of_node_full_name(node)); + pr_err("%pOF: Could not map the clock registers\n", node); return; } diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c index c0e5c10d0091..baa3cf96507b 100644 --- a/drivers/clk/sunxi-ng/ccu_div.c +++ b/drivers/clk/sunxi-ng/ccu_div.c @@ -21,10 +21,18 @@ static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux, { struct ccu_div *cd = data; - return divider_round_rate_parent(&cd->common.hw, parent, + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate *= cd->fixed_post_div; + + rate = divider_round_rate_parent(&cd->common.hw, parent, rate, parent_rate, cd->div.table, cd->div.width, cd->div.flags); + + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate /= cd->fixed_post_div; + + return rate; } static void ccu_div_disable(struct clk_hw *hw) @@ -62,8 +70,13 @@ static unsigned long ccu_div_recalc_rate(struct clk_hw *hw, parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1, parent_rate); - return divider_recalc_rate(hw, parent_rate, val, cd->div.table, - cd->div.flags); + val = divider_recalc_rate(hw, parent_rate, val, cd->div.table, + cd->div.flags); + + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) + val /= cd->fixed_post_div; + + return val; } static int ccu_div_determine_rate(struct clk_hw *hw, @@ -86,6 +99,9 @@ static int ccu_div_set_rate(struct clk_hw *hw, unsigned long rate, parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1, parent_rate); + if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate *= cd->fixed_post_div; + val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width, cd->div.flags); diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h index 08d074451204..f3a5028dcd14 100644 --- a/drivers/clk/sunxi-ng/ccu_div.h +++ b/drivers/clk/sunxi-ng/ccu_div.h @@ -86,9 +86,10 @@ struct ccu_div_internal { struct ccu_div { u32 enable; - struct ccu_div_internal div; + struct ccu_div_internal div; struct ccu_mux_internal mux; struct ccu_common common; + unsigned int fixed_post_div; }; #define SUNXI_CCU_DIV_TABLE_WITH_GATE(_struct, _name, _parent, _reg, \ diff --git a/drivers/clk/sunxi-ng/ccu_frac.c b/drivers/clk/sunxi-ng/ccu_frac.c index 8b5eb7756bf7..d1d168d4c4f0 100644 --- a/drivers/clk/sunxi-ng/ccu_frac.c +++ b/drivers/clk/sunxi-ng/ccu_frac.c @@ -67,25 +67,25 @@ unsigned long ccu_frac_helper_read_rate(struct ccu_common *common, { u32 reg; - printk("%s: Read fractional\n", clk_hw_get_name(&common->hw)); + pr_debug("%s: Read fractional\n", clk_hw_get_name(&common->hw)); if (!(common->features & CCU_FEATURE_FRACTIONAL)) return 0; - printk("%s: clock is fractional (rates %lu and %lu)\n", - clk_hw_get_name(&common->hw), cf->rates[0], cf->rates[1]); + pr_debug("%s: clock is fractional (rates %lu and %lu)\n", + clk_hw_get_name(&common->hw), cf->rates[0], cf->rates[1]); reg = readl(common->base + common->reg); - printk("%s: clock reg is 0x%x (select is 0x%x)\n", - clk_hw_get_name(&common->hw), reg, cf->select); + pr_debug("%s: clock reg is 0x%x (select is 0x%x)\n", + clk_hw_get_name(&common->hw), reg, cf->select); return (reg & cf->select) ? cf->rates[1] : cf->rates[0]; } int ccu_frac_helper_set_rate(struct ccu_common *common, struct ccu_frac_internal *cf, - unsigned long rate) + unsigned long rate, u32 lock) { unsigned long flags; u32 reg, sel; @@ -106,5 +106,7 @@ int ccu_frac_helper_set_rate(struct ccu_common *common, writel(reg | sel, common->base + common->reg); spin_unlock_irqrestore(common->lock, flags); + ccu_helper_wait_for_lock(common, lock); + return 0; } diff --git a/drivers/clk/sunxi-ng/ccu_frac.h b/drivers/clk/sunxi-ng/ccu_frac.h index 7b1ee380156f..efe2dd6bac01 100644 --- a/drivers/clk/sunxi-ng/ccu_frac.h +++ b/drivers/clk/sunxi-ng/ccu_frac.h @@ -48,6 +48,6 @@ unsigned long ccu_frac_helper_read_rate(struct ccu_common *common, int ccu_frac_helper_set_rate(struct ccu_common *common, struct ccu_frac_internal *cf, - unsigned long rate); + unsigned long rate, u32 lock); #endif /* _CCU_FRAC_H_ */ diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c index 20d0300867f2..12e0783caee6 100644 --- a/drivers/clk/sunxi-ng/ccu_mult.c +++ b/drivers/clk/sunxi-ng/ccu_mult.c @@ -111,10 +111,14 @@ static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long flags; u32 reg; - if (ccu_frac_helper_has_rate(&cm->common, &cm->frac, rate)) - return ccu_frac_helper_set_rate(&cm->common, &cm->frac, rate); - else + if (ccu_frac_helper_has_rate(&cm->common, &cm->frac, rate)) { + ccu_frac_helper_enable(&cm->common, &cm->frac); + + return ccu_frac_helper_set_rate(&cm->common, &cm->frac, + rate, cm->lock); + } else { ccu_frac_helper_disable(&cm->common, &cm->frac); + } parent_rate = ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1, parent_rate); diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c index 44b16dc8fea6..841840e35e61 100644 --- a/drivers/clk/sunxi-ng/ccu_nkm.c +++ b/drivers/clk/sunxi-ng/ccu_nkm.c @@ -75,7 +75,7 @@ static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct ccu_nkm *nkm = hw_to_ccu_nkm(hw); - unsigned long n, m, k; + unsigned long n, m, k, rate; u32 reg; reg = readl(nkm->common.base + nkm->common.reg); @@ -98,7 +98,12 @@ static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw, if (!m) m++; - return parent_rate * n * k / m; + rate = parent_rate * n * k / m; + + if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate /= nkm->fixed_post_div; + + return rate; } static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux, @@ -117,9 +122,17 @@ static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux, _nkm.min_m = 1; _nkm.max_m = nkm->m.max ?: 1 << nkm->m.width; + if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate *= nkm->fixed_post_div; + ccu_nkm_find_best(*parent_rate, rate, &_nkm); - return *parent_rate * _nkm.n * _nkm.k / _nkm.m; + rate = *parent_rate * _nkm.n * _nkm.k / _nkm.m; + + if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate /= nkm->fixed_post_div; + + return rate; } static int ccu_nkm_determine_rate(struct clk_hw *hw, @@ -139,6 +152,9 @@ static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long flags; u32 reg; + if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV) + rate *= nkm->fixed_post_div; + _nkm.min_n = nkm->n.min ?: 1; _nkm.max_n = nkm->n.max ?: 1 << nkm->n.width; _nkm.min_k = nkm->k.min ?: 1; diff --git a/drivers/clk/sunxi-ng/ccu_nkm.h b/drivers/clk/sunxi-ng/ccu_nkm.h index 34580894f4d1..cc6efb70a102 100644 --- a/drivers/clk/sunxi-ng/ccu_nkm.h +++ b/drivers/clk/sunxi-ng/ccu_nkm.h @@ -34,6 +34,8 @@ struct ccu_nkm { struct ccu_div_internal m; struct ccu_mux_internal mux; + unsigned int fixed_post_div; + struct ccu_common common; }; diff --git a/drivers/clk/sunxi-ng/ccu_nm.c b/drivers/clk/sunxi-ng/ccu_nm.c index 5e5e90a4a50c..a32158e8f2e3 100644 --- a/drivers/clk/sunxi-ng/ccu_nm.c +++ b/drivers/clk/sunxi-ng/ccu_nm.c @@ -117,10 +117,23 @@ static int ccu_nm_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long flags; u32 reg; - if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) - return ccu_frac_helper_set_rate(&nm->common, &nm->frac, rate); - else + if (ccu_frac_helper_has_rate(&nm->common, &nm->frac, rate)) { + spin_lock_irqsave(nm->common.lock, flags); + + /* most SoCs require M to be 0 if fractional mode is used */ + reg = readl(nm->common.base + nm->common.reg); + reg &= ~GENMASK(nm->m.width + nm->m.shift - 1, nm->m.shift); + writel(reg, nm->common.base + nm->common.reg); + + spin_unlock_irqrestore(nm->common.lock, flags); + + ccu_frac_helper_enable(&nm->common, &nm->frac); + + return ccu_frac_helper_set_rate(&nm->common, &nm->frac, + rate, nm->lock); + } else { ccu_frac_helper_disable(&nm->common, &nm->frac); + } _nm.min_n = nm->n.min ?: 1; _nm.max_n = nm->n.max ?: 1 << nm->n.width; diff --git a/drivers/clk/sunxi/clk-sun8i-bus-gates.c b/drivers/clk/sunxi/clk-sun8i-bus-gates.c index 63fdb790df29..bee305bdddbe 100644 --- a/drivers/clk/sunxi/clk-sun8i-bus-gates.c +++ b/drivers/clk/sunxi/clk-sun8i-bus-gates.c @@ -78,6 +78,10 @@ static void __init sun8i_h3_bus_gates_init(struct device_node *node) clk_parent = APB1; else if (index >= 96 && index <= 127) clk_parent = APB2; + else { + WARN_ON(true); + continue; + } clk_reg = reg + 4 * (index / 32); clk_bit = index % 32; diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index f2c9274b8bd5..aa4add580516 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -666,15 +666,14 @@ static struct clk * __init sunxi_mux_clk_setup(struct device_node *node, reg = of_iomap(node, 0); if (!reg) { - pr_err("Could not map registers for mux-clk: %s\n", - of_node_full_name(node)); + pr_err("Could not map registers for mux-clk: %pOF\n", node); return NULL; } i = of_clk_parent_fill(node, parents, SUNXI_MAX_PARENTS); if (of_property_read_string(node, "clock-output-names", &clk_name)) { - pr_err("%s: could not read clock-output-names from \"%s\"\n", - __func__, of_node_full_name(node)); + pr_err("%s: could not read clock-output-names from \"%pOF\"\n", + __func__, node); goto out_unmap; } @@ -797,16 +796,15 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, reg = of_iomap(node, 0); if (!reg) { - pr_err("Could not map registers for mux-clk: %s\n", - of_node_full_name(node)); + pr_err("Could not map registers for mux-clk: %pOF\n", node); return; } clk_parent = of_clk_get_parent_name(node, 0); if (of_property_read_string(node, "clock-output-names", &clk_name)) { - pr_err("%s: could not read clock-output-names from \"%s\"\n", - __func__, of_node_full_name(node)); + pr_err("%s: could not read clock-output-names from \"%pOF\"\n", + __func__, node); goto out_unmap; } @@ -1010,8 +1008,7 @@ static struct clk ** __init sunxi_divs_clk_setup(struct device_node *node, reg = of_iomap(node, 0); if (!reg) { - pr_err("Could not map registers for divs-clk: %s\n", - of_node_full_name(node)); + pr_err("Could not map registers for divs-clk: %pOF\n", node); return NULL; } diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c index 74e7544f861b..11a5066e5c27 100644 --- a/drivers/clk/tegra/clk-emc.c +++ b/drivers/clk/tegra/clk-emc.c @@ -378,7 +378,7 @@ static int load_one_timing_from_dt(struct tegra_clk_emc *tegra, err = of_property_read_u32(node, "clock-frequency", &tmp); if (err) { - pr_err("timing %s: failed to read rate\n", node->full_name); + pr_err("timing %pOF: failed to read rate\n", node); return err; } @@ -386,8 +386,7 @@ static int load_one_timing_from_dt(struct tegra_clk_emc *tegra, err = of_property_read_u32(node, "nvidia,parent-clock-frequency", &tmp); if (err) { - pr_err("timing %s: failed to read parent rate\n", - node->full_name); + pr_err("timing %pOF: failed to read parent rate\n", node); return err; } @@ -395,8 +394,7 @@ static int load_one_timing_from_dt(struct tegra_clk_emc *tegra, timing->parent = of_clk_get_by_name(node, "emc-parent"); if (IS_ERR(timing->parent)) { - pr_err("timing %s: failed to get parent clock\n", - node->full_name); + pr_err("timing %pOF: failed to get parent clock\n", node); return PTR_ERR(timing->parent); } @@ -409,8 +407,8 @@ static int load_one_timing_from_dt(struct tegra_clk_emc *tegra, } } if (timing->parent_index == 0xff) { - pr_err("timing %s: %s is not a valid parent\n", - node->full_name, __clk_get_name(timing->parent)); + pr_err("timing %pOF: %s is not a valid parent\n", + node, __clk_get_name(timing->parent)); clk_put(timing->parent); return -EINVAL; } diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 159a854779e6..7c369e21c91c 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -363,7 +363,7 @@ static void _clk_pll_enable(struct clk_hw *hw) val = pll_readl(pll->params->iddq_reg, pll); val &= ~BIT(pll->params->iddq_bit_idx); pll_writel(val, pll->params->iddq_reg, pll); - udelay(2); + udelay(5); } if (pll->params->reset_reg) { @@ -418,6 +418,26 @@ static void _clk_pll_disable(struct clk_hw *hw) } } +static void pll_clk_start_ss(struct tegra_clk_pll *pll) +{ + if (pll->params->defaults_set && pll->params->ssc_ctrl_reg) { + u32 val = pll_readl(pll->params->ssc_ctrl_reg, pll); + + val |= pll->params->ssc_ctrl_en_mask; + pll_writel(val, pll->params->ssc_ctrl_reg, pll); + } +} + +static void pll_clk_stop_ss(struct tegra_clk_pll *pll) +{ + if (pll->params->defaults_set && pll->params->ssc_ctrl_reg) { + u32 val = pll_readl(pll->params->ssc_ctrl_reg, pll); + + val &= ~pll->params->ssc_ctrl_en_mask; + pll_writel(val, pll->params->ssc_ctrl_reg, pll); + } +} + static int clk_pll_enable(struct clk_hw *hw) { struct tegra_clk_pll *pll = to_clk_pll(hw); @@ -431,6 +451,8 @@ static int clk_pll_enable(struct clk_hw *hw) ret = clk_pll_wait_for_lock(pll); + pll_clk_start_ss(pll); + if (pll->lock) spin_unlock_irqrestore(pll->lock, flags); @@ -445,6 +467,8 @@ static void clk_pll_disable(struct clk_hw *hw) if (pll->lock) spin_lock_irqsave(pll->lock, flags); + pll_clk_stop_ss(pll); + _clk_pll_disable(hw); if (pll->lock) @@ -666,6 +690,8 @@ static void _get_pll_mnp(struct tegra_clk_pll *pll, struct tegra_clk_pll_params *params = pll->params; struct div_nmp *div_nmp = params->div_nmp; + *cfg = (struct tegra_clk_pll_freq_table) { }; + if ((params->flags & (TEGRA_PLLM | TEGRA_PLLMB)) && (pll_override_readl(PMC_PLLP_WB0_OVERRIDE, pll) & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)) { @@ -716,26 +742,6 @@ static void _update_pll_cpcon(struct tegra_clk_pll *pll, pll_writel_misc(val, pll); } -static void pll_clk_start_ss(struct tegra_clk_pll *pll) -{ - if (pll->params->defaults_set && pll->params->ssc_ctrl_reg) { - u32 val = pll_readl(pll->params->ssc_ctrl_reg, pll); - - val |= pll->params->ssc_ctrl_en_mask; - pll_writel(val, pll->params->ssc_ctrl_reg, pll); - } -} - -static void pll_clk_stop_ss(struct tegra_clk_pll *pll) -{ - if (pll->params->defaults_set && pll->params->ssc_ctrl_reg) { - u32 val = pll_readl(pll->params->ssc_ctrl_reg, pll); - - val &= ~pll->params->ssc_ctrl_en_mask; - pll_writel(val, pll->params->ssc_ctrl_reg, pll); - } -} - static int _program_pll(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, unsigned long rate) { @@ -2251,7 +2257,7 @@ tegra_clk_register_pllu_tegra114(const char *name, const char *parent_name, } #endif -#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC) +#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC) || defined(CONFIG_ARCH_TEGRA_210_SOC) static const struct clk_ops tegra_clk_pllss_ops = { .is_enabled = clk_pll_is_enabled, .enable = clk_pll_enable, @@ -2349,7 +2355,6 @@ struct clk *tegra_clk_register_pllre_tegra210(const char *name, struct tegra_clk_pll_params *pll_params, spinlock_t *lock, unsigned long parent_rate) { - u32 val; struct tegra_clk_pll *pll; struct clk *clk; @@ -2363,26 +2368,8 @@ struct clk *tegra_clk_register_pllre_tegra210(const char *name, if (IS_ERR(pll)) return ERR_CAST(pll); - /* program minimum rate by default */ - - val = pll_readl_base(pll); - if (val & PLL_BASE_ENABLE) - WARN_ON(readl_relaxed(clk_base + pll_params->iddq_reg) & - BIT(pll_params->iddq_bit_idx)); - else { - val = 0x4 << divm_shift(pll); - val |= 0x41 << divn_shift(pll); - pll_writel_base(val, pll); - } - - /* disable lock override */ - - val = pll_readl_misc(pll); - val &= ~BIT(29); - pll_writel_misc(val, pll); - clk = _tegra_clk_register_pll(pll, name, parent_name, flags, - &tegra_clk_pllre_ops); + &tegra_clk_pll_ops); if (IS_ERR(clk)) kfree(pll); @@ -2604,46 +2591,6 @@ struct clk *tegra_clk_register_pllc_tegra210(const char *name, return clk; } -struct clk *tegra_clk_register_pllxc_tegra210(const char *name, - const char *parent_name, void __iomem *clk_base, - void __iomem *pmc, unsigned long flags, - struct tegra_clk_pll_params *pll_params, - spinlock_t *lock) -{ - struct tegra_clk_pll *pll; - struct clk *clk, *parent; - unsigned long parent_rate; - - parent = __clk_lookup(parent_name); - if (!parent) { - WARN(1, "parent clk %s of %s must be registered first\n", - name, parent_name); - return ERR_PTR(-EINVAL); - } - - if (!pll_params->pdiv_tohw) - return ERR_PTR(-EINVAL); - - parent_rate = clk_get_rate(parent); - - pll_params->vco_min = _clip_vco_min(pll_params->vco_min, parent_rate); - - if (pll_params->adjust_vco) - pll_params->vco_min = pll_params->adjust_vco(pll_params, - parent_rate); - - pll = _tegra_init_pll(clk_base, pmc, pll_params, lock); - if (IS_ERR(pll)) - return ERR_CAST(pll); - - clk = _tegra_clk_register_pll(pll, name, parent_name, flags, - &tegra_clk_pll_ops); - if (IS_ERR(clk)) - kfree(pll); - - return clk; -} - struct clk *tegra_clk_register_pllss_tegra210(const char *name, const char *parent_name, void __iomem *clk_base, unsigned long flags, @@ -2652,10 +2599,8 @@ struct clk *tegra_clk_register_pllss_tegra210(const char *name, { struct tegra_clk_pll *pll; struct clk *clk, *parent; - struct tegra_clk_pll_freq_table cfg; unsigned long parent_rate; u32 val; - int i; if (!pll_params->div_nmp) return ERR_PTR(-EINVAL); @@ -2667,13 +2612,11 @@ struct clk *tegra_clk_register_pllss_tegra210(const char *name, return ERR_PTR(-EINVAL); } - pll = _tegra_init_pll(clk_base, NULL, pll_params, lock); - if (IS_ERR(pll)) - return ERR_CAST(pll); - - val = pll_readl_base(pll); - val &= ~PLLSS_REF_SRC_SEL_MASK; - pll_writel_base(val, pll); + val = readl_relaxed(clk_base + pll_params->base_reg); + if (val & PLLSS_REF_SRC_SEL_MASK) { + WARN(1, "not supported reference clock for %s\n", name); + return ERR_PTR(-EINVAL); + } parent_rate = clk_get_rate(parent); @@ -2683,36 +2626,10 @@ struct clk *tegra_clk_register_pllss_tegra210(const char *name, pll_params->vco_min = pll_params->adjust_vco(pll_params, parent_rate); - /* initialize PLL to minimum rate */ - - cfg.m = _pll_fixed_mdiv(pll_params, parent_rate); - cfg.n = cfg.m * pll_params->vco_min / parent_rate; - - for (i = 0; pll_params->pdiv_tohw[i].pdiv; i++) - ; - if (!i) { - kfree(pll); - return ERR_PTR(-EINVAL); - } - - cfg.p = pll_params->pdiv_tohw[i-1].hw_val; - - _update_pll_mnp(pll, &cfg); - - pll_writel_misc(PLLSS_MISC_DEFAULT, pll); - - val = pll_readl_base(pll); - if (val & PLL_BASE_ENABLE) { - if (val & BIT(pll_params->iddq_bit_idx)) { - WARN(1, "%s is on but IDDQ set\n", name); - kfree(pll); - return ERR_PTR(-EINVAL); - } - } else - val |= BIT(pll_params->iddq_bit_idx); - - val &= ~PLLSS_LOCK_OVERRIDE; - pll_writel_base(val, pll); + pll_params->flags |= TEGRA_PLL_BYPASS; + pll = _tegra_init_pll(clk_base, NULL, pll_params, lock); + if (IS_ERR(pll)) + return ERR_CAST(pll); clk = _tegra_clk_register_pll(pll, name, parent_name, flags, &tegra_clk_pll_ops); diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c index 294bfe40a4f5..848255cc0209 100644 --- a/drivers/clk/tegra/clk-tegra-periph.c +++ b/drivers/clk/tegra/clk-tegra-periph.c @@ -216,7 +216,8 @@ _clk_num, _clk_id) \ TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\ 30, MASK(2), 0, 0, 16, 0, TEGRA_DIVIDER_ROUND_UP,\ - _clk_num, 0, _clk_id, _parents##_idx, 0, NULL) + _clk_num, TEGRA_PERIPH_ON_APB, _clk_id, \ + _parents##_idx, 0, NULL) #define XUSB(_name, _parents, _offset, \ _clk_num, _gate_flags, _clk_id) \ diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c index 474de0f0c26d..4f6fd307cb70 100644 --- a/drivers/clk/tegra/clk-tegra-super-gen4.c +++ b/drivers/clk/tegra/clk-tegra-super-gen4.c @@ -232,8 +232,15 @@ static void __init tegra_super_clk_init(void __iomem *clk_base, if (!dt_clk) return; - clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base, - pmc_base, CLK_IGNORE_UNUSED, params, NULL); +#if defined(CONFIG_ARCH_TEGRA_210_SOC) + if (gen_info->gen == gen5) + clk = tegra_clk_register_pllc_tegra210("pll_x", "pll_ref", + clk_base, pmc_base, CLK_IGNORE_UNUSED, params, NULL); + else +#endif + clk = tegra_clk_register_pllxc("pll_x", "pll_ref", clk_base, + pmc_base, CLK_IGNORE_UNUSED, params, NULL); + *dt_clk = clk; /* PLLX_OUT0 */ diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index 1024e853ea65..6d7a613f2656 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -146,7 +146,7 @@ #define PLLD_SDM_EN_MASK BIT(16) #define PLLD2_SDM_EN_MASK BIT(31) -#define PLLD2_SSC_EN_MASK BIT(30) +#define PLLD2_SSC_EN_MASK 0 #define PLLDP_SS_CFG 0x598 #define PLLDP_SDM_EN_MASK BIT(31) @@ -241,6 +241,9 @@ #define PLL_SDM_COEFF BIT(13) #define sdin_din_to_data(din) ((u16)((din) ? : 0xFFFFU)) #define sdin_data_to_din(dat) (((dat) == 0xFFFFU) ? 0 : (s16)dat) +/* This macro returns ndiv effective scaled to SDM range */ +#define sdin_get_n_eff(cfg) ((cfg)->n * PLL_SDM_COEFF + ((cfg)->sdm_data ? \ + (PLL_SDM_COEFF/2 + sdin_data_to_din((cfg)->sdm_data)) : 0)) /* Tegra CPU clock and reset control regs */ #define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS 0x470 @@ -715,8 +718,6 @@ static void plldss_defaults(const char *pll_name, struct tegra_clk_pll *plldss, plldss->params->defaults_set = true; if (val & PLL_ENABLE) { - pr_warn("%s already enabled. Postponing set full defaults\n", - pll_name); /* * PLL is ON: check if defaults already set, then set those @@ -755,6 +756,10 @@ static void plldss_defaults(const char *pll_name, struct tegra_clk_pll *plldss, (~PLLDSS_MISC1_CFG_EN_SDM)); } + if (!plldss->params->defaults_set) + pr_warn("%s already enabled. Postponing set full defaults\n", + pll_name); + /* Enable lock detect */ if (val & PLLDSS_BASE_LOCK_OVERRIDE) { val &= ~PLLDSS_BASE_LOCK_OVERRIDE; @@ -1288,8 +1293,7 @@ static int tegra210_pll_fixed_mdiv_cfg(struct clk_hw *hw, s -= PLL_SDM_COEFF / 2; cfg->sdm_data = sdin_din_to_data(s); } - cfg->output_rate *= cfg->n * PLL_SDM_COEFF + PLL_SDM_COEFF/2 + - sdin_data_to_din(cfg->sdm_data); + cfg->output_rate *= sdin_get_n_eff(cfg); cfg->output_rate /= p * cfg->m * PLL_SDM_COEFF; } else { cfg->output_rate *= cfg->n; @@ -1314,8 +1318,7 @@ static int tegra210_pll_fixed_mdiv_cfg(struct clk_hw *hw, */ static void tegra210_clk_pll_set_gain(struct tegra_clk_pll_freq_table *cfg) { - cfg->n = cfg->n * PLL_SDM_COEFF + PLL_SDM_COEFF/2 + - sdin_data_to_din(cfg->sdm_data); + cfg->n = sdin_get_n_eff(cfg); cfg->m *= PLL_SDM_COEFF; } @@ -2204,7 +2207,6 @@ static struct tegra_clk tegra210_clks[tegra_clk_max] __initdata = { [tegra_clk_gpu] = { .dt_id = TEGRA210_CLK_GPU, .present = true }, [tegra_clk_pll_g_ref] = { .dt_id = TEGRA210_CLK_PLL_G_REF, .present = true, }, [tegra_clk_uartb_8] = { .dt_id = TEGRA210_CLK_UARTB, .present = true }, - [tegra_clk_vfir] = { .dt_id = TEGRA210_CLK_VFIR, .present = true }, [tegra_clk_spdif_in_8] = { .dt_id = TEGRA210_CLK_SPDIF_IN, .present = true }, [tegra_clk_spdif_out] = { .dt_id = TEGRA210_CLK_SPDIF_OUT, .present = true }, [tegra_clk_vi_10] = { .dt_id = TEGRA210_CLK_VI, .present = true }, @@ -2470,15 +2472,14 @@ static void tegra210_utmi_param_configure(void) reg |= UTMIP_PLL_CFG2_STABLE_COUNT(utmi_parameters[i].stable_count); reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0); - reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(utmi_parameters[i].active_delay_count); writel_relaxed(reg, clk_base + UTMIP_PLL_CFG2); /* Program UTMIP PLL delay and oscillator frequency counts */ reg = readl_relaxed(clk_base + UTMIP_PLL_CFG1); - reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0); + reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0); reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(utmi_parameters[i].enable_delay_count); @@ -2494,7 +2495,8 @@ static void tegra210_utmi_param_configure(void) reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN; reg |= UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERUP; writel_relaxed(reg, clk_base + UTMIP_PLL_CFG1); - udelay(1); + + udelay(20); /* Enable samplers for SNPS, XUSB_HOST, XUSB_DEV */ reg = readl_relaxed(clk_base + UTMIP_PLL_CFG2); @@ -2552,6 +2554,7 @@ static int tegra210_enable_pllu(void) reg = readl_relaxed(clk_base + pllu.params->ext_misc_reg[0]); reg &= ~BIT(pllu.params->iddq_bit_idx); writel_relaxed(reg, clk_base + pllu.params->ext_misc_reg[0]); + udelay(5); reg = readl_relaxed(clk_base + PLLU_BASE); reg &= ~GENMASK(20, 0); @@ -2559,6 +2562,7 @@ static int tegra210_enable_pllu(void) reg |= fentry->n << 8; reg |= fentry->p << 16; writel(reg, clk_base + PLLU_BASE); + udelay(1); reg |= PLL_ENABLE; writel(reg, clk_base + PLLU_BASE); @@ -2699,7 +2703,7 @@ static void __init tegra210_pll_init(void __iomem *clk_base, struct clk *clk; /* PLLC */ - clk = tegra_clk_register_pllxc_tegra210("pll_c", "pll_ref", clk_base, + clk = tegra_clk_register_pllc_tegra210("pll_c", "pll_ref", clk_base, pmc, 0, &pll_c_params, NULL); if (!WARN_ON(IS_ERR(clk))) clk_register_clkdev(clk, "pll_c", NULL); @@ -2798,14 +2802,14 @@ static void __init tegra210_pll_init(void __iomem *clk_base, /* PLLU_60M */ clk = clk_register_gate(NULL, "pll_u_60M", "pll_u_out2", CLK_SET_RATE_PARENT, clk_base + PLLU_BASE, - 23, 0, NULL); + 23, 0, &pll_u_lock); clk_register_clkdev(clk, "pll_u_60M", NULL); clks[TEGRA210_CLK_PLL_U_60M] = clk; /* PLLU_48M */ clk = clk_register_gate(NULL, "pll_u_48M", "pll_u_out1", CLK_SET_RATE_PARENT, clk_base + PLLU_BASE, - 25, 0, NULL); + 25, 0, &pll_u_lock); clk_register_clkdev(clk, "pll_u_48M", NULL); clks[TEGRA210_CLK_PLL_U_48M] = clk; diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 945b07093afa..872f1189ad7f 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -362,12 +362,6 @@ struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name, struct tegra_clk_pll_params *pll_params, spinlock_t *lock); -struct clk *tegra_clk_register_pllxc_tegra210(const char *name, - const char *parent_name, void __iomem *clk_base, - void __iomem *pmc, unsigned long flags, - struct tegra_clk_pll_params *pll_params, - spinlock_t *lock); - struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name, void __iomem *clk_base, void __iomem *pmc, unsigned long flags, diff --git a/drivers/clk/ti/adpll.c b/drivers/clk/ti/adpll.c index 255cafb18336..d6036c788fab 100644 --- a/drivers/clk/ti/adpll.c +++ b/drivers/clk/ti/adpll.c @@ -222,7 +222,7 @@ static int ti_adpll_setup_clock(struct ti_adpll_data *d, struct clk *clock, /* Separate con_id in format "pll040dcoclkldo" to fit MAX_CON_ID */ postfix = strrchr(name, '.'); - if (strlen(postfix) > 1) { + if (postfix && strlen(postfix) > 1) { if (strlen(postfix) > ADPLL_MAX_CON_ID) dev_warn(d->dev, "clock %s con_id lookup may fail\n", name); @@ -486,7 +486,7 @@ static u8 ti_adpll_get_parent(struct clk_hw *hw) return 0; } -static struct clk_ops ti_adpll_ops = { +static const struct clk_ops ti_adpll_ops = { .prepare = ti_adpll_prepare, .unprepare = ti_adpll_unprepare, .is_prepared = ti_adpll_is_prepared, diff --git a/drivers/clk/ti/apll.c b/drivers/clk/ti/apll.c index 06f486b3488c..83b148f8037c 100644 --- a/drivers/clk/ti/apll.c +++ b/drivers/clk/ti/apll.c @@ -304,7 +304,7 @@ static void omap2_apll_disable(struct clk_hw *hw) ti_clk_ll_ops->clk_writel(v, &ad->control_reg); } -static struct clk_ops omap2_apll_ops = { +static const struct clk_ops omap2_apll_ops = { .enable = &omap2_apll_enable, .disable = &omap2_apll_disable, .is_enabled = &omap2_apll_is_enabled, diff --git a/drivers/clk/ti/clockdomain.c b/drivers/clk/ti/clockdomain.c index fbedc6a9fed0..07a805125e98 100644 --- a/drivers/clk/ti/clockdomain.c +++ b/drivers/clk/ti/clockdomain.c @@ -138,8 +138,8 @@ static void __init of_ti_clockdomain_setup(struct device_node *node) for (i = 0; i < num_clks; i++) { clk = of_clk_get(node, i); if (IS_ERR(clk)) { - pr_err("%s: Failed get %s' clock nr %d (%ld)\n", - __func__, node->full_name, i, PTR_ERR(clk)); + pr_err("%s: Failed get %pOF' clock nr %d (%ld)\n", + __func__, node, i, PTR_ERR(clk)); continue; } clk_hw = __clk_get_hw(clk); diff --git a/drivers/clk/ti/fapll.c b/drivers/clk/ti/fapll.c index 66a0d0ed8b55..071af44b1ba8 100644 --- a/drivers/clk/ti/fapll.c +++ b/drivers/clk/ti/fapll.c @@ -268,7 +268,7 @@ static int ti_fapll_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static struct clk_ops ti_fapll_ops = { +static const struct clk_ops ti_fapll_ops = { .enable = ti_fapll_enable, .disable = ti_fapll_disable, .is_enabled = ti_fapll_is_enabled, @@ -478,7 +478,7 @@ static int ti_fapll_synth_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static struct clk_ops ti_fapll_synt_ops = { +static const struct clk_ops ti_fapll_synt_ops = { .enable = ti_fapll_synth_enable, .disable = ti_fapll_synth_disable, .is_enabled = ti_fapll_synth_is_enabled, diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c index 2cf386347f0c..e09f3dd46318 100644 --- a/drivers/clk/uniphier/clk-uniphier-core.c +++ b/drivers/clk/uniphier/clk-uniphier-core.c @@ -111,10 +111,6 @@ static int uniphier_clk_remove(struct platform_device *pdev) static const struct of_device_id uniphier_clk_match[] = { /* System clock */ { - .compatible = "socionext,uniphier-sld3-clock", - .data = uniphier_sld3_sys_clk_data, - }, - { .compatible = "socionext,uniphier-ld4-clock", .data = uniphier_ld4_sys_clk_data, }, @@ -142,22 +138,22 @@ static const struct of_device_id uniphier_clk_match[] = { .compatible = "socionext,uniphier-ld20-clock", .data = uniphier_ld20_sys_clk_data, }, - /* Media I/O clock, SD clock */ { - .compatible = "socionext,uniphier-sld3-mio-clock", - .data = uniphier_sld3_mio_clk_data, + .compatible = "socionext,uniphier-pxs3-clock", + .data = uniphier_pxs3_sys_clk_data, }, + /* Media I/O clock, SD clock */ { .compatible = "socionext,uniphier-ld4-mio-clock", - .data = uniphier_sld3_mio_clk_data, + .data = uniphier_ld4_mio_clk_data, }, { .compatible = "socionext,uniphier-pro4-mio-clock", - .data = uniphier_sld3_mio_clk_data, + .data = uniphier_ld4_mio_clk_data, }, { .compatible = "socionext,uniphier-sld8-mio-clock", - .data = uniphier_sld3_mio_clk_data, + .data = uniphier_ld4_mio_clk_data, }, { .compatible = "socionext,uniphier-pro5-sd-clock", @@ -169,12 +165,16 @@ static const struct of_device_id uniphier_clk_match[] = { }, { .compatible = "socionext,uniphier-ld11-mio-clock", - .data = uniphier_sld3_mio_clk_data, + .data = uniphier_ld4_mio_clk_data, }, { .compatible = "socionext,uniphier-ld20-sd-clock", .data = uniphier_pro5_sd_clk_data, }, + { + .compatible = "socionext,uniphier-pxs3-sd-clock", + .data = uniphier_pro5_sd_clk_data, + }, /* Peripheral clock */ { .compatible = "socionext,uniphier-ld4-peri-clock", @@ -204,6 +204,10 @@ static const struct of_device_id uniphier_clk_match[] = { .compatible = "socionext,uniphier-ld20-peri-clock", .data = uniphier_pro4_peri_clk_data, }, + { + .compatible = "socionext,uniphier-pxs3-peri-clock", + .data = uniphier_pro4_peri_clk_data, + }, { /* sentinel */ } }; diff --git a/drivers/clk/uniphier/clk-uniphier-mio.c b/drivers/clk/uniphier/clk-uniphier-mio.c index 218d20f099ce..16e4d303f535 100644 --- a/drivers/clk/uniphier/clk-uniphier-mio.c +++ b/drivers/clk/uniphier/clk-uniphier-mio.c @@ -76,7 +76,7 @@ #define UNIPHIER_MIO_CLK_DMAC(idx) \ UNIPHIER_CLK_GATE("miodmac", (idx), "stdmac", 0x20, 25) -const struct uniphier_clk_data uniphier_sld3_mio_clk_data[] = { +const struct uniphier_clk_data uniphier_ld4_mio_clk_data[] = { UNIPHIER_MIO_CLK_SD_FIXED, UNIPHIER_MIO_CLK_SD(0, 0), UNIPHIER_MIO_CLK_SD(1, 1), @@ -85,11 +85,9 @@ const struct uniphier_clk_data uniphier_sld3_mio_clk_data[] = { UNIPHIER_MIO_CLK_USB2(8, 0), UNIPHIER_MIO_CLK_USB2(9, 1), UNIPHIER_MIO_CLK_USB2(10, 2), - UNIPHIER_MIO_CLK_USB2(11, 3), UNIPHIER_MIO_CLK_USB2_PHY(12, 0), UNIPHIER_MIO_CLK_USB2_PHY(13, 1), UNIPHIER_MIO_CLK_USB2_PHY(14, 2), - UNIPHIER_MIO_CLK_USB2_PHY(15, 3), { /* sentinel */ } }; diff --git a/drivers/clk/uniphier/clk-uniphier-sys.c b/drivers/clk/uniphier/clk-uniphier-sys.c index ad0218182a9f..0e396f3da526 100644 --- a/drivers/clk/uniphier/clk-uniphier-sys.c +++ b/drivers/clk/uniphier/clk-uniphier-sys.c @@ -17,7 +17,7 @@ #include "clk-uniphier.h" -#define UNIPHIER_SLD3_SYS_CLK_SD \ +#define UNIPHIER_LD4_SYS_CLK_SD \ UNIPHIER_CLK_FACTOR("sd-200m", -1, "spll", 1, 8), \ UNIPHIER_CLK_FACTOR("sd-133m", -1, "vpll27a", 1, 2) @@ -30,7 +30,7 @@ UNIPHIER_CLK_FACTOR("sd-133m", -1, "spll", 1, 15) /* Denali driver requires clk_x rate (clk: 50MHz, clk_x & ecc_clk: 200MHz) */ -#define UNIPHIER_SLD3_SYS_CLK_NAND(idx) \ +#define UNIPHIER_LD4_SYS_CLK_NAND(idx) \ UNIPHIER_CLK_FACTOR("nand-200m", -1, "spll", 1, 8), \ UNIPHIER_CLK_GATE("nand", (idx), "nand-200m", 0x2104, 2) @@ -45,7 +45,7 @@ #define UNIPHIER_LD11_SYS_CLK_EMMC(idx) \ UNIPHIER_CLK_GATE("emmc", (idx), NULL, 0x210c, 2) -#define UNIPHIER_SLD3_SYS_CLK_STDMAC(idx) \ +#define UNIPHIER_LD4_SYS_CLK_STDMAC(idx) \ UNIPHIER_CLK_GATE("stdmac", (idx), NULL, 0x2104, 10) #define UNIPHIER_LD11_SYS_CLK_STDMAC(idx) \ @@ -57,19 +57,23 @@ #define UNIPHIER_PRO4_SYS_CLK_USB3(idx, ch) \ UNIPHIER_CLK_GATE("usb3" #ch, (idx), NULL, 0x2104, 16 + (ch)) -const struct uniphier_clk_data uniphier_sld3_sys_clk_data[] = { - UNIPHIER_CLK_FACTOR("spll", -1, "ref", 65, 1), /* 1597.44 MHz */ - UNIPHIER_CLK_FACTOR("upll", -1, "ref", 6000, 512), /* 288 MHz */ - UNIPHIER_CLK_FACTOR("a2pll", -1, "ref", 24, 1), /* 589.824 MHz */ - UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 5625, 512), /* 270 MHz */ - UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 16), - UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), - UNIPHIER_SLD3_SYS_CLK_NAND(2), - UNIPHIER_SLD3_SYS_CLK_SD, - UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), - UNIPHIER_SLD3_SYS_CLK_STDMAC(8), - { /* sentinel */ } -}; +#define UNIPHIER_LD11_SYS_CLK_AIO(idx) \ + UNIPHIER_CLK_FACTOR("aio-io200m", -1, "spll", 1, 10), \ + UNIPHIER_CLK_GATE("aio", (idx), "aio-io200m", 0x2108, 0) + +#define UNIPHIER_LD11_SYS_CLK_EVEA(idx) \ + UNIPHIER_CLK_FACTOR("evea-io100m", -1, "spll", 1, 20), \ + UNIPHIER_CLK_GATE("evea", (idx), "evea-io100m", 0x2108, 1) + +#define UNIPHIER_LD11_SYS_CLK_EXIV(idx) \ + UNIPHIER_CLK_FACTOR("exiv-io200m", -1, "spll", 1, 10), \ + UNIPHIER_CLK_GATE("exiv", (idx), "exiv-io200m", 0x2110, 2) + +#define UNIPHIER_PRO4_SYS_CLK_ETHER(idx) \ + UNIPHIER_CLK_GATE("ether", (idx), NULL, 0x2104, 12) + +#define UNIPHIER_LD11_SYS_CLK_ETHER(idx) \ + UNIPHIER_CLK_GATE("ether", (idx), NULL, 0x210c, 6) const struct uniphier_clk_data uniphier_ld4_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("spll", -1, "ref", 65, 1), /* 1597.44 MHz */ @@ -78,10 +82,10 @@ const struct uniphier_clk_data uniphier_ld4_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 5625, 512), /* 270 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 16), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), - UNIPHIER_SLD3_SYS_CLK_NAND(2), - UNIPHIER_SLD3_SYS_CLK_SD, + UNIPHIER_LD4_SYS_CLK_NAND(2), + UNIPHIER_LD4_SYS_CLK_SD, UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), - UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */ + UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */ { /* sentinel */ } }; @@ -92,10 +96,11 @@ const struct uniphier_clk_data uniphier_pro4_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 8), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 32), - UNIPHIER_SLD3_SYS_CLK_NAND(2), - UNIPHIER_SLD3_SYS_CLK_SD, + UNIPHIER_LD4_SYS_CLK_NAND(2), + UNIPHIER_LD4_SYS_CLK_SD, UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), - UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC, MIO, RLE */ + UNIPHIER_PRO4_SYS_CLK_ETHER(6), + UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* HSC, MIO, RLE */ UNIPHIER_PRO4_SYS_CLK_GIO(12), /* Ether, SATA, USB3 */ UNIPHIER_PRO4_SYS_CLK_USB3(14, 0), UNIPHIER_PRO4_SYS_CLK_USB3(15, 1), @@ -108,10 +113,10 @@ const struct uniphier_clk_data uniphier_sld8_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */ UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 20), UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 16), - UNIPHIER_SLD3_SYS_CLK_NAND(2), - UNIPHIER_SLD3_SYS_CLK_SD, + UNIPHIER_LD4_SYS_CLK_NAND(2), + UNIPHIER_LD4_SYS_CLK_SD, UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12), - UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */ + UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* Ether, HSC, MIO */ { /* sentinel */ } }; @@ -123,7 +128,7 @@ const struct uniphier_clk_data uniphier_pro5_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48), UNIPHIER_PRO5_SYS_CLK_NAND(2), UNIPHIER_PRO5_SYS_CLK_SD, - UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC */ + UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* HSC */ UNIPHIER_PRO4_SYS_CLK_GIO(12), /* PCIe, USB3 */ UNIPHIER_PRO4_SYS_CLK_USB3(14, 0), UNIPHIER_PRO4_SYS_CLK_USB3(15, 1), @@ -136,7 +141,8 @@ const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[] = { UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 48), UNIPHIER_PRO5_SYS_CLK_NAND(2), UNIPHIER_PRO5_SYS_CLK_SD, - UNIPHIER_SLD3_SYS_CLK_STDMAC(8), /* HSC, RLE */ + UNIPHIER_PRO4_SYS_CLK_ETHER(6), + UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* HSC, RLE */ /* GIO is always clock-enabled: no function for 0x2104 bit6 */ UNIPHIER_PRO4_SYS_CLK_USB3(14, 0), UNIPHIER_PRO4_SYS_CLK_USB3(15, 1), @@ -156,8 +162,12 @@ const struct uniphier_clk_data uniphier_ld11_sys_clk_data[] = { UNIPHIER_LD11_SYS_CLK_NAND(2), UNIPHIER_LD11_SYS_CLK_EMMC(4), /* Index 5 reserved for eMMC PHY */ + UNIPHIER_LD11_SYS_CLK_ETHER(6), UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC, MIO */ UNIPHIER_CLK_FACTOR("usb2", -1, "ref", 24, 25), + UNIPHIER_LD11_SYS_CLK_AIO(40), + UNIPHIER_LD11_SYS_CLK_EVEA(41), + UNIPHIER_LD11_SYS_CLK_EXIV(42), /* CPU gears */ UNIPHIER_CLK_DIV4("cpll", 2, 3, 4, 8), UNIPHIER_CLK_DIV4("mpll", 2, 3, 4, 8), @@ -185,6 +195,7 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = { UNIPHIER_LD11_SYS_CLK_EMMC(4), /* Index 5 reserved for eMMC PHY */ UNIPHIER_LD20_SYS_CLK_SD, + UNIPHIER_LD11_SYS_CLK_ETHER(6), UNIPHIER_LD11_SYS_CLK_STDMAC(8), /* HSC */ /* GIO is always clock-enabled: no function for 0x210c bit5 */ /* @@ -194,6 +205,9 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = { UNIPHIER_CLK_GATE("usb30", 14, NULL, 0x210c, 14), UNIPHIER_CLK_GATE("usb30-phy0", 16, NULL, 0x210c, 12), UNIPHIER_CLK_GATE("usb30-phy1", 17, NULL, 0x210c, 13), + UNIPHIER_LD11_SYS_CLK_AIO(40), + UNIPHIER_LD11_SYS_CLK_EVEA(41), + UNIPHIER_LD11_SYS_CLK_EXIV(42), /* CPU gears */ UNIPHIER_CLK_DIV4("cpll", 2, 3, 4, 8), UNIPHIER_CLK_DIV4("spll", 2, 3, 4, 8), @@ -209,3 +223,33 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = { "spll/4", "spll/8", "s2pll/4", "s2pll/8"), { /* sentinel */ } }; + +const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[] = { + UNIPHIER_CLK_FACTOR("cpll", -1, "ref", 104, 1), /* ARM: 2600 MHz */ + UNIPHIER_CLK_FACTOR("spll", -1, "ref", 80, 1), /* 2000 MHz */ + UNIPHIER_CLK_FACTOR("s2pll", -1, "ref", 88, 1), /* IPP: 2400 MHz */ + UNIPHIER_CLK_FACTOR("uart", 0, "spll", 1, 34), + UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 40), + UNIPHIER_LD20_SYS_CLK_SD, + UNIPHIER_LD11_SYS_CLK_NAND(2), + UNIPHIER_LD11_SYS_CLK_EMMC(4), + UNIPHIER_CLK_GATE("usb30", 12, NULL, 0x2104, 4), /* =GIO0 */ + UNIPHIER_CLK_GATE("usb31-0", 13, NULL, 0x2104, 5), /* =GIO1 */ + UNIPHIER_CLK_GATE("usb31-1", 14, NULL, 0x2104, 6), /* =GIO1-1 */ + UNIPHIER_CLK_GATE("usb30-phy0", 16, NULL, 0x210c, 16), + UNIPHIER_CLK_GATE("usb30-phy1", 17, NULL, 0x210c, 18), + UNIPHIER_CLK_GATE("usb30-phy2", 18, NULL, 0x210c, 20), + UNIPHIER_CLK_GATE("usb31-phy0", 20, NULL, 0x210c, 17), + UNIPHIER_CLK_GATE("usb31-phy1", 21, NULL, 0x210c, 19), + /* CPU gears */ + UNIPHIER_CLK_DIV4("cpll", 2, 3, 4, 8), + UNIPHIER_CLK_DIV4("spll", 2, 3, 4, 8), + UNIPHIER_CLK_DIV4("s2pll", 2, 3, 4, 8), + UNIPHIER_CLK_CPUGEAR("cpu-ca53", 33, 0x8080, 0xf, 8, + "cpll/2", "spll/2", "cpll/3", "spll/3", + "spll/4", "spll/8", "cpll/4", "cpll/8"), + UNIPHIER_CLK_CPUGEAR("cpu-ipp", 34, 0x8100, 0xf, 8, + "s2pll/2", "spll/2", "s2pll/3", "spll/3", + "spll/4", "spll/8", "s2pll/4", "s2pll/8"), + { /* sentinel */ } +}; diff --git a/drivers/clk/uniphier/clk-uniphier.h b/drivers/clk/uniphier/clk-uniphier.h index 01c16ecec48f..d10a009ada96 100644 --- a/drivers/clk/uniphier/clk-uniphier.h +++ b/drivers/clk/uniphier/clk-uniphier.h @@ -147,7 +147,6 @@ struct clk_hw *uniphier_clk_register_mux(struct device *dev, const char *name, const struct uniphier_clk_mux_data *data); -extern const struct uniphier_clk_data uniphier_sld3_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_ld4_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_pro4_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_sld8_sys_clk_data[]; @@ -155,7 +154,8 @@ extern const struct uniphier_clk_data uniphier_pro5_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_ld11_sys_clk_data[]; extern const struct uniphier_clk_data uniphier_ld20_sys_clk_data[]; -extern const struct uniphier_clk_data uniphier_sld3_mio_clk_data[]; +extern const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[]; +extern const struct uniphier_clk_data uniphier_ld4_mio_clk_data[]; extern const struct uniphier_clk_data uniphier_pro5_sd_clk_data[]; extern const struct uniphier_clk_data uniphier_ld4_peri_clk_data[]; extern const struct uniphier_clk_data uniphier_pro4_peri_clk_data[]; diff --git a/drivers/clk/ux500/clk-prcc.c b/drivers/clk/ux500/clk-prcc.c index 0e950769ed03..f50592775c9d 100644 --- a/drivers/clk/ux500/clk-prcc.c +++ b/drivers/clk/ux500/clk-prcc.c @@ -79,13 +79,13 @@ static int clk_prcc_is_enabled(struct clk_hw *hw) return clk->is_enabled; } -static struct clk_ops clk_prcc_pclk_ops = { +static const struct clk_ops clk_prcc_pclk_ops = { .enable = clk_prcc_pclk_enable, .disable = clk_prcc_pclk_disable, .is_enabled = clk_prcc_is_enabled, }; -static struct clk_ops clk_prcc_kclk_ops = { +static const struct clk_ops clk_prcc_kclk_ops = { .enable = clk_prcc_kclk_enable, .disable = clk_prcc_kclk_disable, .is_enabled = clk_prcc_is_enabled, @@ -96,7 +96,7 @@ static struct clk *clk_reg_prcc(const char *name, resource_size_t phy_base, u32 cg_sel, unsigned long flags, - struct clk_ops *clk_prcc_ops) + const struct clk_ops *clk_prcc_ops) { struct clk_prcc *clk; struct clk_init_data clk_prcc_init; diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c index 7f343821f4e4..6e3e16b2e5ca 100644 --- a/drivers/clk/ux500/clk-prcmu.c +++ b/drivers/clk/ux500/clk-prcmu.c @@ -186,7 +186,7 @@ static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) clk->is_prepared = 0; } -static struct clk_ops clk_prcmu_scalable_ops = { +static const struct clk_ops clk_prcmu_scalable_ops = { .prepare = clk_prcmu_prepare, .unprepare = clk_prcmu_unprepare, .is_prepared = clk_prcmu_is_prepared, @@ -198,7 +198,7 @@ static struct clk_ops clk_prcmu_scalable_ops = { .set_rate = clk_prcmu_set_rate, }; -static struct clk_ops clk_prcmu_gate_ops = { +static const struct clk_ops clk_prcmu_gate_ops = { .prepare = clk_prcmu_prepare, .unprepare = clk_prcmu_unprepare, .is_prepared = clk_prcmu_is_prepared, @@ -208,19 +208,19 @@ static struct clk_ops clk_prcmu_gate_ops = { .recalc_rate = clk_prcmu_recalc_rate, }; -static struct clk_ops clk_prcmu_scalable_rate_ops = { +static const struct clk_ops clk_prcmu_scalable_rate_ops = { .is_enabled = clk_prcmu_is_enabled, .recalc_rate = clk_prcmu_recalc_rate, .round_rate = clk_prcmu_round_rate, .set_rate = clk_prcmu_set_rate, }; -static struct clk_ops clk_prcmu_rate_ops = { +static const struct clk_ops clk_prcmu_rate_ops = { .is_enabled = clk_prcmu_is_enabled, .recalc_rate = clk_prcmu_recalc_rate, }; -static struct clk_ops clk_prcmu_opp_gate_ops = { +static const struct clk_ops clk_prcmu_opp_gate_ops = { .prepare = clk_prcmu_opp_prepare, .unprepare = clk_prcmu_opp_unprepare, .is_prepared = clk_prcmu_is_prepared, @@ -230,7 +230,7 @@ static struct clk_ops clk_prcmu_opp_gate_ops = { .recalc_rate = clk_prcmu_recalc_rate, }; -static struct clk_ops clk_prcmu_opp_volt_scalable_ops = { +static const struct clk_ops clk_prcmu_opp_volt_scalable_ops = { .prepare = clk_prcmu_opp_volt_prepare, .unprepare = clk_prcmu_opp_volt_unprepare, .is_prepared = clk_prcmu_is_prepared, @@ -247,7 +247,7 @@ static struct clk *clk_reg_prcmu(const char *name, u8 cg_sel, unsigned long rate, unsigned long flags, - struct clk_ops *clk_prcmu_ops) + const struct clk_ops *clk_prcmu_ops) { struct clk_prcmu *clk; struct clk_init_data clk_prcmu_init; diff --git a/drivers/clk/ux500/clk-sysctrl.c b/drivers/clk/ux500/clk-sysctrl.c index 266ddea630d2..8a4e93ce1e42 100644 --- a/drivers/clk/ux500/clk-sysctrl.c +++ b/drivers/clk/ux500/clk-sysctrl.c @@ -98,18 +98,18 @@ static u8 clk_sysctrl_get_parent(struct clk_hw *hw) return clk->parent_index; } -static struct clk_ops clk_sysctrl_gate_ops = { +static const struct clk_ops clk_sysctrl_gate_ops = { .prepare = clk_sysctrl_prepare, .unprepare = clk_sysctrl_unprepare, }; -static struct clk_ops clk_sysctrl_gate_fixed_rate_ops = { +static const struct clk_ops clk_sysctrl_gate_fixed_rate_ops = { .prepare = clk_sysctrl_prepare, .unprepare = clk_sysctrl_unprepare, .recalc_rate = clk_sysctrl_recalc_rate, }; -static struct clk_ops clk_sysctrl_set_parent_ops = { +static const struct clk_ops clk_sysctrl_set_parent_ops = { .set_parent = clk_sysctrl_set_parent, .get_parent = clk_sysctrl_get_parent, }; @@ -124,7 +124,7 @@ static struct clk *clk_reg_sysctrl(struct device *dev, unsigned long rate, unsigned long enable_delay_us, unsigned long flags, - struct clk_ops *clk_sysctrl_ops) + const struct clk_ops *clk_sysctrl_ops) { struct clk_sysctrl *clk; struct clk_init_data clk_sysctrl_init; diff --git a/drivers/clk/versatile/clk-vexpress-osc.c b/drivers/clk/versatile/clk-vexpress-osc.c index 7e5add7d7752..e7a868b83fe5 100644 --- a/drivers/clk/versatile/clk-vexpress-osc.c +++ b/drivers/clk/versatile/clk-vexpress-osc.c @@ -61,7 +61,7 @@ static int vexpress_osc_set_rate(struct clk_hw *hw, unsigned long rate, return regmap_write(osc->reg, 0, rate); } -static struct clk_ops vexpress_osc_ops = { +static const struct clk_ops vexpress_osc_ops = { .recalc_rate = vexpress_osc_recalc_rate, .round_rate = vexpress_osc_round_rate, .set_rate = vexpress_osc_set_rate, diff --git a/drivers/clk/zte/clk-zx296718.c b/drivers/clk/zte/clk-zx296718.c index 27f853d4c76b..354dd508c516 100644 --- a/drivers/clk/zte/clk-zx296718.c +++ b/drivers/clk/zte/clk-zx296718.c @@ -451,7 +451,7 @@ static struct zx_clk_fixed_factor top_ffactor_clk[] = { FFACTOR(0, "emmc_mux_div2", "emmc_mux", 1, 2, CLK_SET_RATE_PARENT), }; -static struct clk_div_table noc_div_table[] = { +static const struct clk_div_table noc_div_table[] = { { .val = 1, .div = 2, }, { .val = 3, .div = 4, }, }; @@ -644,7 +644,7 @@ static int __init top_clocks_init(struct device_node *np) return 0; } -static struct clk_div_table common_even_div_table[] = { +static const struct clk_div_table common_even_div_table[] = { { .val = 0, .div = 1, }, { .val = 1, .div = 2, }, { .val = 3, .div = 4, }, @@ -656,7 +656,7 @@ static struct clk_div_table common_even_div_table[] = { { .val = 15, .div = 16, }, }; -static struct clk_div_table common_div_table[] = { +static const struct clk_div_table common_div_table[] = { { .val = 0, .div = 1, }, { .val = 1, .div = 2, }, { .val = 2, .div = 3, }, diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 277a7a02cba5..acc816b67582 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -1897,6 +1897,8 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) ctrl->cntlid = le16_to_cpu(id->cntlid); ctrl->hmpre = le32_to_cpu(id->hmpre); ctrl->hmmin = le32_to_cpu(id->hmmin); + ctrl->hmminds = le32_to_cpu(id->hmminds); + ctrl->hmmaxd = le16_to_cpu(id->hmmaxd); } kfree(id); @@ -2377,10 +2379,11 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid) nvme_report_ns_ids(ctrl, ns->ns_id, id, ns->eui, ns->nguid, &ns->uuid); - if (nvme_nvm_ns_supported(ns, id) && - nvme_nvm_register(ns, disk_name, node)) { - dev_warn(ctrl->device, "%s: LightNVM init failure\n", __func__); - goto out_free_id; + if ((ctrl->quirks & NVME_QUIRK_LIGHTNVM) && id->vs[0] == 0x1) { + if (nvme_nvm_register(ns, disk_name, node)) { + dev_warn(ctrl->device, "LightNVM init failure\n"); + goto out_free_id; + } } disk = alloc_disk_node(0, node); diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c index c1a28569e843..1f79e3f141e6 100644 --- a/drivers/nvme/host/lightnvm.c +++ b/drivers/nvme/host/lightnvm.c @@ -955,29 +955,3 @@ void nvme_nvm_unregister_sysfs(struct nvme_ns *ns) sysfs_remove_group(&disk_to_dev(ns->disk)->kobj, &nvm_dev_attr_group); } - -/* move to shared place when used in multiple places. */ -#define PCI_VENDOR_ID_CNEX 0x1d1d -#define PCI_DEVICE_ID_CNEX_WL 0x2807 -#define PCI_DEVICE_ID_CNEX_QEMU 0x1f1f - -int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id) -{ - struct nvme_ctrl *ctrl = ns->ctrl; - /* XXX: this is poking into PCI structures from generic code! */ - struct pci_dev *pdev = to_pci_dev(ctrl->dev); - - /* QEMU NVMe simulator - PCI ID + Vendor specific bit */ - if (pdev->vendor == PCI_VENDOR_ID_CNEX && - pdev->device == PCI_DEVICE_ID_CNEX_QEMU && - id->vs[0] == 0x1) - return 1; - - /* CNEX Labs - PCI ID + Vendor specific bit */ - if (pdev->vendor == PCI_VENDOR_ID_CNEX && - pdev->device == PCI_DEVICE_ID_CNEX_WL && - id->vs[0] == 0x1) - return 1; - - return 0; -} diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index a19a587d60ed..d3f3c4447515 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -75,6 +75,11 @@ enum nvme_quirks { * The deepest sleep state should not be used. */ NVME_QUIRK_NO_DEEPEST_PS = (1 << 5), + + /* + * Supports the LighNVM command set if indicated in vs[1]. + */ + NVME_QUIRK_LIGHTNVM = (1 << 6), }; /* @@ -176,8 +181,11 @@ struct nvme_ctrl { u64 ps_max_latency_us; bool apst_enabled; + /* PCIe only: */ u32 hmpre; u32 hmmin; + u32 hmminds; + u16 hmmaxd; /* Fabrics only */ u16 sqsize; @@ -320,7 +328,6 @@ void nvme_stop_keep_alive(struct nvme_ctrl *ctrl); int nvme_reset_ctrl(struct nvme_ctrl *ctrl); #ifdef CONFIG_NVM -int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id); int nvme_nvm_register(struct nvme_ns *ns, char *disk_name, int node); void nvme_nvm_unregister(struct nvme_ns *ns); int nvme_nvm_register_sysfs(struct nvme_ns *ns); @@ -339,10 +346,6 @@ static inline int nvme_nvm_register_sysfs(struct nvme_ns *ns) return 0; } static inline void nvme_nvm_unregister_sysfs(struct nvme_ns *ns) {}; -static inline int nvme_nvm_ns_supported(struct nvme_ns *ns, struct nvme_id_ns *id) -{ - return 0; -} static inline int nvme_nvm_ioctl(struct nvme_ns *ns, unsigned int cmd, unsigned long arg) { diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 198245faba6b..4a2121335f48 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -1612,21 +1612,23 @@ static void nvme_free_host_mem(struct nvme_dev *dev) dev->host_mem_descs = NULL; } -static int nvme_alloc_host_mem(struct nvme_dev *dev, u64 min, u64 preferred) +static int __nvme_alloc_host_mem(struct nvme_dev *dev, u64 preferred, + u32 chunk_size) { struct nvme_host_mem_buf_desc *descs; - u32 chunk_size, max_entries, len; + u32 max_entries, len; dma_addr_t descs_dma; int i = 0; void **bufs; u64 size = 0, tmp; - /* start big and work our way down */ - chunk_size = min(preferred, (u64)PAGE_SIZE << MAX_ORDER); -retry: tmp = (preferred + chunk_size - 1); do_div(tmp, chunk_size); max_entries = tmp; + + if (dev->ctrl.hmmaxd && dev->ctrl.hmmaxd < max_entries) + max_entries = dev->ctrl.hmmaxd; + descs = dma_zalloc_coherent(dev->dev, max_entries * sizeof(*descs), &descs_dma, GFP_KERNEL); if (!descs) @@ -1650,15 +1652,9 @@ retry: i++; } - if (!size || (min && size < min)) { - dev_warn(dev->ctrl.device, - "failed to allocate host memory buffer.\n"); + if (!size) goto out_free_bufs; - } - dev_info(dev->ctrl.device, - "allocated %lld MiB host memory buffer.\n", - size >> ilog2(SZ_1M)); dev->nr_host_mem_descs = i; dev->host_mem_size = size; dev->host_mem_descs = descs; @@ -1679,21 +1675,35 @@ out_free_descs: dma_free_coherent(dev->dev, max_entries * sizeof(*descs), descs, descs_dma); out: - /* try a smaller chunk size if we failed early */ - if (chunk_size >= PAGE_SIZE * 2 && (i == 0 || size < min)) { - chunk_size /= 2; - goto retry; - } dev->host_mem_descs = NULL; return -ENOMEM; } -static void nvme_setup_host_mem(struct nvme_dev *dev) +static int nvme_alloc_host_mem(struct nvme_dev *dev, u64 min, u64 preferred) +{ + u32 chunk_size; + + /* start big and work our way down */ + for (chunk_size = min_t(u64, preferred, PAGE_SIZE * MAX_ORDER_NR_PAGES); + chunk_size >= max_t(u32, dev->ctrl.hmminds * 4096, PAGE_SIZE * 2); + chunk_size /= 2) { + if (!__nvme_alloc_host_mem(dev, preferred, chunk_size)) { + if (!min || dev->host_mem_size >= min) + return 0; + nvme_free_host_mem(dev); + } + } + + return -ENOMEM; +} + +static int nvme_setup_host_mem(struct nvme_dev *dev) { u64 max = (u64)max_host_mem_size_mb * SZ_1M; u64 preferred = (u64)dev->ctrl.hmpre * 4096; u64 min = (u64)dev->ctrl.hmmin * 4096; u32 enable_bits = NVME_HOST_MEM_ENABLE; + int ret = 0; preferred = min(preferred, max); if (min > max) { @@ -1701,7 +1711,7 @@ static void nvme_setup_host_mem(struct nvme_dev *dev) "min host memory (%lld MiB) above limit (%d MiB).\n", min >> ilog2(SZ_1M), max_host_mem_size_mb); nvme_free_host_mem(dev); - return; + return 0; } /* @@ -1715,12 +1725,21 @@ static void nvme_setup_host_mem(struct nvme_dev *dev) } if (!dev->host_mem_descs) { - if (nvme_alloc_host_mem(dev, min, preferred)) - return; + if (nvme_alloc_host_mem(dev, min, preferred)) { + dev_warn(dev->ctrl.device, + "failed to allocate host memory buffer.\n"); + return 0; /* controller must work without HMB */ + } + + dev_info(dev->ctrl.device, + "allocated %lld MiB host memory buffer.\n", + dev->host_mem_size >> ilog2(SZ_1M)); } - if (nvme_set_host_mem(dev, enable_bits)) + ret = nvme_set_host_mem(dev, enable_bits); + if (ret) nvme_free_host_mem(dev); + return ret; } static int nvme_setup_io_queues(struct nvme_dev *dev) @@ -2164,8 +2183,11 @@ static void nvme_reset_work(struct work_struct *work) "unable to allocate dma for dbbuf\n"); } - if (dev->ctrl.hmpre) - nvme_setup_host_mem(dev); + if (dev->ctrl.hmpre) { + result = nvme_setup_host_mem(dev); + if (result < 0) + goto out; + } result = nvme_setup_io_queues(dev); if (result) @@ -2497,6 +2519,10 @@ static const struct pci_device_id nvme_id_table[] = { .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, { PCI_DEVICE(0x144d, 0xa822), /* Samsung PM1725a */ .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, }, + { PCI_DEVICE(0x1d1d, 0x1f1f), /* LighNVM qemu device */ + .driver_data = NVME_QUIRK_LIGHTNVM, }, + { PCI_DEVICE(0x1d1d, 0x2807), /* CNEX WL */ + .driver_data = NVME_QUIRK_LIGHTNVM, }, { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) }, { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2001) }, { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2003) }, diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 72419ac2c52a..e0e58f3b1420 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -227,14 +227,14 @@ config RTC_DRV_AS3722 will be called rtc-as3722. config RTC_DRV_DS1307 - tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025, ISL12057" + tristate "Dallas/Maxim DS1307/37/38/39/40/41, ST M41T00, EPSON RX-8025, ISL12057" help If you say yes here you get support for various compatible RTC chips (often with battery backup) connected with I2C. This driver - should handle DS1307, DS1337, DS1338, DS1339, DS1340, ST M41T00, - EPSON RX-8025, Intersil ISL12057 and probably other chips. In some - cases the RTC must already have been initialized (by manufacturing or - a bootloader). + should handle DS1307, DS1337, DS1338, DS1339, DS1340, DS1341, + ST M41T00, EPSON RX-8025, Intersil ISL12057 and probably other chips. + In some cases the RTC must already have been initialized (by + manufacturing or a bootloader). The first seven registers on these chips hold an RTC, and other registers may add features such as NVRAM, a trickle charger for @@ -371,11 +371,11 @@ config RTC_DRV_MAX77686 will be called rtc-max77686. config RTC_DRV_RK808 - tristate "Rockchip RK808/RK818 RTC" + tristate "Rockchip RK805/RK808/RK818 RTC" depends on MFD_RK808 help If you say yes here you will get support for the - RTC of RK808 and RK818 PMIC. + RTC of RK805, RK808 and RK818 PMIC. This driver can also be built as a module. If so, the module will be called rk808-rtc. @@ -1765,6 +1765,14 @@ config RTC_DRV_CPCAP Say y here for CPCAP rtc found on some Motorola phones and tablets such as Droid 4. +config RTC_DRV_RTD119X + bool "Realtek RTD129x RTC" + depends on ARCH_REALTEK || COMPILE_TEST + default ARCH_REALTEK + help + If you say yes here, you get support for the RTD1295 SoC + Real Time Clock. + comment "HID Sensor RTC drivers" config RTC_DRV_HID_SENSOR_TIME @@ -1780,5 +1788,13 @@ config RTC_DRV_HID_SENSOR_TIME If this driver is compiled as a module, it will be named rtc-hid-sensor-time. +config RTC_DRV_GOLDFISH + tristate "Goldfish Real Time Clock" + depends on MIPS && (GOLDFISH || COMPILE_TEST) + help + Say yes to enable RTC driver for the Goldfish based virtual platform. + + Goldfish is a code name for the virtual platform developed by Google + for Android emulation. endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index acd366b41c85..7230014c92af 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -131,6 +131,7 @@ obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o +obj-$(CONFIG_RTC_DRV_RTD119X) += rtc-rtd119x.o obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o obj-$(CONFIG_RTC_DRV_RV8803) += rtc-rv8803.o obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o @@ -170,3 +171,4 @@ obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o obj-$(CONFIG_RTC_DRV_ZYNQMP) += rtc-zynqmp.o +obj-$(CONFIG_RTC_DRV_GOLDFISH) += rtc-goldfish.o diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 794bc4fa4937..00efe24a6063 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -24,28 +24,19 @@ static dev_t rtc_devt; static int rtc_dev_open(struct inode *inode, struct file *file) { - int err; struct rtc_device *rtc = container_of(inode->i_cdev, struct rtc_device, char_dev); - const struct rtc_class_ops *ops = rtc->ops; if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags)) return -EBUSY; file->private_data = rtc; - err = ops->open ? ops->open(rtc->dev.parent) : 0; - if (err == 0) { - spin_lock_irq(&rtc->irq_lock); - rtc->irq_data = 0; - spin_unlock_irq(&rtc->irq_lock); - - return 0; - } + spin_lock_irq(&rtc->irq_lock); + rtc->irq_data = 0; + spin_unlock_irq(&rtc->irq_lock); - /* something has gone wrong */ - clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); - return err; + return 0; } #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL @@ -438,9 +429,6 @@ static int rtc_dev_release(struct inode *inode, struct file *file) rtc_update_irq_enable(rtc, 0); rtc_irq_set_state(rtc, NULL, 0); - if (rtc->ops->release) - rtc->ops->release(rtc->dev.parent); - clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags); return 0; } diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 4b43aa62fbc7..e7d9215c9201 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -39,6 +39,7 @@ enum ds_type { ds_1338, ds_1339, ds_1340, + ds_1341, ds_1388, ds_3231, m41t0, @@ -50,7 +51,6 @@ enum ds_type { /* rs5c372 too? different address... */ }; - /* RTC registers don't differ much, except for the century flag */ #define DS1307_REG_SECS 0x00 /* 00-59 */ # define DS1307_BIT_CH 0x80 @@ -113,11 +113,7 @@ enum ds_type { # define RX8025_BIT_VDET 0x40 # define RX8025_BIT_XST 0x20 - struct ds1307 { - u8 offset; /* register's offset */ - u8 regs[11]; - u16 nvram_offset; struct nvmem_config nvmem_cfg; enum ds_type type; unsigned long flags; @@ -126,7 +122,6 @@ struct ds1307 { struct device *dev; struct regmap *regmap; const char *name; - int irq; struct rtc_device *rtc; #ifdef CONFIG_COMMON_CLK struct clk_hw clks[2]; @@ -137,18 +132,47 @@ struct chip_desc { unsigned alarm:1; u16 nvram_offset; u16 nvram_size; + u8 offset; /* register's offset */ u8 century_reg; u8 century_enable_bit; u8 century_bit; + u8 bbsqi_bit; + irq_handler_t irq_handler; + const struct rtc_class_ops *rtc_ops; u16 trickle_charger_reg; - u8 trickle_charger_setup; - u8 (*do_trickle_setup)(struct ds1307 *, uint32_t, + u8 (*do_trickle_setup)(struct ds1307 *, u32, bool); }; -static u8 do_trickle_setup_ds1339(struct ds1307 *, uint32_t ohms, bool diode); +static int ds1307_get_time(struct device *dev, struct rtc_time *t); +static int ds1307_set_time(struct device *dev, struct rtc_time *t); +static u8 do_trickle_setup_ds1339(struct ds1307 *, u32 ohms, bool diode); +static irqreturn_t rx8130_irq(int irq, void *dev_id); +static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t); +static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t); +static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled); +static irqreturn_t mcp794xx_irq(int irq, void *dev_id); +static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t); +static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t); +static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled); -static struct chip_desc chips[last_ds_type] = { +static const struct rtc_class_ops rx8130_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = rx8130_read_alarm, + .set_alarm = rx8130_set_alarm, + .alarm_irq_enable = rx8130_alarm_irq_enable, +}; + +static const struct rtc_class_ops mcp794xx_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = mcp794xx_read_alarm, + .set_alarm = mcp794xx_set_alarm, + .alarm_irq_enable = mcp794xx_alarm_irq_enable, +}; + +static const struct chip_desc chips[last_ds_type] = { [ds_1307] = { .nvram_offset = 8, .nvram_size = 56, @@ -170,6 +194,7 @@ static struct chip_desc chips[last_ds_type] = { .alarm = 1, .century_reg = DS1307_REG_MONTH, .century_bit = DS1337_BIT_CENTURY, + .bbsqi_bit = DS1339_BIT_BBSQI, .trickle_charger_reg = 0x10, .do_trickle_setup = &do_trickle_setup_ds1339, }, @@ -179,25 +204,36 @@ static struct chip_desc chips[last_ds_type] = { .century_bit = DS1340_BIT_CENTURY, .trickle_charger_reg = 0x08, }, + [ds_1341] = { + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, + }, [ds_1388] = { + .offset = 1, .trickle_charger_reg = 0x0a, }, [ds_3231] = { .alarm = 1, .century_reg = DS1307_REG_MONTH, .century_bit = DS1337_BIT_CENTURY, + .bbsqi_bit = DS3231_BIT_BBSQW, }, [rx_8130] = { .alarm = 1, /* this is battery backed SRAM */ .nvram_offset = 0x20, .nvram_size = 4, /* 32bit (4 word x 8 bit) */ + .offset = 0x10, + .irq_handler = rx8130_irq, + .rtc_ops = &rx8130_rtc_ops, }, [mcp794xx] = { .alarm = 1, /* this is battery backed SRAM */ .nvram_offset = 0x20, .nvram_size = 0x40, + .irq_handler = mcp794xx_irq, + .rtc_ops = &mcp794xx_rtc_ops, }, }; @@ -209,6 +245,7 @@ static const struct i2c_device_id ds1307_id[] = { { "ds1339", ds_1339 }, { "ds1388", ds_1388 }, { "ds1340", ds_1340 }, + { "ds1341", ds_1341 }, { "ds3231", ds_3231 }, { "m41t0", m41t0 }, { "m41t00", m41t00 }, @@ -253,6 +290,10 @@ static const struct of_device_id ds1307_of_match[] = { .data = (void *)ds_1340 }, { + .compatible = "dallas,ds1341", + .data = (void *)ds_1341 + }, + { .compatible = "maxim,ds3231", .data = (void *)ds_3231 }, @@ -298,6 +339,7 @@ static const struct acpi_device_id ds1307_acpi_ids[] = { { .id = "DS1339", .driver_data = ds_1339 }, { .id = "DS1388", .driver_data = ds_1388 }, { .id = "DS1340", .driver_data = ds_1340 }, + { .id = "DS1341", .driver_data = ds_1341 }, { .id = "DS3231", .driver_data = ds_3231 }, { .id = "M41T0", .driver_data = m41t0 }, { .id = "M41T00", .driver_data = m41t00 }, @@ -352,34 +394,36 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) struct ds1307 *ds1307 = dev_get_drvdata(dev); int tmp, ret; const struct chip_desc *chip = &chips[ds1307->type]; + u8 regs[7]; /* read the RTC date and time registers all at once */ - ret = regmap_bulk_read(ds1307->regmap, ds1307->offset, ds1307->regs, 7); + ret = regmap_bulk_read(ds1307->regmap, chip->offset, regs, + sizeof(regs)); if (ret) { dev_err(dev, "%s error %d\n", "read", ret); return ret; } - dev_dbg(dev, "%s: %7ph\n", "read", ds1307->regs); + dev_dbg(dev, "%s: %7ph\n", "read", regs); /* if oscillator fail bit is set, no data can be trusted */ if (ds1307->type == m41t0 && - ds1307->regs[DS1307_REG_MIN] & M41T0_BIT_OF) { + regs[DS1307_REG_MIN] & M41T0_BIT_OF) { dev_warn_once(dev, "oscillator failed, set time!\n"); return -EINVAL; } - t->tm_sec = bcd2bin(ds1307->regs[DS1307_REG_SECS] & 0x7f); - t->tm_min = bcd2bin(ds1307->regs[DS1307_REG_MIN] & 0x7f); - tmp = ds1307->regs[DS1307_REG_HOUR] & 0x3f; + t->tm_sec = bcd2bin(regs[DS1307_REG_SECS] & 0x7f); + t->tm_min = bcd2bin(regs[DS1307_REG_MIN] & 0x7f); + tmp = regs[DS1307_REG_HOUR] & 0x3f; t->tm_hour = bcd2bin(tmp); - t->tm_wday = bcd2bin(ds1307->regs[DS1307_REG_WDAY] & 0x07) - 1; - t->tm_mday = bcd2bin(ds1307->regs[DS1307_REG_MDAY] & 0x3f); - tmp = ds1307->regs[DS1307_REG_MONTH] & 0x1f; + t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1; + t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f); + tmp = regs[DS1307_REG_MONTH] & 0x1f; t->tm_mon = bcd2bin(tmp) - 1; - t->tm_year = bcd2bin(ds1307->regs[DS1307_REG_YEAR]) + 100; + t->tm_year = bcd2bin(regs[DS1307_REG_YEAR]) + 100; - if (ds1307->regs[chip->century_reg] & chip->century_bit && + if (regs[chip->century_reg] & chip->century_bit && IS_ENABLED(CONFIG_RTC_DRV_DS1307_CENTURY)) t->tm_year += 100; @@ -399,7 +443,7 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) const struct chip_desc *chip = &chips[ds1307->type]; int result; int tmp; - u8 *buf = ds1307->regs; + u8 regs[7]; dev_dbg(dev, "%s secs=%d, mins=%d, " "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", @@ -418,35 +462,36 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) return -EINVAL; #endif - buf[DS1307_REG_SECS] = bin2bcd(t->tm_sec); - buf[DS1307_REG_MIN] = bin2bcd(t->tm_min); - buf[DS1307_REG_HOUR] = bin2bcd(t->tm_hour); - buf[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1); - buf[DS1307_REG_MDAY] = bin2bcd(t->tm_mday); - buf[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1); + regs[DS1307_REG_SECS] = bin2bcd(t->tm_sec); + regs[DS1307_REG_MIN] = bin2bcd(t->tm_min); + regs[DS1307_REG_HOUR] = bin2bcd(t->tm_hour); + regs[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1); + regs[DS1307_REG_MDAY] = bin2bcd(t->tm_mday); + regs[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1); /* assume 20YY not 19YY */ tmp = t->tm_year - 100; - buf[DS1307_REG_YEAR] = bin2bcd(tmp); + regs[DS1307_REG_YEAR] = bin2bcd(tmp); if (chip->century_enable_bit) - buf[chip->century_reg] |= chip->century_enable_bit; + regs[chip->century_reg] |= chip->century_enable_bit; if (t->tm_year > 199 && chip->century_bit) - buf[chip->century_reg] |= chip->century_bit; + regs[chip->century_reg] |= chip->century_bit; if (ds1307->type == mcp794xx) { /* * these bits were cleared when preparing the date/time * values and need to be set again before writing the - * buffer out to the device. + * regsfer out to the device. */ - buf[DS1307_REG_SECS] |= MCP794XX_BIT_ST; - buf[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN; + regs[DS1307_REG_SECS] |= MCP794XX_BIT_ST; + regs[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN; } - dev_dbg(dev, "%s: %7ph\n", "write", buf); + dev_dbg(dev, "%s: %7ph\n", "write", regs); - result = regmap_bulk_write(ds1307->regmap, ds1307->offset, buf, 7); + result = regmap_bulk_write(ds1307->regmap, chip->offset, regs, + sizeof(regs)); if (result) { dev_err(dev, "%s error %d\n", "write", result); return result; @@ -458,33 +503,34 @@ static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); int ret; + u8 regs[9]; if (!test_bit(HAS_ALARM, &ds1307->flags)) return -EINVAL; /* read all ALARM1, ALARM2, and status registers at once */ ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, - ds1307->regs, 9); + regs, sizeof(regs)); if (ret) { dev_err(dev, "%s error %d\n", "alarm read", ret); return ret; } dev_dbg(dev, "%s: %4ph, %3ph, %2ph\n", "alarm read", - &ds1307->regs[0], &ds1307->regs[4], &ds1307->regs[7]); + ®s[0], ®s[4], ®s[7]); /* * report alarm time (ALARM1); assume 24 hour and day-of-month modes, * and that all four fields are checked matches */ - t->time.tm_sec = bcd2bin(ds1307->regs[0] & 0x7f); - t->time.tm_min = bcd2bin(ds1307->regs[1] & 0x7f); - t->time.tm_hour = bcd2bin(ds1307->regs[2] & 0x3f); - t->time.tm_mday = bcd2bin(ds1307->regs[3] & 0x3f); + t->time.tm_sec = bcd2bin(regs[0] & 0x7f); + t->time.tm_min = bcd2bin(regs[1] & 0x7f); + t->time.tm_hour = bcd2bin(regs[2] & 0x3f); + t->time.tm_mday = bcd2bin(regs[3] & 0x3f); /* ... and status */ - t->enabled = !!(ds1307->regs[7] & DS1337_BIT_A1IE); - t->pending = !!(ds1307->regs[8] & DS1337_BIT_A1I); + t->enabled = !!(regs[7] & DS1337_BIT_A1IE); + t->pending = !!(regs[8] & DS1337_BIT_A1I); dev_dbg(dev, "%s secs=%d, mins=%d, " "hours=%d, mday=%d, enabled=%d, pending=%d\n", @@ -498,7 +544,7 @@ static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t) static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); - unsigned char *buf = ds1307->regs; + unsigned char regs[9]; u8 control, status; int ret; @@ -512,33 +558,35 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) t->enabled, t->pending); /* read current status of both alarms and the chip */ - ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, buf, 9); + ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs, + sizeof(regs)); if (ret) { dev_err(dev, "%s error %d\n", "alarm write", ret); return ret; } - control = ds1307->regs[7]; - status = ds1307->regs[8]; + control = regs[7]; + status = regs[8]; dev_dbg(dev, "%s: %4ph, %3ph, %02x %02x\n", "alarm set (old status)", - &ds1307->regs[0], &ds1307->regs[4], control, status); + ®s[0], ®s[4], control, status); /* set ALARM1, using 24 hour and day-of-month modes */ - buf[0] = bin2bcd(t->time.tm_sec); - buf[1] = bin2bcd(t->time.tm_min); - buf[2] = bin2bcd(t->time.tm_hour); - buf[3] = bin2bcd(t->time.tm_mday); + regs[0] = bin2bcd(t->time.tm_sec); + regs[1] = bin2bcd(t->time.tm_min); + regs[2] = bin2bcd(t->time.tm_hour); + regs[3] = bin2bcd(t->time.tm_mday); /* set ALARM2 to non-garbage */ - buf[4] = 0; - buf[5] = 0; - buf[6] = 0; + regs[4] = 0; + regs[5] = 0; + regs[6] = 0; /* disable alarms */ - buf[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE); - buf[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I); + regs[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE); + regs[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I); - ret = regmap_bulk_write(ds1307->regmap, DS1339_REG_ALARM1_SECS, buf, 9); + ret = regmap_bulk_write(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs, + sizeof(regs)); if (ret) { dev_err(dev, "can't set alarm time\n"); return ret; @@ -547,8 +595,8 @@ static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t) /* optionally enable ALARM1 */ if (t->enabled) { dev_dbg(dev, "alarm IRQ armed\n"); - buf[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */ - regmap_write(ds1307->regmap, DS1337_REG_CONTROL, buf[7]); + regs[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */ + regmap_write(ds1307->regmap, DS1337_REG_CONTROL, regs[7]); } return 0; @@ -584,11 +632,11 @@ static const struct rtc_class_ops ds13xx_rtc_ops = { #define RX8130_REG_ALARM_HOUR 0x08 #define RX8130_REG_ALARM_WEEK_OR_DAY 0x09 #define RX8130_REG_EXTENSION 0x0c -#define RX8130_REG_EXTENSION_WADA (1 << 3) +#define RX8130_REG_EXTENSION_WADA BIT(3) #define RX8130_REG_FLAG 0x0d -#define RX8130_REG_FLAG_AF (1 << 3) +#define RX8130_REG_FLAG_AF BIT(3) #define RX8130_REG_CONTROL0 0x0e -#define RX8130_REG_CONTROL0_AIE (1 << 3) +#define RX8130_REG_CONTROL0_AIE BIT(3) static irqreturn_t rx8130_irq(int irq, void *dev_id) { @@ -600,7 +648,8 @@ static irqreturn_t rx8130_irq(int irq, void *dev_id) mutex_lock(lock); /* Read control registers. */ - ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); if (ret < 0) goto out; if (!(ctl[1] & RX8130_REG_FLAG_AF)) @@ -608,7 +657,8 @@ static irqreturn_t rx8130_irq(int irq, void *dev_id) ctl[1] &= ~RX8130_REG_FLAG_AF; ctl[2] &= ~RX8130_REG_CONTROL0_AIE; - ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); if (ret < 0) goto out; @@ -630,12 +680,14 @@ static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t) return -EINVAL; /* Read alarm registers. */ - ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, 3); + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, + sizeof(ald)); if (ret < 0) return ret; /* Read control registers. */ - ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); if (ret < 0) return ret; @@ -676,7 +728,8 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) t->enabled, t->pending); /* Read control registers. */ - ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); if (ret < 0) return ret; @@ -684,7 +737,8 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) ctl[1] |= RX8130_REG_FLAG_AF; ctl[2] &= ~RX8130_REG_CONTROL0_AIE; - ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); if (ret < 0) return ret; @@ -693,7 +747,8 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) ald[1] = bin2bcd(t->time.tm_hour); ald[2] = bin2bcd(t->time.tm_mday); - ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, 3); + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, + sizeof(ald)); if (ret < 0) return ret; @@ -702,7 +757,8 @@ static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) ctl[2] |= RX8130_REG_CONTROL0_AIE; - return regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, 3); + return regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); } static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) @@ -725,14 +781,6 @@ static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, reg); } -static const struct rtc_class_ops rx8130_rtc_ops = { - .read_time = ds1307_get_time, - .set_time = ds1307_set_time, - .read_alarm = rx8130_read_alarm, - .set_alarm = rx8130_set_alarm, - .alarm_irq_enable = rx8130_alarm_irq_enable, -}; - /*----------------------------------------------------------------------*/ /* @@ -748,11 +796,11 @@ static const struct rtc_class_ops rx8130_rtc_ops = { #define MCP794XX_REG_ALARM0_CTRL 0x0d #define MCP794XX_REG_ALARM1_BASE 0x11 #define MCP794XX_REG_ALARM1_CTRL 0x14 -# define MCP794XX_BIT_ALMX_IF (1 << 3) -# define MCP794XX_BIT_ALMX_C0 (1 << 4) -# define MCP794XX_BIT_ALMX_C1 (1 << 5) -# define MCP794XX_BIT_ALMX_C2 (1 << 6) -# define MCP794XX_BIT_ALMX_POL (1 << 7) +# define MCP794XX_BIT_ALMX_IF BIT(3) +# define MCP794XX_BIT_ALMX_C0 BIT(4) +# define MCP794XX_BIT_ALMX_C1 BIT(5) +# define MCP794XX_BIT_ALMX_C2 BIT(6) +# define MCP794XX_BIT_ALMX_POL BIT(7) # define MCP794XX_MSK_ALMX_MATCH (MCP794XX_BIT_ALMX_C0 | \ MCP794XX_BIT_ALMX_C1 | \ MCP794XX_BIT_ALMX_C2) @@ -793,37 +841,38 @@ out: static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); - u8 *regs = ds1307->regs; + u8 regs[10]; int ret; if (!test_bit(HAS_ALARM, &ds1307->flags)) return -EINVAL; /* Read control and alarm 0 registers. */ - ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, 10); + ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); if (ret) return ret; t->enabled = !!(regs[0] & MCP794XX_BIT_ALM0_EN); /* Report alarm 0 time assuming 24-hour and day-of-month modes. */ - t->time.tm_sec = bcd2bin(ds1307->regs[3] & 0x7f); - t->time.tm_min = bcd2bin(ds1307->regs[4] & 0x7f); - t->time.tm_hour = bcd2bin(ds1307->regs[5] & 0x3f); - t->time.tm_wday = bcd2bin(ds1307->regs[6] & 0x7) - 1; - t->time.tm_mday = bcd2bin(ds1307->regs[7] & 0x3f); - t->time.tm_mon = bcd2bin(ds1307->regs[8] & 0x1f) - 1; + t->time.tm_sec = bcd2bin(regs[3] & 0x7f); + t->time.tm_min = bcd2bin(regs[4] & 0x7f); + t->time.tm_hour = bcd2bin(regs[5] & 0x3f); + t->time.tm_wday = bcd2bin(regs[6] & 0x7) - 1; + t->time.tm_mday = bcd2bin(regs[7] & 0x3f); + t->time.tm_mon = bcd2bin(regs[8] & 0x1f) - 1; t->time.tm_year = -1; t->time.tm_yday = -1; t->time.tm_isdst = -1; dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d " - "enabled=%d polarity=%d irq=%d match=%d\n", __func__, + "enabled=%d polarity=%d irq=%d match=%lu\n", __func__, t->time.tm_sec, t->time.tm_min, t->time.tm_hour, t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled, - !!(ds1307->regs[6] & MCP794XX_BIT_ALMX_POL), - !!(ds1307->regs[6] & MCP794XX_BIT_ALMX_IF), - (ds1307->regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4); + !!(regs[6] & MCP794XX_BIT_ALMX_POL), + !!(regs[6] & MCP794XX_BIT_ALMX_IF), + (regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4); return 0; } @@ -831,7 +880,7 @@ static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) { struct ds1307 *ds1307 = dev_get_drvdata(dev); - unsigned char *regs = ds1307->regs; + unsigned char regs[10]; int ret; if (!test_bit(HAS_ALARM, &ds1307->flags)) @@ -844,7 +893,8 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) t->enabled, t->pending); /* Read control and alarm 0 registers. */ - ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, 10); + ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); if (ret) return ret; @@ -863,7 +913,8 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) /* Disable interrupt. We will not enable until completely programmed */ regs[0] &= ~MCP794XX_BIT_ALM0_EN; - ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs, 10); + ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); if (ret) return ret; @@ -885,22 +936,15 @@ static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled) enabled ? MCP794XX_BIT_ALM0_EN : 0); } -static const struct rtc_class_ops mcp794xx_rtc_ops = { - .read_time = ds1307_get_time, - .set_time = ds1307_set_time, - .read_alarm = mcp794xx_read_alarm, - .set_alarm = mcp794xx_set_alarm, - .alarm_irq_enable = mcp794xx_alarm_irq_enable, -}; - /*----------------------------------------------------------------------*/ static int ds1307_nvram_read(void *priv, unsigned int offset, void *val, size_t bytes) { struct ds1307 *ds1307 = priv; + const struct chip_desc *chip = &chips[ds1307->type]; - return regmap_bulk_read(ds1307->regmap, ds1307->nvram_offset + offset, + return regmap_bulk_read(ds1307->regmap, chip->nvram_offset + offset, val, bytes); } @@ -908,15 +952,16 @@ static int ds1307_nvram_write(void *priv, unsigned int offset, void *val, size_t bytes) { struct ds1307 *ds1307 = priv; + const struct chip_desc *chip = &chips[ds1307->type]; - return regmap_bulk_write(ds1307->regmap, ds1307->nvram_offset + offset, + return regmap_bulk_write(ds1307->regmap, chip->nvram_offset + offset, val, bytes); } /*----------------------------------------------------------------------*/ static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, - uint32_t ohms, bool diode) + u32 ohms, bool diode) { u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE : DS1307_TRICKLE_CHARGER_NO_DIODE; @@ -939,23 +984,23 @@ static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, return setup; } -static void ds1307_trickle_init(struct ds1307 *ds1307, - struct chip_desc *chip) +static u8 ds1307_trickle_init(struct ds1307 *ds1307, + const struct chip_desc *chip) { - uint32_t ohms = 0; + u32 ohms; bool diode = true; if (!chip->do_trickle_setup) - goto out; + return 0; + if (device_property_read_u32(ds1307->dev, "trickle-resistor-ohms", &ohms)) - goto out; + return 0; + if (device_property_read_bool(ds1307->dev, "trickle-diode-disable")) diode = false; - chip->trickle_charger_setup = chip->do_trickle_setup(ds1307, - ohms, diode); -out: - return; + + return chip->do_trickle_setup(ds1307, ohms, diode); } /*----------------------------------------------------------------------*/ @@ -995,7 +1040,7 @@ static int ds3231_hwmon_read_temp(struct device *dev, s32 *mC) } static ssize_t ds3231_hwmon_show_temp(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { int ret; s32 temp; @@ -1006,8 +1051,8 @@ static ssize_t ds3231_hwmon_show_temp(struct device *dev, return sprintf(buf, "%d\n", temp); } -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ds3231_hwmon_show_temp, - NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_input, 0444, ds3231_hwmon_show_temp, + NULL, 0); static struct attribute *ds3231_hwmon_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, @@ -1023,7 +1068,8 @@ static void ds1307_hwmon_register(struct ds1307 *ds1307) return; dev = devm_hwmon_device_register_with_groups(ds1307->dev, ds1307->name, - ds1307, ds3231_hwmon_groups); + ds1307, + ds3231_hwmon_groups); if (IS_ERR(dev)) { dev_warn(ds1307->dev, "unable to register hwmon device %ld\n", PTR_ERR(dev)); @@ -1095,7 +1141,7 @@ static unsigned long ds3231_clk_sqw_recalc_rate(struct clk_hw *hw, } static long ds3231_clk_sqw_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) + unsigned long *prate) { int i; @@ -1108,7 +1154,7 @@ static long ds3231_clk_sqw_round_rate(struct clk_hw *hw, unsigned long rate, } static int ds3231_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) + unsigned long parent_rate) { struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); int control = 0; @@ -1168,7 +1214,7 @@ static const struct clk_ops ds3231_clk_sqw_ops = { }; static unsigned long ds3231_clk_32khz_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) + unsigned long parent_rate) { return 32768; } @@ -1259,7 +1305,7 @@ static int ds3231_clks_register(struct ds1307 *ds1307) /* optional override of the clockname */ of_property_read_string_index(node, "clock-output-names", i, - &init.name); + &init.name); ds1307->clks[i].init = &init; onecell->clks[i] = devm_clk_register(ds1307->dev, @@ -1309,22 +1355,14 @@ static int ds1307_probe(struct i2c_client *client, struct ds1307 *ds1307; int err = -ENODEV; int tmp, wday; - struct chip_desc *chip; - bool want_irq = false; + const struct chip_desc *chip; + bool want_irq; bool ds1307_can_wakeup_device = false; - unsigned char *buf; + unsigned char regs[8]; struct ds1307_platform_data *pdata = dev_get_platdata(&client->dev); struct rtc_time tm; unsigned long timestamp; - - irq_handler_t irq_handler = ds1307_irq; - - static const int bbsqi_bitpos[] = { - [ds_1337] = 0, - [ds_1339] = DS1339_BIT_BBSQI, - [ds_3231] = DS3231_BIT_BBSQW, - }; - const struct rtc_class_ops *rtc_ops = &ds13xx_rtc_ops; + u8 trickle_charger_setup = 0; ds1307 = devm_kzalloc(&client->dev, sizeof(struct ds1307), GFP_KERNEL); if (!ds1307) @@ -1333,7 +1371,6 @@ static int ds1307_probe(struct i2c_client *client, dev_set_drvdata(&client->dev, ds1307); ds1307->dev = &client->dev; ds1307->name = client->name; - ds1307->irq = client->irq; ds1307->regmap = devm_regmap_init_i2c(client, ®map_config); if (IS_ERR(ds1307->regmap)) { @@ -1361,23 +1398,22 @@ static int ds1307_probe(struct i2c_client *client, ds1307->type = acpi_id->driver_data; } + want_irq = client->irq > 0 && chip->alarm; + if (!pdata) - ds1307_trickle_init(ds1307, chip); + trickle_charger_setup = ds1307_trickle_init(ds1307, chip); else if (pdata->trickle_charger_setup) - chip->trickle_charger_setup = pdata->trickle_charger_setup; + trickle_charger_setup = pdata->trickle_charger_setup; - if (chip->trickle_charger_setup && chip->trickle_charger_reg) { + if (trickle_charger_setup && chip->trickle_charger_reg) { + trickle_charger_setup |= DS13XX_TRICKLE_CHARGER_MAGIC; dev_dbg(ds1307->dev, "writing trickle charger info 0x%x to 0x%x\n", - DS13XX_TRICKLE_CHARGER_MAGIC | chip->trickle_charger_setup, - chip->trickle_charger_reg); + trickle_charger_setup, chip->trickle_charger_reg); regmap_write(ds1307->regmap, chip->trickle_charger_reg, - DS13XX_TRICKLE_CHARGER_MAGIC | - chip->trickle_charger_setup); + trickle_charger_setup); } - buf = ds1307->regs; - #ifdef CONFIG_OF /* * For devices with no IRQ directly connected to the SoC, the RTC chip @@ -1387,31 +1423,27 @@ static int ds1307_probe(struct i2c_client *client, * This will guarantee the 'wakealarm' sysfs entry is available on the device, * if supported by the RTC. */ - if (of_property_read_bool(client->dev.of_node, "wakeup-source")) { - ds1307_can_wakeup_device = true; - } - /* Intersil ISL12057 DT backward compatibility */ - if (of_property_read_bool(client->dev.of_node, - "isil,irq2-can-wakeup-machine")) { + if (chip->alarm && of_property_read_bool(client->dev.of_node, + "wakeup-source")) ds1307_can_wakeup_device = true; - } #endif switch (ds1307->type) { case ds_1337: case ds_1339: + case ds_1341: case ds_3231: /* get registers that the "rtc" read below won't read... */ err = regmap_bulk_read(ds1307->regmap, DS1337_REG_CONTROL, - buf, 2); + regs, 2); if (err) { dev_dbg(ds1307->dev, "read error %d\n", err); goto exit; } /* oscillator off? turn it on, so clock can tick. */ - if (ds1307->regs[0] & DS1337_BIT_nEOSC) - ds1307->regs[0] &= ~DS1337_BIT_nEOSC; + if (regs[0] & DS1337_BIT_nEOSC) + regs[0] &= ~DS1337_BIT_nEOSC; /* * Using IRQ or defined as wakeup-source? @@ -1419,114 +1451,92 @@ static int ds1307_probe(struct i2c_client *client, * For some variants, be sure alarms can trigger when we're * running on Vbackup (BBSQI/BBSQW) */ - if (chip->alarm && (ds1307->irq > 0 || - ds1307_can_wakeup_device)) { - ds1307->regs[0] |= DS1337_BIT_INTCN - | bbsqi_bitpos[ds1307->type]; - ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE); - - want_irq = true; + if (want_irq || ds1307_can_wakeup_device) { + regs[0] |= DS1337_BIT_INTCN | chip->bbsqi_bit; + regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE); } regmap_write(ds1307->regmap, DS1337_REG_CONTROL, - ds1307->regs[0]); + regs[0]); /* oscillator fault? clear flag, and warn */ - if (ds1307->regs[1] & DS1337_BIT_OSF) { + if (regs[1] & DS1337_BIT_OSF) { regmap_write(ds1307->regmap, DS1337_REG_STATUS, - ds1307->regs[1] & ~DS1337_BIT_OSF); + regs[1] & ~DS1337_BIT_OSF); dev_warn(ds1307->dev, "SET TIME!\n"); } break; case rx_8025: err = regmap_bulk_read(ds1307->regmap, - RX8025_REG_CTRL1 << 4 | 0x08, buf, 2); + RX8025_REG_CTRL1 << 4 | 0x08, regs, 2); if (err) { dev_dbg(ds1307->dev, "read error %d\n", err); goto exit; } /* oscillator off? turn it on, so clock can tick. */ - if (!(ds1307->regs[1] & RX8025_BIT_XST)) { - ds1307->regs[1] |= RX8025_BIT_XST; + if (!(regs[1] & RX8025_BIT_XST)) { + regs[1] |= RX8025_BIT_XST; regmap_write(ds1307->regmap, RX8025_REG_CTRL2 << 4 | 0x08, - ds1307->regs[1]); + regs[1]); dev_warn(ds1307->dev, "oscillator stop detected - SET TIME!\n"); } - if (ds1307->regs[1] & RX8025_BIT_PON) { - ds1307->regs[1] &= ~RX8025_BIT_PON; + if (regs[1] & RX8025_BIT_PON) { + regs[1] &= ~RX8025_BIT_PON; regmap_write(ds1307->regmap, RX8025_REG_CTRL2 << 4 | 0x08, - ds1307->regs[1]); + regs[1]); dev_warn(ds1307->dev, "power-on detected\n"); } - if (ds1307->regs[1] & RX8025_BIT_VDET) { - ds1307->regs[1] &= ~RX8025_BIT_VDET; + if (regs[1] & RX8025_BIT_VDET) { + regs[1] &= ~RX8025_BIT_VDET; regmap_write(ds1307->regmap, RX8025_REG_CTRL2 << 4 | 0x08, - ds1307->regs[1]); + regs[1]); dev_warn(ds1307->dev, "voltage drop detected\n"); } /* make sure we are running in 24hour mode */ - if (!(ds1307->regs[0] & RX8025_BIT_2412)) { + if (!(regs[0] & RX8025_BIT_2412)) { u8 hour; /* switch to 24 hour mode */ regmap_write(ds1307->regmap, RX8025_REG_CTRL1 << 4 | 0x08, - ds1307->regs[0] | RX8025_BIT_2412); + regs[0] | RX8025_BIT_2412); err = regmap_bulk_read(ds1307->regmap, RX8025_REG_CTRL1 << 4 | 0x08, - buf, 2); + regs, 2); if (err) { dev_dbg(ds1307->dev, "read error %d\n", err); goto exit; } /* correct hour */ - hour = bcd2bin(ds1307->regs[DS1307_REG_HOUR]); + hour = bcd2bin(regs[DS1307_REG_HOUR]); if (hour == 12) hour = 0; - if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM) + if (regs[DS1307_REG_HOUR] & DS1307_BIT_PM) hour += 12; regmap_write(ds1307->regmap, DS1307_REG_HOUR << 4 | 0x08, hour); } break; - case rx_8130: - ds1307->offset = 0x10; /* Seconds starts at 0x10 */ - rtc_ops = &rx8130_rtc_ops; - if (chip->alarm && ds1307->irq > 0) { - irq_handler = rx8130_irq; - want_irq = true; - } - break; - case ds_1388: - ds1307->offset = 1; /* Seconds starts at 1 */ - break; - case mcp794xx: - rtc_ops = &mcp794xx_rtc_ops; - if (chip->alarm && (ds1307->irq > 0 || - ds1307_can_wakeup_device)) { - irq_handler = mcp794xx_irq; - want_irq = true; - } - break; default: break; } read_rtc: /* read RTC registers */ - err = regmap_bulk_read(ds1307->regmap, ds1307->offset, buf, 8); + err = regmap_bulk_read(ds1307->regmap, chip->offset, regs, + sizeof(regs)); if (err) { dev_dbg(ds1307->dev, "read error %d\n", err); goto exit; @@ -1537,7 +1547,7 @@ read_rtc: * specify the extra bits as must-be-zero, but there are * still a few values that are clearly out-of-range. */ - tmp = ds1307->regs[DS1307_REG_SECS]; + tmp = regs[DS1307_REG_SECS]; switch (ds1307->type) { case ds_1307: case m41t0: @@ -1556,10 +1566,10 @@ read_rtc: regmap_write(ds1307->regmap, DS1307_REG_SECS, 0); /* oscillator fault? clear flag, and warn */ - if (ds1307->regs[DS1307_REG_CONTROL] & DS1338_BIT_OSF) { + if (regs[DS1307_REG_CONTROL] & DS1338_BIT_OSF) { regmap_write(ds1307->regmap, DS1307_REG_CONTROL, - ds1307->regs[DS1307_REG_CONTROL] & - ~DS1338_BIT_OSF); + regs[DS1307_REG_CONTROL] & + ~DS1338_BIT_OSF); dev_warn(ds1307->dev, "SET TIME!\n"); goto read_rtc; } @@ -1583,9 +1593,9 @@ read_rtc: break; case mcp794xx: /* make sure that the backup battery is enabled */ - if (!(ds1307->regs[DS1307_REG_WDAY] & MCP794XX_BIT_VBATEN)) { + if (!(regs[DS1307_REG_WDAY] & MCP794XX_BIT_VBATEN)) { regmap_write(ds1307->regmap, DS1307_REG_WDAY, - ds1307->regs[DS1307_REG_WDAY] | + regs[DS1307_REG_WDAY] | MCP794XX_BIT_VBATEN); } @@ -1602,7 +1612,7 @@ read_rtc: break; } - tmp = ds1307->regs[DS1307_REG_HOUR]; + tmp = regs[DS1307_REG_HOUR]; switch (ds1307->type) { case ds_1340: case m41t0: @@ -1625,9 +1635,9 @@ read_rtc: tmp = bcd2bin(tmp & 0x1f); if (tmp == 12) tmp = 0; - if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM) + if (regs[DS1307_REG_HOUR] & DS1307_BIT_PM) tmp += 12; - regmap_write(ds1307->regmap, ds1307->offset + DS1307_REG_HOUR, + regmap_write(ds1307->regmap, chip->offset + DS1307_REG_HOUR, bin2bcd(tmp)); } @@ -1650,19 +1660,16 @@ read_rtc: MCP794XX_REG_WEEKDAY_WDAY_MASK, tm.tm_wday + 1); - if (want_irq) { + if (want_irq || ds1307_can_wakeup_device) { device_set_wakeup_capable(ds1307->dev, true); set_bit(HAS_ALARM, &ds1307->flags); } ds1307->rtc = devm_rtc_allocate_device(ds1307->dev); - if (IS_ERR(ds1307->rtc)) { + if (IS_ERR(ds1307->rtc)) return PTR_ERR(ds1307->rtc); - } - if (ds1307_can_wakeup_device && ds1307->irq <= 0) { - /* Disable request for an IRQ */ - want_irq = false; + if (ds1307_can_wakeup_device && !want_irq) { dev_info(ds1307->dev, "'wakeup-source' is set, request for an IRQ is disabled!\n"); /* We cannot support UIE mode if we do not have an IRQ line */ @@ -1670,8 +1677,8 @@ read_rtc: } if (want_irq) { - err = devm_request_threaded_irq(ds1307->dev, - ds1307->irq, NULL, irq_handler, + err = devm_request_threaded_irq(ds1307->dev, client->irq, NULL, + chip->irq_handler ?: ds1307_irq, IRQF_SHARED | IRQF_ONESHOT, ds1307->name, ds1307); if (err) { @@ -1679,8 +1686,9 @@ read_rtc: device_set_wakeup_capable(ds1307->dev, false); clear_bit(HAS_ALARM, &ds1307->flags); dev_err(ds1307->dev, "unable to request IRQ!\n"); - } else + } else { dev_dbg(ds1307->dev, "got IRQ %d\n", client->irq); + } } if (chip->nvram_size) { @@ -1691,13 +1699,12 @@ read_rtc: ds1307->nvmem_cfg.reg_read = ds1307_nvram_read; ds1307->nvmem_cfg.reg_write = ds1307_nvram_write; ds1307->nvmem_cfg.priv = ds1307; - ds1307->nvram_offset = chip->nvram_offset; ds1307->rtc->nvmem_config = &ds1307->nvmem_cfg; ds1307->rtc->nvram_old_abi = true; } - ds1307->rtc->ops = rtc_ops; + ds1307->rtc->ops = chip->rtc_ops ?: &ds13xx_rtc_ops; err = rtc_register_device(ds1307->rtc); if (err) return err; diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 7bf46bfe11a4..9caaccccaa57 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -190,7 +190,7 @@ static int ds1672_probe(struct i2c_client *client, return 0; } -static struct i2c_device_id ds1672_id[] = { +static const struct i2c_device_id ds1672_id[] = { { "ds1672", 0 }, { } }; diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c index 4f4930a2004c..b0ef8cfe742d 100644 --- a/drivers/rtc/rtc-em3027.c +++ b/drivers/rtc/rtc-em3027.c @@ -132,7 +132,7 @@ static int em3027_probe(struct i2c_client *client, return 0; } -static struct i2c_device_id em3027_id[] = { +static const struct i2c_device_id em3027_id[] = { { "em3027", 0 }, { } }; diff --git a/drivers/rtc/rtc-goldfish.c b/drivers/rtc/rtc-goldfish.c new file mode 100644 index 000000000000..d67769265185 --- /dev/null +++ b/drivers/rtc/rtc-goldfish.c @@ -0,0 +1,237 @@ +/* drivers/rtc/rtc-goldfish.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2017 Imagination Technologies Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/io.h> + +#define TIMER_TIME_LOW 0x00 /* get low bits of current time */ + /* and update TIMER_TIME_HIGH */ +#define TIMER_TIME_HIGH 0x04 /* get high bits of time at last */ + /* TIMER_TIME_LOW read */ +#define TIMER_ALARM_LOW 0x08 /* set low bits of alarm and */ + /* activate it */ +#define TIMER_ALARM_HIGH 0x0c /* set high bits of next alarm */ +#define TIMER_IRQ_ENABLED 0x10 +#define TIMER_CLEAR_ALARM 0x14 +#define TIMER_ALARM_STATUS 0x18 +#define TIMER_CLEAR_INTERRUPT 0x1c + +struct goldfish_rtc { + void __iomem *base; + int irq; + struct rtc_device *rtc; +}; + +static int goldfish_rtc_read_alarm(struct device *dev, + struct rtc_wkalrm *alrm) +{ + u64 rtc_alarm; + u64 rtc_alarm_low; + u64 rtc_alarm_high; + void __iomem *base; + struct goldfish_rtc *rtcdrv; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + rtc_alarm_low = readl(base + TIMER_ALARM_LOW); + rtc_alarm_high = readl(base + TIMER_ALARM_HIGH); + rtc_alarm = (rtc_alarm_high << 32) | rtc_alarm_low; + + do_div(rtc_alarm, NSEC_PER_SEC); + memset(alrm, 0, sizeof(struct rtc_wkalrm)); + + rtc_time_to_tm(rtc_alarm, &alrm->time); + + if (readl(base + TIMER_ALARM_STATUS)) + alrm->enabled = 1; + else + alrm->enabled = 0; + + return 0; +} + +static int goldfish_rtc_set_alarm(struct device *dev, + struct rtc_wkalrm *alrm) +{ + struct goldfish_rtc *rtcdrv; + unsigned long rtc_alarm; + u64 rtc_alarm64; + u64 rtc_status_reg; + void __iomem *base; + int ret = 0; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + if (alrm->enabled) { + ret = rtc_tm_to_time(&alrm->time, &rtc_alarm); + if (ret != 0) + return ret; + + rtc_alarm64 = rtc_alarm * NSEC_PER_SEC; + writel((rtc_alarm64 >> 32), base + TIMER_ALARM_HIGH); + writel(rtc_alarm64, base + TIMER_ALARM_LOW); + } else { + /* + * if this function was called with enabled=0 + * then it could mean that the application is + * trying to cancel an ongoing alarm + */ + rtc_status_reg = readl(base + TIMER_ALARM_STATUS); + if (rtc_status_reg) + writel(1, base + TIMER_CLEAR_ALARM); + } + + return ret; +} + +static int goldfish_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + void __iomem *base; + struct goldfish_rtc *rtcdrv; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + if (enabled) + writel(1, base + TIMER_IRQ_ENABLED); + else + writel(0, base + TIMER_IRQ_ENABLED); + + return 0; +} + +static irqreturn_t goldfish_rtc_interrupt(int irq, void *dev_id) +{ + struct goldfish_rtc *rtcdrv = dev_id; + void __iomem *base = rtcdrv->base; + + writel(1, base + TIMER_CLEAR_INTERRUPT); + + rtc_update_irq(rtcdrv->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static int goldfish_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct goldfish_rtc *rtcdrv; + void __iomem *base; + u64 time_high; + u64 time_low; + u64 time; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + time_low = readl(base + TIMER_TIME_LOW); + time_high = readl(base + TIMER_TIME_HIGH); + time = (time_high << 32) | time_low; + + do_div(time, NSEC_PER_SEC); + + rtc_time_to_tm(time, tm); + + return 0; +} + +static int goldfish_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct goldfish_rtc *rtcdrv; + void __iomem *base; + unsigned long now; + u64 now64; + int ret; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + ret = rtc_tm_to_time(tm, &now); + if (ret == 0) { + now64 = now * NSEC_PER_SEC; + writel((now64 >> 32), base + TIMER_TIME_HIGH); + writel(now64, base + TIMER_TIME_LOW); + } + + return ret; +} + +static const struct rtc_class_ops goldfish_rtc_ops = { + .read_time = goldfish_rtc_read_time, + .set_time = goldfish_rtc_set_time, + .read_alarm = goldfish_rtc_read_alarm, + .set_alarm = goldfish_rtc_set_alarm, + .alarm_irq_enable = goldfish_rtc_alarm_irq_enable +}; + +static int goldfish_rtc_probe(struct platform_device *pdev) +{ + struct goldfish_rtc *rtcdrv; + struct resource *r; + int err; + + rtcdrv = devm_kzalloc(&pdev->dev, sizeof(*rtcdrv), GFP_KERNEL); + if (!rtcdrv) + return -ENOMEM; + + platform_set_drvdata(pdev, rtcdrv); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) + return -ENODEV; + + rtcdrv->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(rtcdrv->base)) + return -ENODEV; + + rtcdrv->irq = platform_get_irq(pdev, 0); + if (rtcdrv->irq < 0) + return -ENODEV; + + rtcdrv->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &goldfish_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtcdrv->rtc)) + return PTR_ERR(rtcdrv->rtc); + + err = devm_request_irq(&pdev->dev, rtcdrv->irq, + goldfish_rtc_interrupt, + 0, pdev->name, rtcdrv); + if (err) + return err; + + return 0; +} + +static const struct of_device_id goldfish_rtc_of_match[] = { + { .compatible = "google,goldfish-rtc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_rtc_of_match); + +static struct platform_driver goldfish_rtc = { + .probe = goldfish_rtc_probe, + .driver = { + .name = "goldfish_rtc", + .of_match_table = goldfish_rtc_of_match, + } +}; + +module_platform_driver(goldfish_rtc); diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 8940e9e43ea0..f4c070ea8384 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -440,28 +440,6 @@ static int m41t80_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(m41t80_pm, m41t80_suspend, m41t80_resume); -static ssize_t flags_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct i2c_client *client = to_i2c_client(dev); - int val; - - val = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); - if (val < 0) - return val; - return sprintf(buf, "%#x\n", val); -} -static DEVICE_ATTR_RO(flags); - -static struct attribute *attrs[] = { - &dev_attr_flags.attr, - NULL, -}; - -static struct attribute_group attr_group = { - .attrs = attrs, -}; - #ifdef CONFIG_COMMON_CLK #define sqw_to_m41t80_data(_hw) container_of(_hw, struct m41t80_data, sqw) @@ -912,13 +890,6 @@ static struct notifier_block wdt_notifier = { ***************************************************************************** */ -static void m41t80_remove_sysfs_group(void *_dev) -{ - struct device *dev = _dev; - - sysfs_remove_group(&dev->kobj, &attr_group); -} - static int m41t80_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -927,6 +898,7 @@ static int m41t80_probe(struct i2c_client *client, struct rtc_device *rtc = NULL; struct rtc_time tm; struct m41t80_data *m41t80_data = NULL; + bool wakeup_source = false; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -947,6 +919,10 @@ static int m41t80_probe(struct i2c_client *client, m41t80_data->features = id->driver_data; i2c_set_clientdata(client, m41t80_data); +#ifdef CONFIG_OF + wakeup_source = of_property_read_bool(client->dev.of_node, + "wakeup-source"); +#endif if (client->irq > 0) { rc = devm_request_threaded_irq(&client->dev, client->irq, NULL, m41t80_handle_irq, @@ -955,14 +931,16 @@ static int m41t80_probe(struct i2c_client *client, if (rc) { dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); client->irq = 0; - } else { - m41t80_rtc_ops.read_alarm = m41t80_read_alarm; - m41t80_rtc_ops.set_alarm = m41t80_set_alarm; - m41t80_rtc_ops.alarm_irq_enable = m41t80_alarm_irq_enable; - /* Enable the wakealarm */ - device_init_wakeup(&client->dev, true); + wakeup_source = false; } } + if (client->irq > 0 || wakeup_source) { + m41t80_rtc_ops.read_alarm = m41t80_read_alarm; + m41t80_rtc_ops.set_alarm = m41t80_set_alarm; + m41t80_rtc_ops.alarm_irq_enable = m41t80_alarm_irq_enable; + /* Enable the wakealarm */ + device_init_wakeup(&client->dev, true); + } rtc = devm_rtc_device_register(&client->dev, client->name, &m41t80_rtc_ops, THIS_MODULE); @@ -970,6 +948,10 @@ static int m41t80_probe(struct i2c_client *client, return PTR_ERR(rtc); m41t80_data->rtc = rtc; + if (client->irq <= 0) { + /* We cannot support UIE mode if we do not have an IRQ line */ + rtc->uie_unsupported = 1; + } /* Make sure HT (Halt Update) bit is cleared */ rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR); @@ -1004,21 +986,6 @@ static int m41t80_probe(struct i2c_client *client, return rc; } - /* Export sysfs entries */ - rc = sysfs_create_group(&(&client->dev)->kobj, &attr_group); - if (rc) { - dev_err(&client->dev, "Failed to create sysfs group: %d\n", rc); - return rc; - } - - rc = devm_add_action_or_reset(&client->dev, m41t80_remove_sysfs_group, - &client->dev); - if (rc) { - dev_err(&client->dev, - "Failed to add sysfs cleanup action: %d\n", rc); - return rc; - } - #ifdef CONFIG_RTC_DRV_M41T80_WDT if (m41t80_data->features & M41T80_FEATURE_HT) { save_client = client; diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c index 48b6b411f8b2..cbdc86a560ba 100644 --- a/drivers/rtc/rtc-max6900.c +++ b/drivers/rtc/rtc-max6900.c @@ -226,7 +226,7 @@ max6900_probe(struct i2c_client *client, const struct i2c_device_id *id) return 0; } -static struct i2c_device_id max6900_id[] = { +static const struct i2c_device_id max6900_id[] = { { "max6900", 0 }, { } }; diff --git a/drivers/rtc/rtc-max8925.c b/drivers/rtc/rtc-max8925.c index 16d129a0bb3b..67d6fc2d23e6 100644 --- a/drivers/rtc/rtc-max8925.c +++ b/drivers/rtc/rtc-max8925.c @@ -234,8 +234,6 @@ static int max8925_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x77); else ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x0); - if (ret < 0) - goto out; out: return ret; } diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index 401f46d8f21b..bce427d202ee 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c @@ -238,26 +238,6 @@ static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -/* - * Clear all interrupts and release the IRQ - */ -static void mxc_rtc_release(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct rtc_plat_data *pdata = platform_get_drvdata(pdev); - void __iomem *ioaddr = pdata->ioaddr; - - spin_lock_irq(&pdata->rtc->irq_lock); - - /* Disable all rtc interrupts */ - writew(0, ioaddr + RTC_RTCIENR); - - /* Clear all interrupt status */ - writew(0xffffffff, ioaddr + RTC_RTCISR); - - spin_unlock_irq(&pdata->rtc->irq_lock); -} - static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { mxc_rtc_irq_enable(dev, RTC_ALM_BIT, enabled); @@ -343,7 +323,6 @@ static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) /* RTC layer */ static const struct rtc_class_ops mxc_rtc_ops = { - .release = mxc_rtc_release, .read_time = mxc_rtc_read_time, .set_mmss64 = mxc_rtc_set_mmss, .read_alarm = mxc_rtc_read_alarm, diff --git a/drivers/rtc/rtc-puv3.c b/drivers/rtc/rtc-puv3.c index c0a6e638c672..9e83be32ff43 100644 --- a/drivers/rtc/rtc-puv3.c +++ b/drivers/rtc/rtc-puv3.c @@ -157,49 +157,7 @@ static int puv3_rtc_proc(struct device *dev, struct seq_file *seq) return 0; } -static int puv3_rtc_open(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc_dev = platform_get_drvdata(pdev); - int ret; - - ret = request_irq(puv3_rtc_alarmno, puv3_rtc_alarmirq, - 0, "pkunity-rtc alarm", rtc_dev); - - if (ret) { - dev_err(dev, "IRQ%d error %d\n", puv3_rtc_alarmno, ret); - return ret; - } - - ret = request_irq(puv3_rtc_tickno, puv3_rtc_tickirq, - 0, "pkunity-rtc tick", rtc_dev); - - if (ret) { - dev_err(dev, "IRQ%d error %d\n", puv3_rtc_tickno, ret); - goto tick_err; - } - - return ret; - - tick_err: - free_irq(puv3_rtc_alarmno, rtc_dev); - return ret; -} - -static void puv3_rtc_release(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc_dev = platform_get_drvdata(pdev); - - /* do not clear AIE here, it may be needed for wake */ - puv3_rtc_setpie(dev, 0); - free_irq(puv3_rtc_alarmno, rtc_dev); - free_irq(puv3_rtc_tickno, rtc_dev); -} - static const struct rtc_class_ops puv3_rtcops = { - .open = puv3_rtc_open, - .release = puv3_rtc_release, .read_time = puv3_rtc_gettime, .set_time = puv3_rtc_settime, .read_alarm = puv3_rtc_getalarm, @@ -222,10 +180,6 @@ static void puv3_rtc_enable(struct device *dev, int en) static int puv3_rtc_remove(struct platform_device *dev) { - struct rtc_device *rtc = platform_get_drvdata(dev); - - rtc_device_unregister(rtc); - puv3_rtc_setpie(&dev->dev, 0); puv3_rtc_setaie(&dev->dev, 0); @@ -259,6 +213,24 @@ static int puv3_rtc_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "PKUnity_rtc: tick irq %d, alarm irq %d\n", puv3_rtc_tickno, puv3_rtc_alarmno); + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + ret = devm_request_irq(&pdev->dev, puv3_rtc_alarmno, puv3_rtc_alarmirq, + 0, "pkunity-rtc alarm", rtc); + if (ret) { + dev_err(&pdev->dev, "IRQ%d error %d\n", puv3_rtc_alarmno, ret); + return ret; + } + + ret = devm_request_irq(&pdev->dev, puv3_rtc_tickno, puv3_rtc_tickirq, + 0, "pkunity-rtc tick", rtc); + if (ret) { + dev_err(&pdev->dev, "IRQ%d error %d\n", puv3_rtc_tickno, ret); + return ret; + } + /* get the memory region */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { @@ -278,12 +250,10 @@ static int puv3_rtc_probe(struct platform_device *pdev) puv3_rtc_enable(&pdev->dev, 1); /* register RTC and exit */ - rtc = rtc_device_register("pkunity", &pdev->dev, &puv3_rtcops, - THIS_MODULE); - - if (IS_ERR(rtc)) { + rtc->ops = &puv3_rtcops; + ret = rtc_register_device(rtc); + if (ret) { dev_err(&pdev->dev, "cannot attach rtc\n"); - ret = PTR_ERR(rtc); goto err_nortc; } diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index fe4985b54608..47304f5664d8 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -348,7 +348,7 @@ static int __init pxa_rtc_probe(struct platform_device *pdev) dev_err(dev, "No alarm IRQ resource defined\n"); return -ENXIO; } - pxa_rtc_open(dev); + pxa_rtc->base = devm_ioremap(dev, pxa_rtc->ress->start, resource_size(pxa_rtc->ress)); if (!pxa_rtc->base) { @@ -356,6 +356,8 @@ static int __init pxa_rtc_probe(struct platform_device *pdev) return -ENOMEM; } + pxa_rtc_open(dev); + sa1100_rtc->rcnr = pxa_rtc->base + 0x0; sa1100_rtc->rtsr = pxa_rtc->base + 0x8; sa1100_rtc->rtar = pxa_rtc->base + 0x4; diff --git a/drivers/rtc/rtc-rtd119x.c b/drivers/rtc/rtc-rtd119x.c new file mode 100644 index 000000000000..b233559d950b --- /dev/null +++ b/drivers/rtc/rtc-rtd119x.c @@ -0,0 +1,242 @@ +/* + * Realtek RTD129x RTC + * + * Copyright (c) 2017 Andreas Färber + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/spinlock.h> + +#define RTD_RTCSEC 0x00 +#define RTD_RTCMIN 0x04 +#define RTD_RTCHR 0x08 +#define RTD_RTCDATE1 0x0c +#define RTD_RTCDATE2 0x10 +#define RTD_RTCACR 0x28 +#define RTD_RTCEN 0x2c +#define RTD_RTCCR 0x30 + +#define RTD_RTCSEC_RTCSEC_MASK 0x7f + +#define RTD_RTCMIN_RTCMIN_MASK 0x3f + +#define RTD_RTCHR_RTCHR_MASK 0x1f + +#define RTD_RTCDATE1_RTCDATE1_MASK 0xff + +#define RTD_RTCDATE2_RTCDATE2_MASK 0x7f + +#define RTD_RTCACR_RTCPWR BIT(7) + +#define RTD_RTCEN_RTCEN_MASK 0xff + +#define RTD_RTCCR_RTCRST BIT(6) + +struct rtd119x_rtc { + void __iomem *base; + struct clk *clk; + struct rtc_device *rtcdev; + unsigned int base_year; +}; + +static inline int rtd119x_rtc_days_in_year(int year) +{ + return 365 + (is_leap_year(year) ? 1 : 0); +} + +static void rtd119x_rtc_reset(struct device *dev) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + u32 val; + + val = readl_relaxed(data->base + RTD_RTCCR); + val |= RTD_RTCCR_RTCRST; + writel_relaxed(val, data->base + RTD_RTCCR); + + val &= ~RTD_RTCCR_RTCRST; + writel(val, data->base + RTD_RTCCR); +} + +static void rtd119x_rtc_set_enabled(struct device *dev, bool enable) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + u32 val; + + val = readl_relaxed(data->base + RTD_RTCEN); + if (enable) { + if ((val & RTD_RTCEN_RTCEN_MASK) == 0x5a) + return; + writel_relaxed(0x5a, data->base + RTD_RTCEN); + } else { + writel_relaxed(0, data->base + RTD_RTCEN); + } +} + +static int rtd119x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + s32 day; + u32 sec; + unsigned int year; + int tries = 0; + + while (true) { + tm->tm_sec = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1; + tm->tm_min = readl_relaxed(data->base + RTD_RTCMIN) & RTD_RTCMIN_RTCMIN_MASK; + tm->tm_hour = readl_relaxed(data->base + RTD_RTCHR) & RTD_RTCHR_RTCHR_MASK; + day = readl_relaxed(data->base + RTD_RTCDATE1) & RTD_RTCDATE1_RTCDATE1_MASK; + day |= (readl_relaxed(data->base + RTD_RTCDATE2) & RTD_RTCDATE2_RTCDATE2_MASK) << 8; + sec = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1; + tries++; + + if (sec == tm->tm_sec) + break; + + if (tries >= 3) + return -EINVAL; + } + if (tries > 1) + dev_dbg(dev, "%s: needed %i tries\n", __func__, tries); + + year = data->base_year; + while (day >= rtd119x_rtc_days_in_year(year)) { + day -= rtd119x_rtc_days_in_year(year); + year++; + } + tm->tm_year = year - 1900; + tm->tm_yday = day; + + tm->tm_mon = 0; + while (day >= rtc_month_days(tm->tm_mon, year)) { + day -= rtc_month_days(tm->tm_mon, year); + tm->tm_mon++; + } + tm->tm_mday = day + 1; + + return 0; +} + +static int rtd119x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + unsigned int day; + int i; + + if (1900 + tm->tm_year < data->base_year) + return -EINVAL; + + day = 0; + for (i = data->base_year; i < 1900 + tm->tm_year; i++) + day += rtd119x_rtc_days_in_year(i); + + day += tm->tm_yday; + if (day > 0x7fff) + return -EINVAL; + + rtd119x_rtc_set_enabled(dev, false); + + writel_relaxed((tm->tm_sec << 1) & RTD_RTCSEC_RTCSEC_MASK, data->base + RTD_RTCSEC); + writel_relaxed(tm->tm_min & RTD_RTCMIN_RTCMIN_MASK, data->base + RTD_RTCMIN); + writel_relaxed(tm->tm_hour & RTD_RTCHR_RTCHR_MASK, data->base + RTD_RTCHR); + writel_relaxed(day & RTD_RTCDATE1_RTCDATE1_MASK, data->base + RTD_RTCDATE1); + writel_relaxed((day >> 8) & RTD_RTCDATE2_RTCDATE2_MASK, data->base + RTD_RTCDATE2); + + rtd119x_rtc_set_enabled(dev, true); + + return 0; +} + +static const struct rtc_class_ops rtd119x_rtc_ops = { + .read_time = rtd119x_rtc_read_time, + .set_time = rtd119x_rtc_set_time, +}; + +static const struct of_device_id rtd119x_rtc_dt_ids[] = { + { .compatible = "realtek,rtd1295-rtc" }, + { } +}; + +static int rtd119x_rtc_probe(struct platform_device *pdev) +{ + struct rtd119x_rtc *data; + struct resource *res; + u32 val; + int ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + data->base_year = 2014; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + data->clk = of_clk_get(pdev->dev.of_node, 0); + if (IS_ERR(data->clk)) + return PTR_ERR(data->clk); + + ret = clk_prepare_enable(data->clk); + if (ret) { + clk_put(data->clk); + return ret; + } + + val = readl_relaxed(data->base + RTD_RTCACR); + if (!(val & RTD_RTCACR_RTCPWR)) { + writel_relaxed(RTD_RTCACR_RTCPWR, data->base + RTD_RTCACR); + + rtd119x_rtc_reset(&pdev->dev); + + writel_relaxed(0, data->base + RTD_RTCMIN); + writel_relaxed(0, data->base + RTD_RTCHR); + writel_relaxed(0, data->base + RTD_RTCDATE1); + writel_relaxed(0, data->base + RTD_RTCDATE2); + } + + rtd119x_rtc_set_enabled(&pdev->dev, true); + + data->rtcdev = devm_rtc_device_register(&pdev->dev, "rtc", + &rtd119x_rtc_ops, THIS_MODULE); + if (IS_ERR(data->rtcdev)) { + dev_err(&pdev->dev, "failed to register rtc device"); + clk_disable_unprepare(data->clk); + clk_put(data->clk); + return PTR_ERR(data->rtcdev); + } + + return 0; +} + +static int rtd119x_rtc_remove(struct platform_device *pdev) +{ + struct rtd119x_rtc *data = platform_get_drvdata(pdev); + + rtd119x_rtc_set_enabled(&pdev->dev, false); + + clk_disable_unprepare(data->clk); + clk_put(data->clk); + + return 0; +} + +static struct platform_driver rtd119x_rtc_driver = { + .probe = rtd119x_rtc_probe, + .remove = rtd119x_rtc_remove, + .driver = { + .name = "rtd1295-rtc", + .of_match_table = rtd119x_rtc_dt_ids, + }, +}; +builtin_platform_driver(rtd119x_rtc_driver); diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c index 85fa1da03762..aa09771de04f 100644 --- a/drivers/rtc/rtc-rv3029c2.c +++ b/drivers/rtc/rtc-rv3029c2.c @@ -868,7 +868,7 @@ static int rv3029_i2c_probe(struct i2c_client *client, return rv3029_probe(&client->dev, regmap, client->irq, client->name); } -static struct i2c_device_id rv3029_id[] = { +static const struct i2c_device_id rv3029_id[] = { { "rv3029", 0 }, { "rv3029c2", 0 }, { } diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c index 449820eeefe8..7067bca5c20d 100644 --- a/drivers/rtc/rtc-s35390a.c +++ b/drivers/rtc/rtc-s35390a.c @@ -106,33 +106,12 @@ static int s35390a_get_reg(struct s35390a *s35390a, int reg, char *buf, int len) return 0; } -/* - * Returns <0 on error, 0 if rtc is setup fine and 1 if the chip was reset. - * To keep the information if an irq is pending, pass the value read from - * STATUS1 to the caller. - */ -static int s35390a_reset(struct s35390a *s35390a, char *status1) +static int s35390a_init(struct s35390a *s35390a) { char buf; int ret; unsigned initcount = 0; - ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, status1, 1); - if (ret < 0) - return ret; - - if (*status1 & S35390A_FLAG_POC) - /* - * Do not communicate for 0.5 seconds since the power-on - * detection circuit is in operation. - */ - msleep(500); - else if (!(*status1 & S35390A_FLAG_BLD)) - /* - * If both POC and BLD are unset everything is fine. - */ - return 0; - /* * At least one of POC and BLD are set, so reinitialise chip. Keeping * this information in the hardware to know later that the time isn't @@ -142,7 +121,6 @@ static int s35390a_reset(struct s35390a *s35390a, char *status1) * The 24H bit is kept over reset, so set it already here. */ initialize: - *status1 = S35390A_FLAG_24H; buf = S35390A_FLAG_RESET | S35390A_FLAG_24H; ret = s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1); @@ -165,6 +143,34 @@ initialize: return 1; } +/* + * Returns <0 on error, 0 if rtc is setup fine and 1 if the chip was reset. + * To keep the information if an irq is pending, pass the value read from + * STATUS1 to the caller. + */ +static int s35390a_read_status(struct s35390a *s35390a, char *status1) +{ + int ret; + + ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, status1, 1); + if (ret < 0) + return ret; + + if (*status1 & S35390A_FLAG_POC) { + /* + * Do not communicate for 0.5 seconds since the power-on + * detection circuit is in operation. + */ + msleep(500); + return 1; + } else if (*status1 & S35390A_FLAG_BLD) + return 1; + /* + * If both POC and BLD are unset everything is fine. + */ + return 0; +} + static int s35390a_disable_test_mode(struct s35390a *s35390a) { char buf[1]; @@ -208,13 +214,16 @@ static int s35390a_set_datetime(struct i2c_client *client, struct rtc_time *tm) { struct s35390a *s35390a = i2c_get_clientdata(client); int i, err; - char buf[7]; + char buf[7], status; dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d mday=%d, " "mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + if (s35390a_read_status(s35390a, &status) == 1) + s35390a_init(s35390a); + buf[S35390A_BYTE_YEAR] = bin2bcd(tm->tm_year - 100); buf[S35390A_BYTE_MONTH] = bin2bcd(tm->tm_mon + 1); buf[S35390A_BYTE_DAY] = bin2bcd(tm->tm_mday); @@ -235,9 +244,12 @@ static int s35390a_set_datetime(struct i2c_client *client, struct rtc_time *tm) static int s35390a_get_datetime(struct i2c_client *client, struct rtc_time *tm) { struct s35390a *s35390a = i2c_get_clientdata(client); - char buf[7]; + char buf[7], status; int i, err; + if (s35390a_read_status(s35390a, &status) == 1) + return -EINVAL; + err = s35390a_get_reg(s35390a, S35390A_CMD_TIME1, buf, sizeof(buf)); if (err < 0) return err; @@ -392,12 +404,42 @@ static int s35390a_rtc_set_time(struct device *dev, struct rtc_time *tm) return s35390a_set_datetime(to_i2c_client(dev), tm); } +static int s35390a_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s35390a *s35390a = i2c_get_clientdata(client); + char sts; + int err; + + switch (cmd) { + case RTC_VL_READ: + /* s35390a_reset set lowvoltage flag and init RTC if needed */ + err = s35390a_read_status(s35390a, &sts); + if (err < 0) + return err; + if (copy_to_user((void __user *)arg, &err, sizeof(int))) + return -EFAULT; + break; + case RTC_VL_CLR: + /* update flag and clear register */ + err = s35390a_init(s35390a); + if (err < 0) + return err; + break; + default: + return -ENOIOCTLCMD; + } + + return 0; +} + static const struct rtc_class_ops s35390a_rtc_ops = { .read_time = s35390a_rtc_read_time, .set_time = s35390a_rtc_set_time, .set_alarm = s35390a_rtc_set_alarm, .read_alarm = s35390a_rtc_read_alarm, - + .ioctl = s35390a_rtc_ioctl, }; static struct i2c_driver s35390a_driver; @@ -405,7 +447,7 @@ static struct i2c_driver s35390a_driver; static int s35390a_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int err, err_reset; + int err, err_read; unsigned int i; struct s35390a *s35390a; struct rtc_time tm; @@ -438,9 +480,9 @@ static int s35390a_probe(struct i2c_client *client, } } - err_reset = s35390a_reset(s35390a, &status1); - if (err_reset < 0) { - err = err_reset; + err_read = s35390a_read_status(s35390a, &status1); + if (err_read < 0) { + err = err_read; dev_err(&client->dev, "error resetting chip\n"); goto exit_dummy; } @@ -466,7 +508,7 @@ static int s35390a_probe(struct i2c_client *client, } } - if (err_reset > 0 || s35390a_get_datetime(client, &tm) < 0) + if (err_read > 0 || s35390a_get_datetime(client, &tm) < 0) dev_warn(&client->dev, "clock needs to be set\n"); device_set_wakeup_capable(&client->dev, 1); diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index c2187bf6c7e4..ed71d1113627 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -95,46 +95,6 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int sa1100_rtc_open(struct device *dev) -{ - struct sa1100_rtc *info = dev_get_drvdata(dev); - struct rtc_device *rtc = info->rtc; - int ret; - - ret = request_irq(info->irq_1hz, sa1100_rtc_interrupt, 0, "rtc 1Hz", dev); - if (ret) { - dev_err(dev, "IRQ %d already in use.\n", info->irq_1hz); - goto fail_ui; - } - ret = request_irq(info->irq_alarm, sa1100_rtc_interrupt, 0, "rtc Alrm", dev); - if (ret) { - dev_err(dev, "IRQ %d already in use.\n", info->irq_alarm); - goto fail_ai; - } - rtc->max_user_freq = RTC_FREQ; - rtc_irq_set_freq(rtc, NULL, RTC_FREQ); - - return 0; - - fail_ai: - free_irq(info->irq_1hz, dev); - fail_ui: - clk_disable_unprepare(info->clk); - return ret; -} - -static void sa1100_rtc_release(struct device *dev) -{ - struct sa1100_rtc *info = dev_get_drvdata(dev); - - spin_lock_irq(&info->lock); - writel_relaxed(0, info->rtsr); - spin_unlock_irq(&info->lock); - - free_irq(info->irq_alarm, dev); - free_irq(info->irq_1hz, dev); -} - static int sa1100_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) { u32 rtsr; @@ -216,8 +176,6 @@ static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq) } static const struct rtc_class_ops sa1100_rtc_ops = { - .open = sa1100_rtc_open, - .release = sa1100_rtc_release, .read_time = sa1100_rtc_read_time, .set_time = sa1100_rtc_set_time, .read_alarm = sa1100_rtc_read_alarm, @@ -265,6 +223,9 @@ int sa1100_rtc_init(struct platform_device *pdev, struct sa1100_rtc *info) } info->rtc = rtc; + rtc->max_user_freq = RTC_FREQ; + rtc_irq_set_freq(rtc, NULL, RTC_FREQ); + /* Fix for a nasty initialization problem the in SA11xx RTSR register. * See also the comments in sa1100_rtc_interrupt(). * @@ -299,6 +260,7 @@ static int sa1100_rtc_probe(struct platform_device *pdev) struct resource *iores; void __iomem *base; int irq_1hz, irq_alarm; + int ret; irq_1hz = platform_get_irq_byname(pdev, "rtc 1Hz"); irq_alarm = platform_get_irq_byname(pdev, "rtc alarm"); @@ -311,6 +273,19 @@ static int sa1100_rtc_probe(struct platform_device *pdev) info->irq_1hz = irq_1hz; info->irq_alarm = irq_alarm; + ret = devm_request_irq(&pdev->dev, irq_1hz, sa1100_rtc_interrupt, 0, + "rtc 1Hz", &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "IRQ %d already in use.\n", irq_1hz); + return ret; + } + ret = devm_request_irq(&pdev->dev, irq_alarm, sa1100_rtc_interrupt, 0, + "rtc Alrm", &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "IRQ %d already in use.\n", irq_alarm); + return ret; + } + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, iores); if (IS_ERR(base)) @@ -339,8 +314,12 @@ static int sa1100_rtc_remove(struct platform_device *pdev) { struct sa1100_rtc *info = platform_get_drvdata(pdev); - if (info) + if (info) { + spin_lock_irq(&info->lock); + writel_relaxed(0, info->rtsr); + spin_unlock_irq(&info->lock); clk_disable_unprepare(info->clk); + } return 0; } diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index 39cbc1238b92..3d2216ccd860 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -73,6 +73,9 @@ #define SUN6I_ALARM_CONFIG 0x0050 #define SUN6I_ALARM_CONFIG_WAKEUP BIT(0) +#define SUN6I_LOSC_OUT_GATING 0x0060 +#define SUN6I_LOSC_OUT_GATING_EN BIT(0) + /* * Get date values */ @@ -125,6 +128,7 @@ struct sun6i_rtc_dev { struct clk_hw hw; struct clk_hw *int_osc; struct clk *losc; + struct clk *ext_losc; spinlock_t lock; }; @@ -188,23 +192,24 @@ static void __init sun6i_rtc_clk_init(struct device_node *node) struct clk_init_data init = { .ops = &sun6i_rtc_osc_ops, }; + const char *clkout_name = "osc32k-out"; const char *parents[2]; rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); if (!rtc) return; - spin_lock_init(&rtc->lock); - clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws), + clk_data = kzalloc(sizeof(*clk_data) + (sizeof(*clk_data->hws) * 2), GFP_KERNEL); if (!clk_data) return; + spin_lock_init(&rtc->lock); rtc->base = of_io_request_and_map(node, 0, of_node_full_name(node)); if (IS_ERR(rtc->base)) { pr_crit("Can't map RTC registers"); - return; + goto err; } /* Switch to the external, more precise, oscillator */ @@ -216,7 +221,7 @@ static void __init sun6i_rtc_clk_init(struct device_node *node) /* Deal with old DTs */ if (!of_get_property(node, "clocks", NULL)) - return; + goto err; rtc->int_osc = clk_hw_register_fixed_rate_with_accuracy(NULL, "rtc-int-osc", @@ -235,7 +240,8 @@ static void __init sun6i_rtc_clk_init(struct device_node *node) init.parent_names = parents; init.num_parents = of_clk_get_parent_count(node) + 1; - of_property_read_string(node, "clock-output-names", &init.name); + of_property_read_string_index(node, "clock-output-names", 0, + &init.name); rtc->losc = clk_register(NULL, &rtc->hw); if (IS_ERR(rtc->losc)) { @@ -243,9 +249,25 @@ static void __init sun6i_rtc_clk_init(struct device_node *node) return; } - clk_data->num = 1; + of_property_read_string_index(node, "clock-output-names", 1, + &clkout_name); + rtc->ext_losc = clk_register_gate(NULL, clkout_name, rtc->hw.init->name, + 0, rtc->base + SUN6I_LOSC_OUT_GATING, + SUN6I_LOSC_OUT_GATING_EN, 0, + &rtc->lock); + if (IS_ERR(rtc->ext_losc)) { + pr_crit("Couldn't register the LOSC external gate\n"); + return; + } + + clk_data->num = 2; clk_data->hws[0] = &rtc->hw; + clk_data->hws[1] = __clk_get_hw(rtc->ext_losc); of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); + return; + +err: + kfree(clk_data); } CLK_OF_DECLARE_DRIVER(sun6i_rtc_clk, "allwinner,sun6i-a31-rtc", sun6i_rtc_clk_init); diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index e1b86bb01062..7ce22967fd16 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -119,23 +119,6 @@ static inline void write_elapsed_second(unsigned long sec) spin_unlock_irq(&rtc_lock); } -static void vr41xx_rtc_release(struct device *dev) -{ - - spin_lock_irq(&rtc_lock); - - rtc1_write(ECMPLREG, 0); - rtc1_write(ECMPMREG, 0); - rtc1_write(ECMPHREG, 0); - rtc1_write(RTCL1LREG, 0); - rtc1_write(RTCL1HREG, 0); - - spin_unlock_irq(&rtc_lock); - - disable_irq(aie_irq); - disable_irq(pie_irq); -} - static int vr41xx_rtc_read_time(struct device *dev, struct rtc_time *time) { unsigned long epoch_sec, elapsed_sec; @@ -272,7 +255,6 @@ static irqreturn_t rtclong1_interrupt(int irq, void *dev_id) } static const struct rtc_class_ops vr41xx_rtc_ops = { - .release = vr41xx_rtc_release, .ioctl = vr41xx_rtc_ioctl, .read_time = vr41xx_rtc_read_time, .set_time = vr41xx_rtc_set_time, diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index d145e0d90227..41366339b950 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -283,7 +283,7 @@ config SCSI_ISCSI_ATTRS config SCSI_SAS_ATTRS tristate "SAS Transport Attributes" depends on SCSI - select BLK_DEV_BSG + select BLK_DEV_BSGLIB help If you wish to export transport-specific information about each attached SAS device to sysfs, say Y. diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 831a1c8b9f89..fe3a0da3ec97 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -315,8 +315,6 @@ static void scsi_host_dev_release(struct device *dev) { struct Scsi_Host *shost = dev_to_shost(dev); struct device *parent = dev->parent; - struct request_queue *q; - void *queuedata; scsi_proc_hostdir_rm(shost->hostt); @@ -326,12 +324,6 @@ static void scsi_host_dev_release(struct device *dev) kthread_stop(shost->ehandler); if (shost->work_q) destroy_workqueue(shost->work_q); - q = shost->uspace_req_q; - if (q) { - queuedata = q->queuedata; - blk_cleanup_queue(q); - kfree(queuedata); - } if (shost->shost_state == SHOST_CREATED) { /* diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index c5b6b7165489..e9e97803442a 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -90,7 +90,7 @@ static struct list_head *cuse_conntbl_head(dev_t devt) static ssize_t cuse_read_iter(struct kiocb *kiocb, struct iov_iter *to) { - struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(kiocb->ki_filp); + struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(kiocb); loff_t pos = 0; return fuse_direct_io(&io, to, &pos, FUSE_DIO_CUSE); @@ -98,7 +98,7 @@ static ssize_t cuse_read_iter(struct kiocb *kiocb, struct iov_iter *to) static ssize_t cuse_write_iter(struct kiocb *kiocb, struct iov_iter *from) { - struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(kiocb->ki_filp); + struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(kiocb); loff_t pos = 0; /* * No locking or generic_write_checks(), the server is diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index c16d00e53264..13c65dd2d37d 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1222,9 +1222,6 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, struct fuse_in *in; unsigned reqsize; - if (task_active_pid_ns(current) != fc->pid_ns) - return -EIO; - restart: spin_lock(&fiq->waitq.lock); err = -EAGAIN; @@ -1262,6 +1259,13 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, in = &req->in; reqsize = in->h.len; + + if (task_active_pid_ns(current) != fc->pid_ns) { + rcu_read_lock(); + in->h.pid = pid_vnr(find_pid_ns(in->h.pid, fc->pid_ns)); + rcu_read_unlock(); + } + /* If request is too large, reply with an error and restart the read */ if (nbytes < reqsize) { req->out.h.error = -EIO; @@ -1823,9 +1827,6 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, struct fuse_req *req; struct fuse_out_header oh; - if (task_active_pid_ns(current) != fc->pid_ns) - return -EIO; - if (nbytes < sizeof(struct fuse_out_header)) return -EINVAL; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 00800c07ba1c..622081b97426 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -923,33 +923,29 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat, return err; } -int fuse_update_attributes(struct inode *inode, struct kstat *stat, - struct file *file, bool *refreshed) +static int fuse_update_get_attr(struct inode *inode, struct file *file, + struct kstat *stat) { struct fuse_inode *fi = get_fuse_inode(inode); - int err; - bool r; + int err = 0; if (time_before64(fi->i_time, get_jiffies_64())) { - r = true; forget_all_cached_acls(inode); err = fuse_do_getattr(inode, stat, file); - } else { - r = false; - err = 0; - if (stat) { - generic_fillattr(inode, stat); - stat->mode = fi->orig_i_mode; - stat->ino = fi->orig_ino; - } + } else if (stat) { + generic_fillattr(inode, stat); + stat->mode = fi->orig_i_mode; + stat->ino = fi->orig_ino; } - if (refreshed != NULL) - *refreshed = r; - return err; } +int fuse_update_attributes(struct inode *inode, struct file *file) +{ + return fuse_update_get_attr(inode, file, NULL); +} + int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, u64 child_nodeid, struct qstr *name) { @@ -1786,7 +1782,7 @@ static int fuse_getattr(const struct path *path, struct kstat *stat, if (!fuse_allow_current_process(fc)) return -EACCES; - return fuse_update_attributes(inode, stat, NULL, NULL); + return fuse_update_get_attr(inode, NULL, stat); } static const struct inode_operations fuse_dir_inode_operations = { diff --git a/fs/fuse/file.c b/fs/fuse/file.c index d66789804287..cb7dff5c45d7 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -645,7 +645,7 @@ static size_t fuse_async_req_send(struct fuse_conn *fc, struct fuse_req *req, static size_t fuse_send_read(struct fuse_req *req, struct fuse_io_priv *io, loff_t pos, size_t count, fl_owner_t owner) { - struct file *file = io->file; + struct file *file = io->iocb->ki_filp; struct fuse_file *ff = file->private_data; struct fuse_conn *fc = ff->fc; @@ -707,7 +707,8 @@ static void fuse_short_read(struct fuse_req *req, struct inode *inode, static int fuse_do_readpage(struct file *file, struct page *page) { - struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(file); + struct kiocb iocb; + struct fuse_io_priv io; struct inode *inode = page->mapping->host; struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req; @@ -735,6 +736,8 @@ static int fuse_do_readpage(struct file *file, struct page *page) req->num_pages = 1; req->pages[0] = page; req->page_descs[0].length = count; + init_sync_kiocb(&iocb, file); + io = (struct fuse_io_priv) FUSE_IO_PRIV_SYNC(&iocb); num_read = fuse_send_read(req, &io, pos, count, NULL); err = req->out.h.error; @@ -923,7 +926,7 @@ static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to) if (fc->auto_inval_data || (iocb->ki_pos + iov_iter_count(to) > i_size_read(inode))) { int err; - err = fuse_update_attributes(inode, NULL, iocb->ki_filp, NULL); + err = fuse_update_attributes(inode, iocb->ki_filp); if (err) return err; } @@ -957,13 +960,18 @@ static void fuse_write_fill(struct fuse_req *req, struct fuse_file *ff, static size_t fuse_send_write(struct fuse_req *req, struct fuse_io_priv *io, loff_t pos, size_t count, fl_owner_t owner) { - struct file *file = io->file; + struct kiocb *iocb = io->iocb; + struct file *file = iocb->ki_filp; struct fuse_file *ff = file->private_data; struct fuse_conn *fc = ff->fc; struct fuse_write_in *inarg = &req->misc.write.in; fuse_write_fill(req, ff, pos, count); inarg->flags = file->f_flags; + if (iocb->ki_flags & IOCB_DSYNC) + inarg->flags |= O_DSYNC; + if (iocb->ki_flags & IOCB_SYNC) + inarg->flags |= O_SYNC; if (owner != NULL) { inarg->write_flags |= FUSE_WRITE_LOCKOWNER; inarg->lock_owner = fuse_lock_owner_id(fc, owner); @@ -993,14 +1001,14 @@ bool fuse_write_update_size(struct inode *inode, loff_t pos) return ret; } -static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file, +static size_t fuse_send_write_pages(struct fuse_req *req, struct kiocb *iocb, struct inode *inode, loff_t pos, size_t count) { size_t res; unsigned offset; unsigned i; - struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(file); + struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb); for (i = 0; i < req->num_pages; i++) fuse_wait_on_page_writeback(inode, req->pages[i]->index); @@ -1100,7 +1108,7 @@ static inline unsigned fuse_wr_pages(loff_t pos, size_t len) FUSE_MAX_PAGES_PER_REQ); } -static ssize_t fuse_perform_write(struct file *file, +static ssize_t fuse_perform_write(struct kiocb *iocb, struct address_space *mapping, struct iov_iter *ii, loff_t pos) { @@ -1133,7 +1141,7 @@ static ssize_t fuse_perform_write(struct file *file, } else { size_t num_written; - num_written = fuse_send_write_pages(req, file, inode, + num_written = fuse_send_write_pages(req, iocb, inode, pos, count); err = req->out.h.error; if (!err) { @@ -1169,7 +1177,7 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) if (get_fuse_conn(inode)->writeback_cache) { /* Update size (EOF optimization) and mode (SUID clearing) */ - err = fuse_update_attributes(mapping->host, NULL, file, NULL); + err = fuse_update_attributes(mapping->host, file); if (err) return err; @@ -1201,7 +1209,7 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) pos += written; - written_buffered = fuse_perform_write(file, mapping, from, pos); + written_buffered = fuse_perform_write(iocb, mapping, from, pos); if (written_buffered < 0) { err = written_buffered; goto out; @@ -1220,13 +1228,15 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from) written += written_buffered; iocb->ki_pos = pos + written_buffered; } else { - written = fuse_perform_write(file, mapping, from, iocb->ki_pos); + written = fuse_perform_write(iocb, mapping, from, iocb->ki_pos); if (written >= 0) iocb->ki_pos += written; } out: current->backing_dev_info = NULL; inode_unlock(inode); + if (written > 0) + written = generic_write_sync(iocb, written); return written ? written : err; } @@ -1317,7 +1327,7 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, { int write = flags & FUSE_DIO_WRITE; int cuse = flags & FUSE_DIO_CUSE; - struct file *file = io->file; + struct file *file = io->iocb->ki_filp; struct inode *inode = file->f_mapping->host; struct fuse_file *ff = file->private_data; struct fuse_conn *fc = ff->fc; @@ -1399,8 +1409,7 @@ static ssize_t __fuse_direct_read(struct fuse_io_priv *io, loff_t *ppos) { ssize_t res; - struct file *file = io->file; - struct inode *inode = file_inode(file); + struct inode *inode = file_inode(io->iocb->ki_filp); if (is_bad_inode(inode)) return -EIO; @@ -1414,15 +1423,14 @@ static ssize_t __fuse_direct_read(struct fuse_io_priv *io, static ssize_t fuse_direct_read_iter(struct kiocb *iocb, struct iov_iter *to) { - struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb->ki_filp); + struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb); return __fuse_direct_read(&io, to, &iocb->ki_pos); } static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) { - struct file *file = iocb->ki_filp; - struct inode *inode = file_inode(file); - struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(file); + struct inode *inode = file_inode(iocb->ki_filp); + struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb); ssize_t res; if (is_bad_inode(inode)) @@ -2181,9 +2189,6 @@ static int fuse_setlk(struct file *file, struct file_lock *fl, int flock) if ((fl->fl_flags & FL_CLOSE_POSIX) == FL_CLOSE_POSIX) return 0; - if (pid && pid_nr == 0) - return -EOVERFLOW; - fuse_lk_fill(&args, file, fl, opcode, pid_nr, flock, &inarg); err = fuse_simple_request(fc, &args); @@ -2303,7 +2308,7 @@ static loff_t fuse_lseek(struct file *file, loff_t offset, int whence) return vfs_setpos(file, outarg.offset, inode->i_sb->s_maxbytes); fallback: - err = fuse_update_attributes(inode, NULL, file, NULL); + err = fuse_update_attributes(inode, file); if (!err) return generic_file_llseek(file, offset, whence); else @@ -2323,7 +2328,7 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int whence) break; case SEEK_END: inode_lock(inode); - retval = fuse_update_attributes(inode, NULL, file, NULL); + retval = fuse_update_attributes(inode, file); if (!retval) retval = generic_file_llseek(file, offset, whence); inode_unlock(inode); @@ -2874,7 +2879,6 @@ fuse_direct_IO(struct kiocb *iocb, struct iov_iter *iter) io->offset = offset; io->write = (iov_iter_rw(iter) == WRITE); io->err = 0; - io->file = file; /* * By default, we want to optimize all I/Os with async request * submission to the client filesystem if supported. diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index bd4d2a3e1ec1..d5773ca67ad2 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -252,16 +252,15 @@ struct fuse_io_priv { bool should_dirty; int err; struct kiocb *iocb; - struct file *file; struct completion *done; bool blocking; }; -#define FUSE_IO_PRIV_SYNC(f) \ +#define FUSE_IO_PRIV_SYNC(i) \ { \ .refcnt = KREF_INIT(1), \ .async = 0, \ - .file = f, \ + .iocb = i, \ } /** @@ -905,8 +904,7 @@ u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id); void fuse_update_ctime(struct inode *inode); -int fuse_update_attributes(struct inode *inode, struct kstat *stat, - struct file *file, bool *refreshed); +int fuse_update_attributes(struct inode *inode, struct file *file); void fuse_flush_writepages(struct inode *inode); diff --git a/fs/inode.c b/fs/inode.c index 210054157a49..d1e35b53bb23 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1570,11 +1570,24 @@ EXPORT_SYMBOL(bmap); static void update_ovl_inode_times(struct dentry *dentry, struct inode *inode, bool rcu) { - if (!rcu) { - struct inode *realinode = d_real_inode(dentry); + struct dentry *upperdentry; - if (unlikely(inode != realinode) && - (!timespec_equal(&inode->i_mtime, &realinode->i_mtime) || + /* + * Nothing to do if in rcu or if non-overlayfs + */ + if (rcu || likely(!(dentry->d_flags & DCACHE_OP_REAL))) + return; + + upperdentry = d_real(dentry, NULL, 0, D_REAL_UPPER); + + /* + * If file is on lower then we can't update atime, so no worries about + * stale mtime/ctime. + */ + if (upperdentry) { + struct inode *realinode = d_inode(upperdentry); + + if ((!timespec_equal(&inode->i_mtime, &realinode->i_mtime) || !timespec_equal(&inode->i_ctime, &realinode->i_ctime))) { inode->i_mtime = realinode->i_mtime; inode->i_ctime = realinode->i_ctime; diff --git a/fs/internal.h b/fs/internal.h index fedfe94d84ba..48cee21b4f14 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -71,8 +71,10 @@ extern void __init mnt_init(void); extern int __mnt_want_write(struct vfsmount *); extern int __mnt_want_write_file(struct file *); +extern int mnt_want_write_file_path(struct file *); extern void __mnt_drop_write(struct vfsmount *); extern void __mnt_drop_write_file(struct file *); +extern void mnt_drop_write_file_path(struct file *); /* * fs_struct.c diff --git a/fs/namespace.c b/fs/namespace.c index f8893dc6a989..df0f7521979a 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -431,13 +431,18 @@ int __mnt_want_write_file(struct file *file) } /** - * mnt_want_write_file - get write access to a file's mount + * mnt_want_write_file_path - get write access to a file's mount * @file: the file who's mount on which to take a write * * This is like mnt_want_write, but it takes a file and can * do some optimisations if the file is open for write already + * + * Called by the vfs for cases when we have an open file at hand, but will do an + * inode operation on it (important distinction for files opened on overlayfs, + * since the file operations will come from the real underlying file, while + * inode operations come from the overlay). */ -int mnt_want_write_file(struct file *file) +int mnt_want_write_file_path(struct file *file) { int ret; @@ -447,6 +452,53 @@ int mnt_want_write_file(struct file *file) sb_end_write(file->f_path.mnt->mnt_sb); return ret; } + +static inline int may_write_real(struct file *file) +{ + struct dentry *dentry = file->f_path.dentry; + struct dentry *upperdentry; + + /* Writable file? */ + if (file->f_mode & FMODE_WRITER) + return 0; + + /* Not overlayfs? */ + if (likely(!(dentry->d_flags & DCACHE_OP_REAL))) + return 0; + + /* File refers to upper, writable layer? */ + upperdentry = d_real(dentry, NULL, 0, D_REAL_UPPER); + if (upperdentry && file_inode(file) == d_inode(upperdentry)) + return 0; + + /* Lower layer: can't write to real file, sorry... */ + return -EPERM; +} + +/** + * mnt_want_write_file - get write access to a file's mount + * @file: the file who's mount on which to take a write + * + * This is like mnt_want_write, but it takes a file and can + * do some optimisations if the file is open for write already + * + * Mostly called by filesystems from their ioctl operation before performing + * modification. On overlayfs this needs to check if the file is on a read-only + * lower layer and deny access in that case. + */ +int mnt_want_write_file(struct file *file) +{ + int ret; + + ret = may_write_real(file); + if (!ret) { + sb_start_write(file_inode(file)->i_sb); + ret = __mnt_want_write_file(file); + if (ret) + sb_end_write(file_inode(file)->i_sb); + } + return ret; +} EXPORT_SYMBOL_GPL(mnt_want_write_file); /** @@ -484,10 +536,16 @@ void __mnt_drop_write_file(struct file *file) __mnt_drop_write(file->f_path.mnt); } -void mnt_drop_write_file(struct file *file) +void mnt_drop_write_file_path(struct file *file) { mnt_drop_write(file->f_path.mnt); } + +void mnt_drop_write_file(struct file *file) +{ + __mnt_drop_write(file->f_path.mnt); + sb_end_write(file_inode(file)->i_sb); +} EXPORT_SYMBOL(mnt_drop_write_file); static int mnt_make_readonly(struct mount *mnt) diff --git a/fs/open.c b/fs/open.c index 35bb784763a4..7ea118471dce 100644 --- a/fs/open.c +++ b/fs/open.c @@ -96,7 +96,7 @@ long vfs_truncate(const struct path *path, loff_t length) * write access on the upper inode, not on the overlay inode. For * non-overlay filesystems d_real() is an identity function. */ - upperdentry = d_real(path->dentry, NULL, O_WRONLY); + upperdentry = d_real(path->dentry, NULL, O_WRONLY, 0); error = PTR_ERR(upperdentry); if (IS_ERR(upperdentry)) goto mnt_drop_write_and_out; @@ -670,12 +670,12 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group) if (!f.file) goto out; - error = mnt_want_write_file(f.file); + error = mnt_want_write_file_path(f.file); if (error) goto out_fput; audit_file(f.file); error = chown_common(&f.file->f_path, user, group); - mnt_drop_write_file(f.file); + mnt_drop_write_file_path(f.file); out_fput: fdput(f); out: @@ -857,7 +857,7 @@ EXPORT_SYMBOL(file_path); int vfs_open(const struct path *path, struct file *file, const struct cred *cred) { - struct dentry *dentry = d_real(path->dentry, NULL, file->f_flags); + struct dentry *dentry = d_real(path->dentry, NULL, file->f_flags, 0); if (IS_ERR(dentry)) return PTR_ERR(dentry); diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 48b70e6490f3..9cb0c80e5967 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -155,7 +155,7 @@ static int ovl_set_opaque(struct dentry *dentry, struct dentry *upperdentry) static void ovl_instantiate(struct dentry *dentry, struct inode *inode, struct dentry *newdentry, bool hardlink) { - ovl_dentry_version_inc(dentry->d_parent); + ovl_dentry_version_inc(dentry->d_parent, false); ovl_dentry_set_upper_alias(dentry); if (!hardlink) { ovl_inode_update(inode, newdentry); @@ -692,7 +692,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir) if (flags) ovl_cleanup(wdir, upper); - ovl_dentry_version_inc(dentry->d_parent); + ovl_dentry_version_inc(dentry->d_parent, true); out_d_drop: d_drop(dentry); dput(whiteout); @@ -742,7 +742,7 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir) err = vfs_rmdir(dir, upper); else err = vfs_unlink(dir, upper, NULL); - ovl_dentry_version_inc(dentry->d_parent); + ovl_dentry_version_inc(dentry->d_parent, ovl_type_origin(dentry)); /* * Keeping this dentry hashed would mean having to release @@ -1089,8 +1089,9 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, drop_nlink(d_inode(new)); } - ovl_dentry_version_inc(old->d_parent); - ovl_dentry_version_inc(new->d_parent); + ovl_dentry_version_inc(old->d_parent, + !overwrite && ovl_type_origin(new)); + ovl_dentry_version_inc(new->d_parent, ovl_type_origin(old)); out_dput: dput(newdentry); diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 5bc71642b226..a619addecafc 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -498,6 +498,9 @@ static int ovl_set_nlink_common(struct dentry *dentry, len = snprintf(buf, sizeof(buf), format, (int) (inode->i_nlink - realinode->i_nlink)); + if (WARN_ON(len >= sizeof(buf))) + return -EIO; + return ovl_do_setxattr(ovl_dentry_upper(dentry), OVL_XATTR_NLINK, buf, len, 0); } @@ -576,10 +579,13 @@ static int ovl_inode_set(struct inode *inode, void *data) static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry, struct dentry *upperdentry) { - struct inode *lowerinode = lowerdentry ? d_inode(lowerdentry) : NULL; - - /* Lower (origin) inode must match, even if NULL */ - if (ovl_inode_lower(inode) != lowerinode) + /* + * Allow non-NULL lower inode in ovl_inode even if lowerdentry is NULL. + * This happens when finding a copied up overlay inode for a renamed + * or hardlinked overlay dentry and lower dentry cannot be followed + * by origin because lower fs does not support file handles. + */ + if (lowerdentry && ovl_inode_lower(inode) != d_inode(lowerdentry)) return false; /* diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index e927a62c97ae..d4e8c1a08fb0 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -204,8 +204,8 @@ struct dentry *ovl_i_dentry_upper(struct inode *inode); struct inode *ovl_inode_upper(struct inode *inode); struct inode *ovl_inode_lower(struct inode *inode); struct inode *ovl_inode_real(struct inode *inode); -struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry); -void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache); +struct ovl_dir_cache *ovl_dir_cache(struct inode *inode); +void ovl_set_dir_cache(struct inode *inode, struct ovl_dir_cache *cache); bool ovl_dentry_is_opaque(struct dentry *dentry); bool ovl_dentry_is_whiteout(struct dentry *dentry); void ovl_dentry_set_opaque(struct dentry *dentry); @@ -217,7 +217,7 @@ void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect); void ovl_inode_init(struct inode *inode, struct dentry *upperdentry, struct dentry *lowerdentry); void ovl_inode_update(struct inode *inode, struct dentry *upperdentry); -void ovl_dentry_version_inc(struct dentry *dentry); +void ovl_dentry_version_inc(struct dentry *dentry, bool impurity); u64 ovl_dentry_version_get(struct dentry *dentry); bool ovl_is_whiteout(struct dentry *dentry); struct file *ovl_path_open(struct path *path, int flags); @@ -229,6 +229,7 @@ int ovl_check_setxattr(struct dentry *dentry, struct dentry *upperdentry, int xerr); int ovl_set_impure(struct dentry *dentry, struct dentry *upperdentry); void ovl_set_flag(unsigned long flag, struct inode *inode); +void ovl_clear_flag(unsigned long flag, struct inode *inode); bool ovl_test_flag(unsigned long flag, struct inode *inode); bool ovl_inuse_trylock(struct dentry *dentry); void ovl_inuse_unlock(struct dentry *dentry); @@ -256,6 +257,7 @@ extern const struct file_operations ovl_dir_operations; int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list); void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list); void ovl_cache_free(struct list_head *list); +void ovl_dir_cache_free(struct inode *inode); int ovl_check_d_type_supported(struct path *realpath); void ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt, struct dentry *dentry, int level); diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index f0fd3adb1693..62e9b22a2077 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -15,11 +15,13 @@ #include <linux/rbtree.h> #include <linux/security.h> #include <linux/cred.h> +#include <linux/ratelimit.h> #include "overlayfs.h" struct ovl_cache_entry { unsigned int len; unsigned int type; + u64 real_ino; u64 ino; struct list_head l_node; struct rb_node node; @@ -32,18 +34,20 @@ struct ovl_dir_cache { long refcount; u64 version; struct list_head entries; + struct rb_root root; }; struct ovl_readdir_data { struct dir_context ctx; struct dentry *dentry; bool is_lowest; - struct rb_root root; + struct rb_root *root; struct list_head *list; struct list_head middle; struct ovl_cache_entry *first_maybe_whiteout; int count; int err; + bool is_upper; bool d_type_supported; }; @@ -58,7 +62,33 @@ struct ovl_dir_file { static struct ovl_cache_entry *ovl_cache_entry_from_node(struct rb_node *n) { - return container_of(n, struct ovl_cache_entry, node); + return rb_entry(n, struct ovl_cache_entry, node); +} + +static bool ovl_cache_entry_find_link(const char *name, int len, + struct rb_node ***link, + struct rb_node **parent) +{ + bool found = false; + struct rb_node **newp = *link; + + while (!found && *newp) { + int cmp; + struct ovl_cache_entry *tmp; + + *parent = *newp; + tmp = ovl_cache_entry_from_node(*newp); + cmp = strncmp(name, tmp->name, len); + if (cmp > 0) + newp = &tmp->node.rb_right; + else if (cmp < 0 || len < tmp->len) + newp = &tmp->node.rb_left; + else + found = true; + } + *link = newp; + + return found; } static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root, @@ -82,6 +112,32 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root, return NULL; } +static bool ovl_calc_d_ino(struct ovl_readdir_data *rdd, + struct ovl_cache_entry *p) +{ + /* Don't care if not doing ovl_iter() */ + if (!rdd->dentry) + return false; + + /* Always recalc d_ino for parent */ + if (strcmp(p->name, "..") == 0) + return true; + + /* If this is lower, then native d_ino will do */ + if (!rdd->is_upper) + return false; + + /* + * Recalc d_ino for '.' and for all entries if dir is impure (contains + * copied up entries) + */ + if ((p->name[0] == '.' && p->len == 1) || + ovl_test_flag(OVL_IMPURE, d_inode(rdd->dentry))) + return true; + + return false; +} + static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd, const char *name, int len, u64 ino, unsigned int d_type) @@ -97,7 +153,11 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd, p->name[len] = '\0'; p->len = len; p->type = d_type; + p->real_ino = ino; p->ino = ino; + /* Defer setting d_ino for upper entry to ovl_iterate() */ + if (ovl_calc_d_ino(rdd, p)) + p->ino = 0; p->is_whiteout = false; if (d_type == DT_CHR) { @@ -111,32 +171,22 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd, const char *name, int len, u64 ino, unsigned int d_type) { - struct rb_node **newp = &rdd->root.rb_node; + struct rb_node **newp = &rdd->root->rb_node; struct rb_node *parent = NULL; struct ovl_cache_entry *p; - while (*newp) { - int cmp; - struct ovl_cache_entry *tmp; - - parent = *newp; - tmp = ovl_cache_entry_from_node(*newp); - cmp = strncmp(name, tmp->name, len); - if (cmp > 0) - newp = &tmp->node.rb_right; - else if (cmp < 0 || len < tmp->len) - newp = &tmp->node.rb_left; - else - return 0; - } + if (ovl_cache_entry_find_link(name, len, &newp, &parent)) + return 0; p = ovl_cache_entry_new(rdd, name, len, ino, d_type); - if (p == NULL) + if (p == NULL) { + rdd->err = -ENOMEM; return -ENOMEM; + } list_add_tail(&p->l_node, rdd->list); rb_link_node(&p->node, parent, newp); - rb_insert_color(&p->node, &rdd->root); + rb_insert_color(&p->node, rdd->root); return 0; } @@ -147,7 +197,7 @@ static int ovl_fill_lowest(struct ovl_readdir_data *rdd, { struct ovl_cache_entry *p; - p = ovl_cache_entry_find(&rdd->root, name, namelen); + p = ovl_cache_entry_find(rdd->root, name, namelen); if (p) { list_move_tail(&p->l_node, &rdd->middle); } else { @@ -172,6 +222,16 @@ void ovl_cache_free(struct list_head *list) INIT_LIST_HEAD(list); } +void ovl_dir_cache_free(struct inode *inode) +{ + struct ovl_dir_cache *cache = ovl_dir_cache(inode); + + if (cache) { + ovl_cache_free(&cache->entries); + kfree(cache); + } +} + static void ovl_cache_put(struct ovl_dir_file *od, struct dentry *dentry) { struct ovl_dir_cache *cache = od->cache; @@ -179,8 +239,8 @@ static void ovl_cache_put(struct ovl_dir_file *od, struct dentry *dentry) WARN_ON(cache->refcount <= 0); cache->refcount--; if (!cache->refcount) { - if (ovl_dir_cache(dentry) == cache) - ovl_set_dir_cache(dentry, NULL); + if (ovl_dir_cache(d_inode(dentry)) == cache) + ovl_set_dir_cache(d_inode(dentry), NULL); ovl_cache_free(&cache->entries); kfree(cache); @@ -273,7 +333,8 @@ static void ovl_dir_reset(struct file *file) od->is_real = false; } -static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list) +static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list, + struct rb_root *root) { int err; struct path realpath; @@ -281,13 +342,14 @@ static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list) .ctx.actor = ovl_fill_merge, .dentry = dentry, .list = list, - .root = RB_ROOT, + .root = root, .is_lowest = false, }; int idx, next; for (idx = 0; idx != -1; idx = next) { next = ovl_path_next(idx, dentry, &realpath); + rdd.is_upper = ovl_dentry_upper(dentry) == realpath.dentry; if (next != -1) { err = ovl_dir_read(&realpath, &rdd); @@ -326,12 +388,13 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry) int res; struct ovl_dir_cache *cache; - cache = ovl_dir_cache(dentry); + cache = ovl_dir_cache(d_inode(dentry)); if (cache && ovl_dentry_version_get(dentry) == cache->version) { + WARN_ON(!cache->refcount); cache->refcount++; return cache; } - ovl_set_dir_cache(dentry, NULL); + ovl_set_dir_cache(d_inode(dentry), NULL); cache = kzalloc(sizeof(struct ovl_dir_cache), GFP_KERNEL); if (!cache) @@ -339,8 +402,9 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry) cache->refcount = 1; INIT_LIST_HEAD(&cache->entries); + cache->root = RB_ROOT; - res = ovl_dir_read_merged(dentry, &cache->entries); + res = ovl_dir_read_merged(dentry, &cache->entries, &cache->root); if (res) { ovl_cache_free(&cache->entries); kfree(cache); @@ -348,22 +412,266 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry) } cache->version = ovl_dentry_version_get(dentry); - ovl_set_dir_cache(dentry, cache); + ovl_set_dir_cache(d_inode(dentry), cache); return cache; } +/* + * Set d_ino for upper entries. Non-upper entries should always report + * the uppermost real inode ino and should not call this function. + * + * When not all layer are on same fs, report real ino also for upper. + * + * When all layers are on the same fs, and upper has a reference to + * copy up origin, call vfs_getattr() on the overlay entry to make + * sure that d_ino will be consistent with st_ino from stat(2). + */ +static int ovl_cache_update_ino(struct path *path, struct ovl_cache_entry *p) + +{ + struct dentry *dir = path->dentry; + struct dentry *this = NULL; + enum ovl_path_type type; + u64 ino = p->real_ino; + int err = 0; + + if (!ovl_same_sb(dir->d_sb)) + goto out; + + if (p->name[0] == '.') { + if (p->len == 1) { + this = dget(dir); + goto get; + } + if (p->len == 2 && p->name[1] == '.') { + /* we shall not be moved */ + this = dget(dir->d_parent); + goto get; + } + } + this = lookup_one_len(p->name, dir, p->len); + if (IS_ERR_OR_NULL(this) || !this->d_inode) { + if (IS_ERR(this)) { + err = PTR_ERR(this); + this = NULL; + goto fail; + } + goto out; + } + +get: + type = ovl_path_type(this); + if (OVL_TYPE_ORIGIN(type)) { + struct kstat stat; + struct path statpath = *path; + + statpath.dentry = this; + err = vfs_getattr(&statpath, &stat, STATX_INO, 0); + if (err) + goto fail; + + WARN_ON_ONCE(dir->d_sb->s_dev != stat.dev); + ino = stat.ino; + } + +out: + p->ino = ino; + dput(this); + return err; + +fail: + pr_warn_ratelimited("overlay: failed to look up (%s) for ino (%i)\n", + p->name, err); + goto out; +} + +static int ovl_fill_plain(struct dir_context *ctx, const char *name, + int namelen, loff_t offset, u64 ino, + unsigned int d_type) +{ + struct ovl_cache_entry *p; + struct ovl_readdir_data *rdd = + container_of(ctx, struct ovl_readdir_data, ctx); + + rdd->count++; + p = ovl_cache_entry_new(rdd, name, namelen, ino, d_type); + if (p == NULL) { + rdd->err = -ENOMEM; + return -ENOMEM; + } + list_add_tail(&p->l_node, rdd->list); + + return 0; +} + +static int ovl_dir_read_impure(struct path *path, struct list_head *list, + struct rb_root *root) +{ + int err; + struct path realpath; + struct ovl_cache_entry *p, *n; + struct ovl_readdir_data rdd = { + .ctx.actor = ovl_fill_plain, + .list = list, + .root = root, + }; + + INIT_LIST_HEAD(list); + *root = RB_ROOT; + ovl_path_upper(path->dentry, &realpath); + + err = ovl_dir_read(&realpath, &rdd); + if (err) + return err; + + list_for_each_entry_safe(p, n, list, l_node) { + if (strcmp(p->name, ".") != 0 && + strcmp(p->name, "..") != 0) { + err = ovl_cache_update_ino(path, p); + if (err) + return err; + } + if (p->ino == p->real_ino) { + list_del(&p->l_node); + kfree(p); + } else { + struct rb_node **newp = &root->rb_node; + struct rb_node *parent = NULL; + + if (WARN_ON(ovl_cache_entry_find_link(p->name, p->len, + &newp, &parent))) + return -EIO; + + rb_link_node(&p->node, parent, newp); + rb_insert_color(&p->node, root); + } + } + return 0; +} + +static struct ovl_dir_cache *ovl_cache_get_impure(struct path *path) +{ + int res; + struct dentry *dentry = path->dentry; + struct ovl_dir_cache *cache; + + cache = ovl_dir_cache(d_inode(dentry)); + if (cache && ovl_dentry_version_get(dentry) == cache->version) + return cache; + + /* Impure cache is not refcounted, free it here */ + ovl_dir_cache_free(d_inode(dentry)); + ovl_set_dir_cache(d_inode(dentry), NULL); + + cache = kzalloc(sizeof(struct ovl_dir_cache), GFP_KERNEL); + if (!cache) + return ERR_PTR(-ENOMEM); + + res = ovl_dir_read_impure(path, &cache->entries, &cache->root); + if (res) { + ovl_cache_free(&cache->entries); + kfree(cache); + return ERR_PTR(res); + } + if (list_empty(&cache->entries)) { + /* Good oportunity to get rid of an unnecessary "impure" flag */ + ovl_do_removexattr(ovl_dentry_upper(dentry), OVL_XATTR_IMPURE); + ovl_clear_flag(OVL_IMPURE, d_inode(dentry)); + kfree(cache); + return NULL; + } + + cache->version = ovl_dentry_version_get(dentry); + ovl_set_dir_cache(d_inode(dentry), cache); + + return cache; +} + +struct ovl_readdir_translate { + struct dir_context *orig_ctx; + struct ovl_dir_cache *cache; + struct dir_context ctx; + u64 parent_ino; +}; + +static int ovl_fill_real(struct dir_context *ctx, const char *name, + int namelen, loff_t offset, u64 ino, + unsigned int d_type) +{ + struct ovl_readdir_translate *rdt = + container_of(ctx, struct ovl_readdir_translate, ctx); + struct dir_context *orig_ctx = rdt->orig_ctx; + + if (rdt->parent_ino && strcmp(name, "..") == 0) + ino = rdt->parent_ino; + else if (rdt->cache) { + struct ovl_cache_entry *p; + + p = ovl_cache_entry_find(&rdt->cache->root, name, namelen); + if (p) + ino = p->ino; + } + + return orig_ctx->actor(orig_ctx, name, namelen, offset, ino, d_type); +} + +static int ovl_iterate_real(struct file *file, struct dir_context *ctx) +{ + int err; + struct ovl_dir_file *od = file->private_data; + struct dentry *dir = file->f_path.dentry; + struct ovl_readdir_translate rdt = { + .ctx.actor = ovl_fill_real, + .orig_ctx = ctx, + }; + + if (OVL_TYPE_MERGE(ovl_path_type(dir->d_parent))) { + struct kstat stat; + struct path statpath = file->f_path; + + statpath.dentry = dir->d_parent; + err = vfs_getattr(&statpath, &stat, STATX_INO, 0); + if (err) + return err; + + WARN_ON_ONCE(dir->d_sb->s_dev != stat.dev); + rdt.parent_ino = stat.ino; + } + + if (ovl_test_flag(OVL_IMPURE, d_inode(dir))) { + rdt.cache = ovl_cache_get_impure(&file->f_path); + if (IS_ERR(rdt.cache)) + return PTR_ERR(rdt.cache); + } + + return iterate_dir(od->realfile, &rdt.ctx); +} + + static int ovl_iterate(struct file *file, struct dir_context *ctx) { struct ovl_dir_file *od = file->private_data; struct dentry *dentry = file->f_path.dentry; struct ovl_cache_entry *p; + int err; if (!ctx->pos) ovl_dir_reset(file); - if (od->is_real) + if (od->is_real) { + /* + * If parent is merge, then need to adjust d_ino for '..', if + * dir is impure then need to adjust d_ino for copied up + * entries. + */ + if (ovl_same_sb(dentry->d_sb) && + (ovl_test_flag(OVL_IMPURE, d_inode(dentry)) || + OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent)))) { + return ovl_iterate_real(file, ctx); + } return iterate_dir(od->realfile, ctx); + } if (!od->cache) { struct ovl_dir_cache *cache; @@ -378,9 +686,15 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) while (od->cursor != &od->cache->entries) { p = list_entry(od->cursor, struct ovl_cache_entry, l_node); - if (!p->is_whiteout) + if (!p->is_whiteout) { + if (!p->ino) { + err = ovl_cache_update_ino(&file->f_path, p); + if (err) + return err; + } if (!dir_emit(ctx, p->name, p->len, p->ino, p->type)) break; + } od->cursor = p->l_node.next; ctx->pos++; } @@ -522,8 +836,9 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list) { int err; struct ovl_cache_entry *p; + struct rb_root root = RB_ROOT; - err = ovl_dir_read_merged(dentry, list); + err = ovl_dir_read_merged(dentry, list, &root); if (err) return err; @@ -612,12 +927,13 @@ static void ovl_workdir_cleanup_recurse(struct path *path, int level) int err; struct inode *dir = path->dentry->d_inode; LIST_HEAD(list); + struct rb_root root = RB_ROOT; struct ovl_cache_entry *p; struct ovl_readdir_data rdd = { .ctx.actor = ovl_fill_merge, .dentry = NULL, .list = &list, - .root = RB_ROOT, + .root = &root, .is_lowest = false, }; @@ -675,12 +991,13 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt, struct inode *dir = dentry->d_inode; struct path path = { .mnt = mnt, .dentry = dentry }; LIST_HEAD(list); + struct rb_root root = RB_ROOT; struct ovl_cache_entry *p; struct ovl_readdir_data rdd = { .ctx.actor = ovl_fill_merge, .dentry = NULL, .list = &list, - .root = RB_ROOT, + .root = &root, .is_lowest = false, }; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index d86e89f97201..cd49c0298ddf 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -70,20 +70,20 @@ static int ovl_check_append_only(struct inode *inode, int flag) static struct dentry *ovl_d_real(struct dentry *dentry, const struct inode *inode, - unsigned int open_flags) + unsigned int open_flags, unsigned int flags) { struct dentry *real; int err; + if (flags & D_REAL_UPPER) + return ovl_dentry_upper(dentry); + if (!d_is_reg(dentry)) { if (!inode || inode == d_inode(dentry)) return dentry; goto bug; } - if (d_is_negative(dentry)) - return dentry; - if (open_flags) { err = ovl_open_maybe_copy_up(dentry, open_flags); if (err) @@ -105,7 +105,7 @@ static struct dentry *ovl_d_real(struct dentry *dentry, goto bug; /* Handle recursion */ - real = d_real(real, inode, open_flags); + real = d_real(real, inode, open_flags, 0); if (!inode || inode == d_inode(real)) return real; @@ -198,6 +198,7 @@ static void ovl_destroy_inode(struct inode *inode) dput(oi->__upperdentry); kfree(oi->redirect); + ovl_dir_cache_free(inode); mutex_destroy(&oi->lock); call_rcu(&inode->i_rcu, ovl_i_callback); diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index f46ad75dc96a..117794582f9f 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -180,14 +180,14 @@ struct inode *ovl_inode_real(struct inode *inode) } -struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry) +struct ovl_dir_cache *ovl_dir_cache(struct inode *inode) { - return OVL_I(d_inode(dentry))->cache; + return OVL_I(inode)->cache; } -void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache) +void ovl_set_dir_cache(struct inode *inode, struct ovl_dir_cache *cache) { - OVL_I(d_inode(dentry))->cache = cache; + OVL_I(inode)->cache = cache; } bool ovl_dentry_is_opaque(struct dentry *dentry) @@ -275,12 +275,19 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry) } } -void ovl_dentry_version_inc(struct dentry *dentry) +void ovl_dentry_version_inc(struct dentry *dentry, bool impurity) { struct inode *inode = d_inode(dentry); WARN_ON(!inode_is_locked(inode)); - OVL_I(inode)->version++; + /* + * Version is used by readdir code to keep cache consistent. For merge + * dirs all changes need to be noted. For non-merge dirs, cache only + * contains impure (ones which have been copied up and have origins) + * entries, so only need to note changes to impure entries. + */ + if (OVL_TYPE_MERGE(ovl_path_type(dentry)) || impurity) + OVL_I(inode)->version++; } u64 ovl_dentry_version_get(struct dentry *dentry) @@ -382,6 +389,11 @@ void ovl_set_flag(unsigned long flag, struct inode *inode) set_bit(flag, &OVL_I(inode)->flags); } +void ovl_clear_flag(unsigned long flag, struct inode *inode) +{ + clear_bit(flag, &OVL_I(inode)->flags); +} + bool ovl_test_flag(unsigned long flag, struct inode *inode) { return test_bit(flag, &OVL_I(inode)->flags); diff --git a/fs/xattr.c b/fs/xattr.c index 7b03df6b8be2..4424f7fecf14 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -23,6 +23,7 @@ #include <linux/posix_acl_xattr.h> #include <linux/uaccess.h> +#include "internal.h" static const char * strcmp_prefix(const char *a, const char *a_prefix) @@ -502,10 +503,10 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name, if (!f.file) return error; audit_file(f.file); - error = mnt_want_write_file(f.file); + error = mnt_want_write_file_path(f.file); if (!error) { error = setxattr(f.file->f_path.dentry, name, value, size, flags); - mnt_drop_write_file(f.file); + mnt_drop_write_file_path(f.file); } fdput(f); return error; @@ -734,10 +735,10 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name) if (!f.file) return error; audit_file(f.file); - error = mnt_want_write_file(f.file); + error = mnt_want_write_file_path(f.file); if (!error) { error = removexattr(f.file->f_path.dentry, name); - mnt_drop_write_file(f.file); + mnt_drop_write_file_path(f.file); } fdput(f); return error; diff --git a/include/dt-bindings/clock/qcom,gcc-msm8996.h b/include/dt-bindings/clock/qcom,gcc-msm8996.h index 1f5c42254798..75b07cf5eed0 100644 --- a/include/dt-bindings/clock/qcom,gcc-msm8996.h +++ b/include/dt-bindings/clock/qcom,gcc-msm8996.h @@ -233,6 +233,8 @@ #define GCC_PCIE_CLKREF_CLK 216 #define GCC_RX2_USB2_CLKREF_CLK 217 #define GCC_RX1_USB2_CLKREF_CLK 218 +#define GCC_HLOS1_VOTE_LPASS_CORE_SMMU_CLK 219 +#define GCC_HLOS1_VOTE_LPASS_ADSP_SMMU_CLK 220 #define GCC_SYSTEM_NOC_BCR 0 #define GCC_CONFIG_NOC_BCR 1 diff --git a/include/dt-bindings/clock/r8a77995-cpg-mssr.h b/include/dt-bindings/clock/r8a77995-cpg-mssr.h new file mode 100644 index 000000000000..4e8ae3dee590 --- /dev/null +++ b/include/dt-bindings/clock/r8a77995-cpg-mssr.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 Glider bvba + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __DT_BINDINGS_CLOCK_R8A77995_CPG_MSSR_H__ +#define __DT_BINDINGS_CLOCK_R8A77995_CPG_MSSR_H__ + +#include <dt-bindings/clock/renesas-cpg-mssr.h> + +/* r8a77995 CPG Core Clocks */ +#define R8A77995_CLK_Z2 0 +#define R8A77995_CLK_ZG 1 +#define R8A77995_CLK_ZTR 2 +#define R8A77995_CLK_ZT 3 +#define R8A77995_CLK_ZX 4 +#define R8A77995_CLK_S0D1 5 +#define R8A77995_CLK_S1D1 6 +#define R8A77995_CLK_S1D2 7 +#define R8A77995_CLK_S1D4 8 +#define R8A77995_CLK_S2D1 9 +#define R8A77995_CLK_S2D2 10 +#define R8A77995_CLK_S2D4 11 +#define R8A77995_CLK_S3D1 12 +#define R8A77995_CLK_S3D2 13 +#define R8A77995_CLK_S3D4 14 +#define R8A77995_CLK_S1D4C 15 +#define R8A77995_CLK_S3D1C 16 +#define R8A77995_CLK_S3D2C 17 +#define R8A77995_CLK_S3D4C 18 +#define R8A77995_CLK_LB 19 +#define R8A77995_CLK_CL 20 +#define R8A77995_CLK_ZB3 21 +#define R8A77995_CLK_ZB3D2 22 +#define R8A77995_CLK_CR 23 +#define R8A77995_CLK_CRD2 24 +#define R8A77995_CLK_SD0H 25 +#define R8A77995_CLK_SD0 26 +#define R8A77995_CLK_SSP2 27 +#define R8A77995_CLK_SSP1 28 +#define R8A77995_CLK_RPC 29 +#define R8A77995_CLK_RPCD2 30 +#define R8A77995_CLK_ZA2 31 +#define R8A77995_CLK_ZA8 32 +#define R8A77995_CLK_Z2D 33 +#define R8A77995_CLK_CANFD 34 +#define R8A77995_CLK_MSO 35 +#define R8A77995_CLK_R 36 +#define R8A77995_CLK_OSC 37 +#define R8A77995_CLK_LV0 38 +#define R8A77995_CLK_LV1 39 +#define R8A77995_CLK_CP 40 + +#endif /* __DT_BINDINGS_CLOCK_R8A77995_CPG_MSSR_H__ */ diff --git a/include/dt-bindings/clock/rk3228-cru.h b/include/dt-bindings/clock/rk3228-cru.h index 56f841c22801..55655ab0a4c4 100644 --- a/include/dt-bindings/clock/rk3228-cru.h +++ b/include/dt-bindings/clock/rk3228-cru.h @@ -49,6 +49,7 @@ #define SCLK_EMMC_DRV 117 #define SCLK_SDMMC_SAMPLE 118 #define SCLK_SDIO_SAMPLE 119 +#define SCLK_SDIO_SRC 120 #define SCLK_EMMC_SAMPLE 121 #define SCLK_VOP 122 #define SCLK_HDMI_HDCP 123 diff --git a/include/dt-bindings/clock/rv1108-cru.h b/include/dt-bindings/clock/rv1108-cru.h index f269d833e41a..d8d0e0456dc2 100644 --- a/include/dt-bindings/clock/rv1108-cru.h +++ b/include/dt-bindings/clock/rv1108-cru.h @@ -67,9 +67,9 @@ #define SCLK_SPI 108 #define SCLK_SARADC 109 #define SCLK_TSADC 110 -#define SCLK_MACPHY_PRE 111 -#define SCLK_MACPHY 112 -#define SCLK_MACPHY_RX 113 +#define SCLK_MAC_PRE 111 +#define SCLK_MAC 112 +#define SCLK_MAC_RX 113 #define SCLK_MAC_REF 114 #define SCLK_MAC_REFOUT 115 #define SCLK_DSP_PFM 116 @@ -110,6 +110,7 @@ #define ACLK_CIF2 207 #define ACLK_CIF3 208 #define ACLK_PERI 209 +#define ACLK_GMAC 210 /* pclk gates */ #define PCLK_GPIO1 256 @@ -141,6 +142,7 @@ #define PCLK_EFUSE0 282 #define PCLK_EFUSE1 283 #define PCLK_WDT 284 +#define PCLK_GMAC 285 /* hclk gates */ #define HCLK_I2S0_8CH 320 diff --git a/include/dt-bindings/clock/stm32h7-clks.h b/include/dt-bindings/clock/stm32h7-clks.h new file mode 100644 index 000000000000..6637272b3242 --- /dev/null +++ b/include/dt-bindings/clock/stm32h7-clks.h @@ -0,0 +1,165 @@ +/* SYS, CORE AND BUS CLOCKS */ +#define SYS_D1CPRE 0 +#define HCLK 1 +#define PCLK1 2 +#define PCLK2 3 +#define PCLK3 4 +#define PCLK4 5 +#define HSI_DIV 6 +#define HSE_1M 7 +#define I2S_CKIN 8 +#define CK_DSI_PHY 9 +#define HSE_CK 10 +#define LSE_CK 11 +#define CSI_KER_DIV122 12 +#define RTC_CK 13 +#define CPU_SYSTICK 14 + +/* OSCILLATOR BANK */ +#define OSC_BANK 18 +#define HSI_CK 18 +#define HSI_KER_CK 19 +#define CSI_CK 20 +#define CSI_KER_CK 21 +#define RC48_CK 22 +#define LSI_CK 23 + +/* MCLOCK BANK */ +#define MCLK_BANK 28 +#define PER_CK 28 +#define PLLSRC 29 +#define SYS_CK 30 +#define TRACEIN_CK 31 + +/* ODF BANK */ +#define ODF_BANK 32 +#define PLL1_P 32 +#define PLL1_Q 33 +#define PLL1_R 34 +#define PLL2_P 35 +#define PLL2_Q 36 +#define PLL2_R 37 +#define PLL3_P 38 +#define PLL3_Q 39 +#define PLL3_R 40 + +/* MCO BANK */ +#define MCO_BANK 41 +#define MCO1 41 +#define MCO2 42 + +/* PERIF BANK */ +#define PERIF_BANK 50 +#define D1SRAM1_CK 50 +#define ITCM_CK 51 +#define DTCM2_CK 52 +#define DTCM1_CK 53 +#define FLITF_CK 54 +#define JPGDEC_CK 55 +#define DMA2D_CK 56 +#define MDMA_CK 57 +#define USB2ULPI_CK 58 +#define USB1ULPI_CK 59 +#define ETH1RX_CK 60 +#define ETH1TX_CK 61 +#define ETH1MAC_CK 62 +#define ART_CK 63 +#define DMA2_CK 64 +#define DMA1_CK 65 +#define D2SRAM3_CK 66 +#define D2SRAM2_CK 67 +#define D2SRAM1_CK 68 +#define HASH_CK 69 +#define CRYPT_CK 70 +#define CAMITF_CK 71 +#define BKPRAM_CK 72 +#define HSEM_CK 73 +#define BDMA_CK 74 +#define CRC_CK 75 +#define GPIOK_CK 76 +#define GPIOJ_CK 77 +#define GPIOI_CK 78 +#define GPIOH_CK 79 +#define GPIOG_CK 80 +#define GPIOF_CK 81 +#define GPIOE_CK 82 +#define GPIOD_CK 83 +#define GPIOC_CK 84 +#define GPIOB_CK 85 +#define GPIOA_CK 86 +#define WWDG1_CK 87 +#define DAC12_CK 88 +#define WWDG2_CK 89 +#define TIM14_CK 90 +#define TIM13_CK 91 +#define TIM12_CK 92 +#define TIM7_CK 93 +#define TIM6_CK 94 +#define TIM5_CK 95 +#define TIM4_CK 96 +#define TIM3_CK 97 +#define TIM2_CK 98 +#define MDIOS_CK 99 +#define OPAMP_CK 100 +#define CRS_CK 101 +#define TIM17_CK 102 +#define TIM16_CK 103 +#define TIM15_CK 104 +#define TIM8_CK 105 +#define TIM1_CK 106 +#define TMPSENS_CK 107 +#define RTCAPB_CK 108 +#define VREF_CK 109 +#define COMP12_CK 110 +#define SYSCFG_CK 111 + +/* KERNEL BANK */ +#define KERN_BANK 120 +#define SDMMC1_CK 120 +#define QUADSPI_CK 121 +#define FMC_CK 122 +#define USB2OTG_CK 123 +#define USB1OTG_CK 124 +#define ADC12_CK 125 +#define SDMMC2_CK 126 +#define RNG_CK 127 +#define ADC3_CK 128 +#define DSI_CK 129 +#define LTDC_CK 130 +#define USART8_CK 131 +#define USART7_CK 132 +#define HDMICEC_CK 133 +#define I2C3_CK 134 +#define I2C2_CK 135 +#define I2C1_CK 136 +#define UART5_CK 137 +#define UART4_CK 138 +#define USART3_CK 139 +#define USART2_CK 140 +#define SPDIFRX_CK 141 +#define SPI3_CK 142 +#define SPI2_CK 143 +#define LPTIM1_CK 144 +#define FDCAN_CK 145 +#define SWP_CK 146 +#define HRTIM_CK 147 +#define DFSDM1_CK 148 +#define SAI3_CK 149 +#define SAI2_CK 150 +#define SAI1_CK 151 +#define SPI5_CK 152 +#define SPI4_CK 153 +#define SPI1_CK 154 +#define USART6_CK 155 +#define USART1_CK 156 +#define SAI4B_CK 157 +#define SAI4A_CK 158 +#define LPTIM5_CK 159 +#define LPTIM4_CK 160 +#define LPTIM3_CK 161 +#define LPTIM2_CK 162 +#define I2C4_CK 163 +#define SPI6_CK 164 +#define LPUART1_CK 165 + +#define STM32H7_MAX_CLKS 166 diff --git a/include/dt-bindings/clock/sun4i-a10-ccu.h b/include/dt-bindings/clock/sun4i-a10-ccu.h new file mode 100644 index 000000000000..c5a53f38d654 --- /dev/null +++ b/include/dt-bindings/clock/sun4i-a10-ccu.h @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2017 Priit Laes <plaes@plaes.org> + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DT_BINDINGS_CLK_SUN4I_A10_H_ +#define _DT_BINDINGS_CLK_SUN4I_A10_H_ + +#define CLK_HOSC 1 +#define CLK_CPU 20 + +/* AHB Gates */ +#define CLK_AHB_OTG 26 +#define CLK_AHB_EHCI0 27 +#define CLK_AHB_OHCI0 28 +#define CLK_AHB_EHCI1 29 +#define CLK_AHB_OHCI1 30 +#define CLK_AHB_SS 31 +#define CLK_AHB_DMA 32 +#define CLK_AHB_BIST 33 +#define CLK_AHB_MMC0 34 +#define CLK_AHB_MMC1 35 +#define CLK_AHB_MMC2 36 +#define CLK_AHB_MMC3 37 +#define CLK_AHB_MS 38 +#define CLK_AHB_NAND 39 +#define CLK_AHB_SDRAM 40 +#define CLK_AHB_ACE 41 +#define CLK_AHB_EMAC 42 +#define CLK_AHB_TS 43 +#define CLK_AHB_SPI0 44 +#define CLK_AHB_SPI1 45 +#define CLK_AHB_SPI2 46 +#define CLK_AHB_SPI3 47 +#define CLK_AHB_PATA 48 +#define CLK_AHB_SATA 49 +#define CLK_AHB_GPS 50 +#define CLK_AHB_HSTIMER 51 +#define CLK_AHB_VE 52 +#define CLK_AHB_TVD 53 +#define CLK_AHB_TVE0 54 +#define CLK_AHB_TVE1 55 +#define CLK_AHB_LCD0 56 +#define CLK_AHB_LCD1 57 +#define CLK_AHB_CSI0 58 +#define CLK_AHB_CSI1 59 +#define CLK_AHB_HDMI0 60 +#define CLK_AHB_HDMI1 61 +#define CLK_AHB_DE_BE0 62 +#define CLK_AHB_DE_BE1 63 +#define CLK_AHB_DE_FE0 64 +#define CLK_AHB_DE_FE1 65 +#define CLK_AHB_GMAC 66 +#define CLK_AHB_MP 67 +#define CLK_AHB_GPU 68 + +/* APB0 Gates */ +#define CLK_APB0_CODEC 69 +#define CLK_APB0_SPDIF 70 +#define CLK_APB0_I2S0 71 +#define CLK_APB0_AC97 72 +#define CLK_APB0_I2S1 73 +#define CLK_APB0_PIO 74 +#define CLK_APB0_IR0 75 +#define CLK_APB0_IR1 76 +#define CLK_APB0_I2S2 77 +#define CLK_APB0_KEYPAD 78 + +/* APB1 Gates */ +#define CLK_APB1_I2C0 79 +#define CLK_APB1_I2C1 80 +#define CLK_APB1_I2C2 81 +#define CLK_APB1_I2C3 82 +#define CLK_APB1_CAN 83 +#define CLK_APB1_SCR 84 +#define CLK_APB1_PS20 85 +#define CLK_APB1_PS21 86 +#define CLK_APB1_I2C4 87 +#define CLK_APB1_UART0 88 +#define CLK_APB1_UART1 89 +#define CLK_APB1_UART2 90 +#define CLK_APB1_UART3 91 +#define CLK_APB1_UART4 92 +#define CLK_APB1_UART5 93 +#define CLK_APB1_UART6 94 +#define CLK_APB1_UART7 95 + +/* IP clocks */ +#define CLK_NAND 96 +#define CLK_MS 97 +#define CLK_MMC0 98 +#define CLK_MMC0_OUTPUT 99 +#define CLK_MMC0_SAMPLE 100 +#define CLK_MMC1 101 +#define CLK_MMC1_OUTPUT 102 +#define CLK_MMC1_SAMPLE 103 +#define CLK_MMC2 104 +#define CLK_MMC2_OUTPUT 105 +#define CLK_MMC2_SAMPLE 106 +#define CLK_MMC3 107 +#define CLK_MMC3_OUTPUT 108 +#define CLK_MMC3_SAMPLE 109 +#define CLK_TS 110 +#define CLK_SS 111 +#define CLK_SPI0 112 +#define CLK_SPI1 113 +#define CLK_SPI2 114 +#define CLK_PATA 115 +#define CLK_IR0 116 +#define CLK_IR1 117 +#define CLK_I2S0 118 +#define CLK_AC97 119 +#define CLK_SPDIF 120 +#define CLK_KEYPAD 121 +#define CLK_SATA 122 +#define CLK_USB_OHCI0 123 +#define CLK_USB_OHCI1 124 +#define CLK_USB_PHY 125 +#define CLK_GPS 126 +#define CLK_SPI3 127 +#define CLK_I2S1 128 +#define CLK_I2S2 129 + +/* DRAM Gates */ +#define CLK_DRAM_VE 130 +#define CLK_DRAM_CSI0 131 +#define CLK_DRAM_CSI1 132 +#define CLK_DRAM_TS 133 +#define CLK_DRAM_TVD 134 +#define CLK_DRAM_TVE0 135 +#define CLK_DRAM_TVE1 136 +#define CLK_DRAM_OUT 137 +#define CLK_DRAM_DE_FE1 138 +#define CLK_DRAM_DE_FE0 139 +#define CLK_DRAM_DE_BE0 140 +#define CLK_DRAM_DE_BE1 141 +#define CLK_DRAM_MP 142 +#define CLK_DRAM_ACE 143 + +/* Display Engine Clocks */ +#define CLK_DE_BE0 144 +#define CLK_DE_BE1 145 +#define CLK_DE_FE0 146 +#define CLK_DE_FE1 147 +#define CLK_DE_MP 148 +#define CLK_TCON0_CH0 149 +#define CLK_TCON1_CH0 150 +#define CLK_CSI_SCLK 151 +#define CLK_TVD_SCLK2 152 +#define CLK_TVD 153 +#define CLK_TCON0_CH1_SCLK2 154 +#define CLK_TCON0_CH1 155 +#define CLK_TCON1_CH1_SCLK2 156 +#define CLK_TCON1_CH1 157 +#define CLK_CSI0 158 +#define CLK_CSI1 159 +#define CLK_CODEC 160 +#define CLK_VE 161 +#define CLK_AVS 162 +#define CLK_ACE 163 +#define CLK_HDMI 164 +#define CLK_GPU 165 + +#endif /* _DT_BINDINGS_CLK_SUN4I_A10_H_ */ diff --git a/include/dt-bindings/clock/sun7i-a20-ccu.h b/include/dt-bindings/clock/sun7i-a20-ccu.h new file mode 100644 index 000000000000..045a5178da0c --- /dev/null +++ b/include/dt-bindings/clock/sun7i-a20-ccu.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 Priit Laes <plaes@plaes.org> + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DT_BINDINGS_CLK_SUN7I_A20_H_ +#define _DT_BINDINGS_CLK_SUN7I_A20_H_ + +#include <dt-bindings/clock/sun4i-a10-ccu.h> + +#define CLK_MBUS 166 +#define CLK_HDMI1_SLOW 167 +#define CLK_HDMI1 168 +#define CLK_OUT_A 169 +#define CLK_OUT_B 170 + +#endif /* _DT_BINDINGS_CLK_SUN7I_A20_H_ */ diff --git a/include/dt-bindings/clock/sun8i-r40-ccu.h b/include/dt-bindings/clock/sun8i-r40-ccu.h new file mode 100644 index 000000000000..4fa5f69fc297 --- /dev/null +++ b/include/dt-bindings/clock/sun8i-r40-ccu.h @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io> + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DT_BINDINGS_CLK_SUN8I_R40_H_ +#define _DT_BINDINGS_CLK_SUN8I_R40_H_ + +#define CLK_CPU 24 + +#define CLK_BUS_MIPI_DSI 29 +#define CLK_BUS_CE 30 +#define CLK_BUS_DMA 31 +#define CLK_BUS_MMC0 32 +#define CLK_BUS_MMC1 33 +#define CLK_BUS_MMC2 34 +#define CLK_BUS_MMC3 35 +#define CLK_BUS_NAND 36 +#define CLK_BUS_DRAM 37 +#define CLK_BUS_EMAC 38 +#define CLK_BUS_TS 39 +#define CLK_BUS_HSTIMER 40 +#define CLK_BUS_SPI0 41 +#define CLK_BUS_SPI1 42 +#define CLK_BUS_SPI2 43 +#define CLK_BUS_SPI3 44 +#define CLK_BUS_SATA 45 +#define CLK_BUS_OTG 46 +#define CLK_BUS_EHCI0 47 +#define CLK_BUS_EHCI1 48 +#define CLK_BUS_EHCI2 49 +#define CLK_BUS_OHCI0 50 +#define CLK_BUS_OHCI1 51 +#define CLK_BUS_OHCI2 52 +#define CLK_BUS_VE 53 +#define CLK_BUS_MP 54 +#define CLK_BUS_DEINTERLACE 55 +#define CLK_BUS_CSI0 56 +#define CLK_BUS_CSI1 57 +#define CLK_BUS_HDMI1 58 +#define CLK_BUS_HDMI0 59 +#define CLK_BUS_DE 60 +#define CLK_BUS_TVE0 61 +#define CLK_BUS_TVE1 62 +#define CLK_BUS_TVE_TOP 63 +#define CLK_BUS_GMAC 64 +#define CLK_BUS_GPU 65 +#define CLK_BUS_TVD0 66 +#define CLK_BUS_TVD1 67 +#define CLK_BUS_TVD2 68 +#define CLK_BUS_TVD3 69 +#define CLK_BUS_TVD_TOP 70 +#define CLK_BUS_TCON_LCD0 71 +#define CLK_BUS_TCON_LCD1 72 +#define CLK_BUS_TCON_TV0 73 +#define CLK_BUS_TCON_TV1 74 +#define CLK_BUS_TCON_TOP 75 +#define CLK_BUS_CODEC 76 +#define CLK_BUS_SPDIF 77 +#define CLK_BUS_AC97 78 +#define CLK_BUS_PIO 79 +#define CLK_BUS_IR0 80 +#define CLK_BUS_IR1 81 +#define CLK_BUS_THS 82 +#define CLK_BUS_KEYPAD 83 +#define CLK_BUS_I2S0 84 +#define CLK_BUS_I2S1 85 +#define CLK_BUS_I2S2 86 +#define CLK_BUS_I2C0 87 +#define CLK_BUS_I2C1 88 +#define CLK_BUS_I2C2 89 +#define CLK_BUS_I2C3 90 +#define CLK_BUS_CAN 91 +#define CLK_BUS_SCR 92 +#define CLK_BUS_PS20 93 +#define CLK_BUS_PS21 94 +#define CLK_BUS_I2C4 95 +#define CLK_BUS_UART0 96 +#define CLK_BUS_UART1 97 +#define CLK_BUS_UART2 98 +#define CLK_BUS_UART3 99 +#define CLK_BUS_UART4 100 +#define CLK_BUS_UART5 101 +#define CLK_BUS_UART6 102 +#define CLK_BUS_UART7 103 +#define CLK_BUS_DBG 104 + +#define CLK_THS 105 +#define CLK_NAND 106 +#define CLK_MMC0 107 +#define CLK_MMC1 108 +#define CLK_MMC2 109 +#define CLK_MMC3 110 +#define CLK_TS 111 +#define CLK_CE 112 +#define CLK_SPI0 113 +#define CLK_SPI1 114 +#define CLK_SPI2 115 +#define CLK_SPI3 116 +#define CLK_I2S0 117 +#define CLK_I2S1 118 +#define CLK_I2S2 119 +#define CLK_AC97 120 +#define CLK_SPDIF 121 +#define CLK_KEYPAD 122 +#define CLK_SATA 123 +#define CLK_USB_PHY0 124 +#define CLK_USB_PHY1 125 +#define CLK_USB_PHY2 126 +#define CLK_USB_OHCI0 127 +#define CLK_USB_OHCI1 128 +#define CLK_USB_OHCI2 129 +#define CLK_IR0 130 +#define CLK_IR1 131 + +#define CLK_DRAM_VE 133 +#define CLK_DRAM_CSI0 134 +#define CLK_DRAM_CSI1 135 +#define CLK_DRAM_TS 136 +#define CLK_DRAM_TVD 137 +#define CLK_DRAM_MP 138 +#define CLK_DRAM_DEINTERLACE 139 +#define CLK_DE 140 +#define CLK_MP 141 +#define CLK_TCON_LCD0 142 +#define CLK_TCON_LCD1 143 +#define CLK_TCON_TV0 144 +#define CLK_TCON_TV1 145 +#define CLK_DEINTERLACE 146 +#define CLK_CSI1_MCLK 147 +#define CLK_CSI_SCLK 148 +#define CLK_CSI0_MCLK 149 +#define CLK_VE 150 +#define CLK_CODEC 151 +#define CLK_AVS 152 +#define CLK_HDMI 153 +#define CLK_HDMI_SLOW 154 + +#define CLK_DSI_DPHY 156 +#define CLK_TVE0 157 +#define CLK_TVE1 158 +#define CLK_TVD0 159 +#define CLK_TVD1 160 +#define CLK_TVD2 161 +#define CLK_TVD3 162 +#define CLK_GPU 163 +#define CLK_OUTA 164 +#define CLK_OUTB 165 + +#endif /* _DT_BINDINGS_CLK_SUN8I_R40_H_ */ diff --git a/include/dt-bindings/mfd/stm32h7-rcc.h b/include/dt-bindings/mfd/stm32h7-rcc.h new file mode 100644 index 000000000000..461a8e04453a --- /dev/null +++ b/include/dt-bindings/mfd/stm32h7-rcc.h @@ -0,0 +1,136 @@ +/* + * This header provides constants for the STM32H7 RCC IP + */ + +#ifndef _DT_BINDINGS_MFD_STM32H7_RCC_H +#define _DT_BINDINGS_MFD_STM32H7_RCC_H + +/* AHB3 */ +#define STM32H7_RCC_AHB3_MDMA 0 +#define STM32H7_RCC_AHB3_DMA2D 4 +#define STM32H7_RCC_AHB3_JPGDEC 5 +#define STM32H7_RCC_AHB3_FMC 12 +#define STM32H7_RCC_AHB3_QUADSPI 14 +#define STM32H7_RCC_AHB3_SDMMC1 16 +#define STM32H7_RCC_AHB3_CPU 31 + +#define STM32H7_AHB3_RESET(bit) (STM32H7_RCC_AHB3_##bit + (0x7C * 8)) + +/* AHB1 */ +#define STM32H7_RCC_AHB1_DMA1 0 +#define STM32H7_RCC_AHB1_DMA2 1 +#define STM32H7_RCC_AHB1_ADC12 5 +#define STM32H7_RCC_AHB1_ART 14 +#define STM32H7_RCC_AHB1_ETH1MAC 15 +#define STM32H7_RCC_AHB1_USB1OTG 25 +#define STM32H7_RCC_AHB1_USB2OTG 27 + +#define STM32H7_AHB1_RESET(bit) (STM32H7_RCC_AHB1_##bit + (0x80 * 8)) + +/* AHB2 */ +#define STM32H7_RCC_AHB2_CAMITF 0 +#define STM32H7_RCC_AHB2_CRYPT 4 +#define STM32H7_RCC_AHB2_HASH 5 +#define STM32H7_RCC_AHB2_RNG 6 +#define STM32H7_RCC_AHB2_SDMMC2 9 + +#define STM32H7_AHB2_RESET(bit) (STM32H7_RCC_AHB2_##bit + (0x84 * 8)) + +/* AHB4 */ +#define STM32H7_RCC_AHB4_GPIOA 0 +#define STM32H7_RCC_AHB4_GPIOB 1 +#define STM32H7_RCC_AHB4_GPIOC 2 +#define STM32H7_RCC_AHB4_GPIOD 3 +#define STM32H7_RCC_AHB4_GPIOE 4 +#define STM32H7_RCC_AHB4_GPIOF 5 +#define STM32H7_RCC_AHB4_GPIOG 6 +#define STM32H7_RCC_AHB4_GPIOH 7 +#define STM32H7_RCC_AHB4_GPIOI 8 +#define STM32H7_RCC_AHB4_GPIOJ 9 +#define STM32H7_RCC_AHB4_GPIOK 10 +#define STM32H7_RCC_AHB4_CRC 19 +#define STM32H7_RCC_AHB4_BDMA 21 +#define STM32H7_RCC_AHB4_ADC3 24 +#define STM32H7_RCC_AHB4_HSEM 25 + +#define STM32H7_AHB4_RESET(bit) (STM32H7_RCC_AHB4_##bit + (0x88 * 8)) + +/* APB3 */ +#define STM32H7_RCC_APB3_LTDC 3 +#define STM32H7_RCC_APB3_DSI 4 + +#define STM32H7_APB3_RESET(bit) (STM32H7_RCC_APB3_##bit + (0x8C * 8)) + +/* APB1L */ +#define STM32H7_RCC_APB1L_TIM2 0 +#define STM32H7_RCC_APB1L_TIM3 1 +#define STM32H7_RCC_APB1L_TIM4 2 +#define STM32H7_RCC_APB1L_TIM5 3 +#define STM32H7_RCC_APB1L_TIM6 4 +#define STM32H7_RCC_APB1L_TIM7 5 +#define STM32H7_RCC_APB1L_TIM12 6 +#define STM32H7_RCC_APB1L_TIM13 7 +#define STM32H7_RCC_APB1L_TIM14 8 +#define STM32H7_RCC_APB1L_LPTIM1 9 +#define STM32H7_RCC_APB1L_SPI2 14 +#define STM32H7_RCC_APB1L_SPI3 15 +#define STM32H7_RCC_APB1L_SPDIF_RX 16 +#define STM32H7_RCC_APB1L_USART2 17 +#define STM32H7_RCC_APB1L_USART3 18 +#define STM32H7_RCC_APB1L_UART4 19 +#define STM32H7_RCC_APB1L_UART5 20 +#define STM32H7_RCC_APB1L_I2C1 21 +#define STM32H7_RCC_APB1L_I2C2 22 +#define STM32H7_RCC_APB1L_I2C3 23 +#define STM32H7_RCC_APB1L_HDMICEC 27 +#define STM32H7_RCC_APB1L_DAC12 29 +#define STM32H7_RCC_APB1L_USART7 30 +#define STM32H7_RCC_APB1L_USART8 31 + +#define STM32H7_APB1L_RESET(bit) (STM32H7_RCC_APB1L_##bit + (0x90 * 8)) + +/* APB1H */ +#define STM32H7_RCC_APB1H_CRS 1 +#define STM32H7_RCC_APB1H_SWP 2 +#define STM32H7_RCC_APB1H_OPAMP 4 +#define STM32H7_RCC_APB1H_MDIOS 5 +#define STM32H7_RCC_APB1H_FDCAN 8 + +#define STM32H7_APB1H_RESET(bit) (STM32H7_RCC_APB1H_##bit + (0x94 * 8)) + +/* APB2 */ +#define STM32H7_RCC_APB2_TIM1 0 +#define STM32H7_RCC_APB2_TIM8 1 +#define STM32H7_RCC_APB2_USART1 4 +#define STM32H7_RCC_APB2_USART6 5 +#define STM32H7_RCC_APB2_SPI1 12 +#define STM32H7_RCC_APB2_SPI4 13 +#define STM32H7_RCC_APB2_TIM15 16 +#define STM32H7_RCC_APB2_TIM16 17 +#define STM32H7_RCC_APB2_TIM17 18 +#define STM32H7_RCC_APB2_SPI5 20 +#define STM32H7_RCC_APB2_SAI1 22 +#define STM32H7_RCC_APB2_SAI2 23 +#define STM32H7_RCC_APB2_SAI3 24 +#define STM32H7_RCC_APB2_DFSDM1 28 +#define STM32H7_RCC_APB2_HRTIM 29 + +#define STM32H7_APB2_RESET(bit) (STM32H7_RCC_APB2_##bit + (0x98 * 8)) + +/* APB4 */ +#define STM32H7_RCC_APB4_SYSCFG 1 +#define STM32H7_RCC_APB4_LPUART1 3 +#define STM32H7_RCC_APB4_SPI6 5 +#define STM32H7_RCC_APB4_I2C4 7 +#define STM32H7_RCC_APB4_LPTIM2 9 +#define STM32H7_RCC_APB4_LPTIM3 10 +#define STM32H7_RCC_APB4_LPTIM4 11 +#define STM32H7_RCC_APB4_LPTIM5 12 +#define STM32H7_RCC_APB4_COMP12 14 +#define STM32H7_RCC_APB4_VREF 15 +#define STM32H7_RCC_APB4_SAI4 21 +#define STM32H7_RCC_APB4_TMPSENS 26 + +#define STM32H7_APB4_RESET(bit) (STM32H7_RCC_APB4_##bit + (0x9C * 8)) + +#endif /* _DT_BINDINGS_MFD_STM32H7_RCC_H */ diff --git a/include/dt-bindings/reset/sun4i-a10-ccu.h b/include/dt-bindings/reset/sun4i-a10-ccu.h new file mode 100644 index 000000000000..5f4480bedc8a --- /dev/null +++ b/include/dt-bindings/reset/sun4i-a10-ccu.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017 Priit Laes <plaes@plaes.org> + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DT_BINDINGS_RST_SUN4I_A10_H +#define _DT_BINDINGS_RST_SUN4I_A10_H + +#define RST_USB_PHY0 1 +#define RST_USB_PHY1 2 +#define RST_USB_PHY2 3 +#define RST_GPS 4 +#define RST_DE_BE0 5 +#define RST_DE_BE1 6 +#define RST_DE_FE0 7 +#define RST_DE_FE1 8 +#define RST_DE_MP 9 +#define RST_TVE0 10 +#define RST_TCON0 11 +#define RST_TVE1 12 +#define RST_TCON1 13 +#define RST_CSI0 14 +#define RST_CSI1 15 +#define RST_VE 16 +#define RST_ACE 17 +#define RST_LVDS 18 +#define RST_GPU 19 +#define RST_HDMI_H 20 +#define RST_HDMI_SYS 21 +#define RST_HDMI_AUDIO_DMA 22 + +#endif /* DT_BINDINGS_RST_SUN4I_A10_H */ diff --git a/include/dt-bindings/reset/sun8i-r40-ccu.h b/include/dt-bindings/reset/sun8i-r40-ccu.h new file mode 100644 index 000000000000..c5ebcf6672e4 --- /dev/null +++ b/include/dt-bindings/reset/sun8i-r40-ccu.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io> + * + * This file is dual-licensed: you can use it either under the terms + * of the GPL or the X11 license, at your option. Note that this dual + * licensing only applies to this file, and not this project as a + * whole. + * + * a) This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Or, alternatively, + * + * b) Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DT_BINDINGS_RST_SUN8I_R40_H_ +#define _DT_BINDINGS_RST_SUN8I_R40_H_ + +#define RST_USB_PHY0 0 +#define RST_USB_PHY1 1 +#define RST_USB_PHY2 2 + +#define RST_DRAM 3 +#define RST_MBUS 4 + +#define RST_BUS_MIPI_DSI 5 +#define RST_BUS_CE 6 +#define RST_BUS_DMA 7 +#define RST_BUS_MMC0 8 +#define RST_BUS_MMC1 9 +#define RST_BUS_MMC2 10 +#define RST_BUS_MMC3 11 +#define RST_BUS_NAND 12 +#define RST_BUS_DRAM 13 +#define RST_BUS_EMAC 14 +#define RST_BUS_TS 15 +#define RST_BUS_HSTIMER 16 +#define RST_BUS_SPI0 17 +#define RST_BUS_SPI1 18 +#define RST_BUS_SPI2 19 +#define RST_BUS_SPI3 20 +#define RST_BUS_SATA 21 +#define RST_BUS_OTG 22 +#define RST_BUS_EHCI0 23 +#define RST_BUS_EHCI1 24 +#define RST_BUS_EHCI2 25 +#define RST_BUS_OHCI0 26 +#define RST_BUS_OHCI1 27 +#define RST_BUS_OHCI2 28 +#define RST_BUS_VE 29 +#define RST_BUS_MP 30 +#define RST_BUS_DEINTERLACE 31 +#define RST_BUS_CSI0 32 +#define RST_BUS_CSI1 33 +#define RST_BUS_HDMI0 34 +#define RST_BUS_HDMI1 35 +#define RST_BUS_DE 36 +#define RST_BUS_TVE0 37 +#define RST_BUS_TVE1 38 +#define RST_BUS_TVE_TOP 39 +#define RST_BUS_GMAC 40 +#define RST_BUS_GPU 41 +#define RST_BUS_TVD0 42 +#define RST_BUS_TVD1 43 +#define RST_BUS_TVD2 44 +#define RST_BUS_TVD3 45 +#define RST_BUS_TVD_TOP 46 +#define RST_BUS_TCON_LCD0 47 +#define RST_BUS_TCON_LCD1 48 +#define RST_BUS_TCON_TV0 49 +#define RST_BUS_TCON_TV1 50 +#define RST_BUS_TCON_TOP 51 +#define RST_BUS_DBG 52 +#define RST_BUS_LVDS 53 +#define RST_BUS_CODEC 54 +#define RST_BUS_SPDIF 55 +#define RST_BUS_AC97 56 +#define RST_BUS_IR0 57 +#define RST_BUS_IR1 58 +#define RST_BUS_THS 59 +#define RST_BUS_KEYPAD 60 +#define RST_BUS_I2S0 61 +#define RST_BUS_I2S1 62 +#define RST_BUS_I2S2 63 +#define RST_BUS_I2C0 64 +#define RST_BUS_I2C1 65 +#define RST_BUS_I2C2 66 +#define RST_BUS_I2C3 67 +#define RST_BUS_CAN 68 +#define RST_BUS_SCR 69 +#define RST_BUS_PS20 70 +#define RST_BUS_PS21 71 +#define RST_BUS_I2C4 72 +#define RST_BUS_UART0 73 +#define RST_BUS_UART1 74 +#define RST_BUS_UART2 75 +#define RST_BUS_UART3 76 +#define RST_BUS_UART4 77 +#define RST_BUS_UART5 78 +#define RST_BUS_UART6 79 +#define RST_BUS_UART7 80 + +#endif /* _DT_BINDINGS_RST_SUN8I_R40_H_ */ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index c59c62571e4f..5100ec1b5d55 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -343,6 +343,7 @@ struct clk_hw *clk_hw_register_gate(struct device *dev, const char *name, u8 clk_gate_flags, spinlock_t *lock); void clk_unregister_gate(struct clk *clk); void clk_hw_unregister_gate(struct clk_hw *hw); +int clk_gate_is_enabled(struct clk_hw *hw); struct clk_div_table { unsigned int val; @@ -565,6 +566,9 @@ struct clk_fractional_divider { u8 nwidth; u32 nmask; u8 flags; + void (*approximation)(struct clk_hw *hw, + unsigned long rate, unsigned long *parent_rate, + unsigned long *m, unsigned long *n); spinlock_t *lock; }; diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h index 17f413bbbedf..6aca5ce8a99a 100644 --- a/include/linux/clk/at91_pmc.h +++ b/include/linux/clk/at91_pmc.h @@ -185,4 +185,29 @@ #define AT91_PMC_PCR_EN (0x1 << 28) /* Enable */ #define AT91_PMC_PCR_GCKEN (0x1 << 29) /* GCK Enable */ +#define AT91_PMC_AUDIO_PLL0 0x14c +#define AT91_PMC_AUDIO_PLL_PLLEN (1 << 0) +#define AT91_PMC_AUDIO_PLL_PADEN (1 << 1) +#define AT91_PMC_AUDIO_PLL_PMCEN (1 << 2) +#define AT91_PMC_AUDIO_PLL_RESETN (1 << 3) +#define AT91_PMC_AUDIO_PLL_ND_OFFSET 8 +#define AT91_PMC_AUDIO_PLL_ND_MASK (0x7f << AT91_PMC_AUDIO_PLL_ND_OFFSET) +#define AT91_PMC_AUDIO_PLL_ND(n) ((n) << AT91_PMC_AUDIO_PLL_ND_OFFSET) +#define AT91_PMC_AUDIO_PLL_QDPMC_OFFSET 16 +#define AT91_PMC_AUDIO_PLL_QDPMC_MASK (0x7f << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET) +#define AT91_PMC_AUDIO_PLL_QDPMC(n) ((n) << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET) + +#define AT91_PMC_AUDIO_PLL1 0x150 +#define AT91_PMC_AUDIO_PLL_FRACR_MASK 0x3fffff +#define AT91_PMC_AUDIO_PLL_QDPAD_OFFSET 24 +#define AT91_PMC_AUDIO_PLL_QDPAD_MASK (0x7f << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET) +#define AT91_PMC_AUDIO_PLL_QDPAD(n) ((n) << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET) +#define AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET AT91_PMC_AUDIO_PLL_QDPAD_OFFSET +#define AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK (0x3 << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET) +#define AT91_PMC_AUDIO_PLL_QDPAD_DIV(n) ((n) << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET) +#define AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET 26 +#define AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX 0x1f +#define AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK (AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET) +#define AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(n) ((n) << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET) + #endif diff --git a/include/linux/dcache.h b/include/linux/dcache.h index aae1cdb76851..ed1a7cf6923a 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -147,7 +147,7 @@ struct dentry_operations { struct vfsmount *(*d_automount)(struct path *); int (*d_manage)(const struct path *, bool); struct dentry *(*d_real)(struct dentry *, const struct inode *, - unsigned int); + unsigned int, unsigned int); } ____cacheline_aligned; /* @@ -562,11 +562,15 @@ static inline struct dentry *d_backing_dentry(struct dentry *upper) return upper; } +/* d_real() flags */ +#define D_REAL_UPPER 0x2 /* return upper dentry or NULL if non-upper */ + /** * d_real - Return the real dentry * @dentry: the dentry to query * @inode: inode to select the dentry from multiple layers (can be NULL) - * @flags: open flags to control copy-up behavior + * @open_flags: open flags to control copy-up behavior + * @flags: flags to control what is returned by this function * * If dentry is on a union/overlay, then return the underlying, real dentry. * Otherwise return the dentry itself. @@ -575,10 +579,10 @@ static inline struct dentry *d_backing_dentry(struct dentry *upper) */ static inline struct dentry *d_real(struct dentry *dentry, const struct inode *inode, - unsigned int flags) + unsigned int open_flags, unsigned int flags) { if (unlikely(dentry->d_flags & DCACHE_OP_REAL)) - return dentry->d_op->d_real(dentry, inode, flags); + return dentry->d_op->d_real(dentry, inode, open_flags, flags); else return dentry; } @@ -593,7 +597,7 @@ static inline struct dentry *d_real(struct dentry *dentry, static inline struct inode *d_real_inode(const struct dentry *dentry) { /* This usage of d_real() results in const dentry */ - return d_backing_inode(d_real((struct dentry *) dentry, NULL, 0)); + return d_backing_inode(d_real((struct dentry *) dentry, NULL, 0, 0)); } struct name_snapshot { diff --git a/include/linux/fs.h b/include/linux/fs.h index 2d0e6748e46e..33d8e45cd874 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1235,7 +1235,7 @@ static inline struct inode *file_inode(const struct file *f) static inline struct dentry *file_dentry(const struct file *file) { - return d_real(file->f_path.dentry, file_inode(file), 0); + return d_real(file->f_path.dentry, file_inode(file), 0, 0); } static inline int locks_lock_file_wait(struct file *filp, struct file_lock *fl) diff --git a/include/linux/module.h b/include/linux/module.h index e7bdd549e527..fe5aa3736707 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -209,7 +209,7 @@ extern void cleanup_module(void); #ifdef MODULE /* Creates an alias so file2alias.c can find device table. */ #define MODULE_DEVICE_TABLE(type, name) \ -extern const typeof(name) __mod_##type##__##name##_device_table \ +extern typeof(name) __mod_##type##__##name##_device_table \ __attribute__ ((unused, alias(__stringify(name)))) #else /* !MODULE */ #define MODULE_DEVICE_TABLE(type, name) diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 5144f9103723..87723c86f136 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -226,7 +226,9 @@ struct nvme_id_ctrl { __le16 mntmt; __le16 mxtmt; __le32 sanicap; - __u8 rsvd332[180]; + __le32 hmminds; + __le16 hmmaxd; + __u8 rsvd338[174]; __u8 sqes; __u8 cqes; __le16 maxcmd; diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 0a0f0d14a5fb..e6d0f9c1cafd 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -72,8 +72,6 @@ extern struct class *rtc_class; * issued through ioctl() ... */ struct rtc_class_ops { - int (*open)(struct device *); - void (*release)(struct device *); int (*ioctl)(struct device *, unsigned int, unsigned long); int (*read_time)(struct device *, struct rtc_time *); int (*set_time)(struct device *, struct rtc_time *); diff --git a/include/linux/string.h b/include/linux/string.h index e1eeb0a8a969..54d21783e18d 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -434,20 +434,9 @@ __FORTIFY_INLINE char *strcpy(char *p, const char *q) * @count: The number of bytes to copy * @pad: Character to use for padding if space is left in destination. */ -__FORTIFY_INLINE void memcpy_and_pad(void *dest, size_t dest_len, - const void *src, size_t count, int pad) +static inline void memcpy_and_pad(void *dest, size_t dest_len, + const void *src, size_t count, int pad) { - size_t dest_size = __builtin_object_size(dest, 0); - size_t src_size = __builtin_object_size(src, 0); - - if (__builtin_constant_p(dest_len) && __builtin_constant_p(count)) { - if (dest_size < dest_len && dest_size < count) - __write_overflow(); - else if (src_size < dest_len && src_size < count) - __read_overflow3(); - } - if (dest_size < dest_len) - fortify_panic(__func__); if (dest_len > count) { memcpy(dest, src, count); memset(dest + count, pad, dest_len - count); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index afb04811b7b9..0a804b1a4726 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -691,12 +691,6 @@ struct Scsi_Host { unsigned int prot_capabilities; unsigned char prot_guard_type; - /* - * q used for scsi_tgt msgs, async events or any other requests that - * need to be processed in userspace - */ - struct request_queue *uspace_req_q; - /* legacy crap */ unsigned long base; unsigned long io_port; diff --git a/include/trace/events/block.h b/include/trace/events/block.h index f815aaaef755..1fd7ff1a46f7 100644 --- a/include/trace/events/block.h +++ b/include/trace/events/block.h @@ -397,7 +397,6 @@ DECLARE_EVENT_CLASS(block_get_rq, TP_fast_assign( __entry->dev = bio ? bio_dev(bio) : 0; - __entry->dev = bio_dev(bio); __entry->sector = bio ? bio->bi_iter.bi_sector : 0; __entry->nr_sector = bio ? bio_sectors(bio) : 0; blk_fill_rwbs(__entry->rwbs, @@ -414,7 +413,7 @@ DECLARE_EVENT_CLASS(block_get_rq, /** * block_getrq - get a free request entry in queue for block IO operations * @q: queue for operations - * @bio: pending block IO operation + * @bio: pending block IO operation (can be %NULL) * @rw: low bit indicates a read (%0) or a write (%1) * * A request struct for queue @q has been allocated to handle the @@ -430,7 +429,7 @@ DEFINE_EVENT(block_get_rq, block_getrq, /** * block_sleeprq - waiting to get a free request entry in queue for block IO operation * @q: queue for operation - * @bio: pending block IO operation + * @bio: pending block IO operation (can be %NULL) * @rw: low bit indicates a read (%0) or a write (%1) * * In the case where a request struct cannot be provided for queue @q diff --git a/kernel/module.c b/kernel/module.c index 40f983cbea81..de66ec825992 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -2707,21 +2707,21 @@ static void add_kallsyms(struct module *mod, const struct load_info *info) } #endif /* CONFIG_KALLSYMS */ -static void dynamic_debug_setup(struct _ddebug *debug, unsigned int num) +static void dynamic_debug_setup(struct module *mod, struct _ddebug *debug, unsigned int num) { if (!debug) return; #ifdef CONFIG_DYNAMIC_DEBUG - if (ddebug_add_module(debug, num, debug->modname)) + if (ddebug_add_module(debug, num, mod->name)) pr_err("dynamic debug error adding module: %s\n", debug->modname); #endif } -static void dynamic_debug_remove(struct _ddebug *debug) +static void dynamic_debug_remove(struct module *mod, struct _ddebug *debug) { if (debug) - ddebug_remove_module(debug->modname); + ddebug_remove_module(mod->name); } void * __weak module_alloc(unsigned long size) @@ -3715,7 +3715,7 @@ static int load_module(struct load_info *info, const char __user *uargs, goto free_arch_cleanup; } - dynamic_debug_setup(info->debug, info->num_debug); + dynamic_debug_setup(mod, info->debug, info->num_debug); /* Ftrace init must be called in the MODULE_STATE_UNFORMED state */ ftrace_module_init(mod); @@ -3779,7 +3779,7 @@ static int load_module(struct load_info *info, const char __user *uargs, module_disable_nx(mod); ddebug_cleanup: - dynamic_debug_remove(info->debug); + dynamic_debug_remove(mod, info->debug); synchronize_sched(); kfree(mod->args); free_arch_cleanup: diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 136a76d80dbf..18a6966567da 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -1173,6 +1173,10 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu) WARN_ON_ONCE(debug_locks && !(lockdep_is_held(&p->pi_lock) || lockdep_is_held(&task_rq(p)->lock))); #endif + /* + * Clearly, migrating tasks to offline CPUs is a fairly daft thing. + */ + WARN_ON_ONCE(!cpu_online(new_cpu)); #endif trace_sched_migrate_task(p, new_cpu); diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index 8e536d963652..01217fb5a5de 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -181,11 +181,16 @@ static const struct file_operations sched_feat_fops = { .release = single_release, }; +__read_mostly bool sched_debug_enabled; + static __init int sched_init_debug(void) { debugfs_create_file("sched_features", 0644, NULL, NULL, &sched_feat_fops); + debugfs_create_bool("sched_debug", 0644, NULL, + &sched_debug_enabled); + return 0; } late_initcall(sched_init_debug); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 0a85641e62ce..70ba32e08a23 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -8437,6 +8437,12 @@ static int idle_balance(struct rq *this_rq, struct rq_flags *rf) this_rq->idle_stamp = rq_clock(this_rq); /* + * Do not pull tasks towards !active CPUs... + */ + if (!cpu_active(this_cpu)) + return 0; + + /* * This is OK, because current is on_cpu, which avoids it being picked * for load-balance and preemption/IRQs are still disabled avoiding * further scheduler activity on it and we're being very careful to @@ -8543,6 +8549,13 @@ static int active_load_balance_cpu_stop(void *data) struct rq_flags rf; rq_lock_irq(busiest_rq, &rf); + /* + * Between queueing the stop-work and running it is a hole in which + * CPUs can become inactive. We should not move tasks from or to + * inactive CPUs. + */ + if (!cpu_active(busiest_cpu) || !cpu_active(target_cpu)) + goto out_unlock; /* make sure the requested cpu hasn't gone down in the meantime */ if (unlikely(busiest_cpu != smp_processor_id() || diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 746ac78ff492..14db76cd496f 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -1951,6 +1951,8 @@ extern struct sched_entity *__pick_first_entity(struct cfs_rq *cfs_rq); extern struct sched_entity *__pick_last_entity(struct cfs_rq *cfs_rq); #ifdef CONFIG_SCHED_DEBUG +extern bool sched_debug_enabled; + extern void print_cfs_stats(struct seq_file *m, int cpu); extern void print_rt_stats(struct seq_file *m, int cpu); extern void print_dl_stats(struct seq_file *m, int cpu); diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 5d0062cc10cb..f1cf4f306a82 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -14,11 +14,9 @@ cpumask_var_t sched_domains_tmpmask2; #ifdef CONFIG_SCHED_DEBUG -static __read_mostly int sched_debug_enabled; - static int __init sched_debug_setup(char *str) { - sched_debug_enabled = 1; + sched_debug_enabled = true; return 0; } diff --git a/mm/backing-dev.c b/mm/backing-dev.c index f028a9a472fd..e19606bb41a0 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -569,8 +569,10 @@ static int cgwb_create(struct backing_dev_info *bdi, /* need to create a new one */ wb = kmalloc(sizeof(*wb), gfp); - if (!wb) - return -ENOMEM; + if (!wb) { + ret = -ENOMEM; + goto out_put; + } ret = wb_init(wb, bdi, blkcg_css->id, gfp); if (ret) diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index b920d186ad4a..98314b400a95 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -47,6 +47,12 @@ enum export { export_unused_gpl, export_gpl_future, export_unknown }; +/* In kernel, this size is defined in linux/module.h; + * here we use Elf_Addr instead of long for covering cross-compile + */ + +#define MODULE_NAME_LEN (64 - sizeof(Elf_Addr)) + #define PRINTF __attribute__ ((format (printf, 1, 2))) PRINTF void fatal(const char *fmt, ...) @@ -2111,6 +2117,23 @@ static void check_exports(struct module *mod) } } +static int check_modname_len(struct module *mod) +{ + const char *mod_name; + + mod_name = strrchr(mod->name, '/'); + if (mod_name == NULL) + mod_name = mod->name; + else + mod_name++; + if (strlen(mod_name) >= MODULE_NAME_LEN) { + merror("module name is too long [%s.ko]\n", mod->name); + return 1; + } + + return 0; +} + /** * Header for the generated file **/ @@ -2150,11 +2173,6 @@ static void add_staging_flag(struct buffer *b, const char *name) buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); } -/* In kernel, this size is defined in linux/module.h; - * here we use Elf_Addr instead of long for covering cross-compile - */ -#define MODULE_NAME_LEN (64 - sizeof(Elf_Addr)) - /** * Record CRCs for unresolved symbols **/ @@ -2485,6 +2503,7 @@ int main(int argc, char **argv) buf.pos = 0; + err |= check_modname_len(mod); add_header(&buf, mod); add_intree_flag(&buf, !external_module); add_staging_flag(&buf, mod->name); diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index 677756ae34c9..067459760a7b 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -40,7 +40,6 @@ my $virtualenv = 1; # my %texlive = ( - 'adjustbox.sty' => 'texlive-adjustbox', 'amsfonts.sty' => 'texlive-amsfonts', 'amsmath.sty' => 'texlive-amsmath', 'amssymb.sty' => 'texlive-amsfonts', diff --git a/sound/core/device.c b/sound/core/device.c index 8918838b1999..cb0e46f66cc9 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -128,7 +128,7 @@ void snd_device_disconnect(struct snd_card *card, void *device_data) if (dev) __snd_device_disconnect(dev); else - dev_dbg(card->dev, "device disconnect %p (from %pF), not found\n", + dev_dbg(card->dev, "device disconnect %p (from %pS), not found\n", device_data, __builtin_return_address(0)); } EXPORT_SYMBOL_GPL(snd_device_disconnect); @@ -152,7 +152,7 @@ void snd_device_free(struct snd_card *card, void *device_data) if (dev) __snd_device_free(dev); else - dev_dbg(card->dev, "device free %p (from %pF), not found\n", + dev_dbg(card->dev, "device free %p (from %pS), not found\n", device_data, __builtin_return_address(0)); } EXPORT_SYMBOL(snd_device_free); diff --git a/sound/core/seq_device.c b/sound/core/seq_device.c index c4acf17e9f5e..e40a2cba5002 100644 --- a/sound/core/seq_device.c +++ b/sound/core/seq_device.c @@ -148,8 +148,10 @@ void snd_seq_device_load_drivers(void) flush_work(&autoload_work); } EXPORT_SYMBOL(snd_seq_device_load_drivers); +#define cancel_autoload_drivers() cancel_work_sync(&autoload_work) #else #define queue_autoload_drivers() /* NOP */ +#define cancel_autoload_drivers() /* NOP */ #endif /* @@ -159,6 +161,7 @@ static int snd_seq_device_dev_free(struct snd_device *device) { struct snd_seq_device *dev = device->device_data; + cancel_autoload_drivers(); put_device(&dev->dev); return 0; } diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c index dc5541c8b359..73e7a5e527fc 100644 --- a/sound/firewire/motu/motu-stream.c +++ b/sound/firewire/motu/motu-stream.c @@ -253,24 +253,21 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate) if (err < 0) { dev_err(&motu->unit->device, "fail to start isochronous comm: %d\n", err); - stop_both_streams(motu); - return err; + goto stop_streams; } err = start_isoc_ctx(motu, &motu->rx_stream); if (err < 0) { dev_err(&motu->unit->device, "fail to start IT context: %d\n", err); - stop_both_streams(motu); - return err; + goto stop_streams; } err = protocol->switch_fetching_mode(motu, true); if (err < 0) { dev_err(&motu->unit->device, "fail to enable frame fetching: %d\n", err); - stop_both_streams(motu); - return err; + goto stop_streams; } } @@ -281,12 +278,15 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate) dev_err(&motu->unit->device, "fail to start IR context: %d", err); amdtp_stream_stop(&motu->rx_stream); - stop_both_streams(motu); - return err; + goto stop_streams; } } return 0; + +stop_streams: + stop_both_streams(motu); + return err; } void snd_motu_stream_stop_duplex(struct snd_motu *motu) diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 976a3d23557e..70d023a85bf5 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -558,12 +558,10 @@ static void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream) struct snd_card_asihpi_pcm *dpcm; struct snd_card_asihpi *card; - BUG_ON(!substream); - dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data; card = snd_pcm_substream_chip(substream); - BUG_ON(in_interrupt()); + WARN_ON(in_interrupt()); tasklet_disable(&card->t); card->llmode_streampriv = dpcm; tasklet_enable(&card->t); @@ -578,8 +576,6 @@ static void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream) struct snd_card_asihpi_pcm *dpcm; struct snd_card_asihpi *card; - BUG_ON(!substream); - dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data; card = snd_pcm_substream_chip(substream); diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 97ac80af4447..8f20dec97843 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2622,22 +2622,18 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, err = request_firmware(&chip->assp_kernel_image, "ess/maestro3_assp_kernel.fw", &pci->dev); - if (err < 0) { - snd_m3_free(chip); - return err; - } + if (err < 0) + goto free_chip; err = request_firmware(&chip->assp_minisrc_image, "ess/maestro3_assp_minisrc.fw", &pci->dev); - if (err < 0) { - snd_m3_free(chip); - return err; - } + if (err < 0) + goto free_chip; + + err = pci_request_regions(pci, card->driver); + if (err < 0) + goto free_chip; - if ((err = pci_request_regions(pci, card->driver)) < 0) { - snd_m3_free(chip); - return err; - } chip->iobase = pci_resource_start(pci, 0); /* just to be sure */ @@ -2655,8 +2651,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, if (request_irq(pci->irq, snd_m3_interrupt, IRQF_SHARED, KBUILD_MODNAME, chip)) { dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); - snd_m3_free(chip); - return -ENOMEM; + err = -ENOMEM; + goto free_chip; } chip->irq = pci->irq; @@ -2666,10 +2662,9 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, dev_warn(card->dev, "can't allocate apm buffer\n"); #endif - if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { - snd_m3_free(chip); - return err; - } + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) + goto free_chip; if ((err = snd_m3_mixer(chip)) < 0) return err; @@ -2699,6 +2694,10 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, *chip_ret = chip; return 0; + +free_chip: + snd_m3_free(chip); + return err; } /* @@ -2741,23 +2740,19 @@ snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) break; } - if ((err = snd_m3_create(card, pci, - external_amp[dev], - amp_gpio[dev], - &chip)) < 0) { - snd_card_free(card); - return err; - } + err = snd_m3_create(card, pci, external_amp[dev], amp_gpio[dev], &chip); + if (err < 0) + goto free_card; + card->private_data = chip; sprintf(card->shortname, "ESS %s PCI", card->driver); sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, chip->iobase, chip->irq); - if ((err = snd_card_register(card)) < 0) { - snd_card_free(card); - return err; - } + err = snd_card_register(card); + if (err < 0) + goto free_card; #if 0 /* TODO: not supported yet */ /* TODO enable MIDI IRQ and I/O */ @@ -2772,6 +2767,10 @@ snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) pci_set_drvdata(pci, card); dev++; return 0; + +free_card: + snd_card_free(card); + return err; } static void snd_m3_remove(struct pci_dev *pci) diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 0ff41f9ab434..9f0f73875f01 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -793,11 +793,8 @@ static int hdsp_get_iobox_version (struct hdsp *hdsp) hdsp_write(hdsp, HDSP_control2Reg, HDSP_S200 | HDSP_PROGRAM); hdsp_write (hdsp, HDSP_fifoData, 0); - if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0) { - hdsp->io_type = Multiface; - dev_info(hdsp->card->dev, "Multiface found\n"); - return 0; - } + if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0) + goto set_multi; hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); hdsp_write(hdsp, HDSP_fifoData, 0); @@ -810,20 +807,14 @@ static int hdsp_get_iobox_version (struct hdsp *hdsp) hdsp_write(hdsp, HDSP_control2Reg, HDSP_S300); hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); hdsp_write(hdsp, HDSP_fifoData, 0); - if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) == 0) { - hdsp->io_type = Multiface; - dev_info(hdsp->card->dev, "Multiface found\n"); - return 0; - } + if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) == 0) + goto set_multi; hdsp_write(hdsp, HDSP_control2Reg, HDSP_S300); hdsp_write(hdsp, HDSP_control2Reg, HDSP_S_LOAD); hdsp_write(hdsp, HDSP_fifoData, 0); - if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0) { - hdsp->io_type = Multiface; - dev_info(hdsp->card->dev, "Multiface found\n"); - return 0; - } + if (hdsp_fifo_wait(hdsp, 0, HDSP_SHORT_WAIT) < 0) + goto set_multi; hdsp->io_type = RPM; dev_info(hdsp->card->dev, "RPM found\n"); @@ -838,6 +829,11 @@ static int hdsp_get_iobox_version (struct hdsp *hdsp) hdsp->io_type = Digiface; } return 0; + +set_multi: + hdsp->io_type = Multiface; + dev_info(hdsp->card->dev, "Multiface found\n"); + return 0; } diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 25284d8d9758..f20d42714e4d 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6949,10 +6949,8 @@ static int snd_hdspm_probe(struct pci_dev *pci, hdspm->pci = pci; err = snd_hdspm_create(card, hdspm); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto free_card; if (hdspm->io_type != MADIface) { snprintf(card->shortname, sizeof(card->shortname), "%s_%x", @@ -6970,15 +6968,17 @@ static int snd_hdspm_probe(struct pci_dev *pci, } err = snd_card_register(card); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto free_card; pci_set_drvdata(pci, card); dev++; return 0; + +free_card: + snd_card_free(card); + return err; } static void snd_hdspm_remove(struct pci_dev *pci) diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 4faf3e1ed06a..eafdee384059 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -268,10 +268,9 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci, if ((err = snd_ymfpci_create(card, pci, old_legacy_ctrl, &chip)) < 0) { - snd_card_free(card); release_and_free_resource(mpu_res); release_and_free_resource(fm_res); - return err; + goto free_card; } chip->fm_res = fm_res; chip->mpu_res = mpu_res; @@ -283,35 +282,31 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci, card->shortname, chip->reg_area_phys, chip->irq); - if ((err = snd_ymfpci_pcm(chip, 0)) < 0) { - snd_card_free(card); - return err; - } - if ((err = snd_ymfpci_pcm_spdif(chip, 1)) < 0) { - snd_card_free(card); - return err; - } + err = snd_ymfpci_pcm(chip, 0); + if (err < 0) + goto free_card; + + err = snd_ymfpci_pcm_spdif(chip, 1); + if (err < 0) + goto free_card; + err = snd_ymfpci_mixer(chip, rear_switch[dev]); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto free_card; + if (chip->ac97->ext_id & AC97_EI_SDAC) { err = snd_ymfpci_pcm_4ch(chip, 2); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto free_card; + err = snd_ymfpci_pcm2(chip, 3); - if (err < 0) { - snd_card_free(card); - return err; - } - } - if ((err = snd_ymfpci_timer(chip, 0)) < 0) { - snd_card_free(card); - return err; + if (err < 0) + goto free_card; } + err = snd_ymfpci_timer(chip, 0); + if (err < 0) + goto free_card; + if (chip->mpu_res) { if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI, mpu_port[dev], @@ -336,21 +331,24 @@ static int snd_card_ymfpci_probe(struct pci_dev *pci, legacy_ctrl &= ~YMFPCI_LEGACY_FMEN; pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl); } else if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { - snd_card_free(card); dev_err(card->dev, "cannot create opl3 hwdep\n"); - return err; + goto free_card; } } snd_ymfpci_create_gameport(chip, dev, legacy_ctrl, legacy_ctrl2); - if ((err = snd_card_register(card)) < 0) { - snd_card_free(card); - return err; - } + err = snd_card_register(card); + if (err < 0) + goto free_card; + pci_set_drvdata(pci, card); dev++; return 0; + +free_card: + snd_card_free(card); + return err; } static void snd_card_ymfpci_remove(struct pci_dev *pci) diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index edfd58248082..8ca2e41e5827 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -2399,59 +2399,60 @@ int snd_ymfpci_create(struct snd_card *card, dev_err(chip->card->dev, "unable to grab memory region 0x%lx-0x%lx\n", chip->reg_area_phys, chip->reg_area_phys + 0x8000 - 1); - snd_ymfpci_free(chip); - return -EBUSY; + err = -EBUSY; + goto free_chip; } if (request_irq(pci->irq, snd_ymfpci_interrupt, IRQF_SHARED, KBUILD_MODNAME, chip)) { dev_err(chip->card->dev, "unable to grab IRQ %d\n", pci->irq); - snd_ymfpci_free(chip); - return -EBUSY; + err = -EBUSY; + goto free_chip; } chip->irq = pci->irq; snd_ymfpci_aclink_reset(pci); if (snd_ymfpci_codec_ready(chip, 0) < 0) { - snd_ymfpci_free(chip); - return -EIO; + err = -EIO; + goto free_chip; } err = snd_ymfpci_request_firmware(chip); if (err < 0) { dev_err(chip->card->dev, "firmware request failed: %d\n", err); - snd_ymfpci_free(chip); - return err; + goto free_chip; } snd_ymfpci_download_image(chip); udelay(100); /* seems we need a delay after downloading image.. */ if (snd_ymfpci_memalloc(chip) < 0) { - snd_ymfpci_free(chip); - return -EIO; + err = -EIO; + goto free_chip; } - if ((err = snd_ymfpci_ac3_init(chip)) < 0) { - snd_ymfpci_free(chip); - return err; - } + err = snd_ymfpci_ac3_init(chip); + if (err < 0) + goto free_chip; #ifdef CONFIG_PM_SLEEP chip->saved_regs = kmalloc(YDSXGR_NUM_SAVED_REGS * sizeof(u32), GFP_KERNEL); if (chip->saved_regs == NULL) { - snd_ymfpci_free(chip); - return -ENOMEM; + err = -ENOMEM; + goto free_chip; } #endif - if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { - snd_ymfpci_free(chip); - return err; - } + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) + goto free_chip; snd_ymfpci_proc_init(card, chip); *rchip = chip; return 0; + +free_chip: + snd_ymfpci_free(chip); + return err; } diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c index 0cd7caaed9c4..8445edd06737 100644 --- a/sound/soc/atmel/atmel-classd.c +++ b/sound/soc/atmel/atmel-classd.c @@ -32,7 +32,6 @@ struct atmel_classd { struct regmap *regmap; struct clk *pclk; struct clk *gclk; - struct clk *aclk; int irq; const struct atmel_classd_pdata *pdata; }; @@ -330,11 +329,6 @@ static int atmel_classd_codec_dai_startup(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); - int ret; - - ret = clk_prepare_enable(dd->aclk); - if (ret) - return ret; return clk_prepare_enable(dd->gclk); } @@ -357,31 +351,31 @@ static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai, return 0; } -#define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8) -#define CLASSD_ACLK_RATE_12M288_MPY_8 (12288 * 1000 * 8) +#define CLASSD_GCLK_RATE_11M2896_MPY_8 (112896 * 100 * 8) +#define CLASSD_GCLK_RATE_12M288_MPY_8 (12288 * 1000 * 8) static struct { int rate; int sample_rate; int dsp_clk; - unsigned long aclk_rate; + unsigned long gclk_rate; } const sample_rates[] = { { 8000, CLASSD_INTPMR_FRAME_8K, - CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 }, { 16000, CLASSD_INTPMR_FRAME_16K, - CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 }, { 32000, CLASSD_INTPMR_FRAME_32K, - CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 }, { 48000, CLASSD_INTPMR_FRAME_48K, - CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 }, { 96000, CLASSD_INTPMR_FRAME_96K, - CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 }, + CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 }, { 22050, CLASSD_INTPMR_FRAME_22K, - CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 }, + CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 }, { 44100, CLASSD_INTPMR_FRAME_44K, - CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 }, + CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 }, { 88200, CLASSD_INTPMR_FRAME_88K, - CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 }, + CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 }, }; static int @@ -410,13 +404,12 @@ atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream, } dev_dbg(codec->dev, - "Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz\n", - sample_rates[best].rate, sample_rates[best].aclk_rate); + "Selected SAMPLE_RATE of %dHz, GCLK_RATE of %ldHz\n", + sample_rates[best].rate, sample_rates[best].gclk_rate); clk_disable_unprepare(dd->gclk); - clk_disable_unprepare(dd->aclk); - ret = clk_set_rate(dd->aclk, sample_rates[best].aclk_rate); + ret = clk_set_rate(dd->gclk, sample_rates[best].gclk_rate); if (ret) return ret; @@ -426,10 +419,6 @@ atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream, snd_soc_update_bits(codec, CLASSD_INTPMR, mask, val); - ret = clk_prepare_enable(dd->aclk); - if (ret) - return ret; - return clk_prepare_enable(dd->gclk); } @@ -441,7 +430,6 @@ atmel_classd_codec_dai_shutdown(struct snd_pcm_substream *substream, struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card); clk_disable_unprepare(dd->gclk); - clk_disable_unprepare(dd->aclk); } static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream, @@ -596,13 +584,6 @@ static int atmel_classd_probe(struct platform_device *pdev) return ret; } - dd->aclk = devm_clk_get(dev, "aclk"); - if (IS_ERR(dd->aclk)) { - ret = PTR_ERR(dd->aclk); - dev_err(dev, "failed to get audio clock: %d\n", ret); - return ret; - } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); io_base = devm_ioremap_resource(dev, res); if (IS_ERR(io_base)) { diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index bc2a24f7a791..c7641cb50616 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -143,37 +143,32 @@ static int usb6fire_chip_probe(struct usb_interface *intf, chip->card = card; ret = usb6fire_comm_init(chip); - if (ret < 0) { - usb6fire_chip_destroy(chip); - return ret; - } + if (ret < 0) + goto destroy_chip; ret = usb6fire_midi_init(chip); - if (ret < 0) { - usb6fire_chip_destroy(chip); - return ret; - } + if (ret < 0) + goto destroy_chip; ret = usb6fire_pcm_init(chip); - if (ret < 0) { - usb6fire_chip_destroy(chip); - return ret; - } + if (ret < 0) + goto destroy_chip; ret = usb6fire_control_init(chip); - if (ret < 0) { - usb6fire_chip_destroy(chip); - return ret; - } + if (ret < 0) + goto destroy_chip; ret = snd_card_register(card); if (ret < 0) { dev_err(&intf->dev, "cannot register card."); - usb6fire_chip_destroy(chip); - return ret; + goto destroy_chip; } usb_set_intfdata(intf, chip); return 0; + +destroy_chip: + usb6fire_chip_destroy(chip); + return ret; } static void usb6fire_chip_disconnect(struct usb_interface *intf) diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c index b49d6e953d52..159da1f3924e 100644 --- a/sound/usb/usx2y/us122l.c +++ b/sound/usb/usx2y/us122l.c @@ -508,8 +508,7 @@ static bool us122l_create_card(struct snd_card *card) err = us122l_create_usbmidi(card); if (err < 0) { snd_printk(KERN_ERR "us122l_create_usbmidi error %i \n", err); - us122l_stop(us122l); - return false; + goto stop; } err = usb_stream_hwdep_new(card); if (err < 0) { @@ -518,10 +517,13 @@ static bool us122l_create_card(struct snd_card *card) list_for_each(p, &us122l->midi_list) snd_usbmidi_disconnect(p); - us122l_stop(us122l); - return false; + goto stop; } return true; + +stop: + us122l_stop(us122l); + return false; } static void snd_us122l_free(struct snd_card *card) diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c index fe926cb9192e..4dab49080700 100644 --- a/sound/usb/usx2y/usb_stream.c +++ b/sound/usb/usx2y/usb_stream.c @@ -352,20 +352,22 @@ static int submit_urbs(struct usb_stream_kernel *sk, int err; prepare_inurb(sk->idle_outurb->number_of_packets, sk->idle_inurb); err = usb_submit_urb(sk->idle_inurb, GFP_ATOMIC); - if (err < 0) { - snd_printk(KERN_ERR "%i\n", err); - return err; - } + if (err < 0) + goto report_failure; + sk->idle_inurb = sk->completed_inurb; sk->completed_inurb = inurb; err = usb_submit_urb(sk->idle_outurb, GFP_ATOMIC); - if (err < 0) { - snd_printk(KERN_ERR "%i\n", err); - return err; - } + if (err < 0) + goto report_failure; + sk->idle_outurb = sk->completed_outurb; sk->completed_outurb = outurb; return 0; + +report_failure: + snd_printk(KERN_ERR "%i\n", err); + return err; } #ifdef DEBUG_LOOP_BACK |