From c7827e45a3659869ec7fd6f74b0717a751474f17 Mon Sep 17 00:00:00 2001 From: Ofir Cohen Date: Tue, 13 Dec 2011 20:10:01 +0200 Subject: [PATCH] usb: gadget: SPS BAM-to-BAM - USB BAM driver USB BAM driver to support BAM-to-BAM USB<->Peripheral transactions. Change-Id: Ib49a41f5dcdccb6f6bff2492fa64ead40f18b870 Signed-off-by: Ofir Cohen --- arch/arm/mach-msm/include/mach/usb_bam.h | 40 ++++ drivers/platform/msm/Kconfig | 8 + drivers/platform/msm/Makefile | 1 + drivers/platform/msm/usb_bam.c | 238 +++++++++++++++++++++++ include/linux/usb/msm_hsusb.h | 30 +++ 5 files changed, 317 insertions(+) create mode 100644 arch/arm/mach-msm/include/mach/usb_bam.h create mode 100644 drivers/platform/msm/usb_bam.c diff --git a/arch/arm/mach-msm/include/mach/usb_bam.h b/arch/arm/mach-msm/include/mach/usb_bam.h new file mode 100644 index 00000000000..4caa71bb3de --- /dev/null +++ b/arch/arm/mach-msm/include/mach/usb_bam.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _USB_BAM_H_ +#define _USB_BAM_H_ + +/** + * Connect USB-to-Periperal SPS connection. + * + * This function returns the allocated pipes number. + * + * @idx - Connection index. + * + * @src_pipe_idx - allocated pipe index - USB as a + * source (output) + * + * @dst_pipe_idx - allocated pipe index - USB as a + * destination (output) + * + * @return 0 on success, negative value on error + * + */ +#ifdef CONFIG_USB_BAM +int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx); +#else +int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx) +{ + return -ENODEV; +} +#endif +#endif /* _USB_BAM_H_ */ diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig index 26441cd45d4..23efb008c33 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -23,6 +23,14 @@ config SPS 2. Peripheral-to-Memory. 3. Memory-to-Memory. +config USB_BAM + boolean "USB BAM Driver" + depends on SPS && USB_GADGET + help + Enabling this option adds USB BAM Driver. + USB BAM driver was added to supports SPS Peripheral-to-Peripheral + transfers between the USB and other peripheral. + config SPS_SUPPORT_BAMDMA bool "SPS support BAM DMA" depends on SPS diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile index f6f212ec221..92eb492591a 100644 --- a/drivers/platform/msm/Makefile +++ b/drivers/platform/msm/Makefile @@ -2,4 +2,5 @@ # Makefile for the MSM specific device drivers. # obj-$(CONFIG_MSM_SSBI) += ssbi.o +obj-$(CONFIG_USB_BAM) += usb_bam.o obj-$(CONFIG_SPS) += sps/ diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c new file mode 100644 index 00000000000..b34c35ed414 --- /dev/null +++ b/drivers/platform/msm/usb_bam.c @@ -0,0 +1,238 @@ +/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_SUMMING_THRESHOLD 512 +#define CONNECTIONS_NUM 4 + +static struct sps_bam_props usb_props; +static struct sps_pipe *sps_pipes[CONNECTIONS_NUM][2]; +static struct sps_connect sps_connections[CONNECTIONS_NUM][2]; +static struct sps_mem_buffer data_mem_buf[CONNECTIONS_NUM][2]; +static struct sps_mem_buffer desc_mem_buf[CONNECTIONS_NUM][2]; +static struct platform_device *usb_bam_pdev; + +struct usb_bam_connect_info { + u8 idx; + u8 *src_pipe; + u8 *dst_pipe; + bool enabled; +}; + +static struct usb_bam_connect_info usb_bam_connections[CONNECTIONS_NUM]; + +static int connect_pipe(u8 connection_idx, enum usb_bam_pipe_dir pipe_dir, + u8 *usb_pipe_idx) +{ + int ret; + struct sps_pipe *pipe = sps_pipes[connection_idx][pipe_dir]; + struct sps_connect *connection = + &sps_connections[connection_idx][pipe_dir]; + struct msm_usb_bam_platform_data *pdata = + (struct msm_usb_bam_platform_data *) + (usb_bam_pdev->dev.platform_data); + struct usb_bam_pipe_connect *pipe_connection = + (struct usb_bam_pipe_connect *)(pdata->connections + + (2*connection_idx+pipe_dir)); + + pipe = sps_alloc_endpoint(); + if (pipe == NULL) { + pr_err("%s: sps_alloc_endpoint failed\n", __func__); + return -ENOMEM; + } + + ret = sps_get_config(pipe, connection); + if (ret) { + pr_err("%s: tx get config failed %d\n", __func__, ret); + goto get_config_failed; + } + + ret = sps_phy2h(pipe_connection->src_phy_addr, &(connection->source)); + if (ret) { + pr_err("%s: sps_phy2h failed (src BAM) %d\n", __func__, ret); + goto get_config_failed; + } + + connection->src_pipe_index = pipe_connection->src_pipe_index; + ret = sps_phy2h(pipe_connection->dst_phy_addr, + &(connection->destination)); + if (ret) { + pr_err("%s: sps_phy2h failed (dst BAM) %d\n", __func__, ret); + goto get_config_failed; + } + connection->dest_pipe_index = pipe_connection->dst_pipe_index; + + if (pipe_dir == USB_TO_PEER_PERIPHERAL) { + connection->mode = SPS_MODE_SRC; + *usb_pipe_idx = connection->src_pipe_index; + } else { + connection->mode = SPS_MODE_DEST; + *usb_pipe_idx = connection->dest_pipe_index; + } + + ret = sps_setup_bam2bam_fifo( + &data_mem_buf[connection_idx][pipe_dir], + pipe_connection->data_fifo_base_offset, + pipe_connection->data_fifo_size, 1); + if (ret) { + pr_err("%s: data fifo setup failure %d\n", __func__, ret); + goto fifo_setup_error; + } + connection->data = data_mem_buf[connection_idx][pipe_dir]; + + ret = sps_setup_bam2bam_fifo( + &desc_mem_buf[connection_idx][pipe_dir], + pipe_connection->desc_fifo_base_offset, + pipe_connection->desc_fifo_size, 1); + if (ret) { + pr_err("%s: desc. fifo setup failure %d\n", __func__, ret); + goto fifo_setup_error; + } + connection->desc = desc_mem_buf[connection_idx][pipe_dir]; + connection->event_thresh = 512; + + ret = sps_connect(pipe, connection); + if (ret < 0) { + pr_err("%s: tx connect error %d\n", __func__, ret); + goto error; + } + return 0; + +error: + sps_disconnect(pipe); +fifo_setup_error: +get_config_failed: + sps_free_endpoint(pipe); + return ret; +} + +int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx) +{ + struct usb_bam_connect_info *connection = &usb_bam_connections[idx]; + int ret; + + if (idx >= CONNECTIONS_NUM) { + pr_err("%s: Invalid connection index\n", + __func__); + return -EINVAL; + } + + if (connection->enabled) { + pr_info("%s: connection %d was already established\n", + __func__, idx); + return 0; + } + connection->src_pipe = src_pipe_idx; + connection->dst_pipe = dst_pipe_idx; + connection->idx = idx; + + /* open USB -> Peripheral pipe */ + ret = connect_pipe(connection->idx, USB_TO_PEER_PERIPHERAL, + connection->src_pipe); + if (ret) { + pr_err("%s: src pipe connection failure\n", __func__); + return ret; + } + /* open Peripheral -> USB pipe */ + ret = connect_pipe(connection->idx, PEER_PERIPHERAL_TO_USB, + connection->dst_pipe); + if (ret) { + pr_err("%s: dst pipe connection failure\n", __func__); + return ret; + } + connection->enabled = 1; + + return 0; +} +static int usb_bam_init(void) +{ + u32 h_usb; + int ret; + void *usb_virt_addr; + struct msm_usb_bam_platform_data *pdata = + (struct msm_usb_bam_platform_data *) + (usb_bam_pdev->dev.platform_data); + + usb_virt_addr = ioremap_nocache( + pdata->usb_bam_phy_base, + pdata->usb_bam_phy_size); + if (!usb_virt_addr) { + pr_err("%s: ioremap failed\n", __func__); + return -ENOMEM; + } + usb_props.phys_addr = pdata->usb_bam_phy_base; + usb_props.virt_addr = usb_virt_addr; + usb_props.virt_size = pdata->usb_bam_phy_size; + usb_props.irq = USB1_HS_BAM_IRQ; + usb_props.num_pipes = pdata->usb_bam_num_pipes; + usb_props.summing_threshold = USB_SUMMING_THRESHOLD; + ret = sps_register_bam_device(&usb_props, &h_usb); + if (ret < 0) { + pr_err("%s: register bam error %d\n", __func__, ret); + return -EFAULT; + } + + return 0; +} + +static int usb_bam_probe(struct platform_device *pdev) +{ + int ret, i; + + dev_dbg(&pdev->dev, "usb_bam_probe\n"); + + for (i = 0; i < CONNECTIONS_NUM; i++) + usb_bam_connections[i].enabled = 0; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "missing platform_data\n"); + return -ENODEV; + } + usb_bam_pdev = pdev; + + ret = usb_bam_init(); + if (ret) { + dev_err(&pdev->dev, "failed to get platform resource mem\n"); + return ret; + } + + return 0; +} + +static struct platform_driver usb_bam_driver = { + .probe = usb_bam_probe, + .driver = { .name = "usb_bam", }, +}; + +static int __init init(void) +{ + return platform_driver_register(&usb_bam_driver); +} +module_init(init); + +static void __exit cleanup(void) +{ + platform_driver_unregister(&usb_bam_driver); +} +module_exit(cleanup); + +MODULE_DESCRIPTION("MSM USB BAM DRIVER"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 68fc67c4ef6..eb2c5437b2e 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -131,6 +131,19 @@ enum usb_chg_type { USB_ACA_DOCK_CHARGER, }; +/** + * SPS Pipes direction. + * + * USB_TO_PEER_PERIPHERAL USB (as Producer) to other + * peer peripheral. + * PEER_PERIPHERAL_TO_USB Other Peripheral to + * USB (as consumer). + */ +enum usb_bam_pipe_dir { + USB_TO_PEER_PERIPHERAL, + PEER_PERIPHERAL_TO_USB, +}; + /** * struct msm_otg_platform_data - platform device data * for msm_otg driver. @@ -258,4 +271,21 @@ struct msm_hsic_host_platform_data { unsigned hub_reset; }; +struct usb_bam_pipe_connect { + u32 src_phy_addr; + int src_pipe_index; + u32 dst_phy_addr; + int dst_pipe_index; + u32 data_fifo_base_offset; + u32 data_fifo_size; + u32 desc_fifo_base_offset; + u32 desc_fifo_size; +}; + +struct msm_usb_bam_platform_data { + struct usb_bam_pipe_connect *connections; + unsigned long usb_bam_phy_base; + unsigned long usb_bam_phy_size; + int usb_bam_num_pipes; +}; #endif