You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
4.7 KiB
162 lines
4.7 KiB
/* |
|
* 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 DEVICE_API(clock_control, 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, NULL, 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)
|
|
|