mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 23:09:29 +02:00
Merge tag 'clk-misc-for-v7.3' of ssh://github.com/masneyb/linux into clk-pile
Pull clk patches picked up by Brian Masney: Here's various improvements and fixes for the clk subsystem that was posted prior to the opening of the last merge window. - Add spread spectrum clock (SSC) support to the clk framework, including a new assigned-clock-sscs devicetree property and clk_hw_set_spread_spectrum() API with KUnit tests (Peng Fan) - Add SCMI clock OEM extensions for the i.MX95 clock driver and introduce a common SCMI clock header (Peng Fan) - Add clock, reset, and devicetree bindings for the ESWIN EIC7700 HSP clock and reset generator (Xuyang Dong) - Add clk_determine_rate_noop() helper for clock drivers that can do any rate, and convert existing open-coded implementations across hisilicon, imx, qcom, renesas, rp1, samsung, scpi, sprd, mediatek phy, and mediatek pmdomain drivers (Brian Masney) - Add kernel-doc documentation for struct clk_core and the core clock flags, and wire up clk identifiers into the documentation build (Brian Masney) - Fix clk_divider_bestdiv() returning the minimum rate instead of the maximum rate for large rate requests, with KUnit tests (Lad Prabhakar) - Fix Nuvoton MA35D1 PLL frequency calculation including ignored div_u64 return values, incorrect PLL_CTL1_FRAC bit field width, and broken determine_rate logic (Joey Lu) - Support unique clock names for multi-socket Tegra platforms (Jon Hunter) - Allow COMPILE_TEST builds for HiSilicon clock drivers (Rosen Penev) - Various fixes and cleanups from Akari Tsuyukusa, Alexander A. Klimov, Brian Masney, David Carlier, David Laight, Min zhang, Myeonghun Pak, Pavel Löbl, Randy Dunlap, Rob Herring, Rosen Penev, Uwe Kleine-König, William Theesfeld, Xuyang Dong, and Yu-Chun Lin Signed-off-by: Brian Masney <bmasney@redhat.com> [sboyd@kernel.org: Fix allmodconfig modpost failure in scmi] * tag 'clk-misc-for-v7.3' of ssh://github.com/masneyb/linux: (53 commits) pmdomain: mediatek: mtk-mfg: use clk_determine_rate_noop() pmdomain: mediatek: airoha: use clk_determine_rate_noop() phy: mediatek: phy-mtk-hdmi-mt2701: use clk_determine_rate_noop() clk: sprd: use clk_determine_rate_noop() clk: scpi: use clk_determine_rate_noop() clk: samsung: acpm: use clk_determine_rate_noop() clk: rp1: use clk_determine_rate_noop() clk: renesas: rzg2l-cpg: use clk_determine_rate_noop() clk: qcom: smd-rpm: use clk_determine_rate_noop() clk: qcom: rpmh: use clk_determine_rate_noop() clk: qcom: rpm: use clk_determine_rate_noop() clk: imx: scu: use clk_determine_rate_noop() clk: hisilicon: hi3660-stub: use clk_determine_rate_noop() clk: add clk_determine_rate_noop() clk: imx: scu: drop redundant init.ops variable assignment clk: test: convert constants to use HZ_PER_MHZ docs: clk: include some identifiers to keep documentation up to date clk: add kernel docs for struct clk_core clk: add kernel docs for the core flags clk: hisilicon: allow COMPILE_TEST builds ...
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/eswin,eic7700-hspcrg.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ESWIN EIC7700 HSP Clock and Reset Generator
|
||||
|
||||
maintainers:
|
||||
- Xuyang Dong <dongxuyang@eswincomputing.com>
|
||||
|
||||
description:
|
||||
Clock and reset generator for the ESWIN EIC7700 HSP (high-speed peripherals).
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: eswin,eic7700-hspcrg
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: HSP configuration top clock
|
||||
- description: MMC top clock
|
||||
- description: SATA top clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: cfg
|
||||
- const: mmc
|
||||
- const: sata
|
||||
|
||||
'#clock-cells':
|
||||
const: 1
|
||||
description:
|
||||
See <dt-bindings/clock/eswin,eic7700-hspcrg.h> for valid indices.
|
||||
|
||||
'#reset-cells':
|
||||
const: 1
|
||||
description:
|
||||
See <dt-bindings/reset/eswin,eic7700-hspcrg.h> for valid indices.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- '#clock-cells'
|
||||
- '#reset-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
clock-controller@50440000 {
|
||||
compatible = "eswin,eic7700-hspcrg";
|
||||
reg = <0x50440000 0x2000>;
|
||||
clocks = <&clock 171>, <&clock 254>, <&clock 187>;
|
||||
clock-names = "cfg", "mmc", "sata";
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
@@ -42,21 +42,8 @@ clock interface.
|
||||
Common data structures and api
|
||||
==============================
|
||||
|
||||
Below is the common struct clk_core definition from
|
||||
drivers/clk/clk.c, modified for brevity::
|
||||
|
||||
struct clk_core {
|
||||
const char *name;
|
||||
const struct clk_ops *ops;
|
||||
struct clk_hw *hw;
|
||||
struct module *owner;
|
||||
struct clk_core *parent;
|
||||
const char **parent_names;
|
||||
struct clk_core **parents;
|
||||
u8 num_parents;
|
||||
u8 new_parent_index;
|
||||
...
|
||||
};
|
||||
.. kernel-doc:: drivers/clk/clk.c
|
||||
:identifiers: struct clk_core
|
||||
|
||||
The members above make up the core of the clk tree topology. The clk
|
||||
api itself defines several driver-facing functions which operate on
|
||||
@@ -64,38 +51,17 @@ struct clk. That api is documented in include/linux/clk.h.
|
||||
|
||||
Platforms and devices utilizing the common struct clk_core use the struct
|
||||
clk_ops pointer in struct clk_core to perform the hardware-specific parts of
|
||||
the operations defined in clk-provider.h::
|
||||
the operations defined in clk-provider.h, and can set one or more
|
||||
framework-level flags documented below.
|
||||
|
||||
struct clk_ops {
|
||||
int (*prepare)(struct clk_hw *hw);
|
||||
void (*unprepare)(struct clk_hw *hw);
|
||||
int (*is_prepared)(struct clk_hw *hw);
|
||||
void (*unprepare_unused)(struct clk_hw *hw);
|
||||
int (*enable)(struct clk_hw *hw);
|
||||
void (*disable)(struct clk_hw *hw);
|
||||
int (*is_enabled)(struct clk_hw *hw);
|
||||
void (*disable_unused)(struct clk_hw *hw);
|
||||
unsigned long (*recalc_rate)(struct clk_hw *hw,
|
||||
unsigned long parent_rate);
|
||||
int (*determine_rate)(struct clk_hw *hw,
|
||||
struct clk_rate_request *req);
|
||||
int (*set_parent)(struct clk_hw *hw, u8 index);
|
||||
u8 (*get_parent)(struct clk_hw *hw);
|
||||
int (*set_rate)(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate);
|
||||
int (*set_rate_and_parent)(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate,
|
||||
u8 index);
|
||||
unsigned long (*recalc_accuracy)(struct clk_hw *hw,
|
||||
unsigned long parent_accuracy);
|
||||
int (*get_phase)(struct clk_hw *hw);
|
||||
int (*set_phase)(struct clk_hw *hw, int degrees);
|
||||
void (*init)(struct clk_hw *hw);
|
||||
void (*debug_init)(struct clk_hw *hw,
|
||||
struct dentry *dentry);
|
||||
};
|
||||
.. kernel-doc:: include/linux/clk-provider.h
|
||||
:identifiers: struct clk_ops
|
||||
|
||||
Core flags
|
||||
==========
|
||||
|
||||
.. kernel-doc:: include/linux/clk-provider.h
|
||||
:doc: clk framework flags
|
||||
|
||||
Hardware clk implementations
|
||||
============================
|
||||
|
||||
+3
-2
@@ -9660,9 +9660,10 @@ ESWIN EIC7700 CLOCK DRIVER
|
||||
M: Yifeng Huang <huangyifeng@eswincomputing.com>
|
||||
M: Xuyang Dong <dongxuyang@eswincomputing.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/clock/eswin,eic7700-clock.yaml
|
||||
F: Documentation/devicetree/bindings/clock/eswin,eic7700*
|
||||
F: drivers/clk/eswin/
|
||||
F: include/dt-bindings/clock/eswin,eic7700-clock.h
|
||||
F: include/dt-bindings/clock/eswin,eic7700*
|
||||
F: include/dt-bindings/reset/eswin,eic7700-hspcrg.h
|
||||
|
||||
ET131X NETWORK DRIVER
|
||||
M: Mark Einon <mark.einon@gmail.com>
|
||||
|
||||
@@ -4,6 +4,7 @@ CONFIG_OF=y
|
||||
CONFIG_OF_OVERLAY=y
|
||||
CONFIG_COMMON_CLK=y
|
||||
CONFIG_CLK_KUNIT_TEST=y
|
||||
CONFIG_CLK_DIVIDER_KUNIT_TEST=y
|
||||
CONFIG_CLK_FIXED_RATE_KUNIT_TEST=y
|
||||
CONFIG_CLK_GATE_KUNIT_TEST=y
|
||||
CONFIG_CLK_FD_KUNIT_TEST=y
|
||||
|
||||
@@ -579,4 +579,12 @@ config CLK_FD_KUNIT_TEST
|
||||
help
|
||||
Kunit test for the clk-fractional-divider type.
|
||||
|
||||
config CLK_DIVIDER_KUNIT_TEST
|
||||
tristate "KUnit tests for clk divider bestdiv" if !KUNIT_ALL_TESTS
|
||||
depends on KUNIT
|
||||
depends on !S390
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
Kunit test for the clk-divider type.
|
||||
|
||||
endif
|
||||
|
||||
+14
-2
@@ -18,9 +18,20 @@ clk-test-y := clk_test.o \
|
||||
kunit_clk_assigned_rates_without_consumer.dtbo.o \
|
||||
kunit_clk_assigned_rates_zero.dtbo.o \
|
||||
kunit_clk_assigned_rates_zero_consumer.dtbo.o \
|
||||
kunit_clk_assigned_sscs_one.dtbo.o \
|
||||
kunit_clk_assigned_sscs_one_consumer.dtbo.o \
|
||||
kunit_clk_assigned_sscs_multiple.dtbo.o \
|
||||
kunit_clk_assigned_sscs_multiple_consumer.dtbo.o \
|
||||
kunit_clk_assigned_sscs_null.dtbo.o \
|
||||
kunit_clk_assigned_sscs_null_consumer.dtbo.o \
|
||||
kunit_clk_assigned_sscs_without.dtbo.o \
|
||||
kunit_clk_assigned_sscs_without_consumer.dtbo.o \
|
||||
kunit_clk_assigned_sscs_zero.dtbo.o \
|
||||
kunit_clk_assigned_sscs_zero_consumer.dtbo.o \
|
||||
kunit_clk_hw_get_dev_of_node.dtbo.o \
|
||||
kunit_clk_parent_data_test.dtbo.o
|
||||
obj-$(CONFIG_COMMON_CLK) += clk-divider.o
|
||||
obj-$(CONFIG_CLK_DIVIDER_KUNIT_TEST) += clk-divider_test.o
|
||||
obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o
|
||||
obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o
|
||||
obj-$(CONFIG_CLK_FIXED_RATE_KUNIT_TEST) += clk-fixed-rate-test.o
|
||||
@@ -88,7 +99,8 @@ obj-$(CONFIG_COMMON_CLK_RP1) += clk-rp1.o
|
||||
obj-$(CONFIG_COMMON_CLK_RPMI) += clk-rpmi.o
|
||||
obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o
|
||||
obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
|
||||
obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o
|
||||
scmi-clk-y := clk-scmi.o clk-scmi-oem.o
|
||||
obj-$(CONFIG_COMMON_CLK_SCMI) += scmi-clk.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
|
||||
@@ -121,7 +133,7 @@ obj-y += bcm/
|
||||
obj-$(CONFIG_ARCH_BERLIN) += berlin/
|
||||
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
|
||||
obj-$(CONFIG_COMMON_CLK_ESWIN) += eswin/
|
||||
obj-$(CONFIG_ARCH_HISI) += hisilicon/
|
||||
obj-$(CONFIG_COMMON_CLK_HISI) += hisilicon/
|
||||
obj-y += imgtec/
|
||||
obj-y += imx/
|
||||
obj-y += ingenic/
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
@@ -217,7 +218,7 @@ CLK_OF_DECLARE(of_sama5d4_clk_h32mx_setup, "atmel,sama5d4-clk-h32mx",
|
||||
static void __init of_sama5d2_clk_i2s_mux_setup(struct device_node *np)
|
||||
{
|
||||
struct regmap *regmap_sfr;
|
||||
u8 bus_id;
|
||||
u64 bus_id;
|
||||
const char *parent_names[2];
|
||||
struct device_node *i2s_mux_np;
|
||||
struct clk_hw *hw;
|
||||
@@ -228,7 +229,7 @@ static void __init of_sama5d2_clk_i2s_mux_setup(struct device_node *np)
|
||||
return;
|
||||
|
||||
for_each_child_of_node(np, i2s_mux_np) {
|
||||
if (of_property_read_u8(i2s_mux_np, "reg", &bus_id))
|
||||
if (of_property_read_reg(i2s_mux_np, 0, &bus_id, NULL))
|
||||
continue;
|
||||
|
||||
if (bus_id > I2S_BUS_NR)
|
||||
|
||||
@@ -1098,8 +1098,7 @@ static u8 kona_peri_clk_get_parent(struct clk_hw *hw)
|
||||
|
||||
index = selector_read_index(bcm_clk->ccu, &data->sel);
|
||||
|
||||
/* Not all callers would handle an out-of-range value gracefully */
|
||||
return index == BAD_CLK_INDEX ? 0 : index;
|
||||
return index;
|
||||
}
|
||||
|
||||
static int kona_peri_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
|
||||
@@ -147,12 +147,12 @@ static int bd71837_clk_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct platform_device_id bd718x7_clk_id[] = {
|
||||
{ "bd71837-clk", ROHM_CHIP_TYPE_BD71837 },
|
||||
{ "bd71847-clk", ROHM_CHIP_TYPE_BD71847 },
|
||||
{ "bd71828-clk", ROHM_CHIP_TYPE_BD71828 },
|
||||
{ "bd71815-clk", ROHM_CHIP_TYPE_BD71815 },
|
||||
{ "bd72720-clk", ROHM_CHIP_TYPE_BD72720 },
|
||||
{ },
|
||||
{ .name = "bd71837-clk", .driver_data = ROHM_CHIP_TYPE_BD71837 },
|
||||
{ .name = "bd71847-clk", .driver_data = ROHM_CHIP_TYPE_BD71847 },
|
||||
{ .name = "bd71828-clk", .driver_data = ROHM_CHIP_TYPE_BD71828 },
|
||||
{ .name = "bd71815-clk", .driver_data = ROHM_CHIP_TYPE_BD71815 },
|
||||
{ .name = "bd72720-clk", .driver_data = ROHM_CHIP_TYPE_BD72720 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, bd718x7_clk_id);
|
||||
|
||||
|
||||
@@ -679,7 +679,7 @@ MODULE_DEVICE_TABLE(of, cdce706_dt_match);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id cdce706_id[] = {
|
||||
{ "cdce706" },
|
||||
{ .name = "cdce706" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cdce706_id);
|
||||
|
||||
@@ -826,10 +826,10 @@ static const struct clk_cdce925_chip_info clk_cdce949_info = {
|
||||
};
|
||||
|
||||
static const struct i2c_device_id cdce925_id[] = {
|
||||
{ "cdce913", (kernel_ulong_t)&clk_cdce913_info },
|
||||
{ "cdce925", (kernel_ulong_t)&clk_cdce925_info },
|
||||
{ "cdce937", (kernel_ulong_t)&clk_cdce937_info },
|
||||
{ "cdce949", (kernel_ulong_t)&clk_cdce949_info },
|
||||
{ .name = "cdce913", .driver_data = (kernel_ulong_t)&clk_cdce913_info },
|
||||
{ .name = "cdce925", .driver_data = (kernel_ulong_t)&clk_cdce925_info },
|
||||
{ .name = "cdce937", .driver_data = (kernel_ulong_t)&clk_cdce937_info },
|
||||
{ .name = "cdce949", .driver_data = (kernel_ulong_t)&clk_cdce949_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cdce925_id);
|
||||
|
||||
@@ -155,6 +155,78 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __set_clk_spread_spectrum(struct device_node *node, bool clk_supplier)
|
||||
{
|
||||
u32 elem_size = sizeof(struct clk_spread_spectrum);
|
||||
struct clk_spread_spectrum *sscs;
|
||||
struct of_phandle_args clkspec;
|
||||
int rc, count, index;
|
||||
struct clk *clk;
|
||||
|
||||
/* modfreq, spreadPercent, modmethod */
|
||||
count = of_property_count_elems_of_size(node, "assigned-clock-sscs", elem_size);
|
||||
if (count <= 0)
|
||||
return 0;
|
||||
|
||||
sscs = kcalloc(count, elem_size, GFP_KERNEL);
|
||||
if (!sscs)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = of_property_read_u32_array(node, "assigned-clock-sscs", (u32 *)sscs,
|
||||
count * 3);
|
||||
if (rc)
|
||||
goto free_sscs;
|
||||
|
||||
for (index = 0; index < count; index++) {
|
||||
struct clk_spread_spectrum *conf = &sscs[index];
|
||||
struct clk_hw *hw;
|
||||
|
||||
if (!conf->modfreq_hz && !conf->spread_bp && !conf->method)
|
||||
continue;
|
||||
|
||||
rc = of_parse_phandle_with_args(node, "assigned-clocks", "#clock-cells",
|
||||
index, &clkspec);
|
||||
if (rc < 0) {
|
||||
/* skip empty (null) phandles */
|
||||
if (rc == -ENOENT) {
|
||||
rc = 0;
|
||||
continue;
|
||||
} else
|
||||
goto free_sscs;
|
||||
}
|
||||
|
||||
if (clkspec.np == node && !clk_supplier) {
|
||||
of_node_put(clkspec.np);
|
||||
goto free_sscs;
|
||||
}
|
||||
|
||||
clk = of_clk_get_from_provider(&clkspec);
|
||||
of_node_put(clkspec.np);
|
||||
if (IS_ERR(clk)) {
|
||||
if (PTR_ERR(clk) != -EPROBE_DEFER)
|
||||
pr_warn("clk: couldn't get clock %d for %pOF\n",
|
||||
index, node);
|
||||
rc = PTR_ERR(clk);
|
||||
goto free_sscs;
|
||||
}
|
||||
|
||||
hw = __clk_get_hw(clk);
|
||||
rc = clk_hw_set_spread_spectrum(hw, conf);
|
||||
if (rc < 0) {
|
||||
pr_err("clk: couldn't set %s clk spread spectrum %u %u %u: %d\n",
|
||||
__clk_get_name(clk), conf->modfreq_hz, conf->spread_bp,
|
||||
conf->method, rc);
|
||||
/* Do not fail */
|
||||
rc = 0;
|
||||
}
|
||||
clk_put(clk);
|
||||
}
|
||||
|
||||
free_sscs:
|
||||
kfree(sscs);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_clk_set_defaults() - parse and set assigned clocks configuration
|
||||
* @node: device node to apply clock settings for
|
||||
@@ -174,6 +246,10 @@ int of_clk_set_defaults(struct device_node *node, bool clk_supplier)
|
||||
if (!node)
|
||||
return 0;
|
||||
|
||||
rc = __set_clk_spread_spectrum(node, clk_supplier);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = __set_clk_parents(node, clk_supplier);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
@@ -122,8 +122,8 @@ static const struct of_device_id cs2000_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, cs2000_of_match);
|
||||
|
||||
static const struct i2c_device_id cs2000_id[] = {
|
||||
{ "cs2000-cp", },
|
||||
{}
|
||||
{ .name = "cs2000-cp" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cs2000_id);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/overflow.h>
|
||||
|
||||
/*
|
||||
* DOC: basic adjustable divider clock that cannot gate
|
||||
@@ -301,6 +302,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, struct clk_hw *parent,
|
||||
int i, bestdiv = 0;
|
||||
unsigned long parent_rate, best = 0, now, maxdiv;
|
||||
unsigned long parent_rate_saved = *best_parent_rate;
|
||||
unsigned long target_parent_rate;
|
||||
|
||||
if (!rate)
|
||||
rate = 1;
|
||||
@@ -315,15 +317,11 @@ static int clk_divider_bestdiv(struct clk_hw *hw, struct clk_hw *parent,
|
||||
return bestdiv;
|
||||
}
|
||||
|
||||
/*
|
||||
* The maximum divider we can use without overflowing
|
||||
* unsigned long in rate * i below
|
||||
*/
|
||||
maxdiv = min(ULONG_MAX / rate, maxdiv);
|
||||
|
||||
for (i = _next_div(table, 0, flags); i <= maxdiv;
|
||||
i = _next_div(table, i, flags)) {
|
||||
if (rate * i == parent_rate_saved) {
|
||||
bool overflow = check_mul_overflow(rate, (unsigned long)i, &target_parent_rate);
|
||||
|
||||
if (!overflow && target_parent_rate == parent_rate_saved) {
|
||||
/*
|
||||
* It's the most ideal case if the requested rate can be
|
||||
* divided from parent clock without needing to change
|
||||
@@ -332,13 +330,24 @@ static int clk_divider_bestdiv(struct clk_hw *hw, struct clk_hw *parent,
|
||||
*best_parent_rate = parent_rate_saved;
|
||||
return i;
|
||||
}
|
||||
parent_rate = clk_hw_round_rate(parent, rate * i);
|
||||
/*
|
||||
* Clamp target_parent_rate to ULONG_MAX on overflow. The true
|
||||
* required parent rate exceeds what can be represented, so ask
|
||||
* the parent for the highest rate it can produce. There is no
|
||||
* point continuing the loop past this since larger dividers
|
||||
* only move further from the requested rate.
|
||||
*/
|
||||
if (overflow)
|
||||
target_parent_rate = ULONG_MAX;
|
||||
parent_rate = clk_hw_round_rate(parent, target_parent_rate);
|
||||
now = DIV_ROUND_UP_ULL((u64)parent_rate, i);
|
||||
if (_is_best_div(rate, now, best, flags)) {
|
||||
bestdiv = i;
|
||||
best = now;
|
||||
*best_parent_rate = parent_rate;
|
||||
}
|
||||
if (overflow)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bestdiv) {
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* KUnit tests for clk_divider_bestdiv()
|
||||
*/
|
||||
#include <kunit/test.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#define PARENT_RATE_1GHZ GIGA
|
||||
#define PARENT_RATE_2GHZ (2 * GIGA)
|
||||
#define PARENT_RATE_4GHZ (4 * GIGA)
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_fixed_rate_wrapper,
|
||||
clk_hw_unregister_fixed_rate, struct clk_hw *);
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_divider_wrapper,
|
||||
clk_hw_unregister_divider, struct clk_hw *);
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_mux_wrapper,
|
||||
clk_hw_unregister_mux, struct clk_hw *);
|
||||
|
||||
static const struct clk_div_table bestdiv_table[] = {
|
||||
{ .val = 0, .div = 2 },
|
||||
{ .val = 1, .div = 4 },
|
||||
{ .val = 2, .div = 8 },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
/*
|
||||
* Test that clk_round_rate(clk, ULONG_MAX) returns the maximum achievable
|
||||
* rate for a divider clock.
|
||||
*/
|
||||
static void clk_divider_bestdiv_ulong_max_returns_max_rate(struct kunit *test)
|
||||
{
|
||||
struct clk_hw *parent_hw, *div_hw;
|
||||
unsigned long rate;
|
||||
u32 *fake_reg;
|
||||
|
||||
fake_reg = kunit_kzalloc(test, sizeof(*fake_reg), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg);
|
||||
|
||||
parent_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-parent",
|
||||
NULL, 0, PARENT_RATE_1GHZ);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_hw);
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper,
|
||||
parent_hw));
|
||||
|
||||
div_hw = clk_hw_register_divider_table(NULL, "bestdiv-div",
|
||||
"bestdiv-parent",
|
||||
CLK_SET_RATE_PARENT,
|
||||
(void __iomem __force *)fake_reg,
|
||||
0, 2, 0, bestdiv_table, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, div_hw);
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
kunit_add_action_or_reset(test, clk_hw_unregister_divider_wrapper,
|
||||
div_hw));
|
||||
|
||||
/*
|
||||
* ULONG_MAX is the canonical way to probe the maximum rate a clock
|
||||
* can produce.
|
||||
*/
|
||||
rate = clk_hw_round_rate(div_hw, ULONG_MAX);
|
||||
KUNIT_EXPECT_EQ(test, rate, PARENT_RATE_1GHZ / 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that clk_round_rate(clk, ULONG_MAX) returns the correct maximum rate
|
||||
* when a mux clock sits between a divider and its parent candidates.
|
||||
*
|
||||
* Topology:
|
||||
*
|
||||
* [fixed 4 GHz] --\
|
||||
* +--> [mux CLK_SET_RATE_PARENT] --> [div {2,4,8} CLK_SET_RATE_PARENT]
|
||||
* [fixed 2 GHz] --/
|
||||
*
|
||||
*/
|
||||
static void clk_divider_bestdiv_mux_ulong_max_returns_max_rate(struct kunit *test)
|
||||
{
|
||||
static const char * const mux_parents[] = {
|
||||
"bestdiv-mux-parent-a",
|
||||
"bestdiv-mux-parent-b",
|
||||
};
|
||||
struct clk_hw *parent_a_hw, *parent_b_hw, *mux_hw, *div_hw;
|
||||
u32 *fake_reg_mux, *fake_reg_div;
|
||||
unsigned long rate;
|
||||
|
||||
fake_reg_mux = kunit_kzalloc(test, sizeof(*fake_reg_mux), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg_mux);
|
||||
|
||||
fake_reg_div = kunit_kzalloc(test, sizeof(*fake_reg_div), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg_div);
|
||||
|
||||
/* Higher-rate parent: the mux should select this for ULONG_MAX. */
|
||||
parent_a_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-mux-parent-a",
|
||||
NULL, 0, PARENT_RATE_4GHZ);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_a_hw);
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper,
|
||||
parent_a_hw));
|
||||
|
||||
/* Lower-rate parent: should not be selected. */
|
||||
parent_b_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-mux-parent-b",
|
||||
NULL, 0, PARENT_RATE_2GHZ);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_b_hw);
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper,
|
||||
parent_b_hw));
|
||||
|
||||
/*
|
||||
* 1-bit mux register selects between the two parents.
|
||||
* CLK_SET_RATE_PARENT allows the divider's rate request to
|
||||
* propagate into clk_mux_determine_rate().
|
||||
*/
|
||||
mux_hw = clk_hw_register_mux(NULL, "bestdiv-mux",
|
||||
mux_parents, ARRAY_SIZE(mux_parents),
|
||||
CLK_SET_RATE_PARENT,
|
||||
(void __iomem __force *)fake_reg_mux,
|
||||
0, 1, 0, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mux_hw);
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
kunit_add_action_or_reset(test, clk_hw_unregister_mux_wrapper,
|
||||
mux_hw));
|
||||
|
||||
div_hw = clk_hw_register_divider_table(NULL, "bestdiv-mux-div",
|
||||
"bestdiv-mux",
|
||||
CLK_SET_RATE_PARENT,
|
||||
(void __iomem __force *)fake_reg_div,
|
||||
0, 2, 0, bestdiv_table, NULL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, div_hw);
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
kunit_add_action_or_reset(test, clk_hw_unregister_divider_wrapper,
|
||||
div_hw));
|
||||
|
||||
rate = clk_hw_round_rate(div_hw, ULONG_MAX);
|
||||
KUNIT_EXPECT_EQ(test, rate, PARENT_RATE_4GHZ / 2);
|
||||
}
|
||||
|
||||
static struct kunit_case clk_divider_bestdiv_test_cases[] = {
|
||||
KUNIT_CASE(clk_divider_bestdiv_ulong_max_returns_max_rate),
|
||||
KUNIT_CASE(clk_divider_bestdiv_mux_ulong_max_returns_max_rate),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite clk_divider_bestdiv_test_suite = {
|
||||
.name = "clk_divider_bestdiv",
|
||||
.test_cases = clk_divider_bestdiv_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suite(clk_divider_bestdiv_test_suite);
|
||||
|
||||
MODULE_DESCRIPTION("KUnit tests for clk divider");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -2227,6 +2227,7 @@ static int k230_clk_set_rate_mul(struct clk_hw *hw, unsigned long rate,
|
||||
guard(spinlock)(rate_self->lock);
|
||||
|
||||
mul_reg = readl(rate_self->reg + clk->mul_reg_off);
|
||||
mul_reg &= ~(rate_self->mul_mask << rate_self->mul_shift);
|
||||
mul_reg |= ((mul - 1) & rate_self->mul_mask) << (rate_self->mul_shift);
|
||||
mul_reg |= BIT(rate_self->write_enable_bit);
|
||||
writel(mul_reg, rate_self->reg + clk->mul_reg_off);
|
||||
@@ -2257,6 +2258,7 @@ static int k230_clk_set_rate_div(struct clk_hw *hw, unsigned long rate,
|
||||
guard(spinlock)(rate_self->lock);
|
||||
|
||||
div_reg = readl(rate_self->reg + clk->div_reg_off);
|
||||
div_reg &= ~(rate_self->div_mask << rate_self->div_shift);
|
||||
div_reg |= ((div - 1) & rate_self->div_mask) << (rate_self->div_shift);
|
||||
div_reg |= BIT(rate_self->write_enable_bit);
|
||||
writel(div_reg, rate_self->reg + clk->div_reg_off);
|
||||
@@ -2287,11 +2289,13 @@ static int k230_clk_set_rate_mul_div(struct clk_hw *hw, unsigned long rate,
|
||||
guard(spinlock)(rate_self->lock);
|
||||
|
||||
div_reg = readl(rate_self->reg + clk->div_reg_off);
|
||||
div_reg &= ~(rate_self->div_mask << rate_self->div_shift);
|
||||
div_reg |= ((div - 1) & rate_self->div_mask) << (rate_self->div_shift);
|
||||
div_reg |= BIT(rate_self->write_enable_bit);
|
||||
writel(div_reg, rate_self->reg + clk->div_reg_off);
|
||||
|
||||
mul_reg = readl(rate_self->reg + clk->mul_reg_off);
|
||||
mul_reg &= ~(rate_self->mul_mask << rate_self->mul_shift);
|
||||
mul_reg |= ((mul - 1) & rate_self->mul_mask) << (rate_self->mul_shift);
|
||||
mul_reg |= BIT(rate_self->write_enable_bit);
|
||||
writel(mul_reg, rate_self->reg + clk->mul_reg_off);
|
||||
|
||||
+10
-17
@@ -175,10 +175,6 @@
|
||||
#define LMK04832_REG_RB_HOLDOVER 0x188
|
||||
#define LMK04832_REG_SPI_LOCK 0x555
|
||||
|
||||
enum lmk04832_device_types {
|
||||
LMK04832,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct lmk04832_device_info - Holds static device information that is
|
||||
* specific to the chip revision
|
||||
@@ -197,14 +193,12 @@ struct lmk04832_device_info {
|
||||
unsigned int vco1_range[2];
|
||||
};
|
||||
|
||||
static const struct lmk04832_device_info lmk04832_device_info[] = {
|
||||
[LMK04832] = {
|
||||
.pid = 0x63d1, /* WARNING PROD_ID is inverted in the datasheet */
|
||||
.maskrev = 0x70,
|
||||
.num_channels = 14,
|
||||
.vco0_range = { 2440, 2580 },
|
||||
.vco1_range = { 2945, 3255 },
|
||||
},
|
||||
static const struct lmk04832_device_info lmk04832_device_info = {
|
||||
.pid = 0x63d1, /* WARNING PROD_ID is inverted in the datasheet */
|
||||
.maskrev = 0x70,
|
||||
.num_channels = 14,
|
||||
.vco0_range = { 2440, 2580 },
|
||||
.vco1_range = { 2945, 3255 },
|
||||
};
|
||||
|
||||
enum lmk04832_rdbk_type {
|
||||
@@ -422,11 +416,10 @@ static unsigned long lmk04832_vco_recalc_rate(struct clk_hw *hw,
|
||||
*/
|
||||
static int lmk04832_check_vco_ranges(struct lmk04832 *lmk, unsigned long rate)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(lmk->dev);
|
||||
const struct lmk04832_device_info *info;
|
||||
unsigned long mhz = rate / 1000000;
|
||||
|
||||
info = &lmk04832_device_info[spi_get_device_id(spi)->driver_data];
|
||||
info = &lmk04832_device_info;
|
||||
|
||||
if (mhz >= info->vco0_range[0] && mhz <= info->vco0_range[1])
|
||||
return LMK04832_VAL_VCO_MUX_VCO0;
|
||||
@@ -1405,7 +1398,7 @@ static int lmk04832_probe(struct spi_device *spi)
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
info = &lmk04832_device_info[spi_get_device_id(spi)->driver_data];
|
||||
info = &lmk04832_device_info;
|
||||
|
||||
lmk = devm_kzalloc(&spi->dev, sizeof(struct lmk04832), GFP_KERNEL);
|
||||
if (!lmk)
|
||||
@@ -1553,8 +1546,8 @@ static int lmk04832_probe(struct spi_device *spi)
|
||||
}
|
||||
|
||||
static const struct spi_device_id lmk04832_id[] = {
|
||||
{ "lmk04832", LMK04832 },
|
||||
{}
|
||||
{ .name = "lmk04832" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, lmk04832_id);
|
||||
|
||||
|
||||
@@ -264,10 +264,10 @@ static int max77686_clk_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct platform_device_id max77686_clk_id[] = {
|
||||
{ "max77686-clk", .driver_data = CHIP_MAX77686, },
|
||||
{ "max77802-clk", .driver_data = CHIP_MAX77802, },
|
||||
{ "max77620-clock", .driver_data = CHIP_MAX77620, },
|
||||
{},
|
||||
{ .name = "max77686-clk", .driver_data = CHIP_MAX77686 },
|
||||
{ .name = "max77802-clk", .driver_data = CHIP_MAX77802 },
|
||||
{ .name = "max77620-clock", .driver_data = CHIP_MAX77620 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, max77686_clk_id);
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ static void __init moxart_of_pll_clk_init(struct device_node *node)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct clk_hw *hw;
|
||||
struct clk *ref_clk;
|
||||
unsigned int mul;
|
||||
const char *name = node->name;
|
||||
const char *parent_name;
|
||||
@@ -34,12 +33,6 @@ static void __init moxart_of_pll_clk_init(struct device_node *node)
|
||||
mul = readl(base + 0x30) >> 3 & 0x3f;
|
||||
iounmap(base);
|
||||
|
||||
ref_clk = of_clk_get(node, 0);
|
||||
if (IS_ERR(ref_clk)) {
|
||||
pr_err("%pOF: of_clk_get failed\n", node);
|
||||
return;
|
||||
}
|
||||
|
||||
hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, mul, 1);
|
||||
if (IS_ERR(hw)) {
|
||||
pr_err("%pOF: failed to register clock\n", node);
|
||||
@@ -56,7 +49,6 @@ static void __init moxart_of_apb_clk_init(struct device_node *node)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct clk_hw *hw;
|
||||
struct clk *pll_clk;
|
||||
unsigned int div, val;
|
||||
unsigned int div_idx[] = { 2, 3, 4, 6, 8};
|
||||
const char *name = node->name;
|
||||
@@ -78,12 +70,6 @@ static void __init moxart_of_apb_clk_init(struct device_node *node)
|
||||
val = 0;
|
||||
div = div_idx[val] * 2;
|
||||
|
||||
pll_clk = of_clk_get(node, 0);
|
||||
if (IS_ERR(pll_clk)) {
|
||||
pr_err("%pOF: of_clk_get failed\n", node);
|
||||
return;
|
||||
}
|
||||
|
||||
hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, 1, div);
|
||||
if (IS_ERR(hw)) {
|
||||
pr_err("%pOF: failed to register clock\n", node);
|
||||
|
||||
@@ -394,9 +394,9 @@ static const struct rs9_chip_info renesas_9fgv0841_info = {
|
||||
};
|
||||
|
||||
static const struct i2c_device_id rs9_id[] = {
|
||||
{ "9fgv0241", .driver_data = (kernel_ulong_t)&renesas_9fgv0241_info },
|
||||
{ "9fgv0441", .driver_data = (kernel_ulong_t)&renesas_9fgv0441_info },
|
||||
{ "9fgv0841", .driver_data = (kernel_ulong_t)&renesas_9fgv0841_info },
|
||||
{ .name = "9fgv0241", .driver_data = (kernel_ulong_t)&renesas_9fgv0241_info },
|
||||
{ .name = "9fgv0441", .driver_data = (kernel_ulong_t)&renesas_9fgv0441_info },
|
||||
{ .name = "9fgv0841", .driver_data = (kernel_ulong_t)&renesas_9fgv0841_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, rs9_id);
|
||||
|
||||
@@ -1174,12 +1174,6 @@ static unsigned long rp1_varsrc_recalc_rate(struct clk_hw *hw,
|
||||
return clock->cached_rate;
|
||||
}
|
||||
|
||||
static int rp1_varsrc_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops rp1_pll_core_ops = {
|
||||
.is_prepared = rp1_pll_core_is_on,
|
||||
.prepare = rp1_pll_core_on,
|
||||
@@ -1227,7 +1221,7 @@ static const struct clk_ops rp1_clk_ops = {
|
||||
static const struct clk_ops rp1_varsrc_ops = {
|
||||
.set_rate = rp1_varsrc_set_rate,
|
||||
.recalc_rate = rp1_varsrc_recalc_rate,
|
||||
.determine_rate = rp1_varsrc_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
};
|
||||
|
||||
static struct clk_hw *rp1_register_pll(struct rp1_clockman *clockman,
|
||||
|
||||
@@ -225,12 +225,12 @@ static void s2mps11_clk_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct platform_device_id s2mps11_clk_id[] = {
|
||||
{ "s2mpg10-clk", S2MPG10},
|
||||
{ "s2mps11-clk", S2MPS11X},
|
||||
{ "s2mps13-clk", S2MPS13X},
|
||||
{ "s2mps14-clk", S2MPS14X},
|
||||
{ "s5m8767-clk", S5M8767X},
|
||||
{ },
|
||||
{ .name = "s2mpg10-clk", .driver_data = S2MPG10 },
|
||||
{ .name = "s2mps11-clk", .driver_data = S2MPS11X },
|
||||
{ .name = "s2mps13-clk", .driver_data = S2MPS13X },
|
||||
{ .name = "s2mps14-clk", .driver_data = S2MPS14X },
|
||||
{ .name = "s5m8767-clk", .driver_data = S5M8767X },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, s2mps11_clk_id);
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* The Vendor OEM extension for System Control and Power Interface (SCMI)
|
||||
* Protocol based clock driver
|
||||
*
|
||||
* Copyright 2025 NXP
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/scmi_imx_protocol.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
|
||||
#include "clk-scmi.h"
|
||||
|
||||
#define SCMI_CLOCK_CFG_IMX_SSC 0x80
|
||||
#define SCMI_CLOCK_IMX_SS_PERCENTAGE_MASK GENMASK(7, 0)
|
||||
#define SCMI_CLOCK_IMX_SS_MOD_FREQ_MASK GENMASK(23, 8)
|
||||
#define SCMI_CLOCK_IMX_SS_ENABLE_MASK BIT(24)
|
||||
|
||||
/*
|
||||
* Selection is based on SCMI vendor_id/sub_vendor_id and optional machine
|
||||
* compatible string, without involving impl_ver. impl_ver‑specific behavior
|
||||
* should be considered a bug and handled via SCMI Quirk framework.
|
||||
*/
|
||||
struct scmi_clk_oem_info {
|
||||
char *vendor_id;
|
||||
char *sub_vendor_id;
|
||||
char *compatible;
|
||||
const void *data;
|
||||
};
|
||||
|
||||
static int
|
||||
scmi_clk_imx_set_spread_spectrum(struct clk_hw *hw,
|
||||
const struct clk_spread_spectrum *ss_conf)
|
||||
{
|
||||
struct scmi_clk *clk = to_scmi_clk(hw);
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* extConfigValue[7:0] - spread percentage (%)
|
||||
* extConfigValue[23:8] - Modulation Frequency
|
||||
* extConfigValue[24] - Enable/Disable
|
||||
* extConfigValue[31:25] - Reserved
|
||||
*/
|
||||
val = FIELD_PREP(SCMI_CLOCK_IMX_SS_PERCENTAGE_MASK, ss_conf->spread_bp / 10000);
|
||||
val |= FIELD_PREP(SCMI_CLOCK_IMX_SS_MOD_FREQ_MASK, ss_conf->modfreq_hz);
|
||||
if (ss_conf->method != CLK_SPREAD_NO)
|
||||
val |= SCMI_CLOCK_IMX_SS_ENABLE_MASK;
|
||||
ret = scmi_proto_clk_ops->config_oem_set(clk->ph, clk->id,
|
||||
SCMI_CLOCK_CFG_IMX_SSC,
|
||||
val, false);
|
||||
if (ret)
|
||||
dev_warn(clk->dev,
|
||||
"Failed to set spread spectrum(%u,%u,%u) for clock ID %d\n",
|
||||
ss_conf->modfreq_hz, ss_conf->spread_bp, ss_conf->method,
|
||||
clk->id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
scmi_clk_imx_query_oem_feats(const struct scmi_protocol_handle *ph, u32 id,
|
||||
unsigned int *feats_key)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = scmi_proto_clk_ops->config_oem_get(ph, id,
|
||||
SCMI_CLOCK_CFG_IMX_SSC,
|
||||
&val, NULL, false);
|
||||
if (!ret)
|
||||
*feats_key |= BIT(SCMI_CLK_EXT_OEM_SSC_SUPPORTED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct scmi_clk_oem scmi_clk_oem_imx = {
|
||||
.query_ext_oem_feats = scmi_clk_imx_query_oem_feats,
|
||||
.set_spread_spectrum = scmi_clk_imx_set_spread_spectrum,
|
||||
};
|
||||
|
||||
static const struct scmi_clk_oem_info info[] = {
|
||||
{ SCMI_IMX_VENDOR, SCMI_IMX_SUBVENDOR, NULL, &scmi_clk_oem_imx },
|
||||
};
|
||||
|
||||
int scmi_clk_oem_init(struct scmi_device *sdev)
|
||||
{
|
||||
const struct scmi_handle *handle = sdev->handle;
|
||||
int i, size = ARRAY_SIZE(info);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (strcmp(handle->version->vendor_id, info[i].vendor_id) ||
|
||||
strcmp(handle->version->sub_vendor_id, info[i].sub_vendor_id))
|
||||
continue;
|
||||
if (info[i].compatible &&
|
||||
!of_machine_is_compatible(info[i].compatible))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < size)
|
||||
dev_set_drvdata(&sdev->dev, (void *)info[i].data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+16
-28
@@ -13,32 +13,9 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
|
||||
#define NOT_ATOMIC false
|
||||
#define ATOMIC true
|
||||
#include "clk-scmi.h"
|
||||
|
||||
enum scmi_clk_feats {
|
||||
SCMI_CLK_ATOMIC_SUPPORTED,
|
||||
SCMI_CLK_STATE_CTRL_SUPPORTED,
|
||||
SCMI_CLK_RATE_CTRL_SUPPORTED,
|
||||
SCMI_CLK_PARENT_CTRL_SUPPORTED,
|
||||
SCMI_CLK_DUTY_CYCLE_SUPPORTED,
|
||||
SCMI_CLK_FEATS_COUNT
|
||||
};
|
||||
|
||||
#define SCMI_MAX_CLK_OPS BIT(SCMI_CLK_FEATS_COUNT)
|
||||
|
||||
static const struct scmi_clk_proto_ops *scmi_proto_clk_ops;
|
||||
|
||||
struct scmi_clk {
|
||||
u32 id;
|
||||
struct device *dev;
|
||||
struct clk_hw hw;
|
||||
const struct scmi_clock_info *info;
|
||||
const struct scmi_protocol_handle *ph;
|
||||
struct clk_parent_data *parent_data;
|
||||
};
|
||||
|
||||
#define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw)
|
||||
const struct scmi_clk_proto_ops *scmi_proto_clk_ops;
|
||||
|
||||
static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
@@ -235,6 +212,7 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk,
|
||||
static const struct clk_ops *
|
||||
scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key)
|
||||
{
|
||||
struct scmi_clk_oem *oem_data = dev_get_drvdata(dev);
|
||||
struct clk_ops *ops;
|
||||
|
||||
ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL);
|
||||
@@ -281,11 +259,15 @@ scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key)
|
||||
ops->set_duty_cycle = scmi_clk_set_duty_cycle;
|
||||
}
|
||||
|
||||
if (oem_data && (feats_key & BIT(SCMI_CLK_EXT_OEM_SSC_SUPPORTED)))
|
||||
ops->set_spread_spectrum = oem_data->set_spread_spectrum;
|
||||
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* scmi_clk_ops_select() - Select a proper set of clock operations
|
||||
* @sdev: pointer to the SCMI device
|
||||
* @sclk: A reference to an SCMI clock descriptor
|
||||
* @atomic_capable: A flag to indicate if atomic mode is supported by the
|
||||
* transport
|
||||
@@ -310,8 +292,8 @@ scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key)
|
||||
* NULL otherwise.
|
||||
*/
|
||||
static const struct clk_ops *
|
||||
scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
|
||||
unsigned int atomic_threshold_us,
|
||||
scmi_clk_ops_select(struct scmi_device *sdev, struct scmi_clk *sclk,
|
||||
bool atomic_capable, unsigned int atomic_threshold_us,
|
||||
const struct clk_ops **clk_ops_db, size_t db_size)
|
||||
{
|
||||
int ret;
|
||||
@@ -319,6 +301,7 @@ scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
|
||||
const struct scmi_clock_info *ci = sclk->info;
|
||||
unsigned int feats_key = 0;
|
||||
const struct clk_ops *ops;
|
||||
struct scmi_clk_oem *oem_data = dev_get_drvdata(&sdev->dev);
|
||||
|
||||
/*
|
||||
* Note that when transport is atomic but SCMI protocol did not
|
||||
@@ -343,6 +326,9 @@ scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
|
||||
&val, NULL, false);
|
||||
if (!ret)
|
||||
feats_key |= BIT(SCMI_CLK_DUTY_CYCLE_SUPPORTED);
|
||||
|
||||
if (oem_data && oem_data->query_ext_oem_feats)
|
||||
oem_data->query_ext_oem_feats(sclk->ph, sclk->id, &feats_key);
|
||||
}
|
||||
|
||||
if (WARN_ON(feats_key >= db_size))
|
||||
@@ -400,6 +386,8 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
|
||||
clk_data->num = count;
|
||||
hws = clk_data->hws;
|
||||
|
||||
scmi_clk_oem_init(sdev);
|
||||
|
||||
transport_is_atomic = handle->is_transport_atomic(handle,
|
||||
&atomic_threshold_us);
|
||||
|
||||
@@ -431,7 +419,7 @@ static int scmi_clocks_probe(struct scmi_device *sdev)
|
||||
* to avoid sharing the devm_ allocated clk_ops between multiple
|
||||
* SCMI clk driver instances.
|
||||
*/
|
||||
scmi_ops = scmi_clk_ops_select(sclk, transport_is_atomic,
|
||||
scmi_ops = scmi_clk_ops_select(sdev, sclk, transport_is_atomic,
|
||||
atomic_threshold_us,
|
||||
scmi_clk_ops_db,
|
||||
ARRAY_SIZE(scmi_clk_ops_db));
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2025 NXP
|
||||
*/
|
||||
|
||||
#ifndef __SCMI_CLK_H
|
||||
#define __SCMI_CLK_H
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/scmi_protocol.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define NOT_ATOMIC false
|
||||
#define ATOMIC true
|
||||
|
||||
enum scmi_clk_feats {
|
||||
SCMI_CLK_ATOMIC_SUPPORTED,
|
||||
SCMI_CLK_STATE_CTRL_SUPPORTED,
|
||||
SCMI_CLK_RATE_CTRL_SUPPORTED,
|
||||
SCMI_CLK_PARENT_CTRL_SUPPORTED,
|
||||
SCMI_CLK_DUTY_CYCLE_SUPPORTED,
|
||||
SCMI_CLK_EXT_OEM_SSC_SUPPORTED,
|
||||
SCMI_CLK_FEATS_COUNT
|
||||
};
|
||||
|
||||
#define SCMI_MAX_CLK_OPS BIT(SCMI_CLK_FEATS_COUNT)
|
||||
|
||||
struct scmi_clk {
|
||||
u32 id;
|
||||
struct device *dev;
|
||||
struct clk_hw hw;
|
||||
const struct scmi_clock_info *info;
|
||||
const struct scmi_protocol_handle *ph;
|
||||
struct clk_parent_data *parent_data;
|
||||
};
|
||||
|
||||
#define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw)
|
||||
|
||||
extern const struct scmi_clk_proto_ops *scmi_proto_clk_ops;
|
||||
|
||||
struct scmi_clk_oem {
|
||||
int (*query_ext_oem_feats)(const struct scmi_protocol_handle *ph,
|
||||
u32 id, unsigned int *feats_key);
|
||||
int (*set_spread_spectrum)(struct clk_hw *hw,
|
||||
const struct clk_spread_spectrum *ss_conf);
|
||||
};
|
||||
|
||||
int scmi_clk_oem_init(struct scmi_device *dev);
|
||||
|
||||
#endif
|
||||
+1
-13
@@ -32,18 +32,6 @@ static unsigned long scpi_clk_recalc_rate(struct clk_hw *hw,
|
||||
return clk->scpi_ops->clk_get_val(clk->id);
|
||||
}
|
||||
|
||||
static int scpi_clk_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
/*
|
||||
* We can't figure out what rate it will be, so just return the
|
||||
* rate back to the caller. scpi_clk_recalc_rate() will be called
|
||||
* after the rate is set and we'll know what rate the clock is
|
||||
* running at then.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scpi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
@@ -54,7 +42,7 @@ static int scpi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
|
||||
static const struct clk_ops scpi_clk_ops = {
|
||||
.recalc_rate = scpi_clk_recalc_rate,
|
||||
.determine_rate = scpi_clk_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
.set_rate = scpi_clk_set_rate,
|
||||
};
|
||||
|
||||
|
||||
@@ -379,7 +379,7 @@ static int si514_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id si514_id[] = {
|
||||
{ "si514" },
|
||||
{ .name = "si514" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si514_id);
|
||||
|
||||
@@ -364,9 +364,9 @@ static int __maybe_unused si521xx_resume(struct device *dev)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id si521xx_id[] = {
|
||||
{ "si52144", .driver_data = SI521XX_OE_MAP(0x5, 0xc0) },
|
||||
{ "si52146", .driver_data = SI521XX_OE_MAP(0x15, 0xe0) },
|
||||
{ "si52147", .driver_data = SI521XX_OE_MAP(0x17, 0xf8) },
|
||||
{ .name = "si52144", .driver_data = SI521XX_OE_MAP(0x5, 0xc0) },
|
||||
{ .name = "si52146", .driver_data = SI521XX_OE_MAP(0x15, 0xe0) },
|
||||
{ .name = "si52147", .driver_data = SI521XX_OE_MAP(0x17, 0xf8) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si521xx_id);
|
||||
|
||||
@@ -1825,11 +1825,11 @@ static void si5341_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id si5341_id[] = {
|
||||
{ "si5340", 0 },
|
||||
{ "si5341", 1 },
|
||||
{ "si5342", 2 },
|
||||
{ "si5344", 4 },
|
||||
{ "si5345", 5 },
|
||||
{ .name = "si5340" },
|
||||
{ .name = "si5341" },
|
||||
{ .name = "si5342" },
|
||||
{ .name = "si5344" },
|
||||
{ .name = "si5345" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si5341_id);
|
||||
|
||||
@@ -1425,10 +1425,10 @@ si53351_of_clk_get(struct of_phandle_args *clkspec, void *data)
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static const struct i2c_device_id si5351_i2c_ids[] = {
|
||||
{ "si5351a", SI5351_VARIANT_A },
|
||||
{ "si5351a-msop", SI5351_VARIANT_A3 },
|
||||
{ "si5351b", SI5351_VARIANT_B },
|
||||
{ "si5351c", SI5351_VARIANT_C },
|
||||
{ .name = "si5351a", .driver_data = SI5351_VARIANT_A },
|
||||
{ .name = "si5351a-msop", .driver_data = SI5351_VARIANT_A3 },
|
||||
{ .name = "si5351b", .driver_data = SI5351_VARIANT_B },
|
||||
{ .name = "si5351c", .driver_data = SI5351_VARIANT_C },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids);
|
||||
|
||||
@@ -479,9 +479,9 @@ static int si544_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id si544_id[] = {
|
||||
{ "si544a", 1500000000 },
|
||||
{ "si544b", 800000000 },
|
||||
{ "si544c", 350000000 },
|
||||
{ .name = "si544a", .driver_data = 1500000000 },
|
||||
{ .name = "si544b", .driver_data = 800000000 },
|
||||
{ .name = "si544c", .driver_data = 350000000 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si544_id);
|
||||
|
||||
@@ -503,10 +503,10 @@ static const struct clk_si570_info clk_si590_info = {
|
||||
};
|
||||
|
||||
static const struct i2c_device_id si570_id[] = {
|
||||
{ "si570", (kernel_ulong_t)&clk_si570_info },
|
||||
{ "si571", (kernel_ulong_t)&clk_si570_info },
|
||||
{ "si598", (kernel_ulong_t)&clk_si590_info },
|
||||
{ "si599", (kernel_ulong_t)&clk_si590_info },
|
||||
{ .name = "si570", .driver_data = (kernel_ulong_t)&clk_si570_info },
|
||||
{ .name = "si571", .driver_data = (kernel_ulong_t)&clk_si570_info },
|
||||
{ .name = "si598", .driver_data = (kernel_ulong_t)&clk_si590_info },
|
||||
{ .name = "si599", .driver_data = (kernel_ulong_t)&clk_si590_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si570_id);
|
||||
|
||||
@@ -1310,14 +1310,14 @@ static const struct vc5_chip_info idt_5p49v6975_info = {
|
||||
};
|
||||
|
||||
static const struct i2c_device_id vc5_id[] = {
|
||||
{ "5p49v5923", .driver_data = (kernel_ulong_t)&idt_5p49v5923_info },
|
||||
{ "5p49v5925", .driver_data = (kernel_ulong_t)&idt_5p49v5925_info },
|
||||
{ "5p49v5933", .driver_data = (kernel_ulong_t)&idt_5p49v5933_info },
|
||||
{ "5p49v5935", .driver_data = (kernel_ulong_t)&idt_5p49v5935_info },
|
||||
{ "5p49v60", .driver_data = (kernel_ulong_t)&idt_5p49v60_info },
|
||||
{ "5p49v6901", .driver_data = (kernel_ulong_t)&idt_5p49v6901_info },
|
||||
{ "5p49v6965", .driver_data = (kernel_ulong_t)&idt_5p49v6965_info },
|
||||
{ "5p49v6975", .driver_data = (kernel_ulong_t)&idt_5p49v6975_info },
|
||||
{ .name = "5p49v5923", .driver_data = (kernel_ulong_t)&idt_5p49v5923_info },
|
||||
{ .name = "5p49v5925", .driver_data = (kernel_ulong_t)&idt_5p49v5925_info },
|
||||
{ .name = "5p49v5933", .driver_data = (kernel_ulong_t)&idt_5p49v5933_info },
|
||||
{ .name = "5p49v5935", .driver_data = (kernel_ulong_t)&idt_5p49v5935_info },
|
||||
{ .name = "5p49v60", .driver_data = (kernel_ulong_t)&idt_5p49v60_info },
|
||||
{ .name = "5p49v6901", .driver_data = (kernel_ulong_t)&idt_5p49v6901_info },
|
||||
{ .name = "5p49v6965", .driver_data = (kernel_ulong_t)&idt_5p49v6965_info },
|
||||
{ .name = "5p49v6975", .driver_data = (kernel_ulong_t)&idt_5p49v6975_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, vc5_id);
|
||||
|
||||
@@ -1197,7 +1197,7 @@ static int vc7_probe(struct i2c_client *client)
|
||||
if (ret) {
|
||||
dev_err_probe(&client->dev, ret,
|
||||
"unable to register output %d\n", i);
|
||||
return ret;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
switch (bank_src_map.type) {
|
||||
@@ -1288,8 +1288,8 @@ static const struct regmap_config vc7_regmap_config = {
|
||||
};
|
||||
|
||||
static const struct i2c_device_id vc7_i2c_id[] = {
|
||||
{ "rc21008a", .driver_data = (kernel_ulong_t)&vc7_rc21008a_info },
|
||||
{}
|
||||
{ .name = "rc21008a", .driver_data = (kernel_ulong_t)&vc7_rc21008a_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, vc7_i2c_id);
|
||||
|
||||
|
||||
+104
-8
@@ -63,6 +63,57 @@ struct clk_parent_map {
|
||||
int index;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct clk_core - The internal state of a clk in the clk tree.
|
||||
* @name: Unique name of the clk for identification.
|
||||
* @ops: Pointer to hardware-specific operations for this clk.
|
||||
* @hw: Pointer for traversing from a struct clk to its
|
||||
* corresponding hardware-specific structure.
|
||||
* @owner: Kernel module owning this clk (for reference counting).
|
||||
* @dev: Device associated with this clk (optional)
|
||||
* @rpm_node: Node for runtime power management list management.
|
||||
* @of_node: Device tree node associated with this clk (if applicable)
|
||||
* @parent: Pointer to the current parent in the clock tree.
|
||||
* @parents: Array of possible parents (for muxes/selectable parents).
|
||||
* @num_parents: Number of possible parents.
|
||||
* @new_parent_index: Index of the new parent during parent change operations.
|
||||
* @rate: Current cached clock rate (Hz).
|
||||
* @req_rate: The last rate requested by a call to clk_set_rate(). It's
|
||||
* initialized to clk_core->rate. It's also updated to
|
||||
* clk_core->rate every time the clock is reparented, and
|
||||
* when we're doing the orphan -> !orphan transition.
|
||||
* @new_rate: New rate to be set during a rate change operation.
|
||||
* @new_parent: Pointer to new parent during parent change. This is also
|
||||
* used when a clk's rate is changed.
|
||||
* @new_child: Pointer to new child during reparenting. This is also
|
||||
* used when a clk's rate is changed.
|
||||
* @flags: Clock property and capability flags. See
|
||||
* `clk framework flags`.
|
||||
* @orphan: True if this clk is currently orphaned.
|
||||
* @rpm_enabled: True if runtime power management is enabled for this clk.
|
||||
* @enable_count: Reference count of enables.
|
||||
* @prepare_count: Reference count of prepares.
|
||||
* @protect_count: Protection reference count against disable.
|
||||
* @min_rate: Minimum supported clock rate (Hz).
|
||||
* @max_rate: Maximum supported clock rate (Hz).
|
||||
* @accuracy: Accuracy of the clock rate (parts per billion).
|
||||
* @phase: Current phase (degrees).
|
||||
* @duty: Current duty cycle configuration (as ratio: num/den).
|
||||
* @children: All of the children of this clk.
|
||||
* @child_node: Node for linking as a child in the parent's list.
|
||||
* @hashtable_node: Node for hash table that allows fast clk lookup by name.
|
||||
* @clks: All of the clk consumers registered.
|
||||
* @notifier_count: Number of notifiers registered for this clk.
|
||||
* @dentry: DebugFS entry for this clk.
|
||||
* @debug_node: DebugFS node for this clk.
|
||||
* @ref: Reference count for structure lifetime management.
|
||||
*
|
||||
* Managed by the clk framework. Clk providers and consumers do not interact
|
||||
* with this structure directly. Instead, clk operations flow through the
|
||||
* framework and the framework manipulates this structure to keep track of
|
||||
* parent/child relationships, rate, enable state, etc.
|
||||
*
|
||||
*/
|
||||
struct clk_core {
|
||||
const char *name;
|
||||
const struct clk_ops *ops;
|
||||
@@ -882,6 +933,24 @@ int clk_hw_determine_rate_no_reparent(struct clk_hw *hw,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_hw_determine_rate_no_reparent);
|
||||
|
||||
/**
|
||||
* clk_determine_rate_noop - clk_ops::determine_rate noop implementation
|
||||
* @hw: clk to determine rate on
|
||||
* @req: rate request
|
||||
*
|
||||
* Noop determine rate for clocks where the rate rounding is handled by the
|
||||
* firmware/hardware, or the clock is capable of any rate. The requested rate is
|
||||
* passed through unchanged, and the actual rate will be learned via
|
||||
* recalc_rate() after the rate is set.
|
||||
*
|
||||
* Returns: 0 always
|
||||
*/
|
||||
int clk_determine_rate_noop(struct clk_hw *hw, struct clk_rate_request *req)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_determine_rate_noop);
|
||||
|
||||
/*** clk api ***/
|
||||
|
||||
static void clk_core_rate_unprotect(struct clk_core *core)
|
||||
@@ -1846,10 +1915,10 @@ static int __clk_notify(struct clk_core *core, unsigned long msg,
|
||||
}
|
||||
|
||||
/**
|
||||
* __clk_recalc_accuracies
|
||||
* __clk_recalc_accuracies - recalculate all accuracies in the clk subtree
|
||||
* @core: first clk in the subtree
|
||||
*
|
||||
* Walks the subtree of clks starting with clk and recalculates accuracies as
|
||||
* Walks the subtree of clks starting with @core and recalculates accuracies as
|
||||
* it goes. Note that if a clk does not implement the .recalc_accuracy
|
||||
* callback then it is assumed that the clock will take on the accuracy of its
|
||||
* parent.
|
||||
@@ -1919,16 +1988,16 @@ static unsigned long clk_recalc(struct clk_core *core,
|
||||
}
|
||||
|
||||
/**
|
||||
* __clk_recalc_rates
|
||||
* __clk_recalc_rates - recalculate all rates in the clk subtree
|
||||
* @core: first clk in the subtree
|
||||
* @update_req: Whether req_rate should be updated with the new rate
|
||||
* @msg: notification type (see include/linux/clk.h)
|
||||
*
|
||||
* Walks the subtree of clks starting with clk and recalculates rates as it
|
||||
* Walks the subtree of clks starting with @core and recalculates rates as it
|
||||
* goes. Note that if a clk does not implement the .recalc_rate callback then
|
||||
* it is assumed that the clock will take on the rate of its parent.
|
||||
*
|
||||
* clk_recalc_rates also propagates the POST_RATE_CHANGE notification,
|
||||
* __clk_recalc_rates also propagates the POST_RATE_CHANGE notification,
|
||||
* if necessary.
|
||||
*/
|
||||
static void __clk_recalc_rates(struct clk_core *core, bool update_req,
|
||||
@@ -2191,14 +2260,14 @@ static int __clk_set_parent(struct clk_core *core, struct clk_core *parent,
|
||||
}
|
||||
|
||||
/**
|
||||
* __clk_speculate_rates
|
||||
* __clk_speculate_rates - speculate all rates in the clk subtree
|
||||
* @core: first clk in the subtree
|
||||
* @parent_rate: the "future" rate of clk's parent
|
||||
*
|
||||
* Walks the subtree of clks starting with clk, speculating rates as it
|
||||
* Walks the subtree of clks starting with @core, speculating rates as it
|
||||
* goes and firing off PRE_RATE_CHANGE notifications as necessary.
|
||||
*
|
||||
* Unlike clk_recalc_rates, clk_speculate_rates exists only for sending
|
||||
* Unlike __clk_recalc_rates, __clk_speculate_rates exists only for sending
|
||||
* pre-rate change notifications and returns early if no clks in the
|
||||
* subtree have subscribed to the notifications. Note that if a clk does not
|
||||
* implement the .recalc_rate callback then it is assumed that the clock will
|
||||
@@ -2774,6 +2843,33 @@ int clk_set_max_rate(struct clk *clk, unsigned long rate)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_set_max_rate);
|
||||
|
||||
int clk_hw_set_spread_spectrum(struct clk_hw *hw, const struct clk_spread_spectrum *ss_conf)
|
||||
{
|
||||
struct clk_core *core;
|
||||
int ret;
|
||||
|
||||
if (!hw)
|
||||
return 0;
|
||||
|
||||
core = hw->core;
|
||||
|
||||
clk_prepare_lock();
|
||||
|
||||
ret = clk_pm_runtime_get(core);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
if (core->ops->set_spread_spectrum)
|
||||
ret = core->ops->set_spread_spectrum(hw, ss_conf);
|
||||
|
||||
clk_pm_runtime_put(core);
|
||||
|
||||
fail:
|
||||
clk_prepare_unlock();
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_hw_set_spread_spectrum);
|
||||
|
||||
/**
|
||||
* clk_get_parent - return the parent of a clk
|
||||
* @clk: the clk whose parent gets returned
|
||||
|
||||
+206
-8
@@ -7,6 +7,7 @@
|
||||
#include <linux/clk/clk-conf.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
/* Needed for clk_hw_get_clk() */
|
||||
#include "clk.h"
|
||||
@@ -21,13 +22,14 @@
|
||||
|
||||
static const struct clk_ops empty_clk_ops = { };
|
||||
|
||||
#define DUMMY_CLOCK_INIT_RATE (42 * 1000 * 1000)
|
||||
#define DUMMY_CLOCK_RATE_1 (142 * 1000 * 1000)
|
||||
#define DUMMY_CLOCK_RATE_2 (242 * 1000 * 1000)
|
||||
#define DUMMY_CLOCK_INIT_RATE (42 * HZ_PER_MHZ)
|
||||
#define DUMMY_CLOCK_RATE_1 (142 * HZ_PER_MHZ)
|
||||
#define DUMMY_CLOCK_RATE_2 (242 * HZ_PER_MHZ)
|
||||
|
||||
struct clk_dummy_context {
|
||||
struct clk_hw hw;
|
||||
unsigned long rate;
|
||||
struct clk_spread_spectrum sscs;
|
||||
};
|
||||
|
||||
static unsigned long clk_dummy_recalc_rate(struct clk_hw *hw,
|
||||
@@ -83,6 +85,17 @@ static int clk_dummy_set_rate(struct clk_hw *hw,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_dummy_set_spread_spectrum(struct clk_hw *hw,
|
||||
const struct clk_spread_spectrum *ss_conf)
|
||||
{
|
||||
struct clk_dummy_context *ctx =
|
||||
container_of(hw, struct clk_dummy_context, hw);
|
||||
|
||||
ctx->sscs = *ss_conf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_dummy_single_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
if (index >= clk_hw_get_num_parents(hw))
|
||||
@@ -100,18 +113,21 @@ static const struct clk_ops clk_dummy_rate_ops = {
|
||||
.recalc_rate = clk_dummy_recalc_rate,
|
||||
.determine_rate = clk_dummy_determine_rate,
|
||||
.set_rate = clk_dummy_set_rate,
|
||||
.set_spread_spectrum = clk_dummy_set_spread_spectrum,
|
||||
};
|
||||
|
||||
static const struct clk_ops clk_dummy_maximize_rate_ops = {
|
||||
.recalc_rate = clk_dummy_recalc_rate,
|
||||
.determine_rate = clk_dummy_maximize_rate,
|
||||
.set_rate = clk_dummy_set_rate,
|
||||
.set_spread_spectrum = clk_dummy_set_spread_spectrum,
|
||||
};
|
||||
|
||||
static const struct clk_ops clk_dummy_minimize_rate_ops = {
|
||||
.recalc_rate = clk_dummy_recalc_rate,
|
||||
.determine_rate = clk_dummy_minimize_rate,
|
||||
.set_rate = clk_dummy_set_rate,
|
||||
.set_spread_spectrum = clk_dummy_set_spread_spectrum,
|
||||
};
|
||||
|
||||
static const struct clk_ops clk_dummy_single_parent_ops = {
|
||||
@@ -2232,7 +2248,7 @@ static void
|
||||
clk_leaf_mux_set_rate_parent_determine_rate_test_case_to_desc(
|
||||
const struct clk_leaf_mux_set_rate_parent_determine_rate_test_case *t, char *desc)
|
||||
{
|
||||
strcpy(desc, t->desc);
|
||||
strscpy(desc, t->desc, KUNIT_PARAM_DESC_SIZE);
|
||||
}
|
||||
|
||||
static const struct clk_leaf_mux_set_rate_parent_determine_rate_test_case
|
||||
@@ -2644,7 +2660,7 @@ static void
|
||||
clk_register_clk_parent_data_test_case_to_desc(
|
||||
const struct clk_register_clk_parent_data_test_case *t, char *desc)
|
||||
{
|
||||
strcpy(desc, t->desc);
|
||||
strscpy(desc, t->desc, KUNIT_PARAM_DESC_SIZE);
|
||||
}
|
||||
|
||||
static const struct clk_register_clk_parent_data_test_case
|
||||
@@ -3097,6 +3113,7 @@ struct clk_assigned_rates_context {
|
||||
* @overlay_end: Pointer to end of DT overlay to apply for test
|
||||
* @rate0: Initial rate of first clk
|
||||
* @rate1: Initial rate of second clk
|
||||
* @sscs: Initial spread spectrum settings
|
||||
* @consumer_test: true if a consumer is being tested
|
||||
*/
|
||||
struct clk_assigned_rates_test_param {
|
||||
@@ -3105,6 +3122,7 @@ struct clk_assigned_rates_test_param {
|
||||
u8 *overlay_end;
|
||||
unsigned long rate0;
|
||||
unsigned long rate1;
|
||||
struct clk_spread_spectrum sscs;
|
||||
bool consumer_test;
|
||||
};
|
||||
|
||||
@@ -3116,7 +3134,7 @@ static void
|
||||
clk_assigned_rates_register_clk(struct kunit *test,
|
||||
struct clk_dummy_context *ctx,
|
||||
struct device_node *np, const char *name,
|
||||
unsigned long rate)
|
||||
unsigned long rate, const struct clk_spread_spectrum *sscs)
|
||||
{
|
||||
struct clk_init_data init = { };
|
||||
|
||||
@@ -3124,6 +3142,7 @@ clk_assigned_rates_register_clk(struct kunit *test,
|
||||
init.ops = &clk_dummy_rate_ops;
|
||||
ctx->hw.init = &init;
|
||||
ctx->rate = rate;
|
||||
ctx->sscs = *sscs;
|
||||
|
||||
KUNIT_ASSERT_EQ(test, 0, of_clk_hw_register_kunit(test, np, &ctx->hw));
|
||||
KUNIT_ASSERT_EQ(test, ctx->rate, rate);
|
||||
@@ -3167,14 +3186,16 @@ static int clk_assigned_rates_test_init(struct kunit *test)
|
||||
KUNIT_ASSERT_LT(test, clk_cells, 2);
|
||||
|
||||
clk_assigned_rates_register_clk(test, &ctx->clk0, np,
|
||||
"test_assigned_rate0", test_param->rate0);
|
||||
"test_assigned_rate0", test_param->rate0,
|
||||
&test_param->sscs);
|
||||
if (clk_cells == 0) {
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
of_clk_add_hw_provider_kunit(test, np, of_clk_hw_simple_get,
|
||||
&ctx->clk0.hw));
|
||||
} else if (clk_cells == 1) {
|
||||
clk_assigned_rates_register_clk(test, &ctx->clk1, np,
|
||||
"test_assigned_rate1", test_param->rate1);
|
||||
"test_assigned_rate1", test_param->rate1,
|
||||
&test_param->sscs);
|
||||
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test,
|
||||
data = kunit_kzalloc(test, struct_size(data, hws, 2), GFP_KERNEL));
|
||||
@@ -3403,6 +3424,182 @@ static struct kunit_suite clk_assigned_rates_suite = {
|
||||
.init = clk_assigned_rates_test_init,
|
||||
};
|
||||
|
||||
OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_one);
|
||||
OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_one_consumer);
|
||||
OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_multiple);
|
||||
OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_multiple_consumer);
|
||||
OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_without);
|
||||
OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_without_consumer);
|
||||
OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_zero);
|
||||
OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_zero_consumer);
|
||||
OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_null);
|
||||
OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_null_consumer);
|
||||
|
||||
static void clk_assigned_sscs_assigns_one(struct kunit *test)
|
||||
{
|
||||
struct clk_assigned_rates_context *ctx = test->priv;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.modfreq_hz, ASSIGNED_SSCS_0_MODFREQ);
|
||||
KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.spread_bp, ASSIGNED_SSCS_0_SPREAD);
|
||||
KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.method, ASSIGNED_SSCS_0_METHOD);
|
||||
}
|
||||
|
||||
/* Test cases that assign sscs for one clk */
|
||||
static const struct clk_assigned_rates_test_param clk_assigned_sscs_assigns_one_test_params[] = {
|
||||
{
|
||||
/*
|
||||
* Test that a single cell assigned-clock-sscs property
|
||||
* assigns the sscs when the property is in the provider.
|
||||
*/
|
||||
.desc = "provider assigns",
|
||||
TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_one),
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Test that a single cell assigned-clock-sscs property
|
||||
* assigns the sscs when the property is in the consumer.
|
||||
*/
|
||||
.desc = "consumer assigns",
|
||||
TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_one_consumer),
|
||||
.consumer_test = true,
|
||||
},
|
||||
};
|
||||
KUNIT_ARRAY_PARAM_DESC(clk_assigned_sscs_assigns_one,
|
||||
clk_assigned_sscs_assigns_one_test_params, desc)
|
||||
|
||||
static void clk_assigned_sscs_assigns_multiple(struct kunit *test)
|
||||
{
|
||||
struct clk_assigned_rates_context *ctx = test->priv;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.modfreq_hz, ASSIGNED_SSCS_0_MODFREQ);
|
||||
KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.spread_bp, ASSIGNED_SSCS_0_SPREAD);
|
||||
KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.method, ASSIGNED_SSCS_0_METHOD);
|
||||
KUNIT_EXPECT_EQ(test, ctx->clk1.sscs.modfreq_hz, ASSIGNED_SSCS_1_MODFREQ);
|
||||
KUNIT_EXPECT_EQ(test, ctx->clk1.sscs.spread_bp, ASSIGNED_SSCS_1_SPREAD);
|
||||
KUNIT_EXPECT_EQ(test, ctx->clk1.sscs.method, ASSIGNED_SSCS_1_METHOD);
|
||||
}
|
||||
|
||||
/* Test cases that assign sscs for multiple clks */
|
||||
static const
|
||||
struct clk_assigned_rates_test_param clk_assigned_sscs_assigns_multiple_test_params[] = {
|
||||
{
|
||||
/*
|
||||
* Test that a multiple cell assigned-clock-sscs property
|
||||
* assigns the sscs when the property is in the provider.
|
||||
*/
|
||||
.desc = "provider assigns",
|
||||
TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_multiple),
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Test that a multiple cell assigned-clock-sscs property
|
||||
* assigns the sscs when the property is in the consumer.
|
||||
*/
|
||||
.desc = "consumer assigns",
|
||||
TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_multiple_consumer),
|
||||
.consumer_test = true,
|
||||
},
|
||||
};
|
||||
KUNIT_ARRAY_PARAM_DESC(clk_assigned_sscs_assigns_multiple,
|
||||
clk_assigned_sscs_assigns_multiple_test_params,
|
||||
desc)
|
||||
|
||||
static void clk_assigned_sscs_skips(struct kunit *test)
|
||||
{
|
||||
struct clk_assigned_rates_context *ctx = test->priv;
|
||||
const struct clk_assigned_rates_test_param *test_param = test->param_value;
|
||||
|
||||
KUNIT_EXPECT_NE(test, ctx->clk0.sscs.modfreq_hz, ASSIGNED_SSCS_0_MODFREQ);
|
||||
KUNIT_EXPECT_NE(test, ctx->clk0.sscs.spread_bp, ASSIGNED_SSCS_0_SPREAD);
|
||||
KUNIT_EXPECT_NE(test, ctx->clk0.sscs.method, ASSIGNED_SSCS_0_METHOD);
|
||||
KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.modfreq_hz, test_param->sscs.modfreq_hz);
|
||||
KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.spread_bp, test_param->sscs.spread_bp);
|
||||
KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.method, test_param->sscs.method);
|
||||
}
|
||||
|
||||
/* Test cases that skip changing the sscs due to malformed DT */
|
||||
static const struct clk_assigned_rates_test_param clk_assigned_sscs_skips_test_params[] = {
|
||||
{
|
||||
/*
|
||||
* Test that an assigned-clock-sscs property without an assigned-clocks
|
||||
* property fails when the property is in the provider.
|
||||
*/
|
||||
.desc = "provider missing assigned-clocks",
|
||||
TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_without),
|
||||
.sscs = {50000, 60000, 3},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Test that an assigned-clock-sscs property without an assigned-clocks
|
||||
* property fails when the property is in the consumer.
|
||||
*/
|
||||
.desc = "consumer missing assigned-clocks",
|
||||
TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_without_consumer),
|
||||
.sscs = {50000, 60000, 3},
|
||||
.consumer_test = true,
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Test that an assigned-clock-sscs property of zero doesn't
|
||||
* set sscs when the property is in the provider.
|
||||
*/
|
||||
.desc = "provider assigned-clock-sscs of zero",
|
||||
TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_zero),
|
||||
.sscs = {50000, 60000, 3},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Test that an assigned-clock-sscs property of zero doesn't
|
||||
* set sscs when the property is in the consumer.
|
||||
*/
|
||||
.desc = "consumer assigned-clock-sscs of zero",
|
||||
TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_zero_consumer),
|
||||
.sscs = {50000, 60000, 3},
|
||||
.consumer_test = true,
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Test that an assigned-clocks property with a null phandle
|
||||
* doesn't set sscs when the property is in the provider.
|
||||
*/
|
||||
.desc = "provider assigned-clocks null phandle",
|
||||
TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_null),
|
||||
.sscs = {50000, 60000, 3},
|
||||
},
|
||||
{
|
||||
/*
|
||||
* Test that an assigned-clocks property with a null phandle
|
||||
* doesn't set sscs when the property is in the consumer.
|
||||
*/
|
||||
.desc = "consumer assigned-clocks null phandle",
|
||||
TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_null_consumer),
|
||||
.sscs = {50000, 60000, 3},
|
||||
.consumer_test = true,
|
||||
},
|
||||
};
|
||||
KUNIT_ARRAY_PARAM_DESC(clk_assigned_sscs_skips,
|
||||
clk_assigned_sscs_skips_test_params,
|
||||
desc)
|
||||
|
||||
static struct kunit_case clk_assigned_sscs_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(clk_assigned_sscs_assigns_one,
|
||||
clk_assigned_sscs_assigns_one_gen_params),
|
||||
KUNIT_CASE_PARAM(clk_assigned_sscs_assigns_multiple,
|
||||
clk_assigned_sscs_assigns_multiple_gen_params),
|
||||
KUNIT_CASE_PARAM(clk_assigned_sscs_skips,
|
||||
clk_assigned_sscs_skips_gen_params),
|
||||
{}
|
||||
};
|
||||
|
||||
/*
|
||||
* Test suite for assigned-clock-sscs DT property.
|
||||
*/
|
||||
static struct kunit_suite clk_assigned_sscs_suite = {
|
||||
.name = "clk_assigned_sscs",
|
||||
.test_cases = clk_assigned_sscs_test_cases,
|
||||
.init = clk_assigned_rates_test_init,
|
||||
};
|
||||
|
||||
static const struct clk_init_data clk_hw_get_dev_of_node_init_data = {
|
||||
.name = "clk_hw_get_dev_of_node",
|
||||
.ops = &empty_clk_ops,
|
||||
@@ -3544,6 +3741,7 @@ static struct kunit_suite clk_hw_get_dev_of_node_test_suite = {
|
||||
|
||||
kunit_test_suites(
|
||||
&clk_assigned_rates_suite,
|
||||
&clk_assigned_sscs_suite,
|
||||
&clk_hw_get_dev_of_node_test_suite,
|
||||
&clk_leaf_mux_set_rate_parent_test_suite,
|
||||
&clk_test_suite,
|
||||
|
||||
@@ -13,3 +13,16 @@ config COMMON_CLK_EIC7700
|
||||
SoC. The clock controller generates and supplies clocks to various
|
||||
peripherals within the SoC.
|
||||
Say yes here to support the clock controller on the EIC7700 SoC.
|
||||
|
||||
config COMMON_CLK_EIC7700_HSP
|
||||
tristate "EIC7700 HSP Clock Driver"
|
||||
depends on ARCH_ESWIN || COMPILE_TEST
|
||||
select AUXILIARY_BUS
|
||||
select COMMON_CLK_EIC7700
|
||||
select RESET_EIC7700_HSP if RESET_CONTROLLER
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
This driver provides support for clock controller on ESWIN EIC7700
|
||||
HSP. The clock controller generates and supplies clocks to high
|
||||
speed peripherals within the SoC.
|
||||
Say yes here to support the clock controller on the EIC7700 HSP.
|
||||
|
||||
@@ -6,3 +6,4 @@
|
||||
obj-$(CONFIG_COMMON_CLK_ESWIN) += clk.o
|
||||
|
||||
obj-$(CONFIG_COMMON_CLK_EIC7700) += clk-eic7700.o
|
||||
obj-$(CONFIG_COMMON_CLK_EIC7700_HSP) += clk-eic7700-hsp.o
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd..
|
||||
* All rights reserved.
|
||||
*
|
||||
* ESWIN EIC7700 HSP Clock Driver
|
||||
*
|
||||
* Authors: Xuyang Dong <dongxuyang@eswincomputing.com>
|
||||
*/
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/clock/eswin,eic7700-hspcrg.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define EIC7700_HSP_SATA_REG 0x300
|
||||
#define EIC7700_HSP_MSHC0_REG 0x510
|
||||
#define EIC7700_HSP_MSHC1_REG 0x610
|
||||
#define EIC7700_HSP_MSHC2_REG 0x710
|
||||
#define EIC7700_HSP_USB0_REG 0x800
|
||||
#define EIC7700_HSP_USB0_REF_REG 0x83c
|
||||
#define EIC7700_HSP_USB1_REG 0x900
|
||||
#define EIC7700_HSP_USB1_REF_REG 0x93c
|
||||
|
||||
#define USB_REF_XTAL24M 0x2a
|
||||
#define EIC7700_HSP_NR_CLKS (EIC7700_HSP_CLK_GATE_SATA + 1)
|
||||
|
||||
struct eic7700_hsp_clk_gate {
|
||||
struct clk_hw hw;
|
||||
unsigned int id;
|
||||
struct regmap *regmap;
|
||||
unsigned int reg;
|
||||
unsigned int ref_reg;
|
||||
const char *name;
|
||||
const struct clk_parent_data *parent_data;
|
||||
unsigned long flags;
|
||||
unsigned int offset;
|
||||
unsigned int ref_offset;
|
||||
u8 bit_idx;
|
||||
};
|
||||
|
||||
static const struct regmap_config eic7700_hsp_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.max_register = 0x1ffc,
|
||||
.reg_stride = 4,
|
||||
.fast_io = true,
|
||||
.use_raw_spinlock = true,
|
||||
};
|
||||
|
||||
static inline struct eic7700_hsp_clk_gate *to_gate_clk(struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct eic7700_hsp_clk_gate, hw);
|
||||
}
|
||||
|
||||
#define EIC7700_HSP_GATE(_id, _name, _pdata, _flags, _offset, _idx, \
|
||||
_ref_offset) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_data = _pdata, \
|
||||
.flags = _flags, \
|
||||
.offset = _offset, \
|
||||
.ref_offset = _ref_offset, \
|
||||
.bit_idx = _idx, \
|
||||
}
|
||||
|
||||
static void hsp_clk_gate_endisable(struct clk_hw *hw, bool enable)
|
||||
{
|
||||
struct eic7700_hsp_clk_gate *gate = to_gate_clk(hw);
|
||||
|
||||
if (enable) {
|
||||
/*
|
||||
* Hardware bug: The USB reference clock must be 24MHz.
|
||||
* The default register value after reset is invalid.
|
||||
* Workaround: Rewrite the correct value before enabling
|
||||
* the USB gate clock.
|
||||
*/
|
||||
regmap_update_bits(gate->regmap, gate->ref_reg, 0x3f,
|
||||
USB_REF_XTAL24M);
|
||||
}
|
||||
regmap_assign_bits(gate->regmap, gate->reg, BIT(gate->bit_idx), enable);
|
||||
}
|
||||
|
||||
static int hsp_clk_gate_enable(struct clk_hw *hw)
|
||||
{
|
||||
hsp_clk_gate_endisable(hw, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hsp_clk_gate_disable(struct clk_hw *hw)
|
||||
{
|
||||
hsp_clk_gate_endisable(hw, false);
|
||||
}
|
||||
|
||||
static int hsp_clk_gate_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct eic7700_hsp_clk_gate *gate = to_gate_clk(hw);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(gate->regmap, gate->reg, &val);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
return !!(val & BIT(gate->bit_idx));
|
||||
}
|
||||
|
||||
static const struct clk_ops hsp_clk_gate_ops = {
|
||||
.enable = hsp_clk_gate_enable,
|
||||
.disable = hsp_clk_gate_disable,
|
||||
.is_enabled = hsp_clk_gate_is_enabled,
|
||||
};
|
||||
|
||||
static struct clk_hw *
|
||||
hsp_clk_register_gate(struct device *dev, unsigned int id, const char *name,
|
||||
const struct clk_parent_data *parent_data,
|
||||
unsigned long flags, struct regmap *regmap,
|
||||
unsigned int reg, unsigned int ref_reg, u8 bit_idx)
|
||||
{
|
||||
struct eic7700_hsp_clk_gate *gate;
|
||||
struct clk_init_data init = {};
|
||||
struct clk_hw *hw;
|
||||
int ret;
|
||||
|
||||
gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
|
||||
if (!gate)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &hsp_clk_gate_ops;
|
||||
init.flags = flags;
|
||||
init.parent_data = parent_data;
|
||||
init.num_parents = 1;
|
||||
|
||||
gate->id = id;
|
||||
gate->regmap = regmap;
|
||||
gate->reg = reg;
|
||||
gate->ref_reg = ref_reg;
|
||||
gate->bit_idx = bit_idx;
|
||||
gate->hw.init = &init;
|
||||
|
||||
hw = &gate->hw;
|
||||
ret = devm_clk_hw_register(dev, hw);
|
||||
if (ret)
|
||||
hw = ERR_PTR(ret);
|
||||
|
||||
return hw;
|
||||
}
|
||||
|
||||
static const struct clk_parent_data hsp_cfg[] = {
|
||||
{ .index = 0 }
|
||||
};
|
||||
|
||||
static const struct clk_parent_data hsp_mmc[] = {
|
||||
{ .index = 1 }
|
||||
};
|
||||
|
||||
static const struct clk_parent_data hsp_usb_sata[] = {
|
||||
{ .index = 2 }
|
||||
};
|
||||
|
||||
static struct eswin_fixed_factor_clock eic7700_hsp_factor_clks[] = {
|
||||
ESWIN_FACTOR(EIC7700_HSP_CLK_FAC_CFG_DIV2, "factor_hsp_cfg_div2",
|
||||
hsp_cfg, 1, 2, 0),
|
||||
ESWIN_FACTOR(EIC7700_HSP_CLK_FAC_CFG_DIV4, "factor_hsp_cfg_div4",
|
||||
hsp_cfg, 1, 4, 0),
|
||||
ESWIN_FACTOR(EIC7700_HSP_CLK_FAC_MMC_DIV10, "factor_hsp_mmc_div10",
|
||||
hsp_mmc, 1, 10, 0),
|
||||
};
|
||||
|
||||
static struct eswin_gate_clock eic7700_hsp_gate_clks[] = {
|
||||
ESWIN_GATE(EIC7700_HSP_CLK_GATE_SATA, "gate_clk_hsp_sata", hsp_usb_sata,
|
||||
CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
|
||||
EIC7700_HSP_SATA_REG, 28, 0),
|
||||
ESWIN_GATE(EIC7700_HSP_CLK_GATE_MSHC0_TMR, "gate_clk_hsp_mshc0_tmr",
|
||||
hsp_mmc, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
|
||||
EIC7700_HSP_MSHC0_REG, 8, 0),
|
||||
ESWIN_GATE(EIC7700_HSP_CLK_GATE_MSHC1_TMR, "gate_clk_hsp_mshc1_tmr",
|
||||
hsp_mmc, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
|
||||
EIC7700_HSP_MSHC1_REG, 8, 0),
|
||||
ESWIN_GATE(EIC7700_HSP_CLK_GATE_MSHC2_TMR, "gate_clk_hsp_mshc2_tmr",
|
||||
hsp_mmc, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
|
||||
EIC7700_HSP_MSHC2_REG, 8, 0),
|
||||
};
|
||||
|
||||
static struct eic7700_hsp_clk_gate eic7700_hsp_spec_gate_clks[] = {
|
||||
EIC7700_HSP_GATE(EIC7700_HSP_CLK_GATE_USB0, "gate_clk_hsp_usb0",
|
||||
hsp_usb_sata, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
|
||||
EIC7700_HSP_USB0_REG, 28, EIC7700_HSP_USB0_REF_REG),
|
||||
EIC7700_HSP_GATE(EIC7700_HSP_CLK_GATE_USB1, "gate_clk_hsp_usb1",
|
||||
hsp_usb_sata, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
|
||||
EIC7700_HSP_USB1_REG, 28, EIC7700_HSP_USB1_REF_REG),
|
||||
};
|
||||
|
||||
static const struct clk_parent_data mux_mmc_3mux1_p[] = {
|
||||
{ .fw_name = "cfg" },
|
||||
{ .hw = &eic7700_hsp_factor_clks[0].hw },
|
||||
{ .hw = &eic7700_hsp_factor_clks[1].hw },
|
||||
};
|
||||
|
||||
static const struct clk_parent_data mux_mmc_2mux1_p[] = {
|
||||
{ .fw_name = "mmc" },
|
||||
{ .hw = &eic7700_hsp_factor_clks[2].hw },
|
||||
};
|
||||
|
||||
static u32 mux_mmc_3mux1_tbl[] = { 0x0, 0x1, 0x3 };
|
||||
|
||||
static struct eswin_mux_clock eic7700_hsp_mux_clks[] = {
|
||||
ESWIN_MUX_TBL(EIC7700_HSP_CLK_MUX_EMMC_3MUX1, "mux_hsp_emmc_3mux1",
|
||||
mux_mmc_3mux1_p, ARRAY_SIZE(mux_mmc_3mux1_p),
|
||||
CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC0_REG, 16, 2, 0,
|
||||
mux_mmc_3mux1_tbl),
|
||||
ESWIN_MUX_TBL(EIC7700_HSP_CLK_MUX_SD0_3MUX1, "mux_hsp_sd0_3mux1",
|
||||
mux_mmc_3mux1_p, ARRAY_SIZE(mux_mmc_3mux1_p),
|
||||
CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC1_REG, 16, 2, 0,
|
||||
mux_mmc_3mux1_tbl),
|
||||
ESWIN_MUX_TBL(EIC7700_HSP_CLK_MUX_SD1_3MUX1, "mux_hsp_sd1_3mux1",
|
||||
mux_mmc_3mux1_p, ARRAY_SIZE(mux_mmc_3mux1_p),
|
||||
CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC2_REG, 16, 2, 0,
|
||||
mux_mmc_3mux1_tbl),
|
||||
ESWIN_MUX(EIC7700_HSP_CLK_MUX_EMMC_CQE_2MUX1, "mux_hsp_emmc_cqe_2mux1",
|
||||
mux_mmc_2mux1_p, ARRAY_SIZE(mux_mmc_2mux1_p),
|
||||
CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC0_REG, 0, 1, 0),
|
||||
ESWIN_MUX(EIC7700_HSP_CLK_MUX_SD0_CQE_2MUX1, "mux_hsp_sd0_cqe_2mux1",
|
||||
mux_mmc_2mux1_p, ARRAY_SIZE(mux_mmc_2mux1_p),
|
||||
CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC1_REG, 0, 1, 0),
|
||||
ESWIN_MUX(EIC7700_HSP_CLK_MUX_SD1_CQE_2MUX1, "mux_hsp_sd1_cqe_2mux1",
|
||||
mux_mmc_2mux1_p, ARRAY_SIZE(mux_mmc_2mux1_p),
|
||||
CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC2_REG, 0, 1, 0),
|
||||
};
|
||||
|
||||
static struct eswin_clk_info eic7700_hsp_clks[] = {
|
||||
ESWIN_GATE_TYPE(EIC7700_HSP_CLK_GATE_EMMC, "gate_clk_hsp_emmc",
|
||||
EIC7700_HSP_CLK_MUX_EMMC_3MUX1,
|
||||
CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
|
||||
EIC7700_HSP_MSHC0_REG, 24, 0),
|
||||
ESWIN_GATE_TYPE(EIC7700_HSP_CLK_GATE_SD0, "gate_clk_hsp_sd0",
|
||||
EIC7700_HSP_CLK_MUX_SD0_3MUX1,
|
||||
CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
|
||||
EIC7700_HSP_MSHC1_REG, 24, 0),
|
||||
ESWIN_GATE_TYPE(EIC7700_HSP_CLK_GATE_SD1, "gate_clk_hsp_sd1",
|
||||
EIC7700_HSP_CLK_MUX_SD1_3MUX1,
|
||||
CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
|
||||
EIC7700_HSP_MSHC2_REG, 24, 0),
|
||||
};
|
||||
|
||||
static int eic7700_hsp_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct auxiliary_device *adev;
|
||||
struct eswin_clock_data *data;
|
||||
struct regmap *regmap;
|
||||
struct clk_hw *hw;
|
||||
int i, ret;
|
||||
|
||||
data = eswin_clk_init(pdev, EIC7700_HSP_NR_CLKS);
|
||||
if (IS_ERR(data))
|
||||
return dev_err_probe(dev, PTR_ERR(data),
|
||||
"failed to get clk data!\n");
|
||||
|
||||
regmap = devm_regmap_init_mmio(dev, data->base,
|
||||
&eic7700_hsp_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return dev_err_probe(dev, PTR_ERR(regmap),
|
||||
"failed to get regmap!\n");
|
||||
|
||||
ret = eswin_clk_register_fixed_factor(dev, eic7700_hsp_factor_clks,
|
||||
ARRAY_SIZE(eic7700_hsp_factor_clks),
|
||||
data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to register fixed factor clock\n");
|
||||
|
||||
ret = eswin_clk_register_gate(dev, eic7700_hsp_gate_clks,
|
||||
ARRAY_SIZE(eic7700_hsp_gate_clks), data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to register gate clock\n");
|
||||
|
||||
ret = eswin_clk_register_mux(dev, eic7700_hsp_mux_clks,
|
||||
ARRAY_SIZE(eic7700_hsp_mux_clks),
|
||||
data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to register mux clock\n");
|
||||
|
||||
ret = eswin_clk_register_clks(dev, eic7700_hsp_clks,
|
||||
ARRAY_SIZE(eic7700_hsp_clks), data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to register clock\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eic7700_hsp_spec_gate_clks); i++) {
|
||||
struct eic7700_hsp_clk_gate *gate;
|
||||
|
||||
gate = &eic7700_hsp_spec_gate_clks[i];
|
||||
hw = hsp_clk_register_gate(dev, gate->id, gate->name,
|
||||
gate->parent_data, gate->flags,
|
||||
regmap, gate->offset,
|
||||
gate->ref_offset, gate->bit_idx);
|
||||
if (IS_ERR(hw))
|
||||
return dev_err_probe(dev, PTR_ERR(hw),
|
||||
"failed to register gate clock\n");
|
||||
|
||||
data->clk_data.hws[gate->id] = hw;
|
||||
}
|
||||
|
||||
ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
|
||||
&data->clk_data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "add clk provider failed\n");
|
||||
|
||||
adev = devm_auxiliary_device_create(dev, "hsp-reset", NULL);
|
||||
if (!adev)
|
||||
return dev_err_probe(dev, -ENODEV,
|
||||
"register hsp-reset device failed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id eic7700_hsp_clock_dt_ids[] = {
|
||||
{ .compatible = "eswin,eic7700-hspcrg", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, eic7700_hsp_clock_dt_ids);
|
||||
|
||||
static struct platform_driver eic7700_hsp_clock_driver = {
|
||||
.probe = eic7700_hsp_clk_probe,
|
||||
.driver = {
|
||||
.name = "eic7700-hsp-clock",
|
||||
.of_match_table = eic7700_hsp_clock_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(eic7700_hsp_clock_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Xuyang Dong <dongxuyang@eswincomputing.com>");
|
||||
MODULE_DESCRIPTION("ESWIN EIC7700 HSP clock controller driver");
|
||||
@@ -791,7 +791,8 @@ static struct eswin_clk_info eic7700_clks[] = {
|
||||
EIC7700_CLK_MUX_CPU_ROOT_3MUX1_GFREE,
|
||||
CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_U84, 27, 0),
|
||||
ESWIN_GATE_TYPE(EIC7700_CLK_GATE_NOC_NSP_CLK, "gate_noc_nsp_clk",
|
||||
EIC7700_CLK_DIV_NOC_NSP_DYNM, CLK_SET_RATE_PARENT,
|
||||
EIC7700_CLK_DIV_NOC_NSP_DYNM,
|
||||
CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED,
|
||||
EIC7700_REG_OFFSET_NOC, 31, 0),
|
||||
ESWIN_GATE_TYPE(EIC7700_CLK_GATE_BOOTSPI, "gate_clk_bootspi",
|
||||
EIC7700_CLK_MUX_BOOTSPI_CLK_2MUX1_GFREE,
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config COMMON_CLK_HISI
|
||||
bool
|
||||
default ARCH_HISI
|
||||
|
||||
config COMMON_CLK_HI3516CV300
|
||||
tristate "HI3516CV300 Clock Driver"
|
||||
depends on ARCH_HISI || COMPILE_TEST
|
||||
select RESET_HISI
|
||||
select COMMON_CLK_HISI
|
||||
default ARCH_HISI
|
||||
help
|
||||
Build the clock driver for hi3516cv300.
|
||||
@@ -11,6 +16,7 @@ config COMMON_CLK_HI3519
|
||||
tristate "Hi3519 Clock Driver"
|
||||
depends on ARCH_HISI || COMPILE_TEST
|
||||
select RESET_HISI
|
||||
select COMMON_CLK_HISI
|
||||
default ARCH_HISI
|
||||
help
|
||||
Build the clock driver for hi3519.
|
||||
@@ -19,6 +25,7 @@ config COMMON_CLK_HI3559A
|
||||
bool "Hi3559A Clock Driver"
|
||||
depends on ARCH_HISI || COMPILE_TEST
|
||||
select RESET_HISI
|
||||
select COMMON_CLK_HISI
|
||||
default ARCH_HISI
|
||||
help
|
||||
Build the clock driver for hi3559a.
|
||||
@@ -26,6 +33,7 @@ config COMMON_CLK_HI3559A
|
||||
config COMMON_CLK_HI3660
|
||||
bool "Hi3660 Clock Driver"
|
||||
depends on ARCH_HISI || COMPILE_TEST
|
||||
select COMMON_CLK_HISI
|
||||
default ARCH_HISI
|
||||
help
|
||||
Build the clock driver for hi3660.
|
||||
@@ -33,6 +41,7 @@ config COMMON_CLK_HI3660
|
||||
config COMMON_CLK_HI3670
|
||||
bool "Hi3670 Clock Driver"
|
||||
depends on ARCH_HISI || COMPILE_TEST
|
||||
select COMMON_CLK_HISI
|
||||
default ARCH_HISI
|
||||
help
|
||||
Build the clock driver for hi3670.
|
||||
@@ -41,6 +50,7 @@ config COMMON_CLK_HI3798CV200
|
||||
tristate "Hi3798CV200 Clock Driver"
|
||||
depends on ARCH_HISI || COMPILE_TEST
|
||||
select RESET_HISI
|
||||
select COMMON_CLK_HISI
|
||||
default ARCH_HISI
|
||||
help
|
||||
Build the clock driver for hi3798cv200.
|
||||
@@ -48,6 +58,7 @@ config COMMON_CLK_HI3798CV200
|
||||
config COMMON_CLK_HI6220
|
||||
bool "Hi6220 Clock Driver"
|
||||
depends on ARCH_HISI || COMPILE_TEST
|
||||
select COMMON_CLK_HISI
|
||||
default ARCH_HISI
|
||||
help
|
||||
Build the Hisilicon Hi6220 clock driver based on the common clock framework.
|
||||
@@ -55,6 +66,7 @@ config COMMON_CLK_HI6220
|
||||
config RESET_HISI
|
||||
bool "HiSilicon Reset Controller Driver"
|
||||
depends on ARCH_HISI || COMPILE_TEST
|
||||
select COMMON_CLK_HISI
|
||||
select RESET_CONTROLLER
|
||||
help
|
||||
Build reset controller driver for HiSilicon device chipsets.
|
||||
@@ -63,6 +75,7 @@ config STUB_CLK_HI6220
|
||||
bool "Hi6220 Stub Clock Driver" if EXPERT
|
||||
depends on (COMMON_CLK_HI6220 || COMPILE_TEST)
|
||||
depends on MAILBOX
|
||||
select COMMON_CLK_HISI
|
||||
default COMMON_CLK_HI6220
|
||||
help
|
||||
Build the Hisilicon Hi6220 stub clock driver.
|
||||
@@ -71,6 +84,7 @@ config STUB_CLK_HI3660
|
||||
bool "Hi3660 Stub Clock Driver" if EXPERT
|
||||
depends on (COMMON_CLK_HI3660 || COMPILE_TEST)
|
||||
depends on MAILBOX
|
||||
select COMMON_CLK_HISI
|
||||
default COMMON_CLK_HI3660
|
||||
help
|
||||
Build the Hisilicon Hi3660 stub clock driver.
|
||||
|
||||
@@ -67,16 +67,6 @@ static unsigned long hi3660_stub_clk_recalc_rate(struct clk_hw *hw,
|
||||
return stub_clk->rate;
|
||||
}
|
||||
|
||||
static int hi3660_stub_clk_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
/*
|
||||
* LPM3 handles rate rounding so just return whatever
|
||||
* rate is requested.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hi3660_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
@@ -97,7 +87,7 @@ static int hi3660_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
|
||||
static const struct clk_ops hi3660_stub_clk_ops = {
|
||||
.recalc_rate = hi3660_stub_clk_recalc_rate,
|
||||
.determine_rate = hi3660_stub_clk_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
.set_rate = hi3660_stub_clk_set_rate,
|
||||
};
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev)
|
||||
{
|
||||
struct hisi_reset_controller *rstc;
|
||||
|
||||
rstc = devm_kmalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL);
|
||||
rstc = devm_kzalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL);
|
||||
if (!rstc)
|
||||
return NULL;
|
||||
|
||||
|
||||
@@ -262,23 +262,6 @@ static unsigned long clk_scu_recalc_rate(struct clk_hw *hw,
|
||||
return le32_to_cpu(msg.data.resp.rate);
|
||||
}
|
||||
|
||||
/*
|
||||
* clk_scu_determine_rate - Returns the closest rate for a SCU clock
|
||||
* @hw: clock to round rate for
|
||||
* @req: clock rate request
|
||||
*
|
||||
* Returns 0 on success, a negative error on failure
|
||||
*/
|
||||
static int clk_scu_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
/*
|
||||
* Assume we support all the requested rate and let the SCU firmware
|
||||
* to handle the left work
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
@@ -436,7 +419,7 @@ static void clk_scu_unprepare(struct clk_hw *hw)
|
||||
|
||||
static const struct clk_ops clk_scu_ops = {
|
||||
.recalc_rate = clk_scu_recalc_rate,
|
||||
.determine_rate = clk_scu_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
.set_rate = clk_scu_set_rate,
|
||||
.get_parent = clk_scu_get_parent,
|
||||
.set_parent = clk_scu_set_parent,
|
||||
@@ -446,7 +429,7 @@ static const struct clk_ops clk_scu_ops = {
|
||||
|
||||
static const struct clk_ops clk_scu_cpu_ops = {
|
||||
.recalc_rate = clk_scu_recalc_rate,
|
||||
.determine_rate = clk_scu_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
.set_rate = clk_scu_atf_set_cpu_rate,
|
||||
.prepare = clk_scu_prepare,
|
||||
.unprepare = clk_scu_unprepare,
|
||||
@@ -454,7 +437,7 @@ static const struct clk_ops clk_scu_cpu_ops = {
|
||||
|
||||
static const struct clk_ops clk_scu_pi_ops = {
|
||||
.recalc_rate = clk_scu_recalc_rate,
|
||||
.determine_rate = clk_scu_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
.set_rate = clk_scu_set_rate,
|
||||
};
|
||||
|
||||
@@ -475,7 +458,6 @@ struct clk_hw *__imx_clk_scu(struct device *dev, const char *name,
|
||||
clk->clk_type = clk_type;
|
||||
|
||||
init.name = name;
|
||||
init.ops = &clk_scu_ops;
|
||||
if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 || rsrc_id == IMX_SC_R_A72)
|
||||
init.ops = &clk_scu_cpu_ops;
|
||||
else if (rsrc_id == IMX_SC_R_PI_0_PLL)
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#include <dt-bindings/clock/clock.h>
|
||||
|
||||
#ifndef _KUNIT_CLK_ASSIGNED_RATES_H
|
||||
#define _KUNIT_CLK_ASSIGNED_RATES_H
|
||||
|
||||
#define ASSIGNED_RATES_0_RATE 1600000
|
||||
#define ASSIGNED_RATES_1_RATE 9700000
|
||||
|
||||
#define ASSIGNED_SSCS_0_MODFREQ 10000
|
||||
#define ASSIGNED_SSCS_0_SPREAD 30000
|
||||
#define ASSIGNED_SSCS_0_METHOD CLK_SSC_CENTER_SPREAD
|
||||
#define ASSIGNED_SSCS_1_MODFREQ 20000
|
||||
#define ASSIGNED_SSCS_1_SPREAD 40000
|
||||
#define ASSIGNED_SSCS_1_METHOD CLK_SSC_UP_SPREAD
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,5 +12,11 @@
|
||||
<&clk 1>;
|
||||
assigned-clock-rates-u64 = /bits/ 64 <ASSIGNED_RATES_0_RATE>,
|
||||
/bits/ 64 <ASSIGNED_RATES_1_RATE>;
|
||||
assigned-clock-sscs = <ASSIGNED_SSCS_0_MODFREQ
|
||||
ASSIGNED_SSCS_0_SPREAD
|
||||
ASSIGNED_SSCS_0_METHOD>,
|
||||
<ASSIGNED_SSCS_1_MODFREQ
|
||||
ASSIGNED_SSCS_1_SPREAD
|
||||
ASSIGNED_SSCS_1_METHOD>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -16,5 +16,11 @@
|
||||
<&clk 1>;
|
||||
assigned-clock-rates-u64 = /bits/ 64 <ASSIGNED_RATES_0_RATE>,
|
||||
/bits/ 64 <ASSIGNED_RATES_1_RATE>;
|
||||
assigned-clock-sscs = <ASSIGNED_SSCS_0_MODFREQ
|
||||
ASSIGNED_SSCS_0_SPREAD
|
||||
ASSIGNED_SSCS_0_METHOD>,
|
||||
<ASSIGNED_SSCS_1_MODFREQ
|
||||
ASSIGNED_SSCS_1_SPREAD
|
||||
ASSIGNED_SSCS_1_METHOD>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -10,5 +10,8 @@
|
||||
#clock-cells = <0>;
|
||||
assigned-clocks = <&clk>;
|
||||
assigned-clock-rates-u64 = /bits/ 64 <ASSIGNED_RATES_0_RATE>;
|
||||
assigned-clock-sscs = <ASSIGNED_SSCS_0_MODFREQ
|
||||
ASSIGNED_SSCS_0_SPREAD
|
||||
ASSIGNED_SSCS_0_METHOD>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -14,5 +14,8 @@
|
||||
compatible = "test,clk-consumer";
|
||||
assigned-clocks = <&clk>;
|
||||
assigned-clock-rates-u64 = /bits/ 64 <ASSIGNED_RATES_0_RATE>;
|
||||
assigned-clock-sscs = <ASSIGNED_SSCS_0_MODFREQ
|
||||
ASSIGNED_SSCS_0_SPREAD
|
||||
ASSIGNED_SSCS_0_METHOD>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "kunit_clk_assigned_rates.h"
|
||||
|
||||
&{/} {
|
||||
clk: kunit-clock {
|
||||
compatible = "test,clk-assigned-rates";
|
||||
#clock-cells = <1>;
|
||||
assigned-clocks = <&clk 0>,
|
||||
<&clk 1>;
|
||||
assigned-clock-sscs = <ASSIGNED_SSCS_0_MODFREQ
|
||||
ASSIGNED_SSCS_0_SPREAD
|
||||
ASSIGNED_SSCS_0_METHOD>,
|
||||
<ASSIGNED_SSCS_1_MODFREQ
|
||||
ASSIGNED_SSCS_1_SPREAD
|
||||
ASSIGNED_SSCS_1_METHOD>;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "kunit_clk_assigned_rates.h"
|
||||
|
||||
&{/} {
|
||||
clk: kunit-clock {
|
||||
compatible = "test,clk-assigned-rates";
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
|
||||
kunit-clock-consumer {
|
||||
compatible = "test,clk-consumer";
|
||||
assigned-clocks = <&clk 0>,
|
||||
<&clk 1>;
|
||||
assigned-clock-sscs = <ASSIGNED_SSCS_0_MODFREQ
|
||||
ASSIGNED_SSCS_0_SPREAD
|
||||
ASSIGNED_SSCS_0_METHOD>,
|
||||
<ASSIGNED_SSCS_1_MODFREQ
|
||||
ASSIGNED_SSCS_1_SPREAD
|
||||
ASSIGNED_SSCS_1_METHOD>;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "kunit_clk_assigned_rates.h"
|
||||
|
||||
&{/} {
|
||||
clk: kunit-clock {
|
||||
compatible = "test,clk-assigned-rates";
|
||||
#clock-cells = <0>;
|
||||
assigned-clocks = <0>;
|
||||
assigned-clock-sscs = <ASSIGNED_SSCS_0_MODFREQ
|
||||
ASSIGNED_SSCS_0_SPREAD
|
||||
ASSIGNED_SSCS_0_METHOD>;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "kunit_clk_assigned_rates.h"
|
||||
|
||||
&{/} {
|
||||
clk: kunit-clock {
|
||||
compatible = "test,clk-assigned-rates";
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
kunit-clock-consumer {
|
||||
compatible = "test,clk-consumer";
|
||||
assigned-clocks = <0>;
|
||||
assigned-clock-sscs = <ASSIGNED_SSCS_0_MODFREQ
|
||||
ASSIGNED_SSCS_0_SPREAD
|
||||
ASSIGNED_SSCS_0_METHOD>;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "kunit_clk_assigned_rates.h"
|
||||
|
||||
&{/} {
|
||||
clk: kunit-clock {
|
||||
compatible = "test,clk-assigned-rates";
|
||||
#clock-cells = <0>;
|
||||
assigned-clocks = <&clk>;
|
||||
assigned-clock-sscs = <ASSIGNED_SSCS_0_MODFREQ
|
||||
ASSIGNED_SSCS_0_SPREAD
|
||||
ASSIGNED_SSCS_0_METHOD>;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "kunit_clk_assigned_rates.h"
|
||||
|
||||
&{/} {
|
||||
clk: kunit-clock {
|
||||
compatible = "test,clk-assigned-rates";
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
kunit-clock-consumer {
|
||||
compatible = "test,clk-consumer";
|
||||
assigned-clocks = <&clk>;
|
||||
assigned-clock-sscs = <ASSIGNED_SSCS_0_MODFREQ
|
||||
ASSIGNED_SSCS_0_SPREAD
|
||||
ASSIGNED_SSCS_0_METHOD>;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "kunit_clk_assigned_rates.h"
|
||||
|
||||
&{/} {
|
||||
clk: kunit-clock {
|
||||
compatible = "test,clk-assigned-rates";
|
||||
#clock-cells = <0>;
|
||||
assigned-clock-sscs = <ASSIGNED_SSCS_0_MODFREQ
|
||||
ASSIGNED_SSCS_0_SPREAD
|
||||
ASSIGNED_SSCS_0_METHOD>;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
#include "kunit_clk_assigned_rates.h"
|
||||
|
||||
&{/} {
|
||||
clk: kunit-clock {
|
||||
compatible = "test,clk-assigned-rates";
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
kunit-clock-consumer {
|
||||
compatible = "test,clk-consumer";
|
||||
assigned-clock-sscs = <ASSIGNED_SSCS_0_MODFREQ
|
||||
ASSIGNED_SSCS_0_SPREAD
|
||||
ASSIGNED_SSCS_0_METHOD>;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
&{/} {
|
||||
clk: kunit-clock {
|
||||
compatible = "test,clk-assigned-rates";
|
||||
#clock-cells = <0>;
|
||||
assigned-clocks = <&clk>;
|
||||
assigned-clock-sscs = <0 0 0>;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/dts-v1/;
|
||||
/plugin/;
|
||||
|
||||
&{/} {
|
||||
clk: kunit-clock {
|
||||
compatible = "test,clk-assigned-rates";
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
kunit-clock-consumer {
|
||||
compatible = "test,clk-consumer";
|
||||
assigned-clocks = <&clk>;
|
||||
assigned-clock-sscs = <0 0 0>;
|
||||
};
|
||||
};
|
||||
@@ -1006,6 +1006,7 @@ config COMMON_CLK_MT8196
|
||||
tristate "Clock driver for MediaTek MT8196"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
select COMMON_CLK_MEDIATEK
|
||||
select REGMAP_MMIO
|
||||
default ARCH_MEDIATEK
|
||||
help
|
||||
This driver supports MediaTek MT8196 basic clocks.
|
||||
|
||||
@@ -253,8 +253,8 @@ struct clk_muxing_soc_desc {
|
||||
|
||||
struct clk_muxing_ctrl {
|
||||
spinlock_t *lock;
|
||||
struct clk **muxes;
|
||||
int num_muxes;
|
||||
struct clk *muxes[] __counted_by(num_muxes);
|
||||
};
|
||||
|
||||
static const char *powersave_parents[] = {
|
||||
@@ -297,21 +297,18 @@ static void __init kirkwood_clk_muxing_setup(struct device_node *np,
|
||||
if (WARN_ON(!base))
|
||||
return;
|
||||
|
||||
ctrl = kzalloc_obj(*ctrl);
|
||||
if (WARN_ON(!ctrl))
|
||||
goto ctrl_out;
|
||||
|
||||
/* lock must already be initialized */
|
||||
ctrl->lock = &ctrl_gating_lock;
|
||||
|
||||
/* Count, allocate, and register clock muxes */
|
||||
for (n = 0; desc[n].name;)
|
||||
n++;
|
||||
|
||||
ctrl = kzalloc_flex(*ctrl, muxes, n);
|
||||
if (WARN_ON(!ctrl))
|
||||
goto ctrl_out;
|
||||
|
||||
ctrl->num_muxes = n;
|
||||
ctrl->muxes = kzalloc_objs(struct clk *, ctrl->num_muxes);
|
||||
if (WARN_ON(!ctrl->muxes))
|
||||
goto muxes_out;
|
||||
|
||||
/* lock must already be initialized */
|
||||
ctrl->lock = &ctrl_gating_lock;
|
||||
|
||||
for (n = 0; n < ctrl->num_muxes; n++) {
|
||||
ctrl->muxes[n] = clk_register_mux(NULL, desc[n].name,
|
||||
@@ -324,8 +321,6 @@ static void __init kirkwood_clk_muxing_setup(struct device_node *np,
|
||||
of_clk_add_provider(np, clk_muxing_get_src, ctrl);
|
||||
|
||||
return;
|
||||
muxes_out:
|
||||
kfree(ctrl);
|
||||
ctrl_out:
|
||||
iounmap(base);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
#define PLL_CTL1_PD BIT(0)
|
||||
#define PLL_CTL1_BP BIT(1)
|
||||
#define PLL_CTL1_OUTDIV GENMASK(6, 4)
|
||||
#define PLL_CTL1_FRAC GENMASK(31, 24)
|
||||
#define PLL_CTL1_FRAC GENMASK(31, 8)
|
||||
#define PLL_CTL2_SLOPE GENMASK(23, 0)
|
||||
|
||||
#define INDIV_MIN 1
|
||||
@@ -92,7 +92,7 @@ static unsigned long ma35d1_calc_smic_pll_freq(u32 pll0_ctl0,
|
||||
p = FIELD_GET(SPLL0_CTL0_OUTDIV, pll0_ctl0);
|
||||
outdiv = 1 << p;
|
||||
pll_freq = (u64)parent_rate * n;
|
||||
div_u64(pll_freq, m * outdiv);
|
||||
pll_freq = div_u64(pll_freq, m * outdiv);
|
||||
return pll_freq;
|
||||
}
|
||||
|
||||
@@ -110,12 +110,12 @@ static unsigned long ma35d1_calc_pll_freq(u8 mode, u32 *reg_ctl, unsigned long p
|
||||
|
||||
if (mode == PLL_MODE_INT) {
|
||||
pll_freq = (u64)parent_rate * n;
|
||||
div_u64(pll_freq, m * p);
|
||||
pll_freq = div_u64(pll_freq, m * p);
|
||||
} else {
|
||||
x = FIELD_GET(PLL_CTL1_FRAC, reg_ctl[1]);
|
||||
/* 2 decimal places floating to integer (ex. 1.23 to 123) */
|
||||
n = n * 100 + ((x * 100) / FIELD_MAX(PLL_CTL1_FRAC));
|
||||
pll_freq = div_u64(parent_rate * n, 100 * m * p);
|
||||
/* convert 24-bit fraction to 3 decimal digits, rounding to closest */
|
||||
n = n * 1000 + DIV_ROUND_CLOSEST_ULL((u64)x * 1000, 1ULL << 24);
|
||||
pll_freq = div_u64((u64)parent_rate * n, 1000 * m * p);
|
||||
}
|
||||
return pll_freq;
|
||||
}
|
||||
@@ -255,32 +255,32 @@ static int ma35d1_clk_pll_determine_rate(struct clk_hw *hw,
|
||||
if (req->best_parent_rate < PLL_FREF_MIN_FREQ || req->best_parent_rate > PLL_FREF_MAX_FREQ)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ma35d1_pll_find_closest(pll, req->rate, req->best_parent_rate,
|
||||
reg_ctl, &pll_freq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (pll->id) {
|
||||
case CAPLL:
|
||||
reg_ctl[0] = readl_relaxed(pll->ctl0_base);
|
||||
pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], req->best_parent_rate);
|
||||
req->rate = pll_freq;
|
||||
|
||||
return 0;
|
||||
case DDRPLL:
|
||||
/* Read-only PLLs: return current rate */
|
||||
reg_ctl[0] = readl_relaxed(pll->ctl0_base);
|
||||
if (pll->id == CAPLL) {
|
||||
pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], req->best_parent_rate);
|
||||
} else {
|
||||
reg_ctl[1] = readl_relaxed(pll->ctl1_base);
|
||||
pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, req->best_parent_rate);
|
||||
}
|
||||
req->rate = pll_freq;
|
||||
return 0;
|
||||
case APLL:
|
||||
case EPLL:
|
||||
case VPLL:
|
||||
reg_ctl[0] = readl_relaxed(pll->ctl0_base);
|
||||
reg_ctl[1] = readl_relaxed(pll->ctl1_base);
|
||||
pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, req->best_parent_rate);
|
||||
/* Configurable PLLs: find closest achievable rate */
|
||||
ret = ma35d1_pll_find_closest(pll, req->rate, req->best_parent_rate,
|
||||
reg_ctl, &pll_freq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
req->rate = pll_freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
req->rate = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -351,17 +351,6 @@ static int clk_rpm_set_rate(struct clk_hw *hw,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_rpm_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
/*
|
||||
* RPM handles rate rounding and we don't have a way to
|
||||
* know what the rate will be, so just return whatever
|
||||
* rate is requested.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
@@ -383,7 +372,7 @@ static const struct clk_ops clk_rpm_xo_ops = {
|
||||
static const struct clk_ops clk_rpm_fixed_ops = {
|
||||
.prepare = clk_rpm_fixed_prepare,
|
||||
.unprepare = clk_rpm_fixed_unprepare,
|
||||
.determine_rate = clk_rpm_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
.recalc_rate = clk_rpm_recalc_rate,
|
||||
};
|
||||
|
||||
@@ -391,7 +380,7 @@ static const struct clk_ops clk_rpm_ops = {
|
||||
.prepare = clk_rpm_prepare,
|
||||
.unprepare = clk_rpm_unprepare,
|
||||
.set_rate = clk_rpm_set_rate,
|
||||
.determine_rate = clk_rpm_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
.recalc_rate = clk_rpm_recalc_rate,
|
||||
};
|
||||
|
||||
|
||||
@@ -319,12 +319,6 @@ static int clk_rpmh_bcm_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_rpmh_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long clk_rpmh_bcm_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long prate)
|
||||
{
|
||||
@@ -337,7 +331,7 @@ static const struct clk_ops clk_rpmh_bcm_ops = {
|
||||
.prepare = clk_rpmh_bcm_prepare,
|
||||
.unprepare = clk_rpmh_bcm_unprepare,
|
||||
.set_rate = clk_rpmh_bcm_set_rate,
|
||||
.determine_rate = clk_rpmh_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
.recalc_rate = clk_rpmh_bcm_recalc_rate,
|
||||
};
|
||||
|
||||
|
||||
@@ -370,17 +370,6 @@ static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_smd_rpm_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
/*
|
||||
* RPM handles rate rounding and we don't have a way to
|
||||
* know what the rate will be, so just return whatever
|
||||
* rate is requested.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
@@ -427,7 +416,7 @@ static const struct clk_ops clk_smd_rpm_ops = {
|
||||
.prepare = clk_smd_rpm_prepare,
|
||||
.unprepare = clk_smd_rpm_unprepare,
|
||||
.set_rate = clk_smd_rpm_set_rate,
|
||||
.determine_rate = clk_smd_rpm_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
.recalc_rate = clk_smd_rpm_recalc_rate,
|
||||
};
|
||||
|
||||
|
||||
@@ -944,12 +944,6 @@ static unsigned long rzg2l_cpg_sipll5_recalc_rate(struct clk_hw *hw,
|
||||
return pll5_rate;
|
||||
}
|
||||
|
||||
static int rzg2l_cpg_sipll5_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
@@ -1021,7 +1015,7 @@ static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw,
|
||||
|
||||
static const struct clk_ops rzg2l_cpg_sipll5_ops = {
|
||||
.recalc_rate = rzg2l_cpg_sipll5_recalc_rate,
|
||||
.determine_rate = rzg2l_cpg_sipll5_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
.set_rate = rzg2l_cpg_sipll5_set_rate,
|
||||
};
|
||||
|
||||
|
||||
@@ -72,18 +72,6 @@ static unsigned long acpm_clk_recalc_rate(struct clk_hw *hw,
|
||||
clk->id);
|
||||
}
|
||||
|
||||
static int acpm_clk_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
/*
|
||||
* We can't figure out what rate it will be, so just return the
|
||||
* rate back to the caller. acpm_clk_recalc_rate() will be called
|
||||
* after the rate is set and we'll know what rate the clock is
|
||||
* running at then.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acpm_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
@@ -95,7 +83,7 @@ static int acpm_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
|
||||
static const struct clk_ops acpm_clk_ops = {
|
||||
.recalc_rate = acpm_clk_recalc_rate,
|
||||
.determine_rate = acpm_clk_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
.set_rate = acpm_clk_set_rate,
|
||||
};
|
||||
|
||||
@@ -166,8 +154,8 @@ static int acpm_clk_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct platform_device_id acpm_clk_id[] = {
|
||||
{ "gs101-acpm-clk" },
|
||||
{}
|
||||
{ .name = "gs101-acpm-clk" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, acpm_clk_id);
|
||||
|
||||
|
||||
@@ -254,16 +254,10 @@ static int sprd_pll_clk_prepare(struct clk_hw *hw)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_pll_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops sprd_pll_ops = {
|
||||
.prepare = sprd_pll_clk_prepare,
|
||||
.recalc_rate = sprd_pll_recalc_rate,
|
||||
.determine_rate = sprd_pll_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
.set_rate = sprd_pll_set_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(sprd_pll_ops);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* Author: Gabriel Fernandez <gabriel.fernandez@foss.st.com> for STMicroelectronics.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bus/stm32_firewall_device.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* Author: Gabriel Fernandez <gabriel.fernandez@foss.st.com> for STMicroelectronics.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bus/stm32_firewall_device.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
@@ -367,7 +367,15 @@ static int tegra_bpmp_clk_get_info(struct tegra_bpmp *bpmp, unsigned int id,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
strscpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN);
|
||||
if (dev_to_node(bpmp->dev) == NUMA_NO_NODE) {
|
||||
strscpy(info->name, response.name, sizeof(info->name));
|
||||
} else {
|
||||
err = snprintf(info->name, sizeof(info->name), "%d-%s",
|
||||
dev_to_node(bpmp->dev), response.name);
|
||||
if (WARN_ON(err >= sizeof(info->name)))
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
info->num_parents = response.num_parents;
|
||||
|
||||
for (i = 0; i < info->num_parents; i++)
|
||||
|
||||
@@ -663,8 +663,8 @@ static int clk_wzrd_determine_rate_all(struct clk_hw *hw,
|
||||
d = divider->d;
|
||||
o = divider->o;
|
||||
|
||||
req->rate = div_u64(req->best_parent_rate * (m * 1000 + divider->m_frac),
|
||||
d * (o * 1000 + divider->o_frac));
|
||||
req->rate = mult_frac(req->best_parent_rate, m * 1000 + divider->m_frac,
|
||||
d * (o * 1000 + divider->o_frac));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,11 @@ static void __init zynq_clk_register_periph_clk(enum zynq_clk clk0,
|
||||
spin_lock_init(lock);
|
||||
|
||||
mux_name = kasprintf(GFP_KERNEL, "%s_mux", clk_name0);
|
||||
if (!mux_name)
|
||||
goto err_mux_name;
|
||||
div_name = kasprintf(GFP_KERNEL, "%s_div", clk_name0);
|
||||
if (!div_name)
|
||||
goto err_div_name;
|
||||
|
||||
clk_register_mux(NULL, mux_name, parents, 4,
|
||||
CLK_SET_RATE_NO_REPARENT, clk_ctrl, 4, 2, 0, lock);
|
||||
@@ -205,6 +209,10 @@ static void __init zynq_clk_register_periph_clk(enum zynq_clk clk0,
|
||||
|
||||
return;
|
||||
|
||||
err_div_name:
|
||||
kfree(mux_name);
|
||||
err_mux_name:
|
||||
kfree(lock);
|
||||
err:
|
||||
clks[clk0] = ERR_PTR(-ENOMEM);
|
||||
if (two_gates)
|
||||
|
||||
@@ -90,12 +90,6 @@ static void mtk_hdmi_pll_unprepare(struct clk_hw *hw)
|
||||
usleep_range(80, 100);
|
||||
}
|
||||
|
||||
static int mtk_hdmi_pll_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
@@ -170,7 +164,7 @@ static const struct clk_ops mtk_hdmi_phy_pll_ops = {
|
||||
.prepare = mtk_hdmi_pll_prepare,
|
||||
.unprepare = mtk_hdmi_pll_unprepare,
|
||||
.set_rate = mtk_hdmi_pll_set_rate,
|
||||
.determine_rate = mtk_hdmi_pll_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
.recalc_rate = mtk_hdmi_pll_recalc_rate,
|
||||
};
|
||||
|
||||
|
||||
@@ -21,12 +21,6 @@ struct airoha_cpu_pmdomain_priv {
|
||||
struct generic_pm_domain pd;
|
||||
};
|
||||
|
||||
static int airoha_cpu_pmdomain_clk_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long airoha_cpu_pmdomain_clk_get(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
@@ -48,7 +42,7 @@ static int airoha_cpu_pmdomain_clk_is_enabled(struct clk_hw *hw)
|
||||
static const struct clk_ops airoha_cpu_pmdomain_clk_ops = {
|
||||
.recalc_rate = airoha_cpu_pmdomain_clk_get,
|
||||
.is_enabled = airoha_cpu_pmdomain_clk_is_enabled,
|
||||
.determine_rate = airoha_cpu_pmdomain_clk_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
};
|
||||
|
||||
static int airoha_cpu_pmdomain_set_performance_state(struct generic_pm_domain *domain,
|
||||
|
||||
@@ -310,25 +310,6 @@ static unsigned long mtk_mfg_recalc_rate_gpu(struct clk_hw *hw,
|
||||
return readl(mfg->shared_mem + GF_REG_FREQ_OUT_GPU) * HZ_PER_KHZ;
|
||||
}
|
||||
|
||||
static int mtk_mfg_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
/*
|
||||
* The determine_rate callback needs to be implemented to avoid returning
|
||||
* the current clock frequency, rather than something even remotely
|
||||
* close to the frequency that was asked for.
|
||||
*
|
||||
* Instead of writing considerable amounts of possibly slow code just to
|
||||
* somehow figure out which of the three PLLs to round for, or even to
|
||||
* do a search through one of two OPP tables in order to find the closest
|
||||
* OPP of a frequency, just return the rate as-is. This avoids devfreq
|
||||
* "rounding" a request for the lowest frequency to the possibly very
|
||||
* high current frequency, breaking the powersave governor in the process.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long mtk_mfg_recalc_rate_stack(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
@@ -339,12 +320,12 @@ static unsigned long mtk_mfg_recalc_rate_stack(struct clk_hw *hw,
|
||||
|
||||
static const struct clk_ops mtk_mfg_clk_gpu_ops = {
|
||||
.recalc_rate = mtk_mfg_recalc_rate_gpu,
|
||||
.determine_rate = mtk_mfg_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
};
|
||||
|
||||
static const struct clk_ops mtk_mfg_clk_stack_ops = {
|
||||
.recalc_rate = mtk_mfg_recalc_rate_stack,
|
||||
.determine_rate = mtk_mfg_determine_rate,
|
||||
.determine_rate = clk_determine_rate_noop,
|
||||
};
|
||||
|
||||
static const struct clk_init_data mtk_mfg_clk_gpu_init = {
|
||||
|
||||
@@ -83,6 +83,17 @@ config RESET_EIC7700
|
||||
The driver supports eic7700 series chips and provides functionality for
|
||||
asserting and deasserting resets on the chip.
|
||||
|
||||
config RESET_EIC7700_HSP
|
||||
tristate "EIC7700 HSP Reset controller"
|
||||
depends on ARCH_ESWIN || COMPILE_TEST
|
||||
select AUXILIARY_BUS
|
||||
help
|
||||
This enables the HSP reset controller driver for ESWIN SoCs. This
|
||||
driver is specific to ESWIN SoCs and should only be enabled if using
|
||||
such hardware.
|
||||
The driver supports EIC7700 series chips and provides functionality
|
||||
for asserting and deasserting resets on the chip.
|
||||
|
||||
config RESET_EYEQ
|
||||
bool "Mobileye EyeQ reset controller"
|
||||
depends on EYEQ || COMPILE_TEST
|
||||
|
||||
@@ -15,6 +15,7 @@ obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o
|
||||
obj-$(CONFIG_RESET_BRCMSTB) += reset-brcmstb.o
|
||||
obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o
|
||||
obj-$(CONFIG_RESET_EIC7700) += reset-eic7700.o
|
||||
obj-$(CONFIG_RESET_EIC7700_HSP) += reset-eic7700-hsp.o
|
||||
obj-$(CONFIG_RESET_EYEQ) += reset-eyeq.o
|
||||
obj-$(CONFIG_RESET_GPIO) += reset-gpio.o
|
||||
obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd..
|
||||
* All rights reserved.
|
||||
*
|
||||
* ESWIN EIC7700 HSP Reset Driver
|
||||
*
|
||||
* Authors: Xuyang Dong <dongxuyang@eswincomputing.com>
|
||||
*/
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
#include <dt-bindings/reset/eswin,eic7700-hspcrg.h>
|
||||
|
||||
/**
|
||||
* struct eic7700_hsp_reset_data - reset controller information structure
|
||||
* @rcdev: reset controller entity
|
||||
* @regmap: regmap handle containing the memory-mapped reset registers
|
||||
*/
|
||||
struct eic7700_hsp_reset_data {
|
||||
struct reset_controller_dev rcdev;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
struct eic7700_hsp_reg {
|
||||
u32 reg;
|
||||
u32 bit;
|
||||
bool active_low;
|
||||
};
|
||||
|
||||
static inline struct eic7700_hsp_reset_data *
|
||||
to_eic7700_hsp_reset(struct reset_controller_dev *rcdev)
|
||||
{
|
||||
return container_of(rcdev, struct eic7700_hsp_reset_data, rcdev);
|
||||
}
|
||||
|
||||
static const struct eic7700_hsp_reg eic7700_hsp_reset[] = {
|
||||
[EIC7700_HSP_RST_SATA_P0] = {0x340, BIT(0), false},
|
||||
[EIC7700_HSP_RST_SATA_PHY] = {0x340, BIT(1), false},
|
||||
[EIC7700_HSP_RST_USB0] = {0x800, BIT(24), true},
|
||||
[EIC7700_HSP_RST_USB1] = {0x900, BIT(24), true},
|
||||
[EIC7700_HSP_RST_USB0_PHY] = {0x800, BIT(25), false},
|
||||
[EIC7700_HSP_RST_USB1_PHY] = {0x900, BIT(25), false},
|
||||
};
|
||||
|
||||
static int eic7700_hsp_reset_assert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct eic7700_hsp_reset_data *data = to_eic7700_hsp_reset(rcdev);
|
||||
|
||||
return regmap_assign_bits(data->regmap, eic7700_hsp_reset[id].reg,
|
||||
eic7700_hsp_reset[id].bit,
|
||||
!eic7700_hsp_reset[id].active_low);
|
||||
}
|
||||
|
||||
static int eic7700_hsp_reset_deassert(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
struct eic7700_hsp_reset_data *data = to_eic7700_hsp_reset(rcdev);
|
||||
|
||||
return regmap_assign_bits(data->regmap, eic7700_hsp_reset[id].reg,
|
||||
eic7700_hsp_reset[id].bit,
|
||||
eic7700_hsp_reset[id].active_low);
|
||||
}
|
||||
|
||||
static const struct reset_control_ops eic7700_hsp_reset_ops = {
|
||||
.assert = eic7700_hsp_reset_assert,
|
||||
.deassert = eic7700_hsp_reset_deassert,
|
||||
};
|
||||
|
||||
static int eic7700_hsp_reset_probe(struct auxiliary_device *adev,
|
||||
const struct auxiliary_device_id *id)
|
||||
{
|
||||
struct eic7700_hsp_reset_data *data;
|
||||
struct device *dev = &adev->dev;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!data->regmap)
|
||||
return dev_err_probe(dev, -ENODEV, "failed to get regmap!\n");
|
||||
|
||||
data->rcdev.owner = THIS_MODULE;
|
||||
data->rcdev.ops = &eic7700_hsp_reset_ops;
|
||||
data->rcdev.of_node = dev->parent->of_node;
|
||||
data->rcdev.dev = dev;
|
||||
data->rcdev.nr_resets = ARRAY_SIZE(eic7700_hsp_reset);
|
||||
|
||||
return devm_reset_controller_register(dev, &data->rcdev);
|
||||
}
|
||||
|
||||
static const struct auxiliary_device_id eic7700_hsp_reset_ids[] = {
|
||||
{ .name = "clk_eic7700_hsp.hsp-reset", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, eic7700_hsp_reset_ids);
|
||||
|
||||
static struct auxiliary_driver eic7700_hsp_reset_driver = {
|
||||
.probe = eic7700_hsp_reset_probe,
|
||||
.id_table = eic7700_hsp_reset_ids,
|
||||
};
|
||||
|
||||
module_auxiliary_driver(eic7700_hsp_reset_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Xuyang Dong <dongxuyang@eswincomputing.com>");
|
||||
MODULE_DESCRIPTION("ESWIN EIC7700 HSP Reset Controller Driver");
|
||||
@@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
|
||||
/*
|
||||
* Copyright 2025 NXP
|
||||
*/
|
||||
|
||||
#ifndef __DT_BINDINGS_CLOCK_H
|
||||
#define __DT_BINDINGS_CLOCK_H
|
||||
|
||||
#define CLK_SSC_NO_SPREAD 0
|
||||
#define CLK_SSC_CENTER_SPREAD 1
|
||||
#define CLK_SSC_UP_SPREAD 2
|
||||
#define CLK_SSC_DOWN_SPREAD 3
|
||||
|
||||
#endif /* __DT_BINDINGS_CLOCK_H */
|
||||
@@ -0,0 +1,33 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
/*
|
||||
* Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd..
|
||||
* All rights reserved.
|
||||
*
|
||||
* Device Tree binding constants for EIC7700 HSP clock controller.
|
||||
*
|
||||
* Authors: Xuyang Dong <dongxuyang@eswincomputing.com>
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_CLOCK_H_
|
||||
#define _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_CLOCK_H_
|
||||
|
||||
#define EIC7700_HSP_CLK_FAC_CFG_DIV2 0
|
||||
#define EIC7700_HSP_CLK_FAC_CFG_DIV4 1
|
||||
#define EIC7700_HSP_CLK_FAC_MMC_DIV10 2
|
||||
#define EIC7700_HSP_CLK_MUX_EMMC_3MUX1 3
|
||||
#define EIC7700_HSP_CLK_MUX_SD0_3MUX1 4
|
||||
#define EIC7700_HSP_CLK_MUX_SD1_3MUX1 5
|
||||
#define EIC7700_HSP_CLK_MUX_EMMC_CQE_2MUX1 6
|
||||
#define EIC7700_HSP_CLK_MUX_SD0_CQE_2MUX1 7
|
||||
#define EIC7700_HSP_CLK_MUX_SD1_CQE_2MUX1 8
|
||||
#define EIC7700_HSP_CLK_GATE_MSHC0_TMR 9
|
||||
#define EIC7700_HSP_CLK_GATE_EMMC 10
|
||||
#define EIC7700_HSP_CLK_GATE_MSHC1_TMR 11
|
||||
#define EIC7700_HSP_CLK_GATE_SD0 12
|
||||
#define EIC7700_HSP_CLK_GATE_MSHC2_TMR 13
|
||||
#define EIC7700_HSP_CLK_GATE_SD1 14
|
||||
#define EIC7700_HSP_CLK_GATE_USB0 15
|
||||
#define EIC7700_HSP_CLK_GATE_USB1 16
|
||||
#define EIC7700_HSP_CLK_GATE_SATA 17
|
||||
|
||||
#endif /* _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_CLOCK_H_ */
|
||||
@@ -0,0 +1,21 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
|
||||
/*
|
||||
* Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd..
|
||||
* All rights reserved.
|
||||
*
|
||||
* Device Tree binding constants for EIC7700 HSP reset controller.
|
||||
*
|
||||
* Authors: Xuyang Dong <dongxuyang@eswincomputing.com>
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_RESET_H_
|
||||
#define _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_RESET_H_
|
||||
|
||||
#define EIC7700_HSP_RST_SATA_P0 0
|
||||
#define EIC7700_HSP_RST_SATA_PHY 1
|
||||
#define EIC7700_HSP_RST_USB0 2
|
||||
#define EIC7700_HSP_RST_USB1 3
|
||||
#define EIC7700_HSP_RST_USB0_PHY 4
|
||||
#define EIC7700_HSP_RST_USB1_PHY 5
|
||||
|
||||
#endif /* _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_RESET_H_ */
|
||||
@@ -6,31 +6,44 @@
|
||||
#ifndef __LINUX_CLK_PROVIDER_H
|
||||
#define __LINUX_CLK_PROVIDER_H
|
||||
|
||||
#include <dt-bindings/clock/clock.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_clk.h>
|
||||
|
||||
/*
|
||||
* flags used across common struct clk. these flags should only affect the
|
||||
* top-level framework. custom flags for dealing with hardware specifics
|
||||
* belong in struct clk_foo
|
||||
/**
|
||||
* DOC: clk framework flags
|
||||
*
|
||||
* Please update clk_flags[] in drivers/clk/clk.c when making changes here!
|
||||
* Flags used across common struct clk. These flags should only affect the
|
||||
* top-level framework. Custom flags for dealing with hardware specifics
|
||||
* belong in struct clk_foo.
|
||||
*
|
||||
* * CLK_SET_RATE_GATE - must be gated across rate change
|
||||
* * CLK_SET_PARENT_GATE - must be gated across re-parent
|
||||
* * CLK_SET_RATE_PARENT - propagate rate change up one level
|
||||
* * CLK_IGNORE_UNUSED - do not gate even if unused
|
||||
* * CLK_GET_RATE_NOCACHE - do not use the cached clk rate
|
||||
* * CLK_SET_RATE_NO_REPARENT - don't re-parent on rate change
|
||||
* * CLK_GET_ACCURACY_NOCACHE - do not use the cached clk accuracy
|
||||
* * CLK_RECALC_NEW_RATES - recalc rates after notifications
|
||||
* * CLK_SET_RATE_UNGATE - clock needs to run to set rate
|
||||
* * CLK_IS_CRITICAL - do not gate, ever
|
||||
* * CLK_OPS_PARENT_ENABLE - parents need enable during gate/ungate, set rate and re-parent
|
||||
* * CLK_DUTY_CYCLE_PARENT - duty cycle call may be forwarded to the parent clock
|
||||
*/
|
||||
#define CLK_SET_RATE_GATE BIT(0) /* must be gated across rate change */
|
||||
#define CLK_SET_PARENT_GATE BIT(1) /* must be gated across re-parent */
|
||||
#define CLK_SET_RATE_PARENT BIT(2) /* propagate rate change up one level */
|
||||
#define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */
|
||||
/* Please update clk_flags[] in drivers/clk/clk.c when making changes here! */
|
||||
#define CLK_SET_RATE_GATE BIT(0)
|
||||
#define CLK_SET_PARENT_GATE BIT(1)
|
||||
#define CLK_SET_RATE_PARENT BIT(2)
|
||||
#define CLK_IGNORE_UNUSED BIT(3)
|
||||
/* unused */
|
||||
/* unused */
|
||||
#define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */
|
||||
#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */
|
||||
#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */
|
||||
#define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */
|
||||
#define CLK_SET_RATE_UNGATE BIT(10) /* clock needs to run to set rate */
|
||||
#define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */
|
||||
/* parents need enable during gate/ungate, set rate and re-parent */
|
||||
#define CLK_GET_RATE_NOCACHE BIT(6)
|
||||
#define CLK_SET_RATE_NO_REPARENT BIT(7)
|
||||
#define CLK_GET_ACCURACY_NOCACHE BIT(8)
|
||||
#define CLK_RECALC_NEW_RATES BIT(9)
|
||||
#define CLK_SET_RATE_UNGATE BIT(10)
|
||||
#define CLK_IS_CRITICAL BIT(11)
|
||||
#define CLK_OPS_PARENT_ENABLE BIT(12)
|
||||
/* duty cycle call may be forwarded to the parent clock */
|
||||
#define CLK_DUTY_CYCLE_PARENT BIT(13)
|
||||
|
||||
struct clk;
|
||||
@@ -84,6 +97,26 @@ struct clk_duty {
|
||||
unsigned int den;
|
||||
};
|
||||
|
||||
enum clk_ssc_method {
|
||||
CLK_SPREAD_NO = CLK_SSC_NO_SPREAD,
|
||||
CLK_SPREAD_CENTER = CLK_SSC_CENTER_SPREAD,
|
||||
CLK_SPREAD_UP = CLK_SSC_UP_SPREAD,
|
||||
CLK_SPREAD_DOWN = CLK_SSC_DOWN_SPREAD,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct clk_spread_spectrum - Structure encoding spread spectrum of a clock
|
||||
*
|
||||
* @modfreq_hz: Modulation frequency
|
||||
* @spread_bp: Modulation percent in permyriad
|
||||
* @method: Modulation method
|
||||
*/
|
||||
struct clk_spread_spectrum {
|
||||
u32 modfreq_hz;
|
||||
u32 spread_bp;
|
||||
enum clk_ssc_method method;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct clk_ops - Callback operations for hardware clocks; these are to
|
||||
* be provided by the clock implementation, and will be called by drivers
|
||||
@@ -174,6 +207,12 @@ struct clk_duty {
|
||||
* separately via calls to .set_parent and .set_rate.
|
||||
* Returns 0 on success, -EERROR otherwise.
|
||||
*
|
||||
* @set_spread_spectrum: Optional callback used to configure the spread
|
||||
* spectrum modulation frequency, percentage, and method
|
||||
* to reduce EMI by spreading the clock frequency over a
|
||||
* wider range.
|
||||
* Returns 0 on success, -EERROR otherwise.
|
||||
*
|
||||
* @recalc_accuracy: Recalculate the accuracy of this clock. The clock accuracy
|
||||
* is expressed in ppb (parts per billion). The parent accuracy is
|
||||
* an input parameter.
|
||||
@@ -249,6 +288,8 @@ struct clk_ops {
|
||||
int (*set_rate_and_parent)(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate, u8 index);
|
||||
int (*set_spread_spectrum)(struct clk_hw *hw,
|
||||
const struct clk_spread_spectrum *ss_conf);
|
||||
unsigned long (*recalc_accuracy)(struct clk_hw *hw,
|
||||
unsigned long parent_accuracy);
|
||||
int (*get_phase)(struct clk_hw *hw);
|
||||
@@ -1431,11 +1472,14 @@ int clk_mux_determine_rate_flags(struct clk_hw *hw,
|
||||
unsigned long flags);
|
||||
int clk_hw_determine_rate_no_reparent(struct clk_hw *hw,
|
||||
struct clk_rate_request *req);
|
||||
int clk_determine_rate_noop(struct clk_hw *hw, struct clk_rate_request *req);
|
||||
void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent);
|
||||
void clk_hw_get_rate_range(struct clk_hw *hw, unsigned long *min_rate,
|
||||
unsigned long *max_rate);
|
||||
void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate,
|
||||
unsigned long max_rate);
|
||||
int clk_hw_set_spread_spectrum(struct clk_hw *hw,
|
||||
const struct clk_spread_spectrum *ss_conf);
|
||||
|
||||
static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw *src)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user