129 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright 2024 NXP4 */5 6#include <linux/auxiliary_bus.h>7#include <linux/device.h>8#include <linux/io.h>9#include <linux/module.h>10#include <linux/of.h>11#include <linux/of_address.h>12#include <linux/reset-controller.h>13 14#define EARC 0x20015#define EARC_RESET_MASK 0x316 17struct imx8mp_audiomix_reset {18 struct reset_controller_dev rcdev;19 spinlock_t lock; /* protect register read-modify-write cycle */20 void __iomem *base;21};22 23static struct imx8mp_audiomix_reset *to_imx8mp_audiomix_reset(struct reset_controller_dev *rcdev)24{25 return container_of(rcdev, struct imx8mp_audiomix_reset, rcdev);26}27 28static int imx8mp_audiomix_reset_assert(struct reset_controller_dev *rcdev,29 unsigned long id)30{31 struct imx8mp_audiomix_reset *priv = to_imx8mp_audiomix_reset(rcdev);32 void __iomem *reg_addr = priv->base;33 unsigned int mask, reg;34 unsigned long flags;35 36 mask = BIT(id);37 spin_lock_irqsave(&priv->lock, flags);38 reg = readl(reg_addr + EARC);39 writel(reg & ~mask, reg_addr + EARC);40 spin_unlock_irqrestore(&priv->lock, flags);41 42 return 0;43}44 45static int imx8mp_audiomix_reset_deassert(struct reset_controller_dev *rcdev,46 unsigned long id)47{48 struct imx8mp_audiomix_reset *priv = to_imx8mp_audiomix_reset(rcdev);49 void __iomem *reg_addr = priv->base;50 unsigned int mask, reg;51 unsigned long flags;52 53 mask = BIT(id);54 spin_lock_irqsave(&priv->lock, flags);55 reg = readl(reg_addr + EARC);56 writel(reg | mask, reg_addr + EARC);57 spin_unlock_irqrestore(&priv->lock, flags);58 59 return 0;60}61 62static const struct reset_control_ops imx8mp_audiomix_reset_ops = {63 .assert = imx8mp_audiomix_reset_assert,64 .deassert = imx8mp_audiomix_reset_deassert,65};66 67static int imx8mp_audiomix_reset_probe(struct auxiliary_device *adev,68 const struct auxiliary_device_id *id)69{70 struct imx8mp_audiomix_reset *priv;71 struct device *dev = &adev->dev;72 int ret;73 74 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);75 if (!priv)76 return -ENOMEM;77 78 spin_lock_init(&priv->lock);79 80 priv->rcdev.owner = THIS_MODULE;81 priv->rcdev.nr_resets = fls(EARC_RESET_MASK);82 priv->rcdev.ops = &imx8mp_audiomix_reset_ops;83 priv->rcdev.of_node = dev->parent->of_node;84 priv->rcdev.dev = dev;85 priv->rcdev.of_reset_n_cells = 1;86 priv->base = of_iomap(dev->parent->of_node, 0);87 if (!priv->base)88 return -ENOMEM;89 90 dev_set_drvdata(dev, priv);91 92 ret = devm_reset_controller_register(dev, &priv->rcdev);93 if (ret)94 goto out_unmap;95 96 return 0;97 98out_unmap:99 iounmap(priv->base);100 return ret;101}102 103static void imx8mp_audiomix_reset_remove(struct auxiliary_device *adev)104{105 struct imx8mp_audiomix_reset *priv = dev_get_drvdata(&adev->dev);106 107 iounmap(priv->base);108}109 110static const struct auxiliary_device_id imx8mp_audiomix_reset_ids[] = {111 {112 .name = "clk_imx8mp_audiomix.reset",113 },114 { }115};116MODULE_DEVICE_TABLE(auxiliary, imx8mp_audiomix_reset_ids);117 118static struct auxiliary_driver imx8mp_audiomix_reset_driver = {119 .probe = imx8mp_audiomix_reset_probe,120 .remove = imx8mp_audiomix_reset_remove,121 .id_table = imx8mp_audiomix_reset_ids,122};123 124module_auxiliary_driver(imx8mp_audiomix_reset_driver);125 126MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");127MODULE_DESCRIPTION("Freescale i.MX8MP Audio Block Controller reset driver");128MODULE_LICENSE("GPL");129