mirror of
https://github.com/torvalds/linux.git
synced 2026-06-03 03:53:37 +02:00
Merge branch 'net-dsa-microchip-add-ksz8463-switch-support'
Tristram Ha says: ==================== net: dsa: microchip: Add KSZ8463 switch support This series of patches is to add KSZ8463 switch support to the KSZ DSA driver. ==================== Link: https://patch.msgid.link/20250725001753.6330-1-Tristram.Ha@microchip.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
e3f96b3556
|
|
@ -18,6 +18,7 @@ properties:
|
|||
# required and optional properties.
|
||||
compatible:
|
||||
enum:
|
||||
- microchip,ksz8463
|
||||
- microchip,ksz8765
|
||||
- microchip,ksz8794
|
||||
- microchip,ksz8795
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* Microchip KSZ8XXX series switch driver
|
||||
*
|
||||
* It supports the following switches:
|
||||
* - KSZ8463
|
||||
* - KSZ8863, KSZ8873 aka KSZ88X3
|
||||
* - KSZ8895, KSZ8864 aka KSZ8895 family
|
||||
* - KSZ8794, KSZ8795, KSZ8765 aka KSZ87XX
|
||||
|
|
@ -41,7 +42,8 @@ static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set)
|
|||
static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits,
|
||||
bool set)
|
||||
{
|
||||
regmap_update_bits(ksz_regmap_8(dev), PORT_CTRL_ADDR(port, offset),
|
||||
regmap_update_bits(ksz_regmap_8(dev),
|
||||
dev->dev_ops->get_port_addr(port, offset),
|
||||
bits, set ? bits : 0);
|
||||
}
|
||||
|
||||
|
|
@ -140,6 +142,11 @@ int ksz8_reset_switch(struct ksz_device *dev)
|
|||
KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, true);
|
||||
ksz_cfg(dev, KSZ8863_REG_SW_RESET,
|
||||
KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, false);
|
||||
} else if (ksz_is_ksz8463(dev)) {
|
||||
ksz_cfg(dev, KSZ8463_REG_SW_RESET,
|
||||
KSZ8463_GLOBAL_SOFTWARE_RESET, true);
|
||||
ksz_cfg(dev, KSZ8463_REG_SW_RESET,
|
||||
KSZ8463_GLOBAL_SOFTWARE_RESET, false);
|
||||
} else {
|
||||
/* reset switch */
|
||||
ksz_write8(dev, REG_POWER_MANAGEMENT_1,
|
||||
|
|
@ -194,6 +201,7 @@ int ksz8_change_mtu(struct ksz_device *dev, int port, int mtu)
|
|||
case KSZ8794_CHIP_ID:
|
||||
case KSZ8765_CHIP_ID:
|
||||
return ksz8795_change_mtu(dev, frame_size);
|
||||
case KSZ8463_CHIP_ID:
|
||||
case KSZ88X3_CHIP_ID:
|
||||
case KSZ8864_CHIP_ID:
|
||||
case KSZ8895_CHIP_ID:
|
||||
|
|
@ -227,6 +235,11 @@ static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues)
|
|||
WEIGHTED_FAIR_QUEUE_ENABLE);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (ksz_is_ksz8463(dev)) {
|
||||
mask_4q = KSZ8873_PORT_4QUEUE_SPLIT_EN;
|
||||
mask_2q = KSZ8873_PORT_2QUEUE_SPLIT_EN;
|
||||
reg_4q = P1CR1;
|
||||
reg_2q = P1CR1 + 1;
|
||||
} else {
|
||||
mask_4q = KSZ8795_PORT_4QUEUE_SPLIT_EN;
|
||||
mask_2q = KSZ8795_PORT_2QUEUE_SPLIT_EN;
|
||||
|
|
@ -1265,12 +1278,15 @@ int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val)
|
|||
|
||||
void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member)
|
||||
{
|
||||
int offset = P_MIRROR_CTRL;
|
||||
u8 data;
|
||||
|
||||
ksz_pread8(dev, port, P_MIRROR_CTRL, &data);
|
||||
data &= ~PORT_VLAN_MEMBERSHIP;
|
||||
if (ksz_is_ksz8463(dev))
|
||||
offset = P1CR2;
|
||||
ksz_pread8(dev, port, offset, &data);
|
||||
data &= ~dev->port_mask;
|
||||
data |= (member & dev->port_mask);
|
||||
ksz_pwrite8(dev, port, P_MIRROR_CTRL, data);
|
||||
ksz_pwrite8(dev, port, offset, data);
|
||||
}
|
||||
|
||||
void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port)
|
||||
|
|
@ -1278,6 +1294,8 @@ void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port)
|
|||
u8 learn[DSA_MAX_PORTS];
|
||||
int first, index, cnt;
|
||||
const u16 *regs;
|
||||
int reg = S_FLUSH_TABLE_CTRL;
|
||||
int mask = SW_FLUSH_DYN_MAC_TABLE;
|
||||
|
||||
regs = dev->info->regs;
|
||||
|
||||
|
|
@ -1295,7 +1313,11 @@ void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port)
|
|||
ksz_pwrite8(dev, index, regs[P_STP_CTRL],
|
||||
learn[index] | PORT_LEARN_DISABLE);
|
||||
}
|
||||
ksz_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_DYN_MAC_TABLE, true);
|
||||
if (ksz_is_ksz8463(dev)) {
|
||||
reg = KSZ8463_FLUSH_TABLE_CTRL;
|
||||
mask = KSZ8463_FLUSH_DYN_MAC_TABLE;
|
||||
}
|
||||
ksz_cfg(dev, reg, mask, true);
|
||||
for (index = first; index < cnt; index++) {
|
||||
if (!(learn[index] & PORT_LEARN_DISABLE))
|
||||
ksz_pwrite8(dev, index, regs[P_STP_CTRL], learn[index]);
|
||||
|
|
@ -1434,7 +1456,7 @@ int ksz8_fdb_del(struct ksz_device *dev, int port, const unsigned char *addr,
|
|||
int ksz8_port_vlan_filtering(struct ksz_device *dev, int port, bool flag,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
if (ksz_is_ksz88x3(dev))
|
||||
if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev))
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* Discard packets with VID not enabled on the switch */
|
||||
|
|
@ -1450,9 +1472,12 @@ int ksz8_port_vlan_filtering(struct ksz_device *dev, int port, bool flag,
|
|||
|
||||
static void ksz8_port_enable_pvid(struct ksz_device *dev, int port, bool state)
|
||||
{
|
||||
if (ksz_is_ksz88x3(dev)) {
|
||||
ksz_cfg(dev, REG_SW_INSERT_SRC_PVID,
|
||||
0x03 << (4 - 2 * port), state);
|
||||
if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)) {
|
||||
int reg = REG_SW_INSERT_SRC_PVID;
|
||||
|
||||
if (ksz_is_ksz8463(dev))
|
||||
reg = KSZ8463_REG_SW_CTRL_9;
|
||||
ksz_cfg(dev, reg, 0x03 << (4 - 2 * port), state);
|
||||
} else {
|
||||
ksz_pwrite8(dev, port, REG_PORT_CTRL_12, state ? 0x0f : 0x00);
|
||||
}
|
||||
|
|
@ -1467,7 +1492,7 @@ int ksz8_port_vlan_add(struct ksz_device *dev, int port,
|
|||
u16 data, new_pvid = 0;
|
||||
u8 fid, member, valid;
|
||||
|
||||
if (ksz_is_ksz88x3(dev))
|
||||
if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev))
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* If a VLAN is added with untagged flag different from the
|
||||
|
|
@ -1536,7 +1561,7 @@ int ksz8_port_vlan_del(struct ksz_device *dev, int port,
|
|||
u16 data, pvid;
|
||||
u8 fid, member, valid;
|
||||
|
||||
if (ksz_is_ksz88x3(dev))
|
||||
if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev))
|
||||
return -ENOTSUPP;
|
||||
|
||||
ksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid);
|
||||
|
|
@ -1566,19 +1591,23 @@ int ksz8_port_mirror_add(struct ksz_device *dev, int port,
|
|||
struct dsa_mall_mirror_tc_entry *mirror,
|
||||
bool ingress, struct netlink_ext_ack *extack)
|
||||
{
|
||||
int offset = P_MIRROR_CTRL;
|
||||
|
||||
if (ksz_is_ksz8463(dev))
|
||||
offset = P1CR2;
|
||||
if (ingress) {
|
||||
ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true);
|
||||
ksz_port_cfg(dev, port, offset, PORT_MIRROR_RX, true);
|
||||
dev->mirror_rx |= BIT(port);
|
||||
} else {
|
||||
ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true);
|
||||
ksz_port_cfg(dev, port, offset, PORT_MIRROR_TX, true);
|
||||
dev->mirror_tx |= BIT(port);
|
||||
}
|
||||
|
||||
ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false);
|
||||
ksz_port_cfg(dev, port, offset, PORT_MIRROR_SNIFFER, false);
|
||||
|
||||
/* configure mirror port */
|
||||
if (dev->mirror_rx || dev->mirror_tx)
|
||||
ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
|
||||
ksz_port_cfg(dev, mirror->to_local_port, offset,
|
||||
PORT_MIRROR_SNIFFER, true);
|
||||
|
||||
return 0;
|
||||
|
|
@ -1587,20 +1616,23 @@ int ksz8_port_mirror_add(struct ksz_device *dev, int port,
|
|||
void ksz8_port_mirror_del(struct ksz_device *dev, int port,
|
||||
struct dsa_mall_mirror_tc_entry *mirror)
|
||||
{
|
||||
int offset = P_MIRROR_CTRL;
|
||||
u8 data;
|
||||
|
||||
if (ksz_is_ksz8463(dev))
|
||||
offset = P1CR2;
|
||||
if (mirror->ingress) {
|
||||
ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false);
|
||||
ksz_port_cfg(dev, port, offset, PORT_MIRROR_RX, false);
|
||||
dev->mirror_rx &= ~BIT(port);
|
||||
} else {
|
||||
ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false);
|
||||
ksz_port_cfg(dev, port, offset, PORT_MIRROR_TX, false);
|
||||
dev->mirror_tx &= ~BIT(port);
|
||||
}
|
||||
|
||||
ksz_pread8(dev, port, P_MIRROR_CTRL, &data);
|
||||
ksz_pread8(dev, port, offset, &data);
|
||||
|
||||
if (!dev->mirror_rx && !dev->mirror_tx)
|
||||
ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL,
|
||||
ksz_port_cfg(dev, mirror->to_local_port, offset,
|
||||
PORT_MIRROR_SNIFFER, false);
|
||||
}
|
||||
|
||||
|
|
@ -1625,17 +1657,24 @@ void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port)
|
|||
const u16 *regs = dev->info->regs;
|
||||
struct dsa_switch *ds = dev->ds;
|
||||
const u32 *masks;
|
||||
int offset;
|
||||
u8 member;
|
||||
|
||||
masks = dev->info->masks;
|
||||
|
||||
/* enable broadcast storm limit */
|
||||
ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true);
|
||||
offset = P_BCAST_STORM_CTRL;
|
||||
if (ksz_is_ksz8463(dev))
|
||||
offset = P1CR1;
|
||||
ksz_port_cfg(dev, port, offset, PORT_BROADCAST_STORM, true);
|
||||
|
||||
ksz8_port_queue_split(dev, port, dev->info->num_tx_queues);
|
||||
|
||||
/* replace priority */
|
||||
ksz_port_cfg(dev, port, P_802_1P_CTRL,
|
||||
offset = P_802_1P_CTRL;
|
||||
if (ksz_is_ksz8463(dev))
|
||||
offset = P1CR2;
|
||||
ksz_port_cfg(dev, port, offset,
|
||||
masks[PORT_802_1P_REMAPPING], false);
|
||||
|
||||
if (cpu_port)
|
||||
|
|
@ -1675,6 +1714,7 @@ void ksz8_config_cpu_port(struct dsa_switch *ds)
|
|||
const u32 *masks;
|
||||
const u16 *regs;
|
||||
u8 remote;
|
||||
u8 fiber_ports = 0;
|
||||
int i;
|
||||
|
||||
masks = dev->info->masks;
|
||||
|
|
@ -1705,6 +1745,32 @@ void ksz8_config_cpu_port(struct dsa_switch *ds)
|
|||
else
|
||||
ksz_port_cfg(dev, i, regs[P_STP_CTRL],
|
||||
PORT_FORCE_FLOW_CTRL, false);
|
||||
if (p->fiber)
|
||||
fiber_ports |= (1 << i);
|
||||
}
|
||||
if (ksz_is_ksz8463(dev)) {
|
||||
/* Setup fiber ports. */
|
||||
if (fiber_ports) {
|
||||
fiber_ports &= 3;
|
||||
regmap_update_bits(ksz_regmap_16(dev),
|
||||
KSZ8463_REG_CFG_CTRL,
|
||||
fiber_ports << PORT_COPPER_MODE_S,
|
||||
0);
|
||||
regmap_update_bits(ksz_regmap_16(dev),
|
||||
KSZ8463_REG_DSP_CTRL_6,
|
||||
COPPER_RECEIVE_ADJUSTMENT, 0);
|
||||
}
|
||||
|
||||
/* Turn off PTP function as the switch's proprietary way of
|
||||
* handling timestamp is not supported in current Linux PTP
|
||||
* stack implementation.
|
||||
*/
|
||||
regmap_update_bits(ksz_regmap_16(dev),
|
||||
KSZ8463_PTP_MSG_CONF1,
|
||||
PTP_ENABLE, 0);
|
||||
regmap_update_bits(ksz_regmap_16(dev),
|
||||
KSZ8463_PTP_CLK_CTRL,
|
||||
PTP_CLK_ENABLE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1901,7 +1967,7 @@ int ksz8_setup(struct dsa_switch *ds)
|
|||
|
||||
ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
|
||||
|
||||
if (!ksz_is_ksz88x3(dev))
|
||||
if (!ksz_is_ksz88x3(dev) && !ksz_is_ksz8463(dev))
|
||||
ksz_cfg(dev, REG_SW_CTRL_19, SW_INS_TAG_ENABLE, true);
|
||||
|
||||
for (i = 0; i < (dev->info->num_vlans / 4); i++)
|
||||
|
|
@ -1947,6 +2013,84 @@ u32 ksz8_get_port_addr(int port, int offset)
|
|||
return PORT_CTRL_ADDR(port, offset);
|
||||
}
|
||||
|
||||
u32 ksz8463_get_port_addr(int port, int offset)
|
||||
{
|
||||
return offset + 0x18 * port;
|
||||
}
|
||||
|
||||
static u16 ksz8463_get_phy_addr(u16 phy, u16 reg, u16 offset)
|
||||
{
|
||||
return offset + reg * 2 + phy * (P2MBCR - P1MBCR);
|
||||
}
|
||||
|
||||
int ksz8463_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val)
|
||||
{
|
||||
u16 sw_reg = 0;
|
||||
u16 data = 0;
|
||||
int ret;
|
||||
|
||||
if (phy > 1)
|
||||
return -ENOSPC;
|
||||
switch (reg) {
|
||||
case MII_PHYSID1:
|
||||
sw_reg = ksz8463_get_phy_addr(phy, 0, PHY1IHR);
|
||||
break;
|
||||
case MII_PHYSID2:
|
||||
sw_reg = ksz8463_get_phy_addr(phy, 0, PHY1ILR);
|
||||
break;
|
||||
case MII_BMCR:
|
||||
case MII_BMSR:
|
||||
case MII_ADVERTISE:
|
||||
case MII_LPA:
|
||||
sw_reg = ksz8463_get_phy_addr(phy, reg, P1MBCR);
|
||||
break;
|
||||
case MII_TPISTATUS:
|
||||
/* This register holds the PHY interrupt status for simulated
|
||||
* Micrel KSZ PHY.
|
||||
*/
|
||||
data = 0x0505;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (sw_reg) {
|
||||
ret = ksz_read16(dev, sw_reg, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
*val = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksz8463_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val)
|
||||
{
|
||||
u16 sw_reg = 0;
|
||||
int ret;
|
||||
|
||||
if (phy > 1)
|
||||
return -ENOSPC;
|
||||
|
||||
/* No write to fiber port. */
|
||||
if (dev->ports[phy].fiber)
|
||||
return 0;
|
||||
switch (reg) {
|
||||
case MII_BMCR:
|
||||
case MII_ADVERTISE:
|
||||
sw_reg = ksz8463_get_phy_addr(phy, reg, P1MBCR);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (sw_reg) {
|
||||
ret = ksz_write16(dev, sw_reg, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksz8_switch_init(struct ksz_device *dev)
|
||||
{
|
||||
dev->cpu_port = fls(dev->info->cpu_ports) - 1;
|
||||
|
|
|
|||
|
|
@ -63,4 +63,8 @@ void ksz8_phylink_mac_link_up(struct phylink_config *config,
|
|||
bool tx_pause, bool rx_pause);
|
||||
int ksz8_all_queues_split(struct ksz_device *dev, int queues);
|
||||
|
||||
u32 ksz8463_get_port_addr(int port, int offset);
|
||||
int ksz8463_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val);
|
||||
int ksz8463_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -729,6 +729,55 @@
|
|||
#define PHY_POWER_SAVING_ENABLE BIT(2)
|
||||
#define PHY_REMOTE_LOOPBACK BIT(1)
|
||||
|
||||
/* KSZ8463 specific registers. */
|
||||
#define P1MBCR 0x4C
|
||||
#define P1MBSR 0x4E
|
||||
#define PHY1ILR 0x50
|
||||
#define PHY1IHR 0x52
|
||||
#define P1ANAR 0x54
|
||||
#define P1ANLPR 0x56
|
||||
#define P2MBCR 0x58
|
||||
#define P2MBSR 0x5A
|
||||
#define PHY2ILR 0x5C
|
||||
#define PHY2IHR 0x5E
|
||||
#define P2ANAR 0x60
|
||||
#define P2ANLPR 0x62
|
||||
|
||||
#define P1CR1 0x6C
|
||||
#define P1CR2 0x6E
|
||||
#define P1CR3 0x72
|
||||
#define P1CR4 0x7E
|
||||
#define P1SR 0x80
|
||||
|
||||
#define KSZ8463_FLUSH_TABLE_CTRL 0xAD
|
||||
|
||||
#define KSZ8463_FLUSH_DYN_MAC_TABLE BIT(2)
|
||||
#define KSZ8463_FLUSH_STA_MAC_TABLE BIT(1)
|
||||
|
||||
#define KSZ8463_REG_SW_CTRL_9 0xAE
|
||||
|
||||
#define KSZ8463_REG_CFG_CTRL 0xD8
|
||||
|
||||
#define PORT_2_COPPER_MODE BIT(7)
|
||||
#define PORT_1_COPPER_MODE BIT(6)
|
||||
#define PORT_COPPER_MODE_S 6
|
||||
|
||||
#define KSZ8463_REG_SW_RESET 0x126
|
||||
|
||||
#define KSZ8463_GLOBAL_SOFTWARE_RESET BIT(0)
|
||||
|
||||
#define KSZ8463_PTP_CLK_CTRL 0x600
|
||||
|
||||
#define PTP_CLK_ENABLE BIT(1)
|
||||
|
||||
#define KSZ8463_PTP_MSG_CONF1 0x620
|
||||
|
||||
#define PTP_ENABLE BIT(6)
|
||||
|
||||
#define KSZ8463_REG_DSP_CTRL_6 0x734
|
||||
|
||||
#define COPPER_RECEIVE_ADJUSTMENT BIT(13)
|
||||
|
||||
/* Chip resource */
|
||||
|
||||
#define PRIO_QUEUES 4
|
||||
|
|
|
|||
|
|
@ -331,6 +331,38 @@ static const struct phylink_mac_ops ksz8_phylink_mac_ops = {
|
|||
.mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi,
|
||||
};
|
||||
|
||||
static const struct ksz_dev_ops ksz8463_dev_ops = {
|
||||
.setup = ksz8_setup,
|
||||
.get_port_addr = ksz8463_get_port_addr,
|
||||
.cfg_port_member = ksz8_cfg_port_member,
|
||||
.flush_dyn_mac_table = ksz8_flush_dyn_mac_table,
|
||||
.port_setup = ksz8_port_setup,
|
||||
.r_phy = ksz8463_r_phy,
|
||||
.w_phy = ksz8463_w_phy,
|
||||
.r_mib_cnt = ksz8_r_mib_cnt,
|
||||
.r_mib_pkt = ksz8_r_mib_pkt,
|
||||
.r_mib_stat64 = ksz88xx_r_mib_stats64,
|
||||
.freeze_mib = ksz8_freeze_mib,
|
||||
.port_init_cnt = ksz8_port_init_cnt,
|
||||
.fdb_dump = ksz8_fdb_dump,
|
||||
.fdb_add = ksz8_fdb_add,
|
||||
.fdb_del = ksz8_fdb_del,
|
||||
.mdb_add = ksz8_mdb_add,
|
||||
.mdb_del = ksz8_mdb_del,
|
||||
.vlan_filtering = ksz8_port_vlan_filtering,
|
||||
.vlan_add = ksz8_port_vlan_add,
|
||||
.vlan_del = ksz8_port_vlan_del,
|
||||
.mirror_add = ksz8_port_mirror_add,
|
||||
.mirror_del = ksz8_port_mirror_del,
|
||||
.get_caps = ksz8_get_caps,
|
||||
.config_cpu_port = ksz8_config_cpu_port,
|
||||
.enable_stp_addr = ksz8_enable_stp_addr,
|
||||
.reset = ksz8_reset_switch,
|
||||
.init = ksz8_switch_init,
|
||||
.exit = ksz8_switch_exit,
|
||||
.change_mtu = ksz8_change_mtu,
|
||||
};
|
||||
|
||||
static const struct ksz_dev_ops ksz88xx_dev_ops = {
|
||||
.setup = ksz8_setup,
|
||||
.get_port_addr = ksz8_get_port_addr,
|
||||
|
|
@ -517,6 +549,60 @@ static const struct ksz_dev_ops lan937x_dev_ops = {
|
|||
.exit = lan937x_switch_exit,
|
||||
};
|
||||
|
||||
static const u16 ksz8463_regs[] = {
|
||||
[REG_SW_MAC_ADDR] = 0x10,
|
||||
[REG_IND_CTRL_0] = 0x30,
|
||||
[REG_IND_DATA_8] = 0x26,
|
||||
[REG_IND_DATA_CHECK] = 0x26,
|
||||
[REG_IND_DATA_HI] = 0x28,
|
||||
[REG_IND_DATA_LO] = 0x2C,
|
||||
[REG_IND_MIB_CHECK] = 0x2F,
|
||||
[P_FORCE_CTRL] = 0x0C,
|
||||
[P_LINK_STATUS] = 0x0E,
|
||||
[P_LOCAL_CTRL] = 0x0C,
|
||||
[P_NEG_RESTART_CTRL] = 0x0D,
|
||||
[P_REMOTE_STATUS] = 0x0E,
|
||||
[P_SPEED_STATUS] = 0x0F,
|
||||
[S_TAIL_TAG_CTRL] = 0xAD,
|
||||
[P_STP_CTRL] = 0x6F,
|
||||
[S_START_CTRL] = 0x01,
|
||||
[S_BROADCAST_CTRL] = 0x06,
|
||||
[S_MULTICAST_CTRL] = 0x04,
|
||||
};
|
||||
|
||||
static const u32 ksz8463_masks[] = {
|
||||
[PORT_802_1P_REMAPPING] = BIT(3),
|
||||
[SW_TAIL_TAG_ENABLE] = BIT(0),
|
||||
[MIB_COUNTER_OVERFLOW] = BIT(7),
|
||||
[MIB_COUNTER_VALID] = BIT(6),
|
||||
[VLAN_TABLE_FID] = GENMASK(15, 12),
|
||||
[VLAN_TABLE_MEMBERSHIP] = GENMASK(18, 16),
|
||||
[VLAN_TABLE_VALID] = BIT(19),
|
||||
[STATIC_MAC_TABLE_VALID] = BIT(19),
|
||||
[STATIC_MAC_TABLE_USE_FID] = BIT(21),
|
||||
[STATIC_MAC_TABLE_FID] = GENMASK(25, 22),
|
||||
[STATIC_MAC_TABLE_OVERRIDE] = BIT(20),
|
||||
[STATIC_MAC_TABLE_FWD_PORTS] = GENMASK(18, 16),
|
||||
[DYNAMIC_MAC_TABLE_ENTRIES_H] = GENMASK(1, 0),
|
||||
[DYNAMIC_MAC_TABLE_MAC_EMPTY] = BIT(2),
|
||||
[DYNAMIC_MAC_TABLE_NOT_READY] = BIT(7),
|
||||
[DYNAMIC_MAC_TABLE_ENTRIES] = GENMASK(31, 24),
|
||||
[DYNAMIC_MAC_TABLE_FID] = GENMASK(19, 16),
|
||||
[DYNAMIC_MAC_TABLE_SRC_PORT] = GENMASK(21, 20),
|
||||
[DYNAMIC_MAC_TABLE_TIMESTAMP] = GENMASK(23, 22),
|
||||
};
|
||||
|
||||
static u8 ksz8463_shifts[] = {
|
||||
[VLAN_TABLE_MEMBERSHIP_S] = 16,
|
||||
[STATIC_MAC_FWD_PORTS] = 16,
|
||||
[STATIC_MAC_FID] = 22,
|
||||
[DYNAMIC_MAC_ENTRIES_H] = 8,
|
||||
[DYNAMIC_MAC_ENTRIES] = 24,
|
||||
[DYNAMIC_MAC_FID] = 16,
|
||||
[DYNAMIC_MAC_TIMESTAMP] = 22,
|
||||
[DYNAMIC_MAC_SRC_PORT] = 20,
|
||||
};
|
||||
|
||||
static const u16 ksz8795_regs[] = {
|
||||
[REG_SW_MAC_ADDR] = 0x68,
|
||||
[REG_IND_CTRL_0] = 0x6E,
|
||||
|
|
@ -1387,6 +1473,29 @@ static const struct regmap_access_table ksz8873_register_set = {
|
|||
};
|
||||
|
||||
const struct ksz_chip_data ksz_switch_chips[] = {
|
||||
[KSZ8463] = {
|
||||
.chip_id = KSZ8463_CHIP_ID,
|
||||
.dev_name = "KSZ8463",
|
||||
.num_vlans = 16,
|
||||
.num_alus = 0,
|
||||
.num_statics = 8,
|
||||
.cpu_ports = 0x4, /* can be configured as cpu port */
|
||||
.port_cnt = 3,
|
||||
.num_tx_queues = 4,
|
||||
.num_ipms = 4,
|
||||
.ops = &ksz8463_dev_ops,
|
||||
.phylink_mac_ops = &ksz88x3_phylink_mac_ops,
|
||||
.mib_names = ksz88xx_mib_names,
|
||||
.mib_cnt = ARRAY_SIZE(ksz88xx_mib_names),
|
||||
.reg_mib_cnt = MIB_COUNTER_NUM,
|
||||
.regs = ksz8463_regs,
|
||||
.masks = ksz8463_masks,
|
||||
.shifts = ksz8463_shifts,
|
||||
.supports_mii = {false, false, true},
|
||||
.supports_rmii = {false, false, true},
|
||||
.internal_phy = {true, true, false},
|
||||
},
|
||||
|
||||
[KSZ8563] = {
|
||||
.chip_id = KSZ8563_CHIP_ID,
|
||||
.dev_name = "KSZ8563",
|
||||
|
|
@ -2842,6 +2951,7 @@ static int ksz_parse_drive_strength(struct ksz_device *dev);
|
|||
static int ksz_setup(struct dsa_switch *ds)
|
||||
{
|
||||
struct ksz_device *dev = ds->priv;
|
||||
u16 storm_mask, storm_rate;
|
||||
struct dsa_port *dp;
|
||||
struct ksz_port *p;
|
||||
const u16 *regs;
|
||||
|
|
@ -2871,10 +2981,14 @@ static int ksz_setup(struct dsa_switch *ds)
|
|||
}
|
||||
|
||||
/* set broadcast storm protection 10% rate */
|
||||
storm_mask = BROADCAST_STORM_RATE;
|
||||
storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;
|
||||
if (ksz_is_ksz8463(dev)) {
|
||||
storm_mask = swab16(storm_mask);
|
||||
storm_rate = swab16(storm_rate);
|
||||
}
|
||||
regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL],
|
||||
BROADCAST_STORM_RATE,
|
||||
(BROADCAST_STORM_VALUE *
|
||||
BROADCAST_STORM_PROT_RATE) / 100);
|
||||
storm_mask, storm_rate);
|
||||
|
||||
dev->dev_ops->config_cpu_port(ds);
|
||||
|
||||
|
|
@ -3400,6 +3514,7 @@ static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds,
|
|||
proto = DSA_TAG_PROTO_KSZ8795;
|
||||
|
||||
if (dev->chip_id == KSZ88X3_CHIP_ID ||
|
||||
dev->chip_id == KSZ8463_CHIP_ID ||
|
||||
dev->chip_id == KSZ8563_CHIP_ID ||
|
||||
dev->chip_id == KSZ9893_CHIP_ID ||
|
||||
dev->chip_id == KSZ9563_CHIP_ID)
|
||||
|
|
@ -3512,6 +3627,7 @@ static int ksz_max_mtu(struct dsa_switch *ds, int port)
|
|||
case KSZ8794_CHIP_ID:
|
||||
case KSZ8765_CHIP_ID:
|
||||
return KSZ8795_HUGE_PACKET_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN;
|
||||
case KSZ8463_CHIP_ID:
|
||||
case KSZ88X3_CHIP_ID:
|
||||
case KSZ8864_CHIP_ID:
|
||||
case KSZ8895_CHIP_ID:
|
||||
|
|
@ -3866,6 +3982,9 @@ static int ksz_switch_detect(struct ksz_device *dev)
|
|||
id2 = FIELD_GET(SW_CHIP_ID_M, id16);
|
||||
|
||||
switch (id1) {
|
||||
case KSZ84_FAMILY_ID:
|
||||
dev->chip_id = KSZ8463_CHIP_ID;
|
||||
break;
|
||||
case KSZ87_FAMILY_ID:
|
||||
if (id2 == KSZ87_CHIP_ID_95) {
|
||||
u8 val;
|
||||
|
|
@ -4107,6 +4226,17 @@ static int ksz_ets_band_to_queue(struct tc_ets_qopt_offload_replace_params *p,
|
|||
return p->bands - 1 - band;
|
||||
}
|
||||
|
||||
static u8 ksz8463_tc_ctrl(int port, int queue)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
reg = 0xC8 + port * 4;
|
||||
reg += ((3 - queue) / 2) * 2;
|
||||
reg++;
|
||||
reg -= (queue & 1);
|
||||
return reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksz88x3_tc_ets_add - Configure ETS (Enhanced Transmission Selection)
|
||||
* for a port on KSZ88x3 switch
|
||||
|
|
@ -4142,6 +4272,8 @@ static int ksz88x3_tc_ets_add(struct ksz_device *dev, int port,
|
|||
* port/queue
|
||||
*/
|
||||
reg = KSZ8873_TXQ_SPLIT_CTRL_REG(port, queue);
|
||||
if (ksz_is_ksz8463(dev))
|
||||
reg = ksz8463_tc_ctrl(port, queue);
|
||||
|
||||
/* Clear WFQ enable bit to select strict priority scheduling */
|
||||
ret = ksz_rmw8(dev, reg, KSZ8873_TXQ_WFQ_ENABLE, 0);
|
||||
|
|
@ -4177,6 +4309,8 @@ static int ksz88x3_tc_ets_del(struct ksz_device *dev, int port)
|
|||
* port/queue
|
||||
*/
|
||||
reg = KSZ8873_TXQ_SPLIT_CTRL_REG(port, queue);
|
||||
if (ksz_is_ksz8463(dev))
|
||||
reg = ksz8463_tc_ctrl(port, queue);
|
||||
|
||||
/* Set WFQ enable bit to revert back to default scheduling
|
||||
* mode
|
||||
|
|
@ -4324,7 +4458,7 @@ static int ksz_tc_setup_qdisc_ets(struct dsa_switch *ds, int port,
|
|||
struct ksz_device *dev = ds->priv;
|
||||
int ret;
|
||||
|
||||
if (is_ksz8(dev) && !ksz_is_ksz88x3(dev))
|
||||
if (is_ksz8(dev) && !(ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (qopt->parent != TC_H_ROOT) {
|
||||
|
|
@ -4338,13 +4472,13 @@ static int ksz_tc_setup_qdisc_ets(struct dsa_switch *ds, int port,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ksz_is_ksz88x3(dev))
|
||||
if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev))
|
||||
return ksz88x3_tc_ets_add(dev, port,
|
||||
&qopt->replace_params);
|
||||
else
|
||||
return ksz_tc_ets_add(dev, port, &qopt->replace_params);
|
||||
case TC_ETS_DESTROY:
|
||||
if (ksz_is_ksz88x3(dev))
|
||||
if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev))
|
||||
return ksz88x3_tc_ets_del(dev, port);
|
||||
else
|
||||
return ksz_tc_ets_del(dev, port);
|
||||
|
|
@ -4687,7 +4821,16 @@ int ksz_switch_macaddr_get(struct dsa_switch *ds, int port,
|
|||
|
||||
/* Program the switch MAC address to hardware */
|
||||
for (i = 0; i < ETH_ALEN; i++) {
|
||||
ret = ksz_write8(dev, regs[REG_SW_MAC_ADDR] + i, addr[i]);
|
||||
if (ksz_is_ksz8463(dev)) {
|
||||
u16 addr16 = ((u16)addr[i] << 8) | addr[i + 1];
|
||||
|
||||
ret = ksz_write16(dev, regs[REG_SW_MAC_ADDR] + i,
|
||||
addr16);
|
||||
i++;
|
||||
} else {
|
||||
ret = ksz_write8(dev, regs[REG_SW_MAC_ADDR] + i,
|
||||
addr[i]);
|
||||
}
|
||||
if (ret)
|
||||
goto macaddr_drop;
|
||||
}
|
||||
|
|
@ -5296,6 +5439,9 @@ int ksz_switch_register(struct ksz_device *dev)
|
|||
&dev->ports[port_num].interface);
|
||||
|
||||
ksz_parse_rgmii_delay(dev, port_num, port);
|
||||
dev->ports[port_num].fiber =
|
||||
of_property_read_bool(port,
|
||||
"micrel,fiber-mode");
|
||||
}
|
||||
of_node_put(ports);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ struct ksz_device {
|
|||
|
||||
/* List of supported models */
|
||||
enum ksz_model {
|
||||
KSZ8463,
|
||||
KSZ8563,
|
||||
KSZ8567,
|
||||
KSZ8795,
|
||||
|
|
@ -484,6 +485,11 @@ static inline struct regmap *ksz_regmap_32(struct ksz_device *dev)
|
|||
return dev->regmap[KSZ_REGMAP_32];
|
||||
}
|
||||
|
||||
static inline bool ksz_is_ksz8463(struct ksz_device *dev)
|
||||
{
|
||||
return dev->chip_id == KSZ8463_CHIP_ID;
|
||||
}
|
||||
|
||||
static inline int ksz_read8(struct ksz_device *dev, u32 reg, u8 *val)
|
||||
{
|
||||
unsigned int value;
|
||||
|
|
@ -709,12 +715,13 @@ static inline bool ksz_is_8895_family(struct ksz_device *dev)
|
|||
static inline bool is_ksz8(struct ksz_device *dev)
|
||||
{
|
||||
return ksz_is_ksz87xx(dev) || ksz_is_ksz88x3(dev) ||
|
||||
ksz_is_8895_family(dev);
|
||||
ksz_is_8895_family(dev) || ksz_is_ksz8463(dev);
|
||||
}
|
||||
|
||||
static inline bool is_ksz88xx(struct ksz_device *dev)
|
||||
{
|
||||
return ksz_is_ksz88x3(dev) || ksz_is_8895_family(dev);
|
||||
return ksz_is_ksz88x3(dev) || ksz_is_8895_family(dev) ||
|
||||
ksz_is_ksz8463(dev);
|
||||
}
|
||||
|
||||
static inline bool is_ksz9477(struct ksz_device *dev)
|
||||
|
|
@ -761,6 +768,7 @@ static inline bool ksz_is_sgmii_port(struct ksz_device *dev, int port)
|
|||
#define REG_CHIP_ID0 0x00
|
||||
|
||||
#define SW_FAMILY_ID_M GENMASK(15, 8)
|
||||
#define KSZ84_FAMILY_ID 0x84
|
||||
#define KSZ87_FAMILY_ID 0x87
|
||||
#define KSZ88_FAMILY_ID 0x88
|
||||
#define KSZ8895_FAMILY_ID 0x95
|
||||
|
|
@ -939,4 +947,29 @@ static inline bool ksz_is_sgmii_port(struct ksz_device *dev, int port)
|
|||
[KSZ_REGMAP_32] = KSZ_REGMAP_ENTRY(32, swp, (regbits), (regpad), (regalign)), \
|
||||
}
|
||||
|
||||
#define KSZ8463_REGMAP_ENTRY(width, regbits, regpad, regalign) \
|
||||
{ \
|
||||
.name = #width, \
|
||||
.val_bits = (width), \
|
||||
.reg_stride = (width / 8), \
|
||||
.reg_bits = (regbits) + (regalign), \
|
||||
.pad_bits = (regpad), \
|
||||
.read = ksz8463_spi_read, \
|
||||
.write = ksz8463_spi_write, \
|
||||
.max_register = BIT(regbits) - 1, \
|
||||
.cache_type = REGCACHE_NONE, \
|
||||
.zero_flag_mask = 1, \
|
||||
.use_single_read = 1, \
|
||||
.use_single_write = 1, \
|
||||
.lock = ksz_regmap_lock, \
|
||||
.unlock = ksz_regmap_unlock, \
|
||||
}
|
||||
|
||||
#define KSZ8463_REGMAP_TABLE(ksz, regbits, regpad, regalign) \
|
||||
static const struct regmap_config ksz##_regmap_config[] = { \
|
||||
[KSZ_REGMAP_8] = KSZ8463_REGMAP_ENTRY(8, (regbits), (regpad), (regalign)), \
|
||||
[KSZ_REGMAP_16] = KSZ8463_REGMAP_ENTRY(16, (regbits), (regpad), (regalign)), \
|
||||
[KSZ_REGMAP_32] = KSZ8463_REGMAP_ENTRY(32, (regbits), (regpad), (regalign)), \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -16,10 +16,12 @@
|
|||
* Therefore, we define the base offset as 0x00 here to align with that logic.
|
||||
*/
|
||||
#define KSZ8_REG_PORT_1_CTRL_0 0x00
|
||||
#define KSZ8463_REG_PORT_1_CTRL_0 0x6C
|
||||
#define KSZ8_PORT_DIFFSERV_ENABLE BIT(6)
|
||||
#define KSZ8_PORT_802_1P_ENABLE BIT(5)
|
||||
#define KSZ8_PORT_BASED_PRIO_M GENMASK(4, 3)
|
||||
|
||||
#define KSZ8463_REG_TOS_DSCP_CTRL 0x16
|
||||
#define KSZ88X3_REG_TOS_DSCP_CTRL 0x60
|
||||
#define KSZ8765_REG_TOS_DSCP_CTRL 0x90
|
||||
|
||||
|
|
@ -98,6 +100,8 @@ static void ksz_get_default_port_prio_reg(struct ksz_device *dev, int *reg,
|
|||
*reg = KSZ8_REG_PORT_1_CTRL_0;
|
||||
*mask = KSZ8_PORT_BASED_PRIO_M;
|
||||
*shift = __bf_shf(KSZ8_PORT_BASED_PRIO_M);
|
||||
if (ksz_is_ksz8463(dev))
|
||||
*reg = KSZ8463_REG_PORT_1_CTRL_0;
|
||||
} else {
|
||||
*reg = KSZ9477_REG_PORT_MRI_MAC_CTRL;
|
||||
*mask = KSZ9477_PORT_BASED_PRIO_M;
|
||||
|
|
@ -122,10 +126,12 @@ static void ksz_get_dscp_prio_reg(struct ksz_device *dev, int *reg,
|
|||
*reg = KSZ8765_REG_TOS_DSCP_CTRL;
|
||||
*per_reg = 4;
|
||||
*mask = GENMASK(1, 0);
|
||||
} else if (ksz_is_ksz88x3(dev)) {
|
||||
} else if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)) {
|
||||
*reg = KSZ88X3_REG_TOS_DSCP_CTRL;
|
||||
*per_reg = 4;
|
||||
*mask = GENMASK(1, 0);
|
||||
if (ksz_is_ksz8463(dev))
|
||||
*reg = KSZ8463_REG_TOS_DSCP_CTRL;
|
||||
} else {
|
||||
*reg = KSZ9477_REG_DIFFSERV_PRIO_MAP;
|
||||
*per_reg = 2;
|
||||
|
|
@ -151,6 +157,8 @@ static void ksz_get_apptrust_map_and_reg(struct ksz_device *dev,
|
|||
*map = ksz8_apptrust_map_to_bit;
|
||||
*reg = KSZ8_REG_PORT_1_CTRL_0;
|
||||
*mask = KSZ8_PORT_DIFFSERV_ENABLE | KSZ8_PORT_802_1P_ENABLE;
|
||||
if (ksz_is_ksz8463(dev))
|
||||
*reg = KSZ8463_REG_PORT_1_CTRL_0;
|
||||
} else {
|
||||
*map = ksz9477_apptrust_map_to_bit;
|
||||
*reg = KSZ9477_REG_PORT_MRI_PRIO_CTRL;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
#include "ksz_common.h"
|
||||
|
||||
#define KSZ8463_SPI_ADDR_SHIFT 13
|
||||
#define KSZ8463_SPI_ADDR_ALIGN 3
|
||||
#define KSZ8463_SPI_TURNAROUND_SHIFT 2
|
||||
|
||||
#define KSZ8795_SPI_ADDR_SHIFT 12
|
||||
#define KSZ8795_SPI_ADDR_ALIGN 3
|
||||
#define KSZ8795_SPI_TURNAROUND_SHIFT 1
|
||||
|
|
@ -37,6 +41,99 @@ KSZ_REGMAP_TABLE(ksz8863, 16, KSZ8863_SPI_ADDR_SHIFT,
|
|||
KSZ_REGMAP_TABLE(ksz9477, 32, KSZ9477_SPI_ADDR_SHIFT,
|
||||
KSZ9477_SPI_TURNAROUND_SHIFT, KSZ9477_SPI_ADDR_ALIGN);
|
||||
|
||||
static u16 ksz8463_reg(u16 reg, size_t size)
|
||||
{
|
||||
switch (size) {
|
||||
case 1:
|
||||
reg = ((reg >> 2) << 4) | (1 << (reg & 3));
|
||||
break;
|
||||
case 2:
|
||||
reg = ((reg >> 2) << 4) | (reg & 2 ? 0x0c : 0x03);
|
||||
break;
|
||||
default:
|
||||
reg = ((reg >> 2) << 4) | 0xf;
|
||||
break;
|
||||
}
|
||||
reg <<= KSZ8463_SPI_TURNAROUND_SHIFT;
|
||||
return reg;
|
||||
}
|
||||
|
||||
static int ksz8463_spi_read(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
void *val, size_t val_size)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
u8 bytes[2];
|
||||
u16 cmd;
|
||||
int rc;
|
||||
|
||||
if (reg_size > 2 || val_size > 4)
|
||||
return -EINVAL;
|
||||
memcpy(&cmd, reg, sizeof(u16));
|
||||
cmd = ksz8463_reg(cmd, val_size);
|
||||
/* SPI command uses big-endian format. */
|
||||
put_unaligned_be16(cmd, bytes);
|
||||
rc = spi_write_then_read(spi, bytes, reg_size, val, val_size);
|
||||
#if defined(__BIG_ENDIAN)
|
||||
/* Register value uses little-endian format so need to convert when
|
||||
* running in big-endian system.
|
||||
*/
|
||||
if (!rc && val_size > 1) {
|
||||
if (val_size == 2) {
|
||||
u16 v = get_unaligned_le16(val);
|
||||
|
||||
memcpy(val, &v, sizeof(v));
|
||||
} else if (val_size == 4) {
|
||||
u32 v = get_unaligned_le32(val);
|
||||
|
||||
memcpy(val, &v, sizeof(v));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ksz8463_spi_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
size_t val_size = count - 2;
|
||||
u8 bytes[6];
|
||||
u16 cmd;
|
||||
|
||||
if (count <= 2 || count > 6)
|
||||
return -EINVAL;
|
||||
memcpy(bytes, data, count);
|
||||
memcpy(&cmd, data, sizeof(u16));
|
||||
cmd = ksz8463_reg(cmd, val_size);
|
||||
cmd |= (1 << (KSZ8463_SPI_ADDR_SHIFT + KSZ8463_SPI_TURNAROUND_SHIFT));
|
||||
/* SPI command uses big-endian format. */
|
||||
put_unaligned_be16(cmd, bytes);
|
||||
#if defined(__BIG_ENDIAN)
|
||||
/* Register value uses little-endian format so need to convert when
|
||||
* running in big-endian system.
|
||||
*/
|
||||
if (val_size == 2) {
|
||||
u8 *val = &bytes[2];
|
||||
u16 v;
|
||||
|
||||
memcpy(&v, val, sizeof(v));
|
||||
put_unaligned_le16(v, val);
|
||||
} else if (val_size == 4) {
|
||||
u8 *val = &bytes[2];
|
||||
u32 v;
|
||||
|
||||
memcpy(&v, val, sizeof(v));
|
||||
put_unaligned_le32(v, val);
|
||||
}
|
||||
#endif
|
||||
return spi_write(spi, bytes, count);
|
||||
}
|
||||
|
||||
KSZ8463_REGMAP_TABLE(ksz8463, KSZ8463_SPI_ADDR_SHIFT, 0,
|
||||
KSZ8463_SPI_ADDR_ALIGN);
|
||||
|
||||
static int ksz_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct regmap_config *regmap_config;
|
||||
|
|
@ -58,6 +155,8 @@ static int ksz_spi_probe(struct spi_device *spi)
|
|||
dev->chip_id = chip->chip_id;
|
||||
if (chip->chip_id == KSZ88X3_CHIP_ID)
|
||||
regmap_config = ksz8863_regmap_config;
|
||||
else if (chip->chip_id == KSZ8463_CHIP_ID)
|
||||
regmap_config = ksz8463_regmap_config;
|
||||
else if (chip->chip_id == KSZ8795_CHIP_ID ||
|
||||
chip->chip_id == KSZ8794_CHIP_ID ||
|
||||
chip->chip_id == KSZ8765_CHIP_ID)
|
||||
|
|
@ -125,6 +224,10 @@ static void ksz_spi_shutdown(struct spi_device *spi)
|
|||
}
|
||||
|
||||
static const struct of_device_id ksz_dt_ids[] = {
|
||||
{
|
||||
.compatible = "microchip,ksz8463",
|
||||
.data = &ksz_switch_chips[KSZ8463]
|
||||
},
|
||||
{
|
||||
.compatible = "microchip,ksz8765",
|
||||
.data = &ksz_switch_chips[KSZ8765]
|
||||
|
|
@ -214,6 +317,7 @@ static const struct of_device_id ksz_dt_ids[] = {
|
|||
MODULE_DEVICE_TABLE(of, ksz_dt_ids);
|
||||
|
||||
static const struct spi_device_id ksz_spi_ids[] = {
|
||||
{ "ksz8463" },
|
||||
{ "ksz8765" },
|
||||
{ "ksz8794" },
|
||||
{ "ksz8795" },
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/platform_data/dsa.h>
|
||||
|
||||
enum ksz_chip_id {
|
||||
KSZ8463_CHIP_ID = 0x8463,
|
||||
KSZ8563_CHIP_ID = 0x8563,
|
||||
KSZ8795_CHIP_ID = 0x8795,
|
||||
KSZ8794_CHIP_ID = 0x8794,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user