|  | /* | 
|  | * Copyright (c) 2022 ASPEED Technology Inc. | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT aspeed_ast10x0_clock | 
|  | #include <errno.h> | 
|  | #include <zephyr/dt-bindings/clock/ast10x0_clock.h> | 
|  | #include <zephyr/drivers/clock_control.h> | 
|  | #include <zephyr/drivers/syscon.h> | 
|  | #include <zephyr/sys/util.h> | 
|  |  | 
|  | #define LOG_LEVEL CONFIG_CLOCK_CONTROL_LOG_LEVEL | 
|  | #include <zephyr/logging/log.h> | 
|  | 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) |