mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 10:49:33 +00:00
![Gaël PORTAY](/assets/img/avatar_default.png)
Shutdown properly the timer counter block by masking interruptions. Otherwise, a segmentation may happen when kexec-ing a new kernel (see backtrace below). An interruption may happen before the handler is set, leading to a kernel segmentation fault. Furthermore, we make sure the interruptions are masked when the driver is initialized. This will prevent freshly kexec-ed kernel from crashing when launched from a kernel which does not properly mask interruptions at shutdown. The backtrace below happened after kexec-ing a new kernel, from a kernel that did not shut down properly leaving interruptions unmasked. Unable to handle kernel NULL pointer dereference at virtual address 00000000 pgd = c0004000 [00000000] *pgd=00000000 Internal error: Oops: 80000005 [#1] ARM Modules linked in: CPU: 0 PID: 1 Comm: swapper Not tainted 3.16.0+ #144 task: c1828aa0 ti: c182a000 task.ti: c182a000 PC is at 0x0 LR is at ch2_irq+0x28/0x30 pc : [<00000000>] lr : [<c01db904>] psr: 000000d3 sp : c182bd38 ip : c182bd48 fp : c182bd44 r10: c0373390 r9 : c1825b00 r8 : 60000053 r7 : 00000000 r6 : 00000000 r5 : 00000013 r4 : c036e800 r3 : 00000000 r2 : 00002004 r1 : c036e760 r0 : c036e760 Flags: nzcv IRQs off FIQs off Mode SVC_32 ISA ARM Segment kernel Control: 0005317f Table: 20004000 DAC: 00000017 Process swapper (pid: 1, stack limit = 0xc182a1c0) Stack: (0xc182bd38 to 0xc182c000) bd20: c182bd7c c182bd48 bd40: c0045430 c01db8ec 00000000 c18c6f40 c182bd74 c1825b00 c035cec4 00000000 bd60: c182be2c 60000053 c1825b34 00000000 c182bd94 c182bd80 c0045570 c0045408 bd80: 00000000 c1825b00 c182bdac c182bd98 c0047f34 c0045550 00000013 c036619c bda0: c182bdc4 c182bdb0 c0044da4 c0047e98 0000007f 00000013 c182bde4 c182bdc8 bdc0: c0009e34 c0044d8c fefff000 c0046728 60000053 ffffffff c182bdf4 c182bde8 bde0: c00086a8 c0009ddc c182be74 c182bdf8 c000cb80 c0008674 00000000 00000013 be00: 00000000 00014200 c1825b00 c036e800 00000013 c035ed98 60000053 c1825b34 be20: 00000000 c182be74 c182be20 c182be40 c0047994 c0046728 60000053 ffffffff be40: 00000013 c036e800 c182be64 c1825b00 00000013 c036e800 c035ed98 c03874bc be60: 00000004 c036e700 c182be94 c182be78 c004689c c0046398 c036e760 c18c6080 be80: 00000000 c035ed10 c182bedc c182be98 c0348b08 c004684c 0000000c c034dac8 bea0: 004c4b3f c028c338 c036e760 00000013 c014ecc8 c18e67e0 c035b9c0 c0348884 bec0: c035b9c0 c182a020 00000000 00000000 c182bf54 c182bee0 c00089fc c0348894 bee0: c00da51c c1ffcc78 c182bf0c c182bef8 c002d100 c002d09c c1ffcc78 00000000 bf00: c182bf54 c182bf10 c002d308 c0336570 c182bf3c c0334e44 00000003 00000003 bf20: 00000030 c0334b44 c0044d74 00000003 00000003 c034dac8 c0350a94 c0373440 bf40: c0373440 00000030 c182bf94 c182bf58 c0336d24 c000890c 00000003 00000003 bf60: c0336560 c182bf64 c182bf64 6e616e0d 00000000 c0272fc8 00000000 00000000 bf80: 00000000 00000000 c182bfac c182bf98 c0272fd8 c0336bd8 c182a000 00000000 bfa0: 00000000 c182bfb0 c00095d0 c0272fd8 00000000 00000000 00000000 00000000 bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 bfe0: 00000000 00000000 00000000 00000000 00000013 00000000 374d27cd 33cc33e4 Backtrace: [<c01db8dc>] (ch2_irq) from [<c0045430>] (handle_irq_event_percpu+0x38/0x148) [<c00453f8>] (handle_irq_event_percpu) from [<c0045570>] (handle_irq_event+0x30/0x40) r10:00000000 r9:c1825b34 r8:60000053 r7:c182be2c r6:00000000 r5:c035cec4 r4:c1825b00 [<c0045540>] (handle_irq_event) from [<c0047f34>] (handle_fasteoi_irq+0xac/0x11c) r4:c1825b00 r3:00000000 [<c0047e88>] (handle_fasteoi_irq) from [<c0044da4>] (generic_handle_irq+0x28/0x38) r5:c036619c r4:00000013 [<c0044d7c>] (generic_handle_irq) from [<c0009e34>] (handle_IRQ+0x68/0x88) r4:00000013 r3:0000007f [<c0009dcc>] (handle_IRQ) from [<c00086a8>] (at91_aic_handle_irq+0x44/0x4c) r6:ffffffff r5:60000053 r4:c0046728 r3:fefff000 [<c0008664>] (at91_aic_handle_irq) from [<c000cb80>] (__irq_svc+0x40/0x4c) Exception stack(0xc182bdf8 to 0xc182be40) bde0: 00000000 00000013 be00: 00000000 00014200 c1825b00 c036e800 00000013 c035ed98 60000053 c1825b34 be20: 00000000 c182be74 c182be20 c182be40 c0047994 c0046728 60000053 ffffffff [<c0046388>] (__setup_irq) from [<c004689c>] (setup_irq+0x60/0x8c) r10:c036e700 r9:00000004 r8:c03874bc r7:c035ed98 r6:c036e800 r5:00000013 r4:c1825b00 [<c004683c>] (setup_irq) from [<c0348b08>] (tcb_clksrc_init+0x284/0x31c) r6:c035ed10 r5:00000000 r4:c18c6080 r3:c036e760 [<c0348884>] (tcb_clksrc_init) from [<c00089fc>] (do_one_initcall+0x100/0x1b4) r10:00000000 r9:00000000 r8:c182a020 r7:c035b9c0 r6:c0348884 r5:c035b9c0 r4:c18e67e0 [<c00088fc>] (do_one_initcall) from [<c0336d24>] (kernel_init_freeable+0x15c/0x224) r9:00000030 r8:c0373440 r7:c0373440 r6:c0350a94 r5:c034dac8 r4:00000003 [<c0336bc8>] (kernel_init_freeable) from [<c0272fd8>] (kernel_init+0x10/0xec) r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c0272fc8 r4:00000000 [<c0272fc8>] (kernel_init) from [<c00095d0>] (ret_from_fork+0x14/0x24) r4:00000000 r3:c182a000 Code: bad PC value ---[ end trace 5b30f0017e282e47 ]--- Kernel panic - not syncing: Fatal exception in interrupt Signed-off-by: Gaël PORTAY <gael.portay@gmail.com> Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com> Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
269 lines
11 KiB
C
269 lines
11 KiB
C
/*
|
|
* Timer/Counter Unit (TC) registers.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef ATMEL_TC_H
|
|
#define ATMEL_TC_H
|
|
|
|
#include <linux/compiler.h>
|
|
#include <linux/list.h>
|
|
|
|
/*
|
|
* Many 32-bit Atmel SOCs include one or more TC blocks, each of which holds
|
|
* three general-purpose 16-bit timers. These timers share one register bank.
|
|
* Depending on the SOC, each timer may have its own clock and IRQ, or those
|
|
* may be shared by the whole TC block.
|
|
*
|
|
* These TC blocks may have up to nine external pins: TCLK0..2 signals for
|
|
* clocks or clock gates, and per-timer TIOA and TIOB signals used for PWM
|
|
* or triggering. Those pins need to be set up for use with the TC block,
|
|
* else they will be used as GPIOs or for a different controller.
|
|
*
|
|
* Although we expect each TC block to have a platform_device node, those
|
|
* nodes are not what drivers bind to. Instead, they ask for a specific
|
|
* TC block, by number ... which is a common approach on systems with many
|
|
* timers. Then they use clk_get() and platform_get_irq() to get clock and
|
|
* IRQ resources.
|
|
*/
|
|
|
|
struct clk;
|
|
|
|
/**
|
|
* struct atmel_tcb_config - SoC data for a Timer/Counter Block
|
|
* @counter_width: size in bits of a timer counter register
|
|
*/
|
|
struct atmel_tcb_config {
|
|
size_t counter_width;
|
|
};
|
|
|
|
/**
|
|
* struct atmel_tc - information about a Timer/Counter Block
|
|
* @pdev: physical device
|
|
* @regs: mapping through which the I/O registers can be accessed
|
|
* @id: block id
|
|
* @tcb_config: configuration data from SoC
|
|
* @irq: irq for each of the three channels
|
|
* @clk: internal clock source for each of the three channels
|
|
* @node: list node, for tclib internal use
|
|
* @allocated: if already used, for tclib internal use
|
|
*
|
|
* On some platforms, each TC channel has its own clocks and IRQs,
|
|
* while on others, all TC channels share the same clock and IRQ.
|
|
* Drivers should clk_enable() all the clocks they need even though
|
|
* all the entries in @clk may point to the same physical clock.
|
|
* Likewise, drivers should request irqs independently for each
|
|
* channel, but they must use IRQF_SHARED in case some of the entries
|
|
* in @irq are actually the same IRQ.
|
|
*/
|
|
struct atmel_tc {
|
|
struct platform_device *pdev;
|
|
void __iomem *regs;
|
|
int id;
|
|
const struct atmel_tcb_config *tcb_config;
|
|
int irq[3];
|
|
struct clk *clk[3];
|
|
struct list_head node;
|
|
bool allocated;
|
|
};
|
|
|
|
extern struct atmel_tc *atmel_tc_alloc(unsigned block);
|
|
extern void atmel_tc_free(struct atmel_tc *tc);
|
|
|
|
/* platform-specific ATMEL_TC_TIMER_CLOCKx divisors (0 means 32KiHz) */
|
|
extern const u8 atmel_tc_divisors[5];
|
|
|
|
|
|
/*
|
|
* Two registers have block-wide controls. These are: configuring the three
|
|
* "external" clocks (or event sources) used by the timer channels; and
|
|
* synchronizing the timers by resetting them all at once.
|
|
*
|
|
* "External" can mean "external to chip" using the TCLK0, TCLK1, or TCLK2
|
|
* signals. Or, it can mean "external to timer", using the TIOA output from
|
|
* one of the other two timers that's being run in waveform mode.
|
|
*/
|
|
|
|
#define ATMEL_TC_BCR 0xc0 /* TC Block Control Register */
|
|
#define ATMEL_TC_SYNC (1 << 0) /* synchronize timers */
|
|
|
|
#define ATMEL_TC_BMR 0xc4 /* TC Block Mode Register */
|
|
#define ATMEL_TC_TC0XC0S (3 << 0) /* external clock 0 source */
|
|
#define ATMEL_TC_TC0XC0S_TCLK0 (0 << 0)
|
|
#define ATMEL_TC_TC0XC0S_NONE (1 << 0)
|
|
#define ATMEL_TC_TC0XC0S_TIOA1 (2 << 0)
|
|
#define ATMEL_TC_TC0XC0S_TIOA2 (3 << 0)
|
|
#define ATMEL_TC_TC1XC1S (3 << 2) /* external clock 1 source */
|
|
#define ATMEL_TC_TC1XC1S_TCLK1 (0 << 2)
|
|
#define ATMEL_TC_TC1XC1S_NONE (1 << 2)
|
|
#define ATMEL_TC_TC1XC1S_TIOA0 (2 << 2)
|
|
#define ATMEL_TC_TC1XC1S_TIOA2 (3 << 2)
|
|
#define ATMEL_TC_TC2XC2S (3 << 4) /* external clock 2 source */
|
|
#define ATMEL_TC_TC2XC2S_TCLK2 (0 << 4)
|
|
#define ATMEL_TC_TC2XC2S_NONE (1 << 4)
|
|
#define ATMEL_TC_TC2XC2S_TIOA0 (2 << 4)
|
|
#define ATMEL_TC_TC2XC2S_TIOA1 (3 << 4)
|
|
|
|
|
|
/*
|
|
* Each TC block has three "channels", each with one counter and controls.
|
|
*
|
|
* Note that the semantics of ATMEL_TC_TIMER_CLOCKx (input clock selection
|
|
* when it's not "external") is silicon-specific. AT91 platforms use one
|
|
* set of definitions; AVR32 platforms use a different set. Don't hard-wire
|
|
* such knowledge into your code, use the global "atmel_tc_divisors" ...
|
|
* where index N is the divisor for clock N+1, else zero to indicate it uses
|
|
* the 32 KiHz clock.
|
|
*
|
|
* The timers can be chained in various ways, and operated in "waveform"
|
|
* generation mode (including PWM) or "capture" mode (to time events). In
|
|
* both modes, behavior can be configured in many ways.
|
|
*
|
|
* Each timer has two I/O pins, TIOA and TIOB. Waveform mode uses TIOA as a
|
|
* PWM output, and TIOB as either another PWM or as a trigger. Capture mode
|
|
* uses them only as inputs.
|
|
*/
|
|
#define ATMEL_TC_CHAN(idx) ((idx)*0x40)
|
|
#define ATMEL_TC_REG(idx, reg) (ATMEL_TC_CHAN(idx) + ATMEL_TC_ ## reg)
|
|
|
|
#define ATMEL_TC_CCR 0x00 /* Channel Control Register */
|
|
#define ATMEL_TC_CLKEN (1 << 0) /* clock enable */
|
|
#define ATMEL_TC_CLKDIS (1 << 1) /* clock disable */
|
|
#define ATMEL_TC_SWTRG (1 << 2) /* software trigger */
|
|
|
|
#define ATMEL_TC_CMR 0x04 /* Channel Mode Register */
|
|
|
|
/* Both modes share some CMR bits */
|
|
#define ATMEL_TC_TCCLKS (7 << 0) /* clock source */
|
|
#define ATMEL_TC_TIMER_CLOCK1 (0 << 0)
|
|
#define ATMEL_TC_TIMER_CLOCK2 (1 << 0)
|
|
#define ATMEL_TC_TIMER_CLOCK3 (2 << 0)
|
|
#define ATMEL_TC_TIMER_CLOCK4 (3 << 0)
|
|
#define ATMEL_TC_TIMER_CLOCK5 (4 << 0)
|
|
#define ATMEL_TC_XC0 (5 << 0)
|
|
#define ATMEL_TC_XC1 (6 << 0)
|
|
#define ATMEL_TC_XC2 (7 << 0)
|
|
#define ATMEL_TC_CLKI (1 << 3) /* clock invert */
|
|
#define ATMEL_TC_BURST (3 << 4) /* clock gating */
|
|
#define ATMEL_TC_GATE_NONE (0 << 4)
|
|
#define ATMEL_TC_GATE_XC0 (1 << 4)
|
|
#define ATMEL_TC_GATE_XC1 (2 << 4)
|
|
#define ATMEL_TC_GATE_XC2 (3 << 4)
|
|
#define ATMEL_TC_WAVE (1 << 15) /* true = Waveform mode */
|
|
|
|
/* CAPTURE mode CMR bits */
|
|
#define ATMEL_TC_LDBSTOP (1 << 6) /* counter stops on RB load */
|
|
#define ATMEL_TC_LDBDIS (1 << 7) /* counter disable on RB load */
|
|
#define ATMEL_TC_ETRGEDG (3 << 8) /* external trigger edge */
|
|
#define ATMEL_TC_ETRGEDG_NONE (0 << 8)
|
|
#define ATMEL_TC_ETRGEDG_RISING (1 << 8)
|
|
#define ATMEL_TC_ETRGEDG_FALLING (2 << 8)
|
|
#define ATMEL_TC_ETRGEDG_BOTH (3 << 8)
|
|
#define ATMEL_TC_ABETRG (1 << 10) /* external trigger is TIOA? */
|
|
#define ATMEL_TC_CPCTRG (1 << 14) /* RC compare trigger enable */
|
|
#define ATMEL_TC_LDRA (3 << 16) /* RA loading edge (of TIOA) */
|
|
#define ATMEL_TC_LDRA_NONE (0 << 16)
|
|
#define ATMEL_TC_LDRA_RISING (1 << 16)
|
|
#define ATMEL_TC_LDRA_FALLING (2 << 16)
|
|
#define ATMEL_TC_LDRA_BOTH (3 << 16)
|
|
#define ATMEL_TC_LDRB (3 << 18) /* RB loading edge (of TIOA) */
|
|
#define ATMEL_TC_LDRB_NONE (0 << 18)
|
|
#define ATMEL_TC_LDRB_RISING (1 << 18)
|
|
#define ATMEL_TC_LDRB_FALLING (2 << 18)
|
|
#define ATMEL_TC_LDRB_BOTH (3 << 18)
|
|
|
|
/* WAVEFORM mode CMR bits */
|
|
#define ATMEL_TC_CPCSTOP (1 << 6) /* RC compare stops counter */
|
|
#define ATMEL_TC_CPCDIS (1 << 7) /* RC compare disables counter */
|
|
#define ATMEL_TC_EEVTEDG (3 << 8) /* external event edge */
|
|
#define ATMEL_TC_EEVTEDG_NONE (0 << 8)
|
|
#define ATMEL_TC_EEVTEDG_RISING (1 << 8)
|
|
#define ATMEL_TC_EEVTEDG_FALLING (2 << 8)
|
|
#define ATMEL_TC_EEVTEDG_BOTH (3 << 8)
|
|
#define ATMEL_TC_EEVT (3 << 10) /* external event source */
|
|
#define ATMEL_TC_EEVT_TIOB (0 << 10)
|
|
#define ATMEL_TC_EEVT_XC0 (1 << 10)
|
|
#define ATMEL_TC_EEVT_XC1 (2 << 10)
|
|
#define ATMEL_TC_EEVT_XC2 (3 << 10)
|
|
#define ATMEL_TC_ENETRG (1 << 12) /* external event is trigger */
|
|
#define ATMEL_TC_WAVESEL (3 << 13) /* waveform type */
|
|
#define ATMEL_TC_WAVESEL_UP (0 << 13)
|
|
#define ATMEL_TC_WAVESEL_UPDOWN (1 << 13)
|
|
#define ATMEL_TC_WAVESEL_UP_AUTO (2 << 13)
|
|
#define ATMEL_TC_WAVESEL_UPDOWN_AUTO (3 << 13)
|
|
#define ATMEL_TC_ACPA (3 << 16) /* RA compare changes TIOA */
|
|
#define ATMEL_TC_ACPA_NONE (0 << 16)
|
|
#define ATMEL_TC_ACPA_SET (1 << 16)
|
|
#define ATMEL_TC_ACPA_CLEAR (2 << 16)
|
|
#define ATMEL_TC_ACPA_TOGGLE (3 << 16)
|
|
#define ATMEL_TC_ACPC (3 << 18) /* RC compare changes TIOA */
|
|
#define ATMEL_TC_ACPC_NONE (0 << 18)
|
|
#define ATMEL_TC_ACPC_SET (1 << 18)
|
|
#define ATMEL_TC_ACPC_CLEAR (2 << 18)
|
|
#define ATMEL_TC_ACPC_TOGGLE (3 << 18)
|
|
#define ATMEL_TC_AEEVT (3 << 20) /* external event changes TIOA */
|
|
#define ATMEL_TC_AEEVT_NONE (0 << 20)
|
|
#define ATMEL_TC_AEEVT_SET (1 << 20)
|
|
#define ATMEL_TC_AEEVT_CLEAR (2 << 20)
|
|
#define ATMEL_TC_AEEVT_TOGGLE (3 << 20)
|
|
#define ATMEL_TC_ASWTRG (3 << 22) /* software trigger changes TIOA */
|
|
#define ATMEL_TC_ASWTRG_NONE (0 << 22)
|
|
#define ATMEL_TC_ASWTRG_SET (1 << 22)
|
|
#define ATMEL_TC_ASWTRG_CLEAR (2 << 22)
|
|
#define ATMEL_TC_ASWTRG_TOGGLE (3 << 22)
|
|
#define ATMEL_TC_BCPB (3 << 24) /* RB compare changes TIOB */
|
|
#define ATMEL_TC_BCPB_NONE (0 << 24)
|
|
#define ATMEL_TC_BCPB_SET (1 << 24)
|
|
#define ATMEL_TC_BCPB_CLEAR (2 << 24)
|
|
#define ATMEL_TC_BCPB_TOGGLE (3 << 24)
|
|
#define ATMEL_TC_BCPC (3 << 26) /* RC compare changes TIOB */
|
|
#define ATMEL_TC_BCPC_NONE (0 << 26)
|
|
#define ATMEL_TC_BCPC_SET (1 << 26)
|
|
#define ATMEL_TC_BCPC_CLEAR (2 << 26)
|
|
#define ATMEL_TC_BCPC_TOGGLE (3 << 26)
|
|
#define ATMEL_TC_BEEVT (3 << 28) /* external event changes TIOB */
|
|
#define ATMEL_TC_BEEVT_NONE (0 << 28)
|
|
#define ATMEL_TC_BEEVT_SET (1 << 28)
|
|
#define ATMEL_TC_BEEVT_CLEAR (2 << 28)
|
|
#define ATMEL_TC_BEEVT_TOGGLE (3 << 28)
|
|
#define ATMEL_TC_BSWTRG (3 << 30) /* software trigger changes TIOB */
|
|
#define ATMEL_TC_BSWTRG_NONE (0 << 30)
|
|
#define ATMEL_TC_BSWTRG_SET (1 << 30)
|
|
#define ATMEL_TC_BSWTRG_CLEAR (2 << 30)
|
|
#define ATMEL_TC_BSWTRG_TOGGLE (3 << 30)
|
|
|
|
#define ATMEL_TC_CV 0x10 /* counter Value */
|
|
#define ATMEL_TC_RA 0x14 /* register A */
|
|
#define ATMEL_TC_RB 0x18 /* register B */
|
|
#define ATMEL_TC_RC 0x1c /* register C */
|
|
|
|
#define ATMEL_TC_SR 0x20 /* status (read-only) */
|
|
/* Status-only flags */
|
|
#define ATMEL_TC_CLKSTA (1 << 16) /* clock enabled */
|
|
#define ATMEL_TC_MTIOA (1 << 17) /* TIOA mirror */
|
|
#define ATMEL_TC_MTIOB (1 << 18) /* TIOB mirror */
|
|
|
|
#define ATMEL_TC_IER 0x24 /* interrupt enable (write-only) */
|
|
#define ATMEL_TC_IDR 0x28 /* interrupt disable (write-only) */
|
|
#define ATMEL_TC_IMR 0x2c /* interrupt mask (read-only) */
|
|
|
|
/* Status and IRQ flags */
|
|
#define ATMEL_TC_COVFS (1 << 0) /* counter overflow */
|
|
#define ATMEL_TC_LOVRS (1 << 1) /* load overrun */
|
|
#define ATMEL_TC_CPAS (1 << 2) /* RA compare */
|
|
#define ATMEL_TC_CPBS (1 << 3) /* RB compare */
|
|
#define ATMEL_TC_CPCS (1 << 4) /* RC compare */
|
|
#define ATMEL_TC_LDRAS (1 << 5) /* RA loading */
|
|
#define ATMEL_TC_LDRBS (1 << 6) /* RB loading */
|
|
#define ATMEL_TC_ETRGS (1 << 7) /* external trigger */
|
|
#define ATMEL_TC_ALL_IRQ (ATMEL_TC_COVFS | ATMEL_TC_LOVRS | \
|
|
ATMEL_TC_CPAS | ATMEL_TC_CPBS | \
|
|
ATMEL_TC_CPCS | ATMEL_TC_LDRAS | \
|
|
ATMEL_TC_LDRBS | ATMEL_TC_ETRGS) \
|
|
/* all IRQs */
|
|
|
|
#endif
|