brintos

brintos / linux-shallow public Read only

0
0
Text · 14.2 KiB · bcfb4a8 Raw
681 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Marvell 88E6xxx VLAN [Spanning Tree] Translation Unit (VTU [STU]) support4 *5 * Copyright (c) 2008 Marvell Semiconductor6 * Copyright (c) 2015 CMC Electronics, Inc.7 * Copyright (c) 2017 Savoir-faire Linux, Inc.8 */9 10#include <linux/bitfield.h>11#include <linux/interrupt.h>12#include <linux/irqdomain.h>13 14#include "chip.h"15#include "global1.h"16#include "trace.h"17 18/* Offset 0x02: VTU FID Register */19 20static int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip,21				     struct mv88e6xxx_vtu_entry *entry)22{23	u16 val;24	int err;25 26	err = mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_FID, &val);27	if (err)28		return err;29 30	entry->fid = val & MV88E6352_G1_VTU_FID_MASK;31	entry->policy = !!(val & MV88E6352_G1_VTU_FID_VID_POLICY);32	return 0;33}34 35static int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip,36				      struct mv88e6xxx_vtu_entry *entry)37{38	u16 val = entry->fid & MV88E6352_G1_VTU_FID_MASK;39 40	if (entry->policy)41		val |= MV88E6352_G1_VTU_FID_VID_POLICY;42 43	return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_FID, val);44}45 46/* Offset 0x03: VTU SID Register */47 48static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip, u8 *sid)49{50	u16 val;51	int err;52 53	err = mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID, &val);54	if (err)55		return err;56 57	*sid = val & MV88E6352_G1_VTU_SID_MASK;58 59	return 0;60}61 62static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip, u8 sid)63{64	u16 val = sid & MV88E6352_G1_VTU_SID_MASK;65 66	return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_SID, val);67}68 69/* Offset 0x05: VTU Operation Register */70 71static int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip)72{73	int bit = __bf_shf(MV88E6XXX_G1_VTU_OP_BUSY);74 75	return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_VTU_OP, bit, 0);76}77 78static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op)79{80	int err;81 82	err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_OP,83				 MV88E6XXX_G1_VTU_OP_BUSY | op);84	if (err)85		return err;86 87	return mv88e6xxx_g1_vtu_op_wait(chip);88}89 90/* Offset 0x06: VTU VID Register */91 92static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip,93				     bool *valid, u16 *vid)94{95	u16 val;96	int err;97 98	err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID, &val);99	if (err)100		return err;101 102	if (vid) {103		*vid = val & 0xfff;104 105		if (val & MV88E6390_G1_VTU_VID_PAGE)106			*vid |= 0x1000;107	}108 109	if (valid)110		*valid = !!(val & MV88E6XXX_G1_VTU_VID_VALID);111 112	return 0;113}114 115static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip,116				      bool valid, u16 vid)117{118	u16 val = vid & 0xfff;119 120	if (vid & 0x1000)121		val |= MV88E6390_G1_VTU_VID_PAGE;122 123	if (valid)124		val |= MV88E6XXX_G1_VTU_VID_VALID;125 126	return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_VID, val);127}128 129/* Offset 0x07: VTU/STU Data Register 1130 * Offset 0x08: VTU/STU Data Register 2131 * Offset 0x09: VTU/STU Data Register 3132 */133static int mv88e6185_g1_vtu_stu_data_read(struct mv88e6xxx_chip *chip,134					  u16 *regs)135{136	int i;137 138	/* Read all 3 VTU/STU Data registers */139	for (i = 0; i < 3; ++i) {140		u16 *reg = &regs[i];141		int err;142 143		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);144		if (err)145			return err;146	}147 148	return 0;149}150 151static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip,152				      u8 *member, u8 *state)153{154	u16 regs[3];155	int err;156	int i;157 158	err = mv88e6185_g1_vtu_stu_data_read(chip, regs);159	if (err)160		return err;161 162	/* Extract MemberTag data */163	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {164		unsigned int member_offset = (i % 4) * 4;165		unsigned int state_offset = member_offset + 2;166 167		if (member)168			member[i] = (regs[i / 4] >> member_offset) & 0x3;169 170		if (state)171			state[i] = (regs[i / 4] >> state_offset) & 0x3;172	}173 174	return 0;175}176 177static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip,178				       u8 *member, u8 *state)179{180	u16 regs[3] = { 0 };181	int i;182 183	/* Insert MemberTag and PortState data */184	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {185		unsigned int member_offset = (i % 4) * 4;186		unsigned int state_offset = member_offset + 2;187 188		if (member)189			regs[i / 4] |= (member[i] & 0x3) << member_offset;190 191		if (state)192			regs[i / 4] |= (state[i] & 0x3) << state_offset;193	}194 195	/* Write all 3 VTU/STU Data registers */196	for (i = 0; i < 3; ++i) {197		u16 reg = regs[i];198		int err;199 200		err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);201		if (err)202			return err;203	}204 205	return 0;206}207 208static int mv88e6390_g1_vtu_data_read(struct mv88e6xxx_chip *chip, u8 *data)209{210	u16 regs[2];211	int i;212 213	/* Read the 2 VTU/STU Data registers */214	for (i = 0; i < 2; ++i) {215		u16 *reg = &regs[i];216		int err;217 218		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);219		if (err)220			return err;221	}222 223	/* Extract data */224	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {225		unsigned int offset = (i % 8) * 2;226 227		data[i] = (regs[i / 8] >> offset) & 0x3;228	}229 230	return 0;231}232 233static int mv88e6390_g1_vtu_data_write(struct mv88e6xxx_chip *chip, u8 *data)234{235	u16 regs[2] = { 0 };236	int i;237 238	/* Insert data */239	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {240		unsigned int offset = (i % 8) * 2;241 242		regs[i / 8] |= (data[i] & 0x3) << offset;243	}244 245	/* Write the 2 VTU/STU Data registers */246	for (i = 0; i < 2; ++i) {247		u16 reg = regs[i];248		int err;249 250		err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);251		if (err)252			return err;253	}254 255	return 0;256}257 258/* VLAN Translation Unit Operations */259 260int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip,261			     struct mv88e6xxx_vtu_entry *entry)262{263	int err;264 265	err = mv88e6xxx_g1_vtu_op_wait(chip);266	if (err)267		return err;268 269	/* To get the next higher active VID, the VTU GetNext operation can be270	 * started again without setting the VID registers since it already271	 * contains the last VID.272	 *273	 * To save a few hardware accesses and abstract this to the caller,274	 * write the VID only once, when the entry is given as invalid.275	 */276	if (!entry->valid) {277		err = mv88e6xxx_g1_vtu_vid_write(chip, false, entry->vid);278		if (err)279			return err;280	}281 282	err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_GET_NEXT);283	if (err)284		return err;285 286	return mv88e6xxx_g1_vtu_vid_read(chip, &entry->valid, &entry->vid);287}288 289int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,290			     struct mv88e6xxx_vtu_entry *entry)291{292	u16 val;293	int err;294 295	err = mv88e6xxx_g1_vtu_getnext(chip, entry);296	if (err)297		return err;298 299	if (entry->valid) {300		err = mv88e6185_g1_vtu_data_read(chip, entry->member, entry->state);301		if (err)302			return err;303 304		/* VTU DBNum[3:0] are located in VTU Operation 3:0305		 * VTU DBNum[7:4] ([5:4] for 6250) are located in VTU Operation 11:8 (9:8)306		 */307		err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val);308		if (err)309			return err;310 311		entry->fid = val & 0x000f;312		entry->fid |= (val & 0x0f00) >> 4;313		entry->fid &= mv88e6xxx_num_databases(chip) - 1;314	}315 316	return 0;317}318 319int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip,320			     struct mv88e6xxx_vtu_entry *entry)321{322	int err;323 324	/* Fetch VLAN MemberTag data from the VTU */325	err = mv88e6xxx_g1_vtu_getnext(chip, entry);326	if (err)327		return err;328 329	if (entry->valid) {330		err = mv88e6185_g1_vtu_data_read(chip, entry->member, NULL);331		if (err)332			return err;333 334		err = mv88e6xxx_g1_vtu_fid_read(chip, entry);335		if (err)336			return err;337 338		err = mv88e6xxx_g1_vtu_sid_read(chip, &entry->sid);339		if (err)340			return err;341	}342 343	return 0;344}345 346int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip,347			     struct mv88e6xxx_vtu_entry *entry)348{349	int err;350 351	/* Fetch VLAN MemberTag data from the VTU */352	err = mv88e6xxx_g1_vtu_getnext(chip, entry);353	if (err)354		return err;355 356	if (entry->valid) {357		err = mv88e6390_g1_vtu_data_read(chip, entry->member);358		if (err)359			return err;360 361		err = mv88e6xxx_g1_vtu_fid_read(chip, entry);362		if (err)363			return err;364 365		err = mv88e6xxx_g1_vtu_sid_read(chip, &entry->sid);366		if (err)367			return err;368	}369 370	return 0;371}372 373int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,374			       struct mv88e6xxx_vtu_entry *entry)375{376	u16 op = MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE;377	int err;378 379	err = mv88e6xxx_g1_vtu_op_wait(chip);380	if (err)381		return err;382 383	err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, entry->vid);384	if (err)385		return err;386 387	if (entry->valid) {388		err = mv88e6185_g1_vtu_data_write(chip, entry->member, entry->state);389		if (err)390			return err;391 392		/* VTU DBNum[3:0] are located in VTU Operation 3:0393		 * VTU DBNum[7:4] are located in VTU Operation 11:8394		 *395		 * For the 6250/6220, the latter are really [5:4] and396		 * 9:8, but in those cases bits 7:6 of entry->fid are397		 * 0 since they have num_databases = 64.398		 */399		op |= entry->fid & 0x000f;400		op |= (entry->fid & 0x00f0) << 4;401	}402 403	return mv88e6xxx_g1_vtu_op(chip, op);404}405 406int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,407			       struct mv88e6xxx_vtu_entry *entry)408{409	int err;410 411	err = mv88e6xxx_g1_vtu_op_wait(chip);412	if (err)413		return err;414 415	err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, entry->vid);416	if (err)417		return err;418 419	if (entry->valid) {420		/* Write MemberTag data */421		err = mv88e6185_g1_vtu_data_write(chip, entry->member, NULL);422		if (err)423			return err;424 425		err = mv88e6xxx_g1_vtu_fid_write(chip, entry);426		if (err)427			return err;428 429		err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid);430		if (err)431			return err;432	}433 434	/* Load/Purge VTU entry */435	return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE);436}437 438int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,439			       struct mv88e6xxx_vtu_entry *entry)440{441	int err;442 443	err = mv88e6xxx_g1_vtu_op_wait(chip);444	if (err)445		return err;446 447	err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, entry->vid);448	if (err)449		return err;450 451	if (entry->valid) {452		/* Write MemberTag data */453		err = mv88e6390_g1_vtu_data_write(chip, entry->member);454		if (err)455			return err;456 457		err = mv88e6xxx_g1_vtu_fid_write(chip, entry);458		if (err)459			return err;460 461		err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid);462		if (err)463			return err;464	}465 466	/* Load/Purge VTU entry */467	return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE);468}469 470int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip)471{472	int err;473 474	err = mv88e6xxx_g1_vtu_op_wait(chip);475	if (err)476		return err;477 478	return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_FLUSH_ALL);479}480 481/* Spanning Tree Unit Operations */482 483int mv88e6xxx_g1_stu_getnext(struct mv88e6xxx_chip *chip,484			     struct mv88e6xxx_stu_entry *entry)485{486	int err;487 488	err = mv88e6xxx_g1_vtu_op_wait(chip);489	if (err)490		return err;491 492	/* To get the next higher active SID, the STU GetNext operation can be493	 * started again without setting the SID registers since it already494	 * contains the last SID.495	 *496	 * To save a few hardware accesses and abstract this to the caller,497	 * write the SID only once, when the entry is given as invalid.498	 */499	if (!entry->valid) {500		err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid);501		if (err)502			return err;503	}504 505	err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_GET_NEXT);506	if (err)507		return err;508 509	err = mv88e6xxx_g1_vtu_vid_read(chip, &entry->valid, NULL);510	if (err)511		return err;512 513	if (entry->valid) {514		err = mv88e6xxx_g1_vtu_sid_read(chip, &entry->sid);515		if (err)516			return err;517	}518 519	return 0;520}521 522int mv88e6352_g1_stu_getnext(struct mv88e6xxx_chip *chip,523			     struct mv88e6xxx_stu_entry *entry)524{525	int err;526 527	err = mv88e6xxx_g1_stu_getnext(chip, entry);528	if (err)529		return err;530 531	if (!entry->valid)532		return 0;533 534	return mv88e6185_g1_vtu_data_read(chip, NULL, entry->state);535}536 537int mv88e6390_g1_stu_getnext(struct mv88e6xxx_chip *chip,538			     struct mv88e6xxx_stu_entry *entry)539{540	int err;541 542	err = mv88e6xxx_g1_stu_getnext(chip, entry);543	if (err)544		return err;545 546	if (!entry->valid)547		return 0;548 549	return mv88e6390_g1_vtu_data_read(chip, entry->state);550}551 552int mv88e6352_g1_stu_loadpurge(struct mv88e6xxx_chip *chip,553			       struct mv88e6xxx_stu_entry *entry)554{555	int err;556 557	err = mv88e6xxx_g1_vtu_op_wait(chip);558	if (err)559		return err;560 561	err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, 0);562	if (err)563		return err;564 565	err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid);566	if (err)567		return err;568 569	if (entry->valid) {570		err = mv88e6185_g1_vtu_data_write(chip, NULL, entry->state);571		if (err)572			return err;573	}574 575	/* Load/Purge STU entry */576	return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE);577}578 579int mv88e6390_g1_stu_loadpurge(struct mv88e6xxx_chip *chip,580			       struct mv88e6xxx_stu_entry *entry)581{582	int err;583 584	err = mv88e6xxx_g1_vtu_op_wait(chip);585	if (err)586		return err;587 588	err = mv88e6xxx_g1_vtu_vid_write(chip, entry->valid, 0);589	if (err)590		return err;591 592	err = mv88e6xxx_g1_vtu_sid_write(chip, entry->sid);593	if (err)594		return err;595 596	if (entry->valid) {597		err = mv88e6390_g1_vtu_data_write(chip, entry->state);598		if (err)599			return err;600	}601 602	/* Load/Purge STU entry */603	return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE);604}605 606/* VTU Violation Management */607 608static irqreturn_t mv88e6xxx_g1_vtu_prob_irq_thread_fn(int irq, void *dev_id)609{610	struct mv88e6xxx_chip *chip = dev_id;611	u16 val, vid;612	int spid;613	int err;614 615	mv88e6xxx_reg_lock(chip);616 617	err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_GET_CLR_VIOLATION);618	if (err)619		goto out;620 621	err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val);622	if (err)623		goto out;624 625	err = mv88e6xxx_g1_vtu_vid_read(chip, NULL, &vid);626	if (err)627		goto out;628 629	spid = val & MV88E6XXX_G1_VTU_OP_SPID_MASK;630 631	if (val & MV88E6XXX_G1_VTU_OP_MEMBER_VIOLATION) {632		trace_mv88e6xxx_vtu_member_violation(chip->dev, spid, vid);633		chip->ports[spid].vtu_member_violation++;634	}635 636	if (val & MV88E6XXX_G1_VTU_OP_MISS_VIOLATION) {637		trace_mv88e6xxx_vtu_miss_violation(chip->dev, spid, vid);638		chip->ports[spid].vtu_miss_violation++;639	}640 641	mv88e6xxx_reg_unlock(chip);642 643	return IRQ_HANDLED;644 645out:646	mv88e6xxx_reg_unlock(chip);647 648	dev_err(chip->dev, "VTU problem: error %d while handling interrupt\n",649		err);650 651	return IRQ_HANDLED;652}653 654int mv88e6xxx_g1_vtu_prob_irq_setup(struct mv88e6xxx_chip *chip)655{656	int err;657 658	chip->vtu_prob_irq = irq_find_mapping(chip->g1_irq.domain,659					      MV88E6XXX_G1_STS_IRQ_VTU_PROB);660	if (chip->vtu_prob_irq < 0)661		return chip->vtu_prob_irq;662 663	snprintf(chip->vtu_prob_irq_name, sizeof(chip->vtu_prob_irq_name),664		 "mv88e6xxx-%s-g1-vtu-prob", dev_name(chip->dev));665 666	err = request_threaded_irq(chip->vtu_prob_irq, NULL,667				   mv88e6xxx_g1_vtu_prob_irq_thread_fn,668				   IRQF_ONESHOT, chip->vtu_prob_irq_name,669				   chip);670	if (err)671		irq_dispose_mapping(chip->vtu_prob_irq);672 673	return err;674}675 676void mv88e6xxx_g1_vtu_prob_irq_free(struct mv88e6xxx_chip *chip)677{678	free_irq(chip->vtu_prob_irq, chip);679	irq_dispose_mapping(chip->vtu_prob_irq);680}681