Root/target/linux/generic/files/drivers/net/phy/psb6970.c

1/*
2 * Lantiq PSB6970 (Tantos) Switch driver
3 *
4 * Copyright (c) 2009,2010 Team Embedded.
5 *
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.
9 *
10 * The switch programming done in this driver follows the
11 * "Ethernet Traffic Separation using VLAN" Application Note as
12 * published by Lantiq.
13 */
14
15#include <linux/module.h>
16#include <linux/netdevice.h>
17#include <linux/switch.h>
18#include <linux/phy.h>
19
20#define PSB6970_MAX_VLANS 16
21#define PSB6970_NUM_PORTS 7
22#define PSB6970_DEFAULT_PORT_CPU 6
23#define PSB6970_IS_CPU_PORT(x) ((x) > 4)
24
25#define PHYADDR(_reg) ((_reg >> 5) & 0xff), (_reg & 0x1f)
26
27/* --- Identification --- */
28#define PSB6970_CI0 0x0100
29#define PSB6970_CI0_MASK 0x000f
30#define PSB6970_CI1 0x0101
31#define PSB6970_CI1_VAL 0x2599
32#define PSB6970_CI1_MASK 0xffff
33
34/* --- VLAN filter table --- */
35#define PSB6970_VFxL(i) ((i)*2+0x10) /* VLAN Filter Low */
36#define PSB6970_VFxL_VV (1 << 15) /* VLAN_Valid */
37
38#define PSB6970_VFxH(i) ((i)*2+0x11) /* VLAN Filter High */
39#define PSB6970_VFxH_TM_SHIFT 7 /* Tagged Member */
40
41/* --- Port registers --- */
42#define PSB6970_EC(p) ((p)*0x20+2) /* Extended Control */
43#define PSB6970_EC_IFNTE (1 << 1) /* Input Force No Tag Enable */
44
45#define PSB6970_PBVM(p) ((p)*0x20+3) /* Port Base VLAN Map */
46#define PSB6970_PBVM_VMCE (1 << 8)
47#define PSB6970_PBVM_AOVTP (1 << 9)
48#define PSB6970_PBVM_VSD (1 << 10)
49#define PSB6970_PBVM_VC (1 << 11) /* VID Check with VID table */
50#define PSB6970_PBVM_TBVE (1 << 13) /* Tag-Based VLAN enable */
51
52#define PSB6970_DVID(p) ((p)*0x20+4) /* Default VLAN ID & Priority */
53
54struct psb6970_priv {
55    struct switch_dev dev;
56    struct phy_device *phy;
57    u16 (*read) (struct phy_device* phydev, int reg);
58    void (*write) (struct phy_device* phydev, int reg, u16 val);
59    struct mutex reg_mutex;
60
61    /* all fields below are cleared on reset */
62    bool vlan;
63    u16 vlan_id[PSB6970_MAX_VLANS];
64    u8 vlan_table[PSB6970_MAX_VLANS];
65    u8 vlan_tagged;
66    u16 pvid[PSB6970_NUM_PORTS];
67};
68
69#define to_psb6970(_dev) container_of(_dev, struct psb6970_priv, dev)
70
71static u16 psb6970_mii_read(struct phy_device *phydev, int reg)
72{
73    return phydev->bus->read(phydev->bus, PHYADDR(reg));
74}
75
76static void psb6970_mii_write(struct phy_device *phydev, int reg, u16 val)
77{
78    phydev->bus->write(phydev->bus, PHYADDR(reg), val);
79}
80
81static int
82psb6970_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
83         struct switch_val *val)
84{
85    struct psb6970_priv *priv = to_psb6970(dev);
86    priv->vlan = !!val->value.i;
87    return 0;
88}
89
90static int
91psb6970_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
92         struct switch_val *val)
93{
94    struct psb6970_priv *priv = to_psb6970(dev);
95    val->value.i = priv->vlan;
96    return 0;
97}
98
99static int psb6970_set_pvid(struct switch_dev *dev, int port, int vlan)
100{
101    struct psb6970_priv *priv = to_psb6970(dev);
102
103    /* make sure no invalid PVIDs get set */
104    if (vlan >= dev->vlans)
105        return -EINVAL;
106
107    priv->pvid[port] = vlan;
108    return 0;
109}
110
111static int psb6970_get_pvid(struct switch_dev *dev, int port, int *vlan)
112{
113    struct psb6970_priv *priv = to_psb6970(dev);
114    *vlan = priv->pvid[port];
115    return 0;
116}
117
118static int
119psb6970_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
120        struct switch_val *val)
121{
122    struct psb6970_priv *priv = to_psb6970(dev);
123    priv->vlan_id[val->port_vlan] = val->value.i;
124    return 0;
125}
126
127static int
128psb6970_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
129        struct switch_val *val)
130{
131    struct psb6970_priv *priv = to_psb6970(dev);
132    val->value.i = priv->vlan_id[val->port_vlan];
133    return 0;
134}
135
136static struct switch_attr psb6970_globals[] = {
137    {
138     .type = SWITCH_TYPE_INT,
139     .name = "enable_vlan",
140     .description = "Enable VLAN mode",
141     .set = psb6970_set_vlan,
142     .get = psb6970_get_vlan,
143     .max = 1},
144};
145
146static struct switch_attr psb6970_port[] = {
147};
148
149static struct switch_attr psb6970_vlan[] = {
150    {
151     .type = SWITCH_TYPE_INT,
152     .name = "vid",
153     .description = "VLAN ID (0-4094)",
154     .set = psb6970_set_vid,
155     .get = psb6970_get_vid,
156     .max = 4094,
157     },
158};
159
160static int psb6970_get_ports(struct switch_dev *dev, struct switch_val *val)
161{
162    struct psb6970_priv *priv = to_psb6970(dev);
163    u8 ports = priv->vlan_table[val->port_vlan];
164    int i;
165
166    val->len = 0;
167    for (i = 0; i < PSB6970_NUM_PORTS; i++) {
168        struct switch_port *p;
169
170        if (!(ports & (1 << i)))
171            continue;
172
173        p = &val->value.ports[val->len++];
174        p->id = i;
175        if (priv->vlan_tagged & (1 << i))
176            p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
177        else
178            p->flags = 0;
179    }
180    return 0;
181}
182
183static int psb6970_set_ports(struct switch_dev *dev, struct switch_val *val)
184{
185    struct psb6970_priv *priv = to_psb6970(dev);
186    u8 *vt = &priv->vlan_table[val->port_vlan];
187    int i, j;
188
189    *vt = 0;
190    for (i = 0; i < val->len; i++) {
191        struct switch_port *p = &val->value.ports[i];
192
193        if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED))
194            priv->vlan_tagged |= (1 << p->id);
195        else {
196            priv->vlan_tagged &= ~(1 << p->id);
197            priv->pvid[p->id] = val->port_vlan;
198
199            /* make sure that an untagged port does not
200             * appear in other vlans */
201            for (j = 0; j < PSB6970_MAX_VLANS; j++) {
202                if (j == val->port_vlan)
203                    continue;
204                priv->vlan_table[j] &= ~(1 << p->id);
205            }
206        }
207
208        *vt |= 1 << p->id;
209    }
210    return 0;
211}
212
213static int psb6970_hw_apply(struct switch_dev *dev)
214{
215    struct psb6970_priv *priv = to_psb6970(dev);
216    int i, j;
217
218    mutex_lock(&priv->reg_mutex);
219
220    if (priv->vlan) {
221        /* into the vlan translation unit */
222        for (j = 0; j < PSB6970_MAX_VLANS; j++) {
223            u8 vp = priv->vlan_table[j];
224
225            if (vp) {
226                priv->write(priv->phy, PSB6970_VFxL(j),
227                        PSB6970_VFxL_VV | priv->vlan_id[j]);
228                priv->write(priv->phy, PSB6970_VFxH(j),
229                        ((vp & priv->
230                          vlan_tagged) <<
231                         PSB6970_VFxH_TM_SHIFT) | vp);
232            } else /* clear VLAN Valid flag for unused vlans */
233                priv->write(priv->phy, PSB6970_VFxL(j), 0);
234
235        }
236    }
237
238    /* update the port destination mask registers and tag settings */
239    for (i = 0; i < PSB6970_NUM_PORTS; i++) {
240        int dvid = 1, pbvm = 0x7f | PSB6970_PBVM_VSD, ec = 0;
241
242        if (priv->vlan) {
243            ec = PSB6970_EC_IFNTE;
244            dvid = priv->vlan_id[priv->pvid[i]];
245            pbvm |= PSB6970_PBVM_TBVE | PSB6970_PBVM_VMCE;
246
247            if ((i << 1) & priv->vlan_tagged)
248                pbvm |= PSB6970_PBVM_AOVTP | PSB6970_PBVM_VC;
249        }
250
251        priv->write(priv->phy, PSB6970_PBVM(i), pbvm);
252
253        if (!PSB6970_IS_CPU_PORT(i)) {
254            priv->write(priv->phy, PSB6970_EC(i), ec);
255            priv->write(priv->phy, PSB6970_DVID(i), dvid);
256        }
257    }
258
259    mutex_unlock(&priv->reg_mutex);
260    return 0;
261}
262
263static int psb6970_reset_switch(struct switch_dev *dev)
264{
265    struct psb6970_priv *priv = to_psb6970(dev);
266    int i;
267
268    mutex_lock(&priv->reg_mutex);
269
270    memset(&priv->vlan, 0, sizeof(struct psb6970_priv) -
271           offsetof(struct psb6970_priv, vlan));
272
273    for (i = 0; i < PSB6970_MAX_VLANS; i++)
274        priv->vlan_id[i] = i;
275
276    mutex_unlock(&priv->reg_mutex);
277
278    return psb6970_hw_apply(dev);
279}
280
281static const struct switch_dev_ops psb6970_ops = {
282    .attr_global = {
283            .attr = psb6970_globals,
284            .n_attr = ARRAY_SIZE(psb6970_globals),
285            },
286    .attr_port = {
287              .attr = psb6970_port,
288              .n_attr = ARRAY_SIZE(psb6970_port),
289              },
290    .attr_vlan = {
291              .attr = psb6970_vlan,
292              .n_attr = ARRAY_SIZE(psb6970_vlan),
293              },
294    .get_port_pvid = psb6970_get_pvid,
295    .set_port_pvid = psb6970_set_pvid,
296    .get_vlan_ports = psb6970_get_ports,
297    .set_vlan_ports = psb6970_set_ports,
298    .apply_config = psb6970_hw_apply,
299    .reset_switch = psb6970_reset_switch,
300};
301
302static int psb6970_config_init(struct phy_device *pdev)
303{
304    struct psb6970_priv *priv;
305    struct net_device *dev = pdev->attached_dev;
306    struct switch_dev *swdev;
307    int ret;
308
309    priv = kzalloc(sizeof(struct psb6970_priv), GFP_KERNEL);
310    if (priv == NULL)
311        return -ENOMEM;
312
313    priv->phy = pdev;
314
315    if (pdev->addr == 0)
316        printk(KERN_INFO "%s: psb6970 switch driver attached.\n",
317               pdev->attached_dev->name);
318
319    if (pdev->addr != 0) {
320        kfree(priv);
321        return 0;
322    }
323
324    pdev->supported = pdev->advertising = SUPPORTED_100baseT_Full;
325
326    mutex_init(&priv->reg_mutex);
327    priv->read = psb6970_mii_read;
328    priv->write = psb6970_mii_write;
329
330    pdev->priv = priv;
331
332    swdev = &priv->dev;
333    swdev->cpu_port = PSB6970_DEFAULT_PORT_CPU;
334    swdev->ops = &psb6970_ops;
335
336    swdev->name = "Lantiq PSB6970";
337    swdev->vlans = PSB6970_MAX_VLANS;
338    swdev->ports = PSB6970_NUM_PORTS;
339
340    if ((ret = register_switch(&priv->dev, pdev->attached_dev)) < 0) {
341        kfree(priv);
342        goto done;
343    }
344
345    ret = psb6970_reset_switch(&priv->dev);
346    if (ret) {
347        kfree(priv);
348        goto done;
349    }
350
351    dev->phy_ptr = priv;
352
353done:
354    return ret;
355}
356
357static int psb6970_read_status(struct phy_device *phydev)
358{
359    phydev->speed = SPEED_100;
360    phydev->duplex = DUPLEX_FULL;
361    phydev->link = 1;
362
363    phydev->state = PHY_RUNNING;
364    netif_carrier_on(phydev->attached_dev);
365    phydev->adjust_link(phydev->attached_dev);
366
367    return 0;
368}
369
370static int psb6970_config_aneg(struct phy_device *phydev)
371{
372    return 0;
373}
374
375static int psb6970_probe(struct phy_device *pdev)
376{
377    return 0;
378}
379
380static void psb6970_remove(struct phy_device *pdev)
381{
382    struct psb6970_priv *priv = pdev->priv;
383
384    if (!priv)
385        return;
386
387    if (pdev->addr == 0)
388        unregister_switch(&priv->dev);
389    kfree(priv);
390}
391
392static int psb6970_fixup(struct phy_device *dev)
393{
394    struct mii_bus *bus = dev->bus;
395    u16 reg;
396
397    /* look for the switch on the bus */
398    reg = bus->read(bus, PHYADDR(PSB6970_CI1)) & PSB6970_CI1_MASK;
399    if (reg != PSB6970_CI1_VAL)
400        return 0;
401
402    dev->phy_id = (reg << 16);
403    dev->phy_id |= bus->read(bus, PHYADDR(PSB6970_CI0)) & PSB6970_CI0_MASK;
404
405    return 0;
406}
407
408static struct phy_driver psb6970_driver = {
409    .name = "Lantiq PSB6970",
410    .phy_id = PSB6970_CI1_VAL << 16,
411    .phy_id_mask = 0xffff0000,
412    .features = PHY_BASIC_FEATURES,
413    .probe = psb6970_probe,
414    .remove = psb6970_remove,
415    .config_init = &psb6970_config_init,
416    .config_aneg = &psb6970_config_aneg,
417    .read_status = &psb6970_read_status,
418    .driver = {.owner = THIS_MODULE},
419};
420
421int __init psb6970_init(void)
422{
423    phy_register_fixup_for_id(PHY_ANY_ID, psb6970_fixup);
424    return phy_driver_register(&psb6970_driver);
425}
426
427module_init(psb6970_init);
428
429void __exit psb6970_exit(void)
430{
431    phy_driver_unregister(&psb6970_driver);
432}
433
434module_exit(psb6970_exit);
435
436MODULE_DESCRIPTION("Lantiq PSB6970 Switch");
437MODULE_AUTHOR("Ithamar R. Adema <ithamar.adema@team-embedded.nl>");
438MODULE_LICENSE("GPL");
439

Archive Download this file



interactive