diff options
Diffstat (limited to 'drivers')
135 files changed, 8928 insertions, 612 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 70993fd..c82a94b 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -60,6 +60,8 @@ source "drivers/sound/Kconfig" source "drivers/spi/Kconfig" +source "drivers/spmi/Kconfig" + source "drivers/thermal/Kconfig" source "drivers/timer/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index e7eab66..6900097 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -54,6 +54,7 @@ obj-y += dfu/ obj-$(CONFIG_X86) += pch/ obj-y += rtc/ obj-y += sound/ +obj-y += spmi/ obj-y += timer/ obj-y += tpm/ obj-y += twserial/ diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index f35c4d4..fcc9ccd 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -18,3 +18,12 @@ config DISK types can use this, such as AHCI/SATA. It does not provide any standard operations at present. The block device interface has not been converted to driver model. + +config BLOCK_CACHE + bool "Use block device cache" + default n + help + This option enables a disk-block cache for all block devices. + This is most useful when accessing filesystems under U-Boot since + it will prevent repeated reads from directory structures and other + filesystem data structures. diff --git a/drivers/block/Makefile b/drivers/block/Makefile index b5c7ae1..a43492f 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_LIBATA) += libata.o obj-$(CONFIG_MVSATA_IDE) += mvsata_ide.o obj-$(CONFIG_MX51_PATA) += mxc_ata.o obj-$(CONFIG_PATA_BFIN) += pata_bfin.o +obj-$(CONFIG_SATA_CEVA) += sata_ceva.o obj-$(CONFIG_SATA_DWC) += sata_dwc.o obj-$(CONFIG_SATA_MV) += sata_mv.o obj-$(CONFIG_SATA_SIL3114) += sata_sil3114.o @@ -24,3 +25,4 @@ obj-$(CONFIG_IDE_SIL680) += sil680.o obj-$(CONFIG_SANDBOX) += sandbox.o obj-$(CONFIG_SCSI_SYM53C8XX) += sym53c8xx.o obj-$(CONFIG_SYSTEMACE) += systemace.o +obj-$(CONFIG_BLOCK_CACHE) += blkcache.o diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c index 49df2a6..617db22 100644 --- a/drivers/block/blk-uclass.c +++ b/drivers/block/blk-uclass.c @@ -80,11 +80,20 @@ unsigned long blk_dread(struct blk_desc *block_dev, lbaint_t start, { struct udevice *dev = block_dev->bdev; const struct blk_ops *ops = blk_get_ops(dev); + ulong blks_read; if (!ops->read) return -ENOSYS; - return ops->read(dev, start, blkcnt, buffer); + if (blkcache_read(block_dev->if_type, block_dev->devnum, + start, blkcnt, block_dev->blksz, buffer)) + return blkcnt; + blks_read = ops->read(dev, start, blkcnt, buffer); + if (blks_read == blkcnt) + blkcache_fill(block_dev->if_type, block_dev->devnum, + start, blkcnt, block_dev->blksz, buffer); + + return blks_read; } unsigned long blk_dwrite(struct blk_desc *block_dev, lbaint_t start, @@ -96,6 +105,7 @@ unsigned long blk_dwrite(struct blk_desc *block_dev, lbaint_t start, if (!ops->write) return -ENOSYS; + blkcache_invalidate(block_dev->if_type, block_dev->devnum); return ops->write(dev, start, blkcnt, buffer); } @@ -108,6 +118,7 @@ unsigned long blk_derase(struct blk_desc *block_dev, lbaint_t start, if (!ops->erase) return -ENOSYS; + blkcache_invalidate(block_dev->if_type, block_dev->devnum); return ops->erase(dev, start, blkcnt); } diff --git a/drivers/block/blkcache.c b/drivers/block/blkcache.c new file mode 100644 index 0000000..46a6059 --- /dev/null +++ b/drivers/block/blkcache.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) Nelson Integration, LLC 2016 + * Author: Eric Nelson<eric@nelint.com> + * + * SPDX-License-Identifier: GPL-2.0+ + * + */ +#include <config.h> +#include <common.h> +#include <malloc.h> +#include <part.h> +#include <linux/ctype.h> +#include <linux/list.h> + +struct block_cache_node { + struct list_head lh; + int iftype; + int devnum; + lbaint_t start; + lbaint_t blkcnt; + unsigned long blksz; + char *cache; +}; + +static LIST_HEAD(block_cache); + +static struct block_cache_stats _stats = { + .max_blocks_per_entry = 2, + .max_entries = 32 +}; + +static struct block_cache_node *cache_find(int iftype, int devnum, + lbaint_t start, lbaint_t blkcnt, + unsigned long blksz) +{ + struct block_cache_node *node; + + list_for_each_entry(node, &block_cache, lh) + if ((node->iftype == iftype) && + (node->devnum == devnum) && + (node->blksz == blksz) && + (node->start <= start) && + (node->start + node->blkcnt >= start + blkcnt)) { + if (block_cache.next != &node->lh) { + /* maintain MRU ordering */ + list_del(&node->lh); + list_add(&node->lh, &block_cache); + } + return node; + } + return 0; +} + +int blkcache_read(int iftype, int devnum, + lbaint_t start, lbaint_t blkcnt, + unsigned long blksz, void *buffer) +{ + struct block_cache_node *node = cache_find(iftype, devnum, start, + blkcnt, blksz); + if (node) { + const char *src = node->cache + (start - node->start) * blksz; + memcpy(buffer, src, blksz * blkcnt); + debug("hit: start " LBAF ", count " LBAFU "\n", + start, blkcnt); + ++_stats.hits; + return 1; + } + + debug("miss: start " LBAF ", count " LBAFU "\n", + start, blkcnt); + ++_stats.misses; + return 0; +} + +void blkcache_fill(int iftype, int devnum, + lbaint_t start, lbaint_t blkcnt, + unsigned long blksz, void const *buffer) +{ + lbaint_t bytes; + struct block_cache_node *node; + + /* don't cache big stuff */ + if (blkcnt > _stats.max_blocks_per_entry) + return; + + if (_stats.max_entries == 0) + return; + + bytes = blksz * blkcnt; + if (_stats.max_entries <= _stats.entries) { + /* pop LRU */ + node = (struct block_cache_node *)block_cache.prev; + list_del(&node->lh); + _stats.entries--; + debug("drop: start " LBAF ", count " LBAFU "\n", + node->start, node->blkcnt); + if (node->blkcnt * node->blksz < bytes) { + free(node->cache); + node->cache = 0; + } + } else { + node = malloc(sizeof(*node)); + if (!node) + return; + node->cache = 0; + } + + if (!node->cache) { + node->cache = malloc(bytes); + if (!node->cache) { + free(node); + return; + } + } + + debug("fill: start " LBAF ", count " LBAFU "\n", + start, blkcnt); + + node->iftype = iftype; + node->devnum = devnum; + node->start = start; + node->blkcnt = blkcnt; + node->blksz = blksz; + memcpy(node->cache, buffer, bytes); + list_add(&node->lh, &block_cache); + _stats.entries++; +} + +void blkcache_invalidate(int iftype, int devnum) +{ + struct list_head *entry, *n; + struct block_cache_node *node; + + list_for_each_safe(entry, n, &block_cache) { + node = (struct block_cache_node *)entry; + if ((node->iftype == iftype) && + (node->devnum == devnum)) { + list_del(entry); + free(node->cache); + free(node); + --_stats.entries; + } + } +} + +void blkcache_configure(unsigned blocks, unsigned entries) +{ + struct block_cache_node *node; + if ((blocks != _stats.max_blocks_per_entry) || + (entries != _stats.max_entries)) { + /* invalidate cache */ + while (!list_empty(&block_cache)) { + node = (struct block_cache_node *)block_cache.next; + list_del(&node->lh); + free(node->cache); + free(node); + } + _stats.entries = 0; + } + + _stats.max_blocks_per_entry = blocks; + _stats.max_entries = entries; + + _stats.hits = 0; + _stats.misses = 0; +} + +void blkcache_stats(struct block_cache_stats *stats) +{ + memcpy(stats, &_stats, sizeof(*stats)); + _stats.hits = 0; + _stats.misses = 0; +} diff --git a/drivers/block/sata_ceva.c b/drivers/block/sata_ceva.c new file mode 100644 index 0000000..dcc3b90 --- /dev/null +++ b/drivers/block/sata_ceva.c @@ -0,0 +1,113 @@ +/* + * (C) Copyright 2015 - 2016 Xilinx, Inc. + * Michal Simek <michal.simek@xilinx.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <netdev.h> +#include <ahci.h> +#include <scsi.h> +#include <asm/arch/hardware.h> + +#include <asm/io.h> + +/* Vendor Specific Register Offsets */ +#define AHCI_VEND_PCFG 0xA4 +#define AHCI_VEND_PPCFG 0xA8 +#define AHCI_VEND_PP2C 0xAC +#define AHCI_VEND_PP3C 0xB0 +#define AHCI_VEND_PP4C 0xB4 +#define AHCI_VEND_PP5C 0xB8 +#define AHCI_VEND_PAXIC 0xC0 +#define AHCI_VEND_PTC 0xC8 + +/* Vendor Specific Register bit definitions */ +#define PAXIC_ADBW_BW64 0x1 +#define PAXIC_MAWIDD (1 << 8) +#define PAXIC_MARIDD (1 << 16) +#define PAXIC_OTL (0x4 << 20) + +#define PCFG_TPSS_VAL (0x32 << 16) +#define PCFG_TPRS_VAL (0x2 << 12) +#define PCFG_PAD_VAL 0x2 + +#define PPCFG_TTA 0x1FFFE +#define PPCFG_PSSO_EN (1 << 28) +#define PPCFG_PSS_EN (1 << 29) +#define PPCFG_ESDF_EN (1 << 31) + +#define PP2C_CIBGMN 0x0F +#define PP2C_CIBGMX (0x25 << 8) +#define PP2C_CIBGN (0x18 << 16) +#define PP2C_CINMP (0x29 << 24) + +#define PP3C_CWBGMN 0x04 +#define PP3C_CWBGMX (0x0B << 8) +#define PP3C_CWBGN (0x08 << 16) +#define PP3C_CWNMP (0x0F << 24) + +#define PP4C_BMX 0x0a +#define PP4C_BNM (0x08 << 8) +#define PP4C_SFD (0x4a << 16) +#define PP4C_PTST (0x06 << 24) + +#define PP5C_RIT 0x60216 +#define PP5C_RCT (0x7f0 << 20) + +#define PTC_RX_WM_VAL 0x40 +#define PTC_RSVD (1 << 27) + +#define PORT0_BASE 0x100 +#define PORT1_BASE 0x180 + +/* Port Control Register Bit Definitions */ +#define PORT_SCTL_SPD_GEN3 (0x3 << 4) +#define PORT_SCTL_SPD_GEN2 (0x2 << 4) +#define PORT_SCTL_SPD_GEN1 (0x1 << 4) +#define PORT_SCTL_IPM (0x3 << 8) + +#define PORT_BASE 0x100 +#define PORT_OFFSET 0x80 +#define NR_PORTS 2 +#define DRV_NAME "ahci-ceva" +#define CEVA_FLAG_BROKEN_GEN2 1 + +int init_sata(int dev) +{ + ulong tmp; + ulong mmio = ZYNQMP_SATA_BASEADDR; + int i; + + /* + * AXI Data bus width to 64 + * Set Mem Addr Read, Write ID for data transfers + * Transfer limit to 72 DWord + */ + tmp = PAXIC_ADBW_BW64 | PAXIC_MAWIDD | PAXIC_MARIDD | PAXIC_OTL; + writel(tmp, mmio + AHCI_VEND_PAXIC); + + /* Set AHCI Enable */ + tmp = readl(mmio + HOST_CTL); + tmp |= HOST_AHCI_EN; + writel(tmp, mmio + HOST_CTL); + + for (i = 0; i < NR_PORTS; i++) { + /* TPSS TPRS scalars, CISE and Port Addr */ + tmp = PCFG_TPSS_VAL | PCFG_TPRS_VAL | (PCFG_PAD_VAL + i); + writel(tmp, mmio + AHCI_VEND_PCFG); + + /* Port Phy Cfg register enables */ + tmp = PPCFG_TTA | PPCFG_PSS_EN | PPCFG_ESDF_EN; + writel(tmp, mmio + AHCI_VEND_PPCFG); + + /* Rx Watermark setting */ + tmp = PTC_RX_WM_VAL | PTC_RSVD; + writel(tmp, mmio + AHCI_VEND_PTC); + + /* Default to Gen 2 Speed and Gen 1 if Gen2 is broken */ + tmp = PORT_SCTL_SPD_GEN3 | PORT_SCTL_IPM; + writel(tmp, mmio + PORT_SCR_CTL + PORT_BASE + PORT_OFFSET * i); + } + return 0; +} diff --git a/drivers/block/sym53c8xx.c b/drivers/block/sym53c8xx.c index 6f1ac85..c7c40af 100644 --- a/drivers/block/sym53c8xx.c +++ b/drivers/block/sym53c8xx.c @@ -657,7 +657,7 @@ void scsi_issue(ccb *pccb) /* struct pccb must be set-up correctly */ retrycnt=0; PRINTF("ID %d issue cmd %02X\n",pccb->target,pccb->cmd[0]); - pccb->trans_bytes=0; /* no bytes transfered yet */ + pccb->trans_bytes=0; /* no bytes transferred yet */ scsi_set_script(pccb); /* fill in SCRIPT */ scsi_int_mask=STO | UDC | MA; /* | CMP; / * Interrupts which are enabled */ script_int_mask=0xff; /* enable all Ints */ @@ -712,7 +712,7 @@ retry: for(i=0;i<3;i++) int_stat[i]=0; /* delete all int status */ retrycnt++; - PRINTF("ID: %X Phase Missmatch Retry %d Phase %02X transfered %lx\n", + PRINTF("ID: %X Phase Missmatch Retry %d Phase %02X transferred %lx\n", pccb->target,retrycnt,scsi_read_byte(SBCL),pccb->trans_bytes); scsi_write_dsp(phys_to_bus(&script_cmd[4])); /* start retry script */ goto retry; diff --git a/drivers/bootcount/bootcount_ram.c b/drivers/bootcount/bootcount_ram.c index 5bdabcd..e0d2669 100644 --- a/drivers/bootcount/bootcount_ram.c +++ b/drivers/bootcount/bootcount_ram.c @@ -35,6 +35,9 @@ void bootcount_store(ulong a) writel(patterns[i % NBR_OF_PATTERNS], &save_addr[i + OFFS_PATTERN]); + /* Make sure the data is written to RAM */ + flush_dcache_range((ulong)&save_addr[0], + (ulong)&save_addr[REPEAT_PATTERN + OFFS_PATTERN]); } ulong bootcount_load(void) diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c index e79e0ff..25c163b 100644 --- a/drivers/clk/uniphier/clk-uniphier-core.c +++ b/drivers/clk/uniphier/clk-uniphier-core.c @@ -8,13 +8,12 @@ #include <mapmem.h> #include <linux/bitops.h> #include <linux/io.h> +#include <linux/sizes.h> #include <clk.h> #include <dm/device.h> #include "clk-uniphier.h" -DECLARE_GLOBAL_DATA_PTR; - static int uniphier_clk_enable(struct udevice *dev, int index) { struct uniphier_clk_priv *priv = dev_get_priv(dev); @@ -133,14 +132,12 @@ int uniphier_clk_probe(struct udevice *dev) { struct uniphier_clk_priv *priv = dev_get_priv(dev); fdt_addr_t addr; - fdt_size_t size; - addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", - &size); + addr = dev_get_addr(dev); if (addr == FDT_ADDR_T_NONE) return -EINVAL; - priv->base = map_sysmem(addr, size); + priv->base = map_sysmem(addr, SZ_4K); if (!priv->base) return -ENOMEM; diff --git a/drivers/crypto/fsl/desc.h b/drivers/crypto/fsl/desc.h index 18e2ec8..1ac3a09 100644 --- a/drivers/crypto/fsl/desc.h +++ b/drivers/crypto/fsl/desc.h @@ -436,6 +436,31 @@ #define OP_PCLID_BLOB (0x0d << OP_PCLID_SHIFT) #define OP_PCLID_SECRETKEY (0x11 << OP_PCLID_SHIFT) #define OP_PCLID_PUBLICKEYPAIR (0x14 << OP_PCLID_SHIFT) +#define OP_PCLID_DSA_SIGN (0x15 << OP_PCLID_SHIFT) +#define OP_PCLID_DSA_VERIFY (0x16 << OP_PCLID_SHIFT) + +/* Assuming OP_TYPE = OP_TYPE_DECAP_PROTOCOL */ +#define OP_PCLID_MP_PUB_KEY (0x14 << OP_PCLID_SHIFT) +#define OP_PCLID_MP_SIGN (0x15 << OP_PCLID_SHIFT) + +/* Assuming OP_TYPE = OP_TYPE_ENCAP_PROTOCOL */ +#define OP_PCLID_MP_PRIV_KEY (0x14 << OP_PCLID_SHIFT) + +/* PROTINFO fields for discrete log public key protocols */ +#define OP_PROTINFO_F2M_FP 0x00000001 +#define OP_PROTINFO_ECC_DL 0x00000002 +#define OP_PROTINFO_ENC_PRI 0x00000004 +#define OP_PROTINFO_TEST 0x00000008 +#define OP_PROTINFO_EXT_PRI 0x00000010 +#define OP_PROTINFO_ENC_Z 0x00000020 +#define OP_PROTINFO_EKT_Z 0x00000040 +#define OP_PROTINFO_MES_REP 0x00000400 +#define OP_PROTINFO_HASH_MD5 0x00000000 +#define OP_PROTINFO_HASH_SHA1 0x00000080 +#define OP_PROTINFO_HASH_SHA224 0x00000100 +#define OP_PROTINFO_HASH_SHA256 0x00000180 +#define OP_PROTINFO_HASH_SHA384 0x00000200 +#define OP_PROTINFO_HASH_SHA512 0x00000280 /* For non-protocol/alg-only op commands */ #define OP_ALG_TYPE_SHIFT 24 @@ -663,4 +688,59 @@ #define OP_ALG_RNG4_MAS (0x1f3 << OP_ALG_RNG4_SHIFT) #define OP_ALG_RNG4_SK (0x100 << OP_ALG_RNG4_SHIFT) + +/* Structures for Protocol Data Blocks */ +struct __packed pdb_ecdsa_verify { + uint32_t pdb_hdr; + dma_addr_t dma_q; /* Pointer to q (elliptic curve) */ + dma_addr_t dma_r; /* Pointer to r (elliptic curve) */ + dma_addr_t dma_g_xy; /* Pointer to Gx,y (elliptic curve) */ + dma_addr_t dma_pkey; /* Pointer to Wx,y (public key) */ + dma_addr_t dma_hash; /* Pointer to hash input */ + dma_addr_t dma_c; /* Pointer to C_signature */ + dma_addr_t dma_d; /* Pointer to D_signature */ + dma_addr_t dma_buf; /* Pointer to 64-byte temp buffer */ + dma_addr_t dma_ab; /* Pointer to a,b (elliptic curve ) */ + uint32_t img_size; /* Length of Message */ +}; + +struct __packed pdb_ecdsa_sign { + uint32_t pdb_hdr; + dma_addr_t dma_q; /* Pointer to q (elliptic curve) */ + dma_addr_t dma_r; /* Pointer to r (elliptic curve) */ + dma_addr_t dma_g_xy; /* Pointer to Gx,y (elliptic curve) */ + dma_addr_t dma_pri_key; /* Pointer to S (Private key) */ + dma_addr_t dma_hash; /* Pointer to hash input */ + dma_addr_t dma_c; /* Pointer to C_signature */ + dma_addr_t dma_d; /* Pointer to D_signature */ + dma_addr_t dma_ab; /* Pointer to a,b (elliptic curve ) */ + dma_addr_t dma_u; /* Pointer to Per Message Random */ + uint32_t img_size; /* Length of Message */ +}; + +#define PDB_ECDSA_SGF_SHIFT 23 +#define PDB_ECDSA_L_SHIFT 7 +#define PDB_ECDSA_N_SHIFT 0 + +struct __packed pdb_mp_pub_k { + uint32_t pdb_hdr; + #define PDB_MP_PUB_K_SGF_SHIFT 31 + dma_addr_t dma_pkey; /* Pointer to Wx,y (public key) */ +}; + +struct __packed pdb_mp_sign { + uint32_t pdb_hdr; + #define PDB_MP_SIGN_SGF_SHIFT 28 + dma_addr_t dma_addr_msg; /* Pointer to Message */ + dma_addr_t dma_addr_hash; /* Pointer to hash output */ + dma_addr_t dma_addr_c_sig; /* Pointer to C_signature */ + dma_addr_t dma_addr_d_sig; /* Pointer to D_signature */ + uint32_t img_size; /* Length of Message */ +}; + +#define PDB_MP_CSEL_SHIFT 17 +#define PDB_MP_CSEL_P256 0x3 << PDB_MP_CSEL_SHIFT /* P-256 */ +#define PDB_MP_CSEL_P384 0x4 << PDB_MP_CSEL_SHIFT /* P-384 */ +#define PDB_MP_CSEL_P521 0x5 << PDB_MP_CSEL_SHIFT /* P-521 */ + #endif /* DESC_H */ diff --git a/drivers/crypto/fsl/desc_constr.h b/drivers/crypto/fsl/desc_constr.h index 2559ccd..7dad753 100644 --- a/drivers/crypto/fsl/desc_constr.h +++ b/drivers/crypto/fsl/desc_constr.h @@ -53,6 +53,19 @@ union ptr_addr_t { }; #endif +static inline void pdb_add_ptr(dma_addr_t *offset, dma_addr_t ptr) +{ +#ifdef CONFIG_PHYS_64BIT + /* The Position of low and high part of 64 bit address + * will depend on the endianness of CAAM Block */ + union ptr_addr_t *ptr_addr = (union ptr_addr_t *)offset; + ptr_addr->m_halfs.high = (u32)(ptr >> 32); + ptr_addr->m_halfs.low = (u32)ptr; +#else + *offset = ptr; +#endif +} + static inline int desc_len(u32 *desc) { return *desc & HDR_DESCLEN_MASK; @@ -68,6 +81,11 @@ static inline u32 *desc_end(u32 *desc) return desc + desc_len(desc); } +static inline void *desc_pdb(u32 *desc) +{ + return desc + 1; +} + static inline void init_desc(u32 *desc, u32 options) { *desc = (options | HDR_ONE) + 1; @@ -78,6 +96,15 @@ static inline void init_job_desc(u32 *desc, u32 options) init_desc(desc, CMD_DESC_HDR | options); } +static inline void init_job_desc_pdb(u32 *desc, u32 options, size_t pdb_bytes) +{ + u32 pdb_len = (pdb_bytes + CAAM_CMD_SZ - 1) / CAAM_CMD_SZ; + + init_job_desc(desc, + (((pdb_len + 1) << HDR_START_IDX_SHIFT) + pdb_len) | + options); +} + static inline void append_ptr(u32 *desc, dma_addr_t ptr) { dma_addr_t *offset = (dma_addr_t *)desc_end(desc); @@ -85,10 +112,9 @@ static inline void append_ptr(u32 *desc, dma_addr_t ptr) #ifdef CONFIG_PHYS_64BIT /* The Position of low and high part of 64 bit address * will depend on the endianness of CAAM Block */ - union ptr_addr_t ptr_addr; - ptr_addr.m_halfs.high = (u32)(ptr >> 32); - ptr_addr.m_halfs.low = (u32)ptr; - *offset = ptr_addr.m_whole; + union ptr_addr_t *ptr_addr = (union ptr_addr_t *)offset; + ptr_addr->m_halfs.high = (u32)(ptr >> 32); + ptr_addr->m_halfs.low = (u32)ptr; #else *offset = ptr; #endif diff --git a/drivers/crypto/fsl/jr.c b/drivers/crypto/fsl/jr.c index b766470..8bc517d 100644 --- a/drivers/crypto/fsl/jr.c +++ b/drivers/crypto/fsl/jr.c @@ -360,7 +360,7 @@ int run_descriptor_jr(uint32_t *desc) } } - if (!op.status) { + if (op.status) { debug("Error %x\n", op.status); ret = op.status; } @@ -543,7 +543,20 @@ int sec_init(void) uint32_t liodn_s; #endif + /* + * Modifying CAAM Read/Write Attributes + * For LS2080A + * For AXI Write - Cacheable, Write Back, Write allocate + * For AXI Read - Cacheable, Read allocate + * Only For LS2080a, to solve CAAM coherency issues + */ +#ifdef CONFIG_LS2080A + mcr = (mcr & ~MCFGR_AWCACHE_MASK) | (0xb << MCFGR_AWCACHE_SHIFT); + mcr = (mcr & ~MCFGR_ARCACHE_MASK) | (0x6 << MCFGR_ARCACHE_SHIFT); +#else mcr = (mcr & ~MCFGR_AWCACHE_MASK) | (0x2 << MCFGR_AWCACHE_SHIFT); +#endif + #ifdef CONFIG_PHYS_64BIT mcr |= (1 << MCFGR_PS_SHIFT); #endif diff --git a/drivers/crypto/fsl/jr.h b/drivers/crypto/fsl/jr.h index 545d964..1642dbb 100644 --- a/drivers/crypto/fsl/jr.h +++ b/drivers/crypto/fsl/jr.h @@ -23,6 +23,9 @@ #define MCFGR_PS_SHIFT 16 #define MCFGR_AWCACHE_SHIFT 8 #define MCFGR_AWCACHE_MASK (0xf << MCFGR_AWCACHE_SHIFT) +#define MCFGR_ARCACHE_SHIFT 12 +#define MCFGR_ARCACHE_MASK (0xf << MCFGR_ARCACHE_SHIFT) + #define JR_INTMASK 0x00000001 #define JRCR_RESET 0x01 #define JRINT_ERR_HALT_INPROGRESS 0x4 diff --git a/drivers/ddr/fsl/ctrl_regs.c b/drivers/ddr/fsl/ctrl_regs.c index 0bfcd34..9073917 100644 --- a/drivers/ddr/fsl/ctrl_regs.c +++ b/drivers/ddr/fsl/ctrl_regs.c @@ -895,11 +895,15 @@ static void set_ddr_sdram_cfg_2(const unsigned int ctrl_num, slow = get_ddr_freq(ctrl_num) < 1249000000; #endif - if (popts->registered_dimm_en) { + if (popts->registered_dimm_en) rcw_en = 1; - ap_en = popts->ap_en; - } else { + + /* DDR4 can have address parity for UDIMM and discrete */ + if ((CONFIG_FSL_SDRAM_TYPE != SDRAM_TYPE_DDR4) && + (!popts->registered_dimm_en)) { ap_en = 0; + } else { + ap_en = popts->ap_en; } x4_en = popts->x4_en ? 1 : 0; @@ -1135,6 +1139,7 @@ static void set_ddr_sdram_mode_9(fsl_ddr_cfg_regs_t *ddr, unsigned short esdmode5; /* Extended SDRAM mode 5 */ int rtt_park = 0; bool four_cs = false; + const unsigned int mclk_ps = get_memory_clk_period_ps(0); #if CONFIG_CHIP_SELECTS_PER_CTRL == 4 if ((ddr->cs[0].config & SDRAM_CS_CONFIG_EN) && @@ -1150,6 +1155,19 @@ static void set_ddr_sdram_mode_9(fsl_ddr_cfg_regs_t *ddr, esdmode5 = 0x00000400; /* Data mask enabled */ } + /* set command/address parity latency */ + if (ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) { + if (mclk_ps >= 935) { + /* for DDR4-1600/1866/2133 */ + esdmode5 |= DDR_MR5_CA_PARITY_LAT_4_CLK; + } else if (mclk_ps >= 833) { + /* for DDR4-2400 */ + esdmode5 |= DDR_MR5_CA_PARITY_LAT_5_CLK; + } else { + printf("parity: mclk_ps = %d not supported\n", mclk_ps); + } + } + ddr->ddr_sdram_mode_9 = (0 | ((esdmode4 & 0xffff) << 16) | ((esdmode5 & 0xffff) << 0) @@ -1170,6 +1188,20 @@ static void set_ddr_sdram_mode_9(fsl_ddr_cfg_regs_t *ddr, } else { esdmode5 = 0x00000400; } + + if (ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) { + if (mclk_ps >= 935) { + /* for DDR4-1600/1866/2133 */ + esdmode5 |= DDR_MR5_CA_PARITY_LAT_4_CLK; + } else if (mclk_ps >= 833) { + /* for DDR4-2400 */ + esdmode5 |= DDR_MR5_CA_PARITY_LAT_5_CLK; + } else { + printf("parity: mclk_ps = %d not supported\n", + mclk_ps); + } + } + switch (i) { case 1: ddr->ddr_sdram_mode_11 = (0 @@ -1925,12 +1957,25 @@ static void set_timing_cfg_7(const unsigned int ctrl_num, const common_timing_params_t *common_dimm) { unsigned int txpr, tcksre, tcksrx; - unsigned int cke_rst, cksre, cksrx, par_lat, cs_to_cmd; + unsigned int cke_rst, cksre, cksrx, par_lat = 0, cs_to_cmd; + const unsigned int mclk_ps = get_memory_clk_period_ps(ctrl_num); txpr = max(5U, picos_to_mclk(ctrl_num, common_dimm->trfc1_ps + 10000)); tcksre = max(5U, picos_to_mclk(ctrl_num, 10000)); tcksrx = max(5U, picos_to_mclk(ctrl_num, 10000)); - par_lat = 0; + + if (ddr->ddr_sdram_cfg_2 & SDRAM_CFG2_AP_EN) { + if (mclk_ps >= 935) { + /* parity latency 4 clocks in case of 1600/1866/2133 */ + par_lat = 4; + } else if (mclk_ps >= 833) { + /* parity latency 5 clocks for DDR4-2400 */ + par_lat = 5; + } else { + printf("parity: mclk_ps = %d not supported\n", mclk_ps); + } + } + cs_to_cmd = 0; if (txpr <= 200) diff --git a/drivers/ddr/fsl/fsl_ddr_gen4.c b/drivers/ddr/fsl/fsl_ddr_gen4.c index 6f76980..608810d 100644 --- a/drivers/ddr/fsl/fsl_ddr_gen4.c +++ b/drivers/ddr/fsl/fsl_ddr_gen4.c @@ -12,7 +12,8 @@ #include <fsl_ddr.h> #include <fsl_errata.h> -#ifdef CONFIG_SYS_FSL_ERRATUM_A008511 +#if defined(CONFIG_SYS_FSL_ERRATUM_A008511) | \ + defined(CONFIG_SYS_FSL_ERRATUM_A009803) static void set_wait_for_bits_clear(void *ptr, u32 value, u32 bits) { int timeout = 1000; @@ -24,9 +25,9 @@ static void set_wait_for_bits_clear(void *ptr, u32 value, u32 bits) timeout--; } if (timeout <= 0) - puts("Error: A007865 wait for clear timeout.\n"); + puts("Error: wait for clear timeout.\n"); } -#endif /* CONFIG_SYS_FSL_ERRATUM_A008511 */ +#endif #if (CONFIG_CHIP_SELECTS_PER_CTRL > 4) #error Invalid setting for CONFIG_CHIP_SELECTS_PER_CTRL @@ -201,7 +202,18 @@ void fsl_ddr_set_memctl_regs(const fsl_ddr_cfg_regs_t *regs, ddr_out32(&ddr->init_ext_addr, regs->ddr_init_ext_addr); ddr_out32(&ddr->ddr_cdr2, regs->ddr_cdr2); } + +#ifdef CONFIG_SYS_FSL_ERRATUM_A009803 + /* part 1 of 2 */ + if (regs->ddr_sdram_cfg & SDRAM_CFG_RD_EN) { /* for RDIMM */ + ddr_out32(&ddr->ddr_sdram_rcw_2, + regs->ddr_sdram_rcw_2 & ~0x0f000000); + } + + ddr_out32(&ddr->err_disable, regs->err_disable | DDR_ERR_DISABLE_APED); +#else ddr_out32(&ddr->err_disable, regs->err_disable); +#endif ddr_out32(&ddr->err_int_en, regs->err_int_en); for (i = 0; i < 32; i++) { if (regs->debug[i]) { @@ -297,7 +309,8 @@ step2: mb(); isb(); -#ifdef CONFIG_SYS_FSL_ERRATUM_A008511 +#if defined(CONFIG_SYS_FSL_ERRATUM_A008511) || \ + defined(CONFIG_SYS_FSL_ERRATUM_A009803) /* Part 2 of 2 */ /* This erraum only applies to verion 5.2.0 */ if (fsl_ddr_get_version(ctrl_num) == 0x50200) { @@ -313,6 +326,7 @@ step2: ctrl_num, ddr_in32(&ddr->debug[1])); } +#ifdef CONFIG_SYS_FSL_ERRATUM_A008511 /* The vref setting sequence is different for range 2 */ if (regs->ddr_cdr2 & DDR_CDR2_VREF_RANGE_2) vref_seq = vref_seq2; @@ -359,9 +373,29 @@ step2: } /* Restore D_INIT */ ddr_out32(&ddr->sdram_cfg_2, regs->ddr_sdram_cfg_2); - } #endif /* CONFIG_SYS_FSL_ERRATUM_A008511 */ +#ifdef CONFIG_SYS_FSL_ERRATUM_A009803 + /* if it's RDIMM */ + if (regs->ddr_sdram_cfg & SDRAM_CFG_RD_EN) { + for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { + if (!(regs->cs[i].config & SDRAM_CS_CONFIG_EN)) + continue; + set_wait_for_bits_clear(&ddr->sdram_md_cntl, + MD_CNTL_MD_EN | + MD_CNTL_CS_SEL(i) | + 0x070000ed, + MD_CNTL_MD_EN); + udelay(1); + } + } + + ddr_out32(&ddr->err_disable, + regs->err_disable & ~DDR_ERR_DISABLE_APED); +#endif + } +#endif + total_gb_size_per_controller = 0; for (i = 0; i < CONFIG_CHIP_SELECTS_PER_CTRL; i++) { if (!(regs->cs[i].config & 0x80000000)) diff --git a/drivers/ddr/fsl/lc_common_dimm_params.c b/drivers/ddr/fsl/lc_common_dimm_params.c index 47ad4e5..850c8f6 100644 --- a/drivers/ddr/fsl/lc_common_dimm_params.c +++ b/drivers/ddr/fsl/lc_common_dimm_params.c @@ -60,8 +60,8 @@ compute_cas_latency(const unsigned int ctrl_num, * 18ns for all DDR4 speed grades. */ if (caslat_actual * mclk_ps > taamax) { - printf("The choosen cas latency %d is too large\n", - caslat_actual); + printf("The chosen cas latency %d is too large\n", + caslat_actual); } outpdimm->lowest_common_spd_caslat = caslat_actual; debug("lowest_common_spd_caslat is 0x%x\n", caslat_actual); diff --git a/drivers/ddr/fsl/options.c b/drivers/ddr/fsl/options.c index 791d644..d0075ff 100644 --- a/drivers/ddr/fsl/options.c +++ b/drivers/ddr/fsl/options.c @@ -1002,8 +1002,19 @@ unsigned int populate_memctl_options(const common_timing_params_t *common_dimm, popts->twot_en = 0; popts->threet_en = 0; - /* for RDIMM, address parity enable */ - popts->ap_en = 1; + /* for RDIMM and DDR4 UDIMM/discrete memory, address parity enable */ + if (popts->registered_dimm_en) + popts->ap_en = 1; /* 0 = disable, 1 = enable */ + else + popts->ap_en = 0; /* disabled for DDR4 UDIMM/discrete default */ + + if (hwconfig_sub_f("fsl_ddr", "parity", buf)) { + if (hwconfig_subarg_cmp_f("fsl_ddr", "parity", "on", buf)) { + if (popts->registered_dimm_en || + (CONFIG_FSL_SDRAM_TYPE == SDRAM_TYPE_DDR4)) + popts->ap_en = 1; + } + } /* * BSTTOPRE precharge interval diff --git a/drivers/ddr/marvell/a38x/ddr3_init.c b/drivers/ddr/marvell/a38x/ddr3_init.c index 556f877..ee05f57 100644 --- a/drivers/ddr/marvell/a38x/ddr3_init.c +++ b/drivers/ddr/marvell/a38x/ddr3_init.c @@ -305,8 +305,6 @@ int ddr3_init(void) SAR1_CPU_CORE_OFFSET; switch (soc_num) { case 0x3: - reg_bit_set(CPU_CONFIGURATION_REG(3), CPU_MRVL_ID_OFFSET); - reg_bit_set(CPU_CONFIGURATION_REG(2), CPU_MRVL_ID_OFFSET); case 0x1: reg_bit_set(CPU_CONFIGURATION_REG(1), CPU_MRVL_ID_OFFSET); case 0x0: diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index 6aa24d4..fec3fec 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -17,5 +17,6 @@ obj-y += altera.o obj-$(CONFIG_FPGA_ACEX1K) += ACEX1K.o obj-$(CONFIG_FPGA_CYCLON2) += cyclon2.o obj-$(CONFIG_FPGA_STRATIX_II) += stratixII.o +obj-$(CONFIG_FPGA_STRATIX_V) += stratixv.o obj-$(CONFIG_FPGA_SOCFPGA) += socfpga.o endif diff --git a/drivers/fpga/altera.c b/drivers/fpga/altera.c index a5bfe5d..135a357 100644 --- a/drivers/fpga/altera.c +++ b/drivers/fpga/altera.c @@ -37,6 +37,9 @@ static const struct altera_fpga { { Altera_StratixII, "StratixII", StratixII_load, StratixII_dump, StratixII_info }, #endif +#if defined(CONFIG_FPGA_STRATIX_V) + { Altera_StratixV, "StratixV", stratixv_load, NULL, NULL }, +#endif #if defined(CONFIG_FPGA_SOCFPGA) { Altera_SoCFPGA, "SoC FPGA", socfpga_load, NULL, NULL }, #endif diff --git a/drivers/fpga/stratixII.c b/drivers/fpga/stratixII.c index 820d016..da9c14a 100644 --- a/drivers/fpga/stratixII.c +++ b/drivers/fpga/stratixII.c @@ -130,7 +130,7 @@ int StratixII_ps_fpp_load (Altera_desc * desc, void *buf, size_t bsize, /* 3.1 check stratix has not signaled us an error */ if (fns->status (cookie) != 1) { printf - ("\n%s(%d) Stratix failed (byte transfered till failure 0x%x)\n", + ("\n%s(%d) Stratix failed (byte transferred till failure 0x%x)\n", __FUNCTION__, __LINE__, bytecount); fns->abort (cookie); return FPGA_FAIL; diff --git a/drivers/fpga/stratixv.c b/drivers/fpga/stratixv.c new file mode 100644 index 0000000..cc035eb --- /dev/null +++ b/drivers/fpga/stratixv.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016 Stefan Roese <sr@denx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <altera.h> +#include <spi.h> +#include <asm/io.h> +#include <asm/errno.h> + +/* Write the RBF data to FPGA via SPI */ +static int program_write(int spi_bus, int spi_dev, const void *rbf_data, + unsigned long rbf_size) +{ + struct spi_slave *slave; + int ret; + + debug("%s (%d): data=%p size=%ld\n", + __func__, __LINE__, rbf_data, rbf_size); + + /* FIXME: How to get the max. SPI clock and SPI mode? */ + slave = spi_setup_slave(spi_bus, spi_dev, 27777777, SPI_MODE_3); + if (!slave) + return -1; + + if (spi_claim_bus(slave)) + return -1; + + ret = spi_xfer(slave, rbf_size * 8, rbf_data, (void *)rbf_data, + SPI_XFER_BEGIN | SPI_XFER_END); + + spi_release_bus(slave); + + return ret; +} + +/* + * This is the interface used by FPGA driver. + * Return 0 for sucess, non-zero for error. + */ +int stratixv_load(Altera_desc *desc, const void *rbf_data, size_t rbf_size) +{ + altera_board_specific_func *pfns = desc->iface_fns; + int cookie = desc->cookie; + int spi_bus; + int spi_dev; + int ret = 0; + + if ((u32)rbf_data & 0x3) { + puts("FPGA: Unaligned data, realign to 32bit boundary.\n"); + return -EINVAL; + } + + /* Run the pre configuration function if there is one */ + if (pfns->pre) + (pfns->pre)(cookie); + + /* Establish the initial state */ + if (pfns->config) { + /* De-assert nCONFIG */ + (pfns->config)(false, true, cookie); + + /* nConfig minimum low pulse width is 2us */ + udelay(200); + + /* Assert nCONFIG */ + (pfns->config)(true, true, cookie); + + /* nCONFIG high to first rising clock on DCLK min 1506 us */ + udelay(1600); + } + + /* Write the RBF data to FPGA */ + if (pfns->write) { + /* + * Use board specific data function to write bitstream + * into the FPGA + */ + ret = (pfns->write)(rbf_data, rbf_size, true, cookie); + } else { + /* + * Use common SPI functions to write bitstream into the + * FPGA + */ + spi_bus = COOKIE2SPI_BUS(cookie); + spi_dev = COOKIE2SPI_DEV(cookie); + ret = program_write(spi_bus, spi_dev, rbf_data, rbf_size); + } + if (ret) + return ret; + + /* Check done pin */ + if (pfns->done) { + ret = (pfns->done)(cookie); + + if (ret) + printf("Error: DONE not set (ret=%d)!\n", ret); + } + + return ret; +} diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index a5da5e7..f56a606 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -55,6 +55,30 @@ config LPC32XX_GPIO help Support for the LPC32XX GPIO driver. +config MSM_GPIO + bool "Qualcomm GPIO driver" + depends on DM_GPIO + default n + help + Support GPIO controllers on Qualcomm Snapdragon family of SoCs. + This controller have single bank (default name "soc"), every + gpio has it's own set of registers. + Only simple GPIO operations are supported (get/set, change of + direction and checking pin function). + Supported devices: + - APQ8016 + - MSM8916 + +config PM8916_GPIO + bool "Qualcomm PM8916 PMIC GPIO/keypad driver" + depends on DM_GPIO && PMIC_PM8916 + help + Support for GPIO pins and power/reset buttons found on + Qualcomm PM8916 PMIC. + Default name for GPIO bank is "pm8916". + Power and reset buttons are placed in "pm8916_key" bank and + have gpio numbers 0 and 1 respectively. + config ROCKCHIP_GPIO bool "Rockchip GPIO driver" depends on DM_GPIO @@ -105,4 +129,11 @@ config PIC32_GPIO help Say yes here to support Microchip PIC32 GPIOs. +config MVEBU_GPIO + bool "Marvell MVEBU GPIO driver" + depends on DM_GPIO && ARCH_MVEBU + default y + help + Say yes here to support Marvell MVEBU (Armada XP/38x) GPIOs. + endmenu diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index e7b7ec4..4f071c4 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -49,3 +49,6 @@ obj-$(CONFIG_ZYNQ_GPIO) += zynq_gpio.o obj-$(CONFIG_VYBRID_GPIO) += vybrid_gpio.o obj-$(CONFIG_HIKEY_GPIO) += hi6220_gpio.o obj-$(CONFIG_PIC32_GPIO) += pic32_gpio.o +obj-$(CONFIG_MVEBU_GPIO) += mvebu_gpio.o +obj-$(CONFIG_MSM_GPIO) += msm_gpio.o +obj-$(CONFIG_PM8916_GPIO) += pm8916_gpio.o diff --git a/drivers/gpio/axp_gpio.c b/drivers/gpio/axp_gpio.c index bd2ac89..ec00827 100644 --- a/drivers/gpio/axp_gpio.c +++ b/drivers/gpio/axp_gpio.c @@ -59,10 +59,11 @@ static int axp_gpio_direction_output(struct udevice *dev, unsigned pin, u8 reg; switch (pin) { -#ifdef CONFIG_AXP221_POWER /* Only available on axp221/axp223 */ +#ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC + /* Only available on later PMICs */ case SUNXI_GPIO_AXP0_VBUS_ENABLE: - ret = pmic_bus_clrbits(AXP221_MISC_CTRL, - AXP221_MISC_CTRL_N_VBUSEN_FUNC); + ret = pmic_bus_clrbits(AXP_MISC_CTRL, + AXP_MISC_CTRL_N_VBUSEN_FUNC); if (ret) return ret; @@ -90,10 +91,11 @@ static int axp_gpio_get_value(struct udevice *dev, unsigned pin) mask = AXP_POWER_STATUS_VBUS_PRESENT; break; #endif -#ifdef CONFIG_AXP221_POWER /* Only available on axp221/axp223 */ +#ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC + /* Only available on later PMICs */ case SUNXI_GPIO_AXP0_VBUS_ENABLE: - ret = pmic_bus_read(AXP221_VBUS_IPSOUT, &val); - mask = AXP221_VBUS_IPSOUT_DRIVEBUS; + ret = pmic_bus_read(AXP_VBUS_IPSOUT, &val); + mask = AXP_VBUS_IPSOUT_DRIVEBUS; break; #endif default: @@ -115,14 +117,15 @@ static int axp_gpio_set_value(struct udevice *dev, unsigned pin, int val) u8 reg; switch (pin) { -#ifdef CONFIG_AXP221_POWER /* Only available on axp221/axp223 */ +#ifdef AXP_MISC_CTRL_N_VBUSEN_FUNC + /* Only available on later PMICs */ case SUNXI_GPIO_AXP0_VBUS_ENABLE: if (val) - return pmic_bus_setbits(AXP221_VBUS_IPSOUT, - AXP221_VBUS_IPSOUT_DRIVEBUS); + return pmic_bus_setbits(AXP_VBUS_IPSOUT, + AXP_VBUS_IPSOUT_DRIVEBUS); else - return pmic_bus_clrbits(AXP221_VBUS_IPSOUT, - AXP221_VBUS_IPSOUT_DRIVEBUS); + return pmic_bus_clrbits(AXP_VBUS_IPSOUT, + AXP_VBUS_IPSOUT_DRIVEBUS); #endif default: reg = axp_get_gpio_ctrl_reg(pin); diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c index 80bb16e..bde51ea 100644 --- a/drivers/gpio/gpio-uniphier.c +++ b/drivers/gpio/gpio-uniphier.c @@ -9,6 +9,7 @@ #include <mapmem.h> #include <linux/bitops.h> #include <linux/io.h> +#include <linux/sizes.h> #include <asm/errno.h> #include <asm/gpio.h> @@ -91,17 +92,14 @@ static int uniphier_gpio_probe(struct udevice *dev) { struct uniphier_gpio_priv *priv = dev_get_priv(dev); struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); - DECLARE_GLOBAL_DATA_PTR; fdt_addr_t addr; - fdt_size_t size; unsigned int tmp; - addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", - &size); + addr = dev_get_addr(dev); if (addr == FDT_ADDR_T_NONE) return -EINVAL; - priv->base = map_sysmem(addr, size); + priv->base = map_sysmem(addr, SZ_8); if (!priv->base) return -ENOMEM; diff --git a/drivers/gpio/msm_gpio.c b/drivers/gpio/msm_gpio.c new file mode 100644 index 0000000..0302979 --- /dev/null +++ b/drivers/gpio/msm_gpio.c @@ -0,0 +1,133 @@ +/* + * Qualcomm GPIO driver + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <asm/gpio.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Register offsets */ +#define GPIO_CONFIG_OFF(no) ((no) * 0x1000) +#define GPIO_IN_OUT_OFF(no) ((no) * 0x1000 + 0x4) + +/* OE */ +#define GPIO_OE_DISABLE (0x0 << 9) +#define GPIO_OE_ENABLE (0x1 << 9) +#define GPIO_OE_MASK (0x1 << 9) + +/* GPIO_IN_OUT register shifts. */ +#define GPIO_IN 0 +#define GPIO_OUT 1 + +struct msm_gpio_bank { + phys_addr_t base; +}; + +static int msm_gpio_direction_input(struct udevice *dev, unsigned int gpio) +{ + struct msm_gpio_bank *priv = dev_get_priv(dev); + phys_addr_t reg = priv->base + GPIO_CONFIG_OFF(gpio); + + /* Disable OE bit */ + clrsetbits_le32(reg, GPIO_OE_MASK, GPIO_OE_DISABLE); + + return 0; +} + +static int msm_gpio_set_value(struct udevice *dev, unsigned gpio, int value) +{ + struct msm_gpio_bank *priv = dev_get_priv(dev); + + value = !!value; + /* set value */ + writel(value << GPIO_OUT, priv->base + GPIO_IN_OUT_OFF(gpio)); + + return 0; +} + +static int msm_gpio_direction_output(struct udevice *dev, unsigned gpio, + int value) +{ + struct msm_gpio_bank *priv = dev_get_priv(dev); + phys_addr_t reg = priv->base + GPIO_CONFIG_OFF(gpio); + + value = !!value; + /* set value */ + writel(value << GPIO_OUT, priv->base + GPIO_IN_OUT_OFF(gpio)); + /* switch direction */ + clrsetbits_le32(reg, GPIO_OE_MASK, GPIO_OE_ENABLE); + + return 0; +} + +static int msm_gpio_get_value(struct udevice *dev, unsigned gpio) +{ + struct msm_gpio_bank *priv = dev_get_priv(dev); + + return !!(readl(priv->base + GPIO_IN_OUT_OFF(gpio)) >> GPIO_IN); +} + +static int msm_gpio_get_function(struct udevice *dev, unsigned offset) +{ + struct msm_gpio_bank *priv = dev_get_priv(dev); + + if (readl(priv->base + GPIO_CONFIG_OFF(offset)) & GPIO_OE_ENABLE) + return GPIOF_OUTPUT; + + return GPIOF_INPUT; +} + +static const struct dm_gpio_ops gpio_msm_ops = { + .direction_input = msm_gpio_direction_input, + .direction_output = msm_gpio_direction_output, + .get_value = msm_gpio_get_value, + .set_value = msm_gpio_set_value, + .get_function = msm_gpio_get_function, +}; + +static int msm_gpio_probe(struct udevice *dev) +{ + struct msm_gpio_bank *priv = dev_get_priv(dev); + + priv->base = dev_get_addr(dev); + + return priv->base == FDT_ADDR_T_NONE ? -EINVAL : 0; +} + +static int msm_gpio_ofdata_to_platdata(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "gpio-count", 0); + uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset, + "gpio-bank-name", NULL); + if (uc_priv->bank_name == NULL) + uc_priv->bank_name = "soc"; + + return 0; +} + +static const struct udevice_id msm_gpio_ids[] = { + { .compatible = "qcom,msm8916-pinctrl" }, + { .compatible = "qcom,apq8016-pinctrl" }, + { } +}; + +U_BOOT_DRIVER(gpio_msm) = { + .name = "gpio_msm", + .id = UCLASS_GPIO, + .of_match = msm_gpio_ids, + .ofdata_to_platdata = msm_gpio_ofdata_to_platdata, + .probe = msm_gpio_probe, + .ops = &gpio_msm_ops, + .priv_auto_alloc_size = sizeof(struct msm_gpio_bank), +}; diff --git a/drivers/gpio/mvebu_gpio.c b/drivers/gpio/mvebu_gpio.c new file mode 100644 index 0000000..75dc73e --- /dev/null +++ b/drivers/gpio/mvebu_gpio.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 Stefan Roese <sr@denx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <errno.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define MVEBU_GPIOS_PER_BANK 32 + +struct mvebu_gpio_regs { + u32 data_out; + u32 io_conf; + u32 blink_en; + u32 in_pol; + u32 data_in; +}; + +struct mvebu_gpio_priv { + struct mvebu_gpio_regs *regs; + char name[2]; +}; + +static int mvebu_gpio_direction_input(struct udevice *dev, unsigned int gpio) +{ + struct mvebu_gpio_priv *priv = dev_get_priv(dev); + struct mvebu_gpio_regs *regs = priv->regs; + + setbits_le32(®s->io_conf, BIT(gpio)); + + return 0; +} + +static int mvebu_gpio_direction_output(struct udevice *dev, unsigned gpio, + int value) +{ + struct mvebu_gpio_priv *priv = dev_get_priv(dev); + struct mvebu_gpio_regs *regs = priv->regs; + + if (value) + setbits_le32(®s->data_out, BIT(gpio)); + else + clrbits_le32(®s->data_out, BIT(gpio)); + clrbits_le32(®s->io_conf, BIT(gpio)); + + return 0; +} + +static int mvebu_gpio_get_function(struct udevice *dev, unsigned gpio) +{ + struct mvebu_gpio_priv *priv = dev_get_priv(dev); + struct mvebu_gpio_regs *regs = priv->regs; + u32 val; + + val = readl(®s->io_conf) & BIT(gpio); + if (val) + return GPIOF_INPUT; + else + return GPIOF_OUTPUT; +} + +static int mvebu_gpio_set_value(struct udevice *dev, unsigned gpio, + int value) +{ + struct mvebu_gpio_priv *priv = dev_get_priv(dev); + struct mvebu_gpio_regs *regs = priv->regs; + + if (value) + setbits_le32(®s->data_out, BIT(gpio)); + else + clrbits_le32(®s->data_out, BIT(gpio)); + + return 0; +} + +static int mvebu_gpio_get_value(struct udevice *dev, unsigned gpio) +{ + struct mvebu_gpio_priv *priv = dev_get_priv(dev); + struct mvebu_gpio_regs *regs = priv->regs; + + return !!(readl(®s->data_in) & BIT(gpio)); +} + +static int mvebu_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct mvebu_gpio_priv *priv = dev_get_priv(dev); + + priv->regs = (struct mvebu_gpio_regs *)dev_get_addr(dev); + uc_priv->gpio_count = MVEBU_GPIOS_PER_BANK; + priv->name[0] = 'A' + dev->req_seq; + uc_priv->bank_name = priv->name; + + return 0; +} + +static const struct dm_gpio_ops mvebu_gpio_ops = { + .direction_input = mvebu_gpio_direction_input, + .direction_output = mvebu_gpio_direction_output, + .get_function = mvebu_gpio_get_function, + .get_value = mvebu_gpio_get_value, + .set_value = mvebu_gpio_set_value, +}; + +static const struct udevice_id mvebu_gpio_ids[] = { + { .compatible = "marvell,orion-gpio" }, + { } +}; + +U_BOOT_DRIVER(gpio_mvebu) = { + .name = "gpio_mvebu", + .id = UCLASS_GPIO, + .of_match = mvebu_gpio_ids, + .ops = &mvebu_gpio_ops, + .probe = mvebu_gpio_probe, + .priv_auto_alloc_size = sizeof(struct mvebu_gpio_priv), +}; diff --git a/drivers/gpio/pm8916_gpio.c b/drivers/gpio/pm8916_gpio.c new file mode 100644 index 0000000..1abab7f --- /dev/null +++ b/drivers/gpio/pm8916_gpio.c @@ -0,0 +1,302 @@ +/* + * Qualcomm pm8916 pmic gpio driver - part of Qualcomm PM8916 PMIC + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <power/pmic.h> +#include <spmi/spmi.h> +#include <asm/io.h> +#include <asm/gpio.h> +#include <linux/bitops.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Register offset for each gpio */ +#define REG_OFFSET(x) ((x) * 0x100) + +/* Register maps */ + +/* Type and subtype are shared for all pm8916 peripherals */ +#define REG_TYPE 0x4 +#define REG_SUBTYPE 0x5 + +#define REG_STATUS 0x08 +#define REG_STATUS_VAL_MASK 0x1 + +/* MODE_CTL */ +#define REG_CTL 0x40 +#define REG_CTL_MODE_MASK 0x70 +#define REG_CTL_MODE_INPUT 0x00 +#define REG_CTL_MODE_INOUT 0x20 +#define REG_CTL_MODE_OUTPUT 0x10 +#define REG_CTL_OUTPUT_MASK 0x0F + +#define REG_DIG_VIN_CTL 0x41 +#define REG_DIG_VIN_VIN0 0 + +#define REG_DIG_PULL_CTL 0x42 +#define REG_DIG_PULL_NO_PU 0x5 + +#define REG_DIG_OUT_CTL 0x45 +#define REG_DIG_OUT_CTL_CMOS (0x0 << 4) +#define REG_DIG_OUT_CTL_DRIVE_L 0x1 + +#define REG_EN_CTL 0x46 +#define REG_EN_CTL_ENABLE (1 << 7) + +struct pm8916_gpio_bank { + uint16_t pid; /* Peripheral ID on SPMI bus */ +}; + +static int pm8916_gpio_set_direction(struct udevice *dev, unsigned offset, + bool input, int value) +{ + struct pm8916_gpio_bank *priv = dev_get_priv(dev); + uint32_t gpio_base = priv->pid + REG_OFFSET(offset); + int ret; + + /* Disable the GPIO */ + ret = pmic_clrsetbits(dev->parent, gpio_base + REG_EN_CTL, + REG_EN_CTL_ENABLE, 0); + if (ret < 0) + return ret; + + /* Select the mode */ + if (input) + ret = pmic_reg_write(dev->parent, gpio_base + REG_CTL, + REG_CTL_MODE_INPUT); + else + ret = pmic_reg_write(dev->parent, gpio_base + REG_CTL, + REG_CTL_MODE_INOUT | (value ? 1 : 0)); + if (ret < 0) + return ret; + + /* Set the right pull (no pull) */ + ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_PULL_CTL, + REG_DIG_PULL_NO_PU); + if (ret < 0) + return ret; + + /* Configure output pin drivers if needed */ + if (!input) { + /* Select the VIN - VIN0, pin is input so it doesn't matter */ + ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_VIN_CTL, + REG_DIG_VIN_VIN0); + if (ret < 0) + return ret; + + /* Set the right dig out control */ + ret = pmic_reg_write(dev->parent, gpio_base + REG_DIG_OUT_CTL, + REG_DIG_OUT_CTL_CMOS | + REG_DIG_OUT_CTL_DRIVE_L); + if (ret < 0) + return ret; + } + + /* Enable the GPIO */ + return pmic_clrsetbits(dev->parent, gpio_base + REG_EN_CTL, 0, + REG_EN_CTL_ENABLE); +} + +static int pm8916_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + return pm8916_gpio_set_direction(dev, offset, true, 0); +} + +static int pm8916_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + return pm8916_gpio_set_direction(dev, offset, false, value); +} + +static int pm8916_gpio_get_function(struct udevice *dev, unsigned offset) +{ + struct pm8916_gpio_bank *priv = dev_get_priv(dev); + uint32_t gpio_base = priv->pid + REG_OFFSET(offset); + int reg; + + /* Set the output value of the gpio */ + reg = pmic_reg_read(dev->parent, gpio_base + REG_CTL); + if (reg < 0) + return reg; + + switch (reg & REG_CTL_MODE_MASK) { + case REG_CTL_MODE_INPUT: + return GPIOF_INPUT; + case REG_CTL_MODE_INOUT: /* Fallthrough */ + case REG_CTL_MODE_OUTPUT: + return GPIOF_OUTPUT; + default: + return GPIOF_UNKNOWN; + } +} + +static int pm8916_gpio_get_value(struct udevice *dev, unsigned offset) +{ + struct pm8916_gpio_bank *priv = dev_get_priv(dev); + uint32_t gpio_base = priv->pid + REG_OFFSET(offset); + int reg; + + reg = pmic_reg_read(dev->parent, gpio_base + REG_STATUS); + if (reg < 0) + return reg; + + return !!(reg & REG_STATUS_VAL_MASK); +} + +static int pm8916_gpio_set_value(struct udevice *dev, unsigned offset, + int value) +{ + struct pm8916_gpio_bank *priv = dev_get_priv(dev); + uint32_t gpio_base = priv->pid + REG_OFFSET(offset); + + /* Set the output value of the gpio */ + return pmic_clrsetbits(dev->parent, gpio_base + REG_CTL, + REG_CTL_OUTPUT_MASK, !!value); +} + +static const struct dm_gpio_ops pm8916_gpio_ops = { + .direction_input = pm8916_gpio_direction_input, + .direction_output = pm8916_gpio_direction_output, + .get_value = pm8916_gpio_get_value, + .set_value = pm8916_gpio_set_value, + .get_function = pm8916_gpio_get_function, +}; + +static int pm8916_gpio_probe(struct udevice *dev) +{ + struct pm8916_gpio_bank *priv = dev_get_priv(dev); + int reg; + + priv->pid = dev_get_addr(dev); + if (priv->pid == FDT_ADDR_T_NONE) + return -EINVAL; + + /* Do a sanity check */ + reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE); + if (reg != 0x10) + return -ENODEV; + + reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE); + if (reg != 0x5) + return -ENODEV; + + return 0; +} + +static int pm8916_gpio_ofdata_to_platdata(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "gpio-count", 0); + uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset, + "gpio-bank-name", NULL); + if (uc_priv->bank_name == NULL) + uc_priv->bank_name = "pm8916"; + + return 0; +} + +static const struct udevice_id pm8916_gpio_ids[] = { + { .compatible = "qcom,pm8916-gpio" }, + { } +}; + +U_BOOT_DRIVER(gpio_pm8916) = { + .name = "gpio_pm8916", + .id = UCLASS_GPIO, + .of_match = pm8916_gpio_ids, + .ofdata_to_platdata = pm8916_gpio_ofdata_to_platdata, + .probe = pm8916_gpio_probe, + .ops = &pm8916_gpio_ops, + .priv_auto_alloc_size = sizeof(struct pm8916_gpio_bank), +}; + + +/* Add pmic buttons as GPIO as well - there is no generic way for now */ +#define PON_INT_RT_STS 0x10 +#define KPDPWR_ON_INT_BIT 0 +#define RESIN_ON_INT_BIT 1 + +static int pm8941_pwrkey_get_function(struct udevice *dev, unsigned offset) +{ + return GPIOF_INPUT; +} + +static int pm8941_pwrkey_get_value(struct udevice *dev, unsigned offset) +{ + struct pm8916_gpio_bank *priv = dev_get_priv(dev); + + int reg = pmic_reg_read(dev->parent, priv->pid + PON_INT_RT_STS); + + if (reg < 0) + return 0; + + switch (offset) { + case 0: /* Power button */ + return (reg & BIT(KPDPWR_ON_INT_BIT)) != 0; + break; + case 1: /* Reset button */ + default: + return (reg & BIT(RESIN_ON_INT_BIT)) != 0; + break; + } +} + +static const struct dm_gpio_ops pm8941_pwrkey_ops = { + .get_value = pm8941_pwrkey_get_value, + .get_function = pm8941_pwrkey_get_function, +}; + +static int pm8941_pwrkey_probe(struct udevice *dev) +{ + struct pm8916_gpio_bank *priv = dev_get_priv(dev); + int reg; + + priv->pid = dev_get_addr(dev); + if (priv->pid == FDT_ADDR_T_NONE) + return -EINVAL; + + /* Do a sanity check */ + reg = pmic_reg_read(dev->parent, priv->pid + REG_TYPE); + if (reg != 0x1) + return -ENODEV; + + reg = pmic_reg_read(dev->parent, priv->pid + REG_SUBTYPE); + if (reg != 0x1) + return -ENODEV; + + return 0; +} + +static int pm8941_pwrkey_ofdata_to_platdata(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + + uc_priv->gpio_count = 2; + if (uc_priv->bank_name == NULL) + uc_priv->bank_name = "pm8916_key"; + + return 0; +} + +static const struct udevice_id pm8941_pwrkey_ids[] = { + { .compatible = "qcom,pm8916-pwrkey" }, + { } +}; + +U_BOOT_DRIVER(pwrkey_pm8941) = { + .name = "pwrkey_pm8916", + .id = UCLASS_GPIO, + .of_match = pm8941_pwrkey_ids, + .ofdata_to_platdata = pm8941_pwrkey_ofdata_to_platdata, + .probe = pm8941_pwrkey_probe, + .ops = &pm8941_pwrkey_ops, + .priv_auto_alloc_size = sizeof(struct pm8916_gpio_bank), +}; diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c index 9d8f11e..a7cec18 100644 --- a/drivers/gpio/sunxi_gpio.c +++ b/drivers/gpio/sunxi_gpio.c @@ -277,9 +277,17 @@ static int gpio_sunxi_bind(struct udevice *parent) start = 'L' - 'A'; no_banks = 2; /* L & M */ } else if (fdt_node_check_compatible(gd->fdt_blob, parent->of_offset, - "allwinner,sun8i-a23-r-pinctrl") == 0) { + "allwinner,sun8i-a23-r-pinctrl") == 0 || + fdt_node_check_compatible(gd->fdt_blob, parent->of_offset, + "allwinner,sun8i-a83t-r-pinctrl") == 0 || + fdt_node_check_compatible(gd->fdt_blob, parent->of_offset, + "allwinner,sun8i-h3-r-pinctrl") == 0) { start = 'L' - 'A'; no_banks = 1; /* L only */ + } else if (fdt_node_check_compatible(gd->fdt_blob, parent->of_offset, + "allwinner,sun9i-a80-r-pinctrl") == 0) { + start = 'L' - 'A'; + no_banks = 3; /* L, M & N */ } else { start = 0; no_banks = SUNXI_GPIO_BANKS; @@ -316,9 +324,14 @@ static const struct udevice_id sunxi_gpio_ids[] = { { .compatible = "allwinner,sun7i-a20-pinctrl" }, { .compatible = "allwinner,sun8i-a23-pinctrl" }, { .compatible = "allwinner,sun8i-a33-pinctrl" }, + { .compatible = "allwinner,sun8i-a83t-pinctrl", }, + { .compatible = "allwinner,sun8i-h3-pinctrl" }, { .compatible = "allwinner,sun9i-a80-pinctrl" }, { .compatible = "allwinner,sun6i-a31-r-pinctrl" }, { .compatible = "allwinner,sun8i-a23-r-pinctrl" }, + { .compatible = "allwinner,sun8i-a83t-r-pinctrl" }, + { .compatible = "allwinner,sun8i-h3-r-pinctrl", }, + { .compatible = "allwinner,sun9i-a80-r-pinctrl", }, { } }; diff --git a/drivers/i2c/i2c-uniphier-f.c b/drivers/i2c/i2c-uniphier-f.c index b3349af..aebdcfc 100644 --- a/drivers/i2c/i2c-uniphier-f.c +++ b/drivers/i2c/i2c-uniphier-f.c @@ -7,6 +7,7 @@ #include <common.h> #include <linux/types.h> #include <linux/io.h> +#include <linux/sizes.h> #include <asm/errno.h> #include <dm/device.h> #include <dm/root.h> @@ -14,8 +15,6 @@ #include <fdtdec.h> #include <mapmem.h> -DECLARE_GLOBAL_DATA_PTR; - struct uniphier_fi2c_regs { u32 cr; /* control register */ #define I2C_CR_MST (1 << 3) /* master mode */ @@ -112,15 +111,14 @@ static int check_device_busy(struct uniphier_fi2c_regs __iomem *regs) static int uniphier_fi2c_probe(struct udevice *dev) { fdt_addr_t addr; - fdt_size_t size; struct uniphier_fi2c_dev *priv = dev_get_priv(dev); int ret; - addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", - &size); - - priv->regs = map_sysmem(addr, size); + addr = dev_get_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + priv->regs = map_sysmem(addr, SZ_128); if (!priv->regs) return -ENOMEM; diff --git a/drivers/i2c/i2c-uniphier.c b/drivers/i2c/i2c-uniphier.c index 85b9eff..f8221da 100644 --- a/drivers/i2c/i2c-uniphier.c +++ b/drivers/i2c/i2c-uniphier.c @@ -7,6 +7,7 @@ #include <common.h> #include <linux/types.h> #include <linux/io.h> +#include <linux/sizes.h> #include <asm/errno.h> #include <dm/device.h> #include <dm/root.h> @@ -14,8 +15,6 @@ #include <fdtdec.h> #include <mapmem.h> -DECLARE_GLOBAL_DATA_PTR; - struct uniphier_i2c_regs { u32 dtrm; /* data transmission */ #define I2C_DTRM_STA (1 << 10) @@ -48,13 +47,13 @@ struct uniphier_i2c_dev { static int uniphier_i2c_probe(struct udevice *dev) { fdt_addr_t addr; - fdt_size_t size; struct uniphier_i2c_dev *priv = dev_get_priv(dev); - addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size); - - priv->regs = map_sysmem(addr, size); + addr = dev_get_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + priv->regs = map_sysmem(addr, SZ_64); if (!priv->regs) return -ENOMEM; diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index b2d15c9..445fa21 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -23,6 +23,7 @@ #include <i2c.h> #include <watchdog.h> #include <dm.h> +#include <dm/pinctrl.h> #include <fdtdec.h> DECLARE_GLOBAL_DATA_PTR; @@ -334,17 +335,74 @@ int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus) } #else /* - * Since pinmux is not supported, implement a weak function here. - * You can implement your i2c_bus_idle in board file. When pinctrl - * is supported, this can be removed. + * See Linux Documentation/devicetree/bindings/i2c/i2c-imx.txt + * " + * scl-gpios: specify the gpio related to SCL pin + * sda-gpios: specify the gpio related to SDA pin + * add pinctrl to configure i2c pins to gpio function for i2c + * bus recovery, call it "gpio" state + * " + * + * The i2c_idle_bus is an implementation following Linux Kernel. */ -int __i2c_idle_bus(struct mxc_i2c_bus *i2c_bus) +int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus) { - return 0; -} + struct udevice *bus = i2c_bus->bus; + struct gpio_desc *scl_gpio = &i2c_bus->scl_gpio; + struct gpio_desc *sda_gpio = &i2c_bus->sda_gpio; + int sda, scl; + int i, ret = 0; + ulong elapsed, start_time; -int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus) - __attribute__((weak, alias("__i2c_idle_bus"))); + if (pinctrl_select_state(bus, "gpio")) { + dev_dbg(bus, "Can not to switch to use gpio pinmux\n"); + /* + * GPIO pinctrl for i2c force idle is not a must, + * but it is strongly recommended to be used. + * Because it can help you to recover from bad + * i2c bus state. Do not return failure, because + * it is not a must. + */ + return 0; + } + + dm_gpio_set_dir_flags(scl_gpio, GPIOD_IS_IN); + dm_gpio_set_dir_flags(sda_gpio, GPIOD_IS_IN); + scl = dm_gpio_get_value(scl_gpio); + sda = dm_gpio_get_value(sda_gpio); + + if ((sda & scl) == 1) + goto exit; /* Bus is idle already */ + + /* Send high and low on the SCL line */ + for (i = 0; i < 9; i++) { + dm_gpio_set_dir_flags(scl_gpio, GPIOD_IS_OUT); + dm_gpio_set_value(scl_gpio, 0); + udelay(50); + dm_gpio_set_dir_flags(scl_gpio, GPIOD_IS_IN); + udelay(50); + } + start_time = get_timer(0); + for (;;) { + dm_gpio_set_dir_flags(scl_gpio, GPIOD_IS_IN); + dm_gpio_set_dir_flags(sda_gpio, GPIOD_IS_IN); + scl = dm_gpio_get_value(scl_gpio); + sda = dm_gpio_get_value(sda_gpio); + if ((sda & scl) == 1) + break; + WATCHDOG_RESET(); + elapsed = get_timer(start_time); + if (elapsed > (CONFIG_SYS_HZ / 5)) { /* .2 seconds */ + ret = -EBUSY; + printf("%s: failed to clear bus, sda=%d scl=%d\n", __func__, sda, scl); + break; + } + } + +exit: + pinctrl_select_state(bus, "default"); + return ret; +} #endif static int i2c_init_transfer(struct mxc_i2c_bus *i2c_bus, u8 chip, @@ -664,8 +722,10 @@ static int mxc_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) static int mxc_i2c_probe(struct udevice *bus) { struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus); + const void *fdt = gd->fdt_blob; + int node = bus->of_offset; fdt_addr_t addr; - int ret; + int ret, ret2; i2c_bus->driver_data = dev_get_driver_data(bus); @@ -675,12 +735,35 @@ static int mxc_i2c_probe(struct udevice *bus) i2c_bus->base = addr; i2c_bus->index = bus->seq; + i2c_bus->bus = bus; /* Enable clk */ ret = enable_i2c_clk(1, bus->seq); if (ret < 0) return ret; + /* + * See Documentation/devicetree/bindings/i2c/i2c-imx.txt + * Use gpio to force bus idle when necessary. + */ + ret = fdt_find_string(fdt, node, "pinctrl-names", "gpio"); + if (ret < 0) { + dev_info(dev, "i2c bus %d at %lu, no gpio pinctrl state.\n", bus->seq, i2c_bus->base); + } else { + ret = gpio_request_by_name_nodev(fdt, node, "scl-gpios", + 0, &i2c_bus->scl_gpio, + GPIOD_IS_OUT); + ret2 = gpio_request_by_name_nodev(fdt, node, "sda-gpios", + 0, &i2c_bus->sda_gpio, + GPIOD_IS_OUT); + if (!dm_gpio_is_valid(&i2c_bus->sda_gpio) | + !dm_gpio_is_valid(&i2c_bus->scl_gpio) | + ret | ret2) { + dev_err(dev, "i2c bus %d at %lu, fail to request scl/sda gpio\n", bus->seq, i2c_bus->base); + return -ENODEV; + } + } + ret = i2c_idle_bus(i2c_bus); if (ret < 0) { /* Disable clk */ diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index 79a5c94..a7f3fb4 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -371,6 +371,23 @@ static int omap24_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, return 1; } +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if (alen > 0) + chip |= ((addr >> (alen * 8)) & + CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + /* Wait until bus not busy */ if (wait_for_bb(adap)) return 1; @@ -501,6 +518,23 @@ static int omap24_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, return 1; } +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if (alen > 0) + chip |= ((addr >> (alen * 8)) & + CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + /* Wait until bus not busy */ if (wait_for_bb(adap)) return 1; diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index dc8532f..4d3df11 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -16,6 +16,15 @@ config DM_MMC appear as block devices in U-Boot and can support filesystems such as EXT4 and FAT. +config MSM_SDHCI + bool "Qualcomm SDHCI controller" + depends on DM_MMC + help + Enables support for SDHCI 2.0 controller present on some Qualcomm + Snapdragon devices. This device is compatible with eMMC v4.5 and + SD 3.0 specifications. Both SD and eMMC devices are supported. + Card-detect gpios are not supported. + config ROCKCHIP_DWMMC bool "Rockchip SD/MMC controller support" depends on DM_MMC && OF_CONTROL diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index b85e4bf..585aaf3 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -50,3 +50,4 @@ else obj-$(CONFIG_GENERIC_MMC) += mmc_write.o endif obj-$(CONFIG_PIC32_SDHCI) += pic32_sdhci.o +obj-$(CONFIG_MSM_SDHCI) += msm_sdhci.o diff --git a/drivers/mmc/bcm2835_sdhci.c b/drivers/mmc/bcm2835_sdhci.c index 227d2df..680b754 100644 --- a/drivers/mmc/bcm2835_sdhci.c +++ b/drivers/mmc/bcm2835_sdhci.c @@ -178,7 +178,7 @@ int bcm2835_sdhci_init(u32 regbase, u32 emmc_freq) host = &bcm_host->host; host->name = "bcm2835_sdhci"; - host->ioaddr = (void *)regbase; + host->ioaddr = (void *)(unsigned long)regbase; host->quirks = SDHCI_QUIRK_BROKEN_VOLTAGE | SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_NO_HISPD_BIT; host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index ea5f4bf..3acf9e8 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -20,6 +20,8 @@ #include <fsl_esdhc.h> #include <fdt_support.h> #include <asm/io.h> +#include <dm.h> +#include <asm-generic/gpio.h> DECLARE_GLOBAL_DATA_PTR; @@ -72,6 +74,30 @@ struct fsl_esdhc { uint scr; /* eSDHC control register */ }; +/** + * struct fsl_esdhc_priv + * + * @esdhc_regs: registers of the sdhc controller + * @sdhc_clk: Current clk of the sdhc controller + * @bus_width: bus width, 1bit, 4bit or 8bit + * @cfg: mmc config + * @mmc: mmc + * Following is used when Driver Model is enabled for MMC + * @dev: pointer for the device + * @non_removable: 0: removable; 1: non-removable + * @cd_gpio: gpio for card detection + */ +struct fsl_esdhc_priv { + struct fsl_esdhc *esdhc_regs; + unsigned int sdhc_clk; + unsigned int bus_width; + struct mmc_config cfg; + struct mmc *mmc; + struct udevice *dev; + int non_removable; + struct gpio_desc cd_gpio; +}; + /* Return the XFERTYP flags for a given command and data packet */ static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) { @@ -118,8 +144,8 @@ static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) static void esdhc_pio_read_write(struct mmc *mmc, struct mmc_data *data) { - struct fsl_esdhc_cfg *cfg = mmc->priv; - struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + struct fsl_esdhc_priv *priv = mmc->priv; + struct fsl_esdhc *regs = priv->esdhc_regs; uint blocks; char *buffer; uint databuf; @@ -180,8 +206,8 @@ esdhc_pio_read_write(struct mmc *mmc, struct mmc_data *data) static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) { int timeout; - struct fsl_esdhc_cfg *cfg = mmc->priv; - struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + struct fsl_esdhc_priv *priv = mmc->priv; + struct fsl_esdhc *regs = priv->esdhc_regs; #ifdef CONFIG_FSL_LAYERSCAPE dma_addr_t addr; #endif @@ -312,8 +338,8 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) int err = 0; uint xfertyp; uint irqstat; - struct fsl_esdhc_cfg *cfg = mmc->priv; - volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + struct fsl_esdhc_priv *priv = mmc->priv; + struct fsl_esdhc *regs = priv->esdhc_regs; #ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111 if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) @@ -482,9 +508,9 @@ out: static void set_sysctl(struct mmc *mmc, uint clock) { int div, pre_div; - struct fsl_esdhc_cfg *cfg = mmc->priv; - volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; - int sdhc_clk = cfg->sdhc_clk; + struct fsl_esdhc_priv *priv = mmc->priv; + struct fsl_esdhc *regs = priv->esdhc_regs; + int sdhc_clk = priv->sdhc_clk; uint clk; if (clock < mmc->cfg->f_min) @@ -527,8 +553,8 @@ static void set_sysctl(struct mmc *mmc, uint clock) #ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK static void esdhc_clock_control(struct mmc *mmc, bool enable) { - struct fsl_esdhc_cfg *cfg = mmc->priv; - struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + struct fsl_esdhc_priv *priv = mmc->priv; + struct fsl_esdhc *regs = priv->esdhc_regs; u32 value; u32 time_out; @@ -556,8 +582,8 @@ static void esdhc_clock_control(struct mmc *mmc, bool enable) static void esdhc_set_ios(struct mmc *mmc) { - struct fsl_esdhc_cfg *cfg = mmc->priv; - struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + struct fsl_esdhc_priv *priv = mmc->priv; + struct fsl_esdhc *regs = priv->esdhc_regs; #ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK /* Select to use peripheral clock */ @@ -580,8 +606,8 @@ static void esdhc_set_ios(struct mmc *mmc) static int esdhc_init(struct mmc *mmc) { - struct fsl_esdhc_cfg *cfg = mmc->priv; - struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + struct fsl_esdhc_priv *priv = mmc->priv; + struct fsl_esdhc *regs = priv->esdhc_regs; int timeout = 1000; /* Reset the entire host controller */ @@ -621,14 +647,23 @@ static int esdhc_init(struct mmc *mmc) static int esdhc_getcd(struct mmc *mmc) { - struct fsl_esdhc_cfg *cfg = mmc->priv; - struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + struct fsl_esdhc_priv *priv = mmc->priv; + struct fsl_esdhc *regs = priv->esdhc_regs; int timeout = 1000; #ifdef CONFIG_ESDHC_DETECT_QUIRK if (CONFIG_ESDHC_DETECT_QUIRK) return 1; #endif + +#ifdef CONFIG_DM_MMC + if (priv->non_removable) + return 1; + + if (dm_gpio_is_valid(&priv->cd_gpio)) + return dm_gpio_get_value(&priv->cd_gpio); +#endif + while (!(esdhc_read32(®s->prsstat) & PRSSTAT_CINS) && --timeout) udelay(1000); @@ -656,16 +691,29 @@ static const struct mmc_ops esdhc_ops = { .getcd = esdhc_getcd, }; -int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) +static int fsl_esdhc_cfg_to_priv(struct fsl_esdhc_cfg *cfg, + struct fsl_esdhc_priv *priv) +{ + if (!cfg || !priv) + return -EINVAL; + + priv->esdhc_regs = (struct fsl_esdhc *)(unsigned long)(cfg->esdhc_base); + priv->bus_width = cfg->max_bus_width; + priv->sdhc_clk = cfg->sdhc_clk; + + return 0; +}; + +static int fsl_esdhc_init(struct fsl_esdhc_priv *priv) { struct fsl_esdhc *regs; struct mmc *mmc; u32 caps, voltage_caps; - if (!cfg) - return -1; + if (!priv) + return -EINVAL; - regs = (struct fsl_esdhc *)cfg->esdhc_base; + regs = priv->esdhc_regs; /* First reset the eSDHC controller */ esdhc_reset(regs); @@ -676,7 +724,7 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) #endif writel(SDHCI_IRQ_EN_BITS, ®s->irqstaten); - memset(&cfg->cfg, 0, sizeof(cfg->cfg)); + memset(&priv->cfg, 0, sizeof(priv->cfg)); voltage_caps = 0; caps = esdhc_read32(®s->hostcapblt); @@ -698,47 +746,83 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) if (caps & ESDHC_HOSTCAPBLT_VS33) voltage_caps |= MMC_VDD_32_33 | MMC_VDD_33_34; - cfg->cfg.name = "FSL_SDHC"; - cfg->cfg.ops = &esdhc_ops; + priv->cfg.name = "FSL_SDHC"; + priv->cfg.ops = &esdhc_ops; #ifdef CONFIG_SYS_SD_VOLTAGE - cfg->cfg.voltages = CONFIG_SYS_SD_VOLTAGE; + priv->cfg.voltages = CONFIG_SYS_SD_VOLTAGE; #else - cfg->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + priv->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; #endif - if ((cfg->cfg.voltages & voltage_caps) == 0) { + if ((priv->cfg.voltages & voltage_caps) == 0) { printf("voltage not supported by controller\n"); return -1; } - cfg->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT; + if (priv->bus_width == 8) + priv->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT; + else if (priv->bus_width == 4) + priv->cfg.host_caps = MMC_MODE_4BIT; + + priv->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT; #ifdef CONFIG_SYS_FSL_ESDHC_HAS_DDR_MODE - cfg->cfg.host_caps |= MMC_MODE_DDR_52MHz; + priv->cfg.host_caps |= MMC_MODE_DDR_52MHz; #endif - if (cfg->max_bus_width > 0) { - if (cfg->max_bus_width < 8) - cfg->cfg.host_caps &= ~MMC_MODE_8BIT; - if (cfg->max_bus_width < 4) - cfg->cfg.host_caps &= ~MMC_MODE_4BIT; + if (priv->bus_width > 0) { + if (priv->bus_width < 8) + priv->cfg.host_caps &= ~MMC_MODE_8BIT; + if (priv->bus_width < 4) + priv->cfg.host_caps &= ~MMC_MODE_4BIT; } if (caps & ESDHC_HOSTCAPBLT_HSS) - cfg->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + priv->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; #ifdef CONFIG_ESDHC_DETECT_8_BIT_QUIRK if (CONFIG_ESDHC_DETECT_8_BIT_QUIRK) - cfg->cfg.host_caps &= ~MMC_MODE_8BIT; + priv->cfg.host_caps &= ~MMC_MODE_8BIT; #endif - cfg->cfg.f_min = 400000; - cfg->cfg.f_max = min(cfg->sdhc_clk, (u32)52000000); + priv->cfg.f_min = 400000; + priv->cfg.f_max = min(priv->sdhc_clk, (u32)52000000); - cfg->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + priv->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; - mmc = mmc_create(&cfg->cfg, cfg); + mmc = mmc_create(&priv->cfg, priv); if (mmc == NULL) return -1; + priv->mmc = mmc; + + return 0; +} + +int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) +{ + struct fsl_esdhc_priv *priv; + int ret; + + if (!cfg) + return -EINVAL; + + priv = calloc(sizeof(struct fsl_esdhc_priv), 1); + if (!priv) + return -ENOMEM; + + ret = fsl_esdhc_cfg_to_priv(cfg, priv); + if (ret) { + debug("%s xlate failure\n", __func__); + free(priv); + return ret; + } + + ret = fsl_esdhc_init(priv); + if (ret) { + debug("%s init failure\n", __func__); + free(priv); + return ret; + } + return 0; } @@ -819,3 +903,92 @@ void fdt_fixup_esdhc(void *blob, bd_t *bd) 4 + 1, 1); } #endif + +#ifdef CONFIG_DM_MMC +#include <asm/arch/clock.h> +static int fsl_esdhc_probe(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct fsl_esdhc_priv *priv = dev_get_priv(dev); + const void *fdt = gd->fdt_blob; + int node = dev->of_offset; + fdt_addr_t addr; + unsigned int val; + int ret; + + addr = dev_get_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->esdhc_regs = (struct fsl_esdhc *)addr; + priv->dev = dev; + + val = fdtdec_get_int(fdt, node, "bus-width", -1); + if (val == 8) + priv->bus_width = 8; + else if (val == 4) + priv->bus_width = 4; + else + priv->bus_width = 1; + + if (fdt_get_property(fdt, node, "non-removable", NULL)) { + priv->non_removable = 1; + } else { + priv->non_removable = 0; + gpio_request_by_name_nodev(fdt, node, "cd-gpios", 0, + &priv->cd_gpio, GPIOD_IS_IN); + } + + /* + * TODO: + * Because lack of clk driver, if SDHC clk is not enabled, + * need to enable it first before this driver is invoked. + * + * we use MXC_ESDHC_CLK to get clk freq. + * If one would like to make this function work, + * the aliases should be provided in dts as this: + * + * aliases { + * mmc0 = &usdhc1; + * mmc1 = &usdhc2; + * mmc2 = &usdhc3; + * mmc3 = &usdhc4; + * }; + * Then if your board only supports mmc2 and mmc3, but we can + * correctly get the seq as 2 and 3, then let mxc_get_clock + * work as expected. + */ + priv->sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK + dev->seq); + if (priv->sdhc_clk <= 0) { + dev_err(dev, "Unable to get clk for %s\n", dev->name); + return -EINVAL; + } + + ret = fsl_esdhc_init(priv); + if (ret) { + dev_err(dev, "fsl_esdhc_init failure\n"); + return ret; + } + + upriv->mmc = priv->mmc; + + return 0; +} + +static const struct udevice_id fsl_esdhc_ids[] = { + { .compatible = "fsl,imx6ul-usdhc", }, + { .compatible = "fsl,imx6sx-usdhc", }, + { .compatible = "fsl,imx6sl-usdhc", }, + { .compatible = "fsl,imx6q-usdhc", }, + { .compatible = "fsl,imx7d-usdhc", }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(fsl_esdhc) = { + .name = "fsl-esdhc-mmc", + .id = UCLASS_MMC, + .of_match = fsl_esdhc_ids, + .probe = fsl_esdhc_probe, + .priv_auto_alloc_size = sizeof(struct fsl_esdhc_priv), +}; +#endif diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 8b2e606..d3c22ab 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -61,7 +61,10 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) printf("CMD_SEND:%d\n", cmd->cmdidx); printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg); ret = mmc->cfg->ops->send_cmd(mmc, cmd, data); - switch (cmd->resp_type) { + if (ret) { + printf("\t\tRET\t\t\t %d\n", ret); + } else { + switch (cmd->resp_type) { case MMC_RSP_NONE: printf("\t\tMMC_RSP_NONE\n"); break; @@ -101,6 +104,7 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) default: printf("\t\tERROR MMC rsp not supported\n"); break; + } } #else ret = mmc->cfg->ops->send_cmd(mmc, cmd, data); @@ -906,20 +910,20 @@ retry_scr: mmc->scr[1] = __be32_to_cpu(scr[1]); switch ((mmc->scr[0] >> 24) & 0xf) { - case 0: - mmc->version = SD_VERSION_1_0; - break; - case 1: - mmc->version = SD_VERSION_1_10; - break; - case 2: - mmc->version = SD_VERSION_2; - if ((mmc->scr[0] >> 15) & 0x1) - mmc->version = SD_VERSION_3; - break; - default: - mmc->version = SD_VERSION_1_0; - break; + case 0: + mmc->version = SD_VERSION_1_0; + break; + case 1: + mmc->version = SD_VERSION_1_10; + break; + case 2: + mmc->version = SD_VERSION_2; + if ((mmc->scr[0] >> 15) & 0x1) + mmc->version = SD_VERSION_3; + break; + default: + mmc->version = SD_VERSION_1_0; + break; } if (mmc->scr[0] & SD_DATA_4BIT) @@ -1102,24 +1106,24 @@ static int mmc_startup(struct mmc *mmc) int version = (cmd.response[0] >> 26) & 0xf; switch (version) { - case 0: - mmc->version = MMC_VERSION_1_2; - break; - case 1: - mmc->version = MMC_VERSION_1_4; - break; - case 2: - mmc->version = MMC_VERSION_2_2; - break; - case 3: - mmc->version = MMC_VERSION_3; - break; - case 4: - mmc->version = MMC_VERSION_4; - break; - default: - mmc->version = MMC_VERSION_1_2; - break; + case 0: + mmc->version = MMC_VERSION_1_2; + break; + case 1: + mmc->version = MMC_VERSION_1_4; + break; + case 2: + mmc->version = MMC_VERSION_2_2; + break; + case 3: + mmc->version = MMC_VERSION_3; + break; + case 4: + mmc->version = MMC_VERSION_4; + break; + default: + mmc->version = MMC_VERSION_1_2; + break; } } diff --git a/drivers/mmc/msm_sdhci.c b/drivers/mmc/msm_sdhci.c new file mode 100644 index 0000000..1e2a29b --- /dev/null +++ b/drivers/mmc/msm_sdhci.c @@ -0,0 +1,180 @@ +/* + * Qualcomm SDHCI driver - SD/eMMC controller + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * Based on Linux driver + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <sdhci.h> +#include <wait_bit.h> +#include <asm/io.h> +#include <linux/bitops.h> + +/* Non-standard registers needed for SDHCI startup */ +#define SDCC_MCI_POWER 0x0 +#define SDCC_MCI_POWER_SW_RST BIT(7) + +/* This is undocumented register */ +#define SDCC_MCI_VERSION 0x50 +#define SDCC_MCI_VERSION_MAJOR_SHIFT 28 +#define SDCC_MCI_VERSION_MAJOR_MASK (0xf << SDCC_MCI_VERSION_MAJOR_SHIFT) +#define SDCC_MCI_VERSION_MINOR_MASK 0xff + +#define SDCC_MCI_STATUS2 0x6C +#define SDCC_MCI_STATUS2_MCI_ACT 0x1 +#define SDCC_MCI_HC_MODE 0x78 + +/* Offset to SDHCI registers */ +#define SDCC_SDHCI_OFFSET 0x900 + +/* Non standard (?) SDHCI register */ +#define SDHCI_VENDOR_SPEC_CAPABILITIES0 0x11c + +struct msm_sdhc { + struct sdhci_host host; + void *base; +}; + +DECLARE_GLOBAL_DATA_PTR; + +static int msm_sdc_clk_init(struct udevice *dev) +{ + uint clk_rate = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, + "clock-frequency", 400000); + uint clkd[2]; /* clk_id and clk_no */ + int clk_offset; + struct udevice *clk; + int ret; + + ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, "clock", clkd, + 2); + if (ret) + return ret; + + clk_offset = fdt_node_offset_by_phandle(gd->fdt_blob, clkd[0]); + if (clk_offset < 0) + return clk_offset; + + ret = uclass_get_device_by_of_offset(UCLASS_CLK, clk_offset, &clk); + if (ret) + return ret; + + ret = clk_set_periph_rate(clk, clkd[1], clk_rate); + if (ret < 0) + return ret; + + return 0; +} + +static int msm_sdc_probe(struct udevice *dev) +{ + struct msm_sdhc *prv = dev_get_priv(dev); + struct sdhci_host *host = &prv->host; + u32 core_version, core_minor, core_major; + int ret; + + host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_BROKEN_R1B; + + /* Init clocks */ + ret = msm_sdc_clk_init(dev); + if (ret) + return ret; + + /* Reset the core and Enable SDHC mode */ + writel(readl(prv->base + SDCC_MCI_POWER) | SDCC_MCI_POWER_SW_RST, + prv->base + SDCC_MCI_POWER); + + + /* Wait for reset to be written to register */ + if (wait_for_bit(__func__, prv->base + SDCC_MCI_STATUS2, + SDCC_MCI_STATUS2_MCI_ACT, false, 10, false)) { + printf("msm_sdhci: reset request failed\n"); + return -EIO; + } + + /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */ + if (wait_for_bit(__func__, prv->base + SDCC_MCI_POWER, + SDCC_MCI_POWER_SW_RST, false, 2, false)) { + printf("msm_sdhci: stuck in reset\n"); + return -ETIMEDOUT; + } + + /* Enable host-controller mode */ + writel(1, prv->base + SDCC_MCI_HC_MODE); + + core_version = readl(prv->base + SDCC_MCI_VERSION); + + core_major = (core_version & SDCC_MCI_VERSION_MAJOR_MASK); + core_major >>= SDCC_MCI_VERSION_MAJOR_SHIFT; + + core_minor = core_version & SDCC_MCI_VERSION_MINOR_MASK; + + /* + * Support for some capabilities is not advertised by newer + * controller versions and must be explicitly enabled. + */ + if (core_major >= 1 && core_minor != 0x11 && core_minor != 0x12) { + u32 caps = readl(host->ioaddr + SDHCI_CAPABILITIES); + caps |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT; + writel(caps, host->ioaddr + SDHCI_VENDOR_SPEC_CAPABILITIES0); + } + + /* Set host controller version */ + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + + /* automatically detect max and min speed */ + return add_sdhci(host, 0, 0); +} + +static int msm_sdc_remove(struct udevice *dev) +{ + struct msm_sdhc *priv = dev_get_priv(dev); + + /* Disable host-controller mode */ + writel(0, priv->base + SDCC_MCI_HC_MODE); + + return 0; +} + +static int msm_ofdata_to_platdata(struct udevice *dev) +{ + struct udevice *parent = dev->parent; + struct msm_sdhc *priv = dev_get_priv(dev); + struct sdhci_host *host = &priv->host; + + host->name = strdup(dev->name); + host->ioaddr = (void *)dev_get_addr(dev); + host->bus_width = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "bus-width", 4); + host->index = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, "index", 0); + priv->base = (void *)fdtdec_get_addr_size_auto_parent(gd->fdt_blob, + parent->of_offset, + dev->of_offset, + "reg", 1, NULL); + if (priv->base == (void *)FDT_ADDR_T_NONE || + host->ioaddr == (void *)FDT_ADDR_T_NONE) + return -EINVAL; + + return 0; +} + +static const struct udevice_id msm_mmc_ids[] = { + { .compatible = "qcom,sdhci-msm-v4" }, + { } +}; + +U_BOOT_DRIVER(msm_sdc_drv) = { + .name = "msm_sdc", + .id = UCLASS_MMC, + .of_match = msm_mmc_ids, + .ofdata_to_platdata = msm_ofdata_to_platdata, + .probe = msm_sdc_probe, + .remove = msm_sdc_remove, + .priv_auto_alloc_size = sizeof(struct msm_sdhc), +}; diff --git a/drivers/mmc/socfpga_dw_mmc.c b/drivers/mmc/socfpga_dw_mmc.c index 43a7e7e..097db81 100644 --- a/drivers/mmc/socfpga_dw_mmc.c +++ b/drivers/mmc/socfpga_dw_mmc.c @@ -6,7 +6,6 @@ #include <common.h> #include <asm/arch/clock_manager.h> -#include <asm/arch/dwmmc.h> #include <asm/arch/system_manager.h> #include <dm.h> #include <dwmmc.h> diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c index 7b33094..ce2dc4a 100644 --- a/drivers/mmc/sunxi_mmc.c +++ b/drivers/mmc/sunxi_mmc.c @@ -339,7 +339,7 @@ static int sunxi_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, cmdval |= SUNXI_MMC_CMD_CHK_RESPONSE_CRC; if (data) { - if ((u32) data->dest & 0x3) { + if ((u32)(long)data->dest & 0x3) { error = -1; goto out; } @@ -480,6 +480,10 @@ struct mmc *sunxi_mmc_init(int sdc_no) cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; cfg->host_caps = MMC_MODE_4BIT; +#ifdef CONFIG_MACH_SUN50I + if (sdc_no == 2) + cfg->host_caps = MMC_MODE_8BIT; +#endif cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c index 3bc4d94..81a80cd 100644 --- a/drivers/mmc/uniphier-sd.c +++ b/drivers/mmc/uniphier-sd.c @@ -12,6 +12,7 @@ #include <dm/device.h> #include <linux/compat.h> #include <linux/io.h> +#include <linux/sizes.h> #include <asm/unaligned.h> #include <asm/dma-mapping.h> @@ -650,15 +651,17 @@ int uniphier_sd_probe(struct udevice *dev) struct uniphier_sd_priv *priv = dev_get_priv(dev); struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); fdt_addr_t base; - fdt_size_t size; struct udevice *clk_dev; int clk_id; int ret; priv->dev = dev; - base = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size); - priv->regbase = map_sysmem(base, size); + base = dev_get_addr(dev); + if (base == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regbase = map_sysmem(base, SZ_2K); if (!priv->regbase) return -ENOMEM; diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 7f018a4..703700a 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_FTSMC020) += ftsmc020.o obj-$(CONFIG_FLASH_CFI_LEGACY) += jedec_flash.o obj-$(CONFIG_MW_EEPROM) += mw_eeprom.o obj-$(CONFIG_ST_SMI) += st_smi.o +obj-$(CONFIG_STM32_FLASH) += stm32_flash.o diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index 39932f4..18831c6 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -2203,6 +2203,8 @@ ulong flash_get_size (phys_addr_t base, int banknum) flash_isset (info, sect_cnt, FLASH_OFFSET_PROTECT, FLASH_STATUS_PROTECT); + flash_write_cmd(info, sect_cnt, 0, + FLASH_CMD_RESET); break; case CFI_CMDSET_AMD_EXTENDED: case CFI_CMDSET_AMD_STANDARD: diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 192be7d..5894fcc 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -431,7 +431,16 @@ static void find_valid_banks(struct denali_nand_info *denali) static void detect_max_banks(struct denali_nand_info *denali) { uint32_t features = readl(denali->flash_reg + FEATURES); - denali->max_banks = 2 << (features & FEATURES__N_BANKS); + /* + * Read the revision register, so we can calculate the max_banks + * properly: the encoding changed from rev 5.0 to 5.1 + */ + u32 revision = MAKE_COMPARABLE_REVISION( + readl(denali->flash_reg + REVISION)); + if (revision < REVISION_5_1) + denali->max_banks = 2 << (features & FEATURES__N_BANKS); + else + denali->max_banks = 1 << (features & FEATURES__N_BANKS); } static void detect_partition_feature(struct denali_nand_info *denali) @@ -741,7 +750,7 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op) { uint32_t mode; const int page_count = 1; - uint32_t addr = (uint32_t)denali->buf.dma_buf; + uint64_t addr = (unsigned long)denali->buf.dma_buf; flush_dcache_range(addr, addr + sizeof(denali->buf.dma_buf)); @@ -759,7 +768,7 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op) index_addr(denali, mode, addr); /* 3. set memory high address bits 64:32 */ - index_addr(denali, mode, 0); + index_addr(denali, mode, addr >> 32); #else mode = MODE_10 | BANK(denali->flash_bank); @@ -769,7 +778,7 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op) index_addr(denali, mode | denali->page, 0x2000 | op | page_count); /* 2. set memory high address bits 23:8 */ - index_addr(denali, mode | ((addr >> 16) << 8), 0x2200); + index_addr(denali, mode | (((addr >> 16) & 0xffff) << 8), 0x2200); /* 3. set memory low address bits 23:8 */ index_addr(denali, mode | ((addr & 0xffff) << 8), 0x2300); diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index 93b5725..db1457a 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -166,6 +166,8 @@ #define REVISION 0x370 #define REVISION__VALUE 0xffff +#define MAKE_COMPARABLE_REVISION(x) swab16((x) & REVISION__VALUE) +#define REVISION_5_1 0x00000501 #define ONFI_DEVICE_FEATURES 0x380 #define ONFI_DEVICE_FEATURES__VALUE 0x003f diff --git a/drivers/mtd/nand/kirkwood_nand.c b/drivers/mtd/nand/kirkwood_nand.c index 4fc34d6..d734113 100644 --- a/drivers/mtd/nand/kirkwood_nand.c +++ b/drivers/mtd/nand/kirkwood_nand.c @@ -9,6 +9,7 @@ #include <common.h> #include <asm/io.h> #include <asm/arch/soc.h> +#include <asm/arch/mpp.h> #include <nand.h> /* NAND Flash Soc registers */ @@ -22,6 +23,8 @@ struct kwnandf_registers { static struct kwnandf_registers *nf_reg = (struct kwnandf_registers *)KW_NANDF_BASE; +static u32 nand_mpp_backup[9] = { 0 }; + /* * hardware specific access to control-lines/bits */ @@ -49,6 +52,22 @@ static void kw_nand_hwcontrol(struct mtd_info *mtd, int cmd, void kw_nand_select_chip(struct mtd_info *mtd, int chip) { u32 data; + static const u32 nand_config[] = { + MPP0_NF_IO2, + MPP1_NF_IO3, + MPP2_NF_IO4, + MPP3_NF_IO5, + MPP4_NF_IO6, + MPP5_NF_IO7, + MPP18_NF_IO0, + MPP19_NF_IO1, + 0 + }; + + if (chip >= 0) + kirkwood_mpp_conf(nand_config, nand_mpp_backup); + else + kirkwood_mpp_conf(nand_mpp_backup, NULL); data = readl(&nf_reg->ctrl); data |= NAND_ACTCEBOOT_BIT; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 9392742..d529467 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -19,14 +19,6 @@ #include "pxa3xx_nand.h" -/* Some U-Boot compatibility macros */ -#define writesl(a, d, s) __raw_writesl((unsigned long)a, d, s) -#define readsl(a, d, s) __raw_readsl((unsigned long)a, d, s) -#define writesw(a, d, s) __raw_writesw((unsigned long)a, d, s) -#define readsw(a, d, s) __raw_readsw((unsigned long)a, d, s) -#define writesb(a, d, s) __raw_writesb((unsigned long)a, d, s) -#define readsb(a, d, s) __raw_readsb((unsigned long)a, d, s) - #define TIMEOUT_DRAIN_FIFO 5 /* in ms */ #define CHIP_DELAY_TIMEOUT 200 #define NAND_STOP_DELAY 40 diff --git a/drivers/mtd/stm32_flash.c b/drivers/mtd/stm32_flash.c new file mode 100644 index 0000000..e16b6cd --- /dev/null +++ b/drivers/mtd/stm32_flash.c @@ -0,0 +1,151 @@ +/* + * (C) Copyright 2015 + * Kamil Lulko, <kamil.lulko@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/stm32.h> +#include "stm32_flash.h" + +flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; + +#define STM32_FLASH ((struct stm32_flash_regs *)FLASH_CNTL_BASE) + +void stm32_flash_latency_cfg(int latency) +{ + /* 5 wait states, Prefetch enabled, D-Cache enabled, I-Cache enabled */ + writel(FLASH_ACR_WS(5) | FLASH_ACR_PRFTEN | FLASH_ACR_ICEN + | FLASH_ACR_DCEN, &STM32_FLASH->acr); +} + +static void stm32_flash_lock(u8 lock) +{ + if (lock) { + setbits_le32(&STM32_FLASH->cr, STM32_FLASH_CR_LOCK); + } else { + writel(STM32_FLASH_KEY1, &STM32_FLASH->key); + writel(STM32_FLASH_KEY2, &STM32_FLASH->key); + } +} + +unsigned long flash_init(void) +{ + unsigned long total_size = 0; + u8 i, j; + + for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { + flash_info[i].flash_id = FLASH_STM32; + flash_info[i].sector_count = CONFIG_SYS_MAX_FLASH_SECT; + flash_info[i].start[0] = CONFIG_SYS_FLASH_BASE + (i << 20); + flash_info[i].size = sect_sz_kb[0]; + for (j = 1; j < CONFIG_SYS_MAX_FLASH_SECT; j++) { + flash_info[i].start[j] = flash_info[i].start[j - 1] + + (sect_sz_kb[j - 1]); + flash_info[i].size += sect_sz_kb[j]; + } + total_size += flash_info[i].size; + } + + return total_size; +} + +void flash_print_info(flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf("missing or unknown FLASH type\n"); + return; + } else if (info->flash_id == FLASH_STM32) { + printf("stm32 Embedded Flash\n"); + } + + printf(" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf(" Sector Start Addresses:"); + for (i = 0; i < info->sector_count; ++i) { + if ((i % 5) == 0) + printf("\n "); + printf(" %08lX%s", + info->start[i], + info->protect[i] ? " (RO)" : " "); + } + printf("\n"); + return; +} + +int flash_erase(flash_info_t *info, int first, int last) +{ + u8 bank = 0xFF; + int i; + + for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { + if (info == &flash_info[i]) { + bank = i; + break; + } + } + if (bank == 0xFF) + return -1; + + stm32_flash_lock(0); + + for (i = first; i <= last; i++) { + while (readl(&STM32_FLASH->sr) & STM32_FLASH_SR_BSY) + ; + + /* clear old sector number before writing a new one */ + clrbits_le32(&STM32_FLASH->cr, STM32_FLASH_CR_SNB_MASK); + + if (bank == 0) { + setbits_le32(&STM32_FLASH->cr, + (i << STM32_FLASH_CR_SNB_OFFSET)); + } else if (bank == 1) { + setbits_le32(&STM32_FLASH->cr, + ((0x10 | i) << STM32_FLASH_CR_SNB_OFFSET)); + } else { + stm32_flash_lock(1); + return -1; + } + setbits_le32(&STM32_FLASH->cr, STM32_FLASH_CR_SER); + setbits_le32(&STM32_FLASH->cr, STM32_FLASH_CR_STRT); + + while (readl(&STM32_FLASH->sr) & STM32_FLASH_SR_BSY) + ; + + clrbits_le32(&STM32_FLASH->cr, STM32_FLASH_CR_SER); + } + + stm32_flash_lock(1); + return 0; +} + +int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong i; + + while (readl(&STM32_FLASH->sr) & STM32_FLASH_SR_BSY) + ; + + stm32_flash_lock(0); + + setbits_le32(&STM32_FLASH->cr, STM32_FLASH_CR_PG); + /* To make things simple use byte writes only */ + for (i = 0; i < cnt; i++) { + *(uchar *)(addr + i) = src[i]; + /* avoid re-ordering flash data write and busy status + * check as flash memory space attributes are generally Normal + */ + mb(); + while (readl(&STM32_FLASH->sr) & STM32_FLASH_SR_BSY) + ; + } + clrbits_le32(&STM32_FLASH->cr, STM32_FLASH_CR_PG); + stm32_flash_lock(1); + + return 0; +} diff --git a/drivers/mtd/stm32_flash.h b/drivers/mtd/stm32_flash.h new file mode 100644 index 0000000..8cb81ef --- /dev/null +++ b/drivers/mtd/stm32_flash.h @@ -0,0 +1,27 @@ +struct stm32_flash_regs { + u32 acr; + u32 key; + u32 optkeyr; + u32 sr; + u32 cr; + u32 optcr; + u32 optcr1; +}; + +#define STM32_FLASH_KEY1 0x45670123 +#define STM32_FLASH_KEY2 0xCDEF89AB + +#define STM32_FLASH_SR_BSY (1 << 16) + +#define STM32_FLASH_CR_PG (1 << 0) +#define STM32_FLASH_CR_SER (1 << 1) +#define STM32_FLASH_CR_STRT (1 << 16) +#define STM32_FLASH_CR_LOCK (1 << 31) +#define STM32_FLASH_CR_SNB_OFFSET 3 +#define STM32_FLASH_CR_SNB_MASK (15 << STM32_FLASH_CR_SNB_OFFSET) + +/* Flash ACR: Access control register */ +#define FLASH_ACR_WS(n) n +#define FLASH_ACR_PRFTEN (1 << 8) +#define FLASH_ACR_ICEN (1 << 9) +#define FLASH_ACR_DCEN (1 << 10) diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index bc2f51d..91b7690 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -13,6 +13,27 @@ config PHYLIB help Enable Ethernet PHY (physical media interface) support. +config RTL8211X_PHY_FORCE_MASTER + bool "Ethernet PHY RTL8211x: force 1000BASE-T master mode" + depends on PHYLIB + help + Force master mode for 1000BASE-T on RTl8211x PHYs (except for RTL8211F). + This can work around link stability and data corruption issues on gigabit + links which can occur in slave mode on certain PHYs, e.g. on the + RTL8211C(L). + + Please note that two directly connected devices (i.e. via crossover cable) + will not be able to establish a link between each other if they both force + master mode. Multiple devices forcing master mode when connected by a + network switch do not pose a problem as the switch configures its affected + ports into slave mode. + + This option only affects gigabit links. If you must establish a direct + connection between two devices which both force master mode, try forcing + the link speed to 100MBit/s. + + If unsure, say N. + menuconfig NETDEVICES bool "Network device support" depends on NET @@ -94,6 +115,14 @@ config ETH_DESIGNWARE 100Mbit and 1 Gbit operation. You must enable CONFIG_PHYLIB to provide the PHY (physical media interface). +config MVPP2 + bool "Marvell Armada 375 network interface support" + depends on ARMADA_375 + select PHYLIB + help + This driver supports the network interface units in the + Marvell ARMADA 375 SoC. + config PCH_GBE bool "Intel Platform Controller Hub EG20T GMAC driver" depends on DM_ETH && DM_PCI @@ -102,6 +131,18 @@ config PCH_GBE This MAC is present in Intel Platform Controller Hub EG20T. It supports 10/100/1000 Mbps operation. +config RTL8139 + bool "Realtek 8139 series Ethernet controller driver" + help + This driver supports Realtek 8139 series fast ethernet family of + PCI chipsets/adapters. + +config RTL8169 + bool "Realtek 8169 series Ethernet controller driver" + help + This driver supports Realtek 8169 series gigabit ethernet family of + PCI/PCIe chipsets/adapters. + config XILINX_AXIEMAC depends on DM_ETH && (MICROBLAZE || ARCH_ZYNQ || ARCH_ZYNQMP) select PHYLIB diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 33a81ee..fbedd04 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_MPC5xxx_FEC) += mpc5xxx_fec.o obj-$(CONFIG_MPC512x_FEC) += mpc512x_fec.o obj-$(CONFIG_MVGBE) += mvgbe.o obj-$(CONFIG_MVNETA) += mvneta.o +obj-$(CONFIG_MVPP2) += mvpp2.o obj-$(CONFIG_NATSEMI) += natsemi.o obj-$(CONFIG_DRIVER_NE2000) += ne2000.o ne2000_base.o obj-$(CONFIG_DRIVER_AX88796L) += ax88796.o ne2000_base.o diff --git a/drivers/net/fsl-mc/dpio/qbman_sys.h b/drivers/net/fsl-mc/dpio/qbman_sys.h index 235d641..7a537fb 100644 --- a/drivers/net/fsl-mc/dpio/qbman_sys.h +++ b/drivers/net/fsl-mc/dpio/qbman_sys.h @@ -255,11 +255,11 @@ static inline int qbman_swp_sys_init(struct qbman_swp_sys *s, s->addr_cena = d->cena_bar; s->addr_cinh = d->cinh_bar; s->cena = (void *)valloc(CONFIG_SYS_PAGE_SIZE); - memset((void *)s->cena, 0x00, CONFIG_SYS_PAGE_SIZE); if (!s->cena) { printf("Could not allocate page for cena shadow\n"); return -1; } + memset((void *)s->cena, 0x00, CONFIG_SYS_PAGE_SIZE); #ifdef QBMAN_CHECKING /* We should never be asked to initialise for a portal that isn't in diff --git a/drivers/net/fsl-mc/mc.c b/drivers/net/fsl-mc/mc.c index 53c4966..1811b0f 100644 --- a/drivers/net/fsl-mc/mc.c +++ b/drivers/net/fsl-mc/mc.c @@ -356,6 +356,12 @@ static unsigned long get_mc_boot_timeout_ms(void) } #ifdef CONFIG_SYS_LS_MC_DRAM_AIOP_IMG_OFFSET + +__weak bool soc_has_aiop(void) +{ + return false; +} + static int load_mc_aiop_img(u64 aiop_fw_addr) { u64 mc_ram_addr = mc_get_dram_addr(); @@ -363,6 +369,9 @@ static int load_mc_aiop_img(u64 aiop_fw_addr) void *aiop_img; #endif + /* Check if AIOP is available */ + if (!soc_has_aiop()) + return -ENODEV; /* * Load the MC AIOP image in the MC private DRAM block: */ @@ -747,11 +756,11 @@ static int dpio_init(void) err_get_swp_init: dpio_disable(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpio->dpio_handle); err_get_enable: - free(dflt_dpio); err_get_attr: dpio_close(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpio->dpio_handle); dpio_destroy(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpio->dpio_handle); err_create: + free(dflt_dpio); err_malloc: return err; } @@ -1147,7 +1156,8 @@ int fsl_mc_ldpaa_exit(bd_t *bd) { int err = 0; - if (bd && get_mc_boot_status() == -1) + /* MC is not loaded intentionally, So return success. */ + if (bd && get_mc_boot_status() != 0) return 0; if (bd && !get_mc_boot_status() && get_dpl_apply_status() == -1) { @@ -1234,6 +1244,7 @@ static int do_fsl_mc(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) aiop_fw_addr = simple_strtoull(argv[3], NULL, 16); + /* if SoC doesn't have AIOP, err = -ENODEV */ err = load_mc_aiop_img(aiop_fw_addr); if (!err) printf("fsl-mc: AIOP FW applied\n"); diff --git a/drivers/net/ldpaa_eth/Makefile b/drivers/net/ldpaa_eth/Makefile index 74c4916..5587aa6 100644 --- a/drivers/net/ldpaa_eth/Makefile +++ b/drivers/net/ldpaa_eth/Makefile @@ -7,4 +7,3 @@ obj-y += ldpaa_wriop.o obj-y += ldpaa_eth.o obj-$(CONFIG_LS2080A) += ls2080a.o -obj-$(CONFIG_LS2085A) += ls2080a.o diff --git a/drivers/net/ldpaa_eth/ldpaa_eth.c b/drivers/net/ldpaa_eth/ldpaa_eth.c index 7f96883..bc7f8bb 100644 --- a/drivers/net/ldpaa_eth/ldpaa_eth.c +++ b/drivers/net/ldpaa_eth/ldpaa_eth.c @@ -14,15 +14,32 @@ #include <linux/compat.h> #include <fsl-mc/fsl_dpmac.h> +#include <fsl-mc/ldpaa_wriop.h> #include "ldpaa_eth.h" -#undef CONFIG_PHYLIB +#ifdef CONFIG_PHYLIB static int init_phy(struct eth_device *dev) { - /*TODO for external PHY */ + struct ldpaa_eth_priv *priv = (struct ldpaa_eth_priv *)dev->priv; + struct phy_device *phydev = NULL; + struct mii_dev *bus; - return 0; + bus = wriop_get_mdio(priv->dpmac_id); + if (bus == NULL) + return 0; + + phydev = phy_connect(bus, wriop_get_phy_address(priv->dpmac_id), + dev, wriop_get_enet_if(priv->dpmac_id)); + if (!phydev) { + printf("Failed to connect\n"); + return -1; + } + + priv->phydev = phydev; + + return phy_config(phydev); } +#endif #ifdef DEBUG static void ldpaa_eth_get_dpni_counter(void) @@ -380,7 +397,9 @@ static int ldpaa_eth_open(struct eth_device *net_dev, bd_t *bd) #ifdef DEBUG struct dpni_link_state link_state; #endif - int err; + int err = 0; + struct mii_dev *bus; + phy_interface_t enet_if; if (net_dev->state == ETH_STATE_ACTIVE) return 0; @@ -394,11 +413,48 @@ static int ldpaa_eth_open(struct eth_device *net_dev, bd_t *bd) printf("ERROR (DPL is deployed. No device available)\n"); return -ENODEV; } + /* DPMAC initialization */ err = ldpaa_dpmac_setup(priv); if (err < 0) goto err_dpmac_setup; +#ifdef CONFIG_PHYLIB + if (priv->phydev) + err = phy_startup(priv->phydev); + if (err) { + printf("%s: Could not initialize\n", + priv->phydev->dev->name); + goto err_dpamc_bind; + } +#else + priv->phydev = (struct phy_device *)malloc(sizeof(struct phy_device)); + memset(priv->phydev, 0, sizeof(struct phy_device)); + + priv->phydev->speed = SPEED_1000; + priv->phydev->link = 1; + priv->phydev->duplex = DUPLEX_FULL; +#endif + + bus = wriop_get_mdio(priv->dpmac_id); + enet_if = wriop_get_enet_if(priv->dpmac_id); + if ((bus == NULL) && + (enet_if == PHY_INTERFACE_MODE_XGMII)) { + priv->phydev = (struct phy_device *) + malloc(sizeof(struct phy_device)); + memset(priv->phydev, 0, sizeof(struct phy_device)); + + priv->phydev->speed = SPEED_10000; + priv->phydev->link = 1; + priv->phydev->duplex = DUPLEX_FULL; + } + + if (!priv->phydev->link) { + printf("%s: No link.\n", priv->phydev->dev->name); + err = -1; + goto err_dpamc_bind; + } + /* DPMAC binding DPNI */ err = ldpaa_dpmac_bind(priv); if (err) @@ -425,28 +481,24 @@ static int ldpaa_eth_open(struct eth_device *net_dev, bd_t *bd) return err; } -#ifdef CONFIG_PHYLIB - /* TODO Check this path */ - err = phy_startup(priv->phydev); - if (err) { - printf("%s: Could not initialize\n", priv->phydev->dev->name); - return err; - } -#else - priv->phydev->speed = SPEED_1000; - priv->phydev->link = 1; - priv->phydev->duplex = DUPLEX_FULL; -#endif - err = dpni_enable(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpni->dpni_handle); if (err < 0) { printf("dpni_enable() failed\n"); return err; } - dpmac_link_state.rate = SPEED_1000; - dpmac_link_state.options = DPMAC_LINK_OPT_AUTONEG; - dpmac_link_state.up = 1; + dpmac_link_state.rate = priv->phydev->speed; + + if (priv->phydev->autoneg == AUTONEG_DISABLE) + dpmac_link_state.options &= ~DPMAC_LINK_OPT_AUTONEG; + else + dpmac_link_state.options |= DPMAC_LINK_OPT_AUTONEG; + + if (priv->phydev->duplex == DUPLEX_HALF) + dpmac_link_state.options |= DPMAC_LINK_OPT_HALF_DUPLEX; + + dpmac_link_state.up = priv->phydev->link; + err = dpmac_set_link_state(dflt_mc_io, MC_CMD_NO_FLAGS, priv->dpmac_handle, &dpmac_link_state); if (err < 0) { @@ -484,10 +536,7 @@ static int ldpaa_eth_open(struct eth_device *net_dev, bd_t *bd) goto err_qdid; } - if (!priv->phydev->link) - printf("%s: No link.\n", priv->phydev->dev->name); - - return priv->phydev->link ? 0 : -1; + return priv->phydev->link; err_qdid: err_rx_flow: @@ -495,9 +544,10 @@ err_rx_flow: err_dpni_bind: ldpaa_dpbp_free(); err_dpbp_setup: -err_dpamc_bind: dpni_close(dflt_mc_io, MC_CMD_NO_FLAGS, dflt_dpni->dpni_handle); err_dpni_setup: +err_dpamc_bind: + dpmac_destroy(dflt_mc_io, MC_CMD_NO_FLAGS, priv->dpmac_handle); err_dpmac_setup: return err; } @@ -506,6 +556,9 @@ static void ldpaa_eth_stop(struct eth_device *net_dev) { struct ldpaa_eth_priv *priv = (struct ldpaa_eth_priv *)net_dev->priv; int err = 0; +#ifdef CONFIG_PHYLIB + struct mii_dev *bus = wriop_get_mdio(priv->dpmac_id); +#endif if ((net_dev->state == ETH_STATE_PASSIVE) || (net_dev->state == ETH_STATE_INIT)) @@ -531,7 +584,10 @@ static void ldpaa_eth_stop(struct eth_device *net_dev) printf("dpni_disable() failed\n"); #ifdef CONFIG_PHYLIB - phy_shutdown(priv->phydev); + if (priv->phydev && bus != NULL) + phy_shutdown(priv->phydev); + else + free(priv->phydev); #endif ldpaa_dpbp_free(); @@ -914,15 +970,12 @@ static int ldpaa_eth_netdev_init(struct eth_device *net_dev, net_dev->halt = ldpaa_eth_stop; net_dev->send = ldpaa_eth_tx; net_dev->recv = ldpaa_eth_pull_dequeue_rx; -/* - TODO: PHY MDIO information - priv->bus = info->bus; - priv->phyaddr = info->phy_addr; - priv->enet_if = info->enet_if; -*/ - if (init_phy(net_dev)) - return 0; +#ifdef CONFIG_PHYLIB + err = init_phy(net_dev); + if (err < 0) + return err; +#endif err = eth_register(net_dev); if (err < 0) { diff --git a/drivers/net/mvpp2.c b/drivers/net/mvpp2.c new file mode 100644 index 0000000..900a04c --- /dev/null +++ b/drivers/net/mvpp2.c @@ -0,0 +1,4190 @@ +/* + * Driver for Marvell PPv2 network controller for Armada 375 SoC. + * + * Copyright (C) 2014 Marvell + * + * Marcin Wojtas <mw@semihalf.com> + * + * U-Boot version: + * Copyright (C) 2016 Stefan Roese <sr@denx.de> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <net.h> +#include <netdev.h> +#include <config.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <phy.h> +#include <miiphy.h> +#include <watchdog.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> +#include <linux/compat.h> +#include <linux/mbus.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Some linux -> U-Boot compatibility stuff */ +#define netdev_err(dev, fmt, args...) \ + printf(fmt, ##args) +#define netdev_warn(dev, fmt, args...) \ + printf(fmt, ##args) +#define netdev_info(dev, fmt, args...) \ + printf(fmt, ##args) +#define netdev_dbg(dev, fmt, args...) \ + printf(fmt, ##args) + +#define ETH_ALEN 6 /* Octets in one ethernet addr */ + +#define __verify_pcpu_ptr(ptr) \ +do { \ + const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL; \ + (void)__vpp_verify; \ +} while (0) + +#define VERIFY_PERCPU_PTR(__p) \ +({ \ + __verify_pcpu_ptr(__p); \ + (typeof(*(__p)) __kernel __force *)(__p); \ +}) + +#define per_cpu_ptr(ptr, cpu) ({ (void)(cpu); VERIFY_PERCPU_PTR(ptr); }) +#define smp_processor_id() 0 +#define num_present_cpus() 1 +#define for_each_present_cpu(cpu) \ + for ((cpu) = 0; (cpu) < 1; (cpu)++) + +#define NET_SKB_PAD max(32, MVPP2_CPU_D_CACHE_LINE_SIZE) + +#define CONFIG_NR_CPUS 1 +#define ETH_HLEN ETHER_HDR_SIZE /* Total octets in header */ + +/* 2(HW hdr) 14(MAC hdr) 4(CRC) 32(extra for cache prefetch) */ +#define WRAP (2 + ETH_HLEN + 4 + 32) +#define MTU 1500 +#define RX_BUFFER_SIZE (ALIGN(MTU + WRAP, ARCH_DMA_MINALIGN)) + +#define MVPP2_SMI_TIMEOUT 10000 + +/* RX Fifo Registers */ +#define MVPP2_RX_DATA_FIFO_SIZE_REG(port) (0x00 + 4 * (port)) +#define MVPP2_RX_ATTR_FIFO_SIZE_REG(port) (0x20 + 4 * (port)) +#define MVPP2_RX_MIN_PKT_SIZE_REG 0x60 +#define MVPP2_RX_FIFO_INIT_REG 0x64 + +/* RX DMA Top Registers */ +#define MVPP2_RX_CTRL_REG(port) (0x140 + 4 * (port)) +#define MVPP2_RX_LOW_LATENCY_PKT_SIZE(s) (((s) & 0xfff) << 16) +#define MVPP2_RX_USE_PSEUDO_FOR_CSUM_MASK BIT(31) +#define MVPP2_POOL_BUF_SIZE_REG(pool) (0x180 + 4 * (pool)) +#define MVPP2_POOL_BUF_SIZE_OFFSET 5 +#define MVPP2_RXQ_CONFIG_REG(rxq) (0x800 + 4 * (rxq)) +#define MVPP2_SNOOP_PKT_SIZE_MASK 0x1ff +#define MVPP2_SNOOP_BUF_HDR_MASK BIT(9) +#define MVPP2_RXQ_POOL_SHORT_OFFS 20 +#define MVPP2_RXQ_POOL_SHORT_MASK 0x700000 +#define MVPP2_RXQ_POOL_LONG_OFFS 24 +#define MVPP2_RXQ_POOL_LONG_MASK 0x7000000 +#define MVPP2_RXQ_PACKET_OFFSET_OFFS 28 +#define MVPP2_RXQ_PACKET_OFFSET_MASK 0x70000000 +#define MVPP2_RXQ_DISABLE_MASK BIT(31) + +/* Parser Registers */ +#define MVPP2_PRS_INIT_LOOKUP_REG 0x1000 +#define MVPP2_PRS_PORT_LU_MAX 0xf +#define MVPP2_PRS_PORT_LU_MASK(port) (0xff << ((port) * 4)) +#define MVPP2_PRS_PORT_LU_VAL(port, val) ((val) << ((port) * 4)) +#define MVPP2_PRS_INIT_OFFS_REG(port) (0x1004 + ((port) & 4)) +#define MVPP2_PRS_INIT_OFF_MASK(port) (0x3f << (((port) % 4) * 8)) +#define MVPP2_PRS_INIT_OFF_VAL(port, val) ((val) << (((port) % 4) * 8)) +#define MVPP2_PRS_MAX_LOOP_REG(port) (0x100c + ((port) & 4)) +#define MVPP2_PRS_MAX_LOOP_MASK(port) (0xff << (((port) % 4) * 8)) +#define MVPP2_PRS_MAX_LOOP_VAL(port, val) ((val) << (((port) % 4) * 8)) +#define MVPP2_PRS_TCAM_IDX_REG 0x1100 +#define MVPP2_PRS_TCAM_DATA_REG(idx) (0x1104 + (idx) * 4) +#define MVPP2_PRS_TCAM_INV_MASK BIT(31) +#define MVPP2_PRS_SRAM_IDX_REG 0x1200 +#define MVPP2_PRS_SRAM_DATA_REG(idx) (0x1204 + (idx) * 4) +#define MVPP2_PRS_TCAM_CTRL_REG 0x1230 +#define MVPP2_PRS_TCAM_EN_MASK BIT(0) + +/* Classifier Registers */ +#define MVPP2_CLS_MODE_REG 0x1800 +#define MVPP2_CLS_MODE_ACTIVE_MASK BIT(0) +#define MVPP2_CLS_PORT_WAY_REG 0x1810 +#define MVPP2_CLS_PORT_WAY_MASK(port) (1 << (port)) +#define MVPP2_CLS_LKP_INDEX_REG 0x1814 +#define MVPP2_CLS_LKP_INDEX_WAY_OFFS 6 +#define MVPP2_CLS_LKP_TBL_REG 0x1818 +#define MVPP2_CLS_LKP_TBL_RXQ_MASK 0xff +#define MVPP2_CLS_LKP_TBL_LOOKUP_EN_MASK BIT(25) +#define MVPP2_CLS_FLOW_INDEX_REG 0x1820 +#define MVPP2_CLS_FLOW_TBL0_REG 0x1824 +#define MVPP2_CLS_FLOW_TBL1_REG 0x1828 +#define MVPP2_CLS_FLOW_TBL2_REG 0x182c +#define MVPP2_CLS_OVERSIZE_RXQ_LOW_REG(port) (0x1980 + ((port) * 4)) +#define MVPP2_CLS_OVERSIZE_RXQ_LOW_BITS 3 +#define MVPP2_CLS_OVERSIZE_RXQ_LOW_MASK 0x7 +#define MVPP2_CLS_SWFWD_P2HQ_REG(port) (0x19b0 + ((port) * 4)) +#define MVPP2_CLS_SWFWD_PCTRL_REG 0x19d0 +#define MVPP2_CLS_SWFWD_PCTRL_MASK(port) (1 << (port)) + +/* Descriptor Manager Top Registers */ +#define MVPP2_RXQ_NUM_REG 0x2040 +#define MVPP2_RXQ_DESC_ADDR_REG 0x2044 +#define MVPP2_RXQ_DESC_SIZE_REG 0x2048 +#define MVPP2_RXQ_DESC_SIZE_MASK 0x3ff0 +#define MVPP2_RXQ_STATUS_UPDATE_REG(rxq) (0x3000 + 4 * (rxq)) +#define MVPP2_RXQ_NUM_PROCESSED_OFFSET 0 +#define MVPP2_RXQ_NUM_NEW_OFFSET 16 +#define MVPP2_RXQ_STATUS_REG(rxq) (0x3400 + 4 * (rxq)) +#define MVPP2_RXQ_OCCUPIED_MASK 0x3fff +#define MVPP2_RXQ_NON_OCCUPIED_OFFSET 16 +#define MVPP2_RXQ_NON_OCCUPIED_MASK 0x3fff0000 +#define MVPP2_RXQ_THRESH_REG 0x204c +#define MVPP2_OCCUPIED_THRESH_OFFSET 0 +#define MVPP2_OCCUPIED_THRESH_MASK 0x3fff +#define MVPP2_RXQ_INDEX_REG 0x2050 +#define MVPP2_TXQ_NUM_REG 0x2080 +#define MVPP2_TXQ_DESC_ADDR_REG 0x2084 +#define MVPP2_TXQ_DESC_SIZE_REG 0x2088 +#define MVPP2_TXQ_DESC_SIZE_MASK 0x3ff0 +#define MVPP2_AGGR_TXQ_UPDATE_REG 0x2090 +#define MVPP2_TXQ_THRESH_REG 0x2094 +#define MVPP2_TRANSMITTED_THRESH_OFFSET 16 +#define MVPP2_TRANSMITTED_THRESH_MASK 0x3fff0000 +#define MVPP2_TXQ_INDEX_REG 0x2098 +#define MVPP2_TXQ_PREF_BUF_REG 0x209c +#define MVPP2_PREF_BUF_PTR(desc) ((desc) & 0xfff) +#define MVPP2_PREF_BUF_SIZE_4 (BIT(12) | BIT(13)) +#define MVPP2_PREF_BUF_SIZE_16 (BIT(12) | BIT(14)) +#define MVPP2_PREF_BUF_THRESH(val) ((val) << 17) +#define MVPP2_TXQ_DRAIN_EN_MASK BIT(31) +#define MVPP2_TXQ_PENDING_REG 0x20a0 +#define MVPP2_TXQ_PENDING_MASK 0x3fff +#define MVPP2_TXQ_INT_STATUS_REG 0x20a4 +#define MVPP2_TXQ_SENT_REG(txq) (0x3c00 + 4 * (txq)) +#define MVPP2_TRANSMITTED_COUNT_OFFSET 16 +#define MVPP2_TRANSMITTED_COUNT_MASK 0x3fff0000 +#define MVPP2_TXQ_RSVD_REQ_REG 0x20b0 +#define MVPP2_TXQ_RSVD_REQ_Q_OFFSET 16 +#define MVPP2_TXQ_RSVD_RSLT_REG 0x20b4 +#define MVPP2_TXQ_RSVD_RSLT_MASK 0x3fff +#define MVPP2_TXQ_RSVD_CLR_REG 0x20b8 +#define MVPP2_TXQ_RSVD_CLR_OFFSET 16 +#define MVPP2_AGGR_TXQ_DESC_ADDR_REG(cpu) (0x2100 + 4 * (cpu)) +#define MVPP2_AGGR_TXQ_DESC_SIZE_REG(cpu) (0x2140 + 4 * (cpu)) +#define MVPP2_AGGR_TXQ_DESC_SIZE_MASK 0x3ff0 +#define MVPP2_AGGR_TXQ_STATUS_REG(cpu) (0x2180 + 4 * (cpu)) +#define MVPP2_AGGR_TXQ_PENDING_MASK 0x3fff +#define MVPP2_AGGR_TXQ_INDEX_REG(cpu) (0x21c0 + 4 * (cpu)) + +/* MBUS bridge registers */ +#define MVPP2_WIN_BASE(w) (0x4000 + ((w) << 2)) +#define MVPP2_WIN_SIZE(w) (0x4020 + ((w) << 2)) +#define MVPP2_WIN_REMAP(w) (0x4040 + ((w) << 2)) +#define MVPP2_BASE_ADDR_ENABLE 0x4060 + +/* Interrupt Cause and Mask registers */ +#define MVPP2_ISR_RX_THRESHOLD_REG(rxq) (0x5200 + 4 * (rxq)) +#define MVPP2_ISR_RXQ_GROUP_REG(rxq) (0x5400 + 4 * (rxq)) +#define MVPP2_ISR_ENABLE_REG(port) (0x5420 + 4 * (port)) +#define MVPP2_ISR_ENABLE_INTERRUPT(mask) ((mask) & 0xffff) +#define MVPP2_ISR_DISABLE_INTERRUPT(mask) (((mask) << 16) & 0xffff0000) +#define MVPP2_ISR_RX_TX_CAUSE_REG(port) (0x5480 + 4 * (port)) +#define MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK 0xffff +#define MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK 0xff0000 +#define MVPP2_CAUSE_RX_FIFO_OVERRUN_MASK BIT(24) +#define MVPP2_CAUSE_FCS_ERR_MASK BIT(25) +#define MVPP2_CAUSE_TX_FIFO_UNDERRUN_MASK BIT(26) +#define MVPP2_CAUSE_TX_EXCEPTION_SUM_MASK BIT(29) +#define MVPP2_CAUSE_RX_EXCEPTION_SUM_MASK BIT(30) +#define MVPP2_CAUSE_MISC_SUM_MASK BIT(31) +#define MVPP2_ISR_RX_TX_MASK_REG(port) (0x54a0 + 4 * (port)) +#define MVPP2_ISR_PON_RX_TX_MASK_REG 0x54bc +#define MVPP2_PON_CAUSE_RXQ_OCCUP_DESC_ALL_MASK 0xffff +#define MVPP2_PON_CAUSE_TXP_OCCUP_DESC_ALL_MASK 0x3fc00000 +#define MVPP2_PON_CAUSE_MISC_SUM_MASK BIT(31) +#define MVPP2_ISR_MISC_CAUSE_REG 0x55b0 + +/* Buffer Manager registers */ +#define MVPP2_BM_POOL_BASE_REG(pool) (0x6000 + ((pool) * 4)) +#define MVPP2_BM_POOL_BASE_ADDR_MASK 0xfffff80 +#define MVPP2_BM_POOL_SIZE_REG(pool) (0x6040 + ((pool) * 4)) +#define MVPP2_BM_POOL_SIZE_MASK 0xfff0 +#define MVPP2_BM_POOL_READ_PTR_REG(pool) (0x6080 + ((pool) * 4)) +#define MVPP2_BM_POOL_GET_READ_PTR_MASK 0xfff0 +#define MVPP2_BM_POOL_PTRS_NUM_REG(pool) (0x60c0 + ((pool) * 4)) +#define MVPP2_BM_POOL_PTRS_NUM_MASK 0xfff0 +#define MVPP2_BM_BPPI_READ_PTR_REG(pool) (0x6100 + ((pool) * 4)) +#define MVPP2_BM_BPPI_PTRS_NUM_REG(pool) (0x6140 + ((pool) * 4)) +#define MVPP2_BM_BPPI_PTR_NUM_MASK 0x7ff +#define MVPP2_BM_BPPI_PREFETCH_FULL_MASK BIT(16) +#define MVPP2_BM_POOL_CTRL_REG(pool) (0x6200 + ((pool) * 4)) +#define MVPP2_BM_START_MASK BIT(0) +#define MVPP2_BM_STOP_MASK BIT(1) +#define MVPP2_BM_STATE_MASK BIT(4) +#define MVPP2_BM_LOW_THRESH_OFFS 8 +#define MVPP2_BM_LOW_THRESH_MASK 0x7f00 +#define MVPP2_BM_LOW_THRESH_VALUE(val) ((val) << \ + MVPP2_BM_LOW_THRESH_OFFS) +#define MVPP2_BM_HIGH_THRESH_OFFS 16 +#define MVPP2_BM_HIGH_THRESH_MASK 0x7f0000 +#define MVPP2_BM_HIGH_THRESH_VALUE(val) ((val) << \ + MVPP2_BM_HIGH_THRESH_OFFS) +#define MVPP2_BM_INTR_CAUSE_REG(pool) (0x6240 + ((pool) * 4)) +#define MVPP2_BM_RELEASED_DELAY_MASK BIT(0) +#define MVPP2_BM_ALLOC_FAILED_MASK BIT(1) +#define MVPP2_BM_BPPE_EMPTY_MASK BIT(2) +#define MVPP2_BM_BPPE_FULL_MASK BIT(3) +#define MVPP2_BM_AVAILABLE_BP_LOW_MASK BIT(4) +#define MVPP2_BM_INTR_MASK_REG(pool) (0x6280 + ((pool) * 4)) +#define MVPP2_BM_PHY_ALLOC_REG(pool) (0x6400 + ((pool) * 4)) +#define MVPP2_BM_PHY_ALLOC_GRNTD_MASK BIT(0) +#define MVPP2_BM_VIRT_ALLOC_REG 0x6440 +#define MVPP2_BM_PHY_RLS_REG(pool) (0x6480 + ((pool) * 4)) +#define MVPP2_BM_PHY_RLS_MC_BUFF_MASK BIT(0) +#define MVPP2_BM_PHY_RLS_PRIO_EN_MASK BIT(1) +#define MVPP2_BM_PHY_RLS_GRNTD_MASK BIT(2) +#define MVPP2_BM_VIRT_RLS_REG 0x64c0 +#define MVPP2_BM_MC_RLS_REG 0x64c4 +#define MVPP2_BM_MC_ID_MASK 0xfff +#define MVPP2_BM_FORCE_RELEASE_MASK BIT(12) + +/* TX Scheduler registers */ +#define MVPP2_TXP_SCHED_PORT_INDEX_REG 0x8000 +#define MVPP2_TXP_SCHED_Q_CMD_REG 0x8004 +#define MVPP2_TXP_SCHED_ENQ_MASK 0xff +#define MVPP2_TXP_SCHED_DISQ_OFFSET 8 +#define MVPP2_TXP_SCHED_CMD_1_REG 0x8010 +#define MVPP2_TXP_SCHED_PERIOD_REG 0x8018 +#define MVPP2_TXP_SCHED_MTU_REG 0x801c +#define MVPP2_TXP_MTU_MAX 0x7FFFF +#define MVPP2_TXP_SCHED_REFILL_REG 0x8020 +#define MVPP2_TXP_REFILL_TOKENS_ALL_MASK 0x7ffff +#define MVPP2_TXP_REFILL_PERIOD_ALL_MASK 0x3ff00000 +#define MVPP2_TXP_REFILL_PERIOD_MASK(v) ((v) << 20) +#define MVPP2_TXP_SCHED_TOKEN_SIZE_REG 0x8024 +#define MVPP2_TXP_TOKEN_SIZE_MAX 0xffffffff +#define MVPP2_TXQ_SCHED_REFILL_REG(q) (0x8040 + ((q) << 2)) +#define MVPP2_TXQ_REFILL_TOKENS_ALL_MASK 0x7ffff +#define MVPP2_TXQ_REFILL_PERIOD_ALL_MASK 0x3ff00000 +#define MVPP2_TXQ_REFILL_PERIOD_MASK(v) ((v) << 20) +#define MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(q) (0x8060 + ((q) << 2)) +#define MVPP2_TXQ_TOKEN_SIZE_MAX 0x7fffffff +#define MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(q) (0x8080 + ((q) << 2)) +#define MVPP2_TXQ_TOKEN_CNTR_MAX 0xffffffff + +/* TX general registers */ +#define MVPP2_TX_SNOOP_REG 0x8800 +#define MVPP2_TX_PORT_FLUSH_REG 0x8810 +#define MVPP2_TX_PORT_FLUSH_MASK(port) (1 << (port)) + +/* LMS registers */ +#define MVPP2_SRC_ADDR_MIDDLE 0x24 +#define MVPP2_SRC_ADDR_HIGH 0x28 +#define MVPP2_PHY_AN_CFG0_REG 0x34 +#define MVPP2_PHY_AN_STOP_SMI0_MASK BIT(7) +#define MVPP2_MIB_COUNTERS_BASE(port) (0x1000 + ((port) >> 1) * \ + 0x400 + (port) * 0x400) +#define MVPP2_MIB_LATE_COLLISION 0x7c +#define MVPP2_ISR_SUM_MASK_REG 0x220c +#define MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG 0x305c +#define MVPP2_EXT_GLOBAL_CTRL_DEFAULT 0x27 + +/* Per-port registers */ +#define MVPP2_GMAC_CTRL_0_REG 0x0 +#define MVPP2_GMAC_PORT_EN_MASK BIT(0) +#define MVPP2_GMAC_MAX_RX_SIZE_OFFS 2 +#define MVPP2_GMAC_MAX_RX_SIZE_MASK 0x7ffc +#define MVPP2_GMAC_MIB_CNTR_EN_MASK BIT(15) +#define MVPP2_GMAC_CTRL_1_REG 0x4 +#define MVPP2_GMAC_PERIODIC_XON_EN_MASK BIT(1) +#define MVPP2_GMAC_GMII_LB_EN_MASK BIT(5) +#define MVPP2_GMAC_PCS_LB_EN_BIT 6 +#define MVPP2_GMAC_PCS_LB_EN_MASK BIT(6) +#define MVPP2_GMAC_SA_LOW_OFFS 7 +#define MVPP2_GMAC_CTRL_2_REG 0x8 +#define MVPP2_GMAC_INBAND_AN_MASK BIT(0) +#define MVPP2_GMAC_PCS_ENABLE_MASK BIT(3) +#define MVPP2_GMAC_PORT_RGMII_MASK BIT(4) +#define MVPP2_GMAC_PORT_RESET_MASK BIT(6) +#define MVPP2_GMAC_AUTONEG_CONFIG 0xc +#define MVPP2_GMAC_FORCE_LINK_DOWN BIT(0) +#define MVPP2_GMAC_FORCE_LINK_PASS BIT(1) +#define MVPP2_GMAC_CONFIG_MII_SPEED BIT(5) +#define MVPP2_GMAC_CONFIG_GMII_SPEED BIT(6) +#define MVPP2_GMAC_AN_SPEED_EN BIT(7) +#define MVPP2_GMAC_FC_ADV_EN BIT(9) +#define MVPP2_GMAC_CONFIG_FULL_DUPLEX BIT(12) +#define MVPP2_GMAC_AN_DUPLEX_EN BIT(13) +#define MVPP2_GMAC_PORT_FIFO_CFG_1_REG 0x1c +#define MVPP2_GMAC_TX_FIFO_MIN_TH_OFFS 6 +#define MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK 0x1fc0 +#define MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(v) (((v) << 6) & \ + MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK) + +#define MVPP2_CAUSE_TXQ_SENT_DESC_ALL_MASK 0xff + +/* Descriptor ring Macros */ +#define MVPP2_QUEUE_NEXT_DESC(q, index) \ + (((index) < (q)->last_desc) ? ((index) + 1) : 0) + +/* SMI: 0xc0054 -> offset 0x54 to lms_base */ +#define MVPP2_SMI 0x0054 +#define MVPP2_PHY_REG_MASK 0x1f +/* SMI register fields */ +#define MVPP2_SMI_DATA_OFFS 0 /* Data */ +#define MVPP2_SMI_DATA_MASK (0xffff << MVPP2_SMI_DATA_OFFS) +#define MVPP2_SMI_DEV_ADDR_OFFS 16 /* PHY device address */ +#define MVPP2_SMI_REG_ADDR_OFFS 21 /* PHY device reg addr*/ +#define MVPP2_SMI_OPCODE_OFFS 26 /* Write/Read opcode */ +#define MVPP2_SMI_OPCODE_READ (1 << MVPP2_SMI_OPCODE_OFFS) +#define MVPP2_SMI_READ_VALID (1 << 27) /* Read Valid */ +#define MVPP2_SMI_BUSY (1 << 28) /* Busy */ + +#define MVPP2_PHY_ADDR_MASK 0x1f +#define MVPP2_PHY_REG_MASK 0x1f + +/* Various constants */ + +/* Coalescing */ +#define MVPP2_TXDONE_COAL_PKTS_THRESH 15 +#define MVPP2_TXDONE_HRTIMER_PERIOD_NS 1000000UL +#define MVPP2_RX_COAL_PKTS 32 +#define MVPP2_RX_COAL_USEC 100 + +/* The two bytes Marvell header. Either contains a special value used + * by Marvell switches when a specific hardware mode is enabled (not + * supported by this driver) or is filled automatically by zeroes on + * the RX side. Those two bytes being at the front of the Ethernet + * header, they allow to have the IP header aligned on a 4 bytes + * boundary automatically: the hardware skips those two bytes on its + * own. + */ +#define MVPP2_MH_SIZE 2 +#define MVPP2_ETH_TYPE_LEN 2 +#define MVPP2_PPPOE_HDR_SIZE 8 +#define MVPP2_VLAN_TAG_LEN 4 + +/* Lbtd 802.3 type */ +#define MVPP2_IP_LBDT_TYPE 0xfffa + +#define MVPP2_CPU_D_CACHE_LINE_SIZE 32 +#define MVPP2_TX_CSUM_MAX_SIZE 9800 + +/* Timeout constants */ +#define MVPP2_TX_DISABLE_TIMEOUT_MSEC 1000 +#define MVPP2_TX_PENDING_TIMEOUT_MSEC 1000 + +#define MVPP2_TX_MTU_MAX 0x7ffff + +/* Maximum number of T-CONTs of PON port */ +#define MVPP2_MAX_TCONT 16 + +/* Maximum number of supported ports */ +#define MVPP2_MAX_PORTS 4 + +/* Maximum number of TXQs used by single port */ +#define MVPP2_MAX_TXQ 8 + +/* Maximum number of RXQs used by single port */ +#define MVPP2_MAX_RXQ 8 + +/* Default number of TXQs in use */ +#define MVPP2_DEFAULT_TXQ 1 + +/* Dfault number of RXQs in use */ +#define MVPP2_DEFAULT_RXQ 1 +#define CONFIG_MV_ETH_RXQ 8 /* increment by 8 */ + +/* Total number of RXQs available to all ports */ +#define MVPP2_RXQ_TOTAL_NUM (MVPP2_MAX_PORTS * MVPP2_MAX_RXQ) + +/* Max number of Rx descriptors */ +#define MVPP2_MAX_RXD 16 + +/* Max number of Tx descriptors */ +#define MVPP2_MAX_TXD 16 + +/* Amount of Tx descriptors that can be reserved at once by CPU */ +#define MVPP2_CPU_DESC_CHUNK 64 + +/* Max number of Tx descriptors in each aggregated queue */ +#define MVPP2_AGGR_TXQ_SIZE 256 + +/* Descriptor aligned size */ +#define MVPP2_DESC_ALIGNED_SIZE 32 + +/* Descriptor alignment mask */ +#define MVPP2_TX_DESC_ALIGN (MVPP2_DESC_ALIGNED_SIZE - 1) + +/* RX FIFO constants */ +#define MVPP2_RX_FIFO_PORT_DATA_SIZE 0x2000 +#define MVPP2_RX_FIFO_PORT_ATTR_SIZE 0x80 +#define MVPP2_RX_FIFO_PORT_MIN_PKT 0x80 + +/* RX buffer constants */ +#define MVPP2_SKB_SHINFO_SIZE \ + 0 + +#define MVPP2_RX_PKT_SIZE(mtu) \ + ALIGN((mtu) + MVPP2_MH_SIZE + MVPP2_VLAN_TAG_LEN + \ + ETH_HLEN + ETH_FCS_LEN, MVPP2_CPU_D_CACHE_LINE_SIZE) + +#define MVPP2_RX_BUF_SIZE(pkt_size) ((pkt_size) + NET_SKB_PAD) +#define MVPP2_RX_TOTAL_SIZE(buf_size) ((buf_size) + MVPP2_SKB_SHINFO_SIZE) +#define MVPP2_RX_MAX_PKT_SIZE(total_size) \ + ((total_size) - NET_SKB_PAD - MVPP2_SKB_SHINFO_SIZE) + +#define MVPP2_BIT_TO_BYTE(bit) ((bit) / 8) + +/* IPv6 max L3 address size */ +#define MVPP2_MAX_L3_ADDR_SIZE 16 + +/* Port flags */ +#define MVPP2_F_LOOPBACK BIT(0) + +/* Marvell tag types */ +enum mvpp2_tag_type { + MVPP2_TAG_TYPE_NONE = 0, + MVPP2_TAG_TYPE_MH = 1, + MVPP2_TAG_TYPE_DSA = 2, + MVPP2_TAG_TYPE_EDSA = 3, + MVPP2_TAG_TYPE_VLAN = 4, + MVPP2_TAG_TYPE_LAST = 5 +}; + +/* Parser constants */ +#define MVPP2_PRS_TCAM_SRAM_SIZE 256 +#define MVPP2_PRS_TCAM_WORDS 6 +#define MVPP2_PRS_SRAM_WORDS 4 +#define MVPP2_PRS_FLOW_ID_SIZE 64 +#define MVPP2_PRS_FLOW_ID_MASK 0x3f +#define MVPP2_PRS_TCAM_ENTRY_INVALID 1 +#define MVPP2_PRS_TCAM_DSA_TAGGED_BIT BIT(5) +#define MVPP2_PRS_IPV4_HEAD 0x40 +#define MVPP2_PRS_IPV4_HEAD_MASK 0xf0 +#define MVPP2_PRS_IPV4_MC 0xe0 +#define MVPP2_PRS_IPV4_MC_MASK 0xf0 +#define MVPP2_PRS_IPV4_BC_MASK 0xff +#define MVPP2_PRS_IPV4_IHL 0x5 +#define MVPP2_PRS_IPV4_IHL_MASK 0xf +#define MVPP2_PRS_IPV6_MC 0xff +#define MVPP2_PRS_IPV6_MC_MASK 0xff +#define MVPP2_PRS_IPV6_HOP_MASK 0xff +#define MVPP2_PRS_TCAM_PROTO_MASK 0xff +#define MVPP2_PRS_TCAM_PROTO_MASK_L 0x3f +#define MVPP2_PRS_DBL_VLANS_MAX 100 + +/* Tcam structure: + * - lookup ID - 4 bits + * - port ID - 1 byte + * - additional information - 1 byte + * - header data - 8 bytes + * The fields are represented by MVPP2_PRS_TCAM_DATA_REG(5)->(0). + */ +#define MVPP2_PRS_AI_BITS 8 +#define MVPP2_PRS_PORT_MASK 0xff +#define MVPP2_PRS_LU_MASK 0xf +#define MVPP2_PRS_TCAM_DATA_BYTE(offs) \ + (((offs) - ((offs) % 2)) * 2 + ((offs) % 2)) +#define MVPP2_PRS_TCAM_DATA_BYTE_EN(offs) \ + (((offs) * 2) - ((offs) % 2) + 2) +#define MVPP2_PRS_TCAM_AI_BYTE 16 +#define MVPP2_PRS_TCAM_PORT_BYTE 17 +#define MVPP2_PRS_TCAM_LU_BYTE 20 +#define MVPP2_PRS_TCAM_EN_OFFS(offs) ((offs) + 2) +#define MVPP2_PRS_TCAM_INV_WORD 5 +/* Tcam entries ID */ +#define MVPP2_PE_DROP_ALL 0 +#define MVPP2_PE_FIRST_FREE_TID 1 +#define MVPP2_PE_LAST_FREE_TID (MVPP2_PRS_TCAM_SRAM_SIZE - 31) +#define MVPP2_PE_IP6_EXT_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 30) +#define MVPP2_PE_MAC_MC_IP6 (MVPP2_PRS_TCAM_SRAM_SIZE - 29) +#define MVPP2_PE_IP6_ADDR_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 28) +#define MVPP2_PE_IP4_ADDR_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 27) +#define MVPP2_PE_LAST_DEFAULT_FLOW (MVPP2_PRS_TCAM_SRAM_SIZE - 26) +#define MVPP2_PE_FIRST_DEFAULT_FLOW (MVPP2_PRS_TCAM_SRAM_SIZE - 19) +#define MVPP2_PE_EDSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 18) +#define MVPP2_PE_EDSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 17) +#define MVPP2_PE_DSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 16) +#define MVPP2_PE_DSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 15) +#define MVPP2_PE_ETYPE_EDSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 14) +#define MVPP2_PE_ETYPE_EDSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 13) +#define MVPP2_PE_ETYPE_DSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 12) +#define MVPP2_PE_ETYPE_DSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 11) +#define MVPP2_PE_MH_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 10) +#define MVPP2_PE_DSA_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 9) +#define MVPP2_PE_IP6_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 8) +#define MVPP2_PE_IP4_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 7) +#define MVPP2_PE_ETH_TYPE_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 6) +#define MVPP2_PE_VLAN_DBL (MVPP2_PRS_TCAM_SRAM_SIZE - 5) +#define MVPP2_PE_VLAN_NONE (MVPP2_PRS_TCAM_SRAM_SIZE - 4) +#define MVPP2_PE_MAC_MC_ALL (MVPP2_PRS_TCAM_SRAM_SIZE - 3) +#define MVPP2_PE_MAC_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 2) +#define MVPP2_PE_MAC_NON_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 1) + +/* Sram structure + * The fields are represented by MVPP2_PRS_TCAM_DATA_REG(3)->(0). + */ +#define MVPP2_PRS_SRAM_RI_OFFS 0 +#define MVPP2_PRS_SRAM_RI_WORD 0 +#define MVPP2_PRS_SRAM_RI_CTRL_OFFS 32 +#define MVPP2_PRS_SRAM_RI_CTRL_WORD 1 +#define MVPP2_PRS_SRAM_RI_CTRL_BITS 32 +#define MVPP2_PRS_SRAM_SHIFT_OFFS 64 +#define MVPP2_PRS_SRAM_SHIFT_SIGN_BIT 72 +#define MVPP2_PRS_SRAM_UDF_OFFS 73 +#define MVPP2_PRS_SRAM_UDF_BITS 8 +#define MVPP2_PRS_SRAM_UDF_MASK 0xff +#define MVPP2_PRS_SRAM_UDF_SIGN_BIT 81 +#define MVPP2_PRS_SRAM_UDF_TYPE_OFFS 82 +#define MVPP2_PRS_SRAM_UDF_TYPE_MASK 0x7 +#define MVPP2_PRS_SRAM_UDF_TYPE_L3 1 +#define MVPP2_PRS_SRAM_UDF_TYPE_L4 4 +#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS 85 +#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_MASK 0x3 +#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD 1 +#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_IP4_ADD 2 +#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_IP6_ADD 3 +#define MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS 87 +#define MVPP2_PRS_SRAM_OP_SEL_UDF_BITS 2 +#define MVPP2_PRS_SRAM_OP_SEL_UDF_MASK 0x3 +#define MVPP2_PRS_SRAM_OP_SEL_UDF_ADD 0 +#define MVPP2_PRS_SRAM_OP_SEL_UDF_IP4_ADD 2 +#define MVPP2_PRS_SRAM_OP_SEL_UDF_IP6_ADD 3 +#define MVPP2_PRS_SRAM_OP_SEL_BASE_OFFS 89 +#define MVPP2_PRS_SRAM_AI_OFFS 90 +#define MVPP2_PRS_SRAM_AI_CTRL_OFFS 98 +#define MVPP2_PRS_SRAM_AI_CTRL_BITS 8 +#define MVPP2_PRS_SRAM_AI_MASK 0xff +#define MVPP2_PRS_SRAM_NEXT_LU_OFFS 106 +#define MVPP2_PRS_SRAM_NEXT_LU_MASK 0xf +#define MVPP2_PRS_SRAM_LU_DONE_BIT 110 +#define MVPP2_PRS_SRAM_LU_GEN_BIT 111 + +/* Sram result info bits assignment */ +#define MVPP2_PRS_RI_MAC_ME_MASK 0x1 +#define MVPP2_PRS_RI_DSA_MASK 0x2 +#define MVPP2_PRS_RI_VLAN_MASK 0xc +#define MVPP2_PRS_RI_VLAN_NONE ~(BIT(2) | BIT(3)) +#define MVPP2_PRS_RI_VLAN_SINGLE BIT(2) +#define MVPP2_PRS_RI_VLAN_DOUBLE BIT(3) +#define MVPP2_PRS_RI_VLAN_TRIPLE (BIT(2) | BIT(3)) +#define MVPP2_PRS_RI_CPU_CODE_MASK 0x70 +#define MVPP2_PRS_RI_CPU_CODE_RX_SPEC BIT(4) +#define MVPP2_PRS_RI_L2_CAST_MASK 0x600 +#define MVPP2_PRS_RI_L2_UCAST ~(BIT(9) | BIT(10)) +#define MVPP2_PRS_RI_L2_MCAST BIT(9) +#define MVPP2_PRS_RI_L2_BCAST BIT(10) +#define MVPP2_PRS_RI_PPPOE_MASK 0x800 +#define MVPP2_PRS_RI_L3_PROTO_MASK 0x7000 +#define MVPP2_PRS_RI_L3_UN ~(BIT(12) | BIT(13) | BIT(14)) +#define MVPP2_PRS_RI_L3_IP4 BIT(12) +#define MVPP2_PRS_RI_L3_IP4_OPT BIT(13) +#define MVPP2_PRS_RI_L3_IP4_OTHER (BIT(12) | BIT(13)) +#define MVPP2_PRS_RI_L3_IP6 BIT(14) +#define MVPP2_PRS_RI_L3_IP6_EXT (BIT(12) | BIT(14)) +#define MVPP2_PRS_RI_L3_ARP (BIT(13) | BIT(14)) +#define MVPP2_PRS_RI_L3_ADDR_MASK 0x18000 +#define MVPP2_PRS_RI_L3_UCAST ~(BIT(15) | BIT(16)) +#define MVPP2_PRS_RI_L3_MCAST BIT(15) +#define MVPP2_PRS_RI_L3_BCAST (BIT(15) | BIT(16)) +#define MVPP2_PRS_RI_IP_FRAG_MASK 0x20000 +#define MVPP2_PRS_RI_UDF3_MASK 0x300000 +#define MVPP2_PRS_RI_UDF3_RX_SPECIAL BIT(21) +#define MVPP2_PRS_RI_L4_PROTO_MASK 0x1c00000 +#define MVPP2_PRS_RI_L4_TCP BIT(22) +#define MVPP2_PRS_RI_L4_UDP BIT(23) +#define MVPP2_PRS_RI_L4_OTHER (BIT(22) | BIT(23)) +#define MVPP2_PRS_RI_UDF7_MASK 0x60000000 +#define MVPP2_PRS_RI_UDF7_IP6_LITE BIT(29) +#define MVPP2_PRS_RI_DROP_MASK 0x80000000 + +/* Sram additional info bits assignment */ +#define MVPP2_PRS_IPV4_DIP_AI_BIT BIT(0) +#define MVPP2_PRS_IPV6_NO_EXT_AI_BIT BIT(0) +#define MVPP2_PRS_IPV6_EXT_AI_BIT BIT(1) +#define MVPP2_PRS_IPV6_EXT_AH_AI_BIT BIT(2) +#define MVPP2_PRS_IPV6_EXT_AH_LEN_AI_BIT BIT(3) +#define MVPP2_PRS_IPV6_EXT_AH_L4_AI_BIT BIT(4) +#define MVPP2_PRS_SINGLE_VLAN_AI 0 +#define MVPP2_PRS_DBL_VLAN_AI_BIT BIT(7) + +/* DSA/EDSA type */ +#define MVPP2_PRS_TAGGED true +#define MVPP2_PRS_UNTAGGED false +#define MVPP2_PRS_EDSA true +#define MVPP2_PRS_DSA false + +/* MAC entries, shadow udf */ +enum mvpp2_prs_udf { + MVPP2_PRS_UDF_MAC_DEF, + MVPP2_PRS_UDF_MAC_RANGE, + MVPP2_PRS_UDF_L2_DEF, + MVPP2_PRS_UDF_L2_DEF_COPY, + MVPP2_PRS_UDF_L2_USER, +}; + +/* Lookup ID */ +enum mvpp2_prs_lookup { + MVPP2_PRS_LU_MH, + MVPP2_PRS_LU_MAC, + MVPP2_PRS_LU_DSA, + MVPP2_PRS_LU_VLAN, + MVPP2_PRS_LU_L2, + MVPP2_PRS_LU_PPPOE, + MVPP2_PRS_LU_IP4, + MVPP2_PRS_LU_IP6, + MVPP2_PRS_LU_FLOWS, + MVPP2_PRS_LU_LAST, +}; + +/* L3 cast enum */ +enum mvpp2_prs_l3_cast { + MVPP2_PRS_L3_UNI_CAST, + MVPP2_PRS_L3_MULTI_CAST, + MVPP2_PRS_L3_BROAD_CAST +}; + +/* Classifier constants */ +#define MVPP2_CLS_FLOWS_TBL_SIZE 512 +#define MVPP2_CLS_FLOWS_TBL_DATA_WORDS 3 +#define MVPP2_CLS_LKP_TBL_SIZE 64 + +/* BM constants */ +#define MVPP2_BM_POOLS_NUM 1 +#define MVPP2_BM_LONG_BUF_NUM 16 +#define MVPP2_BM_SHORT_BUF_NUM 16 +#define MVPP2_BM_POOL_SIZE_MAX (16*1024 - MVPP2_BM_POOL_PTR_ALIGN/4) +#define MVPP2_BM_POOL_PTR_ALIGN 128 +#define MVPP2_BM_SWF_LONG_POOL(port) 0 + +/* BM cookie (32 bits) definition */ +#define MVPP2_BM_COOKIE_POOL_OFFS 8 +#define MVPP2_BM_COOKIE_CPU_OFFS 24 + +/* BM short pool packet size + * These value assure that for SWF the total number + * of bytes allocated for each buffer will be 512 + */ +#define MVPP2_BM_SHORT_PKT_SIZE MVPP2_RX_MAX_PKT_SIZE(512) + +enum mvpp2_bm_type { + MVPP2_BM_FREE, + MVPP2_BM_SWF_LONG, + MVPP2_BM_SWF_SHORT +}; + +/* Definitions */ + +/* Shared Packet Processor resources */ +struct mvpp2 { + /* Shared registers' base addresses */ + void __iomem *base; + void __iomem *lms_base; + + /* List of pointers to port structures */ + struct mvpp2_port **port_list; + + /* Aggregated TXQs */ + struct mvpp2_tx_queue *aggr_txqs; + + /* BM pools */ + struct mvpp2_bm_pool *bm_pools; + + /* PRS shadow table */ + struct mvpp2_prs_shadow *prs_shadow; + /* PRS auxiliary table for double vlan entries control */ + bool *prs_double_vlans; + + /* Tclk value */ + u32 tclk; + + struct mii_dev *bus; +}; + +struct mvpp2_pcpu_stats { + u64 rx_packets; + u64 rx_bytes; + u64 tx_packets; + u64 tx_bytes; +}; + +struct mvpp2_port { + u8 id; + + int irq; + + struct mvpp2 *priv; + + /* Per-port registers' base address */ + void __iomem *base; + + struct mvpp2_rx_queue **rxqs; + struct mvpp2_tx_queue **txqs; + + int pkt_size; + + u32 pending_cause_rx; + + /* Per-CPU port control */ + struct mvpp2_port_pcpu __percpu *pcpu; + + /* Flags */ + unsigned long flags; + + u16 tx_ring_size; + u16 rx_ring_size; + struct mvpp2_pcpu_stats __percpu *stats; + + struct phy_device *phy_dev; + phy_interface_t phy_interface; + int phy_node; + int phyaddr; + int init; + unsigned int link; + unsigned int duplex; + unsigned int speed; + + struct mvpp2_bm_pool *pool_long; + struct mvpp2_bm_pool *pool_short; + + /* Index of first port's physical RXQ */ + u8 first_rxq; + + u8 dev_addr[ETH_ALEN]; +}; + +/* The mvpp2_tx_desc and mvpp2_rx_desc structures describe the + * layout of the transmit and reception DMA descriptors, and their + * layout is therefore defined by the hardware design + */ + +#define MVPP2_TXD_L3_OFF_SHIFT 0 +#define MVPP2_TXD_IP_HLEN_SHIFT 8 +#define MVPP2_TXD_L4_CSUM_FRAG BIT(13) +#define MVPP2_TXD_L4_CSUM_NOT BIT(14) +#define MVPP2_TXD_IP_CSUM_DISABLE BIT(15) +#define MVPP2_TXD_PADDING_DISABLE BIT(23) +#define MVPP2_TXD_L4_UDP BIT(24) +#define MVPP2_TXD_L3_IP6 BIT(26) +#define MVPP2_TXD_L_DESC BIT(28) +#define MVPP2_TXD_F_DESC BIT(29) + +#define MVPP2_RXD_ERR_SUMMARY BIT(15) +#define MVPP2_RXD_ERR_CODE_MASK (BIT(13) | BIT(14)) +#define MVPP2_RXD_ERR_CRC 0x0 +#define MVPP2_RXD_ERR_OVERRUN BIT(13) +#define MVPP2_RXD_ERR_RESOURCE (BIT(13) | BIT(14)) +#define MVPP2_RXD_BM_POOL_ID_OFFS 16 +#define MVPP2_RXD_BM_POOL_ID_MASK (BIT(16) | BIT(17) | BIT(18)) +#define MVPP2_RXD_HWF_SYNC BIT(21) +#define MVPP2_RXD_L4_CSUM_OK BIT(22) +#define MVPP2_RXD_IP4_HEADER_ERR BIT(24) +#define MVPP2_RXD_L4_TCP BIT(25) +#define MVPP2_RXD_L4_UDP BIT(26) +#define MVPP2_RXD_L3_IP4 BIT(28) +#define MVPP2_RXD_L3_IP6 BIT(30) +#define MVPP2_RXD_BUF_HDR BIT(31) + +struct mvpp2_tx_desc { + u32 command; /* Options used by HW for packet transmitting.*/ + u8 packet_offset; /* the offset from the buffer beginning */ + u8 phys_txq; /* destination queue ID */ + u16 data_size; /* data size of transmitted packet in bytes */ + u32 buf_phys_addr; /* physical addr of transmitted buffer */ + u32 buf_cookie; /* cookie for access to TX buffer in tx path */ + u32 reserved1[3]; /* hw_cmd (for future use, BM, PON, PNC) */ + u32 reserved2; /* reserved (for future use) */ +}; + +struct mvpp2_rx_desc { + u32 status; /* info about received packet */ + u16 reserved1; /* parser_info (for future use, PnC) */ + u16 data_size; /* size of received packet in bytes */ + u32 buf_phys_addr; /* physical address of the buffer */ + u32 buf_cookie; /* cookie for access to RX buffer in rx path */ + u16 reserved2; /* gem_port_id (for future use, PON) */ + u16 reserved3; /* csum_l4 (for future use, PnC) */ + u8 reserved4; /* bm_qset (for future use, BM) */ + u8 reserved5; + u16 reserved6; /* classify_info (for future use, PnC) */ + u32 reserved7; /* flow_id (for future use, PnC) */ + u32 reserved8; +}; + +/* Per-CPU Tx queue control */ +struct mvpp2_txq_pcpu { + int cpu; + + /* Number of Tx DMA descriptors in the descriptor ring */ + int size; + + /* Number of currently used Tx DMA descriptor in the + * descriptor ring + */ + int count; + + /* Number of Tx DMA descriptors reserved for each CPU */ + int reserved_num; + + /* Index of last TX DMA descriptor that was inserted */ + int txq_put_index; + + /* Index of the TX DMA descriptor to be cleaned up */ + int txq_get_index; +}; + +struct mvpp2_tx_queue { + /* Physical number of this Tx queue */ + u8 id; + + /* Logical number of this Tx queue */ + u8 log_id; + + /* Number of Tx DMA descriptors in the descriptor ring */ + int size; + + /* Number of currently used Tx DMA descriptor in the descriptor ring */ + int count; + + /* Per-CPU control of physical Tx queues */ + struct mvpp2_txq_pcpu __percpu *pcpu; + + u32 done_pkts_coal; + + /* Virtual address of thex Tx DMA descriptors array */ + struct mvpp2_tx_desc *descs; + + /* DMA address of the Tx DMA descriptors array */ + dma_addr_t descs_phys; + + /* Index of the last Tx DMA descriptor */ + int last_desc; + + /* Index of the next Tx DMA descriptor to process */ + int next_desc_to_proc; +}; + +struct mvpp2_rx_queue { + /* RX queue number, in the range 0-31 for physical RXQs */ + u8 id; + + /* Num of rx descriptors in the rx descriptor ring */ + int size; + + u32 pkts_coal; + u32 time_coal; + + /* Virtual address of the RX DMA descriptors array */ + struct mvpp2_rx_desc *descs; + + /* DMA address of the RX DMA descriptors array */ + dma_addr_t descs_phys; + + /* Index of the last RX DMA descriptor */ + int last_desc; + + /* Index of the next RX DMA descriptor to process */ + int next_desc_to_proc; + + /* ID of port to which physical RXQ is mapped */ + int port; + + /* Port's logic RXQ number to which physical RXQ is mapped */ + int logic_rxq; +}; + +union mvpp2_prs_tcam_entry { + u32 word[MVPP2_PRS_TCAM_WORDS]; + u8 byte[MVPP2_PRS_TCAM_WORDS * 4]; +}; + +union mvpp2_prs_sram_entry { + u32 word[MVPP2_PRS_SRAM_WORDS]; + u8 byte[MVPP2_PRS_SRAM_WORDS * 4]; +}; + +struct mvpp2_prs_entry { + u32 index; + union mvpp2_prs_tcam_entry tcam; + union mvpp2_prs_sram_entry sram; +}; + +struct mvpp2_prs_shadow { + bool valid; + bool finish; + + /* Lookup ID */ + int lu; + + /* User defined offset */ + int udf; + + /* Result info */ + u32 ri; + u32 ri_mask; +}; + +struct mvpp2_cls_flow_entry { + u32 index; + u32 data[MVPP2_CLS_FLOWS_TBL_DATA_WORDS]; +}; + +struct mvpp2_cls_lookup_entry { + u32 lkpid; + u32 way; + u32 data; +}; + +struct mvpp2_bm_pool { + /* Pool number in the range 0-7 */ + int id; + enum mvpp2_bm_type type; + + /* Buffer Pointers Pool External (BPPE) size */ + int size; + /* Number of buffers for this pool */ + int buf_num; + /* Pool buffer size */ + int buf_size; + /* Packet size */ + int pkt_size; + + /* BPPE virtual base address */ + u32 *virt_addr; + /* BPPE physical base address */ + dma_addr_t phys_addr; + + /* Ports using BM pool */ + u32 port_map; + + /* Occupied buffers indicator */ + int in_use_thresh; +}; + +struct mvpp2_buff_hdr { + u32 next_buff_phys_addr; + u32 next_buff_virt_addr; + u16 byte_count; + u16 info; + u8 reserved1; /* bm_qset (for future use, BM) */ +}; + +/* Buffer header info bits */ +#define MVPP2_B_HDR_INFO_MC_ID_MASK 0xfff +#define MVPP2_B_HDR_INFO_MC_ID(info) ((info) & MVPP2_B_HDR_INFO_MC_ID_MASK) +#define MVPP2_B_HDR_INFO_LAST_OFFS 12 +#define MVPP2_B_HDR_INFO_LAST_MASK BIT(12) +#define MVPP2_B_HDR_INFO_IS_LAST(info) \ + ((info & MVPP2_B_HDR_INFO_LAST_MASK) >> MVPP2_B_HDR_INFO_LAST_OFFS) + +/* Static declaractions */ + +/* Number of RXQs used by single port */ +static int rxq_number = MVPP2_DEFAULT_RXQ; +/* Number of TXQs used by single port */ +static int txq_number = MVPP2_DEFAULT_TXQ; + +#define MVPP2_DRIVER_NAME "mvpp2" +#define MVPP2_DRIVER_VERSION "1.0" + +/* + * U-Boot internal data, mostly uncached buffers for descriptors and data + */ +struct buffer_location { + struct mvpp2_tx_desc *aggr_tx_descs; + struct mvpp2_tx_desc *tx_descs; + struct mvpp2_rx_desc *rx_descs; + u32 *bm_pool[MVPP2_BM_POOLS_NUM]; + u32 *rx_buffer[MVPP2_BM_LONG_BUF_NUM]; + int first_rxq; +}; + +/* + * All 4 interfaces use the same global buffer, since only one interface + * can be enabled at once + */ +static struct buffer_location buffer_loc; + +/* + * Page table entries are set to 1MB, or multiples of 1MB + * (not < 1MB). driver uses less bd's so use 1MB bdspace. + */ +#define BD_SPACE (1 << 20) + +/* Utility/helper methods */ + +static void mvpp2_write(struct mvpp2 *priv, u32 offset, u32 data) +{ + writel(data, priv->base + offset); +} + +static u32 mvpp2_read(struct mvpp2 *priv, u32 offset) +{ + return readl(priv->base + offset); +} + +static void mvpp2_txq_inc_get(struct mvpp2_txq_pcpu *txq_pcpu) +{ + txq_pcpu->txq_get_index++; + if (txq_pcpu->txq_get_index == txq_pcpu->size) + txq_pcpu->txq_get_index = 0; +} + +/* Get number of physical egress port */ +static inline int mvpp2_egress_port(struct mvpp2_port *port) +{ + return MVPP2_MAX_TCONT + port->id; +} + +/* Get number of physical TXQ */ +static inline int mvpp2_txq_phys(int port, int txq) +{ + return (MVPP2_MAX_TCONT + port) * MVPP2_MAX_TXQ + txq; +} + +/* Parser configuration routines */ + +/* Update parser tcam and sram hw entries */ +static int mvpp2_prs_hw_write(struct mvpp2 *priv, struct mvpp2_prs_entry *pe) +{ + int i; + + if (pe->index > MVPP2_PRS_TCAM_SRAM_SIZE - 1) + return -EINVAL; + + /* Clear entry invalidation bit */ + pe->tcam.word[MVPP2_PRS_TCAM_INV_WORD] &= ~MVPP2_PRS_TCAM_INV_MASK; + + /* Write tcam index - indirect access */ + mvpp2_write(priv, MVPP2_PRS_TCAM_IDX_REG, pe->index); + for (i = 0; i < MVPP2_PRS_TCAM_WORDS; i++) + mvpp2_write(priv, MVPP2_PRS_TCAM_DATA_REG(i), pe->tcam.word[i]); + + /* Write sram index - indirect access */ + mvpp2_write(priv, MVPP2_PRS_SRAM_IDX_REG, pe->index); + for (i = 0; i < MVPP2_PRS_SRAM_WORDS; i++) + mvpp2_write(priv, MVPP2_PRS_SRAM_DATA_REG(i), pe->sram.word[i]); + + return 0; +} + +/* Read tcam entry from hw */ +static int mvpp2_prs_hw_read(struct mvpp2 *priv, struct mvpp2_prs_entry *pe) +{ + int i; + + if (pe->index > MVPP2_PRS_TCAM_SRAM_SIZE - 1) + return -EINVAL; + + /* Write tcam index - indirect access */ + mvpp2_write(priv, MVPP2_PRS_TCAM_IDX_REG, pe->index); + + pe->tcam.word[MVPP2_PRS_TCAM_INV_WORD] = mvpp2_read(priv, + MVPP2_PRS_TCAM_DATA_REG(MVPP2_PRS_TCAM_INV_WORD)); + if (pe->tcam.word[MVPP2_PRS_TCAM_INV_WORD] & MVPP2_PRS_TCAM_INV_MASK) + return MVPP2_PRS_TCAM_ENTRY_INVALID; + + for (i = 0; i < MVPP2_PRS_TCAM_WORDS; i++) + pe->tcam.word[i] = mvpp2_read(priv, MVPP2_PRS_TCAM_DATA_REG(i)); + + /* Write sram index - indirect access */ + mvpp2_write(priv, MVPP2_PRS_SRAM_IDX_REG, pe->index); + for (i = 0; i < MVPP2_PRS_SRAM_WORDS; i++) + pe->sram.word[i] = mvpp2_read(priv, MVPP2_PRS_SRAM_DATA_REG(i)); + + return 0; +} + +/* Invalidate tcam hw entry */ +static void mvpp2_prs_hw_inv(struct mvpp2 *priv, int index) +{ + /* Write index - indirect access */ + mvpp2_write(priv, MVPP2_PRS_TCAM_IDX_REG, index); + mvpp2_write(priv, MVPP2_PRS_TCAM_DATA_REG(MVPP2_PRS_TCAM_INV_WORD), + MVPP2_PRS_TCAM_INV_MASK); +} + +/* Enable shadow table entry and set its lookup ID */ +static void mvpp2_prs_shadow_set(struct mvpp2 *priv, int index, int lu) +{ + priv->prs_shadow[index].valid = true; + priv->prs_shadow[index].lu = lu; +} + +/* Update ri fields in shadow table entry */ +static void mvpp2_prs_shadow_ri_set(struct mvpp2 *priv, int index, + unsigned int ri, unsigned int ri_mask) +{ + priv->prs_shadow[index].ri_mask = ri_mask; + priv->prs_shadow[index].ri = ri; +} + +/* Update lookup field in tcam sw entry */ +static void mvpp2_prs_tcam_lu_set(struct mvpp2_prs_entry *pe, unsigned int lu) +{ + int enable_off = MVPP2_PRS_TCAM_EN_OFFS(MVPP2_PRS_TCAM_LU_BYTE); + + pe->tcam.byte[MVPP2_PRS_TCAM_LU_BYTE] = lu; + pe->tcam.byte[enable_off] = MVPP2_PRS_LU_MASK; +} + +/* Update mask for single port in tcam sw entry */ +static void mvpp2_prs_tcam_port_set(struct mvpp2_prs_entry *pe, + unsigned int port, bool add) +{ + int enable_off = MVPP2_PRS_TCAM_EN_OFFS(MVPP2_PRS_TCAM_PORT_BYTE); + + if (add) + pe->tcam.byte[enable_off] &= ~(1 << port); + else + pe->tcam.byte[enable_off] |= 1 << port; +} + +/* Update port map in tcam sw entry */ +static void mvpp2_prs_tcam_port_map_set(struct mvpp2_prs_entry *pe, + unsigned int ports) +{ + unsigned char port_mask = MVPP2_PRS_PORT_MASK; + int enable_off = MVPP2_PRS_TCAM_EN_OFFS(MVPP2_PRS_TCAM_PORT_BYTE); + + pe->tcam.byte[MVPP2_PRS_TCAM_PORT_BYTE] = 0; + pe->tcam.byte[enable_off] &= ~port_mask; + pe->tcam.byte[enable_off] |= ~ports & MVPP2_PRS_PORT_MASK; +} + +/* Obtain port map from tcam sw entry */ +static unsigned int mvpp2_prs_tcam_port_map_get(struct mvpp2_prs_entry *pe) +{ + int enable_off = MVPP2_PRS_TCAM_EN_OFFS(MVPP2_PRS_TCAM_PORT_BYTE); + + return ~(pe->tcam.byte[enable_off]) & MVPP2_PRS_PORT_MASK; +} + +/* Set byte of data and its enable bits in tcam sw entry */ +static void mvpp2_prs_tcam_data_byte_set(struct mvpp2_prs_entry *pe, + unsigned int offs, unsigned char byte, + unsigned char enable) +{ + pe->tcam.byte[MVPP2_PRS_TCAM_DATA_BYTE(offs)] = byte; + pe->tcam.byte[MVPP2_PRS_TCAM_DATA_BYTE_EN(offs)] = enable; +} + +/* Get byte of data and its enable bits from tcam sw entry */ +static void mvpp2_prs_tcam_data_byte_get(struct mvpp2_prs_entry *pe, + unsigned int offs, unsigned char *byte, + unsigned char *enable) +{ + *byte = pe->tcam.byte[MVPP2_PRS_TCAM_DATA_BYTE(offs)]; + *enable = pe->tcam.byte[MVPP2_PRS_TCAM_DATA_BYTE_EN(offs)]; +} + +/* Set ethertype in tcam sw entry */ +static void mvpp2_prs_match_etype(struct mvpp2_prs_entry *pe, int offset, + unsigned short ethertype) +{ + mvpp2_prs_tcam_data_byte_set(pe, offset + 0, ethertype >> 8, 0xff); + mvpp2_prs_tcam_data_byte_set(pe, offset + 1, ethertype & 0xff, 0xff); +} + +/* Set bits in sram sw entry */ +static void mvpp2_prs_sram_bits_set(struct mvpp2_prs_entry *pe, int bit_num, + int val) +{ + pe->sram.byte[MVPP2_BIT_TO_BYTE(bit_num)] |= (val << (bit_num % 8)); +} + +/* Clear bits in sram sw entry */ +static void mvpp2_prs_sram_bits_clear(struct mvpp2_prs_entry *pe, int bit_num, + int val) +{ + pe->sram.byte[MVPP2_BIT_TO_BYTE(bit_num)] &= ~(val << (bit_num % 8)); +} + +/* Update ri bits in sram sw entry */ +static void mvpp2_prs_sram_ri_update(struct mvpp2_prs_entry *pe, + unsigned int bits, unsigned int mask) +{ + unsigned int i; + + for (i = 0; i < MVPP2_PRS_SRAM_RI_CTRL_BITS; i++) { + int ri_off = MVPP2_PRS_SRAM_RI_OFFS; + + if (!(mask & BIT(i))) + continue; + + if (bits & BIT(i)) + mvpp2_prs_sram_bits_set(pe, ri_off + i, 1); + else + mvpp2_prs_sram_bits_clear(pe, ri_off + i, 1); + + mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_RI_CTRL_OFFS + i, 1); + } +} + +/* Update ai bits in sram sw entry */ +static void mvpp2_prs_sram_ai_update(struct mvpp2_prs_entry *pe, + unsigned int bits, unsigned int mask) +{ + unsigned int i; + int ai_off = MVPP2_PRS_SRAM_AI_OFFS; + + for (i = 0; i < MVPP2_PRS_SRAM_AI_CTRL_BITS; i++) { + + if (!(mask & BIT(i))) + continue; + + if (bits & BIT(i)) + mvpp2_prs_sram_bits_set(pe, ai_off + i, 1); + else + mvpp2_prs_sram_bits_clear(pe, ai_off + i, 1); + + mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_AI_CTRL_OFFS + i, 1); + } +} + +/* Read ai bits from sram sw entry */ +static int mvpp2_prs_sram_ai_get(struct mvpp2_prs_entry *pe) +{ + u8 bits; + int ai_off = MVPP2_BIT_TO_BYTE(MVPP2_PRS_SRAM_AI_OFFS); + int ai_en_off = ai_off + 1; + int ai_shift = MVPP2_PRS_SRAM_AI_OFFS % 8; + + bits = (pe->sram.byte[ai_off] >> ai_shift) | + (pe->sram.byte[ai_en_off] << (8 - ai_shift)); + + return bits; +} + +/* In sram sw entry set lookup ID field of the tcam key to be used in the next + * lookup interation + */ +static void mvpp2_prs_sram_next_lu_set(struct mvpp2_prs_entry *pe, + unsigned int lu) +{ + int sram_next_off = MVPP2_PRS_SRAM_NEXT_LU_OFFS; + + mvpp2_prs_sram_bits_clear(pe, sram_next_off, + MVPP2_PRS_SRAM_NEXT_LU_MASK); + mvpp2_prs_sram_bits_set(pe, sram_next_off, lu); +} + +/* In the sram sw entry set sign and value of the next lookup offset + * and the offset value generated to the classifier + */ +static void mvpp2_prs_sram_shift_set(struct mvpp2_prs_entry *pe, int shift, + unsigned int op) +{ + /* Set sign */ + if (shift < 0) { + mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_SHIFT_SIGN_BIT, 1); + shift = 0 - shift; + } else { + mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_SHIFT_SIGN_BIT, 1); + } + + /* Set value */ + pe->sram.byte[MVPP2_BIT_TO_BYTE(MVPP2_PRS_SRAM_SHIFT_OFFS)] = + (unsigned char)shift; + + /* Reset and set operation */ + mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS, + MVPP2_PRS_SRAM_OP_SEL_SHIFT_MASK); + mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS, op); + + /* Set base offset as current */ + mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_OP_SEL_BASE_OFFS, 1); +} + +/* In the sram sw entry set sign and value of the user defined offset + * generated to the classifier + */ +static void mvpp2_prs_sram_offset_set(struct mvpp2_prs_entry *pe, + unsigned int type, int offset, + unsigned int op) +{ + /* Set sign */ + if (offset < 0) { + mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_UDF_SIGN_BIT, 1); + offset = 0 - offset; + } else { + mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_UDF_SIGN_BIT, 1); + } + + /* Set value */ + mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_UDF_OFFS, + MVPP2_PRS_SRAM_UDF_MASK); + mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_UDF_OFFS, offset); + pe->sram.byte[MVPP2_BIT_TO_BYTE(MVPP2_PRS_SRAM_UDF_OFFS + + MVPP2_PRS_SRAM_UDF_BITS)] &= + ~(MVPP2_PRS_SRAM_UDF_MASK >> (8 - (MVPP2_PRS_SRAM_UDF_OFFS % 8))); + pe->sram.byte[MVPP2_BIT_TO_BYTE(MVPP2_PRS_SRAM_UDF_OFFS + + MVPP2_PRS_SRAM_UDF_BITS)] |= + (offset >> (8 - (MVPP2_PRS_SRAM_UDF_OFFS % 8))); + + /* Set offset type */ + mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_UDF_TYPE_OFFS, + MVPP2_PRS_SRAM_UDF_TYPE_MASK); + mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_UDF_TYPE_OFFS, type); + + /* Set offset operation */ + mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS, + MVPP2_PRS_SRAM_OP_SEL_UDF_MASK); + mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS, op); + + pe->sram.byte[MVPP2_BIT_TO_BYTE(MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS + + MVPP2_PRS_SRAM_OP_SEL_UDF_BITS)] &= + ~(MVPP2_PRS_SRAM_OP_SEL_UDF_MASK >> + (8 - (MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS % 8))); + + pe->sram.byte[MVPP2_BIT_TO_BYTE(MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS + + MVPP2_PRS_SRAM_OP_SEL_UDF_BITS)] |= + (op >> (8 - (MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS % 8))); + + /* Set base offset as current */ + mvpp2_prs_sram_bits_clear(pe, MVPP2_PRS_SRAM_OP_SEL_BASE_OFFS, 1); +} + +/* Find parser flow entry */ +static struct mvpp2_prs_entry *mvpp2_prs_flow_find(struct mvpp2 *priv, int flow) +{ + struct mvpp2_prs_entry *pe; + int tid; + + pe = kzalloc(sizeof(*pe), GFP_KERNEL); + if (!pe) + return NULL; + mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_FLOWS); + + /* Go through the all entires with MVPP2_PRS_LU_FLOWS */ + for (tid = MVPP2_PRS_TCAM_SRAM_SIZE - 1; tid >= 0; tid--) { + u8 bits; + + if (!priv->prs_shadow[tid].valid || + priv->prs_shadow[tid].lu != MVPP2_PRS_LU_FLOWS) + continue; + + pe->index = tid; + mvpp2_prs_hw_read(priv, pe); + bits = mvpp2_prs_sram_ai_get(pe); + + /* Sram store classification lookup ID in AI bits [5:0] */ + if ((bits & MVPP2_PRS_FLOW_ID_MASK) == flow) + return pe; + } + kfree(pe); + + return NULL; +} + +/* Return first free tcam index, seeking from start to end */ +static int mvpp2_prs_tcam_first_free(struct mvpp2 *priv, unsigned char start, + unsigned char end) +{ + int tid; + + if (start > end) + swap(start, end); + + if (end >= MVPP2_PRS_TCAM_SRAM_SIZE) + end = MVPP2_PRS_TCAM_SRAM_SIZE - 1; + + for (tid = start; tid <= end; tid++) { + if (!priv->prs_shadow[tid].valid) + return tid; + } + + return -EINVAL; +} + +/* Enable/disable dropping all mac da's */ +static void mvpp2_prs_mac_drop_all_set(struct mvpp2 *priv, int port, bool add) +{ + struct mvpp2_prs_entry pe; + + if (priv->prs_shadow[MVPP2_PE_DROP_ALL].valid) { + /* Entry exist - update port only */ + pe.index = MVPP2_PE_DROP_ALL; + mvpp2_prs_hw_read(priv, &pe); + } else { + /* Entry doesn't exist - create new */ + memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); + pe.index = MVPP2_PE_DROP_ALL; + + /* Non-promiscuous mode for all ports - DROP unknown packets */ + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_DROP_MASK, + MVPP2_PRS_RI_DROP_MASK); + + mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); + + /* Update shadow table */ + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_MAC); + + /* Mask all ports */ + mvpp2_prs_tcam_port_map_set(&pe, 0); + } + + /* Update port mask */ + mvpp2_prs_tcam_port_set(&pe, port, add); + + mvpp2_prs_hw_write(priv, &pe); +} + +/* Set port to promiscuous mode */ +static void mvpp2_prs_mac_promisc_set(struct mvpp2 *priv, int port, bool add) +{ + struct mvpp2_prs_entry pe; + + /* Promiscuous mode - Accept unknown packets */ + + if (priv->prs_shadow[MVPP2_PE_MAC_PROMISCUOUS].valid) { + /* Entry exist - update port only */ + pe.index = MVPP2_PE_MAC_PROMISCUOUS; + mvpp2_prs_hw_read(priv, &pe); + } else { + /* Entry doesn't exist - create new */ + memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); + pe.index = MVPP2_PE_MAC_PROMISCUOUS; + + /* Continue - set next lookup */ + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_DSA); + + /* Set result info bits */ + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L2_UCAST, + MVPP2_PRS_RI_L2_CAST_MASK); + + /* Shift to ethertype */ + mvpp2_prs_sram_shift_set(&pe, 2 * ETH_ALEN, + MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); + + /* Mask all ports */ + mvpp2_prs_tcam_port_map_set(&pe, 0); + + /* Update shadow table */ + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_MAC); + } + + /* Update port mask */ + mvpp2_prs_tcam_port_set(&pe, port, add); + + mvpp2_prs_hw_write(priv, &pe); +} + +/* Accept multicast */ +static void mvpp2_prs_mac_multi_set(struct mvpp2 *priv, int port, int index, + bool add) +{ + struct mvpp2_prs_entry pe; + unsigned char da_mc; + + /* Ethernet multicast address first byte is + * 0x01 for IPv4 and 0x33 for IPv6 + */ + da_mc = (index == MVPP2_PE_MAC_MC_ALL) ? 0x01 : 0x33; + + if (priv->prs_shadow[index].valid) { + /* Entry exist - update port only */ + pe.index = index; + mvpp2_prs_hw_read(priv, &pe); + } else { + /* Entry doesn't exist - create new */ + memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); + pe.index = index; + + /* Continue - set next lookup */ + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_DSA); + + /* Set result info bits */ + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L2_MCAST, + MVPP2_PRS_RI_L2_CAST_MASK); + + /* Update tcam entry data first byte */ + mvpp2_prs_tcam_data_byte_set(&pe, 0, da_mc, 0xff); + + /* Shift to ethertype */ + mvpp2_prs_sram_shift_set(&pe, 2 * ETH_ALEN, + MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); + + /* Mask all ports */ + mvpp2_prs_tcam_port_map_set(&pe, 0); + + /* Update shadow table */ + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_MAC); + } + + /* Update port mask */ + mvpp2_prs_tcam_port_set(&pe, port, add); + + mvpp2_prs_hw_write(priv, &pe); +} + +/* Parser per-port initialization */ +static void mvpp2_prs_hw_port_init(struct mvpp2 *priv, int port, int lu_first, + int lu_max, int offset) +{ + u32 val; + + /* Set lookup ID */ + val = mvpp2_read(priv, MVPP2_PRS_INIT_LOOKUP_REG); + val &= ~MVPP2_PRS_PORT_LU_MASK(port); + val |= MVPP2_PRS_PORT_LU_VAL(port, lu_first); + mvpp2_write(priv, MVPP2_PRS_INIT_LOOKUP_REG, val); + + /* Set maximum number of loops for packet received from port */ + val = mvpp2_read(priv, MVPP2_PRS_MAX_LOOP_REG(port)); + val &= ~MVPP2_PRS_MAX_LOOP_MASK(port); + val |= MVPP2_PRS_MAX_LOOP_VAL(port, lu_max); + mvpp2_write(priv, MVPP2_PRS_MAX_LOOP_REG(port), val); + + /* Set initial offset for packet header extraction for the first + * searching loop + */ + val = mvpp2_read(priv, MVPP2_PRS_INIT_OFFS_REG(port)); + val &= ~MVPP2_PRS_INIT_OFF_MASK(port); + val |= MVPP2_PRS_INIT_OFF_VAL(port, offset); + mvpp2_write(priv, MVPP2_PRS_INIT_OFFS_REG(port), val); +} + +/* Default flow entries initialization for all ports */ +static void mvpp2_prs_def_flow_init(struct mvpp2 *priv) +{ + struct mvpp2_prs_entry pe; + int port; + + for (port = 0; port < MVPP2_MAX_PORTS; port++) { + memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_FLOWS); + pe.index = MVPP2_PE_FIRST_DEFAULT_FLOW - port; + + /* Mask all ports */ + mvpp2_prs_tcam_port_map_set(&pe, 0); + + /* Set flow ID*/ + mvpp2_prs_sram_ai_update(&pe, port, MVPP2_PRS_FLOW_ID_MASK); + mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_DONE_BIT, 1); + + /* Update shadow table and hw entry */ + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_FLOWS); + mvpp2_prs_hw_write(priv, &pe); + } +} + +/* Set default entry for Marvell Header field */ +static void mvpp2_prs_mh_init(struct mvpp2 *priv) +{ + struct mvpp2_prs_entry pe; + + memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + + pe.index = MVPP2_PE_MH_DEFAULT; + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MH); + mvpp2_prs_sram_shift_set(&pe, MVPP2_MH_SIZE, + MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_MAC); + + /* Unmask all ports */ + mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); + + /* Update shadow table and hw entry */ + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_MH); + mvpp2_prs_hw_write(priv, &pe); +} + +/* Set default entires (place holder) for promiscuous, non-promiscuous and + * multicast MAC addresses + */ +static void mvpp2_prs_mac_init(struct mvpp2 *priv) +{ + struct mvpp2_prs_entry pe; + + memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + + /* Non-promiscuous mode for all ports - DROP unknown packets */ + pe.index = MVPP2_PE_MAC_NON_PROMISCUOUS; + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC); + + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_DROP_MASK, + MVPP2_PRS_RI_DROP_MASK); + mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); + + /* Unmask all ports */ + mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); + + /* Update shadow table and hw entry */ + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_MAC); + mvpp2_prs_hw_write(priv, &pe); + + /* place holders only - no ports */ + mvpp2_prs_mac_drop_all_set(priv, 0, false); + mvpp2_prs_mac_promisc_set(priv, 0, false); + mvpp2_prs_mac_multi_set(priv, MVPP2_PE_MAC_MC_ALL, 0, false); + mvpp2_prs_mac_multi_set(priv, MVPP2_PE_MAC_MC_IP6, 0, false); +} + +/* Match basic ethertypes */ +static int mvpp2_prs_etype_init(struct mvpp2 *priv) +{ + struct mvpp2_prs_entry pe; + int tid; + + /* Ethertype: PPPoE */ + tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, + MVPP2_PE_LAST_FREE_TID); + if (tid < 0) + return tid; + + memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); + pe.index = tid; + + mvpp2_prs_match_etype(&pe, 0, PROT_PPP_SES); + + mvpp2_prs_sram_shift_set(&pe, MVPP2_PPPOE_HDR_SIZE, + MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_PPPOE); + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_PPPOE_MASK, + MVPP2_PRS_RI_PPPOE_MASK); + + /* Update shadow table and hw entry */ + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_L2); + priv->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; + priv->prs_shadow[pe.index].finish = false; + mvpp2_prs_shadow_ri_set(priv, pe.index, MVPP2_PRS_RI_PPPOE_MASK, + MVPP2_PRS_RI_PPPOE_MASK); + mvpp2_prs_hw_write(priv, &pe); + + /* Ethertype: ARP */ + tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, + MVPP2_PE_LAST_FREE_TID); + if (tid < 0) + return tid; + + memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); + pe.index = tid; + + mvpp2_prs_match_etype(&pe, 0, PROT_ARP); + + /* Generate flow in the next iteration*/ + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); + mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_ARP, + MVPP2_PRS_RI_L3_PROTO_MASK); + /* Set L3 offset */ + mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, + MVPP2_ETH_TYPE_LEN, + MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); + + /* Update shadow table and hw entry */ + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_L2); + priv->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; + priv->prs_shadow[pe.index].finish = true; + mvpp2_prs_shadow_ri_set(priv, pe.index, MVPP2_PRS_RI_L3_ARP, + MVPP2_PRS_RI_L3_PROTO_MASK); + mvpp2_prs_hw_write(priv, &pe); + + /* Ethertype: LBTD */ + tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, + MVPP2_PE_LAST_FREE_TID); + if (tid < 0) + return tid; + + memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); + pe.index = tid; + + mvpp2_prs_match_etype(&pe, 0, MVPP2_IP_LBDT_TYPE); + + /* Generate flow in the next iteration*/ + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); + mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_CPU_CODE_RX_SPEC | + MVPP2_PRS_RI_UDF3_RX_SPECIAL, + MVPP2_PRS_RI_CPU_CODE_MASK | + MVPP2_PRS_RI_UDF3_MASK); + /* Set L3 offset */ + mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, + MVPP2_ETH_TYPE_LEN, + MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); + + /* Update shadow table and hw entry */ + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_L2); + priv->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; + priv->prs_shadow[pe.index].finish = true; + mvpp2_prs_shadow_ri_set(priv, pe.index, MVPP2_PRS_RI_CPU_CODE_RX_SPEC | + MVPP2_PRS_RI_UDF3_RX_SPECIAL, + MVPP2_PRS_RI_CPU_CODE_MASK | + MVPP2_PRS_RI_UDF3_MASK); + mvpp2_prs_hw_write(priv, &pe); + + /* Ethertype: IPv4 without options */ + tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, + MVPP2_PE_LAST_FREE_TID); + if (tid < 0) + return tid; + + memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); + pe.index = tid; + + mvpp2_prs_match_etype(&pe, 0, PROT_IP); + mvpp2_prs_tcam_data_byte_set(&pe, MVPP2_ETH_TYPE_LEN, + MVPP2_PRS_IPV4_HEAD | MVPP2_PRS_IPV4_IHL, + MVPP2_PRS_IPV4_HEAD_MASK | + MVPP2_PRS_IPV4_IHL_MASK); + + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4); + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4, + MVPP2_PRS_RI_L3_PROTO_MASK); + /* Skip eth_type + 4 bytes of IP header */ + mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 4, + MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); + /* Set L3 offset */ + mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, + MVPP2_ETH_TYPE_LEN, + MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); + + /* Update shadow table and hw entry */ + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_L2); + priv->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; + priv->prs_shadow[pe.index].finish = false; + mvpp2_prs_shadow_ri_set(priv, pe.index, MVPP2_PRS_RI_L3_IP4, + MVPP2_PRS_RI_L3_PROTO_MASK); + mvpp2_prs_hw_write(priv, &pe); + + /* Ethertype: IPv4 with options */ + tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, + MVPP2_PE_LAST_FREE_TID); + if (tid < 0) + return tid; + + pe.index = tid; + + /* Clear tcam data before updating */ + pe.tcam.byte[MVPP2_PRS_TCAM_DATA_BYTE(MVPP2_ETH_TYPE_LEN)] = 0x0; + pe.tcam.byte[MVPP2_PRS_TCAM_DATA_BYTE_EN(MVPP2_ETH_TYPE_LEN)] = 0x0; + + mvpp2_prs_tcam_data_byte_set(&pe, MVPP2_ETH_TYPE_LEN, + MVPP2_PRS_IPV4_HEAD, + MVPP2_PRS_IPV4_HEAD_MASK); + + /* Clear ri before updating */ + pe.sram.word[MVPP2_PRS_SRAM_RI_WORD] = 0x0; + pe.sram.word[MVPP2_PRS_SRAM_RI_CTRL_WORD] = 0x0; + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4_OPT, + MVPP2_PRS_RI_L3_PROTO_MASK); + + /* Update shadow table and hw entry */ + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_L2); + priv->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; + priv->prs_shadow[pe.index].finish = false; + mvpp2_prs_shadow_ri_set(priv, pe.index, MVPP2_PRS_RI_L3_IP4_OPT, + MVPP2_PRS_RI_L3_PROTO_MASK); + mvpp2_prs_hw_write(priv, &pe); + + /* Ethertype: IPv6 without options */ + tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, + MVPP2_PE_LAST_FREE_TID); + if (tid < 0) + return tid; + + memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); + pe.index = tid; + + mvpp2_prs_match_etype(&pe, 0, PROT_IPV6); + + /* Skip DIP of IPV6 header */ + mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 8 + + MVPP2_MAX_L3_ADDR_SIZE, + MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP6); + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP6, + MVPP2_PRS_RI_L3_PROTO_MASK); + /* Set L3 offset */ + mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, + MVPP2_ETH_TYPE_LEN, + MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); + + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_L2); + priv->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; + priv->prs_shadow[pe.index].finish = false; + mvpp2_prs_shadow_ri_set(priv, pe.index, MVPP2_PRS_RI_L3_IP6, + MVPP2_PRS_RI_L3_PROTO_MASK); + mvpp2_prs_hw_write(priv, &pe); + + /* Default entry for MVPP2_PRS_LU_L2 - Unknown ethtype */ + memset(&pe, 0, sizeof(struct mvpp2_prs_entry)); + mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_L2); + pe.index = MVPP2_PE_ETH_TYPE_UN; + + /* Unmask all ports */ + mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK); + + /* Generate flow in the next iteration*/ + mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1); + mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS); + mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_UN, + MVPP2_PRS_RI_L3_PROTO_MASK); + /* Set L3 offset even it's unknown L3 */ + mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3, + MVPP2_ETH_TYPE_LEN, + MVPP2_PRS_SRAM_OP_SEL_UDF_ADD); + + /* Update shadow table and hw entry */ + mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_L2); + priv->prs_shadow[pe.index].udf = MVPP2_PRS_UDF_L2_DEF; + priv->prs_shadow[pe.index].finish = true; + mvpp2_prs_shadow_ri_set(priv, pe.index, MVPP2_PRS_RI_L3_UN, + MVPP2_PRS_RI_L3_PROTO_MASK); + mvpp2_prs_hw_write(priv, &pe); + + return 0; +} + +/* Parser default initialization */ +static int mvpp2_prs_default_init(struct udevice *dev, + struct mvpp2 *priv) +{ + int err, index, i; + + /* Enable tcam table */ + mvpp2_write(priv, MVPP2_PRS_TCAM_CTRL_REG, MVPP2_PRS_TCAM_EN_MASK); + + /* Clear all tcam and sram entries */ + for (index = 0; index < MVPP2_PRS_TCAM_SRAM_SIZE; index++) { + mvpp2_write(priv, MVPP2_PRS_TCAM_IDX_REG, index); + for (i = 0; i < MVPP2_PRS_TCAM_WORDS; i++) + mvpp2_write(priv, MVPP2_PRS_TCAM_DATA_REG(i), 0); + + mvpp2_write(priv, MVPP2_PRS_SRAM_IDX_REG, index); + for (i = 0; i < MVPP2_PRS_SRAM_WORDS; i++) + mvpp2_write(priv, MVPP2_PRS_SRAM_DATA_REG(i), 0); + } + + /* Invalidate all tcam entries */ + for (index = 0; index < MVPP2_PRS_TCAM_SRAM_SIZE; index++) + mvpp2_prs_hw_inv(priv, index); + + priv->prs_shadow = devm_kcalloc(dev, MVPP2_PRS_TCAM_SRAM_SIZE, + sizeof(struct mvpp2_prs_shadow), + GFP_KERNEL); + if (!priv->prs_shadow) + return -ENOMEM; + + /* Always start from lookup = 0 */ + for (index = 0; index < MVPP2_MAX_PORTS; index++) + mvpp2_prs_hw_port_init(priv, index, MVPP2_PRS_LU_MH, + MVPP2_PRS_PORT_LU_MAX, 0); + + mvpp2_prs_def_flow_init(priv); + + mvpp2_prs_mh_init(priv); + + mvpp2_prs_mac_init(priv); + + err = mvpp2_prs_etype_init(priv); + if (err) + return err; + + return 0; +} + +/* Compare MAC DA with tcam entry data */ +static bool mvpp2_prs_mac_range_equals(struct mvpp2_prs_entry *pe, + const u8 *da, unsigned char *mask) +{ + unsigned char tcam_byte, tcam_mask; + int index; + + for (index = 0; index < ETH_ALEN; index++) { + mvpp2_prs_tcam_data_byte_get(pe, index, &tcam_byte, &tcam_mask); + if (tcam_mask != mask[index]) + return false; + + if ((tcam_mask & tcam_byte) != (da[index] & mask[index])) + return false; + } + + return true; +} + +/* Find tcam entry with matched pair <MAC DA, port> */ +static struct mvpp2_prs_entry * +mvpp2_prs_mac_da_range_find(struct mvpp2 *priv, int pmap, const u8 *da, + unsigned char *mask, int udf_type) +{ + struct mvpp2_prs_entry *pe; + int tid; + + pe = kzalloc(sizeof(*pe), GFP_KERNEL); + if (!pe) + return NULL; + mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_MAC); + + /* Go through the all entires with MVPP2_PRS_LU_MAC */ + for (tid = MVPP2_PE_FIRST_FREE_TID; + tid <= MVPP2_PE_LAST_FREE_TID; tid++) { + unsigned int entry_pmap; + + if (!priv->prs_shadow[tid].valid || + (priv->prs_shadow[tid].lu != MVPP2_PRS_LU_MAC) || + (priv->prs_shadow[tid].udf != udf_type)) + continue; + + pe->index = tid; + mvpp2_prs_hw_read(priv, pe); + entry_pmap = mvpp2_prs_tcam_port_map_get(pe); + + if (mvpp2_prs_mac_range_equals(pe, da, mask) && + entry_pmap == pmap) + return pe; + } + kfree(pe); + + return NULL; +} + +/* Update parser's mac da entry */ +static int mvpp2_prs_mac_da_accept(struct mvpp2 *priv, int port, + const u8 *da, bool add) +{ + struct mvpp2_prs_entry *pe; + unsigned int pmap, len, ri; + unsigned char mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + int tid; + + /* Scan TCAM and see if entry with this <MAC DA, port> already exist */ + pe = mvpp2_prs_mac_da_range_find(priv, (1 << port), da, mask, + MVPP2_PRS_UDF_MAC_DEF); + + /* No such entry */ + if (!pe) { + if (!add) + return 0; + + /* Create new TCAM entry */ + /* Find first range mac entry*/ + for (tid = MVPP2_PE_FIRST_FREE_TID; + tid <= MVPP2_PE_LAST_FREE_TID; tid++) + if (priv->prs_shadow[tid].valid && + (priv->prs_shadow[tid].lu == MVPP2_PRS_LU_MAC) && + (priv->prs_shadow[tid].udf == + MVPP2_PRS_UDF_MAC_RANGE)) + break; + + /* Go through the all entries from first to last */ + tid = mvpp2_prs_tcam_first_free(priv, MVPP2_PE_FIRST_FREE_TID, + tid - 1); + if (tid < 0) + return tid; + + pe = kzalloc(sizeof(*pe), GFP_KERNEL); + if (!pe) + return -1; + mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_MAC); + pe->index = tid; + + /* Mask all ports */ + mvpp2_prs_tcam_port_map_set(pe, 0); + } + + /* Update port mask */ + mvpp2_prs_tcam_port_set(pe, port, add); + + /* Invalidate the entry if no ports are left enabled */ + pmap = mvpp2_prs_tcam_port_map_get(pe); + if (pmap == 0) { + if (add) { + kfree(pe); + return -1; + } + mvpp2_prs_hw_inv(priv, pe->index); + priv->prs_shadow[pe->index].valid = false; + kfree(pe); + return 0; + } + + /* Continue - set next lookup */ + mvpp2_prs_sram_next_lu_set(pe, MVPP2_PRS_LU_DSA); + + /* Set match on DA */ + len = ETH_ALEN; + while (len--) + mvpp2_prs_tcam_data_byte_set(pe, len, da[len], 0xff); + + /* Set result info bits */ + ri = MVPP2_PRS_RI_L2_UCAST | MVPP2_PRS_RI_MAC_ME_MASK; + + mvpp2_prs_sram_ri_update(pe, ri, MVPP2_PRS_RI_L2_CAST_MASK | + MVPP2_PRS_RI_MAC_ME_MASK); + mvpp2_prs_shadow_ri_set(priv, pe->index, ri, MVPP2_PRS_RI_L2_CAST_MASK | + MVPP2_PRS_RI_MAC_ME_MASK); + + /* Shift to ethertype */ + mvpp2_prs_sram_shift_set(pe, 2 * ETH_ALEN, + MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD); + + /* Update shadow table and hw entry */ + priv->prs_shadow[pe->index].udf = MVPP2_PRS_UDF_MAC_DEF; + mvpp2_prs_shadow_set(priv, pe->index, MVPP2_PRS_LU_MAC); + mvpp2_prs_hw_write(priv, pe); + + kfree(pe); + + return 0; +} + +static int mvpp2_prs_update_mac_da(struct mvpp2_port *port, const u8 *da) +{ + int err; + + /* Remove old parser entry */ + err = mvpp2_prs_mac_da_accept(port->priv, port->id, port->dev_addr, + false); + if (err) + return err; + + /* Add new parser entry */ + err = mvpp2_prs_mac_da_accept(port->priv, port->id, da, true); + if (err) + return err; + + /* Set addr in the device */ + memcpy(port->dev_addr, da, ETH_ALEN); + + return 0; +} + +/* Set prs flow for the port */ +static int mvpp2_prs_def_flow(struct mvpp2_port *port) +{ + struct mvpp2_prs_entry *pe; + int tid; + + pe = mvpp2_prs_flow_find(port->priv, port->id); + + /* Such entry not exist */ + if (!pe) { + /* Go through the all entires from last to first */ + tid = mvpp2_prs_tcam_first_free(port->priv, + MVPP2_PE_LAST_FREE_TID, + MVPP2_PE_FIRST_FREE_TID); + if (tid < 0) + return tid; + + pe = kzalloc(sizeof(*pe), GFP_KERNEL); + if (!pe) + return -ENOMEM; + + mvpp2_prs_tcam_lu_set(pe, MVPP2_PRS_LU_FLOWS); + pe->index = tid; + + /* Set flow ID*/ + mvpp2_prs_sram_ai_update(pe, port->id, MVPP2_PRS_FLOW_ID_MASK); + mvpp2_prs_sram_bits_set(pe, MVPP2_PRS_SRAM_LU_DONE_BIT, 1); + + /* Update shadow table */ + mvpp2_prs_shadow_set(port->priv, pe->index, MVPP2_PRS_LU_FLOWS); + } + + mvpp2_prs_tcam_port_map_set(pe, (1 << port->id)); + mvpp2_prs_hw_write(port->priv, pe); + kfree(pe); + + return 0; +} + +/* Classifier configuration routines */ + +/* Update classification flow table registers */ +static void mvpp2_cls_flow_write(struct mvpp2 *priv, + struct mvpp2_cls_flow_entry *fe) +{ + mvpp2_write(priv, MVPP2_CLS_FLOW_INDEX_REG, fe->index); + mvpp2_write(priv, MVPP2_CLS_FLOW_TBL0_REG, fe->data[0]); + mvpp2_write(priv, MVPP2_CLS_FLOW_TBL1_REG, fe->data[1]); + mvpp2_write(priv, MVPP2_CLS_FLOW_TBL2_REG, fe->data[2]); +} + +/* Update classification lookup table register */ +static void mvpp2_cls_lookup_write(struct mvpp2 *priv, + struct mvpp2_cls_lookup_entry *le) +{ + u32 val; + + val = (le->way << MVPP2_CLS_LKP_INDEX_WAY_OFFS) | le->lkpid; + mvpp2_write(priv, MVPP2_CLS_LKP_INDEX_REG, val); + mvpp2_write(priv, MVPP2_CLS_LKP_TBL_REG, le->data); +} + +/* Classifier default initialization */ +static void mvpp2_cls_init(struct mvpp2 *priv) +{ + struct mvpp2_cls_lookup_entry le; + struct mvpp2_cls_flow_entry fe; + int index; + + /* Enable classifier */ + mvpp2_write(priv, MVPP2_CLS_MODE_REG, MVPP2_CLS_MODE_ACTIVE_MASK); + + /* Clear classifier flow table */ + memset(&fe.data, 0, MVPP2_CLS_FLOWS_TBL_DATA_WORDS); + for (index = 0; index < MVPP2_CLS_FLOWS_TBL_SIZE; index++) { + fe.index = index; + mvpp2_cls_flow_write(priv, &fe); + } + + /* Clear classifier lookup table */ + le.data = 0; + for (index = 0; index < MVPP2_CLS_LKP_TBL_SIZE; index++) { + le.lkpid = index; + le.way = 0; + mvpp2_cls_lookup_write(priv, &le); + + le.way = 1; + mvpp2_cls_lookup_write(priv, &le); + } +} + +static void mvpp2_cls_port_config(struct mvpp2_port *port) +{ + struct mvpp2_cls_lookup_entry le; + u32 val; + + /* Set way for the port */ + val = mvpp2_read(port->priv, MVPP2_CLS_PORT_WAY_REG); + val &= ~MVPP2_CLS_PORT_WAY_MASK(port->id); + mvpp2_write(port->priv, MVPP2_CLS_PORT_WAY_REG, val); + + /* Pick the entry to be accessed in lookup ID decoding table + * according to the way and lkpid. + */ + le.lkpid = port->id; + le.way = 0; + le.data = 0; + + /* Set initial CPU queue for receiving packets */ + le.data &= ~MVPP2_CLS_LKP_TBL_RXQ_MASK; + le.data |= port->first_rxq; + + /* Disable classification engines */ + le.data &= ~MVPP2_CLS_LKP_TBL_LOOKUP_EN_MASK; + + /* Update lookup ID table entry */ + mvpp2_cls_lookup_write(port->priv, &le); +} + +/* Set CPU queue number for oversize packets */ +static void mvpp2_cls_oversize_rxq_set(struct mvpp2_port *port) +{ + u32 val; + + mvpp2_write(port->priv, MVPP2_CLS_OVERSIZE_RXQ_LOW_REG(port->id), + port->first_rxq & MVPP2_CLS_OVERSIZE_RXQ_LOW_MASK); + + mvpp2_write(port->priv, MVPP2_CLS_SWFWD_P2HQ_REG(port->id), + (port->first_rxq >> MVPP2_CLS_OVERSIZE_RXQ_LOW_BITS)); + + val = mvpp2_read(port->priv, MVPP2_CLS_SWFWD_PCTRL_REG); + val |= MVPP2_CLS_SWFWD_PCTRL_MASK(port->id); + mvpp2_write(port->priv, MVPP2_CLS_SWFWD_PCTRL_REG, val); +} + +/* Buffer Manager configuration routines */ + +/* Create pool */ +static int mvpp2_bm_pool_create(struct udevice *dev, + struct mvpp2 *priv, + struct mvpp2_bm_pool *bm_pool, int size) +{ + u32 val; + + bm_pool->virt_addr = buffer_loc.bm_pool[bm_pool->id]; + bm_pool->phys_addr = (dma_addr_t)buffer_loc.bm_pool[bm_pool->id]; + if (!bm_pool->virt_addr) + return -ENOMEM; + + if (!IS_ALIGNED((u32)bm_pool->virt_addr, MVPP2_BM_POOL_PTR_ALIGN)) { + dev_err(&pdev->dev, "BM pool %d is not %d bytes aligned\n", + bm_pool->id, MVPP2_BM_POOL_PTR_ALIGN); + return -ENOMEM; + } + + mvpp2_write(priv, MVPP2_BM_POOL_BASE_REG(bm_pool->id), + bm_pool->phys_addr); + mvpp2_write(priv, MVPP2_BM_POOL_SIZE_REG(bm_pool->id), size); + + val = mvpp2_read(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id)); + val |= MVPP2_BM_START_MASK; + mvpp2_write(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id), val); + + bm_pool->type = MVPP2_BM_FREE; + bm_pool->size = size; + bm_pool->pkt_size = 0; + bm_pool->buf_num = 0; + + return 0; +} + +/* Set pool buffer size */ +static void mvpp2_bm_pool_bufsize_set(struct mvpp2 *priv, + struct mvpp2_bm_pool *bm_pool, + int buf_size) +{ + u32 val; + + bm_pool->buf_size = buf_size; + + val = ALIGN(buf_size, 1 << MVPP2_POOL_BUF_SIZE_OFFSET); + mvpp2_write(priv, MVPP2_POOL_BUF_SIZE_REG(bm_pool->id), val); +} + +/* Free all buffers from the pool */ +static void mvpp2_bm_bufs_free(struct udevice *dev, struct mvpp2 *priv, + struct mvpp2_bm_pool *bm_pool) +{ + bm_pool->buf_num = 0; +} + +/* Cleanup pool */ +static int mvpp2_bm_pool_destroy(struct udevice *dev, + struct mvpp2 *priv, + struct mvpp2_bm_pool *bm_pool) +{ + u32 val; + + mvpp2_bm_bufs_free(dev, priv, bm_pool); + if (bm_pool->buf_num) { + dev_err(dev, "cannot free all buffers in pool %d\n", bm_pool->id); + return 0; + } + + val = mvpp2_read(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id)); + val |= MVPP2_BM_STOP_MASK; + mvpp2_write(priv, MVPP2_BM_POOL_CTRL_REG(bm_pool->id), val); + + return 0; +} + +static int mvpp2_bm_pools_init(struct udevice *dev, + struct mvpp2 *priv) +{ + int i, err, size; + struct mvpp2_bm_pool *bm_pool; + + /* Create all pools with maximum size */ + size = MVPP2_BM_POOL_SIZE_MAX; + for (i = 0; i < MVPP2_BM_POOLS_NUM; i++) { + bm_pool = &priv->bm_pools[i]; + bm_pool->id = i; + err = mvpp2_bm_pool_create(dev, priv, bm_pool, size); + if (err) + goto err_unroll_pools; + mvpp2_bm_pool_bufsize_set(priv, bm_pool, 0); + } + return 0; + +err_unroll_pools: + dev_err(&pdev->dev, "failed to create BM pool %d, size %d\n", i, size); + for (i = i - 1; i >= 0; i--) + mvpp2_bm_pool_destroy(dev, priv, &priv->bm_pools[i]); + return err; +} + +static int mvpp2_bm_init(struct udevice *dev, struct mvpp2 *priv) +{ + int i, err; + + for (i = 0; i < MVPP2_BM_POOLS_NUM; i++) { + /* Mask BM all interrupts */ + mvpp2_write(priv, MVPP2_BM_INTR_MASK_REG(i), 0); + /* Clear BM cause register */ + mvpp2_write(priv, MVPP2_BM_INTR_CAUSE_REG(i), 0); + } + + /* Allocate and initialize BM pools */ + priv->bm_pools = devm_kcalloc(dev, MVPP2_BM_POOLS_NUM, + sizeof(struct mvpp2_bm_pool), GFP_KERNEL); + if (!priv->bm_pools) + return -ENOMEM; + + err = mvpp2_bm_pools_init(dev, priv); + if (err < 0) + return err; + return 0; +} + +/* Attach long pool to rxq */ +static void mvpp2_rxq_long_pool_set(struct mvpp2_port *port, + int lrxq, int long_pool) +{ + u32 val; + int prxq; + + /* Get queue physical ID */ + prxq = port->rxqs[lrxq]->id; + + val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(prxq)); + val &= ~MVPP2_RXQ_POOL_LONG_MASK; + val |= ((long_pool << MVPP2_RXQ_POOL_LONG_OFFS) & + MVPP2_RXQ_POOL_LONG_MASK); + + mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(prxq), val); +} + +/* Set pool number in a BM cookie */ +static inline u32 mvpp2_bm_cookie_pool_set(u32 cookie, int pool) +{ + u32 bm; + + bm = cookie & ~(0xFF << MVPP2_BM_COOKIE_POOL_OFFS); + bm |= ((pool & 0xFF) << MVPP2_BM_COOKIE_POOL_OFFS); + + return bm; +} + +/* Get pool number from a BM cookie */ +static inline int mvpp2_bm_cookie_pool_get(u32 cookie) +{ + return (cookie >> MVPP2_BM_COOKIE_POOL_OFFS) & 0xFF; +} + +/* Release buffer to BM */ +static inline void mvpp2_bm_pool_put(struct mvpp2_port *port, int pool, + u32 buf_phys_addr, u32 buf_virt_addr) +{ + mvpp2_write(port->priv, MVPP2_BM_VIRT_RLS_REG, buf_virt_addr); + mvpp2_write(port->priv, MVPP2_BM_PHY_RLS_REG(pool), buf_phys_addr); +} + +/* Refill BM pool */ +static void mvpp2_pool_refill(struct mvpp2_port *port, u32 bm, + u32 phys_addr, u32 cookie) +{ + int pool = mvpp2_bm_cookie_pool_get(bm); + + mvpp2_bm_pool_put(port, pool, phys_addr, cookie); +} + +/* Allocate buffers for the pool */ +static int mvpp2_bm_bufs_add(struct mvpp2_port *port, + struct mvpp2_bm_pool *bm_pool, int buf_num) +{ + int i; + u32 bm; + + if (buf_num < 0 || + (buf_num + bm_pool->buf_num > bm_pool->size)) { + netdev_err(port->dev, + "cannot allocate %d buffers for pool %d\n", + buf_num, bm_pool->id); + return 0; + } + + bm = mvpp2_bm_cookie_pool_set(0, bm_pool->id); + for (i = 0; i < buf_num; i++) { + mvpp2_pool_refill(port, bm, (u32)buffer_loc.rx_buffer[i], + (u32)buffer_loc.rx_buffer[i]); + } + + /* Update BM driver with number of buffers added to pool */ + bm_pool->buf_num += i; + bm_pool->in_use_thresh = bm_pool->buf_num / 4; + + return i; +} + +/* Notify the driver that BM pool is being used as specific type and return the + * pool pointer on success + */ +static struct mvpp2_bm_pool * +mvpp2_bm_pool_use(struct mvpp2_port *port, int pool, enum mvpp2_bm_type type, + int pkt_size) +{ + struct mvpp2_bm_pool *new_pool = &port->priv->bm_pools[pool]; + int num; + + if (new_pool->type != MVPP2_BM_FREE && new_pool->type != type) { + netdev_err(port->dev, "mixing pool types is forbidden\n"); + return NULL; + } + + if (new_pool->type == MVPP2_BM_FREE) + new_pool->type = type; + + /* Allocate buffers in case BM pool is used as long pool, but packet + * size doesn't match MTU or BM pool hasn't being used yet + */ + if (((type == MVPP2_BM_SWF_LONG) && (pkt_size > new_pool->pkt_size)) || + (new_pool->pkt_size == 0)) { + int pkts_num; + + /* Set default buffer number or free all the buffers in case + * the pool is not empty + */ + pkts_num = new_pool->buf_num; + if (pkts_num == 0) + pkts_num = type == MVPP2_BM_SWF_LONG ? + MVPP2_BM_LONG_BUF_NUM : + MVPP2_BM_SHORT_BUF_NUM; + else + mvpp2_bm_bufs_free(NULL, + port->priv, new_pool); + + new_pool->pkt_size = pkt_size; + + /* Allocate buffers for this pool */ + num = mvpp2_bm_bufs_add(port, new_pool, pkts_num); + if (num != pkts_num) { + dev_err(dev, "pool %d: %d of %d allocated\n", + new_pool->id, num, pkts_num); + return NULL; + } + } + + mvpp2_bm_pool_bufsize_set(port->priv, new_pool, + MVPP2_RX_BUF_SIZE(new_pool->pkt_size)); + + return new_pool; +} + +/* Initialize pools for swf */ +static int mvpp2_swf_bm_pool_init(struct mvpp2_port *port) +{ + int rxq; + + if (!port->pool_long) { + port->pool_long = + mvpp2_bm_pool_use(port, MVPP2_BM_SWF_LONG_POOL(port->id), + MVPP2_BM_SWF_LONG, + port->pkt_size); + if (!port->pool_long) + return -ENOMEM; + + port->pool_long->port_map |= (1 << port->id); + + for (rxq = 0; rxq < rxq_number; rxq++) + mvpp2_rxq_long_pool_set(port, rxq, port->pool_long->id); + } + + return 0; +} + +/* Port configuration routines */ + +static void mvpp2_port_mii_set(struct mvpp2_port *port) +{ + u32 val; + + val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); + + switch (port->phy_interface) { + case PHY_INTERFACE_MODE_SGMII: + val |= MVPP2_GMAC_INBAND_AN_MASK; + break; + case PHY_INTERFACE_MODE_RGMII: + val |= MVPP2_GMAC_PORT_RGMII_MASK; + default: + val &= ~MVPP2_GMAC_PCS_ENABLE_MASK; + } + + writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); +} + +static void mvpp2_port_fc_adv_enable(struct mvpp2_port *port) +{ + u32 val; + + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); + val |= MVPP2_GMAC_FC_ADV_EN; + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); +} + +static void mvpp2_port_enable(struct mvpp2_port *port) +{ + u32 val; + + val = readl(port->base + MVPP2_GMAC_CTRL_0_REG); + val |= MVPP2_GMAC_PORT_EN_MASK; + val |= MVPP2_GMAC_MIB_CNTR_EN_MASK; + writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); +} + +static void mvpp2_port_disable(struct mvpp2_port *port) +{ + u32 val; + + val = readl(port->base + MVPP2_GMAC_CTRL_0_REG); + val &= ~(MVPP2_GMAC_PORT_EN_MASK); + writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); +} + +/* Set IEEE 802.3x Flow Control Xon Packet Transmission Mode */ +static void mvpp2_port_periodic_xon_disable(struct mvpp2_port *port) +{ + u32 val; + + val = readl(port->base + MVPP2_GMAC_CTRL_1_REG) & + ~MVPP2_GMAC_PERIODIC_XON_EN_MASK; + writel(val, port->base + MVPP2_GMAC_CTRL_1_REG); +} + +/* Configure loopback port */ +static void mvpp2_port_loopback_set(struct mvpp2_port *port) +{ + u32 val; + + val = readl(port->base + MVPP2_GMAC_CTRL_1_REG); + + if (port->speed == 1000) + val |= MVPP2_GMAC_GMII_LB_EN_MASK; + else + val &= ~MVPP2_GMAC_GMII_LB_EN_MASK; + + if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) + val |= MVPP2_GMAC_PCS_LB_EN_MASK; + else + val &= ~MVPP2_GMAC_PCS_LB_EN_MASK; + + writel(val, port->base + MVPP2_GMAC_CTRL_1_REG); +} + +static void mvpp2_port_reset(struct mvpp2_port *port) +{ + u32 val; + + val = readl(port->base + MVPP2_GMAC_CTRL_2_REG) & + ~MVPP2_GMAC_PORT_RESET_MASK; + writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); + + while (readl(port->base + MVPP2_GMAC_CTRL_2_REG) & + MVPP2_GMAC_PORT_RESET_MASK) + continue; +} + +/* Change maximum receive size of the port */ +static inline void mvpp2_gmac_max_rx_size_set(struct mvpp2_port *port) +{ + u32 val; + + val = readl(port->base + MVPP2_GMAC_CTRL_0_REG); + val &= ~MVPP2_GMAC_MAX_RX_SIZE_MASK; + val |= (((port->pkt_size - MVPP2_MH_SIZE) / 2) << + MVPP2_GMAC_MAX_RX_SIZE_OFFS); + writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); +} + +/* Set defaults to the MVPP2 port */ +static void mvpp2_defaults_set(struct mvpp2_port *port) +{ + int tx_port_num, val, queue, ptxq, lrxq; + + /* Configure port to loopback if needed */ + if (port->flags & MVPP2_F_LOOPBACK) + mvpp2_port_loopback_set(port); + + /* Update TX FIFO MIN Threshold */ + val = readl(port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG); + val &= ~MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK; + /* Min. TX threshold must be less than minimal packet length */ + val |= MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(64 - 4 - 2); + writel(val, port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG); + + /* Disable Legacy WRR, Disable EJP, Release from reset */ + tx_port_num = mvpp2_egress_port(port); + mvpp2_write(port->priv, MVPP2_TXP_SCHED_PORT_INDEX_REG, + tx_port_num); + mvpp2_write(port->priv, MVPP2_TXP_SCHED_CMD_1_REG, 0); + + /* Close bandwidth for all queues */ + for (queue = 0; queue < MVPP2_MAX_TXQ; queue++) { + ptxq = mvpp2_txq_phys(port->id, queue); + mvpp2_write(port->priv, + MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(ptxq), 0); + } + + /* Set refill period to 1 usec, refill tokens + * and bucket size to maximum + */ + mvpp2_write(port->priv, MVPP2_TXP_SCHED_PERIOD_REG, 0xc8); + val = mvpp2_read(port->priv, MVPP2_TXP_SCHED_REFILL_REG); + val &= ~MVPP2_TXP_REFILL_PERIOD_ALL_MASK; + val |= MVPP2_TXP_REFILL_PERIOD_MASK(1); + val |= MVPP2_TXP_REFILL_TOKENS_ALL_MASK; + mvpp2_write(port->priv, MVPP2_TXP_SCHED_REFILL_REG, val); + val = MVPP2_TXP_TOKEN_SIZE_MAX; + mvpp2_write(port->priv, MVPP2_TXP_SCHED_TOKEN_SIZE_REG, val); + + /* Set MaximumLowLatencyPacketSize value to 256 */ + mvpp2_write(port->priv, MVPP2_RX_CTRL_REG(port->id), + MVPP2_RX_USE_PSEUDO_FOR_CSUM_MASK | + MVPP2_RX_LOW_LATENCY_PKT_SIZE(256)); + + /* Enable Rx cache snoop */ + for (lrxq = 0; lrxq < rxq_number; lrxq++) { + queue = port->rxqs[lrxq]->id; + val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue)); + val |= MVPP2_SNOOP_PKT_SIZE_MASK | + MVPP2_SNOOP_BUF_HDR_MASK; + mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(queue), val); + } +} + +/* Enable/disable receiving packets */ +static void mvpp2_ingress_enable(struct mvpp2_port *port) +{ + u32 val; + int lrxq, queue; + + for (lrxq = 0; lrxq < rxq_number; lrxq++) { + queue = port->rxqs[lrxq]->id; + val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue)); + val &= ~MVPP2_RXQ_DISABLE_MASK; + mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(queue), val); + } +} + +static void mvpp2_ingress_disable(struct mvpp2_port *port) +{ + u32 val; + int lrxq, queue; + + for (lrxq = 0; lrxq < rxq_number; lrxq++) { + queue = port->rxqs[lrxq]->id; + val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue)); + val |= MVPP2_RXQ_DISABLE_MASK; + mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(queue), val); + } +} + +/* Enable transmit via physical egress queue + * - HW starts take descriptors from DRAM + */ +static void mvpp2_egress_enable(struct mvpp2_port *port) +{ + u32 qmap; + int queue; + int tx_port_num = mvpp2_egress_port(port); + + /* Enable all initialized TXs. */ + qmap = 0; + for (queue = 0; queue < txq_number; queue++) { + struct mvpp2_tx_queue *txq = port->txqs[queue]; + + if (txq->descs != NULL) + qmap |= (1 << queue); + } + + mvpp2_write(port->priv, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num); + mvpp2_write(port->priv, MVPP2_TXP_SCHED_Q_CMD_REG, qmap); +} + +/* Disable transmit via physical egress queue + * - HW doesn't take descriptors from DRAM + */ +static void mvpp2_egress_disable(struct mvpp2_port *port) +{ + u32 reg_data; + int delay; + int tx_port_num = mvpp2_egress_port(port); + + /* Issue stop command for active channels only */ + mvpp2_write(port->priv, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num); + reg_data = (mvpp2_read(port->priv, MVPP2_TXP_SCHED_Q_CMD_REG)) & + MVPP2_TXP_SCHED_ENQ_MASK; + if (reg_data != 0) + mvpp2_write(port->priv, MVPP2_TXP_SCHED_Q_CMD_REG, + (reg_data << MVPP2_TXP_SCHED_DISQ_OFFSET)); + + /* Wait for all Tx activity to terminate. */ + delay = 0; + do { + if (delay >= MVPP2_TX_DISABLE_TIMEOUT_MSEC) { + netdev_warn(port->dev, + "Tx stop timed out, status=0x%08x\n", + reg_data); + break; + } + mdelay(1); + delay++; + + /* Check port TX Command register that all + * Tx queues are stopped + */ + reg_data = mvpp2_read(port->priv, MVPP2_TXP_SCHED_Q_CMD_REG); + } while (reg_data & MVPP2_TXP_SCHED_ENQ_MASK); +} + +/* Rx descriptors helper methods */ + +/* Get number of Rx descriptors occupied by received packets */ +static inline int +mvpp2_rxq_received(struct mvpp2_port *port, int rxq_id) +{ + u32 val = mvpp2_read(port->priv, MVPP2_RXQ_STATUS_REG(rxq_id)); + + return val & MVPP2_RXQ_OCCUPIED_MASK; +} + +/* Update Rx queue status with the number of occupied and available + * Rx descriptor slots. + */ +static inline void +mvpp2_rxq_status_update(struct mvpp2_port *port, int rxq_id, + int used_count, int free_count) +{ + /* Decrement the number of used descriptors and increment count + * increment the number of free descriptors. + */ + u32 val = used_count | (free_count << MVPP2_RXQ_NUM_NEW_OFFSET); + + mvpp2_write(port->priv, MVPP2_RXQ_STATUS_UPDATE_REG(rxq_id), val); +} + +/* Get pointer to next RX descriptor to be processed by SW */ +static inline struct mvpp2_rx_desc * +mvpp2_rxq_next_desc_get(struct mvpp2_rx_queue *rxq) +{ + int rx_desc = rxq->next_desc_to_proc; + + rxq->next_desc_to_proc = MVPP2_QUEUE_NEXT_DESC(rxq, rx_desc); + prefetch(rxq->descs + rxq->next_desc_to_proc); + return rxq->descs + rx_desc; +} + +/* Set rx queue offset */ +static void mvpp2_rxq_offset_set(struct mvpp2_port *port, + int prxq, int offset) +{ + u32 val; + + /* Convert offset from bytes to units of 32 bytes */ + offset = offset >> 5; + + val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(prxq)); + val &= ~MVPP2_RXQ_PACKET_OFFSET_MASK; + + /* Offset is in */ + val |= ((offset << MVPP2_RXQ_PACKET_OFFSET_OFFS) & + MVPP2_RXQ_PACKET_OFFSET_MASK); + + mvpp2_write(port->priv, MVPP2_RXQ_CONFIG_REG(prxq), val); +} + +/* Obtain BM cookie information from descriptor */ +static u32 mvpp2_bm_cookie_build(struct mvpp2_rx_desc *rx_desc) +{ + int pool = (rx_desc->status & MVPP2_RXD_BM_POOL_ID_MASK) >> + MVPP2_RXD_BM_POOL_ID_OFFS; + int cpu = smp_processor_id(); + + return ((pool & 0xFF) << MVPP2_BM_COOKIE_POOL_OFFS) | + ((cpu & 0xFF) << MVPP2_BM_COOKIE_CPU_OFFS); +} + +/* Tx descriptors helper methods */ + +/* Get number of Tx descriptors waiting to be transmitted by HW */ +static int mvpp2_txq_pend_desc_num_get(struct mvpp2_port *port, + struct mvpp2_tx_queue *txq) +{ + u32 val; + + mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); + val = mvpp2_read(port->priv, MVPP2_TXQ_PENDING_REG); + + return val & MVPP2_TXQ_PENDING_MASK; +} + +/* Get pointer to next Tx descriptor to be processed (send) by HW */ +static struct mvpp2_tx_desc * +mvpp2_txq_next_desc_get(struct mvpp2_tx_queue *txq) +{ + int tx_desc = txq->next_desc_to_proc; + + txq->next_desc_to_proc = MVPP2_QUEUE_NEXT_DESC(txq, tx_desc); + return txq->descs + tx_desc; +} + +/* Update HW with number of aggregated Tx descriptors to be sent */ +static void mvpp2_aggr_txq_pend_desc_add(struct mvpp2_port *port, int pending) +{ + /* aggregated access - relevant TXQ number is written in TX desc */ + mvpp2_write(port->priv, MVPP2_AGGR_TXQ_UPDATE_REG, pending); +} + +/* Get number of sent descriptors and decrement counter. + * The number of sent descriptors is returned. + * Per-CPU access + */ +static inline int mvpp2_txq_sent_desc_proc(struct mvpp2_port *port, + struct mvpp2_tx_queue *txq) +{ + u32 val; + + /* Reading status reg resets transmitted descriptor counter */ + val = mvpp2_read(port->priv, MVPP2_TXQ_SENT_REG(txq->id)); + + return (val & MVPP2_TRANSMITTED_COUNT_MASK) >> + MVPP2_TRANSMITTED_COUNT_OFFSET; +} + +static void mvpp2_txq_sent_counter_clear(void *arg) +{ + struct mvpp2_port *port = arg; + int queue; + + for (queue = 0; queue < txq_number; queue++) { + int id = port->txqs[queue]->id; + + mvpp2_read(port->priv, MVPP2_TXQ_SENT_REG(id)); + } +} + +/* Set max sizes for Tx queues */ +static void mvpp2_txp_max_tx_size_set(struct mvpp2_port *port) +{ + u32 val, size, mtu; + int txq, tx_port_num; + + mtu = port->pkt_size * 8; + if (mtu > MVPP2_TXP_MTU_MAX) + mtu = MVPP2_TXP_MTU_MAX; + + /* WA for wrong Token bucket update: Set MTU value = 3*real MTU value */ + mtu = 3 * mtu; + + /* Indirect access to registers */ + tx_port_num = mvpp2_egress_port(port); + mvpp2_write(port->priv, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num); + + /* Set MTU */ + val = mvpp2_read(port->priv, MVPP2_TXP_SCHED_MTU_REG); + val &= ~MVPP2_TXP_MTU_MAX; + val |= mtu; + mvpp2_write(port->priv, MVPP2_TXP_SCHED_MTU_REG, val); + + /* TXP token size and all TXQs token size must be larger that MTU */ + val = mvpp2_read(port->priv, MVPP2_TXP_SCHED_TOKEN_SIZE_REG); + size = val & MVPP2_TXP_TOKEN_SIZE_MAX; + if (size < mtu) { + size = mtu; + val &= ~MVPP2_TXP_TOKEN_SIZE_MAX; + val |= size; + mvpp2_write(port->priv, MVPP2_TXP_SCHED_TOKEN_SIZE_REG, val); + } + + for (txq = 0; txq < txq_number; txq++) { + val = mvpp2_read(port->priv, + MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq)); + size = val & MVPP2_TXQ_TOKEN_SIZE_MAX; + + if (size < mtu) { + size = mtu; + val &= ~MVPP2_TXQ_TOKEN_SIZE_MAX; + val |= size; + mvpp2_write(port->priv, + MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq), + val); + } + } +} + +/* Free Tx queue skbuffs */ +static void mvpp2_txq_bufs_free(struct mvpp2_port *port, + struct mvpp2_tx_queue *txq, + struct mvpp2_txq_pcpu *txq_pcpu, int num) +{ + int i; + + for (i = 0; i < num; i++) + mvpp2_txq_inc_get(txq_pcpu); +} + +static inline struct mvpp2_rx_queue *mvpp2_get_rx_queue(struct mvpp2_port *port, + u32 cause) +{ + int queue = fls(cause) - 1; + + return port->rxqs[queue]; +} + +static inline struct mvpp2_tx_queue *mvpp2_get_tx_queue(struct mvpp2_port *port, + u32 cause) +{ + int queue = fls(cause) - 1; + + return port->txqs[queue]; +} + +/* Rx/Tx queue initialization/cleanup methods */ + +/* Allocate and initialize descriptors for aggr TXQ */ +static int mvpp2_aggr_txq_init(struct udevice *dev, + struct mvpp2_tx_queue *aggr_txq, + int desc_num, int cpu, + struct mvpp2 *priv) +{ + /* Allocate memory for TX descriptors */ + aggr_txq->descs = buffer_loc.aggr_tx_descs; + aggr_txq->descs_phys = (dma_addr_t)buffer_loc.aggr_tx_descs; + if (!aggr_txq->descs) + return -ENOMEM; + + /* Make sure descriptor address is cache line size aligned */ + BUG_ON(aggr_txq->descs != + PTR_ALIGN(aggr_txq->descs, MVPP2_CPU_D_CACHE_LINE_SIZE)); + + aggr_txq->last_desc = aggr_txq->size - 1; + + /* Aggr TXQ no reset WA */ + aggr_txq->next_desc_to_proc = mvpp2_read(priv, + MVPP2_AGGR_TXQ_INDEX_REG(cpu)); + + /* Set Tx descriptors queue starting address */ + /* indirect access */ + mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_ADDR_REG(cpu), + aggr_txq->descs_phys); + mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_SIZE_REG(cpu), desc_num); + + return 0; +} + +/* Create a specified Rx queue */ +static int mvpp2_rxq_init(struct mvpp2_port *port, + struct mvpp2_rx_queue *rxq) + +{ + rxq->size = port->rx_ring_size; + + /* Allocate memory for RX descriptors */ + rxq->descs = buffer_loc.rx_descs; + rxq->descs_phys = (dma_addr_t)buffer_loc.rx_descs; + if (!rxq->descs) + return -ENOMEM; + + BUG_ON(rxq->descs != + PTR_ALIGN(rxq->descs, MVPP2_CPU_D_CACHE_LINE_SIZE)); + + rxq->last_desc = rxq->size - 1; + + /* Zero occupied and non-occupied counters - direct access */ + mvpp2_write(port->priv, MVPP2_RXQ_STATUS_REG(rxq->id), 0); + + /* Set Rx descriptors queue starting address - indirect access */ + mvpp2_write(port->priv, MVPP2_RXQ_NUM_REG, rxq->id); + mvpp2_write(port->priv, MVPP2_RXQ_DESC_ADDR_REG, rxq->descs_phys); + mvpp2_write(port->priv, MVPP2_RXQ_DESC_SIZE_REG, rxq->size); + mvpp2_write(port->priv, MVPP2_RXQ_INDEX_REG, 0); + + /* Set Offset */ + mvpp2_rxq_offset_set(port, rxq->id, NET_SKB_PAD); + + /* Add number of descriptors ready for receiving packets */ + mvpp2_rxq_status_update(port, rxq->id, 0, rxq->size); + + return 0; +} + +/* Push packets received by the RXQ to BM pool */ +static void mvpp2_rxq_drop_pkts(struct mvpp2_port *port, + struct mvpp2_rx_queue *rxq) +{ + int rx_received, i; + + rx_received = mvpp2_rxq_received(port, rxq->id); + if (!rx_received) + return; + + for (i = 0; i < rx_received; i++) { + struct mvpp2_rx_desc *rx_desc = mvpp2_rxq_next_desc_get(rxq); + u32 bm = mvpp2_bm_cookie_build(rx_desc); + + mvpp2_pool_refill(port, bm, rx_desc->buf_phys_addr, + rx_desc->buf_cookie); + } + mvpp2_rxq_status_update(port, rxq->id, rx_received, rx_received); +} + +/* Cleanup Rx queue */ +static void mvpp2_rxq_deinit(struct mvpp2_port *port, + struct mvpp2_rx_queue *rxq) +{ + mvpp2_rxq_drop_pkts(port, rxq); + + rxq->descs = NULL; + rxq->last_desc = 0; + rxq->next_desc_to_proc = 0; + rxq->descs_phys = 0; + + /* Clear Rx descriptors queue starting address and size; + * free descriptor number + */ + mvpp2_write(port->priv, MVPP2_RXQ_STATUS_REG(rxq->id), 0); + mvpp2_write(port->priv, MVPP2_RXQ_NUM_REG, rxq->id); + mvpp2_write(port->priv, MVPP2_RXQ_DESC_ADDR_REG, 0); + mvpp2_write(port->priv, MVPP2_RXQ_DESC_SIZE_REG, 0); +} + +/* Create and initialize a Tx queue */ +static int mvpp2_txq_init(struct mvpp2_port *port, + struct mvpp2_tx_queue *txq) +{ + u32 val; + int cpu, desc, desc_per_txq, tx_port_num; + struct mvpp2_txq_pcpu *txq_pcpu; + + txq->size = port->tx_ring_size; + + /* Allocate memory for Tx descriptors */ + txq->descs = buffer_loc.tx_descs; + txq->descs_phys = (dma_addr_t)buffer_loc.tx_descs; + if (!txq->descs) + return -ENOMEM; + + /* Make sure descriptor address is cache line size aligned */ + BUG_ON(txq->descs != + PTR_ALIGN(txq->descs, MVPP2_CPU_D_CACHE_LINE_SIZE)); + + txq->last_desc = txq->size - 1; + + /* Set Tx descriptors queue starting address - indirect access */ + mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); + mvpp2_write(port->priv, MVPP2_TXQ_DESC_ADDR_REG, txq->descs_phys); + mvpp2_write(port->priv, MVPP2_TXQ_DESC_SIZE_REG, txq->size & + MVPP2_TXQ_DESC_SIZE_MASK); + mvpp2_write(port->priv, MVPP2_TXQ_INDEX_REG, 0); + mvpp2_write(port->priv, MVPP2_TXQ_RSVD_CLR_REG, + txq->id << MVPP2_TXQ_RSVD_CLR_OFFSET); + val = mvpp2_read(port->priv, MVPP2_TXQ_PENDING_REG); + val &= ~MVPP2_TXQ_PENDING_MASK; + mvpp2_write(port->priv, MVPP2_TXQ_PENDING_REG, val); + + /* Calculate base address in prefetch buffer. We reserve 16 descriptors + * for each existing TXQ. + * TCONTS for PON port must be continuous from 0 to MVPP2_MAX_TCONT + * GBE ports assumed to be continious from 0 to MVPP2_MAX_PORTS + */ + desc_per_txq = 16; + desc = (port->id * MVPP2_MAX_TXQ * desc_per_txq) + + (txq->log_id * desc_per_txq); + + mvpp2_write(port->priv, MVPP2_TXQ_PREF_BUF_REG, + MVPP2_PREF_BUF_PTR(desc) | MVPP2_PREF_BUF_SIZE_16 | + MVPP2_PREF_BUF_THRESH(desc_per_txq/2)); + + /* WRR / EJP configuration - indirect access */ + tx_port_num = mvpp2_egress_port(port); + mvpp2_write(port->priv, MVPP2_TXP_SCHED_PORT_INDEX_REG, tx_port_num); + + val = mvpp2_read(port->priv, MVPP2_TXQ_SCHED_REFILL_REG(txq->log_id)); + val &= ~MVPP2_TXQ_REFILL_PERIOD_ALL_MASK; + val |= MVPP2_TXQ_REFILL_PERIOD_MASK(1); + val |= MVPP2_TXQ_REFILL_TOKENS_ALL_MASK; + mvpp2_write(port->priv, MVPP2_TXQ_SCHED_REFILL_REG(txq->log_id), val); + + val = MVPP2_TXQ_TOKEN_SIZE_MAX; + mvpp2_write(port->priv, MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq->log_id), + val); + + for_each_present_cpu(cpu) { + txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); + txq_pcpu->size = txq->size; + } + + return 0; +} + +/* Free allocated TXQ resources */ +static void mvpp2_txq_deinit(struct mvpp2_port *port, + struct mvpp2_tx_queue *txq) +{ + txq->descs = NULL; + txq->last_desc = 0; + txq->next_desc_to_proc = 0; + txq->descs_phys = 0; + + /* Set minimum bandwidth for disabled TXQs */ + mvpp2_write(port->priv, MVPP2_TXQ_SCHED_TOKEN_CNTR_REG(txq->id), 0); + + /* Set Tx descriptors queue starting address and size */ + mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); + mvpp2_write(port->priv, MVPP2_TXQ_DESC_ADDR_REG, 0); + mvpp2_write(port->priv, MVPP2_TXQ_DESC_SIZE_REG, 0); +} + +/* Cleanup Tx ports */ +static void mvpp2_txq_clean(struct mvpp2_port *port, struct mvpp2_tx_queue *txq) +{ + struct mvpp2_txq_pcpu *txq_pcpu; + int delay, pending, cpu; + u32 val; + + mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); + val = mvpp2_read(port->priv, MVPP2_TXQ_PREF_BUF_REG); + val |= MVPP2_TXQ_DRAIN_EN_MASK; + mvpp2_write(port->priv, MVPP2_TXQ_PREF_BUF_REG, val); + + /* The napi queue has been stopped so wait for all packets + * to be transmitted. + */ + delay = 0; + do { + if (delay >= MVPP2_TX_PENDING_TIMEOUT_MSEC) { + netdev_warn(port->dev, + "port %d: cleaning queue %d timed out\n", + port->id, txq->log_id); + break; + } + mdelay(1); + delay++; + + pending = mvpp2_txq_pend_desc_num_get(port, txq); + } while (pending); + + val &= ~MVPP2_TXQ_DRAIN_EN_MASK; + mvpp2_write(port->priv, MVPP2_TXQ_PREF_BUF_REG, val); + + for_each_present_cpu(cpu) { + txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); + + /* Release all packets */ + mvpp2_txq_bufs_free(port, txq, txq_pcpu, txq_pcpu->count); + + /* Reset queue */ + txq_pcpu->count = 0; + txq_pcpu->txq_put_index = 0; + txq_pcpu->txq_get_index = 0; + } +} + +/* Cleanup all Tx queues */ +static void mvpp2_cleanup_txqs(struct mvpp2_port *port) +{ + struct mvpp2_tx_queue *txq; + int queue; + u32 val; + + val = mvpp2_read(port->priv, MVPP2_TX_PORT_FLUSH_REG); + + /* Reset Tx ports and delete Tx queues */ + val |= MVPP2_TX_PORT_FLUSH_MASK(port->id); + mvpp2_write(port->priv, MVPP2_TX_PORT_FLUSH_REG, val); + + for (queue = 0; queue < txq_number; queue++) { + txq = port->txqs[queue]; + mvpp2_txq_clean(port, txq); + mvpp2_txq_deinit(port, txq); + } + + mvpp2_txq_sent_counter_clear(port); + + val &= ~MVPP2_TX_PORT_FLUSH_MASK(port->id); + mvpp2_write(port->priv, MVPP2_TX_PORT_FLUSH_REG, val); +} + +/* Cleanup all Rx queues */ +static void mvpp2_cleanup_rxqs(struct mvpp2_port *port) +{ + int queue; + + for (queue = 0; queue < rxq_number; queue++) + mvpp2_rxq_deinit(port, port->rxqs[queue]); +} + +/* Init all Rx queues for port */ +static int mvpp2_setup_rxqs(struct mvpp2_port *port) +{ + int queue, err; + + for (queue = 0; queue < rxq_number; queue++) { + err = mvpp2_rxq_init(port, port->rxqs[queue]); + if (err) + goto err_cleanup; + } + return 0; + +err_cleanup: + mvpp2_cleanup_rxqs(port); + return err; +} + +/* Init all tx queues for port */ +static int mvpp2_setup_txqs(struct mvpp2_port *port) +{ + struct mvpp2_tx_queue *txq; + int queue, err; + + for (queue = 0; queue < txq_number; queue++) { + txq = port->txqs[queue]; + err = mvpp2_txq_init(port, txq); + if (err) + goto err_cleanup; + } + + mvpp2_txq_sent_counter_clear(port); + return 0; + +err_cleanup: + mvpp2_cleanup_txqs(port); + return err; +} + +/* Adjust link */ +static void mvpp2_link_event(struct mvpp2_port *port) +{ + struct phy_device *phydev = port->phy_dev; + int status_change = 0; + u32 val; + + if (phydev->link) { + if ((port->speed != phydev->speed) || + (port->duplex != phydev->duplex)) { + u32 val; + + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); + val &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | + MVPP2_GMAC_CONFIG_GMII_SPEED | + MVPP2_GMAC_CONFIG_FULL_DUPLEX | + MVPP2_GMAC_AN_SPEED_EN | + MVPP2_GMAC_AN_DUPLEX_EN); + + if (phydev->duplex) + val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; + + if (phydev->speed == SPEED_1000) + val |= MVPP2_GMAC_CONFIG_GMII_SPEED; + else if (phydev->speed == SPEED_100) + val |= MVPP2_GMAC_CONFIG_MII_SPEED; + + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); + + port->duplex = phydev->duplex; + port->speed = phydev->speed; + } + } + + if (phydev->link != port->link) { + if (!phydev->link) { + port->duplex = -1; + port->speed = 0; + } + + port->link = phydev->link; + status_change = 1; + } + + if (status_change) { + if (phydev->link) { + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); + val |= (MVPP2_GMAC_FORCE_LINK_PASS | + MVPP2_GMAC_FORCE_LINK_DOWN); + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); + mvpp2_egress_enable(port); + mvpp2_ingress_enable(port); + } else { + mvpp2_ingress_disable(port); + mvpp2_egress_disable(port); + } + } +} + +/* Main RX/TX processing routines */ + +/* Display more error info */ +static void mvpp2_rx_error(struct mvpp2_port *port, + struct mvpp2_rx_desc *rx_desc) +{ + u32 status = rx_desc->status; + + switch (status & MVPP2_RXD_ERR_CODE_MASK) { + case MVPP2_RXD_ERR_CRC: + netdev_err(port->dev, "bad rx status %08x (crc error), size=%d\n", + status, rx_desc->data_size); + break; + case MVPP2_RXD_ERR_OVERRUN: + netdev_err(port->dev, "bad rx status %08x (overrun error), size=%d\n", + status, rx_desc->data_size); + break; + case MVPP2_RXD_ERR_RESOURCE: + netdev_err(port->dev, "bad rx status %08x (resource error), size=%d\n", + status, rx_desc->data_size); + break; + } +} + +/* Reuse skb if possible, or allocate a new skb and add it to BM pool */ +static int mvpp2_rx_refill(struct mvpp2_port *port, + struct mvpp2_bm_pool *bm_pool, + u32 bm, u32 phys_addr) +{ + mvpp2_pool_refill(port, bm, phys_addr, phys_addr); + return 0; +} + +/* Set hw internals when starting port */ +static void mvpp2_start_dev(struct mvpp2_port *port) +{ + mvpp2_gmac_max_rx_size_set(port); + mvpp2_txp_max_tx_size_set(port); + + mvpp2_port_enable(port); +} + +/* Set hw internals when stopping port */ +static void mvpp2_stop_dev(struct mvpp2_port *port) +{ + /* Stop new packets from arriving to RXQs */ + mvpp2_ingress_disable(port); + + mvpp2_egress_disable(port); + mvpp2_port_disable(port); +} + +static int mvpp2_phy_connect(struct udevice *dev, struct mvpp2_port *port) +{ + struct phy_device *phy_dev; + + if (!port->init || port->link == 0) { + phy_dev = phy_connect(port->priv->bus, port->phyaddr, dev, + port->phy_interface); + port->phy_dev = phy_dev; + if (!phy_dev) { + netdev_err(port->dev, "cannot connect to phy\n"); + return -ENODEV; + } + phy_dev->supported &= PHY_GBIT_FEATURES; + phy_dev->advertising = phy_dev->supported; + + port->phy_dev = phy_dev; + port->link = 0; + port->duplex = 0; + port->speed = 0; + + phy_config(phy_dev); + phy_startup(phy_dev); + if (!phy_dev->link) { + printf("%s: No link\n", phy_dev->dev->name); + return -1; + } + + port->init = 1; + } else { + mvpp2_egress_enable(port); + mvpp2_ingress_enable(port); + } + + return 0; +} + +static int mvpp2_open(struct udevice *dev, struct mvpp2_port *port) +{ + unsigned char mac_bcast[ETH_ALEN] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + int err; + + err = mvpp2_prs_mac_da_accept(port->priv, port->id, mac_bcast, true); + if (err) { + netdev_err(dev, "mvpp2_prs_mac_da_accept BC failed\n"); + return err; + } + err = mvpp2_prs_mac_da_accept(port->priv, port->id, + port->dev_addr, true); + if (err) { + netdev_err(dev, "mvpp2_prs_mac_da_accept MC failed\n"); + return err; + } + err = mvpp2_prs_def_flow(port); + if (err) { + netdev_err(dev, "mvpp2_prs_def_flow failed\n"); + return err; + } + + /* Allocate the Rx/Tx queues */ + err = mvpp2_setup_rxqs(port); + if (err) { + netdev_err(port->dev, "cannot allocate Rx queues\n"); + return err; + } + + err = mvpp2_setup_txqs(port); + if (err) { + netdev_err(port->dev, "cannot allocate Tx queues\n"); + return err; + } + + err = mvpp2_phy_connect(dev, port); + if (err < 0) + return err; + + mvpp2_link_event(port); + + mvpp2_start_dev(port); + + return 0; +} + +/* No Device ops here in U-Boot */ + +/* Driver initialization */ + +static void mvpp2_port_power_up(struct mvpp2_port *port) +{ + mvpp2_port_mii_set(port); + mvpp2_port_periodic_xon_disable(port); + mvpp2_port_fc_adv_enable(port); + mvpp2_port_reset(port); +} + +/* Initialize port HW */ +static int mvpp2_port_init(struct udevice *dev, struct mvpp2_port *port) +{ + struct mvpp2 *priv = port->priv; + struct mvpp2_txq_pcpu *txq_pcpu; + int queue, cpu, err; + + if (port->first_rxq + rxq_number > MVPP2_RXQ_TOTAL_NUM) + return -EINVAL; + + /* Disable port */ + mvpp2_egress_disable(port); + mvpp2_port_disable(port); + + port->txqs = devm_kcalloc(dev, txq_number, sizeof(*port->txqs), + GFP_KERNEL); + if (!port->txqs) + return -ENOMEM; + + /* Associate physical Tx queues to this port and initialize. + * The mapping is predefined. + */ + for (queue = 0; queue < txq_number; queue++) { + int queue_phy_id = mvpp2_txq_phys(port->id, queue); + struct mvpp2_tx_queue *txq; + + txq = devm_kzalloc(dev, sizeof(*txq), GFP_KERNEL); + if (!txq) + return -ENOMEM; + + txq->pcpu = devm_kzalloc(dev, sizeof(struct mvpp2_txq_pcpu), + GFP_KERNEL); + if (!txq->pcpu) + return -ENOMEM; + + txq->id = queue_phy_id; + txq->log_id = queue; + txq->done_pkts_coal = MVPP2_TXDONE_COAL_PKTS_THRESH; + for_each_present_cpu(cpu) { + txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); + txq_pcpu->cpu = cpu; + } + + port->txqs[queue] = txq; + } + + port->rxqs = devm_kcalloc(dev, rxq_number, sizeof(*port->rxqs), + GFP_KERNEL); + if (!port->rxqs) + return -ENOMEM; + + /* Allocate and initialize Rx queue for this port */ + for (queue = 0; queue < rxq_number; queue++) { + struct mvpp2_rx_queue *rxq; + + /* Map physical Rx queue to port's logical Rx queue */ + rxq = devm_kzalloc(dev, sizeof(*rxq), GFP_KERNEL); + if (!rxq) + return -ENOMEM; + /* Map this Rx queue to a physical queue */ + rxq->id = port->first_rxq + queue; + rxq->port = port->id; + rxq->logic_rxq = queue; + + port->rxqs[queue] = rxq; + } + + /* Configure Rx queue group interrupt for this port */ + mvpp2_write(priv, MVPP2_ISR_RXQ_GROUP_REG(port->id), CONFIG_MV_ETH_RXQ); + + /* Create Rx descriptor rings */ + for (queue = 0; queue < rxq_number; queue++) { + struct mvpp2_rx_queue *rxq = port->rxqs[queue]; + + rxq->size = port->rx_ring_size; + rxq->pkts_coal = MVPP2_RX_COAL_PKTS; + rxq->time_coal = MVPP2_RX_COAL_USEC; + } + + mvpp2_ingress_disable(port); + + /* Port default configuration */ + mvpp2_defaults_set(port); + + /* Port's classifier configuration */ + mvpp2_cls_oversize_rxq_set(port); + mvpp2_cls_port_config(port); + + /* Provide an initial Rx packet size */ + port->pkt_size = MVPP2_RX_PKT_SIZE(PKTSIZE_ALIGN); + + /* Initialize pools for swf */ + err = mvpp2_swf_bm_pool_init(port); + if (err) + return err; + + return 0; +} + +/* Ports initialization */ +static int mvpp2_port_probe(struct udevice *dev, + struct mvpp2_port *port, + int port_node, + struct mvpp2 *priv, + int *next_first_rxq) +{ + int phy_node; + u32 id; + u32 phyaddr; + const char *phy_mode_str; + int phy_mode = -1; + int priv_common_regs_num = 2; + int err; + + phy_node = fdtdec_lookup_phandle(gd->fdt_blob, port_node, "phy"); + if (phy_node < 0) { + dev_err(&pdev->dev, "missing phy\n"); + return -ENODEV; + } + + phy_mode_str = fdt_getprop(gd->fdt_blob, port_node, "phy-mode", NULL); + if (phy_mode_str) + phy_mode = phy_get_interface_by_name(phy_mode_str); + if (phy_mode == -1) { + dev_err(&pdev->dev, "incorrect phy mode\n"); + return -EINVAL; + } + + id = fdtdec_get_int(gd->fdt_blob, port_node, "port-id", -1); + if (id == -1) { + dev_err(&pdev->dev, "missing port-id value\n"); + return -EINVAL; + } + + phyaddr = fdtdec_get_int(gd->fdt_blob, phy_node, "reg", 0); + + port->priv = priv; + port->id = id; + port->first_rxq = *next_first_rxq; + port->phy_node = phy_node; + port->phy_interface = phy_mode; + port->phyaddr = phyaddr; + + port->base = (void __iomem *)dev_get_addr_index(dev->parent, + priv_common_regs_num + + id); + if (IS_ERR(port->base)) + return PTR_ERR(port->base); + + port->tx_ring_size = MVPP2_MAX_TXD; + port->rx_ring_size = MVPP2_MAX_RXD; + + err = mvpp2_port_init(dev, port); + if (err < 0) { + dev_err(&pdev->dev, "failed to init port %d\n", id); + return err; + } + mvpp2_port_power_up(port); + + /* Increment the first Rx queue number to be used by the next port */ + *next_first_rxq += CONFIG_MV_ETH_RXQ; + priv->port_list[id] = port; + return 0; +} + +/* Initialize decoding windows */ +static void mvpp2_conf_mbus_windows(const struct mbus_dram_target_info *dram, + struct mvpp2 *priv) +{ + u32 win_enable; + int i; + + for (i = 0; i < 6; i++) { + mvpp2_write(priv, MVPP2_WIN_BASE(i), 0); + mvpp2_write(priv, MVPP2_WIN_SIZE(i), 0); + + if (i < 4) + mvpp2_write(priv, MVPP2_WIN_REMAP(i), 0); + } + + win_enable = 0; + + for (i = 0; i < dram->num_cs; i++) { + const struct mbus_dram_window *cs = dram->cs + i; + + mvpp2_write(priv, MVPP2_WIN_BASE(i), + (cs->base & 0xffff0000) | (cs->mbus_attr << 8) | + dram->mbus_dram_target_id); + + mvpp2_write(priv, MVPP2_WIN_SIZE(i), + (cs->size - 1) & 0xffff0000); + + win_enable |= (1 << i); + } + + mvpp2_write(priv, MVPP2_BASE_ADDR_ENABLE, win_enable); +} + +/* Initialize Rx FIFO's */ +static void mvpp2_rx_fifo_init(struct mvpp2 *priv) +{ + int port; + + for (port = 0; port < MVPP2_MAX_PORTS; port++) { + mvpp2_write(priv, MVPP2_RX_DATA_FIFO_SIZE_REG(port), + MVPP2_RX_FIFO_PORT_DATA_SIZE); + mvpp2_write(priv, MVPP2_RX_ATTR_FIFO_SIZE_REG(port), + MVPP2_RX_FIFO_PORT_ATTR_SIZE); + } + + mvpp2_write(priv, MVPP2_RX_MIN_PKT_SIZE_REG, + MVPP2_RX_FIFO_PORT_MIN_PKT); + mvpp2_write(priv, MVPP2_RX_FIFO_INIT_REG, 0x1); +} + +/* Initialize network controller common part HW */ +static int mvpp2_init(struct udevice *dev, struct mvpp2 *priv) +{ + const struct mbus_dram_target_info *dram_target_info; + int err, i; + u32 val; + + /* Checks for hardware constraints (U-Boot uses only one rxq) */ + if ((rxq_number > MVPP2_MAX_RXQ) || (txq_number > MVPP2_MAX_TXQ)) { + dev_err(&pdev->dev, "invalid queue size parameter\n"); + return -EINVAL; + } + + /* MBUS windows configuration */ + dram_target_info = mvebu_mbus_dram_info(); + if (dram_target_info) + mvpp2_conf_mbus_windows(dram_target_info, priv); + + /* Disable HW PHY polling */ + val = readl(priv->lms_base + MVPP2_PHY_AN_CFG0_REG); + val |= MVPP2_PHY_AN_STOP_SMI0_MASK; + writel(val, priv->lms_base + MVPP2_PHY_AN_CFG0_REG); + + /* Allocate and initialize aggregated TXQs */ + priv->aggr_txqs = devm_kcalloc(dev, num_present_cpus(), + sizeof(struct mvpp2_tx_queue), + GFP_KERNEL); + if (!priv->aggr_txqs) + return -ENOMEM; + + for_each_present_cpu(i) { + priv->aggr_txqs[i].id = i; + priv->aggr_txqs[i].size = MVPP2_AGGR_TXQ_SIZE; + err = mvpp2_aggr_txq_init(dev, &priv->aggr_txqs[i], + MVPP2_AGGR_TXQ_SIZE, i, priv); + if (err < 0) + return err; + } + + /* Rx Fifo Init */ + mvpp2_rx_fifo_init(priv); + + /* Reset Rx queue group interrupt configuration */ + for (i = 0; i < MVPP2_MAX_PORTS; i++) + mvpp2_write(priv, MVPP2_ISR_RXQ_GROUP_REG(i), + CONFIG_MV_ETH_RXQ); + + writel(MVPP2_EXT_GLOBAL_CTRL_DEFAULT, + priv->lms_base + MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG); + + /* Allow cache snoop when transmiting packets */ + mvpp2_write(priv, MVPP2_TX_SNOOP_REG, 0x1); + + /* Buffer Manager initialization */ + err = mvpp2_bm_init(dev, priv); + if (err < 0) + return err; + + /* Parser default initialization */ + err = mvpp2_prs_default_init(dev, priv); + if (err < 0) + return err; + + /* Classifier default initialization */ + mvpp2_cls_init(priv); + + return 0; +} + +/* SMI / MDIO functions */ + +static int smi_wait_ready(struct mvpp2 *priv) +{ + u32 timeout = MVPP2_SMI_TIMEOUT; + u32 smi_reg; + + /* wait till the SMI is not busy */ + do { + /* read smi register */ + smi_reg = readl(priv->lms_base + MVPP2_SMI); + if (timeout-- == 0) { + printf("Error: SMI busy timeout\n"); + return -EFAULT; + } + } while (smi_reg & MVPP2_SMI_BUSY); + + return 0; +} + +/* + * mpp2_mdio_read - miiphy_read callback function. + * + * Returns 16bit phy register value, or 0xffff on error + */ +static int mpp2_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct mvpp2 *priv = bus->priv; + u32 smi_reg; + u32 timeout; + + /* check parameters */ + if (addr > MVPP2_PHY_ADDR_MASK) { + printf("Error: Invalid PHY address %d\n", addr); + return -EFAULT; + } + + if (reg > MVPP2_PHY_REG_MASK) { + printf("Err: Invalid register offset %d\n", reg); + return -EFAULT; + } + + /* wait till the SMI is not busy */ + if (smi_wait_ready(priv) < 0) + return -EFAULT; + + /* fill the phy address and regiser offset and read opcode */ + smi_reg = (addr << MVPP2_SMI_DEV_ADDR_OFFS) + | (reg << MVPP2_SMI_REG_ADDR_OFFS) + | MVPP2_SMI_OPCODE_READ; + + /* write the smi register */ + writel(smi_reg, priv->lms_base + MVPP2_SMI); + + /* wait till read value is ready */ + timeout = MVPP2_SMI_TIMEOUT; + + do { + /* read smi register */ + smi_reg = readl(priv->lms_base + MVPP2_SMI); + if (timeout-- == 0) { + printf("Err: SMI read ready timeout\n"); + return -EFAULT; + } + } while (!(smi_reg & MVPP2_SMI_READ_VALID)); + + /* Wait for the data to update in the SMI register */ + for (timeout = 0; timeout < MVPP2_SMI_TIMEOUT; timeout++) + ; + + return readl(priv->lms_base + MVPP2_SMI) & MVPP2_SMI_DATA_MASK; +} + +/* + * mpp2_mdio_write - miiphy_write callback function. + * + * Returns 0 if write succeed, -EINVAL on bad parameters + * -ETIME on timeout + */ +static int mpp2_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 value) +{ + struct mvpp2 *priv = bus->priv; + u32 smi_reg; + + /* check parameters */ + if (addr > MVPP2_PHY_ADDR_MASK) { + printf("Error: Invalid PHY address %d\n", addr); + return -EFAULT; + } + + if (reg > MVPP2_PHY_REG_MASK) { + printf("Err: Invalid register offset %d\n", reg); + return -EFAULT; + } + + /* wait till the SMI is not busy */ + if (smi_wait_ready(priv) < 0) + return -EFAULT; + + /* fill the phy addr and reg offset and write opcode and data */ + smi_reg = value << MVPP2_SMI_DATA_OFFS; + smi_reg |= (addr << MVPP2_SMI_DEV_ADDR_OFFS) + | (reg << MVPP2_SMI_REG_ADDR_OFFS); + smi_reg &= ~MVPP2_SMI_OPCODE_READ; + + /* write the smi register */ + writel(smi_reg, priv->lms_base + MVPP2_SMI); + + return 0; +} + +static int mvpp2_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct mvpp2_port *port = dev_get_priv(dev); + struct mvpp2_rx_desc *rx_desc; + struct mvpp2_bm_pool *bm_pool; + dma_addr_t phys_addr; + u32 bm, rx_status; + int pool, rx_bytes, err; + int rx_received; + struct mvpp2_rx_queue *rxq; + u32 cause_rx_tx, cause_rx, cause_misc; + u8 *data; + + cause_rx_tx = mvpp2_read(port->priv, + MVPP2_ISR_RX_TX_CAUSE_REG(port->id)); + cause_rx_tx &= ~MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; + cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK; + if (!cause_rx_tx && !cause_misc) + return 0; + + cause_rx = cause_rx_tx & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; + + /* Process RX packets */ + cause_rx |= port->pending_cause_rx; + rxq = mvpp2_get_rx_queue(port, cause_rx); + + /* Get number of received packets and clamp the to-do */ + rx_received = mvpp2_rxq_received(port, rxq->id); + + /* Return if no packets are received */ + if (!rx_received) + return 0; + + rx_desc = mvpp2_rxq_next_desc_get(rxq); + rx_status = rx_desc->status; + rx_bytes = rx_desc->data_size - MVPP2_MH_SIZE; + phys_addr = rx_desc->buf_phys_addr; + + bm = mvpp2_bm_cookie_build(rx_desc); + pool = mvpp2_bm_cookie_pool_get(bm); + bm_pool = &port->priv->bm_pools[pool]; + + /* Check if buffer header is used */ + if (rx_status & MVPP2_RXD_BUF_HDR) + return 0; + + /* In case of an error, release the requested buffer pointer + * to the Buffer Manager. This request process is controlled + * by the hardware, and the information about the buffer is + * comprised by the RX descriptor. + */ + if (rx_status & MVPP2_RXD_ERR_SUMMARY) { + mvpp2_rx_error(port, rx_desc); + /* Return the buffer to the pool */ + mvpp2_pool_refill(port, bm, rx_desc->buf_phys_addr, + rx_desc->buf_cookie); + return 0; + } + + err = mvpp2_rx_refill(port, bm_pool, bm, phys_addr); + if (err) { + netdev_err(port->dev, "failed to refill BM pools\n"); + return 0; + } + + /* Update Rx queue management counters */ + mb(); + mvpp2_rxq_status_update(port, rxq->id, 1, 1); + + /* give packet to stack - skip on first n bytes */ + data = (u8 *)phys_addr + 2 + 32; + + if (rx_bytes <= 0) + return 0; + + /* + * No cache invalidation needed here, since the rx_buffer's are + * located in a uncached memory region + */ + *packetp = data; + + return rx_bytes; +} + +/* Drain Txq */ +static void mvpp2_txq_drain(struct mvpp2_port *port, struct mvpp2_tx_queue *txq, + int enable) +{ + u32 val; + + mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); + val = mvpp2_read(port->priv, MVPP2_TXQ_PREF_BUF_REG); + if (enable) + val |= MVPP2_TXQ_DRAIN_EN_MASK; + else + val &= ~MVPP2_TXQ_DRAIN_EN_MASK; + mvpp2_write(port->priv, MVPP2_TXQ_PREF_BUF_REG, val); +} + +static int mvpp2_send(struct udevice *dev, void *packet, int length) +{ + struct mvpp2_port *port = dev_get_priv(dev); + struct mvpp2_tx_queue *txq, *aggr_txq; + struct mvpp2_tx_desc *tx_desc; + int tx_done; + int timeout; + + txq = port->txqs[0]; + aggr_txq = &port->priv->aggr_txqs[smp_processor_id()]; + + /* Get a descriptor for the first part of the packet */ + tx_desc = mvpp2_txq_next_desc_get(aggr_txq); + tx_desc->phys_txq = txq->id; + tx_desc->data_size = length; + tx_desc->packet_offset = (u32)packet & MVPP2_TX_DESC_ALIGN; + tx_desc->buf_phys_addr = (u32)packet & ~MVPP2_TX_DESC_ALIGN; + /* First and Last descriptor */ + tx_desc->command = MVPP2_TXD_L4_CSUM_NOT | MVPP2_TXD_IP_CSUM_DISABLE + | MVPP2_TXD_F_DESC | MVPP2_TXD_L_DESC; + + /* Flush tx data */ + flush_dcache_range((u32)packet, (u32)packet + length); + + /* Enable transmit */ + mb(); + mvpp2_aggr_txq_pend_desc_add(port, 1); + + mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); + + timeout = 0; + do { + if (timeout++ > 10000) { + printf("timeout: packet not sent from aggregated to phys TXQ\n"); + return 0; + } + tx_done = mvpp2_txq_pend_desc_num_get(port, txq); + } while (tx_done); + + /* Enable TXQ drain */ + mvpp2_txq_drain(port, txq, 1); + + timeout = 0; + do { + if (timeout++ > 10000) { + printf("timeout: packet not sent\n"); + return 0; + } + tx_done = mvpp2_txq_sent_desc_proc(port, txq); + } while (!tx_done); + + /* Disable TXQ drain */ + mvpp2_txq_drain(port, txq, 0); + + return 0; +} + +static int mvpp2_start(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct mvpp2_port *port = dev_get_priv(dev); + + /* Load current MAC address */ + memcpy(port->dev_addr, pdata->enetaddr, ETH_ALEN); + + /* Reconfigure parser accept the original MAC address */ + mvpp2_prs_update_mac_da(port, port->dev_addr); + + mvpp2_port_power_up(port); + + mvpp2_open(dev, port); + + return 0; +} + +static void mvpp2_stop(struct udevice *dev) +{ + struct mvpp2_port *port = dev_get_priv(dev); + + mvpp2_stop_dev(port); + mvpp2_cleanup_rxqs(port); + mvpp2_cleanup_txqs(port); +} + +static int mvpp2_probe(struct udevice *dev) +{ + struct mvpp2_port *port = dev_get_priv(dev); + struct mvpp2 *priv = dev_get_priv(dev->parent); + int err; + + /* Initialize network controller */ + err = mvpp2_init(dev, priv); + if (err < 0) { + dev_err(&pdev->dev, "failed to initialize controller\n"); + return err; + } + + return mvpp2_port_probe(dev, port, dev->of_offset, priv, + &buffer_loc.first_rxq); +} + +static const struct eth_ops mvpp2_ops = { + .start = mvpp2_start, + .send = mvpp2_send, + .recv = mvpp2_recv, + .stop = mvpp2_stop, +}; + +static struct driver mvpp2_driver = { + .name = "mvpp2", + .id = UCLASS_ETH, + .probe = mvpp2_probe, + .ops = &mvpp2_ops, + .priv_auto_alloc_size = sizeof(struct mvpp2_port), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; + +/* + * Use a MISC device to bind the n instances (child nodes) of the + * network base controller in UCLASS_ETH. + */ +static int mvpp2_base_probe(struct udevice *dev) +{ + struct mvpp2 *priv = dev_get_priv(dev); + struct mii_dev *bus; + void *bd_space; + u32 size = 0; + int i; + + /* + * U-Boot special buffer handling: + * + * Allocate buffer area for descs and rx_buffers. This is only + * done once for all interfaces. As only one interface can + * be active. Make this area DMA-safe by disabling the D-cache + */ + + /* Align buffer area for descs and rx_buffers to 1MiB */ + bd_space = memalign(1 << MMU_SECTION_SHIFT, BD_SPACE); + mmu_set_region_dcache_behaviour((u32)bd_space, BD_SPACE, DCACHE_OFF); + + buffer_loc.aggr_tx_descs = (struct mvpp2_tx_desc *)bd_space; + size += MVPP2_AGGR_TXQ_SIZE * MVPP2_DESC_ALIGNED_SIZE; + + buffer_loc.tx_descs = (struct mvpp2_tx_desc *)((u32)bd_space + size); + size += MVPP2_MAX_TXD * MVPP2_DESC_ALIGNED_SIZE; + + buffer_loc.rx_descs = (struct mvpp2_rx_desc *)((u32)bd_space + size); + size += MVPP2_MAX_RXD * MVPP2_DESC_ALIGNED_SIZE; + + for (i = 0; i < MVPP2_BM_POOLS_NUM; i++) { + buffer_loc.bm_pool[i] = (u32 *)((u32)bd_space + size); + size += MVPP2_BM_POOL_SIZE_MAX * sizeof(u32); + } + + for (i = 0; i < MVPP2_BM_LONG_BUF_NUM; i++) { + buffer_loc.rx_buffer[i] = (u32 *)((u32)bd_space + size); + size += RX_BUFFER_SIZE; + } + + /* Save base addresses for later use */ + priv->base = (void *)dev_get_addr_index(dev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->lms_base = (void *)dev_get_addr_index(dev, 1); + if (IS_ERR(priv->lms_base)) + return PTR_ERR(priv->lms_base); + + /* Finally create and register the MDIO bus driver */ + bus = mdio_alloc(); + if (!bus) { + printf("Failed to allocate MDIO bus\n"); + return -ENOMEM; + } + + bus->read = mpp2_mdio_read; + bus->write = mpp2_mdio_write; + snprintf(bus->name, sizeof(bus->name), dev->name); + bus->priv = (void *)priv; + priv->bus = bus; + + return mdio_register(bus); +} + +static int mvpp2_base_bind(struct udevice *parent) +{ + const void *blob = gd->fdt_blob; + int node = parent->of_offset; + struct uclass_driver *drv; + struct udevice *dev; + struct eth_pdata *plat; + char *name; + int subnode; + u32 id; + + /* Lookup eth driver */ + drv = lists_uclass_lookup(UCLASS_ETH); + if (!drv) { + puts("Cannot find eth driver\n"); + return -ENOENT; + } + + fdt_for_each_subnode(blob, subnode, node) { + /* Skip disabled ports */ + if (!fdtdec_get_is_enabled(blob, subnode)) + continue; + + plat = calloc(1, sizeof(*plat)); + if (!plat) + return -ENOMEM; + + id = fdtdec_get_int(blob, subnode, "port-id", -1); + + name = calloc(1, 16); + sprintf(name, "mvpp2-%d", id); + + /* Create child device UCLASS_ETH and bind it */ + device_bind(parent, &mvpp2_driver, name, plat, subnode, &dev); + dev->of_offset = subnode; + } + + return 0; +} + +static const struct udevice_id mvpp2_ids[] = { + { .compatible = "marvell,armada-375-pp2" }, + { } +}; + +U_BOOT_DRIVER(mvpp2_base) = { + .name = "mvpp2_base", + .id = UCLASS_MISC, + .of_match = mvpp2_ids, + .bind = mvpp2_base_bind, + .probe = mvpp2_base_probe, + .priv_auto_alloc_size = sizeof(struct mvpp2), +}; diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 9e4d492..1e299b9 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -25,4 +25,5 @@ obj-$(CONFIG_PHY_REALTEK) += realtek.o obj-$(CONFIG_PHY_SMSC) += smsc.o obj-$(CONFIG_PHY_TERANETICS) += teranetics.o obj-$(CONFIG_PHY_TI) += ti.o +obj-$(CONFIG_PHY_XILINX) += xilinx_phy.o obj-$(CONFIG_PHY_VITESSE) += vitesse.o diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 17866a2..23c82bb 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -503,6 +503,9 @@ int phy_init(void) #ifdef CONFIG_PHY_VITESSE phy_vitesse_init(); #endif +#ifdef CONFIG_PHY_XILINX + phy_xilinx_init(); +#endif return 0; } diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 259a87f..9d7f55b 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -5,6 +5,7 @@ * * Copyright 2010-2011, 2015 Freescale Semiconductor, Inc. * author Andy Fleming + * Copyright 2016 Karsten Merker <merker@debian.org> */ #include <config.h> #include <common.h> @@ -12,6 +13,10 @@ #define PHY_AUTONEGOTIATE_TIMEOUT 5000 +/* RTL8211x 1000BASE-T Control Register */ +#define MIIM_RTL8211x_CTRL1000T_MSCE (1 << 12); +#define MIIM_RTL8211X_CTRL1000T_MASTER (1 << 11); + /* RTL8211x PHY Status Register */ #define MIIM_RTL8211x_PHY_STATUS 0x11 #define MIIM_RTL8211x_PHYSTAT_SPEED 0xc000 @@ -53,7 +58,14 @@ static int rtl8211x_config(struct phy_device *phydev) */ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER, MIIM_RTL8211x_PHY_INTR_DIS); - +#ifdef CONFIG_RTL8211X_PHY_FORCE_MASTER + unsigned int reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000); + /* force manual master/slave configuration */ + reg |= MIIM_RTL8211x_CTRL1000T_MSCE; + /* force master mode */ + reg |= MIIM_RTL8211X_CTRL1000T_MASTER; + phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, reg); +#endif /* read interrupt status just to clear it */ phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211x_PHY_INER); @@ -223,7 +235,7 @@ static int rtl8211f_startup(struct phy_device *phydev) /* Support for RTL8211B PHY */ static struct phy_driver RTL8211B_driver = { .name = "RealTek RTL8211B", - .uid = 0x1cc910, + .uid = 0x1cc912, .mask = 0xffffff, .features = PHY_GBIT_FEATURES, .config = &rtl8211x_config, diff --git a/drivers/net/phy/ti.c b/drivers/net/phy/ti.c index c3912d5..937426b 100644 --- a/drivers/net/phy/ti.c +++ b/drivers/net/phy/ti.c @@ -12,6 +12,8 @@ #define MII_DP83867_PHYCTRL 0x10 #define MII_DP83867_MICR 0x12 +#define MII_DP83867_CFG2 0x14 +#define MII_DP83867_BISCR 0x16 #define DP83867_CTRL 0x1f /* Extended Registers */ @@ -43,10 +45,22 @@ #define DP83867_PHYCR_FIFO_DEPTH_SHIFT 14 #define DP83867_MDI_CROSSOVER 5 #define DP83867_MDI_CROSSOVER_AUTO 2 +#define DP83867_MDI_CROSSOVER_MDIX 2 +#define DP83867_PHYCTRL_SGMIIEN 0x0800 +#define DP83867_PHYCTRL_RXFIFO_SHIFT 12 +#define DP83867_PHYCTRL_TXFIFO_SHIFT 14 /* RGMIIDCTL bits */ #define DP83867_RGMII_TX_CLK_DELAY_SHIFT 4 +/* CFG2 bits */ +#define MII_DP83867_CFG2_SPEEDOPT_10EN 0x0040 +#define MII_DP83867_CFG2_SGMII_AUTONEGEN 0x0080 +#define MII_DP83867_CFG2_SPEEDOPT_ENH 0x0100 +#define MII_DP83867_CFG2_SPEEDOPT_CNT 0x0800 +#define MII_DP83867_CFG2_SPEEDOPT_INTLOW 0x2000 +#define MII_DP83867_CFG2_MASK 0x003F + #define MII_MMD_CTRL 0x0d /* MMD Access Control Register */ #define MII_MMD_DATA 0x0e /* MMD Access Data Register */ @@ -141,7 +155,7 @@ static inline bool phy_interface_is_rgmii(struct phy_device *phydev) static int dp83867_config(struct phy_device *phydev) { - unsigned int val, delay; + unsigned int val, delay, cfg2; int ret; /* Restart the PHY. */ @@ -155,6 +169,29 @@ static int dp83867_config(struct phy_device *phydev) (FIFO_DEPTH << DP83867_PHYCR_FIFO_DEPTH_SHIFT)); if (ret) return ret; + } else { + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, + (BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED1000)); + + cfg2 = phy_read(phydev, phydev->addr, MII_DP83867_CFG2); + cfg2 &= MII_DP83867_CFG2_MASK; + cfg2 |= (MII_DP83867_CFG2_SPEEDOPT_10EN | + MII_DP83867_CFG2_SGMII_AUTONEGEN | + MII_DP83867_CFG2_SPEEDOPT_ENH | + MII_DP83867_CFG2_SPEEDOPT_CNT | + MII_DP83867_CFG2_SPEEDOPT_INTLOW); + phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_CFG2, cfg2); + + phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, + DP83867_DEVADDR, phydev->addr, 0x0); + + phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_PHYCTRL, + DP83867_PHYCTRL_SGMIIEN | + (DP83867_MDI_CROSSOVER_MDIX << + DP83867_MDI_CROSSOVER) | + (FIFO_DEPTH << DP83867_PHYCTRL_RXFIFO_SHIFT) | + (FIFO_DEPTH << DP83867_PHYCTRL_TXFIFO_SHIFT)); + phy_write(phydev, MDIO_DEVAD_NONE, MII_DP83867_BISCR, 0x0); } if ((phydev->interface >= PHY_INTERFACE_MODE_RGMII_ID) && diff --git a/drivers/net/phy/xilinx_phy.c b/drivers/net/phy/xilinx_phy.c new file mode 100644 index 0000000..f3eaf2e --- /dev/null +++ b/drivers/net/phy/xilinx_phy.c @@ -0,0 +1,144 @@ +/* + * Xilinx PCS/PMA Core phy driver + * + * Copyright (C) 2015 - 2016 Xilinx, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> +#include <common.h> +#include <phy.h> +#include <dm.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define MII_PHY_STATUS_SPD_MASK 0x0C00 +#define MII_PHY_STATUS_FULLDUPLEX 0x1000 +#define MII_PHY_STATUS_1000 0x0800 +#define MII_PHY_STATUS_100 0x0400 +#define XPCSPMA_PHY_CTRL_ISOLATE_DISABLE 0xFBFF + +/* Mask used for ID comparisons */ +#define XILINX_PHY_ID_MASK 0xfffffff0 + +/* Known PHY IDs */ +#define XILINX_PHY_ID 0x01740c00 + +/* struct phy_device dev_flags definitions */ +#define XAE_PHY_TYPE_MII 0 +#define XAE_PHY_TYPE_GMII 1 +#define XAE_PHY_TYPE_RGMII_1_3 2 +#define XAE_PHY_TYPE_RGMII_2_0 3 +#define XAE_PHY_TYPE_SGMII 4 +#define XAE_PHY_TYPE_1000BASE_X 5 + +static int xilinxphy_startup(struct phy_device *phydev) +{ + int err; + int status = 0; + + debug("%s\n", __func__); + /* Update the link, but return if there + * was an error + */ + err = genphy_update_link(phydev); + if (err) + return err; + + if (AUTONEG_ENABLE == phydev->autoneg) { + status = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA); + status = status & MII_PHY_STATUS_SPD_MASK; + + if (status & MII_PHY_STATUS_FULLDUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + switch (status) { + case MII_PHY_STATUS_1000: + phydev->speed = SPEED_1000; + break; + + case MII_PHY_STATUS_100: + phydev->speed = SPEED_100; + break; + + default: + phydev->speed = SPEED_10; + break; + } + } else { + int bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + + if (bmcr < 0) + return bmcr; + + if (bmcr & BMCR_FULLDPLX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + if (bmcr & BMCR_SPEED1000) + phydev->speed = SPEED_1000; + else if (bmcr & BMCR_SPEED100) + phydev->speed = SPEED_100; + else + phydev->speed = SPEED_10; + } + + /* + * For 1000BASE-X Phy Mode the speed/duplex will always be + * 1000Mbps/fullduplex + */ + if (phydev->flags == XAE_PHY_TYPE_1000BASE_X) { + phydev->duplex = DUPLEX_FULL; + phydev->speed = SPEED_1000; + } + + return 0; +} + +static int xilinxphy_of_init(struct phy_device *phydev) +{ + struct udevice *dev = (struct udevice *)&phydev->dev; + u32 phytype; + + debug("%s\n", __func__); + phytype = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "phy-type", -1); + if (phytype == XAE_PHY_TYPE_1000BASE_X) + phydev->flags |= XAE_PHY_TYPE_1000BASE_X; + + return 0; +} + +static int xilinxphy_config(struct phy_device *phydev) +{ + int temp; + + debug("%s\n", __func__); + xilinxphy_of_init(phydev); + temp = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + temp &= XPCSPMA_PHY_CTRL_ISOLATE_DISABLE; + phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, temp); + + return 0; +} + +static struct phy_driver xilinxphy_driver = { + .uid = XILINX_PHY_ID, + .mask = XILINX_PHY_ID_MASK, + .name = "Xilinx PCS/PMA PHY", + .features = PHY_GBIT_FEATURES, + .config = &xilinxphy_config, + .startup = &xilinxphy_startup, + .shutdown = &genphy_shutdown, +}; + +int phy_xilinx_init(void) +{ + debug("%s\n", __func__); + phy_register(&xilinxphy_driver); + + return 0; +} diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index 9e60adf..163b9df 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -995,7 +995,7 @@ static int rtl_init(unsigned long dev_ioaddr, const char *name, /* Force RTL8169 in 10/100/1000 Full/Half mode. */ if (option > 0) { #ifdef DEBUG_RTL8169 - printf("%s: Force-mode Enabled.\n", dev->name); + printf("%s: Force-mode Enabled.\n", name); #endif Cap10_100 = 0, Cap1000 = 0; switch (option) { @@ -1027,7 +1027,7 @@ static int rtl_init(unsigned long dev_ioaddr, const char *name, } else { #ifdef DEBUG_RTL8169 printf("%s: Auto-negotiation Enabled.\n", - dev->name); + name); #endif /* enable 10/100 Full/Half Mode, leave PHY_AUTO_NEGO_REG bit4:0 unchanged */ mdio_write(PHY_AUTO_NEGO_REG, @@ -1054,12 +1054,12 @@ static int rtl_init(unsigned long dev_ioaddr, const char *name, if (option & _1000bpsF) { #ifdef DEBUG_RTL8169 printf("%s: 1000Mbps Full-duplex operation.\n", - dev->name); + name); #endif } else { #ifdef DEBUG_RTL8169 printf("%s: %sMbps %s-duplex operation.\n", - dev->name, + name, (option & _100bps) ? "100" : "10", (option & FullDup) ? "Full" : @@ -1077,7 +1077,7 @@ static int rtl_init(unsigned long dev_ioaddr, const char *name, #ifdef DEBUG_RTL8169 printf ("%s: 1000Mbps Full-duplex operation, TBI Link %s!\n", - dev->name, + name, (RTL_R32(TBICSR) & TBILinkOK) ? "OK" : "Failed"); #endif } diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 44afe14..2388438 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -335,7 +335,7 @@ static int vsc9953_port_vlan_pvid_get(int port_nr, int *pvid) struct vsc9953_analyzer *l2ana_reg; /* Administrative down */ - if (vsc9953_l2sw.port[port_nr].enabled) { + if (!vsc9953_l2sw.port[port_nr].enabled) { printf("Port %d is administrative down\n", port_nr); return -1; } @@ -2525,6 +2525,9 @@ void vsc9953_init(bd_t *bis) if (vsc9953_port_init(i)) printf("Failed to initialize l2switch port %d\n", i); + if (!vsc9953_l2sw.port[i].enabled) + continue; + /* Enable VSC9953 GMII Ports Port ID 0 - 7 */ if (VSC9953_INTERNAL_PORT_CHECK(i)) { out_le32(&l2ana_reg->pfc[i].pfc_cfg, @@ -2537,6 +2540,11 @@ void vsc9953_init(bd_t *bis) out_le32(&l2sys_reg->pause_cfg.mac_fc_cfg[i], VSC9953_MAC_FC_CFG); } + + l2dev_gmii_reg = (struct vsc9953_dev_gmii *) + (VSC9953_OFFSET + VSC9953_DEV_GMII_OFFSET + + T1040_SWITCH_GMII_DEV_OFFSET * i); + out_le32(&l2dev_gmii_reg->port_mode.clock_cfg, VSC9953_CLOCK_CFG); out_le32(&l2dev_gmii_reg->mac_cfg_status.mac_ena_cfg, @@ -2559,10 +2567,6 @@ void vsc9953_init(bd_t *bis) /* WAIT FOR 2 us*/ udelay(2); - l2dev_gmii_reg = (struct vsc9953_dev_gmii *)( - (char *)l2dev_gmii_reg - + T1040_SWITCH_GMII_DEV_OFFSET); - /* Initialize Lynx PHY Wrappers */ phy_addr = 0; if (vsc9953_l2sw.port[i].enet_if == diff --git a/drivers/net/xilinx_axi_emac.c b/drivers/net/xilinx_axi_emac.c index 81274ee..5de06ef 100644 --- a/drivers/net/xilinx_axi_emac.c +++ b/drivers/net/xilinx_axi_emac.c @@ -251,7 +251,7 @@ static int axiemac_phy_init(struct udevice *dev) } /* Interface - look at tsec */ - phydev = phy_connect(priv->bus, priv->phyaddr, dev, 0); + phydev = phy_connect(priv->bus, priv->phyaddr, dev, priv->interface); phydev->supported &= supported; phydev->advertising = phydev->supported; @@ -264,11 +264,29 @@ static int axiemac_phy_init(struct udevice *dev) /* Setting axi emac and phy to proper setting */ static int setup_phy(struct udevice *dev) { - u32 speed, emmc_reg; + u16 temp; + u32 speed, emmc_reg, ret; struct axidma_priv *priv = dev_get_priv(dev); struct axi_regs *regs = priv->iobase; struct phy_device *phydev = priv->phydev; + if (priv->interface == PHY_INTERFACE_MODE_SGMII) { + /* + * In SGMII cases the isolate bit might set + * after DMA and ethernet resets and hence + * check and clear if set. + */ + ret = phyread(priv, priv->phyaddr, MII_BMCR, &temp); + if (ret) + return 0; + if (temp & BMCR_ISOLATE) { + temp &= ~BMCR_ISOLATE; + ret = phywrite(priv, priv->phyaddr, MII_BMCR, temp); + if (ret) + return 0; + } + } + if (phy_startup(phydev)) { printf("axiemac: could not initialize PHY %s\n", phydev->dev->name); @@ -697,7 +715,7 @@ static int axi_emac_ofdata_to_platdata(struct udevice *dev) if (phy_mode) pdata->phy_interface = phy_get_interface_by_name(phy_mode); if (pdata->phy_interface == -1) { - debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode); + printf("%s: Invalid PHY interface '%s'\n", __func__, phy_mode); return -EINVAL; } priv->interface = pdata->phy_interface; diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index b3821c3..aec8077 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -57,6 +57,8 @@ DECLARE_GLOBAL_DATA_PTR; #define ZYNQ_GEM_NWCFG_SPEED1000 0x000000400 /* 1Gbps operation */ #define ZYNQ_GEM_NWCFG_FDEN 0x000000002 /* Full Duplex mode */ #define ZYNQ_GEM_NWCFG_FSREM 0x000020000 /* FCS removal */ +#define ZYNQ_GEM_NWCFG_SGMII_ENBL 0x080000000 /* SGMII Enable */ +#define ZYNQ_GEM_NWCFG_PCS_SEL 0x000000800 /* PCS select */ #ifdef CONFIG_ARM64 #define ZYNQ_GEM_NWCFG_MDCCLKDIV 0x000100000 /* Div pclk by 64, max 160MHz */ #else @@ -91,6 +93,8 @@ DECLARE_GLOBAL_DATA_PTR; #define ZYNQ_GEM_TSR_DONE 0x00000020 /* Tx done mask */ +#define ZYNQ_GEM_PCS_CTL_ANEG_ENBL 0x1000 + /* Use MII register 1 (MII status register) to detect PHY */ #define PHY_DETECT_REG 1 @@ -137,7 +141,9 @@ struct zynq_gem_regs { u32 reserved6[18]; #define STAT_SIZE 44 u32 stat[STAT_SIZE]; /* 0x100 - Octects transmitted Low reg */ - u32 reserved7[164]; + u32 reserved9[20]; + u32 pcscntrl; + u32 reserved7[143]; u32 transmit_q1_ptr; /* 0x440 - Transmit priority queue 1 */ u32 reserved8[15]; u32 receive_q1_ptr; /* 0x480 - Receive priority queue 1 */ @@ -330,10 +336,12 @@ static int zynq_phy_init(struct udevice *dev) /* Enable only MDIO bus */ writel(ZYNQ_GEM_NWCTRL_MDEN_MASK, ®s->nwctrl); - ret = phy_detection(dev); - if (ret) { - printf("GEM PHY init failed\n"); - return ret; + if (priv->interface != PHY_INTERFACE_MODE_SGMII) { + ret = phy_detection(dev); + if (ret) { + printf("GEM PHY init failed\n"); + return ret; + } } priv->phydev = phy_connect(priv->bus, priv->phyaddr, dev, @@ -351,7 +359,7 @@ static int zynq_phy_init(struct udevice *dev) static int zynq_gem_init(struct udevice *dev) { - u32 i; + u32 i, nwconfig; unsigned long clk_rate = 0; struct zynq_gem_priv *priv = dev_get_priv(dev); struct zynq_gem_regs *regs = priv->iobase; @@ -426,14 +434,25 @@ static int zynq_gem_init(struct udevice *dev) return -1; } + nwconfig = ZYNQ_GEM_NWCFG_INIT; + + if (priv->interface == PHY_INTERFACE_MODE_SGMII) { + nwconfig |= ZYNQ_GEM_NWCFG_SGMII_ENBL | + ZYNQ_GEM_NWCFG_PCS_SEL; +#ifdef CONFIG_ARM64 + writel(readl(®s->pcscntrl) | ZYNQ_GEM_PCS_CTL_ANEG_ENBL, + ®s->pcscntrl); +#endif + } + switch (priv->phydev->speed) { case SPEED_1000: - writel(ZYNQ_GEM_NWCFG_INIT | ZYNQ_GEM_NWCFG_SPEED1000, + writel(nwconfig | ZYNQ_GEM_NWCFG_SPEED1000, ®s->nwcfg); clk_rate = ZYNQ_GEM_FREQUENCY_1000; break; case SPEED_100: - writel(ZYNQ_GEM_NWCFG_INIT | ZYNQ_GEM_NWCFG_SPEED100, + writel(nwconfig | ZYNQ_GEM_NWCFG_SPEED100, ®s->nwcfg); clk_rate = ZYNQ_GEM_FREQUENCY_100; break; @@ -561,6 +580,23 @@ static void zynq_gem_halt(struct udevice *dev) ZYNQ_GEM_NWCTRL_TXEN_MASK, 0); } +__weak int zynq_board_read_rom_ethaddr(unsigned char *ethaddr) +{ + return -ENOSYS; +} + +static int zynq_gem_read_rom_mac(struct udevice *dev) +{ + int retval; + struct eth_pdata *pdata = dev_get_platdata(dev); + + retval = zynq_board_read_rom_ethaddr(pdata->enetaddr); + if (retval == -ENOSYS) + retval = 0; + + return retval; +} + static int zynq_gem_miiphy_read(struct mii_dev *bus, int addr, int devad, int reg) { @@ -611,9 +647,7 @@ static int zynq_gem_probe(struct udevice *dev) if (ret) return ret; - zynq_phy_init(dev); - - return 0; + return zynq_phy_init(dev); } static int zynq_gem_remove(struct udevice *dev) @@ -634,6 +668,7 @@ static const struct eth_ops zynq_gem_ops = { .free_pkt = zynq_gem_free_pkt, .stop = zynq_gem_halt, .write_hwaddr = zynq_gem_setup_mac, + .read_rom_hwaddr = zynq_gem_read_rom_mac, }; static int zynq_gem_ofdata_to_platdata(struct udevice *dev) @@ -663,6 +698,8 @@ static int zynq_gem_ofdata_to_platdata(struct udevice *dev) } priv->interface = pdata->phy_interface; + priv->emio = fdtdec_get_bool(gd->fdt_blob, dev->of_offset, "xlnx,emio"); + printf("ZYNQ GEM: %lx, phyaddr %d, interface %s\n", (ulong)priv->iobase, priv->phyaddr, phy_string_for_interface(priv->interface)); diff --git a/drivers/pci/pcie_layerscape.c b/drivers/pci/pcie_layerscape.c index 99f9c83..0ba960e 100644 --- a/drivers/pci/pcie_layerscape.c +++ b/drivers/pci/pcie_layerscape.c @@ -93,6 +93,7 @@ struct ls_pcie { void __iomem *dbi; void __iomem *va_cfg0; void __iomem *va_cfg1; + int next_lut_index; struct pci_controller hose; }; @@ -482,6 +483,147 @@ static void ls_pcie_setup_ep(struct ls_pcie *pcie, struct ls_pcie_info *info) } } +#ifdef CONFIG_FSL_LSCH3 +/* + * Return next available LUT index. + */ +static int ls_pcie_next_lut_index(struct ls_pcie *pcie) +{ + if (pcie->next_lut_index < PCIE_LUT_ENTRY_COUNT) + return pcie->next_lut_index++; + else + return -1; /* LUT is full */ +} + +/* + * Program a single LUT entry + */ +static void ls_pcie_lut_set_mapping(struct ls_pcie *pcie, int index, u32 devid, + u32 streamid) +{ + void __iomem *lut; + + lut = pcie->dbi + PCIE_LUT_BASE; + + /* leave mask as all zeroes, want to match all bits */ + writel((devid << 16), lut + PCIE_LUT_UDR(index)); + writel(streamid | PCIE_LUT_ENABLE, lut + PCIE_LUT_LDR(index)); +} + +/* returns the next available streamid */ +static u32 ls_pcie_next_streamid(void) +{ + static int next_stream_id = FSL_PEX_STREAM_ID_START; + + if (next_stream_id > FSL_PEX_STREAM_ID_END) + return 0xffffffff; + + return next_stream_id++; +} + +/* + * An msi-map is a property to be added to the pci controller + * node. It is a table, where each entry consists of 4 fields + * e.g.: + * + * msi-map = <[devid] [phandle-to-msi-ctrl] [stream-id] [count] + * [devid] [phandle-to-msi-ctrl] [stream-id] [count]>; + */ +static void fdt_pcie_set_msi_map_entry(void *blob, struct ls_pcie *pcie, + u32 devid, u32 streamid) +{ + char pcie_path[19]; + u32 *prop; + u32 phandle; + int nodeoffset; + + /* find pci controller node */ + snprintf(pcie_path, sizeof(pcie_path), "/soc/pcie@%llx", + (u64)pcie->dbi); + nodeoffset = fdt_path_offset(blob, pcie_path); + if (nodeoffset < 0) { + printf("\n%s: ERROR: unable to update PCIe node: %s\n", + __func__, pcie_path); + return; + } + + /* get phandle to MSI controller */ + prop = (u32 *)fdt_getprop(blob, nodeoffset, "msi-parent", 0); + if (prop == NULL) { + printf("\n%s: ERROR: missing msi-parent: %s\n", __func__, + pcie_path); + return; + } + phandle = be32_to_cpu(*prop); + + /* set one msi-map row */ + fdt_appendprop_u32(blob, nodeoffset, "msi-map", devid); + fdt_appendprop_u32(blob, nodeoffset, "msi-map", phandle); + fdt_appendprop_u32(blob, nodeoffset, "msi-map", streamid); + fdt_appendprop_u32(blob, nodeoffset, "msi-map", 1); +} + +static void fdt_fixup_pcie(void *blob) +{ + unsigned int found_multi = 0; + unsigned char header_type; + int index; + u32 streamid; + pci_dev_t dev; + int bus; + unsigned short id; + struct pci_controller *hose; + struct ls_pcie *pcie; + int i; + + for (i = 0, hose = pci_get_hose_head(); hose; hose = hose->next, i++) { + pcie = hose->priv_data; + for (bus = hose->first_busno; bus <= hose->last_busno; bus++) { + + for (dev = PCI_BDF(bus, 0, 0); + dev < PCI_BDF(bus, PCI_MAX_PCI_DEVICES - 1, + PCI_MAX_PCI_FUNCTIONS - 1); + dev += PCI_BDF(0, 0, 1)) { + + if (PCI_FUNC(dev) && !found_multi) + continue; + + pci_read_config_word(dev, PCI_VENDOR_ID, &id); + + pci_read_config_byte(dev, PCI_HEADER_TYPE, + &header_type); + + if ((id == 0xFFFF) || (id == 0x0000)) + continue; + + if (!PCI_FUNC(dev)) + found_multi = header_type & 0x80; + + streamid = ls_pcie_next_streamid(); + if (streamid == 0xffffffff) { + printf("ERROR: no stream ids free\n"); + continue; + } + + index = ls_pcie_next_lut_index(pcie); + if (index < 0) { + printf("ERROR: no LUT indexes free\n"); + continue; + } + + /* map PCI b.d.f to streamID in LUT */ + ls_pcie_lut_set_mapping(pcie, index, dev >> 8, + streamid); + + /* update msi-map in device tree */ + fdt_pcie_set_msi_map_entry(blob, pcie, dev >> 8, + streamid); + } + } + } +} +#endif + int ls_pcie_init_ctrl(int busno, enum srds_prtcl dev, struct ls_pcie_info *info) { struct ls_pcie *pcie; @@ -513,6 +655,7 @@ int ls_pcie_init_ctrl(int busno, enum srds_prtcl dev, struct ls_pcie_info *info) pcie->va_cfg1 = map_physmem(info->cfg1_phys, info->cfg1_size, MAP_NOCACHE); + pcie->next_lut_index = 0; /* outbound memory */ pci_set_region(&hose->regions[0], @@ -657,6 +800,10 @@ void ft_pci_setup(void *blob, bd_t *bd) #ifdef CONFIG_PCIE4 ft_pcie_ls_setup(blob, FSL_PCIE_COMPAT, CONFIG_SYS_PCIE4_ADDR, PCIE4); #endif + + #ifdef CONFIG_FSL_LSCH3 + fdt_fixup_pcie(blob); + #endif } #else @@ -664,73 +811,3 @@ void ft_pci_setup(void *blob, bd_t *bd) { } #endif - -#if defined(CONFIG_LS2080A) || defined(CONFIG_LS2085A) - -void pcie_set_available_streamids(void *blob, const char *pcie_path, - u32 *stream_ids, int count) -{ - int nodeoffset; - int i; - - nodeoffset = fdt_path_offset(blob, pcie_path); - if (nodeoffset < 0) { - printf("\n%s: ERROR: unable to update PCIe node\n", __func__); - return; - } - - /* for each stream ID, append to mmu-masters */ - for (i = 0; i < count; i++) { - fdt_appendprop_u32(blob, nodeoffset, "available-stream-ids", - stream_ids[i]); - } -} - -#define MAX_STREAM_IDS 4 -void fdt_fixup_smmu_pcie(void *blob) -{ - int count; - u32 stream_ids[MAX_STREAM_IDS]; - u32 ctlr_streamid = 0x300; - - #ifdef CONFIG_PCIE1 - /* PEX1 stream ID fixup */ - count = FSL_PEX1_STREAM_ID_END - FSL_PEX1_STREAM_ID_START + 1; - alloc_stream_ids(FSL_PEX1_STREAM_ID_START, count, stream_ids, - MAX_STREAM_IDS); - pcie_set_available_streamids(blob, "/pcie@3400000", stream_ids, count); - append_mmu_masters(blob, "/iommu@5000000", "/pcie@3400000", - &ctlr_streamid, 1); - #endif - - #ifdef CONFIG_PCIE2 - /* PEX2 stream ID fixup */ - count = FSL_PEX2_STREAM_ID_END - FSL_PEX2_STREAM_ID_START + 1; - alloc_stream_ids(FSL_PEX2_STREAM_ID_START, count, stream_ids, - MAX_STREAM_IDS); - pcie_set_available_streamids(blob, "/pcie@3500000", stream_ids, count); - append_mmu_masters(blob, "/iommu@5000000", "/pcie@3500000", - &ctlr_streamid, 1); - #endif - - #ifdef CONFIG_PCIE3 - /* PEX3 stream ID fixup */ - count = FSL_PEX3_STREAM_ID_END - FSL_PEX3_STREAM_ID_START + 1; - alloc_stream_ids(FSL_PEX3_STREAM_ID_START, count, stream_ids, - MAX_STREAM_IDS); - pcie_set_available_streamids(blob, "/pcie@3600000", stream_ids, count); - append_mmu_masters(blob, "/iommu@5000000", "/pcie@3600000", - &ctlr_streamid, 1); - #endif - - #ifdef CONFIG_PCIE4 - /* PEX4 stream ID fixup */ - count = FSL_PEX4_STREAM_ID_END - FSL_PEX4_STREAM_ID_START + 1; - alloc_stream_ids(FSL_PEX4_STREAM_ID_START, count, stream_ids, - MAX_STREAM_IDS); - pcie_set_available_streamids(blob, "/pcie@3700000", stream_ids, count); - append_mmu_masters(blob, "/iommu@5000000", "/pcie@3700000", - &ctlr_streamid, 1); - #endif -} -#endif diff --git a/drivers/pinctrl/uniphier/Kconfig b/drivers/pinctrl/uniphier/Kconfig index 33d6763..1856ff0 100644 --- a/drivers/pinctrl/uniphier/Kconfig +++ b/drivers/pinctrl/uniphier/Kconfig @@ -3,39 +3,45 @@ if ARCH_UNIPHIER config PINCTRL_UNIPHIER bool -config PINCTRL_UNIPHIER_PH1_LD4 +config PINCTRL_UNIPHIER_LD4 bool "UniPhier PH1-LD4 SoC pinctrl driver" - depends on ARCH_UNIPHIER_PH1_LD4 + depends on ARCH_UNIPHIER_LD4 default y select PINCTRL_UNIPHIER -config PINCTRL_UNIPHIER_PH1_PRO4 +config PINCTRL_UNIPHIER_PRO4 bool "UniPhier PH1-Pro4 SoC pinctrl driver" - depends on ARCH_UNIPHIER_PH1_PRO4 + depends on ARCH_UNIPHIER_PRO4 default y select PINCTRL_UNIPHIER -config PINCTRL_UNIPHIER_PH1_SLD8 +config PINCTRL_UNIPHIER_SLD8 bool "UniPhier PH1-sLD8 SoC pinctrl driver" - depends on ARCH_UNIPHIER_PH1_SLD8 + depends on ARCH_UNIPHIER_SLD8 default y select PINCTRL_UNIPHIER -config PINCTRL_UNIPHIER_PH1_PRO5 +config PINCTRL_UNIPHIER_PRO5 bool "UniPhier PH1-Pro5 SoC pinctrl driver" - depends on ARCH_UNIPHIER_PH1_PRO5 + depends on ARCH_UNIPHIER_PRO5 default y select PINCTRL_UNIPHIER -config PINCTRL_UNIPHIER_PROXSTREAM2 +config PINCTRL_UNIPHIER_PXS2 bool "UniPhier ProXstream2 SoC pinctrl driver" - depends on ARCH_UNIPHIER_PROXSTREAM2 + depends on ARCH_UNIPHIER_PXS2 default y select PINCTRL_UNIPHIER -config PINCTRL_UNIPHIER_PH1_LD6B +config PINCTRL_UNIPHIER_LD6B bool "UniPhier PH1-LD6b SoC pinctrl driver" - depends on ARCH_UNIPHIER_PH1_LD6B + depends on ARCH_UNIPHIER_LD6B + default y + select PINCTRL_UNIPHIER + +config PINCTRL_UNIPHIER_LD20 + bool "UniPhier PH1-LD11/PH1-LD20 SoC pinctrl driver" + depends on ARCH_UNIPHIER_LD11 || ARCH_UNIPHIER_LD20 default y select PINCTRL_UNIPHIER diff --git a/drivers/pinctrl/uniphier/Makefile b/drivers/pinctrl/uniphier/Makefile index 3667bd3..bea4dd8 100644 --- a/drivers/pinctrl/uniphier/Makefile +++ b/drivers/pinctrl/uniphier/Makefile @@ -2,11 +2,12 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-y += pinctrl-uniphier-core.o +obj-y += pinctrl-uniphier-core.o -obj-$(CONFIG_PINCTRL_UNIPHIER_PH1_LD4) += pinctrl-ph1-ld4.o -obj-$(CONFIG_PINCTRL_UNIPHIER_PH1_PRO4) += pinctrl-ph1-pro4.o -obj-$(CONFIG_PINCTRL_UNIPHIER_PH1_SLD8) += pinctrl-ph1-sld8.o -obj-$(CONFIG_PINCTRL_UNIPHIER_PH1_PRO5) += pinctrl-ph1-pro5.o -obj-$(CONFIG_PINCTRL_UNIPHIER_PROXSTREAM2) += pinctrl-proxstream2.o -obj-$(CONFIG_PINCTRL_UNIPHIER_PH1_LD6B) += pinctrl-ph1-ld6b.o +obj-$(CONFIG_PINCTRL_UNIPHIER_LD4) += pinctrl-uniphier-ld4.o +obj-$(CONFIG_PINCTRL_UNIPHIER_PRO4) += pinctrl-uniphier-pro4.o +obj-$(CONFIG_PINCTRL_UNIPHIER_SLD8) += pinctrl-uniphier-sld8.o +obj-$(CONFIG_PINCTRL_UNIPHIER_PRO5) += pinctrl-uniphier-pro5.o +obj-$(CONFIG_PINCTRL_UNIPHIER_PXS2) += pinctrl-uniphier-pxs2.o +obj-$(CONFIG_PINCTRL_UNIPHIER_LD6B) += pinctrl-uniphier-ld6b.o +obj-$(CONFIG_PINCTRL_UNIPHIER_LD20) += pinctrl-uniphier-ld20.o diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c index ffdccab..b8e26d9 100644 --- a/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c @@ -8,13 +8,12 @@ #include <mapmem.h> #include <linux/io.h> #include <linux/err.h> +#include <linux/sizes.h> #include <dm/device.h> #include <dm/pinctrl.h> #include "pinctrl-uniphier.h" -DECLARE_GLOBAL_DATA_PTR; - static int uniphier_pinctrl_get_groups_count(struct udevice *dev) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); @@ -45,7 +44,23 @@ static const char *uniphier_pinmux_get_function_name(struct udevice *dev, return priv->socdata->functions[selector]; } -static void uniphier_pinconf_input_enable(struct udevice *dev, unsigned pin) +static void uniphier_pinconf_input_enable_perpin(struct udevice *dev, + unsigned pin) +{ + struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); + unsigned reg; + u32 mask, tmp; + + reg = UNIPHIER_PINCTRL_IECTRL + pin / 32 * 4; + mask = BIT(pin % 32); + + tmp = readl(priv->base + reg); + tmp |= mask; + writel(tmp, priv->base + reg); +} + +static void uniphier_pinconf_input_enable_legacy(struct udevice *dev, + unsigned pin) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); int pins_count = priv->socdata->pins_count; @@ -65,18 +80,47 @@ static void uniphier_pinconf_input_enable(struct udevice *dev, unsigned pin) } } +static void uniphier_pinconf_input_enable(struct udevice *dev, unsigned pin) +{ + struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); + + if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL) + uniphier_pinconf_input_enable_perpin(dev, pin); + else + uniphier_pinconf_input_enable_legacy(dev, pin); +} + static void uniphier_pinmux_set_one(struct udevice *dev, unsigned pin, unsigned muxval) { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); - unsigned mux_bits = priv->socdata->mux_bits; - unsigned reg_stride = priv->socdata->reg_stride; - unsigned reg, reg_end, shift, mask; + unsigned mux_bits, reg_stride, reg, reg_end, shift, mask; + bool load_pinctrl; u32 tmp; /* some pins need input-enabling */ uniphier_pinconf_input_enable(dev, pin); + if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE) { + /* + * Mode offset bit + * Normal 4 * n shift+3:shift + * Debug 4 * n shift+7:shift+4 + */ + mux_bits = 4; + reg_stride = 8; + load_pinctrl = true; + } else { + /* + * Mode offset bit + * Normal 8 * n shift+3:shift + * Debug 8 * n + 4 shift+3:shift + */ + mux_bits = 8; + reg_stride = 4; + load_pinctrl = false; + } + reg = UNIPHIER_PINCTRL_PINMUX_BASE + pin * mux_bits / 32 * reg_stride; reg_end = reg + reg_stride; shift = pin * mux_bits % 32; @@ -95,7 +139,7 @@ static void uniphier_pinmux_set_one(struct udevice *dev, unsigned pin, muxval >>= mux_bits; } - if (priv->socdata->load_pinctrl) + if (load_pinctrl) writel(1, priv->base + UNIPHIER_PINCTRL_LOAD_PINMUX); } @@ -128,14 +172,12 @@ int uniphier_pinctrl_probe(struct udevice *dev, { struct uniphier_pinctrl_priv *priv = dev_get_priv(dev); fdt_addr_t addr; - fdt_size_t size; - addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", - &size); + addr = dev_get_addr(dev); if (addr == FDT_ADDR_T_NONE) return -EINVAL; - priv->base = map_sysmem(addr, size); + priv->base = map_sysmem(addr, SZ_4K); if (!priv->base) return -ENOMEM; diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c new file mode 100644 index 0000000..3d5ac5f --- /dev/null +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2016 Masahiro Yamada <yamada.masahiro@socionext.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <dm/device.h> +#include <dm/pinctrl.h> + +#include "pinctrl-uniphier.h" + +static const unsigned emmc_pins[] = {18, 19, 20, 21, 22, 23, 24, 25}; +static const unsigned emmc_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0}; +static const unsigned emmc_dat8_pins[] = {26, 27, 28, 29}; +static const unsigned emmc_dat8_muxvals[] = {0, 0, 0, 0}; +static const unsigned i2c0_pins[] = {63, 64}; +static const unsigned i2c0_muxvals[] = {0, 0}; +static const unsigned i2c1_pins[] = {65, 66}; +static const unsigned i2c1_muxvals[] = {0, 0}; +static const unsigned i2c3_pins[] = {67, 68}; +static const unsigned i2c3_muxvals[] = {1, 1}; +static const unsigned i2c4_pins[] = {61, 62}; +static const unsigned i2c4_muxvals[] = {1, 1}; +static const unsigned nand_pins[] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16}; +static const unsigned nand_muxvals[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0}; +static const unsigned nand_cs1_pins[] = {}; +static const unsigned nand_cs1_muxvals[] = {}; +static const unsigned sd_pins[] = {10, 11, 12, 13, 14, 15, 16, 17}; +static const unsigned sd_muxvals[] = {8, 8, 8, 8, 8, 8, 8, 8}; /* No SDVOLC */ +static const unsigned uart0_pins[] = {54, 55}; +static const unsigned uart0_muxvals[] = {0, 0}; +static const unsigned uart1_pins[] = {58, 59}; +static const unsigned uart1_muxvals[] = {1, 1}; +static const unsigned uart2_pins[] = {90, 91}; +static const unsigned uart2_muxvals[] = {1, 1}; +static const unsigned uart3_pins[] = {94, 95}; +static const unsigned uart3_muxvals[] = {1, 1}; +static const unsigned usb0_pins[] = {46, 47}; +static const unsigned usb0_muxvals[] = {0, 0}; +static const unsigned usb1_pins[] = {48, 49}; +static const unsigned usb1_muxvals[] = {0, 0}; +static const unsigned usb2_pins[] = {50, 51}; +static const unsigned usb2_muxvals[] = {0, 0}; +static const unsigned usb3_pins[] = {52, 53}; +static const unsigned usb3_muxvals[] = {0, 0}; + +static const struct uniphier_pinctrl_group uniphier_ld20_groups[] = { + UNIPHIER_PINCTRL_GROUP(emmc), + UNIPHIER_PINCTRL_GROUP(emmc_dat8), + UNIPHIER_PINCTRL_GROUP(i2c0), + UNIPHIER_PINCTRL_GROUP(i2c1), + UNIPHIER_PINCTRL_GROUP(i2c3), + UNIPHIER_PINCTRL_GROUP(i2c4), + UNIPHIER_PINCTRL_GROUP(nand), + UNIPHIER_PINCTRL_GROUP(nand_cs1), + UNIPHIER_PINCTRL_GROUP(sd), /* SD does not exist for LD11 */ + UNIPHIER_PINCTRL_GROUP(uart0), + UNIPHIER_PINCTRL_GROUP(uart1), + UNIPHIER_PINCTRL_GROUP(uart2), + UNIPHIER_PINCTRL_GROUP(uart3), + UNIPHIER_PINCTRL_GROUP(usb0), + UNIPHIER_PINCTRL_GROUP(usb1), + UNIPHIER_PINCTRL_GROUP(usb2), + UNIPHIER_PINCTRL_GROUP(usb3), /* USB3 does not exist for LD11 */ +}; + +static const char * const uniphier_ld20_functions[] = { + "emmc", + "i2c0", + "i2c1", + "i2c3", + "i2c4", + "nand", + "sd", /* SD does not exist for LD11 */ + "uart0", + "uart1", + "uart2", + "uart3", + "usb0", + "usb1", + "usb2", + "usb3", /* USB3 does not exist for LD11 */ +}; + +static struct uniphier_pinctrl_socdata uniphier_ld20_pinctrl_socdata = { + .groups = uniphier_ld20_groups, + .groups_count = ARRAY_SIZE(uniphier_ld20_groups), + .functions = uniphier_ld20_functions, + .functions_count = ARRAY_SIZE(uniphier_ld20_functions), + .caps = UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL, +}; + +static int uniphier_ld20_pinctrl_probe(struct udevice *dev) +{ + return uniphier_pinctrl_probe(dev, &uniphier_ld20_pinctrl_socdata); +} + +static const struct udevice_id uniphier_ld20_pinctrl_match[] = { + { .compatible = "socionext,ph1-ld11-pinctrl" }, + { .compatible = "socionext,ph1-ld20-pinctrl" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(uniphier_ld20_pinctrl) = { + .name = "uniphier-ld20-pinctrl", + .id = UCLASS_PINCTRL, + .of_match = of_match_ptr(uniphier_ld20_pinctrl_match), + .probe = uniphier_ld20_pinctrl_probe, + .remove = uniphier_pinctrl_remove, + .priv_auto_alloc_size = sizeof(struct uniphier_pinctrl_priv), + .ops = &uniphier_pinctrl_ops, +}; diff --git a/drivers/pinctrl/uniphier/pinctrl-ph1-ld4.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld4.c index b3d47f0..8f7574e 100644 --- a/drivers/pinctrl/uniphier/pinctrl-ph1-ld4.c +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld4.c @@ -107,9 +107,6 @@ static struct uniphier_pinctrl_socdata ph1_ld4_pinctrl_socdata = { .groups_count = ARRAY_SIZE(ph1_ld4_groups), .functions = ph1_ld4_functions, .functions_count = ARRAY_SIZE(ph1_ld4_functions), - .mux_bits = 8, - .reg_stride = 4, - .load_pinctrl = false, }; static int ph1_ld4_pinctrl_probe(struct udevice *dev) diff --git a/drivers/pinctrl/uniphier/pinctrl-ph1-ld6b.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c index 8703a21..2a5d5f3 100644 --- a/drivers/pinctrl/uniphier/pinctrl-ph1-ld6b.c +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c @@ -107,9 +107,6 @@ static struct uniphier_pinctrl_socdata ph1_ld6b_pinctrl_socdata = { .groups_count = ARRAY_SIZE(ph1_ld6b_groups), .functions = ph1_ld6b_functions, .functions_count = ARRAY_SIZE(ph1_ld6b_functions), - .mux_bits = 8, - .reg_stride = 4, - .load_pinctrl = false, }; static int ph1_ld6b_pinctrl_probe(struct udevice *dev) diff --git a/drivers/pinctrl/uniphier/pinctrl-ph1-pro4.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c index b3eaf13..60fbbaf 100644 --- a/drivers/pinctrl/uniphier/pinctrl-ph1-pro4.c +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c @@ -103,9 +103,7 @@ static struct uniphier_pinctrl_socdata ph1_pro4_pinctrl_socdata = { .groups_count = ARRAY_SIZE(ph1_pro4_groups), .functions = ph1_pro4_functions, .functions_count = ARRAY_SIZE(ph1_pro4_functions), - .mux_bits = 4, - .reg_stride = 8, - .load_pinctrl = true, + .caps = UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE, }; static int ph1_pro4_pinctrl_probe(struct udevice *dev) diff --git a/drivers/pinctrl/uniphier/pinctrl-ph1-pro5.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c index 3749250..30c9b4d 100644 --- a/drivers/pinctrl/uniphier/pinctrl-ph1-pro5.c +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c @@ -117,9 +117,7 @@ static struct uniphier_pinctrl_socdata ph1_pro5_pinctrl_socdata = { .groups_count = ARRAY_SIZE(ph1_pro5_groups), .functions = ph1_pro5_functions, .functions_count = ARRAY_SIZE(ph1_pro5_functions), - .mux_bits = 4, - .reg_stride = 8, - .load_pinctrl = true, + .caps = UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE, }; static int ph1_pro5_pinctrl_probe(struct udevice *dev) diff --git a/drivers/pinctrl/uniphier/pinctrl-proxstream2.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs2.c index 2cca69d..976bb2f 100644 --- a/drivers/pinctrl/uniphier/pinctrl-proxstream2.c +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs2.c @@ -114,9 +114,6 @@ static struct uniphier_pinctrl_socdata proxstream2_pinctrl_socdata = { .groups_count = ARRAY_SIZE(proxstream2_groups), .functions = proxstream2_functions, .functions_count = ARRAY_SIZE(proxstream2_functions), - .mux_bits = 8, - .reg_stride = 4, - .load_pinctrl = false, }; static int proxstream2_pinctrl_probe(struct udevice *dev) diff --git a/drivers/pinctrl/uniphier/pinctrl-ph1-sld8.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-sld8.c index 5fafdb6..6cbf215 100644 --- a/drivers/pinctrl/uniphier/pinctrl-ph1-sld8.c +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-sld8.c @@ -115,9 +115,6 @@ static struct uniphier_pinctrl_socdata ph1_sld8_pinctrl_socdata = { .groups_count = ARRAY_SIZE(ph1_sld8_groups), .functions = ph1_sld8_functions, .functions_count = ARRAY_SIZE(ph1_sld8_functions), - .mux_bits = 8, - .reg_stride = 4, - .load_pinctrl = false, }; static int ph1_sld8_pinctrl_probe(struct udevice *dev) diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier.h b/drivers/pinctrl/uniphier/pinctrl-uniphier.h index 6bdebf2..2b43848 100644 --- a/drivers/pinctrl/uniphier/pinctrl-uniphier.h +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier.h @@ -7,6 +7,7 @@ #ifndef __PINCTRL_UNIPHIER_H__ #define __PINCTRL_UNIPHIER_H__ +#include <linux/bitops.h> #include <linux/bug.h> #include <linux/kernel.h> #include <linux/types.h> @@ -59,8 +60,7 @@ struct uniphier_pinctrl_group { * @functions_count: number of pinmux functions * @mux_bits: bit width of each pinmux register * @reg_stride: stride of pinmux register address - * @load_pinctrl: if true, LOAD_PINMUX register must be set to one for new - * values in pinmux registers to become really effective + * @caps: SoC-specific capability flag */ struct uniphier_pinctrl_socdata { const struct uniphier_pinctrl_pin *pins; @@ -69,9 +69,9 @@ struct uniphier_pinctrl_socdata { int groups_count; const char * const *functions; int functions_count; - unsigned mux_bits; - unsigned reg_stride; - bool load_pinctrl; + unsigned caps; +#define UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL BIT(1) +#define UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE BIT(0) }; #define UNIPHIER_PINCTRL_PIN(a, b) \ diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index adc6455..3c41bca 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -10,7 +10,7 @@ choice default AXP209_POWER if MACH_SUN4I || MACH_SUN5I || MACH_SUN7I default AXP221_POWER if MACH_SUN6I || MACH_SUN8I_A23 || MACH_SUN8I_A33 default AXP818_POWER if MACH_SUN8I_A83T - default SUNXI_NO_PMIC if MACH_SUN8I_H3 + default SUNXI_NO_PMIC if MACH_SUN8I_H3 || MACH_SUN50I config SUNXI_NO_PMIC boolean "board without a pmic" @@ -118,13 +118,12 @@ config AXP_DCDC4_VOLT config AXP_DCDC5_VOLT int "axp pmic dcdc5 voltage" depends on AXP221_POWER || AXP818_POWER - default 1800 if AXP818_POWER default 1500 if MACH_SUN6I || MACH_SUN8I ---help--- Set the voltage (mV) to program the axp pmic dcdc5 at, set to 0 to disable dcdc5. On A23 / A31 / A33 / A83T boards dcdc5 is VCC-DRAM and should be 1.5V, - 1.8V for A83T. + 1.35V if DDR3L is used. config AXP_ALDO1_VOLT int "axp pmic (a)ldo1 voltage" @@ -239,6 +238,33 @@ config AXP_ELDO3_VOLT 1.2V for the SSD2828 chip (converter of parallel LCD interface into MIPI DSI). +config AXP_FLDO1_VOLT + int "axp pmic fldo1 voltage" + depends on AXP818_POWER + default 0 if MACH_SUN8I_A83T + ---help--- + Set the voltage (mV) to program the axp pmic fldo1 at, set to 0 to + disable fldo1. + On A83T / H8 boards fldo1 is VCC-HSIC and should be 1.2V if HSIC is + used. + +config AXP_FLDO2_VOLT + int "axp pmic eldo2 voltage" + depends on AXP818_POWER + default 900 if MACH_SUN8I_A83T + ---help--- + Set the voltage (mV) to program the axp pmic fldo2 at, set to 0 to + disable fldo2. + On A83T / H8 boards fldo2 is VCC-CPUS and should be 0.9V. + +config AXP_FLDO3_VOLT + int "axp pmic fldo3 voltage" + depends on AXP818_POWER + default 0 + ---help--- + Set the voltage (mV) to program the axp pmic fldo3 at, set to 0 to + disable fldo3. + config SY8106A_VOUT1_VOLT int "SY8106A pmic VOUT1 voltage" depends on SY8106A_POWER diff --git a/drivers/power/axp818.c b/drivers/power/axp818.c index e885d02..3ac05ff 100644 --- a/drivers/power/axp818.c +++ b/drivers/power/axp818.c @@ -191,6 +191,40 @@ int axp_set_eldo(int eldo_num, unsigned int mvolt) AXP818_OUTPUT_CTRL2_ELDO1_EN << (eldo_num - 1)); } +int axp_set_fldo(int fldo_num, unsigned int mvolt) +{ + int ret; + u8 cfg; + + if (fldo_num < 1 || fldo_num > 3) + return -EINVAL; + + if (mvolt == 0) + return pmic_bus_clrbits(AXP818_OUTPUT_CTRL3, + AXP818_OUTPUT_CTRL3_FLDO1_EN << (fldo_num - 1)); + + if (fldo_num < 3) { + cfg = axp818_mvolt_to_cfg(mvolt, 700, 1450, 50); + ret = pmic_bus_write(AXP818_FLDO1_CTRL + (fldo_num - 1), cfg); + } else { + /* + * Special case for FLDO3, which is DCDC5 / 2 or FLDOIN / 2 + * Since FLDOIN is unknown, test against DCDC5. + */ + if (mvolt * 2 == CONFIG_AXP_DCDC5_VOLT) + ret = pmic_bus_clrbits(AXP818_FLDO2_3_CTRL, + AXP818_FLDO2_3_CTRL_FLDO3_VOL); + else + ret = pmic_bus_setbits(AXP818_FLDO2_3_CTRL, + AXP818_FLDO2_3_CTRL_FLDO3_VOL); + } + if (ret) + return ret; + + return pmic_bus_setbits(AXP818_OUTPUT_CTRL3, + AXP818_OUTPUT_CTRL3_FLDO1_EN << (fldo_num - 1)); +} + int axp_init(void) { u8 axp_chip_id; diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 7f69ae1..69f8d51 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -54,6 +54,22 @@ config DM_PMIC_MAX77686 This config enables implementation of driver-model pmic uclass features for PMIC MAX77686. The driver implements read/write operations. +config PMIC_PM8916 + bool "Enable Driver Model for Qualcomm PM8916 PMIC" + depends on DM_PMIC + ---help--- + The PM8916 is a PMIC connected to one (or several) processors + with SPMI bus. It has 2 slaves with several peripherals: + - 18x LDO + - 4x GPIO + - Power and Reset buttons + - Watchdog + - RTC + - Vibrator drivers + - Others + + Driver binding info: doc/device-tree-bindings/pmic/pm8916.txt + config PMIC_RK808 bool "Enable support for Rockchip PMIC RK808" depends on DM_PMIC diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index c6e8d0c..52b4f71 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_DM_PMIC_PFUZE100) += pfuze100.o obj-$(CONFIG_PMIC_S2MPS11) += s2mps11.o obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o obj-$(CONFIG_PMIC_ACT8846) += act8846.o +obj-$(CONFIG_PMIC_PM8916) += pm8916.o obj-$(CONFIG_PMIC_RK808) += rk808.o obj-$(CONFIG_PMIC_TPS65090) += tps65090.o obj-$(CONFIG_PMIC_S5M8767) += s5m8767.o diff --git a/drivers/power/pmic/pm8916.c b/drivers/power/pmic/pm8916.c new file mode 100644 index 0000000..9acf5f5 --- /dev/null +++ b/drivers/power/pmic/pm8916.c @@ -0,0 +1,96 @@ +/* + * Qualcomm pm8916 pmic driver + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <dm.h> +#include <dm/root.h> +#include <power/pmic.h> +#include <spmi/spmi.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define PID_SHIFT 8 +#define PID_MASK (0xFF << PID_SHIFT) +#define REG_MASK 0xFF + +struct pm8916_priv { + uint16_t usid; /* Slave ID on SPMI bus */ +}; + +static int pm8916_reg_count(struct udevice *dev) +{ + return 0xFFFF; +} + +static int pm8916_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + struct pm8916_priv *priv = dev_get_priv(dev); + + if (len != 1) + return -EINVAL; + + return spmi_reg_write(dev->parent, priv->usid, + (reg & PID_MASK) >> PID_SHIFT, reg & REG_MASK, + *buff); +} + +static int pm8916_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + struct pm8916_priv *priv = dev_get_priv(dev); + int val; + + if (len != 1) + return -EINVAL; + + val = spmi_reg_read(dev->parent, priv->usid, + (reg & PID_MASK) >> PID_SHIFT, reg & REG_MASK); + + if (val < 0) + return val; + *buff = val; + return 0; +} + +static struct dm_pmic_ops pm8916_ops = { + .reg_count = pm8916_reg_count, + .read = pm8916_read, + .write = pm8916_write, +}; + +static const struct udevice_id pm8916_ids[] = { + { .compatible = "qcom,spmi-pmic" }, + { } +}; + +static int pm8916_probe(struct udevice *dev) +{ + struct pm8916_priv *priv = dev_get_priv(dev); + + priv->usid = dev_get_addr(dev); + + if (priv->usid == FDT_ADDR_T_NONE) + return -EINVAL; + + return 0; +} + + +static int pm8916_bind(struct udevice *dev) +{ + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); +} + +U_BOOT_DRIVER(pmic_pm8916) = { + .name = "pmic_pm8916", + .id = UCLASS_PMIC, + .of_match = pm8916_ids, + .bind = pm8916_bind, + .probe = pm8916_probe, + .ops = &pm8916_ops, + .priv_auto_alloc_size = sizeof(struct pm8916_priv), +}; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 92d4212..a9a5d47 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -169,6 +169,15 @@ config DEBUG_UART_PIC32 will need to provide parameters to make this work. The driver will be available until the real driver model serial is running. +config DEBUG_UART_UNIPHIER + bool "UniPhier on-chip UART" + depends on ARCH_UNIPHIER + help + Select this to enable a debug UART using the UniPhier on-chip UART. + You will need to provide DEBUG_UART_BASE to make this work. The + driver will be available until the real driver-model serial is + running. + endchoice config DEBUG_UART_BASE @@ -311,4 +320,12 @@ config XILINX_UARTLITE If you have a Xilinx based board and want to use the uartlite serial ports, say Y to this option. If unsure, say N. +config MSM_SERIAL + bool "Qualcomm on-chip UART" + depends on DM_SERIAL + help + Support Data Mover UART used on Qualcomm Snapdragon SoCs. + It should support all Qualcomm devices with UARTDM version 1.4, + for example APQ8016 and MSM8916. + Single baudrate is supported in current implementation (115200). endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 05bdf56..b0ac9d8 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -38,6 +38,8 @@ obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o obj-$(CONFIG_STM32_SERIAL) += serial_stm32.o obj-$(CONFIG_PIC32_SERIAL) += serial_pic32.o obj-$(CONFIG_STM32X7_SERIAL) += serial_stm32x7.o +obj-$(CONFIG_BCM283X_MU_SERIAL) += serial_bcm283x_mu.o +obj-$(CONFIG_MSM_SERIAL) += serial_msm.o ifndef CONFIG_SPL_BUILD obj-$(CONFIG_USB_TTY) += usbtty.o diff --git a/drivers/serial/serial_arc.c b/drivers/serial/serial_arc.c index 6292eb1..326a536 100644 --- a/drivers/serial/serial_arc.c +++ b/drivers/serial/serial_arc.c @@ -42,23 +42,7 @@ static int arc_serial_setbrg(struct udevice *dev, int baudrate) int arc_console_baud = gd->cpu_clk / (baudrate * 4) - 1; writeb(arc_console_baud & 0xff, ®s->baudl); - -#ifdef CONFIG_ARC - /* - * UART ISS(Instruction Set simulator) emulation has a subtle bug: - * A existing value of Baudh = 0 is used as a indication to startup - * it's internal state machine. - * Thus if baudh is set to 0, 2 times, it chokes. - * This happens with BAUD=115200 and the formaula above - * Until that is fixed, when running on ISS, we will set baudh to !0 - */ - if (gd->arch.running_on_hw) - writeb((arc_console_baud & 0xff00) >> 8, ®s->baudh); - else - writeb(1, ®s->baudh); -#else writeb((arc_console_baud & 0xff00) >> 8, ®s->baudh); -#endif return 0; } diff --git a/drivers/serial/serial_bcm283x_mu.c b/drivers/serial/serial_bcm283x_mu.c new file mode 100644 index 0000000..fc36bc0 --- /dev/null +++ b/drivers/serial/serial_bcm283x_mu.c @@ -0,0 +1,140 @@ +/* + * (C) Copyright 2016 Stephen Warren <swarren@wwwdotorg.org> + * + * Derived from pl01x code: + * + * (C) Copyright 2000 + * Rob Taylor, Flying Pig Systems. robt@flyingpig.com. + * + * (C) Copyright 2004 + * ARM Ltd. + * Philippe Robin, <philippe.robin@arm.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* Simple U-Boot driver for the BCM283x mini UART */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <watchdog.h> +#include <asm/io.h> +#include <serial.h> +#include <dm/platform_data/serial_bcm283x_mu.h> +#include <linux/compiler.h> +#include <fdtdec.h> + +struct bcm283x_mu_regs { + u32 io; + u32 iir; + u32 ier; + u32 lcr; + u32 mcr; + u32 lsr; + u32 msr; + u32 scratch; + u32 cntl; + u32 stat; + u32 baud; +}; + +#define BCM283X_MU_LCR_DATA_SIZE_8 3 + +#define BCM283X_MU_LSR_TX_IDLE BIT(6) +/* This actually means not full, but is named not empty in the docs */ +#define BCM283X_MU_LSR_TX_EMPTY BIT(5) +#define BCM283X_MU_LSR_RX_READY BIT(0) + +struct bcm283x_mu_priv { + struct bcm283x_mu_regs *regs; +}; + +static int bcm283x_mu_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev); + struct bcm283x_mu_priv *priv = dev_get_priv(dev); + struct bcm283x_mu_regs *regs = priv->regs; + u32 divider; + + if (plat->skip_init) + return 0; + + divider = plat->clock / (baudrate * 8); + + writel(BCM283X_MU_LCR_DATA_SIZE_8, ®s->lcr); + writel(divider - 1, ®s->baud); + + return 0; +} + +static int bcm283x_mu_serial_probe(struct udevice *dev) +{ + struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev); + struct bcm283x_mu_priv *priv = dev_get_priv(dev); + + priv->regs = (struct bcm283x_mu_regs *)plat->base; + + return 0; +} + +static int bcm283x_mu_serial_getc(struct udevice *dev) +{ + struct bcm283x_mu_priv *priv = dev_get_priv(dev); + struct bcm283x_mu_regs *regs = priv->regs; + u32 data; + + /* Wait until there is data in the FIFO */ + if (!(readl(®s->lsr) & BCM283X_MU_LSR_RX_READY)) + return -EAGAIN; + + data = readl(®s->io); + + return (int)data; +} + +static int bcm283x_mu_serial_putc(struct udevice *dev, const char data) +{ + struct bcm283x_mu_priv *priv = dev_get_priv(dev); + struct bcm283x_mu_regs *regs = priv->regs; + + /* Wait until there is space in the FIFO */ + if (!(readl(®s->lsr) & BCM283X_MU_LSR_TX_EMPTY)) + return -EAGAIN; + + /* Send the character */ + writel(data, ®s->io); + + return 0; +} + +static int bcm283x_mu_serial_pending(struct udevice *dev, bool input) +{ + struct bcm283x_mu_priv *priv = dev_get_priv(dev); + struct bcm283x_mu_regs *regs = priv->regs; + unsigned int lsr = readl(®s->lsr); + + if (input) { + WATCHDOG_RESET(); + return lsr & BCM283X_MU_LSR_RX_READY; + } else { + return !(lsr & BCM283X_MU_LSR_TX_IDLE); + } +} + +static const struct dm_serial_ops bcm283x_mu_serial_ops = { + .putc = bcm283x_mu_serial_putc, + .pending = bcm283x_mu_serial_pending, + .getc = bcm283x_mu_serial_getc, + .setbrg = bcm283x_mu_serial_setbrg, +}; + +U_BOOT_DRIVER(serial_bcm283x_mu) = { + .name = "serial_bcm283x_mu", + .id = UCLASS_SERIAL, + .platdata_auto_alloc_size = sizeof(struct bcm283x_mu_serial_platdata), + .probe = bcm283x_mu_serial_probe, + .ops = &bcm283x_mu_serial_ops, + .flags = DM_FLAG_PRE_RELOC, + .priv_auto_alloc_size = sizeof(struct bcm283x_mu_priv), +}; diff --git a/drivers/serial/serial_msm.c b/drivers/serial/serial_msm.c new file mode 100644 index 0000000..80fb89e --- /dev/null +++ b/drivers/serial/serial_msm.c @@ -0,0 +1,217 @@ +/* + * Qualcomm UART driver + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * UART will work in Data Mover mode. + * Based on Linux driver. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <errno.h> +#include <serial.h> +#include <watchdog.h> +#include <asm/io.h> +#include <linux/compiler.h> + +/* Serial registers - this driver works in uartdm mode*/ + +#define UARTDM_DMRX 0x34 /* Max RX transfer length */ +#define UARTDM_NCF_TX 0x40 /* Number of chars to TX */ + +#define UARTDM_RXFS 0x50 /* RX channel status register */ +#define UARTDM_RXFS_BUF_SHIFT 0x7 /* Number of bytes in the packing buffer */ +#define UARTDM_RXFS_BUF_MASK 0x7 + +#define UARTDM_SR 0xA4 /* Status register */ +#define UARTDM_SR_RX_READY (1 << 0) /* Word is the receiver FIFO */ +#define UARTDM_SR_TX_EMPTY (1 << 3) /* Transmitter underrun */ +#define UARTDM_SR_UART_OVERRUN (1 << 4) /* Receive overrun */ + +#define UARTDM_CR 0xA8 /* Command register */ +#define UARTDM_CR_CMD_RESET_ERR (3 << 4) /* Clear overrun error */ +#define UARTDM_CR_CMD_RESET_STALE_INT (8 << 4) /* Clears stale irq */ +#define UARTDM_CR_CMD_RESET_TX_READY (3 << 8) /* Clears TX Ready irq*/ +#define UARTDM_CR_CMD_FORCE_STALE (4 << 8) /* Causes stale event */ +#define UARTDM_CR_CMD_STALE_EVENT_DISABLE (6 << 8) /* Disable stale event */ + +#define UARTDM_IMR 0xB0 /* Interrupt mask register */ +#define UARTDM_ISR 0xB4 /* Interrupt status register */ +#define UARTDM_ISR_TX_READY 0x80 /* TX FIFO empty */ + +#define UARTDM_TF 0x100 /* UART Transmit FIFO register */ +#define UARTDM_RF 0x140 /* UART Receive FIFO register */ + + +DECLARE_GLOBAL_DATA_PTR; + +struct msm_serial_data { + phys_addr_t base; + unsigned chars_cnt; /* number of buffered chars */ + uint32_t chars_buf; /* buffered chars */ +}; + +static int msm_serial_fetch(struct udevice *dev) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + unsigned sr; + + if (priv->chars_cnt) + return priv->chars_cnt; + + /* Clear error in case of buffer overrun */ + if (readl(priv->base + UARTDM_SR) & UARTDM_SR_UART_OVERRUN) + writel(UARTDM_CR_CMD_RESET_ERR, priv->base + UARTDM_CR); + + /* We need to fetch new character */ + sr = readl(priv->base + UARTDM_SR); + + if (sr & UARTDM_SR_RX_READY) { + /* There are at least 4 bytes in fifo */ + priv->chars_buf = readl(priv->base + UARTDM_RF); + priv->chars_cnt = 4; + } else { + /* Check if there is anything in fifo */ + priv->chars_cnt = readl(priv->base + UARTDM_RXFS); + /* Extract number of characters in UART packing buffer*/ + priv->chars_cnt = (priv->chars_cnt >> + UARTDM_RXFS_BUF_SHIFT) & + UARTDM_RXFS_BUF_MASK; + if (!priv->chars_cnt) + return 0; + + /* There is at least one charcter, move it to fifo */ + writel(UARTDM_CR_CMD_FORCE_STALE, + priv->base + UARTDM_CR); + + priv->chars_buf = readl(priv->base + UARTDM_RF); + writel(UARTDM_CR_CMD_RESET_STALE_INT, + priv->base + UARTDM_CR); + writel(0x7, priv->base + UARTDM_DMRX); + } + + return priv->chars_cnt; +} + +static int msm_serial_getc(struct udevice *dev) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + char c; + + if (!msm_serial_fetch(dev)) + return -EAGAIN; + + c = priv->chars_buf & 0xFF; + priv->chars_buf >>= 8; + priv->chars_cnt--; + + return c; +} + +static int msm_serial_putc(struct udevice *dev, const char ch) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + + if (!(readl(priv->base + UARTDM_SR) & UARTDM_SR_TX_EMPTY) && + !(readl(priv->base + UARTDM_ISR) & UARTDM_ISR_TX_READY)) + return -EAGAIN; + + writel(UARTDM_CR_CMD_RESET_TX_READY, priv->base + UARTDM_CR); + + writel(1, priv->base + UARTDM_NCF_TX); + writel(ch, priv->base + UARTDM_TF); + + return 0; +} + +static int msm_serial_pending(struct udevice *dev, bool input) +{ + if (input) { + if (msm_serial_fetch(dev)) + return 1; + } + + return 0; +} + +static const struct dm_serial_ops msm_serial_ops = { + .putc = msm_serial_putc, + .pending = msm_serial_pending, + .getc = msm_serial_getc, +}; + +static int msm_uart_clk_init(struct udevice *dev) +{ + uint clk_rate = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, + "clock-frequency", 115200); + uint clkd[2]; /* clk_id and clk_no */ + int clk_offset; + struct udevice *clk; + int ret; + + ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, "clock", clkd, + 2); + if (ret) + return ret; + + clk_offset = fdt_node_offset_by_phandle(gd->fdt_blob, clkd[0]); + if (clk_offset < 0) + return clk_offset; + + ret = uclass_get_device_by_of_offset(UCLASS_CLK, clk_offset, &clk); + if (ret) + return ret; + + ret = clk_set_periph_rate(clk, clkd[1], clk_rate); + if (ret < 0) + return ret; + + return 0; +} + +static int msm_serial_probe(struct udevice *dev) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + + msm_uart_clk_init(dev); /* Ignore return value and hope clock was + properly initialized by earlier loaders */ + + if (readl(priv->base + UARTDM_SR) & UARTDM_SR_UART_OVERRUN) + writel(UARTDM_CR_CMD_RESET_ERR, priv->base + UARTDM_CR); + + writel(0, priv->base + UARTDM_IMR); + writel(UARTDM_CR_CMD_STALE_EVENT_DISABLE, priv->base + UARTDM_CR); + msm_serial_fetch(dev); + + return 0; +} + +static int msm_serial_ofdata_to_platdata(struct udevice *dev) +{ + struct msm_serial_data *priv = dev_get_priv(dev); + + priv->base = dev_get_addr(dev); + if (priv->base == FDT_ADDR_T_NONE) + return -EINVAL; + + return 0; +} + +static const struct udevice_id msm_serial_ids[] = { + { .compatible = "qcom,msm-uartdm-v1.4" }, + { } +}; + +U_BOOT_DRIVER(serial_msm) = { + .name = "serial_msm", + .id = UCLASS_SERIAL, + .of_match = msm_serial_ids, + .ofdata_to_platdata = msm_serial_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct msm_serial_data), + .probe = msm_serial_probe, + .ops = &msm_serial_ops, +}; diff --git a/drivers/serial/serial_pl01x.c b/drivers/serial/serial_pl01x.c index 552c945..6f83835 100644 --- a/drivers/serial/serial_pl01x.c +++ b/drivers/serial/serial_pl01x.c @@ -284,7 +284,10 @@ static int pl01x_serial_setbrg(struct udevice *dev, int baudrate) struct pl01x_serial_platdata *plat = dev_get_platdata(dev); struct pl01x_priv *priv = dev_get_priv(dev); - pl01x_generic_setbrg(priv->regs, priv->type, plat->clock, baudrate); + if (!plat->skip_init) { + pl01x_generic_setbrg(priv->regs, priv->type, plat->clock, + baudrate); + } return 0; } @@ -296,7 +299,10 @@ static int pl01x_serial_probe(struct udevice *dev) priv->regs = (struct pl01x_regs *)plat->base; priv->type = plat->type; - return pl01x_generic_serial_init(priv->regs, priv->type); + if (!plat->skip_init) + return pl01x_generic_serial_init(priv->regs, priv->type); + else + return 0; } static int pl01x_serial_getc(struct udevice *dev) diff --git a/drivers/serial/serial_uniphier.c b/drivers/serial/serial_uniphier.c index edb9203..525f0a4 100644 --- a/drivers/serial/serial_uniphier.c +++ b/drivers/serial/serial_uniphier.c @@ -6,6 +6,7 @@ #include <linux/io.h> #include <linux/serial_reg.h> +#include <linux/sizes.h> #include <asm/errno.h> #include <dm/device.h> #include <mapmem.h> @@ -91,12 +92,13 @@ static int uniphier_serial_probe(struct udevice *dev) struct uniphier_serial_private_data *priv = dev_get_priv(dev); struct uniphier_serial __iomem *port; fdt_addr_t base; - fdt_size_t size; u32 tmp; - base = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size); + base = dev_get_addr(dev); + if (base == FDT_ADDR_T_NONE) + return -EINVAL; - port = map_sysmem(base, size); + port = map_sysmem(base, SZ_64); if (!port) return -ENOMEM; diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c index 7890796..6851ba9 100644 --- a/drivers/spi/kirkwood_spi.c +++ b/drivers/spi/kirkwood_spi.c @@ -283,6 +283,19 @@ static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen, return _spi_xfer(plat->spireg, bitlen, dout, din, flags); } +static int mvebu_spi_claim_bus(struct udevice *dev) +{ + struct udevice *bus = dev->parent; + struct mvebu_spi_platdata *plat = dev_get_platdata(bus); + + /* Configure the chip-select in the CTRL register */ + clrsetbits_le32(&plat->spireg->ctrl, + KWSPI_CS_MASK << KWSPI_CS_SHIFT, + spi_chip_select(dev) << KWSPI_CS_SHIFT); + + return 0; +} + static int mvebu_spi_probe(struct udevice *bus) { struct mvebu_spi_platdata *plat = dev_get_platdata(bus); @@ -305,6 +318,7 @@ static int mvebu_spi_ofdata_to_platdata(struct udevice *bus) } static const struct dm_spi_ops mvebu_spi_ops = { + .claim_bus = mvebu_spi_claim_bus, .xfer = mvebu_spi_xfer, .set_speed = mvebu_spi_set_speed, .set_mode = mvebu_spi_set_mode, @@ -315,6 +329,7 @@ static const struct dm_spi_ops mvebu_spi_ops = { }; static const struct udevice_id mvebu_spi_ids[] = { + { .compatible = "marvell,armada-375-spi" }, { .compatible = "marvell,armada-380-spi" }, { .compatible = "marvell,armada-xp-spi" }, { } diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig new file mode 100644 index 0000000..8d25b45 --- /dev/null +++ b/drivers/spmi/Kconfig @@ -0,0 +1,23 @@ +menu "SPMI support" + +config SPMI + bool "Enable SPMI bus support" + depends on DM + ---help--- + Select this to enable to support SPMI bus. + SPMI (System Power Management Interface) bus is used + to connect PMIC devices on various SoCs. + +config SPMI_MSM + boolean "Support Qualcomm SPMI bus" + depends on SPMI + ---help--- + Support SPMI bus implementation found on Qualcomm Snapdragon SoCs. + +config SPMI_SANDBOX + boolean "Support for Sandbox SPMI bus" + depends on SPMI + ---help--- + Demo SPMI bus implementation. Emulates part of PM8916 as single + slave (0) on bus. It has 4 GPIO peripherals, pid 0xC0-0xC3. +endmenu diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile new file mode 100644 index 0000000..c0b1220 --- /dev/null +++ b/drivers/spmi/Makefile @@ -0,0 +1,9 @@ +# +# (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_SPMI) += spmi-uclass.o +obj-$(CONFIG_SPMI_MSM) += spmi-msm.o +obj-$(CONFIG_SPMI_SANDBOX) += spmi-sandbox.o diff --git a/drivers/spmi/spmi-msm.c b/drivers/spmi/spmi-msm.c new file mode 100644 index 0000000..0cef505 --- /dev/null +++ b/drivers/spmi/spmi-msm.c @@ -0,0 +1,189 @@ +/* + * Qualcomm SPMI bus driver + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * Loosely based on Little Kernel driver + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <asm/io.h> +#include <spmi/spmi.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define ARB_CHANNEL_OFFSET(n) (0x4 * (n)) +#define SPMI_CH_OFFSET(chnl) ((chnl) * 0x8000) + +#define SPMI_REG_CMD0 0x0 +#define SPMI_REG_CONFIG 0x4 +#define SPMI_REG_STATUS 0x8 +#define SPMI_REG_WDATA 0x10 +#define SPMI_REG_RDATA 0x18 + +#define SPMI_CMD_OPCODE_SHIFT 27 +#define SPMI_CMD_SLAVE_ID_SHIFT 20 +#define SPMI_CMD_ADDR_SHIFT 12 +#define SPMI_CMD_ADDR_OFFSET_SHIFT 4 +#define SPMI_CMD_BYTE_CNT_SHIFT 0 + +#define SPMI_CMD_EXT_REG_WRITE_LONG 0x00 +#define SPMI_CMD_EXT_REG_READ_LONG 0x01 + +#define SPMI_STATUS_DONE 0x1 + +#define SPMI_MAX_CHANNELS 128 +#define SPMI_MAX_SLAVES 16 +#define SPMI_MAX_PERIPH 256 + +struct msm_spmi_priv { + phys_addr_t arb_chnl; /* ARB channel mapping base */ + phys_addr_t spmi_core; /* SPMI core */ + phys_addr_t spmi_obs; /* SPMI observer */ + /* SPMI channel map */ + uint8_t channel_map[SPMI_MAX_SLAVES][SPMI_MAX_PERIPH]; +}; + +static int msm_spmi_write(struct udevice *dev, int usid, int pid, int off, + uint8_t val) +{ + struct msm_spmi_priv *priv = dev_get_priv(dev); + unsigned channel; + uint32_t reg = 0; + + if (usid >= SPMI_MAX_SLAVES) + return -EIO; + if (pid >= SPMI_MAX_PERIPH) + return -EIO; + + channel = priv->channel_map[usid][pid]; + + /* Disable IRQ mode for the current channel*/ + writel(0x0, priv->spmi_core + SPMI_CH_OFFSET(channel) + + SPMI_REG_CONFIG); + + /* Write single byte */ + writel(val, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_WDATA); + + /* Prepare write command */ + reg |= SPMI_CMD_EXT_REG_WRITE_LONG << SPMI_CMD_OPCODE_SHIFT; + reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT); + reg |= (pid << SPMI_CMD_ADDR_SHIFT); + reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT); + reg |= 1; /* byte count */ + + /* Send write command */ + writel(reg, priv->spmi_core + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0); + + /* Wait till CMD DONE status */ + reg = 0; + while (!reg) { + reg = readl(priv->spmi_core + SPMI_CH_OFFSET(channel) + + SPMI_REG_STATUS); + } + + if (reg ^ SPMI_STATUS_DONE) { + printf("SPMI write failure.\n"); + return -EIO; + } + + return 0; +} + +static int msm_spmi_read(struct udevice *dev, int usid, int pid, int off) +{ + struct msm_spmi_priv *priv = dev_get_priv(dev); + unsigned channel; + uint32_t reg = 0; + + if (usid >= SPMI_MAX_SLAVES) + return -EIO; + if (pid >= SPMI_MAX_PERIPH) + return -EIO; + + channel = priv->channel_map[usid][pid]; + + /* Disable IRQ mode for the current channel*/ + writel(0x0, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CONFIG); + + /* Prepare read command */ + reg |= SPMI_CMD_EXT_REG_READ_LONG << SPMI_CMD_OPCODE_SHIFT; + reg |= (usid << SPMI_CMD_SLAVE_ID_SHIFT); + reg |= (pid << SPMI_CMD_ADDR_SHIFT); + reg |= (off << SPMI_CMD_ADDR_OFFSET_SHIFT); + reg |= 1; /* byte count */ + + /* Request read */ + writel(reg, priv->spmi_obs + SPMI_CH_OFFSET(channel) + SPMI_REG_CMD0); + + /* Wait till CMD DONE status */ + reg = 0; + while (!reg) { + reg = readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) + + SPMI_REG_STATUS); + } + + if (reg ^ SPMI_STATUS_DONE) { + printf("SPMI read failure.\n"); + return -EIO; + } + + /* Read the data */ + return readl(priv->spmi_obs + SPMI_CH_OFFSET(channel) + + SPMI_REG_RDATA) & 0xFF; +} + +static struct dm_spmi_ops msm_spmi_ops = { + .read = msm_spmi_read, + .write = msm_spmi_write, +}; + +static int msm_spmi_probe(struct udevice *dev) +{ + struct udevice *parent = dev->parent; + struct msm_spmi_priv *priv = dev_get_priv(dev); + int i; + + priv->arb_chnl = dev_get_addr(dev); + priv->spmi_core = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, + parent->of_offset, + dev->of_offset, + "reg", 1, NULL); + priv->spmi_obs = fdtdec_get_addr_size_auto_parent(gd->fdt_blob, + parent->of_offset, + dev->of_offset, "reg", + 2, NULL); + if (priv->arb_chnl == FDT_ADDR_T_NONE || + priv->spmi_core == FDT_ADDR_T_NONE || + priv->spmi_obs == FDT_ADDR_T_NONE) + return -EINVAL; + + /* Scan peripherals connected to each SPMI channel */ + for (i = 0; i < SPMI_MAX_CHANNELS ; i++) { + uint32_t periph = readl(priv->arb_chnl + ARB_CHANNEL_OFFSET(i)); + uint8_t slave_id = (periph & 0xf0000) >> 16; + uint8_t pid = (periph & 0xff00) >> 8; + + priv->channel_map[slave_id][pid] = i; + } + return 0; +} + +static const struct udevice_id msm_spmi_ids[] = { + { .compatible = "qcom,spmi-pmic-arb" }, + { } +}; + +U_BOOT_DRIVER(msm_spmi) = { + .name = "msm_spmi", + .id = UCLASS_SPMI, + .of_match = msm_spmi_ids, + .ops = &msm_spmi_ops, + .probe = msm_spmi_probe, + .priv_auto_alloc_size = sizeof(struct msm_spmi_priv), +}; diff --git a/drivers/spmi/spmi-sandbox.c b/drivers/spmi/spmi-sandbox.c new file mode 100644 index 0000000..980aff2 --- /dev/null +++ b/drivers/spmi/spmi-sandbox.c @@ -0,0 +1,158 @@ +/* + * Sample SPMI bus driver + * + * It emulates bus with single pm8916-like pmic that has only GPIO reigsters. + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <spmi/spmi.h> +#include <asm/gpio.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define EMUL_GPIO_PID_START 0xC0 +#define EMUL_GPIO_PID_END 0xC3 + +#define EMUL_GPIO_COUNT 4 + +#define EMUL_GPIO_REG_END 0x46 /* Last valid register */ + +#define EMUL_PERM_R 0x1 +#define EMUL_PERM_W 0x2 +#define EMUL_PERM_RW (EMUL_PERM_R | EMUL_PERM_W) + +struct sandbox_emul_fake_regs { + u8 value; + u8 access_mask; + u8 perms; /* Access permissions */ +}; + +struct sandbox_emul_gpio { + /* Fake registers - need one more entry as REG_END is valid address. */ + struct sandbox_emul_fake_regs r[EMUL_GPIO_REG_END + 1]; +}; + +struct sandbox_spmi_priv { + struct sandbox_emul_gpio gpios[EMUL_GPIO_COUNT]; +}; + +/* Check if valid register was requested */ +static bool check_address_valid(int usid, int pid, int off) +{ + if (usid != 0) + return false; + if (pid < EMUL_GPIO_PID_START || pid > EMUL_GPIO_PID_END) + return false; + if (off > EMUL_GPIO_REG_END) + return false; + return true; +} + +static int sandbox_spmi_write(struct udevice *dev, int usid, int pid, int off, + uint8_t val) +{ + struct sandbox_spmi_priv *priv = dev_get_priv(dev); + struct sandbox_emul_fake_regs *regs; + + if (!check_address_valid(usid, pid, off)) + return -EIO; + + regs = priv->gpios[pid & 0x3].r; /* Last 3 bits of pid are gpio # */ + + switch (off) { + case 0x40: /* Control */ + val &= regs[off].access_mask; + if (((val & 0x30) == 0x10) || ((val & 0x30) == 0x20)) { + /* out/inout - set status register */ + regs[0x8].value &= ~0x1; + regs[0x8].value |= val & 0x1; + } + break; + default: + if (regs[off].perms & EMUL_PERM_W) + regs[off].value = val & regs[off].access_mask; + } + return 0; +} + +static int sandbox_spmi_read(struct udevice *dev, int usid, int pid, int off) +{ + struct sandbox_spmi_priv *priv = dev_get_priv(dev); + struct sandbox_emul_fake_regs *regs; + + if (!check_address_valid(usid, pid, off)) + return -EIO; + + regs = priv->gpios[pid & 0x3].r; /* Last 3 bits of pid are gpio # */ + + if (regs[0x46].value == 0) /* Block disabled */ + return 0; + + switch (off) { + case 0x8: /* Status */ + if (regs[0x46].value == 0) /* Block disabled */ + return 0; + return regs[off].value; + default: + if (regs[off].perms & EMUL_PERM_R) + return regs[off].value; + else + return 0; + } +} + +static struct dm_spmi_ops sandbox_spmi_ops = { + .read = sandbox_spmi_read, + .write = sandbox_spmi_write, +}; + +static int sandbox_spmi_probe(struct udevice *dev) +{ + struct sandbox_spmi_priv *priv = dev_get_priv(dev); + int i; + + for (i = 0; i < EMUL_GPIO_COUNT; ++i) { + struct sandbox_emul_fake_regs *regs = priv->gpios[i].r; + regs[4].perms = EMUL_PERM_R; + regs[4].value = 0x10; + regs[5].perms = EMUL_PERM_R; + regs[5].value = 0x5; + regs[8].access_mask = 0x81; + regs[8].perms = EMUL_PERM_RW; + regs[0x40].access_mask = 0x7F; + regs[0x40].perms = EMUL_PERM_RW; + regs[0x41].access_mask = 7; + regs[0x41].perms = EMUL_PERM_RW; + regs[0x42].access_mask = 7; + regs[0x42].perms = EMUL_PERM_RW; + regs[0x42].value = 0x4; + regs[0x45].access_mask = 0x3F; + regs[0x45].perms = EMUL_PERM_RW; + regs[0x45].value = 0x1; + regs[0x46].access_mask = 0x80; + regs[0x46].perms = EMUL_PERM_RW; + regs[0x46].value = 0x80; + } + return 0; +} + +static const struct udevice_id sandbox_spmi_ids[] = { + { .compatible = "sandbox,spmi" }, + { } +}; + +U_BOOT_DRIVER(msm_spmi) = { + .name = "sandbox_spmi", + .id = UCLASS_SPMI, + .of_match = sandbox_spmi_ids, + .ops = &sandbox_spmi_ops, + .probe = sandbox_spmi_probe, + .priv_auto_alloc_size = sizeof(struct sandbox_spmi_priv), +}; diff --git a/drivers/spmi/spmi-uclass.c b/drivers/spmi/spmi-uclass.c new file mode 100644 index 0000000..4ddd51b --- /dev/null +++ b/drivers/spmi/spmi-uclass.c @@ -0,0 +1,48 @@ +/* + * SPMI bus uclass driver + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <dm/root.h> +#include <spmi/spmi.h> +#include <linux/ctype.h> + +DECLARE_GLOBAL_DATA_PTR; + +int spmi_reg_read(struct udevice *dev, int usid, int pid, int reg) +{ + const struct dm_spmi_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->read) + return -ENOSYS; + + return ops->read(dev, usid, pid, reg); +} + +int spmi_reg_write(struct udevice *dev, int usid, int pid, int reg, + uint8_t value) +{ + const struct dm_spmi_ops *ops = dev_get_driver_ops(dev); + + if (!ops || !ops->write) + return -ENOSYS; + + return ops->write(dev, usid, pid, reg, value); +} + +static int spmi_post_bind(struct udevice *dev) +{ + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); +} + +UCLASS_DRIVER(spmi) = { + .id = UCLASS_SPMI, + .name = "spmi", + .post_bind = spmi_post_bind, +}; diff --git a/drivers/tpm/tpm_tis_sandbox.c b/drivers/tpm/tpm_tis_sandbox.c index 9ea9807..4aade56 100644 --- a/drivers/tpm/tpm_tis_sandbox.c +++ b/drivers/tpm/tpm_tis_sandbox.c @@ -217,7 +217,7 @@ static int sandbox_tpm_xfer(struct udevice *dev, const uint8_t *sendbuf, rsk.struct_version = 2; rsk.uid = ROLLBACK_SPACE_KERNEL_UID; rsk.kernel_versions = 0; - rsk.crc8 = crc8((unsigned char *)&rsk, + rsk.crc8 = crc8(0, (unsigned char *)&rsk, offsetof(struct rollback_space_kernel, crc8)); memcpy(data, &rsk, sizeof(rsk)); diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index abb06fc..bccf43e 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -53,6 +53,8 @@ source "drivers/usb/musb-new/Kconfig" source "drivers/usb/emul/Kconfig" +source "drivers/usb/ulpi/Kconfig" + comment "USB peripherals" config USB_STORAGE @@ -88,4 +90,6 @@ endchoice endif +source "drivers/usb/gadget/Kconfig" + endif diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile new file mode 100644 index 0000000..2f3d43d --- /dev/null +++ b/drivers/usb/common/Makefile @@ -0,0 +1,7 @@ +# (C) Copyright 2016 Freescale Semiconductor, Inc. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_USB_EHCI_FSL) += fsl-dt-fixup.o +obj-$(CONFIG_USB_XHCI_FSL) += fsl-dt-fixup.o diff --git a/drivers/usb/common/fsl-dt-fixup.c b/drivers/usb/common/fsl-dt-fixup.c new file mode 100644 index 0000000..6f31932 --- /dev/null +++ b/drivers/usb/common/fsl-dt-fixup.c @@ -0,0 +1,202 @@ +/* + * (C) Copyright 2009, 2011 Freescale Semiconductor, Inc. + * + * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB + * + * Author: Tor Krill tor@excito.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <usb.h> +#include <asm/io.h> +#include <hwconfig.h> +#include <fsl_usb.h> +#include <fdt_support.h> + +#ifndef CONFIG_USB_MAX_CONTROLLER_COUNT +#define CONFIG_USB_MAX_CONTROLLER_COUNT 1 +#endif + +static const char * const compat_usb_fsl[] = { + "fsl-usb2-mph", + "fsl-usb2-dr", + "snps,dwc3", + NULL +}; + +static int fdt_usb_get_node_type(void *blob, int start_offset, + int *node_offset, const char **node_type) +{ + int i; + int ret = -ENOENT; + + for (i = 0; compat_usb_fsl[i]; i++) { + *node_offset = fdt_node_offset_by_compatible + (blob, start_offset, + compat_usb_fsl[i]); + if (*node_offset >= 0) { + *node_type = compat_usb_fsl[i]; + ret = 0; + break; + } + } + + return ret; +} + +static int fdt_fixup_usb_mode_phy_type(void *blob, const char *mode, + const char *phy_type, int start_offset) +{ + const char *prop_mode = "dr_mode"; + const char *prop_type = "phy_type"; + const char *node_type = NULL; + int node_offset; + int err; + + err = fdt_usb_get_node_type(blob, start_offset, + &node_offset, &node_type); + if (err < 0) + return err; + + if (mode) { + err = fdt_setprop(blob, node_offset, prop_mode, mode, + strlen(mode) + 1); + if (err < 0) + printf("WARNING: could not set %s for %s: %s.\n", + prop_mode, node_type, fdt_strerror(err)); + } + + if (phy_type) { + err = fdt_setprop(blob, node_offset, prop_type, phy_type, + strlen(phy_type) + 1); + if (err < 0) + printf("WARNING: could not set %s for %s: %s.\n", + prop_type, node_type, fdt_strerror(err)); + } + + return node_offset; +} + +static int fdt_fixup_usb_erratum(void *blob, const char *prop_erratum, + int start_offset) +{ + int node_offset, err; + const char *node_type = NULL; + + err = fdt_usb_get_node_type(blob, start_offset, + &node_offset, &node_type); + if (err < 0) + return err; + + err = fdt_setprop(blob, node_offset, prop_erratum, NULL, 0); + if (err < 0) { + printf("ERROR: could not set %s for %s: %s.\n", + prop_erratum, node_type, fdt_strerror(err)); + } + + return node_offset; +} + +void fdt_fixup_dr_usb(void *blob, bd_t *bd) +{ + static const char * const modes[] = { "host", "peripheral", "otg" }; + static const char * const phys[] = { "ulpi", "utmi", "utmi_dual" }; + int usb_erratum_a006261_off = -1; + int usb_erratum_a007075_off = -1; + int usb_erratum_a007792_off = -1; + int usb_erratum_a005697_off = -1; + int usb_mode_off = -1; + int usb_phy_off = -1; + char str[5]; + int i, j; + + for (i = 1; i <= CONFIG_USB_MAX_CONTROLLER_COUNT; i++) { + const char *dr_mode_type = NULL; + const char *dr_phy_type = NULL; + int mode_idx = -1, phy_idx = -1; + + snprintf(str, 5, "%s%d", "usb", i); + if (hwconfig(str)) { + for (j = 0; j < ARRAY_SIZE(modes); j++) { + if (hwconfig_subarg_cmp(str, "dr_mode", + modes[j])) { + mode_idx = j; + break; + } + } + + for (j = 0; j < ARRAY_SIZE(phys); j++) { + if (hwconfig_subarg_cmp(str, "phy_type", + phys[j])) { + phy_idx = j; + break; + } + } + + if (mode_idx < 0 && phy_idx < 0) { + printf("WARNING: invalid phy or mode\n"); + return; + } + + if (mode_idx > -1) + dr_mode_type = modes[mode_idx]; + + if (phy_idx > -1) + dr_phy_type = phys[phy_idx]; + } + + if (has_dual_phy()) + dr_phy_type = phys[2]; + + usb_mode_off = fdt_fixup_usb_mode_phy_type(blob, + dr_mode_type, NULL, + usb_mode_off); + + if (usb_mode_off < 0) + return; + + usb_phy_off = fdt_fixup_usb_mode_phy_type(blob, + NULL, dr_phy_type, + usb_phy_off); + + if (usb_phy_off < 0) + return; + + if (has_erratum_a006261()) { + usb_erratum_a006261_off = fdt_fixup_usb_erratum + (blob, + "fsl,usb-erratum-a006261", + usb_erratum_a006261_off); + if (usb_erratum_a006261_off < 0) + return; + } + + if (has_erratum_a007075()) { + usb_erratum_a007075_off = fdt_fixup_usb_erratum + (blob, + "fsl,usb-erratum-a007075", + usb_erratum_a007075_off); + if (usb_erratum_a007075_off < 0) + return; + } + + if (has_erratum_a007792()) { + usb_erratum_a007792_off = fdt_fixup_usb_erratum + (blob, + "fsl,usb-erratum-a007792", + usb_erratum_a007792_off); + if (usb_erratum_a007792_off < 0) + return; + } + if (has_erratum_a005697()) { + usb_erratum_a005697_off = fdt_fixup_usb_erratum + (blob, + "fsl,usb-erratum-a005697", + usb_erratum_a005697_off); + if (usb_erratum_a005697_off < 0) + return; + } + } +} diff --git a/drivers/usb/eth/asix88179.c b/drivers/usb/eth/asix88179.c index cf4085d..5e1ea86 100644 --- a/drivers/usb/eth/asix88179.c +++ b/drivers/usb/eth/asix88179.c @@ -497,7 +497,7 @@ static int asix_send(struct eth_device *eth, void *packet, int length) length + sizeof(packet_len) + sizeof(tx_hdr2), &actual_len, USB_BULK_SEND_TIMEOUT); - debug("Tx: len = %u, actual = %u, err = %d\n", + debug("Tx: len = %zu, actual = %u, err = %d\n", length + sizeof(packet_len), actual_len, err); return err; diff --git a/drivers/usb/eth/smsc95xx.c b/drivers/usb/eth/smsc95xx.c index 3099bf4..08eaed5 100644 --- a/drivers/usb/eth/smsc95xx.c +++ b/drivers/usb/eth/smsc95xx.c @@ -188,10 +188,10 @@ static int smsc95xx_read_reg(struct usb_device *udev, u32 index, u32 *data) len = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), USB_VENDOR_REQUEST_READ_REGISTER, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, index, tmpbuf, sizeof(data), + 0, index, tmpbuf, sizeof(*data), USB_CTRL_GET_TIMEOUT); *data = tmpbuf[0]; - if (len != sizeof(data)) { + if (len != sizeof(*data)) { debug("smsc95xx_read_reg failed: index=%d, len=%d", index, len); return -EIO; diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig new file mode 100644 index 0000000..f4698f4 --- /dev/null +++ b/drivers/usb/gadget/Kconfig @@ -0,0 +1,35 @@ +# +# USB Gadget support on a system involves +# (a) a peripheral controller, and +# (b) the gadget driver using it. +# +# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !! +# +# - Host systems (like PCs) need CONFIG_USB (with "A" jacks). +# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks). +# - Some systems have both kinds of controllers. +# +# With help from a special transceiver and a "Mini-AB" jack, systems with +# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG). +# + +menuconfig USB_GADGET + bool "USB Gadget Support" + help + USB is a master/slave protocol, organized with one master + host (such as a PC) controlling up to 127 peripheral devices. + The USB hardware is asymmetric, which makes it easier to set up: + you can't connect a "to-the-host" connector to a peripheral. + + U-Boot can run in the host, or in the peripheral. In both cases + you need a low level bus controller driver, and some software + talking to it. Peripheral controllers are often discrete silicon, + or are integrated with the CPU in a microcontroller. The more + familiar host side controllers have names like "EHCI", "OHCI", + or "UHCI", and are usually integrated into southbridges on PC + motherboards. + + Enable this configuration option if you want to run U-Boot inside + a USB peripheral device. Configure one hardware driver for your + peripheral/device side bus controller, and a "gadget driver" for + your peripheral protocol. diff --git a/drivers/usb/gadget/bcm_udc_otg_phy.c b/drivers/usb/gadget/bcm_udc_otg_phy.c index 10b2e13..877f162 100644 --- a/drivers/usb/gadget/bcm_udc_otg_phy.c +++ b/drivers/usb/gadget/bcm_udc_otg_phy.c @@ -8,12 +8,16 @@ #include <common.h> #include <asm/io.h> #include <asm/arch/sysmap.h> +#include <asm/kona-common/clk.h> #include "dwc2_udc_otg_priv.h" #include "bcm_udc_otg.h" void otg_phy_init(struct dwc2_udc *dev) { + /* turn on the USB OTG clocks */ + clk_usb_otg_enable((void *)HSOTG_BASE_ADDR); + /* set Phy to driving mode */ wfld_clear(HSOTG_CTRL_BASE_ADDR + HSOTG_CTRL_PHY_P1CTL_OFFSET, HSOTG_CTRL_PHY_P1CTL_NON_DRIVING_MASK); diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c index a54b4ee..2e87fee 100644 --- a/drivers/usb/gadget/f_fastboot.c +++ b/drivers/usb/gadget/f_fastboot.c @@ -413,8 +413,16 @@ static void cb_getvar(struct usb_ep *ep, struct usb_request *req) else strcpy(response, "FAILValue not set"); } else { - printf("WARNING: unknown variable: %s\n", cmd); - strcpy(response, "FAILVariable not implemented"); + char envstr[32]; + + snprintf(envstr, sizeof(envstr) - 1, "fastboot.%s", cmd); + s = getenv(envstr); + if (s) { + strncat(response, s, chars_left); + } else { + printf("WARNING: unknown variable: %s\n", cmd); + strcpy(response, "FAILVariable not implemented"); + } } fastboot_tx_write_str(response); } diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 9332374..d2363c8 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -74,6 +74,17 @@ config USB_EHCI_MX6 ---help--- Enables support for the on-chip EHCI controller on i.MX6 SoCs. +config USB_EHCI_MSM + bool "Support for Qualcomm on-chip EHCI USB controller" + depends on DM_USB + select USB_ULPI_VIEWPORT + default n + ---help--- + Enables support for the on-chip EHCI controller on Qualcomm + Snapdragon SoCs. + This driver supports combination of Chipidea USB controller + and Synapsys USB PHY in host mode only. + config USB_EHCI_GENERIC bool "Support for generic EHCI USB controller" depends on OF_CONTROL diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 9a87d2b..507519e 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_USB_EHCI_MX7) += ehci-mx6.o obj-$(CONFIG_USB_EHCI_OMAP) += ehci-omap.o obj-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o obj-$(CONFIG_USB_EHCI_MARVELL) += ehci-marvell.o +obj-$(CONFIG_USB_EHCI_MSM) += ehci-msm.o obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o obj-$(CONFIG_USB_EHCI_SPEAR) += ehci-spear.o obj-$(CONFIG_USB_EHCI_SUNXI) += ehci-sunxi.o diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 97b7f14..a43d37d 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -12,7 +12,7 @@ #include <pci.h> #include <usb.h> #include <asm/io.h> -#include <usb/ehci-fsl.h> +#include <usb/ehci-ci.h> #include <hwconfig.h> #include <fsl_usb.h> #include <fdt_support.h> @@ -173,198 +173,3 @@ static void set_txfifothresh(struct usb_ehci *ehci, u32 txfifo_thresh) cmd |= TXFIFO_THRESH(txfifo_thresh); ehci_writel(&ehci->txfilltuning, cmd); } - -#if defined(CONFIG_HAS_FSL_DR_USB) || defined(CONFIG_HAS_FSL_MPH_USB) -static int fdt_fixup_usb_mode_phy_type(void *blob, const char *mode, - const char *phy_type, int start_offset) -{ - const char *compat_dr = "fsl-usb2-dr"; - const char *compat_mph = "fsl-usb2-mph"; - const char *prop_mode = "dr_mode"; - const char *prop_type = "phy_type"; - const char *node_type = NULL; - int node_offset; - int err; - - node_offset = fdt_node_offset_by_compatible(blob, - start_offset, compat_mph); - if (node_offset < 0) { - node_offset = fdt_node_offset_by_compatible(blob, - start_offset, - compat_dr); - if (node_offset < 0) { - printf("WARNING: could not find compatible node: %s", - fdt_strerror(node_offset)); - return -1; - } - node_type = compat_dr; - } else { - node_type = compat_mph; - } - - if (mode) { - err = fdt_setprop(blob, node_offset, prop_mode, mode, - strlen(mode) + 1); - if (err < 0) - printf("WARNING: could not set %s for %s: %s.\n", - prop_mode, node_type, fdt_strerror(err)); - } - - if (phy_type) { - err = fdt_setprop(blob, node_offset, prop_type, phy_type, - strlen(phy_type) + 1); - if (err < 0) - printf("WARNING: could not set %s for %s: %s.\n", - prop_type, node_type, fdt_strerror(err)); - } - - return node_offset; -} - -static const char *fdt_usb_get_node_type(void *blob, int start_offset, - int *node_offset) -{ - const char *compat_dr = "fsl-usb2-dr"; - const char *compat_mph = "fsl-usb2-mph"; - const char *node_type = NULL; - - *node_offset = fdt_node_offset_by_compatible(blob, start_offset, - compat_mph); - if (*node_offset < 0) { - *node_offset = fdt_node_offset_by_compatible(blob, - start_offset, - compat_dr); - if (*node_offset < 0) { - printf("ERROR: could not find compatible node: %s\n", - fdt_strerror(*node_offset)); - } else { - node_type = compat_dr; - } - } else { - node_type = compat_mph; - } - - return node_type; -} - -static int fdt_fixup_usb_erratum(void *blob, const char *prop_erratum, - int start_offset) -{ - int node_offset, err; - const char *node_type = NULL; - - node_type = fdt_usb_get_node_type(blob, start_offset, &node_offset); - if (!node_type) - return -1; - - err = fdt_setprop(blob, node_offset, prop_erratum, NULL, 0); - if (err < 0) { - printf("ERROR: could not set %s for %s: %s.\n", - prop_erratum, node_type, fdt_strerror(err)); - } - - return node_offset; -} - -void fdt_fixup_dr_usb(void *blob, bd_t *bd) -{ - static const char * const modes[] = { "host", "peripheral", "otg" }; - static const char * const phys[] = { "ulpi", "utmi", "utmi_dual" }; - int usb_erratum_a006261_off = -1; - int usb_erratum_a007075_off = -1; - int usb_erratum_a007792_off = -1; - int usb_erratum_a005697_off = -1; - int usb_mode_off = -1; - int usb_phy_off = -1; - char str[5]; - int i, j; - - for (i = 1; i <= CONFIG_USB_MAX_CONTROLLER_COUNT; i++) { - const char *dr_mode_type = NULL; - const char *dr_phy_type = NULL; - int mode_idx = -1, phy_idx = -1; - - snprintf(str, 5, "%s%d", "usb", i); - if (hwconfig(str)) { - for (j = 0; j < ARRAY_SIZE(modes); j++) { - if (hwconfig_subarg_cmp(str, "dr_mode", - modes[j])) { - mode_idx = j; - break; - } - } - - for (j = 0; j < ARRAY_SIZE(phys); j++) { - if (hwconfig_subarg_cmp(str, "phy_type", - phys[j])) { - phy_idx = j; - break; - } - } - - if (mode_idx < 0 && phy_idx < 0) { - printf("WARNING: invalid phy or mode\n"); - return; - } - - if (mode_idx > -1) - dr_mode_type = modes[mode_idx]; - - if (phy_idx > -1) - dr_phy_type = phys[phy_idx]; - } - - if (has_dual_phy()) - dr_phy_type = phys[2]; - - usb_mode_off = fdt_fixup_usb_mode_phy_type(blob, - dr_mode_type, NULL, - usb_mode_off); - - if (usb_mode_off < 0) - return; - - usb_phy_off = fdt_fixup_usb_mode_phy_type(blob, - NULL, dr_phy_type, - usb_phy_off); - - if (usb_phy_off < 0) - return; - - if (has_erratum_a006261()) { - usb_erratum_a006261_off = fdt_fixup_usb_erratum - (blob, - "fsl,usb-erratum-a006261", - usb_erratum_a006261_off); - if (usb_erratum_a006261_off < 0) - return; - } - - if (has_erratum_a007075()) { - usb_erratum_a007075_off = fdt_fixup_usb_erratum - (blob, - "fsl,usb-erratum-a007075", - usb_erratum_a007075_off); - if (usb_erratum_a007075_off < 0) - return; - } - - if (has_erratum_a007792()) { - usb_erratum_a007792_off = fdt_fixup_usb_erratum - (blob, - "fsl,usb-erratum-a007792", - usb_erratum_a007792_off); - if (usb_erratum_a007792_off < 0) - return; - } - if (has_erratum_a005697()) { - usb_erratum_a005697_off = fdt_fixup_usb_erratum - (blob, - "fsl,usb-erratum-a005697", - usb_erratum_a005697_off); - if (usb_erratum_a005697_off < 0) - return; - } - } -} -#endif diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 0113c6c..fa5d584 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1615,6 +1615,12 @@ int ehci_register(struct udevice *dev, struct ehci_hccr *hccr, if (ret) goto err; + if (ctrl->ops.init_after_reset) { + ret = ctrl->ops.init_after_reset(ctrl); + if (ret) + goto err; + } + ret = ehci_common_init(ctrl, tweaks); if (ret) goto err; diff --git a/drivers/usb/host/ehci-mpc512x.c b/drivers/usb/host/ehci-mpc512x.c index b320c4a..bb4f461 100644 --- a/drivers/usb/host/ehci-mpc512x.c +++ b/drivers/usb/host/ehci-mpc512x.c @@ -17,7 +17,7 @@ #include <pci.h> #include <usb.h> #include <asm/io.h> -#include <usb/ehci-fsl.h> +#include <usb/ehci-ci.h> #include "ehci.h" @@ -93,7 +93,7 @@ static int reset_usb_controller(volatile struct usb_ehci *ehci) unsigned int i; /* Command a reset of the USB Controller */ - out_be32(&(ehci->usbcmd), EHCI_FSL_USBCMD_RST); + out_be32(&(ehci->usbcmd), CMD_RESET); /* Wait for the reset process to finish */ for (i = 65535 ; i > 0 ; i--) { @@ -101,7 +101,7 @@ static int reset_usb_controller(volatile struct usb_ehci *ehci) * The host will set this bit to zero once the * reset process is complete */ - if ((in_be32(&(ehci->usbcmd)) & EHCI_FSL_USBCMD_RST) == 0) + if ((in_be32(&(ehci->usbcmd)) & CMD_RESET) == 0) return 0; } diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c new file mode 100644 index 0000000..6484c1c --- /dev/null +++ b/drivers/usb/host/ehci-msm.c @@ -0,0 +1,178 @@ +/* + * Qualcomm EHCI driver + * + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + * + * Based on Linux driver + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <libfdt.h> +#include <usb.h> +#include <usb/ehci-ci.h> +#include <usb/ulpi.h> +#include <wait_bit.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <linux/compat.h> +#include "ehci.h" + +/* PHY viewport regs */ +#define ULPI_MISC_A_READ 0x96 +#define ULPI_MISC_A_SET 0x97 +#define ULPI_MISC_A_CLEAR 0x98 +#define ULPI_MISC_A_VBUSVLDEXTSEL (1 << 1) +#define ULPI_MISC_A_VBUSVLDEXT (1 << 0) + +#define GEN2_SESS_VLD_CTRL_EN (1 << 7) + +#define SESS_VLD_CTRL (1 << 25) + +struct msm_ehci_priv { + struct ehci_ctrl ctrl; /* Needed by EHCI */ + struct usb_ehci *ehci; /* Start of IP core*/ + struct ulpi_viewport ulpi_vp; /* ULPI Viewport */ +}; + +int __weak board_prepare_usb(enum usb_init_type type) +{ + return 0; +} + +static void setup_usb_phy(struct msm_ehci_priv *priv) +{ + /* Select and enable external configuration with USB PHY */ + ulpi_write(&priv->ulpi_vp, (u8 *)ULPI_MISC_A_SET, + ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT); +} + +static void reset_usb_phy(struct msm_ehci_priv *priv) +{ + /* Disable VBUS mimicing in the controller. */ + ulpi_write(&priv->ulpi_vp, (u8 *)ULPI_MISC_A_CLEAR, + ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT); +} + + +static int msm_init_after_reset(struct ehci_ctrl *dev) +{ + struct msm_ehci_priv *p = container_of(dev, struct msm_ehci_priv, ctrl); + struct usb_ehci *ehci = p->ehci; + + /* select ULPI phy */ + writel(PORT_PTS_ULPI, &ehci->portsc); + setup_usb_phy(p); + + /* Enable sess_vld */ + setbits_le32(&ehci->genconfig2, GEN2_SESS_VLD_CTRL_EN); + + /* Enable external vbus configuration in the LINK */ + setbits_le32(&ehci->usbcmd, SESS_VLD_CTRL); + + /* USB_OTG_HS_AHB_BURST */ + writel(0x0, &ehci->sbuscfg); + + /* USB_OTG_HS_AHB_MODE: HPROT_MODE */ + /* Bus access related config. */ + writel(0x08, &ehci->sbusmode); + + /* set mode to host controller */ + writel(CM_HOST, &ehci->usbmode); + + return 0; +} + +static const struct ehci_ops msm_ehci_ops = { + .init_after_reset = msm_init_after_reset +}; + +static int ehci_usb_probe(struct udevice *dev) +{ + struct msm_ehci_priv *p = dev_get_priv(dev); + struct usb_ehci *ehci = p->ehci; + struct ehci_hccr *hccr; + struct ehci_hcor *hcor; + int ret; + + hccr = (struct ehci_hccr *)((phys_addr_t)&ehci->caplength); + hcor = (struct ehci_hcor *)((phys_addr_t)hccr + + HC_LENGTH(ehci_readl(&(hccr)->cr_capbase))); + + ret = board_prepare_usb(USB_INIT_HOST); + if (ret < 0) + return ret; + + return ehci_register(dev, hccr, hcor, &msm_ehci_ops, 0, USB_INIT_HOST); +} + +static int ehci_usb_remove(struct udevice *dev) +{ + struct msm_ehci_priv *p = dev_get_priv(dev); + struct usb_ehci *ehci = p->ehci; + int ret; + + ret = ehci_deregister(dev); + if (ret) + return ret; + + /* Stop controller. */ + clrbits_le32(&ehci->usbcmd, CMD_RUN); + + reset_usb_phy(p); + + ret = board_prepare_usb(USB_INIT_DEVICE); /* Board specific hook */ + if (ret < 0) + return ret; + + /* Reset controller */ + setbits_le32(&ehci->usbcmd, CMD_RESET); + + /* Wait for reset */ + if (wait_for_bit(__func__, &ehci->usbcmd, CMD_RESET, false, 30, + false)) { + printf("Stuck on USB reset.\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int ehci_usb_ofdata_to_platdata(struct udevice *dev) +{ + struct msm_ehci_priv *priv = dev_get_priv(dev); + + priv->ulpi_vp.port_num = 0; + priv->ehci = (void *)dev_get_addr(dev); + + if (priv->ehci == (void *)FDT_ADDR_T_NONE) + return -EINVAL; + + /* Warning: this will not work if viewport address is > 64 bit due to + * ULPI design. + */ + priv->ulpi_vp.viewport_addr = (phys_addr_t)&priv->ehci->ulpi_viewpoint; + + return 0; +} + +static const struct udevice_id ehci_usb_ids[] = { + { .compatible = "qcom,ehci-host", }, + { } +}; + +U_BOOT_DRIVER(usb_ehci) = { + .name = "ehci_msm", + .id = UCLASS_USB, + .of_match = ehci_usb_ids, + .ofdata_to_platdata = ehci_usb_ofdata_to_platdata, + .probe = ehci_usb_probe, + .remove = ehci_usb_remove, + .ops = &ehci_usb_ops, + .priv_auto_alloc_size = sizeof(struct msm_ehci_priv), + .flags = DM_FLAG_ALLOC_PRIV_DMA, +}; diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c index d319962..2b36ceb 100644 --- a/drivers/usb/host/ehci-mx5.c +++ b/drivers/usb/host/ehci-mx5.c @@ -9,7 +9,7 @@ #include <usb.h> #include <errno.h> #include <linux/compiler.h> -#include <usb/ehci-fsl.h> +#include <usb/ehci-ci.h> #include <asm/io.h> #include <asm/arch/imx-regs.h> #include <asm/arch/clock.h> diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index e1c67f7..a981b50 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -10,7 +10,7 @@ #include <errno.h> #include <wait_bit.h> #include <linux/compiler.h> -#include <usb/ehci-fsl.h> +#include <usb/ehci-ci.h> #include <asm/io.h> #include <asm/arch/imx-regs.h> #include <asm/arch/clock.h> diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index f09c75a..f8324ee 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -9,7 +9,7 @@ #include <usb.h> #include <asm/io.h> #include <asm/arch/imx-regs.h> -#include <usb/ehci-fsl.h> +#include <usb/ehci-ci.h> #include <errno.h> #include "ehci.h" diff --git a/drivers/usb/host/ehci-sunxi.c b/drivers/usb/host/ehci-sunxi.c index cf3dcc4..f2d83e3 100644 --- a/drivers/usb/host/ehci-sunxi.c +++ b/drivers/usb/host/ehci-sunxi.c @@ -17,6 +17,14 @@ #include <dm.h> #include "ehci.h" +#ifdef CONFIG_SUNXI_GEN_SUN4I +#define BASE_DIST 0x8000 +#define AHB_CLK_DIST 2 +#else +#define BASE_DIST 0x1000 +#define AHB_CLK_DIST 1 +#endif + struct ehci_sunxi_priv { struct ehci_ctrl ehci; int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */ @@ -30,6 +38,7 @@ static int ehci_usb_probe(struct udevice *dev) struct ehci_sunxi_priv *priv = dev_get_priv(dev); struct ehci_hccr *hccr = (struct ehci_hccr *)dev_get_addr(dev); struct ehci_hcor *hcor; + int extra_ahb_gate_mask = 0; /* * This should go away once we've moved to the driver model for @@ -37,14 +46,18 @@ static int ehci_usb_probe(struct udevice *dev) */ priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0; #ifdef CONFIG_MACH_SUN8I_H3 - priv->ahb_gate_mask |= 1 << AHB_GATE_OFFSET_USB_OHCI0; + extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI0; #endif - priv->phy_index = ((u32)hccr - SUNXI_USB1_BASE) / 0x1000 + 1; - priv->ahb_gate_mask <<= priv->phy_index - 1; + priv->phy_index = ((u32)hccr - SUNXI_USB1_BASE) / BASE_DIST; + priv->ahb_gate_mask <<= priv->phy_index * AHB_CLK_DIST; + extra_ahb_gate_mask <<= priv->phy_index * AHB_CLK_DIST; + priv->phy_index++; /* Non otg phys start at 1 */ - setbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask); + setbits_le32(&ccm->ahb_gate0, + priv->ahb_gate_mask | extra_ahb_gate_mask); #ifdef CONFIG_SUNXI_GEN_SUN6I - setbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask); + setbits_le32(&ccm->ahb_reset0_cfg, + priv->ahb_gate_mask | extra_ahb_gate_mask); #endif sunxi_usb_phy_init(priv->phy_index); @@ -82,6 +95,7 @@ static const struct udevice_id ehci_usb_ids[] = { { .compatible = "allwinner,sun6i-a31-ehci", }, { .compatible = "allwinner,sun7i-a20-ehci", }, { .compatible = "allwinner,sun8i-a23-ehci", }, + { .compatible = "allwinner,sun8i-a83t-ehci", }, { .compatible = "allwinner,sun8i-h3-ehci", }, { .compatible = "allwinner,sun9i-a80-ehci", }, { } diff --git a/drivers/usb/host/ehci-vf.c b/drivers/usb/host/ehci-vf.c index 335e303..61789dd 100644 --- a/drivers/usb/host/ehci-vf.c +++ b/drivers/usb/host/ehci-vf.c @@ -17,7 +17,7 @@ #include <asm/arch/crm_regs.h> #include <asm/imx-common/iomux-v3.h> #include <asm/imx-common/regs-usbphy.h> -#include <usb/ehci-fsl.h> +#include <usb/ehci-ci.h> #include "ehci.h" diff --git a/drivers/usb/host/ehci-zynq.c b/drivers/usb/host/ehci-zynq.c index 7770d05..37a7935 100644 --- a/drivers/usb/host/ehci-zynq.c +++ b/drivers/usb/host/ehci-zynq.c @@ -11,7 +11,7 @@ #include <asm/arch/sys_proto.h> #include <asm/io.h> #include <usb.h> -#include <usb/ehci-fsl.h> +#include <usb/ehci-ci.h> #include <usb/ulpi.h> #include "ehci.h" diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 826b3fe..734d7f0 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -240,6 +240,7 @@ struct ehci_ops { void (*powerup_fixup)(struct ehci_ctrl *ctrl, uint32_t *status_reg, uint32_t *reg); uint32_t *(*get_portsc_register)(struct ehci_ctrl *ctrl, int port); + int (*init_after_reset)(struct ehci_ctrl *ctrl); }; struct ehci_ctrl { diff --git a/drivers/usb/host/ohci-sunxi.c b/drivers/usb/host/ohci-sunxi.c index 1b1f651..2a1e8bf 100644 --- a/drivers/usb/host/ohci-sunxi.c +++ b/drivers/usb/host/ohci-sunxi.c @@ -17,6 +17,14 @@ #include <usb.h> #include "ohci.h" +#ifdef CONFIG_SUNXI_GEN_SUN4I +#define BASE_DIST 0x8000 +#define AHB_CLK_DIST 2 +#else +#define BASE_DIST 0x1000 +#define AHB_CLK_DIST 1 +#endif + struct ohci_sunxi_priv { ohci_t ohci; int ahb_gate_mask; /* Mask of ahb_gate0 clk gate bits for this hcd */ @@ -30,6 +38,7 @@ static int ohci_usb_probe(struct udevice *dev) struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev); struct ohci_sunxi_priv *priv = dev_get_priv(dev); struct ohci_regs *regs = (struct ohci_regs *)dev_get_addr(dev); + int extra_ahb_gate_mask = 0; bus_priv->companion = true; @@ -39,17 +48,21 @@ static int ohci_usb_probe(struct udevice *dev) */ priv->ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_OHCI0; #ifdef CONFIG_MACH_SUN8I_H3 - priv->ahb_gate_mask |= 1 << AHB_GATE_OFFSET_USB_EHCI0; + extra_ahb_gate_mask = 1 << AHB_GATE_OFFSET_USB_EHCI0; #endif priv->usb_gate_mask = CCM_USB_CTRL_OHCI0_CLK; - priv->phy_index = ((u32)regs - (SUNXI_USB1_BASE + 0x400)) / 0x1000 + 1; - priv->ahb_gate_mask <<= priv->phy_index - 1; - priv->usb_gate_mask <<= priv->phy_index - 1; + priv->phy_index = ((u32)regs - (SUNXI_USB1_BASE + 0x400)) / BASE_DIST; + priv->ahb_gate_mask <<= priv->phy_index * AHB_CLK_DIST; + extra_ahb_gate_mask <<= priv->phy_index * AHB_CLK_DIST; + priv->usb_gate_mask <<= priv->phy_index; + priv->phy_index++; /* Non otg phys start at 1 */ - setbits_le32(&ccm->ahb_gate0, priv->ahb_gate_mask); + setbits_le32(&ccm->ahb_gate0, + priv->ahb_gate_mask | extra_ahb_gate_mask); setbits_le32(&ccm->usb_clk_cfg, priv->usb_gate_mask); #ifdef CONFIG_SUNXI_GEN_SUN6I - setbits_le32(&ccm->ahb_reset0_cfg, priv->ahb_gate_mask); + setbits_le32(&ccm->ahb_reset0_cfg, + priv->ahb_gate_mask | extra_ahb_gate_mask); #endif sunxi_usb_phy_init(priv->phy_index); @@ -85,6 +98,7 @@ static const struct udevice_id ohci_usb_ids[] = { { .compatible = "allwinner,sun6i-a31-ohci", }, { .compatible = "allwinner,sun7i-a20-ohci", }, { .compatible = "allwinner,sun8i-a23-ohci", }, + { .compatible = "allwinner,sun8i-a83t-ohci", }, { .compatible = "allwinner,sun8i-h3-ohci", }, { .compatible = "allwinner,sun9i-a80-ohci", }, { } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index ca598aa..cb8a04b 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -941,10 +941,12 @@ static int _xhci_submit_control_msg(struct usb_device *udev, unsigned long pipe, if (usb_pipedevice(pipe) == ctrl->rootdev) return xhci_submit_root(udev, pipe, buffer, setup); - if (setup->request == USB_REQ_SET_ADDRESS) + if (setup->request == USB_REQ_SET_ADDRESS && + (setup->requesttype & USB_TYPE_MASK) == USB_TYPE_STANDARD) return xhci_address_device(udev, root_portnr); - if (setup->request == USB_REQ_SET_CONFIGURATION) { + if (setup->request == USB_REQ_SET_CONFIGURATION && + (setup->requesttype & USB_TYPE_MASK) == USB_TYPE_STANDARD) { ret = xhci_set_configuration(udev); if (ret) { puts("Failed to configure xHCI endpoint\n"); diff --git a/drivers/usb/musb-new/Kconfig b/drivers/usb/musb-new/Kconfig index 6a6cb93..4e8a543 100644 --- a/drivers/usb/musb-new/Kconfig +++ b/drivers/usb/musb-new/Kconfig @@ -15,6 +15,13 @@ config USB_MUSB_GADGET if USB_MUSB_HOST || USB_MUSB_GADGET +config USB_MUSB_PIC32 + bool "Enable Microchip PIC32 DRC USB controller" + depends on DM_USB && MACH_PIC32 + help + Say y to enable PIC32 USB DRC controller support + if it is available on your Microchip PIC32 platform. + config USB_MUSB_SUNXI bool "Enable sunxi OTG / DRC USB controller" depends on ARCH_SUNXI diff --git a/drivers/usb/musb-new/Makefile b/drivers/usb/musb-new/Makefile index 072d516..df1c3c8 100644 --- a/drivers/usb/musb-new/Makefile +++ b/drivers/usb/musb-new/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_USB_MUSB_HOST) += musb_host.o musb_core.o musb_uboot.o obj-$(CONFIG_USB_MUSB_DSPS) += musb_dsps.o obj-$(CONFIG_USB_MUSB_AM35X) += am35x.o obj-$(CONFIG_USB_MUSB_OMAP2PLUS) += omap2430.o +obj-$(CONFIG_USB_MUSB_PIC32) += pic32.o obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o ccflags-y := $(call cc-option,-Wno-unused-variable) \ diff --git a/drivers/usb/musb-new/linux-compat.h b/drivers/usb/musb-new/linux-compat.h index 1fc9391..9244977 100644 --- a/drivers/usb/musb-new/linux-compat.h +++ b/drivers/usb/musb-new/linux-compat.h @@ -13,13 +13,6 @@ printf(fmt, ##args); \ ret_warn; }) -#define writesl(a, d, s) __raw_writesl((unsigned long)a, d, s) -#define readsl(a, d, s) __raw_readsl((unsigned long)a, d, s) -#define writesw(a, d, s) __raw_writesw((unsigned long)a, d, s) -#define readsw(a, d, s) __raw_readsw((unsigned long)a, d, s) -#define writesb(a, d, s) __raw_writesb((unsigned long)a, d, s) -#define readsb(a, d, s) __raw_readsb((unsigned long)a, d, s) - #define device_init_wakeup(dev, a) do {} while (0) #define platform_data device_data diff --git a/drivers/usb/musb-new/musb_core.c b/drivers/usb/musb-new/musb_core.c index a6d6af6..dd0443c 100644 --- a/drivers/usb/musb-new/musb_core.c +++ b/drivers/usb/musb-new/musb_core.c @@ -259,7 +259,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) } } -#if !defined(CONFIG_USB_MUSB_AM35X) +#if !defined(CONFIG_USB_MUSB_AM35X) && !defined(CONFIG_USB_MUSB_PIC32) /* * Unload an endpoint's FIFO */ diff --git a/drivers/usb/musb-new/musb_regs.h b/drivers/usb/musb-new/musb_regs.h index 4dc9abb..0f18dd7 100644 --- a/drivers/usb/musb-new/musb_regs.h +++ b/drivers/usb/musb-new/musb_regs.h @@ -434,7 +434,7 @@ static inline u8 musb_read_ulpi_buscontrol(void __iomem *mbase) static inline u8 musb_read_configdata(void __iomem *mbase) { -#ifdef CONFIG_MACH_SUN8I_A33 +#if defined CONFIG_MACH_SUN8I_A33 || defined CONFIG_MACH_SUN8I_A83T /* <Sigh> allwinner saves a reg, and we need to hardcode this */ return 0xde; #else diff --git a/drivers/usb/musb-new/musb_uboot.c b/drivers/usb/musb-new/musb_uboot.c index 233a0e4..6ce528c 100644 --- a/drivers/usb/musb-new/musb_uboot.c +++ b/drivers/usb/musb-new/musb_uboot.c @@ -237,8 +237,10 @@ int musb_lowlevel_init(struct musb_host_data *host) if (musb_readb(mbase, MUSB_DEVCTL) & MUSB_DEVCTL_HM) break; } while (get_timer(0) < timeout); - if (get_timer(0) >= timeout) + if (get_timer(0) >= timeout) { + musb_stop(host->host); return -ENODEV; + } _musb_reset_root_port(host, NULL); host->host->is_active = 1; diff --git a/drivers/usb/musb-new/pic32.c b/drivers/usb/musb-new/pic32.c new file mode 100644 index 0000000..c888c64 --- /dev/null +++ b/drivers/usb/musb-new/pic32.c @@ -0,0 +1,288 @@ +/* + * Microchip PIC32 MUSB "glue layer" + * + * Copyright (C) 2015, Microchip Technology Inc. + * Cristian Birsan <cristian.birsan@microchip.com> + * Purna Chandra Mandal <purna.mandal@microchip.com> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Based on the dsps "glue layer" code. + */ + +#include <common.h> +#include <linux/usb/musb.h> +#include "linux-compat.h" +#include "musb_core.h" +#include "musb_uboot.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define PIC32_TX_EP_MASK 0x0f /* EP0 + 7 Tx EPs */ +#define PIC32_RX_EP_MASK 0x0e /* 7 Rx EPs */ + +#define MUSB_SOFTRST 0x7f +#define MUSB_SOFTRST_NRST BIT(0) +#define MUSB_SOFTRST_NRSTX BIT(1) + +#define USBCRCON 0 +#define USBCRCON_USBWKUPEN BIT(0) /* Enable Wakeup Interrupt */ +#define USBCRCON_USBRIE BIT(1) /* Enable Remote resume Interrupt */ +#define USBCRCON_USBIE BIT(2) /* Enable USB General interrupt */ +#define USBCRCON_SENDMONEN BIT(3) /* Enable Session End VBUS monitoring */ +#define USBCRCON_BSVALMONEN BIT(4) /* Enable B-Device VBUS monitoring */ +#define USBCRCON_ASVALMONEN BIT(5) /* Enable A-Device VBUS monitoring */ +#define USBCRCON_VBUSMONEN BIT(6) /* Enable VBUS monitoring */ +#define USBCRCON_PHYIDEN BIT(7) /* PHY ID monitoring enable */ +#define USBCRCON_USBIDVAL BIT(8) /* USB ID value */ +#define USBCRCON_USBIDOVEN BIT(9) /* USB ID override enable */ +#define USBCRCON_USBWK BIT(24) /* USB Wakeup Status */ +#define USBCRCON_USBRF BIT(25) /* USB Resume Status */ +#define USBCRCON_USBIF BIT(26) /* USB General Interrupt Status */ + +/* PIC32 controller data */ +struct pic32_musb_data { + struct musb_host_data mdata; + struct device dev; + void __iomem *musb_glue; +}; + +#define to_pic32_musb_data(d) \ + container_of(d, struct pic32_musb_data, dev) + +static void pic32_musb_disable(struct musb *musb) +{ + /* no way to shut the controller */ +} + +static int pic32_musb_enable(struct musb *musb) +{ + /* soft reset by NRSTx */ + musb_writeb(musb->mregs, MUSB_SOFTRST, MUSB_SOFTRST_NRSTX); + /* set mode */ + musb_platform_set_mode(musb, musb->board_mode); + + return 0; +} + +static irqreturn_t pic32_interrupt(int irq, void *hci) +{ + struct musb *musb = hci; + irqreturn_t ret = IRQ_NONE; + u32 epintr, usbintr; + + /* ack usb core interrupts */ + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + if (musb->int_usb) + musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); + + /* ack endpoint interrupts */ + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX) & PIC32_RX_EP_MASK; + if (musb->int_rx) + musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); + + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX) & PIC32_TX_EP_MASK; + if (musb->int_tx) + musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); + + /* drop spurious RX and TX if device is disconnected */ + if (musb->int_usb & MUSB_INTR_DISCONNECT) { + musb->int_tx = 0; + musb->int_rx = 0; + } + + if (musb->int_tx || musb->int_rx || musb->int_usb) + ret = musb_interrupt(musb); + + return ret; +} + +static int pic32_musb_set_mode(struct musb *musb, u8 mode) +{ + struct device *dev = musb->controller; + struct pic32_musb_data *pdata = to_pic32_musb_data(dev); + + switch (mode) { + case MUSB_HOST: + clrsetbits_le32(pdata->musb_glue + USBCRCON, + USBCRCON_USBIDVAL, USBCRCON_USBIDOVEN); + break; + case MUSB_PERIPHERAL: + setbits_le32(pdata->musb_glue + USBCRCON, + USBCRCON_USBIDVAL | USBCRCON_USBIDOVEN); + break; + case MUSB_OTG: + dev_err(dev, "support for OTG is unimplemented\n"); + break; + default: + dev_err(dev, "unsupported mode %d\n", mode); + return -EINVAL; + } + + return 0; +} + +static int pic32_musb_init(struct musb *musb) +{ + struct pic32_musb_data *pdata = to_pic32_musb_data(musb->controller); + u32 ctrl, hwvers; + u8 power; + + /* Returns zero if not clocked */ + hwvers = musb_read_hwvers(musb->mregs); + if (!hwvers) + return -ENODEV; + + /* Reset the musb */ + power = musb_readb(musb->mregs, MUSB_POWER); + power = power | MUSB_POWER_RESET; + musb_writeb(musb->mregs, MUSB_POWER, power); + mdelay(100); + + /* Start the on-chip PHY and its PLL. */ + power = power & ~MUSB_POWER_RESET; + musb_writeb(musb->mregs, MUSB_POWER, power); + + musb->isr = pic32_interrupt; + + ctrl = USBCRCON_USBIF | USBCRCON_USBRF | + USBCRCON_USBWK | USBCRCON_USBIDOVEN | + USBCRCON_PHYIDEN | USBCRCON_USBIE | + USBCRCON_USBRIE | USBCRCON_USBWKUPEN | + USBCRCON_VBUSMONEN; + writel(ctrl, pdata->musb_glue + USBCRCON); + + return 0; +} + +/* PIC32 supports only 32bit read operation */ +void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) +{ + void __iomem *fifo = hw_ep->fifo; + u32 val, rem = len % 4; + + /* USB stack ensures dst is always 32bit aligned. */ + readsl(fifo, dst, len / 4); + if (rem) { + dst += len & ~0x03; + val = musb_readl(fifo, 0); + memcpy(dst, &val, rem); + } +} + +const struct musb_platform_ops pic32_musb_ops = { + .init = pic32_musb_init, + .set_mode = pic32_musb_set_mode, + .disable = pic32_musb_disable, + .enable = pic32_musb_enable, +}; + +/* PIC32 default FIFO config - fits in 8KB */ +static struct musb_fifo_cfg pic32_musb_fifo_config[] = { + { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 512, }, +}; + +static struct musb_hdrc_config pic32_musb_config = { + .fifo_cfg = pic32_musb_fifo_config, + .fifo_cfg_size = ARRAY_SIZE(pic32_musb_fifo_config), + .multipoint = 1, + .dyn_fifo = 1, + .num_eps = 8, + .ram_bits = 11, +}; + +/* PIC32 has one MUSB controller which can be host or gadget */ +static struct musb_hdrc_platform_data pic32_musb_plat = { + .mode = MUSB_HOST, + .config = &pic32_musb_config, + .power = 250, /* 500mA */ + .platform_ops = &pic32_musb_ops, +}; + +static int musb_usb_probe(struct udevice *dev) +{ + struct usb_bus_priv *priv = dev_get_uclass_priv(dev); + struct pic32_musb_data *pdata = dev_get_priv(dev); + struct musb_host_data *mdata = &pdata->mdata; + struct fdt_resource mc, glue; + void *fdt = (void *)gd->fdt_blob; + int node = dev->of_offset; + void __iomem *mregs; + int ret; + + priv->desc_before_addr = true; + + ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", + "mc", &mc); + if (ret < 0) { + printf("pic32-musb: resource \"mc\" not found\n"); + return ret; + } + + ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", + "control", &glue); + if (ret < 0) { + printf("pic32-musb: resource \"control\" not found\n"); + return ret; + } + + mregs = ioremap(mc.start, fdt_resource_size(&mc)); + pdata->musb_glue = ioremap(glue.start, fdt_resource_size(&glue)); + + /* init controller */ +#ifdef CONFIG_USB_MUSB_HOST + mdata->host = musb_init_controller(&pic32_musb_plat, + &pdata->dev, mregs); + if (!mdata->host) + return -EIO; + + ret = musb_lowlevel_init(mdata); +#else + pic32_musb_plat.mode = MUSB_PERIPHERAL; + ret = musb_register(&pic32_musb_plat, &pdata->dev, mregs); +#endif + if (ret == 0) + printf("PIC32 MUSB OTG\n"); + + return ret; +} + +static int musb_usb_remove(struct udevice *dev) +{ + struct pic32_musb_data *pdata = dev_get_priv(dev); + + musb_stop(pdata->mdata.host); + + return 0; +} + +static const struct udevice_id pic32_musb_ids[] = { + { .compatible = "microchip,pic32mzda-usb" }, + { } +}; + +U_BOOT_DRIVER(usb_musb) = { + .name = "pic32-musb", + .id = UCLASS_USB, + .of_match = pic32_musb_ids, + .probe = musb_usb_probe, + .remove = musb_usb_remove, +#ifdef CONFIG_USB_MUSB_HOST + .ops = &musb_usb_ops, +#endif + .platdata_auto_alloc_size = sizeof(struct usb_platdata), + .priv_auto_alloc_size = sizeof(struct pic32_musb_data), +}; diff --git a/drivers/usb/musb-new/sunxi.c b/drivers/usb/musb-new/sunxi.c index be1d2ec..3081afc 100644 --- a/drivers/usb/musb-new/sunxi.c +++ b/drivers/usb/musb-new/sunxi.c @@ -201,6 +201,7 @@ static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci) /* musb_core does not call enable / disable in a balanced manner <sigh> */ static bool enabled = false; +static struct musb *sunxi_musb; static int sunxi_musb_enable(struct musb *musb) { @@ -320,13 +321,15 @@ int musb_usb_probe(struct udevice *dev) priv->desc_before_addr = true; - if (!host->host) { - host->host = musb_init_controller(&musb_plat, NULL, + if (!sunxi_musb) { + sunxi_musb = musb_init_controller(&musb_plat, NULL, (void *)SUNXI_USB0_BASE); - if (!host->host) - return -EIO; } + host->host = sunxi_musb; + if (!host->host) + return -EIO; + ret = musb_lowlevel_init(host); if (ret == 0) printf("MUSB OTG\n"); diff --git a/drivers/usb/ulpi/Kconfig b/drivers/usb/ulpi/Kconfig new file mode 100644 index 0000000..329d2df --- /dev/null +++ b/drivers/usb/ulpi/Kconfig @@ -0,0 +1,33 @@ +comment "ULPI drivers" + +choice + prompt "ULPI Viewport type" + optional + default n + help + Select ULPI viewport (SoC-side interface to ULPI) implementation + appropriate for the device if you want to communicate with + UTMI (USB PHY) via ULPI interface. + +config USB_ULPI_VIEWPORT + bool "Generic ULPI Viewport" + help + Support generic ULPI Viewport implementation that is used on + some Tegra and Snapdragon devices. + +config USB_ULPI_VIEWPORT_OMAP + bool "OMAP ULPI Viewport" + help + Support ULPI Viewport implementation that is used on OMAP devices. + +endchoice + +config USB_ULPI + bool "ULPI support" + depends on (USB_ULPI_VIEWPORT || USB_ULPI_VIEWPORT_OMAP) + help + Select to commnicate with USB PHY via ULPI interface. + ULPI is wrapper on UTMI+ core that is used as + PHY Transreceiver for USB controllers. + + This driver uses ULPI viewports that are specific for each SoC. diff --git a/drivers/usb/ulpi/ulpi-viewport.c b/drivers/usb/ulpi/ulpi-viewport.c index 72a06de..d111680 100644 --- a/drivers/usb/ulpi/ulpi-viewport.c +++ b/drivers/usb/ulpi/ulpi-viewport.c @@ -92,7 +92,8 @@ static int ulpi_request(struct ulpi_viewport *ulpi_vp, u32 value) int ulpi_write(struct ulpi_viewport *ulpi_vp, u8 *reg, u32 value) { - u32 val = ULPI_RWRUN | ULPI_RWCTRL | ((u32)reg << 16) | (value & 0xff); + u32 addr = (uintptr_t)reg & 0xFF; + u32 val = ULPI_RWRUN | ULPI_RWCTRL | addr << 16 | (value & 0xff); val |= (ulpi_vp->port_num & 0x7) << 24; return ulpi_request(ulpi_vp, val); @@ -101,7 +102,7 @@ int ulpi_write(struct ulpi_viewport *ulpi_vp, u8 *reg, u32 value) u32 ulpi_read(struct ulpi_viewport *ulpi_vp, u8 *reg) { int err; - u32 val = ULPI_RWRUN | ((u32)reg << 16); + u32 val = ULPI_RWRUN | ((uintptr_t)reg & 0xFF) << 16; val |= (ulpi_vp->port_num & 0x7) << 24; err = ulpi_request(ulpi_vp, val); diff --git a/drivers/video/bcm2835.c b/drivers/video/bcm2835.c index bff1fcb..cd605e6 100644 --- a/drivers/video/bcm2835.c +++ b/drivers/video/bcm2835.c @@ -44,6 +44,7 @@ void lcd_ctrl_init(void *lcdbase) ALLOC_CACHE_ALIGN_BUFFER(struct msg_setup, msg_setup, 1); int ret; u32 w, h; + u32 fb_start, fb_end; debug("bcm2835: Query resolution...\n"); @@ -106,6 +107,14 @@ void lcd_ctrl_init(void *lcdbase) gd->fb_base = bus_to_phys( msg_setup->allocate_buffer.body.resp.fb_address); + + /* Enable dcache for the frame buffer */ + fb_start = gd->fb_base & ~(MMU_SECTION_SIZE - 1); + fb_end = gd->fb_base + msg_setup->allocate_buffer.body.resp.fb_size; + fb_end = ALIGN(fb_end, 1 << MMU_SECTION_SHIFT); + mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start, + DCACHE_WRITEBACK); + lcd_set_flush_dcache(1); } void lcd_enable(void) diff --git a/drivers/video/pxa_lcd.c b/drivers/video/pxa_lcd.c index 2799425..1809fc6 100644 --- a/drivers/video/pxa_lcd.c +++ b/drivers/video/pxa_lcd.c @@ -175,6 +175,7 @@ vidinfo_t panel_info = { vidinfo_t panel_info = { .vl_col = 240, .vl_row = 320, + .vl_rot = 3, .vl_width = 240, .vl_height = 320, .vl_clkp = CONFIG_SYS_HIGH, diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index 9fee66a..56f6c8e 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -767,7 +767,11 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode, (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; int bp, clk_delay, clk_div, clk_double, pin, total, val; +#if defined CONFIG_MACH_SUN8I && defined CONFIG_VIDEO_LCD_IF_LVDS + for (pin = SUNXI_GPD(18); pin <= SUNXI_GPD(27); pin++) { +#else for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++) { +#endif #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LCD0); #endif |