diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/bus/brcmstb_gisb.c | 45 | ||||
-rw-r--r-- | drivers/clk/ti/dpll.c | 15 | ||||
-rw-r--r-- | drivers/clocksource/Makefile | 1 | ||||
-rw-r--r-- | drivers/clocksource/timer-integrator-ap.c | 210 | ||||
-rw-r--r-- | drivers/soc/versatile/Kconfig | 9 | ||||
-rw-r--r-- | drivers/soc/versatile/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/versatile/soc-integrator.c | 154 |
7 files changed, 429 insertions, 6 deletions
diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c index f2cd6a2d40b4..e7ccd21a45c9 100644 --- a/drivers/bus/brcmstb_gisb.c +++ b/drivers/bus/brcmstb_gisb.c @@ -23,6 +23,7 @@ #include <linux/list.h> #include <linux/of.h> #include <linux/bitops.h> +#include <linux/pm.h> #include <asm/bug.h> #include <asm/signal.h> @@ -48,6 +49,7 @@ struct brcmstb_gisb_arb_device { struct list_head next; u32 valid_mask; const char *master_names[sizeof(u32) * BITS_PER_BYTE]; + u32 saved_timeout; }; static LIST_HEAD(brcmstb_gisb_arb_device_list); @@ -160,12 +162,6 @@ static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr, return ret; } -void __init brcmstb_hook_fault_code(void) -{ - hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0, - "imprecise external abort"); -} - static irqreturn_t brcmstb_gisb_timeout_handler(int irq, void *dev_id) { brcmstb_gisb_arb_decode_addr(dev_id, "timeout"); @@ -261,12 +257,48 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev) list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list); + hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0, + "imprecise external abort"); + dev_info(&pdev->dev, "registered mem: %p, irqs: %d, %d\n", gdev->base, timeout_irq, tea_irq); return 0; } +#ifdef CONFIG_PM_SLEEP +static int brcmstb_gisb_arb_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); + + gdev->saved_timeout = ioread32(gdev->base + ARB_TIMER); + + return 0; +} + +/* Make sure we provide the same timeout value that was configured before, and + * do this before the GISB timeout interrupt handler has any chance to run. + */ +static int brcmstb_gisb_arb_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); + + iowrite32(gdev->saved_timeout, gdev->base + ARB_TIMER); + + return 0; +} +#else +#define brcmstb_gisb_arb_suspend NULL +#define brcmstb_gisb_arb_resume_noirq NULL +#endif + +static const struct dev_pm_ops brcmstb_gisb_arb_pm_ops = { + .suspend = brcmstb_gisb_arb_suspend, + .resume_noirq = brcmstb_gisb_arb_resume_noirq, +}; + static const struct of_device_id brcmstb_gisb_arb_of_match[] = { { .compatible = "brcm,gisb-arb" }, { }, @@ -278,6 +310,7 @@ static struct platform_driver brcmstb_gisb_arb_driver = { .name = "brcm-gisb-arb", .owner = THIS_MODULE, .of_match_table = brcmstb_gisb_arb_of_match, + .pm = &brcmstb_gisb_arb_pm_ops, }, }; diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c index 79791e1bf282..85ac0dd501de 100644 --- a/drivers/clk/ti/dpll.c +++ b/drivers/clk/ti/dpll.c @@ -33,6 +33,9 @@ static const struct clk_ops dpll_m4xen_ck_ops = { .recalc_rate = &omap4_dpll_regm4xen_recalc, .round_rate = &omap4_dpll_regm4xen_round_rate, .set_rate = &omap3_noncore_dpll_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent, + .determine_rate = &omap4_dpll_regm4xen_determine_rate, .get_parent = &omap2_init_dpll_parent, }; #else @@ -53,6 +56,9 @@ static const struct clk_ops dpll_ck_ops = { .recalc_rate = &omap3_dpll_recalc, .round_rate = &omap2_dpll_round_rate, .set_rate = &omap3_noncore_dpll_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent, + .determine_rate = &omap3_noncore_dpll_determine_rate, .get_parent = &omap2_init_dpll_parent, }; @@ -61,6 +67,9 @@ static const struct clk_ops dpll_no_gate_ck_ops = { .get_parent = &omap2_init_dpll_parent, .round_rate = &omap2_dpll_round_rate, .set_rate = &omap3_noncore_dpll_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent, + .determine_rate = &omap3_noncore_dpll_determine_rate, }; #else static const struct clk_ops dpll_core_ck_ops = {}; @@ -97,6 +106,9 @@ static const struct clk_ops omap3_dpll_ck_ops = { .get_parent = &omap2_init_dpll_parent, .recalc_rate = &omap3_dpll_recalc, .set_rate = &omap3_noncore_dpll_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent, + .determine_rate = &omap3_noncore_dpll_determine_rate, .round_rate = &omap2_dpll_round_rate, }; @@ -106,6 +118,9 @@ static const struct clk_ops omap3_dpll_per_ck_ops = { .get_parent = &omap2_init_dpll_parent, .recalc_rate = &omap3_dpll_recalc, .set_rate = &omap3_dpll4_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_dpll4_set_rate_and_parent, + .determine_rate = &omap3_noncore_dpll_determine_rate, .round_rate = &omap2_dpll_round_rate, }; #endif diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 756f6f10efa0..fae0435cc23d 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -45,4 +45,5 @@ obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o obj-$(CONFIG_ARCH_KEYSTONE) += timer-keystone.o +obj-$(CONFIG_ARCH_INTEGRATOR_AP) += timer-integrator-ap.o obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o diff --git a/drivers/clocksource/timer-integrator-ap.c b/drivers/clocksource/timer-integrator-ap.c new file mode 100644 index 000000000000..b9efd30513d5 --- /dev/null +++ b/drivers/clocksource/timer-integrator-ap.c @@ -0,0 +1,210 @@ +/* + * Integrator/AP timer driver + * Copyright (C) 2000-2003 Deep Blue Solutions Ltd + * Copyright (c) 2014, Linaro Limited + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/clk.h> +#include <linux/clocksource.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/clockchips.h> +#include <linux/interrupt.h> +#include <linux/sched_clock.h> +#include <asm/hardware/arm_timer.h> + +static void __iomem * sched_clk_base; + +static u64 notrace integrator_read_sched_clock(void) +{ + return -readl(sched_clk_base + TIMER_VALUE); +} + +static void integrator_clocksource_init(unsigned long inrate, + void __iomem *base) +{ + u32 ctrl = TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC; + unsigned long rate = inrate; + + if (rate >= 1500000) { + rate /= 16; + ctrl |= TIMER_CTRL_DIV16; + } + + writel(0xffff, base + TIMER_LOAD); + writel(ctrl, base + TIMER_CTRL); + + clocksource_mmio_init(base + TIMER_VALUE, "timer2", + rate, 200, 16, clocksource_mmio_readl_down); + + sched_clk_base = base; + sched_clock_register(integrator_read_sched_clock, 16, rate); +} + +static unsigned long timer_reload; +static void __iomem * clkevt_base; + +/* + * IRQ handler for the timer + */ +static irqreturn_t integrator_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + /* clear the interrupt */ + writel(1, clkevt_base + TIMER_INTCLR); + + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static void clkevt_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) +{ + u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE; + + /* Disable timer */ + writel(ctrl, clkevt_base + TIMER_CTRL); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + /* Enable the timer and start the periodic tick */ + writel(timer_reload, clkevt_base + TIMER_LOAD); + ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE; + writel(ctrl, clkevt_base + TIMER_CTRL); + break; + case CLOCK_EVT_MODE_ONESHOT: + /* Leave the timer disabled, .set_next_event will enable it */ + ctrl &= ~TIMER_CTRL_PERIODIC; + writel(ctrl, clkevt_base + TIMER_CTRL); + break; + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_RESUME: + default: + /* Just leave in disabled state */ + break; + } + +} + +static int clkevt_set_next_event(unsigned long next, struct clock_event_device *evt) +{ + unsigned long ctrl = readl(clkevt_base + TIMER_CTRL); + + writel(ctrl & ~TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL); + writel(next, clkevt_base + TIMER_LOAD); + writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL); + + return 0; +} + +static struct clock_event_device integrator_clockevent = { + .name = "timer1", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_mode = clkevt_set_mode, + .set_next_event = clkevt_set_next_event, + .rating = 300, +}; + +static struct irqaction integrator_timer_irq = { + .name = "timer", + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = integrator_timer_interrupt, + .dev_id = &integrator_clockevent, +}; + +static void integrator_clockevent_init(unsigned long inrate, + void __iomem *base, int irq) +{ + unsigned long rate = inrate; + unsigned int ctrl = 0; + + clkevt_base = base; + /* Calculate and program a divisor */ + if (rate > 0x100000 * HZ) { + rate /= 256; + ctrl |= TIMER_CTRL_DIV256; + } else if (rate > 0x10000 * HZ) { + rate /= 16; + ctrl |= TIMER_CTRL_DIV16; + } + timer_reload = rate / HZ; + writel(ctrl, clkevt_base + TIMER_CTRL); + + setup_irq(irq, &integrator_timer_irq); + clockevents_config_and_register(&integrator_clockevent, + rate, + 1, + 0xffffU); +} + +static void __init integrator_ap_timer_init_of(struct device_node *node) +{ + const char *path; + void __iomem *base; + int err; + int irq; + struct clk *clk; + unsigned long rate; + struct device_node *pri_node; + struct device_node *sec_node; + + base = of_io_request_and_map(node, 0, "integrator-timer"); + if (!base) + return; + + clk = of_clk_get(node, 0); + if (IS_ERR(clk)) { + pr_err("No clock for %s\n", node->name); + return; + } + clk_prepare_enable(clk); + rate = clk_get_rate(clk); + writel(0, base + TIMER_CTRL); + + err = of_property_read_string(of_aliases, + "arm,timer-primary", &path); + if (WARN_ON(err)) + return; + pri_node = of_find_node_by_path(path); + err = of_property_read_string(of_aliases, + "arm,timer-secondary", &path); + if (WARN_ON(err)) + return; + sec_node = of_find_node_by_path(path); + + if (node == pri_node) { + /* The primary timer lacks IRQ, use as clocksource */ + integrator_clocksource_init(rate, base); + return; + } + + if (node == sec_node) { + /* The secondary timer will drive the clock event */ + irq = irq_of_parse_and_map(node, 0); + integrator_clockevent_init(rate, base, irq); + return; + } + + pr_info("Timer @%p unused\n", base); + clk_disable_unprepare(clk); +} + +CLOCKSOURCE_OF_DECLARE(integrator_ap_timer, "arm,integrator-timer", + integrator_ap_timer_init_of); diff --git a/drivers/soc/versatile/Kconfig b/drivers/soc/versatile/Kconfig index bf5ee9c85330..a928a7fc6be4 100644 --- a/drivers/soc/versatile/Kconfig +++ b/drivers/soc/versatile/Kconfig @@ -1,6 +1,15 @@ # # ARM Versatile SoC drivers # +config SOC_INTEGRATOR_CM + bool "SoC bus device for the ARM Integrator platform core modules" + depends on ARCH_INTEGRATOR + select SOC_BUS + help + Include support for the SoC bus on the ARM Integrator platform + core modules providing some sysfs information about the ASIC + variant. + config SOC_REALVIEW bool "SoC bus device for the ARM RealView platforms" depends on ARCH_REALVIEW diff --git a/drivers/soc/versatile/Makefile b/drivers/soc/versatile/Makefile index ad547435648e..cf612fe3a659 100644 --- a/drivers/soc/versatile/Makefile +++ b/drivers/soc/versatile/Makefile @@ -1 +1,2 @@ +obj-$(CONFIG_SOC_INTEGRATOR_CM) += soc-integrator.o obj-$(CONFIG_SOC_REALVIEW) += soc-realview.o diff --git a/drivers/soc/versatile/soc-integrator.c b/drivers/soc/versatile/soc-integrator.c new file mode 100644 index 000000000000..ccaa53739ab4 --- /dev/null +++ b/drivers/soc/versatile/soc-integrator.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2014 Linaro Ltd. + * + * Author: Linus Walleij <linus.walleij@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + */ +#include <linux/init.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/sys_soc.h> +#include <linux/platform_device.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/of.h> + +#define INTEGRATOR_HDR_ID_OFFSET 0x00 + +static u32 integrator_coreid; + +static const struct of_device_id integrator_cm_match[] = { + { .compatible = "arm,core-module-integrator", }, +}; + +static const char *integrator_arch_str(u32 id) +{ + switch ((id >> 16) & 0xff) { + case 0x00: + return "ASB little-endian"; + case 0x01: + return "AHB little-endian"; + case 0x03: + return "AHB-Lite system bus, bi-endian"; + case 0x04: + return "AHB"; + case 0x08: + return "AHB system bus, ASB processor bus"; + default: + return "Unknown"; + } +} + +static const char *integrator_fpga_str(u32 id) +{ + switch ((id >> 12) & 0xf) { + case 0x01: + return "XC4062"; + case 0x02: + return "XC4085"; + case 0x03: + return "XVC600"; + case 0x04: + return "EPM7256AE (Altera PLD)"; + default: + return "Unknown"; + } +} + +static ssize_t integrator_get_manf(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%02x\n", integrator_coreid >> 24); +} + +static struct device_attribute integrator_manf_attr = + __ATTR(manufacturer, S_IRUGO, integrator_get_manf, NULL); + +static ssize_t integrator_get_arch(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", integrator_arch_str(integrator_coreid)); +} + +static struct device_attribute integrator_arch_attr = + __ATTR(arch, S_IRUGO, integrator_get_arch, NULL); + +static ssize_t integrator_get_fpga(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", integrator_fpga_str(integrator_coreid)); +} + +static struct device_attribute integrator_fpga_attr = + __ATTR(fpga, S_IRUGO, integrator_get_fpga, NULL); + +static ssize_t integrator_get_build(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%02x\n", (integrator_coreid >> 4) & 0xFF); +} + +static struct device_attribute integrator_build_attr = + __ATTR(build, S_IRUGO, integrator_get_build, NULL); + +static int __init integrator_soc_init(void) +{ + static struct regmap *syscon_regmap; + struct soc_device *soc_dev; + struct soc_device_attribute *soc_dev_attr; + struct device_node *np; + struct device *dev; + u32 val; + int ret; + + np = of_find_matching_node(NULL, integrator_cm_match); + if (!np) + return -ENODEV; + + syscon_regmap = syscon_node_to_regmap(np); + if (IS_ERR(syscon_regmap)) + return PTR_ERR(syscon_regmap); + + ret = regmap_read(syscon_regmap, INTEGRATOR_HDR_ID_OFFSET, + &val); + if (ret) + return -ENODEV; + integrator_coreid = val; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENOMEM; + + soc_dev_attr->soc_id = "Integrator"; + soc_dev_attr->machine = "Integrator"; + soc_dev_attr->family = "Versatile"; + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree(soc_dev_attr); + return -ENODEV; + } + dev = soc_device_to_device(soc_dev); + + device_create_file(dev, &integrator_manf_attr); + device_create_file(dev, &integrator_arch_attr); + device_create_file(dev, &integrator_fpga_attr); + device_create_file(dev, &integrator_build_attr); + + dev_info(dev, "Detected ARM core module:\n"); + dev_info(dev, " Manufacturer: %02x\n", (val >> 24)); + dev_info(dev, " Architecture: %s\n", integrator_arch_str(val)); + dev_info(dev, " FPGA: %s\n", integrator_fpga_str(val)); + dev_info(dev, " Build: %02x\n", (val >> 4) & 0xFF); + dev_info(dev, " Rev: %c\n", ('A' + (val & 0x03))); + + return 0; +} +device_initcall(integrator_soc_init); |