1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2019 DENX Software Engineering 4 * Lukasz Majewski, DENX Software Engineering, lukma@denx.de 5 * 6 * Copyright 2012 Freescale Semiconductor, Inc. 7 * Copyright 2012 Linaro Ltd. 8 * 9 * The code contained herein is licensed under the GNU General Public 10 * License. You may obtain a copy of the GNU General Public License 11 * Version 2 or later at the following locations: 12 * 13 * http://www.opensource.org/licenses/gpl-license.html 14 * http://www.gnu.org/copyleft/gpl.html 15 */ 16 17#include <common.h> 18#include <asm/io.h> 19#include <malloc.h> 20#include <clk-uclass.h> 21#include <dm/device.h> 22#include <dm/devres.h> 23#include <linux/clk-provider.h> 24#include <div64.h> 25#include <clk.h> 26#include "clk.h" 27#include <linux/err.h> 28 29#define UBOOT_DM_CLK_IMX_PFD "imx_clk_pfd" 30 31struct clk_pfd { 32 struct clk clk; 33 void __iomem *reg; 34 u8 idx; 35}; 36 37#define to_clk_pfd(_clk) container_of(_clk, struct clk_pfd, clk) 38 39#define SET 0x4 40#define CLR 0x8 41#define OTG 0xc 42 43static unsigned long clk_pfd_recalc_rate(struct clk *clk) 44{ 45 struct clk_pfd *pfd = 46 to_clk_pfd(dev_get_clk_ptr(clk->dev)); 47 unsigned long parent_rate = clk_get_parent_rate(clk); 48 u64 tmp = parent_rate; 49 u8 frac = (readl(pfd->reg) >> (pfd->idx * 8)) & 0x3f; 50 51 tmp *= 18; 52 do_div(tmp, frac); 53 54 return tmp; 55} 56 57static unsigned long clk_pfd_set_rate(struct clk *clk, unsigned long rate) 58{ 59 struct clk_pfd *pfd = to_clk_pfd(clk); 60 unsigned long parent_rate = clk_get_parent_rate(clk); 61 u64 tmp = parent_rate; 62 u8 frac; 63 64 tmp = tmp * 18 + rate / 2; 65 do_div(tmp, rate); 66 frac = tmp; 67 if (frac < 12) 68 frac = 12; 69 else if (frac > 35) 70 frac = 35; 71 72 writel(0x3f << (pfd->idx * 8), pfd->reg + CLR); 73 writel(frac << (pfd->idx * 8), pfd->reg + SET); 74 75 return 0; 76} 77 78static const struct clk_ops clk_pfd_ops = { 79 .get_rate = clk_pfd_recalc_rate, 80 .set_rate = clk_pfd_set_rate, 81}; 82 83struct clk *imx_clk_pfd(const char *name, const char *parent_name, 84 void __iomem *reg, u8 idx) 85{ 86 struct clk_pfd *pfd; 87 struct clk *clk; 88 int ret; 89 90 pfd = kzalloc(sizeof(*pfd), GFP_KERNEL); 91 if (!pfd) 92 return ERR_PTR(-ENOMEM); 93 94 pfd->reg = reg; 95 pfd->idx = idx; 96 97 /* register the clock */ 98 clk = &pfd->clk; 99 100 ret = clk_register(clk, UBOOT_DM_CLK_IMX_PFD, name, parent_name); 101 if (ret) { 102 kfree(pfd); 103 return ERR_PTR(ret); 104 } 105 106 return clk; 107} 108 109U_BOOT_DRIVER(clk_pfd) = { 110 .name = UBOOT_DM_CLK_IMX_PFD, 111 .id = UCLASS_CLK, 112 .ops = &clk_pfd_ops, 113 .flags = DM_FLAG_PRE_RELOC, 114}; 115