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/platform_device.h> 16#include <linux/printk.h> 17#include <linux/slab.h> 18#include <linux/clk-provider.h> 19#include <linux/reset-controller.h> 20#include <linux/mfd/syscon.h> 21#include <linux/of.h> 22#include <linux/of_address.h> 23#include <linux/ioport.h> 24#include <linux/regmap.h> 25 26#include <dt-bindings/clock/bt1-ccu.h> 27 28#include "ccu-div.h" 29#include "ccu-rst.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_BUF_INFO(_id, _name, _pname, _base, _flags) \ 80 { \ 81 .id = _id, \ 82 .name = _name, \ 83 .parent_name = _pname, \ 84 .base = _base, \ 85 .type = CCU_DIV_BUF, \ 86 .flags = _flags \ 87 } 88 89#define CCU_DIV_FIXED_INFO(_id, _name, _pname, _divider) \ 90 { \ 91 .id = _id, \ 92 .name = _name, \ 93 .parent_name = _pname, \ 94 .type = CCU_DIV_FIXED, \ 95 .divider = _divider \ 96 } 97 98struct ccu_div_info { 99 unsigned int id; 100 const char *name; 101 const char *parent_name; 102 unsigned int base; 103 enum ccu_div_type type; 104 union { 105 unsigned int width; 106 unsigned int divider; 107 }; 108 unsigned long flags; 109 unsigned long features; 110}; 111 112struct ccu_div_data { 113 struct device_node *np; 114 struct regmap *sys_regs; 115 116 unsigned int divs_num; 117 const struct ccu_div_info *divs_info; 118 struct ccu_div **divs; 119 120 struct ccu_rst *rsts; 121}; 122 123/* 124 * AXI Main Interconnect (axi_main_clk) and DDR AXI-bus (axi_ddr_clk) clocks 125 * must be left enabled in any case, since former one is responsible for 126 * clocking a bus between CPU cores and the rest of the SoC components, while 127 * the later is clocking the AXI-bus between DDR controller and the Main 128 * Interconnect. So should any of these clocks get to be disabled, the system 129 * will literally stop working. That's why we marked them as critical. 130 */ 131static const struct ccu_div_info axi_info[] = { 132 CCU_DIV_VAR_INFO(CCU_AXI_MAIN_CLK, "axi_main_clk", "pcie_clk", 133 CCU_AXI_MAIN_BASE, 4, 134 CLK_IS_CRITICAL, CCU_DIV_RESET_DOMAIN), 135 CCU_DIV_VAR_INFO(CCU_AXI_DDR_CLK, "axi_ddr_clk", "sata_clk", 136 CCU_AXI_DDR_BASE, 4, 137 CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 138 CCU_DIV_RESET_DOMAIN), 139 CCU_DIV_VAR_INFO(CCU_AXI_SATA_CLK, "axi_sata_clk", "sata_clk", 140 CCU_AXI_SATA_BASE, 4, 141 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 142 CCU_DIV_VAR_INFO(CCU_AXI_GMAC0_CLK, "axi_gmac0_clk", "eth_clk", 143 CCU_AXI_GMAC0_BASE, 4, 144 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 145 CCU_DIV_VAR_INFO(CCU_AXI_GMAC1_CLK, "axi_gmac1_clk", "eth_clk", 146 CCU_AXI_GMAC1_BASE, 4, 147 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 148 CCU_DIV_VAR_INFO(CCU_AXI_XGMAC_CLK, "axi_xgmac_clk", "eth_clk", 149 CCU_AXI_XGMAC_BASE, 4, 150 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 151 CCU_DIV_VAR_INFO(CCU_AXI_PCIE_M_CLK, "axi_pcie_m_clk", "pcie_clk", 152 CCU_AXI_PCIE_M_BASE, 4, 153 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 154 CCU_DIV_VAR_INFO(CCU_AXI_PCIE_S_CLK, "axi_pcie_s_clk", "pcie_clk", 155 CCU_AXI_PCIE_S_BASE, 4, 156 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 157 CCU_DIV_VAR_INFO(CCU_AXI_USB_CLK, "axi_usb_clk", "sata_clk", 158 CCU_AXI_USB_BASE, 4, 159 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 160 CCU_DIV_VAR_INFO(CCU_AXI_HWA_CLK, "axi_hwa_clk", "sata_clk", 161 CCU_AXI_HWA_BASE, 4, 162 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN), 163 CCU_DIV_VAR_INFO(CCU_AXI_SRAM_CLK, "axi_sram_clk", "eth_clk", 164 CCU_AXI_SRAM_BASE, 4, 165 CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN) 166}; 167 168/* 169 * APB-bus clock is marked as critical since it's a main communication bus 170 * for the SoC devices registers IO-operations. 171 */ 172static const struct ccu_div_info sys_info[] = { 173 CCU_DIV_VAR_INFO(CCU_SYS_SATA_CLK, "sys_sata_clk", 174 "sata_clk", CCU_SYS_SATA_REF_BASE, 4, 175 CLK_SET_RATE_GATE, 176 CCU_DIV_SKIP_ONE | CCU_DIV_LOCK_SHIFTED | 177 CCU_DIV_RESET_DOMAIN), 178 CCU_DIV_BUF_INFO(CCU_SYS_SATA_REF_CLK, "sys_sata_ref_clk", 179 "sys_sata_clk", CCU_SYS_SATA_REF_BASE, 180 CLK_SET_RATE_PARENT), 181 CCU_DIV_VAR_INFO(CCU_SYS_APB_CLK, "sys_apb_clk", 182 "pcie_clk", CCU_SYS_APB_BASE, 5, 183 CLK_IS_CRITICAL, CCU_DIV_BASIC | CCU_DIV_RESET_DOMAIN), 184 CCU_DIV_GATE_INFO(CCU_SYS_GMAC0_TX_CLK, "sys_gmac0_tx_clk", 185 "eth_clk", CCU_SYS_GMAC0_BASE, 5), 186 CCU_DIV_FIXED_INFO(CCU_SYS_GMAC0_PTP_CLK, "sys_gmac0_ptp_clk", 187 "eth_clk", 10), 188 CCU_DIV_GATE_INFO(CCU_SYS_GMAC1_TX_CLK, "sys_gmac1_tx_clk", 189 "eth_clk", CCU_SYS_GMAC1_BASE, 5), 190 CCU_DIV_FIXED_INFO(CCU_SYS_GMAC1_PTP_CLK, "sys_gmac1_ptp_clk", 191 "eth_clk", 10), 192 CCU_DIV_GATE_INFO(CCU_SYS_XGMAC_CLK, "sys_xgmac_clk", 193 "eth_clk", CCU_SYS_XGMAC_BASE, 1), 194 CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_REF_CLK, "sys_xgmac_ref_clk", 195 "sys_xgmac_clk", 8), 196 CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_PTP_CLK, "sys_xgmac_ptp_clk", 197 "sys_xgmac_clk", 8), 198 CCU_DIV_GATE_INFO(CCU_SYS_USB_CLK, "sys_usb_clk", 199 "eth_clk", CCU_SYS_USB_BASE, 10), 200 CCU_DIV_VAR_INFO(CCU_SYS_PVT_CLK, "sys_pvt_clk", 201 "ref_clk", CCU_SYS_PVT_BASE, 5, 202 CLK_SET_RATE_GATE, 0), 203 CCU_DIV_VAR_INFO(CCU_SYS_HWA_CLK, "sys_hwa_clk", 204 "sata_clk", CCU_SYS_HWA_BASE, 4, 205 CLK_SET_RATE_GATE, 0), 206 CCU_DIV_VAR_INFO(CCU_SYS_UART_CLK, "sys_uart_clk", 207 "eth_clk", CCU_SYS_UART_BASE, 17, 208 CLK_SET_RATE_GATE, 0), 209 CCU_DIV_FIXED_INFO(CCU_SYS_I2C1_CLK, "sys_i2c1_clk", 210 "eth_clk", 10), 211 CCU_DIV_FIXED_INFO(CCU_SYS_I2C2_CLK, "sys_i2c2_clk", 212 "eth_clk", 10), 213 CCU_DIV_FIXED_INFO(CCU_SYS_GPIO_CLK, "sys_gpio_clk", 214 "ref_clk", 25), 215 CCU_DIV_VAR_INFO(CCU_SYS_TIMER0_CLK, "sys_timer0_clk", 216 "ref_clk", CCU_SYS_TIMER0_BASE, 17, 217 CLK_SET_RATE_GATE, CCU_DIV_BASIC), 218 CCU_DIV_VAR_INFO(CCU_SYS_TIMER1_CLK, "sys_timer1_clk", 219 "ref_clk", CCU_SYS_TIMER1_BASE, 17, 220 CLK_SET_RATE_GATE, CCU_DIV_BASIC), 221 CCU_DIV_VAR_INFO(CCU_SYS_TIMER2_CLK, "sys_timer2_clk", 222 "ref_clk", CCU_SYS_TIMER2_BASE, 17, 223 CLK_SET_RATE_GATE, CCU_DIV_BASIC), 224 CCU_DIV_VAR_INFO(CCU_SYS_WDT_CLK, "sys_wdt_clk", 225 "eth_clk", CCU_SYS_WDT_BASE, 17, 226 CLK_SET_RATE_GATE, CCU_DIV_SKIP_ONE_TO_THREE) 227}; 228 229static struct ccu_div_data *axi_data; 230static struct ccu_div_data *sys_data; 231 232static void ccu_div_set_data(struct ccu_div_data *data) 233{ 234 struct device_node *np = data->np; 235 236 if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) 237 axi_data = data; 238 else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) 239 sys_data = data; 240 else 241 pr_err("Invalid DT node '%s' specified\n", of_node_full_name(np)); 242} 243 244static struct ccu_div_data *ccu_div_get_data(struct device_node *np) 245{ 246 if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) 247 return axi_data; 248 else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) 249 return sys_data; 250 251 pr_err("Invalid DT node '%s' specified\n", of_node_full_name(np)); 252 253 return NULL; 254} 255 256static struct ccu_div *ccu_div_find_desc(struct ccu_div_data *data, 257 unsigned int clk_id) 258{ 259 int idx; 260 261 for (idx = 0; idx < data->divs_num; ++idx) { 262 if (data->divs_info[idx].id == clk_id) 263 return data->divs[idx]; 264 } 265 266 return ERR_PTR(-EINVAL); 267} 268 269static struct ccu_div_data *ccu_div_create_data(struct device_node *np) 270{ 271 struct ccu_div_data *data; 272 int ret; 273 274 data = kzalloc(sizeof(*data), GFP_KERNEL); 275 if (!data) 276 return ERR_PTR(-ENOMEM); 277 278 data->np = np; 279 if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) { 280 data->divs_num = ARRAY_SIZE(axi_info); 281 data->divs_info = axi_info; 282 } else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) { 283 data->divs_num = ARRAY_SIZE(sys_info); 284 data->divs_info = sys_info; 285 } else { 286 pr_err("Incompatible DT node '%s' specified\n", 287 of_node_full_name(np)); 288 ret = -EINVAL; 289 goto err_kfree_data; 290 } 291 292 data->divs = kcalloc(data->divs_num, sizeof(*data->divs), GFP_KERNEL); 293 if (!data->divs) { 294 ret = -ENOMEM; 295 goto err_kfree_data; 296 } 297 298 return data; 299 300err_kfree_data: 301 kfree(data); 302 303 return ERR_PTR(ret); 304} 305 306static void ccu_div_free_data(struct ccu_div_data *data) 307{ 308 kfree(data->divs); 309 310 kfree(data); 311} 312 313static int ccu_div_find_sys_regs(struct ccu_div_data *data) 314{ 315 data->sys_regs = syscon_node_to_regmap(data->np->parent); 316 if (IS_ERR(data->sys_regs)) { 317 pr_err("Failed to find syscon regs for '%s'\n", 318 of_node_full_name(data->np)); 319 return PTR_ERR(data->sys_regs); 320 } 321 322 return 0; 323} 324 325static struct clk_hw *ccu_div_of_clk_hw_get(struct of_phandle_args *clkspec, 326 void *priv) 327{ 328 struct ccu_div_data *data = priv; 329 struct ccu_div *div; 330 unsigned int clk_id; 331 332 clk_id = clkspec->args[0]; 333 div = ccu_div_find_desc(data, clk_id); 334 if (IS_ERR(div)) { 335 if (div != ERR_PTR(-EPROBE_DEFER)) 336 pr_info("Invalid clock ID %d specified\n", clk_id); 337 338 return ERR_CAST(div); 339 } 340 341 return ccu_div_get_clk_hw(div); 342} 343 344static int ccu_div_clk_register(struct ccu_div_data *data, bool defer) 345{ 346 int idx, ret; 347 348 for (idx = 0; idx < data->divs_num; ++idx) { 349 const struct ccu_div_info *info = &data->divs_info[idx]; 350 struct ccu_div_init_data init = {0}; 351 352 if (!!(info->features & CCU_DIV_BASIC) ^ defer) { 353 if (!data->divs[idx]) 354 data->divs[idx] = ERR_PTR(-EPROBE_DEFER); 355 356 continue; 357 } 358 359 init.id = info->id; 360 init.name = info->name; 361 init.parent_name = info->parent_name; 362 init.np = data->np; 363 init.type = info->type; 364 init.flags = info->flags; 365 init.features = info->features; 366 367 if (init.type == CCU_DIV_VAR) { 368 init.base = info->base; 369 init.sys_regs = data->sys_regs; 370 init.width = info->width; 371 } else if (init.type == CCU_DIV_GATE) { 372 init.base = info->base; 373 init.sys_regs = data->sys_regs; 374 init.divider = info->divider; 375 } else if (init.type == CCU_DIV_BUF) { 376 init.base = info->base; 377 init.sys_regs = data->sys_regs; 378 } else { 379 init.divider = info->divider; 380 } 381 382 data->divs[idx] = ccu_div_hw_register(&init); 383 if (IS_ERR(data->divs[idx])) { 384 ret = PTR_ERR(data->divs[idx]); 385 pr_err("Couldn't register divider '%s' hw\n", 386 init.name); 387 goto err_hw_unregister; 388 } 389 } 390 391 return 0; 392 393err_hw_unregister: 394 for (--idx; idx >= 0; --idx) { 395 if (!!(data->divs_info[idx].features & CCU_DIV_BASIC) ^ defer) 396 continue; 397 398 ccu_div_hw_unregister(data->divs[idx]); 399 } 400 401 return ret; 402} 403 404static void ccu_div_clk_unregister(struct ccu_div_data *data, bool defer) 405{ 406 int idx; 407 408 /* Uninstall only the clocks registered on the specfied stage */ 409 for (idx = 0; idx < data->divs_num; ++idx) { 410 if (!!(data->divs_info[idx].features & CCU_DIV_BASIC) ^ defer) 411 continue; 412 413 ccu_div_hw_unregister(data->divs[idx]); 414 } 415} 416 417static int ccu_div_of_register(struct ccu_div_data *data) 418{ 419 int ret; 420 421 ret = of_clk_add_hw_provider(data->np, ccu_div_of_clk_hw_get, data); 422 if (ret) { 423 pr_err("Couldn't register dividers '%s' clock provider\n", 424 of_node_full_name(data->np)); 425 } 426 427 return ret; 428} 429 430static int ccu_div_rst_register(struct ccu_div_data *data) 431{ 432 struct ccu_rst_init_data init = {0}; 433 434 init.sys_regs = data->sys_regs; 435 init.np = data->np; 436 437 data->rsts = ccu_rst_hw_register(&init); 438 if (IS_ERR(data->rsts)) { 439 pr_err("Couldn't register divider '%s' reset controller\n", 440 of_node_full_name(data->np)); 441 return PTR_ERR(data->rsts); 442 } 443 444 return 0; 445} 446 447static int ccu_div_probe(struct platform_device *pdev) 448{ 449 struct ccu_div_data *data; 450 int ret; 451 452 data = ccu_div_get_data(dev_of_node(&pdev->dev)); 453 if (!data) 454 return -EINVAL; 455 456 ret = ccu_div_clk_register(data, false); 457 if (ret) 458 return ret; 459 460 ret = ccu_div_rst_register(data); 461 if (ret) 462 goto err_clk_unregister; 463 464 return 0; 465 466err_clk_unregister: 467 ccu_div_clk_unregister(data, false); 468 469 return ret; 470} 471 472static const struct of_device_id ccu_div_of_match[] = { 473 { .compatible = "baikal,bt1-ccu-axi" }, 474 { .compatible = "baikal,bt1-ccu-sys" }, 475 { } 476}; 477 478static struct platform_driver ccu_div_driver = { 479 .probe = ccu_div_probe, 480 .driver = { 481 .name = "clk-ccu-div", 482 .of_match_table = ccu_div_of_match, 483 .suppress_bind_attrs = true, 484 }, 485}; 486builtin_platform_driver(ccu_div_driver); 487 488static __init void ccu_div_init(struct device_node *np) 489{ 490 struct ccu_div_data *data; 491 int ret; 492 493 data = ccu_div_create_data(np); 494 if (IS_ERR(data)) 495 return; 496 497 ret = ccu_div_find_sys_regs(data); 498 if (ret) 499 goto err_free_data; 500 501 ret = ccu_div_clk_register(data, true); 502 if (ret) 503 goto err_free_data; 504 505 ret = ccu_div_of_register(data); 506 if (ret) 507 goto err_clk_unregister; 508 509 ccu_div_set_data(data); 510 511 return; 512 513err_clk_unregister: 514 ccu_div_clk_unregister(data, true); 515 516err_free_data: 517 ccu_div_free_data(data); 518} 519CLK_OF_DECLARE_DRIVER(ccu_axi, "baikal,bt1-ccu-axi", ccu_div_init); 520CLK_OF_DECLARE_DRIVER(ccu_sys, "baikal,bt1-ccu-sys", ccu_div_init); 521