Skip to content

Commit 9c789de

Browse files
tretterbebarino
authored andcommitted
soc: xilinx: vcu: implement clock provider for output clocks
The VCU System-Level Control uses an internal PLL to drive the core and MCU clock for the allegro encoder and decoder based on an external PL clock. In order be able to ensure that the clocks are enabled and to get their rate from other drivers, the module must implement a clock provider and register the clocks at the common clock framework. Other drivers are then able to access the clock via devicetree bindings. Signed-off-by: Michael Tretter <m.tretter@pengutronix.de> Acked-by: Michal Simek <michal.simek@xilinx.com> Link: https://lore.kernel.org/r/20210121071659.1226489-9-m.tretter@pengutronix.de Signed-off-by: Stephen Boyd <sboyd@kernel.org>
1 parent 5a2b2e1 commit 9c789de

1 file changed

Lines changed: 160 additions & 37 deletions

File tree

drivers/soc/xilinx/xlnx_vcu.c

Lines changed: 160 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include <linux/platform_device.h>
1919
#include <linux/regmap.h>
2020

21+
#include <dt-bindings/clock/xlnx-vcu.h>
22+
2123
/* vcu slcr registers, bitmask and shift */
2224
#define VCU_PLL_CTRL 0x24
2325
#define VCU_PLL_CTRL_RESET_MASK 0x01
@@ -50,11 +52,6 @@
5052
#define VCU_ENC_MCU_CTRL 0x34
5153
#define VCU_DEC_CORE_CTRL 0x38
5254
#define VCU_DEC_MCU_CTRL 0x3c
53-
#define VCU_PLL_DIVISOR_MASK 0x3f
54-
#define VCU_PLL_DIVISOR_SHIFT 4
55-
#define VCU_SRCSEL_MASK 0x01
56-
#define VCU_SRCSEL_SHIFT 0
57-
#define VCU_SRCSEL_PLL 1
5855

5956
#define VCU_PLL_STATUS 0x60
6057
#define VCU_PLL_STATUS_LOCK_STATUS_MASK 0x01
@@ -75,6 +72,7 @@
7572
* @logicore_reg_ba: logicore reg base address
7673
* @vcu_slcr_ba: vcu_slcr Register base address
7774
* @pll: handle for the VCU PLL
75+
* @clk_data: clocks provided by the vcu clock provider
7876
*/
7977
struct xvcu_device {
8078
struct device *dev;
@@ -83,6 +81,7 @@ struct xvcu_device {
8381
struct regmap *logicore_reg_ba;
8482
void __iomem *vcu_slcr_ba;
8583
struct clk_hw *pll;
84+
struct clk_hw_onecell_data *clk_data;
8685
};
8786

8887
static struct regmap_config vcu_settings_regmap_config = {
@@ -404,7 +403,7 @@ static int xvcu_set_vcu_pll_info(struct xvcu_device *xvcu)
404403
u32 refclk, coreclk, mcuclk, inte, deci;
405404
u32 divisor_mcu, divisor_core, fvco;
406405
u32 clkoutdiv, vcu_pll_ctrl, pll_clk;
407-
u32 mod, ctrl;
406+
u32 mod;
408407
int i;
409408
int ret;
410409
const struct xvcu_pll_cfg *found = NULL;
@@ -479,37 +478,6 @@ static int xvcu_set_vcu_pll_info(struct xvcu_device *xvcu)
479478
dev_dbg(xvcu->dev, "Actual Core clock freq is %uHz\n", coreclk);
480479
dev_dbg(xvcu->dev, "Actual Mcu clock freq is %uHz\n", mcuclk);
481480

482-
/* Set divisor for the core and mcu clock */
483-
ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_ENC_CORE_CTRL);
484-
ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT);
485-
ctrl |= (divisor_core & VCU_PLL_DIVISOR_MASK) <<
486-
VCU_PLL_DIVISOR_SHIFT;
487-
ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT);
488-
ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT;
489-
xvcu_write(xvcu->vcu_slcr_ba, VCU_ENC_CORE_CTRL, ctrl);
490-
491-
ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_DEC_CORE_CTRL);
492-
ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT);
493-
ctrl |= (divisor_core & VCU_PLL_DIVISOR_MASK) <<
494-
VCU_PLL_DIVISOR_SHIFT;
495-
ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT);
496-
ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT;
497-
xvcu_write(xvcu->vcu_slcr_ba, VCU_DEC_CORE_CTRL, ctrl);
498-
499-
ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_ENC_MCU_CTRL);
500-
ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT);
501-
ctrl |= (divisor_mcu & VCU_PLL_DIVISOR_MASK) << VCU_PLL_DIVISOR_SHIFT;
502-
ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT);
503-
ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT;
504-
xvcu_write(xvcu->vcu_slcr_ba, VCU_ENC_MCU_CTRL, ctrl);
505-
506-
ctrl = xvcu_read(xvcu->vcu_slcr_ba, VCU_DEC_MCU_CTRL);
507-
ctrl &= ~(VCU_PLL_DIVISOR_MASK << VCU_PLL_DIVISOR_SHIFT);
508-
ctrl |= (divisor_mcu & VCU_PLL_DIVISOR_MASK) << VCU_PLL_DIVISOR_SHIFT;
509-
ctrl &= ~(VCU_SRCSEL_MASK << VCU_SRCSEL_SHIFT);
510-
ctrl |= (VCU_SRCSEL_PLL & VCU_SRCSEL_MASK) << VCU_SRCSEL_SHIFT;
511-
xvcu_write(xvcu->vcu_slcr_ba, VCU_DEC_MCU_CTRL, ctrl);
512-
513481
ret = xvcu_pll_set_rate(xvcu, fvco, refclk);
514482
if (ret)
515483
return ret;
@@ -546,6 +514,151 @@ static int xvcu_set_pll(struct xvcu_device *xvcu)
546514
return xvcu_pll_enable(xvcu);
547515
}
548516

517+
static struct clk_hw *xvcu_clk_hw_register_leaf(struct device *dev,
518+
const char *name,
519+
const struct clk_parent_data *parent_data,
520+
u8 num_parents,
521+
void __iomem *reg)
522+
{
523+
u8 mux_flags = CLK_MUX_ROUND_CLOSEST;
524+
u8 divider_flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO |
525+
CLK_DIVIDER_ROUND_CLOSEST;
526+
struct clk_hw *mux = NULL;
527+
struct clk_hw *divider = NULL;
528+
struct clk_hw *gate = NULL;
529+
char *name_mux;
530+
char *name_div;
531+
int err;
532+
/* Protect register shared by clocks */
533+
spinlock_t *lock;
534+
535+
lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
536+
if (!lock)
537+
return ERR_PTR(-ENOMEM);
538+
spin_lock_init(lock);
539+
540+
name_mux = devm_kasprintf(dev, GFP_KERNEL, "%s%s", name, "_mux");
541+
if (!name_mux)
542+
return ERR_PTR(-ENOMEM);
543+
mux = clk_hw_register_mux_parent_data(dev, name_mux,
544+
parent_data, num_parents,
545+
CLK_SET_RATE_PARENT,
546+
reg, 0, 1, mux_flags, lock);
547+
if (IS_ERR(mux))
548+
return mux;
549+
550+
name_div = devm_kasprintf(dev, GFP_KERNEL, "%s%s", name, "_div");
551+
if (!name_div) {
552+
err = -ENOMEM;
553+
goto unregister_mux;
554+
}
555+
divider = clk_hw_register_divider_parent_hw(dev, name_div, mux,
556+
CLK_SET_RATE_PARENT,
557+
reg, 4, 6, divider_flags,
558+
lock);
559+
if (IS_ERR(divider)) {
560+
err = PTR_ERR(divider);
561+
goto unregister_mux;
562+
}
563+
564+
gate = clk_hw_register_gate_parent_hw(dev, name, divider,
565+
CLK_SET_RATE_PARENT, reg, 12, 0,
566+
lock);
567+
if (IS_ERR(gate)) {
568+
err = PTR_ERR(gate);
569+
goto unregister_divider;
570+
}
571+
572+
return gate;
573+
574+
unregister_divider:
575+
clk_hw_unregister_divider(divider);
576+
unregister_mux:
577+
clk_hw_unregister_mux(mux);
578+
579+
return ERR_PTR(err);
580+
}
581+
582+
static void xvcu_clk_hw_unregister_leaf(struct clk_hw *hw)
583+
{
584+
struct clk_hw *gate = hw;
585+
struct clk_hw *divider;
586+
struct clk_hw *mux;
587+
588+
if (!gate)
589+
return;
590+
591+
divider = clk_hw_get_parent(gate);
592+
clk_hw_unregister_gate(gate);
593+
if (!divider)
594+
return;
595+
596+
mux = clk_hw_get_parent(divider);
597+
clk_hw_unregister_mux(mux);
598+
if (!divider)
599+
return;
600+
601+
clk_hw_unregister_divider(divider);
602+
}
603+
604+
static int xvcu_register_clock_provider(struct xvcu_device *xvcu)
605+
{
606+
struct device *dev = xvcu->dev;
607+
struct clk_parent_data parent_data[2] = { 0 };
608+
struct clk_hw_onecell_data *data;
609+
struct clk_hw **hws;
610+
void __iomem *reg_base = xvcu->vcu_slcr_ba;
611+
612+
data = devm_kzalloc(dev, struct_size(data, hws, CLK_XVCU_NUM_CLOCKS), GFP_KERNEL);
613+
if (!data)
614+
return -ENOMEM;
615+
data->num = CLK_XVCU_NUM_CLOCKS;
616+
hws = data->hws;
617+
618+
xvcu->clk_data = data;
619+
620+
parent_data[0].fw_name = "pll_ref";
621+
parent_data[1].hw = xvcu->pll;
622+
623+
hws[CLK_XVCU_ENC_CORE] =
624+
xvcu_clk_hw_register_leaf(dev, "venc_core_clk",
625+
parent_data,
626+
ARRAY_SIZE(parent_data),
627+
reg_base + VCU_ENC_CORE_CTRL);
628+
hws[CLK_XVCU_ENC_MCU] =
629+
xvcu_clk_hw_register_leaf(dev, "venc_mcu_clk",
630+
parent_data,
631+
ARRAY_SIZE(parent_data),
632+
reg_base + VCU_ENC_MCU_CTRL);
633+
hws[CLK_XVCU_DEC_CORE] =
634+
xvcu_clk_hw_register_leaf(dev, "vdec_core_clk",
635+
parent_data,
636+
ARRAY_SIZE(parent_data),
637+
reg_base + VCU_DEC_CORE_CTRL);
638+
hws[CLK_XVCU_DEC_MCU] =
639+
xvcu_clk_hw_register_leaf(dev, "vdec_mcu_clk",
640+
parent_data,
641+
ARRAY_SIZE(parent_data),
642+
reg_base + VCU_DEC_MCU_CTRL);
643+
644+
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data);
645+
}
646+
647+
static void xvcu_unregister_clock_provider(struct xvcu_device *xvcu)
648+
{
649+
struct clk_hw_onecell_data *data = xvcu->clk_data;
650+
struct clk_hw **hws = data->hws;
651+
652+
if (!IS_ERR_OR_NULL(hws[CLK_XVCU_DEC_MCU]))
653+
xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_DEC_MCU]);
654+
if (!IS_ERR_OR_NULL(hws[CLK_XVCU_DEC_CORE]))
655+
xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_DEC_CORE]);
656+
if (!IS_ERR_OR_NULL(hws[CLK_XVCU_ENC_MCU]))
657+
xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_MCU]);
658+
if (!IS_ERR_OR_NULL(hws[CLK_XVCU_ENC_CORE]))
659+
xvcu_clk_hw_unregister_leaf(hws[CLK_XVCU_ENC_CORE]);
660+
}
661+
549662
/**
550663
* xvcu_probe - Probe existence of the logicoreIP
551664
* and initialize PLL
@@ -640,10 +753,18 @@ static int xvcu_probe(struct platform_device *pdev)
640753
goto error_pll_ref;
641754
}
642755

756+
ret = xvcu_register_clock_provider(xvcu);
757+
if (ret) {
758+
dev_err(&pdev->dev, "failed to register clock provider\n");
759+
goto error_clk_provider;
760+
}
761+
643762
dev_set_drvdata(&pdev->dev, xvcu);
644763

645764
return 0;
646765

766+
error_clk_provider:
767+
xvcu_unregister_clock_provider(xvcu);
647768
error_pll_ref:
648769
clk_disable_unprepare(xvcu->aclk);
649770
return ret;
@@ -665,6 +786,8 @@ static int xvcu_remove(struct platform_device *pdev)
665786
if (!xvcu)
666787
return -ENODEV;
667788

789+
xvcu_unregister_clock_provider(xvcu);
790+
668791
/* Add the the Gasket isolation and put the VCU in reset. */
669792
regmap_write(xvcu->logicore_reg_ba, VCU_GASKET_INIT, 0);
670793

0 commit comments

Comments
 (0)