1
0
Fork 0
alistair23-linux/drivers/staging/fsl_ppfe/pfe_ls1012a_platform.c

384 lines
8.1 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2015-2016 Freescale Semiconductor, Inc.
* Copyright 2017 NXP
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/of_address.h>
#include <linux/of_mdio.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include "pfe_mod.h"
extern bool pfe_use_old_dts_phy;
struct ls1012a_pfe_platform_data pfe_platform_data;
static int pfe_get_gemac_if_properties(struct device_node *gem,
int port,
struct ls1012a_pfe_platform_data *pdata)
{
struct device_node *phy_node = NULL;
int size;
int phy_id = 0;
const u32 *addr;
const u8 *mac_addr;
addr = of_get_property(gem, "reg", &size);
if (addr)
port = be32_to_cpup(addr);
else
goto err;
pdata->ls1012a_eth_pdata[port].gem_id = port;
mac_addr = of_get_mac_address(gem);
if (!IS_ERR_OR_NULL(mac_addr)) {
memcpy(pdata->ls1012a_eth_pdata[port].mac_addr, mac_addr,
ETH_ALEN);
}
phy_node = of_parse_phandle(gem, "phy-handle", 0);
pdata->ls1012a_eth_pdata[port].phy_node = phy_node;
if (phy_node) {
pfe_use_old_dts_phy = false;
goto process_phynode;
} else if (of_phy_is_fixed_link(gem)) {
pfe_use_old_dts_phy = false;
if (of_phy_register_fixed_link(gem) < 0) {
pr_err("broken fixed-link specification\n");
goto err;
}
phy_node = of_node_get(gem);
pdata->ls1012a_eth_pdata[port].phy_node = phy_node;
} else if (of_get_property(gem, "fsl,pfe-phy-if-flags", &size)) {
pfe_use_old_dts_phy = true;
/* Use old dts properties for phy handling */
addr = of_get_property(gem, "fsl,pfe-phy-if-flags", &size);
pdata->ls1012a_eth_pdata[port].phy_flags = be32_to_cpup(addr);
addr = of_get_property(gem, "fsl,gemac-phy-id", &size);
if (!addr) {
pr_err("%s:%d Invalid gemac-phy-id....\n", __func__,
__LINE__);
} else {
phy_id = be32_to_cpup(addr);
pdata->ls1012a_eth_pdata[port].phy_id = phy_id;
pdata->ls1012a_mdio_pdata[0].phy_mask &= ~(1 << phy_id);
}
/* If PHY is enabled, read mdio properties */
if (pdata->ls1012a_eth_pdata[port].phy_flags & GEMAC_NO_PHY)
goto done;
} else {
pr_info("%s: No PHY or fixed-link\n", __func__);
return 0;
}
process_phynode:
pdata->ls1012a_eth_pdata[port].mii_config = of_get_phy_mode(gem);
if ((pdata->ls1012a_eth_pdata[port].mii_config) < 0)
pr_err("%s:%d Incorrect Phy mode....\n", __func__,
__LINE__);
addr = of_get_property(gem, "fsl,mdio-mux-val", &size);
if (!addr) {
pr_err("%s: Invalid mdio-mux-val....\n", __func__);
} else {
phy_id = be32_to_cpup(addr);
pdata->ls1012a_eth_pdata[port].mdio_muxval = phy_id;
}
if (pdata->ls1012a_eth_pdata[port].phy_id < 32)
pfe->mdio_muxval[pdata->ls1012a_eth_pdata[port].phy_id] =
pdata->ls1012a_eth_pdata[port].mdio_muxval;
pdata->ls1012a_mdio_pdata[port].irq[0] = PHY_POLL;
done:
return 0;
err:
return -1;
}
/*
*
* pfe_platform_probe -
*
*
*/
static int pfe_platform_probe(struct platform_device *pdev)
{
struct resource res;
int ii, rc, interface_count = 0, size = 0;
const u32 *prop;
struct device_node *np, *gem = NULL;
struct clk *pfe_clk;
np = pdev->dev.of_node;
if (!np) {
pr_err("Invalid device node\n");
return -EINVAL;
}
pfe = kzalloc(sizeof(*pfe), GFP_KERNEL);
if (!pfe) {
rc = -ENOMEM;
goto err_alloc;
}
platform_set_drvdata(pdev, pfe);
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (of_address_to_resource(np, 1, &res)) {
rc = -ENOMEM;
pr_err("failed to get ddr resource\n");
goto err_ddr;
}
pfe->ddr_phys_baseaddr = res.start;
pfe->ddr_size = resource_size(&res);
pfe->ddr_baseaddr = memremap(res.start, resource_size(&res),
MEMREMAP_WB);
if (!pfe->ddr_baseaddr) {
pr_err("memremap() ddr failed\n");
rc = -ENOMEM;
goto err_ddr;
}
pfe->scfg =
syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"fsl,pfe-scfg");
if (IS_ERR(pfe->scfg)) {
dev_err(&pdev->dev, "No syscfg phandle specified\n");
return PTR_ERR(pfe->scfg);
}
pfe->cbus_baseaddr = of_iomap(np, 0);
if (!pfe->cbus_baseaddr) {
rc = -ENOMEM;
pr_err("failed to get axi resource\n");
goto err_axi;
}
pfe->hif_irq = platform_get_irq(pdev, 0);
if (pfe->hif_irq < 0) {
pr_err("platform_get_irq for hif failed\n");
rc = pfe->hif_irq;
goto err_hif_irq;
}
pfe->wol_irq = platform_get_irq(pdev, 2);
if (pfe->wol_irq < 0) {
pr_err("platform_get_irq for WoL failed\n");
rc = pfe->wol_irq;
goto err_hif_irq;
}
/* Read interface count */
prop = of_get_property(np, "fsl,pfe-num-interfaces", &size);
if (!prop) {
pr_err("Failed to read number of interfaces\n");
rc = -ENXIO;
goto err_prop;
}
interface_count = be32_to_cpup(prop);
if (interface_count <= 0) {
pr_err("No ethernet interface count : %d\n",
interface_count);
rc = -ENXIO;
goto err_prop;
}
pfe_platform_data.ls1012a_mdio_pdata[0].phy_mask = 0xffffffff;
for (ii = 0; ii < interface_count; ii++) {
gem = of_get_next_child(np, gem);
if (gem)
pfe_get_gemac_if_properties(gem, ii,
&pfe_platform_data);
else
pr_err("Unable to find interface %d\n", ii);
}
pfe->dev = &pdev->dev;
pfe->dev->platform_data = &pfe_platform_data;
/* declare WoL capabilities */
device_init_wakeup(&pdev->dev, true);
/* find the clocks */
pfe_clk = devm_clk_get(pfe->dev, "pfe");
if (IS_ERR(pfe_clk))
return PTR_ERR(pfe_clk);
/* PFE clock is (platform clock / 2) */
/* save sys_clk value as KHz */
pfe->ctrl.sys_clk = clk_get_rate(pfe_clk) / (2 * 1000);
rc = pfe_probe(pfe);
if (rc < 0)
goto err_probe;
return 0;
err_probe:
err_prop:
err_hif_irq:
iounmap(pfe->cbus_baseaddr);
err_axi:
memunmap(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;
pr_info("%s\n", __func__);
rc = pfe_remove(pfe);
iounmap(pfe->cbus_baseaddr);
memunmap(pfe->ddr_baseaddr);
platform_set_drvdata(pdev, NULL);
kfree(pfe);
return rc;
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
int pfe_platform_suspend(struct device *dev)
{
struct pfe *pfe = platform_get_drvdata(to_platform_device(dev));
struct net_device *netdev;
int i;
pfe->wake = 0;
for (i = 0; i < (NUM_GEMAC_SUPPORT); i++) {
netdev = pfe->eth.eth_priv[i]->ndev;
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) {
#if defined(LS1012A_PFE_RESET_WA)
pfe_hif_rx_idle(&pfe->hif);
#endif
pfe_ctrl_suspend(&pfe->ctrl);
pfe_firmware_exit(pfe);
pfe_hif_exit(pfe);
pfe_hif_lib_exit(pfe);
pfe_hw_exit(pfe);
}
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;
if (!pfe->wake) {
pfe_hw_init(pfe, 1);
pfe_hif_lib_init(pfe);
pfe_hif_init(pfe);
#if !defined(CONFIG_FSL_PPFE_UTIL_DISABLED)
util_enable();
#endif
tmu_enable(0xf);
class_enable();
pfe_ctrl_resume(&pfe->ctrl);
}
for (i = 0; i < (NUM_GEMAC_SUPPORT); i++) {
netdev = pfe->eth.eth_priv[i]->ndev;
if (pfe->mdio.mdio_priv[i]->mii_bus)
pfe_eth_mdio_reset(pfe->mdio.mdio_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 const struct of_device_id pfe_match[] = {
{
.compatible = "fsl,pfe",
},
{},
};
MODULE_DEVICE_TABLE(of, pfe_match);
static struct platform_driver pfe_platform_driver = {
.probe = pfe_platform_probe,
.remove = pfe_platform_remove,
.driver = {
.name = "pfe",
.of_match_table = pfe_match,
#ifdef CONFIG_PM
.pm = &pfe_platform_pm_ops,
#endif
},
};
module_platform_driver(pfe_platform_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PFE Ethernet driver");
MODULE_AUTHOR("NXP DNCPE");