linux/drivers/reset/reset-starfive-jh7100.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Reset driver for the StarFive JH7100 SoC
   4 *
   5 * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk>
   6 */
   7
   8#include <linux/bitmap.h>
   9#include <linux/io.h>
  10#include <linux/io-64-nonatomic-lo-hi.h>
  11#include <linux/iopoll.h>
  12#include <linux/mod_devicetable.h>
  13#include <linux/platform_device.h>
  14#include <linux/reset-controller.h>
  15#include <linux/spinlock.h>
  16
  17#include <dt-bindings/reset/starfive-jh7100.h>
  18
  19/* register offsets */
  20#define JH7100_RESET_ASSERT0    0x00
  21#define JH7100_RESET_ASSERT1    0x04
  22#define JH7100_RESET_ASSERT2    0x08
  23#define JH7100_RESET_ASSERT3    0x0c
  24#define JH7100_RESET_STATUS0    0x10
  25#define JH7100_RESET_STATUS1    0x14
  26#define JH7100_RESET_STATUS2    0x18
  27#define JH7100_RESET_STATUS3    0x1c
  28
  29/*
  30 * Writing a 1 to the n'th bit of the m'th ASSERT register asserts
  31 * line 32m + n, and writing a 0 deasserts the same line.
  32 * Most reset lines have their status inverted so a 0 bit in the STATUS
  33 * register means the line is asserted and a 1 means it's deasserted. A few
  34 * lines don't though, so store the expected value of the status registers when
  35 * all lines are asserted.
  36 */
  37static const u64 jh7100_reset_asserted[2] = {
  38        /* STATUS0 */
  39        BIT_ULL_MASK(JH7100_RST_U74) |
  40        BIT_ULL_MASK(JH7100_RST_VP6_DRESET) |
  41        BIT_ULL_MASK(JH7100_RST_VP6_BRESET) |
  42        /* STATUS1 */
  43        BIT_ULL_MASK(JH7100_RST_HIFI4_DRESET) |
  44        BIT_ULL_MASK(JH7100_RST_HIFI4_BRESET),
  45        /* STATUS2 */
  46        BIT_ULL_MASK(JH7100_RST_E24) |
  47        /* STATUS3 */
  48        0,
  49};
  50
  51struct jh7100_reset {
  52        struct reset_controller_dev rcdev;
  53        /* protect registers against concurrent read-modify-write */
  54        spinlock_t lock;
  55        void __iomem *base;
  56};
  57
  58static inline struct jh7100_reset *
  59jh7100_reset_from(struct reset_controller_dev *rcdev)
  60{
  61        return container_of(rcdev, struct jh7100_reset, rcdev);
  62}
  63
  64static int jh7100_reset_update(struct reset_controller_dev *rcdev,
  65                               unsigned long id, bool assert)
  66{
  67        struct jh7100_reset *data = jh7100_reset_from(rcdev);
  68        unsigned long offset = BIT_ULL_WORD(id);
  69        u64 mask = BIT_ULL_MASK(id);
  70        void __iomem *reg_assert = data->base + JH7100_RESET_ASSERT0 + offset * sizeof(u64);
  71        void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u64);
  72        u64 done = jh7100_reset_asserted[offset] & mask;
  73        u64 value;
  74        unsigned long flags;
  75        int ret;
  76
  77        if (!assert)
  78                done ^= mask;
  79
  80        spin_lock_irqsave(&data->lock, flags);
  81
  82        value = readq(reg_assert);
  83        if (assert)
  84                value |= mask;
  85        else
  86                value &= ~mask;
  87        writeq(value, reg_assert);
  88
  89        /* if the associated clock is gated, deasserting might otherwise hang forever */
  90        ret = readq_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000);
  91
  92        spin_unlock_irqrestore(&data->lock, flags);
  93        return ret;
  94}
  95
  96static int jh7100_reset_assert(struct reset_controller_dev *rcdev,
  97                               unsigned long id)
  98{
  99        return jh7100_reset_update(rcdev, id, true);
 100}
 101
 102static int jh7100_reset_deassert(struct reset_controller_dev *rcdev,
 103                                 unsigned long id)
 104{
 105        return jh7100_reset_update(rcdev, id, false);
 106}
 107
 108static int jh7100_reset_reset(struct reset_controller_dev *rcdev,
 109                              unsigned long id)
 110{
 111        int ret;
 112
 113        ret = jh7100_reset_assert(rcdev, id);
 114        if (ret)
 115                return ret;
 116
 117        return jh7100_reset_deassert(rcdev, id);
 118}
 119
 120static int jh7100_reset_status(struct reset_controller_dev *rcdev,
 121                               unsigned long id)
 122{
 123        struct jh7100_reset *data = jh7100_reset_from(rcdev);
 124        unsigned long offset = BIT_ULL_WORD(id);
 125        u64 mask = BIT_ULL_MASK(id);
 126        void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u64);
 127        u64 value = readq(reg_status);
 128
 129        return !((value ^ jh7100_reset_asserted[offset]) & mask);
 130}
 131
 132static const struct reset_control_ops jh7100_reset_ops = {
 133        .assert         = jh7100_reset_assert,
 134        .deassert       = jh7100_reset_deassert,
 135        .reset          = jh7100_reset_reset,
 136        .status         = jh7100_reset_status,
 137};
 138
 139static int __init jh7100_reset_probe(struct platform_device *pdev)
 140{
 141        struct jh7100_reset *data;
 142
 143        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 144        if (!data)
 145                return -ENOMEM;
 146
 147        data->base = devm_platform_ioremap_resource(pdev, 0);
 148        if (IS_ERR(data->base))
 149                return PTR_ERR(data->base);
 150
 151        data->rcdev.ops = &jh7100_reset_ops;
 152        data->rcdev.owner = THIS_MODULE;
 153        data->rcdev.nr_resets = JH7100_RSTN_END;
 154        data->rcdev.dev = &pdev->dev;
 155        data->rcdev.of_node = pdev->dev.of_node;
 156        spin_lock_init(&data->lock);
 157
 158        return devm_reset_controller_register(&pdev->dev, &data->rcdev);
 159}
 160
 161static const struct of_device_id jh7100_reset_dt_ids[] = {
 162        { .compatible = "starfive,jh7100-reset" },
 163        { /* sentinel */ }
 164};
 165
 166static struct platform_driver jh7100_reset_driver = {
 167        .driver = {
 168                .name = "jh7100-reset",
 169                .of_match_table = jh7100_reset_dt_ids,
 170                .suppress_bind_attrs = true,
 171        },
 172};
 173builtin_platform_driver_probe(jh7100_reset_driver, jh7100_reset_probe);
 174