Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

net: phy: air_en8811h: Add clk provider for CKO pin

EN8811H outputs 25MHz or 50MHz clocks on CKO, selected by GPIO3.
CKO clock operates continuously from power-up through md32 loading.
Implement clk provider driver so we can disable the clock output in case
it isn't needed, which also helps to reduce EMF noise

Signed-off-by: Lucien.Jheng <lucienx123@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/20250409150902.3596-1-lucienx123@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Lucien.Jheng and committed by
Jakub Kicinski
ba5560e5 faeefc17

+100 -3
+100 -3
drivers/net/phy/air_en8811h.c
··· 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> ··· 116 115 #define EN8811H_GPIO_OUTPUT 0xcf8b8 117 116 #define EN8811H_GPIO_OUTPUT_345 (BIT(3) | BIT(4) | BIT(5)) 118 117 118 + #define EN8811H_HWTRAP1 0xcf914 119 + #define EN8811H_HWTRAP1_CKO BIT(12) 120 + #define EN8811H_CLK_CGM 0xcf958 121 + #define EN8811H_CLK_CGM_CKO BIT(26) 122 + 119 123 #define EN8811H_FW_CTRL_1 0x0f0018 120 124 #define EN8811H_FW_CTRL_1_START 0x0 121 125 #define EN8811H_FW_CTRL_1_FINISH 0x1 ··· 148 142 unsigned long state; 149 143 }; 150 144 145 + #define clk_hw_to_en8811h_priv(_hw) \ 146 + container_of(_hw, struct en8811h_priv, hw) 147 + 151 148 struct en8811h_priv { 152 - u32 firmware_version; 153 - bool mcu_needs_restart; 154 - struct led led[EN8811H_LED_COUNT]; 149 + u32 firmware_version; 150 + bool mcu_needs_restart; 151 + struct led led[EN8811H_LED_COUNT]; 152 + struct clk_hw hw; 153 + struct phy_device *phydev; 155 154 }; 156 155 157 156 enum { ··· 817 806 return 0; 818 807 }; 819 808 809 + static unsigned long en8811h_clk_recalc_rate(struct clk_hw *hw, 810 + unsigned long parent) 811 + { 812 + struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw); 813 + struct phy_device *phydev = priv->phydev; 814 + u32 pbus_value; 815 + int ret; 816 + 817 + ret = air_buckpbus_reg_read(phydev, EN8811H_HWTRAP1, &pbus_value); 818 + if (ret < 0) 819 + return ret; 820 + 821 + return (pbus_value & EN8811H_HWTRAP1_CKO) ? 50000000 : 25000000; 822 + } 823 + 824 + static int en8811h_clk_enable(struct clk_hw *hw) 825 + { 826 + struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw); 827 + struct phy_device *phydev = priv->phydev; 828 + 829 + return air_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM, 830 + EN8811H_CLK_CGM_CKO, 831 + EN8811H_CLK_CGM_CKO); 832 + } 833 + 834 + static void en8811h_clk_disable(struct clk_hw *hw) 835 + { 836 + struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw); 837 + struct phy_device *phydev = priv->phydev; 838 + 839 + air_buckpbus_reg_modify(phydev, EN8811H_CLK_CGM, 840 + EN8811H_CLK_CGM_CKO, 0); 841 + } 842 + 843 + static int en8811h_clk_is_enabled(struct clk_hw *hw) 844 + { 845 + struct en8811h_priv *priv = clk_hw_to_en8811h_priv(hw); 846 + struct phy_device *phydev = priv->phydev; 847 + u32 pbus_value; 848 + int ret; 849 + 850 + ret = air_buckpbus_reg_read(phydev, EN8811H_CLK_CGM, &pbus_value); 851 + if (ret < 0) 852 + return ret; 853 + 854 + return (pbus_value & EN8811H_CLK_CGM_CKO); 855 + } 856 + 857 + static const struct clk_ops en8811h_clk_ops = { 858 + .recalc_rate = en8811h_clk_recalc_rate, 859 + .enable = en8811h_clk_enable, 860 + .disable = en8811h_clk_disable, 861 + .is_enabled = en8811h_clk_is_enabled, 862 + }; 863 + 864 + static int en8811h_clk_provider_setup(struct device *dev, struct clk_hw *hw) 865 + { 866 + struct clk_init_data init; 867 + int ret; 868 + 869 + if (!IS_ENABLED(CONFIG_COMMON_CLK)) 870 + return 0; 871 + 872 + init.name = devm_kasprintf(dev, GFP_KERNEL, "%s-cko", 873 + fwnode_get_name(dev_fwnode(dev))); 874 + if (!init.name) 875 + return -ENOMEM; 876 + 877 + init.ops = &en8811h_clk_ops; 878 + init.flags = 0; 879 + init.num_parents = 0; 880 + hw->init = &init; 881 + 882 + ret = devm_clk_hw_register(dev, hw); 883 + if (ret) 884 + return ret; 885 + 886 + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); 887 + } 888 + 820 889 static int en8811h_probe(struct phy_device *phydev) 821 890 { 822 891 struct en8811h_priv *priv; ··· 928 837 phydev_err(phydev, "Failed to disable leds: %d\n", ret); 929 838 return ret; 930 839 } 840 + 841 + priv->phydev = phydev; 842 + /* Co-Clock Output */ 843 + ret = en8811h_clk_provider_setup(&phydev->mdio.dev, &priv->hw); 844 + if (ret) 845 + return ret; 931 846 932 847 /* Configure led gpio pins as output */ 933 848 ret = air_buckpbus_reg_modify(phydev, EN8811H_GPIO_OUTPUT,