Merge branches 'clk-x86', 'clk-xilinx', 'clk-cleanup', 'clk-mstar' and 'clk-ingenic' into clk-next

- Make MxL's CGU driver secure compatible
 - Support for CPU PLL on MStar/SigmaStar SoCs
 - Ingenic JZ4755 SoC clk support
 - Support audio clks on X1000 SoCs

* clk-x86:
  clk: mxl: syscon_node_to_regmap() returns error pointers
  clk: mxl: Fix a clk entry by adding relevant flags
  clk: mxl: Add option to override gate clks
  clk: mxl: Remove redundant spinlocks
  clk: mxl: Switch from direct readl/writel based IO to regmap based IO

* clk-xilinx:
  clk: xilinx: Drop duplicate depends on COMMON_CLK

* clk-cleanup:
  clk: nomadik: correct struct name kernel-doc warning
  clk: lmk04832: fix kernel-doc warnings
  clk: lmk04832: drop superfluous #include
  clk: lmk04832: drop unnecessary semicolons
  clk: lmk04832: declare variables as const when possible
  clk: socfpga: Fix memory leak in socfpga_gate_init()
  clk: st: Fix memory leak in st_of_quadfs_setup()
  clk: samsung: Fix memory leak in _samsung_clk_register_pll()
  clk: visconti: Fix memory leak in visconti_register_pll()
  clk: Remove a useless include
  clk: samsung: Fix reference to CLK_OF_DECLARE in comment
  clk: stm32mp1: Staticize ethrx_src
  clk: keystone: syscon-clk: Use dev_err_probe() helper
  clk: bulk: Use dev_err_probe() helper in __clk_bulk_get()
  clk: cdce925: simplify using devm_regulator_get_enable()

* clk-mstar:
  clk: mstar: msc313 cpupll clk driver

* clk-ingenic:
  clk: Add Ingenic JZ4755 CGU driver
  dt-bindings: clock: Add Ingenic JZ4755 CGU header
  dt-bindings: ingenic: Add support for the JZ4755 CGU
  clk: ingenic: Minor cosmetic fixups for X1000
  clk: ingenic: Add X1000 audio clocks
  dt-bindings: ingenic,x1000-cgu: Add audio clocks
  clk: ingenic: Add .set_rate_hook() for PLL clocks
  clk: ingenic: Make PLL clock enable_bit and stable_bit optional
  clk: ingenic: Make PLL clock "od" field optional
This commit is contained in:
Stephen Boyd 2022-12-12 11:12:26 -08:00
32 changed files with 897 additions and 223 deletions

View File

@ -22,6 +22,7 @@ select:
enum:
- ingenic,jz4740-cgu
- ingenic,jz4725b-cgu
- ingenic,jz4755-cgu
- ingenic,jz4760-cgu
- ingenic,jz4760b-cgu
- ingenic,jz4770-cgu
@ -51,6 +52,7 @@ properties:
- enum:
- ingenic,jz4740-cgu
- ingenic,jz4725b-cgu
- ingenic,jz4755-cgu
- ingenic,jz4760-cgu
- ingenic,jz4760b-cgu
- ingenic,jz4770-cgu

View File

@ -96,9 +96,9 @@ static int __clk_bulk_get(struct device *dev, int num_clks,
if (ret == -ENOENT && optional)
continue;
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get clk '%s': %d\n",
clks[i].id, ret);
dev_err_probe(dev, ret,
"Failed to get clk '%s'\n",
clks[i].id);
goto err;
}
}

View File

@ -603,28 +603,15 @@ of_clk_cdce925_get(struct of_phandle_args *clkspec, void *_data)
return &data->clk[idx].hw;
}
static void cdce925_regulator_disable(void *regulator)
{
regulator_disable(regulator);
}
static int cdce925_regulator_enable(struct device *dev, const char *name)
{
struct regulator *regulator;
int err;
regulator = devm_regulator_get(dev, name);
if (IS_ERR(regulator))
return PTR_ERR(regulator);
err = devm_regulator_get_enable(dev, name);
if (err)
dev_err_probe(dev, err, "Failed to enable %s:\n", name);
err = regulator_enable(regulator);
if (err) {
dev_err(dev, "Failed to enable %s: %d\n", name, err);
return err;
}
return devm_add_action_or_reset(dev, cdce925_regulator_disable,
regulator);
return err;
}
/* The CDCE925 uses a funky way to read/write registers. Bulk mode is

View File

@ -12,12 +12,10 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/gcd.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
@ -177,14 +175,14 @@ enum lmk04832_device_types {
};
/**
* lmk04832_device_info - Holds static device information that is specific to
* the chip revision
* struct lmk04832_device_info - Holds static device information that is
* specific to the chip revision
*
* pid: Product Identifier
* maskrev: IC version identifier
* num_channels: Number of available output channels (clkout count)
* vco0_range: {min, max} of the VCO0 operating range (in MHz)
* vco1_range: {min, max} of the VCO1 operating range (in MHz)
* @pid: Product Identifier
* @maskrev: IC version identifier
* @num_channels: Number of available output channels (clkout count)
* @vco0_range: {min, max} of the VCO0 operating range (in MHz)
* @vco1_range: {min, max} of the VCO1 operating range (in MHz)
*/
struct lmk04832_device_info {
u16 pid;
@ -282,7 +280,7 @@ static bool lmk04832_regmap_rd_regs(struct device *dev, unsigned int reg)
default:
return false;
};
};
}
static bool lmk04832_regmap_wr_regs(struct device *dev, unsigned int reg)
{
@ -305,7 +303,7 @@ static bool lmk04832_regmap_wr_regs(struct device *dev, unsigned int reg)
default:
return false;
};
};
}
static const struct regmap_config regmap_config = {
.name = "lmk04832",
@ -371,7 +369,7 @@ static unsigned long lmk04832_vco_recalc_rate(struct clk_hw *hw,
unsigned long prate)
{
struct lmk04832 *lmk = container_of(hw, struct lmk04832, vco);
unsigned int pll2_p[] = {8, 2, 2, 3, 4, 5, 6, 7};
const unsigned int pll2_p[] = {8, 2, 2, 3, 4, 5, 6, 7};
unsigned int pll2_n, p, pll2_r;
unsigned int pll2_misc;
unsigned long vco_rate;
@ -403,7 +401,7 @@ static unsigned long lmk04832_vco_recalc_rate(struct clk_hw *hw,
pll2_misc)) * pll2_n * pll2_p[p] / pll2_r;
return vco_rate;
};
}
/**
* lmk04832_check_vco_ranges - Check requested VCO frequency against VCO ranges
@ -414,7 +412,7 @@ static unsigned long lmk04832_vco_recalc_rate(struct clk_hw *hw,
* The LMK04832 has 2 internal VCO, each with independent operating ranges.
* Use the device_info structure to determine which VCO to use based on rate.
*
* Returns VCO_MUX value or negative errno.
* Returns: VCO_MUX value or negative errno.
*/
static int lmk04832_check_vco_ranges(struct lmk04832 *lmk, unsigned long rate)
{
@ -451,7 +449,7 @@ static int lmk04832_check_vco_ranges(struct lmk04832 *lmk, unsigned long rate)
*
* VCO = OSCin * 2 * PLL2_N * PLL2_P / PLL2_R
*
* Returns vco rate or negative errno.
* Returns: vco rate or negative errno.
*/
static long lmk04832_calc_pll2_params(unsigned long prate, unsigned long rate,
unsigned int *n, unsigned int *p,
@ -509,7 +507,7 @@ static long lmk04832_vco_round_rate(struct clk_hw *hw, unsigned long rate,
return -EINVAL;
return vco_rate;
};
}
static int lmk04832_vco_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long prate)
@ -568,7 +566,7 @@ static int lmk04832_vco_set_rate(struct clk_hw *hw, unsigned long rate,
return regmap_write(lmk->regmap, LMK04832_REG_PLL2_N_2,
FIELD_GET(0x0000ff, n));
};
}
static const struct clk_ops lmk04832_vco_ops = {
.is_enabled = lmk04832_vco_is_enabled,
@ -633,7 +631,7 @@ static int lmk04832_register_vco(struct lmk04832 *lmk)
static int lmk04832_clkout_set_ddly(struct lmk04832 *lmk, int id)
{
int dclk_div_adj[] = {0, 0, -2, -2, 0, 3, -1, 0};
const int dclk_div_adj[] = {0, 0, -2, -2, 0, 3, -1, 0};
unsigned int sclkx_y_ddly = 10;
unsigned int dclkx_y_ddly;
unsigned int dclkx_y_div;
@ -1063,7 +1061,7 @@ static unsigned long lmk04832_dclk_recalc_rate(struct clk_hw *hw,
rate = DIV_ROUND_CLOSEST(prate, dclk_div);
return rate;
};
}
static long lmk04832_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
@ -1085,7 +1083,7 @@ static long lmk04832_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
return -EINVAL;
return dclk_rate;
};
}
static int lmk04832_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long prate)
@ -1147,7 +1145,7 @@ static int lmk04832_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
dev_err(lmk->dev, "SYNC sequence failed\n");
return ret;
};
}
static const struct clk_ops lmk04832_dclk_ops = {
.is_enabled = lmk04832_dclk_is_enabled,
@ -1551,6 +1549,7 @@ static void lmk04832_remove(struct spi_device *spi)
clk_disable_unprepare(lmk->oscin);
of_clk_del_provider(spi->dev.of_node);
}
static const struct spi_device_id lmk04832_id[] = {
{ "lmk04832", LMK04832 },
{}

View File

@ -138,7 +138,7 @@ out_put:
}
/**
* struct clk_pll1 - Nomadik PLL1 clock
* struct clk_pll - Nomadik PLL clock
* @hw: corresponding clock hardware entry
* @id: PLL instance: 1 or 2
*/

View File

@ -155,7 +155,7 @@ static const char * const eth_src[] = {
"pll4_p", "pll3_q"
};
const struct clk_parent_data ethrx_src[] = {
static const struct clk_parent_data ethrx_src[] = {
{ .name = "ethck_k", .fw_name = "ETH_RX_CLK/ETH_REF_CLK" },
};

View File

@ -20,7 +20,6 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/rational.h>
#include <linux/regmap.h>
#include <linux/slab.h>

View File

@ -15,6 +15,16 @@ config INGENIC_CGU_JZ4740
If building for a JZ4740 SoC, you want to say Y here.
config INGENIC_CGU_JZ4755
bool "Ingenic JZ4755 CGU driver"
default MACH_JZ4755
select INGENIC_CGU_COMMON
help
Support the clocks provided by the CGU hardware on Ingenic JZ4755
and compatible SoCs.
If building for a JZ4755 SoC, you want to say Y here.
config INGENIC_CGU_JZ4725B
bool "Ingenic JZ4725B CGU driver"
default MACH_JZ4725B

View File

@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_INGENIC_CGU_COMMON) += cgu.o pm.o
obj-$(CONFIG_INGENIC_CGU_JZ4740) += jz4740-cgu.o
obj-$(CONFIG_INGENIC_CGU_JZ4755) += jz4755-cgu.o
obj-$(CONFIG_INGENIC_CGU_JZ4725B) += jz4725b-cgu.o
obj-$(CONFIG_INGENIC_CGU_JZ4760) += jz4760-cgu.o
obj-$(CONFIG_INGENIC_CGU_JZ4770) += jz4770-cgu.o

View File

@ -83,7 +83,7 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_pll_info *pll_info;
unsigned m, n, od_enc, od;
unsigned m, n, od, od_enc = 0;
bool bypass;
u32 ctl;
@ -96,8 +96,11 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
m += pll_info->m_offset;
n = (ctl >> pll_info->n_shift) & GENMASK(pll_info->n_bits - 1, 0);
n += pll_info->n_offset;
od_enc = ctl >> pll_info->od_shift;
od_enc &= GENMASK(pll_info->od_bits - 1, 0);
if (pll_info->od_bits > 0) {
od_enc = ctl >> pll_info->od_shift;
od_enc &= GENMASK(pll_info->od_bits - 1, 0);
}
if (pll_info->bypass_bit >= 0) {
ctl = readl(cgu->base + pll_info->bypass_reg);
@ -108,11 +111,15 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
return parent_rate;
}
for (od = 0; od < pll_info->od_max; od++) {
for (od = 0; od < pll_info->od_max; od++)
if (pll_info->od_encoding[od] == od_enc)
break;
}
BUG_ON(od == pll_info->od_max);
/* if od_max = 0, od_bits should be 0 and od is fixed to 1. */
if (pll_info->od_max == 0)
BUG_ON(pll_info->od_bits != 0);
else
BUG_ON(od == pll_info->od_max);
od++;
return div_u64((u64)parent_rate * m * pll_info->rate_multiplier,
@ -182,6 +189,9 @@ static inline int ingenic_pll_check_stable(struct ingenic_cgu *cgu,
{
u32 ctl;
if (pll_info->stable_bit < 0)
return 0;
return readl_poll_timeout(cgu->base + pll_info->reg, ctl,
ctl & BIT(pll_info->stable_bit),
0, 100 * USEC_PER_MSEC);
@ -215,13 +225,18 @@ ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate,
ctl &= ~(GENMASK(pll_info->n_bits - 1, 0) << pll_info->n_shift);
ctl |= (n - pll_info->n_offset) << pll_info->n_shift;
ctl &= ~(GENMASK(pll_info->od_bits - 1, 0) << pll_info->od_shift);
ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift;
if (pll_info->od_bits > 0) {
ctl &= ~(GENMASK(pll_info->od_bits - 1, 0) << pll_info->od_shift);
ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift;
}
writel(ctl, cgu->base + pll_info->reg);
if (pll_info->set_rate_hook)
pll_info->set_rate_hook(pll_info, rate, parent_rate);
/* If the PLL is enabled, verify that it's stable */
if (ctl & BIT(pll_info->enable_bit))
if (pll_info->enable_bit >= 0 && (ctl & BIT(pll_info->enable_bit)))
ret = ingenic_pll_check_stable(cgu, pll_info);
spin_unlock_irqrestore(&cgu->lock, flags);
@ -239,6 +254,9 @@ static int ingenic_pll_enable(struct clk_hw *hw)
int ret;
u32 ctl;
if (pll_info->enable_bit < 0)
return 0;
spin_lock_irqsave(&cgu->lock, flags);
if (pll_info->bypass_bit >= 0) {
ctl = readl(cgu->base + pll_info->bypass_reg);
@ -269,6 +287,9 @@ static void ingenic_pll_disable(struct clk_hw *hw)
unsigned long flags;
u32 ctl;
if (pll_info->enable_bit < 0)
return;
spin_lock_irqsave(&cgu->lock, flags);
ctl = readl(cgu->base + pll_info->reg);
@ -286,6 +307,9 @@ static int ingenic_pll_is_enabled(struct clk_hw *hw)
const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll;
u32 ctl;
if (pll_info->enable_bit < 0)
return true;
ctl = readl(cgu->base + pll_info->reg);
return !!(ctl & BIT(pll_info->enable_bit));

View File

@ -33,7 +33,8 @@
* @od_shift: the number of bits to shift the post-VCO divider value by (ie.
* the index of the lowest bit of the post-VCO divider value in
* the PLL's control register)
* @od_bits: the size of the post-VCO divider field in bits
* @od_bits: the size of the post-VCO divider field in bits, or 0 if no
* OD field exists (then the OD is fixed to 1)
* @od_max: the maximum post-VCO divider value
* @od_encoding: a pointer to an array mapping post-VCO divider values to
* their encoded values in the PLL control register, or -1 for
@ -41,8 +42,12 @@
* @bypass_reg: the offset of the bypass control register within the CGU
* @bypass_bit: the index of the bypass bit in the PLL control register, or
* -1 if there is no bypass bit
* @enable_bit: the index of the enable bit in the PLL control register
* @stable_bit: the index of the stable bit in the PLL control register
* @enable_bit: the index of the enable bit in the PLL control register, or
* -1 if there is no enable bit (ie, the PLL is always on)
* @stable_bit: the index of the stable bit in the PLL control register, or
* -1 if there is no stable bit
* @set_rate_hook: hook called immediately after updating the CGU register,
* before releasing the spinlock
*/
struct ingenic_cgu_pll_info {
unsigned reg;
@ -53,11 +58,13 @@ struct ingenic_cgu_pll_info {
u8 od_shift, od_bits, od_max;
unsigned bypass_reg;
s8 bypass_bit;
u8 enable_bit;
u8 stable_bit;
s8 enable_bit;
s8 stable_bit;
void (*calc_m_n_od)(const struct ingenic_cgu_pll_info *pll_info,
unsigned long rate, unsigned long parent_rate,
unsigned int *m, unsigned int *n, unsigned int *od);
void (*set_rate_hook)(const struct ingenic_cgu_pll_info *pll_info,
unsigned long rate, unsigned long parent_rate);
};
/**

View File

@ -0,0 +1,346 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Ingenic JZ4755 SoC CGU driver
* Heavily based on JZ4725b CGU driver
*
* Copyright (C) 2022 Siarhei Volkau
* Author: Siarhei Volkau <lis8215@gmail.com>
*/
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <dt-bindings/clock/ingenic,jz4755-cgu.h>
#include "cgu.h"
#include "pm.h"
/* CGU register offsets */
#define CGU_REG_CPCCR 0x00
#define CGU_REG_CPPCR 0x10
#define CGU_REG_CLKGR 0x20
#define CGU_REG_OPCR 0x24
#define CGU_REG_I2SCDR 0x60
#define CGU_REG_LPCDR 0x64
#define CGU_REG_MSCCDR 0x68
#define CGU_REG_SSICDR 0x74
#define CGU_REG_CIMCDR 0x7C
static struct ingenic_cgu *cgu;
static const s8 pll_od_encoding[4] = {
0x0, 0x1, -1, 0x3,
};
static const u8 jz4755_cgu_cpccr_div_table[] = {
1, 2, 3, 4, 6, 8,
};
static const u8 jz4755_cgu_pll_half_div_table[] = {
2, 1,
};
static const struct ingenic_cgu_clk_info jz4755_cgu_clocks[] = {
/* External clocks */
[JZ4755_CLK_EXT] = { "ext", CGU_CLK_EXT },
[JZ4755_CLK_OSC32K] = { "osc32k", CGU_CLK_EXT },
[JZ4755_CLK_PLL] = {
"pll", CGU_CLK_PLL,
.parents = { JZ4755_CLK_EXT, },
.pll = {
.reg = CGU_REG_CPPCR,
.rate_multiplier = 1,
.m_shift = 23,
.m_bits = 9,
.m_offset = 2,
.n_shift = 18,
.n_bits = 5,
.n_offset = 2,
.od_shift = 16,
.od_bits = 2,
.od_max = 4,
.od_encoding = pll_od_encoding,
.stable_bit = 10,
.bypass_reg = CGU_REG_CPPCR,
.bypass_bit = 9,
.enable_bit = 8,
},
},
/* Muxes & dividers */
[JZ4755_CLK_PLL_HALF] = {
"pll half", CGU_CLK_DIV,
.parents = { JZ4755_CLK_PLL, },
.div = {
CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1, 0,
jz4755_cgu_pll_half_div_table,
},
},
[JZ4755_CLK_EXT_HALF] = {
"ext half", CGU_CLK_DIV,
.parents = { JZ4755_CLK_EXT, },
.div = {
CGU_REG_CPCCR, 30, 1, 1, -1, -1, -1, 0,
NULL,
},
},
[JZ4755_CLK_CCLK] = {
"cclk", CGU_CLK_DIV,
.parents = { JZ4755_CLK_PLL, },
.div = {
CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 0,
jz4755_cgu_cpccr_div_table,
},
},
[JZ4755_CLK_H0CLK] = {
"hclk", CGU_CLK_DIV,
.parents = { JZ4755_CLK_PLL, },
.div = {
CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 0,
jz4755_cgu_cpccr_div_table,
},
},
[JZ4755_CLK_PCLK] = {
"pclk", CGU_CLK_DIV,
.parents = { JZ4755_CLK_PLL, },
.div = {
CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 0,
jz4755_cgu_cpccr_div_table,
},
},
[JZ4755_CLK_MCLK] = {
"mclk", CGU_CLK_DIV,
.parents = { JZ4755_CLK_PLL, },
.div = {
CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 0,
jz4755_cgu_cpccr_div_table,
},
},
[JZ4755_CLK_H1CLK] = {
"h1clk", CGU_CLK_DIV,
.parents = { JZ4755_CLK_PLL, },
.div = {
CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1, 0,
jz4755_cgu_cpccr_div_table,
},
},
[JZ4755_CLK_UDC] = {
"udc", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4755_CLK_EXT_HALF, JZ4755_CLK_PLL_HALF, },
.mux = { CGU_REG_CPCCR, 29, 1 },
.div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 10 },
},
[JZ4755_CLK_LCD] = {
"lcd", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4755_CLK_PLL_HALF, },
.div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 9 },
},
[JZ4755_CLK_MMC] = {
"mmc", CGU_CLK_DIV,
.parents = { JZ4755_CLK_PLL_HALF, },
.div = { CGU_REG_MSCCDR, 0, 1, 5, -1, -1, -1 },
},
[JZ4755_CLK_I2S] = {
"i2s", CGU_CLK_MUX | CGU_CLK_DIV,
.parents = { JZ4755_CLK_EXT_HALF, JZ4755_CLK_PLL_HALF, },
.mux = { CGU_REG_CPCCR, 31, 1 },
.div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 },
},
[JZ4755_CLK_SPI] = {
"spi", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4755_CLK_PLL_HALF, },
.div = { CGU_REG_SSICDR, 0, 1, 4, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 4 },
},
[JZ4755_CLK_TVE] = {
"tve", CGU_CLK_MUX | CGU_CLK_GATE,
.parents = { JZ4755_CLK_LCD, JZ4755_CLK_EXT, },
.mux = { CGU_REG_LPCDR, 31, 1 },
.gate = { CGU_REG_CLKGR, 18 },
},
[JZ4755_CLK_RTC] = {
"rtc", CGU_CLK_MUX | CGU_CLK_GATE,
.parents = { JZ4755_CLK_EXT512, JZ4755_CLK_OSC32K, },
.mux = { CGU_REG_OPCR, 2, 1},
.gate = { CGU_REG_CLKGR, 2 },
},
[JZ4755_CLK_CIM] = {
"cim", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4755_CLK_PLL_HALF, },
.div = { CGU_REG_CIMCDR, 0, 1, 8, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 8 },
},
/* Gate-only clocks */
[JZ4755_CLK_UART0] = {
"uart0", CGU_CLK_GATE,
.parents = { JZ4755_CLK_EXT_HALF, },
.gate = { CGU_REG_CLKGR, 0 },
},
[JZ4755_CLK_UART1] = {
"uart1", CGU_CLK_GATE,
.parents = { JZ4755_CLK_EXT_HALF, },
.gate = { CGU_REG_CLKGR, 14 },
},
[JZ4755_CLK_UART2] = {
"uart2", CGU_CLK_GATE,
.parents = { JZ4755_CLK_EXT_HALF, },
.gate = { CGU_REG_CLKGR, 15 },
},
[JZ4755_CLK_ADC] = {
"adc", CGU_CLK_GATE,
.parents = { JZ4755_CLK_EXT_HALF, },
.gate = { CGU_REG_CLKGR, 7 },
},
[JZ4755_CLK_AIC] = {
"aic", CGU_CLK_GATE,
.parents = { JZ4755_CLK_EXT_HALF, },
.gate = { CGU_REG_CLKGR, 5 },
},
[JZ4755_CLK_I2C] = {
"i2c", CGU_CLK_GATE,
.parents = { JZ4755_CLK_EXT_HALF, },
.gate = { CGU_REG_CLKGR, 3 },
},
[JZ4755_CLK_BCH] = {
"bch", CGU_CLK_GATE,
.parents = { JZ4755_CLK_H1CLK, },
.gate = { CGU_REG_CLKGR, 11 },
},
[JZ4755_CLK_TCU] = {
"tcu", CGU_CLK_GATE,
.parents = { JZ4755_CLK_EXT, },
.gate = { CGU_REG_CLKGR, 1 },
},
[JZ4755_CLK_DMA] = {
"dma", CGU_CLK_GATE,
.parents = { JZ4755_CLK_PCLK, },
.gate = { CGU_REG_CLKGR, 12 },
},
[JZ4755_CLK_MMC0] = {
"mmc0", CGU_CLK_GATE,
.parents = { JZ4755_CLK_MMC, },
.gate = { CGU_REG_CLKGR, 6 },
},
[JZ4755_CLK_MMC1] = {
"mmc1", CGU_CLK_GATE,
.parents = { JZ4755_CLK_MMC, },
.gate = { CGU_REG_CLKGR, 16 },
},
[JZ4755_CLK_AUX_CPU] = {
"aux_cpu", CGU_CLK_GATE,
.parents = { JZ4755_CLK_H1CLK, },
.gate = { CGU_REG_CLKGR, 24 },
},
[JZ4755_CLK_AHB1] = {
"ahb1", CGU_CLK_GATE,
.parents = { JZ4755_CLK_H1CLK, },
.gate = { CGU_REG_CLKGR, 23 },
},
[JZ4755_CLK_IDCT] = {
"idct", CGU_CLK_GATE,
.parents = { JZ4755_CLK_H1CLK, },
.gate = { CGU_REG_CLKGR, 22 },
},
[JZ4755_CLK_DB] = {
"db", CGU_CLK_GATE,
.parents = { JZ4755_CLK_H1CLK, },
.gate = { CGU_REG_CLKGR, 21 },
},
[JZ4755_CLK_ME] = {
"me", CGU_CLK_GATE,
.parents = { JZ4755_CLK_H1CLK, },
.gate = { CGU_REG_CLKGR, 20 },
},
[JZ4755_CLK_MC] = {
"mc", CGU_CLK_GATE,
.parents = { JZ4755_CLK_H1CLK, },
.gate = { CGU_REG_CLKGR, 19 },
},
[JZ4755_CLK_TSSI] = {
"tssi", CGU_CLK_GATE,
.parents = { JZ4755_CLK_EXT_HALF/* not sure */, },
.gate = { CGU_REG_CLKGR, 17 },
},
[JZ4755_CLK_IPU] = {
"ipu", CGU_CLK_GATE,
.parents = { JZ4755_CLK_PLL_HALF/* not sure */, },
.gate = { CGU_REG_CLKGR, 13 },
},
[JZ4755_CLK_EXT512] = {
"ext/512", CGU_CLK_FIXDIV,
.parents = { JZ4755_CLK_EXT, },
.fixdiv = { 512 },
},
[JZ4755_CLK_UDC_PHY] = {
"udc_phy", CGU_CLK_GATE,
.parents = { JZ4755_CLK_EXT_HALF, },
.gate = { CGU_REG_OPCR, 6, true },
},
};
static void __init jz4755_cgu_init(struct device_node *np)
{
int retval;
cgu = ingenic_cgu_new(jz4755_cgu_clocks,
ARRAY_SIZE(jz4755_cgu_clocks), np);
if (!cgu) {
pr_err("%s: failed to initialise CGU\n", __func__);
return;
}
retval = ingenic_cgu_register_clocks(cgu);
if (retval)
pr_err("%s: failed to register CGU Clocks\n", __func__);
ingenic_cgu_register_syscore_ops(cgu);
}
/*
* CGU has some children devices, this is useful for probing children devices
* in the case where the device node is compatible with "simple-mfd".
*/
CLK_OF_DECLARE_DRIVER(jz4755_cgu, "ingenic,jz4755-cgu", jz4755_cgu_init);

View File

@ -8,6 +8,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/rational.h>
#include <dt-bindings/clock/ingenic,x1000-cgu.h>
@ -168,6 +169,38 @@ static const struct clk_ops x1000_otg_phy_ops = {
.is_enabled = x1000_usb_phy_is_enabled,
};
static void
x1000_i2spll_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info,
unsigned long rate, unsigned long parent_rate,
unsigned int *pm, unsigned int *pn, unsigned int *pod)
{
const unsigned long m_max = GENMASK(pll_info->m_bits - 1, 0);
const unsigned long n_max = GENMASK(pll_info->n_bits - 1, 0);
unsigned long m, n;
rational_best_approximation(rate, parent_rate, m_max, n_max, &m, &n);
/* n should not be less than 2*m */
if (n < 2 * m)
n = 2 * m;
*pm = m;
*pn = n;
*pod = 1;
}
static void
x1000_i2spll_set_rate_hook(const struct ingenic_cgu_pll_info *pll_info,
unsigned long rate, unsigned long parent_rate)
{
/*
* Writing 0 causes I2SCDR1.I2SDIV_D to be automatically recalculated
* based on the current value of I2SCDR.I2SDIV_N, which is needed for
* the divider to function correctly.
*/
writel(0, cgu->base + CGU_REG_I2SCDR1);
}
static const s8 pll_od_encoding[8] = {
0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3,
};
@ -183,7 +216,7 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_APLL] = {
"apll", CGU_CLK_PLL,
.parents = { X1000_CLK_EXCLK, -1, -1, -1 },
.parents = { X1000_CLK_EXCLK },
.pll = {
.reg = CGU_REG_APLL,
.rate_multiplier = 1,
@ -206,7 +239,7 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_MPLL] = {
"mpll", CGU_CLK_PLL,
.parents = { X1000_CLK_EXCLK, -1, -1, -1 },
.parents = { X1000_CLK_EXCLK },
.pll = {
.reg = CGU_REG_MPLL,
.rate_multiplier = 1,
@ -256,7 +289,7 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
* system; mark it critical.
*/
.flags = CLK_IS_CRITICAL,
.parents = { X1000_CLK_CPUMUX, -1, -1, -1 },
.parents = { X1000_CLK_CPUMUX },
.div = { CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1 },
.gate = { CGU_REG_CLKGR, 30 },
},
@ -268,7 +301,7 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
* disabling it or any parent clocks will hang the system.
*/
.flags = CLK_IS_CRITICAL,
.parents = { X1000_CLK_CPUMUX, -1, -1, -1 },
.parents = { X1000_CLK_CPUMUX },
.div = { CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1 },
},
@ -287,13 +320,13 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_AHB2] = {
"ahb2", CGU_CLK_DIV,
.parents = { X1000_CLK_AHB2PMUX, -1, -1, -1 },
.parents = { X1000_CLK_AHB2PMUX },
.div = { CGU_REG_CPCCR, 12, 1, 4, 20, -1, -1 },
},
[X1000_CLK_PCLK] = {
"pclk", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { X1000_CLK_AHB2PMUX, -1, -1, -1 },
.parents = { X1000_CLK_AHB2PMUX },
.div = { CGU_REG_CPCCR, 16, 1, 4, 20, -1, -1 },
.gate = { CGU_REG_CLKGR, 28 },
},
@ -319,6 +352,37 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
.gate = { CGU_REG_CLKGR, 25 },
},
[X1000_CLK_I2SPLLMUX] = {
"i2s_pll_mux", CGU_CLK_MUX,
.parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL },
.mux = { CGU_REG_I2SCDR, 31, 1 },
},
[X1000_CLK_I2SPLL] = {
"i2s_pll", CGU_CLK_PLL,
.parents = { X1000_CLK_I2SPLLMUX },
.pll = {
.reg = CGU_REG_I2SCDR,
.rate_multiplier = 1,
.m_shift = 13,
.m_bits = 9,
.n_shift = 0,
.n_bits = 13,
.calc_m_n_od = x1000_i2spll_calc_m_n_od,
.set_rate_hook = x1000_i2spll_set_rate_hook,
},
},
[X1000_CLK_I2S] = {
"i2s", CGU_CLK_MUX,
.parents = { X1000_CLK_EXCLK, -1, -1, X1000_CLK_I2SPLL },
/*
* NOTE: the mux is at bit 30; bit 29 enables the M/N divider.
* Therefore, the divider is disabled when EXCLK is selected.
*/
.mux = { CGU_REG_I2SCDR, 29, 2 },
},
[X1000_CLK_LCD] = {
"lcd", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL },
@ -329,13 +393,13 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_MSCMUX] = {
"msc_mux", CGU_CLK_MUX,
.parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL},
.parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL },
.mux = { CGU_REG_MSC0CDR, 31, 1 },
},
[X1000_CLK_MSC0] = {
"msc0", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { X1000_CLK_MSCMUX, -1, -1, -1 },
.parents = { X1000_CLK_MSCMUX },
.div = { CGU_REG_MSC0CDR, 0, 2, 8, 29, 28, 27 },
.gate = { CGU_REG_CLKGR, 4 },
},
@ -349,8 +413,7 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_OTG] = {
"otg", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,
.parents = { X1000_CLK_EXCLK, -1,
X1000_CLK_APLL, X1000_CLK_MPLL },
.parents = { X1000_CLK_EXCLK, -1, X1000_CLK_APLL, X1000_CLK_MPLL },
.mux = { CGU_REG_USBCDR, 30, 2 },
.div = { CGU_REG_USBCDR, 0, 1, 8, 29, 28, 27 },
.gate = { CGU_REG_CLKGR, 3 },
@ -358,7 +421,7 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_SSIPLL] = {
"ssi_pll", CGU_CLK_MUX | CGU_CLK_DIV,
.parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL, -1, -1 },
.parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL },
.mux = { CGU_REG_SSICDR, 31, 1 },
.div = { CGU_REG_SSICDR, 0, 1, 8, 29, 28, 27 },
},
@ -371,7 +434,7 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_SSIMUX] = {
"ssi_mux", CGU_CLK_MUX,
.parents = { X1000_CLK_EXCLK, X1000_CLK_SSIPLL_DIV2, -1, -1 },
.parents = { X1000_CLK_EXCLK, X1000_CLK_SSIPLL_DIV2 },
.mux = { CGU_REG_SSICDR, 30, 1 },
},
@ -392,79 +455,85 @@ static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = {
[X1000_CLK_EMC] = {
"emc", CGU_CLK_GATE,
.parents = { X1000_CLK_AHB2, -1, -1, -1 },
.parents = { X1000_CLK_AHB2 },
.gate = { CGU_REG_CLKGR, 0 },
},
[X1000_CLK_EFUSE] = {
"efuse", CGU_CLK_GATE,
.parents = { X1000_CLK_AHB2, -1, -1, -1 },
.parents = { X1000_CLK_AHB2 },
.gate = { CGU_REG_CLKGR, 1 },
},
[X1000_CLK_SFC] = {
"sfc", CGU_CLK_GATE,
.parents = { X1000_CLK_SSIPLL, -1, -1, -1 },
.parents = { X1000_CLK_SSIPLL },
.gate = { CGU_REG_CLKGR, 2 },
},
[X1000_CLK_I2C0] = {
"i2c0", CGU_CLK_GATE,
.parents = { X1000_CLK_PCLK, -1, -1, -1 },
.parents = { X1000_CLK_PCLK },
.gate = { CGU_REG_CLKGR, 7 },
},
[X1000_CLK_I2C1] = {
"i2c1", CGU_CLK_GATE,
.parents = { X1000_CLK_PCLK, -1, -1, -1 },
.parents = { X1000_CLK_PCLK },
.gate = { CGU_REG_CLKGR, 8 },
},
[X1000_CLK_I2C2] = {
"i2c2", CGU_CLK_GATE,
.parents = { X1000_CLK_PCLK, -1, -1, -1 },
.parents = { X1000_CLK_PCLK },
.gate = { CGU_REG_CLKGR, 9 },
},
[X1000_CLK_AIC] = {
"aic", CGU_CLK_GATE,
.parents = { X1000_CLK_EXCLK },
.gate = { CGU_REG_CLKGR, 11 },
},
[X1000_CLK_UART0] = {
"uart0", CGU_CLK_GATE,
.parents = { X1000_CLK_EXCLK, -1, -1, -1 },
.parents = { X1000_CLK_EXCLK },
.gate = { CGU_REG_CLKGR, 14 },
},
[X1000_CLK_UART1] = {
"uart1", CGU_CLK_GATE,
.parents = { X1000_CLK_EXCLK, -1, -1, -1 },
.parents = { X1000_CLK_EXCLK},
.gate = { CGU_REG_CLKGR, 15 },
},
[X1000_CLK_UART2] = {
"uart2", CGU_CLK_GATE,
.parents = { X1000_CLK_EXCLK, -1, -1, -1 },
.parents = { X1000_CLK_EXCLK },
.gate = { CGU_REG_CLKGR, 16 },
},
[X1000_CLK_TCU] = {
"tcu", CGU_CLK_GATE,
.parents = { X1000_CLK_EXCLK, -1, -1, -1 },
.parents = { X1000_CLK_EXCLK },
.gate = { CGU_REG_CLKGR, 18 },
},
[X1000_CLK_SSI] = {
"ssi", CGU_CLK_GATE,
.parents = { X1000_CLK_SSIMUX, -1, -1, -1 },
.parents = { X1000_CLK_SSIMUX },
.gate = { CGU_REG_CLKGR, 19 },
},
[X1000_CLK_OST] = {
"ost", CGU_CLK_GATE,
.parents = { X1000_CLK_EXCLK, -1, -1, -1 },
.parents = { X1000_CLK_EXCLK },
.gate = { CGU_REG_CLKGR, 20 },
},
[X1000_CLK_PDMA] = {
"pdma", CGU_CLK_GATE,
.parents = { X1000_CLK_EXCLK, -1, -1, -1 },
.parents = { X1000_CLK_EXCLK },
.gate = { CGU_REG_CLKGR, 21 },
},
};

View File

@ -102,12 +102,9 @@ static int ti_syscon_gate_clk_probe(struct platform_device *pdev)
return -EINVAL;
regmap = syscon_node_to_regmap(dev->of_node);
if (IS_ERR(regmap)) {
if (PTR_ERR(regmap) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_err(dev, "failed to find parent regmap\n");
return PTR_ERR(regmap);
}
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap),
"failed to find parent regmap\n");
num_clks = 0;
for (p = data; p->name; p++)

View File

@ -32,7 +32,6 @@
#include <linux/io.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/rational.h>
#include "clk-regmap.h"
#include "clk-pll.h"

View File

@ -1,4 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
config MSTAR_MSC313_CPUPLL
bool "MStar CPUPLL driver"
depends on ARCH_MSTARV7 || COMPILE_TEST
default ARCH_MSTARV7
help
Support for the CPU PLL present on MStar/Sigmastar SoCs.
config MSTAR_MSC313_MPLL
bool "MStar MPLL driver"
depends on ARCH_MSTARV7 || COMPILE_TEST

View File

@ -3,4 +3,5 @@
# Makefile for mstar specific clk
#
obj-$(CONFIG_MSTAR_MSC313_CPUPLL) += clk-msc313-cpupll.o
obj-$(CONFIG_MSTAR_MSC313_MPLL) += clk-msc313-mpll.o

View File

@ -0,0 +1,220 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Daniel Palmer <daniel@thingy.jp>
*/
#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
/*
* This IP is not documented outside of the messy vendor driver.
* Below is what we think the registers look like based on looking at
* the vendor code and poking at the hardware:
*
* 0x140 -- LPF low. Seems to store one half of the clock transition
* 0x144 /
* 0x148 -- LPF high. Seems to store one half of the clock transition
* 0x14c /
* 0x150 -- vendor code says "toggle lpf enable"
* 0x154 -- mu?
* 0x15c -- lpf_update_count?
* 0x160 -- vendor code says "switch to LPF". Clock source config? Register bank?
* 0x164 -- vendor code says "from low to high" which seems to mean transition from LPF low to
* LPF high.
* 0x174 -- Seems to be the PLL lock status bit
* 0x180 -- Seems to be the current frequency, this might need to be populated by software?
* 0x184 / The vendor driver uses these to set the initial value of LPF low
*
* Frequency seems to be calculated like this:
* (parent clock (432mhz) / register_magic_value) * 16 * 524288
* Only the lower 24 bits of the resulting value will be used. In addition, the
* PLL doesn't seem to be able to lock on frequencies lower than 220 MHz, as
* divisor 0xfb586f (220 MHz) works but 0xfb7fff locks up.
*
* Vendor values:
* frequency - register value
*
* 400000000 - 0x0067AE14
* 600000000 - 0x00451EB8,
* 800000000 - 0x0033D70A,
* 1000000000 - 0x002978d4,
*/
#define REG_LPF_LOW_L 0x140
#define REG_LPF_LOW_H 0x144
#define REG_LPF_HIGH_BOTTOM 0x148
#define REG_LPF_HIGH_TOP 0x14c
#define REG_LPF_TOGGLE 0x150
#define REG_LPF_MYSTERYTWO 0x154
#define REG_LPF_UPDATE_COUNT 0x15c
#define REG_LPF_MYSTERYONE 0x160
#define REG_LPF_TRANSITIONCTRL 0x164
#define REG_LPF_LOCK 0x174
#define REG_CURRENT 0x180
#define LPF_LOCK_TIMEOUT 100000000
#define MULTIPLIER_1 16
#define MULTIPLIER_2 524288
#define MULTIPLIER (MULTIPLIER_1 * MULTIPLIER_2)
struct msc313_cpupll {
void __iomem *base;
struct clk_hw clk_hw;
};
#define to_cpupll(_hw) container_of(_hw, struct msc313_cpupll, clk_hw)
static u32 msc313_cpupll_reg_read32(struct msc313_cpupll *cpupll, unsigned int reg)
{
u32 value;
value = ioread16(cpupll->base + reg + 4) << 16;
value |= ioread16(cpupll->base + reg);
return value;
}
static void msc313_cpupll_reg_write32(struct msc313_cpupll *cpupll, unsigned int reg, u32 value)
{
u16 l = value & 0xffff, h = (value >> 16) & 0xffff;
iowrite16(l, cpupll->base + reg);
iowrite16(h, cpupll->base + reg + 4);
}
static void msc313_cpupll_setfreq(struct msc313_cpupll *cpupll, u32 regvalue)
{
ktime_t timeout;
msc313_cpupll_reg_write32(cpupll, REG_LPF_HIGH_BOTTOM, regvalue);
iowrite16(0x1, cpupll->base + REG_LPF_MYSTERYONE);
iowrite16(0x6, cpupll->base + REG_LPF_MYSTERYTWO);
iowrite16(0x8, cpupll->base + REG_LPF_UPDATE_COUNT);
iowrite16(BIT(12), cpupll->base + REG_LPF_TRANSITIONCTRL);
iowrite16(0, cpupll->base + REG_LPF_TOGGLE);
iowrite16(1, cpupll->base + REG_LPF_TOGGLE);
timeout = ktime_add_ns(ktime_get(), LPF_LOCK_TIMEOUT);
while (!(ioread16(cpupll->base + REG_LPF_LOCK))) {
if (ktime_after(ktime_get(), timeout)) {
pr_err("timeout waiting for LPF_LOCK\n");
return;
}
cpu_relax();
}
iowrite16(0, cpupll->base + REG_LPF_TOGGLE);
msc313_cpupll_reg_write32(cpupll, REG_LPF_LOW_L, regvalue);
}
static unsigned long msc313_cpupll_frequencyforreg(u32 reg, unsigned long parent_rate)
{
unsigned long long prescaled = ((unsigned long long)parent_rate) * MULTIPLIER;
if (prescaled == 0 || reg == 0)
return 0;
return DIV_ROUND_DOWN_ULL(prescaled, reg);
}
static u32 msc313_cpupll_regforfrequecy(unsigned long rate, unsigned long parent_rate)
{
unsigned long long prescaled = ((unsigned long long)parent_rate) * MULTIPLIER;
if (prescaled == 0 || rate == 0)
return 0;
return DIV_ROUND_UP_ULL(prescaled, rate);
}
static unsigned long msc313_cpupll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct msc313_cpupll *cpupll = to_cpupll(hw);
return msc313_cpupll_frequencyforreg(msc313_cpupll_reg_read32(cpupll, REG_LPF_LOW_L),
parent_rate);
}
static long msc313_cpupll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
u32 reg = msc313_cpupll_regforfrequecy(rate, *parent_rate);
long rounded = msc313_cpupll_frequencyforreg(reg, *parent_rate);
/*
* This is my poor attempt at making sure the resulting
* rate doesn't overshoot the requested rate.
*/
for (; rounded >= rate && reg > 0; reg--)
rounded = msc313_cpupll_frequencyforreg(reg, *parent_rate);
return rounded;
}
static int msc313_cpupll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
{
struct msc313_cpupll *cpupll = to_cpupll(hw);
u32 reg = msc313_cpupll_regforfrequecy(rate, parent_rate);
msc313_cpupll_setfreq(cpupll, reg);
return 0;
}
static const struct clk_ops msc313_cpupll_ops = {
.recalc_rate = msc313_cpupll_recalc_rate,
.round_rate = msc313_cpupll_round_rate,
.set_rate = msc313_cpupll_set_rate,
};
static const struct of_device_id msc313_cpupll_of_match[] = {
{ .compatible = "mstar,msc313-cpupll" },
{}
};
static int msc313_cpupll_probe(struct platform_device *pdev)
{
struct clk_init_data clk_init = {};
struct clk_parent_data cpupll_parent = { .index = 0 };
struct device *dev = &pdev->dev;
struct msc313_cpupll *cpupll;
int ret;
cpupll = devm_kzalloc(&pdev->dev, sizeof(*cpupll), GFP_KERNEL);
if (!cpupll)
return -ENOMEM;
cpupll->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(cpupll->base))
return PTR_ERR(cpupll->base);
/* LPF might not contain the current frequency so fix that up */
msc313_cpupll_reg_write32(cpupll, REG_LPF_LOW_L,
msc313_cpupll_reg_read32(cpupll, REG_CURRENT));
clk_init.name = dev_name(dev);
clk_init.ops = &msc313_cpupll_ops;
clk_init.parent_data = &cpupll_parent;
clk_init.num_parents = 1;
cpupll->clk_hw.init = &clk_init;
ret = devm_clk_hw_register(dev, &cpupll->clk_hw);
if (ret)
return ret;
return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get, &cpupll->clk_hw);
}
static struct platform_driver msc313_cpupll_driver = {
.driver = {
.name = "mstar-msc313-cpupll",
.of_match_table = msc313_cpupll_of_match,
},
.probe = msc313_cpupll_probe,
};
builtin_platform_driver(msc313_cpupll_driver);

View File

@ -21,7 +21,6 @@
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/reboot.h>
#include <linux/rational.h>
#include "../clk-fractional-divider.h"
#include "clk.h"

View File

@ -47,10 +47,10 @@ static void exynos5_subcmu_defer_gate(struct samsung_clk_provider *ctx,
/*
* Pass the needed clock provider context and register sub-CMU clocks
*
* NOTE: This function has to be called from the main, OF_CLK_DECLARE-
* NOTE: This function has to be called from the main, CLK_OF_DECLARE-
* initialized clock provider driver. This happens very early during boot
* process. Then this driver, during core_initcall registers two platform
* drivers: one which binds to the same device-tree node as OF_CLK_DECLARE
* drivers: one which binds to the same device-tree node as CLK_OF_DECLARE
* driver and second, for handling its per-domain child-devices. Those
* platform drivers are bound to their devices a bit later in arch_initcall,
* when OF-core populates all device-tree nodes.

View File

@ -1583,6 +1583,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx,
if (ret) {
pr_err("%s: failed to register pll clock %s : %d\n",
__func__, pll_clk->name, ret);
kfree(pll->rate_table);
kfree(pll);
return;
}

View File

@ -188,8 +188,10 @@ void __init socfpga_gate_init(struct device_node *node)
return;
ops = kmemdup(&gateclk_ops, sizeof(gateclk_ops), GFP_KERNEL);
if (WARN_ON(!ops))
if (WARN_ON(!ops)) {
kfree(socfpga_clk);
return;
}
rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2);
if (rc)
@ -243,6 +245,7 @@ void __init socfpga_gate_init(struct device_node *node)
err = clk_hw_register(NULL, hw_clk);
if (err) {
kfree(ops);
kfree(socfpga_clk);
return;
}

View File

@ -1020,9 +1020,10 @@ static void __init st_of_quadfs_setup(struct device_node *np,
clk = st_clk_register_quadfs_pll(pll_name, clk_parent_name, datac->data,
reg, lock);
if (IS_ERR(clk))
if (IS_ERR(clk)) {
kfree(lock);
goto err_exit;
else
} else
pr_debug("%s: parent %s rate %u\n",
__clk_get_name(clk),
__clk_get_name(clk_get_parent(clk)),

View File

@ -277,6 +277,7 @@ static struct clk_hw *visconti_register_pll(struct visconti_pll_provider *ctx,
ret = clk_hw_register(NULL, &pll->hw);
if (ret) {
pr_err("failed to register pll clock %s : %d\n", name, ret);
kfree(pll->rate_table);
kfree(pll);
pll_hw_clk = ERR_PTR(ret);
}

View File

@ -1,8 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
config CLK_LGM_CGU
depends on OF && HAS_IOMEM && (X86 || COMPILE_TEST)
select MFD_SYSCON
select OF_EARLY_FLATTREE
bool "Clock driver for Lightning Mountain(LGM) platform"
help
Clock Generation Unit(CGU) driver for Intel Lightning Mountain(LGM)
network processor SoC.
Clock Generation Unit(CGU) driver for MaxLinear's x86 based
Lightning Mountain(LGM) network processor SoC.

View File

@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020-2022 MaxLinear, Inc.
* Copyright (C) 2020 Intel Corporation.
* Zhu YiXin <yixin.zhu@intel.com>
* Rahul Tanwar <rahul.tanwar@intel.com>
* Zhu Yixin <yzhu@maxlinear.com>
* Rahul Tanwar <rtanwar@maxlinear.com>
*/
#include <linux/clk-provider.h>
@ -40,13 +41,10 @@ static unsigned long lgm_pll_recalc_rate(struct clk_hw *hw, unsigned long prate)
{
struct lgm_clk_pll *pll = to_lgm_clk_pll(hw);
unsigned int div, mult, frac;
unsigned long flags;
spin_lock_irqsave(&pll->lock, flags);
mult = lgm_get_clk_val(pll->membase, PLL_REF_DIV(pll->reg), 0, 12);
div = lgm_get_clk_val(pll->membase, PLL_REF_DIV(pll->reg), 18, 6);
frac = lgm_get_clk_val(pll->membase, pll->reg, 2, 24);
spin_unlock_irqrestore(&pll->lock, flags);
if (pll->type == TYPE_LJPLL)
div *= 4;
@ -57,12 +55,9 @@ static unsigned long lgm_pll_recalc_rate(struct clk_hw *hw, unsigned long prate)
static int lgm_pll_is_enabled(struct clk_hw *hw)
{
struct lgm_clk_pll *pll = to_lgm_clk_pll(hw);
unsigned long flags;
unsigned int ret;
spin_lock_irqsave(&pll->lock, flags);
ret = lgm_get_clk_val(pll->membase, pll->reg, 0, 1);
spin_unlock_irqrestore(&pll->lock, flags);
return ret;
}
@ -70,15 +65,13 @@ static int lgm_pll_is_enabled(struct clk_hw *hw)
static int lgm_pll_enable(struct clk_hw *hw)
{
struct lgm_clk_pll *pll = to_lgm_clk_pll(hw);
unsigned long flags;
u32 val;
int ret;
spin_lock_irqsave(&pll->lock, flags);
lgm_set_clk_val(pll->membase, pll->reg, 0, 1, 1);
ret = readl_poll_timeout_atomic(pll->membase + pll->reg,
val, (val & 0x1), 1, 100);
spin_unlock_irqrestore(&pll->lock, flags);
ret = regmap_read_poll_timeout_atomic(pll->membase, pll->reg,
val, (val & 0x1), 1, 100);
return ret;
}
@ -86,11 +79,8 @@ static int lgm_pll_enable(struct clk_hw *hw)
static void lgm_pll_disable(struct clk_hw *hw)
{
struct lgm_clk_pll *pll = to_lgm_clk_pll(hw);
unsigned long flags;
spin_lock_irqsave(&pll->lock, flags);
lgm_set_clk_val(pll->membase, pll->reg, 0, 1, 0);
spin_unlock_irqrestore(&pll->lock, flags);
}
static const struct clk_ops lgm_pll_ops = {
@ -121,7 +111,6 @@ lgm_clk_register_pll(struct lgm_clk_provider *ctx,
return ERR_PTR(-ENOMEM);
pll->membase = ctx->membase;
pll->lock = ctx->lock;
pll->reg = list->reg;
pll->flags = list->flags;
pll->type = list->type;

View File

@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020-2022 MaxLinear, Inc.
* Copyright (C) 2020 Intel Corporation.
* Zhu YiXin <yixin.zhu@intel.com>
* Rahul Tanwar <rahul.tanwar@intel.com>
* Zhu Yixin <yzhu@maxlinear.com>
* Rahul Tanwar <rtanwar@maxlinear.com>
*/
#include <linux/clk-provider.h>
#include <linux/device.h>
@ -24,14 +25,10 @@
static struct clk_hw *lgm_clk_register_fixed(struct lgm_clk_provider *ctx,
const struct lgm_clk_branch *list)
{
unsigned long flags;
if (list->div_flags & CLOCK_FLAG_VAL_INIT) {
spin_lock_irqsave(&ctx->lock, flags);
if (list->div_flags & CLOCK_FLAG_VAL_INIT)
lgm_set_clk_val(ctx->membase, list->div_off, list->div_shift,
list->div_width, list->div_val);
spin_unlock_irqrestore(&ctx->lock, flags);
}
return clk_hw_register_fixed_rate(NULL, list->name,
list->parent_data[0].name,
@ -41,33 +38,27 @@ static struct clk_hw *lgm_clk_register_fixed(struct lgm_clk_provider *ctx,
static u8 lgm_clk_mux_get_parent(struct clk_hw *hw)
{
struct lgm_clk_mux *mux = to_lgm_clk_mux(hw);
unsigned long flags;
u32 val;
spin_lock_irqsave(&mux->lock, flags);
if (mux->flags & MUX_CLK_SW)
val = mux->reg;
else
val = lgm_get_clk_val(mux->membase, mux->reg, mux->shift,
mux->width);
spin_unlock_irqrestore(&mux->lock, flags);
return clk_mux_val_to_index(hw, NULL, mux->flags, val);
}
static int lgm_clk_mux_set_parent(struct clk_hw *hw, u8 index)
{
struct lgm_clk_mux *mux = to_lgm_clk_mux(hw);
unsigned long flags;
u32 val;
val = clk_mux_index_to_val(NULL, mux->flags, index);
spin_lock_irqsave(&mux->lock, flags);
if (mux->flags & MUX_CLK_SW)
mux->reg = val;
else
lgm_set_clk_val(mux->membase, mux->reg, mux->shift,
mux->width, val);
spin_unlock_irqrestore(&mux->lock, flags);
return 0;
}
@ -90,7 +81,7 @@ static struct clk_hw *
lgm_clk_register_mux(struct lgm_clk_provider *ctx,
const struct lgm_clk_branch *list)
{
unsigned long flags, cflags = list->mux_flags;
unsigned long cflags = list->mux_flags;
struct device *dev = ctx->dev;
u8 shift = list->mux_shift;
u8 width = list->mux_width;
@ -111,7 +102,6 @@ lgm_clk_register_mux(struct lgm_clk_provider *ctx,
init.num_parents = list->num_parents;
mux->membase = ctx->membase;
mux->lock = ctx->lock;
mux->reg = reg;
mux->shift = shift;
mux->width = width;
@ -123,11 +113,8 @@ lgm_clk_register_mux(struct lgm_clk_provider *ctx,
if (ret)
return ERR_PTR(ret);
if (cflags & CLOCK_FLAG_VAL_INIT) {
spin_lock_irqsave(&mux->lock, flags);
if (cflags & CLOCK_FLAG_VAL_INIT)
lgm_set_clk_val(mux->membase, reg, shift, width, list->mux_val);
spin_unlock_irqrestore(&mux->lock, flags);
}
return hw;
}
@ -136,13 +123,10 @@ static unsigned long
lgm_clk_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct lgm_clk_divider *divider = to_lgm_clk_divider(hw);
unsigned long flags;
unsigned int val;
spin_lock_irqsave(&divider->lock, flags);
val = lgm_get_clk_val(divider->membase, divider->reg,
divider->shift, divider->width);
spin_unlock_irqrestore(&divider->lock, flags);
return divider_recalc_rate(hw, parent_rate, val, divider->table,
divider->flags, divider->width);
@ -163,7 +147,6 @@ lgm_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long prate)
{
struct lgm_clk_divider *divider = to_lgm_clk_divider(hw);
unsigned long flags;
int value;
value = divider_get_val(rate, prate, divider->table,
@ -171,10 +154,8 @@ lgm_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
if (value < 0)
return value;
spin_lock_irqsave(&divider->lock, flags);
lgm_set_clk_val(divider->membase, divider->reg,
divider->shift, divider->width, value);
spin_unlock_irqrestore(&divider->lock, flags);
return 0;
}
@ -182,12 +163,10 @@ lgm_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
static int lgm_clk_divider_enable_disable(struct clk_hw *hw, int enable)
{
struct lgm_clk_divider *div = to_lgm_clk_divider(hw);
unsigned long flags;
spin_lock_irqsave(&div->lock, flags);
lgm_set_clk_val(div->membase, div->reg, div->shift_gate,
div->width_gate, enable);
spin_unlock_irqrestore(&div->lock, flags);
if (div->flags != DIV_CLK_NO_MASK)
lgm_set_clk_val(div->membase, div->reg, div->shift_gate,
div->width_gate, enable);
return 0;
}
@ -213,7 +192,7 @@ static struct clk_hw *
lgm_clk_register_divider(struct lgm_clk_provider *ctx,
const struct lgm_clk_branch *list)
{
unsigned long flags, cflags = list->div_flags;
unsigned long cflags = list->div_flags;
struct device *dev = ctx->dev;
struct lgm_clk_divider *div;
struct clk_init_data init = {};
@ -236,7 +215,6 @@ lgm_clk_register_divider(struct lgm_clk_provider *ctx,
init.num_parents = 1;
div->membase = ctx->membase;
div->lock = ctx->lock;
div->reg = reg;
div->shift = shift;
div->width = width;
@ -251,11 +229,8 @@ lgm_clk_register_divider(struct lgm_clk_provider *ctx,
if (ret)
return ERR_PTR(ret);
if (cflags & CLOCK_FLAG_VAL_INIT) {
spin_lock_irqsave(&div->lock, flags);
if (cflags & CLOCK_FLAG_VAL_INIT)
lgm_set_clk_val(div->membase, reg, shift, width, list->div_val);
spin_unlock_irqrestore(&div->lock, flags);
}
return hw;
}
@ -264,7 +239,6 @@ static struct clk_hw *
lgm_clk_register_fixed_factor(struct lgm_clk_provider *ctx,
const struct lgm_clk_branch *list)
{
unsigned long flags;
struct clk_hw *hw;
hw = clk_hw_register_fixed_factor(ctx->dev, list->name,
@ -273,12 +247,9 @@ lgm_clk_register_fixed_factor(struct lgm_clk_provider *ctx,
if (IS_ERR(hw))
return ERR_CAST(hw);
if (list->div_flags & CLOCK_FLAG_VAL_INIT) {
spin_lock_irqsave(&ctx->lock, flags);
if (list->div_flags & CLOCK_FLAG_VAL_INIT)
lgm_set_clk_val(ctx->membase, list->div_off, list->div_shift,
list->div_width, list->div_val);
spin_unlock_irqrestore(&ctx->lock, flags);
}
return hw;
}
@ -286,13 +257,10 @@ lgm_clk_register_fixed_factor(struct lgm_clk_provider *ctx,
static int lgm_clk_gate_enable(struct clk_hw *hw)
{
struct lgm_clk_gate *gate = to_lgm_clk_gate(hw);
unsigned long flags;
unsigned int reg;
spin_lock_irqsave(&gate->lock, flags);
reg = GATE_HW_REG_EN(gate->reg);
lgm_set_clk_val(gate->membase, reg, gate->shift, 1, 1);
spin_unlock_irqrestore(&gate->lock, flags);
return 0;
}
@ -300,25 +268,19 @@ static int lgm_clk_gate_enable(struct clk_hw *hw)
static void lgm_clk_gate_disable(struct clk_hw *hw)
{
struct lgm_clk_gate *gate = to_lgm_clk_gate(hw);
unsigned long flags;
unsigned int reg;
spin_lock_irqsave(&gate->lock, flags);
reg = GATE_HW_REG_DIS(gate->reg);
lgm_set_clk_val(gate->membase, reg, gate->shift, 1, 1);
spin_unlock_irqrestore(&gate->lock, flags);
}
static int lgm_clk_gate_is_enabled(struct clk_hw *hw)
{
struct lgm_clk_gate *gate = to_lgm_clk_gate(hw);
unsigned int reg, ret;
unsigned long flags;
spin_lock_irqsave(&gate->lock, flags);
reg = GATE_HW_REG_STAT(gate->reg);
ret = lgm_get_clk_val(gate->membase, reg, gate->shift, 1);
spin_unlock_irqrestore(&gate->lock, flags);
return ret;
}
@ -333,7 +295,7 @@ static struct clk_hw *
lgm_clk_register_gate(struct lgm_clk_provider *ctx,
const struct lgm_clk_branch *list)
{
unsigned long flags, cflags = list->gate_flags;
unsigned long cflags = list->gate_flags;
const char *pname = list->parent_data[0].name;
struct device *dev = ctx->dev;
u8 shift = list->gate_shift;
@ -354,7 +316,6 @@ lgm_clk_register_gate(struct lgm_clk_provider *ctx,
init.num_parents = pname ? 1 : 0;
gate->membase = ctx->membase;
gate->lock = ctx->lock;
gate->reg = reg;
gate->shift = shift;
gate->flags = cflags;
@ -366,9 +327,7 @@ lgm_clk_register_gate(struct lgm_clk_provider *ctx,
return ERR_PTR(ret);
if (cflags & CLOCK_FLAG_VAL_INIT) {
spin_lock_irqsave(&gate->lock, flags);
lgm_set_clk_val(gate->membase, reg, shift, 1, list->gate_val);
spin_unlock_irqrestore(&gate->lock, flags);
}
return hw;
@ -396,8 +355,22 @@ int lgm_clk_register_branches(struct lgm_clk_provider *ctx,
hw = lgm_clk_register_fixed_factor(ctx, list);
break;
case CLK_TYPE_GATE:
hw = lgm_clk_register_gate(ctx, list);
if (list->gate_flags & GATE_CLK_HW) {
hw = lgm_clk_register_gate(ctx, list);
} else {
/*
* GATE_CLKs can be controlled either from
* CGU clk driver i.e. this driver or directly
* from power management driver/daemon. It is
* dependent on the power policy/profile requirements
* of the end product. To override control of gate
* clks from this driver, provide NULL for this index
* of gate clk provider.
*/
hw = NULL;
}
break;
default:
dev_err(ctx->dev, "invalid clk type\n");
return -EINVAL;
@ -443,24 +416,18 @@ lgm_clk_ddiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
static int lgm_clk_ddiv_enable(struct clk_hw *hw)
{
struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw);
unsigned long flags;
spin_lock_irqsave(&ddiv->lock, flags);
lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift_gate,
ddiv->width_gate, 1);
spin_unlock_irqrestore(&ddiv->lock, flags);
return 0;
}
static void lgm_clk_ddiv_disable(struct clk_hw *hw)
{
struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw);
unsigned long flags;
spin_lock_irqsave(&ddiv->lock, flags);
lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift_gate,
ddiv->width_gate, 0);
spin_unlock_irqrestore(&ddiv->lock, flags);
}
static int
@ -497,32 +464,25 @@ lgm_clk_ddiv_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw);
u32 div, ddiv1, ddiv2;
unsigned long flags;
div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate);
spin_lock_irqsave(&ddiv->lock, flags);
if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) {
div = DIV_ROUND_CLOSEST_ULL((u64)div, 5);
div = div * 2;
}
if (div <= 0) {
spin_unlock_irqrestore(&ddiv->lock, flags);
if (div <= 0)
return -EINVAL;
}
if (lgm_clk_get_ddiv_val(div, &ddiv1, &ddiv2)) {
spin_unlock_irqrestore(&ddiv->lock, flags);
if (lgm_clk_get_ddiv_val(div, &ddiv1, &ddiv2))
return -EINVAL;
}
lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift0, ddiv->width0,
ddiv1 - 1);
lgm_set_clk_val(ddiv->membase, ddiv->reg, ddiv->shift1, ddiv->width1,
ddiv2 - 1);
spin_unlock_irqrestore(&ddiv->lock, flags);
return 0;
}
@ -533,18 +493,15 @@ lgm_clk_ddiv_round_rate(struct clk_hw *hw, unsigned long rate,
{
struct lgm_clk_ddiv *ddiv = to_lgm_clk_ddiv(hw);
u32 div, ddiv1, ddiv2;
unsigned long flags;
u64 rate64;
div = DIV_ROUND_CLOSEST_ULL((u64)*prate, rate);
/* if predivide bit is enabled, modify div by factor of 2.5 */
spin_lock_irqsave(&ddiv->lock, flags);
if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) {
div = div * 2;
div = DIV_ROUND_CLOSEST_ULL((u64)div, 5);
}
spin_unlock_irqrestore(&ddiv->lock, flags);
if (div <= 0)
return *prate;
@ -558,12 +515,10 @@ lgm_clk_ddiv_round_rate(struct clk_hw *hw, unsigned long rate,
do_div(rate64, ddiv2);
/* if predivide bit is enabled, modify rounded rate by factor of 2.5 */
spin_lock_irqsave(&ddiv->lock, flags);
if (lgm_get_clk_val(ddiv->membase, ddiv->reg, ddiv->shift2, 1)) {
rate64 = rate64 * 2;
rate64 = DIV_ROUND_CLOSEST_ULL(rate64, 5);
}
spin_unlock_irqrestore(&ddiv->lock, flags);
return rate64;
}
@ -600,7 +555,6 @@ int lgm_clk_register_ddiv(struct lgm_clk_provider *ctx,
init.num_parents = 1;
ddiv->membase = ctx->membase;
ddiv->lock = ctx->lock;
ddiv->reg = list->reg;
ddiv->shift0 = list->shift0;
ddiv->width0 = list->width0;

View File

@ -1,28 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright(c) 2020 Intel Corporation.
* Zhu YiXin <yixin.zhu@intel.com>
* Rahul Tanwar <rahul.tanwar@intel.com>
* Copyright (C) 2020-2022 MaxLinear, Inc.
* Copyright (C) 2020 Intel Corporation.
* Zhu Yixin <yzhu@maxlinear.com>
* Rahul Tanwar <rtanwar@maxlinear.com>
*/
#ifndef __CLK_CGU_H
#define __CLK_CGU_H
#include <linux/io.h>
#include <linux/regmap.h>
struct lgm_clk_mux {
struct clk_hw hw;
void __iomem *membase;
struct regmap *membase;
unsigned int reg;
u8 shift;
u8 width;
unsigned long flags;
spinlock_t lock;
};
struct lgm_clk_divider {
struct clk_hw hw;
void __iomem *membase;
struct regmap *membase;
unsigned int reg;
u8 shift;
u8 width;
@ -30,12 +30,11 @@ struct lgm_clk_divider {
u8 width_gate;
unsigned long flags;
const struct clk_div_table *table;
spinlock_t lock;
};
struct lgm_clk_ddiv {
struct clk_hw hw;
void __iomem *membase;
struct regmap *membase;
unsigned int reg;
u8 shift0;
u8 width0;
@ -48,16 +47,14 @@ struct lgm_clk_ddiv {
unsigned int mult;
unsigned int div;
unsigned long flags;
spinlock_t lock;
};
struct lgm_clk_gate {
struct clk_hw hw;
void __iomem *membase;
struct regmap *membase;
unsigned int reg;
u8 shift;
unsigned long flags;
spinlock_t lock;
};
enum lgm_clk_type {
@ -77,11 +74,10 @@ enum lgm_clk_type {
* @clk_data: array of hw clocks and clk number.
*/
struct lgm_clk_provider {
void __iomem *membase;
struct regmap *membase;
struct device_node *np;
struct device *dev;
struct clk_hw_onecell_data clk_data;
spinlock_t lock;
};
enum pll_type {
@ -92,11 +88,10 @@ enum pll_type {
struct lgm_clk_pll {
struct clk_hw hw;
void __iomem *membase;
struct regmap *membase;
unsigned int reg;
unsigned long flags;
enum pll_type type;
spinlock_t lock;
};
/**
@ -202,6 +197,8 @@ struct lgm_clk_branch {
/* clock flags definition */
#define CLOCK_FLAG_VAL_INIT BIT(16)
#define MUX_CLK_SW BIT(17)
#define GATE_CLK_HW BIT(18)
#define DIV_CLK_NO_MASK BIT(19)
#define LGM_MUX(_id, _name, _pdata, _f, _reg, \
_shift, _width, _cf, _v) \
@ -300,29 +297,32 @@ struct lgm_clk_branch {
.div = _d, \
}
static inline void lgm_set_clk_val(void __iomem *membase, u32 reg,
static inline void lgm_set_clk_val(struct regmap *membase, u32 reg,
u8 shift, u8 width, u32 set_val)
{
u32 mask = (GENMASK(width - 1, 0) << shift);
u32 regval;
regval = readl(membase + reg);
regval = (regval & ~mask) | ((set_val << shift) & mask);
writel(regval, membase + reg);
regmap_update_bits(membase, reg, mask, set_val << shift);
}
static inline u32 lgm_get_clk_val(void __iomem *membase, u32 reg,
static inline u32 lgm_get_clk_val(struct regmap *membase, u32 reg,
u8 shift, u8 width)
{
u32 mask = (GENMASK(width - 1, 0) << shift);
u32 val;
val = readl(membase + reg);
if (regmap_read(membase, reg, &val)) {
WARN_ONCE(1, "Failed to read clk reg: 0x%x\n", reg);
return 0;
}
val = (val & mask) >> shift;
return val;
}
int lgm_clk_register_branches(struct lgm_clk_provider *ctx,
const struct lgm_clk_branch *list,
unsigned int nr_clk);

View File

@ -1,10 +1,12 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020-2022 MaxLinear, Inc.
* Copyright (C) 2020 Intel Corporation.
* Zhu YiXin <yixin.zhu@intel.com>
* Rahul Tanwar <rahul.tanwar@intel.com>
* Zhu Yixin <yzhu@maxlinear.com>
* Rahul Tanwar <rtanwar@maxlinear.com>
*/
#include <linux/clk-provider.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <dt-bindings/clock/intel,lgm-clk.h>
@ -253,8 +255,8 @@ static const struct lgm_clk_branch lgm_branch_clks[] = {
LGM_FIXED(LGM_CLK_SLIC, "slic", NULL, 0, CGU_IF_CLK1,
8, 2, CLOCK_FLAG_VAL_INIT, 8192000, 2),
LGM_FIXED(LGM_CLK_DOCSIS, "v_docsis", NULL, 0, 0, 0, 0, 0, 16000000, 0),
LGM_DIV(LGM_CLK_DCL, "dcl", "v_ifclk", 0, CGU_PCMCR,
25, 3, 0, 0, 0, 0, dcl_div),
LGM_DIV(LGM_CLK_DCL, "dcl", "v_ifclk", CLK_SET_RATE_PARENT, CGU_PCMCR,
25, 3, 0, 0, DIV_CLK_NO_MASK, 0, dcl_div),
LGM_MUX(LGM_CLK_PCM, "pcm", pcm_p, 0, CGU_C55_PCMCR,
0, 1, CLK_MUX_ROUND_CLOSEST, 0),
LGM_FIXED_FACTOR(LGM_CLK_DDR_PHY, "ddr_phy", "ddr",
@ -433,13 +435,15 @@ static int lgm_cgu_probe(struct platform_device *pdev)
ctx->clk_data.num = CLK_NR_CLKS;
ctx->membase = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ctx->membase))
ctx->membase = syscon_node_to_regmap(np);
if (IS_ERR(ctx->membase)) {
dev_err(dev, "Failed to get clk CGU iomem\n");
return PTR_ERR(ctx->membase);
}
ctx->np = np;
ctx->dev = dev;
spin_lock_init(&ctx->lock);
ret = lgm_clk_register_plls(ctx, lgm_pll_clks,
ARRAY_SIZE(lgm_pll_clks));

View File

@ -2,7 +2,7 @@
config XILINX_VCU
tristate "Xilinx VCU logicoreIP Init"
depends on HAS_IOMEM && COMMON_CLK
depends on HAS_IOMEM
select REGMAP_MMIO
help
Provides the driver to enable and disable the isolation between the
@ -19,7 +19,7 @@ config XILINX_VCU
config COMMON_CLK_XLNX_CLKWZRD
tristate "Xilinx Clocking Wizard"
depends on COMMON_CLK && OF
depends on OF
depends on HAS_IOMEM
help
Support for the Xilinx Clocking Wizard IP core clock generator.

View File

@ -0,0 +1,49 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/*
* This header provides clock numbers for the ingenic,jz4755-cgu DT binding.
*/
#ifndef __DT_BINDINGS_CLOCK_JZ4755_CGU_H__
#define __DT_BINDINGS_CLOCK_JZ4755_CGU_H__
#define JZ4755_CLK_EXT 0
#define JZ4755_CLK_OSC32K 1
#define JZ4755_CLK_PLL 2
#define JZ4755_CLK_PLL_HALF 3
#define JZ4755_CLK_EXT_HALF 4
#define JZ4755_CLK_CCLK 5
#define JZ4755_CLK_H0CLK 6
#define JZ4755_CLK_PCLK 7
#define JZ4755_CLK_MCLK 8
#define JZ4755_CLK_H1CLK 9
#define JZ4755_CLK_UDC 10
#define JZ4755_CLK_LCD 11
#define JZ4755_CLK_UART0 12
#define JZ4755_CLK_UART1 13
#define JZ4755_CLK_UART2 14
#define JZ4755_CLK_DMA 15
#define JZ4755_CLK_MMC 16
#define JZ4755_CLK_MMC0 17
#define JZ4755_CLK_MMC1 18
#define JZ4755_CLK_EXT512 19
#define JZ4755_CLK_RTC 20
#define JZ4755_CLK_UDC_PHY 21
#define JZ4755_CLK_I2S 22
#define JZ4755_CLK_SPI 23
#define JZ4755_CLK_AIC 24
#define JZ4755_CLK_ADC 25
#define JZ4755_CLK_TCU 26
#define JZ4755_CLK_BCH 27
#define JZ4755_CLK_I2C 28
#define JZ4755_CLK_TVE 29
#define JZ4755_CLK_CIM 30
#define JZ4755_CLK_AUX_CPU 31
#define JZ4755_CLK_AHB1 32
#define JZ4755_CLK_IDCT 33
#define JZ4755_CLK_DB 34
#define JZ4755_CLK_ME 35
#define JZ4755_CLK_MC 36
#define JZ4755_CLK_TSSI 37
#define JZ4755_CLK_IPU 38
#endif /* __DT_BINDINGS_CLOCK_JZ4755_CGU_H__ */

View File

@ -50,5 +50,9 @@
#define X1000_CLK_PDMA 35
#define X1000_CLK_EXCLK_DIV512 36
#define X1000_CLK_RTC 37
#define X1000_CLK_AIC 38
#define X1000_CLK_I2SPLLMUX 39
#define X1000_CLK_I2SPLL 40
#define X1000_CLK_I2S 41
#endif /* __DT_BINDINGS_CLOCK_X1000_CGU_H__ */