Unverified Commit c8812c2a authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'imx-drivers-5.18' of...

Merge tag 'imx-drivers-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux into arm/drivers

i.MX drivers update for 5.18:

- Drop LS1021A device check from soc-imx driver as it's unneeded since
  commit commit 4ebd29f9 ("soc: imx: Register SoC device only on
  i.MX boards").
- Add support for power domains provided by the VPU blk-ctrl on the
  i.MX8MQ.
- Add resource owner management API which will be used to check whether
  M4 is under control of Linux.
- Add VPU MU resources support into SCU power domain driver.
- Support DT overlay for WEIM bus driver with OF reconfiguration
  notifier handler.

* tag 'imx-drivers-5.18' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux:
  bus: imx-weim: add DT overlay support for WEIM bus
  firmware: imx: scu-pd: imx8q: add vpu mu resources
  firmware: imx: add get resource owner api
  soc: imx: imx8m-blk-ctrl: add i.MX8MQ VPU blk-ctrl
  dt-bindings: power: imx8mq: add defines for VPU blk-ctrl domains
  soc: imx: Remove Layerscape check

Link: https://lore.kernel.org/r/20220222075226.160187-1-shawnguo@kernel.org


Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 25b67f37 e6cb5408
Loading
Loading
Loading
Loading
+126 −9
Original line number Diff line number Diff line
@@ -64,6 +64,11 @@ struct cs_timing_state {
	struct cs_timing cs[MAX_CS_COUNT];
};

struct weim_priv {
	void __iomem *base;
	struct cs_timing_state timing_state;
};

static const struct of_device_id weim_id_table[] = {
	/* i.MX1/21 */
	{ .compatible = "fsl,imx1-weim", .data = &imx1_weim_devtype, },
@@ -128,21 +133,26 @@ static int imx_weim_gpr_setup(struct platform_device *pdev)
}

/* Parse and set the timing for this device. */
static int weim_timing_setup(struct device *dev,
			     struct device_node *np, void __iomem *base,
			     const struct imx_weim_devtype *devtype,
			     struct cs_timing_state *ts)
static int weim_timing_setup(struct device *dev, struct device_node *np,
			     const struct imx_weim_devtype *devtype)
{
	u32 cs_idx, value[MAX_CS_REGS_COUNT];
	int i, ret;
	int reg_idx, num_regs;
	struct cs_timing *cst;
	struct weim_priv *priv;
	struct cs_timing_state *ts;
	void __iomem *base;

	if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT))
		return -EINVAL;
	if (WARN_ON(devtype->cs_count > MAX_CS_COUNT))
		return -EINVAL;

	priv = dev_get_drvdata(dev);
	base = priv->base;
	ts = &priv->timing_state;

	ret = of_property_read_u32_array(np, "fsl,weim-cs-timing",
					 value, devtype->cs_regs_count);
	if (ret)
@@ -189,14 +199,15 @@ static int weim_timing_setup(struct device *dev,
	return 0;
}

static int weim_parse_dt(struct platform_device *pdev, void __iomem *base)
static int weim_parse_dt(struct platform_device *pdev)
{
	const struct of_device_id *of_id = of_match_device(weim_id_table,
							   &pdev->dev);
	const struct imx_weim_devtype *devtype = of_id->data;
	struct device_node *child;
	int ret, have_child = 0;
	struct cs_timing_state ts = {};
	struct weim_priv *priv;
	void __iomem *base;
	u32 reg;

	if (devtype == &imx50_weim_devtype) {
@@ -205,6 +216,9 @@ static int weim_parse_dt(struct platform_device *pdev, void __iomem *base)
			return ret;
	}

	priv = dev_get_drvdata(&pdev->dev);
	base = priv->base;

	if (of_property_read_bool(pdev->dev.of_node, "fsl,burst-clk-enable")) {
		if (devtype->wcr_bcm) {
			reg = readl(base + devtype->wcr_offset);
@@ -229,7 +243,7 @@ static int weim_parse_dt(struct platform_device *pdev, void __iomem *base)
	}

	for_each_available_child_of_node(pdev->dev.of_node, child) {
		ret = weim_timing_setup(&pdev->dev, child, base, devtype, &ts);
		ret = weim_timing_setup(&pdev->dev, child, devtype);
		if (ret)
			dev_warn(&pdev->dev, "%pOF set timing failed.\n",
				child);
@@ -248,17 +262,25 @@ static int weim_parse_dt(struct platform_device *pdev, void __iomem *base)

static int weim_probe(struct platform_device *pdev)
{
	struct weim_priv *priv;
	struct resource *res;
	struct clk *clk;
	void __iomem *base;
	int ret;

	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	/* get the resource */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(base))
		return PTR_ERR(base);

	priv->base = base;
	dev_set_drvdata(&pdev->dev, priv);

	/* get the clock */
	clk = devm_clk_get(&pdev->dev, NULL);
	if (IS_ERR(clk))
@@ -269,7 +291,7 @@ static int weim_probe(struct platform_device *pdev)
		return ret;

	/* parse the device node */
	ret = weim_parse_dt(pdev, base);
	ret = weim_parse_dt(pdev);
	if (ret)
		clk_disable_unprepare(clk);
	else
@@ -278,6 +300,81 @@ static int weim_probe(struct platform_device *pdev)
	return ret;
}

#if IS_ENABLED(CONFIG_OF_DYNAMIC)
static int of_weim_notify(struct notifier_block *nb, unsigned long action,
			  void *arg)
{
	const struct imx_weim_devtype *devtype;
	struct of_reconfig_data *rd = arg;
	const struct of_device_id *of_id;
	struct platform_device *pdev;
	int ret = NOTIFY_OK;

	switch (of_reconfig_get_state_change(action, rd)) {
	case OF_RECONFIG_CHANGE_ADD:
		of_id = of_match_node(weim_id_table, rd->dn->parent);
		if (!of_id)
			return NOTIFY_OK; /* not for us */

		devtype = of_id->data;

		pdev = of_find_device_by_node(rd->dn->parent);
		if (!pdev) {
			pr_err("%s: could not find platform device for '%pOF'\n",
				__func__, rd->dn->parent);

			return notifier_from_errno(-EINVAL);
		}

		if (weim_timing_setup(&pdev->dev, rd->dn, devtype))
			dev_warn(&pdev->dev,
				 "Failed to setup timing for '%pOF'\n", rd->dn);

		if (!of_node_check_flag(rd->dn, OF_POPULATED)) {
			if (!of_platform_device_create(rd->dn, NULL, &pdev->dev)) {
				dev_err(&pdev->dev,
					"Failed to create child device '%pOF'\n",
					rd->dn);
				ret = notifier_from_errno(-EINVAL);
			}
		}

		platform_device_put(pdev);

		break;
	case OF_RECONFIG_CHANGE_REMOVE:
		if (!of_node_check_flag(rd->dn, OF_POPULATED))
			return NOTIFY_OK; /* device already destroyed */

		of_id = of_match_node(weim_id_table, rd->dn->parent);
		if (!of_id)
			return NOTIFY_OK; /* not for us */

		pdev = of_find_device_by_node(rd->dn);
		if (!pdev) {
			dev_err(&pdev->dev,
				"Could not find platform device for '%pOF'\n",
				rd->dn);

			ret = notifier_from_errno(-EINVAL);
		} else {
			of_platform_device_destroy(&pdev->dev, NULL);
			platform_device_put(pdev);
		}

		break;
	default:
		break;
	}

	return ret;
}

struct notifier_block weim_of_notifier = {
	.notifier_call = of_weim_notify,
};
#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */

static struct platform_driver weim_driver = {
	.driver = {
		.name		= "imx-weim",
@@ -285,7 +382,27 @@ static struct platform_driver weim_driver = {
	},
	.probe = weim_probe,
};
module_platform_driver(weim_driver);

static int __init weim_init(void)
{
#if IS_ENABLED(CONFIG_OF_DYNAMIC)
	WARN_ON(of_reconfig_notifier_register(&weim_of_notifier));
#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */

	return platform_driver_register(&weim_driver);
}
module_init(weim_init);

static void __exit weim_exit(void)
{
#if IS_ENABLED(CONFIG_OF_DYNAMIC)
	of_reconfig_notifier_unregister(&weim_of_notifier);
#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */

	return platform_driver_unregister(&weim_driver);

}
module_exit(weim_exit);

MODULE_AUTHOR("Freescale Semiconductor Inc.");
MODULE_DESCRIPTION("i.MX EIM Controller Driver");
+45 −0
Original line number Diff line number Diff line
@@ -43,3 +43,48 @@ bool imx_sc_rm_is_resource_owned(struct imx_sc_ipc *ipc, u16 resource)
	return hdr->func;
}
EXPORT_SYMBOL(imx_sc_rm_is_resource_owned);

struct imx_sc_msg_rm_get_resource_owner {
	struct imx_sc_rpc_msg hdr;
	union {
		struct {
			u16 resource;
		} req;
		struct {
			u8 val;
		} resp;
	} data;
} __packed __aligned(4);

/*
 * This function get @resource partition number
 *
 * @param[in]     ipc         IPC handle
 * @param[in]     resource    resource the control is associated with
 * @param[out]    pt          pointer to return the partition number
 *
 * @return Returns 0 for success and < 0 for errors.
 */
int imx_sc_rm_get_resource_owner(struct imx_sc_ipc *ipc, u16 resource, u8 *pt)
{
	struct imx_sc_msg_rm_get_resource_owner msg;
	struct imx_sc_rpc_msg *hdr = &msg.hdr;
	int ret;

	hdr->ver = IMX_SC_RPC_VERSION;
	hdr->svc = IMX_SC_RPC_SVC_RM;
	hdr->func = IMX_SC_RM_FUNC_GET_RESOURCE_OWNER;
	hdr->size = 2;

	msg.data.req.resource = resource;

	ret = imx_scu_call_rpc(ipc, &msg, true);
	if (ret)
		return ret;

	if (pt)
		*pt = msg.data.resp.val;

	return 0;
}
EXPORT_SYMBOL(imx_sc_rm_get_resource_owner);
+4 −0
Original line number Diff line number Diff line
@@ -155,6 +155,10 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
	{ "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 },
	{ "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 },
	{ "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 },
	{ "vpu-enc1", IMX_SC_R_VPU_ENC_1, 1, false, 0 },
	{ "vpu-mu0", IMX_SC_R_VPU_MU_0, 1, false, 0 },
	{ "vpu-mu1", IMX_SC_R_VPU_MU_1, 1, false, 0 },
	{ "vpu-mu2", IMX_SC_R_VPU_MU_2, 1, false, 0 },

	/* GPU SS */
	{ "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 },
+66 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@

#include <dt-bindings/power/imx8mm-power.h>
#include <dt-bindings/power/imx8mn-power.h>
#include <dt-bindings/power/imx8mq-power.h>

#define BLK_SFT_RSTN	0x0
#define BLK_CLK_EN	0x4
@@ -589,6 +590,68 @@ static const struct imx8m_blk_ctrl_data imx8mn_disp_blk_ctl_dev_data = {
	.num_domains = ARRAY_SIZE(imx8mn_disp_blk_ctl_domain_data),
};

static int imx8mq_vpu_power_notifier(struct notifier_block *nb,
				     unsigned long action, void *data)
{
	struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl,
						 power_nb);

	if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF)
		return NOTIFY_OK;

	/*
	 * The ADB in the VPUMIX domain has no separate reset and clock
	 * enable bits, but is ungated and reset together with the VPUs. The
	 * reset and clock enable inputs to the ADB is a logical OR of the
	 * VPU bits. In order to set the G2 fuse bits, the G2 clock must
	 * also be enabled.
	 */
	regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1));
	regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1));

	if (action == GENPD_NOTIFY_ON) {
		/*
		 * On power up we have no software backchannel to the GPC to
		 * wait for the ADB handshake to happen, so we just delay for a
		 * bit. On power down the GPC driver waits for the handshake.
		 */
		udelay(5);

		/* set "fuse" bits to enable the VPUs */
		regmap_set_bits(bc->regmap, 0x8, 0xffffffff);
		regmap_set_bits(bc->regmap, 0xc, 0xffffffff);
		regmap_set_bits(bc->regmap, 0x10, 0xffffffff);
	}

	return NOTIFY_OK;
}

static const struct imx8m_blk_ctrl_domain_data imx8mq_vpu_blk_ctl_domain_data[] = {
	[IMX8MQ_VPUBLK_PD_G1] = {
		.name = "vpublk-g1",
		.clk_names = (const char *[]){ "g1", },
		.num_clks = 1,
		.gpc_name = "g1",
		.rst_mask = BIT(1),
		.clk_mask = BIT(1),
	},
	[IMX8MQ_VPUBLK_PD_G2] = {
		.name = "vpublk-g2",
		.clk_names = (const char *[]){ "g2", },
		.num_clks = 1,
		.gpc_name = "g2",
		.rst_mask = BIT(0),
		.clk_mask = BIT(0),
	},
};

static const struct imx8m_blk_ctrl_data imx8mq_vpu_blk_ctl_dev_data = {
	.max_reg = 0x14,
	.power_notifier_fn = imx8mq_vpu_power_notifier,
	.domains = imx8mq_vpu_blk_ctl_domain_data,
	.num_domains = ARRAY_SIZE(imx8mq_vpu_blk_ctl_domain_data),
};

static const struct of_device_id imx8m_blk_ctrl_of_match[] = {
	{
		.compatible = "fsl,imx8mm-vpu-blk-ctrl",
@@ -599,6 +662,9 @@ static const struct of_device_id imx8m_blk_ctrl_of_match[] = {
	}, {
		.compatible = "fsl,imx8mn-disp-blk-ctrl",
		.data = &imx8mn_disp_blk_ctl_dev_data
	}, {
		.compatible = "fsl,imx8mq-vpu-blk-ctrl",
		.data = &imx8mq_vpu_blk_ctl_dev_data
	}, {
		/* Sentinel */
	}
+0 −3
Original line number Diff line number Diff line
@@ -40,9 +40,6 @@ static int __init imx_soc_device_init(void)
	if (!__mxc_cpu_type)
		return 0;

	if (of_machine_is_compatible("fsl,ls1021a"))
		return 0;

	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
	if (!soc_dev_attr)
		return -ENOMEM;
Loading