diff options
Diffstat (limited to 'arch/mips/cavium-octeon/octeon-platform.c')
| -rw-r--r-- | arch/mips/cavium-octeon/octeon-platform.c | 703 |
1 files changed, 575 insertions, 128 deletions
diff --git a/arch/mips/cavium-octeon/octeon-platform.c b/arch/mips/cavium-octeon/octeon-platform.c index 7b746e7bf7a1..47677b5d7ed0 100644 --- a/arch/mips/cavium-octeon/octeon-platform.c +++ b/arch/mips/cavium-octeon/octeon-platform.c @@ -3,154 +3,433 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2004-2011 Cavium Networks + * Copyright (C) 2004-2017 Cavium, Inc. * Copyright (C) 2008 Wind River Systems */ -#include <linux/init.h> -#include <linux/irq.h> -#include <linux/i2c.h> -#include <linux/usb.h> -#include <linux/dma-mapping.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/platform_device.h> +#include <linux/etherdevice.h> +#include <linux/of.h> #include <linux/of_platform.h> #include <linux/of_fdt.h> +#include <linux/platform_device.h> #include <linux/libfdt.h> +#include <linux/string.h> #include <asm/octeon/octeon.h> -#include <asm/octeon/cvmx-rnm-defs.h> -#include <asm/octeon/cvmx-helper.h> #include <asm/octeon/cvmx-helper-board.h> -/* Octeon Random Number Generator. */ -static int __init octeon_rng_device_init(void) +#ifdef CONFIG_USB +#include <linux/usb/ehci_def.h> +#include <linux/usb/ehci_pdriver.h> +#include <linux/usb/ohci_pdriver.h> +#include <asm/octeon/cvmx-uctlx-defs.h> + +#define CVMX_UAHCX_EHCI_USBCMD (CVMX_ADD_IO_SEG(0x00016F0000000010ull)) +#define CVMX_UAHCX_OHCI_USBCMD (CVMX_ADD_IO_SEG(0x00016F0000000408ull)) + +static DEFINE_MUTEX(octeon2_usb_clocks_mutex); + +static int octeon2_usb_clock_start_cnt; + +static int __init octeon2_usb_reset(void) { - struct platform_device *pd; - int ret = 0; + union cvmx_uctlx_clk_rst_ctl clk_rst_ctl; + u32 ucmd; - 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 + if (!OCTEON_IS_OCTEON2()) + return 0; + + clk_rst_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_CLK_RST_CTL(0)); + if (clk_rst_ctl.s.hrst) { + ucmd = cvmx_read64_uint32(CVMX_UAHCX_EHCI_USBCMD); + ucmd &= ~CMD_RUN; + cvmx_write64_uint32(CVMX_UAHCX_EHCI_USBCMD, ucmd); + mdelay(2); + ucmd |= CMD_RESET; + cvmx_write64_uint32(CVMX_UAHCX_EHCI_USBCMD, ucmd); + ucmd = cvmx_read64_uint32(CVMX_UAHCX_OHCI_USBCMD); + ucmd |= CMD_RUN; + cvmx_write64_uint32(CVMX_UAHCX_OHCI_USBCMD, ucmd); + } + + return 0; +} +arch_initcall(octeon2_usb_reset); + +static void octeon2_usb_clocks_start(struct device *dev) +{ + u64 div; + union cvmx_uctlx_if_ena if_ena; + union cvmx_uctlx_clk_rst_ctl clk_rst_ctl; + union cvmx_uctlx_uphy_portx_ctl_status port_ctl_status; + int i; + unsigned long io_clk_64_to_ns; + u32 clock_rate = 12000000; + bool is_crystal_clock = false; + + + 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(); + + 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"); + of_node_put(uctl_node); + goto exit; + } + i = of_property_read_string(uctl_node, + "refclk-type", &clock_type); + of_node_put(uctl_node); + if (!i && strcmp("crystal", clock_type) == 0) + is_crystal_clock = true; + } - pd = platform_device_alloc("octeon_rng", -1); - if (!pd) { - ret = -ENOMEM; - goto out; + /* + * 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); + + 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); } - ret = platform_device_add_resources(pd, rng_resources, - ARRAY_SIZE(rng_resources)); - if (ret) - goto fail; + /* 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 */ + 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); + fallthrough; + 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; + } + 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 3 ms for the PHY clock to start. */ + mdelay(3); + + /* Steps 6..9 for ATE only, are skipped. */ + + /* 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 11b */ + udelay(1); + + /* Step 11c */ + clk_rst_ctl.s.p_prst = 0; + cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); + + /* Step 11d */ + mdelay(1); + + /* Step 11e */ + 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: + /* Set uSOF cycle period to 60,000 bits. */ + cvmx_write_csr(CVMX_UCTLX_EHCI_FLA(0), 0x20ull); + +exit: + mutex_unlock(&octeon2_usb_clocks_mutex); +} - ret = platform_device_add(pd); - if (ret) - goto fail; +static void octeon2_usb_clocks_stop(void) +{ + mutex_lock(&octeon2_usb_clocks_mutex); + octeon2_usb_clock_start_cnt--; + mutex_unlock(&octeon2_usb_clocks_mutex); +} - return ret; -fail: - platform_device_put(pd); +static int octeon_ehci_power_on(struct platform_device *pdev) +{ + octeon2_usb_clocks_start(&pdev->dev); + return 0; +} -out: - return ret; +static void octeon_ehci_power_off(struct platform_device *pdev) +{ + octeon2_usb_clocks_stop(); } -device_initcall(octeon_rng_device_init); -#ifdef CONFIG_USB +static struct usb_ehci_pdata octeon_ehci_pdata = { + /* Octeon EHCI matches CPU endianness. */ +#ifdef __BIG_ENDIAN + .big_endian_mmio = 1, +#endif + /* + * We can DMA from anywhere. But the descriptors must be in + * the lower 4GB. + */ + .dma_mask_64 = 0, + .power_on = octeon_ehci_power_on, + .power_off = octeon_ehci_power_off, +}; + +static void __init octeon_ehci_hw_start(struct device *dev) +{ + union cvmx_uctlx_ehci_ctl ehci_ctl; + + octeon2_usb_clocks_start(dev); + + 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; +#ifdef __BIG_ENDIAN + ehci_ctl.s.l2c_buff_emod = 1; /* Byte swapped. */ + ehci_ctl.s.l2c_desc_emod = 1; /* Byte swapped. */ +#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 + cvmx_write_csr(CVMX_UCTLX_EHCI_CTL(0), ehci_ctl.u64); + + octeon2_usb_clocks_stop(); +} static int __init octeon_ehci_device_init(void) { struct platform_device *pd; + struct device_node *ehci_node; int ret = 0; - struct resource usb_resources[] = { - { - .flags = IORESOURCE_MEM, - }, { - .flags = IORESOURCE_IRQ, - } - }; + ehci_node = of_find_node_by_name(NULL, "ehci"); + if (!ehci_node) + return 0; - /* Only Octeon2 has ehci/ohci */ - if (!OCTEON_IS_MODEL(OCTEON_CN63XX)) + pd = of_find_device_by_node(ehci_node); + of_node_put(ehci_node); + if (!pd) return 0; - if (octeon_is_simulation() || usb_disabled()) - return 0; /* No USB in the simulator. */ + pd->dev.platform_data = &octeon_ehci_pdata; + octeon_ehci_hw_start(&pd->dev); + put_device(&pd->dev); - pd = platform_device_alloc("octeon-ehci", 0); - if (!pd) { - ret = -ENOMEM; - goto out; - } + return ret; +} +device_initcall(octeon_ehci_device_init); - usb_resources[0].start = 0x00016F0000000000ULL; - usb_resources[0].end = usb_resources[0].start + 0x100; +static int octeon_ohci_power_on(struct platform_device *pdev) +{ + octeon2_usb_clocks_start(&pdev->dev); + return 0; +} - usb_resources[1].start = OCTEON_IRQ_USB0; - usb_resources[1].end = OCTEON_IRQ_USB0; +static void octeon_ohci_power_off(struct platform_device *pdev) +{ + octeon2_usb_clocks_stop(); +} - ret = platform_device_add_resources(pd, usb_resources, - ARRAY_SIZE(usb_resources)); - if (ret) - goto fail; +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, +}; - ret = platform_device_add(pd); - if (ret) - goto fail; +static void __init octeon_ohci_hw_start(struct device *dev) +{ + union cvmx_uctlx_ohci_ctl ohci_ctl; + + octeon2_usb_clocks_start(dev); + + ohci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_OHCI_CTL(0)); + ohci_ctl.s.l2c_addr_msb = 0; +#ifdef __BIG_ENDIAN + ohci_ctl.s.l2c_buff_emod = 1; /* Byte swapped. */ + ohci_ctl.s.l2c_desc_emod = 1; /* Byte swapped. */ +#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 + cvmx_write_csr(CVMX_UCTLX_OHCI_CTL(0), ohci_ctl.u64); + + octeon2_usb_clocks_stop(); +} + +static int __init octeon_ohci_device_init(void) +{ + struct platform_device *pd; + struct device_node *ohci_node; + int ret = 0; + + ohci_node = of_find_node_by_name(NULL, "ohci"); + if (!ohci_node) + return 0; + + pd = of_find_device_by_node(ohci_node); + of_node_put(ohci_node); + if (!pd) + return 0; + + pd->dev.platform_data = &octeon_ohci_pdata; + octeon_ohci_hw_start(&pd->dev); + put_device(&pd->dev); - return ret; -fail: - platform_device_put(pd); -out: return ret; } -device_initcall(octeon_ehci_device_init); +device_initcall(octeon_ohci_device_init); -static int __init octeon_ohci_device_init(void) +#endif /* CONFIG_USB */ + +/* Octeon Random Number Generator. */ +static int __init octeon_rng_device_init(void) { struct platform_device *pd; int ret = 0; - struct resource usb_resources[] = { + 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_IRQ, + .flags = IORESOURCE_MEM, + .start = cvmx_build_io_address(8, 0), + .end = cvmx_build_io_address(8, 0) + 0x7 } }; - /* Only Octeon2 has ehci/ohci */ - if (!OCTEON_IS_MODEL(OCTEON_CN63XX)) - return 0; - - if (octeon_is_simulation() || usb_disabled()) - return 0; /* No USB in the simulator. */ - - pd = platform_device_alloc("octeon-ohci", 0); + pd = platform_device_alloc("octeon_rng", -1); if (!pd) { ret = -ENOMEM; goto out; } - usb_resources[0].start = 0x00016F0000000400ULL; - usb_resources[0].end = usb_resources[0].start + 0x100; - - usb_resources[1].start = OCTEON_IRQ_USB0; - usb_resources[1].end = OCTEON_IRQ_USB0; - - ret = platform_device_add_resources(pd, usb_resources, - ARRAY_SIZE(usb_resources)); + ret = platform_device_add_resources(pd, rng_resources, + ARRAY_SIZE(rng_resources)); if (ret) goto fail; @@ -161,16 +440,16 @@ static int __init octeon_ohci_device_init(void) return ret; fail: platform_device_put(pd); + out: return ret; } -device_initcall(octeon_ohci_device_init); - -#endif /* CONFIG_USB */ +device_initcall(octeon_rng_device_init); -static struct of_device_id __initdata octeon_ids[] = { +static const struct of_device_id octeon_ids[] __initconst = { { .compatible = "simple-bus", }, { .compatible = "cavium,octeon-6335-uctl", }, + { .compatible = "cavium,octeon-5750-usbn", }, { .compatible = "cavium,octeon-3860-bootbus", }, { .compatible = "cavium,mdio-mux", }, { .compatible = "gpio-leds", }, @@ -184,6 +463,23 @@ static bool __init octeon_has_88e1145(void) !OCTEON_IS_MODEL(OCTEON_CN56XX); } +static bool __init octeon_has_fixed_link(int ipd_port) +{ + switch (cvmx_sysinfo_get()->board_type) { + case CVMX_BOARD_TYPE_CN3005_EVB_HS5: + case CVMX_BOARD_TYPE_CN3010_EVB_HS5: + case CVMX_BOARD_TYPE_CN3020_EVB_HS5: + case CVMX_BOARD_TYPE_CUST_NB5: + case CVMX_BOARD_TYPE_EBH3100: + /* Port 1 on these boards is always gigabit. */ + return ipd_port == 1; + case CVMX_BOARD_TYPE_BBGW_REF: + /* Ports 0 and 1 connect to the switch. */ + return ipd_port == 0 || ipd_port == 1; + } + return false; +} + static void __init octeon_fdt_set_phy(int eth, int phy_addr) { const __be32 *phy_handle; @@ -206,6 +502,7 @@ static void __init octeon_fdt_set_phy(int eth, int phy_addr) 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; @@ -226,7 +523,7 @@ static void __init octeon_fdt_set_phy(int eth, int phy_addr) if (phy_addr >= 256 && alt_phy > 0) { const struct fdt_property *phy_prop; struct fdt_property *alt_prop; - u32 phy_handle_name; + fdt32_t phy_handle_name; /* Use the alt phy node instead.*/ phy_prop = fdt_get_property(initial_boot_params, eth, "phy-handle", NULL); @@ -242,8 +539,7 @@ static void __init octeon_fdt_set_phy(int eth, int phy_addr) 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"); + strscpy_pad(new_name, "marvell,88e1145"); p = fdt_getprop(initial_boot_params, phy, "compatible", ¤t_len); if (p && current_len >= strlen(new_name)) @@ -268,10 +564,17 @@ static void __init octeon_fdt_set_phy(int eth, int phy_addr) static void __init octeon_fdt_set_mac_addr(int n, u64 *pmac) { + const u8 *old_mac; + int old_len; u8 new_mac[6]; u64 mac = *pmac; int r; + old_mac = fdt_getprop(initial_boot_params, n, "local-mac-address", + &old_len); + if (!old_mac || old_len != 6 || is_valid_ether_addr(old_mac)) + return; + new_mac[0] = (mac >> 40) & 0xff; new_mac[1] = (mac >> 32) & 0xff; new_mac[2] = (mac >> 24) & 0xff; @@ -297,18 +600,59 @@ static void __init octeon_fdt_rm_ethernet(int node) 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) +static void __init _octeon_rx_tx_delay(int eth, int rx_delay, int tx_delay) +{ + fdt_setprop_inplace_cell(initial_boot_params, eth, "rx-delay", + rx_delay); + fdt_setprop_inplace_cell(initial_boot_params, eth, "tx-delay", + tx_delay); +} + +static void __init octeon_rx_tx_delay(int eth, int iface, int port) +{ + switch (cvmx_sysinfo_get()->board_type) { + case CVMX_BOARD_TYPE_CN3005_EVB_HS5: + if (iface == 0) { + if (port == 0) { + /* + * Boards with gigabit WAN ports need a + * different setting that is compatible with + * 100 Mbit settings + */ + _octeon_rx_tx_delay(eth, 0xc, 0x0c); + return; + } else if (port == 1) { + /* Different config for switch port. */ + _octeon_rx_tx_delay(eth, 0x0, 0x0); + return; + } + } + break; + case CVMX_BOARD_TYPE_UBNT_E100: + if (iface == 0 && port <= 2) { + _octeon_rx_tx_delay(eth, 0x0, 0x10); + return; + } + break; + } + fdt_nop_property(initial_boot_params, eth, "rx-delay"); + fdt_nop_property(initial_boot_params, eth, "tx-delay"); +} + +static void __init octeon_fdt_pip_port(int iface, int i, int p, int max) { char name_buffer[20]; int eth; int phy_addr; int ipd_port; + int fixed_link; snprintf(name_buffer, sizeof(name_buffer), "ethernet@%x", p); eth = fdt_subnode_offset(initial_boot_params, iface, name_buffer); @@ -326,25 +670,98 @@ static void __init octeon_fdt_pip_port(int iface, int i, int p, int max, u64 *pm phy_addr = cvmx_helper_board_get_mii_address(ipd_port); octeon_fdt_set_phy(eth, phy_addr); - octeon_fdt_set_mac_addr(eth, pmac); + + fixed_link = fdt_subnode_offset(initial_boot_params, eth, "fixed-link"); + if (fixed_link < 0) + WARN_ON(octeon_has_fixed_link(ipd_port)); + else if (!octeon_has_fixed_link(ipd_port)) + fdt_nop_node(initial_boot_params, fixed_link); + octeon_rx_tx_delay(eth, i, p); } -static void __init octeon_fdt_pip_iface(int pip, int idx, u64 *pmac) +static void __init octeon_fdt_pip_iface(int pip, int idx) { char name_buffer[20]; int iface; int p; - int count; - - count = cvmx_helper_interface_enumerate(idx); + int count = 0; snprintf(name_buffer, sizeof(name_buffer), "interface@%d", idx); iface = fdt_subnode_offset(initial_boot_params, pip, name_buffer); if (iface < 0) return; + if (cvmx_helper_interface_enumerate(idx) == 0) + count = cvmx_helper_ports_on_interface(idx); + for (p = 0; p < 16; p++) - octeon_fdt_pip_port(iface, idx, p, count - 1, pmac); + octeon_fdt_pip_port(iface, idx, p, count - 1); +} + +void __init octeon_fill_mac_addresses(void) +{ + const char *alias_prop; + char name_buffer[20]; + u64 mac_addr_base; + int aliases; + int pip; + int i; + + aliases = fdt_path_offset(initial_boot_params, "/aliases"); + if (aliases < 0) + return; + + 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); + + 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) + continue; + mgmt = fdt_path_offset(initial_boot_params, alias_prop); + if (mgmt < 0) + continue; + octeon_fdt_set_mac_addr(mgmt, &mac_addr_base); + } + + alias_prop = fdt_getprop(initial_boot_params, aliases, "pip", NULL); + if (!alias_prop) + return; + + pip = fdt_path_offset(initial_boot_params, alias_prop); + if (pip < 0) + return; + + for (i = 0; i <= 4; i++) { + int iface; + int p; + + snprintf(name_buffer, sizeof(name_buffer), "interface@%d", i); + iface = fdt_subnode_offset(initial_boot_params, pip, + name_buffer); + if (iface < 0) + continue; + for (p = 0; p < 16; p++) { + int eth; + + snprintf(name_buffer, sizeof(name_buffer), + "ethernet@%x", p); + eth = fdt_subnode_offset(initial_boot_params, iface, + name_buffer); + if (eth < 0) + continue; + octeon_fdt_set_mac_addr(eth, &mac_addr_base); + } + } } int __init octeon_prune_device_tree(void) @@ -354,26 +771,20 @@ int __init octeon_prune_device_tree(void) 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."); + WARN(octeon_bootinfo->board_type == CVMX_BOARD_TYPE_CUST_DSR1000N, + "Built-in DTB booting is deprecated on %s. Please switch to use appended DTB.", + cvmx_board_type_to_string(octeon_bootinfo->board_type)); + 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)) @@ -386,6 +797,7 @@ int __init octeon_prune_device_tree(void) 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, @@ -401,8 +813,8 @@ int __init octeon_prune_device_tree(void) 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); } } } @@ -410,9 +822,10 @@ int __init octeon_prune_device_tree(void) pip_path = fdt_getprop(initial_boot_params, aliases, "pip", NULL); if (pip_path) { int pip = fdt_path_offset(initial_boot_params, pip_path); + if (pip >= 0) for (i = 0; i <= 4; i++) - octeon_fdt_pip_iface(pip, i, &mac_addr_base); + octeon_fdt_pip_iface(pip, i); } /* I2C */ @@ -426,6 +839,7 @@ int __init octeon_prune_device_tree(void) 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, @@ -456,11 +870,11 @@ int __init octeon_prune_device_tree(void) 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) @@ -483,6 +897,7 @@ int __init octeon_prune_device_tree(void) 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, @@ -522,6 +937,7 @@ int __init octeon_prune_device_tree(void) 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) { @@ -557,7 +973,7 @@ int __init octeon_prune_device_tree(void) * zero. */ - /* Asume that CS1 immediately follows. */ + /* Assume 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; @@ -571,6 +987,7 @@ int __init octeon_prune_device_tree(void) 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)); } @@ -663,6 +1080,7 @@ end_led: ; } +#ifdef CONFIG_USB /* OHCI/UHCI USB */ alias_prop = fdt_getprop(initial_boot_params, aliases, "uctl", NULL); @@ -681,15 +1099,44 @@ end_led: } } + /* 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)); + fallthrough; + case USB_CLOCK_TYPE_REF_12: + /* Missing "refclk-type" defaults to external. */ + fdt_nop_property(initial_boot_params, usbn, "refclk-type"); + break; + default: + break; + } + } + } +#endif + return 0; } static int __init octeon_publish_devices(void) { - return of_platform_bus_probe(NULL, octeon_ids, NULL); + return of_platform_populate(NULL, octeon_ids, NULL, NULL); } -device_initcall(octeon_publish_devices); - -MODULE_AUTHOR("David Daney <ddaney@caviumnetworks.com>"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Platform driver for Octeon SOC"); +arch_initcall(octeon_publish_devices); |
