diff options
Diffstat (limited to 'drivers')
79 files changed, 5344 insertions, 724 deletions
diff --git a/drivers/bios_emulator/x86emu/ops.c b/drivers/bios_emulator/x86emu/ops.c index c836a20..f8e093d 100644 --- a/drivers/bios_emulator/x86emu/ops.c +++ b/drivers/bios_emulator/x86emu/ops.c @@ -3518,11 +3518,9 @@ Handles opcode 0xcc ****************************************************************************/ void x86emuOp_int3(u8 X86EMU_UNUSED(op1)) { - u16 tmp; - START_OF_INSTR(); DECODE_PRINTF("INT 3\n"); - tmp = (u16) mem_access_word(3 * 4 + 2); + (void)mem_access_word(3 * 4 + 2); /* access the segment register */ TRACE_AND_STEP(); if (_X86EMU_intrTab[3]) { @@ -3546,14 +3544,13 @@ Handles opcode 0xcd ****************************************************************************/ void x86emuOp_int_IMM(u8 X86EMU_UNUSED(op1)) { - u16 tmp; u8 intnum; START_OF_INSTR(); DECODE_PRINTF("INT\t"); intnum = fetch_byte_imm(); DECODE_PRINTF2("%x\n", intnum); - tmp = mem_access_word(intnum * 4 + 2); + (void)mem_access_word(intnum * 4 + 2); TRACE_AND_STEP(); if (_X86EMU_intrTab[intnum]) { (*_X86EMU_intrTab[intnum])(intnum); @@ -3576,13 +3573,11 @@ Handles opcode 0xce ****************************************************************************/ void x86emuOp_into(u8 X86EMU_UNUSED(op1)) { - u16 tmp; - START_OF_INSTR(); DECODE_PRINTF("INTO\n"); TRACE_AND_STEP(); if (ACCESS_FLAG(F_OF)) { - tmp = mem_access_word(4 * 4 + 2); + (void)mem_access_word(4 * 4 + 2); if (_X86EMU_intrTab[4]) { (*_X86EMU_intrTab[4])(4); } else { @@ -3990,11 +3985,9 @@ Handles opcode 0xd5 ****************************************************************************/ void x86emuOp_aad(u8 X86EMU_UNUSED(op1)) { - u8 a; - START_OF_INSTR(); DECODE_PRINTF("AAD\n"); - a = fetch_byte_imm(); + (void)fetch_byte_imm(); TRACE_AND_STEP(); M.x86.R_AX = aad_word(M.x86.R_AX); DECODE_CLEAR_SEGOVR(); diff --git a/drivers/block/ahci.c b/drivers/block/ahci.c index 015b341..7b2ec50 100644 --- a/drivers/block/ahci.c +++ b/drivers/block/ahci.c @@ -561,12 +561,9 @@ static int ata_scsiop_inquiry(ccb *pccb) */ static int ata_scsiop_read10(ccb * pccb) { - u64 lba = 0; u32 len = 0; u8 fis[20]; - lba = (((u64) pccb->cmd[2]) << 24) | (((u64) pccb->cmd[3]) << 16) - | (((u64) pccb->cmd[4]) << 8) | ((u64) pccb->cmd[5]); len = (((u32) pccb->cmd[7]) << 8) | ((u32) pccb->cmd[8]); /* For 10-byte and 16-byte SCSI R/W commands, transfer diff --git a/drivers/block/sata_dwc.c b/drivers/block/sata_dwc.c index b2b3804..69ec5fd 100644 --- a/drivers/block/sata_dwc.c +++ b/drivers/block/sata_dwc.c @@ -440,11 +440,9 @@ static int sata_dwc_softreset(struct ata_port *ap) { u8 nsect,lbal = 0; u8 tmp = 0; - u32 serror = 0; - u8 status = 0; struct ata_ioports *ioaddr = &ap->ioaddr; - serror = in_le32((void *)ap->ioaddr.scr_addr + (SCR_ERROR * 4)); + in_le32((void *)ap->ioaddr.scr_addr + (SCR_ERROR * 4)); writeb(0x55, ioaddr->nsect_addr); writeb(0xaa, ioaddr->lbal_addr); @@ -476,7 +474,7 @@ static int sata_dwc_softreset(struct ata_port *ap) writeb(ap->ctl, ioaddr->ctl_addr); msleep(150); - status = ata_check_status(ap); + ata_check_status(ap); msleep(50); ata_check_status(ap); @@ -535,7 +533,6 @@ int scan_sata(int dev) const u16 *id; struct ata_device *ata_dev = &ata_device; unsigned long pio_mask, mwdma_mask, udma_mask; - unsigned long xfer_mask; char revbuf[7]; u16 iobuf[ATA_SECTOR_WORDS]; @@ -629,10 +626,6 @@ int scan_sata(int dev) if (id[ATA_ID_FIELD_VALID] & (1 << 2)) udma_mask = id[ATA_ID_UDMA_MODES] & 0xff; - xfer_mask = ((pio_mask << ATA_SHIFT_PIO) & ATA_MASK_PIO) | - ((mwdma_mask << ATA_SHIFT_MWDMA) & ATA_MASK_MWDMA) | - ((udma_mask << ATA_SHIFT_UDMA) & ATA_MASK_UDMA); - if (ata_dev->class == ATA_DEV_ATA) { if (ata_id_is_cfa(id)) { if (id[162] & 1) @@ -651,14 +644,11 @@ int scan_sata(int dev) ata_dev->multi_count = ata_dev->id[59] & 0xff; if (ata_id_has_lba(id)) { - const char *lba_desc; char ncq_desc[20]; - lba_desc = "LBA"; ata_dev->flags |= ATA_DFLAG_LBA; if (ata_id_has_lba48(id)) { ata_dev->flags |= ATA_DFLAG_LBA48; - lba_desc = "LBA48"; if (ata_dev->n_sectors >= (1UL << 28) && ata_id_has_flush_ext(id)) @@ -890,6 +880,7 @@ retry: return 0; err_out: + printf("failed to READ ID (%s, err_mask=0x%x)\n", reason, err_mask); return rc; } @@ -1807,7 +1798,6 @@ static int ata_dev_read_sectors(unsigned char *pdata, unsigned long datalen, unsigned int err_mask = 0; const char *reason; int may_fallback = 1; - int rc; if (dev_state == SATA_ERROR) return FALSE; @@ -1904,19 +1894,10 @@ retry: return -ENOENT; } - rc = -EIO; reason = "I/O error"; goto err_out; } - /* Falling back doesn't make sense if ID data was read - * successfully at least once. - */ - may_fallback = 0; - - rc = -EINVAL; - reason = "device reports invalid type"; - return TRUE; err_out: @@ -1991,7 +1972,6 @@ static int ata_dev_write_sectors(unsigned char* pdata, unsigned long datalen, unsigned int err_mask = 0; const char *reason; int may_fallback = 1; - int rc; if (dev_state == SATA_ERROR) return FALSE; @@ -2089,19 +2069,10 @@ retry: return -ENOENT; } - rc = -EIO; reason = "I/O error"; goto err_out; } - /* Falling back doesn't make sense if ID data was read - * successfully at least once. - */ - may_fallback = 0; - - rc = -EINVAL; - reason = "device reports invalid type"; - return TRUE; err_out: diff --git a/drivers/block/sata_sil3114.c b/drivers/block/sata_sil3114.c index 1e60636..34fe038 100644 --- a/drivers/block/sata_sil3114.c +++ b/drivers/block/sata_sil3114.c @@ -782,6 +782,7 @@ int scan_sata (int dev) (iobase[5] + VND_TF2_CH0) | ATA_PCI_CTL_OFS; port[0].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH0; break; +#if (CONFIG_SYS_SATA_MAX_DEVICE >= 1) case 1: port[1].port_no = 0; port[1].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH1; @@ -789,6 +790,7 @@ int scan_sata (int dev) (iobase[5] + VND_TF2_CH1) | ATA_PCI_CTL_OFS; port[1].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH1; break; +#elif (CONFIG_SYS_SATA_MAX_DEVICE >= 2) case 2: port[2].port_no = 0; port[2].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH2; @@ -796,6 +798,7 @@ int scan_sata (int dev) (iobase[5] + VND_TF2_CH2) | ATA_PCI_CTL_OFS; port[2].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH2; break; +#elif (CONFIG_SYS_SATA_MAX_DEVICE >= 3) case 3: port[3].port_no = 0; port[3].ioaddr.cmd_addr = iobase[5] + VND_TF0_CH3; @@ -803,6 +806,7 @@ int scan_sata (int dev) (iobase[5] + VND_TF2_CH3) | ATA_PCI_CTL_OFS; port[3].ioaddr.bmdma_addr = iobase[5] + VND_BMDMA_CH3; break; +#endif default: printf ("Tried to scan unknown port: ata%d\n", dev); return 1; diff --git a/drivers/block/sym53c8xx.c b/drivers/block/sym53c8xx.c index 8094b41..564aa98 100644 --- a/drivers/block/sym53c8xx.c +++ b/drivers/block/sym53c8xx.c @@ -453,11 +453,9 @@ void scsi_int_enable(void) void scsi_write_dsp(unsigned long start) { - unsigned long val; #ifdef SCSI_SINGLE_STEP unsigned char t; #endif - val = start; out32r(scsi_mem_addr + DSP,start); #ifdef SCSI_SINGLE_STEP t=scsi_read_byte(DCNTL); diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 3d9c9f1..5d864b5 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk LIB := $(obj)libdma.o COBJS-$(CONFIG_FSLDMAFEC) += MCD_tasksInit.o MCD_dmaApi.o MCD_tasks.o +COBJS-$(CONFIG_APBH_DMA) += apbh_dma.o COBJS-$(CONFIG_FSL_DMA) += fsl_dma.o COBJS-$(CONFIG_OMAP3_DMA) += omap3_dma.o diff --git a/drivers/dma/apbh_dma.c b/drivers/dma/apbh_dma.c new file mode 100644 index 0000000..69a1042 --- /dev/null +++ b/drivers/dma/apbh_dma.c @@ -0,0 +1,691 @@ +/* + * Freescale i.MX28 APBH DMA driver + * + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * on behalf of DENX Software Engineering GmbH + * + * Based on code from LTIB: + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/list.h> + +#include <common.h> +#include <malloc.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/dma.h> + +static struct mxs_dma_chan mxs_dma_channels[MXS_MAX_DMA_CHANNELS]; + +/* + * Test is the DMA channel is valid channel + */ +int mxs_dma_validate_chan(int channel) +{ + struct mxs_dma_chan *pchan; + + if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS)) + return -EINVAL; + + pchan = mxs_dma_channels + channel; + if (!(pchan->flags & MXS_DMA_FLAGS_ALLOCATED)) + return -EINVAL; + + return 0; +} + +/* + * Enable a DMA channel. + * + * If the given channel has any DMA descriptors on its active list, this + * function causes the DMA hardware to begin processing them. + * + * This function marks the DMA channel as "busy," whether or not there are any + * descriptors to process. + */ +int mxs_dma_enable(int channel) +{ + struct mx28_apbh_regs *apbh_regs = + (struct mx28_apbh_regs *)MXS_APBH_BASE; + unsigned int sem; + struct mxs_dma_chan *pchan; + struct mxs_dma_desc *pdesc; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + pchan = mxs_dma_channels + channel; + + if (pchan->pending_num == 0) { + pchan->flags |= MXS_DMA_FLAGS_BUSY; + return 0; + } + + pdesc = list_first_entry(&pchan->active, struct mxs_dma_desc, node); + if (pdesc == NULL) + return -EFAULT; + + if (pchan->flags & MXS_DMA_FLAGS_BUSY) { + if (!(pdesc->cmd.data & MXS_DMA_DESC_CHAIN)) + return 0; + + sem = mxs_dma_read_semaphore(channel); + if (sem == 0) + return 0; + + if (sem == 1) { + pdesc = list_entry(pdesc->node.next, + struct mxs_dma_desc, node); + writel(mxs_dma_cmd_address(pdesc), + &apbh_regs->ch[channel].hw_apbh_ch_nxtcmdar); + } + writel(pchan->pending_num, + &apbh_regs->ch[channel].hw_apbh_ch_sema); + pchan->active_num += pchan->pending_num; + pchan->pending_num = 0; + } else { + pchan->active_num += pchan->pending_num; + pchan->pending_num = 0; + writel(mxs_dma_cmd_address(pdesc), + &apbh_regs->ch[channel].hw_apbh_ch_nxtcmdar); + writel(pchan->active_num, + &apbh_regs->ch[channel].hw_apbh_ch_sema); + writel(1 << (channel + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET), + &apbh_regs->hw_apbh_ctrl0_clr); + } + + pchan->flags |= MXS_DMA_FLAGS_BUSY; + return 0; +} + +/* + * Disable a DMA channel. + * + * This function shuts down a DMA channel and marks it as "not busy." Any + * descriptors on the active list are immediately moved to the head of the + * "done" list, whether or not they have actually been processed by the + * hardware. The "ready" flags of these descriptors are NOT cleared, so they + * still appear to be active. + * + * This function immediately shuts down a DMA channel's hardware, aborting any + * I/O that may be in progress, potentially leaving I/O hardware in an undefined + * state. It is unwise to call this function if there is ANY chance the hardware + * is still processing a command. + */ +int mxs_dma_disable(int channel) +{ + struct mxs_dma_chan *pchan; + struct mx28_apbh_regs *apbh_regs = + (struct mx28_apbh_regs *)MXS_APBH_BASE; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + pchan = mxs_dma_channels + channel; + + if (!(pchan->flags & MXS_DMA_FLAGS_BUSY)) + return -EINVAL; + + writel(1 << (channel + APBH_CTRL0_CLKGATE_CHANNEL_OFFSET), + &apbh_regs->hw_apbh_ctrl0_set); + + pchan->flags &= ~MXS_DMA_FLAGS_BUSY; + pchan->active_num = 0; + pchan->pending_num = 0; + list_splice_init(&pchan->active, &pchan->done); + + return 0; +} + +/* + * Resets the DMA channel hardware. + */ +int mxs_dma_reset(int channel) +{ + struct mx28_apbh_regs *apbh_regs = + (struct mx28_apbh_regs *)MXS_APBH_BASE; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + writel(1 << (channel + APBH_CHANNEL_CTRL_RESET_CHANNEL_OFFSET), + &apbh_regs->hw_apbh_channel_ctrl_set); + + return 0; +} + +/* + * Freeze a DMA channel. + * + * This function causes the channel to continuously fail arbitration for bus + * access, which halts all forward progress without losing any state. A call to + * mxs_dma_unfreeze() will cause the channel to continue its current operation + * with no ill effect. + */ +int mxs_dma_freeze(int channel) +{ + struct mx28_apbh_regs *apbh_regs = + (struct mx28_apbh_regs *)MXS_APBH_BASE; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + writel(1 << (channel + APBH_CHANNEL_CTRL_FREEZE_CHANNEL_OFFSET), + &apbh_regs->hw_apbh_channel_ctrl_set); + + return 0; +} + +/* + * Unfreeze a DMA channel. + * + * This function reverses the effect of mxs_dma_freeze(), enabling the DMA + * channel to continue from where it was frozen. + */ +int mxs_dma_unfreeze(int channel) +{ + struct mx28_apbh_regs *apbh_regs = + (struct mx28_apbh_regs *)MXS_APBH_BASE; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + writel(1 << (channel + APBH_CHANNEL_CTRL_FREEZE_CHANNEL_OFFSET), + &apbh_regs->hw_apbh_channel_ctrl_clr); + + return 0; +} + +/* + * Read a DMA channel's hardware semaphore. + * + * As used by the MXS platform's DMA software, the DMA channel's hardware + * semaphore reflects the number of DMA commands the hardware will process, but + * has not yet finished. This is a volatile value read directly from hardware, + * so it must be be viewed as immediately stale. + * + * If the channel is not marked busy, or has finished processing all its + * commands, this value should be zero. + * + * See mxs_dma_append() for details on how DMA command blocks must be configured + * to maintain the expected behavior of the semaphore's value. + */ +int mxs_dma_read_semaphore(int channel) +{ + struct mx28_apbh_regs *apbh_regs = + (struct mx28_apbh_regs *)MXS_APBH_BASE; + uint32_t tmp; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + tmp = readl(&apbh_regs->ch[channel].hw_apbh_ch_sema); + + tmp &= APBH_CHn_SEMA_PHORE_MASK; + tmp >>= APBH_CHn_SEMA_PHORE_OFFSET; + + return tmp; +} + +/* + * Enable or disable DMA interrupt. + * + * This function enables the given DMA channel to interrupt the CPU. + */ +int mxs_dma_enable_irq(int channel, int enable) +{ + struct mx28_apbh_regs *apbh_regs = + (struct mx28_apbh_regs *)MXS_APBH_BASE; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + if (enable) + writel(1 << (channel + APBH_CTRL1_CH_CMDCMPLT_IRQ_EN_OFFSET), + &apbh_regs->hw_apbh_ctrl1_set); + else + writel(1 << (channel + APBH_CTRL1_CH_CMDCMPLT_IRQ_EN_OFFSET), + &apbh_regs->hw_apbh_ctrl1_clr); + + return 0; +} + +/* + * Check if a DMA interrupt is pending. + */ +int mxs_dma_irq_is_pending(int channel) +{ + struct mx28_apbh_regs *apbh_regs = + (struct mx28_apbh_regs *)MXS_APBH_BASE; + uint32_t tmp; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + tmp = readl(&apbh_regs->hw_apbh_ctrl1); + tmp |= readl(&apbh_regs->hw_apbh_ctrl2); + + return (tmp >> channel) & 1; +} + +/* + * Clear DMA interrupt. + * + * The software that is using the DMA channel must register to receive its + * interrupts and, when they arrive, must call this function to clear them. + */ +int mxs_dma_ack_irq(int channel) +{ + struct mx28_apbh_regs *apbh_regs = + (struct mx28_apbh_regs *)MXS_APBH_BASE; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + writel(1 << channel, &apbh_regs->hw_apbh_ctrl1_clr); + writel(1 << channel, &apbh_regs->hw_apbh_ctrl2_clr); + + return 0; +} + +/* + * Request to reserve a DMA channel + */ +int mxs_dma_request(int channel) +{ + struct mxs_dma_chan *pchan; + + if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS)) + return -EINVAL; + + pchan = mxs_dma_channels + channel; + if ((pchan->flags & MXS_DMA_FLAGS_VALID) != MXS_DMA_FLAGS_VALID) + return -ENODEV; + + if (pchan->flags & MXS_DMA_FLAGS_ALLOCATED) + return -EBUSY; + + pchan->flags |= MXS_DMA_FLAGS_ALLOCATED; + pchan->active_num = 0; + pchan->pending_num = 0; + + INIT_LIST_HEAD(&pchan->active); + INIT_LIST_HEAD(&pchan->done); + + return 0; +} + +/* + * Release a DMA channel. + * + * This function releases a DMA channel from its current owner. + * + * The channel will NOT be released if it's marked "busy" (see + * mxs_dma_enable()). + */ +int mxs_dma_release(int channel) +{ + struct mxs_dma_chan *pchan; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + pchan = mxs_dma_channels + channel; + + if (pchan->flags & MXS_DMA_FLAGS_BUSY) + return -EBUSY; + + pchan->dev = 0; + pchan->active_num = 0; + pchan->pending_num = 0; + pchan->flags &= ~MXS_DMA_FLAGS_ALLOCATED; + + return 0; +} + +/* + * Allocate DMA descriptor + */ +struct mxs_dma_desc *mxs_dma_desc_alloc(void) +{ + struct mxs_dma_desc *pdesc; + + pdesc = memalign(MXS_DMA_ALIGNMENT, sizeof(struct mxs_dma_desc)); + + if (pdesc == NULL) + return NULL; + + memset(pdesc, 0, sizeof(*pdesc)); + pdesc->address = (dma_addr_t)pdesc; + + return pdesc; +}; + +/* + * Free DMA descriptor + */ +void mxs_dma_desc_free(struct mxs_dma_desc *pdesc) +{ + if (pdesc == NULL) + return; + + free(pdesc); +} + +/* + * Return the address of the command within a descriptor. + */ +unsigned int mxs_dma_cmd_address(struct mxs_dma_desc *desc) +{ + return desc->address + offsetof(struct mxs_dma_desc, cmd); +} + +/* + * Check if descriptor is on a channel's active list. + * + * This function returns the state of a descriptor's "ready" flag. This flag is + * usually set only if the descriptor appears on a channel's active list. The + * descriptor may or may not have already been processed by the hardware. + * + * The "ready" flag is set when the descriptor is submitted to a channel by a + * call to mxs_dma_append() or mxs_dma_append_list(). The "ready" flag is + * cleared when a processed descriptor is moved off the active list by a call + * to mxs_dma_finish(). The "ready" flag is NOT cleared if the descriptor is + * aborted by a call to mxs_dma_disable(). + */ +int mxs_dma_desc_pending(struct mxs_dma_desc *pdesc) +{ + return pdesc->flags & MXS_DMA_DESC_READY; +} + +/* + * Add a DMA descriptor to a channel. + * + * If the descriptor list for this channel is not empty, this function sets the + * CHAIN bit and the NEXTCMD_ADDR fields in the last descriptor's DMA command so + * it will chain to the new descriptor's command. + * + * Then, this function marks the new descriptor as "ready," adds it to the end + * of the active descriptor list, and increments the count of pending + * descriptors. + * + * The MXS platform DMA software imposes some rules on DMA commands to maintain + * important invariants. These rules are NOT checked, but they must be carefully + * applied by software that uses MXS DMA channels. + * + * Invariant: + * The DMA channel's hardware semaphore must reflect the number of DMA + * commands the hardware will process, but has not yet finished. + * + * Explanation: + * A DMA channel begins processing commands when its hardware semaphore is + * written with a value greater than zero, and it stops processing commands + * when the semaphore returns to zero. + * + * When a channel finishes a DMA command, it will decrement its semaphore if + * the DECREMENT_SEMAPHORE bit is set in that command's flags bits. + * + * In principle, it's not necessary for the DECREMENT_SEMAPHORE to be set, + * unless it suits the purposes of the software. For example, one could + * construct a series of five DMA commands, with the DECREMENT_SEMAPHORE + * bit set only in the last one. Then, setting the DMA channel's hardware + * semaphore to one would cause the entire series of five commands to be + * processed. However, this example would violate the invariant given above. + * + * Rule: + * ALL DMA commands MUST have the DECREMENT_SEMAPHORE bit set so that the DMA + * channel's hardware semaphore will be decremented EVERY time a command is + * processed. + */ +int mxs_dma_desc_append(int channel, struct mxs_dma_desc *pdesc) +{ + struct mxs_dma_chan *pchan; + struct mxs_dma_desc *last; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + pchan = mxs_dma_channels + channel; + + pdesc->cmd.next = mxs_dma_cmd_address(pdesc); + pdesc->flags |= MXS_DMA_DESC_FIRST | MXS_DMA_DESC_LAST; + + if (!list_empty(&pchan->active)) { + last = list_entry(pchan->active.prev, struct mxs_dma_desc, + node); + + pdesc->flags &= ~MXS_DMA_DESC_FIRST; + last->flags &= ~MXS_DMA_DESC_LAST; + + last->cmd.next = mxs_dma_cmd_address(pdesc); + last->cmd.data |= MXS_DMA_DESC_CHAIN; + } + pdesc->flags |= MXS_DMA_DESC_READY; + if (pdesc->flags & MXS_DMA_DESC_FIRST) + pchan->pending_num++; + list_add_tail(&pdesc->node, &pchan->active); + + return ret; +} + +/* + * Retrieve processed DMA descriptors. + * + * This function moves all the descriptors from the DMA channel's "done" list to + * the head of the given list. + */ +int mxs_dma_get_finished(int channel, struct list_head *head) +{ + struct mxs_dma_chan *pchan; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + if (head == NULL) + return 0; + + pchan = mxs_dma_channels + channel; + + list_splice(&pchan->done, head); + + return 0; +} + +/* + * Clean up processed DMA descriptors. + * + * This function removes processed DMA descriptors from the "active" list. Pass + * in a non-NULL list head to get the descriptors moved to your list. Pass NULL + * to get the descriptors moved to the channel's "done" list. Descriptors on + * the "done" list can be retrieved with mxs_dma_get_finished(). + * + * This function marks the DMA channel as "not busy" if no unprocessed + * descriptors remain on the "active" list. + */ +int mxs_dma_finish(int channel, struct list_head *head) +{ + int sem; + struct mxs_dma_chan *pchan; + struct list_head *p, *q; + struct mxs_dma_desc *pdesc; + int ret; + + ret = mxs_dma_validate_chan(channel); + if (ret) + return ret; + + pchan = mxs_dma_channels + channel; + + sem = mxs_dma_read_semaphore(channel); + if (sem < 0) + return sem; + + if (sem == pchan->active_num) + return 0; + + list_for_each_safe(p, q, &pchan->active) { + if ((pchan->active_num) <= sem) + break; + + pdesc = list_entry(p, struct mxs_dma_desc, node); + pdesc->flags &= ~MXS_DMA_DESC_READY; + + if (head) + list_move_tail(p, head); + else + list_move_tail(p, &pchan->done); + + if (pdesc->flags & MXS_DMA_DESC_LAST) + pchan->active_num--; + } + + if (sem == 0) + pchan->flags &= ~MXS_DMA_FLAGS_BUSY; + + return 0; +} + +/* + * Wait for DMA channel to complete + */ +int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan) +{ + struct mx28_apbh_regs *apbh_regs = + (struct mx28_apbh_regs *)MXS_APBH_BASE; + int ret; + + ret = mxs_dma_validate_chan(chan); + if (ret) + return ret; + + if (mx28_wait_mask_set(&apbh_regs->hw_apbh_ctrl1_reg, + 1 << chan, timeout)) { + ret = -ETIMEDOUT; + mxs_dma_reset(chan); + } + + return 0; +} + +/* + * Execute the DMA channel + */ +int mxs_dma_go(int chan) +{ + uint32_t timeout = 10000; + int ret; + + LIST_HEAD(tmp_desc_list); + + mxs_dma_enable_irq(chan, 1); + mxs_dma_enable(chan); + + /* Wait for DMA to finish. */ + ret = mxs_dma_wait_complete(timeout, chan); + + /* Clear out the descriptors we just ran. */ + mxs_dma_finish(chan, &tmp_desc_list); + + /* Shut the DMA channel down. */ + mxs_dma_ack_irq(chan); + mxs_dma_reset(chan); + mxs_dma_enable_irq(chan, 0); + mxs_dma_disable(chan); + + return ret; +} + +/* + * Initialize the DMA hardware + */ +int mxs_dma_init(void) +{ + struct mx28_apbh_regs *apbh_regs = + (struct mx28_apbh_regs *)MXS_APBH_BASE; + struct mxs_dma_chan *pchan; + int ret, channel; + + mx28_reset_block(&apbh_regs->hw_apbh_ctrl0_reg); + +#ifdef CONFIG_APBH_DMA_BURST8 + writel(APBH_CTRL0_AHB_BURST8_EN, + &apbh_regs->hw_apbh_ctrl0_set); +#else + writel(APBH_CTRL0_AHB_BURST8_EN, + &apbh_regs->hw_apbh_ctrl0_clr); +#endif + +#ifdef CONFIG_APBH_DMA_BURST + writel(APBH_CTRL0_APB_BURST_EN, + &apbh_regs->hw_apbh_ctrl0_set); +#else + writel(APBH_CTRL0_APB_BURST_EN, + &apbh_regs->hw_apbh_ctrl0_clr); +#endif + + for (channel = 0; channel < MXS_MAX_DMA_CHANNELS; channel++) { + pchan = mxs_dma_channels + channel; + pchan->flags = MXS_DMA_FLAGS_VALID; + + ret = mxs_dma_request(channel); + + if (ret) { + printf("MXS DMA: Can't acquire DMA channel %i\n", + channel); + + goto err; + } + + mxs_dma_reset(channel); + mxs_dma_ack_irq(channel); + } + + return 0; + +err: + while (--channel >= 0) + mxs_dma_release(channel); + return ret; +} diff --git a/drivers/fpga/ivm_core.c b/drivers/fpga/ivm_core.c index 2b5a485..b5a47d1 100755 --- a/drivers/fpga/ivm_core.c +++ b/drivers/fpga/ivm_core.c @@ -2102,7 +2102,6 @@ signed char ispVMLCOUNT(unsigned short a_usCountSize) unsigned char ucState = 0; unsigned short usDelay = 0; unsigned short usToggle = 0; - unsigned char usByte = 0; g_usIntelBufferSize = (unsigned short)ispVMDataSize(); @@ -2171,7 +2170,6 @@ signed char ispVMLCOUNT(unsigned short a_usCountSize) ucState = 0; usDelay = 0; usToggle = 0; - usByte = 0; usContinue = 1; /* diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b5264d1..e22c096 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -30,6 +30,7 @@ COBJS-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o COBJS-$(CONFIG_MARVELL_GPIO) += mvgpio.o COBJS-$(CONFIG_MARVELL_MFP) += mvmfp.o COBJS-$(CONFIG_MXC_GPIO) += mxc_gpio.o +COBJS-$(CONFIG_MXS_GPIO) += mxs_gpio.o COBJS-$(CONFIG_PCA953X) += pca953x.o COBJS-$(CONFIG_PCA9698) += pca9698.o COBJS-$(CONFIG_S5P) += s5p_gpio.o diff --git a/drivers/gpio/mxs_gpio.c b/drivers/gpio/mxs_gpio.c new file mode 100644 index 0000000..b7e9591 --- /dev/null +++ b/drivers/gpio/mxs_gpio.c @@ -0,0 +1,136 @@ +/* + * Freescale i.MX28 GPIO control code + * + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * on behalf of DENX Software Engineering GmbH + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <netdev.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/iomux.h> +#include <asm/arch/imx-regs.h> + +#if defined(CONFIG_MX23) +#define PINCTRL_BANKS 3 +#define PINCTRL_DOUT(n) (0x0500 + ((n) * 0x10)) +#define PINCTRL_DIN(n) (0x0600 + ((n) * 0x10)) +#define PINCTRL_DOE(n) (0x0700 + ((n) * 0x10)) +#define PINCTRL_PIN2IRQ(n) (0x0800 + ((n) * 0x10)) +#define PINCTRL_IRQEN(n) (0x0900 + ((n) * 0x10)) +#define PINCTRL_IRQSTAT(n) (0x0c00 + ((n) * 0x10)) +#elif defined(CONFIG_MX28) +#define PINCTRL_BANKS 5 +#define PINCTRL_DOUT(n) (0x0700 + ((n) * 0x10)) +#define PINCTRL_DIN(n) (0x0900 + ((n) * 0x10)) +#define PINCTRL_DOE(n) (0x0b00 + ((n) * 0x10)) +#define PINCTRL_PIN2IRQ(n) (0x1000 + ((n) * 0x10)) +#define PINCTRL_IRQEN(n) (0x1100 + ((n) * 0x10)) +#define PINCTRL_IRQSTAT(n) (0x1400 + ((n) * 0x10)) +#else +#error "Please select CONFIG_MX23 or CONFIG_MX28" +#endif + +#define GPIO_INT_FALL_EDGE 0x0 +#define GPIO_INT_LOW_LEV 0x1 +#define GPIO_INT_RISE_EDGE 0x2 +#define GPIO_INT_HIGH_LEV 0x3 +#define GPIO_INT_LEV_MASK (1 << 0) +#define GPIO_INT_POL_MASK (1 << 1) + +void mxs_gpio_init(void) +{ + int i; + + for (i = 0; i < PINCTRL_BANKS; i++) { + writel(0, MXS_PINCTRL_BASE + PINCTRL_PIN2IRQ(i)); + writel(0, MXS_PINCTRL_BASE + PINCTRL_IRQEN(i)); + /* Use SCT address here to clear the IRQSTAT bits */ + writel(0xffffffff, MXS_PINCTRL_BASE + PINCTRL_IRQSTAT(i) + 8); + } +} + +int gpio_get_value(int gp) +{ + uint32_t bank = PAD_BANK(gp); + uint32_t offset = PINCTRL_DIN(bank); + struct mx28_register *reg = + (struct mx28_register *)(MXS_PINCTRL_BASE + offset); + + return (readl(®->reg) >> PAD_PIN(gp)) & 1; +} + +void gpio_set_value(int gp, int value) +{ + uint32_t bank = PAD_BANK(gp); + uint32_t offset = PINCTRL_DOUT(bank); + struct mx28_register *reg = + (struct mx28_register *)(MXS_PINCTRL_BASE + offset); + + if (value) + writel(1 << PAD_PIN(gp), ®->reg_set); + else + writel(1 << PAD_PIN(gp), ®->reg_clr); +} + +int gpio_direction_input(int gp) +{ + uint32_t bank = PAD_BANK(gp); + uint32_t offset = PINCTRL_DOE(bank); + struct mx28_register *reg = + (struct mx28_register *)(MXS_PINCTRL_BASE + offset); + + writel(1 << PAD_PIN(gp), ®->reg_clr); + + return 0; +} + +int gpio_direction_output(int gp, int value) +{ + uint32_t bank = PAD_BANK(gp); + uint32_t offset = PINCTRL_DOE(bank); + struct mx28_register *reg = + (struct mx28_register *)(MXS_PINCTRL_BASE + offset); + + writel(1 << PAD_PIN(gp), ®->reg_set); + + gpio_set_value(gp, value); + + return 0; +} + +int gpio_request(int gp, const char *label) +{ + if (PAD_BANK(gp) > PINCTRL_BANKS) + return -EINVAL; + + return 0; +} + +void gpio_free(int gp) +{ +} + +void gpio_toggle_value(int gp) +{ + gpio_set_value(gp, !gpio_get_value(gp)); +} diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index a48047a..2fb521e 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -31,6 +31,7 @@ COBJS-$(CONFIG_FSL_I2C) += fsl_i2c.o COBJS-$(CONFIG_I2C_MVTWSI) += mvtwsi.o COBJS-$(CONFIG_I2C_MV) += mv_i2c.o COBJS-$(CONFIG_I2C_MXC) += mxc_i2c.o +COBJS-$(CONFIG_I2C_MXS) += mxs_i2c.o COBJS-$(CONFIG_DRIVER_OMAP1510_I2C) += omap1510_i2c.o COBJS-$(CONFIG_DRIVER_OMAP24XX_I2C) += omap24xx_i2c.o COBJS-$(CONFIG_DRIVER_OMAP34XX_I2C) += omap24xx_i2c.o diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c index cb13dee..5b017a9 100644 --- a/drivers/i2c/fsl_i2c.c +++ b/drivers/i2c/fsl_i2c.c @@ -214,11 +214,20 @@ static unsigned int set_i2c_bus_speed(const struct fsl_i2c *dev, return speed; } +unsigned int get_i2c_clock(int bus) +{ + if (bus) + return gd->i2c2_clk; /* I2C2 clock */ + else + return gd->i2c1_clk; /* I2C1 clock */ +} + void i2c_init(int speed, int slaveadd) { - struct fsl_i2c *dev; + const struct fsl_i2c *dev; unsigned int temp; + int bus_num, i; #ifdef CONFIG_SYS_I2C_INIT_BOARD /* Call board specific i2c bus reset routine before accessing the @@ -227,29 +236,23 @@ i2c_init(int speed, int slaveadd) */ i2c_init_board(); #endif - dev = (struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C_OFFSET); - - writeb(0, &dev->cr); /* stop I2C controller */ - udelay(5); /* let it shutdown in peace */ - temp = set_i2c_bus_speed(dev, gd->i2c1_clk, speed); - if (gd->flags & GD_FLG_RELOC) - i2c_bus_speed[0] = temp; - writeb(slaveadd << 1, &dev->adr); /* write slave address */ - writeb(0x0, &dev->sr); /* clear status register */ - writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */ - -#ifdef CONFIG_SYS_I2C2_OFFSET - dev = (struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C2_OFFSET); - - writeb(0, &dev->cr); /* stop I2C controller */ - udelay(5); /* let it shutdown in peace */ - temp = set_i2c_bus_speed(dev, gd->i2c2_clk, speed); - if (gd->flags & GD_FLG_RELOC) - i2c_bus_speed[1] = temp; - writeb(slaveadd << 1, &dev->adr); /* write slave address */ - writeb(0x0, &dev->sr); /* clear status register */ - writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */ +#ifdef CONFIG_SYS_I2C2_OFFSET + bus_num = 2; +#else + bus_num = 1; #endif + for (i = 0; i < bus_num; i++) { + dev = i2c_dev[i]; + + writeb(0, &dev->cr); /* stop I2C controller */ + udelay(5); /* let it shutdown in peace */ + temp = set_i2c_bus_speed(dev, get_i2c_clock(i), speed); + if (gd->flags & GD_FLG_RELOC) + i2c_bus_speed[i] = temp; + writeb(slaveadd << 1, &dev->adr);/* write slave address */ + writeb(0x0, &dev->sr); /* clear status register */ + writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */ + } #ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT /* Call board specific i2c bus reset routine AFTER the bus has been diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 2869d7c..c88ac7c 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -37,6 +37,7 @@ #include <asm/arch/clock.h> #include <asm/arch/imx-regs.h> +#include <i2c.h> struct mxc_i2c_regs { uint32_t iadr; @@ -73,6 +74,10 @@ struct mxc_i2c_regs { #define I2C_BASE I2C2_BASE_ADDR #elif defined(CONFIG_SYS_I2C_MX35_PORT1) #define I2C_BASE I2C_BASE_ADDR +#elif defined(CONFIG_SYS_I2C_MX35_PORT2) +#define I2C_BASE I2C2_BASE_ADDR +#elif defined(CONFIG_SYS_I2C_MX35_PORT3) +#define I2C_BASE I2C3_BASE_ADDR #else #error "define CONFIG_SYS_I2C_MX<Processor>_PORTx to use the mx I2C driver" #endif @@ -95,16 +100,14 @@ static u16 i2c_clk_div[50][2] = { { 3072, 0x1E }, { 3840, 0x1F } }; -static u8 clk_div; - /* * Calculate and set proper clock divider */ -static void i2c_imx_set_clk(unsigned int rate) +static uint8_t i2c_imx_get_clk(unsigned int rate) { - struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; unsigned int i2c_clk_rate; unsigned int div; + u8 clk_div; #if defined(CONFIG_MX31) struct clock_control_regs *sc_regs = @@ -127,7 +130,7 @@ static void i2c_imx_set_clk(unsigned int rate) ; /* Store divider value */ - writeb(i2c_clk_div[clk_div][1], &i2c_regs->ifdr); + return clk_div; } /* @@ -146,7 +149,13 @@ void i2c_reset(void) */ void i2c_init(int speed, int unused) { - i2c_imx_set_clk(speed); + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + u8 clk_idx = i2c_imx_get_clk(speed); + u8 idx = i2c_clk_div[clk_idx][1]; + + /* Store divider value */ + writeb(idx, &i2c_regs->ifdr); + i2c_reset(); } @@ -164,6 +173,13 @@ int i2c_set_bus_speed(unsigned int speed) */ unsigned int i2c_get_bus_speed(void) { + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; + u8 clk_idx = readb(&i2c_regs->ifdr); + u8 clk_div; + + for (clk_div = 0; i2c_clk_div[clk_div][1] != clk_idx; clk_div++) + ; + return mxc_get_clock(MXC_IPG_PERCLK) / i2c_clk_div[clk_div][0]; } @@ -232,8 +248,12 @@ int i2c_imx_start(void) struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; unsigned int temp = 0; int result; + int speed = i2c_get_bus_speed(); + u8 clk_idx = i2c_imx_get_clk(speed); + u8 idx = i2c_clk_div[clk_idx][1]; - writeb(i2c_clk_div[clk_div][1], &i2c_regs->ifdr); + /* Store divider value */ + writeb(idx, &i2c_regs->ifdr); /* Enable I2C controller */ writeb(0, &i2c_regs->i2sr); @@ -306,11 +326,10 @@ int i2c_imx_set_chip_addr(uchar chip, int read) int i2c_imx_set_reg_addr(uint addr, int alen) { struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE; - int ret; - int i; + int ret = 0; - for (i = 0; i < (8 * alen); i += 8) { - writeb((addr >> i) & 0xff, &i2c_regs->i2dr); + while (alen--) { + writeb((addr >> (alen * 8)) & 0xff, &i2c_regs->i2dr); ret = i2c_imx_trx_complete(); if (ret) diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c new file mode 100644 index 0000000..c8fea32 --- /dev/null +++ b/drivers/i2c/mxs_i2c.c @@ -0,0 +1,246 @@ +/* + * Freescale i.MX28 I2C Driver + * + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * on behalf of DENX Software Engineering GmbH + * + * Partly based on Linux kernel i2c-mxs.c driver: + * Copyright (C) 2011 Wolfram Sang, Pengutronix e.K. + * + * Which was based on a (non-working) driver which was: + * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <common.h> +#include <malloc.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> + +#define MXS_I2C_MAX_TIMEOUT 1000000 + +void mxs_i2c_reset(void) +{ + struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE; + int ret; + + ret = mx28_reset_block(&i2c_regs->hw_i2c_ctrl0_reg); + if (ret) { + debug("MXS I2C: Block reset timeout\n"); + return; + } + + writel(I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ | I2C_CTRL1_NO_SLAVE_ACK_IRQ | + I2C_CTRL1_EARLY_TERM_IRQ | I2C_CTRL1_MASTER_LOSS_IRQ | + I2C_CTRL1_SLAVE_STOP_IRQ | I2C_CTRL1_SLAVE_IRQ, + &i2c_regs->hw_i2c_ctrl1_clr); + + writel(I2C_QUEUECTRL_PIO_QUEUE_MODE, &i2c_regs->hw_i2c_queuectrl_set); +} + +void mxs_i2c_setup_read(uint8_t chip, int len) +{ + struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE; + + writel(I2C_QUEUECMD_RETAIN_CLOCK | I2C_QUEUECMD_PRE_SEND_START | + I2C_QUEUECMD_MASTER_MODE | I2C_QUEUECMD_DIRECTION | + (1 << I2C_QUEUECMD_XFER_COUNT_OFFSET), + &i2c_regs->hw_i2c_queuecmd); + + writel((chip << 1) | 1, &i2c_regs->hw_i2c_data); + + writel(I2C_QUEUECMD_SEND_NAK_ON_LAST | I2C_QUEUECMD_MASTER_MODE | + (len << I2C_QUEUECMD_XFER_COUNT_OFFSET) | + I2C_QUEUECMD_POST_SEND_STOP, &i2c_regs->hw_i2c_queuecmd); + + writel(I2C_QUEUECTRL_QUEUE_RUN, &i2c_regs->hw_i2c_queuectrl_set); +} + +void mxs_i2c_write(uchar chip, uint addr, int alen, + uchar *buf, int blen, int stop) +{ + struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE; + uint32_t data; + int i, remain, off; + + if ((alen > 4) || (alen == 0)) { + debug("MXS I2C: Invalid address length\n"); + return; + } + + if (stop) + stop = I2C_QUEUECMD_POST_SEND_STOP; + + writel(I2C_QUEUECMD_PRE_SEND_START | + I2C_QUEUECMD_MASTER_MODE | I2C_QUEUECMD_DIRECTION | + ((blen + alen + 1) << I2C_QUEUECMD_XFER_COUNT_OFFSET) | stop, + &i2c_regs->hw_i2c_queuecmd); + + data = (chip << 1) << 24; + + for (i = 0; i < alen; i++) { + data >>= 8; + data |= ((char *)&addr)[i] << 24; + if ((i & 3) == 2) + writel(data, &i2c_regs->hw_i2c_data); + } + + off = i; + for (; i < off + blen; i++) { + data >>= 8; + data |= buf[i - off] << 24; + if ((i & 3) == 2) + writel(data, &i2c_regs->hw_i2c_data); + } + + remain = 24 - ((i & 3) * 8); + if (remain) + writel(data >> remain, &i2c_regs->hw_i2c_data); + + writel(I2C_QUEUECTRL_QUEUE_RUN, &i2c_regs->hw_i2c_queuectrl_set); +} + +int mxs_i2c_wait_for_ack(void) +{ + struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE; + uint32_t tmp; + int timeout = MXS_I2C_MAX_TIMEOUT; + + for (;;) { + tmp = readl(&i2c_regs->hw_i2c_ctrl1); + if (tmp & I2C_CTRL1_NO_SLAVE_ACK_IRQ) { + debug("MXS I2C: No slave ACK\n"); + goto err; + } + + if (tmp & ( + I2C_CTRL1_EARLY_TERM_IRQ | I2C_CTRL1_MASTER_LOSS_IRQ | + I2C_CTRL1_SLAVE_STOP_IRQ | I2C_CTRL1_SLAVE_IRQ)) { + debug("MXS I2C: Error (CTRL1 = %08x)\n", tmp); + goto err; + } + + if (tmp & I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ) + break; + + if (!timeout--) { + debug("MXS I2C: Operation timed out\n"); + goto err; + } + + udelay(1); + } + + return 0; + +err: + mxs_i2c_reset(); + return 1; +} + +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE; + uint32_t tmp = 0; + int ret; + int i; + + mxs_i2c_write(chip, addr, alen, NULL, 0, 0); + ret = mxs_i2c_wait_for_ack(); + if (ret) { + debug("MXS I2C: Failed writing address\n"); + return ret; + } + + mxs_i2c_setup_read(chip, len); + ret = mxs_i2c_wait_for_ack(); + if (ret) { + debug("MXS I2C: Failed reading address\n"); + return ret; + } + + for (i = 0; i < len; i++) { + if (!(i & 3)) { + while (readl(&i2c_regs->hw_i2c_queuestat) & + I2C_QUEUESTAT_RD_QUEUE_EMPTY) + ; + tmp = readl(&i2c_regs->hw_i2c_queuedata); + } + buffer[i] = tmp & 0xff; + tmp >>= 8; + } + + return 0; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int ret; + mxs_i2c_write(chip, addr, alen, buffer, len, 1); + ret = mxs_i2c_wait_for_ack(); + if (ret) + debug("MXS I2C: Failed writing address\n"); + + return ret; +} + +int i2c_probe(uchar chip) +{ + int ret; + mxs_i2c_write(chip, 0, 1, NULL, 0, 1); + ret = mxs_i2c_wait_for_ack(); + mxs_i2c_reset(); + return ret; +} + +void i2c_init(int speed, int slaveadd) +{ + struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE; + + mxs_i2c_reset(); + + switch (speed) { + case 100000: + writel((0x0078 << I2C_TIMING0_HIGH_COUNT_OFFSET) | + (0x0030 << I2C_TIMING0_RCV_COUNT_OFFSET), + &i2c_regs->hw_i2c_timing0); + writel((0x0080 << I2C_TIMING1_LOW_COUNT_OFFSET) | + (0x0030 << I2C_TIMING1_XMIT_COUNT_OFFSET), + &i2c_regs->hw_i2c_timing1); + break; + case 400000: + writel((0x000f << I2C_TIMING0_HIGH_COUNT_OFFSET) | + (0x0007 << I2C_TIMING0_RCV_COUNT_OFFSET), + &i2c_regs->hw_i2c_timing0); + writel((0x001f << I2C_TIMING1_LOW_COUNT_OFFSET) | + (0x000f << I2C_TIMING1_XMIT_COUNT_OFFSET), + &i2c_regs->hw_i2c_timing1); + break; + default: + printf("MXS I2C: Invalid speed selected (%d Hz)\n", speed); + return; + } + + writel((0x0015 << I2C_TIMING2_BUS_FREE_OFFSET) | + (0x000d << I2C_TIMING2_LEADIN_COUNT_OFFSET), + &i2c_regs->hw_i2c_timing2); + + return; +} diff --git a/drivers/misc/pmic_fsl.c b/drivers/misc/pmic_fsl.c index b6e809a..0ff75ed 100644 --- a/drivers/misc/pmic_fsl.c +++ b/drivers/misc/pmic_fsl.c @@ -29,10 +29,7 @@ #if defined(CONFIG_PMIC_SPI) static u32 pmic_spi_prepare_tx(u32 reg, u32 *val, u32 write) { - if ((val == NULL) && (write)) - return *val & ~(1 << 31); - else - return (write << 31) | (reg << 25) | (*val & 0x00FFFFFF); + return (write << 31) | (reg << 25) | (*val & 0x00FFFFFF); } #endif diff --git a/drivers/misc/pmic_spi.c b/drivers/misc/pmic_spi.c index ff35377..5a0dd22 100644 --- a/drivers/misc/pmic_spi.c +++ b/drivers/misc/pmic_spi.c @@ -76,8 +76,7 @@ static u32 pmic_reg(struct pmic *p, u32 reg, u32 *val, u32 write) } if (write) { - pmic_tx = p->hw.spi.prepare_tx(0, NULL, write); - pmic_tx &= ~(1 << 31); + pmic_tx = p->hw.spi.prepare_tx(reg, val, 0); tmp = cpu_to_be32(pmic_tx); if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx, pmic_spi_flags)) { diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 6e94860..506f1d6 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -36,8 +36,10 @@ COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o COBJS-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o COBJS-$(CONFIG_MV_SDHCI) += mv_sdhci.o COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o +COBJS-$(CONFIG_MXS_MMC) += mxsmmc.o COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o +COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o COBJS-$(CONFIG_S5P_MMC) += s5p_mmc.o COBJS-$(CONFIG_SDHCI) += sdhci.o COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c index ed296ee..e6467a2 100644 --- a/drivers/mmc/arm_pl180_mmci.c +++ b/drivers/mmc/arm_pl180_mmci.c @@ -111,7 +111,6 @@ static int do_command(struct mmc *dev, struct mmc_cmd *cmd) static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize) { u32 *tempbuff = dest; - int i; u64 xfercount = blkcount * blksize; struct mmc_host *host = dev->priv; u32 status, status_err; @@ -121,31 +120,6 @@ static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize) status = readl(&host->base->status); status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_RXOVERR); - while (!status_err && - (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32))) { - if (status & SDI_STA_RXFIFOBR) { - for (i = 0; i < SDI_FIFO_BURST_SIZE; i++) - *(tempbuff + i) = readl(&host->base->fifo); - tempbuff += SDI_FIFO_BURST_SIZE; - xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32); - } - status = readl(&host->base->status); - status_err = status & - (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_RXOVERR); - } - - if (status & SDI_STA_DTIMEOUT) { - printf("Read data timed out, xfercount: %llu, status: 0x%08X\n", - xfercount, status); - return -ETIMEDOUT; - } else if (status & SDI_STA_DCRCFAIL) { - printf("Read data blk CRC error: 0x%x\n", status); - return -EILSEQ; - } else if (status & SDI_STA_RXOVERR) { - printf("Read data RX overflow error\n"); - return -EIO; - } - while ((!status_err) && (xfercount >= sizeof(u32))) { if (status & SDI_STA_RXDAVL) { *(tempbuff) = readl(&host->base->fifo); diff --git a/drivers/mmc/davinci_mmc.c b/drivers/mmc/davinci_mmc.c index 5d918e6..ce96736 100644 --- a/drivers/mmc/davinci_mmc.c +++ b/drivers/mmc/davinci_mmc.c @@ -69,8 +69,8 @@ static void dmmc_set_clock(struct mmc *mmc, uint clock) static int dmmc_wait_fifo_status(volatile struct davinci_mmc_regs *regs, uint status) { - uint mmcstatus1, wdog = WATCHDOG_COUNT; - mmcstatus1 = get_val(®s->mmcst1); + uint wdog = WATCHDOG_COUNT; + while (--wdog && ((get_val(®s->mmcst1) & status) != status)) udelay(10); @@ -86,9 +86,8 @@ dmmc_wait_fifo_status(volatile struct davinci_mmc_regs *regs, uint status) /* Busy bit wait loop for MMCST1 */ static int dmmc_busy_wait(volatile struct davinci_mmc_regs *regs) { - uint mmcstatus1, wdog = WATCHDOG_COUNT; + uint wdog = WATCHDOG_COUNT; - mmcstatus1 = get_val(®s->mmcst1); while (--wdog && (get_val(®s->mmcst1) & MMCST1_BUSY)) udelay(10); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index e5fedb3..37ce6e8 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -631,14 +631,12 @@ int mmc_change_freq(struct mmc *mmc) if (mmc->version < MMC_VERSION_4) return 0; - mmc->card_caps |= MMC_MODE_4BIT; - err = mmc_send_ext_csd(mmc, ext_csd); if (err) return err; - cardtype = ext_csd[196] & 0xf; + cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf; err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); @@ -652,7 +650,7 @@ int mmc_change_freq(struct mmc *mmc) return err; /* No high-speed support */ - if (!ext_csd[185]) + if (!ext_csd[EXT_CSD_HS_TIMING]) return 0; /* High Speed is set, there are two types: 52MHz and 26MHz */ @@ -856,11 +854,12 @@ void mmc_set_bus_width(struct mmc *mmc, uint width) int mmc_startup(struct mmc *mmc) { - int err; + int err, width; uint mult, freq; u64 cmult, csize, capacity; struct mmc_cmd cmd; ALLOC_CACHE_ALIGN_BUFFER(char, ext_csd, 512); + ALLOC_CACHE_ALIGN_BUFFER(char, test_csd, 512); int timeout = 1000; #ifdef CONFIG_MMC_SPI_CRC_ON @@ -989,7 +988,7 @@ int mmc_startup(struct mmc *mmc) /* Select the card, and put it into Transfer Mode */ if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ cmd.cmdidx = MMC_CMD_SELECT_CARD; - cmd.resp_type = MMC_RSP_R1b; + cmd.resp_type = MMC_RSP_R1; cmd.cmdarg = mmc->rca << 16; cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); @@ -1006,14 +1005,16 @@ int mmc_startup(struct mmc *mmc) if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) { /* check ext_csd version and capacity */ err = mmc_send_ext_csd(mmc, ext_csd); - if (!err & (ext_csd[192] >= 2)) { + if (!err & (ext_csd[EXT_CSD_REV] >= 2)) { /* * According to the JEDEC Standard, the value of * ext_csd's capacity is valid if the value is more * than 2GB */ - capacity = ext_csd[212] << 0 | ext_csd[213] << 8 | - ext_csd[214] << 16 | ext_csd[215] << 24; + capacity = ext_csd[EXT_CSD_SEC_CNT] << 0 + | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 + | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 + | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; capacity *= 512; if ((capacity >> 20) > 2 * 1024) mmc->capacity = capacity; @@ -1024,8 +1025,9 @@ int mmc_startup(struct mmc *mmc) * group size from ext_csd directly, or calculate * the group size from the csd value. */ - if (ext_csd[175]) - mmc->erase_grp_size = ext_csd[224] * 512 * 1024; + if (ext_csd[EXT_CSD_ERASE_GROUP_DEF]) + mmc->erase_grp_size = + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] * 512 * 1024; else { int erase_gsz, erase_gmul; erase_gsz = (mmc->csd[2] & 0x00007c00) >> 10; @@ -1035,8 +1037,8 @@ int mmc_startup(struct mmc *mmc) } /* store the partition info of emmc */ - if (ext_csd[160] & PART_SUPPORT) - mmc->part_config = ext_csd[179]; + if (ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) + mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; } if (IS_SD(mmc)) @@ -1077,26 +1079,35 @@ int mmc_startup(struct mmc *mmc) else mmc_set_clock(mmc, 25000000); } else { - if (mmc->card_caps & MMC_MODE_4BIT) { + for (width = EXT_CSD_BUS_WIDTH_8; width >= 0; width--) { /* Set the card to use 4 bit*/ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, - EXT_CSD_BUS_WIDTH_4); - - if (err) - return err; - - mmc_set_bus_width(mmc, 4); - } else if (mmc->card_caps & MMC_MODE_8BIT) { - /* Set the card to use 8 bit*/ - err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, - EXT_CSD_BUS_WIDTH_8); + EXT_CSD_BUS_WIDTH, width); if (err) - return err; + continue; - mmc_set_bus_width(mmc, 8); + if (!width) { + mmc_set_bus_width(mmc, 1); + break; + } else + mmc_set_bus_width(mmc, 4 * width); + + err = mmc_send_ext_csd(mmc, test_csd); + if (!err && ext_csd[EXT_CSD_PARTITIONING_SUPPORT] \ + == test_csd[EXT_CSD_PARTITIONING_SUPPORT] + && ext_csd[EXT_CSD_ERASE_GROUP_DEF] \ + == test_csd[EXT_CSD_ERASE_GROUP_DEF] \ + && ext_csd[EXT_CSD_REV] \ + == test_csd[EXT_CSD_REV] + && ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] \ + == test_csd[EXT_CSD_HC_ERASE_GRP_SIZE] + && memcmp(&ext_csd[EXT_CSD_SEC_CNT], \ + &test_csd[EXT_CSD_SEC_CNT], 4) == 0) { + + mmc->card_caps |= width; + break; + } } if (mmc->card_caps & MMC_MODE_HS) { @@ -1179,7 +1190,7 @@ block_dev_desc_t *mmc_get_dev(int dev) int mmc_init(struct mmc *mmc) { - int err; + int err, retry = 3; if (mmc->has_init) return 0; @@ -1202,7 +1213,19 @@ int mmc_init(struct mmc *mmc) mmc->part_num = 0; /* Test for SD version 2 */ - err = mmc_send_if_cond(mmc); + /* + * retry here for 3 times, as for some controller it has dynamic + * clock gating, and only toggle out clk when the first cmd0 send + * out, while some card strictly obey the 74 clocks rule, the interval + * may not be sufficient between the cmd0 and this cmd8, retry to + * fulfil the clock requirement + */ + do { + err = mmc_send_if_cond(mmc); + } while (--retry > 0 && err); + + if (err) + return err; /* Now try to get the SD card's operating condition */ err = sd_send_op_cond(mmc); diff --git a/drivers/mmc/mv_sdhci.c b/drivers/mmc/mv_sdhci.c index 9e59951..f92caeb 100644 --- a/drivers/mmc/mv_sdhci.c +++ b/drivers/mmc/mv_sdhci.c @@ -2,6 +2,33 @@ #include <malloc.h> #include <sdhci.h> +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS +static struct sdhci_ops mv_ops; + +#if defined(CONFIG_SHEEVA_88SV331xV5) +#define SD_CE_ATA_2 0xEA +#define MMC_CARD 0x1000 +#define MMC_WIDTH 0x0100 +static inline void mv_sdhci_writeb(struct sdhci_host *host, u8 val, int reg) +{ + struct mmc *mmc = host->mmc; + u32 ata = (u32)host->ioaddr + SD_CE_ATA_2; + + if (!IS_SD(mmc) && reg == SDHCI_HOST_CONTROL) { + if (mmc->bus_width == 8) + writew(readw(ata) | (MMC_CARD | MMC_WIDTH), ata); + else + writew(readw(ata) & ~(MMC_CARD | MMC_WIDTH), ata); + } + + writeb(val, host->ioaddr + reg); +} + +#else +#define mv_sdhci_writeb NULL +#endif /* CONFIG_SHEEVA_88SV331xV5 */ +#endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */ + static char *MVSDH_NAME = "mv_sdh"; int mv_sdh_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks) { @@ -15,6 +42,12 @@ int mv_sdh_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks) host->name = MVSDH_NAME; host->ioaddr = (void *)regbase; host->quirks = quirks; +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS + memset(&mv_ops, 0, sizeof(struct sdhci_ops)); + if (mv_sdhci_writeb != NULL) + mv_ops.write_b = mv_sdhci_writeb; + host->ops = &mv_ops; +#endif host->version = sdhci_readw(host, SDHCI_HOST_VERSION); add_sdhci(host, max_clk, min_clk); return 0; diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c new file mode 100644 index 0000000..2a9949e --- /dev/null +++ b/drivers/mmc/mxsmmc.c @@ -0,0 +1,351 @@ +/* + * Freescale i.MX28 SSP MMC driver + * + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * on behalf of DENX Software Engineering GmbH + * + * Based on code from LTIB: + * (C) Copyright 2008-2010 Freescale Semiconductor, Inc. + * Terry Lv + * + * Copyright 2007, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the pxa mmc code: + * (C) Copyright 2003 + * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#include <malloc.h> +#include <mmc.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> + +struct mxsmmc_priv { + int id; + struct mx28_ssp_regs *regs; + uint32_t clkseq_bypass; + uint32_t *clkctrl_ssp; + uint32_t buswidth; + int (*mmc_is_wp)(int); +}; + +#define MXSMMC_MAX_TIMEOUT 10000 + +/* + * Sends a command out on the bus. Takes the mmc pointer, + * a command pointer, and an optional data pointer. + */ +static int +mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{ + struct mxsmmc_priv *priv = (struct mxsmmc_priv *)mmc->priv; + struct mx28_ssp_regs *ssp_regs = priv->regs; + uint32_t reg; + int timeout; + uint32_t data_count; + uint32_t *data_ptr; + uint32_t ctrl0; + + debug("MMC%d: CMD%d\n", mmc->block_dev.dev, cmd->cmdidx); + + /* Check bus busy */ + timeout = MXSMMC_MAX_TIMEOUT; + while (--timeout) { + udelay(1000); + reg = readl(&ssp_regs->hw_ssp_status); + if (!(reg & + (SSP_STATUS_BUSY | SSP_STATUS_DATA_BUSY | + SSP_STATUS_CMD_BUSY))) { + break; + } + } + + if (!timeout) { + printf("MMC%d: Bus busy timeout!\n", mmc->block_dev.dev); + return TIMEOUT; + } + + /* See if card is present */ + if (readl(&ssp_regs->hw_ssp_status) & SSP_STATUS_CARD_DETECT) { + printf("MMC%d: No card detected!\n", mmc->block_dev.dev); + return NO_CARD_ERR; + } + + /* Start building CTRL0 contents */ + ctrl0 = priv->buswidth; + + /* Set up command */ + if (!(cmd->resp_type & MMC_RSP_CRC)) + ctrl0 |= SSP_CTRL0_IGNORE_CRC; + if (cmd->resp_type & MMC_RSP_PRESENT) /* Need to get response */ + ctrl0 |= SSP_CTRL0_GET_RESP; + if (cmd->resp_type & MMC_RSP_136) /* It's a 136 bits response */ + ctrl0 |= SSP_CTRL0_LONG_RESP; + + /* Command index */ + reg = readl(&ssp_regs->hw_ssp_cmd0); + reg &= ~(SSP_CMD0_CMD_MASK | SSP_CMD0_APPEND_8CYC); + reg |= cmd->cmdidx << SSP_CMD0_CMD_OFFSET; + if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + reg |= SSP_CMD0_APPEND_8CYC; + writel(reg, &ssp_regs->hw_ssp_cmd0); + + /* Command argument */ + writel(cmd->cmdarg, &ssp_regs->hw_ssp_cmd1); + + /* Set up data */ + if (data) { + /* READ or WRITE */ + if (data->flags & MMC_DATA_READ) { + ctrl0 |= SSP_CTRL0_READ; + } else if (priv->mmc_is_wp(mmc->block_dev.dev)) { + printf("MMC%d: Can not write a locked card!\n", + mmc->block_dev.dev); + return UNUSABLE_ERR; + } + + ctrl0 |= SSP_CTRL0_DATA_XFER; + reg = ((data->blocks - 1) << + SSP_BLOCK_SIZE_BLOCK_COUNT_OFFSET) | + ((ffs(data->blocksize) - 1) << + SSP_BLOCK_SIZE_BLOCK_SIZE_OFFSET); + writel(reg, &ssp_regs->hw_ssp_block_size); + + reg = data->blocksize * data->blocks; + writel(reg, &ssp_regs->hw_ssp_xfer_size); + } + + /* Kick off the command */ + ctrl0 |= SSP_CTRL0_WAIT_FOR_IRQ | SSP_CTRL0_ENABLE | SSP_CTRL0_RUN; + writel(ctrl0, &ssp_regs->hw_ssp_ctrl0); + + /* Wait for the command to complete */ + timeout = MXSMMC_MAX_TIMEOUT; + while (--timeout) { + udelay(1000); + reg = readl(&ssp_regs->hw_ssp_status); + if (!(reg & SSP_STATUS_CMD_BUSY)) + break; + } + + if (!timeout) { + printf("MMC%d: Command %d busy\n", + mmc->block_dev.dev, cmd->cmdidx); + return TIMEOUT; + } + + /* Check command timeout */ + if (reg & SSP_STATUS_RESP_TIMEOUT) { + printf("MMC%d: Command %d timeout (status 0x%08x)\n", + mmc->block_dev.dev, cmd->cmdidx, reg); + return TIMEOUT; + } + + /* Check command errors */ + if (reg & (SSP_STATUS_RESP_CRC_ERR | SSP_STATUS_RESP_ERR)) { + printf("MMC%d: Command %d error (status 0x%08x)!\n", + mmc->block_dev.dev, cmd->cmdidx, reg); + return COMM_ERR; + } + + /* Copy response to response buffer */ + if (cmd->resp_type & MMC_RSP_136) { + cmd->response[3] = readl(&ssp_regs->hw_ssp_sdresp0); + cmd->response[2] = readl(&ssp_regs->hw_ssp_sdresp1); + cmd->response[1] = readl(&ssp_regs->hw_ssp_sdresp2); + cmd->response[0] = readl(&ssp_regs->hw_ssp_sdresp3); + } else + cmd->response[0] = readl(&ssp_regs->hw_ssp_sdresp0); + + /* Return if no data to process */ + if (!data) + return 0; + + /* Process the data */ + data_count = data->blocksize * data->blocks; + timeout = MXSMMC_MAX_TIMEOUT; + if (data->flags & MMC_DATA_READ) { + data_ptr = (uint32_t *)data->dest; + while (data_count && --timeout) { + reg = readl(&ssp_regs->hw_ssp_status); + if (!(reg & SSP_STATUS_FIFO_EMPTY)) { + *data_ptr++ = readl(&ssp_regs->hw_ssp_data); + data_count -= 4; + timeout = MXSMMC_MAX_TIMEOUT; + } else + udelay(1000); + } + } else { + data_ptr = (uint32_t *)data->src; + timeout *= 100; + while (data_count && --timeout) { + reg = readl(&ssp_regs->hw_ssp_status); + if (!(reg & SSP_STATUS_FIFO_FULL)) { + writel(*data_ptr++, &ssp_regs->hw_ssp_data); + data_count -= 4; + timeout = MXSMMC_MAX_TIMEOUT; + } else + udelay(1000); + } + } + + if (!timeout) { + printf("MMC%d: Data timeout with command %d (status 0x%08x)!\n", + mmc->block_dev.dev, cmd->cmdidx, reg); + return COMM_ERR; + } + + /* Check data errors */ + reg = readl(&ssp_regs->hw_ssp_status); + if (reg & + (SSP_STATUS_TIMEOUT | SSP_STATUS_DATA_CRC_ERR | + SSP_STATUS_FIFO_OVRFLW | SSP_STATUS_FIFO_UNDRFLW)) { + printf("MMC%d: Data error with command %d (status 0x%08x)!\n", + mmc->block_dev.dev, cmd->cmdidx, reg); + return COMM_ERR; + } + + return 0; +} + +static void mxsmmc_set_ios(struct mmc *mmc) +{ + struct mxsmmc_priv *priv = (struct mxsmmc_priv *)mmc->priv; + struct mx28_ssp_regs *ssp_regs = priv->regs; + + /* Set the clock speed */ + if (mmc->clock) + mx28_set_ssp_busclock(priv->id, mmc->clock / 1000); + + switch (mmc->bus_width) { + case 1: + priv->buswidth = SSP_CTRL0_BUS_WIDTH_ONE_BIT; + break; + case 4: + priv->buswidth = SSP_CTRL0_BUS_WIDTH_FOUR_BIT; + break; + case 8: + priv->buswidth = SSP_CTRL0_BUS_WIDTH_EIGHT_BIT; + break; + } + + /* Set the bus width */ + clrsetbits_le32(&ssp_regs->hw_ssp_ctrl0, + SSP_CTRL0_BUS_WIDTH_MASK, priv->buswidth); + + debug("MMC%d: Set %d bits bus width\n", + mmc->block_dev.dev, mmc->bus_width); +} + +static int mxsmmc_init(struct mmc *mmc) +{ + struct mxsmmc_priv *priv = (struct mxsmmc_priv *)mmc->priv; + struct mx28_ssp_regs *ssp_regs = priv->regs; + + /* Reset SSP */ + mx28_reset_block(&ssp_regs->hw_ssp_ctrl0_reg); + + /* 8 bits word length in MMC mode */ + clrsetbits_le32(&ssp_regs->hw_ssp_ctrl1, + SSP_CTRL1_SSP_MODE_MASK | SSP_CTRL1_WORD_LENGTH_MASK, + SSP_CTRL1_SSP_MODE_SD_MMC | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS); + + /* Set initial bit clock 400 KHz */ + mx28_set_ssp_busclock(priv->id, 400); + + /* Send initial 74 clock cycles (185 us @ 400 KHz)*/ + writel(SSP_CMD0_CONT_CLKING_EN, &ssp_regs->hw_ssp_cmd0_set); + udelay(200); + writel(SSP_CMD0_CONT_CLKING_EN, &ssp_regs->hw_ssp_cmd0_clr); + + return 0; +} + +int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int)) +{ + struct mx28_clkctrl_regs *clkctrl_regs = + (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE; + struct mmc *mmc = NULL; + struct mxsmmc_priv *priv = NULL; + + mmc = malloc(sizeof(struct mmc)); + if (!mmc) + return -ENOMEM; + + priv = malloc(sizeof(struct mxsmmc_priv)); + if (!priv) { + free(mmc); + return -ENOMEM; + } + + priv->mmc_is_wp = wp; + priv->id = id; + switch (id) { + case 0: + priv->regs = (struct mx28_ssp_regs *)MXS_SSP0_BASE; + priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP0; + priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp0; + break; + case 1: + priv->regs = (struct mx28_ssp_regs *)MXS_SSP1_BASE; + priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP1; + priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp1; + break; + case 2: + priv->regs = (struct mx28_ssp_regs *)MXS_SSP2_BASE; + priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP2; + priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp2; + break; + case 3: + priv->regs = (struct mx28_ssp_regs *)MXS_SSP3_BASE; + priv->clkseq_bypass = CLKCTRL_CLKSEQ_BYPASS_SSP3; + priv->clkctrl_ssp = &clkctrl_regs->hw_clkctrl_ssp3; + break; + } + + sprintf(mmc->name, "MXS MMC"); + mmc->send_cmd = mxsmmc_send_cmd; + mmc->set_ios = mxsmmc_set_ios; + mmc->init = mxsmmc_init; + mmc->priv = priv; + + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + + mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | + MMC_MODE_HS_52MHz | MMC_MODE_HS; + + /* + * SSPCLK = 480 * 18 / 29 / 1 = 297.731 MHz + * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)), + * CLOCK_DIVIDE has to be an even value from 2 to 254, and + * CLOCK_RATE could be any integer from 0 to 255. + */ + mmc->f_min = 400000; + mmc->f_max = mxc_get_clock(MXC_SSP0_CLK + id) * 1000 / 2; + mmc->b_max = 0; + + mmc_register(mmc); + return 0; +} diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index ebda980..c38b9e6 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -36,8 +36,9 @@ /* If we fail after 1 second wait, something is really bad */ #define MAX_RETRY_MS 1000 -static int mmc_read_data(hsmmc_t *mmc_base, char *buf, unsigned int size); -static int mmc_write_data(hsmmc_t *mmc_base, const char *buf, unsigned int siz); +static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size); +static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, + unsigned int siz); static struct mmc hsmmc_dev[2]; #if defined(CONFIG_OMAP44XX) && defined(CONFIG_TWL6030_POWER) @@ -97,7 +98,7 @@ unsigned char mmc_board_init(struct mmc *mmc) return 0; } -void mmc_init_stream(hsmmc_t *mmc_base) +void mmc_init_stream(struct hsmmc *mmc_base) { ulong start; @@ -128,7 +129,7 @@ void mmc_init_stream(hsmmc_t *mmc_base) static int mmc_init_setup(struct mmc *mmc) { - hsmmc_t *mmc_base = (hsmmc_t *)mmc->priv; + struct hsmmc *mmc_base = (struct hsmmc *)mmc->priv; unsigned int reg_val; unsigned int dsor; ulong start; @@ -192,7 +193,7 @@ static int mmc_init_setup(struct mmc *mmc) static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { - hsmmc_t *mmc_base = (hsmmc_t *)mmc->priv; + struct hsmmc *mmc_base = (struct hsmmc *)mmc->priv; unsigned int flags, mmc_stat; ulong start; @@ -305,7 +306,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, return 0; } -static int mmc_read_data(hsmmc_t *mmc_base, char *buf, unsigned int size) +static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size) { unsigned int *output_buf = (unsigned int *)buf; unsigned int mmc_stat; @@ -356,7 +357,8 @@ static int mmc_read_data(hsmmc_t *mmc_base, char *buf, unsigned int size) return 0; } -static int mmc_write_data(hsmmc_t *mmc_base, const char *buf, unsigned int size) +static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, + unsigned int size) { unsigned int *input_buf = (unsigned int *)buf; unsigned int mmc_stat; @@ -409,7 +411,7 @@ static int mmc_write_data(hsmmc_t *mmc_base, const char *buf, unsigned int size) static void mmc_set_ios(struct mmc *mmc) { - hsmmc_t *mmc_base = (hsmmc_t *)mmc->priv; + struct hsmmc *mmc_base = (struct hsmmc *)mmc->priv; unsigned int dsor = 0; ulong start; @@ -473,20 +475,20 @@ int omap_mmc_init(int dev_index) switch (dev_index) { case 0: - mmc->priv = (hsmmc_t *)OMAP_HSMMC1_BASE; + mmc->priv = (struct hsmmc *)OMAP_HSMMC1_BASE; break; #ifdef OMAP_HSMMC2_BASE case 1: - mmc->priv = (hsmmc_t *)OMAP_HSMMC2_BASE; + mmc->priv = (struct hsmmc *)OMAP_HSMMC2_BASE; break; #endif #ifdef OMAP_HSMMC3_BASE case 2: - mmc->priv = (hsmmc_t *)OMAP_HSMMC3_BASE; + mmc->priv = (struct hsmmc *)OMAP_HSMMC3_BASE; break; #endif default: - mmc->priv = (hsmmc_t *)OMAP_HSMMC1_BASE; + mmc->priv = (struct hsmmc *)OMAP_HSMMC1_BASE; return 1; } mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; diff --git a/drivers/mmc/pxa_mmc_gen.c b/drivers/mmc/pxa_mmc_gen.c new file mode 100644 index 0000000..28e37b4 --- /dev/null +++ b/drivers/mmc/pxa_mmc_gen.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com> + * + * Loosely based on the old code and Linux's PXA MMC driver + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <config.h> +#include <common.h> +#include <malloc.h> + +#include <mmc.h> +#include <asm/errno.h> +#include <asm/arch/hardware.h> +#include <asm/arch/regs-mmc.h> +#include <asm/io.h> + +/* PXAMMC Generic default config for various CPUs */ +#if defined(CONFIG_PXA250) +#define PXAMMC_FIFO_SIZE 1 +#define PXAMMC_MIN_SPEED 312500 +#define PXAMMC_MAX_SPEED 20000000 +#define PXAMMC_HOST_CAPS (0) +#elif defined(CONFIG_PXA27X) +#define PXAMMC_CRC_SKIP +#define PXAMMC_FIFO_SIZE 32 +#define PXAMMC_MIN_SPEED 304000 +#define PXAMMC_MAX_SPEED 19500000 +#define PXAMMC_HOST_CAPS (MMC_MODE_4BIT) +#elif defined(CONFIG_CPU_MONAHANS) +#define PXAMMC_FIFO_SIZE 32 +#define PXAMMC_MIN_SPEED 304000 +#define PXAMMC_MAX_SPEED 26000000 +#define PXAMMC_HOST_CAPS (MMC_MODE_4BIT | MMC_MODE_HS) +#else +#error "This CPU isn't supported by PXA MMC!" +#endif + +#define MMC_STAT_ERRORS \ + (MMC_STAT_RES_CRC_ERROR | MMC_STAT_SPI_READ_ERROR_TOKEN | \ + MMC_STAT_CRC_READ_ERROR | MMC_STAT_TIME_OUT_RESPONSE | \ + MMC_STAT_READ_TIME_OUT | MMC_STAT_CRC_WRITE_ERROR) + +/* 1 millisecond (in wait cycles below it's 100 x 10uS waits) */ +#define PXA_MMC_TIMEOUT 100 + +struct pxa_mmc_priv { + struct pxa_mmc_regs *regs; +}; + +/* Wait for bit to be set */ +static int pxa_mmc_wait(struct mmc *mmc, uint32_t mask) +{ + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + unsigned int timeout = PXA_MMC_TIMEOUT; + + /* Wait for bit to be set */ + while (--timeout) { + if (readl(®s->stat) & mask) + break; + udelay(10); + } + + if (!timeout) + return -ETIMEDOUT; + + return 0; +} + +static int pxa_mmc_stop_clock(struct mmc *mmc) +{ + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + unsigned int timeout = PXA_MMC_TIMEOUT; + + /* If the clock aren't running, exit */ + if (!(readl(®s->stat) & MMC_STAT_CLK_EN)) + return 0; + + /* Tell the controller to turn off the clock */ + writel(MMC_STRPCL_STOP_CLK, ®s->strpcl); + + /* Wait until the clock are off */ + while (--timeout) { + if (!(readl(®s->stat) & MMC_STAT_CLK_EN)) + break; + udelay(10); + } + + /* The clock refused to stop, scream and die a painful death */ + if (!timeout) + return -ETIMEDOUT; + + /* The clock stopped correctly */ + return 0; +} + +static int pxa_mmc_start_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + uint32_t cmdat) +{ + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + int ret; + + /* The card can send a "busy" response */ + if (cmd->flags & MMC_RSP_BUSY) + cmdat |= MMC_CMDAT_BUSY; + + /* Inform the controller about response type */ + switch (cmd->resp_type) { + case MMC_RSP_R1: + case MMC_RSP_R1b: + cmdat |= MMC_CMDAT_R1; + break; + case MMC_RSP_R2: + cmdat |= MMC_CMDAT_R2; + break; + case MMC_RSP_R3: + cmdat |= MMC_CMDAT_R3; + break; + default: + break; + } + + /* Load command and it's arguments into the controller */ + writel(cmd->cmdidx, ®s->cmd); + writel(cmd->cmdarg >> 16, ®s->argh); + writel(cmd->cmdarg & 0xffff, ®s->argl); + writel(cmdat, ®s->cmdat); + + /* Start the controller clock and wait until they are started */ + writel(MMC_STRPCL_START_CLK, ®s->strpcl); + + ret = pxa_mmc_wait(mmc, MMC_STAT_CLK_EN); + if (ret) + return ret; + + /* Correct and happy end */ + return 0; +} + +static int pxa_mmc_cmd_done(struct mmc *mmc, struct mmc_cmd *cmd) +{ + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + uint32_t a, b, c; + int i; + int stat; + + /* Read the controller status */ + stat = readl(®s->stat); + + /* + * Linux says: + * Did I mention this is Sick. We always need to + * discard the upper 8 bits of the first 16-bit word. + */ + a = readl(®s->res) & 0xffff; + for (i = 0; i < 4; i++) { + b = readl(®s->res) & 0xffff; + c = readl(®s->res) & 0xffff; + cmd->response[i] = (a << 24) | (b << 8) | (c >> 8); + a = c; + } + + /* The command response didn't arrive */ + if (stat & MMC_STAT_TIME_OUT_RESPONSE) + return -ETIMEDOUT; + else if (stat & MMC_STAT_RES_CRC_ERROR && cmd->flags & MMC_RSP_CRC) { +#ifdef PXAMMC_CRC_SKIP + if (cmd->flags & MMC_RSP_136 && cmd->response[0] & (1 << 31)) + printf("Ignoring CRC, this may be dangerous!\n"); + else +#endif + return -EILSEQ; + } + + /* The command response was successfully read */ + return 0; +} + +static int pxa_mmc_do_read_xfer(struct mmc *mmc, struct mmc_data *data) +{ + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + uint32_t len; + uint32_t *buf = (uint32_t *)data->dest; + int size; + int ret; + + len = data->blocks * data->blocksize; + + while (len) { + /* The controller has data ready */ + if (readl(®s->i_reg) & MMC_I_REG_RXFIFO_RD_REQ) { + size = min(len, PXAMMC_FIFO_SIZE); + len -= size; + size /= 4; + + /* Read data into the buffer */ + while (size--) + *buf++ = readl(®s->rxfifo); + + } + + if (readl(®s->stat) & MMC_STAT_ERRORS) + return -EIO; + } + + /* Wait for the transmission-done interrupt */ + ret = pxa_mmc_wait(mmc, MMC_STAT_DATA_TRAN_DONE); + if (ret) + return ret; + + return 0; +} + +static int pxa_mmc_do_write_xfer(struct mmc *mmc, struct mmc_data *data) +{ + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + uint32_t len; + uint32_t *buf = (uint32_t *)data->src; + int size; + int ret; + + len = data->blocks * data->blocksize; + + while (len) { + /* The controller is ready to receive data */ + if (readl(®s->i_reg) & MMC_I_REG_TXFIFO_WR_REQ) { + size = min(len, PXAMMC_FIFO_SIZE); + len -= size; + size /= 4; + + while (size--) + writel(*buf++, ®s->txfifo); + + if (min(len, PXAMMC_FIFO_SIZE) < 32) + writel(MMC_PRTBUF_BUF_PART_FULL, ®s->prtbuf); + } + + if (readl(®s->stat) & MMC_STAT_ERRORS) + return -EIO; + } + + /* Wait for the transmission-done interrupt */ + ret = pxa_mmc_wait(mmc, MMC_STAT_DATA_TRAN_DONE); + if (ret) + return ret; + + /* Wait until the data are really written to the card */ + ret = pxa_mmc_wait(mmc, MMC_STAT_PRG_DONE); + if (ret) + return ret; + + return 0; +} + +static int pxa_mmc_request(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + uint32_t cmdat = 0; + int ret; + + /* Stop the controller */ + ret = pxa_mmc_stop_clock(mmc); + if (ret) + return ret; + + /* If we're doing data transfer, configure the controller accordingly */ + if (data) { + writel(data->blocks, ®s->nob); + writel(data->blocksize, ®s->blklen); + /* This delay can be optimized, but stick with max value */ + writel(0xffff, ®s->rdto); + cmdat |= MMC_CMDAT_DATA_EN; + if (data->flags & MMC_DATA_WRITE) + cmdat |= MMC_CMDAT_WRITE; + } + + /* Run in 4bit mode if the card can do it */ + if (mmc->bus_width == 4) + cmdat |= MMC_CMDAT_SD_4DAT; + + /* Execute the command */ + ret = pxa_mmc_start_cmd(mmc, cmd, cmdat); + if (ret) + return ret; + + /* Wait until the command completes */ + ret = pxa_mmc_wait(mmc, MMC_STAT_END_CMD_RES); + if (ret) + return ret; + + /* Read back the result */ + ret = pxa_mmc_cmd_done(mmc, cmd); + if (ret) + return ret; + + /* In case there was a data transfer scheduled, do it */ + if (data) { + if (data->flags & MMC_DATA_WRITE) + pxa_mmc_do_write_xfer(mmc, data); + else + pxa_mmc_do_read_xfer(mmc, data); + } + + return 0; +} + +static void pxa_mmc_set_ios(struct mmc *mmc) +{ + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + uint32_t tmp; + uint32_t pxa_mmc_clock; + + if (!mmc->clock) { + pxa_mmc_stop_clock(mmc); + return; + } + + /* PXA3xx can do 26MHz with special settings. */ + if (mmc->clock == 26000000) { + writel(0x7, ®s->clkrt); + return; + } + + /* Set clock to the card the usual way. */ + pxa_mmc_clock = 0; + tmp = mmc->f_max / mmc->clock; + tmp += tmp % 2; + + while (tmp > 1) { + pxa_mmc_clock++; + tmp >>= 1; + } + + writel(pxa_mmc_clock, ®s->clkrt); +} + +static int pxa_mmc_init(struct mmc *mmc) +{ + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; + struct pxa_mmc_regs *regs = priv->regs; + + /* Make sure the clock are stopped */ + pxa_mmc_stop_clock(mmc); + + /* Turn off SPI mode */ + writel(0, ®s->spi); + + /* Set up maximum timeout to wait for command response */ + writel(MMC_RES_TO_MAX_MASK, ®s->resto); + + /* Mask all interrupts */ + writel(~(MMC_I_MASK_TXFIFO_WR_REQ | MMC_I_MASK_RXFIFO_RD_REQ), + ®s->i_mask); + return 0; +} + +int pxa_mmc_register(int card_index) +{ + struct mmc *mmc; + struct pxa_mmc_priv *priv; + uint32_t reg; + int ret = -ENOMEM; + + mmc = malloc(sizeof(struct mmc)); + if (!mmc) + goto err0; + + priv = malloc(sizeof(struct pxa_mmc_priv)); + if (!priv) + goto err1; + + switch (card_index) { + case 0: + priv->regs = (struct pxa_mmc_regs *)MMC0_BASE; + break; + case 1: + priv->regs = (struct pxa_mmc_regs *)MMC1_BASE; + break; + default: + printf("PXA MMC: Invalid MMC controller ID (card_index = %d)\n", + card_index); + goto err2; + } + + mmc->priv = priv; + + sprintf(mmc->name, "PXA MMC"); + mmc->send_cmd = pxa_mmc_request; + mmc->set_ios = pxa_mmc_set_ios; + mmc->init = pxa_mmc_init; + + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->f_max = PXAMMC_MAX_SPEED; + mmc->f_min = PXAMMC_MIN_SPEED; + mmc->host_caps = PXAMMC_HOST_CAPS; + + mmc->b_max = 0; + +#ifndef CONFIG_CPU_MONAHANS /* PXA2xx */ + reg = readl(CKEN); + reg |= CKEN12_MMC; + writel(reg, CKEN); +#else /* PXA3xx */ + reg = readl(CKENA); + reg |= CKENA_12_MMC0 | CKENA_13_MMC1; + writel(reg, CKENA); +#endif + + mmc_register(mmc); + + return 0; + +err2: + free(priv); +err1: + free(mmc); +err0: + return ret; +} diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 9ebd33d..fce0ef0 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -81,8 +81,9 @@ static void sdhci_transfer_pio(struct sdhci_host *host, struct mmc_data *data) static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, unsigned int start_addr) { - unsigned int stat, rdy, mask, block = 0; + unsigned int stat, rdy, mask, timeout, block = 0; + timeout = 10000; rdy = SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL; mask = SDHCI_DATA_AVAILABLE | SDHCI_SPACE_AVAILABLE; do { @@ -103,11 +104,17 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, #ifdef CONFIG_MMC_SDMA if (stat & SDHCI_INT_DMA_END) { sdhci_writel(host, SDHCI_INT_DMA_END, SDHCI_INT_STATUS); - start_addr &= SDHCI_DEFAULT_BOUNDARY_SIZE - 1; + start_addr &= ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1); start_addr += SDHCI_DEFAULT_BOUNDARY_SIZE; sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); } #endif + if (timeout-- > 0) + udelay(10); + else { + printf("Transfer data timeout\n"); + return -1; + } } while (!(stat & SDHCI_INT_DATA_END)); return 0; } @@ -196,7 +203,7 @@ int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); #ifdef CONFIG_MMC_SDMA - flush_cache(0, ~0); + flush_cache(start_addr, trans_bytes); #endif sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND); do { @@ -377,6 +384,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) } mmc->priv = host; + host->mmc = mmc; sprintf(mmc->name, "%s", host->name); mmc->send_cmd = sdhci_send_command; diff --git a/drivers/mmc/tegra2_mmc.c b/drivers/mmc/tegra2_mmc.c index 9e741f2..ccf48bb 100644 --- a/drivers/mmc/tegra2_mmc.c +++ b/drivers/mmc/tegra2_mmc.c @@ -81,7 +81,8 @@ static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data) * 11 = Selects 64-bit Address ADMA2 */ ctrl = readb(&host->reg->hostctl); - ctrl &= ~(3 << 3); /* SDMA */ + ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK; + ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA; writeb(ctrl, &host->reg->hostctl); /* We do not handle DMA boundaries, so set it to max (512 KiB) */ @@ -103,43 +104,36 @@ static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) * ENBLKCNT[1] : Block Count Enable * ENDMA[0] : DMA Enable */ - mode = (1 << 1) | (1 << 0); + mode = (TEGRA_MMC_TRNMOD_DMA_ENABLE | + TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE); + if (data->blocks > 1) - mode |= (1 << 5); + mode |= TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT; + if (data->flags & MMC_DATA_READ) - mode |= (1 << 4); + mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; writew(mode, &host->reg->trnmod); } -static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) +static int mmc_wait_inhibit(struct mmc_host *host, + struct mmc_cmd *cmd, + struct mmc_data *data, + unsigned int timeout) { - struct mmc_host *host = (struct mmc_host *)mmc->priv; - int flags, i; - unsigned int timeout; - unsigned int mask; - unsigned int retry = 0x100000; - debug(" mmc_send_cmd called\n"); - - /* Wait max 10 ms */ - timeout = 10; - /* * PRNSTS - * CMDINHDAT[1] : Command Inhibit (DAT) - * CMDINHCMD[0] : Command Inhibit (CMD) + * CMDINHDAT[1] : Command Inhibit (DAT) + * CMDINHCMD[0] : Command Inhibit (CMD) */ - mask = (1 << 0); - if ((data != NULL) || (cmd->resp_type & MMC_RSP_BUSY)) - mask |= (1 << 1); + unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD; /* * We shouldn't wait for data inhibit for stop commands, even * though they might use busy signaling */ - if (data) - mask &= ~(1 << 1); + if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY)) + mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT; while (readl(&host->reg->prnsts) & mask) { if (timeout == 0) { @@ -150,6 +144,24 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, udelay(1000); } + return 0; +} + +static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct mmc_host *host = (struct mmc_host *)mmc->priv; + int flags, i; + int result; + unsigned int mask; + unsigned int retry = 0x100000; + debug(" mmc_send_cmd called\n"); + + result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */); + + if (result < 0) + return result; + if (data) mmc_prepare_data(host, data); @@ -175,20 +187,20 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, * 11 = Length 48 Check busy after response */ if (!(cmd->resp_type & MMC_RSP_PRESENT)) - flags = 0; + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE; else if (cmd->resp_type & MMC_RSP_136) - flags = (1 << 0); + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136; else if (cmd->resp_type & MMC_RSP_BUSY) - flags = (3 << 0); + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY; else - flags = (2 << 0); + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; if (cmd->resp_type & MMC_RSP_CRC) - flags |= (1 << 3); + flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; if (cmd->resp_type & MMC_RSP_OPCODE) - flags |= (1 << 4); + flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK; if (data) - flags |= (1 << 5); + flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; debug("cmd: %d\n", cmd->cmdidx); @@ -197,7 +209,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, for (i = 0; i < retry; i++) { mask = readl(&host->reg->norintsts); /* Command Complete */ - if (mask & (1 << 0)) { + if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) { if (!data) writel(mask, &host->reg->norintsts); break; @@ -209,11 +221,11 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, return TIMEOUT; } - if (mask & (1 << 16)) { + if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) { /* Timeout Error */ debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); return TIMEOUT; - } else if (mask & (1 << 15)) { + } else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { /* Error Interrupt */ debug("error: %08x cmd %d\n", mask, cmd->cmdidx); return -1; @@ -256,23 +268,44 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, } if (data) { + unsigned long start = get_timer(0); + while (1) { mask = readl(&host->reg->norintsts); - if (mask & (1 << 15)) { + if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { /* Error Interrupt */ writel(mask, &host->reg->norintsts); printf("%s: error during transfer: 0x%08x\n", __func__, mask); return -1; - } else if (mask & (1 << 3)) { - /* DMA Interrupt */ + } else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { + /* + * DMA Interrupt, restart the transfer where + * it was interrupted. + */ + unsigned int address = readl(&host->reg->sysad); + debug("DMA end\n"); - break; - } else if (mask & (1 << 1)) { + writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT, + &host->reg->norintsts); + writel(address, &host->reg->sysad); + } else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) { /* Transfer Complete */ debug("r/w is done\n"); break; + } else if (get_timer(start) > 2000UL) { + writel(mask, &host->reg->norintsts); + printf("%s: MMC Timeout\n" + " Interrupt status 0x%08x\n" + " Interrupt status enable 0x%08x\n" + " Interrupt signal enable 0x%08x\n" + " Present status 0x%08x\n", + __func__, mask, + readl(&host->reg->norintstsen), + readl(&host->reg->norintsigen), + readl(&host->reg->prnsts)); + return -1; } } writel(mask, &host->reg->norintsts); @@ -310,12 +343,14 @@ static void mmc_change_clock(struct mmc_host *host, uint clock) * ENINTCLK[0] : Internal Clock Enable */ div >>= 1; - clk = (div << 8) | (1 << 0); + clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) | + TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE); writew(clk, &host->reg->clkcon); /* Wait max 10 ms */ timeout = 10; - while (!(readw(&host->reg->clkcon) & (1 << 1))) { + while (!(readw(&host->reg->clkcon) & + TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) { if (timeout == 0) { printf("%s: timeout error\n", __func__); return; @@ -324,7 +359,7 @@ static void mmc_change_clock(struct mmc_host *host, uint clock) udelay(1000); } - clk |= (1 << 2); + clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; writew(clk, &host->reg->clkcon); debug("mmc_change_clock: clkcon = %08X\n", clk); @@ -375,7 +410,7 @@ static void mmc_reset(struct mmc_host *host) * 1 = reset * 0 = work */ - writeb((1 << 0), &host->reg->swrst); + writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &host->reg->swrst); host->clock = 0; @@ -383,7 +418,7 @@ static void mmc_reset(struct mmc_host *host) timeout = 100; /* hw clears the bit when it's done */ - while (readb(&host->reg->swrst) & (1 << 0)) { + while (readb(&host->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { if (timeout == 0) { printf("%s: timeout error\n", __func__); return; @@ -413,12 +448,17 @@ static int mmc_core_init(struct mmc *mmc) * NORMAL Interrupt Status Enable Register init * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable + * [3] ENSTADMAINT : DMA boundary interrupt * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable * [0] ENSTACMDCMPLT : Command Complete Status Enable */ mask = readl(&host->reg->norintstsen); mask &= ~(0xffff); - mask |= (1 << 5) | (1 << 4) | (1 << 1) | (1 << 0); + mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | + TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | + TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT | + TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY | + TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); writel(mask, &host->reg->norintstsen); /* @@ -427,7 +467,7 @@ static int mmc_core_init(struct mmc *mmc) */ mask = readl(&host->reg->norintsigen); mask &= ~(0xffff); - mask |= (1 << 1); + mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE; writel(mask, &host->reg->norintsigen); return 0; @@ -435,14 +475,22 @@ static int mmc_core_init(struct mmc *mmc) static int tegra2_mmc_initialize(int dev_index, int bus_width) { + struct mmc_host *host; struct mmc *mmc; debug(" mmc_initialize called\n"); + host = &mmc_host[dev_index]; + + host->clock = 0; + tegra2_get_setup(host, dev_index); + + clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); + mmc = &mmc_dev[dev_index]; sprintf(mmc->name, "Tegra2 SD/MMC"); - mmc->priv = &mmc_host[dev_index]; + mmc->priv = host; mmc->send_cmd = mmc_send_cmd; mmc->set_ios = mmc_set_ios; mmc->init = mmc_core_init; @@ -465,8 +513,6 @@ static int tegra2_mmc_initialize(int dev_index, int bus_width) mmc->f_min = 375000; mmc->f_max = 48000000; - mmc_host[dev_index].clock = 0; - tegra2_get_setup(&mmc_host[dev_index], dev_index); mmc_register(mmc); return 0; diff --git a/drivers/mmc/tegra2_mmc.h b/drivers/mmc/tegra2_mmc.h index 28698e0..671583c 100644 --- a/drivers/mmc/tegra2_mmc.h +++ b/drivers/mmc/tegra2_mmc.h @@ -68,6 +68,55 @@ struct tegra2_mmc { unsigned char res6[0x100]; /* RESERVED, offset 100h-1FFh */ }; +#define TEGRA_MMC_HOSTCTL_DMASEL_MASK (3 << 3) +#define TEGRA_MMC_HOSTCTL_DMASEL_SDMA (0 << 3) +#define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_32BIT (2 << 3) +#define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_64BIT (3 << 3) + +#define TEGRA_MMC_TRNMOD_DMA_ENABLE (1 << 0) +#define TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE (1 << 1) +#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_WRITE (0 << 4) +#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ (1 << 4) +#define TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT (1 << 5) + +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_MASK (3 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE (0 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136 (1 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48 (2 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY (3 << 0) + +#define TEGRA_MMC_TRNMOD_CMD_CRC_CHECK (1 << 3) +#define TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK (1 << 4) +#define TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER (1 << 5) + +#define TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD (1 << 0) +#define TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT (1 << 1) + +#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE (1 << 0) +#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE (1 << 1) +#define TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE (1 << 2) + +#define TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT 8 +#define TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_MASK (0xff << 8) + +#define TEGRA_MMC_SWRST_SW_RESET_FOR_ALL (1 << 0) +#define TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE (1 << 1) +#define TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE (1 << 2) + +#define TEGRA_MMC_NORINTSTS_CMD_COMPLETE (1 << 0) +#define TEGRA_MMC_NORINTSTS_XFER_COMPLETE (1 << 1) +#define TEGRA_MMC_NORINTSTS_DMA_INTERRUPT (1 << 3) +#define TEGRA_MMC_NORINTSTS_ERR_INTERRUPT (1 << 15) +#define TEGRA_MMC_NORINTSTS_CMD_TIMEOUT (1 << 16) + +#define TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE (1 << 0) +#define TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE (1 << 1) +#define TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT (1 << 3) +#define TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY (1 << 4) +#define TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY (1 << 5) + +#define TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE (1 << 1) + struct mmc_host { struct tegra2_mmc *reg; unsigned int version; /* SDHCI spec. version */ diff --git a/drivers/mtd/dataflash.c b/drivers/mtd/dataflash.c index 96cd395..981ccd5 100644 --- a/drivers/mtd/dataflash.c +++ b/drivers/mtd/dataflash.c @@ -39,7 +39,6 @@ int AT91F_DataflashInit (void) int i, j; int dfcode; int part; - int last_part; int found[CONFIG_SYS_MAX_DATAFLASH_BANKS]; unsigned char protected; @@ -136,7 +135,6 @@ int AT91F_DataflashInit (void) dataflash_info[i].Device.pages_size) - 1; part = 0; - last_part = 0; /* set the area addresses */ for(j = 0; j < NB_DATAFLASH_AREA; j++) { if(found[i]!=0) { @@ -147,7 +145,6 @@ int AT91F_DataflashInit (void) dataflash_info[i].Device.area_list[j].end = dataflash_info[i].end_address + dataflash_info[i].logical_address; - last_part = 1; } else { dataflash_info[i].Device.area_list[j].end = area_list[part].end + diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 1eeba5c..36ee454 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -30,6 +30,9 @@ ifdef CONFIG_SPL_BUILD ifdef CONFIG_SPL_NAND_SIMPLE COBJS-y += nand_spl_simple.o endif +ifdef CONFIG_SPL_NAND_LOAD +COBJS-y += nand_spl_load.o +endif else COBJS-y += nand.o COBJS-y += nand_bbt.o @@ -51,6 +54,7 @@ COBJS-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o COBJS-$(CONFIG_NAND_KMETER1) += kmeter1_nand.o COBJS-$(CONFIG_NAND_MPC5121_NFC) += mpc5121_nfc.o COBJS-$(CONFIG_NAND_MXC) += mxc_nand.o +COBJS-$(CONFIG_NAND_MXS) += mxs_nand.o COBJS-$(CONFIG_NAND_NDFC) += ndfc.o COBJS-$(CONFIG_NAND_NOMADIK) += nomadik.o COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c new file mode 100644 index 0000000..ce2a326 --- /dev/null +++ b/drivers/mtd/nand/mxs_nand.c @@ -0,0 +1,1118 @@ +/* + * Freescale i.MX28 NAND flash driver + * + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * on behalf of DENX Software Engineering GmbH + * + * Based on code from LTIB: + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/types.h> +#include <common.h> +#include <malloc.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/dma.h> + +#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4 + +#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512 +#define MXS_NAND_METADATA_SIZE 10 + +#define MXS_NAND_COMMAND_BUFFER_SIZE 32 + +#define MXS_NAND_BCH_TIMEOUT 10000 + +struct mxs_nand_info { + int cur_chip; + + uint32_t cmd_queue_len; + + uint8_t *cmd_buf; + uint8_t *data_buf; + uint8_t *oob_buf; + + uint8_t marking_block_bad; + uint8_t raw_oob_mode; + + /* Functions with altered behaviour */ + int (*hooked_read_oob)(struct mtd_info *mtd, + loff_t from, struct mtd_oob_ops *ops); + int (*hooked_write_oob)(struct mtd_info *mtd, + loff_t to, struct mtd_oob_ops *ops); + int (*hooked_block_markbad)(struct mtd_info *mtd, + loff_t ofs); + + /* DMA descriptors */ + struct mxs_dma_desc **desc; + uint32_t desc_index; +}; + +struct nand_ecclayout fake_ecc_layout; + +static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info) +{ + struct mxs_dma_desc *desc; + + if (info->desc_index >= MXS_NAND_DMA_DESCRIPTOR_COUNT) { + printf("MXS NAND: Too many DMA descriptors requested\n"); + return NULL; + } + + desc = info->desc[info->desc_index]; + info->desc_index++; + + return desc; +} + +static void mxs_nand_return_dma_descs(struct mxs_nand_info *info) +{ + int i; + struct mxs_dma_desc *desc; + + for (i = 0; i < info->desc_index; i++) { + desc = info->desc[i]; + memset(desc, 0, sizeof(struct mxs_dma_desc)); + desc->address = (dma_addr_t)desc; + } + + info->desc_index = 0; +} + +static uint32_t mxs_nand_ecc_chunk_cnt(uint32_t page_data_size) +{ + return page_data_size / MXS_NAND_CHUNK_DATA_CHUNK_SIZE; +} + +static uint32_t mxs_nand_ecc_size_in_bits(uint32_t ecc_strength) +{ + return ecc_strength * 13; +} + +static uint32_t mxs_nand_aux_status_offset(void) +{ + return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3; +} + +static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size, + uint32_t page_oob_size) +{ + if (page_data_size == 2048) + return 8; + + if (page_data_size == 4096) { + if (page_oob_size == 128) + return 8; + + if (page_oob_size == 218) + return 16; + } + + return 0; +} + +static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size, + uint32_t ecc_strength) +{ + uint32_t chunk_data_size_in_bits; + uint32_t chunk_ecc_size_in_bits; + uint32_t chunk_total_size_in_bits; + uint32_t block_mark_chunk_number; + uint32_t block_mark_chunk_bit_offset; + uint32_t block_mark_bit_offset; + + chunk_data_size_in_bits = MXS_NAND_CHUNK_DATA_CHUNK_SIZE * 8; + chunk_ecc_size_in_bits = mxs_nand_ecc_size_in_bits(ecc_strength); + + chunk_total_size_in_bits = + chunk_data_size_in_bits + chunk_ecc_size_in_bits; + + /* Compute the bit offset of the block mark within the physical page. */ + block_mark_bit_offset = page_data_size * 8; + + /* Subtract the metadata bits. */ + block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8; + + /* + * Compute the chunk number (starting at zero) in which the block mark + * appears. + */ + block_mark_chunk_number = + block_mark_bit_offset / chunk_total_size_in_bits; + + /* + * Compute the bit offset of the block mark within its chunk, and + * validate it. + */ + block_mark_chunk_bit_offset = block_mark_bit_offset - + (block_mark_chunk_number * chunk_total_size_in_bits); + + if (block_mark_chunk_bit_offset > chunk_data_size_in_bits) + return 1; + + /* + * Now that we know the chunk number in which the block mark appears, + * we can subtract all the ECC bits that appear before it. + */ + block_mark_bit_offset -= + block_mark_chunk_number * chunk_ecc_size_in_bits; + + return block_mark_bit_offset; +} + +static uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd) +{ + uint32_t ecc_strength; + ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize); + return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) >> 3; +} + +static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd) +{ + uint32_t ecc_strength; + ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize); + return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) & 0x7; +} + +/* + * Wait for BCH complete IRQ and clear the IRQ + */ +static int mxs_nand_wait_for_bch_complete(void) +{ + struct mx28_bch_regs *bch_regs = (struct mx28_bch_regs *)MXS_BCH_BASE; + int timeout = MXS_NAND_BCH_TIMEOUT; + int ret; + + ret = mx28_wait_mask_set(&bch_regs->hw_bch_ctrl_reg, + BCH_CTRL_COMPLETE_IRQ, timeout); + + writel(BCH_CTRL_COMPLETE_IRQ, &bch_regs->hw_bch_ctrl_clr); + + return ret; +} + +/* + * This is the function that we install in the cmd_ctrl function pointer of the + * owning struct nand_chip. The only functions in the reference implementation + * that use these functions pointers are cmdfunc and select_chip. + * + * In this driver, we implement our own select_chip, so this function will only + * be called by the reference implementation's cmdfunc. For this reason, we can + * ignore the chip enable bit and concentrate only on sending bytes to the NAND + * Flash. + */ +static void mxs_nand_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl) +{ + struct nand_chip *nand = mtd->priv; + struct mxs_nand_info *nand_info = nand->priv; + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + int ret; + + /* + * If this condition is true, something is _VERY_ wrong in MTD + * subsystem! + */ + if (nand_info->cmd_queue_len == MXS_NAND_COMMAND_BUFFER_SIZE) { + printf("MXS NAND: Command queue too long\n"); + return; + } + + /* + * Every operation begins with a command byte and a series of zero or + * more address bytes. These are distinguished by either the Address + * Latch Enable (ALE) or Command Latch Enable (CLE) signals being + * asserted. When MTD is ready to execute the command, it will + * deasert both latch enables. + * + * Rather than run a separate DMA operation for every single byte, we + * queue them up and run a single DMA operation for the entire series + * of command and data bytes. + */ + if (ctrl & (NAND_ALE | NAND_CLE)) { + if (data != NAND_CMD_NONE) + nand_info->cmd_buf[nand_info->cmd_queue_len++] = data; + return; + } + + /* + * If control arrives here, MTD has deasserted both the ALE and CLE, + * which means it's ready to run an operation. Check if we have any + * bytes to send. + */ + if (nand_info->cmd_queue_len == 0) + return; + + /* Compile the DMA descriptor -- a descriptor that sends command. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_CHAIN | MXS_DMA_DESC_DEC_SEM | + MXS_DMA_DESC_WAIT4END | (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | + (nand_info->cmd_queue_len << MXS_DMA_DESC_BYTES_OFFSET); + + d->cmd.address = (dma_addr_t)nand_info->cmd_buf; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_CLE | + GPMI_CTRL0_ADDRESS_INCREMENT | + nand_info->cmd_queue_len; + + mxs_dma_desc_append(channel, d); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) + printf("MXS NAND: Error sending command\n"); + + mxs_nand_return_dma_descs(nand_info); + + /* Reset the command queue. */ + nand_info->cmd_queue_len = 0; +} + +/* + * Test if the NAND flash is ready. + */ +static int mxs_nand_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mxs_nand_info *nand_info = chip->priv; + struct mx28_gpmi_regs *gpmi_regs = + (struct mx28_gpmi_regs *)MXS_GPMI_BASE; + uint32_t tmp; + + tmp = readl(&gpmi_regs->hw_gpmi_stat); + tmp >>= (GPMI_STAT_READY_BUSY_OFFSET + nand_info->cur_chip); + + return tmp & 1; +} + +/* + * Select the NAND chip. + */ +static void mxs_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand = mtd->priv; + struct mxs_nand_info *nand_info = nand->priv; + + nand_info->cur_chip = chip; +} + +/* + * Handle block mark swapping. + * + * Note that, when this function is called, it doesn't know whether it's + * swapping the block mark, or swapping it *back* -- but it doesn't matter + * because the the operation is the same. + */ +static void mxs_nand_swap_block_mark(struct mtd_info *mtd, + uint8_t *data_buf, uint8_t *oob_buf) +{ + uint32_t bit_offset; + uint32_t buf_offset; + + uint32_t src; + uint32_t dst; + + bit_offset = mxs_nand_mark_bit_offset(mtd); + buf_offset = mxs_nand_mark_byte_offset(mtd); + + /* + * Get the byte from the data area that overlays the block mark. Since + * the ECC engine applies its own view to the bits in the page, the + * physical block mark won't (in general) appear on a byte boundary in + * the data. + */ + src = data_buf[buf_offset] >> bit_offset; + src |= data_buf[buf_offset + 1] << (8 - bit_offset); + + dst = oob_buf[0]; + + oob_buf[0] = src; + + data_buf[buf_offset] &= ~(0xff << bit_offset); + data_buf[buf_offset + 1] &= 0xff << bit_offset; + + data_buf[buf_offset] |= dst << bit_offset; + data_buf[buf_offset + 1] |= dst >> (8 - bit_offset); +} + +/* + * Read data from NAND. + */ +static void mxs_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int length) +{ + struct nand_chip *nand = mtd->priv; + struct mxs_nand_info *nand_info = nand->priv; + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + int ret; + + if (length > NAND_MAX_PAGESIZE) { + printf("MXS NAND: DMA buffer too big\n"); + return; + } + + if (!buf) { + printf("MXS NAND: DMA buffer is NULL\n"); + return; + } + + /* Compile the DMA descriptor - a descriptor that reads data. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_DMA_WRITE | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | + (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | + (length << MXS_DMA_DESC_BYTES_OFFSET); + + d->cmd.address = (dma_addr_t)nand_info->data_buf; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_READ | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + length; + + mxs_dma_desc_append(channel, d); + + /* + * A DMA descriptor that waits for the command to end and the chip to + * become ready. + * + * I think we actually should *not* be waiting for the chip to become + * ready because, after all, we don't care. I think the original code + * did that and no one has re-thought it yet. + */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_DEC_SEM | + MXS_DMA_DESC_WAIT4END | (4 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA; + + mxs_dma_desc_append(channel, d); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) { + printf("MXS NAND: DMA read error\n"); + goto rtn; + } + + memcpy(buf, nand_info->data_buf, length); + +rtn: + mxs_nand_return_dma_descs(nand_info); +} + +/* + * Write data to NAND. + */ +static void mxs_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int length) +{ + struct nand_chip *nand = mtd->priv; + struct mxs_nand_info *nand_info = nand->priv; + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + int ret; + + if (length > NAND_MAX_PAGESIZE) { + printf("MXS NAND: DMA buffer too big\n"); + return; + } + + if (!buf) { + printf("MXS NAND: DMA buffer is NULL\n"); + return; + } + + memcpy(nand_info->data_buf, buf, length); + + /* Compile the DMA descriptor - a descriptor that writes data. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | + (4 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | + (length << MXS_DMA_DESC_BYTES_OFFSET); + + d->cmd.address = (dma_addr_t)nand_info->data_buf; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + length; + + mxs_dma_desc_append(channel, d); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) + printf("MXS NAND: DMA write error\n"); + + mxs_nand_return_dma_descs(nand_info); +} + +/* + * Read a single byte from NAND. + */ +static uint8_t mxs_nand_read_byte(struct mtd_info *mtd) +{ + uint8_t buf; + mxs_nand_read_buf(mtd, &buf, 1); + return buf; +} + +/* + * Read a page from NAND. + */ +static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, + uint8_t *buf, int page) +{ + struct mxs_nand_info *nand_info = nand->priv; + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + uint32_t corrected = 0, failed = 0; + uint8_t *status; + int i, ret; + + /* Compile the DMA descriptor - wait for ready. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | + MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END | + (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA; + + mxs_dma_desc_append(channel, d); + + /* Compile the DMA descriptor - enable the BCH block and read. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | + MXS_DMA_DESC_WAIT4END | (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_READ | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + (mtd->writesize + mtd->oobsize); + d->cmd.pio_words[1] = 0; + d->cmd.pio_words[2] = + GPMI_ECCCTRL_ENABLE_ECC | + GPMI_ECCCTRL_ECC_CMD_DECODE | + GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE; + d->cmd.pio_words[3] = mtd->writesize + mtd->oobsize; + d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf; + d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf; + + mxs_dma_desc_append(channel, d); + + /* Compile the DMA descriptor - disable the BCH block. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | + MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END | + (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA | + (mtd->writesize + mtd->oobsize); + d->cmd.pio_words[1] = 0; + d->cmd.pio_words[2] = 0; + + mxs_dma_desc_append(channel, d); + + /* Compile the DMA descriptor - deassert the NAND lock and interrupt. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_DEC_SEM; + + d->cmd.address = 0; + + mxs_dma_desc_append(channel, d); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) { + printf("MXS NAND: DMA read error\n"); + goto rtn; + } + + ret = mxs_nand_wait_for_bch_complete(); + if (ret) { + printf("MXS NAND: BCH read timeout\n"); + goto rtn; + } + + /* Read DMA completed, now do the mark swapping. */ + mxs_nand_swap_block_mark(mtd, nand_info->data_buf, nand_info->oob_buf); + + /* Loop over status bytes, accumulating ECC status. */ + status = nand_info->oob_buf + mxs_nand_aux_status_offset(); + for (i = 0; i < mxs_nand_ecc_chunk_cnt(mtd->writesize); i++) { + if (status[i] == 0x00) + continue; + + if (status[i] == 0xff) + continue; + + if (status[i] == 0xfe) { + failed++; + continue; + } + + corrected += status[i]; + } + + /* Propagate ECC status to the owning MTD. */ + mtd->ecc_stats.failed += failed; + mtd->ecc_stats.corrected += corrected; + + /* + * It's time to deliver the OOB bytes. See mxs_nand_ecc_read_oob() for + * details about our policy for delivering the OOB. + * + * We fill the caller's buffer with set bits, and then copy the block + * mark to the caller's buffer. Note that, if block mark swapping was + * necessary, it has already been done, so we can rely on the first + * byte of the auxiliary buffer to contain the block mark. + */ + memset(nand->oob_poi, 0xff, mtd->oobsize); + + nand->oob_poi[0] = nand_info->oob_buf[0]; + + memcpy(buf, nand_info->data_buf, mtd->writesize); + +rtn: + mxs_nand_return_dma_descs(nand_info); + + return ret; +} + +/* + * Write a page to NAND. + */ +static void mxs_nand_ecc_write_page(struct mtd_info *mtd, + struct nand_chip *nand, const uint8_t *buf) +{ + struct mxs_nand_info *nand_info = nand->priv; + struct mxs_dma_desc *d; + uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; + int ret; + + memcpy(nand_info->data_buf, buf, mtd->writesize); + memcpy(nand_info->oob_buf, nand->oob_poi, mtd->oobsize); + + /* Handle block mark swapping. */ + mxs_nand_swap_block_mark(mtd, nand_info->data_buf, nand_info->oob_buf); + + /* Compile the DMA descriptor - write data. */ + d = mxs_nand_get_dma_desc(nand_info); + d->cmd.data = + MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | + MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | + (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + + d->cmd.address = 0; + + d->cmd.pio_words[0] = + GPMI_CTRL0_COMMAND_MODE_WRITE | + GPMI_CTRL0_WORD_LENGTH | + (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) | + GPMI_CTRL0_ADDRESS_NAND_DATA; + d->cmd.pio_words[1] = 0; + d->cmd.pio_words[2] = + GPMI_ECCCTRL_ENABLE_ECC | + GPMI_ECCCTRL_ECC_CMD_ENCODE | + GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE; + d->cmd.pio_words[3] = (mtd->writesize + mtd->oobsize); + d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf; + d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf; + + mxs_dma_desc_append(channel, d); + + /* Execute the DMA chain. */ + ret = mxs_dma_go(channel); + if (ret) { + printf("MXS NAND: DMA write error\n"); + goto rtn; + } + + ret = mxs_nand_wait_for_bch_complete(); + if (ret) { + printf("MXS NAND: BCH write timeout\n"); + goto rtn; + } + +rtn: + mxs_nand_return_dma_descs(nand_info); +} + +/* + * Read OOB from NAND. + * + * This function is a veneer that replaces the function originally installed by + * the NAND Flash MTD code. + */ +static int mxs_nand_hook_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + struct mxs_nand_info *nand_info = chip->priv; + int ret; + + if (ops->mode == MTD_OOB_RAW) + nand_info->raw_oob_mode = 1; + else + nand_info->raw_oob_mode = 0; + + ret = nand_info->hooked_read_oob(mtd, from, ops); + + nand_info->raw_oob_mode = 0; + + return ret; +} + +/* + * Write OOB to NAND. + * + * This function is a veneer that replaces the function originally installed by + * the NAND Flash MTD code. + */ +static int mxs_nand_hook_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct nand_chip *chip = mtd->priv; + struct mxs_nand_info *nand_info = chip->priv; + int ret; + + if (ops->mode == MTD_OOB_RAW) + nand_info->raw_oob_mode = 1; + else + nand_info->raw_oob_mode = 0; + + ret = nand_info->hooked_write_oob(mtd, to, ops); + + nand_info->raw_oob_mode = 0; + + return ret; +} + +/* + * Mark a block bad in NAND. + * + * This function is a veneer that replaces the function originally installed by + * the NAND Flash MTD code. + */ +static int mxs_nand_hook_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + struct mxs_nand_info *nand_info = chip->priv; + int ret; + + nand_info->marking_block_bad = 1; + + ret = nand_info->hooked_block_markbad(mtd, ofs); + + nand_info->marking_block_bad = 0; + + return ret; +} + +/* + * There are several places in this driver where we have to handle the OOB and + * block marks. This is the function where things are the most complicated, so + * this is where we try to explain it all. All the other places refer back to + * here. + * + * These are the rules, in order of decreasing importance: + * + * 1) Nothing the caller does can be allowed to imperil the block mark, so all + * write operations take measures to protect it. + * + * 2) In read operations, the first byte of the OOB we return must reflect the + * true state of the block mark, no matter where that block mark appears in + * the physical page. + * + * 3) ECC-based read operations return an OOB full of set bits (since we never + * allow ECC-based writes to the OOB, it doesn't matter what ECC-based reads + * return). + * + * 4) "Raw" read operations return a direct view of the physical bytes in the + * page, using the conventional definition of which bytes are data and which + * are OOB. This gives the caller a way to see the actual, physical bytes + * in the page, without the distortions applied by our ECC engine. + * + * What we do for this specific read operation depends on whether we're doing + * "raw" read, or an ECC-based read. + * + * It turns out that knowing whether we want an "ECC-based" or "raw" read is not + * easy. When reading a page, for example, the NAND Flash MTD code calls our + * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an + * ECC-based or raw view of the page is implicit in which function it calls + * (there is a similar pair of ECC-based/raw functions for writing). + * + * Since MTD assumes the OOB is not covered by ECC, there is no pair of + * ECC-based/raw functions for reading or or writing the OOB. The fact that the + * caller wants an ECC-based or raw view of the page is not propagated down to + * this driver. + * + * Since our OOB *is* covered by ECC, we need this information. So, we hook the + * ecc.read_oob and ecc.write_oob function pointers in the owning + * struct mtd_info with our own functions. These hook functions set the + * raw_oob_mode field so that, when control finally arrives here, we'll know + * what to do. + */ +static int mxs_nand_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page, int cmd) +{ + struct mxs_nand_info *nand_info = nand->priv; + + /* + * First, fill in the OOB buffer. If we're doing a raw read, we need to + * get the bytes from the physical page. If we're not doing a raw read, + * we need to fill the buffer with set bits. + */ + if (nand_info->raw_oob_mode) { + /* + * If control arrives here, we're doing a "raw" read. Send the + * command to read the conventional OOB and read it. + */ + nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); + nand->read_buf(mtd, nand->oob_poi, mtd->oobsize); + } else { + /* + * If control arrives here, we're not doing a "raw" read. Fill + * the OOB buffer with set bits and correct the block mark. + */ + memset(nand->oob_poi, 0xff, mtd->oobsize); + + nand->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page); + mxs_nand_read_buf(mtd, nand->oob_poi, 1); + } + + return 0; + +} + +/* + * Write OOB data to NAND. + */ +static int mxs_nand_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *nand, + int page) +{ + struct mxs_nand_info *nand_info = nand->priv; + uint8_t block_mark = 0; + + /* + * There are fundamental incompatibilities between the i.MX GPMI NFC and + * the NAND Flash MTD model that make it essentially impossible to write + * the out-of-band bytes. + * + * We permit *ONE* exception. If the *intent* of writing the OOB is to + * mark a block bad, we can do that. + */ + + if (!nand_info->marking_block_bad) { + printf("NXS NAND: Writing OOB isn't supported\n"); + return -EIO; + } + + /* Write the block mark. */ + nand->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); + nand->write_buf(mtd, &block_mark, 1); + nand->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + /* Check if it worked. */ + if (nand->waitfunc(mtd, nand) & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} + +/* + * Claims all blocks are good. + * + * In principle, this function is *only* called when the NAND Flash MTD system + * isn't allowed to keep an in-memory bad block table, so it is forced to ask + * the driver for bad block information. + * + * In fact, we permit the NAND Flash MTD system to have an in-memory BBT, so + * this function is *only* called when we take it away. + * + * Thus, this function is only called when we want *all* blocks to look good, + * so it *always* return success. + */ +static int mxs_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) +{ + return 0; +} + +/* + * Nominally, the purpose of this function is to look for or create the bad + * block table. In fact, since the we call this function at the very end of + * the initialization process started by nand_scan(), and we doesn't have a + * more formal mechanism, we "hook" this function to continue init process. + * + * At this point, the physical NAND Flash chips have been identified and + * counted, so we know the physical geometry. This enables us to make some + * important configuration decisions. + * + * The return value of this function propogates directly back to this driver's + * call to nand_scan(). Anything other than zero will cause this driver to + * tear everything down and declare failure. + */ +static int mxs_nand_scan_bbt(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + struct mxs_nand_info *nand_info = nand->priv; + struct mx28_bch_regs *bch_regs = (struct mx28_bch_regs *)MXS_BCH_BASE; + uint32_t tmp; + + /* Configure BCH and set NFC geometry */ + mx28_reset_block(&bch_regs->hw_bch_ctrl_reg); + + /* Configure layout 0 */ + tmp = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1) + << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET; + tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET; + tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) + << BCH_FLASHLAYOUT0_ECC0_OFFSET; + tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE; + writel(tmp, &bch_regs->hw_bch_flash0layout0); + + tmp = (mtd->writesize + mtd->oobsize) + << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET; + tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) + << BCH_FLASHLAYOUT1_ECCN_OFFSET; + tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE; + writel(tmp, &bch_regs->hw_bch_flash0layout1); + + /* Set *all* chip selects to use layout 0 */ + writel(0, &bch_regs->hw_bch_layoutselect); + + /* Enable BCH complete interrupt */ + writel(BCH_CTRL_COMPLETE_IRQ_EN, &bch_regs->hw_bch_ctrl_set); + + /* Hook some operations at the MTD level. */ + if (mtd->read_oob != mxs_nand_hook_read_oob) { + nand_info->hooked_read_oob = mtd->read_oob; + mtd->read_oob = mxs_nand_hook_read_oob; + } + + if (mtd->write_oob != mxs_nand_hook_write_oob) { + nand_info->hooked_write_oob = mtd->write_oob; + mtd->write_oob = mxs_nand_hook_write_oob; + } + + if (mtd->block_markbad != mxs_nand_hook_block_markbad) { + nand_info->hooked_block_markbad = mtd->block_markbad; + mtd->block_markbad = mxs_nand_hook_block_markbad; + } + + /* We use the reference implementation for bad block management. */ + return nand_default_bbt(mtd); +} + +/* + * Allocate DMA buffers + */ +int mxs_nand_alloc_buffers(struct mxs_nand_info *nand_info) +{ + uint8_t *buf; + const int size = NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE; + + /* DMA buffers */ + buf = memalign(MXS_DMA_ALIGNMENT, size); + if (!buf) { + printf("MXS NAND: Error allocating DMA buffers\n"); + return -ENOMEM; + } + + memset(buf, 0, size); + + nand_info->data_buf = buf; + nand_info->oob_buf = buf + NAND_MAX_PAGESIZE; + + /* Command buffers */ + nand_info->cmd_buf = memalign(MXS_DMA_ALIGNMENT, + MXS_NAND_COMMAND_BUFFER_SIZE); + if (!nand_info->cmd_buf) { + free(buf); + printf("MXS NAND: Error allocating command buffers\n"); + return -ENOMEM; + } + memset(nand_info->cmd_buf, 0, MXS_NAND_COMMAND_BUFFER_SIZE); + nand_info->cmd_queue_len = 0; + + return 0; +} + +/* + * Initializes the NFC hardware. + */ +int mxs_nand_init(struct mxs_nand_info *info) +{ + struct mx28_gpmi_regs *gpmi_regs = + (struct mx28_gpmi_regs *)MXS_GPMI_BASE; + int i = 0; + + info->desc = malloc(sizeof(struct mxs_dma_desc *) * + MXS_NAND_DMA_DESCRIPTOR_COUNT); + if (!info->desc) + goto err1; + + /* Allocate the DMA descriptors. */ + for (i = 0; i < MXS_NAND_DMA_DESCRIPTOR_COUNT; i++) { + info->desc[i] = mxs_dma_desc_alloc(); + if (!info->desc[i]) + goto err2; + } + + /* Init the DMA controller. */ + mxs_dma_init(); + + /* Reset the GPMI block. */ + mx28_reset_block(&gpmi_regs->hw_gpmi_ctrl0_reg); + + /* + * Choose NAND mode, set IRQ polarity, disable write protection and + * select BCH ECC. + */ + clrsetbits_le32(&gpmi_regs->hw_gpmi_ctrl1, + GPMI_CTRL1_GPMI_MODE, + GPMI_CTRL1_ATA_IRQRDY_POLARITY | GPMI_CTRL1_DEV_RESET | + GPMI_CTRL1_BCH_MODE); + + return 0; + +err2: + free(info->desc); +err1: + for (--i; i >= 0; i--) + mxs_dma_desc_free(info->desc[i]); + printf("MXS NAND: Unable to allocate DMA descriptors\n"); + return -ENOMEM; +} + +/*! + * This function is called during the driver binding process. + * + * @param pdev the device structure used to store device specific + * information that is used by the suspend, resume and + * remove functions + * + * @return The function always returns 0. + */ +int board_nand_init(struct nand_chip *nand) +{ + struct mxs_nand_info *nand_info; + int err; + + nand_info = malloc(sizeof(struct mxs_nand_info)); + if (!nand_info) { + printf("MXS NAND: Failed to allocate private data\n"); + return -ENOMEM; + } + memset(nand_info, 0, sizeof(struct mxs_nand_info)); + + err = mxs_nand_alloc_buffers(nand_info); + if (err) + goto err1; + + err = mxs_nand_init(nand_info); + if (err) + goto err2; + + memset(&fake_ecc_layout, 0, sizeof(fake_ecc_layout)); + + nand->priv = nand_info; + nand->options |= NAND_NO_SUBPAGE_WRITE; + + nand->cmd_ctrl = mxs_nand_cmd_ctrl; + + nand->dev_ready = mxs_nand_device_ready; + nand->select_chip = mxs_nand_select_chip; + nand->block_bad = mxs_nand_block_bad; + nand->scan_bbt = mxs_nand_scan_bbt; + + nand->read_byte = mxs_nand_read_byte; + + nand->read_buf = mxs_nand_read_buf; + nand->write_buf = mxs_nand_write_buf; + + nand->ecc.read_page = mxs_nand_ecc_read_page; + nand->ecc.write_page = mxs_nand_ecc_write_page; + nand->ecc.read_oob = mxs_nand_ecc_read_oob; + nand->ecc.write_oob = mxs_nand_ecc_write_oob; + + nand->ecc.layout = &fake_ecc_layout; + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.bytes = 9; + nand->ecc.size = 512; + + return 0; + +err2: + free(nand_info->data_buf); + free(nand_info->cmd_buf); +err1: + free(nand_info); + return err; +} diff --git a/drivers/mtd/nand/nand_spl_load.c b/drivers/mtd/nand/nand_spl_load.c new file mode 100644 index 0000000..ae8d5ac --- /dev/null +++ b/drivers/mtd/nand/nand_spl_load.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2011 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <nand.h> + +/* + * The main entry for NAND booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from NAND into SDRAM and starts it from there. + */ +void nand_boot(void) +{ + int ret; + __attribute__((noreturn)) void (*uboot)(void); + + /* + * Load U-Boot image from NAND into RAM + */ + ret = nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS, + CONFIG_SYS_NAND_U_BOOT_SIZE, + (void *)CONFIG_SYS_NAND_U_BOOT_DST); + +#ifdef CONFIG_NAND_ENV_DST + ret = nand_spl_load_image(CONFIG_ENV_OFFSET, CONFIG_ENV_SIZE, + (void *)CONFIG_NAND_ENV_DST); + +#ifdef CONFIG_ENV_OFFSET_REDUND + ret = nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, CONFIG_ENV_SIZE, + (void *)CONFIG_NAND_ENV_DST + CONFIG_ENV_SIZE); +#endif +#endif + + /* + * Jump to U-Boot image + */ + uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; + (*uboot)(); +} diff --git a/drivers/mtd/nand/nand_spl_simple.c b/drivers/mtd/nand/nand_spl_simple.c index 71491d4..e5003e6 100644 --- a/drivers/mtd/nand/nand_spl_simple.c +++ b/drivers/mtd/nand/nand_spl_simple.c @@ -140,6 +140,47 @@ static int nand_is_bad_block(int block) return 0; } +#if defined(CONFIG_SYS_NAND_HW_ECC_OOBFIRST) +static int nand_read_page(int block, int page, uchar *dst) +{ + struct nand_chip *this = mtd.priv; + u_char *ecc_calc; + u_char *ecc_code; + u_char *oob_data; + int i; + int eccsize = CONFIG_SYS_NAND_ECCSIZE; + int eccbytes = CONFIG_SYS_NAND_ECCBYTES; + int eccsteps = CONFIG_SYS_NAND_ECCSTEPS; + uint8_t *p = dst; + int stat; + + /* + * No malloc available for now, just use some temporary locations + * in SDRAM + */ + ecc_calc = (u_char *)(CONFIG_SYS_SDRAM_BASE + 0x10000); + ecc_code = ecc_calc + 0x100; + oob_data = ecc_calc + 0x200; + + nand_command(block, page, 0, NAND_CMD_READOOB); + this->read_buf(&mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); + nand_command(block, page, 0, NAND_CMD_READ0); + + /* Pick the ECC bytes out of the oob data */ + for (i = 0; i < CONFIG_SYS_NAND_ECCTOTAL; i++) + ecc_code[i] = oob_data[nand_ecc_pos[i]]; + + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + this->ecc.hwctl(&mtd, NAND_ECC_READ); + this->read_buf(&mtd, p, eccsize); + this->ecc.calculate(&mtd, p, &ecc_calc[i]); + stat = this->ecc.correct(&mtd, p, &ecc_code[i], &ecc_calc[i]); + } + + return 0; +} +#else static int nand_read_page(int block, int page, void *dst) { struct nand_chip *this = mtd.priv; @@ -186,6 +227,7 @@ static int nand_read_page(int block, int page, void *dst) return 0; } +#endif int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) { @@ -230,7 +272,6 @@ void nand_init(void) mtd.priv = &nand_chip; nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = (void __iomem *)CONFIG_SYS_NAND_BASE; - nand_chip.options = 0; board_nand_init(&nand_chip); if (nand_chip.select_chip) diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile index b984bd4..b090d40 100644 --- a/drivers/mtd/onenand/Makefile +++ b/drivers/mtd/onenand/Makefile @@ -25,8 +25,12 @@ include $(TOPDIR)/config.mk LIB := $(obj)libonenand.o +ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_CMD_ONENAND) := onenand_uboot.o onenand_base.o onenand_bbt.o COBJS-$(CONFIG_SAMSUNG_ONENAND) += samsung.o +else +COBJS-y := onenand_spl.o +endif COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 24e02c2..06f187f 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1943,16 +1943,10 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int { struct onenand_chip *this = mtd->priv; int start, end, block, value, status; - int wp_status_mask; start = onenand_block(this, ofs); end = onenand_block(this, ofs + len); - if (cmd == ONENAND_CMD_LOCK) - wp_status_mask = ONENAND_WP_LS; - else - wp_status_mask = ONENAND_WP_US; - /* Continuous lock scheme */ if (this->options & ONENAND_HAS_CONT_LOCK) { /* Set start block address */ @@ -2226,19 +2220,21 @@ static const struct onenand_manufacturers onenand_manuf_ids[] = { static int onenand_check_maf(int manuf) { int size = ARRAY_SIZE(onenand_manuf_ids); - char *name; int i; +#ifdef ONENAND_DEBUG + char *name; +#endif for (i = 0; i < size; i++) if (manuf == onenand_manuf_ids[i].id) break; +#ifdef ONENAND_DEBUG if (i < size) name = onenand_manuf_ids[i].name; else name = "Unknown"; -#ifdef ONENAND_DEBUG printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf); #endif @@ -2255,7 +2251,7 @@ static int flexonenand_get_boundary(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; unsigned int die, bdry; - int ret, syscfg, locked; + int syscfg, locked; /* Disable ECC */ syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); @@ -2266,7 +2262,7 @@ static int flexonenand_get_boundary(struct mtd_info *mtd) this->wait(mtd, FL_SYNCING); this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); - ret = this->wait(mtd, FL_READING); + this->wait(mtd, FL_READING); bdry = this->read_word(this->base + ONENAND_DATARAM); if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3) @@ -2276,7 +2272,7 @@ static int flexonenand_get_boundary(struct mtd_info *mtd) this->boundary[die] = bdry & FLEXONENAND_PI_MASK; this->command(mtd, ONENAND_CMD_RESET, 0, 0); - ret = this->wait(mtd, FL_RESETING); + this->wait(mtd, FL_RESETING); printk(KERN_INFO "Die %d boundary: %d%s\n", die, this->boundary[die], locked ? "(Locked)" : "(Unlocked)"); diff --git a/drivers/mtd/onenand/onenand_spl.c b/drivers/mtd/onenand/onenand_spl.c new file mode 100644 index 0000000..50eaa71 --- /dev/null +++ b/drivers/mtd/onenand/onenand_spl.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * + * Based on code: + * Copyright (C) 2005-2009 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <linux/mtd/onenand_regs.h> +#include <onenand_uboot.h> + +/* + * Device geometry: + * - 2048b page, 128k erase block. + * - 4096b page, 256k erase block. + */ +enum onenand_spl_pagesize { + PAGE_2K = 2048, + PAGE_4K = 4096, +}; + +#define ONENAND_PAGES_PER_BLOCK 64 +#define onenand_block_address(block) (block) +#define onenand_sector_address(page) (page << 2) +#define onenand_buffer_address() ((1 << 3) << 8) +#define onenand_bufferram_address(block) (0) + +static inline uint16_t onenand_readw(uint32_t addr) +{ + return readw(CONFIG_SYS_ONENAND_BASE + addr); +} + +static inline void onenand_writew(uint16_t value, uint32_t addr) +{ + writew(value, CONFIG_SYS_ONENAND_BASE + addr); +} + +static enum onenand_spl_pagesize onenand_spl_get_geometry(void) +{ + uint32_t dev_id, density; + + if (!onenand_readw(ONENAND_REG_TECHNOLOGY)) { + dev_id = onenand_readw(ONENAND_REG_DEVICE_ID); + density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; + density &= ONENAND_DEVICE_DENSITY_MASK; + + if (density < ONENAND_DEVICE_DENSITY_4Gb) + return PAGE_2K; + + if (dev_id & ONENAND_DEVICE_IS_DDP) + return PAGE_2K; + } + + return PAGE_4K; +} + +static int onenand_spl_read_page(uint32_t block, uint32_t page, uint32_t *buf, + enum onenand_spl_pagesize pagesize) +{ + const uint32_t addr = CONFIG_SYS_ONENAND_BASE + ONENAND_DATARAM; + uint32_t offset; + + onenand_writew(onenand_block_address(block), + ONENAND_REG_START_ADDRESS1); + + onenand_writew(onenand_bufferram_address(block), + ONENAND_REG_START_ADDRESS2); + + onenand_writew(onenand_sector_address(page), + ONENAND_REG_START_ADDRESS8); + + onenand_writew(onenand_buffer_address(), + ONENAND_REG_START_BUFFER); + + onenand_writew(ONENAND_INT_CLEAR, ONENAND_REG_INTERRUPT); + + onenand_writew(ONENAND_CMD_READ, ONENAND_REG_COMMAND); + + while (!(onenand_readw(ONENAND_REG_INTERRUPT) & ONENAND_INT_READ)) + continue; + + /* Check for invalid block mark */ + if (page < 2 && (onenand_readw(ONENAND_SPARERAM) != 0xffff)) + return 1; + + for (offset = 0; offset < pagesize; offset += 4) + buf[offset / 4] = readl(addr + offset); + + return 0; +} + +void onenand_spl_load_image(uint32_t offs, uint32_t size, void *dst) +{ + uint32_t *addr = (uint32_t *)dst; + uint32_t total_pages; + uint32_t block; + uint32_t page, rpage; + enum onenand_spl_pagesize pagesize; + int ret; + + pagesize = onenand_spl_get_geometry(); + + /* + * The page can be either 2k or 4k, avoid using DIV_ROUND_UP to avoid + * pulling further unwanted functions into the SPL. + */ + if (pagesize == 2048) { + total_pages = DIV_ROUND_UP(size, 2048); + page = offs / 2048; + } else { + total_pages = DIV_ROUND_UP(size, 4096); + page = offs / 4096; + } + + for (; page <= total_pages; page++) { + block = page / ONENAND_PAGES_PER_BLOCK; + rpage = page & (ONENAND_PAGES_PER_BLOCK - 1); + ret = onenand_spl_read_page(block, rpage, addr, pagesize); + if (ret) { + total_pages += ONENAND_PAGES_PER_BLOCK; + page += ONENAND_PAGES_PER_BLOCK - 1; + } else { + addr += pagesize / 4; + } + } +} diff --git a/drivers/net/4xx_enet.c b/drivers/net/4xx_enet.c index 8013ad9..73700dd 100644 --- a/drivers/net/4xx_enet.c +++ b/drivers/net/4xx_enet.c @@ -92,6 +92,7 @@ #include <asm/ppc4xx-mal.h> #include <miiphy.h> #include <malloc.h> +#include <linux/compiler.h> #if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) #error "CONFIG_MII has to be defined!" @@ -872,7 +873,7 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis) defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ defined(CONFIG_405EX) - int ethgroup = -1; + __maybe_unused int ethgroup = -1; #endif #endif u32 bd_cached; @@ -1769,7 +1770,6 @@ static void emac_err (struct eth_device *dev, unsigned long isr) *-----------------------------------------------------------------------------*/ static void enet_rcv (struct eth_device *dev, unsigned long malisr) { - struct enet_frame *ef_ptr; unsigned long data_len; unsigned long rx_eob_isr; EMAC_4XX_HW_PST hw_p = dev->priv; @@ -1828,8 +1828,6 @@ static void enet_rcv (struct eth_device *dev, unsigned long malisr) } else { hw_p->stats.rx_frames++; hw_p->stats.rx += data_len; - ef_ptr = (struct enet_frame *) hw_p->rx[i]. - data_ptr; #ifdef INFO_4XX_ENET hw_p->stats.pkts_rx++; #endif diff --git a/drivers/net/at91_emac.c b/drivers/net/at91_emac.c index 97d2739..9bda1fc 100644 --- a/drivers/net/at91_emac.c +++ b/drivers/net/at91_emac.c @@ -474,11 +474,9 @@ static int at91emac_recv(struct eth_device *netdev) static int at91emac_write_hwaddr(struct eth_device *netdev) { - emac_device *dev; at91_emac_t *emac; at91_pmc_t *pmc = (at91_pmc_t *) ATMEL_BASE_PMC; emac = (at91_emac_t *) netdev->iobase; - dev = (emac_device *) netdev->priv; writel(1 << ATMEL_ID_EMAC, &pmc->pcer); DEBUG_AT91EMAC("init MAC-ADDR %02x:%02x:%02x:%02x:%02x:%02x\n", diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index 7dacb23..36c33af 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -53,6 +53,11 @@ unsigned int emac_dbg = 0; #define emac_gigabit_enable(phy_addr) /* no gigabit to enable */ #endif +#if !defined(CONFIG_SYS_EMAC_TI_CLKDIV) +#define CONFIG_SYS_EMAC_TI_CLKDIV ((EMAC_MDIO_BUS_FREQ / \ + EMAC_MDIO_CLOCK_FREQ) - 1) +#endif + static void davinci_eth_mdio_enable(void); static int gen_init_phy(int phy_addr); @@ -80,15 +85,17 @@ static int emac_rx_queue_active = 0; /* Receive packet buffers */ static unsigned char emac_rx_buffers[EMAC_MAX_RX_BUFFERS * (EMAC_MAX_ETHERNET_PKT_SIZE + EMAC_PKT_ALIGN)]; -#define MAX_PHY 3 +#ifndef CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT +#define CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT 3 +#endif /* PHY address for a discovered PHY (0xff - not found) */ -static u_int8_t active_phy_addr[MAX_PHY] = { 0xff, 0xff, 0xff }; +static u_int8_t active_phy_addr[CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT]; /* number of PHY found active */ static u_int8_t num_phy; -phy_t phy[MAX_PHY]; +phy_t phy[CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT]; static int davinci_eth_set_mac_addr(struct eth_device *dev) { @@ -131,7 +138,7 @@ static void davinci_eth_mdio_enable(void) { u_int32_t clkdiv; - clkdiv = (EMAC_MDIO_BUS_FREQ / EMAC_MDIO_CLOCK_FREQ) - 1; + clkdiv = CONFIG_SYS_EMAC_TI_CLKDIV; writel((clkdiv & 0xff) | MDIO_CONTROL_ENABLE | @@ -155,9 +162,8 @@ static int davinci_eth_phy_detect(void) int j; unsigned int count = 0; - active_phy_addr[0] = 0xff; - active_phy_addr[1] = 0xff; - active_phy_addr[2] = 0xff; + for (i = 0; i < CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT; i++) + active_phy_addr[i] = 0xff; udelay(1000); phy_act_state = readl(&adap_mdio->ALIVE); @@ -170,7 +176,14 @@ static int davinci_eth_phy_detect(void) for (i = 0, j = 0; i < 32; i++) if (phy_act_state & (1 << i)) { count++; - active_phy_addr[j++] = i; + if (count < CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT) { + active_phy_addr[j++] = i; + } else { + printf("%s: to many PHYs detected.\n", + __func__); + count = 0; + break; + } } num_phy = count; @@ -473,7 +486,7 @@ static int davinci_eth_open(struct eth_device *dev, bd_t *bis) #endif /* Init MDIO & get link state */ - clkdiv = (EMAC_MDIO_BUS_FREQ / EMAC_MDIO_CLOCK_FREQ) - 1; + clkdiv = CONFIG_SYS_EMAC_TI_CLKDIV; writel((clkdiv & 0xff) | MDIO_CONTROL_ENABLE | MDIO_CONTROL_FAULT, &adap_mdio->CONTROL); @@ -747,7 +760,7 @@ int davinci_emac_initialize(void) if (!ret) return(0); else - printf(" %d ETH PHY detected\n", ret); + debug_emac(" %d ETH PHY detected\n", ret); /* Get PHY ID and initialize phy_ops for a detected PHY */ for (i = 0; i < num_phy; i++) { @@ -809,7 +822,7 @@ int davinci_emac_initialize(void) phy[i].auto_negotiate = gen_auto_negotiate; } - debug("Ethernet PHY: %s\n", phy.name); + debug("Ethernet PHY: %s\n", phy[i].name); miiphy_register(phy[i].name, davinci_mii_phy_read, davinci_mii_phy_write); diff --git a/drivers/net/e1000.c b/drivers/net/e1000.c index c86bf0a..6b71bd9 100644 --- a/drivers/net/e1000.c +++ b/drivers/net/e1000.c @@ -884,6 +884,7 @@ static int e1000_validate_eeprom_checksum(struct e1000_hw *hw) } /* Compute the checksum */ + checksum = 0; for (i = 0; i < EEPROM_CHECKSUM_REG; i++) checksum += buf[i]; checksum = ((uint16_t)EEPROM_SUM) - checksum; @@ -1369,7 +1370,6 @@ e1000_reset_hw(struct e1000_hw *hw) { uint32_t ctrl; uint32_t ctrl_ext; - uint32_t icr; uint32_t manc; uint32_t pba = 0; @@ -1442,7 +1442,7 @@ e1000_reset_hw(struct e1000_hw *hw) E1000_WRITE_REG(hw, IMC, 0xffffffff); /* Clear any pending interrupt events. */ - icr = E1000_READ_REG(hw, ICR); + E1000_READ_REG(hw, ICR); /* If MWI was previously enabled, reenable it. */ if (hw->mac_type == e1000_82542_rev2_0) { @@ -4446,7 +4446,8 @@ e1000_phy_init_script(struct e1000_hw *hw) mdelay(20); /* Now enable the transmitter */ - e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); + if (!ret_val) + e1000_write_phy_reg(hw, 0x2F5B, phy_saved_data); if (hw->mac_type == e1000_82547) { uint16_t fused, fine, coarse; diff --git a/drivers/net/e1000.h b/drivers/net/e1000.h index 05f2bce..fd1d8f8 100644 --- a/drivers/net/e1000.h +++ b/drivers/net/e1000.h @@ -62,15 +62,15 @@ /* I/O wrapper functions */ #define E1000_WRITE_REG(a, reg, value) \ - (writel((value), ((a)->hw_addr + E1000_##reg))) + writel((value), ((a)->hw_addr + E1000_##reg)) #define E1000_READ_REG(a, reg) \ - (readl((a)->hw_addr + E1000_##reg)) + readl((a)->hw_addr + E1000_##reg) #define E1000_WRITE_REG_ARRAY(a, reg, offset, value) \ - (writel((value), ((a)->hw_addr + E1000_##reg + ((offset) << 2)))) + writel((value), ((a)->hw_addr + E1000_##reg + ((offset) << 2))) #define E1000_READ_REG_ARRAY(a, reg, offset) \ - (readl((a)->hw_addr + E1000_##reg + ((offset) << 2))) + readl((a)->hw_addr + E1000_##reg + ((offset) << 2)) #define E1000_WRITE_FLUSH(a) \ - do { uint32_t x = E1000_READ_REG(a, STATUS); } while (0) + do { E1000_READ_REG(a, STATUS); } while (0) /* Forward declarations of structures used by the shared code */ struct e1000_hw; @@ -1678,14 +1678,6 @@ struct e1000_hw { #define EEPROM_EWEN_OPCODE 0x13 /* EERPOM erase/write enable */ #define EEPROM_EWDS_OPCODE 0x10 /* EERPOM erast/write disable */ -/* EEPROM Word Offsets */ -#define EEPROM_COMPAT 0x0003 -#define EEPROM_ID_LED_SETTINGS 0x0004 -#define EEPROM_INIT_CONTROL1_REG 0x000A -#define EEPROM_INIT_CONTROL2_REG 0x000F -#define EEPROM_FLASH_VERSION 0x0032 -#define EEPROM_CHECKSUM_REG 0x003F - /* Word definitions for ID LED Settings */ #define ID_LED_RESERVED_0000 0x0000 #define ID_LED_RESERVED_FFFF 0xFFFF @@ -2479,7 +2471,6 @@ struct e1000_hw { #define ADVERTISE_100_FULL 0x0008 #define ADVERTISE_1000_HALF 0x0010 #define ADVERTISE_1000_FULL 0x0020 -#define AUTONEG_ADVERTISE_SPEED_DEFAULT 0x002F /* Everything but 1000-Half */ #define ICH_FLASH_GFPREG 0x0000 #define ICH_FLASH_HSFSTS 0x0004 @@ -2504,7 +2495,6 @@ struct e1000_hw { #define ICH_GFPREG_BASE_MASK 0x1FFF #define ICH_FLASH_LINEAR_ADDR_MASK 0x00FFFFFF -#define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ #define E1000_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */ /* SPI EEPROM Status Register */ @@ -2599,7 +2589,6 @@ struct e1000_hw { #define PHY_CFG_TIMEOUT 100 #define DEFAULT_80003ES2LAN_TIPG_IPGT_10_100 0x00000009 #define DEFAULT_80003ES2LAN_TIPG_IPGT_1000 0x00000008 -#define E1000_TXDMAC_DPP 0x00000001 #define AUTO_ALL_MODES 0 #ifndef E1000_MASTER_SLAVE diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index 86709a7..07ec34c 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -923,7 +923,6 @@ static void purge_tx_ring (struct eth_device *dev) static void read_hw_addr (struct eth_device *dev, bd_t * bis) { - u16 eeprom[0x40]; u16 sum = 0; int i, j; int addr_len = read_eeprom (dev, 0, 6) == 0xffff ? 8 : 6; @@ -931,7 +930,6 @@ static void read_hw_addr (struct eth_device *dev, bd_t * bis) for (j = 0, i = 0; i < 0x40; i++) { u16 value = read_eeprom (dev, i, addr_len); - eeprom[i] = value; sum += value; if (i < 3) { dev->enetaddr[j++] = value; diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c index d55cacd..e2011ae 100644 --- a/drivers/net/enc28j60.c +++ b/drivers/net/enc28j60.c @@ -432,7 +432,6 @@ static void enc_receive(enc_dev_t *enc) u16 pkt_len; u16 copy_len; u16 status; - u8 eir_reg; u8 pkt_cnt = 0; u16 rxbuf_rdpt; u8 hbuf[6]; @@ -476,7 +475,7 @@ static void enc_receive(enc_dev_t *enc) /* read pktcnt */ pkt_cnt = enc_r8(enc, CTL_REG_EPKTCNT); if (copy_len == 0) { - eir_reg = enc_r8(enc, CTL_REG_EIR); + (void)enc_r8(enc, CTL_REG_EIR); enc_reset_rx(enc); printf("%s: receive copy_len=0\n", enc->dev->name); continue; @@ -489,7 +488,7 @@ static void enc_receive(enc_dev_t *enc) NetReceive(packet, pkt_len); if (enc_claim_bus(enc)) return; - eir_reg = enc_r8(enc, CTL_REG_EIR); + (void)enc_r8(enc, CTL_REG_EIR); } while (pkt_cnt); /* Use EPKTCNT not EIR.PKTIF flag, see errata pt. 6 */ } @@ -500,14 +499,13 @@ static void enc_receive(enc_dev_t *enc) static void enc_poll(enc_dev_t *enc) { u8 eir_reg; - u8 estat_reg; u8 pkt_cnt; #ifdef CONFIG_USE_IRQ /* clear global interrupt enable bit in enc28j60 */ enc_bclr(enc, CTL_REG_EIE, ENC_EIE_INTIE); #endif - estat_reg = enc_r8(enc, CTL_REG_ESTAT); + (void)enc_r8(enc, CTL_REG_ESTAT); eir_reg = enc_r8(enc, CTL_REG_EIR); if (eir_reg & ENC_EIR_TXIF) { /* clear TXIF bit in EIR */ diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c index 0c0c7cd..b05a4c0 100644 --- a/drivers/net/fec_mxc.c +++ b/drivers/net/fec_mxc.c @@ -42,6 +42,14 @@ DECLARE_GLOBAL_DATA_PTR; #define CONFIG_FEC_XCV_TYPE MII100 #endif +/* + * The i.MX28 operates with packets in big endian. We need to swap them before + * sending and after receiving. + */ +#ifdef CONFIG_MX28 +#define CONFIG_FEC_MXC_SWAP_PACKET +#endif + #undef DEBUG struct nbuf { @@ -51,6 +59,32 @@ struct nbuf { uint8_t head[16]; /**< MAC header(6 + 6 + 2) + 2(aligned) */ }; +#ifdef CONFIG_FEC_MXC_SWAP_PACKET +static void swap_packet(uint32_t *packet, int length) +{ + int i; + + for (i = 0; i < DIV_ROUND_UP(length, 4); i++) + packet[i] = __swab32(packet[i]); +} +#endif + +/* + * The i.MX28 has two ethernet interfaces, but they are not equal. + * Only the first one can access the MDIO bus. + */ +#ifdef CONFIG_MX28 +static inline struct ethernet_regs *fec_miiphy_fec_to_eth(struct fec_priv *fec) +{ + return (struct ethernet_regs *)MXS_ENET0_BASE; +} +#else +static inline struct ethernet_regs *fec_miiphy_fec_to_eth(struct fec_priv *fec) +{ + return fec->eth; +} +#endif + /* * MII-interface related functions */ @@ -59,7 +93,7 @@ static int fec_miiphy_read(const char *dev, uint8_t phyAddr, uint8_t regAddr, { struct eth_device *edev = eth_get_dev_by_name(dev); struct fec_priv *fec = (struct fec_priv *)edev->priv; - struct ethernet_regs *eth = fec->eth; + struct ethernet_regs *eth = fec_miiphy_fec_to_eth(fec); uint32_t reg; /* convenient holder for the PHY register */ uint32_t phy; /* convenient holder for the PHY */ @@ -117,7 +151,7 @@ static int fec_miiphy_write(const char *dev, uint8_t phyAddr, uint8_t regAddr, { struct eth_device *edev = eth_get_dev_by_name(dev); struct fec_priv *fec = (struct fec_priv *)edev->priv; - struct ethernet_regs *eth = fec->eth; + struct ethernet_regs *eth = fec_miiphy_fec_to_eth(fec); uint32_t reg; /* convenient holder for the PHY register */ uint32_t phy; /* convenient holder for the PHY */ @@ -572,6 +606,9 @@ static int fec_send(struct eth_device *dev, volatile void* packet, int length) * Note: We are always using the first buffer for transmission, * the second will be empty and only used to stop the DMA engine */ +#ifdef CONFIG_FEC_MXC_SWAP_PACKET + swap_packet((uint32_t *)packet, length); +#endif writew(length, &fec->tbd_base[fec->tbd_index].data_length); writel((uint32_t)packet, &fec->tbd_base[fec->tbd_index].data_pointer); /* @@ -668,6 +705,9 @@ static int fec_recv(struct eth_device *dev) /* * Fill the buffer and pass it to upper layers */ +#ifdef CONFIG_FEC_MXC_SWAP_PACKET + swap_packet((uint32_t *)frame->data, frame_length); +#endif memcpy(buff, frame->data, frame_length); NetReceive(buff, frame_length); len = frame_length; diff --git a/drivers/net/fm/fm.c b/drivers/net/fm/fm.c index 23ef14b..846c372 100644 --- a/drivers/net/fm/fm.c +++ b/drivers/net/fm/fm.c @@ -395,7 +395,6 @@ int fm_init_common(int index, struct ccsr_fman *reg) int dev = CONFIG_SYS_MMC_ENV_DEV; void *addr = malloc(CONFIG_SYS_FMAN_FW_LENGTH); u32 cnt = CONFIG_SYS_FMAN_FW_LENGTH / 512; - u32 n; u32 blk = CONFIG_SYS_QE_FW_IN_MMC / 512; struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV); @@ -405,7 +404,7 @@ int fm_init_common(int index, struct ccsr_fman *reg) printf("\nMMC read: dev # %u, block # %u, count %u ...\n", dev, blk, cnt); mmc_init(mmc); - n = mmc->block_dev.block_read(dev, blk, cnt, addr); + (void)mmc->block_dev.block_read(dev, blk, cnt, addr); /* flush cache after read */ flush_cache((ulong)addr, cnt * 512); } diff --git a/drivers/net/mvgbe.c b/drivers/net/mvgbe.c index c7f7446..fd13428 100644 --- a/drivers/net/mvgbe.c +++ b/drivers/net/mvgbe.c @@ -37,6 +37,7 @@ #include <asm/types.h> #include <asm/system.h> #include <asm/byteorder.h> +#include <asm/arch/cpu.h> #if defined(CONFIG_KIRKWOOD) #include <asm/arch/kirkwood.h> diff --git a/drivers/net/ns8382x.c b/drivers/net/ns8382x.c index 6dfcd0e..11863fe 100644 --- a/drivers/net/ns8382x.c +++ b/drivers/net/ns8382x.c @@ -322,9 +322,7 @@ ns8382x_initialize(bd_t * bis) pci_read_config_dword(devno, PCI_BASE_ADDRESS_1, &iobase); iobase &= ~0x3; /* 1: unused and 0:I/O Space Indicator */ -#ifdef NS8382X_DEBUG - printf("ns8382x: NatSemi dp8382x @ 0x%x\n", iobase); -#endif + debug("ns8382x: NatSemi dp8382x @ 0x%x\n", iobase); pci_write_config_dword(devno, PCI_COMMAND, PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); @@ -382,11 +380,9 @@ ns8382x_initialize(bd_t * bis) rev = mdio_read(dev, phyAddress, PHYIDR2); if ((rev & ~(0x000f)) == 0x00005c50 || (rev & ~(0x000f)) == 0x00005c60) { -#ifdef NS8382X_DEBUG - printf("phy rev is %x\n", rev); - printf("phy address is %x\n", + debug("phy rev is %x\n", rev); + debug("phy address is %x\n", phyAddress); -#endif break; } } @@ -411,21 +407,21 @@ ns8382x_initialize(bd_t * bis) OUTL(dev, (chip_config & ~(PhyDis)), ChipConfig); mdio_sync(dev, EECtrl); -#ifdef NS8382X_DEBUG + { u32 chpcfg = INL(dev, ChipConfig) ^ SpeedStatus_Polarity; - printf("%s: Transceiver 10%s %s duplex.\n", dev->name, + debug("%s: Transceiver 10%s %s duplex.\n", dev->name, (chpcfg & GigSpeed) ? "00" : (chpcfg & HundSpeed) ? "0" : "", chpcfg & FullDuplex ? "full" : "half"); - printf("%s: %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, + debug("%s: %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, dev->enetaddr[0], dev->enetaddr[1], dev->enetaddr[2], dev->enetaddr[3], dev->enetaddr[4], dev->enetaddr[5]); } -#endif + /* Disable PME: * The PME bit is initialized from the EEPROM contents. * PCI cards probably have PME disabled, but motherboard @@ -563,10 +559,10 @@ ns8382x_init(struct eth_device *dev, bd_t * bis) tx_config = TxCarrierIgn | TxHeartIgn | TxAutoPad | TxCollRetry | TxMxdma_1024 | (0x1002); rx_config = RxMxdma_1024 | 0x20; -#ifdef NS8382X_DEBUG - printf("%s: Setting TxConfig Register %#08X\n", dev->name, tx_config); - printf("%s: Setting RxConfig Register %#08X\n", dev->name, rx_config); -#endif + + debug("%s: Setting TxConfig Register %#08X\n", dev->name, tx_config); + debug("%s: Setting RxConfig Register %#08X\n", dev->name, rx_config); + OUTL(dev, tx_config, TxConfig); OUTL(dev, rx_config, RxConfig); @@ -629,10 +625,9 @@ ns8382x_init_txd(struct eth_device *dev) OUTL(dev, 0x0, TxRingPtrHi); OUTL(dev, phys_to_bus((u32)&txd), TxRingPtr); -#ifdef NS8382X_DEBUG - printf("ns8382x_init_txd: TX descriptor register loaded with: %#08X (&txd: %p)\n", + + debug("ns8382x_init_txd: TX descriptor register loaded with: %#08X (&txd: %p)\n", INL(dev, TxRingPtr), &txd); -#endif } /* Function: ns8382x_init_rxd @@ -658,19 +653,16 @@ ns8382x_init_rxd(struct eth_device *dev) rxd[i].extsts = cpu_to_le32((u32) 0x0); rxd[i].cmdsts = cpu_to_le32((u32) RX_BUF_SIZE); rxd[i].bufptr = cpu_to_le32((u32) & rxb[i * RX_BUF_SIZE]); -#ifdef NS8382X_DEBUG - printf + + debug ("ns8382x_init_rxd: rxd[%d]=%p link=%X cmdsts=%X bufptr=%X\n", i, &rxd[i], le32_to_cpu(rxd[i].link), le32_to_cpu(rxd[i].cmdsts), le32_to_cpu(rxd[i].bufptr)); -#endif } OUTL(dev, phys_to_bus((u32) & rxd), RxRingPtr); -#ifdef NS8382X_DEBUG - printf("ns8382x_init_rxd: RX descriptor register loaded with: %X\n", + debug("ns8382x_init_rxd: RX descriptor register loaded with: %X\n", INL(dev, RxRingPtr)); -#endif } /* Function: ns8382x_set_rx_mode @@ -708,11 +700,11 @@ ns8382x_check_duplex(struct eth_device *dev) duplex = (config & FullDuplex) ? 1 : 0; gig = (config & GigSpeed) ? 1 : 0; hun = (config & HundSpeed) ? 1 : 0; -#ifdef NS8382X_DEBUG - printf("%s: Setting 10%s %s-duplex based on negotiated link" + + debug("%s: Setting 10%s %s-duplex based on negotiated link" " capability.\n", dev->name, (gig) ? "00" : (hun) ? "0" : "", duplex ? "full" : "half"); -#endif + if (duplex) { rx_config |= RxAcceptTx; tx_config |= (TxCarrierIgn | TxHeartIgn); @@ -720,10 +712,10 @@ ns8382x_check_duplex(struct eth_device *dev) rx_config &= ~RxAcceptTx; tx_config &= ~(TxCarrierIgn | TxHeartIgn); } -#ifdef NS8382X_DEBUG - printf("%s: Resetting TxConfig Register %#08X\n", dev->name, tx_config); - printf("%s: Resetting RxConfig Register %#08X\n", dev->name, rx_config); -#endif + + debug("%s: Resetting TxConfig Register %#08X\n", dev->name, tx_config); + debug("%s: Resetting RxConfig Register %#08X\n", dev->name, rx_config); + OUTL(dev, tx_config, TxConfig); OUTL(dev, rx_config, RxConfig); @@ -735,9 +727,8 @@ ns8382x_check_duplex(struct eth_device *dev) else config &= ~Mode1000; -#ifdef NS8382X_DEBUG - printf("%s: %setting Mode1000\n", dev->name, (gig) ? "S" : "Uns"); -#endif + debug("%s: %setting Mode1000\n", dev->name, (gig) ? "S" : "Uns"); + OUTL(dev, config, ChipConfig); } @@ -752,9 +743,8 @@ ns8382x_send(struct eth_device *dev, volatile void *packet, int length) /* Stop the transmitter */ OUTL(dev, TxOff, ChipCmd); -#ifdef NS8382X_DEBUG - printf("ns8382x_send: sending %d bytes\n", (int)length); -#endif + + debug("ns8382x_send: sending %d bytes\n", (int)length); /* set the transmit buffer descriptor and enable Transmit State Machine */ txd.link = cpu_to_le32(0x0); @@ -764,13 +754,13 @@ ns8382x_send(struct eth_device *dev, volatile void *packet, int length) /* load Transmit Descriptor Register */ OUTL(dev, phys_to_bus((u32) & txd), TxRingPtr); -#ifdef NS8382X_DEBUG - printf("ns8382x_send: TX descriptor register loaded with: %#08X\n", + + debug("ns8382x_send: TX descriptor register loaded with: %#08X\n", INL(dev, TxRingPtr)); - printf("\ttxd.link:%X\tbufp:%X\texsts:%X\tcmdsts:%X\n", + debug("\ttxd.link:%X\tbufp:%X\texsts:%X\tcmdsts:%X\n", le32_to_cpu(txd.link), le32_to_cpu(txd.bufptr), le32_to_cpu(txd.extsts), le32_to_cpu(txd.cmdsts)); -#endif + /* restart the transmitter */ OUTL(dev, TxOn, ChipCmd); @@ -786,12 +776,11 @@ ns8382x_send(struct eth_device *dev, volatile void *packet, int length) printf("ns8382x_send: Transmit error, Tx status %lX.\n", tx_stat); goto Done; } -#ifdef NS8382X_DEBUG - printf("ns8382x_send: tx_stat: %#08X\n", tx_stat); -#endif + + debug("ns8382x_send: tx_stat: %#08lX\n", tx_stat); status = 1; - Done: +Done: return status; } @@ -814,10 +803,10 @@ ns8382x_poll(struct eth_device *dev) if (!(rx_status & (u32) DescOwn)) return retstat; -#ifdef NS8382X_DEBUG - printf("ns8382x_poll: got a packet: cur_rx:%u, status:%lx\n", + + debug("ns8382x_poll: got a packet: cur_rx:%u, status:%lx\n", cur_rx, rx_status); -#endif + length = (rx_status & DSIZE) - CRC_SIZE; if ((rx_status & (DescMore | DescPktOK | DescRxLong)) != DescPktOK) { diff --git a/drivers/net/pcnet.c b/drivers/net/pcnet.c index e994cb6..45066c8 100644 --- a/drivers/net/pcnet.c +++ b/drivers/net/pcnet.c @@ -30,21 +30,12 @@ #include <asm/io.h> #include <pci.h> -#if 0 #define PCNET_DEBUG_LEVEL 0 /* 0=off, 1=init, 2=rx/tx */ -#endif -#if PCNET_DEBUG_LEVEL > 0 -#define PCNET_DEBUG1(fmt,args...) printf (fmt ,##args) -#if PCNET_DEBUG_LEVEL > 1 -#define PCNET_DEBUG2(fmt,args...) printf (fmt ,##args) -#else -#define PCNET_DEBUG2(fmt,args...) -#endif -#else -#define PCNET_DEBUG1(fmt,args...) -#define PCNET_DEBUG2(fmt,args...) -#endif +#define PCNET_DEBUG1(fmt,args...) \ + debug_cond(PCNET_DEBUG_LEVEL > 0, fmt ,##args) +#define PCNET_DEBUG2(fmt,args...) \ + debug_cond(PCNET_DEBUG_LEVEL > 1, fmt ,##args) #if !defined(CONF_PCNET_79C973) && defined(CONF_PCNET_79C975) #error "Macro for PCnet chip version is not defined!" diff --git a/drivers/net/rtl8139.c b/drivers/net/rtl8139.c index c2779db..e3feef8 100644 --- a/drivers/net/rtl8139.c +++ b/drivers/net/rtl8139.c @@ -95,10 +95,9 @@ #define RX_BUF_LEN_IDX 0 /* 0, 1, 2 is allowed - 8,16,32K rx buffer */ #define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX) -#undef DEBUG_TX -#undef DEBUG_RX +#define DEBUG_TX 0 /* set to 1 to enable debug code */ +#define DEBUG_RX 0 /* set to 1 to enable debug code */ -#define currticks() get_timer(0) #define bus_to_phys(a) pci_mem_to_phys((pci_dev_t)dev->priv, a) #define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, a) @@ -253,7 +252,6 @@ int rtl8139_initialize(bd_t *bis) static int rtl8139_probe(struct eth_device *dev, bd_t *bis) { int i; - int speed10, fullduplex; int addr_len; unsigned short *ap = (unsigned short *)dev->enetaddr; @@ -266,9 +264,6 @@ static int rtl8139_probe(struct eth_device *dev, bd_t *bis) for (i = 0; i < 3; i++) *ap++ = le16_to_cpu (read_eeprom(i + 7, addr_len)); - speed10 = inb(ioaddr + MediaStatus) & MSRSpeed10; - fullduplex = inw(ioaddr + MII_BMCR) & BMCRDuplex; - rtl_reset(dev); if (inb(ioaddr + MediaStatus) & MSRLinkFail) { @@ -389,9 +384,8 @@ static void rtl_reset(struct eth_device *dev) * from the configuration EEPROM default, because the card manufacturer * should have set that to match the card. */ -#ifdef DEBUG_RX - printf("rx ring address is %X\n",(unsigned long)rx_ring); -#endif + debug_cond(DEBUG_RX, + "rx ring address is %lX\n",(unsigned long)rx_ring); flush_cache((unsigned long)rx_ring, RX_BUF_LEN); outl(phys_to_bus((int)rx_ring), ioaddr + RxBuf); @@ -424,9 +418,7 @@ static int rtl_transmit(struct eth_device *dev, volatile void *packet, int lengt memcpy((char *)tx_buffer, (char *)packet, (int)length); -#ifdef DEBUG_TX - printf("sending %d bytes\n", len); -#endif + debug_cond(DEBUG_TX, "sending %d bytes\n", len); /* Note: RTL8139 doesn't auto-pad, send minimum payload (another 4 * bytes are sent automatically for the FCS, totalling to 64 bytes). */ @@ -453,16 +445,18 @@ static int rtl_transmit(struct eth_device *dev, volatile void *packet, int lengt if (status & TxOK) { cur_tx = (cur_tx + 1) % NUM_TX_DESC; -#ifdef DEBUG_TX - printf("tx done (%d ticks), status %hX txstatus %X\n", - to-currticks(), status, txstatus); -#endif + + debug_cond(DEBUG_TX, + "tx done, status %hX txstatus %lX\n", + status, txstatus); + return length; } else { -#ifdef DEBUG_TX - printf("tx timeout/error (%d usecs), status %hX txstatus %X\n", - 10*i, status, txstatus); -#endif + + debug_cond(DEBUG_TX, + "tx timeout/error (%d usecs), status %hX txstatus %lX\n", + 10*i, status, txstatus); + rtl_reset(dev); return 0; @@ -486,9 +480,7 @@ static int rtl_poll(struct eth_device *dev) /* See below for the rest of the interrupt acknowledges. */ outw(status & ~(RxFIFOOver | RxOverflow | RxOK), ioaddr + IntrStatus); -#ifdef DEBUG_RX - printf("rtl_poll: int %hX ", status); -#endif + debug_cond(DEBUG_RX, "rtl_poll: int %hX ", status); ring_offs = cur_rx % RX_BUF_LEN; /* ring_offs is guaranteed being 4-byte aligned */ @@ -513,14 +505,11 @@ static int rtl_poll(struct eth_device *dev) memcpy(&(rxdata[semi_count]), rx_ring, rx_size-4-semi_count); NetReceive(rxdata, length); -#ifdef DEBUG_RX - printf("rx packet %d+%d bytes", semi_count,rx_size-4-semi_count); -#endif + debug_cond(DEBUG_RX, "rx packet %d+%d bytes", + semi_count, rx_size-4-semi_count); } else { NetReceive(rx_ring + ring_offs + 4, length); -#ifdef DEBUG_RX - printf("rx packet %d bytes", rx_size-4); -#endif + debug_cond(DEBUG_RX, "rx packet %d bytes", rx_size-4); } flush_cache((unsigned long)rx_ring, RX_BUF_LEN); diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index b81dcad..1ad13bd 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -739,7 +739,6 @@ INIT - Look for an adapter, this routine's visible to the outside static int rtl_init(struct eth_device *dev, bd_t *bis) { static int board_idx = -1; - static int printed_version = 0; int i, rc; int option = -1, Cap10_100 = 0, Cap1000 = 0; @@ -751,8 +750,6 @@ static int rtl_init(struct eth_device *dev, bd_t *bis) board_idx++; - printed_version = 1; - /* point to private storage */ tpc = &tpx; diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c index 78ffc95..160bc05 100644 --- a/drivers/net/tsec.c +++ b/drivers/net/tsec.c @@ -19,6 +19,7 @@ #include <tsec.h> #include <fsl_mdio.h> #include <asm/errno.h> +#include <asm/processor.h> DECLARE_GLOBAL_DATA_PTR; @@ -43,6 +44,9 @@ static RTXBD rtx __attribute__ ((aligned(8))); #error "rtx must be 64-bit aligned" #endif +static int tsec_send(struct eth_device *dev, + volatile void *packet, int length); + /* Default initializations for TSEC controllers. */ static struct tsec_info_struct tsec_info[] = { @@ -236,6 +240,87 @@ static void adjust_link(struct tsec_private *priv, struct phy_device *phydev) (phydev->port == PORT_FIBRE) ? ", fiber mode" : ""); } +#ifdef CONFIG_SYS_FSL_ERRATUM_NMG_ETSEC129 +/* + * When MACCFG1[Rx_EN] is enabled during system boot as part + * of the eTSEC port initialization sequence, + * the eTSEC Rx logic may not be properly initialized. + */ +void redundant_init(struct eth_device *dev) +{ + struct tsec_private *priv = dev->priv; + tsec_t *regs = priv->regs; + uint t, count = 0; + int fail = 1; + static const u8 pkt[] = { + 0x00, 0x1e, 0x4f, 0x12, 0xcb, 0x2c, 0x00, 0x25, + 0x64, 0xbb, 0xd1, 0xab, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x5c, 0xdd, 0x22, 0x00, 0x00, 0x80, 0x01, + 0x1f, 0x71, 0x0a, 0xc1, 0x14, 0x22, 0x0a, 0xc1, + 0x14, 0x6a, 0x08, 0x00, 0xef, 0x7e, 0x02, 0x00, + 0x94, 0x05, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, + 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, + 0x71, 0x72}; + + /* Enable promiscuous mode */ + setbits_be32(®s->rctrl, 0x8); + /* Enable loopback mode */ + setbits_be32(®s->maccfg1, MACCFG1_LOOPBACK); + /* Enable transmit and receive */ + setbits_be32(®s->maccfg1, MACCFG1_RX_EN | MACCFG1_TX_EN); + + /* Tell the DMA it is clear to go */ + setbits_be32(®s->dmactrl, DMACTRL_INIT_SETTINGS); + out_be32(®s->tstat, TSTAT_CLEAR_THALT); + out_be32(®s->rstat, RSTAT_CLEAR_RHALT); + clrbits_be32(®s->dmactrl, DMACTRL_GRS | DMACTRL_GTS); + + do { + tsec_send(dev, (void *)pkt, sizeof(pkt)); + + /* Wait for buffer to be received */ + for (t = 0; rtx.rxbd[rxIdx].status & RXBD_EMPTY; t++) { + if (t >= 10 * TOUT_LOOP) { + printf("%s: tsec: rx error\n", dev->name); + break; + } + } + + if (!memcmp(pkt, (void *)NetRxPackets[rxIdx], sizeof(pkt))) + fail = 0; + + rtx.rxbd[rxIdx].length = 0; + rtx.rxbd[rxIdx].status = + RXBD_EMPTY | (((rxIdx + 1) == PKTBUFSRX) ? RXBD_WRAP : 0); + rxIdx = (rxIdx + 1) % PKTBUFSRX; + + if (in_be32(®s->ievent) & IEVENT_BSY) { + out_be32(®s->ievent, IEVENT_BSY); + out_be32(®s->rstat, RSTAT_CLEAR_RHALT); + } + if (fail) { + printf("loopback recv packet error!\n"); + clrbits_be32(®s->maccfg1, MACCFG1_RX_EN); + udelay(1000); + setbits_be32(®s->maccfg1, MACCFG1_RX_EN); + } + } while ((count++ < 4) && (fail == 1)); + + if (fail) + panic("eTSEC init fail!\n"); + /* Disable promiscuous mode */ + clrbits_be32(®s->rctrl, 0x8); + /* Disable loopback mode */ + clrbits_be32(®s->maccfg1, MACCFG1_LOOPBACK); +} +#endif + /* Set up the buffers and their descriptors, and bring up the * interface */ @@ -248,6 +333,9 @@ static void startup_tsec(struct eth_device *dev) /* reset the indices to zero */ rxIdx = 0; txIdx = 0; +#ifdef CONFIG_SYS_FSL_ERRATUM_NMG_ETSEC129 + uint svr; +#endif /* Point to the buffer descriptors */ out_be32(®s->tbase, (unsigned int)(&rtx.txbd[txIdx])); @@ -269,6 +357,11 @@ static void startup_tsec(struct eth_device *dev) } rtx.txbd[TX_BUF_CNT - 1].status |= TXBD_WRAP; +#ifdef CONFIG_SYS_FSL_ERRATUM_NMG_ETSEC129 + svr = get_svr(); + if ((SVR_MAJ(svr) == 1) || IS_SVR_REV(svr, 2, 0)) + redundant_init(dev); +#endif /* Enable Transmit and Receive */ setbits_be32(®s->maccfg1, MACCFG1_RX_EN | MACCFG1_TX_EN); diff --git a/drivers/pcmcia/mpc8xx_pcmcia.c b/drivers/pcmcia/mpc8xx_pcmcia.c index 74a50f1..3732583 100644 --- a/drivers/pcmcia/mpc8xx_pcmcia.c +++ b/drivers/pcmcia/mpc8xx_pcmcia.c @@ -1,6 +1,7 @@ #include <common.h> #include <mpc8xx.h> #include <pcmcia.h> +#include <linux/compiler.h> #undef CONFIG_PCMCIA @@ -73,8 +74,8 @@ int pcmcia_on (void) { u_long reg, base; pcmcia_win_t *win; - u_int slotbit; u_int rc, slot; + __maybe_unused u_int slotbit; int i; debug ("Enable PCMCIA " PCMCIA_SLOT_MSG "\n"); diff --git a/drivers/qe/uec.c b/drivers/qe/uec.c index 1ecb137..3e46e35 100644 --- a/drivers/qe/uec.c +++ b/drivers/qe/uec.c @@ -264,13 +264,10 @@ static int uec_open(uec_private_t *uec, comm_dir_e mode) static int uec_stop(uec_private_t *uec, comm_dir_e mode) { - ucc_fast_private_t *uccf; - if (!uec || !uec->uccf) { printf("%s: No handle passed.\n", __FUNCTION__); return -EINVAL; } - uccf = uec->uccf; /* check if the UCC number is in range. */ if (uec->uec_info->uf_info.ucc_num >= UCC_MAX_NUM) { @@ -325,7 +322,6 @@ static int uec_set_mac_if_mode(uec_private_t *uec, phy_interface_t if_mode, int speed) { phy_interface_t enet_if_mode; - uec_info_t *uec_info; uec_t *uec_regs; u32 upsmr; u32 maccfg2; @@ -335,7 +331,6 @@ static int uec_set_mac_if_mode(uec_private_t *uec, return -EINVAL; } - uec_info = uec->uec_info; uec_regs = uec->uec_regs; enet_if_mode = if_mode; @@ -516,12 +511,10 @@ bus_fail: static void adjust_link(struct eth_device *dev) { uec_private_t *uec = (uec_private_t *)dev->priv; - uec_t *uec_regs; struct uec_mii_info *mii_info = uec->mii_info; extern void change_phy_interface_mode(struct eth_device *dev, phy_interface_t mode, int speed); - uec_regs = uec->uec_regs; if (mii_info->link) { /* Now we make sure that we can be in full duplex mode. diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index df440c6..faf4fcd 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -50,13 +50,14 @@ COBJS-$(CONFIG_RTC_M41T62) += m41t62.o COBJS-$(CONFIG_RTC_M41T94) += m41t94.o COBJS-$(CONFIG_RTC_M48T35A) += m48t35ax.o COBJS-$(CONFIG_RTC_MAX6900) += max6900.o -COBJS-$(CONFIG_RTC_MC13783) += mc13783-rtc.o +COBJS-$(CONFIG_RTC_MC13XXX) += mc13xxx-rtc.o COBJS-$(CONFIG_RTC_MC146818) += mc146818.o COBJS-$(CONFIG_MCFRTC) += mcfrtc.o COBJS-$(CONFIG_RTC_MK48T59) += mk48t59.o COBJS-$(CONFIG_RTC_MPC5200) += mpc5xxx.o COBJS-$(CONFIG_RTC_MPC8xx) += mpc8xx.o COBJS-$(CONFIG_RTC_MV) += mvrtc.o +COBJS-$(CONFIG_RTC_MXS) += mxsrtc.o COBJS-$(CONFIG_RTC_PCF8563) += pcf8563.o COBJS-$(CONFIG_RTC_PL031) += pl031.o COBJS-$(CONFIG_RTC_PT7C4338) += pt7c4338.o diff --git a/drivers/rtc/ds1337.c b/drivers/rtc/ds1337.c index 7abf041..5bb9f94 100644 --- a/drivers/rtc/ds1337.c +++ b/drivers/rtc/ds1337.c @@ -34,16 +34,6 @@ #if defined(CONFIG_CMD_DATE) -/*---------------------------------------------------------------------*/ -#undef DEBUG_RTC - -#ifdef DEBUG_RTC -#define DEBUGR(fmt,args...) printf(fmt ,##args) -#else -#define DEBUGR(fmt,args...) -#endif -/*---------------------------------------------------------------------*/ - /* * RTC register addresses */ @@ -97,7 +87,7 @@ int rtc_get (struct rtc_time *tmp) mon_cent = rtc_read (RTC_MON_REG_ADDR); year = rtc_read (RTC_YR_REG_ADDR); - DEBUGR ("Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x " + debug("Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x " "hr: %02x min: %02x sec: %02x control: %02x status: %02x\n", year, mon_cent, mday, wday, hour, min, sec, control, status); @@ -119,7 +109,7 @@ int rtc_get (struct rtc_time *tmp) tmp->tm_yday = 0; tmp->tm_isdst= 0; - DEBUGR ("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + debug("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); @@ -134,7 +124,7 @@ int rtc_set (struct rtc_time *tmp) { uchar century; - DEBUGR ("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); diff --git a/drivers/rtc/ds3231.c b/drivers/rtc/ds3231.c index 134a0e4..ae59cfb 100644 --- a/drivers/rtc/ds3231.c +++ b/drivers/rtc/ds3231.c @@ -35,16 +35,6 @@ #if defined(CONFIG_CMD_DATE) -/*---------------------------------------------------------------------*/ -#undef DEBUG_RTC - -#ifdef DEBUG_RTC -#define DEBUGR(fmt,args...) printf(fmt ,##args) -#else -#define DEBUGR(fmt,args...) -#endif -/*---------------------------------------------------------------------*/ - /* * RTC register addresses */ @@ -99,7 +89,7 @@ int rtc_get (struct rtc_time *tmp) mon_cent = rtc_read (RTC_MON_REG_ADDR); year = rtc_read (RTC_YR_REG_ADDR); - DEBUGR ("Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x " + debug("Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x " "hr: %02x min: %02x sec: %02x control: %02x status: %02x\n", year, mon_cent, mday, wday, hour, min, sec, control, status); @@ -121,7 +111,7 @@ int rtc_get (struct rtc_time *tmp) tmp->tm_yday = 0; tmp->tm_isdst= 0; - DEBUGR ("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + debug("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); @@ -136,7 +126,7 @@ int rtc_set (struct rtc_time *tmp) { uchar century; - DEBUGR ("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); diff --git a/drivers/rtc/mc13783-rtc.c b/drivers/rtc/mc13xxx-rtc.c index 70ea8a1..70ea8a1 100644 --- a/drivers/rtc/mc13783-rtc.c +++ b/drivers/rtc/mc13xxx-rtc.c diff --git a/drivers/rtc/mvrtc.c b/drivers/rtc/mvrtc.c index ccc573a..edc1f4f 100644 --- a/drivers/rtc/mvrtc.c +++ b/drivers/rtc/mvrtc.c @@ -28,6 +28,7 @@ #include <common.h> #include <command.h> #include <rtc.h> +#include <asm/io.h> #include "mvrtc.h" /* This RTC does not support century, so we assume 20 */ diff --git a/drivers/rtc/mxsrtc.c b/drivers/rtc/mxsrtc.c new file mode 100644 index 0000000..5beb1a0 --- /dev/null +++ b/drivers/rtc/mxsrtc.c @@ -0,0 +1,86 @@ +/* + * Freescale i.MX28 RTC Driver + * + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * on behalf of DENX Software Engineering GmbH + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <common.h> +#include <rtc.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> + +#define MXS_RTC_MAX_TIMEOUT 1000000 + +/* Set time in seconds since 1970-01-01 */ +int mxs_rtc_set_time(uint32_t secs) +{ + struct mx28_rtc_regs *rtc_regs = (struct mx28_rtc_regs *)MXS_RTC_BASE; + int ret; + + writel(secs, &rtc_regs->hw_rtc_seconds); + + /* + * The 0x80 here means seconds were copied to analog. This information + * is taken from the linux kernel driver for the STMP37xx RTC since + * documentation doesn't mention it. + */ + ret = mx28_wait_mask_clr(&rtc_regs->hw_rtc_stat_reg, + 0x80 << RTC_STAT_STALE_REGS_OFFSET, MXS_RTC_MAX_TIMEOUT); + + if (ret) + printf("MXS RTC: Timeout waiting for update\n"); + + return ret; +} + +int rtc_get(struct rtc_time *time) +{ + struct mx28_rtc_regs *rtc_regs = (struct mx28_rtc_regs *)MXS_RTC_BASE; + uint32_t secs; + + secs = readl(&rtc_regs->hw_rtc_seconds); + to_tm(secs, time); + + return 0; +} + +int rtc_set(struct rtc_time *time) +{ + uint32_t secs; + + secs = mktime(time->tm_year, time->tm_mon, time->tm_mday, + time->tm_hour, time->tm_min, time->tm_sec); + + return mxs_rtc_set_time(secs); +} + +void rtc_reset(void) +{ + struct mx28_rtc_regs *rtc_regs = (struct mx28_rtc_regs *)MXS_RTC_BASE; + int ret; + + /* Set time to 1970-01-01 */ + mxs_rtc_set_time(0); + + /* Reset the RTC block */ + ret = mx28_reset_block(&rtc_regs->hw_rtc_ctrl_reg); + if (ret) + printf("MXS RTC: Block reset timeout\n"); +} diff --git a/drivers/rtc/rv3029.c b/drivers/rtc/rv3029.c index e012168..8033695 100644 --- a/drivers/rtc/rv3029.c +++ b/drivers/rtc/rv3029.c @@ -84,12 +84,10 @@ int rtc_get( struct rtc_time *tmp ) tmp->tm_yday = 0; tmp->tm_isdst = 0; -#ifdef RTC_DEBUG - printf( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + debug( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec ); -#endif return 0; } @@ -97,11 +95,10 @@ int rtc_set( struct rtc_time *tmp ) { int ret; unsigned char buf[RTC_RV3029_PAGE_LEN]; -#ifdef RTC_DEBUG - printf( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", + + debug( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); -#endif if (tmp->tm_year < 2000) { printf("RTC: year %d < 2000 not possible\n", tmp->tm_year); @@ -122,16 +119,15 @@ int rtc_set( struct rtc_time *tmp ) /* give the RTC some time to update */ udelay(1000); - return 0; + return ret; } /* sets EERE-Bit (automatic EEPROM refresh) */ static void set_eere_bit(int state) { - int ret; unsigned char reg_ctrl1; - ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL1, 1, + (void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL1, 1, ®_ctrl1, 1); if (state) @@ -139,18 +135,18 @@ static void set_eere_bit(int state) else reg_ctrl1 &= (~RTC_RV3029_CTRL1_EERE); - ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL1, 1, + (void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL1, 1, ®_ctrl1, 1); } /* waits until EEPROM page is no longer busy (times out after 10ms*loops) */ static int wait_eebusy(int loops) { - int i, ret; + int i; unsigned char ctrl_status; for (i = 0; i < loops; i++) { - ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL_STATUS, + (void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL_STATUS, 1, &ctrl_status, 1); if ((ctrl_status & RTC_RV3029_CTRLS_EEBUSY) == 0) @@ -162,11 +158,10 @@ static int wait_eebusy(int loops) void rtc_reset (void) { - int ret; unsigned char buf[RTC_RV3029_PAGE_LEN]; buf[0] = RTC_RV3029_CTRL_SYS_R; - ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL_RESET, 1, + (void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_CTRL_RESET, 1, buf, 1); #if defined(CONFIG_SYS_RV3029_TCR) @@ -178,7 +173,7 @@ void rtc_reset (void) set_eere_bit(0); wait_eebusy(100); /* read current trickle charger setting */ - ret = i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_EEPROM_CTRL, + (void)i2c_read(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_EEPROM_CTRL, 1, buf, 1); /* enable automatic EEPROM refresh again */ set_eere_bit(1); @@ -195,7 +190,7 @@ void rtc_reset (void) */ set_eere_bit(0); wait_eebusy(100); - ret = i2c_write(CONFIG_SYS_I2C_RTC_ADDR, + (void)i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_RV3029_EEPROM_CTRL, 1, buf, 1); /* * it is necessary to wait 10ms before EEBUSY-Bit may be read diff --git a/drivers/rtc/s3c24x0_rtc.c b/drivers/rtc/s3c24x0_rtc.c index 9667939..c16ff2e 100644 --- a/drivers/rtc/s3c24x0_rtc.c +++ b/drivers/rtc/s3c24x0_rtc.c @@ -34,8 +34,7 @@ #include <rtc.h> #include <asm/io.h> - -/*#define DEBUG*/ +#include <linux/compiler.h> typedef enum { RTC_ENABLE, @@ -64,7 +63,8 @@ int rtc_get(struct rtc_time *tmp) { struct s3c24x0_rtc *rtc = s3c24x0_get_base_rtc(); uchar sec, min, hour, mday, wday, mon, year; - uchar a_sec, a_min, a_hour, a_date, a_mon, a_year, a_armed; + __maybe_unused uchar a_sec, a_min, a_hour, a_date, + a_mon, a_year, a_armed; /* enable access to RTC registers */ SetRTC_Access(RTC_ENABLE); diff --git a/drivers/serial/sandbox.c b/drivers/serial/sandbox.c index 0d65587..1927c16 100644 --- a/drivers/serial/sandbox.c +++ b/drivers/serial/sandbox.c @@ -30,6 +30,7 @@ int serial_init(void) { + os_tty_raw(0); return 0; } @@ -44,14 +45,13 @@ void serial_putc(const char ch) void serial_puts(const char *str) { - while (*str) - serial_putc(*str++); + os_write(1, str, strlen(str)); } int serial_getc(void) { char buf; - int count; + ssize_t count; count = os_read(0, &buf, 1); return count == 1 ? buf : 0; diff --git a/drivers/serial/serial_pxa.c b/drivers/serial/serial_pxa.c index 68469a4..84bb17c 100644 --- a/drivers/serial/serial_pxa.c +++ b/drivers/serial/serial_pxa.c @@ -1,4 +1,6 @@ /* + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * * (C) Copyright 2002 * Wolfgang Denk, DENX Software Engineering, <wd@denx.de> * @@ -32,148 +34,161 @@ #include <watchdog.h> #include <serial.h> #include <asm/arch/pxa-regs.h> +#include <asm/arch/regs-uart.h> #include <asm/io.h> DECLARE_GLOBAL_DATA_PTR; -#define FFUART_INDEX 0 -#define BTUART_INDEX 1 -#define STUART_INDEX 2 +/* + * The numbering scheme differs here for PXA25x, PXA27x and PXA3xx so we can + * easily handle enabling of clock. + */ +#ifdef CONFIG_CPU_MONAHANS +#define UART_CLK_BASE CKENA_21_BTUART +#define UART_CLK_REG CKENA +#define BTUART_INDEX 0 +#define FFUART_INDEX 1 +#define STUART_INDEX 2 +#elif CONFIG_PXA250 +#define UART_CLK_BASE (1 << 4) /* HWUART */ +#define UART_CLK_REG CKEN +#define HWUART_INDEX 0 +#define STUART_INDEX 1 +#define FFUART_INDEX 2 +#define BTUART_INDEX 3 +#else /* PXA27x */ +#define UART_CLK_BASE CKEN5_STUART +#define UART_CLK_REG CKEN +#define STUART_INDEX 0 +#define FFUART_INDEX 1 +#define BTUART_INDEX 2 +#endif + +/* + * Only PXA250 has HWUART, to avoid poluting the code with more macros, + * artificially introduce this. + */ +#ifndef CONFIG_PXA250 +#define HWUART_INDEX 0xff +#endif #ifndef CONFIG_SERIAL_MULTI -#if defined (CONFIG_FFUART) +#if defined(CONFIG_FFUART) #define UART_INDEX FFUART_INDEX -#elif defined (CONFIG_BTUART) +#elif defined(CONFIG_BTUART) #define UART_INDEX BTUART_INDEX -#elif defined (CONFIG_STUART) +#elif defined(CONFIG_STUART) #define UART_INDEX STUART_INDEX +#elif defined(CONFIG_HWUART) +#define UART_INDEX HWUART_INDEX #else -#error "Bad: you didn't configure serial ..." +#error "Please select CONFIG_(FF|BT|ST|HW)UART in board config file." #endif #endif -void pxa_setbrg_dev (unsigned int uart_index) +uint32_t pxa_uart_get_baud_divider(void) { - unsigned int quot = 0; - if (gd->baudrate == 1200) - quot = 768; + return 768; else if (gd->baudrate == 9600) - quot = 96; + return 96; else if (gd->baudrate == 19200) - quot = 48; + return 48; else if (gd->baudrate == 38400) - quot = 24; + return 24; else if (gd->baudrate == 57600) - quot = 16; + return 16; else if (gd->baudrate == 115200) - quot = 8; - else - hang (); + return 8; + else /* Unsupported baudrate */ + return 0; +} +struct pxa_uart_regs *pxa_uart_index_to_regs(uint32_t uart_index) +{ switch (uart_index) { - case FFUART_INDEX: -#ifdef CONFIG_CPU_MONAHANS - writel(readl(CKENA) | CKENA_22_FFUART, CKENA); -#else - writel(readl(CKEN) | CKEN6_FFUART, CKEN); -#endif /* CONFIG_CPU_MONAHANS */ - - writel(0, FFIER); /* Disable for now */ - writel(0, FFFCR); /* No fifos enabled */ + case FFUART_INDEX: return (struct pxa_uart_regs *)FFUART_BASE; + case BTUART_INDEX: return (struct pxa_uart_regs *)BTUART_BASE; + case STUART_INDEX: return (struct pxa_uart_regs *)STUART_BASE; + case HWUART_INDEX: return (struct pxa_uart_regs *)HWUART_BASE; + default: + return NULL; + } +} - /* set baud rate */ - writel(LCR_WLS0 | LCR_WLS1 | LCR_DLAB, FFLCR); - writel(quot & 0xff, FFDLL); - writel(quot >> 8, FFDLH); - writel(LCR_WLS0 | LCR_WLS1, FFLCR); +void pxa_uart_toggle_clock(uint32_t uart_index, int enable) +{ + uint32_t clk_reg, clk_offset, reg; - writel(IER_UUE, FFIER); /* Enable FFUART */ - break; + clk_reg = UART_CLK_REG; + clk_offset = UART_CLK_BASE << uart_index; - case BTUART_INDEX: -#ifdef CONFIG_CPU_MONAHANS - writel(readl(CKENA) | CKENA_21_BTUART, CKENA); -#else - writel(readl(CKEN) | CKEN7_BTUART, CKEN); -#endif /* CONFIG_CPU_MONAHANS */ + reg = readl(clk_reg); - writel(0, BTIER); - writel(0, BTFCR); + if (enable) + reg |= clk_offset; + else + reg &= ~clk_offset; - /* set baud rate */ - writel(LCR_DLAB, BTLCR); - writel(quot & 0xff, BTDLL); - writel(quot >> 8, BTDLH); - writel(LCR_WLS0 | LCR_WLS1, BTLCR); + writel(reg, clk_reg); +} - writel(IER_UUE, BTIER); /* Enable BFUART */ +/* + * Enable clock and set baud rate, parity etc. + */ +void pxa_setbrg_dev(uint32_t uart_index) +{ + uint32_t divider = 0; + struct pxa_uart_regs *uart_regs; - break; + divider = pxa_uart_get_baud_divider(); + if (!divider) + hang(); - case STUART_INDEX: -#ifdef CONFIG_CPU_MONAHANS - writel(readl(CKENA) | CKENA_23_STUART, CKENA); -#else - writel(readl(CKEN) | CKEN5_STUART, CKEN); -#endif /* CONFIG_CPU_MONAHANS */ + uart_regs = pxa_uart_index_to_regs(uart_index); + if (!uart_regs) + hang(); - writel(0, STIER); - writel(0, STFCR); + pxa_uart_toggle_clock(uart_index, 1); - /* set baud rate */ - writel(LCR_DLAB, STLCR); - writel(quot & 0xff, STDLL); - writel(quot >> 8, STDLH); - writel(LCR_WLS0 | LCR_WLS1, STLCR); + /* Disable interrupts and FIFOs */ + writel(0, &uart_regs->ier); + writel(0, &uart_regs->fcr); - writel(IER_UUE, STIER); /* Enable STUART */ - break; + /* Set baud rate */ + writel(LCR_WLS0 | LCR_WLS1 | LCR_DLAB, &uart_regs->lcr); + writel(divider & 0xff, &uart_regs->dll); + writel(divider >> 8, &uart_regs->dlh); + writel(LCR_WLS0 | LCR_WLS1, &uart_regs->lcr); - default: - hang(); - } + /* Enable UART */ + writel(IER_UUE, &uart_regs->ier); } - /* * Initialise the serial port with the given baudrate. The settings * are always 8 data bits, no parity, 1 stop bit, no start bits. - * */ -int pxa_init_dev (unsigned int uart_index) +int pxa_init_dev(unsigned int uart_index) { pxa_setbrg_dev (uart_index); - - return (0); + return 0; } - /* * Output a single byte to the serial port. */ -void pxa_putc_dev (unsigned int uart_index,const char c) +void pxa_putc_dev(unsigned int uart_index, const char c) { - switch (uart_index) { - case FFUART_INDEX: - /* wait for room in the tx FIFO on FFUART */ - while ((readl(FFLSR) & LSR_TEMT) == 0) - WATCHDOG_RESET (); /* Reset HW Watchdog, if needed */ - writel(c, FFTHR); - break; - - case BTUART_INDEX: - while ((readl(BTLSR) & LSR_TEMT) == 0) - WATCHDOG_RESET (); /* Reset HW Watchdog, if needed */ - writel(c, BTTHR); - break; - - case STUART_INDEX: - while ((readl(STLSR) & LSR_TEMT) == 0) - WATCHDOG_RESET (); /* Reset HW Watchdog, if needed */ - writel(c, STTHR); - break; - } + struct pxa_uart_regs *uart_regs; + + uart_regs = pxa_uart_index_to_regs(uart_index); + if (!uart_regs) + hang(); + + while (!(readl(&uart_regs->lsr) & LSR_TEMT)) + WATCHDOG_RESET(); + writel(c, &uart_regs->thr); /* If \n, also do \r */ if (c == '\n') @@ -185,17 +200,15 @@ void pxa_putc_dev (unsigned int uart_index,const char c) * otherwise. When the function is succesfull, the character read is * written into its argument c. */ -int pxa_tstc_dev (unsigned int uart_index) +int pxa_tstc_dev(unsigned int uart_index) { - switch (uart_index) { - case FFUART_INDEX: - return readl(FFLSR) & LSR_DR; - case BTUART_INDEX: - return readl(BTLSR) & LSR_DR; - case STUART_INDEX: - return readl(STLSR) & LSR_DR; - } - return -1; + struct pxa_uart_regs *uart_regs; + + uart_regs = pxa_uart_index_to_regs(uart_index); + if (!uart_regs) + return -1; + + return readl(&uart_regs->lsr) & LSR_DR; } /* @@ -203,187 +216,86 @@ int pxa_tstc_dev (unsigned int uart_index) * otherwise. When the function is succesfull, the character read is * written into its argument c. */ -int pxa_getc_dev (unsigned int uart_index) +int pxa_getc_dev(unsigned int uart_index) { - switch (uart_index) { - case FFUART_INDEX: - while (!(readl(FFLSR) & LSR_DR)) - /* Reset HW Watchdog, if needed */ - WATCHDOG_RESET(); - return (char) readl(FFRBR) & 0xff; - - case BTUART_INDEX: - while (!(readl(BTLSR) & LSR_DR)) - /* Reset HW Watchdog, if needed */ - WATCHDOG_RESET(); - return (char) readl(BTRBR) & 0xff; - case STUART_INDEX: - while (!(readl(STLSR) & LSR_DR)) - /* Reset HW Watchdog, if needed */ - WATCHDOG_RESET(); - return (char) readl(STRBR) & 0xff; - } - return -1; -} + struct pxa_uart_regs *uart_regs; -void -pxa_puts_dev (unsigned int uart_index,const char *s) -{ - while (*s) { - pxa_putc_dev (uart_index,*s++); - } -} + uart_regs = pxa_uart_index_to_regs(uart_index); + if (!uart_regs) + return -1; -#if defined (CONFIG_FFUART) -static int ffuart_init(void) -{ - return pxa_init_dev(FFUART_INDEX); + while (!(readl(&uart_regs->lsr) & LSR_DR)) + WATCHDOG_RESET(); + return readl(&uart_regs->rbr) & 0xff; } -static void ffuart_setbrg(void) +void pxa_puts_dev(unsigned int uart_index, const char *s) { - return pxa_setbrg_dev(FFUART_INDEX); + while (*s) + pxa_putc_dev(uart_index, *s++); } -static void ffuart_putc(const char c) -{ - return pxa_putc_dev(FFUART_INDEX,c); -} - -static void ffuart_puts(const char *s) -{ - return pxa_puts_dev(FFUART_INDEX,s); -} - -static int ffuart_getc(void) -{ - return pxa_getc_dev(FFUART_INDEX); -} - -static int ffuart_tstc(void) -{ - return pxa_tstc_dev(FFUART_INDEX); -} - -struct serial_device serial_ffuart_device = -{ - "serial_ffuart", - ffuart_init, - NULL, - ffuart_setbrg, - ffuart_getc, - ffuart_tstc, - ffuart_putc, - ffuart_puts, -}; +#define pxa_uart(uart, UART) \ + int uart##_init(void) \ + { \ + return pxa_init_dev(UART##_INDEX); \ + } \ + \ + void uart##_setbrg(void) \ + { \ + return pxa_setbrg_dev(UART##_INDEX); \ + } \ + \ + void uart##_putc(const char c) \ + { \ + return pxa_putc_dev(UART##_INDEX, c); \ + } \ + \ + void uart##_puts(const char *s) \ + { \ + return pxa_puts_dev(UART##_INDEX, s); \ + } \ + \ + int uart##_getc(void) \ + { \ + return pxa_getc_dev(UART##_INDEX); \ + } \ + \ + int uart##_tstc(void) \ + { \ + return pxa_tstc_dev(UART##_INDEX); \ + } \ + +#define pxa_uart_desc(uart) \ + struct serial_device serial_##uart##_device = \ + { \ + "serial_"#uart, \ + uart##_init, \ + NULL, \ + uart##_setbrg, \ + uart##_getc, \ + uart##_tstc, \ + uart##_putc, \ + uart##_puts, \ + }; + +#define pxa_uart_multi(uart, UART) \ + pxa_uart(uart, UART) \ + pxa_uart_desc(uart) + +#if defined(CONFIG_HWUART) + pxa_uart_multi(hwuart, HWUART) #endif - -#if defined (CONFIG_BTUART) -static int btuart_init(void) -{ - return pxa_init_dev(BTUART_INDEX); -} - -static void btuart_setbrg(void) -{ - return pxa_setbrg_dev(BTUART_INDEX); -} - -static void btuart_putc(const char c) -{ - return pxa_putc_dev(BTUART_INDEX,c); -} - -static void btuart_puts(const char *s) -{ - return pxa_puts_dev(BTUART_INDEX,s); -} - -static int btuart_getc(void) -{ - return pxa_getc_dev(BTUART_INDEX); -} - -static int btuart_tstc(void) -{ - return pxa_tstc_dev(BTUART_INDEX); -} - -struct serial_device serial_btuart_device = -{ - "serial_btuart", - btuart_init, - NULL, - btuart_setbrg, - btuart_getc, - btuart_tstc, - btuart_putc, - btuart_puts, -}; +#if defined(CONFIG_STUART) + pxa_uart_multi(stuart, STUART) #endif - -#if defined (CONFIG_STUART) -static int stuart_init(void) -{ - return pxa_init_dev(STUART_INDEX); -} - -static void stuart_setbrg(void) -{ - return pxa_setbrg_dev(STUART_INDEX); -} - -static void stuart_putc(const char c) -{ - return pxa_putc_dev(STUART_INDEX,c); -} - -static void stuart_puts(const char *s) -{ - return pxa_puts_dev(STUART_INDEX,s); -} - -static int stuart_getc(void) -{ - return pxa_getc_dev(STUART_INDEX); -} - -static int stuart_tstc(void) -{ - return pxa_tstc_dev(STUART_INDEX); -} - -struct serial_device serial_stuart_device = -{ - "serial_stuart", - stuart_init, - NULL, - stuart_setbrg, - stuart_getc, - stuart_tstc, - stuart_putc, - stuart_puts, -}; +#if defined(CONFIG_FFUART) + pxa_uart_multi(ffuart, FFUART) +#endif +#if defined(CONFIG_BTUART) + pxa_uart_multi(btuart, BTUART) #endif - -#ifndef CONFIG_SERIAL_MULTI -inline int serial_init(void) { - return (pxa_init_dev(UART_INDEX)); -} -void serial_setbrg(void) { - pxa_setbrg_dev(UART_INDEX); -} -int serial_getc(void) { - return(pxa_getc_dev(UART_INDEX)); -} -int serial_tstc(void) { - return(pxa_tstc_dev(UART_INDEX)); -} -void serial_putc(const char c) { - pxa_putc_dev(UART_INDEX,c); -} -void serial_puts(const char *s) { - pxa_puts_dev(UART_INDEX,s); -} -#endif /* CONFIG_SERIAL_MULTI */ +#ifndef CONFIG_SERIAL_MULTI + pxa_uart(serial, UART) +#endif diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 84ad6fa..6f389f0 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -37,6 +37,7 @@ COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o +COBJS-$(CONFIG_MXS_SPI) += mxs_spi.o COBJS-$(CONFIG_OC_TINY_SPI) += oc_tiny_spi.o COBJS-$(CONFIG_OMAP3_SPI) += omap3_spi.o COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 33e38b6..83ef8e8 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -136,13 +136,11 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, unsigned int len_tx; unsigned int len_rx; unsigned int len; - int ret; u32 status; const u8 *txp = dout; u8 *rxp = din; u8 value; - ret = 0; if (bitlen == 0) /* Finish any previously submitted transfers */ goto out; diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c new file mode 100644 index 0000000..4c27fef --- /dev/null +++ b/drivers/spi/mxs_spi.c @@ -0,0 +1,186 @@ +/* + * Freescale i.MX28 SPI driver + * + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * on behalf of DENX Software Engineering GmbH + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * NOTE: This driver only supports the SPI-controller chipselects, + * GPIO driven chipselects are not supported. + */ + +#include <common.h> +#include <malloc.h> +#include <spi.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> + +#define MXS_SPI_MAX_TIMEOUT 1000000 +#define MXS_SPI_PORT_OFFSET 0x2000 + +struct mxs_spi_slave { + struct spi_slave slave; + uint32_t max_khz; + uint32_t mode; + struct mx28_ssp_regs *regs; +}; + +static inline struct mxs_spi_slave *to_mxs_slave(struct spi_slave *slave) +{ + return container_of(slave, struct mxs_spi_slave, slave); +} + +void spi_init(void) +{ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct mxs_spi_slave *mxs_slave; + uint32_t addr; + + if (bus > 3) { + printf("MXS SPI: Max bus number is 3\n"); + return NULL; + } + + mxs_slave = malloc(sizeof(struct mxs_spi_slave)); + if (!mxs_slave) + return NULL; + + addr = MXS_SSP0_BASE + (bus * MXS_SPI_PORT_OFFSET); + + mxs_slave->slave.bus = bus; + mxs_slave->slave.cs = cs; + mxs_slave->max_khz = max_hz / 1000; + mxs_slave->mode = mode; + mxs_slave->regs = (struct mx28_ssp_regs *)addr; + + return &mxs_slave->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave); + free(mxs_slave); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave); + struct mx28_ssp_regs *ssp_regs = mxs_slave->regs; + uint32_t reg = 0; + + mx28_reset_block(&ssp_regs->hw_ssp_ctrl0_reg); + + writel(SSP_CTRL0_BUS_WIDTH_ONE_BIT, &ssp_regs->hw_ssp_ctrl0); + + reg = SSP_CTRL1_SSP_MODE_SPI | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS; + reg |= (mxs_slave->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0; + reg |= (mxs_slave->mode & SPI_CPHA) ? SSP_CTRL1_PHASE : 0; + writel(reg, &ssp_regs->hw_ssp_ctrl1); + + writel(0, &ssp_regs->hw_ssp_cmd0); + + mx28_set_ssp_busclock(slave->bus, mxs_slave->max_khz); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ +} + +static void mxs_spi_start_xfer(struct mx28_ssp_regs *ssp_regs) +{ + writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_set); + writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_clr); +} + +static void mxs_spi_end_xfer(struct mx28_ssp_regs *ssp_regs) +{ + writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_clr); + writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_set); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave); + struct mx28_ssp_regs *ssp_regs = mxs_slave->regs; + int len = bitlen / 8; + const char *tx = dout; + char *rx = din; + + if (bitlen == 0) + return 0; + + if (!rx && !tx) + return 0; + + if (flags & SPI_XFER_BEGIN) + mxs_spi_start_xfer(ssp_regs); + + while (len--) { + /* We transfer 1 byte */ + writel(1, &ssp_regs->hw_ssp_xfer_size); + + if ((flags & SPI_XFER_END) && !len) + mxs_spi_end_xfer(ssp_regs); + + if (tx) + writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_clr); + else + writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_set); + + writel(SSP_CTRL0_RUN, &ssp_regs->hw_ssp_ctrl0_set); + + if (mx28_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg, + SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) { + printf("MXS SPI: Timeout waiting for start\n"); + return -1; + } + + if (tx) + writel(*tx++, &ssp_regs->hw_ssp_data); + + writel(SSP_CTRL0_DATA_XFER, &ssp_regs->hw_ssp_ctrl0_set); + + if (rx) { + if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_status_reg, + SSP_STATUS_FIFO_EMPTY, MXS_SPI_MAX_TIMEOUT)) { + printf("MXS SPI: Timeout waiting for data\n"); + return -1; + } + + *rx = readl(&ssp_regs->hw_ssp_data); + rx++; + } + + if (mx28_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg, + SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) { + printf("MXS SPI: Timeout waiting for finish\n"); + return -1; + } + } + + return 0; +} diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 51b2494..09abb75 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -41,6 +41,7 @@ else COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o endif COBJS-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o +COBJS-$(CONFIG_USB_EHCI_MXS) += ehci-mxs.o COBJS-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o COBJS-$(CONFIG_USB_EHCI_IXP4XX) += ehci-ixp.o COBJS-$(CONFIG_USB_EHCI_KIRKWOOD) += ehci-kirkwood.o diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 5a65d92..b2d294e 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -40,11 +40,13 @@ int ehci_hcd_init(void) { struct usb_ehci *ehci; - char usb_phy[5]; const char *phy_type = NULL; size_t len; +#ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY + char usb_phy[5]; usb_phy[0] = '\0'; +#endif ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB_ADDR; hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index a0cfbb7..f403d49 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -120,7 +120,6 @@ int ehci_hcd_init(void) udelay(80); - /* Take USB2 */ ehci = (struct usb_ehci *)(IMX_USB_BASE + (0x200 * CONFIG_MXC_USB_PORT)); hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); diff --git a/drivers/usb/host/ehci-mxs.c b/drivers/usb/host/ehci-mxs.c new file mode 100644 index 0000000..c795f23 --- /dev/null +++ b/drivers/usb/host/ehci-mxs.c @@ -0,0 +1,154 @@ +/* + * Freescale i.MX28 USB Host driver + * + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * on behalf of DENX Software Engineering GmbH + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/regs-common.h> +#include <asm/arch/regs-base.h> +#include <asm/arch/regs-clkctrl.h> +#include <asm/arch/regs-usb.h> +#include <asm/arch/regs-usbphy.h> + +#include "ehci-core.h" +#include "ehci.h" + +#if (CONFIG_EHCI_MXS_PORT != 0) && (CONFIG_EHCI_MXS_PORT != 1) +#error "MXS EHCI: Invalid port selected!" +#endif + +#ifndef CONFIG_EHCI_MXS_PORT +#error "MXS EHCI: Please define correct port using CONFIG_EHCI_MXS_PORT!" +#endif + +static struct ehci_mxs { + struct mx28_usb_regs *usb_regs; + struct mx28_usbphy_regs *phy_regs; +} ehci_mxs; + +int mxs_ehci_get_port(struct ehci_mxs *mxs_usb, int port) +{ + uint32_t usb_base, phy_base; + switch (port) { + case 0: + usb_base = MXS_USBCTRL0_BASE; + phy_base = MXS_USBPHY0_BASE; + break; + case 1: + usb_base = MXS_USBCTRL1_BASE; + phy_base = MXS_USBPHY1_BASE; + break; + default: + printf("CONFIG_EHCI_MXS_PORT (port = %d)\n", port); + return -1; + } + + mxs_usb->usb_regs = (struct mx28_usb_regs *)usb_base; + mxs_usb->phy_regs = (struct mx28_usbphy_regs *)phy_base; + return 0; +} + +/* This DIGCTL register ungates clock to USB */ +#define HW_DIGCTL_CTRL 0x8001c000 +#define HW_DIGCTL_CTRL_USB0_CLKGATE (1 << 2) +#define HW_DIGCTL_CTRL_USB1_CLKGATE (1 << 16) + +int ehci_hcd_init(void) +{ + + int ret; + uint32_t usb_base, cap_base; + struct mx28_register *digctl_ctrl = + (struct mx28_register *)HW_DIGCTL_CTRL; + struct mx28_clkctrl_regs *clkctrl_regs = + (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE; + + ret = mxs_ehci_get_port(&ehci_mxs, CONFIG_EHCI_MXS_PORT); + if (ret) + return ret; + + /* Reset the PHY block */ + writel(USBPHY_CTRL_SFTRST, &ehci_mxs.phy_regs->hw_usbphy_ctrl_set); + udelay(10); + writel(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE, + &ehci_mxs.phy_regs->hw_usbphy_ctrl_clr); + + /* Enable USB clock */ + writel(CLKCTRL_PLL0CTRL0_EN_USB_CLKS | CLKCTRL_PLL0CTRL0_POWER, + &clkctrl_regs->hw_clkctrl_pll0ctrl0_set); + writel(CLKCTRL_PLL1CTRL0_EN_USB_CLKS | CLKCTRL_PLL1CTRL0_POWER, + &clkctrl_regs->hw_clkctrl_pll1ctrl0_set); + + writel(HW_DIGCTL_CTRL_USB0_CLKGATE | HW_DIGCTL_CTRL_USB1_CLKGATE, + &digctl_ctrl->reg_clr); + + /* Start USB PHY */ + writel(0, &ehci_mxs.phy_regs->hw_usbphy_pwd); + + /* Enable UTMI+ Level 2 and Level 3 compatibility */ + writel(USBPHY_CTRL_ENUTMILEVEL3 | USBPHY_CTRL_ENUTMILEVEL2 | 1, + &ehci_mxs.phy_regs->hw_usbphy_ctrl_set); + + usb_base = ((uint32_t)ehci_mxs.usb_regs) + 0x100; + hccr = (struct ehci_hccr *)usb_base; + + cap_base = ehci_readl(&hccr->cr_capbase); + hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base)); + + return 0; +} + +int ehci_hcd_stop(void) +{ + int ret; + uint32_t tmp; + struct mx28_register *digctl_ctrl = + (struct mx28_register *)HW_DIGCTL_CTRL; + struct mx28_clkctrl_regs *clkctrl_regs = + (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE; + + ret = mxs_ehci_get_port(&ehci_mxs, CONFIG_EHCI_MXS_PORT); + if (ret) + return ret; + + /* Stop the USB port */ + tmp = ehci_readl(&hcor->or_usbcmd); + tmp &= ~CMD_RUN; + ehci_writel(tmp, &hcor->or_usbcmd); + + /* Disable the PHY */ + tmp = USBPHY_PWD_RXPWDRX | USBPHY_PWD_RXPWDDIFF | + USBPHY_PWD_RXPWD1PT1 | USBPHY_PWD_RXPWDENV | + USBPHY_PWD_TXPWDV2I | USBPHY_PWD_TXPWDIBIAS | + USBPHY_PWD_TXPWDFS; + writel(tmp, &ehci_mxs.phy_regs->hw_usbphy_pwd); + + /* Disable USB clock */ + writel(CLKCTRL_PLL0CTRL0_EN_USB_CLKS, + &clkctrl_regs->hw_clkctrl_pll0ctrl0_clr); + writel(CLKCTRL_PLL1CTRL0_EN_USB_CLKS, + &clkctrl_regs->hw_clkctrl_pll1ctrl0_clr); + + /* Gate off the USB clock */ + writel(HW_DIGCTL_CTRL_USB0_CLKGATE | HW_DIGCTL_CTRL_USB1_CLKGATE, + &digctl_ctrl->reg_set); + + return 0; +} diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index f56f2df..359c635 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -26,6 +26,10 @@ #include "davinci.h" #include <asm/arch/hardware.h> +#if !defined(CONFIG_DV_USBPHY_CTL) +#define CONFIG_DV_USBPHY_CTL (USBPHY_SESNDEN | USBPHY_VBDTCTEN) +#endif + /* MUSB platform configuration */ struct musb_config musb_cfg = { .regs = (struct musb_regs *)MENTOR_USB0_BASE, @@ -50,7 +54,7 @@ static u8 phy_on(void) writel(USBPHY_PHY24MHZ | USBPHY_SESNDEN | USBPHY_VBDTCTEN, USBPHY_CTL_PADDR); #else - writel(USBPHY_SESNDEN | USBPHY_VBDTCTEN, USBPHY_CTL_PADDR); + writel(CONFIG_DV_USBPHY_CTL, USBPHY_CTL_PADDR); #endif timeout = musb_cfg.timeout; @@ -78,6 +82,17 @@ static void phy_off(void) writel(USBPHY_OSCPDWN | USBPHY_PHYPDWN, USBPHY_CTL_PADDR); } +void __enable_vbus(void) +{ + /* + * nothing to do, vbus is handled through the cpu. + * Define this function in board code, if it is + * different on your board. + */ +} +void enable_vbus(void) + __attribute__((weak, alias("__enable_vbus"))); + /* * This function performs Davinci platform specific initialization for usb0. */ @@ -86,9 +101,8 @@ int musb_platform_init(void) u32 revision; /* enable USB VBUS */ -#ifndef DAVINCI_DM365EVM enable_vbus(); -#endif + /* start the on-chip USB phy and its pll */ if (!phy_on()) return -1; diff --git a/drivers/video/Makefile b/drivers/video/Makefile index ecc1896..6252f6a 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -33,6 +33,7 @@ COBJS-$(CONFIG_S6E63D6) += s6e63d6.o COBJS-$(CONFIG_SED156X) += sed156x.o COBJS-$(CONFIG_VIDEO_AMBA) += amba.o COBJS-$(CONFIG_VIDEO_CT69000) += ct69000.o videomodes.o +COBJS-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o COBJS-$(CONFIG_VIDEO_MB862xx) += mb862xx.o videomodes.o COBJS-$(CONFIG_VIDEO_MB86R0xGDC) += mb86r0xgdc.o videomodes.o COBJS-$(CONFIG_VIDEO_MX3) += mx3fb.o videomodes.o diff --git a/drivers/video/bus_vcxk.c b/drivers/video/bus_vcxk.c index 7aecb92..9c4714d 100644 --- a/drivers/video/bus_vcxk.c +++ b/drivers/video/bus_vcxk.c @@ -393,7 +393,6 @@ int vcxk_display_bitmap(ulong addr, int x, int y) unsigned long width; unsigned long height; unsigned long bpp; - unsigned long compression; unsigned long lw; @@ -404,7 +403,6 @@ int vcxk_display_bitmap(ulong addr, int x, int y) bmp = (bmp_image_t *) addr; if ((bmp->header.signature[0] == 'B') && (bmp->header.signature[1] == 'M')) { - compression = le32_to_cpu(bmp->header.compression); width = le32_to_cpu(bmp->header.width); height = le32_to_cpu(bmp->header.height); bpp = le16_to_cpu(bmp->header.bit_count); diff --git a/drivers/video/cfb_console.c b/drivers/video/cfb_console.c index 1863563..32e890c 100644 --- a/drivers/video/cfb_console.c +++ b/drivers/video/cfb_console.c @@ -101,6 +101,7 @@ #include <common.h> #include <version.h> #include <malloc.h> +#include <linux/compiler.h> /* * Console device defines with SMI graphic @@ -202,6 +203,7 @@ #include <linux/types.h> #include <stdio_dev.h> #include <video_font.h> +#include <video_font_data.h> #if defined(CONFIG_CMD_DATE) #include <rtc.h> @@ -285,6 +287,7 @@ void console_cursor(int state); #ifdef CONFIG_VIDEO_LOGO #ifdef CONFIG_VIDEO_BMP_LOGO #include <bmp_logo.h> +#include <bmp_logo_data.h> #define VIDEO_LOGO_WIDTH BMP_LOGO_WIDTH #define VIDEO_LOGO_HEIGHT BMP_LOGO_HEIGHT #define VIDEO_LOGO_LUT_OFFSET BMP_LOGO_OFFSET @@ -1560,7 +1563,8 @@ void logo_plot(void *screen, int width, int x, int y) static void *video_logo(void) { char info[128]; - int space, len, y_off = 0; + int space, len; + __maybe_unused int y_off = 0; #ifdef CONFIG_SPLASH_SCREEN char *s; diff --git a/drivers/video/ct69000.c b/drivers/video/ct69000.c index 3db614d..0ed5f41 100644 --- a/drivers/video/ct69000.c +++ b/drivers/video/ct69000.c @@ -35,9 +35,11 @@ #undef VGA_DEBUG #undef VGA_DUMP_REG #ifdef VGA_DEBUG -#define PRINTF(fmt,args...) printf (fmt ,##args) +#undef _DEBUG +#define _DEBUG 1 #else -#define PRINTF(fmt,args...) +#undef _DEBUG +#define _DEBUG 0 #endif /* Macros */ @@ -740,7 +742,7 @@ FindAndSetPllParamIntoXrRegs (unsigned int pixelclock, } m += param->mn_diff; n += param->mn_diff; - PRINTF ("VCO %d, pd %d, m %d n %d vld %d \n", fvco, pd, m, n, vld); + debug("VCO %d, pd %d, m %d n %d vld %d\n", fvco, pd, m, n, vld); xr_cb = ((0x7 & PD) << 4) | (vld == param->vld_set ? 0x04 : 0); /* All four of the registers used for dot clock 2 (XRC8 - XRCB) must be * written, and in order from XRC8 to XRCB, before the hardware will @@ -751,7 +753,7 @@ FindAndSetPllParamIntoXrRegs (unsigned int pixelclock, ctWrite_i (CT_XR_O, 0xca, 0); /* because of a hw bug I guess, but we write */ ctWrite_i (CT_XR_O, 0xcb, xr_cb); /* 0 to it for savety */ new_pixclock = ReadPixClckFromXrRegsBack (param); - PRINTF ("pixelclock.set = %d, pixelclock.real = %d \n", + debug("pixelclock.set = %d, pixelclock.real = %d\n", pixelclock, new_pixclock); } @@ -1119,7 +1121,7 @@ video_hw_init (void) pGD->dprBase &= 0xfffff000; pGD->dprBase += 0x00001000; } - PRINTF ("Cursor Start %x Pattern Start %x\n", pGD->dprBase, + debug("Cursor Start %x Pattern Start %x\n", pGD->dprBase, PATTERN_ADR); pGD->vprBase = pci_mem_base; /* Dummy */ pGD->cprBase = pci_mem_base; /* Dummy */ diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c new file mode 100644 index 0000000..a2981b1 --- /dev/null +++ b/drivers/video/da8xx-fb.c @@ -0,0 +1,843 @@ +/* + * Porting to u-boot: + * + * (C) Copyright 2011 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de. + * + * Copyright (C) 2008-2009 MontaVista Software Inc. + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * Based on the LCD driver for TI Avalanche processors written by + * Ajay Singh and Shalom Hai. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <video_fb.h> +#include <linux/list.h> +#include <linux/fb.h> + +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/hardware.h> + +#include "videomodes.h" +#include <asm/arch/da8xx-fb.h> + +#define DRIVER_NAME "da8xx_lcdc" + +/* LCD Status Register */ +#define LCD_END_OF_FRAME1 (1 << 9) +#define LCD_END_OF_FRAME0 (1 << 8) +#define LCD_PL_LOAD_DONE (1 << 6) +#define LCD_FIFO_UNDERFLOW (1 << 5) +#define LCD_SYNC_LOST (1 << 2) + +/* LCD DMA Control Register */ +#define LCD_DMA_BURST_SIZE(x) ((x) << 4) +#define LCD_DMA_BURST_1 0x0 +#define LCD_DMA_BURST_2 0x1 +#define LCD_DMA_BURST_4 0x2 +#define LCD_DMA_BURST_8 0x3 +#define LCD_DMA_BURST_16 0x4 +#define LCD_END_OF_FRAME_INT_ENA (1 << 2) +#define LCD_DUAL_FRAME_BUFFER_ENABLE (1 << 0) + +/* LCD Control Register */ +#define LCD_CLK_DIVISOR(x) ((x) << 8) +#define LCD_RASTER_MODE 0x01 + +/* LCD Raster Control Register */ +#define LCD_PALETTE_LOAD_MODE(x) ((x) << 20) +#define PALETTE_AND_DATA 0x00 +#define PALETTE_ONLY 0x01 +#define DATA_ONLY 0x02 + +#define LCD_MONO_8BIT_MODE (1 << 9) +#define LCD_RASTER_ORDER (1 << 8) +#define LCD_TFT_MODE (1 << 7) +#define LCD_UNDERFLOW_INT_ENA (1 << 6) +#define LCD_PL_ENABLE (1 << 4) +#define LCD_MONOCHROME_MODE (1 << 1) +#define LCD_RASTER_ENABLE (1 << 0) +#define LCD_TFT_ALT_ENABLE (1 << 23) +#define LCD_STN_565_ENABLE (1 << 24) + +/* LCD Raster Timing 2 Register */ +#define LCD_AC_BIAS_TRANSITIONS_PER_INT(x) ((x) << 16) +#define LCD_AC_BIAS_FREQUENCY(x) ((x) << 8) +#define LCD_SYNC_CTRL (1 << 25) +#define LCD_SYNC_EDGE (1 << 24) +#define LCD_INVERT_PIXEL_CLOCK (1 << 22) +#define LCD_INVERT_LINE_CLOCK (1 << 21) +#define LCD_INVERT_FRAME_CLOCK (1 << 20) + +/* LCD Block */ +struct da8xx_lcd_regs { + u32 revid; + u32 ctrl; + u32 stat; + u32 lidd_ctrl; + u32 lidd_cs0_conf; + u32 lidd_cs0_addr; + u32 lidd_cs0_data; + u32 lidd_cs1_conf; + u32 lidd_cs1_addr; + u32 lidd_cs1_data; + u32 raster_ctrl; + u32 raster_timing_0; + u32 raster_timing_1; + u32 raster_timing_2; + u32 raster_subpanel; + u32 reserved; + u32 dma_ctrl; + u32 dma_frm_buf_base_addr_0; + u32 dma_frm_buf_ceiling_addr_0; + u32 dma_frm_buf_base_addr_1; + u32 dma_frm_buf_ceiling_addr_1; +}; + +#define LCD_NUM_BUFFERS 1 + +#define WSI_TIMEOUT 50 +#define PALETTE_SIZE 256 +#define LEFT_MARGIN 64 +#define RIGHT_MARGIN 64 +#define UPPER_MARGIN 32 +#define LOWER_MARGIN 32 + +#define calc_fbsize() (panel.plnSizeX * panel.plnSizeY * panel.gdfBytesPP) +#define mdelay(n) ({unsigned long msec = (n); while (msec--) udelay(1000); }) + +static struct da8xx_lcd_regs *da8xx_fb_reg_base; + +DECLARE_GLOBAL_DATA_PTR; + +/* graphics setup */ +static GraphicDevice gpanel; +static const struct da8xx_panel *lcd_panel; +static struct fb_info *da8xx_fb_info; +static int bits_x_pixel; + +static inline unsigned int lcdc_read(u32 *addr) +{ + return (unsigned int)readl(addr); +} + +static inline void lcdc_write(unsigned int val, u32 *addr) +{ + writel(val, addr); +} + +struct da8xx_fb_par { + u32 p_palette_base; + unsigned char *v_palette_base; + dma_addr_t vram_phys; + unsigned long vram_size; + void *vram_virt; + unsigned int dma_start; + unsigned int dma_end; + struct clk *lcdc_clk; + int irq; + unsigned short pseudo_palette[16]; + unsigned int palette_sz; + unsigned int pxl_clk; + int blank; + int vsync_flag; + int vsync_timeout; +}; + + +/* Variable Screen Information */ +static struct fb_var_screeninfo da8xx_fb_var = { + .xoffset = 0, + .yoffset = 0, + .transp = {0, 0, 0}, + .nonstd = 0, + .activate = 0, + .height = -1, + .width = -1, + .pixclock = 46666, /* 46us - AUO display */ + .accel_flags = 0, + .left_margin = LEFT_MARGIN, + .right_margin = RIGHT_MARGIN, + .upper_margin = UPPER_MARGIN, + .lower_margin = LOWER_MARGIN, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED +}; + +static struct fb_fix_screeninfo da8xx_fb_fix = { + .id = "DA8xx FB Drv", + .type = FB_TYPE_PACKED_PIXELS, + .type_aux = 0, + .visual = FB_VISUAL_PSEUDOCOLOR, + .xpanstep = 0, + .ypanstep = 1, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE +}; + +static const struct display_panel disp_panel = { + QVGA, + 16, + 16, + COLOR_ACTIVE, +}; + +static const struct lcd_ctrl_config lcd_cfg = { + &disp_panel, + .ac_bias = 255, + .ac_bias_intrpt = 0, + .dma_burst_sz = 16, + .bpp = 16, + .fdd = 255, + .tft_alt_mode = 0, + .stn_565_mode = 0, + .mono_8bit_mode = 0, + .invert_line_clock = 1, + .invert_frm_clock = 1, + .sync_edge = 0, + .sync_ctrl = 1, + .raster_order = 0, +}; + +/* Enable the Raster Engine of the LCD Controller */ +static inline void lcd_enable_raster(void) +{ + u32 reg; + + reg = lcdc_read(&da8xx_fb_reg_base->raster_ctrl); + if (!(reg & LCD_RASTER_ENABLE)) + lcdc_write(reg | LCD_RASTER_ENABLE, + &da8xx_fb_reg_base->raster_ctrl); +} + +/* Disable the Raster Engine of the LCD Controller */ +static inline void lcd_disable_raster(void) +{ + u32 reg; + + reg = lcdc_read(&da8xx_fb_reg_base->raster_ctrl); + if (reg & LCD_RASTER_ENABLE) + lcdc_write(reg & ~LCD_RASTER_ENABLE, + &da8xx_fb_reg_base->raster_ctrl); +} + +static void lcd_blit(int load_mode, struct da8xx_fb_par *par) +{ + u32 start; + u32 end; + u32 reg_ras; + u32 reg_dma; + + /* init reg to clear PLM (loading mode) fields */ + reg_ras = lcdc_read(&da8xx_fb_reg_base->raster_ctrl); + reg_ras &= ~(3 << 20); + + reg_dma = lcdc_read(&da8xx_fb_reg_base->dma_ctrl); + + if (load_mode == LOAD_DATA) { + start = par->dma_start; + end = par->dma_end; + + reg_ras |= LCD_PALETTE_LOAD_MODE(DATA_ONLY); + reg_dma |= LCD_END_OF_FRAME_INT_ENA; + +#if (LCD_NUM_BUFFERS == 2) + reg_dma |= LCD_DUAL_FRAME_BUFFER_ENABLE; + lcdc_write(start, &da8xx_fb_reg_base->dma_frm_buf_base_addr_0); + lcdc_write(end, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0); + lcdc_write(start, &da8xx_fb_reg_base->dma_frm_buf_base_addr_1); + lcdc_write(end, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_1); +#else + reg_dma &= ~LCD_DUAL_FRAME_BUFFER_ENABLE; + lcdc_write(start, &da8xx_fb_reg_base->dma_frm_buf_base_addr_0); + lcdc_write(end, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0); + lcdc_write(0, &da8xx_fb_reg_base->dma_frm_buf_base_addr_1); + lcdc_write(0, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_1); +#endif + + } else if (load_mode == LOAD_PALETTE) { + start = par->p_palette_base; + end = start + par->palette_sz - 1; + + reg_ras |= LCD_PALETTE_LOAD_MODE(PALETTE_ONLY); + reg_ras |= LCD_PL_ENABLE; + + lcdc_write(start, &da8xx_fb_reg_base->dma_frm_buf_base_addr_0); + lcdc_write(end, &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0); + } + + lcdc_write(reg_dma, &da8xx_fb_reg_base->dma_ctrl); + lcdc_write(reg_ras, &da8xx_fb_reg_base->raster_ctrl); + + /* + * The Raster enable bit must be set after all other control fields are + * set. + */ + lcd_enable_raster(); +} + +/* Configure the Burst Size of DMA */ +static int lcd_cfg_dma(int burst_size) +{ + u32 reg; + + reg = lcdc_read(&da8xx_fb_reg_base->dma_ctrl) & 0x00000001; + switch (burst_size) { + case 1: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_1); + break; + case 2: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_2); + break; + case 4: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_4); + break; + case 8: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_8); + break; + case 16: + reg |= LCD_DMA_BURST_SIZE(LCD_DMA_BURST_16); + break; + default: + return -EINVAL; + } + lcdc_write(reg, &da8xx_fb_reg_base->dma_ctrl); + + return 0; +} + +static void lcd_cfg_ac_bias(int period, int transitions_per_int) +{ + u32 reg; + + /* Set the AC Bias Period and Number of Transisitons per Interrupt */ + reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_2) & 0xFFF00000; + reg |= LCD_AC_BIAS_FREQUENCY(period) | + LCD_AC_BIAS_TRANSITIONS_PER_INT(transitions_per_int); + lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_2); +} + +static void lcd_cfg_horizontal_sync(int back_porch, int pulse_width, + int front_porch) +{ + u32 reg; + + reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_0) & 0xf; + reg |= ((back_porch & 0xff) << 24) + | ((front_porch & 0xff) << 16) + | ((pulse_width & 0x3f) << 10); + lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_0); +} + +static void lcd_cfg_vertical_sync(int back_porch, int pulse_width, + int front_porch) +{ + u32 reg; + + reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_1) & 0x3ff; + reg |= ((back_porch & 0xff) << 24) + | ((front_porch & 0xff) << 16) + | ((pulse_width & 0x3f) << 10); + lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_1); +} + +static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) +{ + u32 reg; + + reg = lcdc_read(&da8xx_fb_reg_base->raster_ctrl) & ~(LCD_TFT_MODE | + LCD_MONO_8BIT_MODE | + LCD_MONOCHROME_MODE); + + switch (cfg->p_disp_panel->panel_shade) { + case MONOCHROME: + reg |= LCD_MONOCHROME_MODE; + if (cfg->mono_8bit_mode) + reg |= LCD_MONO_8BIT_MODE; + break; + case COLOR_ACTIVE: + reg |= LCD_TFT_MODE; + if (cfg->tft_alt_mode) + reg |= LCD_TFT_ALT_ENABLE; + break; + + case COLOR_PASSIVE: + if (cfg->stn_565_mode) + reg |= LCD_STN_565_ENABLE; + break; + + default: + return -EINVAL; + } + + /* enable additional interrupts here */ + reg |= LCD_UNDERFLOW_INT_ENA; + + lcdc_write(reg, &da8xx_fb_reg_base->raster_ctrl); + + reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_2); + + if (cfg->sync_ctrl) + reg |= LCD_SYNC_CTRL; + else + reg &= ~LCD_SYNC_CTRL; + + if (cfg->sync_edge) + reg |= LCD_SYNC_EDGE; + else + reg &= ~LCD_SYNC_EDGE; + + if (cfg->invert_line_clock) + reg |= LCD_INVERT_LINE_CLOCK; + else + reg &= ~LCD_INVERT_LINE_CLOCK; + + if (cfg->invert_frm_clock) + reg |= LCD_INVERT_FRAME_CLOCK; + else + reg &= ~LCD_INVERT_FRAME_CLOCK; + + lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_2); + + return 0; +} + +static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, + u32 bpp, u32 raster_order) +{ + u32 reg; + + /* Set the Panel Width */ + /* Pixels per line = (PPL + 1)*16 */ + /*0x3F in bits 4..9 gives max horisontal resolution = 1024 pixels*/ + width &= 0x3f0; + reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_0); + reg &= 0xfffffc00; + reg |= ((width >> 4) - 1) << 4; + lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_0); + + /* Set the Panel Height */ + reg = lcdc_read(&da8xx_fb_reg_base->raster_timing_1); + reg = ((height - 1) & 0x3ff) | (reg & 0xfffffc00); + lcdc_write(reg, &da8xx_fb_reg_base->raster_timing_1); + + /* Set the Raster Order of the Frame Buffer */ + reg = lcdc_read(&da8xx_fb_reg_base->raster_ctrl) & ~(1 << 8); + if (raster_order) + reg |= LCD_RASTER_ORDER; + lcdc_write(reg, &da8xx_fb_reg_base->raster_ctrl); + + switch (bpp) { + case 1: + case 2: + case 4: + case 16: + par->palette_sz = 16 * 2; + break; + + case 8: + par->palette_sz = 256 * 2; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + struct da8xx_fb_par *par = info->par; + unsigned short *palette = (unsigned short *) par->v_palette_base; + u_short pal; + int update_hw = 0; + + if (regno > 255) + return 1; + + if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) + return 1; + + if (info->var.bits_per_pixel == 8) { + red >>= 4; + green >>= 8; + blue >>= 12; + + pal = (red & 0x0f00); + pal |= (green & 0x00f0); + pal |= (blue & 0x000f); + + if (palette[regno] != pal) { + update_hw = 1; + palette[regno] = pal; + } + } else if ((info->var.bits_per_pixel == 16) && regno < 16) { + red >>= (16 - info->var.red.length); + red <<= info->var.red.offset; + + green >>= (16 - info->var.green.length); + green <<= info->var.green.offset; + + blue >>= (16 - info->var.blue.length); + blue <<= info->var.blue.offset; + + par->pseudo_palette[regno] = red | green | blue; + + if (palette[0] != 0x4000) { + update_hw = 1; + palette[0] = 0x4000; + } + } + + /* Update the palette in the h/w as needed. */ + if (update_hw) + lcd_blit(LOAD_PALETTE, par); + + return 0; +} + +static void lcd_reset(struct da8xx_fb_par *par) +{ + /* Disable the Raster if previously Enabled */ + lcd_disable_raster(); + + /* DMA has to be disabled */ + lcdc_write(0, &da8xx_fb_reg_base->dma_ctrl); + lcdc_write(0, &da8xx_fb_reg_base->raster_ctrl); +} + +static void lcd_calc_clk_divider(struct da8xx_fb_par *par) +{ + unsigned int lcd_clk, div; + + /* Get clock from sysclk2 */ + lcd_clk = clk_get(2); + + div = lcd_clk / par->pxl_clk; + debug("LCD Clock: 0x%x Divider: 0x%x PixClk: 0x%x\n", + lcd_clk, div, par->pxl_clk); + + /* Configure the LCD clock divisor. */ + lcdc_write(LCD_CLK_DIVISOR(div) | + (LCD_RASTER_MODE & 0x1), &da8xx_fb_reg_base->ctrl); +} + +static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, + const struct da8xx_panel *panel) +{ + u32 bpp; + int ret = 0; + + lcd_reset(par); + + /* Calculate the divider */ + lcd_calc_clk_divider(par); + + if (panel->invert_pxl_clk) + lcdc_write((lcdc_read(&da8xx_fb_reg_base->raster_timing_2) | + LCD_INVERT_PIXEL_CLOCK), + &da8xx_fb_reg_base->raster_timing_2); + else + lcdc_write((lcdc_read(&da8xx_fb_reg_base->raster_timing_2) & + ~LCD_INVERT_PIXEL_CLOCK), + &da8xx_fb_reg_base->raster_timing_2); + + /* Configure the DMA burst size. */ + ret = lcd_cfg_dma(cfg->dma_burst_sz); + if (ret < 0) + return ret; + + /* Configure the AC bias properties. */ + lcd_cfg_ac_bias(cfg->ac_bias, cfg->ac_bias_intrpt); + + /* Configure the vertical and horizontal sync properties. */ + lcd_cfg_vertical_sync(panel->vbp, panel->vsw, panel->vfp); + lcd_cfg_horizontal_sync(panel->hbp, panel->hsw, panel->hfp); + + /* Configure for disply */ + ret = lcd_cfg_display(cfg); + if (ret < 0) + return ret; + + if (QVGA != cfg->p_disp_panel->panel_type) + return -EINVAL; + + if (cfg->bpp <= cfg->p_disp_panel->max_bpp && + cfg->bpp >= cfg->p_disp_panel->min_bpp) + bpp = cfg->bpp; + else + bpp = cfg->p_disp_panel->max_bpp; + if (bpp == 12) + bpp = 16; + ret = lcd_cfg_frame_buffer(par, (unsigned int)panel->width, + (unsigned int)panel->height, bpp, + cfg->raster_order); + if (ret < 0) + return ret; + + /* Configure FDD */ + lcdc_write((lcdc_read(&da8xx_fb_reg_base->raster_ctrl) & 0xfff00fff) | + (cfg->fdd << 12), &da8xx_fb_reg_base->raster_ctrl); + + return 0; +} + +static void lcdc_dma_start(void) +{ + struct da8xx_fb_par *par = da8xx_fb_info->par; + lcdc_write(par->dma_start, + &da8xx_fb_reg_base->dma_frm_buf_base_addr_0); + lcdc_write(par->dma_end, + &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0); + lcdc_write(0, + &da8xx_fb_reg_base->dma_frm_buf_base_addr_1); + lcdc_write(0, + &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_1); +} + +static u32 lcdc_irq_handler(void) +{ + struct da8xx_fb_par *par = da8xx_fb_info->par; + u32 stat = lcdc_read(&da8xx_fb_reg_base->stat); + u32 reg_ras; + + if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) { + debug("LCD_SYNC_LOST\n"); + lcd_disable_raster(); + lcdc_write(stat, &da8xx_fb_reg_base->stat); + lcd_enable_raster(); + return LCD_SYNC_LOST; + } else if (stat & LCD_PL_LOAD_DONE) { + debug("LCD_PL_LOAD_DONE\n"); + /* + * Must disable raster before changing state of any control bit. + * And also must be disabled before clearing the PL loading + * interrupt via the following write to the status register. If + * this is done after then one gets multiple PL done interrupts. + */ + lcd_disable_raster(); + + lcdc_write(stat, &da8xx_fb_reg_base->stat); + + /* Disable PL completion inerrupt */ + reg_ras = lcdc_read(&da8xx_fb_reg_base->raster_ctrl); + reg_ras &= ~LCD_PL_ENABLE; + lcdc_write(reg_ras, &da8xx_fb_reg_base->raster_ctrl); + + /* Setup and start data loading mode */ + lcd_blit(LOAD_DATA, par); + return LCD_PL_LOAD_DONE; + } else { + lcdc_write(stat, &da8xx_fb_reg_base->stat); + + if (stat & LCD_END_OF_FRAME0) + debug("LCD_END_OF_FRAME0\n"); + + lcdc_write(par->dma_start, + &da8xx_fb_reg_base->dma_frm_buf_base_addr_0); + lcdc_write(par->dma_end, + &da8xx_fb_reg_base->dma_frm_buf_ceiling_addr_0); + par->vsync_flag = 1; + return LCD_END_OF_FRAME0; + } + return stat; +} + +static u32 wait_for_event(u32 event) +{ + u32 timeout = 50000; + u32 ret; + + do { + ret = lcdc_irq_handler(); + udelay(1000); + } while (!(ret & event)); + + if (timeout <= 0) { + printf("%s: event %d not hit\n", __func__, event); + return -1; + } + + return 0; + +} + +void *video_hw_init(void) +{ + struct da8xx_fb_par *par; + u32 size; + char *p; + + if (!lcd_panel) { + printf("Display not initialized\n"); + return NULL; + } + gpanel.winSizeX = lcd_panel->width; + gpanel.winSizeY = lcd_panel->height; + gpanel.plnSizeX = lcd_panel->width; + gpanel.plnSizeY = lcd_panel->height; + + switch (bits_x_pixel) { + case 24: + gpanel.gdfBytesPP = 4; + gpanel.gdfIndex = GDF_32BIT_X888RGB; + break; + case 16: + gpanel.gdfBytesPP = 2; + gpanel.gdfIndex = GDF_16BIT_565RGB; + break; + default: + gpanel.gdfBytesPP = 1; + gpanel.gdfIndex = GDF__8BIT_INDEX; + break; + } + + da8xx_fb_reg_base = (struct da8xx_lcd_regs *)DAVINCI_LCD_CNTL_BASE; + + debug("Resolution: %dx%d %x\n", + gpanel.winSizeX, + gpanel.winSizeY, + lcd_cfg.bpp); + + size = sizeof(struct fb_info) + sizeof(struct da8xx_fb_par); + da8xx_fb_info = malloc(size); + debug("da8xx_fb_info at %x\n", (unsigned int)da8xx_fb_info); + + if (!da8xx_fb_info) { + printf("Memory allocation failed for fb_info\n"); + return NULL; + } + memset(da8xx_fb_info, 0, size); + p = (char *)da8xx_fb_info; + da8xx_fb_info->par = p + sizeof(struct fb_info); + debug("da8xx_par at %x\n", (unsigned int)da8xx_fb_info->par); + + par = da8xx_fb_info->par; + par->pxl_clk = lcd_panel->pxl_clk; + + if (lcd_init(par, &lcd_cfg, lcd_panel) < 0) { + printf("lcd_init failed\n"); + goto err_release_fb; + } + + /* allocate frame buffer */ + par->vram_size = lcd_panel->width * lcd_panel->height * lcd_cfg.bpp; + par->vram_size = par->vram_size * LCD_NUM_BUFFERS / 8; + + par->vram_virt = malloc(par->vram_size); + + par->vram_phys = (dma_addr_t) par->vram_virt; + debug("Requesting 0x%x bytes for framebuffer at 0x%x\n", + (unsigned int)par->vram_size, + (unsigned int)par->vram_virt); + if (!par->vram_virt) { + printf("GLCD: malloc for frame buffer failed\n"); + goto err_release_fb; + } + + gpanel.frameAdrs = (unsigned int)par->vram_virt; + da8xx_fb_info->screen_base = (char *) par->vram_virt; + da8xx_fb_fix.smem_start = gpanel.frameAdrs; + da8xx_fb_fix.smem_len = par->vram_size; + da8xx_fb_fix.line_length = (lcd_panel->width * lcd_cfg.bpp) / 8; + + par->dma_start = par->vram_phys; + par->dma_end = par->dma_start + lcd_panel->height * + da8xx_fb_fix.line_length - 1; + + /* allocate palette buffer */ + par->v_palette_base = malloc(PALETTE_SIZE); + if (!par->v_palette_base) { + printf("GLCD: malloc for palette buffer failed\n"); + goto err_release_fb_mem; + } + memset(par->v_palette_base, 0, PALETTE_SIZE); + par->p_palette_base = (unsigned int)par->v_palette_base; + + /* Initialize par */ + da8xx_fb_info->var.bits_per_pixel = lcd_cfg.bpp; + + da8xx_fb_var.xres = lcd_panel->width; + da8xx_fb_var.xres_virtual = lcd_panel->width; + + da8xx_fb_var.yres = lcd_panel->height; + da8xx_fb_var.yres_virtual = lcd_panel->height * LCD_NUM_BUFFERS; + + da8xx_fb_var.grayscale = + lcd_cfg.p_disp_panel->panel_shade == MONOCHROME ? 1 : 0; + da8xx_fb_var.bits_per_pixel = lcd_cfg.bpp; + + da8xx_fb_var.hsync_len = lcd_panel->hsw; + da8xx_fb_var.vsync_len = lcd_panel->vsw; + + /* Initialize fbinfo */ + da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT; + da8xx_fb_info->fix = da8xx_fb_fix; + da8xx_fb_info->var = da8xx_fb_var; + da8xx_fb_info->pseudo_palette = par->pseudo_palette; + da8xx_fb_info->fix.visual = (da8xx_fb_info->var.bits_per_pixel <= 8) ? + FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; + + /* Clear interrupt */ + memset((void *)par->vram_virt, 0, par->vram_size); + lcd_disable_raster(); + lcdc_write(0xFFFF, &da8xx_fb_reg_base->stat); + debug("Palette at 0x%x size %d\n", par->p_palette_base, + par->palette_sz); + lcdc_dma_start(); + + /* Load a default palette */ + fb_setcolreg(0, 0, 0, 0, 0xffff, da8xx_fb_info); + + /* Check that the palette is loaded */ + wait_for_event(LCD_PL_LOAD_DONE); + + /* Wait until DMA is working */ + wait_for_event(LCD_END_OF_FRAME0); + + return (void *)&gpanel; + +err_release_fb_mem: + free(par->vram_virt); + +err_release_fb: + free(da8xx_fb_info); + + return NULL; +} + +void video_set_lut(unsigned int index, /* color number */ + unsigned char r, /* red */ + unsigned char g, /* green */ + unsigned char b /* blue */ + ) +{ + + return; +} + +void da8xx_video_init(const struct da8xx_panel *panel, int bits_pixel) +{ + lcd_panel = panel; + bits_x_pixel = bits_pixel; +} diff --git a/drivers/video/fsl_diu_fb.c b/drivers/video/fsl_diu_fb.c index cb43904..350241e 100644 --- a/drivers/video/fsl_diu_fb.c +++ b/drivers/video/fsl_diu_fb.c @@ -252,8 +252,10 @@ int fsl_diu_init(u16 xres, u16 yres, u32 pixel_format, int gamma_fix) break; case RESOLUTION(800, 600): fsl_diu_mode_db = &fsl_diu_mode_800_600; + break; case RESOLUTION(1024, 768): fsl_diu_mode_db = &fsl_diu_mode_1024_768; + break; case RESOLUTION(1280, 1024): fsl_diu_mode_db = &fsl_diu_mode_1280_1024; break; diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index f30deb3..eb75c6a 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c @@ -824,7 +824,7 @@ void *video_hw_init(void) char *penv; u32 memsize; unsigned long t1, hsynch, vsynch; - int bits_per_pixel, i, tmp, vesa_idx = 0, videomode; + int bits_per_pixel, i, tmp, videomode; tmp = 0; @@ -857,7 +857,6 @@ void *video_hw_init(void) mode = (struct ctfb_res_modes *) &res_mode_init[vesa_modes[i].resindex]; bits_per_pixel = vesa_modes[i].bits_per_pixel; - vesa_idx = vesa_modes[i].resindex; } else { mode = (struct ctfb_res_modes *) &var_mode; bits_per_pixel = video_get_params(mode, penv); diff --git a/drivers/video/sed156x.c b/drivers/video/sed156x.c index 707250d..a610b74 100644 --- a/drivers/video/sed156x.c +++ b/drivers/video/sed156x.c @@ -41,6 +41,7 @@ /* include the font data */ #include <video_font.h> +#include <video_font_data.h> #if VIDEO_FONT_WIDTH != 8 || VIDEO_FONT_HEIGHT != 16 #error Expecting VIDEO_FONT_WIDTH == 8 && VIDEO_FONT_HEIGHT == 16 diff --git a/drivers/video/videomodes.c b/drivers/video/videomodes.c index 6fe5811..ef9bc4c 100644 --- a/drivers/video/videomodes.c +++ b/drivers/video/videomodes.c @@ -159,15 +159,18 @@ video_search_param (char *start, char *param) int video_get_params (struct ctfb_res_modes *pPar, char *penv) { char *p, *s, *val_s; - int i = 0, t; + int i = 0; int bpp; int mode; + /* first search for the environment containing the real param string */ s = penv; - if ((p = getenv (s)) != NULL) { + + if ((p = getenv (s)) != NULL) s = p; - } - /* in case of the bootargs line, we have to start + + /* + * in case of the bootargs line, we have to start * after "video=ctfb:" */ i = video_search_param (s, "video=ctfb:"); @@ -177,19 +180,22 @@ int video_get_params (struct ctfb_res_modes *pPar, char *penv) } /* search for mode as a default value */ p = s; - t = 0; mode = 0; /* default */ + while ((i = video_get_param_len (p, ',')) != 0) { GET_OPTION ("mode:", mode) p += i; if (*p != 0) p++; /* skip ',' */ } + if (mode >= RES_MODES_COUNT) mode = 0; + *pPar = res_mode_init[mode]; /* copy default values */ bpp = 24 - ((mode % 3) * 8); p = s; /* restart */ + while ((i = video_get_param_len (p, ',')) != 0) { GET_OPTION ("x:", pPar->xres) GET_OPTION ("y:", pPar->yres) |