cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

i2s_pll_clock.c (5833B)


      1/*
      2 * Synopsys AXS10X SDP I2S PLL clock driver
      3 *
      4 * Copyright (C) 2016 Synopsys
      5 *
      6 * This file is licensed under the terms of the GNU General Public
      7 * License version 2. This program is licensed "as is" without any
      8 * warranty of any kind, whether express or implied.
      9 */
     10
     11#include <linux/platform_device.h>
     12#include <linux/module.h>
     13#include <linux/clk-provider.h>
     14#include <linux/err.h>
     15#include <linux/device.h>
     16#include <linux/io.h>
     17#include <linux/of_address.h>
     18#include <linux/slab.h>
     19#include <linux/of.h>
     20
     21/* PLL registers addresses */
     22#define PLL_IDIV_REG	0x0
     23#define PLL_FBDIV_REG	0x4
     24#define PLL_ODIV0_REG	0x8
     25#define PLL_ODIV1_REG	0xC
     26
     27struct i2s_pll_cfg {
     28	unsigned int rate;
     29	unsigned int idiv;
     30	unsigned int fbdiv;
     31	unsigned int odiv0;
     32	unsigned int odiv1;
     33};
     34
     35static const struct i2s_pll_cfg i2s_pll_cfg_27m[] = {
     36	/* 27 Mhz */
     37	{ 1024000, 0x104, 0x451, 0x10E38, 0x2000 },
     38	{ 1411200, 0x104, 0x596, 0x10D35, 0x2000 },
     39	{ 1536000, 0x208, 0xA28, 0x10B2C, 0x2000 },
     40	{ 2048000, 0x82, 0x451, 0x10E38, 0x2000 },
     41	{ 2822400, 0x82, 0x596, 0x10D35, 0x2000 },
     42	{ 3072000, 0x104, 0xA28, 0x10B2C, 0x2000 },
     43	{ 2116800, 0x82, 0x3CF, 0x10C30, 0x2000 },
     44	{ 2304000, 0x104, 0x79E, 0x10B2C, 0x2000 },
     45	{ 0, 0, 0, 0, 0 },
     46};
     47
     48static const struct i2s_pll_cfg i2s_pll_cfg_28m[] = {
     49	/* 28.224 Mhz */
     50	{ 1024000, 0x82, 0x105, 0x107DF, 0x2000 },
     51	{ 1411200, 0x28A, 0x1, 0x10001, 0x2000 },
     52	{ 1536000, 0xA28, 0x187, 0x10042, 0x2000 },
     53	{ 2048000, 0x41, 0x105, 0x107DF, 0x2000 },
     54	{ 2822400, 0x145, 0x1, 0x10001, 0x2000 },
     55	{ 3072000, 0x514, 0x187, 0x10042, 0x2000 },
     56	{ 2116800, 0x514, 0x42, 0x10001, 0x2000 },
     57	{ 2304000, 0x619, 0x82, 0x10001, 0x2000 },
     58	{ 0, 0, 0, 0, 0 },
     59};
     60
     61struct i2s_pll_clk {
     62	void __iomem *base;
     63	struct clk_hw hw;
     64	struct device *dev;
     65};
     66
     67static inline void i2s_pll_write(struct i2s_pll_clk *clk, unsigned int reg,
     68		unsigned int val)
     69{
     70	writel_relaxed(val, clk->base + reg);
     71}
     72
     73static inline unsigned int i2s_pll_read(struct i2s_pll_clk *clk,
     74		unsigned int reg)
     75{
     76	return readl_relaxed(clk->base + reg);
     77}
     78
     79static inline struct i2s_pll_clk *to_i2s_pll_clk(struct clk_hw *hw)
     80{
     81	return container_of(hw, struct i2s_pll_clk, hw);
     82}
     83
     84static inline unsigned int i2s_pll_get_value(unsigned int val)
     85{
     86	return (val & 0x3F) + ((val >> 6) & 0x3F);
     87}
     88
     89static const struct i2s_pll_cfg *i2s_pll_get_cfg(unsigned long prate)
     90{
     91	switch (prate) {
     92	case 27000000:
     93		return i2s_pll_cfg_27m;
     94	case 28224000:
     95		return i2s_pll_cfg_28m;
     96	default:
     97		return NULL;
     98	}
     99}
    100
    101static unsigned long i2s_pll_recalc_rate(struct clk_hw *hw,
    102			unsigned long parent_rate)
    103{
    104	struct i2s_pll_clk *clk = to_i2s_pll_clk(hw);
    105	unsigned int idiv, fbdiv, odiv;
    106
    107	idiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_IDIV_REG));
    108	fbdiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_FBDIV_REG));
    109	odiv = i2s_pll_get_value(i2s_pll_read(clk, PLL_ODIV0_REG));
    110
    111	return ((parent_rate / idiv) * fbdiv) / odiv;
    112}
    113
    114static long i2s_pll_round_rate(struct clk_hw *hw, unsigned long rate,
    115			unsigned long *prate)
    116{
    117	struct i2s_pll_clk *clk = to_i2s_pll_clk(hw);
    118	const struct i2s_pll_cfg *pll_cfg = i2s_pll_get_cfg(*prate);
    119	int i;
    120
    121	if (!pll_cfg) {
    122		dev_err(clk->dev, "invalid parent rate=%ld\n", *prate);
    123		return -EINVAL;
    124	}
    125
    126	for (i = 0; pll_cfg[i].rate != 0; i++)
    127		if (pll_cfg[i].rate == rate)
    128			return rate;
    129
    130	return -EINVAL;
    131}
    132
    133static int i2s_pll_set_rate(struct clk_hw *hw, unsigned long rate,
    134			unsigned long parent_rate)
    135{
    136	struct i2s_pll_clk *clk = to_i2s_pll_clk(hw);
    137	const struct i2s_pll_cfg *pll_cfg = i2s_pll_get_cfg(parent_rate);
    138	int i;
    139
    140	if (!pll_cfg) {
    141		dev_err(clk->dev, "invalid parent rate=%ld\n", parent_rate);
    142		return -EINVAL;
    143	}
    144
    145	for (i = 0; pll_cfg[i].rate != 0; i++) {
    146		if (pll_cfg[i].rate == rate) {
    147			i2s_pll_write(clk, PLL_IDIV_REG, pll_cfg[i].idiv);
    148			i2s_pll_write(clk, PLL_FBDIV_REG, pll_cfg[i].fbdiv);
    149			i2s_pll_write(clk, PLL_ODIV0_REG, pll_cfg[i].odiv0);
    150			i2s_pll_write(clk, PLL_ODIV1_REG, pll_cfg[i].odiv1);
    151			return 0;
    152		}
    153	}
    154
    155	dev_err(clk->dev, "invalid rate=%ld, parent_rate=%ld\n", rate,
    156			parent_rate);
    157	return -EINVAL;
    158}
    159
    160static const struct clk_ops i2s_pll_ops = {
    161	.recalc_rate = i2s_pll_recalc_rate,
    162	.round_rate = i2s_pll_round_rate,
    163	.set_rate = i2s_pll_set_rate,
    164};
    165
    166static int i2s_pll_clk_probe(struct platform_device *pdev)
    167{
    168	struct device *dev = &pdev->dev;
    169	struct device_node *node = dev->of_node;
    170	const char *clk_name;
    171	const char *parent_name;
    172	struct clk *clk;
    173	struct i2s_pll_clk *pll_clk;
    174	struct clk_init_data init;
    175
    176	pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL);
    177	if (!pll_clk)
    178		return -ENOMEM;
    179
    180	pll_clk->base = devm_platform_ioremap_resource(pdev, 0);
    181	if (IS_ERR(pll_clk->base))
    182		return PTR_ERR(pll_clk->base);
    183
    184	memset(&init, 0, sizeof(init));
    185	clk_name = node->name;
    186	init.name = clk_name;
    187	init.ops = &i2s_pll_ops;
    188	parent_name = of_clk_get_parent_name(node, 0);
    189	init.parent_names = &parent_name;
    190	init.num_parents = 1;
    191	pll_clk->hw.init = &init;
    192	pll_clk->dev = dev;
    193
    194	clk = devm_clk_register(dev, &pll_clk->hw);
    195	if (IS_ERR(clk)) {
    196		dev_err(dev, "failed to register %s clock (%ld)\n",
    197				clk_name, PTR_ERR(clk));
    198		return PTR_ERR(clk);
    199	}
    200
    201	return of_clk_add_provider(node, of_clk_src_simple_get, clk);
    202}
    203
    204static int i2s_pll_clk_remove(struct platform_device *pdev)
    205{
    206	of_clk_del_provider(pdev->dev.of_node);
    207	return 0;
    208}
    209
    210static const struct of_device_id i2s_pll_clk_id[] = {
    211	{ .compatible = "snps,axs10x-i2s-pll-clock", },
    212	{ },
    213};
    214MODULE_DEVICE_TABLE(of, i2s_pll_clk_id);
    215
    216static struct platform_driver i2s_pll_clk_driver = {
    217	.driver = {
    218		.name = "axs10x-i2s-pll-clock",
    219		.of_match_table = i2s_pll_clk_id,
    220	},
    221	.probe = i2s_pll_clk_probe,
    222	.remove = i2s_pll_clk_remove,
    223};
    224module_platform_driver(i2s_pll_clk_driver);
    225
    226MODULE_AUTHOR("Jose Abreu <joabreu@synopsys.com>");
    227MODULE_DESCRIPTION("Synopsys AXS10X SDP I2S PLL Clock Driver");
    228MODULE_LICENSE("GPL v2");