Commit eaa008d3 authored by David S. Miller's avatar David S. Miller

Merge branch 'ave-fix-the-activation-issues-for-some-UniPhier-SoCs'

Kunihiko Hayashi says:

====================
ave: fix the activation issues for some UniPhier SoCs

This add the following stuffs to fix the activation issues and satisfy
requirements for AVE ethernet driver implemented on some UniPhier SoCs.

- Add support for additional necessary clocks and resets, because the kernel
  is stalled on Pro4 due to lack of them.

- Check whether the SoC supports the specified phy-mode

- Add DT property support indicating system controller that has the feature
  for configurating phy-mode including built-in phy on LD11.

v1: https://www.spinics.net/lists/netdev/msg494904.html

Changes since v1:
- Add 'Reviewed-by' lines
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 0263ea5c 57878f2f
......@@ -13,13 +13,25 @@ Required properties:
- reg: Address where registers are mapped and size of region.
- interrupts: Should contain the MAC interrupt.
- phy-mode: See ethernet.txt in the same directory. Allow to choose
"rgmii", "rmii", or "mii" according to the PHY.
"rgmii", "rmii", "mii", or "internal" according to the PHY.
The acceptable mode is SoC-dependent.
- phy-handle: Should point to the external phy device.
See ethernet.txt file in the same directory.
- clocks: A phandle to the clock for the MAC.
For Pro4 SoC, that is "socionext,uniphier-pro4-ave4",
another MAC clock, GIO bus clock and PHY clock are also required.
- clock-names: Should contain
- "ether", "ether-gb", "gio", "ether-phy" for Pro4 SoC
- "ether" for others
- resets: A phandle to the reset control for the MAC. For Pro4 SoC,
GIO bus reset is also required.
- reset-names: Should contain
- "ether", "gio" for Pro4 SoC
- "ether" for others
- socionext,syscon-phy-mode: A phandle to syscon with one argument
that configures phy mode. The argument is the ID of MAC instance.
Optional properties:
- resets: A phandle to the reset control for the MAC.
- local-mac-address: See ethernet.txt in the same directory.
Required subnode:
......@@ -34,8 +46,11 @@ Example:
interrupts = <0 66 4>;
phy-mode = "rgmii";
phy-handle = <&ethphy>;
clock-names = "ether";
clocks = <&sys_clk 6>;
reset-names = "ether";
resets = <&sys_rst 6>;
socionext,syscon-phy-mode = <&soc_glue 0>;
local-mac-address = [00 00 00 00 00 00];
mdio {
......
......@@ -14,6 +14,8 @@ if NET_VENDOR_SOCIONEXT
config SNI_AVE
tristate "Socionext AVE ethernet support"
depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF
depends on HAS_IOMEM
select MFD_SYSCON
select PHYLIB
---help---
Driver for gigabit ethernet MACs, called AVE, in the
......
......@@ -11,6 +11,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
#include <linux/mii.h>
#include <linux/module.h>
#include <linux/netdevice.h>
......@@ -18,6 +19,7 @@
#include <linux/of_mdio.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/types.h>
#include <linux/u64_stats_sync.h>
......@@ -197,8 +199,16 @@
#define AVE_INTM_COUNT 20
#define AVE_FORCE_TXINTCNT 1
/* SG */
#define SG_ETPINMODE 0x540
#define SG_ETPINMODE_EXTPHY BIT(1) /* for LD11 */
#define SG_ETPINMODE_RMII(ins) BIT(ins)
#define IS_DESC_64BIT(p) ((p)->data->is_desc_64bit)
#define AVE_MAX_CLKS 4
#define AVE_MAX_RSTS 2
enum desc_id {
AVE_DESCID_RX,
AVE_DESCID_TX,
......@@ -225,10 +235,6 @@ struct ave_desc_info {
struct ave_desc *desc; /* skb info related descriptor */
};
struct ave_soc_data {
bool is_desc_64bit;
};
struct ave_stats {
struct u64_stats_sync syncp;
u64 packets;
......@@ -245,11 +251,16 @@ struct ave_private {
int phy_id;
unsigned int desc_size;
u32 msg_enable;
struct clk *clk;
struct reset_control *rst;
int nclks;
struct clk *clk[AVE_MAX_CLKS];
int nrsts;
struct reset_control *rst[AVE_MAX_RSTS];
phy_interface_t phy_mode;
struct phy_device *phydev;
struct mii_bus *mdio;
struct regmap *regmap;
unsigned int pinmode_mask;
unsigned int pinmode_val;
/* stats */
struct ave_stats stats_rx;
......@@ -272,6 +283,14 @@ struct ave_private {
const struct ave_soc_data *data;
};
struct ave_soc_data {
bool is_desc_64bit;
const char *clock_names[AVE_MAX_CLKS];
const char *reset_names[AVE_MAX_RSTS];
int (*get_pinmode)(struct ave_private *priv,
phy_interface_t phy_mode, u32 arg);
};
static u32 ave_desc_read(struct net_device *ndev, enum desc_id id, int entry,
int offset)
{
......@@ -1153,20 +1172,30 @@ static int ave_init(struct net_device *ndev)
struct device_node *np = dev->of_node;
struct device_node *mdio_np;
struct phy_device *phydev;
int ret;
int nc, nr, ret;
/* enable clk because of hw access until ndo_open */
ret = clk_prepare_enable(priv->clk);
if (ret) {
dev_err(dev, "can't enable clock\n");
return ret;
for (nc = 0; nc < priv->nclks; nc++) {
ret = clk_prepare_enable(priv->clk[nc]);
if (ret) {
dev_err(dev, "can't enable clock\n");
goto out_clk_disable;
}
}
ret = reset_control_deassert(priv->rst);
if (ret) {
dev_err(dev, "can't deassert reset\n");
goto out_clk_disable;
for (nr = 0; nr < priv->nrsts; nr++) {
ret = reset_control_deassert(priv->rst[nr]);
if (ret) {
dev_err(dev, "can't deassert reset\n");
goto out_reset_assert;
}
}
ret = regmap_update_bits(priv->regmap, SG_ETPINMODE,
priv->pinmode_mask, priv->pinmode_val);
if (ret)
return ret;
ave_global_reset(ndev);
mdio_np = of_get_child_by_name(np, "mdio");
......@@ -1207,9 +1236,11 @@ static int ave_init(struct net_device *ndev)
out_mdio_unregister:
mdiobus_unregister(priv->mdio);
out_reset_assert:
reset_control_assert(priv->rst);
while (--nr >= 0)
reset_control_assert(priv->rst[nr]);
out_clk_disable:
clk_disable_unprepare(priv->clk);
while (--nc >= 0)
clk_disable_unprepare(priv->clk[nc]);
return ret;
}
......@@ -1217,13 +1248,16 @@ static int ave_init(struct net_device *ndev)
static void ave_uninit(struct net_device *ndev)
{
struct ave_private *priv = netdev_priv(ndev);
int i;
phy_disconnect(priv->phydev);
mdiobus_unregister(priv->mdio);
/* disable clk because of hw access after ndo_stop */
reset_control_assert(priv->rst);
clk_disable_unprepare(priv->clk);
for (i = 0; i < priv->nrsts; i++)
reset_control_assert(priv->rst[i]);
for (i = 0; i < priv->nclks; i++)
clk_disable_unprepare(priv->clk[i]);
}
static int ave_open(struct net_device *ndev)
......@@ -1520,6 +1554,7 @@ static int ave_probe(struct platform_device *pdev)
const struct ave_soc_data *data;
struct device *dev = &pdev->dev;
char buf[ETHTOOL_FWVERS_LEN];
struct of_phandle_args args;
phy_interface_t phy_mode;
struct ave_private *priv;
struct net_device *ndev;
......@@ -1527,8 +1562,9 @@ static int ave_probe(struct platform_device *pdev)
struct resource *res;
const void *mac_addr;
void __iomem *base;
const char *name;
int i, irq, ret;
u64 dma_mask;
int irq, ret;
u32 ave_id;
data = of_device_get_match_data(dev);
......@@ -1541,12 +1577,6 @@ static int ave_probe(struct platform_device *pdev)
dev_err(dev, "phy-mode not found\n");
return -EINVAL;
}
if ((!phy_interface_mode_is_rgmii(phy_mode)) &&
phy_mode != PHY_INTERFACE_MODE_RMII &&
phy_mode != PHY_INTERFACE_MODE_MII) {
dev_err(dev, "phy-mode is invalid\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
......@@ -1614,15 +1644,47 @@ static int ave_probe(struct platform_device *pdev)
u64_stats_init(&priv->stats_tx.syncp);
u64_stats_init(&priv->stats_rx.syncp);
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk)) {
ret = PTR_ERR(priv->clk);
goto out_free_netdev;
for (i = 0; i < AVE_MAX_CLKS; i++) {
name = priv->data->clock_names[i];
if (!name)
break;
priv->clk[i] = devm_clk_get(dev, name);
if (IS_ERR(priv->clk[i])) {
ret = PTR_ERR(priv->clk[i]);
goto out_free_netdev;
}
priv->nclks++;
}
for (i = 0; i < AVE_MAX_RSTS; i++) {
name = priv->data->reset_names[i];
if (!name)
break;
priv->rst[i] = devm_reset_control_get_shared(dev, name);
if (IS_ERR(priv->rst[i])) {
ret = PTR_ERR(priv->rst[i]);
goto out_free_netdev;
}
priv->nrsts++;
}
priv->rst = devm_reset_control_get_optional_shared(dev, NULL);
if (IS_ERR(priv->rst)) {
ret = PTR_ERR(priv->rst);
ret = of_parse_phandle_with_fixed_args(np,
"socionext,syscon-phy-mode",
1, 0, &args);
if (ret) {
netdev_err(ndev, "can't get syscon-phy-mode property\n");
goto out_free_netdev;
}
priv->regmap = syscon_node_to_regmap(args.np);
of_node_put(args.np);
if (IS_ERR(priv->regmap)) {
netdev_err(ndev, "can't map syscon-phy-mode\n");
ret = PTR_ERR(priv->regmap);
goto out_free_netdev;
}
ret = priv->data->get_pinmode(priv, phy_mode, args.args[0]);
if (ret) {
netdev_err(ndev, "invalid phy-mode setting\n");
goto out_free_netdev;
}
......@@ -1685,24 +1747,148 @@ static int ave_remove(struct platform_device *pdev)
return 0;
}
static int ave_pro4_get_pinmode(struct ave_private *priv,
phy_interface_t phy_mode, u32 arg)
{
if (arg > 0)
return -EINVAL;
priv->pinmode_mask = SG_ETPINMODE_RMII(0);
switch (phy_mode) {
case PHY_INTERFACE_MODE_RMII:
priv->pinmode_val = SG_ETPINMODE_RMII(0);
break;
case PHY_INTERFACE_MODE_MII:
case PHY_INTERFACE_MODE_RGMII:
priv->pinmode_val = 0;
break;
default:
return -EINVAL;
}
return 0;
}
static int ave_ld11_get_pinmode(struct ave_private *priv,
phy_interface_t phy_mode, u32 arg)
{
if (arg > 0)
return -EINVAL;
priv->pinmode_mask = SG_ETPINMODE_EXTPHY | SG_ETPINMODE_RMII(0);
switch (phy_mode) {
case PHY_INTERFACE_MODE_INTERNAL:
priv->pinmode_val = 0;
break;
case PHY_INTERFACE_MODE_RMII:
priv->pinmode_val = SG_ETPINMODE_EXTPHY | SG_ETPINMODE_RMII(0);
break;
default:
return -EINVAL;
}
return 0;
}
static int ave_ld20_get_pinmode(struct ave_private *priv,
phy_interface_t phy_mode, u32 arg)
{
if (arg > 0)
return -EINVAL;
priv->pinmode_mask = SG_ETPINMODE_RMII(0);
switch (phy_mode) {
case PHY_INTERFACE_MODE_RMII:
priv->pinmode_val = SG_ETPINMODE_RMII(0);
break;
case PHY_INTERFACE_MODE_RGMII:
priv->pinmode_val = 0;
break;
default:
return -EINVAL;
}
return 0;
}
static int ave_pxs3_get_pinmode(struct ave_private *priv,
phy_interface_t phy_mode, u32 arg)
{
if (arg > 1)
return -EINVAL;
priv->pinmode_mask = SG_ETPINMODE_RMII(arg);
switch (phy_mode) {
case PHY_INTERFACE_MODE_RMII:
priv->pinmode_val = SG_ETPINMODE_RMII(arg);
break;
case PHY_INTERFACE_MODE_RGMII:
priv->pinmode_val = 0;
break;
default:
return -EINVAL;
}
return 0;
}
static const struct ave_soc_data ave_pro4_data = {
.is_desc_64bit = false,
.clock_names = {
"gio", "ether", "ether-gb", "ether-phy",
},
.reset_names = {
"gio", "ether",
},
.get_pinmode = ave_pro4_get_pinmode,
};
static const struct ave_soc_data ave_pxs2_data = {
.is_desc_64bit = false,
.clock_names = {
"ether",
},
.reset_names = {
"ether",
},
.get_pinmode = ave_pro4_get_pinmode,
};
static const struct ave_soc_data ave_ld11_data = {
.is_desc_64bit = false,
.clock_names = {
"ether",
},
.reset_names = {
"ether",
},
.get_pinmode = ave_ld11_get_pinmode,
};
static const struct ave_soc_data ave_ld20_data = {
.is_desc_64bit = true,
.clock_names = {
"ether",
},
.reset_names = {
"ether",
},
.get_pinmode = ave_ld20_get_pinmode,
};
static const struct ave_soc_data ave_pxs3_data = {
.is_desc_64bit = false,
.clock_names = {
"ether",
},
.reset_names = {
"ether",
},
.get_pinmode = ave_pxs3_get_pinmode,
};
static const struct of_device_id of_ave_match[] = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment