[PATCH] [POWERPC] Xilinx: hwicap driver
Grant Likely
grant.likely at secretlab.ca
Fri Feb 1 06:58:32 EST 2008
On 1/31/08, Stephen Neuendorffer <stephen.neuendorffer at xilinx.com> wrote:
> 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 <stephen.neuendorffer at xilinx.com>
Comments below...
Also, I'm not sure if drivers/char/xilinx_hwicap is the best place for
this driver. drivers/misc/ might be better (but I'm not sure; poll
other people on this)
Finally, buffer_icap and fifo_icap are not huge blocks of code. Will
they be used by any other drivers? Can they be rolled into the
xilinx_hwicap.c file?
> +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) {
> + /* 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;
> + }
> + } else {
> + return -EINVAL;
> + }
Style comment: reverse the condition and bail with return -EINVAL at
the test point. It will simplify the code and better match with
common practice in the kernel.
> + 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) {
> + /* 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;
> + }
> + } else {
> + return -EINVAL;
> + }
Ditto
> +/**
> + * 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) {
> + /* 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;
> + } else {
> + buffer_count++;
> + }
Nit: Consider reversing the test condition and using 'buffer_count++; continue'
> +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 (drvdata->is_accessing)
> + return -EBUSY;
> +
> + drvdata->is_accessing = 1;
Race condition, you need to use a semaphore. Otherwise it is possible
to get two processes (or even two threads of the same process) in the
read() hook.
> +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 (drvdata->is_open)
> + return -EBUSY;
> +
> + drvdata->is_open = 1;
Also a race condition. Use a semaphore.
Also, this isn't sufficient to prevent 2 processes from having the
device open. If a process has already opened it and then calls
fork(), then *both* processes will have it opened even though the open
fop was only called once. (It might not matter for this particular
driver as the use case is limited; but it is something you should be
aware of.)
> +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;
Hmmm, I'm not thrilled with the fixed array of HWICAP devices. That
sort of thing just ends up causing headaches down the road. Can the
driver just allocate a new structure and the next available ID at
probe time? (I hold my nose every time I write something like the
above because I know I'm going to regret it later). sysfs/udev can
provide details about which one is which.
> +
> +static int __devexit hwicap_remove(struct device *dev)
> +{
> + struct hwicap_drvdata *drvdata;
> +
> + drvdata = (struct hwicap_drvdata *)dev_get_drvdata(dev);
> +
> + if (drvdata) {
if (!drvdata)
return 0;
Cheers,
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
More information about the Linuxppc-dev
mailing list