mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-26 23:05:14 +07:00
c9acece064
This is a no-op that simply moves all locking and unlocking of ->reg_lock into trivial helpers. I did that to be able to easily add some ad hoc instrumentation to those helpers to get some information on contention and hold times of the mutex. Perhaps others want to do something similar at some point, so this frees them from doing the 'sed -i' yoga, and have a much smaller 'git diff' while fiddling. Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk> Reviewed-by: Vivien Didelot <vivien.didelot@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
647 lines
14 KiB
C
647 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Marvell 88E6xxx VLAN [Spanning Tree] Translation Unit (VTU [STU]) support
|
|
*
|
|
* Copyright (c) 2008 Marvell Semiconductor
|
|
* Copyright (c) 2015 CMC Electronics, Inc.
|
|
* Copyright (c) 2017 Savoir-faire Linux, Inc.
|
|
*/
|
|
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irqdomain.h>
|
|
|
|
#include "chip.h"
|
|
#include "global1.h"
|
|
|
|
/* Offset 0x02: VTU FID Register */
|
|
|
|
static int mv88e6xxx_g1_vtu_fid_read(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
u16 val;
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_FID, &val);
|
|
if (err)
|
|
return err;
|
|
|
|
entry->fid = val & MV88E6352_G1_VTU_FID_MASK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mv88e6xxx_g1_vtu_fid_write(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
u16 val = entry->fid & MV88E6352_G1_VTU_FID_MASK;
|
|
|
|
return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_FID, val);
|
|
}
|
|
|
|
/* Offset 0x03: VTU SID Register */
|
|
|
|
static int mv88e6xxx_g1_vtu_sid_read(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
u16 val;
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_read(chip, MV88E6352_G1_VTU_SID, &val);
|
|
if (err)
|
|
return err;
|
|
|
|
entry->sid = val & MV88E6352_G1_VTU_SID_MASK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mv88e6xxx_g1_vtu_sid_write(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
u16 val = entry->sid & MV88E6352_G1_VTU_SID_MASK;
|
|
|
|
return mv88e6xxx_g1_write(chip, MV88E6352_G1_VTU_SID, val);
|
|
}
|
|
|
|
/* Offset 0x05: VTU Operation Register */
|
|
|
|
static int mv88e6xxx_g1_vtu_op_wait(struct mv88e6xxx_chip *chip)
|
|
{
|
|
return mv88e6xxx_g1_wait(chip, MV88E6XXX_G1_VTU_OP,
|
|
MV88E6XXX_G1_VTU_OP_BUSY);
|
|
}
|
|
|
|
static int mv88e6xxx_g1_vtu_op(struct mv88e6xxx_chip *chip, u16 op)
|
|
{
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_OP,
|
|
MV88E6XXX_G1_VTU_OP_BUSY | op);
|
|
if (err)
|
|
return err;
|
|
|
|
return mv88e6xxx_g1_vtu_op_wait(chip);
|
|
}
|
|
|
|
/* Offset 0x06: VTU VID Register */
|
|
|
|
static int mv88e6xxx_g1_vtu_vid_read(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
u16 val;
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_VID, &val);
|
|
if (err)
|
|
return err;
|
|
|
|
entry->vid = val & 0xfff;
|
|
|
|
if (val & MV88E6390_G1_VTU_VID_PAGE)
|
|
entry->vid |= 0x1000;
|
|
|
|
entry->valid = !!(val & MV88E6XXX_G1_VTU_VID_VALID);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mv88e6xxx_g1_vtu_vid_write(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
u16 val = entry->vid & 0xfff;
|
|
|
|
if (entry->vid & 0x1000)
|
|
val |= MV88E6390_G1_VTU_VID_PAGE;
|
|
|
|
if (entry->valid)
|
|
val |= MV88E6XXX_G1_VTU_VID_VALID;
|
|
|
|
return mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_VID, val);
|
|
}
|
|
|
|
/* Offset 0x07: VTU/STU Data Register 1
|
|
* Offset 0x08: VTU/STU Data Register 2
|
|
* Offset 0x09: VTU/STU Data Register 3
|
|
*/
|
|
|
|
static int mv88e6185_g1_vtu_data_read(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
u16 regs[3];
|
|
int i;
|
|
|
|
/* Read all 3 VTU/STU Data registers */
|
|
for (i = 0; i < 3; ++i) {
|
|
u16 *reg = ®s[i];
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
/* Extract MemberTag and PortState data */
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
|
unsigned int member_offset = (i % 4) * 4;
|
|
unsigned int state_offset = member_offset + 2;
|
|
|
|
entry->member[i] = (regs[i / 4] >> member_offset) & 0x3;
|
|
entry->state[i] = (regs[i / 4] >> state_offset) & 0x3;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mv88e6185_g1_vtu_data_write(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
u16 regs[3] = { 0 };
|
|
int i;
|
|
|
|
/* Insert MemberTag and PortState data */
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
|
unsigned int member_offset = (i % 4) * 4;
|
|
unsigned int state_offset = member_offset + 2;
|
|
|
|
regs[i / 4] |= (entry->member[i] & 0x3) << member_offset;
|
|
regs[i / 4] |= (entry->state[i] & 0x3) << state_offset;
|
|
}
|
|
|
|
/* Write all 3 VTU/STU Data registers */
|
|
for (i = 0; i < 3; ++i) {
|
|
u16 reg = regs[i];
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mv88e6390_g1_vtu_data_read(struct mv88e6xxx_chip *chip, u8 *data)
|
|
{
|
|
u16 regs[2];
|
|
int i;
|
|
|
|
/* Read the 2 VTU/STU Data registers */
|
|
for (i = 0; i < 2; ++i) {
|
|
u16 *reg = ®s[i];
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
/* Extract data */
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
|
unsigned int offset = (i % 8) * 2;
|
|
|
|
data[i] = (regs[i / 8] >> offset) & 0x3;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mv88e6390_g1_vtu_data_write(struct mv88e6xxx_chip *chip, u8 *data)
|
|
{
|
|
u16 regs[2] = { 0 };
|
|
int i;
|
|
|
|
/* Insert data */
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
|
|
unsigned int offset = (i % 8) * 2;
|
|
|
|
regs[i / 8] |= (data[i] & 0x3) << offset;
|
|
}
|
|
|
|
/* Write the 2 VTU/STU Data registers */
|
|
for (i = 0; i < 2; ++i) {
|
|
u16 reg = regs[i];
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_write(chip, MV88E6XXX_G1_VTU_DATA1 + i, reg);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* VLAN Translation Unit Operations */
|
|
|
|
static int mv88e6xxx_g1_vtu_stu_getnext(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_STU_GET_NEXT);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mv88e6xxx_g1_vtu_sid_read(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
return mv88e6xxx_g1_vtu_vid_read(chip, entry);
|
|
}
|
|
|
|
static int mv88e6xxx_g1_vtu_stu_get(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *vtu)
|
|
{
|
|
struct mv88e6xxx_vtu_entry stu;
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_vtu_sid_read(chip, vtu);
|
|
if (err)
|
|
return err;
|
|
|
|
stu.sid = vtu->sid - 1;
|
|
|
|
err = mv88e6xxx_g1_vtu_stu_getnext(chip, &stu);
|
|
if (err)
|
|
return err;
|
|
|
|
if (stu.sid != vtu->sid || !stu.valid)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_vtu_op_wait(chip);
|
|
if (err)
|
|
return err;
|
|
|
|
/* To get the next higher active VID, the VTU GetNext operation can be
|
|
* started again without setting the VID registers since it already
|
|
* contains the last VID.
|
|
*
|
|
* To save a few hardware accesses and abstract this to the caller,
|
|
* write the VID only once, when the entry is given as invalid.
|
|
*/
|
|
if (!entry->valid) {
|
|
err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_GET_NEXT);
|
|
if (err)
|
|
return err;
|
|
|
|
return mv88e6xxx_g1_vtu_vid_read(chip, entry);
|
|
}
|
|
|
|
int mv88e6250_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
u16 val;
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_vtu_getnext(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
if (entry->valid) {
|
|
err = mv88e6185_g1_vtu_data_read(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
/* VTU DBNum[3:0] are located in VTU Operation 3:0
|
|
* VTU DBNum[5:4] are located in VTU Operation 9:8
|
|
*/
|
|
err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val);
|
|
if (err)
|
|
return err;
|
|
|
|
entry->fid = val & 0x000f;
|
|
entry->fid |= (val & 0x0300) >> 4;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
u16 val;
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_vtu_getnext(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
if (entry->valid) {
|
|
err = mv88e6185_g1_vtu_data_read(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
/* VTU DBNum[3:0] are located in VTU Operation 3:0
|
|
* VTU DBNum[7:4] are located in VTU Operation 11:8
|
|
*/
|
|
err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val);
|
|
if (err)
|
|
return err;
|
|
|
|
entry->fid = val & 0x000f;
|
|
entry->fid |= (val & 0x0f00) >> 4;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
int err;
|
|
|
|
/* Fetch VLAN MemberTag data from the VTU */
|
|
err = mv88e6xxx_g1_vtu_getnext(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
if (entry->valid) {
|
|
/* Fetch (and mask) VLAN PortState data from the STU */
|
|
err = mv88e6xxx_g1_vtu_stu_get(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mv88e6185_g1_vtu_data_read(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mv88e6xxx_g1_vtu_fid_read(chip, entry);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
int err;
|
|
|
|
/* Fetch VLAN MemberTag data from the VTU */
|
|
err = mv88e6xxx_g1_vtu_getnext(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
if (entry->valid) {
|
|
err = mv88e6390_g1_vtu_data_read(chip, entry->member);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Fetch VLAN PortState data from the STU */
|
|
err = mv88e6xxx_g1_vtu_stu_get(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mv88e6390_g1_vtu_data_read(chip, entry->state);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mv88e6xxx_g1_vtu_fid_read(chip, entry);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mv88e6250_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
u16 op = MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE;
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_vtu_op_wait(chip);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
if (entry->valid) {
|
|
err = mv88e6185_g1_vtu_data_write(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
/* VTU DBNum[3:0] are located in VTU Operation 3:0
|
|
* VTU DBNum[5:4] are located in VTU Operation 9:8
|
|
*/
|
|
op |= entry->fid & 0x000f;
|
|
op |= (entry->fid & 0x0030) << 4;
|
|
}
|
|
|
|
return mv88e6xxx_g1_vtu_op(chip, op);
|
|
}
|
|
|
|
int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
u16 op = MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE;
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_vtu_op_wait(chip);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
if (entry->valid) {
|
|
err = mv88e6185_g1_vtu_data_write(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
/* VTU DBNum[3:0] are located in VTU Operation 3:0
|
|
* VTU DBNum[7:4] are located in VTU Operation 11:8
|
|
*/
|
|
op |= entry->fid & 0x000f;
|
|
op |= (entry->fid & 0x00f0) << 4;
|
|
}
|
|
|
|
return mv88e6xxx_g1_vtu_op(chip, op);
|
|
}
|
|
|
|
int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_vtu_op_wait(chip);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
if (entry->valid) {
|
|
/* Write MemberTag and PortState data */
|
|
err = mv88e6185_g1_vtu_data_write(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Load STU entry */
|
|
err = mv88e6xxx_g1_vtu_op(chip,
|
|
MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mv88e6xxx_g1_vtu_fid_write(chip, entry);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
/* Load/Purge VTU entry */
|
|
return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE);
|
|
}
|
|
|
|
int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
|
|
struct mv88e6xxx_vtu_entry *entry)
|
|
{
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_vtu_op_wait(chip);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
if (entry->valid) {
|
|
/* Write PortState data */
|
|
err = mv88e6390_g1_vtu_data_write(chip, entry->state);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mv88e6xxx_g1_vtu_sid_write(chip, entry);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Load STU entry */
|
|
err = mv88e6xxx_g1_vtu_op(chip,
|
|
MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Write MemberTag data */
|
|
err = mv88e6390_g1_vtu_data_write(chip, entry->member);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mv88e6xxx_g1_vtu_fid_write(chip, entry);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
/* Load/Purge VTU entry */
|
|
return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE);
|
|
}
|
|
|
|
int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip)
|
|
{
|
|
int err;
|
|
|
|
err = mv88e6xxx_g1_vtu_op_wait(chip);
|
|
if (err)
|
|
return err;
|
|
|
|
return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_FLUSH_ALL);
|
|
}
|
|
|
|
static irqreturn_t mv88e6xxx_g1_vtu_prob_irq_thread_fn(int irq, void *dev_id)
|
|
{
|
|
struct mv88e6xxx_chip *chip = dev_id;
|
|
struct mv88e6xxx_vtu_entry entry;
|
|
int spid;
|
|
int err;
|
|
u16 val;
|
|
|
|
mv88e6xxx_reg_lock(chip);
|
|
|
|
err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_GET_CLR_VIOLATION);
|
|
if (err)
|
|
goto out;
|
|
|
|
err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val);
|
|
if (err)
|
|
goto out;
|
|
|
|
err = mv88e6xxx_g1_vtu_vid_read(chip, &entry);
|
|
if (err)
|
|
goto out;
|
|
|
|
spid = val & MV88E6XXX_G1_VTU_OP_SPID_MASK;
|
|
|
|
if (val & MV88E6XXX_G1_VTU_OP_MEMBER_VIOLATION) {
|
|
dev_err_ratelimited(chip->dev, "VTU member violation for vid %d, source port %d\n",
|
|
entry.vid, spid);
|
|
chip->ports[spid].vtu_member_violation++;
|
|
}
|
|
|
|
if (val & MV88E6XXX_G1_VTU_OP_MISS_VIOLATION) {
|
|
dev_dbg_ratelimited(chip->dev, "VTU miss violation for vid %d, source port %d\n",
|
|
entry.vid, spid);
|
|
chip->ports[spid].vtu_miss_violation++;
|
|
}
|
|
|
|
mv88e6xxx_reg_unlock(chip);
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
out:
|
|
mv88e6xxx_reg_unlock(chip);
|
|
|
|
dev_err(chip->dev, "VTU problem: error %d while handling interrupt\n",
|
|
err);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
int mv88e6xxx_g1_vtu_prob_irq_setup(struct mv88e6xxx_chip *chip)
|
|
{
|
|
int err;
|
|
|
|
chip->vtu_prob_irq = irq_find_mapping(chip->g1_irq.domain,
|
|
MV88E6XXX_G1_STS_IRQ_VTU_PROB);
|
|
if (chip->vtu_prob_irq < 0)
|
|
return chip->vtu_prob_irq;
|
|
|
|
err = request_threaded_irq(chip->vtu_prob_irq, NULL,
|
|
mv88e6xxx_g1_vtu_prob_irq_thread_fn,
|
|
IRQF_ONESHOT, "mv88e6xxx-g1-vtu-prob",
|
|
chip);
|
|
if (err)
|
|
irq_dispose_mapping(chip->vtu_prob_irq);
|
|
|
|
return err;
|
|
}
|
|
|
|
void mv88e6xxx_g1_vtu_prob_irq_free(struct mv88e6xxx_chip *chip)
|
|
{
|
|
free_irq(chip->vtu_prob_irq, chip);
|
|
irq_dispose_mapping(chip->vtu_prob_irq);
|
|
}
|