2009-09-16 14:54:18 -07:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
2012-07-05 18:12:38 +02:00
* Copyright ( C ) 2004 - 2011 Cavium Networks
2009-09-16 14:54:18 -07:00
* Copyright ( C ) 2008 Wind River Systems
*/
2014-11-25 12:28:46 +01:00
# include <linux/delay.h>
2009-09-16 14:54:18 -07:00
# include <linux/init.h>
# include <linux/irq.h>
2010-01-07 11:54:21 -08:00
# include <linux/i2c.h>
2010-10-08 14:47:53 -07:00
# include <linux/usb.h>
2010-10-01 13:27:27 -07:00
# include <linux/dma-mapping.h>
2009-09-16 14:54:18 -07:00
# include <linux/module.h>
2014-11-25 12:28:46 +01:00
# include <linux/mutex.h>
2012-07-05 18:12:38 +02:00
# include <linux/slab.h>
2009-09-16 14:54:18 -07:00
# include <linux/platform_device.h>
2012-07-05 18:12:38 +02:00
# include <linux/of_platform.h>
# include <linux/of_fdt.h>
# include <linux/libfdt.h>
2014-11-25 12:28:46 +01:00
# include <linux/usb/ehci_pdriver.h>
# include <linux/usb/ohci_pdriver.h>
2009-09-16 14:54:18 -07:00
# include <asm/octeon/octeon.h>
# include <asm/octeon/cvmx-rnm-defs.h>
2012-07-05 18:12:38 +02:00
# include <asm/octeon/cvmx-helper.h>
# include <asm/octeon/cvmx-helper-board.h>
2014-11-25 12:28:46 +01:00
# include <asm/octeon/cvmx-uctlx-defs.h>
2009-09-16 14:54:18 -07:00
/* Octeon Random Number Generator. */
static int __init octeon_rng_device_init ( void )
{
struct platform_device * pd ;
int ret = 0 ;
struct resource rng_resources [ ] = {
{
. flags = IORESOURCE_MEM ,
. start = XKPHYS_TO_PHYS ( CVMX_RNM_CTL_STATUS ) ,
. end = XKPHYS_TO_PHYS ( CVMX_RNM_CTL_STATUS ) + 0xf
} , {
. flags = IORESOURCE_MEM ,
. start = cvmx_build_io_address ( 8 , 0 ) ,
. end = cvmx_build_io_address ( 8 , 0 ) + 0x7
}
} ;
pd = platform_device_alloc ( " octeon_rng " , - 1 ) ;
if ( ! pd ) {
ret = - ENOMEM ;
goto out ;
}
ret = platform_device_add_resources ( pd , rng_resources ,
ARRAY_SIZE ( rng_resources ) ) ;
if ( ret )
goto fail ;
ret = platform_device_add ( pd ) ;
if ( ret )
goto fail ;
return ret ;
fail :
platform_device_put ( pd ) ;
out :
return ret ;
}
device_initcall ( octeon_rng_device_init ) ;
2010-10-08 14:47:53 -07:00
# ifdef CONFIG_USB
2014-11-25 12:28:46 +01:00
static DEFINE_MUTEX ( octeon2_usb_clocks_mutex ) ;
static int octeon2_usb_clock_start_cnt ;
2015-01-06 13:48:56 +01:00
static void octeon2_usb_clocks_start ( struct device * dev )
2014-11-25 12:28:46 +01:00
{
u64 div ;
union cvmx_uctlx_if_ena if_ena ;
union cvmx_uctlx_clk_rst_ctl clk_rst_ctl ;
union cvmx_uctlx_uphy_ctl_status uphy_ctl_status ;
union cvmx_uctlx_uphy_portx_ctl_status port_ctl_status ;
int i ;
unsigned long io_clk_64_to_ns ;
2015-01-06 13:48:56 +01:00
u32 clock_rate = 12000000 ;
bool is_crystal_clock = false ;
2014-11-25 12:28:46 +01:00
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 ( ) ;
2015-01-06 13:48:56 +01:00
if ( dev - > of_node ) {
struct device_node * uctl_node ;
const char * clock_type ;
uctl_node = of_get_parent ( dev - > of_node ) ;
if ( ! uctl_node ) {
dev_err ( dev , " No UCTL device node \n " ) ;
goto exit ;
}
i = of_property_read_u32 ( uctl_node ,
" refclk-frequency " , & clock_rate ) ;
if ( i ) {
dev_err ( dev , " No UCTL \" refclk-frequency \" \n " ) ;
goto exit ;
}
i = of_property_read_string ( uctl_node ,
" refclk-type " , & clock_type ) ;
if ( ! i & & strcmp ( " crystal " , clock_type ) = = 0 )
is_crystal_clock = true ;
}
2014-11-25 12:28:46 +01:00
/*
* Step 1 : Wait for voltages stable . That surely happened
* before starting the kernel .
*
* Step 2 : Enable SCLK of UCTL by writing UCTL0_IF_ENA [ EN ] = 1
*/
if_ena . u64 = 0 ;
if_ena . s . en = 1 ;
cvmx_write_csr ( CVMX_UCTLX_IF_ENA ( 0 ) , if_ena . u64 ) ;
/* Step 3: Configure the reference clock, PHY, and HCLK */
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 */
clk_rst_ctl . s . p_por = 1 ;
clk_rst_ctl . s . hrst = 0 ;
clk_rst_ctl . s . p_prst = 0 ;
clk_rst_ctl . s . h_clkdiv_rst = 0 ;
clk_rst_ctl . s . o_clkdiv_rst = 0 ;
clk_rst_ctl . s . h_clkdiv_en = 0 ;
clk_rst_ctl . s . o_clkdiv_en = 0 ;
cvmx_write_csr ( CVMX_UCTLX_CLK_RST_CTL ( 0 ) , clk_rst_ctl . u64 ) ;
/* 3b */
2015-01-06 13:48:56 +01:00
clk_rst_ctl . s . p_refclk_sel = is_crystal_clock ? 0 : 1 ;
switch ( clock_rate ) {
default :
pr_err ( " Invalid UCTL clock rate of %u, using 12000000 instead \n " ,
clock_rate ) ;
/* Fall through */
case 12000000 :
clk_rst_ctl . s . p_refclk_div = 0 ;
break ;
case 24000000 :
clk_rst_ctl . s . p_refclk_div = 1 ;
break ;
case 48000000 :
clk_rst_ctl . s . p_refclk_div = 2 ;
break ;
}
2014-11-25 12:28:46 +01:00
cvmx_write_csr ( CVMX_UCTLX_CLK_RST_CTL ( 0 ) , clk_rst_ctl . u64 ) ;
/* 3c */
div = octeon_get_io_clock_rate ( ) / 130000000ull ;
switch ( div ) {
case 0 :
div = 1 ;
break ;
case 1 :
case 2 :
case 3 :
case 4 :
break ;
case 5 :
div = 4 ;
break ;
case 6 :
case 7 :
div = 6 ;
break ;
case 8 :
case 9 :
case 10 :
case 11 :
div = 8 ;
break ;
default :
div = 12 ;
break ;
}
clk_rst_ctl . s . h_div = div ;
cvmx_write_csr ( CVMX_UCTLX_CLK_RST_CTL ( 0 ) , clk_rst_ctl . u64 ) ;
/* Read it back, */
clk_rst_ctl . u64 = cvmx_read_csr ( CVMX_UCTLX_CLK_RST_CTL ( 0 ) ) ;
clk_rst_ctl . s . h_clkdiv_en = 1 ;
cvmx_write_csr ( CVMX_UCTLX_CLK_RST_CTL ( 0 ) , clk_rst_ctl . u64 ) ;
/* 3d */
clk_rst_ctl . s . h_clkdiv_rst = 1 ;
cvmx_write_csr ( CVMX_UCTLX_CLK_RST_CTL ( 0 ) , clk_rst_ctl . u64 ) ;
/* 3e: delay 64 io clocks */
ndelay ( io_clk_64_to_ns ) ;
/*
* Step 4 : Program the power - on reset field in the UCTL
* clock - reset - control register .
*/
clk_rst_ctl . s . p_por = 0 ;
cvmx_write_csr ( CVMX_UCTLX_CLK_RST_CTL ( 0 ) , clk_rst_ctl . u64 ) ;
/* Step 5: Wait 1 ms for the PHY clock to start. */
mdelay ( 1 ) ;
/*
* Step 6 : Program the reset input from automatic test
* equipment field in the UPHY CSR
*/
uphy_ctl_status . u64 = cvmx_read_csr ( CVMX_UCTLX_UPHY_CTL_STATUS ( 0 ) ) ;
uphy_ctl_status . s . ate_reset = 1 ;
cvmx_write_csr ( CVMX_UCTLX_UPHY_CTL_STATUS ( 0 ) , uphy_ctl_status . u64 ) ;
/* Step 7: Wait for at least 10ns. */
ndelay ( 10 ) ;
/* Step 8: Clear the ATE_RESET field in the UPHY CSR. */
uphy_ctl_status . s . ate_reset = 0 ;
cvmx_write_csr ( CVMX_UCTLX_UPHY_CTL_STATUS ( 0 ) , uphy_ctl_status . u64 ) ;
/*
* Step 9 : Wait for at least 20 ns for UPHY to output PHY clock
* signals and OHCI_CLK48
*/
ndelay ( 20 ) ;
/* Step 10: Configure the OHCI_CLK48 and OHCI_CLK12 clocks. */
/* 10a */
clk_rst_ctl . s . o_clkdiv_rst = 1 ;
cvmx_write_csr ( CVMX_UCTLX_CLK_RST_CTL ( 0 ) , clk_rst_ctl . u64 ) ;
/* 10b */
clk_rst_ctl . s . o_clkdiv_en = 1 ;
cvmx_write_csr ( CVMX_UCTLX_CLK_RST_CTL ( 0 ) , clk_rst_ctl . u64 ) ;
/* 10c */
ndelay ( io_clk_64_to_ns ) ;
/*
* Step 11 : Program the PHY reset field :
* UCTL0_CLK_RST_CTL [ P_PRST ] = 1
*/
clk_rst_ctl . s . p_prst = 1 ;
cvmx_write_csr ( CVMX_UCTLX_CLK_RST_CTL ( 0 ) , clk_rst_ctl . u64 ) ;
/* Step 12: Wait 1 uS. */
udelay ( 1 ) ;
/* Step 13: Program the HRESET_N field: UCTL0_CLK_RST_CTL[HRST] = 1 */
clk_rst_ctl . s . hrst = 1 ;
cvmx_write_csr ( CVMX_UCTLX_CLK_RST_CTL ( 0 ) , clk_rst_ctl . u64 ) ;
end_clock :
/* Now we can set some other registers. */
for ( i = 0 ; i < = 1 ; i + + ) {
port_ctl_status . u64 =
cvmx_read_csr ( CVMX_UCTLX_UPHY_PORTX_CTL_STATUS ( i , 0 ) ) ;
/* Set txvreftune to 15 to obtain compliant 'eye' diagram. */
port_ctl_status . s . txvreftune = 15 ;
port_ctl_status . s . txrisetune = 1 ;
port_ctl_status . s . txpreemphasistune = 1 ;
cvmx_write_csr ( CVMX_UCTLX_UPHY_PORTX_CTL_STATUS ( i , 0 ) ,
port_ctl_status . u64 ) ;
}
/* Set uSOF cycle period to 60,000 bits. */
cvmx_write_csr ( CVMX_UCTLX_EHCI_FLA ( 0 ) , 0x20ull ) ;
exit :
mutex_unlock ( & octeon2_usb_clocks_mutex ) ;
}
static void octeon2_usb_clocks_stop ( void )
{
mutex_lock ( & octeon2_usb_clocks_mutex ) ;
octeon2_usb_clock_start_cnt - - ;
mutex_unlock ( & octeon2_usb_clocks_mutex ) ;
}
static int octeon_ehci_power_on ( struct platform_device * pdev )
{
2015-01-06 13:48:56 +01:00
octeon2_usb_clocks_start ( & pdev - > dev ) ;
2014-11-25 12:28:46 +01:00
return 0 ;
}
static void octeon_ehci_power_off ( struct platform_device * pdev )
{
octeon2_usb_clocks_stop ( ) ;
}
static struct usb_ehci_pdata octeon_ehci_pdata = {
/* Octeon EHCI matches CPU endianness. */
# ifdef __BIG_ENDIAN
. big_endian_mmio = 1 ,
# endif
2015-01-12 16:05:52 +01:00
. dma_mask_64 = 1 ,
2014-11-25 12:28:46 +01:00
. power_on = octeon_ehci_power_on ,
. power_off = octeon_ehci_power_off ,
} ;
2015-01-06 13:48:56 +01:00
static void __init octeon_ehci_hw_start ( struct device * dev )
2014-11-25 12:28:46 +01:00
{
union cvmx_uctlx_ehci_ctl ehci_ctl ;
2015-01-06 13:48:56 +01:00
octeon2_usb_clocks_start ( dev ) ;
2014-11-25 12:28:46 +01:00
ehci_ctl . u64 = cvmx_read_csr ( CVMX_UCTLX_EHCI_CTL ( 0 ) ) ;
/* Use 64-bit addressing. */
ehci_ctl . s . ehci_64b_addr_en = 1 ;
ehci_ctl . s . l2c_addr_msb = 0 ;
2015-03-30 17:00:57 +01:00
# ifdef __BIG_ENDIAN
2014-11-25 12:28:46 +01:00
ehci_ctl . s . l2c_buff_emod = 1 ; /* Byte swapped. */
ehci_ctl . s . l2c_desc_emod = 1 ; /* Byte swapped. */
2015-03-30 17:00:57 +01:00
# else
ehci_ctl . s . l2c_buff_emod = 0 ; /* not swapped. */
ehci_ctl . s . l2c_desc_emod = 0 ; /* not swapped. */
ehci_ctl . s . inv_reg_a2 = 1 ;
# endif
2014-11-25 12:28:46 +01:00
cvmx_write_csr ( CVMX_UCTLX_EHCI_CTL ( 0 ) , ehci_ctl . u64 ) ;
octeon2_usb_clocks_stop ( ) ;
}
2010-10-08 14:47:53 -07:00
static int __init octeon_ehci_device_init ( void )
{
struct platform_device * pd ;
2015-01-06 13:48:56 +01:00
struct device_node * ehci_node ;
2010-10-08 14:47:53 -07:00
int ret = 0 ;
2015-01-06 13:48:56 +01:00
ehci_node = of_find_node_by_name ( NULL , " ehci " ) ;
if ( ! ehci_node )
2010-10-08 14:47:53 -07:00
return 0 ;
2015-01-06 13:48:56 +01:00
pd = of_find_device_by_node ( ehci_node ) ;
if ( ! pd )
return 0 ;
2010-10-08 14:47:53 -07:00
2014-11-25 12:28:46 +01:00
pd - > dev . platform_data = & octeon_ehci_pdata ;
2015-01-06 13:48:56 +01:00
octeon_ehci_hw_start ( & pd - > dev ) ;
2010-10-08 14:47:53 -07:00
return ret ;
}
device_initcall ( octeon_ehci_device_init ) ;
2014-11-25 12:28:46 +01:00
static int octeon_ohci_power_on ( struct platform_device * pdev )
{
2015-01-06 13:48:56 +01:00
octeon2_usb_clocks_start ( & pdev - > dev ) ;
2014-11-25 12:28:46 +01:00
return 0 ;
}
static void octeon_ohci_power_off ( struct platform_device * pdev )
{
octeon2_usb_clocks_stop ( ) ;
}
static struct usb_ohci_pdata octeon_ohci_pdata = {
/* Octeon OHCI matches CPU endianness. */
# ifdef __BIG_ENDIAN
. big_endian_mmio = 1 ,
# endif
. power_on = octeon_ohci_power_on ,
. power_off = octeon_ohci_power_off ,
} ;
2015-01-06 13:48:56 +01:00
static void __init octeon_ohci_hw_start ( struct device * dev )
2014-11-25 12:28:46 +01:00
{
union cvmx_uctlx_ohci_ctl ohci_ctl ;
2015-01-06 13:48:56 +01:00
octeon2_usb_clocks_start ( dev ) ;
2014-11-25 12:28:46 +01:00
ohci_ctl . u64 = cvmx_read_csr ( CVMX_UCTLX_OHCI_CTL ( 0 ) ) ;
ohci_ctl . s . l2c_addr_msb = 0 ;
2015-03-30 17:00:57 +01:00
# ifdef __BIG_ENDIAN
2014-11-25 12:28:46 +01:00
ohci_ctl . s . l2c_buff_emod = 1 ; /* Byte swapped. */
ohci_ctl . s . l2c_desc_emod = 1 ; /* Byte swapped. */
2015-03-30 17:00:57 +01:00
# else
ohci_ctl . s . l2c_buff_emod = 0 ; /* not swapped. */
ohci_ctl . s . l2c_desc_emod = 0 ; /* not swapped. */
ohci_ctl . s . inv_reg_a2 = 1 ;
# endif
2014-11-25 12:28:46 +01:00
cvmx_write_csr ( CVMX_UCTLX_OHCI_CTL ( 0 ) , ohci_ctl . u64 ) ;
octeon2_usb_clocks_stop ( ) ;
}
2010-10-08 14:47:53 -07:00
static int __init octeon_ohci_device_init ( void )
{
struct platform_device * pd ;
2015-01-06 13:48:56 +01:00
struct device_node * ohci_node ;
2010-10-08 14:47:53 -07:00
int ret = 0 ;
2015-01-06 13:48:56 +01:00
ohci_node = of_find_node_by_name ( NULL , " ohci " ) ;
if ( ! ohci_node )
2010-10-08 14:47:53 -07:00
return 0 ;
2015-01-06 13:48:56 +01:00
pd = of_find_device_by_node ( ohci_node ) ;
if ( ! pd )
return 0 ;
2010-10-08 14:47:53 -07:00
2014-11-25 12:28:46 +01:00
pd - > dev . platform_data = & octeon_ohci_pdata ;
2015-01-06 13:48:56 +01:00
octeon_ohci_hw_start ( & pd - > dev ) ;
2010-10-08 14:47:53 -07:00
return ret ;
}
device_initcall ( octeon_ohci_device_init ) ;
# endif /* CONFIG_USB */
2015-01-06 13:48:56 +01:00
2012-07-05 18:12:38 +02:00
static struct of_device_id __initdata octeon_ids [ ] = {
{ . compatible = " simple-bus " , } ,
{ . compatible = " cavium,octeon-6335-uctl " , } ,
2013-12-03 11:46:51 -08:00
{ . compatible = " cavium,octeon-5750-usbn " , } ,
2012-07-05 18:12:38 +02:00
{ . compatible = " cavium,octeon-3860-bootbus " , } ,
{ . compatible = " cavium,mdio-mux " , } ,
{ . compatible = " gpio-leds " , } ,
{ } ,
} ;
static bool __init octeon_has_88e1145 ( void )
{
return ! OCTEON_IS_MODEL ( OCTEON_CN52XX ) & &
! OCTEON_IS_MODEL ( OCTEON_CN6XXX ) & &
! OCTEON_IS_MODEL ( OCTEON_CN56XX ) ;
}
static void __init octeon_fdt_set_phy ( int eth , int phy_addr )
{
const __be32 * phy_handle ;
const __be32 * alt_phy_handle ;
const __be32 * reg ;
u32 phandle ;
int phy ;
int alt_phy ;
const char * p ;
int current_len ;
char new_name [ 20 ] ;
phy_handle = fdt_getprop ( initial_boot_params , eth , " phy-handle " , NULL ) ;
if ( ! phy_handle )
return ;
phandle = be32_to_cpup ( phy_handle ) ;
phy = fdt_node_offset_by_phandle ( initial_boot_params , phandle ) ;
alt_phy_handle = fdt_getprop ( initial_boot_params , eth , " cavium,alt-phy-handle " , NULL ) ;
if ( alt_phy_handle ) {
u32 alt_phandle = be32_to_cpup ( alt_phy_handle ) ;
alt_phy = fdt_node_offset_by_phandle ( initial_boot_params , alt_phandle ) ;
} else {
alt_phy = - 1 ;
}
if ( phy_addr < 0 | | phy < 0 ) {
/* Delete the PHY things */
fdt_nop_property ( initial_boot_params , eth , " phy-handle " ) ;
/* This one may fail */
fdt_nop_property ( initial_boot_params , eth , " cavium,alt-phy-handle " ) ;
if ( phy > = 0 )
fdt_nop_node ( initial_boot_params , phy ) ;
if ( alt_phy > = 0 )
fdt_nop_node ( initial_boot_params , alt_phy ) ;
return ;
}
if ( phy_addr > = 256 & & alt_phy > 0 ) {
const struct fdt_property * phy_prop ;
struct fdt_property * alt_prop ;
u32 phy_handle_name ;
/* Use the alt phy node instead.*/
phy_prop = fdt_get_property ( initial_boot_params , eth , " phy-handle " , NULL ) ;
phy_handle_name = phy_prop - > nameoff ;
fdt_nop_node ( initial_boot_params , phy ) ;
fdt_nop_property ( initial_boot_params , eth , " phy-handle " ) ;
alt_prop = fdt_get_property_w ( initial_boot_params , eth , " cavium,alt-phy-handle " , NULL ) ;
alt_prop - > nameoff = phy_handle_name ;
phy = alt_phy ;
}
phy_addr & = 0xff ;
if ( octeon_has_88e1145 ( ) ) {
fdt_nop_property ( initial_boot_params , phy , " marvell,reg-init " ) ;
memset ( new_name , 0 , sizeof ( new_name ) ) ;
strcpy ( new_name , " marvell,88e1145 " ) ;
p = fdt_getprop ( initial_boot_params , phy , " compatible " ,
& current_len ) ;
if ( p & & current_len > = strlen ( new_name ) )
fdt_setprop_inplace ( initial_boot_params , phy ,
" compatible " , new_name , current_len ) ;
}
reg = fdt_getprop ( initial_boot_params , phy , " reg " , NULL ) ;
if ( phy_addr = = be32_to_cpup ( reg ) )
return ;
fdt_setprop_inplace_cell ( initial_boot_params , phy , " reg " , phy_addr ) ;
snprintf ( new_name , sizeof ( new_name ) , " ethernet-phy@%x " , phy_addr ) ;
p = fdt_get_name ( initial_boot_params , phy , & current_len ) ;
if ( p & & current_len = = strlen ( new_name ) )
fdt_set_name ( initial_boot_params , phy , new_name ) ;
else
pr_err ( " Error: could not rename ethernet phy: <%s> " , p ) ;
}
static void __init octeon_fdt_set_mac_addr ( int n , u64 * pmac )
{
u8 new_mac [ 6 ] ;
u64 mac = * pmac ;
int r ;
new_mac [ 0 ] = ( mac > > 40 ) & 0xff ;
new_mac [ 1 ] = ( mac > > 32 ) & 0xff ;
new_mac [ 2 ] = ( mac > > 24 ) & 0xff ;
new_mac [ 3 ] = ( mac > > 16 ) & 0xff ;
new_mac [ 4 ] = ( mac > > 8 ) & 0xff ;
new_mac [ 5 ] = mac & 0xff ;
r = fdt_setprop_inplace ( initial_boot_params , n , " local-mac-address " ,
new_mac , sizeof ( new_mac ) ) ;
if ( r ) {
pr_err ( " Setting \" local-mac-address \" failed %d " , r ) ;
return ;
}
* pmac = mac + 1 ;
}
static void __init octeon_fdt_rm_ethernet ( int node )
{
const __be32 * phy_handle ;
phy_handle = fdt_getprop ( initial_boot_params , node , " phy-handle " , NULL ) ;
if ( phy_handle ) {
u32 ph = be32_to_cpup ( phy_handle ) ;
int p = fdt_node_offset_by_phandle ( initial_boot_params , ph ) ;
if ( p > = 0 )
fdt_nop_node ( initial_boot_params , p ) ;
}
fdt_nop_node ( initial_boot_params , node ) ;
}
static void __init octeon_fdt_pip_port ( int iface , int i , int p , int max , u64 * pmac )
{
char name_buffer [ 20 ] ;
int eth ;
int phy_addr ;
int ipd_port ;
snprintf ( name_buffer , sizeof ( name_buffer ) , " ethernet@%x " , p ) ;
eth = fdt_subnode_offset ( initial_boot_params , iface , name_buffer ) ;
if ( eth < 0 )
return ;
if ( p > max ) {
pr_debug ( " Deleting port %x:%x \n " , i , p ) ;
octeon_fdt_rm_ethernet ( eth ) ;
return ;
}
if ( OCTEON_IS_MODEL ( OCTEON_CN68XX ) )
ipd_port = ( 0x100 * i ) + ( 0x10 * p ) + 0x800 ;
else
ipd_port = 16 * i + p ;
phy_addr = cvmx_helper_board_get_mii_address ( ipd_port ) ;
octeon_fdt_set_phy ( eth , phy_addr ) ;
octeon_fdt_set_mac_addr ( eth , pmac ) ;
}
static void __init octeon_fdt_pip_iface ( int pip , int idx , u64 * pmac )
{
char name_buffer [ 20 ] ;
int iface ;
int p ;
2013-07-11 21:08:09 +00:00
int count = 0 ;
2012-07-05 18:12:38 +02:00
snprintf ( name_buffer , sizeof ( name_buffer ) , " interface@%d " , idx ) ;
iface = fdt_subnode_offset ( initial_boot_params , pip , name_buffer ) ;
if ( iface < 0 )
return ;
2013-11-01 17:06:04 +02:00
if ( cvmx_helper_interface_enumerate ( idx ) = = 0 )
count = cvmx_helper_ports_on_interface ( idx ) ;
2012-07-05 18:12:38 +02:00
for ( p = 0 ; p < 16 ; p + + )
octeon_fdt_pip_port ( iface , idx , p , count - 1 , pmac ) ;
}
int __init octeon_prune_device_tree ( void )
{
int i , max_port , uart_mask ;
const char * pip_path ;
const char * alias_prop ;
char name_buffer [ 20 ] ;
int aliases ;
u64 mac_addr_base ;
if ( fdt_check_header ( initial_boot_params ) )
panic ( " Corrupt Device Tree. " ) ;
aliases = fdt_path_offset ( initial_boot_params , " /aliases " ) ;
if ( aliases < 0 ) {
pr_err ( " Error: No /aliases node in device tree. " ) ;
return - EINVAL ;
}
mac_addr_base =
( ( octeon_bootinfo - > mac_addr_base [ 0 ] & 0xffull ) ) < < 40 |
( ( octeon_bootinfo - > mac_addr_base [ 1 ] & 0xffull ) ) < < 32 |
( ( octeon_bootinfo - > mac_addr_base [ 2 ] & 0xffull ) ) < < 24 |
( ( octeon_bootinfo - > mac_addr_base [ 3 ] & 0xffull ) ) < < 16 |
( ( octeon_bootinfo - > mac_addr_base [ 4 ] & 0xffull ) ) < < 8 |
( octeon_bootinfo - > mac_addr_base [ 5 ] & 0xffull ) ;
if ( OCTEON_IS_MODEL ( OCTEON_CN52XX ) | | OCTEON_IS_MODEL ( OCTEON_CN63XX ) )
max_port = 2 ;
else if ( OCTEON_IS_MODEL ( OCTEON_CN56XX ) | | OCTEON_IS_MODEL ( OCTEON_CN68XX ) )
max_port = 1 ;
else
max_port = 0 ;
if ( octeon_bootinfo - > board_type = = CVMX_BOARD_TYPE_NIC10E )
max_port = 0 ;
for ( i = 0 ; i < 2 ; i + + ) {
int mgmt ;
snprintf ( name_buffer , sizeof ( name_buffer ) ,
" mix%d " , i ) ;
alias_prop = fdt_getprop ( initial_boot_params , aliases ,
name_buffer , NULL ) ;
if ( alias_prop ) {
mgmt = fdt_path_offset ( initial_boot_params , alias_prop ) ;
if ( mgmt < 0 )
continue ;
if ( i > = max_port ) {
pr_debug ( " Deleting mix%d \n " , i ) ;
octeon_fdt_rm_ethernet ( mgmt ) ;
fdt_nop_property ( initial_boot_params , aliases ,
name_buffer ) ;
} else {
int phy_addr = cvmx_helper_board_get_mii_address ( CVMX_HELPER_BOARD_MGMT_IPD_PORT + i ) ;
octeon_fdt_set_phy ( mgmt , phy_addr ) ;
octeon_fdt_set_mac_addr ( mgmt , & mac_addr_base ) ;
}
}
}
pip_path = fdt_getprop ( initial_boot_params , aliases , " pip " , NULL ) ;
if ( pip_path ) {
int pip = fdt_path_offset ( initial_boot_params , pip_path ) ;
2013-01-22 12:59:30 +01:00
if ( pip > = 0 )
2012-07-05 18:12:38 +02:00
for ( i = 0 ; i < = 4 ; i + + )
octeon_fdt_pip_iface ( pip , i , & mac_addr_base ) ;
}
/* I2C */
if ( OCTEON_IS_MODEL ( OCTEON_CN52XX ) | |
OCTEON_IS_MODEL ( OCTEON_CN63XX ) | |
OCTEON_IS_MODEL ( OCTEON_CN68XX ) | |
OCTEON_IS_MODEL ( OCTEON_CN56XX ) )
max_port = 2 ;
else
max_port = 1 ;
for ( i = 0 ; i < 2 ; i + + ) {
int i2c ;
snprintf ( name_buffer , sizeof ( name_buffer ) ,
" twsi%d " , i ) ;
alias_prop = fdt_getprop ( initial_boot_params , aliases ,
name_buffer , NULL ) ;
if ( alias_prop ) {
i2c = fdt_path_offset ( initial_boot_params , alias_prop ) ;
if ( i2c < 0 )
continue ;
if ( i > = max_port ) {
pr_debug ( " Deleting twsi%d \n " , i ) ;
fdt_nop_node ( initial_boot_params , i2c ) ;
fdt_nop_property ( initial_boot_params , aliases ,
name_buffer ) ;
}
}
}
/* SMI/MDIO */
if ( OCTEON_IS_MODEL ( OCTEON_CN68XX ) )
max_port = 4 ;
else if ( OCTEON_IS_MODEL ( OCTEON_CN52XX ) | |
OCTEON_IS_MODEL ( OCTEON_CN63XX ) | |
OCTEON_IS_MODEL ( OCTEON_CN56XX ) )
max_port = 2 ;
else
max_port = 1 ;
for ( i = 0 ; i < 2 ; i + + ) {
int i2c ;
snprintf ( name_buffer , sizeof ( name_buffer ) ,
" smi%d " , i ) ;
alias_prop = fdt_getprop ( initial_boot_params , aliases ,
name_buffer , NULL ) ;
if ( alias_prop ) {
i2c = fdt_path_offset ( initial_boot_params , alias_prop ) ;
if ( i2c < 0 )
continue ;
if ( i > = max_port ) {
pr_debug ( " Deleting smi%d \n " , i ) ;
fdt_nop_node ( initial_boot_params , i2c ) ;
fdt_nop_property ( initial_boot_params , aliases ,
name_buffer ) ;
}
}
}
/* Serial */
uart_mask = 3 ;
/* Right now CN52XX is the only chip with a third uart */
if ( OCTEON_IS_MODEL ( OCTEON_CN52XX ) )
uart_mask | = 4 ; /* uart2 */
for ( i = 0 ; i < 3 ; i + + ) {
int uart ;
snprintf ( name_buffer , sizeof ( name_buffer ) ,
" uart%d " , i ) ;
alias_prop = fdt_getprop ( initial_boot_params , aliases ,
name_buffer , NULL ) ;
if ( alias_prop ) {
uart = fdt_path_offset ( initial_boot_params , alias_prop ) ;
2013-06-19 20:37:26 +00:00
if ( uart_mask & ( 1 < < i ) ) {
__be32 f ;
f = cpu_to_be32 ( octeon_get_io_clock_rate ( ) ) ;
fdt_setprop_inplace ( initial_boot_params ,
uart , " clock-frequency " ,
& f , sizeof ( f ) ) ;
2012-07-05 18:12:38 +02:00
continue ;
2013-06-19 20:37:26 +00:00
}
2012-07-05 18:12:38 +02:00
pr_debug ( " Deleting uart%d \n " , i ) ;
fdt_nop_node ( initial_boot_params , uart ) ;
fdt_nop_property ( initial_boot_params , aliases ,
name_buffer ) ;
}
}
/* Compact Flash */
alias_prop = fdt_getprop ( initial_boot_params , aliases ,
" cf0 " , NULL ) ;
if ( alias_prop ) {
union cvmx_mio_boot_reg_cfgx mio_boot_reg_cfg ;
unsigned long base_ptr , region_base , region_size ;
unsigned long region1_base = 0 ;
unsigned long region1_size = 0 ;
int cs , bootbus ;
bool is_16bit = false ;
bool is_true_ide = false ;
__be32 new_reg [ 6 ] ;
__be32 * ranges ;
int len ;
int cf = fdt_path_offset ( initial_boot_params , alias_prop ) ;
base_ptr = 0 ;
if ( octeon_bootinfo - > major_version = = 1
& & octeon_bootinfo - > minor_version > = 1 ) {
if ( octeon_bootinfo - > compact_flash_common_base_addr )
base_ptr = octeon_bootinfo - > compact_flash_common_base_addr ;
} else {
base_ptr = 0x1d000800 ;
}
if ( ! base_ptr )
goto no_cf ;
/* Find CS0 region. */
for ( cs = 0 ; cs < 8 ; cs + + ) {
mio_boot_reg_cfg . u64 = cvmx_read_csr ( CVMX_MIO_BOOT_REG_CFGX ( cs ) ) ;
region_base = mio_boot_reg_cfg . s . base < < 16 ;
region_size = ( mio_boot_reg_cfg . s . size + 1 ) < < 16 ;
if ( mio_boot_reg_cfg . s . en & & base_ptr > = region_base
& & base_ptr < region_base + region_size ) {
is_16bit = mio_boot_reg_cfg . s . width ;
break ;
}
}
if ( cs > = 7 ) {
/* cs and cs + 1 are CS0 and CS1, both must be less than 8. */
goto no_cf ;
}
if ( ! ( base_ptr & 0xfffful ) ) {
/*
* Boot loader signals availability of DMA ( true_ide
* mode ) by setting low order bits of base_ptr to
* zero .
*/
/* Asume that CS1 immediately follows. */
mio_boot_reg_cfg . u64 =
cvmx_read_csr ( CVMX_MIO_BOOT_REG_CFGX ( cs + 1 ) ) ;
region1_base = mio_boot_reg_cfg . s . base < < 16 ;
region1_size = ( mio_boot_reg_cfg . s . size + 1 ) < < 16 ;
if ( ! mio_boot_reg_cfg . s . en )
goto no_cf ;
is_true_ide = true ;
} else {
fdt_nop_property ( initial_boot_params , cf , " cavium,true-ide " ) ;
fdt_nop_property ( initial_boot_params , cf , " cavium,dma-engine-handle " ) ;
if ( ! is_16bit ) {
__be32 width = cpu_to_be32 ( 8 ) ;
fdt_setprop_inplace ( initial_boot_params , cf ,
" cavium,bus-width " , & width , sizeof ( width ) ) ;
}
}
new_reg [ 0 ] = cpu_to_be32 ( cs ) ;
new_reg [ 1 ] = cpu_to_be32 ( 0 ) ;
new_reg [ 2 ] = cpu_to_be32 ( 0x10000 ) ;
new_reg [ 3 ] = cpu_to_be32 ( cs + 1 ) ;
new_reg [ 4 ] = cpu_to_be32 ( 0 ) ;
new_reg [ 5 ] = cpu_to_be32 ( 0x10000 ) ;
fdt_setprop_inplace ( initial_boot_params , cf ,
" reg " , new_reg , sizeof ( new_reg ) ) ;
bootbus = fdt_parent_offset ( initial_boot_params , cf ) ;
if ( bootbus < 0 )
goto no_cf ;
ranges = fdt_getprop_w ( initial_boot_params , bootbus , " ranges " , & len ) ;
if ( ! ranges | | len < ( 5 * 8 * sizeof ( __be32 ) ) )
goto no_cf ;
ranges [ ( cs * 5 ) + 2 ] = cpu_to_be32 ( region_base > > 32 ) ;
ranges [ ( cs * 5 ) + 3 ] = cpu_to_be32 ( region_base & 0xffffffff ) ;
ranges [ ( cs * 5 ) + 4 ] = cpu_to_be32 ( region_size ) ;
if ( is_true_ide ) {
cs + + ;
ranges [ ( cs * 5 ) + 2 ] = cpu_to_be32 ( region1_base > > 32 ) ;
ranges [ ( cs * 5 ) + 3 ] = cpu_to_be32 ( region1_base & 0xffffffff ) ;
ranges [ ( cs * 5 ) + 4 ] = cpu_to_be32 ( region1_size ) ;
}
goto end_cf ;
no_cf :
fdt_nop_node ( initial_boot_params , cf ) ;
end_cf :
;
}
/* 8 char LED */
alias_prop = fdt_getprop ( initial_boot_params , aliases ,
" led0 " , NULL ) ;
if ( alias_prop ) {
union cvmx_mio_boot_reg_cfgx mio_boot_reg_cfg ;
unsigned long base_ptr , region_base , region_size ;
int cs , bootbus ;
__be32 new_reg [ 6 ] ;
__be32 * ranges ;
int len ;
int led = fdt_path_offset ( initial_boot_params , alias_prop ) ;
base_ptr = octeon_bootinfo - > led_display_base_addr ;
if ( base_ptr = = 0 )
goto no_led ;
/* Find CS0 region. */
for ( cs = 0 ; cs < 8 ; cs + + ) {
mio_boot_reg_cfg . u64 = cvmx_read_csr ( CVMX_MIO_BOOT_REG_CFGX ( cs ) ) ;
region_base = mio_boot_reg_cfg . s . base < < 16 ;
region_size = ( mio_boot_reg_cfg . s . size + 1 ) < < 16 ;
if ( mio_boot_reg_cfg . s . en & & base_ptr > = region_base
& & base_ptr < region_base + region_size )
break ;
}
if ( cs > 7 )
goto no_led ;
new_reg [ 0 ] = cpu_to_be32 ( cs ) ;
new_reg [ 1 ] = cpu_to_be32 ( 0x20 ) ;
new_reg [ 2 ] = cpu_to_be32 ( 0x20 ) ;
new_reg [ 3 ] = cpu_to_be32 ( cs ) ;
new_reg [ 4 ] = cpu_to_be32 ( 0 ) ;
new_reg [ 5 ] = cpu_to_be32 ( 0x20 ) ;
fdt_setprop_inplace ( initial_boot_params , led ,
" reg " , new_reg , sizeof ( new_reg ) ) ;
bootbus = fdt_parent_offset ( initial_boot_params , led ) ;
if ( bootbus < 0 )
goto no_led ;
ranges = fdt_getprop_w ( initial_boot_params , bootbus , " ranges " , & len ) ;
if ( ! ranges | | len < ( 5 * 8 * sizeof ( __be32 ) ) )
goto no_led ;
ranges [ ( cs * 5 ) + 2 ] = cpu_to_be32 ( region_base > > 32 ) ;
ranges [ ( cs * 5 ) + 3 ] = cpu_to_be32 ( region_base & 0xffffffff ) ;
ranges [ ( cs * 5 ) + 4 ] = cpu_to_be32 ( region_size ) ;
goto end_led ;
no_led :
fdt_nop_node ( initial_boot_params , led ) ;
end_led :
;
}
/* OHCI/UHCI USB */
alias_prop = fdt_getprop ( initial_boot_params , aliases ,
" uctl " , NULL ) ;
if ( alias_prop ) {
int uctl = fdt_path_offset ( initial_boot_params , alias_prop ) ;
if ( uctl > = 0 & & ( ! OCTEON_IS_MODEL ( OCTEON_CN6XXX ) | |
octeon_bootinfo - > board_type = = CVMX_BOARD_TYPE_NIC2E ) ) {
pr_debug ( " Deleting uctl \n " ) ;
fdt_nop_node ( initial_boot_params , uctl ) ;
fdt_nop_property ( initial_boot_params , aliases , " uctl " ) ;
} else if ( octeon_bootinfo - > board_type = = CVMX_BOARD_TYPE_NIC10E | |
octeon_bootinfo - > board_type = = CVMX_BOARD_TYPE_NIC4E ) {
/* Missing "refclk-type" defaults to crystal. */
fdt_nop_property ( initial_boot_params , uctl , " refclk-type " ) ;
}
}
2013-12-03 11:46:51 -08:00
/* DWC2 USB */
alias_prop = fdt_getprop ( initial_boot_params , aliases ,
" usbn " , NULL ) ;
if ( alias_prop ) {
int usbn = fdt_path_offset ( initial_boot_params , alias_prop ) ;
if ( usbn > = 0 & & ( current_cpu_type ( ) = = CPU_CAVIUM_OCTEON2 | |
! octeon_has_feature ( OCTEON_FEATURE_USB ) ) ) {
pr_debug ( " Deleting usbn \n " ) ;
fdt_nop_node ( initial_boot_params , usbn ) ;
fdt_nop_property ( initial_boot_params , aliases , " usbn " ) ;
} else {
__be32 new_f [ 1 ] ;
enum cvmx_helper_board_usb_clock_types c ;
c = __cvmx_helper_board_usb_get_clock_type ( ) ;
switch ( c ) {
case USB_CLOCK_TYPE_REF_48 :
new_f [ 0 ] = cpu_to_be32 ( 48000000 ) ;
fdt_setprop_inplace ( initial_boot_params , usbn ,
" refclk-frequency " , new_f , sizeof ( new_f ) ) ;
/* Fall through ...*/
case USB_CLOCK_TYPE_REF_12 :
/* Missing "refclk-type" defaults to external. */
fdt_nop_property ( initial_boot_params , usbn , " refclk-type " ) ;
break ;
default :
break ;
}
}
}
2015-03-30 00:04:56 +03:00
if ( octeon_bootinfo - > board_type ! = CVMX_BOARD_TYPE_CUST_DSR1000N ) {
int dsr1000n_leds = fdt_path_offset ( initial_boot_params ,
" /dsr1000n-leds " ) ;
if ( dsr1000n_leds > = 0 )
fdt_nop_node ( initial_boot_params , dsr1000n_leds ) ;
}
2012-07-05 18:12:38 +02:00
return 0 ;
}
static int __init octeon_publish_devices ( void )
{
return of_platform_bus_probe ( NULL , octeon_ids , NULL ) ;
}
device_initcall ( octeon_publish_devices ) ;
2009-09-16 14:54:18 -07:00
MODULE_AUTHOR ( " David Daney <ddaney@caviumnetworks.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Platform driver for Octeon SOC " ) ;