162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Serge Semin <Sergey.Semin@baikalelectronics.ru> 762306a36Sopenharmony_ci * Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Baikal-T1 CCU Dividers clock driver 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define pr_fmt(fmt) "bt1-ccu-div: " fmt 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/printk.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/clk-provider.h> 1962306a36Sopenharmony_ci#include <linux/reset-controller.h> 2062306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 2162306a36Sopenharmony_ci#include <linux/of.h> 2262306a36Sopenharmony_ci#include <linux/of_address.h> 2362306a36Sopenharmony_ci#include <linux/ioport.h> 2462306a36Sopenharmony_ci#include <linux/regmap.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <dt-bindings/clock/bt1-ccu.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "ccu-div.h" 2962306a36Sopenharmony_ci#include "ccu-rst.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define CCU_AXI_MAIN_BASE 0x030 3262306a36Sopenharmony_ci#define CCU_AXI_DDR_BASE 0x034 3362306a36Sopenharmony_ci#define CCU_AXI_SATA_BASE 0x038 3462306a36Sopenharmony_ci#define CCU_AXI_GMAC0_BASE 0x03C 3562306a36Sopenharmony_ci#define CCU_AXI_GMAC1_BASE 0x040 3662306a36Sopenharmony_ci#define CCU_AXI_XGMAC_BASE 0x044 3762306a36Sopenharmony_ci#define CCU_AXI_PCIE_M_BASE 0x048 3862306a36Sopenharmony_ci#define CCU_AXI_PCIE_S_BASE 0x04C 3962306a36Sopenharmony_ci#define CCU_AXI_USB_BASE 0x050 4062306a36Sopenharmony_ci#define CCU_AXI_HWA_BASE 0x054 4162306a36Sopenharmony_ci#define CCU_AXI_SRAM_BASE 0x058 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define CCU_SYS_SATA_REF_BASE 0x060 4462306a36Sopenharmony_ci#define CCU_SYS_APB_BASE 0x064 4562306a36Sopenharmony_ci#define CCU_SYS_GMAC0_BASE 0x068 4662306a36Sopenharmony_ci#define CCU_SYS_GMAC1_BASE 0x06C 4762306a36Sopenharmony_ci#define CCU_SYS_XGMAC_BASE 0x070 4862306a36Sopenharmony_ci#define CCU_SYS_USB_BASE 0x074 4962306a36Sopenharmony_ci#define CCU_SYS_PVT_BASE 0x078 5062306a36Sopenharmony_ci#define CCU_SYS_HWA_BASE 0x07C 5162306a36Sopenharmony_ci#define CCU_SYS_UART_BASE 0x084 5262306a36Sopenharmony_ci#define CCU_SYS_TIMER0_BASE 0x088 5362306a36Sopenharmony_ci#define CCU_SYS_TIMER1_BASE 0x08C 5462306a36Sopenharmony_ci#define CCU_SYS_TIMER2_BASE 0x090 5562306a36Sopenharmony_ci#define CCU_SYS_WDT_BASE 0x150 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define CCU_DIV_VAR_INFO(_id, _name, _pname, _base, _width, _flags, _features) \ 5862306a36Sopenharmony_ci { \ 5962306a36Sopenharmony_ci .id = _id, \ 6062306a36Sopenharmony_ci .name = _name, \ 6162306a36Sopenharmony_ci .parent_name = _pname, \ 6262306a36Sopenharmony_ci .base = _base, \ 6362306a36Sopenharmony_ci .type = CCU_DIV_VAR, \ 6462306a36Sopenharmony_ci .width = _width, \ 6562306a36Sopenharmony_ci .flags = _flags, \ 6662306a36Sopenharmony_ci .features = _features \ 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define CCU_DIV_GATE_INFO(_id, _name, _pname, _base, _divider) \ 7062306a36Sopenharmony_ci { \ 7162306a36Sopenharmony_ci .id = _id, \ 7262306a36Sopenharmony_ci .name = _name, \ 7362306a36Sopenharmony_ci .parent_name = _pname, \ 7462306a36Sopenharmony_ci .base = _base, \ 7562306a36Sopenharmony_ci .type = CCU_DIV_GATE, \ 7662306a36Sopenharmony_ci .divider = _divider \ 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define CCU_DIV_BUF_INFO(_id, _name, _pname, _base, _flags) \ 8062306a36Sopenharmony_ci { \ 8162306a36Sopenharmony_ci .id = _id, \ 8262306a36Sopenharmony_ci .name = _name, \ 8362306a36Sopenharmony_ci .parent_name = _pname, \ 8462306a36Sopenharmony_ci .base = _base, \ 8562306a36Sopenharmony_ci .type = CCU_DIV_BUF, \ 8662306a36Sopenharmony_ci .flags = _flags \ 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define CCU_DIV_FIXED_INFO(_id, _name, _pname, _divider) \ 9062306a36Sopenharmony_ci { \ 9162306a36Sopenharmony_ci .id = _id, \ 9262306a36Sopenharmony_ci .name = _name, \ 9362306a36Sopenharmony_ci .parent_name = _pname, \ 9462306a36Sopenharmony_ci .type = CCU_DIV_FIXED, \ 9562306a36Sopenharmony_ci .divider = _divider \ 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistruct ccu_div_info { 9962306a36Sopenharmony_ci unsigned int id; 10062306a36Sopenharmony_ci const char *name; 10162306a36Sopenharmony_ci const char *parent_name; 10262306a36Sopenharmony_ci unsigned int base; 10362306a36Sopenharmony_ci enum ccu_div_type type; 10462306a36Sopenharmony_ci union { 10562306a36Sopenharmony_ci unsigned int width; 10662306a36Sopenharmony_ci unsigned int divider; 10762306a36Sopenharmony_ci }; 10862306a36Sopenharmony_ci unsigned long flags; 10962306a36Sopenharmony_ci unsigned long features; 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistruct ccu_div_data { 11362306a36Sopenharmony_ci struct device_node *np; 11462306a36Sopenharmony_ci struct regmap *sys_regs; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci unsigned int divs_num; 11762306a36Sopenharmony_ci const struct ccu_div_info *divs_info; 11862306a36Sopenharmony_ci struct ccu_div **divs; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci struct ccu_rst *rsts; 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * AXI Main Interconnect (axi_main_clk) and DDR AXI-bus (axi_ddr_clk) clocks 12562306a36Sopenharmony_ci * must be left enabled in any case, since former one is responsible for 12662306a36Sopenharmony_ci * clocking a bus between CPU cores and the rest of the SoC components, while 12762306a36Sopenharmony_ci * the later is clocking the AXI-bus between DDR controller and the Main 12862306a36Sopenharmony_ci * Interconnect. So should any of these clocks get to be disabled, the system 12962306a36Sopenharmony_ci * will literally stop working. That's why we marked them as critical. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cistatic const struct ccu_div_info axi_info[] = { 13262306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_AXI_MAIN_CLK, "axi_main_clk", "pcie_clk", 13362306a36Sopenharmony_ci CCU_AXI_MAIN_BASE, 4, 13462306a36Sopenharmony_ci CLK_IS_CRITICAL, CCU_DIV_RESET_DOMAIN), 13562306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_AXI_DDR_CLK, "axi_ddr_clk", "sata_clk", 13662306a36Sopenharmony_ci CCU_AXI_DDR_BASE, 4, 13762306a36Sopenharmony_ci CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 13862306a36Sopenharmony_ci CCU_DIV_RESET_DOMAIN), 13962306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_AXI_SATA_CLK, "axi_sata_clk", "sata_clk", 14062306a36Sopenharmony_ci CCU_AXI_SATA_BASE, 4, 14162306a36Sopenharmony_ci CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 14262306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_AXI_GMAC0_CLK, "axi_gmac0_clk", "eth_clk", 14362306a36Sopenharmony_ci CCU_AXI_GMAC0_BASE, 4, 14462306a36Sopenharmony_ci CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 14562306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_AXI_GMAC1_CLK, "axi_gmac1_clk", "eth_clk", 14662306a36Sopenharmony_ci CCU_AXI_GMAC1_BASE, 4, 14762306a36Sopenharmony_ci CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 14862306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_AXI_XGMAC_CLK, "axi_xgmac_clk", "eth_clk", 14962306a36Sopenharmony_ci CCU_AXI_XGMAC_BASE, 4, 15062306a36Sopenharmony_ci CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 15162306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_AXI_PCIE_M_CLK, "axi_pcie_m_clk", "pcie_clk", 15262306a36Sopenharmony_ci CCU_AXI_PCIE_M_BASE, 4, 15362306a36Sopenharmony_ci CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 15462306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_AXI_PCIE_S_CLK, "axi_pcie_s_clk", "pcie_clk", 15562306a36Sopenharmony_ci CCU_AXI_PCIE_S_BASE, 4, 15662306a36Sopenharmony_ci CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 15762306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_AXI_USB_CLK, "axi_usb_clk", "sata_clk", 15862306a36Sopenharmony_ci CCU_AXI_USB_BASE, 4, 15962306a36Sopenharmony_ci CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 16062306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_AXI_HWA_CLK, "axi_hwa_clk", "sata_clk", 16162306a36Sopenharmony_ci CCU_AXI_HWA_BASE, 4, 16262306a36Sopenharmony_ci CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 16362306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_AXI_SRAM_CLK, "axi_sram_clk", "eth_clk", 16462306a36Sopenharmony_ci CCU_AXI_SRAM_BASE, 4, 16562306a36Sopenharmony_ci CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN) 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* 16962306a36Sopenharmony_ci * APB-bus clock is marked as critical since it's a main communication bus 17062306a36Sopenharmony_ci * for the SoC devices registers IO-operations. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_cistatic const struct ccu_div_info sys_info[] = { 17362306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_SYS_SATA_CLK, "sys_sata_clk", 17462306a36Sopenharmony_ci "sata_clk", CCU_SYS_SATA_REF_BASE, 4, 17562306a36Sopenharmony_ci CLK_SET_RATE_GATE, 17662306a36Sopenharmony_ci CCU_DIV_SKIP_ONE | CCU_DIV_LOCK_SHIFTED | 17762306a36Sopenharmony_ci CCU_DIV_RESET_DOMAIN), 17862306a36Sopenharmony_ci CCU_DIV_BUF_INFO(CCU_SYS_SATA_REF_CLK, "sys_sata_ref_clk", 17962306a36Sopenharmony_ci "sys_sata_clk", CCU_SYS_SATA_REF_BASE, 18062306a36Sopenharmony_ci CLK_SET_RATE_PARENT), 18162306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_SYS_APB_CLK, "sys_apb_clk", 18262306a36Sopenharmony_ci "pcie_clk", CCU_SYS_APB_BASE, 5, 18362306a36Sopenharmony_ci CLK_IS_CRITICAL, CCU_DIV_BASIC | CCU_DIV_RESET_DOMAIN), 18462306a36Sopenharmony_ci CCU_DIV_GATE_INFO(CCU_SYS_GMAC0_TX_CLK, "sys_gmac0_tx_clk", 18562306a36Sopenharmony_ci "eth_clk", CCU_SYS_GMAC0_BASE, 5), 18662306a36Sopenharmony_ci CCU_DIV_FIXED_INFO(CCU_SYS_GMAC0_PTP_CLK, "sys_gmac0_ptp_clk", 18762306a36Sopenharmony_ci "eth_clk", 10), 18862306a36Sopenharmony_ci CCU_DIV_GATE_INFO(CCU_SYS_GMAC1_TX_CLK, "sys_gmac1_tx_clk", 18962306a36Sopenharmony_ci "eth_clk", CCU_SYS_GMAC1_BASE, 5), 19062306a36Sopenharmony_ci CCU_DIV_FIXED_INFO(CCU_SYS_GMAC1_PTP_CLK, "sys_gmac1_ptp_clk", 19162306a36Sopenharmony_ci "eth_clk", 10), 19262306a36Sopenharmony_ci CCU_DIV_GATE_INFO(CCU_SYS_XGMAC_CLK, "sys_xgmac_clk", 19362306a36Sopenharmony_ci "eth_clk", CCU_SYS_XGMAC_BASE, 1), 19462306a36Sopenharmony_ci CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_REF_CLK, "sys_xgmac_ref_clk", 19562306a36Sopenharmony_ci "sys_xgmac_clk", 8), 19662306a36Sopenharmony_ci CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_PTP_CLK, "sys_xgmac_ptp_clk", 19762306a36Sopenharmony_ci "sys_xgmac_clk", 8), 19862306a36Sopenharmony_ci CCU_DIV_GATE_INFO(CCU_SYS_USB_CLK, "sys_usb_clk", 19962306a36Sopenharmony_ci "eth_clk", CCU_SYS_USB_BASE, 10), 20062306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_SYS_PVT_CLK, "sys_pvt_clk", 20162306a36Sopenharmony_ci "ref_clk", CCU_SYS_PVT_BASE, 5, 20262306a36Sopenharmony_ci CLK_SET_RATE_GATE, 0), 20362306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_SYS_HWA_CLK, "sys_hwa_clk", 20462306a36Sopenharmony_ci "sata_clk", CCU_SYS_HWA_BASE, 4, 20562306a36Sopenharmony_ci CLK_SET_RATE_GATE, 0), 20662306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_SYS_UART_CLK, "sys_uart_clk", 20762306a36Sopenharmony_ci "eth_clk", CCU_SYS_UART_BASE, 17, 20862306a36Sopenharmony_ci CLK_SET_RATE_GATE, 0), 20962306a36Sopenharmony_ci CCU_DIV_FIXED_INFO(CCU_SYS_I2C1_CLK, "sys_i2c1_clk", 21062306a36Sopenharmony_ci "eth_clk", 10), 21162306a36Sopenharmony_ci CCU_DIV_FIXED_INFO(CCU_SYS_I2C2_CLK, "sys_i2c2_clk", 21262306a36Sopenharmony_ci "eth_clk", 10), 21362306a36Sopenharmony_ci CCU_DIV_FIXED_INFO(CCU_SYS_GPIO_CLK, "sys_gpio_clk", 21462306a36Sopenharmony_ci "ref_clk", 25), 21562306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_SYS_TIMER0_CLK, "sys_timer0_clk", 21662306a36Sopenharmony_ci "ref_clk", CCU_SYS_TIMER0_BASE, 17, 21762306a36Sopenharmony_ci CLK_SET_RATE_GATE, CCU_DIV_BASIC), 21862306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_SYS_TIMER1_CLK, "sys_timer1_clk", 21962306a36Sopenharmony_ci "ref_clk", CCU_SYS_TIMER1_BASE, 17, 22062306a36Sopenharmony_ci CLK_SET_RATE_GATE, CCU_DIV_BASIC), 22162306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_SYS_TIMER2_CLK, "sys_timer2_clk", 22262306a36Sopenharmony_ci "ref_clk", CCU_SYS_TIMER2_BASE, 17, 22362306a36Sopenharmony_ci CLK_SET_RATE_GATE, CCU_DIV_BASIC), 22462306a36Sopenharmony_ci CCU_DIV_VAR_INFO(CCU_SYS_WDT_CLK, "sys_wdt_clk", 22562306a36Sopenharmony_ci "eth_clk", CCU_SYS_WDT_BASE, 17, 22662306a36Sopenharmony_ci CLK_SET_RATE_GATE, CCU_DIV_SKIP_ONE_TO_THREE) 22762306a36Sopenharmony_ci}; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic struct ccu_div_data *axi_data; 23062306a36Sopenharmony_cistatic struct ccu_div_data *sys_data; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic void ccu_div_set_data(struct ccu_div_data *data) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct device_node *np = data->np; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) 23762306a36Sopenharmony_ci axi_data = data; 23862306a36Sopenharmony_ci else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) 23962306a36Sopenharmony_ci sys_data = data; 24062306a36Sopenharmony_ci else 24162306a36Sopenharmony_ci pr_err("Invalid DT node '%s' specified\n", of_node_full_name(np)); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic struct ccu_div_data *ccu_div_get_data(struct device_node *np) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) 24762306a36Sopenharmony_ci return axi_data; 24862306a36Sopenharmony_ci else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) 24962306a36Sopenharmony_ci return sys_data; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci pr_err("Invalid DT node '%s' specified\n", of_node_full_name(np)); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return NULL; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic struct ccu_div *ccu_div_find_desc(struct ccu_div_data *data, 25762306a36Sopenharmony_ci unsigned int clk_id) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci int idx; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci for (idx = 0; idx < data->divs_num; ++idx) { 26262306a36Sopenharmony_ci if (data->divs_info[idx].id == clk_id) 26362306a36Sopenharmony_ci return data->divs[idx]; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic struct ccu_div_data *ccu_div_create_data(struct device_node *np) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct ccu_div_data *data; 27262306a36Sopenharmony_ci int ret; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 27562306a36Sopenharmony_ci if (!data) 27662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci data->np = np; 27962306a36Sopenharmony_ci if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) { 28062306a36Sopenharmony_ci data->divs_num = ARRAY_SIZE(axi_info); 28162306a36Sopenharmony_ci data->divs_info = axi_info; 28262306a36Sopenharmony_ci } else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) { 28362306a36Sopenharmony_ci data->divs_num = ARRAY_SIZE(sys_info); 28462306a36Sopenharmony_ci data->divs_info = sys_info; 28562306a36Sopenharmony_ci } else { 28662306a36Sopenharmony_ci pr_err("Incompatible DT node '%s' specified\n", 28762306a36Sopenharmony_ci of_node_full_name(np)); 28862306a36Sopenharmony_ci ret = -EINVAL; 28962306a36Sopenharmony_ci goto err_kfree_data; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci data->divs = kcalloc(data->divs_num, sizeof(*data->divs), GFP_KERNEL); 29362306a36Sopenharmony_ci if (!data->divs) { 29462306a36Sopenharmony_ci ret = -ENOMEM; 29562306a36Sopenharmony_ci goto err_kfree_data; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return data; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cierr_kfree_data: 30162306a36Sopenharmony_ci kfree(data); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return ERR_PTR(ret); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic void ccu_div_free_data(struct ccu_div_data *data) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci kfree(data->divs); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci kfree(data); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int ccu_div_find_sys_regs(struct ccu_div_data *data) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci data->sys_regs = syscon_node_to_regmap(data->np->parent); 31662306a36Sopenharmony_ci if (IS_ERR(data->sys_regs)) { 31762306a36Sopenharmony_ci pr_err("Failed to find syscon regs for '%s'\n", 31862306a36Sopenharmony_ci of_node_full_name(data->np)); 31962306a36Sopenharmony_ci return PTR_ERR(data->sys_regs); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic struct clk_hw *ccu_div_of_clk_hw_get(struct of_phandle_args *clkspec, 32662306a36Sopenharmony_ci void *priv) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct ccu_div_data *data = priv; 32962306a36Sopenharmony_ci struct ccu_div *div; 33062306a36Sopenharmony_ci unsigned int clk_id; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci clk_id = clkspec->args[0]; 33362306a36Sopenharmony_ci div = ccu_div_find_desc(data, clk_id); 33462306a36Sopenharmony_ci if (IS_ERR(div)) { 33562306a36Sopenharmony_ci if (div != ERR_PTR(-EPROBE_DEFER)) 33662306a36Sopenharmony_ci pr_info("Invalid clock ID %d specified\n", clk_id); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return ERR_CAST(div); 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return ccu_div_get_clk_hw(div); 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int ccu_div_clk_register(struct ccu_div_data *data, bool defer) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci int idx, ret; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci for (idx = 0; idx < data->divs_num; ++idx) { 34962306a36Sopenharmony_ci const struct ccu_div_info *info = &data->divs_info[idx]; 35062306a36Sopenharmony_ci struct ccu_div_init_data init = {0}; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (!!(info->features & CCU_DIV_BASIC) ^ defer) { 35362306a36Sopenharmony_ci if (!data->divs[idx]) 35462306a36Sopenharmony_ci data->divs[idx] = ERR_PTR(-EPROBE_DEFER); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci continue; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci init.id = info->id; 36062306a36Sopenharmony_ci init.name = info->name; 36162306a36Sopenharmony_ci init.parent_name = info->parent_name; 36262306a36Sopenharmony_ci init.np = data->np; 36362306a36Sopenharmony_ci init.type = info->type; 36462306a36Sopenharmony_ci init.flags = info->flags; 36562306a36Sopenharmony_ci init.features = info->features; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (init.type == CCU_DIV_VAR) { 36862306a36Sopenharmony_ci init.base = info->base; 36962306a36Sopenharmony_ci init.sys_regs = data->sys_regs; 37062306a36Sopenharmony_ci init.width = info->width; 37162306a36Sopenharmony_ci } else if (init.type == CCU_DIV_GATE) { 37262306a36Sopenharmony_ci init.base = info->base; 37362306a36Sopenharmony_ci init.sys_regs = data->sys_regs; 37462306a36Sopenharmony_ci init.divider = info->divider; 37562306a36Sopenharmony_ci } else if (init.type == CCU_DIV_BUF) { 37662306a36Sopenharmony_ci init.base = info->base; 37762306a36Sopenharmony_ci init.sys_regs = data->sys_regs; 37862306a36Sopenharmony_ci } else { 37962306a36Sopenharmony_ci init.divider = info->divider; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci data->divs[idx] = ccu_div_hw_register(&init); 38362306a36Sopenharmony_ci if (IS_ERR(data->divs[idx])) { 38462306a36Sopenharmony_ci ret = PTR_ERR(data->divs[idx]); 38562306a36Sopenharmony_ci pr_err("Couldn't register divider '%s' hw\n", 38662306a36Sopenharmony_ci init.name); 38762306a36Sopenharmony_ci goto err_hw_unregister; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return 0; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cierr_hw_unregister: 39462306a36Sopenharmony_ci for (--idx; idx >= 0; --idx) { 39562306a36Sopenharmony_ci if (!!(data->divs_info[idx].features & CCU_DIV_BASIC) ^ defer) 39662306a36Sopenharmony_ci continue; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci ccu_div_hw_unregister(data->divs[idx]); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci return ret; 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic void ccu_div_clk_unregister(struct ccu_div_data *data, bool defer) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci int idx; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* Uninstall only the clocks registered on the specfied stage */ 40962306a36Sopenharmony_ci for (idx = 0; idx < data->divs_num; ++idx) { 41062306a36Sopenharmony_ci if (!!(data->divs_info[idx].features & CCU_DIV_BASIC) ^ defer) 41162306a36Sopenharmony_ci continue; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci ccu_div_hw_unregister(data->divs[idx]); 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int ccu_div_of_register(struct ccu_div_data *data) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci int ret; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci ret = of_clk_add_hw_provider(data->np, ccu_div_of_clk_hw_get, data); 42262306a36Sopenharmony_ci if (ret) { 42362306a36Sopenharmony_ci pr_err("Couldn't register dividers '%s' clock provider\n", 42462306a36Sopenharmony_ci of_node_full_name(data->np)); 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return ret; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int ccu_div_rst_register(struct ccu_div_data *data) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct ccu_rst_init_data init = {0}; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci init.sys_regs = data->sys_regs; 43562306a36Sopenharmony_ci init.np = data->np; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci data->rsts = ccu_rst_hw_register(&init); 43862306a36Sopenharmony_ci if (IS_ERR(data->rsts)) { 43962306a36Sopenharmony_ci pr_err("Couldn't register divider '%s' reset controller\n", 44062306a36Sopenharmony_ci of_node_full_name(data->np)); 44162306a36Sopenharmony_ci return PTR_ERR(data->rsts); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic int ccu_div_probe(struct platform_device *pdev) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct ccu_div_data *data; 45062306a36Sopenharmony_ci int ret; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci data = ccu_div_get_data(dev_of_node(&pdev->dev)); 45362306a36Sopenharmony_ci if (!data) 45462306a36Sopenharmony_ci return -EINVAL; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci ret = ccu_div_clk_register(data, false); 45762306a36Sopenharmony_ci if (ret) 45862306a36Sopenharmony_ci return ret; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ret = ccu_div_rst_register(data); 46162306a36Sopenharmony_ci if (ret) 46262306a36Sopenharmony_ci goto err_clk_unregister; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cierr_clk_unregister: 46762306a36Sopenharmony_ci ccu_div_clk_unregister(data, false); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return ret; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic const struct of_device_id ccu_div_of_match[] = { 47362306a36Sopenharmony_ci { .compatible = "baikal,bt1-ccu-axi" }, 47462306a36Sopenharmony_ci { .compatible = "baikal,bt1-ccu-sys" }, 47562306a36Sopenharmony_ci { } 47662306a36Sopenharmony_ci}; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic struct platform_driver ccu_div_driver = { 47962306a36Sopenharmony_ci .probe = ccu_div_probe, 48062306a36Sopenharmony_ci .driver = { 48162306a36Sopenharmony_ci .name = "clk-ccu-div", 48262306a36Sopenharmony_ci .of_match_table = ccu_div_of_match, 48362306a36Sopenharmony_ci .suppress_bind_attrs = true, 48462306a36Sopenharmony_ci }, 48562306a36Sopenharmony_ci}; 48662306a36Sopenharmony_cibuiltin_platform_driver(ccu_div_driver); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic __init void ccu_div_init(struct device_node *np) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct ccu_div_data *data; 49162306a36Sopenharmony_ci int ret; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci data = ccu_div_create_data(np); 49462306a36Sopenharmony_ci if (IS_ERR(data)) 49562306a36Sopenharmony_ci return; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci ret = ccu_div_find_sys_regs(data); 49862306a36Sopenharmony_ci if (ret) 49962306a36Sopenharmony_ci goto err_free_data; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci ret = ccu_div_clk_register(data, true); 50262306a36Sopenharmony_ci if (ret) 50362306a36Sopenharmony_ci goto err_free_data; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci ret = ccu_div_of_register(data); 50662306a36Sopenharmony_ci if (ret) 50762306a36Sopenharmony_ci goto err_clk_unregister; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci ccu_div_set_data(data); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cierr_clk_unregister: 51462306a36Sopenharmony_ci ccu_div_clk_unregister(data, true); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cierr_free_data: 51762306a36Sopenharmony_ci ccu_div_free_data(data); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ciCLK_OF_DECLARE_DRIVER(ccu_axi, "baikal,bt1-ccu-axi", ccu_div_init); 52062306a36Sopenharmony_ciCLK_OF_DECLARE_DRIVER(ccu_sys, "baikal,bt1-ccu-sys", ccu_div_init); 521