summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mmc/omap3_mmc.c137
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/nomadik.c221
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/arm_dcc.c226
5 files changed, 519 insertions, 67 deletions
diff --git a/drivers/mmc/omap3_mmc.c b/drivers/mmc/omap3_mmc.c
index 399c57b..e90db7e 100644
--- a/drivers/mmc/omap3_mmc.c
+++ b/drivers/mmc/omap3_mmc.c
@@ -28,6 +28,7 @@
#include <mmc.h>
#include <part.h>
#include <i2c.h>
+#include <asm/io.h>
#include <asm/arch/mmc.h>
const unsigned short mmc_transspeed_val[15][4] = {
@@ -50,6 +51,7 @@ const unsigned short mmc_transspeed_val[15][4] = {
mmc_card_data cur_card_data;
static block_dev_desc_t mmc_blk_dev;
+static hsmmc_t *mmc_base = (hsmmc_t *)OMAP_HSMMC_BASE;
block_dev_desc_t *mmc_get_dev(int dev)
{
@@ -60,55 +62,49 @@ void twl4030_mmc_config(void)
{
unsigned char data;
- data = 0x20;
- i2c_write(0x4B, 0x82, 1, &data, 1);
- data = 0x2;
- i2c_write(0x4B, 0x85, 1, &data, 1);
+ data = DEV_GRP_P1;
+ i2c_write(PWRMGT_ADDR_ID4, VMMC1_DEV_GRP, 1, &data, 1);
+ data = VMMC1_VSEL_30;
+ i2c_write(PWRMGT_ADDR_ID4, VMMC1_DEDICATED, 1, &data, 1);
}
unsigned char mmc_board_init(void)
{
- unsigned int value = 0;
+ t2_t *t2_base = (t2_t *)T2_BASE;
twl4030_mmc_config();
- value = CONTROL_PBIAS_LITE;
- CONTROL_PBIAS_LITE = value | (1 << 2) | (1 << 1) | (1 << 9);
+ writel(readl(&t2_base->pbias_lite) | PBIASLITEPWRDNZ1 |
+ PBIASSPEEDCTRL0 | PBIASLITEPWRDNZ0,
+ &t2_base->pbias_lite);
- value = CONTROL_DEV_CONF0;
- CONTROL_DEV_CONF0 = value | (1 << 24);
+ writel(readl(&t2_base->devconf0) | MMCSDIO1ADPCLKISEL,
+ &t2_base->devconf0);
return 1;
}
void mmc_init_stream(void)
{
- volatile unsigned int mmc_stat;
+ writel(readl(&mmc_base->con) | INIT_INITSTREAM, &mmc_base->con);
- OMAP_HSMMC_CON |= INIT_INITSTREAM;
+ writel(MMC_CMD0, &mmc_base->cmd);
+ while (!(readl(&mmc_base->stat) & CC_MASK));
- OMAP_HSMMC_CMD = MMC_CMD0;
- do {
- mmc_stat = OMAP_HSMMC_STAT;
- } while (!(mmc_stat & CC_MASK));
-
- OMAP_HSMMC_STAT = CC_MASK;
+ writel(CC_MASK, &mmc_base->stat);
- OMAP_HSMMC_CMD = MMC_CMD0;
- do {
- mmc_stat = OMAP_HSMMC_STAT;
- } while (!(mmc_stat & CC_MASK));
+ writel(MMC_CMD0, &mmc_base->cmd);
+ while (!(readl(&mmc_base->stat) & CC_MASK));
- OMAP_HSMMC_STAT = OMAP_HSMMC_STAT;
- OMAP_HSMMC_CON &= ~INIT_INITSTREAM;
+ writel(readl(&mmc_base->con) & ~INIT_INITSTREAM, &mmc_base->con);
}
unsigned char mmc_clock_config(unsigned int iclk, unsigned short clk_div)
{
unsigned int val;
- mmc_reg_out(OMAP_HSMMC_SYSCTL, (ICE_MASK | DTO_MASK | CEN_MASK),
- (ICE_STOP | DTO_15THDTO | CEN_DISABLE));
+ mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK),
+ (ICE_STOP | DTO_15THDTO | CEN_DISABLE));
switch (iclk) {
case CLK_INITSEQ:
@@ -123,12 +119,12 @@ unsigned char mmc_clock_config(unsigned int iclk, unsigned short clk_div)
default:
return 0;
}
- mmc_reg_out(OMAP_HSMMC_SYSCTL,
- ICE_MASK | CLKD_MASK, (val << CLKD_OFFSET) | ICE_OSCILLATE);
+ mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK,
+ (val << CLKD_OFFSET) | ICE_OSCILLATE);
- while ((OMAP_HSMMC_SYSCTL & ICS_MASK) == ICS_NOTREADY) ;
+ while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY);
- OMAP_HSMMC_SYSCTL |= CEN_ENABLE;
+ writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);
return 1;
}
@@ -138,59 +134,63 @@ unsigned char mmc_init_setup(void)
mmc_board_init();
- OMAP_HSMMC_SYSCONFIG |= MMC_SOFTRESET;
- while ((OMAP_HSMMC_SYSSTATUS & RESETDONE) == 0) ;
+ writel(readl(&mmc_base->sysconfig) | MMC_SOFTRESET,
+ &mmc_base->sysconfig);
+ while ((readl(&mmc_base->sysstatus) & RESETDONE) == 0);
- OMAP_HSMMC_SYSCTL |= SOFTRESETALL;
- while ((OMAP_HSMMC_SYSCTL & SOFTRESETALL) != 0x0) ;
+ writel(readl(&mmc_base->sysctl) | SOFTRESETALL, &mmc_base->sysctl);
+ while ((readl(&mmc_base->sysctl) & SOFTRESETALL) != 0x0);
- OMAP_HSMMC_HCTL = DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0;
- OMAP_HSMMC_CAPA |= VS30_3V0SUP | VS18_1V8SUP;
+ writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl);
+ writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP,
+ &mmc_base->capa);
- reg_val = OMAP_HSMMC_CON & RESERVED_MASK;
+ reg_val = readl(&mmc_base->con) & RESERVED_MASK;
- OMAP_HSMMC_CON = CTPL_MMC_SD | reg_val | WPP_ACTIVEHIGH |
- CDP_ACTIVEHIGH | MIT_CTO | DW8_1_4BITMODE | MODE_FUNC |
- STR_BLOCK | HR_NOHOSTRESP | INIT_NOINIT | NOOPENDRAIN;
+ writel(CTPL_MMC_SD | reg_val | WPP_ACTIVEHIGH | CDP_ACTIVEHIGH |
+ MIT_CTO | DW8_1_4BITMODE | MODE_FUNC | STR_BLOCK |
+ HR_NOHOSTRESP | INIT_NOINIT | NOOPENDRAIN, &mmc_base->con);
mmc_clock_config(CLK_INITSEQ, 0);
- OMAP_HSMMC_HCTL |= SDBP_PWRON;
+ writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl);
- OMAP_HSMMC_IE = 0x307f0033;
+ writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE |
+ IE_CEB | IE_CCRC | IE_CTO | IE_BRR | IE_BWR | IE_TC | IE_CC,
+ &mmc_base->ie);
mmc_init_stream();
return 1;
}
unsigned char mmc_send_cmd(unsigned int cmd, unsigned int arg,
- unsigned int *response)
+ unsigned int *response)
{
- volatile unsigned int mmc_stat;
+ unsigned int mmc_stat;
- while ((OMAP_HSMMC_PSTATE & DATI_MASK) == DATI_CMDDIS) ;
+ while ((readl(&mmc_base->pstate) & DATI_MASK) == DATI_CMDDIS);
- OMAP_HSMMC_BLK = BLEN_512BYTESLEN | NBLK_STPCNT;
- OMAP_HSMMC_STAT = 0xFFFFFFFF;
- OMAP_HSMMC_ARG = arg;
- OMAP_HSMMC_CMD = cmd | CMD_TYPE_NORMAL | CICE_NOCHECK |
- CCCE_NOCHECK | MSBS_SGLEBLK | ACEN_DISABLE | BCE_DISABLE |
- DE_DISABLE;
+ writel(BLEN_512BYTESLEN | NBLK_STPCNT, &mmc_base->blk);
+ writel(0xFFFFFFFF, &mmc_base->stat);
+ writel(arg, &mmc_base->arg);
+ writel(cmd | CMD_TYPE_NORMAL | CICE_NOCHECK | CCCE_NOCHECK |
+ MSBS_SGLEBLK | ACEN_DISABLE | BCE_DISABLE | DE_DISABLE,
+ &mmc_base->cmd);
while (1) {
do {
- mmc_stat = OMAP_HSMMC_STAT;
+ mmc_stat = readl(&mmc_base->stat);
} while (mmc_stat == 0);
if ((mmc_stat & ERRI_MASK) != 0)
return (unsigned char) mmc_stat;
if (mmc_stat & CC_MASK) {
- OMAP_HSMMC_STAT = CC_MASK;
- response[0] = OMAP_HSMMC_RSP10;
+ writel(CC_MASK, &mmc_base->stat);
+ response[0] = readl(&mmc_base->rsp10);
if ((cmd & RSP_TYPE_MASK) == RSP_TYPE_LGHT136) {
- response[1] = OMAP_HSMMC_RSP32;
- response[2] = OMAP_HSMMC_RSP54;
- response[3] = OMAP_HSMMC_RSP76;
+ response[1] = readl(&mmc_base->rsp32);
+ response[2] = readl(&mmc_base->rsp54);
+ response[3] = readl(&mmc_base->rsp76);
}
break;
}
@@ -200,7 +200,7 @@ unsigned char mmc_send_cmd(unsigned int cmd, unsigned int arg,
unsigned char mmc_read_data(unsigned int *output_buf)
{
- volatile unsigned int mmc_stat;
+ unsigned int mmc_stat;
unsigned int read_count = 0;
/*
@@ -208,7 +208,7 @@ unsigned char mmc_read_data(unsigned int *output_buf)
*/
while (1) {
do {
- mmc_stat = OMAP_HSMMC_STAT;
+ mmc_stat = readl(&mmc_base->stat);
} while (mmc_stat == 0);
if ((mmc_stat & ERRI_MASK) != 0)
@@ -217,19 +217,22 @@ unsigned char mmc_read_data(unsigned int *output_buf)
if (mmc_stat & BRR_MASK) {
unsigned int k;
- OMAP_HSMMC_STAT |= BRR_MASK;
+ writel(readl(&mmc_base->stat) | BRR_MASK,
+ &mmc_base->stat);
for (k = 0; k < MMCSD_SECTOR_SIZE / 4; k++) {
- *output_buf = OMAP_HSMMC_DATA;
+ *output_buf = readl(&mmc_base->data);
output_buf++;
read_count += 4;
}
}
if (mmc_stat & BWR_MASK)
- OMAP_HSMMC_STAT |= BWR_MASK;
+ writel(readl(&mmc_base->stat) | BWR_MASK,
+ &mmc_base->stat);
if (mmc_stat & TC_MASK) {
- OMAP_HSMMC_STAT |= TC_MASK;
+ writel(readl(&mmc_base->stat) | TC_MASK,
+ &mmc_base->stat);
break;
}
}
@@ -273,8 +276,8 @@ unsigned char mmc_detect_card(mmc_card_data *mmc_card_cur)
mmc_card_cur->card_type = MMC_CARD;
ocr_value |= MMC_OCR_REG_ACCESS_MODE_SECTOR;
ret_cmd41 = MMC_CMD1;
- OMAP_HSMMC_CON &= ~OD;
- OMAP_HSMMC_CON |= OPENDRAIN;
+ writel(readl(&mmc_base->con) & ~OD, &mmc_base->con);
+ writel(readl(&mmc_base->con) | OPENDRAIN, &mmc_base->con);
}
argument = ocr_value;
@@ -342,8 +345,8 @@ unsigned char mmc_detect_card(mmc_card_data *mmc_card_cur)
mmc_card_cur->RCA = ((mmc_resp_r6 *) resp)->newpublishedrca;
}
- OMAP_HSMMC_CON &= ~OD;
- OMAP_HSMMC_CON |= NOOPENDRAIN;
+ writel(readl(&mmc_base->con) & ~OD, &mmc_base->con);
+ writel(readl(&mmc_base->con) | NOOPENDRAIN, &mmc_base->con);
return 1;
}
@@ -518,7 +521,7 @@ unsigned long mmc_bread(int dev_num, unsigned long blknr, lbaint_t blkcnt,
void *dst)
{
omap_mmc_read_sect(blknr, (blkcnt * MMCSD_SECTOR_SIZE), &cur_card_data,
- (unsigned long *) dst);
+ (unsigned long *) dst);
return 1;
}
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 24edb27..5974d77 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -38,6 +38,7 @@ endif
COBJS-$(CONFIG_DRIVER_NAND_BFIN) += bfin_nand.o
COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o
COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o
+COBJS-$(CONFIG_NAND_NOMADIK) += nomadik.o
COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o
COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
endif
diff --git a/drivers/mtd/nand/nomadik.c b/drivers/mtd/nand/nomadik.c
new file mode 100644
index 0000000..b76f4cb
--- /dev/null
+++ b/drivers/mtd/nand/nomadik.c
@@ -0,0 +1,221 @@
+/*
+ * (C) Copyright 2007 STMicroelectronics, <www.st.com>
+ * (C) Copyright 2009 Alessandro Rubini <rubini@unipv.it>
+ *
+ * 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 <nand.h>
+#include <asm/io.h>
+
+static inline int parity(int b) /* b is really a byte; returns 0 or ~0 */
+{
+ __asm__ __volatile__(
+ "eor %0, %0, %0, lsr #4\n\t"
+ "eor %0, %0, %0, lsr #2\n\t"
+ "eor %0, %0, %0, lsr #1\n\t"
+ "ands %0, %0, #1\n\t"
+ "subne %0, %0, #2\t"
+ : "=r" (b) : "0" (b));
+ return b;
+}
+
+/*
+ * This is the ECC routine used in hardware, according to the manual.
+ * HW claims to make the calculation but not the correction; so we must
+ * recalculate the bytes for a comparison.
+ */
+static int ecc512(const unsigned char *data, unsigned char *ecc)
+{
+ int gpar = 0;
+ int i, val, par;
+ int pbits = 0; /* P8, P16, ... P2048 */
+ int pprime = 0; /* P8', P16', ... P2048' */
+ int lowbits; /* P1, P2, P4 and primes */
+
+ for (i = 0; i < 512; i++) {
+ par = parity((val = data[i]));
+ gpar ^= val;
+ pbits ^= (i & par);
+ }
+ /*
+ * Ok, now gpar is global parity (xor of all bytes)
+ * pbits are all the parity bits (non-prime ones)
+ */
+ par = parity(gpar);
+ pprime = pbits ^ par;
+ /* Put low bits in the right position for ecc[2] (bits 7..2) */
+ lowbits = 0
+ | (parity(gpar & 0xf0) & 0x80) /* P4 */
+ | (parity(gpar & 0x0f) & 0x40) /* P4' */
+ | (parity(gpar & 0xcc) & 0x20) /* P2 */
+ | (parity(gpar & 0x33) & 0x10) /* P2' */
+ | (parity(gpar & 0xaa) & 0x08) /* P1 */
+ | (parity(gpar & 0x55) & 0x04); /* P1' */
+
+ ecc[2] = ~(lowbits | ((pbits & 0x100) >> 7) | ((pprime & 0x100) >> 8));
+ /* now intermix bits for ecc[1] (P1024..P128') and ecc[0] (P64..P8') */
+ ecc[1] = ~( (pbits & 0x80) >> 0 | ((pprime & 0x80) >> 1)
+ | ((pbits & 0x40) >> 1) | ((pprime & 0x40) >> 2)
+ | ((pbits & 0x20) >> 2) | ((pprime & 0x20) >> 3)
+ | ((pbits & 0x10) >> 3) | ((pprime & 0x10) >> 4));
+
+ ecc[0] = ~( (pbits & 0x8) << 4 | ((pprime & 0x8) << 3)
+ | ((pbits & 0x4) << 3) | ((pprime & 0x4) << 2)
+ | ((pbits & 0x2) << 2) | ((pprime & 0x2) << 1)
+ | ((pbits & 0x1) << 1) | ((pprime & 0x1) << 0));
+ return 0;
+}
+
+/* This is the method in the chip->ecc field */
+static int nomadik_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,
+ uint8_t *ecc_code)
+{
+ return ecc512(dat, ecc_code);
+}
+
+static int nomadik_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
+ uint8_t *r_ecc, uint8_t *c_ecc)
+{
+ struct nand_chip *chip = mtd->priv;
+ uint32_t r, c, d, diff; /*read, calculated, xor of them */
+
+ if (!memcmp(r_ecc, c_ecc, chip->ecc.bytes))
+ return 0;
+
+ /* Reorder the bytes into ascending-order 24 bits -- see manual */
+ r = r_ecc[2] << 22 | r_ecc[1] << 14 | r_ecc[0] << 6 | r_ecc[2] >> 2;
+ c = c_ecc[2] << 22 | c_ecc[1] << 14 | c_ecc[0] << 6 | c_ecc[2] >> 2;
+ diff = (r ^ c) & ((1<<24)-1); /* use 24 bits only */
+
+ /* If 12 bits are different, one per pair, it's correctable */
+ if (((diff | (diff>>1)) & 0x555555) == 0x555555) {
+ int bit = ((diff & 2) >> 1)
+ | ((diff & 0x8) >> 2) | ((diff & 0x20) >> 3);
+ int byte;
+
+ d = diff >> 6; /* remove bit-order info */
+ byte = ((d & 2) >> 1)
+ | ((d & 0x8) >> 2) | ((d & 0x20) >> 3)
+ | ((d & 0x80) >> 4) | ((d & 0x200) >> 5)
+ | ((d & 0x800) >> 6) | ((d & 0x2000) >> 7)
+ | ((d & 0x8000) >> 8) | ((d & 0x20000) >> 9);
+ /* correct the single bit */
+ dat[byte] ^= 1<<bit;
+ return 0;
+ }
+ /* If 1 bit only differs, it's one bit error in ECC, ignore */
+ if ((diff ^ (1 << (ffs(diff) - 1))) == 0)
+ return 0;
+ /* Otherwise, uncorrectable */
+ return -1;
+}
+
+static void nomadik_ecc_hwctl(struct mtd_info *mtd, int mode)
+{ /* mandatory in the structure but not used here */ }
+
+
+/* This is the layout used by older installations, we keep compatible */
+struct nand_ecclayout nomadik_ecc_layout = {
+ .eccbytes = 3 * 4,
+ .eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */
+ 0x02, 0x03, 0x04,
+ 0x12, 0x13, 0x14,
+ 0x22, 0x23, 0x24,
+ 0x32, 0x33, 0x34},
+ .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} },
+};
+
+#define MASK_ALE (1 << 24) /* our ALE is AD21 */
+#define MASK_CLE (1 << 23) /* our CLE is AD22 */
+
+/* This is copied from the AT91SAM9 devices (Stelian Pop, Lead Tech Design) */
+static void nomadik_nand_hwcontrol(struct mtd_info *mtd,
+ int cmd, unsigned int ctrl)
+{
+ struct nand_chip *this = mtd->priv;
+ u32 pcr0 = readl(REG_FSMC_PCR0);
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ ulong IO_ADDR_W = (ulong) this->IO_ADDR_W;
+ IO_ADDR_W &= ~(MASK_ALE | MASK_CLE);
+
+ if (ctrl & NAND_CLE)
+ IO_ADDR_W |= MASK_CLE;
+ if (ctrl & NAND_ALE)
+ IO_ADDR_W |= MASK_ALE;
+
+ if (ctrl & NAND_NCE)
+ writel(pcr0 | 0x4, REG_FSMC_PCR0);
+ else
+ writel(pcr0 & ~0x4, REG_FSMC_PCR0);
+
+ this->IO_ADDR_W = (void *) IO_ADDR_W;
+ this->IO_ADDR_R = (void *) IO_ADDR_W;
+ }
+
+ if (cmd != NAND_CMD_NONE)
+ writeb(cmd, this->IO_ADDR_W);
+}
+
+/* Returns 1 when ready; upper layers timeout at 20ms with timer routines */
+static int nomadik_nand_ready(struct mtd_info *mtd)
+{
+ return 1; /* The ready bit is handled in hardware */
+}
+
+/* Copy a buffer 32bits at a time: faster than defualt method which is 8bit */
+static void nomadik_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *chip = mtd->priv;
+ u32 *p = (u32 *) buf;
+
+ len >>= 2;
+ writel(0, REG_FSMC_ECCR0);
+ for (i = 0; i < len; i++)
+ p[i] = readl(chip->IO_ADDR_R);
+}
+
+int board_nand_init(struct nand_chip *chip)
+{
+ /* Set up the FSMC_PCR0 for nand access*/
+ writel(0x0000004a, REG_FSMC_PCR0);
+ /* Set up FSMC_PMEM0, FSMC_PATT0 with timing data for access */
+ writel(0x00020401, REG_FSMC_PMEM0);
+ writel(0x00020404, REG_FSMC_PATT0);
+
+ chip->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING;
+ chip->cmd_ctrl = nomadik_nand_hwcontrol;
+ chip->dev_ready = nomadik_nand_ready;
+ /* The chip allows 32bit reads, so avoid the default 8bit copy */
+ chip->read_buf = nomadik_nand_read_buf;
+
+ /* ECC: follow the hardware-defined rulse, but do it in sw */
+ chip->ecc.mode = NAND_ECC_HW;
+ chip->ecc.bytes = 3;
+ chip->ecc.size = 512;
+ chip->ecc.layout = &nomadik_ecc_layout;
+ chip->ecc.calculate = nomadik_ecc_calculate;
+ chip->ecc.hwctl = nomadik_ecc_hwctl;
+ chip->ecc.correct = nomadik_ecc_correct;
+
+ return 0;
+}
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 0f980ab..d0efd73 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -25,6 +25,7 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libserial.a
+COBJS-$(CONFIG_ARM_DCC) += arm_dcc.o
COBJS-$(CONFIG_ATMEL_USART) += atmel_usart.o
COBJS-$(CONFIG_MCFUART) += mcfuart.o
COBJS-$(CONFIG_NS9750_UART) += ns9750_serial.o
diff --git a/drivers/serial/arm_dcc.c b/drivers/serial/arm_dcc.c
new file mode 100644
index 0000000..5a7fb6b
--- /dev/null
+++ b/drivers/serial/arm_dcc.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2004-2007 ARM Limited.
+ * Copyright (C) 2008 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * As a special exception, if other files instantiate templates or use macros
+ * or inline functions from this file, or you compile this file and link it
+ * with other works to produce a work based on this file, this file does not
+ * by itself cause the resulting work to be covered by the GNU General Public
+ * License. However the source code for this file must still be made available
+ * in accordance with section (3) of the GNU General Public License.
+
+ * This exception does not invalidate any other reasons why a work based on
+ * this file might be covered by the GNU General Public License.
+ */
+
+#include <common.h>
+#include <devices.h>
+
+#define DCC_ARM9_RBIT (1 << 0)
+#define DCC_ARM9_WBIT (1 << 1)
+#define DCC_ARM11_RBIT (1 << 30)
+#define DCC_ARM11_WBIT (1 << 29)
+
+#define read_core_id(x) do { \
+ __asm__ ("mrc p15, 0, %0, c0, c0, 0\n" : "=r" (x)); \
+ x = (x >> 4) & 0xFFF; \
+ } while (0);
+
+/*
+ * ARM9
+ */
+#define write_arm9_dcc(x) \
+ __asm__ volatile ("mcr p14, 0, %0, c1, c0, 0\n" : : "r" (x))
+
+#define read_arm9_dcc(x) \
+ __asm__ volatile ("mrc p14, 0, %0, c1, c0, 0\n" : "=r" (x))
+
+#define status_arm9_dcc(x) \
+ __asm__ volatile ("mrc p14, 0, %0, c0, c0, 0\n" : "=r" (x))
+
+#define can_read_arm9_dcc(x) do { \
+ status_arm9_dcc(x); \
+ x &= DCC_ARM9_RBIT; \
+ } while (0);
+
+#define can_write_arm9_dcc(x) do { \
+ status_arm9_dcc(x); \
+ x &= DCC_ARM9_WBIT; \
+ x = (x == 0); \
+ } while (0);
+
+/*
+ * ARM11
+ */
+#define write_arm11_dcc(x) \
+ __asm__ volatile ("mcr p14, 0, %0, c0, c5, 0\n" : : "r" (x))
+
+#define read_arm11_dcc(x) \
+ __asm__ volatile ("mrc p14, 0, %0, c0, c5, 0\n" : "=r" (x))
+
+#define status_arm11_dcc(x) \
+ __asm__ volatile ("mrc p14, 0, %0, c0, c1, 0\n" : "=r" (x))
+
+#define can_read_arm11_dcc(x) do { \
+ status_arm11_dcc(x); \
+ x &= DCC_ARM11_RBIT; \
+ } while (0);
+
+#define can_write_arm11_dcc(x) do { \
+ status_arm11_dcc(x); \
+ x &= DCC_ARM11_WBIT; \
+ x = (x == 0); \
+ } while (0);
+
+#define TIMEOUT_COUNT 0x4000000
+
+static enum {
+ arm9_and_earlier,
+ arm11_and_later
+} arm_type = arm9_and_earlier;
+
+#ifndef CONFIG_ARM_DCC_MULTI
+#define arm_dcc_init serial_init
+void serial_setbrg(void) {}
+#define arm_dcc_getc serial_getc
+#define arm_dcc_putc serial_putc
+#define arm_dcc_puts serial_puts
+#define arm_dcc_tstc serial_tstc
+#endif
+
+int arm_dcc_init(void)
+{
+ register unsigned int id;
+
+ read_core_id(id);
+
+ if (id >= 0xb00)
+ arm_type = arm11_and_later;
+ else
+ arm_type = arm9_and_earlier;
+
+ return 0;
+}
+
+int arm_dcc_getc(void)
+{
+ int ch;
+ register unsigned int reg;
+
+ switch (arm_type) {
+ case arm11_and_later:
+ do {
+ can_read_arm11_dcc(reg);
+ } while (!reg);
+ read_arm11_dcc(ch);
+ break;
+
+ case arm9_and_earlier:
+ default:
+ do {
+ can_read_arm9_dcc(reg);
+ } while (!reg);
+ read_arm9_dcc(ch);
+ break;
+ }
+
+ return ch;
+}
+
+void arm_dcc_putc(char ch)
+{
+ register unsigned int reg;
+ unsigned int timeout_count = TIMEOUT_COUNT;
+
+ switch (arm_type) {
+ case arm11_and_later:
+ while (--timeout_count) {
+ can_write_arm11_dcc(reg);
+ if (reg)
+ break;
+ }
+ if (timeout_count == 0)
+ return;
+ else
+ write_arm11_dcc(ch);
+ break;
+
+ case arm9_and_earlier:
+ default:
+ while (--timeout_count) {
+ can_write_arm9_dcc(reg);
+ if (reg)
+ break;
+ }
+ if (timeout_count == 0)
+ return;
+ else
+ write_arm9_dcc(ch);
+ break;
+ }
+}
+
+void arm_dcc_puts(const char *s)
+{
+ while (*s)
+ arm_dcc_putc(*s++);
+}
+
+int arm_dcc_tstc(void)
+{
+ register unsigned int reg;
+
+ switch (arm_type) {
+ case arm11_and_later:
+ can_read_arm11_dcc(reg);
+ break;
+ case arm9_and_earlier:
+ default:
+ can_read_arm9_dcc(reg);
+ break;
+ }
+
+ return reg;
+}
+
+#ifdef CONFIG_ARM_DCC_MULTI
+static device_t arm_dcc_dev;
+
+int drv_arm_dcc_init(void)
+{
+ int rc;
+
+ /* Device initialization */
+ memset(&arm_dcc_dev, 0, sizeof(arm_dcc_dev));
+
+ strcpy(arm_dcc_dev.name, "dcc");
+ arm_dcc_dev.ext = 0; /* No extensions */
+ arm_dcc_dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_OUTPUT;
+ arm_dcc_dev.tstc = arm_dcc_tstc; /* 'tstc' function */
+ arm_dcc_dev.getc = arm_dcc_getc; /* 'getc' function */
+ arm_dcc_dev.putc = arm_dcc_putc; /* 'putc' function */
+ arm_dcc_dev.puts = arm_dcc_puts; /* 'puts' function */
+
+ rc = device_register(&arm_dcc_dev);
+
+ if (rc == 0) {
+ arm_dcc_init();
+ return 1;
+ }
+
+ return 0;
+}
+#endif