Hi Greg,
I'd like to submit the following drivers to staging. They
are all for components of the mt7621 MIPS-based SOC from Mediatek.
I lifted them out of libreCMC and (for the ethernet code) out
of email posts from about 2 years ago. I forward-ported
them to mainline, fixed enough bugs that they mostly work,
and moved them to staging.
With these patches the GNUBEE is almost usable. It needs
one patch to arch/mips/kernel/setup.c because mips_cm_probe()
is being called too early. To successfully reboot (i.e warm-restart)
it needs a small patch to drivers/mtd/devices/m25p80.c.
I've made a point of only included code that I can actually test
on hardware that I have. That has meant selecting only a few patches
from a series and in a couple of cases, discarding some files from a
patch. I haven't discard code from within a file that I need part
of.
All the driver patches had a From: of John Crispin so he is cc:ed -
thanks John!!
I have added TODO files, some of which have useful details.
I think some of the drivers (eth,mmc, maybe dma) should be merged
with related mainline code rather than added as separate drivers.
I've included a dts file for the GNUBEE. When all the other drivers
move out of staging, it should follow them.
I plan to continue to work on these drivers, but probably not for a
few weeks at least.
Thanks,
NeilBrown
---
John Crispin (12):
staging: mt7621-pci: MIPS/ralink: add MT7621 pcie driver
staging: mt7621-pinctrl: ralink: add pinctrl driver
staging: mt7621-gpio: ralink: add mt7621 gpio controller
staging: mt7621-spi: add mt7621 support
staging: mt7621-dma: ralink: add rt2880 dma engine
staging: mt7621-mmc: MIPS: ralink: add sdhci for mt7620a SoC
staging: mt7621-eth: Document ralink/mediatek SoC ethernet binding
staging: mt7621-eth: add the drivers core files
staging: mt7621-eth: add gigabit switch driver (GSW)
staging: mt7621-eth: add mdio support for mt762X family
staging: mt7621-eth: add support for mt7621
staging: mt7621-eth: mediatek: add Kconfig and Makefile
NeilBrown (1):
staging: mt7621-dts: add dts files
drivers/staging/Kconfig | 14
drivers/staging/Makefile | 8
drivers/staging/mt7621-dma/Kconfig | 12
drivers/staging/mt7621-dma/Makefile | 4
drivers/staging/mt7621-dma/TODO | 5
drivers/staging/mt7621-dma/mtk-hsdma.c | 767 +++++
drivers/staging/mt7621-dma/ralink-gdma.c | 928 ++++++
drivers/staging/mt7621-dts/Kconfig | 5
drivers/staging/mt7621-dts/Makefile | 3
drivers/staging/mt7621-dts/TODO | 5
drivers/staging/mt7621-dts/gbpc1.dts | 143 +
drivers/staging/mt7621-dts/mt7621.dtsi | 471 +++
.../devicetree/bindings/net/mediatek-net-gsw.txt | 48
drivers/staging/mt7621-eth/Kconfig | 39
drivers/staging/mt7621-eth/Makefile | 14
drivers/staging/mt7621-eth/TODO | 13
drivers/staging/mt7621-eth/ethtool.c | 225 +
drivers/staging/mt7621-eth/ethtool.h | 22
drivers/staging/mt7621-eth/gsw_mt7620.h | 277 ++
drivers/staging/mt7621-eth/gsw_mt7621.c | 298 ++
drivers/staging/mt7621-eth/mdio.c | 271 ++
drivers/staging/mt7621-eth/mdio.h | 27
drivers/staging/mt7621-eth/mdio_mt7620.c | 173 +
drivers/staging/mt7621-eth/mtk_eth_soc.c | 2178 ++++++++++++++
drivers/staging/mt7621-eth/mtk_eth_soc.h | 721 +++++
drivers/staging/mt7621-eth/soc_mt7621.c | 160 +
drivers/staging/mt7621-gpio/Kconfig | 6
drivers/staging/mt7621-gpio/Makefile | 3
drivers/staging/mt7621-gpio/TODO | 5
drivers/staging/mt7621-gpio/gpio-mt7621.c | 353 ++
drivers/staging/mt7621-mmc/Kconfig | 16
drivers/staging/mt7621-mmc/Makefile | 42
drivers/staging/mt7621-mmc/TODO | 8
drivers/staging/mt7621-mmc/board.h | 137 +
drivers/staging/mt7621-mmc/dbg.c | 347 ++
drivers/staging/mt7621-mmc/dbg.h | 156 +
drivers/staging/mt7621-mmc/mt6575_sd.h | 1001 +++++++
drivers/staging/mt7621-mmc/sd.c | 3074 ++++++++++++++++++++
drivers/staging/mt7621-pci/Makefile | 1
drivers/staging/mt7621-pci/TODO | 12
drivers/staging/mt7621-pci/pci-mt7621.c | 840 +++++
drivers/staging/mt7621-pinctrl/Kconfig | 4
drivers/staging/mt7621-pinctrl/Makefile | 3
drivers/staging/mt7621-pinctrl/TODO | 6
drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c | 472 +++
drivers/staging/mt7621-spi/Kconfig | 6
drivers/staging/mt7621-spi/Makefile | 1
drivers/staging/mt7621-spi/TODO | 5
drivers/staging/mt7621-spi/spi-mt7621.c | 489 +++
49 files changed, 13818 insertions(+)
create mode 100644 drivers/staging/mt7621-dma/Kconfig
create mode 100644 drivers/staging/mt7621-dma/Makefile
create mode 100644 drivers/staging/mt7621-dma/TODO
create mode 100644 drivers/staging/mt7621-dma/mtk-hsdma.c
create mode 100644 drivers/staging/mt7621-dma/ralink-gdma.c
create mode 100644 drivers/staging/mt7621-dts/Kconfig
create mode 100644 drivers/staging/mt7621-dts/Makefile
create mode 100644 drivers/staging/mt7621-dts/TODO
create mode 100644 drivers/staging/mt7621-dts/gbpc1.dts
create mode 100644 drivers/staging/mt7621-dts/mt7621.dtsi
create mode 100644 drivers/staging/mt7621-eth/Documentation/devicetree/bindings/net/mediatek-net-gsw.txt
create mode 100644 drivers/staging/mt7621-eth/Kconfig
create mode 100644 drivers/staging/mt7621-eth/Makefile
create mode 100644 drivers/staging/mt7621-eth/TODO
create mode 100644 drivers/staging/mt7621-eth/ethtool.c
create mode 100644 drivers/staging/mt7621-eth/ethtool.h
create mode 100644 drivers/staging/mt7621-eth/gsw_mt7620.h
create mode 100644 drivers/staging/mt7621-eth/gsw_mt7621.c
create mode 100644 drivers/staging/mt7621-eth/mdio.c
create mode 100644 drivers/staging/mt7621-eth/mdio.h
create mode 100644 drivers/staging/mt7621-eth/mdio_mt7620.c
create mode 100644 drivers/staging/mt7621-eth/mtk_eth_soc.c
create mode 100644 drivers/staging/mt7621-eth/mtk_eth_soc.h
create mode 100644 drivers/staging/mt7621-eth/soc_mt7621.c
create mode 100644 drivers/staging/mt7621-gpio/Kconfig
create mode 100644 drivers/staging/mt7621-gpio/Makefile
create mode 100644 drivers/staging/mt7621-gpio/TODO
create mode 100644 drivers/staging/mt7621-gpio/gpio-mt7621.c
create mode 100644 drivers/staging/mt7621-mmc/Kconfig
create mode 100644 drivers/staging/mt7621-mmc/Makefile
create mode 100644 drivers/staging/mt7621-mmc/TODO
create mode 100644 drivers/staging/mt7621-mmc/board.h
create mode 100644 drivers/staging/mt7621-mmc/dbg.c
create mode 100644 drivers/staging/mt7621-mmc/dbg.h
create mode 100644 drivers/staging/mt7621-mmc/mt6575_sd.h
create mode 100644 drivers/staging/mt7621-mmc/sd.c
create mode 100644 drivers/staging/mt7621-pci/Makefile
create mode 100644 drivers/staging/mt7621-pci/TODO
create mode 100644 drivers/staging/mt7621-pci/pci-mt7621.c
create mode 100644 drivers/staging/mt7621-pinctrl/Kconfig
create mode 100644 drivers/staging/mt7621-pinctrl/Makefile
create mode 100644 drivers/staging/mt7621-pinctrl/TODO
create mode 100644 drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c
create mode 100644 drivers/staging/mt7621-spi/Kconfig
create mode 100644 drivers/staging/mt7621-spi/Makefile
create mode 100644 drivers/staging/mt7621-spi/TODO
create mode 100644 drivers/staging/mt7621-spi/spi-mt7621.c
--
Signature
From: John Crispin <[email protected]>
NeilBrown: forward port and hack to work on GNUBEE1
Signed-off-by: John Crispin <[email protected]>
Signed-off-by: NeilBrown <[email protected]>
---
drivers/staging/Makefile | 1
drivers/staging/mt7621-pci/Makefile | 1
drivers/staging/mt7621-pci/TODO | 12
drivers/staging/mt7621-pci/pci-mt7621.c | 840 +++++++++++++++++++++++++++++++
4 files changed, 854 insertions(+)
create mode 100644 drivers/staging/mt7621-pci/Makefile
create mode 100644 drivers/staging/mt7621-pci/TODO
create mode 100644 drivers/staging/mt7621-pci/pci-mt7621.c
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index af8cd6a3a1f6..4e79a4ad6cf6 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -52,3 +52,4 @@ obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/
obj-$(CONFIG_CRYPTO_DEV_CCREE) += ccree/
obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
obj-$(CONFIG_PI433) += pi433/
+obj-$(CONFIG_SOC_MT7621) += mt7621-pci/
diff --git a/drivers/staging/mt7621-pci/Makefile b/drivers/staging/mt7621-pci/Makefile
new file mode 100644
index 000000000000..607b84bedcc3
--- /dev/null
+++ b/drivers/staging/mt7621-pci/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SOC_MT7621) += pci-mt7621.o
diff --git a/drivers/staging/mt7621-pci/TODO b/drivers/staging/mt7621-pci/TODO
new file mode 100644
index 000000000000..cf30f629b9fd
--- /dev/null
+++ b/drivers/staging/mt7621-pci/TODO
@@ -0,0 +1,12 @@
+
+- general code review and cleanup
+- can this be converted to not require PCI_DRIVERS_LEGACY ??
+ The irq returned by pcibios_map_irq is a "hwirq" (hardware irq number)
+ and pci_assign_irq() assigns this directly to dev->irq, which
+ expects a "virq" (virtual irq number). These numbers are different
+ on MIPS. There is a gross hack to make it work on one
+ specific platform, so it can be tested.
+- Should this be merged with arch/mips/pci/pci-mt7620.c ??
+- ensure device-tree requirements are documented
+
+Cc: NeilBrown <[email protected]>
diff --git a/drivers/staging/mt7621-pci/pci-mt7621.c b/drivers/staging/mt7621-pci/pci-mt7621.c
new file mode 100644
index 000000000000..1fa41eb8a87f
--- /dev/null
+++ b/drivers/staging/mt7621-pci/pci-mt7621.c
@@ -0,0 +1,840 @@
+/**************************************************************************
+ *
+ * BRIEF MODULE DESCRIPTION
+ * PCI init for Ralink RT2880 solution
+ *
+ * Copyright 2007 Ralink Inc. ([email protected])
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ **************************************************************************
+ * May 2007 Bruce Chang
+ * Initial Release
+ *
+ * May 2009 Bruce Chang
+ * support RT2880/RT3883 PCIe
+ *
+ * May 2011 Bruce Chang
+ * support RT6855/MT7620 PCIe
+ *
+ **************************************************************************
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <asm/pci.h>
+#include <asm/io.h>
+#include <asm/mips-cm.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_pci.h>
+#include <linux/platform_device.h>
+
+#include <ralink_regs.h>
+
+extern void pcie_phy_init(void);
+extern void chk_phy_pll(void);
+
+/*
+ * These functions and structures provide the BIOS scan and mapping of the PCI
+ * devices.
+ */
+
+#define CONFIG_PCIE_PORT0
+#define CONFIG_PCIE_PORT1
+#define CONFIG_PCIE_PORT2
+#define RALINK_PCIE0_CLK_EN (1<<24)
+#define RALINK_PCIE1_CLK_EN (1<<25)
+#define RALINK_PCIE2_CLK_EN (1<<26)
+
+#define RALINK_PCI_CONFIG_ADDR 0x20
+#define RALINK_PCI_CONFIG_DATA_VIRTUAL_REG 0x24
+#define SURFBOARDINT_PCIE0 11 /* PCIE0 */
+#define RALINK_INT_PCIE0 SURFBOARDINT_PCIE0
+#define RALINK_INT_PCIE1 SURFBOARDINT_PCIE1
+#define RALINK_INT_PCIE2 SURFBOARDINT_PCIE2
+#define SURFBOARDINT_PCIE1 31 /* PCIE1 */
+#define SURFBOARDINT_PCIE2 32 /* PCIE2 */
+#define RALINK_PCI_MEMBASE *(volatile u32 *)(RALINK_PCI_BASE + 0x0028)
+#define RALINK_PCI_IOBASE *(volatile u32 *)(RALINK_PCI_BASE + 0x002C)
+#define RALINK_PCIE0_RST (1<<24)
+#define RALINK_PCIE1_RST (1<<25)
+#define RALINK_PCIE2_RST (1<<26)
+#define RALINK_SYSCTL_BASE 0xBE000000
+
+#define RALINK_PCI_PCICFG_ADDR *(volatile u32 *)(RALINK_PCI_BASE + 0x0000)
+#define RALINK_PCI_PCIMSK_ADDR *(volatile u32 *)(RALINK_PCI_BASE + 0x000C)
+#define RALINK_PCI_BASE 0xBE140000
+
+#define RALINK_PCIEPHY_P0P1_CTL_OFFSET (RALINK_PCI_BASE + 0x9000)
+#define RT6855_PCIE0_OFFSET 0x2000
+#define RT6855_PCIE1_OFFSET 0x3000
+#define RT6855_PCIE2_OFFSET 0x4000
+
+#define RALINK_PCI0_BAR0SETUP_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0010)
+#define RALINK_PCI0_IMBASEBAR0_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0018)
+#define RALINK_PCI0_ID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0030)
+#define RALINK_PCI0_CLASS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0034)
+#define RALINK_PCI0_SUBID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0038)
+#define RALINK_PCI0_STATUS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0050)
+#define RALINK_PCI0_DERR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0060)
+#define RALINK_PCI0_ECRC *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE0_OFFSET + 0x0064)
+
+#define RALINK_PCI1_BAR0SETUP_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0010)
+#define RALINK_PCI1_IMBASEBAR0_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0018)
+#define RALINK_PCI1_ID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0030)
+#define RALINK_PCI1_CLASS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0034)
+#define RALINK_PCI1_SUBID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0038)
+#define RALINK_PCI1_STATUS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0050)
+#define RALINK_PCI1_DERR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0060)
+#define RALINK_PCI1_ECRC *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE1_OFFSET + 0x0064)
+
+#define RALINK_PCI2_BAR0SETUP_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0010)
+#define RALINK_PCI2_IMBASEBAR0_ADDR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0018)
+#define RALINK_PCI2_ID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0030)
+#define RALINK_PCI2_CLASS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0034)
+#define RALINK_PCI2_SUBID *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0038)
+#define RALINK_PCI2_STATUS *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0050)
+#define RALINK_PCI2_DERR *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0060)
+#define RALINK_PCI2_ECRC *(volatile u32 *)(RALINK_PCI_BASE + RT6855_PCIE2_OFFSET + 0x0064)
+
+#define RALINK_PCIEPHY_P0P1_CTL_OFFSET (RALINK_PCI_BASE + 0x9000)
+#define RALINK_PCIEPHY_P2_CTL_OFFSET (RALINK_PCI_BASE + 0xA000)
+
+
+#define MV_WRITE(ofs, data) \
+ *(volatile u32 *)(RALINK_PCI_BASE+(ofs)) = cpu_to_le32(data)
+#define MV_READ(ofs, data) \
+ *(data) = le32_to_cpu(*(volatile u32 *)(RALINK_PCI_BASE+(ofs)))
+#define MV_READ_DATA(ofs) \
+ le32_to_cpu(*(volatile u32 *)(RALINK_PCI_BASE+(ofs)))
+
+#define MV_WRITE_16(ofs, data) \
+ *(volatile u16 *)(RALINK_PCI_BASE+(ofs)) = cpu_to_le16(data)
+#define MV_READ_16(ofs, data) \
+ *(data) = le16_to_cpu(*(volatile u16 *)(RALINK_PCI_BASE+(ofs)))
+
+#define MV_WRITE_8(ofs, data) \
+ *(volatile u8 *)(RALINK_PCI_BASE+(ofs)) = data
+#define MV_READ_8(ofs, data) \
+ *(data) = *(volatile u8 *)(RALINK_PCI_BASE+(ofs))
+
+
+
+#define RALINK_PCI_MM_MAP_BASE 0x60000000
+#define RALINK_PCI_IO_MAP_BASE 0x1e160000
+
+#define RALINK_SYSTEM_CONTROL_BASE 0xbe000000
+#define GPIO_PERST
+#define ASSERT_SYSRST_PCIE(val) do { \
+ if (*(unsigned int *)(0xbe00000c) == 0x00030101) \
+ RALINK_RSTCTRL |= val; \
+ else \
+ RALINK_RSTCTRL &= ~val; \
+ } while(0)
+#define DEASSERT_SYSRST_PCIE(val) do { \
+ if (*(unsigned int *)(0xbe00000c) == 0x00030101) \
+ RALINK_RSTCTRL &= ~val; \
+ else \
+ RALINK_RSTCTRL |= val; \
+ } while(0)
+#define RALINK_SYSCFG1 *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x14)
+#define RALINK_CLKCFG1 *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x30)
+#define RALINK_RSTCTRL *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x34)
+#define RALINK_GPIOMODE *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x60)
+#define RALINK_PCIE_CLK_GEN *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x7c)
+#define RALINK_PCIE_CLK_GEN1 *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x80)
+#define PPLL_CFG1 *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0x9c)
+#define PPLL_DRV *(unsigned int *)(RALINK_SYSTEM_CONTROL_BASE + 0xa0)
+//RALINK_SYSCFG1 bit
+#define RALINK_PCI_HOST_MODE_EN (1<<7)
+#define RALINK_PCIE_RC_MODE_EN (1<<8)
+//RALINK_RSTCTRL bit
+#define RALINK_PCIE_RST (1<<23)
+#define RALINK_PCI_RST (1<<24)
+//RALINK_CLKCFG1 bit
+#define RALINK_PCI_CLK_EN (1<<19)
+#define RALINK_PCIE_CLK_EN (1<<21)
+//RALINK_GPIOMODE bit
+#define PCI_SLOTx2 (1<<11)
+#define PCI_SLOTx1 (2<<11)
+//MTK PCIE PLL bit
+#define PDRV_SW_SET (1<<31)
+#define LC_CKDRVPD_ (1<<19)
+
+#define MEMORY_BASE 0x0
+static int pcie_link_status = 0;
+
+#define PCI_ACCESS_READ_1 0
+#define PCI_ACCESS_READ_2 1
+#define PCI_ACCESS_READ_4 2
+#define PCI_ACCESS_WRITE_1 3
+#define PCI_ACCESS_WRITE_2 4
+#define PCI_ACCESS_WRITE_4 5
+
+static int config_access(unsigned char access_type, struct pci_bus *bus,
+ unsigned int devfn, unsigned int where, u32 * data)
+{
+ unsigned int slot = PCI_SLOT(devfn);
+ u8 func = PCI_FUNC(devfn);
+ uint32_t address_reg, data_reg;
+ unsigned int address;
+
+ address_reg = RALINK_PCI_CONFIG_ADDR;
+ data_reg = RALINK_PCI_CONFIG_DATA_VIRTUAL_REG;
+
+ address = (((where&0xF00)>>8)<<24) |(bus->number << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | 0x80000000;
+ MV_WRITE(address_reg, address);
+
+ switch(access_type) {
+ case PCI_ACCESS_WRITE_1:
+ MV_WRITE_8(data_reg+(where&0x3), *data);
+ break;
+ case PCI_ACCESS_WRITE_2:
+ MV_WRITE_16(data_reg+(where&0x3), *data);
+ break;
+ case PCI_ACCESS_WRITE_4:
+ MV_WRITE(data_reg, *data);
+ break;
+ case PCI_ACCESS_READ_1:
+ MV_READ_8( data_reg+(where&0x3), data);
+ break;
+ case PCI_ACCESS_READ_2:
+ MV_READ_16(data_reg+(where&0x3), data);
+ break;
+ case PCI_ACCESS_READ_4:
+ MV_READ(data_reg, data);
+ break;
+ default:
+ printk("no specify access type\n");
+ break;
+ }
+ return 0;
+}
+
+static int
+read_config_byte(struct pci_bus *bus, unsigned int devfn, int where, u8 * val)
+{
+ return config_access(PCI_ACCESS_READ_1, bus, devfn, (unsigned int)where, (u32 *)val);
+}
+
+static int
+read_config_word(struct pci_bus *bus, unsigned int devfn, int where, u16 * val)
+{
+ return config_access(PCI_ACCESS_READ_2, bus, devfn, (unsigned int)where, (u32 *)val);
+}
+
+static int
+read_config_dword(struct pci_bus *bus, unsigned int devfn, int where, u32 * val)
+{
+ return config_access(PCI_ACCESS_READ_4, bus, devfn, (unsigned int)where, (u32 *)val);
+}
+
+static int
+write_config_byte(struct pci_bus *bus, unsigned int devfn, int where, u8 val)
+{
+ if (config_access(PCI_ACCESS_WRITE_1, bus, devfn, (unsigned int)where, (u32 *)&val))
+ return -1;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+write_config_word(struct pci_bus *bus, unsigned int devfn, int where, u16 val)
+{
+ if (config_access(PCI_ACCESS_WRITE_2, bus, devfn, where, (u32 *)&val))
+ return -1;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+write_config_dword(struct pci_bus *bus, unsigned int devfn, int where, u32 val)
+{
+ if (config_access(PCI_ACCESS_WRITE_4, bus, devfn, where, &val))
+ return -1;
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+
+static int
+pci_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 * val)
+{
+ switch (size) {
+ case 1:
+ return read_config_byte(bus, devfn, where, (u8 *) val);
+ case 2:
+ return read_config_word(bus, devfn, where, (u16 *) val);
+ default:
+ return read_config_dword(bus, devfn, where, val);
+ }
+}
+
+static int
+pci_config_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val)
+{
+ switch (size) {
+ case 1:
+ return write_config_byte(bus, devfn, where, (u8) val);
+ case 2:
+ return write_config_word(bus, devfn, where, (u16) val);
+ default:
+ return write_config_dword(bus, devfn, where, val);
+ }
+}
+
+struct pci_ops mt7621_pci_ops= {
+ .read = pci_config_read,
+ .write = pci_config_write,
+};
+
+static struct resource mt7621_res_pci_mem1 = {
+ .name = "PCI MEM1",
+ .start = RALINK_PCI_MM_MAP_BASE,
+ .end = (u32)((RALINK_PCI_MM_MAP_BASE + (unsigned char *)0x0fffffff)),
+ .flags = IORESOURCE_MEM,
+};
+static struct resource mt7621_res_pci_io1 = {
+ .name = "PCI I/O1",
+ .start = RALINK_PCI_IO_MAP_BASE,
+ .end = (u32)((RALINK_PCI_IO_MAP_BASE + (unsigned char *)0x0ffff)),
+ .flags = IORESOURCE_IO,
+};
+
+static struct pci_controller mt7621_controller = {
+ .pci_ops = &mt7621_pci_ops,
+ .mem_resource = &mt7621_res_pci_mem1,
+ .io_resource = &mt7621_res_pci_io1,
+ .mem_offset = 0x00000000UL,
+ .io_offset = 0x00000000UL,
+ .io_map_base = 0xa0000000,
+};
+
+static void
+read_config(unsigned long bus, unsigned long dev, unsigned long func, unsigned long reg, unsigned long *val)
+{
+ unsigned int address_reg, data_reg, address;
+
+ address_reg = RALINK_PCI_CONFIG_ADDR;
+ data_reg = RALINK_PCI_CONFIG_DATA_VIRTUAL_REG;
+ address = (((reg & 0xF00)>>8)<<24) | (bus << 16) | (dev << 11) | (func << 8) | (reg & 0xfc) | 0x80000000 ;
+ MV_WRITE(address_reg, address);
+ MV_READ(data_reg, val);
+ return;
+}
+
+static void
+write_config(unsigned long bus, unsigned long dev, unsigned long func, unsigned long reg, unsigned long val)
+{
+ unsigned int address_reg, data_reg, address;
+
+ address_reg = RALINK_PCI_CONFIG_ADDR;
+ data_reg = RALINK_PCI_CONFIG_DATA_VIRTUAL_REG;
+ address = (((reg & 0xF00)>>8)<<24) | (bus << 16) | (dev << 11) | (func << 8) | (reg & 0xfc) | 0x80000000 ;
+ MV_WRITE(address_reg, address);
+ MV_WRITE(data_reg, val);
+ return;
+}
+
+
+int
+pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ u16 cmd;
+ u32 val;
+ int irq = 0;
+
+ if ((dev->bus->number == 0) && (slot == 0)) {
+ write_config(0, 0, 0, PCI_BASE_ADDRESS_0, MEMORY_BASE);
+ read_config(0, 0, 0, PCI_BASE_ADDRESS_0, (unsigned long *)&val);
+ printk("BAR0 at slot 0 = %x\n", val);
+ printk("bus=0x%x, slot = 0x%x\n",dev->bus->number, slot);
+ } else if((dev->bus->number == 0) && (slot == 0x1)) {
+ write_config(0, 1, 0, PCI_BASE_ADDRESS_0, MEMORY_BASE);
+ read_config(0, 1, 0, PCI_BASE_ADDRESS_0, (unsigned long *)&val);
+ printk("BAR0 at slot 1 = %x\n", val);
+ printk("bus=0x%x, slot = 0x%x\n",dev->bus->number, slot);
+ } else if((dev->bus->number == 0) && (slot == 0x2)) {
+ write_config(0, 2, 0, PCI_BASE_ADDRESS_0, MEMORY_BASE);
+ read_config(0, 2, 0, PCI_BASE_ADDRESS_0, (unsigned long *)&val);
+ printk("BAR0 at slot 2 = %x\n", val);
+ printk("bus=0x%x, slot = 0x%x\n",dev->bus->number, slot);
+ } else if ((dev->bus->number == 1) && (slot == 0x0)) {
+ switch (pcie_link_status) {
+ case 2:
+ case 6:
+ irq = RALINK_INT_PCIE1;
+ break;
+ case 4:
+ irq = RALINK_INT_PCIE2;
+ break;
+ default:
+ irq = RALINK_INT_PCIE0;
+ }
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
+ } else if ((dev->bus->number == 2) && (slot == 0x0)) {
+ switch (pcie_link_status) {
+ case 5:
+ case 6:
+ irq = RALINK_INT_PCIE2;
+ break;
+ default:
+ irq = RALINK_INT_PCIE1;
+ }
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
+ } else if ((dev->bus->number == 2) && (slot == 0x1)) {
+ switch (pcie_link_status) {
+ case 5:
+ case 6:
+ irq = RALINK_INT_PCIE2;
+ break;
+ default:
+ irq = RALINK_INT_PCIE1;
+ }
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
+ } else if ((dev->bus->number ==3) && (slot == 0x0)) {
+ irq = RALINK_INT_PCIE2;
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
+ } else if ((dev->bus->number ==3) && (slot == 0x1)) {
+ irq = RALINK_INT_PCIE2;
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
+ } else if ((dev->bus->number ==3) && (slot == 0x2)) {
+ irq = RALINK_INT_PCIE2;
+ printk("bus=0x%x, slot = 0x%x, irq=0x%x\n",dev->bus->number, slot, dev->irq);
+ } else {
+ printk("bus=0x%x, slot = 0x%x\n",dev->bus->number, slot);
+ return 0;
+ }
+
+ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x14); //configure cache line size 0x14
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xFF); //configure latency timer 0x10
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ cmd = cmd | PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+#ifdef CONFIG_DTB_GNUBEE1
+ /*
+ * 'irq' here is a hwirq, but a virq is needed. Until we know how and where
+ * to convert one to the other, we have this hack for the GNUBEE1
+ */
+ return irq == 11 ? 22 : irq;
+#else
+ return irq;
+#endif
+}
+
+void
+set_pcie_phy(u32 *addr, int start_b, int bits, int val)
+{
+// printk("0x%p:", addr);
+// printk(" %x", *addr);
+ *(unsigned int *)(addr) &= ~(((1<<bits) - 1)<<start_b);
+ *(unsigned int *)(addr) |= val << start_b;
+// printk(" -> %x\n", *addr);
+}
+
+void
+bypass_pipe_rst(void)
+{
+#if defined (CONFIG_PCIE_PORT0)
+ /* PCIe Port 0 */
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x02c), 12, 1, 0x01); // rg_pe1_pipe_rst_b
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x02c), 4, 1, 0x01); // rg_pe1_pipe_cmd_frc[4]
+#endif
+#if defined (CONFIG_PCIE_PORT1)
+ /* PCIe Port 1 */
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x12c), 12, 1, 0x01); // rg_pe1_pipe_rst_b
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x12c), 4, 1, 0x01); // rg_pe1_pipe_cmd_frc[4]
+#endif
+#if defined (CONFIG_PCIE_PORT2)
+ /* PCIe Port 2 */
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x02c), 12, 1, 0x01); // rg_pe1_pipe_rst_b
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x02c), 4, 1, 0x01); // rg_pe1_pipe_cmd_frc[4]
+#endif
+}
+
+void
+set_phy_for_ssc(void)
+{
+ unsigned long reg = (*(volatile u32 *)(RALINK_SYSCTL_BASE + 0x10));
+
+ reg = (reg >> 6) & 0x7;
+#if defined (CONFIG_PCIE_PORT0) || defined (CONFIG_PCIE_PORT1)
+ /* Set PCIe Port0 & Port1 PHY to disable SSC */
+ /* Debug Xtal Type */
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x400), 8, 1, 0x01); // rg_pe1_frc_h_xtal_type
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x400), 9, 2, 0x00); // rg_pe1_h_xtal_type
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x000), 4, 1, 0x01); // rg_pe1_frc_phy_en //Force Port 0 enable control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x100), 4, 1, 0x01); // rg_pe1_frc_phy_en //Force Port 1 enable control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x000), 5, 1, 0x00); // rg_pe1_phy_en //Port 0 disable
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x100), 5, 1, 0x00); // rg_pe1_phy_en //Port 1 disable
+ if(reg <= 5 && reg >= 3) { // 40MHz Xtal
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 6, 2, 0x01); // RG_PE1_H_PLL_PREDIV //Pre-divider ratio (for host mode)
+ printk("***** Xtal 40MHz *****\n");
+ } else { // 25MHz | 20MHz Xtal
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 6, 2, 0x00); // RG_PE1_H_PLL_PREDIV //Pre-divider ratio (for host mode)
+ if (reg >= 6) {
+ printk("***** Xtal 25MHz *****\n");
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4bc), 4, 2, 0x01); // RG_PE1_H_PLL_FBKSEL //Feedback clock select
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x49c), 0,31, 0x18000000); // RG_PE1_H_LCDDS_PCW_NCPO //DDS NCPO PCW (for host mode)
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4a4), 0,16, 0x18d); // RG_PE1_H_LCDDS_SSC_PRD //DDS SSC dither period control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4a8), 0,12, 0x4a); // RG_PE1_H_LCDDS_SSC_DELTA //DDS SSC dither amplitude control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4a8), 16,12, 0x4a); // RG_PE1_H_LCDDS_SSC_DELTA1 //DDS SSC dither amplitude control for initial
+ } else {
+ printk("***** Xtal 20MHz *****\n");
+ }
+ }
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4a0), 5, 1, 0x01); // RG_PE1_LCDDS_CLK_PH_INV //DDS clock inversion
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 22, 2, 0x02); // RG_PE1_H_PLL_BC
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 18, 4, 0x06); // RG_PE1_H_PLL_BP
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 12, 4, 0x02); // RG_PE1_H_PLL_IR
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 8, 4, 0x01); // RG_PE1_H_PLL_IC
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x4ac), 16, 3, 0x00); // RG_PE1_H_PLL_BR
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x490), 1, 3, 0x02); // RG_PE1_PLL_DIVEN
+ if(reg <= 5 && reg >= 3) { // 40MHz Xtal
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x414), 6, 2, 0x01); // rg_pe1_mstckdiv //value of da_pe1_mstckdiv when force mode enable
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x414), 5, 1, 0x01); // rg_pe1_frc_mstckdiv //force mode enable of da_pe1_mstckdiv
+ }
+ /* Enable PHY and disable force mode */
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x000), 5, 1, 0x01); // rg_pe1_phy_en //Port 0 enable
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x100), 5, 1, 0x01); // rg_pe1_phy_en //Port 1 enable
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x000), 4, 1, 0x00); // rg_pe1_frc_phy_en //Force Port 0 disable control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P0P1_CTL_OFFSET + 0x100), 4, 1, 0x00); // rg_pe1_frc_phy_en //Force Port 1 disable control
+#endif
+#if defined (CONFIG_PCIE_PORT2)
+ /* Set PCIe Port2 PHY to disable SSC */
+ /* Debug Xtal Type */
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x400), 8, 1, 0x01); // rg_pe1_frc_h_xtal_type
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x400), 9, 2, 0x00); // rg_pe1_h_xtal_type
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x000), 4, 1, 0x01); // rg_pe1_frc_phy_en //Force Port 0 enable control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x000), 5, 1, 0x00); // rg_pe1_phy_en //Port 0 disable
+ if(reg <= 5 && reg >= 3) { // 40MHz Xtal
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 6, 2, 0x01); // RG_PE1_H_PLL_PREDIV //Pre-divider ratio (for host mode)
+ } else { // 25MHz | 20MHz Xtal
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 6, 2, 0x00); // RG_PE1_H_PLL_PREDIV //Pre-divider ratio (for host mode)
+ if (reg >= 6) { // 25MHz Xtal
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4bc), 4, 2, 0x01); // RG_PE1_H_PLL_FBKSEL //Feedback clock select
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x49c), 0,31, 0x18000000); // RG_PE1_H_LCDDS_PCW_NCPO //DDS NCPO PCW (for host mode)
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4a4), 0,16, 0x18d); // RG_PE1_H_LCDDS_SSC_PRD //DDS SSC dither period control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4a8), 0,12, 0x4a); // RG_PE1_H_LCDDS_SSC_DELTA //DDS SSC dither amplitude control
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4a8), 16,12, 0x4a); // RG_PE1_H_LCDDS_SSC_DELTA1 //DDS SSC dither amplitude control for initial
+ }
+ }
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4a0), 5, 1, 0x01); // RG_PE1_LCDDS_CLK_PH_INV //DDS clock inversion
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 22, 2, 0x02); // RG_PE1_H_PLL_BC
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 18, 4, 0x06); // RG_PE1_H_PLL_BP
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 12, 4, 0x02); // RG_PE1_H_PLL_IR
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 8, 4, 0x01); // RG_PE1_H_PLL_IC
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x4ac), 16, 3, 0x00); // RG_PE1_H_PLL_BR
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x490), 1, 3, 0x02); // RG_PE1_PLL_DIVEN
+ if(reg <= 5 && reg >= 3) { // 40MHz Xtal
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x414), 6, 2, 0x01); // rg_pe1_mstckdiv //value of da_pe1_mstckdiv when force mode enable
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x414), 5, 1, 0x01); // rg_pe1_frc_mstckdiv //force mode enable of da_pe1_mstckdiv
+ }
+ /* Enable PHY and disable force mode */
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x000), 5, 1, 0x01); // rg_pe1_phy_en //Port 0 enable
+ set_pcie_phy((u32 *)(RALINK_PCIEPHY_P2_CTL_OFFSET + 0x000), 4, 1, 0x00); // rg_pe1_frc_phy_en //Force Port 0 disable control
+#endif
+}
+
+void setup_cm_memory_region(struct resource *mem_resource)
+{
+ resource_size_t mask;
+ if (mips_cps_numiocu(0)) {
+ /* FIXME: hardware doesn't accept mask values with 1s after
+ 0s (e.g. 0xffef), so it would be great to warn if that's
+ about to happen */
+ mask = ~(mem_resource->end - mem_resource->start);
+
+ write_gcr_reg1_base(mem_resource->start);
+ write_gcr_reg1_mask(mask | CM_GCR_REGn_MASK_CMTGT_IOCU0);
+ printk("PCI coherence region base: 0x%08llx, mask/settings: 0x%08llx\n",
+ (unsigned long long)read_gcr_reg1_base(),
+ (unsigned long long)read_gcr_reg1_mask());
+ }
+}
+
+static int mt7621_pci_probe(struct platform_device *pdev)
+{
+ unsigned long val = 0;
+
+ iomem_resource.start = 0;
+ iomem_resource.end= ~0;
+ ioport_resource.start= 0;
+ ioport_resource.end = ~0;
+
+#if defined (CONFIG_PCIE_PORT0)
+ val = RALINK_PCIE0_RST;
+#endif
+#if defined (CONFIG_PCIE_PORT1)
+ val |= RALINK_PCIE1_RST;
+#endif
+#if defined (CONFIG_PCIE_PORT2)
+ val |= RALINK_PCIE2_RST;
+#endif
+ ASSERT_SYSRST_PCIE(RALINK_PCIE0_RST | RALINK_PCIE1_RST | RALINK_PCIE2_RST);
+ printk("pull PCIe RST: RALINK_RSTCTRL = %x\n", RALINK_RSTCTRL);
+#if defined GPIO_PERST /* add GPIO control instead of PERST_N */ /*chhung*/
+ *(unsigned int *)(0xbe000060) &= ~(0x3<<10 | 0x3<<3);
+ *(unsigned int *)(0xbe000060) |= 0x1<<10 | 0x1<<3;
+ mdelay(100);
+ *(unsigned int *)(0xbe000600) |= 0x1<<19 | 0x1<<8 | 0x1<<7; // use GPIO19/GPIO8/GPIO7 (PERST_N/UART_RXD3/UART_TXD3)
+ mdelay(100);
+ *(unsigned int *)(0xbe000620) &= ~(0x1<<19 | 0x1<<8 | 0x1<<7); // clear DATA
+
+ mdelay(100);
+#else
+ *(unsigned int *)(0xbe000060) &= ~0x00000c00;
+#endif
+#if defined (CONFIG_PCIE_PORT0)
+ val = RALINK_PCIE0_RST;
+#endif
+#if defined (CONFIG_PCIE_PORT1)
+ val |= RALINK_PCIE1_RST;
+#endif
+#if defined (CONFIG_PCIE_PORT2)
+ val |= RALINK_PCIE2_RST;
+#endif
+ DEASSERT_SYSRST_PCIE(val);
+ printk("release PCIe RST: RALINK_RSTCTRL = %x\n", RALINK_RSTCTRL);
+
+ if ((*(unsigned int *)(0xbe00000c)&0xFFFF) == 0x0101) // MT7621 E2
+ bypass_pipe_rst();
+ set_phy_for_ssc();
+ printk("release PCIe RST: RALINK_RSTCTRL = %x\n", RALINK_RSTCTRL);
+
+#if defined (CONFIG_PCIE_PORT0)
+ read_config(0, 0, 0, 0x70c, &val);
+ printk("Port 0 N_FTS = %x\n", (unsigned int)val);
+#endif
+#if defined (CONFIG_PCIE_PORT1)
+ read_config(0, 1, 0, 0x70c, &val);
+ printk("Port 1 N_FTS = %x\n", (unsigned int)val);
+#endif
+#if defined (CONFIG_PCIE_PORT2)
+ read_config(0, 2, 0, 0x70c, &val);
+ printk("Port 2 N_FTS = %x\n", (unsigned int)val);
+#endif
+
+ RALINK_RSTCTRL = (RALINK_RSTCTRL | RALINK_PCIE_RST);
+ RALINK_SYSCFG1 &= ~(0x30);
+ RALINK_SYSCFG1 |= (2<<4);
+ RALINK_PCIE_CLK_GEN &= 0x7fffffff;
+ RALINK_PCIE_CLK_GEN1 &= 0x80ffffff;
+ RALINK_PCIE_CLK_GEN1 |= 0xa << 24;
+ RALINK_PCIE_CLK_GEN |= 0x80000000;
+ mdelay(50);
+ RALINK_RSTCTRL = (RALINK_RSTCTRL & ~RALINK_PCIE_RST);
+
+
+#if defined GPIO_PERST /* add GPIO control instead of PERST_N */ /*chhung*/
+ *(unsigned int *)(0xbe000620) |= 0x1<<19 | 0x1<<8 | 0x1<<7; // set DATA
+ mdelay(100);
+#else
+ RALINK_PCI_PCICFG_ADDR &= ~(1<<1); //de-assert PERST
+#endif
+ mdelay(500);
+
+
+ mdelay(500);
+#if defined (CONFIG_PCIE_PORT0)
+ if(( RALINK_PCI0_STATUS & 0x1) == 0)
+ {
+ printk("PCIE0 no card, disable it(RST&CLK)\n");
+ ASSERT_SYSRST_PCIE(RALINK_PCIE0_RST);
+ RALINK_CLKCFG1 = (RALINK_CLKCFG1 & ~RALINK_PCIE0_CLK_EN);
+ pcie_link_status &= ~(1<<0);
+ } else {
+ pcie_link_status |= 1<<0;
+ RALINK_PCI_PCIMSK_ADDR |= (1<<20); // enable pcie1 interrupt
+ }
+#endif
+#if defined (CONFIG_PCIE_PORT1)
+ if(( RALINK_PCI1_STATUS & 0x1) == 0)
+ {
+ printk("PCIE1 no card, disable it(RST&CLK)\n");
+ ASSERT_SYSRST_PCIE(RALINK_PCIE1_RST);
+ RALINK_CLKCFG1 = (RALINK_CLKCFG1 & ~RALINK_PCIE1_CLK_EN);
+ pcie_link_status &= ~(1<<1);
+ } else {
+ pcie_link_status |= 1<<1;
+ RALINK_PCI_PCIMSK_ADDR |= (1<<21); // enable pcie1 interrupt
+ }
+#endif
+#if defined (CONFIG_PCIE_PORT2)
+ if (( RALINK_PCI2_STATUS & 0x1) == 0) {
+ printk("PCIE2 no card, disable it(RST&CLK)\n");
+ ASSERT_SYSRST_PCIE(RALINK_PCIE2_RST);
+ RALINK_CLKCFG1 = (RALINK_CLKCFG1 & ~RALINK_PCIE2_CLK_EN);
+ pcie_link_status &= ~(1<<2);
+ } else {
+ pcie_link_status |= 1<<2;
+ RALINK_PCI_PCIMSK_ADDR |= (1<<22); // enable pcie2 interrupt
+ }
+#endif
+ if (pcie_link_status == 0)
+ return 0;
+
+/*
+pcie(2/1/0) link status pcie2_num pcie1_num pcie0_num
+3'b000 x x x
+3'b001 x x 0
+3'b010 x 0 x
+3'b011 x 1 0
+3'b100 0 x x
+3'b101 1 x 0
+3'b110 1 0 x
+3'b111 2 1 0
+*/
+ switch(pcie_link_status) {
+ case 2:
+ RALINK_PCI_PCICFG_ADDR &= ~0x00ff0000;
+ RALINK_PCI_PCICFG_ADDR |= 0x1 << 16; //port0
+ RALINK_PCI_PCICFG_ADDR |= 0x0 << 20; //port1
+ break;
+ case 4:
+ RALINK_PCI_PCICFG_ADDR &= ~0x0fff0000;
+ RALINK_PCI_PCICFG_ADDR |= 0x1 << 16; //port0
+ RALINK_PCI_PCICFG_ADDR |= 0x2 << 20; //port1
+ RALINK_PCI_PCICFG_ADDR |= 0x0 << 24; //port2
+ break;
+ case 5:
+ RALINK_PCI_PCICFG_ADDR &= ~0x0fff0000;
+ RALINK_PCI_PCICFG_ADDR |= 0x0 << 16; //port0
+ RALINK_PCI_PCICFG_ADDR |= 0x2 << 20; //port1
+ RALINK_PCI_PCICFG_ADDR |= 0x1 << 24; //port2
+ break;
+ case 6:
+ RALINK_PCI_PCICFG_ADDR &= ~0x0fff0000;
+ RALINK_PCI_PCICFG_ADDR |= 0x2 << 16; //port0
+ RALINK_PCI_PCICFG_ADDR |= 0x0 << 20; //port1
+ RALINK_PCI_PCICFG_ADDR |= 0x1 << 24; //port2
+ break;
+ }
+ printk(" -> %x\n", RALINK_PCI_PCICFG_ADDR);
+ //printk(" RALINK_PCI_ARBCTL = %x\n", RALINK_PCI_ARBCTL);
+
+/*
+ ioport_resource.start = mt7621_res_pci_io1.start;
+ ioport_resource.end = mt7621_res_pci_io1.end;
+*/
+
+ RALINK_PCI_MEMBASE = 0xffffffff; //RALINK_PCI_MM_MAP_BASE;
+ RALINK_PCI_IOBASE = RALINK_PCI_IO_MAP_BASE;
+
+#if defined (CONFIG_PCIE_PORT0)
+ //PCIe0
+ if((pcie_link_status & 0x1) != 0) {
+ RALINK_PCI0_BAR0SETUP_ADDR = 0x7FFF0001; //open 7FFF:2G; ENABLE
+ RALINK_PCI0_IMBASEBAR0_ADDR = MEMORY_BASE;
+ RALINK_PCI0_CLASS = 0x06040001;
+ printk("PCIE0 enabled\n");
+ }
+#endif
+#if defined (CONFIG_PCIE_PORT1)
+ //PCIe1
+ if ((pcie_link_status & 0x2) != 0) {
+ RALINK_PCI1_BAR0SETUP_ADDR = 0x7FFF0001; //open 7FFF:2G; ENABLE
+ RALINK_PCI1_IMBASEBAR0_ADDR = MEMORY_BASE;
+ RALINK_PCI1_CLASS = 0x06040001;
+ printk("PCIE1 enabled\n");
+ }
+#endif
+#if defined (CONFIG_PCIE_PORT2)
+ //PCIe2
+ if ((pcie_link_status & 0x4) != 0) {
+ RALINK_PCI2_BAR0SETUP_ADDR = 0x7FFF0001; //open 7FFF:2G; ENABLE
+ RALINK_PCI2_IMBASEBAR0_ADDR = MEMORY_BASE;
+ RALINK_PCI2_CLASS = 0x06040001;
+ printk("PCIE2 enabled\n");
+ }
+#endif
+
+
+ switch(pcie_link_status) {
+ case 7:
+ read_config(0, 2, 0, 0x4, &val);
+ write_config(0, 2, 0, 0x4, val|0x4);
+ // write_config(0, 1, 0, 0x4, val|0x7);
+ read_config(0, 2, 0, 0x70c, &val);
+ val &= ~(0xff)<<8;
+ val |= 0x50<<8;
+ write_config(0, 2, 0, 0x70c, val);
+ case 3:
+ case 5:
+ case 6:
+ read_config(0, 1, 0, 0x4, &val);
+ write_config(0, 1, 0, 0x4, val|0x4);
+ // write_config(0, 1, 0, 0x4, val|0x7);
+ read_config(0, 1, 0, 0x70c, &val);
+ val &= ~(0xff)<<8;
+ val |= 0x50<<8;
+ write_config(0, 1, 0, 0x70c, val);
+ default:
+ read_config(0, 0, 0, 0x4, &val);
+ write_config(0, 0, 0, 0x4, val|0x4); //bus master enable
+ // write_config(0, 0, 0, 0x4, val|0x7); //bus master enable
+ read_config(0, 0, 0, 0x70c, &val);
+ val &= ~(0xff)<<8;
+ val |= 0x50<<8;
+ write_config(0, 0, 0, 0x70c, val);
+ }
+
+ pci_load_of_ranges(&mt7621_controller, pdev->dev.of_node);
+ setup_cm_memory_region(mt7621_controller.mem_resource);
+ register_pci_controller(&mt7621_controller);
+ return 0;
+
+}
+
+int pcibios_plat_dev_init(struct pci_dev *dev)
+{
+ return 0;
+}
+
+static const struct of_device_id mt7621_pci_ids[] = {
+ { .compatible = "mediatek,mt7621-pci" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt7621_pci_ids);
+
+static struct platform_driver mt7621_pci_driver = {
+ .probe = mt7621_pci_probe,
+ .driver = {
+ .name = "mt7621-pci",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(mt7621_pci_ids),
+ },
+};
+
+static int __init mt7621_pci_init(void)
+{
+ return platform_driver_register(&mt7621_pci_driver);
+}
+
+arch_initcall(mt7621_pci_init);
From: John Crispin <[email protected]>
NeilBrown:
The code will fail with a warning if asked to transfer
more than 32 bytes at a time. So used max_transfer_size
interface to tell users about this.
Signed-off-by: John Crispin <[email protected]>
Signed-off-by: NeilBrown <[email protected]>
---
drivers/staging/Kconfig | 2
drivers/staging/Makefile | 1
drivers/staging/mt7621-spi/Kconfig | 6
drivers/staging/mt7621-spi/Makefile | 1
drivers/staging/mt7621-spi/TODO | 5
drivers/staging/mt7621-spi/spi-mt7621.c | 489 +++++++++++++++++++++++++++++++
6 files changed, 504 insertions(+)
create mode 100644 drivers/staging/mt7621-spi/Kconfig
create mode 100644 drivers/staging/mt7621-spi/Makefile
create mode 100644 drivers/staging/mt7621-spi/TODO
create mode 100644 drivers/staging/mt7621-spi/spi-mt7621.c
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 7568e10399fa..bcc6da1f656a 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -126,4 +126,6 @@ source "drivers/staging/mt7621-pinctrl/Kconfig"
source "drivers/staging/mt7621-gpio/Kconfig"
+source "drivers/staging/mt7621-spi/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 609fdb218985..051af9965549 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -55,3 +55,4 @@ obj-$(CONFIG_PI433) += pi433/
obj-$(CONFIG_SOC_MT7621) += mt7621-pci/
obj-$(CONFIG_SOC_MT7621) += mt7621-pinctrl/
obj-$(CONFIG_SOC_MT7621) += mt7621-gpio/
+obj-$(CONFIG_SOC_MT7621) += mt7621-spi/
diff --git a/drivers/staging/mt7621-spi/Kconfig b/drivers/staging/mt7621-spi/Kconfig
new file mode 100644
index 000000000000..0b90f4cfa426
--- /dev/null
+++ b/drivers/staging/mt7621-spi/Kconfig
@@ -0,0 +1,6 @@
+config SPI_MT7621
+ tristate "MediaTek MT7621 SPI Controller"
+ depends on RALINK
+ help
+ This selects a driver for the MediaTek MT7621 SPI Controller.
+
diff --git a/drivers/staging/mt7621-spi/Makefile b/drivers/staging/mt7621-spi/Makefile
new file mode 100644
index 000000000000..3be508f63bac
--- /dev/null
+++ b/drivers/staging/mt7621-spi/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SPI_MT7621) += spi-mt7621.o
diff --git a/drivers/staging/mt7621-spi/TODO b/drivers/staging/mt7621-spi/TODO
new file mode 100644
index 000000000000..fdbc5002c32a
--- /dev/null
+++ b/drivers/staging/mt7621-spi/TODO
@@ -0,0 +1,5 @@
+
+- general code review and clean up
+- ensure device-tree requirements are documented
+
+Cc: NeilBrown <[email protected]>
diff --git a/drivers/staging/mt7621-spi/spi-mt7621.c b/drivers/staging/mt7621-spi/spi-mt7621.c
new file mode 100644
index 000000000000..d95e0b32f1f0
--- /dev/null
+++ b/drivers/staging/mt7621-spi/spi-mt7621.c
@@ -0,0 +1,489 @@
+/*
+ * spi-mt7621.c -- MediaTek MT7621 SPI controller driver
+ *
+ * Copyright (C) 2011 Sergiy <[email protected]>
+ * Copyright (C) 2011-2013 Gabor Juhos <[email protected]>
+ * Copyright (C) 2014-2015 Felix Fietkau <[email protected]>
+ *
+ * Some parts are based on spi-orion.c:
+ * Author: Shadi Ammouri <[email protected]>
+ * Copyright (C) 2007-2008 Marvell Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/swab.h>
+
+#include <ralink_regs.h>
+
+#define SPI_BPW_MASK(bits) BIT((bits) - 1)
+
+#define DRIVER_NAME "spi-mt7621"
+/* in usec */
+#define RALINK_SPI_WAIT_MAX_LOOP 2000
+
+/* SPISTAT register bit field */
+#define SPISTAT_BUSY BIT(0)
+
+#define MT7621_SPI_TRANS 0x00
+#define SPITRANS_BUSY BIT(16)
+
+#define MT7621_SPI_OPCODE 0x04
+#define MT7621_SPI_DATA0 0x08
+#define MT7621_SPI_DATA4 0x18
+#define SPI_CTL_TX_RX_CNT_MASK 0xff
+#define SPI_CTL_START BIT(8)
+
+#define MT7621_SPI_POLAR 0x38
+#define MT7621_SPI_MASTER 0x28
+#define MT7621_SPI_MOREBUF 0x2c
+#define MT7621_SPI_SPACE 0x3c
+
+#define MT7621_CPHA BIT(5)
+#define MT7621_CPOL BIT(4)
+#define MT7621_LSB_FIRST BIT(3)
+
+#define RT2880_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH)
+
+struct mt7621_spi;
+
+struct mt7621_spi {
+ struct spi_master *master;
+ void __iomem *base;
+ unsigned int sys_freq;
+ unsigned int speed;
+ struct clk *clk;
+ spinlock_t lock;
+
+ struct mt7621_spi_ops *ops;
+};
+
+static inline struct mt7621_spi *spidev_to_mt7621_spi(struct spi_device *spi)
+{
+ return spi_master_get_devdata(spi->master);
+}
+
+static inline u32 mt7621_spi_read(struct mt7621_spi *rs, u32 reg)
+{
+ return ioread32(rs->base + reg);
+}
+
+static inline void mt7621_spi_write(struct mt7621_spi *rs, u32 reg, u32 val)
+{
+ iowrite32(val, rs->base + reg);
+}
+
+static void mt7621_spi_reset(struct mt7621_spi *rs, int duplex)
+{
+ u32 master = mt7621_spi_read(rs, MT7621_SPI_MASTER);
+
+ master |= 7 << 29;
+ master |= 1 << 2;
+ if (duplex)
+ master |= 1 << 10;
+ else
+ master &= ~(1 << 10);
+
+ mt7621_spi_write(rs, MT7621_SPI_MASTER, master);
+}
+
+static void mt7621_spi_set_cs(struct spi_device *spi, int enable)
+{
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
+ int cs = spi->chip_select;
+ u32 polar = 0;
+
+ mt7621_spi_reset(rs, cs);
+ if (enable)
+ polar = BIT(cs);
+ mt7621_spi_write(rs, MT7621_SPI_POLAR, polar);
+}
+
+static int mt7621_spi_prepare(struct spi_device *spi, unsigned int speed)
+{
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
+ u32 rate;
+ u32 reg;
+
+ dev_dbg(&spi->dev, "speed:%u\n", speed);
+
+ rate = DIV_ROUND_UP(rs->sys_freq, speed);
+ dev_dbg(&spi->dev, "rate-1:%u\n", rate);
+
+ if (rate > 4097)
+ return -EINVAL;
+
+ if (rate < 2)
+ rate = 2;
+
+ reg = mt7621_spi_read(rs, MT7621_SPI_MASTER);
+ reg &= ~(0xfff << 16);
+ reg |= (rate - 2) << 16;
+ rs->speed = speed;
+
+ reg &= ~MT7621_LSB_FIRST;
+ if (spi->mode & SPI_LSB_FIRST)
+ reg |= MT7621_LSB_FIRST;
+
+ reg &= ~(MT7621_CPHA | MT7621_CPOL);
+ switch(spi->mode & (SPI_CPOL | SPI_CPHA)) {
+ case SPI_MODE_0:
+ break;
+ case SPI_MODE_1:
+ reg |= MT7621_CPHA;
+ break;
+ case SPI_MODE_2:
+ reg |= MT7621_CPOL;
+ break;
+ case SPI_MODE_3:
+ reg |= MT7621_CPOL | MT7621_CPHA;
+ break;
+ }
+ mt7621_spi_write(rs, MT7621_SPI_MASTER, reg);
+
+ return 0;
+}
+
+static inline int mt7621_spi_wait_till_ready(struct spi_device *spi)
+{
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
+ int i;
+
+ for (i = 0; i < RALINK_SPI_WAIT_MAX_LOOP; i++) {
+ u32 status;
+
+ status = mt7621_spi_read(rs, MT7621_SPI_TRANS);
+ if ((status & SPITRANS_BUSY) == 0) {
+ return 0;
+ }
+ cpu_relax();
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int mt7621_spi_transfer_half_duplex(struct spi_master *master,
+ struct spi_message *m)
+{
+ struct mt7621_spi *rs = spi_master_get_devdata(master);
+ struct spi_device *spi = m->spi;
+ unsigned int speed = spi->max_speed_hz;
+ struct spi_transfer *t = NULL;
+ int status = 0;
+ int i, len = 0;
+ int rx_len = 0;
+ u32 data[9] = { 0 };
+ u32 val;
+
+ mt7621_spi_wait_till_ready(spi);
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ const u8 *buf = t->tx_buf;
+
+ if (t->rx_buf)
+ rx_len += t->len;
+
+ if (!buf)
+ continue;
+
+ if (t->speed_hz < speed)
+ speed = t->speed_hz;
+
+ if (WARN_ON(len + t->len > 36)) {
+ status = -EIO;
+ goto msg_done;
+ }
+
+ for (i = 0; i < t->len; i++, len++)
+ data[len / 4] |= buf[i] << (8 * (len & 3));
+ }
+
+ if (WARN_ON(rx_len > 32)) {
+ status = -EIO;
+ goto msg_done;
+ }
+
+ if (mt7621_spi_prepare(spi, speed)) {
+ status = -EIO;
+ goto msg_done;
+ }
+ data[0] = swab32(data[0]);
+ if (len < 4)
+ data[0] >>= (4 - len) * 8;
+
+ for (i = 0; i < len; i += 4)
+ mt7621_spi_write(rs, MT7621_SPI_OPCODE + i, data[i / 4]);
+
+ val = (min_t(int, len, 4) * 8) << 24;
+ if (len > 4)
+ val |= (len - 4) * 8;
+ val |= (rx_len * 8) << 12;
+ mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
+
+ mt7621_spi_set_cs(spi, 1);
+
+ val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
+ val |= SPI_CTL_START;
+ mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
+
+ mt7621_spi_wait_till_ready(spi);
+
+ mt7621_spi_set_cs(spi, 0);
+
+ for (i = 0; i < rx_len; i += 4)
+ data[i / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA0 + i);
+
+ m->actual_length = len + rx_len;
+
+ len = 0;
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ u8 *buf = t->rx_buf;
+
+ if (!buf)
+ continue;
+
+ for (i = 0; i < t->len; i++, len++)
+ buf[i] = data[len / 4] >> (8 * (len & 3));
+ }
+
+msg_done:
+ m->status = status;
+ spi_finalize_current_message(master);
+
+ return 0;
+}
+
+static int mt7621_spi_transfer_full_duplex(struct spi_master *master,
+ struct spi_message *m)
+{
+ struct mt7621_spi *rs = spi_master_get_devdata(master);
+ struct spi_device *spi = m->spi;
+ unsigned int speed = spi->max_speed_hz;
+ struct spi_transfer *t = NULL;
+ int status = 0;
+ int i, len = 0;
+ int rx_len = 0;
+ u32 data[9] = { 0 };
+ u32 val = 0;
+
+ mt7621_spi_wait_till_ready(spi);
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ const u8 *buf = t->tx_buf;
+
+ if (t->rx_buf)
+ rx_len += t->len;
+
+ if (!buf)
+ continue;
+
+ if (WARN_ON(len + t->len > 16)) {
+ status = -EIO;
+ goto msg_done;
+ }
+
+ for (i = 0; i < t->len; i++, len++)
+ data[len / 4] |= buf[i] << (8 * (len & 3));
+ if (speed > t->speed_hz)
+ speed = t->speed_hz;
+ }
+
+ if (WARN_ON(rx_len > 16)) {
+ status = -EIO;
+ goto msg_done;
+ }
+
+ if (mt7621_spi_prepare(spi, speed)) {
+ status = -EIO;
+ goto msg_done;
+ }
+
+ for (i = 0; i < len; i += 4)
+ mt7621_spi_write(rs, MT7621_SPI_DATA0 + i, data[i / 4]);
+
+ val |= len * 8;
+ val |= (rx_len * 8) << 12;
+ mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
+
+ mt7621_spi_set_cs(spi, 1);
+
+ val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
+ val |= SPI_CTL_START;
+ mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
+
+ mt7621_spi_wait_till_ready(spi);
+
+ mt7621_spi_set_cs(spi, 0);
+
+ for (i = 0; i < rx_len; i += 4)
+ data[i / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA4 + i);
+
+ m->actual_length = rx_len;
+
+ len = 0;
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ u8 *buf = t->rx_buf;
+
+ if (!buf)
+ continue;
+
+ for (i = 0; i < t->len; i++, len++)
+ buf[i] = data[len / 4] >> (8 * (len & 3));
+ }
+
+msg_done:
+ m->status = status;
+ spi_finalize_current_message(master);
+
+ return 0;
+}
+
+static int mt7621_spi_transfer_one_message(struct spi_master *master,
+ struct spi_message *m)
+{
+ struct spi_device *spi = m->spi;
+ int cs = spi->chip_select;
+
+ if (cs)
+ return mt7621_spi_transfer_full_duplex(master, m);
+ return mt7621_spi_transfer_half_duplex(master, m);
+}
+
+static int mt7621_spi_setup(struct spi_device *spi)
+{
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
+
+ if ((spi->max_speed_hz == 0) ||
+ (spi->max_speed_hz > (rs->sys_freq / 2)))
+ spi->max_speed_hz = (rs->sys_freq / 2);
+
+ if (spi->max_speed_hz < (rs->sys_freq / 4097)) {
+ dev_err(&spi->dev, "setup: requested speed is too low %d Hz\n",
+ spi->max_speed_hz);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id mt7621_spi_match[] = {
+ { .compatible = "ralink,mt7621-spi" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt7621_spi_match);
+
+static size_t max_transfer_size(struct spi_device *spi)
+{
+ return 32;
+}
+
+static int mt7621_spi_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct spi_master *master;
+ struct mt7621_spi *rs;
+ unsigned long flags;
+ void __iomem *base;
+ struct resource *r;
+ int status = 0;
+ struct clk *clk;
+ struct mt7621_spi_ops *ops;
+
+ match = of_match_device(mt7621_spi_match, &pdev->dev);
+ if (!match)
+ return -EINVAL;
+ ops = (struct mt7621_spi_ops *)match->data;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get SYS clock, err=%d\n",
+ status);
+ return PTR_ERR(clk);
+ }
+
+ status = clk_prepare_enable(clk);
+ if (status)
+ return status;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*rs));
+ if (master == NULL) {
+ dev_info(&pdev->dev, "master allocation failed\n");
+ return -ENOMEM;
+ }
+
+ master->mode_bits = RT2880_SPI_MODE_BITS;
+
+ master->setup = mt7621_spi_setup;
+ master->transfer_one_message = mt7621_spi_transfer_one_message;
+ master->bits_per_word_mask = SPI_BPW_MASK(8);
+ master->dev.of_node = pdev->dev.of_node;
+ master->num_chipselect = 2;
+ master->max_transfer_size = max_transfer_size;
+
+ dev_set_drvdata(&pdev->dev, master);
+
+ rs = spi_master_get_devdata(master);
+ rs->base = base;
+ rs->clk = clk;
+ rs->master = master;
+ rs->sys_freq = clk_get_rate(rs->clk);
+ rs->ops = ops;
+ dev_info(&pdev->dev, "sys_freq: %u\n", rs->sys_freq);
+ spin_lock_irqsave(&rs->lock, flags);
+
+ device_reset(&pdev->dev);
+
+ mt7621_spi_reset(rs, 0);
+
+ return spi_register_master(master);
+}
+
+static int mt7621_spi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master;
+ struct mt7621_spi *rs;
+
+ master = dev_get_drvdata(&pdev->dev);
+ rs = spi_master_get_devdata(master);
+
+ clk_disable(rs->clk);
+ spi_unregister_master(master);
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+
+static struct platform_driver mt7621_spi_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mt7621_spi_match,
+ },
+ .probe = mt7621_spi_probe,
+ .remove = mt7621_spi_remove,
+};
+
+module_platform_driver(mt7621_spi_driver);
+
+MODULE_DESCRIPTION("MT7621 SPI driver");
+MODULE_AUTHOR("Felix Fietkau <[email protected]>");
+MODULE_LICENSE("GPL");
From: John Crispin <[email protected]>
Signed-off-by: John Crispin <[email protected]>
Signed-off-by: NeilBrown <[email protected]>
---
drivers/staging/Kconfig | 2
drivers/staging/Makefile | 1
drivers/staging/mt7621-dma/Kconfig | 12
drivers/staging/mt7621-dma/Makefile | 4
drivers/staging/mt7621-dma/TODO | 5
drivers/staging/mt7621-dma/mtk-hsdma.c | 767 +++++++++++++++++++++++++
drivers/staging/mt7621-dma/ralink-gdma.c | 928 ++++++++++++++++++++++++++++++
7 files changed, 1719 insertions(+)
create mode 100644 drivers/staging/mt7621-dma/Kconfig
create mode 100644 drivers/staging/mt7621-dma/Makefile
create mode 100644 drivers/staging/mt7621-dma/TODO
create mode 100644 drivers/staging/mt7621-dma/mtk-hsdma.c
create mode 100644 drivers/staging/mt7621-dma/ralink-gdma.c
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index bcc6da1f656a..fa8cb95da178 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -128,4 +128,6 @@ source "drivers/staging/mt7621-gpio/Kconfig"
source "drivers/staging/mt7621-spi/Kconfig"
+source "drivers/staging/mt7621-dma/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 051af9965549..a577f3ac2dd0 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -56,3 +56,4 @@ obj-$(CONFIG_SOC_MT7621) += mt7621-pci/
obj-$(CONFIG_SOC_MT7621) += mt7621-pinctrl/
obj-$(CONFIG_SOC_MT7621) += mt7621-gpio/
obj-$(CONFIG_SOC_MT7621) += mt7621-spi/
+obj-$(CONFIG_SOC_MT7621) += mt7621-dma/
diff --git a/drivers/staging/mt7621-dma/Kconfig b/drivers/staging/mt7621-dma/Kconfig
new file mode 100644
index 000000000000..2423c40099d1
--- /dev/null
+++ b/drivers/staging/mt7621-dma/Kconfig
@@ -0,0 +1,12 @@
+config DMA_RALINK
+ tristate "RALINK DMA support"
+ depends on RALINK && !SOC_RT288X
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+
+config MTK_HSDMA
+ tristate "MTK HSDMA support"
+ depends on RALINK && SOC_MT7621
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+
diff --git a/drivers/staging/mt7621-dma/Makefile b/drivers/staging/mt7621-dma/Makefile
new file mode 100644
index 000000000000..d3152d45cf45
--- /dev/null
+++ b/drivers/staging/mt7621-dma/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_DMA_RALINK) += ralink-gdma.o
+obj-$(CONFIG_MTK_HSDMA) += mtk-hsdma.o
+
+ccflags-y += -I$(srctree)/drivers/dma
diff --git a/drivers/staging/mt7621-dma/TODO b/drivers/staging/mt7621-dma/TODO
new file mode 100644
index 000000000000..fdbc5002c32a
--- /dev/null
+++ b/drivers/staging/mt7621-dma/TODO
@@ -0,0 +1,5 @@
+
+- general code review and clean up
+- ensure device-tree requirements are documented
+
+Cc: NeilBrown <[email protected]>
diff --git a/drivers/staging/mt7621-dma/mtk-hsdma.c b/drivers/staging/mt7621-dma/mtk-hsdma.c
new file mode 100644
index 000000000000..5eba86e2d566
--- /dev/null
+++ b/drivers/staging/mt7621-dma/mtk-hsdma.c
@@ -0,0 +1,767 @@
+/*
+ * Copyright (C) 2015, Michael Lee <[email protected]>
+ * MTK HSDMA support
+ *
+ * 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.
+ *
+ */
+
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/irq.h>
+#include <linux/of_dma.h>
+#include <linux/reset.h>
+#include <linux/of_device.h>
+
+#include "virt-dma.h"
+
+#define HSDMA_BASE_OFFSET 0x800
+
+#define HSDMA_REG_TX_BASE 0x00
+#define HSDMA_REG_TX_CNT 0x04
+#define HSDMA_REG_TX_CTX 0x08
+#define HSDMA_REG_TX_DTX 0x0c
+#define HSDMA_REG_RX_BASE 0x100
+#define HSDMA_REG_RX_CNT 0x104
+#define HSDMA_REG_RX_CRX 0x108
+#define HSDMA_REG_RX_DRX 0x10c
+#define HSDMA_REG_INFO 0x200
+#define HSDMA_REG_GLO_CFG 0x204
+#define HSDMA_REG_RST_CFG 0x208
+#define HSDMA_REG_DELAY_INT 0x20c
+#define HSDMA_REG_FREEQ_THRES 0x210
+#define HSDMA_REG_INT_STATUS 0x220
+#define HSDMA_REG_INT_MASK 0x228
+#define HSDMA_REG_SCH_Q01 0x280
+#define HSDMA_REG_SCH_Q23 0x284
+
+#define HSDMA_DESCS_MAX 0xfff
+#define HSDMA_DESCS_NUM 8
+#define HSDMA_DESCS_MASK (HSDMA_DESCS_NUM - 1)
+#define HSDMA_NEXT_DESC(x) (((x) + 1) & HSDMA_DESCS_MASK)
+
+/* HSDMA_REG_INFO */
+#define HSDMA_INFO_INDEX_MASK 0xf
+#define HSDMA_INFO_INDEX_SHIFT 24
+#define HSDMA_INFO_BASE_MASK 0xff
+#define HSDMA_INFO_BASE_SHIFT 16
+#define HSDMA_INFO_RX_MASK 0xff
+#define HSDMA_INFO_RX_SHIFT 8
+#define HSDMA_INFO_TX_MASK 0xff
+#define HSDMA_INFO_TX_SHIFT 0
+
+/* HSDMA_REG_GLO_CFG */
+#define HSDMA_GLO_TX_2B_OFFSET BIT(31)
+#define HSDMA_GLO_CLK_GATE BIT(30)
+#define HSDMA_GLO_BYTE_SWAP BIT(29)
+#define HSDMA_GLO_MULTI_DMA BIT(10)
+#define HSDMA_GLO_TWO_BUF BIT(9)
+#define HSDMA_GLO_32B_DESC BIT(8)
+#define HSDMA_GLO_BIG_ENDIAN BIT(7)
+#define HSDMA_GLO_TX_DONE BIT(6)
+#define HSDMA_GLO_BT_MASK 0x3
+#define HSDMA_GLO_BT_SHIFT 4
+#define HSDMA_GLO_RX_BUSY BIT(3)
+#define HSDMA_GLO_RX_DMA BIT(2)
+#define HSDMA_GLO_TX_BUSY BIT(1)
+#define HSDMA_GLO_TX_DMA BIT(0)
+
+#define HSDMA_BT_SIZE_16BYTES (0 << HSDMA_GLO_BT_SHIFT)
+#define HSDMA_BT_SIZE_32BYTES (1 << HSDMA_GLO_BT_SHIFT)
+#define HSDMA_BT_SIZE_64BYTES (2 << HSDMA_GLO_BT_SHIFT)
+#define HSDMA_BT_SIZE_128BYTES (3 << HSDMA_GLO_BT_SHIFT)
+
+#define HSDMA_GLO_DEFAULT (HSDMA_GLO_MULTI_DMA | \
+ HSDMA_GLO_RX_DMA | HSDMA_GLO_TX_DMA | HSDMA_BT_SIZE_32BYTES)
+
+/* HSDMA_REG_RST_CFG */
+#define HSDMA_RST_RX_SHIFT 16
+#define HSDMA_RST_TX_SHIFT 0
+
+/* HSDMA_REG_DELAY_INT */
+#define HSDMA_DELAY_INT_EN BIT(15)
+#define HSDMA_DELAY_PEND_OFFSET 8
+#define HSDMA_DELAY_TIME_OFFSET 0
+#define HSDMA_DELAY_TX_OFFSET 16
+#define HSDMA_DELAY_RX_OFFSET 0
+
+#define HSDMA_DELAY_INIT(x) (HSDMA_DELAY_INT_EN | \
+ ((x) << HSDMA_DELAY_PEND_OFFSET))
+#define HSDMA_DELAY(x) ((HSDMA_DELAY_INIT(x) << \
+ HSDMA_DELAY_TX_OFFSET) | HSDMA_DELAY_INIT(x))
+
+/* HSDMA_REG_INT_STATUS */
+#define HSDMA_INT_DELAY_RX_COH BIT(31)
+#define HSDMA_INT_DELAY_RX_INT BIT(30)
+#define HSDMA_INT_DELAY_TX_COH BIT(29)
+#define HSDMA_INT_DELAY_TX_INT BIT(28)
+#define HSDMA_INT_RX_MASK 0x3
+#define HSDMA_INT_RX_SHIFT 16
+#define HSDMA_INT_RX_Q0 BIT(16)
+#define HSDMA_INT_TX_MASK 0xf
+#define HSDMA_INT_TX_SHIFT 0
+#define HSDMA_INT_TX_Q0 BIT(0)
+
+/* tx/rx dma desc flags */
+#define HSDMA_PLEN_MASK 0x3fff
+#define HSDMA_DESC_DONE BIT(31)
+#define HSDMA_DESC_LS0 BIT(30)
+#define HSDMA_DESC_PLEN0(_x) (((_x) & HSDMA_PLEN_MASK) << 16)
+#define HSDMA_DESC_TAG BIT(15)
+#define HSDMA_DESC_LS1 BIT(14)
+#define HSDMA_DESC_PLEN1(_x) ((_x) & HSDMA_PLEN_MASK)
+
+/* align 4 bytes */
+#define HSDMA_ALIGN_SIZE 3
+/* align size 128bytes */
+#define HSDMA_MAX_PLEN 0x3f80
+
+struct hsdma_desc {
+ u32 addr0;
+ u32 flags;
+ u32 addr1;
+ u32 unused;
+};
+
+struct mtk_hsdma_sg {
+ dma_addr_t src_addr;
+ dma_addr_t dst_addr;
+ u32 len;
+};
+
+struct mtk_hsdma_desc {
+ struct virt_dma_desc vdesc;
+ unsigned int num_sgs;
+ struct mtk_hsdma_sg sg[1];
+};
+
+struct mtk_hsdma_chan {
+ struct virt_dma_chan vchan;
+ unsigned int id;
+ dma_addr_t desc_addr;
+ int tx_idx;
+ int rx_idx;
+ struct hsdma_desc *tx_ring;
+ struct hsdma_desc *rx_ring;
+ struct mtk_hsdma_desc *desc;
+ unsigned int next_sg;
+};
+
+struct mtk_hsdam_engine {
+ struct dma_device ddev;
+ struct device_dma_parameters dma_parms;
+ void __iomem *base;
+ struct tasklet_struct task;
+ volatile unsigned long chan_issued;
+
+ struct mtk_hsdma_chan chan[1];
+};
+
+static inline struct mtk_hsdam_engine *mtk_hsdma_chan_get_dev(
+ struct mtk_hsdma_chan *chan)
+{
+ return container_of(chan->vchan.chan.device, struct mtk_hsdam_engine,
+ ddev);
+}
+
+static inline struct mtk_hsdma_chan *to_mtk_hsdma_chan(struct dma_chan *c)
+{
+ return container_of(c, struct mtk_hsdma_chan, vchan.chan);
+}
+
+static inline struct mtk_hsdma_desc *to_mtk_hsdma_desc(
+ struct virt_dma_desc *vdesc)
+{
+ return container_of(vdesc, struct mtk_hsdma_desc, vdesc);
+}
+
+static inline u32 mtk_hsdma_read(struct mtk_hsdam_engine *hsdma, u32 reg)
+{
+ return readl(hsdma->base + reg);
+}
+
+static inline void mtk_hsdma_write(struct mtk_hsdam_engine *hsdma,
+ unsigned reg, u32 val)
+{
+ writel(val, hsdma->base + reg);
+}
+
+static void mtk_hsdma_reset_chan(struct mtk_hsdam_engine *hsdma,
+ struct mtk_hsdma_chan *chan)
+{
+ chan->tx_idx = 0;
+ chan->rx_idx = HSDMA_DESCS_NUM - 1;
+
+ mtk_hsdma_write(hsdma, HSDMA_REG_TX_CTX, chan->tx_idx);
+ mtk_hsdma_write(hsdma, HSDMA_REG_RX_CRX, chan->rx_idx);
+
+ mtk_hsdma_write(hsdma, HSDMA_REG_RST_CFG,
+ 0x1 << (chan->id + HSDMA_RST_TX_SHIFT));
+ mtk_hsdma_write(hsdma, HSDMA_REG_RST_CFG,
+ 0x1 << (chan->id + HSDMA_RST_RX_SHIFT));
+}
+
+static void hsdma_dump_reg(struct mtk_hsdam_engine *hsdma)
+{
+ dev_dbg(hsdma->ddev.dev, "tbase %08x, tcnt %08x, " \
+ "tctx %08x, tdtx: %08x, rbase %08x, " \
+ "rcnt %08x, rctx %08x, rdtx %08x\n",
+ mtk_hsdma_read(hsdma, HSDMA_REG_TX_BASE),
+ mtk_hsdma_read(hsdma, HSDMA_REG_TX_CNT),
+ mtk_hsdma_read(hsdma, HSDMA_REG_TX_CTX),
+ mtk_hsdma_read(hsdma, HSDMA_REG_TX_DTX),
+ mtk_hsdma_read(hsdma, HSDMA_REG_RX_BASE),
+ mtk_hsdma_read(hsdma, HSDMA_REG_RX_CNT),
+ mtk_hsdma_read(hsdma, HSDMA_REG_RX_CRX),
+ mtk_hsdma_read(hsdma, HSDMA_REG_RX_DRX));
+
+ dev_dbg(hsdma->ddev.dev, "info %08x, glo %08x, delay %08x, " \
+ "intr_stat %08x, intr_mask %08x\n",
+ mtk_hsdma_read(hsdma, HSDMA_REG_INFO),
+ mtk_hsdma_read(hsdma, HSDMA_REG_GLO_CFG),
+ mtk_hsdma_read(hsdma, HSDMA_REG_DELAY_INT),
+ mtk_hsdma_read(hsdma, HSDMA_REG_INT_STATUS),
+ mtk_hsdma_read(hsdma, HSDMA_REG_INT_MASK));
+}
+
+static void hsdma_dump_desc(struct mtk_hsdam_engine *hsdma,
+ struct mtk_hsdma_chan *chan)
+{
+ struct hsdma_desc *tx_desc;
+ struct hsdma_desc *rx_desc;
+ int i;
+
+ dev_dbg(hsdma->ddev.dev, "tx idx: %d, rx idx: %d\n",
+ chan->tx_idx, chan->rx_idx);
+
+ for (i = 0; i < HSDMA_DESCS_NUM; i++) {
+ tx_desc = &chan->tx_ring[i];
+ rx_desc = &chan->rx_ring[i];
+
+ dev_dbg(hsdma->ddev.dev, "%d tx addr0: %08x, flags %08x, " \
+ "tx addr1: %08x, rx addr0 %08x, flags %08x\n",
+ i, tx_desc->addr0, tx_desc->flags, \
+ tx_desc->addr1, rx_desc->addr0, rx_desc->flags);
+ }
+}
+
+static void mtk_hsdma_reset(struct mtk_hsdam_engine *hsdma,
+ struct mtk_hsdma_chan *chan)
+{
+ int i;
+
+ /* disable dma */
+ mtk_hsdma_write(hsdma, HSDMA_REG_GLO_CFG, 0);
+
+ /* disable intr */
+ mtk_hsdma_write(hsdma, HSDMA_REG_INT_MASK, 0);
+
+ /* init desc value */
+ for (i = 0; i < HSDMA_DESCS_NUM; i++) {
+ chan->tx_ring[i].addr0 = 0;
+ chan->tx_ring[i].flags = HSDMA_DESC_LS0 |
+ HSDMA_DESC_DONE;
+ }
+ for (i = 0; i < HSDMA_DESCS_NUM; i++) {
+ chan->rx_ring[i].addr0 = 0;
+ chan->rx_ring[i].flags = 0;
+ }
+
+ /* reset */
+ mtk_hsdma_reset_chan(hsdma, chan);
+
+ /* enable intr */
+ mtk_hsdma_write(hsdma, HSDMA_REG_INT_MASK, HSDMA_INT_RX_Q0);
+
+ /* enable dma */
+ mtk_hsdma_write(hsdma, HSDMA_REG_GLO_CFG, HSDMA_GLO_DEFAULT);
+}
+
+static int mtk_hsdma_terminate_all(struct dma_chan *c)
+{
+ struct mtk_hsdma_chan *chan = to_mtk_hsdma_chan(c);
+ struct mtk_hsdam_engine *hsdma = mtk_hsdma_chan_get_dev(chan);
+ unsigned long timeout;
+ LIST_HEAD(head);
+
+ spin_lock_bh(&chan->vchan.lock);
+ chan->desc = NULL;
+ clear_bit(chan->id, &hsdma->chan_issued);
+ vchan_get_all_descriptors(&chan->vchan, &head);
+ spin_unlock_bh(&chan->vchan.lock);
+
+ vchan_dma_desc_free_list(&chan->vchan, &head);
+
+ /* wait dma transfer complete */
+ timeout = jiffies + msecs_to_jiffies(2000);
+ while (mtk_hsdma_read(hsdma, HSDMA_REG_GLO_CFG) &
+ (HSDMA_GLO_RX_BUSY | HSDMA_GLO_TX_BUSY)) {
+ if (time_after_eq(jiffies, timeout)) {
+ hsdma_dump_desc(hsdma, chan);
+ mtk_hsdma_reset(hsdma, chan);
+ dev_err(hsdma->ddev.dev, "timeout, reset it\n");
+ break;
+ }
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+static int mtk_hsdma_start_transfer(struct mtk_hsdam_engine *hsdma,
+ struct mtk_hsdma_chan *chan)
+{
+ dma_addr_t src, dst;
+ size_t len, tlen;
+ struct hsdma_desc *tx_desc, *rx_desc;
+ struct mtk_hsdma_sg *sg;
+ unsigned int i;
+ int rx_idx;
+
+ sg = &chan->desc->sg[0];
+ len = sg->len;
+ chan->desc->num_sgs = DIV_ROUND_UP(len, HSDMA_MAX_PLEN);
+
+ /* tx desc */
+ src = sg->src_addr;
+ for (i = 0; i < chan->desc->num_sgs; i++) {
+ if (len > HSDMA_MAX_PLEN)
+ tlen = HSDMA_MAX_PLEN;
+ else
+ tlen = len;
+
+ if (i & 0x1) {
+ tx_desc->addr1 = src;
+ tx_desc->flags |= HSDMA_DESC_PLEN1(tlen);
+ } else {
+ tx_desc = &chan->tx_ring[chan->tx_idx];
+ tx_desc->addr0 = src;
+ tx_desc->flags = HSDMA_DESC_PLEN0(tlen);
+
+ /* update index */
+ chan->tx_idx = HSDMA_NEXT_DESC(chan->tx_idx);
+ }
+
+ src += tlen;
+ len -= tlen;
+ }
+ if (i & 0x1)
+ tx_desc->flags |= HSDMA_DESC_LS0;
+ else
+ tx_desc->flags |= HSDMA_DESC_LS1;
+
+ /* rx desc */
+ rx_idx = HSDMA_NEXT_DESC(chan->rx_idx);
+ len = sg->len;
+ dst = sg->dst_addr;
+ for (i = 0; i < chan->desc->num_sgs; i++) {
+ rx_desc = &chan->rx_ring[rx_idx];
+ if (len > HSDMA_MAX_PLEN)
+ tlen = HSDMA_MAX_PLEN;
+ else
+ tlen = len;
+
+ rx_desc->addr0 = dst;
+ rx_desc->flags = HSDMA_DESC_PLEN0(tlen);
+
+ dst += tlen;
+ len -= tlen;
+
+ /* update index */
+ rx_idx = HSDMA_NEXT_DESC(rx_idx);
+ }
+
+ /* make sure desc and index all up to date */
+ wmb();
+ mtk_hsdma_write(hsdma, HSDMA_REG_TX_CTX, chan->tx_idx);
+
+ return 0;
+}
+
+static int gdma_next_desc(struct mtk_hsdma_chan *chan)
+{
+ struct virt_dma_desc *vdesc;
+
+ vdesc = vchan_next_desc(&chan->vchan);
+ if (!vdesc) {
+ chan->desc = NULL;
+ return 0;
+ }
+ chan->desc = to_mtk_hsdma_desc(vdesc);
+ chan->next_sg = 0;
+
+ return 1;
+}
+
+static void mtk_hsdma_chan_done(struct mtk_hsdam_engine *hsdma,
+ struct mtk_hsdma_chan *chan)
+{
+ struct mtk_hsdma_desc *desc;
+ int chan_issued;
+
+ chan_issued = 0;
+ spin_lock_bh(&chan->vchan.lock);
+ desc = chan->desc;
+ if (likely(desc)) {
+ if (chan->next_sg == desc->num_sgs) {
+ list_del(&desc->vdesc.node);
+ vchan_cookie_complete(&desc->vdesc);
+ chan_issued = gdma_next_desc(chan);
+ }
+ } else
+ dev_dbg(hsdma->ddev.dev, "no desc to complete\n");
+
+ if (chan_issued)
+ set_bit(chan->id, &hsdma->chan_issued);
+ spin_unlock_bh(&chan->vchan.lock);
+}
+
+static irqreturn_t mtk_hsdma_irq(int irq, void *devid)
+{
+ struct mtk_hsdam_engine *hsdma = devid;
+ u32 status;
+
+ status = mtk_hsdma_read(hsdma, HSDMA_REG_INT_STATUS);
+ if (unlikely(!status))
+ return IRQ_NONE;
+
+ if (likely(status & HSDMA_INT_RX_Q0))
+ tasklet_schedule(&hsdma->task);
+ else
+ dev_dbg(hsdma->ddev.dev, "unhandle irq status %08x\n",
+ status);
+ /* clean intr bits */
+ mtk_hsdma_write(hsdma, HSDMA_REG_INT_STATUS, status);
+
+ return IRQ_HANDLED;
+}
+
+static void mtk_hsdma_issue_pending(struct dma_chan *c)
+{
+ struct mtk_hsdma_chan *chan = to_mtk_hsdma_chan(c);
+ struct mtk_hsdam_engine *hsdma = mtk_hsdma_chan_get_dev(chan);
+
+ spin_lock_bh(&chan->vchan.lock);
+ if (vchan_issue_pending(&chan->vchan) && !chan->desc) {
+ if (gdma_next_desc(chan)) {
+ set_bit(chan->id, &hsdma->chan_issued);
+ tasklet_schedule(&hsdma->task);
+ } else
+ dev_dbg(hsdma->ddev.dev, "no desc to issue\n");
+ }
+ spin_unlock_bh(&chan->vchan.lock);
+}
+
+static struct dma_async_tx_descriptor * mtk_hsdma_prep_dma_memcpy(
+ struct dma_chan *c, dma_addr_t dest, dma_addr_t src,
+ size_t len, unsigned long flags)
+{
+ struct mtk_hsdma_chan *chan = to_mtk_hsdma_chan(c);
+ struct mtk_hsdma_desc *desc;
+
+ if (len <= 0)
+ return NULL;
+
+ desc = kzalloc(sizeof(struct mtk_hsdma_desc), GFP_ATOMIC);
+ if (!desc) {
+ dev_err(c->device->dev, "alloc memcpy decs error\n");
+ return NULL;
+ }
+
+ desc->sg[0].src_addr = src;
+ desc->sg[0].dst_addr = dest;
+ desc->sg[0].len = len;
+
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
+}
+
+static enum dma_status mtk_hsdma_tx_status(struct dma_chan *c,
+ dma_cookie_t cookie, struct dma_tx_state *state)
+{
+ return dma_cookie_status(c, cookie, state);
+}
+
+static void mtk_hsdma_free_chan_resources(struct dma_chan *c)
+{
+ vchan_free_chan_resources(to_virt_chan(c));
+}
+
+static void mtk_hsdma_desc_free(struct virt_dma_desc *vdesc)
+{
+ kfree(container_of(vdesc, struct mtk_hsdma_desc, vdesc));
+}
+
+static void mtk_hsdma_tx(struct mtk_hsdam_engine *hsdma)
+{
+ struct mtk_hsdma_chan *chan;
+
+ if (test_and_clear_bit(0, &hsdma->chan_issued)) {
+ chan = &hsdma->chan[0];
+ if (chan->desc) {
+ mtk_hsdma_start_transfer(hsdma, chan);
+ } else
+ dev_dbg(hsdma->ddev.dev,"chan 0 no desc to issue\n");
+ }
+}
+
+static void mtk_hsdma_rx(struct mtk_hsdam_engine *hsdma)
+{
+ struct mtk_hsdma_chan *chan;
+ int next_idx, drx_idx, cnt;
+
+ chan = &hsdma->chan[0];
+ next_idx = HSDMA_NEXT_DESC(chan->rx_idx);
+ drx_idx = mtk_hsdma_read(hsdma, HSDMA_REG_RX_DRX);
+
+ cnt = (drx_idx - next_idx) & HSDMA_DESCS_MASK;
+ if (!cnt)
+ return;
+
+ chan->next_sg += cnt;
+ chan->rx_idx = (chan->rx_idx + cnt) & HSDMA_DESCS_MASK;
+
+ /* update rx crx */
+ wmb();
+ mtk_hsdma_write(hsdma, HSDMA_REG_RX_CRX, chan->rx_idx);
+
+ mtk_hsdma_chan_done(hsdma, chan);
+}
+
+static void mtk_hsdma_tasklet(unsigned long arg)
+{
+ struct mtk_hsdam_engine *hsdma = (struct mtk_hsdam_engine *)arg;
+
+ mtk_hsdma_rx(hsdma);
+ mtk_hsdma_tx(hsdma);
+}
+
+static int mtk_hsdam_alloc_desc(struct mtk_hsdam_engine *hsdma,
+ struct mtk_hsdma_chan *chan)
+{
+ int i;
+
+ chan->tx_ring = dma_alloc_coherent(hsdma->ddev.dev,
+ 2 * HSDMA_DESCS_NUM * sizeof(*chan->tx_ring),
+ &chan->desc_addr, GFP_ATOMIC | __GFP_ZERO);
+ if (!chan->tx_ring)
+ goto no_mem;
+
+ chan->rx_ring = &chan->tx_ring[HSDMA_DESCS_NUM];
+
+ /* init tx ring value */
+ for (i = 0; i < HSDMA_DESCS_NUM; i++)
+ chan->tx_ring[i].flags = HSDMA_DESC_LS0 | HSDMA_DESC_DONE;
+
+ return 0;
+no_mem:
+ return -ENOMEM;
+}
+
+static void mtk_hsdam_free_desc(struct mtk_hsdam_engine *hsdma,
+ struct mtk_hsdma_chan *chan)
+{
+ if (chan->tx_ring) {
+ dma_free_coherent(hsdma->ddev.dev,
+ 2 * HSDMA_DESCS_NUM * sizeof(*chan->tx_ring),
+ chan->tx_ring, chan->desc_addr);
+ chan->tx_ring = NULL;
+ chan->rx_ring = NULL;
+ }
+}
+
+static int mtk_hsdma_init(struct mtk_hsdam_engine *hsdma)
+{
+ struct mtk_hsdma_chan *chan;
+ int ret;
+ u32 reg;
+
+ /* init desc */
+ chan = &hsdma->chan[0];
+ ret = mtk_hsdam_alloc_desc(hsdma, chan);
+ if (ret)
+ return ret;
+
+ /* tx */
+ mtk_hsdma_write(hsdma, HSDMA_REG_TX_BASE, chan->desc_addr);
+ mtk_hsdma_write(hsdma, HSDMA_REG_TX_CNT, HSDMA_DESCS_NUM);
+ /* rx */
+ mtk_hsdma_write(hsdma, HSDMA_REG_RX_BASE, chan->desc_addr +
+ (sizeof(struct hsdma_desc) * HSDMA_DESCS_NUM));
+ mtk_hsdma_write(hsdma, HSDMA_REG_RX_CNT, HSDMA_DESCS_NUM);
+ /* reset */
+ mtk_hsdma_reset_chan(hsdma, chan);
+
+ /* enable rx intr */
+ mtk_hsdma_write(hsdma, HSDMA_REG_INT_MASK, HSDMA_INT_RX_Q0);
+
+ /* enable dma */
+ mtk_hsdma_write(hsdma, HSDMA_REG_GLO_CFG, HSDMA_GLO_DEFAULT);
+
+ /* hardware info */
+ reg = mtk_hsdma_read(hsdma, HSDMA_REG_INFO);
+ dev_info(hsdma->ddev.dev, "rx: %d, tx: %d\n",
+ (reg >> HSDMA_INFO_RX_SHIFT) & HSDMA_INFO_RX_MASK,
+ (reg >> HSDMA_INFO_TX_SHIFT) & HSDMA_INFO_TX_MASK);
+
+ hsdma_dump_reg(hsdma);
+
+ return ret;
+}
+
+static void mtk_hsdma_uninit(struct mtk_hsdam_engine *hsdma)
+{
+ struct mtk_hsdma_chan *chan;
+
+ /* disable dma */
+ mtk_hsdma_write(hsdma, HSDMA_REG_GLO_CFG, 0);
+
+ /* disable intr */
+ mtk_hsdma_write(hsdma, HSDMA_REG_INT_MASK, 0);
+
+ /* free desc */
+ chan = &hsdma->chan[0];
+ mtk_hsdam_free_desc(hsdma, chan);
+
+ /* tx */
+ mtk_hsdma_write(hsdma, HSDMA_REG_TX_BASE, 0);
+ mtk_hsdma_write(hsdma, HSDMA_REG_TX_CNT, 0);
+ /* rx */
+ mtk_hsdma_write(hsdma, HSDMA_REG_RX_BASE, 0);
+ mtk_hsdma_write(hsdma, HSDMA_REG_RX_CNT, 0);
+ /* reset */
+ mtk_hsdma_reset_chan(hsdma, chan);
+}
+
+static const struct of_device_id mtk_hsdma_of_match[] = {
+ { .compatible = "mediatek,mt7621-hsdma" },
+ { },
+};
+
+static int mtk_hsdma_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct mtk_hsdma_chan *chan;
+ struct mtk_hsdam_engine *hsdma;
+ struct dma_device *dd;
+ struct resource *res;
+ int ret;
+ int irq;
+ void __iomem *base;
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ match = of_match_device(mtk_hsdma_of_match, &pdev->dev);
+ if (!match)
+ return -EINVAL;
+
+ hsdma = devm_kzalloc(&pdev->dev, sizeof(*hsdma), GFP_KERNEL);
+ if (!hsdma) {
+ dev_err(&pdev->dev, "alloc dma device failed\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+ hsdma->base = base + HSDMA_BASE_OFFSET;
+ tasklet_init(&hsdma->task, mtk_hsdma_tasklet, (unsigned long)hsdma);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ return -EINVAL;
+ }
+ ret = devm_request_irq(&pdev->dev, irq, mtk_hsdma_irq,
+ 0, dev_name(&pdev->dev), hsdma);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ return ret;
+ }
+
+ device_reset(&pdev->dev);
+
+ dd = &hsdma->ddev;
+ dma_cap_set(DMA_MEMCPY, dd->cap_mask);
+ dd->copy_align = HSDMA_ALIGN_SIZE;
+ dd->device_free_chan_resources = mtk_hsdma_free_chan_resources;
+ dd->device_prep_dma_memcpy = mtk_hsdma_prep_dma_memcpy;
+ dd->device_terminate_all = mtk_hsdma_terminate_all;
+ dd->device_tx_status = mtk_hsdma_tx_status;
+ dd->device_issue_pending = mtk_hsdma_issue_pending;
+ dd->dev = &pdev->dev;
+ dd->dev->dma_parms = &hsdma->dma_parms;
+ dma_set_max_seg_size(dd->dev, HSDMA_MAX_PLEN);
+ INIT_LIST_HEAD(&dd->channels);
+
+ chan = &hsdma->chan[0];
+ chan->id = 0;
+ chan->vchan.desc_free = mtk_hsdma_desc_free;
+ vchan_init(&chan->vchan, dd);
+
+ /* init hardware */
+ ret = mtk_hsdma_init(hsdma);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to alloc ring descs\n");
+ return ret;
+ }
+
+ ret = dma_async_device_register(dd);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register dma device\n");
+ return ret;
+ }
+
+ ret = of_dma_controller_register(pdev->dev.of_node,
+ of_dma_xlate_by_chan_id, hsdma);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register of dma controller\n");
+ goto err_unregister;
+ }
+
+ platform_set_drvdata(pdev, hsdma);
+
+ return 0;
+
+err_unregister:
+ dma_async_device_unregister(dd);
+ return ret;
+}
+
+static int mtk_hsdma_remove(struct platform_device *pdev)
+{
+ struct mtk_hsdam_engine *hsdma = platform_get_drvdata(pdev);
+
+ mtk_hsdma_uninit(hsdma);
+
+ of_dma_controller_free(pdev->dev.of_node);
+ dma_async_device_unregister(&hsdma->ddev);
+
+ return 0;
+}
+
+static struct platform_driver mtk_hsdma_driver = {
+ .probe = mtk_hsdma_probe,
+ .remove = mtk_hsdma_remove,
+ .driver = {
+ .name = "hsdma-mt7621",
+ .of_match_table = mtk_hsdma_of_match,
+ },
+};
+module_platform_driver(mtk_hsdma_driver);
+
+MODULE_AUTHOR("Michael Lee <[email protected]>");
+MODULE_DESCRIPTION("MTK HSDMA driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/mt7621-dma/ralink-gdma.c b/drivers/staging/mt7621-dma/ralink-gdma.c
new file mode 100644
index 000000000000..23ddd64eb1b3
--- /dev/null
+++ b/drivers/staging/mt7621-dma/ralink-gdma.c
@@ -0,0 +1,928 @@
+/*
+ * Copyright (C) 2013, Lars-Peter Clausen <[email protected]>
+ * GDMA4740 DMAC support
+ *
+ * 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.
+ *
+ */
+
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/irq.h>
+#include <linux/of_dma.h>
+#include <linux/reset.h>
+#include <linux/of_device.h>
+
+#include "virt-dma.h"
+
+#define GDMA_REG_SRC_ADDR(x) (0x00 + (x) * 0x10)
+#define GDMA_REG_DST_ADDR(x) (0x04 + (x) * 0x10)
+
+#define GDMA_REG_CTRL0(x) (0x08 + (x) * 0x10)
+#define GDMA_REG_CTRL0_TX_MASK 0xffff
+#define GDMA_REG_CTRL0_TX_SHIFT 16
+#define GDMA_REG_CTRL0_CURR_MASK 0xff
+#define GDMA_REG_CTRL0_CURR_SHIFT 8
+#define GDMA_REG_CTRL0_SRC_ADDR_FIXED BIT(7)
+#define GDMA_REG_CTRL0_DST_ADDR_FIXED BIT(6)
+#define GDMA_REG_CTRL0_BURST_MASK 0x7
+#define GDMA_REG_CTRL0_BURST_SHIFT 3
+#define GDMA_REG_CTRL0_DONE_INT BIT(2)
+#define GDMA_REG_CTRL0_ENABLE BIT(1)
+#define GDMA_REG_CTRL0_SW_MODE BIT(0)
+
+#define GDMA_REG_CTRL1(x) (0x0c + (x) * 0x10)
+#define GDMA_REG_CTRL1_SEG_MASK 0xf
+#define GDMA_REG_CTRL1_SEG_SHIFT 22
+#define GDMA_REG_CTRL1_REQ_MASK 0x3f
+#define GDMA_REG_CTRL1_SRC_REQ_SHIFT 16
+#define GDMA_REG_CTRL1_DST_REQ_SHIFT 8
+#define GDMA_REG_CTRL1_CONTINOUS BIT(14)
+#define GDMA_REG_CTRL1_NEXT_MASK 0x1f
+#define GDMA_REG_CTRL1_NEXT_SHIFT 3
+#define GDMA_REG_CTRL1_COHERENT BIT(2)
+#define GDMA_REG_CTRL1_FAIL BIT(1)
+#define GDMA_REG_CTRL1_MASK BIT(0)
+
+#define GDMA_REG_UNMASK_INT 0x200
+#define GDMA_REG_DONE_INT 0x204
+
+#define GDMA_REG_GCT 0x220
+#define GDMA_REG_GCT_CHAN_MASK 0x3
+#define GDMA_REG_GCT_CHAN_SHIFT 3
+#define GDMA_REG_GCT_VER_MASK 0x3
+#define GDMA_REG_GCT_VER_SHIFT 1
+#define GDMA_REG_GCT_ARBIT_RR BIT(0)
+
+#define GDMA_REG_REQSTS 0x2a0
+#define GDMA_REG_ACKSTS 0x2a4
+#define GDMA_REG_FINSTS 0x2a8
+
+/* for RT305X gdma registers */
+#define GDMA_RT305X_CTRL0_REQ_MASK 0xf
+#define GDMA_RT305X_CTRL0_SRC_REQ_SHIFT 12
+#define GDMA_RT305X_CTRL0_DST_REQ_SHIFT 8
+
+#define GDMA_RT305X_CTRL1_FAIL BIT(4)
+#define GDMA_RT305X_CTRL1_NEXT_MASK 0x7
+#define GDMA_RT305X_CTRL1_NEXT_SHIFT 1
+
+#define GDMA_RT305X_STATUS_INT 0x80
+#define GDMA_RT305X_STATUS_SIGNAL 0x84
+#define GDMA_RT305X_GCT 0x88
+
+/* for MT7621 gdma registers */
+#define GDMA_REG_PERF_START(x) (0x230 + (x) * 0x8)
+#define GDMA_REG_PERF_END(x) (0x234 + (x) * 0x8)
+
+enum gdma_dma_transfer_size {
+ GDMA_TRANSFER_SIZE_4BYTE = 0,
+ GDMA_TRANSFER_SIZE_8BYTE = 1,
+ GDMA_TRANSFER_SIZE_16BYTE = 2,
+ GDMA_TRANSFER_SIZE_32BYTE = 3,
+ GDMA_TRANSFER_SIZE_64BYTE = 4,
+};
+
+struct gdma_dma_sg {
+ dma_addr_t src_addr;
+ dma_addr_t dst_addr;
+ u32 len;
+};
+
+struct gdma_dma_desc {
+ struct virt_dma_desc vdesc;
+
+ enum dma_transfer_direction direction;
+ bool cyclic;
+
+ u32 residue;
+ unsigned int num_sgs;
+ struct gdma_dma_sg sg[];
+};
+
+struct gdma_dmaengine_chan {
+ struct virt_dma_chan vchan;
+ unsigned int id;
+ unsigned int slave_id;
+
+ dma_addr_t fifo_addr;
+ enum gdma_dma_transfer_size burst_size;
+
+ struct gdma_dma_desc *desc;
+ unsigned int next_sg;
+};
+
+struct gdma_dma_dev {
+ struct dma_device ddev;
+ struct device_dma_parameters dma_parms;
+ struct gdma_data *data;
+ void __iomem *base;
+ struct tasklet_struct task;
+ volatile unsigned long chan_issued;
+ atomic_t cnt;
+
+ struct gdma_dmaengine_chan chan[];
+};
+
+struct gdma_data
+{
+ int chancnt;
+ u32 done_int_reg;
+ void (*init)(struct gdma_dma_dev *dma_dev);
+ int (*start_transfer)(struct gdma_dmaengine_chan *chan);
+};
+
+static struct gdma_dma_dev *gdma_dma_chan_get_dev(
+ struct gdma_dmaengine_chan *chan)
+{
+ return container_of(chan->vchan.chan.device, struct gdma_dma_dev,
+ ddev);
+}
+
+static struct gdma_dmaengine_chan *to_gdma_dma_chan(struct dma_chan *c)
+{
+ return container_of(c, struct gdma_dmaengine_chan, vchan.chan);
+}
+
+static struct gdma_dma_desc *to_gdma_dma_desc(struct virt_dma_desc *vdesc)
+{
+ return container_of(vdesc, struct gdma_dma_desc, vdesc);
+}
+
+static inline uint32_t gdma_dma_read(struct gdma_dma_dev *dma_dev,
+ unsigned int reg)
+{
+ return readl(dma_dev->base + reg);
+}
+
+static inline void gdma_dma_write(struct gdma_dma_dev *dma_dev,
+ unsigned reg, uint32_t val)
+{
+ writel(val, dma_dev->base + reg);
+}
+
+static struct gdma_dma_desc *gdma_dma_alloc_desc(unsigned int num_sgs)
+{
+ return kzalloc(sizeof(struct gdma_dma_desc) +
+ sizeof(struct gdma_dma_sg) * num_sgs, GFP_ATOMIC);
+}
+
+static enum gdma_dma_transfer_size gdma_dma_maxburst(u32 maxburst)
+{
+ if (maxburst < 2)
+ return GDMA_TRANSFER_SIZE_4BYTE;
+ else if (maxburst < 4)
+ return GDMA_TRANSFER_SIZE_8BYTE;
+ else if (maxburst < 8)
+ return GDMA_TRANSFER_SIZE_16BYTE;
+ else if (maxburst < 16)
+ return GDMA_TRANSFER_SIZE_32BYTE;
+ else
+ return GDMA_TRANSFER_SIZE_64BYTE;
+}
+
+static int gdma_dma_config(struct dma_chan *c,
+ struct dma_slave_config *config)
+{
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
+ struct gdma_dma_dev *dma_dev = gdma_dma_chan_get_dev(chan);
+
+ if (config->device_fc) {
+ dev_err(dma_dev->ddev.dev, "not support flow controller\n");
+ return -EINVAL;
+ }
+
+ switch (config->direction) {
+ case DMA_MEM_TO_DEV:
+ if (config->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) {
+ dev_err(dma_dev->ddev.dev, "only support 4 byte buswidth\n");
+ return -EINVAL;
+ }
+ chan->slave_id = config->slave_id;
+ chan->fifo_addr = config->dst_addr;
+ chan->burst_size = gdma_dma_maxburst(config->dst_maxburst);
+ break;
+ case DMA_DEV_TO_MEM:
+ if (config->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) {
+ dev_err(dma_dev->ddev.dev, "only support 4 byte buswidth\n");
+ return -EINVAL;
+ }
+ chan->slave_id = config->slave_id;
+ chan->fifo_addr = config->src_addr;
+ chan->burst_size = gdma_dma_maxburst(config->src_maxburst);
+ break;
+ default:
+ dev_err(dma_dev->ddev.dev, "direction type %d error\n",
+ config->direction);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int gdma_dma_terminate_all(struct dma_chan *c)
+{
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
+ struct gdma_dma_dev *dma_dev = gdma_dma_chan_get_dev(chan);
+ unsigned long flags, timeout;
+ LIST_HEAD(head);
+ int i = 0;
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ chan->desc = NULL;
+ clear_bit(chan->id, &dma_dev->chan_issued);
+ vchan_get_all_descriptors(&chan->vchan, &head);
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+
+ vchan_dma_desc_free_list(&chan->vchan, &head);
+
+ /* wait dma transfer complete */
+ timeout = jiffies + msecs_to_jiffies(5000);
+ while (gdma_dma_read(dma_dev, GDMA_REG_CTRL0(chan->id)) &
+ GDMA_REG_CTRL0_ENABLE) {
+ if (time_after_eq(jiffies, timeout)) {
+ dev_err(dma_dev->ddev.dev, "chan %d wait timeout\n",
+ chan->id);
+ /* restore to init value */
+ gdma_dma_write(dma_dev, GDMA_REG_CTRL0(chan->id), 0);
+ break;
+ }
+ cpu_relax();
+ i++;
+ }
+
+ if (i)
+ dev_dbg(dma_dev->ddev.dev, "terminate chan %d loops %d\n",
+ chan->id, i);
+
+ return 0;
+}
+
+static void rt305x_dump_reg(struct gdma_dma_dev *dma_dev, int id)
+{
+ dev_dbg(dma_dev->ddev.dev, "chan %d, src %08x, dst %08x, ctr0 %08x, " \
+ "ctr1 %08x, intr %08x, signal %08x\n", id,
+ gdma_dma_read(dma_dev, GDMA_REG_SRC_ADDR(id)),
+ gdma_dma_read(dma_dev, GDMA_REG_DST_ADDR(id)),
+ gdma_dma_read(dma_dev, GDMA_REG_CTRL0(id)),
+ gdma_dma_read(dma_dev, GDMA_REG_CTRL1(id)),
+ gdma_dma_read(dma_dev, GDMA_RT305X_STATUS_INT),
+ gdma_dma_read(dma_dev, GDMA_RT305X_STATUS_SIGNAL));
+}
+
+static int rt305x_gdma_start_transfer(struct gdma_dmaengine_chan *chan)
+{
+ struct gdma_dma_dev *dma_dev = gdma_dma_chan_get_dev(chan);
+ dma_addr_t src_addr, dst_addr;
+ struct gdma_dma_sg *sg;
+ uint32_t ctrl0, ctrl1;
+
+ /* verify chan is already stopped */
+ ctrl0 = gdma_dma_read(dma_dev, GDMA_REG_CTRL0(chan->id));
+ if (unlikely(ctrl0 & GDMA_REG_CTRL0_ENABLE)) {
+ dev_err(dma_dev->ddev.dev, "chan %d is start(%08x).\n",
+ chan->id, ctrl0);
+ rt305x_dump_reg(dma_dev, chan->id);
+ return -EINVAL;
+ }
+
+ sg = &chan->desc->sg[chan->next_sg];
+ if (chan->desc->direction == DMA_MEM_TO_DEV) {
+ src_addr = sg->src_addr;
+ dst_addr = chan->fifo_addr;
+ ctrl0 = GDMA_REG_CTRL0_DST_ADDR_FIXED | \
+ (8 << GDMA_RT305X_CTRL0_SRC_REQ_SHIFT) | \
+ (chan->slave_id << GDMA_RT305X_CTRL0_DST_REQ_SHIFT);
+ } else if (chan->desc->direction == DMA_DEV_TO_MEM) {
+ src_addr = chan->fifo_addr;
+ dst_addr = sg->dst_addr;
+ ctrl0 = GDMA_REG_CTRL0_SRC_ADDR_FIXED | \
+ (chan->slave_id << GDMA_RT305X_CTRL0_SRC_REQ_SHIFT) | \
+ (8 << GDMA_RT305X_CTRL0_DST_REQ_SHIFT);
+ } else if (chan->desc->direction == DMA_MEM_TO_MEM) {
+ /*
+ * TODO: memcpy function have bugs. sometime it will copy
+ * more 8 bytes data when using dmatest verify.
+ */
+ src_addr = sg->src_addr;
+ dst_addr = sg->dst_addr;
+ ctrl0 = GDMA_REG_CTRL0_SW_MODE | \
+ (8 << GDMA_REG_CTRL1_SRC_REQ_SHIFT) | \
+ (8 << GDMA_REG_CTRL1_DST_REQ_SHIFT);
+ } else {
+ dev_err(dma_dev->ddev.dev, "direction type %d error\n",
+ chan->desc->direction);
+ return -EINVAL;
+ }
+
+ ctrl0 |= (sg->len << GDMA_REG_CTRL0_TX_SHIFT) | \
+ (chan->burst_size << GDMA_REG_CTRL0_BURST_SHIFT) | \
+ GDMA_REG_CTRL0_DONE_INT | GDMA_REG_CTRL0_ENABLE;
+ ctrl1 = chan->id << GDMA_REG_CTRL1_NEXT_SHIFT;
+
+ chan->next_sg++;
+ gdma_dma_write(dma_dev, GDMA_REG_SRC_ADDR(chan->id), src_addr);
+ gdma_dma_write(dma_dev, GDMA_REG_DST_ADDR(chan->id), dst_addr);
+ gdma_dma_write(dma_dev, GDMA_REG_CTRL1(chan->id), ctrl1);
+
+ /* make sure next_sg is update */
+ wmb();
+ gdma_dma_write(dma_dev, GDMA_REG_CTRL0(chan->id), ctrl0);
+
+ return 0;
+}
+
+static void rt3883_dump_reg(struct gdma_dma_dev *dma_dev, int id)
+{
+ dev_dbg(dma_dev->ddev.dev, "chan %d, src %08x, dst %08x, ctr0 %08x, " \
+ "ctr1 %08x, unmask %08x, done %08x, " \
+ "req %08x, ack %08x, fin %08x\n", id,
+ gdma_dma_read(dma_dev, GDMA_REG_SRC_ADDR(id)),
+ gdma_dma_read(dma_dev, GDMA_REG_DST_ADDR(id)),
+ gdma_dma_read(dma_dev, GDMA_REG_CTRL0(id)),
+ gdma_dma_read(dma_dev, GDMA_REG_CTRL1(id)),
+ gdma_dma_read(dma_dev, GDMA_REG_UNMASK_INT),
+ gdma_dma_read(dma_dev, GDMA_REG_DONE_INT),
+ gdma_dma_read(dma_dev, GDMA_REG_REQSTS),
+ gdma_dma_read(dma_dev, GDMA_REG_ACKSTS),
+ gdma_dma_read(dma_dev, GDMA_REG_FINSTS));
+}
+
+static int rt3883_gdma_start_transfer(struct gdma_dmaengine_chan *chan)
+{
+ struct gdma_dma_dev *dma_dev = gdma_dma_chan_get_dev(chan);
+ dma_addr_t src_addr, dst_addr;
+ struct gdma_dma_sg *sg;
+ uint32_t ctrl0, ctrl1;
+
+ /* verify chan is already stopped */
+ ctrl0 = gdma_dma_read(dma_dev, GDMA_REG_CTRL0(chan->id));
+ if (unlikely(ctrl0 & GDMA_REG_CTRL0_ENABLE)) {
+ dev_err(dma_dev->ddev.dev, "chan %d is start(%08x).\n",
+ chan->id, ctrl0);
+ rt3883_dump_reg(dma_dev, chan->id);
+ return -EINVAL;
+ }
+
+ sg = &chan->desc->sg[chan->next_sg];
+ if (chan->desc->direction == DMA_MEM_TO_DEV) {
+ src_addr = sg->src_addr;
+ dst_addr = chan->fifo_addr;
+ ctrl0 = GDMA_REG_CTRL0_DST_ADDR_FIXED;
+ ctrl1 = (32 << GDMA_REG_CTRL1_SRC_REQ_SHIFT) | \
+ (chan->slave_id << GDMA_REG_CTRL1_DST_REQ_SHIFT);
+ } else if (chan->desc->direction == DMA_DEV_TO_MEM) {
+ src_addr = chan->fifo_addr;
+ dst_addr = sg->dst_addr;
+ ctrl0 = GDMA_REG_CTRL0_SRC_ADDR_FIXED;
+ ctrl1 = (chan->slave_id << GDMA_REG_CTRL1_SRC_REQ_SHIFT) | \
+ (32 << GDMA_REG_CTRL1_DST_REQ_SHIFT) | \
+ GDMA_REG_CTRL1_COHERENT;
+ } else if (chan->desc->direction == DMA_MEM_TO_MEM) {
+ src_addr = sg->src_addr;
+ dst_addr = sg->dst_addr;
+ ctrl0 = GDMA_REG_CTRL0_SW_MODE;
+ ctrl1 = (32 << GDMA_REG_CTRL1_SRC_REQ_SHIFT) | \
+ (32 << GDMA_REG_CTRL1_DST_REQ_SHIFT) | \
+ GDMA_REG_CTRL1_COHERENT;
+ } else {
+ dev_err(dma_dev->ddev.dev, "direction type %d error\n",
+ chan->desc->direction);
+ return -EINVAL;
+ }
+
+ ctrl0 |= (sg->len << GDMA_REG_CTRL0_TX_SHIFT) | \
+ (chan->burst_size << GDMA_REG_CTRL0_BURST_SHIFT) | \
+ GDMA_REG_CTRL0_DONE_INT | GDMA_REG_CTRL0_ENABLE;
+ ctrl1 |= chan->id << GDMA_REG_CTRL1_NEXT_SHIFT;
+
+ chan->next_sg++;
+ gdma_dma_write(dma_dev, GDMA_REG_SRC_ADDR(chan->id), src_addr);
+ gdma_dma_write(dma_dev, GDMA_REG_DST_ADDR(chan->id), dst_addr);
+ gdma_dma_write(dma_dev, GDMA_REG_CTRL1(chan->id), ctrl1);
+
+ /* make sure next_sg is update */
+ wmb();
+ gdma_dma_write(dma_dev, GDMA_REG_CTRL0(chan->id), ctrl0);
+
+ return 0;
+}
+
+static inline int gdma_start_transfer(struct gdma_dma_dev *dma_dev,
+ struct gdma_dmaengine_chan *chan)
+{
+ return dma_dev->data->start_transfer(chan);
+}
+
+static int gdma_next_desc(struct gdma_dmaengine_chan *chan)
+{
+ struct virt_dma_desc *vdesc;
+
+ vdesc = vchan_next_desc(&chan->vchan);
+ if (!vdesc) {
+ chan->desc = NULL;
+ return 0;
+ }
+ chan->desc = to_gdma_dma_desc(vdesc);
+ chan->next_sg = 0;
+
+ return 1;
+}
+
+static void gdma_dma_chan_irq(struct gdma_dma_dev *dma_dev,
+ struct gdma_dmaengine_chan *chan)
+{
+ struct gdma_dma_desc *desc;
+ unsigned long flags;
+ int chan_issued;
+
+ chan_issued = 0;
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ desc = chan->desc;
+ if (desc) {
+ if (desc->cyclic) {
+ vchan_cyclic_callback(&desc->vdesc);
+ if (chan->next_sg == desc->num_sgs)
+ chan->next_sg = 0;
+ chan_issued = 1;
+ } else {
+ desc->residue -= desc->sg[chan->next_sg - 1].len;
+ if (chan->next_sg == desc->num_sgs) {
+ list_del(&desc->vdesc.node);
+ vchan_cookie_complete(&desc->vdesc);
+ chan_issued = gdma_next_desc(chan);
+ } else
+ chan_issued = 1;
+ }
+ } else
+ dev_dbg(dma_dev->ddev.dev, "chan %d no desc to complete\n",
+ chan->id);
+ if (chan_issued)
+ set_bit(chan->id, &dma_dev->chan_issued);
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+}
+
+static irqreturn_t gdma_dma_irq(int irq, void *devid)
+{
+ struct gdma_dma_dev *dma_dev = devid;
+ u32 done, done_reg;
+ unsigned int i;
+
+ done_reg = dma_dev->data->done_int_reg;
+ done = gdma_dma_read(dma_dev, done_reg);
+ if (unlikely(!done))
+ return IRQ_NONE;
+
+ /* clean done bits */
+ gdma_dma_write(dma_dev, done_reg, done);
+
+ i = 0;
+ while (done) {
+ if (done & 0x1) {
+ gdma_dma_chan_irq(dma_dev, &dma_dev->chan[i]);
+ atomic_dec(&dma_dev->cnt);
+ }
+ done >>= 1;
+ i++;
+ }
+
+ /* start only have work to do */
+ if (dma_dev->chan_issued)
+ tasklet_schedule(&dma_dev->task);
+
+ return IRQ_HANDLED;
+}
+
+static void gdma_dma_issue_pending(struct dma_chan *c)
+{
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
+ struct gdma_dma_dev *dma_dev = gdma_dma_chan_get_dev(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ if (vchan_issue_pending(&chan->vchan) && !chan->desc) {
+ if (gdma_next_desc(chan)) {
+ set_bit(chan->id, &dma_dev->chan_issued);
+ tasklet_schedule(&dma_dev->task);
+ } else
+ dev_dbg(dma_dev->ddev.dev, "chan %d no desc to issue\n",
+ chan->id);
+ }
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+}
+
+static struct dma_async_tx_descriptor *gdma_dma_prep_slave_sg(
+ struct dma_chan *c, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction direction,
+ unsigned long flags, void *context)
+{
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
+ struct gdma_dma_desc *desc;
+ struct scatterlist *sg;
+ unsigned int i;
+
+ desc = gdma_dma_alloc_desc(sg_len);
+ if (!desc) {
+ dev_err(c->device->dev, "alloc sg decs error\n");
+ return NULL;
+ }
+ desc->residue = 0;
+
+ for_each_sg(sgl, sg, sg_len, i) {
+ if (direction == DMA_MEM_TO_DEV)
+ desc->sg[i].src_addr = sg_dma_address(sg);
+ else if (direction == DMA_DEV_TO_MEM)
+ desc->sg[i].dst_addr = sg_dma_address(sg);
+ else {
+ dev_err(c->device->dev, "direction type %d error\n",
+ direction);
+ goto free_desc;
+ }
+
+ if (unlikely(sg_dma_len(sg) > GDMA_REG_CTRL0_TX_MASK)) {
+ dev_err(c->device->dev, "sg len too large %d\n",
+ sg_dma_len(sg));
+ goto free_desc;
+ }
+ desc->sg[i].len = sg_dma_len(sg);
+ desc->residue += sg_dma_len(sg);
+ }
+
+ desc->num_sgs = sg_len;
+ desc->direction = direction;
+ desc->cyclic = false;
+
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
+
+free_desc:
+ kfree(desc);
+ return NULL;
+}
+
+static struct dma_async_tx_descriptor * gdma_dma_prep_dma_memcpy(
+ struct dma_chan *c, dma_addr_t dest, dma_addr_t src,
+ size_t len, unsigned long flags)
+{
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
+ struct gdma_dma_desc *desc;
+ unsigned int num_periods, i;
+ size_t xfer_count;
+
+ if (len <= 0)
+ return NULL;
+
+ chan->burst_size = gdma_dma_maxburst(len >> 2);
+
+ xfer_count = GDMA_REG_CTRL0_TX_MASK;
+ num_periods = DIV_ROUND_UP(len, xfer_count);
+
+ desc = gdma_dma_alloc_desc(num_periods);
+ if (!desc) {
+ dev_err(c->device->dev, "alloc memcpy decs error\n");
+ return NULL;
+ }
+ desc->residue = len;
+
+ for (i = 0; i < num_periods; i++) {
+ desc->sg[i].src_addr = src;
+ desc->sg[i].dst_addr = dest;
+ if (len > xfer_count) {
+ desc->sg[i].len = xfer_count;
+ } else {
+ desc->sg[i].len = len;
+ }
+ src += desc->sg[i].len;
+ dest += desc->sg[i].len;
+ len -= desc->sg[i].len;
+ }
+
+ desc->num_sgs = num_periods;
+ desc->direction = DMA_MEM_TO_MEM;
+ desc->cyclic = false;
+
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
+}
+
+static struct dma_async_tx_descriptor *gdma_dma_prep_dma_cyclic(
+ struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len,
+ size_t period_len, enum dma_transfer_direction direction,
+ unsigned long flags)
+{
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
+ struct gdma_dma_desc *desc;
+ unsigned int num_periods, i;
+
+ if (buf_len % period_len)
+ return NULL;
+
+ if (period_len > GDMA_REG_CTRL0_TX_MASK) {
+ dev_err(c->device->dev, "cyclic len too large %d\n",
+ period_len);
+ return NULL;
+ }
+
+ num_periods = buf_len / period_len;
+ desc = gdma_dma_alloc_desc(num_periods);
+ if (!desc) {
+ dev_err(c->device->dev, "alloc cyclic decs error\n");
+ return NULL;
+ }
+ desc->residue = buf_len;
+
+ for (i = 0; i < num_periods; i++) {
+ if (direction == DMA_MEM_TO_DEV)
+ desc->sg[i].src_addr = buf_addr;
+ else if (direction == DMA_DEV_TO_MEM)
+ desc->sg[i].dst_addr = buf_addr;
+ else {
+ dev_err(c->device->dev, "direction type %d error\n",
+ direction);
+ goto free_desc;
+ }
+ desc->sg[i].len = period_len;
+ buf_addr += period_len;
+ }
+
+ desc->num_sgs = num_periods;
+ desc->direction = direction;
+ desc->cyclic = true;
+
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
+
+free_desc:
+ kfree(desc);
+ return NULL;
+}
+
+static enum dma_status gdma_dma_tx_status(struct dma_chan *c,
+ dma_cookie_t cookie, struct dma_tx_state *state)
+{
+ struct gdma_dmaengine_chan *chan = to_gdma_dma_chan(c);
+ struct virt_dma_desc *vdesc;
+ enum dma_status status;
+ unsigned long flags;
+ struct gdma_dma_desc *desc;
+
+ status = dma_cookie_status(c, cookie, state);
+ if (status == DMA_COMPLETE || !state)
+ return status;
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ desc = chan->desc;
+ if (desc && (cookie == desc->vdesc.tx.cookie)) {
+ /*
+ * We never update edesc->residue in the cyclic case, so we
+ * can tell the remaining room to the end of the circular
+ * buffer.
+ */
+ if (desc->cyclic)
+ state->residue = desc->residue -
+ ((chan->next_sg - 1) * desc->sg[0].len);
+ else
+ state->residue = desc->residue;
+ } else if ((vdesc = vchan_find_desc(&chan->vchan, cookie)))
+ state->residue = to_gdma_dma_desc(vdesc)->residue;
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+
+ dev_dbg(c->device->dev, "tx residue %d bytes\n", state->residue);
+
+ return status;
+}
+
+static void gdma_dma_free_chan_resources(struct dma_chan *c)
+{
+ vchan_free_chan_resources(to_virt_chan(c));
+}
+
+static void gdma_dma_desc_free(struct virt_dma_desc *vdesc)
+{
+ kfree(container_of(vdesc, struct gdma_dma_desc, vdesc));
+}
+
+static void gdma_dma_tasklet(unsigned long arg)
+{
+ struct gdma_dma_dev *dma_dev = (struct gdma_dma_dev *)arg;
+ struct gdma_dmaengine_chan *chan;
+ static unsigned int last_chan;
+ unsigned int i, chan_mask;
+
+ /* record last chan to round robin all chans */
+ i = last_chan;
+ chan_mask = dma_dev->data->chancnt - 1;
+ do {
+ /*
+ * on mt7621. when verify with dmatest with all
+ * channel is enable. we need to limit only two
+ * channel is working at the same time. otherwise the
+ * data will have problem.
+ */
+ if (atomic_read(&dma_dev->cnt) >= 2) {
+ last_chan = i;
+ break;
+ }
+
+ if (test_and_clear_bit(i, &dma_dev->chan_issued)) {
+ chan = &dma_dev->chan[i];
+ if (chan->desc) {
+ atomic_inc(&dma_dev->cnt);
+ gdma_start_transfer(dma_dev, chan);
+ } else
+ dev_dbg(dma_dev->ddev.dev, "chan %d no desc to issue\n", chan->id);
+
+ if (!dma_dev->chan_issued)
+ break;
+ }
+
+ i = (i + 1) & chan_mask;
+ } while (i != last_chan);
+}
+
+static void rt305x_gdma_init(struct gdma_dma_dev *dma_dev)
+{
+ uint32_t gct;
+
+ /* all chans round robin */
+ gdma_dma_write(dma_dev, GDMA_RT305X_GCT, GDMA_REG_GCT_ARBIT_RR);
+
+ gct = gdma_dma_read(dma_dev, GDMA_RT305X_GCT);
+ dev_info(dma_dev->ddev.dev, "revision: %d, channels: %d\n",
+ (gct >> GDMA_REG_GCT_VER_SHIFT) & GDMA_REG_GCT_VER_MASK,
+ 8 << ((gct >> GDMA_REG_GCT_CHAN_SHIFT) &
+ GDMA_REG_GCT_CHAN_MASK));
+}
+
+static void rt3883_gdma_init(struct gdma_dma_dev *dma_dev)
+{
+ uint32_t gct;
+
+ /* all chans round robin */
+ gdma_dma_write(dma_dev, GDMA_REG_GCT, GDMA_REG_GCT_ARBIT_RR);
+
+ gct = gdma_dma_read(dma_dev, GDMA_REG_GCT);
+ dev_info(dma_dev->ddev.dev, "revision: %d, channels: %d\n",
+ (gct >> GDMA_REG_GCT_VER_SHIFT) & GDMA_REG_GCT_VER_MASK,
+ 8 << ((gct >> GDMA_REG_GCT_CHAN_SHIFT) &
+ GDMA_REG_GCT_CHAN_MASK));
+}
+
+static struct gdma_data rt305x_gdma_data = {
+ .chancnt = 8,
+ .done_int_reg = GDMA_RT305X_STATUS_INT,
+ .init = rt305x_gdma_init,
+ .start_transfer = rt305x_gdma_start_transfer,
+};
+
+static struct gdma_data rt3883_gdma_data = {
+ .chancnt = 16,
+ .done_int_reg = GDMA_REG_DONE_INT,
+ .init = rt3883_gdma_init,
+ .start_transfer = rt3883_gdma_start_transfer,
+};
+
+static const struct of_device_id gdma_of_match_table[] = {
+ { .compatible = "ralink,rt305x-gdma", .data = &rt305x_gdma_data },
+ { .compatible = "ralink,rt3883-gdma", .data = &rt3883_gdma_data },
+ { },
+};
+
+static int gdma_dma_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct gdma_dmaengine_chan *chan;
+ struct gdma_dma_dev *dma_dev;
+ struct dma_device *dd;
+ unsigned int i;
+ struct resource *res;
+ int ret;
+ int irq;
+ void __iomem *base;
+ struct gdma_data *data;
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ match = of_match_device(gdma_of_match_table, &pdev->dev);
+ if (!match)
+ return -EINVAL;
+ data = (struct gdma_data *) match->data;
+
+ dma_dev = devm_kzalloc(&pdev->dev, sizeof(*dma_dev) +
+ (sizeof(struct gdma_dmaengine_chan) * data->chancnt),
+ GFP_KERNEL);
+ if (!dma_dev) {
+ dev_err(&pdev->dev, "alloc dma device failed\n");
+ return -EINVAL;
+ }
+ dma_dev->data = data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+ dma_dev->base = base;
+ tasklet_init(&dma_dev->task, gdma_dma_tasklet, (unsigned long)dma_dev);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ return -EINVAL;
+ }
+ ret = devm_request_irq(&pdev->dev, irq, gdma_dma_irq,
+ 0, dev_name(&pdev->dev), dma_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ return ret;
+ }
+
+ device_reset(&pdev->dev);
+
+ dd = &dma_dev->ddev;
+ dma_cap_set(DMA_MEMCPY, dd->cap_mask);
+ dma_cap_set(DMA_SLAVE, dd->cap_mask);
+ dma_cap_set(DMA_CYCLIC, dd->cap_mask);
+ dd->device_free_chan_resources = gdma_dma_free_chan_resources;
+ dd->device_prep_dma_memcpy = gdma_dma_prep_dma_memcpy;
+ dd->device_prep_slave_sg = gdma_dma_prep_slave_sg;
+ dd->device_prep_dma_cyclic = gdma_dma_prep_dma_cyclic;
+ dd->device_config = gdma_dma_config;
+ dd->device_terminate_all = gdma_dma_terminate_all;
+ dd->device_tx_status = gdma_dma_tx_status;
+ dd->device_issue_pending = gdma_dma_issue_pending;
+
+ dd->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ dd->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+ dd->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
+
+ dd->dev = &pdev->dev;
+ dd->dev->dma_parms = &dma_dev->dma_parms;
+ dma_set_max_seg_size(dd->dev, GDMA_REG_CTRL0_TX_MASK);
+ INIT_LIST_HEAD(&dd->channels);
+
+ for (i = 0; i < data->chancnt; i++) {
+ chan = &dma_dev->chan[i];
+ chan->id = i;
+ chan->vchan.desc_free = gdma_dma_desc_free;
+ vchan_init(&chan->vchan, dd);
+ }
+
+ /* init hardware */
+ data->init(dma_dev);
+
+ ret = dma_async_device_register(dd);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register dma device\n");
+ return ret;
+ }
+
+ ret = of_dma_controller_register(pdev->dev.of_node,
+ of_dma_xlate_by_chan_id, dma_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register of dma controller\n");
+ goto err_unregister;
+ }
+
+ platform_set_drvdata(pdev, dma_dev);
+
+ return 0;
+
+err_unregister:
+ dma_async_device_unregister(dd);
+ return ret;
+}
+
+static int gdma_dma_remove(struct platform_device *pdev)
+{
+ struct gdma_dma_dev *dma_dev = platform_get_drvdata(pdev);
+
+ tasklet_kill(&dma_dev->task);
+ of_dma_controller_free(pdev->dev.of_node);
+ dma_async_device_unregister(&dma_dev->ddev);
+
+ return 0;
+}
+
+static struct platform_driver gdma_dma_driver = {
+ .probe = gdma_dma_probe,
+ .remove = gdma_dma_remove,
+ .driver = {
+ .name = "gdma-rt2880",
+ .of_match_table = gdma_of_match_table,
+ },
+};
+module_platform_driver(gdma_dma_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
+MODULE_DESCRIPTION("Ralink/MTK DMA driver");
+MODULE_LICENSE("GPL v2");
From: John Crispin <[email protected]>
Signed-off-by: John Crispin <[email protected]>
Signed-off-by: NeilBrown <[email protected]>
---
drivers/staging/Kconfig | 2
drivers/staging/Makefile | 1
drivers/staging/mt7621-gpio/Kconfig | 6
drivers/staging/mt7621-gpio/Makefile | 3
drivers/staging/mt7621-gpio/TODO | 5
drivers/staging/mt7621-gpio/gpio-mt7621.c | 353 +++++++++++++++++++++++++++++
6 files changed, 370 insertions(+)
create mode 100644 drivers/staging/mt7621-gpio/Kconfig
create mode 100644 drivers/staging/mt7621-gpio/Makefile
create mode 100644 drivers/staging/mt7621-gpio/TODO
create mode 100644 drivers/staging/mt7621-gpio/gpio-mt7621.c
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index cdd04c8b4445..7568e10399fa 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -124,4 +124,6 @@ source "drivers/staging/pi433/Kconfig"
source "drivers/staging/mt7621-pinctrl/Kconfig"
+source "drivers/staging/mt7621-gpio/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index db90d85a80d3..609fdb218985 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -54,3 +54,4 @@ obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
obj-$(CONFIG_PI433) += pi433/
obj-$(CONFIG_SOC_MT7621) += mt7621-pci/
obj-$(CONFIG_SOC_MT7621) += mt7621-pinctrl/
+obj-$(CONFIG_SOC_MT7621) += mt7621-gpio/
diff --git a/drivers/staging/mt7621-gpio/Kconfig b/drivers/staging/mt7621-gpio/Kconfig
new file mode 100644
index 000000000000..c741ec3f4e50
--- /dev/null
+++ b/drivers/staging/mt7621-gpio/Kconfig
@@ -0,0 +1,6 @@
+config GPIO_MT7621
+ bool "Mediatek GPIO Support"
+ depends on SOC_MT7620 || SOC_MT7621
+ select ARCH_REQUIRE_GPIOLIB
+ help
+ Say yes here to support the Mediatek SoC GPIO device
diff --git a/drivers/staging/mt7621-gpio/Makefile b/drivers/staging/mt7621-gpio/Makefile
new file mode 100644
index 000000000000..e269ab1b8717
--- /dev/null
+++ b/drivers/staging/mt7621-gpio/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o
+
+ccflags-y += -I$(srctree)/$(src)/include
diff --git a/drivers/staging/mt7621-gpio/TODO b/drivers/staging/mt7621-gpio/TODO
new file mode 100644
index 000000000000..71439054e2e4
--- /dev/null
+++ b/drivers/staging/mt7621-gpio/TODO
@@ -0,0 +1,5 @@
+
+- general code review and clean up
+- ensure device-tree requirements are documented
+
+Cc: NeilBrown <[email protected]>
diff --git a/drivers/staging/mt7621-gpio/gpio-mt7621.c b/drivers/staging/mt7621-gpio/gpio-mt7621.c
new file mode 100644
index 000000000000..5c57abea853f
--- /dev/null
+++ b/drivers/staging/mt7621-gpio/gpio-mt7621.c
@@ -0,0 +1,353 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2009-2011 Gabor Juhos <[email protected]>
+ * Copyright (C) 2013 John Crispin <[email protected]>
+ */
+
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/spinlock.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#define MTK_MAX_BANK 3
+#define MTK_BANK_WIDTH 32
+
+enum mediatek_gpio_reg {
+ GPIO_REG_CTRL = 0,
+ GPIO_REG_POL,
+ GPIO_REG_DATA,
+ GPIO_REG_DSET,
+ GPIO_REG_DCLR,
+ GPIO_REG_REDGE,
+ GPIO_REG_FEDGE,
+ GPIO_REG_HLVL,
+ GPIO_REG_LLVL,
+ GPIO_REG_STAT,
+ GPIO_REG_EDGE,
+};
+
+static void __iomem *mediatek_gpio_membase;
+static int mediatek_gpio_irq;
+static struct irq_domain *mediatek_gpio_irq_domain;
+
+struct mtk_gc {
+ struct gpio_chip chip;
+ spinlock_t lock;
+ int bank;
+ u32 rising;
+ u32 falling;
+} *gc_map[MTK_MAX_BANK];
+
+static inline struct mtk_gc
+*to_mediatek_gpio(struct gpio_chip *chip)
+{
+ struct mtk_gc *mgc;
+
+ mgc = container_of(chip, struct mtk_gc, chip);
+
+ return mgc;
+}
+
+static inline void
+mtk_gpio_w32(struct mtk_gc *rg, u8 reg, u32 val)
+{
+ iowrite32(val, mediatek_gpio_membase + (reg * 0x10) + (rg->bank * 0x4));
+}
+
+static inline u32
+mtk_gpio_r32(struct mtk_gc *rg, u8 reg)
+{
+ return ioread32(mediatek_gpio_membase + (reg * 0x10) + (rg->bank * 0x4));
+}
+
+static void
+mediatek_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
+
+ mtk_gpio_w32(rg, (value) ? GPIO_REG_DSET : GPIO_REG_DCLR, BIT(offset));
+}
+
+static int
+mediatek_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
+
+ return !!(mtk_gpio_r32(rg, GPIO_REG_DATA) & BIT(offset));
+}
+
+static int
+mediatek_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
+ unsigned long flags;
+ u32 t;
+
+ spin_lock_irqsave(&rg->lock, flags);
+ t = mtk_gpio_r32(rg, GPIO_REG_CTRL);
+ t &= ~BIT(offset);
+ mtk_gpio_w32(rg, GPIO_REG_CTRL, t);
+ spin_unlock_irqrestore(&rg->lock, flags);
+
+ return 0;
+}
+
+static int
+mediatek_gpio_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
+ unsigned long flags;
+ u32 t;
+
+ spin_lock_irqsave(&rg->lock, flags);
+ t = mtk_gpio_r32(rg, GPIO_REG_CTRL);
+ t |= BIT(offset);
+ mtk_gpio_w32(rg, GPIO_REG_CTRL, t);
+ mediatek_gpio_set(chip, offset, value);
+ spin_unlock_irqrestore(&rg->lock, flags);
+
+ return 0;
+}
+
+static int
+mediatek_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
+ unsigned long flags;
+ u32 t;
+
+ spin_lock_irqsave(&rg->lock, flags);
+ t = mtk_gpio_r32(rg, GPIO_REG_CTRL);
+ spin_unlock_irqrestore(&rg->lock, flags);
+
+ if (t & BIT(offset))
+ return 0;
+
+ return 1;
+}
+
+static int
+mediatek_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
+{
+ struct mtk_gc *rg = to_mediatek_gpio(chip);
+
+ return irq_create_mapping(mediatek_gpio_irq_domain, pin + (rg->bank * MTK_BANK_WIDTH));
+}
+
+static int
+mediatek_gpio_bank_probe(struct platform_device *pdev, struct device_node *bank)
+{
+ const __be32 *id = of_get_property(bank, "reg", NULL);
+ struct mtk_gc *rg = devm_kzalloc(&pdev->dev,
+ sizeof(struct mtk_gc), GFP_KERNEL);
+
+ if (!rg || !id || be32_to_cpu(*id) > MTK_MAX_BANK)
+ return -ENOMEM;
+
+ gc_map[be32_to_cpu(*id)] = rg;
+
+ memset(rg, 0, sizeof(struct mtk_gc));
+
+ spin_lock_init(&rg->lock);
+
+ rg->chip.parent = &pdev->dev;
+ rg->chip.label = dev_name(&pdev->dev);
+ rg->chip.of_node = bank;
+ rg->chip.base = MTK_BANK_WIDTH * be32_to_cpu(*id);
+ rg->chip.ngpio = MTK_BANK_WIDTH;
+ rg->chip.direction_input = mediatek_gpio_direction_input;
+ rg->chip.direction_output = mediatek_gpio_direction_output;
+ rg->chip.get_direction = mediatek_gpio_get_direction;
+ rg->chip.get = mediatek_gpio_get;
+ rg->chip.set = mediatek_gpio_set;
+ if (mediatek_gpio_irq_domain)
+ rg->chip.to_irq = mediatek_gpio_to_irq;
+ rg->bank = be32_to_cpu(*id);
+
+ /* set polarity to low for all gpios */
+ mtk_gpio_w32(rg, GPIO_REG_POL, 0);
+
+ dev_info(&pdev->dev, "registering %d gpios\n", rg->chip.ngpio);
+
+ return gpiochip_add(&rg->chip);
+}
+
+static void
+mediatek_gpio_irq_handler(struct irq_desc *desc)
+{
+ int i;
+
+ for (i = 0; i < MTK_MAX_BANK; i++) {
+ struct mtk_gc *rg = gc_map[i];
+ unsigned long pending;
+ int bit;
+
+ if (!rg)
+ continue;
+
+ pending = mtk_gpio_r32(rg, GPIO_REG_STAT);
+
+ for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) {
+ u32 map = irq_find_mapping(mediatek_gpio_irq_domain, (MTK_BANK_WIDTH * i) + bit);
+
+ generic_handle_irq(map);
+ mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit));
+ }
+ }
+}
+
+static void
+mediatek_gpio_irq_unmask(struct irq_data *d)
+{
+ int pin = d->hwirq;
+ int bank = pin / 32;
+ struct mtk_gc *rg = gc_map[bank];
+ unsigned long flags;
+ u32 rise, fall;
+
+ if (!rg)
+ return;
+
+ rise = mtk_gpio_r32(rg, GPIO_REG_REDGE);
+ fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
+
+ spin_lock_irqsave(&rg->lock, flags);
+ mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(d->hwirq) & rg->rising));
+ mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(d->hwirq) & rg->falling));
+ spin_unlock_irqrestore(&rg->lock, flags);
+}
+
+static void
+mediatek_gpio_irq_mask(struct irq_data *d)
+{
+ int pin = d->hwirq;
+ int bank = pin / 32;
+ struct mtk_gc *rg = gc_map[bank];
+ unsigned long flags;
+ u32 rise, fall;
+
+ if (!rg)
+ return;
+
+ rise = mtk_gpio_r32(rg, GPIO_REG_REDGE);
+ fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
+
+ spin_lock_irqsave(&rg->lock, flags);
+ mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(d->hwirq));
+ mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(d->hwirq));
+ spin_unlock_irqrestore(&rg->lock, flags);
+}
+
+static int
+mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
+{
+ int pin = d->hwirq;
+ int bank = pin / 32;
+ struct mtk_gc *rg = gc_map[bank];
+ u32 mask = BIT(d->hwirq);
+
+ if (!rg)
+ return -1;
+
+ if (type == IRQ_TYPE_PROBE) {
+ if ((rg->rising | rg->falling) & mask)
+ return 0;
+
+ type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+ }
+
+ if (type & IRQ_TYPE_EDGE_RISING)
+ rg->rising |= mask;
+ else
+ rg->rising &= ~mask;
+
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ rg->falling |= mask;
+ else
+ rg->falling &= ~mask;
+
+ return 0;
+}
+
+static struct irq_chip mediatek_gpio_irq_chip = {
+ .name = "GPIO",
+ .irq_unmask = mediatek_gpio_irq_unmask,
+ .irq_mask = mediatek_gpio_irq_mask,
+ .irq_mask_ack = mediatek_gpio_irq_mask,
+ .irq_set_type = mediatek_gpio_irq_type,
+};
+
+static int
+mediatek_gpio_gpio_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(irq, &mediatek_gpio_irq_chip, handle_level_irq);
+ irq_set_handler_data(irq, d);
+
+ return 0;
+}
+
+static const struct irq_domain_ops irq_domain_ops = {
+ .xlate = irq_domain_xlate_onecell,
+ .map = mediatek_gpio_gpio_map,
+};
+
+static int
+mediatek_gpio_probe(struct platform_device *pdev)
+{
+ struct device_node *bank, *np = pdev->dev.of_node;
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ mediatek_gpio_membase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mediatek_gpio_membase))
+ return PTR_ERR(mediatek_gpio_membase);
+
+ mediatek_gpio_irq = irq_of_parse_and_map(np, 0);
+ if (mediatek_gpio_irq) {
+ mediatek_gpio_irq_domain = irq_domain_add_linear(np,
+ MTK_MAX_BANK * MTK_BANK_WIDTH,
+ &irq_domain_ops, NULL);
+ if (!mediatek_gpio_irq_domain)
+ dev_err(&pdev->dev, "irq_domain_add_linear failed\n");
+ }
+
+ for_each_child_of_node(np, bank)
+ if (of_device_is_compatible(bank, "mtk,mt7621-gpio-bank"))
+ mediatek_gpio_bank_probe(pdev, bank);
+
+ if (mediatek_gpio_irq_domain)
+ irq_set_chained_handler(mediatek_gpio_irq, mediatek_gpio_irq_handler);
+
+ return 0;
+}
+
+static const struct of_device_id mediatek_gpio_match[] = {
+ { .compatible = "mtk,mt7621-gpio" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mediatek_gpio_match);
+
+static struct platform_driver mediatek_gpio_driver = {
+ .probe = mediatek_gpio_probe,
+ .driver = {
+ .name = "mt7621_gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = mediatek_gpio_match,
+ },
+};
+
+static int __init
+mediatek_gpio_init(void)
+{
+ return platform_driver_register(&mediatek_gpio_driver);
+}
+
+subsys_initcall(mediatek_gpio_init);
From: John Crispin <[email protected]>
NeilBrown:
Added range-check on pdev->id before assigning ot
host->id
of_dma_configure() sets a default ->dma_mask of
DMA_BIT_MASK(32), claiming devices can DMA from
the full 32bit address space.
The mtk-mmc driver does not support access to
highmem pages, so it is really limited to the
bottom 512M (actually 448M due to 64M of IO space).
Setting ->dma_mask to NULL causes mmc_setup_queue()
to fall-back to using BLK_BOUNCE_HIGH to tell the
block layer to use a bounce-buffer for any highmem
pages requiring IO.
Signed-off-by: John Crispin <[email protected]>
Signed-off-by: NeilBrown <[email protected]>
---
drivers/staging/Kconfig | 2
drivers/staging/Makefile | 1
drivers/staging/mt7621-mmc/Kconfig | 16
drivers/staging/mt7621-mmc/Makefile | 42
drivers/staging/mt7621-mmc/TODO | 8
drivers/staging/mt7621-mmc/board.h | 137 +
drivers/staging/mt7621-mmc/dbg.c | 347 ++++
drivers/staging/mt7621-mmc/dbg.h | 156 ++
drivers/staging/mt7621-mmc/mt6575_sd.h | 1001 ++++++++++
drivers/staging/mt7621-mmc/sd.c | 3074 ++++++++++++++++++++++++++++++++
10 files changed, 4784 insertions(+)
create mode 100644 drivers/staging/mt7621-mmc/Kconfig
create mode 100644 drivers/staging/mt7621-mmc/Makefile
create mode 100644 drivers/staging/mt7621-mmc/TODO
create mode 100644 drivers/staging/mt7621-mmc/board.h
create mode 100644 drivers/staging/mt7621-mmc/dbg.c
create mode 100644 drivers/staging/mt7621-mmc/dbg.h
create mode 100644 drivers/staging/mt7621-mmc/mt6575_sd.h
create mode 100644 drivers/staging/mt7621-mmc/sd.c
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index fa8cb95da178..4c872bf7c280 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -130,4 +130,6 @@ source "drivers/staging/mt7621-spi/Kconfig"
source "drivers/staging/mt7621-dma/Kconfig"
+source "drivers/staging/mt7621-mmc/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index a577f3ac2dd0..8ce10da1ab1d 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -57,3 +57,4 @@ obj-$(CONFIG_SOC_MT7621) += mt7621-pinctrl/
obj-$(CONFIG_SOC_MT7621) += mt7621-gpio/
obj-$(CONFIG_SOC_MT7621) += mt7621-spi/
obj-$(CONFIG_SOC_MT7621) += mt7621-dma/
+obj-$(CONFIG_SOC_MT7621) += mt7621-mmc/
diff --git a/drivers/staging/mt7621-mmc/Kconfig b/drivers/staging/mt7621-mmc/Kconfig
new file mode 100644
index 000000000000..c6dfe8c637dc
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/Kconfig
@@ -0,0 +1,16 @@
+config MTK_MMC
+ tristate "MTK SD/MMC"
+ depends on !MTD_NAND_RALINK && MMC
+
+config MTK_AEE_KDUMP
+ bool "MTK AEE KDUMP"
+ depends on MTK_MMC
+
+config MTK_MMC_CD_POLL
+ bool "Card Detect with Polling"
+ depends on MTK_MMC
+
+config MTK_MMC_EMMC_8BIT
+ bool "eMMC 8-bit support"
+ depends on MTK_MMC && RALINK_MT7628
+
diff --git a/drivers/staging/mt7621-mmc/Makefile b/drivers/staging/mt7621-mmc/Makefile
new file mode 100644
index 000000000000..caead0b54703
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/Makefile
@@ -0,0 +1,42 @@
+# Copyright Statement:
+#
+# This software/firmware and related documentation ("MediaTek Software") are
+# protected under relevant copyright laws. The information contained herein
+# is confidential and proprietary to MediaTek Inc. and/or its licensors.
+# Without the prior written permission of MediaTek inc. and/or its licensors,
+# any reproduction, modification, use or disclosure of MediaTek Software,
+# and information contained herein, in whole or in part, shall be strictly prohibited.
+#
+# MediaTek Inc. (C) 2010. All rights reserved.
+#
+# BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+# THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+# RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+# AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+# NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+# SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+# SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+# THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+# THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+# CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+# SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+# STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+# CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+# AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+# OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+# MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+#
+# The following software/firmware and/or related documentation ("MediaTek Software")
+# have been modified by MediaTek Inc. All revisions are subject to any receiver's
+# applicable license agreements with MediaTek Inc.
+
+obj-$(CONFIG_MTK_MMC) += mtk_sd.o
+mtk_sd-objs := sd.o dbg.o
+ifeq ($(CONFIG_MTK_AEE_KDUMP),y)
+EXTRA_CFLAGS += -DMT6575_SD_DEBUG
+endif
+
+clean:
+ @rm -f *.o modules.order .*.cmd
diff --git a/drivers/staging/mt7621-mmc/TODO b/drivers/staging/mt7621-mmc/TODO
new file mode 100644
index 000000000000..febb32d37e07
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/TODO
@@ -0,0 +1,8 @@
+
+- general code review and clean up
+- ensure device-tree requirements are documented
+- should probably be merged with drivers/mmc/host/mtk-sd.c
+- possibly fix to work with highmem pages so a bounce buffer isn't
+ needed.
+
+Cc: NeilBrown <[email protected]>
diff --git a/drivers/staging/mt7621-mmc/board.h b/drivers/staging/mt7621-mmc/board.h
new file mode 100644
index 000000000000..33bfc7b9597a
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/board.h
@@ -0,0 +1,137 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ */
+/* MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#ifndef __ARCH_ARM_MACH_BOARD_H
+#define __ARCH_ARM_MACH_BOARD_H
+
+#include <generated/autoconf.h>
+#include <linux/pm.h>
+/* --- chhung */
+// #include <mach/mt6575.h>
+// #include <board-custom.h>
+/* end of chhung */
+
+typedef void (*sdio_irq_handler_t)(void*); /* external irq handler */
+typedef void (*pm_callback_t)(pm_message_t state, void *data);
+
+#define MSDC_CD_PIN_EN (1 << 0) /* card detection pin is wired */
+#define MSDC_WP_PIN_EN (1 << 1) /* write protection pin is wired */
+#define MSDC_RST_PIN_EN (1 << 2) /* emmc reset pin is wired */
+#define MSDC_SDIO_IRQ (1 << 3) /* use internal sdio irq (bus) */
+#define MSDC_EXT_SDIO_IRQ (1 << 4) /* use external sdio irq */
+#define MSDC_REMOVABLE (1 << 5) /* removable slot */
+#define MSDC_SYS_SUSPEND (1 << 6) /* suspended by system */
+#define MSDC_HIGHSPEED (1 << 7) /* high-speed mode support */
+#define MSDC_UHS1 (1 << 8) /* uhs-1 mode support */
+#define MSDC_DDR (1 << 9) /* ddr mode support */
+
+
+#define MSDC_SMPL_RISING (0)
+#define MSDC_SMPL_FALLING (1)
+
+#define MSDC_CMD_PIN (0)
+#define MSDC_DAT_PIN (1)
+#define MSDC_CD_PIN (2)
+#define MSDC_WP_PIN (3)
+#define MSDC_RST_PIN (4)
+
+enum {
+ MSDC_CLKSRC_48MHZ = 0,
+// MSDC_CLKSRC_26MHZ = 0,
+// MSDC_CLKSRC_197MHZ = 1,
+// MSDC_CLKSRC_208MHZ = 2
+};
+
+struct msdc_hw {
+ unsigned char clk_src; /* host clock source */
+ unsigned char cmd_edge; /* command latch edge */
+ unsigned char data_edge; /* data latch edge */
+ unsigned char clk_drv; /* clock pad driving */
+ unsigned char cmd_drv; /* command pad driving */
+ unsigned char dat_drv; /* data pad driving */
+ unsigned long flags; /* hardware capability flags */
+ unsigned long data_pins; /* data pins */
+ unsigned long data_offset; /* data address offset */
+
+ /* config gpio pull mode */
+ void (*config_gpio_pin)(int type, int pull);
+
+ /* external power control for card */
+ void (*ext_power_on)(void);
+ void (*ext_power_off)(void);
+
+ /* external sdio irq operations */
+ void (*request_sdio_eirq)(sdio_irq_handler_t sdio_irq_handler, void *data);
+ void (*enable_sdio_eirq)(void);
+ void (*disable_sdio_eirq)(void);
+
+ /* external cd irq operations */
+ void (*request_cd_eirq)(sdio_irq_handler_t cd_irq_handler, void *data);
+ void (*enable_cd_eirq)(void);
+ void (*disable_cd_eirq)(void);
+ int (*get_cd_status)(void);
+
+ /* power management callback for external module */
+ void (*register_pm)(pm_callback_t pm_cb, void *data);
+};
+
+extern struct msdc_hw msdc0_hw;
+extern struct msdc_hw msdc1_hw;
+extern struct msdc_hw msdc2_hw;
+extern struct msdc_hw msdc3_hw;
+
+/*GPS driver*/
+#define GPS_FLAG_FORCE_OFF 0x0001
+struct mt3326_gps_hardware {
+ int (*ext_power_on)(int);
+ int (*ext_power_off)(int);
+};
+extern struct mt3326_gps_hardware mt3326_gps_hw;
+
+/* NAND driver */
+struct mt6575_nand_host_hw {
+ unsigned int nfi_bus_width; /* NFI_BUS_WIDTH */
+ unsigned int nfi_access_timing; /* NFI_ACCESS_TIMING */
+ unsigned int nfi_cs_num; /* NFI_CS_NUM */
+ unsigned int nand_sec_size; /* NAND_SECTOR_SIZE */
+ unsigned int nand_sec_shift; /* NAND_SECTOR_SHIFT */
+ unsigned int nand_ecc_size;
+ unsigned int nand_ecc_bytes;
+ unsigned int nand_ecc_mode;
+};
+extern struct mt6575_nand_host_hw mt6575_nand_hw;
+
+#endif /* __ARCH_ARM_MACH_BOARD_H */
+
diff --git a/drivers/staging/mt7621-mmc/dbg.c b/drivers/staging/mt7621-mmc/dbg.c
new file mode 100644
index 000000000000..4dc115b53993
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/dbg.c
@@ -0,0 +1,347 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ *
+ * MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+// #include <mach/mt6575_gpt.h> /* --- by chhung */
+#include "dbg.h"
+#include "mt6575_sd.h"
+#include <linux/seq_file.h>
+
+static char cmd_buf[256];
+
+/* for debug zone */
+unsigned int sd_debug_zone[4]={
+ 0,
+ 0,
+ 0,
+ 0
+};
+
+/* mode select */
+u32 dma_size[4]={
+ 512,
+ 512,
+ 512,
+ 512
+};
+msdc_mode drv_mode[4]={
+ MODE_SIZE_DEP, /* using DMA or not depend on the size */
+ MODE_SIZE_DEP,
+ MODE_SIZE_DEP,
+ MODE_SIZE_DEP
+};
+
+#if defined (MT6575_SD_DEBUG)
+/* for driver profile */
+#define TICKS_ONE_MS (13000)
+u32 gpt_enable = 0;
+u32 sdio_pro_enable = 0; /* make sure gpt is enabled */
+u32 sdio_pro_time = 0; /* no more than 30s */
+struct sdio_profile sdio_perfomance = {0};
+
+#if 0 /* --- chhung */
+void msdc_init_gpt(void)
+{
+ GPT_CONFIG config;
+
+ config.num = GPT6;
+ config.mode = GPT_FREE_RUN;
+ config.clkSrc = GPT_CLK_SRC_SYS;
+ config.clkDiv = GPT_CLK_DIV_1; /* 13MHz GPT6 */
+
+ if (GPT_Config(config) == FALSE )
+ return;
+
+ GPT_Start(GPT6);
+}
+#endif /* end of --- */
+
+u32 msdc_time_calc(u32 old_L32, u32 old_H32, u32 new_L32, u32 new_H32)
+{
+ u32 ret = 0;
+
+ if (new_H32 == old_H32) {
+ ret = new_L32 - old_L32;
+ } else if(new_H32 == (old_H32 + 1)) {
+ if (new_L32 > old_L32) {
+ printk("msdc old_L<0x%x> new_L<0x%x>\n", old_L32, new_L32);
+ }
+ ret = (0xffffffff - old_L32);
+ ret += new_L32;
+ } else {
+ printk("msdc old_H<0x%x> new_H<0x%x>\n", old_H32, new_H32);
+ }
+
+ return ret;
+}
+
+void msdc_sdio_profile(struct sdio_profile* result)
+{
+ struct cmd_profile* cmd;
+ u32 i;
+
+ printk("sdio === performance dump ===\n");
+ printk("sdio === total execute tick<%d> time<%dms> Tx<%dB> Rx<%dB>\n",
+ result->total_tc, result->total_tc / TICKS_ONE_MS,
+ result->total_tx_bytes, result->total_rx_bytes);
+
+ /* CMD52 Dump */
+ cmd = &result->cmd52_rx;
+ printk("sdio === CMD52 Rx <%d>times tick<%d> Max<%d> Min<%d> Aver<%d>\n", cmd->count, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count);
+ cmd = &result->cmd52_tx;
+ printk("sdio === CMD52 Tx <%d>times tick<%d> Max<%d> Min<%d> Aver<%d>\n", cmd->count, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count);
+
+ /* CMD53 Rx bytes + block mode */
+ for (i=0; i<512; i++) {
+ cmd = &result->cmd53_rx_byte[i];
+ if (cmd->count) {
+ printk("sdio<%6d><%3dB>_Rx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count,
+ cmd->tot_bytes, (cmd->tot_bytes/10)*13 / (cmd->tot_tc/10));
+ }
+ }
+ for (i=0; i<100; i++) {
+ cmd = &result->cmd53_rx_blk[i];
+ if (cmd->count) {
+ printk("sdio<%6d><%3d>B_Rx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count,
+ cmd->tot_bytes, (cmd->tot_bytes/10)*13 / (cmd->tot_tc/10));
+ }
+ }
+
+ /* CMD53 Tx bytes + block mode */
+ for (i=0; i<512; i++) {
+ cmd = &result->cmd53_tx_byte[i];
+ if (cmd->count) {
+ printk("sdio<%6d><%3dB>_Tx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count,
+ cmd->tot_bytes, (cmd->tot_bytes/10)*13 / (cmd->tot_tc/10));
+ }
+ }
+ for (i=0; i<100; i++) {
+ cmd = &result->cmd53_tx_blk[i];
+ if (cmd->count) {
+ printk("sdio<%6d><%3d>B_Tx_<%9d><%9d><%6d><%6d>_<%9dB><%2dM>\n", cmd->count, i, cmd->tot_tc,
+ cmd->max_tc, cmd->min_tc, cmd->tot_tc/cmd->count,
+ cmd->tot_bytes, (cmd->tot_bytes/10)*13 / (cmd->tot_tc/10));
+ }
+ }
+
+ printk("sdio === performance dump done ===\n");
+}
+
+//========= sdio command table ===========
+void msdc_performance(u32 opcode, u32 sizes, u32 bRx, u32 ticks)
+{
+ struct sdio_profile* result = &sdio_perfomance;
+ struct cmd_profile* cmd;
+ u32 block;
+
+ if (sdio_pro_enable == 0) {
+ return;
+ }
+
+ if (opcode == 52) {
+ cmd = bRx ? &result->cmd52_rx : &result->cmd52_tx;
+ } else if (opcode == 53) {
+ if (sizes < 512) {
+ cmd = bRx ? &result->cmd53_rx_byte[sizes] : &result->cmd53_tx_byte[sizes];
+ } else {
+ block = sizes / 512;
+ if (block >= 99) {
+ printk("cmd53 error blocks\n");
+ while(1);
+ }
+ cmd = bRx ? &result->cmd53_rx_blk[block] : &result->cmd53_tx_blk[block];
+ }
+ } else {
+ return;
+ }
+
+ /* update the members */
+ if (ticks > cmd->max_tc){
+ cmd->max_tc = ticks;
+ }
+ if (cmd->min_tc == 0 || ticks < cmd->min_tc) {
+ cmd->min_tc = ticks;
+ }
+ cmd->tot_tc += ticks;
+ cmd->tot_bytes += sizes;
+ cmd->count ++;
+
+ if (bRx) {
+ result->total_rx_bytes += sizes;
+ } else {
+ result->total_tx_bytes += sizes;
+ }
+ result->total_tc += ticks;
+
+ /* dump when total_tc > 30s */
+ if (result->total_tc >= sdio_pro_time * TICKS_ONE_MS * 1000) {
+ msdc_sdio_profile(result);
+ memset(result, 0 , sizeof(struct sdio_profile));
+ }
+}
+
+//========== driver proc interface ===========
+static int msdc_debug_proc_read(struct seq_file *s, void *p)
+{
+ seq_printf(s, "\n=========================================\n");
+ seq_printf(s, "Index<0> + Id + Zone\n");
+ seq_printf(s, "-> PWR<9> WRN<8> | FIO<7> OPS<6> FUN<5> CFG<4> | INT<3> RSP<2> CMD<1> DMA<0>\n");
+ seq_printf(s, "-> echo 0 3 0x3ff >msdc_bebug -> host[3] debug zone set to 0x3ff\n");
+ seq_printf(s, "-> MSDC[0] Zone: 0x%.8x\n", sd_debug_zone[0]);
+ seq_printf(s, "-> MSDC[1] Zone: 0x%.8x\n", sd_debug_zone[1]);
+ seq_printf(s, "-> MSDC[2] Zone: 0x%.8x\n", sd_debug_zone[2]);
+ seq_printf(s, "-> MSDC[3] Zone: 0x%.8x\n", sd_debug_zone[3]);
+
+ seq_printf(s, "Index<1> + ID:4|Mode:4 + DMA_SIZE\n");
+ seq_printf(s, "-> 0)PIO 1)DMA 2)SIZE\n");
+ seq_printf(s, "-> echo 1 22 0x200 >msdc_bebug -> host[2] size mode, dma when >= 512\n");
+ seq_printf(s, "-> MSDC[0] mode<%d> size<%d>\n", drv_mode[0], dma_size[0]);
+ seq_printf(s, "-> MSDC[1] mode<%d> size<%d>\n", drv_mode[1], dma_size[1]);
+ seq_printf(s, "-> MSDC[2] mode<%d> size<%d>\n", drv_mode[2], dma_size[2]);
+ seq_printf(s, "-> MSDC[3] mode<%d> size<%d>\n", drv_mode[3], dma_size[3]);
+
+ seq_printf(s, "Index<3> + SDIO_PROFILE + TIME\n");
+ seq_printf(s, "-> echo 3 1 0x1E >msdc_bebug -> enable sdio_profile, 30s\n");
+ seq_printf(s, "-> SDIO_PROFILE<%d> TIME<%ds>\n", sdio_pro_enable, sdio_pro_time);
+ seq_printf(s, "=========================================\n\n");
+
+ return 0;
+}
+
+static ssize_t msdc_debug_proc_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *data)
+{
+ int ret;
+
+ int cmd, p1, p2;
+ int id, zone;
+ int mode, size;
+
+ if (count == 0)return -1;
+ if(count > 255)count = 255;
+
+ ret = copy_from_user(cmd_buf, buf, count);
+ if (ret < 0)return -1;
+
+ cmd_buf[count] = '\0';
+ printk("msdc Write %s\n", cmd_buf);
+
+ sscanf(cmd_buf, "%x %x %x", &cmd, &p1, &p2);
+
+ if(cmd == SD_TOOL_ZONE) {
+ id = p1; zone = p2; zone &= 0x3ff;
+ printk("msdc host_id<%d> zone<0x%.8x>\n", id, zone);
+ if(id >=0 && id<=3){
+ sd_debug_zone[id] = zone;
+ }
+ else if(id == 4){
+ sd_debug_zone[0] = sd_debug_zone[1] = zone;
+ sd_debug_zone[2] = sd_debug_zone[3] = zone;
+ }
+ else{
+ printk("msdc host_id error when set debug zone\n");
+ }
+ } else if (cmd == SD_TOOL_DMA_SIZE) {
+ id = p1>>4; mode = (p1&0xf); size = p2;
+ if(id >=0 && id<=3){
+ drv_mode[id] = mode;
+ dma_size[id] = p2;
+ }
+ else if(id == 4){
+ drv_mode[0] = drv_mode[1] = mode;
+ drv_mode[2] = drv_mode[3] = mode;
+ dma_size[0] = dma_size[1] = p2;
+ dma_size[2] = dma_size[3] = p2;
+ }
+ else{
+ printk("msdc host_id error when select mode\n");
+ }
+ } else if (cmd == SD_TOOL_SDIO_PROFILE) {
+ if (p1 == 1) { /* enable profile */
+ if (gpt_enable == 0) {
+ // msdc_init_gpt(); /* --- by chhung */
+ gpt_enable = 1;
+ }
+ sdio_pro_enable = 1;
+ if (p2 == 0) p2 = 1; if (p2 >= 30) p2 = 30;
+ sdio_pro_time = p2 ;
+ } else if (p1 == 0) {
+ /* todo */
+ sdio_pro_enable = 0;
+ }
+ }
+
+ return count;
+}
+
+static int msdc_debug_show(struct inode *inode, struct file *file)
+{
+ return single_open(file, msdc_debug_proc_read, NULL);
+}
+
+static const struct file_operations msdc_debug_fops = {
+ .owner = THIS_MODULE,
+ .open = msdc_debug_show,
+ .read = seq_read,
+ .write = msdc_debug_proc_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int msdc_debug_proc_init(void)
+{
+ struct proc_dir_entry *de = proc_create("msdc_debug", 0667, NULL, &msdc_debug_fops);
+
+ if (!de || IS_ERR(de))
+ printk("!! Create MSDC debug PROC fail !!\n");
+
+ return 0 ;
+}
+EXPORT_SYMBOL_GPL(msdc_debug_proc_init);
+#endif
diff --git a/drivers/staging/mt7621-mmc/dbg.h b/drivers/staging/mt7621-mmc/dbg.h
new file mode 100644
index 000000000000..e58c4312933e
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/dbg.h
@@ -0,0 +1,156 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ *
+ * MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+#ifndef __MT_MSDC_DEUBG__
+#define __MT_MSDC_DEUBG__
+
+//==========================
+extern u32 sdio_pro_enable;
+/* for a type command, e.g. CMD53, 2 blocks */
+struct cmd_profile {
+ u32 max_tc; /* Max tick count */
+ u32 min_tc;
+ u32 tot_tc; /* total tick count */
+ u32 tot_bytes;
+ u32 count; /* the counts of the command */
+};
+
+/* dump when total_tc and total_bytes */
+struct sdio_profile {
+ u32 total_tc; /* total tick count of CMD52 and CMD53 */
+ u32 total_tx_bytes; /* total bytes of CMD53 Tx */
+ u32 total_rx_bytes; /* total bytes of CMD53 Rx */
+
+ /*CMD52*/
+ struct cmd_profile cmd52_tx;
+ struct cmd_profile cmd52_rx;
+
+ /*CMD53 in byte unit */
+ struct cmd_profile cmd53_tx_byte[512];
+ struct cmd_profile cmd53_rx_byte[512];
+
+ /*CMD53 in block unit */
+ struct cmd_profile cmd53_tx_blk[100];
+ struct cmd_profile cmd53_rx_blk[100];
+};
+
+//==========================
+typedef enum {
+ SD_TOOL_ZONE = 0,
+ SD_TOOL_DMA_SIZE = 1,
+ SD_TOOL_PM_ENABLE = 2,
+ SD_TOOL_SDIO_PROFILE = 3,
+} msdc_dbg;
+
+typedef enum {
+ MODE_PIO = 0,
+ MODE_DMA = 1,
+ MODE_SIZE_DEP = 2,
+} msdc_mode;
+extern msdc_mode drv_mode[4];
+extern u32 dma_size[4];
+
+/* Debug message event */
+#define DBG_EVT_NONE (0) /* No event */
+#define DBG_EVT_DMA (1 << 0) /* DMA related event */
+#define DBG_EVT_CMD (1 << 1) /* MSDC CMD related event */
+#define DBG_EVT_RSP (1 << 2) /* MSDC CMD RSP related event */
+#define DBG_EVT_INT (1 << 3) /* MSDC INT event */
+#define DBG_EVT_CFG (1 << 4) /* MSDC CFG event */
+#define DBG_EVT_FUC (1 << 5) /* Function event */
+#define DBG_EVT_OPS (1 << 6) /* Read/Write operation event */
+#define DBG_EVT_FIO (1 << 7) /* FIFO operation event */
+#define DBG_EVT_WRN (1 << 8) /* Warning event */
+#define DBG_EVT_PWR (1 << 9) /* Power event */
+#define DBG_EVT_ALL (0xffffffff)
+
+#define DBG_EVT_MASK (DBG_EVT_ALL)
+
+extern unsigned int sd_debug_zone[4];
+#define TAG "msdc"
+#if 0 /* +++ chhung */
+#define BUG_ON(x) \
+do { \
+ if (x) { \
+ printk("[BUG] %s LINE:%d FILE:%s\n", #x, __LINE__, __FILE__); \
+ while(1); \
+ } \
+}while(0)
+#endif /* end of +++ */
+
+#define N_MSG(evt, fmt, args...)
+/*
+do { \
+ if ((DBG_EVT_##evt) & sd_debug_zone[host->id]) { \
+ printk(KERN_ERR TAG"%d -> "fmt" <- %s() : L<%d> PID<%s><0x%x>\n", \
+ host->id, ##args , __FUNCTION__, __LINE__, current->comm, current->pid); \
+ } \
+} while(0)
+*/
+
+#define ERR_MSG(fmt, args...) \
+do { \
+ printk(KERN_ERR TAG"%d -> "fmt" <- %s() : L<%d> PID<%s><0x%x>\n", \
+ host->id, ##args , __FUNCTION__, __LINE__, current->comm, current->pid); \
+} while(0);
+
+#if 1
+//defined CONFIG_MTK_MMC_CD_POLL
+#define INIT_MSG(fmt, args...)
+#define IRQ_MSG(fmt, args...)
+#else
+#define INIT_MSG(fmt, args...) \
+do { \
+ printk(KERN_ERR TAG"%d -> "fmt" <- %s() : L<%d> PID<%s><0x%x>\n", \
+ host->id, ##args , __FUNCTION__, __LINE__, current->comm, current->pid); \
+} while(0);
+
+/* PID in ISR in not corrent */
+#define IRQ_MSG(fmt, args...) \
+do { \
+ printk(KERN_ERR TAG"%d -> "fmt" <- %s() : L<%d>\n", \
+ host->id, ##args , __FUNCTION__, __LINE__); \
+} while(0);
+#endif
+
+int msdc_debug_proc_init(void);
+
+#if 0 /* --- chhung */
+void msdc_init_gpt(void);
+extern void GPT_GetCounter64(UINT32 *cntL32, UINT32 *cntH32);
+#endif /* end of --- */
+u32 msdc_time_calc(u32 old_L32, u32 old_H32, u32 new_L32, u32 new_H32);
+void msdc_performance(u32 opcode, u32 sizes, u32 bRx, u32 ticks);
+
+#endif
diff --git a/drivers/staging/mt7621-mmc/mt6575_sd.h b/drivers/staging/mt7621-mmc/mt6575_sd.h
new file mode 100644
index 000000000000..e90c4f1d1df7
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/mt6575_sd.h
@@ -0,0 +1,1001 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ */
+/* MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#ifndef MT6575_SD_H
+#define MT6575_SD_H
+
+#include <linux/bitops.h>
+#include <linux/mmc/host.h>
+
+// #include <mach/mt6575_reg_base.h> /* --- by chhung */
+
+/*--------------------------------------------------------------------------*/
+/* Common Macro */
+/*--------------------------------------------------------------------------*/
+#define REG_ADDR(x) ((volatile u32*)(base + OFFSET_##x))
+
+/*--------------------------------------------------------------------------*/
+/* Common Definition */
+/*--------------------------------------------------------------------------*/
+#define MSDC_FIFO_SZ (128)
+#define MSDC_FIFO_THD (64) // (128)
+#define MSDC_NUM (4)
+
+#define MSDC_MS (0)
+#define MSDC_SDMMC (1)
+
+#define MSDC_MODE_UNKNOWN (0)
+#define MSDC_MODE_PIO (1)
+#define MSDC_MODE_DMA_BASIC (2)
+#define MSDC_MODE_DMA_DESC (3)
+#define MSDC_MODE_DMA_ENHANCED (4)
+#define MSDC_MODE_MMC_STREAM (5)
+
+#define MSDC_BUS_1BITS (0)
+#define MSDC_BUS_4BITS (1)
+#define MSDC_BUS_8BITS (2)
+
+#define MSDC_BRUST_8B (3)
+#define MSDC_BRUST_16B (4)
+#define MSDC_BRUST_32B (5)
+#define MSDC_BRUST_64B (6)
+
+#define MSDC_PIN_PULL_NONE (0)
+#define MSDC_PIN_PULL_DOWN (1)
+#define MSDC_PIN_PULL_UP (2)
+#define MSDC_PIN_KEEP (3)
+
+#define MSDC_MAX_SCLK (48000000) /* +/- by chhung */
+#define MSDC_MIN_SCLK (260000)
+
+#define MSDC_AUTOCMD12 (0x0001)
+#define MSDC_AUTOCMD23 (0x0002)
+#define MSDC_AUTOCMD19 (0x0003)
+
+#define MSDC_EMMC_BOOTMODE0 (0) /* Pull low CMD mode */
+#define MSDC_EMMC_BOOTMODE1 (1) /* Reset CMD mode */
+
+enum {
+ RESP_NONE = 0,
+ RESP_R1,
+ RESP_R2,
+ RESP_R3,
+ RESP_R4,
+ RESP_R5,
+ RESP_R6,
+ RESP_R7,
+ RESP_R1B
+};
+
+/*--------------------------------------------------------------------------*/
+/* Register Offset */
+/*--------------------------------------------------------------------------*/
+#define OFFSET_MSDC_CFG (0x0)
+#define OFFSET_MSDC_IOCON (0x04)
+#define OFFSET_MSDC_PS (0x08)
+#define OFFSET_MSDC_INT (0x0c)
+#define OFFSET_MSDC_INTEN (0x10)
+#define OFFSET_MSDC_FIFOCS (0x14)
+#define OFFSET_MSDC_TXDATA (0x18)
+#define OFFSET_MSDC_RXDATA (0x1c)
+#define OFFSET_SDC_CFG (0x30)
+#define OFFSET_SDC_CMD (0x34)
+#define OFFSET_SDC_ARG (0x38)
+#define OFFSET_SDC_STS (0x3c)
+#define OFFSET_SDC_RESP0 (0x40)
+#define OFFSET_SDC_RESP1 (0x44)
+#define OFFSET_SDC_RESP2 (0x48)
+#define OFFSET_SDC_RESP3 (0x4c)
+#define OFFSET_SDC_BLK_NUM (0x50)
+#define OFFSET_SDC_CSTS (0x58)
+#define OFFSET_SDC_CSTS_EN (0x5c)
+#define OFFSET_SDC_DCRC_STS (0x60)
+#define OFFSET_EMMC_CFG0 (0x70)
+#define OFFSET_EMMC_CFG1 (0x74)
+#define OFFSET_EMMC_STS (0x78)
+#define OFFSET_EMMC_IOCON (0x7c)
+#define OFFSET_SDC_ACMD_RESP (0x80)
+#define OFFSET_SDC_ACMD19_TRG (0x84)
+#define OFFSET_SDC_ACMD19_STS (0x88)
+#define OFFSET_MSDC_DMA_SA (0x90)
+#define OFFSET_MSDC_DMA_CA (0x94)
+#define OFFSET_MSDC_DMA_CTRL (0x98)
+#define OFFSET_MSDC_DMA_CFG (0x9c)
+#define OFFSET_MSDC_DBG_SEL (0xa0)
+#define OFFSET_MSDC_DBG_OUT (0xa4)
+#define OFFSET_MSDC_PATCH_BIT (0xb0)
+#define OFFSET_MSDC_PATCH_BIT1 (0xb4)
+#define OFFSET_MSDC_PAD_CTL0 (0xe0)
+#define OFFSET_MSDC_PAD_CTL1 (0xe4)
+#define OFFSET_MSDC_PAD_CTL2 (0xe8)
+#define OFFSET_MSDC_PAD_TUNE (0xec)
+#define OFFSET_MSDC_DAT_RDDLY0 (0xf0)
+#define OFFSET_MSDC_DAT_RDDLY1 (0xf4)
+#define OFFSET_MSDC_HW_DBG (0xf8)
+#define OFFSET_MSDC_VERSION (0x100)
+#define OFFSET_MSDC_ECO_VER (0x104)
+
+/*--------------------------------------------------------------------------*/
+/* Register Address */
+/*--------------------------------------------------------------------------*/
+
+/* common register */
+#define MSDC_CFG REG_ADDR(MSDC_CFG)
+#define MSDC_IOCON REG_ADDR(MSDC_IOCON)
+#define MSDC_PS REG_ADDR(MSDC_PS)
+#define MSDC_INT REG_ADDR(MSDC_INT)
+#define MSDC_INTEN REG_ADDR(MSDC_INTEN)
+#define MSDC_FIFOCS REG_ADDR(MSDC_FIFOCS)
+#define MSDC_TXDATA REG_ADDR(MSDC_TXDATA)
+#define MSDC_RXDATA REG_ADDR(MSDC_RXDATA)
+#define MSDC_PATCH_BIT0 REG_ADDR(MSDC_PATCH_BIT)
+
+/* sdmmc register */
+#define SDC_CFG REG_ADDR(SDC_CFG)
+#define SDC_CMD REG_ADDR(SDC_CMD)
+#define SDC_ARG REG_ADDR(SDC_ARG)
+#define SDC_STS REG_ADDR(SDC_STS)
+#define SDC_RESP0 REG_ADDR(SDC_RESP0)
+#define SDC_RESP1 REG_ADDR(SDC_RESP1)
+#define SDC_RESP2 REG_ADDR(SDC_RESP2)
+#define SDC_RESP3 REG_ADDR(SDC_RESP3)
+#define SDC_BLK_NUM REG_ADDR(SDC_BLK_NUM)
+#define SDC_CSTS REG_ADDR(SDC_CSTS)
+#define SDC_CSTS_EN REG_ADDR(SDC_CSTS_EN)
+#define SDC_DCRC_STS REG_ADDR(SDC_DCRC_STS)
+
+/* emmc register*/
+#define EMMC_CFG0 REG_ADDR(EMMC_CFG0)
+#define EMMC_CFG1 REG_ADDR(EMMC_CFG1)
+#define EMMC_STS REG_ADDR(EMMC_STS)
+#define EMMC_IOCON REG_ADDR(EMMC_IOCON)
+
+/* auto command register */
+#define SDC_ACMD_RESP REG_ADDR(SDC_ACMD_RESP)
+#define SDC_ACMD19_TRG REG_ADDR(SDC_ACMD19_TRG)
+#define SDC_ACMD19_STS REG_ADDR(SDC_ACMD19_STS)
+
+/* dma register */
+#define MSDC_DMA_SA REG_ADDR(MSDC_DMA_SA)
+#define MSDC_DMA_CA REG_ADDR(MSDC_DMA_CA)
+#define MSDC_DMA_CTRL REG_ADDR(MSDC_DMA_CTRL)
+#define MSDC_DMA_CFG REG_ADDR(MSDC_DMA_CFG)
+
+/* pad ctrl register */
+#define MSDC_PAD_CTL0 REG_ADDR(MSDC_PAD_CTL0)
+#define MSDC_PAD_CTL1 REG_ADDR(MSDC_PAD_CTL1)
+#define MSDC_PAD_CTL2 REG_ADDR(MSDC_PAD_CTL2)
+
+/* data read delay */
+#define MSDC_DAT_RDDLY0 REG_ADDR(MSDC_DAT_RDDLY0)
+#define MSDC_DAT_RDDLY1 REG_ADDR(MSDC_DAT_RDDLY1)
+
+/* debug register */
+#define MSDC_DBG_SEL REG_ADDR(MSDC_DBG_SEL)
+#define MSDC_DBG_OUT REG_ADDR(MSDC_DBG_OUT)
+
+/* misc register */
+#define MSDC_PATCH_BIT REG_ADDR(MSDC_PATCH_BIT)
+#define MSDC_PATCH_BIT1 REG_ADDR(MSDC_PATCH_BIT1)
+#define MSDC_PAD_TUNE REG_ADDR(MSDC_PAD_TUNE)
+#define MSDC_HW_DBG REG_ADDR(MSDC_HW_DBG)
+#define MSDC_VERSION REG_ADDR(MSDC_VERSION)
+#define MSDC_ECO_VER REG_ADDR(MSDC_ECO_VER) /* ECO Version */
+
+/*--------------------------------------------------------------------------*/
+/* Register Mask */
+/*--------------------------------------------------------------------------*/
+
+/* MSDC_CFG mask */
+#define MSDC_CFG_MODE (0x1 << 0) /* RW */
+#define MSDC_CFG_CKPDN (0x1 << 1) /* RW */
+#define MSDC_CFG_RST (0x1 << 2) /* RW */
+#define MSDC_CFG_PIO (0x1 << 3) /* RW */
+#define MSDC_CFG_CKDRVEN (0x1 << 4) /* RW */
+#define MSDC_CFG_BV18SDT (0x1 << 5) /* RW */
+#define MSDC_CFG_BV18PSS (0x1 << 6) /* R */
+#define MSDC_CFG_CKSTB (0x1 << 7) /* R */
+#define MSDC_CFG_CKDIV (0xff << 8) /* RW */
+#define MSDC_CFG_CKMOD (0x3 << 16) /* RW */
+
+/* MSDC_IOCON mask */
+#define MSDC_IOCON_SDR104CKS (0x1 << 0) /* RW */
+#define MSDC_IOCON_RSPL (0x1 << 1) /* RW */
+#define MSDC_IOCON_DSPL (0x1 << 2) /* RW */
+#define MSDC_IOCON_DDLSEL (0x1 << 3) /* RW */
+#define MSDC_IOCON_DDR50CKD (0x1 << 4) /* RW */
+#define MSDC_IOCON_DSPLSEL (0x1 << 5) /* RW */
+#define MSDC_IOCON_D0SPL (0x1 << 16) /* RW */
+#define MSDC_IOCON_D1SPL (0x1 << 17) /* RW */
+#define MSDC_IOCON_D2SPL (0x1 << 18) /* RW */
+#define MSDC_IOCON_D3SPL (0x1 << 19) /* RW */
+#define MSDC_IOCON_D4SPL (0x1 << 20) /* RW */
+#define MSDC_IOCON_D5SPL (0x1 << 21) /* RW */
+#define MSDC_IOCON_D6SPL (0x1 << 22) /* RW */
+#define MSDC_IOCON_D7SPL (0x1 << 23) /* RW */
+#define MSDC_IOCON_RISCSZ (0x3 << 24) /* RW */
+
+/* MSDC_PS mask */
+#define MSDC_PS_CDEN (0x1 << 0) /* RW */
+#define MSDC_PS_CDSTS (0x1 << 1) /* R */
+#define MSDC_PS_CDDEBOUNCE (0xf << 12) /* RW */
+#define MSDC_PS_DAT (0xff << 16) /* R */
+#define MSDC_PS_CMD (0x1 << 24) /* R */
+#define MSDC_PS_WP (0x1UL<< 31) /* R */
+
+/* MSDC_INT mask */
+#define MSDC_INT_MMCIRQ (0x1 << 0) /* W1C */
+#define MSDC_INT_CDSC (0x1 << 1) /* W1C */
+#define MSDC_INT_ACMDRDY (0x1 << 3) /* W1C */
+#define MSDC_INT_ACMDTMO (0x1 << 4) /* W1C */
+#define MSDC_INT_ACMDCRCERR (0x1 << 5) /* W1C */
+#define MSDC_INT_DMAQ_EMPTY (0x1 << 6) /* W1C */
+#define MSDC_INT_SDIOIRQ (0x1 << 7) /* W1C */
+#define MSDC_INT_CMDRDY (0x1 << 8) /* W1C */
+#define MSDC_INT_CMDTMO (0x1 << 9) /* W1C */
+#define MSDC_INT_RSPCRCERR (0x1 << 10) /* W1C */
+#define MSDC_INT_CSTA (0x1 << 11) /* R */
+#define MSDC_INT_XFER_COMPL (0x1 << 12) /* W1C */
+#define MSDC_INT_DXFER_DONE (0x1 << 13) /* W1C */
+#define MSDC_INT_DATTMO (0x1 << 14) /* W1C */
+#define MSDC_INT_DATCRCERR (0x1 << 15) /* W1C */
+#define MSDC_INT_ACMD19_DONE (0x1 << 16) /* W1C */
+
+/* MSDC_INTEN mask */
+#define MSDC_INTEN_MMCIRQ (0x1 << 0) /* RW */
+#define MSDC_INTEN_CDSC (0x1 << 1) /* RW */
+#define MSDC_INTEN_ACMDRDY (0x1 << 3) /* RW */
+#define MSDC_INTEN_ACMDTMO (0x1 << 4) /* RW */
+#define MSDC_INTEN_ACMDCRCERR (0x1 << 5) /* RW */
+#define MSDC_INTEN_DMAQ_EMPTY (0x1 << 6) /* RW */
+#define MSDC_INTEN_SDIOIRQ (0x1 << 7) /* RW */
+#define MSDC_INTEN_CMDRDY (0x1 << 8) /* RW */
+#define MSDC_INTEN_CMDTMO (0x1 << 9) /* RW */
+#define MSDC_INTEN_RSPCRCERR (0x1 << 10) /* RW */
+#define MSDC_INTEN_CSTA (0x1 << 11) /* RW */
+#define MSDC_INTEN_XFER_COMPL (0x1 << 12) /* RW */
+#define MSDC_INTEN_DXFER_DONE (0x1 << 13) /* RW */
+#define MSDC_INTEN_DATTMO (0x1 << 14) /* RW */
+#define MSDC_INTEN_DATCRCERR (0x1 << 15) /* RW */
+#define MSDC_INTEN_ACMD19_DONE (0x1 << 16) /* RW */
+
+/* MSDC_FIFOCS mask */
+#define MSDC_FIFOCS_RXCNT (0xff << 0) /* R */
+#define MSDC_FIFOCS_TXCNT (0xff << 16) /* R */
+#define MSDC_FIFOCS_CLR (0x1UL<< 31) /* RW */
+
+/* SDC_CFG mask */
+#define SDC_CFG_SDIOINTWKUP (0x1 << 0) /* RW */
+#define SDC_CFG_INSWKUP (0x1 << 1) /* RW */
+#define SDC_CFG_BUSWIDTH (0x3 << 16) /* RW */
+#define SDC_CFG_SDIO (0x1 << 19) /* RW */
+#define SDC_CFG_SDIOIDE (0x1 << 20) /* RW */
+#define SDC_CFG_INTATGAP (0x1 << 21) /* RW */
+#define SDC_CFG_DTOC (0xffUL << 24) /* RW */
+
+/* SDC_CMD mask */
+#define SDC_CMD_OPC (0x3f << 0) /* RW */
+#define SDC_CMD_BRK (0x1 << 6) /* RW */
+#define SDC_CMD_RSPTYP (0x7 << 7) /* RW */
+#define SDC_CMD_DTYP (0x3 << 11) /* RW */
+#define SDC_CMD_DTYP (0x3 << 11) /* RW */
+#define SDC_CMD_RW (0x1 << 13) /* RW */
+#define SDC_CMD_STOP (0x1 << 14) /* RW */
+#define SDC_CMD_GOIRQ (0x1 << 15) /* RW */
+#define SDC_CMD_BLKLEN (0xfff<< 16) /* RW */
+#define SDC_CMD_AUTOCMD (0x3 << 28) /* RW */
+#define SDC_CMD_VOLSWTH (0x1 << 30) /* RW */
+
+/* SDC_STS mask */
+#define SDC_STS_SDCBUSY (0x1 << 0) /* RW */
+#define SDC_STS_CMDBUSY (0x1 << 1) /* RW */
+#define SDC_STS_SWR_COMPL (0x1 << 31) /* RW */
+
+/* SDC_DCRC_STS mask */
+#define SDC_DCRC_STS_NEG (0xf << 8) /* RO */
+#define SDC_DCRC_STS_POS (0xff << 0) /* RO */
+
+/* EMMC_CFG0 mask */
+#define EMMC_CFG0_BOOTSTART (0x1 << 0) /* W */
+#define EMMC_CFG0_BOOTSTOP (0x1 << 1) /* W */
+#define EMMC_CFG0_BOOTMODE (0x1 << 2) /* RW */
+#define EMMC_CFG0_BOOTACKDIS (0x1 << 3) /* RW */
+#define EMMC_CFG0_BOOTWDLY (0x7 << 12) /* RW */
+#define EMMC_CFG0_BOOTSUPP (0x1 << 15) /* RW */
+
+/* EMMC_CFG1 mask */
+#define EMMC_CFG1_BOOTDATTMC (0xfffff << 0) /* RW */
+#define EMMC_CFG1_BOOTACKTMC (0xfffUL << 20) /* RW */
+
+/* EMMC_STS mask */
+#define EMMC_STS_BOOTCRCERR (0x1 << 0) /* W1C */
+#define EMMC_STS_BOOTACKERR (0x1 << 1) /* W1C */
+#define EMMC_STS_BOOTDATTMO (0x1 << 2) /* W1C */
+#define EMMC_STS_BOOTACKTMO (0x1 << 3) /* W1C */
+#define EMMC_STS_BOOTUPSTATE (0x1 << 4) /* R */
+#define EMMC_STS_BOOTACKRCV (0x1 << 5) /* W1C */
+#define EMMC_STS_BOOTDATRCV (0x1 << 6) /* R */
+
+/* EMMC_IOCON mask */
+#define EMMC_IOCON_BOOTRST (0x1 << 0) /* RW */
+
+/* SDC_ACMD19_TRG mask */
+#define SDC_ACMD19_TRG_TUNESEL (0xf << 0) /* RW */
+
+/* MSDC_DMA_CTRL mask */
+#define MSDC_DMA_CTRL_START (0x1 << 0) /* W */
+#define MSDC_DMA_CTRL_STOP (0x1 << 1) /* W */
+#define MSDC_DMA_CTRL_RESUME (0x1 << 2) /* W */
+#define MSDC_DMA_CTRL_MODE (0x1 << 8) /* RW */
+#define MSDC_DMA_CTRL_LASTBUF (0x1 << 10) /* RW */
+#define MSDC_DMA_CTRL_BRUSTSZ (0x7 << 12) /* RW */
+#define MSDC_DMA_CTRL_XFERSZ (0xffffUL << 16)/* RW */
+
+/* MSDC_DMA_CFG mask */
+#define MSDC_DMA_CFG_STS (0x1 << 0) /* R */
+#define MSDC_DMA_CFG_DECSEN (0x1 << 1) /* RW */
+#define MSDC_DMA_CFG_BDCSERR (0x1 << 4) /* R */
+#define MSDC_DMA_CFG_GPDCSERR (0x1 << 5) /* R */
+
+/* MSDC_PATCH_BIT mask */
+#define MSDC_PATCH_BIT_WFLSMODE (0x1 << 0) /* RW */
+#define MSDC_PATCH_BIT_ODDSUPP (0x1 << 1) /* RW */
+#define MSDC_PATCH_BIT_CKGEN_CK (0x1 << 6) /* E2: Fixed to 1 */
+#define MSDC_PATCH_BIT_IODSSEL (0x1 << 16) /* RW */
+#define MSDC_PATCH_BIT_IOINTSEL (0x1 << 17) /* RW */
+#define MSDC_PATCH_BIT_BUSYDLY (0xf << 18) /* RW */
+#define MSDC_PATCH_BIT_WDOD (0xf << 22) /* RW */
+#define MSDC_PATCH_BIT_IDRTSEL (0x1 << 26) /* RW */
+#define MSDC_PATCH_BIT_CMDFSEL (0x1 << 27) /* RW */
+#define MSDC_PATCH_BIT_INTDLSEL (0x1 << 28) /* RW */
+#define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */
+#define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */
+
+/* MSDC_PATCH_BIT1 mask */
+#define MSDC_PATCH_BIT1_WRDAT_CRCS (0x7 << 3)
+#define MSDC_PATCH_BIT1_CMD_RSP (0x7 << 0)
+
+/* MSDC_PAD_CTL0 mask */
+#define MSDC_PAD_CTL0_CLKDRVN (0x7 << 0) /* RW */
+#define MSDC_PAD_CTL0_CLKDRVP (0x7 << 4) /* RW */
+#define MSDC_PAD_CTL0_CLKSR (0x1 << 8) /* RW */
+#define MSDC_PAD_CTL0_CLKPD (0x1 << 16) /* RW */
+#define MSDC_PAD_CTL0_CLKPU (0x1 << 17) /* RW */
+#define MSDC_PAD_CTL0_CLKSMT (0x1 << 18) /* RW */
+#define MSDC_PAD_CTL0_CLKIES (0x1 << 19) /* RW */
+#define MSDC_PAD_CTL0_CLKTDSEL (0xf << 20) /* RW */
+#define MSDC_PAD_CTL0_CLKRDSEL (0xffUL<< 24) /* RW */
+
+/* MSDC_PAD_CTL1 mask */
+#define MSDC_PAD_CTL1_CMDDRVN (0x7 << 0) /* RW */
+#define MSDC_PAD_CTL1_CMDDRVP (0x7 << 4) /* RW */
+#define MSDC_PAD_CTL1_CMDSR (0x1 << 8) /* RW */
+#define MSDC_PAD_CTL1_CMDPD (0x1 << 16) /* RW */
+#define MSDC_PAD_CTL1_CMDPU (0x1 << 17) /* RW */
+#define MSDC_PAD_CTL1_CMDSMT (0x1 << 18) /* RW */
+#define MSDC_PAD_CTL1_CMDIES (0x1 << 19) /* RW */
+#define MSDC_PAD_CTL1_CMDTDSEL (0xf << 20) /* RW */
+#define MSDC_PAD_CTL1_CMDRDSEL (0xffUL<< 24) /* RW */
+
+/* MSDC_PAD_CTL2 mask */
+#define MSDC_PAD_CTL2_DATDRVN (0x7 << 0) /* RW */
+#define MSDC_PAD_CTL2_DATDRVP (0x7 << 4) /* RW */
+#define MSDC_PAD_CTL2_DATSR (0x1 << 8) /* RW */
+#define MSDC_PAD_CTL2_DATPD (0x1 << 16) /* RW */
+#define MSDC_PAD_CTL2_DATPU (0x1 << 17) /* RW */
+#define MSDC_PAD_CTL2_DATIES (0x1 << 19) /* RW */
+#define MSDC_PAD_CTL2_DATSMT (0x1 << 18) /* RW */
+#define MSDC_PAD_CTL2_DATTDSEL (0xf << 20) /* RW */
+#define MSDC_PAD_CTL2_DATRDSEL (0xffUL<< 24) /* RW */
+
+/* MSDC_PAD_TUNE mask */
+#define MSDC_PAD_TUNE_DATWRDLY (0x1F << 0) /* RW */
+#define MSDC_PAD_TUNE_DATRRDLY (0x1F << 8) /* RW */
+#define MSDC_PAD_TUNE_CMDRDLY (0x1F << 16) /* RW */
+#define MSDC_PAD_TUNE_CMDRRDLY (0x1FUL << 22) /* RW */
+#define MSDC_PAD_TUNE_CLKTXDLY (0x1FUL << 27) /* RW */
+
+/* MSDC_DAT_RDDLY0/1 mask */
+#define MSDC_DAT_RDDLY0_D0 (0x1F << 0) /* RW */
+#define MSDC_DAT_RDDLY0_D1 (0x1F << 8) /* RW */
+#define MSDC_DAT_RDDLY0_D2 (0x1F << 16) /* RW */
+#define MSDC_DAT_RDDLY0_D3 (0x1F << 24) /* RW */
+
+#define MSDC_DAT_RDDLY1_D4 (0x1F << 0) /* RW */
+#define MSDC_DAT_RDDLY1_D5 (0x1F << 8) /* RW */
+#define MSDC_DAT_RDDLY1_D6 (0x1F << 16) /* RW */
+#define MSDC_DAT_RDDLY1_D7 (0x1F << 24) /* RW */
+
+#define MSDC_CKGEN_MSDC_DLY_SEL (0x1F<<10)
+#define MSDC_INT_DAT_LATCH_CK_SEL (0x7<<7)
+#define MSDC_CKGEN_MSDC_CK_SEL (0x1<<6)
+#define CARD_READY_FOR_DATA (1<<8)
+#define CARD_CURRENT_STATE(x) ((x&0x00001E00)>>9)
+
+/*--------------------------------------------------------------------------*/
+/* Descriptor Structure */
+/*--------------------------------------------------------------------------*/
+typedef struct {
+ u32 hwo:1; /* could be changed by hw */
+ u32 bdp:1;
+ u32 rsv0:6;
+ u32 chksum:8;
+ u32 intr:1;
+ u32 rsv1:15;
+ void *next;
+ void *ptr;
+ u32 buflen:16;
+ u32 extlen:8;
+ u32 rsv2:8;
+ u32 arg;
+ u32 blknum;
+ u32 cmd;
+} gpd_t;
+
+typedef struct {
+ u32 eol:1;
+ u32 rsv0:7;
+ u32 chksum:8;
+ u32 rsv1:1;
+ u32 blkpad:1;
+ u32 dwpad:1;
+ u32 rsv2:13;
+ void *next;
+ void *ptr;
+ u32 buflen:16;
+ u32 rsv3:16;
+} bd_t;
+
+/*--------------------------------------------------------------------------*/
+/* Register Debugging Structure */
+/*--------------------------------------------------------------------------*/
+
+typedef struct {
+ u32 msdc:1;
+ u32 ckpwn:1;
+ u32 rst:1;
+ u32 pio:1;
+ u32 ckdrven:1;
+ u32 start18v:1;
+ u32 pass18v:1;
+ u32 ckstb:1;
+ u32 ckdiv:8;
+ u32 ckmod:2;
+ u32 pad:14;
+} msdc_cfg_reg;
+typedef struct {
+ u32 sdr104cksel:1;
+ u32 rsmpl:1;
+ u32 dsmpl:1;
+ u32 ddlysel:1;
+ u32 ddr50ckd:1;
+ u32 dsplsel:1;
+ u32 pad1:10;
+ u32 d0spl:1;
+ u32 d1spl:1;
+ u32 d2spl:1;
+ u32 d3spl:1;
+ u32 d4spl:1;
+ u32 d5spl:1;
+ u32 d6spl:1;
+ u32 d7spl:1;
+ u32 riscsz:1;
+ u32 pad2:7;
+} msdc_iocon_reg;
+typedef struct {
+ u32 cden:1;
+ u32 cdsts:1;
+ u32 pad1:10;
+ u32 cddebounce:4;
+ u32 dat:8;
+ u32 cmd:1;
+ u32 pad2:6;
+ u32 wp:1;
+} msdc_ps_reg;
+typedef struct {
+ u32 mmcirq:1;
+ u32 cdsc:1;
+ u32 pad1:1;
+ u32 atocmdrdy:1;
+ u32 atocmdtmo:1;
+ u32 atocmdcrc:1;
+ u32 dmaqempty:1;
+ u32 sdioirq:1;
+ u32 cmdrdy:1;
+ u32 cmdtmo:1;
+ u32 rspcrc:1;
+ u32 csta:1;
+ u32 xfercomp:1;
+ u32 dxferdone:1;
+ u32 dattmo:1;
+ u32 datcrc:1;
+ u32 atocmd19done:1;
+ u32 pad2:15;
+} msdc_int_reg;
+typedef struct {
+ u32 mmcirq:1;
+ u32 cdsc:1;
+ u32 pad1:1;
+ u32 atocmdrdy:1;
+ u32 atocmdtmo:1;
+ u32 atocmdcrc:1;
+ u32 dmaqempty:1;
+ u32 sdioirq:1;
+ u32 cmdrdy:1;
+ u32 cmdtmo:1;
+ u32 rspcrc:1;
+ u32 csta:1;
+ u32 xfercomp:1;
+ u32 dxferdone:1;
+ u32 dattmo:1;
+ u32 datcrc:1;
+ u32 atocmd19done:1;
+ u32 pad2:15;
+} msdc_inten_reg;
+typedef struct {
+ u32 rxcnt:8;
+ u32 pad1:8;
+ u32 txcnt:8;
+ u32 pad2:7;
+ u32 clr:1;
+} msdc_fifocs_reg;
+typedef struct {
+ u32 val;
+} msdc_txdat_reg;
+typedef struct {
+ u32 val;
+} msdc_rxdat_reg;
+typedef struct {
+ u32 sdiowkup:1;
+ u32 inswkup:1;
+ u32 pad1:14;
+ u32 buswidth:2;
+ u32 pad2:1;
+ u32 sdio:1;
+ u32 sdioide:1;
+ u32 intblkgap:1;
+ u32 pad4:2;
+ u32 dtoc:8;
+} sdc_cfg_reg;
+typedef struct {
+ u32 cmd:6;
+ u32 brk:1;
+ u32 rsptyp:3;
+ u32 pad1:1;
+ u32 dtype:2;
+ u32 rw:1;
+ u32 stop:1;
+ u32 goirq:1;
+ u32 blklen:12;
+ u32 atocmd:2;
+ u32 volswth:1;
+ u32 pad2:1;
+} sdc_cmd_reg;
+typedef struct {
+ u32 arg;
+} sdc_arg_reg;
+typedef struct {
+ u32 sdcbusy:1;
+ u32 cmdbusy:1;
+ u32 pad:29;
+ u32 swrcmpl:1;
+} sdc_sts_reg;
+typedef struct {
+ u32 val;
+} sdc_resp0_reg;
+typedef struct {
+ u32 val;
+} sdc_resp1_reg;
+typedef struct {
+ u32 val;
+} sdc_resp2_reg;
+typedef struct {
+ u32 val;
+} sdc_resp3_reg;
+typedef struct {
+ u32 num;
+} sdc_blknum_reg;
+typedef struct {
+ u32 sts;
+} sdc_csts_reg;
+typedef struct {
+ u32 sts;
+} sdc_cstsen_reg;
+typedef struct {
+ u32 datcrcsts:8;
+ u32 ddrcrcsts:4;
+ u32 pad:20;
+} sdc_datcrcsts_reg;
+typedef struct {
+ u32 bootstart:1;
+ u32 bootstop:1;
+ u32 bootmode:1;
+ u32 pad1:9;
+ u32 bootwaidly:3;
+ u32 bootsupp:1;
+ u32 pad2:16;
+} emmc_cfg0_reg;
+typedef struct {
+ u32 bootcrctmc:16;
+ u32 pad:4;
+ u32 bootacktmc:12;
+} emmc_cfg1_reg;
+typedef struct {
+ u32 bootcrcerr:1;
+ u32 bootackerr:1;
+ u32 bootdattmo:1;
+ u32 bootacktmo:1;
+ u32 bootupstate:1;
+ u32 bootackrcv:1;
+ u32 bootdatrcv:1;
+ u32 pad:25;
+} emmc_sts_reg;
+typedef struct {
+ u32 bootrst:1;
+ u32 pad:31;
+} emmc_iocon_reg;
+typedef struct {
+ u32 val;
+} msdc_acmd_resp_reg;
+typedef struct {
+ u32 tunesel:4;
+ u32 pad:28;
+} msdc_acmd19_trg_reg;
+typedef struct {
+ u32 val;
+} msdc_acmd19_sts_reg;
+typedef struct {
+ u32 addr;
+} msdc_dma_sa_reg;
+typedef struct {
+ u32 addr;
+} msdc_dma_ca_reg;
+typedef struct {
+ u32 start:1;
+ u32 stop:1;
+ u32 resume:1;
+ u32 pad1:5;
+ u32 mode:1;
+ u32 pad2:1;
+ u32 lastbuf:1;
+ u32 pad3:1;
+ u32 brustsz:3;
+ u32 pad4:1;
+ u32 xfersz:16;
+} msdc_dma_ctrl_reg;
+typedef struct {
+ u32 status:1;
+ u32 decsen:1;
+ u32 pad1:2;
+ u32 bdcsen:1;
+ u32 gpdcsen:1;
+ u32 pad2:26;
+} msdc_dma_cfg_reg;
+typedef struct {
+ u32 sel:16;
+ u32 pad2:16;
+} msdc_dbg_sel_reg;
+typedef struct {
+ u32 val;
+} msdc_dbg_out_reg;
+typedef struct {
+ u32 clkdrvn:3;
+ u32 rsv0:1;
+ u32 clkdrvp:3;
+ u32 rsv1:1;
+ u32 clksr:1;
+ u32 rsv2:7;
+ u32 clkpd:1;
+ u32 clkpu:1;
+ u32 clksmt:1;
+ u32 clkies:1;
+ u32 clktdsel:4;
+ u32 clkrdsel:8;
+} msdc_pad_ctl0_reg;
+typedef struct {
+ u32 cmddrvn:3;
+ u32 rsv0:1;
+ u32 cmddrvp:3;
+ u32 rsv1:1;
+ u32 cmdsr:1;
+ u32 rsv2:7;
+ u32 cmdpd:1;
+ u32 cmdpu:1;
+ u32 cmdsmt:1;
+ u32 cmdies:1;
+ u32 cmdtdsel:4;
+ u32 cmdrdsel:8;
+} msdc_pad_ctl1_reg;
+typedef struct {
+ u32 datdrvn:3;
+ u32 rsv0:1;
+ u32 datdrvp:3;
+ u32 rsv1:1;
+ u32 datsr:1;
+ u32 rsv2:7;
+ u32 datpd:1;
+ u32 datpu:1;
+ u32 datsmt:1;
+ u32 daties:1;
+ u32 dattdsel:4;
+ u32 datrdsel:8;
+} msdc_pad_ctl2_reg;
+typedef struct {
+ u32 wrrxdly:3;
+ u32 pad1:5;
+ u32 rdrxdly:8;
+ u32 pad2:16;
+} msdc_pad_tune_reg;
+typedef struct {
+ u32 dat0:5;
+ u32 rsv0:3;
+ u32 dat1:5;
+ u32 rsv1:3;
+ u32 dat2:5;
+ u32 rsv2:3;
+ u32 dat3:5;
+ u32 rsv3:3;
+} msdc_dat_rddly0;
+typedef struct {
+ u32 dat4:5;
+ u32 rsv4:3;
+ u32 dat5:5;
+ u32 rsv5:3;
+ u32 dat6:5;
+ u32 rsv6:3;
+ u32 dat7:5;
+ u32 rsv7:3;
+} msdc_dat_rddly1;
+typedef struct {
+ u32 dbg0sel:8;
+ u32 dbg1sel:6;
+ u32 pad1:2;
+ u32 dbg2sel:6;
+ u32 pad2:2;
+ u32 dbg3sel:6;
+ u32 pad3:2;
+} msdc_hw_dbg_reg;
+typedef struct {
+ u32 val;
+} msdc_version_reg;
+typedef struct {
+ u32 val;
+} msdc_eco_ver_reg;
+
+struct msdc_regs {
+ msdc_cfg_reg msdc_cfg; /* base+0x00h */
+ msdc_iocon_reg msdc_iocon; /* base+0x04h */
+ msdc_ps_reg msdc_ps; /* base+0x08h */
+ msdc_int_reg msdc_int; /* base+0x0ch */
+ msdc_inten_reg msdc_inten; /* base+0x10h */
+ msdc_fifocs_reg msdc_fifocs; /* base+0x14h */
+ msdc_txdat_reg msdc_txdat; /* base+0x18h */
+ msdc_rxdat_reg msdc_rxdat; /* base+0x1ch */
+ u32 rsv1[4];
+ sdc_cfg_reg sdc_cfg; /* base+0x30h */
+ sdc_cmd_reg sdc_cmd; /* base+0x34h */
+ sdc_arg_reg sdc_arg; /* base+0x38h */
+ sdc_sts_reg sdc_sts; /* base+0x3ch */
+ sdc_resp0_reg sdc_resp0; /* base+0x40h */
+ sdc_resp1_reg sdc_resp1; /* base+0x44h */
+ sdc_resp2_reg sdc_resp2; /* base+0x48h */
+ sdc_resp3_reg sdc_resp3; /* base+0x4ch */
+ sdc_blknum_reg sdc_blknum; /* base+0x50h */
+ u32 rsv2[1];
+ sdc_csts_reg sdc_csts; /* base+0x58h */
+ sdc_cstsen_reg sdc_cstsen; /* base+0x5ch */
+ sdc_datcrcsts_reg sdc_dcrcsta; /* base+0x60h */
+ u32 rsv3[3];
+ emmc_cfg0_reg emmc_cfg0; /* base+0x70h */
+ emmc_cfg1_reg emmc_cfg1; /* base+0x74h */
+ emmc_sts_reg emmc_sts; /* base+0x78h */
+ emmc_iocon_reg emmc_iocon; /* base+0x7ch */
+ msdc_acmd_resp_reg acmd_resp; /* base+0x80h */
+ msdc_acmd19_trg_reg acmd19_trg; /* base+0x84h */
+ msdc_acmd19_sts_reg acmd19_sts; /* base+0x88h */
+ u32 rsv4[1];
+ msdc_dma_sa_reg dma_sa; /* base+0x90h */
+ msdc_dma_ca_reg dma_ca; /* base+0x94h */
+ msdc_dma_ctrl_reg dma_ctrl; /* base+0x98h */
+ msdc_dma_cfg_reg dma_cfg; /* base+0x9ch */
+ msdc_dbg_sel_reg dbg_sel; /* base+0xa0h */
+ msdc_dbg_out_reg dbg_out; /* base+0xa4h */
+ u32 rsv5[2];
+ u32 patch0; /* base+0xb0h */
+ u32 patch1; /* base+0xb4h */
+ u32 rsv6[10];
+ msdc_pad_ctl0_reg pad_ctl0; /* base+0xe0h */
+ msdc_pad_ctl1_reg pad_ctl1; /* base+0xe4h */
+ msdc_pad_ctl2_reg pad_ctl2; /* base+0xe8h */
+ msdc_pad_tune_reg pad_tune; /* base+0xech */
+ msdc_dat_rddly0 dat_rddly0; /* base+0xf0h */
+ msdc_dat_rddly1 dat_rddly1; /* base+0xf4h */
+ msdc_hw_dbg_reg hw_dbg; /* base+0xf8h */
+ u32 rsv7[1];
+ msdc_version_reg version; /* base+0x100h */
+ msdc_eco_ver_reg eco_ver; /* base+0x104h */
+};
+
+struct scatterlist_ex {
+ u32 cmd;
+ u32 arg;
+ u32 sglen;
+ struct scatterlist *sg;
+};
+
+#define DMA_FLAG_NONE (0x00000000)
+#define DMA_FLAG_EN_CHKSUM (0x00000001)
+#define DMA_FLAG_PAD_BLOCK (0x00000002)
+#define DMA_FLAG_PAD_DWORD (0x00000004)
+
+struct msdc_dma {
+ u32 flags; /* flags */
+ u32 xfersz; /* xfer size in bytes */
+ u32 sglen; /* size of scatter list */
+ u32 blklen; /* block size */
+ struct scatterlist *sg; /* I/O scatter list */
+ struct scatterlist_ex *esg; /* extended I/O scatter list */
+ u8 mode; /* dma mode */
+ u8 burstsz; /* burst size */
+ u8 intr; /* dma done interrupt */
+ u8 padding; /* padding */
+ u32 cmd; /* enhanced mode command */
+ u32 arg; /* enhanced mode arg */
+ u32 rsp; /* enhanced mode command response */
+ u32 autorsp; /* auto command response */
+
+ gpd_t *gpd; /* pointer to gpd array */
+ bd_t *bd; /* pointer to bd array */
+ dma_addr_t gpd_addr; /* the physical address of gpd array */
+ dma_addr_t bd_addr; /* the physical address of bd array */
+ u32 used_gpd; /* the number of used gpd elements */
+ u32 used_bd; /* the number of used bd elements */
+};
+
+struct msdc_host
+{
+ struct msdc_hw *hw;
+
+ struct mmc_host *mmc; /* mmc structure */
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ struct mmc_request *mrq;
+ int cmd_rsp;
+ int cmd_rsp_done;
+ int cmd_r1b_done;
+
+ int error;
+ spinlock_t lock; /* mutex */
+ struct semaphore sem;
+
+ u32 blksz; /* host block size */
+ u32 base; /* host base address */
+ int id; /* host id */
+ int pwr_ref; /* core power reference count */
+
+ u32 xfer_size; /* total transferred size */
+
+ struct msdc_dma dma; /* dma channel */
+ u32 dma_addr; /* dma transfer address */
+ u32 dma_left_size; /* dma transfer left size */
+ u32 dma_xfer_size; /* dma transfer size in bytes */
+ int dma_xfer; /* dma transfer mode */
+
+ u32 timeout_ns; /* data timeout ns */
+ u32 timeout_clks; /* data timeout clks */
+
+ atomic_t abort; /* abort transfer */
+
+ int irq; /* host interrupt */
+
+ struct tasklet_struct card_tasklet;
+#if 0
+ struct work_struct card_workqueue;
+#else
+ struct delayed_work card_delaywork;
+#endif
+
+ struct completion cmd_done;
+ struct completion xfer_done;
+ struct pm_message pm_state;
+
+ u32 mclk; /* mmc subsystem clock */
+ u32 hclk; /* host clock speed */
+ u32 sclk; /* SD/MS clock speed */
+ u8 core_clkon; /* Host core clock on ? */
+ u8 card_clkon; /* Card clock on ? */
+ u8 core_power; /* core power */
+ u8 power_mode; /* host power mode */
+ u8 card_inserted; /* card inserted ? */
+ u8 suspend; /* host suspended ? */
+ u8 reserved;
+ u8 app_cmd; /* for app command */
+ u32 app_cmd_arg;
+ u64 starttime;
+};
+
+static inline unsigned int uffs(unsigned int x)
+{
+ unsigned int r = 1;
+
+ if (!x)
+ return 0;
+ if (!(x & 0xffff)) {
+ x >>= 16;
+ r += 16;
+ }
+ if (!(x & 0xff)) {
+ x >>= 8;
+ r += 8;
+ }
+ if (!(x & 0xf)) {
+ x >>= 4;
+ r += 4;
+ }
+ if (!(x & 3)) {
+ x >>= 2;
+ r += 2;
+ }
+ if (!(x & 1)) {
+ x >>= 1;
+ r += 1;
+ }
+ return r;
+}
+#define sdr_read8(reg) __raw_readb(reg)
+#define sdr_read16(reg) __raw_readw(reg)
+#define sdr_read32(reg) __raw_readl(reg)
+#define sdr_write8(reg,val) __raw_writeb(val,reg)
+#define sdr_write16(reg,val) __raw_writew(val,reg)
+#define sdr_write32(reg,val) __raw_writel(val,reg)
+
+#define sdr_set_bits(reg,bs) ((*(volatile u32*)(reg)) |= (u32)(bs))
+#define sdr_clr_bits(reg,bs) ((*(volatile u32*)(reg)) &= ~((u32)(bs)))
+
+#define sdr_set_field(reg,field,val) \
+ do { \
+ volatile unsigned int tv = sdr_read32(reg); \
+ tv &= ~(field); \
+ tv |= ((val) << (uffs((unsigned int)field) - 1)); \
+ sdr_write32(reg,tv); \
+ } while(0)
+#define sdr_get_field(reg,field,val) \
+ do { \
+ volatile unsigned int tv = sdr_read32(reg); \
+ val = ((tv & (field)) >> (uffs((unsigned int)field) - 1)); \
+ } while(0)
+
+#endif
+
diff --git a/drivers/staging/mt7621-mmc/sd.c b/drivers/staging/mt7621-mmc/sd.c
new file mode 100644
index 000000000000..a1d0173eba56
--- /dev/null
+++ b/drivers/staging/mt7621-mmc/sd.c
@@ -0,0 +1,3074 @@
+/* Copyright Statement:
+ *
+ * This software/firmware and related documentation ("MediaTek Software") are
+ * protected under relevant copyright laws. The information contained herein
+ * is confidential and proprietary to MediaTek Inc. and/or its licensors.
+ * Without the prior written permission of MediaTek inc. and/or its licensors,
+ * any reproduction, modification, use or disclosure of MediaTek Software,
+ * and information contained herein, in whole or in part, shall be strictly prohibited.
+ *
+ * MediaTek Inc. (C) 2010. All rights reserved.
+ *
+ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
+ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
+ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
+ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
+ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
+ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
+ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
+ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
+ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
+ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
+ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
+ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
+ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
+ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
+ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
+ *
+ * The following software/firmware and/or related documentation ("MediaTek Software")
+ * have been modified by MediaTek Inc. All revisions are subject to any receiver's
+ * applicable license agreements with MediaTek Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
+#include <linux/dma-mapping.h>
+
+/* +++ by chhung */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+
+#define MSDC_SMPL_FALLING (1)
+#define MSDC_CD_PIN_EN (1 << 0) /* card detection pin is wired */
+#define MSDC_WP_PIN_EN (1 << 1) /* write protection pin is wired */
+#define MSDC_REMOVABLE (1 << 5) /* removable slot */
+#define MSDC_SYS_SUSPEND (1 << 6) /* suspended by system */
+#define MSDC_HIGHSPEED (1 << 7)
+
+//#define IRQ_SDC 14 //MT7620 /*FIXME*/
+#ifdef CONFIG_SOC_MT7621
+#define RALINK_SYSCTL_BASE 0xbe000000
+#define RALINK_MSDC_BASE 0xbe130000
+#else
+#define RALINK_SYSCTL_BASE 0xb0000000
+#define RALINK_MSDC_BASE 0xb0130000
+#endif
+#define IRQ_SDC 22 /*FIXME*/
+
+#include <asm/dma.h>
+/* end of +++ */
+
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#if 0 /* --- by chhung */
+#include <mach/board.h>
+#include <mach/mt6575_devs.h>
+#include <mach/mt6575_typedefs.h>
+#include <mach/mt6575_clock_manager.h>
+#include <mach/mt6575_pm_ldo.h>
+//#include <mach/mt6575_pll.h>
+//#include <mach/mt6575_gpio.h>
+//#include <mach/mt6575_gpt_sw.h>
+#include <asm/tcm.h>
+// #include <mach/mt6575_gpt.h>
+#endif /* end of --- */
+
+#include "mt6575_sd.h"
+#include "dbg.h"
+
+/* +++ by chhung */
+#include "board.h"
+/* end of +++ */
+
+#if 0 /* --- by chhung */
+#define isb() __asm__ __volatile__ ("" : : : "memory")
+#define dsb() __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 4" \
+ : : "r" (0) : "memory")
+#define dmb() __asm__ __volatile__ ("" : : : "memory")
+#endif /* end of --- */
+
+#define DRV_NAME "mtk-sd"
+
+#define HOST_MAX_NUM (1) /* +/- by chhung */
+
+#if defined (CONFIG_SOC_MT7620)
+#define HOST_MAX_MCLK (48000000) /* +/- by chhung */
+#elif defined (CONFIG_SOC_MT7621)
+#define HOST_MAX_MCLK (50000000) /* +/- by chhung */
+#endif
+#define HOST_MIN_MCLK (260000)
+
+#define HOST_MAX_BLKSZ (2048)
+
+#define MSDC_OCR_AVAIL (MMC_VDD_28_29 | MMC_VDD_29_30 | MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33)
+
+#define GPIO_PULL_DOWN (0)
+#define GPIO_PULL_UP (1)
+
+#if 0 /* --- by chhung */
+#define MSDC_CLKSRC_REG (0xf100000C)
+#define PDN_REG (0xF1000010)
+#endif /* end of --- */
+
+#define DEFAULT_DEBOUNCE (8) /* 8 cycles */
+#define DEFAULT_DTOC (40) /* data timeout counter. 65536x40 sclk. */
+
+#define CMD_TIMEOUT (HZ/10) /* 100ms */
+#define DAT_TIMEOUT (HZ/2 * 5) /* 500ms x5 */
+
+#define MAX_DMA_CNT (64 * 1024 - 512) /* a single transaction for WIFI may be 50K*/
+
+#define MAX_GPD_NUM (1 + 1) /* one null gpd */
+#define MAX_BD_NUM (1024)
+#define MAX_BD_PER_GPD (MAX_BD_NUM)
+
+#define MAX_HW_SGMTS (MAX_BD_NUM)
+#define MAX_PHY_SGMTS (MAX_BD_NUM)
+#define MAX_SGMT_SZ (MAX_DMA_CNT)
+#define MAX_REQ_SZ (MAX_SGMT_SZ * 8)
+
+#ifdef MT6575_SD_DEBUG
+static struct msdc_regs *msdc_reg[HOST_MAX_NUM];
+#endif
+
+static int mtk_sw_poll;
+
+static int cd_active_low = 1;
+
+//=================================
+#define PERI_MSDC0_PDN (15)
+//#define PERI_MSDC1_PDN (16)
+//#define PERI_MSDC2_PDN (17)
+//#define PERI_MSDC3_PDN (18)
+
+struct msdc_host *msdc_6575_host[] = {NULL,NULL,NULL,NULL};
+#if 0 /* --- by chhung */
+/* gate means clock power down */
+static int g_clk_gate = 0;
+#define msdc_gate_clock(id) \
+ do { \
+ g_clk_gate &= ~(1 << ((id) + PERI_MSDC0_PDN)); \
+ } while(0)
+/* not like power down register. 1 means clock on. */
+#define msdc_ungate_clock(id) \
+ do { \
+ g_clk_gate |= 1 << ((id) + PERI_MSDC0_PDN); \
+ } while(0)
+
+// do we need sync object or not
+void msdc_clk_status(int * status)
+{
+ *status = g_clk_gate;
+}
+#endif /* end of --- */
+
+/* +++ by chhung */
+struct msdc_hw msdc0_hw = {
+ .clk_src = 0,
+ .cmd_edge = MSDC_SMPL_FALLING,
+ .data_edge = MSDC_SMPL_FALLING,
+ .clk_drv = 4,
+ .cmd_drv = 4,
+ .dat_drv = 4,
+ .data_pins = 4,
+ .data_offset = 0,
+ .flags = MSDC_SYS_SUSPEND | MSDC_CD_PIN_EN | MSDC_REMOVABLE | MSDC_HIGHSPEED,
+// .flags = MSDC_SYS_SUSPEND | MSDC_WP_PIN_EN | MSDC_CD_PIN_EN | MSDC_REMOVABLE,
+};
+
+static struct resource mtk_sd_resources[] = {
+ [0] = {
+ .start = RALINK_MSDC_BASE,
+ .end = RALINK_MSDC_BASE+0x3fff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_SDC, /*FIXME*/
+ .end = IRQ_SDC, /*FIXME*/
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device mtk_sd_device = {
+ .name = "mtk-sd",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mtk_sd_resources),
+ .resource = mtk_sd_resources,
+};
+/* end of +++ */
+
+static int msdc_rsp[] = {
+ 0, /* RESP_NONE */
+ 1, /* RESP_R1 */
+ 2, /* RESP_R2 */
+ 3, /* RESP_R3 */
+ 4, /* RESP_R4 */
+ 1, /* RESP_R5 */
+ 1, /* RESP_R6 */
+ 1, /* RESP_R7 */
+ 7, /* RESP_R1b */
+};
+
+/* For Inhanced DMA */
+#define msdc_init_gpd_ex(gpd,extlen,cmd,arg,blknum) \
+ do { \
+ ((gpd_t*)gpd)->extlen = extlen; \
+ ((gpd_t*)gpd)->cmd = cmd; \
+ ((gpd_t*)gpd)->arg = arg; \
+ ((gpd_t*)gpd)->blknum = blknum; \
+ }while(0)
+
+#define msdc_init_bd(bd, blkpad, dwpad, dptr, dlen) \
+ do { \
+ BUG_ON(dlen > 0xFFFFUL); \
+ ((bd_t*)bd)->blkpad = blkpad; \
+ ((bd_t*)bd)->dwpad = dwpad; \
+ ((bd_t*)bd)->ptr = (void*)dptr; \
+ ((bd_t*)bd)->buflen = dlen; \
+ }while(0)
+
+#define msdc_txfifocnt() ((sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_TXCNT) >> 16)
+#define msdc_rxfifocnt() ((sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_RXCNT) >> 0)
+#define msdc_fifo_write32(v) sdr_write32(MSDC_TXDATA, (v))
+#define msdc_fifo_write8(v) sdr_write8(MSDC_TXDATA, (v))
+#define msdc_fifo_read32() sdr_read32(MSDC_RXDATA)
+#define msdc_fifo_read8() sdr_read8(MSDC_RXDATA)
+
+
+#define msdc_dma_on() sdr_clr_bits(MSDC_CFG, MSDC_CFG_PIO)
+#define msdc_dma_off() sdr_set_bits(MSDC_CFG, MSDC_CFG_PIO)
+
+#define msdc_retry(expr,retry,cnt) \
+ do { \
+ int backup = cnt; \
+ while (retry) { \
+ if (!(expr)) break; \
+ if (cnt-- == 0) { \
+ retry--; mdelay(1); cnt = backup; \
+ } \
+ } \
+ WARN_ON(retry == 0); \
+ } while(0)
+
+#if 0 /* --- by chhung */
+#define msdc_reset() \
+ do { \
+ int retry = 3, cnt = 1000; \
+ sdr_set_bits(MSDC_CFG, MSDC_CFG_RST); \
+ dsb(); \
+ msdc_retry(sdr_read32(MSDC_CFG) & MSDC_CFG_RST, retry, cnt); \
+ } while(0)
+#else
+#define msdc_reset() \
+ do { \
+ int retry = 3, cnt = 1000; \
+ sdr_set_bits(MSDC_CFG, MSDC_CFG_RST); \
+ msdc_retry(sdr_read32(MSDC_CFG) & MSDC_CFG_RST, retry, cnt); \
+ } while(0)
+#endif /* end of +/- */
+
+#define msdc_clr_int() \
+ do { \
+ volatile u32 val = sdr_read32(MSDC_INT); \
+ sdr_write32(MSDC_INT, val); \
+ } while(0)
+
+#define msdc_clr_fifo() \
+ do { \
+ int retry = 3, cnt = 1000; \
+ sdr_set_bits(MSDC_FIFOCS, MSDC_FIFOCS_CLR); \
+ msdc_retry(sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_CLR, retry, cnt); \
+ } while(0)
+
+#define msdc_irq_save(val) \
+ do { \
+ val = sdr_read32(MSDC_INTEN); \
+ sdr_clr_bits(MSDC_INTEN, val); \
+ } while(0)
+
+#define msdc_irq_restore(val) \
+ do { \
+ sdr_set_bits(MSDC_INTEN, val); \
+ } while(0)
+
+/* clock source for host: global */
+#if defined (CONFIG_SOC_MT7620)
+static u32 hclks[] = {48000000}; /* +/- by chhung */
+#elif defined (CONFIG_SOC_MT7621)
+static u32 hclks[] = {50000000}; /* +/- by chhung */
+#endif
+
+//============================================
+// the power for msdc host controller: global
+// always keep the VMC on.
+//============================================
+#define msdc_vcore_on(host) \
+ do { \
+ INIT_MSG("[+]VMC ref. count<%d>", ++host->pwr_ref); \
+ (void)hwPowerOn(MT65XX_POWER_LDO_VMC, VOL_3300, "SD"); \
+ } while (0)
+#define msdc_vcore_off(host) \
+ do { \
+ INIT_MSG("[-]VMC ref. count<%d>", --host->pwr_ref); \
+ (void)hwPowerDown(MT65XX_POWER_LDO_VMC, "SD"); \
+ } while (0)
+
+//====================================
+// the vdd output for card: global
+// always keep the VMCH on.
+//====================================
+#define msdc_vdd_on(host) \
+ do { \
+ (void)hwPowerOn(MT65XX_POWER_LDO_VMCH, VOL_3300, "SD"); \
+ } while (0)
+#define msdc_vdd_off(host) \
+ do { \
+ (void)hwPowerDown(MT65XX_POWER_LDO_VMCH, "SD"); \
+ } while (0)
+
+#define sdc_is_busy() (sdr_read32(SDC_STS) & SDC_STS_SDCBUSY)
+#define sdc_is_cmd_busy() (sdr_read32(SDC_STS) & SDC_STS_CMDBUSY)
+
+#define sdc_send_cmd(cmd,arg) \
+ do { \
+ sdr_write32(SDC_ARG, (arg)); \
+ sdr_write32(SDC_CMD, (cmd)); \
+ } while(0)
+
+// can modify to read h/w register.
+//#define is_card_present(h) ((sdr_read32(MSDC_PS) & MSDC_PS_CDSTS) ? 0 : 1);
+#define is_card_present(h) (((struct msdc_host*)(h))->card_inserted)
+
+/* +++ by chhung */
+#ifndef __ASSEMBLY__
+#define PHYSADDR(a) (((unsigned long)(a)) & 0x1fffffff)
+#else
+#define PHYSADDR(a) ((a) & 0x1fffffff)
+#endif
+/* end of +++ */
+static unsigned int msdc_do_command(struct msdc_host *host,
+ struct mmc_command *cmd,
+ int tune,
+ unsigned long timeout);
+
+static int msdc_tune_cmdrsp(struct msdc_host*host,struct mmc_command *cmd);
+
+#ifdef MT6575_SD_DEBUG
+static void msdc_dump_card_status(struct msdc_host *host, u32 status)
+{
+/* N_MSG is currently a no-op */
+#if 0
+ static char *state[] = {
+ "Idle", /* 0 */
+ "Ready", /* 1 */
+ "Ident", /* 2 */
+ "Stby", /* 3 */
+ "Tran", /* 4 */
+ "Data", /* 5 */
+ "Rcv", /* 6 */
+ "Prg", /* 7 */
+ "Dis", /* 8 */
+ "Reserved", /* 9 */
+ "Reserved", /* 10 */
+ "Reserved", /* 11 */
+ "Reserved", /* 12 */
+ "Reserved", /* 13 */
+ "Reserved", /* 14 */
+ "I/O mode", /* 15 */
+ };
+#endif
+ if (status & R1_OUT_OF_RANGE)
+ N_MSG(RSP, "[CARD_STATUS] Out of Range");
+ if (status & R1_ADDRESS_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Address Error");
+ if (status & R1_BLOCK_LEN_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Block Len Error");
+ if (status & R1_ERASE_SEQ_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Erase Seq Error");
+ if (status & R1_ERASE_PARAM)
+ N_MSG(RSP, "[CARD_STATUS] Erase Param");
+ if (status & R1_WP_VIOLATION)
+ N_MSG(RSP, "[CARD_STATUS] WP Violation");
+ if (status & R1_CARD_IS_LOCKED)
+ N_MSG(RSP, "[CARD_STATUS] Card is Locked");
+ if (status & R1_LOCK_UNLOCK_FAILED)
+ N_MSG(RSP, "[CARD_STATUS] Lock/Unlock Failed");
+ if (status & R1_COM_CRC_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Command CRC Error");
+ if (status & R1_ILLEGAL_COMMAND)
+ N_MSG(RSP, "[CARD_STATUS] Illegal Command");
+ if (status & R1_CARD_ECC_FAILED)
+ N_MSG(RSP, "[CARD_STATUS] Card ECC Failed");
+ if (status & R1_CC_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] CC Error");
+ if (status & R1_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Error");
+ if (status & R1_UNDERRUN)
+ N_MSG(RSP, "[CARD_STATUS] Underrun");
+ if (status & R1_OVERRUN)
+ N_MSG(RSP, "[CARD_STATUS] Overrun");
+ if (status & R1_CID_CSD_OVERWRITE)
+ N_MSG(RSP, "[CARD_STATUS] CID/CSD Overwrite");
+ if (status & R1_WP_ERASE_SKIP)
+ N_MSG(RSP, "[CARD_STATUS] WP Eraser Skip");
+ if (status & R1_CARD_ECC_DISABLED)
+ N_MSG(RSP, "[CARD_STATUS] Card ECC Disabled");
+ if (status & R1_ERASE_RESET)
+ N_MSG(RSP, "[CARD_STATUS] Erase Reset");
+ if (status & R1_READY_FOR_DATA)
+ N_MSG(RSP, "[CARD_STATUS] Ready for Data");
+ if (status & R1_SWITCH_ERROR)
+ N_MSG(RSP, "[CARD_STATUS] Switch error");
+ if (status & R1_APP_CMD)
+ N_MSG(RSP, "[CARD_STATUS] App Command");
+
+ N_MSG(RSP, "[CARD_STATUS] '%s' State", state[R1_CURRENT_STATE(status)]);
+}
+
+static void msdc_dump_ocr_reg(struct msdc_host *host, u32 resp)
+{
+ if (resp & (1 << 7))
+ N_MSG(RSP, "[OCR] Low Voltage Range");
+ if (resp & (1 << 15))
+ N_MSG(RSP, "[OCR] 2.7-2.8 volt");
+ if (resp & (1 << 16))
+ N_MSG(RSP, "[OCR] 2.8-2.9 volt");
+ if (resp & (1 << 17))
+ N_MSG(RSP, "[OCR] 2.9-3.0 volt");
+ if (resp & (1 << 18))
+ N_MSG(RSP, "[OCR] 3.0-3.1 volt");
+ if (resp & (1 << 19))
+ N_MSG(RSP, "[OCR] 3.1-3.2 volt");
+ if (resp & (1 << 20))
+ N_MSG(RSP, "[OCR] 3.2-3.3 volt");
+ if (resp & (1 << 21))
+ N_MSG(RSP, "[OCR] 3.3-3.4 volt");
+ if (resp & (1 << 22))
+ N_MSG(RSP, "[OCR] 3.4-3.5 volt");
+ if (resp & (1 << 23))
+ N_MSG(RSP, "[OCR] 3.5-3.6 volt");
+ if (resp & (1 << 24))
+ N_MSG(RSP, "[OCR] Switching to 1.8V Accepted (S18A)");
+ if (resp & (1 << 30))
+ N_MSG(RSP, "[OCR] Card Capacity Status (CCS)");
+ if (resp & (1 << 31))
+ N_MSG(RSP, "[OCR] Card Power Up Status (Idle)");
+ else
+ N_MSG(RSP, "[OCR] Card Power Up Status (Busy)");
+}
+
+static void msdc_dump_rca_resp(struct msdc_host *host, u32 resp)
+{
+ u32 status = (((resp >> 15) & 0x1) << 23) |
+ (((resp >> 14) & 0x1) << 22) |
+ (((resp >> 13) & 0x1) << 19) |
+ (resp & 0x1fff);
+
+ N_MSG(RSP, "[RCA] 0x%.4x", resp >> 16);
+ msdc_dump_card_status(host, status);
+}
+
+static void msdc_dump_io_resp(struct msdc_host *host, u32 resp)
+{
+ u32 flags = (resp >> 8) & 0xFF;
+#if 0
+ char *state[] = {"DIS", "CMD", "TRN", "RFU"};
+#endif
+ if (flags & (1 << 7))
+ N_MSG(RSP, "[IO] COM_CRC_ERR");
+ if (flags & (1 << 6))
+ N_MSG(RSP, "[IO] Illgal command");
+ if (flags & (1 << 3))
+ N_MSG(RSP, "[IO] Error");
+ if (flags & (1 << 2))
+ N_MSG(RSP, "[IO] RFU");
+ if (flags & (1 << 1))
+ N_MSG(RSP, "[IO] Function number error");
+ if (flags & (1 << 0))
+ N_MSG(RSP, "[IO] Out of range");
+
+ N_MSG(RSP, "[IO] State: %s, Data:0x%x", state[(resp >> 12) & 0x3], resp & 0xFF);
+}
+#endif
+
+static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks)
+{
+ u32 base = host->base;
+ u32 timeout, clk_ns;
+
+ host->timeout_ns = ns;
+ host->timeout_clks = clks;
+
+ clk_ns = 1000000000UL / host->sclk;
+ timeout = ns / clk_ns + clks;
+ timeout = timeout >> 16; /* in 65536 sclk cycle unit */
+ timeout = timeout > 1 ? timeout - 1 : 0;
+ timeout = timeout > 255 ? 255 : timeout;
+
+ sdr_set_field(SDC_CFG, SDC_CFG_DTOC, timeout);
+
+ N_MSG(OPS, "Set read data timeout: %dns %dclks -> %d x 65536 cycles",
+ ns, clks, timeout + 1);
+}
+
+/* msdc_eirq_sdio() will be called when EIRQ(for WIFI) */
+static void msdc_eirq_sdio(void *data)
+{
+ struct msdc_host *host = (struct msdc_host *)data;
+
+ N_MSG(INT, "SDIO EINT");
+
+ mmc_signal_sdio_irq(host->mmc);
+}
+
+/* msdc_eirq_cd will not be used! We not using EINT for card detection. */
+static void msdc_eirq_cd(void *data)
+{
+ struct msdc_host *host = (struct msdc_host *)data;
+
+ N_MSG(INT, "CD EINT");
+
+#if 0
+ tasklet_hi_schedule(&host->card_tasklet);
+#else
+ schedule_delayed_work(&host->card_delaywork, HZ);
+#endif
+}
+
+#if 0
+static void msdc_tasklet_card(unsigned long arg)
+{
+ struct msdc_host *host = (struct msdc_host *)arg;
+#else
+static void msdc_tasklet_card(struct work_struct *work)
+{
+ struct msdc_host *host = (struct msdc_host *)container_of(work,
+ struct msdc_host, card_delaywork.work);
+#endif
+ struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+ u32 inserted;
+ u32 status = 0;
+ //u32 change = 0;
+
+ spin_lock(&host->lock);
+
+ if (hw->get_cd_status) { // NULL
+ inserted = hw->get_cd_status();
+ } else {
+ status = sdr_read32(MSDC_PS);
+ if (cd_active_low)
+ inserted = (status & MSDC_PS_CDSTS) ? 0 : 1;
+ else
+ inserted = (status & MSDC_PS_CDSTS) ? 1 : 0;
+ }
+
+#if 0
+ change = host->card_inserted ^ inserted;
+ host->card_inserted = inserted;
+
+ if (change && !host->suspend) {
+ if (inserted) {
+ host->mmc->f_max = HOST_MAX_MCLK; // work around
+ }
+ mmc_detect_change(host->mmc, msecs_to_jiffies(20));
+ }
+#else /* Make sure: handle the last interrupt */
+ host->card_inserted = inserted;
+
+ if (!host->suspend) {
+ host->mmc->f_max = HOST_MAX_MCLK;
+ mmc_detect_change(host->mmc, msecs_to_jiffies(20));
+ }
+
+ IRQ_MSG("card found<%s>", inserted ? "inserted" : "removed");
+#endif
+
+ spin_unlock(&host->lock);
+}
+
+#if 0 /* --- by chhung */
+/* For E2 only */
+static u8 clk_src_bit[4] = {
+ 0, 3, 5, 7
+};
+
+static void msdc_select_clksrc(struct msdc_host* host, unsigned char clksrc)
+{
+ u32 val;
+ u32 base = host->base;
+
+ BUG_ON(clksrc > 3);
+ INIT_MSG("set clock source to <%d>", clksrc);
+
+ val = sdr_read32(MSDC_CLKSRC_REG);
+ if (sdr_read32(MSDC_ECO_VER) >= 4) {
+ val &= ~(0x3 << clk_src_bit[host->id]);
+ val |= clksrc << clk_src_bit[host->id];
+ } else {
+ val &= ~0x3; val |= clksrc;
+ }
+ sdr_write32(MSDC_CLKSRC_REG, val);
+
+ host->hclk = hclks[clksrc];
+ host->hw->clk_src = clksrc;
+}
+#endif /* end of --- */
+
+static void msdc_set_mclk(struct msdc_host *host, int ddr, unsigned int hz)
+{
+ //struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+ u32 mode;
+ u32 flags;
+ u32 div;
+ u32 sclk;
+ u32 hclk = host->hclk;
+ //u8 clksrc = hw->clk_src;
+
+ if (!hz) { // set mmc system clock to 0 ?
+ //ERR_MSG("set mclk to 0!!!");
+ msdc_reset();
+ return;
+ }
+
+ msdc_irq_save(flags);
+
+#if defined (CONFIG_MT7621_FPGA) || defined (CONFIG_MT7628_FPGA)
+ mode = 0x0; /* use divisor */
+ if (hz >= (hclk >> 1)) {
+ div = 0; /* mean div = 1/2 */
+ sclk = hclk >> 1; /* sclk = clk / 2 */
+ } else {
+ div = (hclk + ((hz << 2) - 1)) / (hz << 2);
+ sclk = (hclk >> 2) / div;
+ }
+#else
+ if (ddr) {
+ mode = 0x2; /* ddr mode and use divisor */
+ if (hz >= (hclk >> 2)) {
+ div = 1; /* mean div = 1/4 */
+ sclk = hclk >> 2; /* sclk = clk / 4 */
+ } else {
+ div = (hclk + ((hz << 2) - 1)) / (hz << 2);
+ sclk = (hclk >> 2) / div;
+ }
+ } else if (hz >= hclk) { /* bug fix */
+ mode = 0x1; /* no divisor and divisor is ignored */
+ div = 0;
+ sclk = hclk;
+ } else {
+ mode = 0x0; /* use divisor */
+ if (hz >= (hclk >> 1)) {
+ div = 0; /* mean div = 1/2 */
+ sclk = hclk >> 1; /* sclk = clk / 2 */
+ } else {
+ div = (hclk + ((hz << 2) - 1)) / (hz << 2);
+ sclk = (hclk >> 2) / div;
+ }
+ }
+#endif
+ /* set clock mode and divisor */
+ sdr_set_field(MSDC_CFG, MSDC_CFG_CKMOD, mode);
+ sdr_set_field(MSDC_CFG, MSDC_CFG_CKDIV, div);
+
+ /* wait clock stable */
+ while (!(sdr_read32(MSDC_CFG) & MSDC_CFG_CKSTB));
+
+ host->sclk = sclk;
+ host->mclk = hz;
+ msdc_set_timeout(host, host->timeout_ns, host->timeout_clks); // need?
+
+ INIT_MSG("================");
+ INIT_MSG("!!! Set<%dKHz> Source<%dKHz> -> sclk<%dKHz>", hz/1000, hclk/1000, sclk/1000);
+ INIT_MSG("================");
+
+ msdc_irq_restore(flags);
+}
+
+/* Fix me. when need to abort */
+static void msdc_abort_data(struct msdc_host *host)
+{
+ u32 base = host->base;
+ struct mmc_command *stop = host->mrq->stop;
+
+ ERR_MSG("Need to Abort. dma<%d>", host->dma_xfer);
+
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+
+ // need to check FIFO count 0 ?
+
+ if (stop) { /* try to stop, but may not success */
+ ERR_MSG("stop when abort CMD<%d>", stop->opcode);
+ (void)msdc_do_command(host, stop, 0, CMD_TIMEOUT);
+ }
+
+ //if (host->mclk >= 25000000) {
+ // msdc_set_mclk(host, 0, host->mclk >> 1);
+ //}
+}
+
+#if 0 /* --- by chhung */
+static void msdc_pin_config(struct msdc_host *host, int mode)
+{
+ struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+ int pull = (mode == MSDC_PIN_PULL_UP) ? GPIO_PULL_UP : GPIO_PULL_DOWN;
+
+ /* Config WP pin */
+ if (hw->flags & MSDC_WP_PIN_EN) {
+ if (hw->config_gpio_pin) /* NULL */
+ hw->config_gpio_pin(MSDC_WP_PIN, pull);
+ }
+
+ switch (mode) {
+ case MSDC_PIN_PULL_UP:
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPU, 1); /* Check & FIXME */
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPD, 0); /* Check & FIXME */
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPU, 1);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPD, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPU, 1);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPD, 0);
+ break;
+ case MSDC_PIN_PULL_DOWN:
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPU, 0); /* Check & FIXME */
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPD, 1); /* Check & FIXME */
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPU, 0);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPD, 1);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPU, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPD, 1);
+ break;
+ case MSDC_PIN_PULL_NONE:
+ default:
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPU, 0); /* Check & FIXME */
+ //sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKPD, 0); /* Check & FIXME */
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPU, 0);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDPD, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPU, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATPD, 0);
+ break;
+ }
+
+ N_MSG(CFG, "Pins mode(%d), down(%d), up(%d)",
+ mode, MSDC_PIN_PULL_DOWN, MSDC_PIN_PULL_UP);
+}
+
+void msdc_pin_reset(struct msdc_host *host, int mode)
+{
+ struct msdc_hw *hw = (struct msdc_hw *)host->hw;
+ u32 base = host->base;
+ int pull = (mode == MSDC_PIN_PULL_UP) ? GPIO_PULL_UP : GPIO_PULL_DOWN;
+
+ /* Config reset pin */
+ if (hw->flags & MSDC_RST_PIN_EN) {
+ if (hw->config_gpio_pin) /* NULL */
+ hw->config_gpio_pin(MSDC_RST_PIN, pull);
+
+ if (mode == MSDC_PIN_PULL_UP) {
+ sdr_clr_bits(EMMC_IOCON, EMMC_IOCON_BOOTRST);
+ } else {
+ sdr_set_bits(EMMC_IOCON, EMMC_IOCON_BOOTRST);
+ }
+ }
+}
+
+static void msdc_core_power(struct msdc_host *host, int on)
+{
+ N_MSG(CFG, "Turn %s %s power (copower: %d -> %d)",
+ on ? "on" : "off", "core", host->core_power, on);
+
+ if (on && host->core_power == 0) {
+ msdc_vcore_on(host);
+ host->core_power = 1;
+ msleep(1);
+ } else if (!on && host->core_power == 1) {
+ msdc_vcore_off(host);
+ host->core_power = 0;
+ msleep(1);
+ }
+}
+
+static void msdc_host_power(struct msdc_host *host, int on)
+{
+ N_MSG(CFG, "Turn %s %s power ", on ? "on" : "off", "host");
+
+ if (on) {
+ //msdc_core_power(host, 1); // need do card detection.
+ msdc_pin_reset(host, MSDC_PIN_PULL_UP);
+ } else {
+ msdc_pin_reset(host, MSDC_PIN_PULL_DOWN);
+ //msdc_core_power(host, 0);
+ }
+}
+
+static void msdc_card_power(struct msdc_host *host, int on)
+{
+ N_MSG(CFG, "Turn %s %s power ", on ? "on" : "off", "card");
+
+ if (on) {
+ msdc_pin_config(host, MSDC_PIN_PULL_UP);
+ if (host->hw->ext_power_on) {
+ host->hw->ext_power_on();
+ } else {
+ //msdc_vdd_on(host); // need todo card detection.
+ }
+ msleep(1);
+ } else {
+ if (host->hw->ext_power_off) {
+ host->hw->ext_power_off();
+ } else {
+ //msdc_vdd_off(host);
+ }
+ msdc_pin_config(host, MSDC_PIN_PULL_DOWN);
+ msleep(1);
+ }
+}
+
+static void msdc_set_power_mode(struct msdc_host *host, u8 mode)
+{
+ N_MSG(CFG, "Set power mode(%d)", mode);
+
+ if (host->power_mode == MMC_POWER_OFF && mode != MMC_POWER_OFF) {
+ msdc_host_power(host, 1);
+ msdc_card_power(host, 1);
+ } else if (host->power_mode != MMC_POWER_OFF && mode == MMC_POWER_OFF) {
+ msdc_card_power(host, 0);
+ msdc_host_power(host, 0);
+ }
+ host->power_mode = mode;
+}
+#endif /* end of --- */
+
+#ifdef CONFIG_PM
+/*
+ register as callback function of WIFI(combo_sdio_register_pm) .
+ can called by msdc_drv_suspend/resume too.
+*/
+static void msdc_pm(pm_message_t state, void *data)
+{
+ struct msdc_host *host = (struct msdc_host *)data;
+ int evt = state.event;
+
+ if (evt == PM_EVENT_USER_RESUME || evt == PM_EVENT_USER_SUSPEND) {
+ INIT_MSG("USR_%s: suspend<%d> power<%d>",
+ evt == PM_EVENT_USER_RESUME ? "EVENT_USER_RESUME" : "EVENT_USER_SUSPEND",
+ host->suspend, host->power_mode);
+ }
+
+ if (evt == PM_EVENT_SUSPEND || evt == PM_EVENT_USER_SUSPEND) {
+ if (host->suspend) /* already suspend */ /* default 0*/
+ return;
+
+ /* for memory card. already power off by mmc */
+ if (evt == PM_EVENT_SUSPEND && host->power_mode == MMC_POWER_OFF)
+ return;
+
+ host->suspend = 1;
+ host->pm_state = state; /* default PMSG_RESUME */
+
+ INIT_MSG("%s Suspend", evt == PM_EVENT_SUSPEND ? "PM" : "USR");
+ if(host->hw->flags & MSDC_SYS_SUSPEND) /* set for card */
+ (void)mmc_suspend_host(host->mmc);
+ else {
+ // host->mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY; /* just for double confirm */ /* --- by chhung */
+ mmc_remove_host(host->mmc);
+ }
+ } else if (evt == PM_EVENT_RESUME || evt == PM_EVENT_USER_RESUME) {
+ if (!host->suspend){
+ //ERR_MSG("warning: already resume");
+ return;
+ }
+
+ /* No PM resume when USR suspend */
+ if (evt == PM_EVENT_RESUME && host->pm_state.event == PM_EVENT_USER_SUSPEND) {
+ ERR_MSG("PM Resume when in USR Suspend"); /* won't happen. */
+ return;
+ }
+
+ host->suspend = 0;
+ host->pm_state = state;
+
+ INIT_MSG("%s Resume", evt == PM_EVENT_RESUME ? "PM" : "USR");
+ if(host->hw->flags & MSDC_SYS_SUSPEND) { /* will not set for WIFI */
+ (void)mmc_resume_host(host->mmc);
+ }
+ else {
+ // host->mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY; /* --- by chhung */
+ mmc_add_host(host->mmc);
+ }
+ }
+}
+#endif
+
+/*--------------------------------------------------------------------------*/
+/* mmc_host_ops members */
+/*--------------------------------------------------------------------------*/
+static unsigned int msdc_command_start(struct msdc_host *host,
+ struct mmc_command *cmd,
+ int tune, /* not used */
+ unsigned long timeout)
+{
+ u32 base = host->base;
+ u32 opcode = cmd->opcode;
+ u32 rawcmd;
+ u32 wints = MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO |
+ MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO |
+ MSDC_INT_ACMD19_DONE;
+
+ u32 resp;
+ unsigned long tmo;
+
+ /* Protocol layer does not provide response type, but our hardware needs
+ * to know exact type, not just size!
+ */
+ if (opcode == MMC_SEND_OP_COND || opcode == SD_APP_OP_COND)
+ resp = RESP_R3;
+ else if (opcode == MMC_SET_RELATIVE_ADDR || opcode == SD_SEND_RELATIVE_ADDR)
+ resp = (mmc_cmd_type(cmd) == MMC_CMD_BCR) ? RESP_R6 : RESP_R1;
+ else if (opcode == MMC_FAST_IO)
+ resp = RESP_R4;
+ else if (opcode == MMC_GO_IRQ_STATE)
+ resp = RESP_R5;
+ else if (opcode == MMC_SELECT_CARD)
+ resp = (cmd->arg != 0) ? RESP_R1B : RESP_NONE;
+ else if (opcode == SD_IO_RW_DIRECT || opcode == SD_IO_RW_EXTENDED)
+ resp = RESP_R1; /* SDIO workaround. */
+ else if (opcode == SD_SEND_IF_COND && (mmc_cmd_type(cmd) == MMC_CMD_BCR))
+ resp = RESP_R1;
+ else {
+ switch (mmc_resp_type(cmd)) {
+ case MMC_RSP_R1:
+ resp = RESP_R1;
+ break;
+ case MMC_RSP_R1B:
+ resp = RESP_R1B;
+ break;
+ case MMC_RSP_R2:
+ resp = RESP_R2;
+ break;
+ case MMC_RSP_R3:
+ resp = RESP_R3;
+ break;
+ case MMC_RSP_NONE:
+ default:
+ resp = RESP_NONE;
+ break;
+ }
+ }
+
+ cmd->error = 0;
+ /* rawcmd :
+ * vol_swt << 30 | auto_cmd << 28 | blklen << 16 | go_irq << 15 |
+ * stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode
+ */
+ rawcmd = opcode | msdc_rsp[resp] << 7 | host->blksz << 16;
+
+ if (opcode == MMC_READ_MULTIPLE_BLOCK) {
+ rawcmd |= (2 << 11);
+ } else if (opcode == MMC_READ_SINGLE_BLOCK) {
+ rawcmd |= (1 << 11);
+ } else if (opcode == MMC_WRITE_MULTIPLE_BLOCK) {
+ rawcmd |= ((2 << 11) | (1 << 13));
+ } else if (opcode == MMC_WRITE_BLOCK) {
+ rawcmd |= ((1 << 11) | (1 << 13));
+ } else if (opcode == SD_IO_RW_EXTENDED) {
+ if (cmd->data->flags & MMC_DATA_WRITE)
+ rawcmd |= (1 << 13);
+ if (cmd->data->blocks > 1)
+ rawcmd |= (2 << 11);
+ else
+ rawcmd |= (1 << 11);
+ } else if (opcode == SD_IO_RW_DIRECT && cmd->flags == (unsigned int)-1) {
+ rawcmd |= (1 << 14);
+ } else if ((opcode == SD_APP_SEND_SCR) ||
+ (opcode == SD_APP_SEND_NUM_WR_BLKS) ||
+ (opcode == SD_SWITCH && (mmc_cmd_type(cmd) == MMC_CMD_ADTC)) ||
+ (opcode == SD_APP_SD_STATUS && (mmc_cmd_type(cmd) == MMC_CMD_ADTC)) ||
+ (opcode == MMC_SEND_EXT_CSD && (mmc_cmd_type(cmd) == MMC_CMD_ADTC))) {
+ rawcmd |= (1 << 11);
+ } else if (opcode == MMC_STOP_TRANSMISSION) {
+ rawcmd |= (1 << 14);
+ rawcmd &= ~(0x0FFF << 16);
+ }
+
+ N_MSG(CMD, "CMD<%d><0x%.8x> Arg<0x%.8x>", opcode , rawcmd, cmd->arg);
+
+ tmo = jiffies + timeout;
+
+ if (opcode == MMC_SEND_STATUS) {
+ for (;;) {
+ if (!sdc_is_cmd_busy())
+ break;
+
+ if (time_after(jiffies, tmo)) {
+ ERR_MSG("XXX cmd_busy timeout: before CMD<%d>", opcode);
+ cmd->error = (unsigned int)-ETIMEDOUT;
+ msdc_reset();
+ goto end;
+ }
+ }
+ }else {
+ for (;;) {
+ if (!sdc_is_busy())
+ break;
+ if (time_after(jiffies, tmo)) {
+ ERR_MSG("XXX sdc_busy timeout: before CMD<%d>", opcode);
+ cmd->error = (unsigned int)-ETIMEDOUT;
+ msdc_reset();
+ goto end;
+ }
+ }
+ }
+
+ //BUG_ON(in_interrupt());
+ host->cmd = cmd;
+ host->cmd_rsp = resp;
+
+ init_completion(&host->cmd_done);
+
+ sdr_set_bits(MSDC_INTEN, wints);
+ sdc_send_cmd(rawcmd, cmd->arg);
+
+end:
+ return cmd->error;
+}
+
+static unsigned int msdc_command_resp(struct msdc_host *host,
+ struct mmc_command *cmd,
+ int tune,
+ unsigned long timeout)
+{
+ u32 base = host->base;
+ u32 opcode = cmd->opcode;
+ //u32 rawcmd;
+ u32 resp;
+ u32 wints = MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO |
+ MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO |
+ MSDC_INT_ACMD19_DONE;
+
+ resp = host->cmd_rsp;
+
+ BUG_ON(in_interrupt());
+ //init_completion(&host->cmd_done);
+ //sdr_set_bits(MSDC_INTEN, wints);
+
+ spin_unlock(&host->lock);
+ if(!wait_for_completion_timeout(&host->cmd_done, 10*timeout)){
+ ERR_MSG("XXX CMD<%d> wait_for_completion timeout ARG<0x%.8x>", opcode, cmd->arg);
+ cmd->error = (unsigned int)-ETIMEDOUT;
+ msdc_reset();
+ }
+ spin_lock(&host->lock);
+
+ sdr_clr_bits(MSDC_INTEN, wints);
+ host->cmd = NULL;
+
+//end:
+#ifdef MT6575_SD_DEBUG
+ switch (resp) {
+ case RESP_NONE:
+ N_MSG(RSP, "CMD_RSP(%d): %d RSP(%d)", opcode, cmd->error, resp);
+ break;
+ case RESP_R2:
+ N_MSG(RSP, "CMD_RSP(%d): %d RSP(%d)= %.8x %.8x %.8x %.8x",
+ opcode, cmd->error, resp, cmd->resp[0], cmd->resp[1],
+ cmd->resp[2], cmd->resp[3]);
+ break;
+ default: /* Response types 1, 3, 4, 5, 6, 7(1b) */
+ N_MSG(RSP, "CMD_RSP(%d): %d RSP(%d)= 0x%.8x",
+ opcode, cmd->error, resp, cmd->resp[0]);
+ if (cmd->error == 0) {
+ switch (resp) {
+ case RESP_R1:
+ case RESP_R1B:
+ msdc_dump_card_status(host, cmd->resp[0]);
+ break;
+ case RESP_R3:
+ msdc_dump_ocr_reg(host, cmd->resp[0]);
+ break;
+ case RESP_R5:
+ msdc_dump_io_resp(host, cmd->resp[0]);
+ break;
+ case RESP_R6:
+ msdc_dump_rca_resp(host, cmd->resp[0]);
+ break;
+ }
+ }
+ break;
+ }
+#endif
+
+ /* do we need to save card's RCA when SD_SEND_RELATIVE_ADDR */
+
+ if (!tune) {
+ return cmd->error;
+ }
+
+ /* memory card CRC */
+ if(host->hw->flags & MSDC_REMOVABLE && cmd->error == (unsigned int)(-EIO) ) {
+ if (sdr_read32(SDC_CMD) & 0x1800) { /* check if has data phase */
+ msdc_abort_data(host);
+ } else {
+ /* do basic: reset*/
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ }
+ cmd->error = msdc_tune_cmdrsp(host,cmd);
+ }
+
+ // check DAT0
+ /* if (resp == RESP_R1B) {
+ while ((sdr_read32(MSDC_PS) & 0x10000) != 0x10000);
+ } */
+ /* CMD12 Error Handle */
+
+ return cmd->error;
+}
+
+static unsigned int msdc_do_command(struct msdc_host *host,
+ struct mmc_command *cmd,
+ int tune,
+ unsigned long timeout)
+{
+ if (msdc_command_start(host, cmd, tune, timeout))
+ goto end;
+
+ if (msdc_command_resp(host, cmd, tune, timeout))
+ goto end;
+
+end:
+
+ N_MSG(CMD, " return<%d> resp<0x%.8x>", cmd->error, cmd->resp[0]);
+ return cmd->error;
+}
+
+/* The abort condition when PIO read/write
+ tmo:
+*/
+static int msdc_pio_abort(struct msdc_host *host, struct mmc_data *data, unsigned long tmo)
+{
+ int ret = 0;
+ u32 base = host->base;
+
+ if (atomic_read(&host->abort)) {
+ ret = 1;
+ }
+
+ if (time_after(jiffies, tmo)) {
+ data->error = (unsigned int)-ETIMEDOUT;
+ ERR_MSG("XXX PIO Data Timeout: CMD<%d>", host->mrq->cmd->opcode);
+ ret = 1;
+ }
+
+ if(ret) {
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ ERR_MSG("msdc pio find abort");
+ }
+ return ret;
+}
+
+/*
+ Need to add a timeout, or WDT timeout, system reboot.
+*/
+// pio mode data read/write
+static int msdc_pio_read(struct msdc_host *host, struct mmc_data *data)
+{
+ struct scatterlist *sg = data->sg;
+ u32 base = host->base;
+ u32 num = data->sg_len;
+ u32 *ptr;
+ u8 *u8ptr;
+ u32 left = 0;
+ u32 count, size = 0;
+ u32 wints = MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR ;
+ unsigned long tmo = jiffies + DAT_TIMEOUT;
+
+ sdr_set_bits(MSDC_INTEN, wints);
+ while (num) {
+ left = sg_dma_len(sg);
+ ptr = sg_virt(sg);
+ while (left) {
+ if ((left >= MSDC_FIFO_THD) && (msdc_rxfifocnt() >= MSDC_FIFO_THD)) {
+ count = MSDC_FIFO_THD >> 2;
+ do {
+ *ptr++ = msdc_fifo_read32();
+ } while (--count);
+ left -= MSDC_FIFO_THD;
+ } else if ((left < MSDC_FIFO_THD) && msdc_rxfifocnt() >= left) {
+ while (left > 3) {
+ *ptr++ = msdc_fifo_read32();
+ left -= 4;
+ }
+
+ u8ptr = (u8 *)ptr;
+ while(left) {
+ * u8ptr++ = msdc_fifo_read8();
+ left--;
+ }
+ }
+
+ if (msdc_pio_abort(host, data, tmo)) {
+ goto end;
+ }
+ }
+ size += sg_dma_len(sg);
+ sg = sg_next(sg); num--;
+ }
+end:
+ data->bytes_xfered += size;
+ N_MSG(FIO, " PIO Read<%d>bytes", size);
+
+ sdr_clr_bits(MSDC_INTEN, wints);
+ if(data->error) ERR_MSG("read pio data->error<%d> left<%d> size<%d>", data->error, left, size);
+ return data->error;
+}
+
+/* please make sure won't using PIO when size >= 512
+ which means, memory card block read/write won't using pio
+ then don't need to handle the CMD12 when data error.
+*/
+static int msdc_pio_write(struct msdc_host* host, struct mmc_data *data)
+{
+ u32 base = host->base;
+ struct scatterlist *sg = data->sg;
+ u32 num = data->sg_len;
+ u32 *ptr;
+ u8 *u8ptr;
+ u32 left;
+ u32 count, size = 0;
+ u32 wints = MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR ;
+ unsigned long tmo = jiffies + DAT_TIMEOUT;
+
+ sdr_set_bits(MSDC_INTEN, wints);
+ while (num) {
+ left = sg_dma_len(sg);
+ ptr = sg_virt(sg);
+
+ while (left) {
+ if (left >= MSDC_FIFO_SZ && msdc_txfifocnt() == 0) {
+ count = MSDC_FIFO_SZ >> 2;
+ do {
+ msdc_fifo_write32(*ptr); ptr++;
+ } while (--count);
+ left -= MSDC_FIFO_SZ;
+ } else if (left < MSDC_FIFO_SZ && msdc_txfifocnt() == 0) {
+ while (left > 3) {
+ msdc_fifo_write32(*ptr); ptr++;
+ left -= 4;
+ }
+
+ u8ptr = (u8*)ptr;
+ while(left){
+ msdc_fifo_write8(*u8ptr); u8ptr++;
+ left--;
+ }
+ }
+
+ if (msdc_pio_abort(host, data, tmo)) {
+ goto end;
+ }
+ }
+ size += sg_dma_len(sg);
+ sg = sg_next(sg); num--;
+ }
+end:
+ data->bytes_xfered += size;
+ N_MSG(FIO, " PIO Write<%d>bytes", size);
+ if(data->error) ERR_MSG("write pio data->error<%d>", data->error);
+
+ sdr_clr_bits(MSDC_INTEN, wints);
+ return data->error;
+}
+
+#if 0 /* --- by chhung */
+// DMA resume / start / stop
+static void msdc_dma_resume(struct msdc_host *host)
+{
+ u32 base = host->base;
+
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_RESUME, 1);
+
+ N_MSG(DMA, "DMA resume");
+}
+#endif /* end of --- */
+
+static void msdc_dma_start(struct msdc_host *host)
+{
+ u32 base = host->base;
+ u32 wints = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR ;
+
+ sdr_set_bits(MSDC_INTEN, wints);
+ //dsb(); /* --- by chhung */
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1);
+
+ N_MSG(DMA, "DMA start");
+}
+
+static void msdc_dma_stop(struct msdc_host *host)
+{
+ u32 base = host->base;
+ //u32 retries=500;
+ u32 wints = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR ;
+
+ N_MSG(DMA, "DMA status: 0x%.8x",sdr_read32(MSDC_DMA_CFG));
+ //while (sdr_read32(MSDC_DMA_CFG) & MSDC_DMA_CFG_STS);
+
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_STOP, 1);
+ while (sdr_read32(MSDC_DMA_CFG) & MSDC_DMA_CFG_STS);
+
+ //dsb(); /* --- by chhung */
+ sdr_clr_bits(MSDC_INTEN, wints); /* Not just xfer_comp */
+
+ N_MSG(DMA, "DMA stop");
+}
+
+#if 0 /* --- by chhung */
+/* dump a gpd list */
+static void msdc_dma_dump(struct msdc_host *host, struct msdc_dma *dma)
+{
+ gpd_t *gpd = dma->gpd;
+ bd_t *bd = dma->bd;
+ bd_t *ptr;
+ int i = 0;
+ int p_to_v;
+
+ if (dma->mode != MSDC_MODE_DMA_DESC) {
+ return;
+ }
+
+ ERR_MSG("try to dump gpd and bd");
+
+ /* dump gpd */
+ ERR_MSG(".gpd<0x%.8x> gpd_phy<0x%.8x>", (int)gpd, (int)dma->gpd_addr);
+ ERR_MSG("...hwo <%d>", gpd->hwo );
+ ERR_MSG("...bdp <%d>", gpd->bdp );
+ ERR_MSG("...chksum<0x%.8x>", gpd->chksum );
+ //ERR_MSG("...intr <0x%.8x>", gpd->intr );
+ ERR_MSG("...next <0x%.8x>", (int)gpd->next );
+ ERR_MSG("...ptr <0x%.8x>", (int)gpd->ptr );
+ ERR_MSG("...buflen<0x%.8x>", gpd->buflen );
+ //ERR_MSG("...extlen<0x%.8x>", gpd->extlen );
+ //ERR_MSG("...arg <0x%.8x>", gpd->arg );
+ //ERR_MSG("...blknum<0x%.8x>", gpd->blknum );
+ //ERR_MSG("...cmd <0x%.8x>", gpd->cmd );
+
+ /* dump bd */
+ ERR_MSG(".bd<0x%.8x> bd_phy<0x%.8x> gpd_ptr<0x%.8x>", (int)bd, (int)dma->bd_addr, (int)gpd->ptr);
+ ptr = bd;
+ p_to_v = ((u32)bd - (u32)dma->bd_addr);
+ while (1) {
+ ERR_MSG(".bd[%d]", i); i++;
+ ERR_MSG("...eol <%d>", ptr->eol );
+ ERR_MSG("...chksum<0x%.8x>", ptr->chksum );
+ //ERR_MSG("...blkpad<0x%.8x>", ptr->blkpad );
+ //ERR_MSG("...dwpad <0x%.8x>", ptr->dwpad );
+ ERR_MSG("...next <0x%.8x>", (int)ptr->next );
+ ERR_MSG("...ptr <0x%.8x>", (int)ptr->ptr );
+ ERR_MSG("...buflen<0x%.8x>", (int)ptr->buflen );
+
+ if (ptr->eol == 1) {
+ break;
+ }
+
+ /* find the next bd, virtual address of ptr->next */
+ /* don't need to enable when use malloc */
+ //BUG_ON( (ptr->next + p_to_v)!=(ptr+1) );
+ //ERR_MSG(".next bd<0x%.8x><0x%.8x>", (ptr->next + p_to_v), (ptr+1));
+ ptr++;
+ }
+
+ ERR_MSG("dump gpd and bd finished");
+}
+#endif /* end of --- */
+
+/* calc checksum */
+static u8 msdc_dma_calcs(u8 *buf, u32 len)
+{
+ u32 i, sum = 0;
+ for (i = 0; i < len; i++) {
+ sum += buf[i];
+ }
+ return 0xFF - (u8)sum;
+}
+
+/* gpd bd setup + dma registers */
+static int msdc_dma_config(struct msdc_host *host, struct msdc_dma *dma)
+{
+ u32 base = host->base;
+ u32 sglen = dma->sglen;
+ //u32 i, j, num, bdlen, arg, xfersz;
+ u32 j, num, bdlen;
+ u8 blkpad, dwpad, chksum;
+ struct scatterlist *sg = dma->sg;
+ gpd_t *gpd;
+ bd_t *bd;
+
+ switch (dma->mode) {
+ case MSDC_MODE_DMA_BASIC:
+ BUG_ON(dma->xfersz > 65535);
+ BUG_ON(dma->sglen != 1);
+ sdr_write32(MSDC_DMA_SA, PHYSADDR(sg_dma_address(sg)));
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_LASTBUF, 1);
+//#if defined (CONFIG_RALINK_MT7620)
+ if (ralink_soc == MT762X_SOC_MT7620A)
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_XFERSZ, sg_dma_len(sg));
+//#elif defined (CONFIG_RALINK_MT7621) || defined (CONFIG_RALINK_MT7628)
+ else
+ sdr_write32((volatile u32*)(RALINK_MSDC_BASE+0xa8), sg_dma_len(sg));
+//#endif
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_BRUSTSZ, dma->burstsz);
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_MODE, 0);
+ break;
+ case MSDC_MODE_DMA_DESC:
+ blkpad = (dma->flags & DMA_FLAG_PAD_BLOCK) ? 1 : 0;
+ dwpad = (dma->flags & DMA_FLAG_PAD_DWORD) ? 1 : 0;
+ chksum = (dma->flags & DMA_FLAG_EN_CHKSUM) ? 1 : 0;
+
+ /* calculate the required number of gpd */
+ num = (sglen + MAX_BD_PER_GPD - 1) / MAX_BD_PER_GPD;
+ BUG_ON(num !=1 );
+
+ gpd = dma->gpd;
+ bd = dma->bd;
+ bdlen = sglen;
+
+ /* modify gpd*/
+ //gpd->intr = 0;
+ gpd->hwo = 1; /* hw will clear it */
+ gpd->bdp = 1;
+ gpd->chksum = 0; /* need to clear first. */
+ gpd->chksum = (chksum ? msdc_dma_calcs((u8 *)gpd, 16) : 0);
+
+ /* modify bd*/
+ for (j = 0; j < bdlen; j++) {
+ msdc_init_bd(&bd[j], blkpad, dwpad, sg_dma_address(sg), sg_dma_len(sg));
+ if(j == bdlen - 1) {
+ bd[j].eol = 1; /* the last bd */
+ } else {
+ bd[j].eol = 0;
+ }
+ bd[j].chksum = 0; /* checksume need to clear first */
+ bd[j].chksum = (chksum ? msdc_dma_calcs((u8 *)(&bd[j]), 16) : 0);
+ sg++;
+ }
+
+ dma->used_gpd += 2;
+ dma->used_bd += bdlen;
+
+ sdr_set_field(MSDC_DMA_CFG, MSDC_DMA_CFG_DECSEN, chksum);
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_BRUSTSZ, dma->burstsz);
+ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_MODE, 1);
+
+ sdr_write32(MSDC_DMA_SA, PHYSADDR((u32)dma->gpd_addr));
+ break;
+
+ default:
+ break;
+ }
+
+ N_MSG(DMA, "DMA_CTRL = 0x%x", sdr_read32(MSDC_DMA_CTRL));
+ N_MSG(DMA, "DMA_CFG = 0x%x", sdr_read32(MSDC_DMA_CFG));
+ N_MSG(DMA, "DMA_SA = 0x%x", sdr_read32(MSDC_DMA_SA));
+
+ return 0;
+}
+
+static void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma,
+ struct scatterlist *sg, unsigned int sglen)
+{
+ BUG_ON(sglen > MAX_BD_NUM); /* not support currently */
+
+ dma->sg = sg;
+ dma->flags = DMA_FLAG_EN_CHKSUM;
+ //dma->flags = DMA_FLAG_NONE; /* CHECKME */
+ dma->sglen = sglen;
+ dma->xfersz = host->xfer_size;
+ dma->burstsz = MSDC_BRUST_64B;
+
+ if (sglen == 1 && sg_dma_len(sg) <= MAX_DMA_CNT)
+ dma->mode = MSDC_MODE_DMA_BASIC;
+ else
+ dma->mode = MSDC_MODE_DMA_DESC;
+
+ N_MSG(DMA, "DMA mode<%d> sglen<%d> xfersz<%d>", dma->mode, dma->sglen, dma->xfersz);
+
+ msdc_dma_config(host, dma);
+
+ /*if (dma->mode == MSDC_MODE_DMA_DESC) {
+ //msdc_dma_dump(host, dma);
+ } */
+}
+
+/* set block number before send command */
+static void msdc_set_blknum(struct msdc_host *host, u32 blknum)
+{
+ u32 base = host->base;
+
+ sdr_write32(SDC_BLK_NUM, blknum);
+}
+
+static int msdc_do_request(struct mmc_host*mmc, struct mmc_request*mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ u32 base = host->base;
+ //u32 intsts = 0;
+ unsigned int left=0;
+ int dma = 0, read = 1, dir = DMA_FROM_DEVICE, send_type=0;
+
+ #define SND_DAT 0
+ #define SND_CMD 1
+
+ BUG_ON(mmc == NULL);
+ BUG_ON(mrq == NULL);
+
+ host->error = 0;
+ atomic_set(&host->abort, 0);
+
+ cmd = mrq->cmd;
+ data = mrq->cmd->data;
+
+#if 0 /* --- by chhung */
+ //if(host->id ==1){
+ N_MSG(OPS, "enable clock!");
+ msdc_ungate_clock(host->id);
+ //}
+#endif /* end of --- */
+
+ if (!data) {
+ send_type=SND_CMD;
+ if (msdc_do_command(host, cmd, 1, CMD_TIMEOUT) != 0) {
+ goto done;
+ }
+ } else {
+ BUG_ON(data->blksz > HOST_MAX_BLKSZ);
+ send_type=SND_DAT;
+
+ data->error = 0;
+ read = data->flags & MMC_DATA_READ ? 1 : 0;
+ host->data = data;
+ host->xfer_size = data->blocks * data->blksz;
+ host->blksz = data->blksz;
+
+ /* deside the transfer mode */
+ if (drv_mode[host->id] == MODE_PIO) {
+ host->dma_xfer = dma = 0;
+ } else if (drv_mode[host->id] == MODE_DMA) {
+ host->dma_xfer = dma = 1;
+ } else if (drv_mode[host->id] == MODE_SIZE_DEP) {
+ host->dma_xfer = dma = ((host->xfer_size >= dma_size[host->id]) ? 1 : 0);
+ }
+
+ if (read) {
+ if ((host->timeout_ns != data->timeout_ns) ||
+ (host->timeout_clks != data->timeout_clks)) {
+ msdc_set_timeout(host, data->timeout_ns, data->timeout_clks);
+ }
+ }
+
+ msdc_set_blknum(host, data->blocks);
+ //msdc_clr_fifo(); /* no need */
+
+ if (dma) {
+ msdc_dma_on(); /* enable DMA mode first!! */
+ init_completion(&host->xfer_done);
+
+ /* start the command first*/
+ if (msdc_command_start(host, cmd, 1, CMD_TIMEOUT) != 0)
+ goto done;
+
+ dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+ (void)dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len, dir);
+ msdc_dma_setup(host, &host->dma, data->sg, data->sg_len);
+
+ /* then wait command done */
+ if (msdc_command_resp(host, cmd, 1, CMD_TIMEOUT) != 0)
+ goto done;
+
+ /* for read, the data coming too fast, then CRC error
+ start DMA no business with CRC. */
+ //init_completion(&host->xfer_done);
+ msdc_dma_start(host);
+
+ spin_unlock(&host->lock);
+ if(!wait_for_completion_timeout(&host->xfer_done, DAT_TIMEOUT)){
+ ERR_MSG("XXX CMD<%d> wait xfer_done<%d> timeout!!", cmd->opcode, data->blocks * data->blksz);
+ ERR_MSG(" DMA_SA = 0x%x", sdr_read32(MSDC_DMA_SA));
+ ERR_MSG(" DMA_CA = 0x%x", sdr_read32(MSDC_DMA_CA));
+ ERR_MSG(" DMA_CTRL = 0x%x", sdr_read32(MSDC_DMA_CTRL));
+ ERR_MSG(" DMA_CFG = 0x%x", sdr_read32(MSDC_DMA_CFG));
+ data->error = (unsigned int)-ETIMEDOUT;
+
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ }
+ spin_lock(&host->lock);
+ msdc_dma_stop(host);
+ } else {
+ /* Firstly: send command */
+ if (msdc_do_command(host, cmd, 1, CMD_TIMEOUT) != 0) {
+ goto done;
+ }
+
+ /* Secondly: pio data phase */
+ if (read) {
+ if (msdc_pio_read(host, data)){
+ goto done;
+ }
+ } else {
+ if (msdc_pio_write(host, data)) {
+ goto done;
+ }
+ }
+
+ /* For write case: make sure contents in fifo flushed to device */
+ if (!read) {
+ while (1) {
+ left=msdc_txfifocnt();
+ if (left == 0) {
+ break;
+ }
+ if (msdc_pio_abort(host, data, jiffies + DAT_TIMEOUT)) {
+ break;
+ /* Fix me: what about if data error, when stop ? how to? */
+ }
+ }
+ } else {
+ /* Fix me: read case: need to check CRC error */
+ }
+
+ /* For write case: SDCBUSY and Xfer_Comp will assert when DAT0 not busy.
+ For read case : SDCBUSY and Xfer_Comp will assert when last byte read out from FIFO.
+ */
+
+ /* try not to wait xfer_comp interrupt.
+ the next command will check SDC_BUSY.
+ SDC_BUSY means xfer_comp assert
+ */
+
+ } // PIO mode
+
+ /* Last: stop transfer */
+ if (data->stop){
+ if (msdc_do_command(host, data->stop, 0, CMD_TIMEOUT) != 0) {
+ goto done;
+ }
+ }
+ }
+
+done:
+ if (data != NULL) {
+ host->data = NULL;
+ host->dma_xfer = 0;
+ if (dma != 0) {
+ msdc_dma_off();
+ host->dma.used_bd = 0;
+ host->dma.used_gpd = 0;
+ dma_unmap_sg(mmc_dev(mmc), data->sg, data->sg_len, dir);
+ }
+ host->blksz = 0;
+
+#if 0 // don't stop twice!
+ if(host->hw->flags & MSDC_REMOVABLE && data->error) {
+ msdc_abort_data(host);
+ /* reset in IRQ, stop command has issued. -> No need */
+ }
+#endif
+
+ N_MSG(OPS, "CMD<%d> data<%s %s> blksz<%d> block<%d> error<%d>",cmd->opcode, (dma? "dma":"pio"),
+ (read ? "read ":"write") ,data->blksz, data->blocks, data->error);
+ }
+
+#if 0 /* --- by chhung */
+#if 1
+ //if(host->id==1) {
+ if(send_type==SND_CMD) {
+ if(cmd->opcode == MMC_SEND_STATUS) {
+ if((cmd->resp[0] & CARD_READY_FOR_DATA) ||(CARD_CURRENT_STATE(cmd->resp[0]) != 7)){
+ N_MSG(OPS,"disable clock, CMD13 IDLE");
+ msdc_gate_clock(host->id);
+ }
+ } else {
+ N_MSG(OPS,"disable clock, CMD<%d>", cmd->opcode);
+ msdc_gate_clock(host->id);
+ }
+ } else {
+ if(read) {
+ N_MSG(OPS,"disable clock!!! Read CMD<%d>",cmd->opcode);
+ msdc_gate_clock(host->id);
+ }
+ }
+ //}
+#else
+ msdc_gate_clock(host->id);
+#endif
+#endif /* end of --- */
+
+ if (mrq->cmd->error) host->error = 0x001;
+ if (mrq->data && mrq->data->error) host->error |= 0x010;
+ if (mrq->stop && mrq->stop->error) host->error |= 0x100;
+
+ //if (host->error) ERR_MSG("host->error<%d>", host->error);
+
+ return host->error;
+}
+
+static int msdc_app_cmd(struct mmc_host *mmc, struct msdc_host *host)
+{
+ struct mmc_command cmd;
+ struct mmc_request mrq;
+ u32 err;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_APP_CMD;
+#if 0 /* bug: we meet mmc->card is null when ACMD6 */
+ cmd.arg = mmc->card->rca << 16;
+#else
+ cmd.arg = host->app_cmd_arg;
+#endif
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ mrq.cmd = &cmd; cmd.mrq = &mrq;
+ cmd.data = NULL;
+
+ err = msdc_do_command(host, &cmd, 0, CMD_TIMEOUT);
+ return err;
+}
+
+static int msdc_tune_cmdrsp(struct msdc_host*host, struct mmc_command *cmd)
+{
+ int result = -1;
+ u32 base = host->base;
+ u32 rsmpl, cur_rsmpl, orig_rsmpl;
+ u32 rrdly, cur_rrdly = 0xffffffff, orig_rrdly;
+ u32 skip = 1;
+
+ /* ==== don't support 3.0 now ====
+ 1: R_SMPL[1]
+ 2: PAD_CMD_RESP_RXDLY[26:22]
+ ==========================*/
+
+ // save the previous tune result
+ sdr_get_field(MSDC_IOCON, MSDC_IOCON_RSPL, orig_rsmpl);
+ sdr_get_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY, orig_rrdly);
+
+ rrdly = 0;
+ do {
+ for (rsmpl = 0; rsmpl < 2; rsmpl++) {
+ /* Lv1: R_SMPL[1] */
+ cur_rsmpl = (orig_rsmpl + rsmpl) % 2;
+ if (skip == 1) {
+ skip = 0;
+ continue;
+ }
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_RSPL, cur_rsmpl);
+
+ if (host->app_cmd) {
+ result = msdc_app_cmd(host->mmc, host);
+ if (result) {
+ ERR_MSG("TUNE_CMD app_cmd<%d> failed: RESP_RXDLY<%d>,R_SMPL<%d>",
+ host->mrq->cmd->opcode, cur_rrdly, cur_rsmpl);
+ continue;
+ }
+ }
+ result = msdc_do_command(host, cmd, 0, CMD_TIMEOUT); // not tune.
+ ERR_MSG("TUNE_CMD<%d> %s PAD_CMD_RESP_RXDLY[26:22]<%d> R_SMPL[1]<%d>", cmd->opcode,
+ (result == 0) ? "PASS" : "FAIL", cur_rrdly, cur_rsmpl);
+
+ if (result == 0) {
+ return 0;
+ }
+ if (result != (unsigned int)(-EIO)) {
+ ERR_MSG("TUNE_CMD<%d> Error<%d> not -EIO", cmd->opcode, result);
+ return result;
+ }
+
+ /* should be EIO */
+ if (sdr_read32(SDC_CMD) & 0x1800) { /* check if has data phase */
+ msdc_abort_data(host);
+ }
+ }
+
+ /* Lv2: PAD_CMD_RESP_RXDLY[26:22] */
+ cur_rrdly = (orig_rrdly + rrdly + 1) % 32;
+ sdr_set_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY, cur_rrdly);
+ }while (++rrdly < 32);
+
+ return result;
+}
+
+/* Support SD2.0 Only */
+static int msdc_tune_bread(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 base = host->base;
+ u32 ddr=0;
+ u32 dcrc=0;
+ u32 rxdly, cur_rxdly0, cur_rxdly1;
+ u32 dsmpl, cur_dsmpl, orig_dsmpl;
+ u32 cur_dat0, cur_dat1, cur_dat2, cur_dat3;
+ u32 cur_dat4, cur_dat5, cur_dat6, cur_dat7;
+ u32 orig_dat0, orig_dat1, orig_dat2, orig_dat3;
+ u32 orig_dat4, orig_dat5, orig_dat6, orig_dat7;
+ int result = -1;
+ u32 skip = 1;
+
+ sdr_get_field(MSDC_IOCON, MSDC_IOCON_DSPL, orig_dsmpl);
+
+ /* Tune Method 2. */
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
+
+ rxdly = 0;
+ do {
+ for (dsmpl = 0; dsmpl < 2; dsmpl++) {
+ cur_dsmpl = (orig_dsmpl + dsmpl) % 2;
+ if (skip == 1) {
+ skip = 0;
+ continue;
+ }
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, cur_dsmpl);
+
+ if (host->app_cmd) {
+ result = msdc_app_cmd(host->mmc, host);
+ if (result) {
+ ERR_MSG("TUNE_BREAD app_cmd<%d> failed", host->mrq->cmd->opcode);
+ continue;
+ }
+ }
+ result = msdc_do_request(mmc,mrq);
+
+ sdr_get_field(SDC_DCRC_STS, SDC_DCRC_STS_POS|SDC_DCRC_STS_NEG, dcrc); /* RO */
+ if (!ddr) dcrc &= ~SDC_DCRC_STS_NEG;
+ ERR_MSG("TUNE_BREAD<%s> dcrc<0x%x> DATRDDLY0/1<0x%x><0x%x> dsmpl<0x%x>",
+ (result == 0 && dcrc == 0) ? "PASS" : "FAIL", dcrc,
+ sdr_read32(MSDC_DAT_RDDLY0), sdr_read32(MSDC_DAT_RDDLY1), cur_dsmpl);
+
+ /* Fix me: result is 0, but dcrc is still exist */
+ if (result == 0 && dcrc == 0) {
+ goto done;
+ } else {
+ /* there is a case: command timeout, and data phase not processed */
+ if (mrq->data->error != 0 && mrq->data->error != (unsigned int)(-EIO)) {
+ ERR_MSG("TUNE_READ: result<0x%x> cmd_error<%d> data_error<%d>",
+ result, mrq->cmd->error, mrq->data->error);
+ goto done;
+ }
+ }
+ }
+
+ cur_rxdly0 = sdr_read32(MSDC_DAT_RDDLY0);
+ cur_rxdly1 = sdr_read32(MSDC_DAT_RDDLY1);
+
+ /* E1 ECO. YD: Reverse */
+ if (sdr_read32(MSDC_ECO_VER) >= 4) {
+ orig_dat0 = (cur_rxdly0 >> 24) & 0x1F;
+ orig_dat1 = (cur_rxdly0 >> 16) & 0x1F;
+ orig_dat2 = (cur_rxdly0 >> 8) & 0x1F;
+ orig_dat3 = (cur_rxdly0 >> 0) & 0x1F;
+ orig_dat4 = (cur_rxdly1 >> 24) & 0x1F;
+ orig_dat5 = (cur_rxdly1 >> 16) & 0x1F;
+ orig_dat6 = (cur_rxdly1 >> 8) & 0x1F;
+ orig_dat7 = (cur_rxdly1 >> 0) & 0x1F;
+ } else {
+ orig_dat0 = (cur_rxdly0 >> 0) & 0x1F;
+ orig_dat1 = (cur_rxdly0 >> 8) & 0x1F;
+ orig_dat2 = (cur_rxdly0 >> 16) & 0x1F;
+ orig_dat3 = (cur_rxdly0 >> 24) & 0x1F;
+ orig_dat4 = (cur_rxdly1 >> 0) & 0x1F;
+ orig_dat5 = (cur_rxdly1 >> 8) & 0x1F;
+ orig_dat6 = (cur_rxdly1 >> 16) & 0x1F;
+ orig_dat7 = (cur_rxdly1 >> 24) & 0x1F;
+ }
+
+ if (ddr) {
+ cur_dat0 = (dcrc & (1 << 0) || dcrc & (1 << 8)) ? ((orig_dat0 + 1) % 32) : orig_dat0;
+ cur_dat1 = (dcrc & (1 << 1) || dcrc & (1 << 9)) ? ((orig_dat1 + 1) % 32) : orig_dat1;
+ cur_dat2 = (dcrc & (1 << 2) || dcrc & (1 << 10)) ? ((orig_dat2 + 1) % 32) : orig_dat2;
+ cur_dat3 = (dcrc & (1 << 3) || dcrc & (1 << 11)) ? ((orig_dat3 + 1) % 32) : orig_dat3;
+ } else {
+ cur_dat0 = (dcrc & (1 << 0)) ? ((orig_dat0 + 1) % 32) : orig_dat0;
+ cur_dat1 = (dcrc & (1 << 1)) ? ((orig_dat1 + 1) % 32) : orig_dat1;
+ cur_dat2 = (dcrc & (1 << 2)) ? ((orig_dat2 + 1) % 32) : orig_dat2;
+ cur_dat3 = (dcrc & (1 << 3)) ? ((orig_dat3 + 1) % 32) : orig_dat3;
+ }
+ cur_dat4 = (dcrc & (1 << 4)) ? ((orig_dat4 + 1) % 32) : orig_dat4;
+ cur_dat5 = (dcrc & (1 << 5)) ? ((orig_dat5 + 1) % 32) : orig_dat5;
+ cur_dat6 = (dcrc & (1 << 6)) ? ((orig_dat6 + 1) % 32) : orig_dat6;
+ cur_dat7 = (dcrc & (1 << 7)) ? ((orig_dat7 + 1) % 32) : orig_dat7;
+
+ cur_rxdly0 = (cur_dat0 << 24) | (cur_dat1 << 16) | (cur_dat2 << 8) | (cur_dat3 << 0);
+ cur_rxdly1 = (cur_dat4 << 24) | (cur_dat5 << 16) | (cur_dat6 << 8) | (cur_dat7 << 0);
+
+ sdr_write32(MSDC_DAT_RDDLY0, cur_rxdly0);
+ sdr_write32(MSDC_DAT_RDDLY1, cur_rxdly1);
+
+ } while (++rxdly < 32);
+
+done:
+ return result;
+}
+
+static int msdc_tune_bwrite(struct mmc_host *mmc,struct mmc_request *mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 base = host->base;
+
+ u32 wrrdly, cur_wrrdly = 0xffffffff, orig_wrrdly;
+ u32 dsmpl, cur_dsmpl, orig_dsmpl;
+ u32 rxdly, cur_rxdly0;
+ u32 orig_dat0, orig_dat1, orig_dat2, orig_dat3;
+ u32 cur_dat0, cur_dat1, cur_dat2, cur_dat3;
+ int result = -1;
+ u32 skip = 1;
+
+ // MSDC_IOCON_DDR50CKD need to check. [Fix me]
+
+ sdr_get_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATWRDLY, orig_wrrdly);
+ sdr_get_field(MSDC_IOCON, MSDC_IOCON_DSPL, orig_dsmpl );
+
+ /* Tune Method 2. just DAT0 */
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DDLSEL, 1);
+ cur_rxdly0 = sdr_read32(MSDC_DAT_RDDLY0);
+
+ /* E1 ECO. YD: Reverse */
+ if (sdr_read32(MSDC_ECO_VER) >= 4) {
+ orig_dat0 = (cur_rxdly0 >> 24) & 0x1F;
+ orig_dat1 = (cur_rxdly0 >> 16) & 0x1F;
+ orig_dat2 = (cur_rxdly0 >> 8) & 0x1F;
+ orig_dat3 = (cur_rxdly0 >> 0) & 0x1F;
+ } else {
+ orig_dat0 = (cur_rxdly0 >> 0) & 0x1F;
+ orig_dat1 = (cur_rxdly0 >> 8) & 0x1F;
+ orig_dat2 = (cur_rxdly0 >> 16) & 0x1F;
+ orig_dat3 = (cur_rxdly0 >> 24) & 0x1F;
+ }
+
+ rxdly = 0;
+ do {
+ wrrdly = 0;
+ do {
+ for (dsmpl = 0; dsmpl < 2; dsmpl++) {
+ cur_dsmpl = (orig_dsmpl + dsmpl) % 2;
+ if (skip == 1) {
+ skip = 0;
+ continue;
+ }
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, cur_dsmpl);
+
+ if (host->app_cmd) {
+ result = msdc_app_cmd(host->mmc, host);
+ if (result) {
+ ERR_MSG("TUNE_BWRITE app_cmd<%d> failed", host->mrq->cmd->opcode);
+ continue;
+ }
+ }
+ result = msdc_do_request(mmc,mrq);
+
+ ERR_MSG("TUNE_BWRITE<%s> DSPL<%d> DATWRDLY<%d> MSDC_DAT_RDDLY0<0x%x>",
+ result == 0 ? "PASS" : "FAIL",
+ cur_dsmpl, cur_wrrdly, cur_rxdly0);
+
+ if (result == 0) {
+ goto done;
+ }
+ else {
+ /* there is a case: command timeout, and data phase not processed */
+ if (mrq->data->error != (unsigned int)(-EIO)) {
+ ERR_MSG("TUNE_READ: result<0x%x> cmd_error<%d> data_error<%d>",
+ result, mrq->cmd->error, mrq->data->error);
+ goto done;
+ }
+ }
+ }
+ cur_wrrdly = (orig_wrrdly + wrrdly + 1) % 32;
+ sdr_set_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATWRDLY, cur_wrrdly);
+ } while (++wrrdly < 32);
+
+ cur_dat0 = (orig_dat0 + rxdly) % 32; /* only adjust bit-1 for crc */
+ cur_dat1 = orig_dat1;
+ cur_dat2 = orig_dat2;
+ cur_dat3 = orig_dat3;
+
+ cur_rxdly0 = (cur_dat0 << 24) | (cur_dat1 << 16) | (cur_dat2 << 8) | (cur_dat3 << 0);
+ sdr_write32(MSDC_DAT_RDDLY0, cur_rxdly0);
+ } while (++rxdly < 32);
+
+done:
+ return result;
+}
+
+static int msdc_get_card_status(struct mmc_host *mmc, struct msdc_host *host, u32 *status)
+{
+ struct mmc_command cmd;
+ struct mmc_request mrq;
+ u32 err;
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+ cmd.opcode = MMC_SEND_STATUS;
+ if (mmc->card) {
+ cmd.arg = mmc->card->rca << 16;
+ } else {
+ ERR_MSG("cmd13 mmc card is null");
+ cmd.arg = host->app_cmd_arg;
+ }
+ cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
+
+ memset(&mrq, 0, sizeof(struct mmc_request));
+ mrq.cmd = &cmd; cmd.mrq = &mrq;
+ cmd.data = NULL;
+
+ err = msdc_do_command(host, &cmd, 1, CMD_TIMEOUT);
+
+ if (status) {
+ *status = cmd.resp[0];
+ }
+
+ return err;
+}
+
+static int msdc_check_busy(struct mmc_host *mmc, struct msdc_host *host)
+{
+ u32 err = 0;
+ u32 status = 0;
+
+ do {
+ err = msdc_get_card_status(mmc, host, &status);
+ if (err) return err;
+ /* need cmd12? */
+ ERR_MSG("cmd<13> resp<0x%x>", status);
+ } while (R1_CURRENT_STATE(status) == 7);
+
+ return err;
+}
+
+/* failed when msdc_do_request */
+static int msdc_tune_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ //u32 base = host->base;
+ int ret=0, read;
+
+ cmd = mrq->cmd;
+ data = mrq->cmd->data;
+
+ read = data->flags & MMC_DATA_READ ? 1 : 0;
+
+ if (read) {
+ if (data->error == (unsigned int)(-EIO)) {
+ ret = msdc_tune_bread(mmc,mrq);
+ }
+ } else {
+ ret = msdc_check_busy(mmc, host);
+ if (ret){
+ ERR_MSG("XXX cmd13 wait program done failed");
+ return ret;
+ }
+ /* CRC and TO */
+ /* Fix me: don't care card status? */
+ ret = msdc_tune_bwrite(mmc,mrq);
+ }
+
+ return ret;
+}
+
+/* ops.request */
+static void msdc_ops_request(struct mmc_host *mmc,struct mmc_request *mrq)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+
+ //=== for sdio profile ===
+#if 0 /* --- by chhung */
+ u32 old_H32, old_L32, new_H32, new_L32;
+ u32 ticks = 0, opcode = 0, sizes = 0, bRx = 0;
+#endif /* end of --- */
+
+ if(host->mrq){
+ ERR_MSG("XXX host->mrq<0x%.8x>", (int)host->mrq);
+ BUG();
+ }
+
+ if (!is_card_present(host) || host->power_mode == MMC_POWER_OFF) {
+ ERR_MSG("cmd<%d> card<%d> power<%d>", mrq->cmd->opcode, is_card_present(host), host->power_mode);
+ mrq->cmd->error = (unsigned int)-ENOMEDIUM;
+
+#if 1
+ mrq->done(mrq); // call done directly.
+#else
+ mrq->cmd->retries = 0; // please don't retry.
+ mmc_request_done(mmc, mrq);
+#endif
+
+ return;
+ }
+
+ /* start to process */
+ spin_lock(&host->lock);
+#if 0 /* --- by chhung */
+ if (sdio_pro_enable) { //=== for sdio profile ===
+ if (mrq->cmd->opcode == 52 || mrq->cmd->opcode == 53) {
+ GPT_GetCounter64(&old_L32, &old_H32);
+ }
+ }
+#endif /* end of --- */
+
+ host->mrq = mrq;
+
+ if (msdc_do_request(mmc,mrq)) {
+ if(host->hw->flags & MSDC_REMOVABLE && ralink_soc == MT762X_SOC_MT7621AT && mrq->data && mrq->data->error) {
+ msdc_tune_request(mmc,mrq);
+ }
+ }
+
+ /* ==== when request done, check if app_cmd ==== */
+ if (mrq->cmd->opcode == MMC_APP_CMD) {
+ host->app_cmd = 1;
+ host->app_cmd_arg = mrq->cmd->arg; /* save the RCA */
+ } else {
+ host->app_cmd = 0;
+ //host->app_cmd_arg = 0;
+ }
+
+ host->mrq = NULL;
+
+#if 0 /* --- by chhung */
+ //=== for sdio profile ===
+ if (sdio_pro_enable) {
+ if (mrq->cmd->opcode == 52 || mrq->cmd->opcode == 53) {
+ GPT_GetCounter64(&new_L32, &new_H32);
+ ticks = msdc_time_calc(old_L32, old_H32, new_L32, new_H32);
+
+ opcode = mrq->cmd->opcode;
+ if (mrq->cmd->data) {
+ sizes = mrq->cmd->data->blocks * mrq->cmd->data->blksz;
+ bRx = mrq->cmd->data->flags & MMC_DATA_READ ? 1 : 0 ;
+ } else {
+ bRx = mrq->cmd->arg & 0x80000000 ? 1 : 0;
+ }
+
+ if (!mrq->cmd->error) {
+ msdc_performance(opcode, sizes, bRx, ticks);
+ }
+ }
+ }
+#endif /* end of --- */
+ spin_unlock(&host->lock);
+
+ mmc_request_done(mmc, mrq);
+
+ return;
+}
+
+/* called by ops.set_ios */
+static void msdc_set_buswidth(struct msdc_host *host, u32 width)
+{
+ u32 base = host->base;
+ u32 val = sdr_read32(SDC_CFG);
+
+ val &= ~SDC_CFG_BUSWIDTH;
+
+ switch (width) {
+ default:
+ case MMC_BUS_WIDTH_1:
+ width = 1;
+ val |= (MSDC_BUS_1BITS << 16);
+ break;
+ case MMC_BUS_WIDTH_4:
+ val |= (MSDC_BUS_4BITS << 16);
+ break;
+ case MMC_BUS_WIDTH_8:
+ val |= (MSDC_BUS_8BITS << 16);
+ break;
+ }
+
+ sdr_write32(SDC_CFG, val);
+
+ N_MSG(CFG, "Bus Width = %d", width);
+}
+
+/* ops.set_ios */
+static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct msdc_hw *hw=host->hw;
+ u32 base = host->base;
+ u32 ddr = 0;
+
+#ifdef MT6575_SD_DEBUG
+ static char *vdd[] = {
+ "1.50v", "1.55v", "1.60v", "1.65v", "1.70v", "1.80v", "1.90v",
+ "2.00v", "2.10v", "2.20v", "2.30v", "2.40v", "2.50v", "2.60v",
+ "2.70v", "2.80v", "2.90v", "3.00v", "3.10v", "3.20v", "3.30v",
+ "3.40v", "3.50v", "3.60v"
+ };
+ static char *power_mode[] = {
+ "OFF", "UP", "ON"
+ };
+ static char *bus_mode[] = {
+ "UNKNOWN", "OPENDRAIN", "PUSHPULL"
+ };
+ static char *timing[] = {
+ "LEGACY", "MMC_HS", "SD_HS"
+ };
+
+ printk("SET_IOS: CLK(%dkHz), BUS(%s), BW(%u), PWR(%s), VDD(%s), TIMING(%s)",
+ ios->clock / 1000, bus_mode[ios->bus_mode],
+ (ios->bus_width == MMC_BUS_WIDTH_4) ? 4 : 1,
+ power_mode[ios->power_mode], vdd[ios->vdd], timing[ios->timing]);
+#endif
+
+ msdc_set_buswidth(host, ios->bus_width);
+
+ /* Power control ??? */
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ case MMC_POWER_UP:
+ // msdc_set_power_mode(host, ios->power_mode); /* --- by chhung */
+ break;
+ case MMC_POWER_ON:
+ host->power_mode = MMC_POWER_ON;
+ break;
+ default:
+ break;
+ }
+
+ /* Clock control */
+ if (host->mclk != ios->clock) {
+ if(ios->clock > 25000000) {
+ //if (!(host->hw->flags & MSDC_REMOVABLE)) {
+ INIT_MSG("SD data latch edge<%d>", hw->data_edge);
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_RSPL, hw->cmd_edge);
+ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, hw->data_edge);
+ //} /* for tuning debug */
+ } else { /* default value */
+ sdr_write32(MSDC_IOCON, 0x00000000);
+ // sdr_write32(MSDC_DAT_RDDLY0, 0x00000000);
+ sdr_write32(MSDC_DAT_RDDLY0, 0x10101010); // for MT7620 E2 and afterward
+ sdr_write32(MSDC_DAT_RDDLY1, 0x00000000);
+ // sdr_write32(MSDC_PAD_TUNE, 0x00000000);
+ sdr_write32(MSDC_PAD_TUNE, 0x84101010); // for MT7620 E2 and afterward
+ }
+ msdc_set_mclk(host, ddr, ios->clock);
+ }
+}
+
+/* ops.get_ro */
+static int msdc_ops_get_ro(struct mmc_host *mmc)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 base = host->base;
+ unsigned long flags;
+ int ro = 0;
+
+ if (host->hw->flags & MSDC_WP_PIN_EN) { /* set for card */
+ spin_lock_irqsave(&host->lock, flags);
+ ro = (sdr_read32(MSDC_PS) >> 31);
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+ return ro;
+}
+
+/* ops.get_cd */
+static int msdc_ops_get_cd(struct mmc_host *mmc)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 base = host->base;
+ unsigned long flags;
+ int present = 1;
+
+ /* for sdio, MSDC_REMOVABLE not set, always return 1 */
+ if (!(host->hw->flags & MSDC_REMOVABLE)) {
+ /* For sdio, read H/W always get<1>, but may timeout some times */
+#if 1
+ host->card_inserted = 1;
+ return 1;
+#else
+ host->card_inserted = (host->pm_state.event == PM_EVENT_USER_RESUME) ? 1 : 0;
+ INIT_MSG("sdio ops_get_cd<%d>", host->card_inserted);
+ return host->card_inserted;
+#endif
+ }
+
+ /* MSDC_CD_PIN_EN set for card */
+ if (host->hw->flags & MSDC_CD_PIN_EN) {
+ spin_lock_irqsave(&host->lock, flags);
+#if 0
+ present = host->card_inserted; /* why not read from H/W: Fix me*/
+#else
+ // CD
+ if (cd_active_low)
+ present = (sdr_read32(MSDC_PS) & MSDC_PS_CDSTS) ? 0 : 1;
+ else
+ present = (sdr_read32(MSDC_PS) & MSDC_PS_CDSTS) ? 1 : 0;
+ host->card_inserted = present;
+#endif
+ spin_unlock_irqrestore(&host->lock, flags);
+ } else {
+ present = 0; /* TODO? Check DAT3 pins for card detection */
+ }
+
+ INIT_MSG("ops_get_cd return<%d>", present);
+ return present;
+}
+
+/* ops.enable_sdio_irq */
+static void msdc_ops_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+ u32 tmp;
+
+ if (hw->flags & MSDC_EXT_SDIO_IRQ) { /* yes for sdio */
+ if (enable) {
+ hw->enable_sdio_eirq(); /* combo_sdio_enable_eirq */
+ } else {
+ hw->disable_sdio_eirq(); /* combo_sdio_disable_eirq */
+ }
+ } else {
+ ERR_MSG("XXX "); /* so never enter here */
+ tmp = sdr_read32(SDC_CFG);
+ /* FIXME. Need to interrupt gap detection */
+ if (enable) {
+ tmp |= (SDC_CFG_SDIOIDE | SDC_CFG_SDIOINTWKUP);
+ } else {
+ tmp &= ~(SDC_CFG_SDIOIDE | SDC_CFG_SDIOINTWKUP);
+ }
+ sdr_write32(SDC_CFG, tmp);
+ }
+}
+
+static struct mmc_host_ops mt_msdc_ops = {
+ .request = msdc_ops_request,
+ .set_ios = msdc_ops_set_ios,
+ .get_ro = msdc_ops_get_ro,
+ .get_cd = msdc_ops_get_cd,
+ .enable_sdio_irq = msdc_ops_enable_sdio_irq,
+};
+
+/*--------------------------------------------------------------------------*/
+/* interrupt handler */
+/*--------------------------------------------------------------------------*/
+static irqreturn_t msdc_irq(int irq, void *dev_id)
+{
+ struct msdc_host *host = (struct msdc_host *)dev_id;
+ struct mmc_data *data = host->data;
+ struct mmc_command *cmd = host->cmd;
+ u32 base = host->base;
+
+ u32 cmdsts = MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO | MSDC_INT_CMDRDY |
+ MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO | MSDC_INT_ACMDRDY |
+ MSDC_INT_ACMD19_DONE;
+ u32 datsts = MSDC_INT_DATCRCERR |MSDC_INT_DATTMO;
+
+ u32 intsts = sdr_read32(MSDC_INT);
+ u32 inten = sdr_read32(MSDC_INTEN); inten &= intsts;
+
+ sdr_write32(MSDC_INT, intsts); /* clear interrupts */
+ /* MSG will cause fatal error */
+
+ /* card change interrupt */
+ if (intsts & MSDC_INT_CDSC){
+ if (mtk_sw_poll)
+ return IRQ_HANDLED;
+ IRQ_MSG("MSDC_INT_CDSC irq<0x%.8x>", intsts);
+#if 0 /* ---/+++ by chhung: fix slot mechanical bounce issue */
+ tasklet_hi_schedule(&host->card_tasklet);
+#else
+ schedule_delayed_work(&host->card_delaywork, HZ);
+#endif
+ /* tuning when plug card ? */
+ }
+
+ /* sdio interrupt */
+ if (intsts & MSDC_INT_SDIOIRQ){
+ IRQ_MSG("XXX MSDC_INT_SDIOIRQ"); /* seems not sdio irq */
+ //mmc_signal_sdio_irq(host->mmc);
+ }
+
+ /* transfer complete interrupt */
+ if (data != NULL) {
+ if (inten & MSDC_INT_XFER_COMPL) {
+ data->bytes_xfered = host->dma.xfersz;
+ complete(&host->xfer_done);
+ }
+
+ if (intsts & datsts) {
+ /* do basic reset, or stop command will sdc_busy */
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ atomic_set(&host->abort, 1); /* For PIO mode exit */
+
+ if (intsts & MSDC_INT_DATTMO){
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_DATTMO", host->mrq->cmd->opcode);
+ data->error = (unsigned int)-ETIMEDOUT;
+ }
+ else if (intsts & MSDC_INT_DATCRCERR){
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_DATCRCERR, SDC_DCRC_STS<0x%x>", host->mrq->cmd->opcode, sdr_read32(SDC_DCRC_STS));
+ data->error = (unsigned int)-EIO;
+ }
+
+ //if(sdr_read32(MSDC_INTEN) & MSDC_INT_XFER_COMPL) {
+ if (host->dma_xfer) {
+ complete(&host->xfer_done); /* Read CRC come fast, XFER_COMPL not enabled */
+ } /* PIO mode can't do complete, because not init */
+ }
+ }
+
+ /* command interrupts */
+ if ((cmd != NULL) && (intsts & cmdsts)) {
+ if ((intsts & MSDC_INT_CMDRDY) || (intsts & MSDC_INT_ACMDRDY) ||
+ (intsts & MSDC_INT_ACMD19_DONE)) {
+ u32 *rsp = &cmd->resp[0];
+
+ switch (host->cmd_rsp) {
+ case RESP_NONE:
+ break;
+ case RESP_R2:
+ *rsp++ = sdr_read32(SDC_RESP3); *rsp++ = sdr_read32(SDC_RESP2);
+ *rsp++ = sdr_read32(SDC_RESP1); *rsp++ = sdr_read32(SDC_RESP0);
+ break;
+ default: /* Response types 1, 3, 4, 5, 6, 7(1b) */
+ if ((intsts & MSDC_INT_ACMDRDY) || (intsts & MSDC_INT_ACMD19_DONE)) {
+ *rsp = sdr_read32(SDC_ACMD_RESP);
+ } else {
+ *rsp = sdr_read32(SDC_RESP0);
+ }
+ break;
+ }
+ } else if ((intsts & MSDC_INT_RSPCRCERR) || (intsts & MSDC_INT_ACMDCRCERR)) {
+ if(intsts & MSDC_INT_ACMDCRCERR){
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_ACMDCRCERR",cmd->opcode);
+ }
+ else {
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_RSPCRCERR",cmd->opcode);
+ }
+ cmd->error = (unsigned int)-EIO;
+ } else if ((intsts & MSDC_INT_CMDTMO) || (intsts & MSDC_INT_ACMDTMO)) {
+ if(intsts & MSDC_INT_ACMDTMO){
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_ACMDTMO",cmd->opcode);
+ }
+ else {
+ IRQ_MSG("XXX CMD<%d> MSDC_INT_CMDTMO",cmd->opcode);
+ }
+ cmd->error = (unsigned int)-ETIMEDOUT;
+ msdc_reset();
+ msdc_clr_fifo();
+ msdc_clr_int();
+ }
+ complete(&host->cmd_done);
+ }
+
+ /* mmc irq interrupts */
+ if (intsts & MSDC_INT_MMCIRQ) {
+ printk(KERN_INFO "msdc[%d] MMCIRQ: SDC_CSTS=0x%.8x\r\n", host->id, sdr_read32(SDC_CSTS));
+ }
+
+#ifdef MT6575_SD_DEBUG
+ {
+/* msdc_int_reg *int_reg = (msdc_int_reg*)&intsts;*/
+ N_MSG(INT, "IRQ_EVT(0x%x): MMCIRQ(%d) CDSC(%d), ACRDY(%d), ACTMO(%d), ACCRE(%d) AC19DN(%d)",
+ intsts,
+ int_reg->mmcirq,
+ int_reg->cdsc,
+ int_reg->atocmdrdy,
+ int_reg->atocmdtmo,
+ int_reg->atocmdcrc,
+ int_reg->atocmd19done);
+ N_MSG(INT, "IRQ_EVT(0x%x): SDIO(%d) CMDRDY(%d), CMDTMO(%d), RSPCRC(%d), CSTA(%d)",
+ intsts,
+ int_reg->sdioirq,
+ int_reg->cmdrdy,
+ int_reg->cmdtmo,
+ int_reg->rspcrc,
+ int_reg->csta);
+ N_MSG(INT, "IRQ_EVT(0x%x): XFCMP(%d) DXDONE(%d), DATTMO(%d), DATCRC(%d), DMAEMP(%d)",
+ intsts,
+ int_reg->xfercomp,
+ int_reg->dxferdone,
+ int_reg->dattmo,
+ int_reg->datcrc,
+ int_reg->dmaqempty);
+
+ }
+#endif
+
+ return IRQ_HANDLED;
+}
+
+/*--------------------------------------------------------------------------*/
+/* platform_driver members */
+/*--------------------------------------------------------------------------*/
+/* called by msdc_drv_probe/remove */
+static void msdc_enable_cd_irq(struct msdc_host *host, int enable)
+{
+ struct msdc_hw *hw = host->hw;
+ u32 base = host->base;
+
+ /* for sdio, not set */
+ if ((hw->flags & MSDC_CD_PIN_EN) == 0) {
+ /* Pull down card detection pin since it is not avaiable */
+ /*
+ if (hw->config_gpio_pin)
+ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_DOWN);
+ */
+ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN);
+ sdr_clr_bits(MSDC_INTEN, MSDC_INTEN_CDSC);
+ sdr_clr_bits(SDC_CFG, SDC_CFG_INSWKUP);
+ return;
+ }
+
+ N_MSG(CFG, "CD IRQ Eanable(%d)", enable);
+
+ if (enable) {
+ if (hw->enable_cd_eirq) { /* not set, never enter */
+ hw->enable_cd_eirq();
+ } else {
+ /* card detection circuit relies on the core power so that the core power
+ * shouldn't be turned off. Here adds a reference count to keep
+ * the core power alive.
+ */
+ //msdc_vcore_on(host); //did in msdc_init_hw()
+
+ if (hw->config_gpio_pin) /* NULL */
+ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_UP);
+
+ sdr_set_field(MSDC_PS, MSDC_PS_CDDEBOUNCE, DEFAULT_DEBOUNCE);
+ sdr_set_bits(MSDC_PS, MSDC_PS_CDEN);
+ sdr_set_bits(MSDC_INTEN, MSDC_INTEN_CDSC);
+ sdr_set_bits(SDC_CFG, SDC_CFG_INSWKUP); /* not in document! Fix me */
+ }
+ } else {
+ if (hw->disable_cd_eirq) {
+ hw->disable_cd_eirq();
+ } else {
+ if (hw->config_gpio_pin) /* NULL */
+ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_DOWN);
+
+ sdr_clr_bits(SDC_CFG, SDC_CFG_INSWKUP);
+ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN);
+ sdr_clr_bits(MSDC_INTEN, MSDC_INTEN_CDSC);
+
+ /* Here decreases a reference count to core power since card
+ * detection circuit is shutdown.
+ */
+ //msdc_vcore_off(host);
+ }
+ }
+}
+
+/* called by msdc_drv_probe */
+static void msdc_init_hw(struct msdc_host *host)
+{
+ u32 base = host->base;
+ struct msdc_hw *hw = host->hw;
+
+#ifdef MT6575_SD_DEBUG
+ msdc_reg[host->id] = (struct msdc_regs *)host->base;
+#endif
+
+ /* Power on */
+#if 0 /* --- by chhung */
+ msdc_vcore_on(host);
+ msdc_pin_reset(host, MSDC_PIN_PULL_UP);
+ msdc_select_clksrc(host, hw->clk_src);
+ enable_clock(PERI_MSDC0_PDN + host->id, "SD");
+ msdc_vdd_on(host);
+#endif /* end of --- */
+ /* Configure to MMC/SD mode */
+ sdr_set_field(MSDC_CFG, MSDC_CFG_MODE, MSDC_SDMMC);
+
+ /* Reset */
+ msdc_reset();
+ msdc_clr_fifo();
+
+ /* Disable card detection */
+ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN);
+
+ /* Disable and clear all interrupts */
+ sdr_clr_bits(MSDC_INTEN, sdr_read32(MSDC_INTEN));
+ sdr_write32(MSDC_INT, sdr_read32(MSDC_INT));
+
+#if 1
+ /* reset tuning parameter */
+ sdr_write32(MSDC_PAD_CTL0, 0x00090000);
+ sdr_write32(MSDC_PAD_CTL1, 0x000A0000);
+ sdr_write32(MSDC_PAD_CTL2, 0x000A0000);
+ // sdr_write32(MSDC_PAD_TUNE, 0x00000000);
+ sdr_write32(MSDC_PAD_TUNE, 0x84101010); // for MT7620 E2 and afterward
+ // sdr_write32(MSDC_DAT_RDDLY0, 0x00000000);
+ sdr_write32(MSDC_DAT_RDDLY0, 0x10101010); // for MT7620 E2 and afterward
+ sdr_write32(MSDC_DAT_RDDLY1, 0x00000000);
+ sdr_write32(MSDC_IOCON, 0x00000000);
+#if 0 // use MT7620 default value: 0x403c004f
+ sdr_write32(MSDC_PATCH_BIT0, 0x003C000F); /* bit0 modified: Rx Data Clock Source: 1 -> 2.0*/
+#endif
+
+ if (sdr_read32(MSDC_ECO_VER) >= 4) {
+ if (host->id == 1) {
+ sdr_set_field(MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_WRDAT_CRCS, 1);
+ sdr_set_field(MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_CMD_RSP, 1);
+
+ /* internal clock: latch read data */
+ sdr_set_bits(MSDC_PATCH_BIT0, MSDC_PATCH_BIT_CKGEN_CK);
+ }
+ }
+#endif
+
+ /* for safety, should clear SDC_CFG.SDIO_INT_DET_EN & set SDC_CFG.SDIO in
+ pre-loader,uboot,kernel drivers. and SDC_CFG.SDIO_INT_DET_EN will be only
+ set when kernel driver wants to use SDIO bus interrupt */
+ /* Configure to enable SDIO mode. it's must otherwise sdio cmd5 failed */
+ sdr_set_bits(SDC_CFG, SDC_CFG_SDIO);
+
+ /* disable detect SDIO device interupt function */
+ sdr_clr_bits(SDC_CFG, SDC_CFG_SDIOIDE);
+
+ /* eneable SMT for glitch filter */
+ sdr_set_bits(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKSMT);
+ sdr_set_bits(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDSMT);
+ sdr_set_bits(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATSMT);
+
+#if 1
+ /* set clk, cmd, dat pad driving */
+ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVN, hw->clk_drv);
+ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVP, hw->clk_drv);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVN, hw->cmd_drv);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVP, hw->cmd_drv);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVN, hw->dat_drv);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVP, hw->dat_drv);
+#else
+ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVN, 0);
+ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVP, 0);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVN, 0);
+ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVP, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVN, 0);
+ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVP, 0);
+#endif
+
+ /* set sampling edge */
+
+ /* write crc timeout detection */
+ sdr_set_field(MSDC_PATCH_BIT0, 1 << 30, 1);
+
+ /* Configure to default data timeout */
+ sdr_set_field(SDC_CFG, SDC_CFG_DTOC, DEFAULT_DTOC);
+
+ msdc_set_buswidth(host, MMC_BUS_WIDTH_1);
+
+ N_MSG(FUC, "init hardware done!");
+}
+
+/* called by msdc_drv_remove */
+static void msdc_deinit_hw(struct msdc_host *host)
+{
+ u32 base = host->base;
+
+ /* Disable and clear all interrupts */
+ sdr_clr_bits(MSDC_INTEN, sdr_read32(MSDC_INTEN));
+ sdr_write32(MSDC_INT, sdr_read32(MSDC_INT));
+
+ /* Disable card detection */
+ msdc_enable_cd_irq(host, 0);
+ // msdc_set_power_mode(host, MMC_POWER_OFF); /* make sure power down */ /* --- by chhung */
+}
+
+/* init gpd and bd list in msdc_drv_probe */
+static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma)
+{
+ gpd_t *gpd = dma->gpd;
+ bd_t *bd = dma->bd;
+ bd_t *ptr, *prev;
+
+ /* we just support one gpd */
+ int bdlen = MAX_BD_PER_GPD;
+
+ /* init the 2 gpd */
+ memset(gpd, 0, sizeof(gpd_t) * 2);
+ //gpd->next = (void *)virt_to_phys(gpd + 1); /* pointer to a null gpd, bug! kmalloc <-> virt_to_phys */
+ //gpd->next = (dma->gpd_addr + 1); /* bug */
+ gpd->next = (void *)((u32)dma->gpd_addr + sizeof(gpd_t));
+
+ //gpd->intr = 0;
+ gpd->bdp = 1; /* hwo, cs, bd pointer */
+ //gpd->ptr = (void*)virt_to_phys(bd);
+ gpd->ptr = (void *)dma->bd_addr; /* physical address */
+
+ memset(bd, 0, sizeof(bd_t) * bdlen);
+ ptr = bd + bdlen - 1;
+ //ptr->eol = 1; /* 0 or 1 [Fix me]*/
+ //ptr->next = 0;
+
+ while (ptr != bd) {
+ prev = ptr - 1;
+ prev->next = (void *)(dma->bd_addr + sizeof(bd_t) *(ptr - bd));
+ ptr = prev;
+ }
+}
+
+static int msdc_drv_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ __iomem void *base;
+ struct mmc_host *mmc;
+ struct resource *mem;
+ struct msdc_host *host;
+ struct msdc_hw *hw;
+ int ret, irq;
+
+ pdev->dev.platform_data = &msdc0_hw;
+
+ if (of_property_read_bool(pdev->dev.of_node, "mtk,wp-en"))
+ msdc0_hw.flags |= MSDC_WP_PIN_EN;
+
+ /* Allocate MMC host for this device */
+ mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev);
+ if (!mmc) return -ENOMEM;
+
+ hw = (struct msdc_hw*)pdev->dev.platform_data;
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+
+ //BUG_ON((!hw) || (!mem) || (irq < 0)); /* --- by chhung */
+
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ /* Set host parameters to mmc */
+ mmc->ops = &mt_msdc_ops;
+ mmc->f_min = HOST_MIN_MCLK;
+ mmc->f_max = HOST_MAX_MCLK;
+ mmc->ocr_avail = MSDC_OCR_AVAIL;
+
+ /* For sd card: MSDC_SYS_SUSPEND | MSDC_WP_PIN_EN | MSDC_CD_PIN_EN | MSDC_REMOVABLE | MSDC_HIGHSPEED,
+ For sdio : MSDC_EXT_SDIO_IRQ | MSDC_HIGHSPEED */
+ if (hw->flags & MSDC_HIGHSPEED) {
+ mmc->caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
+ }
+ if (hw->data_pins == 4) { /* current data_pins are all 4*/
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+ } else if (hw->data_pins == 8) {
+ mmc->caps |= MMC_CAP_8_BIT_DATA;
+ }
+ if ((hw->flags & MSDC_SDIO_IRQ) || (hw->flags & MSDC_EXT_SDIO_IRQ))
+ mmc->caps |= MMC_CAP_SDIO_IRQ; /* yes for sdio */
+
+ cd_active_low = !of_property_read_bool(pdev->dev.of_node, "mediatek,cd-high");
+ mtk_sw_poll = of_property_read_bool(pdev->dev.of_node, "mediatek,cd-poll");
+
+ if (mtk_sw_poll)
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+ /* MMC core transfer sizes tunable parameters */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3,10,0)
+ mmc->max_segs = MAX_HW_SGMTS;
+#else
+ mmc->max_hw_segs = MAX_HW_SGMTS;
+ mmc->max_phys_segs = MAX_PHY_SGMTS;
+#endif
+ mmc->max_seg_size = MAX_SGMT_SZ;
+ mmc->max_blk_size = HOST_MAX_BLKSZ;
+ mmc->max_req_size = MAX_REQ_SZ;
+ mmc->max_blk_count = mmc->max_req_size;
+
+ host = mmc_priv(mmc);
+ host->hw = hw;
+ host->mmc = mmc;
+ host->id = pdev->id;
+ if (host->id < 0 || host->id >= 4)
+ host->id = 0;
+ host->error = 0;
+ host->irq = irq;
+ host->base = (unsigned long) base;
+ host->mclk = 0; /* mclk: the request clock of mmc sub-system */
+ host->hclk = hclks[hw->clk_src]; /* hclk: clock of clock source to msdc controller */
+ host->sclk = 0; /* sclk: the really clock after divition */
+ host->pm_state = PMSG_RESUME;
+ host->suspend = 0;
+ host->core_clkon = 0;
+ host->card_clkon = 0;
+ host->core_power = 0;
+ host->power_mode = MMC_POWER_OFF;
+// host->card_inserted = hw->flags & MSDC_REMOVABLE ? 0 : 1;
+ host->timeout_ns = 0;
+ host->timeout_clks = DEFAULT_DTOC * 65536;
+
+ host->mrq = NULL;
+ //init_MUTEX(&host->sem); /* we don't need to support multiple threads access */
+
+ host->dma.used_gpd = 0;
+ host->dma.used_bd = 0;
+ mmc_dev(mmc)->dma_mask = NULL;
+
+ /* using dma_alloc_coherent*/ /* todo: using 1, for all 4 slots */
+ host->dma.gpd = dma_alloc_coherent(NULL, MAX_GPD_NUM * sizeof(gpd_t), &host->dma.gpd_addr, GFP_KERNEL);
+ host->dma.bd = dma_alloc_coherent(NULL, MAX_BD_NUM * sizeof(bd_t), &host->dma.bd_addr, GFP_KERNEL);
+ BUG_ON((!host->dma.gpd) || (!host->dma.bd));
+ msdc_init_gpd_bd(host, &host->dma);
+ /*for emmc*/
+ msdc_6575_host[pdev->id] = host;
+
+#if 0
+ tasklet_init(&host->card_tasklet, msdc_tasklet_card, (ulong)host);
+#else
+ INIT_DELAYED_WORK(&host->card_delaywork, msdc_tasklet_card);
+#endif
+ spin_lock_init(&host->lock);
+ msdc_init_hw(host);
+
+ if (ralink_soc == MT762X_SOC_MT7621AT)
+ ret = request_irq((unsigned int)irq, msdc_irq, 0, dev_name(&pdev->dev), host);
+ else
+ ret = request_irq((unsigned int)irq, msdc_irq, IRQF_TRIGGER_LOW, dev_name(&pdev->dev), host);
+
+ if (ret) goto release;
+ // mt65xx_irq_unmask(irq); /* --- by chhung */
+
+ if (hw->flags & MSDC_CD_PIN_EN) { /* not set for sdio */
+ if (hw->request_cd_eirq) { /* not set for MT6575 */
+ hw->request_cd_eirq(msdc_eirq_cd, (void*)host); /* msdc_eirq_cd will not be used! */
+ }
+ }
+
+ if (hw->request_sdio_eirq) /* set to combo_sdio_request_eirq() for WIFI */
+ hw->request_sdio_eirq(msdc_eirq_sdio, (void*)host); /* msdc_eirq_sdio() will be called when EIRQ */
+
+ if (hw->register_pm) {/* yes for sdio */
+#ifdef CONFIG_PM
+ hw->register_pm(msdc_pm, (void*)host); /* combo_sdio_register_pm() */
+#endif
+ if(hw->flags & MSDC_SYS_SUSPEND) { /* will not set for WIFI */
+ ERR_MSG("MSDC_SYS_SUSPEND and register_pm both set");
+ }
+ //mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY; /* pm not controlled by system but by client. */ /* --- by chhung */
+ }
+
+ platform_set_drvdata(pdev, mmc);
+
+ ret = mmc_add_host(mmc);
+ if (ret) goto free_irq;
+
+ /* Config card detection pin and enable interrupts */
+ if (hw->flags & MSDC_CD_PIN_EN) { /* set for card */
+ msdc_enable_cd_irq(host, 1);
+ } else {
+ msdc_enable_cd_irq(host, 0);
+ }
+
+ return 0;
+
+free_irq:
+ free_irq(irq, host);
+release:
+ platform_set_drvdata(pdev, NULL);
+ msdc_deinit_hw(host);
+
+#if 0
+ tasklet_kill(&host->card_tasklet);
+#else
+ cancel_delayed_work_sync(&host->card_delaywork);
+#endif
+
+ if (mem)
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+
+ mmc_free_host(mmc);
+
+ return ret;
+}
+
+/* 4 device share one driver, using "drvdata" to show difference */
+static int msdc_drv_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct msdc_host *host;
+ struct resource *mem;
+
+ mmc = platform_get_drvdata(pdev);
+ BUG_ON(!mmc);
+
+ host = mmc_priv(mmc);
+ BUG_ON(!host);
+
+ ERR_MSG("removed !!!");
+
+ platform_set_drvdata(pdev, NULL);
+ mmc_remove_host(host->mmc);
+ msdc_deinit_hw(host);
+
+#if 0
+ tasklet_kill(&host->card_tasklet);
+#else
+ cancel_delayed_work_sync(&host->card_delaywork);
+#endif
+ free_irq(host->irq, host);
+
+ dma_free_coherent(NULL, MAX_GPD_NUM * sizeof(gpd_t), host->dma.gpd, host->dma.gpd_addr);
+ dma_free_coherent(NULL, MAX_BD_NUM * sizeof(bd_t), host->dma.bd, host->dma.bd_addr);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (mem)
+ release_mem_region(mem->start, mem->end - mem->start + 1);
+
+ mmc_free_host(host->mmc);
+
+ return 0;
+}
+
+/* Fix me: Power Flow */
+#ifdef CONFIG_PM
+static int msdc_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int ret = 0;
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct msdc_host *host = mmc_priv(mmc);
+
+ if (mmc && state.event == PM_EVENT_SUSPEND && (host->hw->flags & MSDC_SYS_SUSPEND)) { /* will set for card */
+ msdc_pm(state, (void*)host);
+ }
+
+ return ret;
+}
+
+static int msdc_drv_resume(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct msdc_host *host = mmc_priv(mmc);
+ struct pm_message state;
+
+ state.event = PM_EVENT_RESUME;
+ if (mmc && (host->hw->flags & MSDC_SYS_SUSPEND)) {/* will set for card */
+ msdc_pm(state, (void*)host);
+ }
+
+ /* This mean WIFI not controller by PM */
+
+ return ret;
+}
+#endif
+
+static const struct of_device_id mt7620_sdhci_match[] = {
+ { .compatible = "ralink,mt7620-sdhci" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt7620_sdhci_match);
+
+static struct platform_driver mt_msdc_driver = {
+ .probe = msdc_drv_probe,
+ .remove = msdc_drv_remove,
+#ifdef CONFIG_PM
+ .suspend = msdc_drv_suspend,
+ .resume = msdc_drv_resume,
+#endif
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mt7620_sdhci_match,
+ },
+};
+
+/*--------------------------------------------------------------------------*/
+/* module init/exit */
+/*--------------------------------------------------------------------------*/
+static int __init mt_msdc_init(void)
+{
+ int ret;
+/* +++ by chhung */
+ u32 reg;
+
+#if defined (CONFIG_MTD_ANY_RALINK)
+ extern int ra_check_flash_type(void);
+ if(ra_check_flash_type() == 2) { /* NAND */
+ printk("%s: !!!!! SDXC Module Initialize Fail !!!!!", __func__);
+ return 0;
+ }
+#endif
+ printk("MTK MSDC device init.\n");
+ mtk_sd_device.dev.platform_data = &msdc0_hw;
+if (ralink_soc == MT762X_SOC_MT7620A || ralink_soc == MT762X_SOC_MT7621AT) {
+//#if defined (CONFIG_RALINK_MT7620) || defined (CONFIG_RALINK_MT7621)
+ reg = sdr_read32((volatile u32*)(RALINK_SYSCTL_BASE + 0x60)) & ~(0x3<<18);
+//#if defined (CONFIG_RALINK_MT7620)
+ if (ralink_soc == MT762X_SOC_MT7620A)
+ reg |= 0x1<<18;
+//#endif
+} else {
+//#elif defined (CONFIG_RALINK_MT7628)
+ /* TODO: maybe omitted when RAether already toggle AGPIO_CFG */
+ reg = sdr_read32((volatile u32*)(RALINK_SYSCTL_BASE + 0x3c));
+ reg |= 0x1e << 16;
+ sdr_write32((volatile u32*)(RALINK_SYSCTL_BASE + 0x3c), reg);
+
+ reg = sdr_read32((volatile u32*)(RALINK_SYSCTL_BASE + 0x60)) & ~(0x3<<10);
+#if defined (CONFIG_MTK_MMC_EMMC_8BIT)
+ reg |= 0x3<<26 | 0x3<<28 | 0x3<<30;
+ msdc0_hw.data_pins = 8,
+#endif
+//#endif
+}
+ sdr_write32((volatile u32*)(RALINK_SYSCTL_BASE + 0x60), reg);
+ //platform_device_register(&mtk_sd_device);
+/* end of +++ */
+
+ ret = platform_driver_register(&mt_msdc_driver);
+ if (ret) {
+ printk(KERN_ERR DRV_NAME ": Can't register driver");
+ return ret;
+ }
+ printk(KERN_INFO DRV_NAME ": MediaTek MT6575 MSDC Driver\n");
+
+#if defined (MT6575_SD_DEBUG)
+ msdc_debug_proc_init();
+#endif
+ return 0;
+}
+
+static void __exit mt_msdc_exit(void)
+{
+// platform_device_unregister(&mtk_sd_device);
+ platform_driver_unregister(&mt_msdc_driver);
+}
+
+module_init(mt_msdc_init);
+module_exit(mt_msdc_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MediaTek MT6575 SD/MMC Card Driver");
+MODULE_AUTHOR("Infinity Chen <[email protected]>");
+
+EXPORT_SYMBOL(msdc_6575_host);
From: John Crispin <[email protected]>
Add support for SoCs from the mt7621 family. These all have 2 GMAC ports,
both of which are attached to the same internal 1000MBit switch. Currently
we only support GMAC1 as the sole CPU port. MT7621 is very similar to
MT7620 with only a few registers having different offsets. MT7621 is the
first SoC to have the new QDMA engine builtin. The older PDMA engine is
also present. unfortunatley, to get the best performance we need to run RX
on PDMA and TX on QDMA. This SoC is also the first to have TX vlan
offloading and TSO6 support.
NeilBrown: the driver didn't work when I tested, so I changed it
to match known-working code as much as possible. This included
converting to the PDMA engine for TX.
Signed-off-by: John Crispin <[email protected]>
Signed-off-by: Felix Fietkau <[email protected]>
Signed-off-by: Michael Lee <[email protected]>
Signed-off-by: NeilBrown <[email protected]>
---
drivers/staging/mt7621-eth/TODO | 3 +
drivers/staging/mt7621-eth/soc_mt7621.c | 160 +++++++++++++++++++++++++++++++
2 files changed, 163 insertions(+)
create mode 100644 drivers/staging/mt7621-eth/soc_mt7621.c
diff --git a/drivers/staging/mt7621-eth/TODO b/drivers/staging/mt7621-eth/TODO
index 50fb5a959ee8..f9e47d4b4cd4 100644
--- a/drivers/staging/mt7621-eth/TODO
+++ b/drivers/staging/mt7621-eth/TODO
@@ -6,5 +6,8 @@
- convert gsw code to use switchdev interfaces
- md7620_mmi_write etc should probably be wrapped
in a regmap abstraction.
+- Get soc_mt7621 to work with QDMA TX if possible.
+- Ensure phys are correctly configured when a cable
+ is plugged in.
Cc: NeilBrown <[email protected]>
diff --git a/drivers/staging/mt7621-eth/soc_mt7621.c b/drivers/staging/mt7621-eth/soc_mt7621.c
new file mode 100644
index 000000000000..743c0eed89b6
--- /dev/null
+++ b/drivers/staging/mt7621-eth/soc_mt7621.c
@@ -0,0 +1,160 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <[email protected]>
+ * Copyright (C) 2009-2016 Felix Fietkau <[email protected]>
+ * Copyright (C) 2013-2016 Michael Lee <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/if_vlan.h>
+#include <linux/of_net.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+
+#include "mtk_eth_soc.h"
+#include "gsw_mt7620.h"
+#include "mdio.h"
+
+#define MT7620_CDMA_CSG_CFG 0x400
+#define MT7621_CDMP_IG_CTRL (MT7620_CDMA_CSG_CFG + 0x00)
+#define MT7621_CDMP_EG_CTRL (MT7620_CDMA_CSG_CFG + 0x04)
+#define MT7621_RESET_FE BIT(6)
+#define MT7621_L4_VALID BIT(24)
+
+#define MT7621_TX_DMA_UDF BIT(19)
+
+#define CDMA_ICS_EN BIT(2)
+#define CDMA_UCS_EN BIT(1)
+#define CDMA_TCS_EN BIT(0)
+
+#define GDMA_ICS_EN BIT(22)
+#define GDMA_TCS_EN BIT(21)
+#define GDMA_UCS_EN BIT(20)
+
+/* frame engine counters */
+#define MT7621_REG_MIB_OFFSET 0x2000
+#define MT7621_PPE_AC_BCNT0 (MT7621_REG_MIB_OFFSET + 0x00)
+#define MT7621_GDM1_TX_GBCNT (MT7621_REG_MIB_OFFSET + 0x400)
+#define MT7621_GDM2_TX_GBCNT (MT7621_GDM1_TX_GBCNT + 0x40)
+
+#define GSW_REG_GDMA1_MAC_ADRL 0x508
+#define GSW_REG_GDMA1_MAC_ADRH 0x50C
+#define GSW_REG_GDMA2_MAC_ADRL 0x1508
+#define GSW_REG_GDMA2_MAC_ADRH 0x150C
+
+
+#define MT7621_MTK_RST_GL 0x04
+#define MT7620_MTK_INT_STATUS2 0x08
+
+/* MTK_INT_STATUS reg on mt7620 define CNT_GDM1_AF at BIT(29)
+ * but after test it should be BIT(13).
+ */
+#define MT7621_MTK_GDM1_AF BIT(28)
+#define MT7621_MTK_GDM2_AF BIT(29)
+
+static const u16 mt7621_reg_table[MTK_REG_COUNT] = {
+ [MTK_REG_PDMA_GLO_CFG] = RT5350_PDMA_GLO_CFG,
+ [MTK_REG_PDMA_RST_CFG] = RT5350_PDMA_RST_CFG,
+ [MTK_REG_DLY_INT_CFG] = RT5350_DLY_INT_CFG,
+ [MTK_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0,
+ [MTK_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0,
+ [MTK_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0,
+ [MTK_REG_TX_DTX_IDX0] = RT5350_TX_DTX_IDX0,
+ [MTK_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0,
+ [MTK_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0,
+ [MTK_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0,
+ [MTK_REG_RX_DRX_IDX0] = RT5350_RX_DRX_IDX0,
+ [MTK_REG_MTK_INT_ENABLE] = RT5350_MTK_INT_ENABLE,
+ [MTK_REG_MTK_INT_STATUS] = RT5350_MTK_INT_STATUS,
+ [MTK_REG_MTK_DMA_VID_BASE] = 0,
+ [MTK_REG_MTK_COUNTER_BASE] = MT7621_GDM1_TX_GBCNT,
+ [MTK_REG_MTK_RST_GL] = MT7621_MTK_RST_GL,
+ [MTK_REG_MTK_INT_STATUS2] = MT7620_MTK_INT_STATUS2,
+};
+
+static void mt7621_mtk_reset(struct mtk_eth *eth)
+{
+ mtk_reset(eth, MT7621_RESET_FE);
+}
+
+static int mt7621_fwd_config(struct mtk_eth *eth)
+{
+ /* Setup GMAC1 only, there is no support for GMAC2 yet */
+ mtk_w32(eth, mtk_r32(eth, MT7620_GDMA1_FWD_CFG) & ~0xffff,
+ MT7620_GDMA1_FWD_CFG);
+
+ /* Enable RX checksum */
+ mtk_w32(eth, mtk_r32(eth, MT7620_GDMA1_FWD_CFG) | (GDMA_ICS_EN |
+ GDMA_TCS_EN | GDMA_UCS_EN),
+ MT7620_GDMA1_FWD_CFG);
+
+ /* Enable RX VLan Offloading */
+ mtk_w32(eth, 0, MT7621_CDMP_EG_CTRL);
+
+ return 0;
+}
+
+static void mt7621_set_mac(struct mtk_mac *mac, unsigned char *hwaddr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->hw->page_lock, flags);
+ if (mac->id == 0) {
+ mtk_w32(mac->hw, (hwaddr[0] << 8) | hwaddr[1], GSW_REG_GDMA1_MAC_ADRH);
+ mtk_w32(mac->hw, (hwaddr[2] << 24) | (hwaddr[3] << 16) |
+ (hwaddr[4] << 8) | hwaddr[5],
+ GSW_REG_GDMA1_MAC_ADRL);
+ }
+ if (mac->id == 1) {
+ mtk_w32(mac->hw, (hwaddr[0] << 8) | hwaddr[1], GSW_REG_GDMA2_MAC_ADRH);
+ mtk_w32(mac->hw, (hwaddr[2] << 24) | (hwaddr[3] << 16) |
+ (hwaddr[4] << 8) | hwaddr[5],
+ GSW_REG_GDMA2_MAC_ADRL);
+ }
+ spin_unlock_irqrestore(&mac->hw->page_lock, flags);
+}
+
+static struct mtk_soc_data mt7621_data = {
+ .hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM |
+ NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
+ NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 |
+ NETIF_F_IPV6_CSUM,
+ .dma_type = MTK_PDMA,
+ .dma_ring_size = 256,
+ .napi_weight = 64,
+ .new_stats = 1,
+ .padding_64b = 1,
+ .rx_2b_offset = 1,
+ .rx_sg_dma = 1,
+ .has_switch = 1,
+ .mac_count = 2,
+ .reset_fe = mt7621_mtk_reset,
+ .set_mac = mt7621_set_mac,
+ .fwd_config = mt7621_fwd_config,
+ .switch_init = mtk_gsw_init,
+ .reg_table = mt7621_reg_table,
+ .pdma_glo_cfg = MTK_PDMA_SIZE_16DWORDS,
+ .rx_int = RT5350_RX_DONE_INT,
+ .tx_int = RT5350_TX_DONE_INT,
+ .status_int = MT7621_MTK_GDM1_AF | MT7621_MTK_GDM2_AF,
+ .checksum_bit = MT7621_L4_VALID,
+ .has_carrier = mt7620_has_carrier,
+ .mdio_read = mt7620_mdio_read,
+ .mdio_write = mt7620_mdio_write,
+ .mdio_adjust_link = mt7620_mdio_link_adjust,
+};
+
+const struct of_device_id of_mtk_match[] = {
+ { .compatible = "mediatek,mt7621-eth", .data = &mt7621_data },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_mtk_match);
From: John Crispin <[email protected]>
This patch adds the Makefile and Kconfig required to make the driver build.
Signed-off-by: John Crispin <[email protected]>
Signed-off-by: Felix Fietkau <[email protected]>
Signed-off-by: Michael Lee <[email protected]>
Signed-off-by: NeilBrown <[email protected]>
---
drivers/staging/Kconfig | 2 ++
drivers/staging/Makefile | 1 +
drivers/staging/mt7621-eth/Kconfig | 39 +++++++++++++++++++++++++++++++++++
drivers/staging/mt7621-eth/Makefile | 14 +++++++++++++
4 files changed, 56 insertions(+)
create mode 100644 drivers/staging/mt7621-eth/Kconfig
create mode 100644 drivers/staging/mt7621-eth/Makefile
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 4c872bf7c280..d9cddad71da2 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -132,4 +132,6 @@ source "drivers/staging/mt7621-dma/Kconfig"
source "drivers/staging/mt7621-mmc/Kconfig"
+source "drivers/staging/mt7621-eth/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 8ce10da1ab1d..2a7defcd6836 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -58,3 +58,4 @@ obj-$(CONFIG_SOC_MT7621) += mt7621-gpio/
obj-$(CONFIG_SOC_MT7621) += mt7621-spi/
obj-$(CONFIG_SOC_MT7621) += mt7621-dma/
obj-$(CONFIG_SOC_MT7621) += mt7621-mmc/
+obj-$(CONFIG_SOC_MT7621) += mt7621-eth/
diff --git a/drivers/staging/mt7621-eth/Kconfig b/drivers/staging/mt7621-eth/Kconfig
new file mode 100644
index 000000000000..44ea86c7a96c
--- /dev/null
+++ b/drivers/staging/mt7621-eth/Kconfig
@@ -0,0 +1,39 @@
+config NET_VENDOR_MEDIATEK_STAGING
+ bool "MediaTek ethernet driver - staging version"
+ depends on RALINK
+ ---help---
+ If you have an MT7621 Mediatek SoC with ethernet, say Y.
+
+if NET_VENDOR_MEDIATEK_STAGING
+choice
+ prompt "MAC type"
+
+config NET_MEDIATEK_MT7621
+ bool "MT7621"
+ depends on MIPS && SOC_MT7621
+
+endchoice
+
+config NET_MEDIATEK_SOC_STAGING
+ tristate "MediaTek SoC Gigabit Ethernet support"
+ depends on NET_VENDOR_MEDIATEK_STAGING
+ select PHYLIB
+ ---help---
+ This driver supports the gigabit ethernet MACs in the
+ MediaTek SoC family.
+
+config NET_MEDIATEK_MDIO
+ def_bool NET_MEDIATEK_SOC_STAGING
+ depends on NET_MEDIATEK_MT7621
+ select PHYLIB
+
+config NET_MEDIATEK_MDIO_MT7620
+ def_bool NET_MEDIATEK_SOC_STAGING
+ depends on NET_MEDIATEK_MT7621
+ select NET_MEDIATEK_MDIO
+
+config NET_MEDIATEK_GSW_MT7621
+ def_tristate NET_MEDIATEK_SOC_STAGING
+ depends on NET_MEDIATEK_MT7621
+
+endif #NET_VENDOR_MEDIATEK_STAGING
diff --git a/drivers/staging/mt7621-eth/Makefile b/drivers/staging/mt7621-eth/Makefile
new file mode 100644
index 000000000000..018bcc3596b3
--- /dev/null
+++ b/drivers/staging/mt7621-eth/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the Ralink SoCs built-in ethernet macs
+#
+
+mtk-eth-soc-y += mtk_eth_soc.o ethtool.o
+
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MDIO) += mdio.o
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MDIO_MT7620) += mdio_mt7620.o
+
+mtk-eth-soc-$(CONFIG_NET_MEDIATEK_MT7621) += soc_mt7621.o
+
+obj-$(CONFIG_NET_MEDIATEK_GSW_MT7621) += gsw_mt7621.o
+
+obj-$(CONFIG_NET_MEDIATEK_SOC_STAGING) += mtk-eth-soc.o
Add device tree source for mt7621 and gnubee1 to
make testing easier.
Signed-off-by: NeilBrown <[email protected]>
---
drivers/staging/Kconfig | 2
drivers/staging/Makefile | 1
drivers/staging/mt7621-dts/Kconfig | 5
drivers/staging/mt7621-dts/Makefile | 3
drivers/staging/mt7621-dts/TODO | 5
drivers/staging/mt7621-dts/gbpc1.dts | 143 ++++++++++
drivers/staging/mt7621-dts/mt7621.dtsi | 471 ++++++++++++++++++++++++++++++++
7 files changed, 630 insertions(+)
create mode 100644 drivers/staging/mt7621-dts/Kconfig
create mode 100644 drivers/staging/mt7621-dts/Makefile
create mode 100644 drivers/staging/mt7621-dts/TODO
create mode 100644 drivers/staging/mt7621-dts/gbpc1.dts
create mode 100644 drivers/staging/mt7621-dts/mt7621.dtsi
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index d9cddad71da2..6dac0c1fecf4 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -134,4 +134,6 @@ source "drivers/staging/mt7621-mmc/Kconfig"
source "drivers/staging/mt7621-eth/Kconfig"
+source "drivers/staging/mt7621-dts/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 2a7defcd6836..2634228aeaa2 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -59,3 +59,4 @@ obj-$(CONFIG_SOC_MT7621) += mt7621-spi/
obj-$(CONFIG_SOC_MT7621) += mt7621-dma/
obj-$(CONFIG_SOC_MT7621) += mt7621-mmc/
obj-$(CONFIG_SOC_MT7621) += mt7621-eth/
+obj-$(CONFIG_SOC_MT7621) += mt7621-dts/
diff --git a/drivers/staging/mt7621-dts/Kconfig b/drivers/staging/mt7621-dts/Kconfig
new file mode 100644
index 000000000000..94a9e16c0b92
--- /dev/null
+++ b/drivers/staging/mt7621-dts/Kconfig
@@ -0,0 +1,5 @@
+config DTB_GNUBEE1
+ bool "GnuBee1 NAS"
+ depends on SOC_MT7621 && DTB_RT_NONE
+ select BUILTIN_DTB
+
diff --git a/drivers/staging/mt7621-dts/Makefile b/drivers/staging/mt7621-dts/Makefile
new file mode 100644
index 000000000000..195eba4a5c65
--- /dev/null
+++ b/drivers/staging/mt7621-dts/Makefile
@@ -0,0 +1,3 @@
+dtb-$(CONFIG_DTB_GNUBEE1) += gbpc1.dtb
+
+obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y))
diff --git a/drivers/staging/mt7621-dts/TODO b/drivers/staging/mt7621-dts/TODO
new file mode 100644
index 000000000000..15803132c1ea
--- /dev/null
+++ b/drivers/staging/mt7621-dts/TODO
@@ -0,0 +1,5 @@
+
+- ensure all usage matches code
+- ensure all features used are documented
+
+Cc: NeilBrown <[email protected]>
\ No newline at end of file
diff --git a/drivers/staging/mt7621-dts/gbpc1.dts b/drivers/staging/mt7621-dts/gbpc1.dts
new file mode 100644
index 000000000000..515c7cbdd15e
--- /dev/null
+++ b/drivers/staging/mt7621-dts/gbpc1.dts
@@ -0,0 +1,143 @@
+/dts-v1/;
+
+#include "mt7621.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+ compatible = "gnubee,gb-pc1", "mediatek,mt7621-soc";
+ model = "GB-PC1";
+
+ memory@0 {
+ device_type = "memory";
+ reg = <0x0 0x1c000000>, <0x20000000 0x4000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttyS0,57600";
+ };
+
+ palmbus: palmbus@1E000000 {
+ i2c@900 {
+ status = "okay";
+ };
+ };
+
+ gpio-keys-polled {
+ compatible = "gpio-keys-polled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ poll-interval = <20>;
+
+ reset {
+ label = "reset";
+ gpios = <&gpio0 18 GPIO_ACTIVE_HIGH>;
+ linux,code = <KEY_RESTART>;
+ };
+ };
+
+ gpio-leds {
+ compatible = "gpio-leds";
+
+ system {
+ label = "gb-pc1:green:system";
+ gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
+ };
+
+ status {
+ label = "gb-pc1:green:status";
+ gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
+ };
+
+ lan1 {
+ label = "gb-pc1:green:lan1";
+ gpios = <&gpio0 24 GPIO_ACTIVE_LOW>;
+ };
+
+ lan2 {
+ label = "gb-pc1:green:lan2";
+ gpios = <&gpio0 25 GPIO_ACTIVE_LOW>;
+ };
+ };
+};
+
+&sdhci {
+ status = "okay";
+};
+
+&spi0 {
+ status = "okay";
+
+ m25p80@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "jedec,spi-nor";
+ reg = <0>;
+ spi-max-frequency = <10000000>;
+ m25p,chunked-io = <32>;
+
+ partition@0 {
+ label = "u-boot";
+ reg = <0x0 0x30000>;
+ read-only;
+ };
+
+ partition@30000 {
+ label = "u-boot-env";
+ reg = <0x30000 0x10000>;
+ read-only;
+ };
+
+ factory: partition@40000 {
+ label = "factory";
+ reg = <0x40000 0x10000>;
+ read-only;
+ };
+
+ partition@50000 {
+ label = "firmware";
+ reg = <0x50000 0xFB0000>;
+ };
+
+ };
+};
+
+&sysclock {
+ compatible = "fixed-clock";
+ clock-frequency = <90000000>;
+};
+
+&cpuclock {
+ compatible = "fixed-clock";
+ clock-frequency = <900000000>;
+};
+
+&pcie {
+ status = "okay";
+};
+
+ðernet {
+ //mtd-mac-address = <&factory 0xe000>;
+ gmac1: mac@0 {
+ compatible = "mediatek,eth-mac";
+ reg = <0>;
+ phy-handle = <&phy1>;
+ };
+
+ mdio-bus {
+ phy1: ethernet-phy@1 {
+ reg = <1>;
+ phy-mode = "rgmii";
+ };
+ };
+};
+
+&pinctrl {
+ state_default: pinctrl0 {
+ gpio {
+ ralink,group = "wdt", "rgmii2", "uart3";
+ ralink,function = "gpio";
+ };
+ };
+};
diff --git a/drivers/staging/mt7621-dts/mt7621.dtsi b/drivers/staging/mt7621-dts/mt7621.dtsi
new file mode 100644
index 000000000000..a6b17ea9ade8
--- /dev/null
+++ b/drivers/staging/mt7621-dts/mt7621.dtsi
@@ -0,0 +1,471 @@
+#include <dt-bindings/interrupt-controller/mips-gic.h>
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "mediatek,mt7621-soc";
+
+ cpus {
+ cpu@0 {
+ compatible = "mips,mips1004Kc";
+ };
+
+ cpu@1 {
+ compatible = "mips,mips1004Kc";
+ };
+ };
+
+ cpuintc: cpuintc@0 {
+ #address-cells = <0>;
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ compatible = "mti,cpu-interrupt-controller";
+ };
+
+ aliases {
+ serial0 = &uartlite;
+ };
+
+ cpuclock: cpuclock@0 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+
+ /* FIXME: there should be way to detect this */
+ clock-frequency = <880000000>;
+ };
+
+ sysclock: sysclock@0 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+
+ /* FIXME: there should be way to detect this */
+ clock-frequency = <50000000>;
+ };
+
+ palmbus: palmbus@1E000000 {
+ compatible = "palmbus";
+ reg = <0x1E000000 0x100000>;
+ ranges = <0x0 0x1E000000 0x0FFFFF>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ sysc: sysc@0 {
+ compatible = "mtk,mt7621-sysc";
+ reg = <0x0 0x100>;
+ };
+
+ wdt: wdt@100 {
+ compatible = "mtk,mt7621-wdt";
+ reg = <0x100 0x100>;
+ };
+
+ gpio@600 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ compatible = "mtk,mt7621-gpio";
+ reg = <0x600 0x100>;
+
+ gpio0: bank@0 {
+ reg = <0>;
+ compatible = "mtk,mt7621-gpio-bank";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpio1: bank@1 {
+ reg = <1>;
+ compatible = "mtk,mt7621-gpio-bank";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+
+ gpio2: bank@2 {
+ reg = <2>;
+ compatible = "mtk,mt7621-gpio-bank";
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ };
+
+ i2c: i2c@900 {
+ compatible = "mediatek,mt7621-i2c";
+ reg = <0x900 0x100>;
+
+ clocks = <&sysclock>;
+
+ resets = <&rstctrl 16>;
+ reset-names = "i2c";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ status = "disabled";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c_pins>;
+ };
+
+ i2s: i2s@a00 {
+ compatible = "mediatek,mt7621-i2s";
+ reg = <0xa00 0x100>;
+
+ clocks = <&sysclock>;
+
+ resets = <&rstctrl 17>;
+ reset-names = "i2s";
+
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 16 IRQ_TYPE_LEVEL_HIGH>;
+
+ txdma-req = <2>;
+ rxdma-req = <3>;
+
+ dmas = <&gdma 4>,
+ <&gdma 6>;
+ dma-names = "tx", "rx";
+
+ status = "disabled";
+ };
+
+ memc: memc@5000 {
+ compatible = "mtk,mt7621-memc";
+ reg = <0x300 0x100>;
+ };
+
+ cpc: cpc@1fbf0000 {
+ compatible = "mtk,mt7621-cpc";
+ reg = <0x1fbf0000 0x8000>;
+ };
+
+ mc: mc@1fbf8000 {
+ compatible = "mtk,mt7621-mc";
+ reg = <0x1fbf8000 0x8000>;
+ };
+
+ uartlite: uartlite@c00 {
+ compatible = "ns16550a";
+ reg = <0xc00 0x100>;
+
+ clocks = <&sysclock>;
+ clock-frequency = <50000000>;
+
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 26 IRQ_TYPE_LEVEL_HIGH>;
+
+ reg-shift = <2>;
+ reg-io-width = <4>;
+ no-loopback-test;
+ };
+
+ spi0: spi@b00 {
+ status = "disabled";
+
+ compatible = "ralink,mt7621-spi";
+ reg = <0xb00 0x100>;
+
+ clocks = <&sysclock>;
+
+ resets = <&rstctrl 18>;
+ reset-names = "spi";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi_pins>;
+ };
+
+ gdma: gdma@2800 {
+ compatible = "ralink,rt3883-gdma";
+ reg = <0x2800 0x800>;
+
+ resets = <&rstctrl 14>;
+ reset-names = "dma";
+
+ interrupt-parent = <&gic>;
+ interrupts = <0 13 4>;
+
+ #dma-cells = <1>;
+ #dma-channels = <16>;
+ #dma-requests = <16>;
+
+ status = "disabled";
+ };
+
+ hsdma: hsdma@7000 {
+ compatible = "mediatek,mt7621-hsdma";
+ reg = <0x7000 0x1000>;
+
+ resets = <&rstctrl 5>;
+ reset-names = "hsdma";
+
+ interrupt-parent = <&gic>;
+ interrupts = <0 11 4>;
+
+ #dma-cells = <1>;
+ #dma-channels = <1>;
+ #dma-requests = <1>;
+
+ status = "disabled";
+ };
+ };
+
+ pinctrl: pinctrl {
+ compatible = "ralink,rt2880-pinmux";
+ pinctrl-names = "default";
+ pinctrl-0 = <&state_default>;
+
+ state_default: pinctrl0 {
+ };
+
+ i2c_pins: i2c {
+ i2c {
+ ralink,group = "i2c";
+ ralink,function = "i2c";
+ };
+ };
+
+ spi_pins: spi {
+ spi {
+ ralink,group = "spi";
+ ralink,function = "spi";
+ };
+ };
+
+ uart1_pins: uart1 {
+ uart1 {
+ ralink,group = "uart1";
+ ralink,function = "uart1";
+ };
+ };
+
+ uart2_pins: uart2 {
+ uart2 {
+ ralink,group = "uart2";
+ ralink,function = "uart2";
+ };
+ };
+
+ uart3_pins: uart3 {
+ uart3 {
+ ralink,group = "uart3";
+ ralink,function = "uart3";
+ };
+ };
+
+ rgmii1_pins: rgmii1 {
+ rgmii1 {
+ ralink,group = "rgmii1";
+ ralink,function = "rgmii1";
+ };
+ };
+
+ rgmii2_pins: rgmii2 {
+ rgmii2 {
+ ralink,group = "rgmii2";
+ ralink,function = "rgmii2";
+ };
+ };
+
+ mdio_pins: mdio {
+ mdio {
+ ralink,group = "mdio";
+ ralink,function = "mdio";
+ };
+ };
+
+ pcie_pins: pcie {
+ pcie {
+ ralink,group = "pcie";
+ ralink,function = "pcie rst";
+ };
+ };
+
+ nand_pins: nand {
+ spi-nand {
+ ralink,group = "spi";
+ ralink,function = "nand1";
+ };
+
+ sdhci-nand {
+ ralink,group = "sdhci";
+ ralink,function = "nand2";
+ };
+ };
+
+ sdhci_pins: sdhci {
+ sdhci {
+ ralink,group = "sdhci";
+ ralink,function = "sdhci";
+ };
+ };
+ };
+
+ rstctrl: rstctrl {
+ compatible = "ralink,rt2880-reset";
+ #reset-cells = <1>;
+ };
+
+ clkctrl: clkctrl {
+ compatible = "ralink,rt2880-clock";
+ #clock-cells = <1>;
+ };
+
+ sdhci: sdhci@1E130000 {
+ status = "disabled";
+
+ compatible = "ralink,mt7620-sdhci";
+ reg = <0x1E130000 0x4000>;
+
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 20 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ xhci: xhci@1E1C0000 {
+ status = "okay";
+
+ compatible = "mediatek,mt8173-xhci";
+ reg = <0x1e1c0000 0x1000
+ 0x1e1d0700 0x0100>;
+ reg-names = "mac", "ippc";
+
+ clocks = <&sysclock>;
+ clock-names = "sys_ck";
+
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 22 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ gic: interrupt-controller@1fbc0000 {
+ compatible = "mti,gic";
+ reg = <0x1fbc0000 0x2000>;
+
+ interrupt-controller;
+ #interrupt-cells = <3>;
+
+ mti,reserved-cpu-vectors = <7>;
+
+ timer {
+ compatible = "mti,gic-timer";
+ interrupts = <GIC_LOCAL 1 IRQ_TYPE_NONE>;
+ clocks = <&cpuclock>;
+ };
+ };
+
+ nand: nand@1e003000 {
+ status = "disabled";
+
+ compatible = "mtk,mt7621-nand";
+ bank-width = <2>;
+ reg = <0x1e003000 0x800
+ 0x1e003800 0x800>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ };
+
+ ethsys: syscon@1e000000 {
+ compatible = "mediatek,mt7621-ethsys",
+ "syscon";
+ reg = <0x1e000000 0x1000>;
+ #clock-cells = <1>;
+ };
+
+ ethernet: ethernet@1e100000 {
+ compatible = "mediatek,mt7621-eth";
+ reg = <0x1e100000 0x10000>;
+
+ clocks = <&sysclock>;
+ clock-names = "ethif";
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ resets = <&rstctrl 6 &rstctrl 23>;
+ reset-names = "fe", "eth";
+
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 3 IRQ_TYPE_LEVEL_HIGH>;
+
+ mediatek,ethsys = <ðsys>;
+
+ mediatek,switch = <&gsw>;
+
+ mdio-bus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ phy1f: ethernet-phy@1f {
+ reg = <0x1f>;
+ phy-mode = "rgmii";
+ };
+ };
+ };
+
+ gsw: gsw@1e110000 {
+ compatible = "mediatek,mt7621-gsw";
+ reg = <0x1e110000 0x8000>;
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 23 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ pcie: pcie@1e140000 {
+ compatible = "mediatek,mt7621-pci";
+ reg = <0x1e140000 0x100
+ 0x1e142000 0x100>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&pcie_pins>;
+
+ device_type = "pci";
+
+ bus-range = <0 255>;
+ ranges = <
+ 0x02000000 0 0x00000000 0x60000000 0 0x10000000 /* pci memory */
+ 0x01000000 0 0x00000000 0x1e160000 0 0x00010000 /* io space */
+ >;
+
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SHARED 4 IRQ_TYPE_LEVEL_HIGH
+ GIC_SHARED 24 IRQ_TYPE_LEVEL_HIGH
+ GIC_SHARED 25 IRQ_TYPE_LEVEL_HIGH>;
+
+ status = "disabled";
+
+ resets = <&rstctrl 24 &rstctrl 25 &rstctrl 26>;
+ reset-names = "pcie0", "pcie1", "pcie2";
+ clocks = <&clkctrl 24 &clkctrl 25 &clkctrl 26>;
+ clock-names = "pcie0", "pcie1", "pcie2";
+
+ pcie0 {
+ reg = <0x0000 0 0 0 0>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ device_type = "pci";
+ };
+
+ pcie1 {
+ reg = <0x0800 0 0 0 0>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ device_type = "pci";
+ };
+
+ pcie2 {
+ reg = <0x1000 0 0 0 0>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ device_type = "pci";
+ };
+ };
+};
From: John Crispin <[email protected]>
Add possible dt binding for mediatek gigabit switches.
Signed-off-by: John Crispin <[email protected]>
Signed-off-by: Felix Fietkau <[email protected]>
Signed-off-by: Michael Lee <[email protected]>
Cc: [email protected]
Signed-off-by: NeilBrown <[email protected]>
---
.../devicetree/bindings/net/mediatek-net-gsw.txt | 48 ++++++++++++++++++++
drivers/staging/mt7621-eth/TODO | 4 ++
2 files changed, 52 insertions(+)
create mode 100644 drivers/staging/mt7621-eth/Documentation/devicetree/bindings/net/mediatek-net-gsw.txt
create mode 100644 drivers/staging/mt7621-eth/TODO
diff --git a/drivers/staging/mt7621-eth/Documentation/devicetree/bindings/net/mediatek-net-gsw.txt b/drivers/staging/mt7621-eth/Documentation/devicetree/bindings/net/mediatek-net-gsw.txt
new file mode 100644
index 000000000000..596b38552697
--- /dev/null
+++ b/drivers/staging/mt7621-eth/Documentation/devicetree/bindings/net/mediatek-net-gsw.txt
@@ -0,0 +1,48 @@
+Mediatek Gigabit Switch
+=======================
+
+The mediatek gigabit switch can be found on Mediatek SoCs.
+
+Required properties:
+- compatible: Should be "mediatek,mt7620-gsw", "mediatek,mt7621-gsw",
+ "mediatek,mt7623-gsw"
+- reg: Address and length of the register set for the device
+- interrupts: Should contain the gigabit switches interrupt
+
+
+Additional required properties for ARM based SoCs:
+- mediatek,reset-pin: phandle describing the reset GPIO
+- clocks: the clocks used by the switch
+- clock-names: the names of the clocks listed in the clocks property
+ these should be "trgpll", "esw", "gp2", "gp1"
+- mt7530-supply: the phandle of the regulator used to power the switch
+- mediatek,pctl-regmap: phandle to the port control regmap. this is used to
+ setup the drive current
+
+
+Optional properties:
+- interrupt-parent: Should be the phandle for the interrupt controller
+ that services interrupts for this device
+
+Example:
+
+gsw: switch@1b100000 {
+ compatible = "mediatek,mt7623-gsw";
+ reg = <0 0x1b110000 0 0x300000>;
+
+ interrupt-parent = <&pio>;
+ interrupts = <168 IRQ_TYPE_EDGE_RISING>;
+
+ clocks = <&apmixedsys CLK_APMIXED_TRGPLL>,
+ <ðsys CLK_ETHSYS_ESW>,
+ <ðsys CLK_ETHSYS_GP2>,
+ <ðsys CLK_ETHSYS_GP1>;
+ clock-names = "trgpll", "esw", "gp2", "gp1";
+
+ mt7530-supply = <&mt6323_vpa_reg>;
+
+ mediatek,pctl-regmap = <&syscfg_pctl_a>;
+ mediatek,reset-pin = <&pio 15 0>;
+
+ status = "okay";
+};
diff --git a/drivers/staging/mt7621-eth/TODO b/drivers/staging/mt7621-eth/TODO
new file mode 100644
index 000000000000..5f269af0db5c
--- /dev/null
+++ b/drivers/staging/mt7621-eth/TODO
@@ -0,0 +1,4 @@
+
+- verify devicetree documentation is consistent with code
+
+Cc: NeilBrown <[email protected]>
From: John Crispin <[email protected]>
The GSW is found in all of the 1000mbit SoCs. it has 5 external ports,
1-2 cpu ports and 1 further port that the internal HW offloading engine
connects to. The switch core used is a MT7530, which also exists as a
standalone chip. Although these SoCs (mt7620/1/3) share the same switch
core, the bring up of these is slightly different. One of the reasons is
that on mt7620 the switch core is mmio mapped while MT7621/3 talks to the
switch via MDIO addr 0x1f. Additionally, the SoCs have different MAC types
and some of them have TRGMII support. MT7621 can do 1,2gbit and MT7623 is
able to do 2,6gbit. The support for the TRGMII bring up is not part of this
series as the code is based on the SDK driver and has between 1500 and 2000
magic values that still need to be converted to defines.
Because of these differences we have 3 separate drivers for these 3 SoCs.
These drivers are very basic and only provides basic init and irq support.
The SoC and switch core both have support for a special tag making DSA
support possible.
NeilBrown:
- added setting to mt7621_hw_init to match working code from libreCMC
This needs to be converted to use switchdev.
Signed-off-by: John Crispin <[email protected]>
Signed-off-by: NeilBrown <[email protected]>
---
drivers/staging/mt7621-eth/TODO | 1
drivers/staging/mt7621-eth/gsw_mt7620.h | 277 +++++++++++++++++++++++++++++
drivers/staging/mt7621-eth/gsw_mt7621.c | 298 +++++++++++++++++++++++++++++++
3 files changed, 576 insertions(+)
create mode 100644 drivers/staging/mt7621-eth/gsw_mt7620.h
create mode 100644 drivers/staging/mt7621-eth/gsw_mt7621.c
diff --git a/drivers/staging/mt7621-eth/TODO b/drivers/staging/mt7621-eth/TODO
index 25c550e8df8c..1ab0530131ae 100644
--- a/drivers/staging/mt7621-eth/TODO
+++ b/drivers/staging/mt7621-eth/TODO
@@ -3,5 +3,6 @@
- fix ethtool - currently doesn't return valid data.
- general code review and clean up
- add support for second MAC on mt7621
+- convert gsw code to use switchdev interfaces
Cc: NeilBrown <[email protected]>
diff --git a/drivers/staging/mt7621-eth/gsw_mt7620.h b/drivers/staging/mt7621-eth/gsw_mt7620.h
new file mode 100644
index 000000000000..1766939e2101
--- /dev/null
+++ b/drivers/staging/mt7621-eth/gsw_mt7620.h
@@ -0,0 +1,277 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <[email protected]>
+ * Copyright (C) 2009-2016 Felix Fietkau <[email protected]>
+ * Copyright (C) 2013-2016 Michael Lee <[email protected]>
+ */
+
+#ifndef _RALINK_GSW_MT7620_H__
+#define _RALINK_GSW_MT7620_H__
+
+#define GSW_REG_PHY_TIMEOUT (5 * HZ)
+
+#define MT7620_GSW_REG_PIAC 0x0004
+
+#define GSW_NUM_VLANS 16
+#define GSW_NUM_VIDS 4096
+#define GSW_NUM_PORTS 7
+#define GSW_PORT6 6
+
+#define GSW_MDIO_ACCESS BIT(31)
+#define GSW_MDIO_READ BIT(19)
+#define GSW_MDIO_WRITE BIT(18)
+#define GSW_MDIO_START BIT(16)
+#define GSW_MDIO_ADDR_SHIFT 20
+#define GSW_MDIO_REG_SHIFT 25
+
+#define GSW_REG_PORT_PMCR(x) (0x3000 + (x * 0x100))
+#define GSW_REG_PORT_STATUS(x) (0x3008 + (x * 0x100))
+#define GSW_REG_SMACCR0 0x3fE4
+#define GSW_REG_SMACCR1 0x3fE8
+#define GSW_REG_CKGCR 0x3ff0
+
+#define GSW_REG_IMR 0x7008
+#define GSW_REG_ISR 0x700c
+#define GSW_REG_GPC1 0x7014
+
+#define SYSC_REG_CHIP_REV_ID 0x0c
+#define SYSC_REG_CFG 0x10
+#define SYSC_REG_CFG1 0x14
+#define RST_CTRL_MCM BIT(2)
+#define SYSC_PAD_RGMII2_MDIO 0x58
+#define SYSC_GPIO_MODE 0x60
+
+#define PORT_IRQ_ST_CHG 0x7f
+
+#define MT7621_ESW_PHY_POLLING 0x0000
+#define MT7620_ESW_PHY_POLLING 0x7000
+
+#define PMCR_IPG BIT(18)
+#define PMCR_MAC_MODE BIT(16)
+#define PMCR_FORCE BIT(15)
+#define PMCR_TX_EN BIT(14)
+#define PMCR_RX_EN BIT(13)
+#define PMCR_BACKOFF BIT(9)
+#define PMCR_BACKPRES BIT(8)
+#define PMCR_RX_FC BIT(5)
+#define PMCR_TX_FC BIT(4)
+#define PMCR_SPEED(_x) (_x << 2)
+#define PMCR_DUPLEX BIT(1)
+#define PMCR_LINK BIT(0)
+
+#define PHY_AN_EN BIT(31)
+#define PHY_PRE_EN BIT(30)
+#define PMY_MDC_CONF(_x) ((_x & 0x3f) << 24)
+
+/* ethernet subsystem config register */
+#define ETHSYS_SYSCFG0 0x14
+/* ethernet subsystem clock register */
+#define ETHSYS_CLKCFG0 0x2c
+#define ETHSYS_TRGMII_CLK_SEL362_5 BIT(11)
+
+/* p5 RGMII wrapper TX clock control register */
+#define MT7530_P5RGMIITXCR 0x7b04
+/* p5 RGMII wrapper RX clock control register */
+#define MT7530_P5RGMIIRXCR 0x7b00
+/* TRGMII TDX ODT registers */
+#define MT7530_TRGMII_TD0_ODT 0x7a54
+#define MT7530_TRGMII_TD1_ODT 0x7a5c
+#define MT7530_TRGMII_TD2_ODT 0x7a64
+#define MT7530_TRGMII_TD3_ODT 0x7a6c
+#define MT7530_TRGMII_TD4_ODT 0x7a74
+#define MT7530_TRGMII_TD5_ODT 0x7a7c
+/* TRGMII TCK ctrl register */
+#define MT7530_TRGMII_TCK_CTRL 0x7a78
+/* TRGMII Tx ctrl register */
+#define MT7530_TRGMII_TXCTRL 0x7a40
+/* port 6 extended control register */
+#define MT7530_P6ECR 0x7830
+/* IO driver control register */
+#define MT7530_IO_DRV_CR 0x7810
+/* top signal control register */
+#define MT7530_TOP_SIG_CTRL 0x7808
+/* modified hwtrap register */
+#define MT7530_MHWTRAP 0x7804
+/* hwtrap status register */
+#define MT7530_HWTRAP 0x7800
+/* status interrupt register */
+#define MT7530_SYS_INT_STS 0x700c
+/* system nterrupt register */
+#define MT7530_SYS_INT_EN 0x7008
+/* system control register */
+#define MT7530_SYS_CTRL 0x7000
+/* port MAC status register */
+#define MT7530_PMSR_P(x) (0x3008 + (x * 0x100))
+/* port MAC control register */
+#define MT7530_PMCR_P(x) (0x3000 + (x * 0x100))
+
+#define MT7621_XTAL_SHIFT 6
+#define MT7621_XTAL_MASK 0x7
+#define MT7621_XTAL_25 6
+#define MT7621_XTAL_40 3
+#define MT7621_MDIO_DRV_MASK (3 << 4)
+#define MT7621_GE1_MODE_MASK (3 << 12)
+
+#define TRGMII_TXCTRL_TXC_INV BIT(30)
+#define P6ECR_INTF_MODE_RGMII BIT(1)
+#define P5RGMIIRXCR_C_ALIGN BIT(8)
+#define P5RGMIIRXCR_DELAY_2 BIT(1)
+#define P5RGMIITXCR_DELAY_2 (BIT(8) | BIT(2))
+
+/* TOP_SIG_CTRL bits */
+#define TOP_SIG_CTRL_NORMAL (BIT(17) | BIT(16))
+
+/* MHWTRAP bits */
+#define MHWTRAP_MANUAL BIT(16)
+#define MHWTRAP_P5_MAC_SEL BIT(13)
+#define MHWTRAP_P6_DIS BIT(8)
+#define MHWTRAP_P5_RGMII_MODE BIT(7)
+#define MHWTRAP_P5_DIS BIT(6)
+#define MHWTRAP_PHY_ACCESS BIT(5)
+
+/* HWTRAP bits */
+#define HWTRAP_XTAL_SHIFT 9
+#define HWTRAP_XTAL_MASK 0x3
+
+/* SYS_CTRL bits */
+#define SYS_CTRL_SW_RST BIT(1)
+#define SYS_CTRL_REG_RST BIT(0)
+
+/* PMCR bits */
+#define PMCR_IFG_XMIT_96 BIT(18)
+#define PMCR_MAC_MODE BIT(16)
+#define PMCR_FORCE_MODE BIT(15)
+#define PMCR_TX_EN BIT(14)
+#define PMCR_RX_EN BIT(13)
+#define PMCR_BACK_PRES_EN BIT(9)
+#define PMCR_BACKOFF_EN BIT(8)
+#define PMCR_TX_FC_EN BIT(5)
+#define PMCR_RX_FC_EN BIT(4)
+#define PMCR_FORCE_SPEED_1000 BIT(3)
+#define PMCR_FORCE_FDX BIT(1)
+#define PMCR_FORCE_LNK BIT(0)
+#define PMCR_FIXED_LINK (PMCR_IFG_XMIT_96 | PMCR_MAC_MODE | \
+ PMCR_FORCE_MODE | PMCR_TX_EN | PMCR_RX_EN | \
+ PMCR_BACK_PRES_EN | PMCR_BACKOFF_EN | \
+ PMCR_FORCE_SPEED_1000 | PMCR_FORCE_FDX | \
+ PMCR_FORCE_LNK)
+
+#define PMCR_FIXED_LINK_FC (PMCR_FIXED_LINK | \
+ PMCR_TX_FC_EN | PMCR_RX_FC_EN)
+
+/* TRGMII control registers */
+#define GSW_INTF_MODE 0x390
+#define GSW_TRGMII_TD0_ODT 0x354
+#define GSW_TRGMII_TD1_ODT 0x35c
+#define GSW_TRGMII_TD2_ODT 0x364
+#define GSW_TRGMII_TD3_ODT 0x36c
+#define GSW_TRGMII_TXCTL_ODT 0x374
+#define GSW_TRGMII_TCK_ODT 0x37c
+#define GSW_TRGMII_RCK_CTRL 0x300
+
+#define INTF_MODE_TRGMII BIT(1)
+#define TRGMII_RCK_CTRL_RX_RST BIT(31)
+
+/* Mac control registers */
+#define MTK_MAC_P2_MCR 0x200
+#define MTK_MAC_P1_MCR 0x100
+
+#define MAC_MCR_MAX_RX_2K BIT(29)
+#define MAC_MCR_IPG_CFG (BIT(18) | BIT(16))
+#define MAC_MCR_FORCE_MODE BIT(15)
+#define MAC_MCR_TX_EN BIT(14)
+#define MAC_MCR_RX_EN BIT(13)
+#define MAC_MCR_BACKOFF_EN BIT(9)
+#define MAC_MCR_BACKPR_EN BIT(8)
+#define MAC_MCR_FORCE_RX_FC BIT(5)
+#define MAC_MCR_FORCE_TX_FC BIT(4)
+#define MAC_MCR_SPEED_1000 BIT(3)
+#define MAC_MCR_FORCE_DPX BIT(1)
+#define MAC_MCR_FORCE_LINK BIT(0)
+#define MAC_MCR_FIXED_LINK (MAC_MCR_MAX_RX_2K | MAC_MCR_IPG_CFG | \
+ MAC_MCR_FORCE_MODE | MAC_MCR_TX_EN | \
+ MAC_MCR_RX_EN | MAC_MCR_BACKOFF_EN | \
+ MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_RX_FC | \
+ MAC_MCR_FORCE_TX_FC | MAC_MCR_SPEED_1000 | \
+ MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_LINK)
+#define MAC_MCR_FIXED_LINK_FC (MAC_MCR_MAX_RX_2K | MAC_MCR_IPG_CFG | \
+ MAC_MCR_FIXED_LINK)
+
+/* possible XTAL speed */
+#define MT7623_XTAL_40 0
+#define MT7623_XTAL_20 1
+#define MT7623_XTAL_25 3
+
+/* GPIO port control registers */
+#define GPIO_OD33_CTRL8 0x4c0
+#define GPIO_BIAS_CTRL 0xed0
+#define GPIO_DRV_SEL10 0xf00
+
+/* on MT7620 the functio of port 4 can be software configured */
+enum {
+ PORT4_EPHY = 0,
+ PORT4_EXT,
+};
+
+/* struct mt7620_gsw - the structure that holds the SoC specific data
+ * @dev: The Device struct
+ * @base: The base address
+ * @piac_offset: The PIAC base may change depending on SoC
+ * @irq: The IRQ we are using
+ * @port4: The port4 mode on MT7620
+ * @autopoll: Is MDIO autopolling enabled
+ * @ethsys: The ethsys register map
+ * @pctl: The pin control register map
+ * @clk_gsw: The switch clock
+ * @clk_gp1: The gmac1 clock
+ * @clk_gp2: The gmac2 clock
+ * @clk_trgpll: The trgmii pll clock
+ */
+struct mt7620_gsw {
+ struct device *dev;
+ void __iomem *base;
+ u32 piac_offset;
+ int irq;
+ int port4;
+ unsigned long int autopoll;
+
+ struct regmap *ethsys;
+ struct regmap *pctl;
+
+ struct clk *clk_gsw;
+ struct clk *clk_gp1;
+ struct clk *clk_gp2;
+ struct clk *clk_trgpll;
+};
+
+/* switch register I/O wrappers */
+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg);
+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg);
+
+/* the callback used by the driver core to bringup the switch */
+int mtk_gsw_init(struct mtk_eth *eth);
+
+/* MDIO access wrappers */
+int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val);
+int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg);
+void mt7620_mdio_link_adjust(struct mtk_eth *eth, int port);
+int mt7620_has_carrier(struct mtk_eth *eth);
+void mt7620_print_link_state(struct mtk_eth *eth, int port, int link,
+ int speed, int duplex);
+void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val);
+u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg);
+void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg);
+
+u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
+ u32 phy_register, u32 write_data);
+u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg);
+void mt7620_handle_carrier(struct mtk_eth *eth);
+
+#endif
diff --git a/drivers/staging/mt7621-eth/gsw_mt7621.c b/drivers/staging/mt7621-eth/gsw_mt7621.c
new file mode 100644
index 000000000000..b49ee946e6bd
--- /dev/null
+++ b/drivers/staging/mt7621-eth/gsw_mt7621.c
@@ -0,0 +1,298 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <[email protected]>
+ * Copyright (C) 2009-2016 Felix Fietkau <[email protected]>
+ * Copyright (C) 2013-2016 Michael Lee <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include <ralink_regs.h>
+
+#include "mtk_eth_soc.h"
+#include "gsw_mt7620.h"
+
+void mtk_switch_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg)
+{
+ iowrite32(val, gsw->base + reg);
+}
+EXPORT_SYMBOL_GPL(mtk_switch_w32);
+
+u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned reg)
+{
+ return ioread32(gsw->base + reg);
+}
+EXPORT_SYMBOL_GPL(mtk_switch_r32);
+
+static irqreturn_t gsw_interrupt_mt7621(int irq, void *_eth)
+{
+ struct mtk_eth *eth = (struct mtk_eth *)_eth;
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
+ u32 reg, i;
+
+ reg = mt7530_mdio_r32(gsw, MT7530_SYS_INT_STS);
+
+ for (i = 0; i < 5; i++) {
+ unsigned int link;
+
+ if ((reg & BIT(i)) == 0)
+ continue;
+
+ link = mt7530_mdio_r32(gsw, MT7530_PMSR_P(i)) & 0x1;
+
+ if (link == eth->link[i])
+ continue;
+
+ eth->link[i] = link;
+ if (link)
+ netdev_info(*eth->netdev,
+ "port %d link up\n", i);
+ else
+ netdev_info(*eth->netdev,
+ "port %d link down\n", i);
+ }
+
+ mt7530_mdio_w32(gsw, MT7530_SYS_INT_STS, 0x1f);
+
+ return IRQ_HANDLED;
+}
+
+static void mt7621_hw_init(struct mtk_eth *eth, struct mt7620_gsw *gsw,
+ struct device_node *np)
+{
+ u32 i;
+ u32 val;
+
+ /* hardware reset the switch */
+ mtk_reset(eth, RST_CTRL_MCM);
+ mdelay(10);
+
+ /* reduce RGMII2 PAD driving strength */
+ rt_sysc_m32(MT7621_MDIO_DRV_MASK, 0, SYSC_PAD_RGMII2_MDIO);
+
+ /* gpio mux - RGMII1=Normal mode */
+ rt_sysc_m32(BIT(14), 0, SYSC_GPIO_MODE);
+
+ /* set GMAC1 RGMII mode */
+ rt_sysc_m32(MT7621_GE1_MODE_MASK, 0, SYSC_REG_CFG1);
+
+ /* enable MDIO to control MT7530 */
+ rt_sysc_m32(3 << 12, 0, SYSC_GPIO_MODE);
+
+ /* turn off all PHYs */
+ for (i = 0; i <= 4; i++) {
+ val = _mt7620_mii_read(gsw, i, 0x0);
+ val |= BIT(11);
+ _mt7620_mii_write(gsw, i, 0x0, val);
+ }
+
+ /* reset the switch */
+ mt7530_mdio_w32(gsw, MT7530_SYS_CTRL,
+ SYS_CTRL_SW_RST | SYS_CTRL_REG_RST);
+ usleep_range(10, 20);
+
+ if ((rt_sysc_r32(SYSC_REG_CHIP_REV_ID) & 0xFFFF) == 0x0101) {
+ /* GE1, Force 1000M/FD, FC ON, MAX_RX_LENGTH 1536 */
+ mtk_switch_w32(gsw, MAC_MCR_FIXED_LINK, MTK_MAC_P2_MCR);
+ mt7530_mdio_w32(gsw, MT7530_PMCR_P(6), PMCR_FIXED_LINK);
+ } else {
+ /* GE1, Force 1000M/FD, FC ON, MAX_RX_LENGTH 1536 */
+ mtk_switch_w32(gsw, MAC_MCR_FIXED_LINK_FC, MTK_MAC_P1_MCR);
+ mt7530_mdio_w32(gsw, MT7530_PMCR_P(6), PMCR_FIXED_LINK_FC);
+ }
+
+ /* GE2, Link down */
+ mtk_switch_w32(gsw, MAC_MCR_FORCE_MODE, MTK_MAC_P2_MCR);
+
+ /* Enable Port 6, P5 as GMAC5, P5 disable */
+ val = mt7530_mdio_r32(gsw, MT7530_MHWTRAP);
+ /* Enable Port 6 */
+ val &= ~MHWTRAP_P6_DIS;
+ /* Disable Port 5 */
+ val |= MHWTRAP_P5_DIS;
+ /* manual override of HW-Trap */
+ val |= MHWTRAP_MANUAL;
+ mt7530_mdio_w32(gsw, MT7530_MHWTRAP, val);
+
+ val = rt_sysc_r32(SYSC_REG_CFG);
+ val = (val >> MT7621_XTAL_SHIFT) & MT7621_XTAL_MASK;
+ if (val < MT7621_XTAL_25 && val >= MT7621_XTAL_40) {
+ /* 40Mhz */
+
+ /* disable MT7530 core clock */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x410);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ _mt7620_mii_write(gsw, 0, 14, 0x0);
+
+ /* disable MT7530 PLL */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x40d);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ _mt7620_mii_write(gsw, 0, 14, 0x2020);
+
+ /* for MT7530 core clock = 500Mhz */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x40e);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ _mt7620_mii_write(gsw, 0, 14, 0x119);
+
+ /* enable MT7530 PLL */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x40d);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ _mt7620_mii_write(gsw, 0, 14, 0x2820);
+
+ usleep_range(20, 40);
+
+ /* enable MT7530 core clock */
+ _mt7620_mii_write(gsw, 0, 13, 0x1f);
+ _mt7620_mii_write(gsw, 0, 14, 0x410);
+ _mt7620_mii_write(gsw, 0, 13, 0x401f);
+ }
+
+ /* RGMII */
+ _mt7620_mii_write(gsw, 0, 14, 0x1);
+
+ /* set MT7530 central align */
+ mt7530_mdio_m32(gsw, BIT(0), P6ECR_INTF_MODE_RGMII, MT7530_P6ECR);
+ mt7530_mdio_m32(gsw, TRGMII_TXCTRL_TXC_INV, 0,
+ MT7530_TRGMII_TXCTRL);
+ mt7530_mdio_w32(gsw, MT7530_TRGMII_TCK_CTRL, 0x855);
+
+ /* delay setting for 10/1000M */
+ mt7530_mdio_w32(gsw, MT7530_P5RGMIIRXCR,
+ P5RGMIIRXCR_C_ALIGN | P5RGMIIRXCR_DELAY_2);
+ mt7530_mdio_w32(gsw, MT7530_P5RGMIITXCR, 0x14);
+
+ /* lower Tx Driving*/
+ mt7530_mdio_w32(gsw, MT7530_TRGMII_TD0_ODT, 0x44);
+ mt7530_mdio_w32(gsw, MT7530_TRGMII_TD1_ODT, 0x44);
+ mt7530_mdio_w32(gsw, MT7530_TRGMII_TD2_ODT, 0x44);
+ mt7530_mdio_w32(gsw, MT7530_TRGMII_TD3_ODT, 0x44);
+ mt7530_mdio_w32(gsw, MT7530_TRGMII_TD4_ODT, 0x44);
+ mt7530_mdio_w32(gsw, MT7530_TRGMII_TD5_ODT, 0x44);
+
+ /* turn on all PHYs */
+ for (i = 0; i <= 4; i++) {
+ val = _mt7620_mii_read(gsw, i, 0);
+ val &= ~BIT(11);
+ _mt7620_mii_write(gsw, i, 0, val);
+ }
+
+#define MT7530_NUM_PORTS 8
+#define REG_ESW_PORT_PCR(x) (0x2004 | ((x) << 8))
+#define REG_ESW_PORT_PVC(x) (0x2010 | ((x) << 8))
+#define REG_ESW_PORT_PPBV1(x) (0x2014 | ((x) << 8))
+#define MT7530_CPU_PORT 6
+
+ /* This is copied from mt7530_apply_config in libreCMC driver */
+ {
+ int i;
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530_mdio_w32(gsw, REG_ESW_PORT_PCR(i), 0x00400000);
+
+ mt7530_mdio_w32(gsw, REG_ESW_PORT_PCR(MT7530_CPU_PORT), 0x00ff0000);
+
+ for (i = 0; i < MT7530_NUM_PORTS; i++)
+ mt7530_mdio_w32(gsw, REG_ESW_PORT_PVC(i), 0x810000c0);
+
+ }
+
+ /* enable irq */
+ mt7530_mdio_m32(gsw, 0, 3 << 16, MT7530_TOP_SIG_CTRL);
+ mt7530_mdio_w32(gsw, MT7530_SYS_INT_EN, 0x1f);
+
+}
+
+static const struct of_device_id mediatek_gsw_match[] = {
+ { .compatible = "mediatek,mt7621-gsw" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mediatek_gsw_match);
+
+int mtk_gsw_init(struct mtk_eth *eth)
+{
+ struct device_node *np = eth->switch_np;
+ struct platform_device *pdev = of_find_device_by_node(np);
+ struct mt7620_gsw *gsw;
+
+ if (!pdev)
+ return -ENODEV;
+
+ if (!of_device_is_compatible(np, mediatek_gsw_match->compatible))
+ return -EINVAL;
+
+ gsw = platform_get_drvdata(pdev);
+ eth->sw_priv = gsw;
+
+ if (!gsw->irq)
+ return -EINVAL;
+
+ request_irq(gsw->irq, gsw_interrupt_mt7621, 0,
+ "gsw", eth);
+ disable_irq(gsw->irq);
+
+ mt7621_hw_init(eth, gsw, np);
+
+ enable_irq(gsw->irq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_gsw_init);
+
+static int mt7621_gsw_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct mt7620_gsw *gsw;
+
+ gsw = devm_kzalloc(&pdev->dev, sizeof(struct mt7620_gsw), GFP_KERNEL);
+ if (!gsw)
+ return -ENOMEM;
+
+ gsw->base = devm_ioremap_resource(&pdev->dev, res);
+ if (!gsw->base)
+ return -EADDRNOTAVAIL;
+
+ gsw->dev = &pdev->dev;
+ gsw->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+
+ platform_set_drvdata(pdev, gsw);
+
+ return 0;
+}
+
+static int mt7621_gsw_remove(struct platform_device *pdev)
+{
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver gsw_driver = {
+ .probe = mt7621_gsw_probe,
+ .remove = mt7621_gsw_remove,
+ .driver = {
+ .name = "mt7621-gsw",
+ .owner = THIS_MODULE,
+ .of_match_table = mediatek_gsw_match,
+ },
+};
+
+module_platform_driver(gsw_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Crispin <[email protected]>");
+MODULE_DESCRIPTION("GBit switch driver for Mediatek MT7621 SoC");
From: John Crispin <[email protected]>
NeilBrown: this patch originally contained soc-mt7620.c
but as I cannot test that, I removed it. Some functions
from mdio-mt7620.c are needed for soc-mt7621.c support
- fixed mt7620_has_carrier() to read correct register.
Original comment:
Add support for SoCs from the mt7620 family. These all have one dedicated
external gbit port and a builtin 5 port 100mbit switch. Additionally one
of the 5 switch ports can be changed to become an additional gbit port
that we can attach a phy to. MT7620 was the first SoC released after
Ralink was acquired by MTK and has seen a lot of changes to the core.
With MT7620 we have seen the addition of some advanced features such as
TX vlan offloading, RX scatter gather and TSO. Newer MTK SoCs are based on
this design.
Although the builtin MT7530 is gbit capable, the builtin PHYs are only
100mbit. There are boards in the wild that use one of the gbit MACs to
attach an external MT7530. For this to work a few hacks need to be applied
to reorganize the MDIO address mappings and autopolling for the SoC to
correctly work with the external switch. This is however not part of the
series and will be part of a later series once we evaluated if we want to
use DSA or switchdev.
Signed-off-by: John Crispin <[email protected]>
Signed-off-by: Felix Fietkau <[email protected]>
Signed-off-by: Michael Lee <[email protected]>
Signed-off-by: NeilBrown <[email protected]>
---
drivers/staging/mt7621-eth/TODO | 2
drivers/staging/mt7621-eth/mdio_mt7620.c | 173 ++++++++++++++++++++++++++++++
2 files changed, 175 insertions(+)
create mode 100644 drivers/staging/mt7621-eth/mdio_mt7620.c
diff --git a/drivers/staging/mt7621-eth/TODO b/drivers/staging/mt7621-eth/TODO
index 1ab0530131ae..50fb5a959ee8 100644
--- a/drivers/staging/mt7621-eth/TODO
+++ b/drivers/staging/mt7621-eth/TODO
@@ -4,5 +4,7 @@
- general code review and clean up
- add support for second MAC on mt7621
- convert gsw code to use switchdev interfaces
+- md7620_mmi_write etc should probably be wrapped
+ in a regmap abstraction.
Cc: NeilBrown <[email protected]>
diff --git a/drivers/staging/mt7621-eth/mdio_mt7620.c b/drivers/staging/mt7621-eth/mdio_mt7620.c
new file mode 100644
index 000000000000..ced605c2914e
--- /dev/null
+++ b/drivers/staging/mt7621-eth/mdio_mt7620.c
@@ -0,0 +1,173 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <[email protected]>
+ * Copyright (C) 2009-2016 Felix Fietkau <[email protected]>
+ * Copyright (C) 2013-2016 Michael Lee <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include "mtk_eth_soc.h"
+#include "gsw_mt7620.h"
+#include "mdio.h"
+
+static int mt7620_mii_busy_wait(struct mt7620_gsw *gsw)
+{
+ unsigned long t_start = jiffies;
+
+ while (1) {
+ if (!(mtk_switch_r32(gsw,
+ gsw->piac_offset + MT7620_GSW_REG_PIAC) &
+ GSW_MDIO_ACCESS))
+ return 0;
+ if (time_after(jiffies, t_start + GSW_REG_PHY_TIMEOUT))
+ break;
+ }
+
+ dev_err(gsw->dev, "mdio: MDIO timeout\n");
+ return -1;
+}
+
+u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr,
+ u32 phy_register, u32 write_data)
+{
+ if (mt7620_mii_busy_wait(gsw))
+ return -1;
+
+ write_data &= 0xffff;
+
+ mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_WRITE |
+ (phy_register << GSW_MDIO_REG_SHIFT) |
+ (phy_addr << GSW_MDIO_ADDR_SHIFT) | write_data,
+ MT7620_GSW_REG_PIAC);
+
+ if (mt7620_mii_busy_wait(gsw))
+ return -1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(_mt7620_mii_write);
+
+u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg)
+{
+ u32 d;
+
+ if (mt7620_mii_busy_wait(gsw))
+ return 0xffff;
+
+ mtk_switch_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_READ |
+ (phy_reg << GSW_MDIO_REG_SHIFT) |
+ (phy_addr << GSW_MDIO_ADDR_SHIFT),
+ MT7620_GSW_REG_PIAC);
+
+ if (mt7620_mii_busy_wait(gsw))
+ return 0xffff;
+
+ d = mtk_switch_r32(gsw, MT7620_GSW_REG_PIAC) & 0xffff;
+
+ return d;
+}
+EXPORT_SYMBOL_GPL(_mt7620_mii_read);
+
+int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val)
+{
+ struct mtk_eth *eth = bus->priv;
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
+
+ return _mt7620_mii_write(gsw, phy_addr, phy_reg, val);
+}
+
+int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
+{
+ struct mtk_eth *eth = bus->priv;
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
+
+ return _mt7620_mii_read(gsw, phy_addr, phy_reg);
+}
+
+void mt7530_mdio_w32(struct mt7620_gsw *gsw, u32 reg, u32 val)
+{
+ _mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
+ _mt7620_mii_write(gsw, 0x1f, (reg >> 2) & 0xf, val & 0xffff);
+ _mt7620_mii_write(gsw, 0x1f, 0x10, val >> 16);
+}
+EXPORT_SYMBOL_GPL(mt7530_mdio_w32);
+
+u32 mt7530_mdio_r32(struct mt7620_gsw *gsw, u32 reg)
+{
+ u16 high, low;
+
+ _mt7620_mii_write(gsw, 0x1f, 0x1f, (reg >> 6) & 0x3ff);
+ low = _mt7620_mii_read(gsw, 0x1f, (reg >> 2) & 0xf);
+ high = _mt7620_mii_read(gsw, 0x1f, 0x10);
+
+ return (high << 16) | (low & 0xffff);
+}
+EXPORT_SYMBOL_GPL(mt7530_mdio_r32);
+
+void mt7530_mdio_m32(struct mt7620_gsw *gsw, u32 mask, u32 set, u32 reg)
+{
+ u32 val = mt7530_mdio_r32(gsw, reg);
+
+ val &= ~mask;
+ val |= set;
+ mt7530_mdio_w32(gsw, reg, val);
+}
+EXPORT_SYMBOL_GPL(mt7530_mdio_m32);
+
+static unsigned char *mtk_speed_str(int speed)
+{
+ switch (speed) {
+ case 2:
+ case SPEED_1000:
+ return "1000";
+ case 1:
+ case SPEED_100:
+ return "100";
+ case 0:
+ case SPEED_10:
+ return "10";
+ }
+
+ return "? ";
+}
+
+int mt7620_has_carrier(struct mtk_eth *eth)
+{
+ struct mt7620_gsw *gsw = (struct mt7620_gsw *)eth->sw_priv;
+ int i;
+
+ for (i = 0; i < GSW_PORT6; i++)
+ if (mt7530_mdio_r32(gsw, GSW_REG_PORT_STATUS(i)) & 0x1)
+ return 1;
+ return 0;
+}
+
+void mt7620_print_link_state(struct mtk_eth *eth, int port, int link,
+ int speed, int duplex)
+{
+ struct mt7620_gsw *gsw = eth->sw_priv;
+
+ if (link)
+ dev_info(gsw->dev, "port %d link up (%sMbps/%s duplex)\n",
+ port, mtk_speed_str(speed),
+ (duplex) ? "Full" : "Half");
+ else
+ dev_info(gsw->dev, "port %d link down\n", port);
+}
+
+void mt7620_mdio_link_adjust(struct mtk_eth *eth, int port)
+{
+ mt7620_print_link_state(eth, port, eth->link[port],
+ eth->phy->speed[port],
+ (eth->phy->duplex[port] == DUPLEX_FULL));
+}
From: John Crispin <[email protected]>
Original comment:
This patch adds the main chunk of the driver. The ethernet core is used in
all of the Mediatek/Ralink Wireless SoCs. Over the years we have seen
various changes to
* the register layout
* the type of ports (single/dual gbit, internal FE/Gbit switch)
* dma engine (PDMA/QDMA)
and new offloading features were added, such as
* checksum
* VLAN TX/RX
* TSO
* LRO
The core functionality has however remained the same allowing us to use
the same code for all SoCs.
The abstraction for the various SoCs uses the typical ops struct pattern
which allows us to extend or override the core functionality depending on
which SoC we are on. The code to bring up the switches and external ports
has also been split into separate files.
There are 2 types of DMA engine, PDMA and the newer QDMA. PDMA uses a
typical ring buffer while QDMA uses a linked list. Unfortunatley we have
the MT7621 which has a few silicon issues. Due to these issues we need to
PDMA for RX and QDMA for TX. All SoCs newer than the MT7621 can can run on
QDMA exclusively.
Most of the SoCs have a switch frontend. Older silicon has a so called ESW
(Ethernet Switch) while newer cores have a GSW (Gigabit switch).
Additionally there is a MDIO bus that can be used to talk to PHYs. In these
cases one switch port get changed into a normal MAC port.
Some SoCs have a dual MAC, we currently only support this on MT7623.
NeilBrown:
- removed everything not closely related to mt7621, as that is all I
can test
- converted ethtool.c to new ethtool_link_ksettings interfaces.
Doesn't work yet.
- updated some phydev interface use: e.g. dev_name() -> phydev_name()
- updated mdio to use mdiobus_get_phy()
- added some missing export_symbols
- updated get_stats64 interface
- TX_DMA_FPORT and TX_DMA_TSO to tx dma descriptor
- range checked RX_DMA_FPORT in rx dma descriptor
- tell hardware what mac address was chosen
- fixed MT7620_GDMA1_FWD_CFG which was using wrong value
Signed-off-by: John Crispin <[email protected]>
Signed-off-by: Felix Fietkau <[email protected]>
Signed-off-by: Michael Lee <[email protected]>
Signed-off-by: NeilBrown <[email protected]>
---
drivers/staging/mt7621-eth/TODO | 3
drivers/staging/mt7621-eth/ethtool.c | 225 +++
drivers/staging/mt7621-eth/ethtool.h | 22
drivers/staging/mt7621-eth/mdio.c | 271 ++++
drivers/staging/mt7621-eth/mdio.h | 27
drivers/staging/mt7621-eth/mtk_eth_soc.c | 2178 ++++++++++++++++++++++++++++++
drivers/staging/mt7621-eth/mtk_eth_soc.h | 721 ++++++++++
7 files changed, 3447 insertions(+)
create mode 100644 drivers/staging/mt7621-eth/ethtool.c
create mode 100644 drivers/staging/mt7621-eth/ethtool.h
create mode 100644 drivers/staging/mt7621-eth/mdio.c
create mode 100644 drivers/staging/mt7621-eth/mdio.h
create mode 100644 drivers/staging/mt7621-eth/mtk_eth_soc.c
create mode 100644 drivers/staging/mt7621-eth/mtk_eth_soc.h
diff --git a/drivers/staging/mt7621-eth/TODO b/drivers/staging/mt7621-eth/TODO
index 5f269af0db5c..25c550e8df8c 100644
--- a/drivers/staging/mt7621-eth/TODO
+++ b/drivers/staging/mt7621-eth/TODO
@@ -1,4 +1,7 @@
- verify devicetree documentation is consistent with code
+- fix ethtool - currently doesn't return valid data.
+- general code review and clean up
+- add support for second MAC on mt7621
Cc: NeilBrown <[email protected]>
diff --git a/drivers/staging/mt7621-eth/ethtool.c b/drivers/staging/mt7621-eth/ethtool.c
new file mode 100644
index 000000000000..38ba0c040aba
--- /dev/null
+++ b/drivers/staging/mt7621-eth/ethtool.c
@@ -0,0 +1,225 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <[email protected]>
+ * Copyright (C) 2009-2016 Felix Fietkau <[email protected]>
+ * Copyright (C) 2013-2016 Michael Lee <[email protected]>
+ */
+
+#include "mtk_eth_soc.h"
+
+static const char mtk_gdma_str[][ETH_GSTRING_LEN] = {
+#define _FE(x...) # x,
+MTK_STAT_REG_DECLARE
+#undef _FE
+};
+
+static int mtk_get_link_ksettings(struct net_device *dev,
+ struct ethtool_link_ksettings *cmd)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ int err;
+
+ if (!mac->phy_dev)
+ return -ENODEV;
+
+ if (mac->phy_flags == MTK_PHY_FLAG_ATTACH) {
+ err = phy_read_status(mac->phy_dev);
+ if (err)
+ return -ENODEV;
+ }
+
+ phy_ethtool_ksettings_get(mac->phy_dev, cmd);
+ return 0;
+}
+
+static int mtk_set_link_ksettings(struct net_device *dev,
+ const struct ethtool_link_ksettings *cmd)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ if (!mac->phy_dev)
+ return -ENODEV;
+
+ if (cmd->base.phy_address != mac->phy_dev->mdio.addr) {
+ if (mac->hw->phy->phy_node[cmd->base.phy_address]) {
+ mac->phy_dev = mac->hw->phy->phy[cmd->base.phy_address];
+ mac->phy_flags = MTK_PHY_FLAG_PORT;
+ } else if (mac->hw->mii_bus) {
+ mac->phy_dev = mdiobus_get_phy(mac->hw->mii_bus, cmd->base.phy_address);
+ if (!mac->phy_dev)
+ return -ENODEV;
+ mac->phy_flags = MTK_PHY_FLAG_ATTACH;
+ } else {
+ return -ENODEV;
+ }
+ }
+
+ return phy_ethtool_ksettings_set(mac->phy_dev, cmd);
+
+}
+
+static void mtk_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_soc_data *soc = mac->hw->soc;
+
+ strlcpy(info->driver, mac->hw->dev->driver->name, sizeof(info->driver));
+ strlcpy(info->bus_info, dev_name(mac->hw->dev), sizeof(info->bus_info));
+
+ if (soc->reg_table[MTK_REG_MTK_COUNTER_BASE])
+ info->n_stats = ARRAY_SIZE(mtk_gdma_str);
+}
+
+static u32 mtk_get_msglevel(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ return mac->hw->msg_enable;
+}
+
+static void mtk_set_msglevel(struct net_device *dev, u32 value)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ mac->hw->msg_enable = value;
+}
+
+static int mtk_nway_reset(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ if (!mac->phy_dev)
+ return -EOPNOTSUPP;
+
+ return genphy_restart_aneg(mac->phy_dev);
+}
+
+static u32 mtk_get_link(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ int err;
+
+ if (!mac->phy_dev)
+ goto out_get_link;
+
+ if (mac->phy_flags == MTK_PHY_FLAG_ATTACH) {
+ err = genphy_update_link(mac->phy_dev);
+ if (err)
+ goto out_get_link;
+ }
+
+ return mac->phy_dev->link;
+
+out_get_link:
+ return ethtool_op_get_link(dev);
+}
+
+static int mtk_set_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ring)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ if ((ring->tx_pending < 2) ||
+ (ring->rx_pending < 2) ||
+ (ring->rx_pending > mac->hw->soc->dma_ring_size) ||
+ (ring->tx_pending > mac->hw->soc->dma_ring_size))
+ return -EINVAL;
+
+ dev->netdev_ops->ndo_stop(dev);
+
+ mac->hw->tx_ring.tx_ring_size = BIT(fls(ring->tx_pending) - 1);
+ mac->hw->rx_ring[0].rx_ring_size = BIT(fls(ring->rx_pending) - 1);
+
+ return dev->netdev_ops->ndo_open(dev);
+}
+
+static void mtk_get_ringparam(struct net_device *dev,
+ struct ethtool_ringparam *ring)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ ring->rx_max_pending = mac->hw->soc->dma_ring_size;
+ ring->tx_max_pending = mac->hw->soc->dma_ring_size;
+ ring->rx_pending = mac->hw->rx_ring[0].rx_ring_size;
+ ring->tx_pending = mac->hw->tx_ring.tx_ring_size;
+}
+
+static void mtk_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+ switch (stringset) {
+ case ETH_SS_STATS:
+ memcpy(data, *mtk_gdma_str, sizeof(mtk_gdma_str));
+ break;
+ }
+}
+
+static int mtk_get_sset_count(struct net_device *dev, int sset)
+{
+ switch (sset) {
+ case ETH_SS_STATS:
+ return ARRAY_SIZE(mtk_gdma_str);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void mtk_get_ethtool_stats(struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_hw_stats *hwstats = mac->hw_stats;
+ u64 *data_src, *data_dst;
+ unsigned int start;
+ int i;
+
+ if (netif_running(dev) && netif_device_present(dev)) {
+ if (spin_trylock(&hwstats->stats_lock)) {
+ mtk_stats_update_mac(mac);
+ spin_unlock(&hwstats->stats_lock);
+ }
+ }
+
+ do {
+ data_src = &hwstats->tx_bytes;
+ data_dst = data;
+ start = u64_stats_fetch_begin_irq(&hwstats->syncp);
+
+ for (i = 0; i < ARRAY_SIZE(mtk_gdma_str); i++)
+ *data_dst++ = *data_src++;
+
+ } while (u64_stats_fetch_retry_irq(&hwstats->syncp, start));
+}
+
+static struct ethtool_ops mtk_ethtool_ops = {
+ .get_link_ksettings = mtk_get_link_ksettings,
+ .set_link_ksettings = mtk_set_link_ksettings,
+ .get_drvinfo = mtk_get_drvinfo,
+ .get_msglevel = mtk_get_msglevel,
+ .set_msglevel = mtk_set_msglevel,
+ .nway_reset = mtk_nway_reset,
+ .get_link = mtk_get_link,
+ .set_ringparam = mtk_set_ringparam,
+ .get_ringparam = mtk_get_ringparam,
+};
+
+void mtk_set_ethtool_ops(struct net_device *netdev)
+{
+ struct mtk_mac *mac = netdev_priv(netdev);
+ struct mtk_soc_data *soc = mac->hw->soc;
+
+ if (soc->reg_table[MTK_REG_MTK_COUNTER_BASE]) {
+ mtk_ethtool_ops.get_strings = mtk_get_strings;
+ mtk_ethtool_ops.get_sset_count = mtk_get_sset_count;
+ mtk_ethtool_ops.get_ethtool_stats = mtk_get_ethtool_stats;
+ }
+
+ netdev->ethtool_ops = &mtk_ethtool_ops;
+}
diff --git a/drivers/staging/mt7621-eth/ethtool.h b/drivers/staging/mt7621-eth/ethtool.h
new file mode 100644
index 000000000000..40b4cf011660
--- /dev/null
+++ b/drivers/staging/mt7621-eth/ethtool.h
@@ -0,0 +1,22 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <[email protected]>
+ * Copyright (C) 2009-2016 Felix Fietkau <[email protected]>
+ * Copyright (C) 2013-2016 Michael Lee <[email protected]>
+ */
+
+#ifndef MTK_ETHTOOL_H
+#define MTK_ETHTOOL_H
+
+#include <linux/ethtool.h>
+
+void mtk_set_ethtool_ops(struct net_device *netdev);
+
+#endif /* MTK_ETHTOOL_H */
diff --git a/drivers/staging/mt7621-eth/mdio.c b/drivers/staging/mt7621-eth/mdio.c
new file mode 100644
index 000000000000..96ecda930c48
--- /dev/null
+++ b/drivers/staging/mt7621-eth/mdio.c
@@ -0,0 +1,271 @@
+/* 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; version 2 of the License
+ *
+ * Copyright (C) 2009-2016 John Crispin <[email protected]>
+ * Copyright (C) 2009-2016 Felix Fietkau <[email protected]>
+ * Copyright (C) 2013-2016 Michael Lee <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/phy.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+
+#include "mtk_eth_soc.h"
+#include "mdio.h"
+
+static int mtk_mdio_reset(struct mii_bus *bus)
+{
+ /* TODO */
+ return 0;
+}
+
+static void mtk_phy_link_adjust(struct net_device *dev)
+{
+ struct mtk_eth *eth = netdev_priv(dev);
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(ð->phy->lock, flags);
+ for (i = 0; i < 8; i++) {
+ if (eth->phy->phy_node[i]) {
+ struct phy_device *phydev = eth->phy->phy[i];
+ int status_change = 0;
+
+ if (phydev->link)
+ if (eth->phy->duplex[i] != phydev->duplex ||
+ eth->phy->speed[i] != phydev->speed)
+ status_change = 1;
+
+ if (phydev->link != eth->link[i])
+ status_change = 1;
+
+ switch (phydev->speed) {
+ case SPEED_1000:
+ case SPEED_100:
+ case SPEED_10:
+ eth->link[i] = phydev->link;
+ eth->phy->duplex[i] = phydev->duplex;
+ eth->phy->speed[i] = phydev->speed;
+
+ if (status_change &&
+ eth->soc->mdio_adjust_link)
+ eth->soc->mdio_adjust_link(eth, i);
+ break;
+ }
+ }
+ }
+}
+
+int mtk_connect_phy_node(struct mtk_eth *eth, struct mtk_mac *mac,
+ struct device_node *phy_node)
+{
+ const __be32 *_port = NULL;
+ struct phy_device *phydev;
+ int phy_mode, port;
+
+ _port = of_get_property(phy_node, "reg", NULL);
+
+ if (!_port || (be32_to_cpu(*_port) >= 0x20)) {
+ pr_err("%s: invalid port id\n", phy_node->name);
+ return -EINVAL;
+ }
+ port = be32_to_cpu(*_port);
+ phy_mode = of_get_phy_mode(phy_node);
+ if (phy_mode < 0) {
+ dev_err(eth->dev, "incorrect phy-mode %d\n", phy_mode);
+ eth->phy->phy_node[port] = NULL;
+ return -EINVAL;
+ }
+
+ phydev = of_phy_connect(eth->netdev[mac->id], phy_node,
+ mtk_phy_link_adjust, 0, phy_mode);
+ if (IS_ERR(phydev)) {
+ dev_err(eth->dev, "could not connect to PHY\n");
+ eth->phy->phy_node[port] = NULL;
+ return PTR_ERR(phydev);
+ }
+
+ phydev->supported &= PHY_GBIT_FEATURES;
+ phydev->advertising = phydev->supported;
+
+ dev_info(eth->dev,
+ "connected port %d to PHY at %s [uid=%08x, driver=%s]\n",
+ port, phydev_name(phydev), phydev->phy_id,
+ phydev->drv->name);
+
+ eth->phy->phy[port] = phydev;
+ eth->link[port] = 0;
+
+ return 0;
+}
+
+static void phy_init(struct mtk_eth *eth, struct mtk_mac *mac,
+ struct phy_device *phy)
+{
+ phy_attach(eth->netdev[mac->id], phydev_name(phy),
+ PHY_INTERFACE_MODE_MII);
+
+ phy->autoneg = AUTONEG_ENABLE;
+ phy->speed = 0;
+ phy->duplex = 0;
+ phy->supported &= PHY_BASIC_FEATURES;
+ phy->advertising = phy->supported | ADVERTISED_Autoneg;
+
+ phy_start_aneg(phy);
+}
+
+static int mtk_phy_connect(struct mtk_mac *mac)
+{
+ struct mtk_eth *eth = mac->hw;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (eth->phy->phy_node[i]) {
+ if (!mac->phy_dev) {
+ mac->phy_dev = eth->phy->phy[i];
+ mac->phy_flags = MTK_PHY_FLAG_PORT;
+ }
+ } else if (eth->mii_bus) {
+ struct phy_device *phy;
+ phy = mdiobus_get_phy(eth->mii_bus, i);
+ if (phy) {
+ phy_init(eth, mac, phy);
+ if (!mac->phy_dev) {
+ mac->phy_dev = phy;
+ mac->phy_flags = MTK_PHY_FLAG_ATTACH;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void mtk_phy_disconnect(struct mtk_mac *mac)
+{
+ struct mtk_eth *eth = mac->hw;
+ unsigned long flags;
+ int i;
+
+ for (i = 0; i < 8; i++)
+ if (eth->phy->phy_fixed[i]) {
+ spin_lock_irqsave(ð->phy->lock, flags);
+ eth->link[i] = 0;
+ if (eth->soc->mdio_adjust_link)
+ eth->soc->mdio_adjust_link(eth, i);
+ spin_unlock_irqrestore(ð->phy->lock, flags);
+ } else if (eth->phy->phy[i]) {
+ phy_disconnect(eth->phy->phy[i]);
+ } else if (eth->mii_bus) {
+ struct phy_device *phy = mdiobus_get_phy(eth->mii_bus, i);
+ if (phy)
+ phy_detach(phy);
+ }
+}
+
+static void mtk_phy_start(struct mtk_mac *mac)
+{
+ struct mtk_eth *eth = mac->hw;
+ unsigned long flags;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (eth->phy->phy_fixed[i]) {
+ spin_lock_irqsave(ð->phy->lock, flags);
+ eth->link[i] = 1;
+ if (eth->soc->mdio_adjust_link)
+ eth->soc->mdio_adjust_link(eth, i);
+ spin_unlock_irqrestore(ð->phy->lock, flags);
+ } else if (eth->phy->phy[i]) {
+ phy_start(eth->phy->phy[i]);
+ }
+ }
+}
+
+static void mtk_phy_stop(struct mtk_mac *mac)
+{
+ struct mtk_eth *eth = mac->hw;
+ unsigned long flags;
+ int i;
+
+ for (i = 0; i < 8; i++)
+ if (eth->phy->phy_fixed[i]) {
+ spin_lock_irqsave(ð->phy->lock, flags);
+ eth->link[i] = 0;
+ if (eth->soc->mdio_adjust_link)
+ eth->soc->mdio_adjust_link(eth, i);
+ spin_unlock_irqrestore(ð->phy->lock, flags);
+ } else if (eth->phy->phy[i]) {
+ phy_stop(eth->phy->phy[i]);
+ }
+}
+
+static struct mtk_phy phy_ralink = {
+ .connect = mtk_phy_connect,
+ .disconnect = mtk_phy_disconnect,
+ .start = mtk_phy_start,
+ .stop = mtk_phy_stop,
+};
+
+int mtk_mdio_init(struct mtk_eth *eth)
+{
+ struct device_node *mii_np;
+ int err;
+
+ if (!eth->soc->mdio_read || !eth->soc->mdio_write)
+ return 0;
+
+ spin_lock_init(&phy_ralink.lock);
+ eth->phy = &phy_ralink;
+
+ mii_np = of_get_child_by_name(eth->dev->of_node, "mdio-bus");
+ if (!mii_np) {
+ dev_err(eth->dev, "no %s child node found", "mdio-bus");
+ return -ENODEV;
+ }
+
+ if (!of_device_is_available(mii_np)) {
+ err = 0;
+ goto err_put_node;
+ }
+
+ eth->mii_bus = mdiobus_alloc();
+ if (!eth->mii_bus) {
+ err = -ENOMEM;
+ goto err_put_node;
+ }
+
+ eth->mii_bus->name = "mdio";
+ eth->mii_bus->read = eth->soc->mdio_read;
+ eth->mii_bus->write = eth->soc->mdio_write;
+ eth->mii_bus->reset = mtk_mdio_reset;
+ eth->mii_bus->priv = eth;
+ eth->mii_bus->parent = eth->dev;
+
+ snprintf(eth->mii_bus->id, MII_BUS_ID_SIZE, "%s", mii_np->name);
+ err = of_mdiobus_register(eth->mii_bus, mii_np);
+ if (err)
+ goto err_free_bus;
+
+ return 0;
+
+err_free_bus:
+ kfree(eth->mii_bus);
+err_put_node:
+ of_node_put(mii_np);
+ eth->mii_bus = NULL;
+ return err;
+}
+
+void mtk_mdio_cleanup(struct mtk_eth *eth)
+{
+ if (!eth->mii_bus)
+ return;
+
+ mdiobus_unregister(eth->mii_bus);
+ of_node_put(eth->mii_bus->dev.of_node);
+ kfree(eth->mii_bus);
+}
diff --git a/drivers/staging/mt7621-eth/mdio.h b/drivers/staging/mt7621-eth/mdio.h
new file mode 100644
index 000000000000..b14e23842a01
--- /dev/null
+++ b/drivers/staging/mt7621-eth/mdio.h
@@ -0,0 +1,27 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <[email protected]>
+ * Copyright (C) 2009-2016 Felix Fietkau <[email protected]>
+ * Copyright (C) 2013-2016 Michael Lee <[email protected]>
+ */
+
+#ifndef _RALINK_MDIO_H__
+#define _RALINK_MDIO_H__
+
+#ifdef CONFIG_NET_MEDIATEK_MDIO
+int mtk_mdio_init(struct mtk_eth *eth);
+void mtk_mdio_cleanup(struct mtk_eth *eth);
+int mtk_connect_phy_node(struct mtk_eth *eth, struct mtk_mac *mac,
+ struct device_node *phy_node);
+#else
+static inline int mtk_mdio_init(struct mtk_eth *eth) { return 0; }
+static inline void mtk_mdio_cleanup(struct mtk_eth *eth) {}
+#endif
+#endif
diff --git a/drivers/staging/mt7621-eth/mtk_eth_soc.c b/drivers/staging/mt7621-eth/mtk_eth_soc.c
new file mode 100644
index 000000000000..98b44629bc1d
--- /dev/null
+++ b/drivers/staging/mt7621-eth/mtk_eth_soc.c
@@ -0,0 +1,2178 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <[email protected]>
+ * Copyright (C) 2009-2016 Felix Fietkau <[email protected]>
+ * Copyright (C) 2013-2016 Michael Lee <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/clk.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <linux/if_vlan.h>
+#include <linux/reset.h>
+#include <linux/tcp.h>
+#include <linux/io.h>
+#include <linux/bug.h>
+#include <linux/regmap.h>
+
+#include "mtk_eth_soc.h"
+#include "mdio.h"
+#include "ethtool.h"
+
+#define MAX_RX_LENGTH 1536
+#define MTK_RX_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
+#define MTK_RX_HLEN (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
+#define DMA_DUMMY_DESC 0xffffffff
+#define MTK_DEFAULT_MSG_ENABLE \
+ (NETIF_MSG_DRV | \
+ NETIF_MSG_PROBE | \
+ NETIF_MSG_LINK | \
+ NETIF_MSG_TIMER | \
+ NETIF_MSG_IFDOWN | \
+ NETIF_MSG_IFUP | \
+ NETIF_MSG_RX_ERR | \
+ NETIF_MSG_TX_ERR)
+
+#define TX_DMA_DESP2_DEF (TX_DMA_LS0 | TX_DMA_DONE)
+#define NEXT_TX_DESP_IDX(X) (((X) + 1) & (ring->tx_ring_size - 1))
+#define NEXT_RX_DESP_IDX(X) (((X) + 1) & (ring->rx_ring_size - 1))
+
+#define SYSC_REG_RSTCTRL 0x34
+
+static int mtk_msg_level = -1;
+module_param_named(msg_level, mtk_msg_level, int, 0);
+MODULE_PARM_DESC(msg_level, "Message level (-1=defaults,0=none,...,16=all)");
+
+static const u16 mtk_reg_table_default[MTK_REG_COUNT] = {
+ [MTK_REG_PDMA_GLO_CFG] = MTK_PDMA_GLO_CFG,
+ [MTK_REG_PDMA_RST_CFG] = MTK_PDMA_RST_CFG,
+ [MTK_REG_DLY_INT_CFG] = MTK_DLY_INT_CFG,
+ [MTK_REG_TX_BASE_PTR0] = MTK_TX_BASE_PTR0,
+ [MTK_REG_TX_MAX_CNT0] = MTK_TX_MAX_CNT0,
+ [MTK_REG_TX_CTX_IDX0] = MTK_TX_CTX_IDX0,
+ [MTK_REG_TX_DTX_IDX0] = MTK_TX_DTX_IDX0,
+ [MTK_REG_RX_BASE_PTR0] = MTK_RX_BASE_PTR0,
+ [MTK_REG_RX_MAX_CNT0] = MTK_RX_MAX_CNT0,
+ [MTK_REG_RX_CALC_IDX0] = MTK_RX_CALC_IDX0,
+ [MTK_REG_RX_DRX_IDX0] = MTK_RX_DRX_IDX0,
+ [MTK_REG_MTK_INT_ENABLE] = MTK_INT_ENABLE,
+ [MTK_REG_MTK_INT_STATUS] = MTK_INT_STATUS,
+ [MTK_REG_MTK_DMA_VID_BASE] = MTK_DMA_VID0,
+ [MTK_REG_MTK_COUNTER_BASE] = MTK_GDMA1_TX_GBCNT,
+ [MTK_REG_MTK_RST_GL] = MTK_RST_GL,
+};
+
+static const u16 *mtk_reg_table = mtk_reg_table_default;
+
+void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg)
+{
+ __raw_writel(val, eth->base + reg);
+}
+
+u32 mtk_r32(struct mtk_eth *eth, unsigned reg)
+{
+ return __raw_readl(eth->base + reg);
+}
+
+static void mtk_reg_w32(struct mtk_eth *eth, u32 val, enum mtk_reg reg)
+{
+ mtk_w32(eth, val, mtk_reg_table[reg]);
+}
+
+static u32 mtk_reg_r32(struct mtk_eth *eth, enum mtk_reg reg)
+{
+ return mtk_r32(eth, mtk_reg_table[reg]);
+}
+
+/* these bits are also exposed via the reset-controller API. however the switch
+ * and FE need to be brought out of reset in the exakt same moemtn and the
+ * reset-controller api does not provide this feature yet. Do the reset manually
+ * until we fixed the reset-controller api to be able to do this
+ */
+void mtk_reset(struct mtk_eth *eth, u32 reset_bits)
+{
+ u32 val;
+
+ regmap_read(eth->ethsys, SYSC_REG_RSTCTRL, &val);
+ val |= reset_bits;
+ regmap_write(eth->ethsys, SYSC_REG_RSTCTRL, val);
+ usleep_range(10, 20);
+ val &= ~reset_bits;
+ regmap_write(eth->ethsys, SYSC_REG_RSTCTRL, val);
+ usleep_range(10, 20);
+}
+EXPORT_SYMBOL(mtk_reset);
+
+static inline void mtk_irq_ack(struct mtk_eth *eth, u32 mask)
+{
+ if (eth->soc->dma_type & MTK_PDMA)
+ mtk_reg_w32(eth, mask, MTK_REG_MTK_INT_STATUS);
+ if (eth->soc->dma_type & MTK_QDMA)
+ mtk_w32(eth, mask, MTK_QMTK_INT_STATUS);
+}
+
+static inline u32 mtk_irq_pending(struct mtk_eth *eth)
+{
+ u32 status = 0;
+
+ if (eth->soc->dma_type & MTK_PDMA)
+ status |= mtk_reg_r32(eth, MTK_REG_MTK_INT_STATUS);
+ if (eth->soc->dma_type & MTK_QDMA)
+ status |= mtk_r32(eth, MTK_QMTK_INT_STATUS);
+
+ return status;
+}
+
+static void mtk_irq_ack_status(struct mtk_eth *eth, u32 mask)
+{
+ u32 status_reg = MTK_REG_MTK_INT_STATUS;
+
+ if (mtk_reg_table[MTK_REG_MTK_INT_STATUS2])
+ status_reg = MTK_REG_MTK_INT_STATUS2;
+
+ mtk_reg_w32(eth, mask, status_reg);
+}
+
+static u32 mtk_irq_pending_status(struct mtk_eth *eth)
+{
+ u32 status_reg = MTK_REG_MTK_INT_STATUS;
+
+ if (mtk_reg_table[MTK_REG_MTK_INT_STATUS2])
+ status_reg = MTK_REG_MTK_INT_STATUS2;
+
+ return mtk_reg_r32(eth, status_reg);
+}
+
+static inline void mtk_irq_disable(struct mtk_eth *eth, u32 mask)
+{
+ u32 val;
+
+ if (eth->soc->dma_type & MTK_PDMA) {
+ val = mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
+ mtk_reg_w32(eth, val & ~mask, MTK_REG_MTK_INT_ENABLE);
+ /* flush write */
+ mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
+ }
+ if (eth->soc->dma_type & MTK_QDMA) {
+ val = mtk_r32(eth, MTK_QMTK_INT_ENABLE);
+ mtk_w32(eth, val & ~mask, MTK_QMTK_INT_ENABLE);
+ /* flush write */
+ mtk_r32(eth, MTK_QMTK_INT_ENABLE);
+ }
+}
+
+static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
+{
+ u32 val;
+
+ if (eth->soc->dma_type & MTK_PDMA) {
+ val = mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
+ mtk_reg_w32(eth, val | mask, MTK_REG_MTK_INT_ENABLE);
+ /* flush write */
+ mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
+ }
+ if (eth->soc->dma_type & MTK_QDMA) {
+ val = mtk_r32(eth, MTK_QMTK_INT_ENABLE);
+ mtk_w32(eth, val | mask, MTK_QMTK_INT_ENABLE);
+ /* flush write */
+ mtk_r32(eth, MTK_QMTK_INT_ENABLE);
+ }
+}
+
+static inline u32 mtk_irq_enabled(struct mtk_eth *eth)
+{
+ u32 enabled = 0;
+
+ if (eth->soc->dma_type & MTK_PDMA)
+ enabled |= mtk_reg_r32(eth, MTK_REG_MTK_INT_ENABLE);
+ if (eth->soc->dma_type & MTK_QDMA)
+ enabled |= mtk_r32(eth, MTK_QMTK_INT_ENABLE);
+
+ return enabled;
+}
+
+static inline void mtk_hw_set_macaddr(struct mtk_mac *mac,
+ unsigned char *macaddr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->hw->page_lock, flags);
+ mtk_w32(mac->hw, (macaddr[0] << 8) | macaddr[1], MTK_GDMA1_MAC_ADRH);
+ mtk_w32(mac->hw, (macaddr[2] << 24) | (macaddr[3] << 16) |
+ (macaddr[4] << 8) | macaddr[5],
+ MTK_GDMA1_MAC_ADRL);
+ spin_unlock_irqrestore(&mac->hw->page_lock, flags);
+}
+
+static int mtk_set_mac_address(struct net_device *dev, void *p)
+{
+ int ret = eth_mac_addr(dev, p);
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
+ if (ret)
+ return ret;
+
+ if (eth->soc->set_mac)
+ eth->soc->set_mac(mac, dev->dev_addr);
+ else
+ mtk_hw_set_macaddr(mac, p);
+
+ return 0;
+}
+
+static inline int mtk_max_frag_size(int mtu)
+{
+ /* make sure buf_size will be at least MAX_RX_LENGTH */
+ if (mtu + MTK_RX_ETH_HLEN < MAX_RX_LENGTH)
+ mtu = MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
+
+ return SKB_DATA_ALIGN(MTK_RX_HLEN + mtu) +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+}
+
+static inline int mtk_max_buf_size(int frag_size)
+{
+ int buf_size = frag_size - NET_SKB_PAD - NET_IP_ALIGN -
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+ WARN_ON(buf_size < MAX_RX_LENGTH);
+
+ return buf_size;
+}
+
+static inline void mtk_get_rxd(struct mtk_rx_dma *rxd,
+ struct mtk_rx_dma *dma_rxd)
+{
+ rxd->rxd1 = READ_ONCE(dma_rxd->rxd1);
+ rxd->rxd2 = READ_ONCE(dma_rxd->rxd2);
+ rxd->rxd3 = READ_ONCE(dma_rxd->rxd3);
+ rxd->rxd4 = READ_ONCE(dma_rxd->rxd4);
+}
+
+static inline void mtk_set_txd_pdma(struct mtk_tx_dma *txd,
+ struct mtk_tx_dma *dma_txd)
+{
+ WRITE_ONCE(dma_txd->txd1, txd->txd1);
+ WRITE_ONCE(dma_txd->txd3, txd->txd3);
+ WRITE_ONCE(dma_txd->txd4, txd->txd4);
+ /* clean dma done flag last */
+ WRITE_ONCE(dma_txd->txd2, txd->txd2);
+}
+
+static void mtk_clean_rx(struct mtk_eth *eth, struct mtk_rx_ring *ring)
+{
+ int i;
+
+ if (ring->rx_data && ring->rx_dma) {
+ for (i = 0; i < ring->rx_ring_size; i++) {
+ if (!ring->rx_data[i])
+ continue;
+ if (!ring->rx_dma[i].rxd1)
+ continue;
+ dma_unmap_single(eth->dev,
+ ring->rx_dma[i].rxd1,
+ ring->rx_buf_size,
+ DMA_FROM_DEVICE);
+ skb_free_frag(ring->rx_data[i]);
+ }
+ kfree(ring->rx_data);
+ ring->rx_data = NULL;
+ }
+
+ if (ring->rx_dma) {
+ dma_free_coherent(eth->dev,
+ ring->rx_ring_size * sizeof(*ring->rx_dma),
+ ring->rx_dma,
+ ring->rx_phys);
+ ring->rx_dma = NULL;
+ }
+}
+
+static int mtk_dma_rx_alloc(struct mtk_eth *eth, struct mtk_rx_ring *ring)
+{
+ int i, pad = 0;
+
+ ring->frag_size = mtk_max_frag_size(ETH_DATA_LEN);
+ ring->rx_buf_size = mtk_max_buf_size(ring->frag_size);
+ ring->rx_ring_size = eth->soc->dma_ring_size;
+ ring->rx_data = kcalloc(ring->rx_ring_size, sizeof(*ring->rx_data),
+ GFP_KERNEL);
+ if (!ring->rx_data)
+ goto no_rx_mem;
+
+ for (i = 0; i < ring->rx_ring_size; i++) {
+ ring->rx_data[i] = netdev_alloc_frag(ring->frag_size);
+ if (!ring->rx_data[i])
+ goto no_rx_mem;
+ }
+
+ ring->rx_dma = dma_alloc_coherent(eth->dev,
+ ring->rx_ring_size * sizeof(*ring->rx_dma),
+ &ring->rx_phys,
+ GFP_ATOMIC | __GFP_ZERO);
+ if (!ring->rx_dma)
+ goto no_rx_mem;
+
+ if (!eth->soc->rx_2b_offset)
+ pad = NET_IP_ALIGN;
+
+ for (i = 0; i < ring->rx_ring_size; i++) {
+ dma_addr_t dma_addr = dma_map_single(eth->dev,
+ ring->rx_data[i] + NET_SKB_PAD + pad,
+ ring->rx_buf_size,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
+ goto no_rx_mem;
+ ring->rx_dma[i].rxd1 = (unsigned int)dma_addr;
+
+ if (eth->soc->rx_sg_dma)
+ ring->rx_dma[i].rxd2 = RX_DMA_PLEN0(ring->rx_buf_size);
+ else
+ ring->rx_dma[i].rxd2 = RX_DMA_LSO;
+ }
+ ring->rx_calc_idx = ring->rx_ring_size - 1;
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+
+ return 0;
+
+no_rx_mem:
+ return -ENOMEM;
+}
+
+static void mtk_txd_unmap(struct device *dev, struct mtk_tx_buf *tx_buf)
+{
+ if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) {
+ dma_unmap_single(dev,
+ dma_unmap_addr(tx_buf, dma_addr0),
+ dma_unmap_len(tx_buf, dma_len0),
+ DMA_TO_DEVICE);
+ } else if (tx_buf->flags & MTK_TX_FLAGS_PAGE0) {
+ dma_unmap_page(dev,
+ dma_unmap_addr(tx_buf, dma_addr0),
+ dma_unmap_len(tx_buf, dma_len0),
+ DMA_TO_DEVICE);
+ }
+ if (tx_buf->flags & MTK_TX_FLAGS_PAGE1)
+ dma_unmap_page(dev,
+ dma_unmap_addr(tx_buf, dma_addr1),
+ dma_unmap_len(tx_buf, dma_len1),
+ DMA_TO_DEVICE);
+
+ tx_buf->flags = 0;
+ if (tx_buf->skb && (tx_buf->skb != (struct sk_buff *)DMA_DUMMY_DESC))
+ dev_kfree_skb_any(tx_buf->skb);
+ tx_buf->skb = NULL;
+}
+
+static void mtk_pdma_tx_clean(struct mtk_eth *eth)
+{
+ struct mtk_tx_ring *ring = ð->tx_ring;
+ int i;
+
+ if (ring->tx_buf) {
+ for (i = 0; i < ring->tx_ring_size; i++)
+ mtk_txd_unmap(eth->dev, &ring->tx_buf[i]);
+ kfree(ring->tx_buf);
+ ring->tx_buf = NULL;
+ }
+
+ if (ring->tx_dma) {
+ dma_free_coherent(eth->dev,
+ ring->tx_ring_size * sizeof(*ring->tx_dma),
+ ring->tx_dma,
+ ring->tx_phys);
+ ring->tx_dma = NULL;
+ }
+}
+
+static void mtk_qdma_tx_clean(struct mtk_eth *eth)
+{
+ struct mtk_tx_ring *ring = ð->tx_ring;
+ int i;
+
+ if (ring->tx_buf) {
+ for (i = 0; i < ring->tx_ring_size; i++)
+ mtk_txd_unmap(eth->dev, &ring->tx_buf[i]);
+ kfree(ring->tx_buf);
+ ring->tx_buf = NULL;
+ }
+
+ if (ring->tx_dma) {
+ dma_free_coherent(eth->dev,
+ ring->tx_ring_size * sizeof(*ring->tx_dma),
+ ring->tx_dma,
+ ring->tx_phys);
+ ring->tx_dma = NULL;
+ }
+}
+
+void mtk_stats_update_mac(struct mtk_mac *mac)
+{
+ struct mtk_hw_stats *hw_stats = mac->hw_stats;
+ unsigned int base = mtk_reg_table[MTK_REG_MTK_COUNTER_BASE];
+ u64 stats;
+
+ base += hw_stats->reg_offset;
+
+ u64_stats_update_begin(&hw_stats->syncp);
+
+ if (mac->hw->soc->new_stats) {
+ hw_stats->rx_bytes += mtk_r32(mac->hw, base);
+ stats = mtk_r32(mac->hw, base + 0x04);
+ if (stats)
+ hw_stats->rx_bytes += (stats << 32);
+ hw_stats->rx_packets += mtk_r32(mac->hw, base + 0x08);
+ hw_stats->rx_overflow += mtk_r32(mac->hw, base + 0x10);
+ hw_stats->rx_fcs_errors += mtk_r32(mac->hw, base + 0x14);
+ hw_stats->rx_short_errors += mtk_r32(mac->hw, base + 0x18);
+ hw_stats->rx_long_errors += mtk_r32(mac->hw, base + 0x1c);
+ hw_stats->rx_checksum_errors += mtk_r32(mac->hw, base + 0x20);
+ hw_stats->rx_flow_control_packets +=
+ mtk_r32(mac->hw, base + 0x24);
+ hw_stats->tx_skip += mtk_r32(mac->hw, base + 0x28);
+ hw_stats->tx_collisions += mtk_r32(mac->hw, base + 0x2c);
+ hw_stats->tx_bytes += mtk_r32(mac->hw, base + 0x30);
+ stats = mtk_r32(mac->hw, base + 0x34);
+ if (stats)
+ hw_stats->tx_bytes += (stats << 32);
+ hw_stats->tx_packets += mtk_r32(mac->hw, base + 0x38);
+ } else {
+ hw_stats->tx_bytes += mtk_r32(mac->hw, base);
+ hw_stats->tx_packets += mtk_r32(mac->hw, base + 0x04);
+ hw_stats->tx_skip += mtk_r32(mac->hw, base + 0x08);
+ hw_stats->tx_collisions += mtk_r32(mac->hw, base + 0x0c);
+ hw_stats->rx_bytes += mtk_r32(mac->hw, base + 0x20);
+ hw_stats->rx_packets += mtk_r32(mac->hw, base + 0x24);
+ hw_stats->rx_overflow += mtk_r32(mac->hw, base + 0x28);
+ hw_stats->rx_fcs_errors += mtk_r32(mac->hw, base + 0x2c);
+ hw_stats->rx_short_errors += mtk_r32(mac->hw, base + 0x30);
+ hw_stats->rx_long_errors += mtk_r32(mac->hw, base + 0x34);
+ hw_stats->rx_checksum_errors += mtk_r32(mac->hw, base + 0x38);
+ hw_stats->rx_flow_control_packets +=
+ mtk_r32(mac->hw, base + 0x3c);
+ }
+
+ u64_stats_update_end(&hw_stats->syncp);
+}
+
+static void mtk_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *storage)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_hw_stats *hw_stats = mac->hw_stats;
+ unsigned int base = mtk_reg_table[MTK_REG_MTK_COUNTER_BASE];
+ unsigned int start;
+
+ if (!base) {
+ netdev_stats_to_stats64(storage, &dev->stats);
+ return;
+ }
+
+ if (netif_running(dev) && netif_device_present(dev)) {
+ if (spin_trylock(&hw_stats->stats_lock)) {
+ mtk_stats_update_mac(mac);
+ spin_unlock(&hw_stats->stats_lock);
+ }
+ }
+
+ do {
+ start = u64_stats_fetch_begin_irq(&hw_stats->syncp);
+ storage->rx_packets = hw_stats->rx_packets;
+ storage->tx_packets = hw_stats->tx_packets;
+ storage->rx_bytes = hw_stats->rx_bytes;
+ storage->tx_bytes = hw_stats->tx_bytes;
+ storage->collisions = hw_stats->tx_collisions;
+ storage->rx_length_errors = hw_stats->rx_short_errors +
+ hw_stats->rx_long_errors;
+ storage->rx_over_errors = hw_stats->rx_overflow;
+ storage->rx_crc_errors = hw_stats->rx_fcs_errors;
+ storage->rx_errors = hw_stats->rx_checksum_errors;
+ storage->tx_aborted_errors = hw_stats->tx_skip;
+ } while (u64_stats_fetch_retry_irq(&hw_stats->syncp, start));
+
+ storage->tx_errors = dev->stats.tx_errors;
+ storage->rx_dropped = dev->stats.rx_dropped;
+ storage->tx_dropped = dev->stats.tx_dropped;
+}
+
+static int mtk_vlan_rx_add_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ u32 idx = (vid & 0xf);
+ u32 vlan_cfg;
+
+ if (!((mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE]) &&
+ (dev->features & NETIF_F_HW_VLAN_CTAG_TX)))
+ return 0;
+
+ if (test_bit(idx, ð->vlan_map)) {
+ netdev_warn(dev, "disable tx vlan offload\n");
+ dev->wanted_features &= ~NETIF_F_HW_VLAN_CTAG_TX;
+ netdev_update_features(dev);
+ } else {
+ vlan_cfg = mtk_r32(eth,
+ mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE] +
+ ((idx >> 1) << 2));
+ if (idx & 0x1) {
+ vlan_cfg &= 0xffff;
+ vlan_cfg |= (vid << 16);
+ } else {
+ vlan_cfg &= 0xffff0000;
+ vlan_cfg |= vid;
+ }
+ mtk_w32(eth,
+ vlan_cfg, mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE] +
+ ((idx >> 1) << 2));
+ set_bit(idx, ð->vlan_map);
+ }
+
+ return 0;
+}
+
+static int mtk_vlan_rx_kill_vid(struct net_device *dev,
+ __be16 proto, u16 vid)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ u32 idx = (vid & 0xf);
+
+ if (!((mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE]) &&
+ (dev->features & NETIF_F_HW_VLAN_CTAG_TX)))
+ return 0;
+
+ clear_bit(idx, ð->vlan_map);
+
+ return 0;
+}
+
+static inline u32 mtk_pdma_empty_txd(struct mtk_tx_ring *ring)
+{
+ barrier();
+ return (u32)(ring->tx_ring_size -
+ ((ring->tx_next_idx - ring->tx_free_idx) &
+ (ring->tx_ring_size - 1)));
+}
+
+static int mtk_skb_padto(struct sk_buff *skb, struct mtk_eth *eth)
+{
+ unsigned int len;
+ int ret;
+
+ if (unlikely(skb->len >= VLAN_ETH_ZLEN))
+ return 0;
+
+ if (eth->soc->padding_64b && !eth->soc->padding_bug)
+ return 0;
+
+ if (skb_vlan_tag_present(skb))
+ len = ETH_ZLEN;
+ else if (skb->protocol == cpu_to_be16(ETH_P_8021Q))
+ len = VLAN_ETH_ZLEN;
+ else if (!eth->soc->padding_64b)
+ len = ETH_ZLEN;
+ else
+ return 0;
+
+ if (skb->len >= len)
+ return 0;
+
+ ret = skb_pad(skb, len - skb->len);
+ if (ret < 0)
+ return ret;
+ skb->len = len;
+ skb_set_tail_pointer(skb, len);
+
+ return ret;
+}
+
+static int mtk_pdma_tx_map(struct sk_buff *skb, struct net_device *dev,
+ int tx_num, struct mtk_tx_ring *ring, bool gso)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ struct skb_frag_struct *frag;
+ struct mtk_tx_dma txd, *ptxd;
+ struct mtk_tx_buf *tx_buf;
+ int i, j, k, frag_size, frag_map_size, offset;
+ dma_addr_t mapped_addr;
+ unsigned int nr_frags;
+ u32 def_txd4;
+
+ if (mtk_skb_padto(skb, eth)) {
+ netif_warn(eth, tx_err, dev, "tx padding failed!\n");
+ return -1;
+ }
+
+ tx_buf = &ring->tx_buf[ring->tx_next_idx];
+ memset(tx_buf, 0, sizeof(*tx_buf));
+ memset(&txd, 0, sizeof(txd));
+ nr_frags = skb_shinfo(skb)->nr_frags;
+
+ /* init tx descriptor */
+ def_txd4 = eth->soc->txd4;
+ txd.txd4 = def_txd4;
+
+ if (eth->soc->mac_count > 1)
+ txd.txd4 |= (mac->id + 1) << TX_DMA_FPORT_SHIFT;
+
+ if (gso)
+ txd.txd4 |= TX_DMA_TSO;
+
+ /* TX Checksum offload */
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ txd.txd4 |= TX_DMA_CHKSUM;
+
+ /* VLAN header offload */
+ if (skb_vlan_tag_present(skb)) {
+ u16 tag = skb_vlan_tag_get(skb);
+
+ txd.txd4 |= TX_DMA_INS_VLAN |
+ ((tag >> VLAN_PRIO_SHIFT) << 4) |
+ (tag & 0xF);
+ }
+
+ mapped_addr = dma_map_single(&dev->dev, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
+ return -1;
+
+ txd.txd1 = mapped_addr;
+ txd.txd2 = TX_DMA_PLEN0(skb_headlen(skb));
+
+ tx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
+ dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
+ dma_unmap_len_set(tx_buf, dma_len0, skb_headlen(skb));
+
+ /* TX SG offload */
+ j = ring->tx_next_idx;
+ k = 0;
+ for (i = 0; i < nr_frags; i++) {
+ offset = 0;
+ frag = &skb_shinfo(skb)->frags[i];
+ frag_size = skb_frag_size(frag);
+
+ while (frag_size > 0) {
+ frag_map_size = min(frag_size, TX_DMA_BUF_LEN);
+ mapped_addr = skb_frag_dma_map(&dev->dev, frag, offset,
+ frag_map_size,
+ DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
+ goto err_dma;
+
+ if (k & 0x1) {
+ j = NEXT_TX_DESP_IDX(j);
+ txd.txd1 = mapped_addr;
+ txd.txd2 = TX_DMA_PLEN0(frag_map_size);
+ txd.txd4 = def_txd4;
+
+ tx_buf = &ring->tx_buf[j];
+ memset(tx_buf, 0, sizeof(*tx_buf));
+
+ tx_buf->flags |= MTK_TX_FLAGS_PAGE0;
+ dma_unmap_addr_set(tx_buf, dma_addr0,
+ mapped_addr);
+ dma_unmap_len_set(tx_buf, dma_len0,
+ frag_map_size);
+ } else {
+ txd.txd3 = mapped_addr;
+ txd.txd2 |= TX_DMA_PLEN1(frag_map_size);
+
+ tx_buf->skb = (struct sk_buff *)DMA_DUMMY_DESC;
+ tx_buf->flags |= MTK_TX_FLAGS_PAGE1;
+ dma_unmap_addr_set(tx_buf, dma_addr1,
+ mapped_addr);
+ dma_unmap_len_set(tx_buf, dma_len1,
+ frag_map_size);
+
+ if (!((i == (nr_frags - 1)) &&
+ (frag_map_size == frag_size))) {
+ mtk_set_txd_pdma(&txd,
+ &ring->tx_dma[j]);
+ memset(&txd, 0, sizeof(txd));
+ }
+ }
+ frag_size -= frag_map_size;
+ offset += frag_map_size;
+ k++;
+ }
+ }
+
+ /* set last segment */
+ if (k & 0x1)
+ txd.txd2 |= TX_DMA_LS1;
+ else
+ txd.txd2 |= TX_DMA_LS0;
+ mtk_set_txd_pdma(&txd, &ring->tx_dma[j]);
+
+ /* store skb to cleanup */
+ tx_buf->skb = skb;
+
+ netdev_sent_queue(dev, skb->len);
+ skb_tx_timestamp(skb);
+
+ ring->tx_next_idx = NEXT_TX_DESP_IDX(j);
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+ atomic_set(&ring->tx_free_count, mtk_pdma_empty_txd(ring));
+
+ if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) || !skb->xmit_more)
+ mtk_reg_w32(eth, ring->tx_next_idx, MTK_REG_TX_CTX_IDX0);
+
+ return 0;
+
+err_dma:
+ j = ring->tx_next_idx;
+ for (i = 0; i < tx_num; i++) {
+ ptxd = &ring->tx_dma[j];
+ tx_buf = &ring->tx_buf[j];
+
+ /* unmap dma */
+ mtk_txd_unmap(&dev->dev, tx_buf);
+
+ ptxd->txd2 = TX_DMA_DESP2_DEF;
+ j = NEXT_TX_DESP_IDX(j);
+ }
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+ return -1;
+}
+
+/* the qdma core needs scratch memory to be setup */
+static int mtk_init_fq_dma(struct mtk_eth *eth)
+{
+ unsigned int phy_ring_head, phy_ring_tail;
+ int cnt = eth->soc->dma_ring_size;
+ dma_addr_t dma_addr;
+ int i;
+
+ eth->scratch_ring = dma_alloc_coherent(eth->dev,
+ cnt * sizeof(struct mtk_tx_dma),
+ &phy_ring_head,
+ GFP_ATOMIC | __GFP_ZERO);
+ if (unlikely(!eth->scratch_ring))
+ return -ENOMEM;
+
+ eth->scratch_head = kcalloc(cnt, QDMA_PAGE_SIZE,
+ GFP_KERNEL);
+ dma_addr = dma_map_single(eth->dev,
+ eth->scratch_head, cnt * QDMA_PAGE_SIZE,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(eth->dev, dma_addr)))
+ return -ENOMEM;
+
+ memset(eth->scratch_ring, 0x0, sizeof(struct mtk_tx_dma) * cnt);
+ phy_ring_tail = phy_ring_head + (sizeof(struct mtk_tx_dma) * (cnt - 1));
+
+ for (i = 0; i < cnt; i++) {
+ eth->scratch_ring[i].txd1 = (dma_addr + (i * QDMA_PAGE_SIZE));
+ if (i < cnt - 1)
+ eth->scratch_ring[i].txd2 = (phy_ring_head +
+ ((i + 1) * sizeof(struct mtk_tx_dma)));
+ eth->scratch_ring[i].txd3 = TX_QDMA_SDL(QDMA_PAGE_SIZE);
+ }
+
+ mtk_w32(eth, phy_ring_head, MTK_QDMA_FQ_HEAD);
+ mtk_w32(eth, phy_ring_tail, MTK_QDMA_FQ_TAIL);
+ mtk_w32(eth, (cnt << 16) | cnt, MTK_QDMA_FQ_CNT);
+ mtk_w32(eth, QDMA_PAGE_SIZE << 16, MTK_QDMA_FQ_BLEN);
+
+ return 0;
+}
+
+static void *mtk_qdma_phys_to_virt(struct mtk_tx_ring *ring, u32 desc)
+{
+ void *ret = ring->tx_dma;
+
+ return ret + (desc - ring->tx_phys);
+}
+
+static struct mtk_tx_dma *mtk_tx_next_qdma(struct mtk_tx_ring *ring,
+ struct mtk_tx_dma *txd)
+{
+ return mtk_qdma_phys_to_virt(ring, txd->txd2);
+}
+
+static struct mtk_tx_buf *mtk_desc_to_tx_buf(struct mtk_tx_ring *ring,
+ struct mtk_tx_dma *txd)
+{
+ int idx = txd - ring->tx_dma;
+
+ return &ring->tx_buf[idx];
+}
+
+static int mtk_qdma_tx_map(struct sk_buff *skb, struct net_device *dev,
+ int tx_num, struct mtk_tx_ring *ring, bool gso)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ struct mtk_tx_dma *itxd, *txd;
+ struct mtk_tx_buf *tx_buf;
+ dma_addr_t mapped_addr;
+ unsigned int nr_frags;
+ int i, n_desc = 1;
+ u32 txd4 = eth->soc->txd4;
+
+ itxd = ring->tx_next_free;
+ if (itxd == ring->tx_last_free)
+ return -ENOMEM;
+
+ if (eth->soc->mac_count > 1)
+ txd4 |= (mac->id + 1) << TX_DMA_FPORT_SHIFT;
+
+ tx_buf = mtk_desc_to_tx_buf(ring, itxd);
+ memset(tx_buf, 0, sizeof(*tx_buf));
+
+ if (gso)
+ txd4 |= TX_DMA_TSO;
+
+ /* TX Checksum offload */
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ txd4 |= TX_DMA_CHKSUM;
+
+ /* VLAN header offload */
+ if (skb_vlan_tag_present(skb))
+ txd4 |= TX_DMA_INS_VLAN_MT7621 | skb_vlan_tag_get(skb);
+
+ mapped_addr = dma_map_single(&dev->dev, skb->data,
+ skb_headlen(skb), DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
+ return -ENOMEM;
+
+ WRITE_ONCE(itxd->txd1, mapped_addr);
+ tx_buf->flags |= MTK_TX_FLAGS_SINGLE0;
+ dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
+ dma_unmap_len_set(tx_buf, dma_len0, skb_headlen(skb));
+
+ /* TX SG offload */
+ txd = itxd;
+ nr_frags = skb_shinfo(skb)->nr_frags;
+ for (i = 0; i < nr_frags; i++) {
+ struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i];
+ unsigned int offset = 0;
+ int frag_size = skb_frag_size(frag);
+
+ while (frag_size) {
+ bool last_frag = false;
+ unsigned int frag_map_size;
+
+ txd = mtk_tx_next_qdma(ring, txd);
+ if (txd == ring->tx_last_free)
+ goto err_dma;
+
+ n_desc++;
+ frag_map_size = min(frag_size, TX_DMA_BUF_LEN);
+ mapped_addr = skb_frag_dma_map(&dev->dev, frag, offset,
+ frag_map_size,
+ DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(&dev->dev, mapped_addr)))
+ goto err_dma;
+
+ if (i == nr_frags - 1 &&
+ (frag_size - frag_map_size) == 0)
+ last_frag = true;
+
+ WRITE_ONCE(txd->txd1, mapped_addr);
+ WRITE_ONCE(txd->txd3, (QDMA_TX_SWC |
+ TX_DMA_PLEN0(frag_map_size) |
+ last_frag * TX_DMA_LS0) |
+ mac->id);
+ WRITE_ONCE(txd->txd4, 0);
+
+ tx_buf->skb = (struct sk_buff *)DMA_DUMMY_DESC;
+ tx_buf = mtk_desc_to_tx_buf(ring, txd);
+ memset(tx_buf, 0, sizeof(*tx_buf));
+
+ tx_buf->flags |= MTK_TX_FLAGS_PAGE0;
+ dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr);
+ dma_unmap_len_set(tx_buf, dma_len0, frag_map_size);
+ frag_size -= frag_map_size;
+ offset += frag_map_size;
+ }
+ }
+
+ /* store skb to cleanup */
+ tx_buf->skb = skb;
+
+ WRITE_ONCE(itxd->txd4, txd4);
+ WRITE_ONCE(itxd->txd3, (QDMA_TX_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
+ (!nr_frags * TX_DMA_LS0)));
+
+ netdev_sent_queue(dev, skb->len);
+ skb_tx_timestamp(skb);
+
+ ring->tx_next_free = mtk_tx_next_qdma(ring, txd);
+ atomic_sub(n_desc, &ring->tx_free_count);
+
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+
+ if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) || !skb->xmit_more)
+ mtk_w32(eth, txd->txd2, MTK_QTX_CTX_PTR);
+
+ return 0;
+
+err_dma:
+ do {
+ tx_buf = mtk_desc_to_tx_buf(ring, txd);
+
+ /* unmap dma */
+ mtk_txd_unmap(&dev->dev, tx_buf);
+
+ itxd->txd3 = TX_DMA_DESP2_DEF;
+ itxd = mtk_tx_next_qdma(ring, itxd);
+ } while (itxd != txd);
+
+ return -ENOMEM;
+}
+
+static inline int mtk_cal_txd_req(struct sk_buff *skb)
+{
+ int i, nfrags;
+ struct skb_frag_struct *frag;
+
+ nfrags = 1;
+ if (skb_is_gso(skb)) {
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ frag = &skb_shinfo(skb)->frags[i];
+ nfrags += DIV_ROUND_UP(frag->size, TX_DMA_BUF_LEN);
+ }
+ } else {
+ nfrags += skb_shinfo(skb)->nr_frags;
+ }
+
+ return DIV_ROUND_UP(nfrags, 2);
+}
+
+static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ struct mtk_tx_ring *ring = ð->tx_ring;
+ struct net_device_stats *stats = &dev->stats;
+ int tx_num;
+ int len = skb->len;
+ bool gso = false;
+
+ tx_num = mtk_cal_txd_req(skb);
+ if (unlikely(atomic_read(&ring->tx_free_count) <= tx_num)) {
+ netif_stop_queue(dev);
+ netif_err(eth, tx_queued, dev,
+ "Tx Ring full when queue awake!\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ /* TSO: fill MSS info in tcp checksum field */
+ if (skb_is_gso(skb)) {
+ if (skb_cow_head(skb, 0)) {
+ netif_warn(eth, tx_err, dev,
+ "GSO expand head fail.\n");
+ goto drop;
+ }
+
+ if (skb_shinfo(skb)->gso_type &
+ (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) {
+ gso = true;
+ tcp_hdr(skb)->check = htons(skb_shinfo(skb)->gso_size);
+ }
+ }
+
+ if (ring->tx_map(skb, dev, tx_num, ring, gso) < 0)
+ goto drop;
+
+ stats->tx_packets++;
+ stats->tx_bytes += len;
+
+ if (unlikely(atomic_read(&ring->tx_free_count) <= ring->tx_thresh)) {
+ netif_stop_queue(dev);
+ smp_mb();
+ if (unlikely(atomic_read(&ring->tx_free_count) >
+ ring->tx_thresh))
+ netif_wake_queue(dev);
+ }
+
+ return NETDEV_TX_OK;
+
+drop:
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+static int mtk_poll_rx(struct napi_struct *napi, int budget,
+ struct mtk_eth *eth, u32 rx_intr)
+{
+ struct mtk_soc_data *soc = eth->soc;
+ struct mtk_rx_ring *ring = ð->rx_ring[0];
+ int idx = ring->rx_calc_idx;
+ u32 checksum_bit;
+ struct sk_buff *skb;
+ u8 *data, *new_data;
+ struct mtk_rx_dma *rxd, trxd;
+ int done = 0, pad;
+
+ if (eth->soc->hw_features & NETIF_F_RXCSUM)
+ checksum_bit = soc->checksum_bit;
+ else
+ checksum_bit = 0;
+
+ if (eth->soc->rx_2b_offset)
+ pad = 0;
+ else
+ pad = NET_IP_ALIGN;
+
+ while (done < budget) {
+ struct net_device *netdev;
+ unsigned int pktlen;
+ dma_addr_t dma_addr;
+ int mac = 0;
+
+ idx = NEXT_RX_DESP_IDX(idx);
+ rxd = &ring->rx_dma[idx];
+ data = ring->rx_data[idx];
+
+ mtk_get_rxd(&trxd, rxd);
+ if (!(trxd.rxd2 & RX_DMA_DONE))
+ break;
+
+ /* find out which mac the packet come from. values start at 1 */
+ if (eth->soc->mac_count > 1) {
+ mac = (trxd.rxd4 >> RX_DMA_FPORT_SHIFT) &
+ RX_DMA_FPORT_MASK;
+ mac--;
+ if (mac < 0 || mac >= eth->soc->mac_count)
+ goto release_desc;
+ }
+
+ netdev = eth->netdev[mac];
+
+ /* alloc new buffer */
+ new_data = napi_alloc_frag(ring->frag_size);
+ if (unlikely(!new_data || !netdev)) {
+ netdev->stats.rx_dropped++;
+ goto release_desc;
+ }
+ dma_addr = dma_map_single(&netdev->dev,
+ new_data + NET_SKB_PAD + pad,
+ ring->rx_buf_size,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(&netdev->dev, dma_addr))) {
+ skb_free_frag(new_data);
+ goto release_desc;
+ }
+
+ /* receive data */
+ skb = build_skb(data, ring->frag_size);
+ if (unlikely(!skb)) {
+ put_page(virt_to_head_page(new_data));
+ goto release_desc;
+ }
+ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
+
+ dma_unmap_single(&netdev->dev, trxd.rxd1,
+ ring->rx_buf_size, DMA_FROM_DEVICE);
+ pktlen = RX_DMA_GET_PLEN0(trxd.rxd2);
+ skb->dev = netdev;
+ skb_put(skb, pktlen);
+ if (trxd.rxd4 & checksum_bit)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ else
+ skb_checksum_none_assert(skb);
+ skb->protocol = eth_type_trans(skb, netdev);
+
+ netdev->stats.rx_packets++;
+ netdev->stats.rx_bytes += pktlen;
+
+ if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX &&
+ RX_DMA_VID(trxd.rxd3))
+ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+ RX_DMA_VID(trxd.rxd3));
+ napi_gro_receive(napi, skb);
+
+ ring->rx_data[idx] = new_data;
+ rxd->rxd1 = (unsigned int)dma_addr;
+
+release_desc:
+ if (eth->soc->rx_sg_dma)
+ rxd->rxd2 = RX_DMA_PLEN0(ring->rx_buf_size);
+ else
+ rxd->rxd2 = RX_DMA_LSO;
+
+ ring->rx_calc_idx = idx;
+ /* make sure that all changes to the dma ring are flushed before
+ * we continue
+ */
+ wmb();
+ if (eth->soc->dma_type == MTK_QDMA)
+ mtk_w32(eth, ring->rx_calc_idx, MTK_QRX_CRX_IDX0);
+ else
+ mtk_reg_w32(eth, ring->rx_calc_idx,
+ MTK_REG_RX_CALC_IDX0);
+ done++;
+ }
+
+ if (done < budget)
+ mtk_irq_ack(eth, rx_intr);
+
+ return done;
+}
+
+static int mtk_pdma_tx_poll(struct mtk_eth *eth, int budget, bool *tx_again)
+{
+ struct sk_buff *skb;
+ struct mtk_tx_buf *tx_buf;
+ int done = 0;
+ u32 idx, hwidx;
+ struct mtk_tx_ring *ring = ð->tx_ring;
+ unsigned int bytes = 0;
+
+ idx = ring->tx_free_idx;
+ hwidx = mtk_reg_r32(eth, MTK_REG_TX_DTX_IDX0);
+
+ while ((idx != hwidx) && budget) {
+ tx_buf = &ring->tx_buf[idx];
+ skb = tx_buf->skb;
+
+ if (!skb)
+ break;
+
+ if (skb != (struct sk_buff *)DMA_DUMMY_DESC) {
+ bytes += skb->len;
+ done++;
+ budget--;
+ }
+ mtk_txd_unmap(eth->dev, tx_buf);
+ idx = NEXT_TX_DESP_IDX(idx);
+ }
+ ring->tx_free_idx = idx;
+ atomic_set(&ring->tx_free_count, mtk_pdma_empty_txd(ring));
+
+ /* read hw index again make sure no new tx packet */
+ if (idx != hwidx || idx != mtk_reg_r32(eth, MTK_REG_TX_DTX_IDX0))
+ *tx_again = 1;
+
+ if (done)
+ netdev_completed_queue(*eth->netdev, done, bytes);
+
+ return done;
+}
+
+static int mtk_qdma_tx_poll(struct mtk_eth *eth, int budget, bool *tx_again)
+{
+ struct mtk_tx_ring *ring = ð->tx_ring;
+ struct mtk_tx_dma *desc;
+ struct sk_buff *skb;
+ struct mtk_tx_buf *tx_buf;
+ int total = 0, done[MTK_MAX_DEVS];
+ unsigned int bytes[MTK_MAX_DEVS];
+ u32 cpu, dma;
+ static int condition;
+ int i;
+
+ memset(done, 0, sizeof(done));
+ memset(bytes, 0, sizeof(bytes));
+
+ cpu = mtk_r32(eth, MTK_QTX_CRX_PTR);
+ dma = mtk_r32(eth, MTK_QTX_DRX_PTR);
+
+ desc = mtk_qdma_phys_to_virt(ring, cpu);
+
+ while ((cpu != dma) && budget) {
+ u32 next_cpu = desc->txd2;
+ int mac;
+
+ desc = mtk_tx_next_qdma(ring, desc);
+ if ((desc->txd3 & QDMA_TX_OWNER_CPU) == 0)
+ break;
+
+ mac = (desc->txd4 >> TX_DMA_FPORT_SHIFT) &
+ TX_DMA_FPORT_MASK;
+ mac--;
+
+ tx_buf = mtk_desc_to_tx_buf(ring, desc);
+ skb = tx_buf->skb;
+ if (!skb) {
+ condition = 1;
+ break;
+ }
+
+ if (skb != (struct sk_buff *)DMA_DUMMY_DESC) {
+ bytes[mac] += skb->len;
+ done[mac]++;
+ budget--;
+ }
+ mtk_txd_unmap(eth->dev, tx_buf);
+
+ ring->tx_last_free->txd2 = next_cpu;
+ ring->tx_last_free = desc;
+ atomic_inc(&ring->tx_free_count);
+
+ cpu = next_cpu;
+ }
+
+ mtk_w32(eth, cpu, MTK_QTX_CRX_PTR);
+
+ /* read hw index again make sure no new tx packet */
+ if (cpu != dma || cpu != mtk_r32(eth, MTK_QTX_DRX_PTR))
+ *tx_again = true;
+
+ for (i = 0; i < eth->soc->mac_count; i++) {
+ if (!done[i])
+ continue;
+ netdev_completed_queue(eth->netdev[i], done[i], bytes[i]);
+ total += done[i];
+ }
+
+ return total;
+}
+
+static int mtk_poll_tx(struct mtk_eth *eth, int budget, u32 tx_intr,
+ bool *tx_again)
+{
+ struct mtk_tx_ring *ring = ð->tx_ring;
+ struct net_device *netdev = eth->netdev[0];
+ int done;
+
+ done = eth->tx_ring.tx_poll(eth, budget, tx_again);
+ if (!*tx_again)
+ mtk_irq_ack(eth, tx_intr);
+
+ if (!done)
+ return 0;
+
+ smp_mb();
+ if (unlikely(!netif_queue_stopped(netdev)))
+ return done;
+
+ if (atomic_read(&ring->tx_free_count) > ring->tx_thresh)
+ netif_wake_queue(netdev);
+
+ return done;
+}
+
+static void mtk_stats_update(struct mtk_eth *eth)
+{
+ int i;
+
+ for (i = 0; i < eth->soc->mac_count; i++) {
+ if (!eth->mac[i] || !eth->mac[i]->hw_stats)
+ continue;
+ if (spin_trylock(ð->mac[i]->hw_stats->stats_lock)) {
+ mtk_stats_update_mac(eth->mac[i]);
+ spin_unlock(ð->mac[i]->hw_stats->stats_lock);
+ }
+ }
+}
+
+static int mtk_poll(struct napi_struct *napi, int budget)
+{
+ struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
+ u32 status, mtk_status, mask, tx_intr, rx_intr, status_intr;
+ int tx_done, rx_done;
+ bool tx_again = false;
+
+ status = mtk_irq_pending(eth);
+ mtk_status = mtk_irq_pending_status(eth);
+ tx_intr = eth->soc->tx_int;
+ rx_intr = eth->soc->rx_int;
+ status_intr = eth->soc->status_int;
+ tx_done = 0;
+ rx_done = 0;
+ tx_again = 0;
+
+ if (status & tx_intr)
+ tx_done = mtk_poll_tx(eth, budget, tx_intr, &tx_again);
+
+ if (status & rx_intr)
+ rx_done = mtk_poll_rx(napi, budget, eth, rx_intr);
+
+ if (unlikely(mtk_status & status_intr)) {
+ mtk_stats_update(eth);
+ mtk_irq_ack_status(eth, status_intr);
+ }
+
+ if (unlikely(netif_msg_intr(eth))) {
+ mask = mtk_irq_enabled(eth);
+ netdev_info(eth->netdev[0],
+ "done tx %d, rx %d, intr 0x%08x/0x%x\n",
+ tx_done, rx_done, status, mask);
+ }
+
+ if (tx_again || rx_done == budget)
+ return budget;
+
+ status = mtk_irq_pending(eth);
+ if (status & (tx_intr | rx_intr))
+ return budget;
+
+ napi_complete(napi);
+ mtk_irq_enable(eth, tx_intr | rx_intr);
+
+ return rx_done;
+}
+
+static int mtk_pdma_tx_alloc(struct mtk_eth *eth)
+{
+ int i;
+ struct mtk_tx_ring *ring = ð->tx_ring;
+
+ ring->tx_ring_size = eth->soc->dma_ring_size;
+ ring->tx_free_idx = 0;
+ ring->tx_next_idx = 0;
+ ring->tx_thresh = max((unsigned long)ring->tx_ring_size >> 2,
+ MAX_SKB_FRAGS);
+
+ ring->tx_buf = kcalloc(ring->tx_ring_size, sizeof(*ring->tx_buf),
+ GFP_KERNEL);
+ if (!ring->tx_buf)
+ goto no_tx_mem;
+
+ ring->tx_dma = dma_alloc_coherent(eth->dev,
+ ring->tx_ring_size * sizeof(*ring->tx_dma),
+ &ring->tx_phys,
+ GFP_ATOMIC | __GFP_ZERO);
+ if (!ring->tx_dma)
+ goto no_tx_mem;
+
+ for (i = 0; i < ring->tx_ring_size; i++) {
+ ring->tx_dma[i].txd2 = TX_DMA_DESP2_DEF;
+ ring->tx_dma[i].txd4 = eth->soc->txd4;
+ }
+
+ atomic_set(&ring->tx_free_count, mtk_pdma_empty_txd(ring));
+ ring->tx_map = mtk_pdma_tx_map;
+ ring->tx_poll = mtk_pdma_tx_poll;
+ ring->tx_clean = mtk_pdma_tx_clean;
+
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+
+ mtk_reg_w32(eth, ring->tx_phys, MTK_REG_TX_BASE_PTR0);
+ mtk_reg_w32(eth, ring->tx_ring_size, MTK_REG_TX_MAX_CNT0);
+ mtk_reg_w32(eth, 0, MTK_REG_TX_CTX_IDX0);
+ mtk_reg_w32(eth, MTK_PST_DTX_IDX0, MTK_REG_PDMA_RST_CFG);
+
+ return 0;
+
+no_tx_mem:
+ return -ENOMEM;
+}
+
+static int mtk_qdma_tx_alloc_tx(struct mtk_eth *eth)
+{
+ struct mtk_tx_ring *ring = ð->tx_ring;
+ int i, sz = sizeof(*ring->tx_dma);
+
+ ring->tx_ring_size = eth->soc->dma_ring_size;
+ ring->tx_buf = kcalloc(ring->tx_ring_size, sizeof(*ring->tx_buf),
+ GFP_KERNEL);
+ if (!ring->tx_buf)
+ goto no_tx_mem;
+
+ ring->tx_dma = dma_alloc_coherent(eth->dev,
+ ring->tx_ring_size * sz,
+ &ring->tx_phys,
+ GFP_ATOMIC | __GFP_ZERO);
+ if (!ring->tx_dma)
+ goto no_tx_mem;
+
+ memset(ring->tx_dma, 0, ring->tx_ring_size * sz);
+ for (i = 0; i < ring->tx_ring_size; i++) {
+ int next = (i + 1) % ring->tx_ring_size;
+ u32 next_ptr = ring->tx_phys + next * sz;
+
+ ring->tx_dma[i].txd2 = next_ptr;
+ ring->tx_dma[i].txd3 = TX_DMA_DESP2_DEF;
+ }
+
+ atomic_set(&ring->tx_free_count, ring->tx_ring_size - 2);
+ ring->tx_next_free = &ring->tx_dma[0];
+ ring->tx_last_free = &ring->tx_dma[ring->tx_ring_size - 2];
+ ring->tx_thresh = max((unsigned long)ring->tx_ring_size >> 2,
+ MAX_SKB_FRAGS);
+
+ ring->tx_map = mtk_qdma_tx_map;
+ ring->tx_poll = mtk_qdma_tx_poll;
+ ring->tx_clean = mtk_qdma_tx_clean;
+
+ /* make sure that all changes to the dma ring are flushed before we
+ * continue
+ */
+ wmb();
+
+ mtk_w32(eth, ring->tx_phys, MTK_QTX_CTX_PTR);
+ mtk_w32(eth, ring->tx_phys, MTK_QTX_DTX_PTR);
+ mtk_w32(eth,
+ ring->tx_phys + ((ring->tx_ring_size - 1) * sz),
+ MTK_QTX_CRX_PTR);
+ mtk_w32(eth,
+ ring->tx_phys + ((ring->tx_ring_size - 1) * sz),
+ MTK_QTX_DRX_PTR);
+
+ return 0;
+
+no_tx_mem:
+ return -ENOMEM;
+}
+
+static int mtk_qdma_init(struct mtk_eth *eth, int ring)
+{
+ int err;
+
+ err = mtk_init_fq_dma(eth);
+ if (err)
+ return err;
+
+ err = mtk_qdma_tx_alloc_tx(eth);
+ if (err)
+ return err;
+
+ err = mtk_dma_rx_alloc(eth, ð->rx_ring[ring]);
+ if (err)
+ return err;
+
+ mtk_w32(eth, eth->rx_ring[ring].rx_phys, MTK_QRX_BASE_PTR0);
+ mtk_w32(eth, eth->rx_ring[ring].rx_ring_size, MTK_QRX_MAX_CNT0);
+ mtk_w32(eth, eth->rx_ring[ring].rx_calc_idx, MTK_QRX_CRX_IDX0);
+ mtk_w32(eth, MTK_PST_DRX_IDX0, MTK_QDMA_RST_IDX);
+ mtk_w32(eth, (QDMA_RES_THRES << 8) | QDMA_RES_THRES, MTK_QTX_CFG(0));
+
+ /* Enable random early drop and set drop threshold automatically */
+ mtk_w32(eth, 0x174444, MTK_QDMA_FC_THRES);
+ mtk_w32(eth, 0x0, MTK_QDMA_HRED2);
+
+ return 0;
+}
+
+static int mtk_pdma_qdma_init(struct mtk_eth *eth)
+{
+ int err = mtk_qdma_init(eth, 1);
+
+ if (err)
+ return err;
+
+ err = mtk_dma_rx_alloc(eth, ð->rx_ring[0]);
+ if (err)
+ return err;
+
+ mtk_reg_w32(eth, eth->rx_ring[0].rx_phys, MTK_REG_RX_BASE_PTR0);
+ mtk_reg_w32(eth, eth->rx_ring[0].rx_ring_size, MTK_REG_RX_MAX_CNT0);
+ mtk_reg_w32(eth, eth->rx_ring[0].rx_calc_idx, MTK_REG_RX_CALC_IDX0);
+ mtk_reg_w32(eth, MTK_PST_DRX_IDX0, MTK_REG_PDMA_RST_CFG);
+
+ return 0;
+}
+
+static int mtk_pdma_init(struct mtk_eth *eth)
+{
+ struct mtk_rx_ring *ring = ð->rx_ring[0];
+ int err;
+
+ err = mtk_pdma_tx_alloc(eth);
+ if (err)
+ return err;
+
+ err = mtk_dma_rx_alloc(eth, ring);
+ if (err)
+ return err;
+
+ mtk_reg_w32(eth, ring->rx_phys, MTK_REG_RX_BASE_PTR0);
+ mtk_reg_w32(eth, ring->rx_ring_size, MTK_REG_RX_MAX_CNT0);
+ mtk_reg_w32(eth, ring->rx_calc_idx, MTK_REG_RX_CALC_IDX0);
+ mtk_reg_w32(eth, MTK_PST_DRX_IDX0, MTK_REG_PDMA_RST_CFG);
+
+ return 0;
+}
+
+static void mtk_dma_free(struct mtk_eth *eth)
+{
+ int i;
+
+ for (i = 0; i < eth->soc->mac_count; i++)
+ if (eth->netdev[i])
+ netdev_reset_queue(eth->netdev[i]);
+ eth->tx_ring.tx_clean(eth);
+ mtk_clean_rx(eth, ð->rx_ring[0]);
+ mtk_clean_rx(eth, ð->rx_ring[1]);
+ kfree(eth->scratch_head);
+}
+
+static void mtk_tx_timeout(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ struct mtk_tx_ring *ring = ð->tx_ring;
+
+ eth->netdev[mac->id]->stats.tx_errors++;
+ netif_err(eth, tx_err, dev,
+ "transmit timed out\n");
+ if (eth->soc->dma_type & MTK_PDMA) {
+ netif_info(eth, drv, dev, "pdma_cfg:%08x\n",
+ mtk_reg_r32(eth, MTK_REG_PDMA_GLO_CFG));
+ netif_info(eth, drv, dev, "tx_ring=%d, "
+ "base=%08x, max=%u, ctx=%u, dtx=%u, fdx=%hu, next=%hu\n",
+ 0, mtk_reg_r32(eth, MTK_REG_TX_BASE_PTR0),
+ mtk_reg_r32(eth, MTK_REG_TX_MAX_CNT0),
+ mtk_reg_r32(eth, MTK_REG_TX_CTX_IDX0),
+ mtk_reg_r32(eth, MTK_REG_TX_DTX_IDX0),
+ ring->tx_free_idx,
+ ring->tx_next_idx);
+ }
+ if (eth->soc->dma_type & MTK_QDMA) {
+ netif_info(eth, drv, dev, "qdma_cfg:%08x\n",
+ mtk_r32(eth, MTK_QDMA_GLO_CFG));
+ netif_info(eth, drv, dev, "tx_ring=%d, "
+ "ctx=%08x, dtx=%08x, crx=%08x, drx=%08x, free=%hu\n",
+ 0, mtk_r32(eth, MTK_QTX_CTX_PTR),
+ mtk_r32(eth, MTK_QTX_DTX_PTR),
+ mtk_r32(eth, MTK_QTX_CRX_PTR),
+ mtk_r32(eth, MTK_QTX_DRX_PTR),
+ atomic_read(&ring->tx_free_count));
+ }
+ netif_info(eth, drv, dev,
+ "rx_ring=%d, base=%08x, max=%u, calc=%u, drx=%u\n",
+ 0, mtk_reg_r32(eth, MTK_REG_RX_BASE_PTR0),
+ mtk_reg_r32(eth, MTK_REG_RX_MAX_CNT0),
+ mtk_reg_r32(eth, MTK_REG_RX_CALC_IDX0),
+ mtk_reg_r32(eth, MTK_REG_RX_DRX_IDX0));
+
+ schedule_work(&mac->pending_work);
+}
+
+static irqreturn_t mtk_handle_irq(int irq, void *_eth)
+{
+ struct mtk_eth *eth = _eth;
+ u32 status, int_mask;
+
+ status = mtk_irq_pending(eth);
+ if (unlikely(!status))
+ return IRQ_NONE;
+
+ int_mask = (eth->soc->rx_int | eth->soc->tx_int);
+ if (likely(status & int_mask)) {
+ if (likely(napi_schedule_prep(ð->rx_napi)))
+ __napi_schedule(ð->rx_napi);
+ } else {
+ mtk_irq_ack(eth, status);
+ }
+ mtk_irq_disable(eth, int_mask);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void mtk_poll_controller(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ u32 int_mask = eth->soc->tx_int | eth->soc->rx_int;
+
+ mtk_irq_disable(eth, int_mask);
+ mtk_handle_irq(dev->irq, dev);
+ mtk_irq_enable(eth, int_mask);
+}
+#endif
+
+int mtk_set_clock_cycle(struct mtk_eth *eth)
+{
+ unsigned long sysclk = eth->sysclk;
+
+ sysclk /= MTK_US_CYC_CNT_DIVISOR;
+ sysclk <<= MTK_US_CYC_CNT_SHIFT;
+
+ mtk_w32(eth, (mtk_r32(eth, MTK_GLO_CFG) &
+ ~(MTK_US_CYC_CNT_MASK << MTK_US_CYC_CNT_SHIFT)) |
+ sysclk,
+ MTK_GLO_CFG);
+ return 0;
+}
+
+void mtk_fwd_config(struct mtk_eth *eth)
+{
+ u32 fwd_cfg;
+
+ fwd_cfg = mtk_r32(eth, MTK_GDMA1_FWD_CFG);
+
+ /* disable jumbo frame */
+ if (eth->soc->jumbo_frame)
+ fwd_cfg &= ~MTK_GDM1_JMB_EN;
+
+ /* set unicast/multicast/broadcast frame to cpu */
+ fwd_cfg &= ~0xffff;
+
+ mtk_w32(eth, fwd_cfg, MTK_GDMA1_FWD_CFG);
+}
+
+void mtk_csum_config(struct mtk_eth *eth)
+{
+ if (eth->soc->hw_features & NETIF_F_RXCSUM)
+ mtk_w32(eth, mtk_r32(eth, MTK_GDMA1_FWD_CFG) |
+ (MTK_GDM1_ICS_EN | MTK_GDM1_TCS_EN | MTK_GDM1_UCS_EN),
+ MTK_GDMA1_FWD_CFG);
+ else
+ mtk_w32(eth, mtk_r32(eth, MTK_GDMA1_FWD_CFG) &
+ ~(MTK_GDM1_ICS_EN | MTK_GDM1_TCS_EN | MTK_GDM1_UCS_EN),
+ MTK_GDMA1_FWD_CFG);
+ if (eth->soc->hw_features & NETIF_F_IP_CSUM)
+ mtk_w32(eth, mtk_r32(eth, MTK_CDMA_CSG_CFG) |
+ (MTK_ICS_GEN_EN | MTK_TCS_GEN_EN | MTK_UCS_GEN_EN),
+ MTK_CDMA_CSG_CFG);
+ else
+ mtk_w32(eth, mtk_r32(eth, MTK_CDMA_CSG_CFG) &
+ ~(MTK_ICS_GEN_EN | MTK_TCS_GEN_EN | MTK_UCS_GEN_EN),
+ MTK_CDMA_CSG_CFG);
+}
+
+static int mtk_start_dma(struct mtk_eth *eth)
+{
+ unsigned long flags;
+ u32 val;
+ int err;
+
+ if (eth->soc->dma_type == MTK_PDMA)
+ err = mtk_pdma_init(eth);
+ else if (eth->soc->dma_type == MTK_QDMA)
+ err = mtk_qdma_init(eth, 0);
+ else
+ err = mtk_pdma_qdma_init(eth);
+ if (err) {
+ mtk_dma_free(eth);
+ return err;
+ }
+
+ spin_lock_irqsave(ð->page_lock, flags);
+
+ val = MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN;
+ if (eth->soc->rx_2b_offset)
+ val |= MTK_RX_2B_OFFSET;
+ val |= eth->soc->pdma_glo_cfg;
+
+ if (eth->soc->dma_type & MTK_PDMA)
+ mtk_reg_w32(eth, val, MTK_REG_PDMA_GLO_CFG);
+
+ if (eth->soc->dma_type & MTK_QDMA)
+ mtk_w32(eth, val, MTK_QDMA_GLO_CFG);
+
+ spin_unlock_irqrestore(ð->page_lock, flags);
+
+ return 0;
+}
+
+static int mtk_open(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
+ if (!atomic_read(ð->dma_refcnt)) {
+ int err = mtk_start_dma(eth);
+
+ if (err)
+ return err;
+
+ napi_enable(ð->rx_napi);
+ mtk_irq_enable(eth, eth->soc->tx_int | eth->soc->rx_int);
+ }
+ atomic_inc(ð->dma_refcnt);
+
+ if (eth->phy)
+ eth->phy->start(mac);
+
+ if (eth->soc->has_carrier && eth->soc->has_carrier(eth))
+ netif_carrier_on(dev);
+
+ netif_start_queue(dev);
+ eth->soc->fwd_config(eth);
+
+ return 0;
+}
+
+static void mtk_stop_dma(struct mtk_eth *eth, u32 glo_cfg)
+{
+ unsigned long flags;
+ u32 val;
+ int i;
+
+ /* stop the dma enfine */
+ spin_lock_irqsave(ð->page_lock, flags);
+ val = mtk_r32(eth, glo_cfg);
+ mtk_w32(eth, val & ~(MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN),
+ glo_cfg);
+ spin_unlock_irqrestore(ð->page_lock, flags);
+
+ /* wait for dma stop */
+ for (i = 0; i < 10; i++) {
+ val = mtk_r32(eth, glo_cfg);
+ if (val & (MTK_TX_DMA_BUSY | MTK_RX_DMA_BUSY)) {
+ msleep(20);
+ continue;
+ }
+ break;
+ }
+}
+
+static int mtk_stop(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
+ netif_tx_disable(dev);
+ if (eth->phy)
+ eth->phy->stop(mac);
+
+ if (!atomic_dec_and_test(ð->dma_refcnt))
+ return 0;
+
+ mtk_irq_disable(eth, eth->soc->tx_int | eth->soc->rx_int);
+ napi_disable(ð->rx_napi);
+
+ if (eth->soc->dma_type & MTK_PDMA)
+ mtk_stop_dma(eth, mtk_reg_table[MTK_REG_PDMA_GLO_CFG]);
+
+ if (eth->soc->dma_type & MTK_QDMA)
+ mtk_stop_dma(eth, MTK_QDMA_GLO_CFG);
+
+ mtk_dma_free(eth);
+
+ return 0;
+}
+
+static int __init mtk_init_hw(struct mtk_eth *eth)
+{
+ int i, err;
+
+ eth->soc->reset_fe(eth);
+
+ if (eth->soc->switch_init)
+ if (eth->soc->switch_init(eth)) {
+ dev_err(eth->dev, "failed to initialize switch core\n");
+ return -ENODEV;
+ }
+
+ err = devm_request_irq(eth->dev, eth->irq, mtk_handle_irq, 0,
+ dev_name(eth->dev), eth);
+ if (err)
+ return err;
+
+ err = mtk_mdio_init(eth);
+ if (err)
+ return err;
+
+ /* disable delay and normal interrupt */
+ mtk_reg_w32(eth, 0, MTK_REG_DLY_INT_CFG);
+ if (eth->soc->dma_type & MTK_QDMA)
+ mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
+ mtk_irq_disable(eth, eth->soc->tx_int | eth->soc->rx_int);
+
+ /* frame engine will push VLAN tag regarding to VIDX field in Tx desc */
+ if (mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE])
+ for (i = 0; i < 16; i += 2)
+ mtk_w32(eth, ((i + 1) << 16) + i,
+ mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE] +
+ (i * 2));
+
+ if (eth->soc->fwd_config(eth))
+ dev_err(eth->dev, "unable to get clock\n");
+
+ if (mtk_reg_table[MTK_REG_MTK_RST_GL]) {
+ mtk_reg_w32(eth, 1, MTK_REG_MTK_RST_GL);
+ mtk_reg_w32(eth, 0, MTK_REG_MTK_RST_GL);
+ }
+
+ return 0;
+}
+
+static int __init mtk_init(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ struct device_node *port;
+ const char *mac_addr;
+ int err;
+
+ mac_addr = of_get_mac_address(mac->of_node);
+ if (mac_addr)
+ ether_addr_copy(dev->dev_addr, mac_addr);
+
+ /* If the mac address is invalid, use random mac address */
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ random_ether_addr(dev->dev_addr);
+ dev_err(eth->dev, "generated random MAC address %pM\n",
+ dev->dev_addr);
+ dev->addr_assign_type = NET_ADDR_RANDOM;
+ }
+ mac->hw->soc->set_mac(mac, dev->dev_addr);
+
+ if (eth->soc->port_init)
+ for_each_child_of_node(mac->of_node, port)
+ if (of_device_is_compatible(port,
+ "mediatek,eth-port") &&
+ of_device_is_available(port))
+ eth->soc->port_init(eth, mac, port);
+
+ if (eth->phy) {
+ err = eth->phy->connect(mac);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static void mtk_uninit(struct net_device *dev)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+
+ if (eth->phy)
+ eth->phy->disconnect(mac);
+ mtk_mdio_cleanup(eth);
+
+ mtk_irq_disable(eth, ~0);
+ free_irq(dev->irq, dev);
+}
+
+static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+
+ if (!mac->phy_dev)
+ return -ENODEV;
+
+ switch (cmd) {
+ case SIOCGMIIPHY:
+ case SIOCGMIIREG:
+ case SIOCSMIIREG:
+ return phy_mii_ioctl(mac->phy_dev, ifr, cmd);
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int mtk_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct mtk_mac *mac = netdev_priv(dev);
+ struct mtk_eth *eth = mac->hw;
+ int frag_size, old_mtu;
+ u32 fwd_cfg;
+
+ if (!eth->soc->jumbo_frame)
+ return eth_change_mtu(dev, new_mtu);
+
+ frag_size = mtk_max_frag_size(new_mtu);
+ if (new_mtu < 68 || frag_size > PAGE_SIZE)
+ return -EINVAL;
+
+ old_mtu = dev->mtu;
+ dev->mtu = new_mtu;
+
+ /* return early if the buffer sizes will not change */
+ if (old_mtu <= ETH_DATA_LEN && new_mtu <= ETH_DATA_LEN)
+ return 0;
+ if (old_mtu > ETH_DATA_LEN && new_mtu > ETH_DATA_LEN)
+ return 0;
+
+ if (new_mtu <= ETH_DATA_LEN)
+ eth->rx_ring[0].frag_size = mtk_max_frag_size(ETH_DATA_LEN);
+ else
+ eth->rx_ring[0].frag_size = PAGE_SIZE;
+ eth->rx_ring[0].rx_buf_size =
+ mtk_max_buf_size(eth->rx_ring[0].frag_size);
+
+ if (!netif_running(dev))
+ return 0;
+
+ mtk_stop(dev);
+ fwd_cfg = mtk_r32(eth, MTK_GDMA1_FWD_CFG);
+ if (new_mtu <= ETH_DATA_LEN) {
+ fwd_cfg &= ~MTK_GDM1_JMB_EN;
+ } else {
+ fwd_cfg &= ~(MTK_GDM1_JMB_LEN_MASK << MTK_GDM1_JMB_LEN_SHIFT);
+ fwd_cfg |= (DIV_ROUND_UP(frag_size, 1024) <<
+ MTK_GDM1_JMB_LEN_SHIFT) | MTK_GDM1_JMB_EN;
+ }
+ mtk_w32(eth, fwd_cfg, MTK_GDMA1_FWD_CFG);
+
+ return mtk_open(dev);
+}
+
+static void mtk_pending_work(struct work_struct *work)
+{
+ struct mtk_mac *mac = container_of(work, struct mtk_mac, pending_work);
+ struct mtk_eth *eth = mac->hw;
+ struct net_device *dev = eth->netdev[mac->id];
+ int err;
+
+ rtnl_lock();
+ mtk_stop(dev);
+
+ err = mtk_open(dev);
+ if (err) {
+ netif_alert(eth, ifup, dev,
+ "Driver up/down cycle failed, closing device.\n");
+ dev_close(dev);
+ }
+ rtnl_unlock();
+}
+
+static int mtk_cleanup(struct mtk_eth *eth)
+{
+ int i;
+
+ for (i = 0; i < eth->soc->mac_count; i++) {
+ struct mtk_mac *mac = netdev_priv(eth->netdev[i]);
+
+ if (!eth->netdev[i])
+ continue;
+
+ unregister_netdev(eth->netdev[i]);
+ free_netdev(eth->netdev[i]);
+ cancel_work_sync(&mac->pending_work);
+ }
+
+ return 0;
+}
+
+static const struct net_device_ops mtk_netdev_ops = {
+ .ndo_init = mtk_init,
+ .ndo_uninit = mtk_uninit,
+ .ndo_open = mtk_open,
+ .ndo_stop = mtk_stop,
+ .ndo_start_xmit = mtk_start_xmit,
+ .ndo_set_mac_address = mtk_set_mac_address,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = mtk_do_ioctl,
+ .ndo_change_mtu = mtk_change_mtu,
+ .ndo_tx_timeout = mtk_tx_timeout,
+ .ndo_get_stats64 = mtk_get_stats64,
+ .ndo_vlan_rx_add_vid = mtk_vlan_rx_add_vid,
+ .ndo_vlan_rx_kill_vid = mtk_vlan_rx_kill_vid,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = mtk_poll_controller,
+#endif
+};
+
+static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
+{
+ struct mtk_mac *mac;
+ const __be32 *_id = of_get_property(np, "reg", NULL);
+ int id, err;
+
+ if (!_id) {
+ dev_err(eth->dev, "missing mac id\n");
+ return -EINVAL;
+ }
+ id = be32_to_cpup(_id);
+ if (id >= eth->soc->mac_count || eth->netdev[id]) {
+ dev_err(eth->dev, "%d is not a valid mac id\n", id);
+ return -EINVAL;
+ }
+
+ eth->netdev[id] = alloc_etherdev(sizeof(*mac));
+ if (!eth->netdev[id]) {
+ dev_err(eth->dev, "alloc_etherdev failed\n");
+ return -ENOMEM;
+ }
+ mac = netdev_priv(eth->netdev[id]);
+ eth->mac[id] = mac;
+ mac->id = id;
+ mac->hw = eth;
+ mac->of_node = np;
+ INIT_WORK(&mac->pending_work, mtk_pending_work);
+
+ if (mtk_reg_table[MTK_REG_MTK_COUNTER_BASE]) {
+ mac->hw_stats = devm_kzalloc(eth->dev,
+ sizeof(*mac->hw_stats),
+ GFP_KERNEL);
+ if (!mac->hw_stats)
+ return -ENOMEM;
+ spin_lock_init(&mac->hw_stats->stats_lock);
+ mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
+ }
+
+ SET_NETDEV_DEV(eth->netdev[id], eth->dev);
+ eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
+ eth->netdev[id]->base_addr = (unsigned long)eth->base;
+
+ if (eth->soc->init_data)
+ eth->soc->init_data(eth->soc, eth->netdev[id]);
+
+ eth->netdev[id]->vlan_features = eth->soc->hw_features &
+ ~(NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX);
+ eth->netdev[id]->features |= eth->soc->hw_features;
+
+ if (mtk_reg_table[MTK_REG_MTK_DMA_VID_BASE])
+ eth->netdev[id]->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+
+ mtk_set_ethtool_ops(eth->netdev[id]);
+
+ err = register_netdev(eth->netdev[id]);
+ if (err) {
+ dev_err(eth->dev, "error bringing up device\n");
+ return err;
+ }
+ eth->netdev[id]->irq = eth->irq;
+ netif_info(eth, probe, eth->netdev[id],
+ "mediatek frame engine at 0x%08lx, irq %d\n",
+ eth->netdev[id]->base_addr, eth->netdev[id]->irq);
+
+ return 0;
+}
+
+static int mtk_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ const struct of_device_id *match;
+ struct device_node *mac_np;
+ struct mtk_soc_data *soc;
+ struct mtk_eth *eth;
+ struct clk *sysclk;
+ int err;
+
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+
+ device_reset(&pdev->dev);
+
+ match = of_match_device(of_mtk_match, &pdev->dev);
+ soc = (struct mtk_soc_data *)match->data;
+
+ if (soc->reg_table)
+ mtk_reg_table = soc->reg_table;
+
+ eth = devm_kzalloc(&pdev->dev, sizeof(*eth), GFP_KERNEL);
+ if (!eth)
+ return -ENOMEM;
+
+ eth->base = devm_ioremap_resource(&pdev->dev, res);
+ if (!eth->base)
+ return -EADDRNOTAVAIL;
+
+ spin_lock_init(ð->page_lock);
+
+ eth->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "mediatek,ethsys");
+ if (IS_ERR(eth->ethsys))
+ return PTR_ERR(eth->ethsys);
+
+ eth->irq = platform_get_irq(pdev, 0);
+ if (eth->irq < 0) {
+ dev_err(&pdev->dev, "no IRQ resource found\n");
+ return -ENXIO;
+ }
+
+ sysclk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(sysclk)) {
+ dev_err(&pdev->dev,
+ "the clock is not defined in the devictree\n");
+ return -ENXIO;
+ }
+ eth->sysclk = clk_get_rate(sysclk);
+
+ eth->switch_np = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,switch", 0);
+ if (soc->has_switch && !eth->switch_np) {
+ dev_err(&pdev->dev, "failed to read switch phandle\n");
+ return -ENODEV;
+ }
+
+ eth->dev = &pdev->dev;
+ eth->soc = soc;
+ eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE);
+
+ err = mtk_init_hw(eth);
+ if (err)
+ return err;
+
+ if (eth->soc->mac_count > 1) {
+ for_each_child_of_node(pdev->dev.of_node, mac_np) {
+ if (!of_device_is_compatible(mac_np,
+ "mediatek,eth-mac"))
+ continue;
+
+ if (!of_device_is_available(mac_np))
+ continue;
+
+ err = mtk_add_mac(eth, mac_np);
+ if (err)
+ goto err_free_dev;
+ }
+
+ init_dummy_netdev(ð->dummy_dev);
+ netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_poll,
+ soc->napi_weight);
+ } else {
+ err = mtk_add_mac(eth, pdev->dev.of_node);
+ if (err)
+ goto err_free_dev;
+ netif_napi_add(eth->netdev[0], ð->rx_napi, mtk_poll,
+ soc->napi_weight);
+ }
+
+ platform_set_drvdata(pdev, eth);
+
+ return 0;
+
+err_free_dev:
+ mtk_cleanup(eth);
+ return err;
+}
+
+static int mtk_remove(struct platform_device *pdev)
+{
+ struct mtk_eth *eth = platform_get_drvdata(pdev);
+
+ netif_napi_del(ð->rx_napi);
+ mtk_cleanup(eth);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver mtk_driver = {
+ .probe = mtk_probe,
+ .remove = mtk_remove,
+ .driver = {
+ .name = "mtk_soc_eth",
+ .owner = THIS_MODULE,
+ .of_match_table = of_mtk_match,
+ },
+};
+
+module_platform_driver(mtk_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Crispin <[email protected]>");
+MODULE_DESCRIPTION("Ethernet driver for MediaTek SoC");
diff --git a/drivers/staging/mt7621-eth/mtk_eth_soc.h b/drivers/staging/mt7621-eth/mtk_eth_soc.h
new file mode 100644
index 000000000000..443f88d8af65
--- /dev/null
+++ b/drivers/staging/mt7621-eth/mtk_eth_soc.h
@@ -0,0 +1,721 @@
+/* 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; version 2 of the License
+ *
+ * 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.
+ *
+ * Copyright (C) 2009-2016 John Crispin <[email protected]>
+ * Copyright (C) 2009-2016 Felix Fietkau <[email protected]>
+ * Copyright (C) 2013-2016 Michael Lee <[email protected]>
+ */
+
+#ifndef MTK_ETH_H
+#define MTK_ETH_H
+
+#include <linux/mii.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/dma-mapping.h>
+#include <linux/phy.h>
+#include <linux/ethtool.h>
+#include <linux/version.h>
+#include <linux/atomic.h>
+
+/* these registers have different offsets depending on the SoC. we use a lookup
+ * table for these
+ */
+enum mtk_reg {
+ MTK_REG_PDMA_GLO_CFG = 0,
+ MTK_REG_PDMA_RST_CFG,
+ MTK_REG_DLY_INT_CFG,
+ MTK_REG_TX_BASE_PTR0,
+ MTK_REG_TX_MAX_CNT0,
+ MTK_REG_TX_CTX_IDX0,
+ MTK_REG_TX_DTX_IDX0,
+ MTK_REG_RX_BASE_PTR0,
+ MTK_REG_RX_MAX_CNT0,
+ MTK_REG_RX_CALC_IDX0,
+ MTK_REG_RX_DRX_IDX0,
+ MTK_REG_MTK_INT_ENABLE,
+ MTK_REG_MTK_INT_STATUS,
+ MTK_REG_MTK_DMA_VID_BASE,
+ MTK_REG_MTK_COUNTER_BASE,
+ MTK_REG_MTK_RST_GL,
+ MTK_REG_MTK_INT_STATUS2,
+ MTK_REG_COUNT
+};
+
+/* delayed interrupt bits */
+#define MTK_DELAY_EN_INT 0x80
+#define MTK_DELAY_MAX_INT 0x04
+#define MTK_DELAY_MAX_TOUT 0x04
+#define MTK_DELAY_TIME 20
+#define MTK_DELAY_CHAN (((MTK_DELAY_EN_INT | MTK_DELAY_MAX_INT) << 8) \
+ | MTK_DELAY_MAX_TOUT)
+#define MTK_DELAY_INIT ((MTK_DELAY_CHAN << 16) | MTK_DELAY_CHAN)
+#define MTK_PSE_FQFC_CFG_INIT 0x80504000
+#define MTK_PSE_FQFC_CFG_256Q 0xff908000
+
+/* interrupt bits */
+#define MTK_CNT_PPE_AF BIT(31)
+#define MTK_CNT_GDM_AF BIT(29)
+#define MTK_PSE_P2_FC BIT(26)
+#define MTK_PSE_BUF_DROP BIT(24)
+#define MTK_GDM_OTHER_DROP BIT(23)
+#define MTK_PSE_P1_FC BIT(22)
+#define MTK_PSE_P0_FC BIT(21)
+#define MTK_PSE_FQ_EMPTY BIT(20)
+#define MTK_GE1_STA_CHG BIT(18)
+#define MTK_TX_COHERENT BIT(17)
+#define MTK_RX_COHERENT BIT(16)
+#define MTK_TX_DONE_INT3 BIT(11)
+#define MTK_TX_DONE_INT2 BIT(10)
+#define MTK_TX_DONE_INT1 BIT(9)
+#define MTK_TX_DONE_INT0 BIT(8)
+#define MTK_RX_DONE_INT0 BIT(2)
+#define MTK_TX_DLY_INT BIT(1)
+#define MTK_RX_DLY_INT BIT(0)
+
+#define MTK_RX_DONE_INT MTK_RX_DONE_INT0
+#define MTK_TX_DONE_INT (MTK_TX_DONE_INT0 | MTK_TX_DONE_INT1 | \
+ MTK_TX_DONE_INT2 | MTK_TX_DONE_INT3)
+
+#define RT5350_RX_DLY_INT BIT(30)
+#define RT5350_TX_DLY_INT BIT(28)
+#define RT5350_RX_DONE_INT1 BIT(17)
+#define RT5350_RX_DONE_INT0 BIT(16)
+#define RT5350_TX_DONE_INT3 BIT(3)
+#define RT5350_TX_DONE_INT2 BIT(2)
+#define RT5350_TX_DONE_INT1 BIT(1)
+#define RT5350_TX_DONE_INT0 BIT(0)
+
+#define RT5350_RX_DONE_INT (RT5350_RX_DONE_INT0 | RT5350_RX_DONE_INT1)
+#define RT5350_TX_DONE_INT (RT5350_TX_DONE_INT0 | RT5350_TX_DONE_INT1 | \
+ RT5350_TX_DONE_INT2 | RT5350_TX_DONE_INT3)
+
+/* registers */
+#define MTK_GDMA_OFFSET 0x0020
+#define MTK_PSE_OFFSET 0x0040
+#define MTK_GDMA2_OFFSET 0x0060
+#define MTK_CDMA_OFFSET 0x0080
+#define MTK_DMA_VID0 0x00a8
+#define MTK_PDMA_OFFSET 0x0100
+#define MTK_PPE_OFFSET 0x0200
+#define MTK_CMTABLE_OFFSET 0x0400
+#define MTK_POLICYTABLE_OFFSET 0x1000
+
+#define MT7621_GDMA_OFFSET 0x0500
+#define MT7620_GDMA_OFFSET 0x0600
+
+#define RT5350_PDMA_OFFSET 0x0800
+#define RT5350_SDM_OFFSET 0x0c00
+
+#define MTK_MDIO_ACCESS 0x00
+#define MTK_MDIO_CFG 0x04
+#define MTK_GLO_CFG 0x08
+#define MTK_RST_GL 0x0C
+#define MTK_INT_STATUS 0x10
+#define MTK_INT_ENABLE 0x14
+#define MTK_MDIO_CFG2 0x18
+#define MTK_FOC_TS_T 0x1C
+
+#define MTK_GDMA1_FWD_CFG (MTK_GDMA_OFFSET + 0x00)
+#define MTK_GDMA1_SCH_CFG (MTK_GDMA_OFFSET + 0x04)
+#define MTK_GDMA1_SHPR_CFG (MTK_GDMA_OFFSET + 0x08)
+#define MTK_GDMA1_MAC_ADRL (MTK_GDMA_OFFSET + 0x0C)
+#define MTK_GDMA1_MAC_ADRH (MTK_GDMA_OFFSET + 0x10)
+
+#define MTK_GDMA2_FWD_CFG (MTK_GDMA2_OFFSET + 0x00)
+#define MTK_GDMA2_SCH_CFG (MTK_GDMA2_OFFSET + 0x04)
+#define MTK_GDMA2_SHPR_CFG (MTK_GDMA2_OFFSET + 0x08)
+#define MTK_GDMA2_MAC_ADRL (MTK_GDMA2_OFFSET + 0x0C)
+#define MTK_GDMA2_MAC_ADRH (MTK_GDMA2_OFFSET + 0x10)
+
+#define MTK_PSE_FQ_CFG (MTK_PSE_OFFSET + 0x00)
+#define MTK_CDMA_FC_CFG (MTK_PSE_OFFSET + 0x04)
+#define MTK_GDMA1_FC_CFG (MTK_PSE_OFFSET + 0x08)
+#define MTK_GDMA2_FC_CFG (MTK_PSE_OFFSET + 0x0C)
+
+#define MTK_CDMA_CSG_CFG (MTK_CDMA_OFFSET + 0x00)
+#define MTK_CDMA_SCH_CFG (MTK_CDMA_OFFSET + 0x04)
+
+#define MT7621_GDMA_FWD_CFG(x) (MT7621_GDMA_OFFSET + (x * 0x1000))
+
+/* FIXME this might be different for different SOCs */
+#define MT7620_GDMA1_FWD_CFG (MT7621_GDMA_OFFSET + 0x00)
+
+#define RT5350_TX_BASE_PTR0 (RT5350_PDMA_OFFSET + 0x00)
+#define RT5350_TX_MAX_CNT0 (RT5350_PDMA_OFFSET + 0x04)
+#define RT5350_TX_CTX_IDX0 (RT5350_PDMA_OFFSET + 0x08)
+#define RT5350_TX_DTX_IDX0 (RT5350_PDMA_OFFSET + 0x0C)
+#define RT5350_TX_BASE_PTR1 (RT5350_PDMA_OFFSET + 0x10)
+#define RT5350_TX_MAX_CNT1 (RT5350_PDMA_OFFSET + 0x14)
+#define RT5350_TX_CTX_IDX1 (RT5350_PDMA_OFFSET + 0x18)
+#define RT5350_TX_DTX_IDX1 (RT5350_PDMA_OFFSET + 0x1C)
+#define RT5350_TX_BASE_PTR2 (RT5350_PDMA_OFFSET + 0x20)
+#define RT5350_TX_MAX_CNT2 (RT5350_PDMA_OFFSET + 0x24)
+#define RT5350_TX_CTX_IDX2 (RT5350_PDMA_OFFSET + 0x28)
+#define RT5350_TX_DTX_IDX2 (RT5350_PDMA_OFFSET + 0x2C)
+#define RT5350_TX_BASE_PTR3 (RT5350_PDMA_OFFSET + 0x30)
+#define RT5350_TX_MAX_CNT3 (RT5350_PDMA_OFFSET + 0x34)
+#define RT5350_TX_CTX_IDX3 (RT5350_PDMA_OFFSET + 0x38)
+#define RT5350_TX_DTX_IDX3 (RT5350_PDMA_OFFSET + 0x3C)
+#define RT5350_RX_BASE_PTR0 (RT5350_PDMA_OFFSET + 0x100)
+#define RT5350_RX_MAX_CNT0 (RT5350_PDMA_OFFSET + 0x104)
+#define RT5350_RX_CALC_IDX0 (RT5350_PDMA_OFFSET + 0x108)
+#define RT5350_RX_DRX_IDX0 (RT5350_PDMA_OFFSET + 0x10C)
+#define RT5350_RX_BASE_PTR1 (RT5350_PDMA_OFFSET + 0x110)
+#define RT5350_RX_MAX_CNT1 (RT5350_PDMA_OFFSET + 0x114)
+#define RT5350_RX_CALC_IDX1 (RT5350_PDMA_OFFSET + 0x118)
+#define RT5350_RX_DRX_IDX1 (RT5350_PDMA_OFFSET + 0x11C)
+#define RT5350_PDMA_GLO_CFG (RT5350_PDMA_OFFSET + 0x204)
+#define RT5350_PDMA_RST_CFG (RT5350_PDMA_OFFSET + 0x208)
+#define RT5350_DLY_INT_CFG (RT5350_PDMA_OFFSET + 0x20c)
+#define RT5350_MTK_INT_STATUS (RT5350_PDMA_OFFSET + 0x220)
+#define RT5350_MTK_INT_ENABLE (RT5350_PDMA_OFFSET + 0x228)
+#define RT5350_PDMA_SCH_CFG (RT5350_PDMA_OFFSET + 0x280)
+
+#define MTK_PDMA_GLO_CFG (MTK_PDMA_OFFSET + 0x00)
+#define MTK_PDMA_RST_CFG (MTK_PDMA_OFFSET + 0x04)
+#define MTK_PDMA_SCH_CFG (MTK_PDMA_OFFSET + 0x08)
+#define MTK_DLY_INT_CFG (MTK_PDMA_OFFSET + 0x0C)
+#define MTK_TX_BASE_PTR0 (MTK_PDMA_OFFSET + 0x10)
+#define MTK_TX_MAX_CNT0 (MTK_PDMA_OFFSET + 0x14)
+#define MTK_TX_CTX_IDX0 (MTK_PDMA_OFFSET + 0x18)
+#define MTK_TX_DTX_IDX0 (MTK_PDMA_OFFSET + 0x1C)
+#define MTK_TX_BASE_PTR1 (MTK_PDMA_OFFSET + 0x20)
+#define MTK_TX_MAX_CNT1 (MTK_PDMA_OFFSET + 0x24)
+#define MTK_TX_CTX_IDX1 (MTK_PDMA_OFFSET + 0x28)
+#define MTK_TX_DTX_IDX1 (MTK_PDMA_OFFSET + 0x2C)
+#define MTK_RX_BASE_PTR0 (MTK_PDMA_OFFSET + 0x30)
+#define MTK_RX_MAX_CNT0 (MTK_PDMA_OFFSET + 0x34)
+#define MTK_RX_CALC_IDX0 (MTK_PDMA_OFFSET + 0x38)
+#define MTK_RX_DRX_IDX0 (MTK_PDMA_OFFSET + 0x3C)
+#define MTK_TX_BASE_PTR2 (MTK_PDMA_OFFSET + 0x40)
+#define MTK_TX_MAX_CNT2 (MTK_PDMA_OFFSET + 0x44)
+#define MTK_TX_CTX_IDX2 (MTK_PDMA_OFFSET + 0x48)
+#define MTK_TX_DTX_IDX2 (MTK_PDMA_OFFSET + 0x4C)
+#define MTK_TX_BASE_PTR3 (MTK_PDMA_OFFSET + 0x50)
+#define MTK_TX_MAX_CNT3 (MTK_PDMA_OFFSET + 0x54)
+#define MTK_TX_CTX_IDX3 (MTK_PDMA_OFFSET + 0x58)
+#define MTK_TX_DTX_IDX3 (MTK_PDMA_OFFSET + 0x5C)
+#define MTK_RX_BASE_PTR1 (MTK_PDMA_OFFSET + 0x60)
+#define MTK_RX_MAX_CNT1 (MTK_PDMA_OFFSET + 0x64)
+#define MTK_RX_CALC_IDX1 (MTK_PDMA_OFFSET + 0x68)
+#define MTK_RX_DRX_IDX1 (MTK_PDMA_OFFSET + 0x6C)
+
+/* Switch DMA configuration */
+#define RT5350_SDM_CFG (RT5350_SDM_OFFSET + 0x00)
+#define RT5350_SDM_RRING (RT5350_SDM_OFFSET + 0x04)
+#define RT5350_SDM_TRING (RT5350_SDM_OFFSET + 0x08)
+#define RT5350_SDM_MAC_ADRL (RT5350_SDM_OFFSET + 0x0C)
+#define RT5350_SDM_MAC_ADRH (RT5350_SDM_OFFSET + 0x10)
+#define RT5350_SDM_TPCNT (RT5350_SDM_OFFSET + 0x100)
+#define RT5350_SDM_TBCNT (RT5350_SDM_OFFSET + 0x104)
+#define RT5350_SDM_RPCNT (RT5350_SDM_OFFSET + 0x108)
+#define RT5350_SDM_RBCNT (RT5350_SDM_OFFSET + 0x10C)
+#define RT5350_SDM_CS_ERR (RT5350_SDM_OFFSET + 0x110)
+
+#define RT5350_SDM_ICS_EN BIT(16)
+#define RT5350_SDM_TCS_EN BIT(17)
+#define RT5350_SDM_UCS_EN BIT(18)
+
+/* QDMA registers */
+#define MTK_QTX_CFG(x) (0x1800 + (x * 0x10))
+#define MTK_QTX_SCH(x) (0x1804 + (x * 0x10))
+#define MTK_QRX_BASE_PTR0 0x1900
+#define MTK_QRX_MAX_CNT0 0x1904
+#define MTK_QRX_CRX_IDX0 0x1908
+#define MTK_QRX_DRX_IDX0 0x190C
+#define MTK_QDMA_GLO_CFG 0x1A04
+#define MTK_QDMA_RST_IDX 0x1A08
+#define MTK_QDMA_DELAY_INT 0x1A0C
+#define MTK_QDMA_FC_THRES 0x1A10
+#define MTK_QMTK_INT_STATUS 0x1A18
+#define MTK_QMTK_INT_ENABLE 0x1A1C
+#define MTK_QDMA_HRED2 0x1A44
+
+#define MTK_QTX_CTX_PTR 0x1B00
+#define MTK_QTX_DTX_PTR 0x1B04
+
+#define MTK_QTX_CRX_PTR 0x1B10
+#define MTK_QTX_DRX_PTR 0x1B14
+
+#define MTK_QDMA_FQ_HEAD 0x1B20
+#define MTK_QDMA_FQ_TAIL 0x1B24
+#define MTK_QDMA_FQ_CNT 0x1B28
+#define MTK_QDMA_FQ_BLEN 0x1B2C
+
+#define QDMA_PAGE_SIZE 2048
+#define QDMA_TX_OWNER_CPU BIT(31)
+#define QDMA_TX_SWC BIT(14)
+#define TX_QDMA_SDL(_x) (((_x) & 0x3fff) << 16)
+#define QDMA_RES_THRES 4
+
+/* MDIO_CFG register bits */
+#define MTK_MDIO_CFG_AUTO_POLL_EN BIT(29)
+#define MTK_MDIO_CFG_GP1_BP_EN BIT(16)
+#define MTK_MDIO_CFG_GP1_FRC_EN BIT(15)
+#define MTK_MDIO_CFG_GP1_SPEED_10 (0 << 13)
+#define MTK_MDIO_CFG_GP1_SPEED_100 (1 << 13)
+#define MTK_MDIO_CFG_GP1_SPEED_1000 (2 << 13)
+#define MTK_MDIO_CFG_GP1_DUPLEX BIT(12)
+#define MTK_MDIO_CFG_GP1_FC_TX BIT(11)
+#define MTK_MDIO_CFG_GP1_FC_RX BIT(10)
+#define MTK_MDIO_CFG_GP1_LNK_DWN BIT(9)
+#define MTK_MDIO_CFG_GP1_AN_FAIL BIT(8)
+#define MTK_MDIO_CFG_MDC_CLK_DIV_1 (0 << 6)
+#define MTK_MDIO_CFG_MDC_CLK_DIV_2 (1 << 6)
+#define MTK_MDIO_CFG_MDC_CLK_DIV_4 (2 << 6)
+#define MTK_MDIO_CFG_MDC_CLK_DIV_8 (3 << 6)
+#define MTK_MDIO_CFG_TURBO_MII_FREQ BIT(5)
+#define MTK_MDIO_CFG_TURBO_MII_MODE BIT(4)
+#define MTK_MDIO_CFG_RX_CLK_SKEW_0 (0 << 2)
+#define MTK_MDIO_CFG_RX_CLK_SKEW_200 (1 << 2)
+#define MTK_MDIO_CFG_RX_CLK_SKEW_400 (2 << 2)
+#define MTK_MDIO_CFG_RX_CLK_SKEW_INV (3 << 2)
+#define MTK_MDIO_CFG_TX_CLK_SKEW_0 0
+#define MTK_MDIO_CFG_TX_CLK_SKEW_200 1
+#define MTK_MDIO_CFG_TX_CLK_SKEW_400 2
+#define MTK_MDIO_CFG_TX_CLK_SKEW_INV 3
+
+/* uni-cast port */
+#define MTK_GDM1_JMB_LEN_MASK 0xf
+#define MTK_GDM1_JMB_LEN_SHIFT 28
+#define MTK_GDM1_ICS_EN BIT(22)
+#define MTK_GDM1_TCS_EN BIT(21)
+#define MTK_GDM1_UCS_EN BIT(20)
+#define MTK_GDM1_JMB_EN BIT(19)
+#define MTK_GDM1_STRPCRC BIT(16)
+#define MTK_GDM1_UFRC_P_CPU (0 << 12)
+#define MTK_GDM1_UFRC_P_GDMA1 (1 << 12)
+#define MTK_GDM1_UFRC_P_PPE (6 << 12)
+
+/* checksums */
+#define MTK_ICS_GEN_EN BIT(2)
+#define MTK_UCS_GEN_EN BIT(1)
+#define MTK_TCS_GEN_EN BIT(0)
+
+/* dma mode */
+#define MTK_PDMA BIT(0)
+#define MTK_QDMA BIT(1)
+#define MTK_PDMA_RX_QDMA_TX (MTK_PDMA | MTK_QDMA)
+
+/* dma ring */
+#define MTK_PST_DRX_IDX0 BIT(16)
+#define MTK_PST_DTX_IDX3 BIT(3)
+#define MTK_PST_DTX_IDX2 BIT(2)
+#define MTK_PST_DTX_IDX1 BIT(1)
+#define MTK_PST_DTX_IDX0 BIT(0)
+
+#define MTK_RX_2B_OFFSET BIT(31)
+#define MTK_TX_WB_DDONE BIT(6)
+#define MTK_RX_DMA_BUSY BIT(3)
+#define MTK_TX_DMA_BUSY BIT(1)
+#define MTK_RX_DMA_EN BIT(2)
+#define MTK_TX_DMA_EN BIT(0)
+
+#define MTK_PDMA_SIZE_4DWORDS (0 << 4)
+#define MTK_PDMA_SIZE_8DWORDS (1 << 4)
+#define MTK_PDMA_SIZE_16DWORDS (2 << 4)
+
+#define MTK_US_CYC_CNT_MASK 0xff
+#define MTK_US_CYC_CNT_SHIFT 0x8
+#define MTK_US_CYC_CNT_DIVISOR 1000000
+
+/* PDMA descriptor rxd2 */
+#define RX_DMA_DONE BIT(31)
+#define RX_DMA_LSO BIT(30)
+#define RX_DMA_PLEN0(_x) (((_x) & 0x3fff) << 16)
+#define RX_DMA_GET_PLEN0(_x) (((_x) >> 16) & 0x3fff)
+#define RX_DMA_TAG BIT(15)
+
+/* PDMA descriptor rxd3 */
+#define RX_DMA_TPID(_x) (((_x) >> 16) & 0xffff)
+#define RX_DMA_VID(_x) ((_x) & 0xfff)
+
+/* PDMA descriptor rxd4 */
+#define RX_DMA_L4VALID BIT(30)
+#define RX_DMA_FPORT_SHIFT 19
+#define RX_DMA_FPORT_MASK 0x7
+
+struct mtk_rx_dma {
+ unsigned int rxd1;
+ unsigned int rxd2;
+ unsigned int rxd3;
+ unsigned int rxd4;
+} __packed __aligned(4);
+
+/* PDMA tx descriptor bits */
+#define TX_DMA_BUF_LEN 0x3fff
+#define TX_DMA_PLEN0_MASK (TX_DMA_BUF_LEN << 16)
+#define TX_DMA_PLEN0(_x) (((_x) & TX_DMA_BUF_LEN) << 16)
+#define TX_DMA_PLEN1(_x) ((_x) & TX_DMA_BUF_LEN)
+#define TX_DMA_GET_PLEN0(_x) (((_x) >> 16) & TX_DMA_BUF_LEN)
+#define TX_DMA_GET_PLEN1(_x) ((_x) & TX_DMA_BUF_LEN)
+#define TX_DMA_LS1 BIT(14)
+#define TX_DMA_LS0 BIT(30)
+#define TX_DMA_DONE BIT(31)
+#define TX_DMA_FPORT_SHIFT 25
+#define TX_DMA_FPORT_MASK 0x7
+#define TX_DMA_INS_VLAN_MT7621 BIT(16)
+#define TX_DMA_INS_VLAN BIT(7)
+#define TX_DMA_INS_PPPOE BIT(12)
+#define TX_DMA_TAG BIT(15)
+#define TX_DMA_TAG_MASK BIT(15)
+#define TX_DMA_QN(_x) ((_x) << 16)
+#define TX_DMA_PN(_x) ((_x) << 24)
+#define TX_DMA_QN_MASK TX_DMA_QN(0x7)
+#define TX_DMA_PN_MASK TX_DMA_PN(0x7)
+#define TX_DMA_UDF BIT(20)
+#define TX_DMA_CHKSUM (0x7 << 29)
+#define TX_DMA_TSO BIT(28)
+#define TX_DMA_DESP4_DEF (TX_DMA_QN(3) | TX_DMA_PN(1))
+
+/* frame engine counters */
+#define MTK_PPE_AC_BCNT0 (MTK_CMTABLE_OFFSET + 0x00)
+#define MTK_GDMA1_TX_GBCNT (MTK_CMTABLE_OFFSET + 0x300)
+#define MTK_GDMA2_TX_GBCNT (MTK_GDMA1_TX_GBCNT + 0x40)
+
+/* phy device flags */
+#define MTK_PHY_FLAG_PORT BIT(0)
+#define MTK_PHY_FLAG_ATTACH BIT(1)
+
+struct mtk_tx_dma {
+ unsigned int txd1;
+ unsigned int txd2;
+ unsigned int txd3;
+ unsigned int txd4;
+} __packed __aligned(4);
+
+struct mtk_eth;
+struct mtk_mac;
+
+/* manage the attached phys */
+struct mtk_phy {
+ spinlock_t lock;
+
+ struct phy_device *phy[8];
+ struct device_node *phy_node[8];
+ const __be32 *phy_fixed[8];
+ int duplex[8];
+ int speed[8];
+ int tx_fc[8];
+ int rx_fc[8];
+ int (*connect)(struct mtk_mac *mac);
+ void (*disconnect)(struct mtk_mac *mac);
+ void (*start)(struct mtk_mac *mac);
+ void (*stop)(struct mtk_mac *mac);
+};
+
+/* struct mtk_soc_data - the structure that holds the SoC specific data
+ * @reg_table: Some of the legacy registers changed their location
+ * over time. Their offsets are stored in this table
+ *
+ * @init_data: Some features depend on the silicon revision. This
+ * callback allows runtime modification of the content of
+ * this struct
+ * @reset_fe: This callback is used to trigger the reset of the frame
+ * engine
+ * @set_mac: This callback is used to set the unicast mac address
+ * filter
+ * @fwd_config: This callback is used to setup the forward config
+ * register of the MAC
+ * @switch_init: This callback is used to bring up the switch core
+ * @port_init: Some SoCs have ports that can be router to a switch port
+ * or an external PHY. This callback is used to setup these
+ * ports.
+ * @has_carrier: This callback allows driver to check if there is a cable
+ * attached.
+ * @mdio_init: This callbck is used to setup the MDIO bus if one is
+ * present
+ * @mdio_cleanup: This callback is used to cleanup the MDIO state.
+ * @mdio_write: This callback is used to write data to the MDIO bus.
+ * @mdio_read: This callback is used to write data to the MDIO bus.
+ * @mdio_adjust_link: This callback is used to apply the PHY settings.
+ * @piac_offset: the PIAC register has a different different base offset
+ * @hw_features: feature set depends on the SoC type
+ * @dma_ring_size: allow GBit SoCs to set bigger rings than FE SoCs
+ * @napi_weight: allow GBit SoCs to set bigger napi weight than FE SoCs
+ * @dma_type: SoCs is PDMA, QDMA or a mix of the 2
+ * @pdma_glo_cfg: the default DMA configuration
+ * @rx_int: the TX interrupt bits used by the SoC
+ * @tx_int: the TX interrupt bits used by the SoC
+ * @status_int: the Status interrupt bits used by the SoC
+ * @checksum_bit: the bits used to turn on HW checksumming
+ * @txd4: default value of the TXD4 descriptor
+ * @mac_count: the number of MACs that the SoC has
+ * @new_stats: there is a old and new way to read hardware stats
+ * registers
+ * @jumbo_frame: does the SoC support jumbo frames ?
+ * @rx_2b_offset: tell the rx dma to offset the data by 2 bytes
+ * @rx_sg_dma: scatter gather support
+ * @padding_64b enable 64 bit padding
+ * @padding_bug: rt2880 has a padding bug
+ * @has_switch: does the SoC have a built-in switch
+ *
+ * Although all of the supported SoCs share the same basic functionality, there
+ * are several SoC specific functions and features that we need to support. This
+ * struct holds the SoC specific data so that the common core can figure out
+ * how to setup and use these differences.
+ */
+struct mtk_soc_data {
+ const u16 *reg_table;
+
+ void (*init_data)(struct mtk_soc_data *data, struct net_device *netdev);
+ void (*reset_fe)(struct mtk_eth *eth);
+ void (*set_mac)(struct mtk_mac *mac, unsigned char *macaddr);
+ int (*fwd_config)(struct mtk_eth *eth);
+ int (*switch_init)(struct mtk_eth *eth);
+ void (*port_init)(struct mtk_eth *eth, struct mtk_mac *mac,
+ struct device_node *port);
+ int (*has_carrier)(struct mtk_eth *eth);
+ int (*mdio_init)(struct mtk_eth *eth);
+ void (*mdio_cleanup)(struct mtk_eth *eth);
+ int (*mdio_write)(struct mii_bus *bus, int phy_addr, int phy_reg,
+ u16 val);
+ int (*mdio_read)(struct mii_bus *bus, int phy_addr, int phy_reg);
+ void (*mdio_adjust_link)(struct mtk_eth *eth, int port);
+ u32 piac_offset;
+ netdev_features_t hw_features;
+ u32 dma_ring_size;
+ u32 napi_weight;
+ u32 dma_type;
+ u32 pdma_glo_cfg;
+ u32 rx_int;
+ u32 tx_int;
+ u32 status_int;
+ u32 checksum_bit;
+ u32 txd4;
+ u32 mac_count;
+
+ u32 new_stats:1;
+ u32 jumbo_frame:1;
+ u32 rx_2b_offset:1;
+ u32 rx_sg_dma:1;
+ u32 padding_64b:1;
+ u32 padding_bug:1;
+ u32 has_switch:1;
+};
+
+/* ugly macro hack to make sure hw_stats and ethtool strings are consistent */
+#define MTK_STAT_OFFSET 0x40
+#define MTK_STAT_REG_DECLARE \
+ _FE(tx_bytes) \
+ _FE(tx_packets) \
+ _FE(tx_skip) \
+ _FE(tx_collisions) \
+ _FE(rx_bytes) \
+ _FE(rx_packets) \
+ _FE(rx_overflow) \
+ _FE(rx_fcs_errors) \
+ _FE(rx_short_errors) \
+ _FE(rx_long_errors) \
+ _FE(rx_checksum_errors) \
+ _FE(rx_flow_control_packets)
+
+/* struct mtk_hw_stats - the structure that holds the traffic statistics.
+ * @stats_lock: make sure that stats operations are atomic
+ * @reg_offset: the status register offset of the SoC
+ * @syncp: the refcount
+ *
+ * All of the supported SoCs have hardware counters for traffic statstics.
+ * Whenever the status IRQ triggers we can read the latest stats from these
+ * counters and store them in this struct.
+ */
+struct mtk_hw_stats {
+ spinlock_t stats_lock;
+ u32 reg_offset;
+ struct u64_stats_sync syncp;
+
+#define _FE(x) u64 x;
+ MTK_STAT_REG_DECLARE
+#undef _FE
+};
+
+/* PDMA descriptor can point at 1-2 segments. This enum allows us to track how
+ * memory was allocated so that it can be freed properly
+ */
+enum mtk_tx_flags {
+ MTK_TX_FLAGS_SINGLE0 = 0x01,
+ MTK_TX_FLAGS_PAGE0 = 0x02,
+ MTK_TX_FLAGS_PAGE1 = 0x04,
+};
+
+/* struct mtk_tx_buf - This struct holds the pointers to the memory pointed at
+ * by the TX descriptor s
+ * @skb: The SKB pointer of the packet being sent
+ * @dma_addr0: The base addr of the first segment
+ * @dma_len0: The length of the first segment
+ * @dma_addr1: The base addr of the second segment
+ * @dma_len1: The length of the second segment
+ */
+struct mtk_tx_buf {
+ struct sk_buff *skb;
+ u32 flags;
+ DEFINE_DMA_UNMAP_ADDR(dma_addr0);
+ DEFINE_DMA_UNMAP_LEN(dma_len0);
+ DEFINE_DMA_UNMAP_ADDR(dma_addr1);
+ DEFINE_DMA_UNMAP_LEN(dma_len1);
+};
+
+/* struct mtk_tx_ring - This struct holds info describing a TX ring
+ * @tx_dma: The descriptor ring
+ * @tx_buf: The memory pointed at by the ring
+ * @tx_phys: The physical addr of tx_buf
+ * @tx_next_free: Pointer to the next free descriptor
+ * @tx_last_free: Pointer to the last free descriptor
+ * @tx_thresh: The threshold of minimum amount of free descriptors
+ * @tx_map: Callback to map a new packet into the ring
+ * @tx_poll: Callback for the housekeeping function
+ * @tx_clean: Callback for the cleanup function
+ * @tx_ring_size: How many descriptors are in the ring
+ * @tx_free_idx: The index of th next free descriptor
+ * @tx_next_idx: QDMA uses a linked list. This element points to the next
+ * free descriptor in the list
+ * @tx_free_count: QDMA uses a linked list. Track how many free descriptors
+ * are present
+ */
+struct mtk_tx_ring {
+ struct mtk_tx_dma *tx_dma;
+ struct mtk_tx_buf *tx_buf;
+ dma_addr_t tx_phys;
+ struct mtk_tx_dma *tx_next_free;
+ struct mtk_tx_dma *tx_last_free;
+ u16 tx_thresh;
+ int (*tx_map)(struct sk_buff *skb, struct net_device *dev, int tx_num,
+ struct mtk_tx_ring *ring, bool gso);
+ int (*tx_poll)(struct mtk_eth *eth, int budget, bool *tx_again);
+ void (*tx_clean)(struct mtk_eth *eth);
+
+ /* PDMA only */
+ u16 tx_ring_size;
+ u16 tx_free_idx;
+
+ /* QDMA only */
+ u16 tx_next_idx;
+ atomic_t tx_free_count;
+};
+
+/* struct mtk_rx_ring - This struct holds info describing a RX ring
+ * @rx_dma: The descriptor ring
+ * @rx_data: The memory pointed at by the ring
+ * @trx_phys: The physical addr of rx_buf
+ * @rx_ring_size: How many descriptors are in the ring
+ * @rx_buf_size: The size of each packet buffer
+ * @rx_calc_idx: The current head of ring
+ */
+struct mtk_rx_ring {
+ struct mtk_rx_dma *rx_dma;
+ u8 **rx_data;
+ dma_addr_t rx_phys;
+ u16 rx_ring_size;
+ u16 frag_size;
+ u16 rx_buf_size;
+ u16 rx_calc_idx;
+};
+
+/* currently no SoC has more than 2 macs */
+#define MTK_MAX_DEVS 2
+
+/* struct mtk_eth - This is the main datasructure for holding the state
+ * of the driver
+ * @dev: The device pointer
+ * @base: The mapped register i/o base
+ * @page_lock: Make sure that register operations are atomic
+ * @soc: pointer to our SoC specific data
+ * @dummy_dev: we run 2 netdevs on 1 physical DMA ring and need a
+ * dummy for NAPI to work
+ * @netdev: The netdev instances
+ * @mac: Each netdev is linked to a physical MAC
+ * @switch_np: The phandle for the switch
+ * @irq: The IRQ that we are using
+ * @msg_enable: Ethtool msg level
+ * @ysclk: The sysclk rate - neeed for calibration
+ * @ethsys: The register map pointing at the range used to setup
+ * MII modes
+ * @dma_refcnt: track how many netdevs are using the DMA engine
+ * @tx_ring: Pointer to the memore holding info about the TX ring
+ * @rx_ring: Pointer to the memore holding info about the RX ring
+ * @rx_napi: The NAPI struct
+ * @scratch_ring: Newer SoCs need memory for a second HW managed TX ring
+ * @scratch_head: The scratch memory that scratch_ring points to.
+ * @phy: Info about the attached PHYs
+ * @mii_bus: If there is a bus we need to create an instance for it
+ * @link: Track if the ports have a physical link
+ * @sw_priv: Pointer to the switches private data
+ * @vlan_map: RX VID tracking
+ */
+
+struct mtk_eth {
+ struct device *dev;
+ void __iomem *base;
+ spinlock_t page_lock;
+ struct mtk_soc_data *soc;
+ struct net_device dummy_dev;
+ struct net_device *netdev[MTK_MAX_DEVS];
+ struct mtk_mac *mac[MTK_MAX_DEVS];
+ struct device_node *switch_np;
+ int irq;
+ u32 msg_enable;
+ unsigned long sysclk;
+ struct regmap *ethsys;
+ atomic_t dma_refcnt;
+ struct mtk_tx_ring tx_ring;
+ struct mtk_rx_ring rx_ring[2];
+ struct napi_struct rx_napi;
+ struct mtk_tx_dma *scratch_ring;
+ void *scratch_head;
+ struct mtk_phy *phy;
+ struct mii_bus *mii_bus;
+ int link[8];
+ void *sw_priv;
+ unsigned long vlan_map;
+};
+
+/* struct mtk_mac - the structure that holds the info about the MACs of the
+ * SoC
+ * @id: The number of the MAC
+ * @of_node: Our devicetree node
+ * @hw: Backpointer to our main datastruture
+ * @hw_stats: Packet statistics counter
+ * @phy_dev: The attached PHY if available
+ * @phy_flags: The PHYs flags
+ * @pending_work: The workqueue used to reset the dma ring
+ */
+struct mtk_mac {
+ int id;
+ struct device_node *of_node;
+ struct mtk_eth *hw;
+ struct mtk_hw_stats *hw_stats;
+ struct phy_device *phy_dev;
+ u32 phy_flags;
+ struct work_struct pending_work;
+};
+
+/* the struct describing the SoC. these are declared in the soc_xyz.c files */
+extern const struct of_device_id of_mtk_match[];
+
+/* read the hardware status register */
+void mtk_stats_update_mac(struct mtk_mac *mac);
+
+/* default checksum setup handler */
+void mtk_reset(struct mtk_eth *eth, u32 reset_bits);
+
+/* register i/o wrappers */
+void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
+u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
+
+/* default clock calibration handler */
+int mtk_set_clock_cycle(struct mtk_eth *eth);
+
+/* default checksum setup handler */
+void mtk_csum_config(struct mtk_eth *eth);
+
+/* default forward config handler */
+void mtk_fwd_config(struct mtk_eth *eth);
+
+#endif /* MTK_ETH_H */
From: John Crispin <[email protected]>
Signed-off-by: John Crispin <[email protected]>
Signed-off-by: NeilBrown <[email protected]>
---
drivers/staging/Kconfig | 2
drivers/staging/Makefile | 1
drivers/staging/mt7621-pinctrl/Kconfig | 4
drivers/staging/mt7621-pinctrl/Makefile | 3
drivers/staging/mt7621-pinctrl/TODO | 6
drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c | 472 +++++++++++++++++++++++
6 files changed, 488 insertions(+)
create mode 100644 drivers/staging/mt7621-pinctrl/Kconfig
create mode 100644 drivers/staging/mt7621-pinctrl/Makefile
create mode 100644 drivers/staging/mt7621-pinctrl/TODO
create mode 100644 drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index e95ab683331e..cdd04c8b4445 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -122,4 +122,6 @@ source "drivers/staging/vboxvideo/Kconfig"
source "drivers/staging/pi433/Kconfig"
+source "drivers/staging/mt7621-pinctrl/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 4e79a4ad6cf6..db90d85a80d3 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -53,3 +53,4 @@ obj-$(CONFIG_CRYPTO_DEV_CCREE) += ccree/
obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
obj-$(CONFIG_PI433) += pi433/
obj-$(CONFIG_SOC_MT7621) += mt7621-pci/
+obj-$(CONFIG_SOC_MT7621) += mt7621-pinctrl/
diff --git a/drivers/staging/mt7621-pinctrl/Kconfig b/drivers/staging/mt7621-pinctrl/Kconfig
new file mode 100644
index 000000000000..37cf9c3273be
--- /dev/null
+++ b/drivers/staging/mt7621-pinctrl/Kconfig
@@ -0,0 +1,4 @@
+config PINCTRL_RT2880
+ bool "RT2800 pinctrl driver for RALINK/Mediatek SOCs"
+ depends on RALINK
+ select PINMUX
diff --git a/drivers/staging/mt7621-pinctrl/Makefile b/drivers/staging/mt7621-pinctrl/Makefile
new file mode 100644
index 000000000000..856102137a1e
--- /dev/null
+++ b/drivers/staging/mt7621-pinctrl/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_PINCTRL_RT2880) += pinctrl-rt2880.o
+
+ccflags-y += -I$(srctree)/drivers/pinctrl
diff --git a/drivers/staging/mt7621-pinctrl/TODO b/drivers/staging/mt7621-pinctrl/TODO
new file mode 100644
index 000000000000..b2c235a16d5c
--- /dev/null
+++ b/drivers/staging/mt7621-pinctrl/TODO
@@ -0,0 +1,6 @@
+
+- general code review and cleanup
+- should probably be always selected by 'config RALINK'
+- ensure device-tree requirements are documented
+
+Cc: NeilBrown <[email protected]>
diff --git a/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c b/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c
new file mode 100644
index 000000000000..3d2d1c2a006f
--- /dev/null
+++ b/drivers/staging/mt7621-pinctrl/pinctrl-rt2880.c
@@ -0,0 +1,472 @@
+/*
+ * linux/drivers/pinctrl/pinctrl-rt2880.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * publishhed by the Free Software Foundation.
+ *
+ * Copyright (C) 2013 John Crispin <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/machine.h>
+
+#include <asm/mach-ralink/ralink_regs.h>
+#include <asm/mach-ralink/pinmux.h>
+#include <asm/mach-ralink/mt7620.h>
+
+#include "core.h"
+
+#define SYSC_REG_GPIO_MODE 0x60
+#define SYSC_REG_GPIO_MODE2 0x64
+
+struct rt2880_priv {
+ struct device *dev;
+
+ struct pinctrl_pin_desc *pads;
+ struct pinctrl_desc *desc;
+
+ struct rt2880_pmx_func **func;
+ int func_count;
+
+ struct rt2880_pmx_group *groups;
+ const char **group_names;
+ int group_count;
+
+ uint8_t *gpio;
+ int max_pins;
+};
+
+static int rt2880_get_group_count(struct pinctrl_dev *pctrldev)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+ return p->group_count;
+}
+
+static const char *rt2880_get_group_name(struct pinctrl_dev *pctrldev,
+ unsigned group)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+ if (group >= p->group_count)
+ return NULL;
+
+ return p->group_names[group];
+}
+
+static int rt2880_get_group_pins(struct pinctrl_dev *pctrldev,
+ unsigned group,
+ const unsigned **pins,
+ unsigned *num_pins)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+ if (group >= p->group_count)
+ return -EINVAL;
+
+ *pins = p->groups[group].func[0].pins;
+ *num_pins = p->groups[group].func[0].pin_count;
+
+ return 0;
+}
+
+static void rt2880_pinctrl_dt_free_map(struct pinctrl_dev *pctrldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ int i;
+
+ for (i = 0; i < num_maps; i++)
+ if (map[i].type == PIN_MAP_TYPE_CONFIGS_PIN ||
+ map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
+ kfree(map[i].data.configs.configs);
+ kfree(map);
+}
+
+static void rt2880_pinctrl_pin_dbg_show(struct pinctrl_dev *pctrldev,
+ struct seq_file *s,
+ unsigned offset)
+{
+ seq_printf(s, "ralink pio");
+}
+
+static void rt2880_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctrldev,
+ struct device_node *np,
+ struct pinctrl_map **map)
+{
+ const char *function;
+ int func = of_property_read_string(np, "ralink,function", &function);
+ int grps = of_property_count_strings(np, "ralink,group");
+ int i;
+
+ if (func || !grps)
+ return;
+
+ for (i = 0; i < grps; i++) {
+ const char *group;
+
+ of_property_read_string_index(np, "ralink,group", i, &group);
+
+ (*map)->type = PIN_MAP_TYPE_MUX_GROUP;
+ (*map)->name = function;
+ (*map)->data.mux.group = group;
+ (*map)->data.mux.function = function;
+ (*map)++;
+ }
+}
+
+static int rt2880_pinctrl_dt_node_to_map(struct pinctrl_dev *pctrldev,
+ struct device_node *np_config,
+ struct pinctrl_map **map,
+ unsigned *num_maps)
+{
+ int max_maps = 0;
+ struct pinctrl_map *tmp;
+ struct device_node *np;
+
+ for_each_child_of_node(np_config, np) {
+ int ret = of_property_count_strings(np, "ralink,group");
+
+ if (ret >= 0)
+ max_maps += ret;
+ }
+
+ if (!max_maps)
+ return max_maps;
+
+ *map = kzalloc(max_maps * sizeof(struct pinctrl_map), GFP_KERNEL);
+ if (!*map)
+ return -ENOMEM;
+
+ tmp = *map;
+
+ for_each_child_of_node(np_config, np)
+ rt2880_pinctrl_dt_subnode_to_map(pctrldev, np, &tmp);
+ *num_maps = max_maps;
+
+ return 0;
+}
+
+static const struct pinctrl_ops rt2880_pctrl_ops = {
+ .get_groups_count = rt2880_get_group_count,
+ .get_group_name = rt2880_get_group_name,
+ .get_group_pins = rt2880_get_group_pins,
+ .pin_dbg_show = rt2880_pinctrl_pin_dbg_show,
+ .dt_node_to_map = rt2880_pinctrl_dt_node_to_map,
+ .dt_free_map = rt2880_pinctrl_dt_free_map,
+};
+
+static int rt2880_pmx_func_count(struct pinctrl_dev *pctrldev)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+ return p->func_count;
+}
+
+static const char *rt2880_pmx_func_name(struct pinctrl_dev *pctrldev,
+ unsigned func)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+ return p->func[func]->name;
+}
+
+static int rt2880_pmx_group_get_groups(struct pinctrl_dev *pctrldev,
+ unsigned func,
+ const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+ if (p->func[func]->group_count == 1)
+ *groups = &p->group_names[p->func[func]->groups[0]];
+ else
+ *groups = p->group_names;
+
+ *num_groups = p->func[func]->group_count;
+
+ return 0;
+}
+
+static int rt2880_pmx_group_enable(struct pinctrl_dev *pctrldev,
+ unsigned func,
+ unsigned group)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+ u32 mode = 0;
+ u32 reg = SYSC_REG_GPIO_MODE;
+ int i;
+ int shift;
+
+ /* dont allow double use */
+ if (p->groups[group].enabled) {
+ dev_err(p->dev, "%s is already enabled\n", p->groups[group].name);
+ return -EBUSY;
+ }
+
+ p->groups[group].enabled = 1;
+ p->func[func]->enabled = 1;
+
+ shift = p->groups[group].shift;
+ if (shift >= 32) {
+ shift -= 32;
+ reg = SYSC_REG_GPIO_MODE2;
+ }
+ mode = rt_sysc_r32(reg);
+ mode &= ~(p->groups[group].mask << shift);
+
+ /* mark the pins as gpio */
+ for (i = 0; i < p->groups[group].func[0].pin_count; i++)
+ p->gpio[p->groups[group].func[0].pins[i]] = 1;
+
+ /* function 0 is gpio and needs special handling */
+ if (func == 0) {
+ mode |= p->groups[group].gpio << shift;
+ } else {
+ for (i = 0; i < p->func[func]->pin_count; i++)
+ p->gpio[p->func[func]->pins[i]] = 0;
+ mode |= p->func[func]->value << shift;
+ }
+ rt_sysc_w32(mode, reg);
+
+ return 0;
+}
+
+static int rt2880_pmx_group_gpio_request_enable(struct pinctrl_dev *pctrldev,
+ struct pinctrl_gpio_range *range,
+ unsigned pin)
+{
+ struct rt2880_priv *p = pinctrl_dev_get_drvdata(pctrldev);
+
+ if (!p->gpio[pin]) {
+ dev_err(p->dev, "pin %d is not set to gpio mux\n", pin);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct pinmux_ops rt2880_pmx_group_ops = {
+ .get_functions_count = rt2880_pmx_func_count,
+ .get_function_name = rt2880_pmx_func_name,
+ .get_function_groups = rt2880_pmx_group_get_groups,
+ .set_mux = rt2880_pmx_group_enable,
+ .gpio_request_enable = rt2880_pmx_group_gpio_request_enable,
+};
+
+static struct pinctrl_desc rt2880_pctrl_desc = {
+ .owner = THIS_MODULE,
+ .name = "rt2880-pinmux",
+ .pctlops = &rt2880_pctrl_ops,
+ .pmxops = &rt2880_pmx_group_ops,
+};
+
+static struct rt2880_pmx_func gpio_func = {
+ .name = "gpio",
+};
+
+static int rt2880_pinmux_index(struct rt2880_priv *p)
+{
+ struct rt2880_pmx_func **f;
+ struct rt2880_pmx_group *mux = p->groups;
+ int i, j, c = 0;
+
+ /* count the mux functions */
+ while (mux->name) {
+ p->group_count++;
+ mux++;
+ }
+
+ /* allocate the group names array needed by the gpio function */
+ p->group_names = devm_kzalloc(p->dev, sizeof(char *) * p->group_count, GFP_KERNEL);
+ if (!p->group_names)
+ return -1;
+
+ for (i = 0; i < p->group_count; i++) {
+ p->group_names[i] = p->groups[i].name;
+ p->func_count += p->groups[i].func_count;
+ }
+
+ /* we have a dummy function[0] for gpio */
+ p->func_count++;
+
+ /* allocate our function and group mapping index buffers */
+ f = p->func = devm_kzalloc(p->dev, sizeof(struct rt2880_pmx_func) * p->func_count, GFP_KERNEL);
+ gpio_func.groups = devm_kzalloc(p->dev, sizeof(int) * p->group_count, GFP_KERNEL);
+ if (!f || !gpio_func.groups)
+ return -1;
+
+ /* add a backpointer to the function so it knows its group */
+ gpio_func.group_count = p->group_count;
+ for (i = 0; i < gpio_func.group_count; i++)
+ gpio_func.groups[i] = i;
+
+ f[c] = &gpio_func;
+ c++;
+
+ /* add remaining functions */
+ for (i = 0; i < p->group_count; i++) {
+ for (j = 0; j < p->groups[i].func_count; j++) {
+ f[c] = &p->groups[i].func[j];
+ f[c]->groups = devm_kzalloc(p->dev, sizeof(int), GFP_KERNEL);
+ f[c]->groups[0] = i;
+ f[c]->group_count = 1;
+ c++;
+ }
+ }
+ return 0;
+}
+
+static int rt2880_pinmux_pins(struct rt2880_priv *p)
+{
+ int i, j;
+
+ /* loop over the functions and initialize the pins array. also work out the highest pin used */
+ for (i = 0; i < p->func_count; i++) {
+ int pin;
+
+ if (!p->func[i]->pin_count)
+ continue;
+
+ p->func[i]->pins = devm_kzalloc(p->dev, sizeof(int) * p->func[i]->pin_count, GFP_KERNEL);
+ for (j = 0; j < p->func[i]->pin_count; j++)
+ p->func[i]->pins[j] = p->func[i]->pin_first + j;
+
+ pin = p->func[i]->pin_first + p->func[i]->pin_count;
+ if (pin > p->max_pins)
+ p->max_pins = pin;
+ }
+
+ /* the buffer that tells us which pins are gpio */
+ p->gpio = devm_kzalloc(p->dev,sizeof(uint8_t) * p->max_pins,
+ GFP_KERNEL);
+ /* the pads needed to tell pinctrl about our pins */
+ p->pads = devm_kzalloc(p->dev,
+ sizeof(struct pinctrl_pin_desc) * p->max_pins,
+ GFP_KERNEL);
+ if (!p->pads || !p->gpio ) {
+ dev_err(p->dev, "Failed to allocate gpio data\n");
+ return -ENOMEM;
+ }
+
+ memset(p->gpio, 1, sizeof(uint8_t) * p->max_pins);
+ for (i = 0; i < p->func_count; i++) {
+ if (!p->func[i]->pin_count)
+ continue;
+
+ for (j = 0; j < p->func[i]->pin_count; j++)
+ p->gpio[p->func[i]->pins[j]] = 0;
+ }
+
+ /* pin 0 is always a gpio */
+ p->gpio[0] = 1;
+
+ /* set the pads */
+ for (i = 0; i < p->max_pins; i++) {
+ /* strlen("ioXY") + 1 = 5 */
+ char *name = devm_kzalloc(p->dev, 5, GFP_KERNEL);
+
+ if (!name) {
+ dev_err(p->dev, "Failed to allocate pad name\n");
+ return -ENOMEM;
+ }
+ snprintf(name, 5, "io%d", i);
+ p->pads[i].number = i;
+ p->pads[i].name = name;
+ }
+ p->desc->pins = p->pads;
+ p->desc->npins = p->max_pins;
+
+ return 0;
+}
+
+static int rt2880_pinmux_probe(struct platform_device *pdev)
+{
+ struct rt2880_priv *p;
+ struct pinctrl_dev *dev;
+ struct device_node *np;
+
+ if (!rt2880_pinmux_data)
+ return -ENOSYS;
+
+ /* setup the private data */
+ p = devm_kzalloc(&pdev->dev, sizeof(struct rt2880_priv), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ p->dev = &pdev->dev;
+ p->desc = &rt2880_pctrl_desc;
+ p->groups = rt2880_pinmux_data;
+ platform_set_drvdata(pdev, p);
+
+ /* init the device */
+ if (rt2880_pinmux_index(p)) {
+ dev_err(&pdev->dev, "failed to load index\n");
+ return -EINVAL;
+ }
+ if (rt2880_pinmux_pins(p)) {
+ dev_err(&pdev->dev, "failed to load pins\n");
+ return -EINVAL;
+ }
+ dev = pinctrl_register(p->desc, &pdev->dev, p);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+
+ /* finalize by adding gpio ranges for enables gpio controllers */
+ for_each_compatible_node(np, NULL, "ralink,rt2880-gpio") {
+ const __be32 *ngpio, *gpiobase;
+ struct pinctrl_gpio_range *range;
+ char *name;
+
+ if (!of_device_is_available(np))
+ continue;
+
+ ngpio = of_get_property(np, "ralink,num-gpios", NULL);
+ gpiobase = of_get_property(np, "ralink,gpio-base", NULL);
+ if (!ngpio || !gpiobase) {
+ dev_err(&pdev->dev, "failed to load chip info\n");
+ return -EINVAL;
+ }
+
+ range = devm_kzalloc(p->dev, sizeof(struct pinctrl_gpio_range) + 4, GFP_KERNEL);
+ range->name = name = (char *) &range[1];
+ sprintf(name, "pio");
+ range->npins = __be32_to_cpu(*ngpio);
+ range->base = __be32_to_cpu(*gpiobase);
+ range->pin_base = range->base;
+ pinctrl_add_gpio_range(dev, range);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id rt2880_pinmux_match[] = {
+ { .compatible = "ralink,rt2880-pinmux" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt2880_pinmux_match);
+
+static struct platform_driver rt2880_pinmux_driver = {
+ .probe = rt2880_pinmux_probe,
+ .driver = {
+ .name = "rt2880-pinmux",
+ .owner = THIS_MODULE,
+ .of_match_table = rt2880_pinmux_match,
+ },
+};
+
+int __init rt2880_pinmux_init(void)
+{
+ return platform_driver_register(&rt2880_pinmux_driver);
+}
+
+core_initcall_sync(rt2880_pinmux_init);
On Thu, Mar 15 2018, NeilBrown wrote:
> Hi Greg,
> I'd like to submit the following drivers to staging. They
> are all for components of the mt7621 MIPS-based SOC from Mediatek.
> I lifted them out of libreCMC and (for the ethernet code) out
> of email posts from about 2 years ago. I forward-ported
> them to mainline, fixed enough bugs that they mostly work,
> and moved them to staging.
>
> With these patches the GNUBEE is almost usable. It needs
> one patch to arch/mips/kernel/setup.c because mips_cm_probe()
> is being called too early. To successfully reboot (i.e warm-restart)
> it needs a small patch to drivers/mtd/devices/m25p80.c.
>
> I've made a point of only included code that I can actually test
> on hardware that I have. That has meant selecting only a few patches
> from a series and in a couple of cases, discarding some files from a
> patch. I haven't discard code from within a file that I need part
> of.
>
> All the driver patches had a From: of John Crispin so he is cc:ed -
> thanks John!!
Unfortunately the email address I had for John bounces. So if anyone
replies for any reason, you might like to delete his address from the
Cc.
Thanks,
NeilBrown
These patches need better changelogs. Some of them don't have a
changelog at all. Like just say this what kind of device this is or
something?
regards,
dan carpenter
This all seems fine. Generally the requirements for staging are that it
has a TODO, someone to work on it, and it doesn't break the build. But
some of the patches don't have commit message and those are required and
some of the commit messages are just the changes you have made not don't
describe the actual code...
John Crispin's email is [email protected].
regards,
dan carpenter
On Thu, Mar 15 2018, Dan Carpenter wrote:
> This all seems fine. Generally the requirements for staging are that it
> has a TODO, someone to work on it, and it doesn't break the build. But
> some of the patches don't have commit message and those are required and
> some of the commit messages are just the changes you have made not don't
> describe the actual code...
Thanks for having a look.
It seems odd to require detailed commit messages, when we don't require
the same level of quality in the code.
Naturally when the driver is moved out of staging a properly detailed
commit message should be added, but is that needed on the way in to
staging? At this stage I don't know much more than is already there.
After I've cleaned up the code I probably will.
For patch 01/13 you asked "what kind of device this is". The subject
line makes it clear that it is a "pcie driver". What extra detail did
you want? Would it be sufficient to just copy the subject line so that
it appears twice in the commit message?
>
> John Crispin's email is [email protected].
Thanks.
NeilBrown
>
> regards,
> dan carpenter
On 15/03/18 11:48, Dan Carpenter wrote:
> This all seems fine. Generally the requirements for staging are that it
> has a TODO, someone to work on it, and it doesn't break the build. But
> some of the patches don't have commit message and those are required and
> some of the commit messages are just the changes you have made not don't
> describe the actual code...
>
> John Crispin's email is [email protected].
>
> regards,
> dan carpenter
>
Hi All,
looks like i was CC'ed on the openwrt addr, which no longer exists. This
series makes no sense. None of the stuff posted is anywhere near ready
to be upstreamed.
* we dont need a dedicated pinctrl driver, pinctrl-single will work fine
on these SoCs
* the DMA/sdhci driver is a hacked up version of the SDK driver.
* drivers/net/ethernet/mediatek/* works on mt7623 and is easily portable
to mt7621, same goes for the gsw driver.
John
On Thu, Mar 15, 2018 at 10:04:33PM +1100, NeilBrown wrote:
> On Thu, Mar 15 2018, Dan Carpenter wrote:
>
> > This all seems fine. Generally the requirements for staging are that it
> > has a TODO, someone to work on it, and it doesn't break the build. But
> > some of the patches don't have commit message and those are required and
> > some of the commit messages are just the changes you have made not don't
> > describe the actual code...
>
> Thanks for having a look.
> It seems odd to require detailed commit messages, when we don't require
> the same level of quality in the code.
> Naturally when the driver is moved out of staging a properly detailed
> commit message should be added, but is that needed on the way in to
> staging? At this stage I don't know much more than is already there.
> After I've cleaned up the code I probably will.
>
> For patch 01/13 you asked "what kind of device this is". The subject
> line makes it clear that it is a "pcie driver". What extra detail did
> you want? Would it be sufficient to just copy the subject line so that
> it appears twice in the commit message?
>
Ah... Sorry. It's literally a pcie driver. For some reason I thought
it was a device that ran over pcie.
We don't require a detailed changelog, but you have to put something...
Probably just restating the subject and adding that it's for the gnubee1
is fine.
regards,
dan carpenter
On Thu, Mar 15, 2018 at 12:07:26PM +0100, John Crispin wrote:
> None of the stuff posted is anywhere near ready to be
> upstreamed.
Yeah. This is staging so we accept any quality...
The rest of the information is very useful. Thanks!
regards,
dan carpenter
On Thu, Mar 15 2018, Dan Carpenter wrote:
> On Thu, Mar 15, 2018 at 10:04:33PM +1100, NeilBrown wrote:
>> On Thu, Mar 15 2018, Dan Carpenter wrote:
>>
>> > This all seems fine. Generally the requirements for staging are that it
>> > has a TODO, someone to work on it, and it doesn't break the build. But
>> > some of the patches don't have commit message and those are required and
>> > some of the commit messages are just the changes you have made not don't
>> > describe the actual code...
>>
>> Thanks for having a look.
>> It seems odd to require detailed commit messages, when we don't require
>> the same level of quality in the code.
>> Naturally when the driver is moved out of staging a properly detailed
>> commit message should be added, but is that needed on the way in to
>> staging? At this stage I don't know much more than is already there.
>> After I've cleaned up the code I probably will.
>>
>> For patch 01/13 you asked "what kind of device this is". The subject
>> line makes it clear that it is a "pcie driver". What extra detail did
>> you want? Would it be sufficient to just copy the subject line so that
>> it appears twice in the commit message?
>>
>
> Ah... Sorry. It's literally a pcie driver. For some reason I thought
> it was a device that ran over pcie.
>
> We don't require a detailed changelog, but you have to put something...
> Probably just restating the subject and adding that it's for the gnubee1
> is fine.
I'll resend sometime next week with more words. However could you
please clarify a couple of things for me?
1/ Why do you (sometimes) call the commit message a "change log". When
I see the term "change log" in the context of a patch, my first
thought is that it it means a log of changes that have been made to
the patch - typically through the review cycle. But that isn't what
you mean. This has confused me a couple of times.
2/ Why don't you consider the first line of the commit message to be
part of the commit message? Why is duplication required?
(You said "some of the patches don't have commit message[s]",
which isn't true, though some of the messages are only one line).
Maybe the requirements on the commit message (including this
duplication) could be included in the "Staging trees" section of
Documentation/process/2.process.rst.
That file only lists the TODO and "doesn't break the build"
requirements. It doesn't metion the "someone to work on it" requirement.
That might seem obvious, but it doesn't hurt to be explicit.
Thanks,
NeilBrown
On Thu, Mar 15 2018, John Crispin wrote:
> On 15/03/18 11:48, Dan Carpenter wrote:
>> This all seems fine. Generally the requirements for staging are that it
>> has a TODO, someone to work on it, and it doesn't break the build. But
>> some of the patches don't have commit message and those are required and
>> some of the commit messages are just the changes you have made not don't
>> describe the actual code...
>>
>> John Crispin's email is [email protected].
>>
>> regards,
>> dan carpenter
>>
> Hi All,
>
> looks like i was CC'ed on the openwrt addr, which no longer exists. This
> series makes no sense. None of the stuff posted is anywhere near ready
> to be upstreamed.
>
> * we dont need a dedicated pinctrl driver, pinctrl-single will work fine
> on these SoCs
> * the DMA/sdhci driver is a hacked up version of the SDK driver.
> * drivers/net/ethernet/mediatek/* works on mt7623 and is easily portable
> to mt7621, same goes for the gsw driver.
Hi John,
I think it makes sense in that, with the patches, the hardware works, and
without the patches (at least the first) you cannot even build with
CONFIG_SOC_MT7621=y as pcibios_map_irq() is undefined. Having
working code is a great starting point for further development.
It certainly isn't ready for upstream, which is why it is heading for
drivers/staging. This is explicitly for code that isn't yet ready.
By putting the code there it should be safe from bit-rot, and can be
worked on by multiple people. It gets increased visibility so people
can say how bad it is (as you have done - thanks). This feed back is a
valuable part of improving the code and getting it out of staging.
I'll add notes to various TODO files based on your comments. If you
have anything else to add, it would be most welcome. Thank you for
making these patches available in the first place, so that my hardware
can work!
Thanks,
NeilBrown
On 15/03/18 21:12, NeilBrown wrote:
> On Thu, Mar 15 2018, John Crispin wrote:
>
>> On 15/03/18 11:48, Dan Carpenter wrote:
>>> This all seems fine. Generally the requirements for staging are that it
>>> has a TODO, someone to work on it, and it doesn't break the build. But
>>> some of the patches don't have commit message and those are required and
>>> some of the commit messages are just the changes you have made not don't
>>> describe the actual code...
>>>
>>> John Crispin's email is [email protected].
>>>
>>> regards,
>>> dan carpenter
>>>
>> Hi All,
>>
>> looks like i was CC'ed on the openwrt addr, which no longer exists. This
>> series makes no sense. None of the stuff posted is anywhere near ready
>> to be upstreamed.
>>
>> * we dont need a dedicated pinctrl driver, pinctrl-single will work fine
>> on these SoCs
>> * the DMA/sdhci driver is a hacked up version of the SDK driver.
>> * drivers/net/ethernet/mediatek/* works on mt7623 and is easily portable
>> to mt7621, same goes for the gsw driver.
> Hi John,
> I think it makes sense in that, with the patches, the hardware works, and
> without the patches (at least the first) you cannot even build with
> CONFIG_SOC_MT7621=y as pcibios_map_irq() is undefined. Having
> working code is a great starting point for further development.
> It certainly isn't ready for upstream, which is why it is heading for
> drivers/staging. This is explicitly for code that isn't yet ready.
> By putting the code there it should be safe from bit-rot, and can be
> worked on by multiple people. It gets increased visibility so people
> can say how bad it is (as you have done - thanks). This feed back is a
> valuable part of improving the code and getting it out of staging.
>
> I'll add notes to various TODO files based on your comments. If you
> have anything else to add, it would be most welcome. Thank you for
> making these patches available in the first place, so that my hardware
> can work!
>
> Thanks,
> NeilBrown
Hi Neil,
I understand your reasoning, however ...
only the pcie driver is worth merging. all other drivers are already
inside the kernel for mt7623 and can be easily adapted to work on
mt7621. having duplicate drivers is a certain no-go.
cleaning up the pci driver is a matter of a few days work. merging a
shitty pci driver just to postpone doing the 3-5 days work involved to
polish it seems a rally bad trade-off.
i strongly oppose having any of this code merged into the kernel, even
if it is only the staging area.
John
On Thu, Mar 15 2018, John Crispin wrote:
> On 15/03/18 21:12, NeilBrown wrote:
>> On Thu, Mar 15 2018, John Crispin wrote:
>>
>>> On 15/03/18 11:48, Dan Carpenter wrote:
>>>> This all seems fine. Generally the requirements for staging are that it
>>>> has a TODO, someone to work on it, and it doesn't break the build. But
>>>> some of the patches don't have commit message and those are required and
>>>> some of the commit messages are just the changes you have made not don't
>>>> describe the actual code...
>>>>
>>>> John Crispin's email is [email protected].
>>>>
>>>> regards,
>>>> dan carpenter
>>>>
>>> Hi All,
>>>
>>> looks like i was CC'ed on the openwrt addr, which no longer exists. This
>>> series makes no sense. None of the stuff posted is anywhere near ready
>>> to be upstreamed.
>>>
>>> * we dont need a dedicated pinctrl driver, pinctrl-single will work fine
>>> on these SoCs
>>> * the DMA/sdhci driver is a hacked up version of the SDK driver.
>>> * drivers/net/ethernet/mediatek/* works on mt7623 and is easily portable
>>> to mt7621, same goes for the gsw driver.
>> Hi John,
>> I think it makes sense in that, with the patches, the hardware works, and
>> without the patches (at least the first) you cannot even build with
>> CONFIG_SOC_MT7621=y as pcibios_map_irq() is undefined. Having
>> working code is a great starting point for further development.
>> It certainly isn't ready for upstream, which is why it is heading for
>> drivers/staging. This is explicitly for code that isn't yet ready.
>> By putting the code there it should be safe from bit-rot, and can be
>> worked on by multiple people. It gets increased visibility so people
>> can say how bad it is (as you have done - thanks). This feed back is a
>> valuable part of improving the code and getting it out of staging.
>>
>> I'll add notes to various TODO files based on your comments. If you
>> have anything else to add, it would be most welcome. Thank you for
>> making these patches available in the first place, so that my hardware
>> can work!
>>
>> Thanks,
>> NeilBrown
>
> Hi Neil,
>
> I understand your reasoning, however ...
> only the pcie driver is worth merging. all other drivers are already
> inside the kernel for mt7623 and can be easily adapted to work on
> mt7621. having duplicate drivers is a certain no-go.
> cleaning up the pci driver is a matter of a few days work. merging a
> shitty pci driver just to postpone doing the 3-5 days work involved to
> polish it seems a rally bad trade-off.
> i strongly oppose having any of this code merged into the kernel, even
> if it is only the staging area.
Hi John,
I don't understand why you would want to deny people easy access to code
which makes their hardware work. I'm sure that isn't your intention,
but it could well be the effect. If you think you can make it work in
3-5 days, then please go right ahead. I will happily test anything you
submit. I suspect the most likely outcome, however, is that I'll end
up doing all the work, and I'm sure it will take a lot more than 3-5
days and will involve a lot of learning. I'll be happy if I can get
all of this back out of staging in 6 months. That is an extra 6 months
(at least) that people will be able to use a mainline kernel on their
gnubee.
With respect to your suggestion that "having duplicate drivers is a
certain no-go", I don't think that is correct. As an approximate
counter-point, I'm in the process of cleaning up the
drivers/staging/lustre filesystem with the hope of eventually moving it
out of staging. It had duplicate wait_event() code, duplicate PRNG,
duplicate workqueues, duplicate resizeable hashtable, duplicate tracing
infrastructure, and more that I haven't had a chance to look closely at
yet. This duplication certainly kept it out of linux/fs/, but has no
bearing on whether it belong in linux/drivers/staging/.
Thanks,
NeilBrown
On Fri, Mar 16, 2018 at 07:02:51AM +1100, NeilBrown wrote:
> On Thu, Mar 15 2018, Dan Carpenter wrote:
>
> > On Thu, Mar 15, 2018 at 10:04:33PM +1100, NeilBrown wrote:
> >> On Thu, Mar 15 2018, Dan Carpenter wrote:
> >>
> >> > This all seems fine. Generally the requirements for staging are that it
> >> > has a TODO, someone to work on it, and it doesn't break the build. But
> >> > some of the patches don't have commit message and those are required and
> >> > some of the commit messages are just the changes you have made not don't
> >> > describe the actual code...
> >>
> >> Thanks for having a look.
> >> It seems odd to require detailed commit messages, when we don't require
> >> the same level of quality in the code.
> >> Naturally when the driver is moved out of staging a properly detailed
> >> commit message should be added, but is that needed on the way in to
> >> staging? At this stage I don't know much more than is already there.
> >> After I've cleaned up the code I probably will.
> >>
> >> For patch 01/13 you asked "what kind of device this is". The subject
> >> line makes it clear that it is a "pcie driver". What extra detail did
> >> you want? Would it be sufficient to just copy the subject line so that
> >> it appears twice in the commit message?
> >>
> >
> > Ah... Sorry. It's literally a pcie driver. For some reason I thought
> > it was a device that ran over pcie.
> >
> > We don't require a detailed changelog, but you have to put something...
> > Probably just restating the subject and adding that it's for the gnubee1
> > is fine.
>
> I'll resend sometime next week with more words. However could you
> please clarify a couple of things for me?
>
> 1/ Why do you (sometimes) call the commit message a "change log". When
> I see the term "change log" in the context of a patch, my first
> thought is that it it means a log of changes that have been made to
> the patch - typically through the review cycle. But that isn't what
> you mean. This has confused me a couple of times.
>
Sorry. Yeah. I do use them interchangeably which is probably not the
right thing.
> 2/ Why don't you consider the first line of the commit message to be
> part of the commit message? Why is duplication required?
> (You said "some of the patches don't have commit message[s]",
> which isn't true, though some of the messages are only one line).
>
First of all, you're a writer. If you don't like to do things then you
need to keep those skills hidden away. I never tell anyone that I know
how to program in COBOL (True story. COBOL is great for formatting text
on dot matrix printers and for doing decimal math.)
This is a hard requirement from Greg, not something that specific to
*me* although I do agree with the requirement as well. The idea of
forcing everyone to write a commit message is that we're hoping they
take a little time to tell a story about what the patch is. Sometimes
they're not English majors or whatever and they just restate the subject
and whatever, that's fine.
The first patch I reviewed in this series was:
[PATCH 03/13] staging: mt7621-gpio: ralink: add mt7621 gpio controller
A good commit message might be:
This adds Mediatek GPIO support for the mt7621 chip. It's used on
the Gnubee NAS hardware and a couple other MIPS SoCs. This code
originally came from the OpenWRT project.
That information was all there in the patch 0 commit but that gets
dropped. It's feels a bit weird to put that boilerplate information in
every commit, but it means that it's there when we do a git log on the
file so I think it's a good idea.
Also, and this is my fault not yours of course, but my email client is
all text and looks exactly like marc.info:
https://marc.info/?l=linux-driver-devel&m=152105965413484&w=2
It's hard to see the subject so I normally don't even read it when I'm
looking at patches.
regards,
dan carpenter