diff options
Diffstat (limited to 'target/linux/sunxi/patches-3.12/140-add-a31-reset-driver.patch')
-rw-r--r-- | target/linux/sunxi/patches-3.12/140-add-a31-reset-driver.patch | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/target/linux/sunxi/patches-3.12/140-add-a31-reset-driver.patch b/target/linux/sunxi/patches-3.12/140-add-a31-reset-driver.patch new file mode 100644 index 0000000000..3d48a5fa1f --- /dev/null +++ b/target/linux/sunxi/patches-3.12/140-add-a31-reset-driver.patch @@ -0,0 +1,189 @@ +From d8d7a5805579db687fdfdfda4125b43d6a50e616 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Tue, 24 Sep 2013 11:07:43 +0300 +Subject: [PATCH] reset: Add Allwinner A31 Reset Controller Driver + +The Allwinner A31 has an IP maintaining a few other IPs in the SoC in +reset by default. Among these IPs are the High Speed Timers, hence why +we can't use the regular driver construct, and need to call the +registering function directly during machine initialisation. + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + drivers/reset/Makefile | 1 + + drivers/reset/reset-sunxi.c | 156 ++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 157 insertions(+) + create mode 100644 drivers/reset/reset-sunxi.c + +diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile +index 1e2d83f..f216b74 100644 +--- a/drivers/reset/Makefile ++++ b/drivers/reset/Makefile +@@ -1 +1,2 @@ + obj-$(CONFIG_RESET_CONTROLLER) += core.o ++obj-$(CONFIG_ARCH_SUNXI) += reset-sun6i.o +diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c +new file mode 100644 +index 0000000..5bbd59a +--- /dev/null ++++ b/drivers/reset/reset-sunxi.c +@@ -0,0 +1,156 @@ ++/* ++ * Allwinner SoCs Reset Controller driver ++ * ++ * Copyright 2013 Maxime Ripard ++ * ++ * Maxime Ripard <maxime.ripard@free-electrons.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include <linux/err.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_address.h> ++#include <linux/platform_device.h> ++#include <linux/reset-controller.h> ++#include <linux/slab.h> ++#include <linux/types.h> ++ ++struct sunxi_reset_data { ++ void __iomem *membase; ++ struct reset_controller_dev rcdev; ++}; ++ ++static int sunxi_reset_assert(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ struct sunxi_reset_data *data = container_of(rcdev, ++ struct sunxi_reset_data, ++ rcdev); ++ int bank = id / BITS_PER_LONG; ++ int offset = id % BITS_PER_LONG; ++ u32 reg = readl(data->membase + (bank * 4)); ++ ++ writel(reg & ~BIT(offset), data->membase + (bank * 4)); ++ ++ return 0; ++} ++ ++static int sunxi_reset_deassert(struct reset_controller_dev *rcdev, ++ unsigned long id) ++{ ++ struct sunxi_reset_data *data = container_of(rcdev, ++ struct sunxi_reset_data, ++ rcdev); ++ int bank = id / BITS_PER_LONG; ++ int offset = id % BITS_PER_LONG; ++ u32 reg = readl(data->membase + (bank * 4)); ++ ++ writel(reg | BIT(offset), data->membase + (bank * 4)); ++ ++ return 0; ++} ++ ++static struct reset_control_ops sunxi_reset_ops = { ++ .assert = sunxi_reset_assert, ++ .deassert = sunxi_reset_deassert, ++}; ++ ++static int sunxi_reset_init(struct device_node *np) ++{ ++ struct sunxi_reset_data *data; ++ struct resource res; ++ resource_size_t size; ++ int ret; ++ ++ data = kzalloc(sizeof(*data), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ ret = of_address_to_resource(np, 0, &res); ++ if (ret) ++ goto err_alloc; ++ ++ size = resource_size(&res); ++ data->membase = ioremap(res.start, size); ++ if (!data->membase) { ++ ret = -ENOMEM; ++ goto err_alloc; ++ } ++ ++ data->rcdev.owner = THIS_MODULE; ++ data->rcdev.nr_resets = size * 32; ++ data->rcdev.ops = &sunxi_reset_ops; ++ data->rcdev.of_node = np; ++ reset_controller_register(&data->rcdev); ++ ++ return 0; ++ ++err_alloc: ++ kfree(data); ++ return ret; ++}; ++ ++/* ++ * These are the reset controller we need to initialize early on in ++ * our system, before we can even think of using a regular device ++ * driver for it. ++ */ ++static const struct of_device_id sunxi_early_reset_dt_ids[] __initdata = { ++ { .compatible = "allwinner,sun6i-a31-ahb1-reset", }, ++ { /* sentinel */ }, ++}; ++ ++void __init sun6i_reset_init(void) ++{ ++ struct device_node *np; ++ ++ for_each_matching_node(np, sunxi_early_reset_dt_ids) ++ sunxi_reset_init(np); ++} ++ ++/* ++ * And these are the controllers we can register through the regular ++ * device model. ++ */ ++static const struct of_device_id sunxi_reset_dt_ids[] = { ++ { .compatible = "allwinner,sun4i-clock-reset", }, ++ { /* sentinel */ }, ++}; ++MODULE_DEVICE_TABLE(of, sunxi_reset_dt_ids); ++ ++static int sunxi_reset_probe(struct platform_device *pdev) ++{ ++ return sunxi_reset_init(pdev->dev.of_node); ++} ++ ++static int sunxi_reset_remove(struct platform_device *pdev) ++{ ++ struct sunxi_reset_data *data = platform_get_drvdata(pdev); ++ ++ reset_controller_unregister(&data->rcdev); ++ iounmap(data->membase); ++ kfree(data); ++ ++ return 0; ++} ++ ++static struct platform_driver sunxi_reset_driver = { ++ .probe = sunxi_reset_probe, ++ .remove = sunxi_reset_remove, ++ .driver = { ++ .name = "sunxi-reset", ++ .owner = THIS_MODULE, ++ .of_match_table = sunxi_reset_dt_ids, ++ }, ++}; ++module_platform_driver(sunxi_reset_driver); ++ ++MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com"); ++MODULE_DESCRIPTION("Allwinner SoCs Reset Controller Driver"); ++MODULE_LICENSE("GPL"); +-- +1.8.4 + |