|
24 | 24 | #include <linux/regmap.h>
|
25 | 25 | #include <linux/slab.h>
|
26 | 26 |
|
| 27 | +#include <dt-bindings/clk/versaclock.h> |
| 28 | + |
27 | 29 | /* VersaClock5 registers */
|
28 | 30 | #define VC5_OTP_CONTROL 0x00
|
29 | 31 |
|
|
89 | 91 |
|
90 | 92 | /* Clock control register for clock 1,2 */
|
91 | 93 | #define VC5_CLK_OUTPUT_CFG(idx, n) (0x60 + ((idx) * 0x2) + (n))
|
| 94 | +#define VC5_CLK_OUTPUT_CFG0_CFG_SHIFT 5 |
| 95 | +#define VC5_CLK_OUTPUT_CFG0_CFG_MASK GENMASK(7, VC5_CLK_OUTPUT_CFG0_CFG_SHIFT) |
| 96 | + |
| 97 | +#define VC5_CLK_OUTPUT_CFG0_CFG_LVPECL (VC5_LVPECL) |
| 98 | +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS (VC5_CMOS) |
| 99 | +#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL33 (VC5_HCSL33) |
| 100 | +#define VC5_CLK_OUTPUT_CFG0_CFG_LVDS (VC5_LVDS) |
| 101 | +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOS2 (VC5_CMOS2) |
| 102 | +#define VC5_CLK_OUTPUT_CFG0_CFG_CMOSD (VC5_CMOSD) |
| 103 | +#define VC5_CLK_OUTPUT_CFG0_CFG_HCSL25 (VC5_HCSL25) |
| 104 | + |
| 105 | +#define VC5_CLK_OUTPUT_CFG0_PWR_SHIFT 3 |
| 106 | +#define VC5_CLK_OUTPUT_CFG0_PWR_MASK GENMASK(4, VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) |
| 107 | +#define VC5_CLK_OUTPUT_CFG0_PWR_18 (0<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) |
| 108 | +#define VC5_CLK_OUTPUT_CFG0_PWR_25 (2<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) |
| 109 | +#define VC5_CLK_OUTPUT_CFG0_PWR_33 (3<<VC5_CLK_OUTPUT_CFG0_PWR_SHIFT) |
| 110 | +#define VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT 0 |
| 111 | +#define VC5_CLK_OUTPUT_CFG0_SLEW_MASK GENMASK(1, VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) |
| 112 | +#define VC5_CLK_OUTPUT_CFG0_SLEW_80 (0<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) |
| 113 | +#define VC5_CLK_OUTPUT_CFG0_SLEW_85 (1<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) |
| 114 | +#define VC5_CLK_OUTPUT_CFG0_SLEW_90 (2<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) |
| 115 | +#define VC5_CLK_OUTPUT_CFG0_SLEW_100 (3<<VC5_CLK_OUTPUT_CFG0_SLEW_SHIFT) |
92 | 116 | #define VC5_CLK_OUTPUT_CFG1_EN_CLKBUF BIT(0)
|
93 | 117 |
|
94 | 118 | #define VC5_CLK_OE_SHDN 0x68
|
@@ -143,6 +167,8 @@ struct vc5_hw_data {
|
143 | 167 | u32 div_int;
|
144 | 168 | u32 div_frc;
|
145 | 169 | unsigned int num;
|
| 170 | + unsigned int clk_output_cfg0; |
| 171 | + unsigned int clk_output_cfg0_mask; |
146 | 172 | };
|
147 | 173 |
|
148 | 174 | struct vc5_driver_data {
|
@@ -567,6 +593,17 @@ static int vc5_clk_out_prepare(struct clk_hw *hw)
|
567 | 593 | regmap_update_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1),
|
568 | 594 | VC5_CLK_OUTPUT_CFG1_EN_CLKBUF,
|
569 | 595 | VC5_CLK_OUTPUT_CFG1_EN_CLKBUF);
|
| 596 | + if (hwdata->clk_output_cfg0_mask) { |
| 597 | + dev_dbg(&vc5->client->dev, "Update output %d mask 0x%0X val 0x%0X\n", |
| 598 | + hwdata->num, hwdata->clk_output_cfg0_mask, |
| 599 | + hwdata->clk_output_cfg0); |
| 600 | + |
| 601 | + regmap_update_bits(vc5->regmap, |
| 602 | + VC5_CLK_OUTPUT_CFG(hwdata->num, 0), |
| 603 | + hwdata->clk_output_cfg0_mask, |
| 604 | + hwdata->clk_output_cfg0); |
| 605 | + } |
| 606 | + |
570 | 607 | return 0;
|
571 | 608 | }
|
572 | 609 |
|
@@ -666,6 +703,120 @@ static int vc5_map_index_to_output(const enum vc5_model model,
|
666 | 703 | }
|
667 | 704 | }
|
668 | 705 |
|
| 706 | +static int vc5_update_mode(struct device_node *np_output, |
| 707 | + struct vc5_hw_data *clk_out) |
| 708 | +{ |
| 709 | + u32 value; |
| 710 | + |
| 711 | + if (!of_property_read_u32(np_output, "idt,mode", &value)) { |
| 712 | + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_CFG_MASK; |
| 713 | + switch (value) { |
| 714 | + case VC5_CLK_OUTPUT_CFG0_CFG_LVPECL: |
| 715 | + case VC5_CLK_OUTPUT_CFG0_CFG_CMOS: |
| 716 | + case VC5_CLK_OUTPUT_CFG0_CFG_HCSL33: |
| 717 | + case VC5_CLK_OUTPUT_CFG0_CFG_LVDS: |
| 718 | + case VC5_CLK_OUTPUT_CFG0_CFG_CMOS2: |
| 719 | + case VC5_CLK_OUTPUT_CFG0_CFG_CMOSD: |
| 720 | + case VC5_CLK_OUTPUT_CFG0_CFG_HCSL25: |
| 721 | + clk_out->clk_output_cfg0 |= |
| 722 | + value << VC5_CLK_OUTPUT_CFG0_CFG_SHIFT; |
| 723 | + break; |
| 724 | + default: |
| 725 | + return -EINVAL; |
| 726 | + } |
| 727 | + } |
| 728 | + return 0; |
| 729 | +} |
| 730 | + |
| 731 | +static int vc5_update_power(struct device_node *np_output, |
| 732 | + struct vc5_hw_data *clk_out) |
| 733 | +{ |
| 734 | + u32 value; |
| 735 | + |
| 736 | + if (!of_property_read_u32(np_output, |
| 737 | + "idt,voltage-microvolts", &value)) { |
| 738 | + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_PWR_MASK; |
| 739 | + switch (value) { |
| 740 | + case 1800000: |
| 741 | + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_18; |
| 742 | + break; |
| 743 | + case 2500000: |
| 744 | + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_25; |
| 745 | + break; |
| 746 | + case 3300000: |
| 747 | + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_33; |
| 748 | + break; |
| 749 | + default: |
| 750 | + return -EINVAL; |
| 751 | + } |
| 752 | + } |
| 753 | + return 0; |
| 754 | +} |
| 755 | + |
| 756 | +static int vc5_update_slew(struct device_node *np_output, |
| 757 | + struct vc5_hw_data *clk_out) |
| 758 | +{ |
| 759 | + u32 value; |
| 760 | + |
| 761 | + if (!of_property_read_u32(np_output, "idt,slew-percent", &value)) { |
| 762 | + clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_SLEW_MASK; |
| 763 | + switch (value) { |
| 764 | + case 80: |
| 765 | + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_80; |
| 766 | + break; |
| 767 | + case 85: |
| 768 | + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_85; |
| 769 | + break; |
| 770 | + case 90: |
| 771 | + clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_90; |
| 772 | + break; |
| 773 | + case 100: |
| 774 | + clk_out->clk_output_cfg0 |= |
| 775 | + VC5_CLK_OUTPUT_CFG0_SLEW_100; |
| 776 | + break; |
| 777 | + default: |
| 778 | + return -EINVAL; |
| 779 | + } |
| 780 | + } |
| 781 | + return 0; |
| 782 | +} |
| 783 | + |
| 784 | +static int vc5_get_output_config(struct i2c_client *client, |
| 785 | + struct vc5_hw_data *clk_out) |
| 786 | +{ |
| 787 | + struct device_node *np_output; |
| 788 | + char *child_name; |
| 789 | + int ret = 0; |
| 790 | + |
| 791 | + child_name = kasprintf(GFP_KERNEL, "OUT%d", clk_out->num + 1); |
| 792 | + np_output = of_get_child_by_name(client->dev.of_node, child_name); |
| 793 | + kfree(child_name); |
| 794 | + if (!np_output) |
| 795 | + goto output_done; |
| 796 | + |
| 797 | + ret = vc5_update_mode(np_output, clk_out); |
| 798 | + if (ret) |
| 799 | + goto output_error; |
| 800 | + |
| 801 | + ret = vc5_update_power(np_output, clk_out); |
| 802 | + if (ret) |
| 803 | + goto output_error; |
| 804 | + |
| 805 | + ret = vc5_update_slew(np_output, clk_out); |
| 806 | + |
| 807 | +output_error: |
| 808 | + if (ret) { |
| 809 | + dev_err(&client->dev, |
| 810 | + "Invalid clock output configuration OUT%d\n", |
| 811 | + clk_out->num + 1); |
| 812 | + } |
| 813 | + |
| 814 | + of_node_put(np_output); |
| 815 | + |
| 816 | +output_done: |
| 817 | + return ret; |
| 818 | +} |
| 819 | + |
669 | 820 | static const struct of_device_id clk_vc5_of_match[];
|
670 | 821 |
|
671 | 822 | static int vc5_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
@@ -863,6 +1014,11 @@ static int vc5_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
863 | 1014 | init.name);
|
864 | 1015 | goto err_clk;
|
865 | 1016 | }
|
| 1017 | + |
| 1018 | + /* Fetch Clock Output configuration from DT (if specified) */ |
| 1019 | + ret = vc5_get_output_config(client, &vc5->clk_out[n]); |
| 1020 | + if (ret) |
| 1021 | + goto err_clk; |
866 | 1022 | }
|
867 | 1023 |
|
868 | 1024 | ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5);
|
|
0 commit comments