* [PATCH 1/9] goldfish: definitions for Goldfish on x86 platforms
2012-12-03 15:04 [RFC PATCH 0/9] Upstream merge of core of Goldfish AVD support Alan Cox
@ 2012-12-03 15:04 ` Alan Cox
2012-12-04 10:07 ` Geert Uytterhoeven
2012-12-03 15:04 ` [PATCH 2/9] goldfish: add the goldfish virtual bus Alan Cox
` (7 subsequent siblings)
8 siblings, 1 reply; 11+ messages in thread
From: Alan Cox @ 2012-12-03 15:04 UTC (permalink / raw)
To: arve, x86, linux-kernel, mikechan
From: Jun Nakajima <jnakajim@gmail.com>
Based on code by Jun Nakajima but stripped of all the old x86 mach-foo
stuff and turned into a single include file for the Goldfish virtual bus
layer
Signed-off-by: Sheng Yang <sheng@linux.intel.com>
Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com>
Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com>
Signed-off-by: Jun Nakajima <jun.nakajima@intel.com>
Signed-off-by: Bruce Beare <bruce.j.beare@intel.com>
[Ported to 3.7 and reorganised so that we can keep most of the code
shared properly]
Signed-off-by: Alan Cox <alan@linux.intel.com>
---
arch/x86/Kconfig | 12 ++++++++++++
arch/x86/include/asm/goldfish.h | 36 ++++++++++++++++++++++++++++++++++++
2 files changed, 48 insertions(+)
create mode 100644 arch/x86/include/asm/goldfish.h
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index cd98a44..dbe8baa 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -326,6 +326,10 @@ config X86_BIGSMP
---help---
This option is needed for the systems that have more than 8 CPUs
+config GOLDFISH
+ def_bool y
+ depends on X86_GOLDFISH
+
if X86_32
config X86_EXTENDED_PLATFORM
bool "Support for extended (non-PC) x86 platforms"
@@ -407,6 +411,14 @@ config X86_UV
# Following is an alphabetically sorted list of 32 bit extended platforms
# Please maintain the alphabetic order if and when there are additions
+config X86_GOLDFISH
+ bool "Goldfish (Virtual Platform)"
+ depends on X86_32
+ ---help---
+ Enable support for the Goldfish virtual platform used primarily
+ for Android development. Unless you are building for the Android
+ Goldfish emulator say N here.
+
config X86_INTEL_CE
bool "CE4100 TV platform"
depends on PCI
diff --git a/arch/x86/include/asm/goldfish.h b/arch/x86/include/asm/goldfish.h
new file mode 100644
index 0000000..d5fcf6d
--- /dev/null
+++ b/arch/x86/include/asm/goldfish.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * arch/x86/include/asm/mach-goldfish/hardware.h
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2012 Intel, Corp.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __X86_ARCH_HARDWARE_H
+#define __X86_ARCH_HARDWARE_H
+
+/*
+ * Where in virtual memory the IO devices (timers, system controllers
+ * and so on)
+ */
+
+#define GOLDFISH_IO_SIZE 0x00800000 /* How much? */
+#define GOLDFISH_IO_START 0xff000000 /* PA of IO */
+
+#define GOLDFISH_PDEV_BUS_BASE (0x1000)
+#define GOLDFISH_PDEV_BUS_END (0x100)
+#define GOLDFISH_PDEV_BUS_IRQ (4)
+
+#define GOLDFISH_TTY_BASE (0x2000)
+
+#endif
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 1/9] goldfish: definitions for Goldfish on x86 platforms
2012-12-03 15:04 ` [PATCH 1/9] goldfish: definitions for Goldfish on x86 platforms Alan Cox
@ 2012-12-04 10:07 ` Geert Uytterhoeven
0 siblings, 0 replies; 11+ messages in thread
From: Geert Uytterhoeven @ 2012-12-04 10:07 UTC (permalink / raw)
To: Alan Cox, Jun Nakajima; +Cc: arve, x86, linux-kernel, mikechan
On Mon, Dec 3, 2012 at 4:04 PM, Alan Cox <alan@linux.intel.com> wrote:
> --- /dev/null
> +++ b/arch/x86/include/asm/goldfish.h
> @@ -0,0 +1,36 @@
> +/*
> + *
> + * arch/x86/include/asm/mach-goldfish/hardware.h
Please don't put file names in header files, they get out of sync too easily.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/9] goldfish: add the goldfish virtual bus
2012-12-03 15:04 [RFC PATCH 0/9] Upstream merge of core of Goldfish AVD support Alan Cox
2012-12-03 15:04 ` [PATCH 1/9] goldfish: definitions for Goldfish on x86 platforms Alan Cox
@ 2012-12-03 15:04 ` Alan Cox
2012-12-03 15:04 ` [PATCH 3/9] goldfish: tty driver Alan Cox
` (6 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Alan Cox @ 2012-12-03 15:04 UTC (permalink / raw)
To: arve, x86, linux-kernel, mikechan
From: Jun Nakajima <jnakajim@gmail.com>
This imports the current Google code and cleans it up slightly to use pr_ and
to properly request its resources.
Goldfish is an emulator used for Android development. It has a virtual bus where
the emulator passes platform device information to the guest which then creates
the appropriate devices.
This part of the emulation is not architecture specific so should not be hiding
in architecture trees as it does in the Google Android tree. The constants it
uses do depend on the platform and so live in arch/goldfish.h
Signed-off-by: Sheng Yang <sheng@linux.intel.com>
Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com>
Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com>
Signed-off-by: Jun Nakajima <jun.nakajima@intel.com>
Signed-off-by: Bruce Beare <bruce.j.beare@intel.com>
[Moved out of x86, cleaned up headers]
Signed-off-by: Alan Cox <alan@linux.intel.com>
---
drivers/platform/Makefile | 1
drivers/platform/goldfish/Makefile | 4 +
drivers/platform/goldfish/pdev_bus.c | 267 ++++++++++++++++++++++++++++++++++
3 files changed, 272 insertions(+)
create mode 100644 drivers/platform/goldfish/Makefile
create mode 100644 drivers/platform/goldfish/pdev_bus.c
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index b17c16c..8a44a4c 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_X86) += x86/
obj-$(CONFIG_OLPC) += olpc/
+obj-$(CONFIG_GOLDFISH) += goldfish/
diff --git a/drivers/platform/goldfish/Makefile b/drivers/platform/goldfish/Makefile
new file mode 100644
index 0000000..6c591f6
--- /dev/null
+++ b/drivers/platform/goldfish/Makefile
@@ -0,0 +1,4 @@
+#
+# Makefile for Goldfish platform specific drivers
+#
+obj-$(CONFIG_GOLDFISH) += pdev_bus.o
diff --git a/drivers/platform/goldfish/pdev_bus.c b/drivers/platform/goldfish/pdev_bus.c
new file mode 100644
index 0000000..dc554d0
--- /dev/null
+++ b/drivers/platform/goldfish/pdev_bus.c
@@ -0,0 +1,267 @@
+/*
+ * drivers/platform/goldfish/pdev_bus.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2011 Intel, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#include <asm/goldfish.h>
+
+#define PDEV_BUS_OP_DONE (0x00)
+#define PDEV_BUS_OP_REMOVE_DEV (0x04)
+#define PDEV_BUS_OP_ADD_DEV (0x08)
+
+#define PDEV_BUS_OP_INIT (0x00)
+
+#define PDEV_BUS_OP (0x00)
+#define PDEV_BUS_GET_NAME (0x04)
+#define PDEV_BUS_NAME_LEN (0x08)
+#define PDEV_BUS_ID (0x0c)
+#define PDEV_BUS_IO_BASE (0x10)
+#define PDEV_BUS_IO_SIZE (0x14)
+#define PDEV_BUS_IRQ (0x18)
+#define PDEV_BUS_IRQ_COUNT (0x1c)
+
+struct pdev_bus_dev {
+ struct list_head list;
+ struct platform_device pdev;
+ struct resource resources[0];
+};
+
+static void goldfish_pdev_worker(struct work_struct *work);
+
+static void __iomem *pdev_bus_base;
+static unsigned long pdev_bus_addr;
+static u32 pdev_bus_irq;
+static LIST_HEAD(pdev_bus_new_devices);
+static LIST_HEAD(pdev_bus_registered_devices);
+static LIST_HEAD(pdev_bus_removed_devices);
+static DECLARE_WORK(pdev_bus_worker, goldfish_pdev_worker);
+
+
+static void goldfish_pdev_worker(struct work_struct *work)
+{
+ int ret;
+ struct pdev_bus_dev *pos, *n;
+
+ list_for_each_entry_safe(pos, n, &pdev_bus_removed_devices, list) {
+ list_del(&pos->list);
+ platform_device_unregister(&pos->pdev);
+ kfree(pos);
+ }
+ list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) {
+ list_del(&pos->list);
+ ret = platform_device_register(&pos->pdev);
+ if (ret)
+ pr_err("goldfish_pdev_worker failed to register device, %s\n",
+ pos->pdev.name);
+ list_add_tail(&pos->list, &pdev_bus_registered_devices);
+ }
+}
+
+static void goldfish_pdev_remove(void)
+{
+ struct pdev_bus_dev *pos, *n;
+ u32 base;
+
+ base = readl(pdev_bus_base + PDEV_BUS_IO_BASE);
+
+ list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) {
+ if (pos->resources[0].start == base) {
+ list_del(&pos->list);
+ kfree(pos);
+ return;
+ }
+ }
+ list_for_each_entry_safe(pos, n, &pdev_bus_registered_devices, list) {
+ if (pos->resources[0].start == base) {
+ list_del(&pos->list);
+ list_add_tail(&pos->list, &pdev_bus_removed_devices);
+ schedule_work(&pdev_bus_worker);
+ return;
+ }
+ };
+ pr_err("goldfish_pdev_remove could not find device at %x\n", base);
+}
+
+static int goldfish_new_pdev(void)
+{
+ struct pdev_bus_dev *dev;
+ u32 name_len;
+ u32 irq = -1, irq_count;
+ int resource_count = 2;
+ u32 base;
+ char *name;
+
+ base = readl(pdev_bus_base + PDEV_BUS_IO_BASE);
+
+ irq_count = readl(pdev_bus_base + PDEV_BUS_IRQ_COUNT);
+ name_len = readl(pdev_bus_base + PDEV_BUS_NAME_LEN);
+ if (irq_count)
+ resource_count++;
+
+ dev = kzalloc(sizeof(*dev) +
+ sizeof(struct resource) * resource_count +
+ name_len + 1 + sizeof(*dev->pdev.dev.dma_mask), GFP_ATOMIC);
+ if (dev == NULL)
+ return -ENOMEM;
+
+ dev->pdev.num_resources = resource_count;
+ dev->pdev.resource = (struct resource *)(dev + 1);
+ dev->pdev.name = name = (char *)(dev->pdev.resource + resource_count);
+ dev->pdev.dev.coherent_dma_mask = ~0;
+ dev->pdev.dev.dma_mask = (void *)(dev->pdev.name + name_len + 1);
+ *dev->pdev.dev.dma_mask = ~0;
+
+ writel((unsigned long)name, pdev_bus_base + PDEV_BUS_GET_NAME);
+ name[name_len] = '\0';
+ dev->pdev.id = readl(pdev_bus_base + PDEV_BUS_ID);
+ dev->pdev.resource[0].start = base;
+ dev->pdev.resource[0].end = base +
+ readl(pdev_bus_base + PDEV_BUS_IO_SIZE) - 1;
+ dev->pdev.resource[0].flags = IORESOURCE_MEM;
+ if (irq_count) {
+ irq = readl(pdev_bus_base + PDEV_BUS_IRQ);
+ dev->pdev.resource[1].start = irq;
+ dev->pdev.resource[1].end = irq + irq_count - 1;
+ dev->pdev.resource[1].flags = IORESOURCE_IRQ;
+ }
+
+ pr_debug("goldfish_new_pdev %s at %x irq %d\n", name, base, irq);
+ list_add_tail(&dev->list, &pdev_bus_new_devices);
+ schedule_work(&pdev_bus_worker);
+
+ return 0;
+}
+
+static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id)
+{
+ irqreturn_t ret = IRQ_NONE;
+ while (1) {
+ u32 op = readl(pdev_bus_base + PDEV_BUS_OP);
+ switch (op) {
+ case PDEV_BUS_OP_DONE:
+ return IRQ_NONE;
+
+ case PDEV_BUS_OP_REMOVE_DEV:
+ goldfish_pdev_remove();
+ break;
+
+ case PDEV_BUS_OP_ADD_DEV:
+ goldfish_new_pdev();
+ break;
+ }
+ ret = IRQ_HANDLED;
+ }
+}
+
+static int __devinit goldfish_pdev_bus_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+
+ r = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (r == NULL)
+ return -EINVAL;
+
+ pdev_bus_addr = GOLDFISH_IO_START + r->start;
+
+ if (request_region(pdev_bus_addr, GOLDFISH_IO_SIZE, "goldfish")) {
+ pr_err("unable to reserve Goldfish MMIO.\n");
+ return -EBUSY;
+ }
+
+ pr_err("Goldfish bus at %lX\n", (long)pdev_bus_addr);
+ pdev_bus_base = ioremap(pdev_bus_addr, GOLDFISH_IO_SIZE);
+ if (pdev_bus_base == NULL) {
+ pr_err("unable to map Goldfish MMIO.\n");
+ return -EIO;
+ }
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (r == NULL)
+ return -EINVAL;
+ pdev_bus_irq = r->start;
+
+ ret = request_irq(pdev_bus_irq, goldfish_pdev_bus_interrupt,
+ IRQF_SHARED, "goldfish_pdev_bus", pdev);
+ if (ret)
+ goto err_request_irq_failed;
+
+ writel(PDEV_BUS_OP_INIT, pdev_bus_base + PDEV_BUS_OP);
+
+err_request_irq_failed:
+ return ret;
+}
+
+static int __devexit goldfish_pdev_bus_remove(struct platform_device *pdev)
+{
+ free_irq(pdev_bus_irq, pdev);
+ release_region(pdev_bus_addr, GOLDFISH_IO_SIZE);
+ return 0;
+}
+
+static struct platform_driver goldfish_pdev_bus_driver = {
+ .probe = goldfish_pdev_bus_probe,
+ .remove = __devexit_p(goldfish_pdev_bus_remove),
+ .driver = {
+ .name = "goldfish_pdev_bus"
+ }
+};
+
+static int __init goldfish_pdev_bus_init(void)
+{
+ return platform_driver_register(&goldfish_pdev_bus_driver);
+}
+
+static void __exit goldfish_pdev_bus_exit(void)
+{
+ platform_driver_unregister(&goldfish_pdev_bus_driver);
+}
+
+module_init(goldfish_pdev_bus_init);
+module_exit(goldfish_pdev_bus_exit);
+
+static struct resource goldfish_pdev_bus_resources[] = {
+ {
+ .start = GOLDFISH_PDEV_BUS_BASE,
+ .end = GOLDFISH_PDEV_BUS_BASE + GOLDFISH_PDEV_BUS_END - 1,
+ .flags = IORESOURCE_IO,
+ },
+ {
+ .start = GOLDFISH_PDEV_BUS_IRQ,
+ .end = GOLDFISH_PDEV_BUS_IRQ,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+struct platform_device goldfish_pdev_bus_device = {
+ .name = "goldfish_pdev_bus",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(goldfish_pdev_bus_resources),
+ .resource = goldfish_pdev_bus_resources
+};
+
+static int __init goldfish_init(void)
+{
+ return platform_device_register(&goldfish_pdev_bus_device);
+}
+device_initcall(goldfish_init);
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/9] goldfish: tty driver
2012-12-03 15:04 [RFC PATCH 0/9] Upstream merge of core of Goldfish AVD support Alan Cox
2012-12-03 15:04 ` [PATCH 1/9] goldfish: definitions for Goldfish on x86 platforms Alan Cox
2012-12-03 15:04 ` [PATCH 2/9] goldfish: add the goldfish virtual bus Alan Cox
@ 2012-12-03 15:04 ` Alan Cox
2012-12-03 15:05 ` [PATCH 4/9] goldfish: virtual input event driver Alan Cox
` (5 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Alan Cox @ 2012-12-03 15:04 UTC (permalink / raw)
To: arve, x86, linux-kernel, mikechan
From: Arve Hjønnevåg <arve@google.com>
This provides a console driver for the Goldfish virtual platform. The original
is from Arve with changes from Jun Nakajima and Tom Keel. This has been then
been ported to the current kernel and to the tty port mechanism by Alan Cox.
In the process it gained proper POSIX semantics and vhangup works. The default
name is not ttyS as this belongs to the 8250 driver. Instead ttyGFx is now used.
In the normal usage case the first port serves as a kernel logging console and
the second one carries various other data streams for the emulation.
Signed-off-by: Arve Hjønnevåg <arve@google.com>
[Cleaned up to handle x86]
Signed-off-by: Sheng Yang <sheng@linux.intel.com>
Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com>
Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com>
Signed-off-by: Jun Nakajima <jun.nakajima@intel.com>
Signed-off-by: Bruce Beare <bruce.j.beare@intel.com>
[Moved to 3.7 and chunks rewritten to use tty_port layer]
Signed-off-by: Alan Cox <alan@linux.intel.com>
---
drivers/tty/Kconfig | 6 +
drivers/tty/Makefile | 1
drivers/tty/goldfish.c | 342 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 349 insertions(+)
create mode 100644 drivers/tty/goldfish.c
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 0ecf22b..89a1007 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -388,3 +388,9 @@ config PPC_EARLY_DEBUG_EHV_BC_HANDLE
If the number you specify is not a valid byte channel handle, then
there simply will be no early console output. This is true also
if you don't boot under a hypervisor at all.
+
+config GOLDFISH_TTY
+ tristate "Goldfish TTY Driver"
+ depends on GOLDFISH
+ help
+ Console and system TTY driver for the Goldfish virtual platform.
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 2953059..f8e59ae 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -27,5 +27,6 @@ obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o
obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o
obj-$(CONFIG_SYNCLINK) += synclink.o
obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o
+obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o
obj-y += ipwireless/
diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c
new file mode 100644
index 0000000..0c2d36c
--- /dev/null
+++ b/drivers/tty/goldfish.c
@@ -0,0 +1,342 @@
+/*
+ * drivers/char/goldfish_tty.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2012 Intel, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+enum {
+ GOLDFISH_TTY_PUT_CHAR = 0x00,
+ GOLDFISH_TTY_BYTES_READY = 0x04,
+ GOLDFISH_TTY_CMD = 0x08,
+
+ GOLDFISH_TTY_DATA_PTR = 0x10,
+ GOLDFISH_TTY_DATA_LEN = 0x14,
+
+ GOLDFISH_TTY_CMD_INT_DISABLE = 0,
+ GOLDFISH_TTY_CMD_INT_ENABLE = 1,
+ GOLDFISH_TTY_CMD_WRITE_BUFFER = 2,
+ GOLDFISH_TTY_CMD_READ_BUFFER = 3,
+};
+
+struct goldfish_tty {
+ struct tty_port port;
+ spinlock_t lock;
+ void __iomem *base;
+ u32 irq;
+ int opencount;
+ struct console console;
+};
+
+static DEFINE_MUTEX(goldfish_tty_lock);
+static struct tty_driver *goldfish_tty_driver;
+static u32 goldfish_tty_line_count = 8;
+static u32 goldfish_tty_current_line_count;
+static struct goldfish_tty *goldfish_ttys;
+
+static void goldfish_tty_do_write(int line, const char *buf, unsigned count)
+{
+ unsigned long irq_flags;
+ struct goldfish_tty *qtty = &goldfish_ttys[line];
+ void __iomem *base = qtty->base;
+ spin_lock_irqsave(&qtty->lock, irq_flags);
+ writel((u32)buf, base + GOLDFISH_TTY_DATA_PTR);
+ writel(count, base + GOLDFISH_TTY_DATA_LEN);
+ writel(GOLDFISH_TTY_CMD_WRITE_BUFFER, base + GOLDFISH_TTY_CMD);
+ spin_unlock_irqrestore(&qtty->lock, irq_flags);
+}
+
+static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct goldfish_tty *qtty = &goldfish_ttys[pdev->id];
+ void __iomem *base = qtty->base;
+ unsigned long irq_flags;
+ unsigned char *buf;
+ u32 count;
+ struct tty_struct *tty;
+
+ count = readl(base + GOLDFISH_TTY_BYTES_READY);
+ if(count == 0)
+ return IRQ_NONE;
+
+ tty = tty_port_tty_get(&qtty->port);
+ if (tty) {
+ count = tty_prepare_flip_string(tty, &buf, count);
+ spin_lock_irqsave(&qtty->lock, irq_flags);
+ writel((u32)buf, base + GOLDFISH_TTY_DATA_PTR);
+ writel(count, base + GOLDFISH_TTY_DATA_LEN);
+ writel(GOLDFISH_TTY_CMD_READ_BUFFER, base + GOLDFISH_TTY_CMD);
+ spin_unlock_irqrestore(&qtty->lock, irq_flags);
+ tty_schedule_flip(tty);
+ tty_kref_put(tty);
+ }
+ return IRQ_HANDLED;
+}
+
+static int goldfish_tty_activate(struct tty_port *port, struct tty_struct *tty)
+{
+ struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, port);
+ writel(GOLDFISH_TTY_CMD_INT_ENABLE, qtty->base + GOLDFISH_TTY_CMD);
+ return 0;
+}
+
+static void goldfish_tty_shutdown(struct tty_port *port)
+{
+ struct goldfish_tty *qtty = container_of(port, struct goldfish_tty, port);
+ writel(GOLDFISH_TTY_CMD_INT_DISABLE, qtty->base + GOLDFISH_TTY_CMD);
+}
+
+static int goldfish_tty_open(struct tty_struct * tty, struct file * filp)
+{
+ struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
+ return tty_port_open(&qtty->port, tty, filp);
+}
+
+static void goldfish_tty_close(struct tty_struct * tty, struct file * filp)
+{
+ tty_port_close(tty->port, tty, filp);
+}
+
+static void goldfish_tty_hangup(struct tty_struct *tty)
+{
+ tty_port_hangup(tty->port);
+}
+
+static int goldfish_tty_write(struct tty_struct * tty, const unsigned char *buf, int count)
+{
+ goldfish_tty_do_write(tty->index, buf, count);
+ return count;
+}
+
+static int goldfish_tty_write_room(struct tty_struct *tty)
+{
+ return 0x10000;
+}
+
+static int goldfish_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ struct goldfish_tty *qtty = &goldfish_ttys[tty->index];
+ void __iomem *base = qtty->base;
+ return readl(base + GOLDFISH_TTY_BYTES_READY);
+}
+
+static void goldfish_tty_console_write(struct console *co, const char *b, unsigned count)
+{
+ goldfish_tty_do_write(co->index, b, count);
+}
+
+static struct tty_driver *goldfish_tty_console_device(struct console *c, int *index)
+{
+ *index = c->index;
+ return goldfish_tty_driver;
+}
+
+static int goldfish_tty_console_setup(struct console *co, char *options)
+{
+ if((unsigned)co->index > goldfish_tty_line_count)
+ return -ENODEV;
+ if(goldfish_ttys[co->index].base == 0)
+ return -ENODEV;
+ return 0;
+}
+
+static struct tty_port_operations goldfish_port_ops = {
+ .activate = goldfish_tty_activate,
+ .shutdown = goldfish_tty_shutdown
+};
+
+static struct tty_operations goldfish_tty_ops = {
+ .open = goldfish_tty_open,
+ .close = goldfish_tty_close,
+ .hangup = goldfish_tty_hangup,
+ .write = goldfish_tty_write,
+ .write_room = goldfish_tty_write_room,
+ .chars_in_buffer = goldfish_tty_chars_in_buffer,
+};
+
+static int __devinit goldfish_tty_create_driver(void)
+{
+ int ret;
+ struct tty_driver *tty;
+
+ goldfish_ttys = kzalloc(sizeof(*goldfish_ttys) * goldfish_tty_line_count, GFP_KERNEL);
+ if(goldfish_ttys == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_goldfish_ttys_failed;
+ }
+ tty = alloc_tty_driver(goldfish_tty_line_count);
+ if(tty == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_tty_driver_failed;
+ }
+ tty->driver_name = "goldfish";
+ tty->name = "ttyGF";
+ tty->type = TTY_DRIVER_TYPE_SERIAL;
+ tty->subtype = SERIAL_TYPE_NORMAL;
+ tty->init_termios = tty_std_termios;
+ tty->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ tty_set_operations(tty, &goldfish_tty_ops);
+ ret = tty_register_driver(tty);
+ if(ret)
+ goto err_tty_register_driver_failed;
+
+ goldfish_tty_driver = tty;
+ return 0;
+
+err_tty_register_driver_failed:
+ put_tty_driver(tty);
+err_alloc_tty_driver_failed:
+ kfree(goldfish_ttys);
+ goldfish_ttys = NULL;
+err_alloc_goldfish_ttys_failed:
+ return ret;
+}
+
+static void goldfish_tty_delete_driver(void)
+{
+ tty_unregister_driver(goldfish_tty_driver);
+ put_tty_driver(goldfish_tty_driver);
+ goldfish_tty_driver = NULL;
+ kfree(goldfish_ttys);
+ goldfish_ttys = NULL;
+}
+
+static int __devinit goldfish_tty_probe(struct platform_device *pdev)
+{
+ struct goldfish_tty *qtty;
+ int ret = -EINVAL;
+ int i;
+ struct resource *r;
+ struct device *ttydev;
+ void __iomem *base;
+ u32 irq;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if(r == NULL)
+ return -EINVAL;
+
+ base = ioremap(r->start, 0x1000);
+ if (base == NULL)
+ pr_err("goldfish_tty: unable to remap base\n");
+
+ r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if(r == NULL)
+ goto err_unmap;
+
+ irq = r->start;
+
+ if(pdev->id >= goldfish_tty_line_count)
+ goto err_unmap;
+
+ mutex_lock(&goldfish_tty_lock);
+ if(goldfish_tty_current_line_count == 0) {
+ ret = goldfish_tty_create_driver();
+ if(ret)
+ goto err_create_driver_failed;
+ }
+ goldfish_tty_current_line_count++;
+
+ qtty = &goldfish_ttys[pdev->id];
+ spin_lock_init(&qtty->lock);
+ tty_port_init(&qtty->port);
+ qtty->port.ops = &goldfish_port_ops;
+ qtty->base = base;
+ qtty->irq = irq;
+
+ writel(GOLDFISH_TTY_CMD_INT_DISABLE, base + GOLDFISH_TTY_CMD);
+
+ ret = request_irq(irq, goldfish_tty_interrupt, IRQF_SHARED, "goldfish_tty", pdev);
+ if(ret)
+ goto err_request_irq_failed;
+
+
+ ttydev = tty_port_register_device(&qtty->port, goldfish_tty_driver, pdev->id, &pdev->dev);
+ if(IS_ERR(ttydev)) {
+ ret = PTR_ERR(ttydev);
+ goto err_tty_register_device_failed;
+ }
+
+ strcpy(qtty->console.name, "ttyGF");
+ qtty->console.write = goldfish_tty_console_write;
+ qtty->console.device = goldfish_tty_console_device;
+ qtty->console.setup = goldfish_tty_console_setup;
+ qtty->console.flags = CON_PRINTBUFFER;
+ qtty->console.index = pdev->id;
+ register_console(&qtty->console);
+
+ mutex_unlock(&goldfish_tty_lock);
+ return 0;
+
+ tty_unregister_device(goldfish_tty_driver, i);
+err_tty_register_device_failed:
+ free_irq(irq, pdev);
+err_request_irq_failed:
+ goldfish_tty_current_line_count--;
+ if(goldfish_tty_current_line_count == 0)
+ goldfish_tty_delete_driver();
+err_create_driver_failed:
+ mutex_unlock(&goldfish_tty_lock);
+err_unmap:
+ iounmap(base);
+ return ret;
+}
+
+static int goldfish_tty_remove(struct platform_device *pdev)
+{
+ struct goldfish_tty *qtty;
+
+ mutex_lock(&goldfish_tty_lock);
+
+ qtty = &goldfish_ttys[pdev->id];
+ unregister_console(&qtty->console);
+ tty_unregister_device(goldfish_tty_driver, pdev->id);
+ iounmap(qtty->base);
+ qtty->base = 0;
+ free_irq(qtty->irq, pdev);
+ goldfish_tty_current_line_count--;
+ if(goldfish_tty_current_line_count == 0)
+ goldfish_tty_delete_driver();
+ mutex_unlock(&goldfish_tty_lock);
+ return 0;
+}
+
+static struct platform_driver goldfish_tty_platform_driver = {
+ .probe = goldfish_tty_probe,
+ .remove = goldfish_tty_remove,
+ .driver = {
+ .name = "goldfish_tty"
+ }
+};
+
+static int __init goldfish_tty_init(void)
+{
+ return platform_driver_register(&goldfish_tty_platform_driver);
+}
+
+static void __exit goldfish_tty_exit(void)
+{
+ platform_driver_unregister(&goldfish_tty_platform_driver);
+}
+
+module_init(goldfish_tty_init);
+module_exit(goldfish_tty_exit);
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 4/9] goldfish: virtual input event driver
2012-12-03 15:04 [RFC PATCH 0/9] Upstream merge of core of Goldfish AVD support Alan Cox
` (2 preceding siblings ...)
2012-12-03 15:04 ` [PATCH 3/9] goldfish: tty driver Alan Cox
@ 2012-12-03 15:05 ` Alan Cox
2012-12-03 15:05 ` [PATCH 5/9] goldfish: framebuffer Alan Cox
` (4 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Alan Cox @ 2012-12-03 15:05 UTC (permalink / raw)
To: arve, x86, linux-kernel, mikechan
From: Brian Swetland <swetland@google.com>
This device is a direct pipe from "hardware" to the input
event subsystem, allowing us to avoid having to route
"keypad" style events through an AT keyboard driver (gross!)
As with the other submissions this driver is cross architecture.
Signed-off-by: Mike A. Chan <mikechan@google.com>
[Tided up to work on x86]
Signed-off-by: Sheng Yang <sheng@linux.intel.com>
Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com>
Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com>
Signed-off-by: Jun Nakajima <jun.nakajima@intel.com>
Signed-off-by: Bruce Beare <bruce.j.beare@intel.com>
[Ported to 3.4]
Signed-off-by: Tom Keel <thomas.keel@intel.com>
[Cleaned up for 3.7 and submission]
Signed-off-by: Alan Cox <alan@linux.intel.com>
---
drivers/input/keyboard/Kconfig | 11 ++
drivers/input/keyboard/Makefile | 1
drivers/input/keyboard/goldfish_events.c | 201 ++++++++++++++++++++++++++++++
3 files changed, 213 insertions(+)
create mode 100644 drivers/input/keyboard/goldfish_events.c
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 7ccd652..e6eba4e 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -479,6 +479,17 @@ config KEYBOARD_SAMSUNG
To compile this driver as a module, choose M here: the
module will be called samsung-keypad.
+config KEYBOARD_GOLDFISH_EVENTS
+ depends on GOLDFISH
+ tristate "Generic Input Event device for Goldfish"
+ help
+ Say Y here to get an input event device for the Goldfish virtual
+ device emulator.
+
+ To compile this driver as a module, choose M here: the
+ module will be called goldfish-events.
+
+
config KEYBOARD_STOWAWAY
tristate "Stowaway keyboard"
select SERIO
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 44e7600..49b1645 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o
+obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o
obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o
diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c
new file mode 100644
index 0000000..bf596b0
--- /dev/null
+++ b/drivers/input/keyboard/goldfish_events.c
@@ -0,0 +1,201 @@
+/*
+ * drivers/input/keyboard/goldfish-events.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2012 Intel, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+enum {
+ REG_READ = 0x00,
+ REG_SET_PAGE = 0x00,
+ REG_LEN = 0x04,
+ REG_DATA = 0x08,
+
+ PAGE_NAME = 0x00000,
+ PAGE_EVBITS = 0x10000,
+ PAGE_ABSDATA = 0x20000 | EV_ABS,
+};
+
+struct event_dev {
+ struct input_dev *input;
+ int irq;
+ void __iomem *addr;
+ char name[0];
+};
+
+static irqreturn_t events_interrupt(int irq, void *dev_id)
+{
+ struct event_dev *edev = dev_id;
+ unsigned type, code, value;
+
+ type = __raw_readl(edev->addr + REG_READ);
+ code = __raw_readl(edev->addr + REG_READ);
+ value = __raw_readl(edev->addr + REG_READ);
+
+ input_event(edev->input, type, code, value);
+ if (type == EV_KEY)
+ input_sync(edev->input);
+ return IRQ_HANDLED;
+}
+
+static void events_import_bits(struct event_dev *edev,
+ unsigned long bits[], unsigned type, size_t count)
+{
+ int i, j;
+ size_t size;
+ uint8_t val;
+ void __iomem *addr = edev->addr;
+ __raw_writel(PAGE_EVBITS | type, addr + REG_SET_PAGE);
+ size = __raw_readl(addr + REG_LEN) * 8;
+ if (size < count)
+ count = size;
+ addr = addr + REG_DATA;
+ for (i = 0; i < count; i += 8) {
+ val = __raw_readb(addr++);
+ for (j = 0; j < 8; j++)
+ if (val & 1 << j)
+ set_bit(i + j, bits);
+ }
+}
+
+static int events_probe(struct platform_device *pdev)
+{
+ struct input_dev *input_dev;
+ struct event_dev *edev = NULL;
+ struct resource *res;
+ unsigned keymapnamelen;
+ int i;
+ int count;
+ int irq;
+ void __iomem *addr;
+ int ret;
+
+ pr_debug("*** events probe ***\n");
+
+ input_dev = input_allocate_device();
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!input_dev || !res)
+ goto fail;
+
+ addr = ioremap(res->start, 4096);
+ irq = platform_get_irq(pdev, 0);
+
+ pr_debug("events_probe() addr=%p irq=%d\n", addr, irq);
+
+ if (!addr)
+ goto fail;
+ if (irq < 0)
+ goto fail;
+
+ __raw_writel(PAGE_NAME, addr + REG_SET_PAGE);
+ keymapnamelen = __raw_readl(addr + REG_LEN);
+
+ edev = kzalloc(sizeof(struct event_dev) + keymapnamelen + 1,
+ GFP_KERNEL);
+ if (!edev)
+ goto fail;
+
+ edev->input = input_dev;
+ edev->addr = addr;
+ edev->irq = irq;
+
+ for (i = 0; i < keymapnamelen; i++)
+ edev->name[i] = __raw_readb(edev->addr + REG_DATA + i);
+
+ pr_debug("events_probe() keymap=%s\n", edev->name);
+
+ events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX);
+ events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX);
+ events_import_bits(edev, input_dev->relbit, EV_REL, REL_MAX);
+ events_import_bits(edev, input_dev->absbit, EV_ABS, ABS_MAX);
+ events_import_bits(edev, input_dev->mscbit, EV_MSC, MSC_MAX);
+ events_import_bits(edev, input_dev->ledbit, EV_LED, LED_MAX);
+ events_import_bits(edev, input_dev->sndbit, EV_SND, SND_MAX);
+ events_import_bits(edev, input_dev->ffbit, EV_FF, FF_MAX);
+ events_import_bits(edev, input_dev->swbit, EV_SW, SW_MAX);
+
+ __raw_writel(PAGE_ABSDATA, addr + REG_SET_PAGE);
+ count = __raw_readl(addr + REG_LEN) / (4 * 4);
+ if (count > ABS_MAX)
+ count = ABS_MAX;
+ for (i = 0; i < count; i++) {
+ int val[4];
+ int j;
+ if (!test_bit(i, input_dev->absbit))
+ continue;
+ for (j = 0; j < ARRAY_SIZE(val); j++)
+ val[j] = __raw_readl(edev->addr + REG_DATA + (i * ARRAY_SIZE(val) + j) * 4);
+ input_set_abs_params(input_dev, i, val[0], val[1],
+ val[2], val[3]);
+ }
+
+ platform_set_drvdata(pdev, edev);
+
+ input_dev->name = edev->name;
+ input_set_drvdata(input_dev, edev);
+
+ ret = input_register_device(input_dev);
+ if (ret)
+ goto fail;
+
+ if (request_irq(edev->irq, events_interrupt, 0,
+ "goldfish-events-keypad", edev) < 0) {
+ input_unregister_device(input_dev);
+ kfree(edev);
+ return -EINVAL;
+ }
+
+ return 0;
+
+fail:
+ kfree(edev);
+ input_free_device(input_dev);
+
+ return -EINVAL;
+}
+
+static struct platform_driver events_driver = {
+ .probe = events_probe,
+ .driver = {
+ .name = "goldfish_events",
+ },
+};
+
+static int __devinit events_init(void)
+{
+ return platform_driver_register(&events_driver);
+}
+
+
+static void __exit events_exit(void)
+{
+ return platform_driver_unregister(&events_driver);
+}
+
+module_init(events_init);
+module_exit(events_exit);
+
+MODULE_AUTHOR("Brian Swetland");
+MODULE_DESCRIPTION("Goldfish Event Device");
+MODULE_LICENSE("GPL");
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 5/9] goldfish: framebuffer
2012-12-03 15:04 [RFC PATCH 0/9] Upstream merge of core of Goldfish AVD support Alan Cox
` (3 preceding siblings ...)
2012-12-03 15:05 ` [PATCH 4/9] goldfish: virtual input event driver Alan Cox
@ 2012-12-03 15:05 ` Alan Cox
2012-12-03 15:05 ` [PATCH 6/9] goldfish: emulated MMC device Alan Cox
` (3 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Alan Cox @ 2012-12-03 15:05 UTC (permalink / raw)
To: arve, x86, linux-kernel, mikechan
From: Arve Hjønnevåg <arve@google.com>
Framebuffer support for the Goldfish emulator. This takes the Google emulator
and applies the x86 cleanups as well as moving the blank methods to the usual
Linux place and dropping the Android early suspend logic (for now at least, that
can be looked at as Android and upstream converge). Dropped various oddities
like setting MTRRs on a virtual frame buffer emulation...
With the drivers so far you can now boot a Linux initrd and have fun.
Signed-off-by: Mike A. Chan <mikechan@google.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
[cleaned up to handle x86]
Signed-off-by: Sheng Yang <sheng@linux.intel.com>
Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com>
Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com>
Signed-off-by: Jun Nakajima <jun.nakajima@intel.com>
Signed-off-by: Bruce Beare <bruce.j.beare@intel.com>
[ported to 3.4]
Signed-off-by: Tom Keel <thomas.keel@intel.com>
[cleaned up for style and 3.7, moved blank methods]
Signed-off-by: Alan Cox <alan@linux.intel.com>
---
drivers/video/Kconfig | 9 +
drivers/video/Makefile | 1
drivers/video/goldfishfb.c | 327 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 337 insertions(+)
create mode 100644 drivers/video/goldfishfb.c
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 50740a8..5cbcfa8 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2183,6 +2183,15 @@ config FB_XILINX
framebuffer. ML300 carries a 640*480 LCD display on the board,
ML403 uses a standard DB15 VGA connector.
+config FB_GOLDFISH
+ tristate "Goldfish Framebuffer"
+ depends on FB
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ Framebuffer driver for Goldfish Virtual Platform
+
config FB_COBALT
tristate "Cobalt server LCD frame buffer support"
depends on FB && (MIPS_COBALT || MIPS_SEAD3)
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 768a137..3363f67 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -98,6 +98,7 @@ obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o
obj-$(CONFIG_FB_PVR2) += pvr2fb.o
obj-$(CONFIG_FB_VOODOO1) += sstfb.o
obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o
+obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o
obj-$(CONFIG_FB_68328) += 68328fb.o
obj-$(CONFIG_FB_GBE) += gbefb.o
obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o
diff --git a/drivers/video/goldfishfb.c b/drivers/video/goldfishfb.c
new file mode 100644
index 0000000..47136ef
--- /dev/null
+++ b/drivers/video/goldfishfb.c
@@ -0,0 +1,327 @@
+/* drivers/video/goldfishfb.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2012 Intel, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+
+enum {
+ FB_GET_WIDTH = 0x00,
+ FB_GET_HEIGHT = 0x04,
+ FB_INT_STATUS = 0x08,
+ FB_INT_ENABLE = 0x0c,
+ FB_SET_BASE = 0x10,
+ FB_SET_ROTATION = 0x14,
+ FB_SET_BLANK = 0x18,
+ FB_GET_PHYS_WIDTH = 0x1c,
+ FB_GET_PHYS_HEIGHT = 0x20,
+
+ FB_INT_VSYNC = 1U << 0,
+ FB_INT_BASE_UPDATE_DONE = 1U << 1
+};
+
+struct goldfish_fb {
+ void __iomem *reg_base;
+ int irq;
+ spinlock_t lock;
+ wait_queue_head_t wait;
+ int base_update_count;
+ int rotation;
+ struct fb_info fb;
+ u32 cmap[16];
+};
+
+static irqreturn_t goldfish_fb_interrupt(int irq, void *dev_id)
+{
+ unsigned long irq_flags;
+ struct goldfish_fb *fb = dev_id;
+ u32 status;
+
+ spin_lock_irqsave(&fb->lock, irq_flags);
+ status = readl(fb->reg_base + FB_INT_STATUS);
+ if (status & FB_INT_BASE_UPDATE_DONE) {
+ fb->base_update_count++;
+ wake_up(&fb->wait);
+ }
+ spin_unlock_irqrestore(&fb->lock, irq_flags);
+ return status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static inline u32 convert_bitfield(int val, struct fb_bitfield *bf)
+{
+ unsigned int mask = (1 << bf->length) - 1;
+
+ return (val >> (16 - bf->length) & mask) << bf->offset;
+}
+
+static int
+goldfish_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
+ unsigned int blue, unsigned int transp, struct fb_info *info)
+{
+ struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
+
+ if (regno < 16) {
+ fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) |
+ convert_bitfield(blue, &fb->fb.var.blue) |
+ convert_bitfield(green, &fb->fb.var.green) |
+ convert_bitfield(red, &fb->fb.var.red);
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int goldfish_fb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ if ((var->rotate & 1) != (info->var.rotate & 1)) {
+ if ((var->xres != info->var.yres) ||
+ (var->yres != info->var.xres) ||
+ (var->xres_virtual != info->var.yres) ||
+ (var->yres_virtual > info->var.xres * 2) ||
+ (var->yres_virtual < info->var.xres)) {
+ return -EINVAL;
+ }
+ } else {
+ if ((var->xres != info->var.xres) ||
+ (var->yres != info->var.yres) ||
+ (var->xres_virtual != info->var.xres) ||
+ (var->yres_virtual > info->var.yres * 2) ||
+ (var->yres_virtual < info->var.yres)) {
+ return -EINVAL;
+ }
+ }
+ if ((var->xoffset != info->var.xoffset) ||
+ (var->bits_per_pixel != info->var.bits_per_pixel) ||
+ (var->grayscale != info->var.grayscale)) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int goldfish_fb_set_par(struct fb_info *info)
+{
+ struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
+ if (fb->rotation != fb->fb.var.rotate) {
+ info->fix.line_length = info->var.xres * 2;
+ fb->rotation = fb->fb.var.rotate;
+ writel(fb->rotation, fb->reg_base + FB_SET_ROTATION);
+ }
+ return 0;
+}
+
+
+static int goldfish_fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ unsigned long irq_flags;
+ int base_update_count;
+ struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
+
+ spin_lock_irqsave(&fb->lock, irq_flags);
+ base_update_count = fb->base_update_count;
+ writel(fb->fb.fix.smem_start + fb->fb.var.xres * 2 * var->yoffset,
+ fb->reg_base + FB_SET_BASE);
+ spin_unlock_irqrestore(&fb->lock, irq_flags);
+ wait_event_timeout(fb->wait,
+ fb->base_update_count != base_update_count, HZ / 15);
+ if (fb->base_update_count == base_update_count)
+ pr_err("goldfish_fb_pan_display: timeout wating for base update\n");
+ return 0;
+}
+
+static int goldfish_fb_blank(int blank, struct fb_info *info)
+{
+ struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
+ switch (blank) {
+ case FB_BLANK_NORMAL:
+ writel(1, fb->reg_base + FB_SET_BLANK);
+ break;
+ case FB_BLANK_UNBLANK:
+ writel(0, fb->reg_base + FB_SET_BLANK);
+ break;
+ }
+ return 0;
+}
+
+static struct fb_ops goldfish_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = goldfish_fb_check_var,
+ .fb_set_par = goldfish_fb_set_par,
+ .fb_setcolreg = goldfish_fb_setcolreg,
+ .fb_pan_display = goldfish_fb_pan_display,
+ .fb_blank = goldfish_fb_blank,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+
+static int goldfish_fb_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+ struct goldfish_fb *fb;
+ size_t framesize;
+ u32 width, height;
+ dma_addr_t fbpaddr;
+
+ fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+ if (fb == NULL) {
+ ret = -ENOMEM;
+ goto err_fb_alloc_failed;
+ }
+ spin_lock_init(&fb->lock);
+ init_waitqueue_head(&fb->wait);
+ platform_set_drvdata(pdev, fb);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ ret = -ENODEV;
+ goto err_no_io_base;
+ }
+ fb->reg_base = ioremap(r->start, PAGE_SIZE);
+ if (fb->reg_base == NULL) {
+ ret = -ENOMEM;
+ goto err_no_io_base;
+ }
+
+ fb->irq = platform_get_irq(pdev, 0);
+ if (fb->irq <= 0) {
+ ret = -ENODEV;
+ goto err_no_irq;
+ }
+
+ width = readl(fb->reg_base + FB_GET_WIDTH);
+ height = readl(fb->reg_base + FB_GET_HEIGHT);
+
+ fb->fb.fbops = &goldfish_fb_ops;
+ fb->fb.flags = FBINFO_FLAG_DEFAULT;
+ fb->fb.pseudo_palette = fb->cmap;
+ fb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
+ fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
+ fb->fb.fix.line_length = width * 2;
+ fb->fb.fix.accel = FB_ACCEL_NONE;
+ fb->fb.fix.ypanstep = 1;
+
+ fb->fb.var.xres = width;
+ fb->fb.var.yres = height;
+ fb->fb.var.xres_virtual = width;
+ fb->fb.var.yres_virtual = height * 2;
+ fb->fb.var.bits_per_pixel = 16;
+ fb->fb.var.activate = FB_ACTIVATE_NOW;
+ fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT);
+ fb->fb.var.width = readl(fb->reg_base + FB_GET_PHYS_WIDTH);
+ fb->fb.var.pixclock = 10000;
+
+ fb->fb.var.red.offset = 11;
+ fb->fb.var.red.length = 5;
+ fb->fb.var.green.offset = 5;
+ fb->fb.var.green.length = 6;
+ fb->fb.var.blue.offset = 0;
+ fb->fb.var.blue.length = 5;
+
+ framesize = width * height * 2 * 2;
+ fb->fb.screen_base = dma_alloc_coherent(&pdev->dev, framesize,
+ &fbpaddr, GFP_KERNEL);
+ pr_debug("allocating frame buffer %d * %d, got %p\n",
+ width, height, fb->fb.screen_base);
+ if (fb->fb.screen_base == 0) {
+ ret = -ENOMEM;
+ goto err_alloc_screen_base_failed;
+ }
+ fb->fb.fix.smem_start = fbpaddr;
+ fb->fb.fix.smem_len = framesize;
+
+ ret = fb_set_var(&fb->fb, &fb->fb.var);
+ if (ret)
+ goto err_fb_set_var_failed;
+
+ ret = request_irq(fb->irq, goldfish_fb_interrupt, IRQF_SHARED,
+ pdev->name, fb);
+ if (ret)
+ goto err_request_irq_failed;
+
+ writel(FB_INT_BASE_UPDATE_DONE, fb->reg_base + FB_INT_ENABLE);
+ goldfish_fb_pan_display(&fb->fb.var, &fb->fb); /* updates base */
+
+ ret = register_framebuffer(&fb->fb);
+ if (ret)
+ goto err_register_framebuffer_failed;
+ return 0;
+
+err_register_framebuffer_failed:
+ free_irq(fb->irq, fb);
+err_request_irq_failed:
+err_fb_set_var_failed:
+ dma_free_coherent(&pdev->dev, framesize,
+ fb->fb.screen_base, fb->fb.fix.smem_start);
+err_alloc_screen_base_failed:
+err_no_irq:
+ iounmap(fb->reg_base);
+err_no_io_base:
+ kfree(fb);
+err_fb_alloc_failed:
+ return ret;
+}
+
+static int goldfish_fb_remove(struct platform_device *pdev)
+{
+ size_t framesize;
+ struct goldfish_fb *fb = platform_get_drvdata(pdev);
+
+ framesize = fb->fb.var.xres_virtual * fb->fb.var.yres_virtual * 2;
+ unregister_framebuffer(&fb->fb);
+ free_irq(fb->irq, fb);
+
+ dma_free_coherent(&pdev->dev, framesize, fb->fb.screen_base,
+ fb->fb.fix.smem_start);
+ iounmap(fb->reg_base);
+ return 0;
+}
+
+
+static struct platform_driver goldfish_fb_driver = {
+ .probe = goldfish_fb_probe,
+ .remove = goldfish_fb_remove,
+ .driver = {
+ .name = "goldfish_fb"
+ }
+};
+
+static int __init goldfish_fb_init(void)
+{
+ return platform_driver_register(&goldfish_fb_driver);
+}
+
+static void __exit goldfish_fb_exit(void)
+{
+ platform_driver_unregister(&goldfish_fb_driver);
+}
+
+module_init(goldfish_fb_init);
+module_exit(goldfish_fb_exit);
+
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 6/9] goldfish: emulated MMC device
2012-12-03 15:04 [RFC PATCH 0/9] Upstream merge of core of Goldfish AVD support Alan Cox
` (4 preceding siblings ...)
2012-12-03 15:05 ` [PATCH 5/9] goldfish: framebuffer Alan Cox
@ 2012-12-03 15:05 ` Alan Cox
2012-12-03 15:05 ` [PATCH 7/9] goldfish: power device Alan Cox
` (2 subsequent siblings)
8 siblings, 0 replies; 11+ messages in thread
From: Alan Cox @ 2012-12-03 15:05 UTC (permalink / raw)
To: arve, x86, linux-kernel, mikechan
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 17226 bytes --]
From: Mike Lockwood <lockwood@android.com>
This driver handles the virtual MMC device present in the Goldfish emulator.
The patch folds together initial work from Mike Lockwood and patches by
San Mehat, Jun Nakajima and Tom Keel <thomas.keel@intel.com> plus cleanups
by Alan Cox to get it all into 3.6 shape.
Signed-off-by: Mike A. Chan <mikechan@google.com>
[cleaned up and x86 support added]
Signed-off-by: Sheng Yang <sheng@linux.intel.com>
Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com>
Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com>
Signed-off-by: Jun Nakajima <jun.nakajima@intel.com>
Signed-off-by: Bruce Beare <bruce.j.beare@intel.com>
[Moved to 3.4]
Signed-off-by: Tom Keel <thomas.keel@intel.com>
[Moved to 3.7]
Signed-off-by: Alan Cox <alan@linux.intel.com>
---
drivers/mmc/host/Kconfig | 7 +
drivers/mmc/host/Makefile | 1
drivers/mmc/host/goldfish.c | 579 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 587 insertions(+)
create mode 100644 drivers/mmc/host/goldfish.c
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 84f4f77..05110b2 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -361,6 +361,13 @@ config MMC_DAVINCI
If you have an DAVINCI board with a Multimedia Card slot,
say Y or M here. If unsure, say N.
+config MMC_GOLDFISH
+ tristate "goldfish qemu Multimedia Card Interface support"
+ depends on GOLDFISH
+ help
+ This selects the Goldfish Multimedia card Interface emulation
+ found on the Goldfish Android virtual device emulation.
+
config MMC_SPI
tristate "MMC/SD/SDIO over SPI"
depends on SPI_MASTER && !HIGHMEM && HAS_DMA
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e4e218c..2ad0e16 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_MSM) += msm_sdcc.o
obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o
obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o
+obj-$(CONFIG_MMC_GOLDFISH) += goldfish.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o
diff --git a/drivers/mmc/host/goldfish.c b/drivers/mmc/host/goldfish.c
new file mode 100644
index 0000000..9f708ad
--- /dev/null
+++ b/drivers/mmc/host/goldfish.c
@@ -0,0 +1,579 @@
+/*
+ * linux/drivers/media/mmc/goldfish.c
+ *
+ * Copyright 2007, Google Inc.
+ * Copyright 2012, Intel Inc.
+ *
+ * based on omap.c driver, which was
+ * Copyright (C) 2004 Nokia Corporation
+ * Written by Tuukka Tikkanen and Juha Yrjölä<juha.yrjola@nokia.com>
+ * Misc hacks here and there by Tony Lindgren <tony@atomide.com>
+ * Other hacks (DMA, SD, etc) by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/major.h>
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/kdev_t.h>
+#include <linux/blkdev.h>
+#include <linux/mutex.h>
+#include <linux/scatterlist.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/scatterlist.h>
+
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#define DRIVER_NAME "goldfish_mmc"
+
+#define BUFFER_SIZE 16384
+
+#define GOLDFISH_MMC_READ(host, addr) (readl(host->reg_base + addr))
+#define GOLDFISH_MMC_WRITE(host, addr, x) (writel(x, host->reg_base + addr))
+
+
+enum {
+ /* status register */
+ MMC_INT_STATUS = 0x00,
+ /* set this to enable IRQ */
+ MMC_INT_ENABLE = 0x04,
+ /* set this to specify buffer address */
+ MMC_SET_BUFFER = 0x08,
+
+ /* MMC command number */
+ MMC_CMD = 0x0C,
+
+ /* MMC argument */
+ MMC_ARG = 0x10,
+
+ /* MMC response (or R2 bits 0 - 31) */
+ MMC_RESP_0 = 0x14,
+
+ /* MMC R2 response bits 32 - 63 */
+ MMC_RESP_1 = 0x18,
+
+ /* MMC R2 response bits 64 - 95 */
+ MMC_RESP_2 = 0x1C,
+
+ /* MMC R2 response bits 96 - 127 */
+ MMC_RESP_3 = 0x20,
+
+ MMC_BLOCK_LENGTH = 0x24,
+ MMC_BLOCK_COUNT = 0x28,
+
+ /* MMC state flags */
+ MMC_STATE = 0x2C,
+
+ /* MMC_INT_STATUS bits */
+
+ MMC_STAT_END_OF_CMD = 1U << 0,
+ MMC_STAT_END_OF_DATA = 1U << 1,
+ MMC_STAT_STATE_CHANGE = 1U << 2,
+ MMC_STAT_CMD_TIMEOUT = 1U << 3,
+
+ /* MMC_STATE bits */
+ MMC_STATE_INSERTED = 1U << 0,
+ MMC_STATE_READ_ONLY = 1U << 1,
+};
+
+/*
+ * Command types
+ */
+#define OMAP_MMC_CMDTYPE_BC 0
+#define OMAP_MMC_CMDTYPE_BCR 1
+#define OMAP_MMC_CMDTYPE_AC 2
+#define OMAP_MMC_CMDTYPE_ADTC 3
+
+
+struct goldfish_mmc_host {
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ struct mmc_host *mmc;
+ struct device *dev;
+ unsigned char id; /* 16xx chips have 2 MMC blocks */
+ void __iomem *virt_base;
+ unsigned int phys_base;
+ int irq;
+ unsigned char bus_mode;
+ unsigned char hw_bus_mode;
+
+ unsigned int sg_len;
+ unsigned dma_done:1;
+ unsigned dma_in_use:1;
+
+ void __iomem *reg_base;
+};
+
+static inline int
+goldfish_mmc_cover_is_open(struct goldfish_mmc_host *host)
+{
+ return 0;
+}
+
+static ssize_t
+goldfish_mmc_show_cover_switch(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct goldfish_mmc_host *host = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", goldfish_mmc_cover_is_open(host) ? "open" :
+ "closed");
+}
+
+static DEVICE_ATTR(cover_switch, S_IRUGO, goldfish_mmc_show_cover_switch, NULL);
+
+static void
+goldfish_mmc_start_command(struct goldfish_mmc_host *host, struct mmc_command *cmd)
+{
+ u32 cmdreg;
+ u32 resptype;
+ u32 cmdtype;
+
+ host->cmd = cmd;
+
+ resptype = 0;
+ cmdtype = 0;
+
+ /* Our hardware needs to know exact type */
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_NONE:
+ break;
+ case MMC_RSP_R1:
+ case MMC_RSP_R1B:
+ /* resp 1, 1b, 6, 7 */
+ resptype = 1;
+ break;
+ case MMC_RSP_R2:
+ resptype = 2;
+ break;
+ case MMC_RSP_R3:
+ resptype = 3;
+ break;
+ default:
+ dev_err(mmc_dev(host->mmc),
+ "Invalid response type: %04x\n", mmc_resp_type(cmd));
+ break;
+ }
+
+ if (mmc_cmd_type(cmd) == MMC_CMD_ADTC)
+ cmdtype = OMAP_MMC_CMDTYPE_ADTC;
+ else if (mmc_cmd_type(cmd) == MMC_CMD_BC)
+ cmdtype = OMAP_MMC_CMDTYPE_BC;
+ else if (mmc_cmd_type(cmd) == MMC_CMD_BCR)
+ cmdtype = OMAP_MMC_CMDTYPE_BCR;
+ else
+ cmdtype = OMAP_MMC_CMDTYPE_AC;
+
+ cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12);
+
+ if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
+ cmdreg |= 1 << 6;
+
+ if (cmd->flags & MMC_RSP_BUSY)
+ cmdreg |= 1 << 11;
+
+ if (host->data && !(host->data->flags & MMC_DATA_WRITE))
+ cmdreg |= 1 << 15;
+
+ GOLDFISH_MMC_WRITE(host, MMC_ARG, cmd->arg);
+ GOLDFISH_MMC_WRITE(host, MMC_CMD, cmdreg);
+}
+
+static void goldfish_mmc_xfer_done(struct goldfish_mmc_host *host,
+ struct mmc_data *data)
+{
+ if (host->dma_in_use) {
+ enum dma_data_direction dma_data_dir;
+
+ if (data->flags & MMC_DATA_WRITE)
+ dma_data_dir = DMA_TO_DEVICE;
+ else
+ dma_data_dir = DMA_FROM_DEVICE;
+
+ if (dma_data_dir == DMA_FROM_DEVICE) {
+ /*
+ * We don't really have DMA, so we need
+ * to copy from our platform driver buffer
+ */
+ uint8_t *dest = (uint8_t *)sg_virt(data->sg);
+ memcpy(dest, host->virt_base, data->sg->length);
+ }
+ host->data->bytes_xfered += data->sg->length;
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
+ dma_data_dir);
+ }
+
+ host->data = NULL;
+ host->sg_len = 0;
+
+ /* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing
+ * dozens of requests until the card finishes writing data.
+ * It'd be cheaper to just wait till an EOFB interrupt arrives...
+ */
+
+ if (!data->stop) {
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, data->mrq);
+ return;
+ }
+
+ goldfish_mmc_start_command(host, data->stop);
+}
+
+static void goldfish_mmc_end_of_data(struct goldfish_mmc_host *host,
+ struct mmc_data *data)
+{
+ if (!host->dma_in_use) {
+ goldfish_mmc_xfer_done(host, data);
+ return;
+ }
+ if (host->dma_done)
+ goldfish_mmc_xfer_done(host, data);
+}
+
+static void goldfish_mmc_cmd_done(struct goldfish_mmc_host *host,
+ struct mmc_command *cmd)
+{
+ host->cmd = NULL;
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136) {
+ /* response type 2 */
+ cmd->resp[3] =
+ GOLDFISH_MMC_READ(host, MMC_RESP_0);
+ cmd->resp[2] =
+ GOLDFISH_MMC_READ(host, MMC_RESP_1);
+ cmd->resp[1] =
+ GOLDFISH_MMC_READ(host, MMC_RESP_2);
+ cmd->resp[0] =
+ GOLDFISH_MMC_READ(host, MMC_RESP_3);
+ } else {
+ /* response types 1, 1b, 3, 4, 5, 6 */
+ cmd->resp[0] =
+ GOLDFISH_MMC_READ(host, MMC_RESP_0);
+ }
+ }
+
+ if (host->data == NULL || cmd->error) {
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, cmd->mrq);
+ }
+}
+
+static irqreturn_t goldfish_mmc_irq(int irq, void *dev_id)
+{
+ struct goldfish_mmc_host *host = (struct goldfish_mmc_host *)dev_id;
+ u16 status;
+ int end_command = 0;
+ int end_transfer = 0;
+ int transfer_error = 0;
+ int state_changed = 0;
+ int cmd_timeout = 0;
+
+ while ((status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS)) != 0) {
+ GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);
+
+ if (status & MMC_STAT_END_OF_CMD)
+ end_command = 1;
+
+ if (status & MMC_STAT_END_OF_DATA)
+ end_transfer = 1;
+
+ if (status & MMC_STAT_STATE_CHANGE)
+ state_changed = 1;
+
+ if (status & MMC_STAT_CMD_TIMEOUT) {
+ end_command = 0;
+ cmd_timeout = 1;
+ }
+ }
+
+ if (cmd_timeout) {
+ struct mmc_request *mrq = host->mrq;
+ mrq->cmd->error = -ETIMEDOUT;
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, mrq);
+ }
+
+ if (end_command)
+ goldfish_mmc_cmd_done(host, host->cmd);
+
+ if (transfer_error)
+ goldfish_mmc_xfer_done(host, host->data);
+ else if (end_transfer) {
+ host->dma_done = 1;
+ goldfish_mmc_end_of_data(host, host->data);
+ } else if (host->data != NULL) {
+ /*
+ * WORKAROUND -- after porting this driver from 2.6 to 3.4,
+ * during device initialization, cases where host->data is
+ * non-null but end_transfer is false would occur. Doing
+ * nothing in such cases results in no further interrupts,
+ * and initialization failure.
+ * TODO -- find the real cause.
+ */
+ host->dma_done = 1;
+ goldfish_mmc_end_of_data(host, host->data);
+ }
+
+ if (state_changed) {
+ u32 state = GOLDFISH_MMC_READ(host, MMC_STATE);
+ pr_info("%s: Card detect now %d\n", __func__,
+ (state & MMC_STATE_INSERTED));
+ mmc_detect_change(host->mmc, 0);
+ }
+
+ if (!end_command && !end_transfer &&
+ !transfer_error && !state_changed && !cmd_timeout) {
+ status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS);
+ dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status);
+ if (status != 0) {
+ GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status);
+ GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE, 0);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void goldfish_mmc_prepare_data(struct goldfish_mmc_host *host,
+ struct mmc_request *req)
+{
+ struct mmc_data *data = req->data;
+ int block_size;
+ unsigned sg_len;
+ enum dma_data_direction dma_data_dir;
+
+ host->data = data;
+ if (data == NULL) {
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, 0);
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, 0);
+ host->dma_in_use = 0;
+ return;
+ }
+
+ block_size = data->blksz;
+
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, data->blocks - 1);
+ GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, block_size - 1);
+
+ /* cope with calling layer confusion; it issues "single
+ * block" writes using multi-block scatterlists.
+ */
+ sg_len = (data->blocks == 1) ? 1 : data->sg_len;
+
+ if (data->flags & MMC_DATA_WRITE)
+ dma_data_dir = DMA_TO_DEVICE;
+ else
+ dma_data_dir = DMA_FROM_DEVICE;
+
+ host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
+ sg_len, dma_data_dir);
+ host->dma_done = 0;
+ host->dma_in_use = 1;
+
+ if (dma_data_dir == DMA_TO_DEVICE) {
+ /* we don't really have DMA, so we need to copy to our
+ platform driver buffer */
+ const uint8_t *src = (uint8_t *)sg_virt(data->sg);
+ memcpy(host->virt_base, src, data->sg->length);
+ }
+}
+
+static void goldfish_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+ struct goldfish_mmc_host *host = mmc_priv(mmc);
+
+ WARN_ON(host->mrq != NULL);
+
+ host->mrq = req;
+ goldfish_mmc_prepare_data(host, req);
+ goldfish_mmc_start_command(host, req->cmd);
+
+ /* this is to avoid accidentally being detected as an SDIO card
+ in mmc_attach_sdio() */
+ if (req->cmd->opcode == SD_IO_SEND_OP_COND &&
+ req->cmd->flags == (MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR))
+ req->cmd->error = -EINVAL;
+}
+
+static void goldfish_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct goldfish_mmc_host *host = mmc_priv(mmc);
+
+ host->bus_mode = ios->bus_mode;
+ host->hw_bus_mode = host->bus_mode;
+}
+
+static int goldfish_mmc_get_ro(struct mmc_host *mmc)
+{
+ uint32_t state;
+ struct goldfish_mmc_host *host = mmc_priv(mmc);
+
+ state = GOLDFISH_MMC_READ(host, MMC_STATE);
+ return ((state & MMC_STATE_READ_ONLY) != 0);
+}
+
+static const struct mmc_host_ops goldfish_mmc_ops = {
+ .request = goldfish_mmc_request,
+ .set_ios = goldfish_mmc_set_ios,
+ .get_ro = goldfish_mmc_get_ro,
+};
+
+static int __init goldfish_mmc_probe(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct goldfish_mmc_host *host = NULL;
+ struct resource *res;
+ int ret = 0;
+ int irq;
+ dma_addr_t buf_addr;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (res == NULL || irq < 0)
+ return -ENXIO;
+
+ mmc = mmc_alloc_host(sizeof(struct goldfish_mmc_host), &pdev->dev);
+ if (mmc == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_host_failed;
+ }
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+
+ pr_err("mmc: Mapping %lX to %lX\n", (long)res->start, (long)res->end);
+ host->reg_base = ioremap(res->start, res->end - res->start + 1);
+ if (host->reg_base == NULL) {
+ ret = -ENOMEM;
+ goto ioremap_failed;
+ }
+ /* FIXME@ discuss with HPA */
+ host->virt_base = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE,
+ &buf_addr, GFP_KERNEL);
+
+ if (host->virt_base == 0) {
+ ret = -ENOMEM;
+ goto dma_alloc_failed;
+ }
+ host->phys_base = buf_addr;
+
+ host->id = pdev->id;
+ host->irq = irq;
+
+ mmc->ops = &goldfish_mmc_ops;
+ mmc->f_min = 400000;
+ mmc->f_max = 24000000;
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->caps = MMC_CAP_4_BIT_DATA;
+
+ /* Use scatterlist DMA to reduce per-transfer costs.
+ * NOTE max_seg_size assumption that small blocks aren't
+ * normally used (except e.g. for reading SD registers).
+ */
+ mmc->max_segs = 32;
+ mmc->max_blk_size = 2048; /* MMC_BLOCK_LENGTH is 11 bits (+1) */
+ mmc->max_blk_count = 2048; /* MMC_BLOCK_COUNT is 11 bits (+1) */
+ mmc->max_req_size = BUFFER_SIZE;
+ mmc->max_seg_size = mmc->max_req_size;
+
+ ret = request_irq(host->irq, goldfish_mmc_irq, 0, DRIVER_NAME, host);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed IRQ Adding goldfish MMC\n");
+
+ goto err_request_irq_failed;
+ }
+
+ host->dev = &pdev->dev;
+ platform_set_drvdata(pdev, host);
+
+ ret = device_create_file(&pdev->dev, &dev_attr_cover_switch);
+ if (ret)
+ dev_warn(mmc_dev(host->mmc),
+ "Unable to create sysfs attributes\n");
+
+ GOLDFISH_MMC_WRITE(host, MMC_SET_BUFFER, host->phys_base);
+ GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE,
+ MMC_STAT_END_OF_CMD | MMC_STAT_END_OF_DATA |
+ MMC_STAT_STATE_CHANGE | MMC_STAT_CMD_TIMEOUT);
+
+ mmc_add_host(mmc);
+ return 0;
+
+err_request_irq_failed:
+ dma_free_coherent(&pdev->dev, BUFFER_SIZE, host->virt_base,
+ host->phys_base);
+dma_alloc_failed:
+ iounmap(host->reg_base);
+ioremap_failed:
+ mmc_free_host(host->mmc);
+err_alloc_host_failed:
+ return ret;
+}
+
+static int goldfish_mmc_remove(struct platform_device *pdev)
+{
+ struct goldfish_mmc_host *host = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ BUG_ON(host == NULL);
+
+ mmc_remove_host(host->mmc);
+ free_irq(host->irq, host);
+ dma_free_coherent(&pdev->dev, BUFFER_SIZE, host->virt_base, host->phys_base);
+ iounmap(host->reg_base);
+ mmc_free_host(host->mmc);
+ return 0;
+}
+
+static struct platform_driver goldfish_mmc_driver = {
+ .probe = goldfish_mmc_probe,
+ .remove = goldfish_mmc_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+static int __init goldfish_mmc_init(void)
+{
+ return platform_driver_register(&goldfish_mmc_driver);
+}
+
+static void __exit goldfish_mmc_exit(void)
+{
+ platform_driver_unregister(&goldfish_mmc_driver);
+}
+
+module_init(goldfish_mmc_init);
+module_exit(goldfish_mmc_exit);
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 7/9] goldfish: power device
2012-12-03 15:04 [RFC PATCH 0/9] Upstream merge of core of Goldfish AVD support Alan Cox
` (5 preceding siblings ...)
2012-12-03 15:05 ` [PATCH 6/9] goldfish: emulated MMC device Alan Cox
@ 2012-12-03 15:05 ` Alan Cox
2012-12-03 15:06 ` [PATCH 8/9] goldfish: real time clock Alan Cox
2012-12-03 15:06 ` [PATCH 9/9] goldfish: NAND flash driver Alan Cox
8 siblings, 0 replies; 11+ messages in thread
From: Alan Cox @ 2012-12-03 15:05 UTC (permalink / raw)
To: arve, x86, linux-kernel, mikechan
From: Mike Lockwood <lockwood@android.com>
Add the emulated power driver for the Goldfish platform.
This folds together the code from the Google tree, Jun Nakajima's cleanups
and x86 porting work, and then a tidy up to pass checkpatch.
Signed-off-by: Mike A. Chan <mikechan@google.com>
[cleanup and x86 support]
Signed-off-by: Sheng Yang <sheng@linux.intel.com>
Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com>
Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com>
Signed-off-by: Jun Nakajima <jun.nakajima@intel.com>
Signed-off-by: Bruce Beare <bruce.j.beare@intel.com>
[ported to 3.4]
Signed-off-by: Tom Keel <thomas.keel@intel.com>
[ported to 3.7 and final tidy]
Signed-off-by: Alan Cox <alan@linux.intel.com>
---
drivers/power/Kconfig | 8 +
drivers/power/Makefile | 1
drivers/power/goldfish_battery.c | 263 ++++++++++++++++++++++++++++++++++++++
3 files changed, 271 insertions(+), 1 deletion(-)
create mode 100644 drivers/power/goldfish_battery.c
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 9f45e2f..0c5208d 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -346,8 +346,14 @@ config AB8500_BM
help
Say Y to include support for AB8500 battery management.
-source "drivers/power/reset/Kconfig"
+config BATTERY_GOLDFISH
+ tristate "Goldfish battery driver"
+ depends on GOLDFISH
+ help
+ Say Y to enable support for the battery and AC power in the
+ Goldfish emulator.
+source "drivers/power/reset/Kconfig"
endif # POWER_SUPPLY
source "drivers/power/avs/Kconfig"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 22c8913..10c2894 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o
+obj-$(CONFIG_BATTERY_GOLDFISH) += goldfish_battery.o
obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o
diff --git a/drivers/power/goldfish_battery.c b/drivers/power/goldfish_battery.c
new file mode 100644
index 0000000..31c01c1
--- /dev/null
+++ b/drivers/power/goldfish_battery.c
@@ -0,0 +1,263 @@
+/* drivers/power/goldfish_battery.c
+ *
+ * Power supply driver for the goldfish emulator
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2012 Intel, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+
+struct goldfish_battery_data {
+ void __iomem *reg_base;
+ int irq;
+ spinlock_t lock;
+
+ struct power_supply battery;
+ struct power_supply ac;
+};
+
+#define GOLDFISH_BATTERY_READ(data, addr) (readl(data->reg_base + addr))
+#define GOLDFISH_BATTERY_WRITE(data, addr, x) (writel(x, data->reg_base + addr))
+
+
+/* temporary variable used between goldfish_battery_probe() and goldfish_battery_open() */
+static struct goldfish_battery_data *battery_data;
+
+enum {
+ /* status register */
+ BATTERY_INT_STATUS = 0x00,
+ /* set this to enable IRQ */
+ BATTERY_INT_ENABLE = 0x04,
+
+ BATTERY_AC_ONLINE = 0x08,
+ BATTERY_STATUS = 0x0C,
+ BATTERY_HEALTH = 0x10,
+ BATTERY_PRESENT = 0x14,
+ BATTERY_CAPACITY = 0x18,
+
+ BATTERY_STATUS_CHANGED = 1U << 0,
+ AC_STATUS_CHANGED = 1U << 1,
+ BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
+};
+
+
+static int goldfish_ac_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct goldfish_battery_data *data = container_of(psy,
+ struct goldfish_battery_data, ac);
+ int ret = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_AC_ONLINE);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int goldfish_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct goldfish_battery_data *data = container_of(psy,
+ struct goldfish_battery_data, battery);
+ int ret = 0;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_STATUS);
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_HEALTH);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_PRESENT);
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static enum power_supply_property goldfish_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static enum power_supply_property goldfish_ac_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static irqreturn_t goldfish_battery_interrupt(int irq, void *dev_id)
+{
+ unsigned long irq_flags;
+ struct goldfish_battery_data *data = dev_id;
+ uint32_t status;
+
+ spin_lock_irqsave(&data->lock, irq_flags);
+
+ /* read status flags, which will clear the interrupt */
+ status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS);
+ status &= BATTERY_INT_MASK;
+
+ if (status & BATTERY_STATUS_CHANGED)
+ power_supply_changed(&data->battery);
+ if (status & AC_STATUS_CHANGED)
+ power_supply_changed(&data->ac);
+
+ spin_unlock_irqrestore(&data->lock, irq_flags);
+ return status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+
+static int goldfish_battery_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+ struct goldfish_battery_data *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (data == NULL) {
+ ret = -ENOMEM;
+ goto err_data_alloc_failed;
+ }
+ spin_lock_init(&data->lock);
+
+ data->battery.properties = goldfish_battery_props;
+ data->battery.num_properties = ARRAY_SIZE(goldfish_battery_props);
+ data->battery.get_property = goldfish_battery_get_property;
+ data->battery.name = "battery";
+ data->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+
+ data->ac.properties = goldfish_ac_props;
+ data->ac.num_properties = ARRAY_SIZE(goldfish_ac_props);
+ data->ac.get_property = goldfish_ac_get_property;
+ data->ac.name = "ac";
+ data->ac.type = POWER_SUPPLY_TYPE_MAINS;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ pr_err("%s: platform_get_resource failed\n", pdev->name);
+ ret = -ENODEV;
+ goto err_no_io_base;
+ }
+ data->reg_base = ioremap(r->start, r->end - r->start + 1);
+ if (data->reg_base == NULL) {
+ pr_err("%s: unable to remap MMIO\n", pdev->name);
+ ret = -ENOMEM;
+ goto err_no_io_base;
+ }
+
+ data->irq = platform_get_irq(pdev, 0);
+ if (data->irq < 0) {
+ pr_err("%s: platform_get_irq failed\n", pdev->name);
+ ret = -ENODEV;
+ goto err_no_irq;
+ }
+
+ ret = request_irq(data->irq, goldfish_battery_interrupt,
+ IRQF_SHARED, pdev->name, data);
+ if (ret)
+ goto err_request_irq_failed;
+
+ ret = power_supply_register(&pdev->dev, &data->ac);
+ if (ret)
+ goto err_ac_failed;
+
+ ret = power_supply_register(&pdev->dev, &data->battery);
+ if (ret)
+ goto err_battery_failed;
+
+ platform_set_drvdata(pdev, data);
+ battery_data = data;
+
+ GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK);
+ return 0;
+
+err_battery_failed:
+ power_supply_unregister(&data->ac);
+err_ac_failed:
+ free_irq(data->irq, data);
+err_request_irq_failed:
+err_no_irq:
+ iounmap(data->reg_base);
+err_no_io_base:
+ kfree(data);
+err_data_alloc_failed:
+ return ret;
+}
+
+static int goldfish_battery_remove(struct platform_device *pdev)
+{
+ struct goldfish_battery_data *data = platform_get_drvdata(pdev);
+
+ power_supply_unregister(&data->battery);
+ power_supply_unregister(&data->ac);
+
+ free_irq(data->irq, data);
+ iounmap(data->reg_base);
+ kfree(data);
+ battery_data = NULL;
+ return 0;
+}
+
+static struct platform_driver goldfish_battery_device = {
+ .probe = goldfish_battery_probe,
+ .remove = goldfish_battery_remove,
+ .driver = {
+ .name = "goldfish-battery"
+ }
+};
+
+static int __init goldfish_battery_init(void)
+{
+ return platform_driver_register(&goldfish_battery_device);
+}
+
+static void __exit goldfish_battery_exit(void)
+{
+ platform_driver_unregister(&goldfish_battery_device);
+}
+
+module_init(goldfish_battery_init);
+module_exit(goldfish_battery_exit);
+
+MODULE_AUTHOR("Mike Lockwood lockwood@android.com");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Battery driver for the Goldfish emulator");
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 8/9] goldfish: real time clock
2012-12-03 15:04 [RFC PATCH 0/9] Upstream merge of core of Goldfish AVD support Alan Cox
` (6 preceding siblings ...)
2012-12-03 15:05 ` [PATCH 7/9] goldfish: power device Alan Cox
@ 2012-12-03 15:06 ` Alan Cox
2012-12-03 15:06 ` [PATCH 9/9] goldfish: NAND flash driver Alan Cox
8 siblings, 0 replies; 11+ messages in thread
From: Alan Cox @ 2012-12-03 15:06 UTC (permalink / raw)
To: arve, x86, linux-kernel, mikechan
From: Arve Hjønnevåg <arve@google.com>
Gets the current time from the host. Alarms are not supported yet.
Signed-off-by: Mike A. Chan <mikechan@google.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
[Ported to 3.4]
Signed-off-by: Tom Keel <thomas.keel@intel.com>
[Cleaned up to use ioremap, types, unload etc]
Signed-off-by: Alan Cox <alan@linux.intel.com>
---
arch/x86/include/asm/goldfish.h | 7 ++
drivers/rtc/Kconfig | 9 ++
drivers/rtc/Makefile | 1
drivers/rtc/rtc-goldfish.c | 146 +++++++++++++++++++++++++++++++++++++++
4 files changed, 163 insertions(+)
create mode 100644 drivers/rtc/rtc-goldfish.c
diff --git a/arch/x86/include/asm/goldfish.h b/arch/x86/include/asm/goldfish.h
index d5fcf6d..28a0b9e 100644
--- a/arch/x86/include/asm/goldfish.h
+++ b/arch/x86/include/asm/goldfish.h
@@ -33,4 +33,11 @@
#define GOLDFISH_TTY_BASE (0x2000)
+#define GOLDFISH_TIME_LOW 0x00
+#define GOLDFISH_TIME_HIGH 0x04
+#define GOLDFISH_ALARM_LOW 0x08
+#define GOLDFISH_ALARM_HIGH 0x0C
+#define GOLDFISH_CLEAR_TIMER_INT 0x10
+#define GOLDFISH_CLEAR_ALARM 0x14
+
#endif
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 5bb0314..358bfa2 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -948,6 +948,15 @@ config RTC_DRV_BFIN
This driver can also be built as a module. If so, the module
will be called rtc-bfin.
+config RTC_DRV_GOLDFISH
+ tristate "GOLDFISH"
+ depends on GOLDFISH
+ help
+ RTC driver for Goldfish Virtual Platform
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-goldfish.
+
config RTC_DRV_RS5C313
tristate "Ricoh RS5C313"
depends on SH_LANDISK
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 59e2132..9a00537 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
+obj-$(CONFIG_RTC_DRV_GOLDFISH) += rtc-goldfish.o
obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
diff --git a/drivers/rtc/rtc-goldfish.c b/drivers/rtc/rtc-goldfish.c
new file mode 100644
index 0000000..2534913
--- /dev/null
+++ b/drivers/rtc/rtc-goldfish.c
@@ -0,0 +1,146 @@
+/*
+ * drivers/rtc/rtc-goldfish.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2012 Intel, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+
+#include <asm/goldfish.h>
+
+struct goldfish_rtc {
+ void __iomem *base;
+ u32 irq;
+ struct rtc_device *rtc;
+};
+
+static irqreturn_t goldfish_rtc_interrupt(int irq, void *dev_id)
+{
+ struct goldfish_rtc *qrtc = dev_id;
+ unsigned long events = 0;
+
+ writel(1, qrtc->base + GOLDFISH_CLEAR_TIMER_INT);
+ events = RTC_IRQF | RTC_AF;
+
+ rtc_update_irq(qrtc->rtc, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static int goldfish_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ int64_t time;
+ struct goldfish_rtc *qrtc = platform_get_drvdata(
+ to_platform_device(dev));
+
+ time = readl(qrtc->base + GOLDFISH_TIME_LOW);
+ time |= (int64_t)readl(qrtc->base + GOLDFISH_TIME_HIGH) << 32;
+ do_div(time, NSEC_PER_SEC);
+
+ rtc_time_to_tm(time, tm);
+ return 0;
+}
+
+static const struct rtc_class_ops goldfish_rtc_ops = {
+ .read_time = goldfish_rtc_read_time,
+};
+
+static int goldfish_rtc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct resource *r;
+ struct goldfish_rtc *qrtc;
+
+ qrtc = kzalloc(sizeof(*qrtc), GFP_KERNEL);
+ if (qrtc == NULL) {
+ ret = -ENOMEM;
+ goto err_qrtc_alloc_failed;
+ }
+ platform_set_drvdata(pdev, qrtc);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ ret = -ENODEV;
+ goto err_no_io_base;
+ }
+ qrtc->base = ioremap(r->start, 0x18);
+ if (qrtc->base == NULL) {
+ ret = -ENOMEM;
+ goto err_no_io_base;
+ }
+ qrtc->irq = platform_get_irq(pdev, 0);
+ if (qrtc->irq < 0) {
+ ret = -ENODEV;
+ goto err_no_irq;
+ }
+ qrtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &goldfish_rtc_ops, THIS_MODULE);
+ if (IS_ERR(qrtc->rtc)) {
+ ret = PTR_ERR(qrtc->rtc);
+ goto err_rtc_device_register_failed;
+ }
+
+ ret = request_irq(qrtc->irq, goldfish_rtc_interrupt, 0, pdev->name, qrtc);
+ if (ret)
+ goto request_irq;
+
+ return 0;
+
+ free_irq(qrtc->irq, qrtc);
+request_irq:
+ rtc_device_unregister(qrtc->rtc);
+err_rtc_device_register_failed:
+err_no_irq:
+ iounmap(qrtc->base);
+err_no_io_base:
+ kfree(qrtc);
+err_qrtc_alloc_failed:
+ return ret;
+}
+
+static int goldfish_rtc_remove(struct platform_device *pdev)
+{
+ struct goldfish_rtc *qrtc = platform_get_drvdata(pdev);
+ free_irq(qrtc->irq, qrtc);
+ rtc_device_unregister(qrtc->rtc);
+ iounmap(qrtc->base);
+ kfree(qrtc);
+ return 0;
+}
+
+static struct platform_driver goldfish_timer = {
+ .probe = goldfish_rtc_probe,
+ .remove = goldfish_rtc_remove,
+ .driver = {
+ .name = "goldfish_rtc"
+ }
+};
+
+static int __devinit goldfish_rtc_init(void)
+{
+ return platform_driver_register(&goldfish_timer);
+}
+
+static void goldfish_rtc_exit(void)
+{
+ return platform_driver_unregister(&goldfish_timer);
+}
+
+module_init(goldfish_rtc_init);
+module_exit(goldfish_rtc_exit);
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 9/9] goldfish: NAND flash driver
2012-12-03 15:04 [RFC PATCH 0/9] Upstream merge of core of Goldfish AVD support Alan Cox
` (7 preceding siblings ...)
2012-12-03 15:06 ` [PATCH 8/9] goldfish: real time clock Alan Cox
@ 2012-12-03 15:06 ` Alan Cox
8 siblings, 0 replies; 11+ messages in thread
From: Alan Cox @ 2012-12-03 15:06 UTC (permalink / raw)
To: arve, x86, linux-kernel, mikechan
From: Arve Hjønnevåg <arve@google.com>
Fold together the NAND driver for Goldfish from Arve with cleanups by
Jun Nakajima and a tidy up to 3.7 and checkpatch.
Signed-off-by: Mike A. Chan <mikechan@google.com>
Signed-off-by: Arve Hjønnevåg <arve@android.com>
[Ported to handle x86]
Signed-off-by: Sheng Yang <sheng@linux.intel.com>
Signed-off-by: Yunhong Jiang <yunhong.jiang@intel.com>
Signed-off-by: Xiaohui Xin <xiaohui.xin@intel.com>
Signed-off-by: Jun Nakajima <jun.nakajima@intel.com>
Signed-off-by: Bruce Beare <bruce.j.beare@intel.com>
[Ported to 3.4]
Signed-off-by: Tom Keel <thomas.keel@intel.com>
[Ported to 3.7 and tided for checkpatch etc]
Signed-off-by: Alan Cox <alan@linux.intel.com>
---
drivers/mtd/devices/Kconfig | 7
drivers/mtd/devices/Makefile | 3
drivers/mtd/devices/goldfish_nand.c | 475 +++++++++++++++++++++++++++++++
drivers/mtd/devices/goldfish_nand_reg.h | 72 +++++
4 files changed, 556 insertions(+), 1 deletion(-)
create mode 100644 drivers/mtd/devices/goldfish_nand.c
create mode 100644 drivers/mtd/devices/goldfish_nand_reg.h
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index c187342..7edecf0 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -342,4 +342,11 @@ config MTD_DOCPROBE_55AA
LinuxBIOS or if you need to recover a DiskOnChip Millennium on which
you have managed to wipe the first block.
+config MTD_GOLDFISH_NAND
+ tristate "Goldfish NAND device"
+ depends on GOLDFISH
+ help
+ Drives the emulated NAND flash device on the Google Goldfish
+ Android virtual device.
+
endmenu
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 395733a..36d4791 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -20,5 +20,6 @@ obj-$(CONFIG_MTD_M25P80) += m25p80.o
obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
+obj-$(CONFIG_MTD_GOLDFISH_NAND) += goldfish_nand.o
-CFLAGS_docg3.o += -I$(src)
\ No newline at end of file
+CFLAGS_docg3.o += -I$(src)
diff --git a/drivers/mtd/devices/goldfish_nand.c b/drivers/mtd/devices/goldfish_nand.c
new file mode 100644
index 0000000..5b932ba
--- /dev/null
+++ b/drivers/mtd/devices/goldfish_nand.c
@@ -0,0 +1,475 @@
+/*
+ * drivers/mtd/devices/goldfish_nand.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (C) 2012 Intel, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/platform_device.h>
+
+#include <asm/div64.h>
+
+#include "goldfish_nand_reg.h"
+
+struct goldfish_nand {
+ spinlock_t lock;
+ unsigned char __iomem *base;
+ struct cmd_params *cmd_params;
+ size_t mtd_count;
+ struct mtd_info mtd[0];
+};
+
+static u32 goldfish_nand_cmd_with_params(struct mtd_info *mtd,
+ enum nand_cmd cmd, u64 addr, u32 len,
+ void *ptr, u32 *rv)
+{
+ u32 cmdp;
+ struct goldfish_nand *nand = mtd->priv;
+ struct cmd_params *cps = nand->cmd_params;
+ unsigned char __iomem *base = nand->base;
+
+ if (cps == NULL)
+ return -1;
+
+ switch (cmd) {
+ case NAND_CMD_ERASE:
+ cmdp = NAND_CMD_ERASE_WITH_PARAMS;
+ break;
+ case NAND_CMD_READ:
+ cmdp = NAND_CMD_READ_WITH_PARAMS;
+ break;
+ case NAND_CMD_WRITE:
+ cmdp = NAND_CMD_WRITE_WITH_PARAMS;
+ break;
+ default:
+ return -1;
+ }
+ cps->dev = mtd - nand->mtd;
+ cps->addr_high = (u32)(addr >> 32);
+ cps->addr_low = (u32)addr;
+ cps->transfer_size = len;
+ cps->data = (u32)ptr;
+ writel(cmdp, base + NAND_COMMAND);
+ *rv = cps->result;
+ return 0;
+}
+
+static u32 goldfish_nand_cmd(struct mtd_info *mtd, enum nand_cmd cmd,
+ u64 addr, u32 len, void *ptr)
+{
+ struct goldfish_nand *nand = mtd->priv;
+ u32 rv;
+ unsigned long irq_flags;
+ unsigned char __iomem *base = nand->base;
+
+ spin_lock_irqsave(&nand->lock, irq_flags);
+ if (goldfish_nand_cmd_with_params(mtd, cmd, addr, len, ptr, &rv)) {
+ writel(mtd - nand->mtd, base + NAND_DEV);
+ writel((u32)(addr >> 32), base + NAND_ADDR_HIGH);
+ writel((u32)addr, base + NAND_ADDR_LOW);
+ writel(len, base + NAND_TRANSFER_SIZE);
+ writel((u32)ptr, base + NAND_DATA);
+ writel(cmd, base + NAND_COMMAND);
+ rv = readl(base + NAND_RESULT);
+ }
+ spin_unlock_irqrestore(&nand->lock, irq_flags);
+ return rv;
+}
+
+static int goldfish_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ loff_t ofs = instr->addr;
+ u32 len = instr->len;
+ u32 rem;
+
+ if (ofs + len > mtd->size)
+ goto invalid_arg;
+ rem = do_div(ofs, mtd->writesize);
+ if (rem)
+ goto invalid_arg;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ if (len % mtd->writesize)
+ goto invalid_arg;
+ len = len / mtd->writesize * (mtd->writesize + mtd->oobsize);
+
+ if (goldfish_nand_cmd(mtd, NAND_CMD_ERASE, ofs, len, NULL) != len) {
+ pr_err("goldfish_nand_erase: erase failed, start %llx, len %x, dev_size %llx, erase_size %x\n",
+ ofs, len, mtd->size, mtd->erasesize);
+ return -EIO;
+ }
+
+ instr->state = MTD_ERASE_DONE;
+ mtd_erase_callback(instr);
+
+ return 0;
+
+invalid_arg:
+ pr_err("goldfish_nand_erase: invalid erase, start %llx, len %x, dev_size %llx, erase_size %x\n",
+ ofs, len, mtd->size, mtd->erasesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_read_oob(struct mtd_info *mtd, loff_t ofs,
+ struct mtd_oob_ops *ops)
+{
+ u32 rem;
+
+ if (ofs + ops->len > mtd->size)
+ goto invalid_arg;
+ if (ops->datbuf && ops->len && ops->len != mtd->writesize)
+ goto invalid_arg;
+ if (ops->ooblen + ops->ooboffs > mtd->oobsize)
+ goto invalid_arg;
+
+ rem = do_div(ofs, mtd->writesize);
+ if (rem)
+ goto invalid_arg;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ if (ops->datbuf)
+ ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
+ ops->len, ops->datbuf);
+ ofs += mtd->writesize + ops->ooboffs;
+ if (ops->oobbuf)
+ ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, ofs,
+ ops->ooblen, ops->oobbuf);
+ return 0;
+
+invalid_arg:
+ pr_err("goldfish_nand_read_oob: invalid read, start %llx, len %x, ooblen %x, dev_size %llx, write_size %x\n",
+ ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_write_oob(struct mtd_info *mtd, loff_t ofs,
+ struct mtd_oob_ops *ops)
+{
+ u32 rem;
+
+ if (ofs + ops->len > mtd->size)
+ goto invalid_arg;
+ if (ops->len && ops->len != mtd->writesize)
+ goto invalid_arg;
+ if (ops->ooblen + ops->ooboffs > mtd->oobsize)
+ goto invalid_arg;
+
+ rem = do_div(ofs, mtd->writesize);
+ if (rem)
+ goto invalid_arg;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ if (ops->datbuf)
+ ops->retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
+ ops->len, ops->datbuf);
+ ofs += mtd->writesize + ops->ooboffs;
+ if (ops->oobbuf)
+ ops->oobretlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, ofs,
+ ops->ooblen, ops->oobbuf);
+ return 0;
+
+invalid_arg:
+ pr_err("goldfish_nand_write_oob: invalid write, start %llx, len %x, ooblen %x, dev_size %llx, write_size %x\n",
+ ofs, ops->len, ops->ooblen, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ u32 rem;
+
+ if (from + len > mtd->size)
+ goto invalid_arg;
+ if (len != mtd->writesize)
+ goto invalid_arg;
+
+ rem = do_div(from, mtd->writesize);
+ if (rem)
+ goto invalid_arg;
+ from *= (mtd->writesize + mtd->oobsize);
+
+ *retlen = goldfish_nand_cmd(mtd, NAND_CMD_READ, from, len, buf);
+ return 0;
+
+invalid_arg:
+ pr_err("goldfish_nand_read: invalid read, start %llx, len %x, dev_size %llx, write_size %x\n",
+ from, len, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ u32 rem;
+
+ if (to + len > mtd->size)
+ goto invalid_arg;
+ if (len != mtd->writesize)
+ goto invalid_arg;
+
+ rem = do_div(to, mtd->writesize);
+ if (rem)
+ goto invalid_arg;
+ to *= (mtd->writesize + mtd->oobsize);
+
+ *retlen = goldfish_nand_cmd(mtd, NAND_CMD_WRITE, to, len, (void *)buf);
+ return 0;
+
+invalid_arg:
+ pr_err("goldfish_nand_write: invalid write, start %llx, len %x, dev_size %llx, write_size %x\n",
+ to, len, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+ u32 rem;
+
+ if (ofs >= mtd->size)
+ goto invalid_arg;
+
+ rem = do_div(ofs, mtd->erasesize);
+ if (rem)
+ goto invalid_arg;
+ ofs *= mtd->erasesize / mtd->writesize;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ return goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_GET, ofs, 0, NULL);
+
+invalid_arg:
+ pr_err("goldfish_nand_block_isbad: invalid arg, ofs %llx, dev_size %llx, write_size %x\n",
+ ofs, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int goldfish_nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ u32 rem;
+
+ if (ofs >= mtd->size)
+ goto invalid_arg;
+
+ rem = do_div(ofs, mtd->erasesize);
+ if (rem)
+ goto invalid_arg;
+ ofs *= mtd->erasesize / mtd->writesize;
+ ofs *= (mtd->writesize + mtd->oobsize);
+
+ if (goldfish_nand_cmd(mtd, NAND_CMD_BLOCK_BAD_SET, ofs, 0, NULL) != 1)
+ return -EIO;
+ return 0;
+
+invalid_arg:
+ pr_err("goldfish_nand_block_markbad: invalid arg, ofs %llx, dev_size %llx, write_size %x\n",
+ ofs, mtd->size, mtd->writesize);
+ return -EINVAL;
+}
+
+static int nand_setup_cmd_params(struct goldfish_nand *nand)
+{
+ u64 paddr;
+ unsigned char __iomem *base = nand->base;
+
+ nand->cmd_params = kmalloc(sizeof(struct cmd_params), GFP_KERNEL);
+ if (!nand->cmd_params)
+ return -1;
+
+ paddr = __pa(nand->cmd_params);
+ writel((u32)(paddr >> 32), base + NAND_CMD_PARAMS_ADDR_HIGH);
+ writel((u32)paddr, base + NAND_CMD_PARAMS_ADDR_LOW);
+ return 0;
+}
+
+static int goldfish_nand_init_device(struct goldfish_nand *nand, int id)
+{
+ u32 name_len;
+ u32 result;
+ u32 flags;
+ unsigned long irq_flags;
+ unsigned char __iomem *base = nand->base;
+ struct mtd_info *mtd = &nand->mtd[id];
+ char *name;
+
+ spin_lock_irqsave(&nand->lock, irq_flags);
+ writel(id, base + NAND_DEV);
+ flags = readl(base + NAND_DEV_FLAGS);
+ name_len = readl(base + NAND_DEV_NAME_LEN);
+ mtd->writesize = readl(base + NAND_DEV_PAGE_SIZE);
+ mtd->size = readl(base + NAND_DEV_SIZE_LOW);
+ mtd->size |= (u64)readl(base + NAND_DEV_SIZE_HIGH) << 32;
+ mtd->oobsize = readl(base + NAND_DEV_EXTRA_SIZE);
+ mtd->oobavail = mtd->oobsize;
+ mtd->erasesize = readl(base + NAND_DEV_ERASE_SIZE) /
+ (mtd->writesize + mtd->oobsize) * mtd->writesize;
+ do_div(mtd->size, mtd->writesize + mtd->oobsize);
+ mtd->size *= mtd->writesize;
+ pr_debug("goldfish nand dev%d: size %llx, page %d, extra %d, erase %d\n",
+ id, mtd->size, mtd->writesize, mtd->oobsize, mtd->erasesize);
+ spin_unlock_irqrestore(&nand->lock, irq_flags);
+
+ mtd->priv = nand;
+
+ mtd->name = name = kmalloc(name_len + 1, GFP_KERNEL);
+ if (name == NULL)
+ return -ENOMEM;
+
+ result = goldfish_nand_cmd(mtd, NAND_CMD_GET_DEV_NAME, 0, name_len,
+ name);
+ if (result != name_len) {
+ kfree(mtd->name);
+ mtd->name = NULL;
+ pr_err("goldfish_nand_init_device failed to get dev name %d != %d\n",
+ result, name_len);
+ return -ENODEV;
+ }
+ ((char *) mtd->name)[name_len] = '\0';
+
+ /* Setup the MTD structure */
+ mtd->type = MTD_NANDFLASH;
+ mtd->flags = MTD_CAP_NANDFLASH;
+ if (flags & NAND_DEV_FLAG_READ_ONLY)
+ mtd->flags &= ~MTD_WRITEABLE;
+ if (flags & NAND_DEV_FLAG_CMD_PARAMS_CAP)
+ nand_setup_cmd_params(nand);
+
+ mtd->owner = THIS_MODULE;
+ mtd->_erase = goldfish_nand_erase;
+ mtd->_read = goldfish_nand_read;
+ mtd->_write = goldfish_nand_write;
+ mtd->_read_oob = goldfish_nand_read_oob;
+ mtd->_write_oob = goldfish_nand_write_oob;
+ mtd->_block_isbad = goldfish_nand_block_isbad;
+ mtd->_block_markbad = goldfish_nand_block_markbad;
+
+ if (mtd_device_register(mtd, NULL, 0)) {
+ kfree(mtd->name);
+ mtd->name = NULL;
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int goldfish_nand_probe(struct platform_device *pdev)
+{
+ u32 num_dev;
+ int i;
+ int err;
+ u32 num_dev_working;
+ u32 version;
+ struct resource *r;
+ struct goldfish_nand *nand;
+ unsigned char __iomem *base;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (r == NULL) {
+ err = -ENODEV;
+ goto err_no_io_base;
+ }
+
+ base = ioremap(r->start, PAGE_SIZE);
+ if (base == NULL) {
+ err = -ENOMEM;
+ goto err_ioremap;
+ }
+ version = readl(base + NAND_VERSION);
+ if (version != NAND_VERSION_CURRENT) {
+ pr_err("goldfish_nand_init: version mismatch, got %d, expected %d\n",
+ version, NAND_VERSION_CURRENT);
+ err = -ENODEV;
+ goto err_no_dev;
+ }
+ num_dev = readl(base + NAND_NUM_DEV);
+ if (num_dev == 0) {
+ err = -ENODEV;
+ goto err_no_dev;
+ }
+
+ nand = kzalloc(sizeof(*nand) + sizeof(struct mtd_info) * num_dev,
+ GFP_KERNEL);
+ if (nand == NULL) {
+ err = -ENOMEM;
+ goto err_nand_alloc_failed;
+ }
+ spin_lock_init(&nand->lock);
+ nand->base = base;
+ nand->mtd_count = num_dev;
+ platform_set_drvdata(pdev, nand);
+
+ num_dev_working = 0;
+ for (i = 0; i < num_dev; i++) {
+ err = goldfish_nand_init_device(nand, i);
+ if (err == 0)
+ num_dev_working++;
+ }
+ if (num_dev_working == 0) {
+ err = -ENODEV;
+ goto err_no_working_dev;
+ }
+ return 0;
+
+err_no_working_dev:
+ kfree(nand);
+err_nand_alloc_failed:
+err_no_dev:
+ iounmap(base);
+err_ioremap:
+err_no_io_base:
+ return err;
+}
+
+static int goldfish_nand_remove(struct platform_device *pdev)
+{
+ struct goldfish_nand *nand = platform_get_drvdata(pdev);
+ int i;
+ for (i = 0; i < nand->mtd_count; i++) {
+ if (nand->mtd[i].name) {
+ mtd_device_unregister(&nand->mtd[i]);
+ kfree(nand->mtd[i].name);
+ }
+ }
+ kfree(nand->cmd_params);
+ iounmap(nand->base);
+ kfree(nand);
+ return 0;
+}
+
+static struct platform_driver goldfish_nand_driver = {
+ .probe = goldfish_nand_probe,
+ .remove = goldfish_nand_remove,
+ .driver = {
+ .name = "goldfish_nand"
+ }
+};
+
+static int __init goldfish_nand_init(void)
+{
+ return platform_driver_register(&goldfish_nand_driver);
+}
+
+static void __exit goldfish_nand_exit(void)
+{
+ platform_driver_unregister(&goldfish_nand_driver);
+}
+
+
+module_init(goldfish_nand_init);
+module_exit(goldfish_nand_exit);
diff --git a/drivers/mtd/devices/goldfish_nand_reg.h b/drivers/mtd/devices/goldfish_nand_reg.h
new file mode 100644
index 0000000..956c6c3
--- /dev/null
+++ b/drivers/mtd/devices/goldfish_nand_reg.h
@@ -0,0 +1,72 @@
+/* drivers/mtd/devices/goldfish_nand_reg.h
+**
+** Copyright (C) 2007 Google, Inc.
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+**
+*/
+
+#ifndef GOLDFISH_NAND_REG_H
+#define GOLDFISH_NAND_REG_H
+
+enum nand_cmd {
+ NAND_CMD_GET_DEV_NAME, /* Write device name for NAND_DEV to NAND_DATA (vaddr) */
+ NAND_CMD_READ,
+ NAND_CMD_WRITE,
+ NAND_CMD_ERASE,
+ NAND_CMD_BLOCK_BAD_GET, /* NAND_RESULT is 1 if block is bad, 0 if it is not */
+ NAND_CMD_BLOCK_BAD_SET,
+ NAND_CMD_READ_WITH_PARAMS,
+ NAND_CMD_WRITE_WITH_PARAMS,
+ NAND_CMD_ERASE_WITH_PARAMS
+};
+
+enum nand_dev_flags {
+ NAND_DEV_FLAG_READ_ONLY = 0x00000001,
+ NAND_DEV_FLAG_CMD_PARAMS_CAP = 0x00000002,
+};
+
+#define NAND_VERSION_CURRENT (1)
+
+enum nand_reg {
+ /* Global */
+ NAND_VERSION = 0x000,
+ NAND_NUM_DEV = 0x004,
+ NAND_DEV = 0x008,
+
+ /* Dev info */
+ NAND_DEV_FLAGS = 0x010,
+ NAND_DEV_NAME_LEN = 0x014,
+ NAND_DEV_PAGE_SIZE = 0x018,
+ NAND_DEV_EXTRA_SIZE = 0x01c,
+ NAND_DEV_ERASE_SIZE = 0x020,
+ NAND_DEV_SIZE_LOW = 0x028,
+ NAND_DEV_SIZE_HIGH = 0x02c,
+
+ /* Command */
+ NAND_RESULT = 0x040,
+ NAND_COMMAND = 0x044,
+ NAND_DATA = 0x048,
+ NAND_TRANSFER_SIZE = 0x04c,
+ NAND_ADDR_LOW = 0x050,
+ NAND_ADDR_HIGH = 0x054,
+ NAND_CMD_PARAMS_ADDR_LOW = 0x058,
+ NAND_CMD_PARAMS_ADDR_HIGH = 0x05c,
+};
+
+struct cmd_params {
+ uint32_t dev;
+ uint32_t addr_low;
+ uint32_t addr_high;
+ uint32_t transfer_size;
+ uint32_t data;
+ uint32_t result;
+};
+#endif
^ permalink raw reply related [flat|nested] 11+ messages in thread