LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] [POWERPC][RFC] MPC8360E-RDK: Device tree and board file
From: Anton Vorontsov @ 2007-12-10 20:29 UTC (permalink / raw)
  To: linuxppc-dev

This is new board made by Freescale Semiconductor Inc. and
Logic Product Development.

Currently supported:
1. UEC1,2 (UEC2 doesn't work, but I'm sure this is firmware issue)
2. I2C
3. SPI
4. NS16550 serial

Not supported so far:
1. StMICRO NAND512W3A2BN6E, 512 Mbit
2. UEC3,4
3. QE SCCs (slow UCCs)
4. PCI
5. ADC AD7843
6. FHCI USB
7. Graphics controller, Fujitsu MB86277

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---

Hi all,

That patch is early RFC:

I tend to submit patches just as they are mature enough, thus not bomb
the list with long queues or huge patches. After I'll fix all upcoming
issues with that basic support, it would be great if someone will
merge it, thus I can start do incremental patches supporting this or
that.

Below is MPC8360E-RDK basic support. I'm following latest fashion,
so dts is v1. ;-)

Thanks,

p.s. not sending defconfig yet.

 arch/powerpc/boot/dts/mpc836x_rdk.dts     |  232 +++++++++++++++++++++++++++++
 arch/powerpc/platforms/83xx/Kconfig       |   10 +-
 arch/powerpc/platforms/83xx/Makefile      |    1 +
 arch/powerpc/platforms/83xx/mpc836x_rdk.c |  109 ++++++++++++++
 4 files changed, 351 insertions(+), 1 deletions(-)
 create mode 100644 arch/powerpc/boot/dts/mpc836x_rdk.dts
 create mode 100644 arch/powerpc/platforms/83xx/mpc836x_rdk.c

diff --git a/arch/powerpc/boot/dts/mpc836x_rdk.dts b/arch/powerpc/boot/dts/mpc836x_rdk.dts
new file mode 100644
index 0000000..a3b37e8
--- /dev/null
+++ b/arch/powerpc/boot/dts/mpc836x_rdk.dts
@@ -0,0 +1,232 @@
+/*
+ * MPC8360E RDK Device Tree Source
+ *
+ * Copyright 2006 Freescale Semiconductor Inc.
+ * Copyright 2007 MontaVista Software, Inc.
+ * 		  Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+/dts-v1/;
+
+/ {
+	model = "MPC8360RDK";
+	compatible = "MPC8360ERDK", "MPC836xRDK", "MPC83xxRDK";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		PowerPC,8360@0 {
+			device_type = "cpu";
+			reg = <0>;
+			d-cache-line-size = <32>;
+			i-cache-line-size = <32>;
+			d-cache-size = <32768>;
+			i-cache-size = <32768>;
+			timebase-frequency = <0>; /* filled by u-boot */
+			bus-frequency = <0>; /* filled by u-boot */
+			clock-frequency = <0>; /* filled by u-boot */
+		};
+	};
+
+	soc8360@e0000000 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		device_type = "soc";
+		ranges = <0 0xe0000000 0x00100000>;
+		reg = <0xe0000000 0x00000200>;
+		bus-frequency = <0>; /* filled by u-boot */
+
+		wdt@200 {
+			device_type = "watchdog";
+			compatible = "mpc83xx_wdt";
+			reg = <0x200 0x100>;
+		};
+
+		i2c@3000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			device_type = "i2c";
+			compatible = "fsl-i2c";
+			reg = <0x3000 0x100>;
+			interrupts = <14 8>;
+			interrupt-parent = <&ipic>;
+			dfsrr;
+		};
+
+		i2c@3100 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			device_type = "i2c";
+			compatible = "fsl-i2c";
+			reg = <0x3100 0x100>;
+			interrupts = <16 8>;
+			interrupt-parent = <&ipic>;
+			dfsrr;
+		};
+
+		serial@4500 {
+			device_type = "serial";
+			compatible = "ns16550";
+			reg = <0x4500 0x100>;
+			clock-frequency = <0>;
+			interrupts = <9 8>;
+			interrupt-parent = <&ipic>;
+		};
+
+		serial@4600 {
+			device_type = "serial";
+			compatible = "ns16550";
+			reg = <0x4600 0x100>;
+			clock-frequency = <0>;
+			interrupts = <10 8>;
+			interrupt-parent = <&ipic>;
+		};
+
+		crypto@30000 {
+			device_type = "crypto";
+			model = "SEC2";
+			compatible = "talitos";
+			reg = <0x30000 0x10000>;
+			interrupts = <11 8>;
+			interrupt-parent = <&ipic>;
+			num-channels = <4>;
+			channel-fifo-len = <24>;
+			exec-units-mask = <0x0000007e>;
+			/*
+			 * desc mask is for rev1.x, we need runtime fixup
+			 * for >=2.x
+			 */
+			descriptor-types-mask = <0x01010ebf>;
+		};
+
+		ipic: pic@700 {
+			interrupt-controller;
+			#address-cells = <0>;
+			#interrupt-cells = <2>;
+			reg = <0x700 0x100>;
+			device_type = "ipic";
+		};
+
+		par_io@1400 {
+			reg = <0x1400 0x100>;
+			num-ports = <7>;
+		};
+	};
+
+	qe@e0100000 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		device_type = "qe";
+		model = "QE";
+		ranges = <0 0xe0100000 0x00100000>;
+		reg = <0xe0100000 0x480>;
+		brg-frequency = <0>; /* filled by u-boot */
+		bus-frequency = <0>; /* filled by u-boot */
+
+		muram@10000 {
+			device_type = "muram";
+			ranges = <0 0x00010000 0x0000c000>;
+
+			data-only@0 {
+				reg = <0 0xc000>;
+			};
+		};
+
+		spi@4c0 {
+			device_type = "spi";
+			compatible = "fsl_spi";
+			reg = <0x4c0 0x40>;
+			interrupts = <2>;
+			interrupt-parent = <&qeic>;
+			mode = "cpu";
+		};
+
+		spi@500 {
+			device_type = "spi";
+			compatible = "fsl_spi";
+			reg = <0x500 0x40>;
+			interrupts = <1>;
+			interrupt-parent = <&qeic>;
+			mode = "cpu";
+		};
+
+		usb@6c0 {
+			device_type = "usb";
+			compatible = "qe_udc";
+			reg = <0x6c0 0x40 0x8b00 0x100>;
+			interrupts = <11>;
+			interrupt-parent = <&qeic>;
+			mode = "slave";
+		};
+
+		ucc@2000 {
+			device_type = "network";
+			compatible = "ucc_geth";
+			model = "UCC";
+			device-id = <1>;
+			reg = <0x2000 0x200>;
+			interrupts = <32>;
+			interrupt-parent = <&qeic>;
+			local-mac-address = [ 00 00 00 00 00 00 ];
+			rx-clock = <0>;
+			tx-clock = <25>;
+			phy-handle = <&phy2>;
+			phy-connection-type = "rgmii-id";
+		};
+
+		ucc@3000 {
+			device_type = "network";
+			compatible = "ucc_geth";
+			model = "UCC";
+			device-id = <2>;
+			reg = <0x3000 0x200>;
+			interrupts = <33>;
+			interrupt-parent = <&qeic>;
+			local-mac-address = [ 00 00 00 00 00 00 ];
+			rx-clock = <0>;
+			tx-clock = <20>;
+			phy-handle = <&phy4>;
+			phy-connection-type = "rgmii-id";
+		};
+
+		mdio@2120 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x2120 0x18>;
+			device_type = "mdio";
+			compatible = "ucc_geth_phy";
+
+			phy2: ethernet-phy@02 {
+				interrupt-parent = <&ipic>;
+				/*interrupts = <17 8>;*/
+				reg = <2>;
+				device_type = "ethernet-phy";
+			};
+			phy4: ethernet-phy@04 {
+				interrupt-parent = <&ipic>;
+				/*interrupts = <18 8>;*/
+				reg = <4>;
+				device_type = "ethernet-phy";
+			};
+		};
+
+		qeic: qeic@80 {
+			interrupt-controller;
+			device_type = "qeic";
+			#address-cells = <0>;
+			#interrupt-cells = <1>;
+			reg = <0x80 0x80>;
+			big-endian;
+			interrupts = <32 8 33 8>;
+			interrupt-parent = <&ipic>;
+		};
+	};
+};
diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig
index ec305f1..98f6358 100644
--- a/arch/powerpc/platforms/83xx/Kconfig
+++ b/arch/powerpc/platforms/83xx/Kconfig
@@ -50,6 +50,14 @@ config MPC836x_MDS
 	help
 	  This option enables support for the MPC836x MDS Processor Board.
 
+config MPC836x_RDK
+	bool "Freescale/Logic MPC836x RDK"
+	select DEFAULT_UIMAGE
+	select QUICC_ENGINE
+	help
+	  This option enables support for the MPC836x RDK Processor Board,
+	  also known as ZOOM PowerQUICC Kit.
+
 endchoice
 
 config PPC_MPC831x
@@ -74,4 +82,4 @@ config PPC_MPC836x
 	bool
 	select PPC_UDBG_16550
 	select PPC_INDIRECT_PCI
-	default y if MPC836x_MDS
+	default y if MPC836x_MDS || MPC836x_RDK
diff --git a/arch/powerpc/platforms/83xx/Makefile b/arch/powerpc/platforms/83xx/Makefile
index 5a98f88..24dcd75 100644
--- a/arch/powerpc/platforms/83xx/Makefile
+++ b/arch/powerpc/platforms/83xx/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_MPC834x_MDS)	+= mpc834x_mds.o
 obj-$(CONFIG_MPC834x_ITX)	+= mpc834x_itx.o
 obj-$(CONFIG_MPC836x_MDS)	+= mpc836x_mds.o
 obj-$(CONFIG_MPC832x_MDS)	+= mpc832x_mds.o
+obj-$(CONFIG_MPC836x_RDK)	+= mpc836x_rdk.o
diff --git a/arch/powerpc/platforms/83xx/mpc836x_rdk.c b/arch/powerpc/platforms/83xx/mpc836x_rdk.c
new file mode 100644
index 0000000..be9e2fd
--- /dev/null
+++ b/arch/powerpc/platforms/83xx/mpc836x_rdk.c
@@ -0,0 +1,109 @@
+/*
+ * MPC8360E-RDK board file.
+ *
+ * Copyright (c) 2006  Freescale Semicondutor, Inc.
+ * Copyright (c) 2007  MontaVista Software, Inc.
+ * 		       Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/of_platform.h>
+#include <asm/time.h>
+#include <asm/ipic.h>
+#include <asm/udbg.h>
+#include <asm/io.h>
+#include <asm/qe.h>
+#include <asm/qe_ic.h>
+#include <sysdev/fsl_soc.h>
+
+#include "mpc83xx.h"
+
+static struct of_device_id mpc836x_rdk_ids[] = {
+	{ .type = "soc", },
+	{ .compatible = "soc", },
+	{ .type = "qe", },
+	{},
+};
+
+static int __init mpc836x_rdk_declare_of_platform_devices(void)
+{
+	if (!machine_is(mpc836x_rdk))
+		return 0;
+
+	of_platform_bus_probe(NULL, mpc836x_rdk_ids, NULL);
+
+	return 0;
+}
+device_initcall(mpc836x_rdk_declare_of_platform_devices);
+
+static void __init mpc836x_rdk_setup_arch(void)
+{
+	struct device_node *np;
+
+	if (ppc_md.progress)
+		ppc_md.progress("mpc836x_rdk_setup_arch()", 0);
+
+#ifdef CONFIG_QUICC_ENGINE
+	qe_reset();
+
+	np = of_find_node_by_name(NULL, "par_io");
+	if (np)
+		par_io_init(np);
+	else
+		pr_warning("QE PIO not initialized!\n");
+#endif
+}
+
+static void __init mpc836x_rdk_init_IRQ(void)
+{
+	struct device_node *np;
+
+	np = of_find_node_by_type(NULL, "ipic");
+	if (!np)
+		return;
+
+	ipic_init(np, 0);
+
+	/*
+	 * Initialize the default interrupt mapping priorities,
+	 * in case the boot rom changed something on us.
+	 */
+	ipic_set_default_priority();
+	of_node_put(np);
+
+#ifdef CONFIG_QUICC_ENGINE
+	np = of_find_node_by_type(NULL, "qeic");
+	if (!np)
+		return;
+
+	qe_ic_init(np, 0, qe_ic_cascade_low_ipic, qe_ic_cascade_high_ipic);
+	of_node_put(np);
+#endif
+}
+
+/*
+ * Called very early, MMU is off, device-tree isn't unflattened
+ */
+static int __init mpc836x_rdk_probe(void)
+{
+	unsigned long root = of_get_flat_dt_root();
+
+	return of_flat_dt_is_compatible(root, "MPC836xRDK");
+}
+
+define_machine(mpc836x_rdk) {
+	.name		= "MPC836x RDK",
+	.probe		= mpc836x_rdk_probe,
+	.setup_arch	= mpc836x_rdk_setup_arch,
+	.init_IRQ	= mpc836x_rdk_init_IRQ,
+	.get_irq	= ipic_get_irq,
+	.restart	= mpc83xx_restart,
+	.time_init	= mpc83xx_time_init,
+	.calibrate_decr	= generic_calibrate_decr,
+	.progress	= udbg_progress,
+};
-- 
1.5.2.2

^ permalink raw reply related

* Re: [i2c] [PATCH 0/4] Series to add device tree naming to i2c
From: Benjamin Herrenschmidt @ 2007-12-10 20:32 UTC (permalink / raw)
  To: Scott Wood; +Cc: Jean Delvare, linuxppc-dev, i2c
In-Reply-To: <20071210164255.GA4497@loki.buserror.net>


On Mon, 2007-12-10 at 10:42 -0600, Scott Wood wrote:
> On Mon, Dec 10, 2007 at 08:38:46AM +1100, Benjamin Herrenschmidt wrote:
> > The more I think about it, the more I tend to agree that tagging isn't
> > necessary and you are right. We should just match the name against the
> > "compatible" property of the OF nodes (which mean we need to support
> > multiple matches though since "compatible" is a list of strings).
> 
> It may not be strictly necessary, but I think it's a good idea not just for
> safety reasons, but as an indication to the driver what additional
> information it has access to.  We could put a match data pointer in the i2c
> device, and have it be a valid node pointer if the match was an OF one (and
> a device-specific struct for a straight platform device, etc).  This could
> be useful if a device needs to have more properties than standard
> address/type/interrupt for some reason.

You don't need that much... On ppc64, all devices have an optional
device node pointer in struct device via the archdata, an we should do
that on ppc32 too (and will soon in order to merge some of the PCI & DMA
stuff anyway).

Ben.

^ permalink raw reply

* Re: [i2c] [PATCH 0/4] Series to add device tree naming to i2c
From: Benjamin Herrenschmidt @ 2007-12-10 20:35 UTC (permalink / raw)
  To: Jon Smirl; +Cc: Jean Delvare, i2c, linuxppc-dev
In-Reply-To: <9e4733910712101006s37829df4w676eea54a98b282d@mail.gmail.com>


On Mon, 2007-12-10 at 13:06 -0500, Jon Smirl wrote:
> 
> I can't see an easy way to do this. The basic problem is that the i2c
> drivers are assumed to be cross platform. I would need to add a path
> through the i2c core for getting a void pointer from the bus to the
> device But then when the device code gets this pointer it has no way
> of knowing what it was. Assuming the void is a pointer to an of_node
> would make the driver for the i2c device platform specific.

As I said, there's an arch data structure that can be added to -any-
struct device and that we use for, among others, an optional device tree
node pointer on ppc64. We should do that on ppc32 too (and will soon for
other reasons). In this case, the i2c core can be modified on powerpc so
that when it instanciate an i2c device from the device-tree, it fills
that field.

Some device drivers can have powerpc specific code that make good use of
that for things like calibration infos etc...

Ben.

^ permalink raw reply

* Re: [PATCH 4/25] powerpc: Reworking machine check handling and Fix 440/440A
From: Benjamin Herrenschmidt @ 2007-12-10 20:33 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev
In-Reply-To: <20071210115926.20278214@weaponx>


On Mon, 2007-12-10 at 11:59 -0600, Josh Boyer wrote:
> 
> This breaks ARCH=ppc builds.  Unfortunately, that tree shares the
> cputable.[ch] files, but has it's own traps.c.  Which means you get
> lots of nice undefined references like below for example:
> 
> arch/powerpc/kernel/built-in.o:(.init.data+0x44): undefined reference
> to `machine_check_4xx'
> arch/powerpc/kernel/built-in.o:(.init.data+0x8c): undefined reference
> to `machine_check_4xx'
> arch/powerpc/kernel/built-in.o:(.init.data+0xd4): undefined reference
> to `machine_check_4xx'
> 
> Because the cputable entries for the processors are setting
> the .machine_check function and it's never built.
> 
> I'm not sure which would be easier, making arch/ppc use traps.c from
> arch/powerpc, or adding similar functionality there.

Split cputable.c ? I hate arch/ppc sharing files ... Or I could port the
changes to arch/ppc. I don't want to use the same traps.c file. Sharing
file is just a pain every time we do major changes.

Ben.

^ permalink raw reply

* [PATCH RFC 0/7] "NAND on UPM" and related patches
From: Anton Vorontsov @ 2007-12-10 20:47 UTC (permalink / raw)
  To: linuxppc-dev

Hi all,

Here are patches to support NAND on UPM. That driver is generic for
all processors with FSL UPMs. And because of that, few more patches are
needed -- GPIO API and generic FSL UPM functions.

This is early RFC, all patches are in single thread, so everyone could
make up overall picture of what is going on. I'll split the thread by
topics after that RFC.

Ok, the patches and what they are for:

1,2,3,4. GPIO API:
------------------
Usually NAND chips exports RNB (Ready-Not-Busy) pin, so drivers
could read it and get a hint when chip is ready.

Often, WP (write protect) pin is also connected to GPIO. So, GPIO API
is mandatory for generic drivers.

OF device tree GPIOs bindings are similar to IRQs:

node {
	gpios = <bank pin bank pin bank pin>;
	gpio-parent = <&par_io_controller>;
};

"bank pin" scheme is controller specific, so controllers that want
to implement flat mappings or any other could do so.

So far I implemented GPIO API for QE and CPM2 chips.

QE GPIO API tested to work with FSL UPM NAND driver and MPC8360E-RDK
(STMicro NAND512W3A2BN6E).

CPM2 GPIO API tested to work with MPC8555E+Samsung HY27UF081G6 (LP).

GPIO API is described in Documentation/gpio.txt, and these
patches are tend to support most of it.

As an additional bonus, PowerPC now gets access to few pleasing
drivers:

- drivers/leds/leds-gpio.c (we could use it to play with
  on-board LEDs);
- drivers/i2c/busses/i2c-gpio.c (generic I2C bit-banging driver);
- drivers/input/keyboard/gpio_keys.c - gpio keys (requires
  gpio_to_irq, so far not implemented);
- Could be more (I named the ones I knew about).


Also, in the upcoming kernels, there will be GPIOLIB[1] addition to
the generic GPIO API, to support off-chip GPIO expanders (like MFDs
on I2C/LBC).

[1] http://www.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.24-rc4/2.6.24-rc4-mm1/broken-out/generic-gpio-gpio_chip-support.patch

But so far we support on-chip GPIOs only.

5 - FSL UPM infrastructure:
---------------------------
UPM address register is shared among UPMs, so we have to do
proper locking. On the other hand, if we know that specific
board using only one UPM we could bypass locking, and gain some
performance win.


6. FSL UPM NAND driver:
-----------------------
It's using FSL UPM functions and GPIO API. It does not implement
device tree partitions parsing. That issue is completely other
matter, that is, I have to factor out parsing functions from
physmap_of.c. This desires separate patch on top of the whole
series. If anyone currently working on this, let me know.


7. Example of usage.
--------------------
MPC8360E-RDK as an example.


I would appreciate any comments and suggestions, thanks!

-- 
Anton Vorontsov
email: cbou@mail.ru
backup email: ya-cbou@yandex.ru
irc://irc.freenode.net/bd2

^ permalink raw reply

* Re: [PATCH 4/25] powerpc: Reworking machine check handling and Fix 440/440A
From: Josh Boyer @ 2007-12-10 20:44 UTC (permalink / raw)
  To: benh; +Cc: linuxppc-dev
In-Reply-To: <1197318815.8692.7.camel@pasglop>

On Tue, 11 Dec 2007 07:33:35 +1100
Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:

> 
> On Mon, 2007-12-10 at 11:59 -0600, Josh Boyer wrote:
> > 
> > This breaks ARCH=ppc builds.  Unfortunately, that tree shares the
> > cputable.[ch] files, but has it's own traps.c.  Which means you get
> > lots of nice undefined references like below for example:
> > 
> > arch/powerpc/kernel/built-in.o:(.init.data+0x44): undefined reference
> > to `machine_check_4xx'
> > arch/powerpc/kernel/built-in.o:(.init.data+0x8c): undefined reference
> > to `machine_check_4xx'
> > arch/powerpc/kernel/built-in.o:(.init.data+0xd4): undefined reference
> > to `machine_check_4xx'
> > 
> > Because the cputable entries for the processors are setting
> > the .machine_check function and it's never built.
> > 
> > I'm not sure which would be easier, making arch/ppc use traps.c from
> > arch/powerpc, or adding similar functionality there.
> 
> Split cputable.c ? I hate arch/ppc sharing files ... Or I could port the
> changes to arch/ppc. I don't want to use the same traps.c file. Sharing
> file is just a pain every time we do major changes.

Splitting cputable.c at this point would be rather annoying, given the
number of changes we've made to arch/ppc recently to accommodate for it
being shared.  Porting the changes to arch/ppc sounds like the most
reasonable path.

I can't wait for arch/ppc to die.

josh

^ permalink raw reply

* [PATCH RFC 1/7] [POWERPC] Implement GPIO API embryo
From: Anton Vorontsov @ 2007-12-10 20:48 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20071210204705.GA31263@localhost.localdomain>

This patch implements GPIO API as described in Documentation/gpio.txt.
Two calls unimplemented though: irq_to_gpio and gpio_to_irq.

I'm also providing OF helpers.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/Kconfig             |    4 +++
 arch/powerpc/kernel/prom_parse.c |   50 +++++++++++++++++++++++++++++++++++++
 include/asm-powerpc/gpio.h       |   51 ++++++++++++++++++++++++++++++++++++++
 include/asm-powerpc/prom.h       |   23 +++++++++++++++++
 4 files changed, 128 insertions(+), 0 deletions(-)
 create mode 100644 include/asm-powerpc/gpio.h

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 232c298..596982f 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -73,6 +73,10 @@ config GENERIC_FIND_NEXT_BIT
 	bool
 	default y
 
+config GENERIC_GPIO
+	bool
+	default n
+
 config ARCH_NO_VIRT_TO_BUS
 	def_bool PPC64
 
diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c
index b5c96af..5228fe6 100644
--- a/arch/powerpc/kernel/prom_parse.c
+++ b/arch/powerpc/kernel/prom_parse.c
@@ -7,6 +7,7 @@
 #include <linux/ioport.h>
 #include <linux/etherdevice.h>
 #include <asm/prom.h>
+#include <asm/gpio.h>
 #include <asm/pci-bridge.h>
 
 #ifdef DEBUG
@@ -1067,3 +1068,52 @@ void __iomem *of_iomap(struct device_node *np, int index)
 	return ioremap(res.start, 1 + res.end - res.start);
 }
 EXPORT_SYMBOL(of_iomap);
+
+int __of_parse_gpio_bank_pin(struct device_node *np, int index,
+			     int bank_width, int max_bank)
+{
+	int bank;
+	int pin;
+	const u32 *gpios;
+	int len;
+
+	gpios = of_get_property(np, "gpios", &len);
+	len /= sizeof(u32);
+
+	if (len < 2 || len % 2 || index > len / 2 - 1)
+		return -EINVAL;
+
+	bank = gpios[index * 2];
+	pin = gpios[index * 2 + 1];
+
+	if (bank >= max_bank || pin >= bank_width)
+		return -EINVAL;
+
+	return bank * bank_width + pin;
+}
+EXPORT_SYMBOL_GPL(__of_parse_gpio_bank_pin);
+
+int of_get_gpio(struct device_node *np, int index)
+{
+	int ret;
+	const phandle *gc_ph;
+	struct device_node *gc;
+	struct of_gpio_chip *of_gpio_chip;
+
+	gc_ph = of_get_property(np, "gpio-parent", NULL);
+	if (!gc_ph)
+		return -EINVAL;
+
+	gc = of_find_node_by_phandle(*gc_ph);
+	if (!gc || !gc->data)
+		return -EINVAL;
+
+	of_gpio_chip = gc->data;
+
+	ret = of_gpio_chip->xlate(np, index);
+	if (ret < 0)
+		of_node_put(gc);
+
+	return ret;
+}
+EXPORT_SYMBOL(of_get_gpio);
diff --git a/include/asm-powerpc/gpio.h b/include/asm-powerpc/gpio.h
new file mode 100644
index 0000000..f7513ff
--- /dev/null
+++ b/include/asm-powerpc/gpio.h
@@ -0,0 +1,51 @@
+/*
+ * Generic GPIO API implementation for PowerPC.
+ *
+ * Copyright (c) 2007  MontaVista Software, Inc.
+ * Copyright (c) 2007  Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __ASM_POWERPC_GPIO_H
+#define __ASM_POWERPC_GPIO_H
+
+#ifdef CONFIG_GENERIC_GPIO
+
+extern int gpio_request(unsigned int gpio, const char *label);
+static inline void gpio_free(unsigned int gpio) {}
+
+extern int gpio_direction_input(unsigned int gpio);
+extern int gpio_direction_output(unsigned int gpio, int value);
+
+extern int gpio_get_value(unsigned int gpio);
+extern int gpio_set_value(unsigned int gpio, int value);
+
+/*
+ * Not implemented, yet.
+ */
+static inline int gpio_to_irq(unsigned int gpio)
+{
+	return -ENOSYS;
+}
+
+static inline int irq_to_gpio(unsigned int irq)
+{
+	return -ENOSYS;
+}
+
+/*
+ * OF specific gpio_chip handler.
+ */
+struct of_gpio_chip {
+	int (*xlate)(struct device_node *np, int index);
+};
+
+#endif /* CONFIG_GENERIC_GPIO */
+
+#include <asm-generic/gpio.h>
+
+#endif /* __ASM_POWERPC_GPIO_H */
diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h
index 925e2d3..019af61 100644
--- a/include/asm-powerpc/prom.h
+++ b/include/asm-powerpc/prom.h
@@ -326,6 +326,29 @@ extern int of_irq_to_resource(struct device_node *dev, int index,
  */
 extern void __iomem *of_iomap(struct device_node *device, int index);
 
+/**
+ * __of_parse_gpio_bank_pin - Helper function to translate "bank pin" GPIOs
+ * @np:		device node to get GPIO from
+ * @index:	index of the GPIO
+ * @bank_width:	pins per bank
+ * @max_bank:	maximum value to represent a bank
+ *
+ * Returns GPIO number to use with Linux generic GPIO api, or
+ * one of the errno value on the error condition.
+ */
+extern int __of_parse_gpio_bank_pin(struct device_node *np, int index,
+				    int bank_width, int max_bank);
+
+/**
+ * of_get_gpio - Translate GPIO number given in the device tree
+ * @np:		device node to get GPIO from
+ * @index:	index of the GPIO
+ *
+ * Returns GPIO number to use with Linux generic GPIO API, or
+ * one of the errno value on the error condition.
+ */
+extern int of_get_gpio(struct device_node *np, int index);
+
 /*
  * NB:  This is here while we transition from using asm/prom.h
  * to linux/of.h
-- 
1.5.2.2

^ permalink raw reply related

* [PATCH RFC 2/7] [POWERPC] QE: implement GPIO API
From: Anton Vorontsov @ 2007-12-10 20:48 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20071210204705.GA31263@localhost.localdomain>

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/platforms/Kconfig     |    1 +
 arch/powerpc/sysdev/qe_lib/qe_io.c |   93 +++++++++++++++++++++++++++++++++++-
 2 files changed, 92 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index ea22cad..3d9ff27 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -265,6 +265,7 @@ config TAU_AVERAGE
 config QUICC_ENGINE
 	bool
 	select PPC_LIB_RHEAP
+	select GENERIC_GPIO
 	help
 	  The QUICC Engine (QE) is a new generation of communications
 	  coprocessors on Freescale embedded CPUs (akin to CPM in older chips).
diff --git a/arch/powerpc/sysdev/qe_lib/qe_io.c b/arch/powerpc/sysdev/qe_lib/qe_io.c
index e53ea4d..3c9cf65 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_io.c
+++ b/arch/powerpc/sysdev/qe_lib/qe_io.c
@@ -23,6 +23,7 @@
 
 #include <asm/io.h>
 #include <asm/prom.h>
+#include <asm/gpio.h>
 #include <sysdev/fsl_soc.h>
 
 #undef DEBUG
@@ -43,12 +44,23 @@ struct port_regs {
 
 static struct port_regs *par_io = NULL;
 static int num_par_io_ports = 0;
+static spinlock_t *qe_pio_locks;
+
+static int par_io_xlate(struct device_node *np, int index)
+{
+	return __of_parse_gpio_bank_pin(np, index, 32, num_par_io_ports);
+}
+
+static struct of_gpio_chip of_gpio_chip = {
+	.xlate = par_io_xlate,
+};
 
 int par_io_init(struct device_node *np)
 {
 	struct resource res;
 	int ret;
 	const u32 *num_ports;
+	int i;
 
 	/* Map Parallel I/O ports registers */
 	ret = of_address_to_resource(np, 0, &res);
@@ -60,6 +72,18 @@ int par_io_init(struct device_node *np)
 	if (num_ports)
 		num_par_io_ports = *num_ports;
 
+	qe_pio_locks = kzalloc(sizeof(*qe_pio_locks) * num_par_io_ports,
+			       GFP_KERNEL);
+	if (!qe_pio_locks) {
+		iounmap(par_io);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < num_par_io_ports; i++)
+		spin_lock_init(&qe_pio_locks[i]);
+
+	np->data = &of_gpio_chip;
+
 	return 0;
 }
 
@@ -67,9 +91,12 @@ int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
 		      int assignment, int has_irq)
 {
 	u32 pin_mask1bit, pin_mask2bits, new_mask2bits, tmp_val;
+	unsigned long flags;
 
-	if (!par_io)
-		return -1;
+	if (!par_io || port > num_par_io_ports)
+		return -EINVAL;
+
+	spin_lock_irqsave(&qe_pio_locks[port], flags);
 
 	/* calculate pin location for single and 2 bits information */
 	pin_mask1bit = (u32) (1 << (NUM_OF_PINS - (pin + 1)));
@@ -126,6 +153,8 @@ int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
 		out_be32(&par_io[port].cppar1, new_mask2bits | tmp_val);
 	}
 
+	spin_unlock_irqrestore(&qe_pio_locks[port], flags);
+
 	return 0;
 }
 EXPORT_SYMBOL(par_io_config_pin);
@@ -133,6 +162,7 @@ EXPORT_SYMBOL(par_io_config_pin);
 int par_io_data_set(u8 port, u8 pin, u8 val)
 {
 	u32 pin_mask, tmp_val;
+	unsigned long flags;
 
 	if (port >= num_par_io_ports)
 		return -EINVAL;
@@ -141,6 +171,8 @@ int par_io_data_set(u8 port, u8 pin, u8 val)
 	/* calculate pin location */
 	pin_mask = (u32) (1 << (NUM_OF_PINS - 1 - pin));
 
+	spin_lock_irqsave(&qe_pio_locks[port], flags);
+
 	tmp_val = in_be32(&par_io[port].cpdata);
 
 	if (val == 0)		/* clear */
@@ -148,10 +180,25 @@ int par_io_data_set(u8 port, u8 pin, u8 val)
 	else			/* set */
 		out_be32(&par_io[port].cpdata, pin_mask | tmp_val);
 
+	spin_unlock_irqrestore(&qe_pio_locks[port], flags);
 	return 0;
 }
 EXPORT_SYMBOL(par_io_data_set);
 
+static inline int par_io_data_get(u8 port, u8 pin)
+{
+	u32 pin_mask;
+
+	if (port >= num_par_io_ports)
+		return -EINVAL;
+	if (pin >= NUM_OF_PINS)
+		return -EINVAL;
+	/* calculate pin location */
+	pin_mask = (u32) (1 << (NUM_OF_PINS - 1 - pin));
+
+	return !!(in_be32(&par_io[port].cpdata) & pin_mask);
+}
+
 int par_io_of_config(struct device_node *np)
 {
 	struct device_node *pio;
@@ -195,6 +242,48 @@ int par_io_of_config(struct device_node *np)
 }
 EXPORT_SYMBOL(par_io_of_config);
 
+int gpio_request(unsigned int gpio, const char *label)
+{
+	if (!qe_pio_locks)
+		return -ENODEV;
+
+	if (gpio / 32 > num_par_io_ports)
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_request);
+
+int gpio_direction_input(unsigned int gpio)
+{
+	return par_io_config_pin(gpio / 32, gpio % 32, 2, 0, 0, 0);
+}
+EXPORT_SYMBOL_GPL(gpio_direction_input);
+
+int gpio_direction_output(unsigned int gpio, int value)
+{
+	int ret;
+
+	ret = par_io_config_pin(gpio / 32, gpio % 32, 1, 0, 0, 0);
+	if (ret)
+		return ret;
+
+	return par_io_data_set(gpio / 32, gpio % 32, value);
+}
+EXPORT_SYMBOL_GPL(gpio_direction_output);
+
+int gpio_get_value(unsigned int gpio)
+{
+	return par_io_data_get(gpio / 32, gpio % 32);
+}
+EXPORT_SYMBOL_GPL(gpio_get_value);
+
+int gpio_set_value(unsigned int gpio, int value)
+{
+	return par_io_data_set(gpio / 32, gpio % 32, value);
+}
+EXPORT_SYMBOL_GPL(gpio_set_value);
+
 #ifdef DEBUG
 static void dump_par_io(void)
 {
-- 
1.5.2.2

^ permalink raw reply related

* [PATCH RFC 3/7] [POWERPC] CPM2: implement GPIO API
From: Anton Vorontsov @ 2007-12-10 20:48 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20071210204705.GA31263@localhost.localdomain>

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/platforms/Kconfig    |    1 +
 arch/powerpc/sysdev/cpm2_common.c |  142 +++++++++++++++++++++++++++++++++++++
 2 files changed, 143 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index 3d9ff27..cc2d54e 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -277,6 +277,7 @@ config CPM2
 	default n
 	select CPM
 	select PPC_LIB_RHEAP
+	select GENERIC_GPIO
 	help
 	  The CPM2 (Communications Processor Module) is a coprocessor on
 	  embedded CPUs made by Freescale.  Selecting this option means that
diff --git a/arch/powerpc/sysdev/cpm2_common.c b/arch/powerpc/sysdev/cpm2_common.c
index 859362f..33b99d4 100644
--- a/arch/powerpc/sysdev/cpm2_common.c
+++ b/arch/powerpc/sysdev/cpm2_common.c
@@ -31,12 +31,14 @@
 #include <linux/param.h>
 #include <linux/string.h>
 #include <linux/mm.h>
+#include <linux/spinlock.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/of.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
+#include <asm/gpio.h>
 #include <asm/mpc8260.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
@@ -61,9 +63,62 @@ cpm2_map_t __iomem *cpm2_immr;
 					   of space for CPM as it is larger
 					   than on PQ2 */
 
+static spinlock_t *cpm2_port_locks;
+static int cpm2_num_ports;
+
+static int par_io_xlate(struct device_node *np, int index)
+{
+	return __of_parse_gpio_bank_pin(np, index, 32, cpm2_num_ports);
+}
+
+static struct of_gpio_chip of_gpio_chip = {
+	.xlate = par_io_xlate,
+};
+
+int cpm2_init_par_io(void)
+{
+	int ret;
+	struct device_node *np;
+	const u32 *num_ports;
+	int i;
+
+	np = of_find_node_by_name(NULL, "par_io");
+	if (!np) {
+		ret = -ENOENT;
+		goto err0;
+	}
+
+	num_ports = of_get_property(np, "num-ports", NULL);
+	if (!num_ports) {
+		ret = -ENOENT;
+		goto err1;
+	}
+
+	cpm2_num_ports = *num_ports;
+	cpm2_port_locks = kzalloc(sizeof(*cpm2_port_locks) * cpm2_num_ports,
+				  GFP_KERNEL);
+	if (!cpm2_port_locks) {
+		ret = -ENOMEM;
+		goto err1;
+	}
+
+	for (i = 0; i < cpm2_num_ports; i++)
+		spin_lock_init(&cpm2_port_locks[i]);
+
+	np->data = &of_gpio_chip;
+
+	return 0;
+err1:
+	of_node_put(np);
+err0:
+	return ret;
+}
+
 void
 cpm2_reset(void)
 {
+	int ret;
+
 #ifdef CONFIG_PPC_85xx
 	cpm2_immr = ioremap(CPM_MAP_ADDR, CPM_MAP_SIZE);
 #else
@@ -81,6 +136,10 @@ cpm2_reset(void)
 	/* Tell everyone where the comm processor resides.
 	 */
 	cpmp = &cpm2_immr->im_cpm;
+
+	ret = cpm2_init_par_io();
+	if (ret)
+		pr_warning("CPM2 PIO not initialized!\n");
 }
 
 /* Set a baud rate generator.  This needs lots of work.  There are
@@ -444,3 +503,86 @@ void cpm2_set_pin(int port, int pin, int flags)
 	else
 		clrbits32(&iop[port].odr, pin);
 }
+
+int gpio_request(unsigned int gpio, const char *label)
+{
+	if (!cpm2_port_locks)
+		return -ENODEV;
+
+	if (gpio / 32 > cpm2_num_ports)
+		return -EINVAL;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_request);
+
+int gpio_direction_input(unsigned int gpio)
+{
+	unsigned long flags;
+	int port = gpio / 32;
+	int pin = gpio % 32;
+
+	spin_lock_irqsave(&cpm2_port_locks[port], flags);
+
+	cpm2_set_pin(port, pin, CPM_PIN_INPUT | CPM_PIN_GPIO);
+
+	spin_unlock_irqrestore(&cpm2_port_locks[port], flags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_direction_input);
+
+int gpio_direction_output(unsigned int gpio, int value)
+{
+	struct cpm2_ioports __iomem *iop =
+		(struct cpm2_ioports __iomem *)&cpm2_immr->im_ioport;
+	int port = gpio / 32;
+	int pin = gpio % 32;
+	unsigned long flags;
+
+	pin = 1 << (31 - pin);
+
+	spin_lock_irqsave(&cpm2_port_locks[port], flags);
+
+	cpm2_set_pin(port, pin, CPM_PIN_OUTPUT | CPM_PIN_GPIO);
+	if (value)
+		setbits32(&iop[port].dat, pin);
+	else
+		clrbits32(&iop[port].dat, pin);
+
+	spin_unlock_irqrestore(&cpm2_port_locks[port], flags);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_direction_output);
+
+int gpio_get_value(unsigned int gpio)
+{
+	struct cpm2_ioports __iomem *iop =
+		(struct cpm2_ioports __iomem *)&cpm2_immr->im_ioport;
+	int port = gpio / 32;
+	int pin = gpio % 32;
+
+	pin = 1 << (31 - pin);
+
+	return !!(in_be32(&iop[port].dat) & pin);
+}
+EXPORT_SYMBOL_GPL(gpio_get_value);
+
+int gpio_set_value(unsigned int gpio, int value)
+{
+	struct cpm2_ioports __iomem *iop =
+		(struct cpm2_ioports __iomem *)&cpm2_immr->im_ioport;
+	int port = gpio / 32;
+	int pin = gpio % 32;
+	unsigned long flags;
+
+	pin = 1 << (31 - pin);
+
+	spin_lock_irqsave(&cpm2_port_locks[port], flags);
+	if (value)
+		setbits32(&iop[port].dat, pin);
+	else
+		clrbits32(&iop[port].dat, pin);
+	spin_unlock_irqrestore(&cpm2_port_locks[port], flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_set_value);
-- 
1.5.2.2

^ permalink raw reply related

* [PATCH RFC 4/7] [GPIO] Let drivers link if they support GPIO API as an addition
From: Anton Vorontsov @ 2007-12-10 20:49 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: dbrownell
In-Reply-To: <20071210204705.GA31263@localhost.localdomain>

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---

Hello David,

I'm interested in your opinion about that patch. You're also Cc'ed
to patch that using that feature.

I know, currently that patch will conflict with GPIOLIB patches in -mm,
so this is only RFC.

 include/asm-generic/gpio.h |   47 ++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 47 insertions(+), 0 deletions(-)

diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 2d0aab1..cf76a69 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -1,6 +1,53 @@
 #ifndef _ASM_GENERIC_GPIO_H
 #define _ASM_GENERIC_GPIO_H
 
+#ifndef CONFIG_GENERIC_GPIO
+
+/*
+ * Drivers could be gpio api aware, and at the same time, some
+ * of them can live without it, or support call-backs approach
+ * in addition. Let them link.
+ */
+static inline int gpio_request(unsigned int gpio, const char *label)
+{
+	return -ENOSYS;
+}
+
+static inline void gpio_free(unsigned int gpio)
+{
+}
+
+static inline int gpio_direction_input(unsigned int gpio)
+{
+	return -ENOSYS;
+}
+
+static inline int gpio_direction_output(unsigned int gpio, int value)
+{
+	return -ENOSYS;
+}
+
+static inline int gpio_get_value(unsigned int gpio)
+{
+	return -ENOSYS;
+}
+
+static inline void gpio_set_value(unsigned int gpio, int value)
+{
+}
+
+static inline int gpio_to_irq(unsigned int gpio)
+{
+	return -ENOSYS;
+}
+
+static inline int irq_to_gpio(unsigned int irq)
+{
+	return -ENOSYS;
+}
+
+#endif /* CONFIG_GENERIC_GPIO */
+
 /* platforms that don't directly support access to GPIOs through I2C, SPI,
  * or other blocking infrastructure can use these wrappers.
  */
-- 
1.5.2.2

^ permalink raw reply related

* [PATCH RFC 5/7] [POWERPC] FSL UPM: routines to manage FSL UPMs
From: Anton Vorontsov @ 2007-12-10 20:49 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20071210204705.GA31263@localhost.localdomain>

Here are few routines needed to manage FSL UPMs properly. FSL UPM
infrastructure also supports lockless variant, to use when we're
pretty sure that only one UPM is used on the board.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/Kconfig          |    7 +++
 arch/powerpc/sysdev/Makefile  |    1 +
 arch/powerpc/sysdev/fsl_upm.c |   74 +++++++++++++++++++++++++++++
 include/asm-powerpc/fsl_upm.h |  102 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 184 insertions(+), 0 deletions(-)
 create mode 100644 arch/powerpc/sysdev/fsl_upm.c
 create mode 100644 include/asm-powerpc/fsl_upm.h

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 596982f..1d47a35 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -464,6 +464,13 @@ config FSL_PCI
  	bool
 	select PPC_INDIRECT_PCI
 
+config FSL_UPM
+	bool
+
+config FSL_UPM_LOCKLESS
+	depends on FSL_UPM
+	bool
+
 # Yes MCA RS/6000s exist but Linux-PPC does not currently support any
 config MCA
 	bool
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 99a77d7..98dbfdd 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_U3_DART)		+= dart_iommu.o
 obj-$(CONFIG_MMIO_NVRAM)	+= mmio_nvram.o
 obj-$(CONFIG_FSL_SOC)		+= fsl_soc.o
 obj-$(CONFIG_FSL_PCI)		+= fsl_pci.o
+obj-$(CONFIG_FSL_UPM)		+= fsl_upm.o
 obj-$(CONFIG_TSI108_BRIDGE)	+= tsi108_pci.o tsi108_dev.o
 obj-$(CONFIG_QUICC_ENGINE)	+= qe_lib/
 obj-$(CONFIG_PPC_BESTCOMM)	+= bestcomm/
diff --git a/arch/powerpc/sysdev/fsl_upm.c b/arch/powerpc/sysdev/fsl_upm.c
new file mode 100644
index 0000000..98d079d
--- /dev/null
+++ b/arch/powerpc/sysdev/fsl_upm.c
@@ -0,0 +1,74 @@
+/*
+ * Freescale UPM routines.
+ *
+ * Copyright (c) 2007  MontaVista Software, Inc.
+ * Copyright (c) 2007  Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <asm/fsl_upm.h>
+
+int fsl_upm_get_for(struct device_node *node, const char *name,
+		    struct fsl_upm *upm)
+{
+	int ret;
+	struct device_node *lbus;
+	struct resource lbc_res;
+	ptrdiff_t mxmr_offs;
+
+	lbus = of_get_parent(node);
+	if (!lbus) {
+		pr_err("FSL UPM: can't get parent local bus node\n");
+		return -ENOENT;
+	}
+
+	ret = of_address_to_resource(lbus, 0, &lbc_res);
+	if (ret) {
+		pr_err("FSL UPM: can't get parent local bus base\n");
+		return -ENOMEM;
+	}
+
+	switch (name[0]) {
+	case 'A':
+		mxmr_offs = LBC_MAMR;
+		break;
+	case 'B':
+		mxmr_offs = LBC_MBMR;
+		break;
+	case 'C':
+		mxmr_offs = LBC_MCMR;
+		break;
+	default:
+		pr_err("FSL UPM: unknown UPM requested\n");
+		return -EINVAL;
+		break;
+	}
+
+	upm->lbc_base = ioremap_nocache(lbc_res.start,
+					lbc_res.end - lbc_res.start + 1);
+	if (!upm->lbc_base)
+		return -ENOMEM;
+
+	upm->mxmr = upm->lbc_base + mxmr_offs;
+	upm->mar = upm->lbc_base + LBC_MAR;
+
+	return 0;
+}
+
+#ifndef CONFIG_FSL_UPM_LOCKLESS
+spinlock_t upm_lock;
+unsigned long upm_lock_flags;
+
+static int __init fsl_upm_init(void)
+{
+	spin_lock_init(&upm_lock);
+	return 0;
+}
+arch_initcall(fsl_upm_init);
+#endif
diff --git a/include/asm-powerpc/fsl_upm.h b/include/asm-powerpc/fsl_upm.h
new file mode 100644
index 0000000..19f5f9d
--- /dev/null
+++ b/include/asm-powerpc/fsl_upm.h
@@ -0,0 +1,102 @@
+/*
+ * Freescale UPM routines.
+ *
+ * Copyright (c) 2007  MontaVista Software, Inc.
+ * Copyright (c) 2007  Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __ASM_POWERPC_FSL_UPM
+#define __ASM_POWERPC_FSL_UPM
+
+#include <linux/spinlock.h>
+#include <asm/io.h>
+
+#define LBC_MAR		0x68
+#define LBC_MAMR	0x70
+#define LBC_MBMR	0x74
+#define LBC_MCMR	0x78
+
+#define LBC_MXMR_RUNP	0x30000000
+
+struct fsl_upm {
+	void __iomem *lbc_base;
+	void __iomem *mxmr;
+	void __iomem *mar;
+};
+
+#ifndef CONFIG_FSL_UPM_LOCKLESS
+extern spinlock_t upm_lock;
+extern unsigned long upm_lock_flags;
+
+static inline void upm_do_lock(void)
+{
+	spin_lock_irqsave(&upm_lock, upm_lock_flags);
+}
+
+static inline void upm_do_unlock(void)
+{
+	spin_unlock_irqrestore(&upm_lock, upm_lock_flags);
+}
+#else /* CONFIG_FSL_UPM_LOCKLESS */
+static inline void upm_do_lock(void) {}
+static inline void upm_do_unlock(void) {}
+#endif /* CONFIG_FSL_UPM_LOCKLESS */
+
+extern int fsl_upm_get_for(struct device_node *node, const char *name,
+			   struct fsl_upm *upm);
+
+static inline void fsl_upm_free(struct fsl_upm *upm)
+{
+	iounmap(upm->lbc_base);
+}
+
+static inline int fsl_upm_got(struct fsl_upm *upm)
+{
+	return !!upm->lbc_base;
+}
+
+static inline void fsl_upm_start_pattern(struct fsl_upm *upm, u32 pat_offset)
+{
+	upm_do_lock();
+	out_be32(upm->mxmr, LBC_MXMR_RUNP | pat_offset);
+}
+
+static inline void fsl_upm_end_pattern(struct fsl_upm *upm)
+{
+	out_be32(upm->mxmr, 0x0);
+
+	while (in_be32(upm->mxmr) != 0x0)
+		cpu_relax();
+
+	upm_do_unlock();
+}
+
+static inline int fsl_upm_run_pattern(struct fsl_upm *upm,
+				      void __iomem *io_base,
+				      int width, u32 cmd)
+{
+	out_be32(upm->mar, cmd << (32 - width));
+	switch (width) {
+	case 8:
+		out_8(io_base, 0x0);
+		break;
+	case 16:
+		out_be16(io_base, 0x0);
+		break;
+	case 32:
+		out_be32(io_base, 0x0);
+		break;
+	default:
+		return -EINVAL;
+		break;
+	}
+
+	return 0;
+}
+
+#endif /* __ASM_POWERPC_FSL_UPM */
-- 
1.5.2.2

^ permalink raw reply related

* [PATCH RFC 6/7] [POWERPC][NAND] FSL UPM NAND driver
From: Anton Vorontsov @ 2007-12-10 20:49 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: tglx, dbrownell, dwmw2
In-Reply-To: <20071210204705.GA31263@localhost.localdomain>

It's using FSL UPM infrastructure. So far only 8 bit accessors
are implemented.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 drivers/mtd/nand/Kconfig   |    7 +
 drivers/mtd/nand/Makefile  |    1 +
 drivers/mtd/nand/fsl_upm.c |  310 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 318 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/nand/fsl_upm.c

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 246d451..910b4d5 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -306,4 +306,11 @@ config MTD_ALAUDA
 	  These two (and possibly other) Alauda-based cardreaders for
 	  SmartMedia and xD allow raw flash access.
 
+config MTD_NAND_FSL_UPM
+	tristate "MTD driver for NAND on Freescale UPM"
+	depends on MTD_NAND && FSL_UPM
+	help
+	  Enables support for NAND Flash wired to Freescale processors'
+	  localbus with pre-programmed User-Programmable Machine.
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 3ad6c01..d553ea3 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -29,5 +29,6 @@ obj-$(CONFIG_MTD_NAND_CM_X270)		+= cmx270_nand.o
 obj-$(CONFIG_MTD_NAND_BASLER_EXCITE)	+= excite_nandflash.o
 obj-$(CONFIG_MTD_NAND_PLATFORM)		+= plat_nand.o
 obj-$(CONFIG_MTD_ALAUDA)		+= alauda.o
+obj-$(CONFIG_MTD_NAND_FSL_UPM)		+= fsl_upm.o
 
 nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c
new file mode 100644
index 0000000..a2bddb0
--- /dev/null
+++ b/drivers/mtd/nand/fsl_upm.c
@@ -0,0 +1,310 @@
+/*
+ * Freescale UPM NAND driver.
+ *
+ * Copyright (c) 2007  MontaVista Software, Inc.
+ * Copyright (c) 2007  Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/mtd.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+#include <asm/fsl_upm.h>
+
+struct upm_data {
+	struct of_device *ofdev;
+	struct mtd_info mtd;
+	struct nand_chip chip;
+	int last_ctrl;
+#ifdef CONFIG_MTD_PARTITIONS
+	struct mtd_partition *parts;
+#endif
+
+	struct fsl_upm upm;
+
+	int width;
+	int upm_addr_offset;
+	int upm_cmd_offset;
+	void __iomem *io_base;
+	int rnb_gpio;
+	const u32 *wait_pattern;
+	const u32 *wait_write;
+	int chip_delay;
+};
+
+#define to_upm_data(mtd) container_of(mtd, struct upm_data, mtd)
+
+static int upm_chip_ready(struct mtd_info *mtd)
+{
+	struct upm_data *ud = to_upm_data(mtd);
+
+	if (gpio_get_value(ud->rnb_gpio))
+		return 1;
+
+	dev_vdbg(&ud->ofdev->dev, "busy\n");
+	return 0;
+}
+
+static void upm_wait_rnb(struct upm_data *ud)
+{
+	int cnt = 1000000;
+
+	if (ud->rnb_gpio >= 0) {
+		while (--cnt && !upm_chip_ready(&ud->mtd))
+			cpu_relax();
+	}
+
+	if (!cnt)
+		dev_err(&ud->ofdev->dev, "tired waiting for RNB\n");
+}
+
+static void upm_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+	struct upm_data *ud = to_upm_data(mtd);
+
+	if (!(ctrl & ud->last_ctrl)) {
+		fsl_upm_end_pattern(&ud->upm);
+
+		if (cmd == NAND_CMD_NONE)
+			return;
+
+		ud->last_ctrl = ctrl & (NAND_ALE | NAND_CLE);
+	}
+
+	if (ctrl & NAND_CTRL_CHANGE) {
+		if (ctrl & NAND_ALE)
+			fsl_upm_start_pattern(&ud->upm, ud->upm_addr_offset);
+		else if (ctrl & NAND_CLE)
+			fsl_upm_start_pattern(&ud->upm, ud->upm_cmd_offset);
+	}
+
+	fsl_upm_run_pattern(&ud->upm, ud->io_base, ud->width, cmd);
+
+	if (ud->wait_pattern)
+		upm_wait_rnb(ud);
+}
+
+static uint8_t upm_read_byte(struct mtd_info *mtd)
+{
+	struct upm_data *ud = to_upm_data(mtd);
+
+	return in_8(ud->chip.IO_ADDR_R);
+}
+
+static void upm_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct upm_data *ud = to_upm_data(mtd);
+	int i;
+
+	for (i = 0; i < len; i++)
+		buf[i] = in_8(ud->chip.IO_ADDR_R);
+}
+
+static void upm_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	struct upm_data *ud = to_upm_data(mtd);
+	int i;
+
+	for (i = 0; i < len; i++) {
+		out_8(ud->chip.IO_ADDR_W, buf[i]);
+		if (ud->wait_write)
+			upm_wait_rnb(ud);
+	}
+}
+
+static int __devinit upm_chip_init(struct upm_data *ud)
+{
+	int ret;
+#ifdef CONFIG_MTD_PARTITIONS
+	static const char *part_types[] = { "cmdlinepart", NULL, };
+#endif
+
+	ud->chip.IO_ADDR_R = ud->io_base;
+	ud->chip.IO_ADDR_W = ud->io_base;
+	ud->chip.cmd_ctrl = upm_cmd_ctrl;
+	ud->chip.chip_delay = ud->chip_delay;
+	ud->chip.read_byte = upm_read_byte;
+	ud->chip.read_buf = upm_read_buf;
+	ud->chip.write_buf = upm_write_buf;
+	ud->chip.ecc.mode = NAND_ECC_SOFT;
+
+	if (ud->rnb_gpio != (unsigned int)-1)
+		ud->chip.dev_ready = upm_chip_ready;
+
+	ud->mtd.priv = &ud->chip;
+	ud->mtd.owner = THIS_MODULE;
+
+	ret = nand_scan(&ud->mtd, 1);
+	if (ret)
+		return ret;
+
+#ifdef CONFIG_MTD_PARTITIONS
+	ret = parse_mtd_partitions(&ud->mtd, part_types, &ud->parts, 0);
+	if (ret > 0)
+		return add_mtd_partitions(&ud->mtd, ud->parts, ret);
+#endif
+	return add_mtd_device(&ud->mtd);
+}
+
+static int __devinit upm_chip_probe(struct of_device *ofdev,
+				    const struct of_device_id *ofid)
+{
+	struct upm_data *ud;
+	struct resource io_res;
+	const u32 *prop;
+	int ret;
+
+	ud = kzalloc(sizeof(*ud), GFP_KERNEL);
+	if (!ud)
+		return -ENOMEM;
+
+	ret = of_address_to_resource(ofdev->node, 0, &io_res);
+	if (ret) {
+		dev_err(&ofdev->dev, "can't get IO base\n");
+		goto err;
+	}
+
+	ud->width = io_res.end - io_res.start + 1;
+	if (ud->width != 1) {
+		dev_err(&ofdev->dev, "> 8 bit accessors not implemented\n");
+		ret = -ENOSYS;
+		goto err;
+	}
+	ud->width *= 8;
+
+	prop = of_get_property(ofdev->node, "upm", NULL);
+	if (!prop) {
+		dev_err(&ofdev->dev, "can't get UPM to use\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = fsl_upm_get_for(ofdev->node, (const char *)prop, &ud->upm);
+	if (ret) {
+		dev_err(&ofdev->dev, "can't get FSL UPM\n");
+		goto err;
+	}
+
+	prop = of_get_property(ofdev->node, "upm-addr-offset", NULL);
+	if (!prop) {
+		dev_err(&ofdev->dev, "can't get UPM address offset\n");
+		ret = -EINVAL;
+		goto err;
+	}
+	ud->upm_addr_offset = *prop;
+
+	prop = of_get_property(ofdev->node, "upm-cmd-offset", NULL);
+	if (!prop) {
+		dev_err(&ofdev->dev, "can't get UPM command offset\n");
+		ret = -EINVAL;
+		goto err;
+	}
+	ud->upm_cmd_offset = *prop;
+
+	ud->rnb_gpio = of_get_gpio(ofdev->node, 0);
+	if (ud->rnb_gpio >= 0) {
+		ret = gpio_request(ud->rnb_gpio, ofdev->dev.bus_id);
+		if (ret) {
+			dev_err(&ofdev->dev, "can't request RNB gpio\n");
+			goto err;
+		}
+	} else if (ud->rnb_gpio == -EINVAL) {
+		dev_err(&ofdev->dev, "specified RNB gpio is invalid\n");
+		goto err;
+	}
+
+	ud->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start,
+					  io_res.end - io_res.start + 1);
+	if (!ud->io_base) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	ud->ofdev = ofdev;
+	ud->last_ctrl = NAND_CLE;
+	ud->wait_pattern = of_get_property(ofdev->node, "wait-pattern", NULL);
+	ud->wait_write = of_get_property(ofdev->node, "wait-write", NULL);
+
+	prop = of_get_property(ofdev->node, "chip-delay", NULL);
+	if (prop)
+		ud->chip_delay = *prop;
+	else
+		ud->chip_delay = 50;
+
+	ret = upm_chip_init(ud);
+	if (ret)
+		goto err;
+
+	dev_set_drvdata(&ofdev->dev, ud);
+
+	return 0;
+
+err:
+	if (fsl_upm_got(&ud->upm))
+		fsl_upm_free(&ud->upm);
+
+	if (ud->rnb_gpio >= 0)
+		gpio_free(ud->rnb_gpio);
+
+	kfree(ud);
+
+	return ret;
+}
+
+static int __devexit upm_chip_remove(struct of_device *ofdev)
+{
+	struct upm_data *ud = dev_get_drvdata(&ofdev->dev);
+
+	nand_release(&ud->mtd);
+
+	fsl_upm_free(&ud->upm);
+
+	if (ud->rnb_gpio != (unsigned int)-1)
+		gpio_free(ud->rnb_gpio);
+
+	kfree(ud);
+
+	return 0;
+}
+
+static struct of_device_id of_upm_nand_match[] = {
+	{ .compatible = "fsl,upm-nand" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_upm_nand_match);
+
+static struct of_platform_driver of_upm_chip_driver = {
+	.name		= "fsl_upm_nand",
+	.match_table	= of_upm_nand_match,
+	.probe		= upm_chip_probe,
+	.remove		= __devexit_p(upm_chip_remove),
+};
+
+static int __init upm_nand_init(void)
+{
+	return of_register_platform_driver(&of_upm_chip_driver);
+}
+
+static void __exit upm_nand_exit(void)
+{
+	of_unregister_platform_driver(&of_upm_chip_driver);
+}
+
+module_init(upm_nand_init);
+module_exit(upm_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
+MODULE_DESCRIPTION("Driver for NAND chips working through Freescale "
+		   "LocalBus User-Programmable Machine");
-- 
1.5.2.2

^ permalink raw reply related

* [PATCH RFC 7/7] [POWERPC] MPC8360E-RDK: add support for NAND on UPM
From: Anton Vorontsov @ 2007-12-10 20:49 UTC (permalink / raw)
  To: linuxppc-dev
In-Reply-To: <20071210204705.GA31263@localhost.localdomain>

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 arch/powerpc/boot/dts/mpc836x_rdk.dts     |   24 +++++++++++++++++++++++-
 arch/powerpc/platforms/83xx/Kconfig       |    2 ++
 arch/powerpc/platforms/83xx/mpc836x_rdk.c |    1 +
 3 files changed, 26 insertions(+), 1 deletions(-)

diff --git a/arch/powerpc/boot/dts/mpc836x_rdk.dts b/arch/powerpc/boot/dts/mpc836x_rdk.dts
index a1b2da6..f57ba53 100644
--- a/arch/powerpc/boot/dts/mpc836x_rdk.dts
+++ b/arch/powerpc/boot/dts/mpc836x_rdk.dts
@@ -115,7 +115,7 @@
 			device_type = "ipic";
 		};
 
-		par_io@1400 {
+		qe_pio: par_io@1400 {
 			reg = <0x1400 0x100>;
 			num-ports = <7>;
 		};
@@ -229,4 +229,26 @@
 			interrupt-parent = <&ipic>;
 		};
 	};
+
+	localbus@e0005000 {
+		#address-cells = <2>;
+		#size-cells = <1>;
+		compatible = "fsl,mpc8360erdk-localbus",
+			     "fsl,mpc8360e-localbus",
+			     "fsl,pq2pro-localbus";
+		reg = <0xe0005000 0xd8>;
+		ranges = <1 0 0x60000000 1>;
+
+		nand-flash@1,0 {
+			compatible = "STMicro,NAND512W3A2BN6E", "fsl,upm-nand";
+			reg = <1 0 1>;
+			upm = "A";
+			upm-addr-offset = <16>;
+			upm-cmd-offset = <8>;
+			gpios = <4 18>;
+			gpio-parent = <&qe_pio>;
+			wait-pattern;
+			wait-write;
+		};
+	};
 };
diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig
index 98f6358..2fc60c1 100644
--- a/arch/powerpc/platforms/83xx/Kconfig
+++ b/arch/powerpc/platforms/83xx/Kconfig
@@ -54,6 +54,7 @@ config MPC836x_RDK
 	bool "Freescale/Logic MPC836x RDK"
 	select DEFAULT_UIMAGE
 	select QUICC_ENGINE
+	select FSL_UPM_LOCKLESS
 	help
 	  This option enables support for the MPC836x RDK Processor Board,
 	  also known as ZOOM PowerQUICC Kit.
@@ -82,4 +83,5 @@ config PPC_MPC836x
 	bool
 	select PPC_UDBG_16550
 	select PPC_INDIRECT_PCI
+	select FSL_UPM
 	default y if MPC836x_MDS || MPC836x_RDK
diff --git a/arch/powerpc/platforms/83xx/mpc836x_rdk.c b/arch/powerpc/platforms/83xx/mpc836x_rdk.c
index be9e2fd..4288e16 100644
--- a/arch/powerpc/platforms/83xx/mpc836x_rdk.c
+++ b/arch/powerpc/platforms/83xx/mpc836x_rdk.c
@@ -27,6 +27,7 @@ static struct of_device_id mpc836x_rdk_ids[] = {
 	{ .type = "soc", },
 	{ .compatible = "soc", },
 	{ .type = "qe", },
+	{ .compatible = "fsl,pq2pro-localbus", },
 	{},
 };
 
-- 
1.5.2.2

^ permalink raw reply related

* ppc vs powerpc
From: Jon Smirl @ 2007-12-10 20:54 UTC (permalink / raw)
  To: PowerPC dev list

Is the plan for ppc to be completely merged with powerpc and then be
eliminated? If so, how close is this to completion?

What should we do with ppc users of mpc-i2c? If the plan is to
eliminate ppc I would say to just copy the existing driver and then
delete the old version when the rest of ppc gets deleted.

-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply

* Re: ppc vs powerpc
From: Grant Likely @ 2007-12-10 20:59 UTC (permalink / raw)
  To: Jon Smirl; +Cc: PowerPC dev list
In-Reply-To: <9e4733910712101254r1bf8925o20ee6eacd3af6a18@mail.gmail.com>

On 12/10/07, Jon Smirl <jonsmirl@gmail.com> wrote:
> Is the plan for ppc to be completely merged with powerpc and then be
> eliminated? If so, how close is this to completion?
>
> What should we do with ppc users of mpc-i2c? If the plan is to
> eliminate ppc I would say to just copy the existing driver and then
> delete the old version when the rest of ppc gets deleted.

arch/ppc is scheduled to be removed middle of 2008.  Don't break
arch/ppc users before then.  Ideally there should be overlap between
the addition of support in arch/powerpc and the removal of support
from arch/ppc.

Personally, I structure the driver to support both platform bus and
of_platform bus.  It's not hard to do and it keeps things
understandable.  Take a look at drivers/block/xsysace for an example.

Cheers,
g.


-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
grant.likely@secretlab.ca
(403) 399-0195

^ permalink raw reply

* Re: ppc vs powerpc
From: Jon Smirl @ 2007-12-10 21:04 UTC (permalink / raw)
  To: Grant Likely; +Cc: PowerPC dev list
In-Reply-To: <fa686aa40712101259h22be4b89ke4f36564a7c1866c@mail.gmail.com>

On 12/10/07, Grant Likely <grant.likely@secretlab.ca> wrote:
> On 12/10/07, Jon Smirl <jonsmirl@gmail.com> wrote:
> > Is the plan for ppc to be completely merged with powerpc and then be
> > eliminated? If so, how close is this to completion?
> >
> > What should we do with ppc users of mpc-i2c? If the plan is to
> > eliminate ppc I would say to just copy the existing driver and then
> > delete the old version when the rest of ppc gets deleted.
>
> arch/ppc is scheduled to be removed middle of 2008.  Don't break
> arch/ppc users before then.  Ideally there should be overlap between
> the addition of support in arch/powerpc and the removal of support
> from arch/ppc.
>
> Personally, I structure the driver to support both platform bus and
> of_platform bus.  It's not hard to do and it keeps things
> understandable.  Take a look at drivers/block/xsysace for an example.

I can look at it but I'm not enthused about writing new code to
support something that is scheduled to be deleted in six months. It
would be much simpler to just copy the old driver. I'm spending way
too much time on what I thought was a fairly simple change.

>
> Cheers,
> g.
>
>
> --
> Grant Likely, B.Sc., P.Eng.
> Secret Lab Technologies Ltd.
> grant.likely@secretlab.ca
> (403) 399-0195
>


-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply

* Re: [PATCH 1/5] PowerPC 74xx: Katana Qp device tree
From: Dale Farnsworth @ 2007-12-10 21:18 UTC (permalink / raw)
  To: Linuxppc-dev; +Cc: David Gibson
In-Reply-To: <20071203015018.GC26919@localhost.localdomain>

David Gibson wrote:
> On Thu, Nov 29, 2007 at 06:28:36PM +0300, Andrei Dolnikov wrote:
> > Device tree source file for the Emerson Katana Qp board

[snip]

> > +	mv64x60@f8100000 { /* Marvell Discovery */
> > +		#address-cells = <1>;
> > +		#size-cells = <1>;
> > +		model = "mv64460";			/* Default */
> > +		compatible = "marvell,mv64x60";

[snip]

> > +		mdio {
> 
> There must be some way of actuall accessing the mdio bus, so this node
> ought to have a 'reg' property and unit address.

There is no way for the cpu to directly access the mdio bus.  The
mdio bus is internally accessed by the ethernet MAC.  That being the
case, maybe it makes more sense to move the mdio node inside of the
multiethernet node, as follows, but I don't see how we can give it
a reg property or a unit address.

		multiethernet@2000 {
			reg = <0x2000 0x2000>;
			ethernet@0 {
				device_type = "network";
				compatible = "marvell,mv64360-eth";
				reg = <0>;
				interrupts = <32>;
				interrupt-parent = <&PIC>;
				phy = <&PHY0>;
				local-mac-address = [ 00 00 00 00 00 00 ];
			};
			ethernet@1 {
				device_type = "network";
				compatible = "marvell,mv64360-eth";
				reg = <1>;
				interrupts = <33>;
				interrupt-parent = <&PIC>;
				phy = <&PHY1>;
				local-mac-address = [ 00 00 00 00 00 00 ];
			};
			mdio {
				#address-cells = <1>;
				#size-cells = <0>;
				device_type = "mdio";
				compatible = "marvell,mv64360-mdio";
				PHY0: ethernet-phy@1 {
					device_type = "ethernet-phy";
					compatible = "broadcom,bcm5421";
					interrupts = <76>;	/* GPP 12 */
					interrupt-parent = <&PIC>;
					reg = <1>;
				};
				PHY1: ethernet-phy@3 {
					device_type = "ethernet-phy";
					compatible = "broadcom,bcm5421";
					interrupts = <76>;	/* GPP 12 */
					interrupt-parent = <&PIC>;
					reg = <3>;
				};
			};
		};

Look OK?

-Dale

^ permalink raw reply

* Re: [PATCH] IB/ehca: Serialize HCA-related hCalls on POWER5
From: Roland Dreier @ 2007-12-10 21:47 UTC (permalink / raw)
  To: Joachim Fenkes
  Cc: Arnd Bergmann, LKML, OF-EWG, linuxppc-dev, Christoph Raisch,
	Marcus Eder, OF-General, Stefan Roscher
In-Reply-To: <200712101841.30010.fenkes@de.ibm.com>

 > >     map_phys_fmr
 > 
 > In fact, we do use hCalls there. Our hardware doesn't actually support FMRs,
 > so we translate a "map FMR" into a "reallocate PMR", which doesn't work
 > without hCalls. What's more, the hCalls involved (e.g. H_FREE_RESOURCE)
 > might well return H_LONG_BUSY, so the whole operation might sleep; no way
 > around it.

It's a big problem.  If you cannot implement FMRs in such a way that
you can handling having map_phys_fmr being called in a context that
can't sleep, then I think the only option is to remove your FMR
support.  It's an optional device feature, so this should be OK
(although the iSER driver currently seems to depend on a device
supporting FMRs, which is probably going to be a problem with iWARP
support in the future anyway).

The fact that consumers can map FMRs from interrupt context, while
holding locks, etc, is pretty fundamental to the use of FMRs so I
don't see any way around the requirement that map_phys_fmr never
sleep.

 - R.

^ permalink raw reply

* Re: ppc vs powerpc
From: Jon Smirl @ 2007-12-10 21:54 UTC (permalink / raw)
  To: Grant Likely; +Cc: PowerPC dev list
In-Reply-To: <fa686aa40712101259h22be4b89ke4f36564a7c1866c@mail.gmail.com>

On 12/10/07, Grant Likely <grant.likely@secretlab.ca> wrote:
> On 12/10/07, Jon Smirl <jonsmirl@gmail.com> wrote:
> > Is the plan for ppc to be completely merged with powerpc and then be
> > eliminated? If so, how close is this to completion?
> >
> > What should we do with ppc users of mpc-i2c? If the plan is to
> > eliminate ppc I would say to just copy the existing driver and then
> > delete the old version when the rest of ppc gets deleted.
>
> arch/ppc is scheduled to be removed middle of 2008.  Don't break
> arch/ppc users before then.  Ideally there should be overlap between
> the addition of support in arch/powerpc and the removal of support
> from arch/ppc.

How can I tell a build for ARCH=ppc vs ARCH=powerpc in Kconfig?

> Personally, I structure the driver to support both platform bus and
> of_platform bus.  It's not hard to do and it keeps things
> understandable.  Take a look at drivers/block/xsysace for an example.
>
> Cheers,
> g.
>
>
> --
> Grant Likely, B.Sc., P.Eng.
> Secret Lab Technologies Ltd.
> grant.likely@secretlab.ca
> (403) 399-0195
>


-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply

* Re: ppc vs powerpc
From: Tony Breeds @ 2007-12-10 22:01 UTC (permalink / raw)
  To: Jon Smirl; +Cc: PowerPC dev list
In-Reply-To: <9e4733910712101354i31020c2w72d74bf33ba3e90b@mail.gmail.com>

On Mon, Dec 10, 2007 at 04:54:32PM -0500, Jon Smirl wrote:
 
> How can I tell a build for ARCH=ppc vs ARCH=powerpc in Kconfig?

IIRC ARCH=powerpc will have CONFIG_PPC_MERGE set, arch=ppc will not.

Yours Tony

  linux.conf.au        http://linux.conf.au/ || http://lca2008.linux.org.au/
  Jan 28 - Feb 02 2008 The Australian Linux Technical Conference!

^ permalink raw reply

* [PATCH] Addition to the i2c series, copy the ppc mpc-i2c driver before changing it on powerpc
From: Jon Smirl @ 2007-12-10 22:20 UTC (permalink / raw)
  To: PowerPC dev list

Copy mpc-i2c to preserve support for ARCH=ppc and allow changes on ARCH=powerpc

Temporarily copy the mpc-i2c driver to continue support for the ppc
architecture until it is removed in mid-2008. This file should be
deleted as part of ppc's final removal.

Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
---

 Makefile                               |    2
 arch/ppc/configs/TQM8540_defconfig     |    2
 arch/ppc/configs/TQM8541_defconfig     |    2
 arch/ppc/configs/TQM8555_defconfig     |    2
 arch/ppc/configs/TQM8560_defconfig     |    2
 arch/ppc/configs/mpc834x_sys_defconfig |    2
 arch/ppc/configs/mpc8540_ads_defconfig |    2
 arch/ppc/configs/mpc8548_cds_defconfig |    2
 arch/ppc/configs/mpc8555_cds_defconfig |    2
 arch/ppc/configs/mpc8560_ads_defconfig |    2
 drivers/i2c/busses/Kconfig             |   16 +
 drivers/i2c/busses/Makefile            |    1
 drivers/i2c/busses/i2c-mpc-ppc.c       |  418 ++++++++++++++++++++++++++++++++
 13 files changed, 444 insertions(+), 11 deletions(-)
 create mode 100644 drivers/i2c/busses/i2c-mpc-ppc.c


diff --git a/Makefile b/Makefile
index 013b43a..3e714e2 100644
--- a/Makefile
+++ b/Makefile
@@ -35,7 +35,7 @@ MAKEFLAGS += -rR --no-print-directory
 # To put more focus on warnings, be less verbose as default
 # Use 'make V=1' to see the full commands

-ARCH=powerpc
+ARCH=ppc
 CROSS_COMPILE=powerpc-603e-linux-gnu-

 ifdef V
diff --git a/arch/ppc/configs/TQM8540_defconfig
b/arch/ppc/configs/TQM8540_defconfig
index f33f0e7..7098ed0 100644
--- a/arch/ppc/configs/TQM8540_defconfig
+++ b/arch/ppc/configs/TQM8540_defconfig
@@ -684,7 +684,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
 # CONFIG_I2C_PIIX4 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_PROSAVAGE is not set
diff --git a/arch/ppc/configs/TQM8541_defconfig
b/arch/ppc/configs/TQM8541_defconfig
index e00cd62..2137d01 100644
--- a/arch/ppc/configs/TQM8541_defconfig
+++ b/arch/ppc/configs/TQM8541_defconfig
@@ -687,7 +687,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
 # CONFIG_I2C_PIIX4 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_MPC8260 is not set
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
diff --git a/arch/ppc/configs/TQM8555_defconfig
b/arch/ppc/configs/TQM8555_defconfig
index 43a0d9d..f2317b6 100644
--- a/arch/ppc/configs/TQM8555_defconfig
+++ b/arch/ppc/configs/TQM8555_defconfig
@@ -687,7 +687,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
 # CONFIG_I2C_PIIX4 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_PROSAVAGE is not set
diff --git a/arch/ppc/configs/TQM8560_defconfig
b/arch/ppc/configs/TQM8560_defconfig
index a814d17..6c19121 100644
--- a/arch/ppc/configs/TQM8560_defconfig
+++ b/arch/ppc/configs/TQM8560_defconfig
@@ -694,7 +694,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
 # CONFIG_I2C_PIIX4 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_MPC8260 is not set
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
diff --git a/arch/ppc/configs/mpc834x_sys_defconfig
b/arch/ppc/configs/mpc834x_sys_defconfig
index d90c8a7..cd568d2 100644
--- a/arch/ppc/configs/mpc834x_sys_defconfig
+++ b/arch/ppc/configs/mpc834x_sys_defconfig
@@ -562,7 +562,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
 # CONFIG_I2C_PIIX4 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_PROSAVAGE is not set
diff --git a/arch/ppc/configs/mpc8540_ads_defconfig
b/arch/ppc/configs/mpc8540_ads_defconfig
index bf676eb..5819835 100644
--- a/arch/ppc/configs/mpc8540_ads_defconfig
+++ b/arch/ppc/configs/mpc8540_ads_defconfig
@@ -452,7 +452,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_AMD8111 is not set
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_PIIX4 is not set
diff --git a/arch/ppc/configs/mpc8548_cds_defconfig
b/arch/ppc/configs/mpc8548_cds_defconfig
index f36fc5d..e5b5071 100644
--- a/arch/ppc/configs/mpc8548_cds_defconfig
+++ b/arch/ppc/configs/mpc8548_cds_defconfig
@@ -413,7 +413,7 @@ CONFIG_I2C_CHARDEV=y
 #
 # I2C Hardware Bus support
 #
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_PCA_ISA is not set

diff --git a/arch/ppc/configs/mpc8555_cds_defconfig
b/arch/ppc/configs/mpc8555_cds_defconfig
index 4f1e320..08dbab0 100644
--- a/arch/ppc/configs/mpc8555_cds_defconfig
+++ b/arch/ppc/configs/mpc8555_cds_defconfig
@@ -518,7 +518,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
 # CONFIG_I2C_PIIX4 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_PROSAVAGE is not set
diff --git a/arch/ppc/configs/mpc8560_ads_defconfig
b/arch/ppc/configs/mpc8560_ads_defconfig
index f12d48f..0e0080b 100644
--- a/arch/ppc/configs/mpc8560_ads_defconfig
+++ b/arch/ppc/configs/mpc8560_ads_defconfig
@@ -489,7 +489,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
 # CONFIG_I2C_PIIX4 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_PROSAVAGE is not set
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index c466c6c..bdde307 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -301,7 +301,7 @@ config I2C_POWERMAC

 config I2C_MPC
 	tristate "MPC107/824x/85xx/52xx/86xx"
-	depends on PPC32
+	depends on PPC32 && PPC_MERGE
 	help
 	  If you say yes to this option, support will be included for the
 	  built-in I2C interface on the MPC107/Tsi107/MPC8240/MPC8245 and
@@ -311,6 +311,20 @@ config I2C_MPC
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-mpc.

+config I2C_MPC_PPC
+	tristate "MPC107/824x/85xx/52xx/86xx"
+	depends on PPC32 && !PPC_MERGE
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in I2C interface on the MPC107/Tsi107/MPC8240/MPC8245 and
+	  MPC85xx/MPC8641 family processors. The driver may also work on 52xx
+	  family processors, though interrupts are known not to work.
+	
+	  This version of the driver is scheduled for deletion with the PPC
architecture.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-mpc.
+
 config I2C_NFORCE2
 	tristate "Nvidia nForce2, nForce3 and nForce4"
 	depends on PCI
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 81d43c2..b7fe095 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_I2C_IXP2000)	+= i2c-ixp2000.o
 obj-$(CONFIG_I2C_IXP4XX)	+= i2c-ixp4xx.o
 obj-$(CONFIG_I2C_POWERMAC)	+= i2c-powermac.o
 obj-$(CONFIG_I2C_MPC)		+= i2c-mpc.o
+obj-$(CONFIG_I2C_MPC_PPC)	+= i2c-mpc-ppc.o
 obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_OCORES)	+= i2c-ocores.o
diff --git a/drivers/i2c/busses/i2c-mpc-ppc.c b/drivers/i2c/busses/i2c-mpc-ppc.c
new file mode 100644
index 0000000..d8de4ac
--- /dev/null
+++ b/drivers/i2c/busses/i2c-mpc-ppc.c
@@ -0,0 +1,418 @@
+/*
+ * (C) Copyright 2003-2004
+ * Humboldt Solutions Ltd, adrian@humboldt.co.uk.
+
+ * This is a combined i2c adapter and algorithm driver for the
+ * MPC107/Tsi107 PowerPC northbridge and processors that include
+ * the same I2C unit (8240, 8245, 85xx).
+ *
+ * Release 0.8
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <linux/fsl_devices.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#define MPC_I2C_ADDR  0x00
+#define MPC_I2C_FDR 	0x04
+#define MPC_I2C_CR	0x08
+#define MPC_I2C_SR	0x0c
+#define MPC_I2C_DR	0x10
+#define MPC_I2C_DFSRR 0x14
+#define MPC_I2C_REGION 0x20
+
+#define CCR_MEN  0x80
+#define CCR_MIEN 0x40
+#define CCR_MSTA 0x20
+#define CCR_MTX  0x10
+#define CCR_TXAK 0x08
+#define CCR_RSTA 0x04
+
+#define CSR_MCF  0x80
+#define CSR_MAAS 0x40
+#define CSR_MBB  0x20
+#define CSR_MAL  0x10
+#define CSR_SRW  0x04
+#define CSR_MIF  0x02
+#define CSR_RXAK 0x01
+
+struct mpc_i2c {
+	void __iomem *base;
+	u32 interrupt;
+	wait_queue_head_t queue;
+	struct i2c_adapter adap;
+	int irq;
+	u32 flags;
+};
+
+static __inline__ void writeccr(struct mpc_i2c *i2c, u32 x)
+{
+	writeb(x, i2c->base + MPC_I2C_CR);
+}
+
+static irqreturn_t mpc_i2c_isr(int irq, void *dev_id)
+{
+	struct mpc_i2c *i2c = dev_id;
+	if (readb(i2c->base + MPC_I2C_SR) & CSR_MIF) {
+		/* Read again to allow register to stabilise */
+		i2c->interrupt = readb(i2c->base + MPC_I2C_SR);
+		writeb(0, i2c->base + MPC_I2C_SR);
+		wake_up_interruptible(&i2c->queue);
+	}
+	return IRQ_HANDLED;
+}
+
+/* Sometimes 9th clock pulse isn't generated, and slave doesn't release
+ * the bus, because it wants to send ACK.
+ * Following sequence of enabling/disabling and sending start/stop generates
+ * the pulse, so it's all OK.
+ */
+static void mpc_i2c_fixup(struct mpc_i2c *i2c)
+{
+	writeccr(i2c, 0);
+	udelay(30);
+	writeccr(i2c, CCR_MEN);
+	udelay(30);
+	writeccr(i2c, CCR_MSTA | CCR_MTX);
+	udelay(30);
+	writeccr(i2c, CCR_MSTA | CCR_MTX | CCR_MEN);
+	udelay(30);
+	writeccr(i2c, CCR_MEN);
+	udelay(30);
+}
+
+static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
+{
+	unsigned long orig_jiffies = jiffies;
+	u32 x;
+	int result = 0;
+
+	if (i2c->irq == 0)
+	{
+		while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) {
+			schedule();
+			if (time_after(jiffies, orig_jiffies + timeout)) {
+				pr_debug("I2C: timeout\n");
+				writeccr(i2c, 0);
+				result = -EIO;
+				break;
+			}
+		}
+		x = readb(i2c->base + MPC_I2C_SR);
+		writeb(0, i2c->base + MPC_I2C_SR);
+	} else {
+		/* Interrupt mode */
+		result = wait_event_interruptible_timeout(i2c->queue,
+			(i2c->interrupt & CSR_MIF), timeout * HZ);
+
+		if (unlikely(result < 0)) {
+			pr_debug("I2C: wait interrupted\n");
+			writeccr(i2c, 0);
+		} else if (unlikely(!(i2c->interrupt & CSR_MIF))) {
+			pr_debug("I2C: wait timeout\n");
+			writeccr(i2c, 0);
+			result = -ETIMEDOUT;
+		}
+
+		x = i2c->interrupt;
+		i2c->interrupt = 0;
+	}
+
+	if (result < 0)
+		return result;
+
+	if (!(x & CSR_MCF)) {
+		pr_debug("I2C: unfinished\n");
+		return -EIO;
+	}
+
+	if (x & CSR_MAL) {
+		pr_debug("I2C: MAL\n");
+		return -EIO;
+	}
+
+	if (writing && (x & CSR_RXAK)) {
+		pr_debug("I2C: No RXAK\n");
+		/* generate stop */
+		writeccr(i2c, CCR_MEN);
+		return -EIO;
+	}
+	return 0;
+}
+
+static void mpc_i2c_setclock(struct mpc_i2c *i2c)
+{
+	/* Set clock and filters */
+	if (i2c->flags & FSL_I2C_DEV_SEPARATE_DFSRR) {
+		writeb(0x31, i2c->base + MPC_I2C_FDR);
+		writeb(0x10, i2c->base + MPC_I2C_DFSRR);
+	} else if (i2c->flags & FSL_I2C_DEV_CLOCK_5200)
+		writeb(0x3f, i2c->base + MPC_I2C_FDR);
+	else
+		writel(0x1031, i2c->base + MPC_I2C_FDR);
+}
+
+static void mpc_i2c_start(struct mpc_i2c *i2c)
+{
+	/* Clear arbitration */
+	writeb(0, i2c->base + MPC_I2C_SR);
+	/* Start with MEN */
+	writeccr(i2c, CCR_MEN);
+}
+
+static void mpc_i2c_stop(struct mpc_i2c *i2c)
+{
+	writeccr(i2c, CCR_MEN);
+}
+
+static int mpc_write(struct mpc_i2c *i2c, int target,
+		     const u8 * data, int length, int restart)
+{
+	int i;
+	unsigned timeout = i2c->adap.timeout;
+	u32 flags = restart ? CCR_RSTA : 0;
+
+	/* Start with MEN */
+	if (!restart)
+		writeccr(i2c, CCR_MEN);
+	/* Start as master */
+	writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
+	/* Write target byte */
+	writeb((target << 1), i2c->base + MPC_I2C_DR);
+
+	if (i2c_wait(i2c, timeout, 1) < 0)
+		return -1;
+
+	for (i = 0; i < length; i++) {
+		/* Write data byte */
+		writeb(data[i], i2c->base + MPC_I2C_DR);
+
+		if (i2c_wait(i2c, timeout, 1) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+static int mpc_read(struct mpc_i2c *i2c, int target,
+		    u8 * data, int length, int restart)
+{
+	unsigned timeout = i2c->adap.timeout;
+	int i;
+	u32 flags = restart ? CCR_RSTA : 0;
+
+	/* Start with MEN */
+	if (!restart)
+		writeccr(i2c, CCR_MEN);
+	/* Switch to read - restart */
+	writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
+	/* Write target address byte - this time with the read flag set */
+	writeb((target << 1) | 1, i2c->base + MPC_I2C_DR);
+
+	if (i2c_wait(i2c, timeout, 1) < 0)
+		return -1;
+
+	if (length) {
+		if (length == 1)
+			writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
+		else
+			writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA);
+		/* Dummy read */
+		readb(i2c->base + MPC_I2C_DR);
+	}
+
+	for (i = 0; i < length; i++) {
+		if (i2c_wait(i2c, timeout, 0) < 0)
+			return -1;
+
+		/* Generate txack on next to last byte */
+		if (i == length - 2)
+			writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
+		/* Generate stop on last byte */
+		if (i == length - 1)
+			writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_TXAK);
+		data[i] = readb(i2c->base + MPC_I2C_DR);
+	}
+
+	return length;
+}
+
+static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+	struct i2c_msg *pmsg;
+	int i;
+	int ret = 0;
+	unsigned long orig_jiffies = jiffies;
+	struct mpc_i2c *i2c = i2c_get_adapdata(adap);
+
+	mpc_i2c_start(i2c);
+
+	/* Allow bus up to 1s to become not busy */
+	while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) {
+		if (signal_pending(current)) {
+			pr_debug("I2C: Interrupted\n");
+			writeccr(i2c, 0);
+			return -EINTR;
+		}
+		if (time_after(jiffies, orig_jiffies + HZ)) {
+			pr_debug("I2C: timeout\n");
+			if (readb(i2c->base + MPC_I2C_SR) ==
+			    (CSR_MCF | CSR_MBB | CSR_RXAK))
+				mpc_i2c_fixup(i2c);
+			return -EIO;
+		}
+		schedule();
+	}
+
+	for (i = 0; ret >= 0 && i < num; i++) {
+		pmsg = &msgs[i];
+		pr_debug("Doing %s %d bytes to 0x%02x - %d of %d messages\n",
+			 pmsg->flags & I2C_M_RD ? "read" : "write",
+			 pmsg->len, pmsg->addr, i + 1, num);
+		if (pmsg->flags & I2C_M_RD)
+			ret =
+			    mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
+		else
+			ret =
+			    mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
+	}
+	mpc_i2c_stop(i2c);
+	return (ret < 0) ? ret : num;
+}
+
+static u32 mpc_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm mpc_algo = {
+	.master_xfer = mpc_xfer,
+	.functionality = mpc_functionality,
+};
+
+static struct i2c_adapter mpc_ops = {
+	.owner = THIS_MODULE,
+	.name = "MPC adapter",
+	.id = I2C_HW_MPC107,
+	.algo = &mpc_algo,
+	.class = I2C_CLASS_HWMON,
+	.timeout = 1,
+	.retries = 1
+};
+
+static int fsl_i2c_probe(struct platform_device *pdev)
+{
+	int result = 0;
+	struct mpc_i2c *i2c;
+	struct fsl_i2c_platform_data *pdata;
+	struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	pdata = (struct fsl_i2c_platform_data *) pdev->dev.platform_data;
+
+	if (!(i2c = kzalloc(sizeof(*i2c), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	i2c->irq = platform_get_irq(pdev, 0);
+	if (i2c->irq < 0) {
+		result = -ENXIO;
+		goto fail_get_irq;
+	}
+	i2c->flags = pdata->device_flags;
+	init_waitqueue_head(&i2c->queue);
+
+	i2c->base = ioremap((phys_addr_t)r->start, MPC_I2C_REGION);
+
+	if (!i2c->base) {
+		printk(KERN_ERR "i2c-mpc - failed to map controller\n");
+		result = -ENOMEM;
+		goto fail_map;
+	}
+
+	if (i2c->irq != 0)
+		if ((result = request_irq(i2c->irq, mpc_i2c_isr,
+					  IRQF_SHARED, "i2c-mpc", i2c)) < 0) {
+			printk(KERN_ERR
+			       "i2c-mpc - failed to attach interrupt\n");
+			goto fail_irq;
+		}
+
+	mpc_i2c_setclock(i2c);
+	platform_set_drvdata(pdev, i2c);
+
+	i2c->adap = mpc_ops;
+	i2c->adap.nr = pdev->id;
+	i2c_set_adapdata(&i2c->adap, i2c);
+	i2c->adap.dev.parent = &pdev->dev;
+	if ((result = i2c_add_numbered_adapter(&i2c->adap)) < 0) {
+		printk(KERN_ERR "i2c-mpc - failed to add adapter\n");
+		goto fail_add;
+	}
+
+	return result;
+
+      fail_add:
+	if (i2c->irq != 0)
+		free_irq(i2c->irq, i2c);
+      fail_irq:
+	iounmap(i2c->base);
+      fail_map:
+      fail_get_irq:
+	kfree(i2c);
+	return result;
+};
+
+static int fsl_i2c_remove(struct platform_device *pdev)
+{
+	struct mpc_i2c *i2c = platform_get_drvdata(pdev);
+
+	i2c_del_adapter(&i2c->adap);
+	platform_set_drvdata(pdev, NULL);
+
+	if (i2c->irq != 0)
+		free_irq(i2c->irq, i2c);
+
+	iounmap(i2c->base);
+	kfree(i2c);
+	return 0;
+};
+
+/* Structure for a device driver */
+static struct platform_driver fsl_i2c_driver = {
+	.probe = fsl_i2c_probe,
+	.remove = fsl_i2c_remove,
+	.driver	= {
+		.owner = THIS_MODULE,
+		.name = "fsl-i2c",
+	},
+};
+
+static int __init fsl_i2c_init(void)
+{
+	return platform_driver_register(&fsl_i2c_driver);
+}
+
+static void __exit fsl_i2c_exit(void)
+{
+	platform_driver_unregister(&fsl_i2c_driver);
+}
+
+module_init(fsl_i2c_init);
+module_exit(fsl_i2c_exit);
+
+MODULE_AUTHOR("Adrian Cox <adrian@humboldt.co.uk>");
+MODULE_DESCRIPTION
+    ("I2C-Bus adapter for MPC107 bridge and MPC824x/85xx/52xx processors");
+MODULE_LICENSE("GPL");

-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply related

* Re: [PATCH] Addition to the i2c series, copy the ppc mpc-i2c driver before changing it on powerpc
From: Tony Breeds @ 2007-12-10 22:42 UTC (permalink / raw)
  To: Jon Smirl; +Cc: PowerPC dev list
In-Reply-To: <9e4733910712101420o15b5e908jc7291a0086832467@mail.gmail.com>

On Mon, Dec 10, 2007 at 05:20:08PM -0500, Jon Smirl wrote:
> Copy mpc-i2c to preserve support for ARCH=ppc and allow changes on ARCH=powerpc
> 
> Temporarily copy the mpc-i2c driver to continue support for the ppc
> architecture until it is removed in mid-2008. This file should be
> deleted as part of ppc's final removal.

<snip?
> 
> diff --git a/Makefile b/Makefile
> index 013b43a..3e714e2 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -35,7 +35,7 @@ MAKEFLAGS += -rR --no-print-directory
>  # To put more focus on warnings, be less verbose as default
>  # Use 'make V=1' to see the full commands
> 
> -ARCH=powerpc
> +ARCH=ppc
>  CROSS_COMPILE=powerpc-603e-linux-gnu-

Umm should this hunk be here?  My Makefile doen't have these 3 line at
all?

Yours Tony

  linux.conf.au        http://linux.conf.au/ || http://lca2008.linux.org.au/
  Jan 28 - Feb 02 2008 The Australian Linux Technical Conference!

^ permalink raw reply

* Re: [PATCH] Addition to the i2c series, copy the ppc mpc-i2c driver before changing it on powerpc
From: Grant Likely @ 2007-12-10 22:44 UTC (permalink / raw)
  To: Jon Smirl; +Cc: PowerPC dev list
In-Reply-To: <9e4733910712101420o15b5e908jc7291a0086832467@mail.gmail.com>

On 12/10/07, Jon Smirl <jonsmirl@gmail.com> wrote:
> Copy mpc-i2c to preserve support for ARCH=ppc and allow changes on ARCH=powerpc
>
> Temporarily copy the mpc-i2c driver to continue support for the ppc
> architecture until it is removed in mid-2008. This file should be
> deleted as part of ppc's final removal.
>
> Signed-off-by: Jon Smirl <jonsmirl@gmail.com>

For the record; I'm not fond of this approach.  Supporting both bus
bindings in the single driver is simple and results in less code churn
when arch/ppc is removed, and encourages separation between the driver
proper and the bus bindings which is just a good idea for all drivers
in general.

Just my $0.02

Cheers,
g.
> ---
>
>  Makefile                               |    2
>  arch/ppc/configs/TQM8540_defconfig     |    2
>  arch/ppc/configs/TQM8541_defconfig     |    2
>  arch/ppc/configs/TQM8555_defconfig     |    2
>  arch/ppc/configs/TQM8560_defconfig     |    2
>  arch/ppc/configs/mpc834x_sys_defconfig |    2
>  arch/ppc/configs/mpc8540_ads_defconfig |    2
>  arch/ppc/configs/mpc8548_cds_defconfig |    2
>  arch/ppc/configs/mpc8555_cds_defconfig |    2
>  arch/ppc/configs/mpc8560_ads_defconfig |    2
>  drivers/i2c/busses/Kconfig             |   16 +
>  drivers/i2c/busses/Makefile            |    1
>  drivers/i2c/busses/i2c-mpc-ppc.c       |  418 ++++++++++++++++++++++++++++++++
>  13 files changed, 444 insertions(+), 11 deletions(-)
>  create mode 100644 drivers/i2c/busses/i2c-mpc-ppc.c
>
>
> diff --git a/Makefile b/Makefile
> index 013b43a..3e714e2 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -35,7 +35,7 @@ MAKEFLAGS += -rR --no-print-directory
>  # To put more focus on warnings, be less verbose as default
>  # Use 'make V=1' to see the full commands
>
> -ARCH=powerpc
> +ARCH=ppc
>  CROSS_COMPILE=powerpc-603e-linux-gnu-
>
>  ifdef V
> diff --git a/arch/ppc/configs/TQM8540_defconfig
> b/arch/ppc/configs/TQM8540_defconfig
> index f33f0e7..7098ed0 100644
> --- a/arch/ppc/configs/TQM8540_defconfig
> +++ b/arch/ppc/configs/TQM8540_defconfig
> @@ -684,7 +684,7 @@ CONFIG_I2C_CHARDEV=y
>  # CONFIG_I2C_I801 is not set
>  # CONFIG_I2C_I810 is not set
>  # CONFIG_I2C_PIIX4 is not set
> -CONFIG_I2C_MPC=y
> +CONFIG_I2C_MPC_PPC=y
>  # CONFIG_I2C_NFORCE2 is not set
>  # CONFIG_I2C_PARPORT_LIGHT is not set
>  # CONFIG_I2C_PROSAVAGE is not set
> diff --git a/arch/ppc/configs/TQM8541_defconfig
> b/arch/ppc/configs/TQM8541_defconfig
> index e00cd62..2137d01 100644
> --- a/arch/ppc/configs/TQM8541_defconfig
> +++ b/arch/ppc/configs/TQM8541_defconfig
> @@ -687,7 +687,7 @@ CONFIG_I2C_CHARDEV=y
>  # CONFIG_I2C_I801 is not set
>  # CONFIG_I2C_I810 is not set
>  # CONFIG_I2C_PIIX4 is not set
> -CONFIG_I2C_MPC=y
> +CONFIG_I2C_MPC_PPC=y
>  # CONFIG_I2C_MPC8260 is not set
>  # CONFIG_I2C_NFORCE2 is not set
>  # CONFIG_I2C_PARPORT_LIGHT is not set
> diff --git a/arch/ppc/configs/TQM8555_defconfig
> b/arch/ppc/configs/TQM8555_defconfig
> index 43a0d9d..f2317b6 100644
> --- a/arch/ppc/configs/TQM8555_defconfig
> +++ b/arch/ppc/configs/TQM8555_defconfig
> @@ -687,7 +687,7 @@ CONFIG_I2C_CHARDEV=y
>  # CONFIG_I2C_I801 is not set
>  # CONFIG_I2C_I810 is not set
>  # CONFIG_I2C_PIIX4 is not set
> -CONFIG_I2C_MPC=y
> +CONFIG_I2C_MPC_PPC=y
>  # CONFIG_I2C_NFORCE2 is not set
>  # CONFIG_I2C_PARPORT_LIGHT is not set
>  # CONFIG_I2C_PROSAVAGE is not set
> diff --git a/arch/ppc/configs/TQM8560_defconfig
> b/arch/ppc/configs/TQM8560_defconfig
> index a814d17..6c19121 100644
> --- a/arch/ppc/configs/TQM8560_defconfig
> +++ b/arch/ppc/configs/TQM8560_defconfig
> @@ -694,7 +694,7 @@ CONFIG_I2C_CHARDEV=y
>  # CONFIG_I2C_I801 is not set
>  # CONFIG_I2C_I810 is not set
>  # CONFIG_I2C_PIIX4 is not set
> -CONFIG_I2C_MPC=y
> +CONFIG_I2C_MPC_PPC=y
>  # CONFIG_I2C_MPC8260 is not set
>  # CONFIG_I2C_NFORCE2 is not set
>  # CONFIG_I2C_PARPORT_LIGHT is not set
> diff --git a/arch/ppc/configs/mpc834x_sys_defconfig
> b/arch/ppc/configs/mpc834x_sys_defconfig
> index d90c8a7..cd568d2 100644
> --- a/arch/ppc/configs/mpc834x_sys_defconfig
> +++ b/arch/ppc/configs/mpc834x_sys_defconfig
> @@ -562,7 +562,7 @@ CONFIG_I2C_CHARDEV=y
>  # CONFIG_I2C_I801 is not set
>  # CONFIG_I2C_I810 is not set
>  # CONFIG_I2C_PIIX4 is not set
> -CONFIG_I2C_MPC=y
> +CONFIG_I2C_MPC_PPC=y
>  # CONFIG_I2C_NFORCE2 is not set
>  # CONFIG_I2C_PARPORT_LIGHT is not set
>  # CONFIG_I2C_PROSAVAGE is not set
> diff --git a/arch/ppc/configs/mpc8540_ads_defconfig
> b/arch/ppc/configs/mpc8540_ads_defconfig
> index bf676eb..5819835 100644
> --- a/arch/ppc/configs/mpc8540_ads_defconfig
> +++ b/arch/ppc/configs/mpc8540_ads_defconfig
> @@ -452,7 +452,7 @@ CONFIG_I2C_CHARDEV=y
>  # CONFIG_I2C_AMD8111 is not set
>  # CONFIG_I2C_I801 is not set
>  # CONFIG_I2C_I810 is not set
> -CONFIG_I2C_MPC=y
> +CONFIG_I2C_MPC_PPC=y
>  # CONFIG_I2C_NFORCE2 is not set
>  # CONFIG_I2C_PARPORT_LIGHT is not set
>  # CONFIG_I2C_PIIX4 is not set
> diff --git a/arch/ppc/configs/mpc8548_cds_defconfig
> b/arch/ppc/configs/mpc8548_cds_defconfig
> index f36fc5d..e5b5071 100644
> --- a/arch/ppc/configs/mpc8548_cds_defconfig
> +++ b/arch/ppc/configs/mpc8548_cds_defconfig
> @@ -413,7 +413,7 @@ CONFIG_I2C_CHARDEV=y
>  #
>  # I2C Hardware Bus support
>  #
> -CONFIG_I2C_MPC=y
> +CONFIG_I2C_MPC_PPC=y
>  # CONFIG_I2C_PARPORT_LIGHT is not set
>  # CONFIG_I2C_PCA_ISA is not set
>
> diff --git a/arch/ppc/configs/mpc8555_cds_defconfig
> b/arch/ppc/configs/mpc8555_cds_defconfig
> index 4f1e320..08dbab0 100644
> --- a/arch/ppc/configs/mpc8555_cds_defconfig
> +++ b/arch/ppc/configs/mpc8555_cds_defconfig
> @@ -518,7 +518,7 @@ CONFIG_I2C_CHARDEV=y
>  # CONFIG_I2C_I801 is not set
>  # CONFIG_I2C_I810 is not set
>  # CONFIG_I2C_PIIX4 is not set
> -CONFIG_I2C_MPC=y
> +CONFIG_I2C_MPC_PPC=y
>  # CONFIG_I2C_NFORCE2 is not set
>  # CONFIG_I2C_PARPORT_LIGHT is not set
>  # CONFIG_I2C_PROSAVAGE is not set
> diff --git a/arch/ppc/configs/mpc8560_ads_defconfig
> b/arch/ppc/configs/mpc8560_ads_defconfig
> index f12d48f..0e0080b 100644
> --- a/arch/ppc/configs/mpc8560_ads_defconfig
> +++ b/arch/ppc/configs/mpc8560_ads_defconfig
> @@ -489,7 +489,7 @@ CONFIG_I2C_CHARDEV=y
>  # CONFIG_I2C_I801 is not set
>  # CONFIG_I2C_I810 is not set
>  # CONFIG_I2C_PIIX4 is not set
> -CONFIG_I2C_MPC=y
> +CONFIG_I2C_MPC_PPC=y
>  # CONFIG_I2C_NFORCE2 is not set
>  # CONFIG_I2C_PARPORT_LIGHT is not set
>  # CONFIG_I2C_PROSAVAGE is not set
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index c466c6c..bdde307 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -301,7 +301,7 @@ config I2C_POWERMAC
>
>  config I2C_MPC
>         tristate "MPC107/824x/85xx/52xx/86xx"
> -       depends on PPC32
> +       depends on PPC32 && PPC_MERGE
>         help
>           If you say yes to this option, support will be included for the
>           built-in I2C interface on the MPC107/Tsi107/MPC8240/MPC8245 and
> @@ -311,6 +311,20 @@ config I2C_MPC
>           This driver can also be built as a module.  If so, the module
>           will be called i2c-mpc.
>
> +config I2C_MPC_PPC
> +       tristate "MPC107/824x/85xx/52xx/86xx"
> +       depends on PPC32 && !PPC_MERGE
> +       help
> +         If you say yes to this option, support will be included for the
> +         built-in I2C interface on the MPC107/Tsi107/MPC8240/MPC8245 and
> +         MPC85xx/MPC8641 family processors. The driver may also work on 52xx
> +         family processors, though interrupts are known not to work.
> +
> +         This version of the driver is scheduled for deletion with the PPC
> architecture.
> +
> +         This driver can also be built as a module.  If so, the module
> +         will be called i2c-mpc.
> +
>  config I2C_NFORCE2
>         tristate "Nvidia nForce2, nForce3 and nForce4"
>         depends on PCI
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 81d43c2..b7fe095 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -23,6 +23,7 @@ obj-$(CONFIG_I2C_IXP2000)     += i2c-ixp2000.o
>  obj-$(CONFIG_I2C_IXP4XX)       += i2c-ixp4xx.o
>  obj-$(CONFIG_I2C_POWERMAC)     += i2c-powermac.o
>  obj-$(CONFIG_I2C_MPC)          += i2c-mpc.o
> +obj-$(CONFIG_I2C_MPC_PPC)      += i2c-mpc-ppc.o
>  obj-$(CONFIG_I2C_MV64XXX)      += i2c-mv64xxx.o
>  obj-$(CONFIG_I2C_NFORCE2)      += i2c-nforce2.o
>  obj-$(CONFIG_I2C_OCORES)       += i2c-ocores.o
> diff --git a/drivers/i2c/busses/i2c-mpc-ppc.c b/drivers/i2c/busses/i2c-mpc-ppc.c
> new file mode 100644
> index 0000000..d8de4ac
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-mpc-ppc.c
> @@ -0,0 +1,418 @@
> +/*
> + * (C) Copyright 2003-2004
> + * Humboldt Solutions Ltd, adrian@humboldt.co.uk.
> +
> + * This is a combined i2c adapter and algorithm driver for the
> + * MPC107/Tsi107 PowerPC northbridge and processors that include
> + * the same I2C unit (8240, 8245, 85xx).
> + *
> + * Release 0.8
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/sched.h>
> +#include <linux/init.h>
> +#include <linux/platform_device.h>
> +
> +#include <asm/io.h>
> +#include <linux/fsl_devices.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +
> +#define MPC_I2C_ADDR  0x00
> +#define MPC_I2C_FDR    0x04
> +#define MPC_I2C_CR     0x08
> +#define MPC_I2C_SR     0x0c
> +#define MPC_I2C_DR     0x10
> +#define MPC_I2C_DFSRR 0x14
> +#define MPC_I2C_REGION 0x20
> +
> +#define CCR_MEN  0x80
> +#define CCR_MIEN 0x40
> +#define CCR_MSTA 0x20
> +#define CCR_MTX  0x10
> +#define CCR_TXAK 0x08
> +#define CCR_RSTA 0x04
> +
> +#define CSR_MCF  0x80
> +#define CSR_MAAS 0x40
> +#define CSR_MBB  0x20
> +#define CSR_MAL  0x10
> +#define CSR_SRW  0x04
> +#define CSR_MIF  0x02
> +#define CSR_RXAK 0x01
> +
> +struct mpc_i2c {
> +       void __iomem *base;
> +       u32 interrupt;
> +       wait_queue_head_t queue;
> +       struct i2c_adapter adap;
> +       int irq;
> +       u32 flags;
> +};
> +
> +static __inline__ void writeccr(struct mpc_i2c *i2c, u32 x)
> +{
> +       writeb(x, i2c->base + MPC_I2C_CR);
> +}
> +
> +static irqreturn_t mpc_i2c_isr(int irq, void *dev_id)
> +{
> +       struct mpc_i2c *i2c = dev_id;
> +       if (readb(i2c->base + MPC_I2C_SR) & CSR_MIF) {
> +               /* Read again to allow register to stabilise */
> +               i2c->interrupt = readb(i2c->base + MPC_I2C_SR);
> +               writeb(0, i2c->base + MPC_I2C_SR);
> +               wake_up_interruptible(&i2c->queue);
> +       }
> +       return IRQ_HANDLED;
> +}
> +
> +/* Sometimes 9th clock pulse isn't generated, and slave doesn't release
> + * the bus, because it wants to send ACK.
> + * Following sequence of enabling/disabling and sending start/stop generates
> + * the pulse, so it's all OK.
> + */
> +static void mpc_i2c_fixup(struct mpc_i2c *i2c)
> +{
> +       writeccr(i2c, 0);
> +       udelay(30);
> +       writeccr(i2c, CCR_MEN);
> +       udelay(30);
> +       writeccr(i2c, CCR_MSTA | CCR_MTX);
> +       udelay(30);
> +       writeccr(i2c, CCR_MSTA | CCR_MTX | CCR_MEN);
> +       udelay(30);
> +       writeccr(i2c, CCR_MEN);
> +       udelay(30);
> +}
> +
> +static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
> +{
> +       unsigned long orig_jiffies = jiffies;
> +       u32 x;
> +       int result = 0;
> +
> +       if (i2c->irq == 0)
> +       {
> +               while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) {
> +                       schedule();
> +                       if (time_after(jiffies, orig_jiffies + timeout)) {
> +                               pr_debug("I2C: timeout\n");
> +                               writeccr(i2c, 0);
> +                               result = -EIO;
> +                               break;
> +                       }
> +               }
> +               x = readb(i2c->base + MPC_I2C_SR);
> +               writeb(0, i2c->base + MPC_I2C_SR);
> +       } else {
> +               /* Interrupt mode */
> +               result = wait_event_interruptible_timeout(i2c->queue,
> +                       (i2c->interrupt & CSR_MIF), timeout * HZ);
> +
> +               if (unlikely(result < 0)) {
> +                       pr_debug("I2C: wait interrupted\n");
> +                       writeccr(i2c, 0);
> +               } else if (unlikely(!(i2c->interrupt & CSR_MIF))) {
> +                       pr_debug("I2C: wait timeout\n");
> +                       writeccr(i2c, 0);
> +                       result = -ETIMEDOUT;
> +               }
> +
> +               x = i2c->interrupt;
> +               i2c->interrupt = 0;
> +       }
> +
> +       if (result < 0)
> +               return result;
> +
> +       if (!(x & CSR_MCF)) {
> +               pr_debug("I2C: unfinished\n");
> +               return -EIO;
> +       }
> +
> +       if (x & CSR_MAL) {
> +               pr_debug("I2C: MAL\n");
> +               return -EIO;
> +       }
> +
> +       if (writing && (x & CSR_RXAK)) {
> +               pr_debug("I2C: No RXAK\n");
> +               /* generate stop */
> +               writeccr(i2c, CCR_MEN);
> +               return -EIO;
> +       }
> +       return 0;
> +}
> +
> +static void mpc_i2c_setclock(struct mpc_i2c *i2c)
> +{
> +       /* Set clock and filters */
> +       if (i2c->flags & FSL_I2C_DEV_SEPARATE_DFSRR) {
> +               writeb(0x31, i2c->base + MPC_I2C_FDR);
> +               writeb(0x10, i2c->base + MPC_I2C_DFSRR);
> +       } else if (i2c->flags & FSL_I2C_DEV_CLOCK_5200)
> +               writeb(0x3f, i2c->base + MPC_I2C_FDR);
> +       else
> +               writel(0x1031, i2c->base + MPC_I2C_FDR);
> +}
> +
> +static void mpc_i2c_start(struct mpc_i2c *i2c)
> +{
> +       /* Clear arbitration */
> +       writeb(0, i2c->base + MPC_I2C_SR);
> +       /* Start with MEN */
> +       writeccr(i2c, CCR_MEN);
> +}
> +
> +static void mpc_i2c_stop(struct mpc_i2c *i2c)
> +{
> +       writeccr(i2c, CCR_MEN);
> +}
> +
> +static int mpc_write(struct mpc_i2c *i2c, int target,
> +                    const u8 * data, int length, int restart)
> +{
> +       int i;
> +       unsigned timeout = i2c->adap.timeout;
> +       u32 flags = restart ? CCR_RSTA : 0;
> +
> +       /* Start with MEN */
> +       if (!restart)
> +               writeccr(i2c, CCR_MEN);
> +       /* Start as master */
> +       writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
> +       /* Write target byte */
> +       writeb((target << 1), i2c->base + MPC_I2C_DR);
> +
> +       if (i2c_wait(i2c, timeout, 1) < 0)
> +               return -1;
> +
> +       for (i = 0; i < length; i++) {
> +               /* Write data byte */
> +               writeb(data[i], i2c->base + MPC_I2C_DR);
> +
> +               if (i2c_wait(i2c, timeout, 1) < 0)
> +                       return -1;
> +       }
> +
> +       return 0;
> +}
> +
> +static int mpc_read(struct mpc_i2c *i2c, int target,
> +                   u8 * data, int length, int restart)
> +{
> +       unsigned timeout = i2c->adap.timeout;
> +       int i;
> +       u32 flags = restart ? CCR_RSTA : 0;
> +
> +       /* Start with MEN */
> +       if (!restart)
> +               writeccr(i2c, CCR_MEN);
> +       /* Switch to read - restart */
> +       writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
> +       /* Write target address byte - this time with the read flag set */
> +       writeb((target << 1) | 1, i2c->base + MPC_I2C_DR);
> +
> +       if (i2c_wait(i2c, timeout, 1) < 0)
> +               return -1;
> +
> +       if (length) {
> +               if (length == 1)
> +                       writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
> +               else
> +                       writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA);
> +               /* Dummy read */
> +               readb(i2c->base + MPC_I2C_DR);
> +       }
> +
> +       for (i = 0; i < length; i++) {
> +               if (i2c_wait(i2c, timeout, 0) < 0)
> +                       return -1;
> +
> +               /* Generate txack on next to last byte */
> +               if (i == length - 2)
> +                       writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
> +               /* Generate stop on last byte */
> +               if (i == length - 1)
> +                       writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_TXAK);
> +               data[i] = readb(i2c->base + MPC_I2C_DR);
> +       }
> +
> +       return length;
> +}
> +
> +static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
> +{
> +       struct i2c_msg *pmsg;
> +       int i;
> +       int ret = 0;
> +       unsigned long orig_jiffies = jiffies;
> +       struct mpc_i2c *i2c = i2c_get_adapdata(adap);
> +
> +       mpc_i2c_start(i2c);
> +
> +       /* Allow bus up to 1s to become not busy */
> +       while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) {
> +               if (signal_pending(current)) {
> +                       pr_debug("I2C: Interrupted\n");
> +                       writeccr(i2c, 0);
> +                       return -EINTR;
> +               }
> +               if (time_after(jiffies, orig_jiffies + HZ)) {
> +                       pr_debug("I2C: timeout\n");
> +                       if (readb(i2c->base + MPC_I2C_SR) ==
> +                           (CSR_MCF | CSR_MBB | CSR_RXAK))
> +                               mpc_i2c_fixup(i2c);
> +                       return -EIO;
> +               }
> +               schedule();
> +       }
> +
> +       for (i = 0; ret >= 0 && i < num; i++) {
> +               pmsg = &msgs[i];
> +               pr_debug("Doing %s %d bytes to 0x%02x - %d of %d messages\n",
> +                        pmsg->flags & I2C_M_RD ? "read" : "write",
> +                        pmsg->len, pmsg->addr, i + 1, num);
> +               if (pmsg->flags & I2C_M_RD)
> +                       ret =
> +                           mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
> +               else
> +                       ret =
> +                           mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
> +       }
> +       mpc_i2c_stop(i2c);
> +       return (ret < 0) ? ret : num;
> +}
> +
> +static u32 mpc_functionality(struct i2c_adapter *adap)
> +{
> +       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm mpc_algo = {
> +       .master_xfer = mpc_xfer,
> +       .functionality = mpc_functionality,
> +};
> +
> +static struct i2c_adapter mpc_ops = {
> +       .owner = THIS_MODULE,
> +       .name = "MPC adapter",
> +       .id = I2C_HW_MPC107,
> +       .algo = &mpc_algo,
> +       .class = I2C_CLASS_HWMON,
> +       .timeout = 1,
> +       .retries = 1
> +};
> +
> +static int fsl_i2c_probe(struct platform_device *pdev)
> +{
> +       int result = 0;
> +       struct mpc_i2c *i2c;
> +       struct fsl_i2c_platform_data *pdata;
> +       struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> +       pdata = (struct fsl_i2c_platform_data *) pdev->dev.platform_data;
> +
> +       if (!(i2c = kzalloc(sizeof(*i2c), GFP_KERNEL))) {
> +               return -ENOMEM;
> +       }
> +
> +       i2c->irq = platform_get_irq(pdev, 0);
> +       if (i2c->irq < 0) {
> +               result = -ENXIO;
> +               goto fail_get_irq;
> +       }
> +       i2c->flags = pdata->device_flags;
> +       init_waitqueue_head(&i2c->queue);
> +
> +       i2c->base = ioremap((phys_addr_t)r->start, MPC_I2C_REGION);
> +
> +       if (!i2c->base) {
> +               printk(KERN_ERR "i2c-mpc - failed to map controller\n");
> +               result = -ENOMEM;
> +               goto fail_map;
> +       }
> +
> +       if (i2c->irq != 0)
> +               if ((result = request_irq(i2c->irq, mpc_i2c_isr,
> +                                         IRQF_SHARED, "i2c-mpc", i2c)) < 0) {
> +                       printk(KERN_ERR
> +                              "i2c-mpc - failed to attach interrupt\n");
> +                       goto fail_irq;
> +               }
> +
> +       mpc_i2c_setclock(i2c);
> +       platform_set_drvdata(pdev, i2c);
> +
> +       i2c->adap = mpc_ops;
> +       i2c->adap.nr = pdev->id;
> +       i2c_set_adapdata(&i2c->adap, i2c);
> +       i2c->adap.dev.parent = &pdev->dev;
> +       if ((result = i2c_add_numbered_adapter(&i2c->adap)) < 0) {
> +               printk(KERN_ERR "i2c-mpc - failed to add adapter\n");
> +               goto fail_add;
> +       }
> +
> +       return result;
> +
> +      fail_add:
> +       if (i2c->irq != 0)
> +               free_irq(i2c->irq, i2c);
> +      fail_irq:
> +       iounmap(i2c->base);
> +      fail_map:
> +      fail_get_irq:
> +       kfree(i2c);
> +       return result;
> +};
> +
> +static int fsl_i2c_remove(struct platform_device *pdev)
> +{
> +       struct mpc_i2c *i2c = platform_get_drvdata(pdev);
> +
> +       i2c_del_adapter(&i2c->adap);
> +       platform_set_drvdata(pdev, NULL);
> +
> +       if (i2c->irq != 0)
> +               free_irq(i2c->irq, i2c);
> +
> +       iounmap(i2c->base);
> +       kfree(i2c);
> +       return 0;
> +};
> +
> +/* Structure for a device driver */
> +static struct platform_driver fsl_i2c_driver = {
> +       .probe = fsl_i2c_probe,
> +       .remove = fsl_i2c_remove,
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "fsl-i2c",
> +       },
> +};
> +
> +static int __init fsl_i2c_init(void)
> +{
> +       return platform_driver_register(&fsl_i2c_driver);
> +}
> +
> +static void __exit fsl_i2c_exit(void)
> +{
> +       platform_driver_unregister(&fsl_i2c_driver);
> +}
> +
> +module_init(fsl_i2c_init);
> +module_exit(fsl_i2c_exit);
> +
> +MODULE_AUTHOR("Adrian Cox <adrian@humboldt.co.uk>");
> +MODULE_DESCRIPTION
> +    ("I2C-Bus adapter for MPC107 bridge and MPC824x/85xx/52xx processors");
> +MODULE_LICENSE("GPL");
>
> --
> Jon Smirl
> jonsmirl@gmail.com
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev
>


-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
grant.likely@secretlab.ca
(403) 399-0195

^ permalink raw reply

* Re: [PATCH] Addition to the i2c series, copy the ppc mpc-i2c driver before changing it on powerpc
From: Jon Smirl @ 2007-12-10 22:48 UTC (permalink / raw)
  To: Tony Breeds; +Cc: PowerPC dev list
In-Reply-To: <20071210224224.GF24243@bakeyournoodle.com>

Same patch minus the Makefile change. It was part of testing the builds.

Copy mpc-i2c to preserve support for ARCH=ppc and allow changes on ARCH=powerpc

Temporarily copy the mpc-i2c driver to continue support for the ppc
architecture until it is removed in mid-2008. This file should be
deleted as part of ppc's final removal.

Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
---

 arch/ppc/configs/TQM8540_defconfig     |    2
 arch/ppc/configs/TQM8541_defconfig     |    2
 arch/ppc/configs/TQM8555_defconfig     |    2
 arch/ppc/configs/TQM8560_defconfig     |    2
 arch/ppc/configs/mpc834x_sys_defconfig |    2
 arch/ppc/configs/mpc8540_ads_defconfig |    2
 arch/ppc/configs/mpc8548_cds_defconfig |    2
 arch/ppc/configs/mpc8555_cds_defconfig |    2
 arch/ppc/configs/mpc8560_ads_defconfig |    2
 drivers/i2c/busses/Kconfig             |   16 +
 drivers/i2c/busses/Makefile            |    1
 drivers/i2c/busses/i2c-mpc-ppc.c       |  418 ++++++++++++++++++++++++++++++++
 12 files changed, 443 insertions(+), 10 deletions(-)
 create mode 100644 drivers/i2c/busses/i2c-mpc-ppc.c


diff --git a/arch/ppc/configs/TQM8540_defconfig
b/arch/ppc/configs/TQM8540_defconfig
index f33f0e7..7098ed0 100644
--- a/arch/ppc/configs/TQM8540_defconfig
+++ b/arch/ppc/configs/TQM8540_defconfig
@@ -684,7 +684,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
 # CONFIG_I2C_PIIX4 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_PROSAVAGE is not set
diff --git a/arch/ppc/configs/TQM8541_defconfig
b/arch/ppc/configs/TQM8541_defconfig
index e00cd62..2137d01 100644
--- a/arch/ppc/configs/TQM8541_defconfig
+++ b/arch/ppc/configs/TQM8541_defconfig
@@ -687,7 +687,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
 # CONFIG_I2C_PIIX4 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_MPC8260 is not set
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
diff --git a/arch/ppc/configs/TQM8555_defconfig
b/arch/ppc/configs/TQM8555_defconfig
index 43a0d9d..f2317b6 100644
--- a/arch/ppc/configs/TQM8555_defconfig
+++ b/arch/ppc/configs/TQM8555_defconfig
@@ -687,7 +687,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
 # CONFIG_I2C_PIIX4 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_PROSAVAGE is not set
diff --git a/arch/ppc/configs/TQM8560_defconfig
b/arch/ppc/configs/TQM8560_defconfig
index a814d17..6c19121 100644
--- a/arch/ppc/configs/TQM8560_defconfig
+++ b/arch/ppc/configs/TQM8560_defconfig
@@ -694,7 +694,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
 # CONFIG_I2C_PIIX4 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_MPC8260 is not set
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
diff --git a/arch/ppc/configs/mpc834x_sys_defconfig
b/arch/ppc/configs/mpc834x_sys_defconfig
index d90c8a7..cd568d2 100644
--- a/arch/ppc/configs/mpc834x_sys_defconfig
+++ b/arch/ppc/configs/mpc834x_sys_defconfig
@@ -562,7 +562,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
 # CONFIG_I2C_PIIX4 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_PROSAVAGE is not set
diff --git a/arch/ppc/configs/mpc8540_ads_defconfig
b/arch/ppc/configs/mpc8540_ads_defconfig
index bf676eb..5819835 100644
--- a/arch/ppc/configs/mpc8540_ads_defconfig
+++ b/arch/ppc/configs/mpc8540_ads_defconfig
@@ -452,7 +452,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_AMD8111 is not set
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_PIIX4 is not set
diff --git a/arch/ppc/configs/mpc8548_cds_defconfig
b/arch/ppc/configs/mpc8548_cds_defconfig
index f36fc5d..e5b5071 100644
--- a/arch/ppc/configs/mpc8548_cds_defconfig
+++ b/arch/ppc/configs/mpc8548_cds_defconfig
@@ -413,7 +413,7 @@ CONFIG_I2C_CHARDEV=y
 #
 # I2C Hardware Bus support
 #
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_PCA_ISA is not set

diff --git a/arch/ppc/configs/mpc8555_cds_defconfig
b/arch/ppc/configs/mpc8555_cds_defconfig
index 4f1e320..08dbab0 100644
--- a/arch/ppc/configs/mpc8555_cds_defconfig
+++ b/arch/ppc/configs/mpc8555_cds_defconfig
@@ -518,7 +518,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
 # CONFIG_I2C_PIIX4 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_PROSAVAGE is not set
diff --git a/arch/ppc/configs/mpc8560_ads_defconfig
b/arch/ppc/configs/mpc8560_ads_defconfig
index f12d48f..0e0080b 100644
--- a/arch/ppc/configs/mpc8560_ads_defconfig
+++ b/arch/ppc/configs/mpc8560_ads_defconfig
@@ -489,7 +489,7 @@ CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_I801 is not set
 # CONFIG_I2C_I810 is not set
 # CONFIG_I2C_PIIX4 is not set
-CONFIG_I2C_MPC=y
+CONFIG_I2C_MPC_PPC=y
 # CONFIG_I2C_NFORCE2 is not set
 # CONFIG_I2C_PARPORT_LIGHT is not set
 # CONFIG_I2C_PROSAVAGE is not set
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index c466c6c..bdde307 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -301,7 +301,7 @@ config I2C_POWERMAC

 config I2C_MPC
 	tristate "MPC107/824x/85xx/52xx/86xx"
-	depends on PPC32
+	depends on PPC32 && PPC_MERGE
 	help
 	  If you say yes to this option, support will be included for the
 	  built-in I2C interface on the MPC107/Tsi107/MPC8240/MPC8245 and
@@ -311,6 +311,20 @@ config I2C_MPC
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-mpc.

+config I2C_MPC_PPC
+	tristate "MPC107/824x/85xx/52xx/86xx"
+	depends on PPC32 && !PPC_MERGE
+	help
+	  If you say yes to this option, support will be included for the
+	  built-in I2C interface on the MPC107/Tsi107/MPC8240/MPC8245 and
+	  MPC85xx/MPC8641 family processors. The driver may also work on 52xx
+	  family processors, though interrupts are known not to work.
+	
+	  This version of the driver is scheduled for deletion with the PPC
architecture.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-mpc.
+
 config I2C_NFORCE2
 	tristate "Nvidia nForce2, nForce3 and nForce4"
 	depends on PCI
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 81d43c2..b7fe095 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_I2C_IXP2000)	+= i2c-ixp2000.o
 obj-$(CONFIG_I2C_IXP4XX)	+= i2c-ixp4xx.o
 obj-$(CONFIG_I2C_POWERMAC)	+= i2c-powermac.o
 obj-$(CONFIG_I2C_MPC)		+= i2c-mpc.o
+obj-$(CONFIG_I2C_MPC_PPC)	+= i2c-mpc-ppc.o
 obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
 obj-$(CONFIG_I2C_NFORCE2)	+= i2c-nforce2.o
 obj-$(CONFIG_I2C_OCORES)	+= i2c-ocores.o
diff --git a/drivers/i2c/busses/i2c-mpc-ppc.c b/drivers/i2c/busses/i2c-mpc-ppc.c
new file mode 100644
index 0000000..d8de4ac
--- /dev/null
+++ b/drivers/i2c/busses/i2c-mpc-ppc.c
@@ -0,0 +1,418 @@
+/*
+ * (C) Copyright 2003-2004
+ * Humboldt Solutions Ltd, adrian@humboldt.co.uk.
+
+ * This is a combined i2c adapter and algorithm driver for the
+ * MPC107/Tsi107 PowerPC northbridge and processors that include
+ * the same I2C unit (8240, 8245, 85xx).
+ *
+ * Release 0.8
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <linux/fsl_devices.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#define MPC_I2C_ADDR  0x00
+#define MPC_I2C_FDR 	0x04
+#define MPC_I2C_CR	0x08
+#define MPC_I2C_SR	0x0c
+#define MPC_I2C_DR	0x10
+#define MPC_I2C_DFSRR 0x14
+#define MPC_I2C_REGION 0x20
+
+#define CCR_MEN  0x80
+#define CCR_MIEN 0x40
+#define CCR_MSTA 0x20
+#define CCR_MTX  0x10
+#define CCR_TXAK 0x08
+#define CCR_RSTA 0x04
+
+#define CSR_MCF  0x80
+#define CSR_MAAS 0x40
+#define CSR_MBB  0x20
+#define CSR_MAL  0x10
+#define CSR_SRW  0x04
+#define CSR_MIF  0x02
+#define CSR_RXAK 0x01
+
+struct mpc_i2c {
+	void __iomem *base;
+	u32 interrupt;
+	wait_queue_head_t queue;
+	struct i2c_adapter adap;
+	int irq;
+	u32 flags;
+};
+
+static __inline__ void writeccr(struct mpc_i2c *i2c, u32 x)
+{
+	writeb(x, i2c->base + MPC_I2C_CR);
+}
+
+static irqreturn_t mpc_i2c_isr(int irq, void *dev_id)
+{
+	struct mpc_i2c *i2c = dev_id;
+	if (readb(i2c->base + MPC_I2C_SR) & CSR_MIF) {
+		/* Read again to allow register to stabilise */
+		i2c->interrupt = readb(i2c->base + MPC_I2C_SR);
+		writeb(0, i2c->base + MPC_I2C_SR);
+		wake_up_interruptible(&i2c->queue);
+	}
+	return IRQ_HANDLED;
+}
+
+/* Sometimes 9th clock pulse isn't generated, and slave doesn't release
+ * the bus, because it wants to send ACK.
+ * Following sequence of enabling/disabling and sending start/stop generates
+ * the pulse, so it's all OK.
+ */
+static void mpc_i2c_fixup(struct mpc_i2c *i2c)
+{
+	writeccr(i2c, 0);
+	udelay(30);
+	writeccr(i2c, CCR_MEN);
+	udelay(30);
+	writeccr(i2c, CCR_MSTA | CCR_MTX);
+	udelay(30);
+	writeccr(i2c, CCR_MSTA | CCR_MTX | CCR_MEN);
+	udelay(30);
+	writeccr(i2c, CCR_MEN);
+	udelay(30);
+}
+
+static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
+{
+	unsigned long orig_jiffies = jiffies;
+	u32 x;
+	int result = 0;
+
+	if (i2c->irq == 0)
+	{
+		while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) {
+			schedule();
+			if (time_after(jiffies, orig_jiffies + timeout)) {
+				pr_debug("I2C: timeout\n");
+				writeccr(i2c, 0);
+				result = -EIO;
+				break;
+			}
+		}
+		x = readb(i2c->base + MPC_I2C_SR);
+		writeb(0, i2c->base + MPC_I2C_SR);
+	} else {
+		/* Interrupt mode */
+		result = wait_event_interruptible_timeout(i2c->queue,
+			(i2c->interrupt & CSR_MIF), timeout * HZ);
+
+		if (unlikely(result < 0)) {
+			pr_debug("I2C: wait interrupted\n");
+			writeccr(i2c, 0);
+		} else if (unlikely(!(i2c->interrupt & CSR_MIF))) {
+			pr_debug("I2C: wait timeout\n");
+			writeccr(i2c, 0);
+			result = -ETIMEDOUT;
+		}
+
+		x = i2c->interrupt;
+		i2c->interrupt = 0;
+	}
+
+	if (result < 0)
+		return result;
+
+	if (!(x & CSR_MCF)) {
+		pr_debug("I2C: unfinished\n");
+		return -EIO;
+	}
+
+	if (x & CSR_MAL) {
+		pr_debug("I2C: MAL\n");
+		return -EIO;
+	}
+
+	if (writing && (x & CSR_RXAK)) {
+		pr_debug("I2C: No RXAK\n");
+		/* generate stop */
+		writeccr(i2c, CCR_MEN);
+		return -EIO;
+	}
+	return 0;
+}
+
+static void mpc_i2c_setclock(struct mpc_i2c *i2c)
+{
+	/* Set clock and filters */
+	if (i2c->flags & FSL_I2C_DEV_SEPARATE_DFSRR) {
+		writeb(0x31, i2c->base + MPC_I2C_FDR);
+		writeb(0x10, i2c->base + MPC_I2C_DFSRR);
+	} else if (i2c->flags & FSL_I2C_DEV_CLOCK_5200)
+		writeb(0x3f, i2c->base + MPC_I2C_FDR);
+	else
+		writel(0x1031, i2c->base + MPC_I2C_FDR);
+}
+
+static void mpc_i2c_start(struct mpc_i2c *i2c)
+{
+	/* Clear arbitration */
+	writeb(0, i2c->base + MPC_I2C_SR);
+	/* Start with MEN */
+	writeccr(i2c, CCR_MEN);
+}
+
+static void mpc_i2c_stop(struct mpc_i2c *i2c)
+{
+	writeccr(i2c, CCR_MEN);
+}
+
+static int mpc_write(struct mpc_i2c *i2c, int target,
+		     const u8 * data, int length, int restart)
+{
+	int i;
+	unsigned timeout = i2c->adap.timeout;
+	u32 flags = restart ? CCR_RSTA : 0;
+
+	/* Start with MEN */
+	if (!restart)
+		writeccr(i2c, CCR_MEN);
+	/* Start as master */
+	writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
+	/* Write target byte */
+	writeb((target << 1), i2c->base + MPC_I2C_DR);
+
+	if (i2c_wait(i2c, timeout, 1) < 0)
+		return -1;
+
+	for (i = 0; i < length; i++) {
+		/* Write data byte */
+		writeb(data[i], i2c->base + MPC_I2C_DR);
+
+		if (i2c_wait(i2c, timeout, 1) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+static int mpc_read(struct mpc_i2c *i2c, int target,
+		    u8 * data, int length, int restart)
+{
+	unsigned timeout = i2c->adap.timeout;
+	int i;
+	u32 flags = restart ? CCR_RSTA : 0;
+
+	/* Start with MEN */
+	if (!restart)
+		writeccr(i2c, CCR_MEN);
+	/* Switch to read - restart */
+	writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
+	/* Write target address byte - this time with the read flag set */
+	writeb((target << 1) | 1, i2c->base + MPC_I2C_DR);
+
+	if (i2c_wait(i2c, timeout, 1) < 0)
+		return -1;
+
+	if (length) {
+		if (length == 1)
+			writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
+		else
+			writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA);
+		/* Dummy read */
+		readb(i2c->base + MPC_I2C_DR);
+	}
+
+	for (i = 0; i < length; i++) {
+		if (i2c_wait(i2c, timeout, 0) < 0)
+			return -1;
+
+		/* Generate txack on next to last byte */
+		if (i == length - 2)
+			writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
+		/* Generate stop on last byte */
+		if (i == length - 1)
+			writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_TXAK);
+		data[i] = readb(i2c->base + MPC_I2C_DR);
+	}
+
+	return length;
+}
+
+static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+	struct i2c_msg *pmsg;
+	int i;
+	int ret = 0;
+	unsigned long orig_jiffies = jiffies;
+	struct mpc_i2c *i2c = i2c_get_adapdata(adap);
+
+	mpc_i2c_start(i2c);
+
+	/* Allow bus up to 1s to become not busy */
+	while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) {
+		if (signal_pending(current)) {
+			pr_debug("I2C: Interrupted\n");
+			writeccr(i2c, 0);
+			return -EINTR;
+		}
+		if (time_after(jiffies, orig_jiffies + HZ)) {
+			pr_debug("I2C: timeout\n");
+			if (readb(i2c->base + MPC_I2C_SR) ==
+			    (CSR_MCF | CSR_MBB | CSR_RXAK))
+				mpc_i2c_fixup(i2c);
+			return -EIO;
+		}
+		schedule();
+	}
+
+	for (i = 0; ret >= 0 && i < num; i++) {
+		pmsg = &msgs[i];
+		pr_debug("Doing %s %d bytes to 0x%02x - %d of %d messages\n",
+			 pmsg->flags & I2C_M_RD ? "read" : "write",
+			 pmsg->len, pmsg->addr, i + 1, num);
+		if (pmsg->flags & I2C_M_RD)
+			ret =
+			    mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
+		else
+			ret =
+			    mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
+	}
+	mpc_i2c_stop(i2c);
+	return (ret < 0) ? ret : num;
+}
+
+static u32 mpc_functionality(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm mpc_algo = {
+	.master_xfer = mpc_xfer,
+	.functionality = mpc_functionality,
+};
+
+static struct i2c_adapter mpc_ops = {
+	.owner = THIS_MODULE,
+	.name = "MPC adapter",
+	.id = I2C_HW_MPC107,
+	.algo = &mpc_algo,
+	.class = I2C_CLASS_HWMON,
+	.timeout = 1,
+	.retries = 1
+};
+
+static int fsl_i2c_probe(struct platform_device *pdev)
+{
+	int result = 0;
+	struct mpc_i2c *i2c;
+	struct fsl_i2c_platform_data *pdata;
+	struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	pdata = (struct fsl_i2c_platform_data *) pdev->dev.platform_data;
+
+	if (!(i2c = kzalloc(sizeof(*i2c), GFP_KERNEL))) {
+		return -ENOMEM;
+	}
+
+	i2c->irq = platform_get_irq(pdev, 0);
+	if (i2c->irq < 0) {
+		result = -ENXIO;
+		goto fail_get_irq;
+	}
+	i2c->flags = pdata->device_flags;
+	init_waitqueue_head(&i2c->queue);
+
+	i2c->base = ioremap((phys_addr_t)r->start, MPC_I2C_REGION);
+
+	if (!i2c->base) {
+		printk(KERN_ERR "i2c-mpc - failed to map controller\n");
+		result = -ENOMEM;
+		goto fail_map;
+	}
+
+	if (i2c->irq != 0)
+		if ((result = request_irq(i2c->irq, mpc_i2c_isr,
+					  IRQF_SHARED, "i2c-mpc", i2c)) < 0) {
+			printk(KERN_ERR
+			       "i2c-mpc - failed to attach interrupt\n");
+			goto fail_irq;
+		}
+
+	mpc_i2c_setclock(i2c);
+	platform_set_drvdata(pdev, i2c);
+
+	i2c->adap = mpc_ops;
+	i2c->adap.nr = pdev->id;
+	i2c_set_adapdata(&i2c->adap, i2c);
+	i2c->adap.dev.parent = &pdev->dev;
+	if ((result = i2c_add_numbered_adapter(&i2c->adap)) < 0) {
+		printk(KERN_ERR "i2c-mpc - failed to add adapter\n");
+		goto fail_add;
+	}
+
+	return result;
+
+      fail_add:
+	if (i2c->irq != 0)
+		free_irq(i2c->irq, i2c);
+      fail_irq:
+	iounmap(i2c->base);
+      fail_map:
+      fail_get_irq:
+	kfree(i2c);
+	return result;
+};
+
+static int fsl_i2c_remove(struct platform_device *pdev)
+{
+	struct mpc_i2c *i2c = platform_get_drvdata(pdev);
+
+	i2c_del_adapter(&i2c->adap);
+	platform_set_drvdata(pdev, NULL);
+
+	if (i2c->irq != 0)
+		free_irq(i2c->irq, i2c);
+
+	iounmap(i2c->base);
+	kfree(i2c);
+	return 0;
+};
+
+/* Structure for a device driver */
+static struct platform_driver fsl_i2c_driver = {
+	.probe = fsl_i2c_probe,
+	.remove = fsl_i2c_remove,
+	.driver	= {
+		.owner = THIS_MODULE,
+		.name = "fsl-i2c",
+	},
+};
+
+static int __init fsl_i2c_init(void)
+{
+	return platform_driver_register(&fsl_i2c_driver);
+}
+
+static void __exit fsl_i2c_exit(void)
+{
+	platform_driver_unregister(&fsl_i2c_driver);
+}
+
+module_init(fsl_i2c_init);
+module_exit(fsl_i2c_exit);
+
+MODULE_AUTHOR("Adrian Cox <adrian@humboldt.co.uk>");
+MODULE_DESCRIPTION
+    ("I2C-Bus adapter for MPC107 bridge and MPC824x/85xx/52xx processors");
+MODULE_LICENSE("GPL");

-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply related

* Re: [PATCH] Addition to the i2c series, copy the ppc mpc-i2c driver before changing it on powerpc
From: Jon Smirl @ 2007-12-10 22:54 UTC (permalink / raw)
  To: Grant Likely; +Cc: PowerPC dev list
In-Reply-To: <fa686aa40712101444l448657c5jb6117e7a60715651@mail.gmail.com>

On 12/10/07, Grant Likely <grant.likely@secretlab.ca> wrote:
> On 12/10/07, Jon Smirl <jonsmirl@gmail.com> wrote:
> > Copy mpc-i2c to preserve support for ARCH=ppc and allow changes on ARCH=powerpc
> >
> > Temporarily copy the mpc-i2c driver to continue support for the ppc
> > architecture until it is removed in mid-2008. This file should be
> > deleted as part of ppc's final removal.
> >
> > Signed-off-by: Jon Smirl <jonsmirl@gmail.com>
>
> For the record; I'm not fond of this approach.  Supporting both bus
> bindings in the single driver is simple and results in less code churn
> when arch/ppc is removed, and encourages separation between the driver
> proper and the bus bindings which is just a good idea for all drivers
> in general.

But it also triggers a testing burden on a bunch of hardware that I
don't own. By copying off the known working ppc driver the testing
burden is avoided.

A subject for later discussion is whether platform bus should even
exist when of_platform_bus is in use. I have removed platform_bus for
the mpc5200 in my local builds. Removing platform bus exposed a bunch
of junk from other platofrms that had inadvertently accumulated into
the mpc5200 build.

-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox