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;
23b6d7406cSEmil 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);
36b6d7406cSEmil Renner Berthing unsigned long offset = id / 32;
37b6d7406cSEmil Renner Berthing u32 mask = BIT(id % 32);
38b6d7406cSEmil Renner Berthing void __iomem *reg_assert = data->assert + offset * sizeof(u32);
39b6d7406cSEmil Renner Berthing void __iomem *reg_status = data->status + offset * sizeof(u32);
40b6d7406cSEmil Renner Berthing u32 done = data->asserted ? data->asserted[offset] & mask : 0;
41b6d7406cSEmil 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
50b6d7406cSEmil 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;
55b6d7406cSEmil Renner Berthing writel(value, reg_assert);
561ec3d20eSEmil Renner Berthing
571ec3d20eSEmil Renner Berthing /* if the associated clock is gated, deasserting might otherwise hang forever */
58b6d7406cSEmil 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);
92b6d7406cSEmil Renner Berthing unsigned long offset = id / 32;
93b6d7406cSEmil Renner Berthing u32 mask = BIT(id % 32);
94b6d7406cSEmil Renner Berthing void __iomem *reg_status = data->status + offset * sizeof(u32);
95b6d7406cSEmil Renner Berthing u32 value = readl(reg_status);
961ec3d20eSEmil Renner Berthing
97*3918b201SChanghuang Liang if (!data->asserted)
98*3918b201SChanghuang Liang return !(value & mask);
99*3918b201SChanghuang Liang
100ed36fcd1SEmil Renner Berthing return !((value ^ data->asserted[offset]) & mask);
1011ec3d20eSEmil Renner Berthing }
1021ec3d20eSEmil Renner Berthing
1030333103eSEmil Renner Berthing static const struct reset_control_ops jh71x0_reset_ops = {
1040333103eSEmil Renner Berthing .assert = jh71x0_reset_assert,
1050333103eSEmil Renner Berthing .deassert = jh71x0_reset_deassert,
1060333103eSEmil Renner Berthing .reset = jh71x0_reset_reset,
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 .status = jh71x0_reset_status,
1081ec3d20eSEmil Renner Berthing };
1091ec3d20eSEmil Renner Berthing
1100333103eSEmil Renner Berthing int reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_node,
111ed36fcd1SEmil Renner Berthing void __iomem *assert, void __iomem *status,
112b6d7406cSEmil Renner Berthing const u32 *asserted, unsigned int nr_resets,
113ed36fcd1SEmil Renner Berthing struct module *owner)
1141ec3d20eSEmil Renner Berthing {
1150333103eSEmil Renner Berthing struct jh71x0_reset *data;
1161ec3d20eSEmil Renner Berthing
117ed36fcd1SEmil Renner Berthing data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
1181ec3d20eSEmil Renner Berthing if (!data)
1191ec3d20eSEmil Renner Berthing return -ENOMEM;
1201ec3d20eSEmil Renner Berthing
1210333103eSEmil Renner Berthing data->rcdev.ops = &jh71x0_reset_ops;
122ed36fcd1SEmil Renner Berthing data->rcdev.owner = owner;
123ed36fcd1SEmil Renner Berthing data->rcdev.nr_resets = nr_resets;
124ed36fcd1SEmil Renner Berthing data->rcdev.dev = dev;
125ed36fcd1SEmil Renner Berthing data->rcdev.of_node = of_node;
1261ec3d20eSEmil Renner Berthing
127ed36fcd1SEmil Renner Berthing spin_lock_init(&data->lock);
128ed36fcd1SEmil Renner Berthing data->assert = assert;
129ed36fcd1SEmil Renner Berthing data->status = status;
130ed36fcd1SEmil Renner Berthing data->asserted = asserted;
131ed36fcd1SEmil Renner Berthing
132ed36fcd1SEmil Renner Berthing return devm_reset_controller_register(dev, &data->rcdev);
1331ec3d20eSEmil Renner Berthing }
1340333103eSEmil Renner Berthing EXPORT_SYMBOL_GPL(reset_starfive_jh71x0_register);
135