162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Reset driver for the StarFive JH71X0 SoCs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/bitmap.h> 962306a36Sopenharmony_ci#include <linux/device.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/iopoll.h> 1262306a36Sopenharmony_ci#include <linux/reset-controller.h> 1362306a36Sopenharmony_ci#include <linux/spinlock.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "reset-starfive-jh71x0.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct jh71x0_reset { 1862306a36Sopenharmony_ci struct reset_controller_dev rcdev; 1962306a36Sopenharmony_ci /* protect registers against concurrent read-modify-write */ 2062306a36Sopenharmony_ci spinlock_t lock; 2162306a36Sopenharmony_ci void __iomem *assert; 2262306a36Sopenharmony_ci void __iomem *status; 2362306a36Sopenharmony_ci const u32 *asserted; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic inline struct jh71x0_reset * 2762306a36Sopenharmony_cijh71x0_reset_from(struct reset_controller_dev *rcdev) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci return container_of(rcdev, struct jh71x0_reset, rcdev); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int jh71x0_reset_update(struct reset_controller_dev *rcdev, 3362306a36Sopenharmony_ci unsigned long id, bool assert) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct jh71x0_reset *data = jh71x0_reset_from(rcdev); 3662306a36Sopenharmony_ci unsigned long offset = id / 32; 3762306a36Sopenharmony_ci u32 mask = BIT(id % 32); 3862306a36Sopenharmony_ci void __iomem *reg_assert = data->assert + offset * sizeof(u32); 3962306a36Sopenharmony_ci void __iomem *reg_status = data->status + offset * sizeof(u32); 4062306a36Sopenharmony_ci u32 done = data->asserted ? data->asserted[offset] & mask : 0; 4162306a36Sopenharmony_ci u32 value; 4262306a36Sopenharmony_ci unsigned long flags; 4362306a36Sopenharmony_ci int ret; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (!assert) 4662306a36Sopenharmony_ci done ^= mask; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci spin_lock_irqsave(&data->lock, flags); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci value = readl(reg_assert); 5162306a36Sopenharmony_ci if (assert) 5262306a36Sopenharmony_ci value |= mask; 5362306a36Sopenharmony_ci else 5462306a36Sopenharmony_ci value &= ~mask; 5562306a36Sopenharmony_ci writel(value, reg_assert); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* if the associated clock is gated, deasserting might otherwise hang forever */ 5862306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci spin_unlock_irqrestore(&data->lock, flags); 6162306a36Sopenharmony_ci return ret; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int jh71x0_reset_assert(struct reset_controller_dev *rcdev, 6562306a36Sopenharmony_ci unsigned long id) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci return jh71x0_reset_update(rcdev, id, true); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int jh71x0_reset_deassert(struct reset_controller_dev *rcdev, 7162306a36Sopenharmony_ci unsigned long id) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci return jh71x0_reset_update(rcdev, id, false); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int jh71x0_reset_reset(struct reset_controller_dev *rcdev, 7762306a36Sopenharmony_ci unsigned long id) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci int ret; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci ret = jh71x0_reset_assert(rcdev, id); 8262306a36Sopenharmony_ci if (ret) 8362306a36Sopenharmony_ci return ret; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return jh71x0_reset_deassert(rcdev, id); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int jh71x0_reset_status(struct reset_controller_dev *rcdev, 8962306a36Sopenharmony_ci unsigned long id) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct jh71x0_reset *data = jh71x0_reset_from(rcdev); 9262306a36Sopenharmony_ci unsigned long offset = id / 32; 9362306a36Sopenharmony_ci u32 mask = BIT(id % 32); 9462306a36Sopenharmony_ci void __iomem *reg_status = data->status + offset * sizeof(u32); 9562306a36Sopenharmony_ci u32 value = readl(reg_status); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return !((value ^ data->asserted[offset]) & mask); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic const struct reset_control_ops jh71x0_reset_ops = { 10162306a36Sopenharmony_ci .assert = jh71x0_reset_assert, 10262306a36Sopenharmony_ci .deassert = jh71x0_reset_deassert, 10362306a36Sopenharmony_ci .reset = jh71x0_reset_reset, 10462306a36Sopenharmony_ci .status = jh71x0_reset_status, 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ciint reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_node, 10862306a36Sopenharmony_ci void __iomem *assert, void __iomem *status, 10962306a36Sopenharmony_ci const u32 *asserted, unsigned int nr_resets, 11062306a36Sopenharmony_ci struct module *owner) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct jh71x0_reset *data; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 11562306a36Sopenharmony_ci if (!data) 11662306a36Sopenharmony_ci return -ENOMEM; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci data->rcdev.ops = &jh71x0_reset_ops; 11962306a36Sopenharmony_ci data->rcdev.owner = owner; 12062306a36Sopenharmony_ci data->rcdev.nr_resets = nr_resets; 12162306a36Sopenharmony_ci data->rcdev.dev = dev; 12262306a36Sopenharmony_ci data->rcdev.of_node = of_node; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci spin_lock_init(&data->lock); 12562306a36Sopenharmony_ci data->assert = assert; 12662306a36Sopenharmony_ci data->status = status; 12762306a36Sopenharmony_ci data->asserted = asserted; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return devm_reset_controller_register(dev, &data->rcdev); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(reset_starfive_jh71x0_register); 132