xref: /linux/drivers/clk/socfpga/clk-gate.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
297259e99SSteffen Trumtrar /*
397259e99SSteffen Trumtrar  *  Copyright 2011-2012 Calxeda, Inc.
497259e99SSteffen Trumtrar  *  Copyright (C) 2012-2013 Altera Corporation <www.altera.com>
597259e99SSteffen Trumtrar  *
697259e99SSteffen Trumtrar  * Based from clk-highbank.c
797259e99SSteffen Trumtrar  */
8b0af24b5SStephen Boyd #include <linux/slab.h>
997259e99SSteffen Trumtrar #include <linux/clk-provider.h>
1097259e99SSteffen Trumtrar #include <linux/io.h>
11044abbdeSDinh Nguyen #include <linux/mfd/syscon.h>
1297259e99SSteffen Trumtrar #include <linux/of.h>
13044abbdeSDinh Nguyen #include <linux/regmap.h>
1497259e99SSteffen Trumtrar 
1597259e99SSteffen Trumtrar #include "clk.h"
1697259e99SSteffen Trumtrar 
1797259e99SSteffen Trumtrar #define SOCFPGA_L4_MP_CLK		"l4_mp_clk"
1897259e99SSteffen Trumtrar #define SOCFPGA_L4_SP_CLK		"l4_sp_clk"
1997259e99SSteffen Trumtrar #define SOCFPGA_NAND_CLK		"nand_clk"
2097259e99SSteffen Trumtrar #define SOCFPGA_NAND_X_CLK		"nand_x_clk"
2197259e99SSteffen Trumtrar #define SOCFPGA_MMC_CLK			"sdmmc_clk"
2297259e99SSteffen Trumtrar #define SOCFPGA_GPIO_DB_CLK_OFFSET	0xA8
2397259e99SSteffen Trumtrar 
2497259e99SSteffen Trumtrar #define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw)
2597259e99SSteffen Trumtrar 
26044abbdeSDinh Nguyen /* SDMMC Group for System Manager defines */
27044abbdeSDinh Nguyen #define SYSMGR_SDMMCGRP_CTRL_OFFSET    0x108
28044abbdeSDinh Nguyen 
socfpga_clk_get_parent(struct clk_hw * hwclk)2997259e99SSteffen Trumtrar static u8 socfpga_clk_get_parent(struct clk_hw *hwclk)
3097259e99SSteffen Trumtrar {
3197259e99SSteffen Trumtrar 	u32 l4_src;
3297259e99SSteffen Trumtrar 	u32 perpll_src;
3309d4922dSStephen Boyd 	const char *name = clk_hw_get_name(hwclk);
3497259e99SSteffen Trumtrar 
3509d4922dSStephen Boyd 	if (streq(name, SOCFPGA_L4_MP_CLK)) {
3697259e99SSteffen Trumtrar 		l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
375c585850SColin Ian King 		return l4_src & 0x1;
3897259e99SSteffen Trumtrar 	}
3909d4922dSStephen Boyd 	if (streq(name, SOCFPGA_L4_SP_CLK)) {
4097259e99SSteffen Trumtrar 		l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
4197259e99SSteffen Trumtrar 		return !!(l4_src & 2);
4297259e99SSteffen Trumtrar 	}
4397259e99SSteffen Trumtrar 
4497259e99SSteffen Trumtrar 	perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
4509d4922dSStephen Boyd 	if (streq(name, SOCFPGA_MMC_CLK))
465c585850SColin Ian King 		return perpll_src & 0x3;
4709d4922dSStephen Boyd 	if (streq(name, SOCFPGA_NAND_CLK) ||
4809d4922dSStephen Boyd 	    streq(name, SOCFPGA_NAND_X_CLK))
4997259e99SSteffen Trumtrar 		return (perpll_src >> 2) & 3;
5097259e99SSteffen Trumtrar 
5197259e99SSteffen Trumtrar 	/* QSPI clock */
5297259e99SSteffen Trumtrar 	return (perpll_src >> 4) & 3;
5397259e99SSteffen Trumtrar 
5497259e99SSteffen Trumtrar }
5597259e99SSteffen Trumtrar 
socfpga_clk_set_parent(struct clk_hw * hwclk,u8 parent)5697259e99SSteffen Trumtrar static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent)
5797259e99SSteffen Trumtrar {
5897259e99SSteffen Trumtrar 	u32 src_reg;
5909d4922dSStephen Boyd 	const char *name = clk_hw_get_name(hwclk);
6097259e99SSteffen Trumtrar 
6109d4922dSStephen Boyd 	if (streq(name, SOCFPGA_L4_MP_CLK)) {
6297259e99SSteffen Trumtrar 		src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
6397259e99SSteffen Trumtrar 		src_reg &= ~0x1;
6497259e99SSteffen Trumtrar 		src_reg |= parent;
6597259e99SSteffen Trumtrar 		writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
6609d4922dSStephen Boyd 	} else if (streq(name, SOCFPGA_L4_SP_CLK)) {
6797259e99SSteffen Trumtrar 		src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
6897259e99SSteffen Trumtrar 		src_reg &= ~0x2;
6997259e99SSteffen Trumtrar 		src_reg |= (parent << 1);
7097259e99SSteffen Trumtrar 		writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
7197259e99SSteffen Trumtrar 	} else {
7297259e99SSteffen Trumtrar 		src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
7309d4922dSStephen Boyd 		if (streq(name, SOCFPGA_MMC_CLK)) {
7497259e99SSteffen Trumtrar 			src_reg &= ~0x3;
7597259e99SSteffen Trumtrar 			src_reg |= parent;
7609d4922dSStephen Boyd 		} else if (streq(name, SOCFPGA_NAND_CLK) ||
7709d4922dSStephen Boyd 			streq(name, SOCFPGA_NAND_X_CLK)) {
7897259e99SSteffen Trumtrar 			src_reg &= ~0xC;
7997259e99SSteffen Trumtrar 			src_reg |= (parent << 2);
8097259e99SSteffen Trumtrar 		} else {/* QSPI clock */
8197259e99SSteffen Trumtrar 			src_reg &= ~0x30;
8297259e99SSteffen Trumtrar 			src_reg |= (parent << 4);
8397259e99SSteffen Trumtrar 		}
8497259e99SSteffen Trumtrar 		writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
8597259e99SSteffen Trumtrar 	}
8697259e99SSteffen Trumtrar 
8797259e99SSteffen Trumtrar 	return 0;
8897259e99SSteffen Trumtrar }
8997259e99SSteffen Trumtrar 
socfpga_clk_get_div(struct socfpga_gate_clk * socfpgaclk)90*601cb6d5SMaxime Ripard static u32 socfpga_clk_get_div(struct socfpga_gate_clk *socfpgaclk)
9197259e99SSteffen Trumtrar {
9297259e99SSteffen Trumtrar 	u32 div = 1, val;
9397259e99SSteffen Trumtrar 
9497259e99SSteffen Trumtrar 	if (socfpgaclk->fixed_div)
9597259e99SSteffen Trumtrar 		div = socfpgaclk->fixed_div;
9697259e99SSteffen Trumtrar 	else if (socfpgaclk->div_reg) {
9797259e99SSteffen Trumtrar 		val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
9825d4d341SAndy Shevchenko 		val &= GENMASK(socfpgaclk->width - 1, 0);
9997259e99SSteffen Trumtrar 		/* Check for GPIO_DB_CLK by its offset */
1002867b974SKrzysztof Kozlowski 		if ((uintptr_t) socfpgaclk->div_reg & SOCFPGA_GPIO_DB_CLK_OFFSET)
10197259e99SSteffen Trumtrar 			div = val + 1;
10297259e99SSteffen Trumtrar 		else
10397259e99SSteffen Trumtrar 			div = (1 << val);
10497259e99SSteffen Trumtrar 	}
10597259e99SSteffen Trumtrar 
106*601cb6d5SMaxime Ripard 	return div;
107*601cb6d5SMaxime Ripard }
108*601cb6d5SMaxime Ripard 
socfpga_clk_recalc_rate(struct clk_hw * hwclk,unsigned long parent_rate)109*601cb6d5SMaxime Ripard static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk,
110*601cb6d5SMaxime Ripard 					     unsigned long parent_rate)
111*601cb6d5SMaxime Ripard {
112*601cb6d5SMaxime Ripard 	struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
113*601cb6d5SMaxime Ripard 	u32 div = socfpga_clk_get_div(socfpgaclk);
114*601cb6d5SMaxime Ripard 
11597259e99SSteffen Trumtrar 	return parent_rate / div;
11697259e99SSteffen Trumtrar }
11797259e99SSteffen Trumtrar 
118*601cb6d5SMaxime Ripard 
socfpga_clk_determine_rate(struct clk_hw * hwclk,struct clk_rate_request * req)119*601cb6d5SMaxime Ripard static int socfpga_clk_determine_rate(struct clk_hw *hwclk,
120*601cb6d5SMaxime Ripard 				      struct clk_rate_request *req)
121*601cb6d5SMaxime Ripard {
122*601cb6d5SMaxime Ripard 	struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk);
123*601cb6d5SMaxime Ripard 	u32 div = socfpga_clk_get_div(socfpgaclk);
124*601cb6d5SMaxime Ripard 
125*601cb6d5SMaxime Ripard 	req->rate = req->best_parent_rate / div;
126*601cb6d5SMaxime Ripard 
127*601cb6d5SMaxime Ripard 	return 0;
128*601cb6d5SMaxime Ripard }
129*601cb6d5SMaxime Ripard 
13097259e99SSteffen Trumtrar static struct clk_ops gateclk_ops = {
13197259e99SSteffen Trumtrar 	.recalc_rate = socfpga_clk_recalc_rate,
132*601cb6d5SMaxime Ripard 	.determine_rate = socfpga_clk_determine_rate,
13397259e99SSteffen Trumtrar 	.get_parent = socfpga_clk_get_parent,
13497259e99SSteffen Trumtrar 	.set_parent = socfpga_clk_set_parent,
13597259e99SSteffen Trumtrar };
13697259e99SSteffen Trumtrar 
socfpga_gate_init(struct device_node * node)137a30a67beSStephen Boyd void __init socfpga_gate_init(struct device_node *node)
13897259e99SSteffen Trumtrar {
13997259e99SSteffen Trumtrar 	u32 clk_gate[2];
14097259e99SSteffen Trumtrar 	u32 div_reg[3];
14197259e99SSteffen Trumtrar 	u32 fixed_div;
1422c2b9c60SDinh Nguyen 	struct clk_hw *hw_clk;
14397259e99SSteffen Trumtrar 	struct socfpga_gate_clk *socfpga_clk;
14497259e99SSteffen Trumtrar 	const char *clk_name = node->name;
14597259e99SSteffen Trumtrar 	const char *parent_name[SOCFPGA_MAX_PARENTS];
14697259e99SSteffen Trumtrar 	struct clk_init_data init;
147a30a67beSStephen Boyd 	struct clk_ops *ops;
14897259e99SSteffen Trumtrar 	int rc;
14997259e99SSteffen Trumtrar 
15097259e99SSteffen Trumtrar 	socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
15197259e99SSteffen Trumtrar 	if (WARN_ON(!socfpga_clk))
15297259e99SSteffen Trumtrar 		return;
15397259e99SSteffen Trumtrar 
154a30a67beSStephen Boyd 	ops = kmemdup(&gateclk_ops, sizeof(gateclk_ops), GFP_KERNEL);
15585f1b574SMarco Pagani 	if (WARN_ON(!ops))
15685f1b574SMarco Pagani 		goto err_kmemdup;
157a30a67beSStephen Boyd 
15897259e99SSteffen Trumtrar 	rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2);
15997259e99SSteffen Trumtrar 	if (rc)
16097259e99SSteffen Trumtrar 		clk_gate[0] = 0;
16197259e99SSteffen Trumtrar 
16297259e99SSteffen Trumtrar 	if (clk_gate[0]) {
16397259e99SSteffen Trumtrar 		socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0];
16497259e99SSteffen Trumtrar 		socfpga_clk->hw.bit_idx = clk_gate[1];
16597259e99SSteffen Trumtrar 
166a30a67beSStephen Boyd 		ops->enable = clk_gate_ops.enable;
167a30a67beSStephen Boyd 		ops->disable = clk_gate_ops.disable;
16897259e99SSteffen Trumtrar 	}
16997259e99SSteffen Trumtrar 
17097259e99SSteffen Trumtrar 	rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
17197259e99SSteffen Trumtrar 	if (rc)
17297259e99SSteffen Trumtrar 		socfpga_clk->fixed_div = 0;
17397259e99SSteffen Trumtrar 	else
17497259e99SSteffen Trumtrar 		socfpga_clk->fixed_div = fixed_div;
17597259e99SSteffen Trumtrar 
17697259e99SSteffen Trumtrar 	rc = of_property_read_u32_array(node, "div-reg", div_reg, 3);
17797259e99SSteffen Trumtrar 	if (!rc) {
17897259e99SSteffen Trumtrar 		socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0];
17997259e99SSteffen Trumtrar 		socfpga_clk->shift = div_reg[1];
18097259e99SSteffen Trumtrar 		socfpga_clk->width = div_reg[2];
18197259e99SSteffen Trumtrar 	} else {
182b011d386SStephen Boyd 		socfpga_clk->div_reg = NULL;
18397259e99SSteffen Trumtrar 	}
18497259e99SSteffen Trumtrar 
18597259e99SSteffen Trumtrar 	of_property_read_string(node, "clock-output-names", &clk_name);
18697259e99SSteffen Trumtrar 
18797259e99SSteffen Trumtrar 	init.name = clk_name;
18897259e99SSteffen Trumtrar 	init.ops = ops;
18997259e99SSteffen Trumtrar 	init.flags = 0;
19097259e99SSteffen Trumtrar 
191761d3e32SDinh Nguyen 	init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS);
192a30a67beSStephen Boyd 	if (init.num_parents < 2) {
193a30a67beSStephen Boyd 		ops->get_parent = NULL;
194a30a67beSStephen Boyd 		ops->set_parent = NULL;
195a30a67beSStephen Boyd 	}
196a30a67beSStephen Boyd 
19797259e99SSteffen Trumtrar 	init.parent_names = parent_name;
19897259e99SSteffen Trumtrar 	socfpga_clk->hw.hw.init = &init;
19997259e99SSteffen Trumtrar 
2002c2b9c60SDinh Nguyen 	hw_clk = &socfpga_clk->hw.hw;
2012c2b9c60SDinh Nguyen 
20285f1b574SMarco Pagani 	rc = clk_hw_register(NULL, hw_clk);
20385f1b574SMarco Pagani 	if (rc) {
20485f1b574SMarco Pagani 		pr_err("Could not register clock:%s\n", clk_name);
20585f1b574SMarco Pagani 		goto err_clk_hw_register;
20697259e99SSteffen Trumtrar 	}
20785f1b574SMarco Pagani 
20885f1b574SMarco Pagani 	rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw_clk);
20985f1b574SMarco Pagani 	if (rc) {
21085f1b574SMarco Pagani 		pr_err("Could not register clock provider for node:%s\n",
21185f1b574SMarco Pagani 		       clk_name);
21285f1b574SMarco Pagani 		goto err_of_clk_add_hw_provider;
21385f1b574SMarco Pagani 	}
21485f1b574SMarco Pagani 
21597259e99SSteffen Trumtrar 	return;
21685f1b574SMarco Pagani 
21785f1b574SMarco Pagani err_of_clk_add_hw_provider:
21885f1b574SMarco Pagani 	clk_hw_unregister(hw_clk);
21985f1b574SMarco Pagani err_clk_hw_register:
22085f1b574SMarco Pagani 	kfree(ops);
22185f1b574SMarco Pagani err_kmemdup:
22285f1b574SMarco Pagani 	kfree(socfpga_clk);
22397259e99SSteffen Trumtrar }
224