brintos

brintos / linux-shallow public Read only

0
0
Text · 11.9 KiB · 820bb1e Raw
374 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Author: Yinbo Zhu <zhuyinbo@loongson.cn>4 * Copyright (C) 2022-2023 Loongson Technology Corporation Limited5 */6 7#include <linux/err.h>8#include <linux/init.h>9#include <linux/clk-provider.h>10#include <linux/slab.h>11#include <linux/module.h>12#include <linux/platform_device.h>13#include <linux/io-64-nonatomic-lo-hi.h>14#include <dt-bindings/clock/loongson,ls2k-clk.h>15 16static const struct clk_parent_data pdata[] = {17	{ .fw_name = "ref_100m", },18};19 20enum loongson2_clk_type {21	CLK_TYPE_PLL,22	CLK_TYPE_SCALE,23	CLK_TYPE_DIVIDER,24	CLK_TYPE_GATE,25	CLK_TYPE_FIXED,26	CLK_TYPE_NONE,27};28 29struct loongson2_clk_provider {30	void __iomem *base;31	struct device *dev;32	struct clk_hw_onecell_data clk_data;33	spinlock_t clk_lock;	/* protect access to DIV registers */34};35 36struct loongson2_clk_data {37	struct clk_hw hw;38	void __iomem *reg;39	u8 div_shift;40	u8 div_width;41	u8 mult_shift;42	u8 mult_width;43};44 45struct loongson2_clk_board_info {46	u8 id;47	enum loongson2_clk_type type;48	const char *name;49	const char *parent_name;50	unsigned long fixed_rate;51	u8 reg_offset;52	u8 div_shift;53	u8 div_width;54	u8 mult_shift;55	u8 mult_width;56	u8 bit_idx;57};58 59#define CLK_DIV(_id, _name, _pname, _offset, _dshift, _dwidth)	\60	{							\61		.id		= _id,				\62		.type		= CLK_TYPE_DIVIDER,		\63		.name		= _name,			\64		.parent_name	= _pname,			\65		.reg_offset	= _offset,			\66		.div_shift	= _dshift,			\67		.div_width	= _dwidth,			\68	}69 70#define CLK_PLL(_id, _name, _offset, _mshift, _mwidth,		\71		_dshift, _dwidth)				\72	{							\73		.id		= _id,				\74		.type		= CLK_TYPE_PLL,			\75		.name		= _name,			\76		.parent_name	= NULL,				\77		.reg_offset	= _offset,			\78		.mult_shift	= _mshift,			\79		.mult_width	= _mwidth,			\80		.div_shift	= _dshift,			\81		.div_width	= _dwidth,			\82	}83 84#define CLK_SCALE(_id, _name, _pname, _offset,			\85		  _dshift, _dwidth)				\86	{							\87		.id		= _id,				\88		.type		= CLK_TYPE_SCALE,		\89		.name		= _name,			\90		.parent_name	= _pname,			\91		.reg_offset	= _offset,			\92		.div_shift	= _dshift,			\93		.div_width	= _dwidth,			\94	}95 96#define CLK_GATE(_id, _name, _pname, _offset, _bidx)		\97	{							\98		.id		= _id,				\99		.type		= CLK_TYPE_GATE,		\100		.name		= _name,			\101		.parent_name	= _pname,			\102		.reg_offset	= _offset,			\103		.bit_idx	= _bidx,			\104	}105 106#define CLK_FIXED(_id, _name, _pname, _rate)			\107	{							\108		.id		= _id,				\109		.type		= CLK_TYPE_FIXED,		\110		.name		= _name,			\111		.parent_name	= _pname,			\112		.fixed_rate	= _rate,			\113	}114 115static const struct loongson2_clk_board_info ls2k0500_clks[] = {116	CLK_PLL(LOONGSON2_NODE_PLL,   "pll_node", 0,    16, 8, 8, 6),117	CLK_PLL(LOONGSON2_DDR_PLL,    "pll_ddr",  0x8,  16, 8, 8, 6),118	CLK_PLL(LOONGSON2_DC_PLL,     "pll_soc",  0x10, 16, 8, 8, 6),119	CLK_PLL(LOONGSON2_PIX0_PLL,   "pll_pix0", 0x18, 16, 8, 8, 6),120	CLK_PLL(LOONGSON2_PIX1_PLL,   "pll_pix1", 0x20, 16, 8, 8, 6),121	CLK_DIV(LOONGSON2_NODE_CLK,   "clk_node", "pll_node", 0,    24, 6),122	CLK_DIV(LOONGSON2_DDR_CLK,    "clk_ddr",  "pll_ddr",  0x8,  24, 6),123	CLK_DIV(LOONGSON2_HDA_CLK,    "clk_hda",  "pll_ddr",  0xc,  8,  6),124	CLK_DIV(LOONGSON2_GPU_CLK,    "clk_gpu",  "pll_soc",  0x10, 24, 6),125	CLK_DIV(LOONGSON2_DC_CLK,     "clk_sb",   "pll_soc",  0x14, 0,  6),126	CLK_DIV(LOONGSON2_GMAC_CLK,   "clk_gmac", "pll_soc",  0x14, 8,  6),127	CLK_DIV(LOONGSON2_PIX0_CLK,   "clk_pix0", "pll_pix0", 0x18, 24, 6),128	CLK_DIV(LOONGSON2_PIX1_CLK,   "clk_pix1", "pll_pix1", 0x20, 24, 6),129	CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", "clk_sb",   0x28, 8,  3),130	CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_sb",   0x28, 12, 3),131	CLK_SCALE(LOONGSON2_USB_CLK,  "clk_usb",  "clk_sb",   0x28, 16, 3),132	CLK_SCALE(LOONGSON2_APB_CLK,  "clk_apb",  "clk_sb",   0x28, 20, 3),133	{ /* Sentinel */ },134};135 136static const struct loongson2_clk_board_info ls2k1000_clks[] = {137	CLK_PLL(LOONGSON2_NODE_PLL,   "pll_node", 0,    32, 10, 26, 6),138	CLK_PLL(LOONGSON2_DDR_PLL,    "pll_ddr",  0x10, 32, 10, 26, 6),139	CLK_PLL(LOONGSON2_DC_PLL,     "pll_dc",   0x20, 32, 10, 26, 6),140	CLK_PLL(LOONGSON2_PIX0_PLL,   "pll_pix0", 0x30, 32, 10, 26, 6),141	CLK_PLL(LOONGSON2_PIX1_PLL,   "pll_pix1", 0x40, 32, 10, 26, 6),142	CLK_DIV(LOONGSON2_NODE_CLK,   "clk_node", "pll_node", 0x8,  0,  6),143	CLK_DIV(LOONGSON2_DDR_CLK,    "clk_ddr",  "pll_ddr",  0x18, 0,  6),144	CLK_DIV(LOONGSON2_GPU_CLK,    "clk_gpu",  "pll_ddr",  0x18, 22, 6),145	/*146	 * The hda clk divisor in the upper 32bits and the clk-prodiver147	 * layer code doesn't support 64bit io operation thus a conversion148	 * is required that subtract shift by 32 and add 4byte to the hda149	 * address150	 */151	CLK_DIV(LOONGSON2_HDA_CLK,    "clk_hda",  "pll_ddr",  0x22, 12, 7),152	CLK_DIV(LOONGSON2_DC_CLK,     "clk_dc",   "pll_dc",   0x28, 0,  6),153	CLK_DIV(LOONGSON2_GMAC_CLK,   "clk_gmac", "pll_dc",   0x28, 22, 6),154	CLK_DIV(LOONGSON2_PIX0_CLK,   "clk_pix0", "pll_pix0", 0x38, 0,  6),155	CLK_DIV(LOONGSON2_PIX1_CLK,   "clk_pix1", "pll_pix1", 0x38, 0,  6),156	CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL,       0x50, 8,  3),157	CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_gmac", 0x50, 12, 3),158	CLK_SCALE(LOONGSON2_USB_CLK,  "clk_usb",  "clk_gmac", 0x50, 16, 3),159	CLK_SCALE(LOONGSON2_APB_CLK,  "clk_apb",  "clk_gmac", 0x50, 20, 3),160	{ /* Sentinel */ },161};162 163static const struct loongson2_clk_board_info ls2k2000_clks[] = {164	CLK_PLL(LOONGSON2_DC_PLL,     "pll_0",    0,    21, 9, 32, 6),165	CLK_PLL(LOONGSON2_DDR_PLL,    "pll_1",    0x10, 21, 9, 32, 6),166	CLK_PLL(LOONGSON2_NODE_PLL,   "pll_2",    0x20, 21, 9, 32, 6),167	CLK_PLL(LOONGSON2_PIX0_PLL,   "pll_pix0", 0x30, 21, 9, 32, 6),168	CLK_PLL(LOONGSON2_PIX1_PLL,   "pll_pix1", 0x40, 21, 9, 32, 6),169	CLK_GATE(LOONGSON2_OUT0_GATE, "out0_gate", "pll_0",    0,    40),170	CLK_GATE(LOONGSON2_GMAC_GATE, "gmac_gate", "pll_0",    0,    41),171	CLK_GATE(LOONGSON2_RIO_GATE,  "rio_gate",  "pll_0",    0,    42),172	CLK_GATE(LOONGSON2_DC_GATE,   "dc_gate",   "pll_1",    0x10, 40),173	CLK_GATE(LOONGSON2_DDR_GATE,  "ddr_gate",  "pll_1",    0x10, 41),174	CLK_GATE(LOONGSON2_GPU_GATE,  "gpu_gate",  "pll_1",    0x10, 42),175	CLK_GATE(LOONGSON2_HDA_GATE,  "hda_gate",  "pll_2",    0x20, 40),176	CLK_GATE(LOONGSON2_NODE_GATE, "node_gate", "pll_2",    0x20, 41),177	CLK_GATE(LOONGSON2_EMMC_GATE, "emmc_gate", "pll_2",    0x20, 42),178	CLK_GATE(LOONGSON2_PIX0_GATE, "pix0_gate", "pll_pix0", 0x30, 40),179	CLK_GATE(LOONGSON2_PIX1_GATE, "pix1_gate", "pll_pix1", 0x40, 40),180	CLK_DIV(LOONGSON2_OUT0_CLK,   "clk_out0", "out0_gate", 0,    0,  6),181	CLK_DIV(LOONGSON2_GMAC_CLK,   "clk_gmac", "gmac_gate", 0,    7,  6),182	CLK_DIV(LOONGSON2_RIO_CLK,    "clk_rio",  "rio_gate",  0,    14, 6),183	CLK_DIV(LOONGSON2_DC_CLK,     "clk_dc",   "dc_gate",   0x10, 0,  6),184	CLK_DIV(LOONGSON2_GPU_CLK,    "clk_gpu",  "gpu_gate",  0x10, 7,  6),185	CLK_DIV(LOONGSON2_DDR_CLK,    "clk_ddr",  "ddr_gate",  0x10, 14, 6),186	CLK_DIV(LOONGSON2_HDA_CLK,    "clk_hda",  "hda_gate",  0x20, 0,  6),187	CLK_DIV(LOONGSON2_NODE_CLK,   "clk_node", "node_gate", 0x20, 7,  6),188	CLK_DIV(LOONGSON2_EMMC_CLK,   "clk_emmc", "emmc_gate", 0x20, 14, 6),189	CLK_DIV(LOONGSON2_PIX0_CLK,   "clk_pix0", "pll_pix0",  0x30, 0,  6),190	CLK_DIV(LOONGSON2_PIX1_CLK,   "clk_pix1", "pll_pix1",  0x40, 0,  6),191	CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_out0",  0x50, 12, 3),192	CLK_SCALE(LOONGSON2_USB_CLK,  "clk_usb",  "clk_out0",  0x50, 16, 3),193	CLK_SCALE(LOONGSON2_APB_CLK,  "clk_apb",  "clk_node",  0x50, 20, 3),194	CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL,        0x50, 23, 3),195	CLK_SCALE(LOONGSON2_DES_CLK,  "clk_des",  "clk_node",  0x50, 40, 3),196	CLK_SCALE(LOONGSON2_I2S_CLK,  "clk_i2s",  "clk_node",  0x50, 44, 3),197	CLK_FIXED(LOONGSON2_MISC_CLK, "clk_misc", NULL, 50000000),198	{ /* Sentinel */ },199};200 201static inline struct loongson2_clk_data *to_loongson2_clk(struct clk_hw *hw)202{203	return container_of(hw, struct loongson2_clk_data, hw);204}205 206static inline unsigned long loongson2_rate_part(u64 val, u8 shift, u8 width)207{208	return (val & GENMASK(shift + width - 1, shift)) >> shift;209}210 211static unsigned long loongson2_pll_recalc_rate(struct clk_hw *hw,212					       unsigned long parent_rate)213{214	u64 val, mult, div;215	struct loongson2_clk_data *clk = to_loongson2_clk(hw);216 217	val  = readq(clk->reg);218	mult = loongson2_rate_part(val, clk->mult_shift, clk->mult_width);219	div  = loongson2_rate_part(val, clk->div_shift,  clk->div_width);220 221	return div_u64((u64)parent_rate * mult, div);222}223 224static const struct clk_ops loongson2_pll_recalc_ops = {225	.recalc_rate = loongson2_pll_recalc_rate,226};227 228static unsigned long loongson2_freqscale_recalc_rate(struct clk_hw *hw,229						     unsigned long parent_rate)230{231	u64 val, mult;232	struct loongson2_clk_data *clk = to_loongson2_clk(hw);233 234	val  = readq(clk->reg);235	mult = loongson2_rate_part(val, clk->div_shift, clk->div_width) + 1;236 237	return div_u64((u64)parent_rate * mult, 8);238}239 240static const struct clk_ops loongson2_freqscale_recalc_ops = {241	.recalc_rate = loongson2_freqscale_recalc_rate,242};243 244static struct clk_hw *loongson2_clk_register(struct loongson2_clk_provider *clp,245					     const struct loongson2_clk_board_info *cld,246					     const struct clk_ops *ops)247{248	int ret;249	struct clk_hw *hw;250	struct loongson2_clk_data *clk;251	struct clk_init_data init = { };252 253	clk = devm_kzalloc(clp->dev, sizeof(*clk), GFP_KERNEL);254	if (!clk)255		return ERR_PTR(-ENOMEM);256 257	init.name  = cld->name;258	init.ops   = ops;259	init.flags = 0;260	init.num_parents = 1;261 262	if (!cld->parent_name)263		init.parent_data = pdata;264	else265		init.parent_names = &cld->parent_name;266 267	clk->reg	= clp->base + cld->reg_offset;268	clk->div_shift	= cld->div_shift;269	clk->div_width	= cld->div_width;270	clk->mult_shift	= cld->mult_shift;271	clk->mult_width	= cld->mult_width;272	clk->hw.init	= &init;273 274	hw = &clk->hw;275	ret = devm_clk_hw_register(clp->dev, hw);276	if (ret)277		clk = ERR_PTR(ret);278 279	return hw;280}281 282static int loongson2_clk_probe(struct platform_device *pdev)283{284	int i, clks_num = 0;285	struct clk_hw *hw;286	struct device *dev = &pdev->dev;287	struct loongson2_clk_provider *clp;288	const struct loongson2_clk_board_info *p, *data;289 290	data = device_get_match_data(dev);291	if (!data)292		return -EINVAL;293 294	for (p = data; p->name; p++)295		clks_num++;296 297	clp = devm_kzalloc(dev, struct_size(clp, clk_data.hws, clks_num),298			   GFP_KERNEL);299	if (!clp)300		return -ENOMEM;301 302	clp->base = devm_platform_ioremap_resource(pdev, 0);303	if (IS_ERR(clp->base))304		return PTR_ERR(clp->base);305 306	spin_lock_init(&clp->clk_lock);307	clp->clk_data.num = clks_num + 1;308	clp->dev = dev;309 310	for (i = 0; i < clks_num; i++) {311		p = &data[i];312		switch (p->type) {313		case CLK_TYPE_PLL:314			hw = loongson2_clk_register(clp, p,315						    &loongson2_pll_recalc_ops);316			break;317		case CLK_TYPE_SCALE:318			hw = loongson2_clk_register(clp, p,319						    &loongson2_freqscale_recalc_ops);320			break;321		case CLK_TYPE_DIVIDER:322			hw = devm_clk_hw_register_divider(dev, p->name,323							  p->parent_name, 0,324							  clp->base + p->reg_offset,325							  p->div_shift, p->div_width,326							  CLK_DIVIDER_ONE_BASED,327							  &clp->clk_lock);328			break;329		case CLK_TYPE_GATE:330			hw = devm_clk_hw_register_gate(dev, p->name, p->parent_name, 0,331						       clp->base + p->reg_offset,332						       p->bit_idx, 0,333						       &clp->clk_lock);334			break;335		case CLK_TYPE_FIXED:336			hw = clk_hw_register_fixed_rate_parent_data(dev, p->name, pdata,337								    0, p->fixed_rate);338			break;339		default:340			return dev_err_probe(dev, -EINVAL, "Invalid clk type\n");341		}342 343		if (IS_ERR(hw))344			return dev_err_probe(dev, PTR_ERR(hw),345					     "Register clk: %s, type: %u failed!\n",346					     p->name, p->type);347 348		clp->clk_data.hws[p->id] = hw;349	}350 351	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clp->clk_data);352}353 354static const struct of_device_id loongson2_clk_match_table[] = {355	{ .compatible = "loongson,ls2k0500-clk", .data = &ls2k0500_clks },356	{ .compatible = "loongson,ls2k-clk", .data = &ls2k1000_clks },357	{ .compatible = "loongson,ls2k2000-clk", .data = &ls2k2000_clks },358	{ }359};360MODULE_DEVICE_TABLE(of, loongson2_clk_match_table);361 362static struct platform_driver loongson2_clk_driver = {363	.probe	= loongson2_clk_probe,364	.driver	= {365		.name	= "loongson2-clk",366		.of_match_table	= loongson2_clk_match_table,367	},368};369module_platform_driver(loongson2_clk_driver);370 371MODULE_DESCRIPTION("Loongson2 clock driver");372MODULE_AUTHOR("Loongson Technology Corporation Limited");373MODULE_LICENSE("GPL");374