| .. | .. |
|---|
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
|---|
| 2 | +// Copyright IBM Corp |
|---|
| 2 | 3 | |
|---|
| 3 | 4 | #define pr_fmt(fmt) "clk-aspeed: " fmt |
|---|
| 4 | 5 | |
|---|
| 5 | | -#include <linux/clk-provider.h> |
|---|
| 6 | 6 | #include <linux/mfd/syscon.h> |
|---|
| 7 | 7 | #include <linux/of_address.h> |
|---|
| 8 | 8 | #include <linux/of_device.h> |
|---|
| 9 | 9 | #include <linux/platform_device.h> |
|---|
| 10 | 10 | #include <linux/regmap.h> |
|---|
| 11 | | -#include <linux/reset-controller.h> |
|---|
| 12 | 11 | #include <linux/slab.h> |
|---|
| 13 | | -#include <linux/spinlock.h> |
|---|
| 14 | 12 | |
|---|
| 15 | 13 | #include <dt-bindings/clock/aspeed-clock.h> |
|---|
| 16 | 14 | |
|---|
| 17 | | -#define ASPEED_NUM_CLKS 36 |
|---|
| 15 | +#include "clk-aspeed.h" |
|---|
| 16 | + |
|---|
| 17 | +#define ASPEED_NUM_CLKS 38 |
|---|
| 18 | 18 | |
|---|
| 19 | 19 | #define ASPEED_RESET2_OFFSET 32 |
|---|
| 20 | 20 | |
|---|
| .. | .. |
|---|
| 28 | 28 | #define AST2400_HPLL_BYPASS_EN BIT(17) |
|---|
| 29 | 29 | #define ASPEED_MISC_CTRL 0x2c |
|---|
| 30 | 30 | #define UART_DIV13_EN BIT(12) |
|---|
| 31 | +#define ASPEED_MAC_CLK_DLY 0x48 |
|---|
| 31 | 32 | #define ASPEED_STRAP 0x70 |
|---|
| 32 | 33 | #define CLKIN_25MHZ_EN BIT(23) |
|---|
| 33 | 34 | #define AST2400_CLK_SOURCE_SEL BIT(18) |
|---|
| .. | .. |
|---|
| 42 | 43 | |
|---|
| 43 | 44 | static void __iomem *scu_base; |
|---|
| 44 | 45 | |
|---|
| 45 | | -/** |
|---|
| 46 | | - * struct aspeed_gate_data - Aspeed gated clocks |
|---|
| 47 | | - * @clock_idx: bit used to gate this clock in the clock register |
|---|
| 48 | | - * @reset_idx: bit used to reset this IP in the reset register. -1 if no |
|---|
| 49 | | - * reset is required when enabling the clock |
|---|
| 50 | | - * @name: the clock name |
|---|
| 51 | | - * @parent_name: the name of the parent clock |
|---|
| 52 | | - * @flags: standard clock framework flags |
|---|
| 53 | | - */ |
|---|
| 54 | | -struct aspeed_gate_data { |
|---|
| 55 | | - u8 clock_idx; |
|---|
| 56 | | - s8 reset_idx; |
|---|
| 57 | | - const char *name; |
|---|
| 58 | | - const char *parent_name; |
|---|
| 59 | | - unsigned long flags; |
|---|
| 60 | | -}; |
|---|
| 61 | | - |
|---|
| 62 | | -/** |
|---|
| 63 | | - * struct aspeed_clk_gate - Aspeed specific clk_gate structure |
|---|
| 64 | | - * @hw: handle between common and hardware-specific interfaces |
|---|
| 65 | | - * @reg: register controlling gate |
|---|
| 66 | | - * @clock_idx: bit used to gate this clock in the clock register |
|---|
| 67 | | - * @reset_idx: bit used to reset this IP in the reset register. -1 if no |
|---|
| 68 | | - * reset is required when enabling the clock |
|---|
| 69 | | - * @flags: hardware-specific flags |
|---|
| 70 | | - * @lock: register lock |
|---|
| 71 | | - * |
|---|
| 72 | | - * Some of the clocks in the Aspeed SoC must be put in reset before enabling. |
|---|
| 73 | | - * This modified version of clk_gate allows an optional reset bit to be |
|---|
| 74 | | - * specified. |
|---|
| 75 | | - */ |
|---|
| 76 | | -struct aspeed_clk_gate { |
|---|
| 77 | | - struct clk_hw hw; |
|---|
| 78 | | - struct regmap *map; |
|---|
| 79 | | - u8 clock_idx; |
|---|
| 80 | | - s8 reset_idx; |
|---|
| 81 | | - u8 flags; |
|---|
| 82 | | - spinlock_t *lock; |
|---|
| 83 | | -}; |
|---|
| 84 | | - |
|---|
| 85 | | -#define to_aspeed_clk_gate(_hw) container_of(_hw, struct aspeed_clk_gate, hw) |
|---|
| 86 | | - |
|---|
| 87 | 46 | /* TODO: ask Aspeed about the actual parent data */ |
|---|
| 88 | 47 | static const struct aspeed_gate_data aspeed_gates[] = { |
|---|
| 89 | 48 | /* clk rst name parent flags */ |
|---|
| 90 | | - [ASPEED_CLK_GATE_ECLK] = { 0, -1, "eclk-gate", "eclk", 0 }, /* Video Engine */ |
|---|
| 49 | + [ASPEED_CLK_GATE_ECLK] = { 0, 6, "eclk-gate", "eclk", 0 }, /* Video Engine */ |
|---|
| 91 | 50 | [ASPEED_CLK_GATE_GCLK] = { 1, 7, "gclk-gate", NULL, 0 }, /* 2D engine */ |
|---|
| 92 | 51 | [ASPEED_CLK_GATE_MCLK] = { 2, -1, "mclk-gate", "mpll", CLK_IS_CRITICAL }, /* SDRAM */ |
|---|
| 93 | | - [ASPEED_CLK_GATE_VCLK] = { 3, 6, "vclk-gate", NULL, 0 }, /* Video Capture */ |
|---|
| 52 | + [ASPEED_CLK_GATE_VCLK] = { 3, -1, "vclk-gate", NULL, 0 }, /* Video Capture */ |
|---|
| 94 | 53 | [ASPEED_CLK_GATE_BCLK] = { 4, 8, "bclk-gate", "bclk", CLK_IS_CRITICAL }, /* PCIe/PCI */ |
|---|
| 95 | 54 | [ASPEED_CLK_GATE_DCLK] = { 5, -1, "dclk-gate", NULL, CLK_IS_CRITICAL }, /* DAC */ |
|---|
| 96 | 55 | [ASPEED_CLK_GATE_REFCLK] = { 6, -1, "refclk-gate", "clkin", CLK_IS_CRITICAL }, |
|---|
| .. | .. |
|---|
| 111 | 70 | [ASPEED_CLK_GATE_UART4CLK] = { 26, -1, "uart4clk-gate", "uart", 0 }, /* UART4 */ |
|---|
| 112 | 71 | [ASPEED_CLK_GATE_SDCLK] = { 27, 16, "sdclk-gate", NULL, 0 }, /* SDIO/SD */ |
|---|
| 113 | 72 | [ASPEED_CLK_GATE_LHCCLK] = { 28, -1, "lhclk-gate", "lhclk", 0 }, /* LPC master/LPC+ */ |
|---|
| 73 | +}; |
|---|
| 74 | + |
|---|
| 75 | +static const char * const eclk_parent_names[] = { |
|---|
| 76 | + "mpll", |
|---|
| 77 | + "hpll", |
|---|
| 78 | + "dpll", |
|---|
| 79 | +}; |
|---|
| 80 | + |
|---|
| 81 | +static const struct clk_div_table ast2500_eclk_div_table[] = { |
|---|
| 82 | + { 0x0, 2 }, |
|---|
| 83 | + { 0x1, 2 }, |
|---|
| 84 | + { 0x2, 3 }, |
|---|
| 85 | + { 0x3, 4 }, |
|---|
| 86 | + { 0x4, 5 }, |
|---|
| 87 | + { 0x5, 6 }, |
|---|
| 88 | + { 0x6, 7 }, |
|---|
| 89 | + { 0x7, 8 }, |
|---|
| 90 | + { 0 } |
|---|
| 114 | 91 | }; |
|---|
| 115 | 92 | |
|---|
| 116 | 93 | static const struct clk_div_table ast2500_mac_div_table[] = { |
|---|
| .. | .. |
|---|
| 190 | 167 | mult, div); |
|---|
| 191 | 168 | } |
|---|
| 192 | 169 | |
|---|
| 193 | | -struct aspeed_clk_soc_data { |
|---|
| 194 | | - const struct clk_div_table *div_table; |
|---|
| 195 | | - const struct clk_div_table *mac_div_table; |
|---|
| 196 | | - struct clk_hw *(*calc_pll)(const char *name, u32 val); |
|---|
| 197 | | -}; |
|---|
| 198 | | - |
|---|
| 199 | 170 | static const struct aspeed_clk_soc_data ast2500_data = { |
|---|
| 200 | 171 | .div_table = ast2500_div_table, |
|---|
| 172 | + .eclk_div_table = ast2500_eclk_div_table, |
|---|
| 201 | 173 | .mac_div_table = ast2500_mac_div_table, |
|---|
| 202 | 174 | .calc_pll = aspeed_ast2500_calc_pll, |
|---|
| 203 | 175 | }; |
|---|
| 204 | 176 | |
|---|
| 205 | 177 | static const struct aspeed_clk_soc_data ast2400_data = { |
|---|
| 206 | 178 | .div_table = ast2400_div_table, |
|---|
| 179 | + .eclk_div_table = ast2400_div_table, |
|---|
| 207 | 180 | .mac_div_table = ast2400_div_table, |
|---|
| 208 | 181 | .calc_pll = aspeed_ast2400_calc_pll, |
|---|
| 209 | 182 | }; |
|---|
| .. | .. |
|---|
| 294 | 267 | .is_enabled = aspeed_clk_is_enabled, |
|---|
| 295 | 268 | }; |
|---|
| 296 | 269 | |
|---|
| 297 | | -/** |
|---|
| 298 | | - * struct aspeed_reset - Aspeed reset controller |
|---|
| 299 | | - * @map: regmap to access the containing system controller |
|---|
| 300 | | - * @rcdev: reset controller device |
|---|
| 301 | | - */ |
|---|
| 302 | | -struct aspeed_reset { |
|---|
| 303 | | - struct regmap *map; |
|---|
| 304 | | - struct reset_controller_dev rcdev; |
|---|
| 305 | | -}; |
|---|
| 306 | | - |
|---|
| 307 | | -#define to_aspeed_reset(p) container_of((p), struct aspeed_reset, rcdev) |
|---|
| 308 | | - |
|---|
| 309 | 270 | static const u8 aspeed_resets[] = { |
|---|
| 310 | 271 | /* SCU04 resets */ |
|---|
| 311 | 272 | [ASPEED_RESET_XDMA] = 25, |
|---|
| .. | .. |
|---|
| 387 | 348 | u8 clk_gate_flags, spinlock_t *lock) |
|---|
| 388 | 349 | { |
|---|
| 389 | 350 | struct aspeed_clk_gate *gate; |
|---|
| 390 | | - struct clk_init_data init = {}; |
|---|
| 351 | + struct clk_init_data init; |
|---|
| 391 | 352 | struct clk_hw *hw; |
|---|
| 392 | 353 | int ret; |
|---|
| 393 | 354 | |
|---|
| .. | .. |
|---|
| 479 | 440 | return PTR_ERR(hw); |
|---|
| 480 | 441 | aspeed_clk_data->hws[ASPEED_CLK_MPLL] = hw; |
|---|
| 481 | 442 | |
|---|
| 482 | | - /* SD/SDIO clock divider (TODO: There's a gate too) */ |
|---|
| 483 | | - hw = clk_hw_register_divider_table(dev, "sdio", "hpll", 0, |
|---|
| 484 | | - scu_base + ASPEED_CLK_SELECTION, 12, 3, 0, |
|---|
| 443 | + /* SD/SDIO clock divider and gate */ |
|---|
| 444 | + hw = clk_hw_register_gate(dev, "sd_extclk_gate", "hpll", 0, |
|---|
| 445 | + scu_base + ASPEED_CLK_SELECTION, 15, 0, |
|---|
| 446 | + &aspeed_clk_lock); |
|---|
| 447 | + if (IS_ERR(hw)) |
|---|
| 448 | + return PTR_ERR(hw); |
|---|
| 449 | + hw = clk_hw_register_divider_table(dev, "sd_extclk", "sd_extclk_gate", |
|---|
| 450 | + 0, scu_base + ASPEED_CLK_SELECTION, 12, 3, 0, |
|---|
| 485 | 451 | soc_data->div_table, |
|---|
| 486 | 452 | &aspeed_clk_lock); |
|---|
| 487 | 453 | if (IS_ERR(hw)) |
|---|
| .. | .. |
|---|
| 496 | 462 | if (IS_ERR(hw)) |
|---|
| 497 | 463 | return PTR_ERR(hw); |
|---|
| 498 | 464 | aspeed_clk_data->hws[ASPEED_CLK_MAC] = hw; |
|---|
| 465 | + |
|---|
| 466 | + if (of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2500-scu")) { |
|---|
| 467 | + /* RMII 50MHz RCLK */ |
|---|
| 468 | + hw = clk_hw_register_fixed_rate(dev, "mac12rclk", "hpll", 0, |
|---|
| 469 | + 50000000); |
|---|
| 470 | + if (IS_ERR(hw)) |
|---|
| 471 | + return PTR_ERR(hw); |
|---|
| 472 | + |
|---|
| 473 | + /* RMII1 50MHz (RCLK) output enable */ |
|---|
| 474 | + hw = clk_hw_register_gate(dev, "mac1rclk", "mac12rclk", 0, |
|---|
| 475 | + scu_base + ASPEED_MAC_CLK_DLY, 29, 0, |
|---|
| 476 | + &aspeed_clk_lock); |
|---|
| 477 | + if (IS_ERR(hw)) |
|---|
| 478 | + return PTR_ERR(hw); |
|---|
| 479 | + aspeed_clk_data->hws[ASPEED_CLK_MAC1RCLK] = hw; |
|---|
| 480 | + |
|---|
| 481 | + /* RMII2 50MHz (RCLK) output enable */ |
|---|
| 482 | + hw = clk_hw_register_gate(dev, "mac2rclk", "mac12rclk", 0, |
|---|
| 483 | + scu_base + ASPEED_MAC_CLK_DLY, 30, 0, |
|---|
| 484 | + &aspeed_clk_lock); |
|---|
| 485 | + if (IS_ERR(hw)) |
|---|
| 486 | + return PTR_ERR(hw); |
|---|
| 487 | + aspeed_clk_data->hws[ASPEED_CLK_MAC2RCLK] = hw; |
|---|
| 488 | + } |
|---|
| 499 | 489 | |
|---|
| 500 | 490 | /* LPC Host (LHCLK) clock divider */ |
|---|
| 501 | 491 | hw = clk_hw_register_divider_table(dev, "lhclk", "hpll", 0, |
|---|
| .. | .. |
|---|
| 522 | 512 | return PTR_ERR(hw); |
|---|
| 523 | 513 | aspeed_clk_data->hws[ASPEED_CLK_24M] = hw; |
|---|
| 524 | 514 | |
|---|
| 515 | + hw = clk_hw_register_mux(dev, "eclk-mux", eclk_parent_names, |
|---|
| 516 | + ARRAY_SIZE(eclk_parent_names), 0, |
|---|
| 517 | + scu_base + ASPEED_CLK_SELECTION, 2, 0x3, 0, |
|---|
| 518 | + &aspeed_clk_lock); |
|---|
| 519 | + if (IS_ERR(hw)) |
|---|
| 520 | + return PTR_ERR(hw); |
|---|
| 521 | + aspeed_clk_data->hws[ASPEED_CLK_ECLK_MUX] = hw; |
|---|
| 522 | + |
|---|
| 523 | + hw = clk_hw_register_divider_table(dev, "eclk", "eclk-mux", 0, |
|---|
| 524 | + scu_base + ASPEED_CLK_SELECTION, 28, |
|---|
| 525 | + 3, 0, soc_data->eclk_div_table, |
|---|
| 526 | + &aspeed_clk_lock); |
|---|
| 527 | + if (IS_ERR(hw)) |
|---|
| 528 | + return PTR_ERR(hw); |
|---|
| 529 | + aspeed_clk_data->hws[ASPEED_CLK_ECLK] = hw; |
|---|
| 530 | + |
|---|
| 525 | 531 | /* |
|---|
| 526 | 532 | * TODO: There are a number of clocks that not included in this driver |
|---|
| 527 | 533 | * as more information is required: |
|---|
| .. | .. |
|---|
| 531 | 537 | * RGMII |
|---|
| 532 | 538 | * RMII |
|---|
| 533 | 539 | * UART[1..5] clock source mux |
|---|
| 534 | | - * Video Engine (ECLK) mux and clock divider |
|---|
| 535 | 540 | */ |
|---|
| 536 | 541 | |
|---|
| 537 | 542 | for (i = 0; i < ARRAY_SIZE(aspeed_gates); i++) { |
|---|