// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2019-20 Sean Anderson <seanga2@gmail.com>
* Copyright (c) 2019 Western Digital Corporation or its affiliates.
*/
#define pr_fmt(fmt) "k210-clk: " fmt
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_clk.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/clk-provider.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <soc/canaan/k210-sysctl.h>
#include <dt-bindings/clock/k210-clk.h>
struct k210_sysclk;
struct k210_clk {
int id;
struct k210_sysclk *ksc;
struct clk_hw hw;
};
struct k210_clk_cfg {
const char *name;
u8 gate_reg;
u8 gate_bit;
u8 div_reg;
u8 div_shift;
u8 div_width;
u8 div_type;
u8 mux_reg;
u8 mux_bit;
};
enum k210_clk_div_type {
K210_DIV_NONE,
K210_DIV_ONE_BASED,
K210_DIV_DOUBLE_ONE_BASED,
K210_DIV_POWER_OF_TWO,
};
#define K210_GATE(_reg, _bit) \
.gate_reg = (_reg), \
.gate_bit = (_bit)
#define K210_DIV(_reg, _shift, _width, _type) \
.div_reg = (_reg), \
.div_shift = (_shift), \
.div_width = (_width), \
.div_type = (_type)
#define K210_MUX(_reg, _bit) \
.mux_reg = (_reg), \
.mux_bit = (_bit)
static struct k210_clk_cfg k210_clk_cfgs[K210_NUM_CLKS] = {
/* Gated clocks, no mux, no divider */
[K210_CLK_CPU] = {
.name = "cpu",
K210_GATE(K210_SYSCTL_EN_CENT, 0)
},
[K210_CLK_DMA] = {
.name = "dma",
K210_GATE(K210_SYSCTL_EN_PERI, 1)
},
[K210_CLK_FFT] = {
.name = "fft",
K210_GATE(K210_SYSCTL_EN_PERI, 4)
},
[K210_CLK_GPIO] = {
.name = "gpio",
K210_GATE(K210_SYSCTL_EN_PERI, 5)
},
[K210_CLK_UART1] = {
.name = "uart1",
K210_GATE(K210_SYSCTL_EN_PERI, 16)
},
[K210_CLK_UART2] = {
.name = "uart2",
K210_GATE(K210_SYSCTL_EN_PERI, 17)
},
[K210_CLK_UART3] = {
.name = "uart3",
K210_GATE(K210_SYSCTL_EN_PERI, 18)
},
[K210_CLK_FPIOA] = {
.name = "fpioa",
K210_GATE(K210_SYSCTL_EN_PERI, 20)
},
[K210_CLK_SHA] = {
.name = "sha",
K210_GATE(K210_SYSCTL_EN_PERI, 26)
},
[K210_CLK_AES] = {
.name = "aes",
K210_GATE(K210_SYSCTL_EN_PERI, 19)
},
[K210_CLK_OTP] = {
.name = "otp",
K210_GATE(K210_SYSCTL_EN_PERI, 27)
},
[K210_CLK_RTC] = {
.name = "rtc",
K210_GATE(K210_SYSCTL_EN_PERI, 29)
},
/* Gated divider clocks */
[K210_CLK_SRAM0] = {
.name = "sram0",
K210_GATE(K210_SYSCTL_EN_CENT, 1),
K210_DIV(K210_SYSCTL_THR0, 0, 4, K210_DIV_ONE_BASED)
},
[K210_CLK_SRAM1] = {
.name = "sram1",
K210_GATE(K210_SYSCTL_EN_CENT, 2),
K210_DIV(K210_SYSCTL_THR0, 4, 4, K210_DIV_ONE_BASED)
},
[K210_CLK_ROM] = {
.name = "rom",
K210_GATE(K210_SYSCTL_EN_PERI, 0),
K210_DIV(K210_SYSCTL_THR0, 16, 4, K210_DIV_ONE_BASED)
},
[K210_CLK_DVP] = {
.name = "dvp",
K210_GATE(K210_SYSCTL_EN_PERI, 3),
K210_DIV(K210_SYSCTL_THR0, 12, 4, K210_DIV_ONE_BASED)
},
[K210_CLK_APB0] = {
.name = "apb0",
K210_GATE(K210_SYSCTL_EN_CENT, 3),
K210_DIV(K210_SYSCTL_SEL0, 3, 3, K210_DIV_ONE_BASED)
},
[K210_CLK_APB1] = {
.name = "apb1",
K210_GATE(K210_SYSCTL_EN_CENT, 4),
K210_DIV(K210_SYSCTL_SEL0, 6, 3, K210_DIV_ONE_BASED)
},
[K210_CLK_APB2] = {
.name = "apb2",
K210_GATE(K210_SYSCTL_EN_CENT, 5),
K210_DIV(K210_SYSCTL_SEL0, 9, 3, K210_DIV_ONE_BASED)
},
[K210_CLK_AI] = {
.name = "ai",
K210_GATE(K210_SYSCTL_EN_PERI, 2),
K210_DIV(K210_SYSCTL_THR0, 8, 4, K210_DIV_ONE_BASED)
},
[K210_CLK_SPI0] = {
.name = "spi0",
K210_GATE(K210_SYSCTL_EN_PERI, 6),
K210_DIV(K210_SYSCTL_THR1, 0, 8, K210_DIV_DOUBLE_ONE_BASED)
},
[K210_CLK_SPI1] = {
.name = "spi1",
K210_GATE(K210_SYSCTL_EN_PERI, 7),
K210_DIV(K210_SYSCTL_THR1, 8, 8, K210_DIV_DOUBLE_ONE_BASED)
},
[K210_CLK_SPI2] = {
.name = "spi2",
K210_GATE(K210_SYSCTL_EN_PERI, 8),
K210_DIV(K210_SYSCTL_THR1, 16, 8, K210_DIV_DOUBLE_ONE_BASED)
},
[K210_CLK_I2C0] = {
.name = "i2c0",
K210_GATE(K210_SYSCTL_EN_PERI, 13),
K210_DIV(K210_SYSCTL_THR5, 8, 8, K210_DIV_DOUBLE_ONE_BASED)
},
[K210_CLK_I2C1] = {
.name = "i2c1",
K210_GATE(K210_SYSCTL_EN_PERI, 14),
K210_DIV(K210_SYSCTL_THR5, 16, 8, K210_DIV_DOUBLE_ONE_BASED)
},
[K210_CLK_I2C2] = {
.name = "i2c2",
K210_GATE(K210_SYSCTL_EN_PERI, 15),
K210_DIV(K210_SYSCTL_THR5, 24, 8, K210_DIV_DOUBLE_ONE_BASED)
},
[K210_CLK_WDT0] = {
.name = "wdt0",
K210_GATE(K210_SYSCTL_EN_PERI, 24),
K210_DIV(K210_SYSCTL_THR6, 0, 8, K210_DIV_DOUBLE_ONE_BASED)
},
[K210_CLK_WDT1] = {
.name = "wdt1",
K210_GATE(K210_SYSCTL_EN_PERI, 25),
K210_DIV(K210_SYSCTL_THR6, 8, 8, K210_DIV_DOUBLE_ONE_BASED)
},
[K210_CLK_I2S0] = {
.name = "i2s0",