1*5eda5d79SJoel Stanley // SPDX-License-Identifier: GPL-2.0+ 2*5eda5d79SJoel Stanley 3*5eda5d79SJoel Stanley #define pr_fmt(fmt) "clk-aspeed: " fmt 4*5eda5d79SJoel Stanley 5*5eda5d79SJoel Stanley #include <linux/clk-provider.h> 6*5eda5d79SJoel Stanley #include <linux/mfd/syscon.h> 7*5eda5d79SJoel Stanley #include <linux/of_address.h> 8*5eda5d79SJoel Stanley #include <linux/regmap.h> 9*5eda5d79SJoel Stanley #include <linux/slab.h> 10*5eda5d79SJoel Stanley #include <linux/spinlock.h> 11*5eda5d79SJoel Stanley 12*5eda5d79SJoel Stanley #include <dt-bindings/clock/aspeed-clock.h> 13*5eda5d79SJoel Stanley 14*5eda5d79SJoel Stanley #define ASPEED_NUM_CLKS 35 15*5eda5d79SJoel Stanley 16*5eda5d79SJoel Stanley #define ASPEED_STRAP 0x70 17*5eda5d79SJoel Stanley 18*5eda5d79SJoel Stanley /* Keeps track of all clocks */ 19*5eda5d79SJoel Stanley static struct clk_hw_onecell_data *aspeed_clk_data; 20*5eda5d79SJoel Stanley 21*5eda5d79SJoel Stanley static void __iomem *scu_base; 22*5eda5d79SJoel Stanley 23*5eda5d79SJoel Stanley /** 24*5eda5d79SJoel Stanley * struct aspeed_gate_data - Aspeed gated clocks 25*5eda5d79SJoel Stanley * @clock_idx: bit used to gate this clock in the clock register 26*5eda5d79SJoel Stanley * @reset_idx: bit used to reset this IP in the reset register. -1 if no 27*5eda5d79SJoel Stanley * reset is required when enabling the clock 28*5eda5d79SJoel Stanley * @name: the clock name 29*5eda5d79SJoel Stanley * @parent_name: the name of the parent clock 30*5eda5d79SJoel Stanley * @flags: standard clock framework flags 31*5eda5d79SJoel Stanley */ 32*5eda5d79SJoel Stanley struct aspeed_gate_data { 33*5eda5d79SJoel Stanley u8 clock_idx; 34*5eda5d79SJoel Stanley s8 reset_idx; 35*5eda5d79SJoel Stanley const char *name; 36*5eda5d79SJoel Stanley const char *parent_name; 37*5eda5d79SJoel Stanley unsigned long flags; 38*5eda5d79SJoel Stanley }; 39*5eda5d79SJoel Stanley 40*5eda5d79SJoel Stanley /** 41*5eda5d79SJoel Stanley * struct aspeed_clk_gate - Aspeed specific clk_gate structure 42*5eda5d79SJoel Stanley * @hw: handle between common and hardware-specific interfaces 43*5eda5d79SJoel Stanley * @reg: register controlling gate 44*5eda5d79SJoel Stanley * @clock_idx: bit used to gate this clock in the clock register 45*5eda5d79SJoel Stanley * @reset_idx: bit used to reset this IP in the reset register. -1 if no 46*5eda5d79SJoel Stanley * reset is required when enabling the clock 47*5eda5d79SJoel Stanley * @flags: hardware-specific flags 48*5eda5d79SJoel Stanley * @lock: register lock 49*5eda5d79SJoel Stanley * 50*5eda5d79SJoel Stanley * Some of the clocks in the Aspeed SoC must be put in reset before enabling. 51*5eda5d79SJoel Stanley * This modified version of clk_gate allows an optional reset bit to be 52*5eda5d79SJoel Stanley * specified. 53*5eda5d79SJoel Stanley */ 54*5eda5d79SJoel Stanley struct aspeed_clk_gate { 55*5eda5d79SJoel Stanley struct clk_hw hw; 56*5eda5d79SJoel Stanley struct regmap *map; 57*5eda5d79SJoel Stanley u8 clock_idx; 58*5eda5d79SJoel Stanley s8 reset_idx; 59*5eda5d79SJoel Stanley u8 flags; 60*5eda5d79SJoel Stanley spinlock_t *lock; 61*5eda5d79SJoel Stanley }; 62*5eda5d79SJoel Stanley 63*5eda5d79SJoel Stanley #define to_aspeed_clk_gate(_hw) container_of(_hw, struct aspeed_clk_gate, hw) 64*5eda5d79SJoel Stanley 65*5eda5d79SJoel Stanley /* TODO: ask Aspeed about the actual parent data */ 66*5eda5d79SJoel Stanley static const struct aspeed_gate_data aspeed_gates[] = { 67*5eda5d79SJoel Stanley /* clk rst name parent flags */ 68*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_ECLK] = { 0, -1, "eclk-gate", "eclk", 0 }, /* Video Engine */ 69*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_GCLK] = { 1, 7, "gclk-gate", NULL, 0 }, /* 2D engine */ 70*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_MCLK] = { 2, -1, "mclk-gate", "mpll", CLK_IS_CRITICAL }, /* SDRAM */ 71*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_VCLK] = { 3, 6, "vclk-gate", NULL, 0 }, /* Video Capture */ 72*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_BCLK] = { 4, 10, "bclk-gate", "bclk", 0 }, /* PCIe/PCI */ 73*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_DCLK] = { 5, -1, "dclk-gate", NULL, 0 }, /* DAC */ 74*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_REFCLK] = { 6, -1, "refclk-gate", "clkin", CLK_IS_CRITICAL }, 75*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_USBPORT2CLK] = { 7, 3, "usb-port2-gate", NULL, 0 }, /* USB2.0 Host port 2 */ 76*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_LCLK] = { 8, 5, "lclk-gate", NULL, 0 }, /* LPC */ 77*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_USBUHCICLK] = { 9, 15, "usb-uhci-gate", NULL, 0 }, /* USB1.1 (requires port 2 enabled) */ 78*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_D1CLK] = { 10, 13, "d1clk-gate", NULL, 0 }, /* GFX CRT */ 79*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_YCLK] = { 13, 4, "yclk-gate", NULL, 0 }, /* HAC */ 80*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_USBPORT1CLK] = { 14, 14, "usb-port1-gate", NULL, 0 }, /* USB2 hub/USB2 host port 1/USB1.1 dev */ 81*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_UART1CLK] = { 15, -1, "uart1clk-gate", "uart", 0 }, /* UART1 */ 82*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_UART2CLK] = { 16, -1, "uart2clk-gate", "uart", 0 }, /* UART2 */ 83*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_UART5CLK] = { 17, -1, "uart5clk-gate", "uart", 0 }, /* UART5 */ 84*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_ESPICLK] = { 19, -1, "espiclk-gate", NULL, 0 }, /* eSPI */ 85*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_MAC1CLK] = { 20, 11, "mac1clk-gate", "mac", 0 }, /* MAC1 */ 86*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_MAC2CLK] = { 21, 12, "mac2clk-gate", "mac", 0 }, /* MAC2 */ 87*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_RSACLK] = { 24, -1, "rsaclk-gate", NULL, 0 }, /* RSA */ 88*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_UART3CLK] = { 25, -1, "uart3clk-gate", "uart", 0 }, /* UART3 */ 89*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_UART4CLK] = { 26, -1, "uart4clk-gate", "uart", 0 }, /* UART4 */ 90*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_SDCLKCLK] = { 27, 16, "sdclk-gate", NULL, 0 }, /* SDIO/SD */ 91*5eda5d79SJoel Stanley [ASPEED_CLK_GATE_LHCCLK] = { 28, -1, "lhclk-gate", "lhclk", 0 }, /* LPC master/LPC+ */ 92*5eda5d79SJoel Stanley }; 93*5eda5d79SJoel Stanley 94*5eda5d79SJoel Stanley static void __init aspeed_cc_init(struct device_node *np) 95*5eda5d79SJoel Stanley { 96*5eda5d79SJoel Stanley struct regmap *map; 97*5eda5d79SJoel Stanley u32 val; 98*5eda5d79SJoel Stanley int ret; 99*5eda5d79SJoel Stanley int i; 100*5eda5d79SJoel Stanley 101*5eda5d79SJoel Stanley scu_base = of_iomap(np, 0); 102*5eda5d79SJoel Stanley if (IS_ERR(scu_base)) 103*5eda5d79SJoel Stanley return; 104*5eda5d79SJoel Stanley 105*5eda5d79SJoel Stanley aspeed_clk_data = kzalloc(sizeof(*aspeed_clk_data) + 106*5eda5d79SJoel Stanley sizeof(*aspeed_clk_data->hws) * ASPEED_NUM_CLKS, 107*5eda5d79SJoel Stanley GFP_KERNEL); 108*5eda5d79SJoel Stanley if (!aspeed_clk_data) 109*5eda5d79SJoel Stanley return; 110*5eda5d79SJoel Stanley 111*5eda5d79SJoel Stanley /* 112*5eda5d79SJoel Stanley * This way all clocks fetched before the platform device probes, 113*5eda5d79SJoel Stanley * except those we assign here for early use, will be deferred. 114*5eda5d79SJoel Stanley */ 115*5eda5d79SJoel Stanley for (i = 0; i < ASPEED_NUM_CLKS; i++) 116*5eda5d79SJoel Stanley aspeed_clk_data->hws[i] = ERR_PTR(-EPROBE_DEFER); 117*5eda5d79SJoel Stanley 118*5eda5d79SJoel Stanley map = syscon_node_to_regmap(np); 119*5eda5d79SJoel Stanley if (IS_ERR(map)) { 120*5eda5d79SJoel Stanley pr_err("no syscon regmap\n"); 121*5eda5d79SJoel Stanley return; 122*5eda5d79SJoel Stanley } 123*5eda5d79SJoel Stanley /* 124*5eda5d79SJoel Stanley * We check that the regmap works on this very first access, 125*5eda5d79SJoel Stanley * but as this is an MMIO-backed regmap, subsequent regmap 126*5eda5d79SJoel Stanley * access is not going to fail and we skip error checks from 127*5eda5d79SJoel Stanley * this point. 128*5eda5d79SJoel Stanley */ 129*5eda5d79SJoel Stanley ret = regmap_read(map, ASPEED_STRAP, &val); 130*5eda5d79SJoel Stanley if (ret) { 131*5eda5d79SJoel Stanley pr_err("failed to read strapping register\n"); 132*5eda5d79SJoel Stanley return; 133*5eda5d79SJoel Stanley } 134*5eda5d79SJoel Stanley 135*5eda5d79SJoel Stanley aspeed_clk_data->num = ASPEED_NUM_CLKS; 136*5eda5d79SJoel Stanley ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, aspeed_clk_data); 137*5eda5d79SJoel Stanley if (ret) 138*5eda5d79SJoel Stanley pr_err("failed to add DT provider: %d\n", ret); 139*5eda5d79SJoel Stanley }; 140*5eda5d79SJoel Stanley CLK_OF_DECLARE_DRIVER(aspeed_cc_g5, "aspeed,ast2500-scu", aspeed_cc_init); 141*5eda5d79SJoel Stanley CLK_OF_DECLARE_DRIVER(aspeed_cc_g4, "aspeed,ast2400-scu", aspeed_cc_init); 142