1ad309a88SDingqiang Lin /* 2ad309a88SDingqiang Lin * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd 3ad309a88SDingqiang Lin * 4ad309a88SDingqiang Lin * SPDX-License-Identifier: (GPL-2.0+ OR MIT) 5ad309a88SDingqiang Lin */ 6ad309a88SDingqiang Lin 7ad309a88SDingqiang Lin #include <common.h> 8ad309a88SDingqiang Lin #include <linux/delay.h> 9ad309a88SDingqiang Lin #include <bouncebuf.h> 10ad309a88SDingqiang Lin #include <asm/io.h> 11ad309a88SDingqiang Lin 12ad309a88SDingqiang Lin #include "sfc.h" 13ad309a88SDingqiang Lin 14ad309a88SDingqiang Lin static void __iomem *g_sfc_reg; 15ad309a88SDingqiang Lin 16ad309a88SDingqiang Lin static void sfc_reset(void) 17ad309a88SDingqiang Lin { 18ad309a88SDingqiang Lin int timeout = 10000; 19ad309a88SDingqiang Lin 20ad309a88SDingqiang Lin writel(SFC_RESET, g_sfc_reg + SFC_RCVR); 21ad309a88SDingqiang Lin while ((readl(g_sfc_reg + SFC_RCVR) == SFC_RESET) && (timeout > 0)) { 22ad309a88SDingqiang Lin sfc_delay(1); 23ad309a88SDingqiang Lin timeout--; 24ad309a88SDingqiang Lin } 25ad309a88SDingqiang Lin writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR); 26ad309a88SDingqiang Lin } 27ad309a88SDingqiang Lin 28ad309a88SDingqiang Lin u16 sfc_get_version(void) 29ad309a88SDingqiang Lin { 30ad309a88SDingqiang Lin return (u32)(readl(g_sfc_reg + SFC_VER) & 0xffff); 31ad309a88SDingqiang Lin } 32ad309a88SDingqiang Lin 33ad309a88SDingqiang Lin int sfc_init(void __iomem *reg_addr) 34ad309a88SDingqiang Lin { 35ad309a88SDingqiang Lin g_sfc_reg = reg_addr; 36ad309a88SDingqiang Lin sfc_reset(); 37ad309a88SDingqiang Lin writel(0, g_sfc_reg + SFC_CTRL); 38ad309a88SDingqiang Lin 39ad309a88SDingqiang Lin return SFC_OK; 40ad309a88SDingqiang Lin } 41ad309a88SDingqiang Lin 42ad309a88SDingqiang Lin void sfc_clean_irq(void) 43ad309a88SDingqiang Lin { 44ad309a88SDingqiang Lin writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR); 45ad309a88SDingqiang Lin writel(0xFFFFFFFF, g_sfc_reg + SFC_IMR); 46ad309a88SDingqiang Lin } 47ad309a88SDingqiang Lin 48ad309a88SDingqiang Lin int sfc_request(u32 sfcmd, u32 sfctrl, u32 addr, void *data) 49ad309a88SDingqiang Lin { 50ad309a88SDingqiang Lin int ret = SFC_OK; 51ad309a88SDingqiang Lin union SFCCMD_DATA cmd; 52ad309a88SDingqiang Lin int reg; 53ad309a88SDingqiang Lin int timeout = 0; 54ad309a88SDingqiang Lin 55ad309a88SDingqiang Lin reg = readl(g_sfc_reg + SFC_FSR); 56ad309a88SDingqiang Lin if (!(reg & SFC_TXEMPTY) || !(reg & SFC_RXEMPTY) || 57ad309a88SDingqiang Lin (readl(g_sfc_reg + SFC_SR) & SFC_BUSY)) 58ad309a88SDingqiang Lin sfc_reset(); 59ad309a88SDingqiang Lin 60ad309a88SDingqiang Lin cmd.d32 = sfcmd; 61ad309a88SDingqiang Lin if (cmd.b.addrbits == SFC_ADDR_XBITS) { 62ad309a88SDingqiang Lin union SFCCTRL_DATA ctrl; 63ad309a88SDingqiang Lin 64ad309a88SDingqiang Lin ctrl.d32 = sfctrl; 65ad309a88SDingqiang Lin if (!ctrl.b.addrbits) 66ad309a88SDingqiang Lin return SFC_PARAM_ERR; 67ad309a88SDingqiang Lin /* Controller plus 1 automatically */ 68ad309a88SDingqiang Lin writel(ctrl.b.addrbits - 1, g_sfc_reg + SFC_ABIT); 69ad309a88SDingqiang Lin } 70ad309a88SDingqiang Lin /* shift in the data at negedge sclk_out */ 71ad309a88SDingqiang Lin sfctrl |= 0x2; 72ad309a88SDingqiang Lin 73ad309a88SDingqiang Lin writel(sfctrl, g_sfc_reg + SFC_CTRL); 74ad309a88SDingqiang Lin writel(sfcmd, g_sfc_reg + SFC_CMD); 75ad309a88SDingqiang Lin if (cmd.b.addrbits) 76ad309a88SDingqiang Lin writel(addr, g_sfc_reg + SFC_ADDR); 77ad309a88SDingqiang Lin if (!cmd.b.datasize) 78ad309a88SDingqiang Lin goto exit_wait; 79ad309a88SDingqiang Lin if (SFC_ENABLE_DMA & sfctrl) { 80ad309a88SDingqiang Lin struct bounce_buffer bb; 81ad309a88SDingqiang Lin unsigned int bb_flags; 82ad309a88SDingqiang Lin 83ad309a88SDingqiang Lin if (cmd.b.rw == SFC_WRITE) 84ad309a88SDingqiang Lin bb_flags = GEN_BB_READ; 85ad309a88SDingqiang Lin else 86ad309a88SDingqiang Lin bb_flags = GEN_BB_WRITE; 87ad309a88SDingqiang Lin 88ad309a88SDingqiang Lin ret = bounce_buffer_start(&bb, data, cmd.b.datasize, bb_flags); 89ad309a88SDingqiang Lin if (ret) 90ad309a88SDingqiang Lin return ret; 91ad309a88SDingqiang Lin 92ad309a88SDingqiang Lin writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR); 93ad309a88SDingqiang Lin writel(~((u32)FINISH_INT), g_sfc_reg + SFC_IMR); 94*1bb49bc4SDingqiang Lin writel((unsigned long)bb.bounce_buffer, g_sfc_reg + SFC_DMA_ADDR); 95ad309a88SDingqiang Lin writel(SFC_DMA_START, g_sfc_reg + SFC_DMA_TRIGGER); 96ad309a88SDingqiang Lin 97ad309a88SDingqiang Lin timeout = cmd.b.datasize * 10; 98ad309a88SDingqiang Lin while ((readl(g_sfc_reg + SFC_SR) & SFC_BUSY) && 99ad309a88SDingqiang Lin (timeout-- > 0)) 100ad309a88SDingqiang Lin sfc_delay(1); 101ad309a88SDingqiang Lin writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR); 102ad309a88SDingqiang Lin if (timeout <= 0) 103ad309a88SDingqiang Lin ret = SFC_WAIT_TIMEOUT; 104ad309a88SDingqiang Lin bounce_buffer_stop(&bb); 105ad309a88SDingqiang Lin } else { 106ad309a88SDingqiang Lin u32 i, words, count, bytes; 107ad309a88SDingqiang Lin union SFCFSR_DATA fifostat; 108ad309a88SDingqiang Lin u32 *p_data = (u32 *)data; 109ad309a88SDingqiang Lin 110ad309a88SDingqiang Lin if (cmd.b.rw == SFC_WRITE) { 111ad309a88SDingqiang Lin words = (cmd.b.datasize + 3) >> 2; 112ad309a88SDingqiang Lin while (words) { 113ad309a88SDingqiang Lin fifostat.d32 = readl(g_sfc_reg + SFC_FSR); 114ad309a88SDingqiang Lin if (fifostat.b.txlevel > 0) { 115ad309a88SDingqiang Lin count = words < fifostat.b.txlevel ? 116ad309a88SDingqiang Lin words : fifostat.b.txlevel; 117ad309a88SDingqiang Lin for (i = 0; i < count; i++) { 118ad309a88SDingqiang Lin writel(*p_data++, 119ad309a88SDingqiang Lin g_sfc_reg + SFC_DATA); 120ad309a88SDingqiang Lin words--; 121ad309a88SDingqiang Lin } 122ad309a88SDingqiang Lin if (words == 0) 123ad309a88SDingqiang Lin break; 124ad309a88SDingqiang Lin timeout = 0; 125ad309a88SDingqiang Lin } else { 126ad309a88SDingqiang Lin sfc_delay(1); 127ad309a88SDingqiang Lin if (timeout++ > 10000) { 128ad309a88SDingqiang Lin ret = SFC_TX_TIMEOUT; 129ad309a88SDingqiang Lin break; 130ad309a88SDingqiang Lin } 131ad309a88SDingqiang Lin } 132ad309a88SDingqiang Lin } 133ad309a88SDingqiang Lin } else { 134ad309a88SDingqiang Lin /* SFC_READ == cmd.b.rw */ 135ad309a88SDingqiang Lin bytes = cmd.b.datasize & 0x3; 136ad309a88SDingqiang Lin words = cmd.b.datasize >> 2; 137ad309a88SDingqiang Lin while (words) { 138ad309a88SDingqiang Lin fifostat.d32 = readl(g_sfc_reg + SFC_FSR); 139ad309a88SDingqiang Lin if (fifostat.b.rxlevel > 0) { 140ad309a88SDingqiang Lin u32 count; 141ad309a88SDingqiang Lin 142ad309a88SDingqiang Lin count = words < fifostat.b.rxlevel ? 143ad309a88SDingqiang Lin words : fifostat.b.rxlevel; 144ad309a88SDingqiang Lin 145ad309a88SDingqiang Lin for (i = 0; i < count; i++) { 146ad309a88SDingqiang Lin *p_data++ = readl(g_sfc_reg + 147ad309a88SDingqiang Lin SFC_DATA); 148ad309a88SDingqiang Lin words--; 149ad309a88SDingqiang Lin } 150ad309a88SDingqiang Lin if (words == 0) 151ad309a88SDingqiang Lin break; 152ad309a88SDingqiang Lin timeout = 0; 153ad309a88SDingqiang Lin } else { 154ad309a88SDingqiang Lin sfc_delay(1); 155ad309a88SDingqiang Lin if (timeout++ > 10000) { 156ad309a88SDingqiang Lin ret = SFC_RX_TIMEOUT; 157ad309a88SDingqiang Lin break; 158ad309a88SDingqiang Lin } 159ad309a88SDingqiang Lin } 160ad309a88SDingqiang Lin } 161ad309a88SDingqiang Lin 162ad309a88SDingqiang Lin timeout = 0; 163ad309a88SDingqiang Lin while (bytes) { 164ad309a88SDingqiang Lin fifostat.d32 = readl(g_sfc_reg + SFC_FSR); 165ad309a88SDingqiang Lin if (fifostat.b.rxlevel > 0) { 166ad309a88SDingqiang Lin u8 *p_data1 = (u8 *)p_data; 167ad309a88SDingqiang Lin 168ad309a88SDingqiang Lin words = readl(g_sfc_reg + SFC_DATA); 169ad309a88SDingqiang Lin for (i = 0; i < bytes; i++) 170ad309a88SDingqiang Lin p_data1[i] = 171ad309a88SDingqiang Lin (u8)((words >> (i * 8)) & 0xFF); 172ad309a88SDingqiang Lin break; 173ad309a88SDingqiang Lin } 174ad309a88SDingqiang Lin 175ad309a88SDingqiang Lin sfc_delay(1); 176ad309a88SDingqiang Lin if (timeout++ > 10000) { 177ad309a88SDingqiang Lin ret = SFC_RX_TIMEOUT; 178ad309a88SDingqiang Lin break; 179ad309a88SDingqiang Lin } 180ad309a88SDingqiang Lin } 181ad309a88SDingqiang Lin } 182ad309a88SDingqiang Lin } 183ad309a88SDingqiang Lin 184ad309a88SDingqiang Lin exit_wait: 185ad309a88SDingqiang Lin timeout = 0; /* wait cmd or data send complete */ 186ad309a88SDingqiang Lin while (!(readl(g_sfc_reg + SFC_FSR) & SFC_TXEMPTY)) { 187ad309a88SDingqiang Lin sfc_delay(1); 188ad309a88SDingqiang Lin if (timeout++ > 100000) { /* wait 100ms */ 189ad309a88SDingqiang Lin ret = SFC_TX_TIMEOUT; 190ad309a88SDingqiang Lin break; 191ad309a88SDingqiang Lin } 192ad309a88SDingqiang Lin } 193ad309a88SDingqiang Lin sfc_delay(1); /* CS# High Time (read/write) >100ns */ 194ad309a88SDingqiang Lin return ret; 195ad309a88SDingqiang Lin } 196