diff options
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | cds9k-reset.c | 91 | ||||
-rw-r--r-- | simple-reset-consumer.c | 36 |
3 files changed, 130 insertions, 0 deletions
@@ -1,6 +1,9 @@ +ccflags-y := -Wall -Werror obj-m := cds9k-mfd-spi.o obj-m += cds9k-gpio.o obj-m += cds9k-led.o +obj-m += cds9k-reset.o +obj-m += simple-reset-consumer.o SRC := $(shell pwd) diff --git a/cds9k-reset.c b/cds9k-reset.c new file mode 100644 index 0000000..d90ed7e --- /dev/null +++ b/cds9k-reset.c @@ -0,0 +1,91 @@ +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> + +#define RESET_REG_0 0x0 + +struct cds9k_reset_priv { + struct reset_controller_dev rcdev; + struct regmap *regmap; + u32 base; +}; + +static int cds9k_reset_reset(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct cds9k_reset_priv *priv = container_of(rcdev, struct cds9k_reset_priv, rcdev); + + /* reset sequence: put 0xDEAD into reg 0 for 250 ms */ + struct reg_sequence reset_sequence[] = { + { RESET_REG_0, 0xDEAD, 250000 }, + { RESET_REG_0, 0x0000 }, + }; + + if (id != 0) + return -EINVAL; + + return regmap_multi_reg_write(priv->regmap, reset_sequence, ARRAY_SIZE(reset_sequence)); +} + +static int cds9k_reset_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *spec) +{ + /* single reset */ + return 0; +} + +const struct reset_control_ops cds9k_reset_ops = { + .reset = cds9k_reset_reset, +}; + +static int cds9k_reset_probe(struct platform_device *pdev) +{ + struct cds9k_reset_priv *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + ret = device_property_read_u32(&pdev->dev, "reg", &priv->base); + if (ret) + return -EINVAL; + + if (!pdev->dev.parent) + return -ENODEV; + + priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!priv->regmap) + return -ENODEV; + + priv->rcdev.ops = &cds9k_reset_ops; + priv->rcdev.owner = pdev->dev.driver->owner; + priv->rcdev.of_node = pdev->dev.of_node; + priv->rcdev.nr_resets = 1; + priv->rcdev.of_reset_n_cells = 0; + priv->rcdev.of_xlate = cds9k_reset_xlate; + + return devm_reset_controller_register(&pdev->dev, &priv->rcdev); +} + +static struct of_device_id cds9k_reset_of_match[] = { + { .compatible = "david,cds9k-reset" }, + {} +}; +MODULE_DEVICE_TABLE(of, cds9k_reset_of_match); + +static struct platform_driver cds9k_reset = { + .driver = { + .name = "cds9k-reset", + .of_match_table = cds9k_reset_of_match, + }, + .probe = cds9k_reset_probe +}; +module_platform_driver(cds9k_reset); + +MODULE_AUTHOR("David Phillips <david@yeah.nah.nz>"); +MODULE_DESCRIPTION("Reset driver for the CDS9K board controller"); +MODULE_LICENSE("GPL v2"); diff --git a/simple-reset-consumer.c b/simple-reset-consumer.c new file mode 100644 index 0000000..21c0da0 --- /dev/null +++ b/simple-reset-consumer.c @@ -0,0 +1,36 @@ +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +static int simple_reset_consumer_probe(struct platform_device *pdev) +{ + struct reset_control *reset = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(reset)) + return PTR_ERR(reset); + + dev_info(&pdev->dev, "firing reset\n"); + reset_control_reset(reset); + + return 0; +} + +static struct of_device_id simple_reset_consumer_of_match[] = { + { .compatible = "david,simple-reset-consumer" }, + {} +}; +MODULE_DEVICE_TABLE(of, simple_reset_consumer_of_match); + +static struct platform_driver simple_reset_consumer = { + .driver = { + .name = "simple-reset-consumer", + .of_match_table = simple_reset_consumer_of_match, + }, + .probe = simple_reset_consumer_probe +}; +module_platform_driver(simple_reset_consumer); + +MODULE_AUTHOR("David Phillips <david@yeah.nah.nz>"); +MODULE_DESCRIPTION("Simple reset consumer for testing/demoing reset controllers"); +MODULE_LICENSE("GPL v2"); |