0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/io.h>
0010 #include <linux/init.h>
0011 #include <linux/mod_devicetable.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/reset-controller.h>
0014 #include <linux/reboot.h>
0015
0016
0017 #define BITS_PER_HWM_REG 16
0018
0019
0020 static const u32 sp_resets[] = {
0021
0022 0x00,
0023 0x02,
0024 0x03,
0025 0x04,
0026 0x05,
0027 0x06,
0028 0x07,
0029 0x08,
0030 0x09,
0031 0x0a,
0032 0x0b,
0033 0x0d,
0034 0x0e,
0035 0x0f,
0036 0x10,
0037 0x12,
0038 0x14,
0039 0x15,
0040 0x16,
0041 0x17,
0042 0x18,
0043 0x19,
0044 0x1a,
0045 0x1b,
0046 0x1c,
0047 0x1d,
0048 0x1e,
0049 0x1f,
0050 0x20,
0051 0x21,
0052 0x22,
0053 0x23,
0054 0x24,
0055 0x25,
0056 0x26,
0057 0x2a,
0058 0x2b,
0059 0x2d,
0060 0x2e,
0061 0x30,
0062 0x31,
0063 0x32,
0064 0x33,
0065 0x3d,
0066 0x3e,
0067 0x3f,
0068 0x42,
0069 0x44,
0070 0x4b,
0071 0x4c,
0072 0x4d,
0073 0x4e,
0074 0x4f,
0075 0x50,
0076 0x55,
0077 0x60,
0078 0x61,
0079 0x6a,
0080 0x6f,
0081 0x70,
0082 0x73,
0083 0x74,
0084 0x86,
0085 0x8a,
0086 0x8b,
0087 0x8d,
0088 0x8e,
0089 0x8f,
0090 0x90,
0091 0x92,
0092 0x93,
0093 0x94,
0094 0x95,
0095 0x96,
0096 0x97,
0097 0x98,
0098 0x99,
0099 };
0100
0101 struct sp_reset {
0102 struct reset_controller_dev rcdev;
0103 struct notifier_block notifier;
0104 void __iomem *base;
0105 };
0106
0107 static inline struct sp_reset *to_sp_reset(struct reset_controller_dev *rcdev)
0108 {
0109 return container_of(rcdev, struct sp_reset, rcdev);
0110 }
0111
0112 static int sp_reset_update(struct reset_controller_dev *rcdev,
0113 unsigned long id, bool assert)
0114 {
0115 struct sp_reset *reset = to_sp_reset(rcdev);
0116 int index = sp_resets[id] / BITS_PER_HWM_REG;
0117 int shift = sp_resets[id] % BITS_PER_HWM_REG;
0118 u32 val;
0119
0120 val = (1 << (16 + shift)) | (assert << shift);
0121 writel(val, reset->base + (index * 4));
0122
0123 return 0;
0124 }
0125
0126 static int sp_reset_assert(struct reset_controller_dev *rcdev,
0127 unsigned long id)
0128 {
0129 return sp_reset_update(rcdev, id, true);
0130 }
0131
0132 static int sp_reset_deassert(struct reset_controller_dev *rcdev,
0133 unsigned long id)
0134 {
0135 return sp_reset_update(rcdev, id, false);
0136 }
0137
0138 static int sp_reset_status(struct reset_controller_dev *rcdev,
0139 unsigned long id)
0140 {
0141 struct sp_reset *reset = to_sp_reset(rcdev);
0142 int index = sp_resets[id] / BITS_PER_HWM_REG;
0143 int shift = sp_resets[id] % BITS_PER_HWM_REG;
0144 u32 reg;
0145
0146 reg = readl(reset->base + (index * 4));
0147
0148 return !!(reg & BIT(shift));
0149 }
0150
0151 static const struct reset_control_ops sp_reset_ops = {
0152 .assert = sp_reset_assert,
0153 .deassert = sp_reset_deassert,
0154 .status = sp_reset_status,
0155 };
0156
0157 static int sp_restart(struct notifier_block *nb, unsigned long mode,
0158 void *cmd)
0159 {
0160 struct sp_reset *reset = container_of(nb, struct sp_reset, notifier);
0161
0162 sp_reset_assert(&reset->rcdev, 0);
0163 sp_reset_deassert(&reset->rcdev, 0);
0164
0165 return NOTIFY_DONE;
0166 }
0167
0168 static int sp_reset_probe(struct platform_device *pdev)
0169 {
0170 struct device *dev = &pdev->dev;
0171 struct sp_reset *reset;
0172 struct resource *res;
0173 int ret;
0174
0175 reset = devm_kzalloc(dev, sizeof(*reset), GFP_KERNEL);
0176 if (!reset)
0177 return -ENOMEM;
0178
0179 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0180 reset->base = devm_ioremap_resource(dev, res);
0181 if (IS_ERR(reset->base))
0182 return PTR_ERR(reset->base);
0183
0184 reset->rcdev.ops = &sp_reset_ops;
0185 reset->rcdev.owner = THIS_MODULE;
0186 reset->rcdev.of_node = dev->of_node;
0187 reset->rcdev.nr_resets = resource_size(res) / 4 * BITS_PER_HWM_REG;
0188
0189 ret = devm_reset_controller_register(dev, &reset->rcdev);
0190 if (ret)
0191 return ret;
0192
0193 reset->notifier.notifier_call = sp_restart;
0194 reset->notifier.priority = 192;
0195
0196 return register_restart_handler(&reset->notifier);
0197 }
0198
0199 static const struct of_device_id sp_reset_dt_ids[] = {
0200 {.compatible = "sunplus,sp7021-reset",},
0201 { },
0202 };
0203
0204 static struct platform_driver sp_reset_driver = {
0205 .probe = sp_reset_probe,
0206 .driver = {
0207 .name = "sunplus-reset",
0208 .of_match_table = sp_reset_dt_ids,
0209 .suppress_bind_attrs = true,
0210 },
0211 };
0212 builtin_platform_driver(sp_reset_driver);