xref: /openbmc/linux/drivers/clk/clk-lan966x.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
154104ee0SKavyasree Kotagiri // SPDX-License-Identifier: GPL-2.0-or-later
254104ee0SKavyasree Kotagiri /*
354104ee0SKavyasree Kotagiri  * Microchip LAN966x SoC Clock driver.
454104ee0SKavyasree Kotagiri  *
554104ee0SKavyasree Kotagiri  * Copyright (C) 2021 Microchip Technology, Inc. and its subsidiaries
654104ee0SKavyasree Kotagiri  *
754104ee0SKavyasree Kotagiri  * Author: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
854104ee0SKavyasree Kotagiri  */
954104ee0SKavyasree Kotagiri 
1054104ee0SKavyasree Kotagiri #include <linux/bitfield.h>
1154104ee0SKavyasree Kotagiri #include <linux/clk-provider.h>
1254104ee0SKavyasree Kotagiri #include <linux/io.h>
1354104ee0SKavyasree Kotagiri #include <linux/kernel.h>
1454104ee0SKavyasree Kotagiri #include <linux/module.h>
1554104ee0SKavyasree Kotagiri #include <linux/of.h>
1654104ee0SKavyasree Kotagiri #include <linux/platform_device.h>
1754104ee0SKavyasree Kotagiri #include <linux/slab.h>
1854104ee0SKavyasree Kotagiri 
1954104ee0SKavyasree Kotagiri #include <dt-bindings/clock/microchip,lan966x.h>
2054104ee0SKavyasree Kotagiri 
2154104ee0SKavyasree Kotagiri #define GCK_ENA         BIT(0)
2254104ee0SKavyasree Kotagiri #define GCK_SRC_SEL     GENMASK(9, 8)
2354104ee0SKavyasree Kotagiri #define GCK_PRESCALER   GENMASK(23, 16)
2454104ee0SKavyasree Kotagiri 
2554104ee0SKavyasree Kotagiri #define DIV_MAX		255
2654104ee0SKavyasree Kotagiri 
2754104ee0SKavyasree Kotagiri static const char *clk_names[N_CLOCKS] = {
2854104ee0SKavyasree Kotagiri 	"qspi0", "qspi1", "qspi2", "sdmmc0",
2954104ee0SKavyasree Kotagiri 	"pi", "mcan0", "mcan1", "flexcom0",
3054104ee0SKavyasree Kotagiri 	"flexcom1", "flexcom2", "flexcom3",
3154104ee0SKavyasree Kotagiri 	"flexcom4", "timer1", "usb_refclk",
3254104ee0SKavyasree Kotagiri };
3354104ee0SKavyasree Kotagiri 
3454104ee0SKavyasree Kotagiri struct lan966x_gck {
3554104ee0SKavyasree Kotagiri 	struct clk_hw hw;
3654104ee0SKavyasree Kotagiri 	void __iomem *reg;
3754104ee0SKavyasree Kotagiri };
3854104ee0SKavyasree Kotagiri #define to_lan966x_gck(hw) container_of(hw, struct lan966x_gck, hw)
3954104ee0SKavyasree Kotagiri 
4054104ee0SKavyasree Kotagiri static const struct clk_parent_data lan966x_gck_pdata[] = {
4154104ee0SKavyasree Kotagiri 	{ .fw_name = "cpu", },
4254104ee0SKavyasree Kotagiri 	{ .fw_name = "ddr", },
4354104ee0SKavyasree Kotagiri 	{ .fw_name = "sys", },
4454104ee0SKavyasree Kotagiri };
4554104ee0SKavyasree Kotagiri 
4654104ee0SKavyasree Kotagiri static struct clk_init_data init = {
4754104ee0SKavyasree Kotagiri 	.parent_data = lan966x_gck_pdata,
4854104ee0SKavyasree Kotagiri 	.num_parents = ARRAY_SIZE(lan966x_gck_pdata),
4954104ee0SKavyasree Kotagiri };
5054104ee0SKavyasree Kotagiri 
515ad5915dSHoratiu Vultur struct clk_gate_soc_desc {
525ad5915dSHoratiu Vultur 	const char *name;
535ad5915dSHoratiu Vultur 	int bit_idx;
545ad5915dSHoratiu Vultur };
555ad5915dSHoratiu Vultur 
565ad5915dSHoratiu Vultur static const struct clk_gate_soc_desc clk_gate_desc[] = {
575ad5915dSHoratiu Vultur 	{ "uhphs", 11 },
585ad5915dSHoratiu Vultur 	{ "udphs", 10 },
595ad5915dSHoratiu Vultur 	{ "mcramc", 9 },
605ad5915dSHoratiu Vultur 	{ "hmatrix", 8 },
615ad5915dSHoratiu Vultur 	{ }
625ad5915dSHoratiu Vultur };
635ad5915dSHoratiu Vultur 
645ad5915dSHoratiu Vultur static DEFINE_SPINLOCK(clk_gate_lock);
6554104ee0SKavyasree Kotagiri static void __iomem *base;
6654104ee0SKavyasree Kotagiri 
lan966x_gck_enable(struct clk_hw * hw)6754104ee0SKavyasree Kotagiri static int lan966x_gck_enable(struct clk_hw *hw)
6854104ee0SKavyasree Kotagiri {
6954104ee0SKavyasree Kotagiri 	struct lan966x_gck *gck = to_lan966x_gck(hw);
7054104ee0SKavyasree Kotagiri 	u32 val = readl(gck->reg);
7154104ee0SKavyasree Kotagiri 
7254104ee0SKavyasree Kotagiri 	val |= GCK_ENA;
7354104ee0SKavyasree Kotagiri 	writel(val, gck->reg);
7454104ee0SKavyasree Kotagiri 
7554104ee0SKavyasree Kotagiri 	return 0;
7654104ee0SKavyasree Kotagiri }
7754104ee0SKavyasree Kotagiri 
lan966x_gck_disable(struct clk_hw * hw)7854104ee0SKavyasree Kotagiri static void lan966x_gck_disable(struct clk_hw *hw)
7954104ee0SKavyasree Kotagiri {
8054104ee0SKavyasree Kotagiri 	struct lan966x_gck *gck = to_lan966x_gck(hw);
8154104ee0SKavyasree Kotagiri 	u32 val = readl(gck->reg);
8254104ee0SKavyasree Kotagiri 
8354104ee0SKavyasree Kotagiri 	val &= ~GCK_ENA;
8454104ee0SKavyasree Kotagiri 	writel(val, gck->reg);
8554104ee0SKavyasree Kotagiri }
8654104ee0SKavyasree Kotagiri 
lan966x_gck_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)8754104ee0SKavyasree Kotagiri static int lan966x_gck_set_rate(struct clk_hw *hw,
8854104ee0SKavyasree Kotagiri 				unsigned long rate,
8954104ee0SKavyasree Kotagiri 				unsigned long parent_rate)
9054104ee0SKavyasree Kotagiri {
9154104ee0SKavyasree Kotagiri 	struct lan966x_gck *gck = to_lan966x_gck(hw);
9254104ee0SKavyasree Kotagiri 	u32 div, val = readl(gck->reg);
9354104ee0SKavyasree Kotagiri 
9454104ee0SKavyasree Kotagiri 	if (rate == 0 || parent_rate == 0)
9554104ee0SKavyasree Kotagiri 		return -EINVAL;
9654104ee0SKavyasree Kotagiri 
9754104ee0SKavyasree Kotagiri 	/* Set Prescalar */
9854104ee0SKavyasree Kotagiri 	div = parent_rate / rate;
9954104ee0SKavyasree Kotagiri 	val &= ~GCK_PRESCALER;
10054104ee0SKavyasree Kotagiri 	val |= FIELD_PREP(GCK_PRESCALER, (div - 1));
10154104ee0SKavyasree Kotagiri 	writel(val, gck->reg);
10254104ee0SKavyasree Kotagiri 
10354104ee0SKavyasree Kotagiri 	return 0;
10454104ee0SKavyasree Kotagiri }
10554104ee0SKavyasree Kotagiri 
lan966x_gck_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)10654104ee0SKavyasree Kotagiri static unsigned long lan966x_gck_recalc_rate(struct clk_hw *hw,
10754104ee0SKavyasree Kotagiri 					     unsigned long parent_rate)
10854104ee0SKavyasree Kotagiri {
10954104ee0SKavyasree Kotagiri 	struct lan966x_gck *gck = to_lan966x_gck(hw);
11054104ee0SKavyasree Kotagiri 	u32 div, val = readl(gck->reg);
11154104ee0SKavyasree Kotagiri 
11254104ee0SKavyasree Kotagiri 	div = FIELD_GET(GCK_PRESCALER, val);
11354104ee0SKavyasree Kotagiri 
11454104ee0SKavyasree Kotagiri 	return parent_rate / (div + 1);
11554104ee0SKavyasree Kotagiri }
11654104ee0SKavyasree Kotagiri 
lan966x_gck_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)11754104ee0SKavyasree Kotagiri static int lan966x_gck_determine_rate(struct clk_hw *hw,
11854104ee0SKavyasree Kotagiri 				      struct clk_rate_request *req)
11954104ee0SKavyasree Kotagiri {
12054104ee0SKavyasree Kotagiri 	struct clk_hw *parent;
12154104ee0SKavyasree Kotagiri 	int i;
12254104ee0SKavyasree Kotagiri 
12354104ee0SKavyasree Kotagiri 	for (i = 0; i < clk_hw_get_num_parents(hw); ++i) {
12454104ee0SKavyasree Kotagiri 		parent = clk_hw_get_parent_by_index(hw, i);
12554104ee0SKavyasree Kotagiri 		if (!parent)
12654104ee0SKavyasree Kotagiri 			continue;
12754104ee0SKavyasree Kotagiri 
12854104ee0SKavyasree Kotagiri 		/* Allowed prescaler divider range is 0-255 */
12954104ee0SKavyasree Kotagiri 		if (clk_hw_get_rate(parent) / req->rate <= DIV_MAX) {
13054104ee0SKavyasree Kotagiri 			req->best_parent_hw = parent;
13154104ee0SKavyasree Kotagiri 			req->best_parent_rate = clk_hw_get_rate(parent);
13254104ee0SKavyasree Kotagiri 
13354104ee0SKavyasree Kotagiri 			return 0;
13454104ee0SKavyasree Kotagiri 		}
13554104ee0SKavyasree Kotagiri 	}
13654104ee0SKavyasree Kotagiri 
13754104ee0SKavyasree Kotagiri 	return -EINVAL;
13854104ee0SKavyasree Kotagiri }
13954104ee0SKavyasree Kotagiri 
lan966x_gck_get_parent(struct clk_hw * hw)14054104ee0SKavyasree Kotagiri static u8 lan966x_gck_get_parent(struct clk_hw *hw)
14154104ee0SKavyasree Kotagiri {
14254104ee0SKavyasree Kotagiri 	struct lan966x_gck *gck = to_lan966x_gck(hw);
14354104ee0SKavyasree Kotagiri 	u32 val = readl(gck->reg);
14454104ee0SKavyasree Kotagiri 
14554104ee0SKavyasree Kotagiri 	return FIELD_GET(GCK_SRC_SEL, val);
14654104ee0SKavyasree Kotagiri }
14754104ee0SKavyasree Kotagiri 
lan966x_gck_set_parent(struct clk_hw * hw,u8 index)14854104ee0SKavyasree Kotagiri static int lan966x_gck_set_parent(struct clk_hw *hw, u8 index)
14954104ee0SKavyasree Kotagiri {
15054104ee0SKavyasree Kotagiri 	struct lan966x_gck *gck = to_lan966x_gck(hw);
15154104ee0SKavyasree Kotagiri 	u32 val = readl(gck->reg);
15254104ee0SKavyasree Kotagiri 
15354104ee0SKavyasree Kotagiri 	val &= ~GCK_SRC_SEL;
15454104ee0SKavyasree Kotagiri 	val |= FIELD_PREP(GCK_SRC_SEL, index);
15554104ee0SKavyasree Kotagiri 	writel(val, gck->reg);
15654104ee0SKavyasree Kotagiri 
15754104ee0SKavyasree Kotagiri 	return 0;
15854104ee0SKavyasree Kotagiri }
15954104ee0SKavyasree Kotagiri 
16054104ee0SKavyasree Kotagiri static const struct clk_ops lan966x_gck_ops = {
16154104ee0SKavyasree Kotagiri 	.enable         = lan966x_gck_enable,
16254104ee0SKavyasree Kotagiri 	.disable        = lan966x_gck_disable,
16354104ee0SKavyasree Kotagiri 	.set_rate       = lan966x_gck_set_rate,
16454104ee0SKavyasree Kotagiri 	.recalc_rate    = lan966x_gck_recalc_rate,
16554104ee0SKavyasree Kotagiri 	.determine_rate = lan966x_gck_determine_rate,
16654104ee0SKavyasree Kotagiri 	.set_parent     = lan966x_gck_set_parent,
16754104ee0SKavyasree Kotagiri 	.get_parent     = lan966x_gck_get_parent,
16854104ee0SKavyasree Kotagiri };
16954104ee0SKavyasree Kotagiri 
lan966x_gck_clk_register(struct device * dev,int i)17054104ee0SKavyasree Kotagiri static struct clk_hw *lan966x_gck_clk_register(struct device *dev, int i)
17154104ee0SKavyasree Kotagiri {
17254104ee0SKavyasree Kotagiri 	struct lan966x_gck *priv;
17354104ee0SKavyasree Kotagiri 	int ret;
17454104ee0SKavyasree Kotagiri 
17554104ee0SKavyasree Kotagiri 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
17654104ee0SKavyasree Kotagiri 	if (!priv)
17754104ee0SKavyasree Kotagiri 		return ERR_PTR(-ENOMEM);
17854104ee0SKavyasree Kotagiri 
17954104ee0SKavyasree Kotagiri 	priv->reg = base + (i * 4);
18054104ee0SKavyasree Kotagiri 	priv->hw.init = &init;
18154104ee0SKavyasree Kotagiri 	ret = devm_clk_hw_register(dev, &priv->hw);
18254104ee0SKavyasree Kotagiri 	if (ret)
18354104ee0SKavyasree Kotagiri 		return ERR_PTR(ret);
18454104ee0SKavyasree Kotagiri 
18554104ee0SKavyasree Kotagiri 	return &priv->hw;
18654104ee0SKavyasree Kotagiri };
18754104ee0SKavyasree Kotagiri 
lan966x_gate_clk_register(struct device * dev,struct clk_hw_onecell_data * hw_data,void __iomem * gate_base)1885ad5915dSHoratiu Vultur static int lan966x_gate_clk_register(struct device *dev,
1895ad5915dSHoratiu Vultur 				     struct clk_hw_onecell_data *hw_data,
1905ad5915dSHoratiu Vultur 				     void __iomem *gate_base)
1915ad5915dSHoratiu Vultur {
1925ad5915dSHoratiu Vultur 	int i;
1935ad5915dSHoratiu Vultur 
1945ad5915dSHoratiu Vultur 	for (i = GCK_GATE_UHPHS; i < N_CLOCKS; ++i) {
1955ad5915dSHoratiu Vultur 		int idx = i - GCK_GATE_UHPHS;
1965ad5915dSHoratiu Vultur 
1975ad5915dSHoratiu Vultur 		hw_data->hws[i] =
1985ad5915dSHoratiu Vultur 			devm_clk_hw_register_gate(dev, clk_gate_desc[idx].name,
19925c2a075SHerve Codina 						  "lan966x", 0, gate_base,
2005ad5915dSHoratiu Vultur 						  clk_gate_desc[idx].bit_idx,
2015ad5915dSHoratiu Vultur 						  0, &clk_gate_lock);
2025ad5915dSHoratiu Vultur 
2035ad5915dSHoratiu Vultur 		if (IS_ERR(hw_data->hws[i]))
2045ad5915dSHoratiu Vultur 			return dev_err_probe(dev, PTR_ERR(hw_data->hws[i]),
2055ad5915dSHoratiu Vultur 					     "failed to register %s clock\n",
2065ad5915dSHoratiu Vultur 					     clk_gate_desc[idx].name);
2075ad5915dSHoratiu Vultur 	}
2085ad5915dSHoratiu Vultur 
2095ad5915dSHoratiu Vultur 	return 0;
2105ad5915dSHoratiu Vultur }
2115ad5915dSHoratiu Vultur 
lan966x_clk_probe(struct platform_device * pdev)21254104ee0SKavyasree Kotagiri static int lan966x_clk_probe(struct platform_device *pdev)
21354104ee0SKavyasree Kotagiri {
21454104ee0SKavyasree Kotagiri 	struct clk_hw_onecell_data *hw_data;
21554104ee0SKavyasree Kotagiri 	struct device *dev = &pdev->dev;
2165ad5915dSHoratiu Vultur 	void __iomem *gate_base;
2175ad5915dSHoratiu Vultur 	struct resource *res;
2185ad5915dSHoratiu Vultur 	int i, ret;
21954104ee0SKavyasree Kotagiri 
22054104ee0SKavyasree Kotagiri 	hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, N_CLOCKS),
22154104ee0SKavyasree Kotagiri 			       GFP_KERNEL);
22254104ee0SKavyasree Kotagiri 	if (!hw_data)
22354104ee0SKavyasree Kotagiri 		return -ENOMEM;
22454104ee0SKavyasree Kotagiri 
22554104ee0SKavyasree Kotagiri 	base = devm_platform_ioremap_resource(pdev, 0);
22654104ee0SKavyasree Kotagiri 	if (IS_ERR(base))
22754104ee0SKavyasree Kotagiri 		return PTR_ERR(base);
22854104ee0SKavyasree Kotagiri 
22954104ee0SKavyasree Kotagiri 	init.ops = &lan966x_gck_ops;
23054104ee0SKavyasree Kotagiri 
2315ad5915dSHoratiu Vultur 	hw_data->num = GCK_GATE_UHPHS;
23254104ee0SKavyasree Kotagiri 
2335ad5915dSHoratiu Vultur 	for (i = 0; i < GCK_GATE_UHPHS; i++) {
23454104ee0SKavyasree Kotagiri 		init.name = clk_names[i];
23554104ee0SKavyasree Kotagiri 		hw_data->hws[i] = lan966x_gck_clk_register(dev, i);
23654104ee0SKavyasree Kotagiri 		if (IS_ERR(hw_data->hws[i])) {
23754104ee0SKavyasree Kotagiri 			dev_err(dev, "failed to register %s clock\n",
23854104ee0SKavyasree Kotagiri 				init.name);
23954104ee0SKavyasree Kotagiri 			return PTR_ERR(hw_data->hws[i]);
24054104ee0SKavyasree Kotagiri 		}
24154104ee0SKavyasree Kotagiri 	}
24254104ee0SKavyasree Kotagiri 
2435ad5915dSHoratiu Vultur 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
2445ad5915dSHoratiu Vultur 	if (res) {
2455ad5915dSHoratiu Vultur 		gate_base = devm_ioremap_resource(&pdev->dev, res);
2465ad5915dSHoratiu Vultur 		if (IS_ERR(gate_base))
2475ad5915dSHoratiu Vultur 			return PTR_ERR(gate_base);
2485ad5915dSHoratiu Vultur 
2495ad5915dSHoratiu Vultur 		hw_data->num = N_CLOCKS;
2505ad5915dSHoratiu Vultur 
2515ad5915dSHoratiu Vultur 		ret = lan966x_gate_clk_register(dev, hw_data, gate_base);
2525ad5915dSHoratiu Vultur 		if (ret)
2535ad5915dSHoratiu Vultur 			return ret;
2545ad5915dSHoratiu Vultur 	}
2555ad5915dSHoratiu Vultur 
25654104ee0SKavyasree Kotagiri 	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data);
25754104ee0SKavyasree Kotagiri }
25854104ee0SKavyasree Kotagiri 
25954104ee0SKavyasree Kotagiri static const struct of_device_id lan966x_clk_dt_ids[] = {
26054104ee0SKavyasree Kotagiri 	{ .compatible = "microchip,lan966x-gck", },
26154104ee0SKavyasree Kotagiri 	{ }
26254104ee0SKavyasree Kotagiri };
26354104ee0SKavyasree Kotagiri MODULE_DEVICE_TABLE(of, lan966x_clk_dt_ids);
26454104ee0SKavyasree Kotagiri 
26554104ee0SKavyasree Kotagiri static struct platform_driver lan966x_clk_driver = {
26654104ee0SKavyasree Kotagiri 	.probe  = lan966x_clk_probe,
26754104ee0SKavyasree Kotagiri 	.driver = {
26854104ee0SKavyasree Kotagiri 		.name = "lan966x-clk",
26954104ee0SKavyasree Kotagiri 		.of_match_table = lan966x_clk_dt_ids,
27054104ee0SKavyasree Kotagiri 	},
27154104ee0SKavyasree Kotagiri };
272*8a977bbbSClément Léger module_platform_driver(lan966x_clk_driver);
27354104ee0SKavyasree Kotagiri 
27454104ee0SKavyasree Kotagiri MODULE_AUTHOR("Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>");
27554104ee0SKavyasree Kotagiri MODULE_DESCRIPTION("LAN966X clock driver");
27654104ee0SKavyasree Kotagiri MODULE_LICENSE("GPL v2");
277