| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- /* Copyright Statement:
- *
- * This software/firmware and related documentation ("MediaTek Software") are
- * protected under relevant copyright laws. The information contained herein
- * is confidential and proprietary to MediaTek Inc. and/or its licensors.
- * Without the prior written permission of MediaTek inc. and/or its licensors,
- * any reproduction, modification, use or disclosure of MediaTek Software,
- * and information contained herein, in whole or in part, shall be strictly prohibited.
- */
- /* MediaTek Inc. (C) 2021. All rights reserved.
- *
- * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
- * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
- * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
- * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
- * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
- * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
- * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
- * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
- * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
- * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
- * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
- * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
- * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
- * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
- * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
- * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
- */
- #include <platform/mt_spi.h>
- #include <malloc.h>
- #include <libfdt.h>
- static struct mtk_spi_bus *spi_bus[BUS_COUNT] = { NULL };
- static int spi_bus_num = -1;
- static const struct mtk_spi_bus_config spi_default_config = {
- .spi_mode = 0,
- .tick_delay = 0,
- };
- static void mtk_spi_dump_register(int bus_num)
- {
- struct mtk_spi_regs *regs = spi_bus[bus_num]->reg_addr;
- dprintf(SPEW, "spi_cfg0_reg:0x%x\n", DRV_Reg32(®s->spi_cfg0_reg));
- dprintf(SPEW, "spi_cfg1_reg:0x%x\n", DRV_Reg32(®s->spi_cfg1_reg));
- dprintf(SPEW, "spi_tx_src_reg:0x%x\n",
- DRV_Reg32(®s->spi_tx_src_reg));
- dprintf(SPEW, "spi_rx_dst_reg:0x%x\n",
- DRV_Reg32(®s->spi_rx_dst_reg));
- dprintf(SPEW, "spi_cmd_reg:0x%x\n", DRV_Reg32(®s->spi_cmd_reg));
- dprintf(SPEW, "spi_sta1_reg:0x%x\n", DRV_Reg32(®s->spi_status1_reg));
- }
- static void mtk_spi_reset(int bus_num)
- {
- int reg_val;
- struct mtk_spi_regs *regs = spi_bus[bus_num]->reg_addr;
- reg_val = DRV_Reg32(®s->spi_cmd_reg);
- reg_val |= 1 << SPI_CMD_RST_SHIFT;
- mt_reg_sync_writel(reg_val, ®s->spi_cmd_reg);
- reg_val = DRV_Reg32(®s->spi_cmd_reg);
- reg_val &= ~(1 << SPI_CMD_RST_SHIFT);
- mt_reg_sync_writel(reg_val, ®s->spi_cmd_reg);
- spi_bus[bus_num]->state = MTK_SPI_IDLE;
- }
- static void mtk_spi_set_cs(int bus_num, bool enable)
- {
- u32 reg_val;
- struct mtk_spi_regs *regs = spi_bus[bus_num]->reg_addr;
- reg_val = DRV_Reg32(®s->spi_cmd_reg);
- if (!enable) {
- reg_val |= 1 << SPI_CMD_PAUSE_EN_SHIFT;
- mt_reg_sync_writel(reg_val, ®s->spi_cmd_reg);
- spi_bus[bus_num]->state = MTK_SPI_PAUSE_IDLE;
- } else {
- reg_val &= ~(1 << SPI_CMD_PAUSE_EN_SHIFT);
- mt_reg_sync_writel(reg_val, ®s->spi_cmd_reg);
- mtk_spi_reset(bus_num);
- spi_bus[bus_num]->state = MTK_SPI_IDLE;
- }
- }
- static void mtk_spi_enable_transfer(int bus_num)
- {
- int reg_val;
- struct mtk_spi_regs *regs = spi_bus[bus_num]->reg_addr;
- if (spi_bus[bus_num]->state == MTK_SPI_IDLE) {
- reg_val = DRV_Reg32(®s->spi_cmd_reg);
- reg_val |= 1 << SPI_CMD_ACT_SHIFT;
- mt_reg_sync_writel(reg_val, ®s->spi_cmd_reg);
- } else if (spi_bus[bus_num]->state == MTK_SPI_PAUSE_IDLE) {
- reg_val = DRV_Reg32(®s->spi_cmd_reg);
- reg_val |= 1 << SPI_CMD_RESUME_SHIFT;
- mt_reg_sync_writel(reg_val, ®s->spi_cmd_reg);
- }
- }
- static void mtk_spi_packet(int bus_num, int size)
- {
- int reg_val, packet_len, packet_loop;
- struct mtk_spi_regs *regs = spi_bus[bus_num]->reg_addr;
- packet_len = size < MTK_PACKET_SIZE ? size : MTK_PACKET_SIZE;
- packet_loop = MTK_SPI_ROUNDUP_DIV(size, packet_len);
- reg_val = DRV_Reg32(®s->spi_cfg1_reg);
- reg_val &= ~(SPI_CFG1_PACKET_LENGTH_MASK | SPI_CFG1_PACKET_LOOP_MASK);
- reg_val |= ((packet_len - 1) << SPI_CFG1_PACKET_LENGTH_SHIFT) |
- ((packet_loop - 1) << SPI_CFG1_PACKET_LOOP_SHIFT);
- mt_reg_sync_writel(reg_val, ®s->spi_cfg1_reg);
- }
- static int mtk_spi_polling(int bus_num)
- {
- int i;
- struct mtk_spi_regs *regs = spi_bus[bus_num]->reg_addr;
- /*
- *spi sw should wait for status1 register to idle before polling
- * status0 register for rx/tx finish.
- */
- i = 0;
- while ((DRV_Reg32(®s->spi_status1_reg) &
- MTK_SPI_BUSY_STATUS) == 0) {
- i++;
- udelay(1);
- if (i > MTK_TXRX_TIMEOUT_US) {
- dprintf(CRITICAL, "Timeout for spi status1 reg.\n");
- goto error;
- }
- }
- i = 0;
- while ((DRV_Reg32(®s->spi_status0_reg) &
- MTK_SPI_PAUSE_FINISH_INT_STATUS) == 0) {
- i++;
- udelay(1);
- if (i > MTK_TXRX_TIMEOUT_US) {
- dprintf(CRITICAL, "Timeout for spi status0 reg.\n");
- goto error;
- }
- }
- return 0;
- error:
- return -1;
- }
- static void spi_prepare_transfer(int bus_num, struct spi_transfer *transfer)
- {
- uint32_t div, sck_ticks, cs_ticks, reg_val;
- struct mtk_spi_regs *regs = spi_bus[bus_num]->reg_addr;
- u32 speed_hz = transfer->speed_hz;
- if (speed_hz < (SPI_HZ / 2))
- div = MTK_SPI_ROUNDUP_DIV(SPI_HZ, speed_hz);
- else
- div = 1;
- sck_ticks = MTK_SPI_ROUNDUP_DIV(div, 2);
- cs_ticks = sck_ticks * 2;
- /* set the timing */
- mt_reg_sync_writel(((cs_ticks - 1) << SPI_CFG0_CS_HOLD_OFFSET) |
- ((cs_ticks - 1) << SPI_CFG0_CS_SETUP_OFFSET),
- ®s->spi_cfg0_reg);
- mt_reg_sync_writel(((sck_ticks - 1) << SPI_CFG2_SCK_HIGH_OFFSET) |
- ((sck_ticks - 1) << SPI_CFG2_SCK_LOW_OFFSET),
- ®s->spi_cfg2_reg);
- reg_val = DRV_Reg32(®s->spi_cfg1_reg);
- reg_val &= ~SPI_CFG1_CS_IDLE_MASK;
- reg_val |= (cs_ticks - 1) << SPI_CFG1_CS_IDLE_SHIFT;
- mt_reg_sync_writel(reg_val, ®s->spi_cfg1_reg);
- /* tick delay */
- reg_val = DRV_Reg32(®s->spi_cfg1_reg);
- reg_val |= (transfer->tick_delay << SPI_CFG1_GET_TICK_DLY_SHIFT);
- mt_reg_sync_writel(reg_val, ®s->spi_cfg1_reg);
- }
- static int mtk_spi_fifo_transfer(int bus_num, unsigned char *rx_buf,
- unsigned char *tx_buf, int size)
- {
- int i, reg_val = 0, word_count, ret, remaind;
- struct mtk_spi_regs *regs = spi_bus[bus_num]->reg_addr;
- if (!size || size > MTK_FIFO_DEPTH)
- return -1;
- mtk_spi_packet(bus_num, size);
- remaind = size & 0x03;
- if (rx_buf && !tx_buf) {
- word_count = size >> 2;
- if (remaind)
- word_count++;
- for (i = 0; i < word_count; i++)
- mt_reg_sync_writel(MTK_ARBITRARY_VALUE,
- ®s->spi_tx_data_reg);
- }
- if (tx_buf) {
- reg_val = 0;
- for (i = 0; i < size - remaind; i++) {
- reg_val |= *(tx_buf + i) << ((i & 0x03) << 3);
- if ((i & 0x03) == 3) {
- mt_reg_sync_writel(reg_val,
- ®s->spi_tx_data_reg);
- reg_val = 0;
- }
- }
- if (remaind) {
- reg_val = 0;
- for (i = 0; i < remaind; i++) {
- reg_val |= *(tx_buf + size - remaind + i)
- << ((i & 0x03) << 3);
- }
- mt_reg_sync_writel(reg_val, ®s->spi_tx_data_reg);
- }
- }
- mtk_spi_enable_transfer(bus_num);
- ret = mtk_spi_polling(bus_num);
- if (!ret && rx_buf) {
- for (i = 0; i < size; i++) {
- if ((i & 0x03) == 0)
- reg_val = DRV_Reg32(®s->spi_rx_data_reg);
- *(rx_buf + i) = (reg_val >> ((i & 0x03) << 3)) & 0xff;
- }
- }
- return ret;
- }
- static int mtk_spi_dma_transfer(int bus_num, unsigned char *rx_buf,
- unsigned char *tx_buf, int size)
- {
- int i, reg_val;
- struct mtk_spi_regs *regs = spi_bus[bus_num]->reg_addr;
- if (!rx_buf && tx_buf) {
- reg_val = DRV_Reg32(®s->spi_cmd_reg);
- reg_val |= 1 << SPI_CMD_TX_DMA_SHIFT;
- mt_reg_sync_writel(reg_val, ®s->spi_cmd_reg);
- } else {
- reg_val = DRV_Reg32(®s->spi_cmd_reg);
- reg_val |= (1 << SPI_CMD_RX_DMA_SHIFT) |
- (1 << SPI_CMD_TX_DMA_SHIFT);
- mt_reg_sync_writel(reg_val, ®s->spi_cmd_reg);
- }
- if (tx_buf) {
- arch_clean_cache_range((addr_t)tx_buf, size);
- mt_reg_sync_writel(tx_buf, ®s->spi_tx_src_reg);
- }
- if (rx_buf) {
- arch_clean_cache_range((addr_t)rx_buf, size);
- mt_reg_sync_writel(rx_buf, ®s->spi_rx_dst_reg);
- }
- mtk_spi_packet(bus_num, size);
- mtk_spi_enable_transfer(bus_num);
- if (mtk_spi_polling(bus_num))
- goto error;
- reg_val = DRV_Reg32(®s->spi_cmd_reg);
- reg_val &= ~(1 << SPI_CMD_RX_DMA_SHIFT | 1 << SPI_CMD_TX_DMA_SHIFT);
- mt_reg_sync_writel(reg_val, ®s->spi_cmd_reg);
- if (rx_buf)
- arch_clean_invalidate_cache_range((addr_t)rx_buf, size);
- return 0;
- error:
- return -1;
- }
- int spi_sync(int bus_num, struct spi_transfer *transfer)
- {
- int ret, min_size;
- u8 *tx_buf = (u8 *)transfer->tx_buf;
- u8 *rx_buf = (u8 *)transfer->rx_buf;
- u32 size = transfer->len;
- spi_prepare_transfer(bus_num, transfer);
- while (size) {
- min_size = size < MTK_PACKET_SIZE ? size : MTK_PACKET_SIZE;
- if (size > MTK_FIFO_DEPTH)
- ret = mtk_spi_dma_transfer(bus_num, rx_buf, tx_buf,
- min_size);
- else
- ret = mtk_spi_fifo_transfer(bus_num, rx_buf, tx_buf,
- min_size);
- if (ret) {
- mtk_spi_reset(bus_num);
- return ret;
- }
- size -= min_size;
- if (rx_buf)
- rx_buf += min_size;
- if (tx_buf)
- tx_buf += min_size;
- }
- if (transfer->cs_change)
- mtk_spi_set_cs(bus_num, 1);
- else
- mtk_spi_set_cs(bus_num, 0);
- return ret;
- }
- static void spi_loopback_test(int bus_num)
- {
- u32 index, status, err_count = 0;
- struct spi_transfer transfer;
- u32 length = 400;
- dprintf(CRITICAL, "spi_loopback_test entry\n");
- transfer.tx_buf = (void *)malloc(length);
- transfer.rx_buf = (void *)malloc(length);
- transfer.speed_hz = 10000;
- transfer.cs_change = 1;
- transfer.len = 30;
- for (index = 0; index < transfer.len; index++) {
- ((u8 *)transfer.tx_buf)[index] = index % 255;
- ((u8 *)transfer.rx_buf)[index] = 0;
- }
- status = spi_sync(bus_num, &transfer);
- if (status)
- dprintf(CRITICAL, "spi transfer err: %d\n", status);
- while (transfer.len--) {
- if (((u8 *)transfer.tx_buf)[transfer.len] !=
- ((u8 *)transfer.rx_buf)[transfer.len]) {
- dprintf(CRITICAL, "spi pio data compare err: tx: %d rx: %d\n",
- ((u8 *)transfer.tx_buf)[transfer.len],
- ((u8 *)transfer.rx_buf)[transfer.len]);
- err_count++;
- }
- }
- dprintf(CRITICAL, "----SPI PIO MODE test done, err count is %d----\n",
- err_count);
- err_count = 0;
- transfer.len = length;
- for (index = 0; index < transfer.len; index++) {
- ((u8 *)transfer.tx_buf)[index] = index % 255;
- ((u8 *)transfer.rx_buf)[index] = 0;
- }
- status = spi_sync(bus_num, &transfer);
- if (status)
- dprintf(CRITICAL, "spi transfer err: %d\n", status);
- while (transfer.len--) {
- if (((u8 *)transfer.tx_buf)[transfer.len] !=
- ((u8 *)transfer.rx_buf)[transfer.len]) {
- dprintf(CRITICAL, "spi dma data compare err: tx: %d rx: %d\n",
- ((u8 *)transfer.tx_buf)[transfer.len],
- ((u8 *)transfer.rx_buf)[transfer.len]);
- err_count++;
- }
- }
- dprintf(CRITICAL, "----SPI DMA MODE test done, err count is %d----\n",
- err_count);
- free((void *)transfer.tx_buf);
- free((void *)transfer.rx_buf);
- dprintf(CRITICAL, "spi_loopback_test done\n");
- }
- static void spi_fdt_getprop_u32_array(int nodeoffset,
- const char *name, unsigned int *out_value)
- {
- unsigned int i;
- unsigned int *data = NULL;
- int len = 0;
- void *lk_drv_fdt = get_lk_overlayed_dtb();
- if (lk_drv_fdt == NULL)
- panic("lk driver fdt is NULL!\n");
- data = (unsigned int *)fdt_getprop(lk_drv_fdt, nodeoffset, name, &len);
- if (len > 0) {
- len = len / sizeof(unsigned int);
- for (i = 0; i < len; i++)
- *(out_value+i) = fdt32_to_cpu(*(data+i));
- } else
- *out_value = 0;
- }
- void mtk_spi_init(int bus_num, struct mtk_spi_bus_config *spi_config)
- {
- uint32_t reg_val;
- uint16_t cpha, cpol;
- struct mtk_spi_bus_config *spi_bus_config = spi_config;
- struct mtk_spi_regs *regs = spi_bus[bus_num]->reg_addr;
- dprintf(SPEW, "mtk_spi_init entry\n");
- if (spi_bus_config == NULL)
- spi_bus_config = &spi_default_config;
- cpha = spi_bus_config->spi_mode & SPI_CPHA ? 1:0;
- cpol = spi_bus_config->spi_mode & SPI_CPOL ? 1:0;
- reg_val = DRV_Reg32(®s->spi_cmd_reg);
- /* set spi mode */
- if (cpha)
- reg_val |= SPI_CMD_CPHA_EN;
- else
- reg_val &= ~SPI_CMD_CPHA_EN;
- if (cpol)
- reg_val |= SPI_CMD_CPOL_EN;
- else
- reg_val &= ~SPI_CMD_CPOL_EN;
- /* set the mlsbx and mlsbtx */
- reg_val &= ~SPI_CMD_TXMSBF_EN;
- reg_val &= ~SPI_CMD_RXMSBF_EN;
- /* set the tx/rx endian */
- reg_val &= ~SPI_CMD_TX_ENDIAN_EN;
- reg_val &= ~SPI_CMD_RX_ENDIAN_EN;
- /* clear pause mode */
- reg_val &= ~SPI_CMD_PAUSE_EN;
- /* set finish interrupt always disable */
- reg_val &= ~SPI_CMD_FINISH_IE_EN;
- /* set pause interrupt always disable */
- reg_val &= ~SPI_CMD_PAUSE_IE_EN;
- /* disable dma mode */
- reg_val &= ~(SPI_CMD_TX_DMA_EN | SPI_CMD_RX_DMA_EN);
- /* set deassert mode */
- reg_val &= ~SPI_CMD_DEASSERT_EN;
- mt_reg_sync_writel(reg_val, ®s->spi_cmd_reg);
- /* pad select */
- reg_val = spi_bus[bus_num]->pad_select;
- mt_reg_sync_writel(reg_val, ®s->spi_pad_sel_reg);
- /* tick delay */
- reg_val = DRV_Reg32(®s->spi_cfg1_reg);
- reg_val |= (spi_bus_config->tick_delay << SPI_CFG1_GET_TICK_DLY_SHIFT);
- mt_reg_sync_writel(reg_val, ®s->spi_cfg1_reg);
- /* cs pull high && reset spi */
- mtk_spi_set_cs(bus_num, 1);
- #ifdef SPI_EARLY_PORTING
- reg_val = 7;
- mt_reg_sync_writel(reg_val, ®s->spi_pad_sel_reg);
- spi_loopback_test(bus_num);
- reg_val = spi_bus[bus_num]->pad_select;
- mt_reg_sync_writel(reg_val, ®s->spi_pad_sel_reg);
- #endif
- mtk_spi_reset(bus_num);
- }
- int init_spi_bus_from_dt(const char *compatible,
- struct mtk_spi_bus_config *config)
- {
- uint32_t node, parent_node = 0;
- uint32_t temp_data[4];
- uint32_t pad_select = 0;
- struct mtk_spi_bus *bus = NULL;
- void *lk_drv_fdt = get_lk_overlayed_dtb();
- if (lk_drv_fdt == NULL)
- panic("lk driver fdt is NULL!\n");
- node = fdt_node_offset_by_compatible(lk_drv_fdt, -1, compatible);
- if (node > 0) {
- parent_node = fdt_parent_offset(lk_drv_fdt, node);
- if (parent_node > 0) {
- bus = malloc(sizeof(struct mtk_spi_bus));
- if (bus) {
- spi_bus_num++;
- spi_bus[spi_bus_num] = bus;
- }
- spi_fdt_getprop_u32_array(parent_node,
- "reg", temp_data);
- spi_fdt_getprop_u32_array(parent_node,
- "mediatek,pad-select", &pad_select);
- spi_bus[spi_bus_num]->reg_addr = temp_data[1];
- spi_bus[spi_bus_num]->pad_select = pad_select;
- spi_bus[spi_bus_num]->spi_bus_fdt_offset = parent_node;
- dprintf(SPEW, "reg addr : %x, pad_select : %d\n",
- spi_bus[spi_bus_num]->reg_addr,
- spi_bus[spi_bus_num]->pad_select);
- } else {
- dprintf(SPEW, "get spi slave parent node failed!\n");
- return -1;
- }
- } else {
- dprintf(SPEW, "get spi slave node failed!\n");
- return -1;
- }
- mtk_spi_init(spi_bus_num, config);
- return spi_bus_num;
- }
|