Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

clk: lan966x: Add lan966x SoC clock driver

This adds Generic Clock Controller driver for lan966x SoC.

Lan966x clock controller contains 3 PLLs - cpu_clk, ddr_clk
and sys_clk. It generates and supplies clock to various
peripherals within SoC.
Register settings required to provide GCK clocking to a
peripheral is as below:
GCK_SRC_SEL = Select clock source.
GCK_PRESCALER = Set divider value.
GCK_ENA = 1 - Enable GCK clock.

Signed-off-by: Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>
Co-developed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@microchip.com>
Link: https://lore.kernel.org/r/20211103061935.25677-4-kavyasree.kotagiri@microchip.com

authored by

Kavyasree Kotagiri and committed by
Nicolas Ferre
54104ee0 07300ef4

+248
+7
drivers/clk/Kconfig
··· 221 221 This driver supports the SoC clocks on the Cortina Systems Gemini 222 222 platform, also known as SL3516 or CS3516. 223 223 224 + config COMMON_CLK_LAN966X 225 + bool "Generic Clock Controller driver for LAN966X SoC" 226 + help 227 + This driver provides support for Generic Clock Controller(GCK) on 228 + LAN966X SoC. GCK generates and supplies clock to various peripherals 229 + within the SoC. 230 + 224 231 config COMMON_CLK_ASPEED 225 232 bool "Clock driver for Aspeed BMC SoCs" 226 233 depends on ARCH_ASPEED || COMPILE_TEST
+1
drivers/clk/Makefile
··· 37 37 obj-$(CONFIG_CLK_HSDK) += clk-hsdk-pll.o 38 38 obj-$(CONFIG_COMMON_CLK_K210) += clk-k210.o 39 39 obj-$(CONFIG_LMK04832) += clk-lmk04832.o 40 + obj-$(CONFIG_COMMON_CLK_LAN966X) += clk-lan966x.o 40 41 obj-$(CONFIG_COMMON_CLK_LOCHNAGAR) += clk-lochnagar.o 41 42 obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o 42 43 obj-$(CONFIG_COMMON_CLK_MAX9485) += clk-max9485.o
+240
drivers/clk/clk-lan966x.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Microchip LAN966x SoC Clock driver. 4 + * 5 + * Copyright (C) 2021 Microchip Technology, Inc. and its subsidiaries 6 + * 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 255 26 + 27 + static 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 + 34 + struct 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 + 40 + static const struct clk_parent_data lan966x_gck_pdata[] = { 41 + { .fw_name = "cpu", }, 42 + { .fw_name = "ddr", }, 43 + { .fw_name = "sys", }, 44 + }; 45 + 46 + static struct clk_init_data init = { 47 + .parent_data = lan966x_gck_pdata, 48 + .num_parents = ARRAY_SIZE(lan966x_gck_pdata), 49 + }; 50 + 51 + static void __iomem *base; 52 + 53 + static int lan966x_gck_enable(struct clk_hw *hw) 54 + { 55 + struct lan966x_gck *gck = to_lan966x_gck(hw); 56 + u32 val = readl(gck->reg); 57 + 58 + val |= GCK_ENA; 59 + writel(val, gck->reg); 60 + 61 + return 0; 62 + } 63 + 64 + static void lan966x_gck_disable(struct clk_hw *hw) 65 + { 66 + struct lan966x_gck *gck = to_lan966x_gck(hw); 67 + u32 val = readl(gck->reg); 68 + 69 + val &= ~GCK_ENA; 70 + writel(val, gck->reg); 71 + } 72 + 73 + static int lan966x_gck_set_rate(struct clk_hw *hw, 74 + unsigned long rate, 75 + unsigned long parent_rate) 76 + { 77 + struct lan966x_gck *gck = to_lan966x_gck(hw); 78 + u32 div, val = readl(gck->reg); 79 + 80 + if (rate == 0 || parent_rate == 0) 81 + return -EINVAL; 82 + 83 + /* Set Prescalar */ 84 + div = parent_rate / rate; 85 + val &= ~GCK_PRESCALER; 86 + val |= FIELD_PREP(GCK_PRESCALER, (div - 1)); 87 + writel(val, gck->reg); 88 + 89 + return 0; 90 + } 91 + 92 + static long lan966x_gck_round_rate(struct clk_hw *hw, unsigned long rate, 93 + unsigned long *parent_rate) 94 + { 95 + unsigned int div; 96 + 97 + if (rate == 0 || *parent_rate == 0) 98 + return -EINVAL; 99 + 100 + if (rate >= *parent_rate) 101 + return *parent_rate; 102 + 103 + div = DIV_ROUND_CLOSEST(*parent_rate, rate); 104 + 105 + return *parent_rate / div; 106 + } 107 + 108 + static unsigned long lan966x_gck_recalc_rate(struct clk_hw *hw, 109 + unsigned long parent_rate) 110 + { 111 + struct lan966x_gck *gck = to_lan966x_gck(hw); 112 + u32 div, val = readl(gck->reg); 113 + 114 + div = FIELD_GET(GCK_PRESCALER, val); 115 + 116 + return parent_rate / (div + 1); 117 + } 118 + 119 + static int lan966x_gck_determine_rate(struct clk_hw *hw, 120 + struct clk_rate_request *req) 121 + { 122 + struct clk_hw *parent; 123 + int i; 124 + 125 + for (i = 0; i < clk_hw_get_num_parents(hw); ++i) { 126 + parent = clk_hw_get_parent_by_index(hw, i); 127 + if (!parent) 128 + continue; 129 + 130 + /* Allowed prescaler divider range is 0-255 */ 131 + if (clk_hw_get_rate(parent) / req->rate <= DIV_MAX) { 132 + req->best_parent_hw = parent; 133 + req->best_parent_rate = clk_hw_get_rate(parent); 134 + 135 + return 0; 136 + } 137 + } 138 + 139 + return -EINVAL; 140 + } 141 + 142 + static u8 lan966x_gck_get_parent(struct clk_hw *hw) 143 + { 144 + struct lan966x_gck *gck = to_lan966x_gck(hw); 145 + u32 val = readl(gck->reg); 146 + 147 + return FIELD_GET(GCK_SRC_SEL, val); 148 + } 149 + 150 + static int lan966x_gck_set_parent(struct clk_hw *hw, u8 index) 151 + { 152 + struct lan966x_gck *gck = to_lan966x_gck(hw); 153 + u32 val = readl(gck->reg); 154 + 155 + val &= ~GCK_SRC_SEL; 156 + val |= FIELD_PREP(GCK_SRC_SEL, index); 157 + writel(val, gck->reg); 158 + 159 + return 0; 160 + } 161 + 162 + static const struct clk_ops lan966x_gck_ops = { 163 + .enable = lan966x_gck_enable, 164 + .disable = lan966x_gck_disable, 165 + .set_rate = lan966x_gck_set_rate, 166 + .round_rate = lan966x_gck_round_rate, 167 + .recalc_rate = lan966x_gck_recalc_rate, 168 + .determine_rate = lan966x_gck_determine_rate, 169 + .set_parent = lan966x_gck_set_parent, 170 + .get_parent = lan966x_gck_get_parent, 171 + }; 172 + 173 + static struct clk_hw *lan966x_gck_clk_register(struct device *dev, int i) 174 + { 175 + struct lan966x_gck *priv; 176 + int ret; 177 + 178 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 179 + if (!priv) 180 + return ERR_PTR(-ENOMEM); 181 + 182 + priv->reg = base + (i * 4); 183 + priv->hw.init = &init; 184 + ret = devm_clk_hw_register(dev, &priv->hw); 185 + if (ret) 186 + return ERR_PTR(ret); 187 + 188 + return &priv->hw; 189 + }; 190 + 191 + static int lan966x_clk_probe(struct platform_device *pdev) 192 + { 193 + struct clk_hw_onecell_data *hw_data; 194 + struct device *dev = &pdev->dev; 195 + int i; 196 + 197 + hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, N_CLOCKS), 198 + GFP_KERNEL); 199 + if (!hw_data) 200 + return -ENOMEM; 201 + 202 + base = devm_platform_ioremap_resource(pdev, 0); 203 + if (IS_ERR(base)) 204 + return PTR_ERR(base); 205 + 206 + init.ops = &lan966x_gck_ops; 207 + 208 + hw_data->num = N_CLOCKS; 209 + 210 + for (i = 0; i < N_CLOCKS; i++) { 211 + init.name = clk_names[i]; 212 + hw_data->hws[i] = lan966x_gck_clk_register(dev, i); 213 + if (IS_ERR(hw_data->hws[i])) { 214 + dev_err(dev, "failed to register %s clock\n", 215 + init.name); 216 + return PTR_ERR(hw_data->hws[i]); 217 + } 218 + } 219 + 220 + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data); 221 + } 222 + 223 + static const struct of_device_id lan966x_clk_dt_ids[] = { 224 + { .compatible = "microchip,lan966x-gck", }, 225 + { } 226 + }; 227 + MODULE_DEVICE_TABLE(of, lan966x_clk_dt_ids); 228 + 229 + static struct platform_driver lan966x_clk_driver = { 230 + .probe = lan966x_clk_probe, 231 + .driver = { 232 + .name = "lan966x-clk", 233 + .of_match_table = lan966x_clk_dt_ids, 234 + }, 235 + }; 236 + builtin_platform_driver(lan966x_clk_driver); 237 + 238 + MODULE_AUTHOR("Kavyasree Kotagiri <kavyasree.kotagiri@microchip.com>"); 239 + MODULE_DESCRIPTION("LAN966X clock driver"); 240 + MODULE_LICENSE("GPL v2");