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