| /* |
| * Copyright (C) 2009 Broadcom Corporation |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/ioport.h> |
| #include <linux/slab.h> |
| #include <linux/types.h> |
| #include <linux/sysfs.h> |
| #include <linux/device.h> |
| #include <linux/interrupt.h> |
| #include <linux/spi/spi.h> |
| #include <linux/platform_device.h> |
| #include <linux/io.h> |
| #include <linux/delay.h> |
| |
| #include <linux/brcmstb/brcmstb.h> |
| |
| #if 0 |
| #define DBG printk |
| #else |
| #define DBG(...) /* */ |
| #endif |
| |
| #if CONFIG_BRCM_BSPI_MAJOR_VERS >= 4 |
| #define BSPI_HAS_4BYTE_ADDRESS |
| #define BSPI_HAS_FLEX_MODE |
| #endif |
| |
| #define BSPI_LR_TIMEOUT (1000) |
| |
| #define BSPI_LR_INTERRUPTS_DATA \ |
| (BCHP_HIF_SPI_INTR2_CPU_SET_SPI_LR_SESSION_DONE_MASK | \ |
| BCHP_HIF_SPI_INTR2_CPU_SET_SPI_LR_FULLNESS_REACHED_MASK) |
| |
| #define BSPI_LR_INTERRUPTS_ERROR \ |
| (BCHP_HIF_SPI_INTR2_CPU_SET_SPI_LR_OVERREAD_MASK | \ |
| BCHP_HIF_SPI_INTR2_CPU_SET_SPI_LR_IMPATIENT_MASK | \ |
| BCHP_HIF_SPI_INTR2_CPU_SET_SPI_LR_SESSION_ABORTED_MASK) |
| |
| #define BSPI_LR_INTERRUPTS_ALL \ |
| (BSPI_LR_INTERRUPTS_ERROR | \ |
| BSPI_LR_INTERRUPTS_DATA) |
| |
| #define NUM_CHIPSELECT 4 |
| #define MSPI_BASE_FREQ 27000000UL |
| #define SPBR_MIN 8U |
| #define SPBR_MAX 255U |
| #define MAX_SPEED_HZ (MSPI_BASE_FREQ / (SPBR_MIN * 2)) |
| #define BSPI_BASE_ADDR KSEG1ADDR(0x1f000000) |
| |
| #define OPCODE_RDID 0x9f |
| #define OPCODE_WREN 0x06 |
| #define OPCODE_WRR 0x01 |
| #define OPCODE_RCR 0x35 |
| #define OPCODE_READ 0x03 |
| #define OPCODE_RDSR 0x05 |
| #define OPCODE_WRSR 0x01 |
| #define OPCODE_FAST_READ 0x0B |
| #define OPCODE_DOR 0x3B |
| #define OPCODE_QOR 0x6B |
| #define OPCODE_FAST_READ_4B 0x0C |
| #define OPCODE_DOR_4B 0x3C |
| #define OPCODE_QOR_4B 0x6C |
| |
| #define OPCODE_DIOR 0xBB |
| #define OPCODE_QIOR 0xEB |
| #define OPCODE_DIOR_4B 0xBC |
| #define OPCODE_QIOR_4B 0xEC |
| |
| #define OPCODE_EN4B 0xB7 |
| #define OPCODE_EX4B 0xE9 |
| #define OPCODE_BRWR 0x17 |
| |
| #define BSPI_WIDTH_1BIT 1 |
| #define BSPI_WIDTH_2BIT 2 |
| #define BSPI_WIDTH_4BIT 4 |
| |
| #define BSPI_ADDRLEN_3BYTES 3 |
| #define BSPI_ADDRLEN_4BYTES 4 |
| |
| #define BSPI_FLASH_TYPE_SPANSION 0 |
| #define BSPI_FLASH_TYPE_MACRONIX 1 |
| #define BSPI_FLASH_TYPE_NUMONYX 2 |
| #define BSPI_FLASH_TYPE_SST 3 |
| #define BSPI_FLASH_TYPE_UNKNOWN -1 |
| |
| static int nobspi; |
| module_param(nobspi, int, 0444); |
| |
| static int bspi_width = BSPI_WIDTH_1BIT; |
| module_param(bspi_width, int, 0444); |
| |
| static int bspi_addrlen = BSPI_ADDRLEN_3BYTES; |
| module_param(bspi_addrlen, int, 0444); |
| |
| static int bspi_hp; |
| module_param(bspi_hp, int, 0444); |
| |
| static int bspi_flash = BSPI_FLASH_TYPE_UNKNOWN; |
| module_param(bspi_flash, int, 0444); |
| |
| struct bcmspi_parms { |
| u32 speed_hz; |
| u8 chip_select; |
| u8 mode; |
| u8 bits_per_word; |
| }; |
| |
| const struct bcmspi_parms bcmspi_default_parms_cs0 = { |
| .speed_hz = MAX_SPEED_HZ, |
| .chip_select = 0, |
| .mode = SPI_MODE_3, |
| .bits_per_word = 8, |
| }; |
| EXPORT_SYMBOL(bcmspi_default_parms_cs0); |
| |
| const struct bcmspi_parms bcmspi_default_parms_cs1 = { |
| .speed_hz = MAX_SPEED_HZ, |
| .chip_select = 1, |
| .mode = SPI_MODE_3, |
| .bits_per_word = 8, |
| }; |
| EXPORT_SYMBOL(bcmspi_default_parms_cs1); |
| |
| struct position { |
| struct spi_message *msg; |
| struct spi_transfer *trans; |
| int byte; |
| }; |
| |
| #define NUM_TXRAM 32 |
| #define NUM_RXRAM 32 |
| #define NUM_CDRAM 16 |
| |
| struct bcm_mspi_hw { |
| u32 spcr0_lsb; /* 0x000 */ |
| u32 spcr0_msb; /* 0x004 */ |
| u32 spcr1_lsb; /* 0x008 */ |
| u32 spcr1_msb; /* 0x00c */ |
| u32 newqp; /* 0x010 */ |
| u32 endqp; /* 0x014 */ |
| u32 spcr2; /* 0x018 */ |
| u32 reserved0; /* 0x01c */ |
| u32 mspi_status; /* 0x020 */ |
| u32 cptqp; /* 0x024 */ |
| u32 reserved1[6]; /* 0x028 */ |
| u32 txram[NUM_TXRAM]; /* 0x040 */ |
| u32 rxram[NUM_RXRAM]; /* 0x0c0 */ |
| u32 cdram[NUM_CDRAM]; /* 0x140 */ |
| }; |
| |
| struct bcm_bspi_hw { |
| u32 revision_id; /* 0x000 */ |
| u32 scratch; /* 0x004 */ |
| u32 mast_n_boot_ctrl; /* 0x008 */ |
| u32 busy_status; /* 0x00c */ |
| u32 intr_status; /* 0x010 */ |
| u32 b0_status; /* 0x014 */ |
| u32 b0_ctrl; /* 0x018 */ |
| u32 b1_status; /* 0x01c */ |
| u32 b1_ctrl; /* 0x020 */ |
| u32 strap_override_ctrl; /* 0x024 */ |
| #if CONFIG_BRCM_BSPI_MAJOR_VERS >= 4 |
| u32 flex_mode_enable; /* 0x028 */ |
| u32 bits_per_cycle; /* 0x02C */ |
| u32 bits_per_phase; /* 0x030 */ |
| u32 cmd_and_mode_byte; /* 0x034 */ |
| u32 flash_upper_addr_byte; /* 0x038 */ |
| u32 xor_value; /* 0x03C */ |
| u32 xor_enable; /* 0x040 */ |
| u32 pio_mode_enable; /* 0x044 */ |
| u32 pio_iodir; /* 0x048 */ |
| u32 pio_data; /* 0x04C */ |
| #endif |
| }; |
| |
| struct bcm_bspi_raf { |
| u32 start_address; /* 0x00 */ |
| u32 num_words; /* 0x04 */ |
| u32 ctrl; /* 0x08 */ |
| u32 fullness; /* 0x0C */ |
| u32 watermark; /* 0x10 */ |
| u32 status; /* 0x14 */ |
| u32 read_data; /* 0x18 */ |
| u32 word_cnt; /* 0x1C */ |
| u32 curr_addr; /* 0x20 */ |
| }; |
| |
| struct bcm_flex_mode { |
| int width; |
| int addrlen; |
| int hp; |
| }; |
| |
| #define STATE_IDLE 0 |
| #define STATE_RUNNING 1 |
| #define STATE_SHUTDOWN 2 |
| |
| struct bcmspi_priv { |
| struct platform_device *pdev; |
| struct spi_master *master; |
| spinlock_t lock; |
| struct bcmspi_parms last_parms; |
| struct position pos; |
| struct list_head msg_queue; |
| int state; |
| int outstanding_bytes; |
| int next_udelay; |
| int cs_change; |
| unsigned int max_speed_hz; |
| volatile struct bcm_mspi_hw *mspi_hw; |
| int irq; |
| struct tasklet_struct tasklet; |
| int curr_cs; |
| |
| /* BSPI */ |
| volatile struct bcm_bspi_hw *bspi_hw; |
| int bspi_enabled; |
| /* all chip selects controlled by BSPI */ |
| int bspi_chip_select; |
| |
| /* LR */ |
| volatile struct bcm_bspi_raf *bspi_hw_raf; |
| struct spi_transfer *cur_xfer; |
| u32 cur_xfer_idx; |
| u32 cur_xfer_len; |
| u32 xfer_status; |
| struct spi_message *cur_msg; |
| u32 actual_length; |
| |
| /* current flex mode settings */ |
| struct bcm_flex_mode flex_mode; |
| |
| /* S3 warm boot context save */ |
| u32 s3_intr2_mask; |
| }; |
| |
| static void bcmspi_enable_interrupt(u32 mask) |
| { |
| BDEV_SET_RB(BCHP_HIF_SPI_INTR2_CPU_MASK_CLEAR, mask); |
| } |
| |
| static void bcmspi_disable_interrupt(u32 mask) |
| { |
| BDEV_SET_RB(BCHP_HIF_SPI_INTR2_CPU_MASK_SET, mask); |
| } |
| |
| static void bcmspi_clear_interrupt(u32 mask) |
| { |
| BDEV_SET_RB(BCHP_HIF_SPI_INTR2_CPU_CLEAR, mask); |
| } |
| |
| static u32 bcmspi_read_interrupt(void) |
| { |
| return BDEV_RD(BCHP_HIF_SPI_INTR2_CPU_STATUS); |
| } |
| |
| static int bcmspi_busy_poll(struct bcmspi_priv *priv) |
| { |
| int i; |
| |
| /* this should normally finish within 10us */ |
| for (i = 0; i < 1000; i++) { |
| if ((priv->bspi_hw->busy_status & 1) == 0) |
| return 0; |
| udelay(1); |
| } |
| dev_warn(&priv->pdev->dev, "timeout waiting for !busy_status\n"); |
| return -EIO; |
| } |
| |
| static void bcmspi_flush_prefetch_buffers(struct bcmspi_priv *priv) |
| { |
| /* SWLINUX-2407: avoid flushing if B1 prefetch is still active */ |
| bcmspi_busy_poll(priv); |
| |
| /* Force rising edge for the b0/b1 'flush' field */ |
| priv->bspi_hw->b0_ctrl = 1; |
| priv->bspi_hw->b1_ctrl = 1; |
| priv->bspi_hw->b0_ctrl = 0; |
| priv->bspi_hw->b1_ctrl = 0; |
| } |
| |
| static int bcmspi_lr_is_fifo_empty(struct bcmspi_priv *priv) |
| { |
| return priv->bspi_hw_raf->status & BCHP_BSPI_RAF_STATUS_FIFO_EMPTY_MASK; |
| } |
| |
| /* BSPI v3 LR is LE only, convert data to host endianness */ |
| #if CONFIG_BRCM_BSPI_MAJOR_VERS >= 4 |
| #define BSPI_DATA(a) (a) |
| #else |
| #define BSPI_DATA(a) le32_to_cpu(a) |
| #endif |
| |
| static inline u32 bcmspi_lr_read_fifo(struct bcmspi_priv *priv) |
| { |
| return BSPI_DATA(priv->bspi_hw_raf->read_data); |
| } |
| |
| static inline void bcmspi_lr_start(struct bcmspi_priv *priv) |
| { |
| priv->bspi_hw_raf->ctrl = BCHP_BSPI_RAF_CTRL_START_MASK; |
| } |
| |
| static inline void bcmspi_lr_clear(struct bcmspi_priv *priv) |
| { |
| priv->bspi_hw_raf->ctrl = BCHP_BSPI_RAF_CTRL_CLEAR_MASK; |
| bcmspi_flush_prefetch_buffers(priv); |
| } |
| |
| static inline int bcmspi_is_4_byte_mode(struct bcmspi_priv *priv) |
| { |
| return priv->flex_mode.addrlen == BSPI_ADDRLEN_4BYTES; |
| } |
| |
| #ifdef BSPI_HAS_FLEX_MODE |
| static int bcmbspi_flash_type(struct bcmspi_priv *priv); |
| |
| static int bcmspi_set_flex_mode(struct bcmspi_priv *priv, |
| int width, int addrlen, int hp) |
| { |
| int bpc = 0, bpp = 0, command; |
| int flex_mode = 1, error = 0; |
| |
| switch (width) { |
| case BSPI_WIDTH_1BIT: |
| bpp = 8; /* dummy cycles */ |
| command = OPCODE_FAST_READ; |
| if (addrlen == BSPI_ADDRLEN_3BYTES) { |
| /* default mode, does not need flex_cmd */ |
| flex_mode = 0; |
| } else { |
| bpp |= BCHP_BSPI_BITS_PER_PHASE_addr_bpp_select_MASK; |
| if (bcmbspi_flash_type(priv) == |
| BSPI_FLASH_TYPE_SPANSION) |
| command = OPCODE_FAST_READ_4B; |
| } |
| break; |
| case BSPI_WIDTH_2BIT: |
| bpc = 0x00000001; /* only data is 2-bit */ |
| if (hp) { |
| bpc |= 0x00010100; /* address and mode are 2-bit too */ |
| bpp |= BCHP_BSPI_BITS_PER_PHASE_mode_bpp_MASK; |
| command = OPCODE_DIOR; |
| if (addrlen == BSPI_ADDRLEN_4BYTES) { |
| bpp |= |
| BCHP_BSPI_BITS_PER_PHASE_addr_bpp_select_MASK; |
| if (bcmbspi_flash_type(priv) == |
| BSPI_FLASH_TYPE_SPANSION) |
| command = OPCODE_DIOR_4B; |
| } |
| } else { |
| bpp = 8; /* dummy cycles */ |
| command = OPCODE_DOR; |
| if (addrlen == BSPI_ADDRLEN_4BYTES) { |
| bpp |= |
| BCHP_BSPI_BITS_PER_PHASE_addr_bpp_select_MASK; |
| if (bcmbspi_flash_type(priv) == |
| BSPI_FLASH_TYPE_SPANSION) |
| command = OPCODE_DOR_4B; |
| } |
| } |
| break; |
| case BSPI_WIDTH_4BIT: |
| bpc = 0x00000002; /* only data is 4-bit */ |
| if (hp) { |
| bpc |= 0x00020200; /* address and mode are 4-bit too */ |
| bpp = 4; /* dummy cycles */ |
| bpp |= BCHP_BSPI_BITS_PER_PHASE_mode_bpp_MASK; |
| command = OPCODE_QIOR; |
| if (addrlen == BSPI_ADDRLEN_4BYTES) { |
| bpp |= |
| BCHP_BSPI_BITS_PER_PHASE_addr_bpp_select_MASK; |
| if (bcmbspi_flash_type(priv) == |
| BSPI_FLASH_TYPE_SPANSION) |
| command = OPCODE_QIOR_4B; |
| } |
| } else { |
| bpp = 8; /* dummy cycles */ |
| command = OPCODE_QOR; |
| if (addrlen == BSPI_ADDRLEN_4BYTES) { |
| bpp |= |
| BCHP_BSPI_BITS_PER_PHASE_addr_bpp_select_MASK; |
| if (bcmbspi_flash_type(priv) == |
| BSPI_FLASH_TYPE_SPANSION) |
| command = OPCODE_QOR_4B; |
| } |
| } |
| break; |
| default: |
| error = 1; |
| break; |
| } |
| |
| if (!error) { |
| priv->bspi_hw->flex_mode_enable = 0; |
| priv->bspi_hw->bits_per_cycle = bpc; |
| priv->bspi_hw->bits_per_phase = bpp; |
| priv->bspi_hw->cmd_and_mode_byte = command; |
| priv->bspi_hw->flex_mode_enable = flex_mode ? |
| BCHP_BSPI_FLEX_MODE_ENABLE_bspi_flex_mode_enable_MASK |
| : 0; |
| DBG("%s: width=%d addrlen=%d hp=%d\n", |
| __func__, width, addrlen, hp); |
| DBG("%s: fme=%08x bpc=%08x bpp=%08x cmd=%08x\n", __func__, |
| priv->bspi_hw->flex_mode_enable, |
| priv->bspi_hw->bits_per_cycle, |
| priv->bspi_hw->bits_per_phase, |
| priv->bspi_hw->cmd_and_mode_byte); |
| } |
| |
| return error; |
| } |
| #else |
| static int bcmspi_set_flex_mode(struct bcmspi_priv *priv, |
| int width, int addrlen, int hp) |
| { |
| u32 strap_override = priv->bspi_hw->strap_override_ctrl; |
| |
| switch (width) { |
| case BSPI_WIDTH_4BIT: |
| strap_override &= ~0x2; /* clear dual mode */ |
| strap_override |= 0x9; /* set quad mode + override */ |
| break; |
| case BSPI_WIDTH_2BIT: |
| strap_override &= ~0x8; /* clear quad mode */ |
| strap_override |= 0x3; /* set dual mode + override */ |
| break; |
| case BSPI_WIDTH_1BIT: |
| strap_override &= ~0xA; /* clear quad/dual mode */ |
| strap_override |= 1; /* set override */ |
| break; |
| default: |
| break; |
| } |
| |
| if (addrlen == BSPI_ADDRLEN_4BYTES) { |
| strap_override |= 0x5; /* set 4byte + override */ |
| } else { |
| strap_override &= ~0x4; /* clear 4 byte */ |
| strap_override |= 0x1; /* set override */ |
| } |
| |
| priv->bspi_hw->strap_override_ctrl = strap_override; |
| |
| return 0; |
| } |
| #endif |
| |
| static void bcmspi_set_mode(struct bcmspi_priv *priv, |
| int width, int addrlen, int hp) |
| { |
| int error = 0; |
| |
| if (width == -1) |
| width = priv->flex_mode.width; |
| if (addrlen == -1) |
| addrlen = priv->flex_mode.addrlen; |
| if (hp == -1) |
| hp = priv->flex_mode.hp; |
| |
| error = bcmspi_set_flex_mode(priv, width, addrlen, hp); |
| |
| if (!error) { |
| priv->flex_mode.width = width; |
| priv->flex_mode.addrlen = addrlen; |
| priv->flex_mode.hp = hp; |
| dev_info(&priv->pdev->dev, |
| "%d-lane output, %d-byte address%s\n", |
| priv->flex_mode.width, |
| priv->flex_mode.addrlen, |
| priv->flex_mode.hp ? ", high-performance mode" : ""); |
| } else |
| dev_warn(&priv->pdev->dev, |
| "INVALID COMBINATION: width=%d addrlen=%d hp=%d\n", |
| width, addrlen, hp); |
| } |
| |
| static void bcmspi_set_chip_select(struct bcmspi_priv *priv, int cs) |
| { |
| if (priv->curr_cs != cs) { |
| DBG("Switching CS%1d => CS%1d\n", |
| priv->curr_cs, cs); |
| BDEV_WR_RB(BCHP_EBI_CS_SPI_SELECT, |
| (BDEV_RD(BCHP_EBI_CS_SPI_SELECT) & ~0xff) | |
| (1 << cs)); |
| udelay(10); |
| } |
| priv->curr_cs = cs; |
| |
| } |
| |
| static inline int is_bspi_chip_select(struct bcmspi_priv *priv, u8 cs) |
| { |
| return priv->bspi_chip_select & (1 << cs); |
| } |
| |
| static void bcmspi_disable_bspi(struct bcmspi_priv *priv) |
| { |
| if (!priv->bspi_hw || !priv->bspi_enabled) |
| return; |
| |
| priv->bspi_enabled = 0; |
| if ((priv->bspi_hw->mast_n_boot_ctrl & 1) == 1) |
| return; |
| |
| DBG("disabling bspi\n"); |
| bcmspi_busy_poll(priv); |
| priv->bspi_hw->mast_n_boot_ctrl = 1; |
| udelay(1); |
| } |
| |
| static void bcmspi_enable_bspi(struct bcmspi_priv *priv) |
| { |
| if (!priv->bspi_hw || priv->bspi_enabled) |
| return; |
| if ((priv->bspi_hw->mast_n_boot_ctrl & 1) == 0) { |
| priv->bspi_enabled = 1; |
| return; |
| } |
| |
| DBG("enabling bspi\n"); |
| bcmspi_flush_prefetch_buffers(priv); |
| udelay(1); |
| priv->bspi_hw->mast_n_boot_ctrl = 0; |
| priv->bspi_enabled = 1; |
| } |
| |
| static void bcmspi_hw_set_parms(struct bcmspi_priv *priv, |
| const struct bcmspi_parms *xp) |
| { |
| if (xp->speed_hz) { |
| unsigned int spbr = MSPI_BASE_FREQ / (2 * xp->speed_hz); |
| |
| priv->mspi_hw->spcr0_lsb = max(min(spbr, SPBR_MAX), SPBR_MIN); |
| } else { |
| priv->mspi_hw->spcr0_lsb = SPBR_MIN; |
| } |
| priv->mspi_hw->spcr0_msb = 0x80 | /* MSTR bit */ |
| ((xp->bits_per_word ? xp->bits_per_word : 8) << 2) | |
| (xp->mode & 3); |
| priv->last_parms = *xp; |
| } |
| |
| #define PARMS_NO_OVERRIDE 0 |
| #define PARMS_OVERRIDE 1 |
| |
| static int bcmspi_update_parms(struct bcmspi_priv *priv, |
| struct spi_device *spidev, struct spi_transfer *trans, int override) |
| { |
| struct bcmspi_parms xp; |
| |
| xp.speed_hz = min(trans->speed_hz ? trans->speed_hz : |
| (spidev->max_speed_hz ? spidev->max_speed_hz : MAX_SPEED_HZ), |
| MAX_SPEED_HZ); |
| xp.chip_select = spidev->chip_select; |
| xp.mode = spidev->mode; |
| xp.bits_per_word = trans->bits_per_word ? trans->bits_per_word : |
| (spidev->bits_per_word ? spidev->bits_per_word : 8); |
| |
| if ((override == PARMS_OVERRIDE) || |
| ((xp.speed_hz == priv->last_parms.speed_hz) && |
| (xp.chip_select == priv->last_parms.chip_select) && |
| (xp.mode == priv->last_parms.mode) && |
| (xp.bits_per_word == priv->last_parms.bits_per_word))) { |
| bcmspi_hw_set_parms(priv, &xp); |
| return 0; |
| } |
| /* no override, and parms do not match */ |
| return 1; |
| } |
| |
| |
| static int bcmspi_setup(struct spi_device *spi) |
| { |
| struct bcmspi_parms *xp; |
| struct bcmspi_priv *priv = spi_master_get_devdata(spi->master); |
| |
| DBG("%s\n", __func__); |
| |
| if (spi->bits_per_word > 16) |
| return -EINVAL; |
| |
| xp = spi_get_ctldata(spi); |
| if (!xp) { |
| xp = kzalloc(sizeof(struct bcmspi_parms), GFP_KERNEL); |
| if (!xp) |
| return -ENOMEM; |
| spi_set_ctldata(spi, xp); |
| } |
| if (spi->max_speed_hz < priv->max_speed_hz) |
| xp->speed_hz = spi->max_speed_hz; |
| else |
| xp->speed_hz = 0; |
| |
| xp->chip_select = spi->chip_select; |
| xp->mode = spi->mode; |
| xp->bits_per_word = spi->bits_per_word ? spi->bits_per_word : 8; |
| |
| return 0; |
| } |
| |
| /* stop at end of transfer, no other reason */ |
| #define FNB_BREAK_NONE 0 |
| /* stop at end of spi_message */ |
| #define FNB_BREAK_EOM 1 |
| /* stop at end of spi_transfer if delay */ |
| #define FNB_BREAK_DELAY 2 |
| /* stop at end of spi_transfer if cs_change */ |
| #define FNB_BREAK_CS_CHANGE 4 |
| /* stop if we run out of bytes */ |
| #define FNB_BREAK_NO_BYTES 8 |
| |
| /* events that make us stop filling TX slots */ |
| #define FNB_BREAK_TX (FNB_BREAK_EOM | FNB_BREAK_DELAY | \ |
| FNB_BREAK_CS_CHANGE) |
| |
| /* events that make us deassert CS */ |
| #define FNB_BREAK_DESELECT (FNB_BREAK_EOM | FNB_BREAK_CS_CHANGE) |
| |
| |
| static int find_next_byte(struct bcmspi_priv *priv, struct position *p, |
| struct list_head *completed, int flags) |
| { |
| int ret = FNB_BREAK_NONE; |
| |
| p->byte++; |
| |
| while (p->byte >= p->trans->len) { |
| /* we're at the end of the spi_transfer */ |
| |
| /* in TX mode, need to pause for a delay or CS change */ |
| if (p->trans->delay_usecs && (flags & FNB_BREAK_DELAY)) |
| ret |= FNB_BREAK_DELAY; |
| if (p->trans->cs_change && (flags & FNB_BREAK_CS_CHANGE)) |
| ret |= FNB_BREAK_CS_CHANGE; |
| if (ret) |
| return ret; |
| |
| /* advance to next spi_message? */ |
| if (list_is_last(&p->trans->transfer_list, |
| &p->msg->transfers)) { |
| struct spi_message *next_msg = NULL; |
| |
| /* TX breaks at the end of each message as well */ |
| if (!completed || (flags & FNB_BREAK_EOM)) { |
| DBG("find_next_byte: advance msg exit\n"); |
| return FNB_BREAK_EOM; |
| } |
| if (!list_is_last(&p->msg->queue, &priv->msg_queue)) { |
| next_msg = list_entry(p->msg->queue.next, |
| struct spi_message, queue); |
| } |
| /* delete from run queue, add to completion queue */ |
| list_del(&p->msg->queue); |
| list_add_tail(&p->msg->queue, completed); |
| |
| p->msg = next_msg; |
| p->byte = 0; |
| if (p->msg == NULL) { |
| p->trans = NULL; |
| ret = FNB_BREAK_NO_BYTES; |
| break; |
| } |
| |
| /* |
| * move on to the first spi_transfer of the new |
| * spi_message |
| */ |
| p->trans = list_entry(p->msg->transfers.next, |
| struct spi_transfer, transfer_list); |
| } else { |
| /* or just advance to the next spi_transfer */ |
| p->trans = list_entry(p->trans->transfer_list.next, |
| struct spi_transfer, transfer_list); |
| p->byte = 0; |
| } |
| } |
| DBG("find_next_byte: msg %p trans %p len %d byte %d ret %x\n", |
| p->msg, p->trans, p->trans ? p->trans->len : 0, p->byte, ret); |
| return ret; |
| } |
| |
| static void read_from_hw(struct bcmspi_priv *priv, struct list_head *completed) |
| { |
| struct position p; |
| int slot = 0, n = priv->outstanding_bytes; |
| |
| DBG("%s\n", __func__); |
| |
| p = priv->pos; |
| |
| while (n > 0) { |
| BUG_ON(p.msg == NULL); |
| |
| if (p.trans->bits_per_word <= 8) { |
| u8 *buf = p.trans->rx_buf; |
| |
| if (buf) { |
| buf[p.byte] = |
| priv->mspi_hw->rxram[(slot << 1) + 1] |
| & 0xff; |
| } |
| DBG("RD %02x\n", buf ? buf[p.byte] : 0xff); |
| } else { |
| u16 *buf = p.trans->rx_buf; |
| |
| if (buf) { |
| buf[p.byte] = |
| ((priv->mspi_hw->rxram[(slot << 1) + 1] |
| & 0xff) << 0) | |
| ((priv->mspi_hw->rxram[(slot << 1) + 0] |
| & 0xff) << 8); |
| } |
| } |
| slot++; |
| n--; |
| p.msg->actual_length++; |
| |
| find_next_byte(priv, &p, completed, FNB_BREAK_NONE); |
| } |
| |
| priv->pos = p; |
| priv->outstanding_bytes = 0; |
| } |
| |
| static void write_to_hw(struct bcmspi_priv *priv) |
| { |
| struct position p; |
| int slot = 0, fnb = 0; |
| struct spi_message *msg = NULL; |
| |
| DBG("%s\n", __func__); |
| |
| bcmspi_disable_bspi(priv); |
| |
| p = priv->pos; |
| |
| while (1) { |
| if (p.msg == NULL) |
| break; |
| if (!msg) { |
| msg = p.msg; |
| bcmspi_update_parms(priv, msg->spi, p.trans, |
| PARMS_OVERRIDE); |
| } else { |
| /* break if the speed, bits, etc. changed */ |
| if (bcmspi_update_parms(priv, msg->spi, p.trans, |
| PARMS_NO_OVERRIDE)) { |
| DBG("parms don't match, breaking\n"); |
| break; |
| } |
| } |
| if (p.trans->bits_per_word <= 8) { |
| const u8 *buf = p.trans->tx_buf; |
| |
| priv->mspi_hw->txram[slot << 1] = buf ? |
| (buf[p.byte] & 0xff) : 0xff; |
| DBG("WR %02x\n", buf ? buf[p.byte] : 0xff); |
| } else { |
| const u16 *buf = p.trans->tx_buf; |
| |
| priv->mspi_hw->txram[(slot << 1) + 0] = buf ? |
| (buf[p.byte] >> 8) : 0xff; |
| priv->mspi_hw->txram[(slot << 1) + 1] = buf ? |
| (buf[p.byte] & 0xff) : 0xff; |
| } |
| priv->mspi_hw->cdram[slot] = |
| ((p.trans->bits_per_word <= 8) ? 0x8e : 0xce); |
| slot++; |
| |
| fnb = find_next_byte(priv, &p, NULL, FNB_BREAK_TX); |
| |
| if (fnb & FNB_BREAK_CS_CHANGE) |
| priv->cs_change = 1; |
| if (fnb & FNB_BREAK_DELAY) |
| priv->next_udelay = p.trans->delay_usecs; |
| if (fnb || (slot == NUM_CDRAM)) |
| break; |
| } |
| if (slot) { |
| DBG("submitting %d slots\n", slot); |
| priv->mspi_hw->newqp = 0; |
| priv->mspi_hw->endqp = slot - 1; |
| |
| /* deassert CS on the final byte */ |
| if (fnb & FNB_BREAK_DESELECT) |
| priv->mspi_hw->cdram[slot - 1] &= ~0x80; |
| |
| /* tell HIF_MSPI which CS to use */ |
| bcmspi_set_chip_select(priv, msg->spi->chip_select); |
| |
| BDEV_WR_F_RB(HIF_MSPI_WRITE_LOCK, WriteLock, 1); |
| priv->mspi_hw->spcr2 = 0xe0; /* cont | spe | spifie */ |
| |
| priv->state = STATE_RUNNING; |
| priv->outstanding_bytes = slot; |
| } else { |
| BDEV_WR_F_RB(HIF_MSPI_WRITE_LOCK, WriteLock, 0); |
| priv->state = STATE_IDLE; |
| } |
| } |
| |
| #define DWORD_ALIGNED(a) (!(((unsigned long)(a)) & 3)) |
| #define ADDR_TO_4MBYTE_SEGMENT(addr) (((u32)(addr)) >> 22) |
| |
| static int bcmspi_emulate_flash_read(struct bcmspi_priv *priv, |
| struct spi_message *msg) |
| { |
| struct spi_transfer *trans; |
| u32 addr, len, len_in_dwords; |
| u8 *buf, *src; |
| unsigned long flags; |
| int idx; |
| |
| #ifndef BSPI_HAS_4BYTE_ADDRESS |
| /* 4-byte address mode is not supported through BSPI */ |
| if (bcmspi_is_4_byte_mode(priv)) |
| return -1; |
| #endif |
| |
| /* acquire lock when the MSPI is idle */ |
| while (1) { |
| spin_lock_irqsave(&priv->lock, flags); |
| if (priv->state == STATE_IDLE) |
| break; |
| spin_unlock_irqrestore(&priv->lock, flags); |
| if (priv->state == STATE_SHUTDOWN) |
| return -EIO; |
| udelay(1); |
| } |
| bcmspi_set_chip_select(priv, msg->spi->chip_select); |
| |
| /* first transfer - OPCODE_READ + 3-byte address */ |
| trans = list_entry(msg->transfers.next, struct spi_transfer, |
| transfer_list); |
| buf = (void *)trans->tx_buf; |
| |
| idx = 1; |
| |
| #ifdef BSPI_HAS_4BYTE_ADDRESS |
| if (bcmspi_is_4_byte_mode(priv)) |
| priv->bspi_hw->flash_upper_addr_byte = buf[idx++] << 24; |
| else |
| priv->bspi_hw->flash_upper_addr_byte = 0; |
| #endif |
| |
| /* |
| * addr coming into this function is a raw flash offset |
| * we need to convert it to the BSPI address |
| */ |
| addr = (buf[idx] << 16) | (buf[idx+1] << 8) | buf[idx+2]; |
| addr = (addr + 0xc00000) & 0xffffff; |
| |
| /* second transfer - read result into buffer */ |
| trans = list_entry(msg->transfers.next->next, struct spi_transfer, |
| transfer_list); |
| |
| buf = (void *)trans->rx_buf; |
| |
| len = trans->len; |
| |
| /* non-aligned and very short transfers are handled by MSPI */ |
| if (unlikely(!DWORD_ALIGNED(addr) || |
| !DWORD_ALIGNED(buf) || |
| len < sizeof(u32) || |
| !priv->bspi_hw_raf)) { |
| spin_unlock_irqrestore(&priv->lock, flags); |
| return -1; |
| } |
| |
| src = (u8 *)BSPI_BASE_ADDR + addr; |
| |
| bcmspi_enable_bspi(priv); |
| |
| DBG("%s: dst %p src %p len %x addr BSPI %06x\n", |
| __func__, buf, src, len, addr); |
| |
| /* We assume address will never cross 4Mbyte boundary |
| * within one transfer. If it does |
| * read might be incorrect */ |
| BUG_ON(ADDR_TO_4MBYTE_SEGMENT(addr) ^ |
| ADDR_TO_4MBYTE_SEGMENT(addr+len-1)); |
| |
| len_in_dwords = (len + 3) >> 2; |
| |
| /* initialize software parameters */ |
| priv->xfer_status = 0; |
| priv->cur_xfer = trans; |
| priv->cur_xfer_idx = 0; |
| priv->cur_xfer_len = len; |
| priv->cur_msg = msg; |
| priv->actual_length = idx + 4 + trans->len; |
| |
| /* setup hardware */ |
| /* address must be 4-byte aligned */ |
| priv->bspi_hw_raf->start_address = addr; |
| priv->bspi_hw_raf->num_words = len_in_dwords; |
| priv->bspi_hw_raf->watermark = 0; |
| |
| DBG("READ: %08x %08x (%08x)\n", addr, len_in_dwords, trans->len); |
| |
| bcmspi_clear_interrupt(0xffffffff); |
| bcmspi_enable_interrupt(BSPI_LR_INTERRUPTS_ALL); |
| bcmspi_lr_start(priv); |
| |
| spin_unlock_irqrestore(&priv->lock, flags); |
| |
| return 0; |
| } |
| |
| /* |
| * m25p80_read() calls wait_till_ready() before each read to check |
| * the flash status register for pending writes. |
| * |
| * This can be safely skipped if our last transaction was just an |
| * emulated BSPI read. |
| */ |
| static int bcmspi_emulate_flash_rdsr(struct bcmspi_priv *priv, |
| struct spi_message *msg) |
| { |
| u8 *buf; |
| struct spi_transfer *trans; |
| |
| if (priv->bspi_enabled == 0) |
| return 1; |
| |
| trans = list_entry(msg->transfers.next->next, struct spi_transfer, |
| transfer_list); |
| |
| buf = (void *)trans->rx_buf; |
| *buf = 0x00; |
| |
| msg->actual_length = 2; |
| msg->complete(msg->context); |
| msg->status = 0; |
| |
| return 0; |
| } |
| |
| static int bcmspi_transfer(struct spi_device *spi, struct spi_message *msg) |
| { |
| struct bcmspi_priv *priv = spi_master_get_devdata(spi->master); |
| unsigned long flags; |
| |
| if (is_bspi_chip_select(priv, msg->spi->chip_select)) { |
| struct spi_transfer *trans; |
| |
| trans = list_entry(msg->transfers.next, |
| struct spi_transfer, transfer_list); |
| if (trans && trans->len && trans->tx_buf) { |
| u8 command = ((u8 *)trans->tx_buf)[0]; |
| switch (command) { |
| case OPCODE_FAST_READ: |
| if (bcmspi_emulate_flash_read(priv, msg) == 0) |
| return 0; |
| break; |
| case OPCODE_RDSR: |
| if (bcmspi_emulate_flash_rdsr(priv, msg) == 0) |
| return 0; |
| break; |
| case OPCODE_EN4B: |
| DBG("ENABLE 4-BYTE MODE\n"); |
| bcmspi_set_mode(priv, -1, |
| BSPI_ADDRLEN_4BYTES, -1); |
| break; |
| case OPCODE_EX4B: |
| DBG("DISABLE 4-BYTE MODE\n"); |
| bcmspi_set_mode(priv, -1, |
| BSPI_ADDRLEN_3BYTES, -1); |
| break; |
| case OPCODE_BRWR: |
| { |
| u8 enable = ((u8 *)trans->tx_buf)[1]; |
| DBG("%s 4-BYTE MODE\n", |
| enable ? "ENABLE" : "DISABLE"); |
| bcmspi_set_mode(priv, -1, |
| enable ? BSPI_ADDRLEN_4BYTES : |
| BSPI_ADDRLEN_3BYTES, -1); |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| spin_lock_irqsave(&priv->lock, flags); |
| |
| if (priv->state == STATE_SHUTDOWN) { |
| spin_unlock_irqrestore(&priv->lock, flags); |
| return -EIO; |
| } |
| |
| msg->actual_length = 0; |
| |
| list_add_tail(&msg->queue, &priv->msg_queue); |
| |
| if (priv->state == STATE_IDLE) { |
| BUG_ON(priv->pos.msg != NULL); |
| priv->pos.msg = msg; |
| priv->pos.trans = list_entry(msg->transfers.next, |
| struct spi_transfer, transfer_list); |
| priv->pos.byte = 0; |
| |
| write_to_hw(priv); |
| } |
| spin_unlock_irqrestore(&priv->lock, flags); |
| |
| return 0; |
| } |
| |
| static void bcmspi_cleanup(struct spi_device *spi) |
| { |
| struct bcmspi_parms *xp = spi_get_ctldata(spi); |
| |
| DBG("%s\n", __func__); |
| |
| kfree(xp); |
| } |
| |
| static irqreturn_t bcmspi_interrupt(int irq, void *dev_id) |
| { |
| struct bcmspi_priv *priv = dev_id; |
| |
| if (priv->bspi_enabled && priv->cur_xfer) { |
| int done = 0; |
| u32 status = bcmspi_read_interrupt(); |
| u32 *buf = (u32 *)priv->cur_xfer->rx_buf; |
| if (status & BSPI_LR_INTERRUPTS_DATA) { |
| while (!bcmspi_lr_is_fifo_empty(priv)) { |
| u32 data = bcmspi_lr_read_fifo(priv); |
| if (likely(priv->cur_xfer_len >= 4)) { |
| buf[priv->cur_xfer_idx++] = data; |
| priv->cur_xfer_len -= 4; |
| } else { |
| /* |
| * Read out remaining bytes, make sure |
| * we do not cross the buffer boundary |
| */ |
| u8 *cbuf = |
| (u8 *)&buf[priv->cur_xfer_idx]; |
| data = cpu_to_le32(data); |
| while (priv->cur_xfer_len) { |
| *cbuf++ = (u8)data; |
| data >>= 8; |
| priv->cur_xfer_len--; |
| } |
| } |
| } |
| } |
| if (status & BSPI_LR_INTERRUPTS_ERROR) { |
| dev_err(&priv->pdev->dev, "ERROR %02x\n", status); |
| priv->xfer_status = -EIO; |
| } else if (!priv->cur_xfer_len) |
| done = 1; |
| if (done) { |
| priv->cur_xfer = NULL; |
| bcmspi_disable_interrupt(BSPI_LR_INTERRUPTS_ALL); |
| |
| if (priv->xfer_status) { |
| bcmspi_lr_clear(priv); |
| } else { |
| bcmspi_flush_prefetch_buffers(priv); |
| |
| if (priv->cur_msg) { |
| priv->cur_msg->actual_length = |
| priv->actual_length; |
| priv->cur_msg->complete( |
| priv->cur_msg->context); |
| priv->cur_msg->status = 0; |
| } |
| } |
| priv->cur_msg = NULL; |
| } |
| bcmspi_clear_interrupt(status); |
| return IRQ_HANDLED; |
| } |
| |
| if (priv->mspi_hw->mspi_status & 1) { |
| /* clear interrupt */ |
| priv->mspi_hw->mspi_status &= ~1; |
| bcmspi_clear_interrupt( |
| BCHP_HIF_SPI_INTR2_CPU_SET_MSPI_DONE_MASK); |
| |
| tasklet_schedule(&priv->tasklet); |
| return IRQ_HANDLED; |
| } else |
| return IRQ_NONE; |
| } |
| |
| static void bcmspi_tasklet(unsigned long param) |
| { |
| struct bcmspi_priv *priv = (void *)param; |
| struct list_head completed; |
| struct spi_message *msg; |
| unsigned long flags; |
| |
| INIT_LIST_HEAD(&completed); |
| spin_lock_irqsave(&priv->lock, flags); |
| |
| if (priv->next_udelay) { |
| udelay(priv->next_udelay); |
| priv->next_udelay = 0; |
| } |
| |
| msg = priv->pos.msg; |
| |
| read_from_hw(priv, &completed); |
| if (priv->cs_change) { |
| udelay(10); |
| priv->cs_change = 0; |
| } |
| |
| write_to_hw(priv); |
| spin_unlock_irqrestore(&priv->lock, flags); |
| |
| while (!list_empty(&completed)) { |
| msg = list_first_entry(&completed, struct spi_message, queue); |
| list_del(&msg->queue); |
| msg->status = 0; |
| if (msg->complete) |
| msg->complete(msg->context); |
| } |
| } |
| |
| static void bcmspi_complete(void *arg) |
| { |
| complete(arg); |
| } |
| |
| static struct spi_master *default_master; |
| |
| int bcmspi_simple_transaction(struct bcmspi_parms *xp, |
| const void *tx_buf, int tx_len, void *rx_buf, int rx_len) |
| { |
| DECLARE_COMPLETION_ONSTACK(fini); |
| struct spi_message m; |
| struct spi_transfer t_tx, t_rx; |
| struct spi_device spi; |
| int ret; |
| |
| memset(&spi, 0, sizeof(spi)); |
| spi.max_speed_hz = xp->speed_hz; |
| spi.chip_select = xp->chip_select; |
| spi.mode = xp->mode; |
| spi.bits_per_word = xp->bits_per_word; |
| spi.master = default_master; |
| |
| spi_message_init(&m); |
| m.complete = bcmspi_complete; |
| m.context = &fini; |
| m.spi = &spi; |
| |
| memset(&t_tx, 0, sizeof(t_tx)); |
| memset(&t_rx, 0, sizeof(t_rx)); |
| t_tx.tx_buf = tx_buf; |
| t_tx.len = tx_len; |
| t_rx.rx_buf = rx_buf; |
| t_rx.len = rx_len; |
| |
| if (tx_len) |
| spi_message_add_tail(&t_tx, &m); |
| if (rx_len) |
| spi_message_add_tail(&t_rx, &m); |
| |
| ret = bcmspi_transfer(&spi, &m); |
| if (!ret) |
| wait_for_completion(&fini); |
| return ret; |
| } |
| EXPORT_SYMBOL(bcmspi_simple_transaction); |
| |
| static void bcmspi_hw_init(struct bcmspi_priv *priv) |
| { |
| priv->mspi_hw->spcr1_lsb = 0; |
| priv->mspi_hw->spcr1_msb = 0; |
| priv->mspi_hw->newqp = 0; |
| priv->mspi_hw->endqp = 0; |
| priv->mspi_hw->spcr2 = 0x20; /* spifie */ |
| |
| bcmspi_hw_set_parms(priv, &bcmspi_default_parms_cs0); |
| |
| if (!priv->bspi_hw) |
| return; |
| |
| priv->bspi_enabled = 1; |
| bcmspi_disable_bspi(priv); |
| |
| priv->bspi_hw->b0_ctrl = 0; |
| priv->bspi_hw->b1_ctrl = 0; |
| } |
| |
| static void bcmspi_hw_uninit(struct bcmspi_priv *priv) |
| { |
| priv->mspi_hw->spcr2 = 0x0; /* disable irq and enable bits */ |
| bcmspi_enable_bspi(priv); |
| } |
| |
| static int bcmbspi_flash_type(struct bcmspi_priv *priv) |
| { |
| char tx_buf[4]; |
| unsigned char jedec_id[5] = {0}; |
| |
| /* command line override */ |
| if (bspi_flash != BSPI_FLASH_TYPE_UNKNOWN) |
| return bspi_flash; |
| |
| /* Read ID */ |
| tx_buf[0] = OPCODE_RDID; |
| bcmspi_simple_transaction(&priv->last_parms, tx_buf, 1, &jedec_id, 5); |
| |
| switch (jedec_id[0]) { |
| case 0x01: /* Spansion */ |
| case 0xef: |
| bspi_flash = BSPI_FLASH_TYPE_SPANSION; |
| break; |
| case 0xc2: /* Macronix */ |
| bspi_flash = BSPI_FLASH_TYPE_MACRONIX; |
| break; |
| case 0xbf: /* SST */ |
| bspi_flash = BSPI_FLASH_TYPE_SST; |
| break; |
| case 0x89: /* Numonyx */ |
| bspi_flash = BSPI_FLASH_TYPE_NUMONYX; |
| break; |
| default: |
| bspi_flash = BSPI_FLASH_TYPE_UNKNOWN; |
| break; |
| } |
| return bspi_flash; |
| } |
| |
| static int bcmspi_set_quad_mode(struct bcmspi_priv *priv, int _enable) |
| { |
| char tx_buf[4]; |
| unsigned char cfg_reg, sts_reg; |
| |
| switch (bcmbspi_flash_type(priv)) { |
| case BSPI_FLASH_TYPE_SPANSION: |
| /* RCR */ |
| tx_buf[0] = OPCODE_RCR; |
| bcmspi_simple_transaction(&priv->last_parms, |
| tx_buf, 1, &cfg_reg, 1); |
| if (_enable) |
| cfg_reg |= 0x2; |
| else |
| cfg_reg &= ~0x2; |
| /* WREN */ |
| tx_buf[0] = OPCODE_WREN; |
| bcmspi_simple_transaction(&priv->last_parms, |
| tx_buf, 1, NULL, 0); |
| /* WRR */ |
| tx_buf[0] = OPCODE_WRR; |
| tx_buf[1] = 0; /* status register */ |
| tx_buf[2] = cfg_reg; /* configuration register */ |
| bcmspi_simple_transaction(&priv->last_parms, |
| tx_buf, 3, NULL, 0); |
| /* wait till ready */ |
| do { |
| tx_buf[0] = OPCODE_RDSR; |
| bcmspi_simple_transaction(&priv->last_parms, |
| tx_buf, 1, &sts_reg, 1); |
| udelay(1); |
| } while (sts_reg & 1); |
| break; |
| case BSPI_FLASH_TYPE_MACRONIX: |
| /* RDSR */ |
| tx_buf[0] = OPCODE_RDSR; |
| bcmspi_simple_transaction(&priv->last_parms, |
| tx_buf, 1, &cfg_reg, 1); |
| if (_enable) |
| cfg_reg |= 0x40; |
| else |
| cfg_reg &= ~0x40; |
| /* WREN */ |
| tx_buf[0] = OPCODE_WREN; |
| bcmspi_simple_transaction(&priv->last_parms, |
| tx_buf, 1, NULL, 0); |
| /* WRSR */ |
| tx_buf[0] = OPCODE_WRSR; |
| tx_buf[1] = cfg_reg; /* status register */ |
| bcmspi_simple_transaction(&priv->last_parms, |
| tx_buf, 2, NULL, 0); |
| /* wait till ready */ |
| do { |
| tx_buf[0] = OPCODE_RDSR; |
| bcmspi_simple_transaction(&priv->last_parms, |
| tx_buf, 1, &sts_reg, 1); |
| udelay(1); |
| } while (sts_reg & 1); |
| /* RDSR */ |
| tx_buf[0] = OPCODE_RDSR; |
| bcmspi_simple_transaction(&priv->last_parms, |
| tx_buf, 1, &cfg_reg, 1); |
| break; |
| case BSPI_FLASH_TYPE_SST: |
| case BSPI_FLASH_TYPE_NUMONYX: |
| /* TODO - send Quad mode control command */ |
| break; |
| default: |
| if (_enable) |
| dev_err(&priv->pdev->dev, |
| "Unrecognized flash type, no QUAD MODE support"); |
| return _enable ? -1 : 0; |
| } |
| |
| return 0; |
| } |
| |
| static int bcmspi_probe(struct platform_device *pdev) |
| { |
| struct device *dev = &pdev->dev; |
| struct brcmspi_platform_data *pdata; |
| struct bcmspi_priv *priv; |
| struct spi_master *master; |
| struct resource *res; |
| int ret; |
| |
| DBG("bcmspi_probe\n"); |
| |
| pdata = (struct brcmspi_platform_data *)pdev->dev.platform_data; |
| |
| BDEV_WR_RB(BCHP_BSPI_MAST_N_BOOT_CTRL, 1); |
| bcmspi_disable_interrupt(0xffffffff); |
| bcmspi_clear_interrupt(0xffffffff); |
| bcmspi_enable_interrupt(BCHP_HIF_SPI_INTR2_CPU_SET_MSPI_DONE_MASK); |
| |
| master = spi_alloc_master(dev, sizeof(struct bcmspi_priv)); |
| if (!master) { |
| dev_err(&pdev->dev, "error allocating spi_master\n"); |
| return -ENOMEM; |
| } |
| |
| priv = spi_master_get_devdata(master); |
| |
| priv->pdev = pdev; |
| priv->state = STATE_IDLE; |
| priv->pos.msg = NULL; |
| priv->master = master; |
| |
| master->bus_num = pdev->id; |
| master->num_chipselect = NUM_CHIPSELECT; |
| master->mode_bits = SPI_MODE_3; |
| |
| master->setup = bcmspi_setup; |
| master->transfer = bcmspi_transfer; |
| master->cleanup = bcmspi_cleanup; |
| |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (!res) { |
| dev_err(&pdev->dev, "can't get resource 0\n"); |
| ret = -EIO; |
| goto err3; |
| } |
| /* MSPI register range */ |
| priv->mspi_hw = (volatile void *)ioremap(res->start, |
| res->end - res->start); |
| if (!priv->mspi_hw) { |
| dev_err(&pdev->dev, "can't ioremap\n"); |
| ret = -EIO; |
| goto err3; |
| } |
| |
| /* IRQ */ |
| priv->irq = platform_get_irq(pdev, 0); |
| if (priv->irq < 0) { |
| dev_err(&pdev->dev, "no IRQ defined\n"); |
| ret = -ENODEV; |
| goto err2; |
| } |
| |
| ret = request_irq(priv->irq, bcmspi_interrupt, 0, "spi_brcmstb", priv); |
| if (ret < 0) { |
| dev_err(&pdev->dev, "unable to allocate IRQ\n"); |
| goto err2; |
| } |
| |
| /* BSPI register range (not supported on all platforms) */ |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
| if (res && !nobspi) { |
| priv->bspi_hw = (volatile void *)ioremap(res->start, |
| res->end - res->start); |
| if (!priv->bspi_hw) { |
| dev_err(&pdev->dev, "can't ioremap BSPI range\n"); |
| ret = -EIO; |
| goto err2; |
| } |
| } else |
| priv->bspi_hw = NULL; |
| |
| /* BSPI_RAF register range */ |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 2); |
| if (res && !nobspi) { |
| priv->bspi_hw_raf = (volatile void *)ioremap(res->start, |
| res->end - res->start); |
| if (!priv->bspi_hw_raf) { |
| dev_err(&pdev->dev, "can't ioremap BSPI_RAF range\n"); |
| ret = -EIO; |
| goto err2_1; |
| } |
| } else |
| priv->bspi_hw_raf = NULL; |
| |
| bcmspi_hw_init(priv); |
| priv->curr_cs = -1; |
| |
| priv->bspi_chip_select = (priv->bspi_hw && pdata) ? pdata->flash_cs : 0; |
| #ifdef BCHP_SUN_TOP_CTRL_STRAP_VALUE_0_strap_nand_flash_boot_MASK |
| if (BDEV_RD(BCHP_SUN_TOP_CTRL_STRAP_VALUE_0) & |
| BCHP_SUN_TOP_CTRL_STRAP_VALUE_0_strap_nand_flash_boot_MASK) { |
| /* BSPI is disabled if the board is strapped for NAND */ |
| priv->bspi_chip_select = 0; |
| } |
| #endif /* BCHP_SUN_TOP_CTRL_STRAP_VALUE_0_strap_nand_flash_boot_MASK */ |
| |
| |
| INIT_LIST_HEAD(&priv->msg_queue); |
| spin_lock_init(&priv->lock); |
| |
| platform_set_drvdata(pdev, priv); |
| |
| tasklet_init(&priv->tasklet, bcmspi_tasklet, (unsigned long)priv); |
| |
| ret = spi_register_master(master); |
| if (ret < 0) { |
| dev_err(&pdev->dev, "can't register master\n"); |
| goto err1; |
| } |
| if (!default_master) |
| default_master = master; |
| |
| /* default values - undefined */ |
| priv->flex_mode.width = |
| priv->flex_mode.addrlen = |
| priv->flex_mode.hp = -1; |
| |
| if (priv->bspi_chip_select) { |
| int quad_mode = bspi_width == BSPI_WIDTH_4BIT; |
| if (bcmspi_set_quad_mode(priv, quad_mode)) |
| bspi_width = BSPI_WIDTH_1BIT; |
| bcmspi_set_mode(priv, bspi_width, bspi_addrlen, bspi_hp); |
| } |
| |
| return 0; |
| |
| err1: |
| bcmspi_hw_uninit(priv); |
| free_irq(priv->irq, priv); |
| iounmap(priv->bspi_hw_raf); |
| err2_1: |
| iounmap(priv->bspi_hw); |
| err2: |
| iounmap(priv->mspi_hw); |
| err3: |
| spi_master_put(master); |
| return ret; |
| } |
| |
| static int bcmspi_remove(struct platform_device *pdev) |
| { |
| struct bcmspi_priv *priv = platform_get_drvdata(pdev); |
| unsigned long flags; |
| |
| /* acquire lock when the MSPI is idle */ |
| while (1) { |
| spin_lock_irqsave(&priv->lock, flags); |
| if (priv->state == STATE_IDLE) |
| break; |
| spin_unlock_irqrestore(&priv->lock, flags); |
| udelay(100); |
| } |
| priv->state = STATE_SHUTDOWN; |
| spin_unlock_irqrestore(&priv->lock, flags); |
| |
| tasklet_kill(&priv->tasklet); |
| platform_set_drvdata(pdev, NULL); |
| bcmspi_hw_uninit(priv); |
| if (priv->bspi_hw_raf) |
| iounmap(priv->bspi_hw_raf); |
| if (priv->bspi_hw) |
| iounmap((const volatile void __iomem *)priv->bspi_hw); |
| free_irq(priv->irq, priv); |
| iounmap((const volatile void __iomem *)priv->mspi_hw); |
| spi_unregister_master(priv->master); |
| |
| return 0; |
| } |
| |
| static int bcmspi_suspend(struct device *dev) |
| { |
| if (brcm_pm_deep_sleep()) { |
| struct bcmspi_priv *priv = dev_get_drvdata(dev); |
| priv->s3_intr2_mask = |
| BDEV_RD(BCHP_HIF_SPI_INTR2_CPU_MASK_STATUS); |
| } |
| return 0; |
| }; |
| |
| static int bcmspi_resume(struct device *dev) |
| { |
| if (brcm_pm_deep_sleep()) { |
| struct bcmspi_priv *priv = dev_get_drvdata(dev); |
| int curr_cs = priv->curr_cs; |
| BDEV_WR_RB(BCHP_HIF_SPI_INTR2_CPU_MASK_CLEAR, |
| ~priv->s3_intr2_mask); |
| bcmspi_hw_init(priv); |
| bcmspi_set_mode(priv, -1, -1, -1); |
| priv->curr_cs = -1; |
| bcmspi_set_chip_select(priv, curr_cs); |
| } |
| return 0; |
| } |
| |
| static const struct dev_pm_ops bcmspi_pm_ops = { |
| .suspend = bcmspi_suspend, |
| .resume = bcmspi_resume, |
| }; |
| |
| static struct platform_driver driver = { |
| .driver = { |
| .name = "spi_brcmstb", |
| .bus = &platform_bus_type, |
| .owner = THIS_MODULE, |
| .pm = &bcmspi_pm_ops, |
| }, |
| .probe = bcmspi_probe, |
| .remove = __devexit_p(bcmspi_remove), |
| }; |
| |
| static int __init bcmspi_spi_init(void) |
| { |
| platform_driver_register(&driver); |
| |
| return 0; |
| } |
| module_init(bcmspi_spi_init); |
| |
| static void __exit bcmspi_spi_exit(void) |
| { |
| platform_driver_unregister(&driver); |
| } |
| module_exit(bcmspi_spi_exit); |
| |
| MODULE_AUTHOR("Broadcom Corporation"); |
| MODULE_DESCRIPTION("MSPI/HIF SPI driver"); |
| MODULE_LICENSE("GPL"); |