diff options
Diffstat (limited to 'drivers/net/ibm_emac/ibm_emac_rgmii.c')
| -rw-r--r-- | drivers/net/ibm_emac/ibm_emac_rgmii.c | 201 | 
1 files changed, 201 insertions, 0 deletions
diff --git a/drivers/net/ibm_emac/ibm_emac_rgmii.c b/drivers/net/ibm_emac/ibm_emac_rgmii.c new file mode 100644 index 00000000000..f0b1ffb2dbb --- /dev/null +++ b/drivers/net/ibm_emac/ibm_emac_rgmii.c @@ -0,0 +1,201 @@ +/* + * drivers/net/ibm_emac/ibm_emac_rgmii.c + * + * Driver for PowerPC 4xx on-chip ethernet controller, RGMII bridge support. + * + * Copyright (c) 2004, 2005 Zultys Technologies. + * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> + * + * Based on original work by + * 	Matt Porter <mporter@kernel.crashing.org> + * 	Copyright 2004 MontaVista Software, 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/ethtool.h> +#include <asm/io.h> + +#include "ibm_emac_core.h" +#include "ibm_emac_debug.h" + +/* RGMIIx_FER */ +#define RGMII_FER_MASK(idx)	(0x7 << ((idx) * 4)) +#define RGMII_FER_RTBI(idx)	(0x4 << ((idx) * 4)) +#define RGMII_FER_RGMII(idx)	(0x5 << ((idx) * 4)) +#define RGMII_FER_TBI(idx)	(0x6 << ((idx) * 4)) +#define RGMII_FER_GMII(idx)	(0x7 << ((idx) * 4)) + +/* RGMIIx_SSR */ +#define RGMII_SSR_MASK(idx)	(0x7 << ((idx) * 8)) +#define RGMII_SSR_100(idx)	(0x2 << ((idx) * 8)) +#define RGMII_SSR_1000(idx)	(0x4 << ((idx) * 8)) + +/* RGMII bridge supports only GMII/TBI and RGMII/RTBI PHYs */ +static inline int rgmii_valid_mode(int phy_mode) +{ +	return  phy_mode == PHY_MODE_GMII || +		phy_mode == PHY_MODE_RGMII || +		phy_mode == PHY_MODE_TBI || +		phy_mode == PHY_MODE_RTBI; +} + +static inline const char *rgmii_mode_name(int mode) +{ +	switch (mode) { +	case PHY_MODE_RGMII: +		return "RGMII"; +	case PHY_MODE_TBI: +		return "TBI"; +	case PHY_MODE_GMII: +		return "GMII"; +	case PHY_MODE_RTBI: +		return "RTBI"; +	default: +		BUG(); +	} +} + +static inline u32 rgmii_mode_mask(int mode, int input) +{ +	switch (mode) { +	case PHY_MODE_RGMII: +		return RGMII_FER_RGMII(input); +	case PHY_MODE_TBI: +		return RGMII_FER_TBI(input); +	case PHY_MODE_GMII: +		return RGMII_FER_GMII(input); +	case PHY_MODE_RTBI: +		return RGMII_FER_RTBI(input); +	default: +		BUG(); +	} +} + +static int __init rgmii_init(struct ocp_device *ocpdev, int input, int mode) +{ +	struct ibm_ocp_rgmii *dev = ocp_get_drvdata(ocpdev); +	struct rgmii_regs *p; + +	RGMII_DBG("%d: init(%d, %d)" NL, ocpdev->def->index, input, mode); + +	if (!dev) { +		dev = kzalloc(sizeof(struct ibm_ocp_rgmii), GFP_KERNEL); +		if (!dev) { +			printk(KERN_ERR +			       "rgmii%d: couldn't allocate device structure!\n", +			       ocpdev->def->index); +			return -ENOMEM; +		} + +		p = (struct rgmii_regs *)ioremap(ocpdev->def->paddr, +						 sizeof(struct rgmii_regs)); +		if (!p) { +			printk(KERN_ERR +			       "rgmii%d: could not ioremap device registers!\n", +			       ocpdev->def->index); +			kfree(dev); +			return -ENOMEM; +		} + +		dev->base = p; +		ocp_set_drvdata(ocpdev, dev); + +		/* Disable all inputs by default */ +		out_be32(&p->fer, 0); +	} else +		p = dev->base; + +	/* Enable this input */ +	out_be32(&p->fer, in_be32(&p->fer) | rgmii_mode_mask(mode, input)); + +	printk(KERN_NOTICE "rgmii%d: input %d in %s mode\n", +	       ocpdev->def->index, input, rgmii_mode_name(mode)); + +	++dev->users; +	return 0; +} + +int __init rgmii_attach(void *emac) +{ +	struct ocp_enet_private *dev = emac; +	struct ocp_func_emac_data *emacdata = dev->def->additions; + +	/* Check if we need to attach to a RGMII */ +	if (emacdata->rgmii_idx >= 0 && rgmii_valid_mode(emacdata->phy_mode)) { +		dev->rgmii_input = emacdata->rgmii_mux; +		dev->rgmii_dev = +		    ocp_find_device(OCP_VENDOR_IBM, OCP_FUNC_RGMII, +				    emacdata->rgmii_idx); +		if (!dev->rgmii_dev) { +			printk(KERN_ERR "emac%d: unknown rgmii%d!\n", +			       dev->def->index, emacdata->rgmii_idx); +			return -ENODEV; +		} +		if (rgmii_init +		    (dev->rgmii_dev, dev->rgmii_input, emacdata->phy_mode)) { +			printk(KERN_ERR +			       "emac%d: rgmii%d initialization failed!\n", +			       dev->def->index, emacdata->rgmii_idx); +			return -ENODEV; +		} +	} +	return 0; +} + +void rgmii_set_speed(struct ocp_device *ocpdev, int input, int speed) +{ +	struct ibm_ocp_rgmii *dev = ocp_get_drvdata(ocpdev); +	u32 ssr = in_be32(&dev->base->ssr) & ~RGMII_SSR_MASK(input); + +	RGMII_DBG("%d: speed(%d, %d)" NL, ocpdev->def->index, input, speed); + +	if (speed == SPEED_1000) +		ssr |= RGMII_SSR_1000(input); +	else if (speed == SPEED_100) +		ssr |= RGMII_SSR_100(input); + +	out_be32(&dev->base->ssr, ssr); +} + +void __exit __rgmii_fini(struct ocp_device *ocpdev, int input) +{ +	struct ibm_ocp_rgmii *dev = ocp_get_drvdata(ocpdev); +	BUG_ON(!dev || dev->users == 0); + +	RGMII_DBG("%d: fini(%d)" NL, ocpdev->def->index, input); + +	/* Disable this input */ +	out_be32(&dev->base->fer, +		 in_be32(&dev->base->fer) & ~RGMII_FER_MASK(input)); + +	if (!--dev->users) { +		/* Free everything if this is the last user */ +		ocp_set_drvdata(ocpdev, NULL); +		iounmap((void *)dev->base); +		kfree(dev); +	} +} + +int __rgmii_get_regs_len(struct ocp_device *ocpdev) +{ +	return sizeof(struct emac_ethtool_regs_subhdr) + +	    sizeof(struct rgmii_regs); +} + +void *rgmii_dump_regs(struct ocp_device *ocpdev, void *buf) +{ +	struct ibm_ocp_rgmii *dev = ocp_get_drvdata(ocpdev); +	struct emac_ethtool_regs_subhdr *hdr = buf; +	struct rgmii_regs *regs = (struct rgmii_regs *)(hdr + 1); + +	hdr->version = 0; +	hdr->index = ocpdev->def->index; +	memcpy_fromio(regs, dev->base, sizeof(struct rgmii_regs)); +	return regs + 1; +}  | 
