* [PATCH][MIPS][0/6]: AR7 final
@ 2008-03-12 1:21 Matteo Croce
2008-03-12 1:25 ` [PATCH][MIPS][2/6]: AR7 mtd partition map Matteo Croce
` (5 more replies)
0 siblings, 6 replies; 38+ messages in thread
From: Matteo Croce @ 2008-03-12 1:21 UTC (permalink / raw)
To: linux-mips; +Cc: nico, nbd, florian, openwrt-devel, Andrew Morton
I hope this is the last try
Works flawlessy with vanilla 2.6.24
^ permalink raw reply [flat|nested] 38+ messages in thread* [PATCH][MIPS][2/6]: AR7 mtd partition map 2008-03-12 1:21 [PATCH][MIPS][0/6]: AR7 final Matteo Croce @ 2008-03-12 1:25 ` Matteo Croce 2008-03-12 1:26 ` [PATCH][MIPS][3/6]: AR7: VLYNQ bus Matteo Croce ` (4 subsequent siblings) 5 siblings, 0 replies; 38+ messages in thread From: Matteo Croce @ 2008-03-12 1:25 UTC (permalink / raw) To: linux-mips Cc: Felix Fietkau, Eugene Konev, dwmw2, linux-mtd, openwrt-devel, Andrew Morton Signed-off-by: Matteo Croce <technoboy85@gmail.com> Signed-off-by: Felix Fietkau <nbd@openwrt.org> Signed-off-by: Eugene Konev <ejka@imfi.kspu.ru> diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index e850334..eed06d0 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -158,6 +158,12 @@ config MTD_OF_PARTS the partition map from the children of the flash node, as described in Documentation/powerpc/booting-without-of.txt. +config MTD_AR7_PARTS + tristate "TI AR7 partitioning support" + depends on MTD_PARTITIONS + ---help--- + TI AR7 partitioning support + comment "User Modules And Translation Layers" config MTD_CHAR diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 538e33d..4b77335 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o +obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o # 'Users' - code which presents functionality to userspace. diff --git a/drivers/mtd/ar7part.c b/drivers/mtd/ar7part.c new file mode 100644 index 0000000..3d160d4 --- /dev/null +++ b/drivers/mtd/ar7part.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2007 Eugene Konev <ejka@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * TI AR7 flash partition table. + * Based on ar7 map by Felix Fietkau <nbd@openwrt.org> + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/bootmem.h> +#include <linux/magic.h> + +#define AR7_PARTS 4 +#define ROOT_OFFSET 0xe0000 + +#define LOADER_MAGIC1 le32_to_cpu(0xfeedfa42) +#define LOADER_MAGIC2 le32_to_cpu(0xfeed1281) + +struct ar7_bin_rec { + unsigned int checksum; + unsigned int length; + unsigned int address; +}; + +static struct mtd_partition ar7_parts[AR7_PARTS]; + +static int create_mtd_partitions(struct mtd_info *master, + struct mtd_partition **pparts, + unsigned long origin) +{ + struct ar7_bin_rec header; + unsigned int offset, len; + unsigned int pre_size = master->erasesize, post_size = 0; + unsigned int root_offset = ROOT_OFFSET; + + int retries = 10; + + ar7_parts[0].name = "loader"; + ar7_parts[0].offset = 0; + ar7_parts[0].size = master->erasesize; + ar7_parts[0].mask_flags = MTD_WRITEABLE; + + ar7_parts[1].name = "config"; + ar7_parts[1].offset = 0; + ar7_parts[1].size = master->erasesize; + ar7_parts[1].mask_flags = 0; + + do { /* Try 10 blocks starting from master->erasesize */ + offset = pre_size; + master->read(master, offset, + sizeof(header), &len, (u8 *)&header); + if (!strncmp((char *)&header, "TIENV0.8", 8)) + ar7_parts[1].offset = pre_size; + if (header.checksum == LOADER_MAGIC1) + break; + if (header.checksum == LOADER_MAGIC2) + break; + pre_size += master->erasesize; + } while (retries--); + + pre_size = offset; + + if (!ar7_parts[1].offset) { + ar7_parts[1].offset = master->size - master->erasesize; + post_size = master->erasesize; + } + + switch (header.checksum) { + case LOADER_MAGIC1: + while (header.length) { + offset += sizeof(header) + header.length; + master->read(master, offset, sizeof(header), + &len, (u8 *)&header); + } + root_offset = offset + sizeof(header) + 4; + break; + case LOADER_MAGIC2: + while (header.length) { + offset += sizeof(header) + header.length; + master->read(master, offset, sizeof(header), + &len, (u8 *)&header); + } + root_offset = offset + sizeof(header) + 4 + 0xff; + root_offset &= ~(u32)0xff; + break; + default: + printk(KERN_WARNING "Unknown magic: %08x\n", header.checksum); + break; + } + + master->read(master, root_offset, + sizeof(header), &len, (u8 *)&header); + if (header.checksum != SQUASHFS_MAGIC) { + root_offset += master->erasesize - 1; + root_offset &= ~(master->erasesize - 1); + } + + ar7_parts[2].name = "linux"; + ar7_parts[2].offset = pre_size; + ar7_parts[2].size = master->size - pre_size - post_size; + ar7_parts[2].mask_flags = 0; + + ar7_parts[3].name = "rootfs"; + ar7_parts[3].offset = root_offset; + ar7_parts[3].size = master->size - root_offset - post_size; + ar7_parts[3].mask_flags = 0; + + *pparts = ar7_parts; + return AR7_PARTS; +} + +static struct mtd_part_parser ar7_parser = { + .owner = THIS_MODULE, + .parse_fn = create_mtd_partitions, + .name = "ar7part", +}; + +static int __init ar7_parser_init(void) +{ + return register_mtd_parser(&ar7_parser); +} + +module_init(ar7_parser_init); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR( "Felix Fietkau <nbd@openwrt.org>, " + "Eugene Konev <ejka@openwrt.org>"); +MODULE_DESCRIPTION("MTD partitioning for TI AR7"); ^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH][MIPS][3/6]: AR7: VLYNQ bus 2008-03-12 1:21 [PATCH][MIPS][0/6]: AR7 final Matteo Croce 2008-03-12 1:25 ` [PATCH][MIPS][2/6]: AR7 mtd partition map Matteo Croce @ 2008-03-12 1:26 ` Matteo Croce 2008-03-29 9:59 ` Florian Lohoff 2008-03-12 1:28 ` [PATCH][MIPS][4/6]: AR7 gpio Matteo Croce ` (3 subsequent siblings) 5 siblings, 1 reply; 38+ messages in thread From: Matteo Croce @ 2008-03-12 1:26 UTC (permalink / raw) To: linux-mips; +Cc: Eugene Konev, Andrew Morton Signed-off-by: Matteo Croce <technoboy85@gmail.com> Signed-off-by: Eugene Konev <ejka@imfi.kspu.ru> diff --git a/drivers/vlynq/Kconfig b/drivers/vlynq/Kconfig new file mode 100644 index 0000000..2c8ffe0 --- /dev/null +++ b/drivers/vlynq/Kconfig @@ -0,0 +1,13 @@ +menu "TI VLYNQ" + +config VLYNQ + bool "TI VLYNQ bus support" + depends on AR7 && EXPERIMENTAL + help + Support for the TI VLYNQ bus + + The module will be called vlynq + + If unsure, say N + +endmenu diff --git a/drivers/vlynq/Makefile b/drivers/vlynq/Makefile new file mode 100644 index 0000000..b3f6114 --- /dev/null +++ b/drivers/vlynq/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for kernel vlynq drivers +# + +obj-$(CONFIG_VLYNQ) += vlynq.o diff --git a/drivers/vlynq/vlynq.c b/drivers/vlynq/vlynq.c new file mode 100644 index 0000000..374562c --- /dev/null +++ b/drivers/vlynq/vlynq.c @@ -0,0 +1,670 @@ +/* + * Copyright (C) 2006, 2007 OpenWrt.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/init.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/io.h> + +#include <linux/vlynq.h> + +#define VLYNQ_CTRL_PM_ENABLE 0x80000000 +#define VLYNQ_CTRL_CLOCK_INT 0x00008000 +#define VLYNQ_CTRL_CLOCK_DIV(x) (((x) & 7) << 16) +#define VLYNQ_CTRL_INT_LOCAL 0x00004000 +#define VLYNQ_CTRL_INT_ENABLE 0x00002000 +#define VLYNQ_CTRL_INT_VECTOR(x) (((x) & 0x1f) << 8) +#define VLYNQ_CTRL_INT2CFG 0x00000080 +#define VLYNQ_CTRL_RESET 0x00000001 + +#define VLYNQ_INT_OFFSET 0x00000014 +#define VLYNQ_REMOTE_OFFSET 0x00000080 + +#define VLYNQ_STATUS_LINK 0x00000001 +#define VLYNQ_STATUS_LERROR 0x00000080 +#define VLYNQ_STATUS_RERROR 0x00000100 + +#define VINT_ENABLE 0x00000100 +#define VINT_TYPE_EDGE 0x00000080 +#define VINT_LEVEL_LOW 0x00000040 +#define VINT_VECTOR(x) ((x) & 0x1f) +#define VINT_OFFSET(irq) (8 * ((irq) % 4)) + +#define VLYNQ_AUTONEGO_V2 0x00010000 + +struct vlynq_regs { + u32 revision; + u32 control; + u32 status; + u32 int_prio; + u32 int_status; + u32 int_pending; + u32 int_ptr; + u32 tx_offset; + struct vlynq_mapping rx_mapping[4]; + u32 chip; + u32 autonego; + u32 unused[6]; + u32 int_device[8]; +} __attribute__ ((packed)); + +#define vlynq_reg_read(reg) readl(&(reg)) +#define vlynq_reg_write(reg, val) writel(val, &(reg)) + +static int __vlynq_enable_device(struct vlynq_device *dev); + +#ifdef VLYNQ_DEBUG +static void vlynq_dump_regs(struct vlynq_device *dev) +{ + int i; + printk(KERN_DEBUG "VLYNQ local=%p remote=%p\n", + dev->local, dev->remote); + for (i = 0; i < 32; i++) { + printk(KERN_DEBUG "VLYNQ: local %d: %08x\n", + i + 1, ((u32 *)dev->local)[i]); + printk(KERN_DEBUG "VLYNQ: remote %d: %08x\n", + i + 1, ((u32 *)dev->remote)[i]); + } +} + +static void vlynq_dump_mem(u32 *base, int count) +{ + int i; + for (i = 0; i < (count + 3) / 4; i++) { + if (i % 4 == 0) printk(KERN_DEBUG "\nMEM[0x%04x]:", i * 4); + printk(KERN_DEBUG " 0x%08x", *(base + i)); + } + printk(KERN_DEBUG "\n"); +} +#endif + +int vlynq_linked(struct vlynq_device *dev) +{ + int i; + + for (i = 0; i < 100; i++) + if (vlynq_reg_read(dev->local->status) & VLYNQ_STATUS_LINK) + return 1; + else + cpu_relax(); + + return 0; +} + +static void vlynq_irq_unmask(unsigned int irq) +{ + u32 val; + struct vlynq_device *dev = get_irq_chip_data(irq); + int virq; + + BUG_ON(!dev); + virq = irq - dev->irq_start; + val = vlynq_reg_read(dev->remote->int_device[virq >> 2]); + val |= (VINT_ENABLE | virq) << VINT_OFFSET(virq); + vlynq_reg_write(dev->remote->int_device[virq >> 2], val); +} + +static void vlynq_irq_mask(unsigned int irq) +{ + u32 val; + struct vlynq_device *dev = get_irq_chip_data(irq); + int virq; + + BUG_ON(!dev); + virq = irq - dev->irq_start; + val = vlynq_reg_read(dev->remote->int_device[virq >> 2]); + val &= ~(VINT_ENABLE << VINT_OFFSET(virq)); + vlynq_reg_write(dev->remote->int_device[virq >> 2], val); +} + +static int vlynq_irq_type(unsigned int irq, unsigned int flow_type) +{ + u32 val; + struct vlynq_device *dev = get_irq_chip_data(irq); + int virq; + + BUG_ON(!dev); + virq = irq - dev->irq_start; + val = vlynq_reg_read(dev->remote->int_device[virq >> 2]); + switch (flow_type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + val |= VINT_TYPE_EDGE << VINT_OFFSET(virq); + val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq)); + break; + case IRQ_TYPE_LEVEL_HIGH: + val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq)); + val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq)); + break; + case IRQ_TYPE_LEVEL_LOW: + val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq)); + val |= VINT_LEVEL_LOW << VINT_OFFSET(virq); + break; + default: + return -EINVAL; + } + vlynq_reg_write(dev->remote->int_device[virq >> 2], val); + return 0; +} + +static void vlynq_local_ack(unsigned int irq) +{ + struct vlynq_device *dev = get_irq_chip_data(irq); + u32 status = vlynq_reg_read(dev->local->status); + if (printk_ratelimit()) + printk(KERN_DEBUG "%s: local status: 0x%08x\n", + dev->dev.bus_id, status); + vlynq_reg_write(dev->local->status, status); +} + +static void vlynq_remote_ack(unsigned int irq) +{ + struct vlynq_device *dev = get_irq_chip_data(irq); + u32 status = vlynq_reg_read(dev->remote->status); + if (printk_ratelimit()) + printk(KERN_DEBUG "%s: remote status: 0x%08x\n", + dev->dev.bus_id, status); + vlynq_reg_write(dev->remote->status, status); +} + +static irqreturn_t vlynq_irq(int irq, void *dev_id) +{ + struct vlynq_device *dev = dev_id; + u32 status; + int virq = 0; + + status = vlynq_reg_read(dev->local->int_status); + vlynq_reg_write(dev->local->int_status, status); + + if (unlikely(!status)) + spurious_interrupt(); + + while (status) { + if (status & 1) + do_IRQ(dev->irq_start + virq); + status >>= 1; + virq++; + } + + return IRQ_HANDLED; +} + +static struct irq_chip vlynq_irq_chip = { + .name = "vlynq", + .unmask = vlynq_irq_unmask, + .mask = vlynq_irq_mask, + .set_type = vlynq_irq_type, +}; + +static struct irq_chip vlynq_local_chip = { + .name = "vlynq local error", + .unmask = vlynq_irq_unmask, + .mask = vlynq_irq_mask, + .ack = vlynq_local_ack, +}; + +static struct irq_chip vlynq_remote_chip = { + .name = "vlynq local error", + .unmask = vlynq_irq_unmask, + .mask = vlynq_irq_mask, + .ack = vlynq_remote_ack, +}; + +static int vlynq_setup_irq(struct vlynq_device *dev) +{ + u32 val; + int i, virq; + + if (dev->local_irq == dev->remote_irq) { + printk(KERN_ERR + "%s: local vlynq irq should be different from remote\n", + dev->dev.bus_id); + return -EINVAL; + } + + /* Clear local and remote error bits */ + vlynq_reg_write(dev->local->status, vlynq_reg_read(dev->local->status)); + vlynq_reg_write(dev->remote->status, + vlynq_reg_read(dev->remote->status)); + + /* Now setup interrupts */ + val = VLYNQ_CTRL_INT_VECTOR(dev->local_irq); + val |= VLYNQ_CTRL_INT_ENABLE | VLYNQ_CTRL_INT_LOCAL | + VLYNQ_CTRL_INT2CFG; + val |= vlynq_reg_read(dev->local->control); + vlynq_reg_write(dev->local->int_ptr, VLYNQ_INT_OFFSET); + vlynq_reg_write(dev->local->control, val); + + val = VLYNQ_CTRL_INT_VECTOR(dev->remote_irq); + val |= VLYNQ_CTRL_INT_ENABLE; + val |= vlynq_reg_read(dev->remote->control); + vlynq_reg_write(dev->remote->int_ptr, VLYNQ_INT_OFFSET); + vlynq_reg_write(dev->remote->control, val); + + for (i = dev->irq_start; i <= dev->irq_end; i++) { + virq = i - dev->irq_start; + if (virq == dev->local_irq) { + set_irq_chip_and_handler(i, &vlynq_local_chip, + handle_level_irq); + set_irq_chip_data(i, dev); + } else if (virq == dev->remote_irq) { + set_irq_chip_and_handler(i, &vlynq_remote_chip, + handle_level_irq); + set_irq_chip_data(i, dev); + } else { + set_irq_chip_and_handler(i, &vlynq_irq_chip, + handle_simple_irq); + set_irq_chip_data(i, dev); + vlynq_reg_write(dev->remote->int_device[virq >> 2], 0); + } + } + + if (request_irq(dev->irq, vlynq_irq, IRQF_SHARED, "vlynq", dev)) { + printk(KERN_ERR "%s: request_irq failed\n", dev->dev.bus_id); + return -EAGAIN; + } + + return 0; +} + +static void vlynq_device_release(struct device *dev) +{ + struct vlynq_device *vdev = to_vlynq_device(dev); + kfree(vdev); +} + +static int vlynq_device_match(struct device *dev, + struct device_driver *drv) +{ + struct vlynq_device *vdev = to_vlynq_device(dev); + struct vlynq_driver *vdrv = to_vlynq_driver(drv); + struct plat_vlynq_ops *ops = dev->platform_data; + struct vlynq_device_id *ids = vdrv->id_table; + u32 id = 0; + int result; + + while (ids->id) { + vdev->divisor = ids->divisor; + result = __vlynq_enable_device(vdev); + if (result == 0) { + id = vlynq_reg_read(vdev->remote->chip); + ops->off(vdev); + if (ids->id == id) { + vlynq_set_drvdata(vdev, ids); + return 1; + } + } + ids++; + } + return 0; +} + +static int vlynq_device_probe(struct device *dev) +{ + struct vlynq_device *vdev = to_vlynq_device(dev); + struct vlynq_driver *drv = to_vlynq_driver(dev->driver); + struct vlynq_device_id *id = vlynq_get_drvdata(vdev); + int result = -ENODEV; + + get_device(dev); + if (drv && drv->probe) + result = drv->probe(vdev, id); + if (result) + put_device(dev); + return result; +} + +static int vlynq_device_remove(struct device *dev) +{ + struct vlynq_driver *drv = to_vlynq_driver(dev->driver); + if (drv && drv->remove) + drv->remove(to_vlynq_device(dev)); + put_device(dev); + return 0; +} + +int __vlynq_register_driver(struct vlynq_driver *driver, struct module *owner) +{ + driver->driver.name = driver->name; + driver->driver.bus = &vlynq_bus_type; + return driver_register(&driver->driver); +} +EXPORT_SYMBOL(__vlynq_register_driver); + +void vlynq_unregister_driver(struct vlynq_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL(vlynq_unregister_driver); + +static int __vlynq_enable_device(struct vlynq_device *dev) +{ + int i, result; + struct plat_vlynq_ops *ops = dev->dev.platform_data; + + result = ops->on(dev); + if (result) + return result; + + switch (dev->divisor) { + case vlynq_div_auto: + /* Only try locally supplied clock, others cause problems */ + vlynq_reg_write(dev->remote->control, 0); + for (i = vlynq_ldiv2; i <= vlynq_ldiv8; i++) { + vlynq_reg_write(dev->local->control, + VLYNQ_CTRL_CLOCK_INT | + VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1)); + if (vlynq_linked(dev)) { + printk(KERN_DEBUG + "%s: using local clock divisor %d\n", + dev->dev.bus_id, i - vlynq_ldiv1 + 1); + dev->divisor = i; + return 0; + } + } + case vlynq_ldiv1: case vlynq_ldiv2: case vlynq_ldiv3: case vlynq_ldiv4: + case vlynq_ldiv5: case vlynq_ldiv6: case vlynq_ldiv7: case vlynq_ldiv8: + vlynq_reg_write(dev->remote->control, 0); + vlynq_reg_write(dev->local->control, + VLYNQ_CTRL_CLOCK_INT | + VLYNQ_CTRL_CLOCK_DIV(dev->divisor - + vlynq_ldiv1)); + if (vlynq_linked(dev)) { + printk(KERN_DEBUG + "%s: using local clock divisor %d\n", + dev->dev.bus_id, dev->divisor - vlynq_ldiv1 + 1); + return 0; + } + break; + case vlynq_rdiv1: case vlynq_rdiv2: case vlynq_rdiv3: case vlynq_rdiv4: + case vlynq_rdiv5: case vlynq_rdiv6: case vlynq_rdiv7: case vlynq_rdiv8: + vlynq_reg_write(dev->local->control, 0); + vlynq_reg_write(dev->remote->control, + VLYNQ_CTRL_CLOCK_INT | + VLYNQ_CTRL_CLOCK_DIV(dev->divisor - + vlynq_rdiv1)); + if (vlynq_linked(dev)) { + printk(KERN_DEBUG + "%s: using remote clock divisor %d\n", + dev->dev.bus_id, dev->divisor - vlynq_rdiv1 + 1); + return 0; + } + break; + case vlynq_div_external: + vlynq_reg_write(dev->local->control, 0); + vlynq_reg_write(dev->remote->control, 0); + if (vlynq_linked(dev)) { + printk(KERN_DEBUG "%s: using external clock\n", + dev->dev.bus_id); + return 0; + } + break; + } + + ops->off(dev); + return -ENODEV; +} + +int vlynq_enable_device(struct vlynq_device *dev) +{ + struct plat_vlynq_ops *ops = dev->dev.platform_data; + int result = -ENODEV; + + result = __vlynq_enable_device(dev); + if (result) + return result; + + result = vlynq_setup_irq(dev); + if (result) + ops->off(dev); + + dev->enabled = !result; + return result; +} +EXPORT_SYMBOL(vlynq_enable_device); + + +void vlynq_disable_device(struct vlynq_device *dev) +{ + struct plat_vlynq_ops *ops = dev->dev.platform_data; + + dev->enabled = 0; + free_irq(dev->irq, dev); + ops->off(dev); +} +EXPORT_SYMBOL(vlynq_disable_device); + +int vlynq_set_local_mapping(struct vlynq_device *dev, u32 tx_offset, + struct vlynq_mapping *mapping) +{ + int i; + + if (!dev->enabled) + return -ENXIO; + + vlynq_reg_write(dev->local->tx_offset, tx_offset); + for (i = 0; i < 4; i++) { + vlynq_reg_write(dev->local->rx_mapping[i].offset, + mapping[i].offset); + vlynq_reg_write(dev->local->rx_mapping[i].size, + mapping[i].size); + } + return 0; +} +EXPORT_SYMBOL(vlynq_set_local_mapping); + +int vlynq_set_remote_mapping(struct vlynq_device *dev, u32 tx_offset, + struct vlynq_mapping *mapping) +{ + int i; + + if (!dev->enabled) + return -ENXIO; + + vlynq_reg_write(dev->remote->tx_offset, tx_offset); + for (i = 0; i < 4; i++) { + vlynq_reg_write(dev->remote->rx_mapping[i].offset, + mapping[i].offset); + vlynq_reg_write(dev->remote->rx_mapping[i].size, + mapping[i].size); + } + return 0; +} +EXPORT_SYMBOL(vlynq_set_remote_mapping); + +int vlynq_set_local_irq(struct vlynq_device *dev, int virq) +{ + int irq = dev->irq_start + virq; + if (dev->enabled) + return -EBUSY; + + if ((irq < dev->irq_start) || (irq > dev->irq_end)) + return -EINVAL; + + if (virq == dev->remote_irq) + return -EINVAL; + + dev->local_irq = virq; + + return 0; +} +EXPORT_SYMBOL(vlynq_set_local_irq); + +int vlynq_set_remote_irq(struct vlynq_device *dev, int virq) +{ + int irq = dev->irq_start + virq; + if (dev->enabled) + return -EBUSY; + + if ((irq < dev->irq_start) || (irq > dev->irq_end)) + return -EINVAL; + + if (virq == dev->local_irq) + return -EINVAL; + + dev->remote_irq = virq; + + return 0; +} +EXPORT_SYMBOL(vlynq_set_remote_irq); + +static int vlynq_probe(struct platform_device *pdev) +{ + struct vlynq_device *dev; + struct resource *regs_res, *mem_res, *irq_res; + int len, result; + + regs_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + if (!regs_res) + return -ENODEV; + + mem_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem"); + if (!mem_res) + return -ENODEV; + + irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "devirq"); + if (!irq_res) + return -ENODEV; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + printk(KERN_ERR + "vlynq: failed to allocate device structure\n"); + return -ENOMEM; + } + + dev->id = pdev->id; + dev->dev.bus = &vlynq_bus_type; + dev->dev.parent = &pdev->dev; + snprintf(dev->dev.bus_id, BUS_ID_SIZE, "vlynq%d", dev->id); + dev->dev.bus_id[BUS_ID_SIZE - 1] = 0; + dev->dev.platform_data = pdev->dev.platform_data; + dev->dev.release = vlynq_device_release; + + dev->regs_start = regs_res->start; + dev->regs_end = regs_res->end; + dev->mem_start = mem_res->start; + dev->mem_end = mem_res->end; + + len = regs_res->end - regs_res->start; + if (!request_mem_region(regs_res->start, len, dev->dev.bus_id)) { + printk(KERN_ERR "%s: Can't request vlynq registers\n", + dev->dev.bus_id); + result = -ENXIO; + goto fail_request; + } + + dev->local = ioremap(regs_res->start, len); + if (!dev->local) { + printk(KERN_ERR "%s: Can't remap vlynq registers\n", + dev->dev.bus_id); + result = -ENXIO; + goto fail_remap; + } + + dev->remote = (struct vlynq_regs *)((void *)dev->local + + VLYNQ_REMOTE_OFFSET); + + dev->irq = platform_get_irq_byname(pdev, "irq"); + dev->irq_start = irq_res->start; + dev->irq_end = irq_res->end; + dev->local_irq = dev->irq_end - dev->irq_start; + dev->remote_irq = dev->local_irq - 1; + + if (device_register(&dev->dev)) + goto fail_register; + platform_set_drvdata(pdev, dev); + + printk(KERN_INFO "%s: regs 0x%p, irq %d, mem 0x%p\n", + dev->dev.bus_id, (void *)dev->regs_start, dev->irq, + (void *)dev->mem_start); + + return 0; + +fail_register: + iounmap(dev->local); +fail_remap: +fail_request: + release_mem_region(regs_res->start, len); + kfree(dev); + return result; +} + +static int vlynq_remove(struct platform_device *pdev) +{ + struct vlynq_device *dev = platform_get_drvdata(pdev); + + device_unregister(&dev->dev); + iounmap(dev->local); + release_mem_region(dev->regs_start, dev->regs_end - dev->regs_start); + + kfree(dev); + + return 0; +} + +static struct platform_driver vlynq_driver = { + .driver.name = "vlynq", + .probe = vlynq_probe, + .remove = __devexit_p(vlynq_remove), +}; + +struct bus_type vlynq_bus_type = { + .name = "vlynq", + .match = vlynq_device_match, + .probe = vlynq_device_probe, + .remove = vlynq_device_remove, +}; +EXPORT_SYMBOL(vlynq_bus_type); + +static int __devinit vlynq_init(void) +{ + int res = 0; + + res = bus_register(&vlynq_bus_type); + if (res) + goto fail_bus; + + res = platform_driver_register(&vlynq_driver); + if (res) + goto fail_platform; + + return 0; + +fail_platform: + bus_unregister(&vlynq_bus_type); +fail_bus: + return res; +} + +static void __devexit vlynq_exit(void) +{ + platform_driver_unregister(&vlynq_driver); + bus_unregister(&vlynq_bus_type); +} + +module_init(vlynq_init); +module_exit(vlynq_exit); diff --git a/include/linux/vlynq.h b/include/linux/vlynq.h new file mode 100644 index 0000000..b3f2474 --- /dev/null +++ b/include/linux/vlynq.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2006, 2007 OpenWrt.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __VLYNQ_H__ +#define __VLYNQ_H__ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/types.h> + +#define VLYNQ_NUM_IRQS 32 + +struct vlynq_mapping { + u32 size; + u32 offset; +}; + +enum vlynq_divisor { + vlynq_div_auto = 0, + vlynq_ldiv1, + vlynq_ldiv2, + vlynq_ldiv3, + vlynq_ldiv4, + vlynq_ldiv5, + vlynq_ldiv6, + vlynq_ldiv7, + vlynq_ldiv8, + vlynq_rdiv1, + vlynq_rdiv2, + vlynq_rdiv3, + vlynq_rdiv4, + vlynq_rdiv5, + vlynq_rdiv6, + vlynq_rdiv7, + vlynq_rdiv8, + vlynq_div_external +}; + +struct vlynq_device_id { + u32 id; + enum vlynq_divisor divisor; + unsigned long driver_data; +}; + +struct vlynq_regs; +struct vlynq_device { + u32 id; + int local_irq; + int remote_irq; + enum vlynq_divisor divisor; + u32 regs_start, regs_end; + u32 mem_start, mem_end; + u32 irq_start, irq_end; + int irq; + int enabled; + struct vlynq_regs *local; + struct vlynq_regs *remote; + struct device dev; +}; + +struct vlynq_driver { + char *name; + struct vlynq_device_id *id_table; + int (*probe)(struct vlynq_device *dev, struct vlynq_device_id *id); + void (*remove)(struct vlynq_device *dev); + struct device_driver driver; +}; + +struct plat_vlynq_ops { + int (*on)(struct vlynq_device *dev); + void (*off)(struct vlynq_device *dev); +}; + +static inline struct vlynq_driver *to_vlynq_driver(struct device_driver *drv) +{ + return container_of(drv, struct vlynq_driver, driver); +} + +static inline struct vlynq_device *to_vlynq_device(struct device *device) +{ + return container_of(device, struct vlynq_device, dev); +} + +extern struct bus_type vlynq_bus_type; + +extern int __vlynq_register_driver(struct vlynq_driver *driver, + struct module *owner); + +static inline int vlynq_register_driver(struct vlynq_driver *driver) +{ + return __vlynq_register_driver(driver, THIS_MODULE); +} + +static inline void *vlynq_get_drvdata(struct vlynq_device *dev) +{ + return dev_get_drvdata(&dev->dev); +} + +static inline void vlynq_set_drvdata(struct vlynq_device *dev, void *data) +{ + dev_set_drvdata(&dev->dev, data); +} + +static inline u32 vlynq_mem_start(struct vlynq_device *dev) +{ + return dev->mem_start; +} + +static inline u32 vlynq_mem_end(struct vlynq_device *dev) +{ + return dev->mem_end; +} + +static inline u32 vlynq_mem_len(struct vlynq_device *dev) +{ + return dev->mem_end - dev->mem_start + 1; +} + +static inline int vlynq_virq_to_irq(struct vlynq_device *dev, int virq) +{ + int irq = dev->irq_start + virq; + if ((irq < dev->irq_start) || (irq > dev->irq_end)) + return -EINVAL; + + return irq; +} + +static inline int vlynq_irq_to_virq(struct vlynq_device *dev, int irq) +{ + if ((irq < dev->irq_start) || (irq > dev->irq_end)) + return -EINVAL; + + return irq - dev->irq_start; +} + +extern void vlynq_unregister_driver(struct vlynq_driver *driver); +extern int vlynq_enable_device(struct vlynq_device *dev); +extern void vlynq_disable_device(struct vlynq_device *dev); +extern int vlynq_set_local_mapping(struct vlynq_device *dev, u32 tx_offset, + struct vlynq_mapping *mapping); +extern int vlynq_set_remote_mapping(struct vlynq_device *dev, u32 tx_offset, + struct vlynq_mapping *mapping); +extern int vlynq_set_local_irq(struct vlynq_device *dev, int virq); +extern int vlynq_set_remote_irq(struct vlynq_device *dev, int virq); + +#endif /* __VLYNQ_H__ */ ^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][3/6]: AR7: VLYNQ bus 2008-03-12 1:26 ` [PATCH][MIPS][3/6]: AR7: VLYNQ bus Matteo Croce @ 2008-03-29 9:59 ` Florian Lohoff 2008-04-02 12:56 ` Matteo Croce ` (2 more replies) 0 siblings, 3 replies; 38+ messages in thread From: Florian Lohoff @ 2008-03-29 9:59 UTC (permalink / raw) To: Matteo Croce; +Cc: linux-mips, Eugene Konev, Andrew Morton [-- Attachment #1: Type: text/plain, Size: 1687 bytes --] Hi Matteo, On Wed, Mar 12, 2008 at 02:26:42AM +0100, Matteo Croce wrote: > + switch (dev->divisor) { > + case vlynq_div_auto: > + /* Only try locally supplied clock, others cause problems */ i have a platform (AR7VWi - Leonardo Board) which has an external supplied clock from an ACX111 so the div_auto autoprobing will not work. I put the vlynq_div_external code in front of this which should simply listen on the vlynq if there is a clock and use it. This solved one of the issues for me. Can you elaborate on the above comment where caused problems? I have seen multiple implementations of this, all of them did autoprobing first by listening on the remote clock first. > + vlynq_reg_write(dev->remote->control, 0); > + for (i = vlynq_ldiv2; i <= vlynq_ldiv8; i++) { > + vlynq_reg_write(dev->local->control, > + VLYNQ_CTRL_CLOCK_INT | > + VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1)); > + if (vlynq_linked(dev)) { > + printk(KERN_DEBUG > + "%s: using local clock divisor %d\n", > + dev->dev.bus_id, i - vlynq_ldiv1 + 1); > + dev->divisor = i; > + return 0; > + } > + } What i found in the TI code is that FIRST the local->control needs to get set before issueing a remote access so shouldnd the vlynq_reg_write(dev->remote ... ) move behind the dev->local ? I mean the logic is to set a local clock divisor - try to access something on the remote end and see if the link got up ?!? Flo -- Florian Lohoff flo@rfc822.org +49-171-2280134 Those who would give up a little freedom to get a little security shall soon have neither - Benjamin Franklin [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 189 bytes --] ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][3/6]: AR7: VLYNQ bus 2008-03-29 9:59 ` Florian Lohoff @ 2008-04-02 12:56 ` Matteo Croce 2008-04-02 18:31 ` Florian Lohoff 2008-04-02 13:57 ` Matteo Croce 2008-04-02 14:58 ` Matteo Croce 2 siblings, 1 reply; 38+ messages in thread From: Matteo Croce @ 2008-04-02 12:56 UTC (permalink / raw) To: Florian Lohoff; +Cc: linux-mips, Eugene Konev, Andrew Morton Il Saturday 29 March 2008 10:59:14 Florian Lohoff ha scritto: > > Hi Matteo, > > On Wed, Mar 12, 2008 at 02:26:42AM +0100, Matteo Croce wrote: > > + switch (dev->divisor) { > > + case vlynq_div_auto: > > + /* Only try locally supplied clock, others cause problems */ > > i have a platform (AR7VWi - Leonardo Board) which has an external > supplied clock from an ACX111 so the div_auto autoprobing will not work. > > I put the vlynq_div_external code in front of this which should > simply listen on the vlynq if there is a clock and use it. This solved > one of the issues for me. Can you elaborate on the above comment where > caused problems? I have seen multiple implementations of this, all of > them did autoprobing first by listening on the remote clock first. > > > + vlynq_reg_write(dev->remote->control, 0); > > + for (i = vlynq_ldiv2; i <= vlynq_ldiv8; i++) { > > + vlynq_reg_write(dev->local->control, > > + VLYNQ_CTRL_CLOCK_INT | > > + VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1)); > > + if (vlynq_linked(dev)) { > > + printk(KERN_DEBUG > > + "%s: using local clock divisor %d\n", > > + dev->dev.bus_id, i - vlynq_ldiv1 + 1); > > + dev->divisor = i; > > + return 0; > > + } > > + } > Il Saturday 29 March 2008 10:59:14 hai scritto: > > Hi Matteo, > > On Wed, Mar 12, 2008 at 02:26:42AM +0100, Matteo Croce wrote: > > + switch (dev->divisor) { > > + case vlynq_div_auto: > > + /* Only try locally supplied clock, others cause problems */ > > i have a platform (AR7VWi - Leonardo Board) which has an external > supplied clock from an ACX111 so the div_auto autoprobing will not work. > > I put the vlynq_div_external code in front of this which should > simply listen on the vlynq if there is a clock and use it. This solved > one of the issues for me. Can you elaborate on the above comment where > caused problems? I have seen multiple implementations of this, all of > them did autoprobing first by listening on the remote clock first. > > > + vlynq_reg_write(dev->remote->control, 0); > > + for (i = vlynq_ldiv2; i <= vlynq_ldiv8; i++) { > > + vlynq_reg_write(dev->local->control, > > + VLYNQ_CTRL_CLOCK_INT | > > + VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1)); > > + if (vlynq_linked(dev)) { > > + printk(KERN_DEBUG > > + "%s: using local clock divisor %d\n", > > + dev->dev.bus_id, i - vlynq_ldiv1 + 1); > > + dev->divisor = i; > > + return 0; > > + } > > + } Works fine for my AR7 which has an interlan clock. Signed-off-by: Matteo Croce <technoboy85@gmail.com> Signed-off-by: Eugene Konev <ejka@imfi.kspu.ru> diff --git a/drivers/vlynq/Kconfig b/drivers/vlynq/Kconfig new file mode 100644 index 0000000..2c8ffe0 --- /dev/null +++ b/drivers/vlynq/Kconfig @@ -0,0 +1,13 @@ +menu "TI VLYNQ" + +config VLYNQ + bool "TI VLYNQ bus support" + depends on AR7 && EXPERIMENTAL + help + Support for the TI VLYNQ bus + + The module will be called vlynq + + If unsure, say N + +endmenu diff --git a/drivers/vlynq/Makefile b/drivers/vlynq/Makefile new file mode 100644 index 0000000..b3f6114 --- /dev/null +++ b/drivers/vlynq/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for kernel vlynq drivers +# + +obj-$(CONFIG_VLYNQ) += vlynq.o diff --git a/drivers/vlynq/vlynq.c b/drivers/vlynq/vlynq.c new file mode 100644 index 0000000..61e15ec --- /dev/null +++ b/drivers/vlynq/vlynq.c @@ -0,0 +1,669 @@ +/* + * Copyright (C) 2006, 2007 Eugene Konev <ejka@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/init.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/io.h> + +#include <linux/vlynq.h> + +#define VLYNQ_CTRL_PM_ENABLE 0x80000000 +#define VLYNQ_CTRL_CLOCK_INT 0x00008000 +#define VLYNQ_CTRL_CLOCK_DIV(x) (((x) & 7) << 16) +#define VLYNQ_CTRL_INT_LOCAL 0x00004000 +#define VLYNQ_CTRL_INT_ENABLE 0x00002000 +#define VLYNQ_CTRL_INT_VECTOR(x) (((x) & 0x1f) << 8) +#define VLYNQ_CTRL_INT2CFG 0x00000080 +#define VLYNQ_CTRL_RESET 0x00000001 + +#define VLYNQ_INT_OFFSET 0x00000014 +#define VLYNQ_REMOTE_OFFSET 0x00000080 + +#define VLYNQ_STATUS_LINK 0x00000001 +#define VLYNQ_STATUS_LERROR 0x00000080 +#define VLYNQ_STATUS_RERROR 0x00000100 + +#define VINT_ENABLE 0x00000100 +#define VINT_TYPE_EDGE 0x00000080 +#define VINT_LEVEL_LOW 0x00000040 +#define VINT_VECTOR(x) ((x) & 0x1f) +#define VINT_OFFSET(irq) (8 * ((irq) % 4)) + +#define VLYNQ_AUTONEGO_V2 0x00010000 + +struct vlynq_regs { + u32 revision; + u32 control; + u32 status; + u32 int_prio; + u32 int_status; + u32 int_pending; + u32 int_ptr; + u32 tx_offset; + struct vlynq_mapping rx_mapping[4]; + u32 chip; + u32 autonego; + u32 unused[6]; + u32 int_device[8]; +} __attribute__ ((packed)); + +#define vlynq_reg_read(reg) readl(&(reg)) +#define vlynq_reg_write(reg, val) writel(val, &(reg)) + +static int __vlynq_enable_device(struct vlynq_device *dev); + +#ifdef VLYNQ_DEBUG +static void vlynq_dump_regs(struct vlynq_device *dev) +{ + int i; + printk(KERN_DEBUG "VLYNQ local=%p remote=%p\n", + dev->local, dev->remote); + for (i = 0; i < 32; i++) { + printk(KERN_DEBUG "VLYNQ: local %d: %08x\n", + i + 1, ((u32 *)dev->local)[i]); + printk(KERN_DEBUG "VLYNQ: remote %d: %08x\n", + i + 1, ((u32 *)dev->remote)[i]); + } +} + +static void vlynq_dump_mem(u32 *base, int count) +{ + int i; + for (i = 0; i < (count + 3) / 4; i++) { + if (i % 4 == 0) printk(KERN_DEBUG "\nMEM[0x%04x]:", i * 4); + printk(KERN_DEBUG " 0x%08x", *(base + i)); + } + printk(KERN_DEBUG "\n"); +} +#endif + +int vlynq_linked(struct vlynq_device *dev) +{ + int i; + + for (i = 0; i < 100; i++) + if (vlynq_reg_read(dev->local->status) & VLYNQ_STATUS_LINK) + return 1; + else + cpu_relax(); + + return 0; +} + +static void vlynq_irq_unmask(unsigned int irq) +{ + u32 val; + struct vlynq_device *dev = get_irq_chip_data(irq); + int virq; + + BUG_ON(!dev); + virq = irq - dev->irq_start; + val = vlynq_reg_read(dev->remote->int_device[virq >> 2]); + val |= (VINT_ENABLE | virq) << VINT_OFFSET(virq); + vlynq_reg_write(dev->remote->int_device[virq >> 2], val); +} + +static void vlynq_irq_mask(unsigned int irq) +{ + u32 val; + struct vlynq_device *dev = get_irq_chip_data(irq); + int virq; + + BUG_ON(!dev); + virq = irq - dev->irq_start; + val = vlynq_reg_read(dev->remote->int_device[virq >> 2]); + val &= ~(VINT_ENABLE << VINT_OFFSET(virq)); + vlynq_reg_write(dev->remote->int_device[virq >> 2], val); +} + +static int vlynq_irq_type(unsigned int irq, unsigned int flow_type) +{ + u32 val; + struct vlynq_device *dev = get_irq_chip_data(irq); + int virq; + + BUG_ON(!dev); + virq = irq - dev->irq_start; + val = vlynq_reg_read(dev->remote->int_device[virq >> 2]); + switch (flow_type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + val |= VINT_TYPE_EDGE << VINT_OFFSET(virq); + val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq)); + break; + case IRQ_TYPE_LEVEL_HIGH: + val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq)); + val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq)); + break; + case IRQ_TYPE_LEVEL_LOW: + val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq)); + val |= VINT_LEVEL_LOW << VINT_OFFSET(virq); + break; + default: + return -EINVAL; + } + vlynq_reg_write(dev->remote->int_device[virq >> 2], val); + return 0; +} + +static void vlynq_local_ack(unsigned int irq) +{ + struct vlynq_device *dev = get_irq_chip_data(irq); + u32 status = vlynq_reg_read(dev->local->status); + if (printk_ratelimit()) + printk(KERN_DEBUG "%s: local status: 0x%08x\n", + dev->dev.bus_id, status); + vlynq_reg_write(dev->local->status, status); +} + +static void vlynq_remote_ack(unsigned int irq) +{ + struct vlynq_device *dev = get_irq_chip_data(irq); + u32 status = vlynq_reg_read(dev->remote->status); + if (printk_ratelimit()) + printk(KERN_DEBUG "%s: remote status: 0x%08x\n", + dev->dev.bus_id, status); + vlynq_reg_write(dev->remote->status, status); +} + +static irqreturn_t vlynq_irq(int irq, void *dev_id) +{ + struct vlynq_device *dev = dev_id; + u32 status; + int virq = 0; + + status = vlynq_reg_read(dev->local->int_status); + vlynq_reg_write(dev->local->int_status, status); + + if (unlikely(!status)) + spurious_interrupt(); + + while (status) { + if (status & 1) + do_IRQ(dev->irq_start + virq); + status >>= 1; + virq++; + } + + return IRQ_HANDLED; +} + +static struct irq_chip vlynq_irq_chip = { + .name = "vlynq", + .unmask = vlynq_irq_unmask, + .mask = vlynq_irq_mask, + .set_type = vlynq_irq_type, +}; + +static struct irq_chip vlynq_local_chip = { + .name = "vlynq local error", + .unmask = vlynq_irq_unmask, + .mask = vlynq_irq_mask, + .ack = vlynq_local_ack, +}; + +static struct irq_chip vlynq_remote_chip = { + .name = "vlynq local error", + .unmask = vlynq_irq_unmask, + .mask = vlynq_irq_mask, + .ack = vlynq_remote_ack, +}; + +static int vlynq_setup_irq(struct vlynq_device *dev) +{ + u32 val; + int i, virq; + + if (dev->local_irq == dev->remote_irq) { + printk(KERN_ERR + "%s: local vlynq irq should be different from remote\n", + dev->dev.bus_id); + return -EINVAL; + } + + /* Clear local and remote error bits */ + vlynq_reg_write(dev->local->status, vlynq_reg_read(dev->local->status)); + vlynq_reg_write(dev->remote->status, + vlynq_reg_read(dev->remote->status)); + + /* Now setup interrupts */ + val = VLYNQ_CTRL_INT_VECTOR(dev->local_irq); + val |= VLYNQ_CTRL_INT_ENABLE | VLYNQ_CTRL_INT_LOCAL | + VLYNQ_CTRL_INT2CFG; + val |= vlynq_reg_read(dev->local->control); + vlynq_reg_write(dev->local->int_ptr, VLYNQ_INT_OFFSET); + vlynq_reg_write(dev->local->control, val); + + val = VLYNQ_CTRL_INT_VECTOR(dev->remote_irq); + val |= VLYNQ_CTRL_INT_ENABLE; + val |= vlynq_reg_read(dev->remote->control); + vlynq_reg_write(dev->remote->int_ptr, VLYNQ_INT_OFFSET); + vlynq_reg_write(dev->remote->control, val); + + for (i = dev->irq_start; i <= dev->irq_end; i++) { + virq = i - dev->irq_start; + if (virq == dev->local_irq) { + set_irq_chip_and_handler(i, &vlynq_local_chip, + handle_level_irq); + set_irq_chip_data(i, dev); + } else if (virq == dev->remote_irq) { + set_irq_chip_and_handler(i, &vlynq_remote_chip, + handle_level_irq); + set_irq_chip_data(i, dev); + } else { + set_irq_chip_and_handler(i, &vlynq_irq_chip, + handle_simple_irq); + set_irq_chip_data(i, dev); + vlynq_reg_write(dev->remote->int_device[virq >> 2], 0); + } + } + + if (request_irq(dev->irq, vlynq_irq, IRQF_SHARED, "vlynq", dev)) { + printk(KERN_ERR "%s: request_irq failed\n", dev->dev.bus_id); + return -EAGAIN; + } + + return 0; +} + +static void vlynq_device_release(struct device *dev) +{ + struct vlynq_device *vdev = to_vlynq_device(dev); + kfree(vdev); +} + +static int vlynq_device_match(struct device *dev, + struct device_driver *drv) +{ + struct vlynq_device *vdev = to_vlynq_device(dev); + struct vlynq_driver *vdrv = to_vlynq_driver(drv); + struct plat_vlynq_ops *ops = dev->platform_data; + struct vlynq_device_id *ids = vdrv->id_table; + u32 id = 0; + int result; + + while (ids->id) { + vdev->divisor = ids->divisor; + result = __vlynq_enable_device(vdev); + if (result == 0) { + id = vlynq_reg_read(vdev->remote->chip); + ops->off(vdev); + if (ids->id == id) { + vlynq_set_drvdata(vdev, ids); + return 1; + } + } + ids++; + } + return 0; +} + +static int vlynq_device_probe(struct device *dev) +{ + struct vlynq_device *vdev = to_vlynq_device(dev); + struct vlynq_driver *drv = to_vlynq_driver(dev->driver); + struct vlynq_device_id *id = vlynq_get_drvdata(vdev); + int result = -ENODEV; + + get_device(dev); + if (drv && drv->probe) + result = drv->probe(vdev, id); + if (result) + put_device(dev); + return result; +} + +static int vlynq_device_remove(struct device *dev) +{ + struct vlynq_driver *drv = to_vlynq_driver(dev->driver); + if (drv && drv->remove) + drv->remove(to_vlynq_device(dev)); + put_device(dev); + return 0; +} + +int __vlynq_register_driver(struct vlynq_driver *driver, struct module *owner) +{ + driver->driver.name = driver->name; + driver->driver.bus = &vlynq_bus_type; + return driver_register(&driver->driver); +} +EXPORT_SYMBOL(__vlynq_register_driver); + +void vlynq_unregister_driver(struct vlynq_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL(vlynq_unregister_driver); + +static int __vlynq_enable_device(struct vlynq_device *dev) +{ + int i, result; + struct plat_vlynq_ops *ops = dev->dev.platform_data; + + result = ops->on(dev); + if (result) + return result; + + vlynq_reg_write(dev->local->control, 0); + vlynq_reg_write(dev->remote->control, 0); + if (vlynq_linked(dev)) { + printk(KERN_DEBUG "%s: using external clock\n", + dev->dev.bus_id); + return 0; + } + + switch (dev->divisor) { + case vlynq_div_auto: + /* Only try locally supplied clock, others cause problems */ + vlynq_reg_write(dev->remote->control, 0); + for (i = vlynq_ldiv2; i <= vlynq_ldiv8; i++) { + vlynq_reg_write(dev->local->control, + VLYNQ_CTRL_CLOCK_INT | + VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1)); + if (vlynq_linked(dev)) { + printk(KERN_DEBUG + "%s: using local clock divisor %d\n", + dev->dev.bus_id, i - vlynq_ldiv1 + 1); + dev->divisor = i; + return 0; + } + } + case vlynq_ldiv1: case vlynq_ldiv2: case vlynq_ldiv3: case vlynq_ldiv4: + case vlynq_ldiv5: case vlynq_ldiv6: case vlynq_ldiv7: case vlynq_ldiv8: + vlynq_reg_write(dev->remote->control, 0); + vlynq_reg_write(dev->local->control, + VLYNQ_CTRL_CLOCK_INT | + VLYNQ_CTRL_CLOCK_DIV(dev->divisor - + vlynq_ldiv1)); + if (vlynq_linked(dev)) { + printk(KERN_DEBUG + "%s: using local clock divisor %d\n", + dev->dev.bus_id, dev->divisor - vlynq_ldiv1 + 1); + return 0; + } + break; + case vlynq_rdiv1: case vlynq_rdiv2: case vlynq_rdiv3: case vlynq_rdiv4: + case vlynq_rdiv5: case vlynq_rdiv6: case vlynq_rdiv7: case vlynq_rdiv8: + vlynq_reg_write(dev->local->control, 0); + vlynq_reg_write(dev->remote->control, + VLYNQ_CTRL_CLOCK_INT | + VLYNQ_CTRL_CLOCK_DIV(dev->divisor - + vlynq_rdiv1)); + if (vlynq_linked(dev)) { + printk(KERN_DEBUG + "%s: using remote clock divisor %d\n", + dev->dev.bus_id, dev->divisor - vlynq_rdiv1 + 1); + return 0; + } + break; + } + + ops->off(dev); + return -ENODEV; +} + +int vlynq_enable_device(struct vlynq_device *dev) +{ + struct plat_vlynq_ops *ops = dev->dev.platform_data; + int result = -ENODEV; + + result = __vlynq_enable_device(dev); + if (result) + return result; + + result = vlynq_setup_irq(dev); + if (result) + ops->off(dev); + + dev->enabled = !result; + return result; +} +EXPORT_SYMBOL(vlynq_enable_device); + + +void vlynq_disable_device(struct vlynq_device *dev) +{ + struct plat_vlynq_ops *ops = dev->dev.platform_data; + + dev->enabled = 0; + free_irq(dev->irq, dev); + ops->off(dev); +} +EXPORT_SYMBOL(vlynq_disable_device); + +int vlynq_set_local_mapping(struct vlynq_device *dev, u32 tx_offset, + struct vlynq_mapping *mapping) +{ + int i; + + if (!dev->enabled) + return -ENXIO; + + vlynq_reg_write(dev->local->tx_offset, tx_offset); + for (i = 0; i < 4; i++) { + vlynq_reg_write(dev->local->rx_mapping[i].offset, + mapping[i].offset); + vlynq_reg_write(dev->local->rx_mapping[i].size, + mapping[i].size); + } + return 0; +} +EXPORT_SYMBOL(vlynq_set_local_mapping); + +int vlynq_set_remote_mapping(struct vlynq_device *dev, u32 tx_offset, + struct vlynq_mapping *mapping) +{ + int i; + + if (!dev->enabled) + return -ENXIO; + + vlynq_reg_write(dev->remote->tx_offset, tx_offset); + for (i = 0; i < 4; i++) { + vlynq_reg_write(dev->remote->rx_mapping[i].offset, + mapping[i].offset); + vlynq_reg_write(dev->remote->rx_mapping[i].size, + mapping[i].size); + } + return 0; +} +EXPORT_SYMBOL(vlynq_set_remote_mapping); + +int vlynq_set_local_irq(struct vlynq_device *dev, int virq) +{ + int irq = dev->irq_start + virq; + if (dev->enabled) + return -EBUSY; + + if ((irq < dev->irq_start) || (irq > dev->irq_end)) + return -EINVAL; + + if (virq == dev->remote_irq) + return -EINVAL; + + dev->local_irq = virq; + + return 0; +} +EXPORT_SYMBOL(vlynq_set_local_irq); + +int vlynq_set_remote_irq(struct vlynq_device *dev, int virq) +{ + int irq = dev->irq_start + virq; + if (dev->enabled) + return -EBUSY; + + if ((irq < dev->irq_start) || (irq > dev->irq_end)) + return -EINVAL; + + if (virq == dev->local_irq) + return -EINVAL; + + dev->remote_irq = virq; + + return 0; +} +EXPORT_SYMBOL(vlynq_set_remote_irq); + +static int vlynq_probe(struct platform_device *pdev) +{ + struct vlynq_device *dev; + struct resource *regs_res, *mem_res, *irq_res; + int len, result; + + regs_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + if (!regs_res) + return -ENODEV; + + mem_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem"); + if (!mem_res) + return -ENODEV; + + irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "devirq"); + if (!irq_res) + return -ENODEV; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + printk(KERN_ERR + "vlynq: failed to allocate device structure\n"); + return -ENOMEM; + } + + dev->id = pdev->id; + dev->dev.bus = &vlynq_bus_type; + dev->dev.parent = &pdev->dev; + snprintf(dev->dev.bus_id, BUS_ID_SIZE, "vlynq%d", dev->id); + dev->dev.bus_id[BUS_ID_SIZE - 1] = 0; + dev->dev.platform_data = pdev->dev.platform_data; + dev->dev.release = vlynq_device_release; + + dev->regs_start = regs_res->start; + dev->regs_end = regs_res->end; + dev->mem_start = mem_res->start; + dev->mem_end = mem_res->end; + + len = regs_res->end - regs_res->start; + if (!request_mem_region(regs_res->start, len, dev->dev.bus_id)) { + printk(KERN_ERR "%s: Can't request vlynq registers\n", + dev->dev.bus_id); + result = -ENXIO; + goto fail_request; + } + + dev->local = ioremap(regs_res->start, len); + if (!dev->local) { + printk(KERN_ERR "%s: Can't remap vlynq registers\n", + dev->dev.bus_id); + result = -ENXIO; + goto fail_remap; + } + + dev->remote = (struct vlynq_regs *)((void *)dev->local + + VLYNQ_REMOTE_OFFSET); + + dev->irq = platform_get_irq_byname(pdev, "irq"); + dev->irq_start = irq_res->start; + dev->irq_end = irq_res->end; + dev->local_irq = dev->irq_end - dev->irq_start; + dev->remote_irq = dev->local_irq - 1; + + if (device_register(&dev->dev)) + goto fail_register; + platform_set_drvdata(pdev, dev); + + printk(KERN_INFO "%s: regs 0x%p, irq %d, mem 0x%p\n", + dev->dev.bus_id, (void *)dev->regs_start, dev->irq, + (void *)dev->mem_start); + + return 0; + +fail_register: + iounmap(dev->local); +fail_remap: +fail_request: + release_mem_region(regs_res->start, len); + kfree(dev); + return result; +} + +static int vlynq_remove(struct platform_device *pdev) +{ + struct vlynq_device *dev = platform_get_drvdata(pdev); + + device_unregister(&dev->dev); + iounmap(dev->local); + release_mem_region(dev->regs_start, dev->regs_end - dev->regs_start); + + kfree(dev); + + return 0; +} + +static struct platform_driver vlynq_driver = { + .driver.name = "vlynq", + .probe = vlynq_probe, + .remove = __devexit_p(vlynq_remove), +}; + +struct bus_type vlynq_bus_type = { + .name = "vlynq", + .match = vlynq_device_match, + .probe = vlynq_device_probe, + .remove = vlynq_device_remove, +}; +EXPORT_SYMBOL(vlynq_bus_type); + +static int __devinit vlynq_init(void) +{ + int res = 0; + + res = bus_register(&vlynq_bus_type); + if (res) + goto fail_bus; + + res = platform_driver_register(&vlynq_driver); + if (res) + goto fail_platform; + + return 0; + +fail_platform: + bus_unregister(&vlynq_bus_type); +fail_bus: + return res; +} + +static void __devexit vlynq_exit(void) +{ + platform_driver_unregister(&vlynq_driver); + bus_unregister(&vlynq_bus_type); +} + +module_init(vlynq_init); +module_exit(vlynq_exit); diff --git a/include/linux/vlynq.h b/include/linux/vlynq.h new file mode 100644 index 0000000..7658fe4 --- /dev/null +++ b/include/linux/vlynq.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2006, 2007 Eugene Konev <ejka@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __VLYNQ_H__ +#define __VLYNQ_H__ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/types.h> + +#define VLYNQ_NUM_IRQS 32 + +struct vlynq_mapping { + u32 size; + u32 offset; +}; + +enum vlynq_divisor { + vlynq_div_auto = 0, + vlynq_ldiv1, + vlynq_ldiv2, + vlynq_ldiv3, + vlynq_ldiv4, + vlynq_ldiv5, + vlynq_ldiv6, + vlynq_ldiv7, + vlynq_ldiv8, + vlynq_rdiv1, + vlynq_rdiv2, + vlynq_rdiv3, + vlynq_rdiv4, + vlynq_rdiv5, + vlynq_rdiv6, + vlynq_rdiv7, + vlynq_rdiv8, + vlynq_div_external +}; + +struct vlynq_device_id { + u32 id; + enum vlynq_divisor divisor; + unsigned long driver_data; +}; + +struct vlynq_regs; +struct vlynq_device { + u32 id; + int local_irq; + int remote_irq; + enum vlynq_divisor divisor; + u32 regs_start, regs_end; + u32 mem_start, mem_end; + u32 irq_start, irq_end; + int irq; + int enabled; + struct vlynq_regs *local; + struct vlynq_regs *remote; + struct device dev; +}; + +struct vlynq_driver { + char *name; + struct vlynq_device_id *id_table; + int (*probe)(struct vlynq_device *dev, struct vlynq_device_id *id); + void (*remove)(struct vlynq_device *dev); + struct device_driver driver; +}; + +struct plat_vlynq_ops { + int (*on)(struct vlynq_device *dev); + void (*off)(struct vlynq_device *dev); +}; + +static inline struct vlynq_driver *to_vlynq_driver(struct device_driver *drv) +{ + return container_of(drv, struct vlynq_driver, driver); +} + +static inline struct vlynq_device *to_vlynq_device(struct device *device) +{ + return container_of(device, struct vlynq_device, dev); +} + +extern struct bus_type vlynq_bus_type; + +extern int __vlynq_register_driver(struct vlynq_driver *driver, + struct module *owner); + +static inline int vlynq_register_driver(struct vlynq_driver *driver) +{ + return __vlynq_register_driver(driver, THIS_MODULE); +} + +static inline void *vlynq_get_drvdata(struct vlynq_device *dev) +{ + return dev_get_drvdata(&dev->dev); +} + +static inline void vlynq_set_drvdata(struct vlynq_device *dev, void *data) +{ + dev_set_drvdata(&dev->dev, data); +} + +static inline u32 vlynq_mem_start(struct vlynq_device *dev) +{ + return dev->mem_start; +} + +static inline u32 vlynq_mem_end(struct vlynq_device *dev) +{ + return dev->mem_end; +} + +static inline u32 vlynq_mem_len(struct vlynq_device *dev) +{ + return dev->mem_end - dev->mem_start + 1; +} + +static inline int vlynq_virq_to_irq(struct vlynq_device *dev, int virq) +{ + int irq = dev->irq_start + virq; + if ((irq < dev->irq_start) || (irq > dev->irq_end)) + return -EINVAL; + + return irq; +} + +static inline int vlynq_irq_to_virq(struct vlynq_device *dev, int irq) +{ + if ((irq < dev->irq_start) || (irq > dev->irq_end)) + return -EINVAL; + + return irq - dev->irq_start; +} + +extern void vlynq_unregister_driver(struct vlynq_driver *driver); +extern int vlynq_enable_device(struct vlynq_device *dev); +extern void vlynq_disable_device(struct vlynq_device *dev); +extern int vlynq_set_local_mapping(struct vlynq_device *dev, u32 tx_offset, + struct vlynq_mapping *mapping); +extern int vlynq_set_remote_mapping(struct vlynq_device *dev, u32 tx_offset, + struct vlynq_mapping *mapping); +extern int vlynq_set_local_irq(struct vlynq_device *dev, int virq); +extern int vlynq_set_remote_irq(struct vlynq_device *dev, int virq); + +#endif /* __VLYNQ_H__ */ ^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][3/6]: AR7: VLYNQ bus 2008-04-02 12:56 ` Matteo Croce @ 2008-04-02 18:31 ` Florian Lohoff 2008-04-03 0:19 ` Matteo Croce 0 siblings, 1 reply; 38+ messages in thread From: Florian Lohoff @ 2008-04-02 18:31 UTC (permalink / raw) To: Matteo Croce; +Cc: linux-mips, Eugene Konev, Andrew Morton [-- Attachment #1: Type: text/plain, Size: 1638 bytes --] On Wed, Apr 02, 2008 at 02:56:44PM +0200, Matteo Croce wrote: > > Works fine for my AR7 which has an interlan clock. > Its doesnt for me with an external clock - thats what i mean - Auto probing should first try to listen for an external clock before letting clocks run against each other. This is the hunk of a patch on top of yours ... @@ -371,12 +371,20 @@ static int __vlynq_enable_device(struct switch (dev->divisor) { case vlynq_div_auto: - /* Only try locally supplied clock, others cause problems */ + + vlynq_reg_write(dev->local->control, 0); vlynq_reg_write(dev->remote->control, 0); + if (vlynq_linked(dev)) { + printk(KERN_DEBUG "%s: using external clock\n", + dev->dev.bus_id); + return 0; + } + for (i = vlynq_ldiv2; i <= vlynq_ldiv8; i++) { vlynq_reg_write(dev->local->control, VLYNQ_CTRL_CLOCK_INT | VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1)); + vlynq_reg_write(dev->remote->control, 0); if (vlynq_linked(dev)) { printk(KERN_DEBUG "%s: using local clock divisor %d\n", Flo -- Florian Lohoff flo@rfc822.org +49-171-2280134 Those who would give up a little freedom to get a little security shall soon have neither - Benjamin Franklin [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 189 bytes --] ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][3/6]: AR7: VLYNQ bus 2008-04-02 18:31 ` Florian Lohoff @ 2008-04-03 0:19 ` Matteo Croce 2008-04-03 6:08 ` Florian Lohoff 0 siblings, 1 reply; 38+ messages in thread From: Matteo Croce @ 2008-04-03 0:19 UTC (permalink / raw) To: Florian Lohoff; +Cc: linux-mips, Eugene Konev, Andrew Morton Il Wednesday 02 April 2008 20:31:14 Florian Lohoff ha scritto: > On Wed, Apr 02, 2008 at 02:56:44PM +0200, Matteo Croce wrote: > > > > Works fine for my AR7 which has an interlan clock. > > > > Its doesnt for me with an external clock - thats what i mean - Auto > probing should first try to listen for an external clock before letting > clocks run against each other. This is the hunk of a patch on top of > yours ... > > @@ -371,12 +371,20 @@ static int __vlynq_enable_device(struct > > switch (dev->divisor) { > case vlynq_div_auto: > - /* Only try locally supplied clock, others cause problems */ > + > + vlynq_reg_write(dev->local->control, 0); > vlynq_reg_write(dev->remote->control, 0); > + if (vlynq_linked(dev)) { > + printk(KERN_DEBUG "%s: using external clock\n", > + dev->dev.bus_id); > + return 0; > + } > + > for (i = vlynq_ldiv2; i <= vlynq_ldiv8; i++) { > vlynq_reg_write(dev->local->control, > VLYNQ_CTRL_CLOCK_INT | > VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1)); > + vlynq_reg_write(dev->remote->control, 0); > if (vlynq_linked(dev)) { > printk(KERN_DEBUG > "%s: using local clock divisor %d\n", > > Flo isn't this what I do in my last patch? ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][3/6]: AR7: VLYNQ bus 2008-04-03 0:19 ` Matteo Croce @ 2008-04-03 6:08 ` Florian Lohoff 0 siblings, 0 replies; 38+ messages in thread From: Florian Lohoff @ 2008-04-03 6:08 UTC (permalink / raw) To: Matteo Croce; +Cc: linux-mips, Eugene Konev, Andrew Morton [-- Attachment #1: Type: text/plain, Size: 1630 bytes --] On Thu, Apr 03, 2008 at 02:19:03AM +0200, Matteo Croce wrote: > > @@ -371,12 +371,20 @@ static int __vlynq_enable_device(struct > > > > switch (dev->divisor) { > > case vlynq_div_auto: > > - /* Only try locally supplied clock, others cause problems */ > > + > > + vlynq_reg_write(dev->local->control, 0); > > vlynq_reg_write(dev->remote->control, 0); > > + if (vlynq_linked(dev)) { > > + printk(KERN_DEBUG "%s: using external clock\n", > > + dev->dev.bus_id); > > + return 0; > > + } > > + > > for (i = vlynq_ldiv2; i <= vlynq_ldiv8; i++) { > > vlynq_reg_write(dev->local->control, > > VLYNQ_CTRL_CLOCK_INT | > > VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1)); > > + vlynq_reg_write(dev->remote->control, 0); > > if (vlynq_linked(dev)) { > > printk(KERN_DEBUG > > "%s: using local clock divisor %d\n", > > > > Flo > > isn't this what I do in my last patch? I was shoveling through my inbox top down and the first mail was the original patch i was referring to - sorry for the noise... Flo -- Florian Lohoff flo@rfc822.org +49-171-2280134 Those who would give up a little freedom to get a little security shall soon have neither - Benjamin Franklin [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 189 bytes --] ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][3/6]: AR7: VLYNQ bus 2008-03-29 9:59 ` Florian Lohoff 2008-04-02 12:56 ` Matteo Croce @ 2008-04-02 13:57 ` Matteo Croce 2008-04-02 14:58 ` Matteo Croce 2 siblings, 0 replies; 38+ messages in thread From: Matteo Croce @ 2008-04-02 13:57 UTC (permalink / raw) To: Florian Lohoff; +Cc: linux-mips, Eugene Konev, Andrew Morton Il Saturday 29 March 2008 10:59:14 Florian Lohoff ha scritto: > What i found in the TI code is that FIRST the local->control needs to > get set before issueing a remote access so shouldnd the > > vlynq_reg_write(dev->remote ... ) > > move behind the dev->local ? I mean the logic is to set a local clock > divisor - try to access something on the remote end and see if the link > got up ?!? > > Flo dunno about the order, maybe "vlynq_reg_write(dev->remote->control, 0);" and other NULL writes works as a reset... ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][3/6]: AR7: VLYNQ bus 2008-03-29 9:59 ` Florian Lohoff 2008-04-02 12:56 ` Matteo Croce 2008-04-02 13:57 ` Matteo Croce @ 2008-04-02 14:58 ` Matteo Croce 2 siblings, 0 replies; 38+ messages in thread From: Matteo Croce @ 2008-04-02 14:58 UTC (permalink / raw) To: Florian Lohoff; +Cc: linux-mips, Eugene Konev, Andrew Morton Il Saturday 29 March 2008 10:59:14 Florian Lohoff ha scritto: > > Hi Matteo, > > On Wed, Mar 12, 2008 at 02:26:42AM +0100, Matteo Croce wrote: > > + switch (dev->divisor) { > > + case vlynq_div_auto: > > + /* Only try locally supplied clock, others cause problems */ > > i have a platform (AR7VWi - Leonardo Board) which has an external > supplied clock from an ACX111 so the div_auto autoprobing will not work. > > I put the vlynq_div_external code in front of this which should > simply listen on the vlynq if there is a clock and use it. This solved > one of the issues for me. Can you elaborate on the above comment where > caused problems? I have seen multiple implementations of this, all of > them did autoprobing first by listening on the remote clock first. > > > + vlynq_reg_write(dev->remote->control, 0); > > + for (i = vlynq_ldiv2; i <= vlynq_ldiv8; i++) { > > + vlynq_reg_write(dev->local->control, > > + VLYNQ_CTRL_CLOCK_INT | > > + VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1)); > > + if (vlynq_linked(dev)) { > > + printk(KERN_DEBUG > > + "%s: using local clock divisor %d\n", > > + dev->dev.bus_id, i - vlynq_ldiv1 + 1); > > + dev->divisor = i; > > + return 0; > > + } > > + } > > What i found in the TI code is that FIRST the local->control needs to > get set before issueing a remote access so shouldnd the > > vlynq_reg_write(dev->remote ... ) > > move behind the dev->local ? I mean the logic is to set a local clock > divisor - try to access something on the remote end and see if the link > got up ?!? > > Flo You were right, here is the fixed vlynq driver: Signed-off-by: Matteo Croce <technoboy85@gmail.com> Signed-off-by: Eugene Konev <ejka@openwrt.org> diff --git a/drivers/vlynq/Kconfig b/drivers/vlynq/Kconfig new file mode 100644 index 0000000..2c8ffe0 --- /dev/null +++ b/drivers/vlynq/Kconfig @@ -0,0 +1,13 @@ +menu "TI VLYNQ" + +config VLYNQ + bool "TI VLYNQ bus support" + depends on AR7 && EXPERIMENTAL + help + Support for the TI VLYNQ bus + + The module will be called vlynq + + If unsure, say N + +endmenu diff --git a/drivers/vlynq/Makefile b/drivers/vlynq/Makefile new file mode 100644 index 0000000..b3f6114 --- /dev/null +++ b/drivers/vlynq/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for kernel vlynq drivers +# + +obj-$(CONFIG_VLYNQ) += vlynq.o diff --git a/drivers/vlynq/vlynq.c b/drivers/vlynq/vlynq.c new file mode 100644 index 0000000..879ed0d --- /dev/null +++ b/drivers/vlynq/vlynq.c @@ -0,0 +1,670 @@ +/* + * Copyright (C) 2006, 2007 Eugene Konev <ejka@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/init.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/io.h> + +#include <linux/vlynq.h> + +#define VLYNQ_CTRL_PM_ENABLE 0x80000000 +#define VLYNQ_CTRL_CLOCK_INT 0x00008000 +#define VLYNQ_CTRL_CLOCK_DIV(x) (((x) & 7) << 16) +#define VLYNQ_CTRL_INT_LOCAL 0x00004000 +#define VLYNQ_CTRL_INT_ENABLE 0x00002000 +#define VLYNQ_CTRL_INT_VECTOR(x) (((x) & 0x1f) << 8) +#define VLYNQ_CTRL_INT2CFG 0x00000080 +#define VLYNQ_CTRL_RESET 0x00000001 + +#define VLYNQ_INT_OFFSET 0x00000014 +#define VLYNQ_REMOTE_OFFSET 0x00000080 + +#define VLYNQ_STATUS_LINK 0x00000001 +#define VLYNQ_STATUS_LERROR 0x00000080 +#define VLYNQ_STATUS_RERROR 0x00000100 + +#define VINT_ENABLE 0x00000100 +#define VINT_TYPE_EDGE 0x00000080 +#define VINT_LEVEL_LOW 0x00000040 +#define VINT_VECTOR(x) ((x) & 0x1f) +#define VINT_OFFSET(irq) (8 * ((irq) % 4)) + +#define VLYNQ_AUTONEGO_V2 0x00010000 + +struct vlynq_regs { + u32 revision; + u32 control; + u32 status; + u32 int_prio; + u32 int_status; + u32 int_pending; + u32 int_ptr; + u32 tx_offset; + struct vlynq_mapping rx_mapping[4]; + u32 chip; + u32 autonego; + u32 unused[6]; + u32 int_device[8]; +} __attribute__ ((packed)); + +#define vlynq_reg_read(reg) readl(&(reg)) +#define vlynq_reg_write(reg, val) writel(val, &(reg)) + +static int __vlynq_enable_device(struct vlynq_device *dev); + +#ifdef VLYNQ_DEBUG +static void vlynq_dump_regs(struct vlynq_device *dev) +{ + int i; + printk(KERN_DEBUG "VLYNQ local=%p remote=%p\n", + dev->local, dev->remote); + for (i = 0; i < 32; i++) { + printk(KERN_DEBUG "VLYNQ: local %d: %08x\n", + i + 1, ((u32 *)dev->local)[i]); + printk(KERN_DEBUG "VLYNQ: remote %d: %08x\n", + i + 1, ((u32 *)dev->remote)[i]); + } +} + +static void vlynq_dump_mem(u32 *base, int count) +{ + int i; + for (i = 0; i < (count + 3) / 4; i++) { + if (i % 4 == 0) printk(KERN_DEBUG "\nMEM[0x%04x]:", i * 4); + printk(KERN_DEBUG " 0x%08x", *(base + i)); + } + printk(KERN_DEBUG "\n"); +} +#endif + +int vlynq_linked(struct vlynq_device *dev) +{ + int i; + + for (i = 0; i < 100; i++) + if (vlynq_reg_read(dev->local->status) & VLYNQ_STATUS_LINK) + return 1; + else + cpu_relax(); + + return 0; +} + +static void vlynq_irq_unmask(unsigned int irq) +{ + u32 val; + struct vlynq_device *dev = get_irq_chip_data(irq); + int virq; + + BUG_ON(!dev); + virq = irq - dev->irq_start; + val = vlynq_reg_read(dev->remote->int_device[virq >> 2]); + val |= (VINT_ENABLE | virq) << VINT_OFFSET(virq); + vlynq_reg_write(dev->remote->int_device[virq >> 2], val); +} + +static void vlynq_irq_mask(unsigned int irq) +{ + u32 val; + struct vlynq_device *dev = get_irq_chip_data(irq); + int virq; + + BUG_ON(!dev); + virq = irq - dev->irq_start; + val = vlynq_reg_read(dev->remote->int_device[virq >> 2]); + val &= ~(VINT_ENABLE << VINT_OFFSET(virq)); + vlynq_reg_write(dev->remote->int_device[virq >> 2], val); +} + +static int vlynq_irq_type(unsigned int irq, unsigned int flow_type) +{ + u32 val; + struct vlynq_device *dev = get_irq_chip_data(irq); + int virq; + + BUG_ON(!dev); + virq = irq - dev->irq_start; + val = vlynq_reg_read(dev->remote->int_device[virq >> 2]); + switch (flow_type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + case IRQ_TYPE_EDGE_FALLING: + case IRQ_TYPE_EDGE_BOTH: + val |= VINT_TYPE_EDGE << VINT_OFFSET(virq); + val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq)); + break; + case IRQ_TYPE_LEVEL_HIGH: + val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq)); + val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq)); + break; + case IRQ_TYPE_LEVEL_LOW: + val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq)); + val |= VINT_LEVEL_LOW << VINT_OFFSET(virq); + break; + default: + return -EINVAL; + } + vlynq_reg_write(dev->remote->int_device[virq >> 2], val); + return 0; +} + +static void vlynq_local_ack(unsigned int irq) +{ + struct vlynq_device *dev = get_irq_chip_data(irq); + u32 status = vlynq_reg_read(dev->local->status); + if (printk_ratelimit()) + printk(KERN_DEBUG "%s: local status: 0x%08x\n", + dev->dev.bus_id, status); + vlynq_reg_write(dev->local->status, status); +} + +static void vlynq_remote_ack(unsigned int irq) +{ + struct vlynq_device *dev = get_irq_chip_data(irq); + u32 status = vlynq_reg_read(dev->remote->status); + if (printk_ratelimit()) + printk(KERN_DEBUG "%s: remote status: 0x%08x\n", + dev->dev.bus_id, status); + vlynq_reg_write(dev->remote->status, status); +} + +static irqreturn_t vlynq_irq(int irq, void *dev_id) +{ + struct vlynq_device *dev = dev_id; + u32 status; + int virq = 0; + + status = vlynq_reg_read(dev->local->int_status); + vlynq_reg_write(dev->local->int_status, status); + + if (unlikely(!status)) + spurious_interrupt(); + + while (status) { + if (status & 1) + do_IRQ(dev->irq_start + virq); + status >>= 1; + virq++; + } + + return IRQ_HANDLED; +} + +static struct irq_chip vlynq_irq_chip = { + .name = "vlynq", + .unmask = vlynq_irq_unmask, + .mask = vlynq_irq_mask, + .set_type = vlynq_irq_type, +}; + +static struct irq_chip vlynq_local_chip = { + .name = "vlynq local error", + .unmask = vlynq_irq_unmask, + .mask = vlynq_irq_mask, + .ack = vlynq_local_ack, +}; + +static struct irq_chip vlynq_remote_chip = { + .name = "vlynq local error", + .unmask = vlynq_irq_unmask, + .mask = vlynq_irq_mask, + .ack = vlynq_remote_ack, +}; + +static int vlynq_setup_irq(struct vlynq_device *dev) +{ + u32 val; + int i, virq; + + if (dev->local_irq == dev->remote_irq) { + printk(KERN_ERR + "%s: local vlynq irq should be different from remote\n", + dev->dev.bus_id); + return -EINVAL; + } + + /* Clear local and remote error bits */ + vlynq_reg_write(dev->local->status, vlynq_reg_read(dev->local->status)); + vlynq_reg_write(dev->remote->status, + vlynq_reg_read(dev->remote->status)); + + /* Now setup interrupts */ + val = VLYNQ_CTRL_INT_VECTOR(dev->local_irq); + val |= VLYNQ_CTRL_INT_ENABLE | VLYNQ_CTRL_INT_LOCAL | + VLYNQ_CTRL_INT2CFG; + val |= vlynq_reg_read(dev->local->control); + vlynq_reg_write(dev->local->int_ptr, VLYNQ_INT_OFFSET); + vlynq_reg_write(dev->local->control, val); + + val = VLYNQ_CTRL_INT_VECTOR(dev->remote_irq); + val |= VLYNQ_CTRL_INT_ENABLE; + val |= vlynq_reg_read(dev->remote->control); + vlynq_reg_write(dev->remote->int_ptr, VLYNQ_INT_OFFSET); + vlynq_reg_write(dev->remote->control, val); + + for (i = dev->irq_start; i <= dev->irq_end; i++) { + virq = i - dev->irq_start; + if (virq == dev->local_irq) { + set_irq_chip_and_handler(i, &vlynq_local_chip, + handle_level_irq); + set_irq_chip_data(i, dev); + } else if (virq == dev->remote_irq) { + set_irq_chip_and_handler(i, &vlynq_remote_chip, + handle_level_irq); + set_irq_chip_data(i, dev); + } else { + set_irq_chip_and_handler(i, &vlynq_irq_chip, + handle_simple_irq); + set_irq_chip_data(i, dev); + vlynq_reg_write(dev->remote->int_device[virq >> 2], 0); + } + } + + if (request_irq(dev->irq, vlynq_irq, IRQF_SHARED, "vlynq", dev)) { + printk(KERN_ERR "%s: request_irq failed\n", dev->dev.bus_id); + return -EAGAIN; + } + + return 0; +} + +static void vlynq_device_release(struct device *dev) +{ + struct vlynq_device *vdev = to_vlynq_device(dev); + kfree(vdev); +} + +static int vlynq_device_match(struct device *dev, + struct device_driver *drv) +{ + struct vlynq_device *vdev = to_vlynq_device(dev); + struct vlynq_driver *vdrv = to_vlynq_driver(drv); + struct plat_vlynq_ops *ops = dev->platform_data; + struct vlynq_device_id *ids = vdrv->id_table; + u32 id = 0; + int result; + + while (ids->id) { + vdev->divisor = ids->divisor; + result = __vlynq_enable_device(vdev); + if (result == 0) { + id = vlynq_reg_read(vdev->remote->chip); + ops->off(vdev); + if (ids->id == id) { + vlynq_set_drvdata(vdev, ids); + return 1; + } + } + ids++; + } + return 0; +} + +static int vlynq_device_probe(struct device *dev) +{ + struct vlynq_device *vdev = to_vlynq_device(dev); + struct vlynq_driver *drv = to_vlynq_driver(dev->driver); + struct vlynq_device_id *id = vlynq_get_drvdata(vdev); + int result = -ENODEV; + + get_device(dev); + if (drv && drv->probe) + result = drv->probe(vdev, id); + if (result) + put_device(dev); + return result; +} + +static int vlynq_device_remove(struct device *dev) +{ + struct vlynq_driver *drv = to_vlynq_driver(dev->driver); + if (drv && drv->remove) + drv->remove(to_vlynq_device(dev)); + put_device(dev); + return 0; +} + +int __vlynq_register_driver(struct vlynq_driver *driver, struct module *owner) +{ + driver->driver.name = driver->name; + driver->driver.bus = &vlynq_bus_type; + return driver_register(&driver->driver); +} +EXPORT_SYMBOL(__vlynq_register_driver); + +void vlynq_unregister_driver(struct vlynq_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL(vlynq_unregister_driver); + +static int __vlynq_enable_device(struct vlynq_device *dev) +{ + int i, result; + struct plat_vlynq_ops *ops = dev->dev.platform_data; + + result = ops->on(dev); + if (result) + return result; + + vlynq_reg_write(dev->local->control, 0); + vlynq_reg_write(dev->remote->control, 0); + if (vlynq_linked(dev)) { + printk(KERN_DEBUG "%s: using external clock\n", + dev->dev.bus_id); + return 0; + } + + switch (dev->divisor) { + case vlynq_div_auto: + /* Only try locally supplied clock, others cause problems */ + vlynq_reg_write(dev->local->control, 0); + vlynq_reg_write(dev->remote->control, 0); + for (i = vlynq_ldiv2; i <= vlynq_ldiv8; i++) { + vlynq_reg_write(dev->local->control, + VLYNQ_CTRL_CLOCK_INT | + VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1)); + if (vlynq_linked(dev)) { + printk(KERN_DEBUG + "%s: using local clock divisor %d\n", + dev->dev.bus_id, i - vlynq_ldiv1 + 1); + dev->divisor = i; + return 0; + } + } + case vlynq_ldiv1: case vlynq_ldiv2: case vlynq_ldiv3: case vlynq_ldiv4: + case vlynq_ldiv5: case vlynq_ldiv6: case vlynq_ldiv7: case vlynq_ldiv8: + vlynq_reg_write(dev->local->control, + VLYNQ_CTRL_CLOCK_INT | + VLYNQ_CTRL_CLOCK_DIV(dev->divisor - + vlynq_ldiv1)); + vlynq_reg_write(dev->remote->control, 0); + if (vlynq_linked(dev)) { + printk(KERN_DEBUG + "%s: using local clock divisor %d\n", + dev->dev.bus_id, dev->divisor - vlynq_ldiv1 + 1); + return 0; + } + break; + case vlynq_rdiv1: case vlynq_rdiv2: case vlynq_rdiv3: case vlynq_rdiv4: + case vlynq_rdiv5: case vlynq_rdiv6: case vlynq_rdiv7: case vlynq_rdiv8: + vlynq_reg_write(dev->local->control, 0); + vlynq_reg_write(dev->remote->control, + VLYNQ_CTRL_CLOCK_INT | + VLYNQ_CTRL_CLOCK_DIV(dev->divisor - + vlynq_rdiv1)); + if (vlynq_linked(dev)) { + printk(KERN_DEBUG + "%s: using remote clock divisor %d\n", + dev->dev.bus_id, dev->divisor - vlynq_rdiv1 + 1); + return 0; + } + break; + } + + ops->off(dev); + return -ENODEV; +} + +int vlynq_enable_device(struct vlynq_device *dev) +{ + struct plat_vlynq_ops *ops = dev->dev.platform_data; + int result = -ENODEV; + + result = __vlynq_enable_device(dev); + if (result) + return result; + + result = vlynq_setup_irq(dev); + if (result) + ops->off(dev); + + dev->enabled = !result; + return result; +} +EXPORT_SYMBOL(vlynq_enable_device); + + +void vlynq_disable_device(struct vlynq_device *dev) +{ + struct plat_vlynq_ops *ops = dev->dev.platform_data; + + dev->enabled = 0; + free_irq(dev->irq, dev); + ops->off(dev); +} +EXPORT_SYMBOL(vlynq_disable_device); + +int vlynq_set_local_mapping(struct vlynq_device *dev, u32 tx_offset, + struct vlynq_mapping *mapping) +{ + int i; + + if (!dev->enabled) + return -ENXIO; + + vlynq_reg_write(dev->local->tx_offset, tx_offset); + for (i = 0; i < 4; i++) { + vlynq_reg_write(dev->local->rx_mapping[i].offset, + mapping[i].offset); + vlynq_reg_write(dev->local->rx_mapping[i].size, + mapping[i].size); + } + return 0; +} +EXPORT_SYMBOL(vlynq_set_local_mapping); + +int vlynq_set_remote_mapping(struct vlynq_device *dev, u32 tx_offset, + struct vlynq_mapping *mapping) +{ + int i; + + if (!dev->enabled) + return -ENXIO; + + vlynq_reg_write(dev->remote->tx_offset, tx_offset); + for (i = 0; i < 4; i++) { + vlynq_reg_write(dev->remote->rx_mapping[i].offset, + mapping[i].offset); + vlynq_reg_write(dev->remote->rx_mapping[i].size, + mapping[i].size); + } + return 0; +} +EXPORT_SYMBOL(vlynq_set_remote_mapping); + +int vlynq_set_local_irq(struct vlynq_device *dev, int virq) +{ + int irq = dev->irq_start + virq; + if (dev->enabled) + return -EBUSY; + + if ((irq < dev->irq_start) || (irq > dev->irq_end)) + return -EINVAL; + + if (virq == dev->remote_irq) + return -EINVAL; + + dev->local_irq = virq; + + return 0; +} +EXPORT_SYMBOL(vlynq_set_local_irq); + +int vlynq_set_remote_irq(struct vlynq_device *dev, int virq) +{ + int irq = dev->irq_start + virq; + if (dev->enabled) + return -EBUSY; + + if ((irq < dev->irq_start) || (irq > dev->irq_end)) + return -EINVAL; + + if (virq == dev->local_irq) + return -EINVAL; + + dev->remote_irq = virq; + + return 0; +} +EXPORT_SYMBOL(vlynq_set_remote_irq); + +static int vlynq_probe(struct platform_device *pdev) +{ + struct vlynq_device *dev; + struct resource *regs_res, *mem_res, *irq_res; + int len, result; + + regs_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + if (!regs_res) + return -ENODEV; + + mem_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem"); + if (!mem_res) + return -ENODEV; + + irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "devirq"); + if (!irq_res) + return -ENODEV; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + printk(KERN_ERR + "vlynq: failed to allocate device structure\n"); + return -ENOMEM; + } + + dev->id = pdev->id; + dev->dev.bus = &vlynq_bus_type; + dev->dev.parent = &pdev->dev; + snprintf(dev->dev.bus_id, BUS_ID_SIZE, "vlynq%d", dev->id); + dev->dev.bus_id[BUS_ID_SIZE - 1] = 0; + dev->dev.platform_data = pdev->dev.platform_data; + dev->dev.release = vlynq_device_release; + + dev->regs_start = regs_res->start; + dev->regs_end = regs_res->end; + dev->mem_start = mem_res->start; + dev->mem_end = mem_res->end; + + len = regs_res->end - regs_res->start; + if (!request_mem_region(regs_res->start, len, dev->dev.bus_id)) { + printk(KERN_ERR "%s: Can't request vlynq registers\n", + dev->dev.bus_id); + result = -ENXIO; + goto fail_request; + } + + dev->local = ioremap(regs_res->start, len); + if (!dev->local) { + printk(KERN_ERR "%s: Can't remap vlynq registers\n", + dev->dev.bus_id); + result = -ENXIO; + goto fail_remap; + } + + dev->remote = (struct vlynq_regs *)((void *)dev->local + + VLYNQ_REMOTE_OFFSET); + + dev->irq = platform_get_irq_byname(pdev, "irq"); + dev->irq_start = irq_res->start; + dev->irq_end = irq_res->end; + dev->local_irq = dev->irq_end - dev->irq_start; + dev->remote_irq = dev->local_irq - 1; + + if (device_register(&dev->dev)) + goto fail_register; + platform_set_drvdata(pdev, dev); + + printk(KERN_INFO "%s: regs 0x%p, irq %d, mem 0x%p\n", + dev->dev.bus_id, (void *)dev->regs_start, dev->irq, + (void *)dev->mem_start); + + return 0; + +fail_register: + iounmap(dev->local); +fail_remap: +fail_request: + release_mem_region(regs_res->start, len); + kfree(dev); + return result; +} + +static int vlynq_remove(struct platform_device *pdev) +{ + struct vlynq_device *dev = platform_get_drvdata(pdev); + + device_unregister(&dev->dev); + iounmap(dev->local); + release_mem_region(dev->regs_start, dev->regs_end - dev->regs_start); + + kfree(dev); + + return 0; +} + +static struct platform_driver vlynq_driver = { + .driver.name = "vlynq", + .probe = vlynq_probe, + .remove = __devexit_p(vlynq_remove), +}; + +struct bus_type vlynq_bus_type = { + .name = "vlynq", + .match = vlynq_device_match, + .probe = vlynq_device_probe, + .remove = vlynq_device_remove, +}; +EXPORT_SYMBOL(vlynq_bus_type); + +static int __devinit vlynq_init(void) +{ + int res = 0; + + res = bus_register(&vlynq_bus_type); + if (res) + goto fail_bus; + + res = platform_driver_register(&vlynq_driver); + if (res) + goto fail_platform; + + return 0; + +fail_platform: + bus_unregister(&vlynq_bus_type); +fail_bus: + return res; +} + +static void __devexit vlynq_exit(void) +{ + platform_driver_unregister(&vlynq_driver); + bus_unregister(&vlynq_bus_type); +} + +module_init(vlynq_init); +module_exit(vlynq_exit); diff --git a/include/linux/vlynq.h b/include/linux/vlynq.h new file mode 100644 index 0000000..7658fe4 --- /dev/null +++ b/include/linux/vlynq.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2006, 2007 Eugene Konev <ejka@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __VLYNQ_H__ +#define __VLYNQ_H__ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/types.h> + +#define VLYNQ_NUM_IRQS 32 + +struct vlynq_mapping { + u32 size; + u32 offset; +}; + +enum vlynq_divisor { + vlynq_div_auto = 0, + vlynq_ldiv1, + vlynq_ldiv2, + vlynq_ldiv3, + vlynq_ldiv4, + vlynq_ldiv5, + vlynq_ldiv6, + vlynq_ldiv7, + vlynq_ldiv8, + vlynq_rdiv1, + vlynq_rdiv2, + vlynq_rdiv3, + vlynq_rdiv4, + vlynq_rdiv5, + vlynq_rdiv6, + vlynq_rdiv7, + vlynq_rdiv8, + vlynq_div_external +}; + +struct vlynq_device_id { + u32 id; + enum vlynq_divisor divisor; + unsigned long driver_data; +}; + +struct vlynq_regs; +struct vlynq_device { + u32 id; + int local_irq; + int remote_irq; + enum vlynq_divisor divisor; + u32 regs_start, regs_end; + u32 mem_start, mem_end; + u32 irq_start, irq_end; + int irq; + int enabled; + struct vlynq_regs *local; + struct vlynq_regs *remote; + struct device dev; +}; + +struct vlynq_driver { + char *name; + struct vlynq_device_id *id_table; + int (*probe)(struct vlynq_device *dev, struct vlynq_device_id *id); + void (*remove)(struct vlynq_device *dev); + struct device_driver driver; +}; + +struct plat_vlynq_ops { + int (*on)(struct vlynq_device *dev); + void (*off)(struct vlynq_device *dev); +}; + +static inline struct vlynq_driver *to_vlynq_driver(struct device_driver *drv) +{ + return container_of(drv, struct vlynq_driver, driver); +} + +static inline struct vlynq_device *to_vlynq_device(struct device *device) +{ + return container_of(device, struct vlynq_device, dev); +} + +extern struct bus_type vlynq_bus_type; + +extern int __vlynq_register_driver(struct vlynq_driver *driver, + struct module *owner); + +static inline int vlynq_register_driver(struct vlynq_driver *driver) +{ + return __vlynq_register_driver(driver, THIS_MODULE); +} + +static inline void *vlynq_get_drvdata(struct vlynq_device *dev) +{ + return dev_get_drvdata(&dev->dev); +} + +static inline void vlynq_set_drvdata(struct vlynq_device *dev, void *data) +{ + dev_set_drvdata(&dev->dev, data); +} + +static inline u32 vlynq_mem_start(struct vlynq_device *dev) +{ + return dev->mem_start; +} + +static inline u32 vlynq_mem_end(struct vlynq_device *dev) +{ + return dev->mem_end; +} + +static inline u32 vlynq_mem_len(struct vlynq_device *dev) +{ + return dev->mem_end - dev->mem_start + 1; +} + +static inline int vlynq_virq_to_irq(struct vlynq_device *dev, int virq) +{ + int irq = dev->irq_start + virq; + if ((irq < dev->irq_start) || (irq > dev->irq_end)) + return -EINVAL; + + return irq; +} + +static inline int vlynq_irq_to_virq(struct vlynq_device *dev, int irq) +{ + if ((irq < dev->irq_start) || (irq > dev->irq_end)) + return -EINVAL; + + return irq - dev->irq_start; +} + +extern void vlynq_unregister_driver(struct vlynq_driver *driver); +extern int vlynq_enable_device(struct vlynq_device *dev); +extern void vlynq_disable_device(struct vlynq_device *dev); +extern int vlynq_set_local_mapping(struct vlynq_device *dev, u32 tx_offset, + struct vlynq_mapping *mapping); +extern int vlynq_set_remote_mapping(struct vlynq_device *dev, u32 tx_offset, + struct vlynq_mapping *mapping); +extern int vlynq_set_local_irq(struct vlynq_device *dev, int virq); +extern int vlynq_set_remote_irq(struct vlynq_device *dev, int virq); + +#endif /* __VLYNQ_H__ */ ^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH][MIPS][4/6]: AR7 gpio 2008-03-12 1:21 [PATCH][MIPS][0/6]: AR7 final Matteo Croce 2008-03-12 1:25 ` [PATCH][MIPS][2/6]: AR7 mtd partition map Matteo Croce 2008-03-12 1:26 ` [PATCH][MIPS][3/6]: AR7: VLYNQ bus Matteo Croce @ 2008-03-12 1:28 ` Matteo Croce 2008-03-12 1:30 ` [PATCH][MIPS][5/6]: AR7: serial hack Matteo Croce ` (2 subsequent siblings) 5 siblings, 0 replies; 38+ messages in thread From: Matteo Croce @ 2008-03-12 1:28 UTC (permalink / raw) To: linux-mips; +Cc: Nicolas Thill, Andrew Morton Signed-off-by: Matteo Croce <technoboy85@gmail.com> Signed-off-by: Nicolas Thill <nico@openwrt.org> diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 47c6be8..07677ae 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -913,6 +913,15 @@ config MWAVE To compile this driver as a module, choose M here: the module will be called mwave. +config AR7_GPIO + tristate "TI AR7 GPIO Support" + depends on AR7 + help + Give userspace access to the GPIO pins on the Texas Instruments AR7 + processors. + + If compiled as a module, it will be called ar7_gpio. + config SCx200_GPIO tristate "NatSemi SCx200 GPIO Support" depends on SCx200 diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 5407b76..2f0f787 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -90,6 +90,7 @@ obj-$(CONFIG_COBALT_LCD) += lcd.o obj-$(CONFIG_PPDEV) += ppdev.o obj-$(CONFIG_NWBUTTON) += nwbutton.o obj-$(CONFIG_NWFLASH) += nwflash.o +obj-$(CONFIG_AR7_GPIO) += ar7_gpio.o obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o diff --git a/drivers/char/ar7_gpio.c b/drivers/char/ar7_gpio.c new file mode 100644 index 0000000..16460cd --- /dev/null +++ b/drivers/char/ar7_gpio.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2007 Nicolas Thill <nico@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include <linux/types.h> +#include <linux/cdev.h> +#include <gpio.h> + +#define DRVNAME "ar7_gpio" +#define LONGNAME "TI AR7 GPIOs Driver" + +MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>"); +MODULE_DESCRIPTION(LONGNAME); +MODULE_LICENSE("GPL"); + +static int ar7_gpio_major; + +static ssize_t ar7_gpio_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + int pin = iminor(file->f_dentry->d_inode); + size_t i; + + for (i = 0; i < len; ++i) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + switch (c) { + case '0': + gpio_set_value(pin, 0); + break; + case '1': + gpio_set_value(pin, 1); + break; + case 'd': + case 'D': + ar7_gpio_disable(pin); + break; + case 'e': + case 'E': + ar7_gpio_enable(pin); + break; + case 'i': + case 'I': + case '<': + gpio_direction_input(pin); + break; + case 'o': + case 'O': + case '>': + gpio_direction_output(pin, 0); + break; + default: + return -EINVAL; + } + } + + return len; +} + +static ssize_t ar7_gpio_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + int pin = iminor(file->f_dentry->d_inode); + int value; + + value = gpio_get_value(pin); + if (put_user(value ? '1' : '0', buf)) + return -EFAULT; + + return 1; +} + +static int ar7_gpio_open(struct inode *inode, struct file *file) +{ + int m = iminor(inode); + + if (m >= AR7_GPIO_MAX) + return -EINVAL; + + return nonseekable_open(inode, file); +} + +static int ar7_gpio_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations ar7_gpio_fops = { + .owner = THIS_MODULE, + .write = ar7_gpio_write, + .read = ar7_gpio_read, + .open = ar7_gpio_open, + .release = ar7_gpio_release, + .llseek = no_llseek, +}; + +static struct platform_device *ar7_gpio_device; + +static int __init ar7_gpio_init(void) +{ + int rc; + + ar7_gpio_device = platform_device_alloc(DRVNAME, -1); + if (!ar7_gpio_device) + return -ENOMEM; + + rc = platform_device_add(ar7_gpio_device); + if (rc < 0) + goto out_put; + + rc = register_chrdev(ar7_gpio_major, DRVNAME, &ar7_gpio_fops); + if (rc < 0) + goto out_put; + + ar7_gpio_major = rc; + + rc = 0; + + goto out; + +out_put: + platform_device_put(ar7_gpio_device); +out: + return rc; +} + +static void __exit ar7_gpio_exit(void) +{ + unregister_chrdev(ar7_gpio_major, DRVNAME); + platform_device_unregister(ar7_gpio_device); +} + +module_init(ar7_gpio_init); +module_exit(ar7_gpio_exit); ^ permalink raw reply related [flat|nested] 38+ messages in thread
* [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-12 1:21 [PATCH][MIPS][0/6]: AR7 final Matteo Croce ` (2 preceding siblings ...) 2008-03-12 1:28 ` [PATCH][MIPS][4/6]: AR7 gpio Matteo Croce @ 2008-03-12 1:30 ` Matteo Croce 2008-03-12 9:31 ` Thomas Bogendoerfer 2008-03-12 11:16 ` Alan Cox 2008-03-12 1:34 ` [PATCH][MIPS][6/6]: AR7 leds Matteo Croce 2008-03-12 18:06 ` [PATCH][MIPS][1/6]: AR7: core Matteo Croce 5 siblings, 2 replies; 38+ messages in thread From: Matteo Croce @ 2008-03-12 1:30 UTC (permalink / raw) To: linux-mips Cc: Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton Ugly but we need it Signed-off-by: Matteo Croce <technoboy85@gmail.com> Signed-off-by: Florian Fainelli <florian@openwrt.org> Signed-off-by: Felix Fietkau <nbd@openwrt.org> Signed-off-by: Nicolas Thill <nico@openwrt.org> diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 77f7a7f..a3a271d 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -267,6 +267,13 @@ static const struct serial8250_config uart_config[] = { .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO, }, + [PORT_AR7] = { + .name = "TI-AR7", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, }; #if defined (CONFIG_SERIAL_8250_AU1X00) @@ -2455,7 +2462,11 @@ static void serial8250_console_putchar(struct uart_port *port, int ch) { struct uart_8250_port *up = (struct uart_8250_port *)port; +#ifdef CONFIG_AR7 + wait_for_xmitr(up, BOTH_EMPTY); +#else wait_for_xmitr(up, UART_LSR_THRE); +#endif serial_out(up, UART_TX, ch); } diff --git a/include/linux/serialP.h b/include/linux/serialP.h index e811a61..cf71de9 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -135,6 +135,10 @@ struct rs_multiport_struct { * the interrupt line _up_ instead of down, so if we register the IRQ * while the UART is in that state, we die in an IRQ storm. */ #define ALPHA_KLUDGE_MCR (UART_MCR_OUT2) +#elif defined(CONFIG_AR7) +/* This is how it is set up by bootloader... */ +#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1 \ + | UART_MCR_RTS | UART_MCR_DTR) #else #define ALPHA_KLUDGE_MCR 0 #endif diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 289942f..869b6df 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -40,6 +40,7 @@ #define PORT_NS16550A 14 #define PORT_XSCALE 15 #define PORT_RM9000 16 /* PMC-Sierra RM9xxx internal UART */ +#define PORT_AR7 16 #define PORT_MAX_8250 16 /* max port ID */ /* ^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-12 1:30 ` [PATCH][MIPS][5/6]: AR7: serial hack Matteo Croce @ 2008-03-12 9:31 ` Thomas Bogendoerfer 2008-03-13 0:38 ` Matteo Croce 2008-03-12 11:16 ` Alan Cox 1 sibling, 1 reply; 38+ messages in thread From: Thomas Bogendoerfer @ 2008-03-12 9:31 UTC (permalink / raw) To: Matteo Croce Cc: linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton On Wed, Mar 12, 2008 at 02:30:06AM +0100, Matteo Croce wrote: > Ugly but we need it why ? I'm running AR7 uarts just like all other 16550. > #if defined (CONFIG_SERIAL_8250_AU1X00) > @@ -2455,7 +2462,11 @@ static void serial8250_console_putchar(struct uart_port *port, int ch) > { > struct uart_8250_port *up = (struct uart_8250_port *)port; > > +#ifdef CONFIG_AR7 > + wait_for_xmitr(up, BOTH_EMPTY); > +#else > wait_for_xmitr(up, UART_LSR_THRE); > +#endif > serial_out(up, UART_TX, ch); > } this doesn't make sense. Why don't you check for the port type and decide, if you need to use the AR7 way or the normal way. This will for example break 16550 uarts connected via PCI on UR8 devices. > diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h > index 289942f..869b6df 100644 > --- a/include/linux/serial_core.h > +++ b/include/linux/serial_core.h > @@ -40,6 +40,7 @@ > #define PORT_NS16550A 14 > #define PORT_XSCALE 15 > #define PORT_RM9000 16 /* PMC-Sierra RM9xxx internal UART */ > +#define PORT_AR7 16 this doesn't look correct. Thomas. -- Crap can work. Given enough thrust pigs will fly, but it's not necessary a good idea. [ RFC1925, 2.3 ] ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-12 9:31 ` Thomas Bogendoerfer @ 2008-03-13 0:38 ` Matteo Croce 2008-03-13 8:45 ` Thomas Bogendoerfer 0 siblings, 1 reply; 38+ messages in thread From: Matteo Croce @ 2008-03-13 0:38 UTC (permalink / raw) To: Thomas Bogendoerfer Cc: linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton Il Wednesday 12 March 2008 10:31:46 Thomas Bogendoerfer ha scritto: > > diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h > > index 289942f..869b6df 100644 > > --- a/include/linux/serial_core.h > > +++ b/include/linux/serial_core.h > > @@ -40,6 +40,7 @@ > > #define PORT_NS16550A 14 > > #define PORT_XSCALE 15 > > #define PORT_RM9000 16 /* PMC-Sierra RM9xxx internal UART */ > > +#define PORT_AR7 16 > > this doesn't look correct. > > Thomas. > Isn't it 16? ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-13 0:38 ` Matteo Croce @ 2008-03-13 8:45 ` Thomas Bogendoerfer 2008-03-14 15:46 ` Matteo Croce 0 siblings, 1 reply; 38+ messages in thread From: Thomas Bogendoerfer @ 2008-03-13 8:45 UTC (permalink / raw) To: Matteo Croce Cc: linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton On Thu, Mar 13, 2008 at 01:38:55AM +0100, Matteo Croce wrote: > Il Wednesday 12 March 2008 10:31:46 Thomas Bogendoerfer ha scritto: > > > diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h > > > index 289942f..869b6df 100644 > > > --- a/include/linux/serial_core.h > > > +++ b/include/linux/serial_core.h > > > @@ -40,6 +40,7 @@ > > > #define PORT_NS16550A 14 > > > #define PORT_XSCALE 15 > > > #define PORT_RM9000 16 /* PMC-Sierra RM9xxx internal UART */ > > > +#define PORT_AR7 16 > > > > this doesn't look correct. > > > > Thomas. > > > > Isn't it 16? PORT_RM9000 is 16, how could PORT_AR7 be 16 as well ? And the 16 for PORT_RM9000 is correct in my counting. Thomas. -- Crap can work. Given enough thrust pigs will fly, but it's not necessary a good idea. [ RFC1925, 2.3 ] ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-13 8:45 ` Thomas Bogendoerfer @ 2008-03-14 15:46 ` Matteo Croce 2008-03-15 10:40 ` Thomas Bogendoerfer 2008-03-15 11:38 ` Alan Cox 0 siblings, 2 replies; 38+ messages in thread From: Matteo Croce @ 2008-03-14 15:46 UTC (permalink / raw) To: Thomas Bogendoerfer Cc: linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton Il Thursday 13 March 2008 09:45:26 Thomas Bogendoerfer ha scritto: > On Thu, Mar 13, 2008 at 01:38:55AM +0100, Matteo Croce wrote: > > Il Wednesday 12 March 2008 10:31:46 Thomas Bogendoerfer ha scritto: > > > > diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h > > > > index 289942f..869b6df 100644 > > > > --- a/include/linux/serial_core.h > > > > +++ b/include/linux/serial_core.h > > > > @@ -40,6 +40,7 @@ > > > > #define PORT_NS16550A 14 > > > > #define PORT_XSCALE 15 > > > > #define PORT_RM9000 16 /* PMC-Sierra RM9xxx internal UART */ > > > > +#define PORT_AR7 16 > > > > > > this doesn't look correct. > > > > > > Thomas. > > > > > > > Isn't it 16? > > PORT_RM9000 is 16, how could PORT_AR7 be 16 as well ? And the 16 for > PORT_RM9000 is correct in my counting. > > Thomas. > This is a bit better diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 77f7a7f..a3a271d 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -267,6 +267,13 @@ static const struct serial8250_config uart_config[] = { .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .flags = UART_CAP_FIFO, }, + [PORT_AR7] = { + .name = "TI-AR7", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, }; #if defined (CONFIG_SERIAL_8250_AU1X00) @@ -2455,7 +2462,11 @@ static void serial8250_console_putchar(struct uart_port *port, int ch) { struct uart_8250_port *up = (struct uart_8250_port *)port; +#ifdef CONFIG_AR7 + wait_for_xmitr(up, BOTH_EMPTY); +#else wait_for_xmitr(up, UART_LSR_THRE); +#endif serial_out(up, UART_TX, ch); } diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 289942f..15e76c8 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -40,7 +40,8 @@ #define PORT_NS16550A 14 #define PORT_XSCALE 15 #define PORT_RM9000 16 /* PMC-Sierra RM9xxx internal UART */ -#define PORT_MAX_8250 16 /* max port ID */ +#define PORT_AR7 17 +#define PORT_MAX_8250 17 /* max port ID */ /* * ARM specific type numbers. These are not currently guaranteed ^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-14 15:46 ` Matteo Croce @ 2008-03-15 10:40 ` Thomas Bogendoerfer 2008-03-16 15:45 ` Matteo Croce 2008-03-15 11:38 ` Alan Cox 1 sibling, 1 reply; 38+ messages in thread From: Thomas Bogendoerfer @ 2008-03-15 10:40 UTC (permalink / raw) To: Matteo Croce Cc: linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton On Fri, Mar 14, 2008 at 04:46:09PM +0100, Matteo Croce wrote: > This is a bit better is it possible to try without the serial changes first ? Use uart_port[0].type = PORT_16550A; in arch/mips/ar7/platform.c. Does it work ? Thomas. -- Crap can work. Given enough thrust pigs will fly, but it's not necessary a good idea. [ RFC1925, 2.3 ] ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-15 10:40 ` Thomas Bogendoerfer @ 2008-03-16 15:45 ` Matteo Croce 2008-03-18 13:30 ` Thomas Bogendoerfer 2008-03-21 1:55 ` Thomas Bogendoerfer 0 siblings, 2 replies; 38+ messages in thread From: Matteo Croce @ 2008-03-16 15:45 UTC (permalink / raw) To: Thomas Bogendoerfer Cc: linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton Il Saturday 15 March 2008 11:40:09 Thomas Bogendoerfer ha scritto: > On Fri, Mar 14, 2008 at 04:46:09PM +0100, Matteo Croce wrote: > > This is a bit better > > is it possible to try without the serial changes first ? > > Use > > uart_port[0].type = PORT_16550A; > > in arch/mips/ar7/platform.c. > > Does it work ? > > Thomas. > Tried I get teh usual broken serial output: IP6 oover IPv4 tuneliing driver NET: eggistered protooll family 17 VFS: Monteed root (squahfss filesystem)reaadonly. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-16 15:45 ` Matteo Croce @ 2008-03-18 13:30 ` Thomas Bogendoerfer 2008-03-18 13:43 ` Sergei Shtylyov 2008-03-18 14:01 ` Alan Cox 2008-03-21 1:55 ` Thomas Bogendoerfer 1 sibling, 2 replies; 38+ messages in thread From: Thomas Bogendoerfer @ 2008-03-18 13:30 UTC (permalink / raw) To: Matteo Croce Cc: linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton On Sun, Mar 16, 2008 at 04:45:06PM +0100, Matteo Croce wrote: > Il Saturday 15 March 2008 11:40:09 Thomas Bogendoerfer ha scritto: > > On Fri, Mar 14, 2008 at 04:46:09PM +0100, Matteo Croce wrote: > > > This is a bit better > > > > is it possible to try without the serial changes first ? > > > > Use > > > > uart_port[0].type = PORT_16550A; > > > > in arch/mips/ar7/platform.c. > > > > Does it work ? > > > > Tried I get teh usual broken serial output: I just checked the latest AR7/UR8 source, I have, and they don't need special hacks. This is a 2.6.10 based tree. At that time there was no serial8250_console_putchar(), console output was done via serial8250_console_write() without any helper. Before writing to the UART_TX, wait_for_xmitr() is called. And this wait_for_xmitr() does check for BOTH_EMPTY. Is there a good reason, why we don't check for BOTH_EMPTY in serial8250_console_putchar() ? To match the 2.6.10 behaviour we would need that and this would fix the AR7 case without any special handling. Thomas. -- Crap can work. Given enough thrust pigs will fly, but it's not necessary a good idea. [ RFC1925, 2.3 ] ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-18 13:30 ` Thomas Bogendoerfer @ 2008-03-18 13:43 ` Sergei Shtylyov 2008-03-18 13:49 ` Sergei Shtylyov 2008-03-18 14:01 ` Alan Cox 1 sibling, 1 reply; 38+ messages in thread From: Sergei Shtylyov @ 2008-03-18 13:43 UTC (permalink / raw) To: Thomas Bogendoerfer Cc: Matteo Croce, linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton Hello. Thomas Bogendoerfer wrote: >>Il Saturday 15 March 2008 11:40:09 Thomas Bogendoerfer ha scritto: >>>On Fri, Mar 14, 2008 at 04:46:09PM +0100, Matteo Croce wrote: >>>>This is a bit better >>>is it possible to try without the serial changes first ? >>>Use >>> uart_port[0].type = PORT_16550A; >>>in arch/mips/ar7/platform.c. >>>Does it work ? >>Tried I get teh usual broken serial output: > I just checked the latest AR7/UR8 source, I have, and they don't need > special hacks. This is a 2.6.10 based tree. At that time there was > no serial8250_console_putchar(), console output was done via > serial8250_console_write() without any helper. Before writing to > the UART_TX, wait_for_xmitr() is called. And this wait_for_xmitr() does > check for BOTH_EMPTY. > Is there a good reason, why we don't check for BOTH_EMPTY in > serial8250_console_putchar() ? I guess transmission will be slower if you check both THRE and TSRE conditions. > To match the 2.6.10 behaviour we > would need that and this would fix the AR7 case without any > special handling. AR7 case seems to be the case of bad hardware, and so require special handling... > Thomas. WBR, Sergei ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-18 13:43 ` Sergei Shtylyov @ 2008-03-18 13:49 ` Sergei Shtylyov 0 siblings, 0 replies; 38+ messages in thread From: Sergei Shtylyov @ 2008-03-18 13:49 UTC (permalink / raw) To: Thomas Bogendoerfer Cc: Matteo Croce, linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton Hi, I wrote: >>>>> This is a bit better >>>> is it possible to try without the serial changes first ? >>>> Use >>>> uart_port[0].type = PORT_16550A; >>>> in arch/mips/ar7/platform.c. >>>> Does it work ? >>> Tried I get teh usual broken serial output: >> I just checked the latest AR7/UR8 source, I have, and they don't need >> special hacks. This is a 2.6.10 based tree. At that time there was >> no serial8250_console_putchar(), console output was done via >> serial8250_console_write() without any helper. Before writing to the >> UART_TX, wait_for_xmitr() is called. And this wait_for_xmitr() does >> check for BOTH_EMPTY. >> Is there a good reason, why we don't check for BOTH_EMPTY in >> serial8250_console_putchar() ? > I guess transmission will be slower if you check both THRE and TSRE > conditions. ... and since TX FIFO is in use, it must be even worse since you're only able to load TX FIFO in the short time slots while TX shift register is empty -- quite possibly that this condition will turn to virtually no TX FIFO as these slots my be to short. BTW, does turning off TX FIFO help? WBR, Sergei ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-18 13:30 ` Thomas Bogendoerfer 2008-03-18 13:43 ` Sergei Shtylyov @ 2008-03-18 14:01 ` Alan Cox 2008-03-18 14:01 ` Alan Cox 2008-03-18 15:28 ` Thomas Bogendoerfer 1 sibling, 2 replies; 38+ messages in thread From: Alan Cox @ 2008-03-18 14:01 UTC (permalink / raw) To: Thomas Bogendoerfer Cc: Matteo Croce, linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton > Is there a good reason, why we don't check for BOTH_EMPTY in > serial8250_console_putchar() ? To match the 2.6.10 behaviour we A very good one - we have at least 1 byte of FIFO and the serial-ethernet magic console devices also use that fifo emptying entirely to deduce when to send a new packet. > would need that and this would fix the AR7 case without any > special handling. If the AR7 is an 8250 why does it need special handling? and indeed why does serial work on it except for console - or does that fail too. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-18 14:01 ` Alan Cox @ 2008-03-18 14:01 ` Alan Cox 2008-03-18 15:28 ` Thomas Bogendoerfer 1 sibling, 0 replies; 38+ messages in thread From: Alan Cox @ 2008-03-18 14:01 UTC (permalink / raw) To: Thomas Bogendoerfer Cc: Matteo Croce, linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton > Is there a good reason, why we don't check for BOTH_EMPTY in > serial8250_console_putchar() ? To match the 2.6.10 behaviour we A very good one - we have at least 1 byte of FIFO and the serial-ethernet magic console devices also use that fifo emptying entirely to deduce when to send a new packet. > would need that and this would fix the AR7 case without any > special handling. If the AR7 is an 8250 why does it need special handling? and indeed why does serial work on it except for console - or does that fail too. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-18 14:01 ` Alan Cox 2008-03-18 14:01 ` Alan Cox @ 2008-03-18 15:28 ` Thomas Bogendoerfer 1 sibling, 0 replies; 38+ messages in thread From: Thomas Bogendoerfer @ 2008-03-18 15:28 UTC (permalink / raw) To: Alan Cox Cc: Matteo Croce, linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton On Tue, Mar 18, 2008 at 02:01:33PM +0000, Alan Cox wrote: > > Is there a good reason, why we don't check for BOTH_EMPTY in > > serial8250_console_putchar() ? To match the 2.6.10 behaviour we > > A very good one - we have at least 1 byte of FIFO and the serial-ethernet > magic console devices also use that fifo emptying entirely to deduce when > to send a new packet. ok, now I understand. > > would need that and this would fix the AR7 case without any > > special handling. > > If the AR7 is an 8250 why does it need special handling? and indeed why > does serial work on it except for console - or does that fail too. well TI calls it a 16550A and I still wonder about the reported problems. Looks like I need to dig a little bit deeper... Thomas. -- Crap can work. Given enough thrust pigs will fly, but it's not necessary a good idea. [ RFC1925, 2.3 ] ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-16 15:45 ` Matteo Croce 2008-03-18 13:30 ` Thomas Bogendoerfer @ 2008-03-21 1:55 ` Thomas Bogendoerfer 2008-03-27 21:14 ` Matteo Croce 2008-03-27 21:17 ` Matteo Croce 1 sibling, 2 replies; 38+ messages in thread From: Thomas Bogendoerfer @ 2008-03-21 1:55 UTC (permalink / raw) To: Matteo Croce Cc: linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton On Sun, Mar 16, 2008 at 04:45:06PM +0100, Matteo Croce wrote: > Tried I get teh usual broken serial output: > > IP6 oover IPv4 tuneliing driver > NET: eggistered protooll family 17 > VFS: Monteed root (squahfss filesystem)reaadonly. Linux version 2.6.25-rc6-00000-g151de9e-dirty (tsbogend@solo.franken.de) (gcc version 3.3.3 (Debian 20040320)) #29 Fri Mar 21 02:40:21 CET 2008 console [early0] enabled CPU revision is: 00018448 (MIPS 4KEc) TI AR7 (Unknown), ID: 0x5700, Revision: 0x00 Determined physical RAM map: memory: 02000000 @ 14000000 (usable) Zone PFN ranges: Normal 81920 -> 90112 Movable zone start PFN for each node early_node_map[1] active PFN ranges 0: 81920 -> 90112 Built 1 zonelists in Zone order, mobility grouping on. Total pages: 8128 Kernel command line: console=ttyS0,115200 Primary instruction cache 16kB, VIPT, 4-way, linesize 16 bytes. Primary data cache 16kB, 4-way, VIPT, no aliases, linesize 16 bytes Synthesized clear page handler (26 instructions). Synthesized copy page handler (46 instructions). PID hash table entries: 128 (order: 7, 512 bytes) Dentry cache hash table entries: 4096 (order: 2, 16384 bytes) Inode-cache hash table entries: 2048 (order: 1, 8192 bytes) Memory: 29700k/32768k available (2075k kernel code, 3068k reserved, 466k data, 136k init, 0k highmem) Mount-cache hash table entries: 512 net_namespace: 152 bytes NET: Registered protocol family 16 SCSI subsystem initialized NET: Registered protocol family 2 Time: MIPS clocksource has been installed. IP route cache hash table entries: 1024 (order: 0, 4096 bytes) TCP established hash table entries: 1024 (order: 1, 8192 bytes) TCP bind hash table entries: 1024 (order: 0, 4096 bytes) TCP: Hash tables configured (established 1024 bind 1024) TCP reno registered io scheduler noop registered io scheduler anticipatory registered (default) io scheduler deadline registered Serial: 8250/16550 driver $Revision: 1.90 $ 2 ports, IRQ sharing disabled serial8250: ttyS0 at MMIO 0x8610e03 (irq = 15) is a 16550A console handover: boot [early0] -> real [ttyS0] serial8250: ttyS1 at MMIO 0x8610f03 (irq = 16) is a 16550A loop: module loaded Fixed MDIO Bus: probed ar7_wdt: failed to unlock WDT disable reg ar7_wdt: failed to unlock WDT prescale reg ar7_wdt: failed to unlock WDT change reg ar7_wdt: timer margin 59 seconds (prescale 65535, change 57180, freq 62500000) TCP cubic registered NET: Registered protocol family 1 NET: Registered protocol family 17 RPC: Registered udp transport module. RPC: Registered tcp transport module. Root-NFS: No NFS server available, giving up. VFS: Unable to mount root fs via NFS, trying floppy. List of all partitions: No filesystem could mount root, tried: Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(2,0) Rebooting in 3 seconds.. I don't see any problems with using PORT_16550A. What I'm still wondering how your kernel could work at all, since there is a missing case for setting up the TLB refill handler. Something like the patch below. And most of the AR7 device driver code will not work for big endian. The log above is from a big endian AR7 system, where I needed to disable CPMAC to get it booting that far. Thomas. diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index 3a93d4c..382738c 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -307,6 +307,7 @@ static void __cpuinit build_tlb_write_entry(u32 **p, struct uasm_label **l, case CPU_R12000: case CPU_R14000: case CPU_4KC: + case CPU_4KEC: case CPU_SB1: case CPU_SB1A: case CPU_4KSC: -- Crap can work. Given enough thrust pigs will fly, but it's not necessary a good idea. [ RFC1925, 2.3 ] ^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-21 1:55 ` Thomas Bogendoerfer @ 2008-03-27 21:14 ` Matteo Croce 2008-03-27 21:17 ` Matteo Croce 1 sibling, 0 replies; 38+ messages in thread From: Matteo Croce @ 2008-03-27 21:14 UTC (permalink / raw) To: Thomas Bogendoerfer Cc: linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton Il Friday 21 March 2008 02:55:40 Thomas Bogendoerfer ha scritto: > On Sun, Mar 16, 2008 at 04:45:06PM +0100, Matteo Croce wrote: > > Tried I get teh usual broken serial output: > > > > IP6 oover IPv4 tuneliing driver > > NET: eggistered protooll family 17 > > VFS: Monteed root (squahfss filesystem)reaadonly. > > Linux version 2.6.25-rc6-00000-g151de9e-dirty (tsbogend@solo.franken.de) > (gcc version 3.3.3 (Debian 20040320)) #29 Fri Mar 21 02:40:21 CET 2008 > console [early0] enabled > CPU revision is: 00018448 (MIPS 4KEc) > TI AR7 (Unknown), ID: 0x5700, Revision: 0x00 > Determined physical RAM map: > memory: 02000000 @ 14000000 (usable) > Zone PFN ranges: > Normal 81920 -> 90112 > Movable zone start PFN for each node > early_node_map[1] active PFN ranges > 0: 81920 -> 90112 > Built 1 zonelists in Zone order, mobility grouping on. Total pages: > 8128 > Kernel command line: console=ttyS0,115200 > Primary instruction cache 16kB, VIPT, 4-way, linesize 16 bytes. > Primary data cache 16kB, 4-way, VIPT, no aliases, linesize 16 bytes > Synthesized clear page handler (26 instructions). > Synthesized copy page handler (46 instructions). > PID hash table entries: 128 (order: 7, 512 bytes) > Dentry cache hash table entries: 4096 (order: 2, 16384 bytes) > Inode-cache hash table entries: 2048 (order: 1, 8192 bytes) > Memory: 29700k/32768k available (2075k kernel code, 3068k reserved, 466k > data, 136k init, 0k highmem) > Mount-cache hash table entries: 512 > net_namespace: 152 bytes > NET: Registered protocol family 16 > SCSI subsystem initialized > NET: Registered protocol family 2 > Time: MIPS clocksource has been installed. > IP route cache hash table entries: 1024 (order: 0, 4096 bytes) > TCP established hash table entries: 1024 (order: 1, 8192 bytes) > TCP bind hash table entries: 1024 (order: 0, 4096 bytes) > TCP: Hash tables configured (established 1024 bind 1024) > TCP reno registered > io scheduler noop registered > io scheduler anticipatory registered (default) > io scheduler deadline registered > Serial: 8250/16550 driver $Revision: 1.90 $ 2 ports, IRQ sharing > disabled > serial8250: ttyS0 at MMIO 0x8610e03 (irq = 15) is a 16550A > console handover: boot [early0] -> real [ttyS0] > serial8250: ttyS1 at MMIO 0x8610f03 (irq = 16) is a 16550A > loop: module loaded > Fixed MDIO Bus: probed > ar7_wdt: failed to unlock WDT disable reg > ar7_wdt: failed to unlock WDT prescale reg > ar7_wdt: failed to unlock WDT change reg > ar7_wdt: timer margin 59 seconds (prescale 65535, change 57180, freq > 62500000) > TCP cubic registered > NET: Registered protocol family 1 > NET: Registered protocol family 17 > RPC: Registered udp transport module. > RPC: Registered tcp transport module. > Root-NFS: No NFS server available, giving up. > VFS: Unable to mount root fs via NFS, trying floppy. > List of all partitions: > No filesystem could mount root, tried: > Kernel panic - not syncing: VFS: Unable to mount root fs on > unknown-block(2,0) > Rebooting in 3 seconds.. > > I don't see any problems with using PORT_16550A. What I'm still > wondering how your kernel could work at all, since there is a > missing case for setting up the TLB refill handler. Something like > the patch below. > > And most of the AR7 device driver code will not work for big endian. > The log above is from a big endian AR7 system, where I needed > to disable CPMAC to get it booting that far. > > Thomas. > > > diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c > index 3a93d4c..382738c 100644 > --- a/arch/mips/mm/tlbex.c > +++ b/arch/mips/mm/tlbex.c > @@ -307,6 +307,7 @@ static void __cpuinit build_tlb_write_entry(u32 **p, struct uasm_label **l, > case CPU_R12000: > case CPU_R14000: > case CPU_4KC: > + case CPU_4KEC: > case CPU_SB1: > case CPU_SB1A: > case CPU_4KSC: > > Actually we use this for 2.6.24: Index: linux-2.6.24/arch/mips/mm/tlbex.c =================================================================== --- linux-2.6.24/arch/mips/mm/tlbex.c 2007-10-10 04:31:38.000000000 +0800 +++ linux-2.6.24/arch/mips/mm/tlbex.c 2007-10-10 13:52:34.000000000 +0800 @@ -902,7 +902,6 @@ case CPU_R10000: case CPU_R12000: case CPU_R14000: - case CPU_4KC: case CPU_SB1: case CPU_SB1A: case CPU_4KSC: @@ -933,6 +932,7 @@ tlbw(p); break; + case CPU_4KC: case CPU_4KEC: case CPU_24K: case CPU_34K: ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-21 1:55 ` Thomas Bogendoerfer 2008-03-27 21:14 ` Matteo Croce @ 2008-03-27 21:17 ` Matteo Croce 1 sibling, 0 replies; 38+ messages in thread From: Matteo Croce @ 2008-03-27 21:17 UTC (permalink / raw) To: Thomas Bogendoerfer Cc: linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton Il Friday 21 March 2008 02:55:40 Thomas Bogendoerfer ha scritto: > On Sun, Mar 16, 2008 at 04:45:06PM +0100, Matteo Croce wrote: > > Tried I get teh usual broken serial output: > > > > IP6 oover IPv4 tuneliing driver > > NET: eggistered protooll family 17 > > VFS: Monteed root (squahfss filesystem)reaadonly. > > Linux version 2.6.25-rc6-00000-g151de9e-dirty (tsbogend@solo.franken.de) > (gcc version 3.3.3 (Debian 20040320)) #29 Fri Mar 21 02:40:21 CET 2008 > console [early0] enabled > CPU revision is: 00018448 (MIPS 4KEc) > TI AR7 (Unknown), ID: 0x5700, Revision: 0x00 > Determined physical RAM map: > memory: 02000000 @ 14000000 (usable) > Zone PFN ranges: > Normal 81920 -> 90112 > Movable zone start PFN for each node > early_node_map[1] active PFN ranges > 0: 81920 -> 90112 > Built 1 zonelists in Zone order, mobility grouping on. Total pages: > 8128 > Kernel command line: console=ttyS0,115200 > Primary instruction cache 16kB, VIPT, 4-way, linesize 16 bytes. > Primary data cache 16kB, 4-way, VIPT, no aliases, linesize 16 bytes > Synthesized clear page handler (26 instructions). > Synthesized copy page handler (46 instructions). > PID hash table entries: 128 (order: 7, 512 bytes) > Dentry cache hash table entries: 4096 (order: 2, 16384 bytes) > Inode-cache hash table entries: 2048 (order: 1, 8192 bytes) > Memory: 29700k/32768k available (2075k kernel code, 3068k reserved, 466k > data, 136k init, 0k highmem) > Mount-cache hash table entries: 512 > net_namespace: 152 bytes > NET: Registered protocol family 16 > SCSI subsystem initialized > NET: Registered protocol family 2 > Time: MIPS clocksource has been installed. > IP route cache hash table entries: 1024 (order: 0, 4096 bytes) > TCP established hash table entries: 1024 (order: 1, 8192 bytes) > TCP bind hash table entries: 1024 (order: 0, 4096 bytes) > TCP: Hash tables configured (established 1024 bind 1024) > TCP reno registered > io scheduler noop registered > io scheduler anticipatory registered (default) > io scheduler deadline registered > Serial: 8250/16550 driver $Revision: 1.90 $ 2 ports, IRQ sharing > disabled > serial8250: ttyS0 at MMIO 0x8610e03 (irq = 15) is a 16550A > console handover: boot [early0] -> real [ttyS0] > serial8250: ttyS1 at MMIO 0x8610f03 (irq = 16) is a 16550A > loop: module loaded > Fixed MDIO Bus: probed > ar7_wdt: failed to unlock WDT disable reg > ar7_wdt: failed to unlock WDT prescale reg > ar7_wdt: failed to unlock WDT change reg > ar7_wdt: timer margin 59 seconds (prescale 65535, change 57180, freq > 62500000) > TCP cubic registered > NET: Registered protocol family 1 > NET: Registered protocol family 17 > RPC: Registered udp transport module. > RPC: Registered tcp transport module. > Root-NFS: No NFS server available, giving up. > VFS: Unable to mount root fs via NFS, trying floppy. > List of all partitions: > No filesystem could mount root, tried: > Kernel panic - not syncing: VFS: Unable to mount root fs on > unknown-block(2,0) > Rebooting in 3 seconds.. > > I don't see any problems with using PORT_16550A. What I'm still > wondering how your kernel could work at all, since there is a > missing case for setting up the TLB refill handler. Something like > the patch below. > > And most of the AR7 device driver code will not work for big endian. > The log above is from a big endian AR7 system, where I needed > to disable CPMAC to get it booting that far. > > Thomas. > > > diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c > index 3a93d4c..382738c 100644 > --- a/arch/mips/mm/tlbex.c > +++ b/arch/mips/mm/tlbex.c > @@ -307,6 +307,7 @@ static void __cpuinit build_tlb_write_entry(u32 **p, struct uasm_label **l, > case CPU_R12000: > case CPU_R14000: > case CPU_4KC: > + case CPU_4KEC: > case CPU_SB1: > case CPU_SB1A: > case CPU_4KSC: > > Actually we use this for 2.6.24: Index: linux-2.6.24/arch/mips/mm/tlbex.c =================================================================== --- linux-2.6.24/arch/mips/mm/tlbex.c 2007-10-10 04:31:38.000000000 +0800 +++ linux-2.6.24/arch/mips/mm/tlbex.c 2007-10-10 13:52:34.000000000 +0800 @@ -902,7 +902,6 @@ case CPU_R10000: case CPU_R12000: case CPU_R14000: - case CPU_4KC: case CPU_SB1: case CPU_SB1A: case CPU_4KSC: @@ -933,6 +932,7 @@ tlbw(p); break; + case CPU_4KC: case CPU_4KEC: case CPU_24K: case CPU_34K: ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-14 15:46 ` Matteo Croce 2008-03-15 10:40 ` Thomas Bogendoerfer @ 2008-03-15 11:38 ` Alan Cox 1 sibling, 0 replies; 38+ messages in thread From: Alan Cox @ 2008-03-15 11:38 UTC (permalink / raw) To: Matteo Croce Cc: Thomas Bogendoerfer, linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton > This is a bit better NAK - especially as there has been a specific response about how to do this without ifdef hacks in core code. Alan ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-12 1:30 ` [PATCH][MIPS][5/6]: AR7: serial hack Matteo Croce 2008-03-12 9:31 ` Thomas Bogendoerfer @ 2008-03-12 11:16 ` Alan Cox 2008-03-13 0:31 ` Matteo Croce 1 sibling, 1 reply; 38+ messages in thread From: Alan Cox @ 2008-03-12 11:16 UTC (permalink / raw) To: Matteo Croce Cc: linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton On Wed, 12 Mar 2008 02:30:06 +0100 Matteo Croce <technoboy85@gmail.com> wrote: > Ugly but we need it Too ugly - NAK However please send an explanation of the problem and lets find a nicer way to do it or bury it in arch code. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-12 11:16 ` Alan Cox @ 2008-03-13 0:31 ` Matteo Croce 2008-03-13 9:01 ` Thomas Bogendoerfer 0 siblings, 1 reply; 38+ messages in thread From: Matteo Croce @ 2008-03-13 0:31 UTC (permalink / raw) To: Alan Cox Cc: linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton Il Wednesday 12 March 2008 12:16:29 Alan Cox ha scritto: > On Wed, 12 Mar 2008 02:30:06 +0100 > Matteo Croce <technoboy85@gmail.com> wrote: > > > Ugly but we need it > > Too ugly - NAK > > However please send an explanation of the problem and lets find a nicer > way to do it or bury it in arch code. > > This is my problem: ffi_cmdset_000: DDisabling erae-ssuspend-progrm ddue to code bokeenness. cmdlinparrt partition arssing not avaiabll RedBoo ppartition parsngg not availabl NET: Rgiistered protocl family 1 NET: Regsteered protocol ammily 10 IPv6 overIPPv4 tunnelingdriiver NET: Regsteered protocolfammily 17 FS:: Mounted roo (ssquashfs filessttem) readonly. Freeing nuused kernel meorry: 120k freed I'll try to find a nicer way to fix it ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-13 0:31 ` Matteo Croce @ 2008-03-13 9:01 ` Thomas Bogendoerfer 2008-03-16 15:27 ` Matteo Croce 2008-03-16 15:49 ` Matteo Croce 0 siblings, 2 replies; 38+ messages in thread From: Thomas Bogendoerfer @ 2008-03-13 9:01 UTC (permalink / raw) To: Matteo Croce Cc: Alan Cox, linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton On Thu, Mar 13, 2008 at 01:31:54AM +0100, Matteo Croce wrote: > Il Wednesday 12 March 2008 12:16:29 Alan Cox ha scritto: > > On Wed, 12 Mar 2008 02:30:06 +0100 > > Matteo Croce <technoboy85@gmail.com> wrote: > > > > > Ugly but we need it > > > > Too ugly - NAK > > > > However please send an explanation of the problem and lets find a nicer > > way to do it or bury it in arch code. > > > > > > This is my problem: > > ffi_cmdset_000: DDisabling erae-ssuspend-progrm ddue to code bokeenness. > cmdlinparrt partition arssing not avaiabll > RedBoo ppartition parsngg not availabl > NET: Rgiistered protocl family 1 > NET: Regsteered protocol ammily 10 > IPv6 overIPPv4 tunnelingdriiver > NET: Regsteered protocolfammily 17 > FS:: Mounted roo (ssquashfs filessttem) readonly. > Freeing nuused kernel meorry: 120k freed > > I'll try to find a nicer way to fix it don't use AFE mode and treat it like a normal 16550 (PORT_16550A). You could also try to use UPIO_MEM32. That's how my console driver (different OS) works for AR7 without the hack to wait for LSR_TEMP and LSR_THRE. Thomas. -- Crap can work. Given enough thrust pigs will fly, but it's not necessary a good idea. [ RFC1925, 2.3 ] ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-13 9:01 ` Thomas Bogendoerfer @ 2008-03-16 15:27 ` Matteo Croce 2008-03-16 15:49 ` Matteo Croce 1 sibling, 0 replies; 38+ messages in thread From: Matteo Croce @ 2008-03-16 15:27 UTC (permalink / raw) To: Thomas Bogendoerfer Cc: Alan Cox, linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton Il Thursday 13 March 2008 10:01:09 Thomas Bogendoerfer ha scritto: > On Thu, Mar 13, 2008 at 01:31:54AM +0100, Matteo Croce wrote: > > Il Wednesday 12 March 2008 12:16:29 Alan Cox ha scritto: > > > On Wed, 12 Mar 2008 02:30:06 +0100 > > > Matteo Croce <technoboy85@gmail.com> wrote: > > > > > > > Ugly but we need it > > > > > > Too ugly - NAK > > > > > > However please send an explanation of the problem and lets find a nicer > > > way to do it or bury it in arch code. > > > > > > > > > > This is my problem: > > > > ffi_cmdset_000: DDisabling erae-ssuspend-progrm ddue to code bokeenness. > > cmdlinparrt partition arssing not avaiabll > > RedBoo ppartition parsngg not availabl > > NET: Rgiistered protocl family 1 > > NET: Regsteered protocol ammily 10 > > IPv6 overIPPv4 tunnelingdriiver > > NET: Regsteered protocolfammily 17 > > FS:: Mounted roo (ssquashfs filessttem) readonly. > > Freeing nuused kernel meorry: 120k freed > > > > I'll try to find a nicer way to fix it > > don't use AFE mode and treat it like a normal 16550 (PORT_16550A). You > could also try to use UPIO_MEM32. That's how my console driver > (different OS) works for AR7 without the hack to wait for LSR_TEMP and > LSR_THRE. > > Thomas. > What about the .fcr field? PORT_16550A uses UART_FCR_R_TRIG_10 while AR7 uses UART_FCR_R_TRIG_00 ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][5/6]: AR7: serial hack 2008-03-13 9:01 ` Thomas Bogendoerfer 2008-03-16 15:27 ` Matteo Croce @ 2008-03-16 15:49 ` Matteo Croce 1 sibling, 0 replies; 38+ messages in thread From: Matteo Croce @ 2008-03-16 15:49 UTC (permalink / raw) To: Thomas Bogendoerfer Cc: Alan Cox, linux-mips, Florian Fainelli, Felix Fietkau, Nicolas Thill, linux-serial, Andrew Morton Il Thursday 13 March 2008 10:01:09 Thomas Bogendoerfer ha scritto: > On Thu, Mar 13, 2008 at 01:31:54AM +0100, Matteo Croce wrote: > > Il Wednesday 12 March 2008 12:16:29 Alan Cox ha scritto: > > > On Wed, 12 Mar 2008 02:30:06 +0100 > > > Matteo Croce <technoboy85@gmail.com> wrote: > > > > > > > Ugly but we need it > > > > > > Too ugly - NAK > > > > > > However please send an explanation of the problem and lets find a nicer > > > way to do it or bury it in arch code. > > > > > > > > > > This is my problem: > > > > ffi_cmdset_000: DDisabling erae-ssuspend-progrm ddue to code bokeenness. > > cmdlinparrt partition arssing not avaiabll > > RedBoo ppartition parsngg not availabl > > NET: Rgiistered protocl family 1 > > NET: Regsteered protocol ammily 10 > > IPv6 overIPPv4 tunnelingdriiver > > NET: Regsteered protocolfammily 17 > > FS:: Mounted roo (ssquashfs filessttem) readonly. > > Freeing nuused kernel meorry: 120k freed > > > > I'll try to find a nicer way to fix it > > don't use AFE mode and treat it like a normal 16550 (PORT_16550A). You > could also try to use UPIO_MEM32. That's how my console driver > (different OS) works for AR7 without the hack to wait for LSR_TEMP and > LSR_THRE. > > Thomas. > What do you mean by don't using AFE? Just removing UART_CAP_AFE from the .fcr field? I've tried but it doesn't work. I tried also UPIO_MEM32 instead of UPIO_MEM (with PORT_AR7) but it doesn't works ^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH][MIPS][6/6]: AR7 leds 2008-03-12 1:21 [PATCH][MIPS][0/6]: AR7 final Matteo Croce ` (3 preceding siblings ...) 2008-03-12 1:30 ` [PATCH][MIPS][5/6]: AR7: serial hack Matteo Croce @ 2008-03-12 1:34 ` Matteo Croce 2008-03-13 0:34 ` Matteo Croce 2008-03-12 18:06 ` [PATCH][MIPS][1/6]: AR7: core Matteo Croce 5 siblings, 1 reply; 38+ messages in thread From: Matteo Croce @ 2008-03-12 1:34 UTC (permalink / raw) To: linux-mips, linux-net Cc: Eugene Konev, netdev, davem, kuznet, pekkas, jmorris, yoshfuji, kaber, Andrew Morton, Jeff Garzik, Anton Vorontsov Signed-off-by: Matteo Croce <technoboy85@gmail.com> Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com> diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index c85194f..05191b6 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -1006,23 +1006,10 @@ static int __devinit cpmac_probe(struct platform_device *pdev) if (phy_id == PHY_MAX_ADDR) { if (external_switch || dumb_switch) { - struct fixed_phy_status status = {}; - - mdio_bus_id = 0; - - /* - * FIXME: this should be in the platform code! - * Since there is not platform code at all (that is, - * no mainline users of that driver), place it here - * for now. - */ - phy_id = 0; - status.link = 1; - status.duplex = 1; - status.speed = 100; - fixed_phy_add(PHY_POLL, phy_id, &status); + mdio_bus_id = 0; /* fixed phys bus */ + phy_id = pdev->id; } else { - printk(KERN_ERR "cpmac: no PHY present\n"); + dev_err(&pdev->dev, "no PHY present\n"); return -ENODEV; } } ^ permalink raw reply related [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][6/6]: AR7 leds 2008-03-12 1:34 ` [PATCH][MIPS][6/6]: AR7 leds Matteo Croce @ 2008-03-13 0:34 ` Matteo Croce 0 siblings, 0 replies; 38+ messages in thread From: Matteo Croce @ 2008-03-13 0:34 UTC (permalink / raw) To: linux-mips Cc: linux-net, Eugene Konev, netdev, davem, kuznet, pekkas, jmorris, yoshfuji, kaber, Andrew Morton, Jeff Garzik, Anton Vorontsov Subject really was: AR7 ethernet ^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH][MIPS][1/6]: AR7: core 2008-03-12 1:21 [PATCH][MIPS][0/6]: AR7 final Matteo Croce ` (4 preceding siblings ...) 2008-03-12 1:34 ` [PATCH][MIPS][6/6]: AR7 leds Matteo Croce @ 2008-03-12 18:06 ` Matteo Croce 2008-03-12 19:33 ` Ralf Baechle 2008-03-29 10:34 ` Florian Lohoff 5 siblings, 2 replies; 38+ messages in thread From: Matteo Croce @ 2008-03-12 18:06 UTC (permalink / raw) To: linux-mips Cc: Florian Fainelli, Felix Fietkau, Eugene Konev, Nicolas Thill, ralf, Andrew Morton [-- Attachment #1: Type: text/plain, Size: 119 bytes --] Sorry but the linux-mips mailing list can't accept this patch for unknown reasons. I'll send a gz file, that will work [-- Attachment #2: core.diff.gz --] [-- Type: application/x-gzip, Size: 14383 bytes --] ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][1/6]: AR7: core 2008-03-12 18:06 ` [PATCH][MIPS][1/6]: AR7: core Matteo Croce @ 2008-03-12 19:33 ` Ralf Baechle 2008-03-29 10:34 ` Florian Lohoff 1 sibling, 0 replies; 38+ messages in thread From: Ralf Baechle @ 2008-03-12 19:33 UTC (permalink / raw) To: Matteo Croce Cc: linux-mips, Florian Fainelli, Felix Fietkau, Eugene Konev, Nicolas Thill, Andrew Morton On Wed, Mar 12, 2008 at 07:06:25PM +0100, Matteo Croce wrote: > Sorry but the linux-mips mailing list can't accept this patch for unknown reasons. > I'll send a gz file, that will work to the best I can say without knowing the message ID of the lost mail it is as if the mail never reached the lmo machine. I can say for certain that it didn't end in the spam filter. Ralf ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH][MIPS][1/6]: AR7: core 2008-03-12 18:06 ` [PATCH][MIPS][1/6]: AR7: core Matteo Croce 2008-03-12 19:33 ` Ralf Baechle @ 2008-03-29 10:34 ` Florian Lohoff 1 sibling, 0 replies; 38+ messages in thread From: Florian Lohoff @ 2008-03-29 10:34 UTC (permalink / raw) To: Matteo Croce Cc: linux-mips, Florian Fainelli, Felix Fietkau, Eugene Konev, Nicolas Thill, ralf [-- Attachment #1: Type: text/plain, Size: 2292 bytes --] Hi, On Wed, Mar 12, 2008 at 07:06:25PM +0100, Matteo Croce wrote: > > Sorry but the linux-mips mailing list can't accept this patch for unknown reasons. > I'll send a gz file, that will work i have a plattform AR7VWi which has a High Active reset for the vlynq_low so the code is not sufficient to get the ACX up and running +static struct plat_vlynq_data vlynq_low_data = { + .ops.on = vlynq_on, + .ops.off = vlynq_off, + .reset_bit = 20, + .gpio_bit = 18, +}; + +static struct plat_vlynq_data vlynq_high_data = { + .ops.on = vlynq_on, + .ops.off = vlynq_off, + .reset_bit = 16, + .gpio_bit = 19, +}; The gpio_bit's are okay but they are high active so the generic code: +static int vlynq_on(struct vlynq_device *dev) +{ + int result; + struct plat_vlynq_data *pdata = dev->dev.platform_data; + + if ((result = gpio_request(pdata->gpio_bit, "vlynq"))) + goto out; + + ar7_device_reset(pdata->reset_bit); + + if ((result = ar7_gpio_disable(pdata->gpio_bit))) + goto out_enabled; + + if ((result = ar7_gpio_enable(pdata->gpio_bit))) + goto out_enabled; + + if ((result = gpio_direction_output(pdata->gpio_bit, 0))) + goto out_gpio_enabled; + + mdelay(50); + + gpio_set_value(pdata->gpio_bit, 1); Is not enough here - gpios might be reverse polarity ... The right thing to actually reset a device would need a toggle not a static state. + mdelay(50); + + return 0; +static void vlynq_off(struct vlynq_device *dev) +{ + struct plat_vlynq_data *pdata = dev->dev.platform_data; + ar7_gpio_disable(pdata->gpio_bit); + gpio_free(pdata->gpio_bit); + ar7_device_disable(pdata->reset_bit); +} gpio_disable to gpio_bit will put the gpio to tristate or special function which will lead to unpredicted results. So gpio_set_value would be needed and then the same as for vlynq_on applies concerning the reverse polarity. Flo -- Florian Lohoff flo@rfc822.org +49-171-2280134 Those who would give up a little freedom to get a little security shall soon have neither - Benjamin Franklin [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 189 bytes --] ^ permalink raw reply [flat|nested] 38+ messages in thread
end of thread, other threads:[~2008-04-03 17:35 UTC | newest] Thread overview: 38+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2008-03-12 1:21 [PATCH][MIPS][0/6]: AR7 final Matteo Croce 2008-03-12 1:25 ` [PATCH][MIPS][2/6]: AR7 mtd partition map Matteo Croce 2008-03-12 1:26 ` [PATCH][MIPS][3/6]: AR7: VLYNQ bus Matteo Croce 2008-03-29 9:59 ` Florian Lohoff 2008-04-02 12:56 ` Matteo Croce 2008-04-02 18:31 ` Florian Lohoff 2008-04-03 0:19 ` Matteo Croce 2008-04-03 6:08 ` Florian Lohoff 2008-04-02 13:57 ` Matteo Croce 2008-04-02 14:58 ` Matteo Croce 2008-03-12 1:28 ` [PATCH][MIPS][4/6]: AR7 gpio Matteo Croce 2008-03-12 1:30 ` [PATCH][MIPS][5/6]: AR7: serial hack Matteo Croce 2008-03-12 9:31 ` Thomas Bogendoerfer 2008-03-13 0:38 ` Matteo Croce 2008-03-13 8:45 ` Thomas Bogendoerfer 2008-03-14 15:46 ` Matteo Croce 2008-03-15 10:40 ` Thomas Bogendoerfer 2008-03-16 15:45 ` Matteo Croce 2008-03-18 13:30 ` Thomas Bogendoerfer 2008-03-18 13:43 ` Sergei Shtylyov 2008-03-18 13:49 ` Sergei Shtylyov 2008-03-18 14:01 ` Alan Cox 2008-03-18 14:01 ` Alan Cox 2008-03-18 15:28 ` Thomas Bogendoerfer 2008-03-21 1:55 ` Thomas Bogendoerfer 2008-03-27 21:14 ` Matteo Croce 2008-03-27 21:17 ` Matteo Croce 2008-03-15 11:38 ` Alan Cox 2008-03-12 11:16 ` Alan Cox 2008-03-13 0:31 ` Matteo Croce 2008-03-13 9:01 ` Thomas Bogendoerfer 2008-03-16 15:27 ` Matteo Croce 2008-03-16 15:49 ` Matteo Croce 2008-03-12 1:34 ` [PATCH][MIPS][6/6]: AR7 leds Matteo Croce 2008-03-13 0:34 ` Matteo Croce 2008-03-12 18:06 ` [PATCH][MIPS][1/6]: AR7: core Matteo Croce 2008-03-12 19:33 ` Ralf Baechle 2008-03-29 10:34 ` Florian Lohoff
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox