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