aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform/goldfish/goldfish_pipe.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/goldfish/goldfish_pipe.c')
-rw-r--r--drivers/platform/goldfish/goldfish_pipe.c399
1 files changed, 192 insertions, 207 deletions
diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c
index 2da567540c2d..7c639006252e 100644
--- a/drivers/platform/goldfish/goldfish_pipe.c
+++ b/drivers/platform/goldfish/goldfish_pipe.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2012 Intel, Inc.
* Copyright (C) 2013 Intel, Inc.
@@ -46,7 +47,6 @@
* exchange is properly mapped during a transfer.
*/
-
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/interrupt.h>
@@ -59,10 +59,11 @@
#include <linux/bitops.h>
#include <linux/slab.h>
#include <linux/io.h>
-#include <linux/goldfish.h>
#include <linux/dma-mapping.h>
#include <linux/mm.h>
#include <linux/acpi.h>
+#include <linux/bug.h>
+#include "goldfish_pipe_qemu.h"
/*
* Update this when something changes in the driver's behavior so the host
@@ -73,71 +74,6 @@ enum {
PIPE_CURRENT_DEVICE_VERSION = 2
};
-/*
- * IMPORTANT: The following constants must match the ones used and defined
- * in external/qemu/hw/goldfish_pipe.c in the Android source tree.
- */
-
-/* List of bitflags returned in status of CMD_POLL command */
-enum PipePollFlags {
- PIPE_POLL_IN = 1 << 0,
- PIPE_POLL_OUT = 1 << 1,
- PIPE_POLL_HUP = 1 << 2
-};
-
-/* Possible status values used to signal errors - see goldfish_pipe_error_convert */
-enum PipeErrors {
- PIPE_ERROR_INVAL = -1,
- PIPE_ERROR_AGAIN = -2,
- PIPE_ERROR_NOMEM = -3,
- PIPE_ERROR_IO = -4
-};
-
-/* Bit-flags used to signal events from the emulator */
-enum PipeWakeFlags {
- PIPE_WAKE_CLOSED = 1 << 0, /* emulator closed pipe */
- PIPE_WAKE_READ = 1 << 1, /* pipe can now be read from */
- PIPE_WAKE_WRITE = 1 << 2 /* pipe can now be written to */
-};
-
-/* Bit flags for the 'flags' field */
-enum PipeFlagsBits {
- BIT_CLOSED_ON_HOST = 0, /* pipe closed by host */
- BIT_WAKE_ON_WRITE = 1, /* want to be woken on writes */
- BIT_WAKE_ON_READ = 2, /* want to be woken on reads */
-};
-
-enum PipeRegs {
- PIPE_REG_CMD = 0,
-
- PIPE_REG_SIGNAL_BUFFER_HIGH = 4,
- PIPE_REG_SIGNAL_BUFFER = 8,
- PIPE_REG_SIGNAL_BUFFER_COUNT = 12,
-
- PIPE_REG_OPEN_BUFFER_HIGH = 20,
- PIPE_REG_OPEN_BUFFER = 24,
-
- PIPE_REG_VERSION = 36,
-
- PIPE_REG_GET_SIGNALLED = 48,
-};
-
-enum PipeCmdCode {
- PIPE_CMD_OPEN = 1, /* to be used by the pipe device itself */
- PIPE_CMD_CLOSE,
- PIPE_CMD_POLL,
- PIPE_CMD_WRITE,
- PIPE_CMD_WAKE_ON_WRITE,
- PIPE_CMD_READ,
- PIPE_CMD_WAKE_ON_READ,
-
- /*
- * TODO(zyy): implement a deferred read/write execution to allow
- * parallel processing of pipe operations on the host.
- */
- PIPE_CMD_WAKE_ON_DONE_IO,
-};
-
enum {
MAX_BUFFERS_PER_COMMAND = 336,
MAX_SIGNALLED_PIPES = 64,
@@ -145,14 +81,12 @@ enum {
};
struct goldfish_pipe_dev;
-struct goldfish_pipe;
-struct goldfish_pipe_command;
/* A per-pipe command structure, shared with the host */
struct goldfish_pipe_command {
- s32 cmd; /* PipeCmdCode, guest -> host */
- s32 id; /* pipe id, guest -> host */
- s32 status; /* command execution status, host -> guest */
+ s32 cmd; /* PipeCmdCode, guest -> host */
+ s32 id; /* pipe id, guest -> host */
+ s32 status; /* command execution status, host -> guest */
s32 reserved; /* to pad to 64-bit boundary */
union {
/* Parameters for PIPE_CMD_{READ,WRITE} */
@@ -184,19 +118,21 @@ struct open_command_param {
/* Device-level set of buffers shared with the host */
struct goldfish_pipe_dev_buffers {
struct open_command_param open_command_params;
- struct signalled_pipe_buffer signalled_pipe_buffers[
- MAX_SIGNALLED_PIPES];
+ struct signalled_pipe_buffer
+ signalled_pipe_buffers[MAX_SIGNALLED_PIPES];
};
/* This data type models a given pipe instance */
struct goldfish_pipe {
/* pipe ID - index into goldfish_pipe_dev::pipes array */
u32 id;
+
/* The wake flags pipe is waiting for
* Note: not protected with any lock, uses atomic operations
* and barriers to make it thread-safe.
*/
unsigned long flags;
+
/* wake flags host have signalled,
* - protected by goldfish_pipe_dev::lock
*/
@@ -220,8 +156,12 @@ struct goldfish_pipe {
/* A wake queue for sleeping until host signals an event */
wait_queue_head_t wake_queue;
+
/* Pointer to the parent goldfish_pipe_dev instance */
struct goldfish_pipe_dev *dev;
+
+ /* A buffer of pages, too large to fit into a stack frame */
+ struct page *pages[MAX_BUFFERS_PER_COMMAND];
};
/* The global driver data. Holds a reference to the i/o page used to
@@ -229,6 +169,9 @@ struct goldfish_pipe {
* waiting to be awoken.
*/
struct goldfish_pipe_dev {
+ /* A magic number to check if this is an instance of this struct */
+ void *magic;
+
/*
* Global device spinlock. Protects the following members:
* - pipes, pipes_capacity
@@ -261,15 +204,22 @@ struct goldfish_pipe_dev {
/* Head of a doubly linked list of signalled pipes */
struct goldfish_pipe *first_signalled_pipe;
+ /* ptr to platform device's device struct */
+ struct device *pdev_dev;
+
/* Some device-specific data */
int irq;
int version;
unsigned char __iomem *base;
-};
-static struct goldfish_pipe_dev pipe_dev[1] = {};
+ /* an irq tasklet to run goldfish_interrupt_task */
+ struct tasklet_struct irq_tasklet;
-static int goldfish_cmd_locked(struct goldfish_pipe *pipe, enum PipeCmdCode cmd)
+ struct miscdevice miscdev;
+};
+
+static int goldfish_pipe_cmd_locked(struct goldfish_pipe *pipe,
+ enum PipeCmdCode cmd)
{
pipe->command_buffer->cmd = cmd;
/* failure by default */
@@ -278,13 +228,13 @@ static int goldfish_cmd_locked(struct goldfish_pipe *pipe, enum PipeCmdCode cmd)
return pipe->command_buffer->status;
}
-static int goldfish_cmd(struct goldfish_pipe *pipe, enum PipeCmdCode cmd)
+static int goldfish_pipe_cmd(struct goldfish_pipe *pipe, enum PipeCmdCode cmd)
{
int status;
if (mutex_lock_interruptible(&pipe->lock))
return PIPE_ERROR_IO;
- status = goldfish_cmd_locked(pipe, cmd);
+ status = goldfish_pipe_cmd_locked(pipe, cmd);
mutex_unlock(&pipe->lock);
return status;
}
@@ -307,10 +257,12 @@ static int goldfish_pipe_error_convert(int status)
}
}
-static int pin_user_pages(unsigned long first_page, unsigned long last_page,
- unsigned int last_page_size, int is_write,
- struct page *pages[MAX_BUFFERS_PER_COMMAND],
- unsigned int *iter_last_page_size)
+static int pin_user_pages(unsigned long first_page,
+ unsigned long last_page,
+ unsigned int last_page_size,
+ int is_write,
+ struct page *pages[MAX_BUFFERS_PER_COMMAND],
+ unsigned int *iter_last_page_size)
{
int ret;
int requested_pages = ((last_page - first_page) >> PAGE_SHIFT) + 1;
@@ -322,18 +274,18 @@ static int pin_user_pages(unsigned long first_page, unsigned long last_page,
*iter_last_page_size = last_page_size;
}
- ret = get_user_pages_fast(
- first_page, requested_pages, !is_write, pages);
+ ret = get_user_pages_fast(first_page, requested_pages, !is_write,
+ pages);
if (ret <= 0)
return -EFAULT;
if (ret < requested_pages)
*iter_last_page_size = PAGE_SIZE;
- return ret;
+ return ret;
}
static void release_user_pages(struct page **pages, int pages_count,
- int is_write, s32 consumed_size)
+ int is_write, s32 consumed_size)
{
int i;
@@ -345,12 +297,15 @@ static void release_user_pages(struct page **pages, int pages_count,
}
/* Populate the call parameters, merging adjacent pages together */
-static void populate_rw_params(
- struct page **pages, int pages_count,
- unsigned long address, unsigned long address_end,
- unsigned long first_page, unsigned long last_page,
- unsigned int iter_last_page_size, int is_write,
- struct goldfish_pipe_command *command)
+static void populate_rw_params(struct page **pages,
+ int pages_count,
+ unsigned long address,
+ unsigned long address_end,
+ unsigned long first_page,
+ unsigned long last_page,
+ unsigned int iter_last_page_size,
+ int is_write,
+ struct goldfish_pipe_command *command)
{
/*
* Process the first page separately - it's the only page that
@@ -382,55 +337,59 @@ static void populate_rw_params(
}
static int transfer_max_buffers(struct goldfish_pipe *pipe,
- unsigned long address, unsigned long address_end, int is_write,
- unsigned long last_page, unsigned int last_page_size,
- s32 *consumed_size, int *status)
+ unsigned long address,
+ unsigned long address_end,
+ int is_write,
+ unsigned long last_page,
+ unsigned int last_page_size,
+ s32 *consumed_size,
+ int *status)
{
- static struct page *pages[MAX_BUFFERS_PER_COMMAND];
unsigned long first_page = address & PAGE_MASK;
unsigned int iter_last_page_size;
- int pages_count = pin_user_pages(first_page, last_page,
- last_page_size, is_write,
- pages, &iter_last_page_size);
-
- if (pages_count < 0)
- return pages_count;
+ int pages_count;
/* Serialize access to the pipe command buffers */
if (mutex_lock_interruptible(&pipe->lock))
return -ERESTARTSYS;
- populate_rw_params(pages, pages_count, address, address_end,
- first_page, last_page, iter_last_page_size, is_write,
- pipe->command_buffer);
+ pages_count = pin_user_pages(first_page, last_page,
+ last_page_size, is_write,
+ pipe->pages, &iter_last_page_size);
+ if (pages_count < 0) {
+ mutex_unlock(&pipe->lock);
+ return pages_count;
+ }
+
+ populate_rw_params(pipe->pages, pages_count, address, address_end,
+ first_page, last_page, iter_last_page_size, is_write,
+ pipe->command_buffer);
/* Transfer the data */
- *status = goldfish_cmd_locked(pipe,
+ *status = goldfish_pipe_cmd_locked(pipe,
is_write ? PIPE_CMD_WRITE : PIPE_CMD_READ);
*consumed_size = pipe->command_buffer->rw_params.consumed_size;
- release_user_pages(pages, pages_count, is_write, *consumed_size);
+ release_user_pages(pipe->pages, pages_count, is_write, *consumed_size);
mutex_unlock(&pipe->lock);
-
return 0;
}
static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write)
{
- u32 wakeBit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ;
+ u32 wake_bit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ;
- set_bit(wakeBit, &pipe->flags);
+ set_bit(wake_bit, &pipe->flags);
/* Tell the emulator we're going to wait for a wake event */
- (void)goldfish_cmd(pipe,
+ goldfish_pipe_cmd(pipe,
is_write ? PIPE_CMD_WAKE_ON_WRITE : PIPE_CMD_WAKE_ON_READ);
- while (test_bit(wakeBit, &pipe->flags)) {
- if (wait_event_interruptible(
- pipe->wake_queue,
- !test_bit(wakeBit, &pipe->flags)))
+ while (test_bit(wake_bit, &pipe->flags)) {
+ if (wait_event_interruptible(pipe->wake_queue,
+ !test_bit(wake_bit, &pipe->flags)))
return -ERESTARTSYS;
if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags))
@@ -441,7 +400,9 @@ static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write)
}
static ssize_t goldfish_pipe_read_write(struct file *filp,
- char __user *buffer, size_t bufflen, int is_write)
+ char __user *buffer,
+ size_t bufflen,
+ int is_write)
{
struct goldfish_pipe *pipe = filp->private_data;
int count = 0, ret = -EINVAL;
@@ -456,7 +417,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp,
return 0;
/* Check the buffer range for access */
if (unlikely(!access_ok(is_write ? VERIFY_WRITE : VERIFY_READ,
- buffer, bufflen)))
+ buffer, bufflen)))
return -EFAULT;
address = (unsigned long)buffer;
@@ -469,8 +430,8 @@ static ssize_t goldfish_pipe_read_write(struct file *filp,
int status;
ret = transfer_max_buffers(pipe, address, address_end, is_write,
- last_page, last_page_size, &consumed_size,
- &status);
+ last_page, last_page_size,
+ &consumed_size, &status);
if (ret < 0)
break;
@@ -496,7 +457,8 @@ static ssize_t goldfish_pipe_read_write(struct file *filp,
* err.
*/
if (status != PIPE_ERROR_AGAIN)
- pr_info_ratelimited("goldfish_pipe: backend error %d on %s\n",
+ dev_err_ratelimited(pipe->dev->pdev_dev,
+ "backend error %d on %s\n",
status, is_write ? "write" : "read");
break;
}
@@ -522,19 +484,21 @@ static ssize_t goldfish_pipe_read_write(struct file *filp,
}
static ssize_t goldfish_pipe_read(struct file *filp, char __user *buffer,
- size_t bufflen, loff_t *ppos)
+ size_t bufflen, loff_t *ppos)
{
return goldfish_pipe_read_write(filp, buffer, bufflen,
- /* is_write */ 0);
+ /* is_write */ 0);
}
static ssize_t goldfish_pipe_write(struct file *filp,
- const char __user *buffer, size_t bufflen,
- loff_t *ppos)
+ const char __user *buffer, size_t bufflen,
+ loff_t *ppos)
{
- return goldfish_pipe_read_write(filp,
- /* cast away the const */(char __user *)buffer, bufflen,
- /* is_write */ 1);
+ /* cast away the const */
+ char __user *no_const_buffer = (char __user *)buffer;
+
+ return goldfish_pipe_read_write(filp, no_const_buffer, bufflen,
+ /* is_write */ 1);
}
static __poll_t goldfish_pipe_poll(struct file *filp, poll_table *wait)
@@ -545,7 +509,7 @@ static __poll_t goldfish_pipe_poll(struct file *filp, poll_table *wait)
poll_wait(filp, &pipe->wake_queue, wait);
- status = goldfish_cmd(pipe, PIPE_CMD_POLL);
+ status = goldfish_pipe_cmd(pipe, PIPE_CMD_POLL);
if (status < 0)
return -ERESTARTSYS;
@@ -562,7 +526,7 @@ static __poll_t goldfish_pipe_poll(struct file *filp, poll_table *wait)
}
static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev,
- u32 id, u32 flags)
+ u32 id, u32 flags)
{
struct goldfish_pipe *pipe;
@@ -574,8 +538,8 @@ static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev,
return;
pipe->signalled_flags |= flags;
- if (pipe->prev_signalled || pipe->next_signalled
- || dev->first_signalled_pipe == pipe)
+ if (pipe->prev_signalled || pipe->next_signalled ||
+ dev->first_signalled_pipe == pipe)
return; /* already in the list */
pipe->next_signalled = dev->first_signalled_pipe;
if (dev->first_signalled_pipe)
@@ -584,7 +548,8 @@ static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev,
}
static void signalled_pipes_remove_locked(struct goldfish_pipe_dev *dev,
- struct goldfish_pipe *pipe) {
+ struct goldfish_pipe *pipe)
+{
if (pipe->prev_signalled)
pipe->prev_signalled->next_signalled = pipe->next_signalled;
if (pipe->next_signalled)
@@ -623,10 +588,10 @@ static struct goldfish_pipe *signalled_pipes_pop_front(
return pipe;
}
-static void goldfish_interrupt_task(unsigned long unused)
+static void goldfish_interrupt_task(unsigned long dev_addr)
{
- struct goldfish_pipe_dev *dev = pipe_dev;
/* Iterate over the signalled pipes and wake them one by one */
+ struct goldfish_pipe_dev *dev = (struct goldfish_pipe_dev *)dev_addr;
struct goldfish_pipe *pipe;
int wakes;
@@ -646,7 +611,9 @@ static void goldfish_interrupt_task(unsigned long unused)
wake_up_interruptible(&pipe->wake_queue);
}
}
-static DECLARE_TASKLET(goldfish_interrupt_tasklet, goldfish_interrupt_task, 0);
+
+static void goldfish_pipe_device_deinit(struct platform_device *pdev,
+ struct goldfish_pipe_dev *dev);
/*
* The general idea of the interrupt handling:
@@ -668,7 +635,7 @@ static irqreturn_t goldfish_pipe_interrupt(int irq, void *dev_id)
unsigned long flags;
struct goldfish_pipe_dev *dev = dev_id;
- if (dev != pipe_dev)
+ if (dev->magic != &goldfish_pipe_device_deinit)
return IRQ_NONE;
/* Request the signalled pipes from the device */
@@ -689,7 +656,7 @@ static irqreturn_t goldfish_pipe_interrupt(int irq, void *dev_id)
spin_unlock_irqrestore(&dev->lock, flags);
- tasklet_schedule(&goldfish_interrupt_tasklet);
+ tasklet_schedule(&dev->irq_tasklet);
return IRQ_HANDLED;
}
@@ -702,7 +669,10 @@ static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev)
return id;
{
- /* Reallocate the array */
+ /* Reallocate the array.
+ * Since get_free_pipe_id_locked runs with interrupts disabled,
+ * we don't want to make calls that could lead to sleep.
+ */
u32 new_capacity = 2 * dev->pipes_capacity;
struct goldfish_pipe **pipes =
kcalloc(new_capacity, sizeof(*pipes), GFP_ATOMIC);
@@ -717,6 +687,14 @@ static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev)
return id;
}
+/* A helper function to get the instance of goldfish_pipe_dev from file */
+static struct goldfish_pipe_dev *to_goldfish_pipe_dev(struct file *file)
+{
+ struct miscdevice *miscdev = file->private_data;
+
+ return container_of(miscdev, struct goldfish_pipe_dev, miscdev);
+}
+
/**
* goldfish_pipe_open - open a channel to the AVD
* @inode: inode of device
@@ -730,14 +708,15 @@ static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev)
*/
static int goldfish_pipe_open(struct inode *inode, struct file *file)
{
- struct goldfish_pipe_dev *dev = pipe_dev;
+ struct goldfish_pipe_dev *dev = to_goldfish_pipe_dev(file);
unsigned long flags;
int id;
int status;
/* Allocate new pipe kernel object */
struct goldfish_pipe *pipe = kzalloc(sizeof(*pipe), GFP_KERNEL);
- if (pipe == NULL)
+
+ if (!pipe)
return -ENOMEM;
pipe->dev = dev;
@@ -748,6 +727,7 @@ static int goldfish_pipe_open(struct inode *inode, struct file *file)
* Command buffer needs to be allocated on its own page to make sure
* it is physically contiguous in host's address space.
*/
+ BUILD_BUG_ON(sizeof(struct goldfish_pipe_command) > PAGE_SIZE);
pipe->command_buffer =
(struct goldfish_pipe_command *)__get_free_page(GFP_KERNEL);
if (!pipe->command_buffer) {
@@ -772,7 +752,7 @@ static int goldfish_pipe_open(struct inode *inode, struct file *file)
MAX_BUFFERS_PER_COMMAND;
dev->buffers->open_command_params.command_buffer_ptr =
(u64)(unsigned long)__pa(pipe->command_buffer);
- status = goldfish_cmd_locked(pipe, PIPE_CMD_OPEN);
+ status = goldfish_pipe_cmd_locked(pipe, PIPE_CMD_OPEN);
spin_unlock_irqrestore(&dev->lock, flags);
if (status < 0)
goto err_cmd;
@@ -798,7 +778,7 @@ static int goldfish_pipe_release(struct inode *inode, struct file *filp)
struct goldfish_pipe_dev *dev = pipe->dev;
/* The guest is closing the channel, so tell the emulator right now */
- (void)goldfish_cmd(pipe, PIPE_CMD_CLOSE);
+ goldfish_pipe_cmd(pipe, PIPE_CMD_CLOSE);
spin_lock_irqsave(&dev->lock, flags);
dev->pipes[pipe->id] = NULL;
@@ -820,36 +800,55 @@ static const struct file_operations goldfish_pipe_fops = {
.release = goldfish_pipe_release,
};
-static struct miscdevice goldfish_pipe_dev = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "goldfish_pipe",
- .fops = &goldfish_pipe_fops,
-};
+static void init_miscdevice(struct miscdevice *miscdev)
+{
+ memset(miscdev, 0, sizeof(*miscdev));
+
+ miscdev->minor = MISC_DYNAMIC_MINOR;
+ miscdev->name = "goldfish_pipe";
+ miscdev->fops = &goldfish_pipe_fops;
+}
+
+static void write_pa_addr(void *addr, void __iomem *portl, void __iomem *porth)
+{
+ const unsigned long paddr = __pa(addr);
+
+ writel(upper_32_bits(paddr), porth);
+ writel(lower_32_bits(paddr), portl);
+}
-static int goldfish_pipe_device_init(struct platform_device *pdev)
+static int goldfish_pipe_device_init(struct platform_device *pdev,
+ struct goldfish_pipe_dev *dev)
{
- char *page;
- struct goldfish_pipe_dev *dev = pipe_dev;
- int err = devm_request_irq(&pdev->dev, dev->irq,
- goldfish_pipe_interrupt,
- IRQF_SHARED, "goldfish_pipe", dev);
+ int err;
+
+ tasklet_init(&dev->irq_tasklet, &goldfish_interrupt_task,
+ (unsigned long)dev);
+
+ err = devm_request_irq(&pdev->dev, dev->irq,
+ goldfish_pipe_interrupt,
+ IRQF_SHARED, "goldfish_pipe", dev);
if (err) {
dev_err(&pdev->dev, "unable to allocate IRQ for v2\n");
return err;
}
- err = misc_register(&goldfish_pipe_dev);
+ init_miscdevice(&dev->miscdev);
+ err = misc_register(&dev->miscdev);
if (err) {
dev_err(&pdev->dev, "unable to register v2 device\n");
return err;
}
+ dev->pdev_dev = &pdev->dev;
dev->first_signalled_pipe = NULL;
dev->pipes_capacity = INITIAL_PIPES_CAPACITY;
dev->pipes = kcalloc(dev->pipes_capacity, sizeof(*dev->pipes),
- GFP_KERNEL);
- if (!dev->pipes)
+ GFP_KERNEL);
+ if (!dev->pipes) {
+ misc_deregister(&dev->miscdev);
return -ENOMEM;
+ }
/*
* We're going to pass two buffers, open_command_params and
@@ -857,75 +856,67 @@ static int goldfish_pipe_device_init(struct platform_device *pdev)
* needs to be contained in a single physical page. The easiest choice
* is to just allocate a page and place the buffers in it.
*/
- if (WARN_ON(sizeof(*dev->buffers) > PAGE_SIZE))
- return -ENOMEM;
-
- page = (char *)__get_free_page(GFP_KERNEL);
- if (!page) {
+ BUILD_BUG_ON(sizeof(struct goldfish_pipe_dev_buffers) > PAGE_SIZE);
+ dev->buffers = (struct goldfish_pipe_dev_buffers *)
+ __get_free_page(GFP_KERNEL);
+ if (!dev->buffers) {
kfree(dev->pipes);
+ misc_deregister(&dev->miscdev);
return -ENOMEM;
}
- dev->buffers = (struct goldfish_pipe_dev_buffers *)page;
/* Send the buffer addresses to the host */
- {
- u64 paddr = __pa(&dev->buffers->signalled_pipe_buffers);
-
- writel((u32)(unsigned long)(paddr >> 32),
- dev->base + PIPE_REG_SIGNAL_BUFFER_HIGH);
- writel((u32)(unsigned long)paddr,
- dev->base + PIPE_REG_SIGNAL_BUFFER);
- writel((u32)MAX_SIGNALLED_PIPES,
- dev->base + PIPE_REG_SIGNAL_BUFFER_COUNT);
-
- paddr = __pa(&dev->buffers->open_command_params);
- writel((u32)(unsigned long)(paddr >> 32),
- dev->base + PIPE_REG_OPEN_BUFFER_HIGH);
- writel((u32)(unsigned long)paddr,
- dev->base + PIPE_REG_OPEN_BUFFER);
- }
+ write_pa_addr(&dev->buffers->signalled_pipe_buffers,
+ dev->base + PIPE_REG_SIGNAL_BUFFER,
+ dev->base + PIPE_REG_SIGNAL_BUFFER_HIGH);
+
+ writel(MAX_SIGNALLED_PIPES,
+ dev->base + PIPE_REG_SIGNAL_BUFFER_COUNT);
+
+ write_pa_addr(&dev->buffers->open_command_params,
+ dev->base + PIPE_REG_OPEN_BUFFER,
+ dev->base + PIPE_REG_OPEN_BUFFER_HIGH);
+
+ platform_set_drvdata(pdev, dev);
return 0;
}
-static void goldfish_pipe_device_deinit(struct platform_device *pdev)
+static void goldfish_pipe_device_deinit(struct platform_device *pdev,
+ struct goldfish_pipe_dev *dev)
{
- struct goldfish_pipe_dev *dev = pipe_dev;
-
- misc_deregister(&goldfish_pipe_dev);
+ misc_deregister(&dev->miscdev);
+ tasklet_kill(&dev->irq_tasklet);
kfree(dev->pipes);
free_page((unsigned long)dev->buffers);
}
static int goldfish_pipe_probe(struct platform_device *pdev)
{
- int err;
struct resource *r;
- struct goldfish_pipe_dev *dev = pipe_dev;
+ struct goldfish_pipe_dev *dev;
- if (WARN_ON(sizeof(struct goldfish_pipe_command) > PAGE_SIZE))
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
return -ENOMEM;
- /* not thread safe, but this should not happen */
- WARN_ON(dev->base != NULL);
-
+ dev->magic = &goldfish_pipe_device_deinit;
spin_lock_init(&dev->lock);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (r == NULL || resource_size(r) < PAGE_SIZE) {
+ if (!r || resource_size(r) < PAGE_SIZE) {
dev_err(&pdev->dev, "can't allocate i/o page\n");
return -EINVAL;
}
dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
- if (dev->base == NULL) {
+ if (!dev->base) {
dev_err(&pdev->dev, "ioremap failed\n");
return -EINVAL;
}
r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (r == NULL) {
- err = -EINVAL;
- goto error;
- }
+ if (!r)
+ return -EINVAL;
+
dev->irq = r->start;
/*
@@ -935,25 +926,19 @@ static int goldfish_pipe_probe(struct platform_device *pdev)
* reading device version back: this allows the host implementation to
* detect the old driver (if there was no version write before read).
*/
- writel((u32)PIPE_DRIVER_VERSION, dev->base + PIPE_REG_VERSION);
+ writel(PIPE_DRIVER_VERSION, dev->base + PIPE_REG_VERSION);
dev->version = readl(dev->base + PIPE_REG_VERSION);
if (WARN_ON(dev->version < PIPE_CURRENT_DEVICE_VERSION))
return -EINVAL;
- err = goldfish_pipe_device_init(pdev);
- if (!err)
- return 0;
-
-error:
- dev->base = NULL;
- return err;
+ return goldfish_pipe_device_init(pdev, dev);
}
static int goldfish_pipe_remove(struct platform_device *pdev)
{
- struct goldfish_pipe_dev *dev = pipe_dev;
- goldfish_pipe_device_deinit(pdev);
- dev->base = NULL;
+ struct goldfish_pipe_dev *dev = platform_get_drvdata(pdev);
+
+ goldfish_pipe_device_deinit(pdev, dev);
return 0;
}
@@ -981,4 +966,4 @@ static struct platform_driver goldfish_pipe_driver = {
module_platform_driver(goldfish_pipe_driver);
MODULE_AUTHOR("David Turner <digit@google.com>");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");