212 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)2/*3 * SP7021 reset driver4 *5 * Copyright (C) Sunplus Technology Co., Ltd.6 * All rights reserved.7 */8 9#include <linux/io.h>10#include <linux/init.h>11#include <linux/mod_devicetable.h>12#include <linux/platform_device.h>13#include <linux/reset-controller.h>14#include <linux/reboot.h>15 16/* HIWORD_MASK_REG BITS */17#define BITS_PER_HWM_REG 1618 19/* resets HW info: reg_index_shift */20static const u32 sp_resets[] = {21/* SP7021: mo_reset0 ~ mo_reset9 */22 0x00,23 0x02,24 0x03,25 0x04,26 0x05,27 0x06,28 0x07,29 0x08,30 0x09,31 0x0a,32 0x0b,33 0x0d,34 0x0e,35 0x0f,36 0x10,37 0x12,38 0x14,39 0x15,40 0x16,41 0x17,42 0x18,43 0x19,44 0x1a,45 0x1b,46 0x1c,47 0x1d,48 0x1e,49 0x1f,50 0x20,51 0x21,52 0x22,53 0x23,54 0x24,55 0x25,56 0x26,57 0x2a,58 0x2b,59 0x2d,60 0x2e,61 0x30,62 0x31,63 0x32,64 0x33,65 0x3d,66 0x3e,67 0x3f,68 0x42,69 0x44,70 0x4b,71 0x4c,72 0x4d,73 0x4e,74 0x4f,75 0x50,76 0x55,77 0x60,78 0x61,79 0x6a,80 0x6f,81 0x70,82 0x73,83 0x74,84 0x86,85 0x8a,86 0x8b,87 0x8d,88 0x8e,89 0x8f,90 0x90,91 0x92,92 0x93,93 0x94,94 0x95,95 0x96,96 0x97,97 0x98,98 0x99,99};100 101struct sp_reset {102 struct reset_controller_dev rcdev;103 struct notifier_block notifier;104 void __iomem *base;105};106 107static inline struct sp_reset *to_sp_reset(struct reset_controller_dev *rcdev)108{109 return container_of(rcdev, struct sp_reset, rcdev);110}111 112static int sp_reset_update(struct reset_controller_dev *rcdev,113 unsigned long id, bool assert)114{115 struct sp_reset *reset = to_sp_reset(rcdev);116 int index = sp_resets[id] / BITS_PER_HWM_REG;117 int shift = sp_resets[id] % BITS_PER_HWM_REG;118 u32 val;119 120 val = (1 << (16 + shift)) | (assert << shift);121 writel(val, reset->base + (index * 4));122 123 return 0;124}125 126static int sp_reset_assert(struct reset_controller_dev *rcdev,127 unsigned long id)128{129 return sp_reset_update(rcdev, id, true);130}131 132static int sp_reset_deassert(struct reset_controller_dev *rcdev,133 unsigned long id)134{135 return sp_reset_update(rcdev, id, false);136}137 138static int sp_reset_status(struct reset_controller_dev *rcdev,139 unsigned long id)140{141 struct sp_reset *reset = to_sp_reset(rcdev);142 int index = sp_resets[id] / BITS_PER_HWM_REG;143 int shift = sp_resets[id] % BITS_PER_HWM_REG;144 u32 reg;145 146 reg = readl(reset->base + (index * 4));147 148 return !!(reg & BIT(shift));149}150 151static const struct reset_control_ops sp_reset_ops = {152 .assert = sp_reset_assert,153 .deassert = sp_reset_deassert,154 .status = sp_reset_status,155};156 157static int sp_restart(struct notifier_block *nb, unsigned long mode,158 void *cmd)159{160 struct sp_reset *reset = container_of(nb, struct sp_reset, notifier);161 162 sp_reset_assert(&reset->rcdev, 0);163 sp_reset_deassert(&reset->rcdev, 0);164 165 return NOTIFY_DONE;166}167 168static int sp_reset_probe(struct platform_device *pdev)169{170 struct device *dev = &pdev->dev;171 struct sp_reset *reset;172 struct resource *res;173 int ret;174 175 reset = devm_kzalloc(dev, sizeof(*reset), GFP_KERNEL);176 if (!reset)177 return -ENOMEM;178 179 reset->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);180 if (IS_ERR(reset->base))181 return PTR_ERR(reset->base);182 183 reset->rcdev.ops = &sp_reset_ops;184 reset->rcdev.owner = THIS_MODULE;185 reset->rcdev.of_node = dev->of_node;186 reset->rcdev.nr_resets = resource_size(res) / 4 * BITS_PER_HWM_REG;187 188 ret = devm_reset_controller_register(dev, &reset->rcdev);189 if (ret)190 return ret;191 192 reset->notifier.notifier_call = sp_restart;193 reset->notifier.priority = 192;194 195 return register_restart_handler(&reset->notifier);196}197 198static const struct of_device_id sp_reset_dt_ids[] = {199 {.compatible = "sunplus,sp7021-reset",},200 { /* sentinel */ },201};202 203static struct platform_driver sp_reset_driver = {204 .probe = sp_reset_probe,205 .driver = {206 .name = "sunplus-reset",207 .of_match_table = sp_reset_dt_ids,208 .suppress_bind_attrs = true,209 },210};211builtin_platform_driver(sp_reset_driver);212