224 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Hisilicon Hi6220 reset controller driver4 *5 * Copyright (c) 2016 Linaro Limited.6 * Copyright (c) 2015-2016 HiSilicon Limited.7 *8 * Author: Feng Chen <puck.chen@hisilicon.com>9 */10 11#include <linux/io.h>12#include <linux/init.h>13#include <linux/module.h>14#include <linux/bitops.h>15#include <linux/of.h>16#include <linux/regmap.h>17#include <linux/mfd/syscon.h>18#include <linux/reset-controller.h>19#include <linux/reset.h>20#include <linux/platform_device.h>21 22#define PERIPH_ASSERT_OFFSET 0x30023#define PERIPH_DEASSERT_OFFSET 0x30424#define PERIPH_MAX_INDEX 0x50925 26#define SC_MEDIA_RSTEN 0x052C27#define SC_MEDIA_RSTDIS 0x053028#define MEDIA_MAX_INDEX 829 30#define to_reset_data(x) container_of(x, struct hi6220_reset_data, rc_dev)31 32enum hi6220_reset_ctrl_type {33 PERIPHERAL,34 MEDIA,35 AO,36};37 38struct hi6220_reset_data {39 struct reset_controller_dev rc_dev;40 struct regmap *regmap;41};42 43static int hi6220_peripheral_assert(struct reset_controller_dev *rc_dev,44 unsigned long idx)45{46 struct hi6220_reset_data *data = to_reset_data(rc_dev);47 struct regmap *regmap = data->regmap;48 u32 bank = idx >> 8;49 u32 offset = idx & 0xff;50 u32 reg = PERIPH_ASSERT_OFFSET + bank * 0x10;51 52 return regmap_write(regmap, reg, BIT(offset));53}54 55static int hi6220_peripheral_deassert(struct reset_controller_dev *rc_dev,56 unsigned long idx)57{58 struct hi6220_reset_data *data = to_reset_data(rc_dev);59 struct regmap *regmap = data->regmap;60 u32 bank = idx >> 8;61 u32 offset = idx & 0xff;62 u32 reg = PERIPH_DEASSERT_OFFSET + bank * 0x10;63 64 return regmap_write(regmap, reg, BIT(offset));65}66 67static const struct reset_control_ops hi6220_peripheral_reset_ops = {68 .assert = hi6220_peripheral_assert,69 .deassert = hi6220_peripheral_deassert,70};71 72static int hi6220_media_assert(struct reset_controller_dev *rc_dev,73 unsigned long idx)74{75 struct hi6220_reset_data *data = to_reset_data(rc_dev);76 struct regmap *regmap = data->regmap;77 78 return regmap_write(regmap, SC_MEDIA_RSTEN, BIT(idx));79}80 81static int hi6220_media_deassert(struct reset_controller_dev *rc_dev,82 unsigned long idx)83{84 struct hi6220_reset_data *data = to_reset_data(rc_dev);85 struct regmap *regmap = data->regmap;86 87 return regmap_write(regmap, SC_MEDIA_RSTDIS, BIT(idx));88}89 90static const struct reset_control_ops hi6220_media_reset_ops = {91 .assert = hi6220_media_assert,92 .deassert = hi6220_media_deassert,93};94 95#define AO_SCTRL_SC_PW_CLKEN0 0x80096#define AO_SCTRL_SC_PW_CLKDIS0 0x80497 98#define AO_SCTRL_SC_PW_RSTEN0 0x81099#define AO_SCTRL_SC_PW_RSTDIS0 0x814100 101#define AO_SCTRL_SC_PW_ISOEN0 0x820102#define AO_SCTRL_SC_PW_ISODIS0 0x824103#define AO_MAX_INDEX 12104 105static int hi6220_ao_assert(struct reset_controller_dev *rc_dev,106 unsigned long idx)107{108 struct hi6220_reset_data *data = to_reset_data(rc_dev);109 struct regmap *regmap = data->regmap;110 int ret;111 112 ret = regmap_write(regmap, AO_SCTRL_SC_PW_RSTEN0, BIT(idx));113 if (ret)114 return ret;115 116 ret = regmap_write(regmap, AO_SCTRL_SC_PW_ISOEN0, BIT(idx));117 if (ret)118 return ret;119 120 ret = regmap_write(regmap, AO_SCTRL_SC_PW_CLKDIS0, BIT(idx));121 return ret;122}123 124static int hi6220_ao_deassert(struct reset_controller_dev *rc_dev,125 unsigned long idx)126{127 struct hi6220_reset_data *data = to_reset_data(rc_dev);128 struct regmap *regmap = data->regmap;129 int ret;130 131 /*132 * It was suggested to disable isolation before enabling133 * the clocks and deasserting reset, to avoid glitches.134 * But this order is preserved to keep it matching the135 * vendor code.136 */137 ret = regmap_write(regmap, AO_SCTRL_SC_PW_RSTDIS0, BIT(idx));138 if (ret)139 return ret;140 141 ret = regmap_write(regmap, AO_SCTRL_SC_PW_ISODIS0, BIT(idx));142 if (ret)143 return ret;144 145 ret = regmap_write(regmap, AO_SCTRL_SC_PW_CLKEN0, BIT(idx));146 return ret;147}148 149static const struct reset_control_ops hi6220_ao_reset_ops = {150 .assert = hi6220_ao_assert,151 .deassert = hi6220_ao_deassert,152};153 154static int hi6220_reset_probe(struct platform_device *pdev)155{156 struct device_node *np = pdev->dev.of_node;157 struct device *dev = &pdev->dev;158 enum hi6220_reset_ctrl_type type;159 struct hi6220_reset_data *data;160 struct regmap *regmap;161 162 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);163 if (!data)164 return -ENOMEM;165 166 type = (uintptr_t)of_device_get_match_data(dev);167 168 regmap = syscon_node_to_regmap(np);169 if (IS_ERR(regmap)) {170 dev_err(dev, "failed to get reset controller regmap\n");171 return PTR_ERR(regmap);172 }173 174 data->regmap = regmap;175 data->rc_dev.of_node = np;176 if (type == MEDIA) {177 data->rc_dev.ops = &hi6220_media_reset_ops;178 data->rc_dev.nr_resets = MEDIA_MAX_INDEX;179 } else if (type == PERIPHERAL) {180 data->rc_dev.ops = &hi6220_peripheral_reset_ops;181 data->rc_dev.nr_resets = PERIPH_MAX_INDEX;182 } else {183 data->rc_dev.ops = &hi6220_ao_reset_ops;184 data->rc_dev.nr_resets = AO_MAX_INDEX;185 }186 187 return reset_controller_register(&data->rc_dev);188}189 190static const struct of_device_id hi6220_reset_match[] = {191 {192 .compatible = "hisilicon,hi6220-sysctrl",193 .data = (void *)PERIPHERAL,194 },195 {196 .compatible = "hisilicon,hi6220-mediactrl",197 .data = (void *)MEDIA,198 },199 {200 .compatible = "hisilicon,hi6220-aoctrl",201 .data = (void *)AO,202 },203 { /* sentinel */ },204};205MODULE_DEVICE_TABLE(of, hi6220_reset_match);206 207static struct platform_driver hi6220_reset_driver = {208 .probe = hi6220_reset_probe,209 .driver = {210 .name = "reset-hi6220",211 .of_match_table = hi6220_reset_match,212 },213};214 215static int __init hi6220_reset_init(void)216{217 return platform_driver_register(&hi6220_reset_driver);218}219 220postcore_initcall(hi6220_reset_init);221 222MODULE_DESCRIPTION("Hisilicon Hi6220 reset controller driver");223MODULE_LICENSE("GPL v2");224