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

clk-xlnx-clock-wizard.c (16857B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Xilinx 'Clocking Wizard' driver
      4 *
      5 *  Copyright (C) 2013 - 2014 Xilinx
      6 *
      7 *  Sören Brinkmann <soren.brinkmann@xilinx.com>
      8 */
      9
     10#include <linux/platform_device.h>
     11#include <linux/clk.h>
     12#include <linux/clk-provider.h>
     13#include <linux/slab.h>
     14#include <linux/io.h>
     15#include <linux/of.h>
     16#include <linux/module.h>
     17#include <linux/err.h>
     18#include <linux/iopoll.h>
     19
     20#define WZRD_NUM_OUTPUTS	7
     21#define WZRD_ACLK_MAX_FREQ	250000000UL
     22
     23#define WZRD_CLK_CFG_REG(n)	(0x200 + 4 * (n))
     24
     25#define WZRD_CLKOUT0_FRAC_EN	BIT(18)
     26#define WZRD_CLKFBOUT_FRAC_EN	BIT(26)
     27
     28#define WZRD_CLKFBOUT_MULT_SHIFT	8
     29#define WZRD_CLKFBOUT_MULT_MASK		(0xff << WZRD_CLKFBOUT_MULT_SHIFT)
     30#define WZRD_CLKFBOUT_FRAC_SHIFT	16
     31#define WZRD_CLKFBOUT_FRAC_MASK		(0x3ff << WZRD_CLKFBOUT_FRAC_SHIFT)
     32#define WZRD_DIVCLK_DIVIDE_SHIFT	0
     33#define WZRD_DIVCLK_DIVIDE_MASK		(0xff << WZRD_DIVCLK_DIVIDE_SHIFT)
     34#define WZRD_CLKOUT_DIVIDE_SHIFT	0
     35#define WZRD_CLKOUT_DIVIDE_WIDTH	8
     36#define WZRD_CLKOUT_DIVIDE_MASK		(0xff << WZRD_DIVCLK_DIVIDE_SHIFT)
     37#define WZRD_CLKOUT_FRAC_SHIFT		8
     38#define WZRD_CLKOUT_FRAC_MASK		0x3ff
     39
     40#define WZRD_DR_MAX_INT_DIV_VALUE	255
     41#define WZRD_DR_STATUS_REG_OFFSET	0x04
     42#define WZRD_DR_LOCK_BIT_MASK		0x00000001
     43#define WZRD_DR_INIT_REG_OFFSET		0x25C
     44#define WZRD_DR_DIV_TO_PHASE_OFFSET	4
     45#define WZRD_DR_BEGIN_DYNA_RECONF	0x03
     46
     47#define WZRD_USEC_POLL		10
     48#define WZRD_TIMEOUT_POLL		1000
     49/* Get the mask from width */
     50#define div_mask(width)			((1 << (width)) - 1)
     51
     52/* Extract divider instance from clock hardware instance */
     53#define to_clk_wzrd_divider(_hw) container_of(_hw, struct clk_wzrd_divider, hw)
     54
     55enum clk_wzrd_int_clks {
     56	wzrd_clk_mul,
     57	wzrd_clk_mul_div,
     58	wzrd_clk_mul_frac,
     59	wzrd_clk_int_max
     60};
     61
     62/**
     63 * struct clk_wzrd - Clock wizard private data structure
     64 *
     65 * @clk_data:		Clock data
     66 * @nb:			Notifier block
     67 * @base:		Memory base
     68 * @clk_in1:		Handle to input clock 'clk_in1'
     69 * @axi_clk:		Handle to input clock 's_axi_aclk'
     70 * @clks_internal:	Internal clocks
     71 * @clkout:		Output clocks
     72 * @speed_grade:	Speed grade of the device
     73 * @suspended:		Flag indicating power state of the device
     74 */
     75struct clk_wzrd {
     76	struct clk_onecell_data clk_data;
     77	struct notifier_block nb;
     78	void __iomem *base;
     79	struct clk *clk_in1;
     80	struct clk *axi_clk;
     81	struct clk *clks_internal[wzrd_clk_int_max];
     82	struct clk *clkout[WZRD_NUM_OUTPUTS];
     83	unsigned int speed_grade;
     84	bool suspended;
     85};
     86
     87/**
     88 * struct clk_wzrd_divider - clock divider specific to clk_wzrd
     89 *
     90 * @hw:		handle between common and hardware-specific interfaces
     91 * @base:	base address of register containing the divider
     92 * @offset:	offset address of register containing the divider
     93 * @shift:	shift to the divider bit field
     94 * @width:	width of the divider bit field
     95 * @flags:	clk_wzrd divider flags
     96 * @table:	array of value/divider pairs, last entry should have div = 0
     97 * @lock:	register lock
     98 */
     99struct clk_wzrd_divider {
    100	struct clk_hw hw;
    101	void __iomem *base;
    102	u16 offset;
    103	u8 shift;
    104	u8 width;
    105	u8 flags;
    106	const struct clk_div_table *table;
    107	spinlock_t *lock;  /* divider lock */
    108};
    109
    110#define to_clk_wzrd(_nb) container_of(_nb, struct clk_wzrd, nb)
    111
    112/* maximum frequencies for input/output clocks per speed grade */
    113static const unsigned long clk_wzrd_max_freq[] = {
    114	800000000UL,
    115	933000000UL,
    116	1066000000UL
    117};
    118
    119/* spin lock variable for clk_wzrd */
    120static DEFINE_SPINLOCK(clkwzrd_lock);
    121
    122static unsigned long clk_wzrd_recalc_rate(struct clk_hw *hw,
    123					  unsigned long parent_rate)
    124{
    125	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
    126	void __iomem *div_addr = divider->base + divider->offset;
    127	unsigned int val;
    128
    129	val = readl(div_addr) >> divider->shift;
    130	val &= div_mask(divider->width);
    131
    132	return divider_recalc_rate(hw, parent_rate, val, divider->table,
    133			divider->flags, divider->width);
    134}
    135
    136static int clk_wzrd_dynamic_reconfig(struct clk_hw *hw, unsigned long rate,
    137				     unsigned long parent_rate)
    138{
    139	int err;
    140	u32 value;
    141	unsigned long flags = 0;
    142	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
    143	void __iomem *div_addr = divider->base + divider->offset;
    144
    145	if (divider->lock)
    146		spin_lock_irqsave(divider->lock, flags);
    147	else
    148		__acquire(divider->lock);
    149
    150	value = DIV_ROUND_CLOSEST(parent_rate, rate);
    151
    152	/* Cap the value to max */
    153	min_t(u32, value, WZRD_DR_MAX_INT_DIV_VALUE);
    154
    155	/* Set divisor and clear phase offset */
    156	writel(value, div_addr);
    157	writel(0x00, div_addr + WZRD_DR_DIV_TO_PHASE_OFFSET);
    158
    159	/* Check status register */
    160	err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET,
    161				 value, value & WZRD_DR_LOCK_BIT_MASK,
    162				 WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
    163	if (err)
    164		goto err_reconfig;
    165
    166	/* Initiate reconfiguration */
    167	writel(WZRD_DR_BEGIN_DYNA_RECONF,
    168	       divider->base + WZRD_DR_INIT_REG_OFFSET);
    169
    170	/* Check status register */
    171	err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET,
    172				 value, value & WZRD_DR_LOCK_BIT_MASK,
    173				 WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
    174err_reconfig:
    175	if (divider->lock)
    176		spin_unlock_irqrestore(divider->lock, flags);
    177	else
    178		__release(divider->lock);
    179	return err;
    180}
    181
    182static long clk_wzrd_round_rate(struct clk_hw *hw, unsigned long rate,
    183				unsigned long *prate)
    184{
    185	u8 div;
    186
    187	/*
    188	 * since we don't change parent rate we just round rate to closest
    189	 * achievable
    190	 */
    191	div = DIV_ROUND_CLOSEST(*prate, rate);
    192
    193	return *prate / div;
    194}
    195
    196static const struct clk_ops clk_wzrd_clk_divider_ops = {
    197	.round_rate = clk_wzrd_round_rate,
    198	.set_rate = clk_wzrd_dynamic_reconfig,
    199	.recalc_rate = clk_wzrd_recalc_rate,
    200};
    201
    202static unsigned long clk_wzrd_recalc_ratef(struct clk_hw *hw,
    203					   unsigned long parent_rate)
    204{
    205	unsigned int val;
    206	u32 div, frac;
    207	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
    208	void __iomem *div_addr = divider->base + divider->offset;
    209
    210	val = readl(div_addr);
    211	div = val & div_mask(divider->width);
    212	frac = (val >> WZRD_CLKOUT_FRAC_SHIFT) & WZRD_CLKOUT_FRAC_MASK;
    213
    214	return mult_frac(parent_rate, 1000, (div * 1000) + frac);
    215}
    216
    217static int clk_wzrd_dynamic_reconfig_f(struct clk_hw *hw, unsigned long rate,
    218				       unsigned long parent_rate)
    219{
    220	int err;
    221	u32 value, pre;
    222	unsigned long rate_div, f, clockout0_div;
    223	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
    224	void __iomem *div_addr = divider->base + divider->offset;
    225
    226	rate_div = ((parent_rate * 1000) / rate);
    227	clockout0_div = rate_div / 1000;
    228
    229	pre = DIV_ROUND_CLOSEST((parent_rate * 1000), rate);
    230	f = (u32)(pre - (clockout0_div * 1000));
    231	f = f & WZRD_CLKOUT_FRAC_MASK;
    232	f = f << WZRD_CLKOUT_DIVIDE_WIDTH;
    233
    234	value = (f  | (clockout0_div & WZRD_CLKOUT_DIVIDE_MASK));
    235
    236	/* Set divisor and clear phase offset */
    237	writel(value, div_addr);
    238	writel(0x0, div_addr + WZRD_DR_DIV_TO_PHASE_OFFSET);
    239
    240	/* Check status register */
    241	err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value,
    242				 value & WZRD_DR_LOCK_BIT_MASK,
    243				 WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
    244	if (err)
    245		return err;
    246
    247	/* Initiate reconfiguration */
    248	writel(WZRD_DR_BEGIN_DYNA_RECONF,
    249	       divider->base + WZRD_DR_INIT_REG_OFFSET);
    250
    251	/* Check status register */
    252	return readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value,
    253				value & WZRD_DR_LOCK_BIT_MASK,
    254				WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
    255}
    256
    257static long clk_wzrd_round_rate_f(struct clk_hw *hw, unsigned long rate,
    258				  unsigned long *prate)
    259{
    260	return rate;
    261}
    262
    263static const struct clk_ops clk_wzrd_clk_divider_ops_f = {
    264	.round_rate = clk_wzrd_round_rate_f,
    265	.set_rate = clk_wzrd_dynamic_reconfig_f,
    266	.recalc_rate = clk_wzrd_recalc_ratef,
    267};
    268
    269static struct clk *clk_wzrd_register_divf(struct device *dev,
    270					  const char *name,
    271					  const char *parent_name,
    272					  unsigned long flags,
    273					  void __iomem *base, u16 offset,
    274					  u8 shift, u8 width,
    275					  u8 clk_divider_flags,
    276					  const struct clk_div_table *table,
    277					  spinlock_t *lock)
    278{
    279	struct clk_wzrd_divider *div;
    280	struct clk_hw *hw;
    281	struct clk_init_data init;
    282	int ret;
    283
    284	div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
    285	if (!div)
    286		return ERR_PTR(-ENOMEM);
    287
    288	init.name = name;
    289
    290	init.ops = &clk_wzrd_clk_divider_ops_f;
    291
    292	init.flags = flags;
    293	init.parent_names = &parent_name;
    294	init.num_parents = 1;
    295
    296	div->base = base;
    297	div->offset = offset;
    298	div->shift = shift;
    299	div->width = width;
    300	div->flags = clk_divider_flags;
    301	div->lock = lock;
    302	div->hw.init = &init;
    303	div->table = table;
    304
    305	hw = &div->hw;
    306	ret =  devm_clk_hw_register(dev, hw);
    307	if (ret)
    308		return ERR_PTR(ret);
    309
    310	return hw->clk;
    311}
    312
    313static struct clk *clk_wzrd_register_divider(struct device *dev,
    314					     const char *name,
    315					     const char *parent_name,
    316					     unsigned long flags,
    317					     void __iomem *base, u16 offset,
    318					     u8 shift, u8 width,
    319					     u8 clk_divider_flags,
    320					     const struct clk_div_table *table,
    321					     spinlock_t *lock)
    322{
    323	struct clk_wzrd_divider *div;
    324	struct clk_hw *hw;
    325	struct clk_init_data init;
    326	int ret;
    327
    328	div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
    329	if (!div)
    330		return ERR_PTR(-ENOMEM);
    331
    332	init.name = name;
    333	init.ops = &clk_wzrd_clk_divider_ops;
    334	init.flags = flags;
    335	init.parent_names =  &parent_name;
    336	init.num_parents =  1;
    337
    338	div->base = base;
    339	div->offset = offset;
    340	div->shift = shift;
    341	div->width = width;
    342	div->flags = clk_divider_flags;
    343	div->lock = lock;
    344	div->hw.init = &init;
    345	div->table = table;
    346
    347	hw = &div->hw;
    348	ret = devm_clk_hw_register(dev, hw);
    349	if (ret)
    350		hw = ERR_PTR(ret);
    351
    352	return hw->clk;
    353}
    354
    355static int clk_wzrd_clk_notifier(struct notifier_block *nb, unsigned long event,
    356				 void *data)
    357{
    358	unsigned long max;
    359	struct clk_notifier_data *ndata = data;
    360	struct clk_wzrd *clk_wzrd = to_clk_wzrd(nb);
    361
    362	if (clk_wzrd->suspended)
    363		return NOTIFY_OK;
    364
    365	if (ndata->clk == clk_wzrd->clk_in1)
    366		max = clk_wzrd_max_freq[clk_wzrd->speed_grade - 1];
    367	else if (ndata->clk == clk_wzrd->axi_clk)
    368		max = WZRD_ACLK_MAX_FREQ;
    369	else
    370		return NOTIFY_DONE;	/* should never happen */
    371
    372	switch (event) {
    373	case PRE_RATE_CHANGE:
    374		if (ndata->new_rate > max)
    375			return NOTIFY_BAD;
    376		return NOTIFY_OK;
    377	case POST_RATE_CHANGE:
    378	case ABORT_RATE_CHANGE:
    379	default:
    380		return NOTIFY_DONE;
    381	}
    382}
    383
    384static int __maybe_unused clk_wzrd_suspend(struct device *dev)
    385{
    386	struct clk_wzrd *clk_wzrd = dev_get_drvdata(dev);
    387
    388	clk_disable_unprepare(clk_wzrd->axi_clk);
    389	clk_wzrd->suspended = true;
    390
    391	return 0;
    392}
    393
    394static int __maybe_unused clk_wzrd_resume(struct device *dev)
    395{
    396	int ret;
    397	struct clk_wzrd *clk_wzrd = dev_get_drvdata(dev);
    398
    399	ret = clk_prepare_enable(clk_wzrd->axi_clk);
    400	if (ret) {
    401		dev_err(dev, "unable to enable s_axi_aclk\n");
    402		return ret;
    403	}
    404
    405	clk_wzrd->suspended = false;
    406
    407	return 0;
    408}
    409
    410static SIMPLE_DEV_PM_OPS(clk_wzrd_dev_pm_ops, clk_wzrd_suspend,
    411			 clk_wzrd_resume);
    412
    413static int clk_wzrd_probe(struct platform_device *pdev)
    414{
    415	int i, ret;
    416	u32 reg, reg_f, mult;
    417	unsigned long rate;
    418	const char *clk_name;
    419	void __iomem *ctrl_reg;
    420	struct clk_wzrd *clk_wzrd;
    421	struct device_node *np = pdev->dev.of_node;
    422	int nr_outputs;
    423	unsigned long flags = 0;
    424
    425	clk_wzrd = devm_kzalloc(&pdev->dev, sizeof(*clk_wzrd), GFP_KERNEL);
    426	if (!clk_wzrd)
    427		return -ENOMEM;
    428	platform_set_drvdata(pdev, clk_wzrd);
    429
    430	clk_wzrd->base = devm_platform_ioremap_resource(pdev, 0);
    431	if (IS_ERR(clk_wzrd->base))
    432		return PTR_ERR(clk_wzrd->base);
    433
    434	ret = of_property_read_u32(np, "xlnx,speed-grade", &clk_wzrd->speed_grade);
    435	if (!ret) {
    436		if (clk_wzrd->speed_grade < 1 || clk_wzrd->speed_grade > 3) {
    437			dev_warn(&pdev->dev, "invalid speed grade '%d'\n",
    438				 clk_wzrd->speed_grade);
    439			clk_wzrd->speed_grade = 0;
    440		}
    441	}
    442
    443	clk_wzrd->clk_in1 = devm_clk_get(&pdev->dev, "clk_in1");
    444	if (IS_ERR(clk_wzrd->clk_in1)) {
    445		if (clk_wzrd->clk_in1 != ERR_PTR(-EPROBE_DEFER))
    446			dev_err(&pdev->dev, "clk_in1 not found\n");
    447		return PTR_ERR(clk_wzrd->clk_in1);
    448	}
    449
    450	clk_wzrd->axi_clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
    451	if (IS_ERR(clk_wzrd->axi_clk)) {
    452		if (clk_wzrd->axi_clk != ERR_PTR(-EPROBE_DEFER))
    453			dev_err(&pdev->dev, "s_axi_aclk not found\n");
    454		return PTR_ERR(clk_wzrd->axi_clk);
    455	}
    456	ret = clk_prepare_enable(clk_wzrd->axi_clk);
    457	if (ret) {
    458		dev_err(&pdev->dev, "enabling s_axi_aclk failed\n");
    459		return ret;
    460	}
    461	rate = clk_get_rate(clk_wzrd->axi_clk);
    462	if (rate > WZRD_ACLK_MAX_FREQ) {
    463		dev_err(&pdev->dev, "s_axi_aclk frequency (%lu) too high\n",
    464			rate);
    465		ret = -EINVAL;
    466		goto err_disable_clk;
    467	}
    468
    469	reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0));
    470	reg_f = reg & WZRD_CLKFBOUT_FRAC_MASK;
    471	reg_f =  reg_f >> WZRD_CLKFBOUT_FRAC_SHIFT;
    472
    473	reg = reg & WZRD_CLKFBOUT_MULT_MASK;
    474	reg =  reg >> WZRD_CLKFBOUT_MULT_SHIFT;
    475	mult = (reg * 1000) + reg_f;
    476	clk_name = kasprintf(GFP_KERNEL, "%s_mul", dev_name(&pdev->dev));
    477	if (!clk_name) {
    478		ret = -ENOMEM;
    479		goto err_disable_clk;
    480	}
    481
    482	ret = of_property_read_u32(np, "nr-outputs", &nr_outputs);
    483	if (ret || nr_outputs > WZRD_NUM_OUTPUTS) {
    484		ret = -EINVAL;
    485		goto err_disable_clk;
    486	}
    487	if (nr_outputs == 1)
    488		flags = CLK_SET_RATE_PARENT;
    489
    490	clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor
    491			(&pdev->dev, clk_name,
    492			 __clk_get_name(clk_wzrd->clk_in1),
    493			0, mult, 1000);
    494	if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) {
    495		dev_err(&pdev->dev, "unable to register fixed-factor clock\n");
    496		ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul]);
    497		goto err_disable_clk;
    498	}
    499
    500	clk_name = kasprintf(GFP_KERNEL, "%s_mul_div", dev_name(&pdev->dev));
    501	if (!clk_name) {
    502		ret = -ENOMEM;
    503		goto err_rm_int_clk;
    504	}
    505
    506	ctrl_reg = clk_wzrd->base + WZRD_CLK_CFG_REG(0);
    507	/* register div */
    508	clk_wzrd->clks_internal[wzrd_clk_mul_div] = clk_register_divider
    509			(&pdev->dev, clk_name,
    510			 __clk_get_name(clk_wzrd->clks_internal[wzrd_clk_mul]),
    511			flags, ctrl_reg, 0, 8, CLK_DIVIDER_ONE_BASED |
    512			CLK_DIVIDER_ALLOW_ZERO, &clkwzrd_lock);
    513	if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div])) {
    514		dev_err(&pdev->dev, "unable to register divider clock\n");
    515		ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul_div]);
    516		goto err_rm_int_clk;
    517	}
    518
    519	/* register div per output */
    520	for (i = nr_outputs - 1; i >= 0 ; i--) {
    521		const char *clkout_name;
    522
    523		clkout_name = kasprintf(GFP_KERNEL, "%s_out%d", dev_name(&pdev->dev), i);
    524		if (!clkout_name) {
    525			ret = -ENOMEM;
    526			goto err_rm_int_clk;
    527		}
    528
    529		if (!i)
    530			clk_wzrd->clkout[i] = clk_wzrd_register_divf
    531				(&pdev->dev, clkout_name,
    532				clk_name, flags,
    533				clk_wzrd->base, (WZRD_CLK_CFG_REG(2) + i * 12),
    534				WZRD_CLKOUT_DIVIDE_SHIFT,
    535				WZRD_CLKOUT_DIVIDE_WIDTH,
    536				CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
    537				NULL, &clkwzrd_lock);
    538		else
    539			clk_wzrd->clkout[i] = clk_wzrd_register_divider
    540				(&pdev->dev, clkout_name,
    541				clk_name, 0,
    542				clk_wzrd->base, (WZRD_CLK_CFG_REG(2) + i * 12),
    543				WZRD_CLKOUT_DIVIDE_SHIFT,
    544				WZRD_CLKOUT_DIVIDE_WIDTH,
    545				CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
    546				NULL, &clkwzrd_lock);
    547		if (IS_ERR(clk_wzrd->clkout[i])) {
    548			int j;
    549
    550			for (j = i + 1; j < nr_outputs; j++)
    551				clk_unregister(clk_wzrd->clkout[j]);
    552			dev_err(&pdev->dev,
    553				"unable to register divider clock\n");
    554			ret = PTR_ERR(clk_wzrd->clkout[i]);
    555			goto err_rm_int_clks;
    556		}
    557	}
    558
    559	kfree(clk_name);
    560
    561	clk_wzrd->clk_data.clks = clk_wzrd->clkout;
    562	clk_wzrd->clk_data.clk_num = ARRAY_SIZE(clk_wzrd->clkout);
    563	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_wzrd->clk_data);
    564
    565	if (clk_wzrd->speed_grade) {
    566		clk_wzrd->nb.notifier_call = clk_wzrd_clk_notifier;
    567
    568		ret = clk_notifier_register(clk_wzrd->clk_in1,
    569					    &clk_wzrd->nb);
    570		if (ret)
    571			dev_warn(&pdev->dev,
    572				 "unable to register clock notifier\n");
    573
    574		ret = clk_notifier_register(clk_wzrd->axi_clk, &clk_wzrd->nb);
    575		if (ret)
    576			dev_warn(&pdev->dev,
    577				 "unable to register clock notifier\n");
    578	}
    579
    580	return 0;
    581
    582err_rm_int_clks:
    583	clk_unregister(clk_wzrd->clks_internal[1]);
    584err_rm_int_clk:
    585	kfree(clk_name);
    586	clk_unregister(clk_wzrd->clks_internal[0]);
    587err_disable_clk:
    588	clk_disable_unprepare(clk_wzrd->axi_clk);
    589
    590	return ret;
    591}
    592
    593static int clk_wzrd_remove(struct platform_device *pdev)
    594{
    595	int i;
    596	struct clk_wzrd *clk_wzrd = platform_get_drvdata(pdev);
    597
    598	of_clk_del_provider(pdev->dev.of_node);
    599
    600	for (i = 0; i < WZRD_NUM_OUTPUTS; i++)
    601		clk_unregister(clk_wzrd->clkout[i]);
    602	for (i = 0; i < wzrd_clk_int_max; i++)
    603		clk_unregister(clk_wzrd->clks_internal[i]);
    604
    605	if (clk_wzrd->speed_grade) {
    606		clk_notifier_unregister(clk_wzrd->axi_clk, &clk_wzrd->nb);
    607		clk_notifier_unregister(clk_wzrd->clk_in1, &clk_wzrd->nb);
    608	}
    609
    610	clk_disable_unprepare(clk_wzrd->axi_clk);
    611
    612	return 0;
    613}
    614
    615static const struct of_device_id clk_wzrd_ids[] = {
    616	{ .compatible = "xlnx,clocking-wizard" },
    617	{ },
    618};
    619MODULE_DEVICE_TABLE(of, clk_wzrd_ids);
    620
    621static struct platform_driver clk_wzrd_driver = {
    622	.driver = {
    623		.name = "clk-wizard",
    624		.of_match_table = clk_wzrd_ids,
    625		.pm = &clk_wzrd_dev_pm_ops,
    626	},
    627	.probe = clk_wzrd_probe,
    628	.remove = clk_wzrd_remove,
    629};
    630module_platform_driver(clk_wzrd_driver);
    631
    632MODULE_LICENSE("GPL");
    633MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com");
    634MODULE_DESCRIPTION("Driver for the Xilinx Clocking Wizard IP core");