|
@@ -3,18 +3,19 @@
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
* for more details.
|
|
* for more details.
|
|
*
|
|
*
|
|
- * Copyright (C) 2010 Cavium Networks
|
|
|
|
|
|
+ * Copyright (C) 2010, 2011 Cavium Networks
|
|
*/
|
|
*/
|
|
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/module.h>
|
|
|
|
+#include <linux/mutex.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/delay.h>
|
|
|
|
|
|
-#include <asm/atomic.h>
|
|
|
|
-
|
|
|
|
#include <asm/octeon/octeon.h>
|
|
#include <asm/octeon/octeon.h>
|
|
#include <asm/octeon/cvmx-uctlx-defs.h>
|
|
#include <asm/octeon/cvmx-uctlx-defs.h>
|
|
|
|
|
|
-static atomic_t octeon2_usb_clock_start_cnt = ATOMIC_INIT(0);
|
|
|
|
|
|
+static DEFINE_MUTEX(octeon2_usb_clocks_mutex);
|
|
|
|
+
|
|
|
|
+static int octeon2_usb_clock_start_cnt;
|
|
|
|
|
|
void octeon2_usb_clocks_start(void)
|
|
void octeon2_usb_clocks_start(void)
|
|
{
|
|
{
|
|
@@ -26,8 +27,12 @@ void octeon2_usb_clocks_start(void)
|
|
int i;
|
|
int i;
|
|
unsigned long io_clk_64_to_ns;
|
|
unsigned long io_clk_64_to_ns;
|
|
|
|
|
|
- if (atomic_inc_return(&octeon2_usb_clock_start_cnt) != 1)
|
|
|
|
- return;
|
|
|
|
|
|
+
|
|
|
|
+ mutex_lock(&octeon2_usb_clocks_mutex);
|
|
|
|
+
|
|
|
|
+ octeon2_usb_clock_start_cnt++;
|
|
|
|
+ if (octeon2_usb_clock_start_cnt != 1)
|
|
|
|
+ goto exit;
|
|
|
|
|
|
io_clk_64_to_ns = 64000000000ull / octeon_get_io_clock_rate();
|
|
io_clk_64_to_ns = 64000000000ull / octeon_get_io_clock_rate();
|
|
|
|
|
|
@@ -43,6 +48,13 @@ void octeon2_usb_clocks_start(void)
|
|
|
|
|
|
/* Step 3: Configure the reference clock, PHY, and HCLK */
|
|
/* Step 3: Configure the reference clock, PHY, and HCLK */
|
|
clk_rst_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_CLK_RST_CTL(0));
|
|
clk_rst_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_CLK_RST_CTL(0));
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If the UCTL looks like it has already been started, skip
|
|
|
|
+ * the initialization, otherwise bus errors are obtained.
|
|
|
|
+ */
|
|
|
|
+ if (clk_rst_ctl.s.hrst)
|
|
|
|
+ goto end_clock;
|
|
/* 3a */
|
|
/* 3a */
|
|
clk_rst_ctl.s.p_por = 1;
|
|
clk_rst_ctl.s.p_por = 1;
|
|
clk_rst_ctl.s.hrst = 0;
|
|
clk_rst_ctl.s.hrst = 0;
|
|
@@ -158,6 +170,7 @@ void octeon2_usb_clocks_start(void)
|
|
clk_rst_ctl.s.hrst = 1;
|
|
clk_rst_ctl.s.hrst = 1;
|
|
cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
|
|
cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
|
|
|
|
|
|
|
|
+end_clock:
|
|
/* Now we can set some other registers. */
|
|
/* Now we can set some other registers. */
|
|
|
|
|
|
for (i = 0; i <= 1; i++) {
|
|
for (i = 0; i <= 1; i++) {
|
|
@@ -168,18 +181,15 @@ void octeon2_usb_clocks_start(void)
|
|
cvmx_write_csr(CVMX_UCTLX_UPHY_PORTX_CTL_STATUS(i, 0),
|
|
cvmx_write_csr(CVMX_UCTLX_UPHY_PORTX_CTL_STATUS(i, 0),
|
|
port_ctl_status.u64);
|
|
port_ctl_status.u64);
|
|
}
|
|
}
|
|
|
|
+exit:
|
|
|
|
+ mutex_unlock(&octeon2_usb_clocks_mutex);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(octeon2_usb_clocks_start);
|
|
EXPORT_SYMBOL(octeon2_usb_clocks_start);
|
|
|
|
|
|
void octeon2_usb_clocks_stop(void)
|
|
void octeon2_usb_clocks_stop(void)
|
|
{
|
|
{
|
|
- union cvmx_uctlx_if_ena if_ena;
|
|
|
|
-
|
|
|
|
- if (atomic_dec_return(&octeon2_usb_clock_start_cnt) != 0)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
- if_ena.u64 = 0;
|
|
|
|
- if_ena.s.en = 0;
|
|
|
|
- cvmx_write_csr(CVMX_UCTLX_IF_ENA(0), if_ena.u64);
|
|
|
|
|
|
+ mutex_lock(&octeon2_usb_clocks_mutex);
|
|
|
|
+ octeon2_usb_clock_start_cnt--;
|
|
|
|
+ mutex_unlock(&octeon2_usb_clocks_mutex);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(octeon2_usb_clocks_stop);
|
|
EXPORT_SYMBOL(octeon2_usb_clocks_stop);
|