mirror of
https://github.com/torvalds/linux.git
synced 2026-05-25 23:52:08 +02:00
Merge branches 'clk-cleanup', 'clk-airoha', 'clk-mediatek', 'clk-sophgo' and 'clk-loongson' into clk-next
- Airoha EN7581 SoC clk driver - Sophgo CV1800B, CV1812H and SG2000 SoC clk driver - Loongson-2k0500 and Loongson-2k2000 SoC clk driver * clk-cleanup: clk: gemini: Remove an unused field in struct clk_gemini_pci clk: highbank: Remove an unused field in struct hb_clk clk: ti: dpll: fix incorrect #ifdef checks clk: nxp: Remove an unused field in struct lpc18xx_pll * clk-airoha: clk: en7523: Add EN7581 support clk: en7523: Add en_clk_soc_data data structure dt-bindings: clock: airoha: add EN7581 binding * clk-mediatek: clk: mediatek: mt8365-mm: fix DPI0 parent clk: mediatek: pllfh: Don't log error for missing fhctl node * clk-sophgo: clk: sophgo: avoid open-coded 64-bit division clk: sophgo: Make synthesizer struct static clk: sophgo: Add clock support for SG2000 SoC clk: sophgo: Add clock support for CV1810 SoC clk: sophgo: Add clock support for CV1800 SoC dt-bindings: clock: sophgo: Add clock controller of SG2000 series SoC * clk-loongson: clk: clk-loongson2: Add Loongson-2K2000 clock support dt-bindings: clock: loongson2: Add Loongson-2K2000 compatible clk: clk-loongson2: Add Loongson-2K0500 clock support dt-bindings: clock: loongson2: Add Loongson-2K0500 compatible clk: clk-loongson2: Refactor driver for adding new platforms dt-bindings: clock: Add Loongson-2K expand clock index
This commit is contained in:
commit
5aabfd91fc
|
|
@ -29,10 +29,13 @@ description: |
|
|||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: airoha,en7523-scu
|
||||
- enum:
|
||||
- airoha,en7523-scu
|
||||
- airoha,en7581-scu
|
||||
|
||||
reg:
|
||||
maxItems: 2
|
||||
minItems: 2
|
||||
maxItems: 3
|
||||
|
||||
"#clock-cells":
|
||||
description:
|
||||
|
|
@ -45,6 +48,30 @@ required:
|
|||
- reg
|
||||
- '#clock-cells'
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
const: airoha,en7523-scu
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: scu base address
|
||||
- description: misc scu base address
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
const: airoha,en7581-scu
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: scu base address
|
||||
- description: misc scu base address
|
||||
- description: pb scu base address
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ description: |
|
|||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- loongson,ls2k-clk
|
||||
- loongson,ls2k0500-clk
|
||||
- loongson,ls2k-clk # This is for Loongson-2K1000
|
||||
- loongson,ls2k2000-clk
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
$id: http://devicetree.org/schemas/clock/sophgo,cv1800-clk.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Sophgo CV1800 Series Clock Controller
|
||||
title: Sophgo CV1800/SG2000 Series Clock Controller
|
||||
|
||||
maintainers:
|
||||
- Inochi Amaoto <inochiama@outlook.com>
|
||||
|
|
@ -14,6 +14,7 @@ properties:
|
|||
enum:
|
||||
- sophgo,cv1800-clk
|
||||
- sophgo,cv1810-clk
|
||||
- sophgo,sg2000-clk
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -489,6 +489,7 @@ source "drivers/clk/rockchip/Kconfig"
|
|||
source "drivers/clk/samsung/Kconfig"
|
||||
source "drivers/clk/sifive/Kconfig"
|
||||
source "drivers/clk/socfpga/Kconfig"
|
||||
source "drivers/clk/sophgo/Kconfig"
|
||||
source "drivers/clk/sprd/Kconfig"
|
||||
source "drivers/clk/starfive/Kconfig"
|
||||
source "drivers/clk/sunxi/Kconfig"
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
|
|||
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
|
||||
obj-$(CONFIG_CLK_SIFIVE) += sifive/
|
||||
obj-y += socfpga/
|
||||
obj-y += sophgo/
|
||||
obj-$(CONFIG_PLAT_SPEAR) += spear/
|
||||
obj-y += sprd/
|
||||
obj-$(CONFIG_ARCH_STI) += st/
|
||||
|
|
|
|||
|
|
@ -3,14 +3,16 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <dt-bindings/clock/en7523-clk.h>
|
||||
|
||||
#define REG_PCI_CONTROL 0x88
|
||||
#define REG_PCI_CONTROL_PERSTOUT BIT(29)
|
||||
#define REG_PCI_CONTROL_PERSTOUT1 BIT(26)
|
||||
#define REG_PCI_CONTROL_REFCLK_EN0 BIT(23)
|
||||
#define REG_PCI_CONTROL_REFCLK_EN1 BIT(22)
|
||||
#define REG_PCI_CONTROL_PERSTOUT2 BIT(16)
|
||||
#define REG_GSW_CLK_DIV_SEL 0x1b4
|
||||
#define REG_EMI_CLK_DIV_SEL 0x1b8
|
||||
#define REG_BUS_CLK_DIV_SEL 0x1bc
|
||||
|
|
@ -18,10 +20,25 @@
|
|||
#define REG_SPI_CLK_FREQ_SEL 0x1c8
|
||||
#define REG_NPU_CLK_DIV_SEL 0x1fc
|
||||
#define REG_CRYPTO_CLKSRC 0x200
|
||||
#define REG_RESET_CONTROL 0x834
|
||||
#define REG_RESET_CONTROL2 0x830
|
||||
#define REG_RESET2_CONTROL_PCIE2 BIT(27)
|
||||
#define REG_RESET_CONTROL1 0x834
|
||||
#define REG_RESET_CONTROL_PCIEHB BIT(29)
|
||||
#define REG_RESET_CONTROL_PCIE1 BIT(27)
|
||||
#define REG_RESET_CONTROL_PCIE2 BIT(26)
|
||||
/* EN7581 */
|
||||
#define REG_PCIE0_MEM 0x00
|
||||
#define REG_PCIE0_MEM_MASK 0x04
|
||||
#define REG_PCIE1_MEM 0x08
|
||||
#define REG_PCIE1_MEM_MASK 0x0c
|
||||
#define REG_PCIE2_MEM 0x10
|
||||
#define REG_PCIE2_MEM_MASK 0x14
|
||||
#define REG_PCIE_RESET_OPEN_DRAIN 0x018c
|
||||
#define REG_PCIE_RESET_OPEN_DRAIN_MASK GENMASK(2, 0)
|
||||
#define REG_NP_SCU_PCIC 0x88
|
||||
#define REG_NP_SCU_SSTR 0x9c
|
||||
#define REG_PCIE_XSI0_SEL_MASK GENMASK(14, 13)
|
||||
#define REG_PCIE_XSI1_SEL_MASK GENMASK(12, 11)
|
||||
|
||||
struct en_clk_desc {
|
||||
int id;
|
||||
|
|
@ -47,6 +64,12 @@ struct en_clk_gate {
|
|||
struct clk_hw hw;
|
||||
};
|
||||
|
||||
struct en_clk_soc_data {
|
||||
const struct clk_ops pcie_ops;
|
||||
int (*hw_init)(struct platform_device *pdev, void __iomem *base,
|
||||
void __iomem *np_base);
|
||||
};
|
||||
|
||||
static const u32 gsw_base[] = { 400000000, 500000000 };
|
||||
static const u32 emi_base[] = { 333000000, 400000000 };
|
||||
static const u32 bus_base[] = { 500000000, 540000000 };
|
||||
|
|
@ -145,11 +168,6 @@ static const struct en_clk_desc en7523_base_clks[] = {
|
|||
}
|
||||
};
|
||||
|
||||
static const struct of_device_id of_match_clk_en7523[] = {
|
||||
{ .compatible = "airoha,en7523-scu", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static unsigned int en7523_get_base_rate(void __iomem *base, unsigned int i)
|
||||
{
|
||||
const struct en_clk_desc *desc = &en7523_base_clks[i];
|
||||
|
|
@ -212,14 +230,14 @@ static int en7523_pci_prepare(struct clk_hw *hw)
|
|||
usleep_range(1000, 2000);
|
||||
|
||||
/* Reset to default */
|
||||
val = readl(np_base + REG_RESET_CONTROL);
|
||||
val = readl(np_base + REG_RESET_CONTROL1);
|
||||
mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2 |
|
||||
REG_RESET_CONTROL_PCIEHB;
|
||||
writel(val & ~mask, np_base + REG_RESET_CONTROL);
|
||||
writel(val & ~mask, np_base + REG_RESET_CONTROL1);
|
||||
usleep_range(1000, 2000);
|
||||
writel(val | mask, np_base + REG_RESET_CONTROL);
|
||||
writel(val | mask, np_base + REG_RESET_CONTROL1);
|
||||
msleep(100);
|
||||
writel(val & ~mask, np_base + REG_RESET_CONTROL);
|
||||
writel(val & ~mask, np_base + REG_RESET_CONTROL1);
|
||||
usleep_range(5000, 10000);
|
||||
|
||||
/* Release device */
|
||||
|
|
@ -247,14 +265,10 @@ static void en7523_pci_unprepare(struct clk_hw *hw)
|
|||
static struct clk_hw *en7523_register_pcie_clk(struct device *dev,
|
||||
void __iomem *np_base)
|
||||
{
|
||||
static const struct clk_ops pcie_gate_ops = {
|
||||
.is_enabled = en7523_pci_is_enabled,
|
||||
.prepare = en7523_pci_prepare,
|
||||
.unprepare = en7523_pci_unprepare,
|
||||
};
|
||||
const struct en_clk_soc_data *soc_data = device_get_match_data(dev);
|
||||
struct clk_init_data init = {
|
||||
.name = "pcie",
|
||||
.ops = &pcie_gate_ops,
|
||||
.ops = &soc_data->pcie_ops,
|
||||
};
|
||||
struct en_clk_gate *cg;
|
||||
|
||||
|
|
@ -264,7 +278,10 @@ static struct clk_hw *en7523_register_pcie_clk(struct device *dev,
|
|||
|
||||
cg->base = np_base;
|
||||
cg->hw.init = &init;
|
||||
en7523_pci_unprepare(&cg->hw);
|
||||
|
||||
if (init.ops->disable)
|
||||
init.ops->disable(&cg->hw);
|
||||
init.ops->unprepare(&cg->hw);
|
||||
|
||||
if (clk_hw_register(dev, &cg->hw))
|
||||
return NULL;
|
||||
|
|
@ -272,6 +289,111 @@ static struct clk_hw *en7523_register_pcie_clk(struct device *dev,
|
|||
return &cg->hw;
|
||||
}
|
||||
|
||||
static int en7581_pci_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
|
||||
u32 val, mask;
|
||||
|
||||
mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1;
|
||||
val = readl(cg->base + REG_PCI_CONTROL);
|
||||
return (val & mask) == mask;
|
||||
}
|
||||
|
||||
static int en7581_pci_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
|
||||
void __iomem *np_base = cg->base;
|
||||
u32 val, mask;
|
||||
|
||||
mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2 |
|
||||
REG_RESET_CONTROL_PCIEHB;
|
||||
val = readl(np_base + REG_RESET_CONTROL1);
|
||||
writel(val & ~mask, np_base + REG_RESET_CONTROL1);
|
||||
val = readl(np_base + REG_RESET_CONTROL2);
|
||||
writel(val & ~REG_RESET2_CONTROL_PCIE2, np_base + REG_RESET_CONTROL2);
|
||||
usleep_range(5000, 10000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int en7581_pci_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
|
||||
void __iomem *np_base = cg->base;
|
||||
u32 val, mask;
|
||||
|
||||
mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 |
|
||||
REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 |
|
||||
REG_PCI_CONTROL_PERSTOUT;
|
||||
val = readl(np_base + REG_PCI_CONTROL);
|
||||
writel(val | mask, np_base + REG_PCI_CONTROL);
|
||||
msleep(250);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void en7581_pci_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
|
||||
void __iomem *np_base = cg->base;
|
||||
u32 val, mask;
|
||||
|
||||
mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2 |
|
||||
REG_RESET_CONTROL_PCIEHB;
|
||||
val = readl(np_base + REG_RESET_CONTROL1);
|
||||
writel(val | mask, np_base + REG_RESET_CONTROL1);
|
||||
mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2;
|
||||
writel(val | mask, np_base + REG_RESET_CONTROL1);
|
||||
val = readl(np_base + REG_RESET_CONTROL2);
|
||||
writel(val | REG_RESET_CONTROL_PCIE2, np_base + REG_RESET_CONTROL2);
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
static void en7581_pci_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw);
|
||||
void __iomem *np_base = cg->base;
|
||||
u32 val, mask;
|
||||
|
||||
mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 |
|
||||
REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 |
|
||||
REG_PCI_CONTROL_PERSTOUT;
|
||||
val = readl(np_base + REG_PCI_CONTROL);
|
||||
writel(val & ~mask, np_base + REG_PCI_CONTROL);
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
static int en7581_clk_hw_init(struct platform_device *pdev,
|
||||
void __iomem *base,
|
||||
void __iomem *np_base)
|
||||
{
|
||||
void __iomem *pb_base;
|
||||
u32 val;
|
||||
|
||||
pb_base = devm_platform_ioremap_resource(pdev, 2);
|
||||
if (IS_ERR(pb_base))
|
||||
return PTR_ERR(pb_base);
|
||||
|
||||
val = readl(np_base + REG_NP_SCU_SSTR);
|
||||
val &= ~(REG_PCIE_XSI0_SEL_MASK | REG_PCIE_XSI1_SEL_MASK);
|
||||
writel(val, np_base + REG_NP_SCU_SSTR);
|
||||
val = readl(np_base + REG_NP_SCU_PCIC);
|
||||
writel(val | 3, np_base + REG_NP_SCU_PCIC);
|
||||
|
||||
writel(0x20000000, pb_base + REG_PCIE0_MEM);
|
||||
writel(0xfc000000, pb_base + REG_PCIE0_MEM_MASK);
|
||||
writel(0x24000000, pb_base + REG_PCIE1_MEM);
|
||||
writel(0xfc000000, pb_base + REG_PCIE1_MEM_MASK);
|
||||
writel(0x28000000, pb_base + REG_PCIE2_MEM);
|
||||
writel(0xfc000000, pb_base + REG_PCIE2_MEM_MASK);
|
||||
|
||||
val = readl(base + REG_PCIE_RESET_OPEN_DRAIN);
|
||||
writel(val | REG_PCIE_RESET_OPEN_DRAIN_MASK,
|
||||
base + REG_PCIE_RESET_OPEN_DRAIN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void en7523_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data,
|
||||
void __iomem *base, void __iomem *np_base)
|
||||
{
|
||||
|
|
@ -304,6 +426,7 @@ static void en7523_register_clocks(struct device *dev, struct clk_hw_onecell_dat
|
|||
static int en7523_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
const struct en_clk_soc_data *soc_data;
|
||||
struct clk_hw_onecell_data *clk_data;
|
||||
void __iomem *base, *np_base;
|
||||
int r;
|
||||
|
|
@ -316,6 +439,13 @@ static int en7523_clk_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(np_base))
|
||||
return PTR_ERR(np_base);
|
||||
|
||||
soc_data = device_get_match_data(&pdev->dev);
|
||||
if (soc_data->hw_init) {
|
||||
r = soc_data->hw_init(pdev, base, np_base);
|
||||
if (r)
|
||||
return r;
|
||||
}
|
||||
|
||||
clk_data = devm_kzalloc(&pdev->dev,
|
||||
struct_size(clk_data, hws, EN7523_NUM_CLOCKS),
|
||||
GFP_KERNEL);
|
||||
|
|
@ -333,6 +463,31 @@ static int en7523_clk_probe(struct platform_device *pdev)
|
|||
return r;
|
||||
}
|
||||
|
||||
static const struct en_clk_soc_data en7523_data = {
|
||||
.pcie_ops = {
|
||||
.is_enabled = en7523_pci_is_enabled,
|
||||
.prepare = en7523_pci_prepare,
|
||||
.unprepare = en7523_pci_unprepare,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct en_clk_soc_data en7581_data = {
|
||||
.pcie_ops = {
|
||||
.is_enabled = en7581_pci_is_enabled,
|
||||
.prepare = en7581_pci_prepare,
|
||||
.enable = en7581_pci_enable,
|
||||
.unprepare = en7581_pci_unprepare,
|
||||
.disable = en7581_pci_disable,
|
||||
},
|
||||
.hw_init = en7581_clk_hw_init,
|
||||
};
|
||||
|
||||
static const struct of_device_id of_match_clk_en7523[] = {
|
||||
{ .compatible = "airoha,en7523-scu", .data = &en7523_data },
|
||||
{ .compatible = "airoha,en7581-scu", .data = &en7581_data },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct platform_driver clk_en7523_drv = {
|
||||
.probe = en7523_clk_probe,
|
||||
.driver = {
|
||||
|
|
|
|||
|
|
@ -13,317 +13,348 @@
|
|||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <dt-bindings/clock/loongson,ls2k-clk.h>
|
||||
|
||||
#define LOONGSON2_PLL_MULT_SHIFT 32
|
||||
#define LOONGSON2_PLL_MULT_WIDTH 10
|
||||
#define LOONGSON2_PLL_DIV_SHIFT 26
|
||||
#define LOONGSON2_PLL_DIV_WIDTH 6
|
||||
#define LOONGSON2_APB_FREQSCALE_SHIFT 20
|
||||
#define LOONGSON2_APB_FREQSCALE_WIDTH 3
|
||||
#define LOONGSON2_USB_FREQSCALE_SHIFT 16
|
||||
#define LOONGSON2_USB_FREQSCALE_WIDTH 3
|
||||
#define LOONGSON2_SATA_FREQSCALE_SHIFT 12
|
||||
#define LOONGSON2_SATA_FREQSCALE_WIDTH 3
|
||||
#define LOONGSON2_BOOT_FREQSCALE_SHIFT 8
|
||||
#define LOONGSON2_BOOT_FREQSCALE_WIDTH 3
|
||||
|
||||
static void __iomem *loongson2_pll_base;
|
||||
|
||||
static const struct clk_parent_data pdata[] = {
|
||||
{ .fw_name = "ref_100m",},
|
||||
{ .fw_name = "ref_100m", },
|
||||
};
|
||||
|
||||
static struct clk_hw *loongson2_clk_register(struct device *dev,
|
||||
const char *name,
|
||||
const char *parent_name,
|
||||
const struct clk_ops *ops,
|
||||
unsigned long flags)
|
||||
{
|
||||
int ret;
|
||||
struct clk_hw *hw;
|
||||
struct clk_init_data init = { };
|
||||
|
||||
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
|
||||
if (!hw)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = ops;
|
||||
init.flags = flags;
|
||||
init.num_parents = 1;
|
||||
|
||||
if (!parent_name)
|
||||
init.parent_data = pdata;
|
||||
else
|
||||
init.parent_names = &parent_name;
|
||||
|
||||
hw->init = &init;
|
||||
|
||||
ret = devm_clk_hw_register(dev, hw);
|
||||
if (ret)
|
||||
hw = ERR_PTR(ret);
|
||||
|
||||
return hw;
|
||||
}
|
||||
|
||||
static unsigned long loongson2_calc_pll_rate(int offset, unsigned long rate)
|
||||
{
|
||||
u64 val;
|
||||
u32 mult, div;
|
||||
|
||||
val = readq(loongson2_pll_base + offset);
|
||||
|
||||
mult = (val >> LOONGSON2_PLL_MULT_SHIFT) &
|
||||
clk_div_mask(LOONGSON2_PLL_MULT_WIDTH);
|
||||
div = (val >> LOONGSON2_PLL_DIV_SHIFT) &
|
||||
clk_div_mask(LOONGSON2_PLL_DIV_WIDTH);
|
||||
|
||||
return div_u64((u64)rate * mult, div);
|
||||
}
|
||||
|
||||
static unsigned long loongson2_node_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_pll_rate(0x0, parent_rate);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_node_clk_ops = {
|
||||
.recalc_rate = loongson2_node_recalc_rate,
|
||||
enum loongson2_clk_type {
|
||||
CLK_TYPE_PLL,
|
||||
CLK_TYPE_SCALE,
|
||||
CLK_TYPE_DIVIDER,
|
||||
CLK_TYPE_GATE,
|
||||
CLK_TYPE_FIXED,
|
||||
CLK_TYPE_NONE,
|
||||
};
|
||||
|
||||
static unsigned long loongson2_ddr_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_pll_rate(0x10, parent_rate);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_ddr_clk_ops = {
|
||||
.recalc_rate = loongson2_ddr_recalc_rate,
|
||||
struct loongson2_clk_provider {
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
struct clk_hw_onecell_data clk_data;
|
||||
spinlock_t clk_lock; /* protect access to DIV registers */
|
||||
};
|
||||
|
||||
static unsigned long loongson2_dc_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_pll_rate(0x20, parent_rate);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_dc_clk_ops = {
|
||||
.recalc_rate = loongson2_dc_recalc_rate,
|
||||
struct loongson2_clk_data {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
u8 div_shift;
|
||||
u8 div_width;
|
||||
u8 mult_shift;
|
||||
u8 mult_width;
|
||||
};
|
||||
|
||||
static unsigned long loongson2_pix0_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_pll_rate(0x30, parent_rate);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_pix0_clk_ops = {
|
||||
.recalc_rate = loongson2_pix0_recalc_rate,
|
||||
struct loongson2_clk_board_info {
|
||||
u8 id;
|
||||
enum loongson2_clk_type type;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
unsigned long fixed_rate;
|
||||
u8 reg_offset;
|
||||
u8 div_shift;
|
||||
u8 div_width;
|
||||
u8 mult_shift;
|
||||
u8 mult_width;
|
||||
u8 bit_idx;
|
||||
};
|
||||
|
||||
static unsigned long loongson2_pix1_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_pll_rate(0x40, parent_rate);
|
||||
}
|
||||
#define CLK_DIV(_id, _name, _pname, _offset, _dshift, _dwidth) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.type = CLK_TYPE_DIVIDER, \
|
||||
.name = _name, \
|
||||
.parent_name = _pname, \
|
||||
.reg_offset = _offset, \
|
||||
.div_shift = _dshift, \
|
||||
.div_width = _dwidth, \
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_pix1_clk_ops = {
|
||||
.recalc_rate = loongson2_pix1_recalc_rate,
|
||||
#define CLK_PLL(_id, _name, _offset, _mshift, _mwidth, \
|
||||
_dshift, _dwidth) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.type = CLK_TYPE_PLL, \
|
||||
.name = _name, \
|
||||
.parent_name = NULL, \
|
||||
.reg_offset = _offset, \
|
||||
.mult_shift = _mshift, \
|
||||
.mult_width = _mwidth, \
|
||||
.div_shift = _dshift, \
|
||||
.div_width = _dwidth, \
|
||||
}
|
||||
|
||||
#define CLK_SCALE(_id, _name, _pname, _offset, \
|
||||
_dshift, _dwidth) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.type = CLK_TYPE_SCALE, \
|
||||
.name = _name, \
|
||||
.parent_name = _pname, \
|
||||
.reg_offset = _offset, \
|
||||
.div_shift = _dshift, \
|
||||
.div_width = _dwidth, \
|
||||
}
|
||||
|
||||
#define CLK_GATE(_id, _name, _pname, _offset, _bidx) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.type = CLK_TYPE_GATE, \
|
||||
.name = _name, \
|
||||
.parent_name = _pname, \
|
||||
.reg_offset = _offset, \
|
||||
.bit_idx = _bidx, \
|
||||
}
|
||||
|
||||
#define CLK_FIXED(_id, _name, _pname, _rate) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.type = CLK_TYPE_FIXED, \
|
||||
.name = _name, \
|
||||
.parent_name = _pname, \
|
||||
.fixed_rate = _rate, \
|
||||
}
|
||||
|
||||
static const struct loongson2_clk_board_info ls2k0500_clks[] = {
|
||||
CLK_PLL(LOONGSON2_NODE_PLL, "pll_node", 0, 16, 8, 8, 6),
|
||||
CLK_PLL(LOONGSON2_DDR_PLL, "pll_ddr", 0x8, 16, 8, 8, 6),
|
||||
CLK_PLL(LOONGSON2_DC_PLL, "pll_soc", 0x10, 16, 8, 8, 6),
|
||||
CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x18, 16, 8, 8, 6),
|
||||
CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x20, 16, 8, 8, 6),
|
||||
CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "pll_node", 0, 24, 6),
|
||||
CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "pll_ddr", 0x8, 24, 6),
|
||||
CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "pll_ddr", 0xc, 8, 6),
|
||||
CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "pll_soc", 0x10, 24, 6),
|
||||
CLK_DIV(LOONGSON2_DC_CLK, "clk_sb", "pll_soc", 0x14, 0, 6),
|
||||
CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "pll_soc", 0x14, 8, 6),
|
||||
CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x18, 24, 6),
|
||||
CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x20, 24, 6),
|
||||
CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", "clk_sb", 0x28, 8, 3),
|
||||
CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_sb", 0x28, 12, 3),
|
||||
CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_sb", 0x28, 16, 3),
|
||||
CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_sb", 0x28, 20, 3),
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
static unsigned long loongson2_calc_rate(unsigned long rate,
|
||||
int shift, int width)
|
||||
{
|
||||
u64 val;
|
||||
u32 mult;
|
||||
|
||||
val = readq(loongson2_pll_base + 0x50);
|
||||
|
||||
mult = (val >> shift) & clk_div_mask(width);
|
||||
|
||||
return div_u64((u64)rate * (mult + 1), 8);
|
||||
}
|
||||
|
||||
static unsigned long loongson2_boot_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_rate(parent_rate,
|
||||
LOONGSON2_BOOT_FREQSCALE_SHIFT,
|
||||
LOONGSON2_BOOT_FREQSCALE_WIDTH);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_boot_clk_ops = {
|
||||
.recalc_rate = loongson2_boot_recalc_rate,
|
||||
};
|
||||
|
||||
static unsigned long loongson2_apb_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_rate(parent_rate,
|
||||
LOONGSON2_APB_FREQSCALE_SHIFT,
|
||||
LOONGSON2_APB_FREQSCALE_WIDTH);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_apb_clk_ops = {
|
||||
.recalc_rate = loongson2_apb_recalc_rate,
|
||||
};
|
||||
|
||||
static unsigned long loongson2_usb_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_rate(parent_rate,
|
||||
LOONGSON2_USB_FREQSCALE_SHIFT,
|
||||
LOONGSON2_USB_FREQSCALE_WIDTH);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_usb_clk_ops = {
|
||||
.recalc_rate = loongson2_usb_recalc_rate,
|
||||
};
|
||||
|
||||
static unsigned long loongson2_sata_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return loongson2_calc_rate(parent_rate,
|
||||
LOONGSON2_SATA_FREQSCALE_SHIFT,
|
||||
LOONGSON2_SATA_FREQSCALE_WIDTH);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_sata_clk_ops = {
|
||||
.recalc_rate = loongson2_sata_recalc_rate,
|
||||
};
|
||||
|
||||
static inline int loongson2_check_clk_hws(struct clk_hw *clks[], unsigned int count)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
if (IS_ERR(clks[i])) {
|
||||
pr_err("Loongson2 clk %u: register failed with %ld\n",
|
||||
i, PTR_ERR(clks[i]));
|
||||
return PTR_ERR(clks[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int loongson2_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct clk_hw **hws;
|
||||
struct clk_hw_onecell_data *clk_hw_data;
|
||||
spinlock_t loongson2_clk_lock;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
loongson2_pll_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(loongson2_pll_base))
|
||||
return PTR_ERR(loongson2_pll_base);
|
||||
|
||||
clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, LOONGSON2_CLK_END),
|
||||
GFP_KERNEL);
|
||||
if (WARN_ON(!clk_hw_data))
|
||||
return -ENOMEM;
|
||||
|
||||
clk_hw_data->num = LOONGSON2_CLK_END;
|
||||
hws = clk_hw_data->hws;
|
||||
|
||||
hws[LOONGSON2_NODE_PLL] = loongson2_clk_register(dev, "node_pll",
|
||||
NULL,
|
||||
&loongson2_node_clk_ops, 0);
|
||||
|
||||
hws[LOONGSON2_DDR_PLL] = loongson2_clk_register(dev, "ddr_pll",
|
||||
NULL,
|
||||
&loongson2_ddr_clk_ops, 0);
|
||||
|
||||
hws[LOONGSON2_DC_PLL] = loongson2_clk_register(dev, "dc_pll",
|
||||
NULL,
|
||||
&loongson2_dc_clk_ops, 0);
|
||||
|
||||
hws[LOONGSON2_PIX0_PLL] = loongson2_clk_register(dev, "pix0_pll",
|
||||
NULL,
|
||||
&loongson2_pix0_clk_ops, 0);
|
||||
|
||||
hws[LOONGSON2_PIX1_PLL] = loongson2_clk_register(dev, "pix1_pll",
|
||||
NULL,
|
||||
&loongson2_pix1_clk_ops, 0);
|
||||
|
||||
hws[LOONGSON2_BOOT_CLK] = loongson2_clk_register(dev, "boot",
|
||||
NULL,
|
||||
&loongson2_boot_clk_ops, 0);
|
||||
|
||||
hws[LOONGSON2_NODE_CLK] = devm_clk_hw_register_divider(dev, "node",
|
||||
"node_pll", 0,
|
||||
loongson2_pll_base + 0x8, 0,
|
||||
6, CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
|
||||
static const struct loongson2_clk_board_info ls2k1000_clks[] = {
|
||||
CLK_PLL(LOONGSON2_NODE_PLL, "pll_node", 0, 32, 10, 26, 6),
|
||||
CLK_PLL(LOONGSON2_DDR_PLL, "pll_ddr", 0x10, 32, 10, 26, 6),
|
||||
CLK_PLL(LOONGSON2_DC_PLL, "pll_dc", 0x20, 32, 10, 26, 6),
|
||||
CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x30, 32, 10, 26, 6),
|
||||
CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x40, 32, 10, 26, 6),
|
||||
CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "pll_node", 0x8, 0, 6),
|
||||
CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "pll_ddr", 0x18, 0, 6),
|
||||
CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "pll_ddr", 0x18, 22, 6),
|
||||
/*
|
||||
* The hda clk divisor in the upper 32bits and the clk-prodiver
|
||||
* layer code doesn't support 64bit io operation thus a conversion
|
||||
* is required that subtract shift by 32 and add 4byte to the hda
|
||||
* address
|
||||
*/
|
||||
hws[LOONGSON2_HDA_CLK] = devm_clk_hw_register_divider(dev, "hda",
|
||||
"ddr_pll", 0,
|
||||
loongson2_pll_base + 0x22, 12,
|
||||
7, CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "pll_ddr", 0x22, 12, 7),
|
||||
CLK_DIV(LOONGSON2_DC_CLK, "clk_dc", "pll_dc", 0x28, 0, 6),
|
||||
CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "pll_dc", 0x28, 22, 6),
|
||||
CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x38, 0, 6),
|
||||
CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x38, 0, 6),
|
||||
CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL, 0x50, 8, 3),
|
||||
CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_gmac", 0x50, 12, 3),
|
||||
CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_gmac", 0x50, 16, 3),
|
||||
CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_gmac", 0x50, 20, 3),
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
hws[LOONGSON2_GPU_CLK] = devm_clk_hw_register_divider(dev, "gpu",
|
||||
"ddr_pll", 0,
|
||||
loongson2_pll_base + 0x18, 22,
|
||||
6, CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
static const struct loongson2_clk_board_info ls2k2000_clks[] = {
|
||||
CLK_PLL(LOONGSON2_DC_PLL, "pll_0", 0, 21, 9, 32, 6),
|
||||
CLK_PLL(LOONGSON2_DDR_PLL, "pll_1", 0x10, 21, 9, 32, 6),
|
||||
CLK_PLL(LOONGSON2_NODE_PLL, "pll_2", 0x20, 21, 9, 32, 6),
|
||||
CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x30, 21, 9, 32, 6),
|
||||
CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x40, 21, 9, 32, 6),
|
||||
CLK_GATE(LOONGSON2_OUT0_GATE, "out0_gate", "pll_0", 0, 40),
|
||||
CLK_GATE(LOONGSON2_GMAC_GATE, "gmac_gate", "pll_0", 0, 41),
|
||||
CLK_GATE(LOONGSON2_RIO_GATE, "rio_gate", "pll_0", 0, 42),
|
||||
CLK_GATE(LOONGSON2_DC_GATE, "dc_gate", "pll_1", 0x10, 40),
|
||||
CLK_GATE(LOONGSON2_DDR_GATE, "ddr_gate", "pll_1", 0x10, 41),
|
||||
CLK_GATE(LOONGSON2_GPU_GATE, "gpu_gate", "pll_1", 0x10, 42),
|
||||
CLK_GATE(LOONGSON2_HDA_GATE, "hda_gate", "pll_2", 0x20, 40),
|
||||
CLK_GATE(LOONGSON2_NODE_GATE, "node_gate", "pll_2", 0x20, 41),
|
||||
CLK_GATE(LOONGSON2_EMMC_GATE, "emmc_gate", "pll_2", 0x20, 42),
|
||||
CLK_GATE(LOONGSON2_PIX0_GATE, "pix0_gate", "pll_pix0", 0x30, 40),
|
||||
CLK_GATE(LOONGSON2_PIX1_GATE, "pix1_gate", "pll_pix1", 0x40, 40),
|
||||
CLK_DIV(LOONGSON2_OUT0_CLK, "clk_out0", "out0_gate", 0, 0, 6),
|
||||
CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "gmac_gate", 0, 7, 6),
|
||||
CLK_DIV(LOONGSON2_RIO_CLK, "clk_rio", "rio_gate", 0, 14, 6),
|
||||
CLK_DIV(LOONGSON2_DC_CLK, "clk_dc", "dc_gate", 0x10, 0, 6),
|
||||
CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "gpu_gate", 0x10, 7, 6),
|
||||
CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "ddr_gate", 0x10, 14, 6),
|
||||
CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "hda_gate", 0x20, 0, 6),
|
||||
CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "node_gate", 0x20, 7, 6),
|
||||
CLK_DIV(LOONGSON2_EMMC_CLK, "clk_emmc", "emmc_gate", 0x20, 14, 6),
|
||||
CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x30, 0, 6),
|
||||
CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x40, 0, 6),
|
||||
CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_out0", 0x50, 12, 3),
|
||||
CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_out0", 0x50, 16, 3),
|
||||
CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_node", 0x50, 20, 3),
|
||||
CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL, 0x50, 23, 3),
|
||||
CLK_SCALE(LOONGSON2_DES_CLK, "clk_des", "clk_node", 0x50, 40, 3),
|
||||
CLK_SCALE(LOONGSON2_I2S_CLK, "clk_i2s", "clk_node", 0x50, 44, 3),
|
||||
CLK_FIXED(LOONGSON2_MISC_CLK, "clk_misc", NULL, 50000000),
|
||||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
hws[LOONGSON2_DDR_CLK] = devm_clk_hw_register_divider(dev, "ddr",
|
||||
"ddr_pll", 0,
|
||||
loongson2_pll_base + 0x18, 0,
|
||||
6, CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
static inline struct loongson2_clk_data *to_loongson2_clk(struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct loongson2_clk_data, hw);
|
||||
}
|
||||
|
||||
hws[LOONGSON2_GMAC_CLK] = devm_clk_hw_register_divider(dev, "gmac",
|
||||
"dc_pll", 0,
|
||||
loongson2_pll_base + 0x28, 22,
|
||||
6, CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
static inline unsigned long loongson2_rate_part(u64 val, u8 shift, u8 width)
|
||||
{
|
||||
return (val & GENMASK(shift + width - 1, shift)) >> shift;
|
||||
}
|
||||
|
||||
hws[LOONGSON2_DC_CLK] = devm_clk_hw_register_divider(dev, "dc",
|
||||
"dc_pll", 0,
|
||||
loongson2_pll_base + 0x28, 0,
|
||||
6, CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
static unsigned long loongson2_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u64 val, mult, div;
|
||||
struct loongson2_clk_data *clk = to_loongson2_clk(hw);
|
||||
|
||||
hws[LOONGSON2_APB_CLK] = loongson2_clk_register(dev, "apb",
|
||||
"gmac",
|
||||
&loongson2_apb_clk_ops, 0);
|
||||
val = readq(clk->reg);
|
||||
mult = loongson2_rate_part(val, clk->mult_shift, clk->mult_width);
|
||||
div = loongson2_rate_part(val, clk->div_shift, clk->div_width);
|
||||
|
||||
hws[LOONGSON2_USB_CLK] = loongson2_clk_register(dev, "usb",
|
||||
"gmac",
|
||||
&loongson2_usb_clk_ops, 0);
|
||||
return div_u64((u64)parent_rate * mult, div);
|
||||
}
|
||||
|
||||
hws[LOONGSON2_SATA_CLK] = loongson2_clk_register(dev, "sata",
|
||||
"gmac",
|
||||
&loongson2_sata_clk_ops, 0);
|
||||
static const struct clk_ops loongson2_pll_recalc_ops = {
|
||||
.recalc_rate = loongson2_pll_recalc_rate,
|
||||
};
|
||||
|
||||
hws[LOONGSON2_PIX0_CLK] = clk_hw_register_divider(NULL, "pix0",
|
||||
"pix0_pll", 0,
|
||||
loongson2_pll_base + 0x38, 0, 6,
|
||||
CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
static unsigned long loongson2_freqscale_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u64 val, mult;
|
||||
struct loongson2_clk_data *clk = to_loongson2_clk(hw);
|
||||
|
||||
hws[LOONGSON2_PIX1_CLK] = clk_hw_register_divider(NULL, "pix1",
|
||||
"pix1_pll", 0,
|
||||
loongson2_pll_base + 0x48, 0, 6,
|
||||
CLK_DIVIDER_ONE_BASED,
|
||||
&loongson2_clk_lock);
|
||||
val = readq(clk->reg);
|
||||
mult = loongson2_rate_part(val, clk->div_shift, clk->div_width) + 1;
|
||||
|
||||
ret = loongson2_check_clk_hws(hws, LOONGSON2_CLK_END);
|
||||
return div_u64((u64)parent_rate * mult, 8);
|
||||
}
|
||||
|
||||
static const struct clk_ops loongson2_freqscale_recalc_ops = {
|
||||
.recalc_rate = loongson2_freqscale_recalc_rate,
|
||||
};
|
||||
|
||||
static struct clk_hw *loongson2_clk_register(struct loongson2_clk_provider *clp,
|
||||
const struct loongson2_clk_board_info *cld,
|
||||
const struct clk_ops *ops)
|
||||
{
|
||||
int ret;
|
||||
struct clk_hw *hw;
|
||||
struct loongson2_clk_data *clk;
|
||||
struct clk_init_data init = { };
|
||||
|
||||
clk = devm_kzalloc(clp->dev, sizeof(*clk), GFP_KERNEL);
|
||||
if (!clk)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = cld->name;
|
||||
init.ops = ops;
|
||||
init.flags = 0;
|
||||
init.num_parents = 1;
|
||||
|
||||
if (!cld->parent_name)
|
||||
init.parent_data = pdata;
|
||||
else
|
||||
init.parent_names = &cld->parent_name;
|
||||
|
||||
clk->reg = clp->base + cld->reg_offset;
|
||||
clk->div_shift = cld->div_shift;
|
||||
clk->div_width = cld->div_width;
|
||||
clk->mult_shift = cld->mult_shift;
|
||||
clk->mult_width = cld->mult_width;
|
||||
clk->hw.init = &init;
|
||||
|
||||
hw = &clk->hw;
|
||||
ret = devm_clk_hw_register(clp->dev, hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
clk = ERR_PTR(ret);
|
||||
|
||||
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_hw_data);
|
||||
return hw;
|
||||
}
|
||||
|
||||
static int loongson2_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
int i, clks_num = 0;
|
||||
struct clk_hw *hw;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct loongson2_clk_provider *clp;
|
||||
const struct loongson2_clk_board_info *p, *data;
|
||||
|
||||
data = device_get_match_data(dev);
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
for (p = data; p->name; p++)
|
||||
clks_num++;
|
||||
|
||||
clp = devm_kzalloc(dev, struct_size(clp, clk_data.hws, clks_num),
|
||||
GFP_KERNEL);
|
||||
if (!clp)
|
||||
return -ENOMEM;
|
||||
|
||||
clp->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(clp->base))
|
||||
return PTR_ERR(clp->base);
|
||||
|
||||
spin_lock_init(&clp->clk_lock);
|
||||
clp->clk_data.num = clks_num + 1;
|
||||
clp->dev = dev;
|
||||
|
||||
for (i = 0; i < clks_num; i++) {
|
||||
p = &data[i];
|
||||
switch (p->type) {
|
||||
case CLK_TYPE_PLL:
|
||||
hw = loongson2_clk_register(clp, p,
|
||||
&loongson2_pll_recalc_ops);
|
||||
break;
|
||||
case CLK_TYPE_SCALE:
|
||||
hw = loongson2_clk_register(clp, p,
|
||||
&loongson2_freqscale_recalc_ops);
|
||||
break;
|
||||
case CLK_TYPE_DIVIDER:
|
||||
hw = devm_clk_hw_register_divider(dev, p->name,
|
||||
p->parent_name, 0,
|
||||
clp->base + p->reg_offset,
|
||||
p->div_shift, p->div_width,
|
||||
CLK_DIVIDER_ONE_BASED,
|
||||
&clp->clk_lock);
|
||||
break;
|
||||
case CLK_TYPE_GATE:
|
||||
hw = devm_clk_hw_register_gate(dev, p->name, p->parent_name, 0,
|
||||
clp->base + p->reg_offset,
|
||||
p->bit_idx, 0,
|
||||
&clp->clk_lock);
|
||||
break;
|
||||
case CLK_TYPE_FIXED:
|
||||
hw = clk_hw_register_fixed_rate_parent_data(dev, p->name, pdata,
|
||||
0, p->fixed_rate);
|
||||
break;
|
||||
default:
|
||||
return dev_err_probe(dev, -EINVAL, "Invalid clk type\n");
|
||||
}
|
||||
|
||||
if (IS_ERR(hw))
|
||||
return dev_err_probe(dev, PTR_ERR(hw),
|
||||
"Register clk: %s, type: %u failed!\n",
|
||||
p->name, p->type);
|
||||
|
||||
clp->clk_data.hws[p->id] = hw;
|
||||
}
|
||||
|
||||
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clp->clk_data);
|
||||
}
|
||||
|
||||
static const struct of_device_id loongson2_clk_match_table[] = {
|
||||
{ .compatible = "loongson,ls2k-clk" },
|
||||
{ .compatible = "loongson,ls2k0500-clk", .data = &ls2k0500_clks },
|
||||
{ .compatible = "loongson,ls2k-clk", .data = &ls2k1000_clks },
|
||||
{ .compatible = "loongson,ls2k2000-clk", .data = &ls2k2000_clks },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, loongson2_clk_match_table);
|
||||
|
|
@ -338,4 +369,5 @@ static struct platform_driver loongson2_clk_driver = {
|
|||
module_platform_driver(loongson2_clk_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Loongson2 clock driver");
|
||||
MODULE_AUTHOR("Loongson Technology Corporation Limited");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ static const struct mtk_gate mm_clks[] = {
|
|||
GATE_MM0(CLK_MM_MM_DSI0, "mm_dsi0", "mm_sel", 17),
|
||||
GATE_MM0(CLK_MM_MM_DISP_RDMA1, "mm_disp_rdma1", "mm_sel", 18),
|
||||
GATE_MM0(CLK_MM_MM_MDP_RDMA1, "mm_mdp_rdma1", "mm_sel", 19),
|
||||
GATE_MM0(CLK_MM_DPI0_DPI0, "mm_dpi0_dpi0", "vpll_dpix", 20),
|
||||
GATE_MM0(CLK_MM_DPI0_DPI0, "mm_dpi0_dpi0", "dpi0_sel", 20),
|
||||
GATE_MM0(CLK_MM_MM_FAKE, "mm_fake", "mm_sel", 21),
|
||||
GATE_MM0(CLK_MM_MM_SMI_COMMON, "mm_smi_common", "mm_sel", 22),
|
||||
GATE_MM0(CLK_MM_MM_SMI_LARB0, "mm_smi_larb0", "mm_sel", 23),
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ void fhctl_parse_dt(const u8 *compatible_node, struct mtk_pllfh_data *pllfhs,
|
|||
|
||||
node = of_find_compatible_node(NULL, NULL, compatible_node);
|
||||
if (!node) {
|
||||
pr_err("cannot find \"%s\"\n", compatible_node);
|
||||
pr_warn("cannot find \"%s\"\n", compatible_node);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
11
drivers/clk/sophgo/Kconfig
Normal file
11
drivers/clk/sophgo/Kconfig
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
# common clock support for SOPHGO SoC family.
|
||||
|
||||
config CLK_SOPHGO_CV1800
|
||||
tristate "Support for the Sophgo CV1800 series SoCs clock controller"
|
||||
depends on ARCH_SOPHGO || COMPILE_TEST
|
||||
help
|
||||
This driver supports clock controller of Sophgo CV18XX series SoC.
|
||||
The driver require a 25MHz Oscillator to function generate clock.
|
||||
It includes PLLs, common clock function and some vendor clock for
|
||||
IPs of CV18XX series SoC
|
||||
7
drivers/clk/sophgo/Makefile
Normal file
7
drivers/clk/sophgo/Makefile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_CLK_SOPHGO_CV1800) += clk-sophgo-cv1800.o
|
||||
|
||||
clk-sophgo-cv1800-y += clk-cv1800.o
|
||||
clk-sophgo-cv1800-y += clk-cv18xx-common.o
|
||||
clk-sophgo-cv1800-y += clk-cv18xx-ip.o
|
||||
clk-sophgo-cv1800-y += clk-cv18xx-pll.o
|
||||
1537
drivers/clk/sophgo/clk-cv1800.c
Normal file
1537
drivers/clk/sophgo/clk-cv1800.c
Normal file
File diff suppressed because it is too large
Load Diff
123
drivers/clk/sophgo/clk-cv1800.h
Normal file
123
drivers/clk/sophgo/clk-cv1800.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
|
||||
*/
|
||||
|
||||
#ifndef _CLK_SOPHGO_CV1800_H_
|
||||
#define _CLK_SOPHGO_CV1800_H_
|
||||
|
||||
#include <dt-bindings/clock/sophgo,cv1800.h>
|
||||
|
||||
#define CV1800_CLK_MAX (CLK_XTAL_AP + 1)
|
||||
#define CV1810_CLK_MAX (CLK_DISP_SRC_VIP + 1)
|
||||
|
||||
#define REG_PLL_G2_CTRL 0x800
|
||||
#define REG_PLL_G2_STATUS 0x804
|
||||
#define REG_MIPIMPLL_CSR 0x808
|
||||
#define REG_A0PLL_CSR 0x80C
|
||||
#define REG_DISPPLL_CSR 0x810
|
||||
#define REG_CAM0PLL_CSR 0x814
|
||||
#define REG_CAM1PLL_CSR 0x818
|
||||
#define REG_PLL_G2_SSC_SYN_CTRL 0x840
|
||||
#define REG_A0PLL_SSC_SYN_CTRL 0x850
|
||||
#define REG_A0PLL_SSC_SYN_SET 0x854
|
||||
#define REG_A0PLL_SSC_SYN_SPAN 0x858
|
||||
#define REG_A0PLL_SSC_SYN_STEP 0x85C
|
||||
#define REG_DISPPLL_SSC_SYN_CTRL 0x860
|
||||
#define REG_DISPPLL_SSC_SYN_SET 0x864
|
||||
#define REG_DISPPLL_SSC_SYN_SPAN 0x868
|
||||
#define REG_DISPPLL_SSC_SYN_STEP 0x86C
|
||||
#define REG_CAM0PLL_SSC_SYN_CTRL 0x870
|
||||
#define REG_CAM0PLL_SSC_SYN_SET 0x874
|
||||
#define REG_CAM0PLL_SSC_SYN_SPAN 0x878
|
||||
#define REG_CAM0PLL_SSC_SYN_STEP 0x87C
|
||||
#define REG_CAM1PLL_SSC_SYN_CTRL 0x880
|
||||
#define REG_CAM1PLL_SSC_SYN_SET 0x884
|
||||
#define REG_CAM1PLL_SSC_SYN_SPAN 0x888
|
||||
#define REG_CAM1PLL_SSC_SYN_STEP 0x88C
|
||||
#define REG_APLL_FRAC_DIV_CTRL 0x890
|
||||
#define REG_APLL_FRAC_DIV_M 0x894
|
||||
#define REG_APLL_FRAC_DIV_N 0x898
|
||||
#define REG_MIPIMPLL_CLK_CSR 0x8A0
|
||||
#define REG_A0PLL_CLK_CSR 0x8A4
|
||||
#define REG_DISPPLL_CLK_CSR 0x8A8
|
||||
#define REG_CAM0PLL_CLK_CSR 0x8AC
|
||||
#define REG_CAM1PLL_CLK_CSR 0x8B0
|
||||
#define REG_CLK_CAM0_SRC_DIV 0x8C0
|
||||
#define REG_CLK_CAM1_SRC_DIV 0x8C4
|
||||
|
||||
/* top_pll_g6 */
|
||||
#define REG_PLL_G6_CTRL 0x900
|
||||
#define REG_PLL_G6_STATUS 0x904
|
||||
#define REG_MPLL_CSR 0x908
|
||||
#define REG_TPLL_CSR 0x90C
|
||||
#define REG_FPLL_CSR 0x910
|
||||
#define REG_PLL_G6_SSC_SYN_CTRL 0x940
|
||||
#define REG_DPLL_SSC_SYN_CTRL 0x950
|
||||
#define REG_DPLL_SSC_SYN_SET 0x954
|
||||
#define REG_DPLL_SSC_SYN_SPAN 0x958
|
||||
#define REG_DPLL_SSC_SYN_STEP 0x95C
|
||||
#define REG_MPLL_SSC_SYN_CTRL 0x960
|
||||
#define REG_MPLL_SSC_SYN_SET 0x964
|
||||
#define REG_MPLL_SSC_SYN_SPAN 0x968
|
||||
#define REG_MPLL_SSC_SYN_STEP 0x96C
|
||||
#define REG_TPLL_SSC_SYN_CTRL 0x970
|
||||
#define REG_TPLL_SSC_SYN_SET 0x974
|
||||
#define REG_TPLL_SSC_SYN_SPAN 0x978
|
||||
#define REG_TPLL_SSC_SYN_STEP 0x97C
|
||||
|
||||
/* clkgen */
|
||||
#define REG_CLK_EN_0 0x000
|
||||
#define REG_CLK_EN_1 0x004
|
||||
#define REG_CLK_EN_2 0x008
|
||||
#define REG_CLK_EN_3 0x00C
|
||||
#define REG_CLK_EN_4 0x010
|
||||
#define REG_CLK_SEL_0 0x020
|
||||
#define REG_CLK_BYP_0 0x030
|
||||
#define REG_CLK_BYP_1 0x034
|
||||
|
||||
#define REG_DIV_CLK_A53_0 0x040
|
||||
#define REG_DIV_CLK_A53_1 0x044
|
||||
#define REG_DIV_CLK_CPU_AXI0 0x048
|
||||
#define REG_DIV_CLK_CPU_GIC 0x050
|
||||
#define REG_DIV_CLK_TPU 0x054
|
||||
#define REG_DIV_CLK_EMMC 0x064
|
||||
#define REG_DIV_CLK_EMMC_100K 0x06C
|
||||
#define REG_DIV_CLK_SD0 0x070
|
||||
#define REG_DIV_CLK_SD0_100K 0x078
|
||||
#define REG_DIV_CLK_SD1 0x07C
|
||||
#define REG_DIV_CLK_SD1_100K 0x084
|
||||
#define REG_DIV_CLK_SPI_NAND 0x088
|
||||
#define REG_DIV_CLK_ETH0_500M 0x08C
|
||||
#define REG_DIV_CLK_ETH1_500M 0x090
|
||||
#define REG_DIV_CLK_GPIO_DB 0x094
|
||||
#define REG_DIV_CLK_SDMA_AUD0 0x098
|
||||
#define REG_DIV_CLK_SDMA_AUD1 0x09C
|
||||
#define REG_DIV_CLK_SDMA_AUD2 0x0A0
|
||||
#define REG_DIV_CLK_SDMA_AUD3 0x0A4
|
||||
#define REG_DIV_CLK_CAM0_200 0x0A8
|
||||
#define REG_DIV_CLK_AXI4 0x0B8
|
||||
#define REG_DIV_CLK_AXI6 0x0BC
|
||||
#define REG_DIV_CLK_DSI_ESC 0x0C4
|
||||
#define REG_DIV_CLK_AXI_VIP 0x0C8
|
||||
#define REG_DIV_CLK_SRC_VIP_SYS_0 0x0D0
|
||||
#define REG_DIV_CLK_SRC_VIP_SYS_1 0x0D8
|
||||
#define REG_DIV_CLK_DISP_SRC_VIP 0x0E0
|
||||
#define REG_DIV_CLK_AXI_VIDEO_CODEC 0x0E4
|
||||
#define REG_DIV_CLK_VC_SRC0 0x0EC
|
||||
#define REG_DIV_CLK_1M 0x0FC
|
||||
#define REG_DIV_CLK_SPI 0x100
|
||||
#define REG_DIV_CLK_I2C 0x104
|
||||
#define REG_DIV_CLK_SRC_VIP_SYS_2 0x110
|
||||
#define REG_DIV_CLK_AUDSRC 0x118
|
||||
#define REG_DIV_CLK_PWM_SRC_0 0x120
|
||||
#define REG_DIV_CLK_AP_DEBUG 0x128
|
||||
#define REG_DIV_CLK_RTCSYS_SRC_0 0x12C
|
||||
#define REG_DIV_CLK_C906_0_0 0x130
|
||||
#define REG_DIV_CLK_C906_0_1 0x134
|
||||
#define REG_DIV_CLK_C906_1_0 0x138
|
||||
#define REG_DIV_CLK_C906_1_1 0x13C
|
||||
#define REG_DIV_CLK_SRC_VIP_SYS_3 0x140
|
||||
#define REG_DIV_CLK_SRC_VIP_SYS_4 0x144
|
||||
|
||||
#endif /* _CLK_SOPHGO_CV1800_H_ */
|
||||
66
drivers/clk/sophgo/clk-cv18xx-common.c
Normal file
66
drivers/clk/sophgo/clk-cv18xx-common.c
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/bug.h>
|
||||
|
||||
#include "clk-cv18xx-common.h"
|
||||
|
||||
int cv1800_clk_setbit(struct cv1800_clk_common *common,
|
||||
struct cv1800_clk_regbit *field)
|
||||
{
|
||||
u32 mask = BIT(field->shift);
|
||||
u32 value;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(common->lock, flags);
|
||||
|
||||
value = readl(common->base + field->reg);
|
||||
writel(value | mask, common->base + field->reg);
|
||||
|
||||
spin_unlock_irqrestore(common->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cv1800_clk_clearbit(struct cv1800_clk_common *common,
|
||||
struct cv1800_clk_regbit *field)
|
||||
{
|
||||
u32 mask = BIT(field->shift);
|
||||
u32 value;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(common->lock, flags);
|
||||
|
||||
value = readl(common->base + field->reg);
|
||||
writel(value & ~mask, common->base + field->reg);
|
||||
|
||||
spin_unlock_irqrestore(common->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cv1800_clk_checkbit(struct cv1800_clk_common *common,
|
||||
struct cv1800_clk_regbit *field)
|
||||
{
|
||||
return readl(common->base + field->reg) & BIT(field->shift);
|
||||
}
|
||||
|
||||
#define PLL_LOCK_TIMEOUT_US (200 * 1000)
|
||||
|
||||
void cv1800_clk_wait_for_lock(struct cv1800_clk_common *common,
|
||||
u32 reg, u32 lock)
|
||||
{
|
||||
void __iomem *addr = common->base + reg;
|
||||
u32 regval;
|
||||
|
||||
if (!lock)
|
||||
return;
|
||||
|
||||
WARN_ON(readl_relaxed_poll_timeout(addr, regval, regval & lock,
|
||||
100, PLL_LOCK_TIMEOUT_US));
|
||||
}
|
||||
81
drivers/clk/sophgo/clk-cv18xx-common.h
Normal file
81
drivers/clk/sophgo/clk-cv18xx-common.h
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
|
||||
*/
|
||||
|
||||
#ifndef _CLK_SOPHGO_CV18XX_IP_H_
|
||||
#define _CLK_SOPHGO_CV18XX_IP_H_
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
struct cv1800_clk_common {
|
||||
void __iomem *base;
|
||||
spinlock_t *lock;
|
||||
struct clk_hw hw;
|
||||
unsigned long features;
|
||||
};
|
||||
|
||||
#define CV1800_CLK_COMMON(_name, _parents, _op, _flags) \
|
||||
{ \
|
||||
.hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parents, \
|
||||
_op, _flags), \
|
||||
}
|
||||
|
||||
static inline struct cv1800_clk_common *
|
||||
hw_to_cv1800_clk_common(struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct cv1800_clk_common, hw);
|
||||
}
|
||||
|
||||
struct cv1800_clk_regbit {
|
||||
u16 reg;
|
||||
s8 shift;
|
||||
};
|
||||
|
||||
struct cv1800_clk_regfield {
|
||||
u16 reg;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
s16 initval;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
#define CV1800_CLK_BIT(_reg, _shift) \
|
||||
{ \
|
||||
.reg = _reg, \
|
||||
.shift = _shift, \
|
||||
}
|
||||
|
||||
#define CV1800_CLK_REG(_reg, _shift, _width, _initval, _flags) \
|
||||
{ \
|
||||
.reg = _reg, \
|
||||
.shift = _shift, \
|
||||
.width = _width, \
|
||||
.initval = _initval, \
|
||||
.flags = _flags, \
|
||||
}
|
||||
|
||||
#define cv1800_clk_regfield_genmask(_reg) \
|
||||
GENMASK((_reg)->shift + (_reg)->width - 1, (_reg)->shift)
|
||||
#define cv1800_clk_regfield_get(_val, _reg) \
|
||||
(((_val) >> (_reg)->shift) & GENMASK((_reg)->width - 1, 0))
|
||||
#define cv1800_clk_regfield_set(_val, _new, _reg) \
|
||||
(((_val) & ~cv1800_clk_regfield_genmask((_reg))) | \
|
||||
(((_new) & GENMASK((_reg)->width - 1, 0)) << (_reg)->shift))
|
||||
|
||||
#define _CV1800_SET_FIELD(_reg, _val, _field) \
|
||||
(((_reg) & ~(_field)) | FIELD_PREP((_field), (_val)))
|
||||
|
||||
int cv1800_clk_setbit(struct cv1800_clk_common *common,
|
||||
struct cv1800_clk_regbit *field);
|
||||
int cv1800_clk_clearbit(struct cv1800_clk_common *common,
|
||||
struct cv1800_clk_regbit *field);
|
||||
int cv1800_clk_checkbit(struct cv1800_clk_common *common,
|
||||
struct cv1800_clk_regbit *field);
|
||||
|
||||
void cv1800_clk_wait_for_lock(struct cv1800_clk_common *common,
|
||||
u32 reg, u32 lock);
|
||||
|
||||
#endif // _CLK_SOPHGO_CV18XX_IP_H_
|
||||
887
drivers/clk/sophgo/clk-cv18xx-ip.c
Normal file
887
drivers/clk/sophgo/clk-cv18xx-ip.c
Normal file
|
|
@ -0,0 +1,887 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gcd.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "clk-cv18xx-ip.h"
|
||||
|
||||
/* GATE */
|
||||
static inline struct cv1800_clk_gate *hw_to_cv1800_clk_gate(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);
|
||||
|
||||
return container_of(common, struct cv1800_clk_gate, common);
|
||||
}
|
||||
|
||||
static int gate_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw);
|
||||
|
||||
return cv1800_clk_setbit(&gate->common, &gate->gate);
|
||||
}
|
||||
|
||||
static void gate_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw);
|
||||
|
||||
cv1800_clk_clearbit(&gate->common, &gate->gate);
|
||||
}
|
||||
|
||||
static int gate_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_gate *gate = hw_to_cv1800_clk_gate(hw);
|
||||
|
||||
return cv1800_clk_checkbit(&gate->common, &gate->gate);
|
||||
}
|
||||
|
||||
static unsigned long gate_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return parent_rate;
|
||||
}
|
||||
|
||||
static long gate_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
return *parent_rate;
|
||||
}
|
||||
|
||||
static int gate_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops cv1800_clk_gate_ops = {
|
||||
.disable = gate_disable,
|
||||
.enable = gate_enable,
|
||||
.is_enabled = gate_is_enabled,
|
||||
|
||||
.recalc_rate = gate_recalc_rate,
|
||||
.round_rate = gate_round_rate,
|
||||
.set_rate = gate_set_rate,
|
||||
};
|
||||
|
||||
/* DIV */
|
||||
#define _DIV_EN_CLK_DIV_FACTOR_FIELD BIT(3)
|
||||
|
||||
#define DIV_GET_EN_CLK_DIV_FACTOR(_reg) \
|
||||
FIELD_GET(_DIV_EN_CLK_DIV_FACTOR_FIELD, _reg)
|
||||
|
||||
#define DIV_SET_EN_DIV_FACTOR(_reg) \
|
||||
_CV1800_SET_FIELD(_reg, 1, _DIV_EN_CLK_DIV_FACTOR_FIELD)
|
||||
|
||||
static inline struct cv1800_clk_div *hw_to_cv1800_clk_div(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);
|
||||
|
||||
return container_of(common, struct cv1800_clk_div, common);
|
||||
}
|
||||
|
||||
static int div_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
|
||||
|
||||
return cv1800_clk_setbit(&div->common, &div->gate);
|
||||
}
|
||||
|
||||
static void div_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
|
||||
|
||||
cv1800_clk_clearbit(&div->common, &div->gate);
|
||||
}
|
||||
|
||||
static int div_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
|
||||
|
||||
return cv1800_clk_checkbit(&div->common, &div->gate);
|
||||
}
|
||||
|
||||
static int div_helper_set_rate(struct cv1800_clk_common *common,
|
||||
struct cv1800_clk_regfield *div,
|
||||
unsigned long val)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
if (div->width == 0)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(common->lock, flags);
|
||||
|
||||
reg = readl(common->base + div->reg);
|
||||
reg = cv1800_clk_regfield_set(reg, val, div);
|
||||
if (div->initval > 0)
|
||||
reg = DIV_SET_EN_DIV_FACTOR(reg);
|
||||
|
||||
writel(reg, common->base + div->reg);
|
||||
|
||||
spin_unlock_irqrestore(common->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 div_helper_get_clockdiv(struct cv1800_clk_common *common,
|
||||
struct cv1800_clk_regfield *div)
|
||||
{
|
||||
u32 clockdiv = 1;
|
||||
u32 reg;
|
||||
|
||||
if (!div || div->initval < 0 || (div->width == 0 && div->initval <= 0))
|
||||
return 1;
|
||||
|
||||
if (div->width == 0 && div->initval > 0)
|
||||
return div->initval;
|
||||
|
||||
reg = readl(common->base + div->reg);
|
||||
|
||||
if (div->initval == 0 || DIV_GET_EN_CLK_DIV_FACTOR(reg))
|
||||
clockdiv = cv1800_clk_regfield_get(reg, div);
|
||||
else if (div->initval > 0)
|
||||
clockdiv = div->initval;
|
||||
|
||||
return clockdiv;
|
||||
}
|
||||
|
||||
static u32 div_helper_round_rate(struct cv1800_clk_regfield *div,
|
||||
struct clk_hw *hw, struct clk_hw *parent,
|
||||
unsigned long rate, unsigned long *prate)
|
||||
{
|
||||
if (div->width == 0) {
|
||||
if (div->initval <= 0)
|
||||
return DIV_ROUND_UP_ULL(*prate, 1);
|
||||
else
|
||||
return DIV_ROUND_UP_ULL(*prate, div->initval);
|
||||
}
|
||||
|
||||
return divider_round_rate_parent(hw, parent, rate, prate, NULL,
|
||||
div->width, div->flags);
|
||||
}
|
||||
|
||||
static long div_round_rate(struct clk_hw *parent, unsigned long *parent_rate,
|
||||
unsigned long rate, int id, void *data)
|
||||
{
|
||||
struct cv1800_clk_div *div = data;
|
||||
|
||||
return div_helper_round_rate(&div->div, &div->common.hw, parent,
|
||||
rate, parent_rate);
|
||||
}
|
||||
|
||||
static bool div_is_better_rate(struct cv1800_clk_common *common,
|
||||
unsigned long target, unsigned long now,
|
||||
unsigned long best)
|
||||
{
|
||||
if (common->features & CLK_DIVIDER_ROUND_CLOSEST)
|
||||
return abs_diff(target, now) < abs_diff(target, best);
|
||||
|
||||
return now <= target && now > best;
|
||||
}
|
||||
|
||||
static int mux_helper_determine_rate(struct cv1800_clk_common *common,
|
||||
struct clk_rate_request *req,
|
||||
long (*round)(struct clk_hw *,
|
||||
unsigned long *,
|
||||
unsigned long,
|
||||
int,
|
||||
void *),
|
||||
void *data)
|
||||
{
|
||||
unsigned long best_parent_rate = 0, best_rate = 0;
|
||||
struct clk_hw *best_parent, *hw = &common->hw;
|
||||
unsigned int i;
|
||||
|
||||
if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) {
|
||||
unsigned long adj_parent_rate;
|
||||
|
||||
best_parent = clk_hw_get_parent(hw);
|
||||
best_parent_rate = clk_hw_get_rate(best_parent);
|
||||
|
||||
best_rate = round(best_parent, &adj_parent_rate,
|
||||
req->rate, -1, data);
|
||||
|
||||
goto find;
|
||||
}
|
||||
|
||||
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
|
||||
unsigned long tmp_rate, parent_rate;
|
||||
struct clk_hw *parent;
|
||||
|
||||
parent = clk_hw_get_parent_by_index(hw, i);
|
||||
if (!parent)
|
||||
continue;
|
||||
|
||||
parent_rate = clk_hw_get_rate(parent);
|
||||
|
||||
tmp_rate = round(parent, &parent_rate, req->rate, i, data);
|
||||
|
||||
if (tmp_rate == req->rate) {
|
||||
best_parent = parent;
|
||||
best_parent_rate = parent_rate;
|
||||
best_rate = tmp_rate;
|
||||
goto find;
|
||||
}
|
||||
|
||||
if (div_is_better_rate(common, req->rate,
|
||||
tmp_rate, best_rate)) {
|
||||
best_parent = parent;
|
||||
best_parent_rate = parent_rate;
|
||||
best_rate = tmp_rate;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_rate == 0)
|
||||
return -EINVAL;
|
||||
|
||||
find:
|
||||
req->best_parent_hw = best_parent;
|
||||
req->best_parent_rate = best_parent_rate;
|
||||
req->rate = best_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int div_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
|
||||
|
||||
return mux_helper_determine_rate(&div->common, req,
|
||||
div_round_rate, div);
|
||||
}
|
||||
|
||||
static unsigned long div_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
|
||||
unsigned long val;
|
||||
|
||||
val = div_helper_get_clockdiv(&div->common, &div->div);
|
||||
if (val == 0)
|
||||
return 0;
|
||||
|
||||
return divider_recalc_rate(hw, parent_rate, val, NULL,
|
||||
div->div.flags, div->div.width);
|
||||
}
|
||||
|
||||
static int div_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
|
||||
unsigned long val;
|
||||
|
||||
val = divider_get_val(rate, parent_rate, NULL,
|
||||
div->div.width, div->div.flags);
|
||||
|
||||
return div_helper_set_rate(&div->common, &div->div, val);
|
||||
}
|
||||
|
||||
const struct clk_ops cv1800_clk_div_ops = {
|
||||
.disable = div_disable,
|
||||
.enable = div_enable,
|
||||
.is_enabled = div_is_enabled,
|
||||
|
||||
.determine_rate = div_determine_rate,
|
||||
.recalc_rate = div_recalc_rate,
|
||||
.set_rate = div_set_rate,
|
||||
};
|
||||
|
||||
static inline struct cv1800_clk_bypass_div *
|
||||
hw_to_cv1800_clk_bypass_div(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_div *div = hw_to_cv1800_clk_div(hw);
|
||||
|
||||
return container_of(div, struct cv1800_clk_bypass_div, div);
|
||||
}
|
||||
|
||||
static long bypass_div_round_rate(struct clk_hw *parent,
|
||||
unsigned long *parent_rate,
|
||||
unsigned long rate, int id, void *data)
|
||||
{
|
||||
struct cv1800_clk_bypass_div *div = data;
|
||||
|
||||
if (id == -1) {
|
||||
if (cv1800_clk_checkbit(&div->div.common, &div->bypass))
|
||||
return *parent_rate;
|
||||
else
|
||||
return div_round_rate(parent, parent_rate, rate,
|
||||
-1, &div->div);
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
return *parent_rate;
|
||||
|
||||
return div_round_rate(parent, parent_rate, rate, id - 1, &div->div);
|
||||
}
|
||||
|
||||
static int bypass_div_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);
|
||||
|
||||
return mux_helper_determine_rate(&div->div.common, req,
|
||||
bypass_div_round_rate, div);
|
||||
}
|
||||
|
||||
static unsigned long bypass_div_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);
|
||||
|
||||
if (cv1800_clk_checkbit(&div->div.common, &div->bypass))
|
||||
return parent_rate;
|
||||
|
||||
return div_recalc_rate(hw, parent_rate);
|
||||
}
|
||||
|
||||
static int bypass_div_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);
|
||||
|
||||
if (cv1800_clk_checkbit(&div->div.common, &div->bypass))
|
||||
return 0;
|
||||
|
||||
return div_set_rate(hw, rate, parent_rate);
|
||||
}
|
||||
|
||||
static u8 bypass_div_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);
|
||||
|
||||
if (cv1800_clk_checkbit(&div->div.common, &div->bypass))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bypass_div_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct cv1800_clk_bypass_div *div = hw_to_cv1800_clk_bypass_div(hw);
|
||||
|
||||
if (index)
|
||||
return cv1800_clk_clearbit(&div->div.common, &div->bypass);
|
||||
|
||||
return cv1800_clk_setbit(&div->div.common, &div->bypass);
|
||||
}
|
||||
|
||||
const struct clk_ops cv1800_clk_bypass_div_ops = {
|
||||
.disable = div_disable,
|
||||
.enable = div_enable,
|
||||
.is_enabled = div_is_enabled,
|
||||
|
||||
.determine_rate = bypass_div_determine_rate,
|
||||
.recalc_rate = bypass_div_recalc_rate,
|
||||
.set_rate = bypass_div_set_rate,
|
||||
|
||||
.set_parent = bypass_div_set_parent,
|
||||
.get_parent = bypass_div_get_parent,
|
||||
};
|
||||
|
||||
/* MUX */
|
||||
static inline struct cv1800_clk_mux *hw_to_cv1800_clk_mux(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);
|
||||
|
||||
return container_of(common, struct cv1800_clk_mux, common);
|
||||
}
|
||||
|
||||
static int mux_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
|
||||
|
||||
return cv1800_clk_setbit(&mux->common, &mux->gate);
|
||||
}
|
||||
|
||||
static void mux_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
|
||||
|
||||
cv1800_clk_clearbit(&mux->common, &mux->gate);
|
||||
}
|
||||
|
||||
static int mux_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
|
||||
|
||||
return cv1800_clk_checkbit(&mux->common, &mux->gate);
|
||||
}
|
||||
|
||||
static long mux_round_rate(struct clk_hw *parent, unsigned long *parent_rate,
|
||||
unsigned long rate, int id, void *data)
|
||||
{
|
||||
struct cv1800_clk_mux *mux = data;
|
||||
|
||||
return div_helper_round_rate(&mux->div, &mux->common.hw, parent,
|
||||
rate, parent_rate);
|
||||
}
|
||||
|
||||
static int mux_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
|
||||
|
||||
return mux_helper_determine_rate(&mux->common, req,
|
||||
mux_round_rate, mux);
|
||||
}
|
||||
|
||||
static unsigned long mux_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
|
||||
unsigned long val;
|
||||
|
||||
val = div_helper_get_clockdiv(&mux->common, &mux->div);
|
||||
if (val == 0)
|
||||
return 0;
|
||||
|
||||
return divider_recalc_rate(hw, parent_rate, val, NULL,
|
||||
mux->div.flags, mux->div.width);
|
||||
}
|
||||
|
||||
static int mux_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
|
||||
unsigned long val;
|
||||
|
||||
val = divider_get_val(rate, parent_rate, NULL,
|
||||
mux->div.width, mux->div.flags);
|
||||
|
||||
return div_helper_set_rate(&mux->common, &mux->div, val);
|
||||
}
|
||||
|
||||
static u8 mux_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
|
||||
u32 reg = readl(mux->common.base + mux->mux.reg);
|
||||
|
||||
return cv1800_clk_regfield_get(reg, &mux->mux);
|
||||
}
|
||||
|
||||
static int _mux_set_parent(struct cv1800_clk_mux *mux, u8 index)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl(mux->common.base + mux->mux.reg);
|
||||
reg = cv1800_clk_regfield_set(reg, index, &mux->mux);
|
||||
writel(reg, mux->common.base + mux->mux.reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(mux->common.lock, flags);
|
||||
|
||||
_mux_set_parent(mux, index);
|
||||
|
||||
spin_unlock_irqrestore(mux->common.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops cv1800_clk_mux_ops = {
|
||||
.disable = mux_disable,
|
||||
.enable = mux_enable,
|
||||
.is_enabled = mux_is_enabled,
|
||||
|
||||
.determine_rate = mux_determine_rate,
|
||||
.recalc_rate = mux_recalc_rate,
|
||||
.set_rate = mux_set_rate,
|
||||
|
||||
.set_parent = mux_set_parent,
|
||||
.get_parent = mux_get_parent,
|
||||
};
|
||||
|
||||
static inline struct cv1800_clk_bypass_mux *
|
||||
hw_to_cv1800_clk_bypass_mux(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_mux *mux = hw_to_cv1800_clk_mux(hw);
|
||||
|
||||
return container_of(mux, struct cv1800_clk_bypass_mux, mux);
|
||||
}
|
||||
|
||||
static long bypass_mux_round_rate(struct clk_hw *parent,
|
||||
unsigned long *parent_rate,
|
||||
unsigned long rate, int id, void *data)
|
||||
{
|
||||
struct cv1800_clk_bypass_mux *mux = data;
|
||||
|
||||
if (id == -1) {
|
||||
if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass))
|
||||
return *parent_rate;
|
||||
else
|
||||
return mux_round_rate(parent, parent_rate, rate,
|
||||
-1, &mux->mux);
|
||||
}
|
||||
|
||||
if (id == 0)
|
||||
return *parent_rate;
|
||||
|
||||
return mux_round_rate(parent, parent_rate, rate, id - 1, &mux->mux);
|
||||
}
|
||||
|
||||
static int bypass_mux_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);
|
||||
|
||||
return mux_helper_determine_rate(&mux->mux.common, req,
|
||||
bypass_mux_round_rate, mux);
|
||||
}
|
||||
|
||||
static unsigned long bypass_mux_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);
|
||||
|
||||
if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass))
|
||||
return parent_rate;
|
||||
|
||||
return mux_recalc_rate(hw, parent_rate);
|
||||
}
|
||||
|
||||
static int bypass_mux_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);
|
||||
|
||||
if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass))
|
||||
return 0;
|
||||
|
||||
return mux_set_rate(hw, rate, parent_rate);
|
||||
}
|
||||
|
||||
static u8 bypass_mux_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);
|
||||
|
||||
if (cv1800_clk_checkbit(&mux->mux.common, &mux->bypass))
|
||||
return 0;
|
||||
|
||||
return mux_get_parent(hw) + 1;
|
||||
}
|
||||
|
||||
static int bypass_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct cv1800_clk_bypass_mux *mux = hw_to_cv1800_clk_bypass_mux(hw);
|
||||
|
||||
if (index == 0)
|
||||
return cv1800_clk_setbit(&mux->mux.common, &mux->bypass);
|
||||
|
||||
return cv1800_clk_clearbit(&mux->mux.common, &mux->bypass);
|
||||
}
|
||||
|
||||
const struct clk_ops cv1800_clk_bypass_mux_ops = {
|
||||
.disable = mux_disable,
|
||||
.enable = mux_enable,
|
||||
.is_enabled = mux_is_enabled,
|
||||
|
||||
.determine_rate = bypass_mux_determine_rate,
|
||||
.recalc_rate = bypass_mux_recalc_rate,
|
||||
.set_rate = bypass_mux_set_rate,
|
||||
|
||||
.set_parent = bypass_mux_set_parent,
|
||||
.get_parent = bypass_mux_get_parent,
|
||||
};
|
||||
|
||||
/* MMUX */
|
||||
static inline struct cv1800_clk_mmux *hw_to_cv1800_clk_mmux(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);
|
||||
|
||||
return container_of(common, struct cv1800_clk_mmux, common);
|
||||
}
|
||||
|
||||
static u8 mmux_get_parent_id(struct cv1800_clk_mmux *mmux)
|
||||
{
|
||||
struct clk_hw *hw = &mmux->common.hw;
|
||||
struct clk_hw *parent = clk_hw_get_parent(hw);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
|
||||
if (parent == clk_hw_get_parent_by_index(hw, i))
|
||||
return i;
|
||||
}
|
||||
|
||||
unreachable();
|
||||
}
|
||||
|
||||
static int mmux_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
|
||||
|
||||
return cv1800_clk_setbit(&mmux->common, &mmux->gate);
|
||||
}
|
||||
|
||||
static void mmux_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
|
||||
|
||||
cv1800_clk_clearbit(&mmux->common, &mmux->gate);
|
||||
}
|
||||
|
||||
static int mmux_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
|
||||
|
||||
return cv1800_clk_checkbit(&mmux->common, &mmux->gate);
|
||||
}
|
||||
|
||||
static long mmux_round_rate(struct clk_hw *parent, unsigned long *parent_rate,
|
||||
unsigned long rate, int id, void *data)
|
||||
{
|
||||
struct cv1800_clk_mmux *mmux = data;
|
||||
s8 div_id;
|
||||
|
||||
if (id == -1) {
|
||||
if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass))
|
||||
return *parent_rate;
|
||||
|
||||
id = mmux_get_parent_id(mmux);
|
||||
}
|
||||
|
||||
div_id = mmux->parent2sel[id];
|
||||
|
||||
if (div_id < 0)
|
||||
return *parent_rate;
|
||||
|
||||
return div_helper_round_rate(&mmux->div[div_id],
|
||||
&mmux->common.hw, parent,
|
||||
rate, parent_rate);
|
||||
}
|
||||
|
||||
static int mmux_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
|
||||
|
||||
return mux_helper_determine_rate(&mmux->common, req,
|
||||
mmux_round_rate, mmux);
|
||||
}
|
||||
|
||||
static unsigned long mmux_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
|
||||
unsigned long val;
|
||||
struct cv1800_clk_regfield *div;
|
||||
|
||||
if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass))
|
||||
return parent_rate;
|
||||
|
||||
if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel))
|
||||
div = &mmux->div[0];
|
||||
else
|
||||
div = &mmux->div[1];
|
||||
|
||||
val = div_helper_get_clockdiv(&mmux->common, div);
|
||||
if (val == 0)
|
||||
return 0;
|
||||
|
||||
return divider_recalc_rate(hw, parent_rate, val, NULL,
|
||||
div->flags, div->width);
|
||||
}
|
||||
|
||||
static int mmux_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
|
||||
struct cv1800_clk_regfield *div;
|
||||
unsigned long val;
|
||||
|
||||
if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass))
|
||||
return parent_rate;
|
||||
|
||||
if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel))
|
||||
div = &mmux->div[0];
|
||||
else
|
||||
div = &mmux->div[1];
|
||||
|
||||
val = divider_get_val(rate, parent_rate, NULL,
|
||||
div->width, div->flags);
|
||||
|
||||
return div_helper_set_rate(&mmux->common, div, val);
|
||||
}
|
||||
|
||||
static u8 mmux_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
|
||||
struct cv1800_clk_regfield *mux;
|
||||
u32 reg;
|
||||
s8 clk_sel;
|
||||
|
||||
if (cv1800_clk_checkbit(&mmux->common, &mmux->bypass))
|
||||
return 0;
|
||||
|
||||
if (cv1800_clk_checkbit(&mmux->common, &mmux->clk_sel))
|
||||
clk_sel = 0;
|
||||
else
|
||||
clk_sel = 1;
|
||||
mux = &mmux->mux[clk_sel];
|
||||
|
||||
reg = readl(mmux->common.base + mux->reg);
|
||||
|
||||
return mmux->sel2parent[clk_sel][cv1800_clk_regfield_get(reg, mux)];
|
||||
}
|
||||
|
||||
static int mmux_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct cv1800_clk_mmux *mmux = hw_to_cv1800_clk_mmux(hw);
|
||||
struct cv1800_clk_regfield *mux;
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
s8 clk_sel = mmux->parent2sel[index];
|
||||
|
||||
if (index == 0 || clk_sel == -1) {
|
||||
cv1800_clk_setbit(&mmux->common, &mmux->bypass);
|
||||
goto release;
|
||||
}
|
||||
|
||||
cv1800_clk_clearbit(&mmux->common, &mmux->bypass);
|
||||
|
||||
if (clk_sel)
|
||||
cv1800_clk_clearbit(&mmux->common, &mmux->clk_sel);
|
||||
else
|
||||
cv1800_clk_setbit(&mmux->common, &mmux->clk_sel);
|
||||
|
||||
spin_lock_irqsave(mmux->common.lock, flags);
|
||||
|
||||
mux = &mmux->mux[clk_sel];
|
||||
reg = readl(mmux->common.base + mux->reg);
|
||||
reg = cv1800_clk_regfield_set(reg, index, mux);
|
||||
|
||||
writel(reg, mmux->common.base + mux->reg);
|
||||
|
||||
spin_unlock_irqrestore(mmux->common.lock, flags);
|
||||
|
||||
release:
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops cv1800_clk_mmux_ops = {
|
||||
.disable = mmux_disable,
|
||||
.enable = mmux_enable,
|
||||
.is_enabled = mmux_is_enabled,
|
||||
|
||||
.determine_rate = mmux_determine_rate,
|
||||
.recalc_rate = mmux_recalc_rate,
|
||||
.set_rate = mmux_set_rate,
|
||||
|
||||
.set_parent = mmux_set_parent,
|
||||
.get_parent = mmux_get_parent,
|
||||
};
|
||||
|
||||
/* AUDIO CLK */
|
||||
static inline struct cv1800_clk_audio *
|
||||
hw_to_cv1800_clk_audio(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);
|
||||
|
||||
return container_of(common, struct cv1800_clk_audio, common);
|
||||
}
|
||||
|
||||
static int aclk_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);
|
||||
|
||||
cv1800_clk_setbit(&aclk->common, &aclk->src_en);
|
||||
return cv1800_clk_setbit(&aclk->common, &aclk->output_en);
|
||||
}
|
||||
|
||||
static void aclk_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);
|
||||
|
||||
cv1800_clk_clearbit(&aclk->common, &aclk->output_en);
|
||||
cv1800_clk_clearbit(&aclk->common, &aclk->src_en);
|
||||
}
|
||||
|
||||
static int aclk_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);
|
||||
|
||||
return cv1800_clk_checkbit(&aclk->common, &aclk->output_en);
|
||||
}
|
||||
|
||||
static int aclk_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);
|
||||
|
||||
req->rate = aclk->target_rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long aclk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);
|
||||
u64 rate = parent_rate;
|
||||
u64 factor = 2;
|
||||
u32 regval;
|
||||
|
||||
if (!cv1800_clk_checkbit(&aclk->common, &aclk->div_en))
|
||||
return 0;
|
||||
|
||||
regval = readl(aclk->common.base + aclk->m.reg);
|
||||
factor *= cv1800_clk_regfield_get(regval, &aclk->m);
|
||||
|
||||
regval = readl(aclk->common.base + aclk->n.reg);
|
||||
rate *= cv1800_clk_regfield_get(regval, &aclk->n);
|
||||
|
||||
return DIV64_U64_ROUND_UP(rate, factor);
|
||||
}
|
||||
|
||||
static void aclk_determine_mn(unsigned long parent_rate, unsigned long rate,
|
||||
u32 *m, u32 *n)
|
||||
{
|
||||
u32 tm = parent_rate / 2;
|
||||
u32 tn = rate;
|
||||
u32 tcommon = gcd(tm, tn);
|
||||
*m = tm / tcommon;
|
||||
*n = tn / tcommon;
|
||||
}
|
||||
|
||||
static int aclk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_audio *aclk = hw_to_cv1800_clk_audio(hw);
|
||||
unsigned long flags;
|
||||
u32 m, n;
|
||||
|
||||
aclk_determine_mn(parent_rate, rate,
|
||||
&m, &n);
|
||||
|
||||
spin_lock_irqsave(aclk->common.lock, flags);
|
||||
|
||||
writel(m, aclk->common.base + aclk->m.reg);
|
||||
writel(n, aclk->common.base + aclk->n.reg);
|
||||
|
||||
cv1800_clk_setbit(&aclk->common, &aclk->div_en);
|
||||
cv1800_clk_setbit(&aclk->common, &aclk->div_up);
|
||||
|
||||
spin_unlock_irqrestore(aclk->common.lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops cv1800_clk_audio_ops = {
|
||||
.disable = aclk_disable,
|
||||
.enable = aclk_enable,
|
||||
.is_enabled = aclk_is_enabled,
|
||||
|
||||
.determine_rate = aclk_determine_rate,
|
||||
.recalc_rate = aclk_recalc_rate,
|
||||
.set_rate = aclk_set_rate,
|
||||
};
|
||||
261
drivers/clk/sophgo/clk-cv18xx-ip.h
Normal file
261
drivers/clk/sophgo/clk-cv18xx-ip.h
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
|
||||
*/
|
||||
|
||||
#ifndef _CLK_SOPHGO_CV1800_IP_H_
|
||||
#define _CLK_SOPHGO_CV1800_IP_H_
|
||||
|
||||
#include "clk-cv18xx-common.h"
|
||||
|
||||
struct cv1800_clk_gate {
|
||||
struct cv1800_clk_common common;
|
||||
struct cv1800_clk_regbit gate;
|
||||
};
|
||||
|
||||
struct cv1800_clk_div_data {
|
||||
u32 reg;
|
||||
u32 mask;
|
||||
u32 width;
|
||||
u32 init;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
struct cv1800_clk_div {
|
||||
struct cv1800_clk_common common;
|
||||
struct cv1800_clk_regbit gate;
|
||||
struct cv1800_clk_regfield div;
|
||||
};
|
||||
|
||||
struct cv1800_clk_bypass_div {
|
||||
struct cv1800_clk_div div;
|
||||
struct cv1800_clk_regbit bypass;
|
||||
};
|
||||
|
||||
struct cv1800_clk_mux {
|
||||
struct cv1800_clk_common common;
|
||||
struct cv1800_clk_regbit gate;
|
||||
struct cv1800_clk_regfield div;
|
||||
struct cv1800_clk_regfield mux;
|
||||
};
|
||||
|
||||
struct cv1800_clk_bypass_mux {
|
||||
struct cv1800_clk_mux mux;
|
||||
struct cv1800_clk_regbit bypass;
|
||||
};
|
||||
|
||||
struct cv1800_clk_mmux {
|
||||
struct cv1800_clk_common common;
|
||||
struct cv1800_clk_regbit gate;
|
||||
struct cv1800_clk_regfield div[2];
|
||||
struct cv1800_clk_regfield mux[2];
|
||||
struct cv1800_clk_regbit bypass;
|
||||
struct cv1800_clk_regbit clk_sel;
|
||||
const s8 *parent2sel;
|
||||
const u8 *sel2parent[2];
|
||||
};
|
||||
|
||||
struct cv1800_clk_audio {
|
||||
struct cv1800_clk_common common;
|
||||
struct cv1800_clk_regbit src_en;
|
||||
struct cv1800_clk_regbit output_en;
|
||||
struct cv1800_clk_regbit div_en;
|
||||
struct cv1800_clk_regbit div_up;
|
||||
struct cv1800_clk_regfield m;
|
||||
struct cv1800_clk_regfield n;
|
||||
u32 target_rate;
|
||||
};
|
||||
|
||||
#define CV1800_GATE(_name, _parent, _gate_reg, _gate_shift, _flags) \
|
||||
struct cv1800_clk_gate _name = { \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
&cv1800_clk_gate_ops, \
|
||||
_flags), \
|
||||
.gate = CV1800_CLK_BIT(_gate_reg, _gate_shift), \
|
||||
}
|
||||
|
||||
#define _CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init, \
|
||||
_div_flag, _ops, _flags) \
|
||||
{ \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
_ops, _flags), \
|
||||
.gate = CV1800_CLK_BIT(_gate_reg, \
|
||||
_gate_shift), \
|
||||
.div = CV1800_CLK_REG(_div_reg, _div_shift, \
|
||||
_div_width, _div_init, \
|
||||
_div_flag), \
|
||||
}
|
||||
|
||||
#define _CV1800_FIXED_DIV_FLAG \
|
||||
(CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ROUND_CLOSEST)
|
||||
|
||||
#define _CV1800_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_fix_div, _ops, _flags) \
|
||||
{ \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
_ops, _flags), \
|
||||
.gate = CV1800_CLK_BIT(_gate_reg, \
|
||||
_gate_shift), \
|
||||
.div = CV1800_CLK_REG(0, 0, 0, \
|
||||
_fix_div, \
|
||||
_CV1800_FIXED_DIV_FLAG),\
|
||||
}
|
||||
|
||||
#define CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init, \
|
||||
_div_flag, _flags) \
|
||||
struct cv1800_clk_div _name = \
|
||||
_CV1800_DIV(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init,\
|
||||
_div_flag, &cv1800_clk_div_ops, _flags)
|
||||
|
||||
#define CV1800_BYPASS_DIV(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init, \
|
||||
_div_flag, _bypass_reg, _bypass_shift, _flags)\
|
||||
struct cv1800_clk_bypass_div _name = { \
|
||||
.div = _CV1800_DIV(_name, _parent, \
|
||||
_gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, \
|
||||
_div_width, _div_init, _div_flag, \
|
||||
&cv1800_clk_bypass_div_ops, \
|
||||
_flags), \
|
||||
.bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \
|
||||
}
|
||||
|
||||
#define CV1800_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_fix_div, _flags) \
|
||||
struct cv1800_clk_div _name = \
|
||||
_CV1800_FIXED_DIV(_name, _parent, \
|
||||
_gate_reg, _gate_shift, \
|
||||
_fix_div, \
|
||||
&cv1800_clk_div_ops, _flags) \
|
||||
|
||||
#define CV1800_BYPASS_FIXED_DIV(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_fix_div, _bypass_reg, _bypass_shift, \
|
||||
_flags) \
|
||||
struct cv1800_clk_bypass_div _name = { \
|
||||
.div = _CV1800_FIXED_DIV(_name, _parent, \
|
||||
_gate_reg, _gate_shift, \
|
||||
_fix_div, \
|
||||
&cv1800_clk_bypass_div_ops, \
|
||||
_flags), \
|
||||
.bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \
|
||||
}
|
||||
|
||||
#define _CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init, \
|
||||
_div_flag, \
|
||||
_mux_reg, _mux_shift, _mux_width, \
|
||||
_ops, _flags) \
|
||||
{ \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
_ops, _flags), \
|
||||
.gate = CV1800_CLK_BIT(_gate_reg, \
|
||||
_gate_shift), \
|
||||
.div = CV1800_CLK_REG(_div_reg, _div_shift, \
|
||||
_div_width, _div_init, \
|
||||
_div_flag), \
|
||||
.mux = CV1800_CLK_REG(_mux_reg, _mux_shift, \
|
||||
_mux_width, 0, 0), \
|
||||
}
|
||||
|
||||
#define CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init, \
|
||||
_div_flag, \
|
||||
_mux_reg, _mux_shift, _mux_width, _flags) \
|
||||
struct cv1800_clk_mux _name = \
|
||||
_CV1800_MUX(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init,\
|
||||
_div_flag, _mux_reg, _mux_shift, _mux_width,\
|
||||
&cv1800_clk_mux_ops, _flags)
|
||||
|
||||
#define CV1800_BYPASS_MUX(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, _div_init, \
|
||||
_div_flag, \
|
||||
_mux_reg, _mux_shift, _mux_width, \
|
||||
_bypass_reg, _bypass_shift, _flags) \
|
||||
struct cv1800_clk_bypass_mux _name = { \
|
||||
.mux = _CV1800_MUX(_name, _parent, \
|
||||
_gate_reg, _gate_shift, \
|
||||
_div_reg, _div_shift, _div_width, \
|
||||
_div_init, _div_flag, \
|
||||
_mux_reg, _mux_shift, _mux_width, \
|
||||
&cv1800_clk_bypass_mux_ops, \
|
||||
_flags), \
|
||||
.bypass = CV1800_CLK_BIT(_bypass_reg, _bypass_shift), \
|
||||
}
|
||||
|
||||
#define CV1800_MMUX(_name, _parent, _gate_reg, _gate_shift, \
|
||||
_div0_reg, _div0_shift, _div0_width, _div0_init, \
|
||||
_div0_flag, \
|
||||
_div1_reg, _div1_shift, _div1_width, _div1_init, \
|
||||
_div1_flag, \
|
||||
_mux0_reg, _mux0_shift, _mux0_width, \
|
||||
_mux1_reg, _mux1_shift, _mux1_width, \
|
||||
_bypass_reg, _bypass_shift, \
|
||||
_clk_sel_reg, _clk_sel_shift, \
|
||||
_parent2sel, _sel2parent0, _sel2parent1, _flags) \
|
||||
struct cv1800_clk_mmux _name = { \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
&cv1800_clk_mmux_ops,\
|
||||
_flags), \
|
||||
.gate = CV1800_CLK_BIT(_gate_reg, _gate_shift),\
|
||||
.div = { \
|
||||
CV1800_CLK_REG(_div0_reg, _div0_shift, \
|
||||
_div0_width, _div0_init, \
|
||||
_div0_flag), \
|
||||
CV1800_CLK_REG(_div1_reg, _div1_shift, \
|
||||
_div1_width, _div1_init, \
|
||||
_div1_flag), \
|
||||
}, \
|
||||
.mux = { \
|
||||
CV1800_CLK_REG(_mux0_reg, _mux0_shift, \
|
||||
_mux0_width, 0, 0), \
|
||||
CV1800_CLK_REG(_mux1_reg, _mux1_shift, \
|
||||
_mux1_width, 0, 0), \
|
||||
}, \
|
||||
.bypass = CV1800_CLK_BIT(_bypass_reg, \
|
||||
_bypass_shift), \
|
||||
.clk_sel = CV1800_CLK_BIT(_clk_sel_reg, \
|
||||
_clk_sel_shift), \
|
||||
.parent2sel = _parent2sel, \
|
||||
.sel2parent = { _sel2parent0, _sel2parent1 }, \
|
||||
}
|
||||
|
||||
#define CV1800_ACLK(_name, _parent, \
|
||||
_src_en_reg, _src_en_reg_shift, \
|
||||
_output_en_reg, _output_en_shift, \
|
||||
_div_en_reg, _div_en_reg_shift, \
|
||||
_div_up_reg, _div_up_reg_shift, \
|
||||
_m_reg, _m_shift, _m_width, _m_flag, \
|
||||
_n_reg, _n_shift, _n_width, _n_flag, \
|
||||
_target_rate, _flags) \
|
||||
struct cv1800_clk_audio _name = { \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
&cv1800_clk_audio_ops,\
|
||||
_flags), \
|
||||
.src_en = CV1800_CLK_BIT(_src_en_reg, \
|
||||
_src_en_reg_shift), \
|
||||
.output_en = CV1800_CLK_BIT(_output_en_reg, \
|
||||
_output_en_shift), \
|
||||
.div_en = CV1800_CLK_BIT(_div_en_reg, \
|
||||
_div_en_reg_shift), \
|
||||
.div_up = CV1800_CLK_BIT(_div_up_reg, \
|
||||
_div_up_reg_shift), \
|
||||
.m = CV1800_CLK_REG(_m_reg, _m_shift, \
|
||||
_m_width, 0, _m_flag), \
|
||||
.n = CV1800_CLK_REG(_n_reg, _n_shift, \
|
||||
_n_width, 0, _n_flag), \
|
||||
.target_rate = _target_rate, \
|
||||
}
|
||||
|
||||
extern const struct clk_ops cv1800_clk_gate_ops;
|
||||
extern const struct clk_ops cv1800_clk_div_ops;
|
||||
extern const struct clk_ops cv1800_clk_bypass_div_ops;
|
||||
extern const struct clk_ops cv1800_clk_mux_ops;
|
||||
extern const struct clk_ops cv1800_clk_bypass_mux_ops;
|
||||
extern const struct clk_ops cv1800_clk_mmux_ops;
|
||||
extern const struct clk_ops cv1800_clk_audio_ops;
|
||||
|
||||
#endif // _CLK_SOPHGO_CV1800_IP_H_
|
||||
419
drivers/clk/sophgo/clk-cv18xx-pll.c
Normal file
419
drivers/clk/sophgo/clk-cv18xx-pll.c
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "clk-cv18xx-pll.h"
|
||||
|
||||
static inline struct cv1800_clk_pll *hw_to_cv1800_clk_pll(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_common *common = hw_to_cv1800_clk_common(hw);
|
||||
|
||||
return container_of(common, struct cv1800_clk_pll, common);
|
||||
}
|
||||
|
||||
static unsigned long ipll_calc_rate(unsigned long parent_rate,
|
||||
unsigned long pre_div_sel,
|
||||
unsigned long div_sel,
|
||||
unsigned long post_div_sel)
|
||||
{
|
||||
uint64_t rate = parent_rate;
|
||||
|
||||
rate *= div_sel;
|
||||
do_div(rate, pre_div_sel * post_div_sel);
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static unsigned long ipll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
u32 value;
|
||||
|
||||
value = readl(pll->common.base + pll->pll_reg);
|
||||
|
||||
return ipll_calc_rate(parent_rate,
|
||||
PLL_GET_PRE_DIV_SEL(value),
|
||||
PLL_GET_DIV_SEL(value),
|
||||
PLL_GET_POST_DIV_SEL(value));
|
||||
}
|
||||
|
||||
static int ipll_find_rate(const struct cv1800_clk_pll_limit *limit,
|
||||
unsigned long prate, unsigned long *rate,
|
||||
u32 *value)
|
||||
{
|
||||
unsigned long best_rate = 0;
|
||||
unsigned long trate = *rate;
|
||||
unsigned long pre_div_sel = 0, div_sel = 0, post_div_sel = 0;
|
||||
unsigned long pre, div, post;
|
||||
u32 detected = *value;
|
||||
unsigned long tmp;
|
||||
|
||||
for_each_pll_limit_range(pre, &limit->pre_div) {
|
||||
for_each_pll_limit_range(div, &limit->div) {
|
||||
for_each_pll_limit_range(post, &limit->post_div) {
|
||||
tmp = ipll_calc_rate(prate, pre, div, post);
|
||||
|
||||
if (tmp > trate)
|
||||
continue;
|
||||
|
||||
if ((trate - tmp) < (trate - best_rate)) {
|
||||
best_rate = tmp;
|
||||
pre_div_sel = pre;
|
||||
div_sel = div;
|
||||
post_div_sel = post;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_rate) {
|
||||
detected = PLL_SET_PRE_DIV_SEL(detected, pre_div_sel);
|
||||
detected = PLL_SET_POST_DIV_SEL(detected, post_div_sel);
|
||||
detected = PLL_SET_DIV_SEL(detected, div_sel);
|
||||
*value = detected;
|
||||
*rate = best_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ipll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
|
||||
{
|
||||
u32 val;
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
return ipll_find_rate(pll->pll_limit, req->best_parent_rate,
|
||||
&req->rate, &val);
|
||||
}
|
||||
|
||||
static void pll_get_mode_ctrl(unsigned long div_sel,
|
||||
bool (*mode_ctrl_check)(unsigned long,
|
||||
unsigned long,
|
||||
unsigned long),
|
||||
const struct cv1800_clk_pll_limit *limit,
|
||||
u32 *value)
|
||||
{
|
||||
unsigned long ictrl = 0, mode = 0;
|
||||
u32 detected = *value;
|
||||
|
||||
for_each_pll_limit_range(mode, &limit->mode) {
|
||||
for_each_pll_limit_range(ictrl, &limit->ictrl) {
|
||||
if (mode_ctrl_check(div_sel, ictrl, mode)) {
|
||||
detected = PLL_SET_SEL_MODE(detected, mode);
|
||||
detected = PLL_SET_ICTRL(detected, ictrl);
|
||||
*value = detected;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool ipll_check_mode_ctrl_restrict(unsigned long div_sel,
|
||||
unsigned long ictrl,
|
||||
unsigned long mode)
|
||||
{
|
||||
unsigned long left_rest = 20 * div_sel;
|
||||
unsigned long right_rest = 35 * div_sel;
|
||||
unsigned long test = 184 * (1 + mode) * (1 + ictrl) / 2;
|
||||
|
||||
return test > left_rest && test <= right_rest;
|
||||
}
|
||||
|
||||
static int ipll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 regval, detected = 0;
|
||||
unsigned long flags;
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
ipll_find_rate(pll->pll_limit, parent_rate, &rate, &detected);
|
||||
pll_get_mode_ctrl(PLL_GET_DIV_SEL(detected),
|
||||
ipll_check_mode_ctrl_restrict,
|
||||
pll->pll_limit, &detected);
|
||||
|
||||
spin_lock_irqsave(pll->common.lock, flags);
|
||||
|
||||
regval = readl(pll->common.base + pll->pll_reg);
|
||||
regval = PLL_COPY_REG(regval, detected);
|
||||
|
||||
writel(regval, pll->common.base + pll->pll_reg);
|
||||
|
||||
spin_unlock_irqrestore(pll->common.lock, flags);
|
||||
|
||||
cv1800_clk_wait_for_lock(&pll->common, pll->pll_status.reg,
|
||||
BIT(pll->pll_status.shift));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pll_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
return cv1800_clk_clearbit(&pll->common, &pll->pll_pwd);
|
||||
}
|
||||
|
||||
static void pll_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
cv1800_clk_setbit(&pll->common, &pll->pll_pwd);
|
||||
}
|
||||
|
||||
static int pll_is_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
return cv1800_clk_checkbit(&pll->common, &pll->pll_pwd) == 0;
|
||||
}
|
||||
|
||||
const struct clk_ops cv1800_clk_ipll_ops = {
|
||||
.disable = pll_disable,
|
||||
.enable = pll_enable,
|
||||
.is_enabled = pll_is_enable,
|
||||
|
||||
.recalc_rate = ipll_recalc_rate,
|
||||
.determine_rate = ipll_determine_rate,
|
||||
.set_rate = ipll_set_rate,
|
||||
};
|
||||
|
||||
#define PLL_SYN_FACTOR_DOT_POS 26
|
||||
#define PLL_SYN_FACTOR_MINIMUM ((4 << PLL_SYN_FACTOR_DOT_POS) + 1)
|
||||
|
||||
static bool fpll_is_factional_mode(struct cv1800_clk_pll *pll)
|
||||
{
|
||||
return cv1800_clk_checkbit(&pll->common, &pll->pll_syn->en);
|
||||
}
|
||||
|
||||
static unsigned long fpll_calc_rate(unsigned long parent_rate,
|
||||
unsigned long pre_div_sel,
|
||||
unsigned long div_sel,
|
||||
unsigned long post_div_sel,
|
||||
unsigned long ssc_syn_set,
|
||||
bool is_full_parent)
|
||||
{
|
||||
u64 dividend = parent_rate * div_sel;
|
||||
u64 factor = ssc_syn_set * pre_div_sel * post_div_sel;
|
||||
unsigned long rate;
|
||||
|
||||
dividend <<= PLL_SYN_FACTOR_DOT_POS - 1;
|
||||
rate = div64_u64_rem(dividend, factor, ÷nd);
|
||||
|
||||
if (is_full_parent) {
|
||||
dividend <<= 1;
|
||||
rate <<= 1;
|
||||
}
|
||||
|
||||
rate += DIV64_U64_ROUND_CLOSEST(dividend, factor);
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
static unsigned long fpll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
u32 value;
|
||||
bool clk_full;
|
||||
u32 syn_set;
|
||||
|
||||
if (!fpll_is_factional_mode(pll))
|
||||
return ipll_recalc_rate(hw, parent_rate);
|
||||
|
||||
syn_set = readl(pll->common.base + pll->pll_syn->set);
|
||||
|
||||
if (syn_set == 0)
|
||||
return 0;
|
||||
|
||||
clk_full = cv1800_clk_checkbit(&pll->common,
|
||||
&pll->pll_syn->clk_half);
|
||||
|
||||
value = readl(pll->common.base + pll->pll_reg);
|
||||
|
||||
return fpll_calc_rate(parent_rate,
|
||||
PLL_GET_PRE_DIV_SEL(value),
|
||||
PLL_GET_DIV_SEL(value),
|
||||
PLL_GET_POST_DIV_SEL(value),
|
||||
syn_set, clk_full);
|
||||
}
|
||||
|
||||
static unsigned long fpll_find_synthesizer(unsigned long parent,
|
||||
unsigned long rate,
|
||||
unsigned long pre_div,
|
||||
unsigned long div,
|
||||
unsigned long post_div,
|
||||
bool is_full_parent,
|
||||
u32 *ssc_syn_set)
|
||||
{
|
||||
u32 test_max = U32_MAX, test_min = PLL_SYN_FACTOR_MINIMUM;
|
||||
unsigned long trate;
|
||||
|
||||
while (test_min < test_max) {
|
||||
u32 tssc = (test_max + test_min) / 2;
|
||||
|
||||
trate = fpll_calc_rate(parent, pre_div, div, post_div,
|
||||
tssc, is_full_parent);
|
||||
|
||||
if (trate == rate) {
|
||||
test_min = tssc;
|
||||
break;
|
||||
}
|
||||
|
||||
if (trate > rate)
|
||||
test_min = tssc + 1;
|
||||
else
|
||||
test_max = tssc - 1;
|
||||
}
|
||||
|
||||
if (trate != 0)
|
||||
*ssc_syn_set = test_min;
|
||||
|
||||
return trate;
|
||||
}
|
||||
|
||||
static int fpll_find_rate(struct cv1800_clk_pll *pll,
|
||||
const struct cv1800_clk_pll_limit *limit,
|
||||
unsigned long prate,
|
||||
unsigned long *rate,
|
||||
u32 *value, u32 *ssc_syn_set)
|
||||
{
|
||||
unsigned long best_rate = 0;
|
||||
unsigned long pre_div_sel = 0, div_sel = 0, post_div_sel = 0;
|
||||
unsigned long pre, div, post;
|
||||
unsigned long trate = *rate;
|
||||
u32 detected = *value;
|
||||
unsigned long tmp;
|
||||
bool clk_full = cv1800_clk_checkbit(&pll->common,
|
||||
&pll->pll_syn->clk_half);
|
||||
|
||||
for_each_pll_limit_range(pre, &limit->pre_div) {
|
||||
for_each_pll_limit_range(post, &limit->post_div) {
|
||||
for_each_pll_limit_range(div, &limit->div) {
|
||||
tmp = fpll_find_synthesizer(prate, trate,
|
||||
pre, div, post,
|
||||
clk_full,
|
||||
ssc_syn_set);
|
||||
|
||||
if ((trate - tmp) < (trate - best_rate)) {
|
||||
best_rate = tmp;
|
||||
pre_div_sel = pre;
|
||||
div_sel = div;
|
||||
post_div_sel = post;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best_rate) {
|
||||
detected = PLL_SET_PRE_DIV_SEL(detected, pre_div_sel);
|
||||
detected = PLL_SET_POST_DIV_SEL(detected, post_div_sel);
|
||||
detected = PLL_SET_DIV_SEL(detected, div_sel);
|
||||
*value = detected;
|
||||
*rate = best_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int fpll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
u32 val, ssc_syn_set;
|
||||
|
||||
if (!fpll_is_factional_mode(pll))
|
||||
return ipll_determine_rate(hw, req);
|
||||
|
||||
fpll_find_rate(pll, &pll->pll_limit[2], req->best_parent_rate,
|
||||
&req->rate, &val, &ssc_syn_set);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool fpll_check_mode_ctrl_restrict(unsigned long div_sel,
|
||||
unsigned long ictrl,
|
||||
unsigned long mode)
|
||||
{
|
||||
unsigned long left_rest = 10 * div_sel;
|
||||
unsigned long right_rest = 24 * div_sel;
|
||||
unsigned long test = 184 * (1 + mode) * (1 + ictrl) / 2;
|
||||
|
||||
return test > left_rest && test <= right_rest;
|
||||
}
|
||||
|
||||
static int fpll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 regval;
|
||||
u32 detected = 0, detected_ssc = 0;
|
||||
unsigned long flags;
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
if (!fpll_is_factional_mode(pll))
|
||||
return ipll_set_rate(hw, rate, parent_rate);
|
||||
|
||||
fpll_find_rate(pll, &pll->pll_limit[2], parent_rate,
|
||||
&rate, &detected, &detected_ssc);
|
||||
pll_get_mode_ctrl(PLL_GET_DIV_SEL(detected),
|
||||
fpll_check_mode_ctrl_restrict,
|
||||
pll->pll_limit, &detected);
|
||||
|
||||
spin_lock_irqsave(pll->common.lock, flags);
|
||||
|
||||
writel(detected_ssc, pll->common.base + pll->pll_syn->set);
|
||||
|
||||
regval = readl(pll->common.base + pll->pll_reg);
|
||||
regval = PLL_COPY_REG(regval, detected);
|
||||
|
||||
writel(regval, pll->common.base + pll->pll_reg);
|
||||
|
||||
spin_unlock_irqrestore(pll->common.lock, flags);
|
||||
|
||||
cv1800_clk_wait_for_lock(&pll->common, pll->pll_status.reg,
|
||||
BIT(pll->pll_status.shift));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 fpll_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
if (fpll_is_factional_mode(pll))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fpll_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct cv1800_clk_pll *pll = hw_to_cv1800_clk_pll(hw);
|
||||
|
||||
if (index)
|
||||
cv1800_clk_setbit(&pll->common, &pll->pll_syn->en);
|
||||
else
|
||||
cv1800_clk_clearbit(&pll->common, &pll->pll_syn->en);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct clk_ops cv1800_clk_fpll_ops = {
|
||||
.disable = pll_disable,
|
||||
.enable = pll_enable,
|
||||
.is_enabled = pll_is_enable,
|
||||
|
||||
.recalc_rate = fpll_recalc_rate,
|
||||
.determine_rate = fpll_determine_rate,
|
||||
.set_rate = fpll_set_rate,
|
||||
|
||||
.set_parent = fpll_set_parent,
|
||||
.get_parent = fpll_get_parent,
|
||||
};
|
||||
118
drivers/clk/sophgo/clk-cv18xx-pll.h
Normal file
118
drivers/clk/sophgo/clk-cv18xx-pll.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2023 Inochi Amaoto <inochiama@outlook.com>
|
||||
*/
|
||||
|
||||
#ifndef _CLK_SOPHGO_CV1800_PLL_H_
|
||||
#define _CLK_SOPHGO_CV1800_PLL_H_
|
||||
|
||||
#include "clk-cv18xx-common.h"
|
||||
|
||||
struct cv1800_clk_pll_limit {
|
||||
struct {
|
||||
u8 min;
|
||||
u8 max;
|
||||
} pre_div, div, post_div, ictrl, mode;
|
||||
};
|
||||
|
||||
#define _CV1800_PLL_LIMIT(_min, _max) \
|
||||
{ \
|
||||
.min = _min, \
|
||||
.max = _max, \
|
||||
} \
|
||||
|
||||
#define for_each_pll_limit_range(_var, _restrict) \
|
||||
for (_var = (_restrict)->min; _var <= (_restrict)->max; _var++)
|
||||
|
||||
struct cv1800_clk_pll_synthesizer {
|
||||
struct cv1800_clk_regbit en;
|
||||
struct cv1800_clk_regbit clk_half;
|
||||
u32 ctrl;
|
||||
u32 set;
|
||||
};
|
||||
|
||||
#define _PLL_PRE_DIV_SEL_FIELD GENMASK(6, 0)
|
||||
#define _PLL_POST_DIV_SEL_FIELD GENMASK(14, 8)
|
||||
#define _PLL_SEL_MODE_FIELD GENMASK(16, 15)
|
||||
#define _PLL_DIV_SEL_FIELD GENMASK(23, 17)
|
||||
#define _PLL_ICTRL_FIELD GENMASK(26, 24)
|
||||
|
||||
#define _PLL_ALL_FIELD_MASK \
|
||||
(_PLL_PRE_DIV_SEL_FIELD | \
|
||||
_PLL_POST_DIV_SEL_FIELD | \
|
||||
_PLL_SEL_MODE_FIELD | \
|
||||
_PLL_DIV_SEL_FIELD | \
|
||||
_PLL_ICTRL_FIELD)
|
||||
|
||||
#define PLL_COPY_REG(_dest, _src) \
|
||||
(((_dest) & (~_PLL_ALL_FIELD_MASK)) | ((_src) & _PLL_ALL_FIELD_MASK))
|
||||
|
||||
#define PLL_GET_PRE_DIV_SEL(_reg) \
|
||||
FIELD_GET(_PLL_PRE_DIV_SEL_FIELD, (_reg))
|
||||
#define PLL_GET_POST_DIV_SEL(_reg) \
|
||||
FIELD_GET(_PLL_POST_DIV_SEL_FIELD, (_reg))
|
||||
#define PLL_GET_SEL_MODE(_reg) \
|
||||
FIELD_GET(_PLL_SEL_MODE_FIELD, (_reg))
|
||||
#define PLL_GET_DIV_SEL(_reg) \
|
||||
FIELD_GET(_PLL_DIV_SEL_FIELD, (_reg))
|
||||
#define PLL_GET_ICTRL(_reg) \
|
||||
FIELD_GET(_PLL_ICTRL_FIELD, (_reg))
|
||||
|
||||
#define PLL_SET_PRE_DIV_SEL(_reg, _val) \
|
||||
_CV1800_SET_FIELD((_reg), (_val), _PLL_PRE_DIV_SEL_FIELD)
|
||||
#define PLL_SET_POST_DIV_SEL(_reg, _val) \
|
||||
_CV1800_SET_FIELD((_reg), (_val), _PLL_POST_DIV_SEL_FIELD)
|
||||
#define PLL_SET_SEL_MODE(_reg, _val) \
|
||||
_CV1800_SET_FIELD((_reg), (_val), _PLL_SEL_MODE_FIELD)
|
||||
#define PLL_SET_DIV_SEL(_reg, _val) \
|
||||
_CV1800_SET_FIELD((_reg), (_val), _PLL_DIV_SEL_FIELD)
|
||||
#define PLL_SET_ICTRL(_reg, _val) \
|
||||
_CV1800_SET_FIELD((_reg), (_val), _PLL_ICTRL_FIELD)
|
||||
|
||||
struct cv1800_clk_pll {
|
||||
struct cv1800_clk_common common;
|
||||
u32 pll_reg;
|
||||
struct cv1800_clk_regbit pll_pwd;
|
||||
struct cv1800_clk_regbit pll_status;
|
||||
const struct cv1800_clk_pll_limit *pll_limit;
|
||||
struct cv1800_clk_pll_synthesizer *pll_syn;
|
||||
};
|
||||
|
||||
#define CV1800_INTEGRAL_PLL(_name, _parent, _pll_reg, \
|
||||
_pll_pwd_reg, _pll_pwd_shift, \
|
||||
_pll_status_reg, _pll_status_shift, \
|
||||
_pll_limit, _flags) \
|
||||
struct cv1800_clk_pll _name = { \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
&cv1800_clk_ipll_ops,\
|
||||
_flags), \
|
||||
.pll_reg = _pll_reg, \
|
||||
.pll_pwd = CV1800_CLK_BIT(_pll_pwd_reg, \
|
||||
_pll_pwd_shift), \
|
||||
.pll_status = CV1800_CLK_BIT(_pll_status_reg, \
|
||||
_pll_status_shift), \
|
||||
.pll_limit = _pll_limit, \
|
||||
.pll_syn = NULL, \
|
||||
}
|
||||
|
||||
#define CV1800_FACTIONAL_PLL(_name, _parent, _pll_reg, \
|
||||
_pll_pwd_reg, _pll_pwd_shift, \
|
||||
_pll_status_reg, _pll_status_shift, \
|
||||
_pll_limit, _pll_syn, _flags) \
|
||||
struct cv1800_clk_pll _name = { \
|
||||
.common = CV1800_CLK_COMMON(#_name, _parent, \
|
||||
&cv1800_clk_fpll_ops,\
|
||||
_flags), \
|
||||
.pll_reg = _pll_reg, \
|
||||
.pll_pwd = CV1800_CLK_BIT(_pll_pwd_reg, \
|
||||
_pll_pwd_shift), \
|
||||
.pll_status = CV1800_CLK_BIT(_pll_status_reg, \
|
||||
_pll_status_shift), \
|
||||
.pll_limit = _pll_limit, \
|
||||
.pll_syn = _pll_syn, \
|
||||
}
|
||||
|
||||
extern const struct clk_ops cv1800_clk_ipll_ops;
|
||||
extern const struct clk_ops cv1800_clk_fpll_ops;
|
||||
|
||||
#endif // _CLK_SOPHGO_CV1800_PLL_H_
|
||||
|
|
@ -7,24 +7,40 @@
|
|||
#ifndef __DT_BINDINGS_CLOCK_LOONGSON2_H
|
||||
#define __DT_BINDINGS_CLOCK_LOONGSON2_H
|
||||
|
||||
#define LOONGSON2_REF_100M 0
|
||||
#define LOONGSON2_NODE_PLL 1
|
||||
#define LOONGSON2_DDR_PLL 2
|
||||
#define LOONGSON2_DC_PLL 3
|
||||
#define LOONGSON2_PIX0_PLL 4
|
||||
#define LOONGSON2_PIX1_PLL 5
|
||||
#define LOONGSON2_NODE_CLK 6
|
||||
#define LOONGSON2_HDA_CLK 7
|
||||
#define LOONGSON2_GPU_CLK 8
|
||||
#define LOONGSON2_DDR_CLK 9
|
||||
#define LOONGSON2_GMAC_CLK 10
|
||||
#define LOONGSON2_DC_CLK 11
|
||||
#define LOONGSON2_APB_CLK 12
|
||||
#define LOONGSON2_USB_CLK 13
|
||||
#define LOONGSON2_SATA_CLK 14
|
||||
#define LOONGSON2_PIX0_CLK 15
|
||||
#define LOONGSON2_PIX1_CLK 16
|
||||
#define LOONGSON2_BOOT_CLK 17
|
||||
#define LOONGSON2_CLK_END 18
|
||||
#define LOONGSON2_REF_100M 0
|
||||
#define LOONGSON2_NODE_PLL 1
|
||||
#define LOONGSON2_DDR_PLL 2
|
||||
#define LOONGSON2_DC_PLL 3
|
||||
#define LOONGSON2_PIX0_PLL 4
|
||||
#define LOONGSON2_PIX1_PLL 5
|
||||
#define LOONGSON2_NODE_CLK 6
|
||||
#define LOONGSON2_HDA_CLK 7
|
||||
#define LOONGSON2_GPU_CLK 8
|
||||
#define LOONGSON2_DDR_CLK 9
|
||||
#define LOONGSON2_GMAC_CLK 10
|
||||
#define LOONGSON2_DC_CLK 11
|
||||
#define LOONGSON2_APB_CLK 12
|
||||
#define LOONGSON2_USB_CLK 13
|
||||
#define LOONGSON2_SATA_CLK 14
|
||||
#define LOONGSON2_PIX0_CLK 15
|
||||
#define LOONGSON2_PIX1_CLK 16
|
||||
#define LOONGSON2_BOOT_CLK 17
|
||||
#define LOONGSON2_OUT0_GATE 18
|
||||
#define LOONGSON2_GMAC_GATE 19
|
||||
#define LOONGSON2_RIO_GATE 20
|
||||
#define LOONGSON2_DC_GATE 21
|
||||
#define LOONGSON2_GPU_GATE 22
|
||||
#define LOONGSON2_DDR_GATE 23
|
||||
#define LOONGSON2_HDA_GATE 24
|
||||
#define LOONGSON2_NODE_GATE 25
|
||||
#define LOONGSON2_EMMC_GATE 26
|
||||
#define LOONGSON2_PIX0_GATE 27
|
||||
#define LOONGSON2_PIX1_GATE 28
|
||||
#define LOONGSON2_OUT0_CLK 29
|
||||
#define LOONGSON2_RIO_CLK 30
|
||||
#define LOONGSON2_EMMC_CLK 31
|
||||
#define LOONGSON2_DES_CLK 32
|
||||
#define LOONGSON2_I2S_CLK 33
|
||||
#define LOONGSON2_MISC_CLK 34
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user