From 8353ec7b0d6666c0674ca978379c55234609dae8 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 21 Jan 2008 23:49:53 +0300 Subject: cpmac: convert to new Fixed PHY infrastructure This patch converts cpmac to the new Fixed PHY infrastructure, though it doesn't fix all the problems with that driver. I didn't even bother to test this patch to compile, because cpmac driver is broken in several ways: 1. This driver won't compile by itself because lack of its header describing platform data; 2. It assumes that fixed PHYs should be created by the ethernet driver. It is wrong assumption: fixed PHYs creation is platform code authority, driver must blindly accept bus_id and phy_id platform data variables instead. Also, it seem that that driver doesn't have actual in-tree users, so nothing to fix further. The main purpose of that patch is to get rid of the following Kconfig warning: scripts/kconfig/conf -s arch/powerpc/Kconfig drivers/net/Kconfig:1713:warning: 'select' used by config symbol 'CPMAC' refers to undefined symbol 'FIXED_MII_100_FDX' Signed-off-by: Anton Vorontsov Signed-off-by: Kumar Gala --- drivers/net/Kconfig | 4 +--- drivers/net/cpmac.c | 55 ++++++++++++++++++----------------------------------- 2 files changed, 19 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index f234ba3f0404..5cac0914a979 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1738,10 +1738,8 @@ config SC92031 config CPMAC tristate "TI AR7 CPMAC Ethernet support (EXPERIMENTAL)" - depends on NET_ETHERNET && EXPERIMENTAL && AR7 + depends on NET_ETHERNET && EXPERIMENTAL && AR7 && BROKEN select PHYLIB - select FIXED_PHY - select FIXED_MII_100_FDX help TI AR7 CPMAC Ethernet support diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index 6ccebb830ff9..c85194f2cd2d 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -845,15 +845,6 @@ static void cpmac_adjust_link(struct net_device *dev) spin_unlock(&priv->lock); } -static int cpmac_link_update(struct net_device *dev, - struct fixed_phy_status *status) -{ - status->link = 1; - status->speed = 100; - status->duplex = 1; - return 0; -} - static int cpmac_open(struct net_device *dev) { int i, size, res; @@ -996,11 +987,11 @@ static int external_switch; static int __devinit cpmac_probe(struct platform_device *pdev) { int rc, phy_id, i; + int mdio_bus_id = cpmac_mii.id; struct resource *mem; struct cpmac_priv *priv; struct net_device *dev; struct plat_cpmac_data *pdata; - struct fixed_info *fixed_phy; DECLARE_MAC_BUF(mac); pdata = pdev->dev.platform_data; @@ -1014,9 +1005,23 @@ static int __devinit cpmac_probe(struct platform_device *pdev) } if (phy_id == PHY_MAX_ADDR) { - if (external_switch || dumb_switch) + 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; - else { + status.link = 1; + status.duplex = 1; + status.speed = 100; + fixed_phy_add(PHY_POLL, phy_id, &status); + } else { printk(KERN_ERR "cpmac: no PHY present\n"); return -ENODEV; } @@ -1060,32 +1065,8 @@ static int __devinit cpmac_probe(struct platform_device *pdev) priv->msg_enable = netif_msg_init(debug_level, 0xff); memcpy(dev->dev_addr, pdata->dev_addr, sizeof(dev->dev_addr)); - if (phy_id == 31) { - snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT, cpmac_mii.id, - phy_id); - } else { - /* Let's try to get a free fixed phy... */ - for (i = 0; i < MAX_PHY_AMNT; i++) { - fixed_phy = fixed_mdio_get_phydev(i); - if (!fixed_phy) - continue; - if (!fixed_phy->phydev->attached_dev) { - strncpy(priv->phy_name, - fixed_phy->phydev->dev.bus_id, - BUS_ID_SIZE); - fixed_mdio_set_link_update(fixed_phy->phydev, - &cpmac_link_update); - goto phy_found; - } - } - if (netif_msg_drv(priv)) - printk(KERN_ERR "%s: Could not find fixed PHY\n", - dev->name); - rc = -ENODEV; - goto fail; - } + snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); -phy_found: priv->phy = phy_connect(dev, priv->phy_name, &cpmac_adjust_link, 0, PHY_INTERFACE_MODE_MII); if (IS_ERR(priv->phy)) { -- cgit v1.2.3 From f4eb010706b6c96c136c7aaa9079159743f33fa8 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 26 Oct 2007 16:54:31 +1000 Subject: [POWERPC] Add of_get_next_parent() Iterating through a device node's parents is simple enough, but dealing with the refcounts properly is a little ugly, and replicating that logic is asking for someone to get it wrong or forget it all together, eg: while (dn != NULL) { /* loop body */ tmp = of_get_parent(dn); of_node_put(dn); dn = tmp; } So add of_get_next_parent(), inspired by of_get_next_child(). The contract is that it returns the parent and drops the reference on the current node, this makes the loop look like: while (dn != NULL) { /* loop body */ dn = of_get_next_parent(dn); } Signed-off-by: Michael Ellerman Acked-by: David S. Miller Signed-off-by: Paul Mackerras --- drivers/of/base.c | 25 +++++++++++++++++++++++++ include/linux/of.h | 1 + 2 files changed, 26 insertions(+) (limited to 'drivers') diff --git a/drivers/of/base.c b/drivers/of/base.c index b306fef1ac41..80c9deca5f35 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -137,6 +137,31 @@ struct device_node *of_get_parent(const struct device_node *node) } EXPORT_SYMBOL(of_get_parent); +/** + * of_get_next_parent - Iterate to a node's parent + * @node: Node to get parent of + * + * This is like of_get_parent() except that it drops the + * refcount on the passed node, making it suitable for iterating + * through a node's parents. + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_get_next_parent(struct device_node *node) +{ + struct device_node *parent; + + if (!node) + return NULL; + + read_lock(&devtree_lock); + parent = of_node_get(node->parent); + of_node_put(node); + read_unlock(&devtree_lock); + return parent; +} + /** * of_get_next_child - Iterate a node childs * @node: parent node diff --git a/include/linux/of.h b/include/linux/of.h index b5f33efcb8e2..6981016dcc25 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -50,6 +50,7 @@ extern struct device_node *of_find_matching_node(struct device_node *from, extern struct device_node *of_find_node_by_path(const char *path); extern struct device_node *of_find_node_by_phandle(phandle handle); extern struct device_node *of_get_parent(const struct device_node *node); +extern struct device_node *of_get_next_parent(struct device_node *node); extern struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev); #define for_each_child_of_node(parent, child) \ -- cgit v1.2.3 From a09ad3c462e802f0a522969ce377820cfad89ee8 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Fri, 25 Jan 2008 16:59:13 +1100 Subject: [POWERPC] Create and hook up of_platform_device_shutdown Although of_platform_device's can have a shutdown routine, at the moment the bus code doesn't actually call it. So add the required glue to hook the shutdown routine. Signed-off-by: Michael Ellerman Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- drivers/of/platform.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/of/platform.c b/drivers/of/platform.c index b47bb2d7476a..ca09a63a64db 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -85,6 +85,15 @@ static int of_platform_device_resume(struct device * dev) return error; } +static void of_platform_device_shutdown(struct device *dev) +{ + struct of_device *of_dev = to_of_device(dev); + struct of_platform_driver *drv = to_of_platform_driver(dev->driver); + + if (dev->driver && drv->shutdown) + drv->shutdown(of_dev); +} + int of_bus_type_init(struct bus_type *bus, const char *name) { bus->name = name; @@ -93,6 +102,7 @@ int of_bus_type_init(struct bus_type *bus, const char *name) bus->remove = of_platform_device_remove; bus->suspend = of_platform_device_suspend; bus->resume = of_platform_device_resume; + bus->shutdown = of_platform_device_shutdown; return bus_register(bus); } -- cgit v1.2.3 From c721192778786ef8f16ece363927b8b68864201a Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 5 Feb 2008 14:17:31 +1100 Subject: [POWERPC] iSeries: Fix section mismatch in viocd WARNING: drivers/cdrom/viocd.o(.text+0x504): Section mismatch in reference from the function .viocd_probe() to the function .init.text:.find_capability() Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras --- drivers/cdrom/viocd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cdrom/viocd.c b/drivers/cdrom/viocd.c index 8473b9f1da96..cac06bc1754b 100644 --- a/drivers/cdrom/viocd.c +++ b/drivers/cdrom/viocd.c @@ -558,7 +558,7 @@ static struct cdrom_device_ops viocd_dops = { .capability = CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_DRIVE_STATUS | CDC_GENERIC_PACKET | CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | CDC_RAM }; -static int __init find_capability(const char *type) +static int find_capability(const char *type) { struct capability_entry *entry; -- cgit v1.2.3 From 911a3175997000c1fcddb2013aaa5fbbee79f0f0 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 6 Feb 2008 10:23:12 -0700 Subject: [POWERPC] Fix incorrectly tagged __devinitdata structures Fix compile errors in the xilinxfb, xsysace and uartlite drivers used by the Xilinx Virtex platform Signed-off-by: Grant Likely Acked-by: Peter Korsgaard --- drivers/block/xsysace.c | 2 +- drivers/serial/uartlite.c | 2 +- drivers/video/xilinxfb.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index 78ebfffc77e3..1110e1b60712 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -1202,7 +1202,7 @@ static int __devexit ace_of_remove(struct of_device *op) } /* Match table for of_platform binding */ -static struct of_device_id __devinit ace_of_match[] = { +static struct of_device_id ace_of_match[] __devinitdata = { { .compatible = "xilinx,xsysace", }, {}, }; diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index 80943409edb0..c54a5ad992b1 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -618,7 +618,7 @@ static int __devexit ulite_of_remove(struct of_device *op) } /* Match table for of_platform binding */ -static struct of_device_id __devinit ulite_of_match[] = { +static struct of_device_id ulite_of_match[] __devinitdata = { { .type = "serial", .compatible = "xilinx,uartlite", }, {}, }; diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c index e38d3b7c3ad7..c92e99eced29 100644 --- a/drivers/video/xilinxfb.c +++ b/drivers/video/xilinxfb.c @@ -459,7 +459,7 @@ static int __devexit xilinxfb_of_remove(struct of_device *op) } /* Match table for of_platform binding */ -static struct of_device_id __devinit xilinxfb_of_match[] = { +static struct of_device_id xilinxfb_of_match[] __devinitdata = { { .compatible = "xilinx,ml300-fb", }, {}, }; -- cgit v1.2.3 From 0e349b0e2d90eb1bb76d16c48d0127feebfeeb89 Mon Sep 17 00:00:00 2001 From: Stephen Neuendorffer Date: Wed, 9 Jan 2008 06:35:05 +1100 Subject: [POWERPC] Xilinx: Update compatible to use values generated by BSP generator. Mainly, this involves two changes: 1) xilinx->xlnx (recognized standard is to use the stock ticker) 2) In order to have the device tree focus on describing what the hardware is as exactly as possible, the compatible strings contain the full IP name and IP version. Signed-off-by: Stephen Neuendorffer Acked-by: Peter Korsgaard Signed-off-by: Grant Likely --- arch/powerpc/platforms/40x/virtex.c | 2 +- drivers/block/xsysace.c | 4 +++- drivers/serial/uartlite.c | 23 +++++++++++++++-------- drivers/video/xilinxfb.c | 2 +- 4 files changed, 20 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/arch/powerpc/platforms/40x/virtex.c b/arch/powerpc/platforms/40x/virtex.c index 88b66444dfb2..0422590040db 100644 --- a/arch/powerpc/platforms/40x/virtex.c +++ b/arch/powerpc/platforms/40x/virtex.c @@ -37,7 +37,7 @@ static int __init virtex_probe(void) { unsigned long root = of_get_flat_dt_root(); - if (!of_flat_dt_is_compatible(root, "xilinx,virtex")) + if (!of_flat_dt_is_compatible(root, "xlnx,virtex")) return 0; return 1; diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index 1110e1b60712..4a7a059ebaf7 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -1203,7 +1203,9 @@ static int __devexit ace_of_remove(struct of_device *op) /* Match table for of_platform binding */ static struct of_device_id ace_of_match[] __devinitdata = { - { .compatible = "xilinx,xsysace", }, + { .compatible = "xlnx,opb-sysace-1.00.b", }, + { .compatible = "xlnx,opb-sysace-1.00.c", }, + { .compatible = "xlnx,xps-sysace-1.00.a", }, {}, }; MODULE_DEVICE_TABLE(of, ace_of_match); diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index c54a5ad992b1..b22023f1ec6b 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -17,10 +17,21 @@ #include #include #include +#include #include #if defined(CONFIG_OF) +#include #include #include + +/* Match table for of_platform binding */ +static struct of_device_id ulite_of_match[] __devinitdata = { + { .compatible = "xlnx,opb-uartlite-1.00.b", }, + { .compatible = "xlnx,xps-uartlite-1.00.a", }, + {} +}; +MODULE_DEVICE_TABLE(of, ulite_of_match); + #endif #define ULITE_NAME "ttyUL" @@ -275,6 +286,9 @@ static void ulite_release_port(struct uart_port *port) static int ulite_request_port(struct uart_port *port) { + pr_debug("ulite console: port=%p; port->mapbase=%x\n", + port, port->mapbase); + if (!request_mem_region(port->mapbase, ULITE_REGION, "uartlite")) { dev_err(port->dev, "Memory region busy\n"); return -EBUSY; @@ -383,7 +397,7 @@ static inline void __init ulite_console_of_find_device(int id) const unsigned int *of_id; int rc; - for_each_compatible_node(np, NULL, "xilinx,uartlite") { + for_each_matching_node(np, ulite_of_match) { of_id = of_get_property(np, "port-number", NULL); if ((!of_id) || (*of_id != id)) continue; @@ -617,13 +631,6 @@ static int __devexit ulite_of_remove(struct of_device *op) return ulite_release(&op->dev); } -/* Match table for of_platform binding */ -static struct of_device_id ulite_of_match[] __devinitdata = { - { .type = "serial", .compatible = "xilinx,uartlite", }, - {}, -}; -MODULE_DEVICE_TABLE(of, ulite_of_match); - static struct of_platform_driver ulite_of_driver = { .owner = THIS_MODULE, .name = "uartlite", diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c index c92e99eced29..7b3a8423f485 100644 --- a/drivers/video/xilinxfb.c +++ b/drivers/video/xilinxfb.c @@ -460,7 +460,7 @@ static int __devexit xilinxfb_of_remove(struct of_device *op) /* Match table for of_platform binding */ static struct of_device_id xilinxfb_of_match[] __devinitdata = { - { .compatible = "xilinx,ml300-fb", }, + { .compatible = "xlnx,plb-tft-cntlr-ref-1.00.a", }, {}, }; MODULE_DEVICE_TABLE(of, xilinxfb_of_match); -- cgit v1.2.3 From 3de66a175d2014246a2e990412e5750922e5c7d8 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 6 Feb 2008 10:23:41 -0700 Subject: [POWERPC] Eliminate broken OF console initialization. Probing of the console at console_initcall time is broken. It tries to call memory allocation routines which aren't initialized yet. Problem solved by removing the early probe entirely. The console init is called again anyway after the uartlite device is initialized and the memory allocation routines can be called safely. Signed-off-by: Grant Likely Acked-by: Peter Korsgaard --- drivers/serial/uartlite.c | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index b22023f1ec6b..1a7bccebd503 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -389,32 +389,6 @@ static void ulite_console_write(struct console *co, const char *s, spin_unlock_irqrestore(&port->lock, flags); } -#if defined(CONFIG_OF) -static inline void __init ulite_console_of_find_device(int id) -{ - struct device_node *np; - struct resource res; - const unsigned int *of_id; - int rc; - - for_each_matching_node(np, ulite_of_match) { - of_id = of_get_property(np, "port-number", NULL); - if ((!of_id) || (*of_id != id)) - continue; - - rc = of_address_to_resource(np, 0, &res); - if (rc) - continue; - - ulite_ports[id].mapbase = res.start; - of_node_put(np); - return; - } -} -#else /* CONFIG_OF */ -static inline void __init ulite_console_of_find_device(int id) { /* do nothing */ } -#endif /* CONFIG_OF */ - static int __init ulite_console_setup(struct console *co, char *options) { struct uart_port *port; @@ -428,11 +402,7 @@ static int __init ulite_console_setup(struct console *co, char *options) port = &ulite_ports[co->index]; - /* Check if it is an OF device */ - if (!port->mapbase) - ulite_console_of_find_device(co->index); - - /* Do we have a device now? */ + /* Has the device been initialized yet? */ if (!port->mapbase) { pr_debug("console on ttyUL%i not present\n", co->index); return -ENODEV; -- cgit v1.2.3 From ef141a0bb0dc6172bb8fe5408cf8adbd5f76ff45 Mon Sep 17 00:00:00 2001 From: Stephen Neuendorffer Date: Wed, 6 Feb 2008 04:24:09 +1100 Subject: [POWERPC] Xilinx: hwicap driver This includes code for new fifo-based xps_hwicap in addition to the older opb_hwicap, which has a significantly different interface. The common code between the two drivers is largely shared. Significant differences exists between this driver and what is supported in the EDK drivers. In particular, most of the architecture-specific code for reconfiguring individual FPGA resources has been removed. This functionality is likely better provided in a user-space support library. In addition, read and write access is supported. In addition, although the xps_hwicap cores support interrupt-driver mode, this driver only supports polled operation, in order to make the code simpler, and since the interrupt processing overhead is likely to slow down the throughput under Linux. Signed-off-by: Stephen Neuendorffer Signed-off-by: Grant Likely --- drivers/char/Kconfig | 10 + drivers/char/Makefile | 1 + drivers/char/xilinx_hwicap/Makefile | 7 + drivers/char/xilinx_hwicap/buffer_icap.c | 380 ++++++++++++ drivers/char/xilinx_hwicap/buffer_icap.h | 57 ++ drivers/char/xilinx_hwicap/fifo_icap.c | 381 ++++++++++++ drivers/char/xilinx_hwicap/fifo_icap.h | 62 ++ drivers/char/xilinx_hwicap/xilinx_hwicap.c | 904 +++++++++++++++++++++++++++++ drivers/char/xilinx_hwicap/xilinx_hwicap.h | 193 ++++++ 9 files changed, 1995 insertions(+) create mode 100644 drivers/char/xilinx_hwicap/Makefile create mode 100644 drivers/char/xilinx_hwicap/buffer_icap.c create mode 100644 drivers/char/xilinx_hwicap/buffer_icap.h create mode 100644 drivers/char/xilinx_hwicap/fifo_icap.c create mode 100644 drivers/char/xilinx_hwicap/fifo_icap.h create mode 100644 drivers/char/xilinx_hwicap/xilinx_hwicap.c create mode 100644 drivers/char/xilinx_hwicap/xilinx_hwicap.h (limited to 'drivers') diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 466629594776..be8371ac3086 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -841,6 +841,16 @@ config DTLK To compile this driver as a module, choose M here: the module will be called dtlk. +config XILINX_HWICAP + tristate "Xilinx HWICAP Support" + depends on XILINX_VIRTEX + help + This option enables support for Xilinx Internal Configuration + Access Port (ICAP) driver. The ICAP is used on Xilinx Virtex + FPGA platforms to partially reconfigure the FPGA at runtime. + + If unsure, say N. + config R3964 tristate "Siemens R3964 line discipline" ---help--- diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 96fc01eddefe..686fabbd85cd 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_EFI_RTC) += efirtc.o obj-$(CONFIG_SGI_DS1286) += ds1286.o obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o obj-$(CONFIG_DS1302) += ds1302.o +obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap/ ifeq ($(CONFIG_GENERIC_NVRAM),y) obj-$(CONFIG_NVRAM) += generic_nvram.o else diff --git a/drivers/char/xilinx_hwicap/Makefile b/drivers/char/xilinx_hwicap/Makefile new file mode 100644 index 000000000000..5491cbc42f43 --- /dev/null +++ b/drivers/char/xilinx_hwicap/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Xilinx OPB hwicap driver +# + +obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap_m.o + +xilinx_hwicap_m-y := xilinx_hwicap.o fifo_icap.o buffer_icap.o diff --git a/drivers/char/xilinx_hwicap/buffer_icap.c b/drivers/char/xilinx_hwicap/buffer_icap.c new file mode 100644 index 000000000000..dfea2bde162b --- /dev/null +++ b/drivers/char/xilinx_hwicap/buffer_icap.c @@ -0,0 +1,380 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2003-2008 Xilinx Inc. + * All rights reserved. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#include "buffer_icap.h" + +/* Indicates how many bytes will fit in a buffer. (1 BRAM) */ +#define XHI_MAX_BUFFER_BYTES 2048 +#define XHI_MAX_BUFFER_INTS (XHI_MAX_BUFFER_BYTES >> 2) + +/* File access and error constants */ +#define XHI_DEVICE_READ_ERROR -1 +#define XHI_DEVICE_WRITE_ERROR -2 +#define XHI_BUFFER_OVERFLOW_ERROR -3 + +#define XHI_DEVICE_READ 0x1 +#define XHI_DEVICE_WRITE 0x0 + +/* Constants for checking transfer status */ +#define XHI_CYCLE_DONE 0 +#define XHI_CYCLE_EXECUTING 1 + +/* buffer_icap register offsets */ + +/* Size of transfer, read & write */ +#define XHI_SIZE_REG_OFFSET 0x800L +/* offset into bram, read & write */ +#define XHI_BRAM_OFFSET_REG_OFFSET 0x804L +/* Read not Configure, direction of transfer. Write only */ +#define XHI_RNC_REG_OFFSET 0x808L +/* Indicates transfer complete. Read only */ +#define XHI_STATUS_REG_OFFSET 0x80CL + +/* Constants for setting the RNC register */ +#define XHI_CONFIGURE 0x0UL +#define XHI_READBACK 0x1UL + +/* Constants for the Done register */ +#define XHI_NOT_FINISHED 0x0UL +#define XHI_FINISHED 0x1UL + +#define XHI_BUFFER_START 0 + +/** + * buffer_icap_get_status: Get the contents of the status register. + * @parameter base_address: is the base address of the device + * + * The status register contains the ICAP status and the done bit. + * + * D8 - cfgerr + * D7 - dalign + * D6 - rip + * D5 - in_abort_l + * D4 - Always 1 + * D3 - Always 1 + * D2 - Always 1 + * D1 - Always 1 + * D0 - Done bit + **/ +static inline u32 buffer_icap_get_status(void __iomem *base_address) +{ + return in_be32(base_address + XHI_STATUS_REG_OFFSET); +} + +/** + * buffer_icap_get_bram: Reads data from the storage buffer bram. + * @parameter base_address: contains the base address of the component. + * @parameter offset: The word offset from which the data should be read. + * + * A bram is used as a configuration memory cache. One frame of data can + * be stored in this "storage buffer". + **/ +static inline u32 buffer_icap_get_bram(void __iomem *base_address, + u32 offset) +{ + return in_be32(base_address + (offset << 2)); +} + +/** + * buffer_icap_busy: Return true if the icap device is busy + * @parameter base_address: is the base address of the device + * + * The queries the low order bit of the status register, which + * indicates whether the current configuration or readback operation + * has completed. + **/ +static inline bool buffer_icap_busy(void __iomem *base_address) +{ + return (buffer_icap_get_status(base_address) & 1) == XHI_NOT_FINISHED; +} + +/** + * buffer_icap_busy: Return true if the icap device is not busy + * @parameter base_address: is the base address of the device + * + * The queries the low order bit of the status register, which + * indicates whether the current configuration or readback operation + * has completed. + **/ +static inline bool buffer_icap_done(void __iomem *base_address) +{ + return (buffer_icap_get_status(base_address) & 1) == XHI_FINISHED; +} + +/** + * buffer_icap_set_size: Set the size register. + * @parameter base_address: is the base address of the device + * @parameter data: The size in bytes. + * + * The size register holds the number of 8 bit bytes to transfer between + * bram and the icap (or icap to bram). + **/ +static inline void buffer_icap_set_size(void __iomem *base_address, + u32 data) +{ + out_be32(base_address + XHI_SIZE_REG_OFFSET, data); +} + +/** + * buffer_icap_mSetoffsetReg: Set the bram offset register. + * @parameter base_address: contains the base address of the device. + * @parameter data: is the value to be written to the data register. + * + * The bram offset register holds the starting bram address to transfer + * data from during configuration or write data to during readback. + **/ +static inline void buffer_icap_set_offset(void __iomem *base_address, + u32 data) +{ + out_be32(base_address + XHI_BRAM_OFFSET_REG_OFFSET, data); +} + +/** + * buffer_icap_set_rnc: Set the RNC (Readback not Configure) register. + * @parameter base_address: contains the base address of the device. + * @parameter data: is the value to be written to the data register. + * + * The RNC register determines the direction of the data transfer. It + * controls whether a configuration or readback take place. Writing to + * this register initiates the transfer. A value of 1 initiates a + * readback while writing a value of 0 initiates a configuration. + **/ +static inline void buffer_icap_set_rnc(void __iomem *base_address, + u32 data) +{ + out_be32(base_address + XHI_RNC_REG_OFFSET, data); +} + +/** + * buffer_icap_set_bram: Write data to the storage buffer bram. + * @parameter base_address: contains the base address of the component. + * @parameter offset: The word offset at which the data should be written. + * @parameter data: The value to be written to the bram offset. + * + * A bram is used as a configuration memory cache. One frame of data can + * be stored in this "storage buffer". + **/ +static inline void buffer_icap_set_bram(void __iomem *base_address, + u32 offset, u32 data) +{ + out_be32(base_address + (offset << 2), data); +} + +/** + * buffer_icap_device_read: Transfer bytes from ICAP to the storage buffer. + * @parameter drvdata: a pointer to the drvdata. + * @parameter offset: The storage buffer start address. + * @parameter count: The number of words (32 bit) to read from the + * device (ICAP). + **/ +static int buffer_icap_device_read(struct hwicap_drvdata *drvdata, + u32 offset, u32 count) +{ + + s32 retries = 0; + void __iomem *base_address = drvdata->base_address; + + if (buffer_icap_busy(base_address)) + return -EBUSY; + + if ((offset + count) > XHI_MAX_BUFFER_INTS) + return -EINVAL; + + /* setSize count*4 to get bytes. */ + buffer_icap_set_size(base_address, (count << 2)); + buffer_icap_set_offset(base_address, offset); + buffer_icap_set_rnc(base_address, XHI_READBACK); + + while (buffer_icap_busy(base_address)) { + retries++; + if (retries > XHI_MAX_RETRIES) + return -EBUSY; + } + return 0; + +}; + +/** + * buffer_icap_device_write: Transfer bytes from ICAP to the storage buffer. + * @parameter drvdata: a pointer to the drvdata. + * @parameter offset: The storage buffer start address. + * @parameter count: The number of words (32 bit) to read from the + * device (ICAP). + **/ +static int buffer_icap_device_write(struct hwicap_drvdata *drvdata, + u32 offset, u32 count) +{ + + s32 retries = 0; + void __iomem *base_address = drvdata->base_address; + + if (buffer_icap_busy(base_address)) + return -EBUSY; + + if ((offset + count) > XHI_MAX_BUFFER_INTS) + return -EINVAL; + + /* setSize count*4 to get bytes. */ + buffer_icap_set_size(base_address, count << 2); + buffer_icap_set_offset(base_address, offset); + buffer_icap_set_rnc(base_address, XHI_CONFIGURE); + + while (buffer_icap_busy(base_address)) { + retries++; + if (retries > XHI_MAX_RETRIES) + return -EBUSY; + } + return 0; + +}; + +/** + * buffer_icap_reset: Reset the logic of the icap device. + * @parameter drvdata: a pointer to the drvdata. + * + * Writing to the status register resets the ICAP logic in an internal + * version of the core. For the version of the core published in EDK, + * this is a noop. + **/ +void buffer_icap_reset(struct hwicap_drvdata *drvdata) +{ + out_be32(drvdata->base_address + XHI_STATUS_REG_OFFSET, 0xFEFE); +} + +/** + * buffer_icap_set_configuration: Load a partial bitstream from system memory. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: Kernel address of the partial bitstream. + * @parameter size: the size of the partial bitstream in 32 bit words. + **/ +int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data, + u32 size) +{ + int status; + s32 buffer_count = 0; + s32 num_writes = 0; + bool dirty = 0; + u32 i; + void __iomem *base_address = drvdata->base_address; + + /* Loop through all the data */ + for (i = 0, buffer_count = 0; i < size; i++) { + + /* Copy data to bram */ + buffer_icap_set_bram(base_address, buffer_count, data[i]); + dirty = 1; + + if (buffer_count < XHI_MAX_BUFFER_INTS - 1) { + buffer_count++; + continue; + } + + /* Write data to ICAP */ + status = buffer_icap_device_write( + drvdata, + XHI_BUFFER_START, + XHI_MAX_BUFFER_INTS); + if (status != 0) { + /* abort. */ + buffer_icap_reset(drvdata); + return status; + } + + buffer_count = 0; + num_writes++; + dirty = 0; + } + + /* Write unwritten data to ICAP */ + if (dirty) { + /* Write data to ICAP */ + status = buffer_icap_device_write(drvdata, XHI_BUFFER_START, + buffer_count); + if (status != 0) { + /* abort. */ + buffer_icap_reset(drvdata); + } + return status; + } + + return 0; +}; + +/** + * buffer_icap_get_configuration: Read configuration data from the device. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: Address of the data representing the partial bitstream + * @parameter size: the size of the partial bitstream in 32 bit words. + **/ +int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data, + u32 size) +{ + int status; + s32 buffer_count = 0; + s32 read_count = 0; + u32 i; + void __iomem *base_address = drvdata->base_address; + + /* Loop through all the data */ + for (i = 0, buffer_count = XHI_MAX_BUFFER_INTS; i < size; i++) { + if (buffer_count == XHI_MAX_BUFFER_INTS) { + u32 words_remaining = size - i; + u32 words_to_read = + words_remaining < + XHI_MAX_BUFFER_INTS ? words_remaining : + XHI_MAX_BUFFER_INTS; + + /* Read data from ICAP */ + status = buffer_icap_device_read( + drvdata, + XHI_BUFFER_START, + words_to_read); + if (status != 0) { + /* abort. */ + buffer_icap_reset(drvdata); + return status; + } + + buffer_count = 0; + read_count++; + } + + /* Copy data from bram */ + data[i] = buffer_icap_get_bram(base_address, buffer_count); + buffer_count++; + } + + return 0; +}; diff --git a/drivers/char/xilinx_hwicap/buffer_icap.h b/drivers/char/xilinx_hwicap/buffer_icap.h new file mode 100644 index 000000000000..03184959fa00 --- /dev/null +++ b/drivers/char/xilinx_hwicap/buffer_icap.h @@ -0,0 +1,57 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2003-2008 Xilinx Inc. + * All rights reserved. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#ifndef XILINX_BUFFER_ICAP_H_ /* prevent circular inclusions */ +#define XILINX_BUFFER_ICAP_H_ /* by using protection macros */ + +#include +#include +#include +#include + +#include +#include "xilinx_hwicap.h" + +void buffer_icap_reset(struct hwicap_drvdata *drvdata); + +/* Loads a partial bitstream from system memory. */ +int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data, + u32 Size); + +/* Loads a partial bitstream from system memory. */ +int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data, + u32 Size); + +#endif diff --git a/drivers/char/xilinx_hwicap/fifo_icap.c b/drivers/char/xilinx_hwicap/fifo_icap.c new file mode 100644 index 000000000000..0988314694a6 --- /dev/null +++ b/drivers/char/xilinx_hwicap/fifo_icap.c @@ -0,0 +1,381 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2007-2008 Xilinx Inc. + * All rights reserved. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#include "fifo_icap.h" + +/* Register offsets for the XHwIcap device. */ +#define XHI_GIER_OFFSET 0x1C /* Device Global Interrupt Enable Reg */ +#define XHI_IPISR_OFFSET 0x20 /* Interrupt Status Register */ +#define XHI_IPIER_OFFSET 0x28 /* Interrupt Enable Register */ +#define XHI_WF_OFFSET 0x100 /* Write FIFO */ +#define XHI_RF_OFFSET 0x104 /* Read FIFO */ +#define XHI_SZ_OFFSET 0x108 /* Size Register */ +#define XHI_CR_OFFSET 0x10C /* Control Register */ +#define XHI_SR_OFFSET 0x110 /* Status Register */ +#define XHI_WFV_OFFSET 0x114 /* Write FIFO Vacancy Register */ +#define XHI_RFO_OFFSET 0x118 /* Read FIFO Occupancy Register */ + +/* Device Global Interrupt Enable Register (GIER) bit definitions */ + +#define XHI_GIER_GIE_MASK 0x80000000 /* Global Interrupt enable Mask */ + +/** + * HwIcap Device Interrupt Status/Enable Registers + * + * Interrupt Status Register (IPISR) : This register holds the + * interrupt status flags for the device. These bits are toggle on + * write. + * + * Interrupt Enable Register (IPIER) : This register is used to enable + * interrupt sources for the device. + * Writing a '1' to a bit enables the corresponding interrupt. + * Writing a '0' to a bit disables the corresponding interrupt. + * + * IPISR/IPIER registers have the same bit definitions and are only defined + * once. + */ +#define XHI_IPIXR_RFULL_MASK 0x00000008 /* Read FIFO Full */ +#define XHI_IPIXR_WEMPTY_MASK 0x00000004 /* Write FIFO Empty */ +#define XHI_IPIXR_RDP_MASK 0x00000002 /* Read FIFO half full */ +#define XHI_IPIXR_WRP_MASK 0x00000001 /* Write FIFO half full */ +#define XHI_IPIXR_ALL_MASK 0x0000000F /* Mask of all interrupts */ + +/* Control Register (CR) */ +#define XHI_CR_SW_RESET_MASK 0x00000008 /* SW Reset Mask */ +#define XHI_CR_FIFO_CLR_MASK 0x00000004 /* FIFO Clear Mask */ +#define XHI_CR_READ_MASK 0x00000002 /* Read from ICAP to FIFO */ +#define XHI_CR_WRITE_MASK 0x00000001 /* Write from FIFO to ICAP */ + +/* Status Register (SR) */ +#define XHI_SR_CFGERR_N_MASK 0x00000100 /* Config Error Mask */ +#define XHI_SR_DALIGN_MASK 0x00000080 /* Data Alignment Mask */ +#define XHI_SR_RIP_MASK 0x00000040 /* Read back Mask */ +#define XHI_SR_IN_ABORT_N_MASK 0x00000020 /* Select Map Abort Mask */ +#define XHI_SR_DONE_MASK 0x00000001 /* Done bit Mask */ + + +#define XHI_WFO_MAX_VACANCY 1024 /* Max Write FIFO Vacancy, in words */ +#define XHI_RFO_MAX_OCCUPANCY 256 /* Max Read FIFO Occupancy, in words */ +/* The maximum amount we can request from fifo_icap_get_configuration + at once, in bytes. */ +#define XHI_MAX_READ_TRANSACTION_WORDS 0xFFF + + +/** + * fifo_icap_fifo_write: Write data to the write FIFO. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: the 32-bit value to be written to the FIFO. + * + * This function will silently fail if the fifo is full. + **/ +static inline void fifo_icap_fifo_write(struct hwicap_drvdata *drvdata, + u32 data) +{ + dev_dbg(drvdata->dev, "fifo_write: %x\n", data); + out_be32(drvdata->base_address + XHI_WF_OFFSET, data); +} + +/** + * fifo_icap_fifo_read: Read data from the Read FIFO. + * @parameter drvdata: a pointer to the drvdata. + * + * This function will silently fail if the fifo is empty. + **/ +static inline u32 fifo_icap_fifo_read(struct hwicap_drvdata *drvdata) +{ + u32 data = in_be32(drvdata->base_address + XHI_RF_OFFSET); + dev_dbg(drvdata->dev, "fifo_read: %x\n", data); + return data; +} + +/** + * fifo_icap_set_read_size: Set the the size register. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: the size of the following read transaction, in words. + **/ +static inline void fifo_icap_set_read_size(struct hwicap_drvdata *drvdata, + u32 data) +{ + out_be32(drvdata->base_address + XHI_SZ_OFFSET, data); +} + +/** + * fifo_icap_start_config: Initiate a configuration (write) to the device. + * @parameter drvdata: a pointer to the drvdata. + **/ +static inline void fifo_icap_start_config(struct hwicap_drvdata *drvdata) +{ + out_be32(drvdata->base_address + XHI_CR_OFFSET, XHI_CR_WRITE_MASK); + dev_dbg(drvdata->dev, "configuration started\n"); +} + +/** + * fifo_icap_start_readback: Initiate a readback from the device. + * @parameter drvdata: a pointer to the drvdata. + **/ +static inline void fifo_icap_start_readback(struct hwicap_drvdata *drvdata) +{ + out_be32(drvdata->base_address + XHI_CR_OFFSET, XHI_CR_READ_MASK); + dev_dbg(drvdata->dev, "readback started\n"); +} + +/** + * fifo_icap_busy: Return true if the ICAP is still processing a transaction. + * @parameter drvdata: a pointer to the drvdata. + **/ +static inline u32 fifo_icap_busy(struct hwicap_drvdata *drvdata) +{ + u32 status = in_be32(drvdata->base_address + XHI_SR_OFFSET); + dev_dbg(drvdata->dev, "Getting status = %x\n", status); + return (status & XHI_SR_DONE_MASK) ? 0 : 1; +} + +/** + * fifo_icap_write_fifo_vacancy: Query the write fifo available space. + * @parameter drvdata: a pointer to the drvdata. + * + * Return the number of words that can be safely pushed into the write fifo. + **/ +static inline u32 fifo_icap_write_fifo_vacancy( + struct hwicap_drvdata *drvdata) +{ + return in_be32(drvdata->base_address + XHI_WFV_OFFSET); +} + +/** + * fifo_icap_read_fifo_occupancy: Query the read fifo available data. + * @parameter drvdata: a pointer to the drvdata. + * + * Return the number of words that can be safely read from the read fifo. + **/ +static inline u32 fifo_icap_read_fifo_occupancy( + struct hwicap_drvdata *drvdata) +{ + return in_be32(drvdata->base_address + XHI_RFO_OFFSET); +} + +/** + * fifo_icap_set_configuration: Send configuration data to the ICAP. + * @parameter drvdata: a pointer to the drvdata. + * @parameter frame_buffer: a pointer to the data to be written to the + * ICAP device. + * @parameter num_words: the number of words (32 bit) to write to the ICAP + * device. + + * This function writes the given user data to the Write FIFO in + * polled mode and starts the transfer of the data to + * the ICAP device. + **/ +int fifo_icap_set_configuration(struct hwicap_drvdata *drvdata, + u32 *frame_buffer, u32 num_words) +{ + + u32 write_fifo_vacancy = 0; + u32 retries = 0; + u32 remaining_words; + + dev_dbg(drvdata->dev, "fifo_set_configuration\n"); + + /* + * Check if the ICAP device is Busy with the last Read/Write + */ + if (fifo_icap_busy(drvdata)) + return -EBUSY; + + /* + * Set up the buffer pointer and the words to be transferred. + */ + remaining_words = num_words; + + while (remaining_words > 0) { + /* + * Wait until we have some data in the fifo. + */ + while (write_fifo_vacancy == 0) { + write_fifo_vacancy = + fifo_icap_write_fifo_vacancy(drvdata); + retries++; + if (retries > XHI_MAX_RETRIES) + return -EIO; + } + + /* + * Write data into the Write FIFO. + */ + while ((write_fifo_vacancy != 0) && + (remaining_words > 0)) { + fifo_icap_fifo_write(drvdata, *frame_buffer); + + remaining_words--; + write_fifo_vacancy--; + frame_buffer++; + } + /* Start pushing whatever is in the FIFO into the ICAP. */ + fifo_icap_start_config(drvdata); + } + + /* Wait until the write has finished. */ + while (fifo_icap_busy(drvdata)) { + retries++; + if (retries > XHI_MAX_RETRIES) + break; + } + + dev_dbg(drvdata->dev, "done fifo_set_configuration\n"); + + /* + * If the requested number of words have not been read from + * the device then indicate failure. + */ + if (remaining_words != 0) + return -EIO; + + return 0; +} + +/** + * fifo_icap_get_configuration: Read configuration data from the device. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: Address of the data representing the partial bitstream + * @parameter size: the size of the partial bitstream in 32 bit words. + * + * This function reads the specified number of words from the ICAP device in + * the polled mode. + */ +int fifo_icap_get_configuration(struct hwicap_drvdata *drvdata, + u32 *frame_buffer, u32 num_words) +{ + + u32 read_fifo_occupancy = 0; + u32 retries = 0; + u32 *data = frame_buffer; + u32 remaining_words; + u32 words_to_read; + + dev_dbg(drvdata->dev, "fifo_get_configuration\n"); + + /* + * Check if the ICAP device is Busy with the last Write/Read + */ + if (fifo_icap_busy(drvdata)) + return -EBUSY; + + remaining_words = num_words; + + while (remaining_words > 0) { + words_to_read = remaining_words; + /* The hardware has a limit on the number of words + that can be read at one time. */ + if (words_to_read > XHI_MAX_READ_TRANSACTION_WORDS) + words_to_read = XHI_MAX_READ_TRANSACTION_WORDS; + + remaining_words -= words_to_read; + + fifo_icap_set_read_size(drvdata, words_to_read); + fifo_icap_start_readback(drvdata); + + while (words_to_read > 0) { + /* Wait until we have some data in the fifo. */ + while (read_fifo_occupancy == 0) { + read_fifo_occupancy = + fifo_icap_read_fifo_occupancy(drvdata); + retries++; + if (retries > XHI_MAX_RETRIES) + return -EIO; + } + + if (read_fifo_occupancy > words_to_read) + read_fifo_occupancy = words_to_read; + + words_to_read -= read_fifo_occupancy; + + /* Read the data from the Read FIFO. */ + while (read_fifo_occupancy != 0) { + *data++ = fifo_icap_fifo_read(drvdata); + read_fifo_occupancy--; + } + } + } + + dev_dbg(drvdata->dev, "done fifo_get_configuration\n"); + + return 0; +} + +/** + * buffer_icap_reset: Reset the logic of the icap device. + * @parameter drvdata: a pointer to the drvdata. + * + * This function forces the software reset of the complete HWICAP device. + * All the registers will return to the default value and the FIFO is also + * flushed as a part of this software reset. + */ +void fifo_icap_reset(struct hwicap_drvdata *drvdata) +{ + u32 reg_data; + /* + * Reset the device by setting/clearing the RESET bit in the + * Control Register. + */ + reg_data = in_be32(drvdata->base_address + XHI_CR_OFFSET); + + out_be32(drvdata->base_address + XHI_CR_OFFSET, + reg_data | XHI_CR_SW_RESET_MASK); + + out_be32(drvdata->base_address + XHI_CR_OFFSET, + reg_data & (~XHI_CR_SW_RESET_MASK)); + +} + +/** + * fifo_icap_flush_fifo: This function flushes the FIFOs in the device. + * @parameter drvdata: a pointer to the drvdata. + */ +void fifo_icap_flush_fifo(struct hwicap_drvdata *drvdata) +{ + u32 reg_data; + /* + * Flush the FIFO by setting/clearing the FIFO Clear bit in the + * Control Register. + */ + reg_data = in_be32(drvdata->base_address + XHI_CR_OFFSET); + + out_be32(drvdata->base_address + XHI_CR_OFFSET, + reg_data | XHI_CR_FIFO_CLR_MASK); + + out_be32(drvdata->base_address + XHI_CR_OFFSET, + reg_data & (~XHI_CR_FIFO_CLR_MASK)); +} + diff --git a/drivers/char/xilinx_hwicap/fifo_icap.h b/drivers/char/xilinx_hwicap/fifo_icap.h new file mode 100644 index 000000000000..4d3068dd0405 --- /dev/null +++ b/drivers/char/xilinx_hwicap/fifo_icap.h @@ -0,0 +1,62 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2007-2008 Xilinx Inc. + * All rights reserved. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#ifndef XILINX_FIFO_ICAP_H_ /* prevent circular inclusions */ +#define XILINX_FIFO_ICAP_H_ /* by using protection macros */ + +#include +#include +#include +#include + +#include +#include "xilinx_hwicap.h" + +/* Reads integers from the device into the storage buffer. */ +int fifo_icap_get_configuration( + struct hwicap_drvdata *drvdata, + u32 *FrameBuffer, + u32 NumWords); + +/* Writes integers to the device from the storage buffer. */ +int fifo_icap_set_configuration( + struct hwicap_drvdata *drvdata, + u32 *FrameBuffer, + u32 NumWords); + +void fifo_icap_reset(struct hwicap_drvdata *drvdata); +void fifo_icap_flush_fifo(struct hwicap_drvdata *drvdata); + +#endif diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c new file mode 100644 index 000000000000..24f6aef0fd3c --- /dev/null +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -0,0 +1,904 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2002 Xilinx Inc., Systems Engineering Group + * (c) Copyright 2004 Xilinx Inc., Systems Engineering Group + * (c) Copyright 2007-2008 Xilinx Inc. + * All rights reserved. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +/* + * This is the code behind /dev/xilinx_icap -- it allows a user-space + * application to use the Xilinx ICAP subsystem. + * + * The following operations are possible: + * + * open open the port and initialize for access. + * release release port + * write Write a bitstream to the configuration processor. + * read Read a data stream from the configuration processor. + * + * After being opened, the port is initialized and accessed to avoid a + * corrupted first read which may occur with some hardware. The port + * is left in a desynched state, requiring that a synch sequence be + * transmitted before any valid configuration data. A user will have + * exclusive access to the device while it remains open, and the state + * of the ICAP cannot be guaranteed after the device is closed. Note + * that a complete reset of the core and the state of the ICAP cannot + * be performed on many versions of the cores, hence users of this + * device should avoid making inconsistent accesses to the device. In + * particular, accessing the read interface, without first generating + * a write containing a readback packet can leave the ICAP in an + * inaccessible state. + * + * Note that in order to use the read interface, it is first necessary + * to write a request packet to the write interface. i.e., it is not + * possible to simply readback the bitstream (or any configuration + * bits) from a device without specifically requesting them first. + * The code to craft such packets is intended to be part of the + * user-space application code that uses this device. The simplest + * way to use this interface is simply: + * + * cp foo.bit /dev/xilinx_icap + * + * Note that unless foo.bit is an appropriately constructed partial + * bitstream, this has a high likelyhood of overwriting the design + * currently programmed in the FPGA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_OF +/* For open firmware. */ +#include +#include +#endif + +#include "xilinx_hwicap.h" +#include "buffer_icap.h" +#include "fifo_icap.h" + +#define DRIVER_NAME "xilinx_icap" + +#define HWICAP_REGS (0x10000) + +/* dynamically allocate device number */ +static int xhwicap_major; +static int xhwicap_minor; +#define HWICAP_DEVICES 1 + +module_param(xhwicap_major, int, S_IRUGO); +module_param(xhwicap_minor, int, S_IRUGO); + +/* An array, which is set to true when the device is registered. */ +static bool probed_devices[HWICAP_DEVICES]; + +static struct class *icap_class; + +#define UNIMPLEMENTED 0xFFFF + +static const struct config_registers v2_config_registers = { + .CRC = 0, + .FAR = 1, + .FDRI = 2, + .FDRO = 3, + .CMD = 4, + .CTL = 5, + .MASK = 6, + .STAT = 7, + .LOUT = 8, + .COR = 9, + .MFWR = 10, + .FLR = 11, + .KEY = 12, + .CBC = 13, + .IDCODE = 14, + .AXSS = UNIMPLEMENTED, + .C0R_1 = UNIMPLEMENTED, + .CSOB = UNIMPLEMENTED, + .WBSTAR = UNIMPLEMENTED, + .TIMER = UNIMPLEMENTED, + .BOOTSTS = UNIMPLEMENTED, + .CTL_1 = UNIMPLEMENTED, +}; + +static const struct config_registers v4_config_registers = { + .CRC = 0, + .FAR = 1, + .FDRI = 2, + .FDRO = 3, + .CMD = 4, + .CTL = 5, + .MASK = 6, + .STAT = 7, + .LOUT = 8, + .COR = 9, + .MFWR = 10, + .FLR = UNIMPLEMENTED, + .KEY = UNIMPLEMENTED, + .CBC = 11, + .IDCODE = 12, + .AXSS = 13, + .C0R_1 = UNIMPLEMENTED, + .CSOB = UNIMPLEMENTED, + .WBSTAR = UNIMPLEMENTED, + .TIMER = UNIMPLEMENTED, + .BOOTSTS = UNIMPLEMENTED, + .CTL_1 = UNIMPLEMENTED, +}; +static const struct config_registers v5_config_registers = { + .CRC = 0, + .FAR = 1, + .FDRI = 2, + .FDRO = 3, + .CMD = 4, + .CTL = 5, + .MASK = 6, + .STAT = 7, + .LOUT = 8, + .COR = 9, + .MFWR = 10, + .FLR = UNIMPLEMENTED, + .KEY = UNIMPLEMENTED, + .CBC = 11, + .IDCODE = 12, + .AXSS = 13, + .C0R_1 = 14, + .CSOB = 15, + .WBSTAR = 16, + .TIMER = 17, + .BOOTSTS = 18, + .CTL_1 = 19, +}; + +/** + * hwicap_command_desync: Send a DESYNC command to the ICAP port. + * @parameter drvdata: a pointer to the drvdata. + * + * This command desynchronizes the ICAP After this command, a + * bitstream containing a NULL packet, followed by a SYNCH packet is + * required before the ICAP will recognize commands. + */ +int hwicap_command_desync(struct hwicap_drvdata *drvdata) +{ + u32 buffer[4]; + u32 index = 0; + + /* + * Create the data to be written to the ICAP. + */ + buffer[index++] = hwicap_type_1_write(drvdata->config_regs->CMD) | 1; + buffer[index++] = XHI_CMD_DESYNCH; + buffer[index++] = XHI_NOOP_PACKET; + buffer[index++] = XHI_NOOP_PACKET; + + /* + * Write the data to the FIFO and intiate the transfer of data present + * in the FIFO to the ICAP device. + */ + return drvdata->config->set_configuration(drvdata, + &buffer[0], index); +} + +/** + * hwicap_command_capture: Send a CAPTURE command to the ICAP port. + * @parameter drvdata: a pointer to the drvdata. + * + * This command captures all of the flip flop states so they will be + * available during readback. One can use this command instead of + * enabling the CAPTURE block in the design. + */ +int hwicap_command_capture(struct hwicap_drvdata *drvdata) +{ + u32 buffer[7]; + u32 index = 0; + + /* + * Create the data to be written to the ICAP. + */ + buffer[index++] = XHI_DUMMY_PACKET; + buffer[index++] = XHI_SYNC_PACKET; + buffer[index++] = XHI_NOOP_PACKET; + buffer[index++] = hwicap_type_1_write(drvdata->config_regs->CMD) | 1; + buffer[index++] = XHI_CMD_GCAPTURE; + buffer[index++] = XHI_DUMMY_PACKET; + buffer[index++] = XHI_DUMMY_PACKET; + + /* + * Write the data to the FIFO and intiate the transfer of data + * present in the FIFO to the ICAP device. + */ + return drvdata->config->set_configuration(drvdata, + &buffer[0], index); + +} + +/** + * hwicap_get_configuration_register: Query a configuration register. + * @parameter drvdata: a pointer to the drvdata. + * @parameter reg: a constant which represents the configuration + * register value to be returned. + * Examples: XHI_IDCODE, XHI_FLR. + * @parameter RegData: returns the value of the register. + * + * Sends a query packet to the ICAP and then receives the response. + * The icap is left in Synched state. + */ +int hwicap_get_configuration_register(struct hwicap_drvdata *drvdata, + u32 reg, u32 *RegData) +{ + int status; + u32 buffer[6]; + u32 index = 0; + + /* + * Create the data to be written to the ICAP. + */ + buffer[index++] = XHI_DUMMY_PACKET; + buffer[index++] = XHI_SYNC_PACKET; + buffer[index++] = XHI_NOOP_PACKET; + buffer[index++] = hwicap_type_1_read(reg) | 1; + buffer[index++] = XHI_NOOP_PACKET; + buffer[index++] = XHI_NOOP_PACKET; + + /* + * Write the data to the FIFO and intiate the transfer of data present + * in the FIFO to the ICAP device. + */ + status = drvdata->config->set_configuration(drvdata, + &buffer[0], index); + if (status) + return status; + + /* + * Read the configuration register + */ + status = drvdata->config->get_configuration(drvdata, RegData, 1); + if (status) + return status; + + return 0; +} + +int hwicap_initialize_hwicap(struct hwicap_drvdata *drvdata) +{ + int status; + u32 idcode; + + dev_dbg(drvdata->dev, "initializing\n"); + + /* Abort any current transaction, to make sure we have the + * ICAP in a good state. */ + dev_dbg(drvdata->dev, "Reset...\n"); + drvdata->config->reset(drvdata); + + dev_dbg(drvdata->dev, "Desync...\n"); + status = hwicap_command_desync(drvdata); + if (status) + return status; + + /* Attempt to read the IDCODE from ICAP. This + * may not be returned correctly, due to the design of the + * hardware. + */ + dev_dbg(drvdata->dev, "Reading IDCODE...\n"); + status = hwicap_get_configuration_register( + drvdata, drvdata->config_regs->IDCODE, &idcode); + dev_dbg(drvdata->dev, "IDCODE = %x\n", idcode); + if (status) + return status; + + dev_dbg(drvdata->dev, "Desync...\n"); + status = hwicap_command_desync(drvdata); + if (status) + return status; + + return 0; +} + +static ssize_t +hwicap_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + struct hwicap_drvdata *drvdata = file->private_data; + ssize_t bytes_to_read = 0; + u32 *kbuf; + u32 words; + u32 bytes_remaining; + int status; + + if (down_interruptible(&drvdata->sem)) + return -ERESTARTSYS; + + if (drvdata->read_buffer_in_use) { + /* If there are leftover bytes in the buffer, just */ + /* return them and don't try to read more from the */ + /* ICAP device. */ + bytes_to_read = + (count < drvdata->read_buffer_in_use) ? count : + drvdata->read_buffer_in_use; + + /* Return the data currently in the read buffer. */ + if (copy_to_user(buf, drvdata->read_buffer, bytes_to_read)) { + status = -EFAULT; + goto error; + } + drvdata->read_buffer_in_use -= bytes_to_read; + memcpy(drvdata->read_buffer + bytes_to_read, + drvdata->read_buffer, 4 - bytes_to_read); + } else { + /* Get new data from the ICAP, and return was was requested. */ + kbuf = (u32 *) get_zeroed_page(GFP_KERNEL); + if (!kbuf) { + status = -ENOMEM; + goto error; + } + + /* The ICAP device is only able to read complete */ + /* words. If a number of bytes that do not correspond */ + /* to complete words is requested, then we read enough */ + /* words to get the required number of bytes, and then */ + /* save the remaining bytes for the next read. */ + + /* Determine the number of words to read, rounding up */ + /* if necessary. */ + words = ((count + 3) >> 2); + bytes_to_read = words << 2; + + if (bytes_to_read > PAGE_SIZE) + bytes_to_read = PAGE_SIZE; + + /* Ensure we only read a complete number of words. */ + bytes_remaining = bytes_to_read & 3; + bytes_to_read &= ~3; + words = bytes_to_read >> 2; + + status = drvdata->config->get_configuration(drvdata, + kbuf, words); + + /* If we didn't read correctly, then bail out. */ + if (status) { + free_page((unsigned long)kbuf); + goto error; + } + + /* If we fail to return the data to the user, then bail out. */ + if (copy_to_user(buf, kbuf, bytes_to_read)) { + free_page((unsigned long)kbuf); + status = -EFAULT; + goto error; + } + memcpy(kbuf, drvdata->read_buffer, bytes_remaining); + drvdata->read_buffer_in_use = bytes_remaining; + free_page((unsigned long)kbuf); + } + status = bytes_to_read; + error: + up(&drvdata->sem); + return status; +} + +static ssize_t +hwicap_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct hwicap_drvdata *drvdata = file->private_data; + ssize_t written = 0; + ssize_t left = count; + u32 *kbuf; + ssize_t len; + ssize_t status; + + if (down_interruptible(&drvdata->sem)) + return -ERESTARTSYS; + + left += drvdata->write_buffer_in_use; + + /* Only write multiples of 4 bytes. */ + if (left < 4) { + status = 0; + goto error; + } + + kbuf = (u32 *) __get_free_page(GFP_KERNEL); + if (!kbuf) { + status = -ENOMEM; + goto error; + } + + while (left > 3) { + /* only write multiples of 4 bytes, so there might */ + /* be as many as 3 bytes left (at the end). */ + len = left; + + if (len > PAGE_SIZE) + len = PAGE_SIZE; + len &= ~3; + + if (drvdata->write_buffer_in_use) { + memcpy(kbuf, drvdata->write_buffer, + drvdata->write_buffer_in_use); + if (copy_from_user( + (((char *)kbuf) + (drvdata->write_buffer_in_use)), + buf + written, + len - (drvdata->write_buffer_in_use))) { + free_page((unsigned long)kbuf); + status = -EFAULT; + goto error; + } + } else { + if (copy_from_user(kbuf, buf + written, len)) { + free_page((unsigned long)kbuf); + status = -EFAULT; + goto error; + } + } + + status = drvdata->config->set_configuration(drvdata, + kbuf, len >> 2); + + if (status) { + free_page((unsigned long)kbuf); + status = -EFAULT; + goto error; + } + if (drvdata->write_buffer_in_use) { + len -= drvdata->write_buffer_in_use; + left -= drvdata->write_buffer_in_use; + drvdata->write_buffer_in_use = 0; + } + written += len; + left -= len; + } + if ((left > 0) && (left < 4)) { + if (!copy_from_user(drvdata->write_buffer, + buf + written, left)) { + drvdata->write_buffer_in_use = left; + written += left; + left = 0; + } + } + + free_page((unsigned long)kbuf); + status = written; + error: + up(&drvdata->sem); + return status; +} + +static int hwicap_open(struct inode *inode, struct file *file) +{ + struct hwicap_drvdata *drvdata; + int status; + + drvdata = container_of(inode->i_cdev, struct hwicap_drvdata, cdev); + + if (down_interruptible(&drvdata->sem)) + return -ERESTARTSYS; + + if (drvdata->is_open) { + status = -EBUSY; + goto error; + } + + status = hwicap_initialize_hwicap(drvdata); + if (status) { + dev_err(drvdata->dev, "Failed to open file"); + goto error; + } + + file->private_data = drvdata; + drvdata->write_buffer_in_use = 0; + drvdata->read_buffer_in_use = 0; + drvdata->is_open = 1; + + error: + up(&drvdata->sem); + return status; +} + +static int hwicap_release(struct inode *inode, struct file *file) +{ + struct hwicap_drvdata *drvdata = file->private_data; + int i; + int status = 0; + + if (down_interruptible(&drvdata->sem)) + return -ERESTARTSYS; + + if (drvdata->write_buffer_in_use) { + /* Flush write buffer. */ + for (i = drvdata->write_buffer_in_use; i < 4; i++) + drvdata->write_buffer[i] = 0; + + status = drvdata->config->set_configuration(drvdata, + (u32 *) drvdata->write_buffer, 1); + if (status) + goto error; + } + + status = hwicap_command_desync(drvdata); + if (status) + goto error; + + error: + drvdata->is_open = 0; + up(&drvdata->sem); + return status; +} + +static struct file_operations hwicap_fops = { + .owner = THIS_MODULE, + .write = hwicap_write, + .read = hwicap_read, + .open = hwicap_open, + .release = hwicap_release, +}; + +static int __devinit hwicap_setup(struct device *dev, int id, + const struct resource *regs_res, + const struct hwicap_driver_config *config, + const struct config_registers *config_regs) +{ + dev_t devt; + struct hwicap_drvdata *drvdata = NULL; + int retval = 0; + + dev_info(dev, "Xilinx icap port driver\n"); + + if (id < 0) { + for (id = 0; id < HWICAP_DEVICES; id++) + if (!probed_devices[id]) + break; + } + if (id < 0 || id >= HWICAP_DEVICES) { + dev_err(dev, "%s%i too large\n", DRIVER_NAME, id); + return -EINVAL; + } + if (probed_devices[id]) { + dev_err(dev, "cannot assign to %s%i; it is already in use\n", + DRIVER_NAME, id); + return -EBUSY; + } + + probed_devices[id] = 1; + + devt = MKDEV(xhwicap_major, xhwicap_minor + id); + + drvdata = kmalloc(sizeof(struct hwicap_drvdata), GFP_KERNEL); + if (!drvdata) { + dev_err(dev, "Couldn't allocate device private record\n"); + return -ENOMEM; + } + memset((void *)drvdata, 0, sizeof(struct hwicap_drvdata)); + dev_set_drvdata(dev, (void *)drvdata); + + if (!regs_res) { + dev_err(dev, "Couldn't get registers resource\n"); + retval = -EFAULT; + goto failed1; + } + + drvdata->mem_start = regs_res->start; + drvdata->mem_end = regs_res->end; + drvdata->mem_size = regs_res->end - regs_res->start + 1; + + if (!request_mem_region(drvdata->mem_start, + drvdata->mem_size, DRIVER_NAME)) { + dev_err(dev, "Couldn't lock memory region at %p\n", + (void *)regs_res->start); + retval = -EBUSY; + goto failed1; + } + + drvdata->devt = devt; + drvdata->dev = dev; + drvdata->base_address = ioremap(drvdata->mem_start, drvdata->mem_size); + if (!drvdata->base_address) { + dev_err(dev, "ioremap() failed\n"); + goto failed2; + } + + drvdata->config = config; + drvdata->config_regs = config_regs; + + init_MUTEX(&drvdata->sem); + drvdata->is_open = 0; + + dev_info(dev, "ioremap %lx to %p with size %x\n", + (unsigned long int)drvdata->mem_start, + drvdata->base_address, drvdata->mem_size); + + cdev_init(&drvdata->cdev, &hwicap_fops); + drvdata->cdev.owner = THIS_MODULE; + retval = cdev_add(&drvdata->cdev, devt, 1); + if (retval) { + dev_err(dev, "cdev_add() failed\n"); + goto failed3; + } + /* devfs_mk_cdev(devt, S_IFCHR|S_IRUGO|S_IWUGO, DRIVER_NAME); */ + class_device_create(icap_class, NULL, devt, NULL, DRIVER_NAME); + return 0; /* success */ + + failed3: + iounmap(drvdata->base_address); + + failed2: + release_mem_region(regs_res->start, drvdata->mem_size); + + failed1: + kfree(drvdata); + + return retval; +} + +static struct hwicap_driver_config buffer_icap_config = { + .get_configuration = buffer_icap_get_configuration, + .set_configuration = buffer_icap_set_configuration, + .reset = buffer_icap_reset, +}; + +static struct hwicap_driver_config fifo_icap_config = { + .get_configuration = fifo_icap_get_configuration, + .set_configuration = fifo_icap_set_configuration, + .reset = fifo_icap_reset, +}; + +static int __devexit hwicap_remove(struct device *dev) +{ + struct hwicap_drvdata *drvdata; + + drvdata = (struct hwicap_drvdata *)dev_get_drvdata(dev); + + if (!drvdata) + return 0; + + class_device_destroy(icap_class, drvdata->devt); + cdev_del(&drvdata->cdev); + iounmap(drvdata->base_address); + release_mem_region(drvdata->mem_start, drvdata->mem_size); + kfree(drvdata); + dev_set_drvdata(dev, NULL); + probed_devices[MINOR(dev->devt)-xhwicap_minor] = 0; + + return 0; /* success */ +} + +static int __devinit hwicap_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + const struct config_registers *regs; + const char *family; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + /* It's most likely that we're using V4, if the family is not + specified */ + regs = &v4_config_registers; + family = pdev->dev.platform_data; + + if (family) { + if (!strcmp(family, "virtex2p")) { + regs = &v2_config_registers; + } else if (!strcmp(family, "virtex4")) { + regs = &v4_config_registers; + } else if (!strcmp(family, "virtex5")) { + regs = &v5_config_registers; + } + } + + return hwicap_setup(&pdev->dev, pdev->id, res, + &buffer_icap_config, regs); +} + +static int __devexit hwicap_drv_remove(struct platform_device *pdev) +{ + return hwicap_remove(&pdev->dev); +} + +static struct platform_driver hwicap_platform_driver = { + .probe = hwicap_drv_probe, + .remove = hwicap_drv_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, +}; + +/* --------------------------------------------------------------------- + * OF bus binding + */ + +#if defined(CONFIG_OF) +static int __devinit +hwicap_of_probe(struct of_device *op, const struct of_device_id *match) +{ + struct resource res; + const unsigned int *id; + const char *family; + int rc; + const struct hwicap_driver_config *config = match->data; + const struct config_registers *regs; + + dev_dbg(&op->dev, "hwicap_of_probe(%p, %p)\n", op, match); + + rc = of_address_to_resource(op->node, 0, &res); + if (rc) { + dev_err(&op->dev, "invalid address\n"); + return rc; + } + + id = of_get_property(op->node, "port-number", NULL); + + /* It's most likely that we're using V4, if the family is not + specified */ + regs = &v4_config_registers; + family = of_get_property(op->node, "xlnx,family", NULL); + + if (family) { + if (!strcmp(family, "virtex2p")) { + regs = &v2_config_registers; + } else if (!strcmp(family, "virtex4")) { + regs = &v4_config_registers; + } else if (!strcmp(family, "virtex5")) { + regs = &v5_config_registers; + } + } + return hwicap_setup(&op->dev, id ? *id : -1, &res, config, + regs); +} + +static int __devexit hwicap_of_remove(struct of_device *op) +{ + return hwicap_remove(&op->dev); +} + +/* Match table for of_platform binding */ +static const struct of_device_id __devinit hwicap_of_match[] = { + { .compatible = "xlnx,opb-hwicap-1.00.b", .data = &buffer_icap_config}, + { .compatible = "xlnx,xps-hwicap-1.00.a", .data = &fifo_icap_config}, + {}, +}; +MODULE_DEVICE_TABLE(of, hwicap_of_match); + +static struct of_platform_driver hwicap_of_driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = hwicap_of_match, + .probe = hwicap_of_probe, + .remove = __devexit_p(hwicap_of_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +/* Registration helpers to keep the number of #ifdefs to a minimum */ +static inline int __devinit hwicap_of_register(void) +{ + pr_debug("hwicap: calling of_register_platform_driver()\n"); + return of_register_platform_driver(&hwicap_of_driver); +} + +static inline void __devexit hwicap_of_unregister(void) +{ + of_unregister_platform_driver(&hwicap_of_driver); +} +#else /* CONFIG_OF */ +/* CONFIG_OF not enabled; do nothing helpers */ +static inline int __devinit hwicap_of_register(void) { return 0; } +static inline void __devexit hwicap_of_unregister(void) { } +#endif /* CONFIG_OF */ + +static int __devinit hwicap_module_init(void) +{ + dev_t devt; + int retval; + + icap_class = class_create(THIS_MODULE, "xilinx_config"); + + if (xhwicap_major) { + devt = MKDEV(xhwicap_major, xhwicap_minor); + retval = register_chrdev_region( + devt, + HWICAP_DEVICES, + DRIVER_NAME); + if (retval < 0) + return retval; + } else { + retval = alloc_chrdev_region(&devt, + xhwicap_minor, + HWICAP_DEVICES, + DRIVER_NAME); + if (retval < 0) + return retval; + xhwicap_major = MAJOR(devt); + } + + retval = platform_driver_register(&hwicap_platform_driver); + + if (retval) + goto failed1; + + retval = hwicap_of_register(); + + if (retval) + goto failed2; + + return retval; + + failed2: + platform_driver_unregister(&hwicap_platform_driver); + + failed1: + unregister_chrdev_region(devt, HWICAP_DEVICES); + + return retval; +} + +static void __devexit hwicap_module_cleanup(void) +{ + dev_t devt = MKDEV(xhwicap_major, xhwicap_minor); + + class_destroy(icap_class); + + platform_driver_unregister(&hwicap_platform_driver); + + hwicap_of_unregister(); + + unregister_chrdev_region(devt, HWICAP_DEVICES); +} + +module_init(hwicap_module_init); +module_exit(hwicap_module_cleanup); + +MODULE_AUTHOR("Xilinx, Inc; Xilinx Research Labs Group"); +MODULE_DESCRIPTION("Xilinx ICAP Port Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.h b/drivers/char/xilinx_hwicap/xilinx_hwicap.h new file mode 100644 index 000000000000..ae771cac1629 --- /dev/null +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.h @@ -0,0 +1,193 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2003-2007 Xilinx Inc. + * All rights reserved. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#ifndef XILINX_HWICAP_H_ /* prevent circular inclusions */ +#define XILINX_HWICAP_H_ /* by using protection macros */ + +#include +#include +#include +#include + +#include + +struct hwicap_drvdata { + u32 write_buffer_in_use; /* Always in [0,3] */ + u8 write_buffer[4]; + u32 read_buffer_in_use; /* Always in [0,3] */ + u8 read_buffer[4]; + u32 mem_start; /* phys. address of the control registers */ + u32 mem_end; /* phys. address of the control registers */ + u32 mem_size; + void __iomem *base_address;/* virt. address of the control registers */ + + struct device *dev; + struct cdev cdev; /* Char device structure */ + dev_t devt; + + const struct hwicap_driver_config *config; + const struct config_registers *config_regs; + void *private_data; + bool is_open; + struct semaphore sem; +}; + +struct hwicap_driver_config { + int (*get_configuration)(struct hwicap_drvdata *drvdata, u32 *data, + u32 size); + int (*set_configuration)(struct hwicap_drvdata *drvdata, u32 *data, + u32 size); + void (*reset)(struct hwicap_drvdata *drvdata); +}; + +/* Number of times to poll the done regsiter */ +#define XHI_MAX_RETRIES 10 + +/************ Constant Definitions *************/ + +#define XHI_PAD_FRAMES 0x1 + +/* Mask for calculating configuration packet headers */ +#define XHI_WORD_COUNT_MASK_TYPE_1 0x7FFUL +#define XHI_WORD_COUNT_MASK_TYPE_2 0x1FFFFFUL +#define XHI_TYPE_MASK 0x7 +#define XHI_REGISTER_MASK 0xF +#define XHI_OP_MASK 0x3 + +#define XHI_TYPE_SHIFT 29 +#define XHI_REGISTER_SHIFT 13 +#define XHI_OP_SHIFT 27 + +#define XHI_TYPE_1 1 +#define XHI_TYPE_2 2 +#define XHI_OP_WRITE 2 +#define XHI_OP_READ 1 + +/* Address Block Types */ +#define XHI_FAR_CLB_BLOCK 0 +#define XHI_FAR_BRAM_BLOCK 1 +#define XHI_FAR_BRAM_INT_BLOCK 2 + +struct config_registers { + u32 CRC; + u32 FAR; + u32 FDRI; + u32 FDRO; + u32 CMD; + u32 CTL; + u32 MASK; + u32 STAT; + u32 LOUT; + u32 COR; + u32 MFWR; + u32 FLR; + u32 KEY; + u32 CBC; + u32 IDCODE; + u32 AXSS; + u32 C0R_1; + u32 CSOB; + u32 WBSTAR; + u32 TIMER; + u32 BOOTSTS; + u32 CTL_1; +}; + +/* Configuration Commands */ +#define XHI_CMD_NULL 0 +#define XHI_CMD_WCFG 1 +#define XHI_CMD_MFW 2 +#define XHI_CMD_DGHIGH 3 +#define XHI_CMD_RCFG 4 +#define XHI_CMD_START 5 +#define XHI_CMD_RCAP 6 +#define XHI_CMD_RCRC 7 +#define XHI_CMD_AGHIGH 8 +#define XHI_CMD_SWITCH 9 +#define XHI_CMD_GRESTORE 10 +#define XHI_CMD_SHUTDOWN 11 +#define XHI_CMD_GCAPTURE 12 +#define XHI_CMD_DESYNCH 13 +#define XHI_CMD_IPROG 15 /* Only in Virtex5 */ +#define XHI_CMD_CRCC 16 /* Only in Virtex5 */ +#define XHI_CMD_LTIMER 17 /* Only in Virtex5 */ + +/* Packet constants */ +#define XHI_SYNC_PACKET 0xAA995566UL +#define XHI_DUMMY_PACKET 0xFFFFFFFFUL +#define XHI_NOOP_PACKET (XHI_TYPE_1 << XHI_TYPE_SHIFT) +#define XHI_TYPE_2_READ ((XHI_TYPE_2 << XHI_TYPE_SHIFT) | \ + (XHI_OP_READ << XHI_OP_SHIFT)) + +#define XHI_TYPE_2_WRITE ((XHI_TYPE_2 << XHI_TYPE_SHIFT) | \ + (XHI_OP_WRITE << XHI_OP_SHIFT)) + +#define XHI_TYPE2_CNT_MASK 0x07FFFFFF + +#define XHI_TYPE_1_PACKET_MAX_WORDS 2047UL +#define XHI_TYPE_1_HEADER_BYTES 4 +#define XHI_TYPE_2_HEADER_BYTES 8 + +/* Constant to use for CRC check when CRC has been disabled */ +#define XHI_DISABLED_AUTO_CRC 0x0000DEFCUL + +/** + * hwicap_type_1_read: Generates a Type 1 read packet header. + * @parameter: Register is the address of the register to be read back. + * + * Generates a Type 1 read packet header, which is used to indirectly + * read registers in the configuration logic. This packet must then + * be sent through the icap device, and a return packet received with + * the information. + **/ +static inline u32 hwicap_type_1_read(u32 Register) +{ + return (XHI_TYPE_1 << XHI_TYPE_SHIFT) | + (Register << XHI_REGISTER_SHIFT) | + (XHI_OP_READ << XHI_OP_SHIFT); +} + +/** + * hwicap_type_1_write: Generates a Type 1 write packet header + * @parameter: Register is the address of the register to be read back. + **/ +static inline u32 hwicap_type_1_write(u32 Register) +{ + return (XHI_TYPE_1 << XHI_TYPE_SHIFT) | + (Register << XHI_REGISTER_SHIFT) | + (XHI_OP_WRITE << XHI_OP_SHIFT); +} + +#endif -- cgit v1.2.3 From 599f030cc596cd41a0f966afd4cee2e2fc48ee86 Mon Sep 17 00:00:00 2001 From: John Rigby Date: Tue, 29 Jan 2008 04:28:55 +1100 Subject: [POWERPC] mpc512x: Factor out 5200 dependencies from 52xx psc driver PSC devices are different between the mpc5200 and the mpc5121 this patch localizes the differences in preparation for adding mpc5121 support to the psc uart driver. Signed-off-by: John Rigby Signed-off-by: Grant Likely --- drivers/serial/mpc52xx_uart.c | 256 +++++++++++++++++++++++++++++++----------- 1 file changed, 192 insertions(+), 64 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 3c4d29e59b2c..7ab73fa4a80f 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -67,7 +67,6 @@ #include #include #include - #include #include @@ -111,8 +110,8 @@ static struct device_node *mpc52xx_uart_nodes[MPC52xx_PSC_MAXNUM]; static void mpc52xx_uart_of_enumerate(void); #endif + #define PSC(port) ((struct mpc52xx_psc __iomem *)((port)->membase)) -#define FIFO(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) /* Forward declaration of the interruption handling routine */ @@ -137,6 +136,162 @@ static struct of_device_id mpc52xx_uart_of_match[] = { }; #endif +/* ======================================================================== */ +/* PSC fifo operations for isolating differences between 52xx and 512x */ +/* ======================================================================== */ + +struct psc_ops { + void (*fifo_init)(struct uart_port *port); + int (*raw_rx_rdy)(struct uart_port *port); + int (*raw_tx_rdy)(struct uart_port *port); + int (*rx_rdy)(struct uart_port *port); + int (*tx_rdy)(struct uart_port *port); + int (*tx_empty)(struct uart_port *port); + void (*stop_rx)(struct uart_port *port); + void (*start_tx)(struct uart_port *port); + void (*stop_tx)(struct uart_port *port); + void (*rx_clr_irq)(struct uart_port *port); + void (*tx_clr_irq)(struct uart_port *port); + void (*write_char)(struct uart_port *port, unsigned char c); + unsigned char (*read_char)(struct uart_port *port); + void (*cw_disable_ints)(struct uart_port *port); + void (*cw_restore_ints)(struct uart_port *port); + unsigned long (*getuartclk)(void *p); +}; + +#define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) +static void mpc52xx_psc_fifo_init(struct uart_port *port) +{ + struct mpc52xx_psc __iomem *psc = PSC(port); + struct mpc52xx_psc_fifo __iomem *fifo = FIFO_52xx(port); + + /* /32 prescaler */ + out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); + + out_8(&fifo->rfcntl, 0x00); + out_be16(&fifo->rfalarm, 0x1ff); + out_8(&fifo->tfcntl, 0x07); + out_be16(&fifo->tfalarm, 0x80); + + port->read_status_mask |= MPC52xx_PSC_IMR_RXRDY | MPC52xx_PSC_IMR_TXRDY; + out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); +} + +static int mpc52xx_psc_raw_rx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status) + & MPC52xx_PSC_SR_RXRDY; +} + +static int mpc52xx_psc_raw_tx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status) + & MPC52xx_PSC_SR_TXRDY; +} + + +static int mpc52xx_psc_rx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_isr) + & port->read_status_mask + & MPC52xx_PSC_IMR_RXRDY; +} + +static int mpc52xx_psc_tx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_isr) + & port->read_status_mask + & MPC52xx_PSC_IMR_TXRDY; +} + +static int mpc52xx_psc_tx_empty(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status) + & MPC52xx_PSC_SR_TXEMP; +} + +static void mpc52xx_psc_start_tx(struct uart_port *port) +{ + port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY; + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_stop_tx(struct uart_port *port) +{ + port->read_status_mask &= ~MPC52xx_PSC_IMR_TXRDY; + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_stop_rx(struct uart_port *port) +{ + port->read_status_mask &= ~MPC52xx_PSC_IMR_RXRDY; + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_rx_clr_irq(struct uart_port *port) +{ +} + +static void mpc52xx_psc_tx_clr_irq(struct uart_port *port) +{ +} + +static void mpc52xx_psc_write_char(struct uart_port *port, unsigned char c) +{ + out_8(&PSC(port)->mpc52xx_psc_buffer_8, c); +} + +static unsigned char mpc52xx_psc_read_char(struct uart_port *port) +{ + return in_8(&PSC(port)->mpc52xx_psc_buffer_8); +} + +static void mpc52xx_psc_cw_disable_ints(struct uart_port *port) +{ + out_be16(&PSC(port)->mpc52xx_psc_imr, 0); +} + +static void mpc52xx_psc_cw_restore_ints(struct uart_port *port) +{ + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +/* Search for bus-frequency property in this node or a parent */ +static unsigned long mpc52xx_getuartclk(void *p) +{ +#if defined(CONFIG_PPC_MERGE) + /* + * 5200 UARTs have a / 32 prescaler + * but the generic serial code assumes 16 + * so return ipb freq / 2 + */ + return mpc52xx_find_ipb_freq(p) / 2; +#else + pr_debug("unexpected call to mpc52xx_getuartclk with arch/ppc\n"); + return NULL; +#endif +} + +static struct psc_ops mpc52xx_psc_ops = { + .fifo_init = mpc52xx_psc_fifo_init, + .raw_rx_rdy = mpc52xx_psc_raw_rx_rdy, + .raw_tx_rdy = mpc52xx_psc_raw_tx_rdy, + .rx_rdy = mpc52xx_psc_rx_rdy, + .tx_rdy = mpc52xx_psc_tx_rdy, + .tx_empty = mpc52xx_psc_tx_empty, + .stop_rx = mpc52xx_psc_stop_rx, + .start_tx = mpc52xx_psc_start_tx, + .stop_tx = mpc52xx_psc_stop_tx, + .rx_clr_irq = mpc52xx_psc_rx_clr_irq, + .tx_clr_irq = mpc52xx_psc_tx_clr_irq, + .write_char = mpc52xx_psc_write_char, + .read_char = mpc52xx_psc_read_char, + .cw_disable_ints = mpc52xx_psc_cw_disable_ints, + .cw_restore_ints = mpc52xx_psc_cw_restore_ints, + .getuartclk = mpc52xx_getuartclk, +}; + +static struct psc_ops *psc_ops = &mpc52xx_psc_ops; /* ======================================================================== */ /* UART operations */ @@ -145,8 +300,7 @@ static struct of_device_id mpc52xx_uart_of_match[] = { static unsigned int mpc52xx_uart_tx_empty(struct uart_port *port) { - int status = in_be16(&PSC(port)->mpc52xx_psc_status); - return (status & MPC52xx_PSC_SR_TXEMP) ? TIOCSER_TEMT : 0; + return psc_ops->tx_empty(port) ? TIOCSER_TEMT : 0; } static void @@ -166,16 +320,14 @@ static void mpc52xx_uart_stop_tx(struct uart_port *port) { /* port->lock taken by caller */ - port->read_status_mask &= ~MPC52xx_PSC_IMR_TXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->stop_tx(port); } static void mpc52xx_uart_start_tx(struct uart_port *port) { /* port->lock taken by caller */ - port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->start_tx(port); } static void @@ -188,8 +340,7 @@ mpc52xx_uart_send_xchar(struct uart_port *port, char ch) if (ch) { /* Make sure tx interrupts are on */ /* Truly necessary ??? They should be anyway */ - port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->start_tx(port); } spin_unlock_irqrestore(&port->lock, flags); @@ -199,8 +350,7 @@ static void mpc52xx_uart_stop_rx(struct uart_port *port) { /* port->lock taken by caller */ - port->read_status_mask &= ~MPC52xx_PSC_IMR_RXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->stop_rx(port); } static void @@ -227,7 +377,6 @@ static int mpc52xx_uart_startup(struct uart_port *port) { struct mpc52xx_psc __iomem *psc = PSC(port); - struct mpc52xx_psc_fifo __iomem *fifo = FIFO(port); int ret; /* Request IRQ */ @@ -242,15 +391,7 @@ mpc52xx_uart_startup(struct uart_port *port) out_be32(&psc->sicr, 0); /* UART mode DCD ignored */ - out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); /* /16 prescaler on */ - - out_8(&fifo->rfcntl, 0x00); - out_be16(&fifo->rfalarm, 0x1ff); - out_8(&fifo->tfcntl, 0x07); - out_be16(&fifo->tfalarm, 0x80); - - port->read_status_mask |= MPC52xx_PSC_IMR_RXRDY | MPC52xx_PSC_IMR_TXRDY; - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->fifo_init(port); out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); @@ -333,8 +474,7 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, * boot for the console, all stuff is not yet ready to receive at that * time and that just makes the kernel oops */ /* while (j-- && mpc52xx_uart_int_rx_chars(port)); */ - while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP) && - --j) + while (!mpc52xx_uart_tx_empty(port) && --j) udelay(1); if (!j) @@ -462,11 +602,9 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) unsigned short status; /* While we can read, do so ! */ - while ((status = in_be16(&PSC(port)->mpc52xx_psc_status)) & - MPC52xx_PSC_SR_RXRDY) { - + while (psc_ops->raw_rx_rdy(port)) { /* Get the char */ - ch = in_8(&PSC(port)->mpc52xx_psc_buffer_8); + ch = psc_ops->read_char(port); /* Handle sysreq char */ #ifdef SUPPORT_SYSRQ @@ -481,6 +619,8 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) flag = TTY_NORMAL; port->icount.rx++; + status = in_be16(&PSC(port)->mpc52xx_psc_status); + if (status & (MPC52xx_PSC_SR_PE | MPC52xx_PSC_SR_FE | MPC52xx_PSC_SR_RB)) { @@ -510,7 +650,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) tty_flip_buffer_push(tty); - return in_be16(&PSC(port)->mpc52xx_psc_status) & MPC52xx_PSC_SR_RXRDY; + return psc_ops->raw_rx_rdy(port); } static inline int @@ -520,7 +660,7 @@ mpc52xx_uart_int_tx_chars(struct uart_port *port) /* Process out of band chars */ if (port->x_char) { - out_8(&PSC(port)->mpc52xx_psc_buffer_8, port->x_char); + psc_ops->write_char(port, port->x_char); port->icount.tx++; port->x_char = 0; return 1; @@ -533,8 +673,8 @@ mpc52xx_uart_int_tx_chars(struct uart_port *port) } /* Send chars */ - while (in_be16(&PSC(port)->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXRDY) { - out_8(&PSC(port)->mpc52xx_psc_buffer_8, xmit->buf[xmit->tail]); + while (psc_ops->raw_tx_rdy(port)) { + psc_ops->write_char(port, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; if (uart_circ_empty(xmit)) @@ -560,7 +700,6 @@ mpc52xx_uart_int(int irq, void *dev_id) struct uart_port *port = dev_id; unsigned long pass = ISR_PASS_LIMIT; unsigned int keepgoing; - unsigned short status; spin_lock(&port->lock); @@ -569,18 +708,12 @@ mpc52xx_uart_int(int irq, void *dev_id) /* If we don't find anything to do, we stop */ keepgoing = 0; - /* Read status */ - status = in_be16(&PSC(port)->mpc52xx_psc_isr); - status &= port->read_status_mask; - - /* Do we need to receive chars ? */ - /* For this RX interrupts must be on and some chars waiting */ - if (status & MPC52xx_PSC_IMR_RXRDY) + psc_ops->rx_clr_irq(port); + if (psc_ops->rx_rdy(port)) keepgoing |= mpc52xx_uart_int_rx_chars(port); - /* Do we need to send chars ? */ - /* For this, TX must be ready and TX interrupt enabled */ - if (status & MPC52xx_PSC_IMR_TXRDY) + psc_ops->tx_clr_irq(port); + if (psc_ops->tx_rdy(port)) keepgoing |= mpc52xx_uart_int_tx_chars(port); /* Limit number of iteration */ @@ -647,36 +780,33 @@ static void mpc52xx_console_write(struct console *co, const char *s, unsigned int count) { struct uart_port *port = &mpc52xx_uart_ports[co->index]; - struct mpc52xx_psc __iomem *psc = PSC(port); unsigned int i, j; /* Disable interrupts */ - out_be16(&psc->mpc52xx_psc_imr, 0); + psc_ops->cw_disable_ints(port); /* Wait the TX buffer to be empty */ j = 5000000; /* Maximum wait */ - while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP) && - --j) + while (!mpc52xx_uart_tx_empty(port) && --j) udelay(1); /* Write all the chars */ for (i = 0; i < count; i++, s++) { /* Line return handling */ if (*s == '\n') - out_8(&psc->mpc52xx_psc_buffer_8, '\r'); + psc_ops->write_char(port, '\r'); /* Send the char */ - out_8(&psc->mpc52xx_psc_buffer_8, *s); + psc_ops->write_char(port, *s); /* Wait the TX buffer to be empty */ j = 20000; /* Maximum wait */ - while (!(in_be16(&psc->mpc52xx_psc_status) & - MPC52xx_PSC_SR_TXEMP) && --j) + while (!mpc52xx_uart_tx_empty(port) && --j) udelay(1); } /* Restore interrupt state */ - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->cw_restore_ints(port); } #if !defined(CONFIG_PPC_MERGE) @@ -721,7 +851,7 @@ mpc52xx_console_setup(struct console *co, char *options) { struct uart_port *port = &mpc52xx_uart_ports[co->index]; struct device_node *np = mpc52xx_uart_nodes[co->index]; - unsigned int ipb_freq; + unsigned int uartclk; struct resource res; int ret; @@ -753,17 +883,16 @@ mpc52xx_console_setup(struct console *co, char *options) return ret; } - /* Search for bus-frequency property in this node or a parent */ - ipb_freq = mpc52xx_find_ipb_freq(np); - if (ipb_freq == 0) { - pr_debug("Could not find IPB bus frequency!\n"); + uartclk = psc_ops->getuartclk(np); + if (uartclk == 0) { + pr_debug("Could not find uart clock frequency!\n"); return -EINVAL; } /* Basic port init. Needed since we use some uart_??? func before * real init for early access */ spin_lock_init(&port->lock); - port->uartclk = ipb_freq / 2; + port->uartclk = uartclk; port->ops = &mpc52xx_uart_ops; port->mapbase = res.start; port->membase = ioremap(res.start, sizeof(struct mpc52xx_psc)); @@ -949,7 +1078,7 @@ static int __devinit mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) { int idx = -1; - unsigned int ipb_freq; + unsigned int uartclk; struct uart_port *port = NULL; struct resource res; int ret; @@ -965,10 +1094,9 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) pr_debug("Found %s assigned to ttyPSC%x\n", mpc52xx_uart_nodes[idx]->full_name, idx); - /* Search for bus-frequency property in this node or a parent */ - ipb_freq = mpc52xx_find_ipb_freq(op->node); - if (ipb_freq == 0) { - dev_dbg(&op->dev, "Could not find IPB bus frequency!\n"); + uartclk = psc_ops->getuartclk(op->node); + if (uartclk == 0) { + dev_dbg(&op->dev, "Could not find uart clock frequency!\n"); return -EINVAL; } @@ -976,7 +1104,7 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) port = &mpc52xx_uart_ports[idx]; spin_lock_init(&port->lock); - port->uartclk = ipb_freq / 2; + port->uartclk = uartclk; port->fifosize = 512; port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF | -- cgit v1.2.3 From 25ae3a0739c69425a911925b43213895a9802b98 Mon Sep 17 00:00:00 2001 From: John Rigby Date: Tue, 29 Jan 2008 04:28:56 +1100 Subject: [POWERPC] mpc512x: Add MPC512x PSC support to MPC52xx psc driver Add 512x support using the psc_ops framework established with the previous patch. All 512x PSCs share the same interrupt so add IRQF_SHARED to irq flags. Signed-off-by: John Rigby Signed-off-by: Grant Likely --- drivers/serial/Kconfig | 12 +-- drivers/serial/mpc52xx_uart.c | 177 ++++++++++++++++++++++++++++++++++++-- include/asm-powerpc/mpc52xx_psc.h | 48 +++++++++++ 3 files changed, 225 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index d962b74e3114..d9d7673b712b 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1114,17 +1114,17 @@ config SERIAL_SGI_L1_CONSOLE say Y. Otherwise, say N. config SERIAL_MPC52xx - tristate "Freescale MPC52xx family PSC serial support" - depends on PPC_MPC52xx + tristate "Freescale MPC52xx/MPC512x family PSC serial support" + depends on PPC_MPC52xx || PPC_MPC512x select SERIAL_CORE help - This drivers support the MPC52xx PSC serial ports. If you would - like to use them, you must answer Y or M to this option. Not that + This driver supports MPC52xx and MPC512x PSC serial ports. If you would + like to use them, you must answer Y or M to this option. Note that for use as console, it must be included in kernel and not as a module. config SERIAL_MPC52xx_CONSOLE - bool "Console on a Freescale MPC52xx family PSC serial port" + bool "Console on a Freescale MPC52xx/MPC512x family PSC serial port" depends on SERIAL_MPC52xx=y select SERIAL_CORE_CONSOLE help @@ -1132,7 +1132,7 @@ config SERIAL_MPC52xx_CONSOLE of the Freescale MPC52xx family as a console. config SERIAL_MPC52xx_CONSOLE_BAUD - int "Freescale MPC52xx family PSC serial port baud" + int "Freescale MPC52xx/MPC512x family PSC serial port baud" depends on SERIAL_MPC52xx_CONSOLE=y default "9600" help diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 7ab73fa4a80f..821facd10bbc 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -16,6 +16,9 @@ * Some of the code has been inspired/copied from the 2.4 code written * by Dale Farnsworth . * + * Copyright (C) 2008 Freescale Semiconductor Inc. + * John Rigby + * Added support for MPC5121 * Copyright (C) 2006 Secret Lab Technologies Ltd. * Grant Likely * Copyright (C) 2004-2006 Sylvain Munaut @@ -78,6 +81,7 @@ #endif #include +#include #include #if defined(CONFIG_SERIAL_MPC52xx_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) @@ -129,13 +133,29 @@ static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id); #if defined(CONFIG_PPC_MERGE) static struct of_device_id mpc52xx_uart_of_match[] = { - { .type = "serial", .compatible = "fsl,mpc5200-psc-uart", }, - { .type = "serial", .compatible = "mpc5200-psc-uart", }, /* lite5200 */ - { .type = "serial", .compatible = "mpc5200-serial", }, /* efika */ +#ifdef CONFIG_PPC_MPC52xx + { .compatible = "fsl,mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, + /* binding used by old lite5200 device trees: */ + { .compatible = "mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, + /* binding used by efika: */ + { .compatible = "mpc5200-serial", .data = &mpc52xx_psc_ops, }, +#endif +#ifdef CONFIG_PPC_MPC512x + { .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, }, + {}, +#endif +}; +#if defined(CONFIG_PPC_MERGE) +static const struct of_device_id mpc52xx_uart_of_match[] = { + {.type = "serial", + .compatible = "mpc5200-psc-uart", +#endif {}, }; #endif +#endif + /* ======================================================================== */ /* PSC fifo operations for isolating differences between 52xx and 512x */ /* ======================================================================== */ @@ -159,6 +179,7 @@ struct psc_ops { unsigned long (*getuartclk)(void *p); }; +#ifdef CONFIG_PPC_MPC52xx #define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) static void mpc52xx_psc_fifo_init(struct uart_port *port) { @@ -291,7 +312,145 @@ static struct psc_ops mpc52xx_psc_ops = { .getuartclk = mpc52xx_getuartclk, }; -static struct psc_ops *psc_ops = &mpc52xx_psc_ops; +#endif /* CONFIG_MPC52xx */ + +#ifdef CONFIG_PPC_MPC512x +#define FIFO_512x(port) ((struct mpc512x_psc_fifo __iomem *)(PSC(port)+1)) +static void mpc512x_psc_fifo_init(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_512x(port)->txalarm, 1); + out_be32(&FIFO_512x(port)->tximr, 0); + + out_be32(&FIFO_512x(port)->rxcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_512x(port)->rxcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_512x(port)->rxalarm, 1); + out_be32(&FIFO_512x(port)->rximr, 0); + + out_be32(&FIFO_512x(port)->tximr, MPC512x_PSC_FIFO_ALARM); + out_be32(&FIFO_512x(port)->rximr, MPC512x_PSC_FIFO_ALARM); +} + +static int mpc512x_psc_raw_rx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_512x(port)->rxsr) & MPC512x_PSC_FIFO_EMPTY); +} + +static int mpc512x_psc_raw_tx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_512x(port)->txsr) & MPC512x_PSC_FIFO_FULL); +} + +static int mpc512x_psc_rx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->rxsr) + & in_be32(&FIFO_512x(port)->rximr) + & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc512x_psc_tx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->txsr) + & in_be32(&FIFO_512x(port)->tximr) + & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc512x_psc_tx_empty(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->txsr) + & MPC512x_PSC_FIFO_EMPTY; +} + +static void mpc512x_psc_stop_rx(struct uart_port *port) +{ + unsigned long rx_fifo_imr; + + rx_fifo_imr = in_be32(&FIFO_512x(port)->rximr); + rx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->rximr, rx_fifo_imr); +} + +static void mpc512x_psc_start_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_512x(port)->tximr); + tx_fifo_imr |= MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->tximr, tx_fifo_imr); +} + +static void mpc512x_psc_stop_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_512x(port)->tximr); + tx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->tximr, tx_fifo_imr); +} + +static void mpc512x_psc_rx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->rxisr, in_be32(&FIFO_512x(port)->rxisr)); +} + +static void mpc512x_psc_tx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->txisr, in_be32(&FIFO_512x(port)->txisr)); +} + +static void mpc512x_psc_write_char(struct uart_port *port, unsigned char c) +{ + out_8(&FIFO_512x(port)->txdata_8, c); +} + +static unsigned char mpc512x_psc_read_char(struct uart_port *port) +{ + return in_8(&FIFO_512x(port)->rxdata_8); +} + +static void mpc512x_psc_cw_disable_ints(struct uart_port *port) +{ + port->read_status_mask = + in_be32(&FIFO_512x(port)->tximr) << 16 | + in_be32(&FIFO_512x(port)->rximr); + out_be32(&FIFO_512x(port)->tximr, 0); + out_be32(&FIFO_512x(port)->rximr, 0); +} + +static void mpc512x_psc_cw_restore_ints(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->tximr, + (port->read_status_mask >> 16) & 0x7f); + out_be32(&FIFO_512x(port)->rximr, port->read_status_mask & 0x7f); +} + +static unsigned long mpc512x_getuartclk(void *p) +{ + return mpc512x_find_ips_freq(p); +} + +static struct psc_ops mpc512x_psc_ops = { + .fifo_init = mpc512x_psc_fifo_init, + .raw_rx_rdy = mpc512x_psc_raw_rx_rdy, + .raw_tx_rdy = mpc512x_psc_raw_tx_rdy, + .rx_rdy = mpc512x_psc_rx_rdy, + .tx_rdy = mpc512x_psc_tx_rdy, + .tx_empty = mpc512x_psc_tx_empty, + .stop_rx = mpc512x_psc_stop_rx, + .start_tx = mpc512x_psc_start_tx, + .stop_tx = mpc512x_psc_stop_tx, + .rx_clr_irq = mpc512x_psc_rx_clr_irq, + .tx_clr_irq = mpc512x_psc_tx_clr_irq, + .write_char = mpc512x_psc_write_char, + .read_char = mpc512x_psc_read_char, + .cw_disable_ints = mpc512x_psc_cw_disable_ints, + .cw_restore_ints = mpc512x_psc_cw_restore_ints, + .getuartclk = mpc512x_getuartclk, +}; +#endif + +static struct psc_ops *psc_ops; /* ======================================================================== */ /* UART operations */ @@ -381,7 +540,8 @@ mpc52xx_uart_startup(struct uart_port *port) /* Request IRQ */ ret = request_irq(port->irq, mpc52xx_uart_int, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "mpc52xx_psc_uart", port); + IRQF_DISABLED | IRQF_SAMPLE_RANDOM | IRQF_SHARED, + "mpc52xx_psc_uart", port); if (ret) return ret; @@ -1208,15 +1368,19 @@ mpc52xx_uart_of_enumerate(void) static int enum_done; struct device_node *np; const unsigned int *devno; + const struct of_device_id *match; int i; if (enum_done) return; for_each_node_by_type(np, "serial") { - if (!of_match_node(mpc52xx_uart_of_match, np)) + match = of_match_node(mpc52xx_uart_of_match, np); + if (!match) continue; + psc_ops = match->data; + /* Is a particular device number requested? */ devno = of_get_property(np, "port-number", NULL); mpc52xx_uart_of_assign(np, devno ? *devno : -1); @@ -1277,6 +1441,7 @@ mpc52xx_uart_init(void) return ret; } #else + psc_ops = &mpc52xx_psc_ops; ret = platform_driver_register(&mpc52xx_uart_platform_driver); if (ret) { printk(KERN_ERR "%s: platform_driver_register failed (%i)\n", diff --git a/include/asm-powerpc/mpc52xx_psc.h b/include/asm-powerpc/mpc52xx_psc.h index bea42b95390f..710c5d36efaa 100644 --- a/include/asm-powerpc/mpc52xx_psc.h +++ b/include/asm-powerpc/mpc52xx_psc.h @@ -190,5 +190,53 @@ struct mpc52xx_psc_fifo { u16 tflwfptr; /* PSC + 0x9e */ }; +#define MPC512x_PSC_FIFO_RESET_SLICE 0x80 +#define MPC512x_PSC_FIFO_ENABLE_SLICE 0x01 +#define MPC512x_PSC_FIFO_ENABLE_DMA 0x04 + +#define MPC512x_PSC_FIFO_EMPTY 0x1 +#define MPC512x_PSC_FIFO_FULL 0x2 +#define MPC512x_PSC_FIFO_ALARM 0x4 +#define MPC512x_PSC_FIFO_URERR 0x8 +#define MPC512x_PSC_FIFO_ORERR 0x01 +#define MPC512x_PSC_FIFO_MEMERROR 0x02 + +struct mpc512x_psc_fifo { + u32 reserved1[10]; + u32 txcmd; /* PSC + 0x80 */ + u32 txalarm; /* PSC + 0x84 */ + u32 txsr; /* PSC + 0x88 */ + u32 txisr; /* PSC + 0x8c */ + u32 tximr; /* PSC + 0x90 */ + u32 txcnt; /* PSC + 0x94 */ + u32 txptr; /* PSC + 0x98 */ + u32 txsz; /* PSC + 0x9c */ + u32 reserved2[7]; + union { + u8 txdata_8; + u16 txdata_16; + u32 txdata_32; + } txdata; /* PSC + 0xbc */ +#define txdata_8 txdata.txdata_8 +#define txdata_16 txdata.txdata_16 +#define txdata_32 txdata.txdata_32 + u32 rxcmd; /* PSC + 0xc0 */ + u32 rxalarm; /* PSC + 0xc4 */ + u32 rxsr; /* PSC + 0xc8 */ + u32 rxisr; /* PSC + 0xcc */ + u32 rximr; /* PSC + 0xd0 */ + u32 rxcnt; /* PSC + 0xd4 */ + u32 rxptr; /* PSC + 0xd8 */ + u32 rxsz; /* PSC + 0xdc */ + u32 reserved3[7]; + union { + u8 rxdata_8; + u16 rxdata_16; + u32 rxdata_32; + } rxdata; /* PSC + 0xfc */ +#define rxdata_8 rxdata.rxdata_8 +#define rxdata_16 rxdata.rxdata_16 +#define rxdata_32 rxdata.rxdata_32 +}; #endif /* __ASM_MPC52xx_PSC_H__ */ -- cgit v1.2.3 From 52b804829ca4cf3d1d9849232dadf67269cd89f4 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 6 Feb 2008 22:29:25 -0700 Subject: [POWERPC] mpc52xx: fix compile error introduce when rebasing patch When rebasing one of the mpc5200 psc UART patches I made a mistake and damaged the patch. This patch fixes the compile failure introduced in commit 25ae3a0739c69425a911925b43213895a9802b98 Signed-off-by: Grant Likely --- drivers/serial/mpc52xx_uart.c | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 821facd10bbc..a638f23c6c61 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -131,31 +131,6 @@ static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id); #define uart_console(port) (0) #endif -#if defined(CONFIG_PPC_MERGE) -static struct of_device_id mpc52xx_uart_of_match[] = { -#ifdef CONFIG_PPC_MPC52xx - { .compatible = "fsl,mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, - /* binding used by old lite5200 device trees: */ - { .compatible = "mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, - /* binding used by efika: */ - { .compatible = "mpc5200-serial", .data = &mpc52xx_psc_ops, }, -#endif -#ifdef CONFIG_PPC_MPC512x - { .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, }, - {}, -#endif -}; -#if defined(CONFIG_PPC_MERGE) -static const struct of_device_id mpc52xx_uart_of_match[] = { - {.type = "serial", - .compatible = "mpc5200-psc-uart", -#endif - {}, -}; -#endif - -#endif - /* ======================================================================== */ /* PSC fifo operations for isolating differences between 52xx and 512x */ /* ======================================================================== */ @@ -1234,6 +1209,20 @@ static struct platform_driver mpc52xx_uart_platform_driver = { /* OF Platform Driver */ /* ======================================================================== */ +static struct of_device_id mpc52xx_uart_of_match[] = { +#ifdef CONFIG_PPC_MPC52xx + { .compatible = "fsl,mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, + /* binding used by old lite5200 device trees: */ + { .compatible = "mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, + /* binding used by efika: */ + { .compatible = "mpc5200-serial", .data = &mpc52xx_psc_ops, }, +#endif +#ifdef CONFIG_PPC_MPC512x + { .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, }, + {}, +#endif +}; + static int __devinit mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) { -- cgit v1.2.3