Commit b1511f7a authored by Stephen Boyd's avatar Stephen Boyd

Merge branches 'clk-bcm63xx', 'clk-silabs', 'clk-lochnagar' and 'clk-rockchip' into clk-next

 - Support gated clk controller on MIPS based BCM63XX SoCs
 - Small frequency support for SiLabs Si544 chips
 - Support SiLabs Si5341 and Si5340 chips

* clk-bcm63xx:
  clk: add BCM63XX gated clock controller driver
  devicetree: document the BCM63XX gated clock bindings

* clk-silabs:
  clk: Add Si5341/Si5340 driver
  dt-bindings: clock: Add silabs,si5341
  clk: clk-si544: Implement small frequency change support

* clk-lochnagar:
  clk: lochnagar: Update DT binding doc to include the primary SPDIF MCLK
  clk: lochnagar: Use new parent_data approach to register clock parents

* clk-rockchip:
  clk: rockchip: export HDMIPHY clock on rk3228
  clk: rockchip: add watchdog pclk on rk3328
  clk: rockchip: add clock id for hdmi_phy special clock on rk3228
  clk: rockchip: add clock id for watchdog pclk on rk3328
  clk: rockchip: convert pclk_wdt boilerplat to new SGRF_GATE macro
  clk: rockchip: add a type from SGRF-controlled gate clocks
  clk: rockchip: Remove 48 MHz PLL rate from rk3288
  clk: rockchip: add 1.464GHz cpu-clock rate to rk3228
  clk: rockchip: Slightly more accurate math in rockchip_mmc_get_phase()
  clk: rockchip: Don't yell about bad mmc phases when getting
  clk: rockchip: Use clk_hw_get_rate() in MMC phase calculation
Gated Clock Controller Bindings for MIPS based BCM63XX SoCs
Required properties:
- compatible: must be one of:
"brcm,bcm3368-clocks"
"brcm,bcm6328-clocks"
"brcm,bcm6358-clocks"
"brcm,bcm6362-clocks"
"brcm,bcm6368-clocks"
"brcm,bcm63268-clocks"
- reg: Address and length of the register set
- #clock-cells: must be <1>
Example:
clkctl: clock-controller@10000004 {
compatible = "brcm,bcm6328-clocks";
reg = <0x10000004 0x4>;
#clock-cells = <1>;
};
...@@ -40,6 +40,7 @@ Optional properties: ...@@ -40,6 +40,7 @@ Optional properties:
input audio clocks from host system. input audio clocks from host system.
- ln-psia1-mclk, ln-psia2-mclk : Optional input audio clocks from - ln-psia1-mclk, ln-psia2-mclk : Optional input audio clocks from
external connector. external connector.
- ln-spdif-mclk : Optional input audio clock from SPDIF.
- ln-spdif-clkout : Optional input audio clock from SPDIF. - ln-spdif-clkout : Optional input audio clock from SPDIF.
- ln-adat-mclk : Optional input audio clock from ADAT. - ln-adat-mclk : Optional input audio clock from ADAT.
- ln-pmic-32k : On board fixed clock. - ln-pmic-32k : On board fixed clock.
......
Binding for Silicon Labs Si5341 and Si5340 programmable i2c clock generator.
Reference
[1] Si5341 Data Sheet
https://www.silabs.com/documents/public/data-sheets/Si5341-40-D-DataSheet.pdf
[2] Si5341 Reference Manual
https://www.silabs.com/documents/public/reference-manuals/Si5341-40-D-RM.pdf
The Si5341 and Si5340 are programmable i2c clock generators with up to 10 output
clocks. The chip contains a PLL that sources 5 (or 4) multisynth clocks, which
in turn can be directed to any of the 10 (or 4) outputs through a divider.
The internal structure of the clock generators can be found in [2].
The driver can be used in "as is" mode, reading the current settings from the
chip at boot, in case you have a (pre-)programmed device. If the PLL is not
configured when the driver probes, it assumes the driver must fully initialize
it.
The device type, speed grade and revision are determined runtime by probing.
The driver currently only supports XTAL input mode, and does not support any
fancy input configurations. They can still be programmed into the chip and
the driver will leave them "as is".
==I2C device node==
Required properties:
- compatible: shall be one of the following:
"silabs,si5340" - Si5340 A/B/C/D
"silabs,si5341" - Si5341 A/B/C/D
- reg: i2c device address, usually 0x74
- #clock-cells: from common clock binding; shall be set to 2.
The first value is "0" for outputs, "1" for synthesizers.
The second value is the output or synthesizer index.
- clocks: from common clock binding; list of parent clock handles,
corresponding to inputs. Use a fixed clock for the "xtal" input.
At least one must be present.
- clock-names: One of: "xtal", "in0", "in1", "in2"
- vdd-supply: Regulator node for VDD
Optional properties:
- vdda-supply: Regulator node for VDDA
- vdds-supply: Regulator node for VDDS
- silabs,pll-m-num, silabs,pll-m-den: Numerator and denominator for PLL
feedback divider. Must be such that the PLL output is in the valid range. For
example, to create 14GHz from a 48MHz xtal, use m-num=14000 and m-den=48. Only
the fraction matters, using 3500 and 12 will deliver the exact same result.
If these are not specified, and the PLL is not yet programmed when the driver
probes, the PLL will be set to 14GHz.
- silabs,reprogram: When present, the driver will always assume the device must
be initialized, and always performs the soft-reset routine. Since this will
temporarily stop all output clocks, don't do this if the chip is generating
the CPU clock for example.
- interrupts: Interrupt for INTRb pin.
- #address-cells: shall be set to 1.
- #size-cells: shall be set to 0.
== Child nodes: Outputs ==
The child nodes list the output clocks.
Each of the clock outputs can be overwritten individually by using a child node.
If a child node for a clock output is not set, the configuration remains
unchanged.
Required child node properties:
- reg: number of clock output.
Optional child node properties:
- vdd-supply: Regulator node for VDD for this output. The driver selects default
values for common-mode and amplitude based on the voltage.
- silabs,format: Output format, one of:
1 = differential (defaults to LVDS levels)
2 = low-power (defaults to HCSL levels)
4 = LVCMOS
- silabs,common-mode: Manually override output common mode, see [2] for values
- silabs,amplitude: Manually override output amplitude, see [2] for values
- silabs,synth-master: boolean. If present, this output is allowed to change the
multisynth frequency dynamically.
- silabs,silabs,disable-high: boolean. If set, the clock output is driven HIGH
when disabled, otherwise it's driven LOW.
==Example==
/* 48MHz reference crystal */
ref48: ref48M {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <48000000>;
};
i2c-master-node {
/* Programmable clock (for logic) */
si5341: clock-generator@74 {
reg = <0x74>;
compatible = "silabs,si5341";
#clock-cells = <2>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&ref48>;
clock-names = "xtal";
silabs,pll-m-num = <14000>; /* PLL at 14.0 GHz */
silabs,pll-m-den = <48>;
silabs,reprogram; /* Chips are not programmed, always reset */
out@0 {
reg = <0>;
silabs,format = <1>; /* LVDS 3v3 */
silabs,common-mode = <3>;
silabs,amplitude = <3>;
silabs,synth-master;
};
/*
* Output 6 configuration:
* LVDS 1v8
*/
out@6 {
reg = <6>;
silabs,format = <1>; /* LVDS 1v8 */
silabs,common-mode = <13>;
silabs,amplitude = <3>;
};
/*
* Output 8 configuration:
* HCSL 3v3
*/
out@8 {
reg = <8>;
silabs,format = <2>;
silabs,common-mode = <11>;
silabs,amplitude = <3>;
};
};
};
some-video-node {
/* Standard clock bindings */
clock-names = "pixel";
clocks = <&si5341 0 7>; /* Output 7 */
/* Set output 7 to use syntesizer 3 as its parent */
assigned-clocks = <&si5341 0 7>, <&si5341 1 3>;
assigned-clock-parents = <&si5341 1 3>;
/* Set output 7 to 148.5 MHz using a synth frequency of 594 MHz */
assigned-clock-rates = <148500000>, <594000000>;
};
some-audio-node {
clock-names = "i2s-clk";
clocks = <&si5341 0 0>;
/*
* since output 0 is a synth-master, the synth will be automatically set
* to an appropriate frequency when the audio driver requests another
* frequency. We give control over synth 2 to this output here.
*/
assigned-clocks = <&si5341 0 0>;
assigned-clock-parents = <&si5341 1 2>;
};
...@@ -91,6 +91,17 @@ config COMMON_CLK_SCPI ...@@ -91,6 +91,17 @@ config COMMON_CLK_SCPI
This driver uses SCPI Message Protocol to interact with the This driver uses SCPI Message Protocol to interact with the
firmware providing all the clock controls. firmware providing all the clock controls.
config COMMON_CLK_SI5341
tristate "Clock driver for SiLabs 5341 and 5340 A/B/C/D devices"
depends on I2C
select REGMAP_I2C
help
This driver supports Silicon Labs Si5341 and Si5340 programmable clock
generators. Not all features of these chips are currently supported
by the driver, in particular it only supports XTAL input. The chip can
be pre-programmed to support other configurations and features not yet
implemented in the driver.
config COMMON_CLK_SI5351 config COMMON_CLK_SI5351
tristate "Clock driver for SiLabs 5351A/B/C" tristate "Clock driver for SiLabs 5351A/B/C"
depends on I2C depends on I2C
......
...@@ -49,6 +49,7 @@ obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o ...@@ -49,6 +49,7 @@ obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o
obj-$(CONFIG_COMMON_CLK_SCPI) += clk-scpi.o obj-$(CONFIG_COMMON_CLK_SCPI) += clk-scpi.o
obj-$(CONFIG_COMMON_CLK_SI5341) += clk-si5341.o
obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
obj-$(CONFIG_COMMON_CLK_SI514) += clk-si514.o obj-$(CONFIG_COMMON_CLK_SI514) += clk-si514.o
obj-$(CONFIG_COMMON_CLK_SI544) += clk-si544.o obj-$(CONFIG_COMMON_CLK_SI544) += clk-si544.o
......
...@@ -16,6 +16,14 @@ config CLK_BCM_63XX ...@@ -16,6 +16,14 @@ config CLK_BCM_63XX
Enable common clock framework support for Broadcom BCM63xx DSL SoCs Enable common clock framework support for Broadcom BCM63xx DSL SoCs
based on the ARM architecture based on the ARM architecture
config CLK_BCM_63XX_GATE
bool "Broadcom BCM63xx gated clock support"
depends on BMIPS_GENERIC || COMPILE_TEST
default BMIPS_GENERIC
help
Enable common clock framework support for Broadcom BCM63xx DSL SoCs
based on the MIPS architecture
config CLK_BCM_KONA config CLK_BCM_KONA
bool "Broadcom Kona CCU clock support" bool "Broadcom Kona CCU clock support"
depends on ARCH_BCM_MOBILE || COMPILE_TEST depends on ARCH_BCM_MOBILE || COMPILE_TEST
......
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_CLK_BCM_63XX) += clk-bcm63xx.o obj-$(CONFIG_CLK_BCM_63XX) += clk-bcm63xx.o
obj-$(CONFIG_CLK_BCM_63XX_GATE) += clk-bcm63xx-gate.o
obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o
obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o
obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o
......
// SPDX-License-Identifier: GPL-2.0
#include <linux/clk-provider.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
struct clk_bcm63xx_table_entry {
const char * const name;
u8 bit;
unsigned long flags;
};
struct clk_bcm63xx_hw {
void __iomem *regs;
spinlock_t lock;
struct clk_hw_onecell_data data;
};
static const struct clk_bcm63xx_table_entry bcm3368_clocks[] = {
{ .name = "mac", .bit = 3, },
{ .name = "tc", .bit = 5, },
{ .name = "us_top", .bit = 6, },
{ .name = "ds_top", .bit = 7, },
{ .name = "acm", .bit = 8, },
{ .name = "spi", .bit = 9, },
{ .name = "usbs", .bit = 10, },
{ .name = "bmu", .bit = 11, },
{ .name = "pcm", .bit = 12, },
{ .name = "ntp", .bit = 13, },
{ .name = "acp_b", .bit = 14, },
{ .name = "acp_a", .bit = 15, },
{ .name = "emusb", .bit = 17, },
{ .name = "enet0", .bit = 18, },
{ .name = "enet1", .bit = 19, },
{ .name = "usbsu", .bit = 20, },
{ .name = "ephy", .bit = 21, },
{ },
};
static const struct clk_bcm63xx_table_entry bcm6328_clocks[] = {
{ .name = "phy_mips", .bit = 0, },
{ .name = "adsl_qproc", .bit = 1, },
{ .name = "adsl_afe", .bit = 2, },
{ .name = "adsl", .bit = 3, },
{ .name = "mips", .bit = 4, .flags = CLK_IS_CRITICAL, },
{ .name = "sar", .bit = 5, },
{ .name = "pcm", .bit = 6, },
{ .name = "usbd", .bit = 7, },
{ .name = "usbh", .bit = 8, },
{ .name = "hsspi", .bit = 9, },
{ .name = "pcie", .bit = 10, },
{ .name = "robosw", .bit = 11, },
{ },
};
static const struct clk_bcm63xx_table_entry bcm6358_clocks[] = {
{ .name = "enet", .bit = 4, },
{ .name = "adslphy", .bit = 5, },
{ .name = "pcm", .bit = 8, },
{ .name = "spi", .bit = 9, },
{ .name = "usbs", .bit = 10, },
{ .name = "sar", .bit = 11, },
{ .name = "emusb", .bit = 17, },
{ .name = "enet0", .bit = 18, },
{ .name = "enet1", .bit = 19, },
{ .name = "usbsu", .bit = 20, },
{ .name = "ephy", .bit = 21, },
{ },
};
static const struct clk_bcm63xx_table_entry bcm6362_clocks[] = {
{ .name = "adsl_qproc", .bit = 1, },
{ .name = "adsl_afe", .bit = 2, },
{ .name = "adsl", .bit = 3, },
{ .name = "mips", .bit = 4, .flags = CLK_IS_CRITICAL, },
{ .name = "wlan_ocp", .bit = 5, },
{ .name = "swpkt_usb", .bit = 7, },
{ .name = "swpkt_sar", .bit = 8, },
{ .name = "sar", .bit = 9, },
{ .name = "robosw", .bit = 10, },
{ .name = "pcm", .bit = 11, },
{ .name = "usbd", .bit = 12, },
{ .name = "usbh", .bit = 13, },
{ .name = "ipsec", .bit = 14, },
{ .name = "spi", .bit = 15, },
{ .name = "hsspi", .bit = 16, },
{ .name = "pcie", .bit = 17, },
{ .name = "fap", .bit = 18, },
{ .name = "phymips", .bit = 19, },
{ .name = "nand", .bit = 20, },
{ },
};
static const struct clk_bcm63xx_table_entry bcm6368_clocks[] = {
{ .name = "vdsl_qproc", .bit = 2, },
{ .name = "vdsl_afe", .bit = 3, },
{ .name = "vdsl_bonding", .bit = 4, },
{ .name = "vdsl", .bit = 5, },
{ .name = "phymips", .bit = 6, },
{ .name = "swpkt_usb", .bit = 7, },
{ .name = "swpkt_sar", .bit = 8, },
{ .name = "spi", .bit = 9, },
{ .name = "usbd", .bit = 10, },
{ .name = "sar", .bit = 11, },
{ .name = "robosw", .bit = 12, },
{ .name = "utopia", .bit = 13, },
{ .name = "pcm", .bit = 14, },
{ .name = "usbh", .bit = 15, },
{ .name = "disable_gless", .bit = 16, },
{ .name = "nand", .bit = 17, },
{ .name = "ipsec", .bit = 18, },
{ },
};
static const struct clk_bcm63xx_table_entry bcm63268_clocks[] = {
{ .name = "disable_gless", .bit = 0, },
{ .name = "vdsl_qproc", .bit = 1, },
{ .name = "vdsl_afe", .bit = 2, },
{ .name = "vdsl", .bit = 3, },
{ .name = "mips", .bit = 4, .flags = CLK_IS_CRITICAL, },
{ .name = "wlan_ocp", .bit = 5, },
{ .name = "dect", .bit = 6, },
{ .name = "fap0", .bit = 7, },
{ .name = "fap1", .bit = 8, },
{ .name = "sar", .bit = 9, },
{ .name = "robosw", .bit = 10, },
{ .name = "pcm", .bit = 11, },
{ .name = "usbd", .bit = 12, },
{ .name = "usbh", .bit = 13, },
{ .name = "ipsec", .bit = 14, },
{ .name = "spi", .bit = 15, },
{ .name = "hsspi", .bit = 16, },
{ .name = "pcie", .bit = 17, },
{ .name = "phymips", .bit = 18, },
{ .name = "gmac", .bit = 19, },
{ .name = "nand", .bit = 20, },
{ .name = "tbus", .bit = 27, },
{ .name = "robosw250", .bit = 31, },
{ },
};
static int clk_bcm63xx_probe(struct platform_device *pdev)
{
const struct clk_bcm63xx_table_entry *entry, *table;
struct clk_bcm63xx_hw *hw;
struct resource *r;
u8 maxbit = 0;
int i, ret;
table = of_device_get_match_data(&pdev->dev);
if (!table)
return -EINVAL;
for (entry = table; entry->name; entry++)
maxbit = max_t(u8, maxbit, entry->bit);
hw = devm_kzalloc(&pdev->dev, struct_size(hw, data.hws, maxbit),
GFP_KERNEL);
if (!hw)
return -ENOMEM;
platform_set_drvdata(pdev, hw);
spin_lock_init(&hw->lock);
hw->data.num = maxbit;
for (i = 0; i < maxbit; i++)
hw->data.hws[i] = ERR_PTR(-ENODEV);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
hw->regs = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(hw->regs))
return PTR_ERR(hw->regs);
for (entry = table; entry->name; entry++) {
struct clk_hw *clk;
clk = clk_hw_register_gate(&pdev->dev, entry->name, NULL,
entry->flags, hw->regs, entry->bit,
CLK_GATE_BIG_ENDIAN, &hw->lock);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto out_err;
}
hw->data.hws[entry->bit] = clk;
}
ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
&hw->data);
if (!ret)
return 0;
out_err:
for (i = 0; i < hw->data.num; i++) {
if (!IS_ERR(hw->data.hws[i]))
clk_hw_unregister_gate(hw->data.hws[i]);
}
return ret;
}
static int clk_bcm63xx_remove(struct platform_device *pdev)
{
struct clk_bcm63xx_hw *hw = platform_get_drvdata(pdev);
int i;
of_clk_del_provider(pdev->dev.of_node);
for (i = 0; i < hw->data.num; i++) {
if (!IS_ERR(hw->data.hws[i]))
clk_hw_unregister_gate(hw->data.hws[i]);
}
return 0;
}
static const struct of_device_id clk_bcm63xx_dt_ids[] = {
{ .compatible = "brcm,bcm3368-clocks", .data = &bcm3368_clocks, },
{ .compatible = "brcm,bcm6328-clocks", .data = &bcm6328_clocks, },
{ .compatible = "brcm,bcm6358-clocks", .data = &bcm6358_clocks, },
{ .compatible = "brcm,bcm6362-clocks", .data = &bcm6362_clocks, },
{ .compatible = "brcm,bcm6368-clocks", .data = &bcm6368_clocks, },
{ .compatible = "brcm,bcm63268-clocks", .data = &bcm63268_clocks, },
{ }
};
static struct platform_driver clk_bcm63xx = {
.probe = clk_bcm63xx_probe,
.remove = clk_bcm63xx_remove,
.driver = {
.name = "bcm63xx-clock",
.of_match_table = clk_bcm63xx_dt_ids,
},
};
builtin_platform_driver(clk_bcm63xx);
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/mfd/lochnagar.h>
#include <linux/mfd/lochnagar1_regs.h> #include <linux/mfd/lochnagar1_regs.h>
#include <linux/mfd/lochnagar2_regs.h> #include <linux/mfd/lochnagar2_regs.h>
...@@ -40,48 +39,46 @@ struct lochnagar_clk { ...@@ -40,48 +39,46 @@ struct lochnagar_clk {
struct lochnagar_clk_priv { struct lochnagar_clk_priv {
struct device *dev; struct device *dev;
struct regmap *regmap; struct regmap *regmap;
enum lochnagar_type type;
const char **parents;
unsigned int nparents;
struct lochnagar_clk lclks[LOCHNAGAR_NUM_CLOCKS]; struct lochnagar_clk lclks[LOCHNAGAR_NUM_CLOCKS];
}; };
static const char * const lochnagar1_clk_parents[] = { #define LN_PARENT(NAME) { .name = NAME, .fw_name = NAME }
"ln-none",
"ln-spdif-mclk", static const struct clk_parent_data lochnagar1_clk_parents[] = {
"ln-psia1-mclk", LN_PARENT("ln-none"),
"ln-psia2-mclk", LN_PARENT("ln-spdif-mclk"),
"ln-cdc-clkout", LN_PARENT("ln-psia1-mclk"),
"ln-dsp-clkout", LN_PARENT("ln-psia2-mclk"),
"ln-pmic-32k", LN_PARENT("ln-cdc-clkout"),
"ln-gf-mclk1", LN_PARENT("ln-dsp-clkout"),
"ln-gf-mclk3", LN_PARENT("ln-pmic-32k"),
"ln-gf-mclk2", LN_PARENT("ln-gf-mclk1"),
"ln-gf-mclk4", LN_PARENT("ln-gf-mclk3"),
LN_PARENT("ln-gf-mclk2"),
LN_PARENT("ln-gf-mclk4"),
}; };
static const char * const lochnagar2_clk_parents[] = { static const struct clk_parent_data lochnagar2_clk_parents[] = {
"ln-none", LN_PARENT("ln-none"),
"ln-cdc-clkout", LN_PARENT("ln-cdc-clkout"),
"ln-dsp-clkout", LN_PARENT("ln-dsp-clkout"),
"ln-pmic-32k", LN_PARENT("ln-pmic-32k"),
"ln-spdif-mclk", LN_PARENT("ln-spdif-mclk"),
"ln-clk-12m", LN_PARENT("ln-clk-12m"),
"ln-clk-11m", LN_PARENT("ln-clk-11m"),
"ln-clk-24m", LN_PARENT("ln-clk-24m"),
"ln-clk-22m", LN_PARENT("ln-clk-22m"),
"ln-clk-8m", LN_PARENT("ln-clk-8m"),
"ln-usb-clk-24m", LN_PARENT("ln-usb-clk-24m"),
"ln-gf-mclk1", LN_PARENT("ln-gf-mclk1"),
"ln-gf-mclk3", LN_PARENT("ln-gf-mclk3"),
"ln-gf-mclk2", LN_PARENT("ln-gf-mclk2"),
"ln-psia1-mclk", LN_PARENT("ln-psia1-mclk"),
"ln-psia2-mclk", LN_PARENT("ln-psia2-mclk"),
"ln-spdif-clkout", LN_PARENT("ln-spdif-clkout"),
"ln-adat-mclk", LN_PARENT("ln-adat-mclk"),
"ln-usb-clk-12m", LN_PARENT("ln-usb-clk-12m"),
}; };
#define LN1_CLK(ID, NAME, REG) \ #define LN1_CLK(ID, NAME, REG) \
...@@ -122,6 +119,24 @@ static const struct lochnagar_clk lochnagar2_clks[LOCHNAGAR_NUM_CLOCKS] = { ...@@ -122,6 +119,24 @@ static const struct lochnagar_clk lochnagar2_clks[LOCHNAGAR_NUM_CLOCKS] = {
LN2_CLK(SOUNDCARD_MCLK, "ln-soundcard-mclk"), LN2_CLK(SOUNDCARD_MCLK, "ln-soundcard-mclk"),
}; };
struct lochnagar_config {
const struct clk_parent_data *parents;
int nparents;
const struct lochnagar_clk *clks;
};
static const struct lochnagar_config lochnagar1_conf = {
.parents = lochnagar1_clk_parents,
.nparents = ARRAY_SIZE(lochnagar1_clk_parents),
.clks = lochnagar1_clks,
};
static const struct lochnagar_config lochnagar2_conf = {
.parents = lochnagar2_clk_parents,
.nparents = ARRAY_SIZE(lochnagar2_clk_parents),
.clks = lochnagar2_clks,
};
static inline struct lochnagar_clk *lochnagar_hw_to_lclk(struct clk_hw *hw) static inline struct lochnagar_clk *lochnagar_hw_to_lclk(struct clk_hw *hw)
{ {
return container_of(hw, struct lochnagar_clk, hw); return container_of(hw, struct lochnagar_clk, hw);
...@@ -183,7 +198,7 @@ static u8 lochnagar_clk_get_parent(struct clk_hw *hw) ...@@ -183,7 +198,7 @@ static u8 lochnagar_clk_get_parent(struct clk_hw *hw)
if (ret < 0) { if (ret < 0) {
dev_dbg(priv->dev, "Failed to read parent of %s: %d\n", dev_dbg(priv->dev, "Failed to read parent of %s: %d\n",
lclk->name, ret); lclk->name, ret);
return priv->nparents; return hw->init->num_parents;
} }
val &= lclk->src_mask; val &= lclk->src_mask;
...@@ -198,46 +213,6 @@ static const struct clk_ops lochnagar_clk_ops = { ...@@ -198,46 +213,6 @@ static const struct clk_ops lochnagar_clk_ops = {
.get_parent = lochnagar_clk_get_parent, .get_parent = lochnagar_clk_get_parent,
}; };
static int lochnagar_init_parents(struct lochnagar_clk_priv *priv)
{
struct device_node *np = priv->dev->of_node;
int i, j;
switch (priv->type) {
case LOCHNAGAR1:
memcpy(priv->lclks, lochnagar1_clks, sizeof(lochnagar1_clks));
priv->nparents = ARRAY_SIZE(lochnagar1_clk_parents);
priv->parents = devm_kmemdup(priv->dev, lochnagar1_clk_parents,
sizeof(lochnagar1_clk_parents),
GFP_KERNEL);
break;
case LOCHNAGAR2:
memcpy(priv->lclks, lochnagar2_clks, sizeof(lochnagar2_clks));
priv->nparents = ARRAY_SIZE(lochnagar2_clk_parents);
priv->parents = devm_kmemdup(priv->dev, lochnagar2_clk_parents,
sizeof(lochnagar2_clk_parents),
GFP_KERNEL);
break;
default:
dev_err(priv->dev, "Unknown Lochnagar type: %d\n", priv->type);
return -EINVAL;
}
if (!priv->parents)
return -ENOMEM;
for (i = 0; i < priv->nparents; i++) {
j = of_property_match_string(np, "clock-names",
priv->parents[i]);
if (j >= 0)
priv->parents[i] = of_clk_get_parent_name(np, j);
}
return 0;
}
static struct clk_hw * static struct clk_hw *
lochnagar_of_clk_hw_get(struct of_phandle_args *clkspec, void *data) lochnagar_of_clk_hw_get(struct of_phandle_args *clkspec, void *data)
{ {
...@@ -252,16 +227,42 @@ lochnagar_of_clk_hw_get(struct of_phandle_args *clkspec, void *data) ...@@ -252,16 +227,42 @@ lochnagar_of_clk_hw_get(struct of_phandle_args *clkspec, void *data)
return &priv->lclks[idx].hw; return &priv->lclks[idx].hw;
} }
static int lochnagar_init_clks(struct lochnagar_clk_priv *priv) static const struct of_device_id lochnagar_of_match[] = {
{ .compatible = "cirrus,lochnagar1-clk", .data = &lochnagar1_conf },
{ .compatible = "cirrus,lochnagar2-clk", .data = &lochnagar2_conf },
{}
};
MODULE_DEVICE_TABLE(of, lochnagar_of_match);
static int lochnagar_clk_probe(struct platform_device *pdev)
{ {
struct clk_init_data clk_init = { struct clk_init_data clk_init = {
.ops = &lochnagar_clk_ops, .ops = &lochnagar_clk_ops,
.parent_names = priv->parents,
.num_parents = priv->nparents,
}; };
struct device *dev = &pdev->dev;
struct lochnagar_clk_priv *priv;
const struct of_device_id *of_id;
struct lochnagar_clk *lclk; struct lochnagar_clk *lclk;
struct lochnagar_config *conf;
int ret, i; int ret, i;
of_id = of_match_device(lochnagar_of_match, dev);
if (!of_id)
return -EINVAL;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
priv->regmap = dev_get_regmap(dev->parent, NULL);
conf = (struct lochnagar_config *)of_id->data;
memcpy(priv->lclks, conf->clks, sizeof(priv->lclks));
clk_init.parent_data = conf->parents;
clk_init.num_parents = conf->nparents;
for (i = 0; i < ARRAY_SIZE(priv->lclks); i++) { for (i = 0; i < ARRAY_SIZE(priv->lclks); i++) {
lclk = &priv->lclks[i]; lclk = &priv->lclks[i];
...@@ -273,55 +274,21 @@ static int lochnagar_init_clks(struct lochnagar_clk_priv *priv) ...@@ -273,55 +274,21 @@ static int lochnagar_init_clks(struct lochnagar_clk_priv *priv)
lclk->priv = priv; lclk->priv = priv;
lclk->hw.init = &clk_init; lclk->hw.init = &clk_init;
ret = devm_clk_hw_register(priv->dev, &lclk->hw); ret = devm_clk_hw_register(dev, &lclk->hw);
if (ret) { if (ret) {
dev_err(priv->dev, "Failed to register %s: %d\n", dev_err(dev, "Failed to register %s: %d\n",
lclk->name, ret); lclk->name, ret);
return ret; return ret;
} }
} }
ret = devm_of_clk_add_hw_provider(priv->dev, lochnagar_of_clk_hw_get, ret = devm_of_clk_add_hw_provider(dev, lochnagar_of_clk_hw_get, priv);
priv);
if (ret < 0) if (ret < 0)
dev_err(priv->dev, "Failed to register provider: %d\n", ret); dev_err(dev, "Failed to register provider: %d\n", ret);
return ret; return ret;
} }
static const struct of_device_id lochnagar_of_match[] = {
{ .compatible = "cirrus,lochnagar1-clk", .data = (void *)LOCHNAGAR1 },
{ .compatible = "cirrus,lochnagar2-clk", .data = (void *)LOCHNAGAR2 },
{}
};
MODULE_DEVICE_TABLE(of, lochnagar_of_match);
static int lochnagar_clk_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct lochnagar_clk_priv *priv;
const struct of_device_id *of_id;
int ret;
of_id = of_match_device(lochnagar_of_match, dev);
if (!of_id)
return -EINVAL;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
priv->regmap = dev_get_regmap(dev->parent, NULL);
priv->type = (enum lochnagar_type)of_id->data;
ret = lochnagar_init_parents(priv);
if (ret)
return ret;
return lochnagar_init_clks(priv);
}
static struct platform_driver lochnagar_clk_driver = { static struct platform_driver lochnagar_clk_driver = {
.driver = { .driver = {
.name = "lochnagar-clk", .name = "lochnagar-clk",
......
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for Silicon Labs Si5341/Si5340 Clock generator
* Copyright (C) 2019 Topic Embedded Products
* Author: Mike Looijmans <mike.looijmans@topic.nl>
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/gcd.h>
#include <linux/math64.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#define SI5341_MAX_NUM_OUTPUTS 10
#define SI5340_MAX_NUM_OUTPUTS 4
#define SI5341_NUM_SYNTH 5
#define SI5340_NUM_SYNTH 4
/* Range of the synthesizer fractional divider */
#define SI5341_SYNTH_N_MIN 10
#define SI5341_SYNTH_N_MAX 4095
/* The chip can get its input clock from 3 input pins or an XTAL */
/* There is one PLL running at 13500–14256 MHz */
#define SI5341_PLL_VCO_MIN 13500000000ull
#define SI5341_PLL_VCO_MAX 14256000000ull
/* The 5 frequency synthesizers obtain their input from the PLL */
struct clk_si5341_synth {
struct clk_hw hw;
struct clk_si5341 *data;
u8 index;
};
#define to_clk_si5341_synth(_hw) \
container_of(_hw, struct clk_si5341_synth, hw)
/* The output stages can be connected to any synth (full mux) */
struct clk_si5341_output {
struct clk_hw hw;
struct clk_si5341 *data;
u8 index;
};
#define to_clk_si5341_output(_hw) \
container_of(_hw, struct clk_si5341_output, hw)
struct clk_si5341 {
struct clk_hw hw;
struct regmap *regmap;
struct i2c_client *i2c_client;
struct clk_si5341_synth synth[SI5341_NUM_SYNTH];
struct clk_si5341_output clk[SI5341_MAX_NUM_OUTPUTS];
struct clk *pxtal;
const char *pxtal_name;
const u16 *reg_output_offset;
const u16 *reg_rdiv_offset;
u64 freq_vco; /* 13500–14256 MHz */
u8 num_outputs;
u8 num_synth;
};
#define to_clk_si5341(_hw) container_of(_hw, struct clk_si5341, hw)
struct clk_si5341_output_config {
u8 out_format_drv_bits;
u8 out_cm_ampl_bits;
bool synth_master;
bool always_on;
};
#define SI5341_PAGE 0x0001
#define SI5341_PN_BASE 0x0002
#define SI5341_DEVICE_REV 0x0005
#define SI5341_STATUS 0x000C
#define SI5341_SOFT_RST 0x001C
/* Input dividers (48-bit) */
#define SI5341_IN_PDIV(x) (0x0208 + ((x) * 10))
#define SI5341_IN_PSET(x) (0x020E + ((x) * 10))
/* PLL configuration */
#define SI5341_PLL_M_NUM 0x0235
#define SI5341_PLL_M_DEN 0x023B
/* Output configuration */
#define SI5341_OUT_CONFIG(output) \
((output)->data->reg_output_offset[(output)->index])
#define SI5341_OUT_FORMAT(output) (SI5341_OUT_CONFIG(output) + 1)
#define SI5341_OUT_CM(output) (SI5341_OUT_CONFIG(output) + 2)
#define SI5341_OUT_MUX_SEL(output) (SI5341_OUT_CONFIG(output) + 3)
#define SI5341_OUT_R_REG(output) \
((output)->data->reg_rdiv_offset[(output)->index])
/* Synthesize N divider */
#define SI5341_SYNTH_N_NUM(x) (0x0302 + ((x) * 11))
#define SI5341_SYNTH_N_DEN(x) (0x0308 + ((x) * 11))
#define SI5341_SYNTH_N_UPD(x) (0x030C + ((x) * 11))
/* Synthesizer output enable, phase bypass, power mode */
#define SI5341_SYNTH_N_CLK_TO_OUTX_EN 0x0A03
#define SI5341_SYNTH_N_PIBYP 0x0A04
#define SI5341_SYNTH_N_PDNB 0x0A05
#define SI5341_SYNTH_N_CLK_DIS 0x0B4A
#define SI5341_REGISTER_MAX 0xBFF
/* SI5341_OUT_CONFIG bits */
#define SI5341_OUT_CFG_PDN BIT(0)
#define SI5341_OUT_CFG_OE BIT(1)
#define SI5341_OUT_CFG_RDIV_FORCE2 BIT(2)
/* Static configuration (to be moved to firmware) */
struct si5341_reg_default {
u16 address;
u8 value;
};
/* Output configuration registers 0..9 are not quite logically organized */
static const u16 si5341_reg_output_offset[] = {
0x0108,
0x010D,
0x0112,
0x0117,
0x011C,
0x0121,
0x0126,
0x012B,
0x0130,
0x013A,
};
static const u16 si5340_reg_output_offset[] = {
0x0112,
0x0117,
0x0126,
0x012B,
};
/* The location of the R divider registers */
static const u16 si5341_reg_rdiv_offset[] = {
0x024A,
0x024D,
0x0250,
0x0253,
0x0256,
0x0259,
0x025C,
0x025F,
0x0262,
0x0268,
};
static const u16 si5340_reg_rdiv_offset[] = {
0x0250,
0x0253,
0x025C,
0x025F,
};
/*
* Programming sequence from ClockBuilder, settings to initialize the system
* using only the XTAL input, without pre-divider.
* This also contains settings that aren't mentioned anywhere in the datasheet.
* The "known" settings like synth and output configuration are done later.
*/
static const struct si5341_reg_default si5341_reg_defaults[] = {
{ 0x0017, 0x3A }, /* INT mask (disable interrupts) */
{ 0x0018, 0xFF }, /* INT mask */
{ 0x0021, 0x0F }, /* Select XTAL as input */
{ 0x0022, 0x00 }, /* Not in datasheet */
{ 0x002B, 0x02 }, /* SPI config */
{ 0x002C, 0x20 }, /* LOS enable for XTAL */
{ 0x002D, 0x00 }, /* LOS timing */
{ 0x002E, 0x00 },
{ 0x002F, 0x00 },
{ 0x0030, 0x00 },
{ 0x0031, 0x00 },
{ 0x0032, 0x00 },
{ 0x0033, 0x00 },
{ 0x0034, 0x00 },
{ 0x0035, 0x00 },
{ 0x0036, 0x00 },
{ 0x0037, 0x00 },
{ 0x0038, 0x00 }, /* LOS setting (thresholds) */
{ 0x0039, 0x00 },
{ 0x003A, 0x00 },
{ 0x003B, 0x00 },
{ 0x003C, 0x00 },
{ 0x003D, 0x00 }, /* LOS setting (thresholds) end */
{ 0x0041, 0x00 }, /* LOS0_DIV_SEL */
{ 0x0042, 0x00 }, /* LOS1_DIV_SEL */
{ 0x0043, 0x00 }, /* LOS2_DIV_SEL */
{ 0x0044, 0x00 }, /* LOS3_DIV_SEL */
{ 0x009E, 0x00 }, /* Not in datasheet */
{ 0x0102, 0x01 }, /* Enable outputs */
{ 0x013F, 0x00 }, /* Not in datasheet */
{ 0x0140, 0x00 }, /* Not in datasheet */
{ 0x0141, 0x40 }, /* OUT LOS */
{ 0x0202, 0x00 }, /* XAXB_FREQ_OFFSET (=0)*/
{ 0x0203, 0x00 },
{ 0x0204, 0x00 },
{ 0x0205, 0x00 },
{ 0x0206, 0x00 }, /* PXAXB (2^x) */
{ 0x0208, 0x00 }, /* Px divider setting (usually 0) */
{ 0x0209, 0x00 },
{ 0x020A, 0x00 },
{ 0x020B, 0x00 },
{ 0x020C, 0x00 },
{ 0x020D, 0x00 },
{ 0x020E, 0x00 },
{ 0x020F, 0x00 },
{ 0x0210, 0x00 },
{ 0x0211, 0x00 },
{ 0x0212, 0x00 },
{ 0x0213, 0x00 },
{ 0x0214, 0x00 },
{ 0x0215, 0x00 },
{ 0x0216, 0x00 },
{ 0x0217, 0x00 },
{ 0x0218, 0x00 },
{ 0x0219, 0x00 },
{ 0x021A, 0x00 },
{ 0x021B, 0x00 },
{ 0x021C, 0x00 },
{ 0x021D, 0x00 },
{ 0x021E, 0x00 },
{ 0x021F, 0x00 },
{ 0x0220, 0x00 },
{ 0x0221, 0x00 },
{ 0x0222, 0x00 },
{ 0x0223, 0x00 },
{ 0x0224, 0x00 },
{ 0x0225, 0x00 },
{ 0x0226, 0x00 },
{ 0x0227, 0x00 },
{ 0x0228, 0x00 },
{ 0x0229, 0x00 },
{ 0x022A, 0x00 },
{ 0x022B, 0x00 },
{ 0x022C, 0x00 },
{ 0x022D, 0x00 },
{ 0x022E, 0x00 },
{ 0x022F, 0x00 }, /* Px divider setting (usually 0) end */
{ 0x026B, 0x00 }, /* DESIGN_ID (ASCII string) */
{ 0x026C, 0x00 },
{ 0x026D, 0x00 },
{ 0x026E, 0x00 },
{ 0x026F, 0x00 },
{ 0x0270, 0x00 },
{ 0x0271, 0x00 },
{ 0x0272, 0x00 }, /* DESIGN_ID (ASCII string) end */
{ 0x0339, 0x1F }, /* N_FSTEP_MSK */
{ 0x033B, 0x00 }, /* Nx_FSTEPW (Frequency step) */
{ 0x033C, 0x00 },
{ 0x033D, 0x00 },
{ 0x033E, 0x00 },
{ 0x033F, 0x00 },
{ 0x0340, 0x00 },
{ 0x0341, 0x00 },
{ 0x0342, 0x00 },
{ 0x0343, 0x00 },
{ 0x0344, 0x00 },
{ 0x0345, 0x00 },
{ 0x0346, 0x00 },
{ 0x0347, 0x00 },
{ 0x0348, 0x00 },
{ 0x0349, 0x00 },
{ 0x034A, 0x00 },
{ 0x034B, 0x00 },
{ 0x034C, 0x00 },
{ 0x034D, 0x00 },
{ 0x034E, 0x00 },
{ 0x034F, 0x00 },
{ 0x0350, 0x00 },
{ 0x0351, 0x00 },
{ 0x0352, 0x00 },
{ 0x0353, 0x00 },
{ 0x0354, 0x00 },
{ 0x0355, 0x00 },
{ 0x0356, 0x00 },
{ 0x0357, 0x00 },
{ 0x0358, 0x00 }, /* Nx_FSTEPW (Frequency step) end */
{ 0x0359, 0x00 }, /* Nx_DELAY */
{ 0x035A, 0x00 },
{ 0x035B, 0x00 },
{ 0x035C, 0x00 },
{ 0x035D, 0x00 },
{ 0x035E, 0x00 },
{ 0x035F, 0x00 },
{ 0x0360, 0x00 },
{ 0x0361, 0x00 },
{ 0x0362, 0x00 }, /* Nx_DELAY end */
{ 0x0802, 0x00 }, /* Not in datasheet */
{ 0x0803, 0x00 }, /* Not in datasheet */
{ 0x0804, 0x00 }, /* Not in datasheet */
{ 0x090E, 0x02 }, /* XAXB_EXTCLK_EN=0 XAXB_PDNB=1 (use XTAL) */
{ 0x091C, 0x04 }, /* ZDM_EN=4 (Normal mode) */
{ 0x0943, 0x00 }, /* IO_VDD_SEL=0 (0=1v8, use 1=3v3) */
{ 0x0949, 0x00 }, /* IN_EN (disable input clocks) */
{ 0x094A, 0x00 }, /* INx_TO_PFD_EN (disabled) */
{ 0x0A02, 0x00 }, /* Not in datasheet */
{ 0x0B44, 0x0F }, /* PDIV_ENB (datasheet does not mention what it is) */
};
/* Read and interpret a 44-bit followed by a 32-bit value in the regmap */
static int si5341_decode_44_32(struct regmap *regmap, unsigned int reg,
u64 *val1, u32 *val2)
{
int err;
u8 r[10];
err = regmap_bulk_read(regmap, reg, r, 10);
if (err < 0)
return err;
*val1 = ((u64)((r[5] & 0x0f) << 8 | r[4]) << 32) |
(get_unaligned_le32(r));
*val2 = get_unaligned_le32(&r[6]);
return 0;
}
static int si5341_encode_44_32(struct regmap *regmap, unsigned int reg,
u64 n_num, u32 n_den)
{
u8 r[10];
/* Shift left as far as possible without overflowing */
while (!(n_num & BIT_ULL(43)) && !(n_den & BIT(31))) {
n_num <<= 1;
n_den <<= 1;
}
/* 44 bits (6 bytes) numerator */
put_unaligned_le32(n_num, r);
r[4] = (n_num >> 32) & 0xff;
r[5] = (n_num >> 40) & 0x0f;
/* 32 bits denominator */
put_unaligned_le32(n_den, &r[6]);
/* Program the fraction */
return regmap_bulk_write(regmap, reg, r, sizeof(r));
}
/* VCO, we assume it runs at a constant frequency */
static unsigned long si5341_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_si5341 *data = to_clk_si5341(hw);
int err;
u64 res;
u64 m_num;
u32 m_den;
unsigned int shift;
/* Assume that PDIV is not being used, just read the PLL setting */
err = si5341_decode_44_32(data->regmap, SI5341_PLL_M_NUM,
&m_num, &m_den);
if (err < 0)
return 0;
if (!m_num || !m_den)
return 0;
/*
* Though m_num is 64-bit, only the upper bits are actually used. While
* calculating m_num and m_den, they are shifted as far as possible to
* the left. To avoid 96-bit division here, we just shift them back so
* we can do with just 64 bits.
*/
shift = 0;
res = m_num;
while (res & 0xffff00000000ULL) {
++shift;
res >>= 1;
}
res *= parent_rate;
do_div(res, (m_den >> shift));
/* We cannot return the actual frequency in 32 bit, store it locally */
data->freq_vco = res;
/* Report kHz since the value is out of range */
do_div(res, 1000);
return (unsigned long)res;
}
static const struct clk_ops si5341_clk_ops = {
.recalc_rate = si5341_clk_recalc_rate,
};
/* Synthesizers, there are 5 synthesizers that connect to any of the outputs */
/* The synthesizer is on if all power and enable bits are set */
static int si5341_synth_clk_is_on(struct clk_hw *hw)
{
struct clk_si5341_synth *synth = to_clk_si5341_synth(hw);
int err;
u32 val;
u8 index = synth->index;
err = regmap_read(synth->data->regmap,
SI5341_SYNTH_N_CLK_TO_OUTX_EN, &val);
if (err < 0)
return 0;
if (!(val & BIT(index)))
return 0;
err = regmap_read(synth->data->regmap, SI5341_SYNTH_N_PDNB, &val);
if (err < 0)
return 0;
if (!(val & BIT(index)))
return 0;
/* This bit must be 0 for the synthesizer to receive clock input */
err = regmap_read(synth->data->regmap, SI5341_SYNTH_N_CLK_DIS, &val);
if (err < 0)
return 0;
return !(val & BIT(index));
}
static void si5341_synth_clk_unprepare(struct clk_hw *hw)
{
struct clk_si5341_synth *synth = to_clk_si5341_synth(hw);
u8 index = synth->index; /* In range 0..5 */
u8 mask = BIT(index);
/* Disable output */
regmap_update_bits(synth->data->regmap,
SI5341_SYNTH_N_CLK_TO_OUTX_EN, mask, 0);
/* Power down */
regmap_update_bits(synth->data->regmap,
SI5341_SYNTH_N_PDNB, mask, 0);
/* Disable clock input to synth (set to 1 to disable) */
regmap_update_bits(synth->data->regmap,
SI5341_SYNTH_N_CLK_DIS, mask, mask);
}
static int si5341_synth_clk_prepare(struct clk_hw *hw)
{
struct clk_si5341_synth *synth = to_clk_si5341_synth(hw);
int err;
u8 index = synth->index;
u8 mask = BIT(index);
/* Power up */
err = regmap_update_bits(synth->data->regmap,
SI5341_SYNTH_N_PDNB, mask, mask);
if (err < 0)
return err;
/* Enable clock input to synth (set bit to 0 to enable) */
err = regmap_update_bits(synth->data->regmap,
SI5341_SYNTH_N_CLK_DIS, mask, 0);
if (err < 0)
return err;
/* Enable output */
return regmap_update_bits(synth->data->regmap,
SI5341_SYNTH_N_CLK_TO_OUTX_EN, mask, mask);
}
/* Synth clock frequency: Fvco * n_den / n_den, with Fvco in 13500-14256 MHz */
static unsigned long si5341_synth_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_si5341_synth *synth = to_clk_si5341_synth(hw);
u64 f;
u64 n_num;
u32 n_den;
int err;
err = si5341_decode_44_32(synth->data->regmap,
SI5341_SYNTH_N_NUM(synth->index), &n_num, &n_den);
if (err < 0)
return err;
/*
* n_num and n_den are shifted left as much as possible, so to prevent
* overflow in 64-bit math, we shift n_den 4 bits to the right
*/
f = synth->data->freq_vco;
f *= n_den >> 4;
/* Now we need to to 64-bit division: f/n_num */
/* And compensate for the 4 bits we dropped */
f = div64_u64(f, (n_num >> 4));
return f;
}
static long si5341_synth_clk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct clk_si5341_synth *synth = to_clk_si5341_synth(hw);
u64 f;
/* The synthesizer accuracy is such that anything in range will work */
f = synth->data->freq_vco;
do_div(f, SI5341_SYNTH_N_MAX);
if (rate < f)
return f;
f = synth->data->freq_vco;
do_div(f, SI5341_SYNTH_N_MIN);
if (rate > f)
return f;
return rate;
}
static int si5341_synth_program(struct clk_si5341_synth *synth,
u64 n_num, u32 n_den, bool is_integer)
{
int err;
u8 index = synth->index;
err = si5341_encode_44_32(synth->data->regmap,
SI5341_SYNTH_N_NUM(index), n_num, n_den);
err = regmap_update_bits(synth->data->regmap,
SI5341_SYNTH_N_PIBYP, BIT(index), is_integer ? BIT(index) : 0);
if (err < 0)
return err;
return regmap_write(synth->data->regmap,
SI5341_SYNTH_N_UPD(index), 0x01);
}
static int si5341_synth_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_si5341_synth *synth = to_clk_si5341_synth(hw);
u64 n_num;
u32 n_den;
u32 r;
u32 g;
bool is_integer;
n_num = synth->data->freq_vco;
n_den = rate;
/* see if there's an integer solution */
r = do_div(n_num, rate);
is_integer = (r == 0);
if (is_integer) {
/* Integer divider equal to n_num */
n_den = 1;
} else {
/* Calculate a fractional solution */
g = gcd(r, rate);
n_den = rate / g;
n_num *= n_den;
n_num += r / g;
}
dev_dbg(&synth->data->i2c_client->dev,
"%s(%u): n=0x%llx d=0x%x %s\n", __func__,
synth->index, n_num, n_den,
is_integer ? "int" : "frac");
return si5341_synth_program(synth, n_num, n_den, is_integer);
}
static const struct clk_ops si5341_synth_clk_ops = {
.is_prepared = si5341_synth_clk_is_on,
.prepare = si5341_synth_clk_prepare,
.unprepare = si5341_synth_clk_unprepare,
.recalc_rate = si5341_synth_clk_recalc_rate,
.round_rate = si5341_synth_clk_round_rate,
.set_rate = si5341_synth_clk_set_rate,
};
static int si5341_output_clk_is_on(struct clk_hw *hw)
{
struct clk_si5341_output *output = to_clk_si5341_output(hw);
int err;
u32 val;
err = regmap_read(output->data->regmap,
SI5341_OUT_CONFIG(output), &val);
if (err < 0)
return err;
/* Bit 0=PDN, 1=OE so only a value of 0x2 enables the output */
return (val & 0x03) == SI5341_OUT_CFG_OE;
}
/* Disables and then powers down the output */
static void si5341_output_clk_unprepare(struct clk_hw *hw)
{
struct clk_si5341_output *output = to_clk_si5341_output(hw);
regmap_update_bits(output->data->regmap,
SI5341_OUT_CONFIG(output),
SI5341_OUT_CFG_OE, 0);
regmap_update_bits(output->data->regmap,
SI5341_OUT_CONFIG(output),
SI5341_OUT_CFG_PDN, SI5341_OUT_CFG_PDN);
}
/* Powers up and then enables the output */
static int si5341_output_clk_prepare(struct clk_hw *hw)
{
struct clk_si5341_output *output = to_clk_si5341_output(hw);
int err;
err = regmap_update_bits(output->data->regmap,
SI5341_OUT_CONFIG(output),
SI5341_OUT_CFG_PDN, 0);
if (err < 0)
return err;
return regmap_update_bits(output->data->regmap,
SI5341_OUT_CONFIG(output),
SI5341_OUT_CFG_OE, SI5341_OUT_CFG_OE);
}
static unsigned long si5341_output_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_si5341_output *output = to_clk_si5341_output(hw);
int err;
u32 val;
u32 r_divider;
u8 r[3];
err = regmap_bulk_read(output->data->regmap,
SI5341_OUT_R_REG(output), r, 3);
if (err < 0)
return err;
/* Calculate value as 24-bit integer*/
r_divider = r[2] << 16 | r[1] << 8 | r[0];
/* If Rx_REG is zero, the divider is disabled, so return a "0" rate */
if (!r_divider)
return 0;
/* Divider is 2*(Rx_REG+1) */
r_divider += 1;
r_divider <<= 1;
err = regmap_read(output->data->regmap,
SI5341_OUT_CONFIG(output), &val);
if (err < 0)
return err;
if (val & SI5341_OUT_CFG_RDIV_FORCE2)
r_divider = 2;
return parent_rate / r_divider;
}
static long si5341_output_clk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned long r;
r = *parent_rate >> 1;
/* If rate is an even divisor, no changes to parent required */
if (r && !(r % rate))
return (long)rate;
if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
if (rate > 200000000) {
/* minimum r-divider is 2 */
r = 2;
} else {
/* Take a parent frequency near 400 MHz */
r = (400000000u / rate) & ~1;
}
*parent_rate = r * rate;
} else {
/* We cannot change our parent's rate, report what we can do */
r /= rate;
rate = *parent_rate / (r << 1);
}
return rate;
}
static int si5341_output_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_si5341_output *output = to_clk_si5341_output(hw);
/* Frequency divider is (r_div + 1) * 2 */
u32 r_div = (parent_rate / rate) >> 1;
int err;
u8 r[3];
if (r_div <= 1)
r_div = 0;
else if (r_div >= BIT(24))
r_div = BIT(24) - 1;
else
--r_div;
/* For a value of "2", we set the "OUT0_RDIV_FORCE2" bit */
err = regmap_update_bits(output->data->regmap,
SI5341_OUT_CONFIG(output),
SI5341_OUT_CFG_RDIV_FORCE2,
(r_div == 0) ? SI5341_OUT_CFG_RDIV_FORCE2 : 0);
if (err < 0)
return err;
/* Always write Rx_REG, because a zero value disables the divider */
r[0] = r_div ? (r_div & 0xff) : 1;
r[1] = (r_div >> 8) & 0xff;
r[2] = (r_div >> 16) & 0xff;
err = regmap_bulk_write(output->data->regmap,
SI5341_OUT_R_REG(output), r, 3);
return 0;
}
static int si5341_output_reparent(struct clk_si5341_output *output, u8 index)
{
return regmap_update_bits(output->data->regmap,
SI5341_OUT_MUX_SEL(output), 0x07, index);
}
static int si5341_output_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_si5341_output *output = to_clk_si5341_output(hw);
if (index >= output->data->num_synth)
return -EINVAL;
return si5341_output_reparent(output, index);
}
static u8 si5341_output_get_parent(struct clk_hw *hw)
{
struct clk_si5341_output *output = to_clk_si5341_output(hw);
int err;
u32 val;
err = regmap_read(output->data->regmap,
SI5341_OUT_MUX_SEL(output), &val);
return val & 0x7;
}
static const struct clk_ops si5341_output_clk_ops = {
.is_prepared = si5341_output_clk_is_on,
.prepare = si5341_output_clk_prepare,
.unprepare = si5341_output_clk_unprepare,
.recalc_rate = si5341_output_clk_recalc_rate,
.round_rate = si5341_output_clk_round_rate,
.set_rate = si5341_output_clk_set_rate,
.set_parent = si5341_output_set_parent,
.get_parent = si5341_output_get_parent,
};
/*
* The chip can be bought in a pre-programmed version, or one can program the
* NVM in the chip to boot up in a preset mode. This routine tries to determine
* if that's the case, or if we need to reset and program everything from
* scratch. Returns negative error, or true/false.
*/
static int si5341_is_programmed_already(struct clk_si5341 *data)
{
int err;
u8 r[4];
/* Read the PLL divider value, it must have a non-zero value */
err = regmap_bulk_read(data->regmap, SI5341_PLL_M_DEN,
r, ARRAY_SIZE(r));
if (err < 0)
return err;
return !!get_unaligned_le32(r);
}
static struct clk_hw *
of_clk_si5341_get(struct of_phandle_args *clkspec, void *_data)
{
struct clk_si5341 *data = _data;
unsigned int idx = clkspec->args[1];
unsigned int group = clkspec->args[0];
switch (group) {
case 0:
if (idx >= data->num_outputs) {
dev_err(&data->i2c_client->dev,
"invalid output index %u\n", idx);
return ERR_PTR(-EINVAL);
}
return &data->clk[idx].hw;
case 1:
if (idx >= data->num_synth) {
dev_err(&data->i2c_client->dev,
"invalid synthesizer index %u\n", idx);
return ERR_PTR(-EINVAL);
}
return &data->synth[idx].hw;
case 2:
if (idx > 0) {
dev_err(&data->i2c_client->dev,
"invalid PLL index %u\n", idx);
return ERR_PTR(-EINVAL);
}
return &data->hw;
default:
dev_err(&data->i2c_client->dev, "invalid group %u\n", group);
return ERR_PTR(-EINVAL);
}
}
static int si5341_probe_chip_id(struct clk_si5341 *data)
{
int err;
u8 reg[4];
u16 model;
err = regmap_bulk_read(data->regmap, SI5341_PN_BASE, reg,
ARRAY_SIZE(reg));
if (err < 0) {
dev_err(&data->i2c_client->dev, "Failed to read chip ID\n");
return err;
}
model = get_unaligned_le16(reg);
dev_info(&data->i2c_client->dev, "Chip: %x Grade: %u Rev: %u\n",
model, reg[2], reg[3]);
switch (model) {
case 0x5340:
data->num_outputs = SI5340_MAX_NUM_OUTPUTS;
data->num_synth = SI5340_NUM_SYNTH;
data->reg_output_offset = si5340_reg_output_offset;
data->reg_rdiv_offset = si5340_reg_rdiv_offset;
break;
case 0x5341:
data->num_outputs = SI5341_MAX_NUM_OUTPUTS;
data->num_synth = SI5341_NUM_SYNTH;
data->reg_output_offset = si5341_reg_output_offset;
data->reg_rdiv_offset = si5341_reg_rdiv_offset;
break;
default:
dev_err(&data->i2c_client->dev, "Model '%x' not supported\n",
model);
return -EINVAL;
}
return 0;
}
/* Read active settings into the regmap cache for later reference */
static int si5341_read_settings(struct clk_si5341 *data)
{
int err;
u8 i;
u8 r[10];
err = regmap_bulk_read(data->regmap, SI5341_PLL_M_NUM, r, 10);
if (err < 0)
return err;
err = regmap_bulk_read(data->regmap,
SI5341_SYNTH_N_CLK_TO_OUTX_EN, r, 3);
if (err < 0)
return err;
err = regmap_bulk_read(data->regmap,
SI5341_SYNTH_N_CLK_DIS, r, 1);
if (err < 0)
return err;
for (i = 0; i < data->num_synth; ++i) {
err = regmap_bulk_read(data->regmap,
SI5341_SYNTH_N_NUM(i), r, 10);
if (err < 0)
return err;
}
for (i = 0; i < data->num_outputs; ++i) {
err = regmap_bulk_read(data->regmap,
data->reg_output_offset[i], r, 4);
if (err < 0)
return err;
err = regmap_bulk_read(data->regmap,
data->reg_rdiv_offset[i], r, 3);
if (err < 0)
return err;
}
return 0;
}
static int si5341_write_multiple(struct clk_si5341 *data,
const struct si5341_reg_default *values, unsigned int num_values)
{
unsigned int i;
int res;
for (i = 0; i < num_values; ++i) {
res = regmap_write(data->regmap,
values[i].address, values[i].value);
if (res < 0) {
dev_err(&data->i2c_client->dev,
"Failed to write %#x:%#x\n",
values[i].address, values[i].value);
return res;
}
}
return 0;
}
static const struct si5341_reg_default si5341_preamble[] = {
{ 0x0B25, 0x00 },
{ 0x0502, 0x01 },
{ 0x0505, 0x03 },
{ 0x0957, 0x1F },
{ 0x0B4E, 0x1A },
};
static int si5341_send_preamble(struct clk_si5341 *data)
{
int res;
u32 revision;
/* For revision 2 and up, the values are slightly different */
res = regmap_read(data->regmap, SI5341_DEVICE_REV, &revision);
if (res < 0)
return res;
/* Write "preamble" as specified by datasheet */
res = regmap_write(data->regmap, 0xB24, revision < 2 ? 0xD8 : 0xC0);
if (res < 0)
return res;
res = si5341_write_multiple(data,
si5341_preamble, ARRAY_SIZE(si5341_preamble));
if (res < 0)
return res;
/* Datasheet specifies a 300ms wait after sending the preamble */
msleep(300);
return 0;
}
/* Perform a soft reset and write post-amble */
static int si5341_finalize_defaults(struct clk_si5341 *data)
{
int res;
u32 revision;
res = regmap_read(data->regmap, SI5341_DEVICE_REV, &revision);
if (res < 0)
return res;
dev_dbg(&data->i2c_client->dev, "%s rev=%u\n", __func__, revision);
res = regmap_write(data->regmap, SI5341_SOFT_RST, 0x01);
if (res < 0)
return res;
/* Datasheet does not explain these nameless registers */
res = regmap_write(data->regmap, 0xB24, revision < 2 ? 0xDB : 0xC3);
if (res < 0)
return res;
res = regmap_write(data->regmap, 0x0B25, 0x02);
if (res < 0)
return res;
return 0;
}
static const struct regmap_range si5341_regmap_volatile_range[] = {
regmap_reg_range(0x000C, 0x0012), /* Status */
regmap_reg_range(0x001C, 0x001E), /* reset, finc/fdec */
regmap_reg_range(0x00E2, 0x00FE), /* NVM, interrupts, device ready */
/* Update bits for synth config */
regmap_reg_range(SI5341_SYNTH_N_UPD(0), SI5341_SYNTH_N_UPD(0)),
regmap_reg_range(SI5341_SYNTH_N_UPD(1), SI5341_SYNTH_N_UPD(1)),
regmap_reg_range(SI5341_SYNTH_N_UPD(2), SI5341_SYNTH_N_UPD(2)),
regmap_reg_range(SI5341_SYNTH_N_UPD(3), SI5341_SYNTH_N_UPD(3)),
regmap_reg_range(SI5341_SYNTH_N_UPD(4), SI5341_SYNTH_N_UPD(4)),
};
static const struct regmap_access_table si5341_regmap_volatile = {
.yes_ranges = si5341_regmap_volatile_range,
.n_yes_ranges = ARRAY_SIZE(si5341_regmap_volatile_range),
};
/* Pages 0, 1, 2, 3, 9, A, B are valid, so there are 12 pages */
static const struct regmap_range_cfg si5341_regmap_ranges[] = {
{
.range_min = 0,
.range_max = SI5341_REGISTER_MAX,
.selector_reg = SI5341_PAGE,
.selector_mask = 0xff,
.selector_shift = 0,
.window_start = 0,
.window_len = 256,
},
};
static const struct regmap_config si5341_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.cache_type = REGCACHE_RBTREE,
.ranges = si5341_regmap_ranges,
.num_ranges = ARRAY_SIZE(si5341_regmap_ranges),
.max_register = SI5341_REGISTER_MAX,
.volatile_table = &si5341_regmap_volatile,
};
static int si5341_dt_parse_dt(struct i2c_client *client,
struct clk_si5341_output_config *config)
{
struct device_node *child;
struct device_node *np = client->dev.of_node;
u32 num;
u32 val;
memset(config, 0, sizeof(struct clk_si5341_output_config) *
SI5341_MAX_NUM_OUTPUTS);
for_each_child_of_node(np, child) {
if (of_property_read_u32(child, "reg", &num)) {
dev_err(&client->dev, "missing reg property of %s\n",
child->name);
goto put_child;
}
if (num >= SI5341_MAX_NUM_OUTPUTS) {
dev_err(&client->dev, "invalid clkout %d\n", num);
goto put_child;
}
if (!of_property_read_u32(child, "silabs,format", &val)) {
/* Set cm and ampl conservatively to 3v3 settings */
switch (val) {
case 1: /* normal differential */
config[num].out_cm_ampl_bits = 0x33;
break;
case 2: /* low-power differential */
config[num].out_cm_ampl_bits = 0x13;
break;
case 4: /* LVCMOS */
config[num].out_cm_ampl_bits = 0x33;
/* Set SI recommended impedance for LVCMOS */
config[num].out_format_drv_bits |= 0xc0;
break;
default:
dev_err(&client->dev,
"invalid silabs,format %u for %u\n",
val, num);
goto put_child;
}
config[num].out_format_drv_bits &= ~0x07;
config[num].out_format_drv_bits |= val & 0x07;
/* Always enable the SYNC feature */
config[num].out_format_drv_bits |= 0x08;
}
if (!of_property_read_u32(child, "silabs,common-mode", &val)) {
if (val > 0xf) {
dev_err(&client->dev,
"invalid silabs,common-mode %u\n",
val);
goto put_child;
}
config[num].out_cm_ampl_bits &= 0xf0;
config[num].out_cm_ampl_bits |= val & 0x0f;
}
if (!of_property_read_u32(child, "silabs,amplitude", &val)) {
if (val > 0xf) {
dev_err(&client->dev,
"invalid silabs,amplitude %u\n",
val);
goto put_child;
}
config[num].out_cm_ampl_bits &= 0x0f;
config[num].out_cm_ampl_bits |= (val << 4) & 0xf0;
}
if (of_property_read_bool(child, "silabs,disable-high"))
config[num].out_format_drv_bits |= 0x10;
config[num].synth_master =
of_property_read_bool(child, "silabs,synth-master");
config[num].always_on =
of_property_read_bool(child, "always-on");
}
return 0;
put_child:
of_node_put(child);
return -EINVAL;
}
/*
* If not pre-configured, calculate and set the PLL configuration manually.
* For low-jitter performance, the PLL should be set such that the synthesizers
* only need integer division.
* Without any user guidance, we'll set the PLL to 14GHz, which still allows
* the chip to generate any frequency on its outputs, but jitter performance
* may be sub-optimal.
*/
static int si5341_initialize_pll(struct clk_si5341 *data)
{
struct device_node *np = data->i2c_client->dev.of_node;
u32 m_num = 0;
u32 m_den = 0;
if (of_property_read_u32(np, "silabs,pll-m-num", &m_num)) {
dev_err(&data->i2c_client->dev,
"PLL configuration requires silabs,pll-m-num\n");
}
if (of_property_read_u32(np, "silabs,pll-m-den", &m_den)) {
dev_err(&data->i2c_client->dev,
"PLL configuration requires silabs,pll-m-den\n");
}
if (!m_num || !m_den) {
dev_err(&data->i2c_client->dev,
"PLL configuration invalid, assume 14GHz\n");
m_den = clk_get_rate(data->pxtal) / 10;
m_num = 1400000000;
}
return si5341_encode_44_32(data->regmap,
SI5341_PLL_M_NUM, m_num, m_den);
}
static int si5341_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct clk_si5341 *data;
struct clk_init_data init;
const char *root_clock_name;
const char *synth_clock_names[SI5341_NUM_SYNTH];
int err;
unsigned int i;
struct clk_si5341_output_config config[SI5341_MAX_NUM_OUTPUTS];
bool initialization_required;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->i2c_client = client;
data->pxtal = devm_clk_get(&client->dev, "xtal");
if (IS_ERR(data->pxtal)) {
if (PTR_ERR(data->pxtal) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_err(&client->dev, "Missing xtal clock input\n");
}
err = si5341_dt_parse_dt(client, config);
if (err)
return err;
if (of_property_read_string(client->dev.of_node, "clock-output-names",
&init.name))
init.name = client->dev.of_node->name;
root_clock_name = init.name;
data->regmap = devm_regmap_init_i2c(client, &si5341_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
i2c_set_clientdata(client, data);
err = si5341_probe_chip_id(data);
if (err < 0)
return err;
/* "Activate" the xtal (usually a fixed clock) */
clk_prepare_enable(data->pxtal);
if (of_property_read_bool(client->dev.of_node, "silabs,reprogram")) {
initialization_required = true;
} else {
err = si5341_is_programmed_already(data);
if (err < 0)
return err;
initialization_required = !err;
}
if (initialization_required) {
/* Populate the regmap cache in preparation for "cache only" */
err = si5341_read_settings(data);
if (err < 0)
return err;
err = si5341_send_preamble(data);
if (err < 0)
return err;
/*
* We intend to send all 'final' register values in a single
* transaction. So cache all register writes until we're done
* configuring.
*/
regcache_cache_only(data->regmap, true);
/* Write the configuration pairs from the firmware blob */
err = si5341_write_multiple(data, si5341_reg_defaults,
ARRAY_SIZE(si5341_reg_defaults));
if (err < 0)
return err;
/* PLL configuration is required */
err = si5341_initialize_pll(data);
if (err < 0)
return err;
}
/* Register the PLL */
data->pxtal_name = __clk_get_name(data->pxtal);
init.parent_names = &data->pxtal_name;
init.num_parents = 1; /* For now, only XTAL input supported */
init.ops = &si5341_clk_ops;
init.flags = 0;
data->hw.init = &init;
err = devm_clk_hw_register(&client->dev, &data->hw);
if (err) {
dev_err(&client->dev, "clock registration failed\n");
return err;
}
init.num_parents = 1;
init.parent_names = &root_clock_name;
init.ops = &si5341_synth_clk_ops;
for (i = 0; i < data->num_synth; ++i) {
synth_clock_names[i] = devm_kasprintf(&client->dev, GFP_KERNEL,
"%s.N%u", client->dev.of_node->name, i);
init.name = synth_clock_names[i];
data->synth[i].index = i;
data->synth[i].data = data;
data->synth[i].hw.init = &init;
err = devm_clk_hw_register(&client->dev, &data->synth[i].hw);
if (err) {
dev_err(&client->dev,
"synth N%u registration failed\n", i);
}
}
init.num_parents = data->num_synth;
init.parent_names = synth_clock_names;
init.ops = &si5341_output_clk_ops;
for (i = 0; i < data->num_outputs; ++i) {
init.name = kasprintf(GFP_KERNEL, "%s.%d",
client->dev.of_node->name, i);
init.flags = config[i].synth_master ? CLK_SET_RATE_PARENT : 0;
data->clk[i].index = i;
data->clk[i].data = data;
data->clk[i].hw.init = &init;
if (config[i].out_format_drv_bits & 0x07) {
regmap_write(data->regmap,
SI5341_OUT_FORMAT(&data->clk[i]),
config[i].out_format_drv_bits);
regmap_write(data->regmap,
SI5341_OUT_CM(&data->clk[i]),
config[i].out_cm_ampl_bits);
}
err = devm_clk_hw_register(&client->dev, &data->clk[i].hw);
kfree(init.name); /* clock framework made a copy of the name */
if (err) {
dev_err(&client->dev,
"output %u registration failed\n", i);
return err;
}
if (config[i].always_on)
clk_prepare(data->clk[i].hw.clk);
}
err = of_clk_add_hw_provider(client->dev.of_node, of_clk_si5341_get,
data);
if (err) {
dev_err(&client->dev, "unable to add clk provider\n");
return err;
}
if (initialization_required) {
/* Synchronize */
regcache_cache_only(data->regmap, false);
err = regcache_sync(data->regmap);
if (err < 0)
return err;
err = si5341_finalize_defaults(data);
if (err < 0)
return err;
}
/* Free the names, clk framework makes copies */
for (i = 0; i < data->num_synth; ++i)
devm_kfree(&client->dev, (void *)synth_clock_names[i]);
return 0;
}
static const struct i2c_device_id si5341_id[] = {
{ "si5340", 0 },
{ "si5341", 1 },
{ }
};
MODULE_DEVICE_TABLE(i2c, si5341_id);
static const struct of_device_id clk_si5341_of_match[] = {
{ .compatible = "silabs,si5340" },
{ .compatible = "silabs,si5341" },
{ }
};
MODULE_DEVICE_TABLE(of, clk_si5341_of_match);
static struct i2c_driver si5341_driver = {
.driver = {
.name = "si5341",
.of_match_table = clk_si5341_of_match,
},
.probe = si5341_probe,
.id_table = si5341_id,
};
module_i2c_driver(si5341_driver);
MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
MODULE_DESCRIPTION("Si5341 driver");
MODULE_LICENSE("GPL");
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/math64.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/regmap.h> #include <linux/regmap.h>
...@@ -50,6 +51,11 @@ ...@@ -50,6 +51,11 @@
/* Lowest frequency synthesizeable using only the HS divider */ /* Lowest frequency synthesizeable using only the HS divider */
#define MIN_HSDIV_FREQ (FVCO_MIN / HS_DIV_MAX) #define MIN_HSDIV_FREQ (FVCO_MIN / HS_DIV_MAX)
/* Range and interpretation of the adjustment value */
#define DELTA_M_MAX 8161512
#define DELTA_M_FRAC_NUM 19
#define DELTA_M_FRAC_DEN 20000
enum si544_speed_grade { enum si544_speed_grade {
si544a, si544a,
si544b, si544b,
...@@ -71,12 +77,14 @@ struct clk_si544 { ...@@ -71,12 +77,14 @@ struct clk_si544 {
* @hs_div: 1st divider, 5..2046, must be even when >33 * @hs_div: 1st divider, 5..2046, must be even when >33
* @ls_div_bits: 2nd divider, as 2^x, range 0..5 * @ls_div_bits: 2nd divider, as 2^x, range 0..5
* If ls_div_bits is non-zero, hs_div must be even * If ls_div_bits is non-zero, hs_div must be even
* @delta_m: Frequency shift for small -950..+950 ppm changes, 24 bit
*/ */
struct clk_si544_muldiv { struct clk_si544_muldiv {
u32 fb_div_frac; u32 fb_div_frac;
u16 fb_div_int; u16 fb_div_int;
u16 hs_div; u16 hs_div;
u8 ls_div_bits; u8 ls_div_bits;
s32 delta_m;
}; };
/* Enables or disables the output driver */ /* Enables or disables the output driver */
...@@ -134,9 +142,30 @@ static int si544_get_muldiv(struct clk_si544 *data, ...@@ -134,9 +142,30 @@ static int si544_get_muldiv(struct clk_si544 *data,
settings->fb_div_int = reg[4] | (reg[5] & 0x07) << 8; settings->fb_div_int = reg[4] | (reg[5] & 0x07) << 8;
settings->fb_div_frac = reg[0] | reg[1] << 8 | reg[2] << 16 | settings->fb_div_frac = reg[0] | reg[1] << 8 | reg[2] << 16 |
reg[3] << 24; reg[3] << 24;
err = regmap_bulk_read(data->regmap, SI544_REG_ADPLL_DELTA_M0, reg, 3);
if (err)
return err;
/* Interpret as 24-bit signed number */
settings->delta_m = reg[0] << 8 | reg[1] << 16 | reg[2] << 24;
settings->delta_m >>= 8;
return 0; return 0;
} }
static int si544_set_delta_m(struct clk_si544 *data, s32 delta_m)
{
u8 reg[3];
reg[0] = delta_m;
reg[1] = delta_m >> 8;
reg[2] = delta_m >> 16;
return regmap_bulk_write(data->regmap, SI544_REG_ADPLL_DELTA_M0,
reg, 3);
}
static int si544_set_muldiv(struct clk_si544 *data, static int si544_set_muldiv(struct clk_si544 *data,
struct clk_si544_muldiv *settings) struct clk_si544_muldiv *settings)
{ {
...@@ -238,11 +267,15 @@ static int si544_calc_muldiv(struct clk_si544_muldiv *settings, ...@@ -238,11 +267,15 @@ static int si544_calc_muldiv(struct clk_si544_muldiv *settings,
do_div(vco, FXO); do_div(vco, FXO);
settings->fb_div_frac = vco; settings->fb_div_frac = vco;
/* Reset the frequency adjustment */
settings->delta_m = 0;
return 0; return 0;
} }
/* Calculate resulting frequency given the register settings */ /* Calculate resulting frequency given the register settings */
static unsigned long si544_calc_rate(struct clk_si544_muldiv *settings) static unsigned long si544_calc_center_rate(
const struct clk_si544_muldiv *settings)
{ {
u32 d = settings->hs_div * BIT(settings->ls_div_bits); u32 d = settings->hs_div * BIT(settings->ls_div_bits);
u64 vco; u64 vco;
...@@ -261,6 +294,25 @@ static unsigned long si544_calc_rate(struct clk_si544_muldiv *settings) ...@@ -261,6 +294,25 @@ static unsigned long si544_calc_rate(struct clk_si544_muldiv *settings)
return vco; return vco;
} }
static unsigned long si544_calc_rate(const struct clk_si544_muldiv *settings)
{
unsigned long rate = si544_calc_center_rate(settings);
s64 delta = (s64)rate * (DELTA_M_FRAC_NUM * settings->delta_m);
/*
* The clock adjustment is much smaller than 1 Hz, round to the
* nearest multiple. Apparently div64_s64 rounds towards zero, hence
* check the sign and adjust into the proper direction.
*/
if (settings->delta_m < 0)
delta -= ((s64)DELTA_M_MAX * DELTA_M_FRAC_DEN) / 2;
else
delta += ((s64)DELTA_M_MAX * DELTA_M_FRAC_DEN) / 2;
delta = div64_s64(delta, ((s64)DELTA_M_MAX * DELTA_M_FRAC_DEN));
return rate + delta;
}
static unsigned long si544_recalc_rate(struct clk_hw *hw, static unsigned long si544_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate) unsigned long parent_rate)
{ {
...@@ -279,33 +331,60 @@ static long si544_round_rate(struct clk_hw *hw, unsigned long rate, ...@@ -279,33 +331,60 @@ static long si544_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate) unsigned long *parent_rate)
{ {
struct clk_si544 *data = to_clk_si544(hw); struct clk_si544 *data = to_clk_si544(hw);
struct clk_si544_muldiv settings;
int err;
if (!is_valid_frequency(data, rate)) if (!is_valid_frequency(data, rate))
return -EINVAL; return -EINVAL;
err = si544_calc_muldiv(&settings, rate); /* The accuracy is less than 1 Hz, so any rate is possible */
if (err) return rate;
return err; }
return si544_calc_rate(&settings); /* Calculates the maximum "small" change, 950 * rate / 1000000 */
static unsigned long si544_max_delta(unsigned long rate)
{
u64 num = rate;
num *= DELTA_M_FRAC_NUM;
do_div(num, DELTA_M_FRAC_DEN);
return num;
}
static s32 si544_calc_delta(s32 delta, s32 max_delta)
{
s64 n = (s64)delta * DELTA_M_MAX;
return div_s64(n, max_delta);
} }
/*
* Update output frequency for "big" frequency changes
*/
static int si544_set_rate(struct clk_hw *hw, unsigned long rate, static int si544_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate) unsigned long parent_rate)
{ {
struct clk_si544 *data = to_clk_si544(hw); struct clk_si544 *data = to_clk_si544(hw);
struct clk_si544_muldiv settings; struct clk_si544_muldiv settings;
unsigned long center;
long max_delta;
long delta;
unsigned int old_oe_state; unsigned int old_oe_state;
int err; int err;
if (!is_valid_frequency(data, rate)) if (!is_valid_frequency(data, rate))
return -EINVAL; return -EINVAL;
/* Try using the frequency adjustment feature for a <= 950ppm change */
err = si544_get_muldiv(data, &settings);
if (err)
return err;
center = si544_calc_center_rate(&settings);
max_delta = si544_max_delta(center);
delta = rate - center;
if (abs(delta) <= max_delta)
return si544_set_delta_m(data,
si544_calc_delta(delta, max_delta));
/* Too big for the delta adjustment, need to reprogram */
err = si544_calc_muldiv(&settings, rate); err = si544_calc_muldiv(&settings, rate);
if (err) if (err)
return err; return err;
...@@ -321,6 +400,9 @@ static int si544_set_rate(struct clk_hw *hw, unsigned long rate, ...@@ -321,6 +400,9 @@ static int si544_set_rate(struct clk_hw *hw, unsigned long rate,
if (err < 0) if (err < 0)
return err; return err;
err = si544_set_delta_m(data, settings.delta_m);
if (err < 0)
return err;
err = si544_set_muldiv(data, &settings); err = si544_set_muldiv(data, &settings);
if (err < 0) if (err < 0)
......
...@@ -55,29 +55,27 @@ static unsigned long rockchip_mmc_recalc(struct clk_hw *hw, ...@@ -55,29 +55,27 @@ static unsigned long rockchip_mmc_recalc(struct clk_hw *hw,
static int rockchip_mmc_get_phase(struct clk_hw *hw) static int rockchip_mmc_get_phase(struct clk_hw *hw)
{ {
struct rockchip_mmc_clock *mmc_clock = to_mmc_clock(hw); struct rockchip_mmc_clock *mmc_clock = to_mmc_clock(hw);
unsigned long rate = clk_get_rate(hw->clk); unsigned long rate = clk_hw_get_rate(hw);
u32 raw_value; u32 raw_value;
u16 degrees; u16 degrees;
u32 delay_num = 0; u32 delay_num = 0;
/* See the comment for rockchip_mmc_set_phase below */ /* See the comment for rockchip_mmc_set_phase below */
if (!rate) { if (!rate)
pr_err("%s: invalid clk rate\n", __func__);
return -EINVAL; return -EINVAL;
}
raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift); raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift);
degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90; degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
if (raw_value & ROCKCHIP_MMC_DELAY_SEL) { if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
/* degrees/delaynum * 10000 */ /* degrees/delaynum * 1000000 */
unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) * unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) *
36 * (rate / 1000000); 36 * (rate / 10000);
delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK); delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET; delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
degrees += DIV_ROUND_CLOSEST(delay_num * factor, 10000); degrees += DIV_ROUND_CLOSEST(delay_num * factor, 1000000);
} }
return degrees % 360; return degrees % 360;
...@@ -86,7 +84,7 @@ static int rockchip_mmc_get_phase(struct clk_hw *hw) ...@@ -86,7 +84,7 @@ static int rockchip_mmc_get_phase(struct clk_hw *hw)
static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees) static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees)
{ {
struct rockchip_mmc_clock *mmc_clock = to_mmc_clock(hw); struct rockchip_mmc_clock *mmc_clock = to_mmc_clock(hw);
unsigned long rate = clk_get_rate(hw->clk); unsigned long rate = clk_hw_get_rate(hw);
u8 nineties, remainder; u8 nineties, remainder;
u8 delay_num; u8 delay_num;
u32 raw_value; u32 raw_value;
......
...@@ -803,6 +803,9 @@ static struct rockchip_clk_branch px30_clk_branches[] __initdata = { ...@@ -803,6 +803,9 @@ static struct rockchip_clk_branch px30_clk_branches[] __initdata = {
GATE(ACLK_GIC, "aclk_gic", "aclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(13), 12, GFLAGS), GATE(ACLK_GIC, "aclk_gic", "aclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(13), 12, GFLAGS),
GATE(ACLK_DCF, "aclk_dcf", "aclk_bus_pre", 0, PX30_CLKGATE_CON(13), 15, GFLAGS), GATE(ACLK_DCF, "aclk_dcf", "aclk_bus_pre", 0, PX30_CLKGATE_CON(13), 15, GFLAGS),
/* aclk_dmac is controlled by sgrf_soc_con1[11]. */
SGRF_GATE(ACLK_DMAC, "aclk_dmac", "aclk_bus_pre"),
GATE(0, "hclk_bus_niu", "hclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(13), 9, GFLAGS), GATE(0, "hclk_bus_niu", "hclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(13), 9, GFLAGS),
GATE(0, "hclk_rom", "hclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(13), 14, GFLAGS), GATE(0, "hclk_rom", "hclk_bus_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(13), 14, GFLAGS),
GATE(HCLK_PDM, "hclk_pdm", "hclk_bus_pre", 0, PX30_CLKGATE_CON(14), 1, GFLAGS), GATE(HCLK_PDM, "hclk_pdm", "hclk_bus_pre", 0, PX30_CLKGATE_CON(14), 1, GFLAGS),
...@@ -966,7 +969,6 @@ static void __init px30_clk_init(struct device_node *np) ...@@ -966,7 +969,6 @@ static void __init px30_clk_init(struct device_node *np)
{ {
struct rockchip_clk_provider *ctx; struct rockchip_clk_provider *ctx;
void __iomem *reg_base; void __iomem *reg_base;
struct clk *clk;
reg_base = of_iomap(np, 0); reg_base = of_iomap(np, 0);
if (!reg_base) { if (!reg_base) {
...@@ -981,14 +983,6 @@ static void __init px30_clk_init(struct device_node *np) ...@@ -981,14 +983,6 @@ static void __init px30_clk_init(struct device_node *np)
return; return;
} }
/* aclk_dmac is controlled by sgrf_soc_con1[11]. */
clk = clk_register_fixed_factor(NULL, "aclk_dmac", "aclk_bus_pre", 0, 1, 1);
if (IS_ERR(clk))
pr_warn("%s: could not register clock aclk_dmac: %ld\n",
__func__, PTR_ERR(clk));
else
rockchip_clk_add_lookup(ctx, clk, ACLK_DMAC);
rockchip_clk_register_plls(ctx, px30_pll_clks, rockchip_clk_register_plls(ctx, px30_pll_clks,
ARRAY_SIZE(px30_pll_clks), ARRAY_SIZE(px30_pll_clks),
PX30_GRF_SOC_STATUS0); PX30_GRF_SOC_STATUS0);
......
...@@ -110,6 +110,7 @@ static struct rockchip_cpuclk_rate_table rk3228_cpuclk_rates[] __initdata = { ...@@ -110,6 +110,7 @@ static struct rockchip_cpuclk_rate_table rk3228_cpuclk_rates[] __initdata = {
RK3228_CPUCLK_RATE(1608000000, 1, 7), RK3228_CPUCLK_RATE(1608000000, 1, 7),
RK3228_CPUCLK_RATE(1512000000, 1, 7), RK3228_CPUCLK_RATE(1512000000, 1, 7),
RK3228_CPUCLK_RATE(1488000000, 1, 5), RK3228_CPUCLK_RATE(1488000000, 1, 5),
RK3228_CPUCLK_RATE(1464000000, 1, 5),
RK3228_CPUCLK_RATE(1416000000, 1, 5), RK3228_CPUCLK_RATE(1416000000, 1, 5),
RK3228_CPUCLK_RATE(1392000000, 1, 5), RK3228_CPUCLK_RATE(1392000000, 1, 5),
RK3228_CPUCLK_RATE(1296000000, 1, 5), RK3228_CPUCLK_RATE(1296000000, 1, 5),
...@@ -255,7 +256,7 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = { ...@@ -255,7 +256,7 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = {
RK2928_CLKGATE_CON(4), 0, GFLAGS), RK2928_CLKGATE_CON(4), 0, GFLAGS),
/* PD_MISC */ /* PD_MISC */
MUX(0, "hdmiphy", mux_hdmiphy_p, CLK_SET_RATE_PARENT, MUX(SCLK_HDMI_PHY, "hdmiphy", mux_hdmiphy_p, CLK_SET_RATE_PARENT,
RK2928_MISC_CON, 13, 1, MFLAGS), RK2928_MISC_CON, 13, 1, MFLAGS),
MUX(0, "usb480m_phy", mux_usb480m_phy_p, CLK_SET_RATE_PARENT, MUX(0, "usb480m_phy", mux_usb480m_phy_p, CLK_SET_RATE_PARENT,
RK2928_MISC_CON, 14, 1, MFLAGS), RK2928_MISC_CON, 14, 1, MFLAGS),
......
...@@ -122,7 +122,6 @@ static struct rockchip_pll_rate_table rk3288_pll_rates[] = { ...@@ -122,7 +122,6 @@ static struct rockchip_pll_rate_table rk3288_pll_rates[] = {
RK3066_PLL_RATE( 160000000, 1, 80, 12), RK3066_PLL_RATE( 160000000, 1, 80, 12),
RK3066_PLL_RATE( 157500000, 1, 105, 16), RK3066_PLL_RATE( 157500000, 1, 105, 16),
RK3066_PLL_RATE( 126000000, 1, 84, 16), RK3066_PLL_RATE( 126000000, 1, 84, 16),
RK3066_PLL_RATE( 48000000, 1, 64, 32),
{ /* sentinel */ }, { /* sentinel */ },
}; };
...@@ -776,6 +775,9 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { ...@@ -776,6 +775,9 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
GATE(PCLK_GRF, "pclk_grf", "pclk_pd_alive", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(14), 11, GFLAGS), GATE(PCLK_GRF, "pclk_grf", "pclk_pd_alive", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(14), 11, GFLAGS),
GATE(0, "pclk_alive_niu", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 12, GFLAGS), GATE(0, "pclk_alive_niu", "pclk_pd_alive", 0, RK3288_CLKGATE_CON(14), 12, GFLAGS),
/* Watchdog pclk is controlled by RK3288_SGRF_SOC_CON0[1]. */
SGRF_GATE(PCLK_WDT, "pclk_wdt", "pclk_pd_alive"),
/* pclk_pd_pmu gates */ /* pclk_pd_pmu gates */
GATE(PCLK_PMU, "pclk_pmu", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(17), 0, GFLAGS), GATE(PCLK_PMU, "pclk_pmu", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(17), 0, GFLAGS),
GATE(0, "pclk_intmem1", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(17), 1, GFLAGS), GATE(0, "pclk_intmem1", "pclk_pd_pmu", CLK_IGNORE_UNUSED, RK3288_CLKGATE_CON(17), 1, GFLAGS),
...@@ -924,7 +926,6 @@ static struct syscore_ops rk3288_clk_syscore_ops = { ...@@ -924,7 +926,6 @@ static struct syscore_ops rk3288_clk_syscore_ops = {
static void __init rk3288_clk_init(struct device_node *np) static void __init rk3288_clk_init(struct device_node *np)
{ {
struct rockchip_clk_provider *ctx; struct rockchip_clk_provider *ctx;
struct clk *clk;
rk3288_cru_base = of_iomap(np, 0); rk3288_cru_base = of_iomap(np, 0);
if (!rk3288_cru_base) { if (!rk3288_cru_base) {
...@@ -939,14 +940,6 @@ static void __init rk3288_clk_init(struct device_node *np) ...@@ -939,14 +940,6 @@ static void __init rk3288_clk_init(struct device_node *np)
return; return;
} }
/* Watchdog pclk is controlled by RK3288_SGRF_SOC_CON0[1]. */
clk = clk_register_fixed_factor(NULL, "pclk_wdt", "pclk_pd_alive", 0, 1, 1);
if (IS_ERR(clk))
pr_warn("%s: could not register clock pclk_wdt: %ld\n",
__func__, PTR_ERR(clk));
else
rockchip_clk_add_lookup(ctx, clk, PCLK_WDT);
rockchip_clk_register_plls(ctx, rk3288_pll_clks, rockchip_clk_register_plls(ctx, rk3288_pll_clks,
ARRAY_SIZE(rk3288_pll_clks), ARRAY_SIZE(rk3288_pll_clks),
RK3288_GRF_SOC_STATUS1); RK3288_GRF_SOC_STATUS1);
......
...@@ -800,6 +800,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { ...@@ -800,6 +800,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
GATE(PCLK_SARADC, "pclk_saradc", "pclk_bus", 0, RK3328_CLKGATE_CON(17), 15, GFLAGS), GATE(PCLK_SARADC, "pclk_saradc", "pclk_bus", 0, RK3328_CLKGATE_CON(17), 15, GFLAGS),
GATE(0, "pclk_pmu", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(28), 3, GFLAGS), GATE(0, "pclk_pmu", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(28), 3, GFLAGS),
/* Watchdog pclk is controlled from the secure GRF */
SGRF_GATE(PCLK_WDT, "pclk_wdt", "pclk_bus"),
GATE(PCLK_USB3PHY_OTG, "pclk_usb3phy_otg", "pclk_phy_pre", 0, RK3328_CLKGATE_CON(28), 1, GFLAGS), GATE(PCLK_USB3PHY_OTG, "pclk_usb3phy_otg", "pclk_phy_pre", 0, RK3328_CLKGATE_CON(28), 1, GFLAGS),
GATE(PCLK_USB3PHY_PIPE, "pclk_usb3phy_pipe", "pclk_phy_pre", 0, RK3328_CLKGATE_CON(28), 2, GFLAGS), GATE(PCLK_USB3PHY_PIPE, "pclk_usb3phy_pipe", "pclk_phy_pre", 0, RK3328_CLKGATE_CON(28), 2, GFLAGS),
GATE(PCLK_USB3_GRF, "pclk_usb3_grf", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 2, GFLAGS), GATE(PCLK_USB3_GRF, "pclk_usb3_grf", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 2, GFLAGS),
......
...@@ -820,6 +820,9 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = { ...@@ -820,6 +820,9 @@ static struct rockchip_clk_branch rk3368_clk_branches[] __initdata = {
GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_pd_alive", 0, RK3368_CLKGATE_CON(22), 2, GFLAGS), GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_pd_alive", 0, RK3368_CLKGATE_CON(22), 2, GFLAGS),
GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_pd_alive", 0, RK3368_CLKGATE_CON(22), 1, GFLAGS), GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_pd_alive", 0, RK3368_CLKGATE_CON(22), 1, GFLAGS),
/* Watchdog pclk is controlled by sgrf_soc_con3[7]. */
SGRF_GATE(PCLK_WDT, "pclk_wdt", "pclk_pd_alive"),
/* /*
* pclk_vio gates * pclk_vio gates
* pclk_vio comes from the exactly same source as hclk_vio * pclk_vio comes from the exactly same source as hclk_vio
...@@ -871,7 +874,6 @@ static void __init rk3368_clk_init(struct device_node *np) ...@@ -871,7 +874,6 @@ static void __init rk3368_clk_init(struct device_node *np)
{ {
struct rockchip_clk_provider *ctx; struct rockchip_clk_provider *ctx;
void __iomem *reg_base; void __iomem *reg_base;
struct clk *clk;
reg_base = of_iomap(np, 0); reg_base = of_iomap(np, 0);
if (!reg_base) { if (!reg_base) {
...@@ -886,14 +888,6 @@ static void __init rk3368_clk_init(struct device_node *np) ...@@ -886,14 +888,6 @@ static void __init rk3368_clk_init(struct device_node *np)
return; return;
} }
/* Watchdog pclk is controlled by sgrf_soc_con3[7]. */
clk = clk_register_fixed_factor(NULL, "pclk_wdt", "pclk_pd_alive", 0, 1, 1);
if (IS_ERR(clk))
pr_warn("%s: could not register clock pclk_wdt: %ld\n",
__func__, PTR_ERR(clk));
else
rockchip_clk_add_lookup(ctx, clk, PCLK_WDT);
rockchip_clk_register_plls(ctx, rk3368_pll_clks, rockchip_clk_register_plls(ctx, rk3368_pll_clks,
ARRAY_SIZE(rk3368_pll_clks), ARRAY_SIZE(rk3368_pll_clks),
RK3368_GRF_SOC_STATUS0); RK3368_GRF_SOC_STATUS0);
......
...@@ -1304,6 +1304,9 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { ...@@ -1304,6 +1304,9 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
GATE(PCLK_PMU_INTR_ARB, "pclk_pmu_intr_arb", "pclk_alive", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(31), 9, GFLAGS), GATE(PCLK_PMU_INTR_ARB, "pclk_pmu_intr_arb", "pclk_alive", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(31), 9, GFLAGS),
GATE(PCLK_SGRF, "pclk_sgrf", "pclk_alive", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(31), 10, GFLAGS), GATE(PCLK_SGRF, "pclk_sgrf", "pclk_alive", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(31), 10, GFLAGS),
/* Watchdog pclk is controlled by RK3399 SECURE_GRF_SOC_CON3[8]. */
SGRF_GATE(PCLK_WDT, "pclk_wdt", "pclk_alive"),
GATE(SCLK_MIPIDPHY_REF, "clk_mipidphy_ref", "xin24m", 0, RK3399_CLKGATE_CON(11), 14, GFLAGS), GATE(SCLK_MIPIDPHY_REF, "clk_mipidphy_ref", "xin24m", 0, RK3399_CLKGATE_CON(11), 14, GFLAGS),
GATE(SCLK_DPHY_PLL, "clk_dphy_pll", "clk_mipidphy_ref", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(21), 0, GFLAGS), GATE(SCLK_DPHY_PLL, "clk_dphy_pll", "clk_mipidphy_ref", CLK_IGNORE_UNUSED, RK3399_CLKGATE_CON(21), 0, GFLAGS),
...@@ -1531,7 +1534,6 @@ static void __init rk3399_clk_init(struct device_node *np) ...@@ -1531,7 +1534,6 @@ static void __init rk3399_clk_init(struct device_node *np)
{ {
struct rockchip_clk_provider *ctx; struct rockchip_clk_provider *ctx;
void __iomem *reg_base; void __iomem *reg_base;
struct clk *clk;
reg_base = of_iomap(np, 0); reg_base = of_iomap(np, 0);
if (!reg_base) { if (!reg_base) {
...@@ -1546,14 +1548,6 @@ static void __init rk3399_clk_init(struct device_node *np) ...@@ -1546,14 +1548,6 @@ static void __init rk3399_clk_init(struct device_node *np)
return; return;
} }
/* Watchdog pclk is controlled by RK3399 SECURE_GRF_SOC_CON3[8]. */
clk = clk_register_fixed_factor(NULL, "pclk_wdt", "pclk_alive", 0, 1, 1);
if (IS_ERR(clk))
pr_warn("%s: could not register clock pclk_wdt: %ld\n",
__func__, PTR_ERR(clk));
else
rockchip_clk_add_lookup(ctx, clk, PCLK_WDT);
rockchip_clk_register_plls(ctx, rk3399_pll_clks, rockchip_clk_register_plls(ctx, rk3399_pll_clks,
ARRAY_SIZE(rk3399_pll_clks), -1); ARRAY_SIZE(rk3399_pll_clks), -1);
......
...@@ -820,6 +820,10 @@ struct rockchip_clk_branch { ...@@ -820,6 +820,10 @@ struct rockchip_clk_branch {
.gate_offset = -1, \ .gate_offset = -1, \
} }
/* SGRF clocks are only accessible from secure mode, so not controllable */
#define SGRF_GATE(_id, cname, pname) \
FACTOR(_id, cname, pname, 0, 1, 1)
struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np, struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
void __iomem *base, unsigned long nr_clks); void __iomem *base, unsigned long nr_clks);
void rockchip_clk_of_add_provider(struct device_node *np, void rockchip_clk_of_add_provider(struct device_node *np,
......
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
#define SCLK_WIFI 141 #define SCLK_WIFI 141
#define SCLK_OTGPHY0 142 #define SCLK_OTGPHY0 142
#define SCLK_OTGPHY1 143 #define SCLK_OTGPHY1 143
#define SCLK_HDMI_PHY 144
/* dclk gates */ /* dclk gates */
#define DCLK_VOP 190 #define DCLK_VOP 190
......
...@@ -173,6 +173,7 @@ ...@@ -173,6 +173,7 @@
#define PCLK_DCF 233 #define PCLK_DCF 233
#define PCLK_SARADC 234 #define PCLK_SARADC 234
#define PCLK_ACODECPHY 235 #define PCLK_ACODECPHY 235
#define PCLK_WDT 236
/* hclk gates */ /* hclk gates */
#define HCLK_PERI 308 #define HCLK_PERI 308
......
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