linux/drivers/clk/hisilicon/reset.c
<<
>>
Prefs
   1/*
   2 * Hisilicon Reset Controller Driver
   3 *
   4 * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include <linux/io.h>
  21#include <linux/of_address.h>
  22#include <linux/platform_device.h>
  23#include <linux/reset-controller.h>
  24#include <linux/slab.h>
  25#include <linux/spinlock.h>
  26#include "reset.h"
  27
  28#define HISI_RESET_BIT_MASK     0x1f
  29#define HISI_RESET_OFFSET_SHIFT 8
  30#define HISI_RESET_OFFSET_MASK  0xffff00
  31
  32struct hisi_reset_controller {
  33        spinlock_t      lock;
  34        void __iomem    *membase;
  35        struct reset_controller_dev     rcdev;
  36};
  37
  38
  39#define to_hisi_reset_controller(rcdev)  \
  40        container_of(rcdev, struct hisi_reset_controller, rcdev)
  41
  42static int hisi_reset_of_xlate(struct reset_controller_dev *rcdev,
  43                        const struct of_phandle_args *reset_spec)
  44{
  45        u32 offset;
  46        u8 bit;
  47
  48        offset = (reset_spec->args[0] << HISI_RESET_OFFSET_SHIFT)
  49                & HISI_RESET_OFFSET_MASK;
  50        bit = reset_spec->args[1] & HISI_RESET_BIT_MASK;
  51
  52        return (offset | bit);
  53}
  54
  55static int hisi_reset_assert(struct reset_controller_dev *rcdev,
  56                              unsigned long id)
  57{
  58        struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev);
  59        unsigned long flags;
  60        u32 offset, reg;
  61        u8 bit;
  62
  63        offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT;
  64        bit = id & HISI_RESET_BIT_MASK;
  65
  66        spin_lock_irqsave(&rstc->lock, flags);
  67
  68        reg = readl(rstc->membase + offset);
  69        writel(reg | BIT(bit), rstc->membase + offset);
  70
  71        spin_unlock_irqrestore(&rstc->lock, flags);
  72
  73        return 0;
  74}
  75
  76static int hisi_reset_deassert(struct reset_controller_dev *rcdev,
  77                                unsigned long id)
  78{
  79        struct hisi_reset_controller *rstc = to_hisi_reset_controller(rcdev);
  80        unsigned long flags;
  81        u32 offset, reg;
  82        u8 bit;
  83
  84        offset = (id & HISI_RESET_OFFSET_MASK) >> HISI_RESET_OFFSET_SHIFT;
  85        bit = id & HISI_RESET_BIT_MASK;
  86
  87        spin_lock_irqsave(&rstc->lock, flags);
  88
  89        reg = readl(rstc->membase + offset);
  90        writel(reg & ~BIT(bit), rstc->membase + offset);
  91
  92        spin_unlock_irqrestore(&rstc->lock, flags);
  93
  94        return 0;
  95}
  96
  97static const struct reset_control_ops hisi_reset_ops = {
  98        .assert         = hisi_reset_assert,
  99        .deassert       = hisi_reset_deassert,
 100};
 101
 102struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev)
 103{
 104        struct hisi_reset_controller *rstc;
 105        struct resource *res;
 106
 107        rstc = devm_kmalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL);
 108        if (!rstc)
 109                return NULL;
 110
 111        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 112        rstc->membase = devm_ioremap(&pdev->dev,
 113                                res->start, resource_size(res));
 114        if (!rstc->membase)
 115                return NULL;
 116
 117        spin_lock_init(&rstc->lock);
 118        rstc->rcdev.owner = THIS_MODULE;
 119        rstc->rcdev.ops = &hisi_reset_ops;
 120        rstc->rcdev.of_node = pdev->dev.of_node;
 121        rstc->rcdev.of_reset_n_cells = 2;
 122        rstc->rcdev.of_xlate = hisi_reset_of_xlate;
 123        reset_controller_register(&rstc->rcdev);
 124
 125        return rstc;
 126}
 127EXPORT_SYMBOL_GPL(hisi_reset_init);
 128
 129void hisi_reset_exit(struct hisi_reset_controller *rstc)
 130{
 131        reset_controller_unregister(&rstc->rcdev);
 132}
 133EXPORT_SYMBOL_GPL(hisi_reset_exit);
 134