diff options
author | Olof Johansson <olof@lixom.net> | 2015-04-03 20:44:28 +0300 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2015-04-03 20:44:28 +0300 |
commit | aabab880c5441cd39aef75fc1ecaa3125efb7a89 (patch) | |
tree | fa16a997164b3445bb3db233e229c56f9919ac49 /drivers | |
parent | 4580cb8a9812c3acc7a1663064d2a2800b3541bd (diff) | |
parent | 8590ca655a19f9e124b52bfbf28f14eb16a05bec (diff) | |
download | linux-aabab880c5441cd39aef75fc1ecaa3125efb7a89.tar.xz |
Merge tag 'at91-cleanup3' of git://git.kernel.org/pub/scm/linux/kernel/git/nferre/linux-at91 into next/drivers
Merge "at91: cleanup for 4.1 #3" from Nicolas Ferre:
Third batch of cleanup for 4.1:
- System Timer (ST) for at91rm9200 re-work (syscon/regmap):
- watchdog
- restart handler
- timer as a proper clocksource
=> remove mach dependency + cleanup
* tag 'at91-cleanup3' of git://git.kernel.org/pub/scm/linux/kernel/git/nferre/linux-at91: (54 commits)
ARM: at91: remove useless include
clocksource: atmel-st: remove mach/hardware dependency
clocksource: atmel-st: use syscon/regmap
ARM: at91: time: move the system timer driver to drivers/clocksource
ARM: at91: properly initialize timer
ARM: at91: at91rm9200: remove deprecated arm_pm_restart
watchdog: at91rm9200: implement restart handler
watchdog: at91rm9200: use the system timer syscon
mfd: syscon: Add atmel system timer registers definition
ARM: at91/dt: declare atmel,at91rm9200-st as a syscon
ARM: at91: remove old setup
ARM: at91: sama5d4: remove useless map_io
ARM: at91: sama5 use SoC detection infrastructure
ARM: at91: at91sam9: use SoC detection infrastructure
ARM: at91: at91rm9200 use SoC detection infrastructure
ARM: at91: add soc detection infrastructure
ARM: at91/dt: introduce atmel,<chip>-dbgu
ARM: at91: remove unused _matrix.h headers
ARM: at91: remove unused at91_ioremap_matrix and header
ARM: at91: remove NEED_MACH_IO_H
...
Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clocksource/Kconfig | 4 | ||||
-rw-r--r-- | drivers/clocksource/Makefile | 1 | ||||
-rw-r--r-- | drivers/clocksource/timer-atmel-st.c | 224 | ||||
-rw-r--r-- | drivers/pcmcia/Kconfig | 1 | ||||
-rw-r--r-- | drivers/pcmcia/at91_cf.c | 13 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 2 | ||||
-rw-r--r-- | drivers/watchdog/at91rm9200_wdt.c | 61 |
7 files changed, 292 insertions, 14 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 68161f7a07d6..168035883afa 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -143,6 +143,10 @@ config ATMEL_PIT select CLKSRC_OF if OF def_bool SOC_AT91SAM9 || SOC_SAMA5 +config ATMEL_ST + bool + select CLKSRC_OF + config CLKSRC_METAG_GENERIC def_bool y if METAG help diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 752d5c70b0ef..5b85f6adb258 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_CLKSRC_OF) += clksrc-of.o obj-$(CONFIG_ATMEL_PIT) += timer-atmel-pit.o +obj-$(CONFIG_ATMEL_ST) += timer-atmel-st.o obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o diff --git a/drivers/clocksource/timer-atmel-st.c b/drivers/clocksource/timer-atmel-st.c new file mode 100644 index 000000000000..1692e17e096b --- /dev/null +++ b/drivers/clocksource/timer-atmel-st.c @@ -0,0 +1,224 @@ +/* + * linux/arch/arm/mach-at91/at91rm9200_time.c + * + * Copyright (C) 2003 SAN People + * Copyright (C) 2003 ATMEL + * + * 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/kernel.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/clockchips.h> +#include <linux/export.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/atmel-st.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> + +static unsigned long last_crtr; +static u32 irqmask; +static struct clock_event_device clkevt; +static struct regmap *regmap_st; + +#define AT91_SLOW_CLOCK 32768 +#define RM9200_TIMER_LATCH ((AT91_SLOW_CLOCK + HZ/2) / HZ) + +/* + * The ST_CRTR is updated asynchronously to the master clock ... but + * the updates as seen by the CPU don't seem to be strictly monotonic. + * Waiting until we read the same value twice avoids glitching. + */ +static inline unsigned long read_CRTR(void) +{ + unsigned int x1, x2; + + regmap_read(regmap_st, AT91_ST_CRTR, &x1); + do { + regmap_read(regmap_st, AT91_ST_CRTR, &x2); + if (x1 == x2) + break; + x1 = x2; + } while (1); + return x1; +} + +/* + * IRQ handler for the timer. + */ +static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id) +{ + u32 sr; + + regmap_read(regmap_st, AT91_ST_SR, &sr); + sr &= irqmask; + + /* + * irqs should be disabled here, but as the irq is shared they are only + * guaranteed to be off if the timer irq is registered first. + */ + WARN_ON_ONCE(!irqs_disabled()); + + /* simulate "oneshot" timer with alarm */ + if (sr & AT91_ST_ALMS) { + clkevt.event_handler(&clkevt); + return IRQ_HANDLED; + } + + /* periodic mode should handle delayed ticks */ + if (sr & AT91_ST_PITS) { + u32 crtr = read_CRTR(); + + while (((crtr - last_crtr) & AT91_ST_CRTV) >= RM9200_TIMER_LATCH) { + last_crtr += RM9200_TIMER_LATCH; + clkevt.event_handler(&clkevt); + } + return IRQ_HANDLED; + } + + /* this irq is shared ... */ + return IRQ_NONE; +} + +static cycle_t read_clk32k(struct clocksource *cs) +{ + return read_CRTR(); +} + +static struct clocksource clk32k = { + .name = "32k_counter", + .rating = 150, + .read = read_clk32k, + .mask = CLOCKSOURCE_MASK(20), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void +clkevt32k_mode(enum clock_event_mode mode, struct clock_event_device *dev) +{ + unsigned int val; + + /* Disable and flush pending timer interrupts */ + regmap_write(regmap_st, AT91_ST_IDR, AT91_ST_PITS | AT91_ST_ALMS); + regmap_read(regmap_st, AT91_ST_SR, &val); + + last_crtr = read_CRTR(); + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + /* PIT for periodic irqs; fixed rate of 1/HZ */ + irqmask = AT91_ST_PITS; + regmap_write(regmap_st, AT91_ST_PIMR, RM9200_TIMER_LATCH); + break; + case CLOCK_EVT_MODE_ONESHOT: + /* ALM for oneshot irqs, set by next_event() + * before 32 seconds have passed + */ + irqmask = AT91_ST_ALMS; + regmap_write(regmap_st, AT91_ST_RTAR, last_crtr); + break; + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_RESUME: + irqmask = 0; + break; + } + regmap_write(regmap_st, AT91_ST_IER, irqmask); +} + +static int +clkevt32k_next_event(unsigned long delta, struct clock_event_device *dev) +{ + u32 alm; + int status = 0; + unsigned int val; + + BUG_ON(delta < 2); + + /* The alarm IRQ uses absolute time (now+delta), not the relative + * time (delta) in our calling convention. Like all clockevents + * using such "match" hardware, we have a race to defend against. + * + * Our defense here is to have set up the clockevent device so the + * delta is at least two. That way we never end up writing RTAR + * with the value then held in CRTR ... which would mean the match + * wouldn't trigger until 32 seconds later, after CRTR wraps. + */ + alm = read_CRTR(); + + /* Cancel any pending alarm; flush any pending IRQ */ + regmap_write(regmap_st, AT91_ST_RTAR, alm); + regmap_read(regmap_st, AT91_ST_SR, &val); + + /* Schedule alarm by writing RTAR. */ + alm += delta; + regmap_write(regmap_st, AT91_ST_RTAR, alm); + + return status; +} + +static struct clock_event_device clkevt = { + .name = "at91_tick", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .rating = 150, + .set_next_event = clkevt32k_next_event, + .set_mode = clkevt32k_mode, +}; + +/* + * ST (system timer) module supports both clockevents and clocksource. + */ +static void __init atmel_st_timer_init(struct device_node *node) +{ + unsigned int val; + int irq, ret; + + regmap_st = syscon_node_to_regmap(node); + if (IS_ERR(regmap_st)) + panic(pr_fmt("Unable to get regmap\n")); + + /* Disable all timer interrupts, and clear any pending ones */ + regmap_write(regmap_st, AT91_ST_IDR, + AT91_ST_PITS | AT91_ST_WDOVF | AT91_ST_RTTINC | AT91_ST_ALMS); + regmap_read(regmap_st, AT91_ST_SR, &val); + + /* Get the interrupts property */ + irq = irq_of_parse_and_map(node, 0); + if (!irq) + panic(pr_fmt("Unable to get IRQ from DT\n")); + + /* Make IRQs happen for the system timer */ + ret = request_irq(irq, at91rm9200_timer_interrupt, + IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL, + "at91_tick", regmap_st); + if (ret) + panic(pr_fmt("Unable to setup IRQ\n")); + + /* The 32KiHz "Slow Clock" (tick every 30517.58 nanoseconds) is used + * directly for the clocksource and all clockevents, after adjusting + * its prescaler from the 1 Hz default. + */ + regmap_write(regmap_st, AT91_ST_RTMR, 1); + + /* Setup timer clockevent, with minimum of two ticks (important!!) */ + clkevt.cpumask = cpumask_of(0); + clockevents_config_and_register(&clkevt, AT91_SLOW_CLOCK, + 2, AT91_ST_ALMV); + + /* register clocksource */ + clocksource_register_hz(&clk32k, AT91_SLOW_CLOCK); +} +CLOCKSOURCE_OF_DECLARE(atmel_st_timer, "atmel,at91rm9200-st", + atmel_st_timer_init); diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 3bb49252a098..075d5cd4c5ab 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -278,6 +278,7 @@ config BFIN_CFPCMCIA config AT91_CF tristate "AT91 CompactFlash Controller" + depends on PCI depends on PCMCIA && ARCH_AT91 depends on !ARCH_MULTIPLATFORM help diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index bfb799c7b343..e7775a41ae5d 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -317,13 +317,14 @@ static int at91_cf_probe(struct platform_device *pdev) } else cf->socket.pci_irq = nr_irqs + 1; - /* pcmcia layer only remaps "real" memory not iospace */ - cf->socket.io_offset = (unsigned long) devm_ioremap(&pdev->dev, - cf->phys_baseaddr + CF_IO_PHYS, SZ_2K); - if (!cf->socket.io_offset) { - status = -ENXIO; + /* + * pcmcia layer only remaps "real" memory not iospace + * io_offset is set to 0x10000 to avoid the check in static_find_io(). + * */ + cf->socket.io_offset = 0x10000; + status = pci_ioremap_io(0x10000, cf->phys_baseaddr + CF_IO_PHYS); + if (status) goto fail0a; - } /* reserve chip-select regions */ if (!devm_request_mem_region(&pdev->dev, io->start, resource_size(io), "at91_cf")) { diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 16f202350997..ce4f3a7f95fd 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -154,7 +154,7 @@ config ARM_SP805_WATCHDOG config AT91RM9200_WATCHDOG tristate "AT91RM9200 watchdog" - depends on SOC_AT91RM9200 + depends on SOC_AT91RM9200 && MFD_SYSCON help Watchdog timer embedded into AT91RM9200 chips. This will reboot your system when the timeout is reached. diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index d244112d5b6f..41cecb55766c 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -12,27 +12,32 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/bitops.h> +#include <linux/delay.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/io.h> #include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/mfd/syscon/atmel-st.h> #include <linux/miscdevice.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/regmap.h> #include <linux/types.h> #include <linux/watchdog.h> #include <linux/uaccess.h> #include <linux/of.h> #include <linux/of_device.h> -#include <mach/at91_st.h> #define WDT_DEFAULT_TIME 5 /* seconds */ #define WDT_MAX_TIME 256 /* seconds */ static int wdt_time = WDT_DEFAULT_TIME; static bool nowayout = WATCHDOG_NOWAYOUT; +static struct regmap *regmap_st; module_param(wdt_time, int, 0); MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default=" @@ -50,12 +55,33 @@ static unsigned long at91wdt_busy; /* ......................................................................... */ +static int at91rm9200_restart(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + /* + * Perform a hardware reset with the use of the Watchdog timer. + */ + regmap_write(regmap_st, AT91_ST_WDMR, + AT91_ST_RSTEN | AT91_ST_EXTEN | 1); + regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST); + + mdelay(2000); + + pr_emerg("Unable to restart system\n"); + return NOTIFY_DONE; +} + +static struct notifier_block at91rm9200_restart_nb = { + .notifier_call = at91rm9200_restart, + .priority = 192, +}; + /* * Disable the watchdog. */ static inline void at91_wdt_stop(void) { - at91_st_write(AT91_ST_WDMR, AT91_ST_EXTEN); + regmap_write(regmap_st, AT91_ST_WDMR, AT91_ST_EXTEN); } /* @@ -63,9 +89,9 @@ static inline void at91_wdt_stop(void) */ static inline void at91_wdt_start(void) { - at91_st_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN | + regmap_write(regmap_st, AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN | (((65536 * wdt_time) >> 8) & AT91_ST_WDV)); - at91_st_write(AT91_ST_CR, AT91_ST_WDRST); + regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST); } /* @@ -73,7 +99,7 @@ static inline void at91_wdt_start(void) */ static inline void at91_wdt_reload(void) { - at91_st_write(AT91_ST_CR, AT91_ST_WDRST); + regmap_write(regmap_st, AT91_ST_CR, AT91_ST_WDRST); } /* ......................................................................... */ @@ -203,16 +229,32 @@ static struct miscdevice at91wdt_miscdev = { static int at91wdt_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct device *parent; int res; if (at91wdt_miscdev.parent) return -EBUSY; at91wdt_miscdev.parent = &pdev->dev; + parent = dev->parent; + if (!parent) { + dev_err(dev, "no parent\n"); + return -ENODEV; + } + + regmap_st = syscon_node_to_regmap(parent->of_node); + if (!regmap_st) + return -ENODEV; + res = misc_register(&at91wdt_miscdev); if (res) return res; + res = register_restart_handler(&at91rm9200_restart_nb); + if (res) + dev_warn(dev, "failed to register restart handler\n"); + pr_info("AT91 Watchdog Timer enabled (%d seconds%s)\n", wdt_time, nowayout ? ", nowayout" : ""); return 0; @@ -220,8 +262,13 @@ static int at91wdt_probe(struct platform_device *pdev) static int at91wdt_remove(struct platform_device *pdev) { + struct device *dev = &pdev->dev; int res; + res = unregister_restart_handler(&at91rm9200_restart_nb); + if (res) + dev_warn(dev, "failed to unregister restart handler\n"); + res = misc_deregister(&at91wdt_miscdev); if (!res) at91wdt_miscdev.parent = NULL; @@ -267,7 +314,7 @@ static struct platform_driver at91wdt_driver = { .suspend = at91wdt_suspend, .resume = at91wdt_resume, .driver = { - .name = "at91_wdt", + .name = "atmel_st_watchdog", .of_match_table = at91_wdt_dt_ids, }, }; @@ -296,4 +343,4 @@ module_exit(at91_wdt_exit); MODULE_AUTHOR("Andrew Victor"); MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:at91_wdt"); +MODULE_ALIAS("platform:atmel_st_watchdog"); |