1ad309a88SDingqiang Lin /* 2ad309a88SDingqiang Lin * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd 3ad309a88SDingqiang Lin * 4ba0501acSDingqiang Lin * SPDX-License-Identifier: GPL-2.0 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 14*534d4d2fSJon Lin #define SFC_MAX_IOSIZE_VER3 (1024 * 8) 15*534d4d2fSJon Lin #define SFC_MAX_IOSIZE_VER4 (0xFFFFFFFF) 16*534d4d2fSJon Lin 17ad309a88SDingqiang Lin static void __iomem *g_sfc_reg; 18ad309a88SDingqiang Lin 19ad309a88SDingqiang Lin static void sfc_reset(void) 20ad309a88SDingqiang Lin { 21ad309a88SDingqiang Lin int timeout = 10000; 22ad309a88SDingqiang Lin 23ad309a88SDingqiang Lin writel(SFC_RESET, g_sfc_reg + SFC_RCVR); 24ad309a88SDingqiang Lin while ((readl(g_sfc_reg + SFC_RCVR) == SFC_RESET) && (timeout > 0)) { 25ad309a88SDingqiang Lin sfc_delay(1); 26ad309a88SDingqiang Lin timeout--; 27ad309a88SDingqiang Lin } 28ad309a88SDingqiang Lin writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR); 29ad309a88SDingqiang Lin } 30ad309a88SDingqiang Lin 31ad309a88SDingqiang Lin u16 sfc_get_version(void) 32ad309a88SDingqiang Lin { 33ad309a88SDingqiang Lin return (u32)(readl(g_sfc_reg + SFC_VER) & 0xffff); 34ad309a88SDingqiang Lin } 35ad309a88SDingqiang Lin 36*534d4d2fSJon Lin u32 sfc_get_max_iosize(void) 37*534d4d2fSJon Lin { 38*534d4d2fSJon Lin if (sfc_get_version() >= SFC_VER_4) 39*534d4d2fSJon Lin return SFC_MAX_IOSIZE_VER4; 40*534d4d2fSJon Lin else 41*534d4d2fSJon Lin return SFC_MAX_IOSIZE_VER3; 42*534d4d2fSJon Lin } 43*534d4d2fSJon Lin 44ad309a88SDingqiang Lin int sfc_init(void __iomem *reg_addr) 45ad309a88SDingqiang Lin { 46ad309a88SDingqiang Lin g_sfc_reg = reg_addr; 47ad309a88SDingqiang Lin sfc_reset(); 48ad309a88SDingqiang Lin writel(0, g_sfc_reg + SFC_CTRL); 49*534d4d2fSJon Lin if (sfc_get_version() >= SFC_VER_4) 50*534d4d2fSJon Lin writel(1, g_sfc_reg + SFC_LEN_CTRL); 51ad309a88SDingqiang Lin 52ad309a88SDingqiang Lin return SFC_OK; 53ad309a88SDingqiang Lin } 54ad309a88SDingqiang Lin 55ad309a88SDingqiang Lin void sfc_clean_irq(void) 56ad309a88SDingqiang Lin { 57ad309a88SDingqiang Lin writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR); 58ad309a88SDingqiang Lin writel(0xFFFFFFFF, g_sfc_reg + SFC_IMR); 59ad309a88SDingqiang Lin } 60ad309a88SDingqiang Lin 6158463f4dSJon Lin int sfc_request(struct rk_sfc_op *op, u32 addr, void *data, u32 size) 62ad309a88SDingqiang Lin { 63ad309a88SDingqiang Lin int ret = SFC_OK; 64ad309a88SDingqiang Lin union SFCCMD_DATA cmd; 65ad309a88SDingqiang Lin int reg; 66ad309a88SDingqiang Lin int timeout = 0; 67ad309a88SDingqiang Lin 68ad309a88SDingqiang Lin reg = readl(g_sfc_reg + SFC_FSR); 69ad309a88SDingqiang Lin if (!(reg & SFC_TXEMPTY) || !(reg & SFC_RXEMPTY) || 70ad309a88SDingqiang Lin (readl(g_sfc_reg + SFC_SR) & SFC_BUSY)) 71ad309a88SDingqiang Lin sfc_reset(); 72ad309a88SDingqiang Lin 7358463f4dSJon Lin cmd.d32 = op->sfcmd.d32; 74ad309a88SDingqiang Lin if (cmd.b.addrbits == SFC_ADDR_XBITS) { 75ad309a88SDingqiang Lin union SFCCTRL_DATA ctrl; 76ad309a88SDingqiang Lin 7758463f4dSJon Lin ctrl.d32 = op->sfctrl.d32; 78ad309a88SDingqiang Lin if (!ctrl.b.addrbits) 79ad309a88SDingqiang Lin return SFC_PARAM_ERR; 80ad309a88SDingqiang Lin /* Controller plus 1 automatically */ 81ad309a88SDingqiang Lin writel(ctrl.b.addrbits - 1, g_sfc_reg + SFC_ABIT); 82ad309a88SDingqiang Lin } 83ad309a88SDingqiang Lin /* shift in the data at negedge sclk_out */ 8458463f4dSJon Lin op->sfctrl.d32 |= 0x2; 8558463f4dSJon Lin cmd.b.datasize = size; 86*534d4d2fSJon Lin if (sfc_get_version() >= SFC_VER_4) 87*534d4d2fSJon Lin writel(size, g_sfc_reg + SFC_LEN_EXT); 88*534d4d2fSJon Lin else 89*534d4d2fSJon Lin cmd.b.datasize = size; 90ad309a88SDingqiang Lin 9158463f4dSJon Lin writel(op->sfctrl.d32, g_sfc_reg + SFC_CTRL); 9258463f4dSJon Lin writel(cmd.d32, g_sfc_reg + SFC_CMD); 93ad309a88SDingqiang Lin if (cmd.b.addrbits) 94ad309a88SDingqiang Lin writel(addr, g_sfc_reg + SFC_ADDR); 9558463f4dSJon Lin if (!size) 96ad309a88SDingqiang Lin goto exit_wait; 9758463f4dSJon Lin if (op->sfctrl.b.enbledma) { 98ad309a88SDingqiang Lin struct bounce_buffer bb; 99ad309a88SDingqiang Lin unsigned int bb_flags; 100ad309a88SDingqiang Lin 101ad309a88SDingqiang Lin if (cmd.b.rw == SFC_WRITE) 102ad309a88SDingqiang Lin bb_flags = GEN_BB_READ; 103ad309a88SDingqiang Lin else 104ad309a88SDingqiang Lin bb_flags = GEN_BB_WRITE; 105ad309a88SDingqiang Lin 10658463f4dSJon Lin ret = bounce_buffer_start(&bb, data, size, bb_flags); 107ad309a88SDingqiang Lin if (ret) 108ad309a88SDingqiang Lin return ret; 109ad309a88SDingqiang Lin 110ad309a88SDingqiang Lin writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR); 11158463f4dSJon Lin writel(~((u32)DMA_INT), g_sfc_reg + SFC_IMR); 1121bb49bc4SDingqiang Lin writel((unsigned long)bb.bounce_buffer, g_sfc_reg + SFC_DMA_ADDR); 113ad309a88SDingqiang Lin writel(SFC_DMA_START, g_sfc_reg + SFC_DMA_TRIGGER); 114ad309a88SDingqiang Lin 11558463f4dSJon Lin timeout = size * 10; 116*534d4d2fSJon Lin while ((readl(g_sfc_reg + SFC_SR) & SFC_BUSY) && 117ad309a88SDingqiang Lin (timeout-- > 0)) 118ad309a88SDingqiang Lin sfc_delay(1); 119ad309a88SDingqiang Lin writel(0xFFFFFFFF, g_sfc_reg + SFC_ICLR); 120ad309a88SDingqiang Lin if (timeout <= 0) 121ad309a88SDingqiang Lin ret = SFC_WAIT_TIMEOUT; 122ad309a88SDingqiang Lin bounce_buffer_stop(&bb); 123ad309a88SDingqiang Lin } else { 124ad309a88SDingqiang Lin u32 i, words, count, bytes; 125ad309a88SDingqiang Lin union SFCFSR_DATA fifostat; 126ad309a88SDingqiang Lin u32 *p_data = (u32 *)data; 127ad309a88SDingqiang Lin 128ad309a88SDingqiang Lin if (cmd.b.rw == SFC_WRITE) { 12958463f4dSJon Lin words = (size + 3) >> 2; 130ad309a88SDingqiang Lin while (words) { 131ad309a88SDingqiang Lin fifostat.d32 = readl(g_sfc_reg + SFC_FSR); 132ad309a88SDingqiang Lin if (fifostat.b.txlevel > 0) { 133ad309a88SDingqiang Lin count = words < fifostat.b.txlevel ? 134ad309a88SDingqiang Lin words : fifostat.b.txlevel; 135ad309a88SDingqiang Lin for (i = 0; i < count; i++) { 136ad309a88SDingqiang Lin writel(*p_data++, 137ad309a88SDingqiang Lin g_sfc_reg + SFC_DATA); 138ad309a88SDingqiang Lin words--; 139ad309a88SDingqiang Lin } 140ad309a88SDingqiang Lin if (words == 0) 141ad309a88SDingqiang Lin break; 142ad309a88SDingqiang Lin timeout = 0; 143ad309a88SDingqiang Lin } else { 144ad309a88SDingqiang Lin sfc_delay(1); 145ad309a88SDingqiang Lin if (timeout++ > 10000) { 146ad309a88SDingqiang Lin ret = SFC_TX_TIMEOUT; 147ad309a88SDingqiang Lin break; 148ad309a88SDingqiang Lin } 149ad309a88SDingqiang Lin } 150ad309a88SDingqiang Lin } 151ad309a88SDingqiang Lin } else { 152ad309a88SDingqiang Lin /* SFC_READ == cmd.b.rw */ 15358463f4dSJon Lin bytes = size & 0x3; 15458463f4dSJon Lin words = size >> 2; 155ad309a88SDingqiang Lin while (words) { 156ad309a88SDingqiang Lin fifostat.d32 = readl(g_sfc_reg + SFC_FSR); 157ad309a88SDingqiang Lin if (fifostat.b.rxlevel > 0) { 158ad309a88SDingqiang Lin u32 count; 159ad309a88SDingqiang Lin 160ad309a88SDingqiang Lin count = words < fifostat.b.rxlevel ? 161ad309a88SDingqiang Lin words : fifostat.b.rxlevel; 162ad309a88SDingqiang Lin 163ad309a88SDingqiang Lin for (i = 0; i < count; i++) { 164ad309a88SDingqiang Lin *p_data++ = readl(g_sfc_reg + 165ad309a88SDingqiang Lin SFC_DATA); 166ad309a88SDingqiang Lin words--; 167ad309a88SDingqiang Lin } 168ad309a88SDingqiang Lin if (words == 0) 169ad309a88SDingqiang Lin break; 170ad309a88SDingqiang Lin timeout = 0; 171ad309a88SDingqiang Lin } else { 172ad309a88SDingqiang Lin sfc_delay(1); 173ad309a88SDingqiang Lin if (timeout++ > 10000) { 174ad309a88SDingqiang Lin ret = SFC_RX_TIMEOUT; 175ad309a88SDingqiang Lin break; 176ad309a88SDingqiang Lin } 177ad309a88SDingqiang Lin } 178ad309a88SDingqiang Lin } 179ad309a88SDingqiang Lin 180ad309a88SDingqiang Lin timeout = 0; 181ad309a88SDingqiang Lin while (bytes) { 182ad309a88SDingqiang Lin fifostat.d32 = readl(g_sfc_reg + SFC_FSR); 183ad309a88SDingqiang Lin if (fifostat.b.rxlevel > 0) { 184ad309a88SDingqiang Lin u8 *p_data1 = (u8 *)p_data; 185ad309a88SDingqiang Lin 186ad309a88SDingqiang Lin words = readl(g_sfc_reg + SFC_DATA); 187ad309a88SDingqiang Lin for (i = 0; i < bytes; i++) 188ad309a88SDingqiang Lin p_data1[i] = 189ad309a88SDingqiang Lin (u8)((words >> (i * 8)) & 0xFF); 190ad309a88SDingqiang Lin break; 191ad309a88SDingqiang Lin } 192ad309a88SDingqiang Lin 193ad309a88SDingqiang Lin sfc_delay(1); 194ad309a88SDingqiang Lin if (timeout++ > 10000) { 195ad309a88SDingqiang Lin ret = SFC_RX_TIMEOUT; 196ad309a88SDingqiang Lin break; 197ad309a88SDingqiang Lin } 198ad309a88SDingqiang Lin } 199ad309a88SDingqiang Lin } 200ad309a88SDingqiang Lin } 201ad309a88SDingqiang Lin 202ad309a88SDingqiang Lin exit_wait: 203ad309a88SDingqiang Lin timeout = 0; /* wait cmd or data send complete */ 20458463f4dSJon Lin while (readl(g_sfc_reg + SFC_SR) & SFC_BUSY) { 205ad309a88SDingqiang Lin sfc_delay(1); 206ad309a88SDingqiang Lin if (timeout++ > 100000) { /* wait 100ms */ 207ad309a88SDingqiang Lin ret = SFC_TX_TIMEOUT; 208ad309a88SDingqiang Lin break; 209ad309a88SDingqiang Lin } 210ad309a88SDingqiang Lin } 211ad309a88SDingqiang Lin sfc_delay(1); /* CS# High Time (read/write) >100ns */ 212ad309a88SDingqiang Lin return ret; 213ad309a88SDingqiang Lin } 214