From: Mark Salter <msalter@redhat.com>
To: linux-kernel@vger.kernel.org
Subject: [PATCH 13/16] C6X: add drivers/platform/c6x files
Date: Wed, 11 May 2011 16:14:00 -0400 [thread overview]
Message-ID: <1305144843-5058-14-git-send-email-msalter@redhat.com> (raw)
In-Reply-To: <1305144843-5058-13-git-send-email-msalter@redhat.com>
This patch adds driver support for miscellaneous drivers for hardware found
on C64x DSP chips but not really part of the CPU core. These could have been
put under arch/c6x/kernel, but that would get cluttered pretty quickly as
new SoC parts are supported.
Signed-off-by: Mark Salter <msalter@redhat.com>
---
drivers/platform/Kconfig | 4 +
drivers/platform/Makefile | 1 +
drivers/platform/c6x/Kconfig | 27 ++
drivers/platform/c6x/Makefile | 8 +
drivers/platform/c6x/irq-c64xplus.c | 583 ++++++++++++++++++++++++++++++++++
drivers/platform/c6x/pll-tci648x.c | 433 +++++++++++++++++++++++++
drivers/platform/c6x/timer-tci648x.c | 121 +++++++
drivers/platform/c6x/tsc-c64xplus.c | 53 +++
8 files changed, 1230 insertions(+), 0 deletions(-)
create mode 100644 drivers/platform/c6x/Kconfig
create mode 100644 drivers/platform/c6x/Makefile
create mode 100644 drivers/platform/c6x/irq-c64xplus.c
create mode 100644 drivers/platform/c6x/pll-tci648x.c
create mode 100644 drivers/platform/c6x/timer-tci648x.c
create mode 100644 drivers/platform/c6x/tsc-c64xplus.c
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
index 8390dca..df334ed 100644
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -1,3 +1,7 @@
if X86
source "drivers/platform/x86/Kconfig"
endif
+
+if TMS320C6X
+source "drivers/platform/c6x/Kconfig"
+endif
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index 782953a..bd9e0e3 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_X86) += x86/
+obj-$(CONFIG_TMS320C6X) += c6x/
diff --git a/drivers/platform/c6x/Kconfig b/drivers/platform/c6x/Kconfig
new file mode 100644
index 0000000..a950fb8
--- /dev/null
+++ b/drivers/platform/c6x/Kconfig
@@ -0,0 +1,27 @@
+#
+# C6X Platform Specific Drivers
+#
+
+menuconfig C6X_PLATFORM_DEVICES
+ bool "C6X Platform Specific Device Drivers"
+ default y
+ depends on TMS320C6X
+ ---help---
+ Say Y here to get to see options for device drivers for various
+ c64x platforms. This option alone does not add any kernel code.
+
+ If you say N, all options in this submenu will be skipped and disabled.
+
+if C6X_PLATFORM_DEVICES
+
+config PLL_TCI648X
+ bool "PLL_TCI648X"
+ depends on COMMON_CLKDEV
+
+config TIMER_TCI648X
+ bool "TIMER_TCI648X"
+
+config PIC_C64XPLUS
+ bool "C64X+ Megamodule Interrupt Controller"
+
+endif # C6X_PLATFORM_DEVICES
diff --git a/drivers/platform/c6x/Makefile b/drivers/platform/c6x/Makefile
new file mode 100644
index 0000000..2f9be75
--- /dev/null
+++ b/drivers/platform/c6x/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for drivers/platform/c6x
+#
+
+obj-$(CONFIG_PLL_TCI648X) += pll-tci648x.o
+obj-$(CONFIG_TMS320C64XPLUS) += tsc-c64xplus.o
+obj-$(CONFIG_TIMER_TCI648X) += timer-tci648x.o
+obj-$(CONFIG_PIC_C64XPLUS) += irq-c64xplus.o
diff --git a/drivers/platform/c6x/irq-c64xplus.c b/drivers/platform/c6x/irq-c64xplus.c
new file mode 100644
index 0000000..4eff863
--- /dev/null
+++ b/drivers/platform/c6x/irq-c64xplus.c
@@ -0,0 +1,583 @@
+/*
+ * Support for C64x+ Megamodule Interrupt Controller
+ *
+ * Copyright (C) 2010, 2011 Texas Instruments Incorporated
+ * Contributed by: Mark Salter <msalter@redhat.com>
+ *
+ * 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/types.h>
+#include <linux/sched.h>
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/hardirq.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/hardirq.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/traps.h>
+#include <asm/page.h>
+#include <asm/hardware.h>
+
+#define IRQ_UNMAPPED 0xffff
+
+#define NR_MEGAMOD_COMBINERS 4
+
+#ifdef CONFIG_SOC_TMS320C6474
+#define NR_CIC_COMBINERS 2
+#define NR_CIC_OUTPUTS 16
+#else
+#define NR_CIC_COMBINERS 0
+#endif
+
+#define NR_COMBINERS (NR_MEGAMOD_COMBINERS + NR_CIC_COMBINERS)
+
+struct combiner_handler_info {
+ volatile uint32_t *mevtflag;
+ volatile uint32_t *evtclr;
+ uint16_t irqmap[32];
+};
+
+struct combiner_mask_info {
+ int irq_base;
+ volatile uint32_t *evtmask;
+};
+
+struct c6x_irq_chip {
+ struct irq_chip chip;
+ struct combiner_mask_info *minfo;
+};
+
+static struct combiner_mask_info megamod_mask_info[NR_MEGAMOD_COMBINERS];
+static struct combiner_handler_info megamod_handler_info[NR_MEGAMOD_COMBINERS];
+
+uint16_t prio_to_irq[NR_SYS_IRQS];
+
+static uint8_t irq_to_prio[NR_IRQS];
+
+/* The 16 SoC GPIOs can span more than one megamodule */
+#define NR_GPIO_CHIPS 2
+
+static struct c6x_irq_chip direct_chips[(INT15 - INT4) + 1];
+static struct c6x_irq_chip gpio_chips[NR_GPIO_CHIPS];
+static struct c6x_irq_chip *prio_saved_chips[NR_SYS_IRQS];
+
+#if NR_CIC_COMBINERS > 0
+static struct combiner_mask_info cic_mask_info[NR_CIC_COMBINERS];
+static struct combiner_handler_info cic_handler_info[NR_CIC_COMBINERS];
+
+static uint8_t cic_output_to_evt[NR_CIC_OUTPUTS];
+static uint8_t cic_evt_to_output[NR_CIC_IRQS];
+static struct c6x_irq_chip cic_mapped_chips[CIC_MAPLEN - NR_CIC_COMBINERS];
+static struct c6x_irq_chip *cic_saved_chips[CIC_MAPLEN - NR_CIC_COMBINERS];
+#endif
+
+/* lock protecting irq mappings */
+static spinlock_t map_lock;
+
+static void mask_direct(struct irq_data *data)
+{
+ uint16_t prio;
+
+ BUG_ON(data->irq >= NR_IRQS);
+ prio = irq_to_prio[data->irq];
+ BUG_ON(prio >= NR_SYS_IRQS);
+ and_creg(IER, ~(1 << prio));
+}
+
+static void unmask_direct(struct irq_data *data)
+{
+ uint16_t prio;
+
+ BUG_ON(data->irq >= NR_IRQS);
+ prio = irq_to_prio[data->irq];
+ BUG_ON(prio >= NR_SYS_IRQS);
+ or_creg(IER, 1 << prio);
+}
+
+static const char * const direct_chip_names[] = {
+ "direct-4",
+ "direct-5",
+ "direct-6",
+ "direct-7",
+ "direct-8",
+ "direct-9",
+ "direct-10",
+ "direct-11",
+ "direct-12",
+ "direct-13",
+ "direct-14",
+ "direct-15",
+};
+
+#if NR_CIC_COMBINERS > 0
+/*
+ * CIC IRQs mapped directly to megamodule use chip data from the megamodule
+ * combiner since the megamodule combiner will controlling masking. All other
+ * CIC IRQs use CIC combiner chip data.
+ */
+static inline int cic_mapped_irq(uint16_t irq)
+{
+ uint8_t output;
+
+ if (irq < IRQ_CIC_START || irq >= (IRQ_CIC_START + NR_CIC_IRQS))
+ return irq;
+
+ /* the CIC combined IRQs are hardwired */
+ if (irq < (IRQ_CIC_START + NR_CIC_COMBINERS))
+ return CIC_MAPBASE + (irq - IRQ_CIC_START);
+
+ /* the other CIC events may or may not be mapped to megamodule */
+ output = cic_evt_to_output[irq - IRQ_CIC_START];
+ if (output)
+ return CIC_MAPBASE + output;
+
+ return irq;
+}
+#else
+#define cic_mapped_irq(i) (i)
+#endif
+
+#define c6x_irq_to_chip(i) ((struct c6x_irq_chip *)irq_to_desc((i))->chip)
+
+static void mask_combined(struct irq_data *data)
+{
+ struct c6x_irq_chip *chip = (struct c6x_irq_chip *)data->chip;
+ int irq = data->irq;
+
+ irq = cic_mapped_irq(irq);
+ *chip->minfo->evtmask |= (1 << (irq - chip->minfo->irq_base));
+}
+
+static void unmask_combined(struct irq_data *data)
+{
+ struct c6x_irq_chip *chip = (struct c6x_irq_chip *)data->chip;
+ int irq = data->irq;
+
+ irq = cic_mapped_irq(irq);
+ *chip->minfo->evtmask &= ~(1 << (irq - chip->minfo->irq_base));
+}
+
+#define __CHIP(namestr, i, m, u) \
+ { \
+ .chip = { .name = namestr #i, \
+ .irq_mask = m, \
+ .irq_unmask = u, \
+ }, \
+ }
+
+/* Combiner chips */
+#define MEGAMOD_CHIP(i) \
+ __CHIP("combiner-", i, mask_combined, unmask_combined)
+
+static struct c6x_irq_chip megamod_chips[] = {
+ MEGAMOD_CHIP(0),
+ MEGAMOD_CHIP(1),
+ MEGAMOD_CHIP(2),
+ MEGAMOD_CHIP(3),
+};
+
+#if NR_CIC_COMBINERS > 0
+
+#define CIC_CHIP(i) \
+ __CHIP("cicombiner-", i, mask_combined, unmask_combined)
+
+static struct c6x_irq_chip cic_chips[] = {
+ CIC_CHIP(0),
+#if NR_CIC_COMBINERS > 1
+ CIC_CHIP(1),
+#endif
+#if NR_CIC_COMBINERS > 2
+ CIC_CHIP(2),
+#endif
+#if NR_CIC_COMBINERS > 3
+ CIC_CHIP(3),
+#endif
+};
+#endif /* NR_CIC_COMBINERS > 0 */
+
+/*
+ * When handling IRQs through the CIC, ack after the handler runs.
+ * For IRQs through the megamodule, ack before handler runs.
+ */
+#if NR_CIC_COMBINERS
+#define PRE_ACK (irq < IRQ_CIC_START || \
+ irq >= (IRQ_CIC_START + NR_CIC_COMBINERS))
+#else
+#define PRE_ACK 1
+#endif
+
+static void handle_combined_irq(unsigned int irq, struct irq_desc *desc)
+{
+ struct combiner_handler_info *info = irq_desc_get_handler_data(desc);
+ unsigned long events;
+ int n;
+
+ while ((events = *info->mevtflag) != 0) {
+ n = __ffs(events);
+ irq = info->irqmap[n]; /* irq to handle */
+
+ if (PRE_ACK)
+ *info->evtclr = (1 << n);
+
+ generic_handle_irq(irq);
+
+ if (!PRE_ACK)
+ *info->evtclr = (1 << n);
+ }
+}
+
+/*
+ * Setup megamodule event mappings.
+ */
+static inline void __irq_megamod_map(unsigned int src, unsigned int dst)
+{
+ uint32_t val;
+ int offset, nr_srcs;
+
+ nr_srcs = (NR_MEGAMOD_COMBINERS * 32);
+
+ BUG_ON(dst < INT4 || dst > INT15);
+ BUG_ON(src >= nr_srcs);
+
+ /* each mux register controls four outputs */
+ offset = (dst & 3) << 3;
+ val = IC_INTMUX[dst >> 2];
+ val &= ~((nr_srcs - 1) << offset);
+ val |= ((src & (nr_srcs - 1)) << offset);
+ IC_INTMUX[dst >> 2] = val;
+}
+
+/*
+ * Map a C64x+ megamodule event to a CPU core priority interrupt.
+ */
+void irq_map(unsigned int irq_src, unsigned int prio)
+{
+ struct irq_desc *desc;
+ struct c6x_irq_chip *chip, *direct;
+ unsigned long flags;
+ unsigned int prio_idx = prio - INT4;
+
+ if (prio < INT4 || prio > INT15)
+ return;
+
+ /* only map megamodule event sources here */
+ if (irq_src >= (NR_MEGAMOD_COMBINERS * 32))
+ return;
+
+ spin_lock_irqsave(&map_lock, flags);
+
+ /* make sure this IRQ is not mapped to another core IRQ */
+ if (irq_to_prio[irq_src])
+ goto out_unlock;
+
+ /* make sure no other IRQ is mapped to the requested priority */
+ if (prio_to_irq[prio] != IRQ_UNMAPPED)
+ goto out_unlock;
+
+#if NR_CIC_COMBINERS > 0
+ /* handle mapping for IRQs coming from CIC */
+ if (irq_src >= CIC_MAPBASE &&
+ irq_src < (CIC_MAPBASE + CIC_MAPLEN)) {
+ uint8_t cic_src, output = (irq_src - CIC_MAPBASE);
+ uint16_t cic_irq;
+ struct irq_data *data;
+
+ if (output >= NR_CIC_COMBINERS) {
+ cic_src = cic_output_to_evt[output];
+ if (cic_src < NR_CIC_COMBINERS)
+ goto out_unlock;
+ cic_irq = IRQ_CIC_START + cic_src;
+ } else
+ cic_irq = IRQ_CIC_START + output;
+
+ desc = irq_to_desc(cic_irq);
+ data = &desc->irq_data;
+ chip = (struct c6x_irq_chip *)data->chip;
+
+ /* mask combined CIC irq in megamodule combiner */
+ if (output < NR_CIC_COMBINERS)
+ mask_combined(data);
+
+ direct = &direct_chips[prio_idx];
+ *direct = *chip;
+ direct->chip.name = direct_chip_names[prio_idx];
+ direct->chip.irq_mask = mask_direct;
+ direct->chip.irq_unmask = unmask_direct;
+
+ prio_saved_chips[prio] = chip;
+ data->chip = (struct irq_chip *)direct;
+ irq_to_prio[cic_irq] = prio;
+ prio_to_irq[prio] = cic_irq;
+
+ __irq_megamod_map(irq_src, prio);
+
+ if (output < NR_CIC_COMBINERS)
+ irq_set_chained_handler(irq_src, handle_combined_irq);
+
+ goto out_unlock;
+ }
+#endif
+ desc = irq_to_desc(irq_src);
+ chip = (struct c6x_irq_chip *)desc->irq_data.chip;
+
+ direct = &direct_chips[prio_idx];
+ if (irq_src < NR_MEGAMOD_COMBINERS)
+ memset(&direct->chip, 0, sizeof(direct->chip));
+ else
+ *direct = *(struct c6x_irq_chip *)chip;
+ direct->chip.name = direct_chip_names[prio_idx];
+ direct->chip.irq_mask = mask_direct;
+ direct->chip.irq_unmask = unmask_direct;
+
+ prio_saved_chips[prio] = chip;
+ irq_set_chip(irq_src, (struct irq_chip *)direct);
+ irq_to_prio[irq_src] = prio;
+ prio_to_irq[prio] = irq_src;
+
+ __irq_megamod_map(irq_src, prio);
+
+ if (irq_src < NR_MEGAMOD_COMBINERS) {
+ irq_set_handler_data(irq_src, &megamod_handler_info[irq_src]);
+ irq_set_chained_handler(irq_src, handle_combined_irq);
+ }
+
+out_unlock:
+ spin_unlock_irqrestore(&map_lock, flags);
+}
+EXPORT_SYMBOL(irq_map);
+
+
+#if NR_CIC_COMBINERS > 0
+/*
+ * Setup CIC event mappings on given core.
+ */
+static inline void __irq_cic_map(int core, unsigned int src, unsigned int dst)
+{
+ uint32_t val;
+ int offset, nr_srcs;
+
+ nr_srcs = (NR_CIC_COMBINERS * 32);
+
+ /* output0 and output1 are hardwired */
+ BUG_ON(dst < 2 || dst > 15);
+ BUG_ON(src >= nr_srcs);
+
+ offset = (dst & 3) << 3;
+ val = CIC_MUX(core)[dst >> 2];
+ val &= ~(0x7f << offset);
+ val |= ((src & 0x7f) << offset);
+ CIC_MUX(core)[dst >> 2] = val;
+}
+
+/*
+ * Map a C64x+ CIC IRQ to a megamodule event
+ *
+ * Do nothing for CIC combined IRQs. They are always mapped.
+ */
+void irq_cic_map(unsigned int irq_src, unsigned int irq_dst)
+{
+ struct irq_desc *desc;
+ struct c6x_irq_chip *chip, *mmchip;
+ unsigned long flags;
+ int src, output;
+ struct combiner_handler_info *info;
+ unsigned int idx = irq_dst - (CIC_MAPBASE + NR_CIC_COMBINERS);
+
+ if (irq_dst < (CIC_MAPBASE + NR_CIC_COMBINERS) ||
+ irq_dst >= (CIC_MAPBASE + CIC_MAPLEN))
+ return;
+
+ /* only map CIC event sources */
+ if (irq_src < (IRQ_CIC_START + NR_CIC_COMBINERS) ||
+ irq_src >= (IRQ_CIC_START + NR_CIC_IRQS))
+ return;
+
+ src = irq_src - IRQ_CIC_START;
+ output = irq_dst - CIC_MAPBASE;
+
+ spin_lock_irqsave(&map_lock, flags);
+
+ /* make sure this IRQ is not already mapped */
+ if (cic_evt_to_output[src])
+ goto out_unlock;
+
+ /* make sure the requested output is not in use */
+ if (cic_output_to_evt[output])
+ goto out_unlock;
+
+ /* record the mapping */
+ cic_evt_to_output[src] = output;
+ cic_output_to_evt[output] = src;
+
+ mmchip = &megamod_chips[(CIC_MAPBASE + output) / 32];
+ info = &megamod_handler_info[(CIC_MAPBASE + output) / 32];
+ info->irqmap[(CIC_MAPBASE + output) & 31] = irq_src;
+
+ desc = irq_to_desc(irq_src);
+ cic_saved_chips[idx] = (struct c6x_irq_chip *)desc->irq_data.chip;
+
+ chip = &cic_mapped_chips[idx];
+ *chip = *(struct c6x_irq_chip *)desc->irq_data.chip;
+ chip->chip.name = mmchip->chip.name;
+ chip->minfo = mmchip->minfo;
+
+ desc->irq_data.chip = (struct irq_chip *)chip;
+
+ __irq_cic_map(get_coreid(), src, output);
+out_unlock:
+ spin_unlock_irqrestore(&map_lock, flags);
+}
+EXPORT_SYMBOL(irq_cic_map);
+
+/*
+ * Map a CIC source to a give CIC output event for a given core or TPCC
+ */
+void cic_raw_map(unsigned int src, unsigned int dst, int core)
+{
+ __irq_cic_map(core, src, dst);
+}
+EXPORT_SYMBOL(cic_raw_map);
+
+#endif /* NR_CIC_COMBINERS > 0 */
+
+void __init init_pic_c64xplus(void)
+{
+ int i, j, idx;
+ struct irq_desc *desc;
+ struct c6x_irq_chip *c6x_chip, *last_chip;
+ struct irq_chip *chip;
+ struct combiner_mask_info *minfo;
+ struct combiner_handler_info *hinfo;
+#if NR_CIC_COMBINERS > 0
+ unsigned core = get_coreid();
+#endif
+
+ spin_lock_init(&map_lock);
+
+ /* initialize chip info */
+ minfo = &megamod_mask_info[0];
+ hinfo = &megamod_handler_info[0];
+ for (i = 0; i < NR_MEGAMOD_COMBINERS; i++) {
+ minfo->irq_base = IRQ_EVT0 + (i * 32);
+ minfo->evtmask = &IC_EVTMASK[i];
+ hinfo->mevtflag = &IC_MEVTFLAG[i];
+ hinfo->evtclr = &IC_EVTCLR[i];
+ for (j = 0; j < 32; j++)
+ hinfo->irqmap[j] = minfo->irq_base + j;
+ minfo++;
+ hinfo++;
+ }
+#if NR_CIC_COMBINERS > 0
+ minfo = &cic_mask_info[0];
+ hinfo = &cic_handler_info[0];
+ for (i = 0; i < NR_CIC_COMBINERS; i++) {
+ minfo->irq_base = IRQ_CIC_START + (i * 32);
+ minfo->evtmask = &CIC_EVTMASK(core)[i];
+ hinfo->mevtflag = &CIC_MEVTFLAG(core)[i];
+ hinfo->evtclr = &CIC_EVTCLR(core)[i];
+ for (j = 0; j < 32; j++)
+ hinfo->irqmap[j] = minfo->irq_base + j;
+ minfo++;
+ hinfo++;
+ }
+#endif
+
+ /* initialize mapping arrays */
+ for (i = 0; i < NR_SYS_IRQS; i++)
+ prio_to_irq[i] = IRQ_UNMAPPED;
+ memset(&irq_to_prio[0], 0, sizeof(irq_to_prio));
+#if NR_CIC_COMBINERS > 0
+ memset(&cic_output_to_evt[0], 0, sizeof(cic_output_to_evt));
+ memset(&cic_evt_to_output[0], 0, sizeof(cic_evt_to_output));
+#endif
+
+ /* initialize megamodule combined IRQs */
+ for (i = 0; i < NR_MEGAMOD_COMBINERS; i++) {
+ desc = irq_to_desc(i);
+
+ IC_EVTMASK[i] = ~0; /* mask all events */
+ IC_EVTCLR[i] = ~0; /* clear all events */
+
+ irq_set_chip_and_handler(i, &dummy_irq_chip, handle_bad_irq);
+
+ megamod_chips[i].minfo = &megamod_mask_info[i];
+ irq_set_handler_data(i, &megamod_handler_info[i]);
+ }
+
+ /* initialize individual megamodule IRQs */
+ for (i = NR_MEGAMOD_COMBINERS; i < (NR_MEGAMOD_COMBINERS * 32); i++) {
+ chip = (struct irq_chip *)&megamod_chips[i / 32];
+ irq_set_chip_and_handler(i, chip, handle_level_irq);
+ }
+
+#if NR_CIC_COMBINERS > 0
+ /* megamodule IRQs coming from CIC cannot be used directly */
+ for (i = CIC_MAPBASE; i < (CIC_MAPBASE + CIC_MAPLEN); i++)
+ irq_set_chip_and_handler(i, &dummy_irq_chip, handle_bad_irq);
+
+ /* CIC combined IRQs are hardwired to CIC_MAPBASE in megamodule */
+ for (i = 0; i < NR_CIC_COMBINERS; i++) {
+ int irq = IRQ_CIC_START + i;
+
+ CIC_EVTMASK(core)[i] = ~0; /* mask all events */
+ CIC_EVTCLR(core)[i] = ~0; /* clear all events */
+
+ cic_chips[i].minfo = &cic_mask_info[i];
+
+ /* chip info for megamodule combiner we are wired through */
+ chip = (struct irq_chip *)&megamod_chips[(CIC_MAPBASE + i)/32];
+ hinfo = &megamod_handler_info[(CIC_MAPBASE + i) / 32];
+
+ /* redirect megamodule irq to right place */
+ hinfo->irqmap[(CIC_MAPBASE + i) & 31] = irq;
+
+ irq_set_chip(irq, chip);
+ irq_set_handler_data(irq, &cic_handler_info[i]);
+ irq_set_chained_handler(irq, handle_combined_irq);
+ }
+
+ /* initialize individual CIC IRQs */
+ for (i = NR_CIC_COMBINERS; i < (NR_CIC_COMBINERS * 32); i++) {
+ int irq = IRQ_CIC_START + i;
+
+ irq_set_chip(irq, (struct irq_chip *)&cic_chips[i / 32]);
+ irq_set_handler(irq, handle_level_irq);
+ }
+
+ /*
+ * clear again megamodule combined IRQs because spurious CIC interrupts
+ * may have occur after the CIC initialization.
+ */
+ for (i = 0; i < NR_MEGAMOD_COMBINERS; i++)
+ IC_EVTCLR[i] = ~0; /* clear all events */
+#endif
+
+ /*
+ * GPIO interrupts need separate copies of megamodule chips
+ * so gpio code can add edge triggering support.
+ */
+ last_chip = NULL;
+ idx = 0;
+ for (i = IRQ_GPIO_START; i <= IRQ_GPIO15; i++) {
+ c6x_chip = (struct c6x_irq_chip *)irq_to_desc(i)->irq_data.chip;
+ if (c6x_chip != last_chip) {
+ last_chip = c6x_chip;
+ gpio_chips[idx++] = *c6x_chip;
+ }
+ irq_to_desc(i)->irq_data.chip = &gpio_chips[idx - 1].chip;
+ }
+
+ /* map megamodule combined IRQs to low-priority core IRQs */
+ irq_map(IRQ_EVT0, INT12);
+ irq_map(IRQ_EVT1, INT13);
+ irq_map(IRQ_EVT2, INT14);
+ irq_map(IRQ_EVT3, INT15);
+}
diff --git a/drivers/platform/c6x/pll-tci648x.c b/drivers/platform/c6x/pll-tci648x.c
new file mode 100644
index 0000000..ae15659
--- /dev/null
+++ b/drivers/platform/c6x/pll-tci648x.c
@@ -0,0 +1,433 @@
+/*
+ * Clock and PLL control for TCI648x devices
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ * Contributed by: Mark Salter <msalter@redhat.com>
+ *
+ * Copied heavily from arm/mach-davinci/clock.c, so:
+ *
+ * Copyright (C) 2006-2007 Texas Instruments.
+ * Copyright (C) 2008-2009 Deep Root Systems, LLC
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#include <asm/clock.h>
+
+static LIST_HEAD(clocks);
+static DEFINE_MUTEX(clocks_mutex);
+static DEFINE_SPINLOCK(clockfw_lock);
+
+static void __clk_enable(struct clk *clk)
+{
+ if (clk->parent)
+ __clk_enable(clk->parent);
+ clk->usecount++;
+}
+
+static void __clk_disable(struct clk *clk)
+{
+ if (WARN_ON(clk->usecount == 0))
+ return;
+ --clk->usecount;
+
+ if (clk->parent)
+ __clk_disable(clk->parent);
+}
+
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+
+ if (clk == NULL || IS_ERR(clk))
+ return -EINVAL;
+
+ spin_lock_irqsave(&clockfw_lock, flags);
+ __clk_enable(clk);
+ spin_unlock_irqrestore(&clockfw_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+ unsigned long flags;
+
+ if (clk == NULL || IS_ERR(clk))
+ return;
+
+ spin_lock_irqsave(&clockfw_lock, flags);
+ __clk_disable(clk);
+ spin_unlock_irqrestore(&clockfw_lock, flags);
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ if (clk == NULL || IS_ERR(clk))
+ return -EINVAL;
+
+ return clk->rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ if (clk == NULL || IS_ERR(clk))
+ return -EINVAL;
+
+ if (clk->round_rate)
+ return clk->round_rate(clk, rate);
+
+ return clk->rate;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+/* Propagate rate to children */
+static void propagate_rate(struct clk *root)
+{
+ struct clk *clk;
+
+ list_for_each_entry(clk, &root->children, childnode) {
+ if (clk->recalc)
+ clk->rate = clk->recalc(clk);
+ propagate_rate(clk);
+ }
+}
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ if (clk == NULL || IS_ERR(clk))
+ return ret;
+
+ if (clk->set_rate)
+ ret = clk->set_rate(clk, rate);
+
+ spin_lock_irqsave(&clockfw_lock, flags);
+ if (ret == 0) {
+ if (clk->recalc)
+ clk->rate = clk->recalc(clk);
+ propagate_rate(clk);
+ }
+ spin_unlock_irqrestore(&clockfw_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ unsigned long flags;
+
+ if (clk == NULL || IS_ERR(clk))
+ return -EINVAL;
+
+ /* Cannot change parent on enabled clock */
+ if (WARN_ON(clk->usecount))
+ return -EINVAL;
+
+ mutex_lock(&clocks_mutex);
+ clk->parent = parent;
+ list_del_init(&clk->childnode);
+ list_add(&clk->childnode, &clk->parent->children);
+ mutex_unlock(&clocks_mutex);
+
+ spin_lock_irqsave(&clockfw_lock, flags);
+ if (clk->recalc)
+ clk->rate = clk->recalc(clk);
+ propagate_rate(clk);
+ spin_unlock_irqrestore(&clockfw_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
+int clk_register(struct clk *clk)
+{
+ if (clk == NULL || IS_ERR(clk))
+ return -EINVAL;
+
+ if (WARN(clk->parent && !clk->parent->rate,
+ "CLK: %s parent %s has no rate!\n",
+ clk->name, clk->parent->name))
+ return -EINVAL;
+
+ mutex_lock(&clocks_mutex);
+ list_add_tail(&clk->node, &clocks);
+ if (clk->parent)
+ list_add_tail(&clk->childnode, &clk->parent->children);
+ mutex_unlock(&clocks_mutex);
+
+ /* If rate is already set, use it */
+ if (clk->rate)
+ return 0;
+
+ /* Else, see if there is a way to calculate it */
+ if (clk->recalc)
+ clk->rate = clk->recalc(clk);
+
+ /* Otherwise, default to parent rate */
+ else if (clk->parent)
+ clk->rate = clk->parent->rate;
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_register);
+
+void clk_unregister(struct clk *clk)
+{
+ if (clk == NULL || IS_ERR(clk))
+ return;
+
+ mutex_lock(&clocks_mutex);
+ list_del(&clk->node);
+ list_del(&clk->childnode);
+ mutex_unlock(&clocks_mutex);
+}
+EXPORT_SYMBOL(clk_unregister);
+
+static unsigned long clk_sysclk_recalc(struct clk *clk)
+{
+ u32 v, plldiv = 0;
+ struct pll_data *pll;
+ unsigned long rate = clk->rate;
+
+ if (WARN_ON(!clk->parent))
+ return rate;
+
+ rate = clk->parent->rate;
+
+ /* the parent must be a PLL */
+ if (WARN_ON(!clk->parent->pll_data))
+ return rate;
+
+ pll = clk->parent->pll_data;
+
+ /* If pre-PLL, source clock is before the multiplier and divider(s) */
+ if (clk->flags & PRE_PLL)
+ rate = pll->input_rate;
+
+ if (!clk->div) {
+ pr_debug("%s: (no divider) rate = %lu KHz\n",
+ clk->name, rate / 1000);
+ return rate;
+ }
+
+ if (clk->flags & FIXED_DIV_PLL) {
+ rate /= clk->div;
+ pr_debug("%s: (fixed divide by %d) rate = %lu KHz\n",
+ clk->name, clk->div, rate / 1000);
+ return rate;
+ }
+
+ v = __raw_readl(pll->base + clk->div);
+ if (v & PLLDIV_EN)
+ plldiv = (v & PLLDIV_RATIO_MASK) + 1;
+
+ if (plldiv == 0)
+ plldiv = 1;
+
+ rate /= plldiv;
+
+ pr_debug("%s: (divide by %d) rate = %lu KHz\n",
+ clk->name, plldiv, rate / 1000);
+
+ return rate;
+}
+
+static unsigned long clk_leafclk_recalc(struct clk *clk)
+{
+ if (WARN_ON(!clk->parent))
+ return clk->rate;
+
+ pr_debug("%s: (parent %s) rate = %lu KHz\n",
+ clk->name, clk->parent->name, clk->parent->rate / 1000);
+
+ return clk->parent->rate;
+}
+
+static unsigned long clk_pllclk_recalc(struct clk *clk)
+{
+ u32 ctrl, mult = 1, prediv = 1;
+ u8 bypass;
+ struct pll_data *pll = clk->pll_data;
+ unsigned long rate = clk->rate;
+
+ if (clk->flags & FIXED_RATE_PLL)
+ return rate;
+
+ pll->base = ioremap(pll->phys_base, 1024);
+ ctrl = __raw_readl(pll->base + PLLCTL);
+ rate = pll->input_rate = clk->parent->rate;
+
+ if (ctrl & PLLCTL_PLLEN) {
+ bypass = 0;
+ mult = __raw_readl(pll->base + PLLM);
+ mult = (mult & PLLM_PLLM_MASK) + 1;
+ } else
+ bypass = 1;
+
+ if (pll->flags & PLL_HAS_PREDIV) {
+ prediv = __raw_readl(pll->base + PREDIV);
+ if (prediv & PLLDIV_EN)
+ prediv = (prediv & PLLDIV_RATIO_MASK) + 1;
+ else
+ prediv = 1;
+ }
+
+ if (!bypass) {
+ rate /= prediv;
+ rate *= mult;
+ }
+
+ pr_debug("PLL%d: input = %lu MHz [ ",
+ pll->num, clk->parent->rate / 1000000);
+ if (bypass)
+ pr_debug("bypass ");
+ if (prediv > 1)
+ pr_debug("/ %d ", prediv);
+ if (mult > 1)
+ pr_debug("* %d ", mult);
+ pr_debug("] --> %lu MHz output.\n", rate / 1000000);
+
+ return rate;
+}
+
+
+int __init c6x_clk_init(struct clk_lookup *clocks)
+{
+ struct clk_lookup *c;
+ struct clk *clk;
+ size_t num_clocks = 0;
+
+ for (c = clocks; c->clk; c++) {
+ clk = c->clk;
+
+ INIT_LIST_HEAD(&clk->node);
+ INIT_LIST_HEAD(&clk->children);
+ INIT_LIST_HEAD(&clk->childnode);
+
+ if (!clk->recalc) {
+
+ /* Check if clock is a PLL */
+ if (clk->pll_data)
+ clk->recalc = clk_pllclk_recalc;
+
+ /* Else, if it is a PLL-derived clock */
+ else if (clk->flags & CLK_PLL)
+ clk->recalc = clk_sysclk_recalc;
+
+ /* Otherwise, it is a leaf clock (PSC clock) */
+ else if (clk->parent)
+ clk->recalc = clk_leafclk_recalc;
+ }
+
+ if (clk->recalc)
+ clk->rate = clk->recalc(clk);
+
+ clk_register(clk);
+ num_clocks++;
+
+ /* Turn on clocks that Linux doesn't otherwise manage */
+ if (clk->flags & ALWAYS_ENABLED)
+ clk_enable(clk);
+ }
+
+ clkdev_add_table(clocks, num_clocks);
+
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#define CLKNAME_MAX 10 /* longest clock name */
+#define NEST_DELTA 2
+#define NEST_MAX 4
+
+static void
+dump_clock(struct seq_file *s, unsigned nest, struct clk *parent)
+{
+ char *state;
+ char buf[CLKNAME_MAX + NEST_DELTA * NEST_MAX];
+ struct clk *clk;
+ unsigned i;
+
+ if (parent->flags & CLK_PLL)
+ state = "pll";
+ else
+ state = "";
+
+ /* <nest spaces> name <pad to end> */
+ memset(buf, ' ', sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = 0;
+ i = strlen(parent->name);
+ memcpy(buf + nest, parent->name,
+ min(i, (unsigned)(sizeof(buf) - 1 - nest)));
+
+ seq_printf(s, "%s users=%2d %-3s %9ld Hz\n",
+ buf, parent->usecount, state, clk_get_rate(parent));
+ /* REVISIT show device associations too */
+
+ /* cost is now small, but not linear... */
+ list_for_each_entry(clk, &parent->children, childnode) {
+ dump_clock(s, nest + NEST_DELTA, clk);
+ }
+}
+
+static int c6x_ck_show(struct seq_file *m, void *v)
+{
+ struct clk *clk;
+
+ /*
+ * Show clock tree; We trust nonzero usecounts equate to PSC enables...
+ */
+ mutex_lock(&clocks_mutex);
+ list_for_each_entry(clk, &clocks, node)
+ if (!clk->parent)
+ dump_clock(m, 0, clk);
+ mutex_unlock(&clocks_mutex);
+
+ return 0;
+}
+
+static int c6x_ck_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, c6x_ck_show, NULL);
+}
+
+static const struct file_operations c6x_ck_operations = {
+ .open = c6x_ck_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init c6x_clk_debugfs_init(void)
+{
+ debugfs_create_file("c6x_clocks", S_IFREG | S_IRUGO, NULL, NULL,
+ &c6x_ck_operations);
+
+ return 0;
+}
+device_initcall(c6x_clk_debugfs_init);
+#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/platform/c6x/timer-tci648x.c b/drivers/platform/c6x/timer-tci648x.c
new file mode 100644
index 0000000..79cda27
--- /dev/null
+++ b/drivers/platform/c6x/timer-tci648x.c
@@ -0,0 +1,121 @@
+/*
+ * Port on Texas Instruments TMS320C6x architecture
+ *
+ * Copyright (C) 2010, 2011 Texas Instruments Incorporated
+ * Contributed by: Mark Salter (msalter@redhat.com)
+ *
+ * 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/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/percpu.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+#include <linux/bug.h>
+
+#include <asm/irq.h>
+#include <asm/timer.h>
+#include <asm/system.h>
+
+#include <mach/board.h>
+
+#ifdef CONFIG_GENERIC_CLOCKEVENTS
+
+static int next_event(unsigned long delta,
+ struct clock_event_device *evt)
+{
+ u32 timer_CNTLO = TIMER_CNTLO_REG(LINUX_TIMER_SRC);
+ u32 timer_PRDLO = TIMER_PRDLO_REG(LINUX_TIMER_SRC);
+ u32 timer_TCR = TIMER_TCR_REG(LINUX_TIMER_SRC);
+
+ TIMER_REG(timer_TCR) &= ~TIMER_B_TCR_ENAMODELO_MASK;
+ TIMER_REG(timer_PRDLO) = delta - 1;
+ TIMER_REG(timer_CNTLO) = 0;
+ TIMER_REG(timer_TCR) |= TIMER_B_TCR_ENAMODELO_ONCE;
+
+ return 0;
+}
+
+static void set_clock_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+}
+
+static void event_handler(struct clock_event_device *dev)
+{
+}
+
+static struct clock_event_device t64_clockevent_device;
+
+static irqreturn_t timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *cd = &t64_clockevent_device;
+
+ cd->event_handler(cd);
+
+ return IRQ_HANDLED;
+}
+
+int __init c6x_arch_init_clockevents(void)
+{
+ struct clock_event_device *cd = &t64_clockevent_device;
+ u64 temp;
+ u32 shift, timer_TCR, timer_TGCR;
+ u32 timer_PRDLO, timer_CNTLO, timer_EMUMGTCLKSPD;
+
+ timer_TCR = TIMER_TCR_REG(LINUX_TIMER_SRC);
+ timer_TGCR = TIMER_TGCR_REG(LINUX_TIMER_SRC);
+ timer_CNTLO = TIMER_CNTLO_REG(LINUX_TIMER_SRC);
+ timer_PRDLO = TIMER_PRDLO_REG(LINUX_TIMER_SRC);
+ timer_EMUMGTCLKSPD = TIMER_EMUMGTCLKSPD_REG(LINUX_TIMER_SRC);
+
+ /* disable timer, reset count */
+ TIMER_REG(timer_TCR) &= ~TIMER_B_TCR_ENAMODELO_MASK;
+ TIMER_REG(timer_PRDLO) = 0;
+
+ /* use internal clock and 1 cycle pulse width */
+ TIMER_REG(timer_TCR) &= ~(TIMER_B_TCR_CLKSRCLO | TIMER_B_TCR_PWIDLO_MASK);
+
+ /* dual 32-bit unchained mode */
+ TIMER_REG(timer_TGCR) &= ~TIMER_B_TGCR_TIMMODE_MASK;
+ TIMER_REG(timer_TGCR) |= (TIMER_B_TGCR_TIMLORS | TIMER_B_TGCR_TIMMODE_UD32);
+
+ cd->irq = LINUX_TIMER_IRQ;
+ cd->name = "TIMER64_EVT32_TIMER";
+ cd->features = CLOCK_EVT_FEAT_ONESHOT;
+
+ /* Calculate the min / max delta */
+ /* Find a shift value */
+ for (shift = 32; shift > 0; shift--) {
+ temp = (u64)(c6x_core_freq / TIMER_DIVISOR(LINUX_TIMER_SRC));
+ temp <<= shift;
+
+ do_div(temp, NSEC_PER_SEC);
+ if ((temp >> 32) == 0)
+ break;
+ }
+ cd->shift = shift;
+ cd->mult = (u32) temp;
+
+ cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
+ cd->min_delta_ns = clockevent_delta2ns(250, cd);
+
+ cd->rating = 200;
+ cd->set_mode = set_clock_mode;
+ cd->event_handler = event_handler;
+ cd->set_next_event = next_event;
+ cd->cpumask = cpumask_of(smp_processor_id());
+
+ clockevents_register_device(cd);
+
+ /* Set handler */
+ request_irq(cd->irq, timer_interrupt, IRQF_DISABLED | IRQF_TIMER,
+ "timer", NULL);
+
+ return 0;
+}
+
+#endif /* CONFIG_GENERIC_CLOCKEVENTS */
+
diff --git a/drivers/platform/c6x/tsc-c64xplus.c b/drivers/platform/c6x/tsc-c64xplus.c
new file mode 100644
index 0000000..437bf25
--- /dev/null
+++ b/drivers/platform/c6x/tsc-c64xplus.c
@@ -0,0 +1,53 @@
+/*
+ * Port on Texas Instruments TMS320C6x architecture
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated
+ * Contributed by: Mark Salter <msalter@redhat.com>
+ *
+ * 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/clocksource.h>
+#include <asm/timer.h>
+
+
+#ifdef CONFIG_GENERIC_TIME
+static cycle_t tsc_read(struct clocksource *cs)
+{
+ return get_cycles();
+}
+
+static struct clocksource clocksource_tsc = {
+ .name = "TSC64",
+ .rating = 300,
+ .read = tsc_read,
+ .mask = CLOCKSOURCE_MASK(64),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+int __init c6x_arch_init_clocksource(void)
+{
+ struct clocksource *cs = &clocksource_tsc;
+ u64 temp;
+ u32 shift;
+
+ /* Find a shift value */
+ for (shift = 32; shift > 0; shift--) {
+ temp = (u64) NSEC_PER_SEC << shift;
+ do_div(temp, c6x_core_freq);
+ if ((temp >> 32) == 0)
+ break;
+ }
+ cs->shift = shift;
+ cs->mult = (u32) temp;
+
+ /* write anything into TSCL to enable counting */
+ set_creg(TSCL, 0);
+
+ clocksource_register(cs);
+
+ return 0;
+}
+#endif /* CONFIG_GENERIC_TIME */
+
--
1.6.2.5
next prev parent reply other threads:[~2011-05-11 21:06 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-05-11 20:13 [PATCH] arch/c6x: new architecture port for linux Mark Salter
2011-05-11 20:13 ` [PATCH 01/16] fix default __strnlen_user macro Mark Salter
2011-05-11 20:13 ` [PATCH 02/16] fixed generic page.h for non-zero PAGE_OFFSET Mark Salter
2011-05-11 20:13 ` [PATCH 03/16] add driver for C64x+ ethernet driver Mark Salter
2011-05-11 20:13 ` [PATCH 04/16] add support for C64x+ debugger based console Mark Salter
2011-05-11 20:13 ` [PATCH 05/16] add ELF machine define for TI C6X DSPs Mark Salter
2011-05-11 20:13 ` [PATCH 06/16] add maintainers entry for C6X arch Mark Salter
2011-05-11 20:13 ` [PATCH 07/16] C6X: add toplevel configury and makefile Mark Salter
2011-05-11 20:13 ` [PATCH 08/16] C6X: add include files Mark Salter
2011-05-11 20:13 ` [PATCH 09/16] C6X: add kernel files Mark Salter
2011-05-11 20:13 ` [PATCH 10/16] C6X: add mm files Mark Salter
2011-05-11 20:13 ` [PATCH 11/16] C6X: add lib files Mark Salter
2011-05-11 20:13 ` [PATCH 12/16] C6X: add platform files Mark Salter
2011-05-11 20:14 ` Mark Salter [this message]
2011-05-11 20:14 ` [PATCH 14/16] C6X: add default configs Mark Salter
2011-05-11 20:14 ` [PATCH 15/16] Add new elf binfmt support for DSBT Mark Salter
2011-05-11 20:14 ` [PATCH 16/16] C6X: add support for DSBT binary format Mark Salter
2011-05-12 2:41 ` [04/16] add support for C64x+ debugger based console Milton Miller
2011-05-12 12:55 ` Mark Salter
2011-05-12 0:49 ` [PATCH 03/16] add driver for C64x+ ethernet driver Joe Perches
2011-05-13 15:30 ` Ben Hutchings
2011-05-13 13:55 ` [PATCH 01/16] fix default __strnlen_user macro Pavel Machek
2011-05-13 14:40 ` Mark Salter
2011-05-11 20:13 ` [PATCH] arch/c6x: new architecture port for linux Milton Miller
2011-05-12 12:34 ` Mark Salter
2011-05-11 21:34 ` Randy Dunlap
2011-05-12 0:57 ` Mark Salter
2011-05-12 1:07 ` Randy Dunlap
2011-05-12 1:16 ` Mark Salter
2011-05-21 17:10 ` Arnd Bergmann
2011-05-21 17:46 ` Geert Uytterhoeven
2011-06-16 12:54 ` Mark Salter
2011-06-16 14:02 ` Arnd Bergmann
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1305144843-5058-14-git-send-email-msalter@redhat.com \
--to=msalter@redhat.com \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox