diff options
author | Gabriel FERNANDEZ <gabriel.fernandez@st.com> | 2014-02-27 10:24:16 -0500 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2014-03-25 18:59:02 -0400 |
commit | 44993d384004fa9ca2dfcca86cddc436a28d6958 (patch) | |
tree | bbfa12964f4b40f8d9eea3f0ffb11b9940d94fb4 /drivers/clk/st | |
parent | b9b8e614b5805a99a5484c3d44fbfebaa8de4c65 (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.c | 272 |
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 | ||
20 | static DEFINE_SPINLOCK(clkgena_divmux_lock); | 20 | static DEFINE_SPINLOCK(clkgena_divmux_lock); |
21 | static DEFINE_SPINLOCK(clkgenf_lock); | ||
21 | 22 | ||
22 | static const char ** __init clkgen_mux_get_parents(struct device_node *np, | 23 | static 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 | } |
529 | CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup); | 530 | CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup); |
531 | |||
532 | struct 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 | |||
541 | static struct clkgen_mux_data clkgen_mux_c_vcc_hd_416 = { | ||
542 | .offset = 0, | ||
543 | .shift = 0, | ||
544 | .width = 1, | ||
545 | }; | ||
546 | |||
547 | static struct clkgen_mux_data clkgen_mux_f_vcc_fvdp_416 = { | ||
548 | .offset = 0, | ||
549 | .shift = 0, | ||
550 | .width = 1, | ||
551 | }; | ||
552 | |||
553 | static struct clkgen_mux_data clkgen_mux_f_vcc_hva_416 = { | ||
554 | .offset = 0, | ||
555 | .shift = 0, | ||
556 | .width = 1, | ||
557 | }; | ||
558 | |||
559 | static 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 | |||
566 | static 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 | |||
573 | static 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 | |||
597 | void __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 | |||
642 | err: | ||
643 | kfree(parents); | ||
644 | |||
645 | return; | ||
646 | } | ||
647 | CLK_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 | |||
655 | struct clkgen_vcc_data { | ||
656 | spinlock_t *lock; | ||
657 | unsigned long clk_flags; | ||
658 | }; | ||
659 | |||
660 | static struct clkgen_vcc_data st_clkgenc_vcc_416 = { | ||
661 | .clk_flags = CLK_SET_RATE_PARENT, | ||
662 | }; | ||
663 | |||
664 | static struct clkgen_vcc_data st_clkgenf_vcc_416 = { | ||
665 | .lock = &clkgenf_lock, | ||
666 | }; | ||
667 | |||
668 | static 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 | |||
674 | void __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 | |||
781 | err: | ||
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 | } | ||
801 | CLK_OF_DECLARE(clkgen_vcc, "st,clkgen-vcc", st_of_clkgen_vcc_setup); | ||