/* * Copyright (c) 2022 ASPEED Technology Inc. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT aspeed_ast10x0_clock #include #include #include #include #include #define LOG_LEVEL CONFIG_CLOCK_CONTROL_LOG_LEVEL #include LOG_MODULE_REGISTER(clock_control_ast10x0); #define HPLL_FREQ MHZ(1000) /* * CLK_STOP_CTRL0/1_SET registers: * - Each bit in these registers controls a clock gate * - Write '1' to a bit: turn OFF the corresponding clock * - Write '0' to a bit: no effect * CLK_STOP_CTRL0/1_CLEAR register: * - Write '1' to a bit: clear the corresponding bit in CLK_STOP_CTRL0/1. * (turn ON the corresponding clock) */ #define CLK_STOP_CTRL0_SET 0x80 #define CLK_STOP_CTRL0_CLEAR 0x84 #define CLK_STOP_CTRL1_SET 0x90 #define CLK_STOP_CTRL1_CLEAR 0x94 #define CLK_SELECTION_REG4 0x310 #define I3C_CLK_SRC_SEL BIT(31) #define I3C_CLK_SRC_HPLL 0 #define I3C_CLK_SRC_480M 1 #define I3C_CLK_DIV_SEL GENMASK(30, 28) #define I3C_CLK_DIV_REG_TO_VAL(x) ((x == 0) ? 2 : (x + 1)) #define PCLK_DIV_SEL GENMASK(11, 8) #define PCLK_DIV_REG_TO_VAL(x) ((x + 1) << 1) #define CLK_SELECTION_REG5 0x314 #define HCLK_DIV_SEL GENMASK(30, 28) #define HCLK_DIV_REG_TO_VAL(x) ((x == 0) ? 2 : x + 1) struct clock_aspeed_config { const struct device *syscon; }; #define DEV_CFG(dev) ((const struct clock_aspeed_config *const)(dev)->config) static int aspeed_clock_control_on(const struct device *dev, clock_control_subsys_t sub_system) { const struct device *syscon = DEV_CFG(dev)->syscon; uint32_t clk_gate = (uint32_t)sub_system; uint32_t addr = CLK_STOP_CTRL0_CLEAR; /* there is no on/off control for group2 clocks */ if (clk_gate >= ASPEED_CLK_GRP_2_OFFSET) { return 0; } if (clk_gate >= ASPEED_CLK_GRP_1_OFFSET) { clk_gate -= ASPEED_CLK_GRP_1_OFFSET; addr = CLK_STOP_CTRL1_CLEAR; } syscon_write_reg(syscon, addr, BIT(clk_gate)); return 0; } static int aspeed_clock_control_off(const struct device *dev, clock_control_subsys_t sub_system) { const struct device *syscon = DEV_CFG(dev)->syscon; uint32_t clk_gate = (uint32_t)sub_system; uint32_t addr = CLK_STOP_CTRL0_SET; /* there is no on/off control for group2 clocks */ if (clk_gate >= ASPEED_CLK_GRP_2_OFFSET) { return 0; } if (clk_gate >= ASPEED_CLK_GRP_1_OFFSET) { clk_gate -= ASPEED_CLK_GRP_1_OFFSET; addr = CLK_STOP_CTRL1_SET; } syscon_write_reg(syscon, addr, BIT(clk_gate)); return 0; } static int aspeed_clock_control_get_rate(const struct device *dev, clock_control_subsys_t sub_system, uint32_t *rate) { const struct device *syscon = DEV_CFG(dev)->syscon; uint32_t clk_id = (uint32_t)sub_system; uint32_t reg, src, clk_div; switch (clk_id) { case ASPEED_CLK_I3C0: case ASPEED_CLK_I3C1: case ASPEED_CLK_I3C2: case ASPEED_CLK_I3C3: syscon_read_reg(syscon, CLK_SELECTION_REG4, ®); if (FIELD_GET(I3C_CLK_SRC_SEL, reg) == I3C_CLK_SRC_HPLL) { src = HPLL_FREQ; } else { src = MHZ(480); } clk_div = I3C_CLK_DIV_REG_TO_VAL(FIELD_GET(I3C_CLK_DIV_SEL, reg)); *rate = src / clk_div; break; case ASPEED_CLK_HCLK: src = HPLL_FREQ; syscon_read_reg(syscon, CLK_SELECTION_REG5, ®); clk_div = HCLK_DIV_REG_TO_VAL(FIELD_GET(HCLK_DIV_SEL, reg)); *rate = src / clk_div; break; case ASPEED_CLK_PCLK: src = HPLL_FREQ; syscon_read_reg(syscon, CLK_SELECTION_REG4, ®); clk_div = PCLK_DIV_REG_TO_VAL(FIELD_GET(PCLK_DIV_SEL, reg)); *rate = src / clk_div; break; case ASPEED_CLK_UART1: case ASPEED_CLK_UART2: case ASPEED_CLK_UART3: case ASPEED_CLK_UART4: case ASPEED_CLK_UART5: case ASPEED_CLK_UART6: case ASPEED_CLK_UART7: case ASPEED_CLK_UART8: case ASPEED_CLK_UART9: case ASPEED_CLK_UART10: case ASPEED_CLK_UART11: case ASPEED_CLK_UART12: case ASPEED_CLK_UART13: *rate = MHZ(24) / 13; break; default: return -EINVAL; } return 0; } static int aspeed_clock_control_init(const struct device *dev) { ARG_UNUSED(dev); return 0; } static const struct clock_control_driver_api aspeed_clk_api = { .on = aspeed_clock_control_on, .off = aspeed_clock_control_off, .get_rate = aspeed_clock_control_get_rate, }; #define ASPEED_CLOCK_INIT(n) \ static const struct clock_aspeed_config clock_aspeed_cfg_##n = { \ .syscon = DEVICE_DT_GET(DT_NODELABEL(syscon)), \ }; \ DEVICE_DT_INST_DEFINE(n, &aspeed_clock_control_init, NULL, NULL, &clock_aspeed_cfg_##n, \ PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &aspeed_clk_api); DT_INST_FOREACH_STATUS_OKAY(ASPEED_CLOCK_INIT)