/* 2007-12-11: File added by Sony Corporation */
/************************************************************
 * Philips ISP176x Hardware Abstraction Layer code file
 *
 * (c) 2002 Koninklijke Philips Electronics N.V. All rights reserved. <usb.linux@philips.com>
 *
 * 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 * File Name: hal_x86.c
 *
 * Refering linux kernel version 2.6.9
 *
 * History:
 *
 * Date                Author                  Comments
 * ---------------------------------------------------------------------
 * Nov 29 2005        Prabhakar Kalasani      Initial Creation
 *
 **********************************************************************
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/sched.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/poll.h>
#include <linux/platform_device.h>
#include <linux/vmalloc.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
#include <asm/dma.h>

/*--------------------------------------------------------------*
 *               linux system include files
 *--------------------------------------------------------------*/
#include "isp1761.h"

/*--------------------------------------------------------------*
 *               Local variable Definitions
 *--------------------------------------------------------------*/
struct isp1761_dev              isp1761_loc_dev[ISP1761_LAST_DEV];
static  struct isp1761_hal      hal_data;
static struct platform_device *s_pdev;
int      iolength = 0;
static   __u32            isp1761_base = 0;

/*--------------------------------------------------------------*
 *               Local # Definitions
 *--------------------------------------------------------------*/
#define         isp1761_driver_name     "1761-m6"
#define         ISP1761_IO_EXTENT 	(SZ_64K - 1)
#define		ISP1761_REGSET_LEN   	(SZ_64K - 1)

#define 	ISP1761_HC_CHIP_ID	0x1761
#define 	ISP1761_DC_CHIP_ID	0x8210

#define 	HC_SCRATCH_MAGIC_NUM	0xFACE



#undef CONFIG_PM /* PM no workie yet. */


#ifdef CONFIG_PM
static int isp1761_pci_suspend (struct pci_dev *dev, __u32 state);
static int isp1761_pci_resume (struct pci_dev *dev);
#endif

/*--------------------------------------------------------------*
 *               ISP1761 Read write routine
 *--------------------------------------------------------------*/

/* Write a 32 bit Register of isp1761 */
static void isp1761_reg_write32(struct isp1761_dev *dev,__u16 reg,__u32 data)
{
	/* Write the 32bit to the register address given to us*/
	writel(data,dev->baseaddress+reg);
}


/* Read a 32 bit Register of isp1761 */
static __u32 isp1761_reg_read32(struct isp1761_dev *dev,__u16 reg,__u32 data)
{
	data = readl(dev->baseaddress + reg);
	return data;
}


/* Read a 16 bit Register of isp1761 */
static __u16 isp1761_reg_read16(struct isp1761_dev *dev,__u16 reg,__u16 data)
{
	data = readw(dev->baseaddress+reg);
	return data;
}

/* Write a 16 bit Register of isp1761 */
static void isp1761_reg_write16(struct isp1761_dev *dev,__u16 reg,__u16 data)
{
	writew(data,dev->baseaddress+reg);
}

/*--------------------------------------------------------------*
 *
 * Module dtatils: isp1761_mem_read
 *
 * Memory read using PIO method.
 *
 *  Input: struct isp1761_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 */

static int
isp1761_mem_read(struct isp1761_dev *dev, __u32 start_add,
		__u32 end_add, __u32 * buffer, __u32 length, __u16 dir)
{
	u8 *temp_base_mem = 0;
	u8 *one = (u8 *) buffer;
	u16 *two = (u16 *) buffer;
	int a = (int)length;
	u32 w;
	temp_base_mem= (dev->baseaddress + start_add);
	/*initialize the Register 0x33C-used to manage Multiple threads */
	writel(start_add,dev->baseaddress + HC_MEM_READ_REG);

last:
	w = readl(temp_base_mem);
	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;
		temp_base_mem = temp_base_mem+4;
		start_add +=4;
		a -= 4;
		if(a == 0)
			break;
		if(a < 4){
			buffer += 1;
			one = (u8 *)buffer;
			two = (u16 *)buffer;
			goto last;
		}
		buffer += 1;
		w = readl(temp_base_mem);
	}
	return ((a < 0) || (a == 0))?0:(-1);

}

/*--------------------------------------------------------------*
 *
 * Module dtatils: isp1761_mem_write
 *
 * Memory write using PIO method.
 *
 *  Input: struct isp1761_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 write function PIO */
static int
isp1761_mem_write(struct isp1761_dev *dev,
		__u32 start_add, __u32 end_add,
		__u32 * buffer, __u32 length,
		__u16 dir)
{
	u8 *temp_base_mem = 0;
	int a = length;
	//u8 *temp = (u8*)buffer;
	u8 one      =(u8 )(*buffer);
	u16 two     =(u16 )(*buffer);
	temp_base_mem = (dev->baseaddress + start_add);

	if(a == 1){
		writeb(one,temp_base_mem);
		return 0;
	}
	if(a == 2){
		writew(two,temp_base_mem);
		return 0;
	}

	while(a>0){
		writel(*buffer, temp_base_mem);
		temp_base_mem = temp_base_mem+4;
		start_add +=4;
		a -=4;
		if(a <= 0)
			break;
		buffer += 1;

	}
	return ((a < 0) || (a == 0))?0:(-1);

}

/*--------------------------------------------------------------*
 *
 * Module dtatils: isp1761_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 ISP1761
 * corresponding to the driver is enabled
 *
 *  Input: struct isp1761_driver *drv  --> Driver structure.
 *  Output result
 *         0= complete
 *         1= error.
 *
 *  Called by: system function module_init
 *
 *
 *--------------------------------------------------------------*/

static int isp1761_register_driver(struct isp1761_driver *drv)
{
	struct isp1761_dev  *dev;
	int result;
	isp1761_id *id;

	hal_entry("++ %s: Entered\n",__FUNCTION__);
	hal_init(" %s: (drv=%p) \n",__FUNCTION__, drv);

	if(!drv)
		return -EINVAL;

	dev = &isp1761_loc_dev[drv->index];
	if(drv->index == ISP1761_DC){	/*FIX for device*/
		result = drv->probe(dev,drv->id);
	}else{
		id = drv->id;
		if(dev->active){
			result = drv->probe(dev,id);
		}
		else{
			result = -ENODEV;
		}
	}

	if(result >= 0 ) {
		hal_init(KERN_INFO "Registered Driver %s\n",
				drv->name);
		dev->driver = drv;
	}
	hal_entry("-- %s: Exit\n",__FUNCTION__);
	return result;
} /* End of isp1761_register_driver */


/*--------------------------------------------------------------*
 *
 * Module dtatils: isp1761_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 isp1761_driver *drv  --> Driver structure.
 *  Output result
 *         0= complete
 *         1= error.
 *
 *  Called by: system function module_init
 *
 *
 *--------------------------------------------------------------*/


static void isp1761_unregister_driver(struct isp1761_driver *drv)
{
	struct isp1761_dev  *dev;
	hal_entry("%s: Entered\n",__FUNCTION__);

	hal_init("isp1761_unregister_driver(drv=%p)\n",drv);

	dev = &isp1761_loc_dev[drv->index];
	if(dev->driver == drv) {
		/* driver registered is same as the requestig driver */
		drv->remove(dev);
		dev->driver = NULL;
		hal_init(": De-registered Driver %s\n",
				drv->name);
		return;
	}
	hal_entry("%s: Exit\n",__FUNCTION__);
} /* End of isp1761_unregister_driver */


/*--------------------------------------------------------------*
 *               ISP1761 PCI driver interface routine.
 *--------------------------------------------------------------*/

/*--------------------------------------------------------------*
 *
 *  Module dtatils: isp1761_probe
 *
 * PCI probe function of ISP1761
 * This function is called from PCI Driver as an initialization function
 * when it founds the PCI device. This functions initializes the information
 * for the 3 Controllers with the assigned resources and tests the register
 * access to these controllers and do a software reset and makes them ready
 * for the drivers to play with them.
 *
 *  Input:
 *              struct pci_dev *dev                     ----> PCI Devie data structure
 *      const struct pci_device_id *id  ----> PCI Device ID
 *  Output void
 *
 *  Called by: system function module_cleanup
 *
 *
 *
 --------------------------------------------------------------**/

static int __devinit isp1761_probe (struct platform_device *pdev)
{
	__u32       reg_data = 0;
	struct isp1761_dev  *loc_dev;
	struct resource *res;
	void *address = 0;
	int length = ISP1761_IO_EXTENT;
	int status = 0;
	hal_entry("++ %s: Entered\n",__FUNCTION__);

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "isp1761-regs");
	if (!res)
		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res)
		return -ENODEV;

	isp1761_base = res->start;
	hal_init("isp1761 pci base address = %x\n", isp1761_base);

	/* Get the Host Controller IO and INT resources
	 */
	loc_dev = &(isp1761_loc_dev[ISP1761_HC]);
	loc_dev->dev = &pdev->dev;
	loc_dev->irq = platform_get_irq(pdev, 0);
	loc_dev->io_base = isp1761_base;
	loc_dev->start   =  isp1761_base;
	loc_dev->length  = ISP1761_REGSET_LEN;
	loc_dev->io_len = ISP1761_REGSET_LEN; 	/*64K*/
	loc_dev->index = ISP1761_HC;		/*zero*/

	loc_dev->io_len = length;
	if(check_mem_region(loc_dev->io_base,length)<0){
		err("check_mem_region: host controller already in use\n");
		return -EBUSY;
	}
	if(!request_mem_region(loc_dev->io_base, length,isp1761_driver_name)){
		err("request_mem_region: host controller already in use\n");
		return -EBUSY;

	}

	/*map available memory*/
	address = ioremap_nocache(isp1761_base,length);
	if(address == NULL){
		err("memory map problem\n");
		release_mem_region(loc_dev->io_base,length);
		return -ENOMEM;
	}

	loc_dev->baseaddress = (u8*)address;

	/* Check Host Controller Chip ID*/
	{
		u32 chipid = 0;
		chipid = readl(address + HC_CHIP_ID_REG);
		if ((chipid & 0xffff) != ISP1761_HC_CHIP_ID ){
			err("isp1761 chipid wrong: ChipID %04x, vid %04x\n",
					(chipid & 0xffff), (chipid >> 16));
			status = -ENODEV;
			goto clean;
		}
		hal_init("HC: ChipID %04x, vid %04x\n", (chipid & 0xffff),
				(chipid >> 16));
	}
	hal_init("isp1761 HC MEM Base= %p irq = %d\n",
			loc_dev->baseaddress,loc_dev->irq);

	/*initialize device controller framework*/
	loc_dev = &(isp1761_loc_dev[ISP1761_DC]);
	loc_dev->dev = &pdev->dev;
	loc_dev->irq = platform_get_irq(pdev, 0);
	loc_dev->io_base = isp1761_base;
	loc_dev->start   = isp1761_base;
	loc_dev->length  = ISP1761_REGSET_LEN;
	loc_dev->io_len = ISP1761_REGSET_LEN;
	loc_dev->index = ISP1761_DC;
	loc_dev->baseaddress = address;
	loc_dev->active = 1;
	memcpy(loc_dev->name,"isp1761_dev",11);
	loc_dev->name[12] = '\0';

	/* Check Device Controller ID */
	{
		u32 chipid = 0;
		chipid = readl(address + DC_CHIP_ID_REG);
		if ((chipid & 0xffff) != ISP1761_DC_CHIP_ID ){
			err("isp1761 chipid wrong: ChipID %04x, vid %04x\n",
					(chipid & 0xffff), (chipid >> 16));
			status = -ENODEV;
			goto clean;
		}
		hal_init("DC: ChipID %04x, vid %04x\n", (chipid & 0xffff),
				(chipid >> 16));
	}
	hal_init("isp1761 DC MEM Base= %lx irq = %d\n",
			loc_dev->io_base,loc_dev->irq);

	/* Get the OTG Controller IO and INT resources
	 * OTG controller resources are same as Host Controller resources
	 */
	loc_dev = &(isp1761_loc_dev[ISP1761_OTG]);
	loc_dev->dev = &pdev->dev;
	loc_dev->irq = platform_get_irq(pdev, 0);
	loc_dev->io_base = isp1761_base;
	loc_dev->start   =  isp1761_base;
	loc_dev->length  = ISP1761_REGSET_LEN;
	loc_dev->io_len = ISP1761_REGSET_LEN;
	loc_dev->index = ISP1761_OTG;
	loc_dev->baseaddress = address; /*having the same address as of host*/
	loc_dev->active = 1;
	memcpy(loc_dev->name,"isp1761_otg",11);
	loc_dev->name[12] = '\0';

	hal_init("isp1761 OTG MEM Base= %lx irq = %x\n",
			loc_dev->io_base,loc_dev->irq);


#if 1 // CUSTOM STUFF - 32 bit mode
	isp1761_reg_write32(loc_dev, HC_HW_MODE_REG, ISP1761_HC_MODE );
#endif

	/* Try to check whether we can access Scratch Register of
	 * Host Controller or not.
	 */

	loc_dev = &(isp1761_loc_dev[ISP1761_HC]);
	isp1761_reg_write16(loc_dev, HC_SCRATCH_REG, HC_SCRATCH_MAGIC_NUM);
	udelay(100);
	reg_data = isp1761_reg_read16(loc_dev, HC_SCRATCH_REG,reg_data);

	/* Host Controller presence is detected by writing to scratch register
	 * and reading back and checking the contents are same or not
	 */
	if(reg_data != HC_SCRATCH_MAGIC_NUM) {
		err("%s scratch register mismatch %x",
				isp1761_driver_name,reg_data);
		status = -ENODEV;
		goto clean;
	}

	memcpy(loc_dev->name, isp1761_driver_name, sizeof(isp1761_driver_name));
	loc_dev->name[sizeof(isp1761_driver_name)] = 0;
	loc_dev->active = 1;

	hal_init("ISP1761 controller address %p\n", &pdev->dev);

	s_pdev = pdev;
	hal_data.irq_usage = 0;
	hal_entry("-- %s: Exit\n",__FUNCTION__);
	return 0;

clean:
	release_mem_region(loc_dev->io_base, loc_dev->io_len);
	iounmap(loc_dev->baseaddress);
	hal_entry("%s: Exit\n",__FUNCTION__);
	return status;
} /* End of isp1761_probe */


/*--------------------------------------------------------------*
 *
 *  Module dtatils: isp1761_remove
 *
 * PCI cleanup function of ISP1761
 * This function is called from PCI Driver as an removal function
 * in the absence of PCI device or a de-registration of driver.
 * This functions checks the registerd drivers (HCD, DCD, OTG) and calls
 * the corresponding removal functions. Also initializes the local variables
 * to zero.
 *
 *  Input:
 *              struct pci_dev *dev                     ----> PCI Devie data structure
 *
 *  Output void
 *
 *  Called by: system function module_cleanup
 *
 *
 *
 --------------------------------------------------------------*/
static int __devexit isp1761_remove (struct platform_device *pdev)
{
	struct isp1761_dev  *loc_dev;
	hal_init("isp1761_remove(dev=%p)\n",pdev);
	/*Lets handle the host first*/
	loc_dev  = &isp1761_loc_dev[ISP1761_HC];
	/*free the memory occupied by host*/
	release_mem_region(loc_dev->io_base, loc_dev->io_len);
	/*unmap the occupied memory resources*/
	iounmap(loc_dev->baseaddress);
	s_pdev = NULL;
	return 0;
} /* End of isp1761_remove */

/*--------------------------------------------------------------*
 *
 *  Module dtatils: isp1761_pci_suspend
 *
 * PCI suspend function of ISP1761
 * This function is called from PCI Driver.
 * This functions checks the registerd drivers (HCD, DCD, OTG) and calls
 * the corresponding suspend functions if present.
 *  Input:
 *              struct pci_dev *dev                     ----> PCI Devie data structure
 *
 *  Output void
 *
 *  Called by: system function
 *
 *
 *
 --------------------------------------------------------------*/

#ifdef CONFIG_PM
static int isp1761_pci_suspend (struct pci_dev *dev, __u32 state)
{
	struct isp1761_dev  *loc_dev;
	int                 index;

	hal_init("isp1761_pci_suspend(dev=%p, state = %x)\n",dev, state);


	loc_dev = (struct isp1761_dev *)pci_get_drvdata(dev);

	/* For each controller check whether driver is registerd
	 * or not. If registerd call the suspend function if it is
	 * present
	 */
	for(index=ISP1761_1ST_DEV;index<ISP1761_LAST_DEV;(index++,loc_dev++)) {
		if(loc_dev->driver && loc_dev->driver->suspend) {
			loc_dev->driver->suspend(loc_dev);
		}
	}

	return 0;
} /* End of isp1761_pci_suspend */
#endif


/*--------------------------------------------------------------*
 *
 *  Module dtatils: isp1761_pci_suspend
 *
 *  PCI resume function of ISP1761
 * This function is called from PCI Driver.
 * This functions checks the registerd drivers (HCD, DCD, OTG) and calls
 * the corresponding resume functions if present.
 *  Input:
 *              struct pci_dev *dev                     ----> PCI Devie data structure
 *
 *  Output void
 *
 *  Called by: system function
 *
 *
 --------------------------------------------------------------*/
#ifdef CONFIG_PM
static int isp1761_pci_resume (struct pci_dev *dev)
{
	struct isp1761_dev  *loc_dev;
	int                 index;
	hal_init("isp1362_pci_resume(dev=%p)\n",dev);
	loc_dev = (struct isp1761_dev *)pci_get_drvdata(dev);

	/* For each controller check whether driver is registerd
	 * or not. If registerd call the resume function if it is
	 * present
	 */
	for(index=ISP1761_1ST_DEV;index<ISP1761_LAST_DEV;(index++,loc_dev++)) {
		if(loc_dev->driver && loc_dev->driver->resume) {
			loc_dev->driver->resume(loc_dev);
		}
	}

	return 0;

} /* End of isp1362_pci_resume */
#endif


static struct platform_driver isp1761_platform_driver = {
	.probe   = isp1761_probe,
	.remove  = isp1761_remove,
#ifdef CONFIG_PM
	.suspend = isp1761_pci_suspend,
	.resume  = isp1761_pci_resume,
#endif
	.driver  = {
		.name = "isp1761",
	},
};


