return 0;
}
EXPORT_SYMBOL_GPL(amlogic_new_usbphy_reset_phycfg_v2);
+
+int amlogic_reset_phycfg_otgport(struct amlogic_usb_v2 *phy, int cnt)
+{
+ u32 val;
+ u32 temp = 0;
+
+ temp = temp | (1 << cnt);
+
+ /* set usb phy to low power mode */
+ val = readl((void __iomem *)
+ ((unsigned long)phy->reset_regs + (0x21 * 4 - 0x8)));
+ writel((val & (~temp)), (void __iomem *)
+ ((unsigned long)phy->reset_regs + (0x21 * 4 - 0x8)));
+
+ udelay(100);
+
+ val = readl((void __iomem *)
+ ((unsigned long)phy->reset_regs + (0x21 * 4 - 0x8)));
+ writel((val | temp), (void __iomem *)
+ ((unsigned long)phy->reset_regs + (0x21 * 4 - 0x8)));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(amlogic_reset_phycfg_otgport);
+
extern int amlogic_new_usbphy_reset_phycfg_v2
(struct amlogic_usb_v2 *phy, int cnt);
void set_usb_phy_host_tuning(int port, int default_val);
+int amlogic_reset_phycfg_otgport(struct amlogic_usb_v2 *phy, int cnt);
}
+void usb2_reset_otgport_phy(void)
+{
+ if (!g_phy2_v2)
+ return;
+ printk(KERN_ERR"%s\n", __func__);
+ amlogic_reset_phycfg_otgport(g_phy2_v2, 17);
+}
void set_usb_pll(struct amlogic_usb_v2 *phy, void __iomem *reg)
{
/* PHY Tune */
if (g_phy2_v2) {
if (g_phy2_v2->phy_version) {
- /**tl1 g12b revB don't need set 0x10 ,0x38 and 0x34**/
+ /**tl1 g12b revB don't need set 0x10 ,0x38**/
writel(phy->pll_setting[3], reg + 0x50);
writel(0x2a, reg + 0x54);
#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/usb/phy.h>
+#include <linux/usb/role.h>
#include <linux/amlogic/usb-v2.h>
#include <linux/amlogic/aml_gpio_consumer.h>
#include <linux/workqueue.h>
struct usb_aml_regs_v2 usb_new_aml_regs_v2;
struct amlogic_usb_v2 *g_phy_v2;
+struct usb_role_switch *host_mode_sw;
+u32 typec_host_mode;
+u32 idpin_host_mode;
+u32 force_host_mode;
static void power_switch_to_pcie(struct amlogic_usb_v2 *phy);
phy->suspend_flag = 1;
}
+static void update_host_mode(struct amlogic_usb_v2 *phy)
+{
+ unsigned long reg_addr = ((unsigned long)phy->usb2_phy_cfg);
+
+ if (idpin_host_mode || typec_host_mode || force_host_mode) {
+ dev_info(phy->dev, "host mode");
+ amlogic_new_set_vbus_power(phy, 1);
+ aml_new_usb_notifier_call(0);
+ set_mode(reg_addr, HOST_MODE);
+ } else {
+ dev_info(phy->dev, "device mode");
+ set_mode(reg_addr, DEVICE_MODE);
+ aml_new_usb_notifier_call(1);
+ amlogic_new_set_vbus_power(phy, 0);
+ }
+}
+
void aml_new_usb_v2_init(void)
{
union usb_r5_v2 r5 = {.d32 = 0};
- unsigned long reg_addr;
if (!g_phy_v2)
return;
- reg_addr = (unsigned long)g_phy_v2->usb2_phy_cfg;
-
r5.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[5]);
- if (r5.b.iddig_curr == 0) {
- amlogic_new_set_vbus_power(g_phy_v2, 1);
- aml_new_usb_notifier_call(0);
- set_mode(reg_addr, HOST_MODE);
- }
+ idpin_host_mode = (r5.b.iddig_curr == 0) ? 1 : 0;
+ update_host_mode(g_phy_v2);
}
EXPORT_SYMBOL(aml_new_usb_v2_init);
udelay(500);
}
+static enum usb_role get_host_mode(struct device *dev)
+{
+ return (force_host_mode || typec_host_mode || idpin_host_mode)
+ ? USB_ROLE_HOST
+ : USB_ROLE_DEVICE;
+}
+
+static int set_host_mode(struct device *dev, enum usb_role role)
+{
+ if (typec_host_mode || idpin_host_mode) {
+ dev_err(dev, "host mode was locked by typec or idpin");
+ return -EPERM;
+ }
+
+ force_host_mode = (role == USB_ROLE_HOST ? 1 : 0);
+
+ if (g_phy_v2)
+ update_host_mode(g_phy_v2);
+
+ return 0;
+}
+
+static const struct usb_role_switch_desc host_mode_sw_desc = {
+ .get = get_host_mode,
+ .set = set_host_mode,
+ .allow_userspace_control = true,
+};
+
+void amlogic_typec_set_mode(int mode)
+{
+ if (!g_phy_v2)
+ return;
+
+ typec_host_mode = (mode == HOST_MODE) ? 1 : 0;
+ update_host_mode(g_phy_v2);
+}
+EXPORT_SYMBOL(amlogic_typec_set_mode);
+
static void amlogic_gxl_work(struct work_struct *work)
{
struct amlogic_usb_v2 *phy =
container_of(work, struct amlogic_usb_v2, work.work);
union usb_r5_v2 r5 = {.d32 = 0};
- unsigned long reg_addr = ((unsigned long)phy->usb2_phy_cfg);
r5.d32 = readl(usb_new_aml_regs_v2.usb_r_v2[5]);
- if (r5.b.iddig_curr == 0) {
- amlogic_new_set_vbus_power(phy, 1);
- aml_new_usb_notifier_call(0);
- set_mode(reg_addr, HOST_MODE);
- } else {
- set_mode(reg_addr, DEVICE_MODE);
- aml_new_usb_notifier_call(1);
- amlogic_new_set_vbus_power(phy, 0);
- }
+ idpin_host_mode = (r5.b.iddig_curr == 0) ? 1 : 0;
+ update_host_mode(phy);
r5.b.usb_iddig_irq = 0;
writel(r5.d32, usb_new_aml_regs_v2.usb_r_v2[5]);
}
g_phy_v2 = phy;
+ host_mode_sw = usb_role_switch_register(dev, &host_mode_sw_desc);
+ if (IS_ERR(host_mode_sw)) {
+ dev_err(dev, "failed to register usb role switch\n");
+ host_mode_sw = NULL;
+ }
+
return 0;
}
static int amlogic_new_usb3_remove(struct platform_device *pdev)
{
+ if (host_mode_sw)
+ usb_role_switch_unregister(host_mode_sw);
+
return 0;
}
int ret, i;
u8 buf[ETH_ALEN], chipcode = 0;
u32 phyid;
+ u16 psc;
struct asix_common_private *priv;
usbnet_get_endpoints(dev,intf);
return ret;
}
+ asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x18, 0, 2, &psc, 0);
+ le16_to_cpus(&psc);
+ asix_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL |
+ (psc & 0x7f00), 0, 0, NULL, 0);
+
/* Read PHYID register *AFTER* the PHY was reset properly */
phyid = asix_get_phyid(dev);
netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid);
/*-------------------------------------------------------------------------*/
+extern void usb2_reset_otgport_phy(void);
+
/* handles CDC Ethernet and many other network "bulk data" interfaces */
int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf)
{
struct usbnet *dev = urb->context;
int status = urb->status;
+ if (status)
+ printk("### (%s) status: %d ###", __func__, status);
+
switch (status) {
/* success */
case 0:
dev->driver_info->status(dev, urb);
break;
+ case -ECONNRESET:
+ usb2_reset_otgport_phy();
+ return;
+
/* software-driven interface shutdown */
case -ENOENT: /* urb killed */
case -ESHUTDOWN: /* hardware gone */
netif_dbg(dev, ifdown, dev->net,
"intr shutdown, code %d\n", status);
return;
+ case -EPROTO:
+ usb2_reset_otgport_phy();
+ return;
/* NOTE: not throttling like RX/TX, since this endpoint
* already polls infrequently
state = rx_done;
entry->urb = NULL;
+ if (urb_status)
+ printk("### (%s) urb_status: %d ###", __func__, urb_status);
+
switch (urb_status) {
/* success */
case 0:
/* software-driven interface shutdown */
case -ECONNRESET: /* async unlink */
+ usb2_reset_otgport_phy();
case -ESHUTDOWN: /* hardware gone */
netif_dbg(dev, ifdown, dev->net,
"rx shutdown, code %d\n", urb_status);
* so we still recover when the fault isn't a hub_wq delay.
*/
case -EPROTO:
+ usb2_reset_otgport_phy();
case -ETIME:
case -EILSEQ:
dev->net->stats.rx_errors++;
dev->net->stats.tx_packets += entry->packets;
dev->net->stats.tx_bytes += entry->length;
} else {
+ printk("### (%s) urb->status: %d ###", __func__, urb->status);
dev->net->stats.tx_errors++;
switch (urb->status) {
/* software-driven interface shutdown */
case -ECONNRESET: // async unlink
- case -ESHUTDOWN: // hardware gone
+ usb2_reset_otgport_phy();
+ case -ESHUTDOWN: // hardware gone
break;
/* like rx, tx gets controller i/o faults during hub_wq
#else
static inline void usb_led_activity(enum usb_led_event ev) {}
#endif
+void usb2_reset_otgport_phy(void);
#endif /* __KERNEL__ */