2 * ADM6996 switch driver
4 * Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License v2 as published by the
8 * Free Software Foundation
10 #include <linux/kernel.h>
11 #include <linux/string.h>
12 #include <linux/errno.h>
13 #include <linux/unistd.h>
14 #include <linux/slab.h>
15 #include <linux/interrupt.h>
16 #include <linux/init.h>
17 #include <linux/delay.h>
18 #include <linux/netdevice.h>
19 #include <linux/etherdevice.h>
20 #include <linux/skbuff.h>
21 #include <linux/spinlock.h>
23 #include <linux/module.h>
24 #include <linux/mii.h>
25 #include <linux/ethtool.h>
26 #include <linux/phy.h>
30 #include <asm/uaccess.h>
33 MODULE_DESCRIPTION("Infineon ADM6996 Switch");
34 MODULE_AUTHOR("Felix Fietkau");
35 MODULE_LICENSE("GPL");
38 /* use abstraction for regops, we want to add gpio support in the future */
39 u16 (*read
)(struct phy_device
*phydev
, enum admreg reg
);
40 void (*write
)(struct phy_device
*phydev
, enum admreg reg
, u16 val
);
43 #define to_adm(_phy) ((struct adm6996_priv *) (_phy)->priv)
47 r16(struct phy_device
*pdev
, enum admreg reg
)
49 return to_adm(pdev
)->read(pdev
, reg
);
53 w16(struct phy_device
*pdev
, enum admreg reg
, u16 val
)
55 to_adm(pdev
)->write(pdev
, reg
, val
);
60 adm6996_read_mii_reg(struct phy_device
*phydev
, enum admreg reg
)
62 return phydev
->bus
->read(phydev
->bus
, PHYADDR(reg
));
66 adm6996_write_mii_reg(struct phy_device
*phydev
, enum admreg reg
, u16 val
)
68 phydev
->bus
->write(phydev
->bus
, PHYADDR(reg
), val
);
72 static int adm6996_config_init(struct phy_device
*pdev
)
76 printk("%s: ADM6996 PHY driver attached.\n", pdev
->attached_dev
->name
);
77 pdev
->supported
= ADVERTISED_100baseT_Full
;
78 pdev
->advertising
= ADVERTISED_100baseT_Full
;
80 /* initialize port and vlan settings */
81 for (i
= 0; i
< ADM_PHY_PORTS
; i
++) {
82 w16(pdev
, adm_portcfg
[i
], ADM_PORTCFG_INIT
|
83 ADM_PORTCFG_PVID((i
== ADM_WAN_PORT
) ? 1 : 0));
85 w16(pdev
, adm_portcfg
[5], ADM_PORTCFG_CPU
);
88 for (i
= 0; i
< ADM_PHY_PORTS
; i
++) {
89 w16(pdev
, ADM_PHY_PORT(i
), ADM_PHYCFG_INIT
);
95 static int adm6996_read_status(struct phy_device
*phydev
)
97 phydev
->speed
= SPEED_100
;
98 phydev
->duplex
= DUPLEX_FULL
;
103 static int adm6996_config_aneg(struct phy_device
*phydev
)
108 static int adm6996_fixup(struct phy_device
*dev
)
110 struct mii_bus
*bus
= dev
->bus
;
113 /* look for the switch on the bus */
114 reg
= bus
->read(bus
, PHYADDR(ADM_SIG0
)) & ADM_SIG0_MASK
;
115 if (reg
!= ADM_SIG0_VAL
)
118 reg
= bus
->read(bus
, PHYADDR(ADM_SIG1
)) & ADM_SIG1_MASK
;
119 if (reg
!= ADM_SIG1_VAL
)
122 dev
->phy_id
= (ADM_SIG0_VAL
<< 16) | ADM_SIG1_VAL
;
126 static int adm6996_probe(struct phy_device
*pdev
)
128 struct adm6996_priv
*priv
;
130 priv
= kzalloc(sizeof(struct adm6996_priv
), GFP_KERNEL
);
134 priv
->read
= adm6996_read_mii_reg
;
135 priv
->write
= adm6996_write_mii_reg
;
140 static void adm6996_remove(struct phy_device
*pdev
)
146 static struct phy_driver adm6996_driver
= {
147 .name
= "Infineon ADM6996",
148 .phy_id
= (ADM_SIG0_VAL
<< 16) | ADM_SIG1_VAL
,
149 .phy_id_mask
= 0xffffffff,
150 .features
= PHY_BASIC_FEATURES
,
151 .probe
= adm6996_probe
,
152 .remove
= adm6996_remove
,
153 .config_init
= &adm6996_config_init
,
154 .config_aneg
= &adm6996_config_aneg
,
155 .read_status
= &adm6996_read_status
,
156 .driver
= { .owner
= THIS_MODULE
,},
159 static int __init
adm6996_init(void)
161 phy_register_fixup_for_id(PHY_ANY_ID
, adm6996_fixup
);
162 return phy_driver_register(&adm6996_driver
);
165 static void __exit
adm6996_exit(void)
167 phy_driver_unregister(&adm6996_driver
);
170 module_init(adm6996_init
);
171 module_exit(adm6996_exit
);