From patchwork Sun Oct 14 22:14:52 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Domenico Andreoli X-Patchwork-Id: 1591851 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork2.kernel.org (Postfix) with ESMTP id 91A62E00AD for ; Sun, 14 Oct 2012 22:33:28 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TNWie-0000px-4X; Sun, 14 Oct 2012 22:31:36 +0000 Received: from mail-wi0-f171.google.com ([209.85.212.171]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1TNWhb-0000VD-OM for linux-arm-kernel@lists.infradead.org; Sun, 14 Oct 2012 22:30:35 +0000 Received: by mail-wi0-f171.google.com with SMTP id hj13so1018087wib.0 for ; Sun, 14 Oct 2012 15:30:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:message-id:user-agent:date:from:to:cc:subject:references :content-disposition; bh=jpQ+ws4oAoLLys7HKzngEjpS9bxAkgrbKDtSTI0W9nI=; b=dtiBXuPMca/E126o2TYBCEc/t+/26Lq4XyIYEQo0e9IpIgE1DO7wYgmCeSR69DAo20 SQUUi7tSdvVxw8NJgiGc+ReqpdyiQ9XiDT9gQb9HK0bfHhh88mdOWnY4vtT+866t67dF x/xkiJdedQ1sGoSv7QjQ6+w4/mufEQpNFbdZ2W4tys8j6HXhFflg1DuSFzZ08xXNhkSl rXCPHjDF6KoY/aoRpFw/GZW7pOCAiC8+6+fmiT+5wUTL1VyJyr76jvx+T+AZJSUjHaTv Dbu1oMO6TBQQg5XXMxdt56j7nyy189OPGd3IGAVRB1moUbhQZglt7rplUm+Pb+zs54eT 0nJA== Received: by 10.216.45.144 with SMTP id p16mr6210528web.170.1350253827796; Sun, 14 Oct 2012 15:30:27 -0700 (PDT) Received: from raptus.dandreoli.com (178-85-163-250.dynamic.upc.nl. [178.85.163.250]) by mx.google.com with ESMTPS id ei1sm12328143wid.7.2012.10.14.15.30.25 (version=TLSv1/SSLv3 cipher=OTHER); Sun, 14 Oct 2012 15:30:26 -0700 (PDT) Received: by raptus.dandreoli.com (Postfix, from userid 1000) id 713033F6209; Mon, 15 Oct 2012 00:30:24 +0200 (CEST) Message-Id: <20121014223024.144323673@gmail.com> User-Agent: quilt/0.60-1 Date: Mon, 15 Oct 2012 00:14:52 +0200 From: Domenico Andreoli To: linux-arm-kernel@lists.infradead.org Subject: [PATCH v2 2/5] ARM: bcm476x: Add system timer References: <20121014221450.866288977@gmail.com> Content-Disposition: inline; filename=arm-bcm476x-add-system-timer.patch X-Spam-Note: CRM114 invocation failed X-Spam-Score: -2.7 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.7 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [209.85.212.171 listed in list.dnswl.org] 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (cavokz[at]gmail.com) -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature Cc: Domenico Andreoli , Olof Johansson X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org From: Domenico Andreoli System timer implementation for the BCM476x SoCs. v2: * match DT with the specific product names instead of the generic bcm476x * accept only 24MHz as clock frequency, the only one I tested. Other values require further investigation and tests. v1: * initial release Signed-off-by: Domenico Andreoli Acked-by: Olof Johansson --- Documentation/devicetree/bindings/timer/brcm,bcm476x-system-timer.txt | 24 + arch/arm/boot/dts/bcm4760.dtsi | 8 + arch/arm/boot/dts/bcm4761.dtsi | 8 + arch/arm/mach-bcm476x/bcm476x.c | 10 - drivers/clocksource/Makefile | 1 + drivers/clocksource/bcm476x_timer.c | 183 ++++++++++ include/linux/bcm476x_timer.h | 24 + 7 files changed, 249 insertions(+), 9 deletions(-) Index: b/Documentation/devicetree/bindings/timer/brcm,bcm476x-system-timer.txt =================================================================== --- /dev/null +++ b/Documentation/devicetree/bindings/timer/brcm,bcm476x-system-timer.txt @@ -0,0 +1,24 @@ +Broadcom BCM4760 and BCM4761 System Timer device tree bindings +-------------------------------------------------------------- + +The BCM476x Timer peripheral provides either two or four 32-bit timer +channels. Three timer blocks are available at 0xba000, 0xbb000 and +0xd1000. The first two provide four channels, the last (in the AON - +Always ON power domain) provides only two. + +Required properties: + +- compatible : should be "brcm,bcm4760-system-timer" or + "brcm,bcm4761-system-timer". +- reg : Specifies base physical address and size of the registers. +- interrupts : A list of 2 or 4 interrupt sinks; one per timer channel. +- clock-frequency : The frequency of the clock that drives the counter, in Hz. + +Example: + +timer@ba000 { + compatible = "brcm,bcm4760-system-timer"; + reg = <0xba000 0x1000>; + interrupts = <4>, <11>; + clock-frequency = <24000000>; +}; Index: b/arch/arm/boot/dts/bcm4760.dtsi =================================================================== --- a/arch/arm/boot/dts/bcm4760.dtsi +++ b/arch/arm/boot/dts/bcm4760.dtsi @@ -10,6 +10,14 @@ #size-cells = <1>; ranges; + timer@ba000 { + compatible = "brcm,bcm4760-system-timer"; + reg = <0xba000 0x1000>; + interrupt-parent = <&vic0>; + interrupts = <4>, <11>; + clock-frequency = <24000000>; + }; + vic0: interrupt-controller@80000 { compatible = "brcm,bcm4760-pl192", "arm,pl192-vic", "arm,primecell"; reg = <0x80000 0x1000>; Index: b/arch/arm/boot/dts/bcm4761.dtsi =================================================================== --- a/arch/arm/boot/dts/bcm4761.dtsi +++ b/arch/arm/boot/dts/bcm4761.dtsi @@ -10,6 +10,14 @@ #size-cells = <1>; ranges; + timer@ba000 { + compatible = "brcm,bcm4761-system-timer"; + reg = <0xba000 0x1000>; + interrupt-parent = <&vic0>; + interrupts = <4>, <11>; + clock-frequency = <24000000>; + }; + vic0: interrupt-controller@80000 { compatible = "brcm,bcm4761-pl192", "arm,pl192-vic", "arm,primecell"; reg = <0x80000 0x1000>; Index: b/arch/arm/mach-bcm476x/bcm476x.c =================================================================== --- a/arch/arm/mach-bcm476x/bcm476x.c +++ b/arch/arm/mach-bcm476x/bcm476x.c @@ -17,11 +17,11 @@ #include #include #include +#include #include #include #include -#include #define BCM476X_PERIPH_PHYS 0x00080000 #define BCM476X_PERIPH_VIRT IOMEM(0xd0080000) @@ -60,14 +60,6 @@ static void __init bcm476x_init_irq(void of_irq_init(vic_of_match); } -static void __init bcm476x_timer_init(void) -{ -} - -static struct sys_timer bcm476x_timer = { - .init = bcm476x_timer_init -}; - static const char * const bcm476x_compat[] __initconst = { "brcm,bcm4760", "brcm,bcm4761", Index: b/drivers/clocksource/Makefile =================================================================== --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -14,5 +14,6 @@ obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_ obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o +obj-$(CONFIG_ARCH_BCM476X) += bcm476x_timer.o obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o Index: b/drivers/clocksource/bcm476x_timer.c =================================================================== --- /dev/null +++ b/drivers/clocksource/bcm476x_timer.c @@ -0,0 +1,183 @@ +/* + * Broadcom BCM476x SoCs system timer + * + * Copyright (C) 2012 Domenico Andreoli + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMER_LOAD_OFFSET 0x00 /* load */ +#define TIMER_VALUE_OFFSET 0x04 /* value */ +#define TIMER_CONTROL_OFFSET 0x08 /* control */ +#define TIMER_INTCLR_OFFSET 0x0c /* interrupt clear */ +#define TIMER_RIS_OFFSET 0x10 /* raw interrupt */ +#define TIMER_MIS_OFFSET 0x14 /* masked interrupt status */ +#define TIMER_BGLOAD_OFFSET 0x18 /* background load */ + +#define TIMER_CTRL_ONESHOTMODE BIT(0) /* One shot mode */ +#define TIMER_CTRL_32BIT BIT(1) /* 32-bit counter mode */ +#define TIMER_CTRL_IE BIT(5) /* Interrupt enable */ +#define TIMER_CTRL_PERIODIC BIT(6) /* Periodic mode */ +#define TIMER_CTRL_EN BIT(7) /* Timer enable */ +#define TIMER_CTRL_CLK2 BIT(9) /* Clock 2 selected */ +#define TIMER_CTRL_PREBY16 (1 << 2) /* prescale divide by 16 */ +#define TIMER_CTRL_PREBY256 (2 << 2) /* prescale divide by 256 */ + +struct bcm476x_timer { + void __iomem *base; + struct clock_event_device evt; + struct irqaction act; +}; + +static inline void __iomem *to_load(struct bcm476x_timer *timer) +{ + return timer->base + TIMER_LOAD_OFFSET; +} + +static inline void __iomem *to_control(struct bcm476x_timer *timer) +{ + return timer->base + TIMER_CONTROL_OFFSET; +} + +static inline void __iomem *to_intclr(struct bcm476x_timer *timer) +{ + return timer->base + TIMER_INTCLR_OFFSET; +} + +static inline void __iomem *to_ris(struct bcm476x_timer *timer) +{ + return timer->base + TIMER_RIS_OFFSET; +} + +static inline void __iomem *to_mis(struct bcm476x_timer *timer) +{ + return timer->base + TIMER_MIS_OFFSET; +} + +static void bcm476x_timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt_dev) +{ + struct bcm476x_timer *timer; + u32 val; + + timer = container_of(evt_dev, struct bcm476x_timer, evt); + val = TIMER_CTRL_CLK2 | TIMER_CTRL_32BIT | + TIMER_CTRL_IE | TIMER_CTRL_EN; + + switch (mode) { + case CLOCK_EVT_MODE_ONESHOT: + writel(val | TIMER_CTRL_ONESHOTMODE, to_control(timer)); + break; + case CLOCK_EVT_MODE_RESUME: + case CLOCK_EVT_MODE_SHUTDOWN: + break; + default: + WARN(1, "%s: unhandled event mode %d\n", __func__, mode); + break; + } +} + +static int bcm476x_timer_set_next_event(unsigned long event, + struct clock_event_device *evt_dev) +{ + struct bcm476x_timer *timer; + + timer = container_of(evt_dev, struct bcm476x_timer, evt); + writel(event, to_load(timer)); + return 0; +} + +static irqreturn_t bcm476x_timer_interrupt(int irq, void *dev_id) +{ + struct bcm476x_timer *timer = dev_id; + void (*event_handler)(struct clock_event_device *); + + /* check the (masked) interrupt status */ + if (!readl_relaxed(to_mis(timer))) + return IRQ_NONE; + + /* clear the timer interrupt */ + writel_relaxed(1, to_intclr(timer)); + + event_handler = ACCESS_ONCE(timer->evt.event_handler); + if (event_handler) + event_handler(&timer->evt); + + return IRQ_HANDLED; +} + +static const struct of_device_id bcm476x_timer_match[] __initconst = { + { .compatible = "brcm,bcm4760-system-timer" }, + { .compatible = "brcm,bcm4761-system-timer" }, + {} +}; + +static void __init bcm476x_timer_init(void) +{ + struct device_node *node; + void __iomem *base; + u32 freq; + int irq; + struct bcm476x_timer *timer; + + node = of_find_matching_node(NULL, bcm476x_timer_match); + if (!node) + panic("No bcm476x timer node"); + + base = of_iomap(node, 0); + if (!base) + panic("Can't remap timer registers"); + + if (of_property_read_u32(node, "clock-frequency", &freq)) + panic("Can't read timer frequency"); + /* TODO allow other frequences by using pre-scaling parameters */ + if (freq != 24000000) + panic("Invalid timer frequency"); + + timer = kzalloc(sizeof(*timer), GFP_KERNEL); + if (!timer) + panic("Can't allocate timer struct\n"); + + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) + panic("Can't parse timer IRQ"); + + timer->base = base; + timer->evt.name = node->name; + timer->evt.rating = 300; + timer->evt.features = CLOCK_EVT_FEAT_ONESHOT; + timer->evt.set_mode = bcm476x_timer_set_mode; + timer->evt.set_next_event = bcm476x_timer_set_next_event; + timer->evt.cpumask = cpumask_of(0); + timer->act.name = node->name; + timer->act.flags = IRQF_TIMER | IRQF_SHARED; + timer->act.dev_id = timer; + timer->act.handler = bcm476x_timer_interrupt; + + if (setup_irq(irq, &timer->act)) + panic("Can't set up timer IRQ\n"); + + clockevents_config_and_register(&timer->evt, freq, 0xf, 0xffffffff); +} + +struct sys_timer bcm476x_timer = { + .init = bcm476x_timer_init, +}; Index: b/include/linux/bcm476x_timer.h =================================================================== --- /dev/null +++ b/include/linux/bcm476x_timer.h @@ -0,0 +1,24 @@ +/* + * Broadcom BCM476x SoCs system timer + * + * Copyright (C) 2012 Domenico Andreoli + * + * 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. + */ + +#ifndef _LINUX_BCM476X_TIMER_H +#define _LINUX_BCM476X_TIMER_H + +#include + +extern struct sys_timer bcm476x_timer; + +#endif