brintos

brintos / linux-shallow public Read only

0
0
Text · 6.2 KiB · 870fd7d Raw
277 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Microchip LAN966x SoC Clock driver.4 *5 * Copyright (C) 2021 Microchip Technology, Inc. and its subsidiaries6 *7 * Author: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>8 */9 10#include <linux/bitfield.h>11#include <linux/clk-provider.h>12#include <linux/io.h>13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/of.h>16#include <linux/platform_device.h>17#include <linux/slab.h>18 19#include <dt-bindings/clock/microchip,lan966x.h>20 21#define GCK_ENA         BIT(0)22#define GCK_SRC_SEL     GENMASK(9, 8)23#define GCK_PRESCALER   GENMASK(23, 16)24 25#define DIV_MAX		25526 27static const char *clk_names[N_CLOCKS] = {28	"qspi0", "qspi1", "qspi2", "sdmmc0",29	"pi", "mcan0", "mcan1", "flexcom0",30	"flexcom1", "flexcom2", "flexcom3",31	"flexcom4", "timer1", "usb_refclk",32};33 34struct lan966x_gck {35	struct clk_hw hw;36	void __iomem *reg;37};38#define to_lan966x_gck(hw) container_of(hw, struct lan966x_gck, hw)39 40static const struct clk_parent_data lan966x_gck_pdata[] = {41	{ .fw_name = "cpu", },42	{ .fw_name = "ddr", },43	{ .fw_name = "sys", },44};45 46static struct clk_init_data init = {47	.parent_data = lan966x_gck_pdata,48	.num_parents = ARRAY_SIZE(lan966x_gck_pdata),49};50 51struct clk_gate_soc_desc {52	const char *name;53	int bit_idx;54};55 56static const struct clk_gate_soc_desc clk_gate_desc[] = {57	{ "uhphs", 11 },58	{ "udphs", 10 },59	{ "mcramc", 9 },60	{ "hmatrix", 8 },61	{ }62};63 64static DEFINE_SPINLOCK(clk_gate_lock);65static void __iomem *base;66 67static int lan966x_gck_enable(struct clk_hw *hw)68{69	struct lan966x_gck *gck = to_lan966x_gck(hw);70	u32 val = readl(gck->reg);71 72	val |= GCK_ENA;73	writel(val, gck->reg);74 75	return 0;76}77 78static void lan966x_gck_disable(struct clk_hw *hw)79{80	struct lan966x_gck *gck = to_lan966x_gck(hw);81	u32 val = readl(gck->reg);82 83	val &= ~GCK_ENA;84	writel(val, gck->reg);85}86 87static int lan966x_gck_set_rate(struct clk_hw *hw,88				unsigned long rate,89				unsigned long parent_rate)90{91	struct lan966x_gck *gck = to_lan966x_gck(hw);92	u32 div, val = readl(gck->reg);93 94	if (rate == 0 || parent_rate == 0)95		return -EINVAL;96 97	/* Set Prescalar */98	div = parent_rate / rate;99	val &= ~GCK_PRESCALER;100	val |= FIELD_PREP(GCK_PRESCALER, (div - 1));101	writel(val, gck->reg);102 103	return 0;104}105 106static unsigned long lan966x_gck_recalc_rate(struct clk_hw *hw,107					     unsigned long parent_rate)108{109	struct lan966x_gck *gck = to_lan966x_gck(hw);110	u32 div, val = readl(gck->reg);111 112	div = FIELD_GET(GCK_PRESCALER, val);113 114	return parent_rate / (div + 1);115}116 117static int lan966x_gck_determine_rate(struct clk_hw *hw,118				      struct clk_rate_request *req)119{120	struct clk_hw *parent;121	int i;122 123	for (i = 0; i < clk_hw_get_num_parents(hw); ++i) {124		parent = clk_hw_get_parent_by_index(hw, i);125		if (!parent)126			continue;127 128		/* Allowed prescaler divider range is 0-255 */129		if (clk_hw_get_rate(parent) / req->rate <= DIV_MAX) {130			req->best_parent_hw = parent;131			req->best_parent_rate = clk_hw_get_rate(parent);132 133			return 0;134		}135	}136 137	return -EINVAL;138}139 140static u8 lan966x_gck_get_parent(struct clk_hw *hw)141{142	struct lan966x_gck *gck = to_lan966x_gck(hw);143	u32 val = readl(gck->reg);144 145	return FIELD_GET(GCK_SRC_SEL, val);146}147 148static int lan966x_gck_set_parent(struct clk_hw *hw, u8 index)149{150	struct lan966x_gck *gck = to_lan966x_gck(hw);151	u32 val = readl(gck->reg);152 153	val &= ~GCK_SRC_SEL;154	val |= FIELD_PREP(GCK_SRC_SEL, index);155	writel(val, gck->reg);156 157	return 0;158}159 160static const struct clk_ops lan966x_gck_ops = {161	.enable         = lan966x_gck_enable,162	.disable        = lan966x_gck_disable,163	.set_rate       = lan966x_gck_set_rate,164	.recalc_rate    = lan966x_gck_recalc_rate,165	.determine_rate = lan966x_gck_determine_rate,166	.set_parent     = lan966x_gck_set_parent,167	.get_parent     = lan966x_gck_get_parent,168};169 170static struct clk_hw *lan966x_gck_clk_register(struct device *dev, int i)171{172	struct lan966x_gck *priv;173	int ret;174 175	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);176	if (!priv)177		return ERR_PTR(-ENOMEM);178 179	priv->reg = base + (i * 4);180	priv->hw.init = &init;181	ret = devm_clk_hw_register(dev, &priv->hw);182	if (ret)183		return ERR_PTR(ret);184 185	return &priv->hw;186};187 188static int lan966x_gate_clk_register(struct device *dev,189				     struct clk_hw_onecell_data *hw_data,190				     void __iomem *gate_base)191{192	int i;193 194	for (i = GCK_GATE_UHPHS; i < N_CLOCKS; ++i) {195		int idx = i - GCK_GATE_UHPHS;196 197		hw_data->hws[i] =198			devm_clk_hw_register_gate(dev, clk_gate_desc[idx].name,199						  "lan966x", 0, gate_base,200						  clk_gate_desc[idx].bit_idx,201						  0, &clk_gate_lock);202 203		if (IS_ERR(hw_data->hws[i]))204			return dev_err_probe(dev, PTR_ERR(hw_data->hws[i]),205					     "failed to register %s clock\n",206					     clk_gate_desc[idx].name);207	}208 209	return 0;210}211 212static int lan966x_clk_probe(struct platform_device *pdev)213{214	struct clk_hw_onecell_data *hw_data;215	struct device *dev = &pdev->dev;216	void __iomem *gate_base;217	struct resource *res;218	int i, ret;219 220	hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, N_CLOCKS),221			       GFP_KERNEL);222	if (!hw_data)223		return -ENOMEM;224 225	base = devm_platform_ioremap_resource(pdev, 0);226	if (IS_ERR(base))227		return PTR_ERR(base);228 229	init.ops = &lan966x_gck_ops;230 231	hw_data->num = GCK_GATE_UHPHS;232 233	for (i = 0; i < GCK_GATE_UHPHS; i++) {234		init.name = clk_names[i];235		hw_data->hws[i] = lan966x_gck_clk_register(dev, i);236		if (IS_ERR(hw_data->hws[i])) {237			dev_err(dev, "failed to register %s clock\n",238				init.name);239			return PTR_ERR(hw_data->hws[i]);240		}241	}242 243	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);244	if (res) {245		gate_base = devm_ioremap_resource(&pdev->dev, res);246		if (IS_ERR(gate_base))247			return PTR_ERR(gate_base);248 249		hw_data->num = N_CLOCKS;250 251		ret = lan966x_gate_clk_register(dev, hw_data, gate_base);252		if (ret)253			return ret;254	}255 256	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data);257}258 259static const struct of_device_id lan966x_clk_dt_ids[] = {260	{ .compatible = "microchip,lan966x-gck", },261	{ }262};263MODULE_DEVICE_TABLE(of, lan966x_clk_dt_ids);264 265static struct platform_driver lan966x_clk_driver = {266	.probe  = lan966x_clk_probe,267	.driver = {268		.name = "lan966x-clk",269		.of_match_table = lan966x_clk_dt_ids,270	},271};272module_platform_driver(lan966x_clk_driver);273 274MODULE_AUTHOR("Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>");275MODULE_DESCRIPTION("LAN966X clock driver");276MODULE_LICENSE("GPL v2");277