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
23 struct imx8mp_reset_map {
24 unsigned int offset;
25 unsigned int mask;
26 bool active_low;
27 };
28
29 static 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
47 struct 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
to_imx8mp_audiomix_reset(struct reset_controller_dev * rcdev)53 static 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
imx8mp_audiomix_update(struct reset_controller_dev * rcdev,unsigned long id,bool assert)58 static 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
imx8mp_audiomix_reset_assert(struct reset_controller_dev * rcdev,unsigned long id)84 static 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
imx8mp_audiomix_reset_deassert(struct reset_controller_dev * rcdev,unsigned long id)90 static 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
96 static const struct reset_control_ops imx8mp_audiomix_reset_ops = {
97 .assert = imx8mp_audiomix_reset_assert,
98 .deassert = imx8mp_audiomix_reset_deassert,
99 };
100
imx8mp_audiomix_reset_probe(struct auxiliary_device * adev,const struct auxiliary_device_id * id)101 static 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
132 out_unmap:
133 iounmap(priv->base);
134 return ret;
135 }
136
imx8mp_audiomix_reset_remove(struct auxiliary_device * adev)137 static 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
144 static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = {
145 {
146 .name = "clk_imx8mp_audiomix.reset",
147 },
148 { }
149 };
150 MODULE_DEVICE_TABLE(auxiliary, imx8mp_audiomix_reset_ids);
151
152 static 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
158 module_auxiliary_driver(imx8mp_audiomix_reset_driver);
159
160 MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
161 MODULE_DESCRIPTION("Freescale i.MX8MP Audio Block Controller reset driver");
162 MODULE_LICENSE("GPL");
163