M7350/kernel/drivers/usb/host/pehci/hal/hal_msm.c
2024-09-09 08:52:07 +00:00

749 lines
19 KiB
C

/*
* Copyright (C) ST-Ericsson AP Pte Ltd 2010
*
* ISP1763 Linux HCD Controller driver : hal
*
* This program is free software; you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation; version
* 2 of the License.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* This is the main hardware abstraction layer file. Hardware initialization, interupt
* processing and read/write routines are handled here.
*
* Author : wired support <wired.support@stericsson.com>
*
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/completion.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/usb.h>
#include <linux/gpio.h>
#include <mach/board.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
/*--------------------------------------------------------------*
* linux system include files
*--------------------------------------------------------------*/
#include "hal_msm.h"
#include "../hal/hal_intf.h"
#include "../hal/isp1763.h"
/*--------------------------------------------------------------*
* Local variable Definitions
*--------------------------------------------------------------*/
struct isp1763_dev isp1763_loc_dev[ISP1763_LAST_DEV];
/*--------------------------------------------------------------*
* Local # Definitions
*--------------------------------------------------------------*/
#define PCI_ACCESS_RETRY_COUNT 20
#define ISP1763_DRIVER_NAME "isp1763_usb"
/*--------------------------------------------------------------*
* Local Function
*--------------------------------------------------------------*/
static int __devexit isp1763_remove(struct platform_device *pdev);
static int __devinit isp1763_probe(struct platform_device *pdev);
/*--------------------------------------------------------------*
* Platform Driver Interface Functions
*--------------------------------------------------------------*/
static struct platform_driver isp1763_usb_driver = {
.remove = __exit_p(isp1763_remove),
.driver = {
.name = ISP1763_DRIVER_NAME,
.owner = THIS_MODULE,
},
};
/*--------------------------------------------------------------*
* ISP1763 Read write routine
*--------------------------------------------------------------*/
/*
* EBI2 on 8660 ignores the first bit and shifts the address by
* one bit to the right.
* Hence, shift left all the register addresses before accessing
* them over EBI2.
* This logic applies only for the register read/writes, for
* read/write from ISP memory this conversion is not needed
* as the ISP obtains the memory address from 'memory' register
*/
/* Write a 32 bit Register of isp1763 */
void
isp1763_reg_write32(struct isp1763_dev *dev, u16 reg, u32 data)
{
/* Write the 32bit to the register address given to us */
reg <<= 1;
#ifdef DATABUS_WIDTH_16
writew((u16) data, dev->baseaddress + ((reg)));
writew((u16) (data >> 16), dev->baseaddress + (((reg + 4))));
#else
writeb((u8) data, dev->baseaddress + (reg));
writeb((u8) (data >> 8), dev->baseaddress + ((reg + 1)));
writeb((u8) (data >> 16), dev->baseaddress + ((reg + 2)));
writeb((u8) (data >> 24), dev->baseaddress + ((reg + 3)));
#endif
}
EXPORT_SYMBOL(isp1763_reg_write32);
/* Read a 32 bit Register of isp1763 */
u32
isp1763_reg_read32(struct isp1763_dev *dev, u16 reg, u32 data)
{
#ifdef DATABUS_WIDTH_16
u16 wvalue1, wvalue2;
#else
u8 bval1, bval2, bval3, bval4;
#endif
data = 0;
reg <<= 1;
#ifdef DATABUS_WIDTH_16
wvalue1 = readw(dev->baseaddress + ((reg)));
wvalue2 = readw(dev->baseaddress + (((reg + 4))));
data |= wvalue2;
data <<= 16;
data |= wvalue1;
#else
bval1 = readb(dev->baseaddress + (reg));
bval2 = readb(dev->baseaddress + (reg + 1));
bval3 = readb(dev->baseaddress + (reg + 2));
bval4 = readb(dev->baseaddress + (reg + 3));
data = 0;
data |= bval4;
data <<= 8;
data |= bval3;
data <<= 8;
data |= bval2;
data <<= 8;
data |= bval1;
#endif
return data;
}
EXPORT_SYMBOL(isp1763_reg_read32);
/* Read a 16 bit Register of isp1763 */
u16
isp1763_reg_read16(struct isp1763_dev * dev, u16 reg, u16 data)
{
reg <<= 1;
#ifdef DATABUS_WIDTH_16
data = readw(dev->baseaddress + ((reg)));
#else
u8 bval1, bval2;
bval1 = readb(dev->baseaddress + (reg));
if (reg == HC_DATA_REG){
bval2 = readb(dev->baseaddress + (reg));
} else {
bval2 = readb(dev->baseaddress + ((reg + 1)));
}
data = 0;
data |= bval2;
data <<= 8;
data |= bval1;
#endif
return data;
}
EXPORT_SYMBOL(isp1763_reg_read16);
/* Write a 16 bit Register of isp1763 */
void
isp1763_reg_write16(struct isp1763_dev *dev, u16 reg, u16 data)
{
reg <<= 1;
#ifdef DATABUS_WIDTH_16
writew(data, dev->baseaddress + ((reg)));
#else
writeb((u8) data, dev->baseaddress + (reg));
if (reg == HC_DATA_REG){
writeb((u8) (data >> 8), dev->baseaddress + (reg));
}else{
writeb((u8) (data >> 8), dev->baseaddress + ((reg + 1)));
}
#endif
}
EXPORT_SYMBOL(isp1763_reg_write16);
/* Read a 8 bit Register of isp1763 */
u8
isp1763_reg_read8(struct isp1763_dev *dev, u16 reg, u8 data)
{
reg <<= 1;
data = readb((dev->baseaddress + (reg)));
return data;
}
EXPORT_SYMBOL(isp1763_reg_read8);
/* Write a 8 bit Register of isp1763 */
void
isp1763_reg_write8(struct isp1763_dev *dev, u16 reg, u8 data)
{
reg <<= 1;
writeb(data, (dev->baseaddress + (reg)));
}
EXPORT_SYMBOL(isp1763_reg_write8);
/*--------------------------------------------------------------*
*
* Module dtatils: isp1763_mem_read
*
* Memory read using PIO method.
*
* Input: struct isp1763_driver *drv --> Driver structure.
* u32 start_add --> Starting address of memory
* u32 end_add ---> End address
*
* u32 * buffer --> Buffer pointer.
* u32 length ---> Length
* u16 dir ---> Direction ( Inc or Dec)
*
* Output int Length ----> Number of bytes read
*
* Called by: system function
*
*
*--------------------------------------------------------------*/
/* Memory read function PIO */
int
isp1763_mem_read(struct isp1763_dev *dev, u32 start_add,
u32 end_add, u32 * buffer, u32 length, u16 dir)
{
u8 *one = (u8 *) buffer;
u16 *two = (u16 *) buffer;
u32 a = (u32) length;
u32 w;
u32 w2;
if (buffer == 0) {
printk("Buffer address zero\n");
return 0;
}
isp1763_reg_write16(dev, HC_MEM_READ_REG, start_add);
/* This delay requirement comes from the ISP1763A programming guide */
ndelay(100);
last:
w = isp1763_reg_read16(dev, HC_DATA_REG, w);
w2 = isp1763_reg_read16(dev, HC_DATA_REG, w);
w2 <<= 16;
w = w | w2;
if (a == 1) {
*one = (u8) w;
return 0;
}
if (a == 2) {
*two = (u16) w;
return 0;
}
if (a == 3) {
*two = (u16) w;
two += 1;
w >>= 16;
*two = (u8) (w);
return 0;
}
while (a > 0) {
*buffer = w;
a -= 4;
if (a <= 0) {
break;
}
if (a < 4) {
buffer += 1;
one = (u8 *) buffer;
two = (u16 *) buffer;
goto last;
}
buffer += 1;
w = isp1763_reg_read16(dev, HC_DATA_REG, w);
w2 = isp1763_reg_read16(dev, HC_DATA_REG, w);
w2 <<= 16;
w = w | w2;
}
return ((a < 0) || (a == 0)) ? 0 : (-1);
}
EXPORT_SYMBOL(isp1763_mem_read);
/*--------------------------------------------------------------*
*
* Module dtatils: isp1763_mem_write
*
* Memory write using PIO method.
*
* Input: struct isp1763_driver *drv --> Driver structure.
* u32 start_add --> Starting address of memory
* u32 end_add ---> End address
*
* u32 * buffer --> Buffer pointer.
* u32 length ---> Length
* u16 dir ---> Direction ( Inc or Dec)
*
* Output int Length ----> Number of bytes read
*
* Called by: system function
*
*
*--------------------------------------------------------------*/
/* Memory read function IO */
int
isp1763_mem_write(struct isp1763_dev *dev,
u32 start_add, u32 end_add, u32 * buffer, u32 length, u16 dir)
{
int a = length;
u8 one = (u8) (*buffer);
u16 two = (u16) (*buffer);
isp1763_reg_write16(dev, HC_MEM_READ_REG, start_add);
/* This delay requirement comes from the ISP1763A programming guide */
ndelay(100);
if (a == 1) {
isp1763_reg_write16(dev, HC_DATA_REG, one);
return 0;
}
if (a == 2) {
isp1763_reg_write16(dev, HC_DATA_REG, two);
return 0;
}
while (a > 0) {
isp1763_reg_write16(dev, HC_DATA_REG, (u16) (*buffer));
if (a >= 3)
isp1763_reg_write16(dev, HC_DATA_REG,
(u16) ((*buffer) >> 16));
start_add += 4;
a -= 4;
if (a <= 0)
break;
buffer += 1;
}
return ((a < 0) || (a == 0)) ? 0 : (-1);
}
EXPORT_SYMBOL(isp1763_mem_write);
/*--------------------------------------------------------------*
*
* Module dtatils: isp1763_register_driver
*
* This function is used by top driver (OTG, HCD, DCD) to register
* their communication functions (probe, remove, suspend, resume) using
* the drv data structure.
* This function will call the probe function of the driver if the ISP1763
* corresponding to the driver is enabled
*
* Input: struct isp1763_driver *drv --> Driver structure.
* Output result
* 0= complete
* 1= error.
*
* Called by: system function module_init
*
*
*--------------------------------------------------------------*/
int
isp1763_register_driver(struct isp1763_driver *drv)
{
struct isp1763_dev *dev;
int result = -EINVAL;
hal_entry("%s: Entered\n", __FUNCTION__);
info("isp1763_register_driver(drv=%p)\n", drv);
if (!drv) {
return -EINVAL;
}
dev = &isp1763_loc_dev[drv->index];
if (!dev->baseaddress)
return -EINVAL;
dev->active = 1; /* set the driver as active*/
if (drv->probe) {
result = drv->probe(dev, drv->id);
} else {
printk("%s no probe function for indes %d \n", __FUNCTION__,
(int)drv->index);
}
if (result >= 0) {
pr_debug(KERN_INFO __FILE__ ": Registered Driver %s\n",
drv->name);
dev->driver = drv;
}
hal_entry("%s: Exit\n", __FUNCTION__);
return result;
} /* End of isp1763_register_driver */
EXPORT_SYMBOL(isp1763_register_driver);
/*--------------------------------------------------------------*
*
* Module dtatils: isp1763_unregister_driver
*
* This function is used by top driver (OTG, HCD, DCD) to de-register
* their communication functions (probe, remove, suspend, resume) using
* the drv data structure.
* This function will check whether the driver is registered or not and
* call the remove function of the driver if registered
*
* Input: struct isp1763_driver *drv --> Driver structure.
* Output result
* 0= complete
* 1= error.
*
* Called by: system function module_init
*
*
*--------------------------------------------------------------*/
void
isp1763_unregister_driver(struct isp1763_driver *drv)
{
struct isp1763_dev *dev;
hal_entry("%s: Entered\n", __FUNCTION__);
info("isp1763_unregister_driver(drv=%p)\n", drv);
dev = &isp1763_loc_dev[drv->index];
if (dev->driver == drv) {
/* driver registered is same as the requestig driver */
drv->remove(dev);
dev->driver = NULL;
info(": De-registered Driver %s\n", drv->name);
return;
}
hal_entry("%s: Exit\n", __FUNCTION__);
} /* End of isp1763_unregister_driver */
EXPORT_SYMBOL(isp1763_unregister_driver);
/*--------------------------------------------------------------*
* ISP1763 Platform driver interface routine.
*--------------------------------------------------------------*/
/*--------------------------------------------------------------*
*
* Module dtatils: isp1763_module_init
*
* This is the module initialization function. It registers to
* driver for a isp1763 platform device. And also resets the
* internal data structures.
*
* Input: void
* Output result
* 0= complete
* 1= error.
*
* Called by: system function module_init
*
*
*
-------------------------------------------------------------------*/
static int __init
isp1763_module_init(void)
{
int result = 0;
hal_entry("%s: Entered\n", __FUNCTION__);
pr_debug(KERN_NOTICE "+isp1763_module_init\n");
memset(isp1763_loc_dev, 0, sizeof(isp1763_loc_dev));
result = platform_driver_probe(&isp1763_usb_driver, isp1763_probe);
pr_debug(KERN_NOTICE "-isp1763_module_init\n");
hal_entry("%s: Exit\n", __FUNCTION__);
return result;
}
/*--------------------------------------------------------------*
*
* Module dtatils: isp1763_module_cleanup
*
* This is the module cleanup function. It de-registers the
* Platform driver and resets the internal data structures.
*
* Input: void
* Output void
*
* Called by: system function module_cleanup
*
*
*
--------------------------------------------------------------*/
static void __exit
isp1763_module_cleanup(void)
{
pr_debug("Hal Module Cleanup\n");
platform_driver_unregister(&isp1763_usb_driver);
memset(isp1763_loc_dev, 0, sizeof(isp1763_loc_dev));
}
void dummy_mem_read(struct isp1763_dev *dev)
{
u32 w = 0;
isp1763_reg_write16(dev, HC_MEM_READ_REG, 0x0400);
w = isp1763_reg_read16(dev, HC_DATA_REG, w);
pr_debug("dummy_read DONE: %x\n", w);
msleep(10);
}
/*--------------------------------------------------------------*
*
* Module dtatils: isp1763_probe
*
* probe function of ISP1763
* This function is called from module_init if the corresponding platform
* device is present. This function initializes the information
* for the Host Controller with the assigned resources and tests the register
* access to the controller and do a software reset and makes it ready
* for the driver to play with. It also calls setup_gpio passed from pdata
* to setup GPIOs (e.g. used for IRQ and RST lines).
*
* Input:
* struct platform_device *dev ----> Platform Device structure
* Output void
*
* Called by: system function module_cleanup
*
*
*
--------------------------------------------------------------**/
static int __devinit
isp1763_probe(struct platform_device *pdev)
{
u32 reg_data = 0;
struct isp1763_dev *loc_dev;
int status = 1;
u32 hwmodectrl = 0;
u16 us_reset_hc = 0;
u32 chipid = 0;
struct isp1763_platform_data *pdata = pdev->dev.platform_data;
hal_entry("%s: Entered\n", __FUNCTION__);
hal_init(("isp1763_probe(dev=%p)\n", dev));
loc_dev = &(isp1763_loc_dev[ISP1763_HC]);
loc_dev->dev = pdev;
/* Get the Host Controller IO and INT resources */
loc_dev->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!loc_dev->mem_res) {
pr_err("%s: failed to get platform resource mem\n", __func__);
return -ENODEV;
}
loc_dev->baseaddress = ioremap_nocache(loc_dev->mem_res->start,
resource_size(loc_dev->mem_res));
if (!loc_dev->baseaddress) {
pr_err("%s: ioremap failed\n", __func__);
status = -ENOMEM;
goto put_mem_res;
}
pr_info("%s: ioremap done at: %x\n", __func__,
(int)loc_dev->baseaddress);
loc_dev->irq = platform_get_irq(pdev, 0);
if (!loc_dev->irq) {
pr_err("%s: platform_get_irq failed\n", __func__);
status = -ENODEV;
goto free_regs;
}
loc_dev->index = ISP1763_HC; /*zero */
loc_dev->length = resource_size(loc_dev->mem_res);
hal_init(("isp1763 HC MEM Base= %p irq = %d\n",
loc_dev->baseaddress, loc_dev->irq));
/* Setup GPIOs and isssue RESET_N to Controller */
if (pdata->setup_gpio)
if (pdata->setup_gpio(1))
pr_err("%s: Failed to setup GPIOs for isp1763\n",
__func__);
if (pdata->reset_gpio) {
gpio_set_value(pdata->reset_gpio, 0);
msleep(10);
gpio_set_value(pdata->reset_gpio, 1);
} else {
pr_err("%s: Failed to issue RESET_N to isp1763\n", __func__);
}
dummy_mem_read(loc_dev);
chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
pr_info("START: chip id:%x\n", chipid);
/*reset the host controller */
pr_debug("RESETTING\n");
us_reset_hc |= 0x1;
isp1763_reg_write16(loc_dev, 0xB8, us_reset_hc);
msleep(20);
us_reset_hc = 0;
us_reset_hc |= 0x2;
isp1763_reg_write16(loc_dev, 0xB8, us_reset_hc);
chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
pr_info("after HC reset, chipid:%x\n", chipid);
msleep(20);
hwmodectrl = isp1763_reg_read16(loc_dev, HC_HWMODECTRL_REG, hwmodectrl);
pr_debug("Mode Ctrl Value b4 setting buswidth: %x\n", hwmodectrl);
#ifdef DATABUS_WIDTH_16
hwmodectrl &= 0xFFEF; /*enable the 16 bit bus */
#else
pr_debug("Setting 8-BIT mode\n");
hwmodectrl |= 0x0010; /*enable the 8 bit bus */
#endif
isp1763_reg_write16(loc_dev, HC_HWMODECTRL_REG, hwmodectrl);
pr_debug("writing 0x%x to hw mode reg\n", hwmodectrl);
hwmodectrl = isp1763_reg_read16(loc_dev, HC_HWMODECTRL_REG, hwmodectrl);
msleep(100);
pr_debug("Mode Ctrl Value after setting buswidth: %x\n", hwmodectrl);
chipid = isp1763_reg_read32(loc_dev, DC_CHIPID, chipid);
pr_debug("after setting HW MODE to 8bit, chipid:%x\n", chipid);
hal_init(("isp1763 DC MEM Base= %lx irq = %d\n",
loc_dev->io_base, loc_dev->irq));
reg_data = isp1763_reg_read16(loc_dev, HC_SCRATCH_REG, reg_data);
pr_debug("Scratch register is 0x%x\n", reg_data);
reg_data = 0xABCD;
isp1763_reg_write16(loc_dev, HC_SCRATCH_REG, reg_data);
reg_data = isp1763_reg_read16(loc_dev, HC_SCRATCH_REG, reg_data);
pr_debug("After write, Scratch register is 0x%x\n", reg_data);
if (reg_data != 0xABCD) {
pr_err("%s: Scratch register write mismatch!!\n", __func__);
status = -ENODEV;
goto free_gpios;
}
memcpy(loc_dev->name, ISP1763_DRIVER_NAME, sizeof(ISP1763_DRIVER_NAME));
loc_dev->name[sizeof(ISP1763_DRIVER_NAME)] = 0;
pr_debug(KERN_NOTICE "-isp1763_pci_probe\n");
hal_entry("%s: Exit\n", __FUNCTION__);
return 0;
free_gpios:
if (pdata->setup_gpio)
pdata->setup_gpio(0);
free_regs:
iounmap(loc_dev->baseaddress);
put_mem_res:
loc_dev->baseaddress = NULL;
hal_entry("%s: Exit\n", __FUNCTION__);
return status;
} /* End of isp1763_probe */
/*--------------------------------------------------------------*
*
* Module details: isp1763_remove
*
* cleanup function of ISP1763
* This functions de-initializes the local variables, frees GPIOs
* and releases memory resource.
*
* Input:
* struct platform_device *dev ----> Platform Device structure
*
* Output void
*
* Called by: system function module_cleanup
*
*
*
--------------------------------------------------------------*/
static int __devexit
isp1763_remove(struct platform_device *pdev)
{
struct isp1763_dev *loc_dev;
struct isp1763_platform_data *pdata = pdev->dev.platform_data;
hal_init(("isp1763_pci_remove(dev=%p)\n", dev));
loc_dev = &isp1763_loc_dev[ISP1763_HC];
iounmap(loc_dev->baseaddress);
loc_dev->baseaddress = NULL;
if (pdata->setup_gpio)
return pdata->setup_gpio(0);
return 0;
} /* End of isp1763_remove */
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_init(isp1763_module_init);
module_exit(isp1763_module_cleanup);