/*
* Marvell 88E6xxx Switch Global 2 Registers support (device address
* 0x1C)
*
* Copyright (c) 2008 Marvell Semiconductor
*
* Copyright (c) 2016-2017 Savoir-faire Linux Inc.
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include "mv88e6xxx.h"
#include "global2.h"
#define ADDR_GLOBAL2 0x1c
static int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
{
return mv88e6xxx_read(chip, ADDR_GLOBAL2, reg, val);
}
static int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
{
return mv88e6xxx_write(chip, ADDR_GLOBAL2, reg, val);
}
static int mv88e6xxx_g2_update(struct mv88e6xxx_chip *chip, int reg, u16 update)
{
return mv88e6xxx_update(chip, ADDR_GLOBAL2, reg, update);
}
static int mv88e6xxx_g2_wait(struct mv88e6xxx_chip *chip, int reg, u16 mask)
{
return mv88e6xxx_wait(chip, ADDR_GLOBAL2, reg, mask);
}
/* Offset 0x02: Management Enable 2x */
/* Offset 0x03: Management Enable 0x */
int mv88e6095_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
{
int err;
/* Consider the frames with reserved multicast destination
* addresses matching 01:80:c2:00:00:2x as MGMT.
*/
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_2X)) {
err = mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_2X, 0xffff);
if (err)
return err;
}
/* Consider the frames with reserved multicast destination
* addresses matching 01:80:c2:00:00:0x as MGMT.
*/
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_MGMT_EN_0X))
return mv88e6xxx_g2_write(chip, GLOBAL2_MGMT_EN_0X, 0xffff);
return 0;
}
/* Offset 0x06: Device Mapping Table register */
static int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip,
int target, int port)
{
u16 val = (target << 8) | (port & 0xf);
return mv88e6xxx_g2_update(chip, GLOBAL2_DEVICE_MAPPING, val);
}
static int mv88e6xxx_g2_set_device_mapping(struct mv88e6xxx_chip *chip)
{
int target, port;
int err;
/* Initialize the routing port to the 32 possible target devices */
for (target = 0; target < 32; ++target) {
port = 0xf;
if (target < DSA_MAX_SWITCHES) {
port = chip->ds->rtable[target];
if (port == DSA_RTABLE_NONE)
port = 0xf;
}
err = mv88e6xxx_g2_device_mapping_write(chip, target, port);
if (err)
break;
}
return err;
}
/* Offset 0x07: Trunk Mask Table register */
static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
bool hask, u16 mask)
{
const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
u16 val = (num << 12) | (mask & port_mask);
if (hask)
val |= GLOBAL2_TRUNK_MASK_HASK;
return mv88e6xxx_g2_update(chip, GLOBAL2_TRUNK_MASK, val);
}
/* Offset 0x08: Trunk Mapping Table register */
static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
u16 map)
{
const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
u16 val = (id << 11) | (map & port_mask);
return mv88e6xxx_g2_update(chip, GLOBAL2_TRUNK_MAPPING, val);
}
static int mv88e6xxx_g2_clear_trunk(struct mv88e6xxx_chip *chip)
{
const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
int i, err;
/* Clear all eight possible Trunk Mask vectors */
for (i = 0; i < 8; ++i) {
err = mv88e6xxx_g2_trunk_mask_write(chip, i, false<