diff mbox

[24/27] UBUNTU: SAUCE: powerpc/85xx: Add DPAA/networking support for P4080DS

Message ID 85c75499fb1d7fb465aae6b7902e04c22fc7864c.1339455422.git.bcollins@ubuntu.com
State New
Headers show

Commit Message

Benjamin Collins June 21, 2011, 4:41 a.m. UTC
* Add fsl,dpaa to device tree nodes that are probed
* setup 'private' storage needed by Qman, Bman and PME
* Add MDIO driver to deal with P4080DS MDIO muxing

This patch is being maintained and will eventually be merged upstream by
Freescale directly. The powerpc-e500mc flavour uses this.

Signed-off-by: Ioana Radulescu <ruxandra.radulescu@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Signed-off-by: Ben Collins <bcollins@ubuntu.com>
---
 arch/powerpc/platforms/85xx/Kconfig         |    6 +
 arch/powerpc/platforms/85xx/Makefile        |    1 +
 arch/powerpc/platforms/85xx/corenet_ds.c    |   49 ++++
 arch/powerpc/platforms/85xx/corenet_ds.h    |    4 +-
 arch/powerpc/platforms/85xx/p4080_ds.c      |   17 +-
 arch/powerpc/platforms/85xx/p4080_ds_mdio.c |  333 +++++++++++++++++++++++++++
 6 files changed, 408 insertions(+), 2 deletions(-)
 create mode 100644 arch/powerpc/platforms/85xx/p4080_ds_mdio.c
diff mbox

Patch

diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig
index f96d56a..750bfbe 100644
--- a/arch/powerpc/platforms/85xx/Kconfig
+++ b/arch/powerpc/platforms/85xx/Kconfig
@@ -252,6 +252,7 @@  config P4080_DS
 	select HAS_FSL_PAMU
 	select HAS_FSL_QBMAN
 	select HAS_FSL_PME
+	select FSL_P4080_DS_MDIO if PHYLIB
 	help
 	  This option enables support for the P4080 DS board
 
@@ -276,5 +277,10 @@  config P5020_DS
 
 endif # FSL_SOC_BOOKE
 
+config FSL_P4080_DS_MDIO
+	bool
+	select VITESSE_PHY
+	depends on PHYLIB
+
 config TQM85xx
 	bool
diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile
index 2125d4c..19f75f1 100644
--- a/arch/powerpc/platforms/85xx/Makefile
+++ b/arch/powerpc/platforms/85xx/Makefile
@@ -20,6 +20,7 @@  obj-$(CONFIG_P3041_DS)    += p3041_ds.o corenet_ds.o
 obj-$(CONFIG_P3060_QDS)   += p3060_qds.o corenet_ds.o
 obj-$(CONFIG_P4080_DS)    += p4080_ds.o corenet_ds.o
 obj-$(CONFIG_P5020_DS)    += p5020_ds.o corenet_ds.o
+obj-$(CONFIG_FSL_P4080_DS_MDIO) += p4080_ds_mdio.o
 obj-$(CONFIG_STX_GP3)	  += stx_gp3.o
 obj-$(CONFIG_TQM85xx)	  += tqm85xx.o
 obj-$(CONFIG_SBC8560)     += sbc8560.o
diff --git a/arch/powerpc/platforms/85xx/corenet_ds.c b/arch/powerpc/platforms/85xx/corenet_ds.c
index dd3617c..9177ad5 100644
--- a/arch/powerpc/platforms/85xx/corenet_ds.c
+++ b/arch/powerpc/platforms/85xx/corenet_ds.c
@@ -91,6 +91,9 @@  static const struct of_device_id of_device_ids[] __devinitconst = {
 		.compatible	= "simple-bus"
 	},
 	{
+		.compatible	= "fsl,dpaa"
+	},
+	{
 		.compatible	= "fsl,srio",
 	},
 	{
@@ -113,3 +116,49 @@  int __init corenet_ds_publish_devices(void)
 {
 	return of_platform_bus_probe(NULL, of_device_ids, NULL);
 }
+
+int __init declare_of_platform_devices(void)
+{
+	struct device_node *np;
+	int err;
+
+	err = of_platform_bus_probe(NULL, of_device_ids, NULL);
+	if (err)
+		return err;
+
+	/* Now probe the fake MDIO buses */
+	for_each_compatible_node(np, NULL, "fsl,p4080ds-mdio")
+		of_platform_device_create(np, NULL, NULL);
+
+	for_each_compatible_node(np, NULL, "fsl,p4080ds-xmdio")
+		of_platform_device_create(np, NULL, NULL);
+
+	return 0;
+}
+
+/* Early setup is required for large chunks of contiguous (and coarsely-aligned)
+ * memory. The following shoe-horns Qman/Bman "init_early" calls into the
+ * platform setup to let them parse their CCSR nodes early on. */
+#ifdef CONFIG_FSL_QMAN_CONFIG
+void __init qman_init_early(void);
+#endif
+#ifdef CONFIG_FSL_BMAN_CONFIG
+void __init bman_init_early(void);
+#endif
+#ifdef CONFIG_FSL_PME2_CTRL
+void __init pme2_init_early(void);
+#endif
+
+__init void corenet_ds_init_early(void)
+{
+#ifdef CONFIG_FSL_QMAN_CONFIG
+	qman_init_early();
+#endif
+#ifdef CONFIG_FSL_BMAN_CONFIG
+	bman_init_early();
+#endif
+#ifdef CONFIG_FSL_PME2_CTRL
+	pme2_init_early();
+#endif
+}
+
diff --git a/arch/powerpc/platforms/85xx/corenet_ds.h b/arch/powerpc/platforms/85xx/corenet_ds.h
index ddd700b..7f2e9b1 100644
--- a/arch/powerpc/platforms/85xx/corenet_ds.h
+++ b/arch/powerpc/platforms/85xx/corenet_ds.h
@@ -1,7 +1,7 @@ 
 /*
  * Corenet based SoC DS Setup
  *
- * Copyright 2009 Freescale Semiconductor Inc.
+ * Copyright 2009-2010 Freescale Semiconductor Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -15,5 +15,7 @@ 
 extern void __init corenet_ds_pic_init(void);
 extern void __init corenet_ds_setup_arch(void);
 extern int __init corenet_ds_publish_devices(void);
+extern int __init declare_of_platform_devices(void);
+extern void __init corenet_ds_init_early(void);
 
 #endif
diff --git a/arch/powerpc/platforms/85xx/p4080_ds.c b/arch/powerpc/platforms/85xx/p4080_ds.c
index c92417d..ad112ca 100644
--- a/arch/powerpc/platforms/85xx/p4080_ds.c
+++ b/arch/powerpc/platforms/85xx/p4080_ds.c
@@ -16,6 +16,7 @@ 
 #include <linux/kdev_t.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/phy.h>
 
 #include <asm/time.h>
 #include <asm/machdep.h>
@@ -66,6 +67,18 @@  static int __init p4080_ds_probe(void)
 	return 0;
 }
 
+#if defined(CONFIG_PHYLIB) && defined(CONFIG_VITESSE_PHY)
+int vsc824x_add_skew(struct phy_device *phydev);
+#define PHY_ID_VSC8244                  0x000fc6c0
+static int __init board_fixups(void)
+{
+	phy_register_fixup_for_uid(PHY_ID_VSC8244, 0xfffff, vsc824x_add_skew);
+
+	return 0;
+}
+machine_device_initcall(p4080_ds, board_fixups);
+#endif
+
 define_machine(p4080_ds) {
 	.name			= "P4080 DS",
 	.probe			= p4080_ds_probe,
@@ -79,9 +92,11 @@  define_machine(p4080_ds) {
 	.calibrate_decr		= generic_calibrate_decr,
 	.progress		= udbg_progress,
 	.power_save		= e500_idle,
+	.init_early		= corenet_ds_init_early,
 };
 
-machine_device_initcall(p4080_ds, corenet_ds_publish_devices);
+machine_device_initcall(p4080_ds, declare_of_platform_devices);
+
 #ifdef CONFIG_SWIOTLB
 machine_arch_initcall(p4080_ds, swiotlb_setup_bus_notifier);
 #endif
diff --git a/arch/powerpc/platforms/85xx/p4080_ds_mdio.c b/arch/powerpc/platforms/85xx/p4080_ds_mdio.c
new file mode 100644
index 0000000..1c08620
--- /dev/null
+++ b/arch/powerpc/platforms/85xx/p4080_ds_mdio.c
@@ -0,0 +1,333 @@ 
+/*
+ * Provides proper gpio muxing for the MDIO buses on the corenet DS boards
+ *
+ * Author: Andy Fleming <afleming@freescale.com>
+ *
+ * Copyright (c) 2009-2010 Freescale Semiconductor, Inc.
+ *
+ * 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/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/gpio.h>
+
+
+struct p4080ds_mdio {
+	struct mii_bus *real_bus;
+	unsigned int num_gpios;
+	unsigned int *gpios;
+	int *gpio_values;
+};
+
+/* Set the GPIO mux, and then write the MDIO regs */
+int p4080ds_mdio_write(struct mii_bus *bus, int port_addr, int dev_addr,
+			int regnum, u16 value)
+{
+	struct p4080ds_mdio *priv = bus->priv;
+	int i;
+
+	/* Write the GPIO regs to select this bus */
+	for (i = 0; i < priv->num_gpios; i++)
+		gpio_set_value(priv->gpios[i], priv->gpio_values[i]);
+
+	/* Write through to the attached MDIO bus */
+	return priv->real_bus->write(priv->real_bus, port_addr, dev_addr,
+					regnum, value);
+}
+
+/* Set the GPIO muxing, and then read from the MDIO bus */
+int p4080ds_mdio_read(struct mii_bus *bus, int port_addr, int dev_addr,
+			int regnum)
+{
+	struct p4080ds_mdio *priv = bus->priv;
+	int i;
+
+	/* Write the GPIO regs to select this bus */
+	for (i = 0; i < priv->num_gpios; i++)
+		gpio_set_value(priv->gpios[i], priv->gpio_values[i]);
+
+	return priv->real_bus->read(priv->real_bus, port_addr, dev_addr,
+					regnum);
+}
+
+
+/* Reset the MIIM registers, and wait for the bus to free */
+static int p4080ds_mdio_reset(struct mii_bus *bus)
+{
+	struct p4080ds_mdio *priv = bus->priv;
+
+	mutex_lock(&bus->mdio_lock);
+	priv->real_bus->reset(priv->real_bus);
+	mutex_unlock(&bus->mdio_lock);
+
+	return 0;
+}
+
+
+static int p4080ds_mdio_probe(struct platform_device *ofdev)
+{
+	struct device_node *np = ofdev->dev.of_node;
+	struct mii_bus *new_bus;
+	struct p4080ds_mdio *priv;
+	struct device_node *mdio;
+	struct platform_device *ofmdiodev;
+	int i;
+	const u32 *val;
+	u32 gpio_mask;
+	int gpio;
+	int err = 0;
+
+	new_bus = mdiobus_alloc();
+	if (NULL == new_bus)
+		return -ENOMEM;
+
+	new_bus->name = "Freescale P4080DS MDIO Bus",
+	new_bus->read = &p4080ds_mdio_read,
+	new_bus->write = &p4080ds_mdio_write,
+	new_bus->reset = &p4080ds_mdio_reset,
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		err = -ENOMEM;
+		goto err_priv_alloc;
+	}
+
+	new_bus->priv = priv;
+
+	mdio = of_parse_phandle(np, "fsl,mdio-handle", 0);
+
+	if (mdio == NULL) {
+		printk(KERN_ERR "Could not find real MDIO bus for %s\n",
+			new_bus->id);
+		err = -ENODEV;
+		goto err_no_mdio_node;
+	}
+
+	ofmdiodev = of_find_device_by_node(mdio);
+
+	if (!ofmdiodev) {
+		printk(KERN_ERR "No of_device for MDIO node %s\n", mdio->name);
+		err = -ENODEV;
+		goto err_no_mdio_dev;
+	}
+
+	of_node_put(mdio);
+
+	priv->real_bus = dev_get_drvdata(&ofmdiodev->dev);
+
+	if (!priv->real_bus) {
+		printk(KERN_ERR "The MDIO bus has no ofdev!\n");
+		err = -ENODEV;
+		goto err_no_ofdev;
+	}
+
+	new_bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
+
+	if (NULL == new_bus->irq) {
+		err = -ENOMEM;
+		goto err_irq_alloc;
+	}
+
+	for (i = 0; i < PHY_MAX_ADDR; i++)
+		new_bus->irq[i] = PHY_POLL;
+
+	new_bus->parent = &ofdev->dev;
+	dev_set_drvdata(&ofdev->dev, new_bus);
+
+	/* Get the number of GPIO pins specified for this MDIO node */
+	priv->num_gpios = of_gpio_count(mdio);
+	if (priv->num_gpios == 0) {
+		printk(KERN_ERR "No GPIO pins specified for MDIO node %s\n",
+			mdio->name);
+		err = -ENODEV;
+		goto err_no_gpios;
+	}
+
+	priv->gpios = kzalloc(sizeof(unsigned int) * priv->num_gpios,
+			      GFP_KERNEL);
+	if (!priv->gpios) {
+		err = -ENOMEM;
+		goto err_gpios_alloc;
+	}
+
+	priv->gpio_values = kzalloc(sizeof(int) * priv->num_gpios, GFP_KERNEL);
+	if (!priv->gpio_values) {
+		err = -ENOMEM;
+		goto err_gpio_values_alloc;
+	}
+
+	/* Grab the value to write to the GPIO */
+	val = of_get_property(np, "fsl,muxval", NULL);
+
+	if (!val) {
+		printk(KERN_ERR "No mux value found for %s\n", np->full_name);
+		err = -ENODEV;
+		goto err_get_muxval;
+	}
+	if (*val >= (1 << priv->num_gpios)) {
+		printk(KERN_ERR "Mux value for %s is out of range: [0 - %d]\n",
+				np->full_name, (1 << priv->num_gpios) - 1);
+		err = -EINVAL;
+		goto err_get_muxval;
+	}
+
+	for (i = 0; i < priv->num_gpios; i++) {
+		/* Based on GPIO pin specified in DT, get a pin number relative
+		 * to the GPIO controller's dynamically assigned base */
+		gpio = of_get_gpio_flags(mdio, i, NULL);
+		if (gpio < 0) {
+			printk(KERN_ERR "Failed to get GPIO pin value\n");
+			err = gpio;
+			goto err_gpio_get;
+		}
+		priv->gpios[i] = gpio;
+		if (!gpio_is_valid(priv->gpios[i])) {
+			printk(KERN_ERR "Invalid GPIO pin value: %d\n",
+				priv->gpios[i]);
+			err = -EINVAL;
+			goto err_gpio_get;
+		}
+
+		/* Set the value to be written to the GPIO pin
+		 * in order to select this mdio bus */
+		gpio_mask = 1 << (priv->num_gpios - i - 1);
+		priv->gpio_values[i] = !!(*val & gpio_mask);
+
+		/* Request the pin (check that pin number is valid & in range).
+		 * More than one P4080 MDIO bus will use these pins; if already
+		 * requested, continue without error */
+		err = gpio_request(priv->gpios[i], NULL);
+		if ((err < 0) && (err != -EBUSY)) {
+			printk(KERN_ERR "Failed to reserve GPIO pin %d\n",
+					priv->gpios[i]);
+			goto err_gpio_req;
+		}
+
+		/* Set the direction to output (we will write to this pin).
+		 * Should be already set from u-boot, but make sure
+		 * it's correct nonetheless */
+		err = gpio_direction_output(priv->gpios[i], 0);
+		if (err < 0) {
+			printk(KERN_ERR "Failed to configure direction for "
+					"GPIO pin %d\n", priv->gpios[i]);
+			goto err_gpio_dir;
+		}
+	}
+
+	sprintf(new_bus->id, "%s@%d", np->name, *val);
+
+	err = of_mdiobus_register(new_bus, np);
+
+	if (err) {
+		printk(KERN_ERR "%s: Cannot register as MDIO bus\n",
+				new_bus->name);
+		goto err_registration;
+	}
+
+	return 0;
+
+err_registration:
+err_gpio_dir:
+	for (i = 0; i < priv->num_gpios; i++)
+		gpio_free(priv->gpios[i]);
+err_gpio_req:
+err_gpio_get:
+err_get_muxval:
+	kfree(priv->gpio_values);
+err_gpio_values_alloc:
+	kfree(priv->gpios);
+err_gpios_alloc:
+err_no_gpios:
+	kfree(new_bus->irq);
+err_irq_alloc:
+err_no_ofdev:
+err_no_mdio_dev:
+err_no_mdio_node:
+	kfree(priv);
+err_priv_alloc:
+	kfree(new_bus);
+
+	return err;
+}
+
+static int p4080ds_mdio_remove(struct platform_device *ofdev)
+{
+	struct device *device = &ofdev->dev;
+	struct mii_bus *bus = dev_get_drvdata(device);
+	struct p4080ds_mdio *priv = bus->priv;
+	int i;
+
+	for (i = 0; i < priv->num_gpios; i++)
+		gpio_free(priv->gpios[i]);
+	kfree(priv->gpios);
+	kfree(priv->gpio_values);
+
+	mdiobus_unregister(bus);
+
+	dev_set_drvdata(device, NULL);
+
+	bus->priv = NULL;
+	mdiobus_free(bus);
+
+	return 0;
+}
+
+static struct of_device_id p4080ds_mdio_match[] = {
+	{
+		.compatible = "fsl,p4080ds-mdio",
+	},
+	{
+		.compatible = "fsl,p4080ds-xmdio",
+	},
+	{}
+};
+
+static struct platform_driver p4080ds_mdio_driver = {
+	.driver = {
+		.name = "p4080ds_mdio",
+		.of_match_table = p4080ds_mdio_match,
+	},
+	.probe = p4080ds_mdio_probe,
+	.remove = p4080ds_mdio_remove,
+};
+
+int __init p4080ds_mdio_init(void)
+{
+	return platform_driver_register(&p4080ds_mdio_driver);
+}
+
+void p4080ds_mdio_exit(void)
+{
+	platform_driver_unregister(&p4080ds_mdio_driver);
+}
+subsys_initcall_sync(p4080ds_mdio_init);
+module_exit(p4080ds_mdio_exit);