summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/amba/bus.c88
-rw-r--r--drivers/misc/Kconfig10
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/arm-charlcd.c396
-rw-r--r--drivers/usb/gadget/at91_udc.c205
-rw-r--r--drivers/usb/gadget/at91_udc.h3
-rw-r--r--drivers/video/omap2/vram.c33
7 files changed, 640 insertions, 96 deletions
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index f60b2b6a093..d31590e7011 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -122,6 +122,31 @@ static int __init amba_init(void)
postcore_initcall(amba_init);
+static int amba_get_enable_pclk(struct amba_device *pcdev)
+{
+ struct clk *pclk = clk_get(&pcdev->dev, "apb_pclk");
+ int ret;
+
+ pcdev->pclk = pclk;
+
+ if (IS_ERR(pclk))
+ return PTR_ERR(pclk);
+
+ ret = clk_enable(pclk);
+ if (ret)
+ clk_put(pclk);
+
+ return ret;
+}
+
+static void amba_put_disable_pclk(struct amba_device *pcdev)
+{
+ struct clk *pclk = pcdev->pclk;
+
+ clk_disable(pclk);
+ clk_put(pclk);
+}
+
/*
* These are the device model conversion veneers; they convert the
* device model structures to our more specific structures.
@@ -130,17 +155,33 @@ static int amba_probe(struct device *dev)
{
struct amba_device *pcdev = to_amba_device(dev);
struct amba_driver *pcdrv = to_amba_driver(dev->driver);
- struct amba_id *id;
+ struct amba_id *id = amba_lookup(pcdrv->id_table, pcdev);
+ int ret;
- id = amba_lookup(pcdrv->id_table, pcdev);
+ do {
+ ret = amba_get_enable_pclk(pcdev);
+ if (ret)
+ break;
+
+ ret = pcdrv->probe(pcdev, id);
+ if (ret == 0)
+ break;
- return pcdrv->probe(pcdev, id);
+ amba_put_disable_pclk(pcdev);
+ } while (0);
+
+ return ret;
}
static int amba_remove(struct device *dev)
{
+ struct amba_device *pcdev = to_amba_device(dev);
struct amba_driver *drv = to_amba_driver(dev->driver);
- return drv->remove(to_amba_device(dev));
+ int ret = drv->remove(pcdev);
+
+ amba_put_disable_pclk(pcdev);
+
+ return ret;
}
static void amba_shutdown(struct device *dev)
@@ -203,7 +244,6 @@ static void amba_device_release(struct device *dev)
*/
int amba_device_register(struct amba_device *dev, struct resource *parent)
{
- u32 pid, cid;
u32 size;
void __iomem *tmp;
int i, ret;
@@ -241,25 +281,35 @@ int amba_device_register(struct amba_device *dev, struct resource *parent)
goto err_release;
}
- /*
- * Read pid and cid based on size of resource
- * they are located at end of region
- */
- for (pid = 0, i = 0; i < 4; i++)
- pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) << (i * 8);
- for (cid = 0, i = 0; i < 4; i++)
- cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << (i * 8);
+ ret = amba_get_enable_pclk(dev);
+ if (ret == 0) {
+ u32 pid, cid;
- iounmap(tmp);
+ /*
+ * Read pid and cid based on size of resource
+ * they are located at end of region
+ */
+ for (pid = 0, i = 0; i < 4; i++)
+ pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) <<
+ (i * 8);
+ for (cid = 0, i = 0; i < 4; i++)
+ cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) <<
+ (i * 8);
- if (cid == 0xb105f00d)
- dev->periphid = pid;
+ amba_put_disable_pclk(dev);
- if (!dev->periphid) {
- ret = -ENODEV;
- goto err_release;
+ if (cid == 0xb105f00d)
+ dev->periphid = pid;
+
+ if (!dev->periphid)
+ ret = -ENODEV;
}
+ iounmap(tmp);
+
+ if (ret)
+ goto err_release;
+
ret = device_add(&dev->dev);
if (ret)
goto err_release;
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 26386a92f5a..9b089dfb173 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -353,6 +353,16 @@ config VMWARE_BALLOON
To compile this driver as a module, choose M here: the
module will be called vmware_balloon.
+config ARM_CHARLCD
+ bool "ARM Ltd. Character LCD Driver"
+ depends on PLAT_VERSATILE
+ help
+ This is a driver for the character LCD found on the ARM Ltd.
+ Versatile and RealView Platform Baseboards. It doesn't do
+ very much more than display the text "ARM Linux" on the first
+ line and the Linux version on the second line, but that's
+ still useful.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 6ed06a19474..67552d6e932 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -31,3 +31,4 @@ obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/
obj-y += eeprom/
obj-y += cb710/
obj-$(CONFIG_VMWARE_BALLOON) += vmware_balloon.o
+obj-$(CONFIG_ARM_CHARLCD) += arm-charlcd.o
diff --git a/drivers/misc/arm-charlcd.c b/drivers/misc/arm-charlcd.c
new file mode 100644
index 00000000000..9e3879ef58f
--- /dev/null
+++ b/drivers/misc/arm-charlcd.c
@@ -0,0 +1,396 @@
+/*
+ * Driver for the on-board character LCD found on some ARM reference boards
+ * This is basically an Hitachi HD44780 LCD with a custom IP block to drive it
+ * http://en.wikipedia.org/wiki/HD44780_Character_LCD
+ * Currently it will just display the text "ARM Linux" and the linux version
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Linus Walleij <triad@df.lth.se>
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <generated/utsrelease.h>
+
+#define DRIVERNAME "arm-charlcd"
+#define CHARLCD_TIMEOUT (msecs_to_jiffies(1000))
+
+/* Offsets to registers */
+#define CHAR_COM 0x00U
+#define CHAR_DAT 0x04U
+#define CHAR_RD 0x08U
+#define CHAR_RAW 0x0CU
+#define CHAR_MASK 0x10U
+#define CHAR_STAT 0x14U
+
+#define CHAR_RAW_CLEAR 0x00000000U
+#define CHAR_RAW_VALID 0x00000100U
+
+/* Hitachi HD44780 display commands */
+#define HD_CLEAR 0x01U
+#define HD_HOME 0x02U
+#define HD_ENTRYMODE 0x04U
+#define HD_ENTRYMODE_INCREMENT 0x02U
+#define HD_ENTRYMODE_SHIFT 0x01U
+#define HD_DISPCTRL 0x08U
+#define HD_DISPCTRL_ON 0x04U
+#define HD_DISPCTRL_CURSOR_ON 0x02U
+#define HD_DISPCTRL_CURSOR_BLINK 0x01U
+#define HD_CRSR_SHIFT 0x10U
+#define HD_CRSR_SHIFT_DISPLAY 0x08U
+#define HD_CRSR_SHIFT_DISPLAY_RIGHT 0x04U
+#define HD_FUNCSET 0x20U
+#define HD_FUNCSET_8BIT 0x10U
+#define HD_FUNCSET_2_LINES 0x08U
+#define HD_FUNCSET_FONT_5X10 0x04U
+#define HD_SET_CGRAM 0x40U
+#define HD_SET_DDRAM 0x80U
+#define HD_BUSY_FLAG 0x80U
+
+/**
+ * @dev: a pointer back to containing device
+ * @phybase: the offset to the controller in physical memory
+ * @physize: the size of the physical page
+ * @virtbase: the offset to the controller in virtual memory
+ * @irq: reserved interrupt number
+ * @complete: completion structure for the last LCD command
+ */
+struct charlcd {
+ struct device *dev;
+ u32 phybase;
+ u32 physize;
+ void __iomem *virtbase;
+ int irq;
+ struct completion complete;
+ struct delayed_work init_work;
+};
+
+static irqreturn_t charlcd_interrupt(int irq, void *data)
+{
+ struct charlcd *lcd = data;
+ u8 status;
+
+ status = readl(lcd->virtbase + CHAR_STAT) & 0x01;
+ /* Clear IRQ */
+ writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
+ if (status)
+ complete(&lcd->complete);
+ else
+ dev_info(lcd->dev, "Spurious IRQ (%02x)\n", status);
+ return IRQ_HANDLED;
+}
+
+
+static void charlcd_wait_complete_irq(struct charlcd *lcd)
+{
+ int ret;
+
+ ret = wait_for_completion_interruptible_timeout(&lcd->complete,
+ CHARLCD_TIMEOUT);
+ /* Disable IRQ after completion */
+ writel(0x00, lcd->virtbase + CHAR_MASK);
+
+ if (ret < 0) {
+ dev_err(lcd->dev,
+ "wait_for_completion_interruptible_timeout() "
+ "returned %d waiting for ready\n", ret);
+ return;
+ }
+
+ if (ret == 0) {
+ dev_err(lcd->dev, "charlcd controller timed out "
+ "waiting for ready\n");
+ return;
+ }
+}
+
+static u8 charlcd_4bit_read_char(struct charlcd *lcd)
+{
+ u8 data;
+ u32 val;
+ int i;
+
+ /* If we can, use an IRQ to wait for the data, else poll */
+ if (lcd->irq >= 0)
+ charlcd_wait_complete_irq(lcd);
+ else {
+ i = 0;
+ val = 0;
+ while (!(val & CHAR_RAW_VALID) && i < 10) {
+ udelay(100);
+ val = readl(lcd->virtbase + CHAR_RAW);
+ i++;
+ }
+
+ writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
+ }
+ msleep(1);
+
+ /* Read the 4 high bits of the data */
+ data = readl(lcd->virtbase + CHAR_RD) & 0xf0;
+
+ /*
+ * The second read for the low bits does not trigger an IRQ
+ * so in this case we have to poll for the 4 lower bits
+ */
+ i = 0;
+ val = 0;
+ while (!(val & CHAR_RAW_VALID) && i < 10) {
+ udelay(100);
+ val = readl(lcd->virtbase + CHAR_RAW);
+ i++;
+ }
+ writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
+ msleep(1);
+
+ /* Read the 4 low bits of the data */
+ data |= (readl(lcd->virtbase + CHAR_RD) >> 4) & 0x0f;
+
+ return data;
+}
+
+static bool charlcd_4bit_read_bf(struct charlcd *lcd)
+{
+ if (lcd->irq >= 0) {
+ /*
+ * If we'll use IRQs to wait for the busyflag, clear any
+ * pending flag and enable IRQ
+ */
+ writel(CHAR_RAW_CLEAR, lcd->virtbase + CHAR_RAW);
+ init_completion(&lcd->complete);
+ writel(0x01, lcd->virtbase + CHAR_MASK);
+ }
+ readl(lcd->virtbase + CHAR_COM);
+ return charlcd_4bit_read_char(lcd) & HD_BUSY_FLAG ? true : false;
+}
+
+static void charlcd_4bit_wait_busy(struct charlcd *lcd)
+{
+ int retries = 50;
+
+ udelay(100);
+ while (charlcd_4bit_read_bf(lcd) && retries)
+ retries--;
+ if (!retries)
+ dev_err(lcd->dev, "timeout waiting for busyflag\n");
+}
+
+static void charlcd_4bit_command(struct charlcd *lcd, u8 cmd)
+{
+ u32 cmdlo = (cmd << 4) & 0xf0;
+ u32 cmdhi = (cmd & 0xf0);
+
+ writel(cmdhi, lcd->virtbase + CHAR_COM);
+ udelay(10);
+ writel(cmdlo, lcd->virtbase + CHAR_COM);
+ charlcd_4bit_wait_busy(lcd);
+}
+
+static void charlcd_4bit_char(struct charlcd *lcd, u8 ch)
+{
+ u32 chlo = (ch << 4) & 0xf0;
+ u32 chhi = (ch & 0xf0);
+
+ writel(chhi, lcd->virtbase + CHAR_DAT);
+ udelay(10);
+ writel(chlo, lcd->virtbase + CHAR_DAT);
+ charlcd_4bit_wait_busy(lcd);
+}
+
+static void charlcd_4bit_print(struct charlcd *lcd, int line, const char *str)
+{
+ u8 offset;
+ int i;
+
+ /*
+ * We support line 0, 1
+ * Line 1 runs from 0x00..0x27
+ * Line 2 runs from 0x28..0x4f
+ */
+ if (line == 0)
+ offset = 0;
+ else if (line == 1)
+ offset = 0x28;
+ else
+ return;
+
+ /* Set offset */
+ charlcd_4bit_command(lcd, HD_SET_DDRAM | offset);
+
+ /* Send string */
+ for (i = 0; i < strlen(str) && i < 0x28; i++)
+ charlcd_4bit_char(lcd, str[i]);
+}
+
+static void charlcd_4bit_init(struct charlcd *lcd)
+{
+ /* These commands cannot be checked with the busy flag */
+ writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM);
+ msleep(5);
+ writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM);
+ udelay(100);
+ writel(HD_FUNCSET | HD_FUNCSET_8BIT, lcd->virtbase + CHAR_COM);
+ udelay(100);
+ /* Go to 4bit mode */
+ writel(HD_FUNCSET, lcd->virtbase + CHAR_COM);
+ udelay(100);
+ /*
+ * 4bit mode, 2 lines, 5x8 font, after this the number of lines
+ * and the font cannot be changed until the next initialization sequence
+ */
+ charlcd_4bit_command(lcd, HD_FUNCSET | HD_FUNCSET_2_LINES);
+ charlcd_4bit_command(lcd, HD_DISPCTRL | HD_DISPCTRL_ON);
+ charlcd_4bit_command(lcd, HD_ENTRYMODE | HD_ENTRYMODE_INCREMENT);
+ charlcd_4bit_command(lcd, HD_CLEAR);
+ charlcd_4bit_command(lcd, HD_HOME);
+ /* Put something useful in the display */
+ charlcd_4bit_print(lcd, 0, "ARM Linux");
+ charlcd_4bit_print(lcd, 1, UTS_RELEASE);
+}
+
+static void charlcd_init_work(struct work_struct *work)
+{
+ struct charlcd *lcd =
+ container_of(work, struct charlcd, init_work.work);
+
+ charlcd_4bit_init(lcd);
+}
+
+static int __init charlcd_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct charlcd *lcd;
+ struct resource *res;
+
+ lcd = kzalloc(sizeof(struct charlcd), GFP_KERNEL);
+ if (!lcd)
+ return -ENOMEM;
+
+ lcd->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENOENT;
+ goto out_no_resource;
+ }
+ lcd->phybase = res->start;
+ lcd->physize = resource_size(res);
+
+ if (request_mem_region(lcd->phybase, lcd->physize,
+ DRIVERNAME) == NULL) {
+ ret = -EBUSY;
+ goto out_no_memregion;
+ }
+
+ lcd->virtbase = ioremap(lcd->phybase, lcd->physize);
+ if (!lcd->virtbase) {
+ ret = -ENOMEM;
+ goto out_no_remap;
+ }
+
+ lcd->irq = platform_get_irq(pdev, 0);
+ /* If no IRQ is supplied, we'll survive without it */
+ if (lcd->irq >= 0) {
+ if (request_irq(lcd->irq, charlcd_interrupt, IRQF_DISABLED,
+ DRIVERNAME, lcd)) {
+ ret = -EIO;
+ goto out_no_irq;
+ }
+ }
+
+ platform_set_drvdata(pdev, lcd);
+
+ /*
+ * Initialize the display in a delayed work, because
+ * it is VERY slow and would slow down the boot of the system.
+ */
+ INIT_DELAYED_WORK(&lcd->init_work, charlcd_init_work);
+ schedule_delayed_work(&lcd->init_work, 0);
+
+ dev_info(&pdev->dev, "initalized ARM character LCD at %08x\n",
+ lcd->phybase);
+
+ return 0;
+
+out_no_irq:
+ iounmap(lcd->virtbase);
+out_no_remap:
+ platform_set_drvdata(pdev, NULL);
+out_no_memregion:
+ release_mem_region(lcd->phybase, SZ_4K);
+out_no_resource:
+ kfree(lcd);
+ return ret;
+}
+
+static int __exit charlcd_remove(struct platform_device *pdev)
+{
+ struct charlcd *lcd = platform_get_drvdata(pdev);
+
+ if (lcd) {
+ free_irq(lcd->irq, lcd);
+ iounmap(lcd->virtbase);
+ release_mem_region(lcd->phybase, lcd->physize);
+ platform_set_drvdata(pdev, NULL);
+ kfree(lcd);
+ }
+
+ return 0;
+}
+
+static int charlcd_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct charlcd *lcd = platform_get_drvdata(pdev);
+
+ /* Power the display off */
+ charlcd_4bit_command(lcd, HD_DISPCTRL);
+ return 0;
+}
+
+static int charlcd_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct charlcd *lcd = platform_get_drvdata(pdev);
+
+ /* Turn the display back on */
+ charlcd_4bit_command(lcd, HD_DISPCTRL | HD_DISPCTRL_ON);
+ return 0;
+}
+
+static const struct dev_pm_ops charlcd_pm_ops = {
+ .suspend = charlcd_suspend,
+ .resume = charlcd_resume,
+};
+
+static struct platform_driver charlcd_driver = {
+ .driver = {
+ .name = DRIVERNAME,
+ .owner = THIS_MODULE,
+ .pm = &charlcd_pm_ops,
+ },
+ .remove = __exit_p(charlcd_remove),
+};
+
+static int __init charlcd_init(void)
+{
+ return platform_driver_probe(&charlcd_driver, charlcd_probe);
+}
+
+static void __exit charlcd_exit(void)
+{
+ platform_driver_unregister(&charlcd_driver);
+}
+
+module_init(charlcd_init);
+module_exit(charlcd_exit);
+
+MODULE_AUTHOR("Linus Walleij <triad@df.lth.se>");
+MODULE_DESCRIPTION("ARM Character LCD Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index eaa79c8a9b8..93ead19507b 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -76,11 +76,12 @@
static const char driver_name [] = "at91_udc";
static const char ep0name[] = "ep0";
+#define VBUS_POLL_TIMEOUT msecs_to_jiffies(1000)
-#define at91_udp_read(dev, reg) \
- __raw_readl((dev)->udp_baseaddr + (reg))
-#define at91_udp_write(dev, reg, val) \
- __raw_writel((val), (dev)->udp_baseaddr + (reg))
+#define at91_udp_read(udc, reg) \
+ __raw_readl((udc)->udp_baseaddr + (reg))
+#define at91_udp_write(udc, reg, val) \
+ __raw_writel((val), (udc)->udp_baseaddr + (reg))
/*-------------------------------------------------------------------------*/
@@ -102,8 +103,9 @@ static void proc_ep_show(struct seq_file *s, struct at91_ep *ep)
u32 csr;
struct at91_request *req;
unsigned long flags;
+ struct at91_udc *udc = ep->udc;
- local_irq_save(flags);
+ spin_lock_irqsave(&udc->lock, flags);
csr = __raw_readl(ep->creg);
@@ -147,7 +149,7 @@ static void proc_ep_show(struct seq_file *s, struct at91_ep *ep)
&req->req, length,
req->req.length, req->req.buf);
}
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&udc->lock, flags);
}
static void proc_irq_show(struct seq_file *s, const char *label, u32 mask)
@@ -272,7 +274,9 @@ static void done(struct at91_ep *ep, struct at91_request *req, int status)
VDBG("%s done %p, status %d\n", ep->ep.name, req, status);
ep->stopped = 1;
+ spin_unlock(&udc->lock);
req->req.complete(&ep->ep, &req->req);
+ spin_lock(&udc->lock);
ep->stopped = stopped;
/* ep0 is always ready; other endpoints need a non-empty queue */
@@ -472,7 +476,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
const struct usb_endpoint_descriptor *desc)
{
struct at91_ep *ep = container_of(_ep, struct at91_ep, ep);
- struct at91_udc *dev = ep->udc;
+ struct at91_udc *udc = ep->udc;
u16 maxpacket;
u32 tmp;
unsigned long flags;
@@ -487,7 +491,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
return -EINVAL;
}
- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
DBG("bogus device state\n");
return -ESHUTDOWN;
}
@@ -521,7 +525,7 @@ bogus_max:
}
ok:
- local_irq_save(flags);
+ spin_lock_irqsave(&udc->lock, flags);
/* initialize endpoint to match this descriptor */
ep->is_in = usb_endpoint_dir_in(desc);
@@ -540,10 +544,10 @@ ok:
* reset/init endpoint fifo. NOTE: leaves fifo_bank alone,
* since endpoint resets don't reset hw pingpong state.
*/
- at91_udp_write(dev, AT91_UDP_RST_EP, ep->int_mask);
- at91_udp_write(dev, AT91_UDP_RST_EP, 0);
+ at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask);
+ at91_udp_write(udc, AT91_UDP_RST_EP, 0);
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
@@ -556,7 +560,7 @@ static int at91_ep_disable (struct usb_ep * _ep)
if (ep == &ep->udc->ep[0])
return -EINVAL;
- local_irq_save(flags);
+ spin_lock_irqsave(&udc->lock, flags);
nuke(ep, -ESHUTDOWN);
@@ -571,7 +575,7 @@ static int at91_ep_disable (struct usb_ep * _ep)
__raw_writel(0, ep->creg);
}
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
@@ -607,7 +611,7 @@ static int at91_ep_queue(struct usb_ep *_ep,
{
struct at91_request *req;
struct at91_ep *ep;
- struct at91_udc *dev;
+ struct at91_udc *udc;
int status;
unsigned long flags;
@@ -625,9 +629,9 @@ static int at91_ep_queue(struct usb_ep *_ep,
return -EINVAL;
}
- dev = ep->udc;
+ udc = ep->udc;
- if (!dev || !dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ if (!udc || !udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
DBG("invalid device\n");
return -EINVAL;
}
@@ -635,7 +639,7 @@ static int at91_ep_queue(struct usb_ep *_ep,
_req->status = -EINPROGRESS;
_req->actual = 0;
- local_irq_save(flags);
+ spin_lock_irqsave(&udc->lock, flags);
/* try to kickstart any empty and idle queue */
if (list_empty(&ep->queue) && !ep->stopped) {
@@ -653,7 +657,7 @@ static int at91_ep_queue(struct usb_ep *_ep,
if (is_ep0) {
u32 tmp;
- if (!dev->req_pending) {
+ if (!udc->req_pending) {
status = -EINVAL;
goto done;
}
@@ -662,11 +666,11 @@ static int at91_ep_queue(struct usb_ep *_ep,
* defer changing CONFG until after the gadget driver
* reconfigures the endpoints.
*/
- if (dev->wait_for_config_ack) {
- tmp = at91_udp_read(dev, AT91_UDP_GLB_STAT);
+ if (udc->wait_for_config_ack) {
+ tmp = at91_udp_read(udc, AT91_UDP_GLB_STAT);
tmp ^= AT91_UDP_CONFG;
VDBG("toggle config\n");
- at91_udp_write(dev, AT91_UDP_GLB_STAT, tmp);
+ at91_udp_write(udc, AT91_UDP_GLB_STAT, tmp);
}
if (req->req.length == 0) {
ep0_in_status:
@@ -676,7 +680,7 @@ ep0_in_status:
tmp &= ~SET_FX;
tmp |= CLR_FX | AT91_UDP_TXPKTRDY;
__raw_writel(tmp, ep->creg);
- dev->req_pending = 0;
+ udc->req_pending = 0;
goto done;
}
}
@@ -695,31 +699,40 @@ ep0_in_status:
if (req && !status) {
list_add_tail (&req->queue, &ep->queue);
- at91_udp_write(dev, AT91_UDP_IER, ep->int_mask);
+ at91_udp_write(udc, AT91_UDP_IER, ep->int_mask);
}
done:
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&udc->lock, flags);
return (status < 0) ? status : 0;
}
static int at91_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
{
- struct at91_ep *ep;
+ struct at91_ep *ep;
struct at91_request *req;
+ unsigned long flags;
+ struct at91_udc *udc;
ep = container_of(_ep, struct at91_ep, ep);
if (!_ep || ep->ep.name == ep0name)
return -EINVAL;
+ udc = ep->udc;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
/* make sure it's actually queued on this endpoint */
list_for_each_entry (req, &ep->queue, queue) {
if (&req->req == _req)
break;
}
- if (&req->req != _req)
+ if (&req->req != _req) {
+ spin_unlock_irqrestore(&udc->lock, flags);
return -EINVAL;
+ }
done(ep, req, -ECONNRESET);
+ spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
@@ -736,7 +749,7 @@ static int at91_ep_set_halt(struct usb_ep *_ep, int value)
return -EINVAL;
creg = ep->creg;
- local_irq_save(flags);
+ spin_lock_irqsave(&udc->lock, flags);
csr = __raw_readl(creg);
@@ -761,7 +774,7 @@ static int at91_ep_set_halt(struct usb_ep *_ep, int value)
__raw_writel(csr, creg);
}
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&udc->lock, flags);
return status;
}
@@ -795,7 +808,7 @@ static int at91_wakeup(struct usb_gadget *gadget)
unsigned long flags;
DBG("%s\n", __func__ );
- local_irq_save(flags);
+ spin_lock_irqsave(&udc->lock, flags);
if (!udc->clocked || !udc->suspended)
goto done;
@@ -809,7 +822,7 @@ static int at91_wakeup(struct usb_gadget *gadget)
at91_udp_write(udc, AT91_UDP_GLB_STAT, glbstate);
done:
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&udc->lock, flags);
return status;
}
@@ -851,8 +864,11 @@ static void stop_activity(struct at91_udc *udc)
ep->stopped = 1;
nuke(ep, -ESHUTDOWN);
}
- if (driver)
+ if (driver) {
+ spin_unlock(&udc->lock);
driver->disconnect(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
udc_reinit(udc);
}
@@ -935,13 +951,13 @@ static int at91_vbus_session(struct usb_gadget *gadget, int is_active)
unsigned long flags;
// VDBG("vbus %s\n", is_active ? "on" : "off");
- local_irq_save(flags);
+ spin_lock_irqsave(&udc->lock, flags);
udc->vbus = (is_active != 0);
if (udc->driver)
pullup(udc, is_active);
else
pullup(udc, 0);
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
@@ -950,10 +966,10 @@ static int at91_pullup(struct usb_gadget *gadget, int is_on)
struct at91_udc *udc = to_udc(gadget);
unsigned long flags;
- local_irq_save(flags);
+ spin_lock_irqsave(&udc->lock, flags);
udc->enabled = is_on = !!is_on;
pullup(udc, is_on);
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
@@ -962,9 +978,9 @@ static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on)
struct at91_udc *udc = to_udc(gadget);
unsigned long flags;
- local_irq_save(flags);
+ spin_lock_irqsave(&udc->lock, flags);
udc->selfpowered = (is_on != 0);
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
@@ -1226,8 +1242,11 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr)
#undef w_length
/* pass request up to the gadget driver */
- if (udc->driver)
+ if (udc->driver) {
+ spin_unlock(&udc->lock);
status = udc->driver->setup(&udc->gadget, &pkt.r);
+ spin_lock(&udc->lock);
+ }
else
status = -ENODEV;
if (status < 0) {
@@ -1378,6 +1397,9 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc)
struct at91_udc *udc = _udc;
u32 rescans = 5;
int disable_clock = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
if (!udc->clocked) {
clk_on(udc);
@@ -1433,8 +1455,11 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc)
* and then into standby to avoid drawing more than
* 500uA power (2500uA for some high-power configs).
*/
- if (udc->driver && udc->driver->suspend)
+ if (udc->driver && udc->driver->suspend) {
+ spin_unlock(&udc->lock);
udc->driver->suspend(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
/* host initiated resume */
} else if (status & AT91_UDP_RXRSM) {
@@ -1451,8 +1476,11 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc)
* would normally want to switch out of slow clock
* mode into normal mode.
*/
- if (udc->driver && udc->driver->resume)
+ if (udc->driver && udc->driver->resume) {
+ spin_unlock(&udc->lock);
udc->driver->resume(&udc->gadget);
+ spin_lock(&udc->lock);
+ }
/* endpoint IRQs are cleared by handling them */
} else {
@@ -1474,6 +1502,8 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc)
if (disable_clock)
clk_off(udc);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
return IRQ_HANDLED;
}
@@ -1556,24 +1586,53 @@ static struct at91_udc controller = {
/* ep6 and ep7 are also reserved (custom silicon might use them) */
};
+static void at91_vbus_update(struct at91_udc *udc, unsigned value)
+{
+ value ^= udc->board.vbus_active_low;
+ if (value != udc->vbus)
+ at91_vbus_session(&udc->gadget, value);
+}
+
static irqreturn_t at91_vbus_irq(int irq, void *_udc)
{
struct at91_udc *udc = _udc;
- unsigned value;
/* vbus needs at least brief debouncing */
udelay(10);
- value = gpio_get_value(udc->board.vbus_pin);
- if (value != udc->vbus)
- at91_vbus_session(&udc->gadget, value);
+ at91_vbus_update(udc, gpio_get_value(udc->board.vbus_pin));
return IRQ_HANDLED;
}
+static void at91_vbus_timer_work(struct work_struct *work)
+{
+ struct at91_udc *udc = container_of(work, struct at91_udc,
+ vbus_timer_work);
+
+ at91_vbus_update(udc, gpio_get_value_cansleep(udc->board.vbus_pin));
+
+ if (!timer_pending(&udc->vbus_timer))
+ mod_timer(&udc->vbus_timer, jiffies + VBUS_POLL_TIMEOUT);
+}
+
+static void at91_vbus_timer(unsigned long data)
+{
+ struct at91_udc *udc = (struct at91_udc *)data;
+
+ /*
+ * If we are polling vbus it is likely that the gpio is on an
+ * bus such as i2c or spi which may sleep, so schedule some work
+ * to read the vbus gpio
+ */
+ if (!work_pending(&udc->vbus_timer_work))
+ schedule_work(&udc->vbus_timer_work);
+}
+
int usb_gadget_register_driver (struct usb_gadget_driver *driver)
{
struct at91_udc *udc = &controller;
int retval;
+ unsigned long flags;
if (!driver
|| driver->speed < USB_SPEED_FULL
@@ -1605,9 +1664,9 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver)
return retval;
}
- local_irq_disable();
+ spin_lock_irqsave(&udc->lock, flags);
pullup(udc, 1);
- local_irq_enable();
+ spin_unlock_irqrestore(&udc->lock, flags);
DBG("bound to %s\n", driver->driver.name);
return 0;
@@ -1617,15 +1676,16 @@ EXPORT_SYMBOL (usb_gadget_register_driver);
int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
{
struct at91_udc *udc = &controller;
+ unsigned long flags;
if (!driver || driver != udc->driver || !driver->unbind)
return -EINVAL;
- local_irq_disable();
+ spin_lock_irqsave(&udc->lock, flags);
udc->enabled = 0;
at91_udp_write(udc, AT91_UDP_IDR, ~0);
pullup(udc, 0);
- local_irq_enable();
+ spin_unlock_irqrestore(&udc->lock, flags);
driver->unbind(&udc->gadget);
udc->gadget.dev.driver = NULL;
@@ -1641,8 +1701,13 @@ EXPORT_SYMBOL (usb_gadget_unregister_driver);
static void at91udc_shutdown(struct platform_device *dev)
{
+ struct at91_udc *udc = platform_get_drvdata(dev);
+ unsigned long flags;
+
/* force disconnect on reboot */
+ spin_lock_irqsave(&udc->lock, flags);
pullup(platform_get_drvdata(dev), 0);
+ spin_unlock_irqrestore(&udc->lock, flags);
}
static int __init at91udc_probe(struct platform_device *pdev)
@@ -1683,6 +1748,7 @@ static int __init at91udc_probe(struct platform_device *pdev)
udc->board = *(struct at91_udc_data *) dev->platform_data;
udc->pdev = pdev;
udc->enabled = 0;
+ spin_lock_init(&udc->lock);
/* rm9200 needs manual D+ pullup; off by default */
if (cpu_is_at91rm9200()) {
@@ -1763,13 +1829,23 @@ static int __init at91udc_probe(struct platform_device *pdev)
* Get the initial state of VBUS - we cannot expect
* a pending interrupt.
*/
- udc->vbus = gpio_get_value(udc->board.vbus_pin);
- if (request_irq(udc->board.vbus_pin, at91_vbus_irq,
- IRQF_DISABLED, driver_name, udc)) {
- DBG("request vbus irq %d failed\n",
- udc->board.vbus_pin);
- retval = -EBUSY;
- goto fail3;
+ udc->vbus = gpio_get_value_cansleep(udc->board.vbus_pin) ^
+ udc->board.vbus_active_low;
+
+ if (udc->board.vbus_polled) {
+ INIT_WORK(&udc->vbus_timer_work, at91_vbus_timer_work);
+ setup_timer(&udc->vbus_timer, at91_vbus_timer,
+ (unsigned long)udc);
+ mod_timer(&udc->vbus_timer,
+ jiffies + VBUS_POLL_TIMEOUT);
+ } else {
+ if (request_irq(udc->board.vbus_pin, at91_vbus_irq,
+ IRQF_DISABLED, driver_name, udc)) {
+ DBG("request vbus irq %d failed\n",
+ udc->board.vbus_pin);
+ retval = -EBUSY;
+ goto fail3;
+ }
}
} else {
DBG("no VBUS detection, assuming always-on\n");
@@ -1804,13 +1880,16 @@ static int __exit at91udc_remove(struct platform_device *pdev)
{
struct at91_udc *udc = platform_get_drvdata(pdev);
struct resource *res;
+ unsigned long flags;
DBG("remove\n");
if (udc->driver)
return -EBUSY;
+ spin_lock_irqsave(&udc->lock, flags);
pullup(udc, 0);
+ spin_unlock_irqrestore(&udc->lock, flags);
device_init_wakeup(&pdev->dev, 0);
remove_debug_file(udc);
@@ -1840,6 +1919,7 @@ static int at91udc_suspend(struct platform_device *pdev, pm_message_t mesg)
{
struct at91_udc *udc = platform_get_drvdata(pdev);
int wake = udc->driver && device_may_wakeup(&pdev->dev);
+ unsigned long flags;
/* Unless we can act normally to the host (letting it wake us up
* whenever it has work for us) force disconnect. Wakeup requires
@@ -1849,13 +1929,15 @@ static int at91udc_suspend(struct platform_device *pdev, pm_message_t mesg)
if ((!udc->suspended && udc->addr)
|| !wake
|| at91_suspend_entering_slow_clock()) {
+ spin_lock_irqsave(&udc->lock, flags);
pullup(udc, 0);
wake = 0;
+ spin_unlock_irqrestore(&udc->lock, flags);
} else
enable_irq_wake(udc->udp_irq);
udc->active_suspend = wake;
- if (udc->board.vbus_pin > 0 && wake)
+ if (udc->board.vbus_pin > 0 && !udc->board.vbus_polled && wake)
enable_irq_wake(udc->board.vbus_pin);
return 0;
}
@@ -1863,15 +1945,20 @@ static int at91udc_suspend(struct platform_device *pdev, pm_message_t mesg)
static int at91udc_resume(struct platform_device *pdev)
{
struct at91_udc *udc = platform_get_drvdata(pdev);
+ unsigned long flags;
- if (udc->board.vbus_pin > 0 && udc->active_suspend)
+ if (udc->board.vbus_pin > 0 && !udc->board.vbus_polled &&
+ udc->active_suspend)
disable_irq_wake(udc->board.vbus_pin);
/* maybe reconnect to host; if so, clocks on */
if (udc->active_suspend)
disable_irq_wake(udc->udp_irq);
- else
+ else {
+ spin_lock_irqsave(&udc->lock, flags);
pullup(udc, 1);
+ spin_unlock_irqrestore(&udc->lock, flags);
+ }
return 0;
}
#else
diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h
index c65d6229589..108ca54f909 100644
--- a/drivers/usb/gadget/at91_udc.h
+++ b/drivers/usb/gadget/at91_udc.h
@@ -144,6 +144,9 @@ struct at91_udc {
struct proc_dir_entry *pde;
void __iomem *udp_baseaddr;
int udp_irq;
+ spinlock_t lock;
+ struct timer_list vbus_timer;
+ struct work_struct vbus_timer_work;
};
static inline struct at91_udc *to_udc(struct usb_gadget *g)
diff --git a/drivers/video/omap2/vram.c b/drivers/video/omap2/vram.c
index 3b1237ad85e..f6fdc2085f3 100644
--- a/drivers/video/omap2/vram.c
+++ b/drivers/video/omap2/vram.c
@@ -25,7 +25,7 @@
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
-#include <linux/bootmem.h>
+#include <linux/memblock.h>
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/jiffies.h>
@@ -525,10 +525,8 @@ early_param("vram", omap_vram_early_vram);
* Called from map_io. We need to call to this early enough so that we
* can reserve the fixed SDRAM regions before VM could get hold of them.
*/
-void __init omap_vram_reserve_sdram(void)
+void __init omap_vram_reserve_sdram_memblock(void)
{
- struct bootmem_data *bdata;
- unsigned long sdram_start, sdram_size;
u32 paddr;
u32 size = 0;
@@ -555,29 +553,28 @@ void __init omap_vram_reserve_sdram(void)
size = PAGE_ALIGN(size);
- bdata = NODE_DATA(0)->bdata;
- sdram_start = bdata->node_min_pfn << PAGE_SHIFT;
- sdram_size = (bdata->node_low_pfn << PAGE_SHIFT) - sdram_start;
-
if (paddr) {
- if ((paddr & ~PAGE_MASK) || paddr < sdram_start ||
- paddr + size > sdram_start + sdram_size) {
+ struct memblock_property res;
+
+ res.base = paddr;
+ res.size = size;
+ if ((paddr & ~PAGE_MASK) || memblock_find(&res) ||
+ res.base != paddr || res.size != size) {
pr_err("Illegal SDRAM region for VRAM\n");
return;
}
- if (reserve_bootmem(paddr, size, BOOTMEM_EXCLUSIVE) < 0) {
- pr_err("FB: failed to reserve VRAM\n");
+ if (memblock_is_region_reserved(paddr, size)) {
+ pr_err("FB: failed to reserve VRAM - busy\n");
return;
}
- } else {
- if (size > sdram_size) {
- pr_err("Illegal SDRAM size for VRAM\n");
+
+ if (memblock_reserve(paddr, size) < 0) {
+ pr_err("FB: failed to reserve VRAM - no memory\n");
return;
}
-
- paddr = virt_to_phys(alloc_bootmem_pages(size));
- BUG_ON(paddr & ~PAGE_MASK);
+ } else {
+ paddr = memblock_alloc_base(size, PAGE_SIZE, MEMBLOCK_REAL_LIMIT);
}
omap_vram_add_region(paddr, size);