1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Texas Instrument's System Control Interface (TI-SCI) reset driver 4 * 5 * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/ 6 * Andrew F. Davis <afd@ti.com> 7 */ 8 9 #include <linux/idr.h> 10 #include <linux/module.h> 11 #include <linux/mutex.h> 12 #include <linux/of.h> 13 #include <linux/platform_device.h> 14 #include <linux/reset-controller.h> 15 #include <linux/soc/ti/ti_sci_protocol.h> 16 17 /** 18 * struct ti_sci_reset_control - reset control structure 19 * @dev_id: SoC-specific device identifier 20 * @reset_mask: reset mask to use for toggling reset 21 * @lock: synchronize reset_mask read-modify-writes 22 */ 23 struct ti_sci_reset_control { 24 u32 dev_id; 25 u32 reset_mask; 26 struct mutex lock; 27 }; 28 29 /** 30 * struct ti_sci_reset_data - reset controller information structure 31 * @rcdev: reset controller entity 32 * @dev: reset controller device pointer 33 * @sci: TI SCI handle used for communication with system controller 34 * @idr: idr structure for mapping ids to reset control structures 35 */ 36 struct ti_sci_reset_data { 37 struct reset_controller_dev rcdev; 38 struct device *dev; 39 const struct ti_sci_handle *sci; 40 struct idr idr; 41 }; 42 43 #define to_ti_sci_reset_data(p) \ 44 container_of((p), struct ti_sci_reset_data, rcdev) 45 46 /** 47 * ti_sci_reset_set() - program a device's reset 48 * @rcdev: reset controller entity 49 * @id: ID of the reset to toggle 50 * @assert: boolean flag to indicate assert or deassert 51 * 52 * This is a common internal function used to assert or deassert a device's 53 * reset using the TI SCI protocol. The device's reset is asserted if the 54 * @assert argument is true, or deasserted if @assert argument is false. 55 * The mechanism itself is a read-modify-write procedure, the current device 56 * reset register is read using a TI SCI device operation, the new value is 57 * set or un-set using the reset's mask, and the new reset value written by 58 * using another TI SCI device operation. 59 * 60 * Return: 0 for successful request, else a corresponding error value 61 */ 62 static int ti_sci_reset_set(struct reset_controller_dev *rcdev, 63 unsigned long id, bool assert) 64 { 65 struct ti_sci_reset_data *data = to_ti_sci_reset_data(rcdev); 66 const struct ti_sci_handle *sci = data->sci; 67 const struct ti_sci_dev_ops *dev_ops = &sci->ops.dev_ops; 68 struct ti_sci_reset_control *control; 69 u32 reset_state; 70 int ret; 71 72 control = idr_find(&data->idr, id); 73 if (!control) 74 return -EINVAL; 75 76 mutex_lock(&control->lock); 77 78 ret = dev_ops->get_device_resets(sci, control->dev_id, &reset_state); 79 if (ret) 80 goto out; 81 82 if (assert) 83 reset_state |= control->reset_mask; 84 else 85 reset_state &= ~control->reset_mask; 86 87 ret = dev_ops->set_device_resets(sci, control->dev_id, reset_state); 88 out: 89 mutex_unlock(&control->lock); 90 91 return ret; 92 } 93 94 /** 95 * ti_sci_reset_assert() - assert device reset 96 * @rcdev: reset controller entity 97 * @id: ID of the reset to be asserted 98 * 99 * This function implements the reset driver op to assert a device's reset 100 * using the TI SCI protocol. This invokes the function ti_sci_reset_set() 101 * with the corresponding parameters as passed in, but with the @assert 102 * argument set to true for asserting the reset. 103 * 104 * Return: 0 for successful request, else a corresponding error value 105 */ 106 static int ti_sci_reset_assert(struct reset_controller_dev *rcdev, 107 unsigned long id) 108 { 109 return ti_sci_reset_set(rcdev, id, true); 110 } 111 112 /** 113 * ti_sci_reset_deassert() - deassert device reset 114 * @rcdev: reset controller entity 115 * @id: ID of the reset to be deasserted 116 * 117 * This function implements the reset driver op to deassert a device's reset 118 * using the TI SCI protocol. This invokes the function ti_sci_reset_set() 119 * with the corresponding parameters as passed in, but with the @assert 120 * argument set to false for deasserting the reset. 121 * 122 * Return: 0 for successful request, else a corresponding error value 123 */ 124 static int ti_sci_reset_deassert(struct reset_controller_dev *rcdev, 125 unsigned long id) 126 { 127 return ti_sci_reset_set(rcdev, id, false); 128 } 129 130 /** 131 * ti_sci_reset_status() - check device reset status 132 * @rcdev: reset controller entity 133 * @id: ID of reset to be checked 134 * 135 * This function implements the reset driver op to return the status of a 136 * device's reset using the TI SCI protocol. The reset register value is read 137 * by invoking the TI SCI device operation .get_device_resets(), and the 138 * status of the specific reset is extracted and returned using this reset's 139 * reset mask. 140 * 141 * Return: 0 if reset is deasserted, or a non-zero value if reset is asserted 142 */ 143 static int ti_sci_reset_status(struct reset_controller_dev *rcdev, 144 unsigned long id) 145 { 146 struct ti_sci_reset_data *data = to_ti_sci_reset_data(rcdev); 147 const struct ti_sci_handle *sci = data->sci; 148 const struct ti_sci_dev_ops *dev_ops = &sci->ops.dev_ops; 149 struct ti_sci_reset_control *control; 150 u32 reset_state; 151 int ret; 152 153 control = idr_find(&data->idr, id); 154 if (!control) 155 return -EINVAL; 156 157 ret = dev_ops->get_device_resets(sci, control->dev_id, &reset_state); 158 if (ret) 159 return ret; 160 161 return reset_state & control->reset_mask; 162 } 163 164 static const struct reset_control_ops ti_sci_reset_ops = { 165 .assert = ti_sci_reset_assert, 166 .deassert = ti_sci_reset_deassert, 167 .status = ti_sci_reset_status, 168 }; 169 170 /** 171 * ti_sci_reset_of_xlate() - translate a set of OF arguments to a reset ID 172 * @rcdev: reset controller entity 173 * @reset_spec: OF reset argument specifier 174 * 175 * This function performs the translation of the reset argument specifier 176 * values defined in a reset consumer device node. The function allocates a 177 * reset control structure for that device reset, and will be used by the 178 * driver for performing any reset functions on that reset. An idr structure 179 * is allocated and used to map to the reset control structure. This idr 180 * is used by the driver to do reset lookups. 181 * 182 * Return: 0 for successful request, else a corresponding error value 183 */ 184 static int ti_sci_reset_of_xlate(struct reset_controller_dev *rcdev, 185 const struct of_phandle_args *reset_spec) 186 { 187 struct ti_sci_reset_data *data = to_ti_sci_reset_data(rcdev); 188 struct ti_sci_reset_control *control; 189 190 if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells)) 191 return -EINVAL; 192 193 control = devm_kzalloc(data->dev, sizeof(*control), GFP_KERNEL); 194 if (!control) 195 return -ENOMEM; 196 197 control->dev_id = reset_spec->args[0]; 198 control->reset_mask = reset_spec->args[1]; 199 mutex_init(&control->lock); 200 201 return idr_alloc(&data->idr, control, 0, 0, GFP_KERNEL); 202 } 203 204 static const struct of_device_id ti_sci_reset_of_match[] = { 205 { .compatible = "ti,sci-reset", }, 206 { /* sentinel */ }, 207 }; 208 MODULE_DEVICE_TABLE(of, ti_sci_reset_of_match); 209 210 static int ti_sci_reset_probe(struct platform_device *pdev) 211 { 212 struct ti_sci_reset_data *data; 213 214 if (!pdev->dev.of_node) 215 return -ENODEV; 216 217 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 218 if (!data) 219 return -ENOMEM; 220 221 data->sci = devm_ti_sci_get_handle(&pdev->dev); 222 if (IS_ERR(data->sci)) 223 return PTR_ERR(data->sci); 224 225 data->rcdev.ops = &ti_sci_reset_ops; 226 data->rcdev.owner = THIS_MODULE; 227 data->rcdev.of_node = pdev->dev.of_node; 228 data->rcdev.of_reset_n_cells = 2; 229 data->rcdev.of_xlate = ti_sci_reset_of_xlate; 230 data->dev = &pdev->dev; 231 idr_init(&data->idr); 232 233 platform_set_drvdata(pdev, data); 234 235 return reset_controller_register(&data->rcdev); 236 } 237 238 static int ti_sci_reset_remove(struct platform_device *pdev) 239 { 240 struct ti_sci_reset_data *data = platform_get_drvdata(pdev); 241 242 reset_controller_unregister(&data->rcdev); 243 244 idr_destroy(&data->idr); 245 246 return 0; 247 } 248 249 static struct platform_driver ti_sci_reset_driver = { 250 .probe = ti_sci_reset_probe, 251 .remove = ti_sci_reset_remove, 252 .driver = { 253 .name = "ti-sci-reset", 254 .of_match_table = ti_sci_reset_of_match, 255 }, 256 }; 257 module_platform_driver(ti_sci_reset_driver); 258 259 MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 260 MODULE_DESCRIPTION("TI System Control Interface (TI SCI) Reset driver"); 261 MODULE_LICENSE("GPL v2"); 262