135 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Reset driver for the StarFive JH71X0 SoCs4 *5 * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk>6 */7 8#include <linux/bitmap.h>9#include <linux/device.h>10#include <linux/io.h>11#include <linux/iopoll.h>12#include <linux/reset-controller.h>13#include <linux/spinlock.h>14 15#include "reset-starfive-jh71x0.h"16 17struct jh71x0_reset {18 struct reset_controller_dev rcdev;19 /* protect registers against concurrent read-modify-write */20 spinlock_t lock;21 void __iomem *assert;22 void __iomem *status;23 const u32 *asserted;24};25 26static inline struct jh71x0_reset *27jh71x0_reset_from(struct reset_controller_dev *rcdev)28{29 return container_of(rcdev, struct jh71x0_reset, rcdev);30}31 32static int jh71x0_reset_update(struct reset_controller_dev *rcdev,33 unsigned long id, bool assert)34{35 struct jh71x0_reset *data = jh71x0_reset_from(rcdev);36 unsigned long offset = id / 32;37 u32 mask = BIT(id % 32);38 void __iomem *reg_assert = data->assert + offset * sizeof(u32);39 void __iomem *reg_status = data->status + offset * sizeof(u32);40 u32 done = data->asserted ? data->asserted[offset] & mask : 0;41 u32 value;42 unsigned long flags;43 int ret;44 45 if (!assert)46 done ^= mask;47 48 spin_lock_irqsave(&data->lock, flags);49 50 value = readl(reg_assert);51 if (assert)52 value |= mask;53 else54 value &= ~mask;55 writel(value, reg_assert);56 57 /* if the associated clock is gated, deasserting might otherwise hang forever */58 ret = readl_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000);59 60 spin_unlock_irqrestore(&data->lock, flags);61 return ret;62}63 64static int jh71x0_reset_assert(struct reset_controller_dev *rcdev,65 unsigned long id)66{67 return jh71x0_reset_update(rcdev, id, true);68}69 70static int jh71x0_reset_deassert(struct reset_controller_dev *rcdev,71 unsigned long id)72{73 return jh71x0_reset_update(rcdev, id, false);74}75 76static int jh71x0_reset_reset(struct reset_controller_dev *rcdev,77 unsigned long id)78{79 int ret;80 81 ret = jh71x0_reset_assert(rcdev, id);82 if (ret)83 return ret;84 85 return jh71x0_reset_deassert(rcdev, id);86}87 88static int jh71x0_reset_status(struct reset_controller_dev *rcdev,89 unsigned long id)90{91 struct jh71x0_reset *data = jh71x0_reset_from(rcdev);92 unsigned long offset = id / 32;93 u32 mask = BIT(id % 32);94 void __iomem *reg_status = data->status + offset * sizeof(u32);95 u32 value = readl(reg_status);96 97 if (!data->asserted)98 return !(value & mask);99 100 return !((value ^ data->asserted[offset]) & mask);101}102 103static const struct reset_control_ops jh71x0_reset_ops = {104 .assert = jh71x0_reset_assert,105 .deassert = jh71x0_reset_deassert,106 .reset = jh71x0_reset_reset,107 .status = jh71x0_reset_status,108};109 110int reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_node,111 void __iomem *assert, void __iomem *status,112 const u32 *asserted, unsigned int nr_resets,113 struct module *owner)114{115 struct jh71x0_reset *data;116 117 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);118 if (!data)119 return -ENOMEM;120 121 data->rcdev.ops = &jh71x0_reset_ops;122 data->rcdev.owner = owner;123 data->rcdev.nr_resets = nr_resets;124 data->rcdev.dev = dev;125 data->rcdev.of_node = of_node;126 127 spin_lock_init(&data->lock);128 data->assert = assert;129 data->status = status;130 data->asserted = asserted;131 132 return devm_reset_controller_register(dev, &data->rcdev);133}134EXPORT_SYMBOL_GPL(reset_starfive_jh71x0_register);135