18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * H8/300 divide clock driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2015 Yoshinori Sato <ysato@users.sourceforge.jp> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/of_address.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(clklock); 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic void __init h8300_div_clk_setup(struct device_node *node) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci unsigned int num_parents; 198c2ecf20Sopenharmony_ci struct clk_hw *hw; 208c2ecf20Sopenharmony_ci const char *clk_name = node->name; 218c2ecf20Sopenharmony_ci const char *parent_name; 228c2ecf20Sopenharmony_ci void __iomem *divcr = NULL; 238c2ecf20Sopenharmony_ci int width; 248c2ecf20Sopenharmony_ci int offset; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci num_parents = of_clk_get_parent_count(node); 278c2ecf20Sopenharmony_ci if (!num_parents) { 288c2ecf20Sopenharmony_ci pr_err("%s: no parent found\n", clk_name); 298c2ecf20Sopenharmony_ci return; 308c2ecf20Sopenharmony_ci } 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci divcr = of_iomap(node, 0); 338c2ecf20Sopenharmony_ci if (divcr == NULL) { 348c2ecf20Sopenharmony_ci pr_err("%s: failed to map divide register\n", clk_name); 358c2ecf20Sopenharmony_ci goto error; 368c2ecf20Sopenharmony_ci } 378c2ecf20Sopenharmony_ci offset = (unsigned long)divcr & 3; 388c2ecf20Sopenharmony_ci offset = (3 - offset) * 8; 398c2ecf20Sopenharmony_ci divcr = (void __iomem *)((unsigned long)divcr & ~3); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci parent_name = of_clk_get_parent_name(node, 0); 428c2ecf20Sopenharmony_ci of_property_read_u32(node, "renesas,width", &width); 438c2ecf20Sopenharmony_ci hw = clk_hw_register_divider(NULL, clk_name, parent_name, 448c2ecf20Sopenharmony_ci CLK_SET_RATE_GATE, divcr, offset, width, 458c2ecf20Sopenharmony_ci CLK_DIVIDER_POWER_OF_TWO, &clklock); 468c2ecf20Sopenharmony_ci if (!IS_ERR(hw)) { 478c2ecf20Sopenharmony_ci of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw); 488c2ecf20Sopenharmony_ci return; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci pr_err("%s: failed to register %s div clock (%ld)\n", 518c2ecf20Sopenharmony_ci __func__, clk_name, PTR_ERR(hw)); 528c2ecf20Sopenharmony_cierror: 538c2ecf20Sopenharmony_ci if (divcr) 548c2ecf20Sopenharmony_ci iounmap(divcr); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ciCLK_OF_DECLARE(h8300_div_clk, "renesas,h8300-div-clock", h8300_div_clk_setup); 58