100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming/*
200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming * drivers/net/phy/davicom.c
300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming *
400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming * Driver for Davicom PHYs
500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming *
600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming * Author: Andy Fleming
700db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming *
800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming * Copyright (c) 2004 Freescale Semiconductor, Inc.
900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming *
1000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming * This program is free software; you can redistribute  it and/or modify it
1100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming * under  the terms of  the GNU General  Public License as published by the
1200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming * Free Software Foundation;  either version 2 of the  License, or (at your
1300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming * option) any later version.
1400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming *
1500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming */
1600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/kernel.h>
1700db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/string.h>
1800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/errno.h>
1900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/unistd.h>
2000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/interrupt.h>
2100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/init.h>
2200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/delay.h>
2300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/netdevice.h>
2400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/etherdevice.h>
2500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/skbuff.h>
2600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/spinlock.h>
2700db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/mm.h>
2800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/module.h>
2900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/mii.h>
3000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/ethtool.h>
3100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <linux/phy.h>
3200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
3300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <asm/io.h>
3400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <asm/irq.h>
3500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#include <asm/uaccess.h>
3600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
3700db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_SCR		0x10
3800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_SCR_INIT	0x0610
398b7c1664405a1262bb78e80011ec10aa6b79df81frederic Rodo#define MII_DM9161_SCR_RMII	0x0100
4000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
4100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming/* DM9161 Interrupt Register */
4200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_INTR	0x15
4300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_INTR_PEND		0x8000
4400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_INTR_DPLX_MASK	0x0800
4500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_INTR_SPD_MASK	0x0400
4600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_INTR_LINK_MASK	0x0200
4700db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_INTR_MASK		0x0100
4800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_INTR_DPLX_CHANGE	0x0010
4900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_INTR_SPD_CHANGE	0x0008
5000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_INTR_LINK_CHANGE	0x0004
5100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_INTR_INIT 		0x0000
5200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_INTR_STOP	\
5300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \
5400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
5500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
5600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming/* DM9161 10BT Configuration/Status */
5700db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_10BTCSR	0x12
5800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define MII_DM9161_10BTCSR_INIT	0x7800
5900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
6000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy FlemingMODULE_DESCRIPTION("Davicom PHY driver");
6100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy FlemingMODULE_AUTHOR("Andy Fleming");
6200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy FlemingMODULE_LICENSE("GPL");
6300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
6400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
6500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming#define DM9161_DELAY 1
6600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Flemingstatic int dm9161_config_intr(struct phy_device *phydev)
6700db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming{
6800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	int temp;
6900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
7000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	temp = phy_read(phydev, MII_DM9161_INTR);
7100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
7200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	if (temp < 0)
7300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming		return temp;
7400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
75a60e7e1e657b9458c3f44681fe81f03a85acfc20Florian Fainelli	if (PHY_INTERRUPT_ENABLED == phydev->interrupts)
7600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming		temp &= ~(MII_DM9161_INTR_STOP);
7700db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	else
7800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming		temp |= MII_DM9161_INTR_STOP;
7900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
8000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	temp = phy_write(phydev, MII_DM9161_INTR, temp);
8100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
8200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	return temp;
8300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming}
8400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
8500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Flemingstatic int dm9161_config_aneg(struct phy_device *phydev)
8600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming{
8700db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	int err;
8800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
8900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	/* Isolate the PHY */
9000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
9100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
9200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	if (err < 0)
9300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming		return err;
9400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
9500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	/* Configure the new settings */
9600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	err = genphy_config_aneg(phydev);
9700db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
9800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	if (err < 0)
9900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming		return err;
10000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
10100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	return 0;
10200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming}
10300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
10400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Flemingstatic int dm9161_config_init(struct phy_device *phydev)
10500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming{
1068b7c1664405a1262bb78e80011ec10aa6b79df81frederic Rodo	int err, temp;
10700db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
10800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	/* Isolate the PHY */
10900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	err = phy_write(phydev, MII_BMCR, BMCR_ISOLATE);
11000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
11100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	if (err < 0)
11200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming		return err;
11300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
1148b7c1664405a1262bb78e80011ec10aa6b79df81frederic Rodo	switch (phydev->interface) {
1158b7c1664405a1262bb78e80011ec10aa6b79df81frederic Rodo	case PHY_INTERFACE_MODE_MII:
1168b7c1664405a1262bb78e80011ec10aa6b79df81frederic Rodo		temp = MII_DM9161_SCR_INIT;
1178b7c1664405a1262bb78e80011ec10aa6b79df81frederic Rodo		break;
1188b7c1664405a1262bb78e80011ec10aa6b79df81frederic Rodo	case PHY_INTERFACE_MODE_RMII:
1198b7c1664405a1262bb78e80011ec10aa6b79df81frederic Rodo		temp =  MII_DM9161_SCR_INIT | MII_DM9161_SCR_RMII;
1208b7c1664405a1262bb78e80011ec10aa6b79df81frederic Rodo		break;
1218b7c1664405a1262bb78e80011ec10aa6b79df81frederic Rodo	default:
1228b7c1664405a1262bb78e80011ec10aa6b79df81frederic Rodo		return -EINVAL;
1238b7c1664405a1262bb78e80011ec10aa6b79df81frederic Rodo	}
12400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
1258b7c1664405a1262bb78e80011ec10aa6b79df81frederic Rodo	/* Do not bypass the scrambler/descrambler */
1268b7c1664405a1262bb78e80011ec10aa6b79df81frederic Rodo	err = phy_write(phydev, MII_DM9161_SCR, temp);
12700db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	if (err < 0)
12800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming		return err;
12900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
13000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	/* Clear 10BTCSR to default */
13100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	err = phy_write(phydev, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
13200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
13300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	if (err < 0)
13400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming		return err;
13500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
13600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	/* Reconnect the PHY, and enable Autonegotiation */
1378bc47ec6efd4d447f3502d9f9d41fe440cd5854dSrinivas Kandagatla	return phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
13800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming}
13900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
14000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Flemingstatic int dm9161_ack_interrupt(struct phy_device *phydev)
14100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming{
14200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	int err = phy_read(phydev, MII_DM9161_INTR);
14300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
14400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	return (err < 0) ? err : 0;
14500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming}
14600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
147d5bf9071e71a4db85a0eea6236ef94a29fc3eec9Christian Hohnstaedtstatic struct phy_driver dm91xx_driver[] = {
148d5bf9071e71a4db85a0eea6236ef94a29fc3eec9Christian Hohnstaedt{
14900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.phy_id		= 0x0181b880,
15000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.name		= "Davicom DM9161E",
15100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.phy_id_mask	= 0x0ffffff0,
15200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.features	= PHY_BASIC_FEATURES,
15363f71dd026cc6b2cc1cc26299720d763269f65b6Joachim Eastwood	.flags		= PHY_HAS_INTERRUPT,
15400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.config_init	= dm9161_config_init,
15500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.config_aneg	= dm9161_config_aneg,
15600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.read_status	= genphy_read_status,
15763f71dd026cc6b2cc1cc26299720d763269f65b6Joachim Eastwood	.ack_interrupt	= dm9161_ack_interrupt,
15863f71dd026cc6b2cc1cc26299720d763269f65b6Joachim Eastwood	.config_intr	= dm9161_config_intr,
15912414db11da8789d9a6b549c4899250f6e116630Kim Phillips	.driver		= { .owner = THIS_MODULE,},
160d5bf9071e71a4db85a0eea6236ef94a29fc3eec9Christian Hohnstaedt}, {
16112414db11da8789d9a6b549c4899250f6e116630Kim Phillips	.phy_id		= 0x0181b8a0,
16212414db11da8789d9a6b549c4899250f6e116630Kim Phillips	.name		= "Davicom DM9161A",
16312414db11da8789d9a6b549c4899250f6e116630Kim Phillips	.phy_id_mask	= 0x0ffffff0,
16412414db11da8789d9a6b549c4899250f6e116630Kim Phillips	.features	= PHY_BASIC_FEATURES,
16563f71dd026cc6b2cc1cc26299720d763269f65b6Joachim Eastwood	.flags		= PHY_HAS_INTERRUPT,
16612414db11da8789d9a6b549c4899250f6e116630Kim Phillips	.config_init	= dm9161_config_init,
16712414db11da8789d9a6b549c4899250f6e116630Kim Phillips	.config_aneg	= dm9161_config_aneg,
16812414db11da8789d9a6b549c4899250f6e116630Kim Phillips	.read_status	= genphy_read_status,
16963f71dd026cc6b2cc1cc26299720d763269f65b6Joachim Eastwood	.ack_interrupt	= dm9161_ack_interrupt,
17063f71dd026cc6b2cc1cc26299720d763269f65b6Joachim Eastwood	.config_intr	= dm9161_config_intr,
17112414db11da8789d9a6b549c4899250f6e116630Kim Phillips	.driver		= { .owner = THIS_MODULE,},
172d5bf9071e71a4db85a0eea6236ef94a29fc3eec9Christian Hohnstaedt}, {
17300db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.phy_id		= 0x00181b80,
17400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.name		= "Davicom DM9131",
17500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.phy_id_mask	= 0x0ffffff0,
17600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.features	= PHY_BASIC_FEATURES,
17700db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.flags		= PHY_HAS_INTERRUPT,
17800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.config_aneg	= genphy_config_aneg,
17900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.read_status	= genphy_read_status,
18000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.ack_interrupt	= dm9161_ack_interrupt,
18100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming	.config_intr	= dm9161_config_intr,
18212414db11da8789d9a6b549c4899250f6e116630Kim Phillips	.driver		= { .owner = THIS_MODULE,},
183d5bf9071e71a4db85a0eea6236ef94a29fc3eec9Christian Hohnstaedt} };
18400db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
18500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Flemingstatic int __init davicom_init(void)
18600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming{
187d5bf9071e71a4db85a0eea6236ef94a29fc3eec9Christian Hohnstaedt	return phy_drivers_register(dm91xx_driver,
188d5bf9071e71a4db85a0eea6236ef94a29fc3eec9Christian Hohnstaedt		ARRAY_SIZE(dm91xx_driver));
18900db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming}
19000db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
19100db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Flemingstatic void __exit davicom_exit(void)
19200db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming{
193d5bf9071e71a4db85a0eea6236ef94a29fc3eec9Christian Hohnstaedt	phy_drivers_unregister(dm91xx_driver,
194d5bf9071e71a4db85a0eea6236ef94a29fc3eec9Christian Hohnstaedt		ARRAY_SIZE(dm91xx_driver));
19500db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming}
19600db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Fleming
19700db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Flemingmodule_init(davicom_init);
19800db8189d984d6c51226dafbbe4a667ce9b7d5daAndy Flemingmodule_exit(davicom_exit);
1994e4f10f6498bc5038c0a110b5f21682fcb5578d7David Woodhouse
200cf93c94581bab447a5634c6d737c1cf38c080261Uwe Kleine-Königstatic struct mdio_device_id __maybe_unused davicom_tbl[] = {
2014e4f10f6498bc5038c0a110b5f21682fcb5578d7David Woodhouse	{ 0x0181b880, 0x0ffffff0 },
2024e4f10f6498bc5038c0a110b5f21682fcb5578d7David Woodhouse	{ 0x0181b8a0, 0x0ffffff0 },
2034e4f10f6498bc5038c0a110b5f21682fcb5578d7David Woodhouse	{ 0x00181b80, 0x0ffffff0 },
2044e4f10f6498bc5038c0a110b5f21682fcb5578d7David Woodhouse	{ }
2054e4f10f6498bc5038c0a110b5f21682fcb5578d7David Woodhouse};
2064e4f10f6498bc5038c0a110b5f21682fcb5578d7David Woodhouse
2074e4f10f6498bc5038c0a110b5f21682fcb5578d7David WoodhouseMODULE_DEVICE_TABLE(mdio, davicom_tbl);
208