* Re: [PATCH 00/14] Pramfs: Persistent and protected ram filesystem
From: Pavel Machek @ 2009-06-22 17:20 UTC (permalink / raw)
To: Marco Stornelli
Cc: Jamie Lokier, Linux Embedded, Linux Kernel, Linux FS Devel,
Daniel Walker
In-Reply-To: <2ea1731b0906212333r20deb71q2f021fc79bcc8a8e@mail.gmail.com>
> > How do you handle hard-links, then?
>
> Indeed hard-links are not supported :) Due to the design of this fs
> there are some limitations explained in the documentation as not
> hard-link, only private memory mapping and so on. However this
> limitations don't limit the fs itself because you must consider the
> special goal of this fs.
I did not see that in the changelog. If it is not general purpose
filesystem, it is lot less interesting.
> >> >From performance point of view:
> >>
> >> Sometimes ago I uploaded here (http://elinux.org/Pram_Fs) some benchmark
> >> results to compare the performance with and without XIP in a real
> >> embedded environment with bonnie++. You could use it as reference point.
> >
> > Well, so XIP helps. ext2 can do XIP too, IIRC. Is your performance
> > better than ext2?
> >
> > Wait... those numbers you pointed me... claim to be as slow as
> > 13MB/sec. That's very very bad. My harddrive is faster than that.
>
> As I said I did the test in a real embedded environment so to have
> comparable result you should use the same environmente with the same
> tools, with the same workload and so on.
Even on real embedded hardware you should get better than 13MB/sec
writing to _RAM_. I guess something is seriously wrong with pramfs.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply
* Re: [PATCH 06/14] Pramfs: Include files
From: Arnd Bergmann @ 2009-06-22 11:17 UTC (permalink / raw)
To: Marco Stornelli
Cc: Sam Ravnborg, Linux FS Devel, Linux Embedded, Linux Kernel
In-Reply-To: <2ea1731b0906212323h6ed8621dg22e1bc8638c26e2c@mail.gmail.com>
On Monday 22 June 2009, Marco Stornelli wrote:
> > It's still a problem. You might be creating a file system image
> > for an embedded board with a different endianess.
>
> It's not possible to create an "image" with pramfs, it's like tmpfs.
But the data is persistant, you even support using it as a root file
system, so the data has to have a way to get there. Even if you
don't do it right now, I don't see any fundamental limitation that
prevents you from creating an image on one machine and dumping it
into the nvram of another machine as part of manufacturing or testing.
> > Or even on the same machine, you could be looking at the file system contents
> > with a 32 bit process running on a 64 bit kernel.
>
> Yes, indeed the most important thing is to be sure that a 64bit kernel
> works well. I'll try to test it in this environment. If there are
> "64bit guys" to help me to test it, it'd be great.
This particular problem (__kernel_off_t on 64-bit machines) can be avoided
by just switching to __kernel_loff_t, which is 64 bit long on all machines,
while __kernel_off_t is always the register length (32 or 64 bits).
Arnd <><
^ permalink raw reply
* Re: [PATCH 00/14] Pramfs: Persistent and protected ram filesystem
From: Marco Stornelli @ 2009-06-22 6:33 UTC (permalink / raw)
To: Pavel Machek
Cc: Jamie Lokier, Linux Embedded, Linux Kernel, Linux FS Devel,
Daniel Walker
In-Reply-To: <20090621205245.GC3254@elf.ucw.cz>
2009/6/21 Pavel Machek <pavel@ucw.cz>:
>
>> >> 1. Disk-based filesystems such as ext2/ext3 were designed for optimum
>> >> performance on spinning disk media, so they implement features such
>> >> as block groups, which attempts to group inode data into a contiguous
>> >> set of data blocks to minimize disk seeking when accessing files. For
>> >> RAM there is no such concern; a file's data blocks can be scattered
>> >> throughout the media with no access speed penalty at all. So block
>> >> groups in a filesystem mounted over RAM just adds unnecessary
>> >> complexity. A better approach is to use a filesystem specifically
>> >> tailored to RAM media which does away with these disk-based features.
>> >> This increases the efficient use of space on the media, i.e. more
>> >> space is dedicated to actual file data storage and less to meta-data
>> >> needed to maintain that file data.
>> >
>> > So... what is the performance difference between ext2 and your new
>> > filesystem?
>> >
>>
>> About the "space" you can read a detailed documentation on the site:
>>
>> http://pramfs.sourceforge.net/pramfs-spec.html
>
> I do not see any numbers there. Do you think you can save significant
> memory when storing for example kernel source trees?
There aren't benchmark, but I pointed it out because if you know ext2
you can do a comparison.
>
>> In addition I can do an example of "compact" information: ext2 uses
>> directory entry objects ("dentries") to associate file names to
>> inodes,
>
> I'm not sure that on-disk directory entry == dentry.
>
>> and these dentries are located in data blocks owned by the parent
>> directory. In pramfs, directory inode's do not need to own any data
>> blocks, because all dentry information is contained within the inode's
>> themselves.
>
> How do you handle hard-links, then?
Indeed hard-links are not supported :) Due to the design of this fs
there are some limitations explained in the documentation as not
hard-link, only private memory mapping and so on. However this
limitations don't limit the fs itself because you must consider the
special goal of this fs.
>
>> >From performance point of view:
>>
>> Sometimes ago I uploaded here (http://elinux.org/Pram_Fs) some benchmark
>> results to compare the performance with and without XIP in a real
>> embedded environment with bonnie++. You could use it as reference point.
>
> Well, so XIP helps. ext2 can do XIP too, IIRC. Is your performance
> better than ext2?
>
> Wait... those numbers you pointed me... claim to be as slow as
> 13MB/sec. That's very very bad. My harddrive is faster than that.
> Pavel
>
As I said I did the test in a real embedded environment so to have
comparable result you should use the same environmente with the same
tools, with the same workload and so on.
Marco
^ permalink raw reply
* Re: [PATCH 06/14] Pramfs: Include files
From: Marco Stornelli @ 2009-06-22 6:23 UTC (permalink / raw)
To: Arnd Bergmann; +Cc: Sam Ravnborg, Linux FS Devel, Linux Embedded, Linux Kernel
In-Reply-To: <200906212222.25434.arnd@arndb.de>
2009/6/21 Arnd Bergmann <arnd@arndb.de>:
> On Sunday 21 June 2009, Marco wrote:
>> I was thinking about your comment and I think I'll use __kernel_off_t
>> for the exported headers. I know that it will differ between 32 and 64
>> bit architectures, but for this kind of fs there isn't any compatibility
>> problem at layout level. You cannot remove a chip of RAM from a board
>> 32bit little endian and attach it to a board with a cpu 64bit big
>> endian, the memory isn't a disk. Indeed, I see that tmpfs uses simply
>> "unsigned long" in the exported header file without any problems to
>> little or big endian.
>
> It's still a problem. You might be creating a file system image
> for an embedded board with a different endianess.
It's not possible to create an "image" with pramfs, it's like tmpfs.
> Or even on the same machine, you could be looking at the file system contents
> with a 32 bit process running on a 64 bit kernel.
>
> Arnd <><
>
Yes, indeed the most important thing is to be sure that a 64bit kernel
works well. I'll try to test it in this environment. If there are
"64bit guys" to help me to test it, it'd be great.
Marco
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH 2/2] Modify MMCI/PL180 to handle agressive clocking v4
From: Linus Walleij @ 2009-06-21 20:59 UTC (permalink / raw)
To: Pierre Ossman; +Cc: linux-arm-kernel, linux-embedded, Linus Walleij
This adds a few clk_enable()/clk_disable() calls to the MMCI
driver so as to handle an set_ios() request with clock
frequency set to 0 as an instruction to gate the block clock.
This also breaks out the clock setting code into its own
function to simplify the set_ios() function a bit.
Signed-off-by: Linus Walleij <linus.walleij@stericsson.com>
---
drivers/mmc/host/mmci.c | 78 ++++++++++++++++++++++++++++++++++------------
1 files changed, 57 insertions(+), 21 deletions(-)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 36875dc..d9f5c9a 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -37,8 +37,34 @@
static unsigned int fmax = 515633;
+/*
+ * This must be called with host->lock held
+ */
+static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
+{
+ u32 clk = 0;
+
+ if (desired) {
+ if (desired >= host->mclk) {
+ clk = MCI_CLK_BYPASS;
+ host->cclk = host->mclk;
+ DBG(host, "MMCI: bypass clock divider, cclk = %u Hz\n", host->cclk);
+ } else {
+ clk = host->mclk / (2 * desired) - 1;
+ if (clk >= 256)
+ clk = 255;
+ host->cclk = host->mclk / (2 * (clk + 1));
+ DBG(host, "MMCI: divide cclk to %u Hz (divide %u by %u)\n", host->cclk, host->mclk, clk);
+ }
+ if (host->hw_designer == 0x80)
+ clk |= MCI_FCEN; /* Bug fix in ST IP block */
+ clk |= MCI_CLK_ENABLE;
+ }
+ writel(clk, host->base + MMCICLOCK);
+}
+
static void
-mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
+ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
{
writel(0, host->base + MMCICOMMAND);
@@ -418,22 +444,7 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct mmci_host *host = mmc_priv(mmc);
- u32 clk = 0, pwr = 0;
-
- if (ios->clock) {
- if (ios->clock >= host->mclk) {
- clk = MCI_CLK_BYPASS;
- host->cclk = host->mclk;
- } else {
- clk = host->mclk / (2 * ios->clock) - 1;
- if (clk >= 256)
- clk = 255;
- host->cclk = host->mclk / (2 * (clk + 1));
- }
- if (host->hw_designer == 0x80)
- clk |= MCI_FCEN; /* Bug fix in ST IP block */
- clk |= MCI_CLK_ENABLE;
- }
+ u32 pwr = 0;
if (host->plat->translate_vdd)
pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
@@ -464,12 +475,23 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
}
- writel(clk, host->base + MMCICLOCK);
+ spin_lock(&host->lock);
+
+ if (ios->clock == 0) {
+ clk_disable(host->clk);
+ } else {
+ clk_enable(host->clk);
+ mmci_set_clkreg(host, ios->clock);
+ }
if (host->pwr != pwr) {
host->pwr = pwr;
+ clk_enable(host->clk);
writel(pwr, host->base + MMCIPOWER);
+ clk_disable(host->clk);
}
+
+ spin_unlock(&host->lock);
}
static const struct mmc_host_ops mmci_ops = {
@@ -615,6 +637,8 @@ static int __devinit mmci_probe(struct amba_device *dev, void *id)
host->timer.expires = jiffies + HZ;
add_timer(&host->timer);
+ /* The first IOS will turn the clock on again */
+ clk_disable(host->clk);
return 0;
irq0_free:
@@ -646,17 +670,22 @@ static int __devexit mmci_remove(struct amba_device *dev)
mmc_remove_host(mmc);
+ /* framework may have gated the block clock */
+ clk_enable(host->clk);
+
writel(0, host->base + MMCIMASK0);
writel(0, host->base + MMCIMASK1);
writel(0, host->base + MMCICOMMAND);
writel(0, host->base + MMCIDATACTRL);
+ clk_disable(host->clk);
+
free_irq(dev->irq[0], host);
free_irq(dev->irq[1], host);
iounmap(host->base);
- clk_disable(host->clk);
+
clk_put(host->clk);
mmc_free_host(mmc);
@@ -677,8 +706,11 @@ static int mmci_suspend(struct amba_device *dev, pm_message_t state)
struct mmci_host *host = mmc_priv(mmc);
ret = mmc_suspend_host(mmc, state);
- if (ret == 0)
+ if (ret == 0) {
+ clk_enable(host->clk);
writel(0, host->base + MMCIMASK0);
+ clk_disable(host->clk);
+ }
}
return ret;
@@ -692,7 +724,11 @@ static int mmci_resume(struct amba_device *dev)
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ if (ret == 0) {
+ clk_enable(host->clk);
+ writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ clk_disable(host->clk);
+ }
ret = mmc_resume_host(mmc);
}
--
1.6.2.1
^ permalink raw reply related
* [PATCH 1/2] MMC Agressive clocking framework v4
From: Linus Walleij @ 2009-06-21 20:59 UTC (permalink / raw)
To: Pierre Ossman; +Cc: linux-arm-kernel, linux-embedded, Linus Walleij
This patch modified the MMC core code to optionally call the
set_ios() operation on the driver with the clock frequency set
to 0 to gate the hardware block clock (and thus the MCI clock)
for an MMC host controller after a grace period of at least 8
MCLK cycles. It is inspired by existing clock gating code found
in the OMAP and Atmel drivers and brings this up to the host
abstraction. Gating is performed before and after any MMC
request.
It exemplifies by implementing this for the MMCI/PL180 MMC/SD
host controller, but it should be simple to switch OMAP and
Atmel over to using this instead.
Signed-off-by: Linus Walleij <linus.walleij@stericsson.com>
----
ChangeLog v3->v4:
Fixed a single spelling error.
---
drivers/mmc/core/Kconfig | 11 +++
drivers/mmc/core/core.c | 35 +++++++++
drivers/mmc/core/core.h | 2 +
drivers/mmc/core/debugfs.c | 10 ++-
drivers/mmc/core/host.c | 165 +++++++++++++++++++++++++++++++++++++++++++-
drivers/mmc/core/host.h | 3 +
include/linux/mmc/host.h | 9 +++
7 files changed, 232 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index ab37a6d..5372fc9 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -14,3 +14,14 @@ config MMC_UNSAFE_RESUME
This option is usually just for embedded systems which use
a MMC/SD card for rootfs. Most people should say N here.
+config MMC_CLKGATE
+ bool "MMC host clock gaing (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ help
+ This will attempt to agressively gate the clock to the MMC host,
+ which typically also will gate the MCI clock to the card. This
+ is done to save power due to gating off the logic and bus noise
+ when MMC is not in use. Your host driver has to support this in
+ order for it to be of any use.
+
+ If unsure, say N.
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 2649117..e9093b6 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -113,6 +113,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
if (mrq->done)
mrq->done(mrq);
+
+ mmc_clk_disable(host);
}
}
@@ -173,6 +175,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
mrq->stop->mrq = mrq;
}
}
+ mmc_clk_enable(host);
host->ops->request(host, mrq);
}
@@ -447,6 +450,38 @@ void mmc_set_clock(struct mmc_host *host, unsigned int hz)
mmc_set_ios(host);
}
+#ifdef CONFIG_MMC_CLKGATE
+/*
+ * This gates the clock by setting it to 0 Hz.
+ */
+void mmc_gate_clock(struct mmc_host *host)
+{
+ host->clk_old = host->ios.clock;
+ host->ios.clock = 0;
+ mmc_set_ios(host);
+}
+
+/*
+ * This restores the clock from gating by using the cached
+ * clock value.
+ */
+void mmc_ungate_clock(struct mmc_host *host)
+{
+ /*
+ * We should previously have gated the clock, so the clock
+ * shall be 0 here!
+ * The clock may however be 0 during intialization,
+ * when some request operations are performed before setting
+ * the frequency. When ungate is requested in that situation
+ * we just ignore the call.
+ */
+ if (host->clk_old) {
+ BUG_ON(host->ios.clock);
+ mmc_set_clock(host, host->clk_old);
+ }
+}
+#endif
+
/*
* Change the bus mode (open drain/push-pull) of a host.
*/
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index c819eff..ee27f81 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -27,6 +27,8 @@ void mmc_detach_bus(struct mmc_host *host);
void mmc_set_chip_select(struct mmc_host *host, int mode);
void mmc_set_clock(struct mmc_host *host, unsigned int hz);
+void mmc_gate_clock(struct mmc_host *host);
+void mmc_ungate_clock(struct mmc_host *host);
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 610dbd1..1a969bd 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -149,11 +149,17 @@ void mmc_add_host_debugfs(struct mmc_host *host)
host->debugfs_root = root;
if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
- goto err_ios;
+ goto err_remove_files;
+
+#ifdef CONFIG_MMC_CLKGATE
+ if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
+ root, &host->clk_delay))
+ goto err_remove_files;
+#endif
return;
-err_ios:
+err_remove_files:
debugfs_remove_recursive(root);
host->debugfs_root = NULL;
err_root:
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 5e945e6..792bbfe 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2003 Russell King, All Rights Reserved.
* Copyright (C) 2007-2008 Pierre Ossman
+ * Copyright (C) 2009 Linus Walleij
*
* 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
@@ -48,6 +49,165 @@ void mmc_unregister_host_class(void)
static DEFINE_IDR(mmc_host_idr);
static DEFINE_SPINLOCK(mmc_host_lock);
+#ifdef CONFIG_MMC_CLKGATE
+
+/*
+ * Enabling clock gating will make the core call out to the host
+ * once up and once down when it performs a request or card operation
+ * intermingled in any fashion. The driver will see this through
+ * set_ios() operations with ios.clock field set to 0 to gate
+ * (disable) the block clock, and to the old frequency to enable
+ * it again.
+ */
+static void mmc_clk_disable_delayed(struct mmc_host *host)
+{
+ unsigned long tick_ns;
+ unsigned long freq = host->ios.clock;
+ unsigned long flags;
+ int users;
+
+ if (!freq) {
+ pr_err("%s: frequency set to 0 in disable function, "
+ "this means the clock is already disabled.\n",
+ mmc_hostname(host));
+ return;
+ }
+ /*
+ * New requests may have appeared while we were scheduling,
+ * then there is no reason to delay the check before
+ * clk_disable().
+ */
+ spin_lock_irqsave(&host->clk_lock, flags);
+ users = host->clk_requests;
+ /*
+ * Delay 8 bus cycles (from MMC spec) before attempting
+ * to disable the MMCI block clock. The reference count
+ * may have gone up again after this delay due to
+ * rescheduling!
+ */
+ if (!users) {
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+ tick_ns = (1000000000 + freq - 1) / freq;
+ ndelay(host->clk_delay * tick_ns);
+ } else {
+ /* New users appeared while waiting for this work */
+ host->clk_pending_gate = false;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+ return;
+ }
+ spin_lock_irqsave(&host->clk_lock, flags);
+ if (!host->clk_requests) {
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+ /* this will set host->ios.clock to 0 */
+ mmc_gate_clock(host);
+ spin_lock_irqsave(&host->clk_lock, flags);
+#ifdef CONFIG_MMC_DEBUG
+ pr_debug("%s: disabled MCI clock\n",
+ mmc_hostname(host));
+#endif
+ }
+ host->clk_pending_gate = false;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+}
+
+/*
+ * Internal work. Work to disable the clock at some later point.
+ */
+static void mmc_clk_disable_work(struct work_struct *work)
+{
+ struct mmc_host *host = container_of(work, struct mmc_host,
+ clk_disable_work);
+
+ mmc_clk_disable_delayed(host);
+}
+
+/*
+ * mmc_clk_enable - enable hardware MCI clock
+ * @host: host with potential hardware clock to control
+ *
+ * Increase clock reference count and enable clock if first user.
+ */
+void mmc_clk_enable(struct mmc_host *host)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->clk_lock, flags);
+ /* If a gate is pending the clock is still on */
+ if (!host->clk_requests &&
+ !host->clk_pending_gate) {
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+ mmc_ungate_clock(host);
+ spin_lock_irqsave(&host->clk_lock, flags);
+#ifdef CONFIG_MMC_DEBUG
+ pr_debug("%s: enabled MCI clock\n",
+ mmc_hostname(host));
+#endif
+ }
+ host->clk_requests++;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+}
+
+/*
+ * mmc_clk_disable - disable hardware MCI clock
+ * @host: host with potential hardware clock to control
+ *
+ * Decrease clock reference count and schedule disablement of clock.
+ */
+void mmc_clk_disable(struct mmc_host *host)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->clk_lock, flags);
+ host->clk_requests--;
+ if (!host->clk_requests) {
+ host->clk_pending_gate = true;
+ schedule_work(&host->clk_disable_work);
+ }
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+}
+
+/*
+ * mmc_clk_init - set up clock gating code
+ * @host: host with potential hardware clock to control
+ */
+static inline void mmc_clk_init(struct mmc_host *host)
+{
+ host->clk_requests = 0;
+ host->clk_delay = 8; /* hold MCI clock in 8 cycles by default */
+ host->clk_pending_gate = false;
+ INIT_WORK(&host->clk_disable_work, mmc_clk_disable_work);
+ spin_lock_init(&host->clk_lock);
+}
+
+/*
+ * mmc_clk_exit - shut down clock gating code
+ * @host: host with potential hardware clock to control
+ */
+static inline void mmc_clk_exit(struct mmc_host *host)
+{
+ if (cancel_work_sync(&host->clk_disable_work))
+ mmc_clk_disable_delayed(host);
+ BUG_ON(host->clk_requests > 0);
+}
+
+#else
+inline void mmc_clk_enable(struct mmc_host *host)
+{
+}
+
+inline void mmc_clk_disable(struct mmc_host *host)
+{
+}
+
+static inline void mmc_clk_init(struct mmc_host *host)
+{
+}
+
+static inline void mmc_clk_exit(struct mmc_host *host)
+{
+}
+#endif
+
/**
* mmc_alloc_host - initialise the per-host structure.
* @extra: sizeof private data structure
@@ -80,6 +240,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host->class_dev.class = &mmc_host_class;
device_initialize(&host->class_dev);
+ mmc_clk_init(host);
+
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
@@ -156,6 +318,8 @@ void mmc_remove_host(struct mmc_host *host)
device_del(&host->class_dev);
led_trigger_unregister_simple(host->led);
+
+ mmc_clk_exit(host);
}
EXPORT_SYMBOL(mmc_remove_host);
@@ -176,4 +340,3 @@ void mmc_free_host(struct mmc_host *host)
}
EXPORT_SYMBOL(mmc_free_host);
-
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
index c2dc3d2..b36aef9 100644
--- a/drivers/mmc/core/host.h
+++ b/drivers/mmc/core/host.h
@@ -10,9 +10,12 @@
*/
#ifndef _MMC_CORE_HOST_H
#define _MMC_CORE_HOST_H
+#include <linux/mmc/host.h>
int mmc_register_host_class(void);
void mmc_unregister_host_class(void);
+void mmc_clk_enable(struct mmc_host *host);
+void mmc_clk_disable(struct mmc_host *host);
#endif
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 3e7615e..13e9534 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -119,6 +119,15 @@ struct mmc_host {
#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */
#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */
+#ifdef CONFIG_MMC_CLKGATE
+ int clk_requests; /* internal reference counter */
+ unsigned int clk_delay; /* number of MCI clk hold cycles */
+ bool clk_pending_gate; /* pending clock gating */
+ struct work_struct clk_disable_work; /* delayed clock disablement */
+ unsigned int clk_old; /* old clock value cache */
+ spinlock_t clk_lock; /* lock for clk fields */
+#endif
+
/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
unsigned short max_hw_segs; /* see blk_queue_max_hw_segments */
--
1.6.2.1
^ permalink raw reply related
* Re: [PATCH 00/14] Pramfs: Persistent and protected ram filesystem
From: Pavel Machek @ 2009-06-21 20:52 UTC (permalink / raw)
To: Marco
Cc: Jamie Lokier, Linux Embedded, Linux Kernel, Linux FS Devel,
Daniel Walker
In-Reply-To: <4A3E6F28.4090404@gmail.com>
> >> 1. Disk-based filesystems such as ext2/ext3 were designed for optimum
> >> performance on spinning disk media, so they implement features such
> >> as block groups, which attempts to group inode data into a contiguous
> >> set of data blocks to minimize disk seeking when accessing files. For
> >> RAM there is no such concern; a file's data blocks can be scattered
> >> throughout the media with no access speed penalty at all. So block
> >> groups in a filesystem mounted over RAM just adds unnecessary
> >> complexity. A better approach is to use a filesystem specifically
> >> tailored to RAM media which does away with these disk-based features.
> >> This increases the efficient use of space on the media, i.e. more
> >> space is dedicated to actual file data storage and less to meta-data
> >> needed to maintain that file data.
> >
> > So... what is the performance difference between ext2 and your new
> > filesystem?
> >
>
> About the "space" you can read a detailed documentation on the site:
>
> http://pramfs.sourceforge.net/pramfs-spec.html
I do not see any numbers there. Do you think you can save significant
memory when storing for example kernel source trees?
> In addition I can do an example of "compact" information: ext2 uses
> directory entry objects ("dentries") to associate file names to
> inodes,
I'm not sure that on-disk directory entry == dentry.
> and these dentries are located in data blocks owned by the parent
> directory. In pramfs, directory inode's do not need to own any data
> blocks, because all dentry information is contained within the inode's
> themselves.
How do you handle hard-links, then?
> >From performance point of view:
>
> Sometimes ago I uploaded here (http://elinux.org/Pram_Fs) some benchmark
> results to compare the performance with and without XIP in a real
> embedded environment with bonnie++. You could use it as reference point.
Well, so XIP helps. ext2 can do XIP too, IIRC. Is your performance
better than ext2?
Wait... those numbers you pointed me... claim to be as slow as
13MB/sec. That's very very bad. My harddrive is faster than that.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply
* Re: [PATCH 1/2] MMC Agressive clocking framework v3
From: Pierre Ossman @ 2009-06-21 20:43 UTC (permalink / raw)
To: Linus Walleij; +Cc: Linus Walleij, linux-arm-kernel, linux-embedded
In-Reply-To: <63386a3d0906211305yd09b4e6x488ac8b572af8204@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 728 bytes --]
On Sun, 21 Jun 2009 22:05:18 +0200
Linus Walleij <linus.ml.walleij@gmail.com> wrote:
> 2009/6/18 Linus Walleij <linus.walleij@stericsson.com>:
>
> > diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
> >(...)
> > + Of unsure, say N.
>
> However the rest of the patch is as I intended. I'll send a V4 pronto
> if you want that for the current merge window.
>
I'm trying to squeeze you in, but time is short so I'm afraid I can't
give you any guarantees. :/
Rgds
--
-- Pierre Ossman
WARNING: This correspondence is being monitored by the
Swedish government. Make sure your server uses encryption
for SMTP traffic and consider using PGP for end-to-end
encryption.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* Re: [PATCH 06/14] Pramfs: Include files
From: Arnd Bergmann @ 2009-06-21 20:22 UTC (permalink / raw)
To: Marco; +Cc: Sam Ravnborg, Linux FS Devel, Linux Embedded, Linux Kernel
In-Reply-To: <4A3E68CD.6080803@gmail.com>
On Sunday 21 June 2009, Marco wrote:
> I was thinking about your comment and I think I'll use __kernel_off_t
> for the exported headers. I know that it will differ between 32 and 64
> bit architectures, but for this kind of fs there isn't any compatibility
> problem at layout level. You cannot remove a chip of RAM from a board
> 32bit little endian and attach it to a board with a cpu 64bit big
> endian, the memory isn't a disk. Indeed, I see that tmpfs uses simply
> "unsigned long" in the exported header file without any problems to
> little or big endian.
It's still a problem. You might be creating a file system image
for an embedded board with a different endianess. Or even on the
same machine, you could be looking at the file system contents
with a 32 bit process running on a 64 bit kernel.
Arnd <><
^ permalink raw reply
* Re: [PATCH 1/2] MMC Agressive clocking framework v3
From: Linus Walleij @ 2009-06-21 20:05 UTC (permalink / raw)
To: Pierre Ossman; +Cc: Linus Walleij, linux-arm-kernel, linux-embedded
In-Reply-To: <1245308220-25069-1-git-send-email-linus.walleij@stericsson.com>
2009/6/18 Linus Walleij <linus.walleij@stericsson.com>:
> diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
>(...)
> + Of unsure, say N.
However the rest of the patch is as I intended. I'll send a V4 pronto
if you want that for the current merge window.
Linus Walleij
^ permalink raw reply
* Re: [PATCH 00/14] Pramfs: Persistent and protected ram filesystem
From: Marco @ 2009-06-21 17:34 UTC (permalink / raw)
To: Pavel Machek
Cc: Jamie Lokier, Linux Embedded, Linux Kernel, Linux FS Devel,
Daniel Walker
In-Reply-To: <20090621064040.GC1656@ucw.cz>
Pavel Machek wrote:
>>> Why is an entire filesystem needed, instead of simply a block driver
>>> if the ramdisk driver cannot be used?
>>>
>> >From documentation:
>>
>> "A relatively straight-forward solution is to write a simple block
>> driver for the non-volatile RAM, and mount over it any disk-based
>> filesystem such as ext2/ext3, reiserfs, etc.
>>
>> But the disk-based fs over non-volatile RAM block driver approach has
>> some drawbacks:
>>
>> 1. Disk-based filesystems such as ext2/ext3 were designed for optimum
>> performance on spinning disk media, so they implement features such
>> as block groups, which attempts to group inode data into a contiguous
>> set of data blocks to minimize disk seeking when accessing files. For
>> RAM there is no such concern; a file's data blocks can be scattered
>> throughout the media with no access speed penalty at all. So block
>> groups in a filesystem mounted over RAM just adds unnecessary
>> complexity. A better approach is to use a filesystem specifically
>> tailored to RAM media which does away with these disk-based features.
>> This increases the efficient use of space on the media, i.e. more
>> space is dedicated to actual file data storage and less to meta-data
>> needed to maintain that file data.
>
> So... what is the performance difference between ext2 and your new
> filesystem?
>
About the "space" you can read a detailed documentation on the site:
http://pramfs.sourceforge.net/pramfs-spec.html
In addition I can do an example of "compact" information: ext2 uses
directory entry objects ("dentries") to associate file names to inodes,
and these dentries are located in data blocks owned by the parent
directory. In pramfs, directory inode's do not need to own any data
blocks, because all dentry information is contained within the inode's
themselves.
From performance point of view:
Sometimes ago I uploaded here (http://elinux.org/Pram_Fs) some benchmark
results to compare the performance with and without XIP in a real
embedded environment with bonnie++. You could use it as reference point.
I hope I've answered to your question.
Regards,
Marco
^ permalink raw reply
* Re: [PATCH 06/14] Pramfs: Include files
From: Marco @ 2009-06-21 17:07 UTC (permalink / raw)
To: Arnd Bergmann; +Cc: Sam Ravnborg, Linux FS Devel, Linux Embedded, Linux Kernel
In-Reply-To: <200906140059.57362.arnd@arndb.de>
Arnd Bergmann wrote:
> On Saturday 13 June 2009, Sam Ravnborg wrote:
>>> + union {
>>> + struct {
>>> + /*
>>> + * ptr to row block of 2D block pointer array,
>>> + * file block #'s 0 to (blocksize/4)^2 - 1.
>>> + */
>>> + off_t row_block;
>> It is my understanding that we shall use: __kernel_off_t
>> in exported headers.
>
> That is a correct understanding in general, however this case is
> different, because it describes an on-disk data structure,
> not a kernel to user space interface. Here, __kernel_off_t is just
> as wrong as off_t, because it will differ between 32 and 64 bit
> architectures, making the fs layout incompatible. I'd suggest
> simply defining this as __u64.
>
> Moreover, file system layout should be described in terms of
> big-endian or little-endian types (e.g. __be64 or __le64),
> together with the right accessor functions.
>
> Arnd <><
>
I was thinking about your comment and I think I'll use __kernel_off_t
for the exported headers. I know that it will differ between 32 and 64
bit architectures, but for this kind of fs there isn't any compatibility
problem at layout level. You cannot remove a chip of RAM from a board
32bit little endian and attach it to a board with a cpu 64bit big
endian, the memory isn't a disk. Indeed, I see that tmpfs uses simply
"unsigned long" in the exported header file without any problems to
little or big endian.
Regards,
Marco
^ permalink raw reply
* Re: [PATCH 00/14] Pramfs: Persistent and protected ram filesystem
From: Pavel Machek @ 2009-06-21 6:40 UTC (permalink / raw)
To: Marco
Cc: Jamie Lokier, Linux Embedded, Linux Kernel, Linux FS Devel,
Daniel Walker
In-Reply-To: <4A34A394.5040509@gmail.com>
> > Why is an entire filesystem needed, instead of simply a block driver
> > if the ramdisk driver cannot be used?
> >
>
> >From documentation:
>
> "A relatively straight-forward solution is to write a simple block
> driver for the non-volatile RAM, and mount over it any disk-based
> filesystem such as ext2/ext3, reiserfs, etc.
>
> But the disk-based fs over non-volatile RAM block driver approach has
> some drawbacks:
>
> 1. Disk-based filesystems such as ext2/ext3 were designed for optimum
> performance on spinning disk media, so they implement features such
> as block groups, which attempts to group inode data into a contiguous
> set of data blocks to minimize disk seeking when accessing files. For
> RAM there is no such concern; a file's data blocks can be scattered
> throughout the media with no access speed penalty at all. So block
> groups in a filesystem mounted over RAM just adds unnecessary
> complexity. A better approach is to use a filesystem specifically
> tailored to RAM media which does away with these disk-based features.
> This increases the efficient use of space on the media, i.e. more
> space is dedicated to actual file data storage and less to meta-data
> needed to maintain that file data.
So... what is the performance difference between ext2 and your new
filesystem?
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
^ permalink raw reply
* Re: Representing Embedded Architectures at the Kernel Summit
From: Kumar Gala @ 2009-06-19 7:53 UTC (permalink / raw)
To: Paul Mundt
Cc: James Bottomley, ksummit-2009-discuss, linux-arch, linux-embedded
In-Reply-To: <20090619030009.GB16806@linux-sh.org>
On Jun 18, 2009, at 10:00 PM, Paul Mundt wrote:
> On Thu, Jun 18, 2009 at 09:59:20PM -0500, Kumar Gala wrote:
>>
>> On Jun 17, 2009, at 9:51 PM, Paul Mundt wrote:
>>
>>> On Wed, Jun 17, 2009 at 09:31:48AM -0500, Kumar Gala wrote:
>>>> One topic that was partially touched on was dealing with various
>>>> memories on embedded systems. We have several sram based
>>>> allocators
>>>> in the kernel for various different arch's:
>>>>
>>>> - Blackfin sram allocator arch/blackfin/mm/sram-alloc.c
>>>> - Lite5200(b) sram allocator arch/powerpc/sysdev/bestcomm/sram.c
>>>> - AVR32 sram allocator arch/avr32/mach-at32ap/at32ap700x.c and
>>>> arch/
>>>> avr32/mach-at32ap/include/mach/sram.h
>>>> - Potential davinci sram allocator
>>>>
>>>> There maybe others.
>>>>
>>> SH does this through NUMA on SRAM blocks that are anywhere from
>>> 128kB to
>>> 64MB. Some of our SMP configurations have upwards of a dozen of
>>> these
>>> blocks.
>>
>> Do you really have that much on chip memory?
>>
> Is this really a serious question? If we didn't, I would not have
> mentioned it.
It was. I'm not familiar with SH hardware so wasn't sure if there was
some other application you had for the multiple regions.
- k
^ permalink raw reply
* Re: Representing Embedded Architectures at the Kernel Summit
From: Paul Mundt @ 2009-06-19 3:00 UTC (permalink / raw)
To: Kumar Gala
Cc: James Bottomley, ksummit-2009-discuss, linux-arch, linux-embedded
In-Reply-To: <AED49645-009B-45A4-BF7B-EBF60D9C34C8@kernel.crashing.org>
On Thu, Jun 18, 2009 at 09:59:20PM -0500, Kumar Gala wrote:
>
> On Jun 17, 2009, at 9:51 PM, Paul Mundt wrote:
>
> >On Wed, Jun 17, 2009 at 09:31:48AM -0500, Kumar Gala wrote:
> >>One topic that was partially touched on was dealing with various
> >>memories on embedded systems. We have several sram based allocators
> >>in the kernel for various different arch's:
> >>
> >>- Blackfin sram allocator arch/blackfin/mm/sram-alloc.c
> >>- Lite5200(b) sram allocator arch/powerpc/sysdev/bestcomm/sram.c
> >>- AVR32 sram allocator arch/avr32/mach-at32ap/at32ap700x.c and arch/
> >>avr32/mach-at32ap/include/mach/sram.h
> >>- Potential davinci sram allocator
> >>
> >>There maybe others.
> >>
> >SH does this through NUMA on SRAM blocks that are anywhere from
> >128kB to
> >64MB. Some of our SMP configurations have upwards of a dozen of these
> >blocks.
>
> Do you really have that much on chip memory?
>
Is this really a serious question? If we didn't, I would not have
mentioned it.
^ permalink raw reply
* Re: Representing Embedded Architectures at the Kernel Summit
From: Kumar Gala @ 2009-06-19 2:59 UTC (permalink / raw)
To: Paul Mundt
Cc: James Bottomley, ksummit-2009-discuss, linux-arch, linux-embedded
In-Reply-To: <20090618025125.GA26531@linux-sh.org>
On Jun 17, 2009, at 9:51 PM, Paul Mundt wrote:
> On Wed, Jun 17, 2009 at 09:31:48AM -0500, Kumar Gala wrote:
>> One topic that was partially touched on was dealing with various
>> memories on embedded systems. We have several sram based allocators
>> in the kernel for various different arch's:
>>
>> - Blackfin sram allocator arch/blackfin/mm/sram-alloc.c
>> - Lite5200(b) sram allocator arch/powerpc/sysdev/bestcomm/sram.c
>> - AVR32 sram allocator arch/avr32/mach-at32ap/at32ap700x.c and arch/
>> avr32/mach-at32ap/include/mach/sram.h
>> - Potential davinci sram allocator
>>
>> There maybe others.
>>
> SH does this through NUMA on SRAM blocks that are anywhere from
> 128kB to
> 64MB. Some of our SMP configurations have upwards of a dozen of these
> blocks.
Do you really have that much on chip memory?
- k
^ permalink raw reply
* [PATCH 2/2] Modify MMCI/PL180 to handle agressive clocking v3
From: Linus Walleij @ 2009-06-18 6:57 UTC (permalink / raw)
To: Pierre Ossman; +Cc: linux-arm-kernel, linux-embedded, Linus Walleij
This adds a few clk_enable()/clk_disable() calls to the MMCI
driver so as to handle an set_ios() request with clock
frequency set to 0 as an instruction to gate the block clock.
This also breaks out the clock setting code into its own
function to simplify the set_ios() function a bit.
Signed-off-by: Linus Walleij <linus.walleij@stericsson.com>
---
drivers/mmc/host/mmci.c | 78 ++++++++++++++++++++++++++++++++++------------
1 files changed, 57 insertions(+), 21 deletions(-)
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 36875dc..d9f5c9a 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -37,8 +37,34 @@
static unsigned int fmax = 515633;
+/*
+ * This must be called with host->lock held
+ */
+static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
+{
+ u32 clk = 0;
+
+ if (desired) {
+ if (desired >= host->mclk) {
+ clk = MCI_CLK_BYPASS;
+ host->cclk = host->mclk;
+ DBG(host, "MMCI: bypass clock divider, cclk = %u Hz\n", host->cclk);
+ } else {
+ clk = host->mclk / (2 * desired) - 1;
+ if (clk >= 256)
+ clk = 255;
+ host->cclk = host->mclk / (2 * (clk + 1));
+ DBG(host, "MMCI: divide cclk to %u Hz (divide %u by %u)\n", host->cclk, host->mclk, clk);
+ }
+ if (host->hw_designer == 0x80)
+ clk |= MCI_FCEN; /* Bug fix in ST IP block */
+ clk |= MCI_CLK_ENABLE;
+ }
+ writel(clk, host->base + MMCICLOCK);
+}
+
static void
-mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
+ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
{
writel(0, host->base + MMCICOMMAND);
@@ -418,22 +444,7 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct mmci_host *host = mmc_priv(mmc);
- u32 clk = 0, pwr = 0;
-
- if (ios->clock) {
- if (ios->clock >= host->mclk) {
- clk = MCI_CLK_BYPASS;
- host->cclk = host->mclk;
- } else {
- clk = host->mclk / (2 * ios->clock) - 1;
- if (clk >= 256)
- clk = 255;
- host->cclk = host->mclk / (2 * (clk + 1));
- }
- if (host->hw_designer == 0x80)
- clk |= MCI_FCEN; /* Bug fix in ST IP block */
- clk |= MCI_CLK_ENABLE;
- }
+ u32 pwr = 0;
if (host->plat->translate_vdd)
pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd);
@@ -464,12 +475,23 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
}
- writel(clk, host->base + MMCICLOCK);
+ spin_lock(&host->lock);
+
+ if (ios->clock == 0) {
+ clk_disable(host->clk);
+ } else {
+ clk_enable(host->clk);
+ mmci_set_clkreg(host, ios->clock);
+ }
if (host->pwr != pwr) {
host->pwr = pwr;
+ clk_enable(host->clk);
writel(pwr, host->base + MMCIPOWER);
+ clk_disable(host->clk);
}
+
+ spin_unlock(&host->lock);
}
static const struct mmc_host_ops mmci_ops = {
@@ -615,6 +637,8 @@ static int __devinit mmci_probe(struct amba_device *dev, void *id)
host->timer.expires = jiffies + HZ;
add_timer(&host->timer);
+ /* The first IOS will turn the clock on again */
+ clk_disable(host->clk);
return 0;
irq0_free:
@@ -646,17 +670,22 @@ static int __devexit mmci_remove(struct amba_device *dev)
mmc_remove_host(mmc);
+ /* framework may have gated the block clock */
+ clk_enable(host->clk);
+
writel(0, host->base + MMCIMASK0);
writel(0, host->base + MMCIMASK1);
writel(0, host->base + MMCICOMMAND);
writel(0, host->base + MMCIDATACTRL);
+ clk_disable(host->clk);
+
free_irq(dev->irq[0], host);
free_irq(dev->irq[1], host);
iounmap(host->base);
- clk_disable(host->clk);
+
clk_put(host->clk);
mmc_free_host(mmc);
@@ -677,8 +706,11 @@ static int mmci_suspend(struct amba_device *dev, pm_message_t state)
struct mmci_host *host = mmc_priv(mmc);
ret = mmc_suspend_host(mmc, state);
- if (ret == 0)
+ if (ret == 0) {
+ clk_enable(host->clk);
writel(0, host->base + MMCIMASK0);
+ clk_disable(host->clk);
+ }
}
return ret;
@@ -692,7 +724,11 @@ static int mmci_resume(struct amba_device *dev)
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ if (ret == 0) {
+ clk_enable(host->clk);
+ writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ clk_disable(host->clk);
+ }
ret = mmc_resume_host(mmc);
}
--
1.6.2.1
^ permalink raw reply related
* [PATCH 1/2] MMC Agressive clocking framework v3
From: Linus Walleij @ 2009-06-18 6:57 UTC (permalink / raw)
To: Pierre Ossman; +Cc: linux-arm-kernel, linux-embedded, Linus Walleij
This patch modified the MMC core code to optionally call the
set_ios() operation on the driver with the clock frequency set
to 0 to gate the hardware block clock (and thus the MCI clock)
for an MMC host controller after a grace period of at least 8
MCLK cycles. It is inspired by existing clock gating code found
in the OMAP and Atmel drivers and brings this up to the host
abstraction. Gating is performed before and after any MMC
request.
It exemplifies by implementing this for the MMCI/PL180 MMC/SD
host controller, but it should be simple to switch OMAP and
Atmel over to using this instead.
Signed-off-by: Linus Walleij <linus.walleij@stericsson.com>
----
ChangeLog v2->v3:
* Swapped alloc*/remove* pair for init*/exit* in the clocking
functions.
* Remove mmc_clk_enable/disable pair in the mmc_rescan(). It's
not needed here.
* Rename struct member .clk_users to .clk_requests so as to
reflect what this is actually counting. It counts the number
of current requests that need to have the MCI clk enabled.
* Save and restore flags on all spinlocks, the code can be
called in different contexts according to lockdep.
* Spelling mistakes fixed.
---
drivers/mmc/core/Kconfig | 11 +++
drivers/mmc/core/core.c | 35 +++++++++
drivers/mmc/core/core.h | 2 +
drivers/mmc/core/debugfs.c | 10 ++-
drivers/mmc/core/host.c | 165 +++++++++++++++++++++++++++++++++++++++++++-
drivers/mmc/core/host.h | 3 +
include/linux/mmc/host.h | 9 +++
7 files changed, 232 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index ab37a6d..6ae2156 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -14,3 +14,14 @@ config MMC_UNSAFE_RESUME
This option is usually just for embedded systems which use
a MMC/SD card for rootfs. Most people should say N here.
+config MMC_CLKGATE
+ bool "MMC host clock gaing (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ help
+ This will attempt to agressively gate the clock to the MMC host,
+ which typically also will gate the MCI clock to the card. This
+ is done to save power due to gating off the logic and bus noise
+ when MMC is not in use. Your host driver has to support this in
+ order for it to be of any use.
+
+ Of unsure, say N.
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 2649117..e9093b6 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -113,6 +113,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
if (mrq->done)
mrq->done(mrq);
+
+ mmc_clk_disable(host);
}
}
@@ -173,6 +175,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
mrq->stop->mrq = mrq;
}
}
+ mmc_clk_enable(host);
host->ops->request(host, mrq);
}
@@ -447,6 +450,38 @@ void mmc_set_clock(struct mmc_host *host, unsigned int hz)
mmc_set_ios(host);
}
+#ifdef CONFIG_MMC_CLKGATE
+/*
+ * This gates the clock by setting it to 0 Hz.
+ */
+void mmc_gate_clock(struct mmc_host *host)
+{
+ host->clk_old = host->ios.clock;
+ host->ios.clock = 0;
+ mmc_set_ios(host);
+}
+
+/*
+ * This restores the clock from gating by using the cached
+ * clock value.
+ */
+void mmc_ungate_clock(struct mmc_host *host)
+{
+ /*
+ * We should previously have gated the clock, so the clock
+ * shall be 0 here!
+ * The clock may however be 0 during intialization,
+ * when some request operations are performed before setting
+ * the frequency. When ungate is requested in that situation
+ * we just ignore the call.
+ */
+ if (host->clk_old) {
+ BUG_ON(host->ios.clock);
+ mmc_set_clock(host, host->clk_old);
+ }
+}
+#endif
+
/*
* Change the bus mode (open drain/push-pull) of a host.
*/
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index c819eff..ee27f81 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -27,6 +27,8 @@ void mmc_detach_bus(struct mmc_host *host);
void mmc_set_chip_select(struct mmc_host *host, int mode);
void mmc_set_clock(struct mmc_host *host, unsigned int hz);
+void mmc_gate_clock(struct mmc_host *host);
+void mmc_ungate_clock(struct mmc_host *host);
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index 610dbd1..1a969bd 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -149,11 +149,17 @@ void mmc_add_host_debugfs(struct mmc_host *host)
host->debugfs_root = root;
if (!debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops))
- goto err_ios;
+ goto err_remove_files;
+
+#ifdef CONFIG_MMC_CLKGATE
+ if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
+ root, &host->clk_delay))
+ goto err_remove_files;
+#endif
return;
-err_ios:
+err_remove_files:
debugfs_remove_recursive(root);
host->debugfs_root = NULL;
err_root:
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 5e945e6..792bbfe 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2003 Russell King, All Rights Reserved.
* Copyright (C) 2007-2008 Pierre Ossman
+ * Copyright (C) 2009 Linus Walleij
*
* 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
@@ -48,6 +49,165 @@ void mmc_unregister_host_class(void)
static DEFINE_IDR(mmc_host_idr);
static DEFINE_SPINLOCK(mmc_host_lock);
+#ifdef CONFIG_MMC_CLKGATE
+
+/*
+ * Enabling clock gating will make the core call out to the host
+ * once up and once down when it performs a request or card operation
+ * intermingled in any fashion. The driver will see this through
+ * set_ios() operations with ios.clock field set to 0 to gate
+ * (disable) the block clock, and to the old frequency to enable
+ * it again.
+ */
+static void mmc_clk_disable_delayed(struct mmc_host *host)
+{
+ unsigned long tick_ns;
+ unsigned long freq = host->ios.clock;
+ unsigned long flags;
+ int users;
+
+ if (!freq) {
+ pr_err("%s: frequency set to 0 in disable function, "
+ "this means the clock is already disabled.\n",
+ mmc_hostname(host));
+ return;
+ }
+ /*
+ * New requests may have appeared while we were scheduling,
+ * then there is no reason to delay the check before
+ * clk_disable().
+ */
+ spin_lock_irqsave(&host->clk_lock, flags);
+ users = host->clk_requests;
+ /*
+ * Delay 8 bus cycles (from MMC spec) before attempting
+ * to disable the MMCI block clock. The reference count
+ * may have gone up again after this delay due to
+ * rescheduling!
+ */
+ if (!users) {
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+ tick_ns = (1000000000 + freq - 1) / freq;
+ ndelay(host->clk_delay * tick_ns);
+ } else {
+ /* New users appeared while waiting for this work */
+ host->clk_pending_gate = false;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+ return;
+ }
+ spin_lock_irqsave(&host->clk_lock, flags);
+ if (!host->clk_requests) {
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+ /* this will set host->ios.clock to 0 */
+ mmc_gate_clock(host);
+ spin_lock_irqsave(&host->clk_lock, flags);
+#ifdef CONFIG_MMC_DEBUG
+ pr_debug("%s: disabled MCI clock\n",
+ mmc_hostname(host));
+#endif
+ }
+ host->clk_pending_gate = false;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+}
+
+/*
+ * Internal work. Work to disable the clock at some later point.
+ */
+static void mmc_clk_disable_work(struct work_struct *work)
+{
+ struct mmc_host *host = container_of(work, struct mmc_host,
+ clk_disable_work);
+
+ mmc_clk_disable_delayed(host);
+}
+
+/*
+ * mmc_clk_enable - enable hardware MCI clock
+ * @host: host with potential hardware clock to control
+ *
+ * Increase clock reference count and enable clock if first user.
+ */
+void mmc_clk_enable(struct mmc_host *host)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->clk_lock, flags);
+ /* If a gate is pending the clock is still on */
+ if (!host->clk_requests &&
+ !host->clk_pending_gate) {
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+ mmc_ungate_clock(host);
+ spin_lock_irqsave(&host->clk_lock, flags);
+#ifdef CONFIG_MMC_DEBUG
+ pr_debug("%s: enabled MCI clock\n",
+ mmc_hostname(host));
+#endif
+ }
+ host->clk_requests++;
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+}
+
+/*
+ * mmc_clk_disable - disable hardware MCI clock
+ * @host: host with potential hardware clock to control
+ *
+ * Decrease clock reference count and schedule disablement of clock.
+ */
+void mmc_clk_disable(struct mmc_host *host)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->clk_lock, flags);
+ host->clk_requests--;
+ if (!host->clk_requests) {
+ host->clk_pending_gate = true;
+ schedule_work(&host->clk_disable_work);
+ }
+ spin_unlock_irqrestore(&host->clk_lock, flags);
+}
+
+/*
+ * mmc_clk_init - set up clock gating code
+ * @host: host with potential hardware clock to control
+ */
+static inline void mmc_clk_init(struct mmc_host *host)
+{
+ host->clk_requests = 0;
+ host->clk_delay = 8; /* hold MCI clock in 8 cycles by default */
+ host->clk_pending_gate = false;
+ INIT_WORK(&host->clk_disable_work, mmc_clk_disable_work);
+ spin_lock_init(&host->clk_lock);
+}
+
+/*
+ * mmc_clk_exit - shut down clock gating code
+ * @host: host with potential hardware clock to control
+ */
+static inline void mmc_clk_exit(struct mmc_host *host)
+{
+ if (cancel_work_sync(&host->clk_disable_work))
+ mmc_clk_disable_delayed(host);
+ BUG_ON(host->clk_requests > 0);
+}
+
+#else
+inline void mmc_clk_enable(struct mmc_host *host)
+{
+}
+
+inline void mmc_clk_disable(struct mmc_host *host)
+{
+}
+
+static inline void mmc_clk_init(struct mmc_host *host)
+{
+}
+
+static inline void mmc_clk_exit(struct mmc_host *host)
+{
+}
+#endif
+
/**
* mmc_alloc_host - initialise the per-host structure.
* @extra: sizeof private data structure
@@ -80,6 +240,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host->class_dev.class = &mmc_host_class;
device_initialize(&host->class_dev);
+ mmc_clk_init(host);
+
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
@@ -156,6 +318,8 @@ void mmc_remove_host(struct mmc_host *host)
device_del(&host->class_dev);
led_trigger_unregister_simple(host->led);
+
+ mmc_clk_exit(host);
}
EXPORT_SYMBOL(mmc_remove_host);
@@ -176,4 +340,3 @@ void mmc_free_host(struct mmc_host *host)
}
EXPORT_SYMBOL(mmc_free_host);
-
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
index c2dc3d2..b36aef9 100644
--- a/drivers/mmc/core/host.h
+++ b/drivers/mmc/core/host.h
@@ -10,9 +10,12 @@
*/
#ifndef _MMC_CORE_HOST_H
#define _MMC_CORE_HOST_H
+#include <linux/mmc/host.h>
int mmc_register_host_class(void);
void mmc_unregister_host_class(void);
+void mmc_clk_enable(struct mmc_host *host);
+void mmc_clk_disable(struct mmc_host *host);
#endif
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 3e7615e..13e9534 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -119,6 +119,15 @@ struct mmc_host {
#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */
#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */
+#ifdef CONFIG_MMC_CLKGATE
+ int clk_requests; /* internal reference counter */
+ unsigned int clk_delay; /* number of MCI clk hold cycles */
+ bool clk_pending_gate; /* pending clock gating */
+ struct work_struct clk_disable_work; /* delayed clock disablement */
+ unsigned int clk_old; /* old clock value cache */
+ spinlock_t clk_lock; /* lock for clk fields */
+#endif
+
/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
unsigned short max_hw_segs; /* see blk_queue_max_hw_segments */
--
1.6.2.1
^ permalink raw reply related
* Re: [PATCH 00/14] Pramfs: Persistent and protected ram filesystem
From: Marco Stornelli @ 2009-06-18 6:35 UTC (permalink / raw)
To: Chris Friesen; +Cc: Linux FS Devel, Linux Embedded, linux-kernel, Daniel Walker
In-Reply-To: <4A3936A0.9050709@nortel.com>
2009/6/17 Chris Friesen <cfriesen@nortel.com>:
> Marco wrote:
>> This is a second attempt at mainlining Pramfs. The first attempt was
>> back in early 2004 by MontaVista. Since then the kernel code has almost
>> been completely rewritten. So my first item on the list was porting the
>> code on a recent kernel version. After that I added the XIP support.
>>
>> Now some FAQs:
>>
>> What is the goal of this filesystem?
>>
>> Many embedded systems have a block of non-volatile RAM separate from
>> normal system memory, i.e. of which the kernel maintains no memory page
>> descriptors. For such systems it would be beneficial to mount a
>> fast read/write filesystem over this "I/O memory", for storing
>> frequently accessed data that must survive system reboots and power
>> cycles. An example usage might be system logs under /var/log, or a user
>> address book in a cell phone or PDA.
>
> Nice to see something like this submitted to mainline. We use something
> similar to provide persistent storage for crash recovery debug data for
> boards which don't have local storage.
>
> In many cases kdump can provide good information, but it's not
> sufficient for "flight recorder" type data if the kernel gets rebooted
> by a hardware mechanism (watchdog, for instance) that doesn't give a
> pre-interrupt.
I'm very happy that this fs has the approval of the kernel community. :)
>
> I'm a bit concerned about your PTE modifications on every write
> though...we do things like log every exception and scheduler operation
> to persistent memory, and I think the overhead of changing the
> protection on every write would be a killer. Instead, we make extensive
> use of checksums at various different levels so that the recovery app
> can determine which data is valid.
>
It's a trade-off between security and performance. Checksum it's a
good way to understand if a data is valid or not (indeed it's used in
this fs), but with this schema you can prevent the system to do
something wrong. This option, however, can be enabled/disabled via
kconfig.
> Also, I'd like to ensure that direct memory access to the memory area
> would be available.
What do you exactly mean with this? Can you explain to me a bit deeper?
> There are some things (like the sched/exception logging mentioned above) where we want to make accesses as fast as possible.
>
> Chris
>
>
Marco
^ permalink raw reply
* Re: [PATCH 13/14] Pramfs: Write Protection
From: Paul Mundt @ 2009-06-18 6:28 UTC (permalink / raw)
To: Marco Stornelli
Cc: Linux FS Devel, linux, linux-m68k, uclinux-dist-devel,
Linux Embedded, Linux Kernel, Daniel Walker
In-Reply-To: <2ea1731b0906172324g4654966eq22cf0bdbd3eafabf@mail.gmail.com>
On Thu, Jun 18, 2009 at 08:24:35AM +0200, Marco Stornelli wrote:
> 2009/6/18 Paul Mundt <lethal@linux-sh.org>:
> > H8300 is a nommu platform, so it has no TLB to flush. Yoshinori Sato is
> > the maintainer. Consult the MAINTAINERS file, that's what it is there for.
>
> I know the MAINTAINERS file but for h8300 there isn't an exactly
> indication (/arch/h8300 as for the other archs).
>
The file patterns are a new thing, I guess not all of the platforms were
updated. In any event:
UCLINUX FOR RENESAS H8/300 (H8300)
P: Yoshinori Sato
M: ysato@users.sourceforge.jp
W: http://uclinux-h8.sourceforge.jp/
S: Supported
Which is basically the first thing you find when looking for H8.
^ permalink raw reply
* Re: [PATCH 13/14] Pramfs: Write Protection
From: Marco Stornelli @ 2009-06-18 6:24 UTC (permalink / raw)
To: Paul Mundt, Marco, Linux FS Devel, linux, linux-m68k, uclinux-
In-Reply-To: <20090618025736.GB26531@linux-sh.org>
2009/6/18 Paul Mundt <lethal@linux-sh.org>:
> On Wed, Jun 17, 2009 at 06:58:00PM +0200, Marco wrote:
>> Jared Hulbert wrote:
>> > > Why not just fix flush_tlb_range()?
>> > >
>> > > If an arch has a flush_tlb_kernel_page() that works then it stands to
>> > > reason that the flush_tlb_kernel_range() shouldn't work with minimal
>> > > effort, no?
>> >
>> > flush_tlb_kernel_page() is a new one to me, it doesn't have any mention
>> > in Documentation/cachetlb.txt anyways.
>> >
>> > Many of the flush_tlb_kernel_range() implementations do ranged checks
>> > with tunables to determine whether it is more expensive to selectively
>> > flush vs just blowing the entire TLB away.
>> >
>> > Likewise, there is no reason why those 4 architectures can not just shove
>> > that if (end <= start + PAGE_SIZE) check in the beginning of their
>> > flush_tlb_kernel_range() and fall back on flush_tlb_kernel_page() for
>> > those cases. Hiding this in generic code is definitely not the way to go.
>>
>> Ok I'll change that function at arch level and I'll remove the ifdef,
>> I'll call only flush_tlb_kernel_page(), but I'd like to know what is
>> the opinion of the arch maintainers to do that. (Who is the maintainer
>> of H8300 arch?)
>>
> No, you should call flush_tlb_kernel_range() and just fix up the
> flush_tlb_kernel_range() calls to wrap in to flush_tlb_kernel_page(). As
> far as the kernel is concerned, flush_tlb_kernel_page() is not a standard
> interface, as it has no mention in Documentation/cachetlb.txt.
> flush_tlb_page() and flush_tlb_kernel_range() on the other hand are both
> standard interfaces.
Oops, my fault. I meant flush_tlb_kernel_range not the page version,
sorry. I agree with you.
>
> H8300 is a nommu platform, so it has no TLB to flush. Yoshinori Sato is
> the maintainer. Consult the MAINTAINERS file, that's what it is there for.
>
I know the MAINTAINERS file but for h8300 there isn't an exactly
indication (/arch/h8300 as for the other archs).
Marco
^ permalink raw reply
* Re: CDC ACM composite gadget serial not working between Linux and Windows?
From: Hans-Christian Egtvedt @ 2009-06-18 5:49 UTC (permalink / raw)
To: Daniel Ng; +Cc: linux-embedded
In-Reply-To: <547eba1b0906171909y44fc89c8q2bc94edd1eff7411@mail.gmail.com>
On Thu, 18 Jun 2009 12:09:24 +1000
Daniel Ng <daniel.ng1234@gmail.com> wrote:
<snipp>
> Please suggest an alternate list if this message is inappropriate for
> this list.
>
I would guess there is a lot more response to this on the linux-usb
mailing list:
http://vger.kernel.org/vger-lists.html#linux-usb
--
Best regards,
Hans-Christian Egtvedt
^ permalink raw reply
* Re: Representing Embedded Architectures at the Kernel Summit
From: Paul Mundt @ 2009-06-18 3:05 UTC (permalink / raw)
To: Mike Rapoport
Cc: James Bottomley, ksummit-2009-discuss, linux-arch, linux-embedded
In-Reply-To: <4A373EE6.6070201@compulab.co.il>
On Tue, Jun 16, 2009 at 09:42:46AM +0300, Mike Rapoport wrote:
> James Bottomley wrote:
> > We've got to the point where there are simply too many embedded
> > architectures to invite all the arch maintainers to the kernel summit.
> > So, this year, we thought we'd do embedded via topic driven invitations
> > instead. So what we're looking for is a proposal to discuss the issues
> > most affecting embedded architectures, or preview any features affecting
> > the main kernel which embedded architectures might need ... or any other
> > topics from embedded architectures which might need discussion or
> > debate.
>
> Another issue that affects embedded architectures is drivers initialization
> order. There are a lot of cases when you need the drivers to be initialized in
> particular order, and current initcalls scheme does not allow fine grained
> control for it.
>
Look at the early platform device abstraction, this allows specific
fine-grained control over when certain drivers are initialized, well
before the driver core is available. On SH this is how we deal with
our system timers as clockevents/clocksources while just using regular
platform devices, and having no other abstraction. You can read more in
Documentation/driver-model/platform.txt. For an example, you can grep
for earlytimer in arch/sh as well as in drivers/clocksource.
^ permalink raw reply
* Re: [PATCH 13/14] Pramfs: Write Protection
From: Paul Mundt @ 2009-06-18 2:57 UTC (permalink / raw)
To: Marco
Cc: Linux FS Devel, linux, linux-m68k, uclinux-dist-devel,
Linux Embedded, Linux Kernel, Daniel Walker
In-Reply-To: <4A392098.9060205@gmail.com>
On Wed, Jun 17, 2009 at 06:58:00PM +0200, Marco wrote:
> Jared Hulbert wrote:
> > > Why not just fix flush_tlb_range()?
> > >
> > > If an arch has a flush_tlb_kernel_page() that works then it stands to
> > > reason that the flush_tlb_kernel_range() shouldn't work with minimal
> > > effort, no?
> >
> > flush_tlb_kernel_page() is a new one to me, it doesn't have any mention
> > in Documentation/cachetlb.txt anyways.
> >
> > Many of the flush_tlb_kernel_range() implementations do ranged checks
> > with tunables to determine whether it is more expensive to selectively
> > flush vs just blowing the entire TLB away.
> >
> > Likewise, there is no reason why those 4 architectures can not just shove
> > that if (end <= start + PAGE_SIZE) check in the beginning of their
> > flush_tlb_kernel_range() and fall back on flush_tlb_kernel_page() for
> > those cases. Hiding this in generic code is definitely not the way to go.
>
> Ok I'll change that function at arch level and I'll remove the ifdef,
> I'll call only flush_tlb_kernel_page(), but I'd like to know what is
> the opinion of the arch maintainers to do that. (Who is the maintainer
> of H8300 arch?)
>
No, you should call flush_tlb_kernel_range() and just fix up the
flush_tlb_kernel_range() calls to wrap in to flush_tlb_kernel_page(). As
far as the kernel is concerned, flush_tlb_kernel_page() is not a standard
interface, as it has no mention in Documentation/cachetlb.txt.
flush_tlb_page() and flush_tlb_kernel_range() on the other hand are both
standard interfaces.
H8300 is a nommu platform, so it has no TLB to flush. Yoshinori Sato is
the maintainer. Consult the MAINTAINERS file, that's what it is there for.
^ permalink raw reply
* Re: Representing Embedded Architectures at the Kernel Summit
From: Paul Mundt @ 2009-06-18 2:51 UTC (permalink / raw)
To: Kumar Gala
Cc: James Bottomley, ksummit-2009-discuss, linux-arch, linux-embedded
In-Reply-To: <BE4622E9-611A-4D56-BB54-3C7786939CF2@kernel.crashing.org>
On Wed, Jun 17, 2009 at 09:31:48AM -0500, Kumar Gala wrote:
> One topic that was partially touched on was dealing with various
> memories on embedded systems. We have several sram based allocators
> in the kernel for various different arch's:
>
> - Blackfin sram allocator arch/blackfin/mm/sram-alloc.c
> - Lite5200(b) sram allocator arch/powerpc/sysdev/bestcomm/sram.c
> - AVR32 sram allocator arch/avr32/mach-at32ap/at32ap700x.c and arch/
> avr32/mach-at32ap/include/mach/sram.h
> - Potential davinci sram allocator
>
> There maybe others.
>
SH does this through NUMA on SRAM blocks that are anywhere from 128kB to
64MB. Some of our SMP configurations have upwards of a dozen of these
blocks.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox