brintos

brintos / linux-shallow public Read only

0
0
Text · 12.8 KiB · 4a3462e Raw
596 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * drivers/clk/clk-axm5516.c4 *5 * Provides clock implementations for three different types of clock devices on6 * the Axxia device: PLL clock, a clock divider and a clock mux.7 *8 * Copyright (C) 2014 LSI Corporation9 */10#include <linux/module.h>11#include <linux/kernel.h>12#include <linux/slab.h>13#include <linux/platform_device.h>14#include <linux/of.h>15#include <linux/of_address.h>16#include <linux/clk-provider.h>17#include <linux/regmap.h>18#include <dt-bindings/clock/lsi,axm5516-clks.h>19 20 21/**22 * struct axxia_clk - Common struct to all Axxia clocks.23 * @hw: clk_hw for the common clk framework24 * @regmap: Regmap for the clock control registers25 */26struct axxia_clk {27	struct clk_hw hw;28	struct regmap *regmap;29};30#define to_axxia_clk(_hw) container_of(_hw, struct axxia_clk, hw)31 32/**33 * struct axxia_pllclk - Axxia PLL generated clock.34 * @aclk: Common struct35 * @reg: Offset into regmap for PLL control register36 */37struct axxia_pllclk {38	struct axxia_clk aclk;39	u32 reg;40};41#define to_axxia_pllclk(_aclk) container_of(_aclk, struct axxia_pllclk, aclk)42 43/**44 * axxia_pllclk_recalc - Calculate the PLL generated clock rate given the45 * parent clock rate.46 */47static unsigned long48axxia_pllclk_recalc(struct clk_hw *hw, unsigned long parent_rate)49{50	struct axxia_clk *aclk = to_axxia_clk(hw);51	struct axxia_pllclk *pll = to_axxia_pllclk(aclk);52	unsigned long rate, fbdiv, refdiv, postdiv;53	u32 control;54 55	regmap_read(aclk->regmap, pll->reg, &control);56	postdiv = ((control >> 0) & 0xf) + 1;57	fbdiv   = ((control >> 4) & 0xfff) + 3;58	refdiv  = ((control >> 16) & 0x1f) + 1;59	rate = (parent_rate / (refdiv * postdiv)) * fbdiv;60 61	return rate;62}63 64static const struct clk_ops axxia_pllclk_ops = {65	.recalc_rate = axxia_pllclk_recalc,66};67 68/**69 * struct axxia_divclk - Axxia clock divider70 * @aclk: Common struct71 * @reg: Offset into regmap for PLL control register72 * @shift: Bit position for divider value73 * @width: Number of bits in divider value74 */75struct axxia_divclk {76	struct axxia_clk aclk;77	u32 reg;78	u32 shift;79	u32 width;80};81#define to_axxia_divclk(_aclk) container_of(_aclk, struct axxia_divclk, aclk)82 83/**84 * axxia_divclk_recalc_rate - Calculate clock divider output rage85 */86static unsigned long87axxia_divclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)88{89	struct axxia_clk *aclk = to_axxia_clk(hw);90	struct axxia_divclk *divclk = to_axxia_divclk(aclk);91	u32 ctrl, div;92 93	regmap_read(aclk->regmap, divclk->reg, &ctrl);94	div = 1 + ((ctrl >> divclk->shift) & ((1 << divclk->width)-1));95 96	return parent_rate / div;97}98 99static const struct clk_ops axxia_divclk_ops = {100	.recalc_rate = axxia_divclk_recalc_rate,101};102 103/**104 * struct axxia_clkmux - Axxia clock mux105 * @aclk: Common struct106 * @reg: Offset into regmap for PLL control register107 * @shift: Bit position for selection value108 * @width: Number of bits in selection value109 */110struct axxia_clkmux {111	struct axxia_clk aclk;112	u32 reg;113	u32 shift;114	u32 width;115};116#define to_axxia_clkmux(_aclk) container_of(_aclk, struct axxia_clkmux, aclk)117 118/**119 * axxia_clkmux_get_parent - Return the index of selected parent clock120 */121static u8 axxia_clkmux_get_parent(struct clk_hw *hw)122{123	struct axxia_clk *aclk = to_axxia_clk(hw);124	struct axxia_clkmux *mux = to_axxia_clkmux(aclk);125	u32 ctrl, parent;126 127	regmap_read(aclk->regmap, mux->reg, &ctrl);128	parent = (ctrl >> mux->shift) & ((1 << mux->width) - 1);129 130	return (u8) parent;131}132 133static const struct clk_ops axxia_clkmux_ops = {134	.get_parent = axxia_clkmux_get_parent,135};136 137 138/*139 * PLLs140 */141 142static struct axxia_pllclk clk_fab_pll = {143	.aclk.hw.init = &(struct clk_init_data){144		.name = "clk_fab_pll",145		.parent_names = (const char *[]){146			"clk_ref0"147		},148		.num_parents = 1,149		.ops = &axxia_pllclk_ops,150	},151	.reg   = 0x01800,152};153 154static struct axxia_pllclk clk_cpu_pll = {155	.aclk.hw.init = &(struct clk_init_data){156		.name = "clk_cpu_pll",157		.parent_names = (const char *[]){158			"clk_ref0"159		},160		.num_parents = 1,161		.ops = &axxia_pllclk_ops,162	},163	.reg   = 0x02000,164};165 166static struct axxia_pllclk clk_sys_pll = {167	.aclk.hw.init = &(struct clk_init_data){168		.name = "clk_sys_pll",169		.parent_names = (const char *[]){170			"clk_ref0"171		},172		.num_parents = 1,173		.ops = &axxia_pllclk_ops,174	},175	.reg   = 0x02800,176};177 178static struct axxia_pllclk clk_sm0_pll = {179	.aclk.hw.init = &(struct clk_init_data){180		.name = "clk_sm0_pll",181		.parent_names = (const char *[]){182			"clk_ref2"183		},184		.num_parents = 1,185		.ops = &axxia_pllclk_ops,186	},187	.reg   = 0x03000,188};189 190static struct axxia_pllclk clk_sm1_pll = {191	.aclk.hw.init = &(struct clk_init_data){192		.name = "clk_sm1_pll",193		.parent_names = (const char *[]){194			"clk_ref1"195		},196		.num_parents = 1,197		.ops = &axxia_pllclk_ops,198	},199	.reg   = 0x03800,200};201 202/*203 * Clock dividers204 */205 206static struct axxia_divclk clk_cpu0_div = {207	.aclk.hw.init = &(struct clk_init_data){208		.name = "clk_cpu0_div",209		.parent_names = (const char *[]){210			"clk_cpu_pll"211		},212		.num_parents = 1,213		.ops = &axxia_divclk_ops,214	},215	.reg   = 0x10008,216	.shift = 0,217	.width = 4,218};219 220static struct axxia_divclk clk_cpu1_div = {221	.aclk.hw.init = &(struct clk_init_data){222		.name = "clk_cpu1_div",223		.parent_names = (const char *[]){224			"clk_cpu_pll"225		},226		.num_parents = 1,227		.ops = &axxia_divclk_ops,228	},229	.reg   = 0x10008,230	.shift = 4,231	.width = 4,232};233 234static struct axxia_divclk clk_cpu2_div = {235	.aclk.hw.init = &(struct clk_init_data){236		.name = "clk_cpu2_div",237		.parent_names = (const char *[]){238			"clk_cpu_pll"239		},240		.num_parents = 1,241		.ops = &axxia_divclk_ops,242	},243	.reg   = 0x10008,244	.shift = 8,245	.width = 4,246};247 248static struct axxia_divclk clk_cpu3_div = {249	.aclk.hw.init = &(struct clk_init_data){250		.name = "clk_cpu3_div",251		.parent_names = (const char *[]){252			"clk_cpu_pll"253		},254		.num_parents = 1,255		.ops = &axxia_divclk_ops,256	},257	.reg   = 0x10008,258	.shift = 12,259	.width = 4,260};261 262static struct axxia_divclk clk_nrcp_div = {263	.aclk.hw.init = &(struct clk_init_data){264		.name = "clk_nrcp_div",265		.parent_names = (const char *[]){266			"clk_sys_pll"267		},268		.num_parents = 1,269		.ops = &axxia_divclk_ops,270	},271	.reg   = 0x1000c,272	.shift = 0,273	.width = 4,274};275 276static struct axxia_divclk clk_sys_div = {277	.aclk.hw.init = &(struct clk_init_data){278		.name = "clk_sys_div",279		.parent_names = (const char *[]){280			"clk_sys_pll"281		},282		.num_parents = 1,283		.ops = &axxia_divclk_ops,284	},285	.reg   = 0x1000c,286	.shift = 4,287	.width = 4,288};289 290static struct axxia_divclk clk_fab_div = {291	.aclk.hw.init = &(struct clk_init_data){292		.name = "clk_fab_div",293		.parent_names = (const char *[]){294			"clk_fab_pll"295		},296		.num_parents = 1,297		.ops = &axxia_divclk_ops,298	},299	.reg   = 0x1000c,300	.shift = 8,301	.width = 4,302};303 304static struct axxia_divclk clk_per_div = {305	.aclk.hw.init = &(struct clk_init_data){306		.name = "clk_per_div",307		.parent_names = (const char *[]){308			"clk_sm1_pll"309		},310		.num_parents = 1,311		.ops = &axxia_divclk_ops,312	},313	.reg   = 0x1000c,314	.shift = 12,315	.width = 4,316};317 318static struct axxia_divclk clk_mmc_div = {319	.aclk.hw.init = &(struct clk_init_data){320		.name = "clk_mmc_div",321		.parent_names = (const char *[]){322			"clk_sm1_pll"323		},324		.num_parents = 1,325		.ops = &axxia_divclk_ops,326	},327	.reg   = 0x1000c,328	.shift = 16,329	.width = 4,330};331 332/*333 * Clock MUXes334 */335 336static struct axxia_clkmux clk_cpu0_mux = {337	.aclk.hw.init = &(struct clk_init_data){338		.name = "clk_cpu0",339		.parent_names = (const char *[]){340			"clk_ref0",341			"clk_cpu_pll",342			"clk_cpu0_div",343			"clk_cpu0_div"344		},345		.num_parents = 4,346		.ops = &axxia_clkmux_ops,347	},348	.reg   = 0x10000,349	.shift = 0,350	.width = 2,351};352 353static struct axxia_clkmux clk_cpu1_mux = {354	.aclk.hw.init = &(struct clk_init_data){355		.name = "clk_cpu1",356		.parent_names = (const char *[]){357			"clk_ref0",358			"clk_cpu_pll",359			"clk_cpu1_div",360			"clk_cpu1_div"361		},362		.num_parents = 4,363		.ops = &axxia_clkmux_ops,364	},365	.reg   = 0x10000,366	.shift = 2,367	.width = 2,368};369 370static struct axxia_clkmux clk_cpu2_mux = {371	.aclk.hw.init = &(struct clk_init_data){372		.name = "clk_cpu2",373		.parent_names = (const char *[]){374			"clk_ref0",375			"clk_cpu_pll",376			"clk_cpu2_div",377			"clk_cpu2_div"378		},379		.num_parents = 4,380		.ops = &axxia_clkmux_ops,381	},382	.reg   = 0x10000,383	.shift = 4,384	.width = 2,385};386 387static struct axxia_clkmux clk_cpu3_mux = {388	.aclk.hw.init = &(struct clk_init_data){389		.name = "clk_cpu3",390		.parent_names = (const char *[]){391			"clk_ref0",392			"clk_cpu_pll",393			"clk_cpu3_div",394			"clk_cpu3_div"395		},396		.num_parents = 4,397		.ops = &axxia_clkmux_ops,398	},399	.reg   = 0x10000,400	.shift = 6,401	.width = 2,402};403 404static struct axxia_clkmux clk_nrcp_mux = {405	.aclk.hw.init = &(struct clk_init_data){406		.name = "clk_nrcp",407		.parent_names = (const char *[]){408			"clk_ref0",409			"clk_sys_pll",410			"clk_nrcp_div",411			"clk_nrcp_div"412		},413		.num_parents = 4,414		.ops = &axxia_clkmux_ops,415	},416	.reg   = 0x10004,417	.shift = 0,418	.width = 2,419};420 421static struct axxia_clkmux clk_sys_mux = {422	.aclk.hw.init = &(struct clk_init_data){423		.name = "clk_sys",424		.parent_names = (const char *[]){425			"clk_ref0",426			"clk_sys_pll",427			"clk_sys_div",428			"clk_sys_div"429		},430		.num_parents = 4,431		.ops = &axxia_clkmux_ops,432	},433	.reg   = 0x10004,434	.shift = 2,435	.width = 2,436};437 438static struct axxia_clkmux clk_fab_mux = {439	.aclk.hw.init = &(struct clk_init_data){440		.name = "clk_fab",441		.parent_names = (const char *[]){442			"clk_ref0",443			"clk_fab_pll",444			"clk_fab_div",445			"clk_fab_div"446		},447		.num_parents = 4,448		.ops = &axxia_clkmux_ops,449	},450	.reg   = 0x10004,451	.shift = 4,452	.width = 2,453};454 455static struct axxia_clkmux clk_per_mux = {456	.aclk.hw.init = &(struct clk_init_data){457		.name = "clk_per",458		.parent_names = (const char *[]){459			"clk_ref1",460			"clk_per_div"461		},462		.num_parents = 2,463		.ops = &axxia_clkmux_ops,464	},465	.reg   = 0x10004,466	.shift = 6,467	.width = 1,468};469 470static struct axxia_clkmux clk_mmc_mux = {471	.aclk.hw.init = &(struct clk_init_data){472		.name = "clk_mmc",473		.parent_names = (const char *[]){474			"clk_ref1",475			"clk_mmc_div"476		},477		.num_parents = 2,478		.ops = &axxia_clkmux_ops,479	},480	.reg   = 0x10004,481	.shift = 9,482	.width = 1,483};484 485/* Table of all supported clocks indexed by the clock identifiers from the486 * device tree binding487 */488static struct axxia_clk *axmclk_clocks[] = {489	[AXXIA_CLK_FAB_PLL]  = &clk_fab_pll.aclk,490	[AXXIA_CLK_CPU_PLL]  = &clk_cpu_pll.aclk,491	[AXXIA_CLK_SYS_PLL]  = &clk_sys_pll.aclk,492	[AXXIA_CLK_SM0_PLL]  = &clk_sm0_pll.aclk,493	[AXXIA_CLK_SM1_PLL]  = &clk_sm1_pll.aclk,494	[AXXIA_CLK_FAB_DIV]  = &clk_fab_div.aclk,495	[AXXIA_CLK_SYS_DIV]  = &clk_sys_div.aclk,496	[AXXIA_CLK_NRCP_DIV] = &clk_nrcp_div.aclk,497	[AXXIA_CLK_CPU0_DIV] = &clk_cpu0_div.aclk,498	[AXXIA_CLK_CPU1_DIV] = &clk_cpu1_div.aclk,499	[AXXIA_CLK_CPU2_DIV] = &clk_cpu2_div.aclk,500	[AXXIA_CLK_CPU3_DIV] = &clk_cpu3_div.aclk,501	[AXXIA_CLK_PER_DIV]  = &clk_per_div.aclk,502	[AXXIA_CLK_MMC_DIV]  = &clk_mmc_div.aclk,503	[AXXIA_CLK_FAB]      = &clk_fab_mux.aclk,504	[AXXIA_CLK_SYS]      = &clk_sys_mux.aclk,505	[AXXIA_CLK_NRCP]     = &clk_nrcp_mux.aclk,506	[AXXIA_CLK_CPU0]     = &clk_cpu0_mux.aclk,507	[AXXIA_CLK_CPU1]     = &clk_cpu1_mux.aclk,508	[AXXIA_CLK_CPU2]     = &clk_cpu2_mux.aclk,509	[AXXIA_CLK_CPU3]     = &clk_cpu3_mux.aclk,510	[AXXIA_CLK_PER]      = &clk_per_mux.aclk,511	[AXXIA_CLK_MMC]      = &clk_mmc_mux.aclk,512};513 514static struct clk_hw *515of_clk_axmclk_get(struct of_phandle_args *clkspec, void *unused)516{517	unsigned int idx = clkspec->args[0];518 519	if (idx >= ARRAY_SIZE(axmclk_clocks)) {520		pr_err("%s: invalid index %u\n", __func__, idx);521		return ERR_PTR(-EINVAL);522	}523 524	return &axmclk_clocks[idx]->hw;525}526 527static const struct regmap_config axmclk_regmap_config = {528	.reg_bits	= 32,529	.reg_stride	= 4,530	.val_bits	= 32,531	.max_register	= 0x1fffc,532	.fast_io	= true,533};534 535static const struct of_device_id axmclk_match_table[] = {536	{ .compatible = "lsi,axm5516-clks" },537	{ }538};539MODULE_DEVICE_TABLE(of, axmclk_match_table);540 541static int axmclk_probe(struct platform_device *pdev)542{543	void __iomem *base;544	int i, ret;545	struct device *dev = &pdev->dev;546	struct regmap *regmap;547	size_t num_clks;548 549	base = devm_platform_ioremap_resource(pdev, 0);550	if (IS_ERR(base))551		return PTR_ERR(base);552 553	regmap = devm_regmap_init_mmio(dev, base, &axmclk_regmap_config);554	if (IS_ERR(regmap))555		return PTR_ERR(regmap);556 557	num_clks = ARRAY_SIZE(axmclk_clocks);558	pr_info("axmclk: supporting %zu clocks\n", num_clks);559 560	/* Update each entry with the allocated regmap and register the clock561	 * with the common clock framework562	 */563	for (i = 0; i < num_clks; i++) {564		axmclk_clocks[i]->regmap = regmap;565		ret = devm_clk_hw_register(dev, &axmclk_clocks[i]->hw);566		if (ret)567			return ret;568	}569 570	return devm_of_clk_add_hw_provider(dev, of_clk_axmclk_get, NULL);571}572 573static struct platform_driver axmclk_driver = {574	.probe		= axmclk_probe,575	.driver		= {576		.name	= "clk-axm5516",577		.of_match_table = axmclk_match_table,578	},579};580 581static int __init axmclk_init(void)582{583	return platform_driver_register(&axmclk_driver);584}585core_initcall(axmclk_init);586 587static void __exit axmclk_exit(void)588{589	platform_driver_unregister(&axmclk_driver);590}591module_exit(axmclk_exit);592 593MODULE_DESCRIPTION("AXM5516 clock driver");594MODULE_LICENSE("GPL v2");595MODULE_ALIAS("platform:clk-axm5516");596