Compare commits

...

18 Commits

Author SHA1 Message Date
7e0984e70f Tweak config-5.4 to admit the VSC848X phy 2021-02-16 03:42:47 +00:00
ad6a039808 Boot OF_I2C || O2_SPI dependency 2021-02-16 03:38:33 +00:00
34ee966cba Further cleanup 2021-02-16 03:27:39 +00:00
8d73f4e647 Fixup patches 2021-02-16 03:06:35 +00:00
4f8715bdbf Add further patches 2021-02-16 02:05:16 +00:00
d803c68954 Fixup spacing, again 2021-02-16 01:50:30 +00:00
b5d1f3cbc3 Fixup spacing 2021-02-16 01:47:43 +00:00
15977758ba Rebase patch on newer kernel 2021-02-16 01:44:01 +00:00
319b41636f Add vsc phy 2021-02-16 01:19:54 +00:00
99afb6ba21 oops 2021-02-10 23:57:52 +00:00
47890af657 Re-enable SFP on the snic10e :^) 2021-02-10 23:44:30 +00:00
0ea1b55c49 gotcha binch 2021-02-10 23:30:36 +00:00
6e303ca958 patch in even more debugs -- all for xaui init 2021-02-10 23:18:31 +00:00
c316f05f21 debug even deeper into cvmx_ipd_enable 2021-02-10 22:47:35 +00:00
a78fd8e4b7 Fix a typo in a cvmx patch 2021-02-10 22:17:14 +00:00
665d617447 do not redundantly patch 2021-02-10 22:16:53 +00:00
fea3bae7b1 Revert "maybe never init xaui"
This reverts commit d8c455f988.

let's try ethernet again!
2021-02-10 20:39:50 +00:00
b1ce6e7731 add debugs everywhere to octeon eth setup stuff 2021-02-10 20:39:03 +00:00
9 changed files with 1506 additions and 25 deletions

View File

@@ -159,6 +159,7 @@ CONFIG_HZ_250=y
CONFIG_HZ_PERIODIC=y
CONFIG_IMAGE_CMDLINE_HACK=y
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_OCTEON=y
CONFIG_INITRAMFS_SOURCE=""
CONFIG_IRQCHIP=y
@@ -205,8 +206,10 @@ CONFIG_MTD_PHYSMAP=y
CONFIG_MTD_SPI_NOR=y
CONFIG_MTD_SPI_NOR_USE_4K_SECTORS=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_NET_DEVLINK=y
CONFIG_NET_DSA=y
CONFIG_NET_FLOW_LIMIT=y
CONFIG_NET_SWITCHDEV=y
CONFIG_NLS=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
@@ -214,9 +217,9 @@ CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y
CONFIG_NR_CPUS=16
CONFIG_NR_CPUS_DEFAULT_64=y
CONFIG_NVMEM=y
CONFIG_OCTEON_ETHERNET=n
CONFIG_OCTEON_ETHERNET=y
CONFIG_OCTEON_ILM=y
CONFIG_OCTEON_MGMT_ETHERNET=n
CONFIG_OCTEON_MGMT_ETHERNET=y
CONFIG_OCTEON_USB=y
CONFIG_OCTEON_WDT=y
CONFIG_OF=y
@@ -227,22 +230,25 @@ CONFIG_OF_GPIO=y
CONFIG_OF_IRQ=y
CONFIG_OF_KOBJ=y
CONFIG_OF_MDIO=y
CONFIG_OF_MEMORY_ACCESSOR=y
CONFIG_OF_NET=y
CONFIG_PADATA=y
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_PCI=y
CONFIG_PCI_DOMAINS=y
CONFIG_PCI_DRIVERS_LEGACY=y
CONFIG_PCIEAER=y
CONFIG_PCIEPORTBUS=y
CONFIG_PCI_DOMAINS=y
CONFIG_PCI_DRIVERS_LEGACY=y
CONFIG_PERF_USE_VMALLOC=y
CONFIG_PGTABLE_LEVELS=3
CONFIG_PHYLIB=y
CONFIG_PHYLINK=y
CONFIG_PHYS_ADDR_T_64BIT=y
CONFIG_POSIX_MQUEUE=y
CONFIG_POSIX_MQUEUE_SYSCTL=y
CONFIG_QUEUED_RWLOCKS=y
CONFIG_QUEUED_SPINLOCKS=y
CONFIG_RAS=y
CONFIG_RCU_NEED_SEGCBLIST=y
CONFIG_RCU_STALL_COMMON=y
CONFIG_RELAY=y
@@ -260,6 +266,7 @@ CONFIG_SPARSEMEM=y
CONFIG_SPARSEMEM_STATIC=y
CONFIG_SPI=y
CONFIG_SPI_MASTER=y
CONFIG_SPI_MEM=y
CONFIG_SPI_OCTEON=y
CONFIG_SRCU=y
CONFIG_SWIOTLB=y
@@ -295,6 +302,7 @@ CONFIG_USE_OF=y
CONFIG_VFAT_FS=y
CONFIG_VITESSE_PHY=y
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_VSC848X_PHY=y
CONFIG_WATCHDOG_CORE=y
CONFIG_WEAK_ORDERING=y
CONFIG_XPS=y

View File

@@ -116,7 +116,6 @@
reg = <0x11800 0x00001800 0x0 0x40>;
status = "disabled";
mphyA: ethernet-phy-nexus@A {
status = "disabled";
reg = <0>;
/* The Vitesse VSC8488 is a dual-PHY where
* some of the configuration is common across
@@ -133,7 +132,6 @@
reset = <&gpio 17 0>;
phy0: ethernet-phy@0 {
status = "disabled";
/* Absolute address */
reg = <0>;
compatible = "vitesse,vsc8488", "ethernet-phy-ieee802.3-c45";
@@ -350,7 +348,6 @@
};
phy1: ethernet-phy@1 {
status = "disabled";
/* Absolute address */
reg = <0x1>;
compatible = "vitesse,vsc8488", "ethernet-phy-ieee802.3-c45";
@@ -567,7 +564,6 @@
};
};
mphyB: ethernet-phy-nexus@B {
status = "disabled";
reg = <0>;
/* The TI TLK10232 is a dual-PHY where
* some of the configuration is common across
@@ -584,7 +580,6 @@
reset = <&gpio 17 2>;
phy11: ethernet-phy@0 {
status = "disabled";
/* Absolute address */
reg = <0>;
compatible = "ti,tlk10232", "ethernet-phy-ieee802.3-c45";
@@ -608,7 +603,6 @@
};
phy10: ethernet-phy@1 {
status = "disabled";
/* Absolute address */
reg = <0x1>;
compatible = "ti,tlk10232", "ethernet-phy-ieee802.3-c45";

View File

@@ -0,0 +1,829 @@
From: Abhishek Paliwal <abhishek.paliwal@aricent.com>
Date: Fri, 13 Feb 2015 15:04:55 +0530
Subject: netdev/phy: Add driver for Vitesse vsc848x single, dual and quad 10G
phys
From: David Daney <david.daney@cavium.com>
These phys implement the standard IEEE 802.3 clause 45 registers but
require additional configuration. Some of these registers in the multi-phy
devices are shared among all phys such as the GPIO registers.
Additionally, this PHY does not automatically access the SFP+ serial EEPROM so
it is up to the PHY driver to parse it and change certain parameters in the
PHY according to the type of module installed and the length of the cable, if
copper.
This module has support for the vsc8488, vsc8486 and vsc8484 Vitesse devices
but thus far has only been tested with the vsc8488 dual PHY.
netdev/phy: Clean up structure names in vsc848x.c
Cut-and-paste snafu left some bad names, no functional change.
Signed-off-by: David Daney <david.daney@cavium.com>
Signed-off-by: Aaron Williams <aaron.williams@cavium.com>
Signed-off-by: Leonid Rosenboim <lrosenboim@caviumnetworks.com>
Signed-off-by: Abhishek Paliwal <abhishek.paliwal@aricent.com>
[Rebased on kernel 5.4 by Martin Kennedy <hurricos@gmail.com>]
Signed-off-by: Martin Kennedy <hurricos@gmail.com>
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -54,6 +54,13 @@ config VITESSE_PHY
---help---
Currently supports the vsc8244
+config VSC848X_PHY
+ tristate "Drivers for the Vitesse 10G PHYs"
+ depends on OF_MEMORY_ACCESSOR
+ help
+ Driver for Vitesse vsc848x single, dual and quad 10G PHY devices.
+ Currently supports the vsc8488, vsc8486 and vsc8484 chips
+
config SMSC_PHY
tristate "Drivers for SMSC PHYs"
---help---
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -108,4 +108,5 @@
obj-$(CONFIG_STE10XP) += ste10Xp.o
obj-$(CONFIG_TERANETICS_PHY) += teranetics.o
obj-$(CONFIG_VITESSE_PHY) += vitesse.o
+obj-$(CONFIG_VSC848X_PHY) += vsc848x.o
obj-$(CONFIG_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
--- /dev/null
+++ b/drivers/net/phy/vsc848x.c
@@ -0,0 +1,774 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012 Cavium, Inc.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/of_mdio.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/phy.h>
+#include <linux/memory.h>
+#include <linux/mutex.h>
+#include <linux/of_memory_accessor.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+
+#define PMD_RX_SIGNAL_DETECT (MII_ADDR_C45 | 0x01000a)
+#define PMA_TXOUTCTRL2 (MII_ADDR_C45 | 0x018014)
+#define EDC_EYE_QUALITY (MII_ADDR_C45 | 0x018034)
+/* EDC Firmware State Machine Status and Lib Force
+ * 15: library force enable
+ * 14:8 - Library number
+ * 7:5 - N/A
+ * 4 - State force
+ * 3:0 - FW state (1=reset, 2=wait for alarm to clear, 3 = convergence,
+ * 4 = tracking, 5 = freeze)
+ */
+#define EDC_FW_SM_STATUS (MII_ADDR_C45 | 0x018036)
+
+#define BASER_PCS_STATUS (MII_ADDR_C45 | 0x030020)
+#define XGXS_LANE_STATUS (MII_ADDR_C45 | 0x040018)
+#define EWIS_INTR_PEND1 (MII_ADDR_C45 | 0x02EE00)
+#define EWIS_INTR_MASKA_1 (MII_ADDR_C45 | 0x02EE01)
+#define EWIS_INTR_MASKB_1 (MII_ADDR_C45 | 0x02EE02)
+#define EWIS_INTR_STAT2 (MII_ADDR_C45 | 0x02EE03)
+#define EWIS_INTR_PEND2 (MII_ADDR_C45 | 0x02EE04)
+#define EWIS_INTR_MASKA_2 (MII_ADDR_C45 | 0x02EE05)
+#define EWIS_INTR_MASKB_2 (MII_ADDR_C45 | 0x02EE06)
+#define EWIS_FAULT_MASK (MII_ADDR_C45 | 0x02EE07)
+#define EWIS_INTR_PEND3 (MII_ADDR_C45 | 0x02EE08)
+#define EWIS_INTR_MASKA_3 (MII_ADDR_C45 | 0x02EE09)
+#define EWIS_INTR_MASKB_3 (MII_ADDR_C45 | 0x02EE0A)
+
+/* Device ID
+ * 15:0 - device ID
+ */
+#define GBL_DEVICE_ID (MII_ADDR_C45 | 0x1e0000)
+/* Device revision
+ * 15:04 - reserved
+ * 03:00 - revision ID
+ */
+#define GBL_DEVICE_REVISION (MII_ADDR_C45 | 0x1e0001)
+/* Block Level Software Reset
+ * 15:14 - reserved
+ * 13: - software reset EDC 1 (1 = reset, autoclears)
+ * 12: - software reset EDC 0 (1 = reset, autoclears)
+ * 11:10 - reserved
+ * 09: - Software reset channel 1 (1 = reset, autoclears)
+ * 08: - Software reset channel 0 (1 = reset, autoclears)
+ * 07: - Microprocessor reset (0 = normal operation, 1 = reset)
+ * 06: - Software reset BIU (1 = reset, autoclears)
+ * 05: - Software reset TWS slave (1 = reset, autoclears)
+ * 04: - Software reset TWS master (1 = reset, autoclears)
+ * 03: - Software reset MDIO (1 = reset, autoclears)
+ * 02: - Software reset UART (1 = reset, autoclears)
+ * 01: - Global register reset (1 = reset, autoclears)
+ * 00: - Software reset chip (1 = reset, autoclears)
+ */
+#define GBL_BLOCK_LVL_SW_RESET (MII_ADDR_C45 | 0x1e0002)
+#define GBL_GPIO_0_CONFIG1_STATUS (MII_ADDR_C45 | 0x1e0100)
+#define GBL_GPIO_0_CONFIG2 (MII_ADDR_C45 | 0x1e0101)
+#define GBL_DEVICE_ID (MII_ADDR_C45 | 0x1e0000)
+#define GBL_FW_CHECKSUM (MII_ADDR_C45 | 0x1e7fe0)
+#define GBL_FW_WATCHDOG (MII_ADDR_C45 | 0x1e7fe1)
+#define GBL_FW_VERSION (MII_ADDR_C45 | 0x1e7fe2)
+#define GBL_FW_VAR_ACC_CTRL (MII_ADDR_C45 | 0x1e7fe3)
+#define GBL_FW_VAR_ACC_DATA (MII_ADDR_C45 | 0x1e7fe4)
+
+/* The Vitesse VSC848X series are 10G PHYs.
+ *
+ * Some of these devices contain multiple PHYs in a single package and
+ * some features are controlled by a global set of registers shared between
+ * all of the PHY devices. Because of this a nexus is used to handle all
+ * of the PHYs on the same device.
+ *
+ * Unlike some PHY devices, it is up to the driver to read the SFP module
+ * serial EEPROM in order to put the PHY into the right mode. The VSC848X
+ * does not provide an I2C interface so the PHY driver relies on the
+ * external AT24 I2C EEPROM driver to read the module whenever it is inserted.
+ *
+ */
+
+/* Enable LOPC detection (see 0x5B for target state)
+ * 15:12 - channel 3
+ * 11:08 - channel 2
+ * 07:04 - channel 1
+ * 03:00 - channel 0
+ * 1 = enable (default), 0 = disable
+ */
+#define FW_VAR_ENABLE_LOPC 0x58
+/* While in tracking mode, go to this state in response to LOPC assertion
+ * 1 = reset, 2 = wait (default), 3 = converging, 4 = tracking, 5 = freeze
+ */
+#define FW_VAR_LOPC_ASSERT_MODE 0x5B
+/* While in freeze mode, enable state transition upon deassertion of LOPC (see
+ * 0x61 for target state)
+ * 1 - reset, 2 = wait, 3 = converging, 4 = tracking (default), 5 = freeze
+ */
+#define FW_VAR_FREEZE_DEASSERT_MODE 0x61
+/* Current functional mode
+ * See VITESSE_FUNC_MODE_XXX below for values
+ * NOTE: When the firmware is done servicing the mode change request, bit 4
+ * will be set to 1.
+ */
+#define FW_VAR_FUNCTIONAL_MODE 0x94
+/* Current state of graded SPSA process
+ * 3: channel 3
+ * 2: channel 2
+ * 1: channel 1
+ * 0: channel 0
+ * 1 = busy, 2 = done
+ */
+#define FW_VAR_GRADED_SPSA_STATE 0x95
+/* BerScore at start of SPSA cycle */
+#define FW_VAR_BERSCORE_START 0x96
+/* BerScore at end of SPSA cycle */
+#define FW_VAR_BERSCORE_END 0x97
+/* Enable/Disable aggressive track phase on entering tracking state
+ * 15:12 - channel 3
+ * 11:08 - channel 2
+ * 07:04 - channel 1
+ * 03:00 - channel 0
+ * 0 = disable, 1 = enable (default)
+ */
+#define FW_VAR_AGG_TRACKING 0xAF
+
+/* Modes for the PHY firmware */
+#define VITESSE_FUNC_MODE_LIMITING 2 /* Optical */
+#define VITESSE_FUNC_MODE_COPPER 3 /* Copper */
+#define VITESSE_FUNC_MODE_LINEAR 4
+#define VITESSE_FUNC_MODE_KR 5
+#define VITESSE_FUNC_MODE_ZR 7
+#define VITESSE_FUNC_MODE_1G 8
+
+
+struct vsc848x_nexus_mdiobus {
+ struct mii_bus *mii_bus;
+ struct mii_bus *parent_mii_bus;
+ int reg_offset;
+ struct mutex lock; /* Lock used for global register sequences */
+ int phy_irq[PHY_MAX_ADDR];
+};
+
+struct vsc848x_phy_info {
+ int sfp_conn; /* Module connected? */
+ int tx_en_gpio; /* GPIO that enables transmit */
+ int mod_abs_gpio; /* Module Absent GPIO line */
+ int tx_fault_gpio; /* TX Fault GPIO line */
+ int inta_gpio, intb_gpio; /* Interrupt GPIO line (output) */
+ uint8_t mode; /* Mode for module */
+ uint8_t channel; /* channel in multi-phy devices */
+ struct device_node *sfp_node; /* EEPROM NODE for SFP */
+ struct memory_accessor *macc; /* memory access routines for EEPROM */
+ struct vsc848x_nexus_mdiobus *nexus; /* Nexus for lock */
+};
+
+/**
+ * Maps GPIO lines to the global GPIO config registers.
+ *
+ * Please see the data sheet since the configuration for each GPIO line is
+ * different.
+ */
+static const struct {
+ uint32_t config1_status_reg;
+ uint32_t config2_reg;
+} vcs848x_gpio_to_reg[12] = {
+ { (MII_ADDR_C45 | 0x1e0100), (MII_ADDR_C45 | 0x1e0101) }, /* 0 */
+ { (MII_ADDR_C45 | 0x1e0102), (MII_ADDR_C45 | 0x1e0103) }, /* 1 */
+ { (MII_ADDR_C45 | 0x1e0104), (MII_ADDR_C45 | 0x1e0105) }, /* 2 */
+ { (MII_ADDR_C45 | 0x1e0106), (MII_ADDR_C45 | 0x1e0107) }, /* 3 */
+ { (MII_ADDR_C45 | 0x1e0108), (MII_ADDR_C45 | 0x1e0109) }, /* 4 */
+ { (MII_ADDR_C45 | 0x1e010A), (MII_ADDR_C45 | 0x1e010B) }, /* 5 */
+ { (MII_ADDR_C45 | 0x1e0124), (MII_ADDR_C45 | 0x1e0125) }, /* 6 */
+ { (MII_ADDR_C45 | 0x1e0126), (MII_ADDR_C45 | 0x1e0127) }, /* 7 */
+ { (MII_ADDR_C45 | 0x1e0128), (MII_ADDR_C45 | 0x1e0129) }, /* 8 */
+ { (MII_ADDR_C45 | 0x1e012a), (MII_ADDR_C45 | 0x1e012b) }, /* 9 */
+ { (MII_ADDR_C45 | 0x1e012c), (MII_ADDR_C45 | 0x1e012d) }, /* 10 */
+ { (MII_ADDR_C45 | 0x1e012e), (MII_ADDR_C45 | 0x1e012f) }, /* 11 */
+};
+
+static int vsc848x_probe(struct phy_device *phydev)
+{
+ struct vsc848x_phy_info *dev_info;
+ int ret;
+
+ dev_info = devm_kzalloc(&phydev->dev, sizeof(*dev_info), GFP_KERNEL);
+ if (dev_info == NULL)
+ return -ENOMEM;
+
+ phydev->priv = dev_info;
+ dev_info->mode = VITESSE_FUNC_MODE_LIMITING; /* Default to optical */
+ phydev->priv = dev_info;
+ dev_info->nexus = phydev->bus->priv;
+
+ ret = of_property_read_u32(phydev->dev.of_node, "mod_abs",
+ &dev_info->mod_abs_gpio);
+ if (ret) {
+ dev_err(&phydev->dev, "%s has invalid mod_abs address\n",
+ phydev->dev.of_node->full_name);
+ return ret;
+ }
+
+ ret = of_property_read_u32(phydev->dev.of_node, "tx_fault",
+ &dev_info->tx_fault_gpio);
+ if (ret) {
+ dev_err(&phydev->dev, "%s has invalid tx_fault address\n",
+ phydev->dev.of_node->full_name);
+ return ret;
+ }
+
+ ret = of_property_read_u32(phydev->dev.of_node, "inta",
+ &dev_info->inta_gpio);
+ if (ret)
+ dev_info->inta_gpio = -1;
+
+ ret = of_property_read_u32(phydev->dev.of_node, "intb",
+ &dev_info->intb_gpio);
+ if (ret)
+ dev_info->intb_gpio = -1;
+
+ ret = of_property_read_u32(phydev->dev.of_node, "txon",
+ &dev_info->tx_en_gpio);
+ if (ret) {
+ dev_err(&phydev->dev, "%s has invalid txon gpio address\n",
+ phydev->dev.of_node->full_name);
+ return -ENXIO;
+ }
+
+ dev_info->sfp_node = of_parse_phandle(phydev->dev.of_node,
+ "sfp-eeprom", 0);
+ if (!dev_info->sfp_node) {
+ dev_err(&phydev->dev, "%s has invalid sfp-eeprom node\n",
+ phydev->dev.of_node->full_name);
+ return -ENXIO;
+ }
+
+ dev_info->macc = of_memory_accessor_get(dev_info->sfp_node);
+
+ ret = phy_read(phydev, GBL_DEVICE_ID);
+ if (ret < 0) {
+ dev_err(&phydev->dev, "%s error reading PHY\n",
+ phydev->dev.of_node->full_name);
+ return ret;
+ }
+
+ /* Check how many devices are in the package to figure out the channel
+ * number.
+ */
+ switch (ret) {
+ case 0x8487: /* Single */
+ case 0x8486:
+ dev_info->channel = 0;
+ break;
+ case 0x8488: /* Dual */
+ dev_info->channel = phydev->addr & 1;
+ break;
+ case 0x8484: /* Quad */
+ dev_info->channel = phydev->addr & 3;
+ break;
+ default:
+ dev_err(&phydev->dev, "%s Unknown Vitesse PHY model %04x\n",
+ phydev->dev.of_node->full_name, ret);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void vsc848x_remove(struct phy_device *phydev)
+{
+ struct vsc848x_phy_info *dev_info = phydev->priv;
+
+ dev_info(&phydev->dev, "%s Exiting\n", phydev->dev.of_node->full_name);
+
+ of_memory_accessor_put(dev_info->sfp_node);
+
+ kfree(dev_info);
+}
+
+static int vsc848x_config_init(struct phy_device *phydev)
+{
+ phydev->supported = SUPPORTED_10000baseR_FEC;
+ phydev->advertising = ADVERTISED_10000baseR_FEC;
+ phydev->state = PHY_NOLINK;
+
+ return 0;
+}
+
+static int vsc848x_config_aneg(struct phy_device *phydev)
+{
+ return -EINVAL;
+}
+
+static int vsc848x_write_global_var(struct phy_device *phydev, uint8_t channel,
+ uint8_t addr, uint16_t value)
+{
+ struct vsc848x_phy_info *dev_info = phydev->priv;
+ int timeout = 1000;
+ int ret = 0;
+
+ mutex_lock(&(dev_info->nexus->lock));
+
+ /* Wait for firmware download to complete */
+ timeout = 100000;
+ do {
+ ret = phy_read(phydev, MII_ADDR_C45 | 0x1e7fe0);
+ if (ret < 0)
+ goto error;
+ if (ret == 3)
+ break;
+ udelay(100);
+ } while (timeout-- > 0);
+ if (timeout <= 0) {
+ dev_err(&phydev->dev, "%s Timeout waiting for PHY firmware to load\n",
+ phydev->dev.of_node->full_name);
+ ret = -EIO;
+ goto error;
+ }
+
+ do {
+ ret = phy_read(phydev, (MII_ADDR_C45 | 0x1e7fe3));
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ break;
+ mdelay(1);
+ } while (timeout-- > 0);
+ if (timeout <= 0) {
+ dev_err(&phydev->dev, "%s timed out waiting to write global\n",
+ phydev->dev.of_node->full_name);
+ ret = -EIO;
+ goto error;
+ }
+ ret = phy_write(phydev, (MII_ADDR_C45 | 0x1e7fe4), value);
+ if (ret < 0)
+ goto error;
+
+ ret = phy_write(phydev, (MII_ADDR_C45 | 0x1e7fe3),
+ 0x8000 | ((channel & 3) << 8) | addr);
+ if (ret < 0)
+ goto error;
+
+ /* Wait for value to be written */
+ do {
+ ret = phy_read(phydev, (MII_ADDR_C45 | 0x1e7fe3));
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ break;
+ mdelay(1);
+ } while (timeout-- > 0);
+ if (timeout <= 0) {
+ dev_err(&phydev->dev, "%s timed out waiting to write global\n",
+ phydev->dev.of_node->full_name);
+ ret = -EIO;
+ goto error;
+ }
+ ret = 0;
+
+error:
+ mutex_unlock(&(dev_info->nexus->lock));
+
+ return ret;
+}
+
+/**
+ * Dumps out the contents of the SFP EEPROM when errors are detected
+ *
+ * @param eeprom - contents of SFP+ EEPROM
+ */
+static void dump_sfp_eeprom(const uint8_t eeprom[64])
+{
+ int addr = 0;
+ int i;
+ char line[17];
+ line[16] = '\0';
+
+ pr_info("SFP+ EEPROM contents:\n");
+ while (addr < 64) {
+ pr_info(" %02x: ", addr);
+ for (i = 0; i < 16; i++)
+ pr_cont("%02x ", eeprom[addr + i]);
+ for (i = 0; i < 16; i++) {
+ if (!isprint(eeprom[addr + i]) ||
+ eeprom[addr + i] >= 0x80)
+ line[i] = '.';
+ else
+ line[i] = eeprom[addr + i];
+ }
+ pr_cont(" %s\n", line);
+ addr += 16;
+ }
+ pr_info("\n");
+}
+
+/**
+ * Read the SFP+ module EEPROM and program the Vitesse PHY accordingly.
+ *
+ * @param phydev - Phy device
+ *
+ * @returns 0 for success, error otherwise.
+ */
+static int vsc848x_read_sfp(struct phy_device *phydev)
+{
+ struct vsc848x_phy_info *dev_info = phydev->priv;
+ uint8_t sfp_buffer[64];
+ ssize_t size;
+ uint8_t csum;
+ uint8_t mode = VITESSE_FUNC_MODE_LIMITING;
+ const char *mode_str = "Unknown";
+ int i;
+ int ret = 0;
+
+ /* For details on the SFP+ EEPROM contents see the SFF-8472
+ * Diagnostic Monitoring Interface for Optical Transceivers.
+ *
+ * This is based on revision 11.1, October 26, 2012.
+ */
+ if (!dev_info->macc) {
+ dev_info->macc = of_memory_accessor_get(dev_info->sfp_node);
+ if (!dev_info->macc) {
+ dev_info(&phydev->dev,
+ "Could not connect to sfp memory accessor %s\n",
+ dev_info->sfp_node->full_name);
+ return -ENODEV;
+ }
+ }
+ size = dev_info->macc->read(dev_info->macc,
+ (char *)sfp_buffer, 0, sizeof(sfp_buffer));
+ if (size != sizeof(sfp_buffer)) {
+ dev_err(&phydev->dev, "%s cannot read SFP module EEPROM\n",
+ phydev->dev.of_node->full_name);
+ return -ENODEV;
+ }
+
+ /* Validate SFP checksum */
+ csum = 0;
+ for (i = 0; i < 63; i++)
+ csum += sfp_buffer[i];
+ if (csum != sfp_buffer[63]) {
+ dev_err(&phydev->dev, "%s SFP EEPROM checksum bad, calculated 0x%02x, should be 0x%02x\n",
+ phydev->dev.of_node->full_name, csum, sfp_buffer[63]);
+ dump_sfp_eeprom(sfp_buffer);
+ return -ENXIO;
+ }
+
+ /* Make sure it's a SFP or SFP+ module */
+ if (sfp_buffer[0] != 3) {
+ dev_err(&phydev->dev, "%s module is not SFP or SFP+\n",
+ phydev->dev.of_node->full_name);
+ dump_sfp_eeprom(sfp_buffer);
+ return -ENXIO;
+ }
+
+ /* Check connector type */
+ switch (sfp_buffer[2]) {
+ case 0x01: /* SC */
+ mode = VITESSE_FUNC_MODE_LIMITING;
+ break;
+ case 0x07: /* LC */
+ mode = VITESSE_FUNC_MODE_LIMITING;
+ break;
+ case 0x0B: /* Optical pigtail */
+ mode = VITESSE_FUNC_MODE_LIMITING;
+ break;
+ case 0x21: /* Copper pigtail */
+ case 0x22: /* RJ45 */
+ mode = VITESSE_FUNC_MODE_COPPER;
+ break;
+ default:
+ dev_err(&phydev->dev, "%s Unknown Connector Type 0x%x\n",
+ phydev->dev.of_node->full_name, sfp_buffer[2]);
+ dump_sfp_eeprom(sfp_buffer);
+ return -EINVAL;
+ }
+
+ if (mode == VITESSE_FUNC_MODE_LIMITING) {
+ if (mode_str[3] & 0x10)
+ mode_str = "10GBase-SR";
+ else if (mode_str[3] & 0x20)
+ mode_str = "10GBase-LR";
+ else if (mode_str[3] & 0x40)
+ mode_str = "10GBase-LRM";
+ else if (mode_str[3] & 0x80)
+ mode_str = "10GBase-ER";
+ else
+ dev_err(&phydev->dev, "%s unknown SFP compatibility\n"
+ "type ID: 0x%02x, extended ID: 0x%02x, Connector type code: 0x%02x\n"
+ "Transceiver compatibility code: (%02x) %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ phydev->dev.of_node->full_name, sfp_buffer[0],
+ sfp_buffer[1], sfp_buffer[2], sfp_buffer[36],
+ sfp_buffer[3], sfp_buffer[4], sfp_buffer[5],
+ sfp_buffer[6], sfp_buffer[7], sfp_buffer[8],
+ sfp_buffer[9], sfp_buffer[10]);
+ } else if (mode == VITESSE_FUNC_MODE_COPPER) {
+ if (sfp_buffer[8] & 0x4) {
+ mode_str = "10G Passive Copper";
+ } else if (sfp_buffer[8] & 0x8) {
+ mode_str = "10G Active Copper";
+ mode = VITESSE_FUNC_MODE_LIMITING;
+ } else {
+ dev_err(&phydev->dev, "%s Unknown SFP+ copper cable capability 0x%02x\n"
+ "Transceiver compatibility code: (%02x) %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ phydev->dev.of_node->full_name, sfp_buffer[8],
+ sfp_buffer[36], sfp_buffer[3], sfp_buffer[4],
+ sfp_buffer[5], sfp_buffer[6], sfp_buffer[7],
+ sfp_buffer[8], sfp_buffer[9], sfp_buffer[10]);
+ return -EINVAL;
+ }
+ } else {
+ dev_err(&phydev->dev, "%s Unsupported phy mode %d\n",
+ phydev->dev.of_node->full_name, mode);
+ dump_sfp_eeprom(sfp_buffer);
+ }
+
+ vsc848x_write_global_var(phydev, dev_info->channel, 0x94, mode);
+
+ /* Adjust PMA_TXOUTCTRL2 based on cable length. Vitesse recommends
+ * 0x1606 for copper cable lengths 5M and longer.
+ *
+ * The default value is 0x1300.
+ */
+ if (mode == VITESSE_FUNC_MODE_COPPER) {
+ if (sfp_buffer[18] >= 5)
+ ret = phy_write(phydev, PMA_TXOUTCTRL2, 0x1606);
+ else
+ ret = phy_write(phydev, PMA_TXOUTCTRL2, 0x1300);
+ if (ret)
+ return ret;
+ }
+
+ /* Reset the state machine */
+ ret = phy_write(phydev, MII_ADDR_C45 | 0x18034, 0x11);
+
+ dev_info(&phydev->dev, "%s configured for %s\n",
+ phydev->dev.of_node->full_name, mode_str);
+
+ return ret;
+}
+
+static int vsc848x_read_status(struct phy_device *phydev)
+{
+ struct vsc848x_phy_info *dev_info = phydev->priv;
+ int rx_signal_detect;
+ int pcs_status;
+ int xgxs_lane_status;
+ int value;
+ int sfp_conn;
+ int ret;
+
+ /* Check if a module is plugged in */
+ value = phy_read(phydev, vcs848x_gpio_to_reg[dev_info->mod_abs_gpio]
+ .config1_status_reg);
+ if (value < 0)
+ return value;
+
+ sfp_conn = !(value & 0x400);
+
+ if (sfp_conn != dev_info->sfp_conn) {
+ /* We detect a module being plugged in */
+ if (sfp_conn) {
+ ret = vsc848x_read_sfp(phydev);
+ if (ret < 0)
+ goto no_link;
+ dev_info->sfp_conn = sfp_conn;
+ } else {
+ dev_info(&phydev->dev, "%s module unplugged\n",
+ phydev->dev.of_node->full_name);
+ dev_info->sfp_conn = sfp_conn;
+ goto no_link;
+ }
+ }
+
+ rx_signal_detect = phy_read(phydev, PMD_RX_SIGNAL_DETECT);
+ if (rx_signal_detect < 0)
+ return rx_signal_detect;
+
+ if ((rx_signal_detect & 1) == 0)
+ goto no_link;
+
+ pcs_status = phy_read(phydev, BASER_PCS_STATUS);
+ if (pcs_status < 0)
+ return pcs_status;
+
+ if ((pcs_status & 1) == 0)
+ goto no_link;
+
+ xgxs_lane_status = phy_read(phydev, XGXS_LANE_STATUS);
+ if (xgxs_lane_status < 0)
+ return xgxs_lane_status;
+
+ if ((xgxs_lane_status & 0x1000) == 0)
+ goto no_link;
+
+ phydev->speed = 10000;
+ phydev->link = 1;
+ phydev->duplex = 1;
+ return 0;
+no_link:
+ phydev->link = 0;
+ return 0;
+}
+
+static struct of_device_id vsc848x_match[] = {
+ {
+ .compatible = "vitesse,vsc8488",
+ },
+ {
+ .compatible = "vitesse,vsc8486",
+ },
+ {
+ .compatible = "vitesse,vsc8484",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, vsc848x_match);
+
+static struct phy_driver vsc848x_phy_driver = {
+ .phy_id = 0x00070400,
+ .phy_id_mask = 0xfffffff0,
+ .name = "Vitesse VSC848X",
+ .config_init = vsc848x_config_init,
+ .probe = vsc848x_probe,
+ .remove = vsc848x_remove,
+ .config_aneg = vsc848x_config_aneg,
+ .read_status = vsc848x_read_status,
+ .driver = {
+ .owner = THIS_MODULE,
+ .of_match_table = vsc848x_match,
+ },
+};
+
+/* Phy nexus support below. */
+
+static int vsc848x_nexus_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+ struct vsc848x_nexus_mdiobus *p = bus->priv;
+ return p->parent_mii_bus->read(p->parent_mii_bus,
+ phy_id + p->reg_offset,
+ regnum);
+}
+
+static int vsc848x_nexus_write(struct mii_bus *bus, int phy_id,
+ int regnum, u16 val)
+{
+ struct vsc848x_nexus_mdiobus *p = bus->priv;
+ return p->parent_mii_bus->write(p->parent_mii_bus,
+ phy_id + p->reg_offset,
+ regnum, val);
+}
+
+static int vsc848x_nexus_probe(struct platform_device *pdev)
+{
+ struct vsc848x_nexus_mdiobus *bus;
+ const char *bus_id;
+ int len;
+ int err = 0;
+
+ bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return -ENOMEM;
+
+ bus->parent_mii_bus = container_of(pdev->dev.parent,
+ struct mii_bus, dev);
+
+ /* The PHY nexux must have a reg property in the range [0-31] */
+ err = of_property_read_u32(pdev->dev.of_node, "reg", &bus->reg_offset);
+ if (err) {
+ dev_err(&pdev->dev, "%s has invalid PHY address\n",
+ pdev->dev.of_node->full_name);
+ return err;
+ }
+
+ bus->mii_bus = mdiobus_alloc();
+ if (!bus->mii_bus)
+ return -ENOMEM;
+
+ bus->mii_bus->priv = bus;
+ bus->mii_bus->irq = bus->phy_irq;
+ bus->mii_bus->name = "vsc848x_nexus";
+ bus_id = bus->parent_mii_bus->id;
+ len = strlen(bus_id);
+ if (len > MII_BUS_ID_SIZE - 4)
+ bus_id += len - (MII_BUS_ID_SIZE - 4);
+ snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%s:%02x",
+ bus_id, bus->reg_offset);
+ bus->mii_bus->parent = &pdev->dev;
+
+ bus->mii_bus->read = vsc848x_nexus_read;
+ bus->mii_bus->write = vsc848x_nexus_write;
+ mutex_init(&bus->lock);
+
+ dev_set_drvdata(&pdev->dev, bus);
+
+ err = of_mdiobus_register(bus->mii_bus, pdev->dev.of_node);
+ if (err) {
+ dev_err(&pdev->dev, "Error registering with device tree\n");
+ goto fail_register;
+ }
+
+ return 0;
+
+fail_register:
+ dev_err(&pdev->dev, "Failed to register\n");
+ mdiobus_free(bus->mii_bus);
+ return err;
+}
+
+static int vsc848x_nexus_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct of_device_id vsc848x_nexus_match[] = {
+ {
+ .compatible = "vitesse,vsc8488-nexus",
+ },
+ {
+ .compatible = "vitesse,vsc8486-nexus",
+ },
+ {
+ .compatible = "vitesse,vsc8484-nexus",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, vsc848x_nexus_match);
+
+static struct platform_driver vsc848x_nexus_driver = {
+ .driver = {
+ .name = "vsc848x-nexus",
+ .owner = THIS_MODULE,
+ .of_match_table = vsc848x_nexus_match,
+ },
+ .probe = vsc848x_nexus_probe,
+ .remove = vsc848x_nexus_remove,
+};
+
+static int __init vsc848x_mod_init(void)
+{
+ int rv;
+
+ rv = platform_driver_register(&vsc848x_nexus_driver);
+ if (rv)
+ return rv;
+
+ rv = phy_driver_register(&vsc848x_phy_driver);
+
+ return rv;
+}
+module_init(vsc848x_mod_init);
+
+static void __exit vsc848x_mod_exit(void)
+{
+ phy_driver_unregister(&vsc848x_phy_driver);
+ platform_driver_unregister(&vsc848x_nexus_driver);
+}
+module_exit(vsc848x_mod_exit);
+
+MODULE_DESCRIPTION("Driver for Vitesse VSC848X PHY");
+MODULE_AUTHOR("David Daney and Aaron Williams");
+MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,310 @@
From: Abhishek Paliwal <abhishek.paliwal@aricent.com>
Date: Fri, 13 Feb 2015 12:47:36 +0530
Subject: of: Add of_memory_accessor to map device tree node to memory accessor
functions.
From: Aaron Williams <aaron.williams@cavium.com>
Currently there is no easy way to map a device tree node to a memory
accessor function for devices like I2C EEPROMs. For example, the Vitesse
vsc848x 10G PHY driver needs to be able to use the I2C at24 serial EEPROM
memory accessor function in order to read the SFP+ eeprom.
This provides a way where the vsc848x module can parse its device tree and
easily gain the accessor functions for the eeprom through a phandle.
This may be useful for any module which provides memory accessor functions.
Signed-off-by: Aaron Williams <aaron.williams@cavium.com>
Signed-off-by: Leonid Rosenboim <lrosenboim@caviumnetworks.com>
Signed-off-by: Abhishek Paliwal <abhishek.paliwal@aricent.com>
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -107,4 +107,10 @@
# arches should select this if DMA is coherent by default for OF devices
bool
+config OF_MEMORY_ACCESSOR
+ def_bool y
+ help
+ OpenFirmware memory accessor support for accessing devices like
+ i2c and SPI eeproms.
+
endif # OF
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -16,3 +16,4 @@
obj-$(CONFIG_OF_NUMA) += of_numa.o
obj-$(CONFIG_OF_UNITTEST) += unittest-data/
+obj-$(CONFIG_OF_MEMORY_ACCESSOR) += of_memory_accessor.o
--- /dev/null
+++ b/drivers/of/of_memory_accessor.c
@@ -0,0 +1,192 @@
+/*
+ * Memory accessor OF helpers
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012 Cavium Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_memory_accessor.h>
+#include <linux/list.h>
+#include <linux/memory.h>
+
+struct of_macc_entry {
+ struct list_head list;
+ struct device *dev;
+ struct memory_accessor *macc;
+ int ref;
+};
+
+static DEFINE_MUTEX(lock);
+static LIST_HEAD(macc_list);
+
+/**
+ * Adds a mapping of a device node to a memory accessor
+ *
+ * @param[in] dev - device
+ * @param[in] macc - memory accessor
+ *
+ * @returns 0 for success or -ENOMEM
+ */
+int of_memory_accessor_register(struct device *dev,
+ struct memory_accessor *macc)
+{
+ struct of_macc_entry *mentry;
+
+ mentry = kmalloc(sizeof(*mentry), GFP_KERNEL);
+ if (mentry == NULL)
+ return -ENOMEM;
+
+ mentry->dev = dev;
+ mentry->macc = macc;
+ mentry->ref = 0;
+
+ mutex_lock(&lock);
+
+ list_add(&(mentry->list), &macc_list);
+
+ mutex_unlock(&lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(of_memory_accessor_register);
+
+/**
+ * removes the mapping of a device node to a memory accessor
+ *
+ * @param[in] devnode - device node to remove
+ *
+ * @returns 0 for success or -ENODEV if device node not found, -EBUSY if still
+ * in use
+ */
+
+int of_memory_accessor_remove(struct device *dev)
+{
+ struct of_macc_entry *mentry;
+ struct list_head *pos, *q;
+ int ret = -ENODEV;
+
+ mutex_lock(&lock);
+
+ list_for_each_safe(pos, q, &macc_list) {
+ mentry = list_entry(pos, struct of_macc_entry, list);
+ if (mentry->dev == dev) {
+ if (mentry->ref > 0) {
+ ret = -EBUSY;
+ goto done;
+ }
+ list_del(pos);
+ kfree(mentry);
+ ret = 0;
+ goto done;
+ }
+ }
+
+ /* Not found */
+done:
+ mutex_unlock(&lock);
+ return ret;
+}
+EXPORT_SYMBOL(of_memory_accessor_remove);
+
+/**
+ * Returns the memory accessor for a device node and increments a reference
+ * count
+ *
+ * @param[in] devnode - device node to look up
+ *
+ * @returns memory accessor for device node or NULL if none found.
+ */
+struct memory_accessor *
+of_memory_accessor_get(const struct device_node *devnode)
+{
+ struct of_macc_entry *mentry;
+ struct list_head *pos;
+ struct memory_accessor *macc = NULL;
+
+ mutex_lock(&lock);
+
+ list_for_each(pos, &macc_list) {
+ mentry = list_entry(pos, struct of_macc_entry, list);
+ if (mentry->dev->of_node == devnode) {
+ macc = mentry->macc;
+ if (!mentry->ref) {
+ if (!try_module_get(mentry->dev->driver->owner)) {
+ macc = NULL;
+ pr_info("Warning: module for %s not found!",
+ mentry->dev->of_node->full_name);
+ }
+ }
+ mentry->ref++;
+ goto done;
+ }
+ }
+done:
+ mutex_unlock(&lock);
+ return macc;
+}
+EXPORT_SYMBOL(of_memory_accessor_get);
+
+/**
+ * Decrements the reference count for the memory accessor attached to the
+ * device node.
+ *
+ * @param[in] devnode - device node to look up
+ *
+ * @returns 0 for success or -ENODEV if the device node was not found.
+ */
+int of_memory_accessor_put(const struct device_node *devnode)
+{
+ struct of_macc_entry *mentry;
+ struct list_head *pos;
+ int ret = -ENODEV;
+
+ mutex_lock(&lock);
+ list_for_each(pos, &macc_list) {
+ mentry = list_entry(pos, struct of_macc_entry, list);
+ if (mentry->dev->of_node == devnode) {
+ if (mentry->ref > 0)
+ mentry->ref--;
+ if (!mentry->ref)
+ module_put(mentry->dev->driver->owner);
+
+ module_put(THIS_MODULE);
+ ret = 0;
+ goto done;
+ }
+ }
+done:
+ mutex_unlock(&lock);
+ return ret;
+}
+EXPORT_SYMBOL(of_memory_accessor_put);
+
+static void __exit of_memory_accessor_exit(void)
+{
+ struct of_macc_entry *mentry;
+ struct list_head *pos, *q;
+
+ list_for_each_safe(pos, q, &macc_list) {
+ mentry = list_entry(pos, struct of_macc_entry, list);
+ if (mentry->ref)
+ module_put(mentry->dev->driver->owner);
+ list_del(pos);
+ kfree(mentry);
+ }
+
+ /* Not found */
+ mutex_destroy(&lock);
+ list_del(&macc_list);
+}
+module_exit(of_memory_accessor_exit);
+
+MODULE_DESCRIPTION("Driver for mapping device nodes to memory accessors");
+MODULE_AUTHOR("Aaron Williams");
+MODULE_LICENSE("GPL");
--- /dev/null
+++ b/include/linux/of_memory_accessor.h
@@ -0,0 +1,71 @@
+#ifndef _LINUX_OF_MEMORY_ACCESSOR_H
+#define _LINUX_OF_MEMORY_ACCESSOR_H
+/*
+ * Memory accessor OF helpers
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012 Cavium Inc.
+ */
+
+#include <linux/of.h>
+#include <linux/memory.h>
+
+/**
+ * Adds a mapping of a device node to a memory accessor
+ *
+ * @param[in] dev - device
+ * @param[in] macc - memory accessor
+ *
+ * @returns 0 for success or -ENOMEM
+ */
+#ifdef CONFIG_OF_MEMORY_ACCESSOR
+int of_memory_accessor_register(struct device *dev,
+ struct memory_accessor *macc);
+#else
+static inline int of_memory_accessor_register(struct device *dev,
+ struct memory_accessor *macc)
+{
+ return 0;
+}
+#endif
+
+/**
+ * removes the mapping of a device node to a memory accessor
+ *
+ * @param[in] devnode - device node to remove
+ *
+ * @returns 0 for success or 1 if device node not found
+ */
+#ifdef CONFIG_OF_MEMORY_ACCESSOR
+int of_memory_accessor_remove(struct device *dev);
+#else
+static inline int of_memory_accessor_remove(struct device *dev)
+{
+ return 0;
+}
+#endif
+
+/**
+ * Returns the memory accessor for a device node
+ *
+ * @param[in] devnode - device node to look up
+ *
+ * @returns memory accessor for device node or NULL if none found.
+ */
+struct memory_accessor *
+of_memory_accessor_get(const struct device_node *devnode);
+
+/**
+ * Decrements the reference count for the memory accessor attached to the
+ * device node.
+ *
+ * @param[in] devnode - device node to look up
+ *
+ * @returns 0 for success or -1 if the device node was not found.
+ */
+int of_memory_accessor_put(const struct device_node *devnode);
+
+#endif /* _LINUX_OF_MEMORY_ACCESSOR_H */

View File

@@ -0,0 +1,42 @@
From: Abhishek Paliwal <abhishek.paliwal@aricent.com>
Date: Fri, 13 Feb 2015 12:48:16 +0530
Subject: misc/at24: Register memory accessor functions with of_memory_accessor
From: Aaron Williams <aaron.williams@cavium.com>
The at24 module will now register its memory accessor functions with its
device tree entry so that other modules may call these functions based on
the device tree node.
Signed-off-by: Aaron Williams <aaron.williams@cavium.com>
Signed-off-by: Leonid Rosenboim <lrosenboim@caviumnetworks.com>
Signed-off-by: Abhishek Paliwal <abhishek.paliwal@aricent.com>
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -23,6 +23,7 @@
#include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include <linux/gpio/consumer.h>
+#include <linux/of_memory_accessor.h>
/* Address pointer is 16 bit. */
#define AT24_FLAG_ADDR16 BIT(7)
@@ -720,6 +721,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
byte_len, client->name,
writable ? "writable" : "read-only", at24->write_max);
+ if (client->dev.of_node)
+ of_memory_accessor_register(&client->dev, &at24->macc);
+
return 0;
}
@@ -728,5 +732,8 @@ static int at24_remove(struct i2c_client *client)
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
+ if (client->dev.of_node)
+ of_memory_accessor_remove(&client->dev);
+
return 0;
}

View File

@@ -1,15 +0,0 @@
--- a/drivers/staging/octeon/ethernet.c
+++ b/drivers/staging/octeon/ethernet.c
@@ -860,8 +860,10 @@ static int cvm_oct_probe(struct platform_device *pdev)
break;
case CVMX_HELPER_INTERFACE_MODE_XAUI:
- dev->netdev_ops = &cvm_oct_xaui_netdev_ops;
- strscpy(dev->name, "xaui%d", sizeof(dev->name));
+ if (0) { //of_device_is_available(priv->of_node)) {
+ dev->netdev_ops = &cvm_oct_xaui_netdev_ops;
+ strscpy(dev->name, "xaui%d", sizeof(dev->name));
+ }
break;
case CVMX_HELPER_INTERFACE_MODE_LOOP:

View File

@@ -0,0 +1,79 @@
--- a/arch/mips/cavium-octeon/executive/cvmx-helper.c
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper.c
@@ -1041,12 +1041,14 @@ int cvmx_helper_initialize_packet_io_global(void)
result |= __cvmx_helper_interface_setup_ipd(interface);
result |= __cvmx_helper_interface_setup_pko(interface);
}
-
+ cvmx_dprintf("Gets past end of interfaces setup\n");
result |= __cvmx_helper_global_setup_ipd();
result |= __cvmx_helper_global_setup_pko();
+ cvmx_dprintf("Sets up global helpers\n");
/* Enable any flow control and backpressure */
result |= __cvmx_helper_global_setup_backpressure();
+ cvmx_dprintf("Sets up backpressure helpers\n");
#if CVMX_HELPER_ENABLE_IPD
result |= cvmx_helper_ipd_and_packet_input_enable();
--- a/drivers/staging/octeon/ethernet.c
+++ b/drivers/staging/octeon/ethernet.c
@@ -700,6 +700,7 @@ static int cvm_oct_probe(struct platform_device *pdev)
cvm_oct_configure_common_hw();
cvmx_helper_initialize_packet_io_global();
+ pr_err("OK, we got out of packet_io_global()\n");
if (receive_group_order) {
if (receive_group_order > 4)
@@ -709,9 +710,11 @@ static int cvm_oct_probe(struct platform_device *pdev)
pow_receive_groups = BIT(pow_receive_group);
}
+ pr_err("We got through receive_group_order\n");
/* Change the input group for all ports before input is enabled */
num_interfaces = cvmx_helper_get_number_of_interfaces();
for (interface = 0; interface < num_interfaces; interface++) {
+ pr_err("We are starting on interface %d\n", interface);
int num_ports = cvmx_helper_ports_on_interface(interface);
int port;
@@ -752,14 +755,14 @@ static int cvm_oct_probe(struct platform_device *pdev)
pip_prt_tagx.s.grptag = 0;
pip_prt_tagx.s.grp = pow_receive_group;
}
-
+ pr_err("COULD IT BE CSR WRITES???\n");
cvmx_write_csr(CVMX_PIP_PRT_TAGX(port),
pip_prt_tagx.u64);
}
}
-
+ pr_err("COULD it be cvmx_helper_ipd_and_packet_input_enable???\n");
cvmx_helper_ipd_and_packet_input_enable();
-
+ pr_err("COULD it be this memset??\n");
memset(cvm_oct_device, 0, sizeof(cvm_oct_device));
/*
@@ -804,6 +807,7 @@ static int cvm_oct_probe(struct platform_device *pdev)
}
}
+ pr_err("OK, we got THIS far, all the way to the interface init. Which is weird, we shoulda crashed by now\n");
num_interfaces = cvmx_helper_get_number_of_interfaces();
for (interface = 0; interface < num_interfaces; interface++) {
cvmx_helper_interface_mode_t imode =
@@ -860,8 +864,10 @@ static int cvm_oct_probe(struct platform_device *pdev)
break;
case CVMX_HELPER_INTERFACE_MODE_XAUI:
- dev->netdev_ops = &cvm_oct_xaui_netdev_ops;
- strscpy(dev->name, "xaui%d", sizeof(dev->name));
+ if (of_device_is_available(priv->of_node)) {
+ dev->netdev_ops = &cvm_oct_xaui_netdev_ops;
+ strscpy(dev->name, "xaui%d", sizeof(dev->name));
+ }
break;
case CVMX_HELPER_INTERFACE_MODE_LOOP:

View File

@@ -0,0 +1,48 @@
--- a/arch/mips/cavium-octeon/executive/cvmx-helper.c
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper.c
@@ -971,6 +971,7 @@ int cvmx_helper_ipd_and_packet_input_enable(void)
int interface;
/* Enable IPD */
+ cvmx_dprintf("COULD IT BE cvmx_ipd_enable???\n");
cvmx_ipd_enable();
/*
@@ -978,13 +979,16 @@ int cvmx_helper_ipd_and_packet_input_enable(void)
* that at this point IPD/PIP must be fully functional and PKO
* must be disabled
*/
+ cvmx_dprintf("COULD IT BE getting no of interfaces????\n");
num_interfaces = cvmx_helper_get_number_of_interfaces();
for (interface = 0; interface < num_interfaces; interface++) {
+ cvmx_dprintf("COULD IT BE interface # %d ??ces????\n", interface);
if (cvmx_helper_ports_on_interface(interface) > 0)
__cvmx_helper_packet_hardware_enable(interface);
}
/* Finally enable PKO now that the entire path is up and running */
+ cvmx_dprintf("COULD IT BE PKO???\n");
cvmx_pko_enable();
if ((OCTEON_IS_MODEL(OCTEON_CN31XX_PASS1)
--- a/arch/mips/include/asm/octeon/cvmx-ipd.h
+++ b/arch/mips/include/asm/octeon/cvmx-ipd.h
@@ -129,15 +129,18 @@ static inline void cvmx_ipd_config(uint64_t mbuff_size,
static inline void cvmx_ipd_enable(void)
{
union cvmx_ipd_ctl_status ipd_reg;
+ cvmx_dprintf("COULD IT BE THIS ONE CSR READ IN cvmx_ipd_enable ??\n");
ipd_reg.u64 = cvmx_read_csr(CVMX_IPD_CTL_STATUS);
if (ipd_reg.s.ipd_en) {
cvmx_dprintf
("Warning: Enabling IPD when IPD already enabled.\n");
}
+ cvmx_dprintf("OK IT PASSED THE READ. BUT THERE'S THIS CVMX_ENABLE_LEN_M8_FIX THAT COULD APPLY??\n");
ipd_reg.s.ipd_en = 1;
#if CVMX_ENABLE_LEN_M8_FIX
if (!OCTEON_IS_MODEL(OCTEON_CN38XX_PASS2))
ipd_reg.s.len_m8 = TRUE;
#endif
+ cvmx_dprintf("COULD IT BE THIS ONE CSR WRITE??? IN cvmx_ipd_enable ??\n");
cvmx_write_csr(CVMX_IPD_CTL_STATUS, ipd_reg.u64);
}

View File

@@ -0,0 +1,186 @@
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c b/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c
index 93a498d05184..27733d710355 100644
--- a/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c
@@ -121,58 +121,80 @@ int __cvmx_helper_xaui_enable(int interface)
union cvmx_gmxx_tx_int_en gmx_tx_int_en;
union cvmx_pcsxx_int_en_reg pcsx_int_en_reg;
+ cvmx_dprintf("COULD IT BE THE FEATURE CHECK FOR PKND??\n");
/* Setup PKND */
if (octeon_has_feature(OCTEON_FEATURE_PKND)) {
+ cvmx_dprintf("COULD IT BE THE FIRST CSR READ??\n");
gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(0, interface));
+ cvmx_dprintf("COULD IT BE THE second CSR READ??\n");
gmx_cfg.s.pknd = cvmx_helper_get_ipd_port(interface, 0);
+ cvmx_dprintf("COULD IT BE THE third CSR /write READ??\n");
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64);
}
/* (1) Interface has already been enabled. */
/* (2) Disable GMX. */
+ cvmx_dprintf("step2 start!?? could it be the csr read??\n");
xauiMiscCtl.u64 = cvmx_read_csr(CVMX_PCSXX_MISC_CTL_REG(interface));
xauiMiscCtl.s.gmxeno = 1;
+ cvmx_dprintf("step2 could it be the csr write to disable gmx??\n");
cvmx_write_csr(CVMX_PCSXX_MISC_CTL_REG(interface), xauiMiscCtl.u64);
/* (3) Disable GMX and PCSX interrupts. */
+ cvmx_dprintf("step3 start. could it be the csr read??\n");
gmx_rx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_RXX_INT_EN(0, interface));
+ cvmx_dprintf("step3 start. could it be the write for disabling interrupts???\n");
cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(0, interface), 0x0);
+ cvmx_dprintf("step3 start. could it be read for setting up the gmx_tx_int_en???\n");
gmx_tx_int_en.u64 = cvmx_read_csr(CVMX_GMXX_TX_INT_EN(interface));
+ cvmx_dprintf("step3 start. could it be the cvmx_gmxx write???\n");
cvmx_write_csr(CVMX_GMXX_TX_INT_EN(interface), 0x0);
+ cvmx_dprintf("step3 start almost done. show up tell us about the PCSXX write???\n");
pcsx_int_en_reg.u64 = cvmx_read_csr(CVMX_PCSXX_INT_EN_REG(interface));
+ cvmx_dprintf("step3, this final csr write?\n");
cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(interface), 0x0);
/* (4) Bring up the PCSX and GMX reconciliation layer. */
/* (4)a Set polarity and lane swapping. */
/* (4)b */
+ cvmx_dprintf("4.1\n");
gmxXauiTxCtl.u64 = cvmx_read_csr(CVMX_GMXX_TX_XAUI_CTL(interface));
/* Enable better IFG packing and improves performance */
gmxXauiTxCtl.s.dic_en = 1;
gmxXauiTxCtl.s.uni_en = 0;
+ cvmx_dprintf("4.2\n");
cvmx_write_csr(CVMX_GMXX_TX_XAUI_CTL(interface), gmxXauiTxCtl.u64);
/* (4)c Aply reset sequence */
+ cvmx_dprintf("4.3\n");
xauiCtl.u64 = cvmx_read_csr(CVMX_PCSXX_CONTROL1_REG(interface));
xauiCtl.s.lo_pwr = 0;
/* Issuing a reset here seems to hang some CN68XX chips. */
if (!OCTEON_IS_MODEL(OCTEON_CN68XX_PASS1_X) &&
- !OCTEON_IS_MODEL(OCTEON_CN68XX_PASS2_X))
+ !OCTEON_IS_MODEL(OCTEON_CN68XX_PASS2_X) &&
+ 0)
xauiCtl.s.reset = 1;
+ cvmx_dprintf("4.4\n");
cvmx_write_csr(CVMX_PCSXX_CONTROL1_REG(interface), xauiCtl.u64);
+ cvmx_dprintf("4.5\n");
/* Wait for PCS to come out of reset */
if (CVMX_WAIT_FOR_FIELD64
(CVMX_PCSXX_CONTROL1_REG(interface), union cvmx_pcsxx_control1_reg,
reset, ==, 0, 10000))
return -1;
+
+ cvmx_dprintf("4.6\n");
/* Wait for PCS to be aligned */
if (CVMX_WAIT_FOR_FIELD64
(CVMX_PCSXX_10GBX_STATUS_REG(interface),
union cvmx_pcsxx_10gbx_status_reg, alignd, ==, 1, 10000))
return -1;
+
+ cvmx_dprintf("4.7\n");
/* Wait for RX to be ready */
if (CVMX_WAIT_FOR_FIELD64
(CVMX_GMXX_RX_XAUI_CTL(interface), union cvmx_gmxx_rx_xaui_ctl,
@@ -180,8 +202,11 @@ int __cvmx_helper_xaui_enable(int interface)
return -1;
/* (6) Configure GMX */
+ cvmx_dprintf("6.1\n");
gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(0, interface));
+
gmx_cfg.s.en = 0;
+ cvmx_dprintf("6.2\n");
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64);
/* Wait for GMX RX to be idle */
@@ -189,62 +214,87 @@ int __cvmx_helper_xaui_enable(int interface)
(CVMX_GMXX_PRTX_CFG(0, interface), union cvmx_gmxx_prtx_cfg,
rx_idle, ==, 1, 10000))
return -1;
+ cvmx_dprintf("6.3\n");
/* Wait for GMX TX to be idle */
if (CVMX_WAIT_FOR_FIELD64
(CVMX_GMXX_PRTX_CFG(0, interface), union cvmx_gmxx_prtx_cfg,
tx_idle, ==, 1, 10000))
return -1;
+ cvmx_dprintf("6.4\n");
/* GMX configure */
gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(0, interface));
gmx_cfg.s.speed = 1;
gmx_cfg.s.speed_msb = 0;
gmx_cfg.s.slottime = 1;
+ cvmx_dprintf("6.5\n");
cvmx_write_csr(CVMX_GMXX_TX_PRTS(interface), 1);
+ cvmx_dprintf("6.6\n");
cvmx_write_csr(CVMX_GMXX_TXX_SLOT(0, interface), 512);
+ cvmx_dprintf("6.7\n");
cvmx_write_csr(CVMX_GMXX_TXX_BURST(0, interface), 8192);
+ cvmx_dprintf("6.8\n");
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64);
/* (7) Clear out any error state */
+ cvmx_dprintf("7.1\n");
cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(0, interface),
cvmx_read_csr(CVMX_GMXX_RXX_INT_REG(0, interface)));
+ cvmx_dprintf("7.2\n");
cvmx_write_csr(CVMX_GMXX_TX_INT_REG(interface),
cvmx_read_csr(CVMX_GMXX_TX_INT_REG(interface)));
+ cvmx_dprintf("7.3\n");
cvmx_write_csr(CVMX_PCSXX_INT_REG(interface),
cvmx_read_csr(CVMX_PCSXX_INT_REG(interface)));
+ cvmx_dprintf("7.4\n");
/* Wait for receive link */
if (CVMX_WAIT_FOR_FIELD64
(CVMX_PCSXX_STATUS1_REG(interface), union cvmx_pcsxx_status1_reg,
rcv_lnk, ==, 1, 10000))
return -1;
+ cvmx_dprintf("7.5\n");
if (CVMX_WAIT_FOR_FIELD64
(CVMX_PCSXX_STATUS2_REG(interface), union cvmx_pcsxx_status2_reg,
xmtflt, ==, 0, 10000))
return -1;
+ cvmx_dprintf("7.6\n");
if (CVMX_WAIT_FOR_FIELD64
(CVMX_PCSXX_STATUS2_REG(interface), union cvmx_pcsxx_status2_reg,
rcvflt, ==, 0, 10000))
return -1;
+ cvmx_dprintf("7.7\n");
cvmx_write_csr(CVMX_GMXX_RXX_INT_EN(0, interface), gmx_rx_int_en.u64);
+ cvmx_dprintf("7.8\n");
cvmx_write_csr(CVMX_GMXX_TX_INT_EN(interface), gmx_tx_int_en.u64);
+ cvmx_dprintf("7.9\n");
cvmx_write_csr(CVMX_PCSXX_INT_EN_REG(interface), pcsx_int_en_reg.u64);
/* (8) Enable packet reception */
xauiMiscCtl.s.gmxeno = 0;
+ cvmx_dprintf("8.1\n");
cvmx_write_csr(CVMX_PCSXX_MISC_CTL_REG(interface), xauiMiscCtl.u64);
+ cvmx_dprintf("8.2\n");
gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(0, interface));
gmx_cfg.s.en = 1;
+ cvmx_dprintf("8.3\n");
cvmx_write_csr(CVMX_GMXX_PRTX_CFG(0, interface), gmx_cfg.u64);
+ cvmx_dprintf("8.4\n");
__cvmx_interrupt_pcsx_intx_en_reg_enable(0, interface);
+ cvmx_dprintf("8.5\n");
__cvmx_interrupt_pcsx_intx_en_reg_enable(1, interface);
+ cvmx_dprintf("8.6\n");
__cvmx_interrupt_pcsx_intx_en_reg_enable(2, interface);
+ cvmx_dprintf("8.7\n");
__cvmx_interrupt_pcsx_intx_en_reg_enable(3, interface);
+ cvmx_dprintf("8.8\n");
__cvmx_interrupt_pcsxx_int_en_reg_enable(interface);
+ cvmx_dprintf("8.9\n");
__cvmx_interrupt_gmxx_enable(interface);
+ cvmx_dprintf("8.10\n");
return 0;
}