xref: /linux/drivers/reset/starfive/reset-starfive-jh71x0.c (revision ed36fcd160f3a703e0264539abdf0da2f3e0fc35)
11ec3d20eSEmil Renner Berthing // SPDX-License-Identifier: GPL-2.0-or-later
21ec3d20eSEmil Renner Berthing /*
31ec3d20eSEmil Renner Berthing  * Reset driver for the StarFive JH7100 SoC
41ec3d20eSEmil Renner Berthing  *
51ec3d20eSEmil Renner Berthing  * Copyright (C) 2021 Emil Renner Berthing <kernel@esmil.dk>
61ec3d20eSEmil Renner Berthing  */
71ec3d20eSEmil Renner Berthing 
81ec3d20eSEmil Renner Berthing #include <linux/bitmap.h>
91ec3d20eSEmil Renner Berthing #include <linux/device.h>
101ec3d20eSEmil Renner Berthing #include <linux/io.h>
111ec3d20eSEmil Renner Berthing #include <linux/io-64-nonatomic-lo-hi.h>
121ec3d20eSEmil Renner Berthing #include <linux/iopoll.h>
131ec3d20eSEmil Renner Berthing #include <linux/reset-controller.h>
141ec3d20eSEmil Renner Berthing #include <linux/spinlock.h>
151ec3d20eSEmil Renner Berthing 
161ec3d20eSEmil Renner Berthing #include "reset-starfive-jh71x0.h"
171ec3d20eSEmil Renner Berthing 
181ec3d20eSEmil Renner Berthing struct jh7100_reset {
191ec3d20eSEmil Renner Berthing 	struct reset_controller_dev rcdev;
201ec3d20eSEmil Renner Berthing 	/* protect registers against concurrent read-modify-write */
211ec3d20eSEmil Renner Berthing 	spinlock_t lock;
22*ed36fcd1SEmil Renner Berthing 	void __iomem *assert;
23*ed36fcd1SEmil Renner Berthing 	void __iomem *status;
24*ed36fcd1SEmil Renner Berthing 	const u64 *asserted;
251ec3d20eSEmil Renner Berthing };
261ec3d20eSEmil Renner Berthing 
271ec3d20eSEmil Renner Berthing static inline struct jh7100_reset *
281ec3d20eSEmil Renner Berthing jh7100_reset_from(struct reset_controller_dev *rcdev)
291ec3d20eSEmil Renner Berthing {
301ec3d20eSEmil Renner Berthing 	return container_of(rcdev, struct jh7100_reset, rcdev);
311ec3d20eSEmil Renner Berthing }
321ec3d20eSEmil Renner Berthing 
331ec3d20eSEmil Renner Berthing static int jh7100_reset_update(struct reset_controller_dev *rcdev,
341ec3d20eSEmil Renner Berthing 			       unsigned long id, bool assert)
351ec3d20eSEmil Renner Berthing {
361ec3d20eSEmil Renner Berthing 	struct jh7100_reset *data = jh7100_reset_from(rcdev);
371ec3d20eSEmil Renner Berthing 	unsigned long offset = BIT_ULL_WORD(id);
381ec3d20eSEmil Renner Berthing 	u64 mask = BIT_ULL_MASK(id);
39*ed36fcd1SEmil Renner Berthing 	void __iomem *reg_assert = data->assert + offset * sizeof(u64);
40*ed36fcd1SEmil Renner Berthing 	void __iomem *reg_status = data->status + offset * sizeof(u64);
41*ed36fcd1SEmil Renner Berthing 	u64 done = data->asserted ? data->asserted[offset] & mask : 0;
421ec3d20eSEmil Renner Berthing 	u64 value;
431ec3d20eSEmil Renner Berthing 	unsigned long flags;
441ec3d20eSEmil Renner Berthing 	int ret;
451ec3d20eSEmil Renner Berthing 
461ec3d20eSEmil Renner Berthing 	if (!assert)
471ec3d20eSEmil Renner Berthing 		done ^= mask;
481ec3d20eSEmil Renner Berthing 
491ec3d20eSEmil Renner Berthing 	spin_lock_irqsave(&data->lock, flags);
501ec3d20eSEmil Renner Berthing 
511ec3d20eSEmil Renner Berthing 	value = readq(reg_assert);
521ec3d20eSEmil Renner Berthing 	if (assert)
531ec3d20eSEmil Renner Berthing 		value |= mask;
541ec3d20eSEmil Renner Berthing 	else
551ec3d20eSEmil Renner Berthing 		value &= ~mask;
561ec3d20eSEmil Renner Berthing 	writeq(value, reg_assert);
571ec3d20eSEmil Renner Berthing 
581ec3d20eSEmil Renner Berthing 	/* if the associated clock is gated, deasserting might otherwise hang forever */
591ec3d20eSEmil Renner Berthing 	ret = readq_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000);
601ec3d20eSEmil Renner Berthing 
611ec3d20eSEmil Renner Berthing 	spin_unlock_irqrestore(&data->lock, flags);
621ec3d20eSEmil Renner Berthing 	return ret;
631ec3d20eSEmil Renner Berthing }
641ec3d20eSEmil Renner Berthing 
651ec3d20eSEmil Renner Berthing static int jh7100_reset_assert(struct reset_controller_dev *rcdev,
661ec3d20eSEmil Renner Berthing 			       unsigned long id)
671ec3d20eSEmil Renner Berthing {
681ec3d20eSEmil Renner Berthing 	return jh7100_reset_update(rcdev, id, true);
691ec3d20eSEmil Renner Berthing }
701ec3d20eSEmil Renner Berthing 
711ec3d20eSEmil Renner Berthing static int jh7100_reset_deassert(struct reset_controller_dev *rcdev,
721ec3d20eSEmil Renner Berthing 				 unsigned long id)
731ec3d20eSEmil Renner Berthing {
741ec3d20eSEmil Renner Berthing 	return jh7100_reset_update(rcdev, id, false);
751ec3d20eSEmil Renner Berthing }
761ec3d20eSEmil Renner Berthing 
771ec3d20eSEmil Renner Berthing static int jh7100_reset_reset(struct reset_controller_dev *rcdev,
781ec3d20eSEmil Renner Berthing 			      unsigned long id)
791ec3d20eSEmil Renner Berthing {
801ec3d20eSEmil Renner Berthing 	int ret;
811ec3d20eSEmil Renner Berthing 
821ec3d20eSEmil Renner Berthing 	ret = jh7100_reset_assert(rcdev, id);
831ec3d20eSEmil Renner Berthing 	if (ret)
841ec3d20eSEmil Renner Berthing 		return ret;
851ec3d20eSEmil Renner Berthing 
861ec3d20eSEmil Renner Berthing 	return jh7100_reset_deassert(rcdev, id);
871ec3d20eSEmil Renner Berthing }
881ec3d20eSEmil Renner Berthing 
891ec3d20eSEmil Renner Berthing static int jh7100_reset_status(struct reset_controller_dev *rcdev,
901ec3d20eSEmil Renner Berthing 			       unsigned long id)
911ec3d20eSEmil Renner Berthing {
921ec3d20eSEmil Renner Berthing 	struct jh7100_reset *data = jh7100_reset_from(rcdev);
931ec3d20eSEmil Renner Berthing 	unsigned long offset = BIT_ULL_WORD(id);
941ec3d20eSEmil Renner Berthing 	u64 mask = BIT_ULL_MASK(id);
95*ed36fcd1SEmil Renner Berthing 	void __iomem *reg_status = data->status + offset * sizeof(u64);
961ec3d20eSEmil Renner Berthing 	u64 value = readq(reg_status);
971ec3d20eSEmil Renner Berthing 
98*ed36fcd1SEmil Renner Berthing 	return !((value ^ data->asserted[offset]) & mask);
991ec3d20eSEmil Renner Berthing }
1001ec3d20eSEmil Renner Berthing 
1011ec3d20eSEmil Renner Berthing static const struct reset_control_ops jh7100_reset_ops = {
1021ec3d20eSEmil Renner Berthing 	.assert		= jh7100_reset_assert,
1031ec3d20eSEmil Renner Berthing 	.deassert	= jh7100_reset_deassert,
1041ec3d20eSEmil Renner Berthing 	.reset		= jh7100_reset_reset,
1051ec3d20eSEmil Renner Berthing 	.status		= jh7100_reset_status,
1061ec3d20eSEmil Renner Berthing };
1071ec3d20eSEmil Renner Berthing 
108*ed36fcd1SEmil Renner Berthing int reset_starfive_jh7100_register(struct device *dev, struct device_node *of_node,
109*ed36fcd1SEmil Renner Berthing 				   void __iomem *assert, void __iomem *status,
110*ed36fcd1SEmil Renner Berthing 				   const u64 *asserted, unsigned int nr_resets,
111*ed36fcd1SEmil Renner Berthing 				   struct module *owner)
1121ec3d20eSEmil Renner Berthing {
1131ec3d20eSEmil Renner Berthing 	struct jh7100_reset *data;
1141ec3d20eSEmil Renner Berthing 
115*ed36fcd1SEmil Renner Berthing 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
1161ec3d20eSEmil Renner Berthing 	if (!data)
1171ec3d20eSEmil Renner Berthing 		return -ENOMEM;
1181ec3d20eSEmil Renner Berthing 
1191ec3d20eSEmil Renner Berthing 	data->rcdev.ops = &jh7100_reset_ops;
120*ed36fcd1SEmil Renner Berthing 	data->rcdev.owner = owner;
121*ed36fcd1SEmil Renner Berthing 	data->rcdev.nr_resets = nr_resets;
122*ed36fcd1SEmil Renner Berthing 	data->rcdev.dev = dev;
123*ed36fcd1SEmil Renner Berthing 	data->rcdev.of_node = of_node;
1241ec3d20eSEmil Renner Berthing 
125*ed36fcd1SEmil Renner Berthing 	spin_lock_init(&data->lock);
126*ed36fcd1SEmil Renner Berthing 	data->assert = assert;
127*ed36fcd1SEmil Renner Berthing 	data->status = status;
128*ed36fcd1SEmil Renner Berthing 	data->asserted = asserted;
129*ed36fcd1SEmil Renner Berthing 
130*ed36fcd1SEmil Renner Berthing 	return devm_reset_controller_register(dev, &data->rcdev);
1311ec3d20eSEmil Renner Berthing }
132*ed36fcd1SEmil Renner Berthing EXPORT_SYMBOL_GPL(reset_starfive_jh7100_register);
133