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 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 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 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 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 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 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