| 1 | |
| 2 | |
| 3 | * PolarFire SoC (MPFS) Peripheral Clock Reset Controller
|
| 4 | *
|
| 5 | * Author: Conor Dooley <[email protected]>
|
| 6 | * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
|
| 7 | *
|
| 8 |
|
| 9 | #include <linux/auxiliary_bus.h>
|
| 10 | #include <linux/delay.h>
|
| 11 | #include <linux/io.h>
|
| 12 | #include <linux/mfd/syscon.h>
|
| 13 | #include <linux/module.h>
|
| 14 | #include <linux/of.h>
|
| 15 | #include <linux/platform_device.h>
|
| 16 | #include <linux/regmap.h>
|
| 17 | #include <linux/reset-controller.h>
|
| 18 | #include <linux/slab.h>
|
| 19 | #include <dt-bindings/clock/microchip,mpfs-clock.h>
|
| 20 | #include <soc/microchip/mpfs.h>
|
| 21 |
|
| 22 | |
| 23 | * The ENVM reset is the lowest bit in the register & I am using the CLK_FOO
|
| 24 | * defines in the dt to make things easier to configure - so this is accounting
|
| 25 | * for the offset of 3 there.
|
| 26 |
|
| 27 | #define MPFS_PERIPH_OFFSET CLK_ENVM
|
| 28 | #define MPFS_NUM_RESETS 30u
|
| 29 | #define MPFS_SLEEP_MIN_US 100
|
| 30 | #define MPFS_SLEEP_MAX_US 200
|
| 31 |
|
| 32 | #define REG_SUBBLK_RESET_CR 0x88u
|
| 33 |
|
| 34 | struct mpfs_reset {
|
| 35 | struct regmap *regmap;
|
| 36 | struct reset_controller_dev rcdev;
|
| 37 | };
|
| 38 |
|
| 39 | static inline struct mpfs_reset *to_mpfs_reset(struct reset_controller_dev *rcdev)
|
| 40 | {
|
| 41 | return container_of(rcdev, struct mpfs_reset, rcdev);
|
| 42 | }
|
| 43 |
|
| 44 | |
| 45 | * Peripheral clock resets
|
| 46 |
|
| 47 | static int mpfs_assert(struct reset_controller_dev *rcdev, unsigned long id)
|
| 48 | {
|
| 49 | struct mpfs_reset *rst = to_mpfs_reset(rcdev);
|
| 50 |
|
| 51 | return regmap_set_bits(rst->regmap, REG_SUBBLK_RESET_CR, BIT(id));
|
| 52 |
|
| 53 | }
|
| 54 |
|
| 55 | static int mpfs_deassert(struct reset_controller_dev *rcdev, unsigned long id)
|
| 56 | {
|
| 57 | struct mpfs_reset *rst = to_mpfs_reset(rcdev);
|
| 58 |
|
| 59 | return regmap_clear_bits(rst->regmap, REG_SUBBLK_RESET_CR, BIT(id));
|
| 60 |
|
| 61 | }
|
| 62 |
|
| 63 | static int mpfs_status(struct reset_controller_dev *rcdev, unsigned long id)
|
| 64 | {
|
| 65 | struct mpfs_reset *rst = to_mpfs_reset(rcdev);
|
| 66 | u32 reg;
|
| 67 |
|
| 68 | regmap_read(rst->regmap, REG_SUBBLK_RESET_CR, ®);
|
| 69 |
|
| 70 | |
| 71 | * It is safe to return here as MPFS_NUM_RESETS makes sure the sign bit
|
| 72 | * is never hit.
|
| 73 |
|
| 74 | return (reg & BIT(id));
|
| 75 | }
|
| 76 |
|
| 77 | static int mpfs_reset(struct reset_controller_dev *rcdev, unsigned long id)
|
| 78 | {
|
| 79 | mpfs_assert(rcdev, id);
|
| 80 |
|
| 81 | usleep_range(MPFS_SLEEP_MIN_US, MPFS_SLEEP_MAX_US);
|
| 82 |
|
| 83 | mpfs_deassert(rcdev, id);
|
| 84 |
|
| 85 | return 0;
|
| 86 | }
|
| 87 |
|
| 88 | static const struct reset_control_ops mpfs_reset_ops = {
|
| 89 | .reset = mpfs_reset,
|
| 90 | .assert = mpfs_assert,
|
| 91 | .deassert = mpfs_deassert,
|
| 92 | .status = mpfs_status,
|
| 93 | };
|
| 94 |
|
| 95 | static int mpfs_reset_xlate(struct reset_controller_dev *rcdev,
|
| 96 | const struct of_phandle_args *reset_spec)
|
| 97 | {
|
| 98 | unsigned int index = reset_spec->args[0];
|
| 99 |
|
| 100 | |
| 101 | * CLK_RESERVED does not map to a clock, but it does map to a reset,
|
| 102 | * so it has to be accounted for here. It is the reset for the fabric,
|
| 103 | * so if this reset gets called - do not reset it.
|
| 104 |
|
| 105 | if (index == CLK_RESERVED) {
|
| 106 | dev_err(rcdev->dev, "Resetting the fabric is not supported\n");
|
| 107 | return -EINVAL;
|
| 108 | }
|
| 109 |
|
| 110 | if (index < MPFS_PERIPH_OFFSET || index >= (MPFS_PERIPH_OFFSET + rcdev->nr_resets)) {
|
| 111 | dev_err(rcdev->dev, "Invalid reset index %u\n", index);
|
| 112 | return -EINVAL;
|
| 113 | }
|
| 114 |
|
| 115 | return index - MPFS_PERIPH_OFFSET;
|
| 116 | }
|
| 117 |
|
| 118 | static int mpfs_reset_mfd_probe(struct platform_device *pdev)
|
| 119 | {
|
| 120 | struct reset_controller_dev *rcdev;
|
| 121 | struct device *dev = &pdev->dev;
|
| 122 | struct mpfs_reset *rst;
|
| 123 |
|
| 124 | rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL);
|
| 125 | if (!rst)
|
| 126 | return -ENOMEM;
|
| 127 |
|
| 128 | rcdev = &rst->rcdev;
|
| 129 | rcdev->dev = dev;
|
| 130 | rcdev->ops = &mpfs_reset_ops;
|
| 131 |
|
| 132 | rcdev->of_node = pdev->dev.parent->of_node;
|
| 133 | rcdev->of_reset_n_cells = 1;
|
| 134 | rcdev->of_xlate = mpfs_reset_xlate;
|
| 135 | rcdev->nr_resets = MPFS_NUM_RESETS;
|
| 136 |
|
| 137 | rst->regmap = device_node_to_regmap(pdev->dev.parent->of_node);
|
| 138 | if (IS_ERR(rst->regmap))
|
| 139 | return dev_err_probe(dev, PTR_ERR(rst->regmap),
|
| 140 | "Failed to find syscon regmap\n");
|
| 141 |
|
| 142 | return devm_reset_controller_register(dev, rcdev);
|
| 143 | }
|
| 144 |
|
| 145 | static struct platform_driver mpfs_reset_mfd_driver = {
|
| 146 | .probe = mpfs_reset_mfd_probe,
|
| 147 | .driver = {
|
| 148 | .name = "mpfs-reset",
|
| 149 | },
|
| 150 | };
|
| 151 | module_platform_driver(mpfs_reset_mfd_driver);
|
| 152 |
|
| 153 | static int mpfs_reset_adev_probe(struct auxiliary_device *adev,
|
| 154 | const struct auxiliary_device_id *id)
|
| 155 | {
|
| 156 | struct reset_controller_dev *rcdev;
|
| 157 | struct device *dev = &adev->dev;
|
| 158 | struct mpfs_reset *rst;
|
| 159 |
|
| 160 | rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL);
|
| 161 | if (!rst)
|
| 162 | return -ENOMEM;
|
| 163 |
|
| 164 | rst->regmap = (struct regmap *)adev->dev.platform_data;
|
| 165 |
|
| 166 | rcdev = &rst->rcdev;
|
| 167 | rcdev->dev = dev;
|
| 168 | rcdev->ops = &mpfs_reset_ops;
|
| 169 |
|
| 170 | rcdev->of_node = dev->parent->of_node;
|
| 171 | rcdev->of_reset_n_cells = 1;
|
| 172 | rcdev->of_xlate = mpfs_reset_xlate;
|
| 173 | rcdev->nr_resets = MPFS_NUM_RESETS;
|
| 174 |
|
| 175 | return devm_reset_controller_register(dev, rcdev);
|
| 176 | }
|
| 177 |
|
| 178 | int mpfs_reset_controller_register(struct device *clk_dev, struct regmap *map)
|
| 179 | {
|
| 180 | struct auxiliary_device *adev;
|
| 181 |
|
| 182 | adev = devm_auxiliary_device_create(clk_dev, "reset-mpfs", (void *)map);
|
| 183 | if (!adev)
|
| 184 | return -ENODEV;
|
| 185 |
|
| 186 | return 0;
|
| 187 | }
|
| 188 | EXPORT_SYMBOL_NS_GPL(mpfs_reset_controller_register, "MCHP_CLK_MPFS");
|
| 189 |
|
| 190 | static const struct auxiliary_device_id mpfs_reset_ids[] = {
|
| 191 | {
|
| 192 | .name = "reset_mpfs.reset-mpfs",
|
| 193 | },
|
| 194 | { }
|
| 195 | };
|
| 196 | MODULE_DEVICE_TABLE(auxiliary, mpfs_reset_ids);
|
| 197 |
|
| 198 | static struct auxiliary_driver mpfs_reset_aux_driver = {
|
| 199 | .probe = mpfs_reset_adev_probe,
|
| 200 | .id_table = mpfs_reset_ids,
|
| 201 | };
|
| 202 |
|
| 203 | module_auxiliary_driver(mpfs_reset_aux_driver);
|
| 204 |
|
| 205 | MODULE_DESCRIPTION("Microchip PolarFire SoC Reset Driver");
|
| 206 | MODULE_AUTHOR("Conor Dooley <[email protected]>");
|
| 207 | MODULE_IMPORT_NS("MCHP_CLK_MPFS");
|