Hi Andrew,
After Andrew Victor explained to me how at91rm9200 does hardware
address initialization and phy probing, I decided to give it a go on
avr32 as well. So here's one patch for the avr32 platform code and one
for the actual macb driver which implements those changes.
I suspect that the avr32 platform patches might get messy, so feel free
to drop them all and pull from the master branch of
git://http://www.atmel.no/~hskinnemoen/linux/kernel/avr32.git master
instead. I won't put any new drivers or updates to drivers that are not
in mainline there.
I will still post patches for the avr32 platform code just to show how
things are affecting the platform-specific bits.
Haavard
Initialize the mac address and mii phy id like at91_ether does. This
means that we read the initial mac address from the MACB SA1B/SA1T
registers and refuse to open() if the address hasn't been set. Also,
instead of getting the mii phy id from platform_data, we probe all
possible phy addresses until we get something that looks like a sane
response.
While we're at it, initialize the MII bit according to the is_rmii
flag in platform_data (it was misnamed and hardcoded on before.)
If no platform_data is available, default to MII mode.
Signed-off-by: Haavard Skinnemoen <[email protected]>
---
drivers/net/macb.c | 109 ++++++++++++++++++++++++++++++++++++++---------------
drivers/net/macb.h | 4 -
2 files changed, 82 insertions(+), 31 deletions(-)
Index: linux-2.6.19-rc5-mm1/drivers/net/macb.c
===================================================================
--- linux-2.6.19-rc5-mm1.orig/drivers/net/macb.c 2006-11-08 18:11:06.000000000 +0100
+++ linux-2.6.19-rc5-mm1/drivers/net/macb.c 2006-11-08 20:12:44.000000000 +0100
@@ -65,6 +65,26 @@ static void __macb_set_hwaddr(struct mac
macb_writel(bp, SA1T, top);
}
+static void __init macb_get_hwaddr(struct macb *bp)
+{
+ u32 bottom;
+ u16 top;
+ u8 addr[6];
+
+ bottom = macb_readl(bp, SA1B);
+ top = macb_readl(bp, SA1T);
+
+ addr[0] = bottom & 0xff;
+ addr[1] = (bottom >> 8) & 0xff;
+ addr[2] = (bottom >> 16) & 0xff;
+ addr[3] = (bottom >> 24) & 0xff;
+ addr[4] = top & 0xff;
+ addr[5] = (top >> 8) & 0xff;
+
+ if (is_valid_ether_addr(addr))
+ memcpy(bp->dev->dev_addr, addr, sizeof(addr));
+}
+
static void macb_enable_mdio(struct macb *bp)
{
unsigned long flags;
@@ -138,6 +158,31 @@ static void macb_mdio_write(struct net_d
mutex_unlock(&bp->mdio_mutex);
}
+static int macb_phy_probe(struct macb *bp)
+{
+ int phy_address;
+ u16 phyid1, phyid2;
+
+ for (phy_address = 0; phy_address < 32; phy_address++) {
+ phyid1 = macb_mdio_read(bp->dev, phy_address, MII_PHYSID1);
+ phyid2 = macb_mdio_read(bp->dev, phy_address, MII_PHYSID2);
+
+ if (phyid1 != 0xffff && phyid1 != 0x0000
+ && phyid2 != 0xffff && phyid2 != 0x0000)
+ break;
+ }
+
+ if (phy_address == 32)
+ return -ENODEV;
+
+ dev_info(&bp->pdev->dev,
+ "detected PHY at address %d (ID %04x:%04x)\n",
+ phy_address, phyid1, phyid2);
+
+ bp->mii.phy_id = phy_address;
+ return 0;
+}
+
static void macb_set_media(struct macb *bp, int media)
{
u32 reg;
@@ -473,17 +518,16 @@ static irqreturn_t macb_interrupt(int ir
spin_lock(&bp->lock);
- /* close possible race with dev_close */
- if (unlikely(!netif_running(dev))) {
- macb_writel(bp, IDR, ~0UL);
- spin_unlock(&bp->lock);
- return IRQ_HANDLED;
- }
-
while (status) {
if (status & MACB_BIT(MFD))
complete(&bp->mdio_complete);
+ /* close possible race with dev_close */
+ if (unlikely(!netif_running(dev))) {
+ macb_writel(bp, IDR, ~0UL);
+ break;
+ }
+
if (status & MACB_RX_INT_FLAGS) {
if (netif_rx_schedule_prep(dev)) {
/*
@@ -701,26 +745,12 @@ static void macb_reset_hw(struct macb *b
static void macb_init_hw(struct macb *bp)
{
- unsigned long pclk_hz;
u32 config;
macb_reset_hw(bp);
__macb_set_hwaddr(bp);
- /* Set RMII mode */
- macb_writel(bp, USRIO, MACB_BIT(RMII));
-
- /* Initialize Network Configuration Register */
- pclk_hz = clk_get_rate(bp->pclk);
- if (pclk_hz <= 20000000)
- config = MACB_BF(CLK, MACB_CLK_DIV8);
- else if (pclk_hz <= 40000000)
- config = MACB_BF(CLK, MACB_CLK_DIV16);
- else if (pclk_hz <= 80000000)
- config = MACB_BF(CLK, MACB_CLK_DIV32);
- else
- config = MACB_BF(CLK, MACB_CLK_DIV64);
-
+ config = macb_readl(bp, NCFGR) & MACB_BF(CLK, -1L);
config |= MACB_BIT(PAE); /* PAuse Enable */
config |= MACB_BIT(DRFCS); /* Discard Rx FCS */
if (bp->dev->flags & IFF_PROMISC)
@@ -766,6 +796,9 @@ static int macb_open(struct net_device *
dev_dbg(&bp->pdev->dev, "open\n");
+ if (!is_valid_ether_addr(dev->dev_addr))
+ return -EADDRNOTAVAIL;
+
err = macb_alloc_consistent(bp);
if (err) {
printk(KERN_ERR
@@ -984,6 +1017,8 @@ static int __devinit macb_probe(struct p
struct resource *regs;
struct net_device *dev;
struct macb *bp;
+ unsigned long pclk_hz;
+ u32 config;
int err = -ENXIO;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1057,20 +1092,36 @@ static int __devinit macb_probe(struct p
mutex_init(&bp->mdio_mutex);
init_completion(&bp->mdio_complete);
+ /* Set MII management clock divider */
+ pclk_hz = clk_get_rate(bp->pclk);
+ if (pclk_hz <= 20000000)
+ config = MACB_BF(CLK, MACB_CLK_DIV8);
+ else if (pclk_hz <= 40000000)
+ config = MACB_BF(CLK, MACB_CLK_DIV16);
+ else if (pclk_hz <= 80000000)
+ config = MACB_BF(CLK, MACB_CLK_DIV32);
+ else
+ config = MACB_BF(CLK, MACB_CLK_DIV64);
+ macb_writel(bp, NCFGR, config);
+
bp->mii.dev = dev;
bp->mii.mdio_read = macb_mdio_read;
bp->mii.mdio_write = macb_mdio_write;
+ bp->mii.phy_id_mask = 0x1f;
+ bp->mii.reg_num_mask = 0x1f;
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "Cannot determine hw address\n");
+ macb_get_hwaddr(bp);
+ err = macb_phy_probe(bp);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to detect PHY, aborting.\n");
goto err_out_free_irq;
}
- memcpy(dev->dev_addr, pdata->hw_addr, dev->addr_len);
- bp->mii.phy_id = pdata->mii_phy_addr;
- bp->mii.phy_id_mask = 0x1f;
- bp->mii.reg_num_mask = 0x1f;
+ pdata = pdev->dev.platform_data;
+ if (pdata && pdata->is_rmii)
+ macb_writel(bp, USRIO, 0);
+ else
+ macb_writel(bp, USRIO, MACB_BIT(MII));
bp->tx_pending = DEF_TX_RING_PENDING;
Index: linux-2.6.19-rc5-mm1/drivers/net/macb.h
===================================================================
--- linux-2.6.19-rc5-mm1.orig/drivers/net/macb.h 2006-11-08 18:11:06.000000000 +0100
+++ linux-2.6.19-rc5-mm1/drivers/net/macb.h 2006-11-08 20:12:44.000000000 +0100
@@ -201,8 +201,8 @@
#define MACB_SOF_SIZE 2
/* Bitfields in USRIO */
-#define MACB_RMII_OFFSET 0
-#define MACB_RMII_SIZE 1
+#define MACB_MII_OFFSET 0
+#define MACB_MII_SIZE 1
#define MACB_EAM_OFFSET 1
#define MACB_EAM_SIZE 1
#define MACB_TX_PAUSE_OFFSET 2
The macb driver will probe for the PHY chip and read the mac address
from the MACB registers, so we don't need them in eth_platform_data
anymore.
Since u-boot doesn't currently initialize the MACB registers with the
mac addresses, the tag parsing code is kept but instead of sticking
the information into eth_platform_data, it uses it to initialize
the MACB registers (in case the boot loader didn't do it.) This code
should be unnecessary at some point in the future.
Signed-off-by: Haavard Skinnemoen <[email protected]>
---
arch/avr32/boards/atstk1000/atstk1002.c | 65 ++++++++++++++++++++++++++++----
include/asm-avr32/arch-at32ap/board.h | 3 -
2 files changed, 57 insertions(+), 11 deletions(-)
Index: linux-2.6.19-rc5-mm1/arch/avr32/boards/atstk1000/atstk1002.c
===================================================================
--- linux-2.6.19-rc5-mm1.orig/arch/avr32/boards/atstk1000/atstk1002.c 2006-11-08 18:12:13.000000000 +0100
+++ linux-2.6.19-rc5-mm1/arch/avr32/boards/atstk1000/atstk1002.c 2006-11-08 20:09:46.000000000 +0100
@@ -7,13 +7,17 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#include <linux/clk.h>
#include <linux/device.h>
+#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/spi/spi.h>
+#include <asm/io.h>
#include <asm/setup.h>
#include <asm/arch/at32ap7000.h>
#include <asm/arch/board.h>
@@ -36,24 +40,70 @@ static struct spi_board_info spi_board_i
},
};
+struct eth_addr {
+ u8 addr[6];
+};
+
+static struct eth_addr __initdata hw_addr[2];
+
struct eth_platform_data __initdata eth_data[2];
extern struct lcdc_platform_data atstk1000_fb0_data;
+/*
+ * The next two functions should go away as the boot loader is
+ * supposed to initialize the macb address registers with a valid
+ * ethernet address. But we need to keep it around for a while until
+ * we can be reasonably sure the boot loader does this.
+ *
+ * The phy_id is ignored as the driver will probe for it.
+ */
static int __init parse_tag_ethernet(struct tag *tag)
{
int i;
i = tag->u.ethernet.mac_index;
- if (i < ARRAY_SIZE(eth_data)) {
- eth_data[i].mii_phy_addr = tag->u.ethernet.mii_phy_addr;
- memcpy(ð_data[i].hw_addr, tag->u.ethernet.hw_address,
- sizeof(eth_data[i].hw_addr));
- eth_data[i].valid = 1;
- }
+ if (i < ARRAY_SIZE(hw_addr))
+ memcpy(hw_addr[i].addr, tag->u.ethernet.hw_address,
+ sizeof(hw_addr[i].addr));
+
return 0;
}
__tagtable(ATAG_ETHERNET, parse_tag_ethernet);
+static void __init set_hw_addr(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ const u8 *addr;
+ void __iomem *regs;
+ struct clk *pclk;
+
+ if (!res)
+ return;
+ if (pdev->id >= ARRAY_SIZE(hw_addr))
+ return;
+
+ addr = hw_addr[pdev->id].addr;
+ if (!is_valid_ether_addr(addr))
+ return;
+
+ /*
+ * Since this is board-specific code, we'll cheat and use the
+ * physical address directly as we happen to know that it's
+ * the same as the virtual address.
+ */
+ regs = (void __iomem __force *)res->start;
+ pclk = clk_get(&pdev->dev, "pclk");
+ if (!pclk)
+ return;
+
+ clk_enable(pclk);
+ __raw_writel((addr[3] << 24) | (addr[2] << 16)
+ | (addr[1] << 8) | addr[0], regs + 0x98);
+ __raw_writel((addr[5] << 8) | addr[4], regs + 0x9c);
+ clk_disable(pclk);
+ clk_put(pclk);
+}
+
void __init setup_board(void)
{
at32_map_usart(1, 0); /* /dev/ttyS0 */
@@ -71,8 +121,7 @@ static int __init atstk1002_init(void)
at32_add_device_usart(1);
at32_add_device_usart(2);
- if (eth_data[0].valid)
- at32_add_device_eth(0, ð_data[0]);
+ set_hw_addr(at32_add_device_eth(0, ð_data[0]));
spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
at32_add_device_spi(0);
Index: linux-2.6.19-rc5-mm1/include/asm-avr32/arch-at32ap/board.h
===================================================================
--- linux-2.6.19-rc5-mm1.orig/include/asm-avr32/arch-at32ap/board.h 2006-11-08 18:10:35.000000000 +0100
+++ linux-2.6.19-rc5-mm1/include/asm-avr32/arch-at32ap/board.h 2006-11-08 20:05:15.000000000 +0100
@@ -21,10 +21,7 @@ void at32_map_usart(unsigned int hw_id,
struct platform_device *at32_add_device_usart(unsigned int id);
struct eth_platform_data {
- u8 valid;
- u8 mii_phy_addr;
u8 is_rmii;
- u8 hw_addr[6];
};
struct platform_device *
at32_add_device_eth(unsigned int id, struct eth_platform_data *data);
On Wed, 8 Nov 2006 20:33:58 +0100
Haavard Skinnemoen <[email protected]> wrote:
> Hi Andrew,
>
> After Andrew Victor explained to me how at91rm9200 does hardware
> address initialization and phy probing, I decided to give it a go on
> avr32 as well. So here's one patch for the avr32 platform code and one
> for the actual macb driver which implements those changes.
>
> I suspect that the avr32 platform patches might get messy, so feel free
> to drop them all
You'll need to be a lot more specific than "platform patches" and "them all".
Patches have names. I currently have
gpio-framework-for-avr32.patch
avr32-spi-ethernet-platform_device-update.patch
avr32-move-spi-device-definitions-into-main-board.patch
atmel-spi-driver.patch
atmel-spi-driver-maintainers-entry.patch
avr32-move-ethernet-tag-parsing-to-board-specific.patch
atmel-macb-ethernet-driver.patch
adapt-macb-driver-to-net_device-changes.patch
I'd prefer to drop the lot, but we do have those SPI patches which David
needs to see.
> and pull from the master branch of
>
> git://http://www.atmel.no/~hskinnemoen/linux/kernel/avr32.git master
>
> instead. I won't put any new drivers or updates to drivers that are not
> in mainline there.
>
> I will still post patches for the avr32 platform code just to show how
> things are affecting the platform-specific bits.
>
So in fact I do think I'd prefer to drop everything. How about
a) you sort out the SPI patches with David, send them over to me when
it's ready and
b) everything else goes into Linus from your git tree, and I include
your git tree in -mm?
(I hope that tree works, btw - for some reason it seems that any git tree
which isn't on kernel.org is down half the time).
On 11/8/06, Andrew Morton <[email protected]> wrote:
> On Wed, 8 Nov 2006 20:33:58 +0100
> Haavard Skinnemoen <[email protected]> wrote:
> Patches have names. I currently have
Sorry. I meant that you can drop these:
> gpio-framework-for-avr32.patch
> avr32-spi-ethernet-platform_device-update.patch
> avr32-move-spi-device-definitions-into-main-board.patch
> avr32-move-ethernet-tag-parsing-to-board-specific.patch
and keep these:
> atmel-spi-driver.patch
> atmel-spi-driver-maintainers-entry.patch
> atmel-macb-ethernet-driver.patch
> adapt-macb-driver-to-net_device-changes.patch
>
> I'd prefer to drop the lot, but we do have those SPI patches which David
> needs to see.
Ok, just do that. We need to get the GPIO stuff sorted out before the
SPI driver can be considered final.
> So in fact I do think I'd prefer to drop everything. How about
>
> a) you sort out the SPI patches with David, send them over to me when
> it's ready and
>
> b) everything else goes into Linus from your git tree, and I include
> your git tree in -mm?
Fine with me. Sorry for pushing this mess to you. I'll post a new macb
patch for review to the netdev list tomorrow.
> (I hope that tree works, btw - for some reason it seems that any git tree
> which isn't on kernel.org is down half the time).
I hope so too. I'll pull from it myself from time to see how it works.
Haavard