* Re: [PATCH RFC]: PHY Abstraction Layer III
From: Kumar Gala @ 2005-07-24 14:20 UTC (permalink / raw)
To: Francois Romieu; +Cc: netdev, Embedded PPC Linux list
In-Reply-To: <20050723215913.GA777@electric-eye.fr.zoreil.com>
Because the patch is all new code. drivers/net/phy/phy.c doesn't
exist in any kernel tree. The patch is adding it.
- kumar
On Jul 23, 2005, at 4:59 PM, Francois Romieu wrote:
> Andy Fleming <afleming@freescale.com> :
>
>> Here's the latest version of the patch, done against a cogito
>> linux-2.6 branch.
>>
>
> The patch includes a diff against drivers/net/phy/phy.c which does not
> seem to exist in my -rc3-git4 local tree. Where can it be found ?
>
> --
> Ueimor
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Re: [PATCH RFC]: PHY Abstraction Layer III
From: Francois Romieu @ 2005-07-23 21:59 UTC (permalink / raw)
To: Andy Fleming; +Cc: netdev, Embedded PPC Linux list
In-Reply-To: <23F9076F-9FBB-43C8-83CD-F630E694203D@freescale.com>
Andy Fleming <afleming@freescale.com> :
> Here's the latest version of the patch, done against a cogito
> linux-2.6 branch.
The patch includes a diff against drivers/net/phy/phy.c which does not
seem to exist in my -rc3-git4 local tree. Where can it be found ?
--
Ueimor
^ permalink raw reply
* Need SEC 1.0 Descriptor programmer's guide
From: Vikas Aggarwal @ 2005-07-23 18:34 UTC (permalink / raw)
To: linuxppc-embedded
In-Reply-To: <528646bc05072310039fb536d@mail.gmail.com>
Hi All,
I am porting a driver for SEC 1.0 on mpc8248(kernel 2.6.10 denx linux)
Can anyone share with me the link/document on "SEC 1.0 Descriptor's
Programmer guide".
On freescale's website, I could "ONLY" find the "SEC 2.0 Descriptor's
programmer guide" at this URL.
http://www.freescale.com/files/32bit/doc/app_note/AN2755.pdf
-vikas
MS CS Student
SUNY Albany
^ permalink raw reply
* Re: ppc_sys.c with platform device model or create opb bus?
From: Grant Likely @ 2005-07-23 17:03 UTC (permalink / raw)
To: Yasushi SHOJI; +Cc: linuxppc-embedded
In-Reply-To: <87hdeuosmq.wl@mail2.atmark-techno.com>
On 7/17/05, Yasushi SHOJI <yashi@atmark-techno.com> wrote:
> I feel that the ppc_sys_spec is for SoC, which doesn't dynamically
> change the peripherals it has. otoh, fpga based platform can have
> arbitrary number of devices if you configured so.
>=20
> I usually implement a device with PLB or OPB. for those bus, should I
> use platform device model or create new buses for each?
ppc_sys is just a convenient wrapper around the platform device model.
You can safely ignore ppc_sys if your board setup code calls
platform_device_register() for all of your devices directly.
Besides, as ppc moves to the flattened device tree for initializing
the platform bus, the whole ppc_sys stuff will be going away and the
device tree parsing code will call platform_device_* directly.
Cheers,
g.
^ permalink raw reply
* Re: [PATCH 0/3] Support for SPI busses and devices
From: Matt Porter @ 2005-07-23 14:30 UTC (permalink / raw)
To: Grant Likely; +Cc: linuxppc-embedded
In-Reply-To: <528646bc050723070419b8f914@mail.gmail.com>
On Sat, Jul 23, 2005 at 10:04:07AM -0400, Grant Likely wrote:
> Request for Comment:
>
> Here is a set of patches that adds SPI support to linux. I'm looking
> for feedback on these and I'd like to eventually get the SPI
> infrastructure into mainline
There is also another attempt at an SPI subsystem (not based on the RMK
code) that was posted to lkml. You might want to check out
http://lkml.org/lkml/2005/5/31/162 for the original posting and
then an updated version at http://lkml.org/lkml/2005/6/23/161
-Matt
^ permalink raw reply
* Re: [PATCH 1/3] SPI bus core infrastructure
From: Dan Malek @ 2005-07-23 15:11 UTC (permalink / raw)
To: Eugene Surovegin; +Cc: linuxppc-embedded
In-Reply-To: <20050723142210.GA24313@gate.ebshome.net>
On Jul 23, 2005, at 10:22 AM, Eugene Surovegin wrote:
> This isn't enough.
>
> You must have a way to specify clock frequency, data order (which bit
> goes first, char length.
... and there is more. Some devices, even when not selected, still
use the SPI clock for clocking internal logic. You have to ensure with
such devices you don't ever run the SPI faster than their spec. This
is application specific, but something that has to be considered when
there are multiple devices on the SPI bus.
Also, many boards use external logic for the assertion of the
device select, which is necessary if there is more than one device
on the bus. Sometimes it's a multiplexer that routes the controller
SPI select, other times the multiplexer drives the selects directly.
In my review of the patch, I didn't see anything that would provide
for this.
I think the SPI driver is a great idea, I hope we can find a way
to make it work at least as well as the I2C.
Thanks!
-- Dan
^ permalink raw reply
* Re: ppc_sys.c with platform device model or create opb bus?
From: Matt Porter @ 2005-07-23 14:48 UTC (permalink / raw)
To: Yasushi SHOJI; +Cc: linuxppc-embedded
In-Reply-To: <87hdeuosmq.wl@mail2.atmark-techno.com>
On Sun, Jul 17, 2005 at 03:26:21PM +0900, Yasushi SHOJI wrote:
> Hi all,
>
> I've been reading some posts regarding to the transition of OCP to
> platform device mode while searching for a good way to implement a
> device driver for our fpga base platform. And now I have one question
> regarding to ppc_sys.c
>
> should I use ppc_sys_*() for platform like fpga?
>
> since I'm working on FPGA base platform, ppc_sys_spec seems to be too
> static. that is, IMHO, having static array of device list isn't ideal
> for a dynamic system like fpga.
>
> I feel that the ppc_sys_spec is for SoC, which doesn't dynamically
> change the peripherals it has. otoh, fpga based platform can have
> arbitrary number of devices if you configured so.
>
> I usually implement a device with PLB or OPB. for those bus, should I
> use platform device model or create new buses for each?
Use the platform model. When you run into a case that can't be
handled properly then the platform model should be expanded to handle
it. If you instantiate a "platform device" by configuring the FPGA
from userspace then that's a hotplug event. The platform model
should be extended to handle hotplug for these kind of cases since
they are pretty common.
-Matt
^ permalink raw reply
* Re: AMCC 440EP (Bamboo board) support?
From: Matt Porter @ 2005-07-23 14:38 UTC (permalink / raw)
To: Josh Boyer; +Cc: Frank Lautenbach, linuxppc-embedded
In-Reply-To: <1122031045.20742.0.camel@yoda.jdub.homelinux.org>
On Fri, Jul 22, 2005 at 06:17:25AM -0500, Josh Boyer wrote:
> On Fri, 2005-07-22 at 12:02 +0200, Gerhard Jaeger wrote:
> > On Friday 22 July 2005 11:47, Frank Lautenbach wrote:
> > > Did anyone bring up linux already on this kind of board? If yes, please
> > > let me know. If not, any hints how to start from scratch
> > > starting with a kernel tree are higly welcome!
> > >
> > > Regards,
> > > Frank
> >
> > Yes, works for me here - Kernel 2.6.12 + Wade Farnsworth patches.
> > and our own toolchain (see my sig ;)
>
> Speaking of which... when will those patches go upstream? Wade?
I've only been waiting on updated patches that use the fpu.S code.
If you'll recall, that was the major hold-up when they were posted
before. I talked to Wade recently and he was busy with some other
things but is planning on posting an update...at which point it
will go upstream.
-Matt
^ permalink raw reply
* Re: [PATCH 1/3] SPI bus core infrastructure
From: Eugene Surovegin @ 2005-07-23 14:22 UTC (permalink / raw)
To: Grant Likely; +Cc: linuxppc-embedded
In-Reply-To: <528646bc05072307074e8fce3e@mail.gmail.com>
On Sat, Jul 23, 2005 at 10:07:38AM -0400, Grant Likely wrote:
> Patch to add support for SPI busses. SPI bus master drivers and SPI
> slave drivers register with the SPI infrastructure.
>
> +#define SPI_CLKEDGE_RISING 0
> +#define SPI_CLKEDGE_FALLING 1
> +struct spi_bus_ops {
> + int (*transfer) (struct spi_bus *bus, int id, int clkedge,
> + uint8_t *in, const uint8_t *out, size_t count);
> +};
This isn't enough.
You must have a way to specify clock frequency, data order (which bit
goes first, char length.
Take a look at how PowerQUICC defines SPI peripheral, to get an idea
on what should be made configurable.
This is what I use on PowerQUICC as SPI interface:
/* SPI parameters for a specific client.
* Most of them have the same meaning as in SPMODE register
*/
struct pq2_spi_client {
unsigned int clock_invert : 1;
unsigned int clock_phase : 1;
unsigned int reverse_data : 1;
int char_length;
int clock; /* in Hz */
const char *name;
void (*cs_control)(int assert);
};
int pq2_spi_xfer(struct pq2_spi_client*, const u8 *tx_buf, u8 *rx_buf, int len);
^ permalink raw reply
* [PATCH 3/3] support for ks8995m managed switch on the SPI bus
From: Grant Likely @ 2005-07-23 14:17 UTC (permalink / raw)
To: linuxppc-embedded
Add support for reading and writing Micrel ks8995m managed switch
register set from user space over the SPI bus.
Signed-off-by: Grant Likely <grant.likely@gdcanada.com>
diff -ruNp linux-spi-mpc5200/drivers/spi/Kconfig
linux-spi-mpc5200-ks8995m/drivers/spi/Kconfig
--- linux-spi-mpc5200/drivers/spi/Kconfig=092005-07-22 17:16:13.000000000 -=
0400
+++ linux-spi-mpc5200-ks8995m/drivers/spi/Kconfig=092005-07-23
02:59:02.000000000 -0400
@@ -30,5 +30,12 @@ config SPI_BUS_MPC52XX_PSC
comment "SPI slaves"
=09depends on SPI
=20
+config KS8995M
+=09tristate "Micrel/Kendin KS8995M Ethernet Switch SPI interface driver"
+=09depends on SPI
+=09help
+=09 Say Y here if you need to control a Kendin/Micrel KS8995M managed
+=09 Ethernet switch via the SPI interface.
+
endmenu
=20
diff -ruNp linux-spi-mpc5200/drivers/spi/Makefile
linux-spi-mpc5200-ks8995m/drivers/spi/Makefile
--- linux-spi-mpc5200/drivers/spi/Makefile=092005-07-22 17:16:13.000000000 =
-0400
+++ linux-spi-mpc5200-ks8995m/drivers/spi/Makefile=092005-07-23
02:59:02.000000000 -0400
@@ -4,3 +4,4 @@
=20
obj-$(CONFIG_SPI)=09=09=09+=3D spi-core.o
obj-$(CONFIG_SPI_BUS_MPC52XX_PSC)=09+=3D spi-mpc52xx-psc.o
+obj-$(CONFIG_KS8995M)=09=09=09+=3D ks8995m.o
diff -ruNp linux-spi-mpc5200/drivers/spi/ks8995m.c
linux-spi-mpc5200-ks8995m/drivers/spi/ks8995m.c
--- linux-spi-mpc5200/drivers/spi/ks8995m.c=091969-12-31 19:00:00.000000000=
-0500
+++ linux-spi-mpc5200-ks8995m/drivers/spi/ks8995m.c=092005-07-23
03:10:37.000000000 -0400
@@ -0,0 +1,237 @@
+/*
+ * drivers/spi/ks8995m-spi.c
+ *
+ * Micrel/Kendin KS8995M managed switch SPI interface driver
+ *
+ * Maintainer : Grant Likely <glikely@gmail.com>
+ *
+ * This file is in the public domain
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/interrupt.h>
+#include <linux/spi.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+
+#define KS8995M_NUM_REGS (128)
+
+struct ks8995m_data {
+=09struct cdev cdev;
+=09struct spi_device *spi_dev;
+=09uint8_t in_buff[KS8995M_NUM_REGS+2];
+=09uint8_t out_buff[KS8995M_NUM_REGS+2];
+};
+
+static int ks8995m_major;
+static int ks8995m_minor;
+static int ks8995m_minor_offset =3D 0;
+
+/* Forward method declarations for cdev */
+static int ks8995m_open(struct inode *inode, struct file *filp);
+static ssize_t ks8995m_read(struct file *filp, char __user *buff,
+=09=09=09=09size_t count, loff_t *offp);
+static ssize_t ks8995m_write(struct file *filp, const char __user *buff,
+=09=09=09=09size_t count, loff_t *offp);
+static int ks8995m_release(struct inode *inode, struct file *filp);
+
+static struct file_operations ks8995m_fops =3D {
+=09.owner =3D THIS_MODULE,
+=09.open =3D ks8995m_open,
+=09.read =3D ks8995m_read,
+=09.write =3D ks8995m_write,
+=09.release =3D ks8995m_release,
+};
+
+static int ks8995m_open(struct inode *inode, struct file *filp)
+{
+=09struct ks8995m_data *data =3D container_of(inode->i_cdev, struct
ks8995m_data, cdev);
+
+=09/* Private data is always set to the ks8995m_data structure */
+=09filp->private_data =3D data;
+=09return 0;
+}
+
+static ssize_t ks8995m_check_size(int start, int count)
+{
+=09if ((count < 1) || (start > KS8995M_NUM_REGS))
+=09=09return 0;
+
+=09if (start + count > KS8995M_NUM_REGS)
+=09=09count =3D KS8995M_NUM_REGS - start;
+
+=09return count;
+}
+
+static ssize_t ks8995m_read(struct file *filp, char __user *buff,
+=09=09=09=09size_t count, loff_t *offp)
+{
+=09struct ks8995m_data *data =3D filp->private_data;
+=09struct spi_bus *bus;
+=09int ret;
+
+=09count =3D ks8995m_check_size(*offp, count);
+=09if (!count)
+=09=09return 0;
+
+=09data->out_buff[0] =3D 0x03; /* read command */
+=09data->out_buff[1] =3D *offp; /* address */
+=09memset(&data->out_buff[2], 0, count);
+
+=09bus =3D to_spi_bus(data->spi_dev->dev.parent);
+
+=09ret =3D bus->ops->transfer(bus, data->spi_dev->id, SPI_CLKEDGE_RISING,
+=09=09=09=09data->in_buff, data->out_buff, count+2);
+=09if (ret)
+=09=09return ret;
+
+=09copy_to_user(buff, &data->in_buff[2], count);
+=09*offp +=3D count;
+
+=09return count;
+}
+
+static ssize_t ks8995m_write(struct file *filp, const char __user *buff,
+=09=09=09=09size_t count, loff_t *offp)
+{
+=09struct ks8995m_data *data =3D filp->private_data;
+=09struct spi_bus *bus;
+=09int ret;
+
+=09count =3D ks8995m_check_size(*offp, count);
+=09if (!count)
+=09=09return 0;
+
+=09data->out_buff[0] =3D 0x02; /* write command */
+=09data->out_buff[1] =3D *offp; /* address */
+=09copy_from_user(&data->out_buff[2], buff, count);
+
+=09bus =3D to_spi_bus(data->spi_dev->dev.parent);
+
+=09ret =3D bus->ops->transfer(bus, data->spi_dev->id, SPI_CLKEDGE_RISING,
+=09=09=09=09data->in_buff, data->out_buff, count+2);
+=09if (ret)
+=09=09return ret;
+
+=09*offp +=3D count;
+
+=09return count;
+}
+
+static int ks8995m_release(struct inode *inode, struct file *filp)
+{
+=09return 0;
+}
+
+static int ks8995m_probe (struct device *dev)
+{
+=09struct ks8995m_data *data;
+=09struct spi_device *spi_dev;
+=09int devno, err;
+
+=09spi_dev =3D to_spi_dev(dev);
+=09printk("ks8995m_probe(dev->bus_id=3D%s) (name=3D%s)\n",
+=09=09dev->bus_id, spi_dev->name);
+
+=09data =3D kmalloc(sizeof(struct ks8995m_data), GFP_KERNEL);
+=09if (!data)
+=09{
+=09=09printk(KERN_ALERT "Could not allocate driver memory\n");
+=09=09return -ENOMEM;
+=09}
+=09memset(data, 0, sizeof(struct ks8995m_data));
+
+=09dev->driver_data =3D data;
+
+=09data->spi_dev =3D spi_dev;
+=09cdev_init(&data->cdev, &ks8995m_fops);
+=09data->cdev.owner =3D THIS_MODULE;
+=09data->cdev.ops =3D &ks8995m_fops;
+
+=09devno =3D MKDEV(ks8995m_major, ks8995m_minor + ks8995m_minor_offset++);
+=09err =3D cdev_add(&data->cdev, devno, 1);
+=09if (err)
+=09{
+=09=09printk(KERN_INFO "ks8995m: could not register char dev\n");
+=09=09return -1;
+=09}
+
+=09return 0;
+}
+
+static int ks8995m_remove (struct device *dev)
+{
+=09struct ks8995m_data *data =3D dev->driver_data;
+
+=09printk("ks8995_spi_remove(dev->bus_id=3D%s)\n", dev->bus_id);
+
+=09cdev_del(&data->cdev);
+=09kfree(data);
+=09dev->driver_data =3D NULL;
+
+=09return 0;
+}
+
+static struct spi_driver ks8995m_driver =3D {
+=09.name =3D "ks8995m",
+=09.module =3D THIS_MODULE,
+=09.driver =3D {
+=09=09.probe =3D ks8995m_probe,
+=09=09.remove =3D ks8995m_remove,
+=09},
+};
+
+static int __init ks8995m_init(void)
+{
+=09dev_t dev;
+=09int ret;
+
+=09printk(KERN_ALERT "ks8995m: initializing\n");
+
+=09if (alloc_chrdev_region(&dev, 0, 128, "ks8995m"))
+=09{
+=09=09printk(KERN_ALERT "ks8995m: could not allocate chrdev\n");
+=09=09goto fail_alloc_chrdev;
+=09}
+=09ks8995m_major =3D MAJOR(dev);
+=09ks8995m_minor =3D MINOR(dev);
+
+=09printk(KERN_ALERT "ks8995m: allocated chrdev region (%i:%i)!\n",
+=09 ks8995m_major, ks8995m_minor);
+
+=09ret =3D spi_driver_register(&ks8995m_driver);
+=09if (ret)
+=09{
+=09=09printk(KERN_ALERT "ks8995m: could not register driver\n");
+=09=09goto fail_register;
+=09}
+
+
+=09return 0;
+
+fail_register:
+
+fail_alloc_chrdev:
+=09return -1;
+}
+
+static void __exit ks8995m_exit(void)
+{
+=09int devno =3D MKDEV(ks8995m_major, ks8995m_minor);
+
+=09spi_driver_unregister(&ks8995m_driver);
+=09unregister_chrdev_region(devno, 128);
+
+=09printk(KERN_ALERT "ks8995m: exiting\n");
+}
+
+module_init(ks8995m_init);
+module_exit(ks8995m_exit);
+
+MODULE_AUTHOR("Grant Likely <glikely@gmail.com>");
+MODULE_DESCRIPTION("SPI register interface for ks8995m managed switch");
+MODULE_LICENSE("Dual BSD/GPL");
^ permalink raw reply
* [PATCH 2/3] support for mpc52xx PSC as an SPI bus master
From: Grant Likely @ 2005-07-23 14:15 UTC (permalink / raw)
To: linuxppc-embedded
SPI bus master driver for the Freescale MPC52xx.
Note: This patch as it stands right now will compile cleanly, but may
not bind the driver to the PSC. arch/ppc/syslib/mpc52xx_devices.c
needs to be modified to rename the desired PSC platform devices to
match the driver. (rename to "mpc52xx-psc:spi") This is a board
specific hack, and I need to make it cleaner before I resubmit this
patch.
Signed-off-by: Grant Likely <grant.likely@gdcanada.com>
diff -ruNp linux-spi/arch/ppc/platforms/lite5200.c
linux-spi-mpc5200/arch/ppc/platforms/lite5200.c
--- linux-spi/arch/ppc/platforms/lite5200.c=092005-06-17 15:48:29.000000000=
-0400
+++ linux-spi-mpc5200/arch/ppc/platforms/lite5200.c=092005-07-23
03:17:39.000000000 -0400
@@ -76,6 +76,21 @@ lite5200_map_irq(struct pci_dev *dev, un
}
#endif
=20
+#if defined(CONFIG_SPI_BUS_MPC52XX_PSC) ||
defined(CONFIG_SPI_BUS_MPC52XX_PSC_MODULE)
+void
+spi_mpc52xx_psc_slave_select(int psc_id, int slave_id)
+{
+}
+
+void
+spi_mpc52xx_psc_slave_deselect(int psc_id)
+{
+}
+
+EXPORT_SYMBOL(spi_mpc52xx_psc_slave_select);
+EXPORT_SYMBOL(spi_mpc52xx_psc_slave_deselect);
+#endif
+
static void __init
lite5200_setup_cpu(void)
{
@@ -107,15 +122,30 @@ lite5200_setup_cpu(void)
=09else
=09=09out_be16(&cdm->fd_counters, 0x5555);
=20
+=09/* Setup clocks for PSC3 SPI mode */
+=09out_be16(&cdm->mclken_div_psc3, 0x8020); /* Enable MClk for PSC3 */
+
=09/* Get port mux config */
=09port_config =3D in_be32(&gpio->port_config);
=20
=09/* 48Mhz internal, pin is GPIO */
-=09port_config &=3D ~0x00800000;
+=09port_config &=3D ~PORT_CONFIG_IR_USB_CLK;
=20
-=09/* USB port */
-=09port_config &=3D ~0x00007000;=09/* Differential mode - USB1 only */
-=09port_config |=3D 0x00001000;
+=09/* USB port: Differential mode; USB1 only */
+=09port_config &=3D ~(PORT_CONFIG_USB_SE | PORT_CONFIG_USB_MASK);
+=09port_config |=3D PORT_CONFIG_USB_USB;
+
+=09/* PSC2 (I2S Codec mode) */
+=09port_config &=3D ~PORT_CONFIG_PSC2_MASK;
+=09port_config |=3D PORT_CONFIG_PSC2_CODEC; /* Codec mode */
+
+=09/* PSC3 (SPI Codec mode) */
+=09port_config &=3D ~PORT_CONFIG_PSC3_MASK;
+=09port_config |=3D PORT_CONFIG_PSC3_CODEC; /* Codec mode */
+
+=09/* PSC6 (I2S Codec mode) */
+=09port_config &=3D ~PORT_CONFIG_IRDA_MASK;
+=09port_config |=3D PORT_CONFIG_IRDA_CODEC; /* Codec mode */
=20
=09/* Commit port config */
=09out_be32(&gpio->port_config, port_config);
diff -ruNp linux-spi/drivers/spi/Kconfig linux-spi-mpc5200/drivers/spi/Kcon=
fig
--- linux-spi/drivers/spi/Kconfig=092005-07-22 17:08:00.000000000 -0400
+++ linux-spi-mpc5200/drivers/spi/Kconfig=092005-07-22 17:16:13.000000000 -=
0400
@@ -20,6 +20,13 @@ config SPI
comment "SPI Bus Drivers"
=09depends on SPI
=20
+config SPI_BUS_MPC52XX_PSC
+=09tristate "SPI bus via MPC52xx PSC port"
+=09depends on SPI && PPC_MPC52xx
+=09help
+=09 Say Y here if you want to use an MPC52xx PSC port as an SPI bus
+=09 master.
+
comment "SPI slaves"
=09depends on SPI
=20
diff -ruNp linux-spi/drivers/spi/Makefile linux-spi-mpc5200/drivers/spi/Mak=
efile
--- linux-spi/drivers/spi/Makefile=092005-07-22 17:08:00.000000000 -0400
+++ linux-spi-mpc5200/drivers/spi/Makefile=092005-07-22 17:16:13.000000000 =
-0400
@@ -3,3 +3,4 @@
#
=20
obj-$(CONFIG_SPI)=09=09=09+=3D spi-core.o
+obj-$(CONFIG_SPI_BUS_MPC52XX_PSC)=09+=3D spi-mpc52xx-psc.o
diff -ruNp linux-spi/drivers/spi/spi-mpc52xx-psc.c
linux-spi-mpc5200/drivers/spi/spi-mpc52xx-psc.c
--- linux-spi/drivers/spi/spi-mpc52xx-psc.c=091969-12-31 19:00:00.000000000=
-0500
+++ linux-spi-mpc5200/drivers/spi/spi-mpc52xx-psc.c=092005-07-22
17:16:13.000000000 -0400
@@ -0,0 +1,307 @@
+/*
+ * drivers/spi/mpc5xxx-psc.c
+ *
+ * Driver for the SPI mode of the MPC52xx PSC peripherals
+ *
+ * Maintainer : Grant Likely <glikely@gmail.com>
+ *
+ * This file is in the public domain
+ */
+
+/* Platform device Usage :
+ * (Blatantly copied from drivers/serial/mpc52xx_uart.c)
+ *
+ * Since PSCs can have multiple function, the correct driver for each one
+ * is selected based on the name assigned to the psc. By convention, the
+ * function is appended to the device name in the board setup code. For
+ * example, this spi psc driver will only bind to mpc52xx_psc.spi devices.
+ *
+ * The driver init all necessary registers to place the PSC in spi mode.
+ * However, the pin multiplexing aren't changed and should be set either
+ * by the bootloader or in the platform init code.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/spi.h>
+
+#include <asm/io.h>
+#include <asm/mpc52xx.h>
+#include <asm/mpc52xx_psc.h>
+
+
+/* The magic data is all in this structure. This structure contains
+ * everything we need to keep track of the PSC
+ */
+struct mpc52xx_psc_spi_port {
+=09struct platform_device *pdev;=09=09/* PSC dev (on platform bus) */
+=09struct mpc52xx_psc __iomem *psc;=09/* PSC registers */
+=09struct spi_bus spi_bus;
+=09int irq;=09=09=09=09/* Assigned irq */
+
+=09/* Current state variables */
+=09struct semaphore sema; /* mutex to enforce one transfer at a time */
+=09wait_queue_head_t wait_queue; /* Wait on this queue during transfer */
+
+=09/* Warning: The following fields are not protected by a mutex.
+=09 * They can only be changed before a transfer starts or after
+=09 * a transfer ends. During a transfer the isr will fiddle with
+=09 * them */
+=09int rx_count;=09=09/* remaining bytes to be received */
+=09uint8_t *rx_ptr;=09/* pointer for next RX character */
+};
+#define to_spi_port(bus) container_of(bus, struct
mpc52xx_psc_spi_port, spi_bus);
+
+struct mpc52xx_psc_spi_port mpc52xx_psc_spi_ports[MPC52xx_PSC_MAXNUM];
+
+
+/* Dynamically update the alarm level so interrupts don't occur too fast *=
/
+static void mpc52xx_psc_spi_adjust_alarm(struct mpc52xx_psc_spi_port *port=
)
+{
+=09struct mpc52xx_psc __iomem *psc =3D port->psc;
+=09int alarm;
+
+=09alarm =3D 0x200 - port->rx_count;
+=09if (alarm < 0) alarm =3D 0;
+=09if (alarm > 0x1ff) alarm =3D 0x1ff;
+=09out_be16(&psc->rfalarm, alarm);
+}
+
+static irqreturn_t
+mpc52xx_psc_spi_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+=09struct mpc52xx_psc_spi_port *port =3D (struct uart_port *)dev_id;
+=09struct mpc52xx_psc __iomem *psc =3D port->psc;
+=09int num, i;
+
+=09if ( irq !=3D port->irq ) {
+=09=09printk( KERN_WARNING "mpc52xx_psc_spi: " \
+=09=09 "Received wrong int %d. Waiting for %d\n",
+=09=09 irq, port->irq);
+=09=09return IRQ_NONE;
+=09}
+=09
+=09if (in_be16(&psc->mpc52xx_psc_isr) & MPC52xx_PSC_IMR_RXRDY)
+=09{
+=09=09num =3D in_be16(&psc->rfnum);
+
+=09=09/* Discard unexpected rx data */
+=09=09if ((!port->rx_count) || (!port->rx_ptr))
+=09=09{
+=09=09=09printk(KERN_WARNING "mpc52xx_psc_spi: "
+=09=09=09=09"discarding %i unexpected bytes\n", num);
+=09=09=09for (i =3D 0; i < num; i++)
+=09=09=09=09in_8(&psc->mpc52xx_psc_buffer_8);
+=09=09=09return IRQ_NONE;
+=09=09}
+
+=09=09/* Don't overflow the buffer */
+=09=09if (num > port->rx_count)
+=09=09=09num =3D port->rx_count;
+
+=09=09/* Transfer data into rx buffer */
+=09=09ioread8_rep(&psc->mpc52xx_psc_buffer_8, port->rx_ptr, num);
+=09=09port->rx_ptr +=3D num;
+=09=09port->rx_count -=3D num;
+
+=09=09/* Dynamically update the alarm level so interrupts don't
+=09=09 * occur too frequently */
+=09=09mpc52xx_psc_spi_adjust_alarm(port);
+
+=09=09/* Only wakeup process if data transfer is complete */
+=09=09if (port->rx_count =3D=3D 0)
+=09=09=09wake_up_interruptible(&port->wait_queue);
+=09}
+
+=09return IRQ_NONE;
+}
+
+static int mpc52xx_psc_spi_startup(struct mpc52xx_psc_spi_port *port)
+{
+=09int ret;
+=09struct mpc52xx_psc __iomem *psc =3D port->psc;
+
+=09ret =3D request_irq(port->irq, mpc52xx_psc_spi_isr,
+=09=09SA_INTERRUPT | SA_SAMPLE_RANDOM, "mpc52xx_psc_spi", port);
+=09if (ret)
+=09{
+=09=09printk(KERN_ALERT "mpc52xx_psc_spi: request_irq failed (%i)\n",
+=09=09=09ret);
+=09=09return ret;
+=09}
+
+=09init_MUTEX(&port->sema);
+=09init_waitqueue_head(&port->wait_queue);
+=09port->rx_count =3D 0;
+=09port->rx_ptr =3D NULL;
+
+=09/* Reset the PSC into a known state */
+=09out_8(&psc->command, MPC52xx_PSC_RST_RX);
+=09out_8(&psc->command, MPC52xx_PSC_RST_TX);
+=09out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE);
+
+=09/* Interrupts must be based on alarm levels */
+=09out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1);
+=09out_8(&psc->mode, MPC52xx_PSC_MODE_FFULL);
+
+=09/* Configure 8bit codec mode as an SPI master and use EOF flags */
+=09out_be32(&psc->sicr, MPC52xx_PSC_SICR_SIM_CODEC8 |
+=09 MPC52xx_PSC_SICR_GENCLK |
+=09 MPC52xx_PSC_SICR_SPI |
+=09 MPC52xx_PSC_SICR_MSTR |
+=09 MPC52xx_PSC_SICR_USEEOF );
+=09out_be16(&psc->ccr, 0x070F);
+
+=09/* Set 2ms DTL delay */
+=09out_8(&psc->ctur, 0x00);
+=09out_8(&psc->ctlr, 0x84);
+}
+
+static int mpc52xx_psc_spi_transfer(struct spi_bus *bus, int id, int clked=
ge,
+=09=09=09=09uint8_t *in, const uint8_t *out, size_t count)
+{
+=09struct mpc52xx_psc_spi_port *port;
+=09struct mpc52xx_psc __iomem *psc;
+=09uint32_t=09sicr;
+=09int err;
+
+=09port =3D to_spi_port(bus);
+=09psc =3D port->psc;
+
+=09//printk(KERN_ALERT "mpc52xx_psc_spi: transfer; count=3D%i\n", count);
+
+=09if (count < 1)
+=09=09return -1;
+
+=09/* Grab the semaphore, only one transfer allowed at a time */
+=09err =3D down_interruptible(&port->sema);
+=09if (err)
+=09=09return err;
+
+=09/* Disable SPI interrupts so rx buffer can be manipulated */
+=09out_be16(&psc->mpc52xx_psc_imr, 0);
+
+=09/* Set the clock polarity */
+=09sicr =3D in_be32(&psc->sicr);
+=09if (clkedge =3D=3D SPI_CLKEDGE_RISING)
+=09=09sicr &=3D ~MPC52xx_PSC_SICR_CPOL; /* Clear means rising edge */
+=09else
+=09=09sicr |=3D MPC52xx_PSC_SICR_CPOL; /* Set means falling edge */
+=09out_be32(&psc->sicr, sicr);
+
+=09/* Initialize the receive buffer; this can be done without locking
+=09 * because the ISR doesn't touch it until after interrupts are
+=09 * enabled
+=09 */
+=09port->rx_ptr =3D in;
+=09port->rx_count =3D count;
+=09mpc52xx_psc_spi_adjust_alarm(port);
+
+=09/* Select the slave, start the engine and transfer data to fifo */
+=09spi_mpc52xx_psc_slave_select(port->pdev->id, id);
+=09iowrite8(MPC52xx_PSC_RX_ENABLE | MPC52xx_PSC_TX_ENABLE, &psc->command);
+=09if (count > 1)
+=09=09iowrite8_rep(&psc->mpc52xx_psc_buffer_8, out, count-1);
+
+=09/* Signal EOF for the last byte to terminate the transfer */
+=09iowrite8(MPC52xx_PSC_IRCR2_NXTEOF, &psc->ircr2);
+=09iowrite8(out[count-1], &psc->mpc52xx_psc_buffer_8);
+
+=09/* Enable interrupts and go to sleep until transfer is complete*/
+=09out_be16(&psc->mpc52xx_psc_imr, MPC52xx_PSC_IMR_RXRDY);
+=09err =3D wait_event_interruptible(port->wait_queue, port->rx_count =3D=
=3D 0);
+
+=09/* Terminate the transfer, and deselect the slave */
+=09port->rx_ptr =3D NULL;
+=09out_8(&psc->command, MPC52xx_PSC_TX_DISABLE | MPC52xx_PSC_RX_DISABLE);
+=09spi_mpc52xx_psc_slave_deselect(port->pdev->id);
+
+=09/* Release the semaphore to allow other transfers */
+=09up(&port->sema);
+=09return err;
+}
+
+static struct spi_bus_ops mpc52xx_psc_spi_ops =3D {
+=09.transfer =3D mpc52xx_psc_spi_transfer,
+};
+
+static int __devinit
+mpc52xx_psc_spi_probe(struct device *dev)
+{
+=09struct platform_device *pdev =3D to_platform_device(dev);
+=09struct resource *res =3D pdev->resource;
+=09struct mpc52xx_psc_spi_port *port;
+=09int idx =3D pdev->id;
+=09int i;
+
+=09printk(KERN_ALERT "mpc52xx-psc-spi: probing idx=3D%i\n", idx);
+
+=09if ((idx !=3D 0) && (idx !=3D 1) && (idx !=3D2) && (idx !=3D 5))
+=09{
+=09=09printk(KERN_ALERT "mpc52xx-psc-spi: PSC%i can't do SPI\n",
+=09=09 idx+1);
+=09=09return -ENODEV;
+=09}
+
+=09port =3D &mpc52xx_psc_spi_ports[idx];
+=09port->pdev =3D pdev;
+=09for (i =3D 0; i < pdev->num_resources; i++, res++)
+=09{
+=09=09if (res->flags & IORESOURCE_MEM)
+=09=09=09port->psc =3D (struct mpc52xx_psc __iomem *) res->start;
+=09=09else if (res->flags & IORESOURCE_IRQ)
+=09=09=09port->irq =3D res->start;
+=09}
+
+=09mpc52xx_psc_spi_startup(port);
+=09port->spi_bus.ops =3D &mpc52xx_psc_spi_ops;
+=09port->spi_bus.dev.parent =3D dev; /* The psc is the parent of the bus =
*/
+=09spi_bus_register(&port->spi_bus);
+
+=09/* Ahhh!!! Run away from the board specific hack (This won't be=20
+=09 * part of the real patch)
+=09 */
+=09spi_device_create(&port->spi_bus, "ks8995m_spi", 0);
+=09spi_device_create(&port->spi_bus, "tlv320aic26", 1);
+=09return 0;
+}
+
+static int=20
+mpc52xx_psc_spi_remove(struct device *dev)
+{
+=09return 0;
+}
+
+static struct device_driver mpc52xx_psc_spi_platform_driver =3D {
+=09.name=09=09=3D "mpc52xx-psc:spi",
+=09.bus=09=09=3D &platform_bus_type,
+=09.probe=09=09=3D mpc52xx_psc_spi_probe,
+=09.remove=09=09=3D mpc52xx_psc_spi_remove,
+};
+
+static int __init mpc52xx_psc_spi_init(void)
+{
+=09int ret;
+
+=09printk(KERN_ALERT "mpc52xx_psc_spi: initializing\n");
+
+=09ret =3D driver_register(&mpc52xx_psc_spi_platform_driver);
+=09return ret;
+
+
+}
+
+static void __exit mpc52xx_psc_spi_exit(void)
+{
+=09driver_unregister(&mpc52xx_psc_spi_platform_driver);
+=09printk(KERN_ALERT "mpc52xx_psc_spi: exiting\n");
+}
+
+module_init(mpc52xx_psc_spi_init);
+module_exit(mpc52xx_psc_spi_exit);
+
+MODULE_AUTHOR("Grant Likely <glikely@gmail.com>");
+MODULE_DESCRIPTION("Freescale MPC52xx PSC driver for SPI mode");
+MODULE_LICENSE("Dual BSD/GPL");
diff -ruNp linux-spi/include/asm-ppc/mpc52xx.h
linux-spi-mpc5200/include/asm-ppc/mpc52xx.h
--- linux-spi/include/asm-ppc/mpc52xx.h=092005-06-17 15:48:29.000000000 -04=
00
+++ linux-spi-mpc5200/include/asm-ppc/mpc52xx.h=092005-07-23
02:29:41.000000000 -0400
@@ -146,6 +146,28 @@ enum ppc_sys_devices {
#define MPC52xx_XLB_ARB_IRQ=09=09(MPC52xx_PERP_IRQ_BASE + 21)
#define MPC52xx_BDLC_IRQ=09=09(MPC52xx_PERP_IRQ_BASE + 22)
=20
+/* CDM Cloke enable bits */
+#define MPC52xx_CDM_CLKENABLE_MEM_CLK=09=09(0x00080000)
+#define MPC52xx_CDM_CLKENABLE_PCI_CLK=09=09(0x00040000)
+#define MPC52xx_CDM_CLKENABLE_LPC_CLK=09=09(0x00020000)
+#define MPC52xx_CDM_CLKENABLE_SIT_CLK=09=09(0x00010000)
+#define MPC52xx_CDM_CLKENABLE_SCOM_CLK=09=09(0x00008000)
+#define MPC52xx_CDM_CLKENABLE_ATA_CLK=09=09(0x00004000)
+#define MPC52xx_CDM_CLKENABLE_ETH_CLK=09=09(0x00002000)
+#define MPC52xx_CDM_CLKENABLE_USB_CLK=09=09(0x00001000)
+#define MPC52xx_CDM_CLKENABLE_SPI_CLK=09=09(0x00000800)
+#define MPC52xx_CDM_CLKENABLE_BDLC_CLK=09=09(0x00000400)
+#define MPC52xx_CDM_CLKENABLE_IRRX_CLK=09=09(0x00000200)
+#define MPC52xx_CDM_CLKENABLE_IRTX_CLK=09=09(0x00000100)
+#define MPC52xx_CDM_CLKENABLE_PSC345_CLK=09(0x00000080)
+#define MPC52xx_CDM_CLKENABLE_PSC2_CLK=09=09(0x00000040)
+#define MPC52xx_CDM_CLKENABLE_PSC1_CLK=09=09(0x00000020)
+#define MPC52xx_CDM_CLKENABLE_PSC6_CLK=09=09(0x00000010)
+#define MPC52xx_CDM_CLKENABLE_MSCAN_CLK=09=09(0x00000008)
+#define MPC52xx_CDM_CLKENABLE_I2C_CLK=09=09(0x00000004)
+#define MPC52xx_CDM_CLKENABLE_TIMER_CLK=09=09(0x00000002)
+#define MPC52xx_CDM_CLKENABLE_GPIO_CLK=09=09(0x00000001)
+
=20
=20
/* =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D */
@@ -266,6 +288,49 @@ struct mpc52xx_rtc {
};
=20
/* GPIO */
+#define PORT_CONFIG_CS1=09=09=090x80000000
+#define PORT_CONFIG_ALT_MASK=09=090x30000000
+#define PORT_CONFIG_CS7=09=09=090x08000000
+#define PORT_CONFIG_CS6=09=09=090x04000000
+#define PORT_CONFIG_ATA=09=09=090x03000000
+#define PORT_CONFIG_IR_USB_CLK=09=090x00800000
+#define PORT_CONFIG_IRDA_MASK=09=090x00700000 /* PSC6 */
+#define PORT_CONFIG_IRDA_GPIO=09=090x00000000
+#define PORT_CONFIG_IRDA_UART=09=090x00500000
+#define PORT_CONFIG_IRDA_CODEC=09=090x00700000
+#define PORT_CONFIG_ETHER_MASK=09=090x000F0000
+#define PORT_CONFIG_PCI_DIS=09=090x00008000
+#define PORT_CONFIG_USB_SE=09=090x00004000 /* Single ended mode */
+#define PORT_CONFIG_USB_MASK=09=090x00004000 /* (USB1 or 2 UARTs) */
+#define PORT_CONFIG_USB_USB=09=090x00001000
+#define PORT_CONFIG_USB_2UART=09=090x00002000
+#define PORT_CONFIG_PSC3_MASK=09=090x00000F00
+#define PORT_CONFIG_PSC3_GPIO=09=090x00000000
+#define PORT_CONFIG_PSC3_USB2=09=090x00000100
+#define PORT_CONFIG_PSC3_UART=09=090x00000400
+#define PORT_CONFIG_PSC3_UARTE_CD=090x00000500
+#define PORT_CONFIG_PSC3_CODEC=09=090x00000600
+#define PORT_CONFIG_PSC3_CODEC_MCLK=090x00000700
+#define PORT_CONFIG_PSC3_SPI=09=090x00000800
+#define PORT_CONFIG_PSC3_SPI_UART=090x00000C00
+#define PORT_CONFIG_PSC3_SPI_UARTE=090x00000D00
+#define PORT_CONFIG_PSC3_SPI_CODEC=090x00000E00
+#define PORT_CONFIG_PSC2_MASK=09=090x00000070
+#define PORT_CONFIG_PSC2_GPIO=09=090x00000000
+#define PORT_CONFIG_PSC2_CAN=09=090x00000010
+#define PORT_CONFIG_PSC2_AC97=09=090x00000020
+#define PORT_CONFIG_PSC2_UART=09=090x00000040
+#define PORT_CONFIG_PSC2_UARTE_CD=090x00000050
+#define PORT_CONFIG_PSC2_CODEC=09=090x00000060
+#define PORT_CONFIG_PSC2_CODEC_MCLK=090x00000070
+#define PORT_CONFIG_PSC1_MASK=09=090x00000007
+#define PORT_CONFIG_PSC1_GPIO=09=090x00000000
+#define PORT_CONFIG_PSC1_AC97=09=090x00000002
+#define PORT_CONFIG_PSC1_UART=09=090x00000004
+#define PORT_CONFIG_PSC1_UARTE_CD=090x00000005
+#define PORT_CONFIG_PSC1_CODEC=09=090x00000006
+#define PORT_CONFIG_PSC1_CODEC_MCLK=090x00000007
+
struct mpc52xx_gpio {
=09u32=09port_config;=09/* GPIO + 0x00 */
=09u32=09simple_gpioe;=09/* GPIO + 0x04 */
diff -ruNp linux-spi/include/asm-ppc/mpc52xx_psc.h
linux-spi-mpc5200/include/asm-ppc/mpc52xx_psc.h
--- linux-spi/include/asm-ppc/mpc52xx_psc.h=092005-06-17 15:48:29.000000000=
-0400
+++ linux-spi-mpc5200/include/asm-ppc/mpc52xx_psc.h=092005-07-22
17:16:13.000000000 -0400
@@ -72,6 +72,51 @@
#define MPC52xx_PSC_D_CTS=09=090x10
#define MPC52xx_PSC_D_DCD=09=090x20
=20
+/* PSC Serial Interface Control Register (SICR) bits */
+/* SICR Field masks */
+#define MPC52xx_PSC_SICR_ACRB=09=090x80000000
+#define MPC52xx_PSC_SICR_AWR=09=090x40000000
+#define MPC52xx_PSC_SICR_DTS1=09=090x20000000
+#define MPC52xx_PSC_SICR_SHDIR=09=090x10000000
+#define MPC52xx_PSC_SICR_SIM=09=090x0F000000
+#define MPC52xx_PSC_SICR_GENCLK=09=090x00800000
+#define MPC52xx_PSC_SICR_MULTIWD=090x00400000
+#define MPC52xx_PSC_SICR_CLKPOL=09=090x00200000
+#define MPC52xx_PSC_SICR_SYNCPOL=090x00100000
+#define MPC52xx_PSC_SICR_CELLSLAVE=090x00080000
+#define MPC52xx_PSC_SICR_CELL2XCLK=090x00040000
+#define MPC52xx_PSC_SICR_SPI=09=090x00008000
+#define MPC52xx_PSC_SICR_MSTR=09=090x00004000
+#define MPC52xx_PSC_SICR_CPOL=09=090x00002000
+#define MPC52xx_PSC_SICR_CPHA=09=090x00001000
+#define MPC52xx_PSC_SICR_USEEOF=09=090x00000800
+/* Operation modes */
+#define MPC52xx_PSC_SICR_SIM_UART=09=090x00000000
+#define MPC52xx_PSC_SICR_SIM_UART_DCD=09=090x08000000
+#define MPC52xx_PSC_SICR_SIM_CODEC8=09=090x01000000
+#define MPC52xx_PSC_SICR_SIM_CODEC16=09=090x02000000
+#define MPC52xx_PSC_SICR_SIM_AC97=09=090x03000000
+#define MPC52xx_PSC_SICR_SIM_SIR=09=090x04000000
+#define MPC52xx_PSC_SICR_SIM_SIR_DCD=09=090x0C000000
+#define MPC52xx_PSC_SICR_SIM_MIR=09=090x05000000
+#define MPC52xx_PSC_SICR_SIM_FIR=09=090x06000000
+#define MPC52xx_PSC_SICR_SIM_CODEC24=09=090x07000000
+#define MPC52xx_PSC_SICR_SIM_CODEC32=09=090x0F000000
+
+/* IRCR1 bit masks */
+#define MPC52xx_PSC_IRCR1_FD=09=09=090x04
+#define MPC52xx_PSC_IRCR1_SIPEN=09=09=090x02
+#define MPC52xx_PSC_IRCR1_SPUL=09=09=090x01
+
+/* IRCR2 bit masks */
+#define MPC52xx_PSC_IRCR2_SIPREQ=09=090x04
+#define MPC52xx_PSC_IRCR2_ABORT=09=09=090x02
+#define MPC52xx_PSC_IRCR2_NXTEOF=09=090x01
+
+/* Codec Clock Register fields */
+#define MPC52xx_PSC_CCR_FRAME_SYNC_DIV=09=090xFF00
+#define MPC52xx_PSC_CCR_BIT_CLK_DIV=09=090xFF00
+
/* PSC mode fields */
#define MPC52xx_PSC_MODE_5_BITS=09=09=090x00
#define MPC52xx_PSC_MODE_6_BITS=09=09=090x01
@@ -187,5 +232,10 @@ struct mpc52xx_psc {
=09u16=09=09tflwfptr;=09/* PSC + 0x9e */
};
=20
+#if defined(CONFIG_SPI_BUS_MPC52XX_PSC) ||
defined(CONFIG_SPI_BUS_MPC52XX_PSC_MODULE)
+/* Helper functions for selecting slaves SPI mode */
+extern void spi_mpc52xx_psc_slave_select(int psc_id, int slave_id);
+extern void spi_mpc52xx_psc_slave_deselect(int psc_id);
+#endif
=20
#endif /* __ASM_MPC52xx_PSC_H__ */
^ permalink raw reply
* [PATCH 1/3] SPI bus core infrastructure
From: Grant Likely @ 2005-07-23 14:07 UTC (permalink / raw)
To: linuxppc-embedded
Patch to add support for SPI busses. SPI bus master drivers and SPI
slave drivers register with the SPI infrastructure.
Signed-off-by: Grant Likely <grant.likely@gdcanada.com>
diff -ruNp linux/drivers/Kconfig linux-spi/drivers/Kconfig
--- linux/drivers/Kconfig=092005-06-17 15:48:29.000000000 -0400
+++ linux-spi/drivers/Kconfig=092005-07-22 17:08:00.000000000 -0400
@@ -58,4 +58,6 @@ source "drivers/mmc/Kconfig"
=20
source "drivers/infiniband/Kconfig"
=20
+source "drivers/spi/Kconfig"
+
endmenu
diff -ruNp linux/drivers/Makefile linux-spi/drivers/Makefile
--- linux/drivers/Makefile=092005-06-17 15:48:29.000000000 -0400
+++ linux-spi/drivers/Makefile=092005-07-22 17:08:00.000000000 -0400
@@ -61,6 +61,7 @@ obj-$(CONFIG_EISA)=09=09+=3D eisa/
obj-$(CONFIG_CPU_FREQ)=09=09+=3D cpufreq/
obj-$(CONFIG_MMC)=09=09+=3D mmc/
obj-$(CONFIG_INFINIBAND)=09+=3D infiniband/
+obj-$(CONFIG_SPI)=09=09+=3D spi/
obj-$(CONFIG_BLK_DEV_SGIIOC4)=09+=3D sn/
obj-y=09=09=09=09+=3D firmware/
obj-$(CONFIG_CRYPTO)=09=09+=3D crypto/
diff -ruNp linux/drivers/spi/Kconfig linux-spi/drivers/spi/Kconfig
--- linux/drivers/spi/Kconfig=091969-12-31 19:00:00.000000000 -0500
+++ linux-spi/drivers/spi/Kconfig=092005-07-22 17:08:00.000000000 -0400
@@ -0,0 +1,27 @@
+#
+# SPI bus configuration
+#
+
+menu "SPI support"
+
+config SPI
+=09tristate "SPI support"
+=09---help---
+=09 SPI is a serial bus protocol for connecting between ICs. SPI
+=09 support requires two components; a bus driver plus drivers for
+=09 each SPI slave device.
+
+=09 Enabling SPI support will only cause SPI device drivers to be
compiled=09 into the kernel. You also need to make sure the platform
setup code
+=09 takes care of mapping SPI slaves onto each SPI bus.
+
+=09 Typically you will only need this for embedded systems.
+=09 Say N if unsure.
+
+comment "SPI Bus Drivers"
+=09depends on SPI
+
+comment "SPI slaves"
+=09depends on SPI
+
+endmenu
+
diff -ruNp linux/drivers/spi/Makefile linux-spi/drivers/spi/Makefile
--- linux/drivers/spi/Makefile=091969-12-31 19:00:00.000000000 -0500
+++ linux-spi/drivers/spi/Makefile=092005-07-22 17:08:00.000000000 -0400
@@ -0,0 +1,5 @@
+#
+# Makefile for the spi core.
+#
+
+obj-$(CONFIG_SPI)=09=09=09+=3D spi-core.o
diff -ruNp linux/drivers/spi/spi-core.c linux-spi/drivers/spi/spi-core.c
--- linux/drivers/spi/spi-core.c=091969-12-31 19:00:00.000000000 -0500
+++ linux-spi/drivers/spi/spi-core.c=092005-07-22 17:08:00.000000000 -0400
@@ -0,0 +1,156 @@
+/*
+ * drivers/spi/spi-core.c
+ *
+ * Core Serial Peripheral Interface (SPI) bus infrastructure
+ *
+ * Adds support for SPI busses. SPI master devices and SPI device drivers
+ * register with spi core. SPI slave devices get attached to SPI masters
+ * and are matched with an SPI device driver using the facillities of the
+ * linux device model.
+ *
+ * SPI is oriented around 'transfers' initiated by the SPI master. A tran=
sfer
+ * begins when the master asserts the 'slave select (SS)' signal. It then
+ * clocks out data one bit at a time on the "Master Out/Slave In (MOSI)" l=
ine
+ * and samples one bit at a time on the "Master In/Slave Out (MISO)" line.
+ * Clocking out and sampling are syncronized with the clock signal which i=
s
+ * driven by the master. Exactly the same amount of data is transmitted a=
s
+ * is received for each transfer. The transfer if finished when the maste=
r
+ * deasserts the SS signal.
+ *
+ * SPI bus drivers provide a 'transfer' method which accepts an input buff=
er,
+ * an output buffer and a count. Slave device drivers use the transfer me=
thod
+ * to communicate with the slave device. It is up to the slave device dri=
ver
+ * to decide what to do with the transfered data.
+ *
+ * This infrastructure is for SPI master devices only. Linux cannot be an
+ * SPI slave with this module. Theoretically multi-master busses will wor=
k
+ * as long as the other master doesn't try to use us as a slave
+ *
+ * Maintainer : Grant Likely <glikely@gmail.com>
+ *=20
+ * This file is in the public domain.
+ *
+ */
+=20
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/spi.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Grant Likely <glikely@gmail.com>");
+MODULE_DESCRIPTION("Freescale MPC52xx PSC in SPI mode");
+MODULE_LICENSE("Dual BSD/GPL");
+
+/* Simple enumeration scheme for assigning bus ids */
+static int spi_next_bus_id =3D 0;
+
+/* Forward Declarations */
+static int spi_match (struct device *dev, struct device_driver *drv);
+static void spi_bus_release(struct device *dev);
+
+/* Linux device model stuff (for registering a new bus type) */
+struct bus_type spi_bus_type =3D {
+=09.name =3D "spi",
+=09.match =3D spi_match,
+};
+
+static struct sysfs_ops spi_driver_sysfs_ops =3D {
+};
+
+static struct kobj_type spi_driver_kobj_type =3D {
+=09.sysfs_ops =3D &spi_driver_sysfs_ops,
+};
+
+/* Called by device model to match devices to device drivers
+ *
+ * Devices are matched to devices based on the name fields in the spi_dev
+ * and device_driver structures. If the names match, then the device and
+ * driver can possibly be connected
+ */
+static int spi_match (struct device *dev, struct device_driver *drv)
+{
+=09struct spi_device *spi_device =3D to_spi_dev(dev);
+=09return !strncmp(spi_device->name, drv->name, strlen(drv->name));
+}
+
+static void spi_bus_release(struct device *dev)
+{
+}
+
+/* Initialize common SPI bus fields before registering the bus device with=
the
+ * device model
+ */
+int spi_bus_register(struct spi_bus *spi)
+{
+=09snprintf(spi->dev.bus_id, BUS_ID_SIZE, "spi%i", spi_next_bus_id++);
+=09spi->dev.release =3D spi_bus_release;
+
+=09return device_register(&spi->dev);
+}
+
+void spi_bus_unregister(struct spi_bus *spi)
+{
+=09device_unregister(&spi->dev);
+}
+
+/* Helper function for attaching SPI slave devices to an SPI bus. Slave
+ * devices should get matched to slave device drivers
+ */
+int spi_device_create(struct spi_bus *bus, char *name, int id)
+{
+=09struct spi_device *dev;
+
+=09dev =3D kmalloc(sizeof (struct spi_device), GFP_KERNEL);
+=09if (!dev) {
+=09=09return -ENOMEM;
+=09}
+=09memset(dev, 0, sizeof(struct spi_device));
+
+=09strlcpy(dev->name, name, SPI_NAME_SIZE);
+=09dev->id =3D id;
+=09snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%s.%i", name, id);
+=09dev->dev.parent =3D &bus->dev;
+=09dev->dev.bus =3D &spi_bus_type;
+
+=09return device_register(&dev->dev);
+}
+
+/* Register an SPI slave device driver with the SPI infrastructure */
+int spi_driver_register(struct spi_driver *drv)
+{
+=09int err;
+
+=09drv->driver.name =3D drv->name;
+=09drv->driver.bus =3D &spi_bus_type;
+=09drv->driver.kobj.ktype =3D &spi_driver_kobj_type;
+=09err =3D driver_register(&drv->driver);
+=09return err;
+}
+
+void spi_driver_unregister(struct spi_driver *drv)
+{
+=09driver_unregister(&drv->driver);
+}
+
+static int __init spi_init(void)
+{
+=09printk(KERN_INFO "spi-core: initializing\n");
+
+=09return bus_register(&spi_bus_type);
+}
+
+static void __exit spi_exit(void)
+{
+=09bus_unregister(&spi_bus_type);
+}
+
+module_init(spi_init);
+module_exit(spi_exit);
+EXPORT_SYMBOL(spi_bus_register);
+EXPORT_SYMBOL(spi_bus_unregister);
+EXPORT_SYMBOL(spi_device_create);
+EXPORT_SYMBOL(spi_driver_register);
+EXPORT_SYMBOL(spi_driver_unregister);
+
diff -ruNp linux/include/linux/spi.h linux-spi/include/linux/spi.h
--- linux/include/linux/spi.h=091969-12-31 19:00:00.000000000 -0500
+++ linux-spi/include/linux/spi.h=092005-07-22 17:08:00.000000000 -0400
@@ -0,0 +1,72 @@
+/*
+ * drivers/spi/spi.h
+ *
+ * Core Serial Peripheral Interface (SPI) bus infrastructure
+ *
+ * Adds support for SPI busses. SPI master devices and SPI device drivers
+ * register with spi core. SPI slave devices get attached to SPI masters
+ * and are matched with an SPI device driver using the facillities of the
+ * linux device model.
+ *
+ * SPI is oriented around 'transfers' initiated by the SPI master. A tran=
sfer
+ * begins when the master asserts the 'slave select (SS)' signal. It then
+ * clocks out data one bit at a time on the "Master Out/Slave In (MOSI)" l=
ine
+ * and samples one bit at a time on the "Master In/Slave Out (MISO)" line.
+ * Clocking out and sampling are syncronized with the clock signal which i=
s
+ * driven by the master. Exactly the same amount of data is transmitted a=
s
+ * is received for each transfer. The transfer if finished when the maste=
r
+ * deasserts the SS signal.
+ *
+ * SPI bus drivers provide a 'transfer' method which accepts an input buff=
er,
+ * an output buffer and a count. Slave device drivers use the transfer me=
thod
+ * to communicate with the slave device. It is up to the slave device dri=
ver
+ * to decide what to do with the transfered data.
+ *
+ * This infrastructure is for SPI master devices only. Linux cannot be an
+ * SPI slave with this module. Multi-master SPI busses aren't supported
+ * either.
+ *
+ * Maintainer : Grant Likely <glikely@gmail.com>
+ *=20
+ * This file is in the public domain
+ */
+
+struct spi_bus {
+=09struct spi_bus_ops *ops;
+=09struct device dev;
+=09void *input_buff;
+=09int input_len;
+};
+#define to_spi_bus(d) container_of(d, struct spi_bus, dev);
+
+#define SPI_CLKEDGE_RISING=090
+#define SPI_CLKEDGE_FALLING=091
+struct spi_bus_ops {
+=09int (*transfer) (struct spi_bus *bus, int id, int clkedge,
+=09 uint8_t *in, const uint8_t *out, size_t count);
+};
+
+struct spi_driver {
+=09char *name;
+=09struct module *module;
+=09struct device_driver driver;
+};
+
+#define SPI_NAME_SIZE=09KOBJ_NAME_LEN
+struct spi_device {
+=09char name[SPI_NAME_SIZE];
+=09int id;
+=09struct spi_driver *driver;
+=09struct device dev;
+};
+#define to_spi_dev(d) container_of(d, struct spi_device, dev);
+
+
+extern int spi_device_create(struct spi_bus *bus, char *name, int id);
+
+/* Interface functions for registering and unregistering SPI busses */
+extern int spi_bus_register(struct spi_bus *bus);
+extern void spi_bus_unregister(struct spi_bus *bus);
+
+extern int spi_driver_register(struct spi_driver *driver);
+extern void spi_driver_unregister(struct spi_driver *driver);
^ permalink raw reply
* [PATCH 0/3] Support for SPI busses and devices
From: Grant Likely @ 2005-07-23 14:04 UTC (permalink / raw)
To: linuxppc-embedded
Request for Comment:
Here is a set of patches that adds SPI support to linux. I'm looking
for feedback on these and I'd like to eventually get the SPI
infrastructure into mainline
There are three patches. The first patch creates a new bus type
'spi'. The second adds support for using a PSC on the MPC52xx as an
SPI bus master. The third is a driver for an ks8995m managed switch
from Micrel.
Patch generated from 2.6.12
Cheers,
g.
^ permalink raw reply
* [PATCH RFC]: PHY Abstraction Layer III
From: Andy Fleming @ 2005-07-22 17:07 UTC (permalink / raw)
To: netdev; +Cc: Embedded PPC Linux list
[-- Attachment #1: Type: text/plain, Size: 666 bytes --]
Here's the latest version of the patch, done against a cogito
linux-2.6 branch. It has a handful of small changes, which I hope
will speed acceptance:
* Interrupts are no longer requested at interrupt time
* There is a function to print out PHY status
* The PHY drivers' probe function now serves only to initialize
driver state
* A new config_init functor has been created to configure PHY state
just after reset (so that the PHYs will be returned to a sane state
if they don't start up that way)
* Stephen Hemminger's patch to allow better module support in the
drivers has been included
Andy Fleming
Open Source Team
Freescale Semiconductor, Inc
[-- Attachment #2: phy_abstraction.phy.20050721.l26.patch --]
[-- Type: application/octet-stream, Size: 92660 bytes --]
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -131,6 +131,8 @@ if NETDEVICES
source "drivers/net/arcnet/Kconfig"
endif
+source "drivers/net/phy/Kconfig"
+
#
# Ethernet
#
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_ADAPTEC_STARFIRE) += starfi
#
obj-$(CONFIG_MII) += mii.o
+obj-$(CONFIG_PHYLIB) += phy/
obj-$(CONFIG_SUNDANCE) += sundance.o
obj-$(CONFIG_HAMACHI) += hamachi.o
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/Kconfig
@@ -0,0 +1,57 @@
+#
+# PHY Layer Configuration
+#
+
+menu "PHY device support"
+
+config PHYLIB
+ bool "PHY Device support and infrastructure"
+ depends on NET_ETHERNET
+ help
+ Ethernet controllers are usually attached to PHY
+ devices. This option provides infrastructure for
+ managing PHY devices.
+
+config PHYCONTROL
+ bool "Support for automatically handling PHY state changes"
+ depends on PHYLIB
+ help
+ Adds code to perform all the work for keeping PHY link
+ state (speed/duplex/etc) up-to-date. Also handles
+ interrupts.
+
+comment "MII PHY device drivers"
+ depends on PHYLIB
+
+config MARVELL_PHY
+ bool "Drivers for Marvell PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently has a driver for the 88E1011S
+
+config DAVICOM_PHY
+ bool "Drivers for Davicom PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently supports dm9161e and dm9131
+
+config QSEMI_PHY
+ bool "Drivers for Quality Semiconductor PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently supports the qs6612
+
+config LXT_PHY
+ bool "Drivers for the Intel LXT PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently supports the lxt970, lxt971
+
+config CICADA_PHY
+ bool "Drivers for the Cicada PHYs"
+ depends on PHYLIB
+ ---help---
+ Currently supports the cis8204
+
+endmenu
+
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/Makefile
@@ -0,0 +1,9 @@
+# Makefile for Linux PHY drivers
+
+obj-$(CONFIG_PHYLIB) += phy.o phy_device.o mdio_bus.o
+
+obj-$(CONFIG_MARVELL_PHY) += marvell.o
+obj-$(CONFIG_DAVICOM_PHY) += davicom.o
+obj-$(CONFIG_CICADA_PHY) += cicada.o
+obj-$(CONFIG_LXT_PHY) += lxt.o
+obj-$(CONFIG_QSEMI_PHY) += qsemi.o
diff --git a/drivers/net/phy/cicada.c b/drivers/net/phy/cicada.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/cicada.c
@@ -0,0 +1,134 @@
+/*
+ * drivers/net/phy/cicada.c
+ *
+ * Driver for Cicada PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 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/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.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/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* Cicada Extended Control Register 1 */
+#define MII_CIS8201_EXT_CON1 0x17
+#define MII_CIS8201_EXTCON1_INIT 0x0000
+
+/* Cicada Interrupt Mask Register */
+#define MII_CIS8201_IMASK 0x19
+#define MII_CIS8201_IMASK_IEN 0x8000
+#define MII_CIS8201_IMASK_SPEED 0x4000
+#define MII_CIS8201_IMASK_LINK 0x2000
+#define MII_CIS8201_IMASK_DUPLEX 0x1000
+#define MII_CIS8201_IMASK_MASK 0xf000
+
+/* Cicada Interrupt Status Register */
+#define MII_CIS8201_ISTAT 0x1a
+#define MII_CIS8201_ISTAT_STATUS 0x8000
+#define MII_CIS8201_ISTAT_SPEED 0x4000
+#define MII_CIS8201_ISTAT_LINK 0x2000
+#define MII_CIS8201_ISTAT_DUPLEX 0x1000
+
+/* Cicada Auxiliary Control/Status Register */
+#define MII_CIS8201_AUX_CONSTAT 0x1c
+#define MII_CIS8201_AUXCONSTAT_INIT 0x0004
+#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
+#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
+#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
+#define MII_CIS8201_AUXCONSTAT_100 0x0008
+
+MODULE_DESCRIPTION("Cicadia PHY driver");
+MODULE_AUTHOR("Andy Fleming");
+MODULE_LICENSE("GPL");
+
+static int cis820x_config_init(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_write(phydev, MII_CIS8201_AUX_CONSTAT,
+ MII_CIS8201_AUXCONSTAT_INIT);
+
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, MII_CIS8201_EXT_CON1,
+ MII_CIS8201_EXTCON1_INIT);
+
+ return err;
+}
+
+static int cis820x_ack_interrupt(struct phy_device *phydev)
+{
+ int err = phy_read(phydev, MII_CIS8201_ISTAT);
+
+ return (err < 0) ? err : 0;
+}
+
+static int cis820x_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_CIS8201_IMASK,
+ MII_CIS8201_IMASK_MASK);
+ else
+ err = phy_write(phydev, MII_CIS8201_IMASK, 0);
+
+ return err;
+}
+
+/* Cicada 820x */
+static struct phy_driver cis8204_driver = {
+ .phy_id = 0x000fc440,
+ .name = "Cicada Cis8204",
+ .phy_id_mask = 0x000fffc0,
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_init = &cis820x_config_init,
+ .config_aneg = &genphy_config_aneg,
+ .read_status = &genphy_read_status,
+ .ack_interrupt = &cis820x_ack_interrupt,
+ .config_intr = &cis820x_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static int __init cis8204_init(void)
+{
+ return phy_driver_register(&cis8204_driver);
+}
+
+static void __exit cis8204_exit(void)
+{
+ phy_driver_unregister(&cis8204_driver);
+}
+
+module_init(cis8204_init);
+module_exit(cis8204_exit);
diff --git a/drivers/net/phy/davicom.c b/drivers/net/phy/davicom.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/davicom.c
@@ -0,0 +1,195 @@
+/*
+ * drivers/net/phy/davicom.c
+ *
+ * Driver for Davicom PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 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/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.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/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#define MII_DM9161_SCR 0x10
+#define MII_DM9161_SCR_INIT 0x0610
+
+/* DM9161 Interrupt Register */
+#define MII_DM9161_INTR 0x15
+#define MII_DM9161_INTR_PEND 0x8000
+#define MII_DM9161_INTR_DPLX_MASK 0x0800
+#define MII_DM9161_INTR_SPD_MASK 0x0400
+#define MII_DM9161_INTR_LINK_MASK 0x0200
+#define MII_DM9161_INTR_MASK 0x0100
+#define MII_DM9161_INTR_DPLX_CHANGE 0x0010
+#define MII_DM9161_INTR_SPD_CHANGE 0x0008
+#define MII_DM9161_INTR_LINK_CHANGE 0x0004
+#define MII_DM9161_INTR_INIT 0x0000
+#define MII_DM9161_INTR_STOP \
+(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \
+ | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
+
+/* DM9161 10BT Configuration/Status */
+#define MII_DM9161_10BTCSR 0x12
+#define MII_DM9161_10BTCSR_INIT 0x7800
+
+MODULE_DESCRIPTION("Davicom PHY driver");
+MODULE_AUTHOR("Andy Fleming");
+MODULE_LICENSE("GPL");
+
+
+#define DM9161_DELAY 1
+static int dm9161_config_intr(struct phy_device *phydev)
+{
+ int temp;
+
+ temp = phy_read(phydev, MII_DM9161_INTR);
+
+ if (temp < 0)
+ return temp;
+
+ if(PHY_INTERRUPT_ENABLED == phydev->interrupts )
+ temp &= ~(MII_DM9161_INTR_STOP);
+ else
+ temp |= MII_DM9161_INTR_STOP;
+
+ temp = phy_write(phydev, MII_DM9161_INTR, temp);
+
+ return temp;
+}
+
+static int dm9161_config_aneg(struct phy_device *phydev)
+{
+ int err;
+
+ /* Isolate the PHY */
+ err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
+
+ if (err < 0)
+ return err;
+
+ /* Configure the new settings */
+ err = genphy_config_aneg(phydev);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int dm9161_config_init(struct phy_device *phydev)
+{
+ int err;
+
+ /* Isolate the PHY */
+ err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
+
+ if (err < 0)
+ return err;
+
+ /* Do not bypass the scrambler/descrambler */
+ err = phy_write(phydev, MII_DM9161_SCR, MII_DM9161_SCR_INIT);
+
+ if (err < 0)
+ return err;
+
+ /* Clear 10BTCSR to default */
+ err = phy_write(phydev, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
+
+ if (err < 0)
+ return err;
+
+ /* Reconnect the PHY, and enable Autonegotiation */
+ err = phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int dm9161_ack_interrupt(struct phy_device *phydev)
+{
+ int err = phy_read(phydev, MII_DM9161_INTR);
+
+ return (err < 0) ? err : 0;
+}
+
+static struct phy_driver dm9161_driver = {
+ .phy_id = 0x0181b880,
+ .name = "Davicom DM9161E",
+ .phy_id_mask = 0x0ffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config_init = dm9161_config_init,
+ .config_aneg = dm9161_config_aneg,
+ .read_status = genphy_read_status,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static struct phy_driver dm9131_driver = {
+ .phy_id = 0x00181b80,
+ .name = "Davicom DM9131",
+ .phy_id_mask = 0x0ffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = dm9161_ack_interrupt,
+ .config_intr = dm9161_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static int __init davicom_init(void)
+{
+ int ret;
+
+ ret = phy_driver_register(&dm9161_driver);
+ if (ret)
+ goto err1;
+
+ ret = phy_driver_register(&dm9131_driver);
+ if (ret)
+ goto err2;
+ return 0;
+
+ err2:
+ phy_driver_unregister(&dm9161_driver);
+ err1:
+ return ret;
+}
+
+static void __exit davicom_exit(void)
+{
+ phy_driver_unregister(&dm9161_driver);
+ phy_driver_unregister(&dm9131_driver);
+}
+
+module_init(davicom_init);
+module_exit(davicom_exit);
diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/lxt.c
@@ -0,0 +1,179 @@
+/*
+ * drivers/net/phy/lxt.c
+ *
+ * Driver for Intel LXT PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 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/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.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/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* The Level one LXT970 is used by many boards */
+
+#define MII_LXT970_IER 17 /* Interrupt Enable Register */
+
+#define MII_LXT970_IER_IEN 0x0002
+
+#define MII_LXT970_ISR 18 /* Interrupt Status Register */
+
+#define MII_LXT970_CONFIG 19 /* Configuration Register */
+
+/* ------------------------------------------------------------------------- */
+/* The Level one LXT971 is used on some of my custom boards */
+
+/* register definitions for the 971 */
+#define MII_LXT971_IER 18 /* Interrupt Enable Register */
+#define MII_LXT971_IER_IEN 0x00f2
+
+#define MII_LXT971_ISR 19 /* Interrupt Status Register */
+
+
+MODULE_DESCRIPTION("Intel LXT PHY driver");
+MODULE_AUTHOR("Andy Fleming");
+MODULE_LICENSE("GPL");
+
+static int lxt970_ack_interrupt(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_read(phydev, MII_BMSR);
+
+ if (err < 0)
+ return err;
+
+ err = phy_read(phydev, MII_LXT970_ISR);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int lxt970_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_LXT970_IER, MII_LXT970_IER_IEN);
+ else
+ err = phy_write(phydev, MII_LXT970_IER, 0);
+
+ return err;
+}
+
+static int lxt970_config_init(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_write(phydev, MII_LXT970_CONFIG, 0);
+
+ return err;
+}
+
+
+static int lxt971_ack_interrupt(struct phy_device *phydev)
+{
+ int err = phy_read(phydev, MII_LXT971_ISR);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int lxt971_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_LXT971_IER, MII_LXT971_IER_IEN);
+ else
+ err = phy_write(phydev, MII_LXT971_IER, 0);
+
+ return err;
+}
+
+static struct phy_driver lxt970_driver = {
+ .phy_id = 0x07810000,
+ .name = "LXT970",
+ .phy_id_mask = 0x0fffffff,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_init = lxt970_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = lxt970_ack_interrupt,
+ .config_intr = lxt970_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static struct phy_driver lxt971_driver = {
+ .phy_id = 0x0001378e,
+ .name = "LXT971",
+ .phy_id_mask = 0x0fffffff,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = lxt971_ack_interrupt,
+ .config_intr = lxt971_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static int __init lxt_init(void)
+{
+ int ret;
+
+ ret = phy_driver_register(&lxt970_driver);
+ if (ret)
+ goto err1;
+
+ ret = phy_driver_register(&lxt971_driver);
+ if (ret)
+ goto err2;
+ return 0;
+
+ err2:
+ phy_driver_unregister(&lxt970_driver);
+ err1:
+ return ret;
+}
+
+static void __exit lxt_exit(void)
+{
+ phy_driver_unregister(&lxt970_driver);
+ phy_driver_unregister(&lxt971_driver);
+}
+
+module_init(lxt_init);
+module_exit(lxt_exit);
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/marvell.c
@@ -0,0 +1,140 @@
+/*
+ * drivers/net/phy/marvell.c
+ *
+ * Driver for Marvell PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 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/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.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/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#define MII_M1011_IEVENT 0x13
+#define MII_M1011_IEVENT_CLEAR 0x0000
+
+#define MII_M1011_IMASK 0x12
+#define MII_M1011_IMASK_INIT 0x6400
+#define MII_M1011_IMASK_CLEAR 0x0000
+
+MODULE_DESCRIPTION("Marvell PHY driver");
+MODULE_AUTHOR("Andy Fleming");
+MODULE_LICENSE("GPL");
+
+static int marvell_ack_interrupt(struct phy_device *phydev)
+{
+ int err;
+
+ /* Clear the interrupts by reading the reg */
+ err = phy_read(phydev, MII_M1011_IEVENT);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int marvell_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if(phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
+ else
+ err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
+
+ return err;
+}
+
+static int marvell_config_aneg(struct phy_device *phydev)
+{
+ int err;
+
+ /* The Marvell PHY has an errata which requires
+ * that certain registers get written in order
+ * to restart autonegotiation */
+ err = phy_write(phydev, MII_BMCR, BMCR_RESET);
+
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1d, 0x1f);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1e, 0x200c);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1d, 0x5);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1e, 0);
+ if (err < 0)
+ return err;
+
+ err = phy_write(phydev, 0x1e, 0x100);
+ if (err < 0)
+ return err;
+
+
+ err = genphy_config_aneg(phydev);
+
+ return err;
+}
+
+
+static struct phy_driver m88e1101_driver = {
+ .phy_id = 0x01410c00,
+ .phy_id_mask = 0xffffff00,
+ .name = "Marvell 88E1101",
+ .features = PHY_GBIT_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_aneg = &marvell_config_aneg,
+ .read_status = &genphy_read_status,
+ .ack_interrupt = &marvell_ack_interrupt,
+ .config_intr = &marvell_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static int __init marvell_init(void)
+{
+ return phy_driver_register(&m88e1101_driver);
+}
+
+static void __exit marvell_exit(void)
+{
+ phy_driver_unregister(&m88e1101_driver);
+}
+
+module_init(marvell_init);
+module_exit(marvell_exit);
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/mdio_bus.c
@@ -0,0 +1,175 @@
+/*
+ * drivers/net/phy/mdio_bus.c
+ *
+ * MDIO Bus interface
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 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/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.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/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* register_mdiobus
+ *
+ * description: Called by a bus driver to bring up all the PHYs
+ * on a given bus, and attach them to the bus
+ */
+int mdiobus_register(struct mii_bus *bus)
+{
+ int i;
+ int err = 0;
+
+ spin_lock_init(&bus->mdio_lock);
+
+ if (NULL == bus || NULL == bus->name ||
+ NULL == bus->read ||
+ NULL == bus->write)
+ return -EINVAL;
+
+ if (bus->reset)
+ bus->reset(bus);
+
+ for (i=0; i < PHY_MAX_ADDR; i++) {
+ struct phy_device *phydev;
+
+ phydev = get_phy_device(bus, i);
+
+ /* There's a PHY at this address
+ * We need to set:
+ * 1) IRQ
+ * 2) bus_id
+ * 3) parent
+ * 4) bus
+ * 5) mii_bus
+ * And, we need to register it */
+ if (phydev) {
+ phydev->irq = bus->irq[i];
+
+ phydev->dev.parent = bus->dev;
+
+ phydev->dev.bus = &mdio_bus_type;
+
+ phydev->bus = bus;
+
+ sprintf(phydev->dev.bus_id, "phy%d:%d", bus->id, i);
+
+ err = device_register(&phydev->dev);
+
+ if (err)
+ printk("phy %d did not register (%d)\n",
+ i, err);
+
+ /* If get_phy_device returned NULL, it may be
+ * because an error occurred. If so, we return
+ * that error */
+ } else if (errno)
+ return errno;
+
+ bus->phy_map[i] = phydev;
+ }
+
+ pr_info("%s: probed\n", bus->name);
+
+ return err;
+}
+EXPORT_SYMBOL(mdiobus_register);
+
+void mdiobus_unregister(struct mii_bus *bus)
+{
+ int i;
+
+ for (i=0; i < PHY_MAX_ADDR; i++)
+ if (bus->phy_map[i]) {
+ device_unregister(&bus->phy_map[i]->dev);
+ kfree(bus->phy_map[i]);
+ }
+
+}
+EXPORT_SYMBOL(mdiobus_unregister);
+
+/* mdio_bus_match
+ *
+ * description: Given a PHY device, and a PHY driver, return 1 if
+ * the driver supports the device. Otherwise, return 0
+ */
+static int mdio_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct phy_device *phydev = to_phy_device(dev);
+ struct phy_driver *phydrv = to_phy_driver(drv);
+
+ return (phydrv->phy_id == (phydev->phy_id & phydrv->phy_id_mask));
+}
+
+/* Suspend and resume. Copied from platform_suspend and
+ * platform_resume
+ */
+static int mdio_bus_suspend(struct device * dev, u32 state)
+{
+ int ret = 0;
+
+ if (dev->driver && dev->driver->suspend) {
+ ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE);
+ if (ret == 0)
+ ret = dev->driver->suspend(dev, state, SUSPEND_SAVE_STATE);
+ if (ret == 0)
+ ret = dev->driver->suspend(dev, state, SUSPEND_POWER_DOWN);
+ }
+ return ret;
+}
+
+static int mdio_bus_resume(struct device * dev)
+{
+ int ret = 0;
+
+ if (dev->driver && dev->driver->resume) {
+ ret = dev->driver->resume(dev, RESUME_POWER_ON);
+ if (ret == 0)
+ ret = dev->driver->resume(dev, RESUME_RESTORE_STATE);
+ if (ret == 0)
+ ret = dev->driver->resume(dev, RESUME_ENABLE);
+ }
+ return ret;
+}
+
+struct bus_type mdio_bus_type = {
+ .name = "mdio_bus",
+ .match = mdio_bus_match,
+ .suspend= mdio_bus_suspend,
+ .resume = mdio_bus_resume,
+};
+
+static int __init mdio_bus_init(void)
+{
+ return bus_register(&mdio_bus_type);
+}
+
+subsys_initcall(mdio_bus_init);
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/phy.c
@@ -0,0 +1,852 @@
+/*
+ * drivers/net/phy/phy.c
+ *
+ * Framework for configuring and reading PHY devices
+ * Based on code in sungem_phy.c and gianfar_phy.c
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 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/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.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/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+int phy_read(struct phy_device *phydev, u16 regnum);
+int phy_write(struct phy_device *phydev, u16 regnum, u16 val);
+void phy_change(void *data);
+void phy_timer(unsigned long data);
+
+/* Convenience function to print out the current phy status
+ */
+void phy_print_status(struct phy_device *phydev)
+{
+ pr_info("%s: Link is %s", phydev->dev.bus_id,
+ phydev->link ? "Up" : "Down");
+ if (phydev->link)
+ printk(" - %d/%s", phydev->speed,
+ DUPLEX_FULL == phydev->duplex ?
+ "Full" : "Half");
+
+ printk("\n");
+}
+EXPORT_SYMBOL(phy_print_status);
+
+
+/* Convenience functions for reading/writing a given PHY
+ * register. They MUST NOT be called from interrupt context,
+ * because the bus read/write functions may wait for an interrupt
+ * to conclude the operation. */
+int phy_read(struct phy_device *phydev, u16 regnum)
+{
+ int retval;
+ struct mii_bus *bus = phydev->bus;
+
+ spin_lock_bh(&bus->mdio_lock);
+ retval = bus->read(bus, phydev->addr, regnum);
+ spin_unlock_bh(&bus->mdio_lock);
+
+ return retval;
+}
+EXPORT_SYMBOL(phy_read);
+
+int phy_write(struct phy_device *phydev, u16 regnum, u16 val)
+{
+ int err;
+ struct mii_bus *bus = phydev->bus;
+
+ spin_lock_bh(&bus->mdio_lock);
+ err = bus->write(bus, phydev->addr, regnum, val);
+ spin_unlock_bh(&bus->mdio_lock);
+
+ return err;
+}
+EXPORT_SYMBOL(phy_write);
+
+
+int phy_clear_interrupt(struct phy_device *phydev)
+{
+ int err = 0;
+
+ if (phydev->drv->ack_interrupt)
+ err = phydev->drv->ack_interrupt(phydev);
+
+ return err;
+}
+
+
+int phy_config_interrupt(struct phy_device *phydev, u32 interrupts)
+{
+ int err = 0;
+
+ phydev->interrupts = interrupts;
+ if (phydev->drv->config_intr)
+ err = phydev->drv->config_intr(phydev);
+
+ return err;
+}
+
+
+/* phy_aneg_done
+ *
+ * description: Reads the status register and returns 0 either if
+ * auto-negotiation is incomplete, or if there was an error.
+ * Returns BMSR_ANEGCOMPLETE if auto-negotiation is done.
+ */
+static inline int phy_aneg_done(struct phy_device *phydev)
+{
+ int retval;
+
+ retval = phy_read(phydev, MII_BMSR);
+
+ if (retval < 0)
+ return retval;
+
+ return retval & BMSR_ANEGCOMPLETE;
+}
+
+/* phy_start_aneg
+ *
+ * description: Calls the PHY driver's config_aneg, and then
+ * sets the PHY state to PHY_AN if auto-negotiation is enabled,
+ * and to PHY_FORCING if auto-negotiation is disabled. Unless
+ * the PHY is currently HALTED.
+ */
+int phy_start_aneg(struct phy_device *phydev)
+{
+ int err = 0;
+
+ spin_lock(&phydev->lock);
+
+ if (AUTONEG_DISABLE == phydev->autoneg)
+ phy_sanitize_settings(phydev);
+
+ err = phydev->drv->config_aneg(phydev);
+
+ if (err < 0)
+ return err;
+
+ if (phydev->state != PHY_HALTED) {
+ if (AUTONEG_ENABLE == phydev->autoneg) {
+ phydev->state = PHY_AN;
+ phydev->link_timeout = PHY_AN_TIMEOUT;
+ } else {
+ phydev->state = PHY_FORCING;
+ phydev->link_timeout = PHY_FORCE_TIMEOUT;
+ }
+ }
+
+ spin_unlock(&phydev->lock);
+
+ return err;
+}
+EXPORT_SYMBOL(phy_start_aneg);
+
+
+/* A structure for mapping a particular speed and duplex
+ * combination to a particular SUPPORTED and ADVERTISED value */
+struct phy_setting {
+ int speed;
+ int duplex;
+ u32 setting;
+};
+
+/* A mapping of all SUPPORTED settings to speed/duplex */
+static struct phy_setting settings[] = {
+ { .speed = 10000, .duplex = DUPLEX_FULL,
+ .setting = SUPPORTED_10000baseT_Full,
+ },
+ { .speed = SPEED_1000, .duplex = DUPLEX_FULL,
+ .setting = SUPPORTED_1000baseT_Full,
+ },
+ { .speed = SPEED_1000, .duplex = DUPLEX_HALF,
+ .setting = SUPPORTED_1000baseT_Half,
+ },
+ { .speed = SPEED_100, .duplex = DUPLEX_FULL,
+ .setting = SUPPORTED_100baseT_Full,
+ },
+ { .speed = SPEED_100, .duplex = DUPLEX_HALF,
+ .setting = SUPPORTED_100baseT_Half,
+ },
+ { .speed = SPEED_10, .duplex = DUPLEX_FULL,
+ .setting = SUPPORTED_10baseT_Full,
+ },
+ { .speed = SPEED_10, .duplex = DUPLEX_HALF,
+ .setting = SUPPORTED_10baseT_Half,
+ },
+};
+
+#define MAX_NUM_SETTINGS (sizeof(settings)/sizeof(struct phy_setting))
+
+/* phy_find_setting
+ *
+ * description: Searches the settings array for the setting which
+ * matches the desired speed and duplex, and returns the index
+ * of that setting. Returns the index of the last setting if
+ * none of the others match.
+ */
+static inline int phy_find_setting(int speed, int duplex)
+{
+ int idx = 0;
+
+ while (idx < MAX_NUM_SETTINGS &&
+ (settings[idx].speed != speed ||
+ settings[idx].duplex != duplex))
+ idx++;
+
+ return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
+}
+
+/* phy_find_valid
+ * idx: The first index in settings[] to search
+ * features: A mask of the valid settings
+ *
+ * description: Returns the index of the first valid setting less
+ * than or equal to the one pointed to by idx, as determined by
+ * the mask in features. Returns the index of the last setting
+ * if nothing else matches.
+ */
+static inline int phy_find_valid(int idx, u32 features)
+{
+ while (idx < MAX_NUM_SETTINGS && !(settings[idx].setting & features))
+ idx++;
+
+ return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
+}
+
+/* phy_sanitize_settings
+ *
+ * description: Make sure the PHY is set to supported speeds and
+ * duplexes. Drop down by one in this order: 1000/FULL,
+ * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF
+ */
+void phy_sanitize_settings(struct phy_device *phydev)
+{
+ u32 features = phydev->supported;
+ int idx;
+
+ /* Sanitize settings based on PHY capabilities */
+ if ((features & SUPPORTED_Autoneg) == 0)
+ phydev->autoneg = 0;
+
+ idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex),
+ features);
+
+ phydev->speed = settings[idx].speed;
+ phydev->duplex = settings[idx].duplex;
+}
+EXPORT_SYMBOL(phy_sanitize_settings);
+
+/* phy_force_reduction
+ *
+ * description: Reduces the speed/duplex settings by
+ * one notch. The order is so:
+ * 1000/FULL, 1000/HALF, 100/FULL, 100/HALF,
+ * 10/FULL, 10/HALF. The function bottoms out at 10/HALF.
+ */
+static void phy_force_reduction(struct phy_device *phydev)
+{
+ int idx;
+
+ idx = phy_find_setting(phydev->speed, phydev->duplex);
+
+ idx++;
+
+ idx = phy_find_valid(idx, phydev->supported);
+
+ phydev->speed = settings[idx].speed;
+ phydev->duplex = settings[idx].duplex;
+
+ pr_info("Trying %d/%s\n", phydev->speed,
+ DUPLEX_FULL == phydev->duplex ?
+ "FULL" : "HALF");
+}
+
+/* phy_ethtool_sset:
+ * A generic ethtool sset function. Handles all the details
+ *
+ * A few notes about parameter checking:
+ * - We don't set port or transceiver, so we don't care what they
+ * were set to.
+ * - phy_start_aneg() will make sure forced settings are sane, and
+ * choose the next best ones from the ones selected, so we don't
+ * care if ethtool tries to give us bad values
+ */
+int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd)
+{
+ if (cmd->phy_address != phydev->addr)
+ return -EINVAL;
+
+ /* We make sure that we don't pass unsupported
+ * values in to the PHY */
+ cmd->advertising &= phydev->supported;
+
+ /* Verify the settings we care about. */
+ if (cmd->autoneg != AUTONEG_ENABLE && cmd->autoneg != AUTONEG_DISABLE)
+ return -EINVAL;
+
+ if (cmd->autoneg == AUTONEG_ENABLE && cmd->advertising == 0)
+ return -EINVAL;
+
+ if (cmd->autoneg == AUTONEG_DISABLE
+ && ((cmd->speed != SPEED_1000
+ && cmd->speed != SPEED_100
+ && cmd->speed != SPEED_10)
+ || (cmd->duplex != DUPLEX_HALF
+ && cmd->duplex != DUPLEX_FULL)))
+ return -EINVAL;
+
+ phydev->autoneg = cmd->autoneg;
+
+ phydev->speed = cmd->speed;
+
+ phydev->advertising = cmd->advertising;
+
+ if (AUTONEG_ENABLE == cmd->autoneg)
+ phydev->advertising |= ADVERTISED_Autoneg;
+ else
+ phydev->advertising &= ~ADVERTISED_Autoneg;
+
+ phydev->duplex = cmd->duplex;
+
+ /* Restart the PHY */
+ phy_start_aneg(phydev);
+
+ return 0;
+}
+
+int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd)
+{
+ cmd->supported = phydev->supported;
+
+ cmd->advertising = phydev->advertising;
+
+ cmd->speed = phydev->speed;
+ cmd->duplex = phydev->duplex;
+ cmd->port = PORT_MII;
+ cmd->phy_address = phydev->addr;
+ cmd->transceiver = XCVR_EXTERNAL;
+ cmd->autoneg = phydev->autoneg;
+
+ return 0;
+}
+
+
+/* Note that this function is currently incompatible with the
+ * PHYCONTROL layer. It changes registers without regard to
+ * current state. Use at own risk
+ */
+int phy_mii_ioctl(struct phy_device *phydev,
+ struct mii_ioctl_data *mii_data, int cmd)
+{
+ u16 val = mii_data->val_in;
+
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ mii_data->phy_id = phydev->addr;
+ break;
+ case SIOCGMIIREG:
+ mii_data->val_out = phy_read(phydev, mii_data->reg_num);
+ break;
+
+ case SIOCSMIIREG:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (mii_data->phy_id == phydev->addr) {
+ switch(mii_data->reg_num) {
+ case MII_BMCR:
+ if (val & (BMCR_RESET|BMCR_ANENABLE))
+ phydev->autoneg = AUTONEG_DISABLE;
+ else
+ phydev->autoneg = AUTONEG_ENABLE;
+ if ((!phydev->autoneg) && (val & BMCR_FULLDPLX))
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+ break;
+ case MII_ADVERTISE:
+ phydev->advertising = val;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+
+ phy_write(phydev, mii_data->reg_num, val);
+
+ if (mii_data->reg_num == MII_BMCR
+ && val & BMCR_RESET
+ && phydev->drv->config_init)
+ phydev->drv->config_init(phydev);
+ break;
+ }
+
+ return 0;
+}
+
+/* phy_start_machine:
+ *
+ * description: The PHY infrastructure can run a state machine
+ * which tracks whether the PHY is starting up, negotiating,
+ * etc. This function starts the timer which tracks the state
+ * of the PHY. If you want to be notified when the state
+ * changes, pass in the callback, otherwise, pass NULL. If you
+ * want to maintain your own state machine, do not call this
+ * function. */
+void phy_start_machine(struct phy_device *phydev,
+ void (*handler)(struct net_device *))
+{
+ phydev->adjust_state = handler;
+
+ init_timer(&phydev->phy_timer);
+ phydev->phy_timer.function = &phy_timer;
+ phydev->phy_timer.data = (unsigned long) phydev;
+ mod_timer(&phydev->phy_timer, jiffies + HZ);
+}
+
+/* phy_stop_machine
+ *
+ * description: Stops the state machine timer, sets the state to
+ * UP (unless it wasn't up yet), and then frees the interrupt,
+ * if it is in use. This function must be called BEFORE
+ * phy_detach.
+ */
+void phy_stop_machine(struct phy_device *phydev)
+{
+ del_timer_sync(&phydev->phy_timer);
+
+ spin_lock(&phydev->lock);
+ if (phydev->state > PHY_UP)
+ phydev->state = PHY_UP;
+ spin_unlock(&phydev->lock);
+
+ if (phydev->irq != PHY_POLL)
+ phy_stop_interrupts(phydev);
+
+ phydev->adjust_state = NULL;
+}
+
+#ifdef CONFIG_PHYCONTROL
+/* phy_error:
+ *
+ * Moves the PHY to the HALTED state in response to a read
+ * or write error, and tells the controller the link is down.
+ * Must not be called from interrupt context, or while the
+ * phydev->lock is held.
+ */
+void phy_error(struct phy_device *phydev)
+{
+ spin_lock(&phydev->lock);
+ phydev->state = PHY_HALTED;
+ spin_unlock(&phydev->lock);
+}
+
+/* phy_interrupt
+ *
+ * description: When a PHY interrupt occurs, the handler disables
+ * interrupts, and schedules a work task to clear the interrupt.
+ */
+static irqreturn_t phy_interrupt(int irq, void *phy_dat, struct pt_regs *regs)
+{
+ struct phy_device *phydev = phy_dat;
+
+ /* The MDIO bus is not allowed to be written in interrupt
+ * context, so we need to disable the irq here. A work
+ * queue will write the PHY to disable and clear the
+ * interrupt, and then reenable the irq line. */
+ disable_irq_nosync(irq);
+
+ schedule_work(&phydev->phy_queue);
+
+ return IRQ_HANDLED;
+}
+
+/* Enable the interrupts from the PHY side */
+int phy_enable_interrupts(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_clear_interrupt(phydev);
+
+ if (err < 0)
+ return err;
+
+ err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED);
+
+ return err;
+}
+
+/* Disable the PHY interrupts from the PHY side */
+int phy_disable_interrupts(struct phy_device *phydev)
+{
+ int err;
+
+ /* Disable PHY interrupts */
+ err = phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
+
+ if (err)
+ goto phy_err;
+
+ /* Clear the interrupt */
+ err = phy_clear_interrupt(phydev);
+
+ if (err)
+ goto phy_err;
+
+ return 0;
+
+phy_err:
+ phy_error(phydev);
+
+ return err;
+}
+
+/* phy_start_interrupts
+ *
+ * description: Request the interrupt for the given PHY. If
+ * this fails, then we set irq to PHY_POLL.
+ * Otherwise, we enable the interrupts in the PHY.
+ * Returns 0 on success.
+ * This should only be called with a valid IRQ number.
+ */
+int phy_start_interrupts(struct phy_device *phydev)
+{
+ int err = 0;
+
+ INIT_WORK(&phydev->phy_queue, phy_change, phydev);
+
+ if (request_irq(phydev->irq, phy_interrupt,
+ SA_SHIRQ,
+ "phy_interrupt",
+ phydev) < 0) {
+ printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
+ phydev->bus->name,
+ phydev->irq);
+ phydev->irq = PHY_POLL;
+ return 0;
+ }
+
+ err = phy_enable_interrupts(phydev);
+
+ return err;
+}
+EXPORT_SYMBOL(phy_start_interrupts);
+
+int phy_stop_interrupts(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_disable_interrupts(phydev);
+
+ if (err)
+ phy_error(phydev);
+
+ free_irq(phydev->irq, phydev);
+
+ return err;
+}
+EXPORT_SYMBOL(phy_stop_interrupts);
+
+
+/* Scheduled by the phy_interrupt/timer to handle PHY changes */
+void phy_change(void *data)
+{
+ int err;
+ struct phy_device *phydev = data;
+
+ err = phy_disable_interrupts(phydev);
+
+ if (err)
+ goto phy_err;
+
+ spin_lock(&phydev->lock);
+ if ((PHY_RUNNING == phydev->state) || (PHY_NOLINK == phydev->state))
+ phydev->state = PHY_CHANGELINK;
+ spin_unlock(&phydev->lock);
+
+ enable_irq(phydev->irq);
+
+ /* Reenable interrupts */
+ err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED);
+
+ if (err)
+ goto irq_enable_err;
+
+ return;
+
+irq_enable_err:
+ disable_irq(phydev->irq);
+phy_err:
+ phy_error(phydev);
+}
+
+/* Bring down the PHY link, and stop checking the status. */
+void phy_stop(struct phy_device *phydev)
+{
+ spin_lock(&phydev->lock);
+
+ if (PHY_HALTED == phydev->state) {
+ spin_unlock(&phydev->lock);
+ return;
+ }
+
+ if (phydev->irq != PHY_POLL) {
+ /* Clear any pending interrupts */
+ phy_clear_interrupt(phydev);
+
+ /* Disable PHY Interrupts */
+ phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
+ }
+
+ phydev->state = PHY_HALTED;
+
+ spin_unlock(&phydev->lock);
+}
+
+
+/* phy_start
+ *
+ * description: Indicates the attached device's readiness to
+ * handle PHY-related work. Used during startup to start the
+ * PHY, and after a call to phy_stop() to resume operation.
+ * Also used to indicate the MDIO bus has cleared an error
+ * condition.
+ */
+void phy_start(struct phy_device *phydev)
+{
+ spin_lock(&phydev->lock);
+
+ switch (phydev->state) {
+ case PHY_STARTING:
+ phydev->state = PHY_PENDING;
+ break;
+ case PHY_READY:
+ phydev->state = PHY_UP;
+ break;
+ case PHY_HALTED:
+ phydev->state = PHY_RESUMING;
+ default:
+ break;
+ }
+ spin_unlock(&phydev->lock);
+}
+EXPORT_SYMBOL(phy_stop);
+EXPORT_SYMBOL(phy_start);
+
+/* PHY timer which handles the state machine */
+void phy_timer(unsigned long data)
+{
+ struct phy_device *phydev = (struct phy_device *)data;
+ int needs_aneg = 0;
+ int err = 0;
+
+ spin_lock(&phydev->lock);
+
+ if (phydev->adjust_state)
+ phydev->adjust_state(phydev->attached_dev);
+
+ switch(phydev->state) {
+ case PHY_DOWN:
+ case PHY_STARTING:
+ case PHY_READY:
+ case PHY_PENDING:
+ break;
+ case PHY_UP:
+ needs_aneg = 1;
+
+ phydev->link_timeout = PHY_AN_TIMEOUT;
+
+ break;
+ case PHY_AN:
+ /* Check if negotiation is done. Break
+ * if there's an error */
+ err = phy_aneg_done(phydev);
+ if (err < 0)
+ break;
+
+ /* If auto-negotiation is done, we change to
+ * either RUNNING, or NOLINK */
+ if (err > 0) {
+ err = phy_read_status(phydev);
+
+ if (err)
+ break;
+
+ if (phydev->link) {
+ phydev->state = PHY_RUNNING;
+ netif_carrier_on(phydev->attached_dev);
+ } else {
+ phydev->state = PHY_NOLINK;
+ netif_carrier_off(phydev->attached_dev);
+ }
+
+ phydev->adjust_link(phydev->attached_dev);
+
+ } else if (0 == phydev->link_timeout--) {
+ /* The counter expired, so either we
+ * switch to forced mode, or the
+ * magic_aneg bit exists, and we try aneg
+ * again */
+ if (!(phydev->drv->flags & PHY_HAS_MAGICANEG)) {
+ int idx;
+
+ /* We'll start from the
+ * fastest speed, and work
+ * our way down */
+ idx = phy_find_valid(0,
+ phydev->supported);
+
+ phydev->speed = settings[idx].speed;
+ phydev->duplex = settings[idx].duplex;
+
+ phydev->autoneg = AUTONEG_DISABLE;
+ phydev->state = PHY_FORCING;
+ phydev->link_timeout =
+ PHY_FORCE_TIMEOUT;
+
+ pr_info("Trying %d/%s\n",
+ phydev->speed,
+ DUPLEX_FULL ==
+ phydev->duplex ?
+ "FULL" : "HALF");
+ }
+
+ needs_aneg = 1;
+ }
+ break;
+ case PHY_NOLINK:
+ err = phy_read_status(phydev);
+
+ if (err)
+ break;
+
+ if (phydev->link) {
+ phydev->state = PHY_RUNNING;
+ netif_carrier_on(phydev->attached_dev);
+ phydev->adjust_link(phydev->attached_dev);
+ }
+ break;
+ case PHY_FORCING:
+ err = phy_read_status(phydev);
+
+ if (err)
+ break;
+
+ if (phydev->link) {
+ phydev->state = PHY_RUNNING;
+ netif_carrier_on(phydev->attached_dev);
+ } else {
+ if (0 == phydev->link_timeout--) {
+ phy_force_reduction(phydev);
+ needs_aneg = 1;
+ }
+ }
+
+ phydev->adjust_link(phydev->attached_dev);
+ break;
+ case PHY_RUNNING:
+ /* Only register a CHANGE if we are
+ * polling */
+ if (PHY_POLL == phydev->irq)
+ phydev->state = PHY_CHANGELINK;
+ break;
+ case PHY_CHANGELINK:
+ err = phy_read_status(phydev);
+
+ if (err)
+ break;
+
+ if (phydev->link) {
+ phydev->state = PHY_RUNNING;
+ netif_carrier_on(phydev->attached_dev);
+ } else {
+ phydev->state = PHY_NOLINK;
+ netif_carrier_off(phydev->attached_dev);
+ }
+
+ phydev->adjust_link(phydev->attached_dev);
+
+ if (PHY_POLL != phydev->irq)
+ err = phy_config_interrupt(phydev,
+ PHY_INTERRUPT_ENABLED);
+ break;
+ case PHY_HALTED:
+ if (phydev->link) {
+ phydev->link = 0;
+ netif_carrier_off(phydev->attached_dev);
+ phydev->adjust_link(phydev->attached_dev);
+ }
+ break;
+ case PHY_RESUMING:
+
+ err = phy_clear_interrupt(phydev);
+
+ if (err)
+ break;
+
+ err = phy_config_interrupt(phydev,
+ PHY_INTERRUPT_ENABLED);
+
+ if (err)
+ break;
+
+ if (AUTONEG_ENABLE == phydev->autoneg) {
+ err = phy_aneg_done(phydev);
+ if (err < 0)
+ break;
+
+ /* err > 0 if AN is done.
+ * Otherwise, it's 0, and we're
+ * still waiting for AN */
+ if (err > 0) {
+ phydev->state = PHY_RUNNING;
+ } else {
+ phydev->state = PHY_AN;
+ phydev->link_timeout = PHY_AN_TIMEOUT;
+ }
+ } else
+ phydev->state = PHY_RUNNING;
+ break;
+ }
+
+ spin_unlock(&phydev->lock);
+
+ if (needs_aneg)
+ err = phy_start_aneg(phydev);
+
+ if (err < 0)
+ phy_error(phydev);
+
+ mod_timer(&phydev->phy_timer, jiffies + PHY_STATE_TIME * HZ);
+}
+
+#endif /* CONFIG_PHYCONTROL */
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/phy_device.c
@@ -0,0 +1,700 @@
+/*
+ * drivers/net/phy/phy_device.c
+ *
+ * Framework for finding and configuring PHYs.
+ * Also contains generic PHY driver
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 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/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.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/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* get_phy_device
+ *
+ * description: Reads the ID registers of the PHY at addr on the
+ * bus, then allocates and returns the phy_device to
+ * represent it.
+ */
+struct phy_device * get_phy_device(struct mii_bus *bus, uint addr)
+{
+ int phy_reg;
+ u32 phy_id;
+ struct phy_device *dev = NULL;
+
+ errno = 0;
+
+ /* Grab the bits from PHYIR1, and put them
+ * in the upper half */
+ phy_reg = bus->read(bus, addr, MII_PHYSID1);
+
+ if (phy_reg < 0) {
+ errno = phy_reg;
+ return NULL;
+ }
+
+ phy_id = (phy_reg & 0xffff) << 16;
+
+ /* Grab the bits from PHYIR2, and put them in the lower half */
+ phy_reg = bus->read(bus, addr, MII_PHYSID2);
+
+ if (phy_reg < 0) {
+ errno = phy_reg;
+ return NULL;
+ }
+
+ phy_id |= (phy_reg & 0xffff);
+
+ /* If the phy_id is all Fs, there is no device there */
+ if (0xffffffff == phy_id)
+ return NULL;
+
+ /* Otherwise, we allocate the device, and initialize the
+ * default values */
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+
+ if (NULL == dev) {
+ errno = -ENOMEM;
+ return NULL;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ dev->speed = 0;
+ dev->duplex = -1;
+ dev->pause = dev->asym_pause = 0;
+ dev->link = 1;
+
+ dev->autoneg = AUTONEG_ENABLE;
+
+ dev->addr = addr;
+ dev->phy_id = phy_id;
+ dev->bus = bus;
+
+ dev->state = PHY_DOWN;
+
+ spin_lock_init(&dev->lock);
+
+ return dev;
+}
+
+/* phy_prepare_link:
+ *
+ * description: Tells the PHY infrastructure to handle the
+ * gory details on monitoring link status (whether through
+ * polling or an interrupt), and to call back to the
+ * connected device driver when the link status changes.
+ * If you want to monitor your own link state, don't call
+ * this function */
+void phy_prepare_link(struct phy_device *phydev,
+ void (*handler)(struct net_device *))
+{
+ phydev->adjust_link = handler;
+}
+
+#ifdef CONFIG_PHYCONTROL
+/* phy_connect:
+ *
+ * description: Convenience function for connecting ethernet
+ * devices to PHY devices. The default behavior is for
+ * the PHY infrastructure to handle everything, and only notify
+ * the connected driver when the link status changes. If you
+ * don't want, or can't use the provided functionality, you may
+ * choose to call only the subset of functions which provide
+ * the desired functionality.
+ */
+struct phy_device * phy_connect(struct net_device *dev, const char *phy_id,
+ void (*handler)(struct net_device *), u32 flags)
+{
+ struct phy_device *phydev;
+
+ phydev = phy_attach(dev, phy_id, flags);
+
+ if (NULL == phydev)
+ return phydev;
+
+ phy_prepare_link(phydev, handler);
+
+ phy_start_machine(phydev, NULL);
+
+ if (phydev->irq > 0)
+ phy_start_interrupts(phydev);
+
+ return phydev;
+}
+EXPORT_SYMBOL(phy_connect);
+
+void phy_disconnect(struct phy_device *phydev)
+{
+ if (phydev->irq > 0)
+ phy_stop_interrupts(phydev);
+
+ phy_stop_machine(phydev);
+
+ phydev->adjust_link = NULL;
+
+ phy_detach(phydev);
+}
+EXPORT_SYMBOL(phy_disconnect);
+
+#endif /* CONFIG_PHYCONTROL */
+
+/* phy_attach:
+ *
+ * description: Called by drivers to attach to a particular PHY
+ * device. The phy_device is found, and properly hooked up
+ * to the phy_driver. If no driver is attached, then the
+ * genphy_driver is used. The phy_device is given a ptr to
+ * the attaching device, and given a callback for link status
+ * change. The phy_device is returned to the attaching
+ * driver.
+ */
+static int phy_compare_id(struct device *dev, void *data)
+{
+ const char *name = data;
+
+ if (strcmp(name, dev->bus_id) == 0)
+ return 1;
+ return 0;
+}
+
+struct phy_device *phy_attach(struct net_device *dev,
+ const char *phy_id, u32 flags)
+{
+ struct phy_device *phydev = NULL;
+ struct bus_type *bus = &mdio_bus_type;
+ struct device *d;
+
+ /* Search the list of PHY devices on the mdio bus for the
+ * PHY with the requested name */
+ d = bus_find_device(bus, NULL, (void *)phy_id, phy_compare_id);
+
+ if (d) {
+ phydev = to_phy_device(d);
+ } else {
+ printk(KERN_ERR "%s not found\n", phy_id);
+ errno = -ENODEV;
+ return NULL;
+ }
+
+ /* Assume that if there is no driver, that it doesn't
+ * exist, and we should use the genphy driver. */
+ if (NULL == phydev->dev.driver) {
+ int err;
+ down_write(&phydev->dev.bus->subsys.rwsem);
+ phydev->dev.driver = &genphy_driver.driver;
+
+ err = phydev->dev.driver->probe(&phydev->dev);
+
+ if (err < 0) {
+ errno = err;
+ return NULL;
+ }
+
+ device_bind_driver(&phydev->dev);
+ up_write(&phydev->dev.bus->subsys.rwsem);
+ }
+
+ if (phydev->attached_dev) {
+ printk(KERN_ERR "%s: %s already attached\n",
+ dev->name, phy_id);
+ errno = -EBUSY;
+ return NULL;
+ }
+
+ phydev->attached_dev = dev;
+
+ phydev->dev_flags = flags;
+
+ return phydev;
+}
+EXPORT_SYMBOL(phy_attach);
+
+void phy_detach(struct phy_device *phydev)
+{
+ phydev->attached_dev = NULL;
+
+ /* If the device had no specific driver before (i.e. - it
+ * was using the generic driver), we unbind the device
+ * from the generic driver so that there's a chance a
+ * real driver could be loaded */
+ if (phydev->dev.driver == &genphy_driver.driver) {
+ down_write(&phydev->dev.bus->subsys.rwsem);
+ device_release_driver(&phydev->dev);
+ up_write(&phydev->dev.bus->subsys.rwsem);
+ }
+}
+EXPORT_SYMBOL(phy_detach);
+
+
+/* Generic PHY support and helper functions */
+
+/* genphy_config_advert
+ *
+ * description: Writes MII_ADVERTISE with the appropriate values,
+ * after sanitizing the values to make sure we only advertise
+ * what is supported
+ */
+int genphy_config_advert(struct phy_device *phydev)
+{
+ u32 advertise;
+ int adv;
+ int err;
+
+ /* Only allow advertising what
+ * this PHY supports */
+ phydev->advertising &= phydev->supported;
+ advertise = phydev->advertising;
+
+ /* Setup standard advertisement */
+ adv = phy_read(phydev, MII_ADVERTISE);
+
+ if (adv < 0)
+ return adv;
+
+ adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |
+ ADVERTISE_PAUSE_ASYM);
+ if (advertise & ADVERTISED_10baseT_Half)
+ adv |= ADVERTISE_10HALF;
+ if (advertise & ADVERTISED_10baseT_Full)
+ adv |= ADVERTISE_10FULL;
+ if (advertise & ADVERTISED_100baseT_Half)
+ adv |= ADVERTISE_100HALF;
+ if (advertise & ADVERTISED_100baseT_Full)
+ adv |= ADVERTISE_100FULL;
+ if (advertise & ADVERTISED_Pause)
+ adv |= ADVERTISE_PAUSE_CAP;
+ if (advertise & ADVERTISED_Asym_Pause)
+ adv |= ADVERTISE_PAUSE_ASYM;
+
+ err = phy_write(phydev, MII_ADVERTISE, adv);
+
+ if (err < 0)
+ return err;
+
+ /* Configure gigabit if it's supported */
+ if (phydev->supported & (SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full)) {
+ adv = phy_read(phydev, MII_CTRL1000);
+
+ if (adv < 0)
+ return adv;
+
+ adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
+ if (advertise & SUPPORTED_1000baseT_Half)
+ adv |= ADVERTISE_1000HALF;
+ if (advertise & SUPPORTED_1000baseT_Full)
+ adv |= ADVERTISE_1000FULL;
+ err = phy_write(phydev, MII_CTRL1000, adv);
+
+ if (err < 0)
+ return err;
+ }
+
+ return adv;
+}
+EXPORT_SYMBOL(genphy_config_advert);
+
+/* genphy_setup_forced
+ *
+ * description: Configures MII_BMCR to force speed/duplex
+ * to the values in phydev. Assumes that the values are valid.
+ * Please see phy_sanitize_settings() */
+int genphy_setup_forced(struct phy_device *phydev)
+{
+ int ctl = BMCR_RESET;
+
+ phydev->pause = phydev->asym_pause = 0;
+
+ if (SPEED_1000 == phydev->speed)
+ ctl |= BMCR_SPEED1000;
+ else if (SPEED_100 == phydev->speed)
+ ctl |= BMCR_SPEED100;
+
+ if (DUPLEX_FULL == phydev->duplex)
+ ctl |= BMCR_FULLDPLX;
+
+ ctl = phy_write(phydev, MII_BMCR, ctl);
+
+ if (ctl < 0)
+ return ctl;
+
+ /* We just reset the device, so we'd better configure any
+ * settings the PHY requires to operate */
+ if (phydev->drv->config_init)
+ ctl = phydev->drv->config_init(phydev);
+
+ return ctl;
+}
+
+
+/* Enable and Restart Autonegotiation */
+int genphy_restart_aneg(struct phy_device *phydev)
+{
+ int ctl;
+
+ ctl = phy_read(phydev, MII_BMCR);
+
+ if (ctl < 0)
+ return ctl;
+
+ ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+
+ /* Don't isolate the PHY if we're negotiating */
+ ctl &= ~(BMCR_ISOLATE);
+
+ ctl = phy_write(phydev, MII_BMCR, ctl);
+
+ return ctl;
+}
+
+
+/* genphy_config_aneg
+ *
+ * description: If auto-negotiation is enabled, we configure the
+ * advertising, and then restart auto-negotiation. If it is not
+ * enabled, then we write the BMCR
+ */
+int genphy_config_aneg(struct phy_device *phydev)
+{
+ int err = 0;
+
+ if (AUTONEG_ENABLE == phydev->autoneg) {
+ err = genphy_config_advert(phydev);
+
+ if (err < 0)
+ return err;
+
+ err = genphy_restart_aneg(phydev);
+ } else
+ err = genphy_setup_forced(phydev);
+
+ return err;
+}
+EXPORT_SYMBOL(genphy_config_aneg);
+
+/* genphy_update_link
+ *
+ * description: Update the value in phydev->link to reflect the
+ * current link value. In order to do this, we need to read
+ * the status register twice, keeping the second value
+ */
+int genphy_update_link(struct phy_device *phydev)
+{
+ int status;
+
+ /* Do a fake read */
+ status = phy_read(phydev, MII_BMSR);
+
+ if (status < 0)
+ return status;
+
+ /* Read link and autonegotiation status */
+ status = phy_read(phydev, MII_BMSR);
+
+ if (status < 0)
+ return status;
+
+ if ((status & BMSR_LSTATUS) == 0)
+ phydev->link = 0;
+ else
+ phydev->link = 1;
+
+ return 0;
+}
+
+/* genphy_read_status
+ *
+ * description: Check the link, then figure out the current state
+ * by comparing what we advertise with what the link partner
+ * advertises. Start by checking the gigabit possibilities,
+ * then move on to 10/100.
+ */
+int genphy_read_status(struct phy_device *phydev)
+{
+ int adv;
+ int err;
+ int lpa;
+ int lpagb = 0;
+
+ /* Update the link, but return if there
+ * was an error */
+ err = genphy_update_link(phydev);
+ if (err)
+ return err;
+
+ if (AUTONEG_ENABLE == phydev->autoneg) {
+ if (phydev->supported & (SUPPORTED_1000baseT_Half
+ | SUPPORTED_1000baseT_Full)) {
+ lpagb = phy_read(phydev, MII_STAT1000);
+
+ if (lpagb < 0)
+ return lpagb;
+
+ adv = phy_read(phydev, MII_CTRL1000);
+
+ if (adv < 0)
+ return adv;
+
+ lpagb &= adv << 2;
+ }
+
+ lpa = phy_read(phydev, MII_LPA);
+
+ if (lpa < 0)
+ return lpa;
+
+ adv = phy_read(phydev, MII_ADVERTISE);
+
+ if (adv < 0)
+ return adv;
+
+ lpa &= adv;
+
+ phydev->speed = SPEED_10;
+ phydev->duplex = DUPLEX_HALF;
+ phydev->pause = phydev->asym_pause = 0;
+
+ if (lpagb & (LPA_1000FULL | LPA_1000HALF)) {
+ phydev->speed = SPEED_1000;
+
+ if (lpagb & LPA_1000FULL)
+ phydev->duplex = DUPLEX_FULL;
+ } else if (lpa & (LPA_100FULL | LPA_100HALF)) {
+ phydev->speed = SPEED_100;
+
+ if (lpa & LPA_100FULL)
+ phydev->duplex = DUPLEX_FULL;
+ } else
+ if (lpa & LPA_10FULL)
+ phydev->duplex = DUPLEX_FULL;
+
+ if (phydev->duplex == DUPLEX_FULL){
+ phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
+ phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
+ }
+ } else {
+ int bmcr = phy_read(phydev, MII_BMCR);
+ if (bmcr < 0)
+ return bmcr;
+
+ if (bmcr & BMCR_FULLDPLX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ if (bmcr & BMCR_SPEED1000)
+ phydev->speed = SPEED_1000;
+ else if (bmcr & BMCR_SPEED100)
+ phydev->speed = SPEED_100;
+ else
+ phydev->speed = SPEED_10;
+
+ phydev->pause = phydev->asym_pause = 0;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(genphy_read_status);
+
+static int genphy_config_init(struct phy_device *phydev)
+{
+ u32 val;
+ u32 features;
+
+ /* For now, I'll claim that the generic driver supports
+ * all possible port types */
+ features = (SUPPORTED_TP | SUPPORTED_MII
+ | SUPPORTED_AUI | SUPPORTED_FIBRE |
+ SUPPORTED_BNC);
+
+ /* Do we support autonegotiation? */
+ val = phy_read(phydev, MII_BMSR);
+
+ if (val < 0)
+ return val;
+
+ if (val & BMSR_ANEGCAPABLE)
+ features |= SUPPORTED_Autoneg;
+
+ if (val & BMSR_100FULL)
+ features |= SUPPORTED_100baseT_Full;
+ if (val & BMSR_100HALF)
+ features |= SUPPORTED_100baseT_Half;
+ if (val & BMSR_10FULL)
+ features |= SUPPORTED_10baseT_Full;
+ if (val & BMSR_10HALF)
+ features |= SUPPORTED_10baseT_Half;
+
+ if (val & BMSR_ESTATEN) {
+ val = phy_read(phydev, MII_ESTATUS);
+
+ if (val < 0)
+ return val;
+
+ if (val & ESTATUS_1000_TFULL)
+ features |= SUPPORTED_1000baseT_Full;
+ if (val & ESTATUS_1000_THALF)
+ features |= SUPPORTED_1000baseT_Half;
+ }
+
+ phydev->supported = features;
+ phydev->advertising = features;
+
+ return 0;
+}
+
+
+/* phy_probe
+ *
+ * description: Take care of setting up the phy_device structure,
+ * set the state to READY (the driver's init function should
+ * set it to STARTING if needed).
+ */
+static int phy_probe(struct device *dev)
+{
+ struct phy_device *phydev;
+ struct phy_driver *phydrv;
+ struct device_driver *drv;
+ int err = 0;
+
+ phydev = to_phy_device(dev);
+
+ /* Make sure the driver is held.
+ * XXX -- Is this correct? */
+ drv = get_driver(phydev->dev.driver);
+ phydrv = to_phy_driver(drv);
+ phydev->drv = phydrv;
+
+ /* Disable the interrupt if the PHY doesn't support it */
+ if (!(phydrv->flags & PHY_HAS_INTERRUPT))
+ phydev->irq = PHY_POLL;
+
+ spin_lock(&phydev->lock);
+
+ /* Start out supporting everything. Eventually,
+ * a controller will attach, and may modify one
+ * or both of these values */
+ phydev->supported = phydrv->features;
+ phydev->advertising = phydrv->features;
+
+ /* Set the state to READY by default */
+ phydev->state = PHY_READY;
+
+ if (phydev->drv->probe)
+ err = phydev->drv->probe(phydev);
+
+ spin_unlock(&phydev->lock);
+
+ if (err < 0)
+ return err;
+
+ if (phydev->drv->config_init)
+ err = phydev->drv->config_init(phydev);
+
+ return err;
+}
+
+static int phy_remove(struct device *dev)
+{
+ struct phy_device *phydev;
+
+ phydev = to_phy_device(dev);
+
+ spin_lock(&phydev->lock);
+ phydev->state = PHY_DOWN;
+ spin_unlock(&phydev->lock);
+
+ if (phydev->drv->remove)
+ phydev->drv->remove(phydev);
+
+ put_driver(phydev->dev.driver);
+ phydev->drv = NULL;
+
+ return 0;
+}
+
+int phy_driver_register(struct phy_driver *new_driver)
+{
+ int retval;
+
+ memset(&new_driver->driver, 0, sizeof(new_driver->driver));
+ new_driver->driver.name = new_driver->name;
+ new_driver->driver.bus = &mdio_bus_type;
+ new_driver->driver.probe = phy_probe;
+ new_driver->driver.remove = phy_remove;
+
+ retval = driver_register(&new_driver->driver);
+
+ if (retval) {
+ printk(KERN_ERR "%s: Error %d in registering driver\n",
+ new_driver->name, retval);
+
+ return retval;
+ }
+
+ pr_info("%s: Registered new driver\n", new_driver->name);
+
+ return 0;
+}
+EXPORT_SYMBOL(phy_driver_register);
+
+void phy_driver_unregister(struct phy_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(phy_driver_unregister);
+
+static struct phy_driver genphy_driver = {
+ .phy_id = 0xffffffff,
+ .phy_id_mask = 0xffffffff,
+ .name = "Generic PHY",
+ .config_init = genphy_config_init,
+ .features = 0,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .driver = {.owner = THIS_MODULE, },
+};
+
+static int __init genphy_init(void)
+{
+ return phy_driver_register(&genphy_driver);
+
+}
+
+static void __exit genphy_exit(void)
+{
+ phy_driver_unregister(&genphy_driver);
+}
+
+module_init(genphy_init);
+module_exit(genphy_exit);
diff --git a/drivers/net/phy/qsemi.c b/drivers/net/phy/qsemi.c
new file mode 100644
--- /dev/null
+++ b/drivers/net/phy/qsemi.c
@@ -0,0 +1,143 @@
+/*
+ * drivers/net/phy/qsemi.c
+ *
+ * Driver for Quality Semiconductor PHYs
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 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/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.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/version.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+/* ------------------------------------------------------------------------- */
+/* The Quality Semiconductor QS6612 is used on the RPX CLLF */
+
+/* register definitions */
+
+#define MII_QS6612_MCR 17 /* Mode Control Register */
+#define MII_QS6612_FTR 27 /* Factory Test Register */
+#define MII_QS6612_MCO 28 /* Misc. Control Register */
+#define MII_QS6612_ISR 29 /* Interrupt Source Register */
+#define MII_QS6612_IMR 30 /* Interrupt Mask Register */
+#define MII_QS6612_IMR_INIT 0x003a
+#define MII_QS6612_PCR 31 /* 100BaseTx PHY Control Reg. */
+
+#define QS6612_PCR_AN_COMPLETE 0x1000
+#define QS6612_PCR_RLBEN 0x0200
+#define QS6612_PCR_DCREN 0x0100
+#define QS6612_PCR_4B5BEN 0x0040
+#define QS6612_PCR_TX_ISOLATE 0x0020
+#define QS6612_PCR_MLT3_DIS 0x0002
+#define QS6612_PCR_SCRM_DESCRM 0x0001
+
+MODULE_DESCRIPTION("Quality Semiconductor PHY driver");
+MODULE_AUTHOR("Andy Fleming");
+MODULE_LICENSE("GPL");
+
+/* Returns 0, unless there's a write error */
+static int qs6612_config_init(struct phy_device *phydev)
+{
+ /* The PHY powers up isolated on the RPX,
+ * so send a command to allow operation.
+ * XXX - My docs indicate this should be 0x0940
+ * ...or something. The current value sets three
+ * reserved bits, bit 11, which specifies it should be
+ * set to one, bit 10, which specifies it should be set
+ * to 0, and bit 7, which doesn't specify. However, my
+ * docs are preliminary, and I will leave it like this
+ * until someone more knowledgable corrects me or it.
+ * -- Andy Fleming
+ */
+ return phy_write(phydev, MII_QS6612_PCR, 0x0dc0);
+}
+
+static int qs6612_ack_interrupt(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_read(phydev, MII_QS6612_ISR);
+
+ if (err < 0)
+ return err;
+
+ err = phy_read(phydev, MII_BMSR);
+
+ if (err < 0)
+ return err;
+
+ err = phy_read(phydev, MII_EXPANSION);
+
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int qs6612_config_intr(struct phy_device *phydev)
+{
+ int err;
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
+ err = phy_write(phydev, MII_QS6612_IMR,
+ MII_QS6612_IMR_INIT);
+ else
+ err = phy_write(phydev, MII_QS6612_IMR, 0);
+
+ return err;
+
+}
+
+static struct phy_driver qs6612_driver = {
+ .phy_id = 0x00181440,
+ .name = "QS6612",
+ .phy_id_mask = 0xfffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .flags = PHY_HAS_INTERRUPT,
+ .config_init = qs6612_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = qs6612_ack_interrupt,
+ .config_intr = qs6612_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+};
+
+static int __init qs6612_init(void)
+{
+ return phy_driver_register(&qs6612_driver);
+}
+
+static void __exit qs6612_exit(void)
+{
+ phy_driver_unregister(&qs6612_driver);
+}
+
+module_init(qs6612_init);
+module_exit(qs6612_exit);
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -408,6 +408,8 @@ struct ethtool_ops {
#define SUPPORTED_FIBRE (1 << 10)
#define SUPPORTED_BNC (1 << 11)
#define SUPPORTED_10000baseT_Full (1 << 12)
+#define SUPPORTED_Pause (1 << 13)
+#define SUPPORTED_Asym_Pause (1 << 14)
/* Indicates what features are advertised by the interface. */
#define ADVERTISED_10baseT_Half (1 << 0)
@@ -423,6 +425,8 @@ struct ethtool_ops {
#define ADVERTISED_FIBRE (1 << 10)
#define ADVERTISED_BNC (1 << 11)
#define ADVERTISED_10000baseT_Full (1 << 12)
+#define ADVERTISED_Pause (1 << 13)
+#define ADVERTISED_Asym_Pause (1 << 14)
/* The following are all involved in forcing a particular link
* mode for the device for setting things. When getting the
diff --git a/include/linux/mii.h b/include/linux/mii.h
--- a/include/linux/mii.h
+++ b/include/linux/mii.h
@@ -22,6 +22,7 @@
#define MII_EXPANSION 0x06 /* Expansion register */
#define MII_CTRL1000 0x09 /* 1000BASE-T control */
#define MII_STAT1000 0x0a /* 1000BASE-T status */
+#define MII_ESTATUS 0x0f /* Extended Status */
#define MII_DCOUNTER 0x12 /* Disconnect counter */
#define MII_FCSCOUNTER 0x13 /* False carrier counter */
#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */
@@ -54,7 +55,10 @@
#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */
#define BMSR_RFAULT 0x0010 /* Remote fault detected */
#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
-#define BMSR_RESV 0x07c0 /* Unused... */
+#define BMSR_RESV 0x00c0 /* Unused... */
+#define BMSR_ESTATEN 0x0100 /* Extended Status in R15 */
+#define BMSR_100FULL2 0x0200 /* Can do 100BASE-T2 HDX */
+#define BMSR_100HALF2 0x0400 /* Can do 100BASE-T2 FDX */
#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */
#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */
#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */
@@ -113,6 +117,9 @@
#define EXPANSION_NPCAPABLE 0x0008 /* Link partner supports npage */
#define EXPANSION_MFAULTS 0x0010 /* Multiple faults detected */
#define EXPANSION_RESV 0xffe0 /* Unused... */
+
+#define ESTATUS_1000_TFULL 0x2000 /* Can do 1000BT Full */
+#define ESTATUS_1000_THALF 0x1000 /* Can do 1000BT Half */
/* N-way test register. */
#define NWAYTEST_RESV1 0x00ff /* Unused... */
diff --git a/include/linux/phy.h b/include/linux/phy.h
new file mode 100644
--- /dev/null
+++ b/include/linux/phy.h
@@ -0,0 +1,378 @@
+/*
+ * include/linux/phy.h
+ *
+ * Framework and drivers for configuring and reading different PHYs
+ * Based on code in sungem_phy.c and gianfar_phy.c
+ *
+ * Author: Andy Fleming
+ *
+ * Copyright (c) 2004 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.
+ *
+ */
+
+#ifndef __PHY_H
+#define __PHY_H
+
+#include <linux/spinlock.h>
+#include <linux/device.h>
+
+#define PHY_BASIC_FEATURES (SUPPORTED_10baseT_Half | \
+ SUPPORTED_10baseT_Full | \
+ SUPPORTED_100baseT_Half | \
+ SUPPORTED_100baseT_Full | \
+ SUPPORTED_Autoneg | \
+ SUPPORTED_TP | \
+ SUPPORTED_MII)
+
+#define PHY_GBIT_FEATURES (PHY_BASIC_FEATURES | \
+ SUPPORTED_1000baseT_Half | \
+ SUPPORTED_1000baseT_Full)
+
+/* Set phydev->irq to PHY_POLL if interrupts are not supported,
+ * or not desired for this PHY. Set to PHY_IGNORE_INTERRUPT if
+ * the attached driver handles the interrupt
+ */
+#define PHY_POLL -1
+#define PHY_IGNORE_INTERRUPT -2
+
+#define PHY_HAS_INTERRUPT 0x00000001
+#define PHY_HAS_MAGICANEG 0x00000002
+
+#define MII_BUS_MAX 4
+
+
+#define PHY_INIT_TIMEOUT 100000
+#define PHY_STATE_TIME 1
+#define PHY_FORCE_TIMEOUT 10
+#define PHY_AN_TIMEOUT 10
+
+#define PHY_MAX_ADDR 32
+
+/* The Bus class for PHYs. Devices which provide access to
+ * PHYs should register using this structure */
+struct mii_bus {
+ const char *name;
+ int id;
+ void *priv;
+ int (*read)(struct mii_bus *bus, int phy_id, int regnum);
+ int (*write)(struct mii_bus *bus, int phy_id, int regnum, u16 val);
+ int (*reset)(struct mii_bus *bus);
+
+ /* A lock to ensure that only one thing can read/write
+ * the MDIO bus at a time */
+ spinlock_t mdio_lock;
+
+ struct device *dev;
+
+ /* list of all PHYs on bus */
+ struct phy_device *phy_map[PHY_MAX_ADDR];
+
+ /* Pointer to an array of interrupts, each PHY's
+ * interrupt at the index matching its address */
+ int *irq;
+};
+
+#define PHY_INTERRUPT_DISABLED 0x0
+#define PHY_INTERRUPT_ENABLED 0x80000000
+
+/* PHY state machine states:
+ *
+ * DOWN: PHY device and driver are not ready for anything. probe
+ * should be called if and only if the PHY is in this state,
+ * given that the PHY device exists.
+ * - PHY driver probe function will, depending on the PHY, set
+ * the state to STARTING or READY
+ *
+ * STARTING: PHY device is coming up, and the ethernet driver is
+ * not ready. PHY drivers may set this in the probe function.
+ * If they do, they are responsible for making sure the state is
+ * eventually set to indicate whether the PHY is UP or READY,
+ * depending on the state when the PHY is done starting up.
+ * - PHY driver will set the state to READY
+ * - start will set the state to PENDING
+ *
+ * READY: PHY is ready to send and receive packets, but the
+ * controller is not. By default, PHYs which do not implement
+ * probe will be set to this state by phy_probe(). If the PHY
+ * driver knows the PHY is ready, and the PHY state is STARTING,
+ * then it sets this STATE.
+ * - start will set the state to UP
+ *
+ * PENDING: PHY device is coming up, but the ethernet driver is
+ * ready. phy_start will set this state if the PHY state is
+ * STARTING.
+ * - PHY driver will set the state to UP when the PHY is ready
+ *
+ * UP: The PHY and attached device are ready to do work.
+ * Interrupts should be started here.
+ * - timer moves to AN
+ *
+ * AN: The PHY is currently negotiating the link state. Link is
+ * therefore down for now. phy_timer will set this state when it
+ * detects the state is UP. config_aneg will set this state
+ * whenever called with phydev->autoneg set to AUTONEG_ENABLE.
+ * - If autonegotiation finishes, but there's no link, it sets
+ * the state to NOLINK.
+ * - If aneg finishes with link, it sets the state to RUNNING,
+ * and calls adjust_link
+ * - If autonegotiation did not finish after an arbitrary amount
+ * of time, autonegotiation should be tried again if the PHY
+ * supports "magic" autonegotiation (back to AN)
+ * - If it didn't finish, and no magic_aneg, move to FORCING.
+ *
+ * NOLINK: PHY is up, but not currently plugged in.
+ * - If the timer notes that the link comes back, we move to RUNNING
+ * - config_aneg moves to AN
+ * - phy_stop moves to HALTED
+ *
+ * FORCING: PHY is being configured with forced settings
+ * - if link is up, move to RUNNING
+ * - If link is down, we drop to the next highest setting, and
+ * retry (FORCING) after a timeout
+ * - phy_stop moves to HALTED
+ *
+ * RUNNING: PHY is currently up, running, and possibly sending
+ * and/or receiving packets
+ * - timer will set CHANGELINK if we're polling (this ensures the
+ * link state is polled every other cycle of this state machine,
+ * which makes it every other second)
+ * - irq will set CHANGELINK
+ * - config_aneg will set AN
+ * - phy_stop moves to HALTED
+ *
+ * CHANGELINK: PHY experienced a change in link state
+ * - timer moves to RUNNING if link
+ * - timer moves to NOLINK if the link is down
+ * - phy_stop moves to HALTED
+ *
+ * HALTED: PHY is up, but no polling or interrupts are done. Or
+ * PHY is in an error state.
+ *
+ * - phy_start moves to RESUMING
+ *
+ * RESUMING: PHY was halted, but now wants to run again.
+ * - If we are forcing, or aneg is done, timer moves to RUNNING
+ * - If aneg is not done, timer moves to AN
+ * - phy_stop moves to HALTED
+ */
+enum phy_state {
+ PHY_DOWN=0,
+ PHY_STARTING,
+ PHY_READY,
+ PHY_PENDING,
+ PHY_UP,
+ PHY_AN,
+ PHY_RUNNING,
+ PHY_NOLINK,
+ PHY_FORCING,
+ PHY_CHANGELINK,
+ PHY_HALTED,
+ PHY_RESUMING
+};
+
+/* phy_device: An instance of a PHY
+ *
+ * drv: Pointer to the driver for this PHY instance
+ * bus: Pointer to the bus this PHY is on
+ * dev: driver model device structure for this PHY
+ * phy_id: UID for this device found during discovery
+ * state: state of the PHY for management purposes
+ * dev_flags: Device-specific flags used by the PHY driver.
+ * addr: Bus address of PHY
+ * link_timeout: The number of timer firings to wait before the
+ * giving up on the current attempt at acquiring a link
+ * irq: IRQ number of the PHY's interrupt (-1 if none)
+ * phy_timer: The timer for handling the state machine
+ * phy_queue: A work_queue for the interrupt
+ * attached_dev: The attached enet driver's device instance ptr
+ * adjust_link: Callback for the enet controller to respond to
+ * changes in the link state.
+ * adjust_state: Callback for the enet driver to respond to
+ * changes in the state machine.
+ *
+ * speed, duplex, pause, supported, advertising, and
+ * autoneg are used like in mii_if_info
+ *
+ * interrupts currently only supports enabled or disabled,
+ * but could be changed in the future to support enabling
+ * and disabling specific interrupts
+ *
+ * Contains some infrastructure for polling and interrupt
+ * handling, as well as handling shifts in PHY hardware state
+ */
+struct phy_device {
+ /* Information about the PHY type */
+ /* And management functions */
+ struct phy_driver *drv;
+
+ struct mii_bus *bus;
+
+ struct device dev;
+
+ u32 phy_id;
+
+ enum phy_state state;
+
+ u32 dev_flags;
+
+ /* Bus address of the PHY (0-32) */
+ int addr;
+
+ /* forced speed & duplex (no autoneg)
+ * partner speed & duplex & pause (autoneg)
+ */
+ int speed;
+ int duplex;
+ int pause;
+ int asym_pause;
+
+ /* The most recently read link state */
+ int link;
+
+ /* Enabled Interrupts */
+ u32 interrupts;
+
+ /* Union of PHY and Attached devices' supported modes */
+ /* See mii.h for more info */
+ u32 supported;
+ u32 advertising;
+
+ int autoneg;
+
+ int link_timeout;
+
+ /* Interrupt number for this PHY
+ * -1 means no interrupt */
+ int irq;
+
+ /* private data pointer */
+ /* For use by PHYs to maintain extra state */
+ void *priv;
+
+ /* Interrupt and Polling infrastructure */
+ struct work_struct phy_queue;
+ struct timer_list phy_timer;
+
+ spinlock_t lock;
+
+ struct net_device *attached_dev;
+
+ void (*adjust_link)(struct net_device *dev);
+
+ void (*adjust_state)(struct net_device *dev);
+};
+#define to_phy_device(d) container_of(d, struct phy_device, dev)
+
+/* struct phy_driver: Driver structure for a particular PHY type
+ *
+ * phy_id: The result of reading the UID registers of this PHY
+ * type, and ANDing them with the phy_id_mask. This driver
+ * only works for PHYs with IDs which match this field
+ * name: The friendly name of this PHY type
+ * phy_id_mask: Defines the important bits of the phy_id
+ * features: A list of features (speed, duplex, etc) supported
+ * by this PHY
+ * flags: A bitfield defining certain other features this PHY
+ * supports (like interrupts)
+ *
+ * The drivers must implement config_aneg and read_status. All
+ * other functions are optional. Note that none of these
+ * functions should be called from interrupt time. The goal is
+ * for the bus read/write functions to be able to block when the
+ * bus transaction is happening, and be freed up by an interrupt
+ * (The MPC85xx has this ability, though it is not currently
+ * supported in the driver).
+ */
+struct phy_driver {
+ u32 phy_id;
+ char *name;
+ unsigned int phy_id_mask;
+ u32 features;
+ u32 flags;
+
+ /* Called to initialize the PHY,
+ * including after a reset */
+ int (*config_init)(struct phy_device *phydev);
+
+ /* Called during discovery. Used to set
+ * up device-specific structures, if any */
+ int (*probe)(struct phy_device *phydev);
+
+ /* PHY Power Management */
+ int (*suspend)(struct phy_device *phydev);
+ int (*resume)(struct phy_device *phydev);
+
+ /* Configures the advertisement and resets
+ * autonegotiation if phydev->autoneg is on,
+ * forces the speed to the current settings in phydev
+ * if phydev->autoneg is off */
+ int (*config_aneg)(struct phy_device *phydev);
+
+ /* Determines the negotiated speed and duplex */
+ int (*read_status)(struct phy_device *phydev);
+
+ /* Clears any pending interrupts */
+ int (*ack_interrupt)(struct phy_device *phydev);
+
+ /* Enables or disables interrupts */
+ int (*config_intr)(struct phy_device *phydev);
+
+ /* Clears up any memory if needed */
+ void (*remove)(struct phy_device *phydev);
+
+ struct device_driver driver;
+};
+#define to_phy_driver(d) container_of(d, struct phy_driver, driver)
+
+int phy_read(struct phy_device *phydev, u16 regnum);
+int phy_write(struct phy_device *phydev, u16 regnum, u16 val);
+struct phy_device* get_phy_device(struct mii_bus *bus, uint addr);
+int phy_clear_interrupt(struct phy_device *phydev);
+int phy_config_interrupt(struct phy_device *phydev, u32 interrupts);
+struct phy_device * phy_attach(struct net_device *dev,
+ const char *phy_id, u32 flags);
+struct phy_device * phy_connect(struct net_device *dev, const char *phy_id,
+ void (*handler)(struct net_device *), u32 flags);
+void phy_disconnect(struct phy_device *phydev);
+void phy_detach(struct phy_device *phydev);
+void phy_start(struct phy_device *phydev);
+void phy_stop(struct phy_device *phydev);
+int phy_start_aneg(struct phy_device *phydev);
+
+int mdiobus_register(struct mii_bus *bus);
+void mdiobus_unregister(struct mii_bus *bus);
+void phy_sanitize_settings(struct phy_device *phydev);
+int phy_stop_interrupts(struct phy_device *phydev);
+
+static inline int phy_read_status(struct phy_device *phydev) {
+ return phydev->drv->read_status(phydev);
+}
+
+int genphy_config_advert(struct phy_device *phydev);
+int genphy_setup_forced(struct phy_device *phydev);
+int genphy_restart_aneg(struct phy_device *phydev);
+int genphy_config_aneg(struct phy_device *phydev);
+int genphy_update_link(struct phy_device *phydev);
+int genphy_read_status(struct phy_device *phydev);
+void phy_driver_unregister(struct phy_driver *drv);
+int phy_driver_register(struct phy_driver *new_driver);
+void phy_prepare_link(struct phy_device *phydev,
+ void (*adjust_link)(struct net_device *));
+void phy_start_machine(struct phy_device *phydev,
+ void (*handler)(struct net_device *));
+void phy_stop_machine(struct phy_device *phydev);
+int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd);
+int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd);
+int phy_mii_ioctl(struct phy_device *phydev,
+ struct mii_ioctl_data *mii_data, int cmd);
+int phy_start_interrupts(struct phy_device *phydev);
+void phy_print_status(struct phy_device *phydev);
+
+extern struct bus_type mdio_bus_type;
+extern struct phy_driver genphy_driver;
+#endif /* __PHY_H */
diff --git a/Documentation/networking/phy.txt b/Documentation/networking/phy.txt
new file mode 100644
--- /dev/null
+++ b/Documentation/networking/phy.txt
@@ -0,0 +1,288 @@
+
+-------
+PHY Abstraction Layer
+(Updated 2005-07-21)
+
+Purpose
+
+ Most network devices consist of set of registers which provide an interface
+ to a MAC layer, which communicates with the physical connection through a
+ PHY. The PHY concerns itself with negotiating link parameters with the link
+ partner on the other side of the network connection (typically, an ethernet
+ cable), and provides a register interface to allow drivers to determine what
+ settings were chosen, and to configure what settings are allowed.
+
+ While these devices are distinct from the network devices, and conform to a
+ standard layout for the registers, it has been common practice to integrate
+ the PHY management code with the network driver. This has resulted in large
+ amounts of redundant code. Also, on embedded systems with multiple (and
+ sometimes quite different) ethernet controllers connected to the same
+ management bus, it is difficult to ensure safe use of the bus.
+
+ Since the PHYs are devices, and the management busses through which they are
+ accessed are, in fact, busses, the PHY Abstraction Layer treats them as such.
+ In doing so, it has these goals:
+
+ 1) Increase code-reuse
+ 2) Increase overall code-maintainability
+ 3) Speed development time for new network drivers, and for new systems
+
+ Basically, this layer is meant to provide an interface to PHY devices which
+ allows network driver writers to write as little code as possible, while
+ still providing a full feature set.
+
+The MDIO bus
+
+ Most network devices are connected to a PHY by means of a management bus.
+ Different devices use different busses (though some share common interfaces).
+ In order to take advantage of the PAL, each bus interface needs to be
+ registered as a distinct device.
+
+ 1) read and write functions must be implemented. Their prototypes are:
+
+ int write(struct mii_bus *bus, int mii_id, int regnum, u16 value);
+ int read(struct mii_bus *bus, int mii_id, int regnum);
+
+ mii_id is the address on the bus for the PHY, and regnum is the register
+ number. These functions are guaranteed not to be called from interrupt
+ time, so it is safe for them to block, waiting for an interrupt to signal
+ the operation is complete
+
+ 2) A reset function is necessary. This is used to return the bus to an
+ initialized state.
+
+ 3) A probe function is needed. This function should set up anything the bus
+ driver needs, setup the mii_bus structure, and register with the PAL using
+ mdiobus_register. Similarly, there's a remove function to undo all of
+ that (use mdiobus_unregister).
+
+ 4) Like any driver, the device_driver structure must be configured, and init
+ exit functions are used to register the driver.
+
+ 5) The bus must also be declared somewhere as a device, and registered.
+
+ As an example for how one driver implemented an mdio bus driver, see
+ drivers/net/gianfar_mii.c and arch/ppc/syslib/mpc85xx_devices.c
+
+Connecting to a PHY
+
+ Sometime during startup, the network driver needs to establish a connection
+ between the PHY device, and the network device. At this time, the PHY's bus
+ and drivers need to all have been loaded, so it is ready for the connection.
+ At this point, there are several ways to connect to the PHY:
+
+ 1) The PAL handles everything, and only calls the network driver when
+ the link state changes, so it can react.
+
+ 2) The PAL handles everything except interrupts (usually because the
+ controller has the interrupt registers).
+
+ 3) The PAL handles everything, but checks in with the driver every second,
+ allowing the network driver to react first to any changes before the PAL
+ does.
+
+ 4) The PAL serves only as a library of functions, with the network device
+ manually calling functions to update status, and configure the PHY
+
+
+Letting the PHY Abstraction Layer do Everything
+
+ If you choose option 1 (The hope is that every driver can, but to still be
+ useful to drivers that can't), connecting to the PHY is simple:
+
+ First, you need a function to react to changes in the link state. This
+ function follows this protocol:
+
+ static void adjust_link(struct net_device *dev);
+
+ Next, you need to know the device name of the PHY connected to this device.
+ The name will look something like, "phy0:0", where the first number is the
+ bus id, and the second is the PHY's address on that bus.
+
+ Now, to connect, just call this function:
+
+ phydev = phy_connect(dev, phy_name, &adjust_link, flags);
+
+ phydev is a pointer to the phy_device structure which represents the PHY. If
+ phy_connect is successful, it will return the pointer. dev, here, is the
+ pointer to your net_device. Once done, this function will have started the
+ PHY's software state machine, and registered for the PHY's interrupt, if it
+ has one. The phydev structure will be populated with information about the
+ current state, though the PHY will not yet be truly operational at this
+ point.
+
+ flags is a u32 which can optionally contain phy-specific flags.
+ This is useful if the system has put hardware restrictions on
+ the PHY/controller, of which the PHY needs to be aware.
+
+ Now just make sure that phydev->supported and phydev->advertising have any
+ values pruned from them which don't make sense for your controller (a 10/100
+ controller may be connected to a gigabit capable PHY, so you would need to
+ mask off SUPPORTED_1000baseT*). See include/linux/ethtool.h for definitions
+ for these bitfields. Note that you should not SET any bits, or the PHY may
+ get put into an unsupported state.
+
+ Lastly, once the controller is ready to handle network traffic, you call
+ phy_start(phydev). This tells the PAL that you are ready, and configures the
+ PHY to connect to the network. If you want to handle your own interrupts,
+ just set phydev->irq to PHY_IGNORE_INTERRUPT before you call phy_start.
+ Similarly, if you don't want to use interrupts, set phydev->irq to PHY_POLL.
+
+ When you want to disconnect from the network (even if just briefly), you call
+ phy_stop(phydev).
+
+Keeping Close Tabs on the PAL
+
+ It is possible that the PAL's built-in state machine needs a little help to
+ keep your network device and the PHY properly in sync. If so, you can
+ register a helper function when connecting to the PHY, which will be called
+ every second before the state machine reacts to any changes. To do this, you
+ need to manually call phy_attach() and phy_prepare_link(), and then call
+ phy_start_machine() with the second argument set to point to your special
+ handler.
+
+ Currently there are no examples of how to use this functionality, and testing
+ on it has been limited because the author does not have any drivers which use
+ it (they all use option 1). So Caveat Emptor.
+
+Doing it all yourself
+
+ There's a remote chance that the PAL's built-in state machine cannot track
+ the complex interactions between the PHY and your network device. If this is
+ so, you can simply call phy_attach(), and not call phy_start_machine or
+ phy_prepare_link(). This will mean that phydev->state is entirely yours to
+ handle (phy_start and phy_stop toggle between some of the states, so you
+ might need to avoid them).
+
+ An effort has been made to make sure that useful functionality can be
+ accessed without the state-machine running, and most of these functions are
+ descended from functions which did not interact with a complex state-machine.
+ However, again, no effort has been made so far to test running without the
+ state machine, so tryer beware.
+
+ Here is a brief rundown of the functions:
+
+ int phy_read(struct phy_device *phydev, u16 regnum);
+ int phy_write(struct phy_device *phydev, u16 regnum, u16 val);
+
+ Simple read/write primitives. They invoke the bus's read/write function
+ pointers.
+
+ void phy_print_status(struct phy_device *phydev);
+
+ A convenience function to print out the PHY status neatly.
+
+ int phy_clear_interrupt(struct phy_device *phydev);
+ int phy_config_interrupt(struct phy_device *phydev, u32 interrupts);
+
+ Clear the PHY's interrupt, and configure which ones are allowed,
+ respectively. Currently only supports all on, or all off.
+
+ int phy_enable_interrupts(struct phy_device *phydev);
+ int phy_disable_interrupts(struct phy_device *phydev);
+
+ Functions which enable/disable PHY interrupts, clearing them
+ before and after, respectively.
+
+ int phy_start_interrupts(struct phy_device *phydev);
+ int phy_stop_interrupts(struct phy_device *phydev);
+
+ Requests the IRQ for the PHY interrupts, then enables them for
+ start, or disables then frees them for stop.
+
+ struct phy_device * phy_attach(struct net_device *dev, const char *phy_id,
+ u32 flags);
+
+ Attaches a network device to a particular PHY, binding the PHY to a generic
+ driver if none was found during bus initialization. Passes in
+ any phy-specific flags as needed.
+
+ int phy_start_aneg(struct phy_device *phydev);
+
+ Using variables inside the phydev structure, either configures advertising
+ and resets autonegotiation, or disables autonegotiation, and configures
+ forced settings.
+
+ static inline int phy_read_status(struct phy_device *phydev);
+
+ Fills the phydev structure with up-to-date information about the current
+ settings in the PHY.
+
+ void phy_sanitize_settings(struct phy_device *phydev)
+
+ Resolves differences between currently desired settings, and
+ supported settings for the given PHY device. Does not make
+ the changes in the hardware, though.
+
+ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd);
+ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd);
+
+ Ethtool convenience functions.
+
+ int phy_mii_ioctl(struct phy_device *phydev,
+ struct mii_ioctl_data *mii_data, int cmd);
+
+ The MII ioctl. Note that this function will completely screw up the state
+ machine if you write registers like BMCR, BMSR, ADVERTISE, etc. Best to
+ use this only to write registers which are not standard, and don't set off
+ a renegotiation.
+
+
+PHY Device Drivers
+
+ With the PHY Abstraction Layer, adding support for new PHYs is
+ quite easy. In some cases, no work is required at all! However,
+ many PHYs require a little hand-holding to get up-and-running.
+
+Generic PHY driver
+
+ If the desired PHY doesn't have any errata, quirks, or special
+ features you want to support, then it may be best to not add
+ support, and let the PHY Abstraction Layer's Generic PHY Driver
+ do all of the work.
+
+Writing a PHY driver
+
+ If you do need to write a PHY driver, the first thing to do is
+ make sure it can be matched with an appropriate PHY device.
+ This is done during bus initialization by reading the device's
+ UID (stored in registers 2 and 3), then comparing it to each
+ driver's phy_id field by ANDing it with each driver's
+ phy_id_mask field. Also, it needs a name. Here's an example:
+
+ static struct phy_driver dm9161_driver = {
+ .phy_id = 0x0181b880,
+ .name = "Davicom DM9161E",
+ .phy_id_mask = 0x0ffffff0,
+ ...
+ }
+
+ Next, you need to specify what features (speed, duplex, autoneg,
+ etc) your PHY device and driver support. Most PHYs support
+ PHY_BASIC_FEATURES, but you can look in include/mii.h for other
+ features.
+
+ Each driver consists of a number of function pointers:
+
+ config_init: configures PHY into a sane state after a reset.
+ For instance, a Davicom PHY requires descrambling disabled.
+ probe: Does any setup needed by the driver
+ suspend/resume: power management
+ config_aneg: Changes the speed/duplex/negotiation settings
+ read_status: Reads the current speed/duplex/negotiation settings
+ ack_interrupt: Clear a pending interrupt
+ config_intr: Enable or disable interrupts
+ remove: Does any driver take-down
+
+ Of these, only config_aneg and read_status are required to be
+ assigned by the driver code. The rest are optional. Also, it is
+ preferred to use the generic phy driver's versions of these two
+ functions if at all possible: genphy_read_status and
+ genphy_config_aneg. If this is not possible, it is likely that
+ you only need to perform some actions before and after invoking
+ these functions, and so your functions will wrap the generic
+ ones.
+
+ Feel free to look at the Marvell, Cicada, and Davicom drivers in
+ drivers/net/phy/ for examples (the lxt and qsemi drivers have
+ not been tested as of this writing)
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -504,6 +504,7 @@ int phy_enable_interrupts(struct phy_dev
return err;
}
+EXPORT_SYMBOL(phy_enable_interrupts);
/* Disable the PHY interrupts from the PHY side */
int phy_disable_interrupts(struct phy_device *phydev)
@@ -529,6 +530,7 @@ phy_err:
return err;
}
+EXPORT_SYMBOL(phy_disable_interrupts);
/* phy_start_interrupts
*
^ permalink raw reply
* Re: Experiences with Debian Sarge on MPC8xx ?
From: Kumar Gala @ 2005-07-22 21:58 UTC (permalink / raw)
To: Yuli Barcohen; +Cc: debian-embedded, linuxppc-embedded
In-Reply-To: <17121.14182.92600.906359@home1.localdomain>
Does that actually work or are you suggesting something to try?
- kumar
On Jul 22, 2005, at 1:13 PM, Yuli Barcohen wrote:
> Try to change cache line size to zero in arch/ppc/kernel/cputable.c
>
> --
> ======================================================================
> ==
> Yuli Barcohen | Phone +972-9-765-1788 | Software Project
> Leader
> yuli@arabellasw.com | Fax +972-9-765-7494 | Arabella Software,
> Israel
> ======================================================================
> ==
>
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded
>
^ permalink raw reply
* Re: Experiences with Debian Sarge on MPC8xx ?
From: Yuli Barcohen @ 2005-07-22 18:13 UTC (permalink / raw)
To: Raphael Bossek; +Cc: debian-embedded, linuxppc-embedded
In-Reply-To: <42E0B826.2070800@gmx.de>
Try to change cache line size to zero in arch/ppc/kernel/cputable.c
--
========================================================================
Yuli Barcohen | Phone +972-9-765-1788 | Software Project Leader
yuli@arabellasw.com | Fax +972-9-765-7494 | Arabella Software, Israel
========================================================================
^ permalink raw reply
* [PATCH] ppc32: Fix building of PQ2FADS with PCI enabled
From: Kumar Gala @ 2005-07-22 16:19 UTC (permalink / raw)
To: Andrew Morton; +Cc: Thomas.Downing, linux-kernel, linuxppc-embedded
Compilation fails for arch/ppc/syslib/m82xx_pci.c in version
2.6.12.3 when CONFIG_PQ2FADS=y. The following patch fixes the
problems, which are just typographical.
Signed-off-by: Thomas Downing <Thomas.Downing@ipc.com>
Signed-off-by: Kumar Gala <kumar.gala@freescale.com>
---
commit 4bc715f73b35eea5c58a9124f31b05f1fbb3b6c4
tree fb2f9683a7260795ef5ff2d7488f7d19de1b5ffa
parent 9ec8020999ffebb9524ca88e86c15923bf744b55
author Kumar K. Gala <kumar.gala@freescale.com> Fri, 22 Jul 2005 10:40:33 -0500
committer Kumar K. Gala <kumar.gala@freescale.com> Fri, 22 Jul 2005 10:40:33 -0500
arch/ppc/syslib/m82xx_pci.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/ppc/syslib/m82xx_pci.c b/arch/ppc/syslib/m82xx_pci.c
--- a/arch/ppc/syslib/m82xx_pci.c
+++ b/arch/ppc/syslib/m82xx_pci.c
@@ -238,9 +238,9 @@ pq2ads_setup_pci(struct pci_controller *
* Setting required to enable IRQ1-IRQ7 (SIUMCR [DPPC]),
* and local bus for PCI (SIUMCR [LBPC]).
*/
- immap->im_siu_conf.siu_82xx.sc_siumcr = (immap->im_siu_conf.sc_siumcr &
- ~(SIUMCR_L2PC11 | SIUMCR_LBPC11 | SIUMCR_CS10PC11 | SIUMCR_APPC11) |
- SIUMCR_BBD | SIUMCR_LBPC01 | SIUMCR_DPPC11 | SIUMCR_APPC10;
+ immap->im_siu_conf.siu_82xx.sc_siumcr = (immap->im_siu_conf.siu_82xx.sc_siumcr &
+ ~(SIUMCR_L2CPC11 | SIUMCR_LBPC11 | SIUMCR_CS10PC11 | SIUMCR_APPC11) |
+ SIUMCR_BBD | SIUMCR_LBPC01 | SIUMCR_DPPC11 | SIUMCR_APPC10);
#endif
/* Enable PCI */
immap->im_pci.pci_gcr = cpu_to_le32(PCIGCR_PCI_BUS_EN);
^ permalink raw reply
* [PATCH] ppc32: Fix typo in setup of 2nd PCI bus on 85xx
From: Kumar Gala @ 2005-07-22 16:18 UTC (permalink / raw)
To: Andrew Morton; +Cc: linux-kernel, linuxppc-embedded
Typo bug that was using PCI1 defines instead of PCI2 when setting up the
second PCI bus controller on 85xx based systems. This hasn't been a real
issue since currently the PCI2 sizes are the same as the PCI1 sizes for
currently supported boards.
Thanks to Andrew Klossner @ Xerox for point this out.
Signed-off-by: Kumar Gala <kumar.gala@freescale.com>
---
commit 9ec8020999ffebb9524ca88e86c15923bf744b55
tree 4f7ac5a96c639ea33a2cec481357a76cfbbeffb3
parent d2a144648ecfe8652d19dd9019141e88a3a2a974
author Kumar K. Gala <kumar.gala@freescale.com> Fri, 22 Jul 2005 09:58:23 -0500
committer Kumar K. Gala <kumar.gala@freescale.com> Fri, 22 Jul 2005 09:58:23 -0500
arch/ppc/syslib/ppc85xx_setup.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/ppc/syslib/ppc85xx_setup.c b/arch/ppc/syslib/ppc85xx_setup.c
--- a/arch/ppc/syslib/ppc85xx_setup.c
+++ b/arch/ppc/syslib/ppc85xx_setup.c
@@ -233,14 +233,14 @@ mpc85xx_setup_pci2(struct pci_controller
pci->powbar1 = (MPC85XX_PCI2_LOWER_MEM >> 12) & 0x000fffff;
/* Enable, Mem R/W */
pci->powar1 = 0x80044000 |
- (__ilog2(MPC85XX_PCI1_UPPER_MEM - MPC85XX_PCI1_LOWER_MEM + 1) - 1);
+ (__ilog2(MPC85XX_PCI2_UPPER_MEM - MPC85XX_PCI2_LOWER_MEM + 1) - 1);
/* Setup outboud IO windows @ MPC85XX_PCI2_IO_BASE */
pci->potar2 = 0x00000000;
pci->potear2 = 0x00000000;
pci->powbar2 = (MPC85XX_PCI2_IO_BASE >> 12) & 0x000fffff;
/* Enable, IO R/W */
- pci->powar2 = 0x80088000 | (__ilog2(MPC85XX_PCI1_IO_SIZE) - 1);
+ pci->powar2 = 0x80088000 | (__ilog2(MPC85XX_PCI2_IO_SIZE) - 1);
/* Setup 2G inbound Memory Window @ 0 */
pci->pitar1 = 0x00000000;
^ permalink raw reply
* Re: [PATCH] 2.6.12.3 Compilation error when CONFIG_PQ2FADS=y
From: Kumar Gala @ 2005-07-22 16:16 UTC (permalink / raw)
To: Downing, Thomas; +Cc: linuxppc-embedded
In-Reply-To: <1D6EDDB3E43F3B40BC089CCFEE99DB7D14D056@exnanycmbx1.corp.root.ipc.com>
Thanks for the patch, I'll send it upstream. In the future can you
follow the patch submission details described in Documentation/
SubmittingPatches.
- kumar
On Jul 22, 2005, at 8:46 AM, Downing, Thomas wrote:
> Compilation fails for arch/ppc/syslib/m82xx_pci.c in version
> 2.6.12.3 when CONFIG_PQ2FADS=y. The following patch fixes the
> problems, which are just typographical.
>
> The patch has been tested on PQ2FADS-ZU and -VR boards on 8280
> and 8275 devices.
>
> --- linux-2.6.12.3/arch/ppc/syslib/m82xx_pci.c 2005-07-15
> 17:18:57.000000000 -0400
> +++ linux-musrum/arch/ppc/syslib/m82xx_pci.c 2005-07-20
> 08:42:41.000000000 -0400
> @@ -238,9 +238,9 @@ pq2ads_setup_pci(struct pci_controller *
> * Setting required to enable IRQ1-IRQ7 (SIUMCR [DPPC]),
> * and local bus for PCI (SIUMCR [LBPC]).
> */
> - immap->im_siu_conf.siu_82xx.sc_siumcr = (immap-
> >im_siu_conf.sc_siumcr &
> - ~(SIUMCR_L2PC11 | SIUMCR_LBPC11 | SIUMCR_CS10PC11
> | SIUMCR_APPC11) |
> - SIUMCR_BBD | SIUMCR_LBPC01 | SIUMCR_DPPC11 |
> SIUMCR_APPC10;
> + immap->im_siu_conf.siu_82xx.sc_siumcr = (immap-
> >im_siu_conf.siu_82xx.sc_siumcr &
> + ~(SIUMCR_L2CPC11 | SIUMCR_LBPC11 | SIUMCR_CS10PC11
> | SIUMCR_APPC11) |
> + SIUMCR_BBD | SIUMCR_LBPC01 | SIUMCR_DPPC11 |
> SIUMCR_APPC10);
> #endif
> /* Enable PCI */
> immap->im_pci.pci_gcr = cpu_to_le32(PCIGCR_PCI_BUS_EN);
>
> Thomas Downing
> IPC Information Systems, LLC
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded
>
^ permalink raw reply
* Re: swap_dup: Bad swap file entry 00480020
From: bogdan antonovici @ 2005-07-22 15:46 UTC (permalink / raw)
To: Dan Malek; +Cc: linuxppc-dev, ppckernel, linuxppc-embedded
In-Reply-To: <1a928a85f53f5dcd972161356611a312@embeddededge.com>
Hi Dan,
I checked the driver code. I found a pointer that was in my opinion
initialized too late and i corrected that but other than that i haven't
found anything.
I ran the driver alone, enabling the interrupts and the interrupt
routine doesn't cause any trouble.
I started my application and i haven't seen any sign of trouble.
But once i started also the snmpd after few interrupts i got the
message:
__alloc_pages: 0-order allocation failed (gfp=0x1d2/0)
VM: killing process sectionmond
sectionmond being my application.
My read and write driver operation are requesting a page for a buffer
but they also release it. Should i declare the buffer pointer with
volatile attribute?
Do you know what may cause that message?
Thanks
Bogdan
On Thu, 2005-07-21 at 12:59, Dan Malek wrote:
> On Jul 21, 2005, at 11:29 AM, bogdan antonovici wrote:
>
> > At the time of swap messages i was running a proprietary driver, my
> > application and few daemons.
>
> Looks like your driver may have written over some of the page
> tables in the kernel space.
>
> > I look on the net for some clues but it's quite confusing, i noticed
> > many emails on swap_dup/swap_free error messages but i couldn't figure
> > out what should i search for.
>
> Those messages are likely due to a bug with swapping to disk
> that has been in some 2.4 kernels, but I don't believe that is
> the case here, since you don't have a disk or swapping enabled.
>
>
> -- Dan
>
^ permalink raw reply
* [PATCH] 2.6.12.3 Compilation error when CONFIG_PQ2FADS=y
From: Downing, Thomas @ 2005-07-22 13:46 UTC (permalink / raw)
To: linuxppc-embedded
Compilation fails for arch/ppc/syslib/m82xx_pci.c in version
2.6.12.3 when CONFIG_PQ2FADS=3Dy. The following patch fixes the
problems, which are just typographical.
The patch has been tested on PQ2FADS-ZU and -VR boards on 8280
and 8275 devices.
--- linux-2.6.12.3/arch/ppc/syslib/m82xx_pci.c 2005-07-15 =
17:18:57.000000000 -0400
+++ linux-musrum/arch/ppc/syslib/m82xx_pci.c 2005-07-20 =
08:42:41.000000000 -0400
@@ -238,9 +238,9 @@ pq2ads_setup_pci(struct pci_controller *
* Setting required to enable IRQ1-IRQ7 (SIUMCR [DPPC]),
* and local bus for PCI (SIUMCR [LBPC]).
*/
- immap->im_siu_conf.siu_82xx.sc_siumcr =3D =
(immap->im_siu_conf.sc_siumcr &
- ~(SIUMCR_L2PC11 | SIUMCR_LBPC11 | SIUMCR_CS10PC11 | SIUMCR_APPC11) =
|
- SIUMCR_BBD | SIUMCR_LBPC01 | SIUMCR_DPPC11 | SIUMCR_APPC10;
+ immap->im_siu_conf.siu_82xx.sc_siumcr =3D =
(immap->im_siu_conf.siu_82xx.sc_siumcr &
+ ~(SIUMCR_L2CPC11 | SIUMCR_LBPC11 | SIUMCR_CS10PC11 | SIUMCR_APPC11) =
|
+ SIUMCR_BBD | SIUMCR_LBPC01 | SIUMCR_DPPC11 | SIUMCR_APPC10);
#endif
/* Enable PCI */
immap->im_pci.pci_gcr =3D cpu_to_le32(PCIGCR_PCI_BUS_EN);
Thomas Downing
IPC Information Systems, LLC
^ permalink raw reply
* Re: swap_dup: Bad swap file entry 00480020
From: Marcelo Tosatti @ 2005-07-22 12:57 UTC (permalink / raw)
To: bogdan antonovici; +Cc: linuxppc-dev, linuxppc-embedded, ppckernel
In-Reply-To: <1122047198.6726.13.camel@rd-lab>
On Fri, Jul 22, 2005 at 10:46:38AM -0500, bogdan antonovici wrote:
> Hi Dan,
>
> I checked the driver code. I found a pointer that was in my opinion
> initialized too late and i corrected that but other than that i haven't
> found anything.
> I ran the driver alone, enabling the interrupts and the interrupt
> routine doesn't cause any trouble.
> I started my application and i haven't seen any sign of trouble.
> But once i started also the snmpd after few interrupts i got the
> message:
>
> __alloc_pages: 0-order allocation failed (gfp=0x1d2/0)
> VM: killing process sectionmond
Thats a different problem: you ran out of memory and the VM can't swap
out any data.
So its likely that the pagetable corruption is gone (it was indeed a bug
in the driver as Dan suspected).
> sectionmond being my application.
> My read and write driver operation are requesting a page for a buffer
> but they also release it. Should i declare the buffer pointer with
> volatile attribute?
> Do you know what may cause that message?
Out of memory condition.
^ permalink raw reply
* Re: AMCC 440EP (Bamboo board) support?
From: Josh Boyer @ 2005-07-22 11:17 UTC (permalink / raw)
To: Gerhard Jaeger; +Cc: Frank Lautenbach, linuxppc-embedded
In-Reply-To: <200507221202.33820.g.jaeger@sysgo.com>
On Fri, 2005-07-22 at 12:02 +0200, Gerhard Jaeger wrote:
> On Friday 22 July 2005 11:47, Frank Lautenbach wrote:
> > Did anyone bring up linux already on this kind of board? If yes, please
> > let me know. If not, any hints how to start from scratch
> > starting with a kernel tree are higly welcome!
> >
> > Regards,
> > Frank
>
> Yes, works for me here - Kernel 2.6.12 + Wade Farnsworth patches.
> and our own toolchain (see my sig ;)
Speaking of which... when will those patches go upstream? Wade?
josh
^ permalink raw reply
* Re: AMCC 440EP (Bamboo board) support?
From: Gerhard Jaeger @ 2005-07-22 10:02 UTC (permalink / raw)
To: linuxppc-embedded; +Cc: Frank Lautenbach
In-Reply-To: <42E0C0BE.6010807@gmx.de>
On Friday 22 July 2005 11:47, Frank Lautenbach wrote:
> Did anyone bring up linux already on this kind of board? If yes, please
> let me know. If not, any hints how to start from scratch
> starting with a kernel tree are higly welcome!
>
> Regards,
> Frank
Yes, works for me here - Kernel 2.6.12 + Wade Farnsworth patches.
and our own toolchain (see my sig ;)
Ciao,
Gerhard
--
Gerhard Jaeger <gjaeger@sysgo.com>
SYSGO AG Embedded and Real-Time Software
www.sysgo.com | www.elinos.com | www.pikeos.com | www.osek.de
^ permalink raw reply
* AMCC 440EP (Bamboo board) support?
From: Frank Lautenbach @ 2005-07-22 9:47 UTC (permalink / raw)
To: linuxppc-embedded@ozlabs.org
Did anyone bring up linux already on this kind of board? If yes, please
let me know. If not, any hints how to start from scratch
starting with a kernel tree are higly welcome!
Regards,
Frank
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox