/* * Driver for Samsung SPEEDY(Serial Protocol in an EffEctive Digital waY) * * Copyright (C) 2015 Samsung Electronics Ltd. * Youngmin Nam * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../pinctrl/core.h" /* SPEEDY Register MAP */ #define SPEEDY_CTRL 0x000 #define SPEEDY_FIFO_CTRL 0x004 #define SPEEDY_CMD 0x008 #define SPEEDY_INT_ENABLE 0x00C #define SPEEDY_INT_STATUS 0x010 #define SPEEDY_FIFO_STATUS 0x030 #define SPEEDY_TX_DATA 0x034 #define SPEEDY_RX_DATA 0x038 #define SPEEDY_PACKET_GAP_TIME 0x044 #define SPEEDY_TIMEOUT_COUNT 0x048 #define SPEEDY_FIFO_DEBUG 0x100 #define SPEEDY_CTRL_STATUS 0x104 /* SPEEDY_CTRL Register bits */ #define SPEEDY_ENABLE (1 << 0) #define SPEEDY_TIMEOUT_CMD_DISABLE (1 << 1) #define SPEEDY_TIMEOUT_STANDBY_DISABLE (1 << 2) #define SPEEDY_TIMEOUT_DATA_DISABLE (1 << 3) #define SPEEDY_ALWAYS_PULLUP_EN (1 << 7) #define SPEEDY_DATA_WIDTH_8BIT (0 << 8) #define SPEEDY_REMOTE_RESET_REQ (1 << 30) #define SPEEDY_SW_RST (1 << 31) /* SPEEDY_FIFO_CTRL Register bits */ #define SPEEDY_RX_TRIGGER_LEVEL(x) ((x) << 0) #define SPEEDY_TX_TRIGGER_LEVEL(x) ((x) << 8) #define SPEEDY_FIFO_DEBUG_INDEX (0 << 24) // TODO : modify define #define SPEEDY_FIFO_RESET (1 << 31) /* SPEEDY_CMD Register bits */ #define SPEEDY_BURST_LENGTH(x) ((x) << 0) #define SPEEDY_BURST_FIXED (0 << 5) #define SPEEDY_BURST_INCR (1 << 5) #define SPEEDY_BURST_EXTENSION (2 << 5) #define SPEEDY_ADDRESS(x) ((x & 0xFFF) << 7) #define SPEEDY_ACCESS_BURST (0 << 19) #define SPEEDY_ACCESS_RANDOM (1 << 19) #define SPEEDY_DIRECTION_READ (0 << 20) #define SPEEDY_DIRECTION_WRITE (1 << 20) /* SPEEDY_INT_ENABLE Register bits */ #define SPEEDY_TRANSFER_DONE_EN (1 << 0) #define SPEEDY_TIMEOUT_CMD_EN (1 << 1) #define SPEEDY_TIMEOUT_STANDBY_EN (1 << 2) #define SPEEDY_TIMEOUT_DATA_EN (1 << 3) #define SPEEDY_FIFO_TX_ALMOST_EMPTY_EN (1 << 4) #define SPEEDY_FIFO_RX_ALMOST_FULL_EN (1 << 8) #define SPEEDY_RX_FIFO_INT_TRAILER_EN (1 << 9) #define SPEEDY_RX_MODEBIT_ERR_EN (1 << 16) #define SPEEDY_RX_GLITCH_ERR_EN (1 << 17) #define SPEEDY_RX_ENDBIT_ERR_EN (1 << 18) #define SPEEDY_TX_LINE_BUSY_ERR_EN (1 << 20) #define SPEEDY_TX_STOPBIT_ERR_EN (1 << 21) #define SPEEDY_REMOTE_RESET_REQ_EN (1 << 31) /* SPEEDY_INT_STATUS Register bits */ #define SPEEDY_TRANSFER_DONE (1 << 0) #define SPEEDY_TIMEOUT_CMD (1 << 1) #define SPEEDY_TIMEOUT_STANDBY (1 << 2) #define SPEEDY_TIMEOUT_DATA (1 << 3) #define SPEEDY_FIFO_TX_ALMOST_EMPTY (1 << 4) #define SPEEDY_FIFO_RX_ALMOST_FULL (1 << 8) #define SPEEDY_RX_FIFO_INT_TRAILER (1 << 9) #define SPEEDY_RX_MODEBIT_ERR (1 << 16) #define SPEEDY_RX_GLITCH_ERR (1 << 17) #define SPEEDY_RX_ENDBIT_ERR (1 << 18) #define SPEEDY_TX_LINE_BUSY_ERR (1 << 20) #define SPEEDY_TX_STOPBIT_ERR (1 << 21) #define SPEEDY_REMOTE_RESET_REQ_STAT (1 << 31) /* SPEEDY_FIFO_STATUS Register bits */ #define SPEEDY_VALID_DATA_CNT (0 << 0) // TODO : modify define #define SPEEDY_FIFO_FULL (1 << 5) #define SPEEDY_FIFO_EMPTY (1 << 6) /* SPEEDY_PACKET_GAP_TIME Register bits */ #define SPEEDY_PULL_EN_CNT (0xF << 0) // TODO : modify define #define SPEEDY_PACKET_GAP_TIME_CNT (0 << 16) // TODO : modify define /* SPEEDY_CTRL_STATUS Register bits */ #define SPEEDY_FSM_IDLE (1 << 0) #define SPEEDY_FSM_INIT (1 << 1) #define SPEEDY_FSM_TX_CMD (1 << 2) #define SPEEDY_FSM_STANDBY (1 << 3) #define SPEEDY_FSM_DATA (1 << 4) #define SPEEDY_FSM_TIMEOUT (1 << 5) #define SPEEDY_FSM_TRANS_DONE (1 << 6) #define SPEEDY_FSM_IO_RX_STAT_MASK (3 << 7) #define SPEEDY_FSM_IO_TX_IDLE (1 << 9) #define SPEEDY_FSM_IO_TX_GET_PACKET (1 << 10) #define SPEEDY_FSM_IO_TX_PACKET (1 << 11) #define SPEEDY_FSM_IO_TX_DONE (1 << 12) /* IP_BATCHER Register MAP */ #define IPBATCHER_CON 0x0500 #define IPBATCHER_STATE 0x0504 #define IPBATCHER_INT_EN 0x0508 #define IPBATCHER_FSM_UNEXPEN 0x050C #define IPBATCHER_FSM_TXEN 0x0510 #define IPBATCHER_FSM_RXFIFO 0x0514 #define IPBATCHER_FSM_CON 0x0518 #define IP_FIFO_STATUS 0x051C #define IP_INT_STATUS 0x0520 #define IP_INTR_UNEXP_STATE 0x0524 #define IP_INTR_TX_STATE 0x0528 #define IP_INTR_RX_STATE 0x052C #define BATCHER_OPCODE 0x0600 #define BATCHER_START_PAYLOAD 0x1000 #define BATCHER_END_PAYLOAD 0x1060 #define IPBATCHER_SEMA_REL 0x0200 /* IPBATCHER_CON Register bits */ #define BATCHER_ENABLE (1 << 0) #define DEDICATED_BATCHER_APB (1 << 1) #define START_BATCHER (1 << 4) #define APB_RESP_CPU (1 << 5) #define IP_SW_RST (1 << 6) #define MP_APBSEMA_SW_RST (1 << 7) #define MP_APBSEMA_DISABLE (1 << 8) #define SW_RESET (1 << 31) /* IPBATCHER_STATE Register bits */ #define BATCHER_OPERATION_COMPLETE (1 << 0) #define UNEXPECTED_IP_INTR (1 << 1) #define BATCHER_FSM_STATE_IDLE (1 << 3) #define BATCHER_FSM_STATE_INIT (1 << 4) #define BATCHER_FSM_STATE_GET_SEMAPHORE (1 << 5) #define BATCHER_FSM_STATE_CONFIG (1 << 6) #define BATCHER_FSM_STATE_WAIT_INT (1 << 7) #define BATCHER_FSM_STATE_SW_RESET_IP (1 << 8) #define BATCHER_FSM_STATE_INTR_ROUTINE (1 << 9) #define BATCHER_FSM_STATE_WRITE_TX_DATA (1 << 10) #define BATCHER_FSM_STATE_READ_RX_DATA (1 << 11) #define BATCHER_FSM_STATE_STOP_I2C (1 << 12) #define BATCHER_FSM_STATE_CLEAN_INTR_STAT (1 << 13) #define BATCHER_FSM_STATE_REL_SEMAPHORE (1 << 14) #define BATCHER_FSM_STATE_GEN_INT (1 << 15) #define BATCHER_FSM_STATE_UNEXPECTED_INT (1 << 16) #define MP_APBSEMA_CH_LOCK_STATUS (1 << 20) #define MP_APBSEMA_DISABLE_STATUS (1 << 21) #define MP_APBSEMA_SW_RST_STATUS (1 << 22) /* IPBATCHER_INT_EN Register bits */ #define BATCHER_INTERRUPT_ENABLE (1 << 0) /* IPBATCHER_FSM_CON Register bits */ #define DISABLE_STOP_CMD (1 << 0) #define DISABLE_SEMAPHORE_RELEASE (1 << 1) #define EXYNOS_SPEEDY_TIMEOUT (msecs_to_jiffies(500)) #define BATCHER_INIT_CMD 0xFFFFFFFF #define ACCESS_BURST 0 #define ACCESS_RANDOM 1 #define DIRECTION_READ 0 #define DIRECTION_WRITE 1 #define SRP_COUNT 3 #define EMULATOR struct exynos_speedy { struct list_head node; struct i2c_adapter adap; struct i2c_msg *msg; unsigned int msg_ptr; unsigned int msg_len; unsigned int irq; void __iomem *regs; struct clk *clk; struct device *dev; unsigned int cmd_buffer; unsigned int cmd_index; unsigned int cmd_pointer; unsigned int desc_pointer; unsigned int batcher_read_addr; int always_intr_high; unsigned int int_en; }; static void dump_speedy_register(struct exynos_speedy *speedy) { dev_err(speedy->dev, "SPEEDY Register dump\n" " CTRL 0x%08x\n" " FIFO_CTRL 0x%08x\n" " CMD 0x%08x\n" " INT_ENABLE 0x%08x\n" " INT_STATUS 0x%08x\n" " FIFO_STATUS 0x%08x\n" " PACKET_GAP_TIME 0x%08x\n" " TIMEOUT_COUNT 0x%08x\n" " CTRL_STATUS 0x%08x\n" , readl(speedy->regs + SPEEDY_CTRL) , readl(speedy->regs + SPEEDY_FIFO_CTRL) , readl(speedy->regs + SPEEDY_CMD) , readl(speedy->regs + SPEEDY_INT_ENABLE) , readl(speedy->regs + SPEEDY_INT_STATUS) , readl(speedy->regs + SPEEDY_FIFO_STATUS) , readl(speedy->regs + SPEEDY_PACKET_GAP_TIME) , readl(speedy->regs + SPEEDY_TIMEOUT_COUNT) , readl(speedy->regs + SPEEDY_CTRL_STATUS) ); } static void dump_batcher_register(struct exynos_speedy *speedy) { int i = 0; char buf_opcode[SZ_256]; char buf_payload[SZ_1K]; u32 len = 0; dev_err(speedy->dev, "Batcher Register dump\n" " CON 0x%08x\n" " State 0x%08x\n" " INT_EN 0x%08x\n" " FSM_UNEXPEN 0x%08x\n" " FSM_TXEN 0x%08x\n" " FSM_RXFIFO 0x%08x\n" " FSM_CON 0x%08x\n" " FIFO_Status 0x%08x\n" " INT_Status 0x%08x\n" " INTR_UNEXP_state 0x%08x\n" " INTR_TX_state 0x%08x\n" " INTR_RX_state 0x%08x\n" , readl(speedy->regs + IPBATCHER_CON) , readl(speedy->regs + IPBATCHER_STATE) , readl(speedy->regs + IPBATCHER_INT_EN) , readl(speedy->regs + IPBATCHER_FSM_UNEXPEN) , readl(speedy->regs + IPBATCHER_FSM_TXEN) , readl(speedy->regs + IPBATCHER_FSM_RXFIFO) , readl(speedy->regs + IPBATCHER_FSM_CON) , readl(speedy->regs + IP_FIFO_STATUS) , readl(speedy->regs + IP_INT_STATUS) , readl(speedy->regs + IP_INTR_UNEXP_STATE) , readl(speedy->regs + IP_INTR_TX_STATE) , readl(speedy->regs + IP_INTR_RX_STATE) ); len += snprintf(buf_opcode + len, sizeof(buf_opcode) - len, "Batcher OPCODE dump\n"); for (i = 0; i < 7; i++) { len += snprintf(buf_opcode + len, sizeof(buf_opcode) - len, "OPCODE %d = 0x%08x\n", i, readl(speedy->regs + (BATCHER_OPCODE + (i * 4)))); } dev_err(speedy->dev, "%s", buf_opcode); len = 0; len += snprintf(buf_payload + len, sizeof(buf_payload) - len, "Batcher PAYLOAD dump\n"); for (i = 0; i < 25; i++) { len += snprintf(buf_payload + len, sizeof(buf_payload) - len, "PAYLOAD %02d = 0x%08x ", i, readl(speedy->regs + (BATCHER_START_PAYLOAD + (i * 4)))); if (i % 5 == 4) len += snprintf(buf_payload + len, sizeof(buf_payload) - len, "\n"); } dev_err(speedy->dev, "%s", buf_payload); } static void write_batcher(struct exynos_speedy *speedy, unsigned int description, unsigned int opcode) { if((BATCHER_START_PAYLOAD + (speedy->desc_pointer * 4)) <= BATCHER_END_PAYLOAD) { /* clear cmd_buffer */ speedy->cmd_buffer &= ~(0xFF << (8 * speedy->cmd_index)); /* write opcode to cmd_buffer */ speedy->cmd_buffer |= (opcode << (8 * speedy->cmd_index)); /* write opcode to OPCODE_TABLE register */ writel(speedy->cmd_buffer, speedy->regs + BATCHER_OPCODE + (speedy->cmd_pointer * 4)); /* write payload to PAYLOAD_FIELD register */ writel(description, speedy->regs + BATCHER_START_PAYLOAD + (speedy->desc_pointer * 4)); /* increase cmd_index for next opcode */ speedy->cmd_index++; /* increase desc_pointer for next payload */ speedy->desc_pointer++; } else { /* Error handling for opcode overflow */ dev_err(speedy->dev, "fail to write speedy batcher\n"); } /* If cmd_index is 4, we need to update cmd_index, cmd_pointer */ if(speedy->cmd_index == 4) { /* initialize cmd_index to use OPCODE_TABLE from start point */ speedy->cmd_index = 0; /* increase OPCODE_TABLE offset to use next OPCODE_TABLE register */ speedy->cmd_pointer++; /* innitialize cmd_buffer */ speedy->cmd_buffer = BATCHER_INIT_CMD; } } static void set_batcher_enable(struct exynos_speedy *speedy) { u32 ip_batcher_con = readl(speedy->regs + IPBATCHER_CON); ip_batcher_con |= BATCHER_ENABLE; ip_batcher_con |= DEDICATED_BATCHER_APB; writel(ip_batcher_con, speedy->regs + IPBATCHER_CON); } static void start_batcher(struct exynos_speedy *speedy) { u32 ip_batcher_con = readl(speedy->regs + IPBATCHER_CON); ip_batcher_con |= START_BATCHER; writel(ip_batcher_con, speedy->regs + IPBATCHER_CON); } static void mp_apbsema_sw_rst(struct exynos_speedy *speedy) { u32 ip_batcher_con = readl(speedy->regs + IPBATCHER_CON); ip_batcher_con |= MP_APBSEMA_SW_RST; writel(ip_batcher_con, speedy->regs + IPBATCHER_CON); ip_batcher_con &= (~MP_APBSEMA_SW_RST); writel(ip_batcher_con, speedy->regs + IPBATCHER_CON); } static void set_batcher_interrupt(struct exynos_speedy *speedy, int enable) { u32 ip_batcher_int_en = readl(speedy->regs + IPBATCHER_INT_EN); if (enable) ip_batcher_int_en |= BATCHER_INTERRUPT_ENABLE; else ip_batcher_int_en &= (~BATCHER_INTERRUPT_ENABLE); writel(ip_batcher_int_en, speedy->regs + IPBATCHER_INT_EN); } static void set_batcher_idle(struct exynos_speedy *speedy) { u32 ip_batcher_con = 0; writel(ip_batcher_con, speedy->regs + IPBATCHER_CON); } static void batcher_swreset(struct exynos_speedy *speedy) { u32 ip_batcher_con = readl(speedy->regs + IPBATCHER_CON); ip_batcher_con |= SW_RESET; writel(ip_batcher_con, speedy->regs + IPBATCHER_CON); ip_batcher_con &= (~SW_RESET); writel(ip_batcher_con, speedy->regs + IPBATCHER_CON); dev_err(speedy->dev, "batcher s/w reset was done\n"); } static void program_batcher_fsm(struct exynos_speedy *speedy) { u32 ip_batcher_fsm_unexpec_enable = 0; u32 ip_batcher_fsm_tx_enable = 0; u32 ip_batcher_fsm_rx_fifo = 0; u32 ip_batcher_fsm_con = 0; /* select unexpected interrupt of IP */ /* "1" in each bit will be handled as unexpected interrupt */ ip_batcher_fsm_unexpec_enable = (SPEEDY_TIMEOUT_CMD_EN | SPEEDY_TIMEOUT_STANDBY_EN | SPEEDY_TIMEOUT_DATA_EN | SPEEDY_RX_MODEBIT_ERR_EN | SPEEDY_RX_GLITCH_ERR_EN | SPEEDY_RX_ENDBIT_ERR_EN | SPEEDY_TX_LINE_BUSY_ERR_EN | SPEEDY_TX_STOPBIT_ERR_EN); writel(ip_batcher_fsm_unexpec_enable, speedy->regs + IPBATCHER_FSM_UNEXPEN); /* select Tx, Rx normal interrupt of IP */ /* "1" in each bit will be handled as Tx normal interrupt */ /* "0" in each bit will be handled as Rx normal interrupt */ ip_batcher_fsm_tx_enable = (SPEEDY_TRANSFER_DONE_EN | SPEEDY_FIFO_TX_ALMOST_EMPTY_EN); writel(ip_batcher_fsm_tx_enable, speedy->regs + IPBATCHER_FSM_TXEN); /* select Rx FIFO empty status check bit */ /* "1" in each bit will monitor IP's RXFIFO empty status */ ip_batcher_fsm_rx_fifo = SPEEDY_FIFO_EMPTY; writel(ip_batcher_fsm_rx_fifo, speedy->regs + IPBATCHER_FSM_RXFIFO); ip_batcher_fsm_con = DISABLE_STOP_CMD; writel(ip_batcher_fsm_con, speedy->regs + IPBATCHER_FSM_CON); } static void release_semaphore(struct exynos_speedy *speedy) { writel(0x01, speedy->regs + IPBATCHER_SEMA_REL); } static void speedy_swreset_directly(struct exynos_speedy *speedy) { u32 speedy_ctl = readl(speedy->regs + SPEEDY_CTRL); speedy_ctl |= SPEEDY_SW_RST; writel(speedy_ctl, speedy->regs + SPEEDY_CTRL); /* delay for speedy sw_rst */ udelay(10); dev_err(speedy->dev, "speedy swreset directly was done\n"); } static void speedy_swreset_with_batcher(struct exynos_speedy *speedy) { u32 ip_batcher_state = readl(speedy->regs + IPBATCHER_STATE); u32 ip_batcher_con; unsigned long timeout; if (ip_batcher_state & MP_APBSEMA_CH_LOCK_STATUS) { dev_err(speedy->dev, "speedy reset is started with semaphore\n"); if (ip_batcher_state & BATCHER_FSM_STATE_WAIT_INT) { ip_batcher_con = readl(speedy->regs + IPBATCHER_CON); ip_batcher_con |= IP_SW_RST; writel(ip_batcher_con, speedy->regs + IPBATCHER_CON); /* delay for speedy sw_rst */ udelay(10); dev_err(speedy->dev, "speedy swreset through batcher was done\n"); } else { /* SPEEDY SW reset directly */ speedy_swreset_directly(speedy); } udelay(100); ip_batcher_state = readl(speedy->regs + IPBATCHER_STATE); if (!(ip_batcher_state & BATCHER_FSM_STATE_INIT)) { mp_apbsema_sw_rst(speedy); udelay(100); batcher_swreset(speedy); program_batcher_fsm(speedy); timeout = jiffies + EXYNOS_SPEEDY_TIMEOUT; /* Wait IDLE or INIT state of IPBATCHER */ while (time_before(jiffies, timeout)) { ip_batcher_state = readl(speedy->regs + IPBATCHER_STATE); if ((ip_batcher_state & BATCHER_FSM_STATE_IDLE) || (ip_batcher_state & BATCHER_FSM_STATE_INIT)) { timeout = 0; break; } else udelay(10); } if (timeout) dev_err(speedy->dev, "Timeout for waiting IDLE or INIT \n"); } release_semaphore(speedy); } else { dev_err(speedy->dev, "speedy reset can't be done by no semaphore\n"); batcher_swreset(speedy); program_batcher_fsm(speedy); } timeout = jiffies + EXYNOS_SPEEDY_TIMEOUT; /* Check IDLE or INIT state of IPBATCHER */ while (time_before(jiffies, timeout)) { ip_batcher_state = readl(speedy->regs + IPBATCHER_STATE); if ((ip_batcher_state & BATCHER_FSM_STATE_IDLE) || (ip_batcher_state & BATCHER_FSM_STATE_INIT)) { timeout = 0; break; } else udelay(10); } if (timeout) dev_err(speedy->dev, "Timeout for waiting IDLE or INIT \n"); dev_err(speedy->dev, "speedy recovery is done\n"); } static void speedy_set_cmd(struct exynos_speedy *speedy, int direction, u16 address, int random, int burst_length) { u32 speedy_fifo_ctl = 0; u32 speedy_int_en = 0; u32 speedy_command = 0; speedy_fifo_ctl |= SPEEDY_FIFO_RESET; speedy_command |= SPEEDY_ADDRESS(address); switch (random) { case ACCESS_BURST: speedy_command |= (SPEEDY_ACCESS_BURST | SPEEDY_BURST_INCR | SPEEDY_BURST_LENGTH(burst_length-1)); /* To prevent batcher timeout, interrupt state should be set as high */ /* So, FIFO trigger level shoud be set to trigger interrupt always */ if (speedy->always_intr_high) { if (direction == DIRECTION_READ) { speedy_fifo_ctl |= ( SPEEDY_RX_TRIGGER_LEVEL(burst_length) | SPEEDY_TX_TRIGGER_LEVEL(16) ); } else { speedy_fifo_ctl |= ( SPEEDY_RX_TRIGGER_LEVEL(0) | SPEEDY_TX_TRIGGER_LEVEL(1) ); } } else { speedy_fifo_ctl |= ( SPEEDY_RX_TRIGGER_LEVEL(burst_length) | SPEEDY_TX_TRIGGER_LEVEL(1) ); } break; case ACCESS_RANDOM: speedy_command |= SPEEDY_ACCESS_RANDOM; speedy_fifo_ctl |= (SPEEDY_RX_TRIGGER_LEVEL(1) | SPEEDY_TX_TRIGGER_LEVEL(1)); break; } /* make opcode and payload to configure SPEEDY_FIFO_CTRL */ write_batcher(speedy, speedy_fifo_ctl, SPEEDY_FIFO_CTRL); speedy_int_en |= (SPEEDY_TIMEOUT_CMD_EN | SPEEDY_TIMEOUT_STANDBY_EN | SPEEDY_TIMEOUT_DATA_EN); switch (direction) { case DIRECTION_READ: speedy_command |= SPEEDY_DIRECTION_READ; speedy_int_en |= (SPEEDY_FIFO_RX_ALMOST_FULL_EN | SPEEDY_RX_FIFO_INT_TRAILER_EN | SPEEDY_RX_MODEBIT_ERR_EN | SPEEDY_RX_GLITCH_ERR_EN | SPEEDY_RX_ENDBIT_ERR_EN); /* To prevent batcher timeout, interrupt state should be set as high */ if (speedy->always_intr_high) { speedy_int_en |= SPEEDY_FIFO_TX_ALMOST_EMPTY_EN; } break; case DIRECTION_WRITE: speedy_command |= SPEEDY_DIRECTION_WRITE; speedy_int_en |= (SPEEDY_TRANSFER_DONE_EN | SPEEDY_FIFO_TX_ALMOST_EMPTY_EN | SPEEDY_TX_LINE_BUSY_ERR_EN | SPEEDY_TX_STOPBIT_ERR_EN); /* To prevent batcher timeout, interrupt state should be set as high */ if (speedy->always_intr_high) { speedy_int_en |= SPEEDY_FIFO_RX_ALMOST_FULL_EN; } break; } /* store speedy_interrupt_enable status for re-configuration later */ speedy->int_en = speedy_int_en; /* clear speedy interrupt status */ write_batcher(speedy, 0xFFFFFFFF, SPEEDY_INT_STATUS); /* make opcode and payload to configure SPEEDY_INT_ENABLE */ write_batcher(speedy, speedy_int_en, SPEEDY_INT_ENABLE); /* make opcode and payload to configure SPEEDY_CMD */ write_batcher(speedy, speedy_command, SPEEDY_CMD); } static int speedy_batcher_wait_complete(struct exynos_speedy *speedy) { u32 ip_batcher_state; u32 ip_batcher_int_status; int ret = -EBUSY; unsigned long timeout; timeout = jiffies + EXYNOS_SPEEDY_TIMEOUT; while (time_before(jiffies, timeout)) { ip_batcher_state = readl(speedy->regs + IPBATCHER_STATE); ip_batcher_int_status = readl(speedy->regs + IP_INT_STATUS); if (ip_batcher_state & BATCHER_OPERATION_COMPLETE) { if ((ip_batcher_int_status & SPEEDY_TIMEOUT_CMD) | (ip_batcher_int_status & SPEEDY_TIMEOUT_STANDBY) | (ip_batcher_int_status & SPEEDY_TIMEOUT_DATA) | (ip_batcher_int_status & SPEEDY_RX_MODEBIT_ERR) | (ip_batcher_int_status & SPEEDY_RX_GLITCH_ERR) | (ip_batcher_int_status & SPEEDY_RX_ENDBIT_ERR) | (ip_batcher_int_status & SPEEDY_TX_LINE_BUSY_ERR) | (ip_batcher_int_status & SPEEDY_TX_STOPBIT_ERR)) { ret = -EIO; break; } else { ret = 0; break; } } else if (ip_batcher_state & UNEXPECTED_IP_INTR) { if ((ip_batcher_int_status & SPEEDY_TIMEOUT_CMD) | (ip_batcher_int_status & SPEEDY_TIMEOUT_STANDBY) | (ip_batcher_int_status & SPEEDY_TIMEOUT_DATA) | (ip_batcher_int_status & SPEEDY_RX_MODEBIT_ERR) | (ip_batcher_int_status & SPEEDY_RX_GLITCH_ERR) | (ip_batcher_int_status & SPEEDY_RX_ENDBIT_ERR) | (ip_batcher_int_status & SPEEDY_TX_LINE_BUSY_ERR) | (ip_batcher_int_status & SPEEDY_TX_STOPBIT_ERR)) { ret = -EIO; break; } } else { udelay(10); } } if (ret != 0) { if (ret == -EIO) dev_err(speedy->dev, "speedy timeout or error is occurred "); else dev_err(speedy->dev, "speedy batcher operation timeout is occurred "); } if (ip_batcher_state & BATCHER_OPERATION_COMPLETE) { /* clear batcher operation complete */ ip_batcher_state |= BATCHER_OPERATION_COMPLETE; writel(ip_batcher_state, speedy->regs + IPBATCHER_STATE); } return ret; } static void finalize_batcher(struct exynos_speedy *speedy) { write_batcher(speedy, 0x00, 0xFF); /* Initialize variables related opcode and payload of batcher */ speedy->cmd_buffer = BATCHER_INIT_CMD; speedy->cmd_index = 0; speedy->cmd_pointer = 0; speedy->desc_pointer = 0; } static void speedy_set_srp(struct exynos_speedy *speedy) { int ret; int i; u32 speedy_ctl; for (i = 0; i < SRP_COUNT; i++) { speedy_ctl = 0x30051; /* set batcher IDLE state */ set_batcher_idle(speedy); /* set batcher IDLE->INIT state */ set_batcher_enable(speedy); speedy_set_cmd(speedy, DIRECTION_WRITE, 0x0, ACCESS_RANDOM, 0); speedy_ctl |= SPEEDY_REMOTE_RESET_REQ; write_batcher(speedy, speedy_ctl, SPEEDY_CTRL); write_batcher(speedy, 0x00, SPEEDY_TX_DATA); speedy_ctl &= (~SPEEDY_REMOTE_RESET_REQ); write_batcher(speedy, speedy_ctl, SPEEDY_CTRL); finalize_batcher(speedy); /* TODO : for polling mode, need to enable batcher interrupt ? */ set_batcher_interrupt(speedy, 1); start_batcher(speedy); ret = speedy_batcher_wait_complete(speedy); /* TODO : for polling mode, need to enable batcher interrupt ? */ set_batcher_interrupt(speedy, 0); set_batcher_idle(speedy); if (!ret) { dev_err(speedy->dev, "SRP was done successfully\n"); break; } else { dev_err(speedy->dev, "SRP timeout was occured\n"); dump_speedy_register(speedy); dump_batcher_register(speedy); speedy_swreset_with_batcher(speedy); } } } static int exynos_speedy_xfer_batcher(struct exynos_speedy *speedy, struct i2c_msg *msgs) { int i = 0; int ret; /* speedy read / write direction */ int direction; /* speedy random(single) / burst access way */ int random; unsigned char byte; unsigned int speedy_int_en; /* initialize as reset value of SPEEDY_CTRL */ u32 speedy_ctl = 0x30050; speedy->msg = msgs; speedy->msg_ptr = 0; speedy->cmd_buffer = BATCHER_INIT_CMD; speedy->cmd_index = 0; speedy->cmd_pointer = 0; speedy->desc_pointer = 0; /* set batcher IDLE state */ set_batcher_idle(speedy); /* set batcher IDLE->INIT state */ set_batcher_enable(speedy); /* enable speedy master */ speedy_ctl |= SPEEDY_ENABLE; write_batcher(speedy, speedy_ctl, SPEEDY_CTRL); if (speedy->msg->flags & I2C_M_RD) direction = DIRECTION_READ; else direction = DIRECTION_WRITE; if (speedy->msg->len > 1) random = ACCESS_BURST; else random = ACCESS_RANDOM; speedy_set_cmd(speedy, direction, speedy->msg->addr, random, speedy->msg->len); if (direction == DIRECTION_READ) { speedy->batcher_read_addr = BATCHER_START_PAYLOAD + ((speedy->desc_pointer) * 4); for (i = 0; i < speedy->msg->len; i++) write_batcher(speedy, 0x77, SPEEDY_RX_DATA); } else { /* direction == DIRECTION_WRITE */ for (i = 0; i < speedy->msg->len; i++) { byte = speedy->msg->buf[i]; write_batcher(speedy, byte, SPEEDY_TX_DATA); } /* * To prevent interrupt pending by FIFO_TX_ALMOST_EMPTY * We should disable FIFO_TX_ALMOST_EMPTY_EN after Tx */ speedy_int_en = speedy->int_en & (~SPEEDY_FIFO_TX_ALMOST_EMPTY_EN); write_batcher(speedy, speedy_int_en, SPEEDY_INT_ENABLE); } finalize_batcher(speedy); /* TODO : for polling mode, need to enable batcher interrupt ? */ set_batcher_interrupt(speedy, 1); start_batcher(speedy); ret = speedy_batcher_wait_complete(speedy); /* TODO : for polling mode, need to enable batcher interrupt ? */ set_batcher_interrupt(speedy, 0); if (!ret) { if (direction == DIRECTION_READ) { for (i = 0; i < speedy->msg->len; i++) { byte = (unsigned char)readl(speedy->regs + speedy->batcher_read_addr + (i * 4)); speedy->msg->buf[i] = byte; } } set_batcher_idle(speedy); } else { set_batcher_idle(speedy); if (direction == DIRECTION_READ) dev_err(speedy->dev, "at Read\n"); else dev_err(speedy->dev, "at Write\n"); dump_speedy_register(speedy); dump_batcher_register(speedy); speedy_swreset_with_batcher(speedy); speedy_set_srp(speedy); udelay(1); ret = -EAGAIN; } return ret; } static irqreturn_t exynos_speedy_irq_batcher(int irqno, void *dev_id) { /* TODO : implementation is needed more */ /* In ISR, we will only handle error situation */ struct exynos_speedy *speedy = dev_id; u32 ip_batcher_state; u32 ip_batcher_int_status; ip_batcher_state = readl(speedy->regs + IPBATCHER_STATE); ip_batcher_int_status = readl(speedy->regs + IP_INT_STATUS); if (ip_batcher_int_status & SPEEDY_REMOTE_RESET_REQ_STAT) { dev_err(speedy->dev, "remote_reset_req is occured\n"); } if (ip_batcher_state & UNEXPECTED_IP_INTR) { dev_err(speedy->dev, "unexpected interrupt is occured\n"); if (ip_batcher_int_status & SPEEDY_TIMEOUT_CMD) dev_err(speedy->dev, "timout_cmd is occured\n"); if (ip_batcher_int_status & SPEEDY_TIMEOUT_STANDBY) dev_err(speedy->dev, "timeout_standby is occured\n"); if (ip_batcher_int_status & SPEEDY_TIMEOUT_DATA) dev_err(speedy->dev, "timeout_data is occured\n"); if (ip_batcher_int_status & SPEEDY_RX_MODEBIT_ERR) dev_err(speedy->dev, "rx_modebit_err is occured\n"); if (ip_batcher_int_status & SPEEDY_RX_GLITCH_ERR) dev_err(speedy->dev, "rx_glitch_err is occured\n"); if (ip_batcher_int_status & SPEEDY_RX_ENDBIT_ERR) dev_err(speedy->dev, "rx_endbit_err interrupt is occured\n"); if (ip_batcher_int_status & SPEEDY_TX_LINE_BUSY_ERR) dev_err(speedy->dev, "tx_line_busy_err interrupt is occured\n"); if (ip_batcher_int_status & SPEEDY_TX_STOPBIT_ERR) dev_err(speedy->dev, "tx_stopbit_err interrupt is occured\n"); } if (ip_batcher_state & BATCHER_OPERATION_COMPLETE) { dev_err(speedy->dev, "batcher operation is completed\n"); /* clear batcher operation complete */ ip_batcher_state |= BATCHER_OPERATION_COMPLETE; writel(BATCHER_OPERATION_COMPLETE, speedy->regs + IPBATCHER_STATE); } return IRQ_HANDLED; } static int exynos_speedy_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct exynos_speedy *speedy = (struct exynos_speedy *)adap->algo_data; struct i2c_msg *msgs_ptr = msgs; int retry, i = 0; int ret = 0; for (retry = 0; retry < adap->retries; retry++) { for (i = 0; i < num; i++) { ret = exynos_speedy_xfer_batcher(speedy, msgs_ptr); msgs_ptr++; if (ret == -EAGAIN) { msgs_ptr = msgs; break; } else if (ret < 0) { goto out; } } if ((i == num) && (ret != -EAGAIN)) break; dev_err(speedy->dev, "retrying transfer (%d)\n", retry); udelay(100); } if (i == num) { ret = num; } else { ret = -EREMOTEIO; dev_err(speedy->dev, "xfer message failed\n"); } out: return ret; } static u32 exynos_speedy_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); } static const struct i2c_algorithm exynos_speedy_algorithm = { .master_xfer = exynos_speedy_xfer, .functionality = exynos_speedy_func, }; static int exynos_speedy_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct exynos_speedy *speedy; struct resource *mem; void __iomem *pmu_batcher; int ret; dev_info(&pdev->dev, "speedy driver probe started\n"); if (!np) { dev_err(&pdev->dev, "no device node\n"); return -ENOENT; } speedy = devm_kzalloc(&pdev->dev, sizeof(struct exynos_speedy), GFP_KERNEL); if (!speedy) { dev_err(&pdev->dev, "no memory for driver data\n"); return -ENOMEM; } if (of_get_property(np, "samsung,always-interrupt-high", NULL)) speedy->always_intr_high = 1; else speedy->always_intr_high = 0; strlcpy(speedy->adap.name, "exynos-speedy", sizeof(speedy->adap.name)); speedy->adap.owner = THIS_MODULE; speedy->adap.algo = &exynos_speedy_algorithm; speedy->adap.retries = 2; speedy->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; speedy->dev = &pdev->dev; speedy->clk = devm_clk_get(&pdev->dev, "gate_speedy"); if (IS_ERR(speedy->clk)) { dev_err(&pdev->dev, "cannot get clock\n"); return -ENOENT; } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); speedy->regs = devm_ioremap_resource(&pdev->dev, mem); if (speedy->regs == NULL) { dev_err(&pdev->dev, "cannot map speedy SFR register\n"); ret = PTR_ERR(speedy->regs); goto err_probe; } /* TODO : need handling PMU register for batcher ? */ /* for enable Batcher in PMU */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (mem != NULL) { pmu_batcher = devm_ioremap_resource(&pdev->dev, mem); if (pmu_batcher == NULL) { dev_err(&pdev->dev, "cannot map PMU register for batcher enable\n"); ret = PTR_ERR(pmu_batcher); goto err_probe; } /* TODO : need handling PMU register for batcher ? */ writel(0x3, pmu_batcher); devm_iounmap(&pdev->dev, pmu_batcher); } speedy->adap.dev.of_node = np; speedy->adap.algo_data = speedy; speedy->adap.dev.parent = &pdev->dev; speedy->irq = irq_of_parse_and_map(np, 0); if(speedy->irq <= 0) { dev_err(&pdev->dev, "cannot find speedy IRQ\n"); ret = -EINVAL; goto err_probe; } /* clear speedy interrupt status */ writel(0xFFFFFFFF, speedy->regs + SPEEDY_INT_STATUS); /* reset speedy ctrl SFR. It may be used by bootloader */ speedy_swreset_directly(speedy); /* Do we need to register ISR for batcher polling mode? */ ret = devm_request_irq(&pdev->dev, speedy->irq, exynos_speedy_irq_batcher, 0, dev_name(&pdev->dev), speedy); disable_irq(speedy->irq); if (ret != 0) { dev_err(&pdev->dev, "cannot request speedy IRQ %d\n", speedy->irq); goto err_probe; } platform_set_drvdata(pdev, speedy); /* release semaphore after direct SPEEDY SFR access */ release_semaphore(speedy); /* reset batcher */ batcher_swreset(speedy); /* select bitfield to monitor interrupt and status by batcher */ program_batcher_fsm(speedy); speedy->adap.nr = -1; ret = i2c_add_numbered_adapter(&speedy->adap); if (ret < 0) { dev_err(&pdev->dev, "failed to add bus to i2c core\n"); goto err_probe; } dev_info(&pdev->dev, "speedy driver probe was succeeded\n"); return 0; err_probe: dev_err(&pdev->dev, "speedy driver probe failed\n"); return ret; } static const struct of_device_id exynos_speedy_match[] = { { .compatible = "samsung,exynos-speedy" }, {} }; MODULE_DEVICE_TABLE(of, exynos_speedy_match); static struct platform_driver exynos_speedy_driver = { .probe = exynos_speedy_probe, .driver = { .name = "exynos-speedy", .owner = THIS_MODULE, .of_match_table = exynos_speedy_match, }, }; static int __init exynos_speedy_init(void) { return platform_driver_register(&exynos_speedy_driver); } subsys_initcall(exynos_speedy_init); static void __exit exynos_speedy_exit(void) { platform_driver_unregister(&exynos_speedy_driver); } module_exit(exynos_speedy_exit); MODULE_DESCRIPTION("Exynos SPEEDY driver"); MODULE_AUTHOR("Youngmin Nam, "); MODULE_LICENSE("GPL v2");