| .. | .. |
|---|
| 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); |
|---|
| 761 | 909 | init.ops = &vc5_mux_ops; |
|---|
| 762 | 910 | init.flags = 0; |
|---|
| 763 | 911 | init.parent_names = parent_names; |
|---|
| 764 | 912 | vc5->clk_mux.init = &init; |
|---|
| 765 | 913 | 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 | | - } |
|---|
| 914 | + if (ret) |
|---|
| 915 | + goto err_clk_register; |
|---|
| 916 | + kfree(init.name); /* clock framework made a copy of the name */ |
|---|
| 770 | 917 | |
|---|
| 771 | 918 | if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) { |
|---|
| 772 | 919 | /* Register frequency doubler */ |
|---|
| 773 | 920 | memset(&init, 0, sizeof(init)); |
|---|
| 774 | | - init.name = vc5_dbl_names[0]; |
|---|
| 921 | + init.name = kasprintf(GFP_KERNEL, "%pOFn.dbl", |
|---|
| 922 | + client->dev.of_node); |
|---|
| 775 | 923 | init.ops = &vc5_dbl_ops; |
|---|
| 776 | 924 | init.flags = CLK_SET_RATE_PARENT; |
|---|
| 777 | | - init.parent_names = vc5_mux_names; |
|---|
| 925 | + init.parent_names = parent_names; |
|---|
| 926 | + parent_names[0] = clk_hw_get_name(&vc5->clk_mux); |
|---|
| 778 | 927 | init.num_parents = 1; |
|---|
| 779 | 928 | vc5->clk_mul.init = &init; |
|---|
| 780 | 929 | 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 | | - } |
|---|
| 930 | + if (ret) |
|---|
| 931 | + goto err_clk_register; |
|---|
| 932 | + kfree(init.name); /* clock framework made a copy of the name */ |
|---|
| 786 | 933 | } |
|---|
| 787 | 934 | |
|---|
| 788 | 935 | /* Register PFD */ |
|---|
| 789 | 936 | memset(&init, 0, sizeof(init)); |
|---|
| 790 | | - init.name = vc5_pfd_names[0]; |
|---|
| 937 | + init.name = kasprintf(GFP_KERNEL, "%pOFn.pfd", client->dev.of_node); |
|---|
| 791 | 938 | init.ops = &vc5_pfd_ops; |
|---|
| 792 | 939 | init.flags = CLK_SET_RATE_PARENT; |
|---|
| 940 | + init.parent_names = parent_names; |
|---|
| 793 | 941 | if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) |
|---|
| 794 | | - init.parent_names = vc5_dbl_names; |
|---|
| 942 | + parent_names[0] = clk_hw_get_name(&vc5->clk_mul); |
|---|
| 795 | 943 | else |
|---|
| 796 | | - init.parent_names = vc5_mux_names; |
|---|
| 944 | + parent_names[0] = clk_hw_get_name(&vc5->clk_mux); |
|---|
| 797 | 945 | init.num_parents = 1; |
|---|
| 798 | 946 | vc5->clk_pfd.init = &init; |
|---|
| 799 | 947 | 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 | | - } |
|---|
| 948 | + if (ret) |
|---|
| 949 | + goto err_clk_register; |
|---|
| 950 | + kfree(init.name); /* clock framework made a copy of the name */ |
|---|
| 804 | 951 | |
|---|
| 805 | 952 | /* Register PLL */ |
|---|
| 806 | 953 | memset(&init, 0, sizeof(init)); |
|---|
| 807 | | - init.name = vc5_pll_names[0]; |
|---|
| 954 | + init.name = kasprintf(GFP_KERNEL, "%pOFn.pll", client->dev.of_node); |
|---|
| 808 | 955 | init.ops = &vc5_pll_ops; |
|---|
| 809 | 956 | init.flags = CLK_SET_RATE_PARENT; |
|---|
| 810 | | - init.parent_names = vc5_pfd_names; |
|---|
| 957 | + init.parent_names = parent_names; |
|---|
| 958 | + parent_names[0] = clk_hw_get_name(&vc5->clk_pfd); |
|---|
| 811 | 959 | init.num_parents = 1; |
|---|
| 812 | 960 | vc5->clk_pll.num = 0; |
|---|
| 813 | 961 | vc5->clk_pll.vc5 = vc5; |
|---|
| 814 | 962 | vc5->clk_pll.hw.init = &init; |
|---|
| 815 | 963 | 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 | | - } |
|---|
| 964 | + if (ret) |
|---|
| 965 | + goto err_clk_register; |
|---|
| 966 | + kfree(init.name); /* clock framework made a copy of the name */ |
|---|
| 820 | 967 | |
|---|
| 821 | 968 | /* Register FODs */ |
|---|
| 822 | 969 | for (n = 0; n < vc5->chip_info->clk_fod_cnt; n++) { |
|---|
| 823 | 970 | idx = vc5_map_index_to_output(vc5->chip_info->model, n); |
|---|
| 824 | 971 | memset(&init, 0, sizeof(init)); |
|---|
| 825 | | - init.name = vc5_fod_names[idx]; |
|---|
| 972 | + init.name = kasprintf(GFP_KERNEL, "%pOFn.fod%d", |
|---|
| 973 | + client->dev.of_node, idx); |
|---|
| 826 | 974 | init.ops = &vc5_fod_ops; |
|---|
| 827 | 975 | init.flags = CLK_SET_RATE_PARENT; |
|---|
| 828 | | - init.parent_names = vc5_pll_names; |
|---|
| 976 | + init.parent_names = parent_names; |
|---|
| 977 | + parent_names[0] = clk_hw_get_name(&vc5->clk_pll.hw); |
|---|
| 829 | 978 | init.num_parents = 1; |
|---|
| 830 | 979 | vc5->clk_fod[n].num = idx; |
|---|
| 831 | 980 | vc5->clk_fod[n].vc5 = vc5; |
|---|
| 832 | 981 | vc5->clk_fod[n].hw.init = &init; |
|---|
| 833 | 982 | 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 | | - } |
|---|
| 983 | + if (ret) |
|---|
| 984 | + goto err_clk_register; |
|---|
| 985 | + kfree(init.name); /* clock framework made a copy of the name */ |
|---|
| 839 | 986 | } |
|---|
| 840 | 987 | |
|---|
| 841 | 988 | /* Register MUX-connected OUT0_I2C_SELB output */ |
|---|
| 842 | 989 | memset(&init, 0, sizeof(init)); |
|---|
| 843 | | - init.name = vc5_clk_out_names[0]; |
|---|
| 990 | + init.name = kasprintf(GFP_KERNEL, "%pOFn.out0_sel_i2cb", |
|---|
| 991 | + client->dev.of_node); |
|---|
| 844 | 992 | init.ops = &vc5_clk_out_ops; |
|---|
| 845 | 993 | init.flags = CLK_SET_RATE_PARENT; |
|---|
| 846 | | - init.parent_names = vc5_mux_names; |
|---|
| 994 | + init.parent_names = parent_names; |
|---|
| 995 | + parent_names[0] = clk_hw_get_name(&vc5->clk_mux); |
|---|
| 847 | 996 | init.num_parents = 1; |
|---|
| 848 | 997 | vc5->clk_out[0].num = idx; |
|---|
| 849 | 998 | vc5->clk_out[0].vc5 = vc5; |
|---|
| 850 | 999 | vc5->clk_out[0].hw.init = &init; |
|---|
| 851 | 1000 | 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 | | - } |
|---|
| 1001 | + if (ret) |
|---|
| 1002 | + goto err_clk_register; |
|---|
| 1003 | + kfree(init.name); /* clock framework made a copy of the name */ |
|---|
| 857 | 1004 | |
|---|
| 858 | 1005 | /* Register FOD-connected OUTx outputs */ |
|---|
| 859 | 1006 | for (n = 1; n < vc5->chip_info->clk_out_cnt; n++) { |
|---|
| 860 | 1007 | idx = vc5_map_index_to_output(vc5->chip_info->model, n - 1); |
|---|
| 861 | | - parent_names[0] = vc5_fod_names[idx]; |
|---|
| 1008 | + parent_names[0] = clk_hw_get_name(&vc5->clk_fod[idx].hw); |
|---|
| 862 | 1009 | if (n == 1) |
|---|
| 863 | | - parent_names[1] = vc5_mux_names[0]; |
|---|
| 1010 | + parent_names[1] = clk_hw_get_name(&vc5->clk_mux); |
|---|
| 864 | 1011 | else |
|---|
| 865 | | - parent_names[1] = vc5_clk_out_names[n - 1]; |
|---|
| 1012 | + parent_names[1] = |
|---|
| 1013 | + clk_hw_get_name(&vc5->clk_out[n - 1].hw); |
|---|
| 866 | 1014 | |
|---|
| 867 | 1015 | memset(&init, 0, sizeof(init)); |
|---|
| 868 | | - init.name = vc5_clk_out_names[idx + 1]; |
|---|
| 1016 | + init.name = kasprintf(GFP_KERNEL, "%pOFn.out%d", |
|---|
| 1017 | + client->dev.of_node, idx + 1); |
|---|
| 869 | 1018 | init.ops = &vc5_clk_out_ops; |
|---|
| 870 | 1019 | init.flags = CLK_SET_RATE_PARENT; |
|---|
| 871 | 1020 | init.parent_names = parent_names; |
|---|
| .. | .. |
|---|
| 873 | 1022 | vc5->clk_out[n].num = idx; |
|---|
| 874 | 1023 | vc5->clk_out[n].vc5 = vc5; |
|---|
| 875 | 1024 | 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); |
|---|
| 1025 | + ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[n].hw); |
|---|
| 1026 | + if (ret) |
|---|
| 1027 | + goto err_clk_register; |
|---|
| 1028 | + kfree(init.name); /* clock framework made a copy of the name */ |
|---|
| 1029 | + |
|---|
| 1030 | + /* Fetch Clock Output configuration from DT (if specified) */ |
|---|
| 1031 | + ret = vc5_get_output_config(client, &vc5->clk_out[n]); |
|---|
| 1032 | + if (ret) |
|---|
| 881 | 1033 | goto err_clk; |
|---|
| 882 | | - } |
|---|
| 883 | 1034 | } |
|---|
| 884 | 1035 | |
|---|
| 885 | 1036 | ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5); |
|---|
| .. | .. |
|---|
| 890 | 1041 | |
|---|
| 891 | 1042 | return 0; |
|---|
| 892 | 1043 | |
|---|
| 1044 | +err_clk_register: |
|---|
| 1045 | + dev_err(&client->dev, "unable to register %s\n", init.name); |
|---|
| 1046 | + kfree(init.name); /* clock framework made a copy of the name */ |
|---|
| 893 | 1047 | err_clk: |
|---|
| 894 | 1048 | if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL) |
|---|
| 895 | 1049 | clk_unregister_fixed_rate(vc5->pin_xin); |
|---|
| .. | .. |
|---|
| 906 | 1060 | clk_unregister_fixed_rate(vc5->pin_xin); |
|---|
| 907 | 1061 | |
|---|
| 908 | 1062 | return 0; |
|---|
| 1063 | +} |
|---|
| 1064 | + |
|---|
| 1065 | +static int __maybe_unused vc5_suspend(struct device *dev) |
|---|
| 1066 | +{ |
|---|
| 1067 | + struct vc5_driver_data *vc5 = dev_get_drvdata(dev); |
|---|
| 1068 | + |
|---|
| 1069 | + regcache_cache_only(vc5->regmap, true); |
|---|
| 1070 | + regcache_mark_dirty(vc5->regmap); |
|---|
| 1071 | + |
|---|
| 1072 | + return 0; |
|---|
| 1073 | +} |
|---|
| 1074 | + |
|---|
| 1075 | +static int __maybe_unused vc5_resume(struct device *dev) |
|---|
| 1076 | +{ |
|---|
| 1077 | + struct vc5_driver_data *vc5 = dev_get_drvdata(dev); |
|---|
| 1078 | + int ret; |
|---|
| 1079 | + |
|---|
| 1080 | + regcache_cache_only(vc5->regmap, false); |
|---|
| 1081 | + ret = regcache_sync(vc5->regmap); |
|---|
| 1082 | + if (ret) |
|---|
| 1083 | + dev_err(dev, "Failed to restore register map: %d\n", ret); |
|---|
| 1084 | + return ret; |
|---|
| 909 | 1085 | } |
|---|
| 910 | 1086 | |
|---|
| 911 | 1087 | static const struct vc5_chip_info idt_5p49v5923_info = { |
|---|
| .. | .. |
|---|
| 940 | 1116 | .model = IDT_VC6_5P49V6901, |
|---|
| 941 | 1117 | .clk_fod_cnt = 4, |
|---|
| 942 | 1118 | .clk_out_cnt = 5, |
|---|
| 943 | | - .flags = VC5_HAS_PFD_FREQ_DBL, |
|---|
| 1119 | + .flags = VC5_HAS_PFD_FREQ_DBL | VC5_HAS_BYPASS_SYNC_BIT, |
|---|
| 1120 | +}; |
|---|
| 1121 | + |
|---|
| 1122 | +static const struct vc5_chip_info idt_5p49v6965_info = { |
|---|
| 1123 | + .model = IDT_VC6_5P49V6965, |
|---|
| 1124 | + .clk_fod_cnt = 4, |
|---|
| 1125 | + .clk_out_cnt = 5, |
|---|
| 1126 | + .flags = VC5_HAS_BYPASS_SYNC_BIT, |
|---|
| 944 | 1127 | }; |
|---|
| 945 | 1128 | |
|---|
| 946 | 1129 | static const struct i2c_device_id vc5_id[] = { |
|---|
| .. | .. |
|---|
| 949 | 1132 | { "5p49v5933", .driver_data = IDT_VC5_5P49V5933 }, |
|---|
| 950 | 1133 | { "5p49v5935", .driver_data = IDT_VC5_5P49V5935 }, |
|---|
| 951 | 1134 | { "5p49v6901", .driver_data = IDT_VC6_5P49V6901 }, |
|---|
| 1135 | + { "5p49v6965", .driver_data = IDT_VC6_5P49V6965 }, |
|---|
| 952 | 1136 | { } |
|---|
| 953 | 1137 | }; |
|---|
| 954 | 1138 | MODULE_DEVICE_TABLE(i2c, vc5_id); |
|---|
| .. | .. |
|---|
| 959 | 1143 | { .compatible = "idt,5p49v5933", .data = &idt_5p49v5933_info }, |
|---|
| 960 | 1144 | { .compatible = "idt,5p49v5935", .data = &idt_5p49v5935_info }, |
|---|
| 961 | 1145 | { .compatible = "idt,5p49v6901", .data = &idt_5p49v6901_info }, |
|---|
| 1146 | + { .compatible = "idt,5p49v6965", .data = &idt_5p49v6965_info }, |
|---|
| 962 | 1147 | { }, |
|---|
| 963 | 1148 | }; |
|---|
| 964 | 1149 | MODULE_DEVICE_TABLE(of, clk_vc5_of_match); |
|---|
| 965 | 1150 | |
|---|
| 1151 | +static SIMPLE_DEV_PM_OPS(vc5_pm_ops, vc5_suspend, vc5_resume); |
|---|
| 1152 | + |
|---|
| 966 | 1153 | static struct i2c_driver vc5_driver = { |
|---|
| 967 | 1154 | .driver = { |
|---|
| 968 | 1155 | .name = "vc5", |
|---|
| 1156 | + .pm = &vc5_pm_ops, |
|---|
| 969 | 1157 | .of_match_table = clk_vc5_of_match, |
|---|
| 970 | 1158 | }, |
|---|
| 971 | 1159 | .probe = vc5_probe, |
|---|