|
11 | 11 | * Copyright (C) 2023 Airoha Technology Corp.
|
12 | 12 | */
|
13 | 13 |
|
| 14 | +#include <linux/clk-provider.h> |
14 | 15 | #include <linux/phy.h>
|
15 | 16 | #include <linux/firmware.h>
|
16 | 17 | #include <linux/property.h>
|
|
115 | 116 | #define EN8811H_GPIO_OUTPUT 0xcf8b8
|
116 | 117 | #define EN8811H_GPIO_OUTPUT_345 (BIT(3) | BIT(4) | BIT(5))
|
117 | 118 |
|
| 119 | +#define EN8811H_HWTRAP1 0xcf914 |
| 120 | +#define EN8811H_HWTRAP1_CKO BIT(12) |
| 121 | +#define EN8811H_CLK_CGM 0xcf958 |
| 122 | +#define EN8811H_CLK_CGM_CKO BIT(26) |
| 123 | + |
118 | 124 | #define EN8811H_FW_CTRL_1 0x0f0018
|
119 | 125 | #define EN8811H_FW_CTRL_1_START 0x0
|
120 | 126 | #define EN8811H_FW_CTRL_1_FINISH 0x1
|
@@ -142,10 +148,15 @@ struct led {
|
142 | 148 | unsigned long state;
|
143 | 149 | };
|
144 | 150 |
|
| 151 | +#define clk_hw_to_en8811h_priv(_hw) \ |
| 152 | + container_of(_hw, struct en8811h_priv, hw) |
| 153 | + |
145 | 154 | struct en8811h_priv {
|
146 |
| - u32 firmware_version; |
147 |
| - bool mcu_needs_restart; |
148 |
| - struct led led[EN8811H_LED_COUNT]; |
| 155 | + u32 firmware_version; |
| 156 | + bool mcu_needs_restart; |
| 157 | + struct led led[EN8811H_LED_COUNT]; |
| 158 | + struct clk_hw hw; |
| 159 | + struct phy_device *phydev; |
149 | 160 | };
|
150 | 161 |
|
151 | 162 | enum {
|
@@ -806,6 +817,86 @@ static int en8811h_led_hw_is_supported(struct phy_device *phydev, u8 index,
|
806 | 817 | return 0;
|
807 | 818 | };
|
808 | 819 |
|
| 820 | +static unsigned long en8811h_clk_recalc_rate(struct clk_hw *hw, |
| 821 | + unsigned long parent) |
| 822 | +{ |
| 823 | + struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw); |
| 824 | + struct phy_device *phydev = priv->phydev; |
| 825 | + u32 pbus_value; |
| 826 | + int ret; |
| 827 | + |
| 828 | + ret = air_buckpbus_reg_read(phydev, EN8811H_HWTRAP1, &pbus_value); |
| 829 | + if (ret < 0) |
| 830 | + return ret; |
| 831 | + |
| 832 | + return (pbus_value & EN8811H_HWTRAP1_CKO) ? 50000000 : 25000000; |
| 833 | +} |
| 834 | + |
| 835 | +static int en8811h_clk_enable(struct clk_hw *hw) |
| 836 | +{ |
| 837 | + struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw); |
| 838 | + struct phy_device *phydev = priv->phydev; |
| 839 | + |
| 840 | + return air_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM, |
| 841 | + EN8811H_CLK_CGM_CKO, |
| 842 | + EN8811H_CLK_CGM_CKO); |
| 843 | +} |
| 844 | + |
| 845 | +static void en8811h_clk_disable(struct clk_hw *hw) |
| 846 | +{ |
| 847 | + struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw); |
| 848 | + struct phy_device *phydev = priv->phydev; |
| 849 | + |
| 850 | + air_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM, |
| 851 | + EN8811H_CLK_CGM_CKO, 0); |
| 852 | +} |
| 853 | + |
| 854 | +static int en8811h_clk_is_enabled(struct clk_hw *hw) |
| 855 | +{ |
| 856 | + struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw); |
| 857 | + struct phy_device *phydev = priv->phydev; |
| 858 | + u32 pbus_value; |
| 859 | + int ret; |
| 860 | + |
| 861 | + ret = air_buckpbus_reg_read(phydev, EN8811H_CLK_CGM, &pbus_value); |
| 862 | + if (ret < 0) |
| 863 | + return ret; |
| 864 | + |
| 865 | + return (pbus_value & EN8811H_CLK_CGM_CKO); |
| 866 | +} |
| 867 | + |
| 868 | +static const struct clk_ops en8811h_clk_ops = { |
| 869 | + .recalc_rate = en8811h_clk_recalc_rate, |
| 870 | + .enable = en8811h_clk_enable, |
| 871 | + .disable = en8811h_clk_disable, |
| 872 | + .is_enabled = en8811h_clk_is_enabled, |
| 873 | +}; |
| 874 | + |
| 875 | +static int en8811h_clk_provider_setup(struct device *dev, struct clk_hw *hw) |
| 876 | +{ |
| 877 | + struct clk_init_data init; |
| 878 | + int ret; |
| 879 | + |
| 880 | + if (!IS_ENABLED(CONFIG_COMMON_CLK)) |
| 881 | + return 0; |
| 882 | + |
| 883 | + init.name = devm_kasprintf(dev, GFP_KERNEL, "%s-cko", |
| 884 | + fwnode_get_name(dev_fwnode(dev))); |
| 885 | + if (!init.name) |
| 886 | + return -ENOMEM; |
| 887 | + |
| 888 | + init.ops = &en8811h_clk_ops; |
| 889 | + init.flags = 0; |
| 890 | + init.num_parents = 0; |
| 891 | + hw->init = &init; |
| 892 | + |
| 893 | + ret = devm_clk_hw_register(dev, hw); |
| 894 | + if (ret) |
| 895 | + return ret; |
| 896 | + |
| 897 | + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); |
| 898 | +} |
| 899 | + |
809 | 900 | static int en8811h_probe(struct phy_device *phydev)
|
810 | 901 | {
|
811 | 902 | struct en8811h_priv *priv;
|
@@ -838,6 +929,12 @@ static int en8811h_probe(struct phy_device *phydev)
|
838 | 929 | return ret;
|
839 | 930 | }
|
840 | 931 |
|
| 932 | + priv->phydev = phydev; |
| 933 | + /* Co-Clock Output */ |
| 934 | + ret = en8811h_clk_provider_setup(&phydev->mdio.dev, &priv->hw); |
| 935 | + if (ret) |
| 936 | + return ret; |
| 937 | + |
841 | 938 | /* Configure led gpio pins as output */
|
842 | 939 | ret = air_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT,
|
843 | 940 | EN8811H_GPIO_OUTPUT_345,
|
|
0 commit comments