cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

clk-ccu-div.c (13392B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
      4 *
      5 * Authors:
      6 *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
      7 *   Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
      8 *
      9 * Baikal-T1 CCU Dividers clock driver
     10 */
     11
     12#define pr_fmt(fmt) "bt1-ccu-div: " fmt
     13
     14#include <linux/kernel.h>
     15#include <linux/printk.h>
     16#include <linux/slab.h>
     17#include <linux/clk-provider.h>
     18#include <linux/reset-controller.h>
     19#include <linux/mfd/syscon.h>
     20#include <linux/of.h>
     21#include <linux/of_address.h>
     22#include <linux/of_platform.h>
     23#include <linux/ioport.h>
     24#include <linux/regmap.h>
     25
     26#include <dt-bindings/clock/bt1-ccu.h>
     27#include <dt-bindings/reset/bt1-ccu.h>
     28
     29#include "ccu-div.h"
     30
     31#define CCU_AXI_MAIN_BASE		0x030
     32#define CCU_AXI_DDR_BASE		0x034
     33#define CCU_AXI_SATA_BASE		0x038
     34#define CCU_AXI_GMAC0_BASE		0x03C
     35#define CCU_AXI_GMAC1_BASE		0x040
     36#define CCU_AXI_XGMAC_BASE		0x044
     37#define CCU_AXI_PCIE_M_BASE		0x048
     38#define CCU_AXI_PCIE_S_BASE		0x04C
     39#define CCU_AXI_USB_BASE		0x050
     40#define CCU_AXI_HWA_BASE		0x054
     41#define CCU_AXI_SRAM_BASE		0x058
     42
     43#define CCU_SYS_SATA_REF_BASE		0x060
     44#define CCU_SYS_APB_BASE		0x064
     45#define CCU_SYS_GMAC0_BASE		0x068
     46#define CCU_SYS_GMAC1_BASE		0x06C
     47#define CCU_SYS_XGMAC_BASE		0x070
     48#define CCU_SYS_USB_BASE		0x074
     49#define CCU_SYS_PVT_BASE		0x078
     50#define CCU_SYS_HWA_BASE		0x07C
     51#define CCU_SYS_UART_BASE		0x084
     52#define CCU_SYS_TIMER0_BASE		0x088
     53#define CCU_SYS_TIMER1_BASE		0x08C
     54#define CCU_SYS_TIMER2_BASE		0x090
     55#define CCU_SYS_WDT_BASE		0x150
     56
     57#define CCU_DIV_VAR_INFO(_id, _name, _pname, _base, _width, _flags, _features) \
     58	{								\
     59		.id = _id,						\
     60		.name = _name,						\
     61		.parent_name = _pname,					\
     62		.base = _base,						\
     63		.type = CCU_DIV_VAR,					\
     64		.width = _width,					\
     65		.flags = _flags,					\
     66		.features = _features					\
     67	}
     68
     69#define CCU_DIV_GATE_INFO(_id, _name, _pname, _base, _divider)	\
     70	{							\
     71		.id = _id,					\
     72		.name = _name,					\
     73		.parent_name = _pname,				\
     74		.base = _base,					\
     75		.type = CCU_DIV_GATE,				\
     76		.divider = _divider				\
     77	}
     78
     79#define CCU_DIV_FIXED_INFO(_id, _name, _pname, _divider)	\
     80	{							\
     81		.id = _id,					\
     82		.name = _name,					\
     83		.parent_name = _pname,				\
     84		.type = CCU_DIV_FIXED,				\
     85		.divider = _divider				\
     86	}
     87
     88#define CCU_DIV_RST_MAP(_rst_id, _clk_id)	\
     89	{					\
     90		.rst_id = _rst_id,		\
     91		.clk_id = _clk_id		\
     92	}
     93
     94struct ccu_div_info {
     95	unsigned int id;
     96	const char *name;
     97	const char *parent_name;
     98	unsigned int base;
     99	enum ccu_div_type type;
    100	union {
    101		unsigned int width;
    102		unsigned int divider;
    103	};
    104	unsigned long flags;
    105	unsigned long features;
    106};
    107
    108struct ccu_div_rst_map {
    109	unsigned int rst_id;
    110	unsigned int clk_id;
    111};
    112
    113struct ccu_div_data {
    114	struct device_node *np;
    115	struct regmap *sys_regs;
    116
    117	unsigned int divs_num;
    118	const struct ccu_div_info *divs_info;
    119	struct ccu_div **divs;
    120
    121	unsigned int rst_num;
    122	const struct ccu_div_rst_map *rst_map;
    123	struct reset_controller_dev rcdev;
    124};
    125#define to_ccu_div_data(_rcdev) container_of(_rcdev, struct ccu_div_data, rcdev)
    126
    127/*
    128 * AXI Main Interconnect (axi_main_clk) and DDR AXI-bus (axi_ddr_clk) clocks
    129 * must be left enabled in any case, since former one is responsible for
    130 * clocking a bus between CPU cores and the rest of the SoC components, while
    131 * the later is clocking the AXI-bus between DDR controller and the Main
    132 * Interconnect. So should any of these clocks get to be disabled, the system
    133 * will literally stop working. That's why we marked them as critical.
    134 */
    135static const struct ccu_div_info axi_info[] = {
    136	CCU_DIV_VAR_INFO(CCU_AXI_MAIN_CLK, "axi_main_clk", "pcie_clk",
    137			 CCU_AXI_MAIN_BASE, 4,
    138			 CLK_IS_CRITICAL, CCU_DIV_RESET_DOMAIN),
    139	CCU_DIV_VAR_INFO(CCU_AXI_DDR_CLK, "axi_ddr_clk", "sata_clk",
    140			 CCU_AXI_DDR_BASE, 4,
    141			 CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
    142			 CCU_DIV_RESET_DOMAIN),
    143	CCU_DIV_VAR_INFO(CCU_AXI_SATA_CLK, "axi_sata_clk", "sata_clk",
    144			 CCU_AXI_SATA_BASE, 4,
    145			 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
    146	CCU_DIV_VAR_INFO(CCU_AXI_GMAC0_CLK, "axi_gmac0_clk", "eth_clk",
    147			 CCU_AXI_GMAC0_BASE, 4,
    148			 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
    149	CCU_DIV_VAR_INFO(CCU_AXI_GMAC1_CLK, "axi_gmac1_clk", "eth_clk",
    150			 CCU_AXI_GMAC1_BASE, 4,
    151			 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
    152	CCU_DIV_VAR_INFO(CCU_AXI_XGMAC_CLK, "axi_xgmac_clk", "eth_clk",
    153			 CCU_AXI_XGMAC_BASE, 4,
    154			 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
    155	CCU_DIV_VAR_INFO(CCU_AXI_PCIE_M_CLK, "axi_pcie_m_clk", "pcie_clk",
    156			 CCU_AXI_PCIE_M_BASE, 4,
    157			 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
    158	CCU_DIV_VAR_INFO(CCU_AXI_PCIE_S_CLK, "axi_pcie_s_clk", "pcie_clk",
    159			 CCU_AXI_PCIE_S_BASE, 4,
    160			 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
    161	CCU_DIV_VAR_INFO(CCU_AXI_USB_CLK, "axi_usb_clk", "sata_clk",
    162			 CCU_AXI_USB_BASE, 4,
    163			 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
    164	CCU_DIV_VAR_INFO(CCU_AXI_HWA_CLK, "axi_hwa_clk", "sata_clk",
    165			 CCU_AXI_HWA_BASE, 4,
    166			 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
    167	CCU_DIV_VAR_INFO(CCU_AXI_SRAM_CLK, "axi_sram_clk", "eth_clk",
    168			 CCU_AXI_SRAM_BASE, 4,
    169			 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN)
    170};
    171
    172static const struct ccu_div_rst_map axi_rst_map[] = {
    173	CCU_DIV_RST_MAP(CCU_AXI_MAIN_RST, CCU_AXI_MAIN_CLK),
    174	CCU_DIV_RST_MAP(CCU_AXI_DDR_RST, CCU_AXI_DDR_CLK),
    175	CCU_DIV_RST_MAP(CCU_AXI_SATA_RST, CCU_AXI_SATA_CLK),
    176	CCU_DIV_RST_MAP(CCU_AXI_GMAC0_RST, CCU_AXI_GMAC0_CLK),
    177	CCU_DIV_RST_MAP(CCU_AXI_GMAC1_RST, CCU_AXI_GMAC1_CLK),
    178	CCU_DIV_RST_MAP(CCU_AXI_XGMAC_RST, CCU_AXI_XGMAC_CLK),
    179	CCU_DIV_RST_MAP(CCU_AXI_PCIE_M_RST, CCU_AXI_PCIE_M_CLK),
    180	CCU_DIV_RST_MAP(CCU_AXI_PCIE_S_RST, CCU_AXI_PCIE_S_CLK),
    181	CCU_DIV_RST_MAP(CCU_AXI_USB_RST, CCU_AXI_USB_CLK),
    182	CCU_DIV_RST_MAP(CCU_AXI_HWA_RST, CCU_AXI_HWA_CLK),
    183	CCU_DIV_RST_MAP(CCU_AXI_SRAM_RST, CCU_AXI_SRAM_CLK)
    184};
    185
    186/*
    187 * APB-bus clock is marked as critical since it's a main communication bus
    188 * for the SoC devices registers IO-operations.
    189 */
    190static const struct ccu_div_info sys_info[] = {
    191	CCU_DIV_VAR_INFO(CCU_SYS_SATA_REF_CLK, "sys_sata_ref_clk",
    192			 "sata_clk", CCU_SYS_SATA_REF_BASE, 4,
    193			 CLK_SET_RATE_GATE,
    194			 CCU_DIV_SKIP_ONE | CCU_DIV_LOCK_SHIFTED |
    195			 CCU_DIV_RESET_DOMAIN),
    196	CCU_DIV_VAR_INFO(CCU_SYS_APB_CLK, "sys_apb_clk",
    197			 "pcie_clk", CCU_SYS_APB_BASE, 5,
    198			 CLK_IS_CRITICAL, CCU_DIV_RESET_DOMAIN),
    199	CCU_DIV_GATE_INFO(CCU_SYS_GMAC0_TX_CLK, "sys_gmac0_tx_clk",
    200			  "eth_clk", CCU_SYS_GMAC0_BASE, 5),
    201	CCU_DIV_FIXED_INFO(CCU_SYS_GMAC0_PTP_CLK, "sys_gmac0_ptp_clk",
    202			   "eth_clk", 10),
    203	CCU_DIV_GATE_INFO(CCU_SYS_GMAC1_TX_CLK, "sys_gmac1_tx_clk",
    204			  "eth_clk", CCU_SYS_GMAC1_BASE, 5),
    205	CCU_DIV_FIXED_INFO(CCU_SYS_GMAC1_PTP_CLK, "sys_gmac1_ptp_clk",
    206			   "eth_clk", 10),
    207	CCU_DIV_GATE_INFO(CCU_SYS_XGMAC_REF_CLK, "sys_xgmac_ref_clk",
    208			  "eth_clk", CCU_SYS_XGMAC_BASE, 8),
    209	CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_PTP_CLK, "sys_xgmac_ptp_clk",
    210			   "eth_clk", 10),
    211	CCU_DIV_GATE_INFO(CCU_SYS_USB_CLK, "sys_usb_clk",
    212			  "eth_clk", CCU_SYS_USB_BASE, 10),
    213	CCU_DIV_VAR_INFO(CCU_SYS_PVT_CLK, "sys_pvt_clk",
    214			 "ref_clk", CCU_SYS_PVT_BASE, 5,
    215			 CLK_SET_RATE_GATE, 0),
    216	CCU_DIV_VAR_INFO(CCU_SYS_HWA_CLK, "sys_hwa_clk",
    217			 "sata_clk", CCU_SYS_HWA_BASE, 4,
    218			 CLK_SET_RATE_GATE, 0),
    219	CCU_DIV_VAR_INFO(CCU_SYS_UART_CLK, "sys_uart_clk",
    220			 "eth_clk", CCU_SYS_UART_BASE, 17,
    221			 CLK_SET_RATE_GATE, 0),
    222	CCU_DIV_FIXED_INFO(CCU_SYS_I2C1_CLK, "sys_i2c1_clk",
    223			   "eth_clk", 10),
    224	CCU_DIV_FIXED_INFO(CCU_SYS_I2C2_CLK, "sys_i2c2_clk",
    225			   "eth_clk", 10),
    226	CCU_DIV_FIXED_INFO(CCU_SYS_GPIO_CLK, "sys_gpio_clk",
    227			   "ref_clk", 25),
    228	CCU_DIV_VAR_INFO(CCU_SYS_TIMER0_CLK, "sys_timer0_clk",
    229			 "ref_clk", CCU_SYS_TIMER0_BASE, 17,
    230			 CLK_SET_RATE_GATE, 0),
    231	CCU_DIV_VAR_INFO(CCU_SYS_TIMER1_CLK, "sys_timer1_clk",
    232			 "ref_clk", CCU_SYS_TIMER1_BASE, 17,
    233			 CLK_SET_RATE_GATE, 0),
    234	CCU_DIV_VAR_INFO(CCU_SYS_TIMER2_CLK, "sys_timer2_clk",
    235			 "ref_clk", CCU_SYS_TIMER2_BASE, 17,
    236			 CLK_SET_RATE_GATE, 0),
    237	CCU_DIV_VAR_INFO(CCU_SYS_WDT_CLK, "sys_wdt_clk",
    238			 "eth_clk", CCU_SYS_WDT_BASE, 17,
    239			 CLK_SET_RATE_GATE, CCU_DIV_SKIP_ONE_TO_THREE)
    240};
    241
    242static const struct ccu_div_rst_map sys_rst_map[] = {
    243	CCU_DIV_RST_MAP(CCU_SYS_SATA_REF_RST, CCU_SYS_SATA_REF_CLK),
    244	CCU_DIV_RST_MAP(CCU_SYS_APB_RST, CCU_SYS_APB_CLK),
    245};
    246
    247static struct ccu_div *ccu_div_find_desc(struct ccu_div_data *data,
    248					 unsigned int clk_id)
    249{
    250	struct ccu_div *div;
    251	int idx;
    252
    253	for (idx = 0; idx < data->divs_num; ++idx) {
    254		div = data->divs[idx];
    255		if (div && div->id == clk_id)
    256			return div;
    257	}
    258
    259	return ERR_PTR(-EINVAL);
    260}
    261
    262static int ccu_div_reset(struct reset_controller_dev *rcdev,
    263			 unsigned long rst_id)
    264{
    265	struct ccu_div_data *data = to_ccu_div_data(rcdev);
    266	const struct ccu_div_rst_map *map;
    267	struct ccu_div *div;
    268	int idx, ret;
    269
    270	for (idx = 0, map = data->rst_map; idx < data->rst_num; ++idx, ++map) {
    271		if (map->rst_id == rst_id)
    272			break;
    273	}
    274	if (idx == data->rst_num) {
    275		pr_err("Invalid reset ID %lu specified\n", rst_id);
    276		return -EINVAL;
    277	}
    278
    279	div = ccu_div_find_desc(data, map->clk_id);
    280	if (IS_ERR(div)) {
    281		pr_err("Invalid clock ID %d in mapping\n", map->clk_id);
    282		return PTR_ERR(div);
    283	}
    284
    285	ret = ccu_div_reset_domain(div);
    286	if (ret) {
    287		pr_err("Reset isn't supported by divider %s\n",
    288			clk_hw_get_name(ccu_div_get_clk_hw(div)));
    289	}
    290
    291	return ret;
    292}
    293
    294static const struct reset_control_ops ccu_div_rst_ops = {
    295	.reset = ccu_div_reset,
    296};
    297
    298static struct ccu_div_data *ccu_div_create_data(struct device_node *np)
    299{
    300	struct ccu_div_data *data;
    301	int ret;
    302
    303	data = kzalloc(sizeof(*data), GFP_KERNEL);
    304	if (!data)
    305		return ERR_PTR(-ENOMEM);
    306
    307	data->np = np;
    308	if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) {
    309		data->divs_num = ARRAY_SIZE(axi_info);
    310		data->divs_info = axi_info;
    311		data->rst_num = ARRAY_SIZE(axi_rst_map);
    312		data->rst_map = axi_rst_map;
    313	} else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) {
    314		data->divs_num = ARRAY_SIZE(sys_info);
    315		data->divs_info = sys_info;
    316		data->rst_num = ARRAY_SIZE(sys_rst_map);
    317		data->rst_map = sys_rst_map;
    318	} else {
    319		pr_err("Incompatible DT node '%s' specified\n",
    320			of_node_full_name(np));
    321		ret = -EINVAL;
    322		goto err_kfree_data;
    323	}
    324
    325	data->divs = kcalloc(data->divs_num, sizeof(*data->divs), GFP_KERNEL);
    326	if (!data->divs) {
    327		ret = -ENOMEM;
    328		goto err_kfree_data;
    329	}
    330
    331	return data;
    332
    333err_kfree_data:
    334	kfree(data);
    335
    336	return ERR_PTR(ret);
    337}
    338
    339static void ccu_div_free_data(struct ccu_div_data *data)
    340{
    341	kfree(data->divs);
    342
    343	kfree(data);
    344}
    345
    346static int ccu_div_find_sys_regs(struct ccu_div_data *data)
    347{
    348	data->sys_regs = syscon_node_to_regmap(data->np->parent);
    349	if (IS_ERR(data->sys_regs)) {
    350		pr_err("Failed to find syscon regs for '%s'\n",
    351			of_node_full_name(data->np));
    352		return PTR_ERR(data->sys_regs);
    353	}
    354
    355	return 0;
    356}
    357
    358static struct clk_hw *ccu_div_of_clk_hw_get(struct of_phandle_args *clkspec,
    359					    void *priv)
    360{
    361	struct ccu_div_data *data = priv;
    362	struct ccu_div *div;
    363	unsigned int clk_id;
    364
    365	clk_id = clkspec->args[0];
    366	div = ccu_div_find_desc(data, clk_id);
    367	if (IS_ERR(div)) {
    368		pr_info("Invalid clock ID %d specified\n", clk_id);
    369		return ERR_CAST(div);
    370	}
    371
    372	return ccu_div_get_clk_hw(div);
    373}
    374
    375static int ccu_div_clk_register(struct ccu_div_data *data)
    376{
    377	int idx, ret;
    378
    379	for (idx = 0; idx < data->divs_num; ++idx) {
    380		const struct ccu_div_info *info = &data->divs_info[idx];
    381		struct ccu_div_init_data init = {0};
    382
    383		init.id = info->id;
    384		init.name = info->name;
    385		init.parent_name = info->parent_name;
    386		init.np = data->np;
    387		init.type = info->type;
    388		init.flags = info->flags;
    389		init.features = info->features;
    390
    391		if (init.type == CCU_DIV_VAR) {
    392			init.base = info->base;
    393			init.sys_regs = data->sys_regs;
    394			init.width = info->width;
    395		} else if (init.type == CCU_DIV_GATE) {
    396			init.base = info->base;
    397			init.sys_regs = data->sys_regs;
    398			init.divider = info->divider;
    399		} else {
    400			init.divider = info->divider;
    401		}
    402
    403		data->divs[idx] = ccu_div_hw_register(&init);
    404		if (IS_ERR(data->divs[idx])) {
    405			ret = PTR_ERR(data->divs[idx]);
    406			pr_err("Couldn't register divider '%s' hw\n",
    407				init.name);
    408			goto err_hw_unregister;
    409		}
    410	}
    411
    412	ret = of_clk_add_hw_provider(data->np, ccu_div_of_clk_hw_get, data);
    413	if (ret) {
    414		pr_err("Couldn't register dividers '%s' clock provider\n",
    415			of_node_full_name(data->np));
    416		goto err_hw_unregister;
    417	}
    418
    419	return 0;
    420
    421err_hw_unregister:
    422	for (--idx; idx >= 0; --idx)
    423		ccu_div_hw_unregister(data->divs[idx]);
    424
    425	return ret;
    426}
    427
    428static void ccu_div_clk_unregister(struct ccu_div_data *data)
    429{
    430	int idx;
    431
    432	of_clk_del_provider(data->np);
    433
    434	for (idx = 0; idx < data->divs_num; ++idx)
    435		ccu_div_hw_unregister(data->divs[idx]);
    436}
    437
    438static int ccu_div_rst_register(struct ccu_div_data *data)
    439{
    440	int ret;
    441
    442	data->rcdev.ops = &ccu_div_rst_ops;
    443	data->rcdev.of_node = data->np;
    444	data->rcdev.nr_resets = data->rst_num;
    445
    446	ret = reset_controller_register(&data->rcdev);
    447	if (ret)
    448		pr_err("Couldn't register divider '%s' reset controller\n",
    449			of_node_full_name(data->np));
    450
    451	return ret;
    452}
    453
    454static void ccu_div_init(struct device_node *np)
    455{
    456	struct ccu_div_data *data;
    457	int ret;
    458
    459	data = ccu_div_create_data(np);
    460	if (IS_ERR(data))
    461		return;
    462
    463	ret = ccu_div_find_sys_regs(data);
    464	if (ret)
    465		goto err_free_data;
    466
    467	ret = ccu_div_clk_register(data);
    468	if (ret)
    469		goto err_free_data;
    470
    471	ret = ccu_div_rst_register(data);
    472	if (ret)
    473		goto err_clk_unregister;
    474
    475	return;
    476
    477err_clk_unregister:
    478	ccu_div_clk_unregister(data);
    479
    480err_free_data:
    481	ccu_div_free_data(data);
    482}
    483
    484CLK_OF_DECLARE(ccu_axi, "baikal,bt1-ccu-axi", ccu_div_init);
    485CLK_OF_DECLARE(ccu_sys, "baikal,bt1-ccu-sys", ccu_div_init);