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:
input audio clocks from host system.
- ln-psia1-mclk, ln-psia2-mclk : Optional input audio clocks from
external connector.
- ln-spdif-mclk : 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-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
This driver uses SCPI Message Protocol to interact with the
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
tristate "Clock driver for SiLabs 5351A/B/C"
depends on I2C
......
......@@ -49,6 +49,7 @@ obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.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_SI514) += clk-si514.o
obj-$(CONFIG_COMMON_CLK_SI544) += clk-si544.o
......
......@@ -16,6 +16,14 @@ config CLK_BCM_63XX
Enable common clock framework support for Broadcom BCM63xx DSL SoCs
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
bool "Broadcom Kona CCU clock support"
depends on ARCH_BCM_MOBILE || COMPILE_TEST
......
# SPDX-License-Identifier: GPL-2.0
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-setup.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 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/mfd/lochnagar.h>
#include <linux/mfd/lochnagar1_regs.h>
#include <linux/mfd/lochnagar2_regs.h>
......@@ -40,48 +39,46 @@ struct lochnagar_clk {
struct lochnagar_clk_priv {
struct device *dev;
struct regmap *regmap;
enum lochnagar_type type;
const char **parents;
unsigned int nparents;
struct lochnagar_clk lclks[LOCHNAGAR_NUM_CLOCKS];
};
static const char * const lochnagar1_clk_parents[] = {
"ln-none",
"ln-spdif-mclk",
"ln-psia1-mclk",
"ln-psia2-mclk",
"ln-cdc-clkout",
"ln-dsp-clkout",
"ln-pmic-32k",
"ln-gf-mclk1",
"ln-gf-mclk3",
"ln-gf-mclk2",
"ln-gf-mclk4",
#define LN_PARENT(NAME) { .name = NAME, .fw_name = NAME }
static const struct clk_parent_data lochnagar1_clk_parents[] = {
LN_PARENT("ln-none"),
LN_PARENT("ln-spdif-mclk"),
LN_PARENT("ln-psia1-mclk"),
LN_PARENT("ln-psia2-mclk"),
LN_PARENT("ln-cdc-clkout"),
LN_PARENT("ln-dsp-clkout"),
LN_PARENT("ln-pmic-32k"),
LN_PARENT("ln-gf-mclk1"),
LN_PARENT("ln-gf-mclk3"),
LN_PARENT("ln-gf-mclk2"),
LN_PARENT("ln-gf-mclk4"),
};
static const char * const lochnagar2_clk_parents[] = {
"ln-none",
"ln-cdc-clkout",
"ln-dsp-clkout",
"ln-pmic-32k",
"ln-spdif-mclk",
"ln-clk-12m",
"ln-clk-11m",
"ln-clk-24m",
"ln-clk-22m",
"ln-clk-8m",
"ln-usb-clk-24m",
"ln-gf-mclk1",
"ln-gf-mclk3",
"ln-gf-mclk2",
"ln-psia1-mclk",
"ln-psia2-mclk",
"ln-spdif-clkout",
"ln-adat-mclk",
"ln-usb-clk-12m",
static const struct clk_parent_data lochnagar2_clk_parents[] = {
LN_PARENT("ln-none"),
LN_PARENT("ln-cdc-clkout"),
LN_PARENT("ln-dsp-clkout"),
LN_PARENT("ln-pmic-32k"),
LN_PARENT("ln-spdif-mclk"),
LN_PARENT("ln-clk-12m"),
LN_PARENT("ln-clk-11m"),
LN_PARENT("ln-clk-24m"),
LN_PARENT("ln-clk-22m"),
LN_PARENT("ln-clk-8m"),
LN_PARENT("ln-usb-clk-24m"),
LN_PARENT("ln-gf-mclk1"),
LN_PARENT("ln-gf-mclk3"),
LN_PARENT("ln-gf-mclk2"),
LN_PARENT("ln-psia1-mclk"),
LN_PARENT("ln-psia2-mclk"),
LN_PARENT("ln-spdif-clkout"),
LN_PARENT("ln-adat-mclk"),
LN_PARENT("ln-usb-clk-12m"),
};
#define LN1_CLK(ID, NAME, REG) \
......@@ -122,6 +119,24 @@ static const struct lochnagar_clk lochnagar2_clks[LOCHNAGAR_NUM_CLOCKS] = {
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)
{
return container_of(hw, struct lochnagar_clk, hw);
......@@ -183,7 +198,7 @@ static u8 lochnagar_clk_get_parent(struct clk_hw *hw)
if (ret < 0) {
dev_dbg(priv->dev, "Failed to read parent of %s: %d\n",
lclk->name, ret);
return priv->nparents;
return hw->init->num_parents;
}
val &= lclk->src_mask;
......@@ -198,46 +213,6 @@ static const struct clk_ops lochnagar_clk_ops = {
.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 *
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;
}
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 = {
.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_config *conf;
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++) {
lclk = &priv->lclks[i];
......@@ -273,55 +274,21 @@ static int lochnagar_init_clks(struct lochnagar_clk_priv *priv)
lclk->priv = priv;
lclk->hw.init = &clk_init;
ret = devm_clk_hw_register(priv->dev, &lclk->hw);
ret = devm_clk_hw_register(dev, &lclk->hw);
if (ret) {
dev_err(priv->dev, "Failed to register %s: %d\n",
dev_err(dev, "Failed to register %s: %d\n",
lclk->name, ret);
return ret;
}
}
ret = devm_of_clk_add_hw_provider(priv->dev, lochnagar_of_clk_hw_get,
priv);
ret = devm_of_clk_add_hw_provider(dev, lochnagar_of_clk_hw_get, priv);
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;
}
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 = {
.driver = {
.name = "lochnagar-clk",
......
This diff is collapsed.
......@@ -7,6 +7,7 @@
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
......@@ -50,6 +51,11 @@
/* Lowest frequency synthesizeable using only the HS divider */
#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 {
si544a,
si544b,
......@@ -71,12 +77,14 @@ struct clk_si544 {
* @hs_div: 1st divider, 5..2046, must be even when >33
* @ls_div_bits: 2nd divider, as 2^x, range 0..5
* 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 {
u32 fb_div_frac;
u16 fb_div_int;
u16 hs_div;
u8 ls_div_bits;
s32 delta_m;
};
/* Enables or disables the output driver */
......@@ -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_frac = reg[0] | reg[1] << 8 | reg[2] << 16 |
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;
}
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,
struct clk_si544_muldiv *settings)
{
......@@ -238,11 +267,15 @@ static int si544_calc_muldiv(struct clk_si544_muldiv *settings,
do_div(vco, FXO);
settings->fb_div_frac = vco;
/* Reset the frequency adjustment */
settings->delta_m = 0;
return 0;
}
/* 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);
u64 vco;
......@@ -261,6 +294,25 @@ static unsigned long si544_calc_rate(struct clk_si544_muldiv *settings)
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,
unsigned long parent_rate)
{
......@@ -279,33 +331,60 @@ static long si544_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct clk_si544 *data = to_clk_si544(hw);
struct clk_si544_muldiv settings;
int err;
if (!is_valid_frequency(data, rate))
return -EINVAL;
err = si544_calc_muldiv(&settings, rate);
if (err)
return err;
/* The accuracy is less than 1 Hz, so any rate is possible */
return rate;
}
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,
unsigned long parent_rate)
{
struct clk_si544 *data = to_clk_si544(hw);
struct clk_si544_muldiv settings;
unsigned long center;
long max_delta;
long delta;
unsigned int old_oe_state;
int err;
if (!is_valid_frequency(data, rate))
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);
if (err)
return err;
......@@ -321,6 +400,9 @@ static int si544_set_rate(struct clk_hw *hw, unsigned long rate,
if (err < 0)
return err;
err = si544_set_delta_m(data, settings.delta_m);
if (err < 0)
return err;
err = si544_set_muldiv(data, &settings);
if (err < 0)
......
......@@ -55,29 +55,27 @@ static unsigned long rockchip_mmc_recalc(struct clk_hw *hw,
static int rockchip_mmc_get_phase(struct clk_hw *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;
u16 degrees;
u32 delay_num = 0;
/* See the comment for rockchip_mmc_set_phase below */
if (!rate) {
pr_err("%s: invalid clk rate\n", __func__);
if (!rate)
return -EINVAL;
}
raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift);
degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
/* degrees/delaynum * 10000 */
/* degrees/delaynum * 1000000 */
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 >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
degrees += DIV_ROUND_CLOSEST(delay_num * factor, 10000);
degrees += DIV_ROUND_CLOSEST(delay_num * factor, 1000000);
}
return degrees % 360;
......@@ -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)
{
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 delay_num;
u32 raw_value;
......
......@@ -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_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_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),
......@@ -966,7 +969,6 @@ static void __init px30_clk_init(struct device_node *np)
{
struct rockchip_clk_provider *ctx;
void __iomem *reg_base;
struct clk *clk;
reg_base = of_iomap(np, 0);
if (!reg_base) {
......@@ -981,14 +983,6 @@ static void __init px30_clk_init(struct device_node *np)
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,
ARRAY_SIZE(px30_pll_clks),
PX30_GRF_SOC_STATUS0);
......
......@@ -110,6 +110,7 @@ static struct rockchip_cpuclk_rate_table rk3228_cpuclk_rates[] __initdata = {
RK3228_CPUCLK_RATE(1608000000, 1, 7),
RK3228_CPUCLK_RATE(1512000000, 1, 7),
RK3228_CPUCLK_RATE(1488000000, 1, 5),
RK3228_CPUCLK_RATE(1464000000, 1, 5),
RK3228_CPUCLK_RATE(1416000000, 1, 5),
RK3228_CPUCLK_RATE(1392000000, 1, 5),
RK3228_CPUCLK_RATE(1296000000, 1, 5),
......@@ -255,7 +256,7 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = {
RK2928_CLKGATE_CON(4), 0, GFLAGS),
/* 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),
MUX(0, "usb480m_phy", mux_usb480m_phy_p, CLK_SET_RATE_PARENT,
RK2928_MISC_CON, 14, 1, MFLAGS),
......
......@@ -122,7 +122,6 @@ static struct rockchip_pll_rate_table rk3288_pll_rates[] = {
RK3066_PLL_RATE( 160000000, 1, 80, 12),
RK3066_PLL_RATE( 157500000, 1, 105, 16),
RK3066_PLL_RATE( 126000000, 1, 84, 16),
RK3066_PLL_RATE( 48000000, 1, 64, 32),
{ /* sentinel */ },
};
......@@ -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(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 */
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),
......@@ -924,7 +926,6 @@ static struct syscore_ops rk3288_clk_syscore_ops = {
static void __init rk3288_clk_init(struct device_node *np)
{
struct rockchip_clk_provider *ctx;
struct clk *clk;
rk3288_cru_base = of_iomap(np, 0);
if (!rk3288_cru_base) {
......@@ -939,14 +940,6 @@ static void __init rk3288_clk_init(struct device_node *np)
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,
ARRAY_SIZE(rk3288_pll_clks),
RK3288_GRF_SOC_STATUS1);
......
......@@ -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(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_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),
......
......@@ -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_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 comes from the exactly same source as hclk_vio
......@@ -871,7 +874,6 @@ static void __init rk3368_clk_init(struct device_node *np)
{
struct rockchip_clk_provider *ctx;
void __iomem *reg_base;
struct clk *clk;
reg_base = of_iomap(np, 0);
if (!reg_base) {
......@@ -886,14 +888,6 @@ static void __init rk3368_clk_init(struct device_node *np)
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,
ARRAY_SIZE(rk3368_pll_clks),
RK3368_GRF_SOC_STATUS0);
......
......@@ -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_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_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)
{
struct rockchip_clk_provider *ctx;
void __iomem *reg_base;
struct clk *clk;
reg_base = of_iomap(np, 0);
if (!reg_base) {
......@@ -1546,14 +1548,6 @@ static void __init rk3399_clk_init(struct device_node *np)
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,
ARRAY_SIZE(rk3399_pll_clks), -1);
......
......@@ -820,6 +820,10 @@ struct rockchip_clk_branch {
.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,
void __iomem *base, unsigned long nr_clks);
void rockchip_clk_of_add_provider(struct device_node *np,
......
......@@ -73,6 +73,7 @@
#define SCLK_WIFI 141
#define SCLK_OTGPHY0 142
#define SCLK_OTGPHY1 143
#define SCLK_HDMI_PHY 144
/* dclk gates */
#define DCLK_VOP 190
......
......@@ -173,6 +173,7 @@
#define PCLK_DCF 233
#define PCLK_SARADC 234
#define PCLK_ACODECPHY 235
#define PCLK_WDT 236
/* hclk gates */
#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