blob: 87f06db33ce3c91043a0daf5f087cc283df612b3 [file] [log] [blame]
/*
*
* Copyright (C) 2007 Freescale Semiconductor, Inc.
*
* 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
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <mach/hardware.h>
#include <mach/comcerto-2000/clk-rst.h>
#include "pfe_mod.h"
/**
* pfe_platform_probe -
*
*
*/
static int pfe_platform_probe(struct platform_device *pdev)
{
struct resource *r;
int rc;
struct clk *clk_axi;
printk(KERN_INFO "%s\n", __func__);
pfe = kzalloc(sizeof(struct pfe), GFP_KERNEL);
if (!pfe) {
rc = -ENOMEM;
goto err_alloc;
}
platform_set_drvdata(pdev, pfe);
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ddr");
if (!r) {
printk(KERN_INFO "platform_get_resource_byname(ddr) failed\n");
rc = -ENXIO;
goto err_ddr;
}
pfe->ddr_phys_baseaddr = r->start;
pfe->ddr_size = resource_size(r);
pfe->ddr_baseaddr = ioremap(r->start, resource_size(r));
pr_err("pfe->ddr_baseaddr = %p size %u\n", pfe->ddr_baseaddr, resource_size(r));
if (!pfe->ddr_baseaddr) {
printk(KERN_INFO "ioremap(%p %08x) ddr failed\n", (void*) r->start, resource_size(r));
rc = -ENOMEM;
goto err_ddr;
}
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "axi");
if (!r) {
printk(KERN_INFO "platform_get_resource_byname(axi) failed\n");
rc = -ENXIO;
goto err_axi;
}
pfe->cbus_baseaddr = ioremap(r->start, resource_size(r));
pr_err("cbus start %08x size %08x\n", r->start, resource_size(r));
if (!pfe->cbus_baseaddr) {
printk(KERN_INFO "ioremap() axi failed\n");
rc = -ENOMEM;
goto err_axi;
}
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apb");
if (!r) {
printk(KERN_INFO "platform_get_resource_byname(apb) failed\n");
rc = -ENXIO;
goto err_apb;
}
pfe->apb_baseaddr = ioremap(r->start, resource_size(r));
if (!pfe->apb_baseaddr) {
printk(KERN_INFO "ioremap() apb failed\n");
rc = -ENOMEM;
goto err_apb;
}
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iram");
if (!r) {
printk(KERN_INFO "platform_get_resource_byname(iram) failed\n");
rc = -ENXIO;
goto err_iram;
}
pfe->iram_phys_baseaddr = r->start;
pfe->iram_baseaddr = ioremap(r->start, resource_size(r));
if (!pfe->iram_baseaddr) {
printk(KERN_INFO "ioremap() iram failed\n");
rc = -ENOMEM;
goto err_iram;
}
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ipsec");
if (!r) {
printk(KERN_INFO "platform_get_resource_byname(ipsec) failed\n");
rc = -ENXIO;
goto err_ipsec;
}
pfe->ipsec_phys_baseaddr = r->start;
/* Just map only initial 1MB , as its enough to access espah engine
*/
//pfe->ipsec_baseaddr = ioremap(r->start, resource_size(r));
pfe->ipsec_baseaddr = ioremap(r->start, 1*1024*1024);
if (!pfe->ipsec_baseaddr) {
printk(KERN_INFO "ioremap() ipsec failed\n");
rc = -ENOMEM;
goto err_ipsec;
}
printk(KERN_INFO "ipsec: baseaddr :%x --- %x\n", (u32)pfe->ipsec_phys_baseaddr, (u32)pfe->ipsec_baseaddr);
pfe->dev = &pdev->dev;
/* FIXME this needs to be done at the BSP level with proper locking */
writel(readl(APB_VADDR(AXI_RESET_1)) | (1 << 3), APB_VADDR(AXI_RESET_1));
mdelay(1);
writel(readl(APB_VADDR(AXI_RESET_1)) & ~(1 << 3), APB_VADDR(AXI_RESET_1));
/* Get the system clock */
clk_axi = clk_get(NULL,"axi");
if (IS_ERR(clk_axi)) {
printk(KERN_INFO "clk_get call failed\n");
rc = -ENXIO;
goto err_clk;
}
pfe->ctrl.clk_axi = clk_axi;
pfe->ctrl.sys_clk = clk_get_rate(clk_axi) / 1000; // save sys_clk value as KHz
rc = pfe_probe(pfe);
if (rc < 0)
goto err_probe;
return 0;
err_probe:
clk_put(clk_axi);
err_clk:
iounmap(pfe->ipsec_baseaddr);
err_ipsec:
iounmap(pfe->iram_baseaddr);
err_iram:
iounmap(pfe->apb_baseaddr);
err_apb:
iounmap(pfe->cbus_baseaddr);
err_axi:
iounmap(pfe->ddr_baseaddr);
err_ddr:
platform_set_drvdata(pdev, NULL);
kfree(pfe);
err_alloc:
return rc;
}
/**
* pfe_platform_remove -
*
*
*/
static int pfe_platform_remove(struct platform_device *pdev)
{
struct pfe *pfe = platform_get_drvdata(pdev);
int rc;
printk(KERN_INFO "%s\n", __func__);
rc = pfe_remove(pfe);
/* FIXME this needs to be done at the BSP level with proper locking */
writel(readl(APB_VADDR(AXI_RESET_1)) | (1 << 3), APB_VADDR(AXI_RESET_1));
clk_put(pfe->ctrl.clk_axi);
iounmap(pfe->ipsec_baseaddr);
iounmap(pfe->iram_baseaddr);
iounmap(pfe->apb_baseaddr);
iounmap(pfe->cbus_baseaddr);
iounmap(pfe->ddr_baseaddr);
platform_set_drvdata(pdev, NULL);
kfree(pfe);
return rc;
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int pfe_platform_suspend(struct device *dev)
{
struct pfe *pfe = platform_get_drvdata(to_platform_device(dev));
struct net_device *netdev;
int i;
printk(KERN_INFO "%s\n", __func__);
pfe->wake = 0;
for (i = 0; i < (NUM_GEMAC_SUPPORT - 1); i++ ) {
netdev = pfe->eth.eth_priv[i]->dev;
netif_device_detach(netdev);
if (netif_running(netdev))
if(pfe_eth_suspend(netdev))
pfe->wake =1;
}
/* Shutdown PFE only if we're not waking up the system */
if (!pfe->wake) {
pfe_ctrl_suspend(&pfe->ctrl);
pfe_hif_exit(pfe);
pfe_hif_lib_exit(pfe);
class_disable();
tmu_disable(0xf);
#if !defined(CONFIG_UTIL_DISABLED)
util_disable();
#endif
pfe_hw_exit(pfe);
c2000_block_reset(COMPONENT_PFE_SYS, 1);
clk_disable(pfe->hfe_clock);
}
return 0;
}
static int pfe_platform_resume(struct device *dev)
{
struct pfe *pfe = platform_get_drvdata(to_platform_device(dev));
struct net_device *netdev;
int i;
printk(KERN_INFO "%s\n", __func__);
if (!pfe->wake) {
/* Sequence follows VLSI recommendation (bug 71927) */
c2000_block_reset(COMPONENT_PFE_SYS, 1);
mdelay(1);
c2000_block_reset(COMPONENT_PFE_SYS, 0);
clk_enable(pfe->hfe_clock);
pfe_hw_init(pfe, 1);
pfe_hif_lib_init(pfe);
pfe_hif_init(pfe);
#if !defined(CONFIG_UTIL_DISABLED)
util_enable();
#endif
tmu_enable(0xf);
class_enable();
pfe_ctrl_resume(&pfe->ctrl);
}
for(i = 0; i < (NUM_GEMAC_SUPPORT - 1); i++) {
netdev = pfe->eth.eth_priv[i]->dev;
if (pfe->eth.eth_priv[i]->mii_bus)
pfe_eth_mdio_reset(pfe->eth.eth_priv[i]->mii_bus);
if (netif_running(netdev))
pfe_eth_resume(netdev);
netif_device_attach(netdev);
}
return 0;
}
#else
#define pfe_platform_suspend NULL
#define pfe_platform_resume NULL
#endif
static const struct dev_pm_ops pfe_platform_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pfe_platform_suspend, pfe_platform_resume)
};
#endif
static struct platform_driver pfe_platform_driver = {
.probe = pfe_platform_probe,
.remove = pfe_platform_remove,
.driver = {
.name = "pfe",
#ifdef CONFIG_PM
.pm = &pfe_platform_pm_ops,
#endif
},
};
static int __init pfe_module_init(void)
{
printk(KERN_INFO "%s\n", __func__);
return platform_driver_register(&pfe_platform_driver);
}
static void __exit pfe_module_exit(void)
{
platform_driver_unregister(&pfe_platform_driver);
printk(KERN_INFO "%s\n", __func__);
}
MODULE_LICENSE("GPL");
module_init(pfe_module_init);
module_exit(pfe_module_exit);