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