18c2ecf20Sopenharmony_ci/*****************************************************************************
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci *     Author: Xilinx, Inc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *     This program is free software; you can redistribute it and/or modify it
68c2ecf20Sopenharmony_ci *     under the terms of the GNU General Public License as published by the
78c2ecf20Sopenharmony_ci *     Free Software Foundation; either version 2 of the License, or (at your
88c2ecf20Sopenharmony_ci *     option) any later version.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS"
118c2ecf20Sopenharmony_ci *     AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND
128c2ecf20Sopenharmony_ci *     SOLUTIONS FOR XILINX DEVICES.  BY PROVIDING THIS DESIGN, CODE,
138c2ecf20Sopenharmony_ci *     OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE,
148c2ecf20Sopenharmony_ci *     APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION
158c2ecf20Sopenharmony_ci *     THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT,
168c2ecf20Sopenharmony_ci *     AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE
178c2ecf20Sopenharmony_ci *     FOR YOUR IMPLEMENTATION.  XILINX EXPRESSLY DISCLAIMS ANY
188c2ecf20Sopenharmony_ci *     WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE
198c2ecf20Sopenharmony_ci *     IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR
208c2ecf20Sopenharmony_ci *     REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF
218c2ecf20Sopenharmony_ci *     INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
228c2ecf20Sopenharmony_ci *     FOR A PARTICULAR PURPOSE.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci *     (c) Copyright 2002 Xilinx Inc., Systems Engineering Group
258c2ecf20Sopenharmony_ci *     (c) Copyright 2004 Xilinx Inc., Systems Engineering Group
268c2ecf20Sopenharmony_ci *     (c) Copyright 2007-2008 Xilinx Inc.
278c2ecf20Sopenharmony_ci *     All rights reserved.
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci *     You should have received a copy of the GNU General Public License along
308c2ecf20Sopenharmony_ci *     with this program; if not, write to the Free Software Foundation, Inc.,
318c2ecf20Sopenharmony_ci *     675 Mass Ave, Cambridge, MA 02139, USA.
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci *****************************************************************************/
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * This is the code behind /dev/icap* -- it allows a user-space
378c2ecf20Sopenharmony_ci * application to use the Xilinx ICAP subsystem.
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * The following operations are possible:
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci * open         open the port and initialize for access.
428c2ecf20Sopenharmony_ci * release      release port
438c2ecf20Sopenharmony_ci * write        Write a bitstream to the configuration processor.
448c2ecf20Sopenharmony_ci * read         Read a data stream from the configuration processor.
458c2ecf20Sopenharmony_ci *
468c2ecf20Sopenharmony_ci * After being opened, the port is initialized and accessed to avoid a
478c2ecf20Sopenharmony_ci * corrupted first read which may occur with some hardware.  The port
488c2ecf20Sopenharmony_ci * is left in a desynched state, requiring that a synch sequence be
498c2ecf20Sopenharmony_ci * transmitted before any valid configuration data.  A user will have
508c2ecf20Sopenharmony_ci * exclusive access to the device while it remains open, and the state
518c2ecf20Sopenharmony_ci * of the ICAP cannot be guaranteed after the device is closed.  Note
528c2ecf20Sopenharmony_ci * that a complete reset of the core and the state of the ICAP cannot
538c2ecf20Sopenharmony_ci * be performed on many versions of the cores, hence users of this
548c2ecf20Sopenharmony_ci * device should avoid making inconsistent accesses to the device.  In
558c2ecf20Sopenharmony_ci * particular, accessing the read interface, without first generating
568c2ecf20Sopenharmony_ci * a write containing a readback packet can leave the ICAP in an
578c2ecf20Sopenharmony_ci * inaccessible state.
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci * Note that in order to use the read interface, it is first necessary
608c2ecf20Sopenharmony_ci * to write a request packet to the write interface.  i.e., it is not
618c2ecf20Sopenharmony_ci * possible to simply readback the bitstream (or any configuration
628c2ecf20Sopenharmony_ci * bits) from a device without specifically requesting them first.
638c2ecf20Sopenharmony_ci * The code to craft such packets is intended to be part of the
648c2ecf20Sopenharmony_ci * user-space application code that uses this device.  The simplest
658c2ecf20Sopenharmony_ci * way to use this interface is simply:
668c2ecf20Sopenharmony_ci *
678c2ecf20Sopenharmony_ci * cp foo.bit /dev/icap0
688c2ecf20Sopenharmony_ci *
698c2ecf20Sopenharmony_ci * Note that unless foo.bit is an appropriately constructed partial
708c2ecf20Sopenharmony_ci * bitstream, this has a high likelihood of overwriting the design
718c2ecf20Sopenharmony_ci * currently programmed in the FPGA.
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#include <linux/module.h>
758c2ecf20Sopenharmony_ci#include <linux/kernel.h>
768c2ecf20Sopenharmony_ci#include <linux/types.h>
778c2ecf20Sopenharmony_ci#include <linux/ioport.h>
788c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
798c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
808c2ecf20Sopenharmony_ci#include <linux/init.h>
818c2ecf20Sopenharmony_ci#include <linux/poll.h>
828c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
838c2ecf20Sopenharmony_ci#include <linux/mutex.h>
848c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
858c2ecf20Sopenharmony_ci#include <linux/fs.h>
868c2ecf20Sopenharmony_ci#include <linux/cdev.h>
878c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
888c2ecf20Sopenharmony_ci#include <linux/slab.h>
898c2ecf20Sopenharmony_ci#include <linux/io.h>
908c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
938c2ecf20Sopenharmony_ci/* For open firmware. */
948c2ecf20Sopenharmony_ci#include <linux/of_address.h>
958c2ecf20Sopenharmony_ci#include <linux/of_device.h>
968c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
978c2ecf20Sopenharmony_ci#endif
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#include "xilinx_hwicap.h"
1008c2ecf20Sopenharmony_ci#include "buffer_icap.h"
1018c2ecf20Sopenharmony_ci#include "fifo_icap.h"
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci#define DRIVER_NAME "icap"
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci#define HWICAP_REGS   (0x10000)
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci#define XHWICAP_MAJOR 259
1088c2ecf20Sopenharmony_ci#define XHWICAP_MINOR 0
1098c2ecf20Sopenharmony_ci#define HWICAP_DEVICES 1
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/* An array, which is set to true when the device is registered. */
1128c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(hwicap_mutex);
1138c2ecf20Sopenharmony_cistatic bool probed_devices[HWICAP_DEVICES];
1148c2ecf20Sopenharmony_cistatic struct mutex icap_sem;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic struct class *icap_class;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci#define UNIMPLEMENTED 0xFFFF
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic const struct config_registers v2_config_registers = {
1218c2ecf20Sopenharmony_ci	.CRC = 0,
1228c2ecf20Sopenharmony_ci	.FAR = 1,
1238c2ecf20Sopenharmony_ci	.FDRI = 2,
1248c2ecf20Sopenharmony_ci	.FDRO = 3,
1258c2ecf20Sopenharmony_ci	.CMD = 4,
1268c2ecf20Sopenharmony_ci	.CTL = 5,
1278c2ecf20Sopenharmony_ci	.MASK = 6,
1288c2ecf20Sopenharmony_ci	.STAT = 7,
1298c2ecf20Sopenharmony_ci	.LOUT = 8,
1308c2ecf20Sopenharmony_ci	.COR = 9,
1318c2ecf20Sopenharmony_ci	.MFWR = 10,
1328c2ecf20Sopenharmony_ci	.FLR = 11,
1338c2ecf20Sopenharmony_ci	.KEY = 12,
1348c2ecf20Sopenharmony_ci	.CBC = 13,
1358c2ecf20Sopenharmony_ci	.IDCODE = 14,
1368c2ecf20Sopenharmony_ci	.AXSS = UNIMPLEMENTED,
1378c2ecf20Sopenharmony_ci	.C0R_1 = UNIMPLEMENTED,
1388c2ecf20Sopenharmony_ci	.CSOB = UNIMPLEMENTED,
1398c2ecf20Sopenharmony_ci	.WBSTAR = UNIMPLEMENTED,
1408c2ecf20Sopenharmony_ci	.TIMER = UNIMPLEMENTED,
1418c2ecf20Sopenharmony_ci	.BOOTSTS = UNIMPLEMENTED,
1428c2ecf20Sopenharmony_ci	.CTL_1 = UNIMPLEMENTED,
1438c2ecf20Sopenharmony_ci};
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic const struct config_registers v4_config_registers = {
1468c2ecf20Sopenharmony_ci	.CRC = 0,
1478c2ecf20Sopenharmony_ci	.FAR = 1,
1488c2ecf20Sopenharmony_ci	.FDRI = 2,
1498c2ecf20Sopenharmony_ci	.FDRO = 3,
1508c2ecf20Sopenharmony_ci	.CMD = 4,
1518c2ecf20Sopenharmony_ci	.CTL = 5,
1528c2ecf20Sopenharmony_ci	.MASK = 6,
1538c2ecf20Sopenharmony_ci	.STAT = 7,
1548c2ecf20Sopenharmony_ci	.LOUT = 8,
1558c2ecf20Sopenharmony_ci	.COR = 9,
1568c2ecf20Sopenharmony_ci	.MFWR = 10,
1578c2ecf20Sopenharmony_ci	.FLR = UNIMPLEMENTED,
1588c2ecf20Sopenharmony_ci	.KEY = UNIMPLEMENTED,
1598c2ecf20Sopenharmony_ci	.CBC = 11,
1608c2ecf20Sopenharmony_ci	.IDCODE = 12,
1618c2ecf20Sopenharmony_ci	.AXSS = 13,
1628c2ecf20Sopenharmony_ci	.C0R_1 = UNIMPLEMENTED,
1638c2ecf20Sopenharmony_ci	.CSOB = UNIMPLEMENTED,
1648c2ecf20Sopenharmony_ci	.WBSTAR = UNIMPLEMENTED,
1658c2ecf20Sopenharmony_ci	.TIMER = UNIMPLEMENTED,
1668c2ecf20Sopenharmony_ci	.BOOTSTS = UNIMPLEMENTED,
1678c2ecf20Sopenharmony_ci	.CTL_1 = UNIMPLEMENTED,
1688c2ecf20Sopenharmony_ci};
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic const struct config_registers v5_config_registers = {
1718c2ecf20Sopenharmony_ci	.CRC = 0,
1728c2ecf20Sopenharmony_ci	.FAR = 1,
1738c2ecf20Sopenharmony_ci	.FDRI = 2,
1748c2ecf20Sopenharmony_ci	.FDRO = 3,
1758c2ecf20Sopenharmony_ci	.CMD = 4,
1768c2ecf20Sopenharmony_ci	.CTL = 5,
1778c2ecf20Sopenharmony_ci	.MASK = 6,
1788c2ecf20Sopenharmony_ci	.STAT = 7,
1798c2ecf20Sopenharmony_ci	.LOUT = 8,
1808c2ecf20Sopenharmony_ci	.COR = 9,
1818c2ecf20Sopenharmony_ci	.MFWR = 10,
1828c2ecf20Sopenharmony_ci	.FLR = UNIMPLEMENTED,
1838c2ecf20Sopenharmony_ci	.KEY = UNIMPLEMENTED,
1848c2ecf20Sopenharmony_ci	.CBC = 11,
1858c2ecf20Sopenharmony_ci	.IDCODE = 12,
1868c2ecf20Sopenharmony_ci	.AXSS = 13,
1878c2ecf20Sopenharmony_ci	.C0R_1 = 14,
1888c2ecf20Sopenharmony_ci	.CSOB = 15,
1898c2ecf20Sopenharmony_ci	.WBSTAR = 16,
1908c2ecf20Sopenharmony_ci	.TIMER = 17,
1918c2ecf20Sopenharmony_ci	.BOOTSTS = 18,
1928c2ecf20Sopenharmony_ci	.CTL_1 = 19,
1938c2ecf20Sopenharmony_ci};
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic const struct config_registers v6_config_registers = {
1968c2ecf20Sopenharmony_ci	.CRC = 0,
1978c2ecf20Sopenharmony_ci	.FAR = 1,
1988c2ecf20Sopenharmony_ci	.FDRI = 2,
1998c2ecf20Sopenharmony_ci	.FDRO = 3,
2008c2ecf20Sopenharmony_ci	.CMD = 4,
2018c2ecf20Sopenharmony_ci	.CTL = 5,
2028c2ecf20Sopenharmony_ci	.MASK = 6,
2038c2ecf20Sopenharmony_ci	.STAT = 7,
2048c2ecf20Sopenharmony_ci	.LOUT = 8,
2058c2ecf20Sopenharmony_ci	.COR = 9,
2068c2ecf20Sopenharmony_ci	.MFWR = 10,
2078c2ecf20Sopenharmony_ci	.FLR = UNIMPLEMENTED,
2088c2ecf20Sopenharmony_ci	.KEY = UNIMPLEMENTED,
2098c2ecf20Sopenharmony_ci	.CBC = 11,
2108c2ecf20Sopenharmony_ci	.IDCODE = 12,
2118c2ecf20Sopenharmony_ci	.AXSS = 13,
2128c2ecf20Sopenharmony_ci	.C0R_1 = 14,
2138c2ecf20Sopenharmony_ci	.CSOB = 15,
2148c2ecf20Sopenharmony_ci	.WBSTAR = 16,
2158c2ecf20Sopenharmony_ci	.TIMER = 17,
2168c2ecf20Sopenharmony_ci	.BOOTSTS = 22,
2178c2ecf20Sopenharmony_ci	.CTL_1 = 24,
2188c2ecf20Sopenharmony_ci};
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci/**
2218c2ecf20Sopenharmony_ci * hwicap_command_desync - Send a DESYNC command to the ICAP port.
2228c2ecf20Sopenharmony_ci * @drvdata: a pointer to the drvdata.
2238c2ecf20Sopenharmony_ci *
2248c2ecf20Sopenharmony_ci * Returns: '0' on success and failure value on error
2258c2ecf20Sopenharmony_ci *
2268c2ecf20Sopenharmony_ci * This command desynchronizes the ICAP After this command, a
2278c2ecf20Sopenharmony_ci * bitstream containing a NULL packet, followed by a SYNCH packet is
2288c2ecf20Sopenharmony_ci * required before the ICAP will recognize commands.
2298c2ecf20Sopenharmony_ci */
2308c2ecf20Sopenharmony_cistatic int hwicap_command_desync(struct hwicap_drvdata *drvdata)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	u32 buffer[4];
2338c2ecf20Sopenharmony_ci	u32 index = 0;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	/*
2368c2ecf20Sopenharmony_ci	 * Create the data to be written to the ICAP.
2378c2ecf20Sopenharmony_ci	 */
2388c2ecf20Sopenharmony_ci	buffer[index++] = hwicap_type_1_write(drvdata->config_regs->CMD) | 1;
2398c2ecf20Sopenharmony_ci	buffer[index++] = XHI_CMD_DESYNCH;
2408c2ecf20Sopenharmony_ci	buffer[index++] = XHI_NOOP_PACKET;
2418c2ecf20Sopenharmony_ci	buffer[index++] = XHI_NOOP_PACKET;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	/*
2448c2ecf20Sopenharmony_ci	 * Write the data to the FIFO and intiate the transfer of data present
2458c2ecf20Sopenharmony_ci	 * in the FIFO to the ICAP device.
2468c2ecf20Sopenharmony_ci	 */
2478c2ecf20Sopenharmony_ci	return drvdata->config->set_configuration(drvdata,
2488c2ecf20Sopenharmony_ci			&buffer[0], index);
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/**
2528c2ecf20Sopenharmony_ci * hwicap_get_configuration_register - Query a configuration register.
2538c2ecf20Sopenharmony_ci * @drvdata: a pointer to the drvdata.
2548c2ecf20Sopenharmony_ci * @reg: a constant which represents the configuration
2558c2ecf20Sopenharmony_ci * register value to be returned.
2568c2ecf20Sopenharmony_ci * Examples: XHI_IDCODE, XHI_FLR.
2578c2ecf20Sopenharmony_ci * @reg_data: returns the value of the register.
2588c2ecf20Sopenharmony_ci *
2598c2ecf20Sopenharmony_ci * Returns: '0' on success and failure value on error
2608c2ecf20Sopenharmony_ci *
2618c2ecf20Sopenharmony_ci * Sends a query packet to the ICAP and then receives the response.
2628c2ecf20Sopenharmony_ci * The icap is left in Synched state.
2638c2ecf20Sopenharmony_ci */
2648c2ecf20Sopenharmony_cistatic int hwicap_get_configuration_register(struct hwicap_drvdata *drvdata,
2658c2ecf20Sopenharmony_ci		u32 reg, u32 *reg_data)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	int status;
2688c2ecf20Sopenharmony_ci	u32 buffer[6];
2698c2ecf20Sopenharmony_ci	u32 index = 0;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/*
2728c2ecf20Sopenharmony_ci	 * Create the data to be written to the ICAP.
2738c2ecf20Sopenharmony_ci	 */
2748c2ecf20Sopenharmony_ci	buffer[index++] = XHI_DUMMY_PACKET;
2758c2ecf20Sopenharmony_ci	buffer[index++] = XHI_NOOP_PACKET;
2768c2ecf20Sopenharmony_ci	buffer[index++] = XHI_SYNC_PACKET;
2778c2ecf20Sopenharmony_ci	buffer[index++] = XHI_NOOP_PACKET;
2788c2ecf20Sopenharmony_ci	buffer[index++] = XHI_NOOP_PACKET;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/*
2818c2ecf20Sopenharmony_ci	 * Write the data to the FIFO and initiate the transfer of data present
2828c2ecf20Sopenharmony_ci	 * in the FIFO to the ICAP device.
2838c2ecf20Sopenharmony_ci	 */
2848c2ecf20Sopenharmony_ci	status = drvdata->config->set_configuration(drvdata,
2858c2ecf20Sopenharmony_ci						    &buffer[0], index);
2868c2ecf20Sopenharmony_ci	if (status)
2878c2ecf20Sopenharmony_ci		return status;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/* If the syncword was not found, then we need to start over. */
2908c2ecf20Sopenharmony_ci	status = drvdata->config->get_status(drvdata);
2918c2ecf20Sopenharmony_ci	if ((status & XHI_SR_DALIGN_MASK) != XHI_SR_DALIGN_MASK)
2928c2ecf20Sopenharmony_ci		return -EIO;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	index = 0;
2958c2ecf20Sopenharmony_ci	buffer[index++] = hwicap_type_1_read(reg) | 1;
2968c2ecf20Sopenharmony_ci	buffer[index++] = XHI_NOOP_PACKET;
2978c2ecf20Sopenharmony_ci	buffer[index++] = XHI_NOOP_PACKET;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/*
3008c2ecf20Sopenharmony_ci	 * Write the data to the FIFO and intiate the transfer of data present
3018c2ecf20Sopenharmony_ci	 * in the FIFO to the ICAP device.
3028c2ecf20Sopenharmony_ci	 */
3038c2ecf20Sopenharmony_ci	status = drvdata->config->set_configuration(drvdata,
3048c2ecf20Sopenharmony_ci			&buffer[0], index);
3058c2ecf20Sopenharmony_ci	if (status)
3068c2ecf20Sopenharmony_ci		return status;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	/*
3098c2ecf20Sopenharmony_ci	 * Read the configuration register
3108c2ecf20Sopenharmony_ci	 */
3118c2ecf20Sopenharmony_ci	status = drvdata->config->get_configuration(drvdata, reg_data, 1);
3128c2ecf20Sopenharmony_ci	if (status)
3138c2ecf20Sopenharmony_ci		return status;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	return 0;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int hwicap_initialize_hwicap(struct hwicap_drvdata *drvdata)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	int status;
3218c2ecf20Sopenharmony_ci	u32 idcode;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	dev_dbg(drvdata->dev, "initializing\n");
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	/* Abort any current transaction, to make sure we have the
3268c2ecf20Sopenharmony_ci	 * ICAP in a good state.
3278c2ecf20Sopenharmony_ci	 */
3288c2ecf20Sopenharmony_ci	dev_dbg(drvdata->dev, "Reset...\n");
3298c2ecf20Sopenharmony_ci	drvdata->config->reset(drvdata);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	dev_dbg(drvdata->dev, "Desync...\n");
3328c2ecf20Sopenharmony_ci	status = hwicap_command_desync(drvdata);
3338c2ecf20Sopenharmony_ci	if (status)
3348c2ecf20Sopenharmony_ci		return status;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* Attempt to read the IDCODE from ICAP.  This
3378c2ecf20Sopenharmony_ci	 * may not be returned correctly, due to the design of the
3388c2ecf20Sopenharmony_ci	 * hardware.
3398c2ecf20Sopenharmony_ci	 */
3408c2ecf20Sopenharmony_ci	dev_dbg(drvdata->dev, "Reading IDCODE...\n");
3418c2ecf20Sopenharmony_ci	status = hwicap_get_configuration_register(
3428c2ecf20Sopenharmony_ci			drvdata, drvdata->config_regs->IDCODE, &idcode);
3438c2ecf20Sopenharmony_ci	dev_dbg(drvdata->dev, "IDCODE = %x\n", idcode);
3448c2ecf20Sopenharmony_ci	if (status)
3458c2ecf20Sopenharmony_ci		return status;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	dev_dbg(drvdata->dev, "Desync...\n");
3488c2ecf20Sopenharmony_ci	status = hwicap_command_desync(drvdata);
3498c2ecf20Sopenharmony_ci	if (status)
3508c2ecf20Sopenharmony_ci		return status;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	return 0;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic ssize_t
3568c2ecf20Sopenharmony_cihwicap_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	struct hwicap_drvdata *drvdata = file->private_data;
3598c2ecf20Sopenharmony_ci	ssize_t bytes_to_read = 0;
3608c2ecf20Sopenharmony_ci	u32 *kbuf;
3618c2ecf20Sopenharmony_ci	u32 words;
3628c2ecf20Sopenharmony_ci	u32 bytes_remaining;
3638c2ecf20Sopenharmony_ci	int status;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	status = mutex_lock_interruptible(&drvdata->sem);
3668c2ecf20Sopenharmony_ci	if (status)
3678c2ecf20Sopenharmony_ci		return status;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	if (drvdata->read_buffer_in_use) {
3708c2ecf20Sopenharmony_ci		/* If there are leftover bytes in the buffer, just */
3718c2ecf20Sopenharmony_ci		/* return them and don't try to read more from the */
3728c2ecf20Sopenharmony_ci		/* ICAP device. */
3738c2ecf20Sopenharmony_ci		bytes_to_read =
3748c2ecf20Sopenharmony_ci			(count < drvdata->read_buffer_in_use) ? count :
3758c2ecf20Sopenharmony_ci			drvdata->read_buffer_in_use;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		/* Return the data currently in the read buffer. */
3788c2ecf20Sopenharmony_ci		if (copy_to_user(buf, drvdata->read_buffer, bytes_to_read)) {
3798c2ecf20Sopenharmony_ci			status = -EFAULT;
3808c2ecf20Sopenharmony_ci			goto error;
3818c2ecf20Sopenharmony_ci		}
3828c2ecf20Sopenharmony_ci		drvdata->read_buffer_in_use -= bytes_to_read;
3838c2ecf20Sopenharmony_ci		memmove(drvdata->read_buffer,
3848c2ecf20Sopenharmony_ci		       drvdata->read_buffer + bytes_to_read,
3858c2ecf20Sopenharmony_ci		       4 - bytes_to_read);
3868c2ecf20Sopenharmony_ci	} else {
3878c2ecf20Sopenharmony_ci		/* Get new data from the ICAP, and return was was requested. */
3888c2ecf20Sopenharmony_ci		kbuf = (u32 *) get_zeroed_page(GFP_KERNEL);
3898c2ecf20Sopenharmony_ci		if (!kbuf) {
3908c2ecf20Sopenharmony_ci			status = -ENOMEM;
3918c2ecf20Sopenharmony_ci			goto error;
3928c2ecf20Sopenharmony_ci		}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci		/* The ICAP device is only able to read complete */
3958c2ecf20Sopenharmony_ci		/* words.  If a number of bytes that do not correspond */
3968c2ecf20Sopenharmony_ci		/* to complete words is requested, then we read enough */
3978c2ecf20Sopenharmony_ci		/* words to get the required number of bytes, and then */
3988c2ecf20Sopenharmony_ci		/* save the remaining bytes for the next read. */
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		/* Determine the number of words to read, rounding up */
4018c2ecf20Sopenharmony_ci		/* if necessary. */
4028c2ecf20Sopenharmony_ci		words = ((count + 3) >> 2);
4038c2ecf20Sopenharmony_ci		bytes_to_read = words << 2;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		if (bytes_to_read > PAGE_SIZE)
4068c2ecf20Sopenharmony_ci			bytes_to_read = PAGE_SIZE;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		/* Ensure we only read a complete number of words. */
4098c2ecf20Sopenharmony_ci		bytes_remaining = bytes_to_read & 3;
4108c2ecf20Sopenharmony_ci		bytes_to_read &= ~3;
4118c2ecf20Sopenharmony_ci		words = bytes_to_read >> 2;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci		status = drvdata->config->get_configuration(drvdata,
4148c2ecf20Sopenharmony_ci				kbuf, words);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci		/* If we didn't read correctly, then bail out. */
4178c2ecf20Sopenharmony_ci		if (status) {
4188c2ecf20Sopenharmony_ci			free_page((unsigned long)kbuf);
4198c2ecf20Sopenharmony_ci			goto error;
4208c2ecf20Sopenharmony_ci		}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		/* If we fail to return the data to the user, then bail out. */
4238c2ecf20Sopenharmony_ci		if (copy_to_user(buf, kbuf, bytes_to_read)) {
4248c2ecf20Sopenharmony_ci			free_page((unsigned long)kbuf);
4258c2ecf20Sopenharmony_ci			status = -EFAULT;
4268c2ecf20Sopenharmony_ci			goto error;
4278c2ecf20Sopenharmony_ci		}
4288c2ecf20Sopenharmony_ci		memcpy(drvdata->read_buffer,
4298c2ecf20Sopenharmony_ci		       kbuf,
4308c2ecf20Sopenharmony_ci		       bytes_remaining);
4318c2ecf20Sopenharmony_ci		drvdata->read_buffer_in_use = bytes_remaining;
4328c2ecf20Sopenharmony_ci		free_page((unsigned long)kbuf);
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci	status = bytes_to_read;
4358c2ecf20Sopenharmony_ci error:
4368c2ecf20Sopenharmony_ci	mutex_unlock(&drvdata->sem);
4378c2ecf20Sopenharmony_ci	return status;
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic ssize_t
4418c2ecf20Sopenharmony_cihwicap_write(struct file *file, const char __user *buf,
4428c2ecf20Sopenharmony_ci		size_t count, loff_t *ppos)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	struct hwicap_drvdata *drvdata = file->private_data;
4458c2ecf20Sopenharmony_ci	ssize_t written = 0;
4468c2ecf20Sopenharmony_ci	ssize_t left = count;
4478c2ecf20Sopenharmony_ci	u32 *kbuf;
4488c2ecf20Sopenharmony_ci	ssize_t len;
4498c2ecf20Sopenharmony_ci	ssize_t status;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	status = mutex_lock_interruptible(&drvdata->sem);
4528c2ecf20Sopenharmony_ci	if (status)
4538c2ecf20Sopenharmony_ci		return status;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	left += drvdata->write_buffer_in_use;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/* Only write multiples of 4 bytes. */
4588c2ecf20Sopenharmony_ci	if (left < 4) {
4598c2ecf20Sopenharmony_ci		status = 0;
4608c2ecf20Sopenharmony_ci		goto error;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	kbuf = (u32 *) __get_free_page(GFP_KERNEL);
4648c2ecf20Sopenharmony_ci	if (!kbuf) {
4658c2ecf20Sopenharmony_ci		status = -ENOMEM;
4668c2ecf20Sopenharmony_ci		goto error;
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	while (left > 3) {
4708c2ecf20Sopenharmony_ci		/* only write multiples of 4 bytes, so there might */
4718c2ecf20Sopenharmony_ci		/* be as many as 3 bytes left (at the end). */
4728c2ecf20Sopenharmony_ci		len = left;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		if (len > PAGE_SIZE)
4758c2ecf20Sopenharmony_ci			len = PAGE_SIZE;
4768c2ecf20Sopenharmony_ci		len &= ~3;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci		if (drvdata->write_buffer_in_use) {
4798c2ecf20Sopenharmony_ci			memcpy(kbuf, drvdata->write_buffer,
4808c2ecf20Sopenharmony_ci					drvdata->write_buffer_in_use);
4818c2ecf20Sopenharmony_ci			if (copy_from_user(
4828c2ecf20Sopenharmony_ci			    (((char *)kbuf) + drvdata->write_buffer_in_use),
4838c2ecf20Sopenharmony_ci			    buf + written,
4848c2ecf20Sopenharmony_ci			    len - (drvdata->write_buffer_in_use))) {
4858c2ecf20Sopenharmony_ci				free_page((unsigned long)kbuf);
4868c2ecf20Sopenharmony_ci				status = -EFAULT;
4878c2ecf20Sopenharmony_ci				goto error;
4888c2ecf20Sopenharmony_ci			}
4898c2ecf20Sopenharmony_ci		} else {
4908c2ecf20Sopenharmony_ci			if (copy_from_user(kbuf, buf + written, len)) {
4918c2ecf20Sopenharmony_ci				free_page((unsigned long)kbuf);
4928c2ecf20Sopenharmony_ci				status = -EFAULT;
4938c2ecf20Sopenharmony_ci				goto error;
4948c2ecf20Sopenharmony_ci			}
4958c2ecf20Sopenharmony_ci		}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci		status = drvdata->config->set_configuration(drvdata,
4988c2ecf20Sopenharmony_ci				kbuf, len >> 2);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci		if (status) {
5018c2ecf20Sopenharmony_ci			free_page((unsigned long)kbuf);
5028c2ecf20Sopenharmony_ci			status = -EFAULT;
5038c2ecf20Sopenharmony_ci			goto error;
5048c2ecf20Sopenharmony_ci		}
5058c2ecf20Sopenharmony_ci		if (drvdata->write_buffer_in_use) {
5068c2ecf20Sopenharmony_ci			len -= drvdata->write_buffer_in_use;
5078c2ecf20Sopenharmony_ci			left -= drvdata->write_buffer_in_use;
5088c2ecf20Sopenharmony_ci			drvdata->write_buffer_in_use = 0;
5098c2ecf20Sopenharmony_ci		}
5108c2ecf20Sopenharmony_ci		written += len;
5118c2ecf20Sopenharmony_ci		left -= len;
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci	if ((left > 0) && (left < 4)) {
5148c2ecf20Sopenharmony_ci		if (!copy_from_user(drvdata->write_buffer,
5158c2ecf20Sopenharmony_ci						buf + written, left)) {
5168c2ecf20Sopenharmony_ci			drvdata->write_buffer_in_use = left;
5178c2ecf20Sopenharmony_ci			written += left;
5188c2ecf20Sopenharmony_ci			left = 0;
5198c2ecf20Sopenharmony_ci		}
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	free_page((unsigned long)kbuf);
5238c2ecf20Sopenharmony_ci	status = written;
5248c2ecf20Sopenharmony_ci error:
5258c2ecf20Sopenharmony_ci	mutex_unlock(&drvdata->sem);
5268c2ecf20Sopenharmony_ci	return status;
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic int hwicap_open(struct inode *inode, struct file *file)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	struct hwicap_drvdata *drvdata;
5328c2ecf20Sopenharmony_ci	int status;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	mutex_lock(&hwicap_mutex);
5358c2ecf20Sopenharmony_ci	drvdata = container_of(inode->i_cdev, struct hwicap_drvdata, cdev);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	status = mutex_lock_interruptible(&drvdata->sem);
5388c2ecf20Sopenharmony_ci	if (status)
5398c2ecf20Sopenharmony_ci		goto out;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	if (drvdata->is_open) {
5428c2ecf20Sopenharmony_ci		status = -EBUSY;
5438c2ecf20Sopenharmony_ci		goto error;
5448c2ecf20Sopenharmony_ci	}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	status = hwicap_initialize_hwicap(drvdata);
5478c2ecf20Sopenharmony_ci	if (status) {
5488c2ecf20Sopenharmony_ci		dev_err(drvdata->dev, "Failed to open file");
5498c2ecf20Sopenharmony_ci		goto error;
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	file->private_data = drvdata;
5538c2ecf20Sopenharmony_ci	drvdata->write_buffer_in_use = 0;
5548c2ecf20Sopenharmony_ci	drvdata->read_buffer_in_use = 0;
5558c2ecf20Sopenharmony_ci	drvdata->is_open = 1;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci error:
5588c2ecf20Sopenharmony_ci	mutex_unlock(&drvdata->sem);
5598c2ecf20Sopenharmony_ci out:
5608c2ecf20Sopenharmony_ci	mutex_unlock(&hwicap_mutex);
5618c2ecf20Sopenharmony_ci	return status;
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cistatic int hwicap_release(struct inode *inode, struct file *file)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	struct hwicap_drvdata *drvdata = file->private_data;
5678c2ecf20Sopenharmony_ci	int i;
5688c2ecf20Sopenharmony_ci	int status = 0;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	mutex_lock(&drvdata->sem);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	if (drvdata->write_buffer_in_use) {
5738c2ecf20Sopenharmony_ci		/* Flush write buffer. */
5748c2ecf20Sopenharmony_ci		for (i = drvdata->write_buffer_in_use; i < 4; i++)
5758c2ecf20Sopenharmony_ci			drvdata->write_buffer[i] = 0;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci		status = drvdata->config->set_configuration(drvdata,
5788c2ecf20Sopenharmony_ci				(u32 *) drvdata->write_buffer, 1);
5798c2ecf20Sopenharmony_ci		if (status)
5808c2ecf20Sopenharmony_ci			goto error;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	status = hwicap_command_desync(drvdata);
5848c2ecf20Sopenharmony_ci	if (status)
5858c2ecf20Sopenharmony_ci		goto error;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci error:
5888c2ecf20Sopenharmony_ci	drvdata->is_open = 0;
5898c2ecf20Sopenharmony_ci	mutex_unlock(&drvdata->sem);
5908c2ecf20Sopenharmony_ci	return status;
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cistatic const struct file_operations hwicap_fops = {
5948c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
5958c2ecf20Sopenharmony_ci	.write = hwicap_write,
5968c2ecf20Sopenharmony_ci	.read = hwicap_read,
5978c2ecf20Sopenharmony_ci	.open = hwicap_open,
5988c2ecf20Sopenharmony_ci	.release = hwicap_release,
5998c2ecf20Sopenharmony_ci	.llseek = noop_llseek,
6008c2ecf20Sopenharmony_ci};
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic int hwicap_setup(struct device *dev, int id,
6038c2ecf20Sopenharmony_ci		const struct resource *regs_res,
6048c2ecf20Sopenharmony_ci		const struct hwicap_driver_config *config,
6058c2ecf20Sopenharmony_ci		const struct config_registers *config_regs)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	dev_t devt;
6088c2ecf20Sopenharmony_ci	struct hwicap_drvdata *drvdata = NULL;
6098c2ecf20Sopenharmony_ci	int retval = 0;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	dev_info(dev, "Xilinx icap port driver\n");
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	mutex_lock(&icap_sem);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if (id < 0) {
6168c2ecf20Sopenharmony_ci		for (id = 0; id < HWICAP_DEVICES; id++)
6178c2ecf20Sopenharmony_ci			if (!probed_devices[id])
6188c2ecf20Sopenharmony_ci				break;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci	if (id < 0 || id >= HWICAP_DEVICES) {
6218c2ecf20Sopenharmony_ci		mutex_unlock(&icap_sem);
6228c2ecf20Sopenharmony_ci		dev_err(dev, "%s%i too large\n", DRIVER_NAME, id);
6238c2ecf20Sopenharmony_ci		return -EINVAL;
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci	if (probed_devices[id]) {
6268c2ecf20Sopenharmony_ci		mutex_unlock(&icap_sem);
6278c2ecf20Sopenharmony_ci		dev_err(dev, "cannot assign to %s%i; it is already in use\n",
6288c2ecf20Sopenharmony_ci			DRIVER_NAME, id);
6298c2ecf20Sopenharmony_ci		return -EBUSY;
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	probed_devices[id] = 1;
6338c2ecf20Sopenharmony_ci	mutex_unlock(&icap_sem);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	devt = MKDEV(XHWICAP_MAJOR, XHWICAP_MINOR + id);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	drvdata = kzalloc(sizeof(struct hwicap_drvdata), GFP_KERNEL);
6388c2ecf20Sopenharmony_ci	if (!drvdata) {
6398c2ecf20Sopenharmony_ci		retval = -ENOMEM;
6408c2ecf20Sopenharmony_ci		goto failed0;
6418c2ecf20Sopenharmony_ci	}
6428c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, (void *)drvdata);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	if (!regs_res) {
6458c2ecf20Sopenharmony_ci		dev_err(dev, "Couldn't get registers resource\n");
6468c2ecf20Sopenharmony_ci		retval = -EFAULT;
6478c2ecf20Sopenharmony_ci		goto failed1;
6488c2ecf20Sopenharmony_ci	}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	drvdata->mem_start = regs_res->start;
6518c2ecf20Sopenharmony_ci	drvdata->mem_end = regs_res->end;
6528c2ecf20Sopenharmony_ci	drvdata->mem_size = resource_size(regs_res);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	if (!request_mem_region(drvdata->mem_start,
6558c2ecf20Sopenharmony_ci					drvdata->mem_size, DRIVER_NAME)) {
6568c2ecf20Sopenharmony_ci		dev_err(dev, "Couldn't lock memory region at %Lx\n",
6578c2ecf20Sopenharmony_ci			(unsigned long long) regs_res->start);
6588c2ecf20Sopenharmony_ci		retval = -EBUSY;
6598c2ecf20Sopenharmony_ci		goto failed1;
6608c2ecf20Sopenharmony_ci	}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	drvdata->devt = devt;
6638c2ecf20Sopenharmony_ci	drvdata->dev = dev;
6648c2ecf20Sopenharmony_ci	drvdata->base_address = ioremap(drvdata->mem_start, drvdata->mem_size);
6658c2ecf20Sopenharmony_ci	if (!drvdata->base_address) {
6668c2ecf20Sopenharmony_ci		dev_err(dev, "ioremap() failed\n");
6678c2ecf20Sopenharmony_ci		retval = -ENOMEM;
6688c2ecf20Sopenharmony_ci		goto failed2;
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	drvdata->config = config;
6728c2ecf20Sopenharmony_ci	drvdata->config_regs = config_regs;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	mutex_init(&drvdata->sem);
6758c2ecf20Sopenharmony_ci	drvdata->is_open = 0;
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	dev_info(dev, "ioremap %llx to %p with size %llx\n",
6788c2ecf20Sopenharmony_ci		 (unsigned long long) drvdata->mem_start,
6798c2ecf20Sopenharmony_ci		 drvdata->base_address,
6808c2ecf20Sopenharmony_ci		 (unsigned long long) drvdata->mem_size);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	cdev_init(&drvdata->cdev, &hwicap_fops);
6838c2ecf20Sopenharmony_ci	drvdata->cdev.owner = THIS_MODULE;
6848c2ecf20Sopenharmony_ci	retval = cdev_add(&drvdata->cdev, devt, 1);
6858c2ecf20Sopenharmony_ci	if (retval) {
6868c2ecf20Sopenharmony_ci		dev_err(dev, "cdev_add() failed\n");
6878c2ecf20Sopenharmony_ci		goto failed3;
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	device_create(icap_class, dev, devt, NULL, "%s%d", DRIVER_NAME, id);
6918c2ecf20Sopenharmony_ci	return 0;		/* success */
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci failed3:
6948c2ecf20Sopenharmony_ci	iounmap(drvdata->base_address);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci failed2:
6978c2ecf20Sopenharmony_ci	release_mem_region(regs_res->start, drvdata->mem_size);
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci failed1:
7008c2ecf20Sopenharmony_ci	kfree(drvdata);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci failed0:
7038c2ecf20Sopenharmony_ci	mutex_lock(&icap_sem);
7048c2ecf20Sopenharmony_ci	probed_devices[id] = 0;
7058c2ecf20Sopenharmony_ci	mutex_unlock(&icap_sem);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	return retval;
7088c2ecf20Sopenharmony_ci}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_cistatic struct hwicap_driver_config buffer_icap_config = {
7118c2ecf20Sopenharmony_ci	.get_configuration = buffer_icap_get_configuration,
7128c2ecf20Sopenharmony_ci	.set_configuration = buffer_icap_set_configuration,
7138c2ecf20Sopenharmony_ci	.get_status = buffer_icap_get_status,
7148c2ecf20Sopenharmony_ci	.reset = buffer_icap_reset,
7158c2ecf20Sopenharmony_ci};
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_cistatic struct hwicap_driver_config fifo_icap_config = {
7188c2ecf20Sopenharmony_ci	.get_configuration = fifo_icap_get_configuration,
7198c2ecf20Sopenharmony_ci	.set_configuration = fifo_icap_set_configuration,
7208c2ecf20Sopenharmony_ci	.get_status = fifo_icap_get_status,
7218c2ecf20Sopenharmony_ci	.reset = fifo_icap_reset,
7228c2ecf20Sopenharmony_ci};
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_cistatic int hwicap_remove(struct device *dev)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	struct hwicap_drvdata *drvdata;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	drvdata = dev_get_drvdata(dev);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	if (!drvdata)
7318c2ecf20Sopenharmony_ci		return 0;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	device_destroy(icap_class, drvdata->devt);
7348c2ecf20Sopenharmony_ci	cdev_del(&drvdata->cdev);
7358c2ecf20Sopenharmony_ci	iounmap(drvdata->base_address);
7368c2ecf20Sopenharmony_ci	release_mem_region(drvdata->mem_start, drvdata->mem_size);
7378c2ecf20Sopenharmony_ci	kfree(drvdata);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	mutex_lock(&icap_sem);
7408c2ecf20Sopenharmony_ci	probed_devices[MINOR(dev->devt)-XHWICAP_MINOR] = 0;
7418c2ecf20Sopenharmony_ci	mutex_unlock(&icap_sem);
7428c2ecf20Sopenharmony_ci	return 0;		/* success */
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
7468c2ecf20Sopenharmony_cistatic int hwicap_of_probe(struct platform_device *op,
7478c2ecf20Sopenharmony_ci				     const struct hwicap_driver_config *config)
7488c2ecf20Sopenharmony_ci{
7498c2ecf20Sopenharmony_ci	struct resource res;
7508c2ecf20Sopenharmony_ci	const unsigned int *id;
7518c2ecf20Sopenharmony_ci	const char *family;
7528c2ecf20Sopenharmony_ci	int rc;
7538c2ecf20Sopenharmony_ci	const struct config_registers *regs;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	rc = of_address_to_resource(op->dev.of_node, 0, &res);
7578c2ecf20Sopenharmony_ci	if (rc) {
7588c2ecf20Sopenharmony_ci		dev_err(&op->dev, "invalid address\n");
7598c2ecf20Sopenharmony_ci		return rc;
7608c2ecf20Sopenharmony_ci	}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	id = of_get_property(op->dev.of_node, "port-number", NULL);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	/* It's most likely that we're using V4, if the family is not
7658c2ecf20Sopenharmony_ci	 * specified
7668c2ecf20Sopenharmony_ci	 */
7678c2ecf20Sopenharmony_ci	regs = &v4_config_registers;
7688c2ecf20Sopenharmony_ci	family = of_get_property(op->dev.of_node, "xlnx,family", NULL);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	if (family) {
7718c2ecf20Sopenharmony_ci		if (!strcmp(family, "virtex2p"))
7728c2ecf20Sopenharmony_ci			regs = &v2_config_registers;
7738c2ecf20Sopenharmony_ci		else if (!strcmp(family, "virtex4"))
7748c2ecf20Sopenharmony_ci			regs = &v4_config_registers;
7758c2ecf20Sopenharmony_ci		else if (!strcmp(family, "virtex5"))
7768c2ecf20Sopenharmony_ci			regs = &v5_config_registers;
7778c2ecf20Sopenharmony_ci		else if (!strcmp(family, "virtex6"))
7788c2ecf20Sopenharmony_ci			regs = &v6_config_registers;
7798c2ecf20Sopenharmony_ci	}
7808c2ecf20Sopenharmony_ci	return hwicap_setup(&op->dev, id ? *id : -1, &res, config,
7818c2ecf20Sopenharmony_ci			regs);
7828c2ecf20Sopenharmony_ci}
7838c2ecf20Sopenharmony_ci#else
7848c2ecf20Sopenharmony_cistatic inline int hwicap_of_probe(struct platform_device *op,
7858c2ecf20Sopenharmony_ci				  const struct hwicap_driver_config *config)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	return -EINVAL;
7888c2ecf20Sopenharmony_ci}
7898c2ecf20Sopenharmony_ci#endif /* CONFIG_OF */
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_cistatic const struct of_device_id hwicap_of_match[];
7928c2ecf20Sopenharmony_cistatic int hwicap_drv_probe(struct platform_device *pdev)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	const struct of_device_id *match;
7958c2ecf20Sopenharmony_ci	struct resource *res;
7968c2ecf20Sopenharmony_ci	const struct config_registers *regs;
7978c2ecf20Sopenharmony_ci	const char *family;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	match = of_match_device(hwicap_of_match, &pdev->dev);
8008c2ecf20Sopenharmony_ci	if (match)
8018c2ecf20Sopenharmony_ci		return hwicap_of_probe(pdev, match->data);
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
8048c2ecf20Sopenharmony_ci	if (!res)
8058c2ecf20Sopenharmony_ci		return -ENODEV;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	/* It's most likely that we're using V4, if the family is not
8088c2ecf20Sopenharmony_ci	 * specified
8098c2ecf20Sopenharmony_ci	 */
8108c2ecf20Sopenharmony_ci	regs = &v4_config_registers;
8118c2ecf20Sopenharmony_ci	family = pdev->dev.platform_data;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	if (family) {
8148c2ecf20Sopenharmony_ci		if (!strcmp(family, "virtex2p"))
8158c2ecf20Sopenharmony_ci			regs = &v2_config_registers;
8168c2ecf20Sopenharmony_ci		else if (!strcmp(family, "virtex4"))
8178c2ecf20Sopenharmony_ci			regs = &v4_config_registers;
8188c2ecf20Sopenharmony_ci		else if (!strcmp(family, "virtex5"))
8198c2ecf20Sopenharmony_ci			regs = &v5_config_registers;
8208c2ecf20Sopenharmony_ci		else if (!strcmp(family, "virtex6"))
8218c2ecf20Sopenharmony_ci			regs = &v6_config_registers;
8228c2ecf20Sopenharmony_ci	}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	return hwicap_setup(&pdev->dev, pdev->id, res,
8258c2ecf20Sopenharmony_ci			&buffer_icap_config, regs);
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_cistatic int hwicap_drv_remove(struct platform_device *pdev)
8298c2ecf20Sopenharmony_ci{
8308c2ecf20Sopenharmony_ci	return hwicap_remove(&pdev->dev);
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
8348c2ecf20Sopenharmony_ci/* Match table for device tree binding */
8358c2ecf20Sopenharmony_cistatic const struct of_device_id hwicap_of_match[] = {
8368c2ecf20Sopenharmony_ci	{ .compatible = "xlnx,opb-hwicap-1.00.b", .data = &buffer_icap_config},
8378c2ecf20Sopenharmony_ci	{ .compatible = "xlnx,xps-hwicap-1.00.a", .data = &fifo_icap_config},
8388c2ecf20Sopenharmony_ci	{},
8398c2ecf20Sopenharmony_ci};
8408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, hwicap_of_match);
8418c2ecf20Sopenharmony_ci#else
8428c2ecf20Sopenharmony_ci#define hwicap_of_match NULL
8438c2ecf20Sopenharmony_ci#endif
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_cistatic struct platform_driver hwicap_platform_driver = {
8468c2ecf20Sopenharmony_ci	.probe = hwicap_drv_probe,
8478c2ecf20Sopenharmony_ci	.remove = hwicap_drv_remove,
8488c2ecf20Sopenharmony_ci	.driver = {
8498c2ecf20Sopenharmony_ci		.name = DRIVER_NAME,
8508c2ecf20Sopenharmony_ci		.of_match_table = hwicap_of_match,
8518c2ecf20Sopenharmony_ci	},
8528c2ecf20Sopenharmony_ci};
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_cistatic int __init hwicap_module_init(void)
8558c2ecf20Sopenharmony_ci{
8568c2ecf20Sopenharmony_ci	dev_t devt;
8578c2ecf20Sopenharmony_ci	int retval;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	icap_class = class_create(THIS_MODULE, "xilinx_config");
8608c2ecf20Sopenharmony_ci	mutex_init(&icap_sem);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	devt = MKDEV(XHWICAP_MAJOR, XHWICAP_MINOR);
8638c2ecf20Sopenharmony_ci	retval = register_chrdev_region(devt,
8648c2ecf20Sopenharmony_ci					HWICAP_DEVICES,
8658c2ecf20Sopenharmony_ci					DRIVER_NAME);
8668c2ecf20Sopenharmony_ci	if (retval < 0)
8678c2ecf20Sopenharmony_ci		return retval;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	retval = platform_driver_register(&hwicap_platform_driver);
8708c2ecf20Sopenharmony_ci	if (retval)
8718c2ecf20Sopenharmony_ci		goto failed;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	return retval;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci failed:
8768c2ecf20Sopenharmony_ci	unregister_chrdev_region(devt, HWICAP_DEVICES);
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	return retval;
8798c2ecf20Sopenharmony_ci}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cistatic void __exit hwicap_module_cleanup(void)
8828c2ecf20Sopenharmony_ci{
8838c2ecf20Sopenharmony_ci	dev_t devt = MKDEV(XHWICAP_MAJOR, XHWICAP_MINOR);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	class_destroy(icap_class);
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	platform_driver_unregister(&hwicap_platform_driver);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	unregister_chrdev_region(devt, HWICAP_DEVICES);
8908c2ecf20Sopenharmony_ci}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_cimodule_init(hwicap_module_init);
8938c2ecf20Sopenharmony_cimodule_exit(hwicap_module_cleanup);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ciMODULE_AUTHOR("Xilinx, Inc; Xilinx Research Labs Group");
8968c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xilinx ICAP Port Driver");
8978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
898