linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/9] Upstream merge of core of Goldfish AVD support
@ 2012-12-03 15:04 Alan Cox
  2012-12-03 15:04 ` [PATCH 1/9] goldfish: definitions for Goldfish on x86 platforms Alan Cox
                   ` (8 more replies)
  0 siblings, 9 replies; 11+ messages in thread
From: Alan Cox @ 2012-12-03 15:04 UTC (permalink / raw)
  To: arve, x86, linux-kernel, mikechan

This is a port and clean up of the core platform and driver code for
the Goldfish AVD (Android Virtual Device).

With these patches applied you can boot Debian on Goldfish for minimal testing
purposes. It won't boot Android as there are other drivers needed, some of
which still need out of tree Android code.

Goldfish is both an ARM and an x86 platform. These patches add the x86 code
but they also add all the cleanups done to make most of the code portable. All
the drivers therefore are put into drivers/platform/goldfish or into the 
relevant driver directories  so they can be shared by other processors as
needed in future.

Once these drivers are in an upstream acceptable shape I'll look at feeding
the other bits in.

Alan
---

Arve Hjønnevåg (4):
      goldfish: tty driver
      goldfish: framebuffer
      goldfish: real time clock
      goldfish: NAND flash driver

Brian Swetland (1):
      goldfish: virtual input event driver

Jun Nakajima (2):
      goldfish: definitions for Goldfish on x86 platforms
      goldfish: add the goldfish virtual bus

Mike Lockwood (2):
      goldfish: emulated MMC device
      goldfish: power device


 arch/x86/Kconfig                         |   12 +
 arch/x86/include/asm/goldfish.h          |   43 ++
 drivers/input/keyboard/Kconfig           |   11 +
 drivers/input/keyboard/Makefile          |    1 
 drivers/input/keyboard/goldfish_events.c |  201 ++++++++++
 drivers/mmc/host/Kconfig                 |    7 
 drivers/mmc/host/Makefile                |    1 
 drivers/mmc/host/goldfish.c              |  579 ++++++++++++++++++++++++++++++
 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 ++++
 drivers/platform/Makefile                |    1 
 drivers/platform/goldfish/Makefile       |    4 
 drivers/platform/goldfish/pdev_bus.c     |  267 ++++++++++++++
 drivers/power/Kconfig                    |    8 
 drivers/power/Makefile                   |    1 
 drivers/power/goldfish_battery.c         |  263 ++++++++++++++
 drivers/rtc/Kconfig                      |    9 
 drivers/rtc/Makefile                     |    1 
 drivers/rtc/rtc-goldfish.c               |  146 ++++++++
 drivers/tty/Kconfig                      |    6 
 drivers/tty/Makefile                     |    1 
 drivers/tty/goldfish.c                   |  342 ++++++++++++++++++
 drivers/video/Kconfig                    |    9 
 drivers/video/Makefile                   |    1 
 drivers/video/goldfishfb.c               |  327 +++++++++++++++++
 27 files changed, 2796 insertions(+), 2 deletions(-)
 create mode 100644 arch/x86/include/asm/goldfish.h
 create mode 100644 drivers/input/keyboard/goldfish_events.c
 create mode 100644 drivers/mmc/host/goldfish.c
 create mode 100644 drivers/mtd/devices/goldfish_nand.c
 create mode 100644 drivers/mtd/devices/goldfish_nand_reg.h
 create mode 100644 drivers/platform/goldfish/Makefile
 create mode 100644 drivers/platform/goldfish/pdev_bus.c
 create mode 100644 drivers/power/goldfish_battery.c
 create mode 100644 drivers/rtc/rtc-goldfish.c
 create mode 100644 drivers/tty/goldfish.c
 create mode 100644 drivers/video/goldfishfb.c

-- 
Moo!


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [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

* [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

* 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

end of thread, other threads:[~2012-12-04 10:07 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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-04 10:07   ` Geert Uytterhoeven
2012-12-03 15:04 ` [PATCH 2/9] goldfish: add the goldfish virtual bus Alan Cox
2012-12-03 15:04 ` [PATCH 3/9] goldfish: tty driver Alan Cox
2012-12-03 15:05 ` [PATCH 4/9] goldfish: virtual input event driver Alan Cox
2012-12-03 15:05 ` [PATCH 5/9] goldfish: framebuffer Alan Cox
2012-12-03 15:05 ` [PATCH 6/9] goldfish: emulated MMC device Alan Cox
2012-12-03 15:05 ` [PATCH 7/9] goldfish: power device 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).