2013-04-04 09:02:13

by Daniel Tang

[permalink] [raw]
Subject: [RFC PATCH arm: initial TI-Nspire support]

We're sending out for comments an early patch adding TI-Nspire support
to Linux.

Some words on the Nspire platform: it's a series of graphing
calculators, made of four models: "Clickpad" (2007-2010), "Touchpad"
(2010-2012?), "CX" (2011-), "CM-C" (2011-).

The main hardware characteristics are significantly outdated - and
yet, Nspires are the most powerful graphing calculators on the
market:
* ARM926EJ-S based ASICs by LSI Logic, up to 200+ MHz;
* 32 MB of SDRAM for Clickpad/Touchpad/CM-C, 64 MB of SDRAM for CX;
* 512 KB of external NOR Flash for first-stage bootloader ("boot1")
on the oldest models, internal since then;
* NAND Flash for manufacturer data + boot2 (second-stage
platform-specific bootloader) + "diags" (diagnostics software)
+ filesystem (containing OS): 32 MB for Clickpad / Touchpad,
128 MB for CX. Multiple chip models are used.
* 320x240 grayscale non-backlit DSTN screen for Clickpad / Touchpad
(usually used in 4 bpp mode), 320x240 16 bpp backlit color screen
for CX / CM-C;
* USB OTG capable controller, for calc-to-calc transfers;
* RS232 TTL and GPIO on proprietary-pitch "dock" connector.


>From a software point of view, the Nspire is usually running a
proprietary Nucleus-based OS, which uses the proprietary Datalight
Reliance filesystem, layered on top of proprietary FlashFX.
TI uses RSA signature validation for boot2, diags and OS upgrades;
boot2 and diags are compressed, OS upgrades are encrypted (Blowfish).
On the CX, some chunks of the boot2 are encrypted with 3-DES.
The officially sanctioned programming abilities are pretty limited:
* a BASIC which cannot draw pixels to the screen or read arbitrary
keys from the keyboard (yes, we're talking about a calculator
used for teaching purposes !!);
* a proprietary Lua with significant two-way incompatibilities with
standard Lua: no io.*, so no file I/O, and no os.*; but a
proprietary framework for event-driven programming.

Native code is accessible on select OS versions (Ndless) or boot2
versions (nLaunch & nLaunch CX) through arbitrary native code execution
exploits.
TI actively fights attempts to use native code on the Nspire series.


Patch contents
--------------
This patch (against mainline, but it also applies to linux-next)
contains the essential support code required to boot Linux to a shell
on all models, but contains nothing else at the moment.
Code containing drivers for other peripherals exists, and will
eventually be posted for review as well: we need to do a bit of
cleanup. If you prefer them to be posted here now, we'll do.
They're at https://github.com/tangrs/linux .

A possibly noteworthy fact is that despite the gradual shift to
using Device Tree definitions for ARM machine types, we've decided not
to use it, for the following reasons:

* the (perceived) extra complexity and code size;
* the fact that we're using our own, simple, bootloader, due to
the impossibility to bootstrap on most models (because boot1
is usually not modifiable) and the fact that the image is stored
in a proprietary FS not supported by mainline Linux and
common bootloaders...


TODO
----
* Address review comments, of course ;-)
* Expand and clean up the drivers that have been written so far
(for instance, GPIO) and submit them for review.
* Continue to reverse engineer and write drivers to support the
remaining hardware. We still need to work on power management and
the NAND Flash. The platform also has a SPI somewhere, probably
another USB port, and more anecdotal, 3-DES and SHA-256 hardware
acceleration.


Thanks in advance for reviews ;)

Signed-off-by: Daniel Tang <[email protected]>
Signed-off-by: Fabian Vogt <[email protected]>
Signed-off-by: Lionel Debroux <[email protected]>
---
arch/arm/Kconfig | 17 +
arch/arm/Makefile | 1 +
arch/arm/mach-nspire/Kconfig | 36 +++
arch/arm/mach-nspire/Makefile | 16 +
arch/arm/mach-nspire/Makefile.boot | 1 +
arch/arm/mach-nspire/classic.c | 363 ++++++++++++++++++++++
arch/arm/mach-nspire/classic.h | 23 ++
arch/arm/mach-nspire/clock.c | 39 +++
arch/arm/mach-nspire/clock.h | 10 +
arch/arm/mach-nspire/common.c | 311 ++++++++++++++++++
arch/arm/mach-nspire/common.h | 52 ++++
arch/arm/mach-nspire/include/mach/clkdev.h | 24 ++
arch/arm/mach-nspire/include/mach/debug-macro.S | 28 ++
arch/arm/mach-nspire/include/mach/hardware.h | 15 +
arch/arm/mach-nspire/include/mach/irqs.h | 34 ++
arch/arm/mach-nspire/include/mach/keypad.h | 31 ++
arch/arm/mach-nspire/include/mach/memory.h | 17 +
arch/arm/mach-nspire/include/mach/nspire_clock.h | 52 ++++
arch/arm/mach-nspire/include/mach/nspire_mmio.h | 67 ++++
arch/arm/mach-nspire/include/mach/sram.h | 26 ++
arch/arm/mach-nspire/include/mach/timex.h | 15 +
arch/arm/mach-nspire/include/mach/uncompress.h | 42 +++
arch/arm/mach-nspire/keypad.c | 100 ++++++
arch/arm/mach-nspire/nspire_clp.c | 57 ++++
arch/arm/mach-nspire/nspire_cx.c | 316 +++++++++++++++++++
arch/arm/mach-nspire/nspire_tp.c | 80 +++++
arch/arm/mach-nspire/sram.c | 66 ++++
arch/arm/mach-nspire/touchpad.c | 30 ++
arch/arm/mach-nspire/touchpad.h | 17 +
arch/arm/tools/mach-types | 3 +
30 files changed, 1889 insertions(+)
create mode 100644 arch/arm/mach-nspire/Kconfig
create mode 100644 arch/arm/mach-nspire/Makefile
create mode 100644 arch/arm/mach-nspire/Makefile.boot
create mode 100644 arch/arm/mach-nspire/classic.c
create mode 100644 arch/arm/mach-nspire/classic.h
create mode 100644 arch/arm/mach-nspire/clock.c
create mode 100644 arch/arm/mach-nspire/clock.h
create mode 100644 arch/arm/mach-nspire/common.c
create mode 100644 arch/arm/mach-nspire/common.h
create mode 100644 arch/arm/mach-nspire/include/mach/clkdev.h
create mode 100644 arch/arm/mach-nspire/include/mach/debug-macro.S
create mode 100644 arch/arm/mach-nspire/include/mach/hardware.h
create mode 100644 arch/arm/mach-nspire/include/mach/irqs.h
create mode 100644 arch/arm/mach-nspire/include/mach/keypad.h
create mode 100644 arch/arm/mach-nspire/include/mach/memory.h
create mode 100644 arch/arm/mach-nspire/include/mach/nspire_clock.h
create mode 100644 arch/arm/mach-nspire/include/mach/nspire_mmio.h
create mode 100644 arch/arm/mach-nspire/include/mach/sram.h
create mode 100644 arch/arm/mach-nspire/include/mach/timex.h
create mode 100644 arch/arm/mach-nspire/include/mach/uncompress.h
create mode 100644 arch/arm/mach-nspire/keypad.c
create mode 100644 arch/arm/mach-nspire/nspire_clp.c
create mode 100644 arch/arm/mach-nspire/nspire_cx.c
create mode 100644 arch/arm/mach-nspire/nspire_tp.c
create mode 100644 arch/arm/mach-nspire/sram.c
create mode 100644 arch/arm/mach-nspire/touchpad.c
create mode 100644 arch/arm/mach-nspire/touchpad.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 13b7394..4aa5029 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -492,6 +492,21 @@ config ARCH_NETX
help
This enables support for systems based on the Hilscher NetX Soc

+config ARCH_NSPIRE
+ bool "TI-NSPIRE based"
+ depends on MMU
+ select CPU_ARM926T
+ select HAVE_MACH_CLKDEV
+ select CLKDEV_LOOKUP
+ select ARM_AMBA
+ select USB_ARCH_HAS_EHCI
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ select GENERIC_ALLOCATOR
+ select ARCH_HAS_CPUFREQ
+ select CPU_FREQ_TABLE
+ help
+ This enables support for systems using the TI-NSPIRE CPU
+
config ARCH_H720X
bool "Hynix HMS720x-based"
select ARCH_USES_GETTIMEOFFSET
@@ -1081,6 +1096,8 @@ source "arch/arm/mach-netx/Kconfig"

source "arch/arm/mach-nomadik/Kconfig"

+source "arch/arm/mach-nspire/Kconfig"
+
source "arch/arm/plat-omap/Kconfig"

source "arch/arm/mach-omap1/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index ee4605f..2580d2b 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -165,6 +165,7 @@ machine-$(CONFIG_ARCH_MXS) += mxs
machine-$(CONFIG_ARCH_MVEBU) += mvebu
machine-$(CONFIG_ARCH_NETX) += netx
machine-$(CONFIG_ARCH_NOMADIK) += nomadik
+machine-$(CONFIG_ARCH_NSPIRE) += nspire
machine-$(CONFIG_ARCH_OMAP1) += omap1
machine-$(CONFIG_ARCH_OMAP2PLUS) += omap2
machine-$(CONFIG_ARCH_ORION5X) += orion5x
diff --git a/arch/arm/mach-nspire/Kconfig b/arch/arm/mach-nspire/Kconfig
new file mode 100644
index 0000000..da032b7
--- /dev/null
+++ b/arch/arm/mach-nspire/Kconfig
@@ -0,0 +1,36 @@
+if ARCH_NSPIRE
+
+choice
+ prompt "Early printk and boot message serial interface"
+ help
+ Early printk output interface
+ depends on EARLY_PRINTK
+ default NSPIRE_EARLYPRINTK_CX
+
+config NSPIRE_EARLYPRINTK_CLASSIC
+ bool "Classic"
+
+config NSPIRE_EARLYPRINTK_CX
+ bool "CX model"
+endchoice
+
+
+menu "Supported models"
+
+config MACH_NSPIRECX
+ select GENERIC_CLOCKEVENTS
+ select ARM_VIC
+ select ARM_TIMER_SP804
+ bool "CX/CX CAS"
+
+config MACH_NSPIRETP
+ select GENERIC_IRQ_CHIP
+ bool "Touchpad/Touchpad CAS"
+
+config MACH_NSPIRECLP
+ select GENERIC_IRQ_CHIP
+ bool "Clickpad/Clickpad CAS"
+
+endmenu
+
+endif
diff --git a/arch/arm/mach-nspire/Makefile b/arch/arm/mach-nspire/Makefile
new file mode 100644
index 0000000..f7108fe
--- /dev/null
+++ b/arch/arm/mach-nspire/Makefile
@@ -0,0 +1,16 @@
+obj-y :=
+
+obj-y += common.o
+obj-y += clock.o
+obj-y += sram.o
+obj-y += keypad.o
+
+obj-$(CONFIG_MACH_NSPIRECX) += nspire_cx.o
+obj-$(CONFIG_MACH_NSPIRECX) += touchpad.o
+
+obj-$(CONFIG_MACH_NSPIRECLP) += nspire_clp.o
+obj-$(CONFIG_MACH_NSPIRECLP) += classic.o
+
+obj-$(CONFIG_MACH_NSPIRETP) += nspire_tp.o
+obj-$(CONFIG_MACH_NSPIRETP) += classic.o
+obj-$(CONFIG_MACH_NSPIRETP) += touchpad.o
diff --git a/arch/arm/mach-nspire/Makefile.boot b/arch/arm/mach-nspire/Makefile.boot
new file mode 100644
index 0000000..7db966b
--- /dev/null
+++ b/arch/arm/mach-nspire/Makefile.boot
@@ -0,0 +1 @@
+zreladdr-y := 0x10008000
diff --git a/arch/arm/mach-nspire/classic.c b/arch/arm/mach-nspire/classic.c
new file mode 100644
index 0000000..b1eddf3
--- /dev/null
+++ b/arch/arm/mach-nspire/classic.c
@@ -0,0 +1,363 @@
+/*
+ * linux/arch/arm/mach-nspire/classic.c
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/io.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/cpumask.h>
+#include <linux/serial_8250.h>
+#include <linux/platform_device.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/mach/time.h>
+#include <asm/exception.h>
+
+#include <mach/nspire_mmio.h>
+#include <mach/nspire_clock.h>
+#include <mach/clkdev.h>
+#include <mach/keypad.h>
+#include <mach/irqs.h>
+
+#include "common.h"
+
+/* Clock */
+
+union reg_clk_speed {
+ unsigned long raw;
+ struct {
+ unsigned long __padding0:1;
+ unsigned long base_cpu_ratio:7;
+ unsigned long is_base_27mhz:1;
+ unsigned long __padding1:3;
+ unsigned long cpu_ahb_ratio:3;
+ unsigned long __padding2:1;
+ unsigned long base_val:5;
+ } val;
+};
+
+static struct nspire_clk_speeds classic_io_to_clocks(unsigned long val)
+{
+ struct nspire_clk_speeds clks;
+ union reg_clk_speed reg;
+
+ reg.raw = val;
+ reg.val.base_cpu_ratio *= 2;
+ reg.val.cpu_ahb_ratio++;
+
+ BUG_ON(reg.val.base_cpu_ratio == 0);
+
+ clks.base = reg.val.is_base_27mhz ? 27 : (300 - (6*reg.val.base_val));
+ clks.base *= 1000000; /* Convert to Hz */
+
+ clks.div.base_cpu = reg.val.base_cpu_ratio;
+ clks.div.cpu_ahb = reg.val.cpu_ahb_ratio;
+
+ return clks;
+}
+
+static unsigned long classic_clocks_to_io(struct nspire_clk_speeds *clks)
+{
+ union reg_clk_speed reg;
+
+ BUG_ON(clks->div.base_cpu < 2);
+ BUG_ON(clks->div.cpu_ahb < 1);
+
+ reg.raw = 0;
+ reg.val.base_cpu_ratio = clks->div.base_cpu / 2;
+ reg.val.cpu_ahb_ratio = clks->div.cpu_ahb - 1;
+ reg.val.is_base_27mhz = (clks->base <= 27000000);
+ reg.val.base_val = (300 - (clks->base / 1000000)) / 6;
+
+ return reg.raw;
+}
+
+/* Interrupt handling */
+
+static inline int check_interrupt(void __iomem *base, struct pt_regs *regs)
+{
+ if (readl(base + 0x0)) {
+ int irqnr = readl(base + 0x24);
+ unsigned prev_priority;
+ handle_IRQ(irqnr, regs);
+
+ /* Reset priorities */
+ prev_priority = readl(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x28));
+ writel(prev_priority, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x2c));
+ return 1;
+ }
+ return 0;
+}
+
+asmlinkage void __exception_irq_entry
+ nspire_classic_handle_irq(struct pt_regs *regs)
+{
+ int serviced;
+
+ do {
+ void __iomem *reg_base = IOMEM(NSPIRE_INTERRUPT_VIRT_BASE);
+ serviced = 0;
+
+ /* IRQ */
+ serviced += check_interrupt(reg_base, regs);
+ /* FIQ */
+ serviced += check_interrupt(reg_base + 0x100, regs);
+ } while (serviced > 0);
+}
+
+static void classic_irq_ack(struct irq_data *d)
+{
+ readl(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x28));
+}
+
+static void __init classic_allocate_gc(void)
+{
+ struct irq_chip_generic *gc;
+ struct irq_chip_type *ct;
+
+ gc = irq_alloc_generic_chip("NINT", 1, 0,
+ IOMEM(NSPIRE_INTERRUPT_VIRT_BASE), handle_level_irq);
+
+ ct = gc->chip_types;
+ ct->chip.irq_ack = classic_irq_ack;
+ ct->chip.irq_mask = irq_gc_mask_disable_reg;
+ ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
+
+ ct->regs.mask = 0x8;
+ ct->regs.enable = 0x8;
+ ct->regs.disable = 0xc;
+
+ irq_setup_generic_chip(gc, IRQ_MSK(NR_IRQS), IRQ_GC_INIT_MASK_CACHE,
+ IRQ_NOREQUEST, 0);
+}
+
+void __init nspire_classic_init_irq(void)
+{
+ /* No stickies */
+ writel(0, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x204));
+
+ /* Disable all interrupts */
+ writel(~0, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0xc));
+ writel(~0, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x10c));
+
+ /* Set all priorities to 0 */
+ memset_io(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x300), 0, 0x7f);
+
+ /* Accept interrupts of all priorities */
+ writel(0xf, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x2c));
+ writel(0xf, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x12c));
+
+ /* Clear existing interrupts */
+ readl(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x28));
+ readl(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x128));
+
+ /* Add chip */
+ classic_allocate_gc();
+}
+
+
+/* Timer */
+
+static int classic_timer_set_event(unsigned long delta,
+ struct clock_event_device *dev)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ writel(delta, NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2));
+ writel(1, NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2 + 0x8));
+ writel(0, NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2 + 0x18));
+ local_irq_restore(flags);
+
+ return 0;
+}
+static void classic_timer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ evt->mode = mode;
+}
+
+static struct clock_event_device nspire_clkevt = {
+ .name = "clockevent",
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ .shift = 32,
+ .rating = 400,
+ .set_next_event = classic_timer_set_event,
+ .set_mode = classic_timer_set_mode,
+ .cpumask = cpu_all_mask,
+};
+
+static irqreturn_t classic_timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *c = dev_id;
+
+ /* Acknowledge */
+ writel((1<<0), NSPIRE_APB_VIRTIO(NSPIRE_APB_MISC + 0x20));
+
+ if (c->mode != CLOCK_EVT_FEAT_PERIODIC)
+ writel((1<<4) | 1, NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2 + 0x08));
+
+ if (c->event_handler)
+ c->event_handler(c);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction classic_timer_irq = {
+ .name = "timer2",
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = classic_timer_interrupt,
+ .dev_id = &nspire_clkevt,
+};
+
+
+void __init nspire_classic_timer_init(void)
+{
+ struct clk *timer_clk;
+
+ /* Count down from 1 */
+ writel(1, NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2));
+
+ /* Divider is zero */
+ writel(0, NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2 + 0x4));
+
+ /* Decreasing timer and interrupt */
+ writel(1, NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2 + 0x8));
+
+ /* Interrupt on timer value reaching 0 */
+ writel(0, NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2 + 0x18));
+
+ /* Acknowledge existing interrupts */
+ writel(~0, NSPIRE_APB_VIRTIO(NSPIRE_APB_MISC + 0x20));
+
+ /* Set interrupt masks */
+ writel((1<<0), NSPIRE_APB_VIRTIO(NSPIRE_APB_MISC + 0x24));
+
+ setup_irq(NSPIRE_IRQ_TIMER2, &classic_timer_irq);
+
+ timer_clk = clk_get(NULL, "timer2");
+ clk_enable(timer_clk);
+
+ /* Set clocksource to zero */
+ writel(0, NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2 + 0xc));
+
+ /* Divider is zero */
+ writel(0, NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2 + 0x10));
+
+ /* Ever increasing timer */
+ writel((1<<3) | 7, NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2 + 0x14));
+
+ clocksource_mmio_init(NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2 + 0xc),
+ "clocksource", clk_get_rate(timer_clk), 200, 16,
+ clocksource_mmio_readw_up);
+
+ clockevents_config_and_register(&nspire_clkevt,
+ clk_get_rate(timer_clk), 0x0001, 0xfffe);
+}
+
+
+/* Serial */
+static struct plat_serial8250_port classic_serial_platform_data[] = {
+ {
+ .mapbase = NSPIRE_APB_PHYS(NSPIRE_APB_UART),
+ .irq = NSPIRE_IRQ_UART,
+ .uartclk = 29491200,
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
+ UPF_IOREMAP,
+ .regshift = 2
+ },
+ { }
+};
+
+struct platform_device nspire_classic_serial_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = &classic_serial_platform_data
+ }
+};
+
+/* Framebuffer */
+static struct clcd_panel classic_lcd_panel = {
+ .mode = {
+ .name = "grayscale lcd",
+ .refresh = 60,
+ .xres = 320,
+ .yres = 240,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED,
+ .pixclock = 1,
+ .hsync_len = 6,
+ .vsync_len = 1,
+ .right_margin = 6,
+ .left_margin = 6,
+ },
+ .width = 71, /* 7.11cm */
+ .height = 53, /* 5.33cm */
+ .tim2 = 0x80007d0,
+ .cntl = CNTL_LCDBPP8 | CNTL_LCDMONO8,
+ .bpp = 8,
+ .grayscale = 1
+};
+#define PANEL_SIZE (19 * SZ_4K)
+
+static int classic_clcd_setup(struct clcd_fb *fb)
+{
+ return nspire_clcd_setup(fb, PANEL_SIZE, &classic_lcd_panel);
+}
+
+static struct clcd_board classic_clcd_data = {
+ .name = "lcd controller",
+ .check = clcdfb_check,
+ .decode = clcdfb_decode,
+ .setup = classic_clcd_setup,
+ .mmap = nspire_clcd_mmap,
+ .remove = nspire_clcd_remove,
+};
+
+AMBA_AHB_DEVICE(fb, "fb", 0, NSPIRE_LCD_PHYS_BASE,
+ { NSPIRE_IRQ_LCD }, &classic_clcd_data);
+
+/* Init */
+void __init nspire_classic_init_early(void)
+{
+ nspire_io_to_clocks = classic_io_to_clocks;
+ nspire_clocks_to_io = classic_clocks_to_io;
+
+ nspire_init_early();
+}
+
+void __init nspire_classic_init(void)
+{
+ /*
+ * Temporarily disable NAND writes on classics to prevent
+ * accidental bricking.
+ */
+ writel((1<<7), NSPIRE_APB_VIRTIO(NSPIRE_APB_POWER + 0x18));
+
+ nspire_keypad_data.active_low = 1;
+
+ amba_device_register(&fb_device, &iomem_resource);
+ platform_device_register(&nspire_keypad_device);
+ platform_device_register(&nspire_classic_serial_device);
+
+ nspire_init();
+}
+
+void __init nspire_classic_init_late(void)
+{
+ nspire_init_late();
+}
diff --git a/arch/arm/mach-nspire/classic.h b/arch/arm/mach-nspire/classic.h
new file mode 100644
index 0000000..c440ebc
--- /dev/null
+++ b/arch/arm/mach-nspire/classic.h
@@ -0,0 +1,23 @@
+/*
+ * linux/arch/arm/mach-nspire/classic.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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 <asm/exception.h>
+
+void __init nspire_classic_init_irq(void);
+void __init nspire_classic_init_early(void);
+void __init nspire_classic_init(void);
+void __init nspire_classic_init_late(void);
+asmlinkage void __exception_irq_entry
+ nspire_classic_handle_irq(struct pt_regs *regs);
+
+void __init nspire_classic_timer_init(void);
+
+extern struct irq_chip nspire_classic_irq_chip;
diff --git a/arch/arm/mach-nspire/clock.c b/arch/arm/mach-nspire/clock.c
new file mode 100644
index 0000000..c679449
--- /dev/null
+++ b/arch/arm/mach-nspire/clock.c
@@ -0,0 +1,39 @@
+/*
+ * linux/arch/arm/mach-nspire/clock.c
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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 <asm-generic/errno.h>
+
+#include <mach/clkdev.h>
+
+void clk_disable(struct clk *clk)
+{
+}
+
+int clk_enable(struct clk *clk)
+{
+ return 0;
+}
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ if (clk->get_rate)
+ clk->get_rate(clk);
+
+ return clk->rate;
+}
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ if (clk->set_rate)
+ return clk->set_rate(clk, rate);
+
+ return -ENOSYS;
+}
diff --git a/arch/arm/mach-nspire/clock.h b/arch/arm/mach-nspire/clock.h
new file mode 100644
index 0000000..43e99bd
--- /dev/null
+++ b/arch/arm/mach-nspire/clock.h
@@ -0,0 +1,10 @@
+/*
+ * linux/arch/arm/mach-nspire/clock.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
diff --git a/arch/arm/mach-nspire/common.c b/arch/arm/mach-nspire/common.c
new file mode 100644
index 0000000..a7abc68
--- /dev/null
+++ b/arch/arm/mach-nspire/common.c
@@ -0,0 +1,311 @@
+/*
+ * linux/arch/arm/mach-nspire/common.c
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/clkdev.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ehci_pdriver.h>
+#include <linux/usb/chipidea.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd.h>
+#include <linux/dma-mapping.h>
+
+#include <mach/nspire_mmio.h>
+#include <mach/nspire_clock.h>
+#include <mach/irqs.h>
+#include <mach/clkdev.h>
+#include <mach/keypad.h>
+#include <mach/sram.h>
+
+#include <asm/mach/time.h>
+#include <asm/mach-types.h>
+#include <asm/mach/map.h>
+
+#include "common.h"
+#include "clock.h"
+
+/* Clocks */
+
+struct nspire_clk_speeds (*nspire_io_to_clocks)(unsigned long);
+unsigned long (*nspire_clocks_to_io)(struct nspire_clk_speeds *);
+
+/* AHB clock */
+static void ahb_get_rate(struct clk *clk)
+{
+ struct nspire_clk_speeds speeds = nspire_get_clocks();
+ clk->rate = CLK_GET_AHB(&speeds);
+}
+
+static struct clk ahb_clk = {
+ .get_rate = ahb_get_rate,
+};
+
+/* APB clock */
+
+static void apb_get_rate(struct clk *clk)
+{
+ clk->rate = clk_get_rate(&ahb_clk) / 2;
+}
+
+static struct clk apb_clk = {
+ .get_rate = apb_get_rate
+};
+
+/* Misc */
+
+static struct clk systimer_clk = {
+ .rate = 32768,
+};
+
+static struct clk uart_clk = {
+ .rate = 12000000,
+};
+
+#ifdef CONFIG_MACH_NSPIRECX
+static struct clk i2c_clk = {
+ /* Doesn't matter, we set it manually */
+ .rate = 250000,
+};
+#endif
+
+static struct clk_lookup nspire_clk_lookup[] = {
+ {
+ .dev_id = "uart",
+ .clk = &uart_clk
+ },
+ {
+ .dev_id = "fb",
+ .clk = &ahb_clk
+ },
+ {
+ .con_id = "ahb",
+ .clk = &ahb_clk
+ },
+ {
+ .dev_id = "watchdog",
+ .clk = &apb_clk
+ },
+ {
+ .dev_id = "nspire-keypad.0",
+ .clk = &apb_clk
+ },
+#ifdef CONFIG_MACH_NSPIRECX
+ {
+ .dev_id = "sp804",
+ .con_id = "timer2",
+ .clk = &systimer_clk
+ },
+ {
+ .dev_id = "i2c_designware.0",
+ .clk = &i2c_clk
+ },
+#endif
+#if defined(CONFIG_MACH_NSPIRECLP) || defined(CONFIG_MACH_NSPIRETP)
+ {
+ .dev_id = NULL,
+ .con_id = "timer2",
+ .clk = &systimer_clk
+ },
+#endif
+};
+
+/* Keypad */
+static struct resource nspire_keypad_resources[] = {
+ {
+ .start = NSPIRE_APB_PHYS(NSPIRE_APB_KEYPAD),
+ .end = NSPIRE_APB_PHYS(NSPIRE_APB_KEYPAD + SZ_4K - 1),
+ .flags = IORESOURCE_MEM,
+ },
+ RESOURCE_ENTRY_IRQ(KEYPAD)
+};
+
+struct nspire_keypad_data nspire_keypad_data = {
+ .scan_interval = 1000,
+ .row_delay = 200
+};
+
+struct platform_device nspire_keypad_device = {
+ .name = "nspire-keypad",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(nspire_keypad_resources),
+ .resource = nspire_keypad_resources,
+ .dev = {
+ .platform_data = &nspire_keypad_data
+ }
+};
+
+
+/* GPIO */
+static struct resource nspire_gpio_resources[] = {
+ {
+ .start = NSPIRE_APB_PHYS(NSPIRE_APB_GPIO),
+ .end = NSPIRE_APB_PHYS(NSPIRE_APB_GPIO + SZ_4K - 1),
+ .flags = IORESOURCE_MEM,
+ },
+ RESOURCE_ENTRY_IRQ(GPIO)
+};
+
+static struct platform_device nspire_gpio_device = {
+ .name = "gpio-nspire",
+ .resource = nspire_gpio_resources,
+ .num_resources = ARRAY_SIZE(nspire_gpio_resources),
+};
+
+/* ADC */
+static struct resource nspire_adc_resources[] = {
+ RESOURCE_ENTRY_MEM(ADC),
+ RESOURCE_ENTRY_IRQ(ADC)
+};
+
+static struct platform_device nspire_adc_device = {
+ .name = "nspire-adc",
+ .resource = nspire_adc_resources,
+ .num_resources = ARRAY_SIZE(nspire_adc_resources)
+};
+
+/* Framebuffer */
+int nspire_clcd_setup(struct clcd_fb *fb, unsigned panel_size,
+ struct clcd_panel *panel)
+{
+ dma_addr_t dma;
+
+ fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev,
+ panel_size, &dma, GFP_KERNEL);
+ if (!fb->fb.screen_base) {
+ pr_err("CLCD: unable to map framebuffer\n");
+ return -ENOMEM;
+ }
+
+ fb->fb.fix.smem_start = dma;
+ fb->fb.fix.smem_len = panel_size;
+ fb->panel = panel;
+
+ return 0;
+}
+
+int nspire_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
+{
+ return dma_mmap_writecombine(&fb->dev->dev, vma,
+ fb->fb.screen_base, fb->fb.fix.smem_start,
+ fb->fb.fix.smem_len);
+}
+
+void nspire_clcd_remove(struct clcd_fb *fb)
+{
+ dma_free_writecombine(&fb->dev->dev, fb->fb.fix.smem_len,
+ fb->fb.screen_base, fb->fb.fix.smem_start);
+}
+
+/* Watchdog */
+
+AMBA_APB_DEVICE(watchdog, "watchdog", 0, NSPIRE_APB_PHYS(NSPIRE_APB_WATCHDOG),
+ { NSPIRE_IRQ_WATCHDOG }, NULL);
+
+/* Generic OTG */
+
+u64 nspire_usb_dma_mask = ~(u32)0;
+
+static struct resource otg_resources[] = {
+ RESOURCE_ENTRY_MEM(OTG),
+ RESOURCE_ENTRY_IRQ(OTG)
+};
+
+static struct ci13xxx_platform_data otg_pdata = {
+ .name = "nspire_usb",
+ .capoffset = 0x100,
+ .flags = CI13XXX_REGS_SHARED,
+};
+
+
+struct platform_device nspire_otg_device = {
+ .name = "ci_hdrc",
+ .id = 0,
+ .dev = {
+ .platform_data = &otg_pdata,
+ .coherent_dma_mask = ~0,
+ .dma_mask = &nspire_usb_dma_mask
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources)
+};
+
+struct platform_device nspire_usb_nop_xceiver = {
+ .name = "nop_usb_xceiv",
+};
+
+/* RTC */
+static struct resource nspire_rtc_resources[] = {
+ {
+ .start = NSPIRE_APB_PHYS(NSPIRE_APB_RTC),
+ .end = NSPIRE_APB_PHYS(NSPIRE_APB_RTC + SZ_4K - 1),
+ .flags = IORESOURCE_MEM,
+ },
+ RESOURCE_ENTRY_IRQ(RTC)
+};
+
+static struct platform_device nspire_rtc_device = {
+ .name = "nspire-rtc",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(nspire_rtc_resources),
+ .resource = nspire_rtc_resources,
+};
+
+/* Memory mapped IO */
+struct map_desc nspire_io_regs[] __initdata = {
+ IOTABLE_ENTRY(ADC),
+ IOTABLE_ENTRY(APB),
+ IOTABLE_ENTRY(INTERRUPT),
+};
+
+void __init nspire_map_io(void)
+{
+ iotable_init(nspire_io_regs, ARRAY_SIZE(nspire_io_regs));
+}
+
+/* Clocks */
+void __init nspire_init_early(void)
+{
+ clkdev_add_table(nspire_clk_lookup, ARRAY_SIZE(nspire_clk_lookup));
+
+ /* Renable bus access to everything in case the OS disabled them */
+ writel(0, NSPIRE_APB_VIRTIO(NSPIRE_APB_POWER + 0x18));
+ writel(0, NSPIRE_APB_VIRTIO(NSPIRE_APB_POWER + 0x20));
+
+ /*
+ * Ack some non-maskable clock speed change interrupts before cpufreq
+ * driver is brought up to avoid a race condition between an interrupt
+ * happening and driver init.
+ */
+
+ writel(3, NSPIRE_APB_VIRTIO(NSPIRE_APB_POWER + 0x14));
+}
+
+/* Common init */
+void __init nspire_init(void)
+{
+ sram_init(NSPIRE_SRAM_PHYS_BASE, NSPIRE_SRAM_SIZE);
+ amba_device_register(&watchdog_device, &iomem_resource);
+
+ platform_device_register(&nspire_gpio_device);
+ platform_device_register(&nspire_rtc_device);
+ platform_device_register(&nspire_adc_device);
+
+}
+
+void __init nspire_init_late(void)
+{
+}
+
+/* Restart */
+void nspire_restart(char mode, const char *cmd)
+{
+ writel(2, NSPIRE_APB_VIRTIO(NSPIRE_APB_MISC + 0x8));
+}
diff --git a/arch/arm/mach-nspire/common.h b/arch/arm/mach-nspire/common.h
new file mode 100644
index 0000000..27e81b6
--- /dev/null
+++ b/arch/arm/mach-nspire/common.h
@@ -0,0 +1,52 @@
+/*
+ * linux/arch/arm/mach-nspire/common.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+
+#define IOTABLE_ENTRY(t) \
+ { \
+ .virtual = NSPIRE_##t##_VIRT_BASE, \
+ .pfn = __phys_to_pfn(NSPIRE_##t##_PHYS_BASE), \
+ .length = NSPIRE_##t##_SIZE, \
+ .type = MT_DEVICE \
+ }
+
+#define RESOURCE_ENTRY_IRQ(t) \
+ { \
+ .start = NSPIRE_IRQ_##t, \
+ .end = NSPIRE_IRQ_##t, \
+ .flags = IORESOURCE_IRQ \
+ }
+
+#define RESOURCE_ENTRY_MEM(t) \
+ { \
+ .start = NSPIRE_##t##_PHYS_BASE, \
+ .end = NSPIRE_##t##_PHYS_BASE + NSPIRE_##t##_SIZE - 1, \
+ .flags = IORESOURCE_MEM \
+ }
+
+extern struct platform_device nspire_keypad_device;
+extern struct platform_device nspire_otg_device;
+extern struct platform_device nspire_usb_nop_xceiver;
+extern struct nspire_keypad_data nspire_keypad_data;
+
+extern u64 nspire_usb_dma_mask;
+
+void __init nspire_map_io(void);
+void __init nspire_init_early(void);
+void __init nspire_init(void);
+void __init nspire_init_late(void);
+
+void nspire_restart(char mode, const char *cmd);
+
+int nspire_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma);
+void nspire_clcd_remove(struct clcd_fb *fb);
+int nspire_clcd_setup(struct clcd_fb *fb, unsigned panel_size,
+ struct clcd_panel *panel);
diff --git a/arch/arm/mach-nspire/include/mach/clkdev.h b/arch/arm/mach-nspire/include/mach/clkdev.h
new file mode 100644
index 0000000..b4afe65
--- /dev/null
+++ b/arch/arm/mach-nspire/include/mach/clkdev.h
@@ -0,0 +1,24 @@
+/*
+ * linux/arch/arm/mach-nspire/include/mach/clkdev.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef NSPIRE_CLKDEV_H
+#define NSPIRE_CLKDEV_H
+
+struct clk {
+ void (*get_rate)(struct clk *clk);
+ int (*set_rate)(struct clk *clk, unsigned long rate);
+ unsigned long rate;
+};
+
+#define __clk_get(clk) ({ 1; })
+#define __clk_put(clk) do { } while (0)
+
+#endif
diff --git a/arch/arm/mach-nspire/include/mach/debug-macro.S b/arch/arm/mach-nspire/include/mach/debug-macro.S
new file mode 100644
index 0000000..b71daa4
--- /dev/null
+++ b/arch/arm/mach-nspire/include/mach/debug-macro.S
@@ -0,0 +1,28 @@
+/*
+ * linux/arch/arm/mach-nspire/include/mach/debug-macro.S
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#define NSPIRE_EARLY_UART_PHYS_BASE 0x90020000
+#define NSPIRE_EARLY_UART_VIRT_BASE 0xfee20000
+
+.macro addruart, rp, rv, tmp
+ ldr \rp, =(NSPIRE_EARLY_UART_PHYS_BASE) @ physical base address
+ ldr \rv, =(NSPIRE_EARLY_UART_VIRT_BASE) @ virtual base address
+.endm
+
+
+#ifdef CONFIG_NSPIRE_EARLYPRINTK_CX
+#include <asm/hardware/debug-pl01x.S>
+#endif
+
+#ifdef CONFIG_NSPIRE_EARLYPRINTK_CLASSIC
+#define UART_SHIFT 2
+#include <asm/hardware/debug-8250.S>
+#endif
diff --git a/arch/arm/mach-nspire/include/mach/hardware.h b/arch/arm/mach-nspire/include/mach/hardware.h
new file mode 100644
index 0000000..7c9c3b6
--- /dev/null
+++ b/arch/arm/mach-nspire/include/mach/hardware.h
@@ -0,0 +1,15 @@
+/*
+ * linux/arch/arm/mach-nspire/include/mach/hardware.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef NSPIRE_HARDWARE_H
+#define NSPIRE_HARDWARE_H
+
+#endif
diff --git a/arch/arm/mach-nspire/include/mach/irqs.h b/arch/arm/mach-nspire/include/mach/irqs.h
new file mode 100644
index 0000000..70be120
--- /dev/null
+++ b/arch/arm/mach-nspire/include/mach/irqs.h
@@ -0,0 +1,34 @@
+/*
+ * linux/arch/arm/mach-nspire/include/mach/irqs.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef NSPIRE_IRQS_H
+#define NSPIRE_IRQS_H
+
+#define NSPIRE_IRQ_MASK 0x007FEB9A
+
+enum {
+ NSPIRE_IRQ_UART = 1,
+ NSPIRE_IRQ_WATCHDOG = 3,
+ NSPIRE_IRQ_RTC = 4,
+ NSPIRE_IRQ_GPIO = 7,
+ NSPIRE_IRQ_OTG = 8,
+ NSPIRE_IRQ_HOSTUSB = 9,
+ NSPIRE_IRQ_ADC = 11,
+ NSPIRE_IRQ_PWR = 15,
+ NSPIRE_IRQ_KEYPAD = 16,
+ NSPIRE_IRQ_TIMER2 = 19,
+ NSPIRE_IRQ_I2C = 20,
+ NSPIRE_IRQ_LCD = 21
+};
+
+#define NR_IRQS 32
+
+#endif
diff --git a/arch/arm/mach-nspire/include/mach/keypad.h b/arch/arm/mach-nspire/include/mach/keypad.h
new file mode 100644
index 0000000..18cf3fd
--- /dev/null
+++ b/arch/arm/mach-nspire/include/mach/keypad.h
@@ -0,0 +1,31 @@
+/*
+ * linux/arch/arm/mach-nspire/include/mach/keypad.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef NSPIRE_KEYPAD_H
+#define NSPIRE_KEYPAD_H
+
+#define KEYPAD_BITMASK_COLS 11
+#define KEYPAD_BITMASK_ROWS 8
+
+struct nspire_keypad_data {
+ unsigned int (*evtcodes)[KEYPAD_BITMASK_COLS];
+
+ /* Maximum delay estimated assuming 33MHz APB */
+ unsigned short scan_interval; /* In microseconds (~2000us max) */
+ unsigned short row_delay; /* In microseconds (~500us max) */
+
+ bool active_low;
+};
+
+extern unsigned int nspire_touchpad_evtcode_map[][KEYPAD_BITMASK_COLS];
+extern unsigned int nspire_clickpad_evtcode_map[][KEYPAD_BITMASK_COLS];
+
+#endif
diff --git a/arch/arm/mach-nspire/include/mach/memory.h b/arch/arm/mach-nspire/include/mach/memory.h
new file mode 100644
index 0000000..eec3f36
--- /dev/null
+++ b/arch/arm/mach-nspire/include/mach/memory.h
@@ -0,0 +1,17 @@
+/*
+ * linux/arch/arm/mach-nspire/include/mach/memory.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef NSPIRE_MEMORY_H
+#define NSPIRE_MEMORY_H
+
+#define PLAT_PHYS_OFFSET 0x10000000
+
+#endif
diff --git a/arch/arm/mach-nspire/include/mach/nspire_clock.h b/arch/arm/mach-nspire/include/mach/nspire_clock.h
new file mode 100644
index 0000000..a96b089
--- /dev/null
+++ b/arch/arm/mach-nspire/include/mach/nspire_clock.h
@@ -0,0 +1,52 @@
+/*
+ * linux/arch/arm/mach-nspire/include/mach/nspire_clock.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef NSPIRE_CLOCK_H
+#define NSPIRE_CLOCK_H
+
+#include <linux/io.h>
+
+#include <mach/nspire_mmio.h>
+
+struct nspire_clk_divider {
+ unsigned char base_cpu, cpu_ahb;
+};
+
+struct nspire_clk_speeds {
+ unsigned long base;
+ struct nspire_clk_divider div;
+};
+
+#define CLK_GET_CPU(cs) ((cs)->base / (cs)->div.base_cpu)
+#define CLK_GET_AHB(cs) (CLK_GET_CPU(cs) / (cs)->div.cpu_ahb)
+
+extern struct nspire_clk_speeds (*nspire_io_to_clocks)(unsigned long);
+extern unsigned long (*nspire_clocks_to_io)(struct nspire_clk_speeds *);
+
+static inline struct nspire_clk_speeds nspire_get_clocks(void)
+{
+ unsigned long val = readl(NSPIRE_APB_VIRTIO(NSPIRE_APB_POWER + 0x00));
+ BUG_ON(!nspire_io_to_clocks);
+ return nspire_io_to_clocks(val);
+}
+
+static inline void nspire_set_clocks(struct nspire_clk_speeds *clks)
+{
+ unsigned long val;
+ BUG_ON(!nspire_io_to_clocks);
+
+ val = nspire_clocks_to_io(clks);
+
+ writel(val, NSPIRE_APB_VIRTIO(NSPIRE_APB_POWER + 0x00));
+ writel(4, NSPIRE_APB_VIRTIO(NSPIRE_APB_POWER + 0x0c));
+}
+
+#endif
diff --git a/arch/arm/mach-nspire/include/mach/nspire_mmio.h b/arch/arm/mach-nspire/include/mach/nspire_mmio.h
new file mode 100644
index 0000000..eaeb100
--- /dev/null
+++ b/arch/arm/mach-nspire/include/mach/nspire_mmio.h
@@ -0,0 +1,67 @@
+/*
+ * linux/arch/arm/mach-nspire/include/mach/nspire_mmio.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef NSPIRE_MMIO_H
+#define NSPIRE_MMIO_H
+
+#include <asm-generic/sizes.h>
+
+/*
+ Memory map:
+ 0xFEE00000 - 0xFF000000 0x90000000 - 0x90200000 (APB)
+ 0xFEDFF000 - 0xFEE00000 0xDC000000 - 0xDC001000 (Interrupt Controller)
+ 0xFED7F000 - 0xFEDFF000 0x00000000 - 0x00080000 (Boot1 ROM)
+ 0xFED7E000 - 0xFED7F000 0xC4000000 - 0xC4001000 (ADC)
+*/
+#define NSPIRE_APB_PHYS_BASE 0x90000000
+#define NSPIRE_APB_SIZE SZ_2M
+#define NSPIRE_APB_VIRT_BASE 0xFEE00000
+
+#define NSPIRE_APB_PHYS(x) ((NSPIRE_APB_PHYS_BASE) + (x))
+#define NSPIRE_APB_VIRT(x) ((NSPIRE_APB_VIRT_BASE) + (x))
+#define NSPIRE_APB_VIRTIO(x) IOMEM(NSPIRE_APB_VIRT(x))
+
+
+#define NSPIRE_INTERRUPT_PHYS_BASE 0xDC000000
+#define NSPIRE_INTERRUPT_SIZE SZ_4K
+#define NSPIRE_INTERRUPT_VIRT_BASE 0xFEDFF000
+
+#define NSPIRE_LCD_PHYS_BASE 0xC0000000
+
+#define NSPIRE_OTG_PHYS_BASE 0xB0000000
+#define NSPIRE_OTG_SIZE SZ_8K
+
+#define NSPIRE_NAND_PHYS_BASE 0x81000000
+#define NSPIRE_NAND_SIZE SZ_16M
+
+#define NSPIRE_BOOT1_PHYS_BASE 0x00000000
+#define NSPIRE_BOOT1_SIZE 0x00080000
+#define NSPIRE_BOOT1_VIRT_BASE 0xFED7F000
+
+#define NSPIRE_APB_GPIO 0x00000
+#define NSPIRE_APB_UART 0x20000
+#define NSPIRE_APB_I2C 0x50000
+#define NSPIRE_APB_WATCHDOG 0x60000
+#define NSPIRE_APB_RTC 0x90000
+#define NSPIRE_APB_MISC 0xA0000
+#define NSPIRE_APB_POWER 0xB0000
+#define NSPIRE_APB_TIMER2 0xD0000
+#define NSPIRE_APB_KEYPAD 0xE0000
+#define NSPIRE_APB_CONTRAST 0xF0000
+
+#define NSPIRE_SRAM_PHYS_BASE 0xA4000000
+#define NSPIRE_SRAM_SIZE 0x00020000
+
+#define NSPIRE_ADC_PHYS_BASE 0xC4000000
+#define NSPIRE_ADC_SIZE SZ_4K
+#define NSPIRE_ADC_VIRT_BASE 0xFED7E000
+
+#endif
diff --git a/arch/arm/mach-nspire/include/mach/sram.h b/arch/arm/mach-nspire/include/mach/sram.h
new file mode 100644
index 0000000..2f652f3
--- /dev/null
+++ b/arch/arm/mach-nspire/include/mach/sram.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+#ifndef NSPIRE_SRAM_H
+#define NSPIRE_SRAM_H
+
+void *sram_alloc(unsigned int size, dma_addr_t *dma_addr);
+void sram_free(dma_addr_t addr, unsigned int size);
+int __init sram_init(unsigned long base, unsigned long size);
+
+#endif
diff --git a/arch/arm/mach-nspire/include/mach/timex.h b/arch/arm/mach-nspire/include/mach/timex.h
new file mode 100644
index 0000000..2d4449e
--- /dev/null
+++ b/arch/arm/mach-nspire/include/mach/timex.h
@@ -0,0 +1,15 @@
+/*
+ * linux/arch/arm/mach-nspire/include/mach/timex.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef NSPIRE_TIMEX_H
+#define NSPIRE_TIMEX_H
+
+#endif
diff --git a/arch/arm/mach-nspire/include/mach/uncompress.h b/arch/arm/mach-nspire/include/mach/uncompress.h
new file mode 100644
index 0000000..7be9d06
--- /dev/null
+++ b/arch/arm/mach-nspire/include/mach/uncompress.h
@@ -0,0 +1,42 @@
+/*
+ * linux/arch/arm/mach-nspire/include/mach/uncompress.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ * Copyright (C) 2013 Lionel Debroux <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef NSPIRE_UNCOMPRESS_H
+#define NSPIRE_UNCOMPRESS_H
+
+#include <mach/nspire_mmio.h>
+
+#define OFFSET_VAL(var, offset) ((var)[(offset)>>2])
+static inline void putc(int c)
+{
+ volatile unsigned __attribute__((unused)) *serial_base =
+ (volatile unsigned *) NSPIRE_APB_PHYS(NSPIRE_APB_UART);
+
+#ifdef CONFIG_NSPIRE_EARLYPRINTK_CLASSIC
+ OFFSET_VAL(serial_base, 0x00) = (unsigned char)c;
+ while (!(OFFSET_VAL(serial_base, 0x14) & (1<<5)))
+ barrier();
+#endif
+
+#ifdef CONFIG_NSPIRE_EARLYPRINTK_CX
+ OFFSET_VAL(serial_base, 0x00) = (unsigned char)c;
+ while (OFFSET_VAL(serial_base, 0x18) & (1<<5))
+ barrier();
+#endif
+
+}
+#undef OFFSET_VAL
+
+#define arch_decomp_setup()
+#define flush()
+
+#endif
diff --git a/arch/arm/mach-nspire/keypad.c b/arch/arm/mach-nspire/keypad.c
new file mode 100644
index 0000000..5cd9344
--- /dev/null
+++ b/arch/arm/mach-nspire/keypad.c
@@ -0,0 +1,100 @@
+/*
+ * linux/arch/arm/mach-nspire/keypad.c
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/input.h>
+
+#include <mach/keypad.h>
+
+unsigned int nspire_touchpad_evtcode_map[][KEYPAD_BITMASK_COLS] = {
+ {
+ KEY_ENTER, KEY_ENTER, 0, 0,
+ KEY_SPACE, KEY_Z, KEY_Y, KEY_0,
+ KEY_TAB, 0, 0,
+ },
+ {
+ KEY_X, KEY_W, KEY_V, KEY_3,
+ KEY_U, KEY_T, KEY_S, KEY_1,
+ 0, 0, KEY_RIGHT
+ },
+ {
+ KEY_R, KEY_Q, KEY_P, KEY_6,
+ KEY_O, KEY_N, KEY_M, KEY_4,
+ KEY_APOSTROPHE, KEY_DOWN, 0
+ },
+ {
+ KEY_L, KEY_K, KEY_J, KEY_9,
+ KEY_I, KEY_H, KEY_G, KEY_7,
+ KEY_SLASH, KEY_LEFT, 0
+ },
+ {
+ KEY_F, KEY_E, KEY_D, 0,
+ KEY_C, KEY_B, KEY_A, KEY_EQUAL,
+ KEY_KPASTERISK, KEY_UP, 0
+ },
+ {
+ 0, KEY_LEFTALT, KEY_MINUS, KEY_RIGHTBRACE,
+ KEY_DOT, KEY_LEFTBRACE, KEY_5, 0,
+ KEY_SEMICOLON, KEY_BACKSPACE, KEY_DELETE
+ },
+ {
+ KEY_BACKSLASH, 0, KEY_KPPLUS, KEY_PAGEUP,
+ KEY_2, KEY_PAGEDOWN, KEY_8, KEY_ESC,
+ 0, KEY_TAB, 0
+ },
+ {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ KEY_LEFTSHIFT, KEY_LEFTCTRL, KEY_COMMA
+ },
+};
+
+unsigned int nspire_clickpad_evtcode_map[][KEYPAD_BITMASK_COLS] = {
+ {
+ KEY_ENTER, KEY_ENTER, KEY_SPACE, 0,
+ KEY_Z, KEY_DOT, KEY_Y, KEY_0,
+ KEY_X, 0, 0,
+ },
+ {
+ KEY_COMMA, KEY_KPPLUS, KEY_W, KEY_3,
+ KEY_V, KEY_2, KEY_U, KEY_1,
+ KEY_T, 0, 0
+ },
+ {
+ KEY_KPSLASH, KEY_MINUS, KEY_S, KEY_6,
+ KEY_R, KEY_5, KEY_Q, KEY_4,
+ KEY_P, 0, 0
+ },
+ {
+ KEY_SEMICOLON, KEY_KPASTERISK, KEY_O, KEY_9,
+ KEY_N, KEY_8, KEY_M, KEY_7,
+ KEY_L, 0, 0
+ },
+ {
+ KEY_APOSTROPHE, KEY_SLASH, KEY_K, 0,
+ KEY_J, 0, KEY_I, 0,
+ KEY_H, 0, 0
+ },
+ {
+ KEY_APOSTROPHE, 0, KEY_G, KEY_RIGHTBRACE,
+ KEY_F, KEY_LEFTBRACE, KEY_E, KEY_DELETE,
+ KEY_D, KEY_LEFTSHIFT, 0
+ },
+ {
+ 0, KEY_ENTER, KEY_C, KEY_PAGEUP,
+ KEY_B, KEY_PAGEDOWN, KEY_A, KEY_ESC,
+ KEY_BACKSLASH, KEY_TAB, 0
+ },
+ {
+ KEY_UP, 0, KEY_RIGHT, 0,
+ KEY_DOWN, 0, KEY_LEFT, KEY_BACKSPACE,
+ KEY_LEFTCTRL, 0, KEY_EQUAL
+ },
+};
diff --git a/arch/arm/mach-nspire/nspire_clp.c b/arch/arm/mach-nspire/nspire_clp.c
new file mode 100644
index 0000000..945ab5d
--- /dev/null
+++ b/arch/arm/mach-nspire/nspire_clp.c
@@ -0,0 +1,57 @@
+/*
+ * linux/arch/arm/mach-nspire/nspire_clp.c
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/clkdev.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd.h>
+#include <linux/input.h>
+#include <linux/usb/ehci_pdriver.h>
+#include <linux/mtd/nand.h>
+
+#include <mach/nspire_mmio.h>
+#include <mach/irqs.h>
+#include <mach/clkdev.h>
+#include <mach/sram.h>
+#include <mach/keypad.h>
+
+#include <asm/mach/time.h>
+#include <asm/mach/map.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include "common.h"
+#include "classic.h"
+
+static void __init clp_init(void)
+{
+ nspire_keypad_data.evtcodes = nspire_clickpad_evtcode_map;
+ platform_device_register(&nspire_otg_device);
+ platform_device_register(&nspire_usb_nop_xceiver);
+
+ nspire_classic_init();
+}
+
+MACHINE_START(NSPIRECLP, "TI-NSPIRE Clickpad Calculator")
+ .map_io = nspire_map_io,
+ .init_irq = nspire_classic_init_irq,
+ .handle_irq = nspire_classic_handle_irq,
+ .init_time = nspire_classic_timer_init,
+ .init_early = nspire_classic_init_early,
+ .init_machine = clp_init,
+ .init_late = nspire_classic_init_late,
+ .restart = nspire_restart,
+MACHINE_END
diff --git a/arch/arm/mach-nspire/nspire_cx.c b/arch/arm/mach-nspire/nspire_cx.c
new file mode 100644
index 0000000..81bbbf8
--- /dev/null
+++ b/arch/arm/mach-nspire/nspire_cx.c
@@ -0,0 +1,316 @@
+/*
+ * linux/arch/arm/mach-nspire/nspire_cx.c
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/clkdev.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd.h>
+#include <linux/input.h>
+#include <linux/usb/ehci_pdriver.h>
+#include <linux/mtd/nand.h>
+#include <linux/irqchip/arm-vic.h>
+
+#include <mach/nspire_mmio.h>
+#include <mach/nspire_clock.h>
+#include <mach/irqs.h>
+#include <mach/clkdev.h>
+#include <mach/sram.h>
+#include <mach/keypad.h>
+
+#include <asm/mach/time.h>
+#include <asm/hardware/timer-sp.h>
+#include <asm/mach/map.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include "common.h"
+#include "touchpad.h"
+
+/* Clock */
+
+union reg_clk_speed {
+ unsigned long raw;
+ struct {
+ unsigned long __padding0:1;
+ unsigned long base_cpu_ratio:7;
+ unsigned long is_base_48mhz:1;
+ unsigned long __padding1:3;
+ unsigned long cpu_ahb_ratio:3;
+ unsigned long base_val:6;
+ unsigned long unknown:2;
+ } val;
+};
+
+static struct nspire_clk_speeds cx_io_to_clocks(unsigned long val)
+{
+ struct nspire_clk_speeds clks;
+ union reg_clk_speed reg;
+
+ reg.raw = val;
+ reg.val.base_cpu_ratio *= reg.val.unknown;
+ reg.val.cpu_ahb_ratio++;
+
+ BUG_ON(reg.val.base_cpu_ratio == 0);
+
+ clks.base = reg.val.is_base_48mhz ? 48 : 6*reg.val.base_val;
+ clks.base *= 1000000; /* Convert to Hz */
+
+ clks.div.base_cpu = reg.val.base_cpu_ratio;
+ clks.div.cpu_ahb = reg.val.cpu_ahb_ratio;
+
+ return clks;
+}
+
+static unsigned long cx_clocks_to_io(struct nspire_clk_speeds *clks)
+{
+ union reg_clk_speed reg;
+
+ BUG_ON(clks->div.base_cpu < 1);
+ BUG_ON(clks->div.cpu_ahb < 1);
+
+ reg.raw = 0;
+ reg.val.unknown = (clks->div.base_cpu & 0x1) ? 0b01 : 0b10;
+ reg.val.base_cpu_ratio = clks->div.base_cpu / reg.val.unknown;
+ reg.val.cpu_ahb_ratio = clks->div.cpu_ahb - 1;
+ reg.val.is_base_48mhz = (clks->base <= 48000000);
+ reg.val.base_val = (clks->base / 6000000);
+
+ return reg.raw;
+}
+
+/* IRQ */
+static void __init cx_init_irq(void)
+{
+ vic_init(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE), 0, NSPIRE_IRQ_MASK, 0);
+}
+
+/* UART */
+
+static AMBA_APB_DEVICE(uart, "uart", 0, NSPIRE_APB_PHYS(NSPIRE_APB_UART),
+ { NSPIRE_IRQ_UART }, NULL);
+
+/* TIMER */
+
+void __init cx_timer_init(void)
+{
+ sp804_clockevents_init(NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2),
+ NSPIRE_IRQ_TIMER2, "timer2");
+}
+
+/* FRAMEBUFFER */
+static struct clcd_panel cx_lcd_panel = {
+ .mode = {
+ .name = "color lcd",
+ .refresh = 60,
+ .xres = 320,
+ .yres = 240,
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+ .pixclock = 1,
+ .hsync_len = 6,
+ .vsync_len = 1,
+ .right_margin = 50,
+ .left_margin = 38,
+ .lower_margin = 3,
+ .upper_margin = 17,
+ },
+ .width = 65, /* ~6.50 cm */
+ .height = 49, /* ~4.87 cm */
+ .tim2 = TIM2_IPC,
+ .cntl = (CNTL_BGR | CNTL_LCDTFT | CNTL_LCDVCOMP(1) |
+ CNTL_LCDBPP16_565),
+ .bpp = 16,
+};
+#define PANEL_SIZE (38 * SZ_4K)
+
+static int cx_clcd_setup(struct clcd_fb *fb)
+{
+ return nspire_clcd_setup(fb, PANEL_SIZE, &cx_lcd_panel);
+}
+
+static struct clcd_board cx_clcd_data = {
+ .name = "lcd controller",
+ .check = clcdfb_check,
+ .decode = clcdfb_decode,
+ .setup = cx_clcd_setup,
+ .mmap = nspire_clcd_mmap,
+ .remove = nspire_clcd_remove,
+};
+
+static AMBA_AHB_DEVICE(fb, "fb", 0, NSPIRE_LCD_PHYS_BASE,
+ { NSPIRE_IRQ_LCD }, &cx_clcd_data);
+
+/* USB HOST */
+
+static struct usb_ehci_pdata cxusbhost_pdata = {
+ .has_tt = 1,
+ .caps_offset = 0x100
+};
+
+static struct resource cxusbhost_resources_pdata[] = {
+ RESOURCE_ENTRY_MEM(OTG),
+ RESOURCE_ENTRY_IRQ(OTG)
+};
+
+static struct platform_device usbhost_device = {
+ .name = "ehci-platform",
+ .id = 0,
+ .dev = {
+ .platform_data = &cxusbhost_pdata,
+ .coherent_dma_mask = ~0,
+ .dma_mask = &nspire_usb_dma_mask
+ },
+ .resource = cxusbhost_resources_pdata,
+ .num_resources = ARRAY_SIZE(cxusbhost_resources_pdata)
+};
+
+
+static __init int cx_usb_init(void)
+{
+ int err = 0;
+ unsigned val;
+ void __iomem *hostusb_addr =
+ ioremap(NSPIRE_OTG_PHYS_BASE, NSPIRE_OTG_SIZE);
+
+ if (!hostusb_addr) {
+ pr_warn("Could not allocate enough memory to initialize NSPIRE host USB\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* Disable OTG interrupts */
+ pr_info("Disable OTG interrupts\n");
+ val = readl(hostusb_addr + 0x1a4);
+ val &= ~(0x7f<<24);
+ writel(val, hostusb_addr + 0x1a4);
+
+ iounmap(hostusb_addr);
+
+ pr_info("Adding USB controller as platform device\n");
+ err = platform_device_register(&usbhost_device);
+out:
+
+ return err;
+}
+
+static __init int cx_usb_workaround(void)
+{
+ int err = 0;
+ unsigned val;
+ void __iomem *hostusb_addr =
+ ioremap(NSPIRE_OTG_PHYS_BASE, NSPIRE_OTG_SIZE);
+
+ if (!hostusb_addr) {
+ pr_warn("Could not do USB workaround\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ pr_info("Temporary USB hack to force USB to connect as fullspeed\n");
+ val = readl(hostusb_addr + 0x184);
+ val |= (1<<24);
+ writel(val, hostusb_addr + 0x184);
+
+ iounmap(hostusb_addr);
+out:
+
+ return err;
+}
+
+/* Backlight driver */
+
+#define CX_BACKLIGHT_UPPER 0x1d0
+#define CX_BACKLIGHT_LOWER 0x100 /* Should be (around about) off */
+
+static void cx_set_backlight(int val)
+{
+ val += CX_BACKLIGHT_LOWER;
+
+ if (val <= CX_BACKLIGHT_UPPER)
+ writel(val, NSPIRE_APB_VIRTIO(NSPIRE_APB_CONTRAST + 0x20));
+}
+
+static struct generic_bl_info cx_bl = {
+ .name = "nspire_backlight",
+ .max_intensity = CX_BACKLIGHT_UPPER - CX_BACKLIGHT_LOWER,
+ .default_intensity = (CX_BACKLIGHT_UPPER - CX_BACKLIGHT_LOWER) / 2,
+ .set_bl_intensity = cx_set_backlight
+};
+
+static struct platform_device bl_device = {
+ .name = "generic-bl",
+ .id = 0,
+ .dev = {
+ .platform_data = &cx_bl,
+ }
+};
+
+/* Init */
+
+bool cx_use_otg;
+static int __init set_cx_otg(char *dummy __attribute__((unused)))
+{
+ cx_use_otg = 1;
+ return 0;
+}
+early_param("cx_use_otg", set_cx_otg);
+
+static void __init cx_early_init(void)
+{
+ nspire_io_to_clocks = cx_io_to_clocks;
+ nspire_clocks_to_io = cx_clocks_to_io;
+
+ nspire_init_early();
+}
+
+static void __init cx_init(void)
+{
+ nspire_init();
+ amba_device_register(&fb_device, &iomem_resource);
+ amba_device_register(&uart_device, &iomem_resource);
+
+ nspire_keypad_data.evtcodes = nspire_touchpad_evtcode_map;
+ platform_device_register(&nspire_keypad_device);
+ platform_device_register(&bl_device);
+ nspire_touchpad_init();
+
+ if (!cx_use_otg) {
+ pr_info("Selecting USB host only driver for CX\n");
+ cx_usb_init();
+ } else {
+ pr_info("Selecting USB OTG driver for CX\n");
+ platform_device_register(&nspire_otg_device);
+ platform_device_register(&nspire_usb_nop_xceiver);
+ }
+}
+
+static void __init cx_init_late(void)
+{
+ if (!cx_use_otg)
+ cx_usb_workaround();
+ nspire_init_late();
+}
+
+MACHINE_START(NSPIRECX, "TI-NSPIRE CX Calculator")
+ .nr_irqs = NR_IRQS,
+ .map_io = nspire_map_io,
+ .init_irq = cx_init_irq,
+ .init_time = cx_timer_init,
+ .init_early = cx_early_init,
+ .init_machine = cx_init,
+ .init_late = cx_init_late,
+ .restart = nspire_restart,
+MACHINE_END
diff --git a/arch/arm/mach-nspire/nspire_tp.c b/arch/arm/mach-nspire/nspire_tp.c
new file mode 100644
index 0000000..cec05b6
--- /dev/null
+++ b/arch/arm/mach-nspire/nspire_tp.c
@@ -0,0 +1,80 @@
+/*
+ * linux/arch/arm/mach-nspire/nspire_clp.c
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/clkdev.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd.h>
+#include <linux/input.h>
+#include <linux/usb/ehci_pdriver.h>
+#include <linux/mtd/nand.h>
+#include <linux/irq.h>
+#include <linux/i2c-gpio.h>
+
+#include <mach/nspire_mmio.h>
+#include <mach/irqs.h>
+#include <mach/clkdev.h>
+#include <mach/sram.h>
+#include <mach/keypad.h>
+
+#include <asm/mach/time.h>
+#include <asm/mach/map.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+#include "common.h"
+#include "classic.h"
+#include "touchpad.h"
+
+/* I2C GPIO (touchpad) */
+
+static struct i2c_gpio_platform_data i2c_pdata = {
+ .sda_pin = 3,
+ .scl_pin = 1,
+ .udelay = 1,
+ .timeout = 1000,
+};
+
+static struct platform_device i2c_device = {
+ .name = "i2c-gpio",
+ .id = 0,
+ .dev = {
+ .platform_data = &i2c_pdata,
+ }
+};
+
+static void __init tp_init(void)
+{
+ nspire_keypad_data.evtcodes = nspire_touchpad_evtcode_map;
+ platform_device_register(&i2c_device);
+
+ platform_device_register(&nspire_otg_device);
+ platform_device_register(&nspire_usb_nop_xceiver);
+
+ nspire_classic_init();
+ nspire_touchpad_init();
+}
+
+MACHINE_START(NSPIRETP, "TI-NSPIRE Touchpad Calculator")
+ .map_io = nspire_map_io,
+ .init_irq = nspire_classic_init_irq,
+ .handle_irq = nspire_classic_handle_irq,
+ .init_time = nspire_classic_timer_init,
+ .init_early = nspire_classic_init_early,
+ .init_machine = tp_init,
+ .init_late = nspire_classic_init_late,
+ .restart = nspire_restart,
+MACHINE_END
diff --git a/arch/arm/mach-nspire/sram.c b/arch/arm/mach-nspire/sram.c
new file mode 100644
index 0000000..52b8e5c
--- /dev/null
+++ b/arch/arm/mach-nspire/sram.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/genalloc.h>
+
+static unsigned long sram_phys_base;
+static __iomem void *sram_virt_base;
+static struct gen_pool *sram_pool;
+
+#define sram_phys_to_virt(p) (sram_virt_base + ((p) - sram_phys_base))
+
+void *sram_alloc(unsigned int size, dma_addr_t *dma_addr)
+{
+ if (!sram_pool)
+ return NULL;
+
+ *dma_addr = gen_pool_alloc(sram_pool, size);
+ pr_info("sram alloc - %dB@0x%p\n", size, (void *)*dma_addr);
+ return sram_phys_to_virt(*dma_addr);
+}
+EXPORT_SYMBOL(sram_alloc);
+
+void sram_free(dma_addr_t addr, unsigned int size)
+{
+ if (!sram_pool)
+ return;
+
+ gen_pool_free(sram_pool, addr, size);
+}
+EXPORT_SYMBOL(sram_free);
+
+int __init sram_init(unsigned long base, unsigned long size)
+{
+ sram_phys_base = base;
+
+ sram_pool = gen_pool_create(10, -1);
+ if (!sram_pool) {
+ pr_warn("Cannot create sram pool!\n");
+ return -ENOMEM;
+ }
+ gen_pool_add(sram_pool, base, size, -1);
+ sram_virt_base = ioremap(sram_phys_base, size);
+
+ pr_info("sram pool: %ld KB@0x%p\n", size / 1024, sram_virt_base);
+ return 0;
+}
diff --git a/arch/arm/mach-nspire/touchpad.c b/arch/arm/mach-nspire/touchpad.c
new file mode 100644
index 0000000..2330451
--- /dev/null
+++ b/arch/arm/mach-nspire/touchpad.c
@@ -0,0 +1,30 @@
+/*
+ * linux/arch/arm/mach-nspire/touchpad.c
+ *
+ * Copyright (C) 2012 Fabian Vogt <[email protected]>
+ *
+ * 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/i2c.h>
+
+#include "touchpad.h"
+
+#if defined(CONFIG_MOUSE_SYNAPTICS_I2C) || \
+ defined(CONFIG_MOUSE_SYNAPTICS_I2C_MODULE)
+static struct i2c_board_info synaptics_i2c = {
+ I2C_BOARD_INFO("synaptics_i2c", 0x20),
+ .irq = 0,
+};
+
+void __init nspire_touchpad_init()
+{
+ i2c_register_board_info(0, &synaptics_i2c, 1);
+}
+
+#else
+inline void nspire_touchpad_init() {}
+#endif
diff --git a/arch/arm/mach-nspire/touchpad.h b/arch/arm/mach-nspire/touchpad.h
new file mode 100644
index 0000000..9222572
--- /dev/null
+++ b/arch/arm/mach-nspire/touchpad.h
@@ -0,0 +1,17 @@
+/*
+ * linux/arch/arm/mach-nspire/touchpad.c
+ *
+ * Copyright (C) 2012 Fabian Vogt <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef TOUCHPAD_H
+#define TOUCHPAD_H
+
+void __init nspire_touchpad_init(void);
+
+#endif
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index 831e1fd..e76c16b 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -1204,3 +1204,6 @@ baileys MACH_BAILEYS BAILEYS 4169
familybox MACH_FAMILYBOX FAMILYBOX 4170
ensemble_mx35 MACH_ENSEMBLE_MX35 ENSEMBLE_MX35 4171
sc_sps_1 MACH_SC_SPS_1 SC_SPS_1 4172
+nspireclp MACH_NSPIRECLP NSPIRECLP 4441
+nspiretp MACH_NSPIRETP NSPIRETP 4442
+nspirecx MACH_NSPIRECX NSPIRECX 4443
--
1.7.9.6 (Apple Git-31.1)


2013-04-04 11:12:40

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

On Thursday 04 April 2013, Daniel Tang wrote:
> We're sending out for comments an early patch adding TI-Nspire support
> to Linux.
>
> Some words on the Nspire platform: it's a series of graphing
> calculators, made of four models: "Clickpad" (2007-2010), "Touchpad"
> (2010-2012?), "CX" (2011-), "CM-C" (2011-).

Very cool! I hope we can get this merged.

> Patch contents
> --------------
> This patch (against mainline, but it also applies to linux-next)
> contains the essential support code required to boot Linux to a shell
> on all models, but contains nothing else at the moment.
> Code containing drivers for other peripherals exists, and will
> eventually be posted for review as well: we need to do a bit of
> cleanup. If you prefer them to be posted here now, we'll do.
> They're at https://github.com/tangrs/linux .
>
> A possibly noteworthy fact is that despite the gradual shift to
> using Device Tree definitions for ARM machine types, we've decided not
> to use it, for the following reasons:
>
> * the (perceived) extra complexity and code size;
> * the fact that we're using our own, simple, bootloader, due to
> the impossibility to bootstrap on most models (because boot1
> is usually not modifiable) and the fact that the image is stored
> in a proprietary FS not supported by mainline Linux and
> common bootloaders...

For new platforms, we want to have only the absolute minimum amount of
code in arch/arm and move everything else into drivers. However, that
is only possible using device tree. It should not add any significant
complexity to your code, and you can easily bundle the device tree blob
with the kernel.
>
> +config ARCH_NSPIRE
> + bool "TI-NSPIRE based"
> + depends on MMU
> + select CPU_ARM926T
> + select HAVE_MACH_CLKDEV
> + select CLKDEV_LOOKUP
> + select ARM_AMBA
> + select USB_ARCH_HAS_EHCI
> + select ARCH_WANT_OPTIONAL_GPIOLIB
> + select GENERIC_ALLOCATOR
> + select ARCH_HAS_CPUFREQ
> + select CPU_FREQ_TABLE

Any new platform should use COMMON_CLK and SPARSE_IRQ in addition
to these.

> diff --git a/arch/arm/mach-nspire/Kconfig b/arch/arm/mach-nspire/Kconfig
> new file mode 100644
> index 0000000..da032b7
> --- /dev/null
> +++ b/arch/arm/mach-nspire/Kconfig
> @@ -0,0 +1,36 @@
> +if ARCH_NSPIRE
> +
> +choice
> + prompt "Early printk and boot message serial interface"
> + help
> + Early printk output interface
> + depends on EARLY_PRINTK
> + default NSPIRE_EARLYPRINTK_CX
> +
> +config NSPIRE_EARLYPRINTK_CLASSIC
> + bool "Classic"
> +
> +config NSPIRE_EARLYPRINTK_CX
> + bool "CX model"
> +endchoice

This should go into arch/arm/Kconfig.debug.

> +
> +menu "Supported models"
> +
> +config MACH_NSPIRECX
> + select GENERIC_CLOCKEVENTS
> + select ARM_VIC
> + select ARM_TIMER_SP804
> + bool "CX/CX CAS"

The SP804 driver is currently being changed in the move to drivers/clocksource,
which may impact your code as well.

> diff --git a/arch/arm/mach-nspire/Makefile.boot b/arch/arm/mach-nspire/Makefile.boot
> new file mode 100644
> index 0000000..7db966b
> --- /dev/null
> +++ b/arch/arm/mach-nspire/Makefile.boot
> @@ -0,0 +1 @@
> +zreladdr-y := 0x10008000

Please use AUTO_ZRELADDR

> +static unsigned long classic_clocks_to_io(struct nspire_clk_speeds *clks)
> +{
> + union reg_clk_speed reg;
> +
> + BUG_ON(clks->div.base_cpu < 2);
> + BUG_ON(clks->div.cpu_ahb < 1);
> +
> + reg.raw = 0;
> + reg.val.base_cpu_ratio = clks->div.base_cpu / 2;
> + reg.val.cpu_ahb_ratio = clks->div.cpu_ahb - 1;
> + reg.val.is_base_27mhz = (clks->base <= 27000000);
> + reg.val.base_val = (300 - (clks->base / 1000000)) / 6;
> +
> + return reg.raw;
> +}

Clock code should live in drivers/clk/

> +/* Interrupt handling */
> +
> +static inline int check_interrupt(void __iomem *base, struct pt_regs *regs)
> +{
> + if (readl(base + 0x0)) {
> + int irqnr = readl(base + 0x24);
> + unsigned prev_priority;
> + handle_IRQ(irqnr, regs);
> +
> + /* Reset priorities */
> + prev_priority = readl(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x28));
> + writel(prev_priority, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x2c));
> + return 1;
> + }
> + return 0;
> +}

Interrupt code should live in drivers/irqchip/

Your method of doing "readl(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x28));" has multiple
flaws:

* A FOO_VIRT_BASE constant should already be of type "void __iomem *" and not
require another IOMEM()
* Normally the base address is generated at runtime using ioremap or of_iomap,
since in multiplatform kernel, you have no access to platform specific constants
from a device driver
* You might want to define symbolic constants for "0x28".

> +void __init nspire_classic_init_irq(void)
> +{
> + /* No stickies */
> + writel(0, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x204));
> +
> + /* Disable all interrupts */
> + writel(~0, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0xc));
> + writel(~0, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x10c));
> +
> + /* Set all priorities to 0 */
> + memset_io(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x300), 0, 0x7f);
> +
> + /* Accept interrupts of all priorities */
> + writel(0xf, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x2c));
> + writel(0xf, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x12c));
> +
> + /* Clear existing interrupts */
> + readl(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x28));
> + readl(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x128));
> +
> + /* Add chip */
> + classic_allocate_gc();
> +}

For sparse irq and for devicetree, you need to allocate an irqdomain here.

> +
> +
> +static struct clock_event_device nspire_clkevt = {
> + .name = "clockevent",
> + .features = CLOCK_EVT_FEAT_ONESHOT,
> + .shift = 32,
> + .rating = 400,
> + .set_next_event = classic_timer_set_event,
> + .set_mode = classic_timer_set_mode,
> + .cpumask = cpu_all_mask,
> +};

clockevent code should live in drivers/clocksource, although there
is ongoing discussion about creating a new directory for clockevent
separate from clocksource. Not in arch/arm though.

> +/* Serial */
> +static struct plat_serial8250_port classic_serial_platform_data[] = {
> + {
> + .mapbase = NSPIRE_APB_PHYS(NSPIRE_APB_UART),
> + .irq = NSPIRE_IRQ_UART,
> + .uartclk = 29491200,
> + .iotype = UPIO_MEM,
> + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST |
> + UPF_IOREMAP,
> + .regshift = 2
> + },
> + { }
> +};
> +
> +struct platform_device nspire_classic_serial_device = {
> + .name = "serial8250",
> + .id = PLAT8250_DEV_PLATFORM,
> + .dev = {
> + .platform_data = &classic_serial_platform_data
> + }
> +};

You can use the of_serial driver to get that information from the device tree.

> > +/* Framebuffer */
> +static struct clcd_panel classic_lcd_panel = {
> + .mode = {
> + .name = "grayscale lcd",
> + .refresh = 60,
> + .xres = 320,
> + .yres = 240,
> + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
> + .vmode = FB_VMODE_NONINTERLACED,
> + .pixclock = 1,
> + .hsync_len = 6,
> + .vsync_len = 1,
> + .right_margin = 6,
> + .left_margin = 6,
> + },
> + .width = 71, /* 7.11cm */
> + .height = 53, /* 5.33cm */
> + .tim2 = 0x80007d0,
> + .cntl = CNTL_LCDBPP8 | CNTL_LCDMONO8,
> + .bpp = 8,
> + .grayscale = 1
> +};
> +#define PANEL_SIZE (19 * SZ_4K)
> +
> +static int classic_clcd_setup(struct clcd_fb *fb)
> +{
> + return nspire_clcd_setup(fb, PANEL_SIZE, &classic_lcd_panel);
> +}

And drivers/video/of_display_timing.c for these.

> diff --git a/arch/arm/mach-nspire/clock.c b/arch/arm/mach-nspire/clock.c
> new file mode 100644
> index 0000000..c679449
> --- /dev/null
> +++ b/arch/arm/mach-nspire/clock.c

Not needed with COMMON_CLK


> +
> +#define IOTABLE_ENTRY(t) \
> + { \
> + .virtual = NSPIRE_##t##_VIRT_BASE, \
> + .pfn = __phys_to_pfn(NSPIRE_##t##_PHYS_BASE), \
> + .length = NSPIRE_##t##_SIZE, \
> + .type = MT_DEVICE \
> + }
> +
> +#define RESOURCE_ENTRY_IRQ(t) \
> + { \
> + .start = NSPIRE_IRQ_##t, \
> + .end = NSPIRE_IRQ_##t, \
> + .flags = IORESOURCE_IRQ \
> + }
> +
> +#define RESOURCE_ENTRY_MEM(t) \
> + { \
> + .start = NSPIRE_##t##_PHYS_BASE, \
> + .end = NSPIRE_##t##_PHYS_BASE + NSPIRE_##t##_SIZE - 1, \
> + .flags = IORESOURCE_MEM \
> + }

Please don't define your own macros for generic functionality. If you think
that macros are good for these, you can submit a patch to make them available
for everyone.

For the resources, we already have macros in include/linux/ioport.h

> +extern struct platform_device nspire_keypad_device;
> +extern struct platform_device nspire_otg_device;
> +extern struct platform_device nspire_usb_nop_xceiver;
> +extern struct nspire_keypad_data nspire_keypad_data;

Do not define "struct platform_devices" as global data structures, it messes
up reference counting. Ideally all platform and AMBA devices get created
from device tree. If that should not be possible for one of them, you can
use platform_device_register_resndata or the associated functions.

> diff --git a/arch/arm/mach-nspire/include/mach/clkdev.h b/arch/arm/mach-nspire/include/mach/clkdev.h
> new file mode 100644
> index 0000000..b4afe65
> --- /dev/null
> +++ b/arch/arm/mach-nspire/include/mach/clkdev.h

Not needed with common clk.

> diff --git a/arch/arm/mach-nspire/include/mach/debug-macro.S b/arch/arm/mach-nspire/include/mach/debug-macro.S
> new file mode 100644
> index 0000000..b71daa4
> --- /dev/null
> +++ b/arch/arm/mach-nspire/include/mach/debug-macro.S

Move this to arch/arm/include/debug/

> diff --git a/arch/arm/mach-nspire/include/mach/hardware.h b/arch/arm/mach-nspire/include/mach/hardware.h
> new file mode 100644
> index 0000000..7c9c3b6
> --- /dev/null
> +++ b/arch/arm/mach-nspire/include/mach/hardware.h

delete this

> diff --git a/arch/arm/mach-nspire/include/mach/irqs.h b/arch/arm/mach-nspire/include/mach/irqs.h
> new file mode 100644
> index 0000000..70be120
> --- /dev/null
> +++ b/arch/arm/mach-nspire/include/mach/irqs.h
> @@ -0,0 +1,34 @@
> +/*
> + * linux/arch/arm/mach-nspire/include/mach/irqs.h
> + *
> + * Copyright (C) 2012 Daniel Tang <[email protected]>
> + *
> + * 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.
> + *
> + */
> +
> +#ifndef NSPIRE_IRQS_H
> +#define NSPIRE_IRQS_H
> +
> +#define NSPIRE_IRQ_MASK 0x007FEB9A
> +
> +enum {
> + NSPIRE_IRQ_UART = 1,
> + NSPIRE_IRQ_WATCHDOG = 3,
> + NSPIRE_IRQ_RTC = 4,
> + NSPIRE_IRQ_GPIO = 7,
> + NSPIRE_IRQ_OTG = 8,
> + NSPIRE_IRQ_HOSTUSB = 9,
> + NSPIRE_IRQ_ADC = 11,
> + NSPIRE_IRQ_PWR = 15,
> + NSPIRE_IRQ_KEYPAD = 16,
> + NSPIRE_IRQ_TIMER2 = 19,
> + NSPIRE_IRQ_I2C = 20,
> + NSPIRE_IRQ_LCD = 21
> +};
> +
> +#define NR_IRQS 32
> +
> +#endif

Not needed as a globally visible file with sparse IRQ.

> diff --git a/arch/arm/mach-nspire/include/mach/keypad.h b/arch/arm/mach-nspire/include/mach/keypad.h
> new file mode 100644
> index 0000000..18cf3fd
> --- /dev/null
> +++ b/arch/arm/mach-nspire/include/mach/keypad.h

Move this to include/linux/platform_data or delete this once you have converted
to device tree.


> diff --git a/arch/arm/mach-nspire/include/mach/memory.h b/arch/arm/mach-nspire/include/mach/memory.h
> new file mode 100644
> index 0000000..eec3f36
> --- /dev/null
> +++ b/arch/arm/mach-nspire/include/mach/memory.h
> @@ -0,0 +1,17 @@
> +/*
> + * linux/arch/arm/mach-nspire/include/mach/memory.h
> + *
> + * Copyright (C) 2012 Daniel Tang <[email protected]>
> + *
> + * 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.
> + *
> + */
> +
> +#ifndef NSPIRE_MEMORY_H
> +#define NSPIRE_MEMORY_H
> +
> +#define PLAT_PHYS_OFFSET 0x10000000
> +
> +#endif

I think you already don't use this.

> diff --git a/arch/arm/mach-nspire/include/mach/nspire_mmio.h b/arch/arm/mach-nspire/include/mach/nspire_mmio.h
> new file mode 100644
> index 0000000..eaeb100
> --- /dev/null
> +++ b/arch/arm/mach-nspire/include/mach/nspire_mmio.h

Move to arch/arm/mach-nspire/, drivers should not see this.

> diff --git a/arch/arm/mach-nspire/include/mach/sram.h b/arch/arm/mach-nspire/include/mach/sram.h
> new file mode 100644
> index 0000000..2f652f3
> --- /dev/null
> +++ b/arch/arm/mach-nspire/include/mach/sram.h

A new sram subsystem is getting added, so don't provide your own copy.

> diff --git a/arch/arm/mach-nspire/include/mach/timex.h b/arch/arm/mach-nspire/include/mach/timex.h
> new file mode 100644
> index 0000000..2d4449e
> --- /dev/null
> +++ b/arch/arm/mach-nspire/include/mach/timex.h

delete this.

> diff --git a/arch/arm/mach-nspire/include/mach/uncompress.h b/arch/arm/mach-nspire/include/mach/uncompress.h
> new file mode 100644
> index 0000000..7be9d06
> --- /dev/null
> +++ b/arch/arm/mach-nspire/include/mach/uncompress.h

You can delete this once you enable CONFIG_ARCH_MULTIPLATFORM

> +#if defined(CONFIG_MOUSE_SYNAPTICS_I2C) || \
> + defined(CONFIG_MOUSE_SYNAPTICS_I2C_MODULE)
> +static struct i2c_board_info synaptics_i2c = {
> + I2C_BOARD_INFO("synaptics_i2c", 0x20),
> + .irq = 0,
> +};
> +
> +void __init nspire_touchpad_init()
> +{
> + i2c_register_board_info(0, &synaptics_i2c, 1);
> +}
> +
> +#else
> +inline void nspire_touchpad_init() {}
> +#endif

Device definitions should not be conditional on the presence of the driver.
This will of course be no issue once you move to DT.

Arnd

2013-04-06 00:26:27

by Daniel Tang

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

Hi,

First of all, thank you for your comments!

On 04/04/2013, at 10:12 PM, Arnd Bergmann <[email protected]> wrote:

> For new platforms, we want to have only the absolute minimum amount of
> code in arch/arm and move everything else into drivers. However, that
> is only possible using device tree. It should not add any significant
> complexity to your code, and you can easily bundle the device tree blob
> with the kernel.

Given that most of your comments described some very fundamental changes (esp switching to DTB) to the structure of our port, we've decided we'll probably start from scratch and fix the issues you outlined as we reimplement our platform.

At the moment, we're working on getting a basic DTB-booting kernel working so our next patch will be starting from basics.

Cheers,
tangrs-

2013-04-06 11:51:26

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

On Saturday 06 April 2013, Daniel Tang wrote:
> Hi,
>
> First of all, thank you for your comments!
>
> On 04/04/2013, at 10:12 PM, Arnd Bergmann <[email protected]> wrote:
>
> > For new platforms, we want to have only the absolute minimum amount of
> > code in arch/arm and move everything else into drivers. However, that
> > is only possible using device tree. It should not add any significant
> > complexity to your code, and you can easily bundle the device tree blob
> > with the kernel.
>
> Given that most of your comments described some very fundamental changes
> (esp switching to DTB) to the structure of our port, we've decided we'll
> probably start from scratch and fix the issues you outlined as we
> reimplement our platform.
>
> At the moment, we're working on getting a basic DTB-booting kernel working
> so our next patch will be starting from basics.

Ok, whichever way you prefer. If you have questions while working on this,
feel free to join #armlinux on irc.freenode.net, there are usually other
people working on the same things.

Arnd

2013-04-06 12:00:27

by Daniel Tang

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

Hi,


On 06/04/2013, at 10:51 PM, Arnd Bergmann <[email protected]> wrote:
>
> Ok, whichever way you prefer. If you have questions while working on this,
> feel free to join #armlinux on irc.freenode.net, there are usually other
> people working on the same things.
>

Cheers.

We already have something basic that boots successfully using device trees.

Some comments before we continue would be greatly appreciated.

Signed-off-by: Daniel Tang <[email protected]>
---
arch/arm/Kconfig | 13 ++
arch/arm/Makefile | 3 +-
arch/arm/boot/dts/nspire-cx.dts | 85 +++++++++++++
arch/arm/boot/dts/nspire.dtsi | 154 +++++++++++++++++++++++
arch/arm/mach-nspire/Makefile | 3 +
arch/arm/mach-nspire/include/mach/debug-macro.S | 25 ++++
arch/arm/mach-nspire/include/mach/timex.h | 15 +++
arch/arm/mach-nspire/include/mach/uncompress.h | 25 ++++
arch/arm/mach-nspire/mmio.h | 13 ++
arch/arm/mach-nspire/nspire.c | 107 ++++++++++++++++
10 files changed, 442 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/boot/dts/nspire-cx.dts
create mode 100644 arch/arm/boot/dts/nspire.dtsi
create mode 100644 arch/arm/mach-nspire/Makefile
create mode 100644 arch/arm/mach-nspire/Makefile.boot
create mode 100644 arch/arm/mach-nspire/include/mach/debug-macro.S
create mode 100644 arch/arm/mach-nspire/include/mach/timex.h
create mode 100644 arch/arm/mach-nspire/include/mach/uncompress.h
create mode 100644 arch/arm/mach-nspire/mmio.h
create mode 100644 arch/arm/mach-nspire/nspire.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 1cacda4..e8ce316 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -980,6 +980,19 @@ config ARCH_OMAP1
help
Support for older TI OMAP1 (omap7xx, omap15xx or omap16xx)

+config ARCH_NSPIRE
+ bool "TI-NSPIRE based"
+ depends on MMU
+ select CPU_ARM926T
+ select COMMON_CLK
+ select GENERIC_CLOCKEVENTS
+ select SPARSE_IRQ
+ select ARM_AMBA
+ select ARM_VIC
+ select ARM_TIMER_SP804
+ help
+ This enables support for systems using the TI-NSPIRE CPU
+
endchoice

menu "Multiple platform selection"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index ee4605f..f47a8a7 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -165,6 +165,7 @@ machine-$(CONFIG_ARCH_MXS) += mxs
machine-$(CONFIG_ARCH_MVEBU) += mvebu
machine-$(CONFIG_ARCH_NETX) += netx
machine-$(CONFIG_ARCH_NOMADIK) += nomadik
+machine-$(CONFIG_ARCH_NSPIRE) += nspire
machine-$(CONFIG_ARCH_OMAP1) += omap1
machine-$(CONFIG_ARCH_OMAP2PLUS) += omap2
machine-$(CONFIG_ARCH_ORION5X) += orion5x
@@ -313,7 +314,7 @@ define archhelp
echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'
echo '* xipImage - XIP kernel image, if configured (arch/$(ARCH)/boot/xipImage)'
echo ' uImage - U-Boot wrapped zImage'
- echo ' bootpImage - Combined zImage and initial RAM disk'
+ echo ' bootpImage - Combined zImage and initial RAM disk'
echo ' (supply initrd image via make variable INITRD=<path>)'
echo '* dtbs - Build device tree blobs for enabled boards'
echo ' install - Install uncompressed kernel'
diff --git a/arch/arm/boot/dts/nspire-cx.dts b/arch/arm/boot/dts/nspire-cx.dts
new file mode 100644
index 0000000..6ab3c00
--- /dev/null
+++ b/arch/arm/boot/dts/nspire-cx.dts
@@ -0,0 +1,85 @@
+/*
+ * linux/arch/arm/boot/nspire-cx.dts
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+/dts-v1/;
+
+/include/ "nspire.dtsi"
+
+/ {
+ model = "TI-NSPIRE CX";
+ compatible = "arm,nspire-cx";
+
+ memory {
+ device_type = "memory";
+ reg = <0x10000000 0x4000000>; /* 64 MB */
+ };
+
+ aliases {
+ uart0 = &uart0;
+ timer0 = &timer0;
+ timer1 = &timer1;
+ fast_timer = &fast_timer;
+ };
+
+ uart_clk: uart_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <12000000>;
+ };
+
+ /* Not really a fixed clock but we'll fix this later */
+ apb_pclk: apb_pclk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <33000000>;
+ };
+
+ ahb {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ intc: interrupt-controller@DC000000 {
+ compatible = "arm,pl190-vic";
+ interrupt-controller;
+ reg = <0xDC000000 0x1000>;
+ #interrupt-cells = <1>;
+ };
+
+ apb@90000000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ i2c@90050000 {
+ compatible = "snps,designware-i2c";
+ reg = <0x90050000 0x1000>;
+ interrupts = <20>;
+ };
+
+ fast_timer: timer@90010000 {
+ compatible = "arm,sp804", "arm,primecell";
+ };
+
+ uart0: uart@90020000 {
+ compatible = "arm,pl011", "arm,primecell";
+ };
+
+ timer0: timer0@900C0000 {
+ compatible = "arm,sp804", "arm,primecell";
+ };
+
+ timer1: timer1@900D0000 {
+ compatible = "arm,sp804", "arm,primecell";
+ };
+ };
+ };
+ chosen {
+ bootargs = "debug earlyprintk console=ttyAMA0,115200n8";
+ };
+};
diff --git a/arch/arm/boot/dts/nspire.dtsi b/arch/arm/boot/dts/nspire.dtsi
new file mode 100644
index 0000000..db7584a
--- /dev/null
+++ b/arch/arm/boot/dts/nspire.dtsi
@@ -0,0 +1,154 @@
+/*
+ * linux/arch/arm/boot/nspire.dtsi
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/ "skeleton.dtsi"
+
+/ {
+ cpus {
+ cpu@0 {
+ compatible = "arm,arm926";
+ };
+ };
+
+ bootrom: bootrom@00000000 {
+ reg = <0x00000000 0x80000>;
+ };
+
+ sram: sram@A4000000 {
+ device = "memory";
+ reg = <0xA4000000 0x20000>;
+ };
+
+ timer_clk: timer_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ };
+
+ ahb {
+ compatible = "arm,amba-bus", "simple-bus";
+ interrupt-parent = <&intc>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ spi: spi@A9000000 {
+ reg = <0xA9000000 0x1000>;
+ };
+
+ usb0: usb@B0000000 {
+ reg = <0xB0000000 0x1000>;
+ interrupts = <8>;
+ };
+
+ usb1: usb@B4000000 {
+ reg = <0xB4000000 0x1000>;
+ interrupts = <9>;
+ status = "disabled";
+ };
+
+ lcd: lcd@C0000000 {
+ compatible = "arm,amba-primecell";
+ reg = <0xC0000000 0x1000>;
+ interrupts = <21>;
+ };
+
+ adc: adc@C4000000 {
+ reg = <0xC4000000 0x1000>;
+ interrupts = <11>;
+ };
+
+ tdes: tdes@C8010000 {
+ reg = <0xC8010000 0x1000>;
+ };
+
+ sha256: sha256@CC000000 {
+ reg = <0xCC000000 0x1000>;
+ };
+
+ apb@90000000 {
+ compatible = "arm,amba-bus", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&apb_pclk>;
+ clock-names = "apb_pclk";
+ clock-ranges;
+ ranges;
+
+ gpio: gpio@90000000 {
+ reg = <0x90000000 0x1000>;
+ interrupts = <7>;
+ };
+
+ fast_timer: timer@90010000 {
+ reg = <0x90010000 0x1000>;
+ interrupts = <17>;
+ };
+
+ uart: uart@90020000 {
+ reg = <0x90020000 0x1000>;
+ interrupts = <1>;
+
+ clocks = <&uart_clk>;
+ clock-names = "uart_clk";
+ };
+
+ timer0: timer0@900C0000 {
+ reg = <0x900C0000 0x1000>;
+ interrupts = <18>;
+
+ clocks = <&timer_clk>;
+ clock-names = "timer_clk";
+ };
+
+ timer1: timer1@900D0000 {
+ reg = <0x900D0000 0x1000>;
+ interrupts = <19>;
+
+ clocks = <&timer_clk>;
+ clock-names = "timer_clk";
+ };
+
+ watchdog: watchdog@90060000 {
+ compatible = "arm,amba-primecell";
+ reg = <0x90060000 0x1000>;
+ interrupts = <3>;
+ };
+
+ rtc: rtc@90090000 {
+ reg = <0x90090000 0x1000>;
+ interrupts = <4>;
+ };
+
+ misc: misc@900A0000 {
+ reg = <0x900A0000 0x1000>;
+ };
+
+ pwr: pwr@900B0000 {
+ reg = <0x900B0000 0x1000>;
+ interrupts = <15>;
+ };
+
+ keypad: input@900E0000 {
+ reg = <0x900E0000 0x1000>;
+ interrupts = <16>;
+ };
+
+ contrast: contrast@900F0000 {
+ reg = <0x900F0000 0x1000>;
+ };
+
+ led: led@90110000 {
+ reg = <0x90110000 0x1000>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/mach-nspire/Makefile b/arch/arm/mach-nspire/Makefile
new file mode 100644
index 0000000..62b9baa
--- /dev/null
+++ b/arch/arm/mach-nspire/Makefile
@@ -0,0 +1,3 @@
+obj-y :=
+
+obj-y += nspire.o
diff --git a/arch/arm/mach-nspire/Makefile.boot b/arch/arm/mach-nspire/Makefile.boot
new file mode 100644
index 0000000..e69de29
diff --git a/arch/arm/mach-nspire/include/mach/debug-macro.S b/arch/arm/mach-nspire/include/mach/debug-macro.S
new file mode 100644
index 0000000..d583485
--- /dev/null
+++ b/arch/arm/mach-nspire/include/mach/debug-macro.S
@@ -0,0 +1,25 @@
+/*
+ * linux/arch/arm/mach-nspire/include/mach/debug-macro.S
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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 "../../mmio.h"
+
+.macro addruart, rp, rv, tmp
+ ldr \rp, =(NSPIRE_EARLY_UART_PHYS_BASE) @ physical base address
+ ldr \rv, =(NSPIRE_EARLY_UART_VIRT_BASE) @ virtual base address
+.endm
+
+#include <asm/hardware/debug-pl01x.S>
+
+
+/*
+#define UART_SHIFT 2
+#include <asm/hardware/debug-8250.S>
+*/
diff --git a/arch/arm/mach-nspire/include/mach/timex.h b/arch/arm/mach-nspire/include/mach/timex.h
new file mode 100644
index 0000000..2d4449e
--- /dev/null
+++ b/arch/arm/mach-nspire/include/mach/timex.h
@@ -0,0 +1,15 @@
+/*
+ * linux/arch/arm/mach-nspire/include/mach/timex.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef NSPIRE_TIMEX_H
+#define NSPIRE_TIMEX_H
+
+#endif
diff --git a/arch/arm/mach-nspire/include/mach/uncompress.h b/arch/arm/mach-nspire/include/mach/uncompress.h
new file mode 100644
index 0000000..d957214
--- /dev/null
+++ b/arch/arm/mach-nspire/include/mach/uncompress.h
@@ -0,0 +1,25 @@
+/*
+ * linux/arch/arm/mach-nspire/include/mach/uncompress.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ * Copyright (C) 2013 Lionel Debroux <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef NSPIRE_UNCOMPRESS_H
+#define NSPIRE_UNCOMPRESS_H
+
+#define OFFSET_VAL(var, offset) ((var)[(offset)>>2])
+static inline void putc(int c)
+{
+}
+#undef OFFSET_VAL
+
+#define arch_decomp_setup()
+#define flush()
+
+#endif
diff --git a/arch/arm/mach-nspire/mmio.h b/arch/arm/mach-nspire/mmio.h
new file mode 100644
index 0000000..6e2fe95
--- /dev/null
+++ b/arch/arm/mach-nspire/mmio.h
@@ -0,0 +1,13 @@
+/*
+ * linux/arch/arm/mach-nspire/mmio.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#define NSPIRE_EARLY_UART_PHYS_BASE 0x90020000
+#define NSPIRE_EARLY_UART_VIRT_BASE 0xfee20000
diff --git a/arch/arm/mach-nspire/nspire.c b/arch/arm/mach-nspire/nspire.c
new file mode 100644
index 0000000..ec7734a
--- /dev/null
+++ b/arch/arm/mach-nspire/nspire.c
@@ -0,0 +1,107 @@
+/*
+ * linux/arch/arm/mach-nspire/nspire.c
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/arm-vic.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+#include <asm/mach/map.h>
+
+#include <asm/hardware/timer-sp.h>
+
+#include "mmio.h"
+
+static const char *nspire_dt_match[] __initconst = {
+ "arm,nspire",
+ "arm,nspire-cx",
+ "arm,nspire-tp",
+ "arm,nspire-clp",
+ NULL,
+};
+
+static struct map_desc nspire_io_desc[] __initdata = {
+ {
+ .virtual = NSPIRE_EARLY_UART_VIRT_BASE,
+ .pfn = __phys_to_pfn(NSPIRE_EARLY_UART_PHYS_BASE),
+ .length = SZ_4K,
+ .type = MT_DEVICE
+ }
+};
+
+static void __init nspire_init_timer(void)
+{
+ struct device_node *timer;
+ void __iomem *base;
+ const char *path;
+ struct clk *clk;
+ int irq, err;
+
+ of_clk_init(NULL);
+
+ err = of_property_read_string(of_aliases, "timer0", &path);
+ if (WARN_ON(err))
+ return;
+
+ timer = of_find_node_by_path(path);
+ base = of_iomap(timer, 0);
+ if (WARN_ON(!base))
+ return;
+
+ clk = of_clk_get_by_name(timer, NULL);
+ clk_register_clkdev(clk, timer->name, "sp804");
+
+ sp804_clocksource_init(base, timer->name);
+
+ err = of_property_read_string(of_aliases, "timer1", &path);
+ if (WARN_ON(err))
+ return;
+
+ timer = of_find_node_by_path(path);
+ base = of_iomap(timer, 0);
+ if (WARN_ON(!base))
+ return;
+
+ clk = of_clk_get_by_name(timer, NULL);
+ clk_register_clkdev(clk, timer->name, "sp804");
+
+ irq = irq_of_parse_and_map(timer, 0);
+ sp804_clockevents_init(base, irq, timer->name);
+}
+
+static void __init nspire_map_io(void)
+{
+ iotable_init(nspire_io_desc, ARRAY_SIZE(nspire_io_desc));
+}
+
+static void __init nspire_init(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table,
+ NULL, NULL);
+}
+
+static void nspire_restart(char mode, const char *cmd)
+{
+}
+
+DT_MACHINE_START(NSPIRE, "TI-NSPIRE")
+ .map_io = nspire_map_io,
+ .init_irq = irqchip_init,
+ .init_time = nspire_init_timer,
+ .init_machine = nspire_init,
+ .dt_compat = nspire_dt_match,
+ .restart = nspire_restart,
+MACHINE_END
--
1.7.9.6 (Apple Git-31.1)

2013-04-06 13:25:08

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

On Saturday 06 April 2013, Daniel Tang wrote:
> Hi,
>
>
> On 06/04/2013, at 10:51 PM, Arnd Bergmann <[email protected]> wrote:
> >
> > Ok, whichever way you prefer. If you have questions while working on this,
> > feel free to join #armlinux on irc.freenode.net, there are usually other
> > people working on the same things.
> >
>
> Cheers.
>
> We already have something basic that boots successfully using device trees.
>
> Some comments before we continue would be greatly appreciated.
>
> Signed-off-by: Daniel Tang <[email protected]>

Wow, that was quick!

The patch looks great overall, I just found a few details and have some comments
that you might find helpful.

> arch/arm/Kconfig | 13 ++
> arch/arm/Makefile | 3 +-
> arch/arm/boot/dts/nspire-cx.dts | 85 +++++++++++++
> arch/arm/boot/dts/nspire.dtsi | 154 +++++++++++++++++++++++
> arch/arm/mach-nspire/Makefile | 3 +
> arch/arm/mach-nspire/include/mach/debug-macro.S | 25 ++++
> arch/arm/mach-nspire/include/mach/timex.h | 15 +++
> arch/arm/mach-nspire/include/mach/uncompress.h | 25 ++++
> arch/arm/mach-nspire/mmio.h | 13 ++
> arch/arm/mach-nspire/nspire.c | 107 ++++++++++++++++

A good next step before doing anything else might be to put it under
CONFIG_ARCH_MULTIPLATFORM and remove the include/mach directory.

The only requirement for that should be to move debug-macro.S to
include/debug/nspire.S

> +config ARCH_NSPIRE
> + bool "TI-NSPIRE based"
> + depends on MMU
> + select CPU_ARM926T
> + select COMMON_CLK
> + select GENERIC_CLOCKEVENTS
> + select SPARSE_IRQ
> + select ARM_AMBA
> + select ARM_VIC
> + select ARM_TIMER_SP804
> + help
> + This enables support for systems using the TI-NSPIRE CPU

Since this has all the required changes already.

> @@ -313,7 +314,7 @@ define archhelp
> echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'
> echo '* xipImage - XIP kernel image, if configured (arch/$(ARCH)/boot/xipImage)'
> echo ' uImage - U-Boot wrapped zImage'
> - echo ' bootpImage - Combined zImage and initial RAM disk'
> + echo ' bootpImage - Combined zImage and initial RAM disk'
> echo ' (supply initrd image via make variable INITRD=<path>)'
> echo '* dtbs - Build device tree blobs for enabled boards'
> echo ' install - Install uncompressed kernel'

This looks like it wasn't meant to be in the patch.

> + tdes: tdes@C8010000 {
> + reg = <0xC8010000 0x1000>;
> + };
> +
> + sha256: sha256@CC000000 {
> + reg = <0xCC000000 0x1000>;
> + };

maybe rename the actual nodes to "crypto@c...". The device name should
be a really generic word in general.

> + uart: uart@90020000 {
> + reg = <0x90020000 0x1000>;
> + interrupts = <1>;
> +
> + clocks = <&uart_clk>;
> + clock-names = "uart_clk";
> + };

The name for a uart should be "serial". Since this is a pl01x, please add
the required properties for the device, e.g.

compatible = "arm,pl011", "arm,primecell";

You will need the "arm,primecell" bit to make the device appear on the
amba bus rather than the platform bus.

> + timer0: timer0@900C0000 {
> + reg = <0x900C0000 0x1000>;
> + interrupts = <18>;
> +
> + clocks = <&timer_clk>;
> + clock-names = "timer_clk";
> + };
> +
> + timer1: timer1@900D0000 {
> + reg = <0x900D0000 0x1000>;
> + interrupts = <19>;
> +
> + clocks = <&timer_clk>;
> + clock-names = "timer_clk";
> + };

Name the devices "timer", not "timer0" and "timer1", the address after @ is
used to disambiguate them. There are currently patches for sp804 under
discussion on the mailing list, you should probably watch those.

> --- /dev/null
> +++ b/arch/arm/mach-nspire/Makefile
> @@ -0,0 +1,3 @@
> +obj-y :=
> +
> +obj-y += nspire.o

The first line is not actually needed.

> +
> +#include "../../mmio.h"
> +
> +.macro addruart, rp, rv, tmp
> + ldr \rp, =(NSPIRE_EARLY_UART_PHYS_BASE) @ physical base address
> + ldr \rv, =(NSPIRE_EARLY_UART_VIRT_BASE) @ virtual base address
> +.endm
> +
> +#include <asm/hardware/debug-pl01x.S>
> +

There is no nice solution for getting the addresses here, but the consensus
was to just define the macros in this file rather than try to include a
header from elsewhere.


> + err = of_property_read_string(of_aliases, "timer0", &path);
> + if (WARN_ON(err))
> + return;
> +
> + timer = of_find_node_by_path(path);
> + base = of_iomap(timer, 0);
> + if (WARN_ON(!base))
> + return;
> +
> + clk = of_clk_get_by_name(timer, NULL);
> + clk_register_clkdev(clk, timer->name, "sp804");
> +
> + sp804_clocksource_init(base, timer->name);
> +
> + err = of_property_read_string(of_aliases, "timer1", &path);
> + if (WARN_ON(err))
> + return;

In particular, I think the method of using aliases to pick the right sp804
instance is being deprecated now. If both timers are identical, the kernel
will now just pick one of them.

Arnd

2013-04-07 00:06:35

by Daniel Tang

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

Hi,

On 07/04/2013, at 12:24 AM, Arnd Bergmann <[email protected]> wrote:

>> arch/arm/Kconfig | 13 ++
>> arch/arm/Makefile | 3 +-
>> arch/arm/boot/dts/nspire-cx.dts | 85 +++++++++++++
>> arch/arm/boot/dts/nspire.dtsi | 154 +++++++++++++++++++++++
>> arch/arm/mach-nspire/Makefile | 3 +
>> arch/arm/mach-nspire/include/mach/debug-macro.S | 25 ++++
>> arch/arm/mach-nspire/include/mach/timex.h | 15 +++
>> arch/arm/mach-nspire/include/mach/uncompress.h | 25 ++++
>> arch/arm/mach-nspire/mmio.h | 13 ++
>> arch/arm/mach-nspire/nspire.c | 107 ++++++++++++++++
>
> A good next step before doing anything else might be to put it under
> CONFIG_ARCH_MULTIPLATFORM and remove the include/mach directory.
>
> The only requirement for that should be to move debug-macro.S to
> include/debug/nspire.S
>

Done.
>
>
>> @@ -313,7 +314,7 @@ define archhelp
>> echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'
>> echo '* xipImage - XIP kernel image, if configured (arch/$(ARCH)/boot/xipImage)'
>> echo ' uImage - U-Boot wrapped zImage'
>> - echo ' bootpImage - Combined zImage and initial RAM disk'
>> + echo ' bootpImage - Combined zImage and initial RAM disk'
>> echo ' (supply initrd image via make variable INITRD=<path>)'
>> echo '* dtbs - Build device tree blobs for enabled boards'
>> echo ' install - Install uncompressed kernel'
>
> This looks like it wasn't meant to be in the patch.

It probably isn't. I think there was trailing whitespace on that and my editor happened to remove it automatically.

Should this be a separate patch to fix up formatting or should I leave it in as a drive-by fix?

>
>> + tdes: tdes@C8010000 {
>> + reg = <0xC8010000 0x1000>;
>> + };
>> +
>> + sha256: sha256@CC000000 {
>> + reg = <0xCC000000 0x1000>;
>> + };
>
> maybe rename the actual nodes to "crypto@c...". The device name should
> be a really generic word in general.

Done.

>
>> + uart: uart@90020000 {
>> + reg = <0x90020000 0x1000>;
>> + interrupts = <1>;
>> +
>> + clocks = <&uart_clk>;
>> + clock-names = "uart_clk";
>> + };
>
> The name for a uart should be "serial". Since this is a pl01x, please add
> the required properties for the device, e.g.
>
> compatible = "arm,pl011", "arm,primecell";
>
> You will need the "arm,primecell" bit to make the device appear on the
> amba bus rather than the platform bus.

That was actually deliberate because different models of the TI-NSPIRE have different serial hardware. On the newer CX models, it is a PL01x and on the older models, it has a 8250-like interface. They all reside at the same address with the same IRQ though.

I thought it might be cleaner to specify the interrupts and registers in the common file and leave it to the board specific ones to implement the "compatible" property.

>
>> + timer0: timer0@900C0000 {
>> + reg = <0x900C0000 0x1000>;
>> + interrupts = <18>;
>> +
>> + clocks = <&timer_clk>;
>> + clock-names = "timer_clk";
>> + };
>> +
>> + timer1: timer1@900D0000 {
>> + reg = <0x900D0000 0x1000>;
>> + interrupts = <19>;
>> +
>> + clocks = <&timer_clk>;
>> + clock-names = "timer_clk";
>> + };
>
> Name the devices "timer", not "timer0" and "timer1", the address after @ is
> used to disambiguate them. There are currently patches for sp804 under
> discussion on the mailing list, you should probably watch those.
>

Done. Yep, I also noticed there were patches to have device tree bindings for SP804. I'll integrate them once they're in mainline.

>> --- /dev/null
>> +++ b/arch/arm/mach-nspire/Makefile
>> @@ -0,0 +1,3 @@
>> +obj-y :=
>> +
>> +obj-y += nspire.o
>
> The first line is not actually needed.

Done.

>
>> +
>> +#include "../../mmio.h"
>> +
>> +.macro addruart, rp, rv, tmp
>> + ldr \rp, =(NSPIRE_EARLY_UART_PHYS_BASE) @ physical base address
>> + ldr \rv, =(NSPIRE_EARLY_UART_VIRT_BASE) @ virtual base address
>> +.endm
>> +
>> +#include <asm/hardware/debug-pl01x.S>
>> +
>
> There is no nice solution for getting the addresses here, but the consensus
> was to just define the macros in this file rather than try to include a
> header from elsewhere.
>

Fair enough, I've added the macros in.

>
>> + err = of_property_read_string(of_aliases, "timer0", &path);
>> + if (WARN_ON(err))
>> + return;
>> +
>> + timer = of_find_node_by_path(path);
>> + base = of_iomap(timer, 0);
>> + if (WARN_ON(!base))
>> + return;
>> +
>> + clk = of_clk_get_by_name(timer, NULL);
>> + clk_register_clkdev(clk, timer->name, "sp804");
>> +
>> + sp804_clocksource_init(base, timer->name);
>> +
>> + err = of_property_read_string(of_aliases, "timer1", &path);
>> + if (WARN_ON(err))
>> + return;
>
> In particular, I think the method of using aliases to pick the right sp804
> instance is being deprecated now. If both timers are identical, the kernel
> will now just pick one of them.

Sorry, I don't quite understand.

Out of the timers, I want to add one as a clocksource and one as a clockevent. If they're identical (i.e. without using aliases), how should I tell the kernel, "Take the first timer you see and make it a clocksource, take the next one you see and make it a clockevent"?

>
> Arnd

Here's an updated patch:

Signed-off-by: Daniel Tang <[email protected]>
---
arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 16 ++++
arch/arm/Makefile | 3 +-
arch/arm/boot/dts/nspire-cx.dts | 85 ++++++++++++++++++++
arch/arm/boot/dts/nspire.dtsi | 154 +++++++++++++++++++++++++++++++++++++
arch/arm/include/debug/nspire.S | 28 +++++++
arch/arm/mach-nspire/Kconfig | 12 +++
arch/arm/mach-nspire/Makefile | 1 +
arch/arm/mach-nspire/Makefile.boot | 0
arch/arm/mach-nspire/mmio.h | 13 ++++
arch/arm/mach-nspire/nspire.c | 107 ++++++++++++++++++++++++++
11 files changed, 420 insertions(+), 1 deletion(-)
create mode 100644 arch/arm/boot/dts/nspire-cx.dts
create mode 100644 arch/arm/boot/dts/nspire.dtsi
create mode 100644 arch/arm/include/debug/nspire.S
create mode 100644 arch/arm/mach-nspire/Kconfig
create mode 100644 arch/arm/mach-nspire/Makefile
create mode 100644 arch/arm/mach-nspire/Makefile.boot
create mode 100644 arch/arm/mach-nspire/mmio.h
create mode 100644 arch/arm/mach-nspire/nspire.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 1cacda4..3f0cd8c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1081,6 +1081,8 @@ source "arch/arm/mach-netx/Kconfig"

source "arch/arm/mach-nomadik/Kconfig"

+source "arch/arm/mach-nspire/Kconfig"
+
source "arch/arm/plat-omap/Kconfig"

source "arch/arm/mach-omap1/Kconfig"
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 9b31f43..5da3a50 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -298,6 +298,20 @@ choice
Say Y here if you want kernel low-level debugging support
on MVEBU based platforms.

+ config DEBUG_NSPIRE_CLASSIC_UART
+ bool "Kernel low-level debugging via TI-NSPIRE 8250 UART"
+ depends on ARCH_NSPIRE
+ help
+ Say Y here if you want kernel low-level debugging support
+ on TI-NSPIRE classic models.
+
+ config DEBUG_NSPIRE_CX_UART
+ bool "Kernel low-level debugging via TI-NSPIRE PL011 UART"
+ depends on ARCH_NSPIRE
+ help
+ Say Y here if you want kernel low-level debugging support
+ on TI-NSPIRE CX models.
+
config DEBUG_OMAP2PLUS_UART
bool "Kernel low-level debugging messages via OMAP2PLUS UART"
depends on ARCH_OMAP2PLUS
@@ -591,6 +605,8 @@ config DEBUG_LL_INCLUDE
DEBUG_IMX6Q_UART
default "debug/highbank.S" if DEBUG_HIGHBANK_UART
default "debug/mvebu.S" if DEBUG_MVEBU_UART
+ default "debug/nspire.S" if DEBUG_NSPIRE_CX_UART || \
+ DEBUG_NSPIRE_CLASSIC_UART
default "debug/omap2plus.S" if DEBUG_OMAP2PLUS_UART
default "debug/picoxcell.S" if DEBUG_PICOXCELL_UART
default "debug/socfpga.S" if DEBUG_SOCFPGA_UART
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index ee4605f..f47a8a7 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -165,6 +165,7 @@ machine-$(CONFIG_ARCH_MXS) += mxs
machine-$(CONFIG_ARCH_MVEBU) += mvebu
machine-$(CONFIG_ARCH_NETX) += netx
machine-$(CONFIG_ARCH_NOMADIK) += nomadik
+machine-$(CONFIG_ARCH_NSPIRE) += nspire
machine-$(CONFIG_ARCH_OMAP1) += omap1
machine-$(CONFIG_ARCH_OMAP2PLUS) += omap2
machine-$(CONFIG_ARCH_ORION5X) += orion5x
@@ -313,7 +314,7 @@ define archhelp
echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'
echo '* xipImage - XIP kernel image, if configured (arch/$(ARCH)/boot/xipImage)'
echo ' uImage - U-Boot wrapped zImage'
- echo ' bootpImage - Combined zImage and initial RAM disk'
+ echo ' bootpImage - Combined zImage and initial RAM disk'
echo ' (supply initrd image via make variable INITRD=<path>)'
echo '* dtbs - Build device tree blobs for enabled boards'
echo ' install - Install uncompressed kernel'
diff --git a/arch/arm/boot/dts/nspire-cx.dts b/arch/arm/boot/dts/nspire-cx.dts
new file mode 100644
index 0000000..0a80488
--- /dev/null
+++ b/arch/arm/boot/dts/nspire-cx.dts
@@ -0,0 +1,85 @@
+/*
+ * linux/arch/arm/boot/nspire-cx.dts
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+/dts-v1/;
+
+/include/ "nspire.dtsi"
+
+/ {
+ model = "TI-NSPIRE CX";
+ compatible = "arm,nspire-cx";
+
+ memory {
+ device_type = "memory";
+ reg = <0x10000000 0x4000000>; /* 64 MB */
+ };
+
+ aliases {
+ uart0 = &uart0;
+ timer0 = &timer0;
+ timer1 = &timer1;
+ fast_timer = &fast_timer;
+ };
+
+ uart_clk: uart_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <12000000>;
+ };
+
+ /* Not really a fixed clock but we'll fix this later */
+ apb_pclk: apb_pclk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <33000000>;
+ };
+
+ ahb {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ intc: interrupt-controller@DC000000 {
+ compatible = "arm,pl190-vic";
+ interrupt-controller;
+ reg = <0xDC000000 0x1000>;
+ #interrupt-cells = <1>;
+ };
+
+ apb@90000000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ i2c@90050000 {
+ compatible = "snps,designware-i2c";
+ reg = <0x90050000 0x1000>;
+ interrupts = <20>;
+ };
+
+ fast_timer: timer@90010000 {
+ compatible = "arm,sp804", "arm,primecell";
+ };
+
+ uart0: serial@90020000 {
+ compatible = "arm,pl011", "arm,primecell";
+ };
+
+ timer0: timer@900C0000 {
+ compatible = "arm,sp804", "arm,primecell";
+ };
+
+ timer1: timer@900D0000 {
+ compatible = "arm,sp804", "arm,primecell";
+ };
+ };
+ };
+ chosen {
+ bootargs = "debug earlyprintk console=ttyAMA0,115200n8";
+ };
+};
diff --git a/arch/arm/boot/dts/nspire.dtsi b/arch/arm/boot/dts/nspire.dtsi
new file mode 100644
index 0000000..7e9681f
--- /dev/null
+++ b/arch/arm/boot/dts/nspire.dtsi
@@ -0,0 +1,154 @@
+/*
+ * linux/arch/arm/boot/nspire.dtsi
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/ "skeleton.dtsi"
+
+/ {
+ cpus {
+ cpu@0 {
+ compatible = "arm,arm926";
+ };
+ };
+
+ bootrom: bootrom@00000000 {
+ reg = <0x00000000 0x80000>;
+ };
+
+ sram: sram@A4000000 {
+ device = "memory";
+ reg = <0xA4000000 0x20000>;
+ };
+
+ timer_clk: timer_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ };
+
+ ahb {
+ compatible = "arm,amba-bus", "simple-bus";
+ interrupt-parent = <&intc>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ spi: spi@A9000000 {
+ reg = <0xA9000000 0x1000>;
+ };
+
+ usb0: usb@B0000000 {
+ reg = <0xB0000000 0x1000>;
+ interrupts = <8>;
+ };
+
+ usb1: usb@B4000000 {
+ reg = <0xB4000000 0x1000>;
+ interrupts = <9>;
+ status = "disabled";
+ };
+
+ lcd: lcd@C0000000 {
+ compatible = "arm,amba-primecell";
+ reg = <0xC0000000 0x1000>;
+ interrupts = <21>;
+ };
+
+ adc: adc@C4000000 {
+ reg = <0xC4000000 0x1000>;
+ interrupts = <11>;
+ };
+
+ tdes: crypto@C8010000 {
+ reg = <0xC8010000 0x1000>;
+ };
+
+ sha256: crypto@CC000000 {
+ reg = <0xCC000000 0x1000>;
+ };
+
+ apb@90000000 {
+ compatible = "arm,amba-bus", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&apb_pclk>;
+ clock-names = "apb_pclk";
+ clock-ranges;
+ ranges;
+
+ gpio: gpio@90000000 {
+ reg = <0x90000000 0x1000>;
+ interrupts = <7>;
+ };
+
+ fast_timer: timer@90010000 {
+ reg = <0x90010000 0x1000>;
+ interrupts = <17>;
+ };
+
+ uart: serial@90020000 {
+ reg = <0x90020000 0x1000>;
+ interrupts = <1>;
+
+ clocks = <&uart_clk>;
+ clock-names = "uart_clk";
+ };
+
+ timer0: timer@900C0000 {
+ reg = <0x900C0000 0x1000>;
+ interrupts = <18>;
+
+ clocks = <&timer_clk>;
+ clock-names = "timer_clk";
+ };
+
+ timer1: timer@900D0000 {
+ reg = <0x900D0000 0x1000>;
+ interrupts = <19>;
+
+ clocks = <&timer_clk>;
+ clock-names = "timer_clk";
+ };
+
+ watchdog: watchdog@90060000 {
+ compatible = "arm,amba-primecell";
+ reg = <0x90060000 0x1000>;
+ interrupts = <3>;
+ };
+
+ rtc: rtc@90090000 {
+ reg = <0x90090000 0x1000>;
+ interrupts = <4>;
+ };
+
+ misc: misc@900A0000 {
+ reg = <0x900A0000 0x1000>;
+ };
+
+ pwr: pwr@900B0000 {
+ reg = <0x900B0000 0x1000>;
+ interrupts = <15>;
+ };
+
+ keypad: input@900E0000 {
+ reg = <0x900E0000 0x1000>;
+ interrupts = <16>;
+ };
+
+ contrast: contrast@900F0000 {
+ reg = <0x900F0000 0x1000>;
+ };
+
+ led: led@90110000 {
+ reg = <0x90110000 0x1000>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/include/debug/nspire.S b/arch/arm/include/debug/nspire.S
new file mode 100644
index 0000000..3a9729c
--- /dev/null
+++ b/arch/arm/include/debug/nspire.S
@@ -0,0 +1,28 @@
+/*
+ * linux/arch/arm/include/debug/nspire.S
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#define NSPIRE_EARLY_UART_PHYS_BASE 0x90020000
+#define NSPIRE_EARLY_UART_VIRT_BASE 0xfee20000
+
+.macro addruart, rp, rv, tmp
+ ldr \rp, =(NSPIRE_EARLY_UART_PHYS_BASE) @ physical base address
+ ldr \rv, =(NSPIRE_EARLY_UART_VIRT_BASE) @ virtual base address
+.endm
+
+
+#ifdef CONFIG_DEBUG_NSPIRE_CX_UART
+#include <asm/hardware/debug-pl01x.S>
+#endif
+
+#ifdef CONFIG_DEBUG_NSPIRE_CLASSIC_UART
+#define UART_SHIFT 2
+#include <asm/hardware/debug-8250.S>
+#endif
diff --git a/arch/arm/mach-nspire/Kconfig b/arch/arm/mach-nspire/Kconfig
new file mode 100644
index 0000000..b6bf06c
--- /dev/null
+++ b/arch/arm/mach-nspire/Kconfig
@@ -0,0 +1,12 @@
+config ARCH_NSPIRE
+ bool "TI-NSPIRE based"
+ depends on MMU
+ select CPU_ARM926T
+ select COMMON_CLK
+ select GENERIC_CLOCKEVENTS
+ select SPARSE_IRQ
+ select ARM_AMBA
+ select ARM_VIC
+ select ARM_TIMER_SP804
+ help
+ This enables support for systems using the TI-NSPIRE CPU
diff --git a/arch/arm/mach-nspire/Makefile b/arch/arm/mach-nspire/Makefile
new file mode 100644
index 0000000..c96c2c4
--- /dev/null
+++ b/arch/arm/mach-nspire/Makefile
@@ -0,0 +1 @@
+obj-y += nspire.o
diff --git a/arch/arm/mach-nspire/Makefile.boot b/arch/arm/mach-nspire/Makefile.boot
new file mode 100644
index 0000000..e69de29
diff --git a/arch/arm/mach-nspire/mmio.h b/arch/arm/mach-nspire/mmio.h
new file mode 100644
index 0000000..6e2fe95
--- /dev/null
+++ b/arch/arm/mach-nspire/mmio.h
@@ -0,0 +1,13 @@
+/*
+ * linux/arch/arm/mach-nspire/mmio.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#define NSPIRE_EARLY_UART_PHYS_BASE 0x90020000
+#define NSPIRE_EARLY_UART_VIRT_BASE 0xfee20000
diff --git a/arch/arm/mach-nspire/nspire.c b/arch/arm/mach-nspire/nspire.c
new file mode 100644
index 0000000..ec7734a
--- /dev/null
+++ b/arch/arm/mach-nspire/nspire.c
@@ -0,0 +1,107 @@
+/*
+ * linux/arch/arm/mach-nspire/nspire.c
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/arm-vic.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+#include <asm/mach/map.h>
+
+#include <asm/hardware/timer-sp.h>
+
+#include "mmio.h"
+
+static const char *nspire_dt_match[] __initconst = {
+ "arm,nspire",
+ "arm,nspire-cx",
+ "arm,nspire-tp",
+ "arm,nspire-clp",
+ NULL,
+};
+
+static struct map_desc nspire_io_desc[] __initdata = {
+ {
+ .virtual = NSPIRE_EARLY_UART_VIRT_BASE,
+ .pfn = __phys_to_pfn(NSPIRE_EARLY_UART_PHYS_BASE),
+ .length = SZ_4K,
+ .type = MT_DEVICE
+ }
+};
+
+static void __init nspire_init_timer(void)
+{
+ struct device_node *timer;
+ void __iomem *base;
+ const char *path;
+ struct clk *clk;
+ int irq, err;
+
+ of_clk_init(NULL);
+
+ err = of_property_read_string(of_aliases, "timer0", &path);
+ if (WARN_ON(err))
+ return;
+
+ timer = of_find_node_by_path(path);
+ base = of_iomap(timer, 0);
+ if (WARN_ON(!base))
+ return;
+
+ clk = of_clk_get_by_name(timer, NULL);
+ clk_register_clkdev(clk, timer->name, "sp804");
+
+ sp804_clocksource_init(base, timer->name);
+
+ err = of_property_read_string(of_aliases, "timer1", &path);
+ if (WARN_ON(err))
+ return;
+
+ timer = of_find_node_by_path(path);
+ base = of_iomap(timer, 0);
+ if (WARN_ON(!base))
+ return;
+
+ clk = of_clk_get_by_name(timer, NULL);
+ clk_register_clkdev(clk, timer->name, "sp804");
+
+ irq = irq_of_parse_and_map(timer, 0);
+ sp804_clockevents_init(base, irq, timer->name);
+}
+
+static void __init nspire_map_io(void)
+{
+ iotable_init(nspire_io_desc, ARRAY_SIZE(nspire_io_desc));
+}
+
+static void __init nspire_init(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table,
+ NULL, NULL);
+}
+
+static void nspire_restart(char mode, const char *cmd)
+{
+}
+
+DT_MACHINE_START(NSPIRE, "TI-NSPIRE")
+ .map_io = nspire_map_io,
+ .init_irq = irqchip_init,
+ .init_time = nspire_init_timer,
+ .init_machine = nspire_init,
+ .dt_compat = nspire_dt_match,
+ .restart = nspire_restart,
+MACHINE_END
--
1.8.1.3

2013-04-07 03:56:24

by Daniel Tang

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

Here's an updated patch that enables support for the LCD.

I looked into drivers/video/of_display_timing.c but it doesn't have the fields to describe the PL11x specific options needed in struct clcd_panel. At the moment, it is implemented by hardcoding the values in the kernel and using the device tree to select the correct configuration.

Signed-off-by: Daniel Tang <[email protected]>
---
arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 16 +++
arch/arm/Makefile | 3 +-
arch/arm/boot/dts/nspire-cx.dts | 89 ++++++++++++++
arch/arm/boot/dts/nspire.dtsi | 157 +++++++++++++++++++++++++
arch/arm/include/debug/nspire.S | 28 +++++
arch/arm/mach-nspire/Kconfig | 12 ++
arch/arm/mach-nspire/Makefile | 1 +
arch/arm/mach-nspire/Makefile.boot | 0
arch/arm/mach-nspire/mmio.h | 15 +++
arch/arm/mach-nspire/nspire.c | 233 +++++++++++++++++++++++++++++++++++++
11 files changed, 555 insertions(+), 1 deletion(-)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 1cacda4..3f0cd8c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1081,6 +1081,8 @@ source "arch/arm/mach-netx/Kconfig"

source "arch/arm/mach-nomadik/Kconfig"

+source "arch/arm/mach-nspire/Kconfig"
+
source "arch/arm/plat-omap/Kconfig"

source "arch/arm/mach-omap1/Kconfig"
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 9b31f43..5da3a50 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -298,6 +298,20 @@ choice
Say Y here if you want kernel low-level debugging support
on MVEBU based platforms.

+ config DEBUG_NSPIRE_CLASSIC_UART
+ bool "Kernel low-level debugging via TI-NSPIRE 8250 UART"
+ depends on ARCH_NSPIRE
+ help
+ Say Y here if you want kernel low-level debugging support
+ on TI-NSPIRE classic models.
+
+ config DEBUG_NSPIRE_CX_UART
+ bool "Kernel low-level debugging via TI-NSPIRE PL011 UART"
+ depends on ARCH_NSPIRE
+ help
+ Say Y here if you want kernel low-level debugging support
+ on TI-NSPIRE CX models.
+
config DEBUG_OMAP2PLUS_UART
bool "Kernel low-level debugging messages via OMAP2PLUS UART"
depends on ARCH_OMAP2PLUS
@@ -591,6 +605,8 @@ config DEBUG_LL_INCLUDE
DEBUG_IMX6Q_UART
default "debug/highbank.S" if DEBUG_HIGHBANK_UART
default "debug/mvebu.S" if DEBUG_MVEBU_UART
+ default "debug/nspire.S" if DEBUG_NSPIRE_CX_UART || \
+ DEBUG_NSPIRE_CLASSIC_UART
default "debug/omap2plus.S" if DEBUG_OMAP2PLUS_UART
default "debug/picoxcell.S" if DEBUG_PICOXCELL_UART
default "debug/socfpga.S" if DEBUG_SOCFPGA_UART
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index ee4605f..f47a8a7 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -165,6 +165,7 @@ machine-$(CONFIG_ARCH_MXS) += mxs
machine-$(CONFIG_ARCH_MVEBU) += mvebu
machine-$(CONFIG_ARCH_NETX) += netx
machine-$(CONFIG_ARCH_NOMADIK) += nomadik
+machine-$(CONFIG_ARCH_NSPIRE) += nspire
machine-$(CONFIG_ARCH_OMAP1) += omap1
machine-$(CONFIG_ARCH_OMAP2PLUS) += omap2
machine-$(CONFIG_ARCH_ORION5X) += orion5x
@@ -313,7 +314,7 @@ define archhelp
echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'
echo '* xipImage - XIP kernel image, if configured (arch/$(ARCH)/boot/xipImage)'
echo ' uImage - U-Boot wrapped zImage'
- echo ' bootpImage - Combined zImage and initial RAM disk'
+ echo ' bootpImage - Combined zImage and initial RAM disk'
echo ' (supply initrd image via make variable INITRD=<path>)'
echo '* dtbs - Build device tree blobs for enabled boards'
echo ' install - Install uncompressed kernel'
diff --git a/arch/arm/boot/dts/nspire-cx.dts b/arch/arm/boot/dts/nspire-cx.dts
new file mode 100644
index 0000000..d843e60
--- /dev/null
+++ b/arch/arm/boot/dts/nspire-cx.dts
@@ -0,0 +1,89 @@
+/*
+ * linux/arch/arm/boot/nspire-cx.dts
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+/dts-v1/;
+
+/include/ "nspire.dtsi"
+
+/ {
+ model = "TI-NSPIRE CX";
+ compatible = "arm,nspire-cx";
+
+ memory {
+ device_type = "memory";
+ reg = <0x10000000 0x4000000>; /* 64 MB */
+ };
+
+ aliases {
+ uart0 = &uart0;
+ timer0 = &timer0;
+ timer1 = &timer1;
+ fast_timer = &fast_timer;
+ };
+
+ uart_clk: uart_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <12000000>;
+ };
+
+ /* Not really a fixed clock but we'll fix this later */
+ apb_pclk: apb_pclk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <33000000>;
+ };
+
+ ahb {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ intc: interrupt-controller@DC000000 {
+ compatible = "arm,pl190-vic";
+ interrupt-controller;
+ reg = <0xDC000000 0x1000>;
+ #interrupt-cells = <1>;
+ };
+
+ lcd: lcd@C0000000 {
+ lcd-type = "cx";
+ };
+
+ apb@90000000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ i2c@90050000 {
+ compatible = "snps,designware-i2c";
+ reg = <0x90050000 0x1000>;
+ interrupts = <20>;
+ };
+
+ fast_timer: timer@90010000 {
+ compatible = "arm,sp804", "arm,primecell";
+ };
+
+ uart0: serial@90020000 {
+ compatible = "arm,pl011", "arm,primecell";
+ };
+
+ timer0: timer@900C0000 {
+ compatible = "arm,sp804", "arm,primecell";
+ };
+
+ timer1: timer@900D0000 {
+ compatible = "arm,sp804", "arm,primecell";
+ };
+ };
+ };
+ chosen {
+ bootargs = "debug earlyprintk console=ttyAMA0,115200n8 console=tty0";
+ };
+};
diff --git a/arch/arm/boot/dts/nspire.dtsi b/arch/arm/boot/dts/nspire.dtsi
new file mode 100644
index 0000000..5b6fad7
--- /dev/null
+++ b/arch/arm/boot/dts/nspire.dtsi
@@ -0,0 +1,157 @@
+/*
+ * linux/arch/arm/boot/nspire.dtsi
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/ "skeleton.dtsi"
+
+/ {
+ cpus {
+ cpu@0 {
+ compatible = "arm,arm926";
+ };
+ };
+
+ bootrom: bootrom@00000000 {
+ reg = <0x00000000 0x80000>;
+ };
+
+ sram: sram@A4000000 {
+ device = "memory";
+ reg = <0xA4000000 0x20000>;
+ };
+
+ timer_clk: timer_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ };
+
+ ahb {
+ compatible = "arm,amba-bus", "simple-bus";
+ interrupt-parent = <&intc>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ spi: spi@A9000000 {
+ reg = <0xA9000000 0x1000>;
+ };
+
+ usb0: usb@B0000000 {
+ reg = <0xB0000000 0x1000>;
+ interrupts = <8>;
+ };
+
+ usb1: usb@B4000000 {
+ reg = <0xB4000000 0x1000>;
+ interrupts = <9>;
+ status = "disabled";
+ };
+
+ lcd: lcd@C0000000 {
+ compatible = "arm,pl111", "arm,primecell";
+ reg = <0xC0000000 0x1000>;
+ interrupts = <21>;
+
+ clocks = <&apb_pclk>;
+ clock-names = "apb_pclk";
+ };
+
+ adc: adc@C4000000 {
+ reg = <0xC4000000 0x1000>;
+ interrupts = <11>;
+ };
+
+ tdes: crypto@C8010000 {
+ reg = <0xC8010000 0x1000>;
+ };
+
+ sha256: crypto@CC000000 {
+ reg = <0xCC000000 0x1000>;
+ };
+
+ apb@90000000 {
+ compatible = "arm,amba-bus", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clocks = <&apb_pclk>;
+ clock-names = "apb_pclk";
+ clock-ranges;
+ ranges;
+
+ gpio: gpio@90000000 {
+ reg = <0x90000000 0x1000>;
+ interrupts = <7>;
+ };
+
+ fast_timer: timer@90010000 {
+ reg = <0x90010000 0x1000>;
+ interrupts = <17>;
+ };
+
+ uart: serial@90020000 {
+ reg = <0x90020000 0x1000>;
+ interrupts = <1>;
+
+ clocks = <&uart_clk>;
+ clock-names = "uart_clk";
+ };
+
+ timer0: timer@900C0000 {
+ reg = <0x900C0000 0x1000>;
+ interrupts = <18>;
+
+ clocks = <&timer_clk>;
+ clock-names = "timer_clk";
+ };
+
+ timer1: timer@900D0000 {
+ reg = <0x900D0000 0x1000>;
+ interrupts = <19>;
+
+ clocks = <&timer_clk>;
+ clock-names = "timer_clk";
+ };
+
+ watchdog: watchdog@90060000 {
+ compatible = "arm,amba-primecell";
+ reg = <0x90060000 0x1000>;
+ interrupts = <3>;
+ };
+
+ rtc: rtc@90090000 {
+ reg = <0x90090000 0x1000>;
+ interrupts = <4>;
+ };
+
+ misc: misc@900A0000 {
+ reg = <0x900A0000 0x1000>;
+ };
+
+ pwr: pwr@900B0000 {
+ reg = <0x900B0000 0x1000>;
+ interrupts = <15>;
+ };
+
+ keypad: input@900E0000 {
+ reg = <0x900E0000 0x1000>;
+ interrupts = <16>;
+ };
+
+ contrast: contrast@900F0000 {
+ reg = <0x900F0000 0x1000>;
+ };
+
+ led: led@90110000 {
+ reg = <0x90110000 0x1000>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/include/debug/nspire.S b/arch/arm/include/debug/nspire.S
new file mode 100644
index 0000000..3a9729c
--- /dev/null
+++ b/arch/arm/include/debug/nspire.S
@@ -0,0 +1,28 @@
+/*
+ * linux/arch/arm/include/debug/nspire.S
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#define NSPIRE_EARLY_UART_PHYS_BASE 0x90020000
+#define NSPIRE_EARLY_UART_VIRT_BASE 0xfee20000
+
+.macro addruart, rp, rv, tmp
+ ldr \rp, =(NSPIRE_EARLY_UART_PHYS_BASE) @ physical base address
+ ldr \rv, =(NSPIRE_EARLY_UART_VIRT_BASE) @ virtual base address
+.endm
+
+
+#ifdef CONFIG_DEBUG_NSPIRE_CX_UART
+#include <asm/hardware/debug-pl01x.S>
+#endif
+
+#ifdef CONFIG_DEBUG_NSPIRE_CLASSIC_UART
+#define UART_SHIFT 2
+#include <asm/hardware/debug-8250.S>
+#endif
diff --git a/arch/arm/mach-nspire/Kconfig b/arch/arm/mach-nspire/Kconfig
new file mode 100644
index 0000000..b6bf06c
--- /dev/null
+++ b/arch/arm/mach-nspire/Kconfig
@@ -0,0 +1,12 @@
+config ARCH_NSPIRE
+ bool "TI-NSPIRE based"
+ depends on MMU
+ select CPU_ARM926T
+ select COMMON_CLK
+ select GENERIC_CLOCKEVENTS
+ select SPARSE_IRQ
+ select ARM_AMBA
+ select ARM_VIC
+ select ARM_TIMER_SP804
+ help
+ This enables support for systems using the TI-NSPIRE CPU
diff --git a/arch/arm/mach-nspire/Makefile b/arch/arm/mach-nspire/Makefile
new file mode 100644
index 0000000..c96c2c4
--- /dev/null
+++ b/arch/arm/mach-nspire/Makefile
@@ -0,0 +1 @@
+obj-y += nspire.o
diff --git a/arch/arm/mach-nspire/Makefile.boot b/arch/arm/mach-nspire/Makefile.boot
new file mode 100644
index 0000000..e69de29
diff --git a/arch/arm/mach-nspire/mmio.h b/arch/arm/mach-nspire/mmio.h
new file mode 100644
index 0000000..f0a1fa5
--- /dev/null
+++ b/arch/arm/mach-nspire/mmio.h
@@ -0,0 +1,15 @@
+/*
+ * linux/arch/arm/mach-nspire/mmio.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#define NSPIRE_EARLY_UART_PHYS_BASE 0x90020000
+#define NSPIRE_EARLY_UART_VIRT_BASE 0xfee20000
+
+#define NSPIRE_LCD_PHYS_BASE 0xC0000000
diff --git a/arch/arm/mach-nspire/nspire.c b/arch/arm/mach-nspire/nspire.c
new file mode 100644
index 0000000..e349c80
--- /dev/null
+++ b/arch/arm/mach-nspire/nspire.c
@@ -0,0 +1,233 @@
+/*
+ * linux/arch/arm/mach-nspire/nspire.c
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/arm-vic.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+#include <asm/mach/map.h>
+
+#include <asm/hardware/timer-sp.h>
+
+#include "mmio.h"
+
+static const char *nspire_dt_match[] __initconst = {
+ "arm,nspire",
+ "arm,nspire-cx",
+ "arm,nspire-tp",
+ "arm,nspire-clp",
+ NULL,
+};
+
+static struct map_desc nspire_io_desc[] __initdata = {
+ {
+ .virtual = NSPIRE_EARLY_UART_VIRT_BASE,
+ .pfn = __phys_to_pfn(NSPIRE_EARLY_UART_PHYS_BASE),
+ .length = SZ_4K,
+ .type = MT_DEVICE
+ }
+};
+
+static void __init nspire_init_timer(void)
+{
+ struct device_node *timer;
+ void __iomem *base;
+ const char *path;
+ struct clk *clk;
+ int irq, err;
+
+ of_clk_init(NULL);
+
+ err = of_property_read_string(of_aliases, "timer0", &path);
+ if (WARN_ON(err))
+ return;
+
+ timer = of_find_node_by_path(path);
+ base = of_iomap(timer, 0);
+ if (WARN_ON(!base))
+ return;
+
+ clk = of_clk_get_by_name(timer, NULL);
+ clk_register_clkdev(clk, timer->name, "sp804");
+
+ sp804_clocksource_init(base, timer->name);
+
+ err = of_property_read_string(of_aliases, "timer1", &path);
+ if (WARN_ON(err))
+ return;
+
+ timer = of_find_node_by_path(path);
+ base = of_iomap(timer, 0);
+ if (WARN_ON(!base))
+ return;
+
+ clk = of_clk_get_by_name(timer, NULL);
+ clk_register_clkdev(clk, timer->name, "sp804");
+
+ irq = irq_of_parse_and_map(timer, 0);
+ sp804_clockevents_init(base, irq, timer->name);
+}
+
+static void __init nspire_map_io(void)
+{
+ iotable_init(nspire_io_desc, ARRAY_SIZE(nspire_io_desc));
+}
+
+static struct clcd_panel nspire_cx_lcd_panel = {
+ .mode = {
+ .name = "Color LCD",
+ .refresh = 60,
+ .xres = 320,
+ .yres = 240,
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+ .pixclock = 1,
+ .hsync_len = 6,
+ .vsync_len = 1,
+ .right_margin = 50,
+ .left_margin = 38,
+ .lower_margin = 3,
+ .upper_margin = 17,
+ },
+ .width = 65, /* ~6.50 cm */
+ .height = 49, /* ~4.87 cm */
+ .tim2 = TIM2_IPC,
+ .cntl = (CNTL_BGR | CNTL_LCDTFT | CNTL_LCDVCOMP(1) |
+ CNTL_LCDBPP16_565),
+ .bpp = 16,
+};
+
+static struct clcd_panel nspire_classic_lcd_panel = {
+ .mode = {
+ .name = "Grayscale LCD",
+ .refresh = 60,
+ .xres = 320,
+ .yres = 240,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED,
+ .pixclock = 1,
+ .hsync_len = 6,
+ .vsync_len = 1,
+ .right_margin = 6,
+ .left_margin = 6,
+ },
+ .width = 71, /* 7.11cm */
+ .height = 53, /* 5.33cm */
+ .tim2 = 0x80007d0,
+ .cntl = CNTL_LCDBPP8 | CNTL_LCDMONO8,
+ .bpp = 8,
+ .grayscale = 1
+};
+
+static int nspire_clcd_setup(struct clcd_fb *fb)
+{
+ struct clcd_panel *panel;
+ size_t panel_size;
+ const char *type;
+ dma_addr_t dma;
+ int err;
+
+ BUG_ON(!fb->dev->dev.of_node);
+
+ err = of_property_read_string(fb->dev->dev.of_node, "lcd-type", &type);
+ if (err) {
+ pr_err("CLCD: Could not find lcd-type property\n");
+ return err;
+ }
+
+ if (!strcmp(type, "cx"))
+ {
+ panel = &nspire_cx_lcd_panel;
+ }
+ else if (!strcmp(type, "classic"))
+ {
+ panel = &nspire_classic_lcd_panel;
+ }
+ else
+ {
+ pr_err("CLCD: Unknown lcd-type %s\n", type);
+ return -EINVAL;
+ }
+
+ panel_size = ((panel->mode.xres * panel->mode.yres) * panel->bpp) / 8;
+ panel_size = ALIGN(panel_size, PAGE_SIZE);
+
+ fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev,
+ panel_size, &dma, GFP_KERNEL);
+
+ if (!fb->fb.screen_base) {
+ pr_err("CLCD: unable to map framebuffer\n");
+ return -ENOMEM;
+ }
+
+ fb->fb.fix.smem_start = dma;
+ fb->fb.fix.smem_len = panel_size;
+ fb->panel = panel;
+
+ return 0;
+}
+
+int nspire_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
+{
+ return dma_mmap_writecombine(&fb->dev->dev, vma,
+ fb->fb.screen_base, fb->fb.fix.smem_start,
+ fb->fb.fix.smem_len);
+}
+
+void nspire_clcd_remove(struct clcd_fb *fb)
+{
+ dma_free_writecombine(&fb->dev->dev, fb->fb.fix.smem_len,
+ fb->fb.screen_base, fb->fb.fix.smem_start);
+}
+
+static struct clcd_board nspire_clcd_data = {
+ .name = "LCD",
+ .check = clcdfb_check,
+ .decode = clcdfb_decode,
+ .setup = nspire_clcd_setup,
+ .mmap = nspire_clcd_mmap,
+ .remove = nspire_clcd_remove,
+};
+
+
+static struct of_dev_auxdata nspire_auxdata[] __initdata = {
+ OF_DEV_AUXDATA("arm,pl111", NSPIRE_LCD_PHYS_BASE,
+ NULL, &nspire_clcd_data),
+ { }
+};
+
+static void __init nspire_init(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table,
+ nspire_auxdata, NULL);
+}
+
+static void nspire_restart(char mode, const char *cmd)
+{
+}
+
+DT_MACHINE_START(NSPIRE, "TI-NSPIRE")
+ .map_io = nspire_map_io,
+ .init_irq = irqchip_init,
+ .init_time = nspire_init_timer,
+ .init_machine = nspire_init,
+ .dt_compat = nspire_dt_match,
+ .restart = nspire_restart,
+MACHINE_END

2013-04-07 14:33:08

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

On Sunday 07 April 2013, Daniel Tang wrote:
> On 07/04/2013, at 12:24 AM, Arnd Bergmann <[email protected]> wrote:
> >
> >> @@ -313,7 +314,7 @@ define archhelp
> >> echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'
> >> echo '* xipImage - XIP kernel image, if configured (arch/$(ARCH)/boot/xipImage)'
> >> echo ' uImage - U-Boot wrapped zImage'
> >> - echo ' bootpImage - Combined zImage and initial RAM disk'
> >> + echo ' bootpImage - Combined zImage and initial RAM disk'
> >> echo ' (supply initrd image via make variable INITRD=<path>)'
> >> echo '* dtbs - Build device tree blobs for enabled boards'
> >> echo ' install - Install uncompressed kernel'
> >
> > This looks like it wasn't meant to be in the patch.
>
> It probably isn't. I think there was trailing whitespace on that and my editor happened to remove it automatically.
>
> Should this be a separate patch to fix up formatting or should I leave it in as a drive-by fix?

Any cleanups like this should be separate patches, and ideally even
part of a different patch series.

> >
> >> + uart: uart@90020000 {
> >> + reg = <0x90020000 0x1000>;
> >> + interrupts = <1>;
> >> +
> >> + clocks = <&uart_clk>;
> >> + clock-names = "uart_clk";
> >> + };
> >
> > The name for a uart should be "serial". Since this is a pl01x, please add
> > the required properties for the device, e.g.
> >
> > compatible = "arm,pl011", "arm,primecell";
> >
> > You will need the "arm,primecell" bit to make the device appear on the
> > amba bus rather than the platform bus.
>
> That was actually deliberate because different models of the TI-NSPIRE have different
> serial hardware. On the newer CX models, it is a PL01x and on the older models, it has
> a 8250-like interface. They all reside at the same address with the same IRQ though.
>
> I thought it might be cleaner to specify the interrupts and registers in the common file
> and leave it to the board specific ones to implement the "compatible" property.

I see. It seems a little confusing to the reader though. I don't have a good answer,
but there are two other options how this can be handled:

* Put both devices in the devicetree and mark them as status="disabled" in the main file,
but enable one of them in the version specific files.

* leave them out of the .dtsi file and only define them in the specific .dts files.
> >> + err = of_property_read_string(of_aliases, "timer0", &path);
> >> + if (WARN_ON(err))
> >> + return;
> >> +
> >> + timer = of_find_node_by_path(path);
> >> + base = of_iomap(timer, 0);
> >> + if (WARN_ON(!base))
> >> + return;
> >> +
> >> + clk = of_clk_get_by_name(timer, NULL);
> >> + clk_register_clkdev(clk, timer->name, "sp804");
> >> +
> >> + sp804_clocksource_init(base, timer->name);
> >> +
> >> + err = of_property_read_string(of_aliases, "timer1", &path);
> >> + if (WARN_ON(err))
> >> + return;
> >
> > In particular, I think the method of using aliases to pick the right sp804
> > instance is being deprecated now. If both timers are identical, the kernel
> > will now just pick one of them.
>
> Sorry, I don't quite understand.
>
> Out of the timers, I want to add one as a clocksource and one as a clockevent.
> If they're identical (i.e. without using aliases), how should I tell the kernel,
> "Take the first timer you see and make it a clocksource, take the next one you
> see and make it a clockevent"?

The modified sp804 driver will have logic to do that. I think in the end we
decided that the driver should first look for any device that can be used as
a clocksource and use it that way. If it finds a second device, that can be
used as clockevent.

Arnd

2013-04-07 21:23:52

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

On Sunday 07 April 2013, Daniel Tang wrote:
> Here's an updated patch that enables support for the LCD.
>
> I looked into drivers/video/of_display_timing.c but it doesn't have the fields to describe the PL11x specific options needed in struct clcd_panel. At the moment, it is implemented by hardcoding the values in the kernel and using the device tree to select the correct configuration.
>
> Signed-off-by: Daniel Tang <[email protected]>

I think you should for now keep the clcd stuff in a separate file,
since it will be replaced with DT logic eventually. For now, the
auxdata method is ok, but Linus Walleij might already have thought
about how pl111 should get all its data from the device tree.

Arnd

2013-04-08 11:33:57

by Daniel Tang

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

Changes:

* Moved CLCD stuff to separate file until DT support for PL11x comes along
* Whitespace cleanup in arch/arm/Makefile removed from patch
* Rewrote code to find SP804 devices and use the first available one as a clockevent and the rest as clock sources until DT support comes to the SP804 driver.
* Fix up platform selection in Kconfig. TI-NSPIRE based option was visible even when ARCH_MULTI_V4_V5 is not selected.
* Added keypad driver and keymap
* Support for 'make dtbs'

Signed-off-by: Daniel Tang <[email protected]>
---
arch/arm/Kconfig | 2 +
arch/arm/Kconfig.debug | 16 ++
arch/arm/Makefile | 1 +
arch/arm/boot/dts/Makefile | 1 +
arch/arm/boot/dts/nspire-cx.dts | 110 ++++++++++
arch/arm/boot/dts/nspire.dtsi | 168 +++++++++++++++
arch/arm/include/debug/nspire.S | 28 +++
arch/arm/mach-nspire/Kconfig | 14 ++
arch/arm/mach-nspire/Makefile | 2 +
arch/arm/mach-nspire/Makefile.boot | 0
arch/arm/mach-nspire/clcd.c | 123 +++++++++++
arch/arm/mach-nspire/clcd.h | 14 ++
arch/arm/mach-nspire/mmio.h | 15 ++
arch/arm/mach-nspire/nspire.c | 139 ++++++++++++
drivers/input/keyboard/Kconfig | 10 +
drivers/input/keyboard/Makefile | 1 +
drivers/input/keyboard/nspire-keypad.c | 318 ++++++++++++++++++++++++++++
include/linux/platform_data/nspire-keypad.h | 28 +++
18 files changed, 990 insertions(+)

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 1cacda4..3f0cd8c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1081,6 +1081,8 @@ source "arch/arm/mach-netx/Kconfig"

source "arch/arm/mach-nomadik/Kconfig"

+source "arch/arm/mach-nspire/Kconfig"
+
source "arch/arm/plat-omap/Kconfig"

source "arch/arm/mach-omap1/Kconfig"
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 9b31f43..5da3a50 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -298,6 +298,20 @@ choice
Say Y here if you want kernel low-level debugging support
on MVEBU based platforms.

+ config DEBUG_NSPIRE_CLASSIC_UART
+ bool "Kernel low-level debugging via TI-NSPIRE 8250 UART"
+ depends on ARCH_NSPIRE
+ help
+ Say Y here if you want kernel low-level debugging support
+ on TI-NSPIRE classic models.
+
+ config DEBUG_NSPIRE_CX_UART
+ bool "Kernel low-level debugging via TI-NSPIRE PL011 UART"
+ depends on ARCH_NSPIRE
+ help
+ Say Y here if you want kernel low-level debugging support
+ on TI-NSPIRE CX models.
+
config DEBUG_OMAP2PLUS_UART
bool "Kernel low-level debugging messages via OMAP2PLUS UART"
depends on ARCH_OMAP2PLUS
@@ -591,6 +605,8 @@ config DEBUG_LL_INCLUDE
DEBUG_IMX6Q_UART
default "debug/highbank.S" if DEBUG_HIGHBANK_UART
default "debug/mvebu.S" if DEBUG_MVEBU_UART
+ default "debug/nspire.S" if DEBUG_NSPIRE_CX_UART || \
+ DEBUG_NSPIRE_CLASSIC_UART
default "debug/omap2plus.S" if DEBUG_OMAP2PLUS_UART
default "debug/picoxcell.S" if DEBUG_PICOXCELL_UART
default "debug/socfpga.S" if DEBUG_SOCFPGA_UART
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index ee4605f..2580d2b 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -165,6 +165,7 @@ machine-$(CONFIG_ARCH_MXS) += mxs
machine-$(CONFIG_ARCH_MVEBU) += mvebu
machine-$(CONFIG_ARCH_NETX) += netx
machine-$(CONFIG_ARCH_NOMADIK) += nomadik
+machine-$(CONFIG_ARCH_NSPIRE) += nspire
machine-$(CONFIG_ARCH_OMAP1) += omap1
machine-$(CONFIG_ARCH_OMAP2PLUS) += omap2
machine-$(CONFIG_ARCH_ORION5X) += orion5x
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 9c62558..adf8116 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -114,6 +114,7 @@ dtb-$(CONFIG_ARCH_MXS) += imx23-evk.dtb \
imx28-sps1.dtb \
imx28-tx28.dtb
dtb-$(CONFIG_ARCH_NOMADIK) += ste-nomadik-s8815.dtb
+dtb-$(CONFIG_ARCH_NSPIRE) += nspire-cx.dtb
dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \
omap3-beagle.dtb \
omap3-beagle-xm.dtb \
diff --git a/arch/arm/boot/dts/nspire-cx.dts b/arch/arm/boot/dts/nspire-cx.dts
new file mode 100644
index 0000000..1da2e44
--- /dev/null
+++ b/arch/arm/boot/dts/nspire-cx.dts
@@ -0,0 +1,110 @@
+/*
+ * linux/arch/arm/boot/nspire-cx.dts
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+/dts-v1/;
+
+/include/ "nspire.dtsi"
+
+/ {
+ model = "TI-NSPIRE CX";
+ compatible = "arm,nspire-cx";
+
+ memory {
+ device_type = "memory";
+ reg = <0x10000000 0x4000000>; /* 64 MB */
+ };
+
+ uart_clk: uart_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <12000000>;
+ };
+
+ /* Not really a fixed clock but we'll fix this later */
+ apb_pclk: apb_pclk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <33000000>;
+ };
+
+ ahb {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ intc: interrupt-controller@DC000000 {
+ compatible = "arm,pl190-vic";
+ interrupt-controller;
+ reg = <0xDC000000 0x1000>;
+ #interrupt-cells = <1>;
+ };
+
+ lcd: lcd@C0000000 {
+ lcd-type = "cx";
+ };
+
+ apb@90000000 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ i2c@90050000 {
+ compatible = "snps,designware-i2c";
+ reg = <0x90050000 0x1000>;
+ interrupts = <20>;
+ };
+
+ /*
+ fast_timer: timer@90010000 {
+ compatible = "arm,sp804", "arm,primecell";
+ };
+ */
+
+ uart0: serial@90020000 {
+ compatible = "arm,pl011", "arm,primecell";
+ };
+
+ timer0: timer@900C0000 {
+ compatible = "arm,sp804", "arm,primecell";
+ };
+
+ timer1: timer@900D0000 {
+ compatible = "arm,sp804", "arm,primecell";
+ };
+
+ keypad: input@900E0000 {
+ keymap = <
+ 0x0000001c 0x0001001c 0x00040039
+ 0x0005002c 0x00060015 0x0007000b
+ 0x0008000f 0x0100002d 0x01010011
+ 0x0102002f 0x01030004 0x01040016
+ 0x01050014 0x0106001f 0x01070002
+ 0x010a006a 0x02000013 0x02010010
+ 0x02020019 0x02030007 0x02040018
+ 0x02050031 0x02060032 0x02070005
+ 0x02080028 0x0209006c 0x03000026
+ 0x03010025 0x03020024 0x0303000a
+ 0x03040017 0x03050023 0x03060022
+ 0x03070008 0x03080035 0x03090069
+ 0x04000021 0x04010012 0x04020020
+ 0x0404002e 0x04050030 0x0406001e
+ 0x0407000d 0x04080037 0x04090067
+ 0x05010038 0x0502000c 0x0503001b
+ 0x05040034 0x0505001a 0x05060006
+ 0x05080027 0x0509000e 0x050a006f
+ 0x0600002b 0x0602004e 0x06030068
+ 0x06040003 0x0605006d 0x06060009
+ 0x06070001 0x0609000f 0x0708002a
+ 0x0709001d 0x070a0033 >;
+ };
+ };
+ };
+ chosen {
+ bootargs = "debug earlyprintk console=tty0 console=ttyAMA0,115200n8 root=/dev/ram0";
+ };
+};
diff --git a/arch/arm/boot/dts/nspire.dtsi b/arch/arm/boot/dts/nspire.dtsi
new file mode 100644
index 0000000..d745e3d
--- /dev/null
+++ b/arch/arm/boot/dts/nspire.dtsi
@@ -0,0 +1,168 @@
+/*
+ * linux/arch/arm/boot/nspire.dtsi
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/ "skeleton.dtsi"
+
+/ {
+ interrupt-parent = <&intc>;
+
+ cpus {
+ cpu@0 {
+ compatible = "arm,arm926ejs";
+ };
+ };
+
+ bootrom: bootrom@00000000 {
+ reg = <0x00000000 0x80000>;
+ };
+
+ sram: sram@A4000000 {
+ device = "memory";
+ reg = <0xA4000000 0x20000>;
+ };
+
+ timer_clk: timer_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <32768>;
+ };
+
+ lcd_clk: lcd_clk {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <660000000>;
+ };
+
+ ahb {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ spi: spi@A9000000 {
+ reg = <0xA9000000 0x1000>;
+ };
+
+ usb0: usb@B0000000 {
+ reg = <0xB0000000 0x1000>;
+ interrupts = <8>;
+ };
+
+ usb1: usb@B4000000 {
+ reg = <0xB4000000 0x1000>;
+ interrupts = <9>;
+ status = "disabled";
+ };
+
+ lcd: lcd@C0000000 {
+ compatible = "arm,pl111", "arm,primecell";
+ reg = <0xC0000000 0x1000>;
+ interrupts = <21>;
+
+ clocks = <&lcd_clk>;
+ clock-names = "apb_pclk";
+ };
+
+ adc: adc@C4000000 {
+ reg = <0xC4000000 0x1000>;
+ interrupts = <11>;
+ };
+
+ tdes: crypto@C8010000 {
+ reg = <0xC8010000 0x1000>;
+ };
+
+ sha256: crypto@CC000000 {
+ reg = <0xCC000000 0x1000>;
+ };
+
+ apb@90000000 {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ clock-ranges;
+ ranges;
+
+ gpio: gpio@90000000 {
+ reg = <0x90000000 0x1000>;
+ interrupts = <7>;
+ };
+
+ fast_timer: timer@90010000 {
+ reg = <0x90010000 0x1000>;
+ interrupts = <17>;
+ };
+
+ uart: serial@90020000 {
+ reg = <0x90020000 0x1000>;
+ interrupts = <1>;
+
+ clocks = <&uart_clk>, <&apb_pclk>;
+ clock-names = "uart_clk", "apb_pclk";
+ };
+
+ timer0: timer@900C0000 {
+ reg = <0x900C0000 0x1000>;
+
+ clocks = <&timer_clk>;
+ clock-names = "timer_clk";
+ };
+
+ timer1: timer@900D0000 {
+ reg = <0x900D0000 0x1000>;
+ interrupts = <19>;
+
+ clocks = <&timer_clk>;
+ clock-names = "timer_clk";
+ };
+
+ watchdog: watchdog@90060000 {
+ compatible = "arm,amba-primecell";
+ reg = <0x90060000 0x1000>;
+ interrupts = <3>;
+ };
+
+ rtc: rtc@90090000 {
+ reg = <0x90090000 0x1000>;
+ interrupts = <4>;
+ };
+
+ misc: misc@900A0000 {
+ reg = <0x900A0000 0x1000>;
+ };
+
+ pwr: pwr@900B0000 {
+ reg = <0x900B0000 0x1000>;
+ interrupts = <15>;
+ };
+
+ keypad: input@900E0000 {
+ compatible = "nspire-keypad";
+ reg = <0x900E0000 0x1000>;
+ interrupts = <16>;
+
+ scan-interval = <1000>;
+ row-delay = <200>;
+
+ clocks = <&apb_pclk>;
+ clock-names = "apb_pclk";
+ };
+
+ contrast: contrast@900F0000 {
+ reg = <0x900F0000 0x1000>;
+ };
+
+ led: led@90110000 {
+ reg = <0x90110000 0x1000>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/include/debug/nspire.S b/arch/arm/include/debug/nspire.S
new file mode 100644
index 0000000..3a9729c
--- /dev/null
+++ b/arch/arm/include/debug/nspire.S
@@ -0,0 +1,28 @@
+/*
+ * linux/arch/arm/include/debug/nspire.S
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#define NSPIRE_EARLY_UART_PHYS_BASE 0x90020000
+#define NSPIRE_EARLY_UART_VIRT_BASE 0xfee20000
+
+.macro addruart, rp, rv, tmp
+ ldr \rp, =(NSPIRE_EARLY_UART_PHYS_BASE) @ physical base address
+ ldr \rv, =(NSPIRE_EARLY_UART_VIRT_BASE) @ virtual base address
+.endm
+
+
+#ifdef CONFIG_DEBUG_NSPIRE_CX_UART
+#include <asm/hardware/debug-pl01x.S>
+#endif
+
+#ifdef CONFIG_DEBUG_NSPIRE_CLASSIC_UART
+#define UART_SHIFT 2
+#include <asm/hardware/debug-8250.S>
+#endif
diff --git a/arch/arm/mach-nspire/Kconfig b/arch/arm/mach-nspire/Kconfig
new file mode 100644
index 0000000..d485280
--- /dev/null
+++ b/arch/arm/mach-nspire/Kconfig
@@ -0,0 +1,14 @@
+config ARCH_NSPIRE
+ bool "TI-NSPIRE based"
+ depends on ARCH_MULTI_V4_V5
+ depends on MMU
+ select CPU_ARM926T
+ select COMMON_CLK
+ select GENERIC_CLOCKEVENTS
+ select SPARSE_IRQ
+ select ARM_AMBA
+ select ARM_VIC
+ select ARM_TIMER_SP804
+ select USE_OF
+ help
+ This enables support for systems using the TI-NSPIRE CPU
diff --git a/arch/arm/mach-nspire/Makefile b/arch/arm/mach-nspire/Makefile
new file mode 100644
index 0000000..1bec256
--- /dev/null
+++ b/arch/arm/mach-nspire/Makefile
@@ -0,0 +1,2 @@
+obj-y += nspire.o
+obj-y += clcd.o
diff --git a/arch/arm/mach-nspire/Makefile.boot b/arch/arm/mach-nspire/Makefile.boot
new file mode 100644
index 0000000..e69de29
diff --git a/arch/arm/mach-nspire/clcd.c b/arch/arm/mach-nspire/clcd.c
new file mode 100644
index 0000000..ee6ffcb
--- /dev/null
+++ b/arch/arm/mach-nspire/clcd.c
@@ -0,0 +1,123 @@
+/*
+ * linux/arch/arm/mach-nspire/clcd.c
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/of.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd.h>
+#include <linux/dma-mapping.h>
+
+static struct clcd_panel nspire_cx_lcd_panel = {
+ .mode = {
+ .name = "Color LCD",
+ .refresh = 60,
+ .xres = 320,
+ .yres = 240,
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+ .pixclock = 1,
+ .hsync_len = 6,
+ .vsync_len = 1,
+ .right_margin = 50,
+ .left_margin = 38,
+ .lower_margin = 3,
+ .upper_margin = 17,
+ },
+ .width = 65, /* ~6.50 cm */
+ .height = 49, /* ~4.87 cm */
+ .tim2 = TIM2_IPC,
+ .cntl = (CNTL_BGR | CNTL_LCDTFT | CNTL_LCDVCOMP(1) |
+ CNTL_LCDBPP16_565),
+ .bpp = 16,
+};
+
+static struct clcd_panel nspire_classic_lcd_panel = {
+ .mode = {
+ .name = "Grayscale LCD",
+ .refresh = 60,
+ .xres = 320,
+ .yres = 240,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED,
+ .pixclock = 1,
+ .hsync_len = 6,
+ .vsync_len = 1,
+ .right_margin = 6,
+ .left_margin = 6,
+ },
+ .width = 71, /* 7.11cm */
+ .height = 53, /* 5.33cm */
+ .tim2 = 0x80007d0,
+ .cntl = CNTL_LCDBPP8 | CNTL_LCDMONO8,
+ .bpp = 8,
+ .grayscale = 1
+};
+
+int nspire_clcd_setup(struct clcd_fb *fb)
+{
+ struct clcd_panel *panel;
+ size_t panel_size;
+ const char *type;
+ dma_addr_t dma;
+ int err;
+
+ BUG_ON(!fb->dev->dev.of_node);
+
+ err = of_property_read_string(fb->dev->dev.of_node, "lcd-type", &type);
+ if (err) {
+ pr_err("CLCD: Could not find lcd-type property\n");
+ return err;
+ }
+
+ if (!strcmp(type, "cx"))
+ {
+ panel = &nspire_cx_lcd_panel;
+ }
+ else if (!strcmp(type, "classic"))
+ {
+ panel = &nspire_classic_lcd_panel;
+ }
+ else
+ {
+ pr_err("CLCD: Unknown lcd-type %s\n", type);
+ return -EINVAL;
+ }
+
+ panel_size = ((panel->mode.xres * panel->mode.yres) * panel->bpp) / 8;
+ panel_size = ALIGN(panel_size, PAGE_SIZE);
+
+ fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev,
+ panel_size, &dma, GFP_KERNEL);
+
+ if (!fb->fb.screen_base) {
+ pr_err("CLCD: unable to map framebuffer\n");
+ return -ENOMEM;
+ }
+
+ fb->fb.fix.smem_start = dma;
+ fb->fb.fix.smem_len = panel_size;
+ fb->panel = panel;
+
+ return 0;
+}
+
+int nspire_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
+{
+ return dma_mmap_writecombine(&fb->dev->dev, vma,
+ fb->fb.screen_base, fb->fb.fix.smem_start,
+ fb->fb.fix.smem_len);
+}
+
+void nspire_clcd_remove(struct clcd_fb *fb)
+{
+ dma_free_writecombine(&fb->dev->dev, fb->fb.fix.smem_len,
+ fb->fb.screen_base, fb->fb.fix.smem_start);
+}
diff --git a/arch/arm/mach-nspire/clcd.h b/arch/arm/mach-nspire/clcd.h
new file mode 100644
index 0000000..7144399
--- /dev/null
+++ b/arch/arm/mach-nspire/clcd.h
@@ -0,0 +1,14 @@
+/*
+ * linux/arch/arm/mach-nspire/clcd.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+int nspire_clcd_setup(struct clcd_fb *fb);
+int nspire_clcd_mmap(struct clcd_fb *fb, struct vm_area_struct *vma);
+void nspire_clcd_remove(struct clcd_fb *fb);
diff --git a/arch/arm/mach-nspire/mmio.h b/arch/arm/mach-nspire/mmio.h
new file mode 100644
index 0000000..f0a1fa5
--- /dev/null
+++ b/arch/arm/mach-nspire/mmio.h
@@ -0,0 +1,15 @@
+/*
+ * linux/arch/arm/mach-nspire/mmio.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#define NSPIRE_EARLY_UART_PHYS_BASE 0x90020000
+#define NSPIRE_EARLY_UART_VIRT_BASE 0xfee20000
+
+#define NSPIRE_LCD_PHYS_BASE 0xC0000000
diff --git a/arch/arm/mach-nspire/nspire.c b/arch/arm/mach-nspire/nspire.c
new file mode 100644
index 0000000..7d972b8
--- /dev/null
+++ b/arch/arm/mach-nspire/nspire.c
@@ -0,0 +1,139 @@
+/*
+ * linux/arch/arm/mach-nspire/nspire.c
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/arm-vic.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+#include <asm/mach/map.h>
+
+#include <asm/hardware/timer-sp.h>
+
+#include "mmio.h"
+#include "clcd.h"
+
+static const char *nspire_dt_match[] __initconst = {
+ "arm,nspire",
+ "arm,nspire-cx",
+ "arm,nspire-tp",
+ "arm,nspire-clp",
+ NULL,
+};
+
+static struct map_desc nspire_io_desc[] __initdata = {
+ {
+ .virtual = NSPIRE_EARLY_UART_VIRT_BASE,
+ .pfn = __phys_to_pfn(NSPIRE_EARLY_UART_PHYS_BASE),
+ .length = SZ_4K,
+ .type = MT_DEVICE
+ }
+};
+
+#define TIMER_NAME_MAXLEN 32
+static void __init nspire_add_sp804_timers(void)
+{
+ struct device_node *of_timer;
+ int clockevents_found = 0;
+
+ for_each_compatible_node(of_timer, NULL, "arm,sp804") {
+ struct resource res;
+ void __iomem *base;
+ char *timer_name;
+ struct clk *clk;
+ int irq;
+
+ clk = of_clk_get_by_name(of_timer, NULL);
+ if (WARN_ON(!clk))
+ continue;
+
+ timer_name = kzalloc(TIMER_NAME_MAXLEN, GFP_ATOMIC);
+ if (!timer_name)
+ break;
+
+ base = of_iomap(of_timer, 0);
+ if (WARN_ON(!base)) {
+ kfree(timer_name);
+ continue;
+ }
+
+ of_address_to_resource(of_timer, 0, &res);
+ scnprintf(timer_name, TIMER_NAME_MAXLEN, "%llx.%s",
+ (unsigned long long)res.start,
+ of_timer->name);
+
+ clk_register_clkdev(clk, timer_name, "sp804");
+ if (!clockevents_found) {
+ irq = irq_of_parse_and_map(of_timer, 0);
+ if (irq) {
+ sp804_clockevents_init(base, irq,timer_name);
+ clockevents_found = 1;
+ continue;
+ }
+ }
+
+ sp804_clocksource_init(base, timer_name);
+ }
+}
+
+
+static void __init nspire_init_timer(void)
+{
+ of_clk_init(NULL);
+ nspire_add_sp804_timers();
+}
+
+static void __init nspire_map_io(void)
+{
+ iotable_init(nspire_io_desc, ARRAY_SIZE(nspire_io_desc));
+}
+
+static struct clcd_board nspire_clcd_data = {
+ .name = "LCD",
+ .check = clcdfb_check,
+ .decode = clcdfb_decode,
+ .setup = nspire_clcd_setup,
+ .mmap = nspire_clcd_mmap,
+ .remove = nspire_clcd_remove,
+};
+
+
+static struct of_dev_auxdata nspire_auxdata[] __initdata = {
+ OF_DEV_AUXDATA("arm,pl111", NSPIRE_LCD_PHYS_BASE,
+ NULL, &nspire_clcd_data),
+ { }
+};
+
+static void __init nspire_init(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table,
+ nspire_auxdata, NULL);
+}
+
+static void nspire_restart(char mode, const char *cmd)
+{
+}
+
+DT_MACHINE_START(NSPIRE, "TI-NSPIRE")
+ .map_io = nspire_map_io,
+ .init_irq = irqchip_init,
+ .init_time = nspire_init_timer,
+ .init_machine = nspire_init,
+ .dt_compat = nspire_dt_match,
+ .restart = nspire_restart,
+MACHINE_END
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index ac05006..677adba 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -418,6 +418,16 @@ config KEYBOARD_NOMADIK
To compile this driver as a module, choose M here: the
module will be called nmk-ske-keypad.

+config KEYBOARD_NSPIRE
+ tristate "TI-NSPIRE builtin keyboard"
+ depends on ARCH_NSPIRE
+ select INPUT_MATRIXKMAP
+ help
+ Say Y here if you want to use the builtin keypad on the TI-NSPIRE.
+
+ To compile this driver as a module, choose M here: the
+ module will be called nspire-keypad.
+
config KEYBOARD_TEGRA
tristate "NVIDIA Tegra internal matrix keyboard controller support"
depends on ARCH_TEGRA && OF
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 49b1645..89d997b 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o
obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o
+obj-$(CONFIG_KEYBOARD_NSPIRE) += nspire-keypad.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
diff --git a/drivers/input/keyboard/nspire-keypad.c b/drivers/input/keyboard/nspire-keypad.c
new file mode 100644
index 0000000..5b2b3cc
--- /dev/null
+++ b/drivers/input/keyboard/nspire-keypad.c
@@ -0,0 +1,318 @@
+/*
+ * linux/drivers/input/keyboard/nspire-keypad.c
+ *
+ * Copyright (C) 2013 Daniel Tang <[email protected]>
+ *
+ * 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/input/matrix_keypad.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <linux/platform_data/nspire-keypad.h>
+
+#define KEYPAD_SCAN_MODE 0x00
+#define KEYPAD_CNTL 0x04
+#define KEYPAD_INT 0x08
+#define KEYPAD_INTMSK 0x0C
+
+#define KEYPAD_DATA 0x10
+#define KEYPAD_GPIO 0x30
+
+#define KEYPAD_UNKNOWN_INT 0x40
+#define KEYPAD_UNKNOWN_INT_STS 0x44
+
+
+struct nspire_keypad {
+ spinlock_t lock;
+
+ void __iomem *reg_base;
+ int irq;
+ u32 int_mask;
+
+ struct input_dev *input;
+ struct clk *clk;
+
+ struct nspire_keypad_data options;
+ int row_shift;
+};
+
+static inline void nspire_report_state(struct nspire_keypad *keypad,
+ int row, int col, unsigned int state)
+{
+ int code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
+ unsigned short *keymap = keypad->input->keycode;
+
+ state = keypad->options.active_low ? !state : !!state;
+ input_report_key(keypad->input, keymap[code], state);
+}
+
+static irqreturn_t nspire_keypad_irq(int irq, void *dev_id)
+{
+ struct nspire_keypad *keypad = dev_id;
+ u32 int_sts;
+ u16 state[8];
+ int row, col;
+
+ int_sts = readl(keypad->reg_base + KEYPAD_INT) & keypad->int_mask;
+
+ if (!int_sts)
+ return IRQ_NONE;
+
+ spin_lock(&keypad->lock);
+
+ memcpy_fromio(state, keypad->reg_base + KEYPAD_DATA, sizeof(state));
+
+ for (row = 0; row < KEYPAD_BITMASK_ROWS; row++) {
+ u16 bits = state[row];
+ for (col = 0; col < KEYPAD_BITMASK_COLS; col++) {
+ nspire_report_state(keypad, row, col, bits & (1<<col));
+ }
+ }
+ input_sync(keypad->input);
+ writel(0x3, keypad->reg_base + KEYPAD_INT);
+
+ spin_unlock(&keypad->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int nspire_keypad_chip_init(struct nspire_keypad *keypad)
+{
+ unsigned long val = 0, cycles_per_us, delay_cycles, row_delay_cycles;
+
+ cycles_per_us = (clk_get_rate(keypad->clk) / 1000000);
+ if (cycles_per_us == 0)
+ cycles_per_us = 1;
+
+ delay_cycles = cycles_per_us * keypad->options.scan_interval;
+ WARN_ON(delay_cycles >= (1<<16)); /* Overflow */
+ delay_cycles &= 0xffff;
+
+ row_delay_cycles = cycles_per_us * keypad->options.row_delay;
+ WARN_ON(row_delay_cycles >= (1<<14)); /* Overflow */
+ row_delay_cycles &= 0x3fff;
+
+
+ val |= (3<<0); /* Set scan mode to 3 (continuous scan) */
+ val |= (row_delay_cycles<<2); /* Delay between scanning each row */
+ val |= (delay_cycles<<16); /* Delay between scans */
+ writel(val, keypad->reg_base + KEYPAD_SCAN_MODE);
+
+ val = (KEYPAD_BITMASK_ROWS & 0xff) | (KEYPAD_BITMASK_COLS & 0xff)<<8;
+ writel(val, keypad->reg_base + KEYPAD_CNTL);
+
+ /* Enable interrupts */
+ keypad->int_mask = (1<<1);
+ writel(keypad->int_mask, keypad->reg_base + 0xc);
+
+ /* Disable GPIO interrupts to prevent hanging on touchpad */
+ /* Possibly used to detect touchpad events */
+ writel(0, keypad->reg_base + KEYPAD_UNKNOWN_INT);
+ /* Acknowledge existing interrupts */
+ writel(~0, keypad->reg_base + KEYPAD_UNKNOWN_INT_STS);
+
+ return 0;
+}
+
+static int nspire_keypad_probe(struct platform_device *pdev)
+{
+ const struct nspire_keypad_data *plat = pdev->dev.platform_data;
+ const struct device_node *of_node = pdev->dev.of_node;
+ struct nspire_keypad *keypad;
+ struct input_dev *input;
+ struct resource *res;
+ struct clk *clk;
+ int irq, error;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get keypad irq\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "missing platform resources\n");
+ return -EINVAL;
+ }
+
+ clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get clock\n");
+ return -EINVAL;
+ }
+
+ clk_prepare(clk);
+ error = clk_enable(clk);
+ if (error) {
+ goto err_put_clk;
+ }
+
+ keypad = kzalloc(sizeof(struct nspire_keypad), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!keypad || !input) {
+ dev_err(&pdev->dev, "failed to allocate keypad memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ if (plat) {
+ memcpy(&keypad->options, plat, sizeof(*plat));
+ } else {
+ /* Load values from device tree */
+
+ error = of_property_read_u32(of_node, "scan-interval",
+ &keypad->options.scan_interval);
+ if (error) {
+ dev_err(&pdev->dev, "failed to get scan-interval\n");
+ goto err_free_mem;
+ }
+
+ error = of_property_read_u32(of_node, "row-delay",
+ &keypad->options.row_delay);
+ if (error) {
+ dev_err(&pdev->dev, "failed to get row-delay\n");
+ goto err_free_mem;
+ }
+
+ keypad->options.active_low = of_property_read_bool(of_node,
+ "active-low");
+
+ keypad->options.keymap = NULL;
+ }
+
+ keypad->row_shift = get_count_order(KEYPAD_BITMASK_COLS);
+ keypad->irq = irq;
+ keypad->clk = clk;
+ keypad->input = input;
+ spin_lock_init(&keypad->lock);
+
+ if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ error = -EBUSY;
+ goto err_free_mem;
+ }
+
+ keypad->reg_base = ioremap(res->start, resource_size(res));
+ if (!keypad->reg_base) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ error = -ENXIO;
+ goto err_free_mem_region;
+ }
+
+ input->id.bustype = BUS_HOST;
+ input->name = "nspire-keypad";
+ input->dev.parent = &pdev->dev;
+
+ set_bit(EV_KEY, input->evbit);
+ set_bit(EV_REP, input->evbit);
+
+ error = matrix_keypad_build_keymap(keypad->options.keymap, "keymap",
+ KEYPAD_BITMASK_ROWS, KEYPAD_BITMASK_COLS, NULL, input);
+ if (error) {
+ dev_err(&pdev->dev, "building keymap failed\n");
+ goto err_iounmap;
+ }
+
+ error = nspire_keypad_chip_init(keypad);
+ if (error) {
+ dev_err(&pdev->dev, "unable to init keypad hardware\n");
+ goto err_iounmap;
+ }
+
+ error = request_irq(keypad->irq, nspire_keypad_irq, 0,
+ "nspire_keypad", keypad);
+ if (error) {
+ dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);
+ goto err_iounmap;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&pdev->dev,
+ "unable to register input device: %d\n", error);
+ goto err_free_irq;
+ }
+
+ platform_set_drvdata(pdev, keypad);
+
+ dev_info(&pdev->dev, "TI-NSPIRE keypad at %#08x ("
+ "scan_interval=%uus,row_delay=%uus"
+ "%s)\n", res->start,
+ keypad->options.row_delay,
+ keypad->options.scan_interval,
+ keypad->options.active_low ? ",active_low" : "");
+
+ return 0;
+
+err_free_irq:
+ free_irq(keypad->irq, keypad);
+err_iounmap:
+ iounmap(keypad->reg_base);
+err_free_mem_region:
+ release_mem_region(res->start, resource_size(res));
+err_free_mem:
+ input_free_device(input);
+ kfree(keypad);
+
+ clk_disable(clk);
+err_put_clk:
+ clk_unprepare(clk);
+ clk_put(clk);
+ return error;
+}
+
+static int nspire_keypad_remove(struct platform_device *pdev)
+{
+ struct nspire_keypad *keypad = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ free_irq(keypad->irq, keypad);
+
+ input_unregister_device(keypad->input);
+
+ iounmap(keypad->reg_base);
+ release_mem_region(res->start, resource_size(res));
+ kfree(keypad);
+
+ clk_disable(keypad->clk);
+ clk_unprepare(keypad->clk);
+ clk_put(keypad->clk);
+
+ return 0;
+}
+#ifdef CONFIG_OF
+static const struct of_device_id nspire_keypad_dt_match[] = {
+ { .compatible = "nspire-keypad" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, nspire_keypad_dt_match);
+#endif
+
+static struct platform_driver nspire_keypad_driver = {
+ .driver = {
+ .name = "nspire-keypad",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(nspire_keypad_dt_match),
+ },
+ .remove = nspire_keypad_remove,
+ .probe = nspire_keypad_probe
+};
+
+module_platform_driver(nspire_keypad_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("TI-NSPIRE Keypad Driver");
diff --git a/include/linux/platform_data/nspire-keypad.h b/include/linux/platform_data/nspire-keypad.h
new file mode 100644
index 0000000..03deb64
--- /dev/null
+++ b/include/linux/platform_data/nspire-keypad.h
@@ -0,0 +1,28 @@
+/*
+ * linux/include/linux/platform_data/nspire-keypad.h
+ *
+ * Copyright (C) 2012 Daniel Tang <[email protected]>
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_NSPIRE_KEYPAD_H
+#define _LINUX_NSPIRE_KEYPAD_H
+
+#define KEYPAD_BITMASK_COLS 11
+#define KEYPAD_BITMASK_ROWS 8
+
+struct nspire_keypad_data {
+ struct matrix_keymap_data *keymap;
+
+ /* Maximum delay estimated assuming 33MHz APB */
+ u32 scan_interval; /* In microseconds (~2000us max) */
+ u32 row_delay; /* In microseconds (~500us max) */
+
+ bool active_low;
+};
+
+#endif

2013-04-08 19:15:26

by Fabian Vogt

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

The latest kernel it seems to get stuck at
console_lock() in register_framebuffer (drivers/video/fbmem.c:1655)
if the LCD-controller is enabled. (Early printk and serial console works
fine)
CONFIG_NO_HZ is not activated, it works completely.
Could this be a kernel bug or is this an issue with our timer
configuration?

2013-04-08 19:38:21

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

On Monday 08 April 2013, Fabian Vogt wrote:
> The latest kernel it seems to get stuck at
> console_lock() in register_framebuffer (drivers/video/fbmem.c:1655)
> if the LCD-controller is enabled. (Early printk and serial console works
> fine)
> CONFIG_NO_HZ is not activated, it works completely.
> Could this be a kernel bug or is this an issue with our timer
> configuration?

This usually happens when the console driver or anything it uses
calls a function that does a printk(), which means you get a recursive
call into the console code and try to get the same lock again.

Arnd

2013-04-08 20:05:20

by Fabian Vogt

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

> On Monday 08 April 2013, Fabian Vogt wrote:
>> The latest kernel it seems to get stuck at
>> console_lock() in do_register_framebuffer (drivers/video/fbmem.c:1655)
>> if the LCD-controller is enabled. (Early printk and serial console works
>> fine)
>> CONFIG_NO_HZ is not activated, it works completely.
>> Could this be a kernel bug or is this an issue with our timer
>> configuration?
>
> This usually happens when the console driver or anything it uses
> calls a function that does a printk(), which means you get a recursive
> call into the console code and try to get the same lock again.
Yes, I've heard that before, but than it would work/not work independently
of NO_HZ. Can printk's be executet within console_lock() ..
console_unlock() blocks?
If not, it could be fb_notifier_call_chain, too, but that wouldn't be
likely at all.
I also tried spinlock/mutex debugging and soft lockup detection,
but neither of them outputs anything.

2013-04-08 21:29:25

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

On Monday 08 April 2013, Fabian Vogt wrote:
> Yes, I've heard that before, but than it would work/not work independently
> of NO_HZ. Can printk's be executet within console_lock() ..
> console_unlock() blocks?

I think it's legal to call printk in that case, but the message will
not be printed until you reach the console_unlock()

> If not, it could be fb_notifier_call_chain, too, but that wouldn't be
> likely at all.
> I also tried spinlock/mutex debugging and soft lockup detection,
> but neither of them outputs anything.

Unfortunately, lockdep does not help with console_lock, since that
is a weird semaphore variant, not a standard mutex or spinlock.

Arnd

2013-04-09 06:00:04

by Daniel Tang

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]


On 09/04/2013, at 5:16 AM, Fabian Vogt <[email protected]> wrote:

> The latest kernel it seems to get stuck at
> console_lock() in register_framebuffer (drivers/video/fbmem.c:1655)
> if the LCD-controller is enabled. (Early printk and serial console works fine)
> CONFIG_NO_HZ is not activated, it works completely.
> Could this be a kernel bug or is this an issue with our timer configuration?

Even with the CONFIG_NO_HZ option disabled, I've found that probing of our LCD hardware sometimes still fail with an error of -2.

Depending on how I arrange the device tree or the contents of the boot arguments or, as said, the CONFIG_NO_HZ option, I get different behaviours ranging from:
* Getting a -2 error during device probe (possibly due to a clock not being found).
* LCD hardware is found but boot hangs right after the driver is initialised.
* LCD hardware is apparently initialised correctly and text appears onscreen but user mode init hangs and I never get to a shell prompt.

This smells of a race condition that occurs before the clcd driver even gets to initialise. Some bug in our timer code? A OF or console bug perhaps? Though, I'm a bit hesitant to blame it on a kernel subsystem bug until it's clear that our code isn't at fault.

Any suggestions?-

2013-04-09 11:14:31

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

On Thu, Apr 4, 2013 at 11:01 AM, Daniel Tang <[email protected]> wrote:

(I suspect I duplicate a lot of Arnd's comments, I haven't really checked.
If we contradict each other, point it out so we can discuss.)

> +config ARCH_NSPIRE
> + bool "TI-NSPIRE based"
> + depends on MMU
> + select CPU_ARM926T
> + select HAVE_MACH_CLKDEV
> + select CLKDEV_LOOKUP

For a new platform you should be selecting COMMON_CLK
and implement your clock drivers under drivers/clk/*.

> + select ARM_AMBA
> + select USB_ARCH_HAS_EHCI
> + select ARCH_WANT_OPTIONAL_GPIOLIB
> + select GENERIC_ALLOCATOR

I wonder what that is used for ... will be fun to see :-)

> + select ARCH_HAS_CPUFREQ
> + select CPU_FREQ_TABLE
> + help
> + This enables support for systems using the TI-NSPIRE CPU

This does not select GENERIC_CLOCKEVENTS and SPARSE_IRQ
which is not a good sign.

> +choice
> + prompt "Early printk and boot message serial interface"
> + help
> + Early printk output interface
> + depends on EARLY_PRINTK
> + default NSPIRE_EARLYPRINTK_CX
> +
> +config NSPIRE_EARLYPRINTK_CLASSIC
> + bool "Classic"
> +
> +config NSPIRE_EARLYPRINTK_CX
> + bool "CX model"
> +endchoice

This kind of stuf should go into
arch/arm/Kconfig.debug these days, along with implementation
of debug macro in arch/arm/debug/foo.S

> diff --git a/arch/arm/mach-nspire/classic.c b/arch/arm/mach-nspire/classic.c
(...)

> +union reg_clk_speed {
> + unsigned long raw;
> + struct {
> + unsigned long __padding0:1;
> + unsigned long base_cpu_ratio:7;
> + unsigned long is_base_27mhz:1;
> + unsigned long __padding1:3;
> + unsigned long cpu_ahb_ratio:3;
> + unsigned long __padding2:1;
> + unsigned long base_val:5;
> + } val;
> +};

Usually to try to fit a struct over a register range is not such a good
idea in Linux.

Instead define abstract representations of what you want to do
(remove everything named "padding" above, use proper data types instead
of these unsigned longs and that complex union) then use offsets to
registers and remap the base offset in memory.

It makes for simpler debugging and ability to properly use read|write[lwb]
macros.

> +static unsigned long classic_clocks_to_io(struct nspire_clk_speeds *clks)
> +{
> + union reg_clk_speed reg;
> +
> + BUG_ON(clks->div.base_cpu < 2);
> + BUG_ON(clks->div.cpu_ahb < 1);
> +
> + reg.raw = 0;
> + reg.val.base_cpu_ratio = clks->div.base_cpu / 2;
> + reg.val.cpu_ahb_ratio = clks->div.cpu_ahb - 1;
> + reg.val.is_base_27mhz = (clks->base <= 27000000);
> + reg.val.base_val = (300 - (clks->base / 1000000)) / 6;
> +
> + return reg.raw;
> +}

And that avoid having to create special helper functions like this.

In integer divisions consider using DIV_ROUND_CLOSEST()
from <linux/kernel.h> where applicable.

> +/* Interrupt handling */

The interrupt controller driver shall be placed in drivers/irqchips
these days.

> +static inline int check_interrupt(void __iomem *base, struct pt_regs *regs)
> +{
> + if (readl(base + 0x0)) {

Arrow antipattern, turn it around.

if (!readl(base))
return 0;

Then de-indent the rest of the code.

> + int irqnr = readl(base + 0x24);
> + unsigned prev_priority;
> + handle_IRQ(irqnr, regs);
> +
> + /* Reset priorities */
> + prev_priority = readl(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x28));
> + writel(prev_priority, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x2c));
> + return 1;
> + }
> + return 0;
> +}

I don't understand this, put in some explanation of what this function
does please.

> +asmlinkage void __exception_irq_entry
> + nspire_classic_handle_irq(struct pt_regs *regs)
> +{
> + int serviced;
> +
> + do {
> + void __iomem *reg_base = IOMEM(NSPIRE_INTERRUPT_VIRT_BASE);

Instead of casting this in every IRQ entry define a static local
in the irq driver file to point to the base.

Avoids time in the IRQ handler, so it obviously the right thing to do.

Please also use a dynamic remapping ioremap* insteaf of
this static IOMEM() thing.

> + serviced = 0;
> +
> + /* IRQ */
> + serviced += check_interrupt(reg_base, regs);
> + /* FIQ */
> + serviced += check_interrupt(reg_base + 0x100, regs);

Should you now handle FIQs first at all times?

> + } while (serviced > 0);
> +}
> +
> +static void classic_irq_ack(struct irq_data *d)
> +{
> + readl(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x28));
> +}

As stated use an ioremap:ed base static local.

(...)
> +void __init nspire_classic_init_irq(void)
> +{
> + /* No stickies */
> + writel(0, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x204));

0x204???

Use

#define FOO_REGISTER 0x204

to define understandable names for all of this so we can read the code.

Usually this means the comments are no longer needed because the
code becomes self-evident.

> +
> + /* Disable all interrupts */
> + writel(~0, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0xc));
> + writel(~0, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x10c));
> +
> + /* Set all priorities to 0 */
> + memset_io(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x300), 0, 0x7f);
> +
> + /* Accept interrupts of all priorities */
> + writel(0xf, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x2c));
> + writel(0xf, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x12c));
> +
> + /* Clear existing interrupts */
> + readl(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x28));
> + readl(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x128));

Dito, everywhere.

In the end looking like this:

readl(base + NSPIRE_IRQ_FOO); or something.

No IOMEM, etc.

(...)
> +/* Timer */

Hm, looks like you just forgot to select GENERIC_CLOCKEVENTS?

Strange if it works anyway :-/

We are comtemplating putting these things into drivers/timer,
nothing decided yet.

> +static int classic_timer_set_event(unsigned long delta,
> + struct clock_event_device *dev)
> +{
> + unsigned long flags;
> +
> + local_irq_save(flags);
> + writel(delta, NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2));
> + writel(1, NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2 + 0x8));
> + writel(0, NSPIRE_APB_VIRTIO(NSPIRE_APB_TIMER2 + 0x18));

Remove magic numbers, define register names.

Remap a base, get rid of NSPIRE_APB_VIRTIO().

> + local_irq_restore(flags);
> +
> + return 0;
> +}
> +static void classic_timer_set_mode(enum clock_event_mode mode,
> + struct clock_event_device *evt)
> +{
> + evt->mode = mode;
> +}
> +
> +static struct clock_event_device nspire_clkevt = {
> + .name = "clockevent",
> + .features = CLOCK_EVT_FEAT_ONESHOT,
> + .shift = 32,
> + .rating = 400,
> + .set_next_event = classic_timer_set_event,
> + .set_mode = classic_timer_set_mode,
> + .cpumask = cpu_all_mask,
> +};
> +
> +static irqreturn_t classic_timer_interrupt(int irq, void *dev_id)
> +{
> + struct clock_event_device *c = dev_id;
> +
> + /* Acknowledge */
> + writel((1<<0), NSPIRE_APB_VIRTIO(NSPIRE_APB_MISC + 0x20));

writel((1<<0) isn't very helpful.

#include <linux/bitops.h>

#define NSPIRE_TIMER_ACK_REG 0x20
#define NSPIRE_TIMER_ACK BIT(0)

writel(NSPIRE_TIMER_ACK, base + NSPIRE_TIMER_ACK_REG);

Then follow a lot of platform data and devices. These have to be moved
over to the device tree instead and deleted.

(...)
> +++ b/arch/arm/mach-nspire/touchpad.c
> @@ -0,0 +1,30 @@
> +/*
> + * linux/arch/arm/mach-nspire/touchpad.c
> + *
> + * Copyright (C) 2012 Fabian Vogt <[email protected]>
> + *
> + * 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/i2c.h>
> +
> +#include "touchpad.h"
> +
> +#if defined(CONFIG_MOUSE_SYNAPTICS_I2C) || \
> + defined(CONFIG_MOUSE_SYNAPTICS_I2C_MODULE)
> +static struct i2c_board_info synaptics_i2c = {
> + I2C_BOARD_INFO("synaptics_i2c", 0x20),
> + .irq = 0,
> +};
> +
> +void __init nspire_touchpad_init()
> +{
> + i2c_register_board_info(0, &synaptics_i2c, 1);
> +}


Not only should this be done from devicetree, but exactly which
synaptics driver are you using with this?

I don't think there is one in the kernel tree yet.

> diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
> index 831e1fd..e76c16b 100644
> --- a/arch/arm/tools/mach-types
> +++ b/arch/arm/tools/mach-types
> @@ -1204,3 +1204,6 @@ baileys MACH_BAILEYS BAILEYS 4169
> familybox MACH_FAMILYBOX FAMILYBOX 4170
> ensemble_mx35 MACH_ENSEMBLE_MX35 ENSEMBLE_MX35 4171
> sc_sps_1 MACH_SC_SPS_1 SC_SPS_1 4172
> +nspireclp MACH_NSPIRECLP NSPIRECLP 4441
> +nspiretp MACH_NSPIRETP NSPIRETP 4442
> +nspirecx MACH_NSPIRECX NSPIRECX 4443

We don't patch this file, Russell updates it from the machine registry on
his webpage.

And with device tree it goes irrelevant.

Yours,
Linus Walleij

2013-04-09 11:23:31

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

On Sun, Apr 7, 2013 at 11:23 PM, Arnd Bergmann <[email protected]> wrote:
> On Sunday 07 April 2013, Daniel Tang wrote:
>> Here's an updated patch that enables support for the LCD.
>>
>> I looked into drivers/video/of_display_timing.c but it doesn't have the fields to describe the
>> PL11x specific options needed in struct clcd_panel. At the moment, it is implemented by
>> hardcoding the values in the kernel and using the device tree to select the correct
>> configuration.
>>
>> Signed-off-by: Daniel Tang <[email protected]>
>
> I think you should for now keep the clcd stuff in a separate file,
> since it will be replaced with DT logic eventually. For now, the
> auxdata method is ok, but Linus Walleij might already have thought
> about how pl111 should get all its data from the device tree.

So last thing I heard there was *someone* at ARM working on
device tree support for the PL110/PL111, so I just don't know who
this was.

But I bet Pawel knows, because it will be needed for all
ARM reference designs.

Yours,
Linus Walleij

2013-04-09 11:39:50

by Daniel Tang

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

Hi,

Thanks for your comments! They're much appreciated.

Just to bring you up to speed, we decided to begin reimplementing the machine from scratch and slowly pull things in from the original patch. Arnd pointed out a lot of fundamental issues with our patch so we thought it'd be better to just start over instead of patch things up.

The latest copy of our patch is somewhere in this thread (http://archive.arm.linux.org.uk/lurker/message/20130408.113343.585af217.en.html) which will have already addressed some of the problems you've pointed out (using the device tree being a major one). We would also appreciate it if you could take a look at that one too.

On 09/04/2013, at 9:14 PM, Linus Walleij <[email protected]> wrote:

>>
>> +union reg_clk_speed {
>> + unsigned long raw;
>> + struct {
>> + unsigned long __padding0:1;
>> + unsigned long base_cpu_ratio:7;
>> + unsigned long is_base_27mhz:1;
>> + unsigned long __padding1:3;
>> + unsigned long cpu_ahb_ratio:3;
>> + unsigned long __padding2:1;
>> + unsigned long base_val:5;
>> + } val;
>> +};
> Usually to try to fit a struct over a register range is not such a good
> idea in Linux.
>
> Instead define abstract representations of what you want to do
> (remove everything named "padding" above, use proper data types instead
> of these unsigned longs and that complex union) then use offsets to
> registers and remap the base offset in memory.
>
> It makes for simpler debugging and ability to properly use read|write[lwb]
> macros.

The structure is actually a bitfield. We'd readl() the raw unsigned long into the 'raw' field and then access the data via the 'val' structure.

Should we be using bitmasks and bitshifting to get at those values instead?

>
>> +static unsigned long classic_clocks_to_io(struct nspire_clk_speeds *clks)
>> +{
>> + union reg_clk_speed reg;
>> +
>> + BUG_ON(clks->div.base_cpu < 2);
>> + BUG_ON(clks->div.cpu_ahb < 1);
>> +
>> + reg.raw = 0;
>> + reg.val.base_cpu_ratio = clks->div.base_cpu / 2;
>> + reg.val.cpu_ahb_ratio = clks->div.cpu_ahb - 1;
>> + reg.val.is_base_27mhz = (clks->base <= 27000000);
>> + reg.val.base_val = (300 - (clks->base / 1000000)) / 6;
>> +
>> + return reg.raw;
>> +}
>
> And that avoid having to create special helper functions like this.
>

Fair enough.

>
>> + int irqnr = readl(base + 0x24);
>> + unsigned prev_priority;
>> + handle_IRQ(irqnr, regs);
>> +
>> + /* Reset priorities */
>> + prev_priority = readl(IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x28));
>> + writel(prev_priority, IOMEM(NSPIRE_INTERRUPT_VIRT_BASE + 0x2c));
>> + return 1;
>> + }
>> + return 0;
>> +}
>
> I don't understand this, put in some explanation of what this function
> does please.

Yep gotcha. In future patches, we'll also put the magic numbers into proper defines.

>
>> +asmlinkage void __exception_irq_entry
>> + nspire_classic_handle_irq(struct pt_regs *regs)
>> +{
>> + int serviced;
>> +
>> + do {
>> + void __iomem *reg_base = IOMEM(NSPIRE_INTERRUPT_VIRT_BASE);
>
> Instead of casting this in every IRQ entry define a static local
> in the irq driver file to point to the base.
>
> Avoids time in the IRQ handler, so it obviously the right thing to do.
>
> Please also use a dynamic remapping ioremap* insteaf of
> this static IOMEM() thing.
>
>> + serviced = 0;
>> +
>> + /* IRQ */
>> + serviced += check_interrupt(reg_base, regs);
>> + /* FIQ */
>> + serviced += check_interrupt(reg_base + 0x100, regs);
>
> Should you now handle FIQs first at all times?

Ah yes, that would make sense.

>
> Hm, looks like you just forgot to select GENERIC_CLOCKEVENTS?
>
> Strange if it works anyway :-/
>
> We are comtemplating putting these things into drivers/timer,
> nothing decided yet.

That's fine, we'll deal with it when we pull this file into the 'good' patch.

>
> Not only should this be done from devicetree, but exactly which
> synaptics driver are you using with this?
>
> I don't think there is one in the kernel tree yet.
>

It's this one here http://lxr.free-electrons.com/source/drivers/input/mouse/synaptics_i2c.c
>
>
> And with device tree it goes irrelevant.

Yep, this has been addressed in our updated patch.

>
> Yours,
> Linus Walleij


Also, how would you like us to submit updates? Should we continue posting updated patches as replies to this thread?

Cheers,
tangrs-

2013-04-09 11:58:48

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

On Tue, Apr 9, 2013 at 1:39 PM, Daniel Tang <[email protected]> wrote:

>>> +union reg_clk_speed {
>>> + unsigned long raw;
>>> + struct {
>>> + unsigned long __padding0:1;
>>> + unsigned long base_cpu_ratio:7;
>>> + unsigned long is_base_27mhz:1;
>>> + unsigned long __padding1:3;
>>> + unsigned long cpu_ahb_ratio:3;
>>> + unsigned long __padding2:1;
>>> + unsigned long base_val:5;
>>> + } val;
>>> +};
>>
>> Usually to try to fit a struct over a register range is not such a good
>> idea in Linux.
>>
>> Instead define abstract representations of what you want to do
>> (remove everything named "padding" above, use proper data types instead
>> of these unsigned longs and that complex union) then use offsets to
>> registers and remap the base offset in memory.
>>
>> It makes for simpler debugging and ability to properly use read|write[lwb]
>> macros.
>
> The structure is actually a bitfield. We'd readl() the raw unsigned long into the 'raw' field and then access the data via the 'val' structure.
>
> Should we be using bitmasks and bitshifting to get at those values instead?

I personally think that is better because it avoids complex structures
and strange helper functions.

When it really shows it's ugly face is when you get a second generation
of the hardware that have some other bits in slightly different places,
and you have to create another struct to map a small difference instead
of being able to just tweak the code slightly.

>> Not only should this be done from devicetree, but exactly which
>> synaptics driver are you using with this?
>>
>> I don't think there is one in the kernel tree yet.
>>
>
> It's this one here http://lxr.free-electrons.com/source/drivers/input/mouse/synaptics_i2c.c

Aha I thought it was one of those modern RMI4 things.
OK then...

> Also, how would you like us to submit updates? Should we continue posting updated patches as replies to this thread?

Just repost it somehow, include relevant people on To/Cc. No need
to use the same thread I think?

Yours,
Linus Walleij

2013-04-09 12:02:04

by Pawel Moll

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

On Tue, 2013-04-09 at 12:23 +0100, Linus Walleij wrote:
> On Sun, Apr 7, 2013 at 11:23 PM, Arnd Bergmann <[email protected]> wrote:
> > On Sunday 07 April 2013, Daniel Tang wrote:
> >> Here's an updated patch that enables support for the LCD.
> >>
> >> I looked into drivers/video/of_display_timing.c but it doesn't have the fields to describe the
> >> PL11x specific options needed in struct clcd_panel. At the moment, it is implemented by
> >> hardcoding the values in the kernel and using the device tree to select the correct
> >> configuration.
> >>
> >> Signed-off-by: Daniel Tang <[email protected]>
> >
> > I think you should for now keep the clcd stuff in a separate file,
> > since it will be replaced with DT logic eventually. For now, the
> > auxdata method is ok, but Linus Walleij might already have thought
> > about how pl111 should get all its data from the device tree.
>
> So last thing I heard there was *someone* at ARM working on
> device tree support for the PL110/PL111, so I just don't know who
> this was.
>
> But I bet Pawel knows, because it will be needed for all
> ARM reference designs.

So I have PL111 code almost working here, however today it depends on
not-yet-upstream (to my knowledge) generic/common panel/display
framework. If I'm not disturbed again I may get something done this
week, hopefully removing the dependency.

There's also some work going on on a DRM driver for PL111, but I can't
promise any dates.

As to PL110 I have no idea how different is it from the PL111, but
nothing is happening about it anyway.

Paweł

2013-04-09 12:05:30

by Linus Walleij

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

On Tue, Apr 9, 2013 at 2:01 PM, Pawel Moll <[email protected]> wrote:

> As to PL110 I have no idea how different is it from the PL111, but
> nothing is happening about it anyway.

I have that on my Integrator/CP (in working condition!) so I will
be able to test it as it arrives, but basically I think the panel
model will be the same.

Yours,
Linus Walleij

2013-04-09 13:52:16

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [RFC PATCH arm: initial TI-Nspire support]

On Tue, Apr 09, 2013 at 01:01:56PM +0100, Pawel Moll wrote:
> So I have PL111 code almost working here, however today it depends on
> not-yet-upstream (to my knowledge) generic/common panel/display
> framework. If I'm not disturbed again I may get something done this
> week, hopefully removing the dependency.
>
> There's also some work going on on a DRM driver for PL111, but I can't
> promise any dates.
>
> As to PL110 I have no idea how different is it from the PL111, but
> nothing is happening about it anyway.

If you're doing PL111, then you might as well do PL110, because the two
are basically the same. But note that all PL111's aren't equal. Some
are closer to PL110 than the current PL111, so if you want to support
all PL111s, you also need to have the support in place for PL110.

Also note that the way the bits are assigned in the frame buffer varies
between platforms, and most certainly the TRMs documentation of colour
bitfield layouts can _not_ be relied upon.

I have considerable knowledge in this area, but as seems to be the norm
now, no one cares about that. I'll go back to twiddling my thumbs now,
waiting for the next email to arrive. Or maybe I'll go down to the
shops and wander around down there. Or something.