summaryrefslogtreecommitdiffstats
path: root/firmware/target/mips/ingenic_x1000/usb-x1000.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/usb-x1000.c')
-rw-r--r--firmware/target/mips/ingenic_x1000/usb-x1000.c214
1 files changed, 214 insertions, 0 deletions
diff --git a/firmware/target/mips/ingenic_x1000/usb-x1000.c b/firmware/target/mips/ingenic_x1000/usb-x1000.c
new file mode 100644
index 0000000000..398528c6c4
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/usb-x1000.c
@@ -0,0 +1,214 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "usb.h"
+#include "usb_core.h"
+#include "usb_drv.h"
+#include "usb-designware.h"
+#include "irq-x1000.h"
+#include "gpio-x1000.h"
+#include "x1000/cpm.h"
+
+/*
+ * USB-Designware driver API
+ */
+
+const struct usb_dw_config usb_dw_config = {
+ .phytype = DWC_PHYTYPE_UTMI_16,
+
+ /* Available FIFO memory: 3576 words
+ * Number of endpoints: 9
+ * Max packet size: 512 bytes
+ */
+ .rx_fifosz = 816, /* shared RxFIFO */
+ .nptx_fifosz = 32, /* only used for EP0 IN */
+ .ptx_fifosz = 384, /* room for 7 IN EPs */
+
+#ifndef USB_DW_ARCH_SLAVE
+ .ahb_burst_len = HBSTLEN_INCR16,
+ /* Disable Rx FIFO thresholding. It appears to cause problems,
+ * apparently a known issue -- Synopsys recommends disabling it
+ * because it can cause issues during certain error conditions.
+ */
+ .ahb_threshold = 0,
+#else
+ .disable_double_buffering = false,
+#endif
+};
+
+/* USB PHY init from Linux kernel code:
+ * - arch/mips/xburst/soc-x1000/common/cpm_usb.c
+ * Copyright (C) 2005-2017 Ingenic Semiconductor
+ */
+void usb_dw_target_enable_clocks(void)
+{
+ /* Enable CPM clock */
+ jz_writef(CPM_CLKGR, OTG(0));
+#if X1000_EXCLK_FREQ == 24000000
+ jz_writef(CPM_USBCDR, CLKSRC_V(EXCLK), CE(1), CLKDIV(0), PHY_GATE(0));
+#else
+# error "please add USB clock settings for 26 MHz EXCLK"
+#endif
+ while(jz_readf(CPM_USBCDR, BUSY));
+ jz_writef(CPM_USBCDR, CE(0));
+
+ /* PHY soft reset */
+ jz_writef(CPM_SRBC, OTG_SR(1));
+ udelay(10);
+ jz_writef(CPM_SRBC, OTG_SR(0));
+
+ /* Ungate PHY clock */
+ jz_writef(CPM_OPCR, GATE_USBPHY_CLK(0));
+
+ /* Exit suspend state */
+ jz_writef(CPM_OPCR, SPENDN0(1));
+ udelay(45);
+
+ /* Program core configuration */
+ jz_overwritef(CPM_USBVBFIL,
+ IDDIGFIL(0),
+ VBFIL(0));
+ jz_overwritef(CPM_USBRDT,
+ HB_MASK(0),
+ VBFIL_LD_EN(1),
+ IDDIG_EN(0),
+ RDT(0x96));
+ jz_overwritef(CPM_USBPCR,
+ OTG_DISABLE(1),
+ COMMONONN(1),
+ VBUSVLDEXT(1),
+ VBUSVLDEXTSEL(1),
+ SQRXTUNE(7),
+ TXPREEMPHTUNE(1),
+ TXHSXVTUNE(1),
+ TXVREFTUNE(7));
+ jz_overwritef(CPM_USBPCR1,
+ BVLD_REG(1),
+ REFCLK_SEL_V(CLKCORE),
+ REFCLK_DIV_V(24MHZ), /* applies for 26 MHz EXCLK too */
+ WORD_IF_V(16BIT));
+
+ /* Power on reset */
+ jz_writef(CPM_USBPCR, POR(1));
+ mdelay(1);
+ jz_writef(CPM_USBPCR, POR(0));
+ mdelay(1);
+}
+
+void usb_dw_target_disable_clocks(void)
+{
+ /* Suspend and power down PHY, then gate its clock */
+ jz_writef(CPM_OPCR, SPENDN0(0));
+ udelay(5);
+ jz_writef(CPM_USBPCR, OTG_DISABLE(1), SIDDQ(1));
+ jz_writef(CPM_OPCR, GATE_USBPHY_CLK(1));
+
+ /* Disable CPM clock */
+ jz_writef(CPM_USBCDR, CE(1), STOP(1), PHY_GATE(1));
+ while(jz_readf(CPM_USBCDR, BUSY));
+ jz_writef(CPM_USBCDR, CE(0));
+ jz_writef(CPM_CLKGR, OTG(1));
+}
+
+void usb_dw_target_enable_irq(void)
+{
+ system_enable_irq(IRQ_OTG);
+}
+
+void usb_dw_target_disable_irq(void)
+{
+ system_disable_irq(IRQ_OTG);
+}
+
+void usb_dw_target_clear_irq(void)
+{
+}
+
+/*
+ * Rockbox API
+ */
+
+#ifdef USB_STATUS_BY_EVENT
+static volatile int usb_status = USB_EXTRACTED;
+static void usb_detect_interrupt(void);
+#endif
+
+static int __usb_detect(void)
+{
+ /* XXX: Do we need an active level define for this? */
+ if(gpio_get_level(GPIO_USB_DETECT))
+ return USB_INSERTED;
+ else
+ return USB_EXTRACTED;
+}
+
+void usb_enable(bool on)
+{
+ if(on)
+ usb_core_init();
+ else
+ usb_core_exit();
+}
+
+void usb_init_device(void)
+{
+ /* Disable drvvbus pin -- it is only used when acting as a host,
+ * which Rockbox does not support */
+ gpio_set_function(GPIO_USB_DRVVBUS, GPIOF_OUTPUT(0));
+
+ /* Power up the core clocks to allow writing
+ to some registers needed to power it down */
+ usb_dw_target_disable_irq();
+ usb_dw_target_enable_clocks();
+ usb_drv_exit();
+
+#ifdef USB_STATUS_BY_EVENT
+ /* Setup USB detect pin IRQ */
+ usb_status = __usb_detect();
+ system_set_irq_handler(GPIO_TO_IRQ(GPIO_USB_DETECT), usb_detect_interrupt);
+ gpio_set_function(GPIO_USB_DETECT, GPIOF_IRQ_EDGE(1));
+ gpio_flip_edge_irq(GPIO_USB_DETECT);
+ gpio_enable_irq(GPIO_USB_DETECT);
+#endif
+}
+
+#ifndef USB_STATUS_BY_EVENT
+int usb_detect(void)
+{
+ return __usb_detect();
+}
+#else
+int usb_detect(void)
+{
+ return usb_status;
+}
+
+static void usb_detect_interrupt(void)
+{
+ /* Update status and flip the IRQ trigger edge */
+ usb_status = __usb_detect();
+ gpio_flip_edge_irq(GPIO_USB_DETECT);
+
+ /* Notify Rockbox of event */
+ usb_status_event(usb_status);
+}
+#endif