Commit 32faca66 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'staging-5.1-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging

Pull staging driver fixes from Greg KH:
 "Here are some small staging driver fixes for 5.1-rc3, and one driver
  removal.

  The biggest thing here is the removal of the mt7621-eth driver as a
  "real" network driver was merged in 5.1-rc1 for this hardware, so this
  old driver can now be removed.

  Other than that, there are just a number of small fixes, all resolving
  reported issues and some potential corner cases for error handling
  paths.

  All of these have been in linux-next with no reported issues"

* tag 'staging-5.1-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging:
  staging: vt6655: Remove vif check from vnt_interrupt
  staging: erofs: keep corrupted fs from crashing kernel in erofs_readdir()
  staging: octeon-ethernet: fix incorrect PHY mode
  staging: vc04_services: Fix an error code in vchiq_probe()
  staging: erofs: fix error handling when failed to read compresssed data
  staging: vt6655: Fix interrupt race condition on device start up.
  staging: rtlwifi: Fix potential NULL pointer dereference of kzalloc
  staging: rtl8712: uninitialized memory in read_bbreg_hdl()
  staging: rtlwifi: rtl8822b: fix to avoid potential NULL pointer dereference
  staging: rtl8188eu: Fix potential NULL pointer dereference of kcalloc
  staging, mt7621-pci: fix build without pci support
  staging: speakup_soft: Fix alternate speech with other synths
  staging: axis-fifo: add CONFIG_OF dependency
  staging: olpc_dcon_xo_1: add missing 'const' qualifier
  staging: comedi: ni_mio_common: Fix divide-by-zero for DIO cmdtest
  staging: erofs: fix to handle error path of erofs_vmap()
  staging: mt7621-dts: update ethernet settings.
  staging: remove mt7621-eth
parents 52afe190 cc26358f
...@@ -114,8 +114,6 @@ source "drivers/staging/ralink-gdma/Kconfig" ...@@ -114,8 +114,6 @@ source "drivers/staging/ralink-gdma/Kconfig"
source "drivers/staging/mt7621-mmc/Kconfig" source "drivers/staging/mt7621-mmc/Kconfig"
source "drivers/staging/mt7621-eth/Kconfig"
source "drivers/staging/mt7621-dts/Kconfig" source "drivers/staging/mt7621-dts/Kconfig"
source "drivers/staging/gasket/Kconfig" source "drivers/staging/gasket/Kconfig"
......
...@@ -47,7 +47,6 @@ obj-$(CONFIG_SPI_MT7621) += mt7621-spi/ ...@@ -47,7 +47,6 @@ obj-$(CONFIG_SPI_MT7621) += mt7621-spi/
obj-$(CONFIG_SOC_MT7621) += mt7621-dma/ obj-$(CONFIG_SOC_MT7621) += mt7621-dma/
obj-$(CONFIG_DMA_RALINK) += ralink-gdma/ obj-$(CONFIG_DMA_RALINK) += ralink-gdma/
obj-$(CONFIG_MTK_MMC) += mt7621-mmc/ obj-$(CONFIG_MTK_MMC) += mt7621-mmc/
obj-$(CONFIG_NET_MEDIATEK_SOC_STAGING) += mt7621-eth/
obj-$(CONFIG_SOC_MT7621) += mt7621-dts/ obj-$(CONFIG_SOC_MT7621) += mt7621-dts/
obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/ obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/
obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# #
config XIL_AXIS_FIFO config XIL_AXIS_FIFO
tristate "Xilinx AXI-Stream FIFO IP core driver" tristate "Xilinx AXI-Stream FIFO IP core driver"
depends on OF
default n default n
help help
This adds support for the Xilinx AXI-Stream This adds support for the Xilinx AXI-Stream
......
...@@ -1001,6 +1001,8 @@ int comedi_dio_insn_config(struct comedi_device *dev, ...@@ -1001,6 +1001,8 @@ int comedi_dio_insn_config(struct comedi_device *dev,
unsigned int mask); unsigned int mask);
unsigned int comedi_dio_update_state(struct comedi_subdevice *s, unsigned int comedi_dio_update_state(struct comedi_subdevice *s,
unsigned int *data); unsigned int *data);
unsigned int comedi_bytes_per_scan_cmd(struct comedi_subdevice *s,
struct comedi_cmd *cmd);
unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s); unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s);
unsigned int comedi_nscans_left(struct comedi_subdevice *s, unsigned int comedi_nscans_left(struct comedi_subdevice *s,
unsigned int nscans); unsigned int nscans);
......
...@@ -394,11 +394,13 @@ unsigned int comedi_dio_update_state(struct comedi_subdevice *s, ...@@ -394,11 +394,13 @@ unsigned int comedi_dio_update_state(struct comedi_subdevice *s,
EXPORT_SYMBOL_GPL(comedi_dio_update_state); EXPORT_SYMBOL_GPL(comedi_dio_update_state);
/** /**
* comedi_bytes_per_scan() - Get length of asynchronous command "scan" in bytes * comedi_bytes_per_scan_cmd() - Get length of asynchronous command "scan" in
* bytes
* @s: COMEDI subdevice. * @s: COMEDI subdevice.
* @cmd: COMEDI command.
* *
* Determines the overall scan length according to the subdevice type and the * Determines the overall scan length according to the subdevice type and the
* number of channels in the scan. * number of channels in the scan for the specified command.
* *
* For digital input, output or input/output subdevices, samples for * For digital input, output or input/output subdevices, samples for
* multiple channels are assumed to be packed into one or more unsigned * multiple channels are assumed to be packed into one or more unsigned
...@@ -408,9 +410,9 @@ EXPORT_SYMBOL_GPL(comedi_dio_update_state); ...@@ -408,9 +410,9 @@ EXPORT_SYMBOL_GPL(comedi_dio_update_state);
* *
* Returns the overall scan length in bytes. * Returns the overall scan length in bytes.
*/ */
unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s) unsigned int comedi_bytes_per_scan_cmd(struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{ {
struct comedi_cmd *cmd = &s->async->cmd;
unsigned int num_samples; unsigned int num_samples;
unsigned int bits_per_sample; unsigned int bits_per_sample;
...@@ -427,6 +429,29 @@ unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s) ...@@ -427,6 +429,29 @@ unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s)
} }
return comedi_samples_to_bytes(s, num_samples); return comedi_samples_to_bytes(s, num_samples);
} }
EXPORT_SYMBOL_GPL(comedi_bytes_per_scan_cmd);
/**
* comedi_bytes_per_scan() - Get length of asynchronous command "scan" in bytes
* @s: COMEDI subdevice.
*
* Determines the overall scan length according to the subdevice type and the
* number of channels in the scan for the current command.
*
* For digital input, output or input/output subdevices, samples for
* multiple channels are assumed to be packed into one or more unsigned
* short or unsigned int values according to the subdevice's %SDF_LSAMPL
* flag. For other types of subdevice, samples are assumed to occupy a
* whole unsigned short or unsigned int according to the %SDF_LSAMPL flag.
*
* Returns the overall scan length in bytes.
*/
unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s)
{
struct comedi_cmd *cmd = &s->async->cmd;
return comedi_bytes_per_scan_cmd(s, cmd);
}
EXPORT_SYMBOL_GPL(comedi_bytes_per_scan); EXPORT_SYMBOL_GPL(comedi_bytes_per_scan);
static unsigned int __comedi_nscans_left(struct comedi_subdevice *s, static unsigned int __comedi_nscans_left(struct comedi_subdevice *s,
......
...@@ -3545,6 +3545,7 @@ static int ni_cdio_cmdtest(struct comedi_device *dev, ...@@ -3545,6 +3545,7 @@ static int ni_cdio_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_cmd *cmd) struct comedi_subdevice *s, struct comedi_cmd *cmd)
{ {
struct ni_private *devpriv = dev->private; struct ni_private *devpriv = dev->private;
unsigned int bytes_per_scan;
int err = 0; int err = 0;
/* Step 1 : check if triggers are trivially valid */ /* Step 1 : check if triggers are trivially valid */
...@@ -3579,9 +3580,12 @@ static int ni_cdio_cmdtest(struct comedi_device *dev, ...@@ -3579,9 +3580,12 @@ static int ni_cdio_cmdtest(struct comedi_device *dev,
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
cmd->chanlist_len); cmd->chanlist_len);
err |= comedi_check_trigger_arg_max(&cmd->stop_arg, bytes_per_scan = comedi_bytes_per_scan_cmd(s, cmd);
s->async->prealloc_bufsz / if (bytes_per_scan) {
comedi_bytes_per_scan(s)); err |= comedi_check_trigger_arg_max(&cmd->stop_arg,
s->async->prealloc_bufsz /
bytes_per_scan);
}
if (err) if (err)
return 3; return 3;
......
...@@ -23,6 +23,21 @@ static const unsigned char erofs_filetype_table[EROFS_FT_MAX] = { ...@@ -23,6 +23,21 @@ static const unsigned char erofs_filetype_table[EROFS_FT_MAX] = {
[EROFS_FT_SYMLINK] = DT_LNK, [EROFS_FT_SYMLINK] = DT_LNK,
}; };
static void debug_one_dentry(unsigned char d_type, const char *de_name,
unsigned int de_namelen)
{
#ifdef CONFIG_EROFS_FS_DEBUG
/* since the on-disk name could not have the trailing '\0' */
unsigned char dbg_namebuf[EROFS_NAME_LEN + 1];
memcpy(dbg_namebuf, de_name, de_namelen);
dbg_namebuf[de_namelen] = '\0';
debugln("found dirent %s de_len %u d_type %d", dbg_namebuf,
de_namelen, d_type);
#endif
}
static int erofs_fill_dentries(struct dir_context *ctx, static int erofs_fill_dentries(struct dir_context *ctx,
void *dentry_blk, unsigned int *ofs, void *dentry_blk, unsigned int *ofs,
unsigned int nameoff, unsigned int maxsize) unsigned int nameoff, unsigned int maxsize)
...@@ -33,14 +48,10 @@ static int erofs_fill_dentries(struct dir_context *ctx, ...@@ -33,14 +48,10 @@ static int erofs_fill_dentries(struct dir_context *ctx,
de = dentry_blk + *ofs; de = dentry_blk + *ofs;
while (de < end) { while (de < end) {
const char *de_name; const char *de_name;
int de_namelen; unsigned int de_namelen;
unsigned char d_type; unsigned char d_type;
#ifdef CONFIG_EROFS_FS_DEBUG
unsigned int dbg_namelen;
unsigned char dbg_namebuf[EROFS_NAME_LEN];
#endif
if (unlikely(de->file_type < EROFS_FT_MAX)) if (de->file_type < EROFS_FT_MAX)
d_type = erofs_filetype_table[de->file_type]; d_type = erofs_filetype_table[de->file_type];
else else
d_type = DT_UNKNOWN; d_type = DT_UNKNOWN;
...@@ -48,26 +59,20 @@ static int erofs_fill_dentries(struct dir_context *ctx, ...@@ -48,26 +59,20 @@ static int erofs_fill_dentries(struct dir_context *ctx,
nameoff = le16_to_cpu(de->nameoff); nameoff = le16_to_cpu(de->nameoff);
de_name = (char *)dentry_blk + nameoff; de_name = (char *)dentry_blk + nameoff;
de_namelen = unlikely(de + 1 >= end) ? /* the last dirent in the block? */
/* last directory entry */ if (de + 1 >= end)
strnlen(de_name, maxsize - nameoff) : de_namelen = strnlen(de_name, maxsize - nameoff);
le16_to_cpu(de[1].nameoff) - nameoff; else
de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;
/* a corrupted entry is found */ /* a corrupted entry is found */
if (unlikely(de_namelen < 0)) { if (unlikely(nameoff + de_namelen > maxsize ||
de_namelen > EROFS_NAME_LEN)) {
DBG_BUGON(1); DBG_BUGON(1);
return -EIO; return -EIO;
} }
#ifdef CONFIG_EROFS_FS_DEBUG debug_one_dentry(d_type, de_name, de_namelen);
dbg_namelen = min(EROFS_NAME_LEN - 1, de_namelen);
memcpy(dbg_namebuf, de_name, dbg_namelen);
dbg_namebuf[dbg_namelen] = '\0';
debugln("%s, found de_name %s de_len %d d_type %d", __func__,
dbg_namebuf, de_namelen, d_type);
#endif
if (!dir_emit(ctx, de_name, de_namelen, if (!dir_emit(ctx, de_name, de_namelen,
le64_to_cpu(de->nid), d_type)) le64_to_cpu(de->nid), d_type))
/* stopped by some reason */ /* stopped by some reason */
......
...@@ -972,6 +972,7 @@ static int z_erofs_vle_unzip(struct super_block *sb, ...@@ -972,6 +972,7 @@ static int z_erofs_vle_unzip(struct super_block *sb,
overlapped = false; overlapped = false;
compressed_pages = grp->compressed_pages; compressed_pages = grp->compressed_pages;
err = 0;
for (i = 0; i < clusterpages; ++i) { for (i = 0; i < clusterpages; ++i) {
unsigned int pagenr; unsigned int pagenr;
...@@ -981,26 +982,39 @@ static int z_erofs_vle_unzip(struct super_block *sb, ...@@ -981,26 +982,39 @@ static int z_erofs_vle_unzip(struct super_block *sb,
DBG_BUGON(!page); DBG_BUGON(!page);
DBG_BUGON(!page->mapping); DBG_BUGON(!page->mapping);
if (z_erofs_is_stagingpage(page)) if (!z_erofs_is_stagingpage(page)) {
continue;
#ifdef EROFS_FS_HAS_MANAGED_CACHE #ifdef EROFS_FS_HAS_MANAGED_CACHE
if (page->mapping == MNGD_MAPPING(sbi)) { if (page->mapping == MNGD_MAPPING(sbi)) {
DBG_BUGON(!PageUptodate(page)); if (unlikely(!PageUptodate(page)))
continue; err = -EIO;
} continue;
}
#endif #endif
/* only non-head page could be reused as a compressed page */ /*
pagenr = z_erofs_onlinepage_index(page); * only if non-head page can be selected
* for inplace decompression
*/
pagenr = z_erofs_onlinepage_index(page);
DBG_BUGON(pagenr >= nr_pages); DBG_BUGON(pagenr >= nr_pages);
DBG_BUGON(pages[pagenr]); DBG_BUGON(pages[pagenr]);
++sparsemem_pages; ++sparsemem_pages;
pages[pagenr] = page; pages[pagenr] = page;
overlapped = true; overlapped = true;
}
/* PG_error needs checking for inplaced and staging pages */
if (unlikely(PageError(page))) {
DBG_BUGON(PageUptodate(page));
err = -EIO;
}
} }
if (unlikely(err))
goto out;
llen = (nr_pages << PAGE_SHIFT) - work->pageofs; llen = (nr_pages << PAGE_SHIFT) - work->pageofs;
if (z_erofs_vle_workgrp_fmt(grp) == Z_EROFS_VLE_WORKGRP_FMT_PLAIN) { if (z_erofs_vle_workgrp_fmt(grp) == Z_EROFS_VLE_WORKGRP_FMT_PLAIN) {
...@@ -1029,6 +1043,10 @@ static int z_erofs_vle_unzip(struct super_block *sb, ...@@ -1029,6 +1043,10 @@ static int z_erofs_vle_unzip(struct super_block *sb,
skip_allocpage: skip_allocpage:
vout = erofs_vmap(pages, nr_pages); vout = erofs_vmap(pages, nr_pages);
if (!vout) {
err = -ENOMEM;
goto out;
}
err = z_erofs_vle_unzip_vmap(compressed_pages, err = z_erofs_vle_unzip_vmap(compressed_pages,
clusterpages, vout, llen, work->pageofs, overlapped); clusterpages, vout, llen, work->pageofs, overlapped);
...@@ -1194,6 +1212,7 @@ pickup_page_for_submission(struct z_erofs_vle_workgroup *grp, ...@@ -1194,6 +1212,7 @@ pickup_page_for_submission(struct z_erofs_vle_workgroup *grp,
if (page->mapping == mc) { if (page->mapping == mc) {
WRITE_ONCE(grp->compressed_pages[nr], page); WRITE_ONCE(grp->compressed_pages[nr], page);
ClearPageError(page);
if (!PagePrivate(page)) { if (!PagePrivate(page)) {
/* /*
* impossible to be !PagePrivate(page) for * impossible to be !PagePrivate(page) for
......
...@@ -136,10 +136,13 @@ int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages, ...@@ -136,10 +136,13 @@ int z_erofs_vle_unzip_fast_percpu(struct page **compressed_pages,
nr_pages = DIV_ROUND_UP(outlen + pageofs, PAGE_SIZE); nr_pages = DIV_ROUND_UP(outlen + pageofs, PAGE_SIZE);
if (clusterpages == 1) if (clusterpages == 1) {
vin = kmap_atomic(compressed_pages[0]); vin = kmap_atomic(compressed_pages[0]);
else } else {
vin = erofs_vmap(compressed_pages, clusterpages); vin = erofs_vmap(compressed_pages, clusterpages);
if (!vin)
return -ENOMEM;
}
preempt_disable(); preempt_disable();
vout = erofs_pcpubuf[smp_processor_id()].data; vout = erofs_pcpubuf[smp_processor_id()].data;
......
...@@ -117,22 +117,6 @@ &pcie { ...@@ -117,22 +117,6 @@ &pcie {
status = "okay"; status = "okay";
}; };
&ethernet {
//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 { &pinctrl {
state_default: pinctrl0 { state_default: pinctrl0 {
gpio { gpio {
...@@ -141,3 +125,16 @@ gpio { ...@@ -141,3 +125,16 @@ gpio {
}; };
}; };
}; };
&switch0 {
ports {
port@0 {
label = "ethblack";
status = "ok";
};
port@4 {
label = "ethblue";
status = "ok";
};
};
};
...@@ -372,16 +372,83 @@ ethernet: ethernet@1e100000 { ...@@ -372,16 +372,83 @@ ethernet: ethernet@1e100000 {
mediatek,ethsys = <&ethsys>; mediatek,ethsys = <&ethsys>;
mediatek,switch = <&gsw>;
gmac0: mac@0 {
compatible = "mediatek,eth-mac";
reg = <0>;
phy-mode = "rgmii";
fixed-link {
speed = <1000>;
full-duplex;
pause;
};
};
gmac1: mac@1 {
compatible = "mediatek,eth-mac";
reg = <1>;
status = "off";
phy-mode = "rgmii";
phy-handle = <&phy5>;
};
mdio-bus { mdio-bus {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
phy1f: ethernet-phy@1f { phy5: ethernet-phy@5 {
reg = <0x1f>; reg = <5>;
phy-mode = "rgmii"; phy-mode = "rgmii";
}; };
switch0: switch0@0 {
compatible = "mediatek,mt7621";
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
mediatek,mcm;
resets = <&rstctrl 2>;
reset-names = "mcm";
ports {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
port@0 {
status = "off";
reg = <0>;
label = "lan0";
};
port@1 {
status = "off";
reg = <1>;
label = "lan1";
};
port@2 {
status = "off";
reg = <2>;
label = "lan2";
};
port@3 {
status = "off";
reg = <3>;
label = "lan3";
};
port@4 {
status = "off";
reg = <4>;
label = "lan4";
};
port@6 {
reg = <6>;
label = "cpu";
ethernet = <&gmac0>;
phy-mode = "trgmii";
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};
}; };
}; };
......
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>,
<&ethsys CLK_ETHSYS_ESW>,
<&ethsys CLK_ETHSYS_GP2>,
<&ethsys 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";
};
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
#
# 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
- 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
- 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 <neil@brown.name>
// SPDX-License-Identifier: GPL-2.0
/* 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 <blogic@openwrt.org>
* Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
*/
#include "mtk_eth_soc.h"
#include "ethtool.h"
struct mtk_stat {
char name[ETH_GSTRING_LEN];
unsigned int idx;
};
#define MTK_HW_STAT(stat) { \
.name = #stat, \
.idx = offsetof(struct mtk_hw_stats, stat) / sizeof(u64) \
}
static const struct mtk_stat mtk_ethtool_hw_stats[] = {
MTK_HW_STAT(tx_bytes),
MTK_HW_STAT(tx_packets),
MTK_HW_STAT(tx_skip),
MTK_HW_STAT(tx_collisions),
MTK_HW_STAT(rx_bytes),
MTK_HW_STAT(rx_packets),
MTK_HW_STAT(rx_overflow),
MTK_HW_STAT(rx_fcs_errors),
MTK_HW_STAT(rx_short_errors),
MTK_HW_STAT(rx_long_errors),
MTK_HW_STAT(rx_checksum_errors),
MTK_HW_STAT(rx_flow_control_packets),
};
#define MTK_HW_STATS_LEN ARRAY_SIZE(mtk_ethtool_hw_stats)
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 = MTK_HW_STATS_LEN;
}
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)
{
int i;
switch (stringset) {
case ETH_SS_STATS:
for (i = 0; i < MTK_HW_STATS_LEN; i++) {
memcpy(data, mtk_ethtool_hw_stats[i].name,
ETH_GSTRING_LEN);
data += ETH_GSTRING_LEN;
}
break;
}
}
static int mtk_get_sset_count(struct net_device *dev, int sset)
{
switch (sset) {
case ETH_SS_STATS:
return MTK_HW_STATS_LEN;
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;
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 {
start = u64_stats_fetch_begin_irq(&hwstats->syncp);
for (i = 0; i < MTK_HW_STATS_LEN; i++)
data[i] = ((u64 *)hwstats)[mtk_ethtool_hw_stats[i].idx];
} 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;
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2009-2016 John Crispin <blogic@openwrt.org>
* Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
*/
#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 */
/* 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 <blogic@openwrt.org>
* Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
*/
#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 int reg);
u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned int 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
/* 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 <blogic@openwrt.org>
* Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
*/
#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 int reg)
{
iowrite32(val, gsw->base + reg);
}
EXPORT_SYMBOL_GPL(mtk_switch_w32);
u32 mtk_switch_r32(struct mt7620_gsw *gsw, unsigned int 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 (IS_ERR(gsw->base))
return PTR_ERR(gsw->base);
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",
.of_match_table = mediatek_gsw_match,
},
};
module_platform_driver(gsw_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
MODULE_DESCRIPTION("GBit switch driver for Mediatek MT7621 SoC");
/* 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 <blogic@openwrt.org>
* Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
*/
#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(&eth->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;
}
}
}
spin_unlock_irqrestore(&eth->phy->lock, flags);
}
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("%pOFn: invalid port id\n", phy_node);
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 (!phydev) {
dev_err(eth->dev, "could not connect to PHY\n");
eth->phy->phy_node[port] = NULL;
return -ENODEV;
}
phydev->supported &= PHY_1000BT_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_set_max_speed(phy, SPEED_100);
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(&eth->phy->lock, flags);
eth->link[i] = 0;
if (eth->soc->mdio_adjust_link)
eth->soc->mdio_adjust_link(eth, i);
spin_unlock_irqrestore(&eth->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(&eth->phy->lock, flags);
eth->link[i] = 1;
if (eth->soc->mdio_adjust_link)
eth->soc->mdio_adjust_link(eth, i);
spin_unlock_irqrestore(&eth->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(&eth->phy->lock, flags);
eth->link[i] = 0;
if (eth->soc->mdio_adjust_link)
eth->soc->mdio_adjust_link(eth, i);
spin_unlock_irqrestore(&eth->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, "%pOFn", mii_np);
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);
}
/* 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 <blogic@openwrt.org>
* Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
*/
#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
/* 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 <blogic@openwrt.org>
* Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
*/
#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));
}
This diff is collapsed.
This diff is collapsed.
/* 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 <blogic@openwrt.org>
* Copyright (C) 2009-2016 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2013-2016 Michael Lee <igvtee@gmail.com>
*/
#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);
config PCI_MT7621 config PCI_MT7621
tristate "MediaTek MT7621 PCI Controller" tristate "MediaTek MT7621 PCI Controller"
depends on RALINK depends on RALINK
depends on PCI
select PCI_DRIVERS_GENERIC select PCI_DRIVERS_GENERIC
help help
This selects a driver for the MediaTek MT7621 PCI Controller. This selects a driver for the MediaTek MT7621 PCI Controller.
......
...@@ -163,7 +163,7 @@ int cvm_oct_phy_setup_device(struct net_device *dev) ...@@ -163,7 +163,7 @@ int cvm_oct_phy_setup_device(struct net_device *dev)
goto no_phy; goto no_phy;
phydev = of_phy_connect(dev, phy_node, cvm_oct_adjust_link, 0, phydev = of_phy_connect(dev, phy_node, cvm_oct_adjust_link, 0,
PHY_INTERFACE_MODE_GMII); priv->phy_mode);
of_node_put(phy_node); of_node_put(phy_node);
if (!phydev) if (!phydev)
......
...@@ -653,14 +653,37 @@ static struct device_node *cvm_oct_node_for_port(struct device_node *pip, ...@@ -653,14 +653,37 @@ static struct device_node *cvm_oct_node_for_port(struct device_node *pip,
return np; return np;
} }
static void cvm_set_rgmii_delay(struct device_node *np, int iface, int port) static void cvm_set_rgmii_delay(struct octeon_ethernet *priv, int iface,
int port)
{ {
struct device_node *np = priv->of_node;
u32 delay_value; u32 delay_value;
bool rx_delay;
bool tx_delay;
if (!of_property_read_u32(np, "rx-delay", &delay_value)) /* By default, both RX/TX delay is enabled in
* __cvmx_helper_rgmii_enable().
*/
rx_delay = true;
tx_delay = true;
if (!of_property_read_u32(np, "rx-delay", &delay_value)) {
cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, iface), delay_value); cvmx_write_csr(CVMX_ASXX_RX_CLK_SETX(port, iface), delay_value);
if (!of_property_read_u32(np, "tx-delay", &delay_value)) rx_delay = delay_value > 0;
}
if (!of_property_read_u32(np, "tx-delay", &delay_value)) {
cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, iface), delay_value); cvmx_write_csr(CVMX_ASXX_TX_CLK_SETX(port, iface), delay_value);
tx_delay = delay_value > 0;
}
if (!rx_delay && !tx_delay)
priv->phy_mode = PHY_INTERFACE_MODE_RGMII_ID;
else if (!rx_delay)
priv->phy_mode = PHY_INTERFACE_MODE_RGMII_RXID;
else if (!tx_delay)
priv->phy_mode = PHY_INTERFACE_MODE_RGMII_TXID;
else
priv->phy_mode = PHY_INTERFACE_MODE_RGMII;
} }
static int cvm_oct_probe(struct platform_device *pdev) static int cvm_oct_probe(struct platform_device *pdev)
...@@ -825,6 +848,7 @@ static int cvm_oct_probe(struct platform_device *pdev) ...@@ -825,6 +848,7 @@ static int cvm_oct_probe(struct platform_device *pdev)
priv->port = port; priv->port = port;
priv->queue = cvmx_pko_get_base_queue(priv->port); priv->queue = cvmx_pko_get_base_queue(priv->port);
priv->fau = fau - cvmx_pko_get_num_queues(port) * 4; priv->fau = fau - cvmx_pko_get_num_queues(port) * 4;
priv->phy_mode = PHY_INTERFACE_MODE_NA;
for (qos = 0; qos < 16; qos++) for (qos = 0; qos < 16; qos++)
skb_queue_head_init(&priv->tx_free_list[qos]); skb_queue_head_init(&priv->tx_free_list[qos]);
for (qos = 0; qos < cvmx_pko_get_num_queues(port); for (qos = 0; qos < cvmx_pko_get_num_queues(port);
...@@ -856,6 +880,7 @@ static int cvm_oct_probe(struct platform_device *pdev) ...@@ -856,6 +880,7 @@ static int cvm_oct_probe(struct platform_device *pdev)
break; break;
case CVMX_HELPER_INTERFACE_MODE_SGMII: case CVMX_HELPER_INTERFACE_MODE_SGMII:
priv->phy_mode = PHY_INTERFACE_MODE_SGMII;
dev->netdev_ops = &cvm_oct_sgmii_netdev_ops; dev->netdev_ops = &cvm_oct_sgmii_netdev_ops;
strcpy(dev->name, "eth%d"); strcpy(dev->name, "eth%d");
break; break;
...@@ -865,11 +890,16 @@ static int cvm_oct_probe(struct platform_device *pdev) ...@@ -865,11 +890,16 @@ static int cvm_oct_probe(struct platform_device *pdev)
strcpy(dev->name, "spi%d"); strcpy(dev->name, "spi%d");
break; break;
case CVMX_HELPER_INTERFACE_MODE_RGMII:
case CVMX_HELPER_INTERFACE_MODE_GMII: case CVMX_HELPER_INTERFACE_MODE_GMII:
priv->phy_mode = PHY_INTERFACE_MODE_GMII;
dev->netdev_ops = &cvm_oct_rgmii_netdev_ops;
strcpy(dev->name, "eth%d");
break;
case CVMX_HELPER_INTERFACE_MODE_RGMII:
dev->netdev_ops = &cvm_oct_rgmii_netdev_ops; dev->netdev_ops = &cvm_oct_rgmii_netdev_ops;
strcpy(dev->name, "eth%d"); strcpy(dev->name, "eth%d");
cvm_set_rgmii_delay(priv->of_node, interface, cvm_set_rgmii_delay(priv, interface,
port_index); port_index);
break; break;
} }
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#define OCTEON_ETHERNET_H #define OCTEON_ETHERNET_H
#include <linux/of.h> #include <linux/of.h>
#include <linux/phy.h>
#include <asm/octeon/cvmx-helper-board.h> #include <asm/octeon/cvmx-helper-board.h>
/** /**
...@@ -33,6 +33,8 @@ struct octeon_ethernet { ...@@ -33,6 +33,8 @@ struct octeon_ethernet {
* cvmx_helper_interface_mode_t * cvmx_helper_interface_mode_t
*/ */
int imode; int imode;
/* PHY mode */
phy_interface_t phy_mode;
/* List of outstanding tx buffers per queue */ /* List of outstanding tx buffers per queue */
struct sk_buff_head tx_free_list[16]; struct sk_buff_head tx_free_list[16];
unsigned int last_speed; unsigned int last_speed;
......
...@@ -45,7 +45,7 @@ static int dcon_init_xo_1(struct dcon_priv *dcon) ...@@ -45,7 +45,7 @@ static int dcon_init_xo_1(struct dcon_priv *dcon)
{ {
unsigned char lob; unsigned char lob;
int ret, i; int ret, i;
struct dcon_gpio *pin = &gpios_asis[0]; const struct dcon_gpio *pin = &gpios_asis[0];
for (i = 0; i < ARRAY_SIZE(gpios_asis); i++) { for (i = 0; i < ARRAY_SIZE(gpios_asis); i++) {
gpios[i] = devm_gpiod_get(&dcon->client->dev, pin[i].name, gpios[i] = devm_gpiod_get(&dcon->client->dev, pin[i].name,
......
This diff is collapsed.
...@@ -336,7 +336,7 @@ s32 rtw_txframes_sta_ac_pending(struct adapter *padapter, ...@@ -336,7 +336,7 @@ s32 rtw_txframes_sta_ac_pending(struct adapter *padapter,
void rtw_init_hwxmits(struct hw_xmit *phwxmit, int entry); void rtw_init_hwxmits(struct hw_xmit *phwxmit, int entry);
s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter); s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter);
void _rtw_free_xmit_priv(struct xmit_priv *pxmitpriv); void _rtw_free_xmit_priv(struct xmit_priv *pxmitpriv);
void rtw_alloc_hwxmits(struct adapter *padapter); s32 rtw_alloc_hwxmits(struct adapter *padapter);
void rtw_free_hwxmits(struct adapter *padapter); void rtw_free_hwxmits(struct adapter *padapter);
s32 rtw_xmit(struct adapter *padapter, struct sk_buff **pkt); s32 rtw_xmit(struct adapter *padapter, struct sk_buff **pkt);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -74,6 +74,7 @@ int synth_request_region(unsigned long start, unsigned long n); ...@@ -74,6 +74,7 @@ int synth_request_region(unsigned long start, unsigned long n);
int synth_release_region(unsigned long start, unsigned long n); int synth_release_region(unsigned long start, unsigned long n);
int synth_add(struct spk_synth *in_synth); int synth_add(struct spk_synth *in_synth);
void synth_remove(struct spk_synth *in_synth); void synth_remove(struct spk_synth *in_synth);
struct spk_synth *synth_current(void);
extern struct speakup_info_t speakup_info; extern struct speakup_info_t speakup_info;
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment