diff options
author | Colin Cross <ccross@android.com> | 2011-02-12 14:13:29 -0800 |
---|---|---|
committer | Colin Cross <ccross@android.com> | 2011-02-12 14:13:29 -0800 |
commit | 5a7997f3fb084ebf162f9481cd27bb2b1af390a5 (patch) | |
tree | 90cfb981361dd7483384ec2a255ac347ec2ae182 | |
parent | 87666eb18a15ffecfbd6108a9e2cb0d54e33ec99 (diff) | |
parent | 6b53bad8ac54b3d748c4b0dbe6b0a4ed6e2e60f4 (diff) |
Merge branch 'linux-tegra-2.6.36' into android-tegra-2.6.36
-rw-r--r-- | arch/arm/mach-tegra/cpuidle.c | 5 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 21 | ||||
-rw-r--r-- | drivers/usb/host/ehci-tegra.c | 90 | ||||
-rw-r--r-- | include/linux/mmc/mmc.h | 1 | ||||
-rw-r--r-- | include/linux/usb.h | 1 | ||||
-rw-r--r-- | include/linux/usb/hcd.h | 16 |
6 files changed, 129 insertions, 5 deletions
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c index a063c34ecf60..23cb9acc588c 100644 --- a/arch/arm/mach-tegra/cpuidle.c +++ b/arch/arm/mach-tegra/cpuidle.c @@ -445,6 +445,7 @@ static int tegra_idle_enter_lp2(struct cpuidle_device *dev, return tegra_idle_enter_lp3(dev, state); local_irq_disable(); + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); local_fiq_disable(); enter = ktime_get(); @@ -463,11 +464,9 @@ static int tegra_idle_enter_lp2(struct cpuidle_device *dev, us = ktime_to_us(exit); local_fiq_enable(); + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); local_irq_enable(); - /* cpu clockevents may have been reset by powerdown */ - hrtimer_peek_ahead_timers(); - smp_rmb(); state->exit_latency = tegra_lp2_exit_latency; state->target_residency = tegra_lp2_exit_latency + diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 5cca00a6d09d..f24f40eba180 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1265,6 +1265,14 @@ static void hcd_free_coherent(struct usb_bus *bus, dma_addr_t *dma_handle, static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) { + if (hcd->driver->unmap_urb_for_dma) + hcd->driver->unmap_urb_for_dma(hcd, urb); + else + usb_hcd_unmap_urb_for_dma(hcd, urb); +} + +void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +{ enum dma_data_direction dir; if (urb->transfer_flags & URB_SETUP_MAP_SINGLE) @@ -1307,10 +1315,20 @@ static void unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) URB_DMA_MAP_SG | URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE | URB_MAP_LOCAL); } +EXPORT_SYMBOL_GPL(usb_hcd_unmap_urb_for_dma); static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { + if (hcd->driver->map_urb_for_dma) + return hcd->driver->map_urb_for_dma(hcd, urb, mem_flags); + else + return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); +} + +int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ enum dma_data_direction dir; int ret = 0; @@ -1400,10 +1418,11 @@ static int map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, } if (ret && (urb->transfer_flags & (URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL))) - unmap_urb_for_dma(hcd, urb); + usb_hcd_unmap_urb_for_dma(hcd, urb); } return ret; } +EXPORT_SYMBOL_GPL(usb_hcd_map_urb_for_dma); /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 3c72ed4c6cdc..a4f297544a1c 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -32,6 +32,8 @@ #define TEGRA_USB_USBMODE_HOST (3 << 0) #define TEGRA_USB_PORTSC1_PTC(x) (((x) & 0xf) << 16) +#define TEGRA_USB_DMA_ALIGN 32 + struct tegra_ehci_context { bool valid; u32 command; @@ -468,6 +470,92 @@ static int tegra_ehci_bus_resume(struct usb_hcd *hcd) } #endif +struct temp_buffer { + void *kmalloc_ptr; + void *old_xfer_buffer; + u8 data[0]; +}; + +static void free_temp_buffer(struct urb *urb) +{ + enum dma_data_direction dir; + struct temp_buffer *temp; + + if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) + return; + + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + temp = container_of(urb->transfer_buffer, struct temp_buffer, + data); + + if (dir == DMA_FROM_DEVICE) + memcpy(temp->old_xfer_buffer, temp->data, + urb->transfer_buffer_length); + urb->transfer_buffer = temp->old_xfer_buffer; + kfree(temp->kmalloc_ptr); + + urb->transfer_flags &= ~URB_ALIGNED_TEMP_BUFFER; +} + +static int alloc_temp_buffer(struct urb *urb, gfp_t mem_flags) +{ + enum dma_data_direction dir; + struct temp_buffer *temp, *kmalloc_ptr; + size_t kmalloc_size; + + if (urb->num_sgs || urb->sg || + urb->transfer_buffer_length == 0 || + !((uintptr_t)urb->transfer_buffer & (TEGRA_USB_DMA_ALIGN - 1))) + return 0; + + dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + + /* Allocate a buffer with enough padding for alignment */ + kmalloc_size = urb->transfer_buffer_length + + sizeof(struct temp_buffer) + TEGRA_USB_DMA_ALIGN - 1; + + kmalloc_ptr = kmalloc(kmalloc_size, mem_flags); + if (!kmalloc_ptr) + return -ENOMEM; + + /* Position our struct temp_buffer such that data is aligned */ + temp = PTR_ALIGN(kmalloc_ptr + 1, TEGRA_USB_DMA_ALIGN) - 1; + + temp->kmalloc_ptr = kmalloc_ptr; + temp->old_xfer_buffer = urb->transfer_buffer; + if (dir == DMA_TO_DEVICE) + memcpy(temp->data, urb->transfer_buffer, + urb->transfer_buffer_length); + urb->transfer_buffer = temp->data; + + urb->transfer_flags |= URB_ALIGNED_TEMP_BUFFER; + + return 0; +} + +static int tegra_ehci_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + int ret; + + ret = alloc_temp_buffer(urb, mem_flags); + if (ret) + return ret; + + ret = usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); + if (ret) + free_temp_buffer(urb); + + return ret; +} + +static void tegra_ehci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) +{ + usb_hcd_unmap_urb_for_dma(hcd, urb); + free_temp_buffer(urb); +} + static const struct hc_driver tegra_ehci_hc_driver = { .description = hcd_name, .product_desc = "Tegra EHCI Host Controller", @@ -483,6 +571,8 @@ static const struct hc_driver tegra_ehci_hc_driver = { .shutdown = tegra_ehci_shutdown, .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, + .map_urb_for_dma = tegra_ehci_map_urb_for_dma, + .unmap_urb_for_dma = tegra_ehci_unmap_urb_for_dma, .endpoint_disable = ehci_endpoint_disable, .endpoint_reset = ehci_endpoint_reset, .get_frame_number = ehci_get_frame, diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 5b35987e691b..dd11ae51fb68 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -262,7 +262,6 @@ struct _mmc_csd { #define EXT_CSD_S_A_TIMEOUT 217 /* RO */ #define EXT_CSD_ERASE_TIMEOUT_MULT 223 /* RO */ #define EXT_CSD_HC_ERASE_GRP_SIZE 224 /* RO */ -#define EXT_CSD_BOOT_SIZE_MULTI 226 #define EXT_CSD_SEC_TRIM_MULT 229 /* RO */ #define EXT_CSD_SEC_ERASE_MULT 230 /* RO */ #define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 35fe6ab222bb..6d4566ee34c9 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -975,6 +975,7 @@ extern int usb_disabled(void); #define URB_SETUP_MAP_SINGLE 0x00100000 /* Setup packet DMA mapped */ #define URB_SETUP_MAP_LOCAL 0x00200000 /* HCD-local setup packet */ #define URB_DMA_SG_COMBINED 0x00400000 /* S-G entries were combined */ +#define URB_ALIGNED_TEMP_BUFFER 0x00800000 /* Temp buffer was alloc'd */ struct usb_iso_packet_descriptor { unsigned int offset; diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 3b571f1ffbb3..6e1b7507e103 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -233,6 +233,19 @@ struct hc_driver { int (*urb_dequeue)(struct usb_hcd *hcd, struct urb *urb, int status); + /* + * (optional) these hooks allow an HCD to override the default DMA + * mapping and unmapping routines. In general, they shouldn't be + * necessary unless the host controller has special DMA requirements, + * such as alignment contraints. If these are not specified, the + * general usb_hcd_(un)?map_urb_for_dma functions will be used instead + * (and it may be a good idea to call these functions in your HCD + * implementation) + */ + int (*map_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags); + void (*unmap_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb); + /* hw synch, freeing endpoint resources that urb_dequeue can't */ void (*endpoint_disable)(struct usb_hcd *hcd, struct usb_host_endpoint *ep); @@ -329,6 +342,9 @@ extern int usb_hcd_submit_urb(struct urb *urb, gfp_t mem_flags); extern int usb_hcd_unlink_urb(struct urb *urb, int status); extern void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status); +extern int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags); +extern void usb_hcd_unmap_urb_for_dma(struct usb_hcd *, struct urb *); extern void usb_hcd_flush_endpoint(struct usb_device *udev, struct usb_host_endpoint *ep); extern void usb_hcd_disable_endpoint(struct usb_device *udev, |