* [GIT PULL] Samsung Fixes for v3.1-rc7
From: Arnd Bergmann @ 2011-09-16 16:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110916134300.GB16280@suse.de>
On Friday 16 September 2011 15:43:00 Greg KH wrote:
> > In this case, I'm not sure which following method is proper...
> > - to send 'pull request' to Greg / stable at kernel.org like bug fix during -rc
> > - to submit each patches with adding 'Cc: stable at kernel.org' again
> > - or ?
The patches are currently queued in the stable branch of the arm-soc
tree, I haven't forwarded them to Linus yet. I can easily change
the changelog before I actually send them on (if you tell me which ones
they are, or I can pull an updated set of patches to replace them.
> Are these in Linus's tree already? If so, send me the git commit ids
> and I will add them to the stable kernels.
>
> If not, wait until they are, and then send me the git commit ids, and I
> will then add them.
This is fine with me as well, but I'm trying to get more people to
send me patches that are annotated already. I forgot to ask during
the last set of bug fixes I forwarded and should probably go through
the ones that are already merged and ask the authors about backporting.
Arnd
^ permalink raw reply
* [PATCH v2 5/6] OMAP: omap_device: Create a default omap_device_pm_latency
From: Kevin Hilman @ 2011-09-16 16:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4E73387B.2030403@ti.com>
"Cousson, Benoit" <b-cousson@ti.com> writes:
> Hi Kevin,
>
> On 9/2/2011 4:25 PM, Cousson, Benoit wrote:
>> Most devices are using the same default omap_device_pm_latency structure
>> during device built. In order to avoid the duplication of the same
>> structure everywhere, add a default structure that will be used if
>> the device does not have an explicit one.
>
> [...]
>
>> - od->pm_lats = pm_lats;
>> - od->pm_lats_cnt = pm_lats_cnt;
>> + if (pm_lats) {
>> + od->pm_lats = pm_lats;
>> + od->pm_lats_cnt = pm_lats_cnt;
>> + } else {
>> + od->pm_lats = omap_default_latency;
>> + od->pm_lats_cnt = ARRAY_SIZE(omap_default_latency);
>> + }
>
> Here is the fix for that part. I did the easy version :-). Splitting
> the structure in two parts will indeed require more work.
The kmemdup() method is fine with me.
> I updated the for_3.2/1_omap_device_cleanup branch.
Thanks, I'll pick these into my branch (which is now renamed to
for_3.2/omap_device-cleanup)
Kevin
> Regards,
> Benoit
>
> ---
> From ad163000568dd61dee441473d0a576d84152da9e Mon Sep 17 00:00:00 2001
> From: Benoit Cousson <b-cousson@ti.com>
> Date: Tue, 9 Aug 2011 16:54:19 +0200
> Subject: [PATCH v3 5/6] OMAP: omap_device: Create a default omap_device_pm_latency
>
> Most devices are using the same default omap_device_pm_latency structure
> during device built. In order to avoid the duplication of the same
> structure everywhere, add a default structure that will be used if
> the device does not have an explicit one.
>
> Next patches will clean the duplicated structures.
>
> Signed-off-by: Benoit Cousson <b-cousson@ti.com>
> Cc: Kevin Hilman <khilman@ti.com>
> Cc: Paul Walmsley <paul@pwsan.com>
> ---
> arch/arm/plat-omap/omap_device.c | 19 ++++++++++++++++++-
> 1 files changed, 18 insertions(+), 1 deletions(-)
>
> diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c
> index 7b2cf62..54bbe7b 100644
> --- a/arch/arm/plat-omap/omap_device.c
> +++ b/arch/arm/plat-omap/omap_device.c
> @@ -97,6 +97,14 @@
> static int omap_device_register(struct platform_device *pdev);
> static int omap_early_device_register(struct platform_device *pdev);
>
> +static struct omap_device_pm_latency omap_default_latency[] = {
> + {
> + .deactivate_func = omap_device_idle_hwmods,
> + .activate_func = omap_device_enable_hwmods,
> + .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST,
> + }
> +};
> +
> /* Private functions */
>
> /**
> @@ -510,8 +518,17 @@ struct platform_device *omap_device_build_ss(const char *pdev_name, int pdev_id,
> if (ret)
> goto odbs_exit3;
>
> - od->pm_lats = pm_lats;
> + if (!pm_lats) {
> + pm_lats = omap_default_latency;
> + pm_lats_cnt = ARRAY_SIZE(omap_default_latency);
> + }
> +
> od->pm_lats_cnt = pm_lats_cnt;
> + od->pm_lats = kmemdup(pm_lats,
> + sizeof(struct omap_device_pm_latency) * pm_lats_cnt,
> + GFP_KERNEL);
> + if (!od->pm_lats)
> + goto odbs_exit3;
>
> for (i = 0; i < oh_cnt; i++) {
> hwmods[i]->od = od;
^ permalink raw reply
* [PATCH 3/7] ASoC: edb93xx: convert to use snd_soc_register_card()
From: Alexander Sverdlin @ 2011-09-16 16:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <0fef3fd00e8cdb8a606f4a81ad4e7f2b7014621c.1315732452.git.mika.westerberg@iki.fi>
Hello, Mika!
Have you tried the driver on reference boards?
For me it doesn't work any more. Boot messages are ok, as before, but
alsa open produces such messages:
Jan 1 00:32:19 IPCUn user.err kernel: asoc: can't open platform
ep93xx-pcm-audio
and fails.
I'll try to investigate further...
On Sun, 2011-09-11 at 12:28 +0300, Mika Westerberg wrote:
> Current method for machine driver to register with the ASoC core is to use
> snd_soc_register_card() instead of creating a "soc-audio" platform device.
>
> Signed-off-by: Mika Westerberg <mika.westerberg@iki.fi>
> Cc: Alexander Sverdlin <subaparts@yandex.ru>
> ---
> sound/soc/ep93xx/edb93xx.c | 60 ++++++++++++++++++++++++-------------------
> 1 files changed, 33 insertions(+), 27 deletions(-)
>
> diff --git a/sound/soc/ep93xx/edb93xx.c b/sound/soc/ep93xx/edb93xx.c
> index d3aa151..0134d4e 100644
> --- a/sound/soc/ep93xx/edb93xx.c
> +++ b/sound/soc/ep93xx/edb93xx.c
> @@ -28,12 +28,6 @@
> #include <mach/hardware.h>
> #include "ep93xx-pcm.h"
>
> -#define edb93xx_has_audio() (machine_is_edb9301() || \
> - machine_is_edb9302() || \
> - machine_is_edb9302a() || \
> - machine_is_edb9307a() || \
> - machine_is_edb9315a())
> -
> static int edb93xx_hw_params(struct snd_pcm_substream *substream,
> struct snd_pcm_hw_params *params)
> {
> @@ -94,49 +88,61 @@ static struct snd_soc_card snd_soc_edb93xx = {
> .num_links = 1,
> };
>
> -static struct platform_device *edb93xx_snd_device;
> -
> -static int __init edb93xx_init(void)
> +static int __devinit edb93xx_probe(struct platform_device *pdev)
> {
> + struct snd_soc_card *card = &snd_soc_edb93xx;
> int ret;
>
> - if (!edb93xx_has_audio())
> - return -ENODEV;
> -
> ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97,
> EP93XX_SYSCON_I2SCLKDIV_ORIDE |
> EP93XX_SYSCON_I2SCLKDIV_SPOL);
> if (ret)
> return ret;
>
> - edb93xx_snd_device = platform_device_alloc("soc-audio", -1);
> - if (!edb93xx_snd_device) {
> - ret = -ENOMEM;
> - goto free_i2s;
> + card->dev = &pdev->dev;
> +
> + ret = snd_soc_register_card(card);
> + if (ret) {
> + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
> + ret);
> + ep93xx_i2s_release();
> }
>
> - platform_set_drvdata(edb93xx_snd_device, &snd_soc_edb93xx);
> - ret = platform_device_add(edb93xx_snd_device);
> - if (ret)
> - goto device_put;
> + return ret;
> +}
>
> - return 0;
> +static int __devexit edb93xx_remove(struct platform_device *pdev)
> +{
> + struct snd_soc_card *card = platform_get_drvdata(pdev);
>
> -device_put:
> - platform_device_put(edb93xx_snd_device);
> -free_i2s:
> + snd_soc_unregister_card(card);
> ep93xx_i2s_release();
> - return ret;
> +
> + return 0;
> +}
> +
> +static struct platform_driver edb93xx_driver = {
> + .driver = {
> + .name = "edb93xx-audio",
> + .owner = THIS_MODULE,
> + },
> + .probe = edb93xx_probe,
> + .remove = __devexit_p(edb93xx_remove),
> +};
> +
> +static int __init edb93xx_init(void)
> +{
> + return platform_driver_register(&edb93xx_driver);
> }
> module_init(edb93xx_init);
>
> static void __exit edb93xx_exit(void)
> {
> - platform_device_unregister(edb93xx_snd_device);
> - ep93xx_i2s_release();
> + platform_driver_unregister(&edb93xx_driver);
> }
> module_exit(edb93xx_exit);
>
> MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
> MODULE_DESCRIPTION("ALSA SoC EDB93xx");
> MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:edb93xx-audio");
^ permalink raw reply
* [RFC v3] arm: Add platform bus driver for memory mapped virtio device
From: Pawel Moll @ 2011-09-16 16:47 UTC (permalink / raw)
To: linux-arm-kernel
This patch, based on virtio PCI driver, adds support for memory
mapped (platform) virtio device. This should allow environments
like qemu to use virtio-based block & network devices.
One can define and register a platform device which resources
will describe memory mapped control registers and "mailbox"
interrupt. Such device can be also instantiated using the Device
Tree node with compatible property equal "virtio,mmio".
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Anthony Liguori <aliguori@us.ibm.com>
Cc: Michael S.Tsirkin <mst@redhat.com>
Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
This version incorporates all the discussed changes. I've also changed
the name (again ;-) to virtio-mmio, as this seems to be more meaningful
and not as generic as -platform.
The config_ops->get_features is ready for >32 bits API and the Host is
notified about the Used Ring alignment when the queue is being
activated. The queue size, once the virtio API is in place, may be
set writing to QUEUE_NUM register. I've also left a lot of spare
space in the registers map, so we should be able to accommodate future
extensions. One thing left TODO is the magic value check - I'll add
this on next opportunity.
Now, if it looks sane, next week I'd like to start working with Peter
Maydell (subject to his availability :-) to get the qemu bits in place
and test is all (just to make things clear - I _did_ test the original
design as a block device, but it was our proprietary emulation
environment, not qemu).
Do you think this patch could get into 3.2?
Cheers!
Pawel
drivers/virtio/Kconfig | 11 +
drivers/virtio/Makefile | 1 +
drivers/virtio/virtio_mmio.c | 431 ++++++++++++++++++++++++++++++++++++++++++
include/linux/virtio_mmio.h | 71 +++++++
4 files changed, 514 insertions(+), 0 deletions(-)
create mode 100644 drivers/virtio/virtio_mmio.c
create mode 100644 include/linux/virtio_mmio.h
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 57e493b..816ed08 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -35,4 +35,15 @@ config VIRTIO_BALLOON
If unsure, say M.
+ config VIRTIO_MMIO
+ tristate "Platform bus driver for memory mapped virtio devices (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ select VIRTIO
+ select VIRTIO_RING
+ ---help---
+ This drivers provides support for memory mapped virtio
+ platform device driver.
+
+ If unsure, say N.
+
endmenu
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index 6738c44..5a4c63c 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_VIRTIO) += virtio.o
obj-$(CONFIG_VIRTIO_RING) += virtio_ring.o
+obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
new file mode 100644
index 0000000..354f2f2
--- /dev/null
+++ b/drivers/virtio/virtio_mmio.c
@@ -0,0 +1,431 @@
+/*
+ * Virtio memory mapped device driver
+ *
+ * Copyright 2011, ARM Ltd.
+ *
+ * This module allows virtio devices to be used over a virtual, memory mapped
+ * platform device.
+ *
+ * Registers layout (all 32-bit wide):
+ *
+ * offset name description
+ * ------ ---------------- -----------------
+ *
+ * 0x000 MagicValue Magic value "virt" (0x74726976 LE)
+ * 0x004 DeviceID Virtio device ID
+ * 0x008 VendorID Virtio vendor ID
+ *
+ * 0x010 HostFeatures Features supported by the host
+ * 0x014 HostFeaturesSel Set of host features to access via HostFeatures
+ * 0x020 GuestFeatures Features activated by the guest
+ * 0x024 GuestFeaturesSel Set of activated features to set via GuestFeatures
+ *
+ * 0x030 QueueSel Queue selector
+ * 0x034 QueueNum Queue size for the currently selected queue
+ * 0x038 QueueAlign Used Ring alignment for the current queue
+ * 0x03c QueuePFN PFN for the currently selected queue
+
+ * 0x050 QueueNotify Queue notifier
+ * 0x060 InterruptACK Interrupt acknowledge register
+ * 0x070 Status Device status register
+ *
+ * 0x100+ Device-specific configuration space
+ *
+ * Based on Virtio PCI driver by Anthony Liguori, copyright IBM Corp. 2007
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <linux/highmem.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_mmio.h>
+#include <linux/virtio_ring.h>
+
+
+
+#define to_virtio_mmio_device(_plat_dev) \
+ container_of(_plat_dev, struct virtio_mmio_device, vdev)
+
+struct virtio_mmio_device {
+ struct virtio_device vdev;
+ struct platform_device *pdev;
+
+ void __iomem *base;
+
+ /* a list of queues so we can dispatch IRQs */
+ spinlock_t lock;
+ struct list_head virtqueues;
+};
+
+struct virtio_mmio_vq_info {
+ /* the actual virtqueue */
+ struct virtqueue *vq;
+
+ /* the number of entries in the queue */
+ int num;
+
+ /* the index of the queue */
+ int queue_index;
+
+ /* the virtual address of the ring queue */
+ void *queue;
+
+ /* the list node for the virtqueues list */
+ struct list_head node;
+};
+
+
+
+/* Configuration interface */
+
+static u32 vm_get_features(struct virtio_device *vdev)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+
+ /* TODO: Features > 32 bits */
+ writel(0, vm_dev->base + VIRTIO_MMIO_HOST_FEATURES_SEL);
+
+ return readl(vm_dev->base + VIRTIO_MMIO_HOST_FEATURES);
+}
+
+static void vm_finalize_features(struct virtio_device *vdev)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+ int i;
+
+ /* Give virtio_ring a chance to accept features. */
+ vring_transport_features(vdev);
+
+ for (i = 0; i < ARRAY_SIZE(vdev->features); i++) {
+ writel(i, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES_SET);
+ writel(vdev->features[i],
+ vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES);
+ }
+}
+
+static void vm_get(struct virtio_device *vdev, unsigned offset,
+ void *buf, unsigned len)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+ u8 *ptr = buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ ptr[i] = readb(vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
+}
+
+static void vm_set(struct virtio_device *vdev, unsigned offset,
+ const void *buf, unsigned len)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+ const u8 *ptr = buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ writeb(ptr[i], vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
+}
+
+static u8 vm_get_status(struct virtio_device *vdev)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+
+ return readl(vm_dev->base + VIRTIO_MMIO_STATUS) & 0xff;
+}
+
+static void vm_set_status(struct virtio_device *vdev, u8 status)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+
+ /* We should never be setting status to 0. */
+ BUG_ON(status == 0);
+
+ writel(status, vm_dev->base + VIRTIO_MMIO_STATUS);
+}
+
+static void vm_reset(struct virtio_device *vdev)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+
+ /* 0 status means a reset. */
+ writel(0, vm_dev->base + VIRTIO_MMIO_STATUS);
+}
+
+
+
+/* Transport interface */
+
+/* the notify function used when creating a virt queue */
+static void vm_notify(struct virtqueue *vq)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev);
+ struct virtio_mmio_vq_info *info = vq->priv;
+
+ /* We write the queue's selector into the notification register to
+ * signal the other end */
+ writel(info->queue_index, vm_dev->base + VIRTIO_MMIO_QUEUE_NOTIFY);
+}
+
+/* Notify all virtqueues on an interrupt. */
+static irqreturn_t vm_interrupt(int irq, void *opaque)
+{
+ struct virtio_mmio_device *vm_dev = opaque;
+ struct virtio_mmio_vq_info *info;
+ irqreturn_t ret = IRQ_NONE;
+ unsigned long flags;
+
+ writel(1, vm_dev->base + VIRTIO_MMIO_INTERRUPT_ACK);
+
+ spin_lock_irqsave(&vm_dev->lock, flags);
+ list_for_each_entry(info, &vm_dev->virtqueues, node) {
+ if (vring_interrupt(irq, info->vq) == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+ spin_unlock_irqrestore(&vm_dev->lock, flags);
+
+ return ret;
+}
+
+
+
+static void vm_del_vq(struct virtqueue *vq)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev);
+ struct virtio_mmio_vq_info *info = vq->priv;
+ unsigned long flags, size;
+
+ spin_lock_irqsave(&vm_dev->lock, flags);
+ list_del(&info->node);
+ spin_unlock_irqrestore(&vm_dev->lock, flags);
+
+ vring_del_virtqueue(vq);
+
+ /* Select and deactivate the queue */
+ writel(info->queue_index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL);
+ writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
+
+ size = PAGE_ALIGN(vring_size(info->num, VIRTIO_MMIO_VRING_ALIGN));
+ free_pages_exact(info->queue, size);
+ kfree(info);
+}
+
+static void vm_del_vqs(struct virtio_device *vdev)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+ struct virtqueue *vq, *n;
+
+ list_for_each_entry_safe(vq, n, &vdev->vqs, list)
+ vm_del_vq(vq);
+
+ free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev);
+}
+
+
+
+static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index,
+ void (*callback)(struct virtqueue *vq),
+ const char *name)
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+ struct virtio_mmio_vq_info *info;
+ struct virtqueue *vq;
+ unsigned long flags, size;
+ u16 num;
+ int err;
+
+ /* Select the queue we're interested in */
+ writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL);
+
+ /* Check if queue is either not available or already active. */
+ num = readl(vm_dev->base + VIRTIO_MMIO_QUEUE_NUM);
+ if (!num || readl(vm_dev->base + VIRTIO_MMIO_QUEUE_PFN)) {
+ err = -ENOENT;
+ goto error_available;
+ }
+
+ /* Allocate and fill out our structure the represents an active
+ * queue */
+ info = kmalloc(sizeof(struct virtio_mmio_vq_info), GFP_KERNEL);
+ if (!info) {
+ err = -ENOMEM;
+ goto error_kmalloc;
+ }
+
+ info->queue_index = index;
+ info->num = num;
+
+ size = PAGE_ALIGN(vring_size(num, VIRTIO_MMIO_VRING_ALIGN));
+ info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
+ if (info->queue == NULL) {
+ err = -ENOMEM;
+ goto error_alloc_pages;
+ }
+
+ /* Activate the queue */
+ writel(VIRTIO_MMIO_VRING_ALIGN,
+ vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN);
+ writel(virt_to_phys(info->queue) >> VIRTIO_MMIO_QUEUE_ADDR_SHIFT,
+ vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
+
+ /* Create the vring */
+ vq = vring_new_virtqueue(info->num, VIRTIO_MMIO_VRING_ALIGN,
+ vdev, info->queue, vm_notify, callback, name);
+ if (!vq) {
+ err = -ENOMEM;
+ goto error_new_virtqueue;
+ }
+
+ vq->priv = info;
+ info->vq = vq;
+
+ spin_lock_irqsave(&vm_dev->lock, flags);
+ list_add(&info->node, &vm_dev->virtqueues);
+ spin_unlock_irqrestore(&vm_dev->lock, flags);
+
+ return vq;
+
+error_new_virtqueue:
+ writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
+ free_pages_exact(info->queue, size);
+error_alloc_pages:
+ kfree(info);
+error_kmalloc:
+error_available:
+ return ERR_PTR(err);
+}
+
+static int vm_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+ struct virtqueue *vqs[],
+ vq_callback_t *callbacks[],
+ const char *names[])
+{
+ struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
+ unsigned int irq = platform_get_irq(vm_dev->pdev, 0);
+ int i, err;
+
+ err = request_irq(irq, vm_interrupt, IRQF_SHARED,
+ dev_name(&vdev->dev), vm_dev);
+ if (err)
+ return err;
+
+ for (i = 0; i < nvqs; ++i) {
+ vqs[i] = vm_setup_vq(vdev, i, callbacks[i], names[i]);
+ if (IS_ERR(vqs[i])) {
+ vm_del_vqs(vdev);
+ free_irq(irq, vm_dev);
+ return PTR_ERR(vqs[i]);
+ }
+ }
+
+ return 0;
+}
+
+
+
+static struct virtio_config_ops virtio_mmio_config_ops = {
+ .get = vm_get,
+ .set = vm_set,
+ .get_status = vm_get_status,
+ .set_status = vm_set_status,
+ .reset = vm_reset,
+ .find_vqs = vm_find_vqs,
+ .del_vqs = vm_del_vqs,
+ .get_features = vm_get_features,
+ .finalize_features = vm_finalize_features,
+};
+
+
+
+/* Platform device */
+
+static int __devinit virtio_mmio_probe(struct platform_device *pdev)
+{
+ struct virtio_mmio_device *vm_dev;
+ struct resource *mem;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ return -EINVAL;
+
+ if (!devm_request_mem_region(&pdev->dev, mem->start,
+ resource_size(mem), pdev->name))
+ return -EBUSY;
+
+ vm_dev = devm_kzalloc(&pdev->dev, sizeof(struct virtio_mmio_device),
+ GFP_KERNEL);
+ if (!vm_dev)
+ return -ENOMEM;
+
+ vm_dev->vdev.dev.parent = &pdev->dev;
+ vm_dev->vdev.config = &virtio_mmio_config_ops;
+ vm_dev->pdev = pdev;
+ INIT_LIST_HEAD(&vm_dev->virtqueues);
+ spin_lock_init(&vm_dev->lock);
+
+ vm_dev->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (vm_dev->base == NULL)
+ return -EFAULT;
+
+ /* TODO: check magic value (VIRTIO_MMIO_MAGIC_VALUE) */
+
+ vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID);
+ vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID);
+
+ platform_set_drvdata(pdev, vm_dev);
+
+ return register_virtio_device(&vm_dev->vdev);
+}
+
+static int __devexit virtio_mmio_remove(struct platform_device *pdev)
+{
+ struct virtio_mmio_device *vm_dev = platform_get_drvdata(pdev);
+
+ unregister_virtio_device(&vm_dev->vdev);
+
+ return 0;
+}
+
+
+
+/* Platform driver */
+
+static struct of_device_id virtio_mmio_match[] = {
+ { .compatible = "virtio,mmio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, virtio_mmio_match);
+
+static struct platform_driver virtio_mmio_driver = {
+ .probe = virtio_mmio_probe,
+ .remove = __devexit_p(virtio_mmio_remove),
+ .driver = {
+ .name = "virtio-mmio",
+ .owner = THIS_MODULE,
+ .of_match_table = virtio_mmio_match,
+ },
+};
+
+static int __init virtio_mmio_init(void)
+{
+ return platform_driver_register(&virtio_mmio_driver);
+}
+
+static void __exit virtio_mmio_exit(void)
+{
+ platform_driver_unregister(&virtio_mmio_driver);
+}
+
+module_init(virtio_mmio_init);
+module_exit(virtio_mmio_exit);
+
+MODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>");
+MODULE_DESCRIPTION("Platform bus driver for memory mapped virtio devices");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/virtio_mmio.h b/include/linux/virtio_mmio.h
new file mode 100644
index 0000000..2a57908
--- /dev/null
+++ b/include/linux/virtio_mmio.h
@@ -0,0 +1,71 @@
+/*
+ * Virtio platform device driver
+ *
+ * Copyright 2011, ARM Ltd.
+ *
+ * Based on Virtio PCI driver by Anthony Liguori, copyright IBM Corp. 2007
+ *
+ * This header is BSD licensed so anyone can use the definitions to implement
+ * compatible drivers/servers.
+ */
+
+#ifndef _LINUX_VIRTIO_MMIO_H
+#define _LINUX_VIRTIO_MMIO_H
+
+/* Magic value ("virt" string == 0x74726976 Little Endian word */
+#define VIRTIO_MMIO_MAGIC_VALUE 0x000
+
+/* Virtio device ID */
+#define VIRTIO_MMIO_DEVICE_ID 0x004
+
+/* Virtio vendor ID */
+#define VIRTIO_MMIO_VENDOR_ID 0x008
+
+/* Bitmask of the features supported by the host (32 bits per set) */
+#define VIRTIO_MMIO_HOST_FEATURES 0x010
+
+/* Host features set selector */
+#define VIRTIO_MMIO_HOST_FEATURES_SEL 0x014
+
+/* Bitmask of features activated by the guest (32 bits per set) */
+#define VIRTIO_MMIO_GUEST_FEATURES 0x020
+
+/* Activated features set selector */
+#define VIRTIO_MMIO_GUEST_FEATURES_SET 0x024
+
+/* Queue selector */
+#define VIRTIO_MMIO_QUEUE_SEL 0x030
+
+/* Queue size for the currently selected queue */
+#define VIRTIO_MMIO_QUEUE_NUM 0x034
+
+/* Used Ring ailgnment for the currently selected queue */
+#define VIRTIO_MMIO_QUEUE_ALIGN 0x038
+
+/* PFN for the currently selected queue */
+#define VIRTIO_MMIO_QUEUE_PFN 0x03c
+
+/* Queue notifier */
+#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050
+
+/* Interrupt acknowledge */
+#define VIRTIO_MMIO_INTERRUPT_ACK 0x060
+
+/* Device status register */
+#define VIRTIO_MMIO_STATUS 0x070
+
+/* The config space is defined by each driver as
+ * the per-driver configuration space */
+#define VIRTIO_MMIO_CONFIG 0x100
+
+
+
+/* How many bits to shift physical queue address written to QUEUE_PFN.
+ * 12 is historical, and due to 4kb page size. */
+#define VIRTIO_MMIO_QUEUE_ADDR_SHIFT 12
+
+/* The alignment to use between consumer and producer parts of vring.
+ * Page size again. */
+#define VIRTIO_MMIO_VRING_ALIGN 4096
+
+#endif
--
1.6.3.3
^ permalink raw reply related
* [PATCH] OMAP3: PM: fix UART handling when using no_console_suspend
From: Kevin Hilman @ 2011-09-16 16:47 UTC (permalink / raw)
To: linux-arm-kernel
During the idle/suspend path, we expect the console lock to be held so
that no console output is done during/after the UARTs are idled.
However, when using the no_console_suspend argument on the
command-line, the console driver does not take the console lock. This
allows the possibility of console activity after UARTs have been
disabled.
To fix, update the current is_suspending() to also check the
console_suspend_enabled flag.
Reported-by: Abhilash Koyamangalath <abhilash.kv@ti.com>
Tested-by: Abhilash Koyamangalath <abhilash.kv@ti.com>
Signed-off-by: Kevin Hilman <khilman@ti.com>
---
This patch will be included in my PM cleanup series for v3.2
(branch: for_3.2/pm-cleanup)
arch/arm/mach-omap2/pm34xx.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index 7255d9b..c8cbd00 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -55,7 +55,7 @@
static suspend_state_t suspend_state = PM_SUSPEND_ON;
static inline bool is_suspending(void)
{
- return (suspend_state != PM_SUSPEND_ON);
+ return (suspend_state != PM_SUSPEND_ON) && console_suspend_enabled;
}
#else
static inline bool is_suspending(void)
--
1.7.6
^ permalink raw reply related
* [GIT PULL] OMAP PM cleanup for v3.2
From: Kevin Hilman @ 2011-09-16 16:50 UTC (permalink / raw)
To: linux-arm-kernel
Tony,
Please pull the following PM cleanups for v3.2
Kevin
The following changes since commit fcb8ce5cfe30ca9ca5c9a79cdfe26d1993e65e0c:
Linux 3.1-rc3 (2011-08-22 11:42:53 -0700)
are available in the git repository at:
git://gitorious.org/khilman/linux-omap-pm.git for_3.2/pm-cleanup
Johan Hovold (2):
OMAP2+: PM: fix typos in misc. comment and error messages
OMAP2+: PM: clean up error messages: replace printk with pr_err.
Kevin Hilman (1):
OMAP3: PM: fix UART handling when using no_console_suspend
Michael Jones (1):
OMAP2: PM debug: remove leftover debug code
arch/arm/mach-omap2/pm.c | 22 ++++++++++------------
arch/arm/mach-omap2/pm24xx.c | 27 ---------------------------
arch/arm/mach-omap2/pm34xx.c | 2 +-
3 files changed, 11 insertions(+), 40 deletions(-)
^ permalink raw reply
* [RFC v3] arm: Add platform bus driver for memory mapped virtio device
From: Anthony Liguori @ 2011-09-16 17:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316191623-3835-1-git-send-email-pawel.moll@arm.com>
On 09/16/2011 11:47 AM, Pawel Moll wrote:
> This patch, based on virtio PCI driver, adds support for memory
> mapped (platform) virtio device. This should allow environments
> like qemu to use virtio-based block& network devices.
>
> One can define and register a platform device which resources
> will describe memory mapped control registers and "mailbox"
> interrupt. Such device can be also instantiated using the Device
> Tree node with compatible property equal "virtio,mmio".
>
> Cc: Rusty Russell<rusty@rustcorp.com.au>
> Cc: Anthony Liguori<aliguori@us.ibm.com>
> Cc: Michael S.Tsirkin<mst@redhat.com>
> Signed-off-by: Pawel Moll<pawel.moll@arm.com>
Have you written a specification for this device?
Rusty maintains a formal spec for all virtio devices at:
http://ozlabs.org/~rusty/virtio-spec/
The spec should be written before merging the code to make sure that there
aren't future compatibility problems.
Regards,
Anthony Liguori
> ---
>
> This version incorporates all the discussed changes. I've also changed
> the name (again ;-) to virtio-mmio, as this seems to be more meaningful
> and not as generic as -platform.
>
> The config_ops->get_features is ready for>32 bits API and the Host is
> notified about the Used Ring alignment when the queue is being
> activated. The queue size, once the virtio API is in place, may be
> set writing to QUEUE_NUM register. I've also left a lot of spare
> space in the registers map, so we should be able to accommodate future
> extensions. One thing left TODO is the magic value check - I'll add
> this on next opportunity.
>
> Now, if it looks sane, next week I'd like to start working with Peter
> Maydell (subject to his availability :-) to get the qemu bits in place
> and test is all (just to make things clear - I _did_ test the original
> design as a block device, but it was our proprietary emulation
> environment, not qemu).
>
> Do you think this patch could get into 3.2?
>
> Cheers!
>
> Pawel
>
>
> drivers/virtio/Kconfig | 11 +
> drivers/virtio/Makefile | 1 +
> drivers/virtio/virtio_mmio.c | 431 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/virtio_mmio.h | 71 +++++++
> 4 files changed, 514 insertions(+), 0 deletions(-)
> create mode 100644 drivers/virtio/virtio_mmio.c
> create mode 100644 include/linux/virtio_mmio.h
>
> diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
> index 57e493b..816ed08 100644
> --- a/drivers/virtio/Kconfig
> +++ b/drivers/virtio/Kconfig
> @@ -35,4 +35,15 @@ config VIRTIO_BALLOON
>
> If unsure, say M.
>
> + config VIRTIO_MMIO
> + tristate "Platform bus driver for memory mapped virtio devices (EXPERIMENTAL)"
> + depends on EXPERIMENTAL
> + select VIRTIO
> + select VIRTIO_RING
> + ---help---
> + This drivers provides support for memory mapped virtio
> + platform device driver.
> +
> + If unsure, say N.
> +
> endmenu
> diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
> index 6738c44..5a4c63c 100644
> --- a/drivers/virtio/Makefile
> +++ b/drivers/virtio/Makefile
> @@ -1,4 +1,5 @@
> obj-$(CONFIG_VIRTIO) += virtio.o
> obj-$(CONFIG_VIRTIO_RING) += virtio_ring.o
> +obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o
> obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
> obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
> diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
> new file mode 100644
> index 0000000..354f2f2
> --- /dev/null
> +++ b/drivers/virtio/virtio_mmio.c
> @@ -0,0 +1,431 @@
> +/*
> + * Virtio memory mapped device driver
> + *
> + * Copyright 2011, ARM Ltd.
> + *
> + * This module allows virtio devices to be used over a virtual, memory mapped
> + * platform device.
> + *
> + * Registers layout (all 32-bit wide):
> + *
> + * offset name description
> + * ------ ---------------- -----------------
> + *
> + * 0x000 MagicValue Magic value "virt" (0x74726976 LE)
> + * 0x004 DeviceID Virtio device ID
> + * 0x008 VendorID Virtio vendor ID
> + *
> + * 0x010 HostFeatures Features supported by the host
> + * 0x014 HostFeaturesSel Set of host features to access via HostFeatures
> + * 0x020 GuestFeatures Features activated by the guest
> + * 0x024 GuestFeaturesSel Set of activated features to set via GuestFeatures
> + *
> + * 0x030 QueueSel Queue selector
> + * 0x034 QueueNum Queue size for the currently selected queue
> + * 0x038 QueueAlign Used Ring alignment for the current queue
> + * 0x03c QueuePFN PFN for the currently selected queue
> +
> + * 0x050 QueueNotify Queue notifier
> + * 0x060 InterruptACK Interrupt acknowledge register
> + * 0x070 Status Device status register
> + *
> + * 0x100+ Device-specific configuration space
> + *
> + * Based on Virtio PCI driver by Anthony Liguori, copyright IBM Corp. 2007
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + */
> +
> +#include<linux/highmem.h>
> +#include<linux/interrupt.h>
> +#include<linux/io.h>
> +#include<linux/list.h>
> +#include<linux/module.h>
> +#include<linux/platform_device.h>
> +#include<linux/slab.h>
> +#include<linux/spinlock.h>
> +#include<linux/virtio.h>
> +#include<linux/virtio_config.h>
> +#include<linux/virtio_mmio.h>
> +#include<linux/virtio_ring.h>
> +
> +
> +
> +#define to_virtio_mmio_device(_plat_dev) \
> + container_of(_plat_dev, struct virtio_mmio_device, vdev)
> +
> +struct virtio_mmio_device {
> + struct virtio_device vdev;
> + struct platform_device *pdev;
> +
> + void __iomem *base;
> +
> + /* a list of queues so we can dispatch IRQs */
> + spinlock_t lock;
> + struct list_head virtqueues;
> +};
> +
> +struct virtio_mmio_vq_info {
> + /* the actual virtqueue */
> + struct virtqueue *vq;
> +
> + /* the number of entries in the queue */
> + int num;
> +
> + /* the index of the queue */
> + int queue_index;
> +
> + /* the virtual address of the ring queue */
> + void *queue;
> +
> + /* the list node for the virtqueues list */
> + struct list_head node;
> +};
> +
> +
> +
> +/* Configuration interface */
> +
> +static u32 vm_get_features(struct virtio_device *vdev)
> +{
> + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
> +
> + /* TODO: Features> 32 bits */
> + writel(0, vm_dev->base + VIRTIO_MMIO_HOST_FEATURES_SEL);
> +
> + return readl(vm_dev->base + VIRTIO_MMIO_HOST_FEATURES);
> +}
> +
> +static void vm_finalize_features(struct virtio_device *vdev)
> +{
> + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
> + int i;
> +
> + /* Give virtio_ring a chance to accept features. */
> + vring_transport_features(vdev);
> +
> + for (i = 0; i< ARRAY_SIZE(vdev->features); i++) {
> + writel(i, vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES_SET);
> + writel(vdev->features[i],
> + vm_dev->base + VIRTIO_MMIO_GUEST_FEATURES);
> + }
> +}
> +
> +static void vm_get(struct virtio_device *vdev, unsigned offset,
> + void *buf, unsigned len)
> +{
> + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
> + u8 *ptr = buf;
> + int i;
> +
> + for (i = 0; i< len; i++)
> + ptr[i] = readb(vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
> +}
> +
> +static void vm_set(struct virtio_device *vdev, unsigned offset,
> + const void *buf, unsigned len)
> +{
> + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
> + const u8 *ptr = buf;
> + int i;
> +
> + for (i = 0; i< len; i++)
> + writeb(ptr[i], vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
> +}
> +
> +static u8 vm_get_status(struct virtio_device *vdev)
> +{
> + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
> +
> + return readl(vm_dev->base + VIRTIO_MMIO_STATUS)& 0xff;
> +}
> +
> +static void vm_set_status(struct virtio_device *vdev, u8 status)
> +{
> + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
> +
> + /* We should never be setting status to 0. */
> + BUG_ON(status == 0);
> +
> + writel(status, vm_dev->base + VIRTIO_MMIO_STATUS);
> +}
> +
> +static void vm_reset(struct virtio_device *vdev)
> +{
> + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
> +
> + /* 0 status means a reset. */
> + writel(0, vm_dev->base + VIRTIO_MMIO_STATUS);
> +}
> +
> +
> +
> +/* Transport interface */
> +
> +/* the notify function used when creating a virt queue */
> +static void vm_notify(struct virtqueue *vq)
> +{
> + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev);
> + struct virtio_mmio_vq_info *info = vq->priv;
> +
> + /* We write the queue's selector into the notification register to
> + * signal the other end */
> + writel(info->queue_index, vm_dev->base + VIRTIO_MMIO_QUEUE_NOTIFY);
> +}
> +
> +/* Notify all virtqueues on an interrupt. */
> +static irqreturn_t vm_interrupt(int irq, void *opaque)
> +{
> + struct virtio_mmio_device *vm_dev = opaque;
> + struct virtio_mmio_vq_info *info;
> + irqreturn_t ret = IRQ_NONE;
> + unsigned long flags;
> +
> + writel(1, vm_dev->base + VIRTIO_MMIO_INTERRUPT_ACK);
> +
> + spin_lock_irqsave(&vm_dev->lock, flags);
> + list_for_each_entry(info,&vm_dev->virtqueues, node) {
> + if (vring_interrupt(irq, info->vq) == IRQ_HANDLED)
> + ret = IRQ_HANDLED;
> + }
> + spin_unlock_irqrestore(&vm_dev->lock, flags);
> +
> + return ret;
> +}
> +
> +
> +
> +static void vm_del_vq(struct virtqueue *vq)
> +{
> + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev);
> + struct virtio_mmio_vq_info *info = vq->priv;
> + unsigned long flags, size;
> +
> + spin_lock_irqsave(&vm_dev->lock, flags);
> + list_del(&info->node);
> + spin_unlock_irqrestore(&vm_dev->lock, flags);
> +
> + vring_del_virtqueue(vq);
> +
> + /* Select and deactivate the queue */
> + writel(info->queue_index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL);
> + writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
> +
> + size = PAGE_ALIGN(vring_size(info->num, VIRTIO_MMIO_VRING_ALIGN));
> + free_pages_exact(info->queue, size);
> + kfree(info);
> +}
> +
> +static void vm_del_vqs(struct virtio_device *vdev)
> +{
> + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
> + struct virtqueue *vq, *n;
> +
> + list_for_each_entry_safe(vq, n,&vdev->vqs, list)
> + vm_del_vq(vq);
> +
> + free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev);
> +}
> +
> +
> +
> +static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index,
> + void (*callback)(struct virtqueue *vq),
> + const char *name)
> +{
> + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
> + struct virtio_mmio_vq_info *info;
> + struct virtqueue *vq;
> + unsigned long flags, size;
> + u16 num;
> + int err;
> +
> + /* Select the queue we're interested in */
> + writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL);
> +
> + /* Check if queue is either not available or already active. */
> + num = readl(vm_dev->base + VIRTIO_MMIO_QUEUE_NUM);
> + if (!num || readl(vm_dev->base + VIRTIO_MMIO_QUEUE_PFN)) {
> + err = -ENOENT;
> + goto error_available;
> + }
> +
> + /* Allocate and fill out our structure the represents an active
> + * queue */
> + info = kmalloc(sizeof(struct virtio_mmio_vq_info), GFP_KERNEL);
> + if (!info) {
> + err = -ENOMEM;
> + goto error_kmalloc;
> + }
> +
> + info->queue_index = index;
> + info->num = num;
> +
> + size = PAGE_ALIGN(vring_size(num, VIRTIO_MMIO_VRING_ALIGN));
> + info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
> + if (info->queue == NULL) {
> + err = -ENOMEM;
> + goto error_alloc_pages;
> + }
> +
> + /* Activate the queue */
> + writel(VIRTIO_MMIO_VRING_ALIGN,
> + vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN);
> + writel(virt_to_phys(info->queue)>> VIRTIO_MMIO_QUEUE_ADDR_SHIFT,
> + vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
> +
> + /* Create the vring */
> + vq = vring_new_virtqueue(info->num, VIRTIO_MMIO_VRING_ALIGN,
> + vdev, info->queue, vm_notify, callback, name);
> + if (!vq) {
> + err = -ENOMEM;
> + goto error_new_virtqueue;
> + }
> +
> + vq->priv = info;
> + info->vq = vq;
> +
> + spin_lock_irqsave(&vm_dev->lock, flags);
> + list_add(&info->node,&vm_dev->virtqueues);
> + spin_unlock_irqrestore(&vm_dev->lock, flags);
> +
> + return vq;
> +
> +error_new_virtqueue:
> + writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN);
> + free_pages_exact(info->queue, size);
> +error_alloc_pages:
> + kfree(info);
> +error_kmalloc:
> +error_available:
> + return ERR_PTR(err);
> +}
> +
> +static int vm_find_vqs(struct virtio_device *vdev, unsigned nvqs,
> + struct virtqueue *vqs[],
> + vq_callback_t *callbacks[],
> + const char *names[])
> +{
> + struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
> + unsigned int irq = platform_get_irq(vm_dev->pdev, 0);
> + int i, err;
> +
> + err = request_irq(irq, vm_interrupt, IRQF_SHARED,
> + dev_name(&vdev->dev), vm_dev);
> + if (err)
> + return err;
> +
> + for (i = 0; i< nvqs; ++i) {
> + vqs[i] = vm_setup_vq(vdev, i, callbacks[i], names[i]);
> + if (IS_ERR(vqs[i])) {
> + vm_del_vqs(vdev);
> + free_irq(irq, vm_dev);
> + return PTR_ERR(vqs[i]);
> + }
> + }
> +
> + return 0;
> +}
> +
> +
> +
> +static struct virtio_config_ops virtio_mmio_config_ops = {
> + .get = vm_get,
> + .set = vm_set,
> + .get_status = vm_get_status,
> + .set_status = vm_set_status,
> + .reset = vm_reset,
> + .find_vqs = vm_find_vqs,
> + .del_vqs = vm_del_vqs,
> + .get_features = vm_get_features,
> + .finalize_features = vm_finalize_features,
> +};
> +
> +
> +
> +/* Platform device */
> +
> +static int __devinit virtio_mmio_probe(struct platform_device *pdev)
> +{
> + struct virtio_mmio_device *vm_dev;
> + struct resource *mem;
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem)
> + return -EINVAL;
> +
> + if (!devm_request_mem_region(&pdev->dev, mem->start,
> + resource_size(mem), pdev->name))
> + return -EBUSY;
> +
> + vm_dev = devm_kzalloc(&pdev->dev, sizeof(struct virtio_mmio_device),
> + GFP_KERNEL);
> + if (!vm_dev)
> + return -ENOMEM;
> +
> + vm_dev->vdev.dev.parent =&pdev->dev;
> + vm_dev->vdev.config =&virtio_mmio_config_ops;
> + vm_dev->pdev = pdev;
> + INIT_LIST_HEAD(&vm_dev->virtqueues);
> + spin_lock_init(&vm_dev->lock);
> +
> + vm_dev->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
> + if (vm_dev->base == NULL)
> + return -EFAULT;
> +
> + /* TODO: check magic value (VIRTIO_MMIO_MAGIC_VALUE) */
> +
> + vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID);
> + vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID);
> +
> + platform_set_drvdata(pdev, vm_dev);
> +
> + return register_virtio_device(&vm_dev->vdev);
> +}
> +
> +static int __devexit virtio_mmio_remove(struct platform_device *pdev)
> +{
> + struct virtio_mmio_device *vm_dev = platform_get_drvdata(pdev);
> +
> + unregister_virtio_device(&vm_dev->vdev);
> +
> + return 0;
> +}
> +
> +
> +
> +/* Platform driver */
> +
> +static struct of_device_id virtio_mmio_match[] = {
> + { .compatible = "virtio,mmio", },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, virtio_mmio_match);
> +
> +static struct platform_driver virtio_mmio_driver = {
> + .probe = virtio_mmio_probe,
> + .remove = __devexit_p(virtio_mmio_remove),
> + .driver = {
> + .name = "virtio-mmio",
> + .owner = THIS_MODULE,
> + .of_match_table = virtio_mmio_match,
> + },
> +};
> +
> +static int __init virtio_mmio_init(void)
> +{
> + return platform_driver_register(&virtio_mmio_driver);
> +}
> +
> +static void __exit virtio_mmio_exit(void)
> +{
> + platform_driver_unregister(&virtio_mmio_driver);
> +}
> +
> +module_init(virtio_mmio_init);
> +module_exit(virtio_mmio_exit);
> +
> +MODULE_AUTHOR("Pawel Moll<pawel.moll@arm.com>");
> +MODULE_DESCRIPTION("Platform bus driver for memory mapped virtio devices");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/virtio_mmio.h b/include/linux/virtio_mmio.h
> new file mode 100644
> index 0000000..2a57908
> --- /dev/null
> +++ b/include/linux/virtio_mmio.h
> @@ -0,0 +1,71 @@
> +/*
> + * Virtio platform device driver
> + *
> + * Copyright 2011, ARM Ltd.
> + *
> + * Based on Virtio PCI driver by Anthony Liguori, copyright IBM Corp. 2007
> + *
> + * This header is BSD licensed so anyone can use the definitions to implement
> + * compatible drivers/servers.
> + */
> +
> +#ifndef _LINUX_VIRTIO_MMIO_H
> +#define _LINUX_VIRTIO_MMIO_H
> +
> +/* Magic value ("virt" string == 0x74726976 Little Endian word */
> +#define VIRTIO_MMIO_MAGIC_VALUE 0x000
> +
> +/* Virtio device ID */
> +#define VIRTIO_MMIO_DEVICE_ID 0x004
> +
> +/* Virtio vendor ID */
> +#define VIRTIO_MMIO_VENDOR_ID 0x008
> +
> +/* Bitmask of the features supported by the host (32 bits per set) */
> +#define VIRTIO_MMIO_HOST_FEATURES 0x010
> +
> +/* Host features set selector */
> +#define VIRTIO_MMIO_HOST_FEATURES_SEL 0x014
> +
> +/* Bitmask of features activated by the guest (32 bits per set) */
> +#define VIRTIO_MMIO_GUEST_FEATURES 0x020
> +
> +/* Activated features set selector */
> +#define VIRTIO_MMIO_GUEST_FEATURES_SET 0x024
> +
> +/* Queue selector */
> +#define VIRTIO_MMIO_QUEUE_SEL 0x030
> +
> +/* Queue size for the currently selected queue */
> +#define VIRTIO_MMIO_QUEUE_NUM 0x034
> +
> +/* Used Ring ailgnment for the currently selected queue */
> +#define VIRTIO_MMIO_QUEUE_ALIGN 0x038
> +
> +/* PFN for the currently selected queue */
> +#define VIRTIO_MMIO_QUEUE_PFN 0x03c
> +
> +/* Queue notifier */
> +#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050
> +
> +/* Interrupt acknowledge */
> +#define VIRTIO_MMIO_INTERRUPT_ACK 0x060
> +
> +/* Device status register */
> +#define VIRTIO_MMIO_STATUS 0x070
> +
> +/* The config space is defined by each driver as
> + * the per-driver configuration space */
> +#define VIRTIO_MMIO_CONFIG 0x100
> +
> +
> +
> +/* How many bits to shift physical queue address written to QUEUE_PFN.
> + * 12 is historical, and due to 4kb page size. */
> +#define VIRTIO_MMIO_QUEUE_ADDR_SHIFT 12
> +
> +/* The alignment to use between consumer and producer parts of vring.
> + * Page size again. */
> +#define VIRTIO_MMIO_VRING_ALIGN 4096
> +
> +#endif
^ permalink raw reply
* [PATCH 6/6] ARM: zImage: prevent constant copy+rebuild of lib1funcs.S
From: Stephen Boyd @ 2011-09-16 17:20 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110916071554.GB22533@n2100.arm.linux.org.uk>
On 9/16/2011 12:15 AM, Russell King - ARM Linux wrote:
> On Thu, Sep 15, 2011 at 08:58:39PM -0700, Stephen Boyd wrote:
>>
>> I already posted a very similar patch and put it into Russell's patch
>> tracker. Can't you just use that instead?
> You did, but then there followed quite a lengthy discussion about it
> so I avoided applying it. The discussion didn't seem to really reach
> any conclusion wrt the patch so I didn't know what to do about it.
I was under the impression that the kbuild stuff would come later and so
it was safe to apply the patch now (apparently it was because that
happened here).
I guess next time I'll push harder.
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
^ permalink raw reply
* [PATCH 01/19] ARM: sort the meminfo array earlier
From: Stephen Boyd @ 2011-09-16 17:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316156850-31013-2-git-send-email-nico@fluxnic.net>
On 9/16/2011 12:07 AM, Nicolas Pitre wrote:
> @@ -875,6 +876,12 @@ static struct machine_desc * __init setup_machine_tags(unsigned int nr)
> return mdesc;
> }
>
> +static int __init meminfo_cmp(const void *_a, const void *_b)
> +{
> + const struct membank *a = _a, *b = _b;
> + long cmp = bank_pfn_start(a) - bank_pfn_start(b);
> + return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
This looks like:
return clamp(bank_pfn_start(a) - bank_pfn_start(b), -1, 1);
> +}
>
> void __init setup_arch(char **cmdline_p)
> {
> @@ -903,6 +910,7 @@ void __init setup_arch(char **cmdline_p)
>
> parse_early_param();
>
> + sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL);
> sanity_check_meminfo();
> arm_memblock_init(&meminfo, mdesc);
>
> diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
> index 91bca355cd..7080f10cc2 100644
> --- a/arch/arm/mm/init.c
> +++ b/arch/arm/mm/init.c
> @@ -318,19 +317,10 @@ static void arm_memory_present(void)
> }
> #endif
>
> -static int __init meminfo_cmp(const void *_a, const void *_b)
> -{
> - const struct membank *a = _a, *b = _b;
> - long cmp = bank_pfn_start(a) - bank_pfn_start(b);
> - return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
> -}
> -
> void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc)
> {
> int i;
>
> - sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL);
> -
Why not call sanity_check_meminfo() here?
> memblock_init();
> for (i = 0; i < mi->nr_banks; i++)
> memblock_add(mi->bank[i].start, mi->bank[i].size);
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
^ permalink raw reply
* [PATCH 20/25] OMAP4: PM: Add L2X0 cache lowpower support
From: Kevin Hilman @ 2011-09-16 17:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1315144466-9395-21-git-send-email-santosh.shilimkar@ti.com>
Santosh Shilimkar <santosh.shilimkar@ti.com> writes:
> When MPUSS hits off-mode e, L2 cache is lost. This patch adds L2X0
^^^
extra 'e' ?
> necessary maintenance operations and context restoration in the
> low power code.
>
> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
> Cc: Kevin Hilman <khilman@ti.com>
[...]
> @@ -135,6 +138,33 @@ static void scu_pwrst_prepare(unsigned int cpu_id, unsigned int cpu_state)
> __raw_writel(scu_pwr_st, pm_info->scu_sar_addr);
> }
>
> +/*
> + * Store the CPU cluster state for L2X0 low power operations.
> + */
> +static void l2x0_pwrst_prepare(unsigned int cpu_id, unsigned int save_state)
> +{
> + struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
> +
> + __raw_writel(save_state, pm_info->l2x0_sar_addr);
> +}
> +
> +/*
> + * Save the L2X0 AUXCTRL and POR value to SAR memory. Its used to
> + * in every restore MPUSS OFF path.
> + */
> +static void save_l2x0_context(void)
> +{
> +#ifdef CONFIG_CACHE_L2X0
> + u32 val;
> + void __iomem *l2x0_base = omap4_get_l2cache_base();
> +
> + val = __raw_readl(l2x0_base + L2X0_AUX_CTRL);
> + __raw_writel(val, sar_base + L2X0_AUXCTRL_OFFSET);
> + val = __raw_readl(l2x0_base + L2X0_PREFETCH_CTRL);
> + __raw_writel(val, sar_base + L2X0_PREFETCH_CTRL_OFFSET);
> +#endif
nit: (c.f. '#ifdefs are ugly' in Documentatin/SubmittingPatches)
This should probably be more like
#ifdef CONFIG_CACHE_L2X0
static void save_l2x0_context(void)
{
/* real function */
}
#else
static void save_l2x0_context(void) {}
#endif
Kevin
^ permalink raw reply
* [PATCH v2 0/3] ARM: imx: Add cpuidle for imx platforms
From: Robert Lee @ 2011-09-16 17:27 UTC (permalink / raw)
To: linux-arm-kernel
This patch series adds a common imx cpuidle driver, some common
mach-mx5 level cpuidle functionality, and an i.MX51 instance of
using this driver.
The patch series is based on v3.1-rc2.
Changes since v1:
* To address all the problems found during review of v1, a complete
re-write was needed.
Robert Lee (3):
ARM: imx: Add imx cpuidle driver
ARM: imx: Add cpuidle for mach-mx5
ARM: imx: Add cpuidle for i.MX51
arch/arm/mach-mx5/Makefile | 3 +-
arch/arm/mach-mx5/cpu_op-mx51.c | 35 +++++++-
arch/arm/mach-mx5/cpu_op-mx51.h | 1 +
arch/arm/mach-mx5/mm.c | 4 +
arch/arm/mach-mx5/system.c | 35 +++++++
arch/arm/plat-mxc/Makefile | 1 +
arch/arm/plat-mxc/cpuidle.c | 151 ++++++++++++++++++++++++++++++
arch/arm/plat-mxc/include/mach/cpuidle.h | 56 +++++++++++
arch/arm/plat-mxc/include/mach/system.h | 3 +
9 files changed, 286 insertions(+), 3 deletions(-)
create mode 100644 arch/arm/plat-mxc/cpuidle.c
create mode 100644 arch/arm/plat-mxc/include/mach/cpuidle.h
^ permalink raw reply
* [PATCH v2 1/3] ARM: imx: Add imx cpuidle driver
From: Robert Lee @ 2011-09-16 17:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316194070-21889-1-git-send-email-rob.lee@linaro.org>
Introduce a new cpuidle driver which provides the common cpuidle
functionality necessary for any imx soc cpuidle implementation.
Signed-off-by: Robert Lee <rob.lee@linaro.org>
---
arch/arm/plat-mxc/Makefile | 1 +
arch/arm/plat-mxc/cpuidle.c | 151 ++++++++++++++++++++++++++++++
arch/arm/plat-mxc/include/mach/cpuidle.h | 56 +++++++++++
3 files changed, 208 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/plat-mxc/cpuidle.c
create mode 100644 arch/arm/plat-mxc/include/mach/cpuidle.h
diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile
index d53c35f..8939f79 100644
--- a/arch/arm/plat-mxc/Makefile
+++ b/arch/arm/plat-mxc/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o
obj-$(CONFIG_ARCH_MXC_AUDMUX_V2) += audmux-v2.o
obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o
obj-$(CONFIG_CPU_FREQ_IMX) += cpufreq.o
+obj-$(CONFIG_CPU_IDLE) += cpuidle.o
ifdef CONFIG_SND_IMX_SOC
obj-y += ssi-fiq.o
obj-y += ssi-fiq-ksym.o
diff --git a/arch/arm/plat-mxc/cpuidle.c b/arch/arm/plat-mxc/cpuidle.c
new file mode 100644
index 0000000..9e4c176
--- /dev/null
+++ b/arch/arm/plat-mxc/cpuidle.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/cpuidle.h>
+#include <linux/err.h>
+#include <asm/proc-fns.h>
+#include <mach/cpuidle.h>
+
+static int (*mach_cpuidle)(struct cpuidle_device *dev,
+ struct cpuidle_state *state);
+static struct cpuidle_driver *imx_cpuidle_driver;
+static struct __initdata cpuidle_device * device;
+
+static int imx_enter_idle(struct cpuidle_device *dev,
+ struct cpuidle_state *state)
+{
+ struct timeval before, after;
+ int idle_time;
+
+ local_irq_disable();
+ local_fiq_disable();
+
+ do_gettimeofday(&before);
+
+ mach_cpuidle(dev, state);
+
+ do_gettimeofday(&after);
+
+ local_fiq_enable();
+ local_irq_enable();
+
+ idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
+ (after.tv_usec - before.tv_usec);
+
+ return idle_time;
+}
+
+static DEFINE_PER_CPU(struct cpuidle_device, imx_cpuidle_device);
+
+int __init imx_cpuidle_init(struct imx_cpuidle_data *cpuidle_data)
+{
+ int i, cpu_id;
+
+ if (cpuidle_data == NULL) {
+ pr_err("%s: cpuidle_data pointer NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ if (cpuidle_data->mach_cpuidle == NULL) {
+ pr_err("%s: idle callback function NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ imx_cpuidle_driver = cpuidle_data->imx_cpuidle_driver;
+
+ mach_cpuidle = cpuidle_data->mach_cpuidle;
+
+ /* register imx_cpuidle driver */
+ if (cpuidle_register_driver(imx_cpuidle_driver)) {
+ pr_err("%s: Failed to register cpuidle driver\n", __func__);
+ return -ENODEV;
+ }
+
+ /* if provided, initialize the mach level cpuidle functionality */
+ if (cpuidle_data->mach_cpuidle_init) {
+ if (cpuidle_data->mach_cpuidle_init(cpuidle_data)) {
+ pr_err("%s: Failed to register cpuidle driver\n",
+ __func__);
+ cpuidle_unregister_driver(imx_cpuidle_driver);
+ return -ENODEV;
+ }
+ }
+
+ /* initialize state data for each cpuidle_device */
+ for_each_cpu(cpu_id, cpu_online_mask) {
+
+ device = &per_cpu(imx_cpuidle_device, cpu_id);
+ device->cpu = cpu_id;
+
+ device->state_count = min((unsigned int) CPUIDLE_STATE_MAX,
+ cpuidle_data->num_states);
+
+ device->prepare = cpuidle_data->prepare;
+
+ for (i = 0; i < device->state_count; i++) {
+ strlcpy(device->states[i].name,
+ cpuidle_data->state_data[i].name,
+ CPUIDLE_NAME_LEN);
+
+ strlcpy(device->states[i].desc,
+ cpuidle_data->state_data[i].desc,
+ CPUIDLE_DESC_LEN);
+
+ device->states[i].driver_data =
+ (void *)cpuidle_data->
+ state_data[i].mach_cpu_pwr_state;
+
+ /*
+ * Because the imx_enter_idle function measures
+ * and returns a valid time for all imx SoCs,
+ * we always set this flag.
+ */
+ device->states[i].flags = CPUIDLE_FLAG_TIME_VALID;
+
+ device->states[i].exit_latency =
+ cpuidle_data->state_data[i].exit_latency;
+
+ device->states[i].power_usage =
+ cpuidle_data->state_data[i].power_usage;
+
+ device->states[i].target_residency =
+ cpuidle_data->state_data[i].target_residency;
+
+ device->states[i].enter = imx_enter_idle;
+ }
+ }
+
+ return 0;
+}
+
+int __init imx_cpuidle_dev_init(void)
+{
+ int cpu_id;
+
+ if (device == NULL) {
+ pr_err("%s: Failed to register (No device)\n", __func__);
+ return -ENODEV;
+ }
+
+ for_each_cpu(cpu_id, cpu_online_mask) {
+ device = &per_cpu(imx_cpuidle_device, cpu_id);
+ if (cpuidle_register_device(device)) {
+ pr_err("%s: Failed to register\n", __func__);
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+late_initcall(imx_cpuidle_dev_init);
diff --git a/arch/arm/plat-mxc/include/mach/cpuidle.h b/arch/arm/plat-mxc/include/mach/cpuidle.h
new file mode 100644
index 0000000..2bb109e
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/cpuidle.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __ARCH_ARM_PLAT_MXC_CPUIDLE_H__
+#define __ARCH_ARM_PLAT_MXC_CPUIDLE_H__
+
+#include <linux/cpuidle.h>
+#include <mach/hardware.h>
+
+/* for passing cpuidle state info to the cpuidle driver. */
+struct imx_cpuidle_state_data {
+ enum mxc_cpu_pwr_mode mach_cpu_pwr_state;
+ char *name;
+ char *desc;
+ /* time in uS to exit this idle state */
+ unsigned int exit_latency;
+ /* OPTIONAL - power usage of this idle state in mW */
+ unsigned int power_usage;
+ /* OPTIONAL - in uS. See drivers/cpuidle/governors/menu.c for usage */
+ unsigned int target_residency;
+};
+
+struct imx_cpuidle_data {
+ unsigned int num_states;
+ struct cpuidle_driver *imx_cpuidle_driver;
+ struct imx_cpuidle_state_data *state_data;
+ int (*mach_cpuidle)(struct cpuidle_device *dev,
+ struct cpuidle_state *state);
+
+ /* OPTIONAL - parameter of mach_cpuidle_init func below */
+ void *mach_init_data;
+ /* OPTIONAL - callback for mach level cpuidle initialization */
+ int (*mach_cpuidle_init)(void *mach_init_data);
+ /* OPTIONAL - Search drivers/cpuidle/cpuidle.c for usage */
+ int (*prepare)(struct cpuidle_device *dev);
+};
+
+#ifdef CONFIG_CPU_IDLE
+int imx_cpuidle_init(struct imx_cpuidle_data *cpuidle_data);
+#else
+static inline int imx_cpuidle_init(struct imx_cpuidle_data *cpuidle_data)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_CPU_IDLE */
+
+#endif /* __ARCH_ARM_PLAT_MXC_CPUIDLE_H__ */
--
1.7.1
^ permalink raw reply related
* [PATCH v2 2/3] ARM: imx: Add cpuidle for mach-mx5
From: Robert Lee @ 2011-09-16 17:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316194070-21889-1-git-send-email-rob.lee@linaro.org>
Add functionality for initialization and handling of a cpuidle driver
requests entering a cpu idle state.
Signed-off-by: Robert Lee <rob.lee@linaro.org>
---
arch/arm/mach-mx5/system.c | 35 +++++++++++++++++++++++++++++++
arch/arm/plat-mxc/include/mach/system.h | 3 ++
2 files changed, 38 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-mx5/system.c b/arch/arm/mach-mx5/system.c
index 76ae8dc..85d6dd7 100644
--- a/arch/arm/mach-mx5/system.c
+++ b/arch/arm/mach-mx5/system.c
@@ -12,7 +12,11 @@
*/
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <asm/proc-fns.h>
#include <mach/hardware.h>
+#include <mach/cpuidle.h>
#include "crm_regs.h"
/* set cpu low power mode before WFI instruction. This function is called
@@ -82,3 +86,34 @@ void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
__raw_writel(empgc1, MXC_SRPG_EMPGC1_SRPGCR);
}
}
+
+int mx5_cpuidle(struct cpuidle_device *dev, struct cpuidle_state *state)
+{
+ mx5_cpu_lp_set((enum mxc_cpu_pwr_mode)state->driver_data);
+
+ cpu_do_idle();
+
+ return 0;
+}
+
+int __init mx5_cpuidle_init(void * init_data)
+{
+ int ret;
+ struct clk *gpc_dvfs_clk;
+
+ gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs");
+
+ if (IS_ERR(gpc_dvfs_clk)) {
+ pr_err("%s: Failed to get gpc_dvfs clock\n", __func__);
+ return (int)gpc_dvfs_clk;
+ }
+
+ ret = clk_enable(gpc_dvfs_clk);
+
+ if (IS_ERR(&ret)) {
+ pr_err("%s: Failed to enable gpc_dvfs clock\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/arch/arm/plat-mxc/include/mach/system.h b/arch/arm/plat-mxc/include/mach/system.h
index 51f02a9..f7c4be5 100644
--- a/arch/arm/plat-mxc/include/mach/system.h
+++ b/arch/arm/plat-mxc/include/mach/system.h
@@ -17,10 +17,13 @@
#ifndef __ASM_ARCH_MXC_SYSTEM_H__
#define __ASM_ARCH_MXC_SYSTEM_H__
+#include <linux/cpuidle.h>
#include <mach/hardware.h>
#include <mach/common.h>
extern void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode);
+extern int mx5_cpuidle_init(void *init_data);
+extern int mx5_cpuidle(struct cpuidle_device *dev, struct cpuidle_state *state);
static inline void arch_idle(void)
{
--
1.7.1
^ permalink raw reply related
* [PATCH v2 3/3] ARM: imx: Add cpuidle for i.MX51
From: Robert Lee @ 2011-09-16 17:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316194070-21889-1-git-send-email-rob.lee@linaro.org>
Add the i.MX51 SoC specific cpuidle operating point data and other
necessary data. Add init call to fill in imx cpuidle driver data
structures.
Signed-off-by: Robert Lee <rob.lee@linaro.org>
---
arch/arm/mach-mx5/Makefile | 3 +--
arch/arm/mach-mx5/cpu_op-mx51.c | 35 ++++++++++++++++++++++++++++++++++-
arch/arm/mach-mx5/cpu_op-mx51.h | 1 +
arch/arm/mach-mx5/mm.c | 4 ++++
4 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/arch/arm/mach-mx5/Makefile b/arch/arm/mach-mx5/Makefile
index 383e7cd..dc5413b 100644
--- a/arch/arm/mach-mx5/Makefile
+++ b/arch/arm/mach-mx5/Makefile
@@ -5,9 +5,8 @@
# Object file lists.
obj-y := cpu.o mm.o clock-mx51-mx53.o devices.o ehci.o system.o
obj-$(CONFIG_SOC_IMX50) += mm-mx50.o
-
+obj-$(CONFIG_SOC_IMX51) += cpu_op-mx51.o
obj-$(CONFIG_PM) += pm-imx5.o
-obj-$(CONFIG_CPU_FREQ_IMX) += cpu_op-mx51.o
obj-$(CONFIG_MACH_MX51_BABBAGE) += board-mx51_babbage.o
obj-$(CONFIG_MACH_MX51_3DS) += board-mx51_3ds.o
obj-$(CONFIG_MACH_MX53_EVK) += board-mx53_evk.o
diff --git a/arch/arm/mach-mx5/cpu_op-mx51.c b/arch/arm/mach-mx5/cpu_op-mx51.c
index 9d34c3d..d85ced3 100644
--- a/arch/arm/mach-mx5/cpu_op-mx51.c
+++ b/arch/arm/mach-mx5/cpu_op-mx51.c
@@ -12,9 +12,12 @@
*/
#include <linux/types.h>
-#include <mach/hardware.h>
#include <linux/kernel.h>
+#include <asm/proc-fns.h>
+#include <mach/cpuidle.h>
+#include <mach/system.h>
+#ifdef CONFIG_CPU_FREQ_IMX
static struct cpu_op mx51_cpu_op[] = {
{
.cpu_rate = 160000000,},
@@ -27,3 +30,33 @@ struct cpu_op *mx51_get_cpu_op(int *op)
*op = ARRAY_SIZE(mx51_cpu_op);
return mx51_cpu_op;
}
+#endif /* CONFIG_CPU_FREQ_IMX */
+
+#ifdef CONFIG_CPU_IDLE
+static struct imx_cpuidle_state_data mx51_cpuidle_state_data[] __initdata = {
+ {
+ .name = "powered_noclock",
+ .desc = "idle cpu powered, unclocked.",
+ .exit_latency = 12,
+ .mach_cpu_pwr_state = WAIT_UNCLOCKED,
+ }, {
+ .name = "nopower_noclock",
+ .desc = "idle cpu unpowered, unclocked.",
+ .exit_latency = 20,
+ .mach_cpu_pwr_state = WAIT_UNCLOCKED_POWER_OFF,
+ }
+};
+
+static struct cpuidle_driver imx51_cpuidle_driver = {
+ .name = "imx51_cpuidle",
+ .owner = THIS_MODULE,
+};
+
+struct imx_cpuidle_data mx51_cpuidle_data __initdata = {
+ .imx_cpuidle_driver = &imx51_cpuidle_driver,
+ .state_data = mx51_cpuidle_state_data,
+ .mach_cpuidle = mx5_cpuidle,
+ .mach_cpuidle_init = mx5_cpuidle_init,
+ .num_states = ARRAY_SIZE(mx51_cpuidle_state_data),
+};
+#endif /* CONFIG_CPU_IDLE */
diff --git a/arch/arm/mach-mx5/cpu_op-mx51.h b/arch/arm/mach-mx5/cpu_op-mx51.h
index 97477fe..60c60fd 100644
--- a/arch/arm/mach-mx5/cpu_op-mx51.h
+++ b/arch/arm/mach-mx5/cpu_op-mx51.h
@@ -12,3 +12,4 @@
*/
extern struct cpu_op *mx51_get_cpu_op(int *op);
+extern struct imx_cpuidle_data mx51_cpuidle_data;
diff --git a/arch/arm/mach-mx5/mm.c b/arch/arm/mach-mx5/mm.c
index baea6e5..d20827e 100644
--- a/arch/arm/mach-mx5/mm.c
+++ b/arch/arm/mach-mx5/mm.c
@@ -20,6 +20,8 @@
#include <mach/common.h>
#include <mach/devices-common.h>
#include <mach/iomux-v3.h>
+#include <mach/cpuidle.h>
+#include "cpu_op-mx51.h"
/*
* Define the MX51 memory map.
@@ -148,6 +150,8 @@ void __init imx51_soc_init(void)
/* i.mx51 has the i.mx35 type sdma */
imx_add_imx_sdma("imx35-sdma", MX51_SDMA_BASE_ADDR, MX51_INT_SDMA, &imx51_sdma_pdata);
+
+ imx_cpuidle_init(&mx51_cpuidle_data);
}
void __init imx53_soc_init(void)
--
1.7.1
^ permalink raw reply related
* [PATCH 23/25] OMAP4: PM: Add CPUidle support
From: Kevin Hilman @ 2011-09-16 17:45 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1315144466-9395-24-git-send-email-santosh.shilimkar@ti.com>
Hi Santosh,
Santosh Shilimkar <santosh.shilimkar@ti.com> writes:
> Add OMAP4 CPUIDLE support. CPU1 is left with defualt idle and
> the low power state for it is managed via cpu-hotplug.
>
> This patch adds MPUSS low power states in cpuidle.
>
> C1 - CPU0 ON + CPU1 ON + MPU ON
> C2 - CPU0 OFF + CPU1 OFF + MPU CSWR
> C3 - CPU0 OFF + CPU1 OFF + MPU OSWR
>
> OMAP4460 onwards, MPUSS power domain doesn't support OFF state any more
> anymore just like CORE power domain. The deepest state supported is OSWr.
> Ofcourse when MPUSS and CORE PD transitions to OSWR along with device
> off mode, even the memory contemts are lost which is as good as
> the PD off state.
>
> On OMAP4 because of hardware constraints, no low power states are
> targeted when both CPUs are online and in SMP mode. The low power
> states are attempted only when secondary CPU gets offline to OFF
> through hotplug infrastructure.
>
> Thanks to Nicole Chalhoub <n-chalhoub@ti.com> for doing exhaustive
> C-state latency profiling.
>
> Signed-off-by: Rajendra Nayak <rnayak@ti.com>
> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
> Cc: Kevin Hilman <khilman@ti.com>
A handful of minor comments below...
[...]
> +/**
> + * omap4_enter_idle - Programs OMAP4 to enter the specified state
> + * @dev: cpuidle device
> + * @state: The target state to be programmed
> + *
> + * Called from the CPUidle framework to program the device to the
> + * specified low power state selected by the governor.
> + * Returns the amount of time spent in the low power state.
> + */
> +static int omap4_enter_idle(struct cpuidle_device *dev,
> + struct cpuidle_state *state)
> +{
> + struct omap4_idle_statedata *cx = cpuidle_get_statedata(state);
> + struct timespec ts_preidle, ts_postidle, ts_idle;
> + u32 cpu1_state;
> +
> + /* Used to keep track of the total time in idle */
> + getnstimeofday(&ts_preidle);
> +
> + local_irq_disable();
> + local_fiq_disable();
> +
> + /*
> + * Keep demoting CPU0 C-state till CPU1 hits OFF state.
IMO, the working here isn't as clear as it could be. IOW, I don't like
"keep demoting", which makes it sound iterative through a set of states,
where what's happening here is a one-time decision.
Rather, what I think you mean is "CPU0 has to stay on (e.g in C1) until
CPU1 is off."
> + * This is necessary to honour hardware recommondation
> + * of triggeing all the possible low power modes once CPU1 is
> + * out of coherency and in OFF mode.
> + * Update dev->last_state so that governor stats reflects right
> + * data.
> + */
> + cpu1_state = pwrdm_read_pwrst(cpu1_pd);
> + if (cpu1_state != PWRDM_POWER_OFF) {
> + dev->last_state = dev->safe_state;
> + cx = cpuidle_get_statedata(dev->safe_state);
> + }
> +
> + /* Call idle CPU PM enter notifier chain */
This comment doesn't add any value over the code. If anything, a
comment explaining why it's only there for off-mode transitions would be
helpful.
> + if (cx->cpu_state == PWRDM_POWER_OFF)
> + cpu_pm_enter();
I think the CPU PM notifier usage should probably be a separate patch.
> + pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
> + omap_set_pwrdm_state(mpu_pd, cx->mpu_state);
> +
> + /* Call idle CPU cluster PM enter notifier chain */
> + if ((cx->mpu_state == PWRDM_POWER_RET) &&
> + (cx->mpu_logic_state == PWRDM_POWER_OFF))
> + cpu_cluster_pm_enter();
> +
> + omap4_enter_lowpower(dev->cpu, cx->cpu_state);
> +
> + /* Call idle CPU PM exit notifier chain */
As above, this comment doesn't add any value over the code.
While I understand why it's only done for CPU0 here (CPU1 is managed by
hotplug), we should anticipating the moment where we will have forgotten
why it's only done for CPU0, and add a comment here.
> + if (pwrdm_read_prev_pwrst(cpu0_pd) == PWRDM_POWER_OFF)
> + cpu_pm_exit();
> +
> + /* Call idle CPU cluster PM exit notifier chain */
again, comment not helpful
> + if (omap4_mpuss_read_prev_context_state())
> + cpu_cluster_pm_exit();
> +
> + getnstimeofday(&ts_postidle);
> + ts_idle = timespec_sub(ts_postidle, ts_preidle);
> +
> + local_irq_enable();
> + local_fiq_enable();
> +
> + return ts_idle.tv_nsec / NSEC_PER_USEC + ts_idle.tv_sec * USEC_PER_SEC;
> +}
> +
> +DEFINE_PER_CPU(struct cpuidle_device, omap4_idle_dev);
> +
> +struct cpuidle_driver omap4_idle_driver = {
> + .name = "omap4_idle",
> + .owner = THIS_MODULE,
> +};
> +
> +/* Fill in the state data from the mach tables and register the driver_data */
if documented, it should have a kerneldoc header instead
> +static inline struct omap4_idle_statedata *_fill_cstate(
> + struct cpuidle_device *dev,
> + int idx, const char *descr)
> +{
> + struct omap4_idle_statedata *cx = &omap4_idle_data[idx];
> + struct cpuidle_state *state = &dev->states[idx];
> +
> + state->exit_latency = cpuidle_params_table[idx].exit_latency;
> + state->target_residency = cpuidle_params_table[idx].target_residency;
> + state->flags = CPUIDLE_FLAG_TIME_VALID;
> + state->enter = omap4_enter_idle;
> + cx->valid = cpuidle_params_table[idx].valid;
> + sprintf(state->name, "C%d", idx + 1);
> + strncpy(state->desc, descr, CPUIDLE_DESC_LEN);
> + cpuidle_set_statedata(state, cx);
> +
> + return cx;
> +}
Kevin
^ permalink raw reply
* [PATCH v2 0/6] OMAP3+ Clock Fixes
From: Jon Hunter @ 2011-09-16 17:46 UTC (permalink / raw)
To: linux-arm-kernel
From: Jon Hunter <jon-hunter@ti.com>
Various clock fixes for OMAP3 and OMAP4 devices.
Jon Hunter (4):
OMAP4: Add missing clock divider for OCP_ABE_ICLK
OMAP3+: use DPLLs recalc function instead of omap2_get_dpll_rate
OMAP3+: Update DPLL Fint range for OMAP36xx and OMAP4xxx devices
OMAP4: Clock: Correct the name of SLIMBUS interface clocks
Mike Turquette (2):
OMAP4: Clock: round_rate and recalc functions for DPLL_ABE
OMAP3+: use DPLL's round_rate when setting rate
arch/arm/mach-omap2/clkt_dpll.c | 51 +++++++++++++++++++++++----------
arch/arm/mach-omap2/clock.h | 2 +
arch/arm/mach-omap2/clock44xx.h | 2 +
arch/arm/mach-omap2/clock44xx_data.c | 32 +++++++++++++++------
arch/arm/mach-omap2/dpll3xxx.c | 6 ++--
arch/arm/mach-omap2/dpll44xx.c | 45 ++++++++++++++++++++++++++++++
6 files changed, 110 insertions(+), 28 deletions(-)
--
1.7.4.1
^ permalink raw reply
* [PATCH v2 1/6] OMAP4: Add missing clock divider for OCP_ABE_ICLK
From: Jon Hunter @ 2011-09-16 17:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Jon Hunter <jon-hunter@ti.com>
The parent clock of the OCP_ABE_ICLK is the AESS_FCLK and the
parent clock of the AESS_FCLK is the ABE_FCLK...
ABE_FCLK --> AESS_FCLK --> OCP_ABE_ICLK
The AESS_FCLK and OCP_ABE_ICLK clocks both have dividers which
determine their operational frequency. However, the dividers for
the AESS_FCLK and OCP_ABE_ICLK are controlled via a single bit,
which is the CM1_ABE_AESS_CLKCTRL[24] bit. When this bit is set to
0, the AESS_FCLK divider is 1 and the OCP_ABE_ICLK divider is 2.
Similarly, when this bit is set to 1, the AESS_FCLK divider is 2
and the OCP_ABE_ICLK is 1.
The above relationship between the AESS_FCLK and OCP_ABE_ICLK
dividers ensure that the OCP_ABE_ICLK clock is always half the
frequency of the ABE_CLK...
OCP_ABE_ICLK = ABE_FCLK/2
The divider for the OCP_ABE_ICLK is currently missing so add a
divider that will ensure the OCP_ABE_ICLK frequency is always half
the ABE_FCLK frequency.
Signed-off-by: Jon Hunter <jon-hunter@ti.com>
---
arch/arm/mach-omap2/clock44xx_data.c | 16 +++++++++++++++-
1 files changed, 15 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-omap2/clock44xx_data.c b/arch/arm/mach-omap2/clock44xx_data.c
index c0b6fbd..3e34dcd 100644
--- a/arch/arm/mach-omap2/clock44xx_data.c
+++ b/arch/arm/mach-omap2/clock44xx_data.c
@@ -1195,11 +1195,25 @@ static struct clk l4_wkup_clk_mux_ck = {
.recalc = &omap2_clksel_recalc,
};
+static const struct clksel_rate div2_2to1_rates[] = {
+ { .div = 1, .val = 1, .flags = RATE_IN_4430 },
+ { .div = 2, .val = 0, .flags = RATE_IN_4430 },
+ { .div = 0 },
+};
+
+static const struct clksel ocp_abe_iclk_div[] = {
+ { .parent = &aess_fclk, .rates = div2_2to1_rates },
+ { .parent = NULL },
+};
+
static struct clk ocp_abe_iclk = {
.name = "ocp_abe_iclk",
.parent = &aess_fclk,
+ .clksel = ocp_abe_iclk_div,
+ .clksel_reg = OMAP4430_CM1_ABE_AESS_CLKCTRL,
+ .clksel_mask = OMAP4430_CLKSEL_AESS_FCLK_MASK,
.ops = &clkops_null,
- .recalc = &followparent_recalc,
+ .recalc = &omap2_clksel_recalc,
};
static struct clk per_abe_24m_fclk = {
--
1.7.4.1
^ permalink raw reply related
* [PATCH v2 2/6] OMAP4: Clock: round_rate and recalc functions for DPLL_ABE
From: Jon Hunter @ 2011-09-16 17:47 UTC (permalink / raw)
To: linux-arm-kernel
From: Mike Turquette <mturquette@ti.com>
OMAP4 DPLL_ABE can enable a 4X multipler on top of the normal MN multipler
and divider. This is achieved by setting CM_CLKMODE_DPLL_ABE.DPLL_REGM4XEN
bit in CKGEN module of CM1. From the OMAP4 TRM:
Fdpll = Fref x 2 x (4 x M/(N+1)) in case REGM4XEN bit field is set (only
applicable to DPLL_ABE).
Add new round_rate() and recalc() functions for OMAP4, that check the
setting of REGM4XEN bit and handle this appropriately. The new functions
are a simple wrapper on top of the existing omap2_dpll_round_rate() and
omap2_dpll_get_rate() functions to handle the REGM4XEN bit.
The REGM4XEN bit is only implemented for the ABE DPLL on OMAP4 and so
only dpll_abe_ck uses omap4_dpll_regm4xen_round_rate() and
omap4_dpll_regm4xen_recalc() functions.
Signed-off-by: Mike Turquette <mturquette@ti.com>
Tested-by: Jon Hunter <jon-hunter@ti.com>
---
arch/arm/mach-omap2/clock.h | 2 +
arch/arm/mach-omap2/clock44xx.h | 2 +
arch/arm/mach-omap2/clock44xx_data.c | 4 +-
arch/arm/mach-omap2/dpll44xx.c | 45 ++++++++++++++++++++++++++++++++++
4 files changed, 51 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h
index 48ac568..2311bc2 100644
--- a/arch/arm/mach-omap2/clock.h
+++ b/arch/arm/mach-omap2/clock.h
@@ -66,6 +66,8 @@ void omap3_noncore_dpll_disable(struct clk *clk);
int omap4_dpllmx_gatectrl_read(struct clk *clk);
void omap4_dpllmx_allow_gatectrl(struct clk *clk);
void omap4_dpllmx_deny_gatectrl(struct clk *clk);
+long omap4_dpll_regm4xen_round_rate(struct clk *clk, unsigned long target_rate);
+unsigned long omap4_dpll_regm4xen_recalc(struct clk *clk);
#ifdef CONFIG_OMAP_RESET_CLOCKS
void omap2_clk_disable_unused(struct clk *clk);
diff --git a/arch/arm/mach-omap2/clock44xx.h b/arch/arm/mach-omap2/clock44xx.h
index 7ceb870..e9cce7d 100644
--- a/arch/arm/mach-omap2/clock44xx.h
+++ b/arch/arm/mach-omap2/clock44xx.h
@@ -8,6 +8,8 @@
#ifndef __ARCH_ARM_MACH_OMAP2_CLOCK44XX_H
#define __ARCH_ARM_MACH_OMAP2_CLOCK44XX_H
+#define OMAP4430_REGM4XEN_MULT 4
+
int omap4xxx_clk_init(void);
#endif
diff --git a/arch/arm/mach-omap2/clock44xx_data.c b/arch/arm/mach-omap2/clock44xx_data.c
index 3e34dcd..eb2a345 100644
--- a/arch/arm/mach-omap2/clock44xx_data.c
+++ b/arch/arm/mach-omap2/clock44xx_data.c
@@ -270,8 +270,8 @@ static struct clk dpll_abe_ck = {
.dpll_data = &dpll_abe_dd,
.init = &omap2_init_dpll_parent,
.ops = &clkops_omap3_noncore_dpll_ops,
- .recalc = &omap3_dpll_recalc,
- .round_rate = &omap2_dpll_round_rate,
+ .recalc = &omap4_dpll_regm4xen_recalc,
+ .round_rate = &omap4_dpll_regm4xen_round_rate,
.set_rate = &omap3_noncore_dpll_set_rate,
};
diff --git a/arch/arm/mach-omap2/dpll44xx.c b/arch/arm/mach-omap2/dpll44xx.c
index 4e4da61..4f31530 100644
--- a/arch/arm/mach-omap2/dpll44xx.c
+++ b/arch/arm/mach-omap2/dpll44xx.c
@@ -19,6 +19,7 @@
#include <plat/clock.h>
#include "clock.h"
+#include "clock44xx.h"
#include "cm-regbits-44xx.h"
/* Supported only on OMAP4 */
@@ -82,3 +83,47 @@ const struct clkops clkops_omap4_dpllmx_ops = {
.deny_idle = omap4_dpllmx_deny_gatectrl,
};
+unsigned long omap4_dpll_regm4xen_recalc(struct clk *clk)
+{
+ u32 v;
+ unsigned long rate;
+ struct dpll_data *dd;
+
+ if (!clk || !clk->dpll_data)
+ return -EINVAL;
+
+ dd = clk->dpll_data;
+
+ rate = omap2_get_dpll_rate(clk);
+
+ /* regm4xen adds a multiplier of 4 to DPLL calculations */
+ v = __raw_readl(dd->control_reg);
+ if (v & OMAP4430_DPLL_REGM4XEN_MASK)
+ rate *= OMAP4430_REGM4XEN_MULT;
+
+ return rate;
+}
+
+long omap4_dpll_regm4xen_round_rate(struct clk *clk, unsigned long target_rate)
+{
+ u32 v;
+ struct dpll_data *dd;
+
+ if (!clk || !clk->dpll_data)
+ return -EINVAL;
+
+ dd = clk->dpll_data;
+
+ /* regm4xen adds a multiplier of 4 to DPLL calculations */
+ v = __raw_readl(dd->control_reg) & OMAP4430_DPLL_REGM4XEN_MASK;
+
+ if (v)
+ target_rate = target_rate / OMAP4430_REGM4XEN_MULT;
+
+ omap2_dpll_round_rate(clk, target_rate);
+
+ if (v)
+ clk->dpll_data->last_rounded_rate *= OMAP4430_REGM4XEN_MULT;
+
+ return clk->dpll_data->last_rounded_rate;
+}
--
1.7.4.1
^ permalink raw reply related
* [PATCH v3 3/6] OMAP3+: use DPLL's round_rate when setting rate
From: Jon Hunter @ 2011-09-16 17:48 UTC (permalink / raw)
To: linux-arm-kernel
From: Mike Turquette <mturquette@ti.com>
omap3_noncore_dpll_set_rate uses omap2_dpll_round_rate explicitly. Instead
use the struct clk pointer's round_rate function to allow for DPLL's with
special needs.
Also the rounded rate can differ from target rate, so to better reflect
reality set clk->rate equal to the rounded rate when setting DPLL frequency.
This avoids issues where the DPLL frequency is slightly different than what
debugfs clock tree reports using the old target rate.
An example of both of these needs is DPLL_ABE on OMAP4 which can have a 4x
multiplier on top of the usual MN dividers depending on register settings.
This requires a special round_rate function that might yield a rate
different from the initial target.
Signed-off-by: Mike Turquette <mturquette@ti.com>
Signed-off-by: Jon Hunter <jon-hunter@ti.com>
---
arch/arm/mach-omap2/dpll3xxx.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c
index f77022b..73a1595 100644
--- a/arch/arm/mach-omap2/dpll3xxx.c
+++ b/arch/arm/mach-omap2/dpll3xxx.c
@@ -455,7 +455,7 @@ int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
new_parent = dd->clk_bypass;
} else {
if (dd->last_rounded_rate != rate)
- omap2_dpll_round_rate(clk, rate);
+ rate = clk->round_rate(clk, rate);
if (dd->last_rounded_rate == 0)
return -EINVAL;
--
1.7.4.1
^ permalink raw reply related
* [PATCH v2 4/6] OMAP3+: use DPLLs recalc function instead of omap2_get_dpll_rate
From: Jon Hunter @ 2011-09-16 17:48 UTC (permalink / raw)
To: linux-arm-kernel
From: Jon Hunter <jon-hunter@ti.com>
This is a continuation of Mike Turquette's patch "OMAP3+: use
DPLL's round_rate when setting rate".
omap3_noncore_dpll_set_rate() and omap3_noncore_dpll_enable() call
omap2_get_dpll_rate() explicitly. It may be necessary for some
DPLLs to use a different function and so use the DPLLs recalc()
function pointer instead.
An example is the DPLL_ABE on OMAP4 which can have a 4X multiplier
in addition to the usual MN multipler and dividers and therefore
uses a different round_rate and recalc function.
Signed-off-by: Jon Hunter <jon-hunter@ti.com>
---
arch/arm/mach-omap2/dpll3xxx.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c
index 73a1595..7916f66 100644
--- a/arch/arm/mach-omap2/dpll3xxx.c
+++ b/arch/arm/mach-omap2/dpll3xxx.c
@@ -390,7 +390,7 @@ int omap3_noncore_dpll_enable(struct clk *clk)
* propagating?
*/
if (!r)
- clk->rate = omap2_get_dpll_rate(clk);
+ clk->rate = clk->recalc(clk);
return r;
}
@@ -435,7 +435,7 @@ int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
if (!dd)
return -EINVAL;
- if (rate == omap2_get_dpll_rate(clk))
+ if (rate == clk->recalc(clk))
return 0;
/*
--
1.7.4.1
^ permalink raw reply related
* [PATCH v2 5/6] OMAP3+: Update DPLL Fint range for OMAP36xx and OMAP4xxx devices
From: Jon Hunter @ 2011-09-16 17:48 UTC (permalink / raw)
To: linux-arm-kernel
From: Jon Hunter <jon-hunter@ti.com>
The OMAP36xx and OMAP4xxx DPLLs have a different internal reference
clock frequency (fint) operating range than OMAP3430. Update the
dpll_test_fint() function to check for the correct frequency ranges
for OMAP36xx and OMAP4xxx.
For OMAP36xx and OMAP4xxx devices, DPLLs fint range is 0.5MHz to
2.5MHz for j-type DPLLs and otherwise it is 32KHz to 52MHz for all
other DPLLs.
Signed-off-by: Jon Hunter <jon-hunter@ti.com>
---
arch/arm/mach-omap2/clkt_dpll.c | 51 ++++++++++++++++++++++++++------------
1 files changed, 35 insertions(+), 16 deletions(-)
diff --git a/arch/arm/mach-omap2/clkt_dpll.c b/arch/arm/mach-omap2/clkt_dpll.c
index bcffee0..e069a9b 100644
--- a/arch/arm/mach-omap2/clkt_dpll.c
+++ b/arch/arm/mach-omap2/clkt_dpll.c
@@ -46,10 +46,19 @@
(DPLL_SCALE_FACTOR / DPLL_SCALE_BASE))
/* DPLL valid Fint frequency band limits - from 34xx TRM Section 4.7.6.2 */
-#define DPLL_FINT_BAND1_MIN 750000
-#define DPLL_FINT_BAND1_MAX 2100000
-#define DPLL_FINT_BAND2_MIN 7500000
-#define DPLL_FINT_BAND2_MAX 21000000
+#define OMAP3430_DPLL_FINT_BAND1_MIN 750000
+#define OMAP3430_DPLL_FINT_BAND1_MAX 2100000
+#define OMAP3430_DPLL_FINT_BAND2_MIN 7500000
+#define OMAP3430_DPLL_FINT_BAND2_MAX 21000000
+
+/*
+ * DPLL valid Fint frequency range for OMAP36xx and OMAP4xxx.
+ * From device data manual section 4.3 "DPLL and DLL Specifications".
+ */
+#define OMAP3PLUS_DPLL_FINT_JTYPE_MIN 500000
+#define OMAP3PLUS_DPLL_FINT_JTYPE_MAX 2500000
+#define OMAP3PLUS_DPLL_FINT_MIN 32000
+#define OMAP3PLUS_DPLL_FINT_MAX 52000000
/* _dpll_test_fint() return codes */
#define DPLL_FINT_UNDERFLOW -1
@@ -71,33 +80,43 @@
static int _dpll_test_fint(struct clk *clk, u8 n)
{
struct dpll_data *dd;
- long fint;
+ long fint, fint_min, fint_max;
int ret = 0;
dd = clk->dpll_data;
/* DPLL divider must result in a valid jitter correction val */
fint = clk->parent->rate / n;
- if (fint < DPLL_FINT_BAND1_MIN) {
+ if (cpu_is_omap24xx()) {
+ /* Should not be called for OMAP2, so warn if it is called */
+ WARN(1, "No fint limits available for OMAP2!\n");
+ return DPLL_FINT_INVALID;
+ } else if (cpu_is_omap3430()) {
+ fint_min = OMAP3430_DPLL_FINT_BAND1_MIN;
+ fint_max = OMAP3430_DPLL_FINT_BAND2_MAX;
+ } else if (dd->flags & DPLL_J_TYPE) {
+ fint_min = OMAP3PLUS_DPLL_FINT_JTYPE_MIN;
+ fint_max = OMAP3PLUS_DPLL_FINT_JTYPE_MAX;
+ } else {
+ fint_min = OMAP3PLUS_DPLL_FINT_MIN;
+ fint_max = OMAP3PLUS_DPLL_FINT_MAX;
+ }
+
+ if (fint < fint_min) {
pr_debug("rejecting n=%d due to Fint failure, "
"lowering max_divider\n", n);
dd->max_divider = n;
ret = DPLL_FINT_UNDERFLOW;
-
- } else if (fint > DPLL_FINT_BAND1_MAX &&
- fint < DPLL_FINT_BAND2_MIN) {
-
- pr_debug("rejecting n=%d due to Fint failure\n", n);
- ret = DPLL_FINT_INVALID;
-
- } else if (fint > DPLL_FINT_BAND2_MAX) {
-
+ } else if (fint > fint_max) {
pr_debug("rejecting n=%d due to Fint failure, "
"boosting min_divider\n", n);
dd->min_divider = n;
ret = DPLL_FINT_INVALID;
-
+ } else if (cpu_is_omap3430() && fint > OMAP3430_DPLL_FINT_BAND1_MAX &&
+ fint < OMAP3430_DPLL_FINT_BAND2_MIN) {
+ pr_debug("rejecting n=%d due to Fint failure\n", n);
+ ret = DPLL_FINT_INVALID;
}
return ret;
--
1.7.4.1
^ permalink raw reply related
* [PATCH v2 6/6] OMAP4: Clock: Correct the name of SLIMBUS interface clocks
From: Jon Hunter @ 2011-09-16 17:48 UTC (permalink / raw)
To: linux-arm-kernel
From: Jon Hunter <jon-hunter@ti.com>
Currently the interface clocks for the two SLIMBUS peripherals are
named slimbus1_fck and slimbus2_fck. Rename these clocks to be
slimbus1_ick and slimbus2_ick so it is clear that these are
interface clocks and not functional clocks.
Signed-off-by: Jon Hunter <jon-hunter@ti.com>
---
arch/arm/mach-omap2/clock44xx_data.c | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/arch/arm/mach-omap2/clock44xx_data.c b/arch/arm/mach-omap2/clock44xx_data.c
index eb2a345..d75ebdd 100644
--- a/arch/arm/mach-omap2/clock44xx_data.c
+++ b/arch/arm/mach-omap2/clock44xx_data.c
@@ -2210,8 +2210,8 @@ static struct clk slimbus1_slimbus_clk = {
.recalc = &followparent_recalc,
};
-static struct clk slimbus1_fck = {
- .name = "slimbus1_fck",
+static struct clk slimbus1_ick = {
+ .name = "slimbus1_ick",
.ops = &clkops_omap2_dflt,
.enable_reg = OMAP4430_CM1_ABE_SLIMBUS_CLKCTRL,
.enable_bit = OMAP4430_MODULEMODE_SWCTRL,
@@ -2250,8 +2250,8 @@ static struct clk slimbus2_slimbus_clk = {
.recalc = &followparent_recalc,
};
-static struct clk slimbus2_fck = {
- .name = "slimbus2_fck",
+static struct clk slimbus2_ick = {
+ .name = "slimbus2_ick",
.ops = &clkops_omap2_dflt,
.enable_reg = OMAP4430_CM_L4PER_SLIMBUS2_CLKCTRL,
.enable_bit = OMAP4430_MODULEMODE_SWCTRL,
@@ -3272,11 +3272,11 @@ static struct omap_clk omap44xx_clks[] = {
CLK(NULL, "slimbus1_fclk_0", &slimbus1_fclk_0, CK_443X),
CLK(NULL, "slimbus1_fclk_2", &slimbus1_fclk_2, CK_443X),
CLK(NULL, "slimbus1_slimbus_clk", &slimbus1_slimbus_clk, CK_443X),
- CLK(NULL, "slimbus1_fck", &slimbus1_fck, CK_443X),
+ CLK(NULL, "slimbus1_ick", &slimbus1_ick, CK_443X),
CLK(NULL, "slimbus2_fclk_1", &slimbus2_fclk_1, CK_443X),
CLK(NULL, "slimbus2_fclk_0", &slimbus2_fclk_0, CK_443X),
CLK(NULL, "slimbus2_slimbus_clk", &slimbus2_slimbus_clk, CK_443X),
- CLK(NULL, "slimbus2_fck", &slimbus2_fck, CK_443X),
+ CLK(NULL, "slimbus2_ick", &slimbus2_ick, CK_443X),
CLK(NULL, "smartreflex_core_fck", &smartreflex_core_fck, CK_443X),
CLK(NULL, "smartreflex_iva_fck", &smartreflex_iva_fck, CK_443X),
CLK(NULL, "smartreflex_mpu_fck", &smartreflex_mpu_fck, CK_443X),
--
1.7.4.1
^ permalink raw reply related
* [PATCH 24/25] OMAP4: cpuidle: Switch to gptimer from twd in deeper C-states.
From: Kevin Hilman @ 2011-09-16 17:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1315144466-9395-25-git-send-email-santosh.shilimkar@ti.com>
Santosh Shilimkar <santosh.shilimkar@ti.com> writes:
> CPU local timer(TWD) stops when the CPU is transitioning into
> deeper C-States. Since these timers are not wakeup capable, we
> need the wakeup capable global timer to program the wakeup time
> depending on the next timer expiry.
>
> It can be handled by registering a global wakeup capable timer along
> with local timers marked with (mis)feature flag CLOCK_EVT_FEAT_C3STOP.
nit: this comment makes is sound like this patch adds the registration
of this timer, when all this patch does is add the notification.
Changelog should be updated to make it clear that the global timer exists
already, and is already marked with the C3STOP flag.
> Then notify the clock events layer from idle code using
> CLOCK_EVT_NOTIFY_BROADCAST_ENTER/EXIT).
>
> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
> Cc: Kevin Hilman <khilman@ti.com>
Other than that
Acked-by: Kevin Hilman <khilman@ti.com>
^ permalink raw reply
* [PATCH v3 0/6] iommu: split mapping to page sizes as supported by the hardware
From: Ohad Ben-Cohen @ 2011-09-16 17:51 UTC (permalink / raw)
To: linux-arm-kernel
v2->v3:
- s/KB/KiB/ (David W)
v1->v2:
- split to patches (by keeping the old code around until all drivers are converted) (Joerg)
Ohad Ben-Cohen (6):
iommu/core: split mapping to page sizes as supported by the hardware
iommu/omap: announce supported page sizes
iommu/msm: announce supported page sizes
iommu/amd: announce supported page sizes
iommu/intel: announce supported page sizes
iommu/core: remove the temporary register_iommu_pgsize API
drivers/iommu/amd_iommu.c | 20 ++++++-
drivers/iommu/intel-iommu.c | 20 ++++++-
drivers/iommu/iommu.c | 130 +++++++++++++++++++++++++++++++++++++++----
drivers/iommu/msm_iommu.c | 8 ++-
drivers/iommu/omap-iommu.c | 6 ++-
drivers/iommu/omap-iovmm.c | 12 +---
include/linux/iommu.h | 7 +-
virt/kvm/iommu.c | 4 +-
8 files changed, 177 insertions(+), 30 deletions(-)
--
1.7.4.1
^ permalink raw reply
* [PATCH v3 1/6] iommu/core: split mapping to page sizes as supported by the hardware
From: Ohad Ben-Cohen @ 2011-09-16 17:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316195506-9777-1-git-send-email-ohad@wizery.com>
When mapping a memory region, split it to page sizes as supported
by the iommu hardware. Always prefer bigger pages, when possible,
in order to reduce the TLB pressure.
The logic to do that is now added to the IOMMU core, so neither the iommu
drivers themselves nor users of the IOMMU API have to duplicate it.
This allows a more lenient granularity of mappings; traditionally the
IOMMU API took 'order' (of a page) as a mapping size, and directly let
the low level iommu drivers handle the mapping, but now that the IOMMU
core can split arbitrary memory regions into pages, we can remove this
limitation, so users don't have to split those regions by themselves.
Currently the supported page sizes are advertised once and they then
remain static. That works well for OMAP (and seemingly MSM too) but
it would probably not fly with intel's hardware, where the page size
capabilities seem to have the potential to be different between
several DMA remapping devices. This limitation can be dealt with
later, if desired. For now, the existing IOMMU API behavior is retained
(see: "iommu/intel: announce supported page sizes").
As requested, register_iommu() isn't changed yet, so we can convert
the IOMMU drivers in subsequent patches, and after all the drivers
are converted, register_iommu will be changed (and the temporary
register_iommu_pgsize() will be removed).
Mainline users of the IOMMU API (kvm and omap-iovmm) are adopted
to send the mapping size in bytes instead of in page order.
Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
Cc: David Brown <davidb@codeaurora.org>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Joerg Roedel <Joerg.Roedel@amd.com>
Cc: Stepan Moskovchenko <stepanm@codeaurora.org>
Cc: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Cc: kvm at vger.kernel.org
---
drivers/iommu/iommu.c | 158 +++++++++++++++++++++++++++++++++++++++++---
drivers/iommu/omap-iovmm.c | 12 +---
include/linux/iommu.h | 6 +-
virt/kvm/iommu.c | 4 +-
4 files changed, 157 insertions(+), 23 deletions(-)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index c68ff29..7c01c8c 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -16,6 +16,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
#include <linux/kernel.h>
#include <linux/bug.h>
#include <linux/types.h>
@@ -23,15 +25,73 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/iommu.h>
+#include <linux/bitmap.h>
static struct iommu_ops *iommu_ops;
+/* bitmap of supported page sizes */
+static unsigned long *iommu_pgsize_bitmap;
+
+/* number of bits used to represent the supported pages */
+static unsigned int iommu_nr_page_bits;
+
+/* size of the smallest supported page (in bytes) */
+static unsigned int iommu_min_pagesz;
+
+/* bit number of the smallest supported page */
+static unsigned int iommu_min_page_idx;
+
+/**
+ * register_iommu() - register an IOMMU hardware
+ * @ops: iommu handlers
+ * @pgsize_bitmap: bitmap of page sizes supported by the hardware
+ * @nr_page_bits: size of @pgsize_bitmap (in bits)
+ *
+ * Note: this is a temporary function, which will be removed once
+ * all IOMMU drivers are converted. The only reason it exists is to
+ * allow splitting the pgsizes changes to several patches in order to ease
+ * the review.
+ */
+void register_iommu_pgsize(struct iommu_ops *ops, unsigned long *pgsize_bitmap,
+ unsigned int nr_page_bits)
+{
+ if (iommu_ops || iommu_pgsize_bitmap || !nr_page_bits)
+ BUG();
+
+ iommu_ops = ops;
+ iommu_pgsize_bitmap = pgsize_bitmap;
+ iommu_nr_page_bits = nr_page_bits;
+
+ /* find the minimum page size and its index only once */
+ iommu_min_page_idx = find_first_bit(pgsize_bitmap, nr_page_bits);
+ iommu_min_pagesz = 1 << iommu_min_page_idx;
+}
+
+/*
+ * default pagesize bitmap, will be removed once all IOMMU drivers
+ * are converted
+ */
+static unsigned long default_iommu_pgsizes = ~0xFFFUL;
+
void register_iommu(struct iommu_ops *ops)
{
if (iommu_ops)
BUG();
iommu_ops = ops;
+
+ /*
+ * set default pgsize values, which retain the existing
+ * IOMMU API behavior: drivers will be called to map
+ * regions that are sized/aligned to order of 4KiB pages
+ */
+ iommu_pgsize_bitmap = &default_iommu_pgsizes;
+ iommu_nr_page_bits = BITS_PER_LONG;
+
+ /* find the minimum page size and its index only once */
+ iommu_min_page_idx = find_first_bit(iommu_pgsize_bitmap,
+ iommu_nr_page_bits);
+ iommu_min_pagesz = 1 << iommu_min_page_idx;
}
bool iommu_found(void)
@@ -109,26 +169,104 @@ int iommu_domain_has_cap(struct iommu_domain *domain,
EXPORT_SYMBOL_GPL(iommu_domain_has_cap);
int iommu_map(struct iommu_domain *domain, unsigned long iova,
- phys_addr_t paddr, int gfp_order, int prot)
+ phys_addr_t paddr, size_t size, int prot)
{
- size_t size;
+ int ret = 0;
+
+ /*
+ * both the virtual address and the physical one, as well as
+ * the size of the mapping, must be aligned (at least) to the
+ * size of the smallest page supported by the hardware
+ */
+ if (!IS_ALIGNED(iova | paddr | size, iommu_min_pagesz)) {
+ pr_err("unaligned: iova 0x%lx pa 0x%lx size 0x%lx min_pagesz "
+ "0x%x\n", iova, (unsigned long)paddr,
+ (unsigned long)size, iommu_min_pagesz);
+ return -EINVAL;
+ }
+
+ pr_debug("map: iova 0x%lx pa 0x%lx size 0x%lx\n", iova,
+ (unsigned long)paddr, (unsigned long)size);
+
+ while (size) {
+ unsigned long pgsize = iommu_min_pagesz;
+ unsigned long idx = iommu_min_page_idx;
+ unsigned long addr_merge = iova | paddr;
+ int order;
+
+ /* find the max page size with which iova, paddr are aligned */
+ for (;;) {
+ unsigned long try_pgsize;
+
+ idx = find_next_bit(iommu_pgsize_bitmap,
+ iommu_nr_page_bits, idx + 1);
+
+ /* no more pages to check ? */
+ if (idx >= iommu_nr_page_bits)
+ break;
+
+ try_pgsize = 1 << idx;
- size = 0x1000UL << gfp_order;
+ /* page too big ? addresses not aligned ? */
+ if (size < try_pgsize ||
+ !IS_ALIGNED(addr_merge, try_pgsize))
+ break;
- BUG_ON(!IS_ALIGNED(iova | paddr, size));
+ pgsize = try_pgsize;
+ }
- return iommu_ops->map(domain, iova, paddr, gfp_order, prot);
+ order = get_order(pgsize);
+
+ pr_debug("mapping: iova 0x%lx pa 0x%lx order %d\n", iova,
+ (unsigned long)paddr, order);
+
+ ret = iommu_ops->map(domain, iova, paddr, order, prot);
+ if (ret)
+ break;
+
+ size -= pgsize;
+ iova += pgsize;
+ paddr += pgsize;
+ }
+
+ return ret;
}
EXPORT_SYMBOL_GPL(iommu_map);
-int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order)
+int iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
{
- size_t size;
+ int order, unmapped_size, unmapped_order, total_unmapped = 0;
+
+ /*
+ * The virtual address, as well as the size of the mapping, must be
+ * aligned (at least) to the size of the smallest page supported
+ * by the hardware
+ */
+ if (!IS_ALIGNED(iova | size, iommu_min_pagesz)) {
+ pr_err("unaligned: iova 0x%lx size 0x%lx min_pagesz 0x%x\n",
+ iova, (unsigned long)size, iommu_min_pagesz);
+ return -EINVAL;
+ }
+
+ pr_debug("unmap this: iova 0x%lx size 0x%lx\n", iova,
+ (unsigned long)size);
+
+ while (size > total_unmapped) {
+ order = get_order(size - total_unmapped);
+
+ unmapped_order = iommu_ops->unmap(domain, iova, order);
+ if (unmapped_order < 0)
+ return unmapped_order;
+
+ pr_debug("unmapped: iova 0x%lx order %d\n", iova,
+ unmapped_order);
- size = 0x1000UL << gfp_order;
+ unmapped_size = 0x1000UL << unmapped_order;
- BUG_ON(!IS_ALIGNED(iova, size));
+ iova += unmapped_size;
+ total_unmapped += unmapped_size;
+ }
- return iommu_ops->unmap(domain, iova, gfp_order);
+ return get_order(total_unmapped);
}
EXPORT_SYMBOL_GPL(iommu_unmap);
diff --git a/drivers/iommu/omap-iovmm.c b/drivers/iommu/omap-iovmm.c
index e8fdb88..f4dea5a 100644
--- a/drivers/iommu/omap-iovmm.c
+++ b/drivers/iommu/omap-iovmm.c
@@ -409,7 +409,6 @@ static int map_iovm_area(struct iommu_domain *domain, struct iovm_struct *new,
unsigned int i, j;
struct scatterlist *sg;
u32 da = new->da_start;
- int order;
if (!domain || !sgt)
return -EINVAL;
@@ -428,12 +427,10 @@ static int map_iovm_area(struct iommu_domain *domain, struct iovm_struct *new,
if (bytes_to_iopgsz(bytes) < 0)
goto err_out;
- order = get_order(bytes);
-
pr_debug("%s: [%d] %08x %08x(%x)\n", __func__,
i, da, pa, bytes);
- err = iommu_map(domain, da, pa, order, flags);
+ err = iommu_map(domain, da, pa, bytes, flags);
if (err)
goto err_out;
@@ -448,10 +445,9 @@ err_out:
size_t bytes;
bytes = sg->length + sg->offset;
- order = get_order(bytes);
/* ignore failures.. we're already handling one */
- iommu_unmap(domain, da, order);
+ iommu_unmap(domain, da, bytes);
da += bytes;
}
@@ -474,12 +470,10 @@ static void unmap_iovm_area(struct iommu_domain *domain, struct omap_iommu *obj,
start = area->da_start;
for_each_sg(sgt->sgl, sg, sgt->nents, i) {
size_t bytes;
- int order;
bytes = sg->length + sg->offset;
- order = get_order(bytes);
- err = iommu_unmap(domain, start, order);
+ err = iommu_unmap(domain, start, bytes);
if (err < 0)
break;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index d084e87..1806956 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -61,6 +61,8 @@ struct iommu_ops {
#ifdef CONFIG_IOMMU_API
extern void register_iommu(struct iommu_ops *ops);
+extern void register_iommu_pgsize(struct iommu_ops *ops,
+ unsigned long *pgsize_bitmap, unsigned int nr_page_bits);
extern bool iommu_found(void);
extern struct iommu_domain *iommu_domain_alloc(void);
extern void iommu_domain_free(struct iommu_domain *domain);
@@ -69,9 +71,9 @@ extern int iommu_attach_device(struct iommu_domain *domain,
extern void iommu_detach_device(struct iommu_domain *domain,
struct device *dev);
extern int iommu_map(struct iommu_domain *domain, unsigned long iova,
- phys_addr_t paddr, int gfp_order, int prot);
+ phys_addr_t paddr, size_t size, int prot);
extern int iommu_unmap(struct iommu_domain *domain, unsigned long iova,
- int gfp_order);
+ size_t size);
extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain,
unsigned long iova);
extern int iommu_domain_has_cap(struct iommu_domain *domain,
diff --git a/virt/kvm/iommu.c b/virt/kvm/iommu.c
index 78c80f6..ea142d3 100644
--- a/virt/kvm/iommu.c
+++ b/virt/kvm/iommu.c
@@ -111,7 +111,7 @@ int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot)
/* Map into IO address space */
r = iommu_map(domain, gfn_to_gpa(gfn), pfn_to_hpa(pfn),
- get_order(page_size), flags);
+ page_size, flags);
if (r) {
printk(KERN_ERR "kvm_iommu_map_address:"
"iommu failed to map pfn=%llx\n", pfn);
@@ -293,7 +293,7 @@ static void kvm_iommu_put_pages(struct kvm *kvm,
pfn = phys >> PAGE_SHIFT;
/* Unmap address from IO address space */
- order = iommu_unmap(domain, gfn_to_gpa(gfn), 0);
+ order = iommu_unmap(domain, gfn_to_gpa(gfn), PAGE_SIZE);
unmap_pages = 1ULL << order;
/* Unpin all pages we just unmapped to not leak any memory */
--
1.7.4.1
^ permalink raw reply related
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