1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * TI SYSCON regmap reset driver 4 * 5 * Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/ 6 * Andrew F. Davis <afd@ti.com> 7 * Suman Anna <afd@ti.com> 8 */ 9 10 #include <linux/mfd/syscon.h> 11 #include <linux/module.h> 12 #include <linux/of.h> 13 #include <linux/platform_device.h> 14 #include <linux/regmap.h> 15 #include <linux/reset-controller.h> 16 17 #include <dt-bindings/reset/ti-syscon.h> 18 19 /** 20 * struct ti_syscon_reset_control - reset control structure 21 * @assert_offset: reset assert control register offset from syscon base 22 * @assert_bit: reset assert bit in the reset assert control register 23 * @deassert_offset: reset deassert control register offset from syscon base 24 * @deassert_bit: reset deassert bit in the reset deassert control register 25 * @status_offset: reset status register offset from syscon base 26 * @status_bit: reset status bit in the reset status register 27 * @flags: reset flag indicating how the (de)assert and status are handled 28 */ 29 struct ti_syscon_reset_control { 30 unsigned int assert_offset; 31 unsigned int assert_bit; 32 unsigned int deassert_offset; 33 unsigned int deassert_bit; 34 unsigned int status_offset; 35 unsigned int status_bit; 36 u32 flags; 37 }; 38 39 /** 40 * struct ti_syscon_reset_data - reset controller information structure 41 * @rcdev: reset controller entity 42 * @regmap: regmap handle containing the memory-mapped reset registers 43 * @controls: array of reset controls 44 * @nr_controls: number of controls in control array 45 */ 46 struct ti_syscon_reset_data { 47 struct reset_controller_dev rcdev; 48 struct regmap *regmap; 49 struct ti_syscon_reset_control *controls; 50 unsigned int nr_controls; 51 }; 52 53 #define to_ti_syscon_reset_data(_rcdev) \ 54 container_of(_rcdev, struct ti_syscon_reset_data, rcdev) 55 56 /** 57 * ti_syscon_reset_assert() - assert device reset 58 * @rcdev: reset controller entity 59 * @id: ID of the reset to be asserted 60 * 61 * This function implements the reset driver op to assert a device's reset. 62 * This asserts the reset in a manner prescribed by the reset flags. 63 * 64 * Return: 0 for successful request, else a corresponding error value 65 */ 66 static int ti_syscon_reset_assert(struct reset_controller_dev *rcdev, 67 unsigned long id) 68 { 69 struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev); 70 struct ti_syscon_reset_control *control; 71 unsigned int mask, value; 72 73 if (id >= data->nr_controls) 74 return -EINVAL; 75 76 control = &data->controls[id]; 77 78 if (control->flags & ASSERT_NONE) 79 return -ENOTSUPP; /* assert not supported for this reset */ 80 81 mask = BIT(control->assert_bit); 82 value = (control->flags & ASSERT_SET) ? mask : 0x0; 83 84 return regmap_write_bits(data->regmap, control->assert_offset, mask, value); 85 } 86 87 /** 88 * ti_syscon_reset_deassert() - deassert device reset 89 * @rcdev: reset controller entity 90 * @id: ID of reset to be deasserted 91 * 92 * This function implements the reset driver op to deassert a device's reset. 93 * This deasserts the reset in a manner prescribed by the reset flags. 94 * 95 * Return: 0 for successful request, else a corresponding error value 96 */ 97 static int ti_syscon_reset_deassert(struct reset_controller_dev *rcdev, 98 unsigned long id) 99 { 100 struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev); 101 struct ti_syscon_reset_control *control; 102 unsigned int mask, value; 103 104 if (id >= data->nr_controls) 105 return -EINVAL; 106 107 control = &data->controls[id]; 108 109 if (control->flags & DEASSERT_NONE) 110 return -ENOTSUPP; /* deassert not supported for this reset */ 111 112 mask = BIT(control->deassert_bit); 113 value = (control->flags & DEASSERT_SET) ? mask : 0x0; 114 115 return regmap_write_bits(data->regmap, control->deassert_offset, mask, value); 116 } 117 118 /** 119 * ti_syscon_reset_status() - check device reset status 120 * @rcdev: reset controller entity 121 * @id: ID of the reset for which the status is being requested 122 * 123 * This function implements the reset driver op to return the status of a 124 * device's reset. 125 * 126 * Return: 0 if reset is deasserted, true if reset is asserted, else a 127 * corresponding error value 128 */ 129 static int ti_syscon_reset_status(struct reset_controller_dev *rcdev, 130 unsigned long id) 131 { 132 struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev); 133 struct ti_syscon_reset_control *control; 134 unsigned int reset_state; 135 int ret; 136 137 if (id >= data->nr_controls) 138 return -EINVAL; 139 140 control = &data->controls[id]; 141 142 if (control->flags & STATUS_NONE) 143 return -ENOTSUPP; /* status not supported for this reset */ 144 145 ret = regmap_read(data->regmap, control->status_offset, &reset_state); 146 if (ret) 147 return ret; 148 149 return !(reset_state & BIT(control->status_bit)) == 150 !(control->flags & STATUS_SET); 151 } 152 153 static const struct reset_control_ops ti_syscon_reset_ops = { 154 .assert = ti_syscon_reset_assert, 155 .deassert = ti_syscon_reset_deassert, 156 .status = ti_syscon_reset_status, 157 }; 158 159 static int ti_syscon_reset_probe(struct platform_device *pdev) 160 { 161 struct device *dev = &pdev->dev; 162 struct device_node *np = dev->of_node; 163 struct ti_syscon_reset_data *data; 164 struct regmap *regmap; 165 const __be32 *list; 166 struct ti_syscon_reset_control *controls; 167 int size, nr_controls, i; 168 169 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 170 if (!data) 171 return -ENOMEM; 172 173 regmap = syscon_node_to_regmap(np->parent); 174 if (IS_ERR(regmap)) 175 return PTR_ERR(regmap); 176 177 list = of_get_property(np, "ti,reset-bits", &size); 178 if (!list || (size / sizeof(*list)) % 7 != 0) { 179 dev_err(dev, "invalid DT reset description\n"); 180 return -EINVAL; 181 } 182 183 nr_controls = (size / sizeof(*list)) / 7; 184 controls = devm_kcalloc(dev, nr_controls, sizeof(*controls), 185 GFP_KERNEL); 186 if (!controls) 187 return -ENOMEM; 188 189 for (i = 0; i < nr_controls; i++) { 190 controls[i].assert_offset = be32_to_cpup(list++); 191 controls[i].assert_bit = be32_to_cpup(list++); 192 controls[i].deassert_offset = be32_to_cpup(list++); 193 controls[i].deassert_bit = be32_to_cpup(list++); 194 controls[i].status_offset = be32_to_cpup(list++); 195 controls[i].status_bit = be32_to_cpup(list++); 196 controls[i].flags = be32_to_cpup(list++); 197 } 198 199 data->rcdev.ops = &ti_syscon_reset_ops; 200 data->rcdev.owner = THIS_MODULE; 201 data->rcdev.of_node = np; 202 data->rcdev.nr_resets = nr_controls; 203 data->regmap = regmap; 204 data->controls = controls; 205 data->nr_controls = nr_controls; 206 207 return devm_reset_controller_register(dev, &data->rcdev); 208 } 209 210 static const struct of_device_id ti_syscon_reset_of_match[] = { 211 { .compatible = "ti,syscon-reset", }, 212 { /* sentinel */ }, 213 }; 214 MODULE_DEVICE_TABLE(of, ti_syscon_reset_of_match); 215 216 static struct platform_driver ti_syscon_reset_driver = { 217 .probe = ti_syscon_reset_probe, 218 .driver = { 219 .name = "ti-syscon-reset", 220 .of_match_table = ti_syscon_reset_of_match, 221 }, 222 }; 223 module_platform_driver(ti_syscon_reset_driver); 224 225 MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 226 MODULE_AUTHOR("Suman Anna <s-anna@ti.com>"); 227 MODULE_DESCRIPTION("TI SYSCON Regmap Reset Driver"); 228 MODULE_LICENSE("GPL v2"); 229