cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

clk-pfd.c (3126B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright 2012 Freescale Semiconductor, Inc.
      4 * Copyright 2012 Linaro Ltd.
      5 */
      6
      7#include <linux/clk-provider.h>
      8#include <linux/io.h>
      9#include <linux/slab.h>
     10#include <linux/err.h>
     11#include "clk.h"
     12
     13/**
     14 * struct clk_pfd - IMX PFD clock
     15 * @hw:		clock source
     16 * @reg:	PFD register address
     17 * @idx:	the index of PFD encoded in the register
     18 *
     19 * PFD clock found on i.MX6 series.  Each register for PFD has 4 clk_pfd
     20 * data encoded, and member idx is used to specify the one.  And each
     21 * register has SET, CLR and TOG registers at offset 0x4 0x8 and 0xc.
     22 */
     23struct clk_pfd {
     24	struct clk_hw	hw;
     25	void __iomem	*reg;
     26	u8		idx;
     27};
     28
     29#define to_clk_pfd(_hw) container_of(_hw, struct clk_pfd, hw)
     30
     31#define SET	0x4
     32#define CLR	0x8
     33#define OTG	0xc
     34
     35static int clk_pfd_enable(struct clk_hw *hw)
     36{
     37	struct clk_pfd *pfd = to_clk_pfd(hw);
     38
     39	writel_relaxed(1 << ((pfd->idx + 1) * 8 - 1), pfd->reg + CLR);
     40
     41	return 0;
     42}
     43
     44static void clk_pfd_disable(struct clk_hw *hw)
     45{
     46	struct clk_pfd *pfd = to_clk_pfd(hw);
     47
     48	writel_relaxed(1 << ((pfd->idx + 1) * 8 - 1), pfd->reg + SET);
     49}
     50
     51static unsigned long clk_pfd_recalc_rate(struct clk_hw *hw,
     52					 unsigned long parent_rate)
     53{
     54	struct clk_pfd *pfd = to_clk_pfd(hw);
     55	u64 tmp = parent_rate;
     56	u8 frac = (readl_relaxed(pfd->reg) >> (pfd->idx * 8)) & 0x3f;
     57
     58	tmp *= 18;
     59	do_div(tmp, frac);
     60
     61	return tmp;
     62}
     63
     64static long clk_pfd_round_rate(struct clk_hw *hw, unsigned long rate,
     65			       unsigned long *prate)
     66{
     67	u64 tmp = *prate;
     68	u8 frac;
     69
     70	tmp = tmp * 18 + rate / 2;
     71	do_div(tmp, rate);
     72	frac = tmp;
     73	if (frac < 12)
     74		frac = 12;
     75	else if (frac > 35)
     76		frac = 35;
     77	tmp = *prate;
     78	tmp *= 18;
     79	do_div(tmp, frac);
     80
     81	return tmp;
     82}
     83
     84static int clk_pfd_set_rate(struct clk_hw *hw, unsigned long rate,
     85		unsigned long parent_rate)
     86{
     87	struct clk_pfd *pfd = to_clk_pfd(hw);
     88	u64 tmp = parent_rate;
     89	u8 frac;
     90
     91	tmp = tmp * 18 + rate / 2;
     92	do_div(tmp, rate);
     93	frac = tmp;
     94	if (frac < 12)
     95		frac = 12;
     96	else if (frac > 35)
     97		frac = 35;
     98
     99	writel_relaxed(0x3f << (pfd->idx * 8), pfd->reg + CLR);
    100	writel_relaxed(frac << (pfd->idx * 8), pfd->reg + SET);
    101
    102	return 0;
    103}
    104
    105static int clk_pfd_is_enabled(struct clk_hw *hw)
    106{
    107	struct clk_pfd *pfd = to_clk_pfd(hw);
    108
    109	if (readl_relaxed(pfd->reg) & (1 << ((pfd->idx + 1) * 8 - 1)))
    110		return 0;
    111
    112	return 1;
    113}
    114
    115static const struct clk_ops clk_pfd_ops = {
    116	.enable		= clk_pfd_enable,
    117	.disable	= clk_pfd_disable,
    118	.recalc_rate	= clk_pfd_recalc_rate,
    119	.round_rate	= clk_pfd_round_rate,
    120	.set_rate	= clk_pfd_set_rate,
    121	.is_enabled     = clk_pfd_is_enabled,
    122};
    123
    124struct clk_hw *imx_clk_hw_pfd(const char *name, const char *parent_name,
    125			void __iomem *reg, u8 idx)
    126{
    127	struct clk_pfd *pfd;
    128	struct clk_hw *hw;
    129	struct clk_init_data init;
    130	int ret;
    131
    132	pfd = kzalloc(sizeof(*pfd), GFP_KERNEL);
    133	if (!pfd)
    134		return ERR_PTR(-ENOMEM);
    135
    136	pfd->reg = reg;
    137	pfd->idx = idx;
    138
    139	init.name = name;
    140	init.ops = &clk_pfd_ops;
    141	init.flags = 0;
    142	init.parent_names = &parent_name;
    143	init.num_parents = 1;
    144
    145	pfd->hw.init = &init;
    146	hw = &pfd->hw;
    147
    148	ret = clk_hw_register(NULL, hw);
    149	if (ret) {
    150		kfree(pfd);
    151		return ERR_PTR(ret);
    152	}
    153
    154	return hw;
    155}