11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
225824d52SJiancheng Xue /*
325824d52SJiancheng Xue * Hisilicon Reset Controller Driver
425824d52SJiancheng Xue *
525824d52SJiancheng Xue * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd.
625824d52SJiancheng Xue */
725824d52SJiancheng Xue
825824d52SJiancheng Xue #include <linux/io.h>
925824d52SJiancheng Xue #include <linux/of_address.h>
1097b7129cSJiancheng Xue #include <linux/platform_device.h>
1125824d52SJiancheng Xue #include <linux/reset-controller.h>
1225824d52SJiancheng Xue #include <linux/slab.h>
1325824d52SJiancheng Xue #include <linux/spinlock.h>
1425824d52SJiancheng Xue #include "reset.h"
1525824d52SJiancheng Xue
1625824d52SJiancheng Xue #define HISI_RESET_BIT_MASK 0x1f
1725824d52SJiancheng Xue #define HISI_RESET_OFFSET_SHIFT 8
1825824d52SJiancheng Xue #define HISI_RESET_OFFSET_MASK 0xffff00
1925824d52SJiancheng Xue
2025824d52SJiancheng Xue struct hisi_reset_controller {
2125824d52SJiancheng Xue spinlock_t lock;
2225824d52SJiancheng Xue void __iomem *membase;
2325824d52SJiancheng Xue struct reset_controller_dev rcdev;
2425824d52SJiancheng Xue };
2525824d52SJiancheng Xue
2625824d52SJiancheng Xue
2725824d52SJiancheng Xue #define to_hisi_reset_controller(rcdev) \
2825824d52SJiancheng Xue container_of(rcdev, struct hisi_reset_controller, rcdev)
2925824d52SJiancheng Xue
hisi_reset_of_xlate(struct reset_controller_dev * rcdev,const struct of_phandle_args * reset_spec)3025824d52SJiancheng Xue static int hisi_reset_of_xlate(struct reset_controller_dev *rcdev,
3125824d52SJiancheng Xue const struct of_phandle_args *reset_spec)
3225824d52SJiancheng Xue {
3325824d52SJiancheng Xue u32 offset;
3425824d52SJiancheng Xue u8 bit;
3525824d52SJiancheng Xue
3625824d52SJiancheng Xue offset = (reset_spec->args[0] << HISI_RESET_OFFSET_SHIFT)
3725824d52SJiancheng Xue & HISI_RESET_OFFSET_MASK;
3825824d52SJiancheng Xue bit = reset_spec->args[1] & HISI_RESET_BIT_MASK;
3925824d52SJiancheng Xue
4025824d52SJiancheng Xue return (offset | bit);
4125824d52SJiancheng Xue }
4225824d52SJiancheng Xue
hisi_reset_assert(struct reset_controller_dev * rcdev,unsigned long id)4325824d52SJiancheng Xue static int hisi_reset_assert(struct reset_controller_dev *rcdev,
4425824d52SJiancheng Xue unsigned long id)
4525824d52SJiancheng Xue {
4625824d52SJiancheng Xue struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev);
4725824d52SJiancheng Xue unsigned long flags;
4825824d52SJiancheng Xue u32 offset, reg;
4925824d52SJiancheng Xue u8 bit;
5025824d52SJiancheng Xue
5125824d52SJiancheng Xue offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT;
5225824d52SJiancheng Xue bit = id & HISI_RESET_BIT_MASK;
5325824d52SJiancheng Xue
5425824d52SJiancheng Xue spin_lock_irqsave(&rstc->lock, flags);
5525824d52SJiancheng Xue
5625824d52SJiancheng Xue reg = readl(rstc->membase + offset);
5725824d52SJiancheng Xue writel(reg | BIT(bit), rstc->membase + offset);
5825824d52SJiancheng Xue
5925824d52SJiancheng Xue spin_unlock_irqrestore(&rstc->lock, flags);
6025824d52SJiancheng Xue
6125824d52SJiancheng Xue return 0;
6225824d52SJiancheng Xue }
6325824d52SJiancheng Xue
hisi_reset_deassert(struct reset_controller_dev * rcdev,unsigned long id)6425824d52SJiancheng Xue static int hisi_reset_deassert(struct reset_controller_dev *rcdev,
6525824d52SJiancheng Xue unsigned long id)
6625824d52SJiancheng Xue {
6725824d52SJiancheng Xue struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev);
6825824d52SJiancheng Xue unsigned long flags;
6925824d52SJiancheng Xue u32 offset, reg;
7025824d52SJiancheng Xue u8 bit;
7125824d52SJiancheng Xue
7225824d52SJiancheng Xue offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT;
7325824d52SJiancheng Xue bit = id & HISI_RESET_BIT_MASK;
7425824d52SJiancheng Xue
7525824d52SJiancheng Xue spin_lock_irqsave(&rstc->lock, flags);
7625824d52SJiancheng Xue
7725824d52SJiancheng Xue reg = readl(rstc->membase + offset);
7825824d52SJiancheng Xue writel(reg & ~BIT(bit), rstc->membase + offset);
7925824d52SJiancheng Xue
8025824d52SJiancheng Xue spin_unlock_irqrestore(&rstc->lock, flags);
8125824d52SJiancheng Xue
8225824d52SJiancheng Xue return 0;
8325824d52SJiancheng Xue }
8425824d52SJiancheng Xue
8525824d52SJiancheng Xue static const struct reset_control_ops hisi_reset_ops = {
8625824d52SJiancheng Xue .assert = hisi_reset_assert,
8725824d52SJiancheng Xue .deassert = hisi_reset_deassert,
8825824d52SJiancheng Xue };
8925824d52SJiancheng Xue
hisi_reset_init(struct platform_device * pdev)9097b7129cSJiancheng Xue struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev)
9125824d52SJiancheng Xue {
9225824d52SJiancheng Xue struct hisi_reset_controller *rstc;
9325824d52SJiancheng Xue
9497b7129cSJiancheng Xue rstc = devm_kmalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL);
9525824d52SJiancheng Xue if (!rstc)
9625824d52SJiancheng Xue return NULL;
9725824d52SJiancheng Xue
98*75cc0a12SYueHaibing rstc->membase = devm_platform_ioremap_resource(pdev, 0);
99e9a2310fSGustavo A. R. Silva if (IS_ERR(rstc->membase))
10025824d52SJiancheng Xue return NULL;
10125824d52SJiancheng Xue
10225824d52SJiancheng Xue spin_lock_init(&rstc->lock);
10325824d52SJiancheng Xue rstc->rcdev.owner = THIS_MODULE;
10425824d52SJiancheng Xue rstc->rcdev.ops = &hisi_reset_ops;
10597b7129cSJiancheng Xue rstc->rcdev.of_node = pdev->dev.of_node;
10625824d52SJiancheng Xue rstc->rcdev.of_reset_n_cells = 2;
10725824d52SJiancheng Xue rstc->rcdev.of_xlate = hisi_reset_of_xlate;
10825824d52SJiancheng Xue reset_controller_register(&rstc->rcdev);
10925824d52SJiancheng Xue
11025824d52SJiancheng Xue return rstc;
11125824d52SJiancheng Xue }
11225824d52SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_reset_init);
11325824d52SJiancheng Xue
hisi_reset_exit(struct hisi_reset_controller * rstc)11425824d52SJiancheng Xue void hisi_reset_exit(struct hisi_reset_controller *rstc)
11525824d52SJiancheng Xue {
11625824d52SJiancheng Xue reset_controller_unregister(&rstc->rcdev);
11725824d52SJiancheng Xue }
11825824d52SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_reset_exit);
119