18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Freescale Semiconductor, Inc. 48c2ecf20Sopenharmony_ci * Copyright 2017~2018 NXP 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/bits.h> 98c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "clk.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define PCG_PCS_SHIFT 24 168c2ecf20Sopenharmony_ci#define PCG_PCS_MASK 0x7 178c2ecf20Sopenharmony_ci#define PCG_CGC_SHIFT 30 188c2ecf20Sopenharmony_ci#define PCG_FRAC_SHIFT 3 198c2ecf20Sopenharmony_ci#define PCG_FRAC_WIDTH 1 208c2ecf20Sopenharmony_ci#define PCG_FRAC_MASK BIT(3) 218c2ecf20Sopenharmony_ci#define PCG_PCD_SHIFT 0 228c2ecf20Sopenharmony_ci#define PCG_PCD_WIDTH 3 238c2ecf20Sopenharmony_ci#define PCG_PCD_MASK 0x7 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct clk_hw *imx7ulp_clk_hw_composite(const char *name, 268c2ecf20Sopenharmony_ci const char * const *parent_names, 278c2ecf20Sopenharmony_ci int num_parents, bool mux_present, 288c2ecf20Sopenharmony_ci bool rate_present, bool gate_present, 298c2ecf20Sopenharmony_ci void __iomem *reg) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct clk_hw *mux_hw = NULL, *fd_hw = NULL, *gate_hw = NULL; 328c2ecf20Sopenharmony_ci struct clk_fractional_divider *fd = NULL; 338c2ecf20Sopenharmony_ci struct clk_gate *gate = NULL; 348c2ecf20Sopenharmony_ci struct clk_mux *mux = NULL; 358c2ecf20Sopenharmony_ci struct clk_hw *hw; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (mux_present) { 388c2ecf20Sopenharmony_ci mux = kzalloc(sizeof(*mux), GFP_KERNEL); 398c2ecf20Sopenharmony_ci if (!mux) 408c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 418c2ecf20Sopenharmony_ci mux_hw = &mux->hw; 428c2ecf20Sopenharmony_ci mux->reg = reg; 438c2ecf20Sopenharmony_ci mux->shift = PCG_PCS_SHIFT; 448c2ecf20Sopenharmony_ci mux->mask = PCG_PCS_MASK; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (rate_present) { 488c2ecf20Sopenharmony_ci fd = kzalloc(sizeof(*fd), GFP_KERNEL); 498c2ecf20Sopenharmony_ci if (!fd) { 508c2ecf20Sopenharmony_ci kfree(mux); 518c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci fd_hw = &fd->hw; 548c2ecf20Sopenharmony_ci fd->reg = reg; 558c2ecf20Sopenharmony_ci fd->mshift = PCG_FRAC_SHIFT; 568c2ecf20Sopenharmony_ci fd->mwidth = PCG_FRAC_WIDTH; 578c2ecf20Sopenharmony_ci fd->mmask = PCG_FRAC_MASK; 588c2ecf20Sopenharmony_ci fd->nshift = PCG_PCD_SHIFT; 598c2ecf20Sopenharmony_ci fd->nwidth = PCG_PCD_WIDTH; 608c2ecf20Sopenharmony_ci fd->nmask = PCG_PCD_MASK; 618c2ecf20Sopenharmony_ci fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED; 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (gate_present) { 658c2ecf20Sopenharmony_ci gate = kzalloc(sizeof(*gate), GFP_KERNEL); 668c2ecf20Sopenharmony_ci if (!gate) { 678c2ecf20Sopenharmony_ci kfree(mux); 688c2ecf20Sopenharmony_ci kfree(fd); 698c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci gate_hw = &gate->hw; 728c2ecf20Sopenharmony_ci gate->reg = reg; 738c2ecf20Sopenharmony_ci gate->bit_idx = PCG_CGC_SHIFT; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 778c2ecf20Sopenharmony_ci mux_hw, &clk_mux_ops, fd_hw, 788c2ecf20Sopenharmony_ci &clk_fractional_divider_ops, gate_hw, 798c2ecf20Sopenharmony_ci &clk_gate_ops, CLK_SET_RATE_GATE | 808c2ecf20Sopenharmony_ci CLK_SET_PARENT_GATE); 818c2ecf20Sopenharmony_ci if (IS_ERR(hw)) { 828c2ecf20Sopenharmony_ci kfree(mux); 838c2ecf20Sopenharmony_ci kfree(fd); 848c2ecf20Sopenharmony_ci kfree(gate); 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return hw; 888c2ecf20Sopenharmony_ci} 89