|  | // SPDX-License-Identifier: GPL-2.0-or-later | 
|  | /* | 
|  | * Copyright (c) 2014 MundoReader S.L. | 
|  | * Author: Heiko Stuebner <heiko@sntech.de> | 
|  | */ | 
|  |  | 
|  | #include <linux/slab.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/reset-controller.h> | 
|  | #include <linux/spinlock.h> | 
|  | #include "clk.h" | 
|  |  | 
|  | struct rockchip_softrst { | 
|  | struct reset_controller_dev	rcdev; | 
|  | void __iomem			*reg_base; | 
|  | int				num_regs; | 
|  | int				num_per_reg; | 
|  | u8				flags; | 
|  | spinlock_t			lock; | 
|  | }; | 
|  |  | 
|  | static int rockchip_softrst_assert(struct reset_controller_dev *rcdev, | 
|  | unsigned long id) | 
|  | { | 
|  | struct rockchip_softrst *softrst = container_of(rcdev, | 
|  | struct rockchip_softrst, | 
|  | rcdev); | 
|  | int bank = id / softrst->num_per_reg; | 
|  | int offset = id % softrst->num_per_reg; | 
|  |  | 
|  | if (softrst->flags & ROCKCHIP_SOFTRST_HIWORD_MASK) { | 
|  | writel(BIT(offset) | (BIT(offset) << 16), | 
|  | softrst->reg_base + (bank * 4)); | 
|  | } else { | 
|  | unsigned long flags; | 
|  | u32 reg; | 
|  |  | 
|  | spin_lock_irqsave(&softrst->lock, flags); | 
|  |  | 
|  | reg = readl(softrst->reg_base + (bank * 4)); | 
|  | writel(reg | BIT(offset), softrst->reg_base + (bank * 4)); | 
|  |  | 
|  | spin_unlock_irqrestore(&softrst->lock, flags); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int rockchip_softrst_deassert(struct reset_controller_dev *rcdev, | 
|  | unsigned long id) | 
|  | { | 
|  | struct rockchip_softrst *softrst = container_of(rcdev, | 
|  | struct rockchip_softrst, | 
|  | rcdev); | 
|  | int bank = id / softrst->num_per_reg; | 
|  | int offset = id % softrst->num_per_reg; | 
|  |  | 
|  | if (softrst->flags & ROCKCHIP_SOFTRST_HIWORD_MASK) { | 
|  | writel((BIT(offset) << 16), softrst->reg_base + (bank * 4)); | 
|  | } else { | 
|  | unsigned long flags; | 
|  | u32 reg; | 
|  |  | 
|  | spin_lock_irqsave(&softrst->lock, flags); | 
|  |  | 
|  | reg = readl(softrst->reg_base + (bank * 4)); | 
|  | writel(reg & ~BIT(offset), softrst->reg_base + (bank * 4)); | 
|  |  | 
|  | spin_unlock_irqrestore(&softrst->lock, flags); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct reset_control_ops rockchip_softrst_ops = { | 
|  | .assert		= rockchip_softrst_assert, | 
|  | .deassert	= rockchip_softrst_deassert, | 
|  | }; | 
|  |  | 
|  | void rockchip_register_softrst(struct device_node *np, | 
|  | unsigned int num_regs, | 
|  | void __iomem *base, u8 flags) | 
|  | { | 
|  | struct rockchip_softrst *softrst; | 
|  | int ret; | 
|  |  | 
|  | softrst = kzalloc(sizeof(*softrst), GFP_KERNEL); | 
|  | if (!softrst) | 
|  | return; | 
|  |  | 
|  | spin_lock_init(&softrst->lock); | 
|  |  | 
|  | softrst->reg_base = base; | 
|  | softrst->flags = flags; | 
|  | softrst->num_regs = num_regs; | 
|  | softrst->num_per_reg = (flags & ROCKCHIP_SOFTRST_HIWORD_MASK) ? 16 | 
|  | : 32; | 
|  |  | 
|  | softrst->rcdev.owner = THIS_MODULE; | 
|  | softrst->rcdev.nr_resets =  num_regs * softrst->num_per_reg; | 
|  | softrst->rcdev.ops = &rockchip_softrst_ops; | 
|  | softrst->rcdev.of_node = np; | 
|  | ret = reset_controller_register(&softrst->rcdev); | 
|  | if (ret) { | 
|  | pr_err("%s: could not register reset controller, %d\n", | 
|  | __func__, ret); | 
|  | kfree(softrst); | 
|  | } | 
|  | }; | 
|  | EXPORT_SYMBOL_GPL(rockchip_register_softrst); |