1 /* 2 * Hisilicon Hi6220 reset controller driver 3 * 4 * Copyright (c) 2016 Linaro Limited. 5 * Copyright (c) 2015-2016 Hisilicon Limited. 6 * 7 * Author: Feng Chen <puck.chen@hisilicon.com> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13 14 #include <linux/io.h> 15 #include <linux/init.h> 16 #include <linux/module.h> 17 #include <linux/bitops.h> 18 #include <linux/of.h> 19 #include <linux/of_device.h> 20 #include <linux/regmap.h> 21 #include <linux/mfd/syscon.h> 22 #include <linux/reset-controller.h> 23 #include <linux/reset.h> 24 #include <linux/platform_device.h> 25 26 #define PERIPH_ASSERT_OFFSET 0x300 27 #define PERIPH_DEASSERT_OFFSET 0x304 28 #define PERIPH_MAX_INDEX 0x509 29 30 #define SC_MEDIA_RSTEN 0x052C 31 #define SC_MEDIA_RSTDIS 0x0530 32 #define MEDIA_MAX_INDEX 8 33 34 #define to_reset_data(x) container_of(x, struct hi6220_reset_data, rc_dev) 35 36 enum hi6220_reset_ctrl_type { 37 PERIPHERAL, 38 MEDIA, 39 }; 40 41 struct hi6220_reset_data { 42 struct reset_controller_dev rc_dev; 43 struct regmap *regmap; 44 }; 45 46 static int hi6220_peripheral_assert(struct reset_controller_dev *rc_dev, 47 unsigned long idx) 48 { 49 struct hi6220_reset_data *data = to_reset_data(rc_dev); 50 struct regmap *regmap = data->regmap; 51 u32 bank = idx >> 8; 52 u32 offset = idx & 0xff; 53 u32 reg = PERIPH_ASSERT_OFFSET + bank * 0x10; 54 55 return regmap_write(regmap, reg, BIT(offset)); 56 } 57 58 static int hi6220_peripheral_deassert(struct reset_controller_dev *rc_dev, 59 unsigned long idx) 60 { 61 struct hi6220_reset_data *data = to_reset_data(rc_dev); 62 struct regmap *regmap = data->regmap; 63 u32 bank = idx >> 8; 64 u32 offset = idx & 0xff; 65 u32 reg = PERIPH_DEASSERT_OFFSET + bank * 0x10; 66 67 return regmap_write(regmap, reg, BIT(offset)); 68 } 69 70 static const struct reset_control_ops hi6220_peripheral_reset_ops = { 71 .assert = hi6220_peripheral_assert, 72 .deassert = hi6220_peripheral_deassert, 73 }; 74 75 static int hi6220_media_assert(struct reset_controller_dev *rc_dev, 76 unsigned long idx) 77 { 78 struct hi6220_reset_data *data = to_reset_data(rc_dev); 79 struct regmap *regmap = data->regmap; 80 81 return regmap_write(regmap, SC_MEDIA_RSTEN, BIT(idx)); 82 } 83 84 static int hi6220_media_deassert(struct reset_controller_dev *rc_dev, 85 unsigned long idx) 86 { 87 struct hi6220_reset_data *data = to_reset_data(rc_dev); 88 struct regmap *regmap = data->regmap; 89 90 return regmap_write(regmap, SC_MEDIA_RSTDIS, BIT(idx)); 91 } 92 93 static const struct reset_control_ops hi6220_media_reset_ops = { 94 .assert = hi6220_media_assert, 95 .deassert = hi6220_media_deassert, 96 }; 97 98 static int hi6220_reset_probe(struct platform_device *pdev) 99 { 100 struct device_node *np = pdev->dev.of_node; 101 struct device *dev = &pdev->dev; 102 enum hi6220_reset_ctrl_type type; 103 struct hi6220_reset_data *data; 104 struct regmap *regmap; 105 106 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 107 if (!data) 108 return -ENOMEM; 109 110 type = (enum hi6220_reset_ctrl_type)of_device_get_match_data(dev); 111 112 regmap = syscon_node_to_regmap(np); 113 if (IS_ERR(regmap)) { 114 dev_err(dev, "failed to get reset controller regmap\n"); 115 return PTR_ERR(regmap); 116 } 117 118 data->regmap = regmap; 119 data->rc_dev.of_node = np; 120 if (type == MEDIA) { 121 data->rc_dev.ops = &hi6220_media_reset_ops; 122 data->rc_dev.nr_resets = MEDIA_MAX_INDEX; 123 } else { 124 data->rc_dev.ops = &hi6220_peripheral_reset_ops; 125 data->rc_dev.nr_resets = PERIPH_MAX_INDEX; 126 } 127 128 return reset_controller_register(&data->rc_dev); 129 } 130 131 static const struct of_device_id hi6220_reset_match[] = { 132 { 133 .compatible = "hisilicon,hi6220-sysctrl", 134 .data = (void *)PERIPHERAL, 135 }, 136 { 137 .compatible = "hisilicon,hi6220-mediactrl", 138 .data = (void *)MEDIA, 139 }, 140 { /* sentinel */ }, 141 }; 142 MODULE_DEVICE_TABLE(of, hi6220_reset_match); 143 144 static struct platform_driver hi6220_reset_driver = { 145 .probe = hi6220_reset_probe, 146 .driver = { 147 .name = "reset-hi6220", 148 .of_match_table = hi6220_reset_match, 149 }, 150 }; 151 152 static int __init hi6220_reset_init(void) 153 { 154 return platform_driver_register(&hi6220_reset_driver); 155 } 156 157 postcore_initcall(hi6220_reset_init); 158