aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/st
diff options
context:
space:
mode:
authorGabriel FERNANDEZ <gabriel.fernandez@st.com>2014-02-27 10:24:16 -0500
committerMike Turquette <mturquette@linaro.org>2014-03-25 18:59:02 -0400
commit44993d384004fa9ca2dfcca86cddc436a28d6958 (patch)
treebbfa12964f4b40f8d9eea3f0ffb11b9940d94fb4 /drivers/clk/st
parentb9b8e614b5805a99a5484c3d44fbfebaa8de4c65 (diff)
clk: st: Support for VCC-mux and MUX clocks
The patch supports the VCC-mux and MUX clocks used by ClockGenC/F VCC-mux clock : Divider-Multiplexer-Gate inside ClockGenC/F It includes support for each channel : 4-parent Multiplexer, Post Divide by 1, 2, 4 or 8, Gate to switch OFF the output channel. The clock is implemented using generic clocks implemented in the kernel clk_divider, clk_mux, clk_gate and clk_composite (to combine all) MUX clock : 2-parent clock used inside ClockGenC/F. The clock is implemented using generic clocks implemented in the kernel clk_mux. Signed-off-by: Pankaj Dev <pankaj.dev@st.com> Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com> Signed-off-by: Mike Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/clk/st')
-rw-r--r--drivers/clk/st/clkgen-mux.c272
1 files changed, 272 insertions, 0 deletions
diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c
index 65d702c2df8d..7ccff620a2fe 100644
--- a/drivers/clk/st/clkgen-mux.c
+++ b/drivers/clk/st/clkgen-mux.c
@@ -18,6 +18,7 @@
18#include <linux/clk-provider.h> 18#include <linux/clk-provider.h>
19 19
20static DEFINE_SPINLOCK(clkgena_divmux_lock); 20static DEFINE_SPINLOCK(clkgena_divmux_lock);
21static DEFINE_SPINLOCK(clkgenf_lock);
21 22
22static const char ** __init clkgen_mux_get_parents(struct device_node *np, 23static const char ** __init clkgen_mux_get_parents(struct device_node *np,
23 int *num_parents) 24 int *num_parents)
@@ -527,3 +528,274 @@ void __init st_of_clkgena_prediv_setup(struct device_node *np)
527 return; 528 return;
528} 529}
529CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup); 530CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup);
531
532struct clkgen_mux_data {
533 u32 offset;
534 u8 shift;
535 u8 width;
536 spinlock_t *lock;
537 unsigned long clk_flags;
538 u8 mux_flags;
539};
540
541static struct clkgen_mux_data clkgen_mux_c_vcc_hd_416 = {
542 .offset = 0,
543 .shift = 0,
544 .width = 1,
545};
546
547static struct clkgen_mux_data clkgen_mux_f_vcc_fvdp_416 = {
548 .offset = 0,
549 .shift = 0,
550 .width = 1,
551};
552
553static struct clkgen_mux_data clkgen_mux_f_vcc_hva_416 = {
554 .offset = 0,
555 .shift = 0,
556 .width = 1,
557};
558
559static struct clkgen_mux_data clkgen_mux_f_vcc_hd_416 = {
560 .offset = 0,
561 .shift = 16,
562 .width = 1,
563 .lock = &clkgenf_lock,
564};
565
566static struct clkgen_mux_data clkgen_mux_c_vcc_sd_416 = {
567 .offset = 0,
568 .shift = 17,
569 .width = 1,
570 .lock = &clkgenf_lock,
571};
572
573static struct of_device_id mux_of_match[] = {
574 {
575 .compatible = "st,stih416-clkgenc-vcc-hd",
576 .data = &clkgen_mux_c_vcc_hd_416,
577 },
578 {
579 .compatible = "st,stih416-clkgenf-vcc-fvdp",
580 .data = &clkgen_mux_f_vcc_fvdp_416,
581 },
582 {
583 .compatible = "st,stih416-clkgenf-vcc-hva",
584 .data = &clkgen_mux_f_vcc_hva_416,
585 },
586 {
587 .compatible = "st,stih416-clkgenf-vcc-hd",
588 .data = &clkgen_mux_f_vcc_hd_416,
589 },
590 {
591 .compatible = "st,stih416-clkgenf-vcc-sd",
592 .data = &clkgen_mux_c_vcc_sd_416,
593 },
594 {}
595};
596
597void __init st_of_clkgen_mux_setup(struct device_node *np)
598{
599 const struct of_device_id *match;
600 struct clk *clk;
601 void __iomem *reg;
602 const char **parents;
603 int num_parents;
604 struct clkgen_mux_data *data;
605
606 match = of_match_node(mux_of_match, np);
607 if (!match) {
608 pr_err("%s: No matching data\n", __func__);
609 return;
610 }
611
612 data = (struct clkgen_mux_data *)match->data;
613
614 reg = of_iomap(np, 0);
615 if (!reg) {
616 pr_err("%s: Failed to get base address\n", __func__);
617 return;
618 }
619
620 parents = clkgen_mux_get_parents(np, &num_parents);
621 if (IS_ERR(parents)) {
622 pr_err("%s: Failed to get parents (%ld)\n",
623 __func__, PTR_ERR(parents));
624 return;
625 }
626
627 clk = clk_register_mux(NULL, np->name, parents, num_parents,
628 data->clk_flags | CLK_SET_RATE_PARENT,
629 reg + data->offset,
630 data->shift, data->width, data->mux_flags,
631 data->lock);
632 if (IS_ERR(clk))
633 goto err;
634
635 pr_debug("%s: parent %s rate %u\n",
636 __clk_get_name(clk),
637 __clk_get_name(clk_get_parent(clk)),
638 (unsigned int)clk_get_rate(clk));
639
640 of_clk_add_provider(np, of_clk_src_simple_get, clk);
641
642err:
643 kfree(parents);
644
645 return;
646}
647CLK_OF_DECLARE(clkgen_mux, "st,clkgen-mux", st_of_clkgen_mux_setup);
648
649#define VCC_MAX_CHANNELS 16
650
651#define VCC_GATE_OFFSET 0x0
652#define VCC_MUX_OFFSET 0x4
653#define VCC_DIV_OFFSET 0x8
654
655struct clkgen_vcc_data {
656 spinlock_t *lock;
657 unsigned long clk_flags;
658};
659
660static struct clkgen_vcc_data st_clkgenc_vcc_416 = {
661 .clk_flags = CLK_SET_RATE_PARENT,
662};
663
664static struct clkgen_vcc_data st_clkgenf_vcc_416 = {
665 .lock = &clkgenf_lock,
666};
667
668static struct of_device_id vcc_of_match[] = {
669 { .compatible = "st,stih416-clkgenc", .data = &st_clkgenc_vcc_416 },
670 { .compatible = "st,stih416-clkgenf", .data = &st_clkgenf_vcc_416 },
671 {}
672};
673
674void __init st_of_clkgen_vcc_setup(struct device_node *np)
675{
676 const struct of_device_id *match;
677 void __iomem *reg;
678 const char **parents;
679 int num_parents, i;
680 struct clk_onecell_data *clk_data;
681 struct clkgen_vcc_data *data;
682
683 match = of_match_node(vcc_of_match, np);
684 if (WARN_ON(!match))
685 return;
686 data = (struct clkgen_vcc_data *)match->data;
687
688 reg = of_iomap(np, 0);
689 if (!reg)
690 return;
691
692 parents = clkgen_mux_get_parents(np, &num_parents);
693 if (IS_ERR(parents))
694 return;
695
696 clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
697 if (!clk_data)
698 goto err;
699
700 clk_data->clk_num = VCC_MAX_CHANNELS;
701 clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
702 GFP_KERNEL);
703
704 if (!clk_data->clks)
705 goto err;
706
707 for (i = 0; i < clk_data->clk_num; i++) {
708 struct clk *clk;
709 const char *clk_name;
710 struct clk_gate *gate;
711 struct clk_divider *div;
712 struct clk_mux *mux;
713
714 if (of_property_read_string_index(np, "clock-output-names",
715 i, &clk_name))
716 break;
717
718 /*
719 * If we read an empty clock name then the output is unused
720 */
721 if (*clk_name == '\0')
722 continue;
723
724 gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
725 if (!gate)
726 break;
727
728 div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
729 if (!div) {
730 kfree(gate);
731 break;
732 }
733
734 mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
735 if (!mux) {
736 kfree(gate);
737 kfree(div);
738 break;
739 }
740
741 gate->reg = reg + VCC_GATE_OFFSET;
742 gate->bit_idx = i;
743 gate->flags = CLK_GATE_SET_TO_DISABLE;
744 gate->lock = data->lock;
745
746 div->reg = reg + VCC_DIV_OFFSET;
747 div->shift = 2 * i;
748 div->width = 2;
749 div->flags = CLK_DIVIDER_POWER_OF_TWO;
750
751 mux->reg = reg + VCC_MUX_OFFSET;
752 mux->shift = 2 * i;
753 mux->mask = 0x3;
754
755 clk = clk_register_composite(NULL, clk_name, parents,
756 num_parents,
757 &mux->hw, &clk_mux_ops,
758 &div->hw, &clk_divider_ops,
759 &gate->hw, &clk_gate_ops,
760 data->clk_flags);
761 if (IS_ERR(clk)) {
762 kfree(gate);
763 kfree(div);
764 kfree(mux);
765 goto err;
766 }
767
768 pr_debug("%s: parent %s rate %u\n",
769 __clk_get_name(clk),
770 __clk_get_name(clk_get_parent(clk)),
771 (unsigned int)clk_get_rate(clk));
772
773 clk_data->clks[i] = clk;
774 }
775
776 kfree(parents);
777
778 of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
779 return;
780
781err:
782 for (i = 0; i < clk_data->clk_num; i++) {
783 struct clk_composite *composite;
784
785 if (!clk_data->clks[i])
786 continue;
787
788 composite = container_of(__clk_get_hw(clk_data->clks[i]),
789 struct clk_composite, hw);
790 kfree(container_of(composite->gate_hw, struct clk_gate, hw));
791 kfree(container_of(composite->rate_hw, struct clk_divider, hw));
792 kfree(container_of(composite->mux_hw, struct clk_mux, hw));
793 }
794
795 if (clk_data)
796 kfree(clk_data->clks);
797
798 kfree(clk_data);
799 kfree(parents);
800}
801CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup);