162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/delay.h> 462306a36Sopenharmony_ci#include <linux/clk-provider.h> 562306a36Sopenharmony_ci#include <linux/io.h> 662306a36Sopenharmony_ci#include <linux/of.h> 762306a36Sopenharmony_ci#include <linux/platform_device.h> 862306a36Sopenharmony_ci#include <dt-bindings/clock/en7523-clk.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define REG_PCI_CONTROL 0x88 1162306a36Sopenharmony_ci#define REG_PCI_CONTROL_PERSTOUT BIT(29) 1262306a36Sopenharmony_ci#define REG_PCI_CONTROL_PERSTOUT1 BIT(26) 1362306a36Sopenharmony_ci#define REG_PCI_CONTROL_REFCLK_EN1 BIT(22) 1462306a36Sopenharmony_ci#define REG_GSW_CLK_DIV_SEL 0x1b4 1562306a36Sopenharmony_ci#define REG_EMI_CLK_DIV_SEL 0x1b8 1662306a36Sopenharmony_ci#define REG_BUS_CLK_DIV_SEL 0x1bc 1762306a36Sopenharmony_ci#define REG_SPI_CLK_DIV_SEL 0x1c4 1862306a36Sopenharmony_ci#define REG_SPI_CLK_FREQ_SEL 0x1c8 1962306a36Sopenharmony_ci#define REG_NPU_CLK_DIV_SEL 0x1fc 2062306a36Sopenharmony_ci#define REG_CRYPTO_CLKSRC 0x200 2162306a36Sopenharmony_ci#define REG_RESET_CONTROL 0x834 2262306a36Sopenharmony_ci#define REG_RESET_CONTROL_PCIEHB BIT(29) 2362306a36Sopenharmony_ci#define REG_RESET_CONTROL_PCIE1 BIT(27) 2462306a36Sopenharmony_ci#define REG_RESET_CONTROL_PCIE2 BIT(26) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct en_clk_desc { 2762306a36Sopenharmony_ci int id; 2862306a36Sopenharmony_ci const char *name; 2962306a36Sopenharmony_ci u32 base_reg; 3062306a36Sopenharmony_ci u8 base_bits; 3162306a36Sopenharmony_ci u8 base_shift; 3262306a36Sopenharmony_ci union { 3362306a36Sopenharmony_ci const unsigned int *base_values; 3462306a36Sopenharmony_ci unsigned int base_value; 3562306a36Sopenharmony_ci }; 3662306a36Sopenharmony_ci size_t n_base_values; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci u16 div_reg; 3962306a36Sopenharmony_ci u8 div_bits; 4062306a36Sopenharmony_ci u8 div_shift; 4162306a36Sopenharmony_ci u16 div_val0; 4262306a36Sopenharmony_ci u8 div_step; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct en_clk_gate { 4662306a36Sopenharmony_ci void __iomem *base; 4762306a36Sopenharmony_ci struct clk_hw hw; 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic const u32 gsw_base[] = { 400000000, 500000000 }; 5162306a36Sopenharmony_cistatic const u32 emi_base[] = { 333000000, 400000000 }; 5262306a36Sopenharmony_cistatic const u32 bus_base[] = { 500000000, 540000000 }; 5362306a36Sopenharmony_cistatic const u32 slic_base[] = { 100000000, 3125000 }; 5462306a36Sopenharmony_cistatic const u32 npu_base[] = { 333000000, 400000000, 500000000 }; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic const struct en_clk_desc en7523_base_clks[] = { 5762306a36Sopenharmony_ci { 5862306a36Sopenharmony_ci .id = EN7523_CLK_GSW, 5962306a36Sopenharmony_ci .name = "gsw", 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci .base_reg = REG_GSW_CLK_DIV_SEL, 6262306a36Sopenharmony_ci .base_bits = 1, 6362306a36Sopenharmony_ci .base_shift = 8, 6462306a36Sopenharmony_ci .base_values = gsw_base, 6562306a36Sopenharmony_ci .n_base_values = ARRAY_SIZE(gsw_base), 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci .div_bits = 3, 6862306a36Sopenharmony_ci .div_shift = 0, 6962306a36Sopenharmony_ci .div_step = 1, 7062306a36Sopenharmony_ci }, { 7162306a36Sopenharmony_ci .id = EN7523_CLK_EMI, 7262306a36Sopenharmony_ci .name = "emi", 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci .base_reg = REG_EMI_CLK_DIV_SEL, 7562306a36Sopenharmony_ci .base_bits = 1, 7662306a36Sopenharmony_ci .base_shift = 8, 7762306a36Sopenharmony_ci .base_values = emi_base, 7862306a36Sopenharmony_ci .n_base_values = ARRAY_SIZE(emi_base), 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci .div_bits = 3, 8162306a36Sopenharmony_ci .div_shift = 0, 8262306a36Sopenharmony_ci .div_step = 1, 8362306a36Sopenharmony_ci }, { 8462306a36Sopenharmony_ci .id = EN7523_CLK_BUS, 8562306a36Sopenharmony_ci .name = "bus", 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci .base_reg = REG_BUS_CLK_DIV_SEL, 8862306a36Sopenharmony_ci .base_bits = 1, 8962306a36Sopenharmony_ci .base_shift = 8, 9062306a36Sopenharmony_ci .base_values = bus_base, 9162306a36Sopenharmony_ci .n_base_values = ARRAY_SIZE(bus_base), 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci .div_bits = 3, 9462306a36Sopenharmony_ci .div_shift = 0, 9562306a36Sopenharmony_ci .div_step = 1, 9662306a36Sopenharmony_ci }, { 9762306a36Sopenharmony_ci .id = EN7523_CLK_SLIC, 9862306a36Sopenharmony_ci .name = "slic", 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci .base_reg = REG_SPI_CLK_FREQ_SEL, 10162306a36Sopenharmony_ci .base_bits = 1, 10262306a36Sopenharmony_ci .base_shift = 0, 10362306a36Sopenharmony_ci .base_values = slic_base, 10462306a36Sopenharmony_ci .n_base_values = ARRAY_SIZE(slic_base), 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci .div_reg = REG_SPI_CLK_DIV_SEL, 10762306a36Sopenharmony_ci .div_bits = 5, 10862306a36Sopenharmony_ci .div_shift = 24, 10962306a36Sopenharmony_ci .div_val0 = 20, 11062306a36Sopenharmony_ci .div_step = 2, 11162306a36Sopenharmony_ci }, { 11262306a36Sopenharmony_ci .id = EN7523_CLK_SPI, 11362306a36Sopenharmony_ci .name = "spi", 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci .base_reg = REG_SPI_CLK_DIV_SEL, 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci .base_value = 400000000, 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci .div_bits = 5, 12062306a36Sopenharmony_ci .div_shift = 8, 12162306a36Sopenharmony_ci .div_val0 = 40, 12262306a36Sopenharmony_ci .div_step = 2, 12362306a36Sopenharmony_ci }, { 12462306a36Sopenharmony_ci .id = EN7523_CLK_NPU, 12562306a36Sopenharmony_ci .name = "npu", 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci .base_reg = REG_NPU_CLK_DIV_SEL, 12862306a36Sopenharmony_ci .base_bits = 2, 12962306a36Sopenharmony_ci .base_shift = 8, 13062306a36Sopenharmony_ci .base_values = npu_base, 13162306a36Sopenharmony_ci .n_base_values = ARRAY_SIZE(npu_base), 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci .div_bits = 3, 13462306a36Sopenharmony_ci .div_shift = 0, 13562306a36Sopenharmony_ci .div_step = 1, 13662306a36Sopenharmony_ci }, { 13762306a36Sopenharmony_ci .id = EN7523_CLK_CRYPTO, 13862306a36Sopenharmony_ci .name = "crypto", 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci .base_reg = REG_CRYPTO_CLKSRC, 14162306a36Sopenharmony_ci .base_bits = 1, 14262306a36Sopenharmony_ci .base_shift = 8, 14362306a36Sopenharmony_ci .base_values = emi_base, 14462306a36Sopenharmony_ci .n_base_values = ARRAY_SIZE(emi_base), 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic const struct of_device_id of_match_clk_en7523[] = { 14962306a36Sopenharmony_ci { .compatible = "airoha,en7523-scu", }, 15062306a36Sopenharmony_ci { /* sentinel */ } 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic unsigned int en7523_get_base_rate(void __iomem *base, unsigned int i) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci const struct en_clk_desc *desc = &en7523_base_clks[i]; 15662306a36Sopenharmony_ci u32 val; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (!desc->base_bits) 15962306a36Sopenharmony_ci return desc->base_value; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci val = readl(base + desc->base_reg); 16262306a36Sopenharmony_ci val >>= desc->base_shift; 16362306a36Sopenharmony_ci val &= (1 << desc->base_bits) - 1; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (val >= desc->n_base_values) 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return desc->base_values[val]; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic u32 en7523_get_div(void __iomem *base, int i) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci const struct en_clk_desc *desc = &en7523_base_clks[i]; 17462306a36Sopenharmony_ci u32 reg, val; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (!desc->div_bits) 17762306a36Sopenharmony_ci return 1; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci reg = desc->div_reg ? desc->div_reg : desc->base_reg; 18062306a36Sopenharmony_ci val = readl(base + reg); 18162306a36Sopenharmony_ci val >>= desc->div_shift; 18262306a36Sopenharmony_ci val &= (1 << desc->div_bits) - 1; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (!val && desc->div_val0) 18562306a36Sopenharmony_ci return desc->div_val0; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return (val + 1) * desc->div_step; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int en7523_pci_is_enabled(struct clk_hw *hw) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return !!(readl(cg->base + REG_PCI_CONTROL) & REG_PCI_CONTROL_REFCLK_EN1); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int en7523_pci_prepare(struct clk_hw *hw) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); 20062306a36Sopenharmony_ci void __iomem *np_base = cg->base; 20162306a36Sopenharmony_ci u32 val, mask; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Need to pull device low before reset */ 20462306a36Sopenharmony_ci val = readl(np_base + REG_PCI_CONTROL); 20562306a36Sopenharmony_ci val &= ~(REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT); 20662306a36Sopenharmony_ci writel(val, np_base + REG_PCI_CONTROL); 20762306a36Sopenharmony_ci usleep_range(1000, 2000); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* Enable PCIe port 1 */ 21062306a36Sopenharmony_ci val |= REG_PCI_CONTROL_REFCLK_EN1; 21162306a36Sopenharmony_ci writel(val, np_base + REG_PCI_CONTROL); 21262306a36Sopenharmony_ci usleep_range(1000, 2000); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* Reset to default */ 21562306a36Sopenharmony_ci val = readl(np_base + REG_RESET_CONTROL); 21662306a36Sopenharmony_ci mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2 | 21762306a36Sopenharmony_ci REG_RESET_CONTROL_PCIEHB; 21862306a36Sopenharmony_ci writel(val & ~mask, np_base + REG_RESET_CONTROL); 21962306a36Sopenharmony_ci usleep_range(1000, 2000); 22062306a36Sopenharmony_ci writel(val | mask, np_base + REG_RESET_CONTROL); 22162306a36Sopenharmony_ci msleep(100); 22262306a36Sopenharmony_ci writel(val & ~mask, np_base + REG_RESET_CONTROL); 22362306a36Sopenharmony_ci usleep_range(5000, 10000); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* Release device */ 22662306a36Sopenharmony_ci mask = REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT; 22762306a36Sopenharmony_ci val = readl(np_base + REG_PCI_CONTROL); 22862306a36Sopenharmony_ci writel(val & ~mask, np_base + REG_PCI_CONTROL); 22962306a36Sopenharmony_ci usleep_range(1000, 2000); 23062306a36Sopenharmony_ci writel(val | mask, np_base + REG_PCI_CONTROL); 23162306a36Sopenharmony_ci msleep(250); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return 0; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void en7523_pci_unprepare(struct clk_hw *hw) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); 23962306a36Sopenharmony_ci void __iomem *np_base = cg->base; 24062306a36Sopenharmony_ci u32 val; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci val = readl(np_base + REG_PCI_CONTROL); 24362306a36Sopenharmony_ci val &= ~REG_PCI_CONTROL_REFCLK_EN1; 24462306a36Sopenharmony_ci writel(val, np_base + REG_PCI_CONTROL); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic struct clk_hw *en7523_register_pcie_clk(struct device *dev, 24862306a36Sopenharmony_ci void __iomem *np_base) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci static const struct clk_ops pcie_gate_ops = { 25162306a36Sopenharmony_ci .is_enabled = en7523_pci_is_enabled, 25262306a36Sopenharmony_ci .prepare = en7523_pci_prepare, 25362306a36Sopenharmony_ci .unprepare = en7523_pci_unprepare, 25462306a36Sopenharmony_ci }; 25562306a36Sopenharmony_ci struct clk_init_data init = { 25662306a36Sopenharmony_ci .name = "pcie", 25762306a36Sopenharmony_ci .ops = &pcie_gate_ops, 25862306a36Sopenharmony_ci }; 25962306a36Sopenharmony_ci struct en_clk_gate *cg; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci cg = devm_kzalloc(dev, sizeof(*cg), GFP_KERNEL); 26262306a36Sopenharmony_ci if (!cg) 26362306a36Sopenharmony_ci return NULL; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci cg->base = np_base; 26662306a36Sopenharmony_ci cg->hw.init = &init; 26762306a36Sopenharmony_ci en7523_pci_unprepare(&cg->hw); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (clk_hw_register(dev, &cg->hw)) 27062306a36Sopenharmony_ci return NULL; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return &cg->hw; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void en7523_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, 27662306a36Sopenharmony_ci void __iomem *base, void __iomem *np_base) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct clk_hw *hw; 27962306a36Sopenharmony_ci u32 rate; 28062306a36Sopenharmony_ci int i; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(en7523_base_clks); i++) { 28362306a36Sopenharmony_ci const struct en_clk_desc *desc = &en7523_base_clks[i]; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci rate = en7523_get_base_rate(base, i); 28662306a36Sopenharmony_ci rate /= en7523_get_div(base, i); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci hw = clk_hw_register_fixed_rate(dev, desc->name, NULL, 0, rate); 28962306a36Sopenharmony_ci if (IS_ERR(hw)) { 29062306a36Sopenharmony_ci pr_err("Failed to register clk %s: %ld\n", 29162306a36Sopenharmony_ci desc->name, PTR_ERR(hw)); 29262306a36Sopenharmony_ci continue; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci clk_data->hws[desc->id] = hw; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci hw = en7523_register_pcie_clk(dev, np_base); 29962306a36Sopenharmony_ci clk_data->hws[EN7523_CLK_PCIE] = hw; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci clk_data->num = EN7523_NUM_CLOCKS; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int en7523_clk_probe(struct platform_device *pdev) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 30762306a36Sopenharmony_ci struct clk_hw_onecell_data *clk_data; 30862306a36Sopenharmony_ci void __iomem *base, *np_base; 30962306a36Sopenharmony_ci int r; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, 0); 31262306a36Sopenharmony_ci if (IS_ERR(base)) 31362306a36Sopenharmony_ci return PTR_ERR(base); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci np_base = devm_platform_ioremap_resource(pdev, 1); 31662306a36Sopenharmony_ci if (IS_ERR(np_base)) 31762306a36Sopenharmony_ci return PTR_ERR(np_base); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci clk_data = devm_kzalloc(&pdev->dev, 32062306a36Sopenharmony_ci struct_size(clk_data, hws, EN7523_NUM_CLOCKS), 32162306a36Sopenharmony_ci GFP_KERNEL); 32262306a36Sopenharmony_ci if (!clk_data) 32362306a36Sopenharmony_ci return -ENOMEM; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci en7523_register_clocks(&pdev->dev, clk_data, base, np_base); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); 32862306a36Sopenharmony_ci if (r) 32962306a36Sopenharmony_ci dev_err(&pdev->dev, 33062306a36Sopenharmony_ci "could not register clock provider: %s: %d\n", 33162306a36Sopenharmony_ci pdev->name, r); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci return r; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic struct platform_driver clk_en7523_drv = { 33762306a36Sopenharmony_ci .probe = en7523_clk_probe, 33862306a36Sopenharmony_ci .driver = { 33962306a36Sopenharmony_ci .name = "clk-en7523", 34062306a36Sopenharmony_ci .of_match_table = of_match_clk_en7523, 34162306a36Sopenharmony_ci .suppress_bind_attrs = true, 34262306a36Sopenharmony_ci }, 34362306a36Sopenharmony_ci}; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic int __init clk_en7523_init(void) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci return platform_driver_register(&clk_en7523_drv); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ciarch_initcall(clk_en7523_init); 351