diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/Makefile | 2 | ||||
-rw-r--r-- | drivers/dma/Makefile | 2 | ||||
-rw-r--r-- | drivers/dma/keystone_nav.c | 332 | ||||
-rw-r--r-- | drivers/dma/keystone_nav_cfg.c | 27 | ||||
-rw-r--r-- | drivers/dma/ti-edma3.c | 384 | ||||
-rw-r--r-- | drivers/net/keystone_net.c | 416 | ||||
-rw-r--r-- | drivers/net/phy/phy.c | 2 | ||||
-rw-r--r-- | drivers/soc/Makefile | 5 | ||||
-rw-r--r-- | drivers/soc/keystone/Makefile | 1 | ||||
-rw-r--r-- | drivers/soc/keystone/keystone_serdes.c | 210 | ||||
-rw-r--r-- | drivers/spi/mxc_spi.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/host/xhci-keystone.c | 329 |
13 files changed, 1444 insertions, 268 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index d8361d9..33227c8 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -19,3 +19,5 @@ obj-$(CONFIG_QE) += qe/ obj-y += memory/ obj-y += pwm/ obj-y += input/ +# SOC specific infrastructure drivers. +obj-y += soc/ diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index a79c391..4c8fcc2 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -8,3 +8,5 @@ obj-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o obj-$(CONFIG_APBH_DMA) += apbh_dma.o obj-$(CONFIG_FSL_DMA) += fsl_dma.o +obj-$(CONFIG_TI_KSNAV) += keystone_nav.o keystone_nav_cfg.o +obj-$(CONFIG_TI_EDMA3) += ti-edma3.o diff --git a/drivers/dma/keystone_nav.c b/drivers/dma/keystone_nav.c new file mode 100644 index 0000000..77707c2 --- /dev/null +++ b/drivers/dma/keystone_nav.c @@ -0,0 +1,332 @@ +/* + * Multicore Navigator driver for TI Keystone 2 devices. + * + * (C) Copyright 2012-2014 + * Texas Instruments Incorporated, <www.ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <asm/io.h> +#include <asm/ti-common/keystone_nav.h> + +struct qm_config qm_memmap = { + .stat_cfg = CONFIG_KSNAV_QM_QUEUE_STATUS_BASE, + .queue = (void *)CONFIG_KSNAV_QM_MANAGER_QUEUES_BASE, + .mngr_vbusm = CONFIG_KSNAV_QM_BASE_ADDRESS, + .i_lram = CONFIG_KSNAV_QM_LINK_RAM_BASE, + .proxy = (void *)CONFIG_KSNAV_QM_MANAGER_Q_PROXY_BASE, + .status_ram = CONFIG_KSNAV_QM_STATUS_RAM_BASE, + .mngr_cfg = (void *)CONFIG_KSNAV_QM_CONF_BASE, + .intd_cfg = CONFIG_KSNAV_QM_INTD_CONF_BASE, + .desc_mem = (void *)CONFIG_KSNAV_QM_DESC_SETUP_BASE, + .region_num = CONFIG_KSNAV_QM_REGION_NUM, + .pdsp_cmd = CONFIG_KSNAV_QM_PDSP1_CMD_BASE, + .pdsp_ctl = CONFIG_KSNAV_QM_PDSP1_CTRL_BASE, + .pdsp_iram = CONFIG_KSNAV_QM_PDSP1_IRAM_BASE, + .qpool_num = CONFIG_KSNAV_QM_QPOOL_NUM, +}; + +/* + * We are going to use only one type of descriptors - host packet + * descriptors. We staticaly allocate memory for them here + */ +struct qm_host_desc desc_pool[HDESC_NUM] __aligned(sizeof(struct qm_host_desc)); + +static struct qm_config *qm_cfg; + +inline int num_of_desc_to_reg(int num_descr) +{ + int j, num; + + for (j = 0, num = 32; j < 15; j++, num *= 2) { + if (num_descr <= num) + return j; + } + + return 15; +} + +int _qm_init(struct qm_config *cfg) +{ + u32 j; + + qm_cfg = cfg; + + qm_cfg->mngr_cfg->link_ram_base0 = qm_cfg->i_lram; + qm_cfg->mngr_cfg->link_ram_size0 = HDESC_NUM * 8; + qm_cfg->mngr_cfg->link_ram_base1 = 0; + qm_cfg->mngr_cfg->link_ram_size1 = 0; + qm_cfg->mngr_cfg->link_ram_base2 = 0; + + qm_cfg->desc_mem[0].base_addr = (u32)desc_pool; + qm_cfg->desc_mem[0].start_idx = 0; + qm_cfg->desc_mem[0].desc_reg_size = + (((sizeof(struct qm_host_desc) >> 4) - 1) << 16) | + num_of_desc_to_reg(HDESC_NUM); + + memset(desc_pool, 0, sizeof(desc_pool)); + for (j = 0; j < HDESC_NUM; j++) + qm_push(&desc_pool[j], qm_cfg->qpool_num); + + return QM_OK; +} + +int qm_init(void) +{ + return _qm_init(&qm_memmap); +} + +void qm_close(void) +{ + u32 j; + + if (qm_cfg == NULL) + return; + + queue_close(qm_cfg->qpool_num); + + qm_cfg->mngr_cfg->link_ram_base0 = 0; + qm_cfg->mngr_cfg->link_ram_size0 = 0; + qm_cfg->mngr_cfg->link_ram_base1 = 0; + qm_cfg->mngr_cfg->link_ram_size1 = 0; + qm_cfg->mngr_cfg->link_ram_base2 = 0; + + for (j = 0; j < qm_cfg->region_num; j++) { + qm_cfg->desc_mem[j].base_addr = 0; + qm_cfg->desc_mem[j].start_idx = 0; + qm_cfg->desc_mem[j].desc_reg_size = 0; + } + + qm_cfg = NULL; +} + +void qm_push(struct qm_host_desc *hd, u32 qnum) +{ + u32 regd; + + if (!qm_cfg) + return; + + cpu_to_bus((u32 *)hd, sizeof(struct qm_host_desc)/4); + regd = (u32)hd | ((sizeof(struct qm_host_desc) >> 4) - 1); + writel(regd, &qm_cfg->queue[qnum].ptr_size_thresh); +} + +void qm_buff_push(struct qm_host_desc *hd, u32 qnum, + void *buff_ptr, u32 buff_len) +{ + hd->orig_buff_len = buff_len; + hd->buff_len = buff_len; + hd->orig_buff_ptr = (u32)buff_ptr; + hd->buff_ptr = (u32)buff_ptr; + qm_push(hd, qnum); +} + +struct qm_host_desc *qm_pop(u32 qnum) +{ + u32 uhd; + + if (!qm_cfg) + return NULL; + + uhd = readl(&qm_cfg->queue[qnum].ptr_size_thresh) & ~0xf; + if (uhd) + cpu_to_bus((u32 *)uhd, sizeof(struct qm_host_desc)/4); + + return (struct qm_host_desc *)uhd; +} + +struct qm_host_desc *qm_pop_from_free_pool(void) +{ + if (!qm_cfg) + return NULL; + + return qm_pop(qm_cfg->qpool_num); +} + +void queue_close(u32 qnum) +{ + struct qm_host_desc *hd; + + while ((hd = qm_pop(qnum))) + ; +} + +/** + * DMA API + */ + +static int ksnav_rx_disable(struct pktdma_cfg *pktdma) +{ + u32 j, v, k; + + for (j = 0; j < pktdma->rx_ch_num; j++) { + v = readl(&pktdma->rx_ch[j].cfg_a); + if (!(v & CPDMA_CHAN_A_ENABLE)) + continue; + + writel(v | CPDMA_CHAN_A_TDOWN, &pktdma->rx_ch[j].cfg_a); + for (k = 0; k < TDOWN_TIMEOUT_COUNT; k++) { + udelay(100); + v = readl(&pktdma->rx_ch[j].cfg_a); + if (!(v & CPDMA_CHAN_A_ENABLE)) + continue; + } + /* TODO: teardown error on if TDOWN_TIMEOUT_COUNT is reached */ + } + + /* Clear all of the flow registers */ + for (j = 0; j < pktdma->rx_flow_num; j++) { + writel(0, &pktdma->rx_flows[j].control); + writel(0, &pktdma->rx_flows[j].tags); + writel(0, &pktdma->rx_flows[j].tag_sel); + writel(0, &pktdma->rx_flows[j].fdq_sel[0]); + writel(0, &pktdma->rx_flows[j].fdq_sel[1]); + writel(0, &pktdma->rx_flows[j].thresh[0]); + writel(0, &pktdma->rx_flows[j].thresh[1]); + writel(0, &pktdma->rx_flows[j].thresh[2]); + } + + return QM_OK; +} + +static int ksnav_tx_disable(struct pktdma_cfg *pktdma) +{ + u32 j, v, k; + + for (j = 0; j < pktdma->tx_ch_num; j++) { + v = readl(&pktdma->tx_ch[j].cfg_a); + if (!(v & CPDMA_CHAN_A_ENABLE)) + continue; + + writel(v | CPDMA_CHAN_A_TDOWN, &pktdma->tx_ch[j].cfg_a); + for (k = 0; k < TDOWN_TIMEOUT_COUNT; k++) { + udelay(100); + v = readl(&pktdma->tx_ch[j].cfg_a); + if (!(v & CPDMA_CHAN_A_ENABLE)) + continue; + } + /* TODO: teardown error on if TDOWN_TIMEOUT_COUNT is reached */ + } + + return QM_OK; +} + +int ksnav_init(struct pktdma_cfg *pktdma, struct rx_buff_desc *rx_buffers) +{ + u32 j, v; + struct qm_host_desc *hd; + u8 *rx_ptr; + + if (pktdma == NULL || rx_buffers == NULL || + rx_buffers->buff_ptr == NULL || qm_cfg == NULL) + return QM_ERR; + + pktdma->rx_flow = rx_buffers->rx_flow; + + /* init rx queue */ + rx_ptr = rx_buffers->buff_ptr; + + for (j = 0; j < rx_buffers->num_buffs; j++) { + hd = qm_pop(qm_cfg->qpool_num); + if (hd == NULL) + return QM_ERR; + + qm_buff_push(hd, pktdma->rx_free_q, + rx_ptr, rx_buffers->buff_len); + + rx_ptr += rx_buffers->buff_len; + } + + ksnav_rx_disable(pktdma); + + /* configure rx channels */ + v = CPDMA_REG_VAL_MAKE_RX_FLOW_A(1, 1, 0, 0, 0, 0, 0, pktdma->rx_rcv_q); + writel(v, &pktdma->rx_flows[pktdma->rx_flow].control); + writel(0, &pktdma->rx_flows[pktdma->rx_flow].tags); + writel(0, &pktdma->rx_flows[pktdma->rx_flow].tag_sel); + + v = CPDMA_REG_VAL_MAKE_RX_FLOW_D(0, pktdma->rx_free_q, 0, + pktdma->rx_free_q); + + writel(v, &pktdma->rx_flows[pktdma->rx_flow].fdq_sel[0]); + writel(v, &pktdma->rx_flows[pktdma->rx_flow].fdq_sel[1]); + writel(0, &pktdma->rx_flows[pktdma->rx_flow].thresh[0]); + writel(0, &pktdma->rx_flows[pktdma->rx_flow].thresh[1]); + writel(0, &pktdma->rx_flows[pktdma->rx_flow].thresh[2]); + + for (j = 0; j < pktdma->rx_ch_num; j++) + writel(CPDMA_CHAN_A_ENABLE, &pktdma->rx_ch[j].cfg_a); + + /* configure tx channels */ + /* Disable loopback in the tx direction */ + writel(0, &pktdma->global->emulation_control); + + /* Set QM base address, only for K2x devices */ + writel(CONFIG_KSNAV_QM_BASE_ADDRESS, &pktdma->global->qm_base_addr[0]); + + /* Enable all channels. The current state isn't important */ + for (j = 0; j < pktdma->tx_ch_num; j++) { + writel(0, &pktdma->tx_ch[j].cfg_b); + writel(CPDMA_CHAN_A_ENABLE, &pktdma->tx_ch[j].cfg_a); + } + + return QM_OK; +} + +int ksnav_close(struct pktdma_cfg *pktdma) +{ + if (!pktdma) + return QM_ERR; + + ksnav_tx_disable(pktdma); + ksnav_rx_disable(pktdma); + + queue_close(pktdma->rx_free_q); + queue_close(pktdma->rx_rcv_q); + queue_close(pktdma->tx_snd_q); + + return QM_OK; +} + +int ksnav_send(struct pktdma_cfg *pktdma, u32 *pkt, int num_bytes, u32 swinfo2) +{ + struct qm_host_desc *hd; + + hd = qm_pop(qm_cfg->qpool_num); + if (hd == NULL) + return QM_ERR; + + hd->desc_info = num_bytes; + hd->swinfo[2] = swinfo2; + hd->packet_info = qm_cfg->qpool_num; + + qm_buff_push(hd, pktdma->tx_snd_q, pkt, num_bytes); + + return QM_OK; +} + +void *ksnav_recv(struct pktdma_cfg *pktdma, u32 **pkt, int *num_bytes) +{ + struct qm_host_desc *hd; + + hd = qm_pop(pktdma->rx_rcv_q); + if (!hd) + return NULL; + + *pkt = (u32 *)hd->buff_ptr; + *num_bytes = hd->desc_info & 0x3fffff; + + return hd; +} + +void ksnav_release_rxhd(struct pktdma_cfg *pktdma, void *hd) +{ + struct qm_host_desc *_hd = (struct qm_host_desc *)hd; + + _hd->buff_len = _hd->orig_buff_len; + _hd->buff_ptr = _hd->orig_buff_ptr; + + qm_push(_hd, pktdma->rx_free_q); +} diff --git a/drivers/dma/keystone_nav_cfg.c b/drivers/dma/keystone_nav_cfg.c new file mode 100644 index 0000000..bdd30a0 --- /dev/null +++ b/drivers/dma/keystone_nav_cfg.c @@ -0,0 +1,27 @@ +/* + * Multicore Navigator driver for TI Keystone 2 devices. + * + * (C) Copyright 2012-2014 + * Texas Instruments Incorporated, <www.ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/ti-common/keystone_nav.h> + +#ifdef CONFIG_KSNAV_PKTDMA_NETCP +/* NETCP Pktdma */ +struct pktdma_cfg netcp_pktdma = { + .global = (void *)CONFIG_KSNAV_NETCP_PDMA_CTRL_BASE, + .tx_ch = (void *)CONFIG_KSNAV_NETCP_PDMA_TX_BASE, + .tx_ch_num = CONFIG_KSNAV_NETCP_PDMA_TX_CH_NUM, + .rx_ch = (void *)CONFIG_KSNAV_NETCP_PDMA_RX_BASE, + .rx_ch_num = CONFIG_KSNAV_NETCP_PDMA_RX_CH_NUM, + .tx_sched = (u32 *)CONFIG_KSNAV_NETCP_PDMA_SCHED_BASE, + .rx_flows = (void *)CONFIG_KSNAV_NETCP_PDMA_RX_FLOW_BASE, + .rx_flow_num = CONFIG_KSNAV_NETCP_PDMA_RX_FLOW_NUM, + .rx_free_q = CONFIG_KSNAV_NETCP_PDMA_RX_FREE_QUEUE, + .rx_rcv_q = CONFIG_KSNAV_NETCP_PDMA_RX_RCV_QUEUE, + .tx_snd_q = CONFIG_KSNAV_NETCP_PDMA_TX_SND_QUEUE, +}; +#endif diff --git a/drivers/dma/ti-edma3.c b/drivers/dma/ti-edma3.c new file mode 100644 index 0000000..8184ded --- /dev/null +++ b/drivers/dma/ti-edma3.c @@ -0,0 +1,384 @@ +/* + * Enhanced Direct Memory Access (EDMA3) Controller + * + * (C) Copyright 2014 + * Texas Instruments Incorporated, <www.ti.com> + * + * Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <asm/io.h> +#include <common.h> +#include <asm/ti-common/ti-edma3.h> + +#define EDMA3_SL_BASE(slot) (0x4000 + ((slot) << 5)) +#define EDMA3_SL_MAX_NUM 512 +#define EDMA3_SLOPT_FIFO_WIDTH_MASK (0x7 << 8) + +#define EDMA3_QCHMAP(ch) 0x0200 + ((ch) << 2) +#define EDMA3_CHMAP_PARSET_MASK 0x1ff +#define EDMA3_CHMAP_PARSET_SHIFT 0x5 +#define EDMA3_CHMAP_TRIGWORD_SHIFT 0x2 + +#define EDMA3_QEMCR 0x314 +#define EDMA3_IPR 0x1068 +#define EDMA3_IPRH 0x106c +#define EDMA3_ICR 0x1070 +#define EDMA3_ICRH 0x1074 +#define EDMA3_QEECR 0x1088 +#define EDMA3_QEESR 0x108c +#define EDMA3_QSECR 0x1094 + +/** + * qedma3_start - start qdma on a channel + * @base: base address of edma + * @cfg: pinter to struct edma3_channel_config where you can set + * the slot number to associate with, the chnum, which corresponds + * your quick channel number 0-7, complete code - transfer complete code + * and trigger slot word - which has to correspond to the word number in + * edma3_slot_layout struct for generating event. + * + */ +void qedma3_start(u32 base, struct edma3_channel_config *cfg) +{ + u32 qchmap; + + /* Clear the pending int bit */ + if (cfg->complete_code < 32) + __raw_writel(1 << cfg->complete_code, base + EDMA3_ICR); + else + __raw_writel(1 << cfg->complete_code, base + EDMA3_ICRH); + + /* Map parameter set and trigger word 7 to quick channel */ + qchmap = ((EDMA3_CHMAP_PARSET_MASK & cfg->slot) + << EDMA3_CHMAP_PARSET_SHIFT) | + (cfg->trigger_slot_word << EDMA3_CHMAP_TRIGWORD_SHIFT); + + __raw_writel(qchmap, base + EDMA3_QCHMAP(cfg->chnum)); + + /* Clear missed event if set*/ + __raw_writel(1 << cfg->chnum, base + EDMA3_QSECR); + __raw_writel(1 << cfg->chnum, base + EDMA3_QEMCR); + + /* Enable qdma channel event */ + __raw_writel(1 << cfg->chnum, base + EDMA3_QEESR); +} + +/** + * edma3_set_dest - set initial DMA destination address in parameter RAM slot + * @base: base address of edma + * @slot: parameter RAM slot being configured + * @dst: physical address of destination (memory, controller FIFO, etc) + * @addressMode: INCR, except in very rare cases + * @width: ignored unless @addressMode is FIFO, else specifies the + * width to use when addressing the fifo (e.g. W8BIT, W32BIT) + * + * Note that the destination address is modified during the DMA transfer + * according to edma3_set_dest_index(). + */ +void edma3_set_dest(u32 base, int slot, u32 dst, enum edma3_address_mode mode, + enum edma3_fifo_width width) +{ + u32 opt; + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + + opt = __raw_readl(&rg->opt); + if (mode == FIFO) + opt = (opt & EDMA3_SLOPT_FIFO_WIDTH_MASK) | + (EDMA3_SLOPT_DST_ADDR_CONST_MODE | + EDMA3_SLOPT_FIFO_WIDTH_SET(width)); + else + opt &= ~EDMA3_SLOPT_DST_ADDR_CONST_MODE; + + __raw_writel(opt, &rg->opt); + __raw_writel(dst, &rg->dst); +} + +/** + * edma3_set_dest_index - configure DMA destination address indexing + * @base: base address of edma + * @slot: parameter RAM slot being configured + * @bidx: byte offset between destination arrays in a frame + * @cidx: byte offset between destination frames in a block + * + * Offsets are specified to support either contiguous or discontiguous + * memory transfers, or repeated access to a hardware register, as needed. + * When accessing hardware registers, both offsets are normally zero. + */ +void edma3_set_dest_index(u32 base, unsigned slot, int bidx, int cidx) +{ + u32 src_dst_bidx; + u32 src_dst_cidx; + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + + src_dst_bidx = __raw_readl(&rg->src_dst_bidx); + src_dst_cidx = __raw_readl(&rg->src_dst_cidx); + + __raw_writel((src_dst_bidx & 0x0000ffff) | (bidx << 16), + &rg->src_dst_bidx); + __raw_writel((src_dst_cidx & 0x0000ffff) | (cidx << 16), + &rg->src_dst_cidx); +} + +/** + * edma3_set_dest_addr - set destination address for slot only + */ +void edma3_set_dest_addr(u32 base, int slot, u32 dst) +{ + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + __raw_writel(dst, &rg->dst); +} + +/** + * edma3_set_src - set initial DMA source address in parameter RAM slot + * @base: base address of edma + * @slot: parameter RAM slot being configured + * @src_port: physical address of source (memory, controller FIFO, etc) + * @mode: INCR, except in very rare cases + * @width: ignored unless @addressMode is FIFO, else specifies the + * width to use when addressing the fifo (e.g. W8BIT, W32BIT) + * + * Note that the source address is modified during the DMA transfer + * according to edma3_set_src_index(). + */ +void edma3_set_src(u32 base, int slot, u32 src, enum edma3_address_mode mode, + enum edma3_fifo_width width) +{ + u32 opt; + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + + opt = __raw_readl(&rg->opt); + if (mode == FIFO) + opt = (opt & EDMA3_SLOPT_FIFO_WIDTH_MASK) | + (EDMA3_SLOPT_DST_ADDR_CONST_MODE | + EDMA3_SLOPT_FIFO_WIDTH_SET(width)); + else + opt &= ~EDMA3_SLOPT_DST_ADDR_CONST_MODE; + + __raw_writel(opt, &rg->opt); + __raw_writel(src, &rg->src); +} + +/** + * edma3_set_src_index - configure DMA source address indexing + * @base: base address of edma + * @slot: parameter RAM slot being configured + * @bidx: byte offset between source arrays in a frame + * @cidx: byte offset between source frames in a block + * + * Offsets are specified to support either contiguous or discontiguous + * memory transfers, or repeated access to a hardware register, as needed. + * When accessing hardware registers, both offsets are normally zero. + */ +void edma3_set_src_index(u32 base, unsigned slot, int bidx, int cidx) +{ + u32 src_dst_bidx; + u32 src_dst_cidx; + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + + src_dst_bidx = __raw_readl(&rg->src_dst_bidx); + src_dst_cidx = __raw_readl(&rg->src_dst_cidx); + + __raw_writel((src_dst_bidx & 0xffff0000) | bidx, + &rg->src_dst_bidx); + __raw_writel((src_dst_cidx & 0xffff0000) | cidx, + &rg->src_dst_cidx); +} + +/** + * edma3_set_src_addr - set source address for slot only + */ +void edma3_set_src_addr(u32 base, int slot, u32 src) +{ + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + __raw_writel(src, &rg->src); +} + +/** + * edma3_set_transfer_params - configure DMA transfer parameters + * @base: base address of edma + * @slot: parameter RAM slot being configured + * @acnt: how many bytes per array (at least one) + * @bcnt: how many arrays per frame (at least one) + * @ccnt: how many frames per block (at least one) + * @bcnt_rld: used only for A-Synchronized transfers; this specifies + * the value to reload into bcnt when it decrements to zero + * @sync_mode: ASYNC or ABSYNC + * + * See the EDMA3 documentation to understand how to configure and link + * transfers using the fields in PaRAM slots. If you are not doing it + * all at once with edma3_write_slot(), you will use this routine + * plus two calls each for source and destination, setting the initial + * address and saying how to index that address. + * + * An example of an A-Synchronized transfer is a serial link using a + * single word shift register. In that case, @acnt would be equal to + * that word size; the serial controller issues a DMA synchronization + * event to transfer each word, and memory access by the DMA transfer + * controller will be word-at-a-time. + * + * An example of an AB-Synchronized transfer is a device using a FIFO. + * In that case, @acnt equals the FIFO width and @bcnt equals its depth. + * The controller with the FIFO issues DMA synchronization events when + * the FIFO threshold is reached, and the DMA transfer controller will + * transfer one frame to (or from) the FIFO. It will probably use + * efficient burst modes to access memory. + */ +void edma3_set_transfer_params(u32 base, int slot, int acnt, + int bcnt, int ccnt, u16 bcnt_rld, + enum edma3_sync_dimension sync_mode) +{ + u32 opt; + u32 link_bcntrld; + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + + link_bcntrld = __raw_readl(&rg->link_bcntrld); + + __raw_writel((bcnt_rld << 16) | (0x0000ffff & link_bcntrld), + &rg->link_bcntrld); + + opt = __raw_readl(&rg->opt); + if (sync_mode == ASYNC) + __raw_writel(opt & ~EDMA3_SLOPT_AB_SYNC, &rg->opt); + else + __raw_writel(opt | EDMA3_SLOPT_AB_SYNC, &rg->opt); + + /* Set the acount, bcount, ccount registers */ + __raw_writel((bcnt << 16) | (acnt & 0xffff), &rg->a_b_cnt); + __raw_writel(0xffff & ccnt, &rg->ccnt); +} + +/** + * edma3_write_slot - write parameter RAM data for slot + * @base: base address of edma + * @slot: number of parameter RAM slot being modified + * @param: data to be written into parameter RAM slot + * + * Use this to assign all parameters of a transfer at once. This + * allows more efficient setup of transfers than issuing multiple + * calls to set up those parameters in small pieces, and provides + * complete control over all transfer options. + */ +void edma3_write_slot(u32 base, int slot, struct edma3_slot_layout *param) +{ + int i; + u32 *p = (u32 *)param; + u32 *addr = (u32 *)(base + EDMA3_SL_BASE(slot)); + + for (i = 0; i < sizeof(struct edma3_slot_layout)/4; i += 4) + __raw_writel(*p++, addr++); +} + +/** + * edma3_read_slot - read parameter RAM data from slot + * @base: base address of edma + * @slot: number of parameter RAM slot being copied + * @param: where to store copy of parameter RAM data + * + * Use this to read data from a parameter RAM slot, perhaps to + * save them as a template for later reuse. + */ +void edma3_read_slot(u32 base, int slot, struct edma3_slot_layout *param) +{ + int i; + u32 *p = (u32 *)param; + u32 *addr = (u32 *)(base + EDMA3_SL_BASE(slot)); + + for (i = 0; i < sizeof(struct edma3_slot_layout)/4; i += 4) + *p++ = __raw_readl(addr++); +} + +void edma3_slot_configure(u32 base, int slot, struct edma3_slot_config *cfg) +{ + struct edma3_slot_layout *rg; + + rg = (struct edma3_slot_layout *)(base + EDMA3_SL_BASE(slot)); + + __raw_writel(cfg->opt, &rg->opt); + __raw_writel(cfg->src, &rg->src); + __raw_writel((cfg->bcnt << 16) | (cfg->acnt & 0xffff), &rg->a_b_cnt); + __raw_writel(cfg->dst, &rg->dst); + __raw_writel((cfg->dst_bidx << 16) | + (cfg->src_bidx & 0xffff), &rg->src_dst_bidx); + __raw_writel((cfg->bcntrld << 16) | + (cfg->link & 0xffff), &rg->link_bcntrld); + __raw_writel((cfg->dst_cidx << 16) | + (cfg->src_cidx & 0xffff), &rg->src_dst_cidx); + __raw_writel(0xffff & cfg->ccnt, &rg->ccnt); +} + +/** + * edma3_check_for_transfer - check if transfer coplete by checking + * interrupt pending bit. Clear interrupt pending bit if complete. + * @base: base address of edma + * @cfg: pinter to struct edma3_channel_config which was passed + * to qedma3_start when you started qdma channel + * + * Return 0 if complete, 1 if not. + */ +int edma3_check_for_transfer(u32 base, struct edma3_channel_config *cfg) +{ + u32 inum; + u32 ipr_base; + u32 icr_base; + + if (cfg->complete_code < 32) { + ipr_base = base + EDMA3_IPR; + icr_base = base + EDMA3_ICR; + inum = 1 << cfg->complete_code; + } else { + ipr_base = base + EDMA3_IPRH; + icr_base = base + EDMA3_ICRH; + inum = 1 << (cfg->complete_code - 32); + } + + /* check complete interrupt */ + if (!(__raw_readl(ipr_base) & inum)) + return 1; + + /* clean up the pending int bit */ + __raw_writel(inum, icr_base); + + return 0; +} + +/** + * qedma3_stop - stops dma on the channel passed + * @base: base address of edma + * @cfg: pinter to struct edma3_channel_config which was passed + * to qedma3_start when you started qdma channel + */ +void qedma3_stop(u32 base, struct edma3_channel_config *cfg) +{ + /* Disable qdma channel event */ + __raw_writel(1 << cfg->chnum, base + EDMA3_QEECR); + + /* clean up the interrupt indication */ + if (cfg->complete_code < 32) + __raw_writel(1 << cfg->complete_code, base + EDMA3_ICR); + else + __raw_writel(1 << cfg->complete_code, base + EDMA3_ICRH); + + /* Clear missed event if set*/ + __raw_writel(1 << cfg->chnum, base + EDMA3_QSECR); + __raw_writel(1 << cfg->chnum, base + EDMA3_QEMCR); + + /* Clear the channel map */ + __raw_writel(0, base + EDMA3_QCHMAP(cfg->chnum)); +} diff --git a/drivers/net/keystone_net.c b/drivers/net/keystone_net.c index d22b722..c8681d0 100644 --- a/drivers/net/keystone_net.c +++ b/drivers/net/keystone_net.c @@ -10,15 +10,16 @@ #include <command.h> #include <net.h> +#include <phy.h> +#include <errno.h> #include <miiphy.h> #include <malloc.h> -#include <asm/arch/emac_defs.h> -#include <asm/arch/psc_defs.h> -#include <asm/arch/keystone_nav.h> - -unsigned int emac_dbg; +#include <asm/ti-common/keystone_nav.h> +#include <asm/ti-common/keystone_net.h> +#include <asm/ti-common/keystone_serdes.h> unsigned int emac_open; +static struct mii_dev *mdio_bus; static unsigned int sys_has_mdio = 1; #ifdef KEYSTONE2_EMAC_GIG_ENABLE @@ -30,6 +31,7 @@ static unsigned int sys_has_mdio = 1; #define RX_BUFF_NUMS 24 #define RX_BUFF_LEN 1520 #define MAX_SIZE_STREAM_BUFFER RX_BUFF_LEN +#define SGMII_ANEG_TIMEOUT 4000 static u8 rx_buffs[RX_BUFF_NUMS * RX_BUFF_LEN] __aligned(16); @@ -40,15 +42,7 @@ struct rx_buff_desc net_rx_buffs = { .rx_flow = 22, }; -static void keystone2_eth_mdio_enable(void); - -static int gen_get_link_speed(int phy_addr); - -/* EMAC Addresses */ -static volatile struct emac_regs *adap_emac = - (struct emac_regs *)EMAC_EMACSL_BASE_ADDR; -static volatile struct mdio_regs *adap_mdio = - (struct mdio_regs *)EMAC_MDIO_BASE_ADDR; +static void keystone2_net_serdes_setup(void); int keystone2_eth_read_mac_addr(struct eth_device *dev) { @@ -74,64 +68,67 @@ int keystone2_eth_read_mac_addr(struct eth_device *dev) return 0; } -static void keystone2_eth_mdio_enable(void) +/* MDIO */ + +static int keystone2_mdio_reset(struct mii_dev *bus) { - u_int32_t clkdiv; + u_int32_t clkdiv; + struct mdio_regs *adap_mdio = bus->priv; clkdiv = (EMAC_MDIO_BUS_FREQ / EMAC_MDIO_CLOCK_FREQ) - 1; - writel((clkdiv & 0xffff) | - MDIO_CONTROL_ENABLE | - MDIO_CONTROL_FAULT | - MDIO_CONTROL_FAULT_ENABLE, + writel((clkdiv & 0xffff) | MDIO_CONTROL_ENABLE | + MDIO_CONTROL_FAULT | MDIO_CONTROL_FAULT_ENABLE, &adap_mdio->control); while (readl(&adap_mdio->control) & MDIO_CONTROL_IDLE) ; + + return 0; } -/* Read a PHY register via MDIO inteface. Returns 1 on success, 0 otherwise */ -int keystone2_eth_phy_read(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t *data) +/** + * keystone2_mdio_read - read a PHY register via MDIO interface. + * Blocks until operation is complete. + */ +static int keystone2_mdio_read(struct mii_dev *bus, + int addr, int devad, int reg) { - int tmp; + int tmp; + struct mdio_regs *adap_mdio = bus->priv; while (readl(&adap_mdio->useraccess0) & MDIO_USERACCESS0_GO) ; - writel(MDIO_USERACCESS0_GO | - MDIO_USERACCESS0_WRITE_READ | - ((reg_num & 0x1f) << 21) | - ((phy_addr & 0x1f) << 16), + writel(MDIO_USERACCESS0_GO | MDIO_USERACCESS0_WRITE_READ | + ((reg & 0x1f) << 21) | ((addr & 0x1f) << 16), &adap_mdio->useraccess0); /* Wait for command to complete */ while ((tmp = readl(&adap_mdio->useraccess0)) & MDIO_USERACCESS0_GO) ; - if (tmp & MDIO_USERACCESS0_ACK) { - *data = tmp & 0xffff; - return 0; - } + if (tmp & MDIO_USERACCESS0_ACK) + return tmp & 0xffff; - *data = -1; return -1; } -/* - * Write to a PHY register via MDIO inteface. +/** + * keystone2_mdio_write - write to a PHY register via MDIO interface. * Blocks until operation is complete. */ -int keystone2_eth_phy_write(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t data) +static int keystone2_mdio_write(struct mii_dev *bus, + int addr, int devad, int reg, u16 val) { + struct mdio_regs *adap_mdio = bus->priv; + while (readl(&adap_mdio->useraccess0) & MDIO_USERACCESS0_GO) ; - writel(MDIO_USERACCESS0_GO | - MDIO_USERACCESS0_WRITE_WRITE | - ((reg_num & 0x1f) << 21) | - ((phy_addr & 0x1f) << 16) | - (data & 0xffff), - &adap_mdio->useraccess0); + writel(MDIO_USERACCESS0_GO | MDIO_USERACCESS0_WRITE_WRITE | + ((reg & 0x1f) << 21) | ((addr & 0x1f) << 16) | + (val & 0xffff), &adap_mdio->useraccess0); /* Wait for command to complete */ while (readl(&adap_mdio->useraccess0) & MDIO_USERACCESS0_GO) @@ -140,19 +137,6 @@ int keystone2_eth_phy_write(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t data) return 0; } -/* PHY functions for a generic PHY */ -static int gen_get_link_speed(int phy_addr) -{ - u_int16_t tmp; - - if ((!keystone2_eth_phy_read(phy_addr, MII_STATUS_REG, &tmp)) && - (tmp & 0x04)) { - return 0; - } - - return -1; -} - static void __attribute__((unused)) keystone2_eth_gigabit_enable(struct eth_device *dev) { @@ -160,8 +144,10 @@ static void __attribute__((unused)) struct eth_priv_t *eth_priv = (struct eth_priv_t *)dev->priv; if (sys_has_mdio) { - if (keystone2_eth_phy_read(eth_priv->phy_addr, 0, &data) || - !(data & (1 << 6))) /* speed selection MSB */ + data = keystone2_mdio_read(mdio_bus, eth_priv->phy_addr, + MDIO_DEVAD_NONE, 0); + /* speed selection MSB */ + if (!(data & (1 << 6))) return; } @@ -169,10 +155,10 @@ static void __attribute__((unused)) * Check if link detected is giga-bit * If Gigabit mode detected, enable gigbit in MAC */ - writel(readl(&(adap_emac[eth_priv->slave_port - 1].maccontrol)) | + writel(readl(DEVICE_EMACSL_BASE(eth_priv->slave_port - 1) + + CPGMACSL_REG_CTL) | EMAC_MACCONTROL_GIGFORCE | EMAC_MACCONTROL_GIGABIT_ENABLE, - &(adap_emac[eth_priv->slave_port - 1].maccontrol)) - ; + DEVICE_EMACSL_BASE(eth_priv->slave_port - 1) + CPGMACSL_REG_CTL); } int keystone_sgmii_link_status(int port) @@ -181,38 +167,11 @@ int keystone_sgmii_link_status(int port) status = __raw_readl(SGMII_STATUS_REG(port)); - return status & SGMII_REG_STATUS_LINK; + return (status & SGMII_REG_STATUS_LOCK) && + (status & SGMII_REG_STATUS_LINK); } - -int keystone_get_link_status(struct eth_device *dev) -{ - struct eth_priv_t *eth_priv = (struct eth_priv_t *)dev->priv; - int sgmii_link; - int link_state = 0; -#if CONFIG_GET_LINK_STATUS_ATTEMPTS > 1 - int j; - - for (j = 0; (j < CONFIG_GET_LINK_STATUS_ATTEMPTS) && (link_state == 0); - j++) { -#endif - sgmii_link = - keystone_sgmii_link_status(eth_priv->slave_port - 1); - - if (sgmii_link) { - link_state = 1; - - if (eth_priv->sgmii_link_type == SGMII_LINK_MAC_PHY) - if (gen_get_link_speed(eth_priv->phy_addr)) - link_state = 0; - } -#if CONFIG_GET_LINK_STATUS_ATTEMPTS > 1 - } -#endif - return link_state; -} - -int keystone_sgmii_config(int port, int interface) +int keystone_sgmii_config(struct phy_device *phy_dev, int port, int interface) { unsigned int i, status, mask; unsigned int mr_adv_ability, control; @@ -273,11 +232,35 @@ int keystone_sgmii_config(int port, int interface) if (control & SGMII_REG_CONTROL_AUTONEG) mask |= SGMII_REG_STATUS_AUTONEG; - for (i = 0; i < 1000; i++) { + status = __raw_readl(SGMII_STATUS_REG(port)); + if ((status & mask) == mask) + return 0; + + printf("\n%s Waiting for SGMII auto negotiation to complete", + phy_dev->dev->name); + while ((status & mask) != mask) { + /* + * Timeout reached ? + */ + if (i > SGMII_ANEG_TIMEOUT) { + puts(" TIMEOUT !\n"); + phy_dev->link = 0; + return 0; + } + + if (ctrlc()) { + puts("user interrupt!\n"); + phy_dev->link = 0; + return -EINTR; + } + + if ((i++ % 500) == 0) + printf("."); + + udelay(1000); /* 1 ms */ status = __raw_readl(SGMII_STATUS_REG(port)); - if ((status & mask) == mask) - break; } + puts(" done\n"); return 0; } @@ -332,6 +315,11 @@ int mac_sl_config(u_int16_t port, struct mac_sl_cfg *cfg) writel(cfg->max_rx_len, DEVICE_EMACSL_BASE(port) + CPGMACSL_REG_MAXLEN); writel(cfg->ctl, DEVICE_EMACSL_BASE(port) + CPGMACSL_REG_CTL); +#ifdef CONFIG_K2E_EVM + /* Map RX packet flow priority to 0 */ + writel(0, DEVICE_EMACSL_BASE(port) + CPGMACSL_REG_RX_PRI_MAP); +#endif + return ret; } @@ -393,15 +381,15 @@ int32_t cpmac_drv_send(u32 *buffer, int num_bytes, int slave_port_num) if (num_bytes < EMAC_MIN_ETHERNET_PKT_SIZE) num_bytes = EMAC_MIN_ETHERNET_PKT_SIZE; - return netcp_send(buffer, num_bytes, (slave_port_num) << 16); + return ksnav_send(&netcp_pktdma, buffer, + num_bytes, (slave_port_num) << 16); } /* Eth device open */ static int keystone2_eth_open(struct eth_device *dev, bd_t *bis) { - u_int32_t clkdiv; - int link; struct eth_priv_t *eth_priv = (struct eth_priv_t *)dev->priv; + struct phy_device *phy_dev = eth_priv->phy_dev; debug("+ emac_open\n"); @@ -410,15 +398,9 @@ static int keystone2_eth_open(struct eth_device *dev, bd_t *bis) sys_has_mdio = (eth_priv->sgmii_link_type == SGMII_LINK_MAC_PHY) ? 1 : 0; - psc_enable_module(KS2_LPSC_PA); - psc_enable_module(KS2_LPSC_CPGMAC); - - sgmii_serdes_setup_156p25mhz(); - - if (sys_has_mdio) - keystone2_eth_mdio_enable(); + keystone2_net_serdes_setup(); - keystone_sgmii_config(eth_priv->slave_port - 1, + keystone_sgmii_config(phy_dev, eth_priv->slave_port - 1, eth_priv->sgmii_link_type); udelay(10000); @@ -431,7 +413,7 @@ static int keystone2_eth_open(struct eth_device *dev, bd_t *bis) printf("ERROR: qm_init()\n"); return -1; } - if (netcp_init(&net_rx_buffs)) { + if (ksnav_init(&netcp_pktdma, &net_rx_buffs)) { qm_close(); printf("ERROR: netcp_init()\n"); return -1; @@ -445,18 +427,11 @@ static int keystone2_eth_open(struct eth_device *dev, bd_t *bis) hw_config_streaming_switch(); if (sys_has_mdio) { - /* Init MDIO & get link state */ - clkdiv = (EMAC_MDIO_BUS_FREQ / EMAC_MDIO_CLOCK_FREQ) - 1; - writel((clkdiv & 0xff) | MDIO_CONTROL_ENABLE | - MDIO_CONTROL_FAULT, &adap_mdio->control) - ; - - /* We need to wait for MDIO to start */ - udelay(1000); - - link = keystone_get_link_status(dev); - if (link == 0) { - netcp_close(); + keystone2_mdio_reset(mdio_bus); + + phy_startup(phy_dev); + if (phy_dev->link == 0) { + ksnav_close(&netcp_pktdma); qm_close(); return -1; } @@ -476,6 +451,9 @@ static int keystone2_eth_open(struct eth_device *dev, bd_t *bis) /* Eth device close */ void keystone2_eth_close(struct eth_device *dev) { + struct eth_priv_t *eth_priv = (struct eth_priv_t *)dev->priv; + struct phy_device *phy_dev = eth_priv->phy_dev; + debug("+ emac_close\n"); if (!emac_open) @@ -483,16 +461,15 @@ void keystone2_eth_close(struct eth_device *dev) ethss_stop(); - netcp_close(); + ksnav_close(&netcp_pktdma); qm_close(); + phy_shutdown(phy_dev); emac_open = 0; debug("- emac_close\n"); } -static int tx_send_loop; - /* * This function sends a single packet on the network and returns * positive number (number of bytes transmitted) or negative for error @@ -502,22 +479,15 @@ static int keystone2_eth_send_packet(struct eth_device *dev, { int ret_status = -1; struct eth_priv_t *eth_priv = (struct eth_priv_t *)dev->priv; + struct phy_device *phy_dev = eth_priv->phy_dev; - tx_send_loop = 0; - - if (keystone_get_link_status(dev) == 0) + genphy_update_link(phy_dev); + if (phy_dev->link == 0) return -1; - emac_gigabit_enable(dev); - if (cpmac_drv_send((u32 *)packet, length, eth_priv->slave_port) != 0) return ret_status; - if (keystone_get_link_status(dev) == 0) - return -1; - - emac_gigabit_enable(dev); - return length; } @@ -530,13 +500,13 @@ static int keystone2_eth_rcv_packet(struct eth_device *dev) int pkt_size; u32 *pkt; - hd = netcp_recv(&pkt, &pkt_size); + hd = ksnav_recv(&netcp_pktdma, &pkt, &pkt_size); if (hd == NULL) return 0; NetReceive((uchar *)pkt, pkt_size); - netcp_release_rxhd(hd); + ksnav_release_rxhd(&netcp_pktdma, hd); return pkt_size; } @@ -546,7 +516,9 @@ static int keystone2_eth_rcv_packet(struct eth_device *dev) */ int keystone2_emac_initialize(struct eth_priv_t *eth_priv) { + int res; struct eth_device *dev; + struct phy_device *phy_dev; dev = malloc(sizeof(struct eth_device)); if (dev == NULL) @@ -567,145 +539,55 @@ int keystone2_emac_initialize(struct eth_priv_t *eth_priv) eth_register(dev); - return 0; -} - -void sgmii_serdes_setup_156p25mhz(void) -{ - unsigned int cnt; - - /* - * configure Serializer/Deserializer (SerDes) hardware. SerDes IP - * hardware vendor published only register addresses and their values - * to be used for configuring SerDes. So had to use hardcoded values - * below. - */ - clrsetbits_le32(0x0232a000, 0xffff0000, 0x00800000); - clrsetbits_le32(0x0232a014, 0x0000ffff, 0x00008282); - clrsetbits_le32(0x0232a060, 0x00ffffff, 0x00142438); - clrsetbits_le32(0x0232a064, 0x00ffff00, 0x00c3c700); - clrsetbits_le32(0x0232a078, 0x0000ff00, 0x0000c000); - - clrsetbits_le32(0x0232a204, 0xff0000ff, 0x38000080); - clrsetbits_le32(0x0232a208, 0x000000ff, 0x00000000); - clrsetbits_le32(0x0232a20c, 0xff000000, 0x02000000); - clrsetbits_le32(0x0232a210, 0xff000000, 0x1b000000); - clrsetbits_le32(0x0232a214, 0x0000ffff, 0x00006fb8); - clrsetbits_le32(0x0232a218, 0xffff00ff, 0x758000e4); - clrsetbits_le32(0x0232a2ac, 0x0000ff00, 0x00004400); - clrsetbits_le32(0x0232a22c, 0x00ffff00, 0x00200800); - clrsetbits_le32(0x0232a280, 0x00ff00ff, 0x00820082); - clrsetbits_le32(0x0232a284, 0xffffffff, 0x1d0f0385); - - clrsetbits_le32(0x0232a404, 0xff0000ff, 0x38000080); - clrsetbits_le32(0x0232a408, 0x000000ff, 0x00000000); - clrsetbits_le32(0x0232a40c, 0xff000000, 0x02000000); - clrsetbits_le32(0x0232a410, 0xff000000, 0x1b000000); - clrsetbits_le32(0x0232a414, 0x0000ffff, 0x00006fb8); - clrsetbits_le32(0x0232a418, 0xffff00ff, 0x758000e4); - clrsetbits_le32(0x0232a4ac, 0x0000ff00, 0x00004400); - clrsetbits_le32(0x0232a42c, 0x00ffff00, 0x00200800); - clrsetbits_le32(0x0232a480, 0x00ff00ff, 0x00820082); - clrsetbits_le32(0x0232a484, 0xffffffff, 0x1d0f0385); - - clrsetbits_le32(0x0232a604, 0xff0000ff, 0x38000080); - clrsetbits_le32(0x0232a608, 0x000000ff, 0x00000000); - clrsetbits_le32(0x0232a60c, 0xff000000, 0x02000000); - clrsetbits_le32(0x0232a610, 0xff000000, 0x1b000000); - clrsetbits_le32(0x0232a614, 0x0000ffff, 0x00006fb8); - clrsetbits_le32(0x0232a618, 0xffff00ff, 0x758000e4); - clrsetbits_le32(0x0232a6ac, 0x0000ff00, 0x00004400); - clrsetbits_le32(0x0232a62c, 0x00ffff00, 0x00200800); - clrsetbits_le32(0x0232a680, 0x00ff00ff, 0x00820082); - clrsetbits_le32(0x0232a684, 0xffffffff, 0x1d0f0385); - - clrsetbits_le32(0x0232a804, 0xff0000ff, 0x38000080); - clrsetbits_le32(0x0232a808, 0x000000ff, 0x00000000); - clrsetbits_le32(0x0232a80c, 0xff000000, 0x02000000); - clrsetbits_le32(0x0232a810, 0xff000000, 0x1b000000); - clrsetbits_le32(0x0232a814, 0x0000ffff, 0x00006fb8); - clrsetbits_le32(0x0232a818, 0xffff00ff, 0x758000e4); - clrsetbits_le32(0x0232a8ac, 0x0000ff00, 0x00004400); - clrsetbits_le32(0x0232a82c, 0x00ffff00, 0x00200800); - clrsetbits_le32(0x0232a880, 0x00ff00ff, 0x00820082); - clrsetbits_le32(0x0232a884, 0xffffffff, 0x1d0f0385); - - clrsetbits_le32(0x0232aa00, 0x0000ff00, 0x00000800); - clrsetbits_le32(0x0232aa08, 0xffff0000, 0x38a20000); - clrsetbits_le32(0x0232aa30, 0x00ffff00, 0x008a8a00); - clrsetbits_le32(0x0232aa84, 0x0000ff00, 0x00000600); - clrsetbits_le32(0x0232aa94, 0xff000000, 0x10000000); - clrsetbits_le32(0x0232aaa0, 0xff000000, 0x81000000); - clrsetbits_le32(0x0232aabc, 0xff000000, 0xff000000); - clrsetbits_le32(0x0232aac0, 0x000000ff, 0x0000008b); - clrsetbits_le32(0x0232ab08, 0xffff0000, 0x583f0000); - clrsetbits_le32(0x0232ab0c, 0x000000ff, 0x0000004e); - clrsetbits_le32(0x0232a000, 0x000000ff, 0x00000003); - clrsetbits_le32(0x0232aa00, 0x000000ff, 0x0000005f); - - clrsetbits_le32(0x0232aa48, 0x00ffff00, 0x00fd8c00); - clrsetbits_le32(0x0232aa54, 0x00ffffff, 0x002fec72); - clrsetbits_le32(0x0232aa58, 0xffffff00, 0x00f92100); - clrsetbits_le32(0x0232aa5c, 0xffffffff, 0x00040060); - clrsetbits_le32(0x0232aa60, 0xffffffff, 0x00008000); - clrsetbits_le32(0x0232aa64, 0xffffffff, 0x0c581220); - clrsetbits_le32(0x0232aa68, 0xffffffff, 0xe13b0602); - clrsetbits_le32(0x0232aa6c, 0xffffffff, 0xb8074cc1); - clrsetbits_le32(0x0232aa70, 0xffffffff, 0x3f02e989); - clrsetbits_le32(0x0232aa74, 0x000000ff, 0x00000001); - clrsetbits_le32(0x0232ab20, 0x00ff0000, 0x00370000); - clrsetbits_le32(0x0232ab1c, 0xff000000, 0x37000000); - clrsetbits_le32(0x0232ab20, 0x000000ff, 0x0000005d); - - /*Bring SerDes out of Reset if SerDes is Shutdown & is in Reset Mode*/ - clrbits_le32(0x0232a010, 1 << 28); - - /* Enable TX and RX via the LANExCTL_STS 0x0000 + x*4 */ - clrbits_le32(0x0232a228, 1 << 29); - writel(0xF800F8C0, 0x0232bfe0); - clrbits_le32(0x0232a428, 1 << 29); - writel(0xF800F8C0, 0x0232bfe4); - clrbits_le32(0x0232a628, 1 << 29); - writel(0xF800F8C0, 0x0232bfe8); - clrbits_le32(0x0232a828, 1 << 29); - writel(0xF800F8C0, 0x0232bfec); - - /*Enable pll via the pll_ctrl 0x0014*/ - writel(0xe0000000, 0x0232bff4) - ; - - /*Waiting for SGMII Serdes PLL lock.*/ - for (cnt = 10000; cnt > 0 && ((readl(0x02090114) & 0x10) == 0); cnt--) - ; - - for (cnt = 10000; cnt > 0 && ((readl(0x02090214) & 0x10) == 0); cnt--) - ; - - for (cnt = 10000; cnt > 0 && ((readl(0x02090414) & 0x10) == 0); cnt--) - ; + /* Register MDIO bus if it's not registered yet */ + if (!mdio_bus) { + mdio_bus = mdio_alloc(); + mdio_bus->read = keystone2_mdio_read; + mdio_bus->write = keystone2_mdio_write; + mdio_bus->reset = keystone2_mdio_reset; + mdio_bus->priv = (void *)EMAC_MDIO_BASE_ADDR; + sprintf(mdio_bus->name, "ethernet-mdio"); + + res = mdio_register(mdio_bus); + if (res) + return res; + } - for (cnt = 10000; cnt > 0 && ((readl(0x02090514) & 0x10) == 0); cnt--) - ; + /* Create phy device and bind it with driver */ +#ifdef CONFIG_KSNET_MDIO_PHY_CONFIG_ENABLE + phy_dev = phy_connect(mdio_bus, eth_priv->phy_addr, + dev, PHY_INTERFACE_MODE_SGMII); + phy_config(phy_dev); +#else + phy_dev = phy_find_by_mask(mdio_bus, 1 << eth_priv->phy_addr, + PHY_INTERFACE_MODE_SGMII); + phy_dev->dev = dev; +#endif + eth_priv->phy_dev = phy_dev; - udelay(45000); + return 0; } -void sgmii_serdes_shutdown(void) +struct ks2_serdes ks2_serdes_sgmii_156p25mhz = { + .clk = SERDES_CLOCK_156P25M, + .rate = SERDES_RATE_5G, + .rate_mode = SERDES_QUARTER_RATE, + .intf = SERDES_PHY_SGMII, + .loopback = 0, +}; + +static void keystone2_net_serdes_setup(void) { - /* - * shutdown SerDes hardware. SerDes hardware vendor published only - * register addresses and their values. So had to use hardcoded - * values below. - */ - clrbits_le32(0x0232bfe0, 3 << 29 | 3 << 13); - setbits_le32(0x02320228, 1 << 29); - clrbits_le32(0x0232bfe4, 3 << 29 | 3 << 13); - setbits_le32(0x02320428, 1 << 29); - clrbits_le32(0x0232bfe8, 3 << 29 | 3 << 13); - setbits_le32(0x02320628, 1 << 29); - clrbits_le32(0x0232bfec, 3 << 29 | 3 << 13); - setbits_le32(0x02320828, 1 << 29); - - clrbits_le32(0x02320034, 3 << 29); - setbits_le32(0x02320010, 1 << 28); + ks2_serdes_init(CONFIG_KSNET_SERDES_SGMII_BASE, + &ks2_serdes_sgmii_156p25mhz, + CONFIG_KSNET_SERDES_LANES_PER_SGMII); + +#ifdef CONFIG_SOC_K2E + ks2_serdes_init(CONFIG_KSNET_SERDES_SGMII2_BASE, + &ks2_serdes_sgmii_156p25mhz, + CONFIG_KSNET_SERDES_LANES_PER_SGMII); +#endif + + /* wait till setup */ + udelay(5000); } diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index f1ace3c..467c972 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -648,7 +648,7 @@ static struct phy_device *get_phy_device_by_mask(struct mii_dev *bus, if (phydev) return phydev; } - printf("Phy not found\n"); + printf("Phy %d not found\n", ffs(phy_mask) - 1); return phy_device_create(bus, ffs(phy_mask) - 1, 0xffffffff, interface); } diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile new file mode 100644 index 0000000..3d4baa5 --- /dev/null +++ b/drivers/soc/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the U-boot SOC specific device drivers. +# + +obj-$(CONFIG_ARCH_KEYSTONE) += keystone/ diff --git a/drivers/soc/keystone/Makefile b/drivers/soc/keystone/Makefile new file mode 100644 index 0000000..c000eca --- /dev/null +++ b/drivers/soc/keystone/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TI_KEYSTONE_SERDES) += keystone_serdes.o diff --git a/drivers/soc/keystone/keystone_serdes.c b/drivers/soc/keystone/keystone_serdes.c new file mode 100644 index 0000000..dd5eac9 --- /dev/null +++ b/drivers/soc/keystone/keystone_serdes.c @@ -0,0 +1,210 @@ +/* + * TI serdes driver for keystone2. + * + * (C) Copyright 2014 + * Texas Instruments Incorporated, <www.ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <errno.h> +#include <common.h> +#include <asm/ti-common/keystone_serdes.h> + +#define SERDES_CMU_REGS(x) (0x0000 + (0x0c00 * (x))) +#define SERDES_LANE_REGS(x) (0x0200 + (0x200 * (x))) +#define SERDES_COMLANE_REGS 0x0a00 +#define SERDES_WIZ_REGS 0x1fc0 + +#define SERDES_CMU_REG_000(x) (SERDES_CMU_REGS(x) + 0x000) +#define SERDES_CMU_REG_010(x) (SERDES_CMU_REGS(x) + 0x010) +#define SERDES_COMLANE_REG_000 (SERDES_COMLANE_REGS + 0x000) +#define SERDES_LANE_REG_000(x) (SERDES_LANE_REGS(x) + 0x000) +#define SERDES_LANE_REG_028(x) (SERDES_LANE_REGS(x) + 0x028) +#define SERDES_LANE_CTL_STATUS_REG(x) (SERDES_WIZ_REGS + 0x0020 + (4 * (x))) +#define SERDES_PLL_CTL_REG (SERDES_WIZ_REGS + 0x0034) + +#define SERDES_RESET BIT(28) +#define SERDES_LANE_RESET BIT(29) +#define SERDES_LANE_LOOPBACK BIT(30) +#define SERDES_LANE_EN_VAL(x, y, z) (x[y] | (z << 26) | (z << 10)) + +#define SERDES_CMU_CFG_NUM 5 +#define SERDES_COMLANE_CFG_NUM 10 +#define SERDES_LANE_CFG_NUM 10 + +struct serdes_cfg { + u32 ofs; + u32 val; + u32 mask; +}; + +struct cfg_entry { + enum ks2_serdes_clock clk; + enum ks2_serdes_rate rate; + struct serdes_cfg cmu[SERDES_CMU_CFG_NUM]; + struct serdes_cfg comlane[SERDES_COMLANE_CFG_NUM]; + struct serdes_cfg lane[SERDES_LANE_CFG_NUM]; +}; + +/* SERDES PHY lane enable configuration value, indexed by PHY interface */ +static u32 serdes_cfg_lane_enable[] = { + 0xf000f0c0, /* SGMII */ + 0xf0e9f038, /* PCSR */ +}; + +/* SERDES PHY PLL enable configuration value, indexed by PHY interface */ +static u32 serdes_cfg_pll_enable[] = { + 0xe0000000, /* SGMII */ + 0xee000000, /* PCSR */ +}; + +/** + * Array to hold all possible serdes configurations. + * Combination for 5 clock settings and 6 baud rates. + */ +static struct cfg_entry cfgs[] = { + { + .clk = SERDES_CLOCK_156P25M, + .rate = SERDES_RATE_5G, + .cmu = { + {0x0000, 0x00800000, 0xffff0000}, + {0x0014, 0x00008282, 0x0000ffff}, + {0x0060, 0x00142438, 0x00ffffff}, + {0x0064, 0x00c3c700, 0x00ffff00}, + {0x0078, 0x0000c000, 0x0000ff00} + }, + .comlane = { + {0x0a00, 0x00000800, 0x0000ff00}, + {0x0a08, 0x38a20000, 0xffff0000}, + {0x0a30, 0x008a8a00, 0x00ffff00}, + {0x0a84, 0x00000600, 0x0000ff00}, + {0x0a94, 0x10000000, 0xff000000}, + {0x0aa0, 0x81000000, 0xff000000}, + {0x0abc, 0xff000000, 0xff000000}, + {0x0ac0, 0x0000008b, 0x000000ff}, + {0x0b08, 0x583f0000, 0xffff0000}, + {0x0b0c, 0x0000004e, 0x000000ff} + }, + .lane = { + {0x0004, 0x38000080, 0xff0000ff}, + {0x0008, 0x00000000, 0x000000ff}, + {0x000c, 0x02000000, 0xff000000}, + {0x0010, 0x1b000000, 0xff000000}, + {0x0014, 0x00006fb8, 0x0000ffff}, + {0x0018, 0x758000e4, 0xffff00ff}, + {0x00ac, 0x00004400, 0x0000ff00}, + {0x002c, 0x00100800, 0x00ffff00}, + {0x0080, 0x00820082, 0x00ff00ff}, + {0x0084, 0x1d0f0385, 0xffffffff} + }, + }, +}; + +static inline void ks2_serdes_rmw(u32 addr, u32 value, u32 mask) +{ + writel(((readl(addr) & (~mask)) | (value & mask)), addr); +} + +static void ks2_serdes_cfg_setup(u32 base, struct serdes_cfg *cfg, u32 size) +{ + u32 i; + + for (i = 0; i < size; i++) + ks2_serdes_rmw(base + cfg[i].ofs, cfg[i].val, cfg[i].mask); +} + +static void ks2_serdes_lane_config(u32 base, struct serdes_cfg *cfg_lane, + u32 size, u32 lane) +{ + u32 i; + + for (i = 0; i < size; i++) + ks2_serdes_rmw(base + cfg_lane[i].ofs + SERDES_LANE_REGS(lane), + cfg_lane[i].val, cfg_lane[i].mask); +} + +static int ks2_serdes_init_cfg(u32 base, struct cfg_entry *cfg, u32 num_lanes) +{ + u32 i; + + ks2_serdes_cfg_setup(base, cfg->cmu, SERDES_CMU_CFG_NUM); + ks2_serdes_cfg_setup(base, cfg->comlane, SERDES_COMLANE_CFG_NUM); + + for (i = 0; i < num_lanes; i++) + ks2_serdes_lane_config(base, cfg->lane, SERDES_LANE_CFG_NUM, i); + + return 0; +} + +static void ks2_serdes_cmu_comlane_enable(u32 base, struct ks2_serdes *serdes) +{ + /* Bring SerDes out of Reset */ + ks2_serdes_rmw(base + SERDES_CMU_REG_010(0), 0x0, SERDES_RESET); + if (serdes->intf == SERDES_PHY_PCSR) + ks2_serdes_rmw(base + SERDES_CMU_REG_010(1), 0x0, SERDES_RESET); + + /* Enable CMU and COMLANE */ + ks2_serdes_rmw(base + SERDES_CMU_REG_000(0), 0x03, 0x000000ff); + if (serdes->intf == SERDES_PHY_PCSR) + ks2_serdes_rmw(base + SERDES_CMU_REG_000(1), 0x03, 0x000000ff); + + ks2_serdes_rmw(base + SERDES_COMLANE_REG_000, 0x5f, 0x000000ff); +} + +static void ks2_serdes_pll_enable(u32 base, struct ks2_serdes *serdes) +{ + writel(serdes_cfg_pll_enable[serdes->intf], + base + SERDES_PLL_CTL_REG); +} + +static void ks2_serdes_lane_reset(u32 base, u32 reset, u32 lane) +{ + if (reset) + ks2_serdes_rmw(base + SERDES_LANE_REG_028(lane), + 0x1, SERDES_LANE_RESET); + else + ks2_serdes_rmw(base + SERDES_LANE_REG_028(lane), + 0x0, SERDES_LANE_RESET); +} + +static void ks2_serdes_lane_enable(u32 base, + struct ks2_serdes *serdes, u32 lane) +{ + /* Bring lane out of reset */ + ks2_serdes_lane_reset(base, 0, lane); + + writel(SERDES_LANE_EN_VAL(serdes_cfg_lane_enable, serdes->intf, + serdes->rate_mode), + base + SERDES_LANE_CTL_STATUS_REG(lane)); + + /* Set NES bit if Loopback Enabled */ + if (serdes->loopback) + ks2_serdes_rmw(base + SERDES_LANE_REG_000(lane), + 0x1, SERDES_LANE_LOOPBACK); +} + +int ks2_serdes_init(u32 base, struct ks2_serdes *serdes, u32 num_lanes) +{ + int i; + int ret = 0; + + for (i = 0; i < ARRAY_SIZE(cfgs); i++) + if (serdes->clk == cfgs[i].clk && serdes->rate == cfgs[i].rate) + break; + + if (i >= ARRAY_SIZE(cfgs)) { + puts("Cannot find keystone SerDes configuration"); + return -EINVAL; + } + + ks2_serdes_init_cfg(base, &cfgs[i], num_lanes); + + ks2_serdes_cmu_comlane_enable(base, serdes); + for (i = 0; i < num_lanes; i++) + ks2_serdes_lane_enable(base, serdes, i); + + ks2_serdes_pll_enable(base, serdes); + + return ret; +} diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c index 026f680..be10269 100644 --- a/drivers/spi/mxc_spi.c +++ b/drivers/spi/mxc_spi.c @@ -12,6 +12,7 @@ #include <asm/gpio.h> #include <asm/arch/imx-regs.h> #include <asm/arch/clock.h> +#include <asm/imx-common/spi.h> #ifdef CONFIG_MX27 /* i.MX27 has a completely wrong register layout and register definitions in the diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index c9d2ed5..1c35929 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_USB_EHCI_ZYNQ) += ehci-zynq.o # xhci obj-$(CONFIG_USB_XHCI) += xhci.o xhci-mem.o xhci-ring.o +obj-$(CONFIG_USB_XHCI_KEYSTONE) += xhci-keystone.o obj-$(CONFIG_USB_XHCI_EXYNOS) += xhci-exynos5.o obj-$(CONFIG_USB_XHCI_OMAP) += xhci-omap.o diff --git a/drivers/usb/host/xhci-keystone.c b/drivers/usb/host/xhci-keystone.c new file mode 100644 index 0000000..05d338f --- /dev/null +++ b/drivers/usb/host/xhci-keystone.c @@ -0,0 +1,329 @@ +/* + * USB 3.0 DRD Controller + * + * (C) Copyright 2012-2014 + * Texas Instruments Incorporated, <www.ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <watchdog.h> +#include <usb.h> +#include <asm/arch/psc_defs.h> +#include <asm/io.h> +#include <linux/usb/dwc3.h> +#include <asm/arch/xhci-keystone.h> +#include <asm-generic/errno.h> +#include <linux/list.h> +#include "xhci.h" + +struct kdwc3_irq_regs { + u32 revision; /* 0x000 */ + u32 rsvd0[3]; + u32 sysconfig; /* 0x010 */ + u32 rsvd1[1]; + u32 irq_eoi; + u32 rsvd2[1]; + struct { + u32 raw_status; + u32 status; + u32 enable_set; + u32 enable_clr; + } irqs[16]; +}; + +struct keystone_xhci { + struct xhci_hccr *hcd; + struct dwc3 *dwc3_reg; + struct xhci_hcor *hcor; + struct kdwc3_irq_regs *usbss; + struct keystone_xhci_phy *phy; +}; + +struct keystone_xhci keystone; + +static void keystone_xhci_phy_set(struct keystone_xhci_phy *phy) +{ + u32 val; + + /* + * VBUSVLDEXTSEL has a default value of 1 in BootCfg but shouldn't. + * It should always be cleared because our USB PHY has an onchip VBUS + * analog comparator. + */ + val = readl(&phy->phy_clock); + /* quit selecting the vbusvldextsel by default! */ + val &= ~USB3_PHY_OTG_VBUSVLDECTSEL; + writel(val, &phy->phy_clock); +} + +static void keystone_xhci_phy_unset(struct keystone_xhci_phy *phy) +{ + u32 val; + + /* Disable the PHY REFCLK clock gate */ + val = readl(&phy->phy_clock); + val &= ~USB3_PHY_REF_SSP_EN; + writel(val, &phy->phy_clock); +} + +static void dwc3_set_mode(struct dwc3 *dwc3_reg, u32 mode) +{ + clrsetbits_le32(&dwc3_reg->g_ctl, + DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG), + DWC3_GCTL_PRTCAPDIR(mode)); +} + +static void dwc3_core_soft_reset(struct dwc3 *dwc3_reg) +{ + /* Before Resetting PHY, put Core in Reset */ + setbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET); + + /* Assert USB3 PHY reset */ + setbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); + + /* Assert USB2 PHY reset */ + setbits_le32(&dwc3_reg->g_usb2phycfg[0], DWC3_GUSB2PHYCFG_PHYSOFTRST); + + mdelay(100); + + /* Clear USB3 PHY reset */ + clrbits_le32(&dwc3_reg->g_usb3pipectl[0], DWC3_GUSB3PIPECTL_PHYSOFTRST); + + /* Clear USB2 PHY reset */ + clrbits_le32(&dwc3_reg->g_usb2phycfg[0], DWC3_GUSB2PHYCFG_PHYSOFTRST); + + /* After PHYs are stable we can take Core out of reset state */ + clrbits_le32(&dwc3_reg->g_ctl, DWC3_GCTL_CORESOFTRESET); +} + +static int dwc3_core_init(struct dwc3 *dwc3_reg) +{ + u32 revision, val; + unsigned long t_rst; + unsigned int dwc3_hwparams1; + + revision = readl(&dwc3_reg->g_snpsid); + /* This should read as U3 followed by revision number */ + if ((revision & DWC3_GSNPSID_MASK) != 0x55330000) { + puts("this is not a DesignWare USB3 DRD Core\n"); + return -EINVAL; + } + + /* issue device SoftReset too */ + writel(DWC3_DCTL_CSFTRST, &dwc3_reg->d_ctl); + + t_rst = get_timer(0); + do { + val = readl(&dwc3_reg->d_ctl); + if (!(val & DWC3_DCTL_CSFTRST)) + break; + WATCHDOG_RESET(); + } while (get_timer(t_rst) < 500); + + if (val & DWC3_DCTL_CSFTRST) { + debug("Reset timed out\n"); + return -2; + } + + dwc3_core_soft_reset(dwc3_reg); + + dwc3_hwparams1 = readl(&dwc3_reg->g_hwparams1); + + val = readl(&dwc3_reg->g_ctl); + val &= ~DWC3_GCTL_SCALEDOWN_MASK; + val &= ~DWC3_GCTL_DISSCRAMBLE; + switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc3_hwparams1)) { + case DWC3_GHWPARAMS1_EN_PWROPT_CLK: + val &= ~DWC3_GCTL_DSBLCLKGTNG; + break; + default: + printf("No power optimization available\n"); + } + + /* + * WORKAROUND: DWC3 revisions <1.90a have a bug + * where the device can fail to connect at SuperSpeed + * and falls back to high-speed mode which causes + * the device to enter a Connect/Disconnect loop + */ + if ((revision & DWC3_REVISION_MASK) < 0x190a) + val |= DWC3_GCTL_U2RSTECN; + + writel(val, &dwc3_reg->g_ctl); + + return 0; +} + +static int keystone_xhci_core_init(struct dwc3 *dwc3_reg) +{ + int ret; + + ret = dwc3_core_init(dwc3_reg); + if (ret) { + debug("failed to initialize core\n"); + return -EINVAL; + } + + /* We are hard-coding DWC3 core to Host Mode */ + dwc3_set_mode(dwc3_reg, DWC3_GCTL_PRTCAP_HOST); + + return 0; +} + +int xhci_hcd_init(int index, + struct xhci_hccr **ret_hccr, struct xhci_hcor **ret_hcor) +{ + u32 val; + int ret; + struct xhci_hccr *hcd; + struct xhci_hcor *hcor; + struct kdwc3_irq_regs *usbss; + struct keystone_xhci_phy *phy; + + usbss = (struct kdwc3_irq_regs *)CONFIG_USB_SS_BASE; + phy = (struct keystone_xhci_phy *)CONFIG_DEV_USB_PHY_BASE; + + /* Enable the PHY REFCLK clock gate with phy_ref_ssp_en = 1 */ + val = readl(&(phy->phy_clock)); + val |= USB3_PHY_REF_SSP_EN; + writel(val, &phy->phy_clock); + + mdelay(100); + + /* Release USB from reset */ + ret = psc_enable_module(KS2_LPSC_USB); + if (ret) { + puts("Cannot enable USB module"); + return -1; + } + + mdelay(100); + + /* Initialize usb phy */ + keystone_xhci_phy_set(phy); + + /* soft reset usbss */ + writel(1, &usbss->sysconfig); + while (readl(&usbss->sysconfig) & 1) + ; + + val = readl(&usbss->revision); + debug("usbss revision %x\n", val); + + /* Initialize usb core */ + hcd = (struct xhci_hccr *)CONFIG_USB_HOST_XHCI_BASE; + keystone.dwc3_reg = (struct dwc3 *)(CONFIG_USB_HOST_XHCI_BASE + + DWC3_REG_OFFSET); + + keystone_xhci_core_init(keystone.dwc3_reg); + + /* set register addresses */ + hcor = (struct xhci_hcor *)((uint32_t)hcd + + HC_LENGTH(readl(&hcd->cr_capbase))); + + debug("Keystone2-xhci: init hccr %08x and hcor %08x hc_length %d\n", + (u32)hcd, (u32)hcor, + (u32)HC_LENGTH(xhci_readl(&hcd->cr_capbase))); + + keystone.usbss = usbss; + keystone.phy = phy; + keystone.hcd = hcd; + keystone.hcor = hcor; + + *ret_hccr = hcd; + *ret_hcor = hcor; + + return 0; +} + +static int keystone_xhci_phy_suspend(void) +{ + int loop_cnt = 0; + struct xhci_hcor *hcor; + uint32_t *portsc_1 = NULL; + uint32_t *portsc_2 = NULL; + u32 val, usb2_pls, usb3_pls, event_q; + struct dwc3 *dwc3_reg = keystone.dwc3_reg; + + /* set register addresses */ + hcor = keystone.hcor; + + /* Bypass Scrambling and Set Shorter Training sequence for simulation */ + val = DWC3_GCTL_PWRDNSCALE(0x4b0) | DWC3_GCTL_PRTCAPDIR(0x2); + writel(val, &dwc3_reg->g_ctl); + + /* GUSB2PHYCFG */ + val = readl(&dwc3_reg->g_usb2phycfg[0]); + + /* assert bit 6 (SusPhy) */ + val |= DWC3_GUSB2PHYCFG_SUSPHY; + writel(val, &dwc3_reg->g_usb2phycfg[0]); + + /* GUSB3PIPECTL */ + val = readl(&dwc3_reg->g_usb3pipectl[0]); + + /* + * assert bit 29 to allow PHY to go to suspend when idle + * and cause the USB3 SS PHY to enter suspend mode + */ + val |= (BIT(29) | DWC3_GUSB3PIPECTL_SUSPHY); + writel(val, &dwc3_reg->g_usb3pipectl[0]); + + /* + * Steps necessary to allow controller to suspend even when + * VBUS is HIGH: + * - Init DCFG[2:0] (DevSpd) to: 1=FS + * - Init GEVNTADR0 to point to an eventQ + * - Init GEVNTSIZ0 to 0x0100 to specify the size of the eventQ + * - Init DCTL::Run_nStop = 1 + */ + writel(0x00020001, &dwc3_reg->d_cfg); + /* TODO: local2global( (Uint32) eventQ )? */ + writel((u32)&event_q, &dwc3_reg->g_evnt_buf[0].g_evntadrlo); + writel(0, &dwc3_reg->g_evnt_buf[0].g_evntadrhi); + writel(0x4, &dwc3_reg->g_evnt_buf[0].g_evntsiz); + /* Run */ + writel(DWC3_DCTL_RUN_STOP, &dwc3_reg->d_ctl); + + mdelay(100); + + /* Wait for USB2 & USB3 PORTSC::PortLinkState to indicate suspend */ + portsc_1 = (uint32_t *)(&hcor->portregs[0].or_portsc); + portsc_2 = (uint32_t *)(&hcor->portregs[1].or_portsc); + usb2_pls = 0; + usb3_pls = 0; + do { + ++loop_cnt; + usb2_pls = (readl(portsc_1) & PORT_PLS_MASK) >> 5; + usb3_pls = (readl(portsc_2) & PORT_PLS_MASK) >> 5; + } while (((usb2_pls != 0x4) || (usb3_pls != 0x4)) && loop_cnt < 1000); + + if (usb2_pls != 0x4 || usb3_pls != 0x4) { + debug("USB suspend failed - PLS USB2=%02x, USB3=%02x\n", + usb2_pls, usb3_pls); + return -1; + } + + debug("USB2 and USB3 PLS - Disabled, loop_cnt=%d\n", loop_cnt); + return 0; +} + +void xhci_hcd_stop(int index) +{ + /* Disable USB */ + if (keystone_xhci_phy_suspend()) + return; + + if (psc_disable_module(KS2_LPSC_USB)) { + debug("PSC disable module USB failed!\n"); + return; + } + + /* Disable PHY */ + keystone_xhci_phy_unset(keystone.phy); + +/* memset(&keystone, 0, sizeof(struct keystone_xhci)); */ + debug("xhci_hcd_stop OK.\n"); +} |