diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2010-02-01 16:16:12 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2010-02-01 16:16:12 +0100 |
commit | 7ca411ecd2dc1c6bc47caad4b1e25a118bd7441b (patch) | |
tree | 716f34c98b8e9a1535a9dc42605644d0793f5161 /drivers | |
parent | dc6550ed3be2c47476ccaefdee2277b9360a8ed6 (diff) | |
parent | 976b4be6021569e9808cf83ab7600231d1315307 (diff) | |
download | barebox-7ca411ecd2dc1c6bc47caad4b1e25a118bd7441b.tar.gz barebox-7ca411ecd2dc1c6bc47caad4b1e25a118bd7441b.tar.xz |
Merge branch 'next'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/Kconfig | 1 | ||||
-rw-r--r-- | drivers/Makefile | 1 | ||||
-rw-r--r-- | drivers/ata/Kconfig | 26 | ||||
-rw-r--r-- | drivers/ata/Makefile | 7 | ||||
-rw-r--r-- | drivers/ata/bios.c | 291 | ||||
-rw-r--r-- | drivers/ata/disk_drive.c | 346 | ||||
-rw-r--r-- | drivers/i2c/Kconfig | 3 | ||||
-rw-r--r-- | drivers/i2c/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/lp3972.c | 110 | ||||
-rw-r--r-- | drivers/i2c/mc13892.c | 91 | ||||
-rw-r--r-- | drivers/i2c/mc9sdz60.c | 84 | ||||
-rw-r--r-- | drivers/net/Kconfig | 5 | ||||
-rw-r--r-- | drivers/net/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/ep93xx.c | 676 | ||||
-rw-r--r-- | drivers/net/ep93xx.h | 147 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 7 | ||||
-rw-r--r-- | drivers/serial/Makefile | 2 | ||||
-rw-r--r-- | drivers/serial/serial_pl010.c | 172 | ||||
-rw-r--r-- | drivers/serial/serial_pl010.h | 100 |
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*)§or[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*)§or[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(®s->selfctl); + value |= SELFCTL_RESET; + writel(value, ®s->selfctl); + + while (readl(®s->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, ®s->txdq.badd); + writel((uint32_t)priv->tx_dq.base, ®s->txdq.curadd); + writel(sizeof(struct tx_descriptor) * NUMTXDESC, ®s->txdq.blen); + + writel((uint32_t)priv->tx_sq.base, ®s->txstsq.badd); + writel((uint32_t)priv->tx_sq.base, ®s->txstsq.curadd); + writel(sizeof(struct tx_status) * NUMTXDESC, ®s->txstsq.blen); + + writel(0x00040000, ®s->txdthrshld); + writel(0x00040000, ®s->txststhrshld); + + writel((TXSTARTMAX << 0) | (PKTSIZE_ALIGN << 16), ®s->maxfrmlen); + writel(BMCTL_TXEN, ®s->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, ®s->rxdq.badd); + writel((uint32_t)priv->rx_dq.base, ®s->rxdq.curadd); + writel(sizeof(struct rx_descriptor) * NUMRXDESC, ®s->rxdq.blen); + + writel((uint32_t)priv->rx_sq.base, ®s->rxstsq.badd); + writel((uint32_t)priv->rx_sq.base, ®s->rxstsq.curadd); + writel(sizeof(struct rx_status) * NUMRXDESC, ®s->rxstsq.blen); + + writel(0x00040000, ®s->rxdthrshld); + + writel(BMCTL_RXEN, ®s->bmctl); + + writel(0x00040000, ®s->rxststhrshld); + + /* Wait until the receive descriptor processor is active */ + while (!(readl(®s->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, ®s->rxdqenq); + writel(NUMRXDESC, ®s->rxstsqenq); + + /* Turn on RX and TX */ + writel(RXCTL_IA0 | RXCTL_BA | RXCTL_SRXON | + RXCTL_RCRCA | RXCTL_MA, ®s->rxctl); + writel(TXCTL_STXON, ®s->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, ®s->rxctl); + writel(0x00000000, ®s->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(®s->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(®s->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, ®s->afp); + + writel(mac_addr[0] | (mac_addr[1] << 8) | + (mac_addr[2] << 16) | (mac_addr[3] << 24), + ®s->indad); + writel(mac_addr[4] | (mac_addr[5] << 8), ®s->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, ®s->rxdqenq); + writel(1, ®s->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, ®s->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(®s->selfctl); +#if defined(CONFIG_MII_SUPPRESS_PREAMBLE) /* TODO */ + writel(self_ctl & ~(1 << 8), ®s->selfctl); +#endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */ + + while (readl(®s->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, ®s->miicmd); + while (readl(®s->miists) & MIISTS_BUSY) + ; /* noop */ + + *value = (unsigned short)readl(®s->miidata); + + /* Restore the saved SelfCTL value and return. */ + writel(self_ctl, ®s->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(®s->selfctl); +#if defined(CONFIG_MII_SUPPRESS_PREAMBLE) /* TODO */ + writel(self_ctl & ~(1 << 8), ®s->selfctl); +#endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */ + + while (readl(®s->miists) & MIISTS_BUSY) + ; /* noop */ + + /* Issue the MII 'write' command. Wait for the command to complete. */ + writel((uint32_t)value, ®s->miidata); + writel(MIICMD_OPCODE_WRITE | ((uint32_t)phy_addr << 5) | phy_reg, + ®s->miicmd); + while (readl(®s->miists) & MIISTS_BUSY) + ; /* noop */ + + /* Restore the saved SelfCTL value and return. */ + writel(self_ctl, ®s->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 |