File Index
Files and directories
Sign in
FILE

reset-imx8mp-audiomix.c

File preview with syntax highlighting.

linux-6.19.14/drivers/reset/reset-imx8mp-audiomix.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright 2024 NXP
  4 */
  5
  6#include <dt-bindings/reset/imx8mp-reset-audiomix.h>
  7
  8#include <linux/auxiliary_bus.h>
  9#include <linux/device.h>
 10#include <linux/io.h>
 11#include <linux/module.h>
 12#include <linux/of.h>
 13#include <linux/of_address.h>
 14#include <linux/reset-controller.h>
 15
 16#define IMX8MP_AUDIOMIX_EARC_RESET_OFFSET	0x200
 17#define IMX8MP_AUDIOMIX_EARC_RESET_MASK		BIT(0)
 18#define IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK	BIT(1)
 19
 20#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET	0x108
 21#define IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK	BIT(5)
 22
 23struct imx8mp_reset_map {
 24	unsigned int offset;
 25	unsigned int mask;
 26	bool active_low;
 27};
 28
 29static const struct imx8mp_reset_map reset_map[] = {
 30	[IMX8MP_AUDIOMIX_EARC_RESET] = {
 31		.offset	= IMX8MP_AUDIOMIX_EARC_RESET_OFFSET,
 32		.mask	= IMX8MP_AUDIOMIX_EARC_RESET_MASK,
 33		.active_low = true,
 34	},
 35	[IMX8MP_AUDIOMIX_EARC_PHY_RESET] = {
 36		.offset	= IMX8MP_AUDIOMIX_EARC_RESET_OFFSET,
 37		.mask	= IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK,
 38		.active_low = true,
 39	},
 40	[IMX8MP_AUDIOMIX_DSP_RUNSTALL] = {
 41		.offset	= IMX8MP_AUDIOMIX_DSP_RUNSTALL_OFFSET,
 42		.mask	= IMX8MP_AUDIOMIX_DSP_RUNSTALL_MASK,
 43		.active_low = false,
 44	},
 45};
 46
 47struct imx8mp_audiomix_reset {
 48	struct reset_controller_dev rcdev;
 49	spinlock_t lock; /* protect register read-modify-write cycle */
 50	void __iomem *base;
 51};
 52
 53static struct imx8mp_audiomix_reset *to_imx8mp_audiomix_reset(struct reset_controller_dev *rcdev)
 54{
 55	return container_of(rcdev, struct imx8mp_audiomix_reset, rcdev);
 56}
 57
 58static int imx8mp_audiomix_update(struct reset_controller_dev *rcdev,
 59				  unsigned long id, bool assert)
 60{
 61	struct imx8mp_audiomix_reset *priv = to_imx8mp_audiomix_reset(rcdev);
 62	void __iomem *reg_addr = priv->base;
 63	unsigned int mask, offset, active_low;
 64	unsigned long reg, flags;
 65
 66	mask = reset_map[id].mask;
 67	offset = reset_map[id].offset;
 68	active_low = reset_map[id].active_low;
 69
 70	spin_lock_irqsave(&priv->lock, flags);
 71
 72	reg = readl(reg_addr + offset);
 73	if (active_low ^ assert)
 74		reg |= mask;
 75	else
 76		reg &= ~mask;
 77	writel(reg, reg_addr + offset);
 78
 79	spin_unlock_irqrestore(&priv->lock, flags);
 80
 81	return 0;
 82}
 83
 84static int imx8mp_audiomix_reset_assert(struct reset_controller_dev *rcdev,
 85					unsigned long id)
 86{
 87	return imx8mp_audiomix_update(rcdev, id, true);
 88}
 89
 90static int imx8mp_audiomix_reset_deassert(struct reset_controller_dev *rcdev,
 91					  unsigned long id)
 92{
 93	return imx8mp_audiomix_update(rcdev, id, false);
 94}
 95
 96static const struct reset_control_ops imx8mp_audiomix_reset_ops = {
 97	.assert   = imx8mp_audiomix_reset_assert,
 98	.deassert = imx8mp_audiomix_reset_deassert,
 99};
100
101static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,
102				       const struct auxiliary_device_id *id)
103{
104	struct imx8mp_audiomix_reset *priv;
105	struct device *dev = &adev->dev;
106	int ret;
107
108	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
109	if (!priv)
110		return -ENOMEM;
111
112	spin_lock_init(&priv->lock);
113
114	priv->rcdev.owner     = THIS_MODULE;
115	priv->rcdev.nr_resets = ARRAY_SIZE(reset_map);
116	priv->rcdev.ops       = &imx8mp_audiomix_reset_ops;
117	priv->rcdev.of_node   = dev->parent->of_node;
118	priv->rcdev.dev	      = dev;
119	priv->rcdev.of_reset_n_cells = 1;
120	priv->base            = of_iomap(dev->parent->of_node, 0);
121	if (!priv->base)
122		return -ENOMEM;
123
124	dev_set_drvdata(dev, priv);
125
126	ret = devm_reset_controller_register(dev, &priv->rcdev);
127	if (ret)
128		goto out_unmap;
129
130	return 0;
131
132out_unmap:
133	iounmap(priv->base);
134	return ret;
135}
136
137static void imx8mp_audiomix_reset_remove(struct auxiliary_device *adev)
138{
139	struct imx8mp_audiomix_reset *priv = dev_get_drvdata(&adev->dev);
140
141	iounmap(priv->base);
142}
143
144static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = {
145	{
146		.name = "clk_imx8mp_audiomix.reset",
147	},
148	{ }
149};
150MODULE_DEVICE_TABLE(auxiliary, imx8mp_audiomix_reset_ids);
151
152static struct auxiliary_driver imx8mp_audiomix_reset_driver = {
153	.probe		= imx8mp_audiomix_reset_probe,
154	.remove		= imx8mp_audiomix_reset_remove,
155	.id_table	= imx8mp_audiomix_reset_ids,
156};
157
158module_auxiliary_driver(imx8mp_audiomix_reset_driver);
159
160MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
161MODULE_DESCRIPTION("Freescale i.MX8MP Audio Block Controller reset driver");
162MODULE_LICENSE("GPL");
~/ Read-only file preview.