Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * TI SYSCON regmap reset driver
0004  *
0005  * Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/
0006  *  Andrew F. Davis <afd@ti.com>
0007  *  Suman Anna <afd@ti.com>
0008  */
0009 
0010 #include <linux/mfd/syscon.h>
0011 #include <linux/module.h>
0012 #include <linux/of.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/regmap.h>
0015 #include <linux/reset-controller.h>
0016 
0017 #include <dt-bindings/reset/ti-syscon.h>
0018 
0019 /**
0020  * struct ti_syscon_reset_control - reset control structure
0021  * @assert_offset: reset assert control register offset from syscon base
0022  * @assert_bit: reset assert bit in the reset assert control register
0023  * @deassert_offset: reset deassert control register offset from syscon base
0024  * @deassert_bit: reset deassert bit in the reset deassert control register
0025  * @status_offset: reset status register offset from syscon base
0026  * @status_bit: reset status bit in the reset status register
0027  * @flags: reset flag indicating how the (de)assert and status are handled
0028  */
0029 struct ti_syscon_reset_control {
0030     unsigned int assert_offset;
0031     unsigned int assert_bit;
0032     unsigned int deassert_offset;
0033     unsigned int deassert_bit;
0034     unsigned int status_offset;
0035     unsigned int status_bit;
0036     u32 flags;
0037 };
0038 
0039 /**
0040  * struct ti_syscon_reset_data - reset controller information structure
0041  * @rcdev: reset controller entity
0042  * @regmap: regmap handle containing the memory-mapped reset registers
0043  * @controls: array of reset controls
0044  * @nr_controls: number of controls in control array
0045  */
0046 struct ti_syscon_reset_data {
0047     struct reset_controller_dev rcdev;
0048     struct regmap *regmap;
0049     struct ti_syscon_reset_control *controls;
0050     unsigned int nr_controls;
0051 };
0052 
0053 #define to_ti_syscon_reset_data(_rcdev) \
0054     container_of(_rcdev, struct ti_syscon_reset_data, rcdev)
0055 
0056 /**
0057  * ti_syscon_reset_assert() - assert device reset
0058  * @rcdev: reset controller entity
0059  * @id: ID of the reset to be asserted
0060  *
0061  * This function implements the reset driver op to assert a device's reset.
0062  * This asserts the reset in a manner prescribed by the reset flags.
0063  *
0064  * Return: 0 for successful request, else a corresponding error value
0065  */
0066 static int ti_syscon_reset_assert(struct reset_controller_dev *rcdev,
0067                   unsigned long id)
0068 {
0069     struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev);
0070     struct ti_syscon_reset_control *control;
0071     unsigned int mask, value;
0072 
0073     if (id >= data->nr_controls)
0074         return -EINVAL;
0075 
0076     control = &data->controls[id];
0077 
0078     if (control->flags & ASSERT_NONE)
0079         return -ENOTSUPP; /* assert not supported for this reset */
0080 
0081     mask = BIT(control->assert_bit);
0082     value = (control->flags & ASSERT_SET) ? mask : 0x0;
0083 
0084     return regmap_write_bits(data->regmap, control->assert_offset, mask, value);
0085 }
0086 
0087 /**
0088  * ti_syscon_reset_deassert() - deassert device reset
0089  * @rcdev: reset controller entity
0090  * @id: ID of reset to be deasserted
0091  *
0092  * This function implements the reset driver op to deassert a device's reset.
0093  * This deasserts the reset in a manner prescribed by the reset flags.
0094  *
0095  * Return: 0 for successful request, else a corresponding error value
0096  */
0097 static int ti_syscon_reset_deassert(struct reset_controller_dev *rcdev,
0098                     unsigned long id)
0099 {
0100     struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev);
0101     struct ti_syscon_reset_control *control;
0102     unsigned int mask, value;
0103 
0104     if (id >= data->nr_controls)
0105         return -EINVAL;
0106 
0107     control = &data->controls[id];
0108 
0109     if (control->flags & DEASSERT_NONE)
0110         return -ENOTSUPP; /* deassert not supported for this reset */
0111 
0112     mask = BIT(control->deassert_bit);
0113     value = (control->flags & DEASSERT_SET) ? mask : 0x0;
0114 
0115     return regmap_write_bits(data->regmap, control->deassert_offset, mask, value);
0116 }
0117 
0118 /**
0119  * ti_syscon_reset_status() - check device reset status
0120  * @rcdev: reset controller entity
0121  * @id: ID of the reset for which the status is being requested
0122  *
0123  * This function implements the reset driver op to return the status of a
0124  * device's reset.
0125  *
0126  * Return: 0 if reset is deasserted, true if reset is asserted, else a
0127  * corresponding error value
0128  */
0129 static int ti_syscon_reset_status(struct reset_controller_dev *rcdev,
0130                   unsigned long id)
0131 {
0132     struct ti_syscon_reset_data *data = to_ti_syscon_reset_data(rcdev);
0133     struct ti_syscon_reset_control *control;
0134     unsigned int reset_state;
0135     int ret;
0136 
0137     if (id >= data->nr_controls)
0138         return -EINVAL;
0139 
0140     control = &data->controls[id];
0141 
0142     if (control->flags & STATUS_NONE)
0143         return -ENOTSUPP; /* status not supported for this reset */
0144 
0145     ret = regmap_read(data->regmap, control->status_offset, &reset_state);
0146     if (ret)
0147         return ret;
0148 
0149     return !(reset_state & BIT(control->status_bit)) ==
0150         !(control->flags & STATUS_SET);
0151 }
0152 
0153 static const struct reset_control_ops ti_syscon_reset_ops = {
0154     .assert     = ti_syscon_reset_assert,
0155     .deassert   = ti_syscon_reset_deassert,
0156     .status     = ti_syscon_reset_status,
0157 };
0158 
0159 static int ti_syscon_reset_probe(struct platform_device *pdev)
0160 {
0161     struct device *dev = &pdev->dev;
0162     struct device_node *np = dev->of_node;
0163     struct ti_syscon_reset_data *data;
0164     struct regmap *regmap;
0165     const __be32 *list;
0166     struct ti_syscon_reset_control *controls;
0167     int size, nr_controls, i;
0168 
0169     data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
0170     if (!data)
0171         return -ENOMEM;
0172 
0173     regmap = syscon_node_to_regmap(np->parent);
0174     if (IS_ERR(regmap))
0175         return PTR_ERR(regmap);
0176 
0177     list = of_get_property(np, "ti,reset-bits", &size);
0178     if (!list || (size / sizeof(*list)) % 7 != 0) {
0179         dev_err(dev, "invalid DT reset description\n");
0180         return -EINVAL;
0181     }
0182 
0183     nr_controls = (size / sizeof(*list)) / 7;
0184     controls = devm_kcalloc(dev, nr_controls, sizeof(*controls),
0185                 GFP_KERNEL);
0186     if (!controls)
0187         return -ENOMEM;
0188 
0189     for (i = 0; i < nr_controls; i++) {
0190         controls[i].assert_offset = be32_to_cpup(list++);
0191         controls[i].assert_bit = be32_to_cpup(list++);
0192         controls[i].deassert_offset = be32_to_cpup(list++);
0193         controls[i].deassert_bit = be32_to_cpup(list++);
0194         controls[i].status_offset = be32_to_cpup(list++);
0195         controls[i].status_bit = be32_to_cpup(list++);
0196         controls[i].flags = be32_to_cpup(list++);
0197     }
0198 
0199     data->rcdev.ops = &ti_syscon_reset_ops;
0200     data->rcdev.owner = THIS_MODULE;
0201     data->rcdev.of_node = np;
0202     data->rcdev.nr_resets = nr_controls;
0203     data->regmap = regmap;
0204     data->controls = controls;
0205     data->nr_controls = nr_controls;
0206 
0207     platform_set_drvdata(pdev, data);
0208 
0209     return devm_reset_controller_register(dev, &data->rcdev);
0210 }
0211 
0212 static const struct of_device_id ti_syscon_reset_of_match[] = {
0213     { .compatible = "ti,syscon-reset", },
0214     { /* sentinel */ },
0215 };
0216 MODULE_DEVICE_TABLE(of, ti_syscon_reset_of_match);
0217 
0218 static struct platform_driver ti_syscon_reset_driver = {
0219     .probe = ti_syscon_reset_probe,
0220     .driver = {
0221         .name = "ti-syscon-reset",
0222         .of_match_table = ti_syscon_reset_of_match,
0223     },
0224 };
0225 module_platform_driver(ti_syscon_reset_driver);
0226 
0227 MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
0228 MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
0229 MODULE_DESCRIPTION("TI SYSCON Regmap Reset Driver");
0230 MODULE_LICENSE("GPL v2");