summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2010-02-01 16:16:12 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2010-02-01 16:16:12 +0100
commit7ca411ecd2dc1c6bc47caad4b1e25a118bd7441b (patch)
tree716f34c98b8e9a1535a9dc42605644d0793f5161 /drivers
parentdc6550ed3be2c47476ccaefdee2277b9360a8ed6 (diff)
parent976b4be6021569e9808cf83ab7600231d1315307 (diff)
downloadbarebox-7ca411ecd2dc1c6bc47caad4b1e25a118bd7441b.tar.gz
barebox-7ca411ecd2dc1c6bc47caad4b1e25a118bd7441b.tar.xz
Merge branch 'next'
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig1
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/ata/Kconfig26
-rw-r--r--drivers/ata/Makefile7
-rw-r--r--drivers/ata/bios.c291
-rw-r--r--drivers/ata/disk_drive.c346
-rw-r--r--drivers/i2c/Kconfig3
-rw-r--r--drivers/i2c/Makefile1
-rw-r--r--drivers/i2c/lp3972.c110
-rw-r--r--drivers/i2c/mc13892.c91
-rw-r--r--drivers/i2c/mc9sdz60.c84
-rw-r--r--drivers/net/Kconfig5
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/ep93xx.c676
-rw-r--r--drivers/net/ep93xx.h147
-rw-r--r--drivers/serial/Kconfig7
-rw-r--r--drivers/serial/Makefile2
-rw-r--r--drivers/serial/serial_pl010.c172
-rw-r--r--drivers/serial/serial_pl010.h100
19 files changed, 2031 insertions, 40 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 8bab7ac0bb..bf559c4054 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -6,6 +6,7 @@ source "drivers/spi/Kconfig"
source "drivers/i2c/Kconfig"
source "drivers/nor/Kconfig"
source "drivers/nand/Kconfig"
+source "drivers/ata/Kconfig"
source "drivers/usb/Kconfig"
source "drivers/video/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 5dc7756aa1..7bae6ffcb6 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -3,6 +3,7 @@ obj-y += serial/
obj-y += nand/
obj-y += nor/
obj-y += usb/
+obj-$(CONFIG_ATA) += ata/
obj-$(CONFIG_SPI) += spi/
obj-$(CONFIG_I2C) += i2c/
obj-$(CONFIG_VIDEO) += video/
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
new file mode 100644
index 0000000000..b43c975d3b
--- /dev/null
+++ b/drivers/ata/Kconfig
@@ -0,0 +1,26 @@
+menuconfig ATA
+ bool "ATA "
+ help
+ Add support for ATA types of drives like harddisks and CDROMs.
+
+if ATA
+
+comment "drive types"
+
+config ATA_DISK
+ bool "disk drives"
+ help
+ Add support for regular disk drives
+
+comment "interface types"
+
+config ATA_BIOS
+ bool "BIOS based"
+ depends on X86_BIOS_BRINGUP
+ help
+ Gain disk drive access via int13 calls to the standard PC-BIOS.
+ The advantage of this driver is, it still uses user's defined boot
+ media to work on. Disadvantage is: Due to its 16 bit nature it is
+ slow.
+
+endif
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
new file mode 100644
index 0000000000..30d15ccb02
--- /dev/null
+++ b/drivers/ata/Makefile
@@ -0,0 +1,7 @@
+# drive types
+
+obj-$(CONFIG_ATA_DISK) += disk_drive.o
+
+# interface types
+
+obj-$(CONFIG_ATA_BIOS) += bios.o
diff --git a/drivers/ata/bios.c b/drivers/ata/bios.c
new file mode 100644
index 0000000000..51e2425a9d
--- /dev/null
+++ b/drivers/ata/bios.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2009 Juergen Beisert, Pengutronix
+ *
+ * Mostly stolen from the GRUB2 project
+ * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008 Free Software Foundation, 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+/**
+ * @file
+ * @brief Media communication layer through the standard 16 bit PC-BIOS
+ *
+ * This communication driver does all accesses to the boot medium via 16 bit
+ * real mode calls into the standard BIOS. Due to this method, its possible
+ * to use all the medias to boot from that are supported by the BIOS. This
+ * also includes emulated only medias.
+ *
+ * To be able to call the real mode BIOS, this driver must switch back to
+ * real mode for each access. This will slow down the access a little bit, but
+ * we are a boot loader here, not an operating system...
+ *
+ * Note: We need scratch memory for the BIOS communication, because the BIOS
+ * can only handle memory below 0xA0000. So we must copy all data between
+ * the flat mode buffers and realmode buffers.
+ *
+ * Note: This driver makes no sense on other architectures than x86.
+ *
+ * Note: This driver does only support LBA addressing. Currently no CHS!
+ */
+
+#include <stdio.h>
+#include <linux/types.h>
+#include <init.h>
+#include <driver.h>
+#include <string.h>
+#include <xfuncs.h>
+#include <asm/syslib.h>
+#include <ata.h>
+#include <errno.h>
+
+/**
+ * Sector count handled in one count
+ *
+ * @todo 127 are always possible, some BIOS manufacturer supports up to 255.
+ * Is it's worth to detect Phoenic's restriction?
+ */
+#define SECTORS_AT_ONCE 64
+
+/** Size of one sector in bytes */
+#define SECTOR_SIZE 512
+
+/** Command to read sectors from media */
+#define BIOS_READ_CMD 0
+
+/** Command to write sectors to media */
+#define BIOS_WRT_CMD 1
+
+/**
+ * "Disk Address Packet Structure" to be used when calling
+ * BIOS's int13, function 0x42/0x43
+ */
+struct DAPS
+{
+ uint8_t size; /**< always '16' */
+ uint8_t res1; /**< always '0' */
+ int8_t count; /**< number of sectors 0...127 */
+ uint8_t res2; /**< always '0' */
+ uint16_t offset; /**< buffer address: offset */
+ uint16_t segment; /**< buffer address: segment */
+ uint64_t lba; /**< LBA of the start sector */
+} __attribute__ ((packed));
+
+/**
+ * Collection of data we need to know about the connected drive
+ */
+struct media_access {
+ int drive_no; /**< drive number used by the BIOS */
+ int is_cdrom; /**< drive is a CDROM e.g. no write support */
+};
+
+/**
+ * Scratch memory for BIOS communication to handle data in chunks of 32 kiB
+ *
+ * Note: This variable is located in the .bss segment, assuming it is located
+ * below 0xA0000. If not, the BIOS is not able to read or store any data
+ * from/to it. The variable must also aligned to a 16 byte boundary to easify
+ * linear to segment:offset address conversion.
+ */
+static uint8_t scratch_buffer[SECTORS_AT_ONCE * SECTOR_SIZE] __attribute__((aligned(16)));
+
+/**
+ * Communication buffer for the 16 bit int13 BIOS call
+ *
+ * Note: This variable is located in the .bss segment, assuming it is located
+ * below 0xA0000. If not, the BIOS is not able to read or store any data
+ * from/to it. The variable must also aligned to a 16 byte boundary to easify
+ * linear to segment:offset conversion.
+ */
+static struct DAPS bios_daps __attribute__((aligned(16)));
+
+/**
+ * @param media our data we need to do the access
+ * @param cmd Command to forward to the BIOS
+ * @param sector_start LBA of the start sector
+ * @param sector_count Sector count
+ * @param buffer Buffer to read from or write to (in the low memory area)
+ * @return 0 on success, anything else on failure
+ */
+static int biosdisk_bios_call(struct media_access *media, int cmd, uint64_t sector_start, unsigned sector_count, void *buffer)
+{
+ int rc;
+
+ /* prepare the DAPS for the int13 call */
+ bios_daps.size = sizeof(struct DAPS);
+ bios_daps.res1 = 0;
+ bios_daps.count = sector_count; /* always less than 128! */
+ bios_daps.res2 = 0;
+ bios_daps.segment = (unsigned long)buffer >> 4;
+ bios_daps.offset = (unsigned long)buffer - (unsigned long)(bios_daps.segment << 4);
+ bios_daps.lba = sector_start;
+
+ if (cmd == BIOS_READ_CMD)
+ rc = bios_disk_rw_int13_extensions(0x42, media->drive_no, &bios_daps);
+ else if (cmd == BIOS_WRT_CMD)
+ rc = bios_disk_rw_int13_extensions(0x43, media->drive_no, &bios_daps);
+ else
+ return -1;
+
+ return rc;
+}
+
+/**
+ * Read a chunk of sectors from media
+ * @param dev our data we need to do the access
+ * @param sector_start Sector's LBA number to start read from
+ * @param sector_count Sectors to read
+ * @param buffer Buffer to read into
+ * @return 0 on success, anything else on failure
+ *
+ * This routine expects the buffer has the correct size to store all data!
+ */
+static int biosdisk_read(struct device_d *dev, uint64_t sector_start, unsigned sector_count, void *buffer)
+{
+ int rc;
+ struct ata_interface *intf = dev->platform_data;
+ struct media_access *media = intf->priv;
+
+ while (sector_count >= SECTORS_AT_ONCE) {
+ rc = biosdisk_bios_call(media, BIOS_READ_CMD, sector_start, SECTORS_AT_ONCE, scratch_buffer);
+ if (rc != 0)
+ return rc;
+ __builtin_memcpy(buffer, scratch_buffer, sizeof(scratch_buffer));
+ buffer += sizeof(scratch_buffer);
+ sector_start += SECTORS_AT_ONCE;
+ sector_count -= SECTORS_AT_ONCE;
+ };
+
+ /* Are sectors still remaining? */
+ if (sector_count) {
+ rc = biosdisk_bios_call(media, BIOS_READ_CMD, sector_start, sector_count, scratch_buffer);
+ __builtin_memcpy(buffer, scratch_buffer, sector_count * SECTOR_SIZE);
+ } else
+ rc = 0;
+
+ return rc;
+}
+
+/**
+ * Write a chunk of sectors to media
+ * @param dev our data we need to do the access
+ * @param sector_start Sector's LBA number to start write to
+ * @param sector_count Sectors to write
+ * @param buffer Buffer to write from
+ * @return 0 on success, anything else on failure
+ *
+ * This routine expects the buffer has the correct size to read all data!
+ */
+static int biosdisk_write(struct device_d *dev, uint64_t sector_start, unsigned sector_count, const void *buffer)
+{
+ int rc;
+ struct ata_interface *intf = dev->platform_data;
+ struct media_access *media = intf->priv;
+
+ while (sector_count >= SECTORS_AT_ONCE) {
+ __builtin_memcpy(scratch_buffer, buffer, sizeof(scratch_buffer));
+ rc = biosdisk_bios_call(media, BIOS_WRT_CMD, sector_start, SECTORS_AT_ONCE, scratch_buffer);
+ if (rc != 0)
+ return rc;
+ buffer += sizeof(scratch_buffer);
+ sector_start += SECTORS_AT_ONCE;
+ sector_count -= SECTORS_AT_ONCE;
+ };
+
+ /* Are sectors still remaining? */
+ if (sector_count) {
+ __builtin_memcpy(scratch_buffer, buffer, sector_count * SECTOR_SIZE);
+ rc = biosdisk_bios_call(media, BIOS_WRT_CMD, sector_start, sector_count, scratch_buffer);
+ } else
+ rc = 0;
+
+ return rc;
+}
+
+/**
+ * Probe for connected drives and register them
+ *
+ * Detecting if a drive is present is done by simply reading its MBR.
+ *
+ * FIXME: Relation between BIOS disk numbering scheme and our representation
+ * here in barebox (and later on in the linux kernel)
+ */
+static int biosdisk_probe(struct device_d *dev)
+{
+ int drive, rc;
+ struct media_access media, *m;
+ struct device_d *drive_dev;
+ struct ata_interface *p;
+
+ for (drive = 0x80; drive < 0x90; drive++) {
+ media.drive_no = drive;
+ media.is_cdrom = 0; /* don't know yet */
+ rc = biosdisk_bios_call(&media, BIOS_READ_CMD, 0, 1, scratch_buffer);
+ if (rc != 0)
+ continue;
+
+ printf("BIOSdrive %d seems valid. Registering...\n", media.drive_no);
+
+ drive_dev = xzalloc(sizeof(struct device_d) + sizeof(struct media_access) + sizeof(struct ata_interface));
+ if (drive_dev == NULL) {
+ dev_err(dev, "Out of memory\n");
+ return -1;
+ }
+ m = (struct media_access*)&drive_dev[1];
+ p = (struct ata_interface*)&m[1];
+
+ m->drive_no = drive;
+ m->is_cdrom = 0;
+
+ p->write = biosdisk_write;
+ p->read = biosdisk_read;
+ p->priv = m;
+
+ strcpy(drive_dev->name, "biosdisk");
+ drive_dev->id = drive - 0x80;
+ drive_dev->size = 1;
+ drive_dev->map_base = 0;
+ drive_dev->platform_data = p;
+
+ register_device(drive_dev);
+ }
+
+ return 0;
+}
+
+static struct driver_d biosdisk_driver = {
+ .name = "biosdrive",
+ .probe = biosdisk_probe,
+};
+
+static int biosdisk_init(void)
+{
+ /* sanity */
+ if (scratch_buffer > (uint8_t*)0x9FFFF) {
+ printf("BIOS driver: Scratch memory not in real mode area. Cannot continue!\n");
+ return -EIO;
+ }
+ if (&bios_daps > (struct DAPS*)0x9FFFF) {
+ printf("BIOS driver: DAPS memory not in real mode area. Cannot continue!\n");
+ return -EIO;
+ }
+
+ register_driver(&biosdisk_driver);
+ return 0;
+}
+
+device_initcall(biosdisk_init);
diff --git a/drivers/ata/disk_drive.c b/drivers/ata/disk_drive.c
new file mode 100644
index 0000000000..250dadaa2a
--- /dev/null
+++ b/drivers/ata/disk_drive.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2009 Juergen Beisert, Pengutronix
+ *
+ * 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
+ *
+ */
+
+/**
+ * @file
+ * @brief Generic disk drive support
+ *
+ * @todo Support for disks larger than 4 GiB
+ */
+
+#include <stdio.h>
+#include <init.h>
+#include <driver.h>
+#include <types.h>
+#include <ata.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <string.h>
+#include <linux/kernel.h>
+
+/**
+ * Description of one partition table entry (D*S type)
+ */
+struct partition_entry {
+ uint8_t boot_indicator;
+ uint8_t chs_begin[3];
+ uint8_t type;
+ uint8_t chs_end[3];
+ uint32_t partition_start;
+ uint32_t partition_size;
+} __attribute__ ((packed));
+
+/** one for all */
+#define SECTOR_SIZE 512
+
+/**
+ * Guess the size of the disk, based on the partition table entries
+ * @param dev device to create partitions for
+ * @param table partition table
+ * @return size in sectors
+ */
+static unsigned long disk_guess_size(struct device_d *dev, struct partition_entry *table)
+{
+ int part_order[4] = {0, 1, 2, 3};
+ unsigned long size = 0;
+ int i;
+
+ /* TODO order the partitions */
+
+ for (i = 0; i < 4; i++) {
+ if (table[part_order[i]].partition_start != 0) {
+ size += table[part_order[i]].partition_start - size; /* the gap */
+ size += table[part_order[i]].partition_size;
+ }
+ }
+#if 1
+/* limit disk sizes we can't handle due to 32 bit limits */
+ if (size > 0x7fffff) {
+ dev_warn(dev, "Warning: Size limited due to 32 bit contraints\n");
+ size = 0x7fffff;
+ }
+#endif
+ return size;
+}
+
+/**
+ * Register partitions found on the drive
+ * @param dev device to create partitions for
+ * @param table partition table
+ * @return 0 on success
+ */
+static int disk_register_partitions(struct device_d *dev, struct partition_entry *table)
+{
+ int part_order[4] = {0, 1, 2, 3};
+ int i, rc;
+ char drive_name[16], partition_name[19];
+
+ /* TODO order the partitions */
+
+ for (i = 0; i < 4; i++) {
+ sprintf(drive_name, "%s%d", dev->name, dev->id);
+ sprintf(partition_name, "%s%d.%d", dev->name, dev->id, i);
+ if (table[part_order[i]].partition_start != 0) {
+#if 1
+/* ignore partitions we can't handle due to 32 bit limits */
+ if (table[part_order[i]].partition_start > 0x7fffff)
+ continue;
+ if (table[part_order[i]].partition_size > 0x7fffff)
+ continue;
+#endif
+ dev_info(dev, "Registering partition %s to drive %s\n", partition_name, drive_name);
+ rc = devfs_add_partition(drive_name,
+ table[part_order[i]].partition_start * SECTOR_SIZE,
+ table[part_order[i]].partition_size * SECTOR_SIZE,
+ DEVFS_PARTITION_FIXED, partition_name);
+ if (rc != 0)
+ dev_err(dev, "Failed to register partition %s (%d)\n", partition_name, rc);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Write some data to a disk
+ * @param cdev the device to write to
+ * @param _buf source of data
+ * @param count byte count to write
+ * @param offset where to write to disk
+ * @param flags Ignored
+ * @return Written bytes or negative value in case of failure
+ */
+static ssize_t disk_write(struct cdev *cdev, const void *_buf, size_t count, ulong offset, ulong flags)
+{
+ struct device_d *dev = cdev->dev;
+ struct ata_interface *intf = dev->platform_data;
+ int rc;
+ unsigned sep_count = offset & (SECTOR_SIZE - 1);
+ ssize_t written = 0;
+
+ /* starting at no sector boundary? */
+ if (sep_count != 0) {
+ uint8_t tmp_buf[SECTOR_SIZE];
+ unsigned to_write = min(SECTOR_SIZE - sep_count, count);
+
+ rc = intf->read(dev, offset / SECTOR_SIZE, 1, tmp_buf);
+ if (rc != 0) {
+ dev_err(dev, "Cannot read data\n");
+ return -1;
+ }
+ memcpy(&tmp_buf[sep_count], _buf, to_write);
+ rc = intf->write(dev, offset / SECTOR_SIZE, 1, tmp_buf);
+ if (rc != 0) {
+ dev_err(dev, "Cannot write data\n");
+ return -1;
+ }
+
+ _buf += to_write;
+ offset += to_write;
+ count -= to_write;
+ written += to_write;
+ }
+
+ /* full sector part */
+ sep_count = count / SECTOR_SIZE;
+ if (sep_count) {
+ rc = intf->write(dev, offset / SECTOR_SIZE, sep_count, _buf);
+ if (rc != 0) {
+ dev_err(dev, "Cannot write data\n");
+ return -1;
+ }
+ _buf += sep_count * SECTOR_SIZE;
+ offset += sep_count * SECTOR_SIZE;
+ count -= sep_count * SECTOR_SIZE;
+ written += sep_count * SECTOR_SIZE;
+ }
+
+ /* ending at no sector boundary? */
+ if (count) {
+ uint8_t tmp_buf[SECTOR_SIZE];
+
+ rc = intf->read(dev, offset / SECTOR_SIZE, 1, tmp_buf);
+ if (rc != 0) {
+ dev_err(dev, "Cannot read data\n");
+ return -1;
+ }
+ memcpy(tmp_buf, _buf, count);
+ rc = intf->write(dev, offset / SECTOR_SIZE, 1, tmp_buf);
+ if (rc != 0) {
+ dev_err(dev, "Cannot write data\n");
+ return -1;
+ }
+ written += count;
+ }
+
+ return written;
+}
+
+/**
+ * Read some data from a disk
+ * @param cdev the device to read from
+ * @param _buf destination of the data
+ * @param count byte count to read
+ * @param offset where to read from
+ * @param flags Ignored
+ * @return Read bytes or negative value in case of failure
+ */
+static ssize_t disk_read(struct cdev *cdev, void *_buf, size_t count, ulong offset, ulong flags)
+{
+ struct device_d *dev = cdev->dev;
+ struct ata_interface *intf = dev->platform_data;
+ int rc;
+ unsigned sep_count = offset & (SECTOR_SIZE - 1);
+ ssize_t read = 0;
+
+ /* starting at no sector boundary? */
+ if (sep_count != 0) {
+ uint8_t tmp_buf[SECTOR_SIZE];
+ unsigned to_read = min(SECTOR_SIZE - sep_count, count);
+
+ rc = intf->read(dev, offset / SECTOR_SIZE, 1, tmp_buf);
+ if (rc != 0) {
+ dev_err(dev, "Cannot read data\n");
+ return -1;
+ }
+ memcpy(_buf, &tmp_buf[sep_count], to_read);
+ _buf += to_read;
+ offset += to_read;
+ count -= to_read;
+ read += to_read;
+ }
+
+ /* full sector part */
+ sep_count = count / SECTOR_SIZE;
+ if (sep_count) {
+ rc = intf->read(dev, offset / SECTOR_SIZE, sep_count, _buf);
+ if (rc != 0) {
+ dev_err(dev, "Cannot read data\n");
+ return -1;
+ }
+ _buf += sep_count * SECTOR_SIZE;
+ offset += sep_count * SECTOR_SIZE;
+ count -= sep_count * SECTOR_SIZE;
+ read += sep_count * SECTOR_SIZE;
+ }
+
+ /* ending at no sector boundary? */
+ if (count) {
+ uint8_t tmp_buf[SECTOR_SIZE];
+
+ rc = intf->read(dev, offset / SECTOR_SIZE, 1, tmp_buf);
+ if (rc != 0) {
+ dev_err(dev, "Cannot read data\n");
+ return -1;
+ }
+ memcpy(_buf, tmp_buf, count);
+ read += count;
+ }
+
+ return read;
+}
+
+static struct file_operations disk_ops = {
+ .read = disk_read,
+ .write = disk_write,
+ .lseek = dev_lseek_default,
+};
+
+/**
+ * Probe the connected disk drive
+ */
+static int disk_probe(struct device_d *dev)
+{
+ uint8_t sector[512];
+ int rc;
+ struct ata_interface *intf = dev->platform_data;
+ struct cdev *disk_cdev;
+
+ rc = intf->read(dev, 0, 1, sector);
+ if (rc != 0) {
+ dev_err(dev, "Cannot read MBR of this device\n");
+ return -1;
+ }
+
+ /* It seems a valuable disk. Register it */
+ disk_cdev = xzalloc(sizeof(struct cdev));
+ if (disk_cdev == NULL) {
+ dev_err(dev, "Out of memory\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * BIOS based disks needs special handling. Not the driver can
+ * enumerate the hardware, the BIOS did it already. To show the user
+ * the drive ordering must not correspond to the Linux drive order,
+ * use the 'biosdisk' name instead.
+ */
+#ifdef CONFIG_ATA_BIOS
+ if (strcmp(dev->driver->name, "biosdisk") == 0)
+ disk_cdev->name = asprintf("biosdisk%d", dev->id);
+ else
+#endif
+ disk_cdev->name = asprintf("disk%d", dev->id);
+ /**
+ * @todo we need the size of the drive, else its nearly impossible
+ * to do anything with it (at least with the generic routines)
+ */
+ disk_cdev->size = 32; /* FIXME */
+ disk_cdev->ops = &disk_ops;
+ disk_cdev->dev = dev;
+ devfs_create(disk_cdev);
+
+ if ((sector[510] != 0x55) || (sector[511] != 0xAA)) {
+ dev_info(dev, "No partition table found\n");
+ return 0;
+ }
+
+ /* guess the size of this drive */
+ dev->size = disk_guess_size(dev, (struct partition_entry*)&sector[446]) * SECTOR_SIZE;
+ dev_info(dev, "Drive size guessed to %u kiB\n", dev->size / 1024);
+ disk_cdev->size = dev->size;
+
+ disk_register_partitions(dev, (struct partition_entry*)&sector[446]);
+
+ return 0;
+}
+
+#ifdef CONFIG_ATA_BIOS
+static struct driver_d biosdisk_driver = {
+ .name = "biosdisk",
+ .probe = disk_probe,
+};
+#endif
+
+static struct driver_d disk_driver = {
+ .name = "disk",
+ .probe = disk_probe,
+};
+
+static int disk_init(void)
+{
+#ifdef CONFIG_ATA_BIOS
+ register_driver(&biosdisk_driver);
+#endif
+ register_driver(&disk_driver);
+ return 0;
+}
+
+device_initcall(disk_init);
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 46723ed84a..f1b2949fa7 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -13,4 +13,7 @@ config DRIVER_I2C_MC13892
config DRIVER_I2C_MC9SDZ60
bool "MC9SDZ60 driver"
+config DRIVER_I2C_LP3972
+ bool "LP3972 driver"
+
endif
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 5dd642fda3..62d030bf0b 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_DRIVER_I2C_IMX) += i2c-imx.o
obj-$(CONFIG_DRIVER_I2C_MC13892) += mc13892.o
obj-$(CONFIG_DRIVER_I2C_MC9SDZ60) += mc9sdz60.o
+obj-$(CONFIG_DRIVER_I2C_LP3972) += lp3972.o
diff --git a/drivers/i2c/lp3972.c b/drivers/i2c/lp3972.c
new file mode 100644
index 0000000000..98266990dc
--- /dev/null
+++ b/drivers/i2c/lp3972.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2007 Sascha Hauer, Pengutronix
+ * 2009 Marc Kleine-Budde <mkl@pengutronix.de>
+ * 2009 Eric Benard <eric@eukrea.com>
+ *
+ * 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 <init.h>
+#include <driver.h>
+#include <xfuncs.h>
+#include <errno.h>
+
+#include <i2c/i2c.h>
+
+#include <asm/byteorder.h>
+
+#define DRIVERNAME "lp3972"
+
+struct lp_priv {
+ struct cdev cdev;
+ struct i2c_client *client;
+};
+
+#define to_lp_priv(a) container_of(a, struct lp_priv, cdev)
+
+static struct lp_priv *lp_dev;
+
+struct i2c_client *lp3972_get_client(void)
+{
+ if (!lp_dev)
+ return NULL;
+
+ return lp_dev->client;
+}
+
+static u32 lp_read_reg(struct lp_priv *lp, int reg)
+{
+ u8 buf;
+
+ i2c_read_reg(lp->client, reg, &buf, sizeof(buf));
+
+ return buf;
+}
+
+static ssize_t lp_read(struct cdev *cdev, void *_buf, size_t count, ulong offset, ulong flags)
+{
+ struct lp_priv *priv = to_lp_priv(cdev);
+ int i = count;
+ u8 *buf = _buf;
+
+ while (i) {
+ *buf = lp_read_reg(priv, offset);
+ buf++;
+ i--;
+ offset++;
+ }
+
+ return count;
+}
+
+static struct file_operations lp_fops = {
+ .lseek = dev_lseek_default,
+ .read = lp_read,
+};
+
+static int lp_probe(struct device_d *dev)
+{
+ if (lp_dev)
+ return -EBUSY;
+
+ lp_dev = xzalloc(sizeof(struct lp_priv));
+ lp_dev->cdev.name = DRIVERNAME;
+ lp_dev->client = to_i2c_client(dev);
+ lp_dev->cdev.size = 256;
+ lp_dev->cdev.dev = dev;
+ lp_dev->cdev.ops = &lp_fops;
+
+ devfs_create(&lp_dev->cdev);
+
+ return 0;
+}
+
+static struct driver_d lp_driver = {
+ .name = DRIVERNAME,
+ .probe = lp_probe,
+};
+
+static int lp_init(void)
+{
+ register_driver(&lp_driver);
+ return 0;
+}
+
+device_initcall(lp_init);
diff --git a/drivers/i2c/mc13892.c b/drivers/i2c/mc13892.c
index 54661d4f5e..67d4232a23 100644
--- a/drivers/i2c/mc13892.c
+++ b/drivers/i2c/mc13892.c
@@ -26,47 +26,99 @@
#include <errno.h>
#include <i2c/i2c.h>
-
-#include <asm/byteorder.h>
+#include <i2c/mc13892.h>
#define DRIVERNAME "mc13892"
-struct mc_priv {
- struct cdev cdev;
- struct i2c_client *client;
-};
-
-#define to_mc_priv(a) container_of(a, struct mc_priv, cdev)
+#define to_mc13892(a) container_of(a, struct mc13892, cdev)
-static struct mc_priv *mc_dev;
+static struct mc13892 *mc_dev;
-struct i2c_client *mc13892_get_client(void)
+struct mc13892 *mc13892_get(void)
{
if (!mc_dev)
return NULL;
- return mc_dev->client;
+ return mc_dev;
+}
+EXPORT_SYMBOL(mc13892_get);
+
+int mc13892_reg_read(struct mc13892 *mc13892, enum mc13892_reg reg, u32 *val)
+{
+ u8 buf[3];
+ int ret;
+
+ ret = i2c_read_reg(mc13892->client, reg, buf, 3);
+ *val = buf[0] << 16 | buf[1] << 8 | buf[2] << 0;
+
+ return ret == 3 ? 0 : ret;
}
+EXPORT_SYMBOL(mc13892_reg_read)
-static u32 mc_read_reg(struct mc_priv *mc, int reg)
+int mc13892_reg_write(struct mc13892 *mc13892, enum mc13892_reg reg, u32 val)
{
- u32 buf;
+ u8 buf[] = {
+ val >> 16,
+ val >> 8,
+ val >> 0,
+ };
+ int ret;
+
+ ret = i2c_write_reg(mc13892->client, reg, buf, 3);
+
+ return ret == 3 ? 0 : ret;
+}
+EXPORT_SYMBOL(mc13892_reg_write)
+
+int mc13892_set_bits(struct mc13892 *mc13892, enum mc13892_reg reg, u32 mask, u32 val)
+{
+ u32 tmp;
+ int err;
+
+ err = mc13892_reg_read(mc13892, reg, &tmp);
+ tmp = (tmp & ~mask) | val;
- i2c_read_reg(mc->client, reg, (u8 *)&buf, sizeof(buf));
+ if (!err)
+ err = mc13892_reg_write(mc13892, reg, tmp);
- return buf;
+ return err;
}
+EXPORT_SYMBOL(mc13892_set_bits);
static ssize_t mc_read(struct cdev *cdev, void *_buf, size_t count, ulong offset, ulong flags)
{
- struct mc_priv *priv = to_mc_priv(cdev);
- int i = count >> 2;
+ struct mc13892 *priv = to_mc13892(cdev);
u32 *buf = _buf;
+ size_t i = count >> 2;
+ int err;
+
+ offset >>= 2;
+
+ while (i) {
+ err = mc13892_reg_read(priv, offset, buf);
+ if (err)
+ return (ssize_t)err;
+ buf++;
+ i--;
+ offset++;
+ }
+
+ return count;
+}
+
+static ssize_t mc_write(struct cdev *cdev, const void *_buf, size_t count, ulong offset, ulong flags)
+{
+ struct mc13892 *mc13892 = to_mc13892(cdev);
+ const u32 *buf = _buf;
+ size_t i = count >> 2;
+ int err;
offset >>= 2;
while (i) {
- *buf = mc_read_reg(priv, offset);
+ err = mc13892_reg_write(mc13892, offset, *buf);
+ if (err)
+ return (ssize_t)err;
buf++;
i--;
offset++;
@@ -78,6 +130,7 @@ static ssize_t mc_read(struct cdev *cdev, void *_buf, size_t count, ulong offset
static struct file_operations mc_fops = {
.lseek = dev_lseek_default,
.read = mc_read,
+ .write = mc_write,
};
static int mc_probe(struct device_d *dev)
@@ -85,7 +138,7 @@ static int mc_probe(struct device_d *dev)
if (mc_dev)
return -EBUSY;
- mc_dev = xzalloc(sizeof(struct mc_priv));
+ mc_dev = xzalloc(sizeof(struct mc13892));
mc_dev->cdev.name = DRIVERNAME;
mc_dev->client = to_i2c_client(dev);
mc_dev->cdev.size = 256;
diff --git a/drivers/i2c/mc9sdz60.c b/drivers/i2c/mc9sdz60.c
index 4b1068d17e..3580af8852 100644
--- a/drivers/i2c/mc9sdz60.c
+++ b/drivers/i2c/mc9sdz60.c
@@ -26,45 +26,88 @@
#include <errno.h>
#include <i2c/i2c.h>
-
-#include <asm/byteorder.h>
+#include <i2c/mc9sdz60.h>
#define DRIVERNAME "mc9sdz60"
-struct mc_priv {
- struct cdev cdev;
- struct i2c_client *client;
-};
-
-#define to_mc_priv(a) container_of(a, struct mc_priv, cdev)
+#define to_mc9sdz60(a) container_of(a, struct mc9sdz60, cdev)
-static struct mc_priv *mc_dev;
+static struct mc9sdz60 *mc_dev;
-struct i2c_client *mc9sdz60_get_client(void)
+struct mc9sdz60 *mc9sdz60_get(void)
{
if (!mc_dev)
return NULL;
- return mc_dev->client;
+ return mc_dev;
}
+EXPORT_SYMBOL(mc9sdz60_get);
-static u32 mc_read_reg(struct mc_priv *mc, int reg)
+int mc9sdz60_reg_read(struct mc9sdz60 *mc9sdz60, enum mc9sdz60_reg reg, u8 *val)
{
- u8 buf;
+ int ret;
- i2c_read_reg(mc->client, reg, &buf, sizeof(buf));
+ ret = i2c_read_reg(mc9sdz60->client, reg, val, 1);
- return buf;
+ return ret == 1 ? 0 : ret;
}
+EXPORT_SYMBOL(mc9sdz60_reg_read)
+
+int mc9sdz60_reg_write(struct mc9sdz60 *mc9sdz60, enum mc9sdz60_reg reg, u8 val)
+{
+ int ret;
+
+ ret = i2c_write_reg(mc9sdz60->client, reg, &val, 1);
+
+ return ret == 1 ? 0 : ret;
+}
+EXPORT_SYMBOL(mc9sdz60_reg_write)
+
+int mc9sdz60_set_bits(struct mc9sdz60 *mc9sdz60, enum mc9sdz60_reg reg, u8 mask, u8 val)
+{
+ u8 tmp;
+ int err;
+
+ err = mc9sdz60_reg_read(mc9sdz60, reg, &tmp);
+ tmp = (tmp & ~mask) | val;
+
+ if (!err)
+ err = mc9sdz60_reg_write(mc9sdz60, reg, tmp);
+
+ return err;
+}
+EXPORT_SYMBOL(mc9sdz60_set_bits);
static ssize_t mc_read(struct cdev *cdev, void *_buf, size_t count, ulong offset, ulong flags)
{
- struct mc_priv *priv = to_mc_priv(cdev);
- int i = count;
+ struct mc9sdz60 *mc9sdz60 = to_mc9sdz60(cdev);
u8 *buf = _buf;
+ size_t i = count;
+ int err;
+
+ while (i) {
+ err = mc9sdz60_reg_read(mc9sdz60, offset, buf);
+ if (err)
+ return (ssize_t)err;
+ buf++;
+ i--;
+ offset++;
+ }
+
+ return count;
+}
+
+static ssize_t mc_write(struct cdev *cdev, const void *_buf, size_t count, ulong offset, ulong flags)
+{
+ struct mc9sdz60 *mc9sdz60 = to_mc9sdz60(cdev);
+ const u8 *buf = _buf;
+ size_t i = count;
+ int err;
while (i) {
- *buf = mc_read_reg(priv, offset);
+ err = mc9sdz60_reg_write(mc9sdz60, offset, *buf);
+ if (err)
+ return (ssize_t)err;
buf++;
i--;
offset++;
@@ -76,6 +119,7 @@ static ssize_t mc_read(struct cdev *cdev, void *_buf, size_t count, ulong offset
static struct file_operations mc_fops = {
.lseek = dev_lseek_default,
.read = mc_read,
+ .write = mc_write,
};
static int mc_probe(struct device_d *dev)
@@ -83,10 +127,10 @@ static int mc_probe(struct device_d *dev)
if (mc_dev)
return -EBUSY;
- mc_dev = xzalloc(sizeof(struct mc_priv));
+ mc_dev = xzalloc(sizeof(struct mc9sdz60));
mc_dev->cdev.name = DRIVERNAME;
mc_dev->client = to_i2c_client(dev);
- mc_dev->cdev.size = 256;
+ mc_dev->cdev.size = 64; /* 35 known registers */
mc_dev->cdev.dev = dev;
mc_dev->cdev.ops = &mc_fops;
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index ed7656ee20..09555622b4 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -64,6 +64,11 @@ config DRIVER_NET_FEC_IMX
depends on ARCH_HAS_FEC_IMX
select MIIPHY
+config DRIVER_NET_EP93XX
+ bool "EP93xx Ethernet driver"
+ depends on ARCH_EP93XX
+ select MIIPHY
+
config DRIVER_NET_MACB
bool "macb Ethernet driver"
depends on ARCH_AT91
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 6751920470..1b6f1046d5 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_DRIVER_NET_NETX) += netx_eth.o
obj-$(CONFIG_DRIVER_NET_AT91_ETHER) += at91_ether.o
obj-$(CONFIG_DRIVER_NET_MPC5200) += fec_mpc5200.o
obj-$(CONFIG_DRIVER_NET_FEC_IMX) += fec_imx.o
+obj-$(CONFIG_DRIVER_NET_EP93XX) += ep93xx.o
obj-$(CONFIG_DRIVER_NET_MACB) += macb.o
obj-$(CONFIG_DRIVER_NET_TAP) += tap.o
obj-$(CONFIG_MIIPHY) += miiphy.o
diff --git a/drivers/net/ep93xx.c b/drivers/net/ep93xx.c
new file mode 100644
index 0000000000..aa1a00536d
--- /dev/null
+++ b/drivers/net/ep93xx.c
@@ -0,0 +1,676 @@
+/*
+ * Cirrus Logic EP93xx ethernet MAC / MII driver.
+ *
+ * Copyright (C) 2009 Matthias Kaehlcke <matthias@kaehlcke.net>
+ *
+ * Copyright (C) 2004, 2005
+ * Cory T. Tusar, Videon Central, Inc., <ctusar@videon-central.com>
+ *
+ * Based on the original eth.[ch] Cirrus Logic EP93xx Rev D. Ethernet Driver,
+ * which is
+ *
+ * (C) Copyright 2002 2003
+ * Adam Bezanson, Network Audio Technologies, Inc.
+ * <bezanson@netaudiotech.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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <common.h>
+#include <command.h>
+#include <init.h>
+#include <malloc.h>
+#include <miiphy.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <mach/ep93xx-regs.h>
+#include "ep93xx.h"
+
+static int ep93xx_eth_send_packet(struct eth_device *edev,
+ void *packet, int length);
+static int ep93xx_eth_rcv_packet(struct eth_device *edev);
+
+static int ep93xx_phy_read(struct miiphy_device *mdev, uint8_t phy_addr,
+ uint8_t phy_reg, uint16_t *value);
+static int ep93xx_phy_write(struct miiphy_device *mdev, uint8_t phy_addr,
+ uint8_t phy_reg, uint16_t value);
+
+static inline struct ep93xx_eth_priv *ep93xx_get_priv(struct eth_device *edev)
+{
+ return (struct ep93xx_eth_priv *)edev->priv;
+}
+
+static inline struct mac_regs *ep93xx_get_regs(struct eth_device *edev)
+{
+ struct ep93xx_eth_priv *priv = ep93xx_get_priv(edev);
+
+ return priv->regs;
+}
+
+#if defined(EP93XX_MAC_DEBUG)
+/**
+ * Dump ep93xx_mac values to the terminal.
+ */
+inline void dump_dev(struct eth_device *edev)
+{
+ struct ep93xx_eth_priv *priv = ep93xx_get_priv(edev);
+ int i;
+
+ printf("\ndump_dev()\n");
+ printf(" rx_dq.base %08X\n", priv->rx_dq.base);
+ printf(" rx_dq.current %08X\n", priv->rx_dq.current);
+ printf(" rx_dq.end %08X\n", priv->rx_dq.end);
+ printf(" rx_sq.base %08X\n", priv->rx_sq.base);
+ printf(" rx_sq.current %08X\n", priv->rx_sq.current);
+ printf(" rx_sq.end %08X\n", priv->rx_sq.end);
+
+ for (i = 0; i < NUMRXDESC; i++)
+ printf(" rx_buffer[%2.d] %08X\n", i, NetRxPackets[i]);
+
+ printf(" tx_dq.base %08X\n", priv->tx_dq.base);
+ printf(" tx_dq.current %08X\n", priv->tx_dq.current);
+ printf(" tx_dq.end %08X\n", priv->tx_dq.end);
+ printf(" tx_sq.base %08X\n", priv->tx_sq.base);
+ printf(" tx_sq.current %08X\n", priv->tx_sq.current);
+ printf(" tx_sq.end %08X\n", priv->tx_sq.end);
+}
+
+/**
+ * Dump all RX descriptor queue entries to the terminal.
+ */
+inline void dump_rx_descriptor_queue(struct eth_device *edev)
+{
+ struct ep93xx_eth_priv *priv = ep93xx_get_priv(edev);
+ int i;
+
+ printf("\ndump_rx_descriptor_queue()\n");
+ printf(" descriptor address word1 word2\n");
+ for (i = 0; i < NUMRXDESC; i++) {
+ printf(" [ %08X ] %08X %08X\n",
+ (priv->rx_dq.base + i),
+ (priv->rx_dq.base + i)->word1,
+ (priv->rx_dq.base + i)->word2);
+ }
+}
+
+/**
+ * Dump all RX status queue entries to the terminal.
+ */
+inline void dump_rx_status_queue(void)
+{
+ struct ep93xx_eth_priv *priv = ep93xx_get_priv(edev);
+ int i;
+
+ printf("\ndump_rx_status_queue()\n");
+ printf(" descriptor address word1 word2\n");
+ for (i = 0; i < NUMRXDESC; i++) {
+ printf(" [ %08X ] %08X %08X\n",
+ (priv->rx_sq.base + i),
+ (priv->rx_sq.base + i)->word1,
+ (priv->rx_sq.base + i)->word2);
+ }
+}
+
+/**
+ * Dump all TX descriptor queue entries to the terminal.
+ */
+inline void dump_tx_descriptor_queue(void)
+{
+ struct ep93xx_eth_priv *priv = ep93xx_get_priv(edev);
+ int i;
+
+ printf("\ndump_tx_descriptor_queue()\n");
+ printf(" descriptor address word1 word2\n");
+ for (i = 0; i < NUMTXDESC; i++) {
+ printf(" [ %08X ] %08X %08X\n",
+ (priv->tx_dq.base + i),
+ (priv->tx_dq.base + i)->word1,
+ (priv->tx_dq.base + i)->word2);
+ }
+}
+
+/**
+ * Dump all TX status queue entries to the terminal.
+ */
+inline void dump_tx_status_queue(void)
+{
+ struct ep93xx_eth_priv *priv = ep93xx_get_priv(edev);
+ int i;
+
+ printf("\ndump_tx_status_queue()\n");
+ printf(" descriptor address word1\n");
+ for (i = 0; i < NUMTXDESC; i++) {
+ printf(" [ %08X ] %08X\n",
+ (priv->rx_sq.base + i),
+ (priv->rx_sq.base + i)->word1);
+ }
+}
+#else
+#define dump_dev(x)
+#define dump_rx_descriptor_queue()
+#define dump_rx_status_queue()
+#define dump_tx_descriptor_queue()
+#define dump_tx_status_queue()
+#endif /* defined(EP93XX_MAC_DEBUG) */
+
+/**
+ * Reset the EP93xx MAC by twiddling the soft reset bit and spinning until
+ * it's cleared.
+ */
+static void ep93xx_eth_reset(struct eth_device *edev)
+{
+ struct mac_regs *regs = ep93xx_get_regs(edev);
+ uint32_t value;
+
+ pr_debug("+ep93xx_eth_reset\n");
+
+ value = readl(&regs->selfctl);
+ value |= SELFCTL_RESET;
+ writel(value, &regs->selfctl);
+
+ while (readl(&regs->selfctl) & SELFCTL_RESET)
+ ; /* noop */
+
+ pr_debug("-ep93xx_eth_reset\n");
+}
+
+static int ep93xx_eth_init_dev(struct eth_device *edev)
+{
+ pr_debug("+ep93xx_eth_init_dev\n");
+
+ pr_debug("-ep93xx_eth_init_dev\n");
+
+ return 0;
+}
+
+static int ep93xx_eth_open(struct eth_device *edev)
+{
+ struct ep93xx_eth_priv *priv = ep93xx_get_priv(edev);
+ struct mac_regs *regs = ep93xx_get_regs(edev);
+ int i;
+
+ pr_debug("+ep93xx_eth_open\n");
+
+ ep93xx_eth_reset(edev);
+
+ /* Reset the descriptor queues' current and end address values */
+ priv->tx_dq.current = priv->tx_dq.base;
+ priv->tx_dq.end = (priv->tx_dq.base + NUMTXDESC);
+
+ priv->tx_sq.current = priv->tx_sq.base;
+ priv->tx_sq.end = (priv->tx_sq.base + NUMTXDESC);
+
+ priv->rx_dq.current = priv->rx_dq.base;
+ priv->rx_dq.end = (priv->rx_dq.base + NUMRXDESC);
+
+ priv->rx_sq.current = priv->rx_sq.base;
+ priv->rx_sq.end = (priv->rx_sq.base + NUMRXDESC);
+
+ /*
+ * Set the transmit descriptor and status queues' base address,
+ * current address, and length registers. Set the maximum frame
+ * length and threshold. Enable the transmit descriptor processor.
+ */
+ writel((uint32_t)priv->tx_dq.base, &regs->txdq.badd);
+ writel((uint32_t)priv->tx_dq.base, &regs->txdq.curadd);
+ writel(sizeof(struct tx_descriptor) * NUMTXDESC, &regs->txdq.blen);
+
+ writel((uint32_t)priv->tx_sq.base, &regs->txstsq.badd);
+ writel((uint32_t)priv->tx_sq.base, &regs->txstsq.curadd);
+ writel(sizeof(struct tx_status) * NUMTXDESC, &regs->txstsq.blen);
+
+ writel(0x00040000, &regs->txdthrshld);
+ writel(0x00040000, &regs->txststhrshld);
+
+ writel((TXSTARTMAX << 0) | (PKTSIZE_ALIGN << 16), &regs->maxfrmlen);
+ writel(BMCTL_TXEN, &regs->bmctl);
+
+ /*
+ * Set the receive descriptor and status queues' base address,
+ * current address, and length registers. Enable the receive
+ * descriptor processor.
+ */
+ writel((uint32_t)priv->rx_dq.base, &regs->rxdq.badd);
+ writel((uint32_t)priv->rx_dq.base, &regs->rxdq.curadd);
+ writel(sizeof(struct rx_descriptor) * NUMRXDESC, &regs->rxdq.blen);
+
+ writel((uint32_t)priv->rx_sq.base, &regs->rxstsq.badd);
+ writel((uint32_t)priv->rx_sq.base, &regs->rxstsq.curadd);
+ writel(sizeof(struct rx_status) * NUMRXDESC, &regs->rxstsq.blen);
+
+ writel(0x00040000, &regs->rxdthrshld);
+
+ writel(BMCTL_RXEN, &regs->bmctl);
+
+ writel(0x00040000, &regs->rxststhrshld);
+
+ /* Wait until the receive descriptor processor is active */
+ while (!(readl(&regs->bmsts) & BMSTS_RXACT))
+ ; /* noop */
+
+ /*
+ * Initialize the RX descriptor queue. Clear the TX descriptor queue.
+ * Clear the RX and TX status queues. Enqueue the RX descriptor and
+ * status entries to the MAC.
+ */
+ for (i = 0; i < NUMRXDESC; i++) {
+ /* set buffer address */
+ (priv->rx_dq.base + i)->word1 = (uint32_t)NetRxPackets[i];
+
+ /* set buffer length, clear buffer index and NSOF */
+ (priv->rx_dq.base + i)->word2 = PKTSIZE_ALIGN;
+ }
+
+ memset(priv->tx_dq.base, 0,
+ (sizeof(struct tx_descriptor) * NUMTXDESC));
+ memset(priv->rx_sq.base, 0,
+ (sizeof(struct rx_status) * NUMRXDESC));
+ memset(priv->tx_sq.base, 0,
+ (sizeof(struct tx_status) * NUMTXDESC));
+
+ writel(NUMRXDESC, &regs->rxdqenq);
+ writel(NUMRXDESC, &regs->rxstsqenq);
+
+ /* Turn on RX and TX */
+ writel(RXCTL_IA0 | RXCTL_BA | RXCTL_SRXON |
+ RXCTL_RCRCA | RXCTL_MA, &regs->rxctl);
+ writel(TXCTL_STXON, &regs->txctl);
+
+ /* Dump data structures if we're debugging */
+ dump_dev();
+ dump_rx_descriptor_queue();
+ dump_rx_status_queue();
+ dump_tx_descriptor_queue();
+ dump_tx_status_queue();
+
+ pr_debug("-ep93xx_eth_open\n");
+
+ return 0;
+}
+
+/**
+ * Halt EP93xx MAC transmit and receive by clearing the TxCTL and RxCTL
+ * registers.
+ */
+static void ep93xx_eth_halt(struct eth_device *edev)
+{
+ struct mac_regs *regs = ep93xx_get_regs(edev);
+
+ pr_debug("+ep93xx_eth_halt\n");
+
+ writel(0x00000000, &regs->rxctl);
+ writel(0x00000000, &regs->txctl);
+
+ pr_debug("-ep93xx_eth_halt\n");
+}
+
+static int ep93xx_eth_get_ethaddr(struct eth_device *edev,
+ unsigned char *mac_addr)
+{
+ struct mac_regs *regs = ep93xx_get_regs(edev);
+ uint32_t value;
+
+ value = readl(&regs->indad);
+ mac_addr[0] = value & 0xFF;
+ mac_addr[1] = (value >> 8) & 0xFF;
+ mac_addr[2] = (value >> 16) & 0xFF;
+ mac_addr[3] = (value >> 24) & 0xFF;
+
+ value = readl(&regs->indad_upper);
+ mac_addr[4] = value & 0xFF;
+ mac_addr[5] = (value >> 8) & 0xFF;
+
+ return 0;
+}
+
+static int ep93xx_eth_set_ethaddr(struct eth_device *edev,
+ unsigned char *mac_addr)
+{
+ struct mac_regs *regs = ep93xx_get_regs(edev);
+
+ writel(AFP_IAPRIMARY, &regs->afp);
+
+ writel(mac_addr[0] | (mac_addr[1] << 8) |
+ (mac_addr[2] << 16) | (mac_addr[3] << 24),
+ &regs->indad);
+ writel(mac_addr[4] | (mac_addr[5] << 8), &regs->indad_upper);
+
+ return 0;
+}
+
+static int ep93xx_eth_probe(struct device_d *dev)
+{
+ struct eth_device *edev;
+ struct ep93xx_eth_priv *priv;
+ int ret = -1;
+
+ pr_debug("ep93xx_eth_probe()\n");
+
+ edev = xzalloc(sizeof(struct eth_device) +
+ sizeof(struct ep93xx_eth_priv));
+ dev->type_data = edev;
+ edev->priv = (struct ep93xx_eth_priv *)(edev + 1);
+
+ priv = edev->priv;
+ priv->regs = (struct mac_regs *)MAC_BASE;
+
+ edev->init = ep93xx_eth_init_dev;
+ edev->open = ep93xx_eth_open;
+ edev->send = ep93xx_eth_send_packet;
+ edev->recv = ep93xx_eth_rcv_packet;
+ edev->halt = ep93xx_eth_halt;
+ edev->get_ethaddr = ep93xx_eth_get_ethaddr;
+ edev->set_ethaddr = ep93xx_eth_set_ethaddr;
+
+ priv->miiphy.read = ep93xx_phy_read;
+ priv->miiphy.write = ep93xx_phy_write;
+ priv->miiphy.address = 0;
+ priv->miiphy.flags = 0;
+
+ priv->tx_dq.base = calloc(NUMTXDESC,
+ sizeof(struct tx_descriptor));
+ if (priv->tx_dq.base == NULL) {
+ pr_err("calloc() failed: tx_dq.base");
+ goto eth_probe_failed_0;
+ }
+
+ priv->tx_sq.base = calloc(NUMTXDESC,
+ sizeof(struct tx_status));
+ if (priv->tx_sq.base == NULL) {
+ pr_err("calloc() failed: tx_sq.base");
+ goto eth_probe_failed_1;
+ }
+
+ priv->rx_dq.base = calloc(NUMRXDESC,
+ sizeof(struct rx_descriptor));
+ if (priv->rx_dq.base == NULL) {
+ pr_err("calloc() failed: rx_dq.base");
+ goto eth_probe_failed_2;
+ }
+
+ priv->rx_sq.base = calloc(NUMRXDESC,
+ sizeof(struct rx_status));
+ if (priv->rx_sq.base == NULL) {
+ pr_err("calloc() failed: rx_sq.base");
+ goto eth_probe_failed_3;
+ }
+
+ miiphy_register(&priv->miiphy);
+ eth_register(edev);
+
+ ret = 0;
+
+ goto eth_probe_done;
+
+eth_probe_failed_3:
+ free(priv->rx_dq.base);
+ /* Fall through */
+
+eth_probe_failed_2:
+ free(priv->tx_sq.base);
+ /* Fall through */
+
+eth_probe_failed_1:
+ free(priv->tx_dq.base);
+ /* Fall through */
+
+eth_probe_failed_0:
+ /* Fall through */
+
+eth_probe_done:
+ return ret;
+}
+
+/**
+ * Copy a frame of data from the MAC into the protocol layer for further
+ * processing.
+ */
+static int ep93xx_eth_rcv_packet(struct eth_device *edev)
+{
+ struct ep93xx_eth_priv *priv = ep93xx_get_priv(edev);
+ struct mac_regs *regs = ep93xx_get_regs(edev);
+ int ret = -1;
+
+ pr_debug("+ep93xx_eth_rcv_packet\n");
+
+ if (RX_STATUS_RFP(priv->rx_sq.current)) {
+ if (RX_STATUS_RWE(priv->rx_sq.current)) {
+ /*
+ * We have a good frame. Extract the frame's length
+ * from the current rx_status_queue entry, and copy
+ * the frame's data into NetRxPackets[] of the
+ * protocol stack. We track the total number of
+ * bytes in the frame (nbytes_frame) which will be
+ * used when we pass the data off to the protocol
+ * layer via NetReceive().
+ */
+ NetReceive((uchar *)priv->rx_dq.current->word1,
+ RX_STATUS_FRAME_LEN(priv->rx_sq.current));
+ pr_debug("reporting %d bytes...\n",
+ RX_STATUS_FRAME_LEN(priv->rx_sq.current));
+
+ ret = 0;
+
+ } else {
+ /* Do we have an erroneous packet? */
+ pr_err("packet rx error, status %08X %08X\n",
+ priv->rx_sq.current->word1,
+ priv->rx_sq.current->word2);
+ dump_rx_descriptor_queue();
+ dump_rx_status_queue();
+ }
+
+ /*
+ * Clear the associated status queue entry, and
+ * increment our current pointers to the next RX
+ * descriptor and status queue entries (making sure
+ * we wrap properly).
+ */
+ memset((void *)priv->rx_sq.current, 0,
+ sizeof(struct rx_status));
+
+ priv->rx_sq.current++;
+ if (priv->rx_sq.current >= priv->rx_sq.end)
+ priv->rx_sq.current = priv->rx_sq.base;
+
+ priv->rx_dq.current++;
+ if (priv->rx_dq.current >= priv->rx_dq.end)
+ priv->rx_dq.current = priv->rx_dq.base;
+
+ /*
+ * Finally, return the RX descriptor and status entries
+ * back to the MAC engine, and loop again, checking for
+ * more descriptors to process.
+ */
+ writel(1, &regs->rxdqenq);
+ writel(1, &regs->rxstsqenq);
+ } else {
+ ret = 0;
+ }
+
+ pr_debug("-ep93xx_eth_rcv_packet %d\n", ret);
+
+ return ret;
+}
+
+/**
+ * Send a block of data via ethernet.
+ */
+static int ep93xx_eth_send_packet(struct eth_device *edev,
+ void *packet, int length)
+{
+ struct ep93xx_eth_priv *priv = ep93xx_get_priv(edev);
+ struct mac_regs *regs = ep93xx_get_regs(edev);
+ int ret = -1;
+
+ pr_debug("+ep93xx_eth_send_packet\n");
+
+ /*
+ * Initialize the TX descriptor queue with the new packet's info.
+ * Clear the associated status queue entry. Enqueue the packet
+ * to the MAC for transmission.
+ */
+
+ /* set buffer address */
+ priv->tx_dq.current->word1 = (uint32_t)packet;
+
+ /* set buffer length and EOF bit */
+ priv->tx_dq.current->word2 = length | TX_DESC_EOF;
+
+ /* clear tx status */
+ priv->tx_sq.current->word1 = 0;
+
+ /* enqueue the TX descriptor */
+ writel(1, &regs->txdqenq);
+
+ /* wait for the frame to become processed */
+ while (!TX_STATUS_TXFP(priv->tx_sq.current))
+ ; /* noop */
+
+ if (!TX_STATUS_TXWE(priv->tx_sq.current)) {
+ pr_err("packet tx error, status %08X\n",
+ priv->tx_sq.current->word1);
+ dump_tx_descriptor_queue();
+ dump_tx_status_queue();
+
+ /* TODO: Add better error handling? */
+ goto eth_send_failed_0;
+ }
+
+ ret = 0;
+ /* Fall through */
+
+eth_send_failed_0:
+ pr_debug("-ep93xx_eth_send_packet %d\n", ret);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * EP93xx ethernet MII functionality.
+ */
+
+/**
+ * Maximum MII address we support
+ */
+#define MII_ADDRESS_MAX (31)
+
+/**
+ * Maximum MII register address we support
+ */
+#define MII_REGISTER_MAX (31)
+
+/**
+ * Read a 16-bit value from an MII register.
+ */
+static int ep93xx_phy_read(struct miiphy_device *mdev, uint8_t phy_addr,
+ uint8_t phy_reg, uint16_t *value)
+{
+ struct mac_regs *regs = ep93xx_get_regs(mdev->edev);
+ int ret = -1;
+ uint32_t self_ctl;
+
+ pr_debug("+ep93xx_phy_read\n");
+
+ /*
+ * Save the current SelfCTL register value. Set MAC to suppress
+ * preamble bits. Wait for any previous MII command to complete
+ * before issuing the new command.
+ */
+ self_ctl = readl(&regs->selfctl);
+#if defined(CONFIG_MII_SUPPRESS_PREAMBLE) /* TODO */
+ writel(self_ctl & ~(1 << 8), &regs->selfctl);
+#endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */
+
+ while (readl(&regs->miists) & MIISTS_BUSY)
+ ; /* noop */
+
+ /*
+ * Issue the MII 'read' command. Wait for the command to complete.
+ * Read the MII data value.
+ */
+ writel(MIICMD_OPCODE_READ | ((uint32_t)phy_addr << 5) |
+ (uint32_t)phy_reg, &regs->miicmd);
+ while (readl(&regs->miists) & MIISTS_BUSY)
+ ; /* noop */
+
+ *value = (unsigned short)readl(&regs->miidata);
+
+ /* Restore the saved SelfCTL value and return. */
+ writel(self_ctl, &regs->selfctl);
+
+ ret = 0;
+
+ pr_debug("-ep93xx_phy_read\n");
+
+ return ret;
+}
+
+/**
+ * Write a 16-bit value to an MII register.
+ */
+static int ep93xx_phy_write(struct miiphy_device *mdev, uint8_t phy_addr,
+ uint8_t phy_reg, uint16_t value)
+{
+ struct mac_regs *regs = ep93xx_get_regs(mdev->edev);
+ int ret = -1;
+ uint32_t self_ctl;
+
+ pr_debug("+ep93xx_phy_write\n");
+
+ /*
+ * Save the current SelfCTL register value. Set MAC to suppress
+ * preamble bits. Wait for any previous MII command to complete
+ * before issuing the new command.
+ */
+ self_ctl = readl(&regs->selfctl);
+#if defined(CONFIG_MII_SUPPRESS_PREAMBLE) /* TODO */
+ writel(self_ctl & ~(1 << 8), &regs->selfctl);
+#endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */
+
+ while (readl(&regs->miists) & MIISTS_BUSY)
+ ; /* noop */
+
+ /* Issue the MII 'write' command. Wait for the command to complete. */
+ writel((uint32_t)value, &regs->miidata);
+ writel(MIICMD_OPCODE_WRITE | ((uint32_t)phy_addr << 5) | phy_reg,
+ &regs->miicmd);
+ while (readl(&regs->miists) & MIISTS_BUSY)
+ ; /* noop */
+
+ /* Restore the saved SelfCTL value and return. */
+ writel(self_ctl, &regs->selfctl);
+
+ ret = 0;
+
+ pr_debug("-ep93xx_phy_write\n");
+
+ return ret;
+}
+
+static struct driver_d ep93xx_eth_driver = {
+ .name = "ep93xx_eth",
+ .probe = ep93xx_eth_probe,
+};
+
+static int ep93xx_eth_init(void)
+{
+ register_driver(&ep93xx_eth_driver);
+ return 0;
+}
+
+device_initcall(ep93xx_eth_init);
diff --git a/drivers/net/ep93xx.h b/drivers/net/ep93xx.h
new file mode 100644
index 0000000000..ae45c54f5c
--- /dev/null
+++ b/drivers/net/ep93xx.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2009 Matthias Kaehlcke <matthias@kaehlcke.net>
+ *
+ * Copyright (C) 2004, 2005
+ * Cory T. Tusar, Videon Central, Inc., <ctusar@videon-central.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
+ *
+ */
+
+#ifndef _ETH_H
+#define _ETH_H
+
+#include <net.h>
+
+/**
+ * #define this to dump device status and queue info during initialization and
+ * following errors.
+ */
+#undef EP93XX_MAC_DEBUG
+
+/**
+ * Number of descriptor and status entries in our RX queues.
+ * It must be power of 2 !
+ */
+#define NUMRXDESC PKTBUFSRX
+
+/**
+ * Number of descriptor and status entries in our TX queues.
+ */
+#define NUMTXDESC 1
+
+/**
+ * 944 = (1024 - 64) - 16, Fifo size - Minframesize - 16 (Chip FACT)
+ */
+#define TXSTARTMAX 944
+
+/**
+ * Receive descriptor queue entry
+ */
+struct rx_descriptor {
+ uint32_t word1;
+ uint32_t word2;
+} __attribute__((packed));
+
+/**
+ * Receive status queue entry
+ */
+struct rx_status {
+ uint32_t word1;
+ uint32_t word2;
+} __attribute__((packed));
+
+#define RX_STATUS_RWE(rx_status) ((rx_status->word1 >> 30) & 0x01)
+#define RX_STATUS_RFP(rx_status) ((rx_status->word1 >> 31) & 0x01)
+#define RX_STATUS_FRAME_LEN(rx_status) (rx_status->word2 & 0xFFFF)
+
+
+/**
+ * Transmit descriptor queue entry
+ */
+struct tx_descriptor {
+ uint32_t word1;
+ uint32_t word2;
+} __attribute__((packed));
+
+#define TX_DESC_EOF (1 << 31)
+
+/**
+ * Transmit status queue entry
+ */
+struct tx_status {
+ uint32_t word1;
+} __attribute__((packed));
+
+#define TX_STATUS_TXWE(tx_status) (((tx_status)->word1 >> 30) & 0x01)
+#define TX_STATUS_TXFP(tx_status) (((tx_status)->word1 >> 31) & 0x01)
+
+/**
+ * Transmit descriptor queue
+ */
+struct tx_descriptor_queue {
+ struct tx_descriptor *base;
+ struct tx_descriptor *current;
+ struct tx_descriptor *end;
+};
+
+/**
+ * Transmit status queue
+ */
+struct tx_status_queue {
+ struct tx_status *base;
+ volatile struct tx_status *current;
+ struct tx_status *end;
+};
+
+/**
+ * Receive descriptor queue
+ */
+struct rx_descriptor_queue {
+ struct rx_descriptor *base;
+ struct rx_descriptor *current;
+ struct rx_descriptor *end;
+};
+
+/**
+ * Receive status queue
+ */
+struct rx_status_queue {
+ struct rx_status *base;
+ volatile struct rx_status *current;
+ struct rx_status *end;
+};
+
+/**
+ * EP93xx MAC private data structure
+ */
+struct ep93xx_eth_priv {
+ struct mac_regs *regs;
+
+ struct rx_descriptor_queue rx_dq;
+ struct rx_status_queue rx_sq;
+ void *rx_buffer[NUMRXDESC];
+
+ struct tx_descriptor_queue tx_dq;
+ struct tx_status_queue tx_sq;
+
+ struct miiphy_device miiphy;
+};
+
+#endif
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index b0ff5fa133..8a5056bc69 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -47,6 +47,13 @@ config DRIVER_SERIAL_NS16550_OMAP_EXTENSIONS
help
Say Y here if you are using OMAP extensions to NS16550
+config DRIVER_SERIAL_PL010
+ depends on ARCH_EP93XX
+ default y
+ bool "ARM AMBA PL010 support"
+ help
+ Enable this to get support for AMBA PL010 based serial devices
+
config DRIVER_SERIAL_S3C24X0
bool "Samsung S3C24X0 serial driver"
depends on ARCH_S3C24xx
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 8ab680de7a..9f203bbc06 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -3,7 +3,6 @@
# s3c4510b_uart.o
# serial_max3100.o
# serial_pl010.o
-# serial_pl011.o
# serial_xuartlite.o
obj-$(CONFIG_DRIVER_SERIAL_ARM_DCC) += arm_dcc.o
obj-$(CONFIG_DRIVER_SERIAL_IMX) += serial_imx.o
@@ -13,4 +12,5 @@ obj-$(CONFIG_DRIVER_SERIAL_LINUX_COMSOLE) += linux_console.o
obj-$(CONFIG_DRIVER_SERIAL_MPC5XXX) += serial_mpc5xxx.o
obj-$(CONFIG_DRIVER_SERIAL_BLACKFIN) += serial_blackfin.o
obj-$(CONFIG_DRIVER_SERIAL_NS16550) += serial_ns16550.o
+obj-$(CONFIG_DRIVER_SERIAL_PL010) += serial_pl010.o
obj-$(CONFIG_DRIVER_SERIAL_S3C24X0) += serial_s3c24x0.o
diff --git a/drivers/serial/serial_pl010.c b/drivers/serial/serial_pl010.c
new file mode 100644
index 0000000000..1a6366f63c
--- /dev/null
+++ b/drivers/serial/serial_pl010.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2010 Matthias Kaehlcke <matthias@kaehlcke.net>
+ *
+ * (C) Copyright 2000
+ * Rob Taylor, Flying Pig Systems. robt@flyingpig.com.
+ *
+ * (C) Copyright 2004
+ * ARM Ltd.
+ * Philippe Robin, <philippe.robin@arm.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
+ */
+
+/* Simple U-Boot driver for the PrimeCell PL010/PL011 UARTs */
+
+#include <common.h>
+#include <init.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include "serial_pl010.h"
+
+static int pl010_setbaudrate(struct console_device *cdev, int baudrate)
+{
+ struct pl010_struct *pl010 = (struct pl010_struct *)cdev->dev->map_base;
+ unsigned int divisor;
+
+ switch (baudrate) {
+ case 9600:
+ divisor = UART_PL010_BAUD_9600;
+ break;
+
+ case 19200:
+ divisor = UART_PL010_BAUD_9600;
+ break;
+
+ case 38400:
+ divisor = UART_PL010_BAUD_38400;
+ break;
+
+ case 57600:
+ divisor = UART_PL010_BAUD_57600;
+ break;
+
+ case 115200:
+ divisor = UART_PL010_BAUD_115200;
+ break;
+
+ default:
+ divisor = UART_PL010_BAUD_38400;
+ }
+
+ writel((divisor & 0xf00) >> 8, &pl010->linctrlmid);
+ writel(divisor & 0xff, &pl010->linctrllow);
+
+ /* high register must always be written */
+ writel(readl(&pl010->linctrlhigh), &pl010->linctrlhigh);
+
+ return 0;
+}
+
+static int pl010_init_port(struct console_device *cdev)
+{
+ struct pl010_struct *pl010 = (struct pl010_struct *)cdev->dev->map_base;
+
+ /*
+ * First, disable everything.
+ */
+ writel(0x00, &pl010->ctrl);
+
+ /*
+ * Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled.
+ */
+ writel(UART_PL010_LCRH_WLEN_8 | UART_PL010_LCRH_FEN,
+ &pl010->linctrlhigh);
+
+ /*
+ * Finally, enable the UART
+ */
+ writel(UART_PL010_CR_UARTEN, &pl010->ctrl);
+
+ return 0;
+}
+
+static void pl010_putc(struct console_device *cdev, char c)
+{
+ struct pl010_struct *pl010 = (struct pl010_struct *)cdev->dev->map_base;
+
+ /* Wait until there is space in the FIFO */
+ while (readl(&pl010->flag) & UART_PL010_FR_TXFF)
+ ; /* noop */
+
+ /* Send the character */
+ writel(c, &pl010->data);
+}
+
+static int pl010_getc(struct console_device *cdev)
+{
+ struct pl010_struct *pl010 = (struct pl010_struct *)cdev->dev->map_base;
+ unsigned int data;
+
+ /* Wait until there is data in the FIFO */
+ while (readl(&pl010->flag) & UART_PL010_FR_RXFE)
+ ; /* noop */
+
+ data = readl(&pl010->data);
+
+ /* Check for an error flag */
+ if (data & 0xFFFFFF00) {
+ /* Clear the error */
+ writel(0xFFFFFFFF, &pl010->errclr);
+ return -1;
+ }
+
+ return (int)data;
+}
+
+static int pl010_tstc(struct console_device *cdev)
+{
+ struct pl010_struct *pl010 = (struct pl010_struct *)cdev->dev->map_base;
+
+ return !(readl(&pl010->flag) & UART_PL010_FR_RXFE);
+}
+
+static int pl010_probe(struct device_d *dev)
+{
+ struct console_device *cdev;
+
+ cdev = malloc(sizeof(struct console_device));
+ dev->type_data = cdev;
+ cdev->dev = dev;
+ cdev->f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR;
+ cdev->tstc = pl010_tstc;
+ cdev->putc = pl010_putc;
+ cdev->getc = pl010_getc;
+ cdev->setbrg = pl010_setbaudrate;
+
+ pl010_init_port(cdev);
+
+ console_register(cdev);
+
+ return 0;
+}
+
+static struct driver_d pl010_driver = {
+ .name = "pl010_serial",
+ .probe = pl010_probe,
+};
+
+static int pl010_init(void)
+{
+ register_driver(&pl010_driver);
+
+ return 0;
+}
+
+console_initcall(pl010_init);
diff --git a/drivers/serial/serial_pl010.h b/drivers/serial/serial_pl010.h
new file mode 100644
index 0000000000..6124e0e0f7
--- /dev/null
+++ b/drivers/serial/serial_pl010.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 Matthias Kaehlcke <matthias@kaehlcke.net>
+ *
+ * (C) Copyright 2003, 2004
+ * ARM Ltd.
+ * Philippe Robin, <philippe.robin@arm.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
+ */
+
+struct hldc_struct {
+ uint32_t ctrl;
+ uint32_t addmtchval;
+ uint32_t addmask;
+ uint32_t rxinfobuf;
+ uint32_t sts;
+};
+
+struct pl010_struct {
+ uint32_t data;
+ union {
+ uint32_t rxsts;
+ uint32_t errclr;
+ };
+ uint32_t linctrlhigh;
+ uint32_t linctrlmid;
+ uint32_t linctrllow;
+ uint32_t ctrl;
+ uint32_t flag;
+ union {
+ uint32_t intid;
+ uint32_t intclr;
+ };
+ uint32_t not_used0[2];
+ uint32_t dmactrl;
+ uint32_t not_used1[53];
+ uint32_t modemctrl;
+ uint32_t modemsts;
+ uint32_t not_used2[65];
+ struct hldc_struct hldc;
+};
+
+#define UART_PL010_RSR_OE (1 << 3)
+#define UART_PL010_RSR_BE (1 << 2)
+#define UART_PL010_RSR_PE (1 << 1)
+#define UART_PL010_RSR_FE (1 << 0)
+
+#define UART_PL010_FR_TXFE (1 << 7)
+#define UART_PL010_FR_RXFF (1 << 6)
+#define UART_PL010_FR_TXFF (1 << 5)
+#define UART_PL010_FR_RXFE (1 << 4)
+#define UART_PL010_FR_BUSY (1 << 3)
+#define UART_PL010_FR_TMSK (UART_PL010_FR_TXFF + UART_PL010_FR_BUSY)
+
+#define UART_PL010_CR_LPE (1 << 7)
+#define UART_PL010_CR_RTIE (1 << 6)
+#define UART_PL010_CR_TIE (1 << 5)
+#define UART_PL010_CR_RIE (1 << 4)
+#define UART_PL010_CR_MSIE (1 << 3)
+#define UART_PL010_CR_IIRLP (1 << 2)
+#define UART_PL010_CR_SIREN (1 << 1)
+#define UART_PL010_CR_UARTEN (1 << 0)
+
+#define UART_PL010_LCRH_WLEN_8 (3 << 5)
+#define UART_PL010_LCRH_WLEN_7 (2 << 5)
+#define UART_PL010_LCRH_WLEN_6 (1 << 5)
+#define UART_PL010_LCRH_WLEN_5 (0 << 5)
+#define UART_PL010_LCRH_FEN (1 << 4)
+#define UART_PL010_LCRH_STP2 (1 << 3)
+#define UART_PL010_LCRH_EPS (1 << 2)
+#define UART_PL010_LCRH_PEN (1 << 1)
+#define UART_PL010_LCRH_BRK (1 << 0)
+
+#define UART_PL010_BAUD_460800 1
+#define UART_PL010_BAUD_230400 3
+#define UART_PL010_BAUD_115200 7
+#define UART_PL010_BAUD_57600 15
+#define UART_PL010_BAUD_38400 23
+#define UART_PL010_BAUD_19200 47
+#define UART_PL010_BAUD_14400 63
+#define UART_PL010_BAUD_9600 95
+#define UART_PL010_BAUD_4800 191
+#define UART_PL010_BAUD_2400 383
+#define UART_PL010_BAUD_1200 767