xref: /linux/drivers/reset/starfive/reset-starfive-jh71x0.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
11ec3d20eSEmil Renner Berthing // SPDX-License-Identifier: GPL-2.0-or-later
21ec3d20eSEmil Renner Berthing /*
30333103eSEmil Renner Berthing  * Reset driver for the StarFive JH71X0 SoCs
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/iopoll.h>
121ec3d20eSEmil Renner Berthing #include <linux/reset-controller.h>
131ec3d20eSEmil Renner Berthing #include <linux/spinlock.h>
141ec3d20eSEmil Renner Berthing 
151ec3d20eSEmil Renner Berthing #include "reset-starfive-jh71x0.h"
161ec3d20eSEmil Renner Berthing 
170333103eSEmil Renner Berthing struct jh71x0_reset {
181ec3d20eSEmil Renner Berthing 	struct reset_controller_dev rcdev;
191ec3d20eSEmil Renner Berthing 	/* protect registers against concurrent read-modify-write */
201ec3d20eSEmil Renner Berthing 	spinlock_t lock;
21ed36fcd1SEmil Renner Berthing 	void __iomem *assert;
22ed36fcd1SEmil Renner Berthing 	void __iomem *status;
23*b6d7406cSEmil Renner Berthing 	const u32 *asserted;
241ec3d20eSEmil Renner Berthing };
251ec3d20eSEmil Renner Berthing 
260333103eSEmil Renner Berthing static inline struct jh71x0_reset *
jh71x0_reset_from(struct reset_controller_dev * rcdev)270333103eSEmil Renner Berthing jh71x0_reset_from(struct reset_controller_dev *rcdev)
281ec3d20eSEmil Renner Berthing {
290333103eSEmil Renner Berthing 	return container_of(rcdev, struct jh71x0_reset, rcdev);
301ec3d20eSEmil Renner Berthing }
311ec3d20eSEmil Renner Berthing 
jh71x0_reset_update(struct reset_controller_dev * rcdev,unsigned long id,bool assert)320333103eSEmil Renner Berthing static int jh71x0_reset_update(struct reset_controller_dev *rcdev,
331ec3d20eSEmil Renner Berthing 			       unsigned long id, bool assert)
341ec3d20eSEmil Renner Berthing {
350333103eSEmil Renner Berthing 	struct jh71x0_reset *data = jh71x0_reset_from(rcdev);
36*b6d7406cSEmil Renner Berthing 	unsigned long offset = id / 32;
37*b6d7406cSEmil Renner Berthing 	u32 mask = BIT(id % 32);
38*b6d7406cSEmil Renner Berthing 	void __iomem *reg_assert = data->assert + offset * sizeof(u32);
39*b6d7406cSEmil Renner Berthing 	void __iomem *reg_status = data->status + offset * sizeof(u32);
40*b6d7406cSEmil Renner Berthing 	u32 done = data->asserted ? data->asserted[offset] & mask : 0;
41*b6d7406cSEmil Renner Berthing 	u32 value;
421ec3d20eSEmil Renner Berthing 	unsigned long flags;
431ec3d20eSEmil Renner Berthing 	int ret;
441ec3d20eSEmil Renner Berthing 
451ec3d20eSEmil Renner Berthing 	if (!assert)
461ec3d20eSEmil Renner Berthing 		done ^= mask;
471ec3d20eSEmil Renner Berthing 
481ec3d20eSEmil Renner Berthing 	spin_lock_irqsave(&data->lock, flags);
491ec3d20eSEmil Renner Berthing 
50*b6d7406cSEmil Renner Berthing 	value = readl(reg_assert);
511ec3d20eSEmil Renner Berthing 	if (assert)
521ec3d20eSEmil Renner Berthing 		value |= mask;
531ec3d20eSEmil Renner Berthing 	else
541ec3d20eSEmil Renner Berthing 		value &= ~mask;
55*b6d7406cSEmil Renner Berthing 	writel(value, reg_assert);
561ec3d20eSEmil Renner Berthing 
571ec3d20eSEmil Renner Berthing 	/* if the associated clock is gated, deasserting might otherwise hang forever */
58*b6d7406cSEmil Renner Berthing 	ret = readl_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000);
591ec3d20eSEmil Renner Berthing 
601ec3d20eSEmil Renner Berthing 	spin_unlock_irqrestore(&data->lock, flags);
611ec3d20eSEmil Renner Berthing 	return ret;
621ec3d20eSEmil Renner Berthing }
631ec3d20eSEmil Renner Berthing 
jh71x0_reset_assert(struct reset_controller_dev * rcdev,unsigned long id)640333103eSEmil Renner Berthing static int jh71x0_reset_assert(struct reset_controller_dev *rcdev,
651ec3d20eSEmil Renner Berthing 			       unsigned long id)
661ec3d20eSEmil Renner Berthing {
670333103eSEmil Renner Berthing 	return jh71x0_reset_update(rcdev, id, true);
681ec3d20eSEmil Renner Berthing }
691ec3d20eSEmil Renner Berthing 
jh71x0_reset_deassert(struct reset_controller_dev * rcdev,unsigned long id)700333103eSEmil Renner Berthing static int jh71x0_reset_deassert(struct reset_controller_dev *rcdev,
711ec3d20eSEmil Renner Berthing 				 unsigned long id)
721ec3d20eSEmil Renner Berthing {
730333103eSEmil Renner Berthing 	return jh71x0_reset_update(rcdev, id, false);
741ec3d20eSEmil Renner Berthing }
751ec3d20eSEmil Renner Berthing 
jh71x0_reset_reset(struct reset_controller_dev * rcdev,unsigned long id)760333103eSEmil Renner Berthing static int jh71x0_reset_reset(struct reset_controller_dev *rcdev,
771ec3d20eSEmil Renner Berthing 			      unsigned long id)
781ec3d20eSEmil Renner Berthing {
791ec3d20eSEmil Renner Berthing 	int ret;
801ec3d20eSEmil Renner Berthing 
810333103eSEmil Renner Berthing 	ret = jh71x0_reset_assert(rcdev, id);
821ec3d20eSEmil Renner Berthing 	if (ret)
831ec3d20eSEmil Renner Berthing 		return ret;
841ec3d20eSEmil Renner Berthing 
850333103eSEmil Renner Berthing 	return jh71x0_reset_deassert(rcdev, id);
861ec3d20eSEmil Renner Berthing }
871ec3d20eSEmil Renner Berthing 
jh71x0_reset_status(struct reset_controller_dev * rcdev,unsigned long id)880333103eSEmil Renner Berthing static int jh71x0_reset_status(struct reset_controller_dev *rcdev,
891ec3d20eSEmil Renner Berthing 			       unsigned long id)
901ec3d20eSEmil Renner Berthing {
910333103eSEmil Renner Berthing 	struct jh71x0_reset *data = jh71x0_reset_from(rcdev);
92*b6d7406cSEmil Renner Berthing 	unsigned long offset = id / 32;
93*b6d7406cSEmil Renner Berthing 	u32 mask = BIT(id % 32);
94*b6d7406cSEmil Renner Berthing 	void __iomem *reg_status = data->status + offset * sizeof(u32);
95*b6d7406cSEmil Renner Berthing 	u32 value = readl(reg_status);
961ec3d20eSEmil Renner Berthing 
97ed36fcd1SEmil Renner Berthing 	return !((value ^ data->asserted[offset]) & mask);
981ec3d20eSEmil Renner Berthing }
991ec3d20eSEmil Renner Berthing 
1000333103eSEmil Renner Berthing static const struct reset_control_ops jh71x0_reset_ops = {
1010333103eSEmil Renner Berthing 	.assert		= jh71x0_reset_assert,
1020333103eSEmil Renner Berthing 	.deassert	= jh71x0_reset_deassert,
1030333103eSEmil Renner Berthing 	.reset		= jh71x0_reset_reset,
1040333103eSEmil Renner Berthing 	.status		= jh71x0_reset_status,
1051ec3d20eSEmil Renner Berthing };
1061ec3d20eSEmil Renner Berthing 
reset_starfive_jh71x0_register(struct device * dev,struct device_node * of_node,void __iomem * assert,void __iomem * status,const u32 * asserted,unsigned int nr_resets,struct module * owner)1070333103eSEmil Renner Berthing int reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_node,
108ed36fcd1SEmil Renner Berthing 				   void __iomem *assert, void __iomem *status,
109*b6d7406cSEmil Renner Berthing 				   const u32 *asserted, unsigned int nr_resets,
110ed36fcd1SEmil Renner Berthing 				   struct module *owner)
1111ec3d20eSEmil Renner Berthing {
1120333103eSEmil Renner Berthing 	struct jh71x0_reset *data;
1131ec3d20eSEmil Renner Berthing 
114ed36fcd1SEmil Renner Berthing 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
1151ec3d20eSEmil Renner Berthing 	if (!data)
1161ec3d20eSEmil Renner Berthing 		return -ENOMEM;
1171ec3d20eSEmil Renner Berthing 
1180333103eSEmil Renner Berthing 	data->rcdev.ops = &jh71x0_reset_ops;
119ed36fcd1SEmil Renner Berthing 	data->rcdev.owner = owner;
120ed36fcd1SEmil Renner Berthing 	data->rcdev.nr_resets = nr_resets;
121ed36fcd1SEmil Renner Berthing 	data->rcdev.dev = dev;
122ed36fcd1SEmil Renner Berthing 	data->rcdev.of_node = of_node;
1231ec3d20eSEmil Renner Berthing 
124ed36fcd1SEmil Renner Berthing 	spin_lock_init(&data->lock);
125ed36fcd1SEmil Renner Berthing 	data->assert = assert;
126ed36fcd1SEmil Renner Berthing 	data->status = status;
127ed36fcd1SEmil Renner Berthing 	data->asserted = asserted;
128ed36fcd1SEmil Renner Berthing 
129ed36fcd1SEmil Renner Berthing 	return devm_reset_controller_register(dev, &data->rcdev);
1301ec3d20eSEmil Renner Berthing }
1310333103eSEmil Renner Berthing EXPORT_SYMBOL_GPL(reset_starfive_jh71x0_register);
132