/*
 * Marvell 88E61xx switch driver
 *
 * Copyright (c) 2014 Claudio Leite <leitec@staticky.com>
 * Copyright (c) 2014 Nikita Nazarenko <nnazarenko@radiofid.com>
 *
 * Based on code (c) 2008 Felix Fietkau <nbd@nbd.name>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License v2 as published by the
 * Free Software Foundation
 */

#ifndef __MVSW61XX_H
#define __MVSW61XX_H

#define MV_PORTS			7
#define MV_PORTS_MASK			((1 << MV_PORTS) - 1)

#define MV_BASE				0x10

#define MV_SWITCHPORT_BASE		0x10
#define MV_SWITCHPORT(_n)		(MV_SWITCHPORT_BASE + (_n))
#define MV_SWITCHREGS			(MV_BASE + 0xb)

#define MV_VLANS			64

enum {
	MV_PORT_STATUS			= 0x00,
	MV_PORT_PHYCTL			= 0x01,
	MV_PORT_JAMCTL			= 0x02,
	MV_PORT_IDENT			= 0x03,
	MV_PORT_CONTROL			= 0x04,
	MV_PORT_CONTROL1		= 0x05,
	MV_PORT_VLANMAP			= 0x06,
	MV_PORT_VLANID			= 0x07,
	MV_PORT_CONTROL2		= 0x08,
	MV_PORT_ASSOC			= 0x0b,
	MV_PORT_RX_DISCARD_LOW		= 0x10,
	MV_PORT_RX_DISCARD_HIGH		= 0x11,
	MV_PORT_IN_FILTERED		= 0x12,
	MV_PORT_OUT_ACCEPTED		= 0x13,
};
#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type

enum {
	MV_PORT_STATUS_FDX		= (1 << 10),
	MV_PORT_STATUS_LINK		= (1 << 11),
};

enum {
	MV_PORT_STATUS_CMODE_100BASE_X	= 0x8,
	MV_PORT_STATUS_CMODE_1000BASE_X	= 0x9,
	MV_PORT_STATUS_CMODE_SGMII	= 0xa,
};

#define MV_PORT_STATUS_CMODE_MASK	0xf

enum {
	MV_PORT_STATUS_SPEED_10		= 0x00,
	MV_PORT_STATUS_SPEED_100	= 0x01,
	MV_PORT_STATUS_SPEED_1000	= 0x02,
};
#define MV_PORT_STATUS_SPEED_SHIFT	8
#define MV_PORT_STATUS_SPEED_MASK	(3 << 8)

enum {
	MV_PORTCTRL_DISABLED		= (0 << 0),
	MV_PORTCTRL_BLOCKING		= (1 << 0),
	MV_PORTCTRL_LEARNING		= (2 << 0),
	MV_PORTCTRL_FORWARDING		= (3 << 0),
	MV_PORTCTRL_VLANTUN		= (1 << 7),
	MV_PORTCTRL_EGRESS		= (1 << 12),
};

#define MV_PHYCTL_FC_MASK		(3 << 6)

enum {
	MV_PHYCTL_FC_ENABLE		= (3 << 6),
	MV_PHYCTL_FC_DISABLE		= (1 << 6),
};

enum {
	MV_8021Q_EGRESS_UNMODIFIED	= 0x00,
	MV_8021Q_EGRESS_UNTAGGED	= 0x01,
	MV_8021Q_EGRESS_TAGGED		= 0x02,
	MV_8021Q_EGRESS_ADDTAG		= 0x03,
};

#define MV_8021Q_MODE_SHIFT		10
#define MV_8021Q_MODE_MASK		(0x3 << MV_8021Q_MODE_SHIFT)

enum {
	MV_8021Q_MODE_DISABLE		= 0x00,
	MV_8021Q_MODE_FALLBACK		= 0x01,
	MV_8021Q_MODE_CHECK		= 0x02,
	MV_8021Q_MODE_SECURE		= 0x03,
};

enum {
	MV_8021Q_VLAN_ONLY		= (1 << 15),
};

#define MV_PORTASSOC_MONITOR		(1 << 15)

enum {
	MV_SWITCH_ATU_FID0		= 0x01,
	MV_SWITCH_ATU_FID1		= 0x02,
	MV_SWITCH_ATU_SID		= 0x03,
	MV_SWITCH_CTRL			= 0x04,
	MV_SWITCH_ATU_CTRL		= 0x0a,
	MV_SWITCH_ATU_OP		= 0x0b,
	MV_SWITCH_ATU_DATA		= 0x0c,
	MV_SWITCH_ATU_MAC0		= 0x0d,
	MV_SWITCH_ATU_MAC1		= 0x0e,
	MV_SWITCH_ATU_MAC2		= 0x0f,
	MV_SWITCH_GLOBAL		= 0x1b,
	MV_SWITCH_GLOBAL2		= 0x1c,
};
#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type

enum {
	MV_SWITCHCTL_EEIE		= (1 << 0),
	MV_SWITCHCTL_PHYIE		= (1 << 1),
	MV_SWITCHCTL_ATUDONE		= (1 << 2),
	MV_SWITCHCTL_ATUIE		= (1 << 3),
	MV_SWITCHCTL_CTRMODE		= (1 << 8),
	MV_SWITCHCTL_RELOAD		= (1 << 9),
	MV_SWITCHCTL_MSIZE		= (1 << 10),
	MV_SWITCHCTL_DROP		= (1 << 13),
};

enum {
#define MV_ATUCTL_AGETIME_MIN		16
#define MV_ATUCTL_AGETIME_MAX		4080
#define MV_ATUCTL_AGETIME(_n)		((((_n) / 16) & 0xff) << 4)
	MV_ATUCTL_ATU_256		= (0 << 12),
	MV_ATUCTL_ATU_512		= (1 << 12),
	MV_ATUCTL_ATU_1K		= (2 << 12),
	MV_ATUCTL_ATUMASK		= (3 << 12),
	MV_ATUCTL_NO_LEARN		= (1 << 14),
	MV_ATUCTL_RESET			= (1 << 15),
};

enum {
#define MV_ATUOP_DBNUM(_n)		((_n) & 0x0f)
	MV_ATUOP_NOOP			= (0 << 12),
	MV_ATUOP_FLUSH_ALL		= (1 << 12),
	MV_ATUOP_FLUSH_U		= (2 << 12),
	MV_ATUOP_LOAD_DB		= (3 << 12),
	MV_ATUOP_GET_NEXT		= (4 << 12),
	MV_ATUOP_FLUSH_DB		= (5 << 12),
	MV_ATUOP_FLUSH_DB_UU		= (6 << 12),
	MV_ATUOP_INPROGRESS		= (1 << 15),
};

enum {
	MV_GLOBAL_STATUS		= 0x00,
	MV_GLOBAL_ATU_FID		= 0x01,
	MV_GLOBAL_VTU_FID		= 0x02,
	MV_GLOBAL_VTU_SID		= 0x03,
	MV_GLOBAL_CONTROL		= 0x04,
	MV_GLOBAL_VTU_OP		= 0x05,
	MV_GLOBAL_VTU_VID		= 0x06,
	MV_GLOBAL_VTU_DATA1		= 0x07,
	MV_GLOBAL_VTU_DATA2		= 0x08,
	MV_GLOBAL_VTU_DATA3		= 0x09,
	MV_GLOBAL_CONTROL2		= 0x1c,
};
#define MV_GLOBALREG(_type) MV_SWITCH_GLOBAL, MV_GLOBAL_##_type

enum {
	MV_GLOBAL2_SMI_OP		= 0x18,
	MV_GLOBAL2_SMI_DATA		= 0x19,
	MV_GLOBAL2_SDET_POLARITY	= 0x1d,
};
#define MV_GLOBAL2REG(_type) MV_SWITCH_GLOBAL2, MV_GLOBAL2_##_type

enum {
	MV_VTU_VID_VALID		= (1 << 12),
};

enum {
	MV_VTUOP_PURGE			= (1 << 12),
	MV_VTUOP_LOAD			= (3 << 12),
	MV_VTUOP_INPROGRESS		= (1 << 15),
	MV_VTUOP_STULOAD		= (5 << 12),
	MV_VTUOP_VTU_GET_NEXT		= (4 << 12),
	MV_VTUOP_STU_GET_NEXT		= (6 << 12),
	MV_VTUOP_GET_VIOLATION		= (7 << 12),
};

enum {
	MV_CONTROL_RESET		= (1 << 15),
	MV_CONTROL_PPU_ENABLE		= (1 << 14),
};

enum {
	MV_VTUCTL_EGRESS_UNMODIFIED	= (0 << 0),
	MV_VTUCTL_EGRESS_UNTAGGED	= (1 << 0),
	MV_VTUCTL_EGRESS_TAGGED		= (2 << 0),
	MV_VTUCTL_DISCARD		= (3 << 0),
};

enum {
	MV_STUCTL_STATE_DISABLED	= (0 << 0),
	MV_STUCTL_STATE_BLOCKING	= (1 << 0),
	MV_STUCTL_STATE_LEARNING	= (2 << 0),
	MV_STUCTL_STATE_FORWARDING	= (3 << 0),
};

enum {
	MV_INDIRECT_REG_CMD		= 0,
	MV_INDIRECT_REG_DATA		= 1,
};

enum {
	MV_INDIRECT_INPROGRESS		= 0x8000,
	MV_INDIRECT_WRITE		= 0x9400,
	MV_INDIRECT_READ		= 0x9800,
};
#define MV_INDIRECT_ADDR_S		5

#define MV_IDENT_MASK			0xfff0

#define MV_IDENT_VALUE_6171		0x1710
#define MV_IDENT_STR_6171		"MV88E6171"

#define MV_IDENT_VALUE_6172		0x1720
#define MV_IDENT_STR_6172		"MV88E6172"

#define MV_IDENT_VALUE_6176		0x1760
#define MV_IDENT_STR_6176		"MV88E6176"

#define MV_IDENT_VALUE_6352		0x3520
#define MV_IDENT_STR_6352		"MV88E6352"

#define MV_PVID_MASK			0x0fff

#define MV_FDB_HI_MASK			0x00ff
#define MV_FDB_LO_MASK			0xf000
#define MV_FDB_HI_SHIFT			4
#define MV_FDB_LO_SHIFT			12

/* Marvell Specific PHY register */
#define MII_MV_SPEC_CTRL		16
enum {
	MV_SPEC_MDI_CROSS_AUTO		= (0x6 << 4),
	MV_SPEC_ENERGY_DETECT		= (0x3 << 8),
	MV_SPEC_DOWNSHIFT_COUNTER	= (0x3 << 12),
};

#define MII_MV_PAGE			22

#define MV_REG_FIBER_SERDES		0xf
#define MV_PAGE_FIBER_SERDES		0x1

struct mvsw61xx_state {
	struct switch_dev dev;
	struct mii_bus *bus;
	int base_addr;
	u16 model;

	bool registered;
	bool is_indirect;

	int cpu_port0;
	int cpu_port1;

	int vlan_enabled;
	struct port_state {
		u16 fdb;
		u16 pvid;
		u16 mask;
		u8 qmode;
	} ports[MV_PORTS];

	struct vlan_state {
		bool port_based;

		u16 mask;
		u16 vid;
		u32 port_mode;
		u32 port_sstate;
	} vlans[MV_VLANS];

	char buf[128];
};

#define get_state(_dev) container_of((_dev), struct mvsw61xx_state, dev)

#endif
