Files
lkl_linux/arch/mips/cavium-octeon/octeon-platform.c
Ladislav Michl 976f82e8aa usb: dwc3: dwc3-octeon: Convert to glue driver
DWC3 as implemented in Cavium SoC is using UCTL bridge unit
between I/O interconnect and USB controller.

Currently there is no bond with dwc3 core code, so if anything goes
wrong in UCTL setup dwc3 is left in reset, which leads to bus error
while trying to read any device register. Thus any failure in UCTL
initialization ends with kernel panic.

To avoid this move Octeon DWC3 glue code from arch/mips and make it
proper glue driver which is used instead of dwc3-of-simple.

Signed-off-by: Ladislav Michl <ladis@linux-mips.org>
Acked-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Link: https://lore.kernel.org/r/ZMd/ReyiY7wS6DvN@lenoch
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-04 14:52:15 +02:00

1141 lines
29 KiB
C

/*
* 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.
*
* Copyright (C) 2004-2017 Cavium, Inc.
* Copyright (C) 2008 Wind River Systems
*/
#include <linux/etherdevice.h>
#include <linux/of_platform.h>
#include <linux/of_fdt.h>
#include <linux/libfdt.h>
#include <asm/octeon/octeon.h>
#include <asm/octeon/cvmx-helper-board.h>
#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)
{
union cvmx_uctlx_clk_rst_ctl clk_rst_ctl;
u32 ucmd;
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;
}
/*
* 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);
}
/* 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);
}
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)
{
octeon2_usb_clocks_start(&pdev->dev);
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
/*
* 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;
ehci_node = of_find_node_by_name(NULL, "ehci");
if (!ehci_node)
return 0;
pd = of_find_device_by_node(ehci_node);
of_node_put(ehci_node);
if (!pd)
return 0;
pd->dev.platform_data = &octeon_ehci_pdata;
octeon_ehci_hw_start(&pd->dev);
put_device(&pd->dev);
return ret;
}
device_initcall(octeon_ehci_device_init);
static int octeon_ohci_power_on(struct platform_device *pdev)
{
octeon2_usb_clocks_start(&pdev->dev);
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,
};
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;
}
device_initcall(octeon_ohci_device_init);
#endif /* CONFIG_USB */
/* 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);
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", },
{},
};
static bool __init octeon_has_88e1145(void)
{
return !OCTEON_IS_MODEL(OCTEON_CN52XX) &&
!OCTEON_IS_MODEL(OCTEON_CN6XXX) &&
!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;
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;
fdt32_t 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)
{
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;
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_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);
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);
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)
{
char name_buffer[20];
int iface;
int p;
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);
}
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)
{
int i, max_port, uart_mask;
const char *pip_path;
const char *alias_prop;
char name_buffer[20];
int aliases;
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;
}
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);
}
}
}
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);
}
/* 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);
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));
continue;
}
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:
;
}
#ifdef CONFIG_USB
/* 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");
}
}
/* 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_populate(NULL, octeon_ids, NULL, NULL);
}
arch_initcall(octeon_publish_devices);