1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Texas Instruments System Control Interface (TI SCI) reset driver 4 * 5 * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ 6 * Andreas Dannenberg <dannenberg@ti.com> 7 * 8 * Loosely based on Linux kernel reset-ti-sci.c... 9 */ 10 11#include <dm.h> 12#include <errno.h> 13#include <log.h> 14#include <malloc.h> 15#include <reset-uclass.h> 16#include <dm/device_compat.h> 17#include <linux/err.h> 18#include <linux/soc/ti/ti_sci_protocol.h> 19 20/** 21 * struct ti_sci_reset_data - reset controller information structure 22 * @sci: TI SCI handle used for communication with system controller 23 */ 24struct ti_sci_reset_data { 25 const struct ti_sci_handle *sci; 26}; 27 28static int ti_sci_reset_probe(struct udevice *dev) 29{ 30 struct ti_sci_reset_data *data = dev_get_priv(dev); 31 32 debug("%s(dev=%p)\n", __func__, dev); 33 34 if (!data) 35 return -ENOMEM; 36 37 /* Store handle for communication with the system controller */ 38 data->sci = ti_sci_get_handle(dev); 39 if (IS_ERR(data->sci)) 40 return PTR_ERR(data->sci); 41 42 return 0; 43} 44 45static int ti_sci_reset_of_xlate(struct reset_ctl *rst, 46 struct ofnode_phandle_args *args) 47{ 48 debug("%s(rst=%p, args_count=%d)\n", __func__, rst, args->args_count); 49 50 if (args->args_count != 2) { 51 debug("Invalid args_count: %d\n", args->args_count); 52 return -EINVAL; 53 } 54 55 /* 56 * On TI SCI-based devices, the reset provider id field is used as a 57 * device ID, and the data field is used as the associated reset mask. 58 */ 59 rst->id = args->args[0]; 60 rst->data = args->args[1]; 61 62 return 0; 63} 64 65/** 66 * ti_sci_reset_set() - program a device's reset 67 * @rst: Handle to a single reset signal 68 * @assert: boolean flag to indicate assert or deassert 69 * 70 * This is a common internal function used to assert or deassert a device's 71 * reset using the TI SCI protocol. The device's reset is asserted if the 72 * @assert argument is true, or deasserted if @assert argument is false. 73 * The mechanism itself is a read-modify-write procedure, the current device 74 * reset register is read using a TI SCI device operation, the new value is 75 * set or un-set using the reset's mask, and the new reset value written by 76 * using another TI SCI device operation. 77 * 78 * Return: 0 for successful request, else a corresponding error value 79 */ 80static int ti_sci_reset_set(struct reset_ctl *rst, bool assert) 81{ 82 struct ti_sci_reset_data *data = dev_get_priv(rst->dev); 83 const struct ti_sci_handle *sci = data->sci; 84 const struct ti_sci_dev_ops *dops = &sci->ops.dev_ops; 85 u32 reset_state; 86 int ret; 87 88 ret = dops->get_device_resets(sci, rst->id, &reset_state); 89 if (ret) { 90 dev_err(rst->dev, "%s: get_device_resets failed (%d)\n", 91 __func__, ret); 92 return ret; 93 } 94 95 if (assert) 96 reset_state |= rst->data; 97 else 98 reset_state &= ~rst->data; 99 100 ret = dops->set_device_resets(sci, rst->id, reset_state); 101 if (ret) { 102 dev_err(rst->dev, "%s: set_device_resets failed (%d)\n", 103 __func__, ret); 104 return ret; 105 } 106 107 return 0; 108} 109 110/** 111 * ti_sci_reset_assert() - assert device reset 112 * @rst: Handle to a single reset signal 113 * 114 * This function implements the reset driver op to assert a device's reset 115 * using the TI SCI protocol. This invokes the function ti_sci_reset_set() 116 * with the corresponding parameters as passed in, but with the @assert 117 * argument set to true for asserting the reset. 118 * 119 * Return: 0 for successful request, else a corresponding error value 120 */ 121static int ti_sci_reset_assert(struct reset_ctl *rst) 122{ 123 debug("%s(rst=%p)\n", __func__, rst); 124 return ti_sci_reset_set(rst, true); 125} 126 127/** 128 * ti_sci_reset_deassert() - deassert device reset 129 * @rst: Handle to a single reset signal 130 * 131 * This function implements the reset driver op to deassert a device's reset 132 * using the TI SCI protocol. This invokes the function ti_sci_reset_set() 133 * with the corresponding parameters as passed in, but with the @assert 134 * argument set to false for deasserting the reset. 135 * 136 * Return: 0 for successful request, else a corresponding error value 137 */ 138static int ti_sci_reset_deassert(struct reset_ctl *rst) 139{ 140 debug("%s(rst=%p)\n", __func__, rst); 141 return ti_sci_reset_set(rst, false); 142} 143 144/** 145 * ti_sci_reset_status() - check device reset status 146 * @rst: Handle to a single reset signal 147 * 148 * This function implements the reset driver op to return the status of a 149 * device's reset using the TI SCI protocol. The reset register value is read 150 * by invoking the TI SCI device operation .get_device_resets(), and the 151 * status of the specific reset is extracted and returned using this reset's 152 * reset mask. 153 * 154 * Return: 0 if reset is deasserted, or a non-zero value if reset is asserted 155 */ 156static int ti_sci_reset_status(struct reset_ctl *rst) 157{ 158 struct ti_sci_reset_data *data = dev_get_priv(rst->dev); 159 const struct ti_sci_handle *sci = data->sci; 160 const struct ti_sci_dev_ops *dops = &sci->ops.dev_ops; 161 u32 reset_state; 162 int ret; 163 164 debug("%s(rst=%p)\n", __func__, rst); 165 166 ret = dops->get_device_resets(sci, rst->id, &reset_state); 167 if (ret) { 168 dev_err(rst->dev, "%s: get_device_resets failed (%d)\n", 169 __func__, ret); 170 return ret; 171 } 172 173 return reset_state & rst->data; 174} 175 176static const struct udevice_id ti_sci_reset_of_match[] = { 177 { .compatible = "ti,sci-reset", }, 178 { /* sentinel */ }, 179}; 180 181static struct reset_ops ti_sci_reset_ops = { 182 .of_xlate = ti_sci_reset_of_xlate, 183 .rst_assert = ti_sci_reset_assert, 184 .rst_deassert = ti_sci_reset_deassert, 185 .rst_status = ti_sci_reset_status, 186}; 187 188U_BOOT_DRIVER(ti_sci_reset) = { 189 .name = "ti-sci-reset", 190 .id = UCLASS_RESET, 191 .of_match = ti_sci_reset_of_match, 192 .probe = ti_sci_reset_probe, 193 .priv_auto = sizeof(struct ti_sci_reset_data), 194 .ops = &ti_sci_reset_ops, 195}; 196