| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533 |
- /* 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 <platform/mt_spi_slave.h>
- #include <platform/mt_gpio.h>
- #include <malloc.h>
- #include <bits.h>
- #include <libfdt.h>
- /*
- * SPI command description.
- */
- #define CMD_PWOFF 0x02 /* Power Off */
- #define CMD_PWON 0x04 /* Power On */
- #define CMD_RS 0x06 /* Read Status */
- #define CMD_WS 0x08 /* Write Status */
- #define CMD_CR 0x0a /* Config Read */
- #define CMD_CW 0x0c /* Config Write */
- #define CMD_RD 0x81 /* Read Data */
- #define CMD_WD 0x0e /* Write Data */
- #define CMD_CT 0x10 /* Config Type */
- /*
- * SPI slave status register (to master).
- */
- #define SLV_ON BIT(0)
- #define SR_CFG_SUCCESS BIT(1)
- #define SR_TXRX_FIFO_RDY BIT(2)
- #define SR_RD_ERR BIT(3)
- #define SR_WR_ERR BIT(4)
- #define SR_RDWR_FINISH BIT(5)
- #define SR_TIMEOUT_ERR BIT(6)
- #define SR_CMD_ERR BIT(7)
- #define CONFIG_READY ((SR_CFG_SUCCESS | SR_TXRX_FIFO_RDY))
- /*
- * hardware limit for once transfter.
- */
- #define MTK_SPI_BUFSIZ 32
- #define MAX_SPI_XFER_SIZE_ONCE (64 * 1024 - 1)
- #define MAX_SPI_TRY_CNT (10)
- #define SPI_READ true
- #define SPI_WRITE false
- #define SPI_READ_STA_ERR_RET (1)
- /*
- * spi slave config
- */
- #define IOCFG_BASE_ADDR 0x00005000
- #define DRV_CFG0 (IOCFG_BASE_ADDR + 0x0)
- #define SPIS_SLVO_MASK (0x7 << 21)
- #define SPISLV_BASE_ADDR 0x00002000
- #define SPISLV_CTRL (SPISLV_BASE_ADDR + 0x0)
- #define EARLY_TRANS_MASK (0x1 << 16)
- static int spi_bus_num;
- /* specific SPI data */
- struct mtk_spi_slave_data {
- u32 tx_speed_hz;
- u32 rx_speed_hz;
- u32 addr;
- u32 len;
- void *buffer;
- u8 slave_drive_strength;
- u8 high_speed_tick_delay;
- u8 low_speed_tick_delay;
- u8 high_speed_early_trans;
- u8 low_speed_early_trans;
- bool is_read:1;
- };
- static u8 cmd_trans_type_4byte_single[2] = {CMD_CT, 0x04};
- static u8 tx_cmd_read_sta[2] = {CMD_RS, 0x00};
- static u8 rx_cmd_read_sta[2] = {0x00, 0x00};
- static struct spi_transfer CT_TRANSFER = {0};
- static struct spi_transfer RS_TRANSFER = {0};
- static struct mtk_spi_slave_data slv_data = {
- .tx_speed_hz = SPI_TX_LOW_SPEED_HZ,
- .rx_speed_hz = SPI_RX_LOW_SPEED_HZ,
- .addr = 0,
- .len = 0,
- .buffer = NULL,
- .is_read = 0,
- .slave_drive_strength = 0,
- .high_speed_tick_delay = 0,
- .low_speed_tick_delay = 0,
- .high_speed_early_trans = 0,
- .low_speed_early_trans = 0,
- };
- struct mtk_spi_bus_config spislv_chip_info = {
- .spi_mode = 0,
- .tick_delay = 0,
- };
- static int spislv_sync_sub(void)
- {
- int ret = 0, i = 0;
- struct spi_transfer x[2] = {0};
- void *local_buf = NULL;
- u8 mtk_spi_buffer[MTK_SPI_BUFSIZ];
- u8 cmd_write_sta[2] = {CMD_WS, 0xff};
- u8 status = 0;
- u32 retry = 0;
- u8 cmd_config[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
- /* CR or CW */
- if (slv_data.is_read)
- cmd_config[0] = CMD_CR;
- else
- cmd_config[0] = CMD_CW;
- for (i = 0; i < 4; i++) {
- cmd_config[1 + i] = (slv_data.addr & (0xff << (i * 8)))
- >> (i * 8);
- cmd_config[5 + i] = ((slv_data.len - 1) & (0xff << (i * 8)))
- >> (i * 8);
- }
- x[0].tx_buf = cmd_config;
- x[0].len = ARRAY_SIZE(cmd_config);
- x[0].speed_hz = slv_data.tx_speed_hz;
- x[0].cs_change = 1;
- x[0].tick_delay = spislv_chip_info.tick_delay;
- ret = spi_sync(spi_bus_num, x);
- if (ret)
- goto tail;
- /* RS */
- rx_cmd_read_sta[1] = 0;
- RS_TRANSFER.tick_delay = spislv_chip_info.tick_delay;
- ret = spi_sync(spi_bus_num, &RS_TRANSFER);
- if (ret)
- goto tail;
- status = rx_cmd_read_sta[1];
- if ((status & CONFIG_READY) != CONFIG_READY) {
- dprintf(CRITICAL, "SPI config %s status error: 0x%x, err addr: 0x%x\n",
- slv_data.is_read ? "read" : "write", status, slv_data.addr);
- ret = SPI_READ_STA_ERR_RET;
- goto tail;
- }
- /* RD or WD */
- if (slv_data.len > MTK_SPI_BUFSIZ - 1) {
- local_buf = malloc(slv_data.len + 1);
- if (!local_buf) {
- dprintf(CRITICAL, "[spislv]local buf malloc fail\n");
- goto tail;
- }
- } else {
- local_buf = mtk_spi_buffer;
- memset(local_buf, 0, MTK_SPI_BUFSIZ);
- }
- if (slv_data.is_read) {
- *((u8 *)local_buf) = CMD_RD;
- x[1].tx_buf = local_buf;
- x[1].rx_buf = local_buf;
- x[1].speed_hz = slv_data.rx_speed_hz;
- } else {
- *((u8 *)local_buf) = CMD_WD;
- memcpy((u8 *)local_buf + 1, slv_data.buffer, slv_data.len);
- x[1].tx_buf = local_buf;
- x[1].speed_hz = slv_data.tx_speed_hz;
- }
- x[1].len = slv_data.len + 1;
- x[1].cs_change = 1;
- x[1].tick_delay = spislv_chip_info.tick_delay;
- ret = spi_sync(spi_bus_num, x+1);
- if (ret)
- goto tail;
- /* RS */
- rx_cmd_read_sta[1] = 0;
- RS_TRANSFER.tick_delay = spislv_chip_info.tick_delay;
- ret = spi_sync(spi_bus_num, &RS_TRANSFER);
- if (ret)
- goto tail;
- status = rx_cmd_read_sta[1];
- /* ignore status for set early transfer bit */
- if (slv_data.addr == SPISLV_CTRL)
- status = 0x26;
- if (((status & SR_RD_ERR) == SR_RD_ERR) ||
- ((status & SR_WR_ERR) == SR_WR_ERR) ||
- ((status & SR_TIMEOUT_ERR) == SR_TIMEOUT_ERR)) {
- dprintf(CRITICAL, "SPI %s error: 0x%x, err addr: 0x%x\n",
- slv_data.is_read ? "read" : "write", status, slv_data.addr);
- /* WS */
- x[2].tx_buf = cmd_write_sta;
- x[2].len = ARRAY_SIZE(cmd_write_sta);
- x[2].speed_hz = slv_data.tx_speed_hz;
- x[2].cs_change = 1;
- x[2].tick_delay = spislv_chip_info.tick_delay;
- ret = spi_sync(spi_bus_num, x+2);
- if (ret)
- goto tail;
- ret = SPI_READ_STA_ERR_RET;
- } else {
- while (((status & SR_RDWR_FINISH) != SR_RDWR_FINISH)) {
- dprintf(CRITICAL, "SPI %s not finish: 0x%x, err addr: 0x%x, polling: %d\n",
- slv_data.is_read ? "read" : "write", status, slv_data.addr, retry);
- if (retry++ >= MAX_SPI_TRY_CNT) {
- ret = SPI_READ_STA_ERR_RET;
- goto tail;
- }
- mdelay(1);
- /* RS */
- rx_cmd_read_sta[1] = 0;
- RS_TRANSFER.tick_delay = spislv_chip_info.tick_delay;
- ret = spi_sync(spi_bus_num, &RS_TRANSFER);
- if (ret)
- goto tail;
- status = rx_cmd_read_sta[1];
- }
- }
- tail:
- /* Only for successful read */
- if (slv_data.is_read && !ret)
- memcpy(slv_data.buffer, ((u8 *)x[1].rx_buf + 1), slv_data.len);
- if (local_buf != mtk_spi_buffer)
- free(local_buf);
- return ret;
- }
- static int spislv_sync(void)
- {
- int ret = 0;
- int once_addr = MAX_SPI_XFER_SIZE_ONCE / 4;
- u32 len = 0;
- u32 try = 0;
- if (slv_data.len < MAX_SPI_XFER_SIZE_ONCE)
- goto transfer_drect;
- len = slv_data.len;
- while (len > MAX_SPI_XFER_SIZE_ONCE) {
- slv_data.len = MAX_SPI_XFER_SIZE_ONCE;
- ret = spislv_sync_sub();
- while (ret) {
- dprintf(CRITICAL, "spi slave error, addr: 0x%x, ret(%d), retry: %d\n",
- slv_data.addr, ret, try);
- if (try++ == MAX_SPI_TRY_CNT)
- goto tail;
- ret = spislv_sync_sub();
- }
- slv_data.addr = slv_data.addr + once_addr;
- slv_data.buffer = (u8 *)slv_data.buffer + MAX_SPI_XFER_SIZE_ONCE;
- len = len - MAX_SPI_XFER_SIZE_ONCE;
- }
- slv_data.len = len;
- transfer_drect:
- ret = spislv_sync_sub();
- while (ret) {
- dprintf(CRITICAL, "spi slave error, addr: 0x%x, ret(%d), retry: %d\n",
- slv_data.addr, ret, try);
- if (try++ == MAX_SPI_TRY_CNT)
- goto tail;
- ret = spislv_sync_sub();
- }
- tail:
- /* slv_data's transfer info will not be original if split. */
- return ret;
- }
- int spislv_init(void)
- {
- int ret = 0;
- spislv_chip_info.tick_delay = slv_data.low_speed_tick_delay;
- CT_TRANSFER.tick_delay = spislv_chip_info.tick_delay;
- ret = spi_sync(spi_bus_num, &CT_TRANSFER);
- ret = spislv_write_register_mask(SPISLV_CTRL,
- (slv_data.low_speed_early_trans << 16),
- EARLY_TRANS_MASK);
- if (slv_data.slave_drive_strength)
- ret = spislv_write_register_mask(DRV_CFG0, (0x7 << 21),
- SPIS_SLVO_MASK);
- return ret;
- }
- int spislv_switch_speed_hz(u32 tx_speed_hz, u32 rx_speed_hz)
- {
- int ret = 0;
- slv_data.tx_speed_hz =
- (tx_speed_hz > SPI_TX_MAX_SPEED_HZ ? SPI_TX_MAX_SPEED_HZ : tx_speed_hz);
- slv_data.rx_speed_hz =
- (rx_speed_hz > SPI_RX_MAX_SPEED_HZ ? SPI_RX_MAX_SPEED_HZ : rx_speed_hz);
- RS_TRANSFER.speed_hz = slv_data.rx_speed_hz;
- if (slv_data.rx_speed_hz == SPI_RX_MAX_SPEED_HZ) {
- spislv_chip_info.tick_delay = slv_data.high_speed_tick_delay;
- ret = spislv_write_register_mask(SPISLV_CTRL,
- (slv_data.high_speed_early_trans << 16),
- EARLY_TRANS_MASK);
- } else {
- spislv_chip_info.tick_delay = slv_data.low_speed_tick_delay;
- ret = spislv_write_register_mask(SPISLV_CTRL,
- (slv_data.low_speed_early_trans << 16),
- EARLY_TRANS_MASK);
- }
- return ret;
- }
- int spislv_write(u32 addr, void *val, int len)
- {
- slv_data.buffer = val;
- slv_data.addr = addr;
- slv_data.len = len;
- slv_data.is_read = 0;
- return spislv_sync();
- }
- int spislv_read(u32 addr, void *val, int len)
- {
- slv_data.buffer = val;
- slv_data.addr = addr;
- slv_data.len = len;
- slv_data.is_read = 1;
- return spislv_sync();
- }
- int spislv_read_register(u32 addr, u32 *val)
- {
- return spislv_read(addr, (u8 *)val, 4);
- }
- int spislv_write_register(u32 addr, u32 val)
- {
- return spislv_write(addr, (u8 *)&val, 4);
- }
- int spislv_set_register32(u32 addr, u32 val)
- {
- u32 ret = 0;
- u32 read_val;
- ret = spislv_read_register(addr, &read_val);
- if (ret)
- return ret;
- ret = spislv_write_register(addr, read_val | val);
- return ret;
- }
- int spislv_clr_register32(u32 addr, u32 val)
- {
- u32 ret = 0;
- u32 read_val;
- ret = spislv_read_register(addr, &read_val);
- if (ret)
- return ret;
- ret = spislv_write_register(addr, read_val & (~val));
- return ret;
- }
- int spislv_write_register_mask(u32 addr, u32 val, u32 msk)
- {
- u32 ret = 0;
- u32 read_val;
- ret = spislv_read_register(addr, &read_val);
- if (ret)
- return ret;
- ret = spislv_write_register(addr, ((read_val & (~(msk))) |
- ((val) & (msk))));
- return ret;
- }
- static void spislv_fdt_getprop_u32_array(int nodeoffset,
- const char *name, unsigned int *out_value)
- {
- u32 i;
- u32 *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;
- }
- static u32 init_spislv_from_dt(const char *compatible)
- {
- u32 node = 0;
- u32 *data = NULL;
- int len = 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) {
- dprintf(CRITICAL, "[spislv]fdt node err: %d\n", node);
- return node;
- }
- data = (unsigned char *)fdt_getprop(lk_drv_fdt, node,
- "slave-drive-strength", &len);
- slv_data.slave_drive_strength = *data;
- data = (unsigned char *)fdt_getprop(lk_drv_fdt, node,
- "high-speed-tick-delay", &len);
- slv_data.high_speed_tick_delay = *data;
- data = (unsigned char *)fdt_getprop(lk_drv_fdt, node,
- "low-speed-tick-delay", &len);
- slv_data.low_speed_tick_delay = *data;
- data = (unsigned char *)fdt_getprop(lk_drv_fdt, node,
- "high-speed-early-trans", &len);
- slv_data.high_speed_early_trans = *data;
- data = (unsigned char *)fdt_getprop(lk_drv_fdt, node,
- "low-speed-early-trans", &len);
- slv_data.low_speed_early_trans = *data;
- dprintf(CRITICAL, "[spislv]slave-drive-strength: %d\n",
- slv_data.slave_drive_strength);
- dprintf(CRITICAL, "[spislv]high-speed-tick-delay: %d\n",
- slv_data.high_speed_tick_delay);
- dprintf(CRITICAL, "[spislv]low-speed-tick-delay: %d\n",
- slv_data.low_speed_tick_delay);
- dprintf(CRITICAL, "[spislv]high-speed-early-trans: %d\n",
- slv_data.high_speed_early_trans);
- dprintf(CRITICAL, "[spislv]low-speed-early-trans: %d\n",
- slv_data.low_speed_early_trans);
- return 0;
- }
- static void init_gpio_from_dt(const char *path)
- {
- u32 sub_node;
- u32 pin_mux[4];
- u32 driving;
- void *lk_drv_fdt = get_lk_overlayed_dtb();
- int node = fdt_path_offset(lk_drv_fdt, path);
- if (node > 0) {
- sub_node = fdt_first_subnode(lk_drv_fdt, node);
- if (sub_node > 0) {
- spislv_fdt_getprop_u32_array(sub_node,
- "pinmux", pin_mux);
- spislv_fdt_getprop_u32_array(sub_node,
- "drive-strength", &driving);
- }
- }
- dprintf(CRITICAL, "[spislv]pin_mux[0]: %d\n", pin_mux[0] >> 8);
- dprintf(CRITICAL, "[spislv]pin_mux[1]: %d\n", pin_mux[1] >> 8);
- dprintf(CRITICAL, "[spislv]pin_mux[2]: %d\n", pin_mux[2] >> 8);
- dprintf(CRITICAL, "[spislv]pin_mux[3]: %d\n", pin_mux[3] >> 8);
- dprintf(CRITICAL, "[spislv]drive-strength: %d\n", driving);
- mt_set_gpio_driving(pin_mux[0] >> 8, driving);
- mt_set_gpio_driving(pin_mux[1] >> 8, driving);
- mt_set_gpio_driving(pin_mux[2] >> 8, driving);
- mt_set_gpio_driving(pin_mux[3] >> 8, driving);
- }
- void spi_slave_probe(void)
- {
- int bus_num;
- const char *spislv_compatible = "mediatek,spi_slave";
- CT_TRANSFER.tx_buf = cmd_trans_type_4byte_single;
- CT_TRANSFER.len = ARRAY_SIZE(cmd_trans_type_4byte_single);;
- CT_TRANSFER.cs_change = 1;
- CT_TRANSFER.speed_hz = slv_data.tx_speed_hz;
- RS_TRANSFER.tx_buf = tx_cmd_read_sta;
- RS_TRANSFER.rx_buf = rx_cmd_read_sta;
- RS_TRANSFER.len = ARRAY_SIZE(tx_cmd_read_sta);
- RS_TRANSFER.cs_change = 1;
- RS_TRANSFER.speed_hz = slv_data.rx_speed_hz;
- init_gpio_from_dt("/pinctrl/spislv_mode_default");
- init_spislv_from_dt(spislv_compatible);
- bus_num = init_spi_bus_from_dt(spislv_compatible, &spislv_chip_info);
- if (bus_num >= 0)
- spi_bus_num = bus_num;
- else
- dprintf(CRITICAL, "[spislv]init spi bus fail!\n");
- }
|