|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
| 2 | + |
| 3 | +#include <linux/delay.h> |
| 4 | +#include <linux/clk-provider.h> |
| 5 | +#include <linux/of.h> |
| 6 | +#include <linux/of_address.h> |
| 7 | +#include <linux/of_device.h> |
| 8 | +#include <linux/platform_device.h> |
| 9 | +#include <dt-bindings/clock/en7523-clk.h> |
| 10 | + |
| 11 | +#define REG_PCI_CONTROL 0x88 |
| 12 | +#define REG_PCI_CONTROL_PERSTOUT BIT(29) |
| 13 | +#define REG_PCI_CONTROL_PERSTOUT1 BIT(26) |
| 14 | +#define REG_PCI_CONTROL_REFCLK_EN1 BIT(22) |
| 15 | +#define REG_GSW_CLK_DIV_SEL 0x1b4 |
| 16 | +#define REG_EMI_CLK_DIV_SEL 0x1b8 |
| 17 | +#define REG_BUS_CLK_DIV_SEL 0x1bc |
| 18 | +#define REG_SPI_CLK_DIV_SEL 0x1c4 |
| 19 | +#define REG_SPI_CLK_FREQ_SEL 0x1c8 |
| 20 | +#define REG_NPU_CLK_DIV_SEL 0x1fc |
| 21 | +#define REG_CRYPTO_CLKSRC 0x200 |
| 22 | +#define REG_RESET_CONTROL 0x834 |
| 23 | +#define REG_RESET_CONTROL_PCIEHB BIT(29) |
| 24 | +#define REG_RESET_CONTROL_PCIE1 BIT(27) |
| 25 | +#define REG_RESET_CONTROL_PCIE2 BIT(26) |
| 26 | + |
| 27 | +struct en_clk_desc { |
| 28 | + int id; |
| 29 | + const char *name; |
| 30 | + u32 base_reg; |
| 31 | + u8 base_bits; |
| 32 | + u8 base_shift; |
| 33 | + union { |
| 34 | + const unsigned int *base_values; |
| 35 | + unsigned int base_value; |
| 36 | + }; |
| 37 | + size_t n_base_values; |
| 38 | + |
| 39 | + u16 div_reg; |
| 40 | + u8 div_bits; |
| 41 | + u8 div_shift; |
| 42 | + u16 div_val0; |
| 43 | + u8 div_step; |
| 44 | +}; |
| 45 | + |
| 46 | +struct en_clk_gate { |
| 47 | + void __iomem *base; |
| 48 | + struct clk_hw hw; |
| 49 | +}; |
| 50 | + |
| 51 | +static const u32 gsw_base[] = { 400000000, 500000000 }; |
| 52 | +static const u32 emi_base[] = { 333000000, 400000000 }; |
| 53 | +static const u32 bus_base[] = { 500000000, 540000000 }; |
| 54 | +static const u32 slic_base[] = { 100000000, 3125000 }; |
| 55 | +static const u32 npu_base[] = { 333000000, 400000000, 500000000 }; |
| 56 | + |
| 57 | +static const struct en_clk_desc en7523_base_clks[] = { |
| 58 | + { |
| 59 | + .id = EN7523_CLK_GSW, |
| 60 | + .name = "gsw", |
| 61 | + |
| 62 | + .base_reg = REG_GSW_CLK_DIV_SEL, |
| 63 | + .base_bits = 1, |
| 64 | + .base_shift = 8, |
| 65 | + .base_values = gsw_base, |
| 66 | + .n_base_values = ARRAY_SIZE(gsw_base), |
| 67 | + |
| 68 | + .div_bits = 3, |
| 69 | + .div_shift = 0, |
| 70 | + .div_step = 1, |
| 71 | + }, { |
| 72 | + .id = EN7523_CLK_EMI, |
| 73 | + .name = "emi", |
| 74 | + |
| 75 | + .base_reg = REG_EMI_CLK_DIV_SEL, |
| 76 | + .base_bits = 1, |
| 77 | + .base_shift = 8, |
| 78 | + .base_values = emi_base, |
| 79 | + .n_base_values = ARRAY_SIZE(emi_base), |
| 80 | + |
| 81 | + .div_bits = 3, |
| 82 | + .div_shift = 0, |
| 83 | + .div_step = 1, |
| 84 | + }, { |
| 85 | + .id = EN7523_CLK_BUS, |
| 86 | + .name = "bus", |
| 87 | + |
| 88 | + .base_reg = REG_BUS_CLK_DIV_SEL, |
| 89 | + .base_bits = 1, |
| 90 | + .base_shift = 8, |
| 91 | + .base_values = bus_base, |
| 92 | + .n_base_values = ARRAY_SIZE(bus_base), |
| 93 | + |
| 94 | + .div_bits = 3, |
| 95 | + .div_shift = 0, |
| 96 | + .div_step = 1, |
| 97 | + }, { |
| 98 | + .id = EN7523_CLK_SLIC, |
| 99 | + .name = "slic", |
| 100 | + |
| 101 | + .base_reg = REG_SPI_CLK_FREQ_SEL, |
| 102 | + .base_bits = 1, |
| 103 | + .base_shift = 0, |
| 104 | + .base_values = slic_base, |
| 105 | + .n_base_values = ARRAY_SIZE(slic_base), |
| 106 | + |
| 107 | + .div_reg = REG_SPI_CLK_DIV_SEL, |
| 108 | + .div_bits = 5, |
| 109 | + .div_shift = 24, |
| 110 | + .div_val0 = 20, |
| 111 | + .div_step = 2, |
| 112 | + }, { |
| 113 | + .id = EN7523_CLK_SPI, |
| 114 | + .name = "spi", |
| 115 | + |
| 116 | + .base_reg = REG_SPI_CLK_DIV_SEL, |
| 117 | + |
| 118 | + .base_value = 400000000, |
| 119 | + |
| 120 | + .div_bits = 5, |
| 121 | + .div_shift = 8, |
| 122 | + .div_val0 = 40, |
| 123 | + .div_step = 2, |
| 124 | + }, { |
| 125 | + .id = EN7523_CLK_NPU, |
| 126 | + .name = "npu", |
| 127 | + |
| 128 | + .base_reg = REG_NPU_CLK_DIV_SEL, |
| 129 | + .base_bits = 2, |
| 130 | + .base_shift = 8, |
| 131 | + .base_values = npu_base, |
| 132 | + .n_base_values = ARRAY_SIZE(npu_base), |
| 133 | + |
| 134 | + .div_bits = 3, |
| 135 | + .div_shift = 0, |
| 136 | + .div_step = 1, |
| 137 | + }, { |
| 138 | + .id = EN7523_CLK_CRYPTO, |
| 139 | + .name = "crypto", |
| 140 | + |
| 141 | + .base_reg = REG_CRYPTO_CLKSRC, |
| 142 | + .base_bits = 1, |
| 143 | + .base_shift = 8, |
| 144 | + .base_values = emi_base, |
| 145 | + .n_base_values = ARRAY_SIZE(emi_base), |
| 146 | + } |
| 147 | +}; |
| 148 | + |
| 149 | +static const struct of_device_id of_match_clk_en7523[] = { |
| 150 | + { .compatible = "airoha,en7523-scu", }, |
| 151 | + { /* sentinel */ } |
| 152 | +}; |
| 153 | + |
| 154 | +static unsigned int en7523_get_base_rate(void __iomem *base, unsigned int i) |
| 155 | +{ |
| 156 | + const struct en_clk_desc *desc = &en7523_base_clks[i]; |
| 157 | + u32 val; |
| 158 | + |
| 159 | + if (!desc->base_bits) |
| 160 | + return desc->base_value; |
| 161 | + |
| 162 | + val = readl(base + desc->base_reg); |
| 163 | + val >>= desc->base_shift; |
| 164 | + val &= (1 << desc->base_bits) - 1; |
| 165 | + |
| 166 | + if (val >= desc->n_base_values) |
| 167 | + return 0; |
| 168 | + |
| 169 | + return desc->base_values[val]; |
| 170 | +} |
| 171 | + |
| 172 | +static u32 en7523_get_div(void __iomem *base, int i) |
| 173 | +{ |
| 174 | + const struct en_clk_desc *desc = &en7523_base_clks[i]; |
| 175 | + u32 reg, val; |
| 176 | + |
| 177 | + if (!desc->div_bits) |
| 178 | + return 1; |
| 179 | + |
| 180 | + reg = desc->div_reg ? desc->div_reg : desc->base_reg; |
| 181 | + val = readl(base + reg); |
| 182 | + val >>= desc->div_shift; |
| 183 | + val &= (1 << desc->div_bits) - 1; |
| 184 | + |
| 185 | + if (!val && desc->div_val0) |
| 186 | + return desc->div_val0; |
| 187 | + |
| 188 | + return (val + 1) * desc->div_step; |
| 189 | +} |
| 190 | + |
| 191 | +static int en7523_pci_is_enabled(struct clk_hw *hw) |
| 192 | +{ |
| 193 | + struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); |
| 194 | + |
| 195 | + return !!(readl(cg->base + REG_PCI_CONTROL) & REG_PCI_CONTROL_REFCLK_EN1); |
| 196 | +} |
| 197 | + |
| 198 | +static int en7523_pci_prepare(struct clk_hw *hw) |
| 199 | +{ |
| 200 | + struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); |
| 201 | + void __iomem *np_base = cg->base; |
| 202 | + u32 val, mask; |
| 203 | + |
| 204 | + /* Need to pull device low before reset */ |
| 205 | + val = readl(np_base + REG_PCI_CONTROL); |
| 206 | + val &= ~(REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT); |
| 207 | + writel(val, np_base + REG_PCI_CONTROL); |
| 208 | + usleep_range(1000, 2000); |
| 209 | + |
| 210 | + /* Enable PCIe port 1 */ |
| 211 | + val |= REG_PCI_CONTROL_REFCLK_EN1; |
| 212 | + writel(val, np_base + REG_PCI_CONTROL); |
| 213 | + usleep_range(1000, 2000); |
| 214 | + |
| 215 | + /* Reset to default */ |
| 216 | + val = readl(np_base + REG_RESET_CONTROL); |
| 217 | + mask = REG_RESET_CONTROL_PCIE1 | REG_RESET_CONTROL_PCIE2 | |
| 218 | + REG_RESET_CONTROL_PCIEHB; |
| 219 | + writel(val & ~mask, np_base + REG_RESET_CONTROL); |
| 220 | + usleep_range(1000, 2000); |
| 221 | + writel(val | mask, np_base + REG_RESET_CONTROL); |
| 222 | + msleep(100); |
| 223 | + writel(val & ~mask, np_base + REG_RESET_CONTROL); |
| 224 | + usleep_range(5000, 10000); |
| 225 | + |
| 226 | + /* Release device */ |
| 227 | + mask = REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT; |
| 228 | + val = readl(np_base + REG_PCI_CONTROL); |
| 229 | + writel(val & ~mask, np_base + REG_PCI_CONTROL); |
| 230 | + usleep_range(1000, 2000); |
| 231 | + writel(val | mask, np_base + REG_PCI_CONTROL); |
| 232 | + msleep(250); |
| 233 | + |
| 234 | + return 0; |
| 235 | +} |
| 236 | + |
| 237 | +static void en7523_pci_unprepare(struct clk_hw *hw) |
| 238 | +{ |
| 239 | + struct en_clk_gate *cg = container_of(hw, struct en_clk_gate, hw); |
| 240 | + void __iomem *np_base = cg->base; |
| 241 | + u32 val; |
| 242 | + |
| 243 | + val = readl(np_base + REG_PCI_CONTROL); |
| 244 | + val &= ~REG_PCI_CONTROL_REFCLK_EN1; |
| 245 | + writel(val, np_base + REG_PCI_CONTROL); |
| 246 | +} |
| 247 | + |
| 248 | +static struct clk_hw *en7523_register_pcie_clk(struct device *dev, |
| 249 | + void __iomem *np_base) |
| 250 | +{ |
| 251 | + static const struct clk_ops pcie_gate_ops = { |
| 252 | + .is_enabled = en7523_pci_is_enabled, |
| 253 | + .prepare = en7523_pci_prepare, |
| 254 | + .unprepare = en7523_pci_unprepare, |
| 255 | + }; |
| 256 | + struct clk_init_data init = { |
| 257 | + .name = "pcie", |
| 258 | + .ops = &pcie_gate_ops, |
| 259 | + }; |
| 260 | + struct en_clk_gate *cg; |
| 261 | + |
| 262 | + cg = devm_kzalloc(dev, sizeof(*cg), GFP_KERNEL); |
| 263 | + if (!cg) |
| 264 | + return NULL; |
| 265 | + |
| 266 | + cg->base = np_base; |
| 267 | + cg->hw.init = &init; |
| 268 | + en7523_pci_unprepare(&cg->hw); |
| 269 | + |
| 270 | + if (clk_hw_register(dev, &cg->hw)) |
| 271 | + return NULL; |
| 272 | + |
| 273 | + return &cg->hw; |
| 274 | +} |
| 275 | + |
| 276 | +static void en7523_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, |
| 277 | + void __iomem *base, void __iomem *np_base) |
| 278 | +{ |
| 279 | + struct clk_hw *hw; |
| 280 | + u32 rate; |
| 281 | + int i; |
| 282 | + |
| 283 | + for (i = 0; i < ARRAY_SIZE(en7523_base_clks); i++) { |
| 284 | + const struct en_clk_desc *desc = &en7523_base_clks[i]; |
| 285 | + |
| 286 | + rate = en7523_get_base_rate(base, i); |
| 287 | + rate /= en7523_get_div(base, i); |
| 288 | + |
| 289 | + hw = clk_hw_register_fixed_rate(dev, desc->name, NULL, 0, rate); |
| 290 | + if (IS_ERR(hw)) { |
| 291 | + pr_err("Failed to register clk %s: %ld\n", |
| 292 | + desc->name, PTR_ERR(hw)); |
| 293 | + continue; |
| 294 | + } |
| 295 | + |
| 296 | + clk_data->hws[desc->id] = hw; |
| 297 | + } |
| 298 | + |
| 299 | + hw = en7523_register_pcie_clk(dev, np_base); |
| 300 | + clk_data->hws[EN7523_CLK_PCIE] = hw; |
| 301 | + |
| 302 | + clk_data->num = EN7523_NUM_CLOCKS; |
| 303 | +} |
| 304 | + |
| 305 | +static int en7523_clk_probe(struct platform_device *pdev) |
| 306 | +{ |
| 307 | + struct device_node *node = pdev->dev.of_node; |
| 308 | + struct clk_hw_onecell_data *clk_data; |
| 309 | + void __iomem *base, *np_base; |
| 310 | + int r; |
| 311 | + |
| 312 | + base = devm_platform_ioremap_resource(pdev, 0); |
| 313 | + if (IS_ERR(base)) |
| 314 | + return PTR_ERR(base); |
| 315 | + |
| 316 | + np_base = devm_platform_ioremap_resource(pdev, 1); |
| 317 | + if (IS_ERR(base)) |
| 318 | + return PTR_ERR(np_base); |
| 319 | + |
| 320 | + clk_data = devm_kzalloc(&pdev->dev, |
| 321 | + struct_size(clk_data, hws, EN7523_NUM_CLOCKS), |
| 322 | + GFP_KERNEL); |
| 323 | + if (!clk_data) |
| 324 | + return -ENOMEM; |
| 325 | + |
| 326 | + en7523_register_clocks(&pdev->dev, clk_data, base, np_base); |
| 327 | + |
| 328 | + r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); |
| 329 | + if (r) |
| 330 | + dev_err(&pdev->dev, |
| 331 | + "could not register clock provider: %s: %d\n", |
| 332 | + pdev->name, r); |
| 333 | + |
| 334 | + return r; |
| 335 | +} |
| 336 | + |
| 337 | +static struct platform_driver clk_en7523_drv = { |
| 338 | + .probe = en7523_clk_probe, |
| 339 | + .driver = { |
| 340 | + .name = "clk-en7523", |
| 341 | + .of_match_table = of_match_clk_en7523, |
| 342 | + .suppress_bind_attrs = true, |
| 343 | + }, |
| 344 | +}; |
| 345 | + |
| 346 | +static int __init clk_en7523_init(void) |
| 347 | +{ |
| 348 | + return platform_driver_register(&clk_en7523_drv); |
| 349 | +} |
| 350 | + |
| 351 | +arch_initcall(clk_en7523_init); |
0 commit comments