brintos

brintos / linux-shallow public Read only

0
0
Text · 11.5 KiB · 19a62da Raw
469 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright 2024 NXP4 */5 6#include <dt-bindings/clock/nxp,imx95-clock.h>7#include <linux/clk.h>8#include <linux/clk-provider.h>9#include <linux/pm_runtime.h>10#include <linux/debugfs.h>11#include <linux/device.h>12#include <linux/err.h>13#include <linux/io.h>14#include <linux/module.h>15#include <linux/of_address.h>16#include <linux/of_device.h>17#include <linux/of_platform.h>18#include <linux/platform_device.h>19#include <linux/regmap.h>20#include <linux/slab.h>21#include <linux/spinlock.h>22#include <linux/types.h>23 24enum {25	CLK_GATE,26	CLK_DIVIDER,27	CLK_MUX,28};29 30struct imx95_blk_ctl {31	struct device *dev;32	spinlock_t lock;33	struct clk *clk_apb;34 35	void __iomem *base;36	/* clock gate register */37	u32 clk_reg_restore;38};39 40struct imx95_blk_ctl_clk_dev_data {41	const char *name;42	const char * const *parent_names;43	u32 num_parents;44	u32 reg;45	u32 bit_idx;46	u32 bit_width;47	u32 clk_type;48	u32 flags;49	u32 flags2;50	u32 type;51};52 53struct imx95_blk_ctl_dev_data {54	const struct imx95_blk_ctl_clk_dev_data *clk_dev_data;55	u32 num_clks;56	bool rpm_enabled;57	u32 clk_reg_offset;58};59 60static const struct imx95_blk_ctl_clk_dev_data vpublk_clk_dev_data[] = {61	[IMX95_CLK_VPUBLK_WAVE] = {62		.name = "vpublk_wave_vpu",63		.parent_names = (const char *[]){ "vpu", },64		.num_parents = 1,65		.reg = 8,66		.bit_idx = 0,67		.type = CLK_GATE,68		.flags = CLK_SET_RATE_PARENT,69		.flags2 = CLK_GATE_SET_TO_DISABLE,70	},71	[IMX95_CLK_VPUBLK_JPEG_ENC] = {72		.name = "vpublk_jpeg_enc",73		.parent_names = (const char *[]){ "vpujpeg", },74		.num_parents = 1,75		.reg = 8,76		.bit_idx = 1,77		.type = CLK_GATE,78		.flags = CLK_SET_RATE_PARENT,79		.flags2 = CLK_GATE_SET_TO_DISABLE,80	},81	[IMX95_CLK_VPUBLK_JPEG_DEC] = {82		.name = "vpublk_jpeg_dec",83		.parent_names = (const char *[]){ "vpujpeg", },84		.num_parents = 1,85		.reg = 8,86		.bit_idx = 2,87		.type = CLK_GATE,88		.flags = CLK_SET_RATE_PARENT,89		.flags2 = CLK_GATE_SET_TO_DISABLE,90	}91};92 93static const struct imx95_blk_ctl_dev_data vpublk_dev_data = {94	.num_clks = ARRAY_SIZE(vpublk_clk_dev_data),95	.clk_dev_data = vpublk_clk_dev_data,96	.rpm_enabled = true,97	.clk_reg_offset = 8,98};99 100static const struct imx95_blk_ctl_clk_dev_data camblk_clk_dev_data[] = {101	[IMX95_CLK_CAMBLK_CSI2_FOR0] = {102		.name = "camblk_csi2_for0",103		.parent_names = (const char *[]){ "camisi", },104		.num_parents = 1,105		.reg = 0,106		.bit_idx = 0,107		.type = CLK_GATE,108		.flags = CLK_SET_RATE_PARENT,109		.flags2 = CLK_GATE_SET_TO_DISABLE,110	},111	[IMX95_CLK_CAMBLK_CSI2_FOR1] = {112		.name = "camblk_csi2_for1",113		.parent_names = (const char *[]){ "camisi", },114		.num_parents = 1,115		.reg = 0,116		.bit_idx = 1,117		.type = CLK_GATE,118		.flags = CLK_SET_RATE_PARENT,119		.flags2 = CLK_GATE_SET_TO_DISABLE,120	},121	[IMX95_CLK_CAMBLK_ISP_AXI] = {122		.name = "camblk_isp_axi",123		.parent_names = (const char *[]){ "camaxi", },124		.num_parents = 1,125		.reg = 0,126		.bit_idx = 4,127		.type = CLK_GATE,128		.flags = CLK_SET_RATE_PARENT,129		.flags2 = CLK_GATE_SET_TO_DISABLE,130	},131	[IMX95_CLK_CAMBLK_ISP_PIXEL] = {132		.name = "camblk_isp_pixel",133		.parent_names = (const char *[]){ "camisi", },134		.num_parents = 1,135		.reg = 0,136		.bit_idx = 5,137		.type = CLK_GATE,138		.flags = CLK_SET_RATE_PARENT,139		.flags2 = CLK_GATE_SET_TO_DISABLE,140	},141	[IMX95_CLK_CAMBLK_ISP] = {142		.name = "camblk_isp",143		.parent_names = (const char *[]){ "camisi", },144		.num_parents = 1,145		.reg = 0,146		.bit_idx = 6,147		.type = CLK_GATE,148		.flags = CLK_SET_RATE_PARENT,149		.flags2 = CLK_GATE_SET_TO_DISABLE,150	}151};152 153static const struct imx95_blk_ctl_dev_data camblk_dev_data = {154	.num_clks = ARRAY_SIZE(camblk_clk_dev_data),155	.clk_dev_data = camblk_clk_dev_data,156	.clk_reg_offset = 0,157};158 159static const struct imx95_blk_ctl_clk_dev_data lvds_clk_dev_data[] = {160	[IMX95_CLK_DISPMIX_LVDS_PHY_DIV] = {161		.name = "ldb_phy_div",162		.parent_names = (const char *[]){ "ldbpll", },163		.num_parents = 1,164		.reg = 0,165		.bit_idx = 0,166		.bit_width = 1,167		.type = CLK_DIVIDER,168		.flags2 = CLK_DIVIDER_POWER_OF_TWO,169	},170	[IMX95_CLK_DISPMIX_LVDS_CH0_GATE] = {171		.name = "lvds_ch0_gate",172		.parent_names = (const char *[]){ "ldb_phy_div", },173		.num_parents = 1,174		.reg = 0,175		.bit_idx = 1,176		.bit_width = 1,177		.type = CLK_GATE,178		.flags = CLK_SET_RATE_PARENT,179		.flags2 = CLK_GATE_SET_TO_DISABLE,180	},181	[IMX95_CLK_DISPMIX_LVDS_CH1_GATE] = {182		.name = "lvds_ch1_gate",183		.parent_names = (const char *[]){ "ldb_phy_div", },184		.num_parents = 1,185		.reg = 0,186		.bit_idx = 2,187		.bit_width = 1,188		.type = CLK_GATE,189		.flags = CLK_SET_RATE_PARENT,190		.flags2 = CLK_GATE_SET_TO_DISABLE,191	},192	[IMX95_CLK_DISPMIX_PIX_DI0_GATE] = {193		.name = "lvds_di0_gate",194		.parent_names = (const char *[]){ "ldb_pll_div7", },195		.num_parents = 1,196		.reg = 0,197		.bit_idx = 3,198		.bit_width = 1,199		.type = CLK_GATE,200		.flags = CLK_SET_RATE_PARENT,201		.flags2 = CLK_GATE_SET_TO_DISABLE,202	},203	[IMX95_CLK_DISPMIX_PIX_DI1_GATE] = {204		.name = "lvds_di1_gate",205		.parent_names = (const char *[]){ "ldb_pll_div7", },206		.num_parents = 1,207		.reg = 0,208		.bit_idx = 4,209		.bit_width = 1,210		.type = CLK_GATE,211		.flags = CLK_SET_RATE_PARENT,212		.flags2 = CLK_GATE_SET_TO_DISABLE,213	},214};215 216static const struct imx95_blk_ctl_dev_data lvds_csr_dev_data = {217	.num_clks = ARRAY_SIZE(lvds_clk_dev_data),218	.clk_dev_data = lvds_clk_dev_data,219	.clk_reg_offset = 0,220};221 222static const struct imx95_blk_ctl_clk_dev_data dispmix_csr_clk_dev_data[] = {223	[IMX95_CLK_DISPMIX_ENG0_SEL] = {224		.name = "disp_engine0_sel",225		.parent_names = (const char *[]){"videopll1", "dsi_pll", "ldb_pll_div7", },226		.num_parents = 4,227		.reg = 0,228		.bit_idx = 0,229		.bit_width = 2,230		.type = CLK_MUX,231		.flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,232	},233	[IMX95_CLK_DISPMIX_ENG1_SEL] = {234		.name = "disp_engine1_sel",235		.parent_names = (const char *[]){"videopll1", "dsi_pll", "ldb_pll_div7", },236		.num_parents = 4,237		.reg = 0,238		.bit_idx = 2,239		.bit_width = 2,240		.type = CLK_MUX,241		.flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,242	}243};244 245static const struct imx95_blk_ctl_dev_data dispmix_csr_dev_data = {246	.num_clks = ARRAY_SIZE(dispmix_csr_clk_dev_data),247	.clk_dev_data = dispmix_csr_clk_dev_data,248	.clk_reg_offset = 0,249};250 251static const struct imx95_blk_ctl_clk_dev_data netxmix_clk_dev_data[] = {252	[IMX95_CLK_NETCMIX_ENETC0_RMII] = {253		.name = "enetc0_rmii_sel",254		.parent_names = (const char *[]){"ext_enetref", "enetref"},255		.num_parents = 2,256		.reg = 4,257		.bit_idx = 5,258		.bit_width = 1,259		.type = CLK_MUX,260		.flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,261	},262	[IMX95_CLK_NETCMIX_ENETC1_RMII] = {263		.name = "enetc1_rmii_sel",264		.parent_names = (const char *[]){"ext_enetref", "enetref"},265		.num_parents = 2,266		.reg = 4,267		.bit_idx = 10,268		.bit_width = 1,269		.type = CLK_MUX,270		.flags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,271	},272};273 274static const struct imx95_blk_ctl_dev_data netcmix_dev_data = {275	.num_clks = ARRAY_SIZE(netxmix_clk_dev_data),276	.clk_dev_data = netxmix_clk_dev_data,277	.clk_reg_offset = 0,278};279 280static int imx95_bc_probe(struct platform_device *pdev)281{282	struct device *dev = &pdev->dev;283	const struct imx95_blk_ctl_dev_data *bc_data;284	struct imx95_blk_ctl *bc;285	struct clk_hw_onecell_data *clk_hw_data;286	struct clk_hw **hws;287	void __iomem *base;288	int i, ret;289 290	bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL);291	if (!bc)292		return -ENOMEM;293	bc->dev = dev;294	dev_set_drvdata(&pdev->dev, bc);295 296	spin_lock_init(&bc->lock);297 298	base = devm_platform_ioremap_resource(pdev, 0);299	if (IS_ERR(base))300		return PTR_ERR(base);301 302	bc->base = base;303	bc->clk_apb = devm_clk_get(dev, NULL);304	if (IS_ERR(bc->clk_apb))305		return dev_err_probe(dev, PTR_ERR(bc->clk_apb), "failed to get APB clock\n");306 307	ret = clk_prepare_enable(bc->clk_apb);308	if (ret) {309		dev_err(dev, "failed to enable apb clock: %d\n", ret);310		return ret;311	}312 313	bc_data = of_device_get_match_data(dev);314	if (!bc_data)315		return devm_of_platform_populate(dev);316 317	clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, bc_data->num_clks),318				   GFP_KERNEL);319	if (!clk_hw_data)320		return -ENOMEM;321 322	if (bc_data->rpm_enabled)323		pm_runtime_enable(&pdev->dev);324 325	clk_hw_data->num = bc_data->num_clks;326	hws = clk_hw_data->hws;327 328	for (i = 0; i < bc_data->num_clks; i++) {329		const struct imx95_blk_ctl_clk_dev_data *data = &bc_data->clk_dev_data[i];330		void __iomem *reg = base + data->reg;331 332		if (data->type == CLK_MUX) {333			hws[i] = clk_hw_register_mux(dev, data->name, data->parent_names,334						     data->num_parents, data->flags, reg,335						     data->bit_idx, data->bit_width,336						     data->flags2, &bc->lock);337		} else if (data->type == CLK_DIVIDER) {338			hws[i] = clk_hw_register_divider(dev, data->name, data->parent_names[0],339							 data->flags, reg, data->bit_idx,340							 data->bit_width, data->flags2, &bc->lock);341		} else {342			hws[i] = clk_hw_register_gate(dev, data->name, data->parent_names[0],343						      data->flags, reg, data->bit_idx,344						      data->flags2, &bc->lock);345		}346		if (IS_ERR(hws[i])) {347			ret = PTR_ERR(hws[i]);348			dev_err(dev, "failed to register: %s:%d\n", data->name, ret);349			goto cleanup;350		}351	}352 353	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, clk_hw_data);354	if (ret)355		goto cleanup;356 357	ret = devm_of_platform_populate(dev);358	if (ret) {359		of_clk_del_provider(dev->of_node);360		goto cleanup;361	}362 363	if (pm_runtime_enabled(bc->dev))364		clk_disable_unprepare(bc->clk_apb);365 366	return 0;367 368cleanup:369	for (i = 0; i < bc_data->num_clks; i++) {370		if (IS_ERR_OR_NULL(hws[i]))371			continue;372		clk_hw_unregister(hws[i]);373	}374 375	if (bc_data->rpm_enabled)376		pm_runtime_disable(&pdev->dev);377 378	return ret;379}380 381#ifdef CONFIG_PM382static int imx95_bc_runtime_suspend(struct device *dev)383{384	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);385 386	clk_disable_unprepare(bc->clk_apb);387	return 0;388}389 390static int imx95_bc_runtime_resume(struct device *dev)391{392	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);393 394	return clk_prepare_enable(bc->clk_apb);395}396#endif397 398#ifdef CONFIG_PM_SLEEP399static int imx95_bc_suspend(struct device *dev)400{401	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);402	const struct imx95_blk_ctl_dev_data *bc_data;403	int ret;404 405	bc_data = of_device_get_match_data(dev);406	if (!bc_data)407		return 0;408 409	if (bc_data->rpm_enabled) {410		ret = pm_runtime_get_sync(bc->dev);411		if (ret < 0) {412			pm_runtime_put_noidle(bc->dev);413			return ret;414		}415	}416 417	bc->clk_reg_restore = readl(bc->base + bc_data->clk_reg_offset);418 419	return 0;420}421 422static int imx95_bc_resume(struct device *dev)423{424	struct imx95_blk_ctl *bc = dev_get_drvdata(dev);425	const struct imx95_blk_ctl_dev_data *bc_data;426 427	bc_data = of_device_get_match_data(dev);428	if (!bc_data)429		return 0;430 431	writel(bc->clk_reg_restore, bc->base + bc_data->clk_reg_offset);432 433	if (bc_data->rpm_enabled)434		pm_runtime_put(bc->dev);435 436	return 0;437}438#endif439 440static const struct dev_pm_ops imx95_bc_pm_ops = {441	SET_RUNTIME_PM_OPS(imx95_bc_runtime_suspend, imx95_bc_runtime_resume, NULL)442	SET_SYSTEM_SLEEP_PM_OPS(imx95_bc_suspend, imx95_bc_resume)443};444 445static const struct of_device_id imx95_bc_of_match[] = {446	{ .compatible = "nxp,imx95-camera-csr", .data = &camblk_dev_data },447	{ .compatible = "nxp,imx95-display-master-csr", },448	{ .compatible = "nxp,imx95-lvds-csr", .data = &lvds_csr_dev_data },449	{ .compatible = "nxp,imx95-display-csr", .data = &dispmix_csr_dev_data },450	{ .compatible = "nxp,imx95-vpu-csr", .data = &vpublk_dev_data },451	{ .compatible = "nxp,imx95-netcmix-blk-ctrl", .data = &netcmix_dev_data},452	{ /* Sentinel */ },453};454MODULE_DEVICE_TABLE(of, imx95_bc_of_match);455 456static struct platform_driver imx95_bc_driver = {457	.probe = imx95_bc_probe,458	.driver = {459		.name = "imx95-blk-ctl",460		.of_match_table = imx95_bc_of_match,461		.pm = &imx95_bc_pm_ops,462	},463};464module_platform_driver(imx95_bc_driver);465 466MODULE_DESCRIPTION("NXP i.MX95 blk ctl driver");467MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");468MODULE_LICENSE("GPL");469