Commit f66477a0 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'clk-for-linus-20151104' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux

Pull clk updates from Stephen Boyd:
 "The majority of the changes are driver updates and new device support.
  The core framework is mostly unchanged this time around, with only a
  couple patches to expose a clk provider API and make getting clk
  parent names from DT more robust.

  Driver updates:

   - Support for clock controllers found on Broadcom Northstar SoCs and
     bcm2835 SoC

   - Support for Allwinner audio clocks

   - A few cleanup patches for Tegra drivers and support for the highest
     DFLL frequencies on Tegra124

   - Samsung exynos7 fixes and improvements

   - i.Mx SoC updates to add a few missing clocks and keep debug uart
     clocks on during kernel intialization

   - Some mediatek cleanups and support for more subsystem clocks

   - Support for msm8916 gpu/audio clocks and qcom's GDSC power domain
     controllers

   - A new driver for the Silabs si514 clock chip"

* tag 'clk-for-linus-20151104' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (143 commits)
  clk: qcom: msm8960: Fix dsi1/2 halt bits
  clk: lpc18xx-cgu: fix potential system hang when disabling unused clocks
  clk: lpc18xx-ccu: fix potential system hang when disabling unused clocks
  clk: Add clk_hw_is_enabled() for use by clk providers
  clk: Add stubs for of_clk_*() APIs when CONFIG_OF=n
  clk: versatile-icst: fix memory leak
  clk: Remove clk_{register,unregister}_multiplier()
  clk: iproc: define Broadcom NS2 iProc clock binding
  clk: iproc: define Broadcom NSP iProc clock binding
  clk: ns2: add clock support for Broadcom Northstar 2 SoC
  clk: iproc: Separate status and control variables
  clk: iproc: Split off dig_filter
  clk: iproc: Add PLL base write function
  clk: nsp: add clock support for Broadcom Northstar Plus SoC
  clk: iproc: Add PWRCTRL support
  clk: cygnus: Convert all macros to all caps
  ARM: cygnus: fix link failures when CONFIG_COMMON_CLK_IPROC is disabled
  clk: imx31: add missing of_node_put
  clk: imx27: add missing of_node_put
  clk: si5351: add missing of_node_put
  ...
parents 400c5bd5 e5bf1991
Mediatek imgsys controller
============================
The Mediatek imgsys controller provides various clocks to the system.
Required Properties:
- compatible: Should be:
- "mediatek,mt8173-imgsys", "syscon"
- #clock-cells: Must be 1
The imgsys controller uses the common clk binding from
Documentation/devicetree/bindings/clock/clock-bindings.txt
The available clocks are defined in dt-bindings/clock/mt*-clk.h.
Example:
imgsys: clock-controller@15000000 {
compatible = "mediatek,mt8173-imgsys", "syscon";
reg = <0 0x15000000 0 0x1000>;
#clock-cells = <1>;
};
Mediatek mmsys controller
============================
The Mediatek mmsys controller provides various clocks to the system.
Required Properties:
- compatible: Should be:
- "mediatek,mt8173-mmsys", "syscon"
- #clock-cells: Must be 1
The mmsys controller uses the common clk binding from
Documentation/devicetree/bindings/clock/clock-bindings.txt
The available clocks are defined in dt-bindings/clock/mt*-clk.h.
Example:
mmsys: clock-controller@14000000 {
compatible = "mediatek,mt8173-mmsys", "syscon";
reg = <0 0x14000000 0 0x1000>;
#clock-cells = <1>;
};
Mediatek vdecsys controller
============================
The Mediatek vdecsys controller provides various clocks to the system.
Required Properties:
- compatible: Should be:
- "mediatek,mt8173-vdecsys", "syscon"
- #clock-cells: Must be 1
The vdecsys controller uses the common clk binding from
Documentation/devicetree/bindings/clock/clock-bindings.txt
The available clocks are defined in dt-bindings/clock/mt*-clk.h.
Example:
vdecsys: clock-controller@16000000 {
compatible = "mediatek,mt8173-vdecsys", "syscon";
reg = <0 0x16000000 0 0x1000>;
#clock-cells = <1>;
};
Mediatek vencltsys controller
============================
The Mediatek vencltsys controller provides various clocks to the system.
Required Properties:
- compatible: Should be:
- "mediatek,mt8173-vencltsys", "syscon"
- #clock-cells: Must be 1
The vencltsys controller uses the common clk binding from
Documentation/devicetree/bindings/clock/clock-bindings.txt
The available clocks are defined in dt-bindings/clock/mt*-clk.h.
Example:
vencltsys: clock-controller@19000000 {
compatible = "mediatek,mt8173-vencltsys", "syscon";
reg = <0 0x19000000 0 0x1000>;
#clock-cells = <1>;
};
Mediatek vencsys controller
============================
The Mediatek vencsys controller provides various clocks to the system.
Required Properties:
- compatible: Should be:
- "mediatek,mt8173-vencsys", "syscon"
- #clock-cells: Must be 1
The vencsys controller uses the common clk binding from
Documentation/devicetree/bindings/clock/clock-bindings.txt
The available clocks are defined in dt-bindings/clock/mt*-clk.h.
Example:
vencsys: clock-controller@18000000 {
compatible = "mediatek,mt8173-vencsys", "syscon";
reg = <0 0x18000000 0 0x1000>;
#clock-cells = <1>;
};
......@@ -77,6 +77,9 @@ Required properties:
"atmel,sama5d4-clk-h32mx":
at91 h32mx clock
"atmel,sama5d2-clk-generated":
at91 generated 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).
......@@ -461,3 +464,35 @@ For example:
compatible = "atmel,sama5d4-clk-h32mx";
clocks = <&mck>;
};
Required properties for generated clocks:
- #size-cells : shall be 0 (reg is used to encode clk id).
- #address-cells : shall be 1 (reg is used to encode clk id).
- clocks : shall be the generated clock source phandles.
e.g. clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>, <&audio_pll_pmc>;
- name: device tree node describing a specific generated clock.
* #clock-cells : from common clock binding; shall be set to 0.
* reg: peripheral id. See Atmel's datasheets to get a full
list of peripheral ids.
* atmel,clk-output-range : minimum and maximum clock frequency
(two u32 fields).
For example:
gck {
compatible = "atmel,sama5d2-clk-generated";
#address-cells = <1>;
#size-cells = <0>;
clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>, <&audio_pll_pmc>;
tcb0_gclk: tcb0_gclk {
#clock-cells = <0>;
reg = <35>;
atmel,clk-output-range = <0 83000000>;
};
pwm_gclk: pwm_gclk {
#clock-cells = <0>;
reg = <38>;
atmel,clk-output-range = <0 83000000>;
};
};
Broadcom BCM2835 CPRMAN clocks
This binding uses the common clock binding:
Documentation/devicetree/bindings/clock/clock-bindings.txt
The CPRMAN clock controller generates clocks in the audio power domain
of the BCM2835. There is a level of PLLs deriving from an external
oscillator, a level of PLL dividers that produce channels off of the
few PLLs, and a level of mostly-generic clock generators sourcing from
the PLL channels. Most other hardware components source from the
clock generators, but a few (like the ARM or HDMI) will source from
the PLL dividers directly.
Required properties:
- compatible: Should be "brcm,bcm2835-cprman"
- #clock-cells: Should be <1>. The permitted clock-specifier values can be
found in include/dt-bindings/clock/bcm2835.h
- reg: Specifies base physical address and size of the registers
- clocks: The external oscillator clock phandle
Example:
clk_osc: clock@3 {
compatible = "fixed-clock";
reg = <3>;
#clock-cells = <0>;
clock-output-names = "osc";
clock-frequency = <19200000>;
};
clocks: cprman@7e101000 {
compatible = "brcm,bcm2835-cprman";
#clock-cells = <1>;
reg = <0x7e101000 0x2000>;
clocks = <&clk_osc>;
};
i2c0: i2c@7e205000 {
compatible = "brcm,bcm2835-i2c";
reg = <0x7e205000 0x1000>;
interrupts = <2 21>;
clocks = <&clocks BCM2835_CLOCK_VPU>;
#address-cells = <1>;
#size-cells = <0>;
};
......@@ -130,3 +130,81 @@ These clock IDs are defined in:
ch3_unused mipipll 4 BCM_CYGNUS_MIPIPLL_CH3_UNUSED
ch4_unused mipipll 5 BCM_CYGNUS_MIPIPLL_CH4_UNUSED
ch5_unused mipipll 6 BCM_CYGNUS_MIPIPLL_CH5_UNUSED
Northstar and Northstar Plus
------
PLL and leaf clock compatible strings for Northstar and Northstar Plus are:
"brcm,nsp-armpll"
"brcm,nsp-genpll"
"brcm,nsp-lcpll0"
The following table defines the set of PLL/clock index and ID for Northstar and
Northstar Plus. These clock IDs are defined in:
"include/dt-bindings/clock/bcm-nsp.h"
Clock Source Index ID
--- ----- ----- ---------
crystal N/A N/A N/A
armpll crystal N/A N/A
genpll crystal 0 BCM_NSP_GENPLL
phy genpll 1 BCM_NSP_GENPLL_PHY_CLK
ethernetclk genpll 2 BCM_NSP_GENPLL_ENET_SW_CLK
usbclk genpll 3 BCM_NSP_GENPLL_USB_PHY_REF_CLK
iprocfast genpll 4 BCM_NSP_GENPLL_IPROCFAST_CLK
sata1 genpll 5 BCM_NSP_GENPLL_SATA1_CLK
sata2 genpll 6 BCM_NSP_GENPLL_SATA2_CLK
lcpll0 crystal 0 BCM_NSP_LCPLL0
pcie_phy lcpll0 1 BCM_NSP_LCPLL0_PCIE_PHY_REF_CLK
sdio lcpll0 2 BCM_NSP_LCPLL0_SDIO_CLK
ddr_phy lcpll0 3 BCM_NSP_LCPLL0_DDR_PHY_CLK
Northstar 2
-----------
PLL and leaf clock compatible strings for Northstar 2 are:
"brcm,ns2-genpll-scr"
"brcm,ns2-genpll-sw"
"brcm,ns2-lcpll-ddr"
"brcm,ns2-lcpll-ports"
The following table defines the set of PLL/clock index and ID for Northstar 2.
These clock IDs are defined in:
"include/dt-bindings/clock/bcm-ns2.h"
Clock Source Index ID
--- ----- ----- ---------
crystal N/A N/A N/A
genpll_scr crystal 0 BCM_NS2_GENPLL_SCR
scr genpll_scr 1 BCM_NS2_GENPLL_SCR_SCR_CLK
fs genpll_scr 2 BCM_NS2_GENPLL_SCR_FS_CLK
audio_ref genpll_scr 3 BCM_NS2_GENPLL_SCR_AUDIO_CLK
ch3_unused genpll_scr 4 BCM_NS2_GENPLL_SCR_CH3_UNUSED
ch4_unused genpll_scr 5 BCM_NS2_GENPLL_SCR_CH4_UNUSED
ch5_unused genpll_scr 6 BCM_NS2_GENPLL_SCR_CH5_UNUSED
genpll_sw crystal 0 BCM_NS2_GENPLL_SW
rpe genpll_sw 1 BCM_NS2_GENPLL_SW_RPE_CLK
250 genpll_sw 2 BCM_NS2_GENPLL_SW_250_CLK
nic genpll_sw 3 BCM_NS2_GENPLL_SW_NIC_CLK
chimp genpll_sw 4 BCM_NS2_GENPLL_SW_CHIMP_CLK
port genpll_sw 5 BCM_NS2_GENPLL_SW_PORT_CLK
sdio genpll_sw 6 BCM_NS2_GENPLL_SW_SDIO_CLK
lcpll_ddr crystal 0 BCM_NS2_LCPLL_DDR
pcie_sata_usb lcpll_ddr 1 BCM_NS2_LCPLL_DDR_PCIE_SATA_USB_CLK
ddr lcpll_ddr 2 BCM_NS2_LCPLL_DDR_DDR_CLK
ch2_unused lcpll_ddr 3 BCM_NS2_LCPLL_DDR_CH2_UNUSED
ch3_unused lcpll_ddr 4 BCM_NS2_LCPLL_DDR_CH3_UNUSED
ch4_unused lcpll_ddr 5 BCM_NS2_LCPLL_DDR_CH4_UNUSED
ch5_unused lcpll_ddr 6 BCM_NS2_LCPLL_DDR_CH5_UNUSED
lcpll_ports crystal 0 BCM_NS2_LCPLL_PORTS
wan lcpll_ports 1 BCM_NS2_LCPLL_PORTS_WAN_CLK
rgmii lcpll_ports 2 BCM_NS2_LCPLL_PORTS_RGMII_CLK
ch2_unused lcpll_ports 3 BCM_NS2_LCPLL_PORTS_CH2_UNUSED
ch3_unused lcpll_ports 4 BCM_NS2_LCPLL_PORTS_CH3_UNUSED
ch4_unused lcpll_ports 5 BCM_NS2_LCPLL_PORTS_CH4_UNUSED
ch5_unused lcpll_ports 6 BCM_NS2_LCPLL_PORTS_CH5_UNUSED
* Renesas CPG DIV6 Clock
The CPG DIV6 clocks are variable factor clocks provided by the Clock Pulse
Generator (CPG). They clock input is divided by a configurable factor from 1
Generator (CPG). Their clock input is divided by a configurable factor from 1
to 64.
Required Properties:
......
* Renesas Clock Pulse Generator / Module Standby and Software Reset
On Renesas ARM SoCs (SH/R-Mobile, R-Car, RZ), the CPG (Clock Pulse Generator)
and MSSR (Module Standby and Software Reset) blocks are intimately connected,
and share the same register block.
They provide the following functionalities:
- The CPG block generates various core clocks,
- The MSSR block provides two functions:
1. Module Standby, providing a Clock Domain to control the clock supply
to individual SoC devices,
2. Reset Control, to perform a software reset of individual SoC devices.
Required Properties:
- compatible: Must be one of:
- "renesas,r8a7795-cpg-mssr" for the r8a7795 SoC
- reg: Base address and length of the memory resource used by the CPG/MSSR
block
- clocks: References to external parent clocks, one entry for each entry in
clock-names
- clock-names: List of external parent clock names. Valid names are:
- "extal" (r8a7795)
- "extalr" (r8a7795)
- #clock-cells: Must be 2
- For CPG core clocks, the two clock specifier cells must be "CPG_CORE"
and a core clock reference, as defined in
<dt-bindings/clock/*-cpg-mssr.h>.
- For module clocks, the two clock specifier cells must be "CPG_MOD" and
a module number, as defined in the datasheet.
- #power-domain-cells: Must be 0
- SoC devices that are part of the CPG/MSSR Clock Domain and can be
power-managed through Module Standby should refer to the CPG device
node in their "power-domains" property, as documented by the generic PM
Domain bindings in
Documentation/devicetree/bindings/power/power_domain.txt.
Examples
--------
- CPG device node:
cpg: clock-controller@e6150000 {
compatible = "renesas,r8a7795-cpg-mssr";
reg = <0 0xe6150000 0 0x1000>;
clocks = <&extal_clk>, <&extalr_clk>;
clock-names = "extal", "extalr";
#clock-cells = <2>;
#power-domain-cells = <0>;
};
- CPG/MSSR Clock Domain member device node:
scif2: serial@e6e88000 {
compatible = "renesas,scif-r8a7795", "renesas,scif";
reg = <0 0xe6e88000 0 64>;
interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 310>;
clock-names = "sci_ick";
dmas = <&dmac1 0x13>, <&dmac1 0x12>;
dma-names = "tx", "rx";
power-domains = <&cpg>;
status = "disabled";
};
Binding for Silicon Labs 514 programmable I2C clock generator.
Reference
This binding uses the common clock binding[1]. Details about the device can be
found in the datasheet[2].
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
[2] Si514 datasheet
http://www.silabs.com/Support%20Documents/TechnicalDocs/si514.pdf
Required properties:
- compatible: Shall be "silabs,si514"
- reg: I2C device address.
- #clock-cells: From common clock bindings: Shall be 0.
Optional properties:
- clock-output-names: From common clock bindings. Recommended to be "si514".
Example:
si514: clock-generator@55 {
reg = <0x55>;
#clock-cells = <0>;
compatible = "silabs,si514";
};
......@@ -23,6 +23,7 @@ Required properties:
"st,stih407-plls-c32-a9", "st,clkgen-plls-c32"
"sst,plls-c32-cx_0", "st,clkgen-plls-c32"
"sst,plls-c32-cx_1", "st,clkgen-plls-c32"
"st,stih418-plls-c28-a9", "st,clkgen-plls-c32"
"st,stih415-gpu-pll-c32", "st,clkgengpu-pll-c32"
"st,stih416-gpu-pll-c32", "st,clkgengpu-pll-c32"
......
......@@ -102,6 +102,9 @@ config HAVE_AT91_SMD
config HAVE_AT91_H32MX
bool
config HAVE_AT91_GENERATED_CLK
bool
config SOC_SAM_V4_V5
bool
......
......@@ -14,7 +14,7 @@ config ARCH_BCM_IPROC
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
select ARM_GLOBAL_TIMER
select COMMON_CLK_IPROC
select CLKSRC_MMIO
select ARCH_REQUIRE_GPIOLIB
select ARM_AMBA
......
......@@ -1353,6 +1353,7 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
return ret;
}
EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain);
/**
* pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
......@@ -1400,6 +1401,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
return ret;
}
EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain);
/* Default device callbacks for generic PM domains. */
......
......@@ -14,6 +14,7 @@ config COMMON_CLK
select HAVE_CLK_PREPARE
select CLKDEV_LOOKUP
select SRCU
select RATIONAL
---help---
The common clock framework is a single definition of struct
clk, useful across many platforms, as well as an
......@@ -68,6 +69,16 @@ config COMMON_CLK_SI5351
This driver supports Silicon Labs 5351A/B/C programmable clock
generators.
config COMMON_CLK_SI514
tristate "Clock driver for SiLabs 514 devices"
depends on I2C
depends on OF
select REGMAP_I2C
help
---help---
This driver supports the Silicon Labs 514 programmable clock
generator.
config COMMON_CLK_SI570
tristate "Clock driver for SiLabs 570 and compatible devices"
depends on I2C
......@@ -113,7 +124,7 @@ config CLK_TWL6040
config COMMON_CLK_AXI_CLKGEN
tristate "AXI clkgen driver"
depends on ARCH_ZYNQ || MICROBLAZE
depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST
help
---help---
Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx
......@@ -121,7 +132,7 @@ config COMMON_CLK_AXI_CLKGEN
config CLK_QORIQ
bool "Clock driver for Freescale QorIQ platforms"
depends on (PPC_E500MC || ARM) && OF
depends on (PPC_E500MC || ARM || COMPILE_TEST) && OF
---help---
This adds the clock driver support for Freescale QorIQ platforms
using common clock framework.
......@@ -129,13 +140,13 @@ config CLK_QORIQ
config COMMON_CLK_XGENE
bool "Clock driver for APM XGene SoC"
default y
depends on ARM64
depends on ARM64 || COMPILE_TEST
---help---
Sypport for the APM X-Gene SoC reference, PLL, and device clocks.
config COMMON_CLK_KEYSTONE
tristate "Clock drivers for Keystone based SOCs"
depends on ARCH_KEYSTONE && OF
depends on (ARCH_KEYSTONE || COMPILE_TEST) && OF
---help---
Supports clock drivers for Keystone based SOCs. These SOCs have local
a power sleep control module that gate the clock to the IPs and PLLs.
......
......@@ -6,6 +6,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-divider.o
obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o
obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o
obj-$(CONFIG_COMMON_CLK) += clk-gate.o
obj-$(CONFIG_COMMON_CLK) += clk-multiplier.o
obj-$(CONFIG_COMMON_CLK) += clk-mux.o
obj-$(CONFIG_COMMON_CLK) += clk-composite.o
obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o
......@@ -19,7 +20,6 @@ endif
obj-$(CONFIG_MACH_ASM9260) += clk-asm9260.o
obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
obj-$(CONFIG_COMMON_CLK_CDCE706) += clk-cdce706.o
obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
......@@ -37,6 +37,7 @@ obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o
obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
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_COMMON_CLK_CDCE925) += clk-cdce925.o
obj-$(CONFIG_ARCH_STM32) += clk-stm32f4.o
......@@ -47,7 +48,7 @@ obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
obj-$(CONFIG_ARCH_BCM) += bcm/
obj-y += bcm/
obj-$(CONFIG_ARCH_BERLIN) += berlin/
obj-$(CONFIG_ARCH_HISI) += hisilicon/
obj-$(CONFIG_ARCH_MXC) += imx/
......
......@@ -10,3 +10,4 @@ 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
obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o
obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o
/*
* Copyright (C) 2015 Atmel Corporation,
* Nicolas Ferre <nicolas.ferre@atmel.com>
*
* Based on clk-programmable & clk-peripheral drivers by Boris BREZILLON.
*
* 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.
*
*/
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include "pmc.h"
#define PERIPHERAL_MAX 64
#define PERIPHERAL_ID_MIN 2
#define GENERATED_SOURCE_MAX 6
#define GENERATED_MAX_DIV 255
struct clk_generated {
struct clk_hw hw;
struct at91_pmc *pmc;
struct clk_range range;
u32 id;
u32 gckdiv;
u8 parent_id;
};
#define to_clk_generated(hw) \
container_of(hw, struct clk_generated, hw)
static int clk_generated_enable(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
struct at91_pmc *pmc = gck->pmc;
u32 tmp;
pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n",
__func__, gck->gckdiv, gck->parent_id);
pmc_lock(pmc);
pmc_write(pmc, AT91_PMC_PCR, (gck->id & AT91_PMC_PCR_PID_MASK));
tmp = pmc_read(pmc, AT91_PMC_PCR) &
~(AT91_PMC_PCR_GCKDIV_MASK | AT91_PMC_PCR_GCKCSS_MASK);
pmc_write(pmc, AT91_PMC_PCR, tmp | AT91_PMC_PCR_GCKCSS(gck->parent_id)
| AT91_PMC_PCR_CMD
| AT91_PMC_PCR_GCKDIV(gck->gckdiv)
| AT91_PMC_PCR_GCKEN);
pmc_unlock(pmc);
return 0;
}
static void clk_generated_disable(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
struct at91_pmc *pmc = gck->pmc;
u32 tmp;
pmc_lock(pmc);
pmc_write(pmc, AT91_PMC_PCR, (gck->id & AT91_PMC_PCR_PID_MASK));
tmp = pmc_read(pmc, AT91_PMC_PCR) & ~AT91_PMC_PCR_GCKEN;
pmc_write(pmc, AT91_PMC_PCR, tmp | AT91_PMC_PCR_CMD);
pmc_unlock(pmc);
}
static int clk_generated_is_enabled(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
struct at91_pmc *pmc = gck->pmc;
int ret;
pmc_lock(pmc);
pmc_write(pmc, AT91_PMC_PCR, (gck->id & AT91_PMC_PCR_PID_MASK));
ret = !!(pmc_read(pmc, AT91_PMC_PCR) & AT91_PMC_PCR_GCKEN);
pmc_unlock(pmc);
return ret;
}
static unsigned long
clk_generated_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_generated *gck = to_clk_generated(hw);
return DIV_ROUND_CLOSEST(parent_rate, gck->gckdiv + 1);
}
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;
long best_rate = -EINVAL;
unsigned long tmp_rate, min_rate;
int best_diff = -1;
int tmp_diff;
int i;
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
u32 div;
unsigned long parent_rate;
parent = clk_hw_get_parent_by_index(hw, i);
if (!parent)
continue;
parent_rate = clk_hw_get_rate(parent);
min_rate = DIV_ROUND_CLOSEST(parent_rate, GENERATED_MAX_DIV + 1);
if (!parent_rate ||
(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);
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;
}
if (!best_diff || tmp_rate < req->rate)
break;
}
if (!best_diff)
break;
}
pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
__func__, best_rate,
__clk_get_name((req->best_parent_hw)->clk),
req->best_parent_rate);
if (best_rate < 0)
return best_rate;
req->rate = best_rate;
return 0;
}
/* No modification of hardware as we have the flag CLK_SET_PARENT_GATE set */
static int clk_generated_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_generated *gck = to_clk_generated(hw);
if (index >= clk_hw_get_num_parents(hw))
return -EINVAL;
gck->parent_id = index;
return 0;
}
static u8 clk_generated_get_parent(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
return gck->parent_id;
}
/* No modification of hardware as we have the flag CLK_SET_RATE_GATE set */
static int clk_generated_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
{
struct clk_generated *gck = to_clk_generated(hw);
u32 div;
if (!rate)
return -EINVAL;
if (gck->range.max && rate > gck->range.max)
return -EINVAL;
div = DIV_ROUND_CLOSEST(parent_rate, rate);
if (div > GENERATED_MAX_DIV + 1 || !div)
return -EINVAL;
gck->gckdiv = div - 1;
return 0;
}
static const struct clk_ops generated_ops = {
.enable = clk_generated_enable,
.disable = clk_generated_disable,
.is_enabled = clk_generated_is_enabled,
.recalc_rate = clk_generated_recalc_rate,
.determine_rate = clk_generated_determine_rate,
.get_parent = clk_generated_get_parent,
.set_parent = clk_generated_set_parent,
.set_rate = clk_generated_set_rate,
};
/**
* clk_generated_startup - Initialize a given clock to its default parent and
* divisor parameter.
*
* @gck: Generated clock to set the startup parameters for.
*
* Take parameters from the hardware and update local clock configuration
* accordingly.
*/
static void clk_generated_startup(struct clk_generated *gck)
{
struct at91_pmc *pmc = gck->pmc;
u32 tmp;
pmc_lock(pmc);
pmc_write(pmc, AT91_PMC_PCR, (gck->id & AT91_PMC_PCR_PID_MASK));
tmp = pmc_read(pmc, AT91_PMC_PCR);
pmc_unlock(pmc);
gck->parent_id = (tmp & AT91_PMC_PCR_GCKCSS_MASK)
>> AT91_PMC_PCR_GCKCSS_OFFSET;
gck->gckdiv = (tmp & AT91_PMC_PCR_GCKDIV_MASK)
>> AT91_PMC_PCR_GCKDIV_OFFSET;
}
static struct clk * __init
at91_clk_register_generated(struct at91_pmc *pmc, const char *name,
const char **parent_names, u8 num_parents,
u8 id, const struct clk_range *range)
{
struct clk_generated *gck;
struct clk *clk = NULL;
struct clk_init_data init;
gck = kzalloc(sizeof(*gck), GFP_KERNEL);
if (!gck)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &generated_ops;
init.parent_names = parent_names;
init.num_parents = num_parents;
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
gck->id = id;
gck->hw.init = &init;
gck->pmc = pmc;
gck->range = *range;
clk = clk_register(NULL, &gck->hw);
if (IS_ERR(clk))
kfree(gck);
else
clk_generated_startup(gck);
return clk;
}
void __init of_sama5d2_clk_generated_setup(struct device_node *np,
struct at91_pmc *pmc)
{
int num;
u32 id;
const char *name;
struct clk *clk;
int num_parents;
const char *parent_names[GENERATED_SOURCE_MAX];
struct device_node *gcknp;
struct clk_range range = CLK_RANGE(0, 0);
num_parents = of_clk_get_parent_count(np);
if (num_parents <= 0 || num_parents > GENERATED_SOURCE_MAX)
return;
of_clk_parent_fill(np, parent_names, num_parents);
num = of_get_child_count(np);
if (!num || num > PERIPHERAL_MAX)
return;
for_each_child_of_node(np, gcknp) {
if (of_property_read_u32(gcknp, "reg", &id))
continue;
if (id < PERIPHERAL_ID_MIN || id >= PERIPHERAL_MAX)
continue;
if (of_property_read_string(np, "clock-output-names", &name))
name = gcknp->name;
of_at91_get_clk_range(gcknp, "atmel,clk-output-range",
&range);
clk = at91_clk_register_generated(pmc, name, parent_names,
num_parents, id, &range);
if (IS_ERR(clk))
continue;
of_clk_add_provider(gcknp, of_clk_src_simple_get, clk);
}
}
......@@ -161,14 +161,18 @@ static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
struct at91_pmc *pmc = periph->pmc;
u32 tmp;
if (periph->id < PERIPHERAL_ID_MIN)
return 0;
pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID) |
AT91_PMC_PCR_CMD |
AT91_PMC_PCR_DIV(periph->div) |
AT91_PMC_PCR_EN);
pmc_lock(pmc);
pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID_MASK));
tmp = pmc_read(pmc, AT91_PMC_PCR) & ~AT91_PMC_PCR_DIV_MASK;
pmc_write(pmc, AT91_PMC_PCR, tmp | AT91_PMC_PCR_DIV(periph->div)
| AT91_PMC_PCR_CMD
| AT91_PMC_PCR_EN);
pmc_unlock(pmc);
return 0;
}
......@@ -176,12 +180,16 @@ static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
struct at91_pmc *pmc = periph->pmc;
u32 tmp;
if (periph->id < PERIPHERAL_ID_MIN)
return;
pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID) |
AT91_PMC_PCR_CMD);
pmc_lock(pmc);
pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID_MASK));
tmp = pmc_read(pmc, AT91_PMC_PCR) & ~AT91_PMC_PCR_EN;
pmc_write(pmc, AT91_PMC_PCR, tmp | AT91_PMC_PCR_CMD);
pmc_unlock(pmc);
}
static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
......@@ -194,7 +202,7 @@ static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
return 1;
pmc_lock(pmc);
pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID));
pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID_MASK));
ret = !!(pmc_read(pmc, AT91_PMC_PCR) & AT91_PMC_PCR_EN);
pmc_unlock(pmc);
......@@ -213,7 +221,7 @@ clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
return parent_rate;
pmc_lock(pmc);
pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID));
pmc_write(pmc, AT91_PMC_PCR, (periph->id & AT91_PMC_PCR_PID_MASK));
tmp = pmc_read(pmc, AT91_PMC_PCR);
pmc_unlock(pmc);
......
......@@ -138,7 +138,8 @@ at91_clk_register_system(struct at91_pmc *pmc, const char *name,
clk = clk_register(NULL, &sys->hw);
if (IS_ERR(clk)) {
free_irq(sys->irq, sys);
if (irq)
free_irq(sys->irq, sys);
kfree(sys);
}
......
......@@ -47,7 +47,7 @@ static int clk_utmi_prepare(struct clk_hw *hw)
{
struct clk_utmi *utmi = to_clk_utmi(hw);
struct at91_pmc *pmc = utmi->pmc;
u32 tmp = at91_pmc_read(AT91_CKGR_UCKR) | AT91_PMC_UPLLEN |
u32 tmp = pmc_read(pmc, AT91_CKGR_UCKR) | AT91_PMC_UPLLEN |
AT91_PMC_UPLLCOUNT | AT91_PMC_BIASEN;
pmc_write(pmc, AT91_CKGR_UCKR, tmp);
......@@ -73,7 +73,7 @@ static void clk_utmi_unprepare(struct clk_hw *hw)
{
struct clk_utmi *utmi = to_clk_utmi(hw);
struct at91_pmc *pmc = utmi->pmc;
u32 tmp = at91_pmc_read(AT91_CKGR_UCKR) & ~AT91_PMC_UPLLEN;
u32 tmp = pmc_read(pmc, AT91_CKGR_UCKR) & ~AT91_PMC_UPLLEN;
pmc_write(pmc, AT91_CKGR_UCKR, tmp);
}
......
......@@ -206,6 +206,14 @@ static const struct at91_pmc_caps at91sam9x5_caps = {
AT91_PMC_MOSCRCS | AT91_PMC_CFDEV,
};
static const struct at91_pmc_caps sama5d2_caps = {
.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY |
AT91_PMC_LOCKU | AT91_PMC_PCK0RDY |
AT91_PMC_PCK1RDY | AT91_PMC_PCK2RDY |
AT91_PMC_MOSCSELS | AT91_PMC_MOSCRCS |
AT91_PMC_CFDEV | AT91_PMC_GCKRDY,
};
static const struct at91_pmc_caps sama5d3_caps = {
.available_irqs = AT91_PMC_MOSCS | AT91_PMC_LOCKA | AT91_PMC_MCKRDY |
AT91_PMC_LOCKU | AT91_PMC_PCK0RDY |
......@@ -368,6 +376,12 @@ static const struct of_device_id pmc_clk_ids[] __initconst = {
.compatible = "atmel,sama5d4-clk-h32mx",
.data = of_sama5d4_clk_h32mx_setup,
},
#endif
#if defined(CONFIG_HAVE_AT91_GENERATED_CLK)
{
.compatible = "atmel,sama5d2-clk-generated",
.data = of_sama5d2_clk_generated_setup,
},
#endif
{ /*sentinel*/ }
};
......@@ -436,6 +450,13 @@ static void __init of_at91sam9x5_pmc_setup(struct device_node *np)
CLK_OF_DECLARE(at91sam9x5_clk_pmc, "atmel,at91sam9x5-pmc",
of_at91sam9x5_pmc_setup);
static void __init of_sama5d2_pmc_setup(struct device_node *np)
{
of_at91_pmc_setup(np, &sama5d2_caps);
}
CLK_OF_DECLARE(sama5d2_clk_pmc, "atmel,sama5d2-pmc",
of_sama5d2_pmc_setup);
static void __init of_sama5d3_pmc_setup(struct device_node *np)
{
of_at91_pmc_setup(np, &sama5d3_caps);
......
......@@ -118,4 +118,7 @@ void of_at91sam9x5_clk_smd_setup(struct device_node *np,
void of_sama5d4_clk_h32mx_setup(struct device_node *np,
struct at91_pmc *pmc);
void of_sama5d2_clk_generated_setup(struct device_node *np,
struct at91_pmc *pmc);
#endif /* __PMC_H_ */
config CLK_BCM_KONA
bool "Broadcom Kona CCU clock support"
depends on ARCH_BCM_MOBILE
depends on ARCH_BCM_MOBILE || COMPILE_TEST
depends on COMMON_CLK
default y
help
......@@ -9,10 +9,8 @@ config CLK_BCM_KONA
in the BCM281xx and BCM21664 families.
config COMMON_CLK_IPROC
bool "Broadcom iProc clock support"
depends on ARCH_BCM_IPROC
bool
depends on COMMON_CLK
default ARCH_BCM_IPROC
help
Enable common clock framework support for Broadcom SoCs
based on the iProc architecture
......@@ -3,4 +3,8 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o
obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o
obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o
obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
obj-$(CONFIG_COMMON_CLK_IPROC) += clk-ns2.o
obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o
obj-$(CONFIG_ARCH_BCM_NSP) += clk-nsp.o
obj-$(CONFIG_ARCH_BCM_5301X) += clk-nsp.o
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -48,6 +48,18 @@
*/
#define IPROC_CLK_PLL_NEEDS_SW_CFG BIT(4)
/*
* Some PLLs use a different way to control clock power, via the PWRDWN bit in
* the PLL control register
*/
#define IPROC_CLK_EMBED_PWRCTRL BIT(5)
/*
* Some PLLs have separate registers for Status and Control. Identify this to
* let the driver know if additional registers need to be used
*/
#define IPROC_CLK_PLL_SPLIT_STAT_CTRL BIT(6)
/*
* Parameters for VCO frequency configuration
*
......@@ -88,12 +100,19 @@ struct iproc_pll_aon_pwr_ctrl {
};
/*
* Control of the PLL reset, with Ki, Kp, and Ka parameters
* Control of the PLL reset
*/
struct iproc_pll_reset_ctrl {
unsigned int offset;
unsigned int reset_shift;
unsigned int p_reset_shift;
};
/*
* Control of the Ki, Kp, and Ka parameters
*/
struct iproc_pll_dig_filter_ctrl {
unsigned int offset;
unsigned int ki_shift;
unsigned int ki_width;
unsigned int kp_shift;
......@@ -123,6 +142,7 @@ struct iproc_pll_ctrl {
struct iproc_pll_aon_pwr_ctrl aon;
struct iproc_asiu_gate asiu;
struct iproc_pll_reset_ctrl reset;
struct iproc_pll_dig_filter_ctrl dig_filter;
struct iproc_pll_sw_ctrl sw_ctrl;
struct iproc_clk_reg_op ndiv_int;
struct iproc_clk_reg_op ndiv_frac;
......
/*
* Copyright (C) 2015 Broadcom Corporation
*
* 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.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <dt-bindings/clock/bcm-ns2.h>
#include "clk-iproc.h"
#define REG_VAL(o, s, w) { .offset = o, .shift = s, .width = w, }
#define AON_VAL(o, pw, ps, is) { .offset = o, .pwr_width = pw, \
.pwr_shift = ps, .iso_shift = is }
#define RESET_VAL(o, rs, prs) { .offset = o, .reset_shift = rs, \
.p_reset_shift = prs }
#define DF_VAL(o, kis, kiw, kps, kpw, kas, kaw) { .offset = o, .ki_shift = kis,\
.ki_width = kiw, .kp_shift = kps, .kp_width = kpw, .ka_shift = kas, \
.ka_width = kaw }
#define VCO_CTRL_VAL(uo, lo) { .u_offset = uo, .l_offset = lo }
#define ENABLE_VAL(o, es, hs, bs) { .offset = o, .enable_shift = es, \
.hold_shift = hs, .bypass_shift = bs }
static const struct iproc_pll_ctrl genpll_scr = {
.flags = IPROC_CLK_AON | IPROC_CLK_PLL_SPLIT_STAT_CTRL,
.aon = AON_VAL(0x0, 1, 15, 12),
.reset = RESET_VAL(0x4, 2, 1),
.dig_filter = DF_VAL(0x0, 9, 3, 5, 4, 2, 3),
.ndiv_int = REG_VAL(0x8, 4, 10),
.pdiv = REG_VAL(0x8, 0, 4),
.vco_ctrl = VCO_CTRL_VAL(0x10, 0xc),
.status = REG_VAL(0x0, 27, 1),
};
static const struct iproc_clk_ctrl genpll_scr_clk[] = {
/* bypass_shift, the last value passed into ENABLE_VAL(), is not defined
* in NS2. However, it doesn't appear to be used anywhere, so setting
* it to 0.
*/
[BCM_NS2_GENPLL_SCR_SCR_CLK] = {
.channel = BCM_NS2_GENPLL_SCR_SCR_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 18, 12, 0),
.mdiv = REG_VAL(0x18, 0, 8),
},
[BCM_NS2_GENPLL_SCR_FS_CLK] = {
.channel = BCM_NS2_GENPLL_SCR_FS_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 19, 13, 0),
.mdiv = REG_VAL(0x18, 8, 8),
},
[BCM_NS2_GENPLL_SCR_AUDIO_CLK] = {
.channel = BCM_NS2_GENPLL_SCR_AUDIO_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 20, 14, 0),
.mdiv = REG_VAL(0x14, 0, 8),
},
[BCM_NS2_GENPLL_SCR_CH3_UNUSED] = {
.channel = BCM_NS2_GENPLL_SCR_CH3_UNUSED,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 21, 15, 0),
.mdiv = REG_VAL(0x14, 8, 8),
},
[BCM_NS2_GENPLL_SCR_CH4_UNUSED] = {
.channel = BCM_NS2_GENPLL_SCR_CH4_UNUSED,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 22, 16, 0),
.mdiv = REG_VAL(0x14, 16, 8),
},
[BCM_NS2_GENPLL_SCR_CH5_UNUSED] = {
.channel = BCM_NS2_GENPLL_SCR_CH5_UNUSED,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 23, 17, 0),
.mdiv = REG_VAL(0x14, 24, 8),
},
};
static void __init ns2_genpll_scr_clk_init(struct device_node *node)
{
iproc_pll_clk_setup(node, &genpll_scr, NULL, 0, genpll_scr_clk,
ARRAY_SIZE(genpll_scr_clk));
}
CLK_OF_DECLARE(ns2_genpll_src_clk, "brcm,ns2-genpll-scr",
ns2_genpll_scr_clk_init);
static const struct iproc_pll_ctrl genpll_sw = {
.flags = IPROC_CLK_AON | IPROC_CLK_PLL_SPLIT_STAT_CTRL,
.aon = AON_VAL(0x0, 2, 9, 8),
.reset = RESET_VAL(0x4, 2, 1),
.dig_filter = DF_VAL(0x0, 9, 3, 5, 4, 2, 3),
.ndiv_int = REG_VAL(0x8, 4, 10),
.pdiv = REG_VAL(0x8, 0, 4),
.vco_ctrl = VCO_CTRL_VAL(0x10, 0xc),
.status = REG_VAL(0x0, 13, 1),
};
static const struct iproc_clk_ctrl genpll_sw_clk[] = {
/* bypass_shift, the last value passed into ENABLE_VAL(), is not defined
* in NS2. However, it doesn't appear to be used anywhere, so setting
* it to 0.
*/
[BCM_NS2_GENPLL_SW_RPE_CLK] = {
.channel = BCM_NS2_GENPLL_SW_RPE_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 18, 12, 0),
.mdiv = REG_VAL(0x18, 0, 8),
},
[BCM_NS2_GENPLL_SW_250_CLK] = {
.channel = BCM_NS2_GENPLL_SW_250_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 19, 13, 0),
.mdiv = REG_VAL(0x18, 8, 8),
},
[BCM_NS2_GENPLL_SW_NIC_CLK] = {
.channel = BCM_NS2_GENPLL_SW_NIC_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 20, 14, 0),
.mdiv = REG_VAL(0x14, 0, 8),
},
[BCM_NS2_GENPLL_SW_CHIMP_CLK] = {
.channel = BCM_NS2_GENPLL_SW_CHIMP_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 21, 15, 0),
.mdiv = REG_VAL(0x14, 8, 8),
},
[BCM_NS2_GENPLL_SW_PORT_CLK] = {
.channel = BCM_NS2_GENPLL_SW_PORT_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 22, 16, 0),
.mdiv = REG_VAL(0x14, 16, 8),
},
[BCM_NS2_GENPLL_SW_SDIO_CLK] = {
.channel = BCM_NS2_GENPLL_SW_SDIO_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 23, 17, 0),
.mdiv = REG_VAL(0x14, 24, 8),
},
};
static void __init ns2_genpll_sw_clk_init(struct device_node *node)
{
iproc_pll_clk_setup(node, &genpll_sw, NULL, 0, genpll_sw_clk,
ARRAY_SIZE(genpll_sw_clk));
}
CLK_OF_DECLARE(ns2_genpll_sw_clk, "brcm,ns2-genpll-sw",
ns2_genpll_sw_clk_init);
static const struct iproc_pll_ctrl lcpll_ddr = {
.flags = IPROC_CLK_AON | IPROC_CLK_PLL_SPLIT_STAT_CTRL,
.aon = AON_VAL(0x0, 2, 1, 0),
.reset = RESET_VAL(0x4, 2, 1),
.dig_filter = DF_VAL(0x0, 9, 3, 5, 4, 1, 4),
.ndiv_int = REG_VAL(0x8, 4, 10),
.pdiv = REG_VAL(0x8, 0, 4),
.vco_ctrl = VCO_CTRL_VAL(0x10, 0xc),
.status = REG_VAL(0x0, 0, 1),
};
static const struct iproc_clk_ctrl lcpll_ddr_clk[] = {
/* bypass_shift, the last value passed into ENABLE_VAL(), is not defined
* in NS2. However, it doesn't appear to be used anywhere, so setting
* it to 0.
*/
[BCM_NS2_LCPLL_DDR_PCIE_SATA_USB_CLK] = {
.channel = BCM_NS2_LCPLL_DDR_PCIE_SATA_USB_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 18, 12, 0),
.mdiv = REG_VAL(0x14, 0, 8),
},
[BCM_NS2_LCPLL_DDR_DDR_CLK] = {
.channel = BCM_NS2_LCPLL_DDR_DDR_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 19, 13, 0),
.mdiv = REG_VAL(0x14, 8, 8),
},
[BCM_NS2_LCPLL_DDR_CH2_UNUSED] = {
.channel = BCM_NS2_LCPLL_DDR_CH2_UNUSED,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 20, 14, 0),
.mdiv = REG_VAL(0x10, 0, 8),
},
[BCM_NS2_LCPLL_DDR_CH3_UNUSED] = {
.channel = BCM_NS2_LCPLL_DDR_CH3_UNUSED,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 21, 15, 0),
.mdiv = REG_VAL(0x10, 8, 8),
},
[BCM_NS2_LCPLL_DDR_CH4_UNUSED] = {
.channel = BCM_NS2_LCPLL_DDR_CH4_UNUSED,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 22, 16, 0),
.mdiv = REG_VAL(0x10, 16, 8),
},
[BCM_NS2_LCPLL_DDR_CH5_UNUSED] = {
.channel = BCM_NS2_LCPLL_DDR_CH5_UNUSED,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 23, 17, 0),
.mdiv = REG_VAL(0x10, 24, 8),
},
};
static void __init ns2_lcpll_ddr_clk_init(struct device_node *node)
{
iproc_pll_clk_setup(node, &lcpll_ddr, NULL, 0, lcpll_ddr_clk,
ARRAY_SIZE(lcpll_ddr_clk));
}
CLK_OF_DECLARE(ns2_lcpll_ddr_clk, "brcm,ns2-lcpll-ddr",
ns2_lcpll_ddr_clk_init);
static const struct iproc_pll_ctrl lcpll_ports = {
.flags = IPROC_CLK_AON | IPROC_CLK_PLL_SPLIT_STAT_CTRL,
.aon = AON_VAL(0x0, 2, 5, 4),
.reset = RESET_VAL(0x4, 2, 1),
.dig_filter = DF_VAL(0x0, 9, 3, 5, 4, 1, 4),
.ndiv_int = REG_VAL(0x8, 4, 10),
.pdiv = REG_VAL(0x8, 0, 4),
.vco_ctrl = VCO_CTRL_VAL(0x10, 0xc),
.status = REG_VAL(0x0, 0, 1),
};
static const struct iproc_clk_ctrl lcpll_ports_clk[] = {
/* bypass_shift, the last value passed into ENABLE_VAL(), is not defined
* in NS2. However, it doesn't appear to be used anywhere, so setting
* it to 0.
*/
[BCM_NS2_LCPLL_PORTS_WAN_CLK] = {
.channel = BCM_NS2_LCPLL_PORTS_WAN_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 18, 12, 0),
.mdiv = REG_VAL(0x14, 0, 8),
},
[BCM_NS2_LCPLL_PORTS_RGMII_CLK] = {
.channel = BCM_NS2_LCPLL_PORTS_RGMII_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 19, 13, 0),
.mdiv = REG_VAL(0x14, 8, 8),
},
[BCM_NS2_LCPLL_PORTS_CH2_UNUSED] = {
.channel = BCM_NS2_LCPLL_PORTS_CH2_UNUSED,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 20, 14, 0),
.mdiv = REG_VAL(0x10, 0, 8),
},
[BCM_NS2_LCPLL_PORTS_CH3_UNUSED] = {
.channel = BCM_NS2_LCPLL_PORTS_CH3_UNUSED,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 21, 15, 0),
.mdiv = REG_VAL(0x10, 8, 8),
},
[BCM_NS2_LCPLL_PORTS_CH4_UNUSED] = {
.channel = BCM_NS2_LCPLL_PORTS_CH4_UNUSED,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 22, 16, 0),
.mdiv = REG_VAL(0x10, 16, 8),
},
[BCM_NS2_LCPLL_PORTS_CH5_UNUSED] = {
.channel = BCM_NS2_LCPLL_PORTS_CH5_UNUSED,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 23, 17, 0),
.mdiv = REG_VAL(0x10, 24, 8),
},
};
static void __init ns2_lcpll_ports_clk_init(struct device_node *node)
{
iproc_pll_clk_setup(node, &lcpll_ports, NULL, 0, lcpll_ports_clk,
ARRAY_SIZE(lcpll_ports_clk));
}
CLK_OF_DECLARE(ns2_lcpll_ports_clk, "brcm,ns2-lcpll-ports",
ns2_lcpll_ports_clk_init);
/*
* Copyright (C) 2015 Broadcom Corporation
*
* 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.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <dt-bindings/clock/bcm-nsp.h>
#include "clk-iproc.h"
#define REG_VAL(o, s, w) { .offset = o, .shift = s, .width = w, }
#define AON_VAL(o, pw, ps, is) { .offset = o, .pwr_width = pw, \
.pwr_shift = ps, .iso_shift = is }
#define RESET_VAL(o, rs, prs) { .offset = o, .reset_shift = rs, \
.p_reset_shift = prs }
#define DF_VAL(o, kis, kiw, kps, kpw, kas, kaw) { .offset = o, .ki_shift = kis,\
.ki_width = kiw, .kp_shift = kps, .kp_width = kpw, .ka_shift = kas, \
.ka_width = kaw }
#define ENABLE_VAL(o, es, hs, bs) { .offset = o, .enable_shift = es, \
.hold_shift = hs, .bypass_shift = bs }
static void __init nsp_armpll_init(struct device_node *node)
{
iproc_armpll_setup(node);
}
CLK_OF_DECLARE(nsp_armpll, "brcm,nsp-armpll", nsp_armpll_init);
static const struct iproc_pll_ctrl genpll = {
.flags = IPROC_CLK_PLL_HAS_NDIV_FRAC | IPROC_CLK_EMBED_PWRCTRL,
.aon = AON_VAL(0x0, 1, 12, 0),
.reset = RESET_VAL(0x0, 11, 10),
.dig_filter = DF_VAL(0x0, 4, 3, 0, 4, 7, 3),
.ndiv_int = REG_VAL(0x14, 20, 10),
.ndiv_frac = REG_VAL(0x14, 0, 20),
.pdiv = REG_VAL(0x18, 24, 3),
.status = REG_VAL(0x20, 12, 1),
};
static const struct iproc_clk_ctrl genpll_clk[] = {
[BCM_NSP_GENPLL_PHY_CLK] = {
.channel = BCM_NSP_GENPLL_PHY_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x4, 12, 6, 18),
.mdiv = REG_VAL(0x18, 16, 8),
},
[BCM_NSP_GENPLL_ENET_SW_CLK] = {
.channel = BCM_NSP_GENPLL_ENET_SW_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x4, 13, 7, 19),
.mdiv = REG_VAL(0x18, 8, 8),
},
[BCM_NSP_GENPLL_USB_PHY_REF_CLK] = {
.channel = BCM_NSP_GENPLL_USB_PHY_REF_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x4, 14, 8, 20),
.mdiv = REG_VAL(0x18, 0, 8),
},
[BCM_NSP_GENPLL_IPROCFAST_CLK] = {
.channel = BCM_NSP_GENPLL_IPROCFAST_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x4, 15, 9, 21),
.mdiv = REG_VAL(0x1c, 16, 8),
},
[BCM_NSP_GENPLL_SATA1_CLK] = {
.channel = BCM_NSP_GENPLL_SATA1_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x4, 16, 10, 22),
.mdiv = REG_VAL(0x1c, 8, 8),
},
[BCM_NSP_GENPLL_SATA2_CLK] = {
.channel = BCM_NSP_GENPLL_SATA2_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x4, 17, 11, 23),
.mdiv = REG_VAL(0x1c, 0, 8),
},
};
static void __init nsp_genpll_clk_init(struct device_node *node)
{
iproc_pll_clk_setup(node, &genpll, NULL, 0, genpll_clk,
ARRAY_SIZE(genpll_clk));
}
CLK_OF_DECLARE(nsp_genpll_clk, "brcm,nsp-genpll", nsp_genpll_clk_init);
static const struct iproc_pll_ctrl lcpll0 = {
.flags = IPROC_CLK_PLL_HAS_NDIV_FRAC | IPROC_CLK_EMBED_PWRCTRL,
.aon = AON_VAL(0x0, 1, 24, 0),
.reset = RESET_VAL(0x0, 23, 22),
.dig_filter = DF_VAL(0x0, 16, 3, 12, 4, 19, 4),
.ndiv_int = REG_VAL(0x4, 20, 8),
.ndiv_frac = REG_VAL(0x4, 0, 20),
.pdiv = REG_VAL(0x4, 28, 3),
.status = REG_VAL(0x10, 12, 1),
};
static const struct iproc_clk_ctrl lcpll0_clk[] = {
[BCM_NSP_LCPLL0_PCIE_PHY_REF_CLK] = {
.channel = BCM_NSP_LCPLL0_PCIE_PHY_REF_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 6, 3, 9),
.mdiv = REG_VAL(0x8, 24, 8),
},
[BCM_NSP_LCPLL0_SDIO_CLK] = {
.channel = BCM_NSP_LCPLL0_SDIO_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 7, 4, 10),
.mdiv = REG_VAL(0x8, 16, 8),
},
[BCM_NSP_LCPLL0_DDR_PHY_CLK] = {
.channel = BCM_NSP_LCPLL0_DDR_PHY_CLK,
.flags = IPROC_CLK_AON,
.enable = ENABLE_VAL(0x0, 8, 5, 11),
.mdiv = REG_VAL(0x8, 8, 8),
},
};
static void __init nsp_lcpll0_clk_init(struct device_node *node)
{
iproc_pll_clk_setup(node, &lcpll0, NULL, 0, lcpll0_clk,
ARRAY_SIZE(lcpll0_clk));
}
CLK_OF_DECLARE(nsp_lcpll0_clk, "brcm,nsp-lcpll0", nsp_lcpll0_clk_init);
......@@ -490,8 +490,8 @@ static const struct berlin2_gate_data bg2_gates[] __initconst = {
{ "usb0", "perif", 11 },
{ "usb1", "perif", 12 },
{ "pbridge", "perif", 13, CLK_IGNORE_UNUSED },
{ "sdio0", "perif", 14, CLK_IGNORE_UNUSED },
{ "sdio1", "perif", 15, CLK_IGNORE_UNUSED },
{ "sdio0", "perif", 14 },
{ "sdio1", "perif", 15 },
{ "nfc", "perif", 17 },
{ "smemc", "perif", 19 },
{ "audiohd", "audiohd_pll", 26 },
......
......@@ -283,7 +283,7 @@ static const struct berlin2_gate_data bg2q_gates[] __initconst = {
{ "usb2", "perif", 13 },
{ "usb3", "perif", 14 },
{ "pbridge", "perif", 15, CLK_IGNORE_UNUSED },
{ "sdio", "perif", 16, CLK_IGNORE_UNUSED },
{ "sdio", "perif", 16 },
{ "nfc", "perif", 18 },
{ "pcie", "perif", 22 },
};
......
......@@ -24,7 +24,7 @@
* Traits of this clock:
* prepare - clk_prepare only ensures that parents are prepared
* enable - clk_enable only ensures that parents are enabled
* rate - rate is adjustable. clk->rate = DIV_ROUND_UP(parent->rate / divisor)
* rate - rate is adjustable. clk->rate = ceiling(parent->rate / divisor)
* parent - fixed parent. No clk_set_parent support
*/
......@@ -132,7 +132,7 @@ unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
return parent_rate;
}
return DIV_ROUND_UP(parent_rate, div);
return DIV_ROUND_UP_ULL((u64)parent_rate, div);
}
EXPORT_SYMBOL_GPL(divider_recalc_rate);
......@@ -210,7 +210,7 @@ static int _div_round_up(const struct clk_div_table *table,
unsigned long parent_rate, unsigned long rate,
unsigned long flags)
{
int div = DIV_ROUND_UP(parent_rate, rate);
int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
if (flags & CLK_DIVIDER_POWER_OF_TWO)
div = __roundup_pow_of_two(div);
......@@ -227,7 +227,7 @@ static int _div_round_closest(const struct clk_div_table *table,
int up, down;
unsigned long up_rate, down_rate;
up = DIV_ROUND_UP(parent_rate, rate);
up = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
down = parent_rate / rate;
if (flags & CLK_DIVIDER_POWER_OF_TWO) {
......@@ -238,8 +238,8 @@ static int _div_round_closest(const struct clk_div_table *table,
down = _round_down_table(table, down);
}
up_rate = DIV_ROUND_UP(parent_rate, up);
down_rate = DIV_ROUND_UP(parent_rate, down);
up_rate = DIV_ROUND_UP_ULL((u64)parent_rate, up);
down_rate = DIV_ROUND_UP_ULL((u64)parent_rate, down);
return (rate - up_rate) <= (down_rate - rate) ? up : down;
}
......@@ -318,7 +318,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
}
parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
rate * i);
now = DIV_ROUND_UP(parent_rate, i);
now = DIV_ROUND_UP_ULL((u64)parent_rate, i);
if (_is_best_div(rate, now, best, flags)) {
bestdiv = i;
best = now;
......@@ -342,7 +342,7 @@ long divider_round_rate(struct clk_hw *hw, unsigned long rate,
div = clk_divider_bestdiv(hw, rate, prate, table, width, flags);
return DIV_ROUND_UP(*prate, div);
return DIV_ROUND_UP_ULL((u64)*prate, div);
}
EXPORT_SYMBOL_GPL(divider_round_rate);
......@@ -358,7 +358,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
bestdiv &= div_mask(divider->width);
bestdiv = _get_div(divider->table, bestdiv, divider->flags,
divider->width);
return DIV_ROUND_UP(*prate, bestdiv);
return DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
}
return divider_round_rate(hw, rate, prate, divider->table,
......@@ -371,7 +371,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate,
{
unsigned int div, value;
div = DIV_ROUND_UP(parent_rate, rate);
div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
if (!_is_valid_div(table, div, flags))
return -EINVAL;
......
......@@ -7,13 +7,14 @@
*
* Adjustable fractional divider clock implementation.
* Output rate = (m / n) * parent_rate.
* Uses rational best approximation algorithm.
*/
#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/gcd.h>
#include <linux/rational.h>
#define to_clk_fd(_hw) container_of(_hw, struct clk_fractional_divider, hw)
......@@ -22,7 +23,8 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
{
struct clk_fractional_divider *fd = to_clk_fd(hw);
unsigned long flags = 0;
u32 val, m, n;
unsigned long m, n;
u32 val;
u64 ret;
if (fd->lock)
......@@ -50,23 +52,33 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
}
static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
unsigned long *parent_rate)
{
struct clk_fractional_divider *fd = to_clk_fd(hw);
unsigned maxn = (fd->nmask >> fd->nshift) + 1;
unsigned div;
unsigned long scale;
unsigned long m, n;
u64 ret;
if (!rate || rate >= *prate)
return *prate;
if (!rate || rate >= *parent_rate)
return *parent_rate;
div = gcd(*prate, 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;
while ((*prate / div) > maxn) {
div <<= 1;
rate <<= 1;
}
rational_best_approximation(rate, *parent_rate,
GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
&m, &n);
return rate;
ret = (u64)*parent_rate * m;
do_div(ret, n);
return ret;
}
static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate,
......@@ -74,13 +86,12 @@ static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_fractional_divider *fd = to_clk_fd(hw);
unsigned long flags = 0;
unsigned long div;
unsigned n, m;
unsigned long m, n;
u32 val;
div = gcd(parent_rate, rate);
m = rate / div;
n = parent_rate / div;
rational_best_approximation(rate, parent_rate,
GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
&m, &n);
if (fd->lock)
spin_lock_irqsave(fd->lock, flags);
......@@ -128,9 +139,11 @@ struct clk *clk_register_fractional_divider(struct device *dev,
fd->reg = reg;
fd->mshift = mshift;
fd->mmask = (BIT(mwidth) - 1) << mshift;
fd->mwidth = mwidth;
fd->mmask = GENMASK(mwidth - 1, 0) << mshift;
fd->nshift = nshift;
fd->nmask = (BIT(nwidth) - 1) << nshift;
fd->nwidth = nwidth;
fd->nmask = GENMASK(nwidth - 1, 0) << nshift;
fd->flags = clk_divider_flags;
fd->lock = lock;
fd->hw.init = &init;
......
......@@ -94,5 +94,5 @@ static struct platform_driver max77802_clk_driver = {
module_platform_driver(max77802_clk_driver);
MODULE_DESCRIPTION("MAXIM 77802 Clock Driver");
MODULE_AUTHOR("Javier Martinez Canillas <javier.martinez@collabora.co.uk>");
MODULE_AUTHOR("Javier Martinez Canillas <javier@osg.samsung.com");
MODULE_LICENSE("GPL");
/*
* Copyright (C) 2015 Maxime Ripard <maxime.ripard@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 version 2 as
* published by the Free Software Foundation.
*/
#include <linux/bitops.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/slab.h>
#define to_clk_multiplier(_hw) container_of(_hw, struct clk_multiplier, hw)
static unsigned long __get_mult(struct clk_multiplier *mult,
unsigned long rate,
unsigned long parent_rate)
{
if (mult->flags & CLK_MULTIPLIER_ROUND_CLOSEST)
return DIV_ROUND_CLOSEST(rate, parent_rate);
return rate / parent_rate;
}
static unsigned long clk_multiplier_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_multiplier *mult = to_clk_multiplier(hw);
unsigned long val;
val = clk_readl(mult->reg) >> mult->shift;
val &= GENMASK(mult->width - 1, 0);
if (!val && mult->flags & CLK_MULTIPLIER_ZERO_BYPASS)
val = 1;
return parent_rate * val;
}
static bool __is_best_rate(unsigned long rate, unsigned long new,
unsigned long best, unsigned long flags)
{
if (flags & CLK_MULTIPLIER_ROUND_CLOSEST)
return abs(rate - new) < abs(rate - best);
return new >= rate && new < best;
}
static unsigned long __bestmult(struct clk_hw *hw, unsigned long rate,
unsigned long *best_parent_rate,
u8 width, unsigned long flags)
{
unsigned long orig_parent_rate = *best_parent_rate;
unsigned long parent_rate, current_rate, best_rate = ~0;
unsigned int i, bestmult = 0;
if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT))
return rate / *best_parent_rate;
for (i = 1; i < ((1 << width) - 1); i++) {
if (rate == orig_parent_rate * i) {
/*
* This is the best case for us if we have a
* perfect match without changing the parent
* rate.
*/
*best_parent_rate = orig_parent_rate;
return i;
}
parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
rate / i);
current_rate = parent_rate * i;
if (__is_best_rate(rate, current_rate, best_rate, flags)) {
bestmult = i;
best_rate = current_rate;
*best_parent_rate = parent_rate;
}
}
return bestmult;
}
static long clk_multiplier_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct clk_multiplier *mult = to_clk_multiplier(hw);
unsigned long factor = __bestmult(hw, rate, parent_rate,
mult->width, mult->flags);
return *parent_rate * factor;
}
static int clk_multiplier_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_multiplier *mult = to_clk_multiplier(hw);
unsigned long factor = __get_mult(mult, rate, parent_rate);
unsigned long flags = 0;
unsigned long val;
if (mult->lock)
spin_lock_irqsave(mult->lock, flags);
else
__acquire(mult->lock);
val = clk_readl(mult->reg);
val &= ~GENMASK(mult->width + mult->shift - 1, mult->shift);
val |= factor << mult->shift;
clk_writel(val, mult->reg);
if (mult->lock)
spin_unlock_irqrestore(mult->lock, flags);
else
__release(mult->lock);
return 0;
}
const struct clk_ops clk_multiplier_ops = {
.recalc_rate = clk_multiplier_recalc_rate,
.round_rate = clk_multiplier_round_rate,
.set_rate = clk_multiplier_set_rate,
};
EXPORT_SYMBOL_GPL(clk_multiplier_ops);
/*
* Driver for Silicon Labs Si514 Programmable Oscillator
*
* Copyright (C) 2015 Topic Embedded Products
*
* Author: Mike Looijmans <mike.looijmans@topic.nl>
*
* 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.
*/
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
/* I2C registers */
#define SI514_REG_LP 0
#define SI514_REG_M_FRAC1 5
#define SI514_REG_M_FRAC2 6
#define SI514_REG_M_FRAC3 7
#define SI514_REG_M_INT_FRAC 8
#define SI514_REG_M_INT 9
#define SI514_REG_HS_DIV 10
#define SI514_REG_LS_HS_DIV 11
#define SI514_REG_OE_STATE 14
#define SI514_REG_RESET 128
#define SI514_REG_CONTROL 132
/* Register values */
#define SI514_RESET_RST BIT(7)
#define SI514_CONTROL_FCAL BIT(0)
#define SI514_CONTROL_OE BIT(2)
#define SI514_MIN_FREQ 100000U
#define SI514_MAX_FREQ 250000000U
#define FXO 31980000U
#define FVCO_MIN 2080000000U
#define FVCO_MAX 2500000000U
#define HS_DIV_MAX 1022
struct clk_si514 {
struct clk_hw hw;
struct regmap *regmap;
struct i2c_client *i2c_client;
};
#define to_clk_si514(_hw) container_of(_hw, struct clk_si514, hw)
/* Multiplier/divider settings */
struct clk_si514_muldiv {
u32 m_frac; /* 29-bit Fractional part of multiplier M */
u8 m_int; /* Integer part of multiplier M, 65..78 */
u8 ls_div_bits; /* 2nd divider, as 2^x */
u16 hs_div; /* 1st divider, must be even and 10<=x<=1022 */
};
/* Enables or disables the output driver */
static int si514_enable_output(struct clk_si514 *data, bool enable)
{
return regmap_update_bits(data->regmap, SI514_REG_CONTROL,
SI514_CONTROL_OE, enable ? SI514_CONTROL_OE : 0);
}
/* Retrieve clock multiplier and dividers from hardware */
static int si514_get_muldiv(struct clk_si514 *data,
struct clk_si514_muldiv *settings)
{
int err;
u8 reg[7];
err = regmap_bulk_read(data->regmap, SI514_REG_M_FRAC1,
reg, ARRAY_SIZE(reg));
if (err)
return err;
settings->m_frac = reg[0] | reg[1] << 8 | reg[2] << 16 |
(reg[3] & 0x1F) << 24;
settings->m_int = (reg[4] & 0x3f) << 3 | reg[3] >> 5;
settings->ls_div_bits = (reg[6] >> 4) & 0x07;
settings->hs_div = (reg[6] & 0x03) << 8 | reg[5];
return 0;
}
static int si514_set_muldiv(struct clk_si514 *data,
struct clk_si514_muldiv *settings)
{
u8 lp;
u8 reg[7];
int err;
/* Calculate LP1/LP2 according to table 13 in the datasheet */
/* 65.259980246 */
if (settings->m_int < 65 ||
(settings->m_int == 65 && settings->m_frac <= 139575831))
lp = 0x22;
/* 67.859763463 */
else if (settings->m_int < 67 ||
(settings->m_int == 67 && settings->m_frac <= 461581994))
lp = 0x23;
/* 72.937624981 */
else if (settings->m_int < 72 ||
(settings->m_int == 72 && settings->m_frac <= 503383578))
lp = 0x33;
/* 75.843265046 */
else if (settings->m_int < 75 ||
(settings->m_int == 75 && settings->m_frac <= 452724474))
lp = 0x34;
else
lp = 0x44;
err = regmap_write(data->regmap, SI514_REG_LP, lp);
if (err < 0)
return err;
reg[0] = settings->m_frac;
reg[1] = settings->m_frac >> 8;
reg[2] = settings->m_frac >> 16;
reg[3] = settings->m_frac >> 24 | settings->m_int << 5;
reg[4] = settings->m_int >> 3;
reg[5] = settings->hs_div;
reg[6] = (settings->hs_div >> 8) | (settings->ls_div_bits << 4);
err = regmap_bulk_write(data->regmap, SI514_REG_HS_DIV, reg + 5, 2);
if (err < 0)
return err;
/*
* Writing to SI514_REG_M_INT_FRAC triggers the clock change, so that
* must be written last
*/
return regmap_bulk_write(data->regmap, SI514_REG_M_FRAC1, reg, 5);
}
/* Calculate divider settings for a given frequency */
static int si514_calc_muldiv(struct clk_si514_muldiv *settings,
unsigned long frequency)
{
u64 m;
u32 ls_freq;
u32 tmp;
u8 res;
if ((frequency < SI514_MIN_FREQ) || (frequency > SI514_MAX_FREQ))
return -EINVAL;
/* Determine the minimum value of LS_DIV and resulting target freq. */
ls_freq = frequency;
if (frequency >= (FVCO_MIN / HS_DIV_MAX))
settings->ls_div_bits = 0;
else {
res = 1;
tmp = 2 * HS_DIV_MAX;
while (tmp <= (HS_DIV_MAX * 32)) {
if ((frequency * tmp) >= FVCO_MIN)
break;
++res;
tmp <<= 1;
}
settings->ls_div_bits = res;
ls_freq = frequency << res;
}
/* Determine minimum HS_DIV, round up to even number */
settings->hs_div = DIV_ROUND_UP(FVCO_MIN >> 1, ls_freq) << 1;
/* M = LS_DIV x HS_DIV x frequency / F_XO (in fixed-point) */
m = ((u64)(ls_freq * settings->hs_div) << 29) + (FXO / 2);
do_div(m, FXO);
settings->m_frac = (u32)m & (BIT(29) - 1);
settings->m_int = (u32)(m >> 29);
return 0;
}
/* Calculate resulting frequency given the register settings */
static unsigned long si514_calc_rate(struct clk_si514_muldiv *settings)
{
u64 m = settings->m_frac | ((u64)settings->m_int << 29);
u32 d = settings->hs_div * BIT(settings->ls_div_bits);
return ((u32)(((m * FXO) + (FXO / 2)) >> 29)) / d;
}
static unsigned long si514_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_si514 *data = to_clk_si514(hw);
struct clk_si514_muldiv settings;
int err;
err = si514_get_muldiv(data, &settings);
if (err) {
dev_err(&data->i2c_client->dev, "unable to retrieve settings\n");
return 0;
}
return si514_calc_rate(&settings);
}
static long si514_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct clk_si514_muldiv settings;
int err;
if (!rate)
return 0;
err = si514_calc_muldiv(&settings, rate);
if (err)
return err;
return si514_calc_rate(&settings);
}
/*
* Update output frequency for big frequency changes (> 1000 ppm).
* The chip supports <1000ppm changes "on the fly", we haven't implemented
* that here.
*/
static int si514_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_si514 *data = to_clk_si514(hw);
struct clk_si514_muldiv settings;
int err;
err = si514_calc_muldiv(&settings, rate);
if (err)
return err;
si514_enable_output(data, false);
err = si514_set_muldiv(data, &settings);
if (err < 0)
return err; /* Undefined state now, best to leave disabled */
/* Trigger calibration */
err = regmap_write(data->regmap, SI514_REG_CONTROL, SI514_CONTROL_FCAL);
if (err < 0)
return err;
/* Applying a new frequency can take up to 10ms */
usleep_range(10000, 12000);
si514_enable_output(data, true);
return err;
}
static const struct clk_ops si514_clk_ops = {
.recalc_rate = si514_recalc_rate,
.round_rate = si514_round_rate,
.set_rate = si514_set_rate,
};
static bool si514_regmap_is_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case SI514_REG_CONTROL:
case SI514_REG_RESET:
return true;
default:
return false;
}
}
static bool si514_regmap_is_writeable(struct device *dev, unsigned int reg)
{
switch (reg) {
case SI514_REG_LP:
case SI514_REG_M_FRAC1 ... SI514_REG_LS_HS_DIV:
case SI514_REG_OE_STATE:
case SI514_REG_RESET:
case SI514_REG_CONTROL:
return true;
default:
return false;
}
}
static const struct regmap_config si514_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.cache_type = REGCACHE_RBTREE,
.max_register = SI514_REG_CONTROL,
.writeable_reg = si514_regmap_is_writeable,
.volatile_reg = si514_regmap_is_volatile,
};
static int si514_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct clk_si514 *data;
struct clk_init_data init;
struct clk *clk;
int err;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
init.ops = &si514_clk_ops;
init.flags = CLK_IS_ROOT;
init.num_parents = 0;
data->hw.init = &init;
data->i2c_client = client;
if (of_property_read_string(client->dev.of_node, "clock-output-names",
&init.name))
init.name = client->dev.of_node->name;
data->regmap = devm_regmap_init_i2c(client, &si514_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(&client->dev, "failed to allocate register map\n");
return PTR_ERR(data->regmap);
}
i2c_set_clientdata(client, data);
clk = devm_clk_register(&client->dev, &data->hw);
if (IS_ERR(clk)) {
dev_err(&client->dev, "clock registration failed\n");
return PTR_ERR(clk);
}
err = of_clk_add_provider(client->dev.of_node, of_clk_src_simple_get,
clk);
if (err) {
dev_err(&client->dev, "unable to add clk provider\n");
return err;
}
return 0;
}
static int si514_remove(struct i2c_client *client)
{
of_clk_del_provider(client->dev.of_node);
return 0;
}
static const struct i2c_device_id si514_id[] = {
{ "si514", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, si514_id);
static const struct of_device_id clk_si514_of_match[] = {
{ .compatible = "silabs,si514" },
{ },
};
MODULE_DEVICE_TABLE(of, clk_si514_of_match);
static struct i2c_driver si514_driver = {
.driver = {
.name = "si514",
.of_match_table = clk_si514_of_match,
},
.probe = si514_probe,
.remove = si514_remove,
.id_table = si514_id,
};
module_i2c_driver(si514_driver);
MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
MODULE_DESCRIPTION("Si514 driver");
MODULE_LICENSE("GPL");
......@@ -1183,13 +1183,13 @@ static int si5351_dt_parse(struct i2c_client *client,
if (of_property_read_u32(child, "reg", &num)) {
dev_err(&client->dev, "missing reg property of %s\n",
child->name);
return -EINVAL;
goto put_child;
}
if (num >= 8 ||
(variant == SI5351_VARIANT_A3 && num >= 3)) {
dev_err(&client->dev, "invalid clkout %d\n", num);
return -EINVAL;
goto put_child;
}
if (!of_property_read_u32(child, "silabs,multisynth-source",
......@@ -1207,7 +1207,7 @@ static int si5351_dt_parse(struct i2c_client *client,
dev_err(&client->dev,
"invalid parent %d for multisynth %d\n",
val, num);
return -EINVAL;
goto put_child;
}
}
......@@ -1230,7 +1230,7 @@ static int si5351_dt_parse(struct i2c_client *client,
dev_err(&client->dev,
"invalid parent %d for clkout %d\n",
val, num);
return -EINVAL;
goto put_child;
}
pdata->clkout[num].clkout_src =
SI5351_CLKOUT_SRC_CLKIN;
......@@ -1239,7 +1239,7 @@ static int si5351_dt_parse(struct i2c_client *client,
dev_err(&client->dev,
"invalid parent %d for clkout %d\n",
val, num);
return -EINVAL;
goto put_child;
}
}
......@@ -1256,7 +1256,7 @@ static int si5351_dt_parse(struct i2c_client *client,
dev_err(&client->dev,
"invalid drive strength %d for clkout %d\n",
val, num);
return -EINVAL;
goto put_child;
}
}
......@@ -1283,7 +1283,7 @@ static int si5351_dt_parse(struct i2c_client *client,
dev_err(&client->dev,
"invalid disable state %d for clkout %d\n",
val, num);
return -EINVAL;
goto put_child;
}
}
......@@ -1296,6 +1296,9 @@ static int si5351_dt_parse(struct i2c_client *client,
client->dev.platform_data = pdata;
return 0;
put_child:
of_node_put(child);
return -EINVAL;
}
#else
static int si5351_dt_parse(struct i2c_client *client, enum si5351_variant variant)
......
......@@ -27,7 +27,6 @@
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/of_address.h>
#include <asm/setup.h>
/* Register SCU_PCPPLL bit fields */
#define N_DIV_RD(src) (((src) & 0x000001ff))
......
......@@ -272,7 +272,7 @@ late_initcall_sync(clk_disable_unused);
/*** helper functions ***/
const char *__clk_get_name(struct clk *clk)
const char *__clk_get_name(const struct clk *clk)
{
return !clk ? NULL : clk->core->name;
}
......@@ -427,6 +427,11 @@ bool clk_hw_is_prepared(const struct clk_hw *hw)
return clk_core_is_prepared(hw->core);
}
bool clk_hw_is_enabled(const struct clk_hw *hw)
{
return clk_core_is_enabled(hw->core);
}
bool __clk_is_enabled(struct clk *clk)
{
if (!clk)
......@@ -1685,7 +1690,7 @@ static struct clk_core *__clk_init_parent(struct clk_core *core)
"%s: multi-parent clocks must implement .get_parent\n",
__func__);
goto out;
};
}
/*
* Do our best to cache parent clocks in core->parents. This prevents
......@@ -2932,7 +2937,7 @@ struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data)
unsigned int idx = clkspec->args[0];
if (idx >= clk_data->clk_num) {
pr_err("%s: invalid clock index %d\n", __func__, idx);
pr_err("%s: invalid clock index %u\n", __func__, idx);
return ERR_PTR(-EINVAL);
}
......@@ -3055,6 +3060,7 @@ const char *of_clk_get_parent_name(struct device_node *np, int index)
u32 pv;
int rc;
int count;
struct clk *clk;
if (index < 0)
return NULL;
......@@ -3080,8 +3086,25 @@ const char *of_clk_get_parent_name(struct device_node *np, int index)
if (of_property_read_string_index(clkspec.np, "clock-output-names",
index,
&clk_name) < 0)
clk_name = clkspec.np->name;
&clk_name) < 0) {
/*
* Best effort to get the name if the clock has been
* registered with the framework. If the clock isn't
* registered, we return the node name as the name of
* the clock as long as #clock-cells = 0.
*/
clk = of_clk_get_from_provider(&clkspec);
if (IS_ERR(clk)) {
if (clkspec.args_count == 0)
clk_name = clkspec.np->name;
else
clk_name = NULL;
} else {
clk_name = __clk_get_name(clk);
clk_put(clk);
}
}
of_node_put(clkspec.np);
return clk_name;
......@@ -3179,13 +3202,15 @@ void __init of_clk_init(const struct of_device_id *matches)
list_for_each_entry_safe(clk_provider, next,
&clk_provider_list, node) {
list_del(&clk_provider->node);
of_node_put(clk_provider->np);
kfree(clk_provider);
}
of_node_put(np);
return;
}
parent->clk_init_cb = match->data;
parent->np = np;
parent->np = of_node_get(np);
list_add_tail(&parent->node, &clk_provider_list);
}
......@@ -3199,6 +3224,7 @@ void __init of_clk_init(const struct of_device_id *matches)
of_clk_set_defaults(clk_provider->np, true);
list_del(&clk_provider->node);
of_node_put(clk_provider->np);
kfree(clk_provider);
is_init_done = true;
}
......
......@@ -230,7 +230,7 @@ static int hi6220_stub_clk_probe(struct platform_device *pdev)
if (IS_ERR(stub_clk->mbox)) {
dev_err(dev, "failed get mailbox channel\n");
return PTR_ERR(stub_clk->mbox);
};
}
init.name = "acpu0";
init.ops = &hi6220_stub_clk_ops;
......
......@@ -86,6 +86,16 @@ enum mx25_clks {
static struct clk *clk[clk_max];
static struct clk ** const uart_clks[] __initconst = {
&clk[uart_ipg_per],
&clk[uart1_ipg],
&clk[uart2_ipg],
&clk[uart3_ipg],
&clk[uart4_ipg],
&clk[uart5_ipg],
NULL
};
static int __init __mx25_clocks_init(unsigned long osc_rate,
void __iomem *ccm_base)
{
......@@ -233,6 +243,8 @@ static int __init __mx25_clocks_init(unsigned long osc_rate,
*/
clk_set_parent(clk[cko_sel], clk[ipg]);
imx_register_uart_clocks(uart_clks);
return 0;
}
......
......@@ -47,6 +47,17 @@ static const char *ssi_sel_clks[] = { "spll_gate", "mpll", };
static struct clk *clk[IMX27_CLK_MAX];
static struct clk_onecell_data clk_data;
static struct clk ** const uart_clks[] __initconst = {
&clk[IMX27_CLK_PER1_GATE],
&clk[IMX27_CLK_UART1_IPG_GATE],
&clk[IMX27_CLK_UART2_IPG_GATE],
&clk[IMX27_CLK_UART3_IPG_GATE],
&clk[IMX27_CLK_UART4_IPG_GATE],
&clk[IMX27_CLK_UART5_IPG_GATE],
&clk[IMX27_CLK_UART6_IPG_GATE],
NULL
};
static void __init _mx27_clocks_init(unsigned long fref)
{
BUG_ON(!ccm);
......@@ -163,6 +174,8 @@ static void __init _mx27_clocks_init(unsigned long fref)
clk_prepare_enable(clk[IMX27_CLK_EMI_AHB_GATE]);
imx_register_uart_clocks(uart_clks);
imx_print_silicon_rev("i.MX27", mx27_revision());
}
......@@ -248,8 +261,10 @@ static void __init mx27_clocks_init_dt(struct device_node *np)
if (!of_device_is_compatible(refnp, "fsl,imx-osc26m"))
continue;
if (!of_property_read_u32(refnp, "clock-frequency", &fref))
if (!of_property_read_u32(refnp, "clock-frequency", &fref)) {
of_node_put(refnp);
break;
}
}
ccm = of_iomap(np, 0);
......
......@@ -62,7 +62,17 @@ enum mx31_clks {
static struct clk *clk[clk_max];
static struct clk_onecell_data clk_data;
int __init mx31_clocks_init(unsigned long fref)
static struct clk ** const uart_clks[] __initconst = {
&clk[ipg],
&clk[uart1_gate],
&clk[uart2_gate],
&clk[uart3_gate],
&clk[uart4_gate],
&clk[uart5_gate],
NULL
};
static void __init _mx31_clocks_init(unsigned long fref)
{
void __iomem *base;
struct device_node *np;
......@@ -132,6 +142,12 @@ int __init mx31_clocks_init(unsigned long fref)
imx_check_clocks(clk, ARRAY_SIZE(clk));
clk_set_parent(clk[csi], clk[upll]);
clk_prepare_enable(clk[emi_gate]);
clk_prepare_enable(clk[iim_gate]);
mx31_revision();
clk_disable_unprepare(clk[iim_gate]);
np = of_find_compatible_node(NULL, NULL, "fsl,imx31-ccm");
if (np) {
......@@ -139,6 +155,13 @@ int __init mx31_clocks_init(unsigned long fref)
clk_data.clk_num = ARRAY_SIZE(clk);
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
}
}
int __init mx31_clocks_init(void)
{
u32 fref = 26000000; /* default */
_mx31_clocks_init(fref);
clk_register_clkdev(clk[gpt_gate], "per", "imx-gpt.0");
clk_register_clkdev(clk[ipg], "ipg", "imx-gpt.0");
......@@ -194,12 +217,8 @@ int __init mx31_clocks_init(unsigned long fref)
clk_register_clkdev(clk[sdma_gate], NULL, "imx31-sdma");
clk_register_clkdev(clk[iim_gate], "iim", NULL);
clk_set_parent(clk[csi], clk[upll]);
clk_prepare_enable(clk[emi_gate]);
clk_prepare_enable(clk[iim_gate]);
mx31_revision();
clk_disable_unprepare(clk[iim_gate]);
imx_register_uart_clocks(uart_clks);
mxc_timer_init(MX31_GPT1_BASE_ADDR, MX31_INT_GPT, GPT_TYPE_IMX31);
return 0;
......@@ -214,9 +233,13 @@ int __init mx31_clocks_init_dt(void)
if (!of_device_is_compatible(np, "fsl,imx-osc26m"))
continue;
if (!of_property_read_u32(np, "clock-frequency", &fref))
if (!of_property_read_u32(np, "clock-frequency", &fref)) {
of_node_put(np);
break;
}
}
return mx31_clocks_init(fref);
_mx31_clocks_init(fref);
return 0;
}
......@@ -84,7 +84,15 @@ enum mx35_clks {
static struct clk *clk[clk_max];
int __init mx35_clocks_init(void)
static struct clk ** const uart_clks[] __initconst = {
&clk[ipg],
&clk[uart1_gate],
&clk[uart2_gate],
&clk[uart3_gate],
NULL
};
static void __init _mx35_clocks_init(void)
{
void __iomem *base;
u32 pdr0, consumer_sel, hsp_sel;
......@@ -220,6 +228,32 @@ int __init mx35_clocks_init(void)
imx_check_clocks(clk, ARRAY_SIZE(clk));
clk_prepare_enable(clk[spba_gate]);
clk_prepare_enable(clk[gpio1_gate]);
clk_prepare_enable(clk[gpio2_gate]);
clk_prepare_enable(clk[gpio3_gate]);
clk_prepare_enable(clk[iim_gate]);
clk_prepare_enable(clk[emi_gate]);
clk_prepare_enable(clk[max_gate]);
clk_prepare_enable(clk[iomuxc_gate]);
/*
* SCC is needed to boot via mmc after a watchdog reset. The clock code
* before conversion to common clk also enabled UART1 (which isn't
* handled here and not needed for mmc) and IIM (which is enabled
* unconditionally above).
*/
clk_prepare_enable(clk[scc_gate]);
imx_register_uart_clocks(uart_clks);
imx_print_silicon_rev("i.MX35", mx35_revision());
}
int __init mx35_clocks_init(void)
{
_mx35_clocks_init();
clk_register_clkdev(clk[pata_gate], NULL, "pata_imx");
clk_register_clkdev(clk[can1_gate], NULL, "flexcan.0");
clk_register_clkdev(clk[can2_gate], NULL, "flexcan.1");
......@@ -279,25 +313,6 @@ int __init mx35_clocks_init(void)
clk_register_clkdev(clk[csi_gate], NULL, "mx3-camera.0");
clk_register_clkdev(clk[admux_gate], "audmux", NULL);
clk_prepare_enable(clk[spba_gate]);
clk_prepare_enable(clk[gpio1_gate]);
clk_prepare_enable(clk[gpio2_gate]);
clk_prepare_enable(clk[gpio3_gate]);
clk_prepare_enable(clk[iim_gate]);
clk_prepare_enable(clk[emi_gate]);
clk_prepare_enable(clk[max_gate]);
clk_prepare_enable(clk[iomuxc_gate]);
/*
* SCC is needed to boot via mmc after a watchdog reset. The clock code
* before conversion to common clk also enabled UART1 (which isn't
* handled here and not needed for mmc) and IIM (which is enabled
* unconditionally above).
*/
clk_prepare_enable(clk[scc_gate]);
imx_print_silicon_rev("i.MX35", mx35_revision());
mxc_timer_init(MX35_GPT1_BASE_ADDR, MX35_INT_GPT, GPT_TYPE_IMX31);
return 0;
......@@ -305,10 +320,10 @@ int __init mx35_clocks_init(void)
static void __init mx35_clocks_init_dt(struct device_node *ccm_node)
{
_mx35_clocks_init();
clk_data.clks = clk;
clk_data.clk_num = ARRAY_SIZE(clk);
of_clk_add_provider(ccm_node, of_clk_src_onecell_get, &clk_data);
mx35_clocks_init();
}
CLK_OF_DECLARE(imx35, "fsl,imx35-ccm", mx35_clocks_init_dt);
......@@ -130,6 +130,20 @@ static const char *cpu_podf_sels[] = { "pll1_sw", "step_sel" };
static struct clk *clk[IMX5_CLK_END];
static struct clk_onecell_data clk_data;
static struct clk ** const uart_clks[] __initconst = {
&clk[IMX5_CLK_UART1_IPG_GATE],
&clk[IMX5_CLK_UART1_PER_GATE],
&clk[IMX5_CLK_UART2_IPG_GATE],
&clk[IMX5_CLK_UART2_PER_GATE],
&clk[IMX5_CLK_UART3_IPG_GATE],
&clk[IMX5_CLK_UART3_PER_GATE],
&clk[IMX5_CLK_UART4_IPG_GATE],
&clk[IMX5_CLK_UART4_PER_GATE],
&clk[IMX5_CLK_UART5_IPG_GATE],
&clk[IMX5_CLK_UART5_PER_GATE],
NULL
};
static void __init mx5_clocks_common_init(void __iomem *ccm_base)
{
clk[IMX5_CLK_DUMMY] = imx_clk_fixed("dummy", 0);
......@@ -310,6 +324,8 @@ static void __init mx5_clocks_common_init(void __iomem *ccm_base)
clk_prepare_enable(clk[IMX5_CLK_TMAX1]);
clk_prepare_enable(clk[IMX5_CLK_TMAX2]); /* esdhc2, fec */
clk_prepare_enable(clk[IMX5_CLK_TMAX3]); /* esdhc1, esdhc4 */
imx_register_uart_clocks(uart_clks);
}
static void __init mx50_clocks_init(struct device_node *np)
......
......@@ -119,6 +119,7 @@ static unsigned int share_count_ssi1;
static unsigned int share_count_ssi2;
static unsigned int share_count_ssi3;
static unsigned int share_count_mipi_core_cfg;
static unsigned int share_count_spdif;
static inline int clk_on_imx6q(void)
{
......@@ -130,6 +131,12 @@ static inline int clk_on_imx6dl(void)
return of_machine_is_compatible("fsl,imx6dl");
}
static struct clk ** const uart_clks[] __initconst = {
&clk[IMX6QDL_CLK_UART_IPG],
&clk[IMX6QDL_CLK_UART_SERIAL],
NULL
};
static void __init imx6q_clocks_init(struct device_node *ccm_node)
{
struct device_node *np;
......@@ -456,7 +463,8 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk[IMX6QDL_CLK_SATA] = imx_clk_gate2("sata", "ahb", base + 0x7c, 4);
clk[IMX6QDL_CLK_SDMA] = imx_clk_gate2("sdma", "ahb", base + 0x7c, 6);
clk[IMX6QDL_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12);
clk[IMX6QDL_CLK_SPDIF] = imx_clk_gate2("spdif", "spdif_podf", base + 0x7c, 14);
clk[IMX6QDL_CLK_SPDIF] = imx_clk_gate2_shared("spdif", "spdif_podf", base + 0x7c, 14, &share_count_spdif);
clk[IMX6QDL_CLK_SPDIF_GCLK] = imx_clk_gate2_shared("spdif_gclk", "ipg", base + 0x7c, 14, &share_count_spdif);
clk[IMX6QDL_CLK_SSI1_IPG] = imx_clk_gate2_shared("ssi1_ipg", "ipg", base + 0x7c, 18, &share_count_ssi1);
clk[IMX6QDL_CLK_SSI2_IPG] = imx_clk_gate2_shared("ssi2_ipg", "ipg", base + 0x7c, 20, &share_count_ssi2);
clk[IMX6QDL_CLK_SSI3_IPG] = imx_clk_gate2_shared("ssi3_ipg", "ipg", base + 0x7c, 22, &share_count_ssi3);
......@@ -541,5 +549,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
/* All existing boards with PCIe use LVDS1 */
if (IS_ENABLED(CONFIG_PCI_IMX6))
clk_set_parent(clk[IMX6QDL_CLK_LVDS1_SEL], clk[IMX6QDL_CLK_SATA_REF_100M]);
imx_register_uart_clocks(uart_clks);
}
CLK_OF_DECLARE(imx6q, "fsl,imx6q-ccm", imx6q_clocks_init);
......@@ -97,6 +97,7 @@ static struct clk_div_table video_div_table[] = {
static unsigned int share_count_ssi1;
static unsigned int share_count_ssi2;
static unsigned int share_count_ssi3;
static unsigned int share_count_spdif;
static struct clk *clks[IMX6SL_CLK_END];
static struct clk_onecell_data clk_data;
......@@ -184,6 +185,12 @@ void imx6sl_set_wait_clk(bool enter)
imx6sl_enable_pll_arm(false);
}
static struct clk ** const uart_clks[] __initconst = {
&clks[IMX6SL_CLK_UART],
&clks[IMX6SL_CLK_UART_SERIAL],
NULL
};
static void __init imx6sl_clocks_init(struct device_node *ccm_node)
{
struct device_node *np;
......@@ -391,7 +398,8 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
clks[IMX6SL_CLK_PWM4] = imx_clk_gate2("pwm4", "perclk", base + 0x78, 22);
clks[IMX6SL_CLK_SDMA] = imx_clk_gate2("sdma", "ipg", base + 0x7c, 6);
clks[IMX6SL_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12);
clks[IMX6SL_CLK_SPDIF] = imx_clk_gate2("spdif", "spdif0_podf", base + 0x7c, 14);
clks[IMX6SL_CLK_SPDIF] = imx_clk_gate2_shared("spdif", "spdif0_podf", base + 0x7c, 14, &share_count_spdif);
clks[IMX6SL_CLK_SPDIF_GCLK] = imx_clk_gate2_shared("spdif_gclk", "ipg", base + 0x7c, 14, &share_count_spdif);
clks[IMX6SL_CLK_SSI1_IPG] = imx_clk_gate2_shared("ssi1_ipg", "ipg", base + 0x7c, 18, &share_count_ssi1);
clks[IMX6SL_CLK_SSI2_IPG] = imx_clk_gate2_shared("ssi2_ipg", "ipg", base + 0x7c, 20, &share_count_ssi2);
clks[IMX6SL_CLK_SSI3_IPG] = imx_clk_gate2_shared("ssi3_ipg", "ipg", base + 0x7c, 22, &share_count_ssi3);
......@@ -439,5 +447,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
clk_set_parent(clks[IMX6SL_CLK_LCDIF_AXI_SEL],
clks[IMX6SL_CLK_PLL2_PFD2]);
imx_register_uart_clocks(uart_clks);
}
CLK_OF_DECLARE(imx6sl, "fsl,imx6sl-ccm", imx6sl_clocks_init);
......@@ -135,6 +135,12 @@ static u32 share_count_ssi1;
static u32 share_count_ssi2;
static u32 share_count_ssi3;
static struct clk ** const uart_clks[] __initconst = {
&clks[IMX6SX_CLK_UART_IPG],
&clks[IMX6SX_CLK_UART_SERIAL],
NULL
};
static void __init imx6sx_clocks_init(struct device_node *ccm_node)
{
struct device_node *np;
......@@ -454,6 +460,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
clks[IMX6SX_CLK_SPBA] = imx_clk_gate2("spba", "ipg", base + 0x7c, 12);
clks[IMX6SX_CLK_AUDIO] = imx_clk_gate2_shared("audio", "audio_podf", base + 0x7c, 14, &share_count_audio);
clks[IMX6SX_CLK_SPDIF] = imx_clk_gate2_shared("spdif", "spdif_podf", base + 0x7c, 14, &share_count_audio);
clks[IMX6SX_CLK_SPDIF_GCLK] = imx_clk_gate2_shared("spdif_gclk", "ipg", base + 0x7c, 14, &share_count_audio);
clks[IMX6SX_CLK_SSI1_IPG] = imx_clk_gate2_shared("ssi1_ipg", "ipg", base + 0x7c, 18, &share_count_ssi1);
clks[IMX6SX_CLK_SSI2_IPG] = imx_clk_gate2_shared("ssi2_ipg", "ipg", base + 0x7c, 20, &share_count_ssi2);
clks[IMX6SX_CLK_SSI3_IPG] = imx_clk_gate2_shared("ssi3_ipg", "ipg", base + 0x7c, 22, &share_count_ssi3);
......@@ -557,5 +564,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
clk_set_parent(clks[IMX6SX_CLK_QSPI1_SEL], clks[IMX6SX_CLK_PLL2_BUS]);
clk_set_parent(clks[IMX6SX_CLK_QSPI2_SEL], clks[IMX6SX_CLK_PLL2_BUS]);
imx_register_uart_clocks(uart_clks);
}
CLK_OF_DECLARE(imx6sx, "fsl,imx6sx-ccm", imx6sx_clocks_init);
......@@ -407,6 +407,24 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
clk_data.clk_num = ARRAY_SIZE(clks);
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
/*
* Lower the AHB clock rate before changing the parent clock source,
* as AHB clock rate can NOT be higher than 133MHz, but its parent
* will be switched from 396MHz PFD to 528MHz PLL in order to increase
* AXI clock rate, so we need to lower AHB rate first to make sure at
* any time, AHB rate is <= 133MHz.
*/
clk_set_rate(clks[IMX6UL_CLK_AHB], 99000000);
/* Change periph_pre clock to pll2_bus to adjust AXI rate to 264MHz */
clk_set_parent(clks[IMX6UL_CLK_PERIPH_CLK2_SEL], clks[IMX6UL_CLK_PLL3_USB_OTG]);
clk_set_parent(clks[IMX6UL_CLK_PERIPH], clks[IMX6UL_CLK_PERIPH_CLK2]);
clk_set_parent(clks[IMX6UL_CLK_PERIPH_PRE], clks[IMX6UL_CLK_PLL2_BUS]);
clk_set_parent(clks[IMX6UL_CLK_PERIPH], clks[IMX6UL_CLK_PERIPH_PRE]);
/* Make sure AHB rate is 132MHz */
clk_set_rate(clks[IMX6UL_CLK_AHB], 132000000);
/* set perclk to from OSC */
clk_set_parent(clks[IMX6UL_CLK_PERCLK_SEL], clks[IMX6UL_CLK_OSC]);
......
......@@ -363,6 +363,17 @@ static const char *pll_video_bypass_sel[] = { "pll_video_main", "pll_video_main_
static struct clk_onecell_data clk_data;
static struct clk ** const uart_clks[] __initconst = {
&clks[IMX7D_UART1_ROOT_CLK],
&clks[IMX7D_UART2_ROOT_CLK],
&clks[IMX7D_UART3_ROOT_CLK],
&clks[IMX7D_UART4_ROOT_CLK],
&clks[IMX7D_UART5_ROOT_CLK],
&clks[IMX7D_UART6_ROOT_CLK],
&clks[IMX7D_UART7_ROOT_CLK],
NULL
};
static void __init imx7d_clocks_init(struct device_node *ccm_node)
{
struct device_node *np;
......@@ -818,6 +829,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
clks[IMX7D_CSI_MCLK_ROOT_CLK] = imx_clk_gate2("csi_mclk_root_clk", "csi_mclk_post_div", base + 0x4490, 0);
clks[IMX7D_AUDIO_MCLK_ROOT_CLK] = imx_clk_gate2("audio_mclk_root_clk", "audio_mclk_post_div", base + 0x4790, 0);
clks[IMX7D_WRCLK_ROOT_CLK] = imx_clk_gate2("wrclk_root_clk", "wrclk_post_div", base + 0x47a0, 0);
clks[IMX7D_ADC_ROOT_CLK] = imx_clk_gate2("adc_root_clk", "ipg_root_clk", base + 0x4200, 0);
clks[IMX7D_GPT_3M_CLK] = imx_clk_fixed_factor("gpt_3m", "osc", 1, 8);
......@@ -856,5 +868,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
/* set uart module clock's parent clock source that must be great then 80MHz */
clk_set_parent(clks[IMX7D_UART1_ROOT_SRC], clks[IMX7D_OSC_24M_CLK]);
imx_register_uart_clocks(uart_clks);
}
CLK_OF_DECLARE(imx7d, "fsl,imx7d-ccm", imx7d_clocks_init);
......@@ -77,7 +77,7 @@ struct clk_pllv2 {
static unsigned long __clk_pllv2_recalc_rate(unsigned long parent_rate,
u32 dp_ctl, u32 dp_op, u32 dp_mfd, u32 dp_mfn)
{
long mfi, mfn, mfd, pdf, ref_clk, mfn_abs;
long mfi, mfn, mfd, pdf, ref_clk;
unsigned long dbl;
s64 temp;
......@@ -87,19 +87,15 @@ static unsigned long __clk_pllv2_recalc_rate(unsigned long parent_rate,
mfi = (dp_op & MXC_PLL_DP_OP_MFI_MASK) >> MXC_PLL_DP_OP_MFI_OFFSET;
mfi = (mfi <= 5) ? 5 : mfi;
mfd = dp_mfd & MXC_PLL_DP_MFD_MASK;
mfn = mfn_abs = dp_mfn & MXC_PLL_DP_MFN_MASK;
/* Sign extend to 32-bits */
if (mfn >= 0x04000000) {
mfn |= 0xFC000000;
mfn_abs = -mfn;
}
mfn = dp_mfn & MXC_PLL_DP_MFN_MASK;
mfn = sign_extend32(mfn, 26);
ref_clk = 2 * parent_rate;
if (dbl != 0)
ref_clk *= 2;
ref_clk /= (pdf + 1);
temp = (u64) ref_clk * mfn_abs;
temp = (u64) ref_clk * abs(mfn);
do_div(temp, mfd + 1);
if (mfn < 0)
temp = -temp;
......
......@@ -387,6 +387,7 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
clk[VF610_CLK_SNVS] = imx_clk_gate2("snvs-rtc", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(7));
clk[VF610_CLK_DAP] = imx_clk_gate("dap", "platform_bus", CCM_CCSR, 24);
clk[VF610_CLK_OCOTP] = imx_clk_gate("ocotp", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(5));
imx_check_clocks(clk, ARRAY_SIZE(clk));
......
This diff is collapsed.
......@@ -7,6 +7,7 @@
extern spinlock_t imx_ccm_lock;
void imx_check_clocks(struct clk *clks[], unsigned int count);
void imx_register_uart_clocks(struct clk ** const clks[]);
extern void imx_cscmr1_fixup(u32 *val);
......
......@@ -157,7 +157,7 @@ static struct clk *clk_register_pll(struct device *dev,
* _of_clk_init - PLL initialisation via DT
* @node: device tree node for this clock
* @pllctrl: If true, lower 6 bits of multiplier is in pllm register of
* pll controller, else it is in the control regsiter0(bit 11-6)
* pll controller, else it is in the control register0(bit 11-6)
*/
static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl)
{
......
obj-y += clk-mtk.o clk-pll.o clk-gate.o
obj-y += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o
obj-$(CONFIG_RESET_CONTROLLER) += reset.o
obj-y += clk-mt8135.o
obj-y += clk-mt8173.o
This diff is collapsed.
......@@ -97,7 +97,7 @@ const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
.disable = mtk_cg_disable_inv,
};
struct clk *mtk_clk_register_gate(
struct clk * __init mtk_clk_register_gate(
const char *name,
const char *parent_name,
struct regmap *regmap,
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment