blob: c88a194b4cb62b425450ba1ad4aa4ed9f9286fe5 [file] [log] [blame]
Russell King5ae68b02016-06-23 14:50:05 +01001/*
2 * Software PHY emulation
3 *
4 * Code taken from fixed_phy.c by Russell King <rmk+kernel@arm.linux.org.uk>
5 *
6 * Author: Vitaly Bordug <vbordug@ru.mvista.com>
7 * Anton Vorontsov <avorontsov@ru.mvista.com>
8 *
9 * Copyright (c) 2006-2007 MontaVista Software, Inc.
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
15 */
16#include <linux/export.h>
17#include <linux/mii.h>
18#include <linux/phy.h>
19#include <linux/phy_fixed.h>
20
21#include "swphy.h"
22
Russell King0629bf12016-06-23 14:50:10 +010023struct swmii_regs {
24 u16 bmcr;
25 u16 bmsr;
26 u16 lpa;
27 u16 lpagb;
28};
29
30enum {
31 SWMII_SPEED_10 = 0,
32 SWMII_SPEED_100,
33 SWMII_SPEED_1000,
34 SWMII_DUPLEX_HALF = 0,
35 SWMII_DUPLEX_FULL,
36};
37
38/*
39 * These two tables get bitwise-anded together to produce the final result.
40 * This means the speed table must contain both duplex settings, and the
41 * duplex table must contain all speed settings.
42 */
43static const struct swmii_regs speed[] = {
44 [SWMII_SPEED_10] = {
45 .bmcr = BMCR_FULLDPLX,
46 .lpa = LPA_10FULL | LPA_10HALF,
47 },
48 [SWMII_SPEED_100] = {
49 .bmcr = BMCR_FULLDPLX | BMCR_SPEED100,
50 .bmsr = BMSR_100FULL | BMSR_100HALF,
51 .lpa = LPA_100FULL | LPA_100HALF,
52 },
53 [SWMII_SPEED_1000] = {
54 .bmcr = BMCR_FULLDPLX | BMCR_SPEED1000,
55 .bmsr = BMSR_ESTATEN,
56 .lpagb = LPA_1000FULL | LPA_1000HALF,
57 },
58};
59
60static const struct swmii_regs duplex[] = {
61 [SWMII_DUPLEX_HALF] = {
62 .bmcr = ~BMCR_FULLDPLX,
63 .bmsr = BMSR_ESTATEN | BMSR_100HALF,
64 .lpa = LPA_10HALF | LPA_100HALF,
65 .lpagb = LPA_1000HALF,
66 },
67 [SWMII_DUPLEX_FULL] = {
68 .bmcr = ~0,
69 .bmsr = BMSR_ESTATEN | BMSR_100FULL,
70 .lpa = LPA_10FULL | LPA_100FULL,
71 .lpagb = LPA_1000FULL,
72 },
73};
74
75static int swphy_decode_speed(int speed)
76{
77 switch (speed) {
78 case 1000:
79 return SWMII_SPEED_1000;
80 case 100:
81 return SWMII_SPEED_100;
82 case 10:
83 return SWMII_SPEED_10;
84 default:
85 return -EINVAL;
86 }
87}
88
Russell King5ae68b02016-06-23 14:50:05 +010089/**
90 * swphy_update_regs - update MII register array with fixed phy state
91 * @regs: array of 32 registers to update
92 * @state: fixed phy status
93 *
94 * Update the array of MII registers with the fixed phy link, speed,
95 * duplex and pause mode settings.
96 */
97int swphy_update_regs(u16 *regs, const struct fixed_phy_status *state)
98{
Russell King0629bf12016-06-23 14:50:10 +010099 int speed_index, duplex_index;
Russell King5ae68b02016-06-23 14:50:05 +0100100 u16 bmsr = BMSR_ANEGCAPABLE;
101 u16 bmcr = 0;
102 u16 lpagb = 0;
103 u16 lpa = 0;
104
Russell King0629bf12016-06-23 14:50:10 +0100105 speed_index = swphy_decode_speed(state->speed);
106 if (speed_index < 0) {
107 pr_warn("swphy: unknown speed\n");
108 return -EINVAL;
Russell King5ae68b02016-06-23 14:50:05 +0100109 }
110
Russell King0629bf12016-06-23 14:50:10 +0100111 duplex_index = state->duplex ? SWMII_DUPLEX_FULL : SWMII_DUPLEX_HALF;
112
113 bmsr |= speed[speed_index].bmsr & duplex[duplex_index].bmsr;
114
Russell King5ae68b02016-06-23 14:50:05 +0100115 if (state->link) {
116 bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE;
117
Russell King0629bf12016-06-23 14:50:10 +0100118 bmcr |= speed[speed_index].bmcr & duplex[duplex_index].bmcr;
119 lpa |= speed[speed_index].lpa & duplex[duplex_index].lpa;
120 lpagb |= speed[speed_index].lpagb & duplex[duplex_index].lpagb;
Russell King5ae68b02016-06-23 14:50:05 +0100121
122 if (state->pause)
123 lpa |= LPA_PAUSE_CAP;
124
125 if (state->asym_pause)
126 lpa |= LPA_PAUSE_ASYM;
127 }
128
129 regs[MII_PHYSID1] = 0;
130 regs[MII_PHYSID2] = 0;
131
132 regs[MII_BMSR] = bmsr;
133 regs[MII_BMCR] = bmcr;
134 regs[MII_LPA] = lpa;
135 regs[MII_STAT1000] = lpagb;
136
137 return 0;
138}
139EXPORT_SYMBOL_GPL(swphy_update_regs);