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