111f68120SShawn Guo /* 211f68120SShawn Guo * Copyright 2012 Freescale Semiconductor, Inc. 311f68120SShawn Guo * Copyright 2012 Linaro Ltd. 411f68120SShawn Guo * 511f68120SShawn Guo * The code contained herein is licensed under the GNU General Public 611f68120SShawn Guo * License. You may obtain a copy of the GNU General Public License 711f68120SShawn Guo * Version 2 or later at the following locations: 811f68120SShawn Guo * 911f68120SShawn Guo * http://www.opensource.org/licenses/gpl-license.html 1011f68120SShawn Guo * http://www.gnu.org/copyleft/gpl.html 1111f68120SShawn Guo */ 1211f68120SShawn Guo 1311f68120SShawn Guo #include <linux/clk-provider.h> 1411f68120SShawn Guo #include <linux/io.h> 1511f68120SShawn Guo #include <linux/slab.h> 1611f68120SShawn Guo #include <linux/err.h> 1711f68120SShawn Guo #include "clk.h" 1811f68120SShawn Guo 1911f68120SShawn Guo /** 2011f68120SShawn Guo * struct clk_pfd - IMX PFD clock 2111f68120SShawn Guo * @clk_hw: clock source 2211f68120SShawn Guo * @reg: PFD register address 2311f68120SShawn Guo * @idx: the index of PFD encoded in the register 2411f68120SShawn Guo * 2511f68120SShawn Guo * PFD clock found on i.MX6 series. Each register for PFD has 4 clk_pfd 2611f68120SShawn Guo * data encoded, and member idx is used to specify the one. And each 2711f68120SShawn Guo * register has SET, CLR and TOG registers at offset 0x4 0x8 and 0xc. 2811f68120SShawn Guo */ 2911f68120SShawn Guo struct clk_pfd { 3011f68120SShawn Guo struct clk_hw hw; 3111f68120SShawn Guo void __iomem *reg; 3211f68120SShawn Guo u8 idx; 3311f68120SShawn Guo }; 3411f68120SShawn Guo 3511f68120SShawn Guo #define to_clk_pfd(_hw) container_of(_hw, struct clk_pfd, hw) 3611f68120SShawn Guo 3711f68120SShawn Guo #define SET 0x4 3811f68120SShawn Guo #define CLR 0x8 3911f68120SShawn Guo #define OTG 0xc 4011f68120SShawn Guo 4111f68120SShawn Guo static int clk_pfd_enable(struct clk_hw *hw) 4211f68120SShawn Guo { 4311f68120SShawn Guo struct clk_pfd *pfd = to_clk_pfd(hw); 4411f68120SShawn Guo 4511f68120SShawn Guo writel_relaxed(1 << ((pfd->idx + 1) * 8 - 1), pfd->reg + CLR); 4611f68120SShawn Guo 4711f68120SShawn Guo return 0; 4811f68120SShawn Guo } 4911f68120SShawn Guo 5011f68120SShawn Guo static void clk_pfd_disable(struct clk_hw *hw) 5111f68120SShawn Guo { 5211f68120SShawn Guo struct clk_pfd *pfd = to_clk_pfd(hw); 5311f68120SShawn Guo 5411f68120SShawn Guo writel_relaxed(1 << ((pfd->idx + 1) * 8 - 1), pfd->reg + SET); 5511f68120SShawn Guo } 5611f68120SShawn Guo 5711f68120SShawn Guo static unsigned long clk_pfd_recalc_rate(struct clk_hw *hw, 5811f68120SShawn Guo unsigned long parent_rate) 5911f68120SShawn Guo { 6011f68120SShawn Guo struct clk_pfd *pfd = to_clk_pfd(hw); 6111f68120SShawn Guo u64 tmp = parent_rate; 6211f68120SShawn Guo u8 frac = (readl_relaxed(pfd->reg) >> (pfd->idx * 8)) & 0x3f; 6311f68120SShawn Guo 6411f68120SShawn Guo tmp *= 18; 6511f68120SShawn Guo do_div(tmp, frac); 6611f68120SShawn Guo 6711f68120SShawn Guo return tmp; 6811f68120SShawn Guo } 6911f68120SShawn Guo 7011f68120SShawn Guo static long clk_pfd_round_rate(struct clk_hw *hw, unsigned long rate, 7111f68120SShawn Guo unsigned long *prate) 7211f68120SShawn Guo { 7311f68120SShawn Guo u64 tmp = *prate; 7411f68120SShawn Guo u8 frac; 7511f68120SShawn Guo 7611f68120SShawn Guo tmp = tmp * 18 + rate / 2; 7711f68120SShawn Guo do_div(tmp, rate); 7811f68120SShawn Guo frac = tmp; 7911f68120SShawn Guo if (frac < 12) 8011f68120SShawn Guo frac = 12; 8111f68120SShawn Guo else if (frac > 35) 8211f68120SShawn Guo frac = 35; 8311f68120SShawn Guo tmp = *prate; 8411f68120SShawn Guo tmp *= 18; 8511f68120SShawn Guo do_div(tmp, frac); 8611f68120SShawn Guo 8711f68120SShawn Guo return tmp; 8811f68120SShawn Guo } 8911f68120SShawn Guo 9011f68120SShawn Guo static int clk_pfd_set_rate(struct clk_hw *hw, unsigned long rate, 9111f68120SShawn Guo unsigned long parent_rate) 9211f68120SShawn Guo { 9311f68120SShawn Guo struct clk_pfd *pfd = to_clk_pfd(hw); 9411f68120SShawn Guo u64 tmp = parent_rate; 9511f68120SShawn Guo u8 frac; 9611f68120SShawn Guo 9711f68120SShawn Guo tmp = tmp * 18 + rate / 2; 9811f68120SShawn Guo do_div(tmp, rate); 9911f68120SShawn Guo frac = tmp; 10011f68120SShawn Guo if (frac < 12) 10111f68120SShawn Guo frac = 12; 10211f68120SShawn Guo else if (frac > 35) 10311f68120SShawn Guo frac = 35; 10411f68120SShawn Guo 10511f68120SShawn Guo writel_relaxed(0x3f << (pfd->idx * 8), pfd->reg + CLR); 10611f68120SShawn Guo writel_relaxed(frac << (pfd->idx * 8), pfd->reg + SET); 10711f68120SShawn Guo 10811f68120SShawn Guo return 0; 10911f68120SShawn Guo } 11011f68120SShawn Guo 11111f68120SShawn Guo static int clk_pfd_is_enabled(struct clk_hw *hw) 11211f68120SShawn Guo { 11311f68120SShawn Guo struct clk_pfd *pfd = to_clk_pfd(hw); 11411f68120SShawn Guo 11511f68120SShawn Guo if (readl_relaxed(pfd->reg) & (1 << ((pfd->idx + 1) * 8 - 1))) 11611f68120SShawn Guo return 0; 11711f68120SShawn Guo 11811f68120SShawn Guo return 1; 11911f68120SShawn Guo } 12011f68120SShawn Guo 12111f68120SShawn Guo static const struct clk_ops clk_pfd_ops = { 12211f68120SShawn Guo .enable = clk_pfd_enable, 12311f68120SShawn Guo .disable = clk_pfd_disable, 12411f68120SShawn Guo .recalc_rate = clk_pfd_recalc_rate, 12511f68120SShawn Guo .round_rate = clk_pfd_round_rate, 12611f68120SShawn Guo .set_rate = clk_pfd_set_rate, 12711f68120SShawn Guo .is_enabled = clk_pfd_is_enabled, 12811f68120SShawn Guo }; 12911f68120SShawn Guo 130*995087c9SAbel Vesa struct clk_hw *imx_clk_hw_pfd(const char *name, const char *parent_name, 13111f68120SShawn Guo void __iomem *reg, u8 idx) 13211f68120SShawn Guo { 13311f68120SShawn Guo struct clk_pfd *pfd; 134*995087c9SAbel Vesa struct clk_hw *hw; 13511f68120SShawn Guo struct clk_init_data init; 136*995087c9SAbel Vesa int ret; 13711f68120SShawn Guo 13811f68120SShawn Guo pfd = kzalloc(sizeof(*pfd), GFP_KERNEL); 13911f68120SShawn Guo if (!pfd) 14011f68120SShawn Guo return ERR_PTR(-ENOMEM); 14111f68120SShawn Guo 14211f68120SShawn Guo pfd->reg = reg; 14311f68120SShawn Guo pfd->idx = idx; 14411f68120SShawn Guo 14511f68120SShawn Guo init.name = name; 14611f68120SShawn Guo init.ops = &clk_pfd_ops; 14711f68120SShawn Guo init.flags = 0; 14811f68120SShawn Guo init.parent_names = &parent_name; 14911f68120SShawn Guo init.num_parents = 1; 15011f68120SShawn Guo 15111f68120SShawn Guo pfd->hw.init = &init; 152*995087c9SAbel Vesa hw = &pfd->hw; 15311f68120SShawn Guo 154*995087c9SAbel Vesa ret = clk_hw_register(NULL, hw); 155*995087c9SAbel Vesa if (ret) { 15611f68120SShawn Guo kfree(pfd); 157*995087c9SAbel Vesa return ERR_PTR(ret); 158*995087c9SAbel Vesa } 15911f68120SShawn Guo 160*995087c9SAbel Vesa return hw; 16111f68120SShawn Guo } 162