diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/dfu/dfu.c | 24 | ||||
-rw-r--r-- | drivers/dfu/dfu_mmc.c | 8 | ||||
-rw-r--r-- | drivers/gpio/intel_ich6_gpio.c | 20 | ||||
-rw-r--r-- | drivers/net/rtl8169.c | 127 | ||||
-rw-r--r-- | drivers/pci/Makefile | 1 | ||||
-rw-r--r-- | drivers/pci/pci_tegra.c | 1143 | ||||
-rw-r--r-- | drivers/power/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/as3722.c | 264 | ||||
-rw-r--r-- | drivers/serial/Makefile | 2 | ||||
-rw-r--r-- | drivers/serial/serial_x86.c (renamed from drivers/serial/serial_coreboot.c) | 12 | ||||
-rw-r--r-- | drivers/usb/eth/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/eth/asix88179.c | 700 | ||||
-rw-r--r-- | drivers/usb/eth/usb_ether.c | 7 | ||||
-rw-r--r-- | drivers/usb/gadget/atmel_usba_udc.c | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/f_dfu.c | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/f_fastboot.c | 14 | ||||
-rw-r--r-- | drivers/usb/gadget/f_thor.c | 16 | ||||
-rw-r--r-- | drivers/usb/gadget/g_dnl.c | 17 |
18 files changed, 2288 insertions, 72 deletions
diff --git a/drivers/dfu/dfu.c b/drivers/dfu/dfu.c index 14cb366b01..ad0a7e7c25 100644 --- a/drivers/dfu/dfu.c +++ b/drivers/dfu/dfu.c @@ -17,7 +17,6 @@ #include <linux/list.h> #include <linux/compiler.h> -static bool dfu_detach_request; static LIST_HEAD(dfu_list); static int dfu_alt_num; static int alt_num_cnt; @@ -39,21 +38,6 @@ __weak bool dfu_usb_get_reset(void) return true; } -bool dfu_detach(void) -{ - return dfu_detach_request; -} - -void dfu_trigger_detach(void) -{ - dfu_detach_request = true; -} - -void dfu_clear_detach(void) -{ - dfu_detach_request = false; -} - static int dfu_find_alt_num(const char *s) { int i = 0; @@ -111,8 +95,12 @@ unsigned char *dfu_get_buf(struct dfu_entity *dfu) return dfu_buf; s = getenv("dfu_bufsiz"); - dfu_buf_size = s ? (unsigned long)simple_strtol(s, NULL, 16) : - CONFIG_SYS_DFU_DATA_BUF_SIZE; + if (s) + dfu_buf_size = (unsigned long)simple_strtol(s, NULL, 0); + + if (!s || !dfu_buf_size) + dfu_buf_size = CONFIG_SYS_DFU_DATA_BUF_SIZE; + if (dfu->max_buf_size && dfu_buf_size > dfu->max_buf_size) dfu_buf_size = dfu->max_buf_size; diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c index 72fa03eeda..62d72fe4c6 100644 --- a/drivers/dfu/dfu_mmc.c +++ b/drivers/dfu/dfu_mmc.c @@ -40,10 +40,16 @@ static int mmc_access_part(struct dfu_entity *dfu, struct mmc *mmc, int part) static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu, u64 offset, void *buf, long *len) { - struct mmc *mmc = find_mmc_device(dfu->data.mmc.dev_num); + struct mmc *mmc; u32 blk_start, blk_count, n = 0; int ret, part_num_bkp = 0; + mmc = find_mmc_device(dfu->data.mmc.dev_num); + if (!mmc) { + error("Device MMC %d - not found!", dfu->data.mmc.dev_num); + return -ENODEV; + } + /* * We must ensure that we work in lba_blk_size chunks, so ALIGN * this value. diff --git a/drivers/gpio/intel_ich6_gpio.c b/drivers/gpio/intel_ich6_gpio.c index 3433216cb6..7720cc3dad 100644 --- a/drivers/gpio/intel_ich6_gpio.c +++ b/drivers/gpio/intel_ich6_gpio.c @@ -39,9 +39,9 @@ struct ich6_bank_priv { /* These are I/O addresses */ - uint32_t use_sel; - uint32_t io_sel; - uint32_t lvl; + uint16_t use_sel; + uint16_t io_sel; + uint16_t lvl; }; /* TODO: Move this to device tree, or platform data */ @@ -57,7 +57,7 @@ static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) u8 tmpbyte; u16 tmpword; u32 tmplong; - u32 gpiobase; + u16 gpiobase; int offset; /* Where should it be? */ @@ -116,11 +116,15 @@ static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) /* * GPIOBASE moved to its current offset with ICH6, but prior to * that it was unused (or undocumented). Check that it looks - * okay: not all ones or zeros, and mapped to I/O space (bit 0). + * okay: not all ones or zeros. + * + * Note we don't need check bit0 here, because the Tunnel Creek + * GPIO base address register bit0 is reserved (read returns 0), + * while on the Ivybridge the bit0 is used to indicate it is an + * I/O space. */ tmplong = pci_read_config32(pci_dev, PCI_CFG_GPIOBASE); - if (tmplong == 0x00000000 || tmplong == 0xffffffff || - !(tmplong & 0x00000001)) { + if (tmplong == 0x00000000 || tmplong == 0xffffffff) { debug("%s: unexpected GPIOBASE value\n", __func__); return -ENODEV; } @@ -131,7 +135,7 @@ static int gpio_ich6_ofdata_to_platdata(struct udevice *dev) * at the offset that we just read. Bit 0 indicates that it's * an I/O address, not a memory address, so mask that off. */ - gpiobase = tmplong & 0xfffffffe; + gpiobase = tmplong & 0xfffe; offset = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1); if (offset == -1) { debug("%s: Invalid register offset %d\n", __func__, offset); diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index c3ce17516c..cea6701203 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -41,6 +41,7 @@ * Modified to use le32_to_cpu and cpu_to_le32 properly */ #include <common.h> +#include <errno.h> #include <malloc.h> #include <net.h> #include <netdev.h> @@ -79,7 +80,11 @@ static int media[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 }; #define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ #define NUM_TX_DESC 1 /* Number of Tx descriptor registers */ -#define NUM_RX_DESC 4 /* Number of Rx descriptor registers */ +#ifdef CONFIG_SYS_RX_ETH_BUFFER + #define NUM_RX_DESC CONFIG_SYS_RX_ETH_BUFFER +#else + #define NUM_RX_DESC 4 /* Number of Rx descriptor registers */ +#endif #define RX_BUF_SIZE 1536 /* Rx Buffer size */ #define RX_BUF_LEN 8192 @@ -248,6 +253,7 @@ static struct { {"RTL-8168b/8111sb", 0x38, 0xff7e1880,}, {"RTL-8168d/8111d", 0x28, 0xff7e1880,}, {"RTL-8168evl/8111evl", 0x2e, 0xff7e1880,}, + {"RTL-8168/8111g", 0x4c, 0xff7e1880,}, {"RTL-8101e", 0x34, 0xff7e1880,}, {"RTL-8100e", 0x32, 0xff7e1880,}, }; @@ -273,23 +279,40 @@ struct RxDesc { u32 buf_Haddr; }; -/* Define the TX Descriptor */ -static u8 tx_ring[NUM_TX_DESC * sizeof(struct TxDesc) + 256]; -/* __attribute__ ((aligned(256))); */ +#define RTL8169_DESC_SIZE 16 -/* Create a static buffer of size RX_BUF_SZ for each -TX Descriptor. All descriptors point to a -part of this buffer */ -static unsigned char txb[NUM_TX_DESC * RX_BUF_SIZE]; +#if ARCH_DMA_MINALIGN > 256 +# define RTL8169_ALIGN ARCH_DMA_MINALIGN +#else +# define RTL8169_ALIGN 256 +#endif -/* Define the RX Descriptor */ -static u8 rx_ring[NUM_RX_DESC * sizeof(struct TxDesc) + 256]; - /* __attribute__ ((aligned(256))); */ +/* + * Warn if the cache-line size is larger than the descriptor size. In such + * cases the driver will likely fail because the CPU needs to flush the cache + * when requeuing RX buffers, therefore descriptors written by the hardware + * may be discarded. + * + * This can be fixed by defining CONFIG_SYS_NONCACHED_MEMORY which will cause + * the driver to allocate descriptors from a pool of non-cached memory. + */ +#if RTL8169_DESC_SIZE < ARCH_DMA_MINALIGN +#if !defined(CONFIG_SYS_NONCACHED_MEMORY) && !defined(CONFIG_SYS_DCACHE_OFF) +#warning cache-line size is larger than descriptor size +#endif +#endif -/* Create a static buffer of size RX_BUF_SZ for each -RX Descriptor All descriptors point to a -part of this buffer */ -static unsigned char rxb[NUM_RX_DESC * RX_BUF_SIZE]; +/* + * Create a static buffer of size RX_BUF_SZ for each TX Descriptor. All + * descriptors point to a part of this buffer. + */ +DEFINE_ALIGN_BUFFER(u8, txb, NUM_TX_DESC * RX_BUF_SIZE, RTL8169_ALIGN); + +/* + * Create a static buffer of size RX_BUF_SZ for each RX Descriptor. All + * descriptors point to a part of this buffer. + */ +DEFINE_ALIGN_BUFFER(u8, rxb, NUM_RX_DESC * RX_BUF_SIZE, RTL8169_ALIGN); struct rtl8169_private { void *mmio_addr; /* memory map physical address */ @@ -297,8 +320,6 @@ struct rtl8169_private { unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ unsigned long cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ unsigned long dirty_tx; - unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */ - unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */ struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */ struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */ unsigned char *RxBufferRings; /* Index of Rx Buffer */ @@ -398,34 +419,71 @@ match: } /* + * TX and RX descriptors are 16 bytes. This causes problems with the cache + * maintenance on CPUs where the cache-line size exceeds the size of these + * descriptors. What will happen is that when the driver receives a packet + * it will be immediately requeued for the hardware to reuse. The CPU will + * therefore need to flush the cache-line containing the descriptor, which + * will cause all other descriptors in the same cache-line to be flushed + * along with it. If one of those descriptors had been written to by the + * device those changes (and the associated packet) will be lost. + * + * To work around this, we make use of non-cached memory if available. If + * descriptors are mapped uncached there's no need to manually flush them + * or invalidate them. + * + * Note that this only applies to descriptors. The packet data buffers do + * not have the same constraints since they are 1536 bytes large, so they + * are unlikely to share cache-lines. + */ +static void *rtl_alloc_descs(unsigned int num) +{ + size_t size = num * RTL8169_DESC_SIZE; + +#ifdef CONFIG_SYS_NONCACHED_MEMORY + return (void *)noncached_alloc(size, RTL8169_ALIGN); +#else + return memalign(RTL8169_ALIGN, size); +#endif +} + +/* * Cache maintenance functions. These are simple wrappers around the more * general purpose flush_cache() and invalidate_dcache_range() functions. */ static void rtl_inval_rx_desc(struct RxDesc *desc) { +#ifndef CONFIG_SYS_NONCACHED_MEMORY unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1); unsigned long end = ALIGN(start + sizeof(*desc), ARCH_DMA_MINALIGN); invalidate_dcache_range(start, end); +#endif } static void rtl_flush_rx_desc(struct RxDesc *desc) { +#ifndef CONFIG_SYS_NONCACHED_MEMORY flush_cache((unsigned long)desc, sizeof(*desc)); +#endif } static void rtl_inval_tx_desc(struct TxDesc *desc) { +#ifndef CONFIG_SYS_NONCACHED_MEMORY unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1); unsigned long end = ALIGN(start + sizeof(*desc), ARCH_DMA_MINALIGN); invalidate_dcache_range(start, end); +#endif } static void rtl_flush_tx_desc(struct TxDesc *desc) { +#ifndef CONFIG_SYS_NONCACHED_MEMORY flush_cache((unsigned long)desc, sizeof(*desc)); +#endif } static void rtl_inval_buffer(void *buf, size_t size) @@ -707,16 +765,6 @@ static int rtl_reset(struct eth_device *dev, bd_t *bis) printf ("%s\n", __FUNCTION__); #endif - tpc->TxDescArrays = tx_ring; - /* Tx Desscriptor needs 256 bytes alignment; */ - tpc->TxDescArray = (struct TxDesc *) ((unsigned long)(tpc->TxDescArrays + - 255) & ~255); - - tpc->RxDescArrays = rx_ring; - /* Rx Desscriptor needs 256 bytes alignment; */ - tpc->RxDescArray = (struct RxDesc *) ((unsigned long)(tpc->RxDescArrays + - 255) & ~255); - rtl8169_init_ring(dev); rtl8169_hw_start(dev); /* Construct a perfect filter frame with the mac address as first match @@ -758,10 +806,6 @@ static void rtl_halt(struct eth_device *dev) RTL_W32(RxMissed, 0); - tpc->TxDescArrays = NULL; - tpc->RxDescArrays = NULL; - tpc->TxDescArray = NULL; - tpc->RxDescArray = NULL; for (i = 0; i < NUM_RX_DESC; i++) { tpc->RxBufferRing[i] = NULL; } @@ -906,7 +950,16 @@ static int rtl_init(struct eth_device *dev, bd_t *bis) #endif } - return 1; + + tpc->RxDescArray = rtl_alloc_descs(NUM_RX_DESC); + if (!tpc->RxDescArray) + return -ENOMEM; + + tpc->TxDescArray = rtl_alloc_descs(NUM_TX_DESC); + if (!tpc->TxDescArray) + return -ENOMEM; + + return 0; } int rtl8169_initialize(bd_t *bis) @@ -920,6 +973,7 @@ int rtl8169_initialize(bd_t *bis) while(1){ unsigned int region; u16 device; + int err; /* Find RTL8169 */ if ((devno = pci_find_devices(supported, idx++)) < 0) @@ -958,9 +1012,14 @@ int rtl8169_initialize(bd_t *bis) dev->send = rtl_send; dev->recv = rtl_recv; - eth_register (dev); + err = rtl_init(dev, bis); + if (err < 0) { + printf(pr_fmt("failed to initialize card: %d\n"), err); + free(dev); + continue; + } - rtl_init(dev, bis); + eth_register (dev); card_number++; } diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 85e82bdb8c..50b7be53ca 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_FTPCI100) += pci_ftpci100.o obj-$(CONFIG_SH4_PCI) += pci_sh4.o obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o obj-$(CONFIG_SH7780_PCI) +=pci_sh7780.o +obj-$(CONFIG_PCI_TEGRA) += pci_tegra.o obj-$(CONFIG_TSI108_PCI) += tsi108_pci.o obj-$(CONFIG_WINBOND_83C553) += w83c553f.o obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape.o diff --git a/drivers/pci/pci_tegra.c b/drivers/pci/pci_tegra.c new file mode 100644 index 0000000000..a03ad5ff1f --- /dev/null +++ b/drivers/pci/pci_tegra.c @@ -0,0 +1,1143 @@ +/* + * Copyright (c) 2010, CompuLab, Ltd. + * Author: Mike Rapoport <mike@compulab.co.il> + * + * Based on NVIDIA PCIe driver + * Copyright (c) 2008-2009, NVIDIA Corporation. + * + * Copyright (c) 2013-2014, NVIDIA Corporation. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#define DEBUG +#define pr_fmt(fmt) "tegra-pcie: " fmt + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <malloc.h> +#include <pci.h> + +#include <asm/io.h> +#include <asm/gpio.h> + +#include <asm/arch/clock.h> +#include <asm/arch/powergate.h> +#include <asm/arch-tegra/xusb-padctl.h> + +#include <linux/list.h> + +#include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define AFI_AXI_BAR0_SZ 0x00 +#define AFI_AXI_BAR1_SZ 0x04 +#define AFI_AXI_BAR2_SZ 0x08 +#define AFI_AXI_BAR3_SZ 0x0c +#define AFI_AXI_BAR4_SZ 0x10 +#define AFI_AXI_BAR5_SZ 0x14 + +#define AFI_AXI_BAR0_START 0x18 +#define AFI_AXI_BAR1_START 0x1c +#define AFI_AXI_BAR2_START 0x20 +#define AFI_AXI_BAR3_START 0x24 +#define AFI_AXI_BAR4_START 0x28 +#define AFI_AXI_BAR5_START 0x2c + +#define AFI_FPCI_BAR0 0x30 +#define AFI_FPCI_BAR1 0x34 +#define AFI_FPCI_BAR2 0x38 +#define AFI_FPCI_BAR3 0x3c +#define AFI_FPCI_BAR4 0x40 +#define AFI_FPCI_BAR5 0x44 + +#define AFI_CACHE_BAR0_SZ 0x48 +#define AFI_CACHE_BAR0_ST 0x4c +#define AFI_CACHE_BAR1_SZ 0x50 +#define AFI_CACHE_BAR1_ST 0x54 + +#define AFI_MSI_BAR_SZ 0x60 +#define AFI_MSI_FPCI_BAR_ST 0x64 +#define AFI_MSI_AXI_BAR_ST 0x68 + +#define AFI_CONFIGURATION 0xac +#define AFI_CONFIGURATION_EN_FPCI (1 << 0) + +#define AFI_FPCI_ERROR_MASKS 0xb0 + +#define AFI_INTR_MASK 0xb4 +#define AFI_INTR_MASK_INT_MASK (1 << 0) +#define AFI_INTR_MASK_MSI_MASK (1 << 8) + +#define AFI_SM_INTR_ENABLE 0xc4 +#define AFI_SM_INTR_INTA_ASSERT (1 << 0) +#define AFI_SM_INTR_INTB_ASSERT (1 << 1) +#define AFI_SM_INTR_INTC_ASSERT (1 << 2) +#define AFI_SM_INTR_INTD_ASSERT (1 << 3) +#define AFI_SM_INTR_INTA_DEASSERT (1 << 4) +#define AFI_SM_INTR_INTB_DEASSERT (1 << 5) +#define AFI_SM_INTR_INTC_DEASSERT (1 << 6) +#define AFI_SM_INTR_INTD_DEASSERT (1 << 7) + +#define AFI_AFI_INTR_ENABLE 0xc8 +#define AFI_INTR_EN_INI_SLVERR (1 << 0) +#define AFI_INTR_EN_INI_DECERR (1 << 1) +#define AFI_INTR_EN_TGT_SLVERR (1 << 2) +#define AFI_INTR_EN_TGT_DECERR (1 << 3) +#define AFI_INTR_EN_TGT_WRERR (1 << 4) +#define AFI_INTR_EN_DFPCI_DECERR (1 << 5) +#define AFI_INTR_EN_AXI_DECERR (1 << 6) +#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) +#define AFI_INTR_EN_PRSNT_SENSE (1 << 8) + +#define AFI_PCIE_CONFIG 0x0f8 +#define AFI_PCIE_CONFIG_PCIE_DISABLE(x) (1 << ((x) + 1)) +#define AFI_PCIE_CONFIG_PCIE_DISABLE_ALL 0xe +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420 (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1 (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1 (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20) + +#define AFI_FUSE 0x104 +#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) + +#define AFI_PEX0_CTRL 0x110 +#define AFI_PEX1_CTRL 0x118 +#define AFI_PEX2_CTRL 0x128 +#define AFI_PEX_CTRL_RST (1 << 0) +#define AFI_PEX_CTRL_CLKREQ_EN (1 << 1) +#define AFI_PEX_CTRL_REFCLK_EN (1 << 3) +#define AFI_PEX_CTRL_OVERRIDE_EN (1 << 4) + +#define AFI_PLLE_CONTROL 0x160 +#define AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL (1 << 9) +#define AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN (1 << 1) + +#define AFI_PEXBIAS_CTRL_0 0x168 + +#define PADS_CTL_SEL 0x0000009C + +#define PADS_CTL 0x000000A0 +#define PADS_CTL_IDDQ_1L (1 << 0) +#define PADS_CTL_TX_DATA_EN_1L (1 << 6) +#define PADS_CTL_RX_DATA_EN_1L (1 << 10) + +#define PADS_PLL_CTL_TEGRA20 0x000000B8 +#define PADS_PLL_CTL_TEGRA30 0x000000B4 +#define PADS_PLL_CTL_RST_B4SM (0x1 << 1) +#define PADS_PLL_CTL_LOCKDET (0x1 << 8) +#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16) +#define PADS_PLL_CTL_REFCLK_INTERNAL_CML (0x0 << 16) +#define PADS_PLL_CTL_REFCLK_INTERNAL_CMOS (0x1 << 16) +#define PADS_PLL_CTL_REFCLK_EXTERNAL (0x2 << 16) +#define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20) +#define PADS_PLL_CTL_TXCLKREF_DIV10 (0x0 << 20) +#define PADS_PLL_CTL_TXCLKREF_DIV5 (0x1 << 20) +#define PADS_PLL_CTL_TXCLKREF_BUF_EN (0x1 << 22) + +#define PADS_REFCLK_CFG0 0x000000C8 +#define PADS_REFCLK_CFG1 0x000000CC + +/* + * Fields in PADS_REFCLK_CFG*. Those registers form an array of 16-bit + * entries, one entry per PCIe port. These field definitions and desired + * values aren't in the TRM, but do come from NVIDIA. + */ +#define PADS_REFCLK_CFG_TERM_SHIFT 2 /* 6:2 */ +#define PADS_REFCLK_CFG_E_TERM_SHIFT 7 +#define PADS_REFCLK_CFG_PREDI_SHIFT 8 /* 11:8 */ +#define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */ + +/* Default value provided by HW engineering is 0xfa5c */ +#define PADS_REFCLK_CFG_VALUE \ + ( \ + (0x17 << PADS_REFCLK_CFG_TERM_SHIFT) | \ + (0 << PADS_REFCLK_CFG_E_TERM_SHIFT) | \ + (0xa << PADS_REFCLK_CFG_PREDI_SHIFT) | \ + (0xf << PADS_REFCLK_CFG_DRVI_SHIFT) \ + ) + +#define RP_VEND_XP 0x00000F00 +#define RP_VEND_XP_DL_UP (1 << 30) + +#define RP_PRIV_MISC 0x00000FE0 +#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xE << 0) +#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xF << 0) + +#define RP_LINK_CONTROL_STATUS 0x00000090 +#define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000 +#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 + +struct tegra_pcie; + +struct tegra_pcie_port { + struct tegra_pcie *pcie; + + struct fdt_resource regs; + unsigned int num_lanes; + unsigned int index; + + struct list_head list; +}; + +struct tegra_pcie_soc { + unsigned int num_ports; + unsigned long pads_pll_ctl; + unsigned long tx_ref_sel; + bool has_pex_clkreq_en; + bool has_pex_bias_ctrl; + bool has_cml_clk; + bool has_gen2; +}; + +struct tegra_pcie { + struct pci_controller hose; + + struct fdt_resource pads; + struct fdt_resource afi; + struct fdt_resource cs; + + struct fdt_resource prefetch; + struct fdt_resource mem; + struct fdt_resource io; + + struct list_head ports; + unsigned long xbar; + + const struct tegra_pcie_soc *soc; + struct tegra_xusb_phy *phy; +}; + +static inline struct tegra_pcie *to_tegra_pcie(struct pci_controller *hose) +{ + return container_of(hose, struct tegra_pcie, hose); +} + +static void afi_writel(struct tegra_pcie *pcie, unsigned long value, + unsigned long offset) +{ + writel(value, pcie->afi.start + offset); +} + +static unsigned long afi_readl(struct tegra_pcie *pcie, unsigned long offset) +{ + return readl(pcie->afi.start + offset); +} + +static void pads_writel(struct tegra_pcie *pcie, unsigned long value, + unsigned long offset) +{ + writel(value, pcie->pads.start + offset); +} + +static unsigned long pads_readl(struct tegra_pcie *pcie, unsigned long offset) +{ + return readl(pcie->pads.start + offset); +} + +static unsigned long rp_readl(struct tegra_pcie_port *port, + unsigned long offset) +{ + return readl(port->regs.start + offset); +} + +static void rp_writel(struct tegra_pcie_port *port, unsigned long value, + unsigned long offset) +{ + writel(value, port->regs.start + offset); +} + +static unsigned long tegra_pcie_conf_offset(pci_dev_t bdf, int where) +{ + return ((where & 0xf00) << 16) | (PCI_BUS(bdf) << 16) | + (PCI_DEV(bdf) << 11) | (PCI_FUNC(bdf) << 8) | + (where & 0xfc); +} + +static int tegra_pcie_conf_address(struct tegra_pcie *pcie, pci_dev_t bdf, + int where, unsigned long *address) +{ + unsigned int bus = PCI_BUS(bdf); + + if (bus == 0) { + unsigned int dev = PCI_DEV(bdf); + struct tegra_pcie_port *port; + + list_for_each_entry(port, &pcie->ports, list) { + if (port->index + 1 == dev) { + *address = port->regs.start + (where & ~3); + return 0; + } + } + } else { + *address = pcie->cs.start + tegra_pcie_conf_offset(bdf, where); + return 0; + } + + return -1; +} + +static int tegra_pcie_read_conf(struct pci_controller *hose, pci_dev_t bdf, + int where, u32 *value) +{ + struct tegra_pcie *pcie = to_tegra_pcie(hose); + unsigned long address; + int err; + + err = tegra_pcie_conf_address(pcie, bdf, where, &address); + if (err < 0) { + *value = 0xffffffff; + return 1; + } + + *value = readl(address); + + /* fixup root port class */ + if (PCI_BUS(bdf) == 0) { + if (where == PCI_CLASS_REVISION) { + *value &= ~0x00ff0000; + *value |= PCI_CLASS_BRIDGE_PCI << 16; + } + } + + return 0; +} + +static int tegra_pcie_write_conf(struct pci_controller *hose, pci_dev_t bdf, + int where, u32 value) +{ + struct tegra_pcie *pcie = to_tegra_pcie(hose); + unsigned long address; + int err; + + err = tegra_pcie_conf_address(pcie, bdf, where, &address); + if (err < 0) + return 1; + + writel(value, address); + + return 0; +} + +static int tegra_pcie_port_parse_dt(const void *fdt, int node, + struct tegra_pcie_port *port) +{ + const u32 *addr; + int len; + + addr = fdt_getprop(fdt, node, "assigned-addresses", &len); + if (!addr) { + error("property \"assigned-addresses\" not found"); + return -FDT_ERR_NOTFOUND; + } + + port->regs.start = fdt32_to_cpu(addr[2]); + port->regs.end = port->regs.start + fdt32_to_cpu(addr[4]); + + return 0; +} + +static int tegra_pcie_get_xbar_config(const void *fdt, int node, u32 lanes, + unsigned long *xbar) +{ + enum fdt_compat_id id = fdtdec_lookup(fdt, node); + + switch (id) { + case COMPAT_NVIDIA_TEGRA20_PCIE: + switch (lanes) { + case 0x00000004: + debug("single-mode configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE; + return 0; + + case 0x00000202: + debug("dual-mode configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL; + return 0; + } + break; + + case COMPAT_NVIDIA_TEGRA30_PCIE: + switch (lanes) { + case 0x00000204: + debug("4x1, 2x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420; + return 0; + + case 0x00020202: + debug("2x3 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222; + return 0; + + case 0x00010104: + debug("4x1, 1x2 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411; + return 0; + } + break; + + case COMPAT_NVIDIA_TEGRA124_PCIE: + switch (lanes) { + case 0x0000104: + debug("4x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1; + return 0; + + case 0x0000102: + debug("2x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1; + return 0; + } + break; + + default: + break; + } + + return -FDT_ERR_NOTFOUND; +} + +static int tegra_pcie_parse_dt_ranges(const void *fdt, int node, + struct tegra_pcie *pcie) +{ + const u32 *ptr, *end; + int len; + + ptr = fdt_getprop(fdt, node, "ranges", &len); + if (!ptr) { + error("missing \"ranges\" property"); + return -FDT_ERR_NOTFOUND; + } + + end = ptr + len / 4; + + while (ptr < end) { + struct fdt_resource *res = NULL; + u32 space = fdt32_to_cpu(*ptr); + + switch ((space >> 24) & 0x3) { + case 0x01: + res = &pcie->io; + break; + + case 0x02: /* 32 bit */ + case 0x03: /* 64 bit */ + if (space & (1 << 30)) + res = &pcie->prefetch; + else + res = &pcie->mem; + + break; + } + + if (res) { + res->start = fdt32_to_cpu(ptr[3]); + res->end = res->start + fdt32_to_cpu(ptr[5]); + } + + ptr += 3 + 1 + 2; + } + + debug("PCI regions:\n"); + debug(" I/O: %#x-%#x\n", pcie->io.start, pcie->io.end); + debug(" non-prefetchable memory: %#x-%#x\n", pcie->mem.start, + pcie->mem.end); + debug(" prefetchable memory: %#x-%#x\n", pcie->prefetch.start, + pcie->prefetch.end); + + return 0; +} + +static int tegra_pcie_parse_port_info(const void *fdt, int node, + unsigned int *index, + unsigned int *lanes) +{ + pci_dev_t bdf; + int err; + + err = fdtdec_get_int(fdt, node, "nvidia,num-lanes", 0); + if (err < 0) { + error("failed to parse \"nvidia,num-lanes\" property"); + return err; + } + + *lanes = err; + + err = fdtdec_pci_get_bdf(fdt, node, &bdf); + if (err < 0) { + error("failed to parse \"reg\" property"); + return err; + } + + *index = PCI_DEV(bdf) - 1; + + return 0; +} + +static int tegra_pcie_parse_dt(const void *fdt, int node, + struct tegra_pcie *pcie) +{ + int err, subnode; + u32 lanes = 0; + + err = fdt_get_named_resource(fdt, node, "reg", "reg-names", "pads", + &pcie->pads); + if (err < 0) { + error("resource \"pads\" not found"); + return err; + } + + err = fdt_get_named_resource(fdt, node, "reg", "reg-names", "afi", + &pcie->afi); + if (err < 0) { + error("resource \"afi\" not found"); + return err; + } + + err = fdt_get_named_resource(fdt, node, "reg", "reg-names", "cs", + &pcie->cs); + if (err < 0) { + error("resource \"cs\" not found"); + return err; + } + + pcie->phy = tegra_xusb_phy_get(TEGRA_XUSB_PADCTL_PCIE); + if (pcie->phy) { + err = tegra_xusb_phy_prepare(pcie->phy); + if (err < 0) { + error("failed to prepare PHY: %d", err); + return err; + } + } + + err = tegra_pcie_parse_dt_ranges(fdt, node, pcie); + if (err < 0) { + error("failed to parse \"ranges\" property"); + return err; + } + + fdt_for_each_subnode(fdt, subnode, node) { + unsigned int index = 0, num_lanes = 0; + struct tegra_pcie_port *port; + + err = tegra_pcie_parse_port_info(fdt, subnode, &index, + &num_lanes); + if (err < 0) { + error("failed to obtain root port info"); + continue; + } + + lanes |= num_lanes << (index << 3); + + if (!fdtdec_get_is_enabled(fdt, subnode)) + continue; + + port = malloc(sizeof(*port)); + if (!port) + continue; + + memset(port, 0, sizeof(*port)); + port->num_lanes = num_lanes; + port->index = index; + + err = tegra_pcie_port_parse_dt(fdt, subnode, port); + if (err < 0) { + free(port); + continue; + } + + list_add_tail(&port->list, &pcie->ports); + port->pcie = pcie; + } + + err = tegra_pcie_get_xbar_config(fdt, node, lanes, &pcie->xbar); + if (err < 0) { + error("invalid lane configuration"); + return err; + } + + return 0; +} + +int __weak tegra_pcie_board_init(void) +{ + return 0; +} + +static int tegra_pcie_power_on(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + unsigned long value; + int err; + + /* reset PCIEXCLK logic, AFI controller and PCIe controller */ + reset_set_enable(PERIPH_ID_PCIEXCLK, 1); + reset_set_enable(PERIPH_ID_AFI, 1); + reset_set_enable(PERIPH_ID_PCIE, 1); + + err = tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); + if (err < 0) { + error("failed to power off PCIe partition: %d", err); + return err; + } + + tegra_pcie_board_init(); + + err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, + PERIPH_ID_PCIE); + if (err < 0) { + error("failed to power up PCIe partition: %d", err); + return err; + } + + /* take AFI controller out of reset */ + reset_set_enable(PERIPH_ID_AFI, 0); + + /* enable AFI clock */ + clock_enable(PERIPH_ID_AFI); + + if (soc->has_cml_clk) { + /* enable CML clock */ + value = readl(NV_PA_CLK_RST_BASE + 0x48c); + value |= (1 << 0); + value &= ~(1 << 1); + writel(value, NV_PA_CLK_RST_BASE + 0x48c); + } + + err = tegra_plle_enable(); + if (err < 0) { + error("failed to enable PLLE: %d\n", err); + return err; + } + + return 0; +} + +static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + unsigned long start = get_timer(0); + u32 value; + + while (get_timer(start) < timeout) { + value = pads_readl(pcie, soc->pads_pll_ctl); + if (value & PADS_PLL_CTL_LOCKDET) + return 0; + } + + return -ETIMEDOUT; +} + +static int tegra_pcie_phy_enable(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + u32 value; + int err; + + /* initialize internal PHY, enable up to 16 PCIe lanes */ + pads_writel(pcie, 0, PADS_CTL_SEL); + + /* override IDDQ to 1 on all 4 lanes */ + value = pads_readl(pcie, PADS_CTL); + value |= PADS_CTL_IDDQ_1L; + pads_writel(pcie, value, PADS_CTL); + + /* + * Set up PHY PLL inputs select PLLE output as refclock, set TX + * ref sel to div10 (not div5). + */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK); + value |= PADS_PLL_CTL_REFCLK_INTERNAL_CML | soc->tx_ref_sel; + pads_writel(pcie, value, soc->pads_pll_ctl); + + /* reset PLL */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value &= ~PADS_PLL_CTL_RST_B4SM; + pads_writel(pcie, value, soc->pads_pll_ctl); + + udelay(20); + + /* take PLL out of reset */ + value = pads_readl(pcie, soc->pads_pll_ctl); + value |= PADS_PLL_CTL_RST_B4SM; + pads_writel(pcie, value, soc->pads_pll_ctl); + + /* configure the reference clock driver */ + value = PADS_REFCLK_CFG_VALUE | (PADS_REFCLK_CFG_VALUE << 16); + pads_writel(pcie, value, PADS_REFCLK_CFG0); + + if (soc->num_ports > 2) + pads_writel(pcie, PADS_REFCLK_CFG_VALUE, PADS_REFCLK_CFG1); + + /* wait for the PLL to lock */ + err = tegra_pcie_pll_wait(pcie, 500); + if (err < 0) { + error("PLL failed to lock: %d", err); + return err; + } + + /* turn off IDDQ override */ + value = pads_readl(pcie, PADS_CTL); + value &= ~PADS_CTL_IDDQ_1L; + pads_writel(pcie, value, PADS_CTL); + + /* enable TX/RX data */ + value = pads_readl(pcie, PADS_CTL); + value |= PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L; + pads_writel(pcie, value, PADS_CTL); + + return 0; +} + +static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + struct tegra_pcie_port *port; + u32 value; + int err; + + if (pcie->phy) { + value = afi_readl(pcie, AFI_PLLE_CONTROL); + value &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL; + value |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN; + afi_writel(pcie, value, AFI_PLLE_CONTROL); + } + + if (soc->has_pex_bias_ctrl) + afi_writel(pcie, 0, AFI_PEXBIAS_CTRL_0); + + value = afi_readl(pcie, AFI_PCIE_CONFIG); + value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK; + value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar; + + list_for_each_entry(port, &pcie->ports, list) + value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index); + + afi_writel(pcie, value, AFI_PCIE_CONFIG); + + value = afi_readl(pcie, AFI_FUSE); + + if (soc->has_gen2) + value &= ~AFI_FUSE_PCIE_T0_GEN2_DIS; + else + value |= AFI_FUSE_PCIE_T0_GEN2_DIS; + + afi_writel(pcie, value, AFI_FUSE); + + if (pcie->phy) + err = tegra_xusb_phy_enable(pcie->phy); + else + err = tegra_pcie_phy_enable(pcie); + + if (err < 0) { + error("failed to power on PHY: %d\n", err); + return err; + } + + /* take the PCIEXCLK logic out of reset */ + reset_set_enable(PERIPH_ID_PCIEXCLK, 0); + + /* finally enable PCIe */ + value = afi_readl(pcie, AFI_CONFIGURATION); + value |= AFI_CONFIGURATION_EN_FPCI; + afi_writel(pcie, value, AFI_CONFIGURATION); + + /* disable all interrupts */ + afi_writel(pcie, 0, AFI_AFI_INTR_ENABLE); + afi_writel(pcie, 0, AFI_SM_INTR_ENABLE); + afi_writel(pcie, 0, AFI_INTR_MASK); + afi_writel(pcie, 0, AFI_FPCI_ERROR_MASKS); + + return 0; +} + +static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) +{ + unsigned long fpci, axi, size; + + /* BAR 0: type 1 extended configuration space */ + fpci = 0xfe100000; + size = fdt_resource_size(&pcie->cs); + axi = pcie->cs.start; + + afi_writel(pcie, axi, AFI_AXI_BAR0_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR0_SZ); + afi_writel(pcie, fpci, AFI_FPCI_BAR0); + + /* BAR 1: downstream I/O */ + fpci = 0xfdfc0000; + size = fdt_resource_size(&pcie->io); + axi = pcie->io.start; + + afi_writel(pcie, axi, AFI_AXI_BAR1_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR1_SZ); + afi_writel(pcie, fpci, AFI_FPCI_BAR1); + + /* BAR 2: prefetchable memory */ + fpci = (((pcie->prefetch.start >> 12) & 0x0fffffff) << 4) | 0x1; + size = fdt_resource_size(&pcie->prefetch); + axi = pcie->prefetch.start; + + afi_writel(pcie, axi, AFI_AXI_BAR2_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR2_SZ); + afi_writel(pcie, fpci, AFI_FPCI_BAR2); + + /* BAR 3: non-prefetchable memory */ + fpci = (((pcie->mem.start >> 12) & 0x0fffffff) << 4) | 0x1; + size = fdt_resource_size(&pcie->mem); + axi = pcie->mem.start; + + afi_writel(pcie, axi, AFI_AXI_BAR3_START); + afi_writel(pcie, size >> 12, AFI_AXI_BAR3_SZ); + afi_writel(pcie, fpci, AFI_FPCI_BAR3); + + /* NULL out the remaining BARs as they are not used */ + afi_writel(pcie, 0, AFI_AXI_BAR4_START); + afi_writel(pcie, 0, AFI_AXI_BAR4_SZ); + afi_writel(pcie, 0, AFI_FPCI_BAR4); + + afi_writel(pcie, 0, AFI_AXI_BAR5_START); + afi_writel(pcie, 0, AFI_AXI_BAR5_SZ); + afi_writel(pcie, 0, AFI_FPCI_BAR5); + + /* map all upstream transactions as uncached */ + afi_writel(pcie, NV_PA_SDRAM_BASE, AFI_CACHE_BAR0_ST); + afi_writel(pcie, 0, AFI_CACHE_BAR0_SZ); + afi_writel(pcie, 0, AFI_CACHE_BAR1_ST); + afi_writel(pcie, 0, AFI_CACHE_BAR1_SZ); + + /* MSI translations are setup only when needed */ + afi_writel(pcie, 0, AFI_MSI_FPCI_BAR_ST); + afi_writel(pcie, 0, AFI_MSI_BAR_SZ); + afi_writel(pcie, 0, AFI_MSI_AXI_BAR_ST); + afi_writel(pcie, 0, AFI_MSI_BAR_SZ); +} + +static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port) +{ + unsigned long ret = 0; + + switch (port->index) { + case 0: + ret = AFI_PEX0_CTRL; + break; + + case 1: + ret = AFI_PEX1_CTRL; + break; + + case 2: + ret = AFI_PEX2_CTRL; + break; + } + + return ret; +} + +static void tegra_pcie_port_reset(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + unsigned long value; + + /* pulse reset signel */ + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); + + udelay(2000); + + value = afi_readl(port->pcie, ctrl); + value |= AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); +} + +static void tegra_pcie_port_enable(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + unsigned long value; + + /* enable reference clock */ + value = afi_readl(port->pcie, ctrl); + value |= AFI_PEX_CTRL_REFCLK_EN; + + if (port->pcie->soc->has_pex_clkreq_en) + value |= AFI_PEX_CTRL_CLKREQ_EN; + + value |= AFI_PEX_CTRL_OVERRIDE_EN; + + afi_writel(port->pcie, value, ctrl); + + tegra_pcie_port_reset(port); +} + +static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port) +{ + unsigned int retries = 3; + unsigned long value; + + value = rp_readl(port, RP_PRIV_MISC); + value &= ~RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT; + value |= RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT; + rp_writel(port, value, RP_PRIV_MISC); + + do { + unsigned int timeout = 200; + + do { + value = rp_readl(port, RP_VEND_XP); + if (value & RP_VEND_XP_DL_UP) + break; + + udelay(2000); + } while (--timeout); + + if (!timeout) { + debug("link %u down, retrying\n", port->index); + goto retry; + } + + timeout = 200; + + do { + value = rp_readl(port, RP_LINK_CONTROL_STATUS); + if (value & RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE) + return true; + + udelay(2000); + } while (--timeout); + +retry: + tegra_pcie_port_reset(port); + } while (--retries); + + return false; +} + +static void tegra_pcie_port_disable(struct tegra_pcie_port *port) +{ + unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); + unsigned long value; + + /* assert port reset */ + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); + + /* disable reference clock */ + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_REFCLK_EN; + afi_writel(port->pcie, value, ctrl); +} + +static void tegra_pcie_port_free(struct tegra_pcie_port *port) +{ + list_del(&port->list); + free(port); +} + +static int tegra_pcie_enable(struct tegra_pcie *pcie) +{ + struct tegra_pcie_port *port, *tmp; + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + debug("probing port %u, using %u lanes\n", port->index, + port->num_lanes); + + tegra_pcie_port_enable(port); + + if (tegra_pcie_port_check_link(port)) + continue; + + debug("link %u down, ignoring\n", port->index); + + tegra_pcie_port_disable(port); + tegra_pcie_port_free(port); + } + + return 0; +} + +static const struct tegra_pcie_soc tegra20_pcie_soc = { + .num_ports = 2, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA20, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10, + .has_pex_clkreq_en = false, + .has_pex_bias_ctrl = false, + .has_cml_clk = false, + .has_gen2 = false, +}; + +static const struct tegra_pcie_soc tegra30_pcie_soc = { + .num_ports = 3, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, + .has_pex_clkreq_en = true, + .has_pex_bias_ctrl = true, + .has_cml_clk = true, + .has_gen2 = false, +}; + +static const struct tegra_pcie_soc tegra124_pcie_soc = { + .num_ports = 2, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, + .has_pex_clkreq_en = true, + .has_pex_bias_ctrl = true, + .has_cml_clk = true, + .has_gen2 = true, +}; + +static int process_nodes(const void *fdt, int nodes[], unsigned int count) +{ + unsigned int i; + + for (i = 0; i < count; i++) { + const struct tegra_pcie_soc *soc; + struct tegra_pcie *pcie; + enum fdt_compat_id id; + int err; + + if (!fdtdec_get_is_enabled(fdt, nodes[i])) + continue; + + id = fdtdec_lookup(fdt, nodes[i]); + switch (id) { + case COMPAT_NVIDIA_TEGRA20_PCIE: + soc = &tegra20_pcie_soc; + break; + + case COMPAT_NVIDIA_TEGRA30_PCIE: + soc = &tegra30_pcie_soc; + break; + + case COMPAT_NVIDIA_TEGRA124_PCIE: + soc = &tegra124_pcie_soc; + break; + + default: + error("unsupported compatible: %s", + fdtdec_get_compatible(id)); + continue; + } + + pcie = malloc(sizeof(*pcie)); + if (!pcie) { + error("failed to allocate controller"); + continue; + } + + memset(pcie, 0, sizeof(*pcie)); + pcie->soc = soc; + + INIT_LIST_HEAD(&pcie->ports); + + err = tegra_pcie_parse_dt(fdt, nodes[i], pcie); + if (err < 0) { + free(pcie); + continue; + } + + err = tegra_pcie_power_on(pcie); + if (err < 0) { + error("failed to power on"); + continue; + } + + err = tegra_pcie_enable_controller(pcie); + if (err < 0) { + error("failed to enable controller"); + continue; + } + + tegra_pcie_setup_translations(pcie); + + err = tegra_pcie_enable(pcie); + if (err < 0) { + error("failed to enable PCIe"); + continue; + } + + pcie->hose.first_busno = 0; + pcie->hose.current_busno = 0; + pcie->hose.last_busno = 0; + + pci_set_region(&pcie->hose.regions[0], NV_PA_SDRAM_BASE, + NV_PA_SDRAM_BASE, gd->ram_size, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); + + pci_set_region(&pcie->hose.regions[1], pcie->io.start, + pcie->io.start, fdt_resource_size(&pcie->io), + PCI_REGION_IO); + + pci_set_region(&pcie->hose.regions[2], pcie->mem.start, + pcie->mem.start, fdt_resource_size(&pcie->mem), + PCI_REGION_MEM); + + pci_set_region(&pcie->hose.regions[3], pcie->prefetch.start, + pcie->prefetch.start, + fdt_resource_size(&pcie->prefetch), + PCI_REGION_MEM | PCI_REGION_PREFETCH); + + pcie->hose.region_count = 4; + + pci_set_ops(&pcie->hose, + pci_hose_read_config_byte_via_dword, + pci_hose_read_config_word_via_dword, + tegra_pcie_read_conf, + pci_hose_write_config_byte_via_dword, + pci_hose_write_config_word_via_dword, + tegra_pcie_write_conf); + + pci_register_hose(&pcie->hose); + +#ifdef CONFIG_PCI_SCAN_SHOW + printf("PCI: Enumerating devices...\n"); + printf("---------------------------------------\n"); + printf(" Device ID Description\n"); + printf(" ------ -- -----------\n"); +#endif + + pcie->hose.last_busno = pci_hose_scan(&pcie->hose); + } + + return 0; +} + +void pci_init_board(void) +{ + const void *fdt = gd->fdt_blob; + int count, nodes[1]; + + count = fdtdec_find_aliases_for_id(fdt, "pcie-controller", + COMPAT_NVIDIA_TEGRA124_PCIE, + nodes, ARRAY_SIZE(nodes)); + if (process_nodes(fdt, nodes, count)) + return; + + count = fdtdec_find_aliases_for_id(fdt, "pcie-controller", + COMPAT_NVIDIA_TEGRA30_PCIE, + nodes, ARRAY_SIZE(nodes)); + if (process_nodes(fdt, nodes, count)) + return; + + count = fdtdec_find_aliases_for_id(fdt, "pcie-controller", + COMPAT_NVIDIA_TEGRA20_PCIE, + nodes, ARRAY_SIZE(nodes)); + if (process_nodes(fdt, nodes, count)) + return; +} + +int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev) +{ + if (PCI_BUS(dev) != 0 && PCI_DEV(dev) > 0) + return 1; + + return 0; +} diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 04bd996cad..214565241e 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -5,6 +5,7 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_AS3722_POWER) += as3722.o obj-$(CONFIG_AXP152_POWER) += axp152.o obj-$(CONFIG_AXP209_POWER) += axp209.o obj-$(CONFIG_AXP221_POWER) += axp221.o diff --git a/drivers/power/as3722.c b/drivers/power/as3722.c new file mode 100644 index 0000000000..4c6de79cd6 --- /dev/null +++ b/drivers/power/as3722.c @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2014 NVIDIA Corporation + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#define pr_fmt(fmt) "as3722: " fmt + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <i2c.h> + +#include <power/as3722.h> + +#define AS3722_SD_VOLTAGE(n) (0x00 + (n)) +#define AS3722_GPIO_CONTROL(n) (0x08 + (n)) +#define AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH (1 << 0) +#define AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDL (7 << 0) +#define AS3722_GPIO_CONTROL_INVERT (1 << 7) +#define AS3722_LDO_VOLTAGE(n) (0x10 + (n)) +#define AS3722_GPIO_SIGNAL_OUT 0x20 +#define AS3722_SD_CONTROL 0x4d +#define AS3722_LDO_CONTROL 0x4e +#define AS3722_ASIC_ID1 0x90 +#define AS3722_DEVICE_ID 0x0c +#define AS3722_ASIC_ID2 0x91 + +static int as3722_read(struct udevice *pmic, u8 reg, u8 *value) +{ + int err; + + err = i2c_read(pmic, reg, value, 1); + if (err < 0) + return err; + + return 0; +} + +static int as3722_write(struct udevice *pmic, u8 reg, u8 value) +{ + int err; + + err = i2c_write(pmic, reg, &value, 1); + if (err < 0) + return err; + + return 0; +} + +static int as3722_read_id(struct udevice *pmic, u8 *id, u8 *revision) +{ + int err; + + err = as3722_read(pmic, AS3722_ASIC_ID1, id); + if (err) { + error("failed to read ID1 register: %d", err); + return err; + } + + err = as3722_read(pmic, AS3722_ASIC_ID2, revision); + if (err) { + error("failed to read ID2 register: %d", err); + return err; + } + + return 0; +} + +int as3722_sd_enable(struct udevice *pmic, unsigned int sd) +{ + u8 value; + int err; + + if (sd > 6) + return -EINVAL; + + err = as3722_read(pmic, AS3722_SD_CONTROL, &value); + if (err) { + error("failed to read SD control register: %d", err); + return err; + } + + value |= 1 << sd; + + err = as3722_write(pmic, AS3722_SD_CONTROL, value); + if (err < 0) { + error("failed to write SD control register: %d", err); + return err; + } + + return 0; +} + +int as3722_sd_set_voltage(struct udevice *pmic, unsigned int sd, u8 value) +{ + int err; + + if (sd > 6) + return -EINVAL; + + err = as3722_write(pmic, AS3722_SD_VOLTAGE(sd), value); + if (err < 0) { + error("failed to write SD%u voltage register: %d", sd, err); + return err; + } + + return 0; +} + +int as3722_ldo_enable(struct udevice *pmic, unsigned int ldo) +{ + u8 value; + int err; + + if (ldo > 11) + return -EINVAL; + + err = as3722_read(pmic, AS3722_LDO_CONTROL, &value); + if (err) { + error("failed to read LDO control register: %d", err); + return err; + } + + value |= 1 << ldo; + + err = as3722_write(pmic, AS3722_LDO_CONTROL, value); + if (err < 0) { + error("failed to write LDO control register: %d", err); + return err; + } + + return 0; +} + +int as3722_ldo_set_voltage(struct udevice *pmic, unsigned int ldo, u8 value) +{ + int err; + + if (ldo > 11) + return -EINVAL; + + err = as3722_write(pmic, AS3722_LDO_VOLTAGE(ldo), value); + if (err < 0) { + error("failed to write LDO%u voltage register: %d", ldo, + err); + return err; + } + + return 0; +} + +int as3722_gpio_configure(struct udevice *pmic, unsigned int gpio, + unsigned long flags) +{ + u8 value = 0; + int err; + + if (flags & AS3722_GPIO_OUTPUT_VDDH) + value |= AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH; + + if (flags & AS3722_GPIO_INVERT) + value |= AS3722_GPIO_CONTROL_INVERT; + + err = as3722_write(pmic, AS3722_GPIO_CONTROL(gpio), value); + if (err) { + error("failed to configure GPIO#%u: %d", gpio, err); + return err; + } + + return 0; +} + +static int as3722_gpio_set(struct udevice *pmic, unsigned int gpio, + unsigned int level) +{ + const char *l; + u8 value; + int err; + + if (gpio > 7) + return -EINVAL; + + err = as3722_read(pmic, AS3722_GPIO_SIGNAL_OUT, &value); + if (err < 0) { + error("failed to read GPIO signal out register: %d", err); + return err; + } + + if (level == 0) { + value &= ~(1 << gpio); + l = "low"; + } else { + value |= 1 << gpio; + l = "high"; + } + + err = as3722_write(pmic, AS3722_GPIO_SIGNAL_OUT, value); + if (err) { + error("failed to set GPIO#%u %s: %d", gpio, l, err); + return err; + } + + return 0; +} + +int as3722_gpio_direction_output(struct udevice *pmic, unsigned int gpio, + unsigned int level) +{ + u8 value; + int err; + + if (gpio > 7) + return -EINVAL; + + if (level == 0) + value = AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDL; + else + value = AS3722_GPIO_CONTROL_MODE_OUTPUT_VDDH; + + err = as3722_write(pmic, AS3722_GPIO_CONTROL(gpio), value); + if (err) { + error("failed to configure GPIO#%u as output: %d", gpio, err); + return err; + } + + err = as3722_gpio_set(pmic, gpio, level); + if (err < 0) { + error("failed to set GPIO#%u high: %d", gpio, err); + return err; + } + + return 0; +} + +int as3722_init(struct udevice **devp) +{ + struct udevice *pmic; + u8 id, revision; + const unsigned int bus = 0; + const unsigned int address = 0x40; + int err; + + err = i2c_get_chip_for_busnum(bus, address, &pmic); + if (err) + return err; + err = as3722_read_id(pmic, &id, &revision); + if (err < 0) { + error("failed to read ID: %d", err); + return err; + } + + if (id != AS3722_DEVICE_ID) { + error("unknown device"); + return -ENOENT; + } + + debug("AS3722 revision %#x found on I2C bus %u, address %#x\n", + revision, bus, address); + *devp = pmic; + + return 0; +} diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 8c84942761..4cc00cd2f8 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -43,7 +43,7 @@ obj-$(CONFIG_ARC_SERIAL) += serial_arc.o obj-$(CONFIG_TEGRA_SERIAL) += serial_tegra.o obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o obj-$(CONFIG_OMAP_SERIAL) += serial_omap.o -obj-$(CONFIG_COREBOOT_SERIAL) += serial_coreboot.o +obj-$(CONFIG_X86_SERIAL) += serial_x86.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_USB_TTY) += usbtty.o diff --git a/drivers/serial/serial_coreboot.c b/drivers/serial/serial_x86.c index 5c6a76c59c..e81e035ec2 100644 --- a/drivers/serial/serial_coreboot.c +++ b/drivers/serial/serial_x86.c @@ -9,12 +9,12 @@ #include <ns16550.h> #include <serial.h> -static const struct udevice_id coreboot_serial_ids[] = { - { .compatible = "coreboot-uart" }, +static const struct udevice_id x86_serial_ids[] = { + { .compatible = "x86-uart" }, { } }; -static int coreboot_serial_ofdata_to_platdata(struct udevice *dev) +static int x86_serial_ofdata_to_platdata(struct udevice *dev) { struct ns16550_platdata *plat = dev_get_platdata(dev); int ret; @@ -27,10 +27,10 @@ static int coreboot_serial_ofdata_to_platdata(struct udevice *dev) return 0; } U_BOOT_DRIVER(serial_ns16550) = { - .name = "serial_coreboot", + .name = "serial_x86", .id = UCLASS_SERIAL, - .of_match = coreboot_serial_ids, - .ofdata_to_platdata = coreboot_serial_ofdata_to_platdata, + .of_match = x86_serial_ids, + .ofdata_to_platdata = x86_serial_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct ns16550_platdata), .priv_auto_alloc_size = sizeof(struct NS16550), .probe = ns16550_serial_probe, diff --git a/drivers/usb/eth/Makefile b/drivers/usb/eth/Makefile index e6ae9f1e52..c92d2b02d2 100644 --- a/drivers/usb/eth/Makefile +++ b/drivers/usb/eth/Makefile @@ -6,5 +6,6 @@ # new USB host ethernet layer dependencies obj-$(CONFIG_USB_HOST_ETHER) += usb_ether.o obj-$(CONFIG_USB_ETHER_ASIX) += asix.o +obj-$(CONFIG_USB_ETHER_ASIX88179) += asix88179.o obj-$(CONFIG_USB_ETHER_MCS7830) += mcs7830.o obj-$(CONFIG_USB_ETHER_SMSC95XX) += smsc95xx.o diff --git a/drivers/usb/eth/asix88179.c b/drivers/usb/eth/asix88179.c new file mode 100644 index 0000000000..b8ca720e25 --- /dev/null +++ b/drivers/usb/eth/asix88179.c @@ -0,0 +1,700 @@ +/* + * Copyright (c) 2014 Rene Griessl <rgriessl@cit-ec.uni-bielefeld.de> + * based on the U-Boot Asix driver as well as information + * from the Linux AX88179_178a driver + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <usb.h> +#include <net.h> +#include <linux/mii.h> +#include "usb_ether.h" +#include <malloc.h> +#include <errno.h> + +/* ASIX AX88179 based USB 3.0 Ethernet Devices */ +#define AX88179_PHY_ID 0x03 +#define AX_EEPROM_LEN 0x100 +#define AX88179_EEPROM_MAGIC 0x17900b95 +#define AX_MCAST_FLTSIZE 8 +#define AX_MAX_MCAST 64 +#define AX_INT_PPLS_LINK (1 << 16) +#define AX_RXHDR_L4_TYPE_MASK 0x1c +#define AX_RXHDR_L4_TYPE_UDP 4 +#define AX_RXHDR_L4_TYPE_TCP 16 +#define AX_RXHDR_L3CSUM_ERR 2 +#define AX_RXHDR_L4CSUM_ERR 1 +#define AX_RXHDR_CRC_ERR (1 << 29) +#define AX_RXHDR_DROP_ERR (1 << 31) +#define AX_ENDPOINT_INT 0x01 +#define AX_ENDPOINT_IN 0x02 +#define AX_ENDPOINT_OUT 0x03 +#define AX_ACCESS_MAC 0x01 +#define AX_ACCESS_PHY 0x02 +#define AX_ACCESS_EEPROM 0x04 +#define AX_ACCESS_EFUS 0x05 +#define AX_PAUSE_WATERLVL_HIGH 0x54 +#define AX_PAUSE_WATERLVL_LOW 0x55 + +#define PHYSICAL_LINK_STATUS 0x02 + #define AX_USB_SS (1 << 2) + #define AX_USB_HS (1 << 1) + +#define GENERAL_STATUS 0x03 + #define AX_SECLD (1 << 2) + +#define AX_SROM_ADDR 0x07 +#define AX_SROM_CMD 0x0a + #define EEP_RD (1 << 2) + #define EEP_BUSY (1 << 4) + +#define AX_SROM_DATA_LOW 0x08 +#define AX_SROM_DATA_HIGH 0x09 + +#define AX_RX_CTL 0x0b + #define AX_RX_CTL_DROPCRCERR (1 << 8) + #define AX_RX_CTL_IPE (1 << 9) + #define AX_RX_CTL_START (1 << 7) + #define AX_RX_CTL_AP (1 << 5) + #define AX_RX_CTL_AM (1 << 4) + #define AX_RX_CTL_AB (1 << 3) + #define AX_RX_CTL_AMALL (1 << 1) + #define AX_RX_CTL_PRO (1 << 0) + #define AX_RX_CTL_STOP 0 + +#define AX_NODE_ID 0x10 +#define AX_MULFLTARY 0x16 + +#define AX_MEDIUM_STATUS_MODE 0x22 + #define AX_MEDIUM_GIGAMODE (1 << 0) + #define AX_MEDIUM_FULL_DUPLEX (1 << 1) + #define AX_MEDIUM_EN_125MHZ (1 << 3) + #define AX_MEDIUM_RXFLOW_CTRLEN (1 << 4) + #define AX_MEDIUM_TXFLOW_CTRLEN (1 << 5) + #define AX_MEDIUM_RECEIVE_EN (1 << 8) + #define AX_MEDIUM_PS (1 << 9) + #define AX_MEDIUM_JUMBO_EN 0x8040 + +#define AX_MONITOR_MOD 0x24 + #define AX_MONITOR_MODE_RWLC (1 << 1) + #define AX_MONITOR_MODE_RWMP (1 << 2) + #define AX_MONITOR_MODE_PMEPOL (1 << 5) + #define AX_MONITOR_MODE_PMETYPE (1 << 6) + +#define AX_GPIO_CTRL 0x25 + #define AX_GPIO_CTRL_GPIO3EN (1 << 7) + #define AX_GPIO_CTRL_GPIO2EN (1 << 6) + #define AX_GPIO_CTRL_GPIO1EN (1 << 5) + +#define AX_PHYPWR_RSTCTL 0x26 + #define AX_PHYPWR_RSTCTL_BZ (1 << 4) + #define AX_PHYPWR_RSTCTL_IPRL (1 << 5) + #define AX_PHYPWR_RSTCTL_AT (1 << 12) + +#define AX_RX_BULKIN_QCTRL 0x2e +#define AX_CLK_SELECT 0x33 + #define AX_CLK_SELECT_BCS (1 << 0) + #define AX_CLK_SELECT_ACS (1 << 1) + #define AX_CLK_SELECT_ULR (1 << 3) + +#define AX_RXCOE_CTL 0x34 + #define AX_RXCOE_IP (1 << 0) + #define AX_RXCOE_TCP (1 << 1) + #define AX_RXCOE_UDP (1 << 2) + #define AX_RXCOE_TCPV6 (1 << 5) + #define AX_RXCOE_UDPV6 (1 << 6) + +#define AX_TXCOE_CTL 0x35 + #define AX_TXCOE_IP (1 << 0) + #define AX_TXCOE_TCP (1 << 1) + #define AX_TXCOE_UDP (1 << 2) + #define AX_TXCOE_TCPV6 (1 << 5) + #define AX_TXCOE_UDPV6 (1 << 6) + +#define AX_LEDCTRL 0x73 + +#define GMII_PHY_PHYSR 0x11 + #define GMII_PHY_PHYSR_SMASK 0xc000 + #define GMII_PHY_PHYSR_GIGA (1 << 15) + #define GMII_PHY_PHYSR_100 (1 << 14) + #define GMII_PHY_PHYSR_FULL (1 << 13) + #define GMII_PHY_PHYSR_LINK (1 << 10) + +#define GMII_LED_ACT 0x1a + #define GMII_LED_ACTIVE_MASK 0xff8f + #define GMII_LED0_ACTIVE (1 << 4) + #define GMII_LED1_ACTIVE (1 << 5) + #define GMII_LED2_ACTIVE (1 << 6) + +#define GMII_LED_LINK 0x1c + #define GMII_LED_LINK_MASK 0xf888 + #define GMII_LED0_LINK_10 (1 << 0) + #define GMII_LED0_LINK_100 (1 << 1) + #define GMII_LED0_LINK_1000 (1 << 2) + #define GMII_LED1_LINK_10 (1 << 4) + #define GMII_LED1_LINK_100 (1 << 5) + #define GMII_LED1_LINK_1000 (1 << 6) + #define GMII_LED2_LINK_10 (1 << 8) + #define GMII_LED2_LINK_100 (1 << 9) + #define GMII_LED2_LINK_1000 (1 << 10) + #define LED0_ACTIVE (1 << 0) + #define LED0_LINK_10 (1 << 1) + #define LED0_LINK_100 (1 << 2) + #define LED0_LINK_1000 (1 << 3) + #define LED0_FD (1 << 4) + #define LED0_USB3_MASK 0x001f + #define LED1_ACTIVE (1 << 5) + #define LED1_LINK_10 (1 << 6) + #define LED1_LINK_100 (1 << 7) + #define LED1_LINK_1000 (1 << 8) + #define LED1_FD (1 << 9) + #define LED1_USB3_MASK 0x03e0 + #define LED2_ACTIVE (1 << 10) + #define LED2_LINK_1000 (1 << 13) + #define LED2_LINK_100 (1 << 12) + #define LED2_LINK_10 (1 << 11) + #define LED2_FD (1 << 14) + #define LED_VALID (1 << 15) + #define LED2_USB3_MASK 0x7c00 + +#define GMII_PHYPAGE 0x1e +#define GMII_PHY_PAGE_SELECT 0x1f + #define GMII_PHY_PGSEL_EXT 0x0007 + #define GMII_PHY_PGSEL_PAGE0 0x0000 + +/* local defines */ +#define ASIX_BASE_NAME "axg" +#define USB_CTRL_SET_TIMEOUT 5000 +#define USB_CTRL_GET_TIMEOUT 5000 +#define USB_BULK_SEND_TIMEOUT 5000 +#define USB_BULK_RECV_TIMEOUT 5000 + +#define AX_RX_URB_SIZE 1024 * 0x12 +#define BLK_FRAME_SIZE 0x200 +#define PHY_CONNECT_TIMEOUT 5000 + +#define TIMEOUT_RESOLUTION 50 /* ms */ + +#define FLAG_NONE 0 +#define FLAG_TYPE_AX88179 (1U << 0) +#define FLAG_TYPE_AX88178a (1U << 1) +#define FLAG_TYPE_DLINK_DUB1312 (1U << 2) +#define FLAG_TYPE_SITECOM (1U << 3) +#define FLAG_TYPE_SAMSUNG (1U << 4) +#define FLAG_TYPE_LENOVO (1U << 5) + +/* local vars */ +static const struct { + unsigned char ctrl, timer_l, timer_h, size, ifg; +} AX88179_BULKIN_SIZE[] = { + {7, 0x4f, 0, 0x02, 0xff}, + {7, 0x20, 3, 0x03, 0xff}, + {7, 0xae, 7, 0x04, 0xff}, + {7, 0xcc, 0x4c, 0x04, 8}, +}; + +static int curr_eth_dev; /* index for name of next device detected */ + +/* driver private */ +struct asix_private { + int flags; + int rx_urb_size; + int maxpacketsize; +}; + +/* + * Asix infrastructure commands + */ +static int asix_write_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, size); + + debug("asix_write_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", + cmd, value, index, size); + + memcpy(buf, data, size); + + len = usb_control_msg( + dev->pusb_dev, + usb_sndctrlpipe(dev->pusb_dev, 0), + cmd, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + buf, + size, + USB_CTRL_SET_TIMEOUT); + + return len == size ? 0 : ECOMM; +} + +static int asix_read_cmd(struct ueth_data *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data) +{ + int len; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buf, size); + + debug("asix_read_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d\n", + cmd, value, index, size); + + len = usb_control_msg( + dev->pusb_dev, + usb_rcvctrlpipe(dev->pusb_dev, 0), + cmd, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + buf, + size, + USB_CTRL_GET_TIMEOUT); + + memcpy(data, buf, size); + + return len == size ? 0 : ECOMM; +} + +static int asix_read_mac(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + u8 buf[ETH_ALEN]; + + asix_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, 6, 6, buf); + debug("asix_read_mac() returning %02x:%02x:%02x:%02x:%02x:%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); + + memcpy(eth->enetaddr, buf, ETH_ALEN); + + return 0; +} + +static int asix_basic_reset(struct ueth_data *dev) +{ + struct asix_private *dev_priv = (struct asix_private *)dev->dev_priv; + u8 buf[5]; + u16 *tmp16; + u8 *tmp; + + tmp16 = (u16 *)buf; + tmp = (u8 *)buf; + + /* Power up ethernet PHY */ + *tmp16 = 0; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); + + *tmp16 = AX_PHYPWR_RSTCTL_IPRL; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16); + mdelay(200); + + *tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp); + mdelay(200); + + /* RX bulk configuration */ + memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); + asix_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); + + dev_priv->rx_urb_size = 128 * 20; + + /* Water Level configuration */ + *tmp = 0x34; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp); + + *tmp = 0x52; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp); + + /* Enable checksum offload */ + *tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | + AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp); + + *tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP | + AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp); + + /* Configure RX control register => start operation */ + *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | + AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16); + + *tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | + AX_MONITOR_MODE_RWMP; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp); + + /* Configure default medium type => giga */ + *tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | + AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX | + AX_MEDIUM_GIGAMODE | AX_MEDIUM_JUMBO_EN; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, tmp16); + + u16 adv = 0; + adv = ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_LPACK | + ADVERTISE_NPAGE | ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP; + asix_write_cmd(dev, AX_ACCESS_PHY, 0x03, MII_ADVERTISE, 2, &adv); + + adv = ADVERTISE_1000FULL; + asix_write_cmd(dev, AX_ACCESS_PHY, 0x03, MII_CTRL1000, 2, &adv); + + return 0; +} + +static int asix_wait_link(struct ueth_data *dev) +{ + int timeout = 0; + int link_detected; + u8 buf[2]; + u16 *tmp16; + + tmp16 = (u16 *)buf; + + do { + asix_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + MII_BMSR, 2, buf); + link_detected = *tmp16 & BMSR_LSTATUS; + if (!link_detected) { + if (timeout == 0) + printf("Waiting for Ethernet connection... "); + mdelay(TIMEOUT_RESOLUTION); + timeout += TIMEOUT_RESOLUTION; + } + } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT); + + if (link_detected) { + if (timeout > 0) + printf("done.\n"); + return 0; + } else { + printf("unable to connect.\n"); + return -ENETUNREACH; + } +} + +/* + * Asix callbacks + */ +static int asix_init(struct eth_device *eth, bd_t *bd) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct asix_private *dev_priv = (struct asix_private *)dev->dev_priv; + u8 buf[2], tmp[5], link_sts; + u16 *tmp16, mode; + + + tmp16 = (u16 *)buf; + + debug("** %s()\n", __func__); + + /* Configure RX control register => start operation */ + *tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | + AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; + if (asix_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16) != 0) + goto out_err; + + if (asix_wait_link(dev) != 0) { + /*reset device and try again*/ + printf("Reset Ethernet Device\n"); + asix_basic_reset(dev); + if (asix_wait_link(dev) != 0) + goto out_err; + } + + /* Configure link */ + mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | + AX_MEDIUM_RXFLOW_CTRLEN; + + asix_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, + 1, 1, &link_sts); + + asix_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + GMII_PHY_PHYSR, 2, tmp16); + + if (!(*tmp16 & GMII_PHY_PHYSR_LINK)) { + return 0; + } else if (GMII_PHY_PHYSR_GIGA == (*tmp16 & GMII_PHY_PHYSR_SMASK)) { + mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_EN_125MHZ | + AX_MEDIUM_JUMBO_EN; + + if (link_sts & AX_USB_SS) + memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5); + else if (link_sts & AX_USB_HS) + memcpy(tmp, &AX88179_BULKIN_SIZE[1], 5); + else + memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); + } else if (GMII_PHY_PHYSR_100 == (*tmp16 & GMII_PHY_PHYSR_SMASK)) { + mode |= AX_MEDIUM_PS; + + if (link_sts & (AX_USB_SS | AX_USB_HS)) + memcpy(tmp, &AX88179_BULKIN_SIZE[2], 5); + else + memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); + } else { + memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5); + } + + /* RX bulk configuration */ + asix_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp); + + dev_priv->rx_urb_size = (1024 * (tmp[3] + 2)); + if (*tmp16 & GMII_PHY_PHYSR_FULL) + mode |= AX_MEDIUM_FULL_DUPLEX; + asix_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, + 2, 2, &mode); + + return 0; +out_err: + return -1; +} + +static int asix_send(struct eth_device *eth, void *packet, int length) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct asix_private *dev_priv = (struct asix_private *)dev->dev_priv; + + int err; + u32 packet_len, tx_hdr2; + int actual_len, framesize; + ALLOC_CACHE_ALIGN_BUFFER(unsigned char, msg, + PKTSIZE + (2 * sizeof(packet_len))); + + debug("** %s(), len %d\n", __func__, length); + + packet_len = length; + cpu_to_le32s(&packet_len); + + memcpy(msg, &packet_len, sizeof(packet_len)); + framesize = dev_priv->maxpacketsize; + tx_hdr2 = 0; + if (((length + 8) % framesize) == 0) + tx_hdr2 |= 0x80008000; /* Enable padding */ + + cpu_to_le32s(&tx_hdr2); + + memcpy(msg + sizeof(packet_len), &tx_hdr2, sizeof(tx_hdr2)); + + memcpy(msg + sizeof(packet_len) + sizeof(tx_hdr2), + (void *)packet, length); + + err = usb_bulk_msg(dev->pusb_dev, + usb_sndbulkpipe(dev->pusb_dev, dev->ep_out), + (void *)msg, + length + sizeof(packet_len) + sizeof(tx_hdr2), + &actual_len, + USB_BULK_SEND_TIMEOUT); + debug("Tx: len = %u, actual = %u, err = %d\n", + length + sizeof(packet_len), actual_len, err); + + return err; +} + +static int asix_recv(struct eth_device *eth) +{ + struct ueth_data *dev = (struct ueth_data *)eth->priv; + struct asix_private *dev_priv = (struct asix_private *)dev->dev_priv; + + u16 frame_pos; + int err; + int actual_len; + + int pkt_cnt; + u32 rx_hdr; + u16 hdr_off; + u32 *pkt_hdr; + ALLOC_CACHE_ALIGN_BUFFER(u8, recv_buf, dev_priv->rx_urb_size); + + actual_len = -1; + + debug("** %s()\n", __func__); + + err = usb_bulk_msg(dev->pusb_dev, + usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in), + (void *)recv_buf, + dev_priv->rx_urb_size, + &actual_len, + USB_BULK_RECV_TIMEOUT); + debug("Rx: len = %u, actual = %u, err = %d\n", dev_priv->rx_urb_size, + actual_len, err); + + if (err != 0) { + debug("Rx: failed to receive\n"); + return -ECOMM; + } + if (actual_len > dev_priv->rx_urb_size) { + debug("Rx: received too many bytes %d\n", actual_len); + return -EMSGSIZE; + } + + + rx_hdr = *(u32 *)(recv_buf + actual_len - 4); + le32_to_cpus(&pkt_hdr); + + pkt_cnt = (u16)rx_hdr; + hdr_off = (u16)(rx_hdr >> 16); + pkt_hdr = (u32 *)(recv_buf + hdr_off); + + + frame_pos = 0; + + while (pkt_cnt--) { + u16 pkt_len; + + le32_to_cpus(pkt_hdr); + pkt_len = (*pkt_hdr >> 16) & 0x1fff; + + frame_pos += 2; + + NetReceive(recv_buf + frame_pos, pkt_len); + + pkt_hdr++; + frame_pos += ((pkt_len + 7) & 0xFFF8)-2; + + if (pkt_cnt == 0) + return 0; + } + return err; +} + +static void asix_halt(struct eth_device *eth) +{ + debug("** %s()\n", __func__); +} + +/* + * Asix probing functions + */ +void ax88179_eth_before_probe(void) +{ + curr_eth_dev = 0; +} + +struct asix_dongle { + unsigned short vendor; + unsigned short product; + int flags; +}; + +static const struct asix_dongle asix_dongles[] = { + { 0x0b95, 0x1790, FLAG_TYPE_AX88179 }, + { 0x0b95, 0x178a, FLAG_TYPE_AX88178a }, + { 0x2001, 0x4a00, FLAG_TYPE_DLINK_DUB1312 }, + { 0x0df6, 0x0072, FLAG_TYPE_SITECOM }, + { 0x04e8, 0xa100, FLAG_TYPE_SAMSUNG }, + { 0x17ef, 0x304b, FLAG_TYPE_LENOVO }, + { 0x0000, 0x0000, FLAG_NONE } /* END - Do not remove */ +}; + +/* Probe to see if a new device is actually an asix device */ +int ax88179_eth_probe(struct usb_device *dev, unsigned int ifnum, + struct ueth_data *ss) +{ + struct usb_interface *iface; + struct usb_interface_descriptor *iface_desc; + struct asix_private *dev_priv; + int ep_in_found = 0, ep_out_found = 0; + int i; + + /* let's examine the device now */ + iface = &dev->config.if_desc[ifnum]; + iface_desc = &dev->config.if_desc[ifnum].desc; + + for (i = 0; asix_dongles[i].vendor != 0; i++) { + if (dev->descriptor.idVendor == asix_dongles[i].vendor && + dev->descriptor.idProduct == asix_dongles[i].product) + /* Found a supported dongle */ + break; + } + + if (asix_dongles[i].vendor == 0) + return 0; + + memset(ss, 0, sizeof(struct ueth_data)); + + /* At this point, we know we've got a live one */ + debug("\n\nUSB Ethernet device detected: %#04x:%#04x\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); + + /* Initialize the ueth_data structure with some useful info */ + ss->ifnum = ifnum; + ss->pusb_dev = dev; + ss->subclass = iface_desc->bInterfaceSubClass; + ss->protocol = iface_desc->bInterfaceProtocol; + + /* alloc driver private */ + ss->dev_priv = calloc(1, sizeof(struct asix_private)); + if (!ss->dev_priv) + return 0; + dev_priv = ss->dev_priv; + dev_priv->flags = asix_dongles[i].flags; + + /* + * We are expecting a minimum of 3 endpoints - in, out (bulk), and + * int. We will ignore any others. + */ + for (i = 0; i < iface_desc->bNumEndpoints; i++) { + /* is it an interrupt endpoint? */ + if ((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { + ss->ep_int = iface->ep_desc[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ss->irqinterval = iface->ep_desc[i].bInterval; + continue; + } + + /* is it an BULK endpoint? */ + if (!((iface->ep_desc[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)) + continue; + + u8 ep_addr = iface->ep_desc[i].bEndpointAddress; + if ((ep_addr & USB_DIR_IN) && !ep_in_found) { + ss->ep_in = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + ep_in_found = 1; + } + if (!(ep_addr & USB_DIR_IN) && !ep_out_found) { + ss->ep_out = ep_addr & + USB_ENDPOINT_NUMBER_MASK; + dev_priv->maxpacketsize = + dev->epmaxpacketout[AX_ENDPOINT_OUT]; + ep_out_found = 1; + } + } + debug("Endpoints In %d Out %d Int %d\n", + ss->ep_in, ss->ep_out, ss->ep_int); + + /* Do some basic sanity checks, and bail if we find a problem */ + if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) || + !ss->ep_in || !ss->ep_out || !ss->ep_int) { + debug("Problems with device\n"); + return 0; + } + dev->privptr = (void *)ss; + return 1; +} + +int ax88179_eth_get_info(struct usb_device *dev, struct ueth_data *ss, + struct eth_device *eth) +{ + if (!eth) { + debug("%s: missing parameter.\n", __func__); + return 0; + } + sprintf(eth->name, "%s%d", ASIX_BASE_NAME, curr_eth_dev++); + eth->init = asix_init; + eth->send = asix_send; + eth->recv = asix_recv; + eth->halt = asix_halt; + eth->priv = ss; + + if (asix_basic_reset(ss)) + return 0; + + /* Get the MAC address */ + if (asix_read_mac(eth)) + return 0; + debug("MAC %pM\n", eth->enetaddr); + + return 1; +} diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c index 1dda54c2f1..7cb96e3bf6 100644 --- a/drivers/usb/eth/usb_ether.c +++ b/drivers/usb/eth/usb_ether.c @@ -30,6 +30,13 @@ static const struct usb_eth_prob_dev prob_dev[] = { .get_info = asix_eth_get_info, }, #endif +#ifdef CONFIG_USB_ETHER_ASIX88179 + { + .before_probe = ax88179_eth_before_probe, + .probe = ax88179_eth_probe, + .get_info = ax88179_eth_get_info, + }, +#endif #ifdef CONFIG_USB_ETHER_MCS7830 { .before_probe = mcs7830_eth_before_probe, diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 12628effe8..fbc74f3bed 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1062,7 +1062,6 @@ static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep) if ((epstatus & epctrl) & USBA_RX_BK_RDY) { DBG(DBG_BUS, "%s: RX data ready\n", ep->ep.name); receive_data(ep); - usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); } } diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index 16fc9ddf82..ead71eba6b 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -366,7 +366,7 @@ static int state_dfu_idle(struct f_dfu *f_dfu, to_runtime_mode(f_dfu); f_dfu->dfu_state = DFU_STATE_appIDLE; - dfu_trigger_detach(); + g_dnl_trigger_detach(); break; default: f_dfu->dfu_state = DFU_STATE_dfuERROR; diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index 71b62e5005..310175acfe 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -480,6 +480,17 @@ static void cb_boot(struct usb_ep *ep, struct usb_request *req) fastboot_tx_write_str("OKAY"); } +static void do_exit_on_complete(struct usb_ep *ep, struct usb_request *req) +{ + g_dnl_trigger_detach(); +} + +static void cb_continue(struct usb_ep *ep, struct usb_request *req) +{ + fastboot_func->in_req->complete = do_exit_on_complete; + fastboot_tx_write_str("OKAY"); +} + #ifdef CONFIG_FASTBOOT_FLASH static void cb_flash(struct usb_ep *ep, struct usb_request *req) { @@ -520,6 +531,9 @@ static const struct cmd_dispatch_info cmd_dispatch_info[] = { }, { .cmd = "boot", .cb = cb_boot, + }, { + .cmd = "continue", + .cb = cb_continue, }, #ifdef CONFIG_FASTBOOT_FLASH { diff --git a/drivers/usb/gadget/f_thor.c b/drivers/usb/gadget/f_thor.c index 78519fa41f..2d0410d795 100644 --- a/drivers/usb/gadget/f_thor.c +++ b/drivers/usb/gadget/f_thor.c @@ -205,12 +205,24 @@ static long long int download_head(unsigned long long total, static int download_tail(long long int left, int cnt) { - struct dfu_entity *dfu_entity = dfu_get_entity(alt_setting_num); - void *transfer_buffer = dfu_get_buf(dfu_entity); + struct dfu_entity *dfu_entity; + void *transfer_buffer; int ret; debug("%s: left: %llu cnt: %d\n", __func__, left, cnt); + dfu_entity = dfu_get_entity(alt_setting_num); + if (!dfu_entity) { + error("Alt setting: %d entity not found!\n", alt_setting_num); + return -ENOENT; + } + + transfer_buffer = dfu_get_buf(dfu_entity); + if (!transfer_buffer) { + error("Transfer buffer not allocated!"); + return -ENXIO; + } + if (left) { ret = dfu_write(dfu_entity, transfer_buffer, left, cnt++); if (ret) { diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index 25611acd60..ee52a29467 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -163,6 +163,23 @@ __weak int g_dnl_board_usb_cable_connected(void) return -EOPNOTSUPP; } +static bool g_dnl_detach_request; + +bool g_dnl_detach(void) +{ + return g_dnl_detach_request; +} + +void g_dnl_trigger_detach(void) +{ + g_dnl_detach_request = true; +} + +void g_dnl_clear_detach(void) +{ + g_dnl_detach_request = false; +} + static int g_dnl_get_bcd_device_number(struct usb_composite_dev *cdev) { struct usb_gadget *gadget = cdev->gadget; |