.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Driver for IDT Versaclock 5 |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com> |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify |
---|
7 | | - * it under the terms of the GNU General Public License as published by |
---|
8 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
9 | | - * (at your option) any later version. |
---|
10 | | - * |
---|
11 | | - * This program is distributed in the hope that it will be useful, |
---|
12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
14 | | - * GNU General Public License for more details. |
---|
15 | 6 | */ |
---|
16 | 7 | |
---|
17 | 8 | /* |
---|
.. | .. |
---|
32 | 23 | #include <linux/rational.h> |
---|
33 | 24 | #include <linux/regmap.h> |
---|
34 | 25 | #include <linux/slab.h> |
---|
| 26 | + |
---|
| 27 | +#include <dt-bindings/clk/versaclock.h> |
---|
35 | 28 | |
---|
36 | 29 | /* VersaClock5 registers */ |
---|
37 | 30 | #define VC5_OTP_CONTROL 0x00 |
---|
.. | .. |
---|
76 | 69 | #define VC5_FEEDBACK_FRAC_DIV(n) (0x19 + (n)) |
---|
77 | 70 | #define VC5_RC_CONTROL0 0x1e |
---|
78 | 71 | #define VC5_RC_CONTROL1 0x1f |
---|
79 | | -/* Register 0x20 is factory reserved */ |
---|
| 72 | + |
---|
| 73 | +/* These registers are named "Unused Factory Reserved Registers" */ |
---|
| 74 | +#define VC5_RESERVED_X0(idx) (0x20 + ((idx) * 0x10)) |
---|
| 75 | +#define VC5_RESERVED_X0_BYPASS_SYNC BIT(7) /* bypass_sync<idx> bit */ |
---|
80 | 76 | |
---|
81 | 77 | /* Output divider control for divider 1,2,3,4 */ |
---|
82 | 78 | #define VC5_OUT_DIV_CONTROL(idx) (0x21 + ((idx) * 0x10)) |
---|
.. | .. |
---|
94 | 90 | #define VC5_OUT_DIV_SKEW_INT(idx, n) (0x2b + ((idx) * 0x10) + (n)) |
---|
95 | 91 | #define VC5_OUT_DIV_INT(idx, n) (0x2d + ((idx) * 0x10) + (n)) |
---|
96 | 92 | #define VC5_OUT_DIV_SKEW_FRAC(idx) (0x2f + ((idx) * 0x10)) |
---|
97 | | -/* Registers 0x30, 0x40, 0x50 are factory reserved */ |
---|
98 | 93 | |
---|
99 | 94 | /* Clock control register for clock 1,2 */ |
---|
100 | 95 | #define VC5_CLK_OUTPUT_CFG(idx, n) (0x60 + ((idx) * 0x2) + (n)) |
---|
| 96 | +#define VC5_CLK_OUTPUT_CFG0_CFG_SHIFT 5 |
---|
| 97 | +#define VC5_CLK_OUTPUT_CFG0_CFG_MASK GENMASK(7, VC5_CLK_OUTPUT_CFG0_CFG_SHIFT) |
---|
| 98 | + |
---|
| 99 | +#define VC5_CLK_OUTPUT_CFG0_CFG_LVPECL (VC5_LVPECL) |
---|
| 100 | +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS (VC5_CMOS) |
---|
| 101 | +#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL33 (VC5_HCSL33) |
---|
| 102 | +#define VC5_CLK_OUTPUT_CFG0_CFG_LVDS (VC5_LVDS) |
---|
| 103 | +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS2 (VC5_CMOS2) |
---|
| 104 | +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOSD (VC5_CMOSD) |
---|
| 105 | +#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL25 (VC5_HCSL25) |
---|
| 106 | + |
---|
| 107 | +#define VC5_CLK_OUTPUT_CFG0_PWR_SHIFT 3 |
---|
| 108 | +#define VC5_CLK_OUTPUT_CFG0_PWR_MASK GENMASK(4, VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) |
---|
| 109 | +#define VC5_CLK_OUTPUT_CFG0_PWR_18 (0<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) |
---|
| 110 | +#define VC5_CLK_OUTPUT_CFG0_PWR_25 (2<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) |
---|
| 111 | +#define VC5_CLK_OUTPUT_CFG0_PWR_33 (3<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) |
---|
| 112 | +#define VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT 0 |
---|
| 113 | +#define VC5_CLK_OUTPUT_CFG0_SLEW_MASK GENMASK(1, VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) |
---|
| 114 | +#define VC5_CLK_OUTPUT_CFG0_SLEW_80 (0<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) |
---|
| 115 | +#define VC5_CLK_OUTPUT_CFG0_SLEW_85 (1<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) |
---|
| 116 | +#define VC5_CLK_OUTPUT_CFG0_SLEW_90 (2<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) |
---|
| 117 | +#define VC5_CLK_OUTPUT_CFG0_SLEW_100 (3<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) |
---|
101 | 118 | #define VC5_CLK_OUTPUT_CFG1_EN_CLKBUF BIT(0) |
---|
102 | 119 | |
---|
103 | 120 | #define VC5_CLK_OE_SHDN 0x68 |
---|
.. | .. |
---|
125 | 142 | #define VC5_HAS_INTERNAL_XTAL BIT(0) |
---|
126 | 143 | /* chip has PFD requency doubler */ |
---|
127 | 144 | #define VC5_HAS_PFD_FREQ_DBL BIT(1) |
---|
| 145 | +/* chip has bits to disable FOD sync */ |
---|
| 146 | +#define VC5_HAS_BYPASS_SYNC_BIT BIT(2) |
---|
128 | 147 | |
---|
129 | 148 | /* Supported IDT VC5 models. */ |
---|
130 | 149 | enum vc5_model { |
---|
.. | .. |
---|
133 | 152 | IDT_VC5_5P49V5933, |
---|
134 | 153 | IDT_VC5_5P49V5935, |
---|
135 | 154 | IDT_VC6_5P49V6901, |
---|
| 155 | + IDT_VC6_5P49V6965, |
---|
136 | 156 | }; |
---|
137 | 157 | |
---|
138 | 158 | /* Structure to describe features of a particular VC5 model */ |
---|
.. | .. |
---|
153 | 173 | unsigned int num; |
---|
154 | 174 | }; |
---|
155 | 175 | |
---|
| 176 | +struct vc5_out_data { |
---|
| 177 | + struct clk_hw hw; |
---|
| 178 | + struct vc5_driver_data *vc5; |
---|
| 179 | + unsigned int num; |
---|
| 180 | + unsigned int clk_output_cfg0; |
---|
| 181 | + unsigned int clk_output_cfg0_mask; |
---|
| 182 | +}; |
---|
| 183 | + |
---|
156 | 184 | struct vc5_driver_data { |
---|
157 | 185 | struct i2c_client *client; |
---|
158 | 186 | struct regmap *regmap; |
---|
.. | .. |
---|
166 | 194 | struct clk_hw clk_pfd; |
---|
167 | 195 | struct vc5_hw_data clk_pll; |
---|
168 | 196 | struct vc5_hw_data clk_fod[VC5_MAX_FOD_NUM]; |
---|
169 | | - struct vc5_hw_data clk_out[VC5_MAX_CLK_OUT_NUM]; |
---|
170 | | -}; |
---|
171 | | - |
---|
172 | | -static const char * const vc5_mux_names[] = { |
---|
173 | | - "mux" |
---|
174 | | -}; |
---|
175 | | - |
---|
176 | | -static const char * const vc5_dbl_names[] = { |
---|
177 | | - "dbl" |
---|
178 | | -}; |
---|
179 | | - |
---|
180 | | -static const char * const vc5_pfd_names[] = { |
---|
181 | | - "pfd" |
---|
182 | | -}; |
---|
183 | | - |
---|
184 | | -static const char * const vc5_pll_names[] = { |
---|
185 | | - "pll" |
---|
186 | | -}; |
---|
187 | | - |
---|
188 | | -static const char * const vc5_fod_names[] = { |
---|
189 | | - "fod0", "fod1", "fod2", "fod3", |
---|
190 | | -}; |
---|
191 | | - |
---|
192 | | -static const char * const vc5_clk_out_names[] = { |
---|
193 | | - "out0_sel_i2cb", "out1", "out2", "out3", "out4", |
---|
| 197 | + struct vc5_out_data clk_out[VC5_MAX_CLK_OUT_NUM]; |
---|
194 | 198 | }; |
---|
195 | 199 | |
---|
196 | 200 | /* |
---|
.. | .. |
---|
573 | 577 | |
---|
574 | 578 | static int vc5_clk_out_prepare(struct clk_hw *hw) |
---|
575 | 579 | { |
---|
576 | | - struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); |
---|
| 580 | + struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw); |
---|
577 | 581 | struct vc5_driver_data *vc5 = hwdata->vc5; |
---|
578 | 582 | const u8 mask = VC5_OUT_DIV_CONTROL_SELB_NORM | |
---|
579 | 583 | VC5_OUT_DIV_CONTROL_SEL_EXT | |
---|
580 | 584 | VC5_OUT_DIV_CONTROL_EN_FOD; |
---|
581 | 585 | unsigned int src; |
---|
582 | 586 | int ret; |
---|
| 587 | + |
---|
| 588 | + /* |
---|
| 589 | + * When enabling a FOD, all currently enabled FODs are briefly |
---|
| 590 | + * stopped in order to synchronize all of them. This causes a clock |
---|
| 591 | + * disruption to any unrelated chips that might be already using |
---|
| 592 | + * other clock outputs. Bypass the sync feature to avoid the issue, |
---|
| 593 | + * which is possible on the VersaClock 6E family via reserved |
---|
| 594 | + * registers. |
---|
| 595 | + */ |
---|
| 596 | + if (vc5->chip_info->flags & VC5_HAS_BYPASS_SYNC_BIT) { |
---|
| 597 | + ret = regmap_update_bits(vc5->regmap, |
---|
| 598 | + VC5_RESERVED_X0(hwdata->num), |
---|
| 599 | + VC5_RESERVED_X0_BYPASS_SYNC, |
---|
| 600 | + VC5_RESERVED_X0_BYPASS_SYNC); |
---|
| 601 | + if (ret) |
---|
| 602 | + return ret; |
---|
| 603 | + } |
---|
583 | 604 | |
---|
584 | 605 | /* |
---|
585 | 606 | * If the input mux is disabled, enable it first and |
---|
.. | .. |
---|
599 | 620 | regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), |
---|
600 | 621 | VC5_CLK_OUTPUT_CFG1_EN_CLKBUF, |
---|
601 | 622 | VC5_CLK_OUTPUT_CFG1_EN_CLKBUF); |
---|
| 623 | + if (hwdata->clk_output_cfg0_mask) { |
---|
| 624 | + dev_dbg(&vc5->client->dev, "Update output %d mask 0x%0X val 0x%0X\n", |
---|
| 625 | + hwdata->num, hwdata->clk_output_cfg0_mask, |
---|
| 626 | + hwdata->clk_output_cfg0); |
---|
| 627 | + |
---|
| 628 | + regmap_update_bits(vc5->regmap, |
---|
| 629 | + VC5_CLK_OUTPUT_CFG(hwdata->num, 0), |
---|
| 630 | + hwdata->clk_output_cfg0_mask, |
---|
| 631 | + hwdata->clk_output_cfg0); |
---|
| 632 | + } |
---|
| 633 | + |
---|
602 | 634 | return 0; |
---|
603 | 635 | } |
---|
604 | 636 | |
---|
605 | 637 | static void vc5_clk_out_unprepare(struct clk_hw *hw) |
---|
606 | 638 | { |
---|
607 | | - struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); |
---|
| 639 | + struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw); |
---|
608 | 640 | struct vc5_driver_data *vc5 = hwdata->vc5; |
---|
609 | 641 | |
---|
610 | 642 | /* Disable the clock buffer */ |
---|
.. | .. |
---|
614 | 646 | |
---|
615 | 647 | static unsigned char vc5_clk_out_get_parent(struct clk_hw *hw) |
---|
616 | 648 | { |
---|
617 | | - struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); |
---|
| 649 | + struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw); |
---|
618 | 650 | struct vc5_driver_data *vc5 = hwdata->vc5; |
---|
619 | 651 | const u8 mask = VC5_OUT_DIV_CONTROL_SELB_NORM | |
---|
620 | 652 | VC5_OUT_DIV_CONTROL_SEL_EXT | |
---|
.. | .. |
---|
644 | 676 | |
---|
645 | 677 | static int vc5_clk_out_set_parent(struct clk_hw *hw, u8 index) |
---|
646 | 678 | { |
---|
647 | | - struct vc5_hw_data *hwdata = container_of(hw, struct vc5_hw_data, hw); |
---|
| 679 | + struct vc5_out_data *hwdata = container_of(hw, struct vc5_out_data, hw); |
---|
648 | 680 | struct vc5_driver_data *vc5 = hwdata->vc5; |
---|
649 | 681 | const u8 mask = VC5_OUT_DIV_CONTROL_RESET | |
---|
650 | 682 | VC5_OUT_DIV_CONTROL_SELB_NORM | |
---|
.. | .. |
---|
692 | 724 | case IDT_VC5_5P49V5925: |
---|
693 | 725 | case IDT_VC5_5P49V5935: |
---|
694 | 726 | case IDT_VC6_5P49V6901: |
---|
| 727 | + case IDT_VC6_5P49V6965: |
---|
695 | 728 | default: |
---|
696 | 729 | return n; |
---|
697 | 730 | } |
---|
698 | 731 | } |
---|
699 | 732 | |
---|
| 733 | +static int vc5_update_mode(struct device_node *np_output, |
---|
| 734 | + struct vc5_out_data *clk_out) |
---|
| 735 | +{ |
---|
| 736 | + u32 value; |
---|
| 737 | + |
---|
| 738 | + if (!of_property_read_u32(np_output, "idt,mode", &value)) { |
---|
| 739 | + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_CFG_MASK; |
---|
| 740 | + switch (value) { |
---|
| 741 | + case VC5_CLK_OUTPUT_CFG0_CFG_LVPECL: |
---|
| 742 | + case VC5_CLK_OUTPUT_CFG0_CFG_CMOS: |
---|
| 743 | + case VC5_CLK_OUTPUT_CFG0_CFG_HCSL33: |
---|
| 744 | + case VC5_CLK_OUTPUT_CFG0_CFG_LVDS: |
---|
| 745 | + case VC5_CLK_OUTPUT_CFG0_CFG_CMOS2: |
---|
| 746 | + case VC5_CLK_OUTPUT_CFG0_CFG_CMOSD: |
---|
| 747 | + case VC5_CLK_OUTPUT_CFG0_CFG_HCSL25: |
---|
| 748 | + clk_out->clk_output_cfg0 |= |
---|
| 749 | + value << VC5_CLK_OUTPUT_CFG0_CFG_SHIFT; |
---|
| 750 | + break; |
---|
| 751 | + default: |
---|
| 752 | + return -EINVAL; |
---|
| 753 | + } |
---|
| 754 | + } |
---|
| 755 | + return 0; |
---|
| 756 | +} |
---|
| 757 | + |
---|
| 758 | +static int vc5_update_power(struct device_node *np_output, |
---|
| 759 | + struct vc5_out_data *clk_out) |
---|
| 760 | +{ |
---|
| 761 | + u32 value; |
---|
| 762 | + |
---|
| 763 | + if (!of_property_read_u32(np_output, "idt,voltage-microvolt", |
---|
| 764 | + &value)) { |
---|
| 765 | + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_PWR_MASK; |
---|
| 766 | + switch (value) { |
---|
| 767 | + case 1800000: |
---|
| 768 | + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_18; |
---|
| 769 | + break; |
---|
| 770 | + case 2500000: |
---|
| 771 | + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_25; |
---|
| 772 | + break; |
---|
| 773 | + case 3300000: |
---|
| 774 | + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_33; |
---|
| 775 | + break; |
---|
| 776 | + default: |
---|
| 777 | + return -EINVAL; |
---|
| 778 | + } |
---|
| 779 | + } |
---|
| 780 | + return 0; |
---|
| 781 | +} |
---|
| 782 | + |
---|
| 783 | +static int vc5_update_slew(struct device_node *np_output, |
---|
| 784 | + struct vc5_out_data *clk_out) |
---|
| 785 | +{ |
---|
| 786 | + u32 value; |
---|
| 787 | + |
---|
| 788 | + if (!of_property_read_u32(np_output, "idt,slew-percent", &value)) { |
---|
| 789 | + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_SLEW_MASK; |
---|
| 790 | + switch (value) { |
---|
| 791 | + case 80: |
---|
| 792 | + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_80; |
---|
| 793 | + break; |
---|
| 794 | + case 85: |
---|
| 795 | + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_85; |
---|
| 796 | + break; |
---|
| 797 | + case 90: |
---|
| 798 | + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_90; |
---|
| 799 | + break; |
---|
| 800 | + case 100: |
---|
| 801 | + clk_out->clk_output_cfg0 |= |
---|
| 802 | + VC5_CLK_OUTPUT_CFG0_SLEW_100; |
---|
| 803 | + break; |
---|
| 804 | + default: |
---|
| 805 | + return -EINVAL; |
---|
| 806 | + } |
---|
| 807 | + } |
---|
| 808 | + return 0; |
---|
| 809 | +} |
---|
| 810 | + |
---|
| 811 | +static int vc5_get_output_config(struct i2c_client *client, |
---|
| 812 | + struct vc5_out_data *clk_out) |
---|
| 813 | +{ |
---|
| 814 | + struct device_node *np_output; |
---|
| 815 | + char *child_name; |
---|
| 816 | + int ret = 0; |
---|
| 817 | + |
---|
| 818 | + child_name = kasprintf(GFP_KERNEL, "OUT%d", clk_out->num + 1); |
---|
| 819 | + if (!child_name) |
---|
| 820 | + return -ENOMEM; |
---|
| 821 | + |
---|
| 822 | + np_output = of_get_child_by_name(client->dev.of_node, child_name); |
---|
| 823 | + kfree(child_name); |
---|
| 824 | + if (!np_output) |
---|
| 825 | + return 0; |
---|
| 826 | + |
---|
| 827 | + ret = vc5_update_mode(np_output, clk_out); |
---|
| 828 | + if (ret) |
---|
| 829 | + goto output_error; |
---|
| 830 | + |
---|
| 831 | + ret = vc5_update_power(np_output, clk_out); |
---|
| 832 | + if (ret) |
---|
| 833 | + goto output_error; |
---|
| 834 | + |
---|
| 835 | + ret = vc5_update_slew(np_output, clk_out); |
---|
| 836 | + |
---|
| 837 | +output_error: |
---|
| 838 | + if (ret) { |
---|
| 839 | + dev_err(&client->dev, |
---|
| 840 | + "Invalid clock output configuration OUT%d\n", |
---|
| 841 | + clk_out->num + 1); |
---|
| 842 | + } |
---|
| 843 | + |
---|
| 844 | + of_node_put(np_output); |
---|
| 845 | + |
---|
| 846 | + return ret; |
---|
| 847 | +} |
---|
| 848 | + |
---|
700 | 849 | static const struct of_device_id clk_vc5_of_match[]; |
---|
701 | 850 | |
---|
702 | | -static int vc5_probe(struct i2c_client *client, |
---|
703 | | - const struct i2c_device_id *id) |
---|
| 851 | +static int vc5_probe(struct i2c_client *client, const struct i2c_device_id *id) |
---|
704 | 852 | { |
---|
705 | 853 | struct vc5_driver_data *vc5; |
---|
706 | | - struct clk_init_data init = {}; |
---|
| 854 | + struct clk_init_data init; |
---|
707 | 855 | const char *parent_names[2]; |
---|
708 | 856 | unsigned int n, idx = 0; |
---|
709 | 857 | int ret; |
---|
710 | 858 | |
---|
711 | 859 | vc5 = devm_kzalloc(&client->dev, sizeof(*vc5), GFP_KERNEL); |
---|
712 | | - if (vc5 == NULL) |
---|
| 860 | + if (!vc5) |
---|
713 | 861 | return -ENOMEM; |
---|
714 | 862 | |
---|
715 | 863 | i2c_set_clientdata(client, vc5); |
---|
.. | .. |
---|
749 | 897 | if (!IS_ERR(vc5->pin_clkin)) { |
---|
750 | 898 | vc5->clk_mux_ins |= VC5_MUX_IN_CLKIN; |
---|
751 | 899 | parent_names[init.num_parents++] = |
---|
752 | | - __clk_get_name(vc5->pin_clkin); |
---|
| 900 | + __clk_get_name(vc5->pin_clkin); |
---|
753 | 901 | } |
---|
754 | 902 | |
---|
755 | 903 | if (!init.num_parents) { |
---|
.. | .. |
---|
757 | 905 | return -EINVAL; |
---|
758 | 906 | } |
---|
759 | 907 | |
---|
760 | | - init.name = vc5_mux_names[0]; |
---|
| 908 | + init.name = kasprintf(GFP_KERNEL, "%pOFn.mux", client->dev.of_node); |
---|
| 909 | + if (!init.name) { |
---|
| 910 | + ret = -ENOMEM; |
---|
| 911 | + goto err_clk; |
---|
| 912 | + } |
---|
| 913 | + |
---|
761 | 914 | init.ops = &vc5_mux_ops; |
---|
762 | 915 | init.flags = 0; |
---|
763 | 916 | init.parent_names = parent_names; |
---|
764 | 917 | vc5->clk_mux.init = &init; |
---|
765 | 918 | ret = devm_clk_hw_register(&client->dev, &vc5->clk_mux); |
---|
766 | | - if (ret) { |
---|
767 | | - dev_err(&client->dev, "unable to register %s\n", init.name); |
---|
768 | | - goto err_clk; |
---|
769 | | - } |
---|
| 919 | + if (ret) |
---|
| 920 | + goto err_clk_register; |
---|
| 921 | + kfree(init.name); /* clock framework made a copy of the name */ |
---|
770 | 922 | |
---|
771 | 923 | if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) { |
---|
772 | 924 | /* Register frequency doubler */ |
---|
773 | 925 | memset(&init, 0, sizeof(init)); |
---|
774 | | - init.name = vc5_dbl_names[0]; |
---|
| 926 | + init.name = kasprintf(GFP_KERNEL, "%pOFn.dbl", |
---|
| 927 | + client->dev.of_node); |
---|
| 928 | + if (!init.name) { |
---|
| 929 | + ret = -ENOMEM; |
---|
| 930 | + goto err_clk; |
---|
| 931 | + } |
---|
775 | 932 | init.ops = &vc5_dbl_ops; |
---|
776 | 933 | init.flags = CLK_SET_RATE_PARENT; |
---|
777 | | - init.parent_names = vc5_mux_names; |
---|
| 934 | + init.parent_names = parent_names; |
---|
| 935 | + parent_names[0] = clk_hw_get_name(&vc5->clk_mux); |
---|
778 | 936 | init.num_parents = 1; |
---|
779 | 937 | vc5->clk_mul.init = &init; |
---|
780 | 938 | ret = devm_clk_hw_register(&client->dev, &vc5->clk_mul); |
---|
781 | | - if (ret) { |
---|
782 | | - dev_err(&client->dev, "unable to register %s\n", |
---|
783 | | - init.name); |
---|
784 | | - goto err_clk; |
---|
785 | | - } |
---|
| 939 | + if (ret) |
---|
| 940 | + goto err_clk_register; |
---|
| 941 | + kfree(init.name); /* clock framework made a copy of the name */ |
---|
786 | 942 | } |
---|
787 | 943 | |
---|
788 | 944 | /* Register PFD */ |
---|
789 | 945 | memset(&init, 0, sizeof(init)); |
---|
790 | | - init.name = vc5_pfd_names[0]; |
---|
| 946 | + init.name = kasprintf(GFP_KERNEL, "%pOFn.pfd", client->dev.of_node); |
---|
| 947 | + if (!init.name) { |
---|
| 948 | + ret = -ENOMEM; |
---|
| 949 | + goto err_clk; |
---|
| 950 | + } |
---|
791 | 951 | init.ops = &vc5_pfd_ops; |
---|
792 | 952 | init.flags = CLK_SET_RATE_PARENT; |
---|
| 953 | + init.parent_names = parent_names; |
---|
793 | 954 | if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) |
---|
794 | | - init.parent_names = vc5_dbl_names; |
---|
| 955 | + parent_names[0] = clk_hw_get_name(&vc5->clk_mul); |
---|
795 | 956 | else |
---|
796 | | - init.parent_names = vc5_mux_names; |
---|
| 957 | + parent_names[0] = clk_hw_get_name(&vc5->clk_mux); |
---|
797 | 958 | init.num_parents = 1; |
---|
798 | 959 | vc5->clk_pfd.init = &init; |
---|
799 | 960 | ret = devm_clk_hw_register(&client->dev, &vc5->clk_pfd); |
---|
800 | | - if (ret) { |
---|
801 | | - dev_err(&client->dev, "unable to register %s\n", init.name); |
---|
802 | | - goto err_clk; |
---|
803 | | - } |
---|
| 961 | + if (ret) |
---|
| 962 | + goto err_clk_register; |
---|
| 963 | + kfree(init.name); /* clock framework made a copy of the name */ |
---|
804 | 964 | |
---|
805 | 965 | /* Register PLL */ |
---|
806 | 966 | memset(&init, 0, sizeof(init)); |
---|
807 | | - init.name = vc5_pll_names[0]; |
---|
| 967 | + init.name = kasprintf(GFP_KERNEL, "%pOFn.pll", client->dev.of_node); |
---|
| 968 | + if (!init.name) { |
---|
| 969 | + ret = -ENOMEM; |
---|
| 970 | + goto err_clk; |
---|
| 971 | + } |
---|
808 | 972 | init.ops = &vc5_pll_ops; |
---|
809 | 973 | init.flags = CLK_SET_RATE_PARENT; |
---|
810 | | - init.parent_names = vc5_pfd_names; |
---|
| 974 | + init.parent_names = parent_names; |
---|
| 975 | + parent_names[0] = clk_hw_get_name(&vc5->clk_pfd); |
---|
811 | 976 | init.num_parents = 1; |
---|
812 | 977 | vc5->clk_pll.num = 0; |
---|
813 | 978 | vc5->clk_pll.vc5 = vc5; |
---|
814 | 979 | vc5->clk_pll.hw.init = &init; |
---|
815 | 980 | ret = devm_clk_hw_register(&client->dev, &vc5->clk_pll.hw); |
---|
816 | | - if (ret) { |
---|
817 | | - dev_err(&client->dev, "unable to register %s\n", init.name); |
---|
818 | | - goto err_clk; |
---|
819 | | - } |
---|
| 981 | + if (ret) |
---|
| 982 | + goto err_clk_register; |
---|
| 983 | + kfree(init.name); /* clock framework made a copy of the name */ |
---|
820 | 984 | |
---|
821 | 985 | /* Register FODs */ |
---|
822 | 986 | for (n = 0; n < vc5->chip_info->clk_fod_cnt; n++) { |
---|
823 | 987 | idx = vc5_map_index_to_output(vc5->chip_info->model, n); |
---|
824 | 988 | memset(&init, 0, sizeof(init)); |
---|
825 | | - init.name = vc5_fod_names[idx]; |
---|
| 989 | + init.name = kasprintf(GFP_KERNEL, "%pOFn.fod%d", |
---|
| 990 | + client->dev.of_node, idx); |
---|
| 991 | + if (!init.name) { |
---|
| 992 | + ret = -ENOMEM; |
---|
| 993 | + goto err_clk; |
---|
| 994 | + } |
---|
826 | 995 | init.ops = &vc5_fod_ops; |
---|
827 | 996 | init.flags = CLK_SET_RATE_PARENT; |
---|
828 | | - init.parent_names = vc5_pll_names; |
---|
| 997 | + init.parent_names = parent_names; |
---|
| 998 | + parent_names[0] = clk_hw_get_name(&vc5->clk_pll.hw); |
---|
829 | 999 | init.num_parents = 1; |
---|
830 | 1000 | vc5->clk_fod[n].num = idx; |
---|
831 | 1001 | vc5->clk_fod[n].vc5 = vc5; |
---|
832 | 1002 | vc5->clk_fod[n].hw.init = &init; |
---|
833 | 1003 | ret = devm_clk_hw_register(&client->dev, &vc5->clk_fod[n].hw); |
---|
834 | | - if (ret) { |
---|
835 | | - dev_err(&client->dev, "unable to register %s\n", |
---|
836 | | - init.name); |
---|
837 | | - goto err_clk; |
---|
838 | | - } |
---|
| 1004 | + if (ret) |
---|
| 1005 | + goto err_clk_register; |
---|
| 1006 | + kfree(init.name); /* clock framework made a copy of the name */ |
---|
839 | 1007 | } |
---|
840 | 1008 | |
---|
841 | 1009 | /* Register MUX-connected OUT0_I2C_SELB output */ |
---|
842 | 1010 | memset(&init, 0, sizeof(init)); |
---|
843 | | - init.name = vc5_clk_out_names[0]; |
---|
| 1011 | + init.name = kasprintf(GFP_KERNEL, "%pOFn.out0_sel_i2cb", |
---|
| 1012 | + client->dev.of_node); |
---|
| 1013 | + if (!init.name) { |
---|
| 1014 | + ret = -ENOMEM; |
---|
| 1015 | + goto err_clk; |
---|
| 1016 | + } |
---|
844 | 1017 | init.ops = &vc5_clk_out_ops; |
---|
845 | 1018 | init.flags = CLK_SET_RATE_PARENT; |
---|
846 | | - init.parent_names = vc5_mux_names; |
---|
| 1019 | + init.parent_names = parent_names; |
---|
| 1020 | + parent_names[0] = clk_hw_get_name(&vc5->clk_mux); |
---|
847 | 1021 | init.num_parents = 1; |
---|
848 | 1022 | vc5->clk_out[0].num = idx; |
---|
849 | 1023 | vc5->clk_out[0].vc5 = vc5; |
---|
850 | 1024 | vc5->clk_out[0].hw.init = &init; |
---|
851 | 1025 | ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[0].hw); |
---|
852 | | - if (ret) { |
---|
853 | | - dev_err(&client->dev, "unable to register %s\n", |
---|
854 | | - init.name); |
---|
855 | | - goto err_clk; |
---|
856 | | - } |
---|
| 1026 | + if (ret) |
---|
| 1027 | + goto err_clk_register; |
---|
| 1028 | + kfree(init.name); /* clock framework made a copy of the name */ |
---|
857 | 1029 | |
---|
858 | 1030 | /* Register FOD-connected OUTx outputs */ |
---|
859 | 1031 | for (n = 1; n < vc5->chip_info->clk_out_cnt; n++) { |
---|
860 | 1032 | idx = vc5_map_index_to_output(vc5->chip_info->model, n - 1); |
---|
861 | | - parent_names[0] = vc5_fod_names[idx]; |
---|
| 1033 | + parent_names[0] = clk_hw_get_name(&vc5->clk_fod[idx].hw); |
---|
862 | 1034 | if (n == 1) |
---|
863 | | - parent_names[1] = vc5_mux_names[0]; |
---|
| 1035 | + parent_names[1] = clk_hw_get_name(&vc5->clk_mux); |
---|
864 | 1036 | else |
---|
865 | | - parent_names[1] = vc5_clk_out_names[n - 1]; |
---|
| 1037 | + parent_names[1] = |
---|
| 1038 | + clk_hw_get_name(&vc5->clk_out[n - 1].hw); |
---|
866 | 1039 | |
---|
867 | 1040 | memset(&init, 0, sizeof(init)); |
---|
868 | | - init.name = vc5_clk_out_names[idx + 1]; |
---|
| 1041 | + init.name = kasprintf(GFP_KERNEL, "%pOFn.out%d", |
---|
| 1042 | + client->dev.of_node, idx + 1); |
---|
| 1043 | + if (!init.name) { |
---|
| 1044 | + ret = -ENOMEM; |
---|
| 1045 | + goto err_clk; |
---|
| 1046 | + } |
---|
869 | 1047 | init.ops = &vc5_clk_out_ops; |
---|
870 | 1048 | init.flags = CLK_SET_RATE_PARENT; |
---|
871 | 1049 | init.parent_names = parent_names; |
---|
.. | .. |
---|
873 | 1051 | vc5->clk_out[n].num = idx; |
---|
874 | 1052 | vc5->clk_out[n].vc5 = vc5; |
---|
875 | 1053 | vc5->clk_out[n].hw.init = &init; |
---|
876 | | - ret = devm_clk_hw_register(&client->dev, |
---|
877 | | - &vc5->clk_out[n].hw); |
---|
878 | | - if (ret) { |
---|
879 | | - dev_err(&client->dev, "unable to register %s\n", |
---|
880 | | - init.name); |
---|
| 1054 | + ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[n].hw); |
---|
| 1055 | + if (ret) |
---|
| 1056 | + goto err_clk_register; |
---|
| 1057 | + kfree(init.name); /* clock framework made a copy of the name */ |
---|
| 1058 | + |
---|
| 1059 | + /* Fetch Clock Output configuration from DT (if specified) */ |
---|
| 1060 | + ret = vc5_get_output_config(client, &vc5->clk_out[n]); |
---|
| 1061 | + if (ret) |
---|
881 | 1062 | goto err_clk; |
---|
882 | | - } |
---|
883 | 1063 | } |
---|
884 | 1064 | |
---|
885 | 1065 | ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5); |
---|
.. | .. |
---|
890 | 1070 | |
---|
891 | 1071 | return 0; |
---|
892 | 1072 | |
---|
| 1073 | +err_clk_register: |
---|
| 1074 | + dev_err(&client->dev, "unable to register %s\n", init.name); |
---|
| 1075 | + kfree(init.name); /* clock framework made a copy of the name */ |
---|
893 | 1076 | err_clk: |
---|
894 | 1077 | if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL) |
---|
895 | 1078 | clk_unregister_fixed_rate(vc5->pin_xin); |
---|
.. | .. |
---|
906 | 1089 | clk_unregister_fixed_rate(vc5->pin_xin); |
---|
907 | 1090 | |
---|
908 | 1091 | return 0; |
---|
| 1092 | +} |
---|
| 1093 | + |
---|
| 1094 | +static int __maybe_unused vc5_suspend(struct device *dev) |
---|
| 1095 | +{ |
---|
| 1096 | + struct vc5_driver_data *vc5 = dev_get_drvdata(dev); |
---|
| 1097 | + |
---|
| 1098 | + regcache_cache_only(vc5->regmap, true); |
---|
| 1099 | + regcache_mark_dirty(vc5->regmap); |
---|
| 1100 | + |
---|
| 1101 | + return 0; |
---|
| 1102 | +} |
---|
| 1103 | + |
---|
| 1104 | +static int __maybe_unused vc5_resume(struct device *dev) |
---|
| 1105 | +{ |
---|
| 1106 | + struct vc5_driver_data *vc5 = dev_get_drvdata(dev); |
---|
| 1107 | + int ret; |
---|
| 1108 | + |
---|
| 1109 | + regcache_cache_only(vc5->regmap, false); |
---|
| 1110 | + ret = regcache_sync(vc5->regmap); |
---|
| 1111 | + if (ret) |
---|
| 1112 | + dev_err(dev, "Failed to restore register map: %d\n", ret); |
---|
| 1113 | + return ret; |
---|
909 | 1114 | } |
---|
910 | 1115 | |
---|
911 | 1116 | static const struct vc5_chip_info idt_5p49v5923_info = { |
---|
.. | .. |
---|
940 | 1145 | .model = IDT_VC6_5P49V6901, |
---|
941 | 1146 | .clk_fod_cnt = 4, |
---|
942 | 1147 | .clk_out_cnt = 5, |
---|
943 | | - .flags = VC5_HAS_PFD_FREQ_DBL, |
---|
| 1148 | + .flags = VC5_HAS_PFD_FREQ_DBL | VC5_HAS_BYPASS_SYNC_BIT, |
---|
| 1149 | +}; |
---|
| 1150 | + |
---|
| 1151 | +static const struct vc5_chip_info idt_5p49v6965_info = { |
---|
| 1152 | + .model = IDT_VC6_5P49V6965, |
---|
| 1153 | + .clk_fod_cnt = 4, |
---|
| 1154 | + .clk_out_cnt = 5, |
---|
| 1155 | + .flags = VC5_HAS_BYPASS_SYNC_BIT, |
---|
944 | 1156 | }; |
---|
945 | 1157 | |
---|
946 | 1158 | static const struct i2c_device_id vc5_id[] = { |
---|
.. | .. |
---|
949 | 1161 | { "5p49v5933", .driver_data = IDT_VC5_5P49V5933 }, |
---|
950 | 1162 | { "5p49v5935", .driver_data = IDT_VC5_5P49V5935 }, |
---|
951 | 1163 | { "5p49v6901", .driver_data = IDT_VC6_5P49V6901 }, |
---|
| 1164 | + { "5p49v6965", .driver_data = IDT_VC6_5P49V6965 }, |
---|
952 | 1165 | { } |
---|
953 | 1166 | }; |
---|
954 | 1167 | MODULE_DEVICE_TABLE(i2c, vc5_id); |
---|
.. | .. |
---|
959 | 1172 | { .compatible = "idt,5p49v5933", .data = &idt_5p49v5933_info }, |
---|
960 | 1173 | { .compatible = "idt,5p49v5935", .data = &idt_5p49v5935_info }, |
---|
961 | 1174 | { .compatible = "idt,5p49v6901", .data = &idt_5p49v6901_info }, |
---|
| 1175 | + { .compatible = "idt,5p49v6965", .data = &idt_5p49v6965_info }, |
---|
962 | 1176 | { }, |
---|
963 | 1177 | }; |
---|
964 | 1178 | MODULE_DEVICE_TABLE(of, clk_vc5_of_match); |
---|
965 | 1179 | |
---|
| 1180 | +static SIMPLE_DEV_PM_OPS(vc5_pm_ops, vc5_suspend, vc5_resume); |
---|
| 1181 | + |
---|
966 | 1182 | static struct i2c_driver vc5_driver = { |
---|
967 | 1183 | .driver = { |
---|
968 | 1184 | .name = "vc5", |
---|
| 1185 | + .pm = &vc5_pm_ops, |
---|
969 | 1186 | .of_match_table = clk_vc5_of_match, |
---|
970 | 1187 | }, |
---|
971 | 1188 | .probe = vc5_probe, |
---|