Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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(1)
18#define IMX8MP_AUDIOMIX_EARC_PHY_RESET_MASK BIT(2)
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");