105f9e363SConor Dooley // SPDX-License-Identifier: GPL-2.0-only
205f9e363SConor Dooley /*
305f9e363SConor Dooley * PolarFire SoC (MPFS) Peripheral Clock Reset Controller
405f9e363SConor Dooley *
505f9e363SConor Dooley * Author: Conor Dooley <conor.dooley@microchip.com>
605f9e363SConor Dooley * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
705f9e363SConor Dooley *
805f9e363SConor Dooley */
905f9e363SConor Dooley #include <linux/auxiliary_bus.h>
1005f9e363SConor Dooley #include <linux/delay.h>
1105f9e363SConor Dooley #include <linux/module.h>
12*bad8a8afSRob Herring #include <linux/of.h>
1305f9e363SConor Dooley #include <linux/platform_device.h>
1405f9e363SConor Dooley #include <linux/reset-controller.h>
1505f9e363SConor Dooley #include <dt-bindings/clock/microchip,mpfs-clock.h>
1605f9e363SConor Dooley #include <soc/microchip/mpfs.h>
1705f9e363SConor Dooley
1805f9e363SConor Dooley /*
1905f9e363SConor Dooley * The ENVM reset is the lowest bit in the register & I am using the CLK_FOO
2005f9e363SConor Dooley * defines in the dt to make things easier to configure - so this is accounting
2105f9e363SConor Dooley * for the offset of 3 there.
2205f9e363SConor Dooley */
2305f9e363SConor Dooley #define MPFS_PERIPH_OFFSET CLK_ENVM
2405f9e363SConor Dooley #define MPFS_NUM_RESETS 30u
2505f9e363SConor Dooley #define MPFS_SLEEP_MIN_US 100
2605f9e363SConor Dooley #define MPFS_SLEEP_MAX_US 200
2705f9e363SConor Dooley
2805f9e363SConor Dooley /* block concurrent access to the soft reset register */
2905f9e363SConor Dooley static DEFINE_SPINLOCK(mpfs_reset_lock);
3005f9e363SConor Dooley
3105f9e363SConor Dooley /*
3205f9e363SConor Dooley * Peripheral clock resets
3305f9e363SConor Dooley */
3405f9e363SConor Dooley
mpfs_assert(struct reset_controller_dev * rcdev,unsigned long id)3505f9e363SConor Dooley static int mpfs_assert(struct reset_controller_dev *rcdev, unsigned long id)
3605f9e363SConor Dooley {
3705f9e363SConor Dooley unsigned long flags;
3805f9e363SConor Dooley u32 reg;
3905f9e363SConor Dooley
4005f9e363SConor Dooley spin_lock_irqsave(&mpfs_reset_lock, flags);
4105f9e363SConor Dooley
4205f9e363SConor Dooley reg = mpfs_reset_read(rcdev->dev);
4305f9e363SConor Dooley reg |= BIT(id);
4405f9e363SConor Dooley mpfs_reset_write(rcdev->dev, reg);
4505f9e363SConor Dooley
4605f9e363SConor Dooley spin_unlock_irqrestore(&mpfs_reset_lock, flags);
4705f9e363SConor Dooley
4805f9e363SConor Dooley return 0;
4905f9e363SConor Dooley }
5005f9e363SConor Dooley
mpfs_deassert(struct reset_controller_dev * rcdev,unsigned long id)5105f9e363SConor Dooley static int mpfs_deassert(struct reset_controller_dev *rcdev, unsigned long id)
5205f9e363SConor Dooley {
5305f9e363SConor Dooley unsigned long flags;
5405f9e363SConor Dooley u32 reg;
5505f9e363SConor Dooley
5605f9e363SConor Dooley spin_lock_irqsave(&mpfs_reset_lock, flags);
5705f9e363SConor Dooley
5805f9e363SConor Dooley reg = mpfs_reset_read(rcdev->dev);
5905f9e363SConor Dooley reg &= ~BIT(id);
6005f9e363SConor Dooley mpfs_reset_write(rcdev->dev, reg);
6105f9e363SConor Dooley
6205f9e363SConor Dooley spin_unlock_irqrestore(&mpfs_reset_lock, flags);
6305f9e363SConor Dooley
6405f9e363SConor Dooley return 0;
6505f9e363SConor Dooley }
6605f9e363SConor Dooley
mpfs_status(struct reset_controller_dev * rcdev,unsigned long id)6705f9e363SConor Dooley static int mpfs_status(struct reset_controller_dev *rcdev, unsigned long id)
6805f9e363SConor Dooley {
6905f9e363SConor Dooley u32 reg = mpfs_reset_read(rcdev->dev);
7005f9e363SConor Dooley
7105f9e363SConor Dooley /*
7205f9e363SConor Dooley * It is safe to return here as MPFS_NUM_RESETS makes sure the sign bit
7305f9e363SConor Dooley * is never hit.
7405f9e363SConor Dooley */
7505f9e363SConor Dooley return (reg & BIT(id));
7605f9e363SConor Dooley }
7705f9e363SConor Dooley
mpfs_reset(struct reset_controller_dev * rcdev,unsigned long id)7805f9e363SConor Dooley static int mpfs_reset(struct reset_controller_dev *rcdev, unsigned long id)
7905f9e363SConor Dooley {
8005f9e363SConor Dooley mpfs_assert(rcdev, id);
8105f9e363SConor Dooley
8205f9e363SConor Dooley usleep_range(MPFS_SLEEP_MIN_US, MPFS_SLEEP_MAX_US);
8305f9e363SConor Dooley
8405f9e363SConor Dooley mpfs_deassert(rcdev, id);
8505f9e363SConor Dooley
8605f9e363SConor Dooley return 0;
8705f9e363SConor Dooley }
8805f9e363SConor Dooley
8905f9e363SConor Dooley static const struct reset_control_ops mpfs_reset_ops = {
9005f9e363SConor Dooley .reset = mpfs_reset,
9105f9e363SConor Dooley .assert = mpfs_assert,
9205f9e363SConor Dooley .deassert = mpfs_deassert,
9305f9e363SConor Dooley .status = mpfs_status,
9405f9e363SConor Dooley };
9505f9e363SConor Dooley
mpfs_reset_xlate(struct reset_controller_dev * rcdev,const struct of_phandle_args * reset_spec)9605f9e363SConor Dooley static int mpfs_reset_xlate(struct reset_controller_dev *rcdev,
9705f9e363SConor Dooley const struct of_phandle_args *reset_spec)
9805f9e363SConor Dooley {
9905f9e363SConor Dooley unsigned int index = reset_spec->args[0];
10005f9e363SConor Dooley
10105f9e363SConor Dooley /*
10205f9e363SConor Dooley * CLK_RESERVED does not map to a clock, but it does map to a reset,
10305f9e363SConor Dooley * so it has to be accounted for here. It is the reset for the fabric,
10405f9e363SConor Dooley * so if this reset gets called - do not reset it.
10505f9e363SConor Dooley */
10605f9e363SConor Dooley if (index == CLK_RESERVED) {
10705f9e363SConor Dooley dev_err(rcdev->dev, "Resetting the fabric is not supported\n");
10805f9e363SConor Dooley return -EINVAL;
10905f9e363SConor Dooley }
11005f9e363SConor Dooley
11105f9e363SConor Dooley if (index < MPFS_PERIPH_OFFSET || index >= (MPFS_PERIPH_OFFSET + rcdev->nr_resets)) {
11205f9e363SConor Dooley dev_err(rcdev->dev, "Invalid reset index %u\n", index);
11305f9e363SConor Dooley return -EINVAL;
11405f9e363SConor Dooley }
11505f9e363SConor Dooley
11605f9e363SConor Dooley return index - MPFS_PERIPH_OFFSET;
11705f9e363SConor Dooley }
11805f9e363SConor Dooley
mpfs_reset_probe(struct auxiliary_device * adev,const struct auxiliary_device_id * id)11905f9e363SConor Dooley static int mpfs_reset_probe(struct auxiliary_device *adev,
12005f9e363SConor Dooley const struct auxiliary_device_id *id)
12105f9e363SConor Dooley {
12205f9e363SConor Dooley struct device *dev = &adev->dev;
12305f9e363SConor Dooley struct reset_controller_dev *rcdev;
12405f9e363SConor Dooley
12505f9e363SConor Dooley rcdev = devm_kzalloc(dev, sizeof(*rcdev), GFP_KERNEL);
12605f9e363SConor Dooley if (!rcdev)
12705f9e363SConor Dooley return -ENOMEM;
12805f9e363SConor Dooley
12905f9e363SConor Dooley rcdev->dev = dev;
13005f9e363SConor Dooley rcdev->dev->parent = dev->parent;
13105f9e363SConor Dooley rcdev->ops = &mpfs_reset_ops;
13205f9e363SConor Dooley rcdev->of_node = dev->parent->of_node;
13305f9e363SConor Dooley rcdev->of_reset_n_cells = 1;
13405f9e363SConor Dooley rcdev->of_xlate = mpfs_reset_xlate;
13505f9e363SConor Dooley rcdev->nr_resets = MPFS_NUM_RESETS;
13605f9e363SConor Dooley
13705f9e363SConor Dooley return devm_reset_controller_register(dev, rcdev);
13805f9e363SConor Dooley }
13905f9e363SConor Dooley
14005f9e363SConor Dooley static const struct auxiliary_device_id mpfs_reset_ids[] = {
14105f9e363SConor Dooley {
14205f9e363SConor Dooley .name = "clk_mpfs.reset-mpfs",
14305f9e363SConor Dooley },
14405f9e363SConor Dooley { }
14505f9e363SConor Dooley };
14605f9e363SConor Dooley MODULE_DEVICE_TABLE(auxiliary, mpfs_reset_ids);
14705f9e363SConor Dooley
14805f9e363SConor Dooley static struct auxiliary_driver mpfs_reset_driver = {
14905f9e363SConor Dooley .probe = mpfs_reset_probe,
15005f9e363SConor Dooley .id_table = mpfs_reset_ids,
15105f9e363SConor Dooley };
15205f9e363SConor Dooley
15305f9e363SConor Dooley module_auxiliary_driver(mpfs_reset_driver);
15405f9e363SConor Dooley
15505f9e363SConor Dooley MODULE_DESCRIPTION("Microchip PolarFire SoC Reset Driver");
15605f9e363SConor Dooley MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
15705f9e363SConor Dooley MODULE_IMPORT_NS(MCHP_CLK_MPFS);
158