Merge branches 'clk-baikal', 'clk-broadcom', 'clk-vc5' and 'clk-versaclock' into clk-next

- Convert Baikal-T1 CCU driver to platform driver
 - Split reset support out of primary Baikal-T1 CCU driver
 - Add some missing clks required for RPiVid Video Decoder on RaspberryPi
 - Mark PLLC critical on bcm2835
 - Support for Renesas VersaClock7 clock generator family

* clk-baikal:
  clk: baikal-t1: Convert to platform device driver
  clk: baikal-t1: Add DDR/PCIe directly controlled resets support
  dt-bindings: clk: baikal-t1: Add DDR/PCIe reset IDs
  clk: baikal-t1: Move reset-controls code into a dedicated module
  clk: baikal-t1: Add SATA internal ref clock buffer
  clk: baikal-t1: Add shared xGMAC ref/ptp clocks internal parent
  clk: baikal-t1: Fix invalid xGMAC PTP clock divider
  clk: vc5: Fix 5P49V6901 outputs disabling when enabling FOD

* clk-broadcom:
  clk: bcm: rpi: Add support for VEC clock
  clk: bcm: rpi: Handle pixel clock in firmware
  clk: bcm: rpi: Add support HEVC clock
  clk: bcm2835: fix bcm2835_clock_rate_from_divisor declaration
  clk: bcm2835: Round UART input clock up
  clk: bcm2835: Make peripheral PLLC critical

* clk-vc5:
  clk: vc5: Add support for IDT/Renesas VersaClock 5P49V6975
  dt-bindings: clock: vc5: Add 5P49V6975
  clk: vc5: Use regmap_{set,clear}_bits() where appropriate
  clk: vc5: Check IO access results

* clk-versaclock:
  clk: Renesas versaclock7 ccf device driver
  dt-bindings: Renesas versaclock7 device tree bindings
This commit is contained in:
Stephen Boyd 2022-10-04 10:54:34 -07:00
19 changed files with 2182 additions and 224 deletions

View File

@ -56,6 +56,7 @@ properties:
- idt,5p49v5935
- idt,5p49v6901
- idt,5p49v6965
- idt,5p49v6975
reg:
description: I2C device address
@ -134,6 +135,7 @@ allOf:
enum:
- idt,5p49v5933
- idt,5p49v5935
- idt,5p49v6975
then:
# Devices with builtin crystal + optional external input
properties:

View File

@ -0,0 +1,64 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/renesas,versaclock7.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas Versaclock7 Programmable Clock Device Tree Bindings
maintainers:
- Alex Helms <alexander.helms.jy@renesas.com>
description: |
Renesas Versaclock7 is a family of configurable clock generator and
jitter attenuator ICs with fractional and integer dividers.
properties:
'#clock-cells':
const: 1
compatible:
enum:
- renesas,rc21008a
reg:
maxItems: 1
clocks:
items:
- description: External crystal or oscillator
clock-names:
items:
- const: xin
required:
- '#clock-cells'
- compatible
- reg
- clocks
- clock-names
additionalProperties: false
examples:
- |
vc7_xin: clock {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <49152000>;
};
i2c@0 {
reg = <0x0 0x100>;
#address-cells = <1>;
#size-cells = <0>;
vc7: clock-controller@9 {
compatible = "renesas,rc21008a";
reg = <0x9>;
#clock-cells = <1>;
clocks = <&vc7_xin>;
clock-names = "xin";
};
};

View File

@ -17442,6 +17442,12 @@ S: Maintained
F: Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
F: drivers/mtd/nand/raw/renesas-nand-controller.c
RENESAS VERSACLOCK 7 CLOCK DRIVER
M: Alex Helms <alexander.helms.jy@renesas.com>
S: Maintained
F: Documentation/devicetree/bindings/clock/renesas,versaclock7.yaml
F: drivers/clk/clk-versaclock7.c
RESET CONTROLLER FRAMEWORK
M: Philipp Zabel <p.zabel@pengutronix.de>
S: Maintained

View File

@ -377,6 +377,15 @@ config COMMON_CLK_VC5
This driver supports the IDT VersaClock 5 and VersaClock 6
programmable clock generators.
config COMMON_CLK_VC7
tristate "Clock driver for Renesas Versaclock 7 devices"
depends on I2C
depends on OF
select REGMAP_I2C
help
Renesas Versaclock7 is a family of configurable clock generator
and jitter attenuator ICs with fractional and integer dividers.
config COMMON_CLK_STM32MP135
def_bool COMMON_CLK && MACH_STM32MP13
help

View File

@ -73,6 +73,7 @@ obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
obj-$(CONFIG_COMMON_CLK_RS9_PCIE) += clk-renesas-pcie.o
obj-$(CONFIG_COMMON_CLK_VC5) += clk-versaclock5.o
obj-$(CONFIG_COMMON_CLK_VC7) += clk-versaclock7.o
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o

View File

@ -29,7 +29,6 @@ config CLK_BT1_CCU_PLL
config CLK_BT1_CCU_DIV
bool "Baikal-T1 CCU Dividers support"
select RESET_CONTROLLER
select MFD_SYSCON
default MIPS_BAIKAL_T1
help
@ -39,4 +38,15 @@ config CLK_BT1_CCU_DIV
either gateable or ungateable. Some of the CCU dividers can be as well
used to reset the domains they're supplying clock to.
config CLK_BT1_CCU_RST
bool "Baikal-T1 CCU Resets support"
select RESET_CONTROLLER
select MFD_SYSCON
default MIPS_BAIKAL_T1
help
Enable this to support the CCU reset blocks responsible for the
AXI-bus and some subsystems reset. These are mainly the
self-deasserted reset controls but there are several lines which
can be directly asserted/de-asserted (PCIe and DDR sub-domains).
endif

View File

@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_CLK_BT1_CCU_PLL) += ccu-pll.o clk-ccu-pll.o
obj-$(CONFIG_CLK_BT1_CCU_DIV) += ccu-div.o clk-ccu-div.o
obj-$(CONFIG_CLK_BT1_CCU_RST) += ccu-rst.o

View File

@ -34,9 +34,9 @@
#define CCU_DIV_CTL_CLKDIV_MASK(_width) \
GENMASK((_width) + CCU_DIV_CTL_CLKDIV_FLD - 1, CCU_DIV_CTL_CLKDIV_FLD)
#define CCU_DIV_CTL_LOCK_SHIFTED BIT(27)
#define CCU_DIV_CTL_GATE_REF_BUF BIT(28)
#define CCU_DIV_CTL_LOCK_NORMAL BIT(31)
#define CCU_DIV_RST_DELAY_US 1
#define CCU_DIV_LOCK_CHECK_RETRIES 50
#define CCU_DIV_CLKDIV_MIN 0
@ -170,6 +170,40 @@ static int ccu_div_gate_is_enabled(struct clk_hw *hw)
return !!(val & CCU_DIV_CTL_EN);
}
static int ccu_div_buf_enable(struct clk_hw *hw)
{
struct ccu_div *div = to_ccu_div(hw);
unsigned long flags;
spin_lock_irqsave(&div->lock, flags);
regmap_update_bits(div->sys_regs, div->reg_ctl,
CCU_DIV_CTL_GATE_REF_BUF, 0);
spin_unlock_irqrestore(&div->lock, flags);
return 0;
}
static void ccu_div_buf_disable(struct clk_hw *hw)
{
struct ccu_div *div = to_ccu_div(hw);
unsigned long flags;
spin_lock_irqsave(&div->lock, flags);
regmap_update_bits(div->sys_regs, div->reg_ctl,
CCU_DIV_CTL_GATE_REF_BUF, CCU_DIV_CTL_GATE_REF_BUF);
spin_unlock_irqrestore(&div->lock, flags);
}
static int ccu_div_buf_is_enabled(struct clk_hw *hw)
{
struct ccu_div *div = to_ccu_div(hw);
u32 val = 0;
regmap_read(div->sys_regs, div->reg_ctl, &val);
return !(val & CCU_DIV_CTL_GATE_REF_BUF);
}
static unsigned long ccu_div_var_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
@ -288,24 +322,6 @@ static int ccu_div_fixed_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
int ccu_div_reset_domain(struct ccu_div *div)
{
unsigned long flags;
if (!div || !(div->features & CCU_DIV_RESET_DOMAIN))
return -EINVAL;
spin_lock_irqsave(&div->lock, flags);
regmap_update_bits(div->sys_regs, div->reg_ctl,
CCU_DIV_CTL_RST, CCU_DIV_CTL_RST);
spin_unlock_irqrestore(&div->lock, flags);
/* The next delay must be enough to cover all the resets. */
udelay(CCU_DIV_RST_DELAY_US);
return 0;
}
#ifdef CONFIG_DEBUG_FS
struct ccu_div_dbgfs_bit {
@ -323,6 +339,7 @@ static const struct ccu_div_dbgfs_bit ccu_div_bits[] = {
CCU_DIV_DBGFS_BIT_ATTR("div_en", CCU_DIV_CTL_EN),
CCU_DIV_DBGFS_BIT_ATTR("div_rst", CCU_DIV_CTL_RST),
CCU_DIV_DBGFS_BIT_ATTR("div_bypass", CCU_DIV_CTL_SET_CLKDIV),
CCU_DIV_DBGFS_BIT_ATTR("div_buf", CCU_DIV_CTL_GATE_REF_BUF),
CCU_DIV_DBGFS_BIT_ATTR("div_lock", CCU_DIV_CTL_LOCK_NORMAL)
};
@ -441,6 +458,9 @@ static void ccu_div_var_debug_init(struct clk_hw *hw, struct dentry *dentry)
continue;
}
if (!strcmp("div_buf", name))
continue;
bits[didx] = ccu_div_bits[bidx];
bits[didx].div = div;
@ -477,6 +497,21 @@ static void ccu_div_gate_debug_init(struct clk_hw *hw, struct dentry *dentry)
&ccu_div_dbgfs_fixed_clkdiv_fops);
}
static void ccu_div_buf_debug_init(struct clk_hw *hw, struct dentry *dentry)
{
struct ccu_div *div = to_ccu_div(hw);
struct ccu_div_dbgfs_bit *bit;
bit = kmalloc(sizeof(*bit), GFP_KERNEL);
if (!bit)
return;
*bit = ccu_div_bits[3];
bit->div = div;
debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit,
&ccu_div_dbgfs_bit_fops);
}
static void ccu_div_fixed_debug_init(struct clk_hw *hw, struct dentry *dentry)
{
struct ccu_div *div = to_ccu_div(hw);
@ -489,6 +524,7 @@ static void ccu_div_fixed_debug_init(struct clk_hw *hw, struct dentry *dentry)
#define ccu_div_var_debug_init NULL
#define ccu_div_gate_debug_init NULL
#define ccu_div_buf_debug_init NULL
#define ccu_div_fixed_debug_init NULL
#endif /* !CONFIG_DEBUG_FS */
@ -520,6 +556,13 @@ static const struct clk_ops ccu_div_gate_ops = {
.debug_init = ccu_div_gate_debug_init
};
static const struct clk_ops ccu_div_buf_ops = {
.enable = ccu_div_buf_enable,
.disable = ccu_div_buf_disable,
.is_enabled = ccu_div_buf_is_enabled,
.debug_init = ccu_div_buf_debug_init
};
static const struct clk_ops ccu_div_fixed_ops = {
.recalc_rate = ccu_div_fixed_recalc_rate,
.round_rate = ccu_div_fixed_round_rate,
@ -566,6 +609,8 @@ struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *div_init)
} else if (div_init->type == CCU_DIV_GATE) {
hw_init.ops = &ccu_div_gate_ops;
div->divider = div_init->divider;
} else if (div_init->type == CCU_DIV_BUF) {
hw_init.ops = &ccu_div_buf_ops;
} else if (div_init->type == CCU_DIV_FIXED) {
hw_init.ops = &ccu_div_fixed_ops;
div->divider = div_init->divider;
@ -579,6 +624,7 @@ struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *div_init)
goto err_free_div;
}
parent_data.fw_name = div_init->parent_name;
parent_data.name = div_init->parent_name;
hw_init.parent_data = &parent_data;
hw_init.num_parents = 1;

View File

@ -13,15 +13,26 @@
#include <linux/bits.h>
#include <linux/of.h>
/*
* CCU Divider private clock IDs
* @CCU_SYS_SATA_CLK: CCU SATA internal clock
* @CCU_SYS_XGMAC_CLK: CCU XGMAC internal clock
*/
#define CCU_SYS_SATA_CLK -1
#define CCU_SYS_XGMAC_CLK -2
/*
* CCU Divider private flags
* @CCU_DIV_BASIC: Basic divider clock required by the kernel as early as
* possible.
* @CCU_DIV_SKIP_ONE: Due to some reason divider can't be set to 1.
* It can be 0 though, which is functionally the same.
* @CCU_DIV_SKIP_ONE_TO_THREE: For some reason divider can't be within [1,3].
* It can be either 0 or greater than 3.
* @CCU_DIV_LOCK_SHIFTED: Find lock-bit at non-standard position.
* @CCU_DIV_RESET_DOMAIN: Provide reset clock domain method.
* @CCU_DIV_RESET_DOMAIN: There is a clock domain reset handle.
*/
#define CCU_DIV_BASIC BIT(0)
#define CCU_DIV_SKIP_ONE BIT(1)
#define CCU_DIV_SKIP_ONE_TO_THREE BIT(2)
#define CCU_DIV_LOCK_SHIFTED BIT(3)
@ -31,11 +42,13 @@
* enum ccu_div_type - CCU Divider types
* @CCU_DIV_VAR: Clocks gate with variable divider.
* @CCU_DIV_GATE: Clocks gate with fixed divider.
* @CCU_DIV_BUF: Clock gate with no divider.
* @CCU_DIV_FIXED: Ungateable clock with fixed divider.
*/
enum ccu_div_type {
CCU_DIV_VAR,
CCU_DIV_GATE,
CCU_DIV_BUF,
CCU_DIV_FIXED
};
@ -105,6 +118,4 @@ struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *init);
void ccu_div_hw_unregister(struct ccu_div *div);
int ccu_div_reset_domain(struct ccu_div *div);
#endif /* __CLK_BT1_CCU_DIV_H__ */

View File

@ -13,6 +13,12 @@
#include <linux/bits.h>
#include <linux/of.h>
/*
* CCU PLL private flags
* @CCU_PLL_BASIC: Basic PLL required by the kernel as early as possible.
*/
#define CCU_PLL_BASIC BIT(0)
/*
* struct ccu_pll_init_data - CCU PLL initialization data
* @id: Clock private identifier.
@ -22,6 +28,7 @@
* @sys_regs: Baikal-T1 System Controller registers map.
* @np: Pointer to the node describing the CCU PLLs.
* @flags: PLL clock flags.
* @features: PLL private features.
*/
struct ccu_pll_init_data {
unsigned int id;
@ -31,6 +38,7 @@ struct ccu_pll_init_data {
struct regmap *sys_regs;
struct device_node *np;
unsigned long flags;
unsigned long features;
};
/*

View File

@ -0,0 +1,217 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021 BAIKAL ELECTRONICS, JSC
*
* Authors:
* Serge Semin <Sergey.Semin@baikalelectronics.ru>
*
* Baikal-T1 CCU Resets interface driver
*/
#define pr_fmt(fmt) "bt1-ccu-rst: " fmt
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/printk.h>
#include <linux/regmap.h>
#include <linux/reset-controller.h>
#include <linux/slab.h>
#include <dt-bindings/reset/bt1-ccu.h>
#include "ccu-rst.h"
#define CCU_AXI_MAIN_BASE 0x030
#define CCU_AXI_DDR_BASE 0x034
#define CCU_AXI_SATA_BASE 0x038
#define CCU_AXI_GMAC0_BASE 0x03C
#define CCU_AXI_GMAC1_BASE 0x040
#define CCU_AXI_XGMAC_BASE 0x044
#define CCU_AXI_PCIE_M_BASE 0x048
#define CCU_AXI_PCIE_S_BASE 0x04C
#define CCU_AXI_USB_BASE 0x050
#define CCU_AXI_HWA_BASE 0x054
#define CCU_AXI_SRAM_BASE 0x058
#define CCU_SYS_DDR_BASE 0x02c
#define CCU_SYS_SATA_REF_BASE 0x060
#define CCU_SYS_APB_BASE 0x064
#define CCU_SYS_PCIE_BASE 0x144
#define CCU_RST_DELAY_US 1
#define CCU_RST_TRIG(_base, _ofs) \
{ \
.type = CCU_RST_TRIG, \
.base = _base, \
.mask = BIT(_ofs), \
}
#define CCU_RST_DIR(_base, _ofs) \
{ \
.type = CCU_RST_DIR, \
.base = _base, \
.mask = BIT(_ofs), \
}
struct ccu_rst_info {
enum ccu_rst_type type;
unsigned int base;
unsigned int mask;
};
/*
* Each AXI-bus clock divider is equipped with the corresponding clock-consumer
* domain reset (it's self-deasserted reset control).
*/
static const struct ccu_rst_info axi_rst_info[] = {
[CCU_AXI_MAIN_RST] = CCU_RST_TRIG(CCU_AXI_MAIN_BASE, 1),
[CCU_AXI_DDR_RST] = CCU_RST_TRIG(CCU_AXI_DDR_BASE, 1),
[CCU_AXI_SATA_RST] = CCU_RST_TRIG(CCU_AXI_SATA_BASE, 1),
[CCU_AXI_GMAC0_RST] = CCU_RST_TRIG(CCU_AXI_GMAC0_BASE, 1),
[CCU_AXI_GMAC1_RST] = CCU_RST_TRIG(CCU_AXI_GMAC1_BASE, 1),
[CCU_AXI_XGMAC_RST] = CCU_RST_TRIG(CCU_AXI_XGMAC_BASE, 1),
[CCU_AXI_PCIE_M_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_M_BASE, 1),
[CCU_AXI_PCIE_S_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_S_BASE, 1),
[CCU_AXI_USB_RST] = CCU_RST_TRIG(CCU_AXI_USB_BASE, 1),
[CCU_AXI_HWA_RST] = CCU_RST_TRIG(CCU_AXI_HWA_BASE, 1),
[CCU_AXI_SRAM_RST] = CCU_RST_TRIG(CCU_AXI_SRAM_BASE, 1),
};
/*
* SATA reference clock domain and APB-bus domain are connected with the
* sefl-deasserted reset control, which can be activated via the corresponding
* clock divider register. DDR and PCIe sub-domains can be reset with directly
* controlled reset signals. Resetting the DDR controller though won't end up
* well while the Linux kernel is working.
*/
static const struct ccu_rst_info sys_rst_info[] = {
[CCU_SYS_SATA_REF_RST] = CCU_RST_TRIG(CCU_SYS_SATA_REF_BASE, 1),
[CCU_SYS_APB_RST] = CCU_RST_TRIG(CCU_SYS_APB_BASE, 1),
[CCU_SYS_DDR_FULL_RST] = CCU_RST_DIR(CCU_SYS_DDR_BASE, 1),
[CCU_SYS_DDR_INIT_RST] = CCU_RST_DIR(CCU_SYS_DDR_BASE, 2),
[CCU_SYS_PCIE_PCS_PHY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 0),
[CCU_SYS_PCIE_PIPE0_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 4),
[CCU_SYS_PCIE_CORE_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 8),
[CCU_SYS_PCIE_PWR_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 9),
[CCU_SYS_PCIE_STICKY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 10),
[CCU_SYS_PCIE_NSTICKY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 11),
[CCU_SYS_PCIE_HOT_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 12),
};
static int ccu_rst_reset(struct reset_controller_dev *rcdev, unsigned long idx)
{
struct ccu_rst *rst = to_ccu_rst(rcdev);
const struct ccu_rst_info *info = &rst->rsts_info[idx];
if (info->type != CCU_RST_TRIG)
return -EOPNOTSUPP;
regmap_update_bits(rst->sys_regs, info->base, info->mask, info->mask);
/* The next delay must be enough to cover all the resets. */
udelay(CCU_RST_DELAY_US);
return 0;
}
static int ccu_rst_set(struct reset_controller_dev *rcdev,
unsigned long idx, bool high)
{
struct ccu_rst *rst = to_ccu_rst(rcdev);
const struct ccu_rst_info *info = &rst->rsts_info[idx];
if (info->type != CCU_RST_DIR)
return high ? -EOPNOTSUPP : 0;
return regmap_update_bits(rst->sys_regs, info->base,
info->mask, high ? info->mask : 0);
}
static int ccu_rst_assert(struct reset_controller_dev *rcdev,
unsigned long idx)
{
return ccu_rst_set(rcdev, idx, true);
}
static int ccu_rst_deassert(struct reset_controller_dev *rcdev,
unsigned long idx)
{
return ccu_rst_set(rcdev, idx, false);
}
static int ccu_rst_status(struct reset_controller_dev *rcdev,
unsigned long idx)
{
struct ccu_rst *rst = to_ccu_rst(rcdev);
const struct ccu_rst_info *info = &rst->rsts_info[idx];
u32 val;
if (info->type != CCU_RST_DIR)
return -EOPNOTSUPP;
regmap_read(rst->sys_regs, info->base, &val);
return !!(val & info->mask);
}
static const struct reset_control_ops ccu_rst_ops = {
.reset = ccu_rst_reset,
.assert = ccu_rst_assert,
.deassert = ccu_rst_deassert,
.status = ccu_rst_status,
};
struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *rst_init)
{
struct ccu_rst *rst;
int ret;
if (!rst_init)
return ERR_PTR(-EINVAL);
rst = kzalloc(sizeof(*rst), GFP_KERNEL);
if (!rst)
return ERR_PTR(-ENOMEM);
rst->sys_regs = rst_init->sys_regs;
if (of_device_is_compatible(rst_init->np, "baikal,bt1-ccu-axi")) {
rst->rcdev.nr_resets = ARRAY_SIZE(axi_rst_info);
rst->rsts_info = axi_rst_info;
} else if (of_device_is_compatible(rst_init->np, "baikal,bt1-ccu-sys")) {
rst->rcdev.nr_resets = ARRAY_SIZE(sys_rst_info);
rst->rsts_info = sys_rst_info;
} else {
pr_err("Incompatible DT node '%s' specified\n",
of_node_full_name(rst_init->np));
ret = -EINVAL;
goto err_kfree_rst;
}
rst->rcdev.owner = THIS_MODULE;
rst->rcdev.ops = &ccu_rst_ops;
rst->rcdev.of_node = rst_init->np;
ret = reset_controller_register(&rst->rcdev);
if (ret) {
pr_err("Couldn't register '%s' reset controller\n",
of_node_full_name(rst_init->np));
goto err_kfree_rst;
}
return rst;
err_kfree_rst:
kfree(rst);
return ERR_PTR(ret);
}
void ccu_rst_hw_unregister(struct ccu_rst *rst)
{
reset_controller_unregister(&rst->rcdev);
kfree(rst);
}

View File

@ -0,0 +1,67 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2021 BAIKAL ELECTRONICS, JSC
*
* Baikal-T1 CCU Resets interface driver
*/
#ifndef __CLK_BT1_CCU_RST_H__
#define __CLK_BT1_CCU_RST_H__
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/reset-controller.h>
struct ccu_rst_info;
/*
* enum ccu_rst_type - CCU Reset types
* @CCU_RST_TRIG: Self-deasserted reset signal.
* @CCU_RST_DIR: Directly controlled reset signal.
*/
enum ccu_rst_type {
CCU_RST_TRIG,
CCU_RST_DIR,
};
/*
* struct ccu_rst_init_data - CCU Resets initialization data
* @sys_regs: Baikal-T1 System Controller registers map.
* @np: Pointer to the node with the System CCU block.
*/
struct ccu_rst_init_data {
struct regmap *sys_regs;
struct device_node *np;
};
/*
* struct ccu_rst - CCU Reset descriptor
* @rcdev: Reset controller descriptor.
* @sys_regs: Baikal-T1 System Controller registers map.
* @rsts_info: Reset flag info (base address and mask).
*/
struct ccu_rst {
struct reset_controller_dev rcdev;
struct regmap *sys_regs;
const struct ccu_rst_info *rsts_info;
};
#define to_ccu_rst(_rcdev) container_of(_rcdev, struct ccu_rst, rcdev)
#ifdef CONFIG_CLK_BT1_CCU_RST
struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *init);
void ccu_rst_hw_unregister(struct ccu_rst *rst);
#else
static inline
struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *init)
{
return NULL;
}
static inline void ccu_rst_hw_unregister(struct ccu_rst *rst) {}
#endif
#endif /* __CLK_BT1_CCU_RST_H__ */

View File

@ -12,6 +12,7 @@
#define pr_fmt(fmt) "bt1-ccu-div: " fmt
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>
@ -24,9 +25,9 @@
#include <linux/regmap.h>
#include <dt-bindings/clock/bt1-ccu.h>
#include <dt-bindings/reset/bt1-ccu.h>
#include "ccu-div.h"
#include "ccu-rst.h"
#define CCU_AXI_MAIN_BASE 0x030
#define CCU_AXI_DDR_BASE 0x034
@ -76,6 +77,16 @@
.divider = _divider \
}
#define CCU_DIV_BUF_INFO(_id, _name, _pname, _base, _flags) \
{ \
.id = _id, \
.name = _name, \
.parent_name = _pname, \
.base = _base, \
.type = CCU_DIV_BUF, \
.flags = _flags \
}
#define CCU_DIV_FIXED_INFO(_id, _name, _pname, _divider) \
{ \
.id = _id, \
@ -85,12 +96,6 @@
.divider = _divider \
}
#define CCU_DIV_RST_MAP(_rst_id, _clk_id) \
{ \
.rst_id = _rst_id, \
.clk_id = _clk_id \
}
struct ccu_div_info {
unsigned int id;
const char *name;
@ -105,11 +110,6 @@ struct ccu_div_info {
unsigned long features;
};
struct ccu_div_rst_map {
unsigned int rst_id;
unsigned int clk_id;
};
struct ccu_div_data {
struct device_node *np;
struct regmap *sys_regs;
@ -118,11 +118,8 @@ struct ccu_div_data {
const struct ccu_div_info *divs_info;
struct ccu_div **divs;
unsigned int rst_num;
const struct ccu_div_rst_map *rst_map;
struct reset_controller_dev rcdev;
struct ccu_rst *rsts;
};
#define to_ccu_div_data(_rcdev) container_of(_rcdev, struct ccu_div_data, rcdev)
/*
* AXI Main Interconnect (axi_main_clk) and DDR AXI-bus (axi_ddr_clk) clocks
@ -169,33 +166,22 @@ static const struct ccu_div_info axi_info[] = {
CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN)
};
static const struct ccu_div_rst_map axi_rst_map[] = {
CCU_DIV_RST_MAP(CCU_AXI_MAIN_RST, CCU_AXI_MAIN_CLK),
CCU_DIV_RST_MAP(CCU_AXI_DDR_RST, CCU_AXI_DDR_CLK),
CCU_DIV_RST_MAP(CCU_AXI_SATA_RST, CCU_AXI_SATA_CLK),
CCU_DIV_RST_MAP(CCU_AXI_GMAC0_RST, CCU_AXI_GMAC0_CLK),
CCU_DIV_RST_MAP(CCU_AXI_GMAC1_RST, CCU_AXI_GMAC1_CLK),
CCU_DIV_RST_MAP(CCU_AXI_XGMAC_RST, CCU_AXI_XGMAC_CLK),
CCU_DIV_RST_MAP(CCU_AXI_PCIE_M_RST, CCU_AXI_PCIE_M_CLK),
CCU_DIV_RST_MAP(CCU_AXI_PCIE_S_RST, CCU_AXI_PCIE_S_CLK),
CCU_DIV_RST_MAP(CCU_AXI_USB_RST, CCU_AXI_USB_CLK),
CCU_DIV_RST_MAP(CCU_AXI_HWA_RST, CCU_AXI_HWA_CLK),
CCU_DIV_RST_MAP(CCU_AXI_SRAM_RST, CCU_AXI_SRAM_CLK)
};
/*
* APB-bus clock is marked as critical since it's a main communication bus
* for the SoC devices registers IO-operations.
*/
static const struct ccu_div_info sys_info[] = {
CCU_DIV_VAR_INFO(CCU_SYS_SATA_REF_CLK, "sys_sata_ref_clk",
CCU_DIV_VAR_INFO(CCU_SYS_SATA_CLK, "sys_sata_clk",
"sata_clk", CCU_SYS_SATA_REF_BASE, 4,
CLK_SET_RATE_GATE,
CCU_DIV_SKIP_ONE | CCU_DIV_LOCK_SHIFTED |
CCU_DIV_RESET_DOMAIN),
CCU_DIV_BUF_INFO(CCU_SYS_SATA_REF_CLK, "sys_sata_ref_clk",
"sys_sata_clk", CCU_SYS_SATA_REF_BASE,
CLK_SET_RATE_PARENT),
CCU_DIV_VAR_INFO(CCU_SYS_APB_CLK, "sys_apb_clk",
"pcie_clk", CCU_SYS_APB_BASE, 5,
CLK_IS_CRITICAL, CCU_DIV_RESET_DOMAIN),
CLK_IS_CRITICAL, CCU_DIV_BASIC | CCU_DIV_RESET_DOMAIN),
CCU_DIV_GATE_INFO(CCU_SYS_GMAC0_TX_CLK, "sys_gmac0_tx_clk",
"eth_clk", CCU_SYS_GMAC0_BASE, 5),
CCU_DIV_FIXED_INFO(CCU_SYS_GMAC0_PTP_CLK, "sys_gmac0_ptp_clk",
@ -204,10 +190,12 @@ static const struct ccu_div_info sys_info[] = {
"eth_clk", CCU_SYS_GMAC1_BASE, 5),
CCU_DIV_FIXED_INFO(CCU_SYS_GMAC1_PTP_CLK, "sys_gmac1_ptp_clk",
"eth_clk", 10),
CCU_DIV_GATE_INFO(CCU_SYS_XGMAC_REF_CLK, "sys_xgmac_ref_clk",
"eth_clk", CCU_SYS_XGMAC_BASE, 8),
CCU_DIV_GATE_INFO(CCU_SYS_XGMAC_CLK, "sys_xgmac_clk",
"eth_clk", CCU_SYS_XGMAC_BASE, 1),
CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_REF_CLK, "sys_xgmac_ref_clk",
"sys_xgmac_clk", 8),
CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_PTP_CLK, "sys_xgmac_ptp_clk",
"eth_clk", 10),
"sys_xgmac_clk", 8),
CCU_DIV_GATE_INFO(CCU_SYS_USB_CLK, "sys_usb_clk",
"eth_clk", CCU_SYS_USB_BASE, 10),
CCU_DIV_VAR_INFO(CCU_SYS_PVT_CLK, "sys_pvt_clk",
@ -227,74 +215,58 @@ static const struct ccu_div_info sys_info[] = {
"ref_clk", 25),
CCU_DIV_VAR_INFO(CCU_SYS_TIMER0_CLK, "sys_timer0_clk",
"ref_clk", CCU_SYS_TIMER0_BASE, 17,
CLK_SET_RATE_GATE, 0),
CLK_SET_RATE_GATE, CCU_DIV_BASIC),
CCU_DIV_VAR_INFO(CCU_SYS_TIMER1_CLK, "sys_timer1_clk",
"ref_clk", CCU_SYS_TIMER1_BASE, 17,
CLK_SET_RATE_GATE, 0),
CLK_SET_RATE_GATE, CCU_DIV_BASIC),
CCU_DIV_VAR_INFO(CCU_SYS_TIMER2_CLK, "sys_timer2_clk",
"ref_clk", CCU_SYS_TIMER2_BASE, 17,
CLK_SET_RATE_GATE, 0),
CLK_SET_RATE_GATE, CCU_DIV_BASIC),
CCU_DIV_VAR_INFO(CCU_SYS_WDT_CLK, "sys_wdt_clk",
"eth_clk", CCU_SYS_WDT_BASE, 17,
CLK_SET_RATE_GATE, CCU_DIV_SKIP_ONE_TO_THREE)
};
static const struct ccu_div_rst_map sys_rst_map[] = {
CCU_DIV_RST_MAP(CCU_SYS_SATA_REF_RST, CCU_SYS_SATA_REF_CLK),
CCU_DIV_RST_MAP(CCU_SYS_APB_RST, CCU_SYS_APB_CLK),
};
static struct ccu_div_data *axi_data;
static struct ccu_div_data *sys_data;
static void ccu_div_set_data(struct ccu_div_data *data)
{
struct device_node *np = data->np;
if (of_device_is_compatible(np, "baikal,bt1-ccu-axi"))
axi_data = data;
else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys"))
sys_data = data;
else
pr_err("Invalid DT node '%s' specified\n", of_node_full_name(np));
}
static struct ccu_div_data *ccu_div_get_data(struct device_node *np)
{
if (of_device_is_compatible(np, "baikal,bt1-ccu-axi"))
return axi_data;
else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys"))
return sys_data;
pr_err("Invalid DT node '%s' specified\n", of_node_full_name(np));
return NULL;
}
static struct ccu_div *ccu_div_find_desc(struct ccu_div_data *data,
unsigned int clk_id)
{
struct ccu_div *div;
int idx;
for (idx = 0; idx < data->divs_num; ++idx) {
div = data->divs[idx];
if (div && div->id == clk_id)
return div;
if (data->divs_info[idx].id == clk_id)
return data->divs[idx];
}
return ERR_PTR(-EINVAL);
}
static int ccu_div_reset(struct reset_controller_dev *rcdev,
unsigned long rst_id)
{
struct ccu_div_data *data = to_ccu_div_data(rcdev);
const struct ccu_div_rst_map *map;
struct ccu_div *div;
int idx, ret;
for (idx = 0, map = data->rst_map; idx < data->rst_num; ++idx, ++map) {
if (map->rst_id == rst_id)
break;
}
if (idx == data->rst_num) {
pr_err("Invalid reset ID %lu specified\n", rst_id);
return -EINVAL;
}
div = ccu_div_find_desc(data, map->clk_id);
if (IS_ERR(div)) {
pr_err("Invalid clock ID %d in mapping\n", map->clk_id);
return PTR_ERR(div);
}
ret = ccu_div_reset_domain(div);
if (ret) {
pr_err("Reset isn't supported by divider %s\n",
clk_hw_get_name(ccu_div_get_clk_hw(div)));
}
return ret;
}
static const struct reset_control_ops ccu_div_rst_ops = {
.reset = ccu_div_reset,
};
static struct ccu_div_data *ccu_div_create_data(struct device_node *np)
{
struct ccu_div_data *data;
@ -308,13 +280,9 @@ static struct ccu_div_data *ccu_div_create_data(struct device_node *np)
if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) {
data->divs_num = ARRAY_SIZE(axi_info);
data->divs_info = axi_info;
data->rst_num = ARRAY_SIZE(axi_rst_map);
data->rst_map = axi_rst_map;
} else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) {
data->divs_num = ARRAY_SIZE(sys_info);
data->divs_info = sys_info;
data->rst_num = ARRAY_SIZE(sys_rst_map);
data->rst_map = sys_rst_map;
} else {
pr_err("Incompatible DT node '%s' specified\n",
of_node_full_name(np));
@ -365,14 +333,16 @@ static struct clk_hw *ccu_div_of_clk_hw_get(struct of_phandle_args *clkspec,
clk_id = clkspec->args[0];
div = ccu_div_find_desc(data, clk_id);
if (IS_ERR(div)) {
if (div != ERR_PTR(-EPROBE_DEFER))
pr_info("Invalid clock ID %d specified\n", clk_id);
return ERR_CAST(div);
}
return ccu_div_get_clk_hw(div);
}
static int ccu_div_clk_register(struct ccu_div_data *data)
static int ccu_div_clk_register(struct ccu_div_data *data, bool defer)
{
int idx, ret;
@ -380,6 +350,13 @@ static int ccu_div_clk_register(struct ccu_div_data *data)
const struct ccu_div_info *info = &data->divs_info[idx];
struct ccu_div_init_data init = {0};
if (!!(info->features & CCU_DIV_BASIC) ^ defer) {
if (!data->divs[idx])
data->divs[idx] = ERR_PTR(-EPROBE_DEFER);
continue;
}
init.id = info->id;
init.name = info->name;
init.parent_name = info->parent_name;
@ -396,6 +373,9 @@ static int ccu_div_clk_register(struct ccu_div_data *data)
init.base = info->base;
init.sys_regs = data->sys_regs;
init.divider = info->divider;
} else if (init.type == CCU_DIV_BUF) {
init.base = info->base;
init.sys_regs = data->sys_regs;
} else {
init.divider = info->divider;
}
@ -409,49 +389,104 @@ static int ccu_div_clk_register(struct ccu_div_data *data)
}
}
return 0;
err_hw_unregister:
for (--idx; idx >= 0; --idx) {
if (!!(data->divs_info[idx].features & CCU_DIV_BASIC) ^ defer)
continue;
ccu_div_hw_unregister(data->divs[idx]);
}
return ret;
}
static void ccu_div_clk_unregister(struct ccu_div_data *data, bool defer)
{
int idx;
/* Uninstall only the clocks registered on the specfied stage */
for (idx = 0; idx < data->divs_num; ++idx) {
if (!!(data->divs_info[idx].features & CCU_DIV_BASIC) ^ defer)
continue;
ccu_div_hw_unregister(data->divs[idx]);
}
}
static int ccu_div_of_register(struct ccu_div_data *data)
{
int ret;
ret = of_clk_add_hw_provider(data->np, ccu_div_of_clk_hw_get, data);
if (ret) {
pr_err("Couldn't register dividers '%s' clock provider\n",
of_node_full_name(data->np));
goto err_hw_unregister;
}
return 0;
err_hw_unregister:
for (--idx; idx >= 0; --idx)
ccu_div_hw_unregister(data->divs[idx]);
return ret;
}
static void ccu_div_clk_unregister(struct ccu_div_data *data)
{
int idx;
of_clk_del_provider(data->np);
for (idx = 0; idx < data->divs_num; ++idx)
ccu_div_hw_unregister(data->divs[idx]);
}
static int ccu_div_rst_register(struct ccu_div_data *data)
{
int ret;
struct ccu_rst_init_data init = {0};
data->rcdev.ops = &ccu_div_rst_ops;
data->rcdev.of_node = data->np;
data->rcdev.nr_resets = data->rst_num;
init.sys_regs = data->sys_regs;
init.np = data->np;
ret = reset_controller_register(&data->rcdev);
if (ret)
data->rsts = ccu_rst_hw_register(&init);
if (IS_ERR(data->rsts)) {
pr_err("Couldn't register divider '%s' reset controller\n",
of_node_full_name(data->np));
return PTR_ERR(data->rsts);
}
return 0;
}
static int ccu_div_probe(struct platform_device *pdev)
{
struct ccu_div_data *data;
int ret;
data = ccu_div_get_data(dev_of_node(&pdev->dev));
if (!data)
return -EINVAL;
ret = ccu_div_clk_register(data, false);
if (ret)
return ret;
ret = ccu_div_rst_register(data);
if (ret)
goto err_clk_unregister;
return 0;
err_clk_unregister:
ccu_div_clk_unregister(data, false);
return ret;
}
static void ccu_div_init(struct device_node *np)
static const struct of_device_id ccu_div_of_match[] = {
{ .compatible = "baikal,bt1-ccu-axi" },
{ .compatible = "baikal,bt1-ccu-sys" },
{ }
};
static struct platform_driver ccu_div_driver = {
.probe = ccu_div_probe,
.driver = {
.name = "clk-ccu-div",
.of_match_table = ccu_div_of_match,
.suppress_bind_attrs = true,
},
};
builtin_platform_driver(ccu_div_driver);
static __init void ccu_div_init(struct device_node *np)
{
struct ccu_div_data *data;
int ret;
@ -464,22 +499,23 @@ static void ccu_div_init(struct device_node *np)
if (ret)
goto err_free_data;
ret = ccu_div_clk_register(data);
ret = ccu_div_clk_register(data, true);
if (ret)
goto err_free_data;
ret = ccu_div_rst_register(data);
ret = ccu_div_of_register(data);
if (ret)
goto err_clk_unregister;
ccu_div_set_data(data);
return;
err_clk_unregister:
ccu_div_clk_unregister(data);
ccu_div_clk_unregister(data, true);
err_free_data:
ccu_div_free_data(data);
}
CLK_OF_DECLARE(ccu_axi, "baikal,bt1-ccu-axi", ccu_div_init);
CLK_OF_DECLARE(ccu_sys, "baikal,bt1-ccu-sys", ccu_div_init);
CLK_OF_DECLARE_DRIVER(ccu_axi, "baikal,bt1-ccu-axi", ccu_div_init);
CLK_OF_DECLARE_DRIVER(ccu_sys, "baikal,bt1-ccu-sys", ccu_div_init);

View File

@ -12,6 +12,7 @@
#define pr_fmt(fmt) "bt1-ccu-pll: " fmt
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>
@ -31,13 +32,14 @@
#define CCU_PCIE_PLL_BASE 0x018
#define CCU_ETH_PLL_BASE 0x020
#define CCU_PLL_INFO(_id, _name, _pname, _base, _flags) \
#define CCU_PLL_INFO(_id, _name, _pname, _base, _flags, _features) \
{ \
.id = _id, \
.name = _name, \
.parent_name = _pname, \
.base = _base, \
.flags = _flags \
.flags = _flags, \
.features = _features, \
}
#define CCU_PLL_NUM ARRAY_SIZE(pll_info)
@ -48,6 +50,7 @@ struct ccu_pll_info {
const char *parent_name;
unsigned int base;
unsigned long flags;
unsigned long features;
};
/*
@ -61,15 +64,15 @@ struct ccu_pll_info {
*/
static const struct ccu_pll_info pll_info[] = {
CCU_PLL_INFO(CCU_CPU_PLL, "cpu_pll", "ref_clk", CCU_CPU_PLL_BASE,
CLK_IS_CRITICAL),
CLK_IS_CRITICAL, CCU_PLL_BASIC),
CCU_PLL_INFO(CCU_SATA_PLL, "sata_pll", "ref_clk", CCU_SATA_PLL_BASE,
CLK_IS_CRITICAL | CLK_SET_RATE_GATE),
CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0),
CCU_PLL_INFO(CCU_DDR_PLL, "ddr_pll", "ref_clk", CCU_DDR_PLL_BASE,
CLK_IS_CRITICAL | CLK_SET_RATE_GATE),
CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0),
CCU_PLL_INFO(CCU_PCIE_PLL, "pcie_pll", "ref_clk", CCU_PCIE_PLL_BASE,
CLK_IS_CRITICAL),
CLK_IS_CRITICAL, CCU_PLL_BASIC),
CCU_PLL_INFO(CCU_ETH_PLL, "eth_pll", "ref_clk", CCU_ETH_PLL_BASE,
CLK_IS_CRITICAL | CLK_SET_RATE_GATE)
CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0)
};
struct ccu_pll_data {
@ -78,16 +81,16 @@ struct ccu_pll_data {
struct ccu_pll *plls[CCU_PLL_NUM];
};
static struct ccu_pll_data *pll_data;
static struct ccu_pll *ccu_pll_find_desc(struct ccu_pll_data *data,
unsigned int clk_id)
{
struct ccu_pll *pll;
int idx;
for (idx = 0; idx < CCU_PLL_NUM; ++idx) {
pll = data->plls[idx];
if (pll && pll->id == clk_id)
return pll;
if (pll_info[idx].id == clk_id)
return data->plls[idx];
}
return ERR_PTR(-EINVAL);
@ -133,14 +136,16 @@ static struct clk_hw *ccu_pll_of_clk_hw_get(struct of_phandle_args *clkspec,
clk_id = clkspec->args[0];
pll = ccu_pll_find_desc(data, clk_id);
if (IS_ERR(pll)) {
if (pll != ERR_PTR(-EPROBE_DEFER))
pr_info("Invalid PLL clock ID %d specified\n", clk_id);
return ERR_CAST(pll);
}
return ccu_pll_get_clk_hw(pll);
}
static int ccu_pll_clk_register(struct ccu_pll_data *data)
static int ccu_pll_clk_register(struct ccu_pll_data *data, bool defer)
{
int idx, ret;
@ -148,6 +153,14 @@ static int ccu_pll_clk_register(struct ccu_pll_data *data)
const struct ccu_pll_info *info = &pll_info[idx];
struct ccu_pll_init_data init = {0};
/* Defer non-basic PLLs allocation for the probe stage */
if (!!(info->features & CCU_PLL_BASIC) ^ defer) {
if (!data->plls[idx])
data->plls[idx] = ERR_PTR(-EPROBE_DEFER);
continue;
}
init.id = info->id;
init.name = info->name;
init.parent_name = info->parent_name;
@ -155,6 +168,7 @@ static int ccu_pll_clk_register(struct ccu_pll_data *data)
init.sys_regs = data->sys_regs;
init.np = data->np;
init.flags = info->flags;
init.features = info->features;
data->plls[idx] = ccu_pll_hw_register(&init);
if (IS_ERR(data->plls[idx])) {
@ -165,22 +179,70 @@ static int ccu_pll_clk_register(struct ccu_pll_data *data)
}
}
return 0;
err_hw_unregister:
for (--idx; idx >= 0; --idx) {
if (!!(pll_info[idx].features & CCU_PLL_BASIC) ^ defer)
continue;
ccu_pll_hw_unregister(data->plls[idx]);
}
return ret;
}
static void ccu_pll_clk_unregister(struct ccu_pll_data *data, bool defer)
{
int idx;
/* Uninstall only the clocks registered on the specfied stage */
for (idx = 0; idx < CCU_PLL_NUM; ++idx) {
if (!!(pll_info[idx].features & CCU_PLL_BASIC) ^ defer)
continue;
ccu_pll_hw_unregister(data->plls[idx]);
}
}
static int ccu_pll_of_register(struct ccu_pll_data *data)
{
int ret;
ret = of_clk_add_hw_provider(data->np, ccu_pll_of_clk_hw_get, data);
if (ret) {
pr_err("Couldn't register PLL provider of '%s'\n",
of_node_full_name(data->np));
goto err_hw_unregister;
}
return 0;
err_hw_unregister:
for (--idx; idx >= 0; --idx)
ccu_pll_hw_unregister(data->plls[idx]);
return ret;
}
static int ccu_pll_probe(struct platform_device *pdev)
{
struct ccu_pll_data *data = pll_data;
if (!data)
return -EINVAL;
return ccu_pll_clk_register(data, false);
}
static const struct of_device_id ccu_pll_of_match[] = {
{ .compatible = "baikal,bt1-ccu-pll" },
{ }
};
static struct platform_driver ccu_pll_driver = {
.probe = ccu_pll_probe,
.driver = {
.name = "clk-ccu-pll",
.of_match_table = ccu_pll_of_match,
.suppress_bind_attrs = true,
},
};
builtin_platform_driver(ccu_pll_driver);
static __init void ccu_pll_init(struct device_node *np)
{
struct ccu_pll_data *data;
@ -194,13 +256,22 @@ static __init void ccu_pll_init(struct device_node *np)
if (ret)
goto err_free_data;
ret = ccu_pll_clk_register(data);
ret = ccu_pll_clk_register(data, true);
if (ret)
goto err_free_data;
ret = ccu_pll_of_register(data);
if (ret)
goto err_clk_unregister;
pll_data = data;
return;
err_clk_unregister:
ccu_pll_clk_unregister(data, true);
err_free_data:
ccu_pll_free_data(data);
}
CLK_OF_DECLARE(ccu_pll, "baikal,bt1-ccu-pll", ccu_pll_init);
CLK_OF_DECLARE_DRIVER(ccu_pll, "baikal,bt1-ccu-pll", ccu_pll_init);

View File

@ -30,6 +30,7 @@
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/math.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
@ -502,6 +503,8 @@ struct bcm2835_clock_data {
bool low_jitter;
u32 tcnt_mux;
bool round_up;
};
struct bcm2835_gate_data {
@ -966,7 +969,7 @@ static u32 bcm2835_clock_choose_div(struct clk_hw *hw,
return div;
}
static long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock,
static unsigned long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock,
unsigned long parent_rate,
u32 div)
{
@ -993,12 +996,34 @@ static long bcm2835_clock_rate_from_divisor(struct bcm2835_clock *clock,
return temp;
}
static unsigned long bcm2835_round_rate(unsigned long rate)
{
unsigned long scaler;
unsigned long limit;
limit = rate / 100000;
scaler = 1;
while (scaler < limit)
scaler *= 10;
/*
* If increasing a clock by less than 0.1% changes it
* from ..999.. to ..000.., round up.
*/
if ((rate + scaler - 1) / scaler % 1000 == 0)
rate = roundup(rate, scaler);
return rate;
}
static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct bcm2835_clock *clock = bcm2835_clock_from_hw(hw);
struct bcm2835_cprman *cprman = clock->cprman;
const struct bcm2835_clock_data *data = clock->data;
unsigned long rate;
u32 div;
if (data->int_bits == 0 && data->frac_bits == 0)
@ -1006,7 +1031,12 @@ static unsigned long bcm2835_clock_get_rate(struct clk_hw *hw,
div = cprman_read(cprman, data->div_reg);
return bcm2835_clock_rate_from_divisor(clock, parent_rate, div);
rate = bcm2835_clock_rate_from_divisor(clock, parent_rate, div);
if (data->round_up)
rate = bcm2835_round_rate(rate);
return rate;
}
static void bcm2835_clock_wait_busy(struct bcm2835_clock *clock)
@ -1784,7 +1814,7 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.load_mask = CM_PLLC_LOADPER,
.hold_mask = CM_PLLC_HOLDPER,
.fixed_divider = 1,
.flags = CLK_SET_RATE_PARENT),
.flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT),
/*
* PLLD is the display PLL, used to drive DSI display panels.
@ -2143,7 +2173,8 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.div_reg = CM_UARTDIV,
.int_bits = 10,
.frac_bits = 12,
.tcnt_mux = 28),
.tcnt_mux = 28,
.round_up = true),
/* TV encoder clock. Only operating frequency is 108Mhz. */
[BCM2835_CLOCK_VEC] = REGISTER_PER_CLK(

View File

@ -33,6 +33,7 @@ enum rpi_firmware_clk_id {
RPI_FIRMWARE_EMMC2_CLK_ID,
RPI_FIRMWARE_M2MC_CLK_ID,
RPI_FIRMWARE_PIXEL_BVB_CLK_ID,
RPI_FIRMWARE_VEC_CLK_ID,
RPI_FIRMWARE_NUM_CLK_ID,
};
@ -51,6 +52,7 @@ static char *rpi_firmware_clk_names[] = {
[RPI_FIRMWARE_EMMC2_CLK_ID] = "emmc2",
[RPI_FIRMWARE_M2MC_CLK_ID] = "m2mc",
[RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = "pixel-bvb",
[RPI_FIRMWARE_VEC_CLK_ID] = "vec",
};
#define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0)
@ -129,9 +131,18 @@ raspberrypi_clk_variants[RPI_FIRMWARE_NUM_CLK_ID] = {
[RPI_FIRMWARE_V3D_CLK_ID] = {
.export = true,
},
[RPI_FIRMWARE_PIXEL_CLK_ID] = {
.export = true,
},
[RPI_FIRMWARE_HEVC_CLK_ID] = {
.export = true,
},
[RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = {
.export = true,
},
[RPI_FIRMWARE_VEC_CLK_ID] = {
.export = true,
},
};
/*

View File

@ -153,6 +153,7 @@ enum vc5_model {
IDT_VC5_5P49V5935,
IDT_VC6_5P49V6901,
IDT_VC6_5P49V6965,
IDT_VC6_5P49V6975,
};
/* Structure to describe features of a particular VC5 model */
@ -230,8 +231,12 @@ static unsigned char vc5_mux_get_parent(struct clk_hw *hw)
container_of(hw, struct vc5_driver_data, clk_mux);
const u8 mask = VC5_PRIM_SRC_SHDN_EN_XTAL | VC5_PRIM_SRC_SHDN_EN_CLKIN;
unsigned int src;
int ret;
ret = regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &src);
if (ret)
return 0;
regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &src);
src &= mask;
if (src == VC5_PRIM_SRC_SHDN_EN_XTAL)
@ -286,8 +291,12 @@ static unsigned long vc5_dbl_recalc_rate(struct clk_hw *hw,
struct vc5_driver_data *vc5 =
container_of(hw, struct vc5_driver_data, clk_mul);
unsigned int premul;
int ret;
ret = regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &premul);
if (ret)
return 0;
regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &premul);
if (premul & VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ)
parent_rate *= 2;
@ -315,11 +324,9 @@ static int vc5_dbl_set_rate(struct clk_hw *hw, unsigned long rate,
else
mask = 0;
regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN,
return regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN,
VC5_PRIM_SRC_SHDN_EN_DOUBLE_XTAL_FREQ,
mask);
return 0;
}
static const struct clk_ops vc5_dbl_ops = {
@ -334,14 +341,19 @@ static unsigned long vc5_pfd_recalc_rate(struct clk_hw *hw,
struct vc5_driver_data *vc5 =
container_of(hw, struct vc5_driver_data, clk_pfd);
unsigned int prediv, div;
int ret;
regmap_read(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, &prediv);
ret = regmap_read(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, &prediv);
if (ret)
return 0;
/* The bypass_prediv is set, PLL fed from Ref_in directly. */
if (prediv & VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV)
return parent_rate;
regmap_read(vc5->regmap, VC5_REF_DIVIDER, &div);
ret = regmap_read(vc5->regmap, VC5_REF_DIVIDER, &div);
if (ret)
return 0;
/* The Sel_prediv2 is set, PLL fed from prediv2 (Ref_in / 2) */
if (div & VC5_REF_DIVIDER_SEL_PREDIV2)
@ -376,15 +388,17 @@ static int vc5_pfd_set_rate(struct clk_hw *hw, unsigned long rate,
struct vc5_driver_data *vc5 =
container_of(hw, struct vc5_driver_data, clk_pfd);
unsigned long idiv;
int ret;
u8 div;
/* CLKIN within range of PLL input, feed directly to PLL. */
if (parent_rate <= 50000000) {
regmap_update_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV,
VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV,
ret = regmap_set_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV,
VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV);
regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, 0x00);
return 0;
if (ret)
return ret;
return regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, 0x00);
}
idiv = DIV_ROUND_UP(parent_rate, rate);
@ -395,11 +409,12 @@ static int vc5_pfd_set_rate(struct clk_hw *hw, unsigned long rate,
else
div = VC5_REF_DIVIDER_REF_DIV(idiv);
regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, div);
regmap_update_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV,
VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV, 0);
ret = regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, div);
if (ret)
return ret;
return 0;
return regmap_clear_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV,
VC5_VCO_CTRL_AND_PREDIV_BYPASS_PREDIV);
}
static const struct clk_ops vc5_pfd_ops = {
@ -551,9 +566,12 @@ static int vc5_fod_set_rate(struct clk_hw *hw, unsigned long rate,
hwdata->div_int >> 4, hwdata->div_int << 4,
0
};
int ret;
regmap_bulk_write(vc5->regmap, VC5_OUT_DIV_FRAC(hwdata->num, 0),
ret = regmap_bulk_write(vc5->regmap, VC5_OUT_DIV_FRAC(hwdata->num, 0),
data, 14);
if (ret)
return ret;
/*
* Toggle magic bit in undocumented register for unknown reason.
@ -561,12 +579,13 @@ static int vc5_fod_set_rate(struct clk_hw *hw, unsigned long rate,
* datasheet somewhat implies this is needed, but the register
* and the bit is not documented.
*/
regmap_update_bits(vc5->regmap, VC5_GLOBAL_REGISTER,
VC5_GLOBAL_REGISTER_GLOBAL_RESET, 0);
regmap_update_bits(vc5->regmap, VC5_GLOBAL_REGISTER,
VC5_GLOBAL_REGISTER_GLOBAL_RESET,
ret = regmap_clear_bits(vc5->regmap, VC5_GLOBAL_REGISTER,
VC5_GLOBAL_REGISTER_GLOBAL_RESET);
if (ret)
return ret;
return regmap_set_bits(vc5->regmap, VC5_GLOBAL_REGISTER,
VC5_GLOBAL_REGISTER_GLOBAL_RESET);
return 0;
}
static const struct clk_ops vc5_fod_ops = {
@ -594,9 +613,8 @@ static int vc5_clk_out_prepare(struct clk_hw *hw)
* registers.
*/
if (vc5->chip_info->flags & VC5_HAS_BYPASS_SYNC_BIT) {
ret = regmap_update_bits(vc5->regmap,
ret = regmap_set_bits(vc5->regmap,
VC5_RESERVED_X0(hwdata->num),
VC5_RESERVED_X0_BYPASS_SYNC,
VC5_RESERVED_X0_BYPASS_SYNC);
if (ret)
return ret;
@ -606,7 +624,10 @@ static int vc5_clk_out_prepare(struct clk_hw *hw)
* If the input mux is disabled, enable it first and
* select source from matching FOD.
*/
regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src);
ret = regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src);
if (ret)
return ret;
if ((src & mask) == 0) {
src = VC5_OUT_DIV_CONTROL_RESET | VC5_OUT_DIV_CONTROL_EN_FOD;
ret = regmap_update_bits(vc5->regmap,
@ -617,18 +638,22 @@ static int vc5_clk_out_prepare(struct clk_hw *hw)
}
/* Enable the clock buffer */
regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
VC5_CLK_OUTPUT_CFG1_EN_CLKBUF,
ret = regmap_set_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
VC5_CLK_OUTPUT_CFG1_EN_CLKBUF);
if (ret)
return ret;
if (hwdata->clk_output_cfg0_mask) {
dev_dbg(&vc5->client->dev, "Update output %d mask 0x%0X val 0x%0X\n",
hwdata->num, hwdata->clk_output_cfg0_mask,
hwdata->clk_output_cfg0);
regmap_update_bits(vc5->regmap,
ret = regmap_update_bits(vc5->regmap,
VC5_CLK_OUTPUT_CFG(hwdata->num, 0),
hwdata->clk_output_cfg0_mask,
hwdata->clk_output_cfg0);
if (ret)
return ret;
}
return 0;
@ -640,8 +665,8 @@ static void vc5_clk_out_unprepare(struct clk_hw *hw)
struct vc5_driver_data *vc5 = hwdata->vc5;
/* Disable the clock buffer */
regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
VC5_CLK_OUTPUT_CFG1_EN_CLKBUF, 0);
regmap_clear_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
VC5_CLK_OUTPUT_CFG1_EN_CLKBUF);
}
static unsigned char vc5_clk_out_get_parent(struct clk_hw *hw)
@ -656,8 +681,12 @@ static unsigned char vc5_clk_out_get_parent(struct clk_hw *hw)
const u8 extclk = VC5_OUT_DIV_CONTROL_SELB_NORM |
VC5_OUT_DIV_CONTROL_SEL_EXT;
unsigned int src;
int ret;
ret = regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src);
if (ret)
return 0;
regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src);
src &= mask;
if (src == 0) /* Input mux set to DISABLED */
@ -725,6 +754,7 @@ static int vc5_map_index_to_output(const enum vc5_model model,
case IDT_VC5_5P49V5935:
case IDT_VC6_5P49V6901:
case IDT_VC6_5P49V6965:
case IDT_VC6_5P49V6975:
default:
return n;
}
@ -819,8 +849,11 @@ static int vc5_update_cap_load(struct device_node *node, struct vc5_driver_data
{
u32 value;
int mapped_value;
int ret;
if (of_property_read_u32(node, "idt,xtal-load-femtofarads", &value))
return 0;
if (!of_property_read_u32(node, "idt,xtal-load-femtofarads", &value)) {
mapped_value = vc5_map_cap_value(value);
if (mapped_value < 0)
return mapped_value;
@ -830,11 +863,13 @@ static int vc5_update_cap_load(struct device_node *node, struct vc5_driver_data
* VC5_XTAL_X1_LOAD_CAP and VC5_XTAL_X2_LOAD_CAP, so
* shift the value 2 places.
*/
regmap_update_bits(vc5->regmap, VC5_XTAL_X1_LOAD_CAP, ~0x03, mapped_value << 2);
regmap_update_bits(vc5->regmap, VC5_XTAL_X2_LOAD_CAP, ~0x03, mapped_value << 2);
}
ret = regmap_update_bits(vc5->regmap, VC5_XTAL_X1_LOAD_CAP, ~0x03,
mapped_value << 2);
if (ret)
return ret;
return 0;
return regmap_update_bits(vc5->regmap, VC5_XTAL_X2_LOAD_CAP, ~0x03,
mapped_value << 2);
}
static int vc5_update_slew(struct device_node *np_output,
@ -956,7 +991,10 @@ static int vc5_probe(struct i2c_client *client)
"could not read idt,output-enable-active\n");
}
regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, src_mask, src_val);
ret = regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, src_mask,
src_val);
if (ret)
return ret;
/* Register clock input mux */
memset(&init, 0, sizeof(init));
@ -1204,7 +1242,7 @@ static const struct vc5_chip_info idt_5p49v6901_info = {
.model = IDT_VC6_5P49V6901,
.clk_fod_cnt = 4,
.clk_out_cnt = 5,
.flags = VC5_HAS_PFD_FREQ_DBL,
.flags = VC5_HAS_PFD_FREQ_DBL | VC5_HAS_BYPASS_SYNC_BIT,
};
static const struct vc5_chip_info idt_5p49v6965_info = {
@ -1214,6 +1252,13 @@ static const struct vc5_chip_info idt_5p49v6965_info = {
.flags = VC5_HAS_BYPASS_SYNC_BIT,
};
static const struct vc5_chip_info idt_5p49v6975_info = {
.model = IDT_VC6_5P49V6975,
.clk_fod_cnt = 4,
.clk_out_cnt = 5,
.flags = VC5_HAS_BYPASS_SYNC_BIT | VC5_HAS_INTERNAL_XTAL,
};
static const struct i2c_device_id vc5_id[] = {
{ "5p49v5923", .driver_data = IDT_VC5_5P49V5923 },
{ "5p49v5925", .driver_data = IDT_VC5_5P49V5925 },
@ -1221,6 +1266,7 @@ static const struct i2c_device_id vc5_id[] = {
{ "5p49v5935", .driver_data = IDT_VC5_5P49V5935 },
{ "5p49v6901", .driver_data = IDT_VC6_5P49V6901 },
{ "5p49v6965", .driver_data = IDT_VC6_5P49V6965 },
{ "5p49v6975", .driver_data = IDT_VC6_5P49V6975 },
{ }
};
MODULE_DEVICE_TABLE(i2c, vc5_id);
@ -1232,6 +1278,7 @@ static const struct of_device_id clk_vc5_of_match[] = {
{ .compatible = "idt,5p49v5935", .data = &idt_5p49v5935_info },
{ .compatible = "idt,5p49v6901", .data = &idt_5p49v6901_info },
{ .compatible = "idt,5p49v6965", .data = &idt_5p49v6965_info },
{ .compatible = "idt,5p49v6975", .data = &idt_5p49v6975_info },
{ },
};
MODULE_DEVICE_TABLE(of, clk_vc5_of_match);

File diff suppressed because it is too large Load Diff

View File

@ -21,5 +21,14 @@
#define CCU_SYS_SATA_REF_RST 0
#define CCU_SYS_APB_RST 1
#define CCU_SYS_DDR_FULL_RST 2
#define CCU_SYS_DDR_INIT_RST 3
#define CCU_SYS_PCIE_PCS_PHY_RST 4
#define CCU_SYS_PCIE_PIPE0_RST 5
#define CCU_SYS_PCIE_CORE_RST 6
#define CCU_SYS_PCIE_PWR_RST 7
#define CCU_SYS_PCIE_STICKY_RST 8
#define CCU_SYS_PCIE_NSTICKY_RST 9
#define CCU_SYS_PCIE_HOT_RST 10
#endif /* __DT_BINDINGS_RESET_BT1_CCU_H */