net: dsa: mv88e6xxx: factorize EEPROM access
Add a MV88E6XXX_FLAG_EEPROM flag to describe switch models featuring an EEPROM and distribute the EEPROM access routines to all models. Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
6d5834a1ad
Коммит
d24645bebc
|
@ -124,6 +124,8 @@ struct dsa_switch_driver mv88e6123_switch_driver = {
|
|||
#ifdef CONFIG_NET_DSA_HWMON
|
||||
.get_temp = mv88e6xxx_get_temp,
|
||||
#endif
|
||||
.get_eeprom = mv88e6xxx_get_eeprom,
|
||||
.set_eeprom = mv88e6xxx_set_eeprom,
|
||||
.get_regs_len = mv88e6xxx_get_regs_len,
|
||||
.get_regs = mv88e6xxx_get_regs,
|
||||
};
|
||||
|
|
|
@ -153,6 +153,8 @@ struct dsa_switch_driver mv88e6131_switch_driver = {
|
|||
.get_strings = mv88e6xxx_get_strings,
|
||||
.get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
|
||||
.get_sset_count = mv88e6xxx_get_sset_count,
|
||||
.get_eeprom = mv88e6xxx_get_eeprom,
|
||||
.set_eeprom = mv88e6xxx_set_eeprom,
|
||||
.adjust_link = mv88e6xxx_adjust_link,
|
||||
.port_bridge_join = mv88e6xxx_port_bridge_join,
|
||||
.port_bridge_leave = mv88e6xxx_port_bridge_leave,
|
||||
|
|
|
@ -133,6 +133,8 @@ struct dsa_switch_driver mv88e6171_switch_driver = {
|
|||
#ifdef CONFIG_NET_DSA_HWMON
|
||||
.get_temp = mv88e6xxx_get_temp,
|
||||
#endif
|
||||
.get_eeprom = mv88e6xxx_get_eeprom,
|
||||
.set_eeprom = mv88e6xxx_set_eeprom,
|
||||
.get_regs_len = mv88e6xxx_get_regs_len,
|
||||
.get_regs = mv88e6xxx_get_regs,
|
||||
.port_bridge_join = mv88e6xxx_port_bridge_join,
|
||||
|
|
|
@ -125,8 +125,6 @@ static int mv88e6352_setup(struct dsa_switch *ds)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_init(&ps->eeprom_mutex);
|
||||
|
||||
ret = mv88e6xxx_switch_reset(ps, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -138,207 +136,6 @@ static int mv88e6352_setup(struct dsa_switch *ds)
|
|||
return mv88e6xxx_setup_ports(ds);
|
||||
}
|
||||
|
||||
static int mv88e6352_read_eeprom_word(struct dsa_switch *ds, int addr)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ps->eeprom_mutex);
|
||||
|
||||
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
|
||||
GLOBAL2_EEPROM_OP_READ |
|
||||
(addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = mv88e6xxx_eeprom_busy_wait(ds);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA);
|
||||
error:
|
||||
mutex_unlock(&ps->eeprom_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mv88e6352_get_eeprom(struct dsa_switch *ds,
|
||||
struct ethtool_eeprom *eeprom, u8 *data)
|
||||
{
|
||||
int offset;
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
offset = eeprom->offset;
|
||||
len = eeprom->len;
|
||||
eeprom->len = 0;
|
||||
|
||||
eeprom->magic = 0xc3ec4951;
|
||||
|
||||
ret = mv88e6xxx_eeprom_load_wait(ds);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (offset & 1) {
|
||||
int word;
|
||||
|
||||
word = mv88e6352_read_eeprom_word(ds, offset >> 1);
|
||||
if (word < 0)
|
||||
return word;
|
||||
|
||||
*data++ = (word >> 8) & 0xff;
|
||||
|
||||
offset++;
|
||||
len--;
|
||||
eeprom->len++;
|
||||
}
|
||||
|
||||
while (len >= 2) {
|
||||
int word;
|
||||
|
||||
word = mv88e6352_read_eeprom_word(ds, offset >> 1);
|
||||
if (word < 0)
|
||||
return word;
|
||||
|
||||
*data++ = word & 0xff;
|
||||
*data++ = (word >> 8) & 0xff;
|
||||
|
||||
offset += 2;
|
||||
len -= 2;
|
||||
eeprom->len += 2;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
int word;
|
||||
|
||||
word = mv88e6352_read_eeprom_word(ds, offset >> 1);
|
||||
if (word < 0)
|
||||
return word;
|
||||
|
||||
*data++ = word & 0xff;
|
||||
|
||||
offset++;
|
||||
len--;
|
||||
eeprom->len++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv88e6352_eeprom_is_readonly(struct dsa_switch *ds)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int ret;
|
||||
|
||||
ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
|
||||
return -EROFS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv88e6352_write_eeprom_word(struct dsa_switch *ds, int addr,
|
||||
u16 data)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ps->eeprom_mutex);
|
||||
|
||||
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
|
||||
GLOBAL2_EEPROM_OP_WRITE |
|
||||
(addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = mv88e6xxx_eeprom_busy_wait(ds);
|
||||
error:
|
||||
mutex_unlock(&ps->eeprom_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mv88e6352_set_eeprom(struct dsa_switch *ds,
|
||||
struct ethtool_eeprom *eeprom, u8 *data)
|
||||
{
|
||||
int offset;
|
||||
int ret;
|
||||
int len;
|
||||
|
||||
if (eeprom->magic != 0xc3ec4951)
|
||||
return -EINVAL;
|
||||
|
||||
ret = mv88e6352_eeprom_is_readonly(ds);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
offset = eeprom->offset;
|
||||
len = eeprom->len;
|
||||
eeprom->len = 0;
|
||||
|
||||
ret = mv88e6xxx_eeprom_load_wait(ds);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (offset & 1) {
|
||||
int word;
|
||||
|
||||
word = mv88e6352_read_eeprom_word(ds, offset >> 1);
|
||||
if (word < 0)
|
||||
return word;
|
||||
|
||||
word = (*data++ << 8) | (word & 0xff);
|
||||
|
||||
ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
offset++;
|
||||
len--;
|
||||
eeprom->len++;
|
||||
}
|
||||
|
||||
while (len >= 2) {
|
||||
int word;
|
||||
|
||||
word = *data++;
|
||||
word |= *data++ << 8;
|
||||
|
||||
ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
offset += 2;
|
||||
len -= 2;
|
||||
eeprom->len += 2;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
int word;
|
||||
|
||||
word = mv88e6352_read_eeprom_word(ds, offset >> 1);
|
||||
if (word < 0)
|
||||
return word;
|
||||
|
||||
word = (word & 0xff00) | *data++;
|
||||
|
||||
ret = mv88e6352_write_eeprom_word(ds, offset >> 1, word);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
offset++;
|
||||
len--;
|
||||
eeprom->len++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dsa_switch_driver mv88e6352_switch_driver = {
|
||||
.tag_protocol = DSA_TAG_PROTO_EDSA,
|
||||
.probe = mv88e6352_drv_probe,
|
||||
|
@ -358,8 +155,8 @@ struct dsa_switch_driver mv88e6352_switch_driver = {
|
|||
.set_temp_limit = mv88e6xxx_set_temp_limit,
|
||||
.get_temp_alarm = mv88e6xxx_get_temp_alarm,
|
||||
#endif
|
||||
.get_eeprom = mv88e6352_get_eeprom,
|
||||
.set_eeprom = mv88e6352_set_eeprom,
|
||||
.get_eeprom = mv88e6xxx_get_eeprom,
|
||||
.set_eeprom = mv88e6xxx_set_eeprom,
|
||||
.get_regs_len = mv88e6xxx_get_regs_len,
|
||||
.get_regs = mv88e6xxx_get_regs,
|
||||
.port_bridge_join = mv88e6xxx_port_bridge_join,
|
||||
|
|
|
@ -823,7 +823,7 @@ static int _mv88e6xxx_phy_wait(struct mv88e6xxx_priv_state *ps)
|
|||
GLOBAL2_SMI_OP_BUSY);
|
||||
}
|
||||
|
||||
int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
|
||||
static int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
|
||||
|
@ -831,7 +831,7 @@ int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
|
|||
GLOBAL2_EEPROM_OP_LOAD);
|
||||
}
|
||||
|
||||
int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
|
||||
static int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
|
||||
|
@ -839,6 +839,215 @@ int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
|
|||
GLOBAL2_EEPROM_OP_BUSY);
|
||||
}
|
||||
|
||||
static int mv88e6xxx_read_eeprom_word(struct dsa_switch *ds, int addr)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ps->eeprom_mutex);
|
||||
|
||||
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
|
||||
GLOBAL2_EEPROM_OP_READ |
|
||||
(addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = mv88e6xxx_eeprom_busy_wait(ds);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA);
|
||||
error:
|
||||
mutex_unlock(&ps->eeprom_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mv88e6xxx_get_eeprom(struct dsa_switch *ds, struct ethtool_eeprom *eeprom,
|
||||
u8 *data)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int offset;
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
offset = eeprom->offset;
|
||||
len = eeprom->len;
|
||||
eeprom->len = 0;
|
||||
|
||||
eeprom->magic = 0xc3ec4951;
|
||||
|
||||
ret = mv88e6xxx_eeprom_load_wait(ds);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (offset & 1) {
|
||||
int word;
|
||||
|
||||
word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
|
||||
if (word < 0)
|
||||
return word;
|
||||
|
||||
*data++ = (word >> 8) & 0xff;
|
||||
|
||||
offset++;
|
||||
len--;
|
||||
eeprom->len++;
|
||||
}
|
||||
|
||||
while (len >= 2) {
|
||||
int word;
|
||||
|
||||
word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
|
||||
if (word < 0)
|
||||
return word;
|
||||
|
||||
*data++ = word & 0xff;
|
||||
*data++ = (word >> 8) & 0xff;
|
||||
|
||||
offset += 2;
|
||||
len -= 2;
|
||||
eeprom->len += 2;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
int word;
|
||||
|
||||
word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
|
||||
if (word < 0)
|
||||
return word;
|
||||
|
||||
*data++ = word & 0xff;
|
||||
|
||||
offset++;
|
||||
len--;
|
||||
eeprom->len++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv88e6xxx_eeprom_is_readonly(struct dsa_switch *ds)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int ret;
|
||||
|
||||
ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
|
||||
return -EROFS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv88e6xxx_write_eeprom_word(struct dsa_switch *ds, int addr,
|
||||
u16 data)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ps->eeprom_mutex);
|
||||
|
||||
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
|
||||
GLOBAL2_EEPROM_OP_WRITE |
|
||||
(addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = mv88e6xxx_eeprom_busy_wait(ds);
|
||||
error:
|
||||
mutex_unlock(&ps->eeprom_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mv88e6xxx_set_eeprom(struct dsa_switch *ds, struct ethtool_eeprom *eeprom,
|
||||
u8 *data)
|
||||
{
|
||||
struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
|
||||
int offset;
|
||||
int ret;
|
||||
int len;
|
||||
|
||||
if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (eeprom->magic != 0xc3ec4951)
|
||||
return -EINVAL;
|
||||
|
||||
ret = mv88e6xxx_eeprom_is_readonly(ds);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
offset = eeprom->offset;
|
||||
len = eeprom->len;
|
||||
eeprom->len = 0;
|
||||
|
||||
ret = mv88e6xxx_eeprom_load_wait(ds);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (offset & 1) {
|
||||
int word;
|
||||
|
||||
word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
|
||||
if (word < 0)
|
||||
return word;
|
||||
|
||||
word = (*data++ << 8) | (word & 0xff);
|
||||
|
||||
ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
offset++;
|
||||
len--;
|
||||
eeprom->len++;
|
||||
}
|
||||
|
||||
while (len >= 2) {
|
||||
int word;
|
||||
|
||||
word = *data++;
|
||||
word |= *data++ << 8;
|
||||
|
||||
ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
offset += 2;
|
||||
len -= 2;
|
||||
eeprom->len += 2;
|
||||
}
|
||||
|
||||
if (len) {
|
||||
int word;
|
||||
|
||||
word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
|
||||
if (word < 0)
|
||||
return word;
|
||||
|
||||
word = (word & 0xff00) | *data++;
|
||||
|
||||
ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
offset++;
|
||||
len--;
|
||||
eeprom->len++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _mv88e6xxx_atu_wait(struct mv88e6xxx_priv_state *ps)
|
||||
{
|
||||
return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_ATU_OP,
|
||||
|
@ -2596,6 +2805,9 @@ int mv88e6xxx_setup_common(struct mv88e6xxx_priv_state *ps)
|
|||
|
||||
INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
|
||||
|
||||
if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
|
||||
mutex_init(&ps->eeprom_mutex);
|
||||
|
||||
if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
|
||||
mv88e6xxx_ppu_state_init(ps);
|
||||
|
||||
|
|
|
@ -351,6 +351,11 @@ enum mv88e6xxx_family {
|
|||
};
|
||||
|
||||
enum mv88e6xxx_cap {
|
||||
/* EEPROM Command and Data registers.
|
||||
* See GLOBAL2_EEPROM_OP and GLOBAL2_EEPROM_DATA.
|
||||
*/
|
||||
MV88E6XXX_CAP_EEPROM,
|
||||
|
||||
/* PHY Polling Unit.
|
||||
* See GLOBAL_CONTROL_PPU_ENABLE and GLOBAL_STATUS_PPU_POLLING.
|
||||
*/
|
||||
|
@ -364,6 +369,7 @@ enum mv88e6xxx_cap {
|
|||
};
|
||||
|
||||
/* Bitmask of capabilities */
|
||||
#define MV88E6XXX_FLAG_EEPROM BIT(MV88E6XXX_CAP_EEPROM)
|
||||
#define MV88E6XXX_FLAG_PPU BIT(MV88E6XXX_CAP_PPU)
|
||||
#define MV88E6XXX_FLAG_SMI_PHY BIT(MV88E6XXX_CAP_SMI_PHY)
|
||||
|
||||
|
@ -379,13 +385,15 @@ enum mv88e6xxx_cap {
|
|||
MV88E6XXX_FLAG_PPU
|
||||
|
||||
#define MV88E6XXX_FLAGS_FAMILY_6320 \
|
||||
MV88E6XXX_FLAG_SMI_PHY
|
||||
(MV88E6XXX_FLAG_EEPROM | \
|
||||
MV88E6XXX_FLAG_SMI_PHY)
|
||||
|
||||
#define MV88E6XXX_FLAGS_FAMILY_6351 \
|
||||
MV88E6XXX_FLAG_SMI_PHY
|
||||
|
||||
#define MV88E6XXX_FLAGS_FAMILY_6352 \
|
||||
MV88E6XXX_FLAG_SMI_PHY
|
||||
(MV88E6XXX_FLAG_EEPROM | \
|
||||
MV88E6XXX_FLAG_SMI_PHY)
|
||||
|
||||
struct mv88e6xxx_info {
|
||||
enum mv88e6xxx_family family;
|
||||
|
@ -521,8 +529,10 @@ int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp);
|
|||
int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp);
|
||||
int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp);
|
||||
int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm);
|
||||
int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds);
|
||||
int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds);
|
||||
int mv88e6xxx_get_eeprom(struct dsa_switch *ds, struct ethtool_eeprom *eeprom,
|
||||
u8 *data);
|
||||
int mv88e6xxx_set_eeprom(struct dsa_switch *ds, struct ethtool_eeprom *eeprom,
|
||||
u8 *data);
|
||||
int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e);
|
||||
int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
|
||||
struct phy_device *phydev, struct ethtool_eee *e);
|
||||
|
|
Загрузка…
Ссылка в новой задаче