Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drivers: reset: STi SoC system configuration reset controller support

This patch adds a reset controller implementation for STMicroelectronics
STi family SoCs; it allows a group of related reset like controls found
in multiple system configuration registers to be represented by a single
controller device. System configuration registers are accessed through
the regmap framework and the mfd/syscon driver.

The implementation optionally supports waiting for the reset action to
be acknowledged in a separate status register and supports both
active high and active low reset lines. These properties are common across
all the reset channels in a specific reset controller instance, hence
all channels in a paritcular controller are expected to behave in the
same way.

Signed-off-by: Stephen Gallimore <stephen.gallimore@st.com>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@st.com>
Acked-by: Philipp Zabel <p.zabel@pengutronix.de>

authored by

Stephen Gallimore and committed by
Srinivas Kandagatla
e5d76075 e063735f

+266
+2
drivers/reset/Kconfig
··· 11 11 via GPIOs or SoC-internal reset controller modules. 12 12 13 13 If unsure, say no. 14 + 15 + source "drivers/reset/sti/Kconfig"
+1
drivers/reset/Makefile
··· 1 1 obj-$(CONFIG_RESET_CONTROLLER) += core.o 2 2 obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o 3 + obj-$(CONFIG_ARCH_STI) += sti/
+7
drivers/reset/sti/Kconfig
··· 1 + if ARCH_STI 2 + 3 + config STI_RESET_SYSCFG 4 + bool 5 + select RESET_CONTROLLER 6 + 7 + endif
+1
drivers/reset/sti/Makefile
··· 1 + obj-$(CONFIG_STI_RESET_SYSCFG) += reset-syscfg.o
+186
drivers/reset/sti/reset-syscfg.c
··· 1 + /* 2 + * Copyright (C) 2013 STMicroelectronics Limited 3 + * Author: Stephen Gallimore <stephen.gallimore@st.com> 4 + * 5 + * Inspired by mach-imx/src.c 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License as published by 9 + * the Free Software Foundation; either version 2 of the License, or 10 + * (at your option) any later version. 11 + */ 12 + #include <linux/kernel.h> 13 + #include <linux/platform_device.h> 14 + #include <linux/module.h> 15 + #include <linux/err.h> 16 + #include <linux/types.h> 17 + #include <linux/of_device.h> 18 + #include <linux/regmap.h> 19 + #include <linux/mfd/syscon.h> 20 + 21 + #include "reset-syscfg.h" 22 + 23 + /** 24 + * Reset channel regmap configuration 25 + * 26 + * @reset: regmap field for the channel's reset bit. 27 + * @ack: regmap field for the channel's ack bit (optional). 28 + */ 29 + struct syscfg_reset_channel { 30 + struct regmap_field *reset; 31 + struct regmap_field *ack; 32 + }; 33 + 34 + /** 35 + * A reset controller which groups together a set of related reset bits, which 36 + * may be located in different system configuration registers. 37 + * 38 + * @rst: base reset controller structure. 39 + * @active_low: are the resets in this controller active low, i.e. clearing 40 + * the reset bit puts the hardware into reset. 41 + * @channels: An array of reset channels for this controller. 42 + */ 43 + struct syscfg_reset_controller { 44 + struct reset_controller_dev rst; 45 + bool active_low; 46 + struct syscfg_reset_channel *channels; 47 + }; 48 + 49 + #define to_syscfg_reset_controller(_rst) \ 50 + container_of(_rst, struct syscfg_reset_controller, rst) 51 + 52 + static int syscfg_reset_program_hw(struct reset_controller_dev *rcdev, 53 + unsigned long idx, int assert) 54 + { 55 + struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev); 56 + const struct syscfg_reset_channel *ch; 57 + u32 ctrl_val = rst->active_low ? !assert : !!assert; 58 + int err; 59 + 60 + if (idx >= rcdev->nr_resets) 61 + return -EINVAL; 62 + 63 + ch = &rst->channels[idx]; 64 + 65 + err = regmap_field_write(ch->reset, ctrl_val); 66 + if (err) 67 + return err; 68 + 69 + if (ch->ack) { 70 + unsigned long timeout = jiffies + msecs_to_jiffies(1000); 71 + u32 ack_val; 72 + 73 + while (true) { 74 + err = regmap_field_read(ch->ack, &ack_val); 75 + if (err) 76 + return err; 77 + 78 + if (ack_val == ctrl_val) 79 + break; 80 + 81 + if (time_after(jiffies, timeout)) 82 + return -ETIME; 83 + 84 + cpu_relax(); 85 + } 86 + } 87 + 88 + return 0; 89 + } 90 + 91 + static int syscfg_reset_assert(struct reset_controller_dev *rcdev, 92 + unsigned long idx) 93 + { 94 + return syscfg_reset_program_hw(rcdev, idx, true); 95 + } 96 + 97 + static int syscfg_reset_deassert(struct reset_controller_dev *rcdev, 98 + unsigned long idx) 99 + { 100 + return syscfg_reset_program_hw(rcdev, idx, false); 101 + } 102 + 103 + static int syscfg_reset_dev(struct reset_controller_dev *rcdev, 104 + unsigned long idx) 105 + { 106 + int err = syscfg_reset_assert(rcdev, idx); 107 + if (err) 108 + return err; 109 + 110 + return syscfg_reset_deassert(rcdev, idx); 111 + } 112 + 113 + static struct reset_control_ops syscfg_reset_ops = { 114 + .reset = syscfg_reset_dev, 115 + .assert = syscfg_reset_assert, 116 + .deassert = syscfg_reset_deassert, 117 + }; 118 + 119 + static int syscfg_reset_controller_register(struct device *dev, 120 + const struct syscfg_reset_controller_data *data) 121 + { 122 + struct syscfg_reset_controller *rc; 123 + size_t size; 124 + int i, err; 125 + 126 + rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL); 127 + if (!rc) 128 + return -ENOMEM; 129 + 130 + size = sizeof(struct syscfg_reset_channel) * data->nr_channels; 131 + 132 + rc->channels = devm_kzalloc(dev, size, GFP_KERNEL); 133 + if (!rc->channels) 134 + return -ENOMEM; 135 + 136 + rc->rst.ops = &syscfg_reset_ops, 137 + rc->rst.of_node = dev->of_node; 138 + rc->rst.nr_resets = data->nr_channels; 139 + rc->active_low = data->active_low; 140 + 141 + for (i = 0; i < data->nr_channels; i++) { 142 + struct regmap *map; 143 + struct regmap_field *f; 144 + const char *compatible = data->channels[i].compatible; 145 + 146 + map = syscon_regmap_lookup_by_compatible(compatible); 147 + if (IS_ERR(map)) 148 + return PTR_ERR(map); 149 + 150 + f = devm_regmap_field_alloc(dev, map, data->channels[i].reset); 151 + if (IS_ERR(f)) 152 + return PTR_ERR(f); 153 + 154 + rc->channels[i].reset = f; 155 + 156 + if (!data->wait_for_ack) 157 + continue; 158 + 159 + f = devm_regmap_field_alloc(dev, map, data->channels[i].ack); 160 + if (IS_ERR(f)) 161 + return PTR_ERR(f); 162 + 163 + rc->channels[i].ack = f; 164 + } 165 + 166 + err = reset_controller_register(&rc->rst); 167 + if (!err) 168 + dev_info(dev, "registered\n"); 169 + 170 + return err; 171 + } 172 + 173 + int syscfg_reset_probe(struct platform_device *pdev) 174 + { 175 + struct device *dev = pdev ? &pdev->dev : NULL; 176 + const struct of_device_id *match; 177 + 178 + if (!dev || !dev->driver) 179 + return -ENODEV; 180 + 181 + match = of_match_device(dev->driver->of_match_table, dev); 182 + if (!match || !match->data) 183 + return -EINVAL; 184 + 185 + return syscfg_reset_controller_register(dev, match->data); 186 + }
+69
drivers/reset/sti/reset-syscfg.h
··· 1 + /* 2 + * Copyright (C) 2013 STMicroelectronics (R&D) Limited 3 + * Author: Stephen Gallimore <stephen.gallimore@st.com> 4 + * 5 + * This program is free software; you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License as published by 7 + * the Free Software Foundation; either version 2 of the License, or 8 + * (at your option) any later version. 9 + */ 10 + #ifndef __STI_RESET_SYSCFG_H 11 + #define __STI_RESET_SYSCFG_H 12 + 13 + #include <linux/device.h> 14 + #include <linux/regmap.h> 15 + #include <linux/reset-controller.h> 16 + 17 + /** 18 + * Reset channel description for a system configuration register based 19 + * reset controller. 20 + * 21 + * @compatible: Compatible string of the syscon regmap containing this 22 + * channel's control and ack (status) bits. 23 + * @reset: Regmap field description of the channel's reset bit. 24 + * @ack: Regmap field description of the channel's acknowledge bit. 25 + */ 26 + struct syscfg_reset_channel_data { 27 + const char *compatible; 28 + struct reg_field reset; 29 + struct reg_field ack; 30 + }; 31 + 32 + #define _SYSCFG_RST_CH(_c, _rr, _rb, _ar, _ab) \ 33 + { .compatible = _c, \ 34 + .reset = REG_FIELD(_rr, _rb, _rb), \ 35 + .ack = REG_FIELD(_ar, _ab, _ab), } 36 + 37 + #define _SYSCFG_RST_CH_NO_ACK(_c, _rr, _rb) \ 38 + { .compatible = _c, \ 39 + .reset = REG_FIELD(_rr, _rb, _rb), } 40 + 41 + /** 42 + * Description of a system configuration register based reset controller. 43 + * 44 + * @wait_for_ack: The controller will wait for reset assert and de-assert to 45 + * be "ack'd" in a channel's ack field. 46 + * @active_low: Are the resets in this controller active low, i.e. clearing 47 + * the reset bit puts the hardware into reset. 48 + * @nr_channels: The number of reset channels in this controller. 49 + * @channels: An array of reset channel descriptions. 50 + */ 51 + struct syscfg_reset_controller_data { 52 + bool wait_for_ack; 53 + bool active_low; 54 + int nr_channels; 55 + const struct syscfg_reset_channel_data *channels; 56 + }; 57 + 58 + /** 59 + * syscfg_reset_probe(): platform device probe function used by syscfg 60 + * reset controller drivers. This registers a reset 61 + * controller configured by the OF match data for 62 + * the compatible device which should be of type 63 + * "struct syscfg_reset_controller_data". 64 + * 65 + * @pdev: platform device 66 + */ 67 + int syscfg_reset_probe(struct platform_device *pdev); 68 + 69 + #endif /* __STI_RESET_SYSCFG_H */