X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fstaging%2Fchunkeey.git;a=blobdiff_plain;f=target%2Flinux%2Fgeneric%2Ffiles%2Fdrivers%2Fnet%2Fphy%2Frtl8366_smi.c;h=c21ad9425de596908aeea750c4b97bf0a38baf07;hp=ac8862ced6fc1d27894669b2b01f2d1fac06c032;hb=d8c7cd9cb15a51cbbf5dba9aab63da3addee9d87;hpb=b093cec6cca842bd2f6bf1b6f904738b273e8682 diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c index ac8862ced6..c21ad9425d 100644 --- a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c +++ b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c @@ -15,19 +15,27 @@ #include #include #include - -#ifdef CONFIG_RTL8366S_PHY_DEBUG_FS +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_RTL8366_SMI_DEBUG_FS #include #endif #include "rtl8366_smi.h" #define RTL8366_SMI_ACK_RETRY_COUNT 5 -#define RTL8366_SMI_CLK_DELAY 10 /* nsec */ + +#define RTL8366_SMI_HW_STOP_DELAY 25 /* msecs */ +#define RTL8366_SMI_HW_START_DELAY 100 /* msecs */ static inline void rtl8366_smi_clk_delay(struct rtl8366_smi *smi) { - ndelay(RTL8366_SMI_CLK_DELAY); + ndelay(smi->clk_delay); } static void rtl8366_smi_start(struct rtl8366_smi *smi) @@ -142,8 +150,10 @@ static int rtl8366_smi_wait_for_ack(struct rtl8366_smi *smi) if (ack == 0) break; - if (++retry_cnt > RTL8366_SMI_ACK_RETRY_COUNT) - return -EIO; + if (++retry_cnt > RTL8366_SMI_ACK_RETRY_COUNT) { + dev_err(smi->parent, "ACK timeout\n"); + return -ETIMEDOUT; + } } while (1); return 0; @@ -155,6 +165,12 @@ static int rtl8366_smi_write_byte(struct rtl8366_smi *smi, u8 data) return rtl8366_smi_wait_for_ack(smi); } +static int rtl8366_smi_write_byte_noack(struct rtl8366_smi *smi, u8 data) +{ + rtl8366_smi_write_bits(smi, data, 8); + return 0; +} + static int rtl8366_smi_read_byte0(struct rtl8366_smi *smi, u8 *data) { u32 t; @@ -195,7 +211,7 @@ int rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data) rtl8366_smi_start(smi); /* send READ command */ - ret = rtl8366_smi_write_byte(smi, 0x0a << 4 | 0x04 << 1 | 0x01); + ret = rtl8366_smi_write_byte(smi, smi->cmd_read); if (ret) goto out; @@ -226,7 +242,8 @@ int rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data) } EXPORT_SYMBOL_GPL(rtl8366_smi_read_reg); -int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data) +static int __rtl8366_smi_write_reg(struct rtl8366_smi *smi, + u32 addr, u32 data, bool ack) { unsigned long flags; int ret; @@ -236,7 +253,7 @@ int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data) rtl8366_smi_start(smi); /* send WRITE command */ - ret = rtl8366_smi_write_byte(smi, 0x0a << 4 | 0x04 << 1 | 0x00); + ret = rtl8366_smi_write_byte(smi, smi->cmd_write); if (ret) goto out; @@ -256,7 +273,10 @@ int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data) goto out; /* write DATA[15:8] */ - ret = rtl8366_smi_write_byte(smi, data >> 8); + if (ack) + ret = rtl8366_smi_write_byte(smi, data >> 8); + else + ret = rtl8366_smi_write_byte_noack(smi, data >> 8); if (ret) goto out; @@ -268,8 +288,19 @@ int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data) return ret; } + +int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data) +{ + return __rtl8366_smi_write_reg(smi, addr, data, true); +} EXPORT_SYMBOL_GPL(rtl8366_smi_write_reg); +int rtl8366_smi_write_reg_noack(struct rtl8366_smi *smi, u32 addr, u32 data) +{ + return __rtl8366_smi_write_reg(smi, addr, data, false); +} +EXPORT_SYMBOL_GPL(rtl8366_smi_write_reg_noack); + int rtl8366_smi_rmwr(struct rtl8366_smi *smi, u32 addr, u32 mask, u32 data) { u32 t; @@ -285,6 +316,19 @@ int rtl8366_smi_rmwr(struct rtl8366_smi *smi, u32 addr, u32 mask, u32 data) } EXPORT_SYMBOL_GPL(rtl8366_smi_rmwr); +static int rtl8366_reset(struct rtl8366_smi *smi) +{ + if (smi->hw_reset) { + smi->hw_reset(true); + msleep(RTL8366_SMI_HW_STOP_DELAY); + smi->hw_reset(false); + msleep(RTL8366_SMI_HW_START_DELAY); + return 0; + } + + return smi->ops->reset_chip(smi); +} + static int rtl8366_mc_is_used(struct rtl8366_smi *smi, int mc_index, int *used) { int err; @@ -307,8 +351,8 @@ static int rtl8366_mc_is_used(struct rtl8366_smi *smi, int mc_index, int *used) return 0; } -int rtl8366_set_vlan(struct rtl8366_smi *smi, int vid, u32 member, u32 untag, - u32 fid) +static int rtl8366_set_vlan(struct rtl8366_smi *smi, int vid, u32 member, + u32 untag, u32 fid) { struct rtl8366_vlan_4k vlan4k; int err; @@ -347,47 +391,8 @@ int rtl8366_set_vlan(struct rtl8366_smi *smi, int vid, u32 member, u32 untag, return err; } -EXPORT_SYMBOL_GPL(rtl8366_set_vlan); -int rtl8366_reset_vlan(struct rtl8366_smi *smi) -{ - struct rtl8366_vlan_mc vlanmc; - int err; - int i; - - /* clear VLAN member configurations */ - vlanmc.vid = 0; - vlanmc.priority = 0; - vlanmc.member = 0; - vlanmc.untag = 0; - vlanmc.fid = 0; - for (i = 0; i < smi->num_vlan_mc; i++) { - err = smi->ops->set_vlan_mc(smi, i, &vlanmc); - if (err) - return err; - } - - for (i = 0; i < smi->num_ports; i++) { - if (i == smi->cpu_port) - continue; - - err = rtl8366_set_vlan(smi, (i + 1), - (1 << i) | (1 << smi->cpu_port), - (1 << i) | (1 << smi->cpu_port), - 0); - if (err) - return err; - - err = rtl8366_set_pvid(smi, i, (i + 1)); - if (err) - return err; - } - - return 0; -} -EXPORT_SYMBOL_GPL(rtl8366_reset_vlan); - -int rtl8366_get_pvid(struct rtl8366_smi *smi, int port, int *val) +static int rtl8366_get_pvid(struct rtl8366_smi *smi, int port, int *val) { struct rtl8366_vlan_mc vlanmc; int err; @@ -404,9 +409,9 @@ int rtl8366_get_pvid(struct rtl8366_smi *smi, int port, int *val) *val = vlanmc.vid; return 0; } -EXPORT_SYMBOL_GPL(rtl8366_get_pvid); -int rtl8366_set_pvid(struct rtl8366_smi *smi, unsigned port, unsigned vid) +static int rtl8366_set_pvid(struct rtl8366_smi *smi, unsigned port, + unsigned vid) { struct rtl8366_vlan_mc vlanmc; struct rtl8366_vlan_4k vlan4k; @@ -486,9 +491,116 @@ int rtl8366_set_pvid(struct rtl8366_smi *smi, unsigned port, unsigned vid) return -ENOSPC; } -EXPORT_SYMBOL_GPL(rtl8366_set_pvid); -#ifdef CONFIG_RTL8366S_PHY_DEBUG_FS +int rtl8366_enable_vlan(struct rtl8366_smi *smi, int enable) +{ + int err; + + err = smi->ops->enable_vlan(smi, enable); + if (err) + return err; + + smi->vlan_enabled = enable; + + if (!enable) { + smi->vlan4k_enabled = 0; + err = smi->ops->enable_vlan4k(smi, enable); + } + + return err; +} +EXPORT_SYMBOL_GPL(rtl8366_enable_vlan); + +static int rtl8366_enable_vlan4k(struct rtl8366_smi *smi, int enable) +{ + int err; + + if (enable) { + err = smi->ops->enable_vlan(smi, enable); + if (err) + return err; + + smi->vlan_enabled = enable; + } + + err = smi->ops->enable_vlan4k(smi, enable); + if (err) + return err; + + smi->vlan4k_enabled = enable; + return 0; +} + +int rtl8366_enable_all_ports(struct rtl8366_smi *smi, int enable) +{ + int port; + int err; + + for (port = 0; port < smi->num_ports; port++) { + err = smi->ops->enable_port(smi, port, enable); + if (err) + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(rtl8366_enable_all_ports); + +int rtl8366_reset_vlan(struct rtl8366_smi *smi) +{ + struct rtl8366_vlan_mc vlanmc; + int err; + int i; + + rtl8366_enable_vlan(smi, 0); + rtl8366_enable_vlan4k(smi, 0); + + /* clear VLAN member configurations */ + vlanmc.vid = 0; + vlanmc.priority = 0; + vlanmc.member = 0; + vlanmc.untag = 0; + vlanmc.fid = 0; + for (i = 0; i < smi->num_vlan_mc; i++) { + err = smi->ops->set_vlan_mc(smi, i, &vlanmc); + if (err) + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(rtl8366_reset_vlan); + +static int rtl8366_init_vlan(struct rtl8366_smi *smi) +{ + int port; + int err; + + err = rtl8366_reset_vlan(smi); + if (err) + return err; + + for (port = 0; port < smi->num_ports; port++) { + u32 mask; + + if (port == smi->cpu_port) + mask = (1 << smi->num_ports) - 1; + else + mask = (1 << port) | (1 << smi->cpu_port); + + err = rtl8366_set_vlan(smi, (port + 1), mask, mask, 0); + if (err) + return err; + + err = rtl8366_set_pvid(smi, port, (port + 1)); + if (err) + return err; + } + + return rtl8366_enable_vlan(smi, 1); +} + +#ifdef CONFIG_RTL8366_SMI_DEBUG_FS int rtl8366_debugfs_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; @@ -522,6 +634,71 @@ static ssize_t rtl8366_read_debugfs_vlan_mc(struct file *file, return simple_read_from_buffer(user_buf, count, ppos, buf, len); } +#define RTL8366_VLAN4K_PAGE_SIZE 64 +#define RTL8366_VLAN4K_NUM_PAGES (4096 / RTL8366_VLAN4K_PAGE_SIZE) + +static ssize_t rtl8366_read_debugfs_vlan_4k(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data; + int i, len = 0; + int offset; + char *buf = smi->buf; + + if (smi->dbg_vlan_4k_page >= RTL8366_VLAN4K_NUM_PAGES) { + len += snprintf(buf + len, sizeof(smi->buf) - len, + "invalid page: %u\n", smi->dbg_vlan_4k_page); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); + } + + len += snprintf(buf + len, sizeof(smi->buf) - len, + "%4s %6s %6s %3s\n", + "vid", "member", "untag", "fid"); + + offset = RTL8366_VLAN4K_PAGE_SIZE * smi->dbg_vlan_4k_page; + for (i = 0; i < RTL8366_VLAN4K_PAGE_SIZE; i++) { + struct rtl8366_vlan_4k vlan4k; + + smi->ops->get_vlan_4k(smi, offset + i, &vlan4k); + + len += snprintf(buf + len, sizeof(smi->buf) - len, + "%4d 0x%04x 0x%04x %3d\n", + vlan4k.vid, vlan4k.member, + vlan4k.untag, vlan4k.fid); + } + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t rtl8366_read_debugfs_pvid(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data; + char *buf = smi->buf; + int len = 0; + int i; + + len += snprintf(buf + len, sizeof(smi->buf) - len, "%4s %4s\n", + "port", "pvid"); + + for (i = 0; i < smi->num_ports; i++) { + int pvid; + int err; + + err = rtl8366_get_pvid(smi, i, &pvid); + if (err) + len += snprintf(buf + len, sizeof(smi->buf) - len, + "%4d error\n", i); + else + len += snprintf(buf + len, sizeof(smi->buf) - len, + "%4d %4d\n", i, pvid); + } + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + static ssize_t rtl8366_read_debugfs_reg(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -568,7 +745,7 @@ static ssize_t rtl8366_write_debugfs_reg(struct file *file, buf[len - 1] = '\0'; - if (strict_strtoul(buf, 16, &data)) { + if (kstrtoul(buf, 16, &data)) { dev_err(smi->parent, "Invalid reg value %s\n", buf); } else { err = rtl8366_smi_write_reg(smi, reg, data); @@ -636,6 +813,18 @@ static const struct file_operations fops_rtl8366_vlan_mc = { .owner = THIS_MODULE }; +static const struct file_operations fops_rtl8366_vlan_4k = { + .read = rtl8366_read_debugfs_vlan_4k, + .open = rtl8366_debugfs_open, + .owner = THIS_MODULE +}; + +static const struct file_operations fops_rtl8366_pvid = { + .read = rtl8366_read_debugfs_pvid, + .open = rtl8366_debugfs_open, + .owner = THIS_MODULE +}; + static const struct file_operations fops_rtl8366_mibs = { .read = rtl8366_read_debugfs_mibs, .open = rtl8366_debugfs_open, @@ -681,6 +870,30 @@ static void rtl8366_debugfs_init(struct rtl8366_smi *smi) return; } + node = debugfs_create_u8("vlan_4k_page", S_IRUGO | S_IWUSR, root, + &smi->dbg_vlan_4k_page); + if (!node) { + dev_err(smi->parent, "Creating debugfs file '%s' failed\n", + "vlan_4k_page"); + return; + } + + node = debugfs_create_file("vlan_4k", S_IRUSR, root, smi, + &fops_rtl8366_vlan_4k); + if (!node) { + dev_err(smi->parent, "Creating debugfs file '%s' failed\n", + "vlan_4k"); + return; + } + + node = debugfs_create_file("pvid", S_IRUSR, root, smi, + &fops_rtl8366_pvid); + if (!node) { + dev_err(smi->parent, "Creating debugfs file '%s' failed\n", + "pvid"); + return; + } + node = debugfs_create_file("mibs", S_IRUSR, smi->debugfs_root, smi, &fops_rtl8366_mibs); if (!node) @@ -698,12 +911,17 @@ static void rtl8366_debugfs_remove(struct rtl8366_smi *smi) #else static inline void rtl8366_debugfs_init(struct rtl8366_smi *smi) {} static inline void rtl8366_debugfs_remove(struct rtl8366_smi *smi) {} -#endif /* CONFIG_RTL8366S_PHY_DEBUG_FS */ +#endif /* CONFIG_RTL8366_SMI_DEBUG_FS */ static int rtl8366_smi_mii_init(struct rtl8366_smi *smi) { int ret; - int i; + +#ifdef CONFIG_OF + struct device_node *np = NULL; + + np = of_get_child_by_name(smi->parent->of_node, "mdio-bus"); +#endif smi->mii_bus = mdiobus_alloc(); if (smi->mii_bus == NULL) { @@ -719,11 +937,22 @@ static int rtl8366_smi_mii_init(struct rtl8366_smi *smi) dev_name(smi->parent)); smi->mii_bus->parent = smi->parent; smi->mii_bus->phy_mask = ~(0x1f); - smi->mii_bus->irq = smi->mii_irq; - for (i = 0; i < PHY_MAX_ADDR; i++) - smi->mii_irq[i] = PHY_POLL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0) + { + int i; + smi->mii_bus->irq = smi->mii_irq; + for (i = 0; i < PHY_MAX_ADDR; i++) + smi->mii_irq[i] = PHY_POLL; + } +#endif + +#ifdef CONFIG_OF + if (np) + ret = of_mdiobus_register(smi->mii_bus, np); + else +#endif + ret = mdiobus_register(smi->mii_bus); - ret = mdiobus_register(smi->mii_bus); if (ret) goto err_free; @@ -741,6 +970,297 @@ static void rtl8366_smi_mii_cleanup(struct rtl8366_smi *smi) mdiobus_free(smi->mii_bus); } +int rtl8366_sw_reset_switch(struct switch_dev *dev) +{ + struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); + int err; + + err = rtl8366_reset(smi); + if (err) + return err; + + err = smi->ops->setup(smi); + if (err) + return err; + + err = rtl8366_reset_vlan(smi); + if (err) + return err; + + err = rtl8366_enable_vlan(smi, 1); + if (err) + return err; + + return rtl8366_enable_all_ports(smi, 1); +} +EXPORT_SYMBOL_GPL(rtl8366_sw_reset_switch); + +int rtl8366_sw_get_port_pvid(struct switch_dev *dev, int port, int *val) +{ + struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); + return rtl8366_get_pvid(smi, port, val); +} +EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_pvid); + +int rtl8366_sw_set_port_pvid(struct switch_dev *dev, int port, int val) +{ + struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); + return rtl8366_set_pvid(smi, port, val); +} +EXPORT_SYMBOL_GPL(rtl8366_sw_set_port_pvid); + +int rtl8366_sw_get_port_mib(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); + int i, len = 0; + unsigned long long counter = 0; + char *buf = smi->buf; + + if (val->port_vlan >= smi->num_ports) + return -EINVAL; + + len += snprintf(buf + len, sizeof(smi->buf) - len, + "Port %d MIB counters\n", + val->port_vlan); + + for (i = 0; i < smi->num_mib_counters; ++i) { + len += snprintf(buf + len, sizeof(smi->buf) - len, + "%-36s: ", smi->mib_counters[i].name); + if (!smi->ops->get_mib_counter(smi, i, val->port_vlan, + &counter)) + len += snprintf(buf + len, sizeof(smi->buf) - len, + "%llu\n", counter); + else + len += snprintf(buf + len, sizeof(smi->buf) - len, + "%s\n", "error"); + } + + val->value.s = buf; + val->len = len; + return 0; +} +EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_mib); + +int rtl8366_sw_get_port_stats(struct switch_dev *dev, int port, + struct switch_port_stats *stats, + int txb_id, int rxb_id) +{ + struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); + unsigned long long counter = 0; + int ret; + + if (port >= smi->num_ports) + return -EINVAL; + + ret = smi->ops->get_mib_counter(smi, txb_id, port, &counter); + if (ret) + return ret; + + stats->tx_bytes = counter; + + ret = smi->ops->get_mib_counter(smi, rxb_id, port, &counter); + if (ret) + return ret; + + stats->rx_bytes = counter; + + return 0; +} +EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_stats); + +int rtl8366_sw_get_vlan_info(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + int i; + u32 len = 0; + struct rtl8366_vlan_4k vlan4k; + struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); + char *buf = smi->buf; + int err; + + if (!smi->ops->is_vlan_valid(smi, val->port_vlan)) + return -EINVAL; + + memset(buf, '\0', sizeof(smi->buf)); + + err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k); + if (err) + return err; + + len += snprintf(buf + len, sizeof(smi->buf) - len, + "VLAN %d: Ports: '", vlan4k.vid); + + for (i = 0; i < smi->num_ports; i++) { + if (!(vlan4k.member & (1 << i))) + continue; + + len += snprintf(buf + len, sizeof(smi->buf) - len, "%d%s", i, + (vlan4k.untag & (1 << i)) ? "" : "t"); + } + + len += snprintf(buf + len, sizeof(smi->buf) - len, + "', members=%04x, untag=%04x, fid=%u", + vlan4k.member, vlan4k.untag, vlan4k.fid); + + val->value.s = buf; + val->len = len; + + return 0; +} +EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_info); + +int rtl8366_sw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val) +{ + struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); + struct switch_port *port; + struct rtl8366_vlan_4k vlan4k; + int i; + + if (!smi->ops->is_vlan_valid(smi, val->port_vlan)) + return -EINVAL; + + smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k); + + port = &val->value.ports[0]; + val->len = 0; + for (i = 0; i < smi->num_ports; i++) { + if (!(vlan4k.member & BIT(i))) + continue; + + port->id = i; + port->flags = (vlan4k.untag & BIT(i)) ? + 0 : BIT(SWITCH_PORT_FLAG_TAGGED); + val->len++; + port++; + } + return 0; +} +EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_ports); + +int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) +{ + struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); + struct switch_port *port; + u32 member = 0; + u32 untag = 0; + int err; + int i; + + if (!smi->ops->is_vlan_valid(smi, val->port_vlan)) + return -EINVAL; + + port = &val->value.ports[0]; + for (i = 0; i < val->len; i++, port++) { + int pvid = 0; + member |= BIT(port->id); + + if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED))) + untag |= BIT(port->id); + + /* + * To ensure that we have a valid MC entry for this VLAN, + * initialize the port VLAN ID here. + */ + err = rtl8366_get_pvid(smi, port->id, &pvid); + if (err < 0) + return err; + if (pvid == 0) { + err = rtl8366_set_pvid(smi, port->id, val->port_vlan); + if (err < 0) + return err; + } + } + + return rtl8366_set_vlan(smi, val->port_vlan, member, untag, 0); +} +EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_ports); + +int rtl8366_sw_get_vlan_fid(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct rtl8366_vlan_4k vlan4k; + struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); + int err; + + if (!smi->ops->is_vlan_valid(smi, val->port_vlan)) + return -EINVAL; + + err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k); + if (err) + return err; + + val->value.i = vlan4k.fid; + + return 0; +} +EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_fid); + +int rtl8366_sw_set_vlan_fid(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct rtl8366_vlan_4k vlan4k; + struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); + int err; + + if (!smi->ops->is_vlan_valid(smi, val->port_vlan)) + return -EINVAL; + + if (val->value.i < 0 || val->value.i > attr->max) + return -EINVAL; + + err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k); + if (err) + return err; + + return rtl8366_set_vlan(smi, val->port_vlan, + vlan4k.member, + vlan4k.untag, + val->value.i); +} +EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_fid); + +int rtl8366_sw_get_vlan_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); + + if (attr->ofs > 2) + return -EINVAL; + + if (attr->ofs == 1) + val->value.i = smi->vlan_enabled; + else + val->value.i = smi->vlan4k_enabled; + + return 0; +} +EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_enable); + +int rtl8366_sw_set_vlan_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev); + int err; + + if (attr->ofs > 2) + return -EINVAL; + + if (attr->ofs == 1) + err = rtl8366_enable_vlan(smi, val->value.i); + else + err = rtl8366_enable_vlan4k(smi, val->value.i); + + return err; +} +EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_enable); + struct rtl8366_smi *rtl8366_smi_alloc(struct device *parent) { struct rtl8366_smi *smi; @@ -758,29 +1278,99 @@ struct rtl8366_smi *rtl8366_smi_alloc(struct device *parent) } EXPORT_SYMBOL_GPL(rtl8366_smi_alloc); -int rtl8366_smi_init(struct rtl8366_smi *smi) +static int __rtl8366_smi_init(struct rtl8366_smi *smi, const char *name) { int err; - if (!smi->ops) - return -EINVAL; - - err = gpio_request(smi->gpio_sda, dev_name(smi->parent)); + err = gpio_request(smi->gpio_sda, name); if (err) { - dev_err(smi->parent, "gpio_request failed for %u, err=%d\n", + printk(KERN_ERR "rtl8366_smi: gpio_request failed for %u, err=%d\n", smi->gpio_sda, err); goto err_out; } - err = gpio_request(smi->gpio_sck, dev_name(smi->parent)); + err = gpio_request(smi->gpio_sck, name); if (err) { - dev_err(smi->parent, "gpio_request failed for %u, err=%d\n", + printk(KERN_ERR "rtl8366_smi: gpio_request failed for %u, err=%d\n", smi->gpio_sck, err); goto err_free_sda; } spin_lock_init(&smi->lock); + /* start the switch */ + if (smi->hw_reset) { + smi->hw_reset(false); + msleep(RTL8366_SMI_HW_START_DELAY); + } + + return 0; + + err_free_sda: + gpio_free(smi->gpio_sda); + err_out: + return err; +} + +static void __rtl8366_smi_cleanup(struct rtl8366_smi *smi) +{ + if (smi->hw_reset) + smi->hw_reset(true); + + gpio_free(smi->gpio_sck); + gpio_free(smi->gpio_sda); +} + +enum rtl8366_type rtl8366_smi_detect(struct rtl8366_platform_data *pdata) +{ + static struct rtl8366_smi smi; + enum rtl8366_type type = RTL8366_TYPE_UNKNOWN; + u32 reg = 0; + + memset(&smi, 0, sizeof(smi)); + smi.gpio_sda = pdata->gpio_sda; + smi.gpio_sck = pdata->gpio_sck; + smi.clk_delay = 10; + smi.cmd_read = 0xa9; + smi.cmd_write = 0xa8; + + if (__rtl8366_smi_init(&smi, "rtl8366")) + goto out; + + if (rtl8366_smi_read_reg(&smi, 0x5c, ®)) + goto cleanup; + + switch(reg) { + case 0x6027: + printk("Found an RTL8366S switch\n"); + type = RTL8366_TYPE_S; + break; + case 0x5937: + printk("Found an RTL8366RB switch\n"); + type = RTL8366_TYPE_RB; + break; + default: + printk("Found an Unknown RTL8366 switch (id=0x%04x)\n", reg); + break; + } + +cleanup: + __rtl8366_smi_cleanup(&smi); +out: + return type; +} + +int rtl8366_smi_init(struct rtl8366_smi *smi) +{ + int err; + + if (!smi->ops) + return -EINVAL; + + err = __rtl8366_smi_init(smi, dev_name(smi->parent)); + if (err) + goto err_out; + dev_info(smi->parent, "using GPIO pins %u (SDA) and %u (SCK)\n", smi->gpio_sda, smi->gpio_sck); @@ -790,6 +1380,27 @@ int rtl8366_smi_init(struct rtl8366_smi *smi) goto err_free_sck; } + err = rtl8366_reset(smi); + if (err) + goto err_free_sck; + + err = smi->ops->setup(smi); + if (err) { + dev_err(smi->parent, "chip setup failed, err=%d\n", err); + goto err_free_sck; + } + + err = rtl8366_init_vlan(smi); + if (err) { + dev_err(smi->parent, "VLAN initialization failed, err=%d\n", + err); + goto err_free_sck; + } + + err = rtl8366_enable_all_ports(smi, 1); + if (err) + goto err_free_sck; + err = rtl8366_smi_mii_init(smi); if (err) goto err_free_sck; @@ -799,9 +1410,7 @@ int rtl8366_smi_init(struct rtl8366_smi *smi) return 0; err_free_sck: - gpio_free(smi->gpio_sck); - err_free_sda: - gpio_free(smi->gpio_sda); + __rtl8366_smi_cleanup(smi); err_out: return err; } @@ -811,11 +1420,75 @@ void rtl8366_smi_cleanup(struct rtl8366_smi *smi) { rtl8366_debugfs_remove(smi); rtl8366_smi_mii_cleanup(smi); - gpio_free(smi->gpio_sck); - gpio_free(smi->gpio_sda); + __rtl8366_smi_cleanup(smi); } EXPORT_SYMBOL_GPL(rtl8366_smi_cleanup); +#ifdef CONFIG_OF +int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi) +{ + int sck = of_get_named_gpio(pdev->dev.of_node, "gpio-sck", 0); + int sda = of_get_named_gpio(pdev->dev.of_node, "gpio-sda", 0); + + if (!gpio_is_valid(sck) || !gpio_is_valid(sda)) { + dev_err(&pdev->dev, "gpios missing in devictree\n"); + return -EINVAL; + } + + smi->gpio_sda = sda; + smi->gpio_sck = sck; + + return 0; +} +#else +static inline int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi) +{ + return -ENODEV; +} +#endif + +int rtl8366_smi_probe_plat(struct platform_device *pdev, struct rtl8366_smi *smi) +{ + struct rtl8366_platform_data *pdata = pdev->dev.platform_data; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform data specified\n"); + return -EINVAL; + } + + smi->gpio_sda = pdata->gpio_sda; + smi->gpio_sck = pdata->gpio_sck; + smi->hw_reset = pdata->hw_reset; + + return 0; +} + + +struct rtl8366_smi *rtl8366_smi_probe(struct platform_device *pdev) +{ + struct rtl8366_smi *smi; + int err; + + smi = rtl8366_smi_alloc(&pdev->dev); + if (!smi) + return NULL; + + if (pdev->dev.of_node) + err = rtl8366_smi_probe_of(pdev, smi); + else + err = rtl8366_smi_probe_plat(pdev, smi); + + if (err) + goto free_smi; + + return smi; + +free_smi: + kfree(smi); + return NULL; +} +EXPORT_SYMBOL_GPL(rtl8366_smi_probe); + MODULE_DESCRIPTION("Realtek RTL8366 SMI interface driver"); MODULE_AUTHOR("Gabor Juhos "); MODULE_LICENSE("GPL v2");