* [PATCH 5/5] net: ethernet: mtk_eth_mac: use devm_register_netdev()
From: Bartosz Golaszewski @ 2020-05-20 11:44 UTC (permalink / raw)
To: Jonathan Corbet, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200520114415.13041-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Use the new devres variant of register_netdev() in the mtk-eth-mac
driver and shrink the code by a couple lines.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
drivers/net/ethernet/mediatek/mtk_eth_mac.c | 17 +----------------
1 file changed, 1 insertion(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_mac.c b/drivers/net/ethernet/mediatek/mtk_eth_mac.c
index 4dfe7c2c4e3d..2919ce27efeb 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_mac.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_mac.c
@@ -1513,13 +1513,6 @@ static void mtk_mac_clk_disable_unprepare(void *data)
clk_bulk_disable_unprepare(MTK_MAC_NCLKS, priv->clks);
}
-static void mtk_mac_unregister_netdev(void *data)
-{
- struct net_device *ndev = data;
-
- unregister_netdev(ndev);
-}
-
static int mtk_mac_probe(struct platform_device *pdev)
{
struct device_node *of_node;
@@ -1631,15 +1624,7 @@ static int mtk_mac_probe(struct platform_device *pdev)
netif_napi_add(ndev, &priv->napi, mtk_mac_poll, MTK_MAC_NAPI_WEIGHT);
- ret = register_netdev(ndev);
- if (ret)
- return ret;
-
- ret = devm_add_action_or_reset(dev, mtk_mac_unregister_netdev, ndev);
- if (ret)
- return ret;
-
- return 0;
+ return devm_register_netdev(dev, ndev);
}
static const struct of_device_id mtk_mac_of_match[] = {
--
2.25.0
^ permalink raw reply related
* [PATCH 4/5] net: devres: provide devm_register_netdev()
From: Bartosz Golaszewski @ 2020-05-20 11:44 UTC (permalink / raw)
To: Jonathan Corbet, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200520114415.13041-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Provide devm_register_netdev() - a device resource managed variant
of register_netdev(). This new helper will only work for net_device
structs that are also already managed by devres.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../driver-api/driver-model/devres.rst | 1 +
include/linux/netdevice.h | 2 +
net/devres.c | 55 +++++++++++++++++++
3 files changed, 58 insertions(+)
diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst
index 50df28d20fa7..fc242ed4bde5 100644
--- a/Documentation/driver-api/driver-model/devres.rst
+++ b/Documentation/driver-api/driver-model/devres.rst
@@ -375,6 +375,7 @@ MUX
NET
devm_alloc_etherdev()
devm_alloc_etherdev_mqs()
+ devm_register_netdev()
PER-CPU MEM
devm_alloc_percpu()
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 130a668049ab..c4ad728993dd 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -4208,6 +4208,8 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
int register_netdev(struct net_device *dev);
void unregister_netdev(struct net_device *dev);
+int devm_register_netdev(struct device *dev, struct net_device *ndev);
+
/* General hardware address lists handling functions */
int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
struct netdev_hw_addr_list *from_list, int addr_len);
diff --git a/net/devres.c b/net/devres.c
index b97b0c5a8216..57a6a88d11f6 100644
--- a/net/devres.c
+++ b/net/devres.c
@@ -38,3 +38,58 @@ struct net_device *devm_alloc_etherdev_mqs(struct device *dev, int sizeof_priv,
return dr->ndev;
}
EXPORT_SYMBOL(devm_alloc_etherdev_mqs);
+
+static void devm_netdev_release(struct device *dev, void *this)
+{
+ struct net_device_devres *res = this;
+
+ unregister_netdev(res->ndev);
+}
+
+static int netdev_devres_match(struct device *dev, void *this, void *match_data)
+{
+ struct net_device_devres *res = this;
+ struct net_device *ndev = match_data;
+
+ return ndev == res->ndev;
+}
+
+/**
+ * devm_register_netdev - resource managed variant of register_netdev()
+ * @dev: managing device for this netdev - usually the parent device
+ * @ndev: device to register
+ *
+ * This is a devres variant of register_netdev() for which the unregister
+ * function will be call automatically when the managing device is
+ * detached. Note: the net_device used must also be resource managed by
+ * the same struct device.
+ */
+int devm_register_netdev(struct device *dev, struct net_device *ndev)
+{
+ struct net_device_devres *dr;
+ int ret;
+
+ /* struct net_device must itself be managed. For now a managed netdev
+ * can only be allocated by devm_alloc_etherdev_mqs() so the check is
+ * straightforward.
+ */
+ if (WARN_ON(!devres_find(dev, devm_free_netdev,
+ netdev_devres_match, ndev)))
+ return -EINVAL;
+
+ dr = devres_alloc(devm_netdev_release, sizeof(*dr), GFP_KERNEL);
+ if (!dr)
+ return -ENOMEM;
+
+ ret = register_netdev(ndev);
+ if (ret) {
+ devres_free(dr);
+ return ret;
+ }
+
+ dr->ndev = ndev;
+ devres_add(ndev->dev.parent, dr);
+
+ return 0;
+}
+EXPORT_SYMBOL(devm_register_netdev);
--
2.25.0
^ permalink raw reply related
* [PATCH 1/5] Documentation: devres: add a missing section for networking helpers
From: Bartosz Golaszewski @ 2020-05-20 11:44 UTC (permalink / raw)
To: Jonathan Corbet, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200520114415.13041-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add a new section for networking devres helpers to devres.rst and list
the two existing devm functions.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
Documentation/driver-api/driver-model/devres.rst | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst
index 46c13780994c..50df28d20fa7 100644
--- a/Documentation/driver-api/driver-model/devres.rst
+++ b/Documentation/driver-api/driver-model/devres.rst
@@ -372,6 +372,10 @@ MUX
devm_mux_chip_register()
devm_mux_control_get()
+NET
+ devm_alloc_etherdev()
+ devm_alloc_etherdev_mqs()
+
PER-CPU MEM
devm_alloc_percpu()
devm_free_percpu()
--
2.25.0
^ permalink raw reply related
* [PATCH 0/5] net: provide a devres variant of register_netdev()
From: Bartosz Golaszewski @ 2020-05-20 11:44 UTC (permalink / raw)
To: Jonathan Corbet, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This series applies on top of my mtk-eth-mac series[1].
Using devres helpers allows to shrink the probing code, avoid memory leaks in
error paths make sure the order in which resources are freed is the exact
opposite of their allocation. This series proposes to add a devres variant
of register_netdev() that will only work with net_device structures whose
memory is also managed.
First we add the missing documentation entry for the only other networking
devres helper: devm_alloc_etherdev().
Next we move devm_alloc_etherdev() into a separate source file.
We then use a proxy structure in devm_alloc_etherdev() to improve readability.
Last: we implement devm_register_netdev() and use it in mtk-eth-mac driver.
[1] https://lkml.org/lkml/2020/5/20/507
Bartosz Golaszewski (5):
Documentation: devres: add a missing section for networking helpers
net: move devres helpers into a separate source file
net: devres: define a separate devres structure for
devm_alloc_etherdev()
net: devres: provide devm_register_netdev()
net: ethernet: mtk_eth_mac: use devm_register_netdev()
.../driver-api/driver-model/devres.rst | 5 +
drivers/net/ethernet/mediatek/mtk_eth_mac.c | 17 +---
include/linux/netdevice.h | 2 +
net/Makefile | 2 +-
net/devres.c | 95 +++++++++++++++++++
net/ethernet/eth.c | 28 ------
6 files changed, 104 insertions(+), 45 deletions(-)
create mode 100644 net/devres.c
--
2.25.0
^ permalink raw reply
* [PATCH v9 0/2] PCI: Microchip: Add host driver for Microchip PCIe controller
From: Daire.McNamara @ 2020-05-20 11:43 UTC (permalink / raw)
To: amurray, lorenzo.pieralisi, linux-pci, bhelgaas, robh-dt,
devicetree
This v9 patch adds support for the Microchip PCIe PolarFire PCIe
controller when configured in host (Root Complex) mode.
Updates since v8:
* Refactored as per Rob Herring's comments:
- bindings in schema format
- Adjusted licence to GPLv2.0
- Refactored access to config space between driver and common eCAM code
- Adopted pci_host_probe()
- Miscellanous other improvements
Updates since v7:
* Build for 64bit RISCV architecture only
Updates since v6:
* Refactored to use common eCAM driver
* Updated to CONFIG_PCIE_MICROCHIP_HOST etc
* Formatting improvements
* Removed code for selection between bridge 0 and 1
Updates since v5:
* Fixed Kconfig typo noted by Randy Dunlap
* Updated with comments from Bjorn Helgaas
Updates since v4:
* Fix compile issues.
Updates since v3:
* Update all references to Microsemi to Microchip
* Separate MSI functionality from legacy PCIe interrupt handling functionality
Updates since v2:
* Split out DT bindings and Vendor ID updates into their own patch
from PCIe driver.
* Updated Change Log
Updates since v1:
* Incorporate feedback from Bjorn Helgaas
Daire McNamara (2):
PCI: Microchip: Add host driver for Microchip PCIe controller
PCI: Microchip: Add host driver for Microchip PCIe controller
.../bindings/pci/microchip,pcie-host.yaml | 94 +++
drivers/pci/controller/Kconfig | 9 +
drivers/pci/controller/Makefile | 1 +
drivers/pci/controller/pcie-microchip-host.c | 664 ++++++++++++++++++
4 files changed, 768 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml
create mode 100644 drivers/pci/controller/pcie-microchip-host.c
base-commit: c0cc271173b2e1c2d8d0ceaef14e4dfa79eefc0d
--
2.17.1
^ permalink raw reply
* Re: [PATCH v2 1/2] drivers: thermal: tsens: Add zeroc interrupt support
From: Amit Kucheria @ 2020-05-20 11:42 UTC (permalink / raw)
To: Manaf Meethalavalappu Pallikunhi
Cc: Andy Gross, Bjorn Andersson, Zhang Rui, Daniel Lezcano,
Rob Herring, Linux PM list, linux-arm-msm,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS, LKML
In-Reply-To: <20200517104627.29501-2-manafm@codeaurora.org>
On Sun, May 17, 2020 at 4:17 PM Manaf Meethalavalappu Pallikunhi
<manafm@codeaurora.org> wrote:
>
> TSENS IP v2.6+ adds zeroc interrupt support. It triggers set
As I re-read through these patches, shouldn't we just call it the
"cold" interrupt?
> interrupt when aggregated minimum temperature of all TSENS falls
> below zeroc preset threshold and triggers reset interrupt when
Again, cold would just capture the intent much better given that it
doesn't even triggered at zero but at 5 degrees. And this value could
change in firmware.
> aggregated minimum temperature of all TSENS crosses above reset
> threshold. Add support for this interrupt in the driver.
>
> It adds another sensor to the of-thermal along with all individual
> TSENS. It enables to add any mitigation for zeroc interrupt.
>
> Signed-off-by: Manaf Meethalavalappu Pallikunhi <manafm@codeaurora.org>
> ---
> drivers/thermal/qcom/tsens-v2.c | 5 ++
> drivers/thermal/qcom/tsens.c | 107 +++++++++++++++++++++++++++++++-
> drivers/thermal/qcom/tsens.h | 10 +++
> 3 files changed, 120 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c
> index b293ed32174b..8f30b859ab22 100644
> --- a/drivers/thermal/qcom/tsens-v2.c
> +++ b/drivers/thermal/qcom/tsens-v2.c
> @@ -23,6 +23,7 @@
> #define TM_Sn_UPPER_LOWER_THRESHOLD_OFF 0x0020
> #define TM_Sn_CRITICAL_THRESHOLD_OFF 0x0060
> #define TM_Sn_STATUS_OFF 0x00a0
> +#define TM_ZEROC_INT_STATUS_OFF 0x00e0
> #define TM_TRDY_OFF 0x00e4
> #define TM_WDOG_LOG_OFF 0x013c
>
> @@ -86,6 +87,9 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
> REG_FIELD_FOR_EACH_SENSOR16(CRITICAL_STATUS, TM_Sn_STATUS_OFF, 19, 19),
> REG_FIELD_FOR_EACH_SENSOR16(MAX_STATUS, TM_Sn_STATUS_OFF, 20, 20),
>
> + /* ZEROC INTERRUPT STATUS */
> + [ZEROC_STATUS] = REG_FIELD(TM_ZEROC_INT_STATUS_OFF, 0, 0),
> +
> /* TRDY: 1=ready, 0=in progress */
> [TRDY] = REG_FIELD(TM_TRDY_OFF, 0, 0),
> };
> @@ -93,6 +97,7 @@ static const struct reg_field tsens_v2_regfields[MAX_REGFIELDS] = {
> static const struct tsens_ops ops_generic_v2 = {
> .init = init_common,
> .get_temp = get_temp_tsens_valid,
> + .get_zeroc_status = get_tsens_zeroc_status,
> };
>
> struct tsens_plat_data data_tsens_v2 = {
> diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
> index 8d3e94d2a9ed..dd0172f05eb6 100644
> --- a/drivers/thermal/qcom/tsens.c
> +++ b/drivers/thermal/qcom/tsens.c
> @@ -205,7 +205,8 @@ static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id,
> index = LOW_INT_CLEAR_0 + hw_id;
> break;
> case CRITICAL:
> - /* No critical interrupts before v2 */
> + case ZEROC:
> + /* No critical and zeroc interrupts before v2 */
> return;
> }
> regmap_field_write(priv->rf[index], enable ? 0 : 1);
> @@ -236,6 +237,9 @@ static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id,
> index_mask = CRIT_INT_MASK_0 + hw_id;
> index_clear = CRIT_INT_CLEAR_0 + hw_id;
> break;
> + case ZEROC:
> + /* Nothing to handle for zeroc interrupt */
> + return;
> }
>
> if (enable) {
> @@ -367,6 +371,35 @@ static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver)
> return 0;
> }
>
> +/**
> + * tsens_zeroc_irq_thread - Threaded interrupt handler for zeroc interrupt
> + * @irq: irq number
> + * @data: tsens controller private data
> + *
> + * Whenever interrupt triggers notify thermal framework using
> + * thermal_zone_device_update().
> + *
> + * Return: IRQ_HANDLED
> + */
> +
> +irqreturn_t tsens_zeroc_irq_thread(int irq, void *data)
> +{
> + struct tsens_priv *priv = data;
> + struct tsens_sensor *s = &priv->sensor[priv->num_sensors];
> + int temp, ret;
> +
> + ret = regmap_field_read(priv->rf[ZEROC_STATUS], &temp);
> + if (ret)
> + return ret;
> +
> + dev_dbg(priv->dev, "[%u] %s: zeroc interrupt is %s\n",
> + s->hw_id, __func__, temp ? "triggered" : "cleared");
Rename temp to cold or something similar since you're not really
returning temperature but a boolean state on whether we're in cold
zone or not.
> +
> + thermal_zone_device_update(s->tzd, THERMAL_EVENT_UNSPECIFIED);
> +
> + return IRQ_HANDLED;
> +}
> +
> /**
> * tsens_critical_irq_thread() - Threaded handler for critical interrupts
> * @irq: irq number
> @@ -575,6 +608,20 @@ void tsens_disable_irq(struct tsens_priv *priv)
> regmap_field_write(priv->rf[INT_EN], 0);
> }
>
> +int get_tsens_zeroc_status(const struct tsens_sensor *s, int *temp)
> +{
> + struct tsens_priv *priv = s->priv;
> + int last_temp = 0, ret;
> +
> + ret = regmap_field_read(priv->rf[ZEROC_STATUS], &last_temp);
> + if (ret)
> + return ret;
> +
> + *temp = last_temp;
same here. Don't use temp and last_temp. Use cold and prev_cold, for example.
> +
> + return 0;
> +}
> +
> int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp)
> {
> struct tsens_priv *priv = s->priv;
> @@ -843,6 +890,19 @@ int __init init_common(struct tsens_priv *priv)
> regmap_field_write(priv->rf[CC_MON_MASK], 1);
> }
>
> + if (tsens_version(priv) > VER_1_X && ver_minor > 5) {
> + /* ZEROC interrupt is present only on v2.6+ */
> + priv->feat->zeroc_int = 1;
> + priv->rf[ZEROC_STATUS] = devm_regmap_field_alloc(
> + dev,
> + priv->tm_map,
> + priv->fields[ZEROC_STATUS]);
> + if (IS_ERR(priv->rf[ZEROC_STATUS])) {
> + ret = PTR_ERR(priv->rf[ZEROC_STATUS]);
> + goto err_put_device;
> + }
> + }
> +
> spin_lock_init(&priv->ul_lock);
> tsens_enable_irq(priv);
> tsens_debug_init(op);
> @@ -852,6 +912,17 @@ int __init init_common(struct tsens_priv *priv)
> return ret;
> }
>
> +static int tsens_zeroc_get_temp(void *data, int *temp)
Add kernel doc to this function since it doesn't return temperature,
but a cold state, 0 or 1, on success.
One question: You need to poll this value from userspace, right? For
the userspace interface being discussed on the list currently, you
would still not get automatic notifications for this interrupt unless
you plan to add trip points that will cause thermal core to trigger
userspace events.
> +{
> + struct tsens_sensor *s = data;
> + struct tsens_priv *priv = s->priv;
> +
> + if (priv->ops->get_zeroc_status)
> + return priv->ops->get_zeroc_status(s, temp);
> +
> + return -ENOTSUPP;
> +}
> +
> static int tsens_get_temp(void *data, int *temp)
> {
> struct tsens_sensor *s = data;
> @@ -923,6 +994,10 @@ static const struct thermal_zone_of_device_ops tsens_of_ops = {
> .set_trips = tsens_set_trips,
> };
>
> +static const struct thermal_zone_of_device_ops tsens_zeroc_of_ops = {
> + .get_temp = tsens_zeroc_get_temp,
> +};
> +
> static int tsens_register_irq(struct tsens_priv *priv, char *irqname,
> irq_handler_t thread_fn)
> {
> @@ -980,6 +1055,21 @@ static int tsens_register(struct tsens_priv *priv)
> ret = tsens_register_irq(priv, "critical",
> tsens_critical_irq_thread);
>
> + if (priv->feat->zeroc_int && priv->zeroc_en) {
> + priv->sensor[priv->num_sensors].priv = priv;
> + tzd = devm_thermal_zone_of_sensor_register(priv->dev,
> + priv->sensor[priv->num_sensors].hw_id,
> + &priv->sensor[priv->num_sensors],
> + &tsens_zeroc_of_ops);
> + if (IS_ERR(tzd)) {
> + ret = 0;
> + return ret;
> + }
> +
> + priv->sensor[priv->num_sensors].tzd = tzd;
> + ret = tsens_register_irq(priv, "zeroc", tsens_zeroc_irq_thread);
> + }
> +
> return ret;
> }
>
> @@ -992,6 +1082,7 @@ static int tsens_probe(struct platform_device *pdev)
> const struct tsens_plat_data *data;
> const struct of_device_id *id;
> u32 num_sensors;
> + bool zeroc_en = false;
>
> if (pdev->dev.of_node)
> dev = &pdev->dev;
> @@ -1016,6 +1107,12 @@ static int tsens_probe(struct platform_device *pdev)
> return -EINVAL;
> }
>
> + /* Check whether zeroc interrupt is enabled or not */
> + if (platform_get_irq_byname(pdev, "zeroc") > 0) {
> + zeroc_en = true;
This check can be done in tsens_register_irq() from tsens_register. It
is OK to have an extra struct tsens_sensor allocated even on platforms
that don't have it.
> + num_sensors++;
> + }
> +
> priv = devm_kzalloc(dev,
> struct_size(priv, sensor, num_sensors),
make this num_sensors + 1 to account for the zeroc virtual sensor. See below.
> GFP_KERNEL);
> @@ -1023,7 +1120,7 @@ static int tsens_probe(struct platform_device *pdev)
> return -ENOMEM;
>
> priv->dev = dev;
> - priv->num_sensors = num_sensors;
> + priv->num_sensors = zeroc_en ? num_sensors - 1 : num_sensors;
> priv->ops = data->ops;
> for (i = 0; i < priv->num_sensors; i++) {
> if (data->hw_ids)
> @@ -1031,6 +1128,12 @@ static int tsens_probe(struct platform_device *pdev)
> else
> priv->sensor[i].hw_id = i;
> }
> +
> + if (zeroc_en) {
> + priv->zeroc_en = zeroc_en;
> + priv->sensor[num_sensors].hw_id = data->feat->max_sensors;
This is going to break as soon as we have a platform that actually
uses all 16 sensors. i.e. you won't have a spare one to use if
num_sensors >= max_sensors.
I think you should introduce a new member 'struct tsens_sensor
zeroc_sensor' in tsens_priv and avoid playing with num_sensors
completely.
> + }
> +
> priv->feat = data->feat;
> priv->fields = data->fields;
>
> diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
> index 59d01162c66a..34d24332b320 100644
> --- a/drivers/thermal/qcom/tsens.h
> +++ b/drivers/thermal/qcom/tsens.h
> @@ -34,6 +34,7 @@ enum tsens_irq_type {
> LOWER,
> UPPER,
> CRITICAL,
> + ZEROC,
> };
>
> /**
> @@ -64,6 +65,7 @@ struct tsens_sensor {
> * @suspend: Function to suspend the tsens device
> * @resume: Function to resume the tsens device
> * @get_trend: Function to get the thermal/temp trend
> + * @get_zeroc_status: Function to get the zeroc interrupt status
> */
> struct tsens_ops {
> /* mandatory callbacks */
> @@ -76,6 +78,7 @@ struct tsens_ops {
> int (*suspend)(struct tsens_priv *priv);
> int (*resume)(struct tsens_priv *priv);
> int (*get_trend)(struct tsens_sensor *s, enum thermal_trend *trend);
> + int (*get_zeroc_status)(const struct tsens_sensor *s, int *temp);
this fn doesn't return temp, but a boolean status of whether we're in
cold zone or not. Replace 'int *temp' with 'boolean *zeroc'?
> };
>
> #define REG_FIELD_FOR_EACH_SENSOR11(_name, _offset, _startbit, _stopbit) \
> @@ -485,6 +488,8 @@ enum regfield_ids {
> MAX_STATUS_14,
> MAX_STATUS_15,
>
> + ZEROC_STATUS, /* ZEROC INTERRUPT status */
Indent comment at same level as others above it
> +
> /* Keep last */
> MAX_REGFIELDS
> };
> @@ -497,6 +502,7 @@ enum regfield_ids {
> * @srot_split: does the IP neatly splits the register space into SROT and TM,
> * with SROT only being available to secure boot firmware?
> * @has_watchdog: does this IP support watchdog functionality?
> + * @zeroc_int: does this IP support ZEROC interrupt ?
> * @max_sensors: maximum sensors supported by this version of the IP
> */
> struct tsens_features {
> @@ -505,6 +511,7 @@ struct tsens_features {
> unsigned int adc:1;
> unsigned int srot_split:1;
> unsigned int has_watchdog:1;
> + unsigned int zeroc_int:1;
> unsigned int max_sensors;
> };
>
> @@ -549,6 +556,7 @@ struct tsens_context {
> * @feat: features of the IP
> * @fields: bitfield locations
> * @ops: pointer to list of callbacks supported by this device
> + * @zeroc_en: variable to check zeroc interrupt sensor is enabled or not
> * @debug_root: pointer to debugfs dentry for all tsens
> * @debug: pointer to debugfs dentry for tsens controller
> * @sensor: list of sensors attached to this device
> @@ -568,6 +576,7 @@ struct tsens_priv {
> struct tsens_features *feat;
> const struct reg_field *fields;
> const struct tsens_ops *ops;
> + bool zeroc_en;
>
> struct dentry *debug_root;
> struct dentry *debug;
> @@ -580,6 +589,7 @@ void compute_intercept_slope(struct tsens_priv *priv, u32 *pt1, u32 *pt2, u32 mo
> int init_common(struct tsens_priv *priv);
> int get_temp_tsens_valid(const struct tsens_sensor *s, int *temp);
> int get_temp_common(const struct tsens_sensor *s, int *temp);
> +int get_tsens_zeroc_status(const struct tsens_sensor *s, int *temp);
>
> /* TSENS target */
> extern struct tsens_plat_data data_8960;
> --
> 2.26.2
^ permalink raw reply
* Re: [PATCH v11 2/3] i2c: npcm7xx: Add Nuvoton NPCM I2C controller driver
From: Avi Fishman @ 2020-05-20 11:37 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Tali Perry, ofery, Brendan Higgins, Tomer Maimon, kfting,
Patrick Venture, Nancy Yuen, Benjamin Fair, Rob Herring, wsa,
Linux ARM, linux-i2c, OpenBMC Maillist, devicetree,
Linux Kernel Mailing List
In-Reply-To: <20200520102452.GP1634618@smile.fi.intel.com>
Thanks Andy,
Question below:
On Wed, May 20, 2020 at 1:24 PM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Wed, May 20, 2020 at 12:51:12PM +0300, Tali Perry wrote:
> > Add Nuvoton NPCM BMC I2C controller driver.
>
> ...
>
> > +#ifdef CONFIG_DEBUG_FS
>
> Why?!
It is made to save code size if CONFIG_DEBUG_FS is not defined?
We see a lot of kernel code that is doing it.
So could you elaborate what is the problem?
>
> > +#include <linux/debugfs.h>
> > +#endif
>
^ permalink raw reply
* Re: [PATCH V1 1/7] dt-bindings: mmc: Add information for DLL register properties
From: Ulf Hansson @ 2020-05-20 11:33 UTC (permalink / raw)
To: Sarthak Garg
Cc: Adrian Hunter, Veerabhadrarao Badiganti, Sahitya Tummala,
linux-mmc@vger.kernel.org, Linux Kernel Mailing List,
linux-arm-msm, Rob Herring,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS
In-Reply-To: <1588838535-6050-2-git-send-email-sartgarg@codeaurora.org>
On Thu, 7 May 2020 at 10:03, Sarthak Garg <sartgarg@codeaurora.org> wrote:
>
> Add information regarding DLL register properties for getting target
> specific configurations. These DLL register settings may vary from
> target to target.
>
> Also new compatible string value for sm8250 target.
As Rob indicated, it's a bit confusing to understand the bindings. I
think the word "target" is what makes this confusing.
I would suggest splitting this patch into two pieces. One that adds
the new compatible string for the new variant and another patch that
adds the *board specific* dll bindings. That should clarify this.
Kind regards
Uffe
>
> Signed-off-by: Sarthak Garg <sartgarg@codeaurora.org>
> ---
> Documentation/devicetree/bindings/mmc/sdhci-msm.txt | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
> index 5445931..b8e1d2b 100644
> --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
> +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
> @@ -17,6 +17,7 @@ Required properties:
> "qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"
> "qcom,msm8992-sdhci", "qcom,sdhci-msm-v4"
> "qcom,msm8996-sdhci", "qcom,sdhci-msm-v4"
> + "qcom,sm8250-sdhci", "qcom,sdhci-msm-v5"
> "qcom,sdm845-sdhci", "qcom,sdhci-msm-v5"
> "qcom,qcs404-sdhci", "qcom,sdhci-msm-v5"
> "qcom,sc7180-sdhci", "qcom,sdhci-msm-v5";
> @@ -46,6 +47,13 @@ Required properties:
> "cal" - reference clock for RCLK delay calibration (optional)
> "sleep" - sleep clock for RCLK delay calibration (optional)
>
> +- qcom,ddr-config: Certain chipsets and platforms require particular settings
> + for the DDR_CONFIG register. Use this field to specify the register
> + value as per the Hardware Programming Guide.
> +
> +- qcom,dll-config: Chipset and Platform specific value. Use this field to
> + specify the DLL_CONFIG register value as per Hardware Programming Guide.
> +
> Example:
>
> sdhc_1: sdhci@f9824900 {
> @@ -63,6 +71,9 @@ Example:
>
> clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>;
> clock-names = "core", "iface";
> +
> + qcom,dll-config = <0x000f642c>;
> + qcom,ddr-config = <0x80040868>;
> };
>
> sdhc_2: sdhci@f98a4900 {
> @@ -80,4 +91,7 @@ Example:
>
> clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>;
> clock-names = "core", "iface";
> +
> + qcom,dll-config = <0x0007642c>;
> + qcom,ddr-config = <0x80040868>;
> };
> --
> 2.7.4
>
^ permalink raw reply
* Re: [PATCH v8 1/6] MIPS: JZ4780: Introduce SMP support.
From: Paul Cercueil @ 2020-05-20 11:33 UTC (permalink / raw)
To: Zhou Yanjie
Cc: linux-mips, linux-kernel, devicetree, tsbogend, paulburton,
jiaxun.yang, chenhc, tglx, robh+dt, daniel.lezcano, keescook,
krzk, hns, ebiederm, dongsheng.qiu, yanfei.li, rick.tyliu,
sernia.zhou, zhenwenjin
In-Reply-To: <5EC4DADD.1000801@wanyeetech.com>
Hi Zhou,
Le mer. 20 mai 2020 à 15:23, Zhou Yanjie <zhouyanjie@wanyeetech.com> a
écrit :
> Hi Paul,
>
> On 2020年05月20日 03:41, Paul Cercueil wrote:
>> Hi Zhou,
>>
>> Le mar. 19 mai 2020 à 22:35, 周琰杰 (Zhou Yanjie)
>> \x7f<zhouyanjie@wanyeetech.com> a écrit :
>>> Forward port smp support from kernel 3.18.3 of CI20_linux
>>> to upstream kernel 5.6.
>>>
>>> Tested-by: H. Nikolaus Schaller <hns@goldelico.com>
>>> Tested-by: Paul Boddie <paul@boddie.org.uk>
>>> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
>>> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
>>> ---
>>>
>>> Notes:
>>> v1->v2:
>>> 1.Remove unnecessary "plat_irq_dispatch(void)" in irq-ingenic.c.
>>> 2.Add a timeout check for "jz4780_boot_secondary()" to avoid a
>>> \x7f\x7fdead loop.
>>> 3.Replace hard code in smp.c with macro.
>>>
>>> v2->v3:
>>> 1.Remove unnecessary "extern void (*r4k_blast_dcache)(void)" in
>>> \x7f\x7fsmp.c.
>>> 2.Use "for_each_of_cpu_node" instead "for_each_compatible_node"
>>> \x7f\x7fin smp.c.
>>> 3.Use "of_cpu_node_to_id" instead "of_property_read_u32_index"
>>> in \x7f\x7fsmp.c.
>>> 4.Move LCR related operations to jz4780-cgu.c.
>>>
>>> v3->v4:
>>> Rebase on top of kernel 5.6-rc1.
>>>
>>> v4->v5:
>>> 1.Splitting changes involving "jz4780-cgu.c" into separate
>>> commit.
>>> 2.Use "request_irq()" replace "setup_irq()".
>>>
>>> v5->v6:
>>> In order to have a kernel that works on multiple SoCs at the
>>> same
>>> time, use "IS_ENABLED()" replace "#ifdef".
>>>
>>> v6->v7:
>>> 1.SMP has be decoupled from the SoC version.
>>> 2.Add mailboxes 3 and 4 for XBurst.
>>> 3.Adjust code in "jz4780_smp_prepare_cpus()".
>>> 4."jz4780_smp_init()" has be marked "__init".
>>>
>>> v7->v8:
>>> No change.
>>>
>>> arch/mips/include/asm/mach-jz4740/smp.h | 87 +++++++++++
>>> arch/mips/jz4740/Kconfig | 2 +
>>> arch/mips/jz4740/Makefile | 5 +
>>> arch/mips/jz4740/prom.c | 4 +
>>> arch/mips/jz4740/smp-entry.S | 57 +++++++
>>> arch/mips/jz4740/smp.c | 258
>>> \x7f\x7f++++++++++++++++++++++++++++++++
>>> arch/mips/kernel/idle.c | 35 ++++-
>>> 7 files changed, 447 insertions(+), 1 deletion(-)
>>> create mode 100644 arch/mips/include/asm/mach-jz4740/smp.h
>>> create mode 100644 arch/mips/jz4740/smp-entry.S
>>> create mode 100644 arch/mips/jz4740/smp.c
>>>
>>> diff --git a/arch/mips/include/asm/mach-jz4740/smp.h
>>> \x7f\x7fb/arch/mips/include/asm/mach-jz4740/smp.h
>>> new file mode 100644
>>> index 00000000..86f660f
>>> --- /dev/null
>>> +++ b/arch/mips/include/asm/mach-jz4740/smp.h
>>> @@ -0,0 +1,87 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>>> +/*
>>> + * Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
>>> + * JZ4780 SMP definitions
>>> + */
>>> +
>>> +#ifndef __MIPS_ASM_MACH_JZ4740_SMP_H__
>>> +#define __MIPS_ASM_MACH_JZ4740_SMP_H__
>>> +
>>> +#define read_c0_corectrl() __read_32bit_c0_register($12, 2)
>>> +#define write_c0_corectrl(val) __write_32bit_c0_register($12, 2,
>>> val)
>>> +
>>> +#define read_c0_corestatus() __read_32bit_c0_register($12, 3)
>>> +#define write_c0_corestatus(val) __write_32bit_c0_register($12, 3,
>>> val)
>>> +
>>> +#define read_c0_reim() __read_32bit_c0_register($12, 4)
>>> +#define write_c0_reim(val) __write_32bit_c0_register($12, 4, val)
>>> +
>>> +#define read_c0_mailbox0() __read_32bit_c0_register($20, 0)
>>> +#define write_c0_mailbox0(val) __write_32bit_c0_register($20, 0,
>>> val)
>>> +
>>> +#define read_c0_mailbox1() __read_32bit_c0_register($20, 1)
>>> +#define write_c0_mailbox1(val) __write_32bit_c0_register($20, 1,
>>> val)
>>> +
>>> +#define read_c0_mailbox2() __read_32bit_c0_register($20, 2)
>>> +#define write_c0_mailbox2(val) __write_32bit_c0_register($20, 2,
>>> val)
>>> +
>>> +#define read_c0_mailbox3() __read_32bit_c0_register($20, 3)
>>> +#define write_c0_mailbox3(val) __write_32bit_c0_register($20, 3,
>>> val)
>>> +
>>> +#define smp_clr_pending(mask) do { \
>>> + unsigned int stat; \
>>> + stat = read_c0_corestatus(); \
>>> + stat &= ~((mask) & 0xff); \
>>> + write_c0_corestatus(stat); \
>>> + } while (0)
>>> +
>>> +/*
>>> + * Core Control register
>>> + */
>>> +#define CORECTRL_SLEEP1M_SHIFT 17
>>> +#define CORECTRL_SLEEP1M (_ULCAST_(0x1) <<
>>> CORECTRL_SLEEP1M_SHIFT)
>>> +#define CORECTRL_SLEEP0M_SHIFT 16
>>> +#define CORECTRL_SLEEP0M (_ULCAST_(0x1) <<
>>> CORECTRL_SLEEP0M_SHIFT)
>>> +#define CORECTRL_RPC1_SHIFT 9
>>> +#define CORECTRL_RPC1 (_ULCAST_(0x1) << CORECTRL_RPC1_SHIFT)
>>> +#define CORECTRL_RPC0_SHIFT 8
>>> +#define CORECTRL_RPC0 (_ULCAST_(0x1) << CORECTRL_RPC0_SHIFT)
>>> +#define CORECTRL_SWRST1_SHIFT 1
>>> +#define CORECTRL_SWRST1 (_ULCAST_(0x1) <<
>>> CORECTRL_SWRST1_SHIFT)
>>> +#define CORECTRL_SWRST0_SHIFT 0
>>> +#define CORECTRL_SWRST0 (_ULCAST_(0x1) <<
>>> CORECTRL_SWRST0_SHIFT)
>>> +
>>> +/*
>>> + * Core Status register
>>> + */
>>> +#define CORESTATUS_SLEEP1_SHIFT 17
>>> +#define CORESTATUS_SLEEP1 (_ULCAST_(0x1) <<
>>> CORESTATUS_SLEEP1_SHIFT)
>>> +#define CORESTATUS_SLEEP0_SHIFT 16
>>> +#define CORESTATUS_SLEEP0 (_ULCAST_(0x1) <<
>>> CORESTATUS_SLEEP0_SHIFT)
>>> +#define CORESTATUS_IRQ1P_SHIFT 9
>>> +#define CORESTATUS_IRQ1P (_ULCAST_(0x1) <<
>>> CORESTATUS_IRQ1P_SHIFT)
>>> +#define CORESTATUS_IRQ0P_SHIFT 8
>>> +#define CORESTATUS_IRQ0P (_ULCAST_(0x1) <<
>>> CORESTATUS_IRQ8P_SHIFT)
>>> +#define CORESTATUS_MIRQ1P_SHIFT 1
>>> +#define CORESTATUS_MIRQ1P (_ULCAST_(0x1) <<
>>> CORESTATUS_MIRQ1P_SHIFT)
>>> +#define CORESTATUS_MIRQ0P_SHIFT 0
>>> +#define CORESTATUS_MIRQ0P (_ULCAST_(0x1) <<
>>> CORESTATUS_MIRQ0P_SHIFT)
>>> +
>>> +/*
>>> + * Reset Entry & IRQ Mask register
>>> + */
>>> +#define REIM_ENTRY_SHIFT 16
>>> +#define REIM_ENTRY (_ULCAST_(0xffff) << REIM_ENTRY_SHIFT)
>>> +#define REIM_IRQ1M_SHIFT 9
>>> +#define REIM_IRQ1M (_ULCAST_(0x1) << REIM_IRQ1M_SHIFT)
>>> +#define REIM_IRQ0M_SHIFT 8
>>> +#define REIM_IRQ0M (_ULCAST_(0x1) << REIM_IRQ0M_SHIFT)
>>> +#define REIM_MBOXIRQ1M_SHIFT 1
>>> +#define REIM_MBOXIRQ1M (_ULCAST_(0x1) <<
>>> REIM_MBOXIRQ1M_SHIFT)
>>> +#define REIM_MBOXIRQ0M_SHIFT 0
>>> +#define REIM_MBOXIRQ0M (_ULCAST_(0x1) <<
>>> REIM_MBOXIRQ0M_SHIFT)
>>> +
>>> +extern void jz4780_smp_init(void);
>>> +extern void jz4780_secondary_cpu_entry(void);
>>> +
>>> +#endif /* __MIPS_ASM_MACH_JZ4740_SMP_H__ */
>>> diff --git a/arch/mips/jz4740/Kconfig b/arch/mips/jz4740/Kconfig
>>> index 412d2fa..2b88557 100644
>>> --- a/arch/mips/jz4740/Kconfig
>>> +++ b/arch/mips/jz4740/Kconfig
>>> @@ -34,9 +34,11 @@ config MACH_JZ4770
>>>
>>> config MACH_JZ4780
>>> bool
>>> + select GENERIC_CLOCKEVENTS_BROADCAST if SMP
>>> select MIPS_CPU_SCACHE
>>> select SYS_HAS_CPU_MIPS32_R2
>>> select SYS_SUPPORTS_HIGHMEM
>>> + select SYS_SUPPORTS_SMP
>>>
>>> config MACH_X1000
>>> bool
>>> diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile
>>> index 6de14c0..0a0f024 100644
>>> --- a/arch/mips/jz4740/Makefile
>>> +++ b/arch/mips/jz4740/Makefile
>>> @@ -12,3 +12,8 @@ CFLAGS_setup.o =
>>> -I$(src)/../../../scripts/dtc/libfdt
>>> # PM support
>>>
>>> obj-$(CONFIG_PM) += pm.o
>>> +
>>> +# SMP support
>>> +
>>> +obj-$(CONFIG_SMP) += smp.o
>>> +obj-$(CONFIG_SMP) += smp-entry.o
>>> diff --git a/arch/mips/jz4740/prom.c b/arch/mips/jz4740/prom.c
>>> index ff4555c..4acf5c2c 100644
>>> --- a/arch/mips/jz4740/prom.c
>>> +++ b/arch/mips/jz4740/prom.c
>>> @@ -8,10 +8,14 @@
>>>
>>> #include <asm/bootinfo.h>
>>> #include <asm/fw/fw.h>
>>> +#include <asm/mach-jz4740/smp.h>
>>>
>>> void __init prom_init(void)
>>> {
>>> fw_init_cmdline();
>>> +
>>> + if (IS_ENABLED(CONFIG_SMP))
>>> + jz4780_smp_init();
>>> }
>>>
>>> void __init prom_free_prom_memory(void)
>>> diff --git a/arch/mips/jz4740/smp-entry.S
>>> b/arch/mips/jz4740/smp-entry.S
>>> new file mode 100644
>>> index 00000000..20049a3
>>> --- /dev/null
>>> +++ b/arch/mips/jz4740/smp-entry.S
>>> @@ -0,0 +1,57 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>>> +/*
>>> + * Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
>>> + * JZ4780 SMP entry point
>>> + */
>>> +
>>> +#include <asm/addrspace.h>
>>> +#include <asm/asm.h>
>>> +#include <asm/asmmacro.h>
>>> +#include <asm/cacheops.h>
>>> +#include <asm/mipsregs.h>
>>> +
>>> +#define CACHE_SIZE (32 * 1024)
>>> +#define CACHE_LINESIZE 32
>>> +
>>> +.extern jz4780_cpu_entry_sp
>>> +.extern jz4780_cpu_entry_gp
>>> +
>>> +.section .text.smp-entry
>>> +.balign 0x10000
>>> +.set noreorder
>>> +LEAF(jz4780_secondary_cpu_entry)
>>> + mtc0 zero, CP0_CAUSE
>>> +
>>> + li t0, ST0_CU0
>>> + mtc0 t0, CP0_STATUS
>>> +
>>> + /* cache setup */
>>> + li t0, KSEG0
>>> + ori t1, t0, CACHE_SIZE
>>> + mtc0 zero, CP0_TAGLO, 0
>>> +1: cache Index_Store_Tag_I, 0(t0)
>>> + cache Index_Store_Tag_D, 0(t0)
>>> + bne t0, t1, 1b
>>> + addiu t0, t0, CACHE_LINESIZE
>>> +
>>> + /* kseg0 cache attribute */
>>> + mfc0 t0, CP0_CONFIG, 0
>>> + ori t0, t0, CONF_CM_CACHABLE_NONCOHERENT
>>> + mtc0 t0, CP0_CONFIG, 0
>>> +
>>> + /* pagemask */
>>> + mtc0 zero, CP0_PAGEMASK, 0
>>> +
>>> + /* retrieve sp */
>>> + la t0, jz4780_cpu_entry_sp
>>> + lw sp, 0(t0)
>>> +
>>> + /* retrieve gp */
>>> + la t0, jz4780_cpu_entry_gp
>>> + lw gp, 0(t0)
>>> +
>>> + /* jump to the kernel in kseg0 */
>>> + la t0, smp_bootstrap
>>> + jr t0
>>> + nop
>>> + END(jz4780_secondary_cpu_entry)
>>> diff --git a/arch/mips/jz4740/smp.c b/arch/mips/jz4740/smp.c
>>> new file mode 100644
>>> index 00000000..d95d22a
>>> --- /dev/null
>>> +++ b/arch/mips/jz4740/smp.c
>>> @@ -0,0 +1,258 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +/*
>>> + * Copyright (C) 2013, Paul Burton <paul.burton@imgtec.com>
>>> + * JZ4780 SMP
>>> + */
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/of.h>
>>> +#include <linux/sched.h>
>>> +#include <linux/sched/task_stack.h>
>>> +#include <linux/smp.h>
>>> +#include <linux/tick.h>
>>> +#include <asm/mach-jz4740/smp.h>
>>> +#include <asm/smp-ops.h>
>>> +
>>> +static struct clk *cpu_clock_gates[CONFIG_NR_CPUS] = { NULL };
>>> +
>>> +u32 jz4780_cpu_entry_sp;
>>> +u32 jz4780_cpu_entry_gp;
>>> +
>>> +static struct cpumask cpu_running;
>>
>> This cpumask is written, but never read anywhere. Since it's static,
>> I \x7fbelieve it's dead code.
>>
>
> Sure, I will remove it.
>
>>> +
>>> +static DEFINE_SPINLOCK(smp_lock);
>>> +
>>> +static irqreturn_t mbox_handler(int irq, void *dev_id)
>>> +{
>>> + int cpu = smp_processor_id();
>>> + u32 action, status;
>>> +
>>> + spin_lock(&smp_lock);
>>> +
>>> + switch (cpu) {
>>> + case 0:
>>> + action = read_c0_mailbox0();
>>> + write_c0_mailbox0(0);
>>> + break;
>>> + case 1:
>>> + action = read_c0_mailbox1();
>>> + write_c0_mailbox1(0);
>>> + break;
>>> + case 2:
>>> + action = read_c0_mailbox2();
>>> + write_c0_mailbox2(0);
>>> + break;
>>> + case 3:
>>> + action = read_c0_mailbox3();
>>> + write_c0_mailbox3(0);
>>> + break;
>>> + default:
>>> + panic("unhandled cpu %d!", cpu);
>>> + }
>>> +
>>> + /* clear pending mailbox interrupt */
>>> + status = read_c0_corestatus();
>>> + status &= ~(CORESTATUS_MIRQ0P << cpu);
>>> + write_c0_corestatus(status);
>>> +
>>> + spin_unlock(&smp_lock);
>>> +
>>> + if (action & SMP_RESCHEDULE_YOURSELF)
>>> + scheduler_ipi();
>>> + if (action & SMP_CALL_FUNCTION)
>>> + generic_smp_call_function_interrupt();
>>> +
>>> + return IRQ_HANDLED;
>>> +}
>>> +
>>> +static void jz4780_smp_setup(void)
>>> +{
>>> + u32 addr, reim;
>>> + int cpu;
>>> +
>>> + reim = read_c0_reim();
>>> +
>>> + for (cpu = 0; cpu < NR_CPUS; cpu++) {
>>> + __cpu_number_map[cpu] = cpu;
>>> + __cpu_logical_map[cpu] = cpu;
>>> + set_cpu_possible(cpu, true);
>>
>> I assume if you do that, you will have num_possible_cpus() ==
>> NR_CPUS, \x7fwhich is not what you want.
>>
>> Correct me if I'm wrong, but I think you would need to call
>> \x7fset_cpu_possible() for each CPU node found.
>>
>
> Yes, the current way is indeed a little problem, it will cause
> num_possible_cpus() == NR_CPUS, I will try to find a better way.
You can do:
for_each_of_cpu_node(cpu_node) {
cpu = of_cpu_node_to_id(cpu_node);
__cpu_number_map[cpu] = cpu;
__cpu_logical_map[cpu] = cpu;
set_cpu_possible(cpu, true);
}
>>> + }
>>> +
>>> + /* mask mailbox interrupts for this core */
>>> + reim &= ~REIM_MBOXIRQ0M;
>>> + write_c0_reim(reim);
>>> +
>>> + /* clear mailboxes & pending mailbox IRQs */
>>> + write_c0_mailbox0(0);
>>> + write_c0_mailbox1(0);
>>
>> Write mailbox2/3 too.
>>
>
> Although the XBurst1 architecture can have up to four cores, but
> JZ4780 only has two. If we need to write all four mailboxes here, do
> we need change the function name to "xburst1_smp_setup" or other
> similar names? This seems more appropriate.
Yes, these functions are not jz4780-specific, you can rename them all.
>>> + write_c0_corestatus(0);
>>> +
>>> + /* set reset entry point */
>>> + addr = KSEG1ADDR((u32)&jz4780_secondary_cpu_entry);
>>> + WARN_ON(addr & ~REIM_ENTRY);
>>> + reim &= ~REIM_ENTRY;
>>> + reim |= addr & REIM_ENTRY;
>>> +
>>> + /* unmask mailbox interrupts for this core */
>>> + reim |= REIM_MBOXIRQ0M;
>>> + write_c0_reim(reim);
>>> + set_c0_status(STATUSF_IP3);
>>> + irq_enable_hazard();
>>> +
>>> + cpumask_set_cpu(cpu, &cpu_running);
>>> +}
>>> +
>>> +static void jz4780_smp_prepare_cpus(unsigned int max_cpus)
>>> +{
>>> + struct device_node *cpu_node;
>>> + unsigned cpu, ctrl;
>>> + int err;
>>> +
>>> + /* setup the mailbox IRQ */
>>> + err = request_irq(MIPS_CPU_IRQ_BASE + 3, mbox_handler,
>>> + IRQF_PERCPU | IRQF_NO_THREAD, "core mailbox", NULL);
>>
>> Please don't hardcode the IRQ number. Instead, it should be read
>> from \x7fdevicetree, maybe from the 'cpus' node (not sure).
>>
>
> OK, I'll try to figure it out.
>
>>> + if (err)
>>> + pr_err("request_irq() on core mailbox failed\n");
>>> +
>>> + ctrl = read_c0_corectrl();
>>> +
>>> + for_each_of_cpu_node(cpu_node) {
>>> + cpu = of_cpu_node_to_id(cpu_node);
>>> + if (cpu < 0) {
>>> + pr_err("Failed to read index of %s\n",
>>> + cpu_node->full_name);
>>> + continue;
>>> + }
>>> +
>>> + /* use reset entry point from REIM register */
>>> + ctrl |= CORECTRL_RPC0 << cpu;
>>> +
>>> + cpu_clock_gates[cpu] = of_clk_get(cpu_node, 0);
>>> + if (IS_ERR(cpu_clock_gates[cpu])) {
>>> + cpu_clock_gates[cpu] = NULL;
>>> + continue;
>>> + }
>>> +
>>> + err = clk_prepare(cpu_clock_gates[cpu]);
>>> + if (err)
>>> + pr_err("Failed to prepare CPU clock gate\n");
>>
>> I'd suggest to call clk_prepare() in jz4780_boot_secondary(), since
>> \x7fyou can't handle errors here. That would also avoid the static
>> \x7fcpu_clock_gates[] array which can grow quite big since its size is
>> \x7fgiven by NR_CPUS.
>>
>
> Sure, I will move it to jz4780_boot_secondary().
>
>>> + }
>>> +
>>> + write_c0_corectrl(ctrl);
>>> +}
>>> +
>>> +static int jz4780_boot_secondary(int cpu, struct task_struct *idle)
>>> +{
>>> + unsigned long flags;
>>> + u32 ctrl;
>>> +
>>> + spin_lock_irqsave(&smp_lock, flags);
>>> +
>>> + /* ensure the core is in reset */
>>> + ctrl = read_c0_corectrl();
>>> + ctrl |= CORECTRL_SWRST0 << cpu;
>>> + write_c0_corectrl(ctrl);
>>> +
>>> + /* ungate core clock */
>>> + if (cpu_clock_gates[cpu])
>>> + clk_enable(cpu_clock_gates[cpu]);
>>
>> You should check the return value of clk_enable().
>>
>> + break;
>
> Sure.
>
>>> +
>>> + /* set entry sp/gp register values */
>>> + jz4780_cpu_entry_sp = __KSTK_TOS(idle);
>>> + jz4780_cpu_entry_gp = (u32)task_thread_info(idle);
>>> + smp_wmb();
>>> +
>>> + /* take the core out of reset */
>>> + ctrl &= ~(CORECTRL_SWRST0 << cpu);
>>> + write_c0_corectrl(ctrl);
>>> +
>>> + cpumask_set_cpu(cpu, &cpu_running);
>>> +
>>> + spin_unlock_irqrestore(&smp_lock, flags);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void jz4780_init_secondary(void)
>>> +{
>>> +}
>>> +
>>> +static void jz4780_smp_finish(void)
>>> +{
>>> + u32 reim;
>>> +
>>> + spin_lock(&smp_lock);
>>> +
>>> + /* unmask mailbox interrupts for this core */
>>> + reim = read_c0_reim();
>>> + reim |= REIM_MBOXIRQ0M << smp_processor_id();
>>> + write_c0_reim(reim);
>>> +
>>> + spin_unlock(&smp_lock);
>>> +
>>> + /* unmask interrupts for this core */
>>> + change_c0_status(ST0_IM, STATUSF_IP3 | STATUSF_IP2 |
>>> + STATUSF_IP1 | STATUSF_IP0);
>>> + irq_enable_hazard();
>>> +
>>> + /* force broadcast timer */
>>> + tick_broadcast_force();
>>> +}
>>> +
>>> +static void jz4780_send_ipi_single_locked(int cpu, unsigned int
>>> action)
>>> +{
>>> + u32 mbox;
>>> +
>>> + switch (cpu) {
>>> + case 0:
>>> + mbox = read_c0_mailbox0();
>>> + write_c0_mailbox0(mbox | action);
>>> + break;
>>> + case 1:
>>> + mbox = read_c0_mailbox1();
>>> + write_c0_mailbox1(mbox | action);
>>
>> Handle mailboxes 2/3 too here.
>>
>
> Same to the above, do we need to change the function to a more
> appropriate name?
>
>>> + default:
>>> + panic("unhandled cpu %d!", cpu);
>>> + }
>>> +}
>>> +
>>> +static void jz4780_send_ipi_single(int cpu, unsigned int action)
>>> +{
>>> + unsigned long flags;
>>> +
>>> + spin_lock_irqsave(&smp_lock, flags);
>>> + jz4780_send_ipi_single_locked(cpu, action);
>>> + spin_unlock_irqrestore(&smp_lock, flags);
>>> +}
>>> +
>>> +static void jz4780_send_ipi_mask(const struct cpumask *mask,
>>> + unsigned int action)
>>> +{
>>> + unsigned long flags;
>>> + int cpu;
>>> +
>>> + spin_lock_irqsave(&smp_lock, flags);
>>> +
>>> + for_each_cpu(cpu, mask)
>>> + jz4780_send_ipi_single_locked(cpu, action);
>>> +
>>> + spin_unlock_irqrestore(&smp_lock, flags);
>>> +}
>>> +
>>> +static struct plat_smp_ops jz4780_smp_ops = {
>>> + .send_ipi_single = jz4780_send_ipi_single,
>>> + .send_ipi_mask = jz4780_send_ipi_mask,
>>> + .init_secondary = jz4780_init_secondary,
>>> + .smp_finish = jz4780_smp_finish,
>>> + .boot_secondary = jz4780_boot_secondary,
>>> + .smp_setup = jz4780_smp_setup,
>>> + .prepare_cpus = jz4780_smp_prepare_cpus,
>>> +};
>>> +
>>> +void __init jz4780_smp_init(void)
>>> +{
>>> + register_smp_ops(&jz4780_smp_ops);
>>> +}
>>> diff --git a/arch/mips/kernel/idle.c b/arch/mips/kernel/idle.c
>>> index 37f8e78..d33f2d4 100644
>>> --- a/arch/mips/kernel/idle.c
>>> +++ b/arch/mips/kernel/idle.c
>>> @@ -18,6 +18,7 @@
>>> #include <asm/cpu-type.h>
>>> #include <asm/idle.h>
>>> #include <asm/mipsregs.h>
>>> +#include <asm/r4kcache.h>
>>>
>>> /*
>>> * Not all of the MIPS CPUs have the "wait" instruction available.
>>> \x7f\x7fMoreover,
>>> @@ -88,6 +89,34 @@ static void __cpuidle rm7k_wait_irqoff(void)
>>> }
>>>
>>> /*
>>> + * The Ingenic jz4780 SMP variant has to write back dirty cache
>>> \x7f\x7flines before
>>> + * executing wait. The CPU & cache clock will be gated until we
>>> \x7f\x7freturn from
>>> + * the wait, and if another core attempts to access data from our
>>> \x7f\x7fdata cache
>>> + * during this time then it will lock up.
>>> + */
>>> +void jz4780_smp_wait_irqoff(void)
>>> +{
>>> + unsigned long pending = read_c0_cause() & read_c0_status() &
>>> \x7f\x7fCAUSEF_IP;
>>> +
>>> + /*
>>> + * Going to idle has a significant overhead due to the cache
>>> \x7f\x7fflush so
>>> + * try to avoid it if we'll immediately be woken again due to
>>> an \x7f\x7fIRQ.
>>> + */
>>
>> You could add a fast path here where you just call r4k_wait() if
>> \x7fnum_online_cpus() < 2.
>>
>
> Please correct me if I'm wrong, if we add it here, when the number of
> CPU cores is greater than 1 (which should be the case on most
> occasions), each call to "jz4780_smp_wait_irqoff" will generate
> additional overhead (judging the number of CPUs), is it better to
> change "if (IS_ENABLED(CONFIG_SMP))" in "case CPU_XBURST" below to
> "if (IS_ENABLED(CONFIG_SMP) && (num_possible_cpus() > 1))"?
Is the number of possible CPUs already known when cpu_wait is set?
Cheers,
-Paul
> Thanks and best regards!
>
>> -Paul
>>
>>> + if (!need_resched() && !pending) {
>>> + r4k_blast_dcache();
>>> +
>>> + __asm__(
>>> + " .set push \n"
>>> + " .set mips3 \n"
>>> + " sync \n"
>>> + " wait \n"
>>> + " .set pop \n");
>>> + }
>>> +
>>> + local_irq_enable();
>>> +}
>>> +
>>> +/*
>>> * Au1 'wait' is only useful when the 32kHz counter is used as
>>> timer,
>>> * since coreclock (and the cp0 counter) stops upon executing it.
>>> \x7f\x7fOnly an
>>> * interrupt can wake it, so they must be enabled before entering
>>> \x7f\x7fidle modes.
>>> @@ -172,7 +201,6 @@ void __init check_wait(void)
>>> case CPU_CAVIUM_OCTEON_PLUS:
>>> case CPU_CAVIUM_OCTEON2:
>>> case CPU_CAVIUM_OCTEON3:
>>> - case CPU_XBURST:
>>> case CPU_LOONGSON32:
>>> case CPU_XLR:
>>> case CPU_XLP:
>>> @@ -246,6 +274,11 @@ void __init check_wait(void)
>>> cpu_wait = r4k_wait;
>>> */
>>> break;
>>> + case CPU_XBURST:
>>> + if (IS_ENABLED(CONFIG_SMP))
>>> + cpu_wait = jz4780_smp_wait_irqoff;
>>> + else
>>> + cpu_wait = r4k_wait;
>>> default:
>>> break;
>>> }
>>> --
>>> 2.7.4
>>>
>>
>
^ permalink raw reply
* Re: [PATCH v2 12/20] mips: MAAR: Add XPA mode support
From: Serge Semin @ 2020-05-20 11:30 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: Serge Semin, Alexey Malahov, Paul Burton, Ralf Baechle,
Arnd Bergmann, Rob Herring, linux-pm, devicetree,
Greg Kroah-Hartman, Allison Randal, Thomas Gleixner, Zhou Yanjie,
Paul Cercueil, Jiaxun Yang, Mike Rapoport, Andrew Morton,
linux-mips, linux-kernel
In-Reply-To: <20200519154213.GA15797@alpha.franken.de>
On Tue, May 19, 2020 at 05:42:13PM +0200, Thomas Bogendoerfer wrote:
> On Wed, May 06, 2020 at 08:42:30PM +0300, Sergey.Semin@baikalelectronics.ru wrote:
> > From: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> >
> > When XPA mode is enabled the normally 32-bits MAAR pair registers
> > are extended to be of 64-bits width as in pure 64-bits MIPS
> > architecture. In this case the MAAR registers can enable the
> > speculative loads/stores for addresses of up to 39-bits width.
> > But in this case the process of the MAAR initialization changes a bit.
> > The upper 32-bits of the registers are supposed to be accessed by mean
> > of the dedicated instructions mfhc0/mthc0 and there is a CP0.MAAR.VH
> > bit which should be set together with CP0.MAAR.VL as indication
> > of the boundary validity. All of these peculiarities were taken into
> > account in this commit so the speculative loads/stores would work
> > when XPA mode is enabled.
> >
> > Co-developed-by: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
> > Signed-off-by: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
> > Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
> > Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
> > Cc: Paul Burton <paulburton@kernel.org>
> > Cc: Ralf Baechle <ralf@linux-mips.org>
> > Cc: Arnd Bergmann <arnd@arndb.de>
> > Cc: Rob Herring <robh+dt@kernel.org>
> > Cc: linux-pm@vger.kernel.org
> > Cc: devicetree@vger.kernel.org
> > ---
> > arch/mips/include/asm/maar.h | 17 +++++++++++++++--
> > arch/mips/include/asm/mipsregs.h | 10 ++++++++++
> > arch/mips/mm/init.c | 8 +++++++-
> > 3 files changed, 32 insertions(+), 3 deletions(-)
>
> applied to mips-next.
Great! Thanks.
-Sergey
>
> Thomas.
>
> --
> Crap can work. Given enough thrust pigs will fly, but it's not necessarily a
> good idea. [ RFC1925, 2.3 ]
^ permalink raw reply
* Re: [PATCH 09/15] device core: Add ability to handle multiple dma offsets
From: Nicolas Saenz Julienne @ 2020-05-20 11:28 UTC (permalink / raw)
To: Jim Quinlan
Cc: Rob Herring, Frank Rowand, Christoph Hellwig, Marek Szyprowski,
Robin Murphy, Greg Kroah-Hartman, Suzuki K Poulose,
Saravana Kannan, Heikki Krogerus, Rafael J. Wysocki, Dan Williams,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE, open list,
open list:DMA MAPPING HELPERS
In-Reply-To: <20200519203419.12369-10-james.quinlan@broadcom.com>
[-- Attachment #1: Type: text/plain, Size: 2480 bytes --]
Hi Jim,
thanks for having a go at this! My two cents.
On Tue, 2020-05-19 at 16:34 -0400, Jim Quinlan wrote:
> The device variable 'dma_pfn_offset' is used to do a single
> linear map between cpu addrs and dma addrs. The variable
> 'dma_map' is added to struct device to point to an array
> of multiple offsets which is required for some devices.
>
> Signed-off-by: Jim Quinlan <james.quinlan@broadcom.com>
> ---
[...]
> --- a/include/linux/device.h
> +++ b/include/linux/device.h
> @@ -493,6 +493,8 @@ struct dev_links_info {
> * @bus_dma_limit: Limit of an upstream bridge or bus which imposes a smaller
> * DMA limit than the device itself supports.
> * @dma_pfn_offset: offset of DMA memory range relatively of RAM
> + * @dma_map: Like dma_pfn_offset but used when there are multiple
> + * pfn offsets for multiple dma-ranges.
> * @dma_parms: A low level driver may set these to teach IOMMU code
> about
> * segment limitations.
> * @dma_pools: Dma pools (if dma'ble device).
> @@ -578,7 +580,12 @@ struct device {
> allocations such descriptors. */
> u64 bus_dma_limit; /* upstream dma constraint */
> unsigned long dma_pfn_offset;
> -
> +#ifdef CONFIG_DMA_PFN_OFFSET_MAP
> + const void *dma_offset_map; /* Like dma_pfn_offset, but for
> + * the unlikely case of multiple
> + * offsets. If non-null, dma_pfn_offset
> + * will be 0. */
I get a bad feeling about separating the DMA offset handling into two distinct
variables. Albeit generally frowned upon, there is a fair amount of trickery
around dev->dma_pfn_offset all over the kernel. usb_alloc_dev() comes to mind
for example. And this obviously doesn't play well with it. I feel a potential
solution to multiple DMA ranges should completely integrate with the current
device DMA handling code, without special cases, on top of that, be transparent
to the user.
In more concrete terms, I'd repackage dev->bus_dma_limit and
dev->dma_pfn_offset into a list/array of DMA range structures and adapt/create
the relevant getter/setter functions so as for DMA users not to have to worry
about the specifics of a device's DMA constraints. For example, instead of
editing dev->dma_pfn_offset, you'd be passing a DMA range structure to the
device core, and let it take the relevant decisions on how to handle it
internally (overwrite, add a new entry, merge them, etc...).
Easier said than done. :)
Regards,
Nicolas
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* [PATCH v4 00/11] mediatek: add support for MediaTek Ethernet MAC
From: Bartosz Golaszewski @ 2020-05-20 11:25 UTC (permalink / raw)
To: Rob Herring, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This adds support for the Ethernet Controller present on MediaTeK SoCs from
the MT8* family.
First we convert the existing DT bindings for the PERICFG controller to YAML
and add a new compatible string for mt8516 variant of it. Then we add the DT
bindings for the MAC.
Next we do some cleanup of the mediatek ethernet drivers directory.
The largest patch in the series adds the actual new driver.
The rest of the patches add DT fixups for the boards already supported
upstream.
v1 -> v2:
- add a generic helper for retrieving the net_device associated with given
private data
- fix several typos in commit messages
- remove MTK_MAC_VERSION and don't set the driver version
- use NET_IP_ALIGN instead of a magic number (2) but redefine it as it defaults
to 0 on arm64
- don't manually turn the carrier off in mtk_mac_enable()
- process TX cleanup in napi poll callback
- configure pause in the adjust_link callback
- use regmap_read_poll_timeout() instead of handcoding the polling
- use devres_find() to verify that struct net_device is managed by devres in
devm_register_netdev()
- add a patch moving all networking devres helpers into net/devres.c
- tweak the dma barriers: remove where unnecessary and add comments to the
remaining barriers
- don't reset internal counters when enabling the NIC
- set the net_device's mtu size instead of checking the framesize in
ndo_start_xmit() callback
- fix a race condition in waking up the netif queue
- don't emit log messages on OOM errors
- use dma_set_mask_and_coherent()
- use eth_hw_addr_random()
- rework the receive callback so that we reuse the previous skb if unmapping
fails, like we already do if skb allocation fails
- rework hash table operations: add proper timeout handling and clear bits when
appropriate
v2 -> v3:
- drop the patch adding priv_to_netdev() and store the netdev pointer in the
driver private data
- add an additional dma_wmb() after reseting the descriptor in
mtk_mac_ring_pop_tail()
- check the return value of dma_set_mask_and_coherent()
- improve the DT bindings for mtk-eth-mac: make the reg property in the example
use single-cell address and size, extend the description of the PERICFG
phandle and document the mdio sub-node
- add a patch converting the old .txt bindings for PERICFG to yaml
- limit reading the DMA memory by storing the mapped addresses in the driver
private structure
- add a patch documenting the existing networking devres helpers
v3 -> v4:
- drop the devres patches: they will be sent separately
- call netdev_sent_queue() & netdev_completed_queue() where appropriate
- don't redefine NET_IP_ALIGN: define a private constant in the driver
- fix a couple typos
- only disabe/enable the MAC in suspend/resume if netif is running
- drop the count field from the ring structure and instead calculate the number
of used descriptors from the tail and head indicies
- rework the locking used to protect the ring structures from concurrent
access: use cheaper spin_lock_bh() and completely disable the internal
spinlock used by regmap
- rework the interrupt handling to make it more fine-grained: onle re-enable
TX and RX interrupts while they're needed, process the stats updates in a
workqueue, not in napi context
- shrink the code responsible for unmapping and freeing skb memory
- rework the barriers as advised by Arnd
Bartosz Golaszewski (11):
dt-bindings: convert the binding document for mediatek PERICFG to yaml
dt-bindings: add new compatible to mediatek,pericfg
dt-bindings: net: add a binding document for MediaTek Ethernet MAC
net: ethernet: mediatek: rename Kconfig prompt
net: ethernet: mediatek: remove unnecessary spaces from Makefile
net: ethernet: mtk-eth-mac: new driver
ARM64: dts: mediatek: add pericfg syscon to mt8516.dtsi
ARM64: dts: mediatek: add the ethernet node to mt8516.dtsi
ARM64: dts: mediatek: add an alias for ethernet0 for pumpkin boards
ARM64: dts: mediatek: add ethernet pins for pumpkin boards
ARM64: dts: mediatek: enable ethernet on pumpkin boards
.../arm/mediatek/mediatek,pericfg.txt | 36 -
.../arm/mediatek/mediatek,pericfg.yaml | 64 +
.../bindings/net/mediatek,eth-mac.yaml | 89 +
arch/arm64/boot/dts/mediatek/mt8516.dtsi | 17 +
.../boot/dts/mediatek/pumpkin-common.dtsi | 34 +
drivers/net/ethernet/mediatek/Kconfig | 8 +-
drivers/net/ethernet/mediatek/Makefile | 3 +-
drivers/net/ethernet/mediatek/mtk_eth_mac.c | 1668 +++++++++++++++++
8 files changed, 1881 insertions(+), 38 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml
create mode 100644 Documentation/devicetree/bindings/net/mediatek,eth-mac.yaml
create mode 100644 drivers/net/ethernet/mediatek/mtk_eth_mac.c
--
2.25.0
^ permalink raw reply
* [PATCH v4 03/11] dt-bindings: net: add a binding document for MediaTek Ethernet MAC
From: Bartosz Golaszewski @ 2020-05-20 11:25 UTC (permalink / raw)
To: Rob Herring, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200520112523.30995-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This adds yaml DT bindings for the MediaTek Ethernet MAC present on the
mt8* family of SoCs.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../bindings/net/mediatek,eth-mac.yaml | 89 +++++++++++++++++++
1 file changed, 89 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/mediatek,eth-mac.yaml
diff --git a/Documentation/devicetree/bindings/net/mediatek,eth-mac.yaml b/Documentation/devicetree/bindings/net/mediatek,eth-mac.yaml
new file mode 100644
index 000000000000..8ffd0b762c0f
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/mediatek,eth-mac.yaml
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/mediatek,eth-mac.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek Ethernet MAC Controller
+
+maintainers:
+ - Bartosz Golaszewski <bgolaszewski@baylibre.com>
+
+description:
+ This Ethernet MAC is used on the MT8* family of SoCs from MediaTek.
+ It's compliant with 802.3 standards and supports half- and full-duplex
+ modes with flow-control as well as CRC offloading and VLAN tags.
+
+allOf:
+ - $ref: "ethernet-controller.yaml#"
+
+properties:
+ compatible:
+ enum:
+ - mediatek,mt8516-eth
+ - mediatek,mt8518-eth
+ - mediatek,mt8175-eth
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ minItems: 3
+ maxItems: 3
+
+ clock-names:
+ additionalItems: false
+ items:
+ - const: core
+ - const: reg
+ - const: trans
+
+ mediatek,pericfg:
+ $ref: /schemas/types.yaml#definitions/phandle
+ description:
+ Phandle to the device containing the PERICFG register range. This is used
+ to control the MII mode.
+
+ mdio:
+ type: object
+ description:
+ Creates and registers an MDIO bus.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - mediatek,pericfg
+ - phy-handle
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/mt8516-clk.h>
+
+ ethernet: ethernet@11180000 {
+ compatible = "mediatek,mt8516-eth";
+ reg = <0x11180000 0x1000>;
+ mediatek,pericfg = <&pericfg>;
+ interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&topckgen CLK_TOP_RG_ETH>,
+ <&topckgen CLK_TOP_66M_ETH>,
+ <&topckgen CLK_TOP_133M_ETH>;
+ clock-names = "core", "reg", "trans";
+ phy-handle = <ð_phy>;
+ phy-mode = "rmii";
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eth_phy: ethernet-phy@0 {
+ reg = <0>;
+ };
+ };
+ };
--
2.25.0
^ permalink raw reply related
* [PATCH v4 02/11] dt-bindings: add new compatible to mediatek,pericfg
From: Bartosz Golaszewski @ 2020-05-20 11:25 UTC (permalink / raw)
To: Rob Herring, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200520112523.30995-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
The PERICFG controller is present on the MT8516 SoC. Add an appropriate
compatible variant.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml
index 1340c6288024..55209a2baedc 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml
@@ -25,6 +25,7 @@ properties:
- mediatek,mt8135-pericfg
- mediatek,mt8173-pericfg
- mediatek,mt8183-pericfg
+ - mediatek,mt8516-pericfg
- const: syscon
- items:
# Special case for mt7623 for backward compatibility
--
2.25.0
^ permalink raw reply related
* [PATCH v4 04/11] net: ethernet: mediatek: rename Kconfig prompt
From: Bartosz Golaszewski @ 2020-05-20 11:25 UTC (permalink / raw)
To: Rob Herring, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200520112523.30995-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
We'll soon by adding a second MediaTek Ethernet driver so modify the
Kconfig prompt.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
drivers/net/ethernet/mediatek/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/mediatek/Kconfig b/drivers/net/ethernet/mediatek/Kconfig
index 4968352ba188..5079b8090f16 100644
--- a/drivers/net/ethernet/mediatek/Kconfig
+++ b/drivers/net/ethernet/mediatek/Kconfig
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
config NET_VENDOR_MEDIATEK
- bool "MediaTek ethernet driver"
+ bool "MediaTek devices"
depends on ARCH_MEDIATEK || SOC_MT7621 || SOC_MT7620
---help---
If you have a Mediatek SoC with ethernet, say Y.
--
2.25.0
^ permalink raw reply related
* [PATCH v4 05/11] net: ethernet: mediatek: remove unnecessary spaces from Makefile
From: Bartosz Golaszewski @ 2020-05-20 11:25 UTC (permalink / raw)
To: Rob Herring, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200520112523.30995-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
The Makefile formatting in the kernel tree usually doesn't use tabs,
so remove them before we add a second driver.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
drivers/net/ethernet/mediatek/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
index 2d8362f9341b..3362fb7ef859 100644
--- a/drivers/net/ethernet/mediatek/Makefile
+++ b/drivers/net/ethernet/mediatek/Makefile
@@ -3,5 +3,5 @@
# Makefile for the Mediatek SoCs built-in ethernet macs
#
-obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
+obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o
--
2.25.0
^ permalink raw reply related
* [PATCH v4 07/11] ARM64: dts: mediatek: add pericfg syscon to mt8516.dtsi
From: Bartosz Golaszewski @ 2020-05-20 11:25 UTC (permalink / raw)
To: Rob Herring, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200520112523.30995-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This adds support for the PERICFG register range as a syscon. This will
soon be used by the MediaTek Ethernet MAC driver for NIC configuration.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
arch/arm64/boot/dts/mediatek/mt8516.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8516.dtsi b/arch/arm64/boot/dts/mediatek/mt8516.dtsi
index 2f8adf042195..8cedaf74ae86 100644
--- a/arch/arm64/boot/dts/mediatek/mt8516.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8516.dtsi
@@ -191,6 +191,11 @@ infracfg: infracfg@10001000 {
#clock-cells = <1>;
};
+ pericfg: pericfg@10003050 {
+ compatible = "mediatek,mt8516-pericfg", "syscon";
+ reg = <0 0x10003050 0 0x1000>;
+ };
+
apmixedsys: apmixedsys@10018000 {
compatible = "mediatek,mt8516-apmixedsys", "syscon";
reg = <0 0x10018000 0 0x710>;
--
2.25.0
^ permalink raw reply related
* [PATCH v4 06/11] net: ethernet: mtk-eth-mac: new driver
From: Bartosz Golaszewski @ 2020-05-20 11:25 UTC (permalink / raw)
To: Rob Herring, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200520112523.30995-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
This adds the driver for the MediaTek Ethernet MAC used on the MT8* SoC
family. For now we only support full-duplex.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
drivers/net/ethernet/mediatek/Kconfig | 6 +
drivers/net/ethernet/mediatek/Makefile | 1 +
drivers/net/ethernet/mediatek/mtk_eth_mac.c | 1668 +++++++++++++++++++
3 files changed, 1675 insertions(+)
create mode 100644 drivers/net/ethernet/mediatek/mtk_eth_mac.c
diff --git a/drivers/net/ethernet/mediatek/Kconfig b/drivers/net/ethernet/mediatek/Kconfig
index 5079b8090f16..5c3793076765 100644
--- a/drivers/net/ethernet/mediatek/Kconfig
+++ b/drivers/net/ethernet/mediatek/Kconfig
@@ -14,4 +14,10 @@ config NET_MEDIATEK_SOC
This driver supports the gigabit ethernet MACs in the
MediaTek SoC family.
+config NET_MEDIATEK_MAC
+ tristate "MediaTek Ethernet MAC support"
+ select PHYLIB
+ help
+ This driver supports the ethernet IP on MediaTek MT85** SoCs.
+
endif #NET_VENDOR_MEDIATEK
diff --git a/drivers/net/ethernet/mediatek/Makefile b/drivers/net/ethernet/mediatek/Makefile
index 3362fb7ef859..f7f5638943a0 100644
--- a/drivers/net/ethernet/mediatek/Makefile
+++ b/drivers/net/ethernet/mediatek/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o
+obj-$(CONFIG_NET_MEDIATEK_MAC) += mtk_eth_mac.o
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_mac.c b/drivers/net/ethernet/mediatek/mtk_eth_mac.c
new file mode 100644
index 000000000000..4dfe7c2c4e3d
--- /dev/null
+++ b/drivers/net/ethernet/mediatek/mtk_eth_mac.c
@@ -0,0 +1,1668 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 MediaTek Corporation
+ * Copyright (c) 2020 BayLibre SAS
+ *
+ * Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+#define MTK_MAC_DRVNAME "mtk_eth_mac"
+
+#define MTK_MAC_WAIT_TIMEOUT 300
+#define MTK_MAC_MAX_FRAME_SIZE 1514
+#define MTK_MAC_SKB_ALIGNMENT 16
+#define MTK_MAC_NAPI_WEIGHT 64
+#define MTK_MAC_HASHTABLE_MC_LIMIT 256
+#define MTK_MAC_HASHTABLE_SIZE_MAX 512
+
+/* Normally we'd use NET_IP_ALIGN but on arm64 its value is 0 and it doesn't
+ * work for this controller.
+ */
+#define MTK_MAC_IP_ALIGN 2
+
+static const char *const mtk_mac_clk_names[] = { "core", "reg", "trans" };
+#define MTK_MAC_NCLKS ARRAY_SIZE(mtk_mac_clk_names)
+
+/* PHY Control Register 0 */
+#define MTK_MAC_REG_PHY_CTRL0 0x0000
+#define MTK_MAC_BIT_PHY_CTRL0_WTCMD BIT(13)
+#define MTK_MAC_BIT_PHY_CTRL0_RDCMD BIT(14)
+#define MTK_MAC_BIT_PHY_CTRL0_RWOK BIT(15)
+#define MTK_MAC_MSK_PHY_CTRL0_PREG GENMASK(12, 8)
+#define MTK_MAC_OFF_PHY_CTRL0_PREG 8
+#define MTK_MAC_MSK_PHY_CTRL0_RWDATA GENMASK(31, 16)
+#define MTK_MAC_OFF_PHY_CTRL0_RWDATA 16
+
+/* PHY Control Register 1 */
+#define MTK_MAC_REG_PHY_CTRL1 0x0004
+#define MTK_MAC_BIT_PHY_CTRL1_LINK_ST BIT(0)
+#define MTK_MAC_BIT_PHY_CTRL1_AN_EN BIT(8)
+#define MTK_MAC_OFF_PHY_CTRL1_FORCE_SPD 9
+#define MTK_MAC_VAL_PHY_CTRL1_FORCE_SPD_10M 0x00
+#define MTK_MAC_VAL_PHY_CTRL1_FORCE_SPD_100M 0x01
+#define MTK_MAC_VAL_PHY_CTRL1_FORCE_SPD_1000M 0x02
+#define MTK_MAC_BIT_PHY_CTRL1_FORCE_DPX BIT(11)
+#define MTK_MAC_BIT_PHY_CTRL1_FORCE_FC_RX BIT(12)
+#define MTK_MAC_BIT_PHY_CTRL1_FORCE_FC_TX BIT(13)
+
+/* MAC Configuration Register */
+#define MTK_MAC_REG_MAC_CFG 0x0008
+#define MTK_MAC_OFF_MAC_CFG_IPG 10
+#define MTK_MAC_VAL_MAC_CFG_IPG_96BIT GENMASK(4, 0)
+#define MTK_MAC_BIT_MAC_CFG_MAXLEN_1522 BIT(16)
+#define MTK_MAC_BIT_MAC_CFG_AUTO_PAD BIT(19)
+#define MTK_MAC_BIT_MAC_CFG_CRC_STRIP BIT(20)
+#define MTK_MAC_BIT_MAC_CFG_VLAN_STRIP BIT(22)
+#define MTK_MAC_BIT_MAC_CFG_NIC_PD BIT(31)
+
+/* Flow-Control Configuration Register */
+#define MTK_MAC_REG_FC_CFG 0x000c
+#define MTK_MAC_BIT_FC_CFG_BP_EN BIT(7)
+#define MTK_MAC_BIT_FC_CFG_UC_PAUSE_DIR BIT(8)
+#define MTK_MAC_OFF_FC_CFG_SEND_PAUSE_TH 16
+#define MTK_MAC_MSK_FC_CFG_SEND_PAUSE_TH GENMASK(27, 16)
+#define MTK_MAC_VAL_FC_CFG_SEND_PAUSE_TH_2K 0x800
+
+/* ARL Configuration Register */
+#define MTK_MAC_REG_ARL_CFG 0x0010
+#define MTK_MAC_BIT_ARL_CFG_HASH_ALG BIT(0)
+#define MTK_MAC_BIT_ARL_CFG_MISC_MODE BIT(4)
+
+/* MAC High and Low Bytes Registers */
+#define MTK_MAC_REG_MY_MAC_H 0x0014
+#define MTK_MAC_REG_MY_MAC_L 0x0018
+
+/* Hash Table Control Register */
+#define MTK_MAC_REG_HASH_CTRL 0x001c
+#define MTK_MAC_MSK_HASH_CTRL_HASH_BIT_ADDR GENMASK(8, 0)
+#define MTK_MAC_BIT_HASH_CTRL_HASH_BIT_DATA BIT(12)
+#define MTK_MAC_BIT_HASH_CTRL_ACC_CMD BIT(13)
+#define MTK_MAC_BIT_HASH_CTRL_CMD_START BIT(14)
+#define MTK_MAC_BIT_HASH_CTRL_BIST_OK BIT(16)
+#define MTK_MAC_BIT_HASH_CTRL_BIST_DONE BIT(17)
+#define MTK_MAC_BIT_HASH_CTRL_BIST_EN BIT(31)
+
+/* TX DMA Control Register */
+#define MTK_MAC_REG_TX_DMA_CTRL 0x0034
+#define MTK_MAC_BIT_TX_DMA_CTRL_START BIT(0)
+#define MTK_MAC_BIT_TX_DMA_CTRL_STOP BIT(1)
+#define MTK_MAC_BIT_TX_DMA_CTRL_RESUME BIT(2)
+
+/* RX DMA Control Register */
+#define MTK_MAC_REG_RX_DMA_CTRL 0x0038
+#define MTK_MAC_BIT_RX_DMA_CTRL_START BIT(0)
+#define MTK_MAC_BIT_RX_DMA_CTRL_STOP BIT(1)
+#define MTK_MAC_BIT_RX_DMA_CTRL_RESUME BIT(2)
+
+/* DMA Address Registers */
+#define MTK_MAC_REG_TX_DPTR 0x003c
+#define MTK_MAC_REG_RX_DPTR 0x0040
+#define MTK_MAC_REG_TX_BASE_ADDR 0x0044
+#define MTK_MAC_REG_RX_BASE_ADDR 0x0048
+
+/* Interrupt Status Register */
+#define MTK_MAC_REG_INT_STS 0x0050
+#define MTK_MAC_REG_INT_STS_PORT_STS_CHG BIT(2)
+#define MTK_MAC_REG_INT_STS_MIB_CNT_TH BIT(3)
+#define MTK_MAC_BIT_INT_STS_FNRC BIT(6)
+#define MTK_MAC_BIT_INT_STS_TNTC BIT(8)
+
+/* Interrupt Mask Register */
+#define MTK_MAC_REG_INT_MASK 0x0054
+#define MTK_MAC_BIT_INT_MASK_FNRC BIT(6)
+
+/* Misc. Config Register */
+#define MTK_MAC_REG_TEST1 0x005c
+#define MTK_MAC_BIT_TEST1_RST_HASH_MBIST BIT(31)
+
+/* Extended Configuration Register */
+#define MTK_MAC_REG_EXT_CFG 0x0060
+#define MTK_MAC_OFF_EXT_CFG_SND_PAUSE_RLS 16
+#define MTK_MAC_MSK_EXT_CFG_SND_PAUSE_RLS GENMASK(26, 16)
+#define MTK_MAC_VAL_EXT_CFG_SND_PAUSE_RLS_1K 0x400
+
+/* EthSys Configuration Register */
+#define MTK_MAC_REG_SYS_CONF 0x0094
+#define MTK_MAC_BIT_MII_PAD_OUT_ENABLE BIT(0)
+#define MTK_MAC_BIT_EXT_MDC_MODE BIT(1)
+#define MTK_MAC_BIT_SWC_MII_MODE BIT(2)
+
+/* MAC Clock Configuration Register */
+#define MTK_MAC_REG_MAC_CLK_CONF 0x00ac
+#define MTK_MAC_MSK_MAC_CLK_CONF GENMASK(7, 0)
+#define MTK_MAC_BIT_CLK_DIV_10 0x0a
+
+/* Counter registers. */
+#define MTK_MAC_REG_C_RXOKPKT 0x0100
+#define MTK_MAC_REG_C_RXOKBYTE 0x0104
+#define MTK_MAC_REG_C_RXRUNT 0x0108
+#define MTK_MAC_REG_C_RXLONG 0x010c
+#define MTK_MAC_REG_C_RXDROP 0x0110
+#define MTK_MAC_REG_C_RXCRC 0x0114
+#define MTK_MAC_REG_C_RXARLDROP 0x0118
+#define MTK_MAC_REG_C_RXVLANDROP 0x011c
+#define MTK_MAC_REG_C_RXCSERR 0x0120
+#define MTK_MAC_REG_C_RXPAUSE 0x0124
+#define MTK_MAC_REG_C_TXOKPKT 0x0128
+#define MTK_MAC_REG_C_TXOKBYTE 0x012c
+#define MTK_MAC_REG_C_TXPAUSECOL 0x0130
+#define MTK_MAC_REG_C_TXRTY 0x0134
+#define MTK_MAC_REG_C_TXSKIP 0x0138
+#define MTK_MAC_REG_C_TX_ARP 0x013c
+#define MTK_MAC_REG_C_RX_RERR 0x01d8
+#define MTK_MAC_REG_C_RX_UNI 0x01dc
+#define MTK_MAC_REG_C_RX_MULTI 0x01e0
+#define MTK_MAC_REG_C_RX_BROAD 0x01e4
+#define MTK_MAC_REG_C_RX_ALIGNERR 0x01e8
+#define MTK_MAC_REG_C_TX_UNI 0x01ec
+#define MTK_MAC_REG_C_TX_MULTI 0x01f0
+#define MTK_MAC_REG_C_TX_BROAD 0x01f4
+#define MTK_MAC_REG_C_TX_TIMEOUT 0x01f8
+#define MTK_MAC_REG_C_TX_LATECOL 0x01fc
+#define MTK_MAC_REG_C_RX_LENGTHERR 0x0214
+#define MTK_MAC_REG_C_RX_TWIST 0x0218
+
+/* Ethernet CFG Control */
+#define MTK_PERICFG_REG_NIC_CFG_CON 0x03c4
+#define MTK_PERICFG_MSK_NIC_CFG_CON_CFG_MII GENMASK(3, 0)
+#define MTK_PERICFG_BIT_NIC_CFG_CON_RMII BIT(0)
+
+/* Represents the actual structure of descriptors used by the MAC. We can
+ * reuse the same structure for both TX and RX - the layout is the same, only
+ * the flags differ slightly.
+ */
+struct mtk_mac_ring_desc {
+ /* Contains both the status flags as well as packet length. */
+ u32 status;
+ u32 data_ptr;
+ u32 vtag;
+ u32 reserved;
+};
+
+#define MTK_MAC_DESC_MSK_LEN GENMASK(15, 0)
+#define MTK_MAC_DESC_BIT_RX_CRCE BIT(24)
+#define MTK_MAC_DESC_BIT_RX_OSIZE BIT(25)
+#define MTK_MAC_DESC_BIT_INT BIT(27)
+#define MTK_MAC_DESC_BIT_LS BIT(28)
+#define MTK_MAC_DESC_BIT_FS BIT(29)
+#define MTK_MAC_DESC_BIT_EOR BIT(30)
+#define MTK_MAC_DESC_BIT_COWN BIT(31)
+
+/* Helper structure for storing data read from/written to descriptors in order
+ * to limit reads from/writes to DMA memory.
+ */
+struct mtk_mac_ring_desc_data {
+ unsigned int len;
+ unsigned int flags;
+ dma_addr_t dma_addr;
+ struct sk_buff *skb;
+};
+
+#define MTK_MAC_RING_NUM_DESCS 128
+#define MTK_MAC_NUM_TX_DESCS MTK_MAC_RING_NUM_DESCS
+#define MTK_MAC_NUM_RX_DESCS MTK_MAC_RING_NUM_DESCS
+#define MTK_MAC_NUM_DESCS_TOTAL (MTK_MAC_RING_NUM_DESCS * 2)
+#define MTK_MAC_DMA_SIZE \
+ (MTK_MAC_NUM_DESCS_TOTAL * sizeof(struct mtk_mac_ring_desc))
+
+struct mtk_mac_ring {
+ struct mtk_mac_ring_desc *descs;
+ struct sk_buff *skbs[MTK_MAC_RING_NUM_DESCS];
+ dma_addr_t dma_addrs[MTK_MAC_RING_NUM_DESCS];
+ unsigned int head;
+ unsigned int tail;
+};
+
+struct mtk_mac_priv {
+ struct net_device *ndev;
+
+ struct regmap *regs;
+ struct regmap *pericfg;
+
+ struct clk_bulk_data clks[MTK_MAC_NCLKS];
+
+ void *ring_base;
+ struct mtk_mac_ring_desc *descs_base;
+ dma_addr_t dma_addr;
+ struct mtk_mac_ring tx_ring;
+ struct mtk_mac_ring rx_ring;
+
+ struct mii_bus *mii;
+ struct napi_struct napi;
+
+ struct device_node *phy_node;
+ phy_interface_t phy_intf;
+ struct phy_device *phydev;
+ unsigned int link;
+ int speed;
+ int duplex;
+ int pause;
+
+ /* Protects against concurrent descriptor access. */
+ spinlock_t lock;
+
+ struct rtnl_link_stats64 stats;
+ struct work_struct stats_work;
+};
+
+static struct device *mtk_mac_get_dev(struct mtk_mac_priv *priv)
+{
+ return priv->ndev->dev.parent;
+}
+
+static const struct regmap_config mtk_mac_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .disable_locking = true,
+};
+
+static void mtk_mac_ring_init(struct mtk_mac_ring *ring,
+ struct mtk_mac_ring_desc *descs)
+{
+ memset(ring, 0, sizeof(*ring));
+ ring->descs = descs;
+ ring->head = 0;
+ ring->tail = 0;
+}
+
+static int mtk_mac_ring_pop_tail(struct mtk_mac_ring *ring,
+ struct mtk_mac_ring_desc_data *desc_data)
+{
+ struct mtk_mac_ring_desc *desc = &ring->descs[ring->tail];
+ unsigned int status;
+
+ status = desc->status;
+ dma_rmb(); /* Make sure we read the status bits before checking it. */
+
+ if (!(status & MTK_MAC_DESC_BIT_COWN))
+ return -1;
+
+ desc_data->len = status & MTK_MAC_DESC_MSK_LEN;
+ desc_data->flags = status & ~MTK_MAC_DESC_MSK_LEN;
+ desc_data->dma_addr = ring->dma_addrs[ring->tail];
+ desc_data->skb = ring->skbs[ring->tail];
+
+ ring->dma_addrs[ring->tail] = 0;
+ ring->skbs[ring->tail] = NULL;
+
+ desc->data_ptr = 0;
+ desc->status = MTK_MAC_DESC_BIT_COWN;
+ if (status & MTK_MAC_DESC_BIT_EOR)
+ desc->status |= MTK_MAC_DESC_BIT_EOR;
+
+ ring->tail = (ring->tail + 1) % MTK_MAC_RING_NUM_DESCS;
+
+ return 0;
+}
+
+static void mtk_mac_ring_push_head(struct mtk_mac_ring *ring,
+ struct mtk_mac_ring_desc_data *desc_data,
+ unsigned int flags)
+{
+ struct mtk_mac_ring_desc *desc = &ring->descs[ring->head];
+ unsigned int status;
+
+ status = desc->status;
+
+ ring->skbs[ring->head] = desc_data->skb;
+ ring->dma_addrs[ring->head] = desc_data->dma_addr;
+ desc->data_ptr = desc_data->dma_addr;
+
+ status |= desc_data->len;
+ if (flags)
+ status |= flags;
+ desc->status = status;
+
+ /* Flush previous modifications before ownership change. */
+ dma_wmb();
+ desc->status &= ~MTK_MAC_DESC_BIT_COWN;
+
+ ring->head = (ring->head + 1) % MTK_MAC_RING_NUM_DESCS;
+}
+
+static void mtk_mac_ring_push_head_rx(struct mtk_mac_ring *ring,
+ struct mtk_mac_ring_desc_data *desc_data)
+{
+ mtk_mac_ring_push_head(ring, desc_data, 0);
+}
+
+static void mtk_mac_ring_push_head_tx(struct mtk_mac_ring *ring,
+ struct mtk_mac_ring_desc_data *desc_data)
+{
+ static const unsigned int flags = MTK_MAC_DESC_BIT_FS |
+ MTK_MAC_DESC_BIT_LS |
+ MTK_MAC_DESC_BIT_INT;
+
+ mtk_mac_ring_push_head(ring, desc_data, flags);
+}
+
+static unsigned int mtk_mac_ring_num_used_descs(struct mtk_mac_ring *ring)
+{
+ return abs(ring->head - ring->tail);
+}
+
+static bool mtk_mac_ring_full(struct mtk_mac_ring *ring)
+{
+ return mtk_mac_ring_num_used_descs(ring) == MTK_MAC_RING_NUM_DESCS;
+}
+
+static bool mtk_mac_ring_descs_available(struct mtk_mac_ring *ring)
+{
+ return mtk_mac_ring_num_used_descs(ring) > 0;
+}
+
+static void mtk_mac_lock(struct mtk_mac_priv *priv)
+{
+ spin_lock_bh(&priv->lock);
+}
+
+static void mtk_mac_unlock(struct mtk_mac_priv *priv)
+{
+ spin_unlock_bh(&priv->lock);
+}
+
+static dma_addr_t mtk_mac_dma_map_rx(struct mtk_mac_priv *priv,
+ struct sk_buff *skb)
+{
+ struct device *dev = mtk_mac_get_dev(priv);
+
+ /* Data pointer for the RX DMA descriptor must be aligned to 4N + 2. */
+ return dma_map_single(dev, skb_tail_pointer(skb) - 2,
+ skb_tailroom(skb), DMA_FROM_DEVICE);
+}
+
+static void mtk_mac_dma_unmap_rx(struct mtk_mac_priv *priv,
+ struct mtk_mac_ring_desc_data *desc_data)
+{
+ struct device *dev = mtk_mac_get_dev(priv);
+
+ dma_unmap_single(dev, desc_data->dma_addr,
+ skb_tailroom(desc_data->skb), DMA_FROM_DEVICE);
+}
+
+static dma_addr_t mtk_mac_dma_map_tx(struct mtk_mac_priv *priv,
+ struct sk_buff *skb)
+{
+ struct device *dev = mtk_mac_get_dev(priv);
+
+ return dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);
+}
+
+static void mtk_mac_dma_unmap_tx(struct mtk_mac_priv *priv,
+ struct mtk_mac_ring_desc_data *desc_data)
+{
+ struct device *dev = mtk_mac_get_dev(priv);
+
+ return dma_unmap_single(dev, desc_data->dma_addr,
+ skb_headlen(desc_data->skb), DMA_TO_DEVICE);
+}
+
+static void mtk_mac_nic_disable_pd(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->regs, MTK_MAC_REG_MAC_CFG,
+ MTK_MAC_BIT_MAC_CFG_NIC_PD, 0);
+}
+
+/* Unmask the three interrupts we care about, mask all others. */
+static void mtk_mac_intr_enable(struct mtk_mac_priv *priv)
+{
+ unsigned int val = MTK_MAC_BIT_INT_STS_TNTC |
+ MTK_MAC_BIT_INT_STS_FNRC |
+ MTK_MAC_REG_INT_STS_MIB_CNT_TH;
+
+ regmap_write(priv->regs, MTK_MAC_REG_INT_MASK, ~val);
+}
+
+static void mtk_mac_intr_disable(struct mtk_mac_priv *priv)
+{
+ regmap_write(priv->regs, MTK_MAC_REG_INT_MASK, ~0);
+}
+
+static void mtk_mac_intr_enable_tx(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->regs, MTK_MAC_REG_INT_MASK,
+ MTK_MAC_BIT_INT_STS_TNTC, 0);
+}
+
+static void mtk_mac_intr_enable_rx(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->regs, MTK_MAC_REG_INT_MASK,
+ MTK_MAC_BIT_INT_STS_FNRC, 0);
+}
+
+static void mtk_mac_intr_enable_stats(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->regs, MTK_MAC_REG_INT_MASK,
+ MTK_MAC_REG_INT_STS_MIB_CNT_TH, 0);
+}
+
+static void mtk_mac_intr_disable_tx(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->regs, MTK_MAC_REG_INT_MASK,
+ MTK_MAC_BIT_INT_STS_TNTC, MTK_MAC_BIT_INT_STS_TNTC);
+}
+
+static void mtk_mac_intr_disable_rx(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->regs, MTK_MAC_REG_INT_MASK,
+ MTK_MAC_BIT_INT_STS_FNRC, MTK_MAC_BIT_INT_STS_FNRC);
+}
+
+static void mtk_mac_intr_disable_stats(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->regs, MTK_MAC_REG_INT_MASK,
+ MTK_MAC_REG_INT_STS_MIB_CNT_TH,
+ MTK_MAC_REG_INT_STS_MIB_CNT_TH);
+}
+
+static unsigned int mtk_mac_intr_read(struct mtk_mac_priv *priv)
+{
+ unsigned int val;
+
+ regmap_read(priv->regs, MTK_MAC_REG_INT_STS, &val);
+
+ return val;
+}
+
+static unsigned int mtk_mac_intr_ack_all(struct mtk_mac_priv *priv)
+{
+ unsigned int val;
+
+ val = mtk_mac_intr_read(priv);
+ regmap_write(priv->regs, MTK_MAC_REG_INT_STS, val);
+
+ return val;
+}
+
+static void mtk_mac_dma_init(struct mtk_mac_priv *priv)
+{
+ struct mtk_mac_ring_desc *desc;
+ unsigned int val;
+ int i;
+
+ priv->descs_base = (struct mtk_mac_ring_desc *)priv->ring_base;
+
+ for (i = 0; i < MTK_MAC_NUM_DESCS_TOTAL; i++) {
+ desc = &priv->descs_base[i];
+
+ memset(desc, 0, sizeof(*desc));
+ desc->status = MTK_MAC_DESC_BIT_COWN;
+ if ((i == MTK_MAC_NUM_TX_DESCS - 1) ||
+ (i == MTK_MAC_NUM_DESCS_TOTAL - 1))
+ desc->status |= MTK_MAC_DESC_BIT_EOR;
+ }
+
+ mtk_mac_ring_init(&priv->tx_ring, priv->descs_base);
+ mtk_mac_ring_init(&priv->rx_ring,
+ priv->descs_base + MTK_MAC_NUM_TX_DESCS);
+
+ /* Set DMA pointers. */
+ val = (unsigned int)priv->dma_addr;
+ regmap_write(priv->regs, MTK_MAC_REG_TX_BASE_ADDR, val);
+ regmap_write(priv->regs, MTK_MAC_REG_TX_DPTR, val);
+
+ val += sizeof(struct mtk_mac_ring_desc) * MTK_MAC_NUM_TX_DESCS;
+ regmap_write(priv->regs, MTK_MAC_REG_RX_BASE_ADDR, val);
+ regmap_write(priv->regs, MTK_MAC_REG_RX_DPTR, val);
+}
+
+static void mtk_mac_dma_start(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->regs, MTK_MAC_REG_TX_DMA_CTRL,
+ MTK_MAC_BIT_TX_DMA_CTRL_START,
+ MTK_MAC_BIT_TX_DMA_CTRL_START);
+ regmap_update_bits(priv->regs, MTK_MAC_REG_RX_DMA_CTRL,
+ MTK_MAC_BIT_RX_DMA_CTRL_START,
+ MTK_MAC_BIT_RX_DMA_CTRL_START);
+}
+
+static void mtk_mac_dma_stop(struct mtk_mac_priv *priv)
+{
+ regmap_write(priv->regs, MTK_MAC_REG_TX_DMA_CTRL,
+ MTK_MAC_BIT_TX_DMA_CTRL_STOP);
+ regmap_write(priv->regs, MTK_MAC_REG_RX_DMA_CTRL,
+ MTK_MAC_BIT_RX_DMA_CTRL_STOP);
+}
+
+static void mtk_mac_dma_disable(struct mtk_mac_priv *priv)
+{
+ int i;
+
+ mtk_mac_dma_stop(priv);
+
+ /* Take back all descriptors. */
+ for (i = 0; i < MTK_MAC_NUM_DESCS_TOTAL; i++)
+ priv->descs_base[i].status |= MTK_MAC_DESC_BIT_COWN;
+}
+
+static void mtk_mac_dma_resume_rx(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->regs, MTK_MAC_REG_RX_DMA_CTRL,
+ MTK_MAC_BIT_RX_DMA_CTRL_RESUME,
+ MTK_MAC_BIT_RX_DMA_CTRL_RESUME);
+}
+
+static void mtk_mac_dma_resume_tx(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->regs, MTK_MAC_REG_TX_DMA_CTRL,
+ MTK_MAC_BIT_TX_DMA_CTRL_RESUME,
+ MTK_MAC_BIT_TX_DMA_CTRL_RESUME);
+}
+
+static void mtk_mac_set_mac_addr(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ u8 *mac_addr = ndev->dev_addr;
+ unsigned int high, low;
+
+ high = mac_addr[0] << 8 | mac_addr[1] << 0;
+ low = mac_addr[2] << 24 | mac_addr[3] << 16 |
+ mac_addr[4] << 8 | mac_addr[5];
+
+ regmap_write(priv->regs, MTK_MAC_REG_MY_MAC_H, high);
+ regmap_write(priv->regs, MTK_MAC_REG_MY_MAC_L, low);
+}
+
+static void mtk_mac_reset_counters(struct mtk_mac_priv *priv)
+{
+ static const unsigned int counter_regs[] = {
+ MTK_MAC_REG_C_RXOKPKT,
+ MTK_MAC_REG_C_RXOKBYTE,
+ MTK_MAC_REG_C_RXRUNT,
+ MTK_MAC_REG_C_RXLONG,
+ MTK_MAC_REG_C_RXDROP,
+ MTK_MAC_REG_C_RXCRC,
+ MTK_MAC_REG_C_RXARLDROP,
+ MTK_MAC_REG_C_RXVLANDROP,
+ MTK_MAC_REG_C_RXCSERR,
+ MTK_MAC_REG_C_RXPAUSE,
+ MTK_MAC_REG_C_TXOKPKT,
+ MTK_MAC_REG_C_TXOKBYTE,
+ MTK_MAC_REG_C_TXPAUSECOL,
+ MTK_MAC_REG_C_TXRTY,
+ MTK_MAC_REG_C_TXSKIP,
+ MTK_MAC_REG_C_TX_ARP,
+ MTK_MAC_REG_C_RX_RERR,
+ MTK_MAC_REG_C_RX_UNI,
+ MTK_MAC_REG_C_RX_MULTI,
+ MTK_MAC_REG_C_RX_BROAD,
+ MTK_MAC_REG_C_RX_ALIGNERR,
+ MTK_MAC_REG_C_TX_UNI,
+ MTK_MAC_REG_C_TX_MULTI,
+ MTK_MAC_REG_C_TX_BROAD,
+ MTK_MAC_REG_C_TX_TIMEOUT,
+ MTK_MAC_REG_C_TX_LATECOL,
+ MTK_MAC_REG_C_RX_LENGTHERR,
+ MTK_MAC_REG_C_RX_TWIST,
+ };
+
+ unsigned int i, val;
+
+ for (i = 0; i < ARRAY_SIZE(counter_regs); i++)
+ regmap_read(priv->regs, counter_regs[i], &val);
+}
+
+static void mtk_mac_update_stat(struct mtk_mac_priv *priv,
+ unsigned int reg, u64 *stat)
+{
+ unsigned int val;
+
+ regmap_read(priv->regs, reg, &val);
+ *stat += val;
+}
+
+/* Try to get as many stats as possible from the internal registers instead
+ * of tracking them ourselves.
+ */
+static void mtk_mac_update_stats(struct mtk_mac_priv *priv)
+{
+ struct rtnl_link_stats64 *stats = &priv->stats;
+
+ /* OK packets and bytes. */
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RXOKPKT, &stats->rx_packets);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_TXOKPKT, &stats->tx_packets);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RXOKBYTE, &stats->rx_bytes);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_TXOKBYTE, &stats->tx_bytes);
+
+ /* RX & TX multicast. */
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RX_MULTI, &stats->multicast);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_TX_MULTI, &stats->multicast);
+
+ /* Collisions. */
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_TXPAUSECOL, &stats->collisions);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_TX_LATECOL, &stats->collisions);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RXRUNT, &stats->collisions);
+
+ /* RX Errors. */
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RX_LENGTHERR,
+ &stats->rx_length_errors);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RXLONG, &stats->rx_over_errors);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RXCRC, &stats->rx_crc_errors);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RX_ALIGNERR,
+ &stats->rx_frame_errors);
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RXDROP, &stats->rx_fifo_errors);
+ /* Sum of the general RX error counter + all of the above. */
+ mtk_mac_update_stat(priv, MTK_MAC_REG_C_RX_RERR, &stats->rx_errors);
+ stats->rx_errors += stats->rx_length_errors;
+ stats->rx_errors += stats->rx_over_errors;
+ stats->rx_errors += stats->rx_crc_errors;
+ stats->rx_errors += stats->rx_frame_errors;
+ stats->rx_errors += stats->rx_fifo_errors;
+}
+
+/* This runs in process context and parallel TX and RX paths executing in
+ * napi context may result in losing some stats data but this should happen
+ * seldom enough to be acceptable.
+ */
+static void mtk_mac_update_stats_work(struct work_struct *work)
+{
+ struct mtk_mac_priv *priv = container_of(work, struct mtk_mac_priv,
+ stats_work);
+
+ mtk_mac_update_stats(priv);
+ mtk_mac_reset_counters(priv);
+ mtk_mac_intr_enable_stats(priv);
+}
+
+static struct sk_buff *mtk_mac_alloc_skb(struct net_device *ndev)
+{
+ uintptr_t tail, offset;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(MTK_MAC_MAX_FRAME_SIZE);
+ if (!skb)
+ return NULL;
+
+ /* Align to 16 bytes. */
+ tail = (uintptr_t)skb_tail_pointer(skb);
+ if (tail & (MTK_MAC_SKB_ALIGNMENT - 1)) {
+ offset = tail & (MTK_MAC_SKB_ALIGNMENT - 1);
+ skb_reserve(skb, MTK_MAC_SKB_ALIGNMENT - offset);
+ }
+
+ /* Ensure 16-byte alignment of the skb pointer: eth_type_trans() will
+ * extract the Ethernet header (14 bytes) so we need two more bytes.
+ */
+ skb_reserve(skb, MTK_MAC_IP_ALIGN);
+
+ return skb;
+}
+
+static int mtk_mac_prepare_rx_skbs(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ struct mtk_mac_ring *ring = &priv->rx_ring;
+ struct device *dev = mtk_mac_get_dev(priv);
+ struct mtk_mac_ring_desc *desc;
+ struct sk_buff *skb;
+ dma_addr_t dma_addr;
+ int i;
+
+ for (i = 0; i < MTK_MAC_NUM_RX_DESCS; i++) {
+ skb = mtk_mac_alloc_skb(ndev);
+ if (!skb)
+ return -ENOMEM;
+
+ dma_addr = mtk_mac_dma_map_rx(priv, skb);
+ if (dma_mapping_error(dev, dma_addr)) {
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ desc = &ring->descs[i];
+ desc->data_ptr = dma_addr;
+ desc->status |= skb_tailroom(skb) & MTK_MAC_DESC_MSK_LEN;
+ desc->status &= ~MTK_MAC_DESC_BIT_COWN;
+ ring->skbs[i] = skb;
+ ring->dma_addrs[i] = dma_addr;
+ }
+
+ return 0;
+}
+
+static void
+mtk_mac_ring_free_skbs(struct mtk_mac_priv *priv, struct mtk_mac_ring *ring,
+ void (*unmap_func)(struct mtk_mac_priv *,
+ struct mtk_mac_ring_desc_data *))
+{
+ struct mtk_mac_ring_desc_data desc_data;
+ struct mtk_mac_ring_desc *desc;
+ int i;
+
+ for (i = 0; i < MTK_MAC_RING_NUM_DESCS; i++) {
+ if (!ring->dma_addrs[i])
+ continue;
+
+ desc = &ring->descs[i];
+
+ desc_data.dma_addr = ring->dma_addrs[i];
+ desc_data.skb = ring->skbs[i];
+
+ unmap_func(priv, &desc_data);
+ dev_kfree_skb(desc_data.skb);
+ }
+}
+
+static void mtk_mac_free_rx_skbs(struct mtk_mac_priv *priv)
+{
+ struct mtk_mac_ring *ring = &priv->rx_ring;
+
+ mtk_mac_ring_free_skbs(priv, ring, mtk_mac_dma_unmap_rx);
+}
+
+static void mtk_mac_free_tx_skbs(struct mtk_mac_priv *priv)
+{
+ struct mtk_mac_ring *ring = &priv->tx_ring;
+
+ mtk_mac_ring_free_skbs(priv, ring, mtk_mac_dma_unmap_tx);
+}
+
+/* All processing for TX and RX happens in the napi poll callback. */
+static irqreturn_t mtk_mac_handle_irq(int irq, void *data)
+{
+ struct mtk_mac_priv *priv;
+ struct net_device *ndev;
+ bool need_napi = false;
+ unsigned int status;
+
+ ndev = data;
+ priv = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ status = mtk_mac_intr_read(priv);
+
+ if (status & MTK_MAC_BIT_INT_STS_TNTC) {
+ mtk_mac_intr_disable_tx(priv);
+ need_napi = true;
+ }
+
+ if (status & MTK_MAC_BIT_INT_STS_FNRC) {
+ mtk_mac_intr_disable_rx(priv);
+ need_napi = true;
+ }
+
+ if (need_napi)
+ napi_schedule(&priv->napi);
+
+ /* One of the counters reached 0x8000000 - update stats and
+ * reset all counters.
+ */
+ if (unlikely(status & MTK_MAC_REG_INT_STS_MIB_CNT_TH)) {
+ mtk_mac_intr_disable_stats(priv);
+ schedule_work(&priv->stats_work);
+ }
+
+ mtk_mac_intr_ack_all(priv);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* Wait for the completion of any previous command - CMD_START bit must be
+ * cleared by hardware.
+ */
+static int mtk_mac_hash_wait_cmd_start(struct mtk_mac_priv *priv)
+{
+ unsigned int val;
+
+ return regmap_read_poll_timeout_atomic(priv->regs,
+ MTK_MAC_REG_HASH_CTRL, val,
+ !(val & MTK_MAC_BIT_HASH_CTRL_CMD_START),
+ 10, MTK_MAC_WAIT_TIMEOUT);
+}
+
+static int mtk_mac_hash_wait_ok(struct mtk_mac_priv *priv)
+{
+ unsigned int val;
+ int ret;
+
+ /* Wait for BIST_DONE bit. */
+ ret = regmap_read_poll_timeout_atomic(priv->regs,
+ MTK_MAC_REG_HASH_CTRL, val,
+ val & MTK_MAC_BIT_HASH_CTRL_BIST_DONE,
+ 10, MTK_MAC_WAIT_TIMEOUT);
+ if (ret)
+ return ret;
+
+ /* Check the BIST_OK bit. */
+ regmap_read(priv->regs, MTK_MAC_REG_HASH_CTRL, &val);
+ if (!(val & MTK_MAC_BIT_HASH_CTRL_BIST_OK))
+ return -EIO;
+
+ return 0;
+}
+
+static int mtk_mac_set_hashbit(struct mtk_mac_priv *priv,
+ unsigned int hash_addr)
+{
+ unsigned int val;
+ int ret;
+
+ ret = mtk_mac_hash_wait_cmd_start(priv);
+ if (ret)
+ return ret;
+
+ val = hash_addr & MTK_MAC_MSK_HASH_CTRL_HASH_BIT_ADDR;
+ val |= MTK_MAC_BIT_HASH_CTRL_ACC_CMD;
+ val |= MTK_MAC_BIT_HASH_CTRL_CMD_START;
+ val |= MTK_MAC_BIT_HASH_CTRL_BIST_EN;
+ val |= MTK_MAC_BIT_HASH_CTRL_HASH_BIT_DATA;
+ regmap_write(priv->regs, MTK_MAC_REG_HASH_CTRL, val);
+
+ return mtk_mac_hash_wait_ok(priv);
+}
+
+static int mtk_mac_reset_hash_table(struct mtk_mac_priv *priv)
+{
+ int ret;
+
+ ret = mtk_mac_hash_wait_cmd_start(priv);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(priv->regs, MTK_MAC_REG_HASH_CTRL,
+ MTK_MAC_BIT_HASH_CTRL_BIST_EN,
+ MTK_MAC_BIT_HASH_CTRL_BIST_EN);
+ regmap_update_bits(priv->regs, MTK_MAC_REG_TEST1,
+ MTK_MAC_BIT_TEST1_RST_HASH_MBIST,
+ MTK_MAC_BIT_TEST1_RST_HASH_MBIST);
+
+ return mtk_mac_hash_wait_ok(priv);
+}
+
+static void mtk_mac_phy_config(struct mtk_mac_priv *priv)
+{
+ unsigned int val;
+
+ if (priv->speed == SPEED_1000)
+ val = MTK_MAC_VAL_PHY_CTRL1_FORCE_SPD_1000M;
+ else if (priv->speed == SPEED_100)
+ val = MTK_MAC_VAL_PHY_CTRL1_FORCE_SPD_100M;
+ else
+ val = MTK_MAC_VAL_PHY_CTRL1_FORCE_SPD_10M;
+ val <<= MTK_MAC_OFF_PHY_CTRL1_FORCE_SPD;
+
+ val |= MTK_MAC_BIT_PHY_CTRL1_AN_EN;
+ val |= MTK_MAC_BIT_PHY_CTRL1_FORCE_FC_RX;
+ val |= MTK_MAC_BIT_PHY_CTRL1_FORCE_FC_TX;
+ /* Only full-duplex supported for now. */
+ val |= MTK_MAC_BIT_PHY_CTRL1_FORCE_DPX;
+
+ regmap_write(priv->regs, MTK_MAC_REG_PHY_CTRL1, val);
+
+ if (priv->pause) {
+ val = MTK_MAC_VAL_FC_CFG_SEND_PAUSE_TH_2K;
+ val <<= MTK_MAC_OFF_FC_CFG_SEND_PAUSE_TH;
+ val |= MTK_MAC_BIT_FC_CFG_UC_PAUSE_DIR;
+ } else {
+ val = 0;
+ }
+
+ regmap_update_bits(priv->regs, MTK_MAC_REG_FC_CFG,
+ MTK_MAC_MSK_FC_CFG_SEND_PAUSE_TH |
+ MTK_MAC_BIT_FC_CFG_UC_PAUSE_DIR, val);
+
+ if (priv->pause) {
+ val = MTK_MAC_VAL_EXT_CFG_SND_PAUSE_RLS_1K;
+ val <<= MTK_MAC_OFF_EXT_CFG_SND_PAUSE_RLS;
+ } else {
+ val = 0;
+ }
+
+ regmap_update_bits(priv->regs, MTK_MAC_REG_EXT_CFG,
+ MTK_MAC_MSK_EXT_CFG_SND_PAUSE_RLS, val);
+}
+
+static void mtk_mac_adjust_link(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ struct phy_device *phydev = priv->phydev;
+ bool new_state = false;
+
+ if (phydev->link) {
+ if (!priv->link) {
+ priv->link = phydev->link;
+ new_state = true;
+ }
+
+ if (priv->speed != phydev->speed) {
+ priv->speed = phydev->speed;
+ new_state = true;
+ }
+
+ if (priv->pause != phydev->pause) {
+ priv->pause = phydev->pause;
+ new_state = true;
+ }
+ } else {
+ if (priv->link) {
+ priv->link = phydev->link;
+ new_state = true;
+ }
+ }
+
+ if (new_state) {
+ if (phydev->link)
+ mtk_mac_phy_config(priv);
+
+ phy_print_status(ndev->phydev);
+ }
+}
+
+static void mtk_mac_init_config(struct mtk_mac_priv *priv)
+{
+ unsigned int val;
+
+ val = (MTK_MAC_BIT_MII_PAD_OUT_ENABLE |
+ MTK_MAC_BIT_EXT_MDC_MODE |
+ MTK_MAC_BIT_SWC_MII_MODE);
+
+ regmap_write(priv->regs, MTK_MAC_REG_SYS_CONF, val);
+ regmap_update_bits(priv->regs, MTK_MAC_REG_MAC_CLK_CONF,
+ MTK_MAC_MSK_MAC_CLK_CONF,
+ MTK_MAC_BIT_CLK_DIV_10);
+}
+
+static void mtk_mac_set_mode_rmii(struct mtk_mac_priv *priv)
+{
+ regmap_update_bits(priv->pericfg, MTK_PERICFG_REG_NIC_CFG_CON,
+ MTK_PERICFG_MSK_NIC_CFG_CON_CFG_MII,
+ MTK_PERICFG_BIT_NIC_CFG_CON_RMII);
+}
+
+static int mtk_mac_enable(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ unsigned int val;
+ int ret;
+
+ mtk_mac_nic_disable_pd(priv);
+ mtk_mac_intr_disable(priv);
+ mtk_mac_dma_stop(priv);
+
+ mtk_mac_set_mac_addr(ndev);
+
+ /* Configure the MAC */
+ val = MTK_MAC_VAL_MAC_CFG_IPG_96BIT;
+ val <<= MTK_MAC_OFF_MAC_CFG_IPG;
+ val |= MTK_MAC_BIT_MAC_CFG_MAXLEN_1522;
+ val |= MTK_MAC_BIT_MAC_CFG_AUTO_PAD;
+ val |= MTK_MAC_BIT_MAC_CFG_CRC_STRIP;
+ regmap_write(priv->regs, MTK_MAC_REG_MAC_CFG, val);
+
+ /* Enable Hash Table BIST and reset it */
+ ret = mtk_mac_reset_hash_table(priv);
+ if (ret)
+ return ret;
+
+ /* Setup the hashing algorithm */
+ regmap_update_bits(priv->regs, MTK_MAC_REG_ARL_CFG,
+ MTK_MAC_BIT_ARL_CFG_HASH_ALG |
+ MTK_MAC_BIT_ARL_CFG_MISC_MODE, 0);
+
+ /* Don't strip VLAN tags */
+ regmap_update_bits(priv->regs, MTK_MAC_REG_MAC_CFG,
+ MTK_MAC_BIT_MAC_CFG_VLAN_STRIP, 0);
+
+ /* Setup DMA */
+ mtk_mac_dma_init(priv);
+
+ ret = mtk_mac_prepare_rx_skbs(ndev);
+ if (ret)
+ goto err_out;
+
+ /* Request the interrupt */
+ ret = request_irq(ndev->irq, mtk_mac_handle_irq,
+ IRQF_TRIGGER_FALLING, ndev->name, ndev);
+ if (ret)
+ goto err_free_skbs;
+
+ napi_enable(&priv->napi);
+
+ mtk_mac_intr_ack_all(priv);
+ mtk_mac_intr_enable(priv);
+
+ /* Connect to and start PHY */
+ priv->phydev = of_phy_connect(ndev, priv->phy_node,
+ mtk_mac_adjust_link, 0, priv->phy_intf);
+ if (!priv->phydev) {
+ netdev_err(ndev, "failed to connect to PHY\n");
+ goto err_free_irq;
+ }
+
+ mtk_mac_dma_start(priv);
+ phy_start(priv->phydev);
+ netif_start_queue(ndev);
+
+ return 0;
+
+err_free_irq:
+ free_irq(ndev->irq, ndev);
+err_free_skbs:
+ mtk_mac_free_rx_skbs(priv);
+err_out:
+ return ret;
+}
+
+static void mtk_mac_disable(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+
+ netif_stop_queue(ndev);
+ napi_disable(&priv->napi);
+ mtk_mac_intr_disable(priv);
+ mtk_mac_dma_disable(priv);
+ mtk_mac_intr_ack_all(priv);
+ phy_stop(priv->phydev);
+ phy_disconnect(priv->phydev);
+ free_irq(ndev->irq, ndev);
+ mtk_mac_free_rx_skbs(priv);
+ mtk_mac_free_tx_skbs(priv);
+}
+
+static int mtk_mac_netdev_open(struct net_device *ndev)
+{
+ return mtk_mac_enable(ndev);
+}
+
+static int mtk_mac_netdev_stop(struct net_device *ndev)
+{
+ mtk_mac_disable(ndev);
+
+ return 0;
+}
+
+static int mtk_mac_netdev_ioctl(struct net_device *ndev,
+ struct ifreq *req, int cmd)
+{
+ if (!netif_running(ndev))
+ return -EINVAL;
+
+ return phy_mii_ioctl(ndev->phydev, req, cmd);
+}
+
+static int mtk_mac_netdev_start_xmit(struct sk_buff *skb,
+ struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ struct mtk_mac_ring *ring = &priv->tx_ring;
+ struct device *dev = mtk_mac_get_dev(priv);
+ struct mtk_mac_ring_desc_data desc_data;
+
+ desc_data.dma_addr = mtk_mac_dma_map_tx(priv, skb);
+ if (dma_mapping_error(dev, desc_data.dma_addr))
+ goto err_drop_packet;
+
+ desc_data.skb = skb;
+ desc_data.len = skb->len;
+
+ mtk_mac_lock(priv);
+
+ mtk_mac_ring_push_head_tx(ring, &desc_data);
+
+ netdev_sent_queue(ndev, skb->len);
+
+ if (mtk_mac_ring_full(ring))
+ netif_stop_queue(ndev);
+
+ mtk_mac_unlock(priv);
+
+ mtk_mac_dma_resume_tx(priv);
+
+ return NETDEV_TX_OK;
+
+err_drop_packet:
+ dev_kfree_skb(skb);
+ ndev->stats.tx_dropped++;
+ return NETDEV_TX_BUSY;
+}
+
+/* Returns the number of bytes sent or a negative number on the first
+ * descriptor owned by DMA.
+ */
+static int mtk_mac_tx_complete_one(struct mtk_mac_priv *priv)
+{
+ struct mtk_mac_ring *ring = &priv->tx_ring;
+ struct mtk_mac_ring_desc_data desc_data;
+ int ret;
+
+ ret = mtk_mac_ring_pop_tail(ring, &desc_data);
+ if (ret)
+ return ret;
+
+ mtk_mac_dma_unmap_tx(priv, &desc_data);
+ ret = desc_data.skb->len;
+ dev_kfree_skb_irq(desc_data.skb);
+
+ return ret;
+}
+
+static void mtk_mac_tx_complete_all(struct mtk_mac_priv *priv)
+{
+ struct mtk_mac_ring *ring = &priv->tx_ring;
+ struct net_device *ndev = priv->ndev;
+ int ret, pkts_compl, bytes_compl;
+ bool wake = false;
+
+ mtk_mac_lock(priv);
+
+ for (pkts_compl = 0, bytes_compl = 0;;
+ pkts_compl++, bytes_compl += ret, wake = true) {
+ if (!mtk_mac_ring_descs_available(ring))
+ break;
+
+ ret = mtk_mac_tx_complete_one(priv);
+ if (ret < 0)
+ break;
+ }
+
+ netdev_completed_queue(ndev, pkts_compl, bytes_compl);
+
+ if (wake && netif_queue_stopped(ndev))
+ netif_wake_queue(ndev);
+
+ mtk_mac_intr_enable_tx(priv);
+
+ mtk_mac_unlock(priv);
+}
+
+static void mtk_mac_netdev_get_stats64(struct net_device *ndev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+
+ mtk_mac_update_stats(priv);
+
+ memcpy(stats, &priv->stats, sizeof(*stats));
+}
+
+static void mtk_mac_set_rx_mode(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ struct netdev_hw_addr *hw_addr;
+ unsigned int hash_addr, i;
+ int ret;
+
+ if (ndev->flags & IFF_PROMISC) {
+ regmap_update_bits(priv->regs, MTK_MAC_REG_ARL_CFG,
+ MTK_MAC_BIT_ARL_CFG_MISC_MODE,
+ MTK_MAC_BIT_ARL_CFG_MISC_MODE);
+ } else if (netdev_mc_count(ndev) > MTK_MAC_HASHTABLE_MC_LIMIT ||
+ ndev->flags & IFF_ALLMULTI) {
+ for (i = 0; i < MTK_MAC_HASHTABLE_SIZE_MAX; i++) {
+ ret = mtk_mac_set_hashbit(priv, i);
+ if (ret)
+ goto hash_fail;
+ }
+ } else {
+ /* Clear previous settings. */
+ ret = mtk_mac_reset_hash_table(priv);
+ if (ret)
+ goto hash_fail;
+
+ netdev_for_each_mc_addr(hw_addr, ndev) {
+ hash_addr = (hw_addr->addr[0] & 0x01) << 8;
+ hash_addr += hw_addr->addr[5];
+ ret = mtk_mac_set_hashbit(priv, hash_addr);
+ if (ret)
+ goto hash_fail;
+ }
+ }
+
+ return;
+
+hash_fail:
+ if (ret == -ETIMEDOUT)
+ netdev_err(ndev, "setting hash bit timed out\n");
+ else
+ /* Should be -EIO */
+ netdev_err(ndev, "unable to set hash bit");
+}
+
+static const struct net_device_ops mtk_mac_netdev_ops = {
+ .ndo_open = mtk_mac_netdev_open,
+ .ndo_stop = mtk_mac_netdev_stop,
+ .ndo_start_xmit = mtk_mac_netdev_start_xmit,
+ .ndo_get_stats64 = mtk_mac_netdev_get_stats64,
+ .ndo_set_rx_mode = mtk_mac_set_rx_mode,
+ .ndo_do_ioctl = mtk_mac_netdev_ioctl,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static void mtk_mac_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, MTK_MAC_DRVNAME, sizeof(info->driver));
+}
+
+/* TODO Add ethtool stats. */
+static const struct ethtool_ops mtk_mac_ethtool_ops = {
+ .get_drvinfo = mtk_mac_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+ .get_link_ksettings = phy_ethtool_get_link_ksettings,
+ .set_link_ksettings = phy_ethtool_set_link_ksettings,
+};
+
+static int mtk_mac_receive_packet(struct mtk_mac_priv *priv)
+{
+ struct mtk_mac_ring *ring = &priv->rx_ring;
+ struct device *dev = mtk_mac_get_dev(priv);
+ struct mtk_mac_ring_desc_data desc_data;
+ struct net_device *ndev = priv->ndev;
+ struct sk_buff *curr_skb, *new_skb;
+ dma_addr_t new_dma_addr;
+ int ret;
+
+ mtk_mac_lock(priv);
+ ret = mtk_mac_ring_pop_tail(ring, &desc_data);
+ mtk_mac_unlock(priv);
+ if (ret)
+ return -1;
+
+ curr_skb = desc_data.skb;
+
+ if ((desc_data.flags & MTK_MAC_DESC_BIT_RX_CRCE) ||
+ (desc_data.flags & MTK_MAC_DESC_BIT_RX_OSIZE)) {
+ /* Error packet -> drop and reuse skb. */
+ new_skb = curr_skb;
+ goto push_new_skb;
+ }
+
+ /* Prepare new skb before receiving the current one. Reuse the current
+ * skb if we fail at any point.
+ */
+ new_skb = mtk_mac_alloc_skb(ndev);
+ if (!new_skb) {
+ ndev->stats.rx_dropped++;
+ new_skb = curr_skb;
+ goto push_new_skb;
+ }
+
+ new_dma_addr = mtk_mac_dma_map_rx(priv, new_skb);
+ if (dma_mapping_error(dev, new_dma_addr)) {
+ ndev->stats.rx_dropped++;
+ dev_kfree_skb(new_skb);
+ new_skb = curr_skb;
+ netdev_err(ndev, "DMA mapping error of RX descriptor\n");
+ goto push_new_skb;
+ }
+
+ /* We can't fail anymore at this point: it's safe to unmap the skb. */
+ mtk_mac_dma_unmap_rx(priv, &desc_data);
+
+ skb_put(desc_data.skb, desc_data.len);
+ desc_data.skb->ip_summed = CHECKSUM_NONE;
+ desc_data.skb->protocol = eth_type_trans(desc_data.skb, ndev);
+ desc_data.skb->dev = ndev;
+ netif_receive_skb(desc_data.skb);
+
+push_new_skb:
+ desc_data.dma_addr = new_dma_addr;
+ desc_data.len = skb_tailroom(new_skb);
+ desc_data.skb = new_skb;
+
+ mtk_mac_lock(priv);
+ mtk_mac_ring_push_head_rx(ring, &desc_data);
+ mtk_mac_unlock(priv);
+
+ return 0;
+}
+
+static int mtk_mac_process_rx(struct mtk_mac_priv *priv, int budget)
+{
+ int received, ret;
+
+ for (received = 0, ret = 0; received < budget && ret == 0; received++)
+ ret = mtk_mac_receive_packet(priv);
+
+ mtk_mac_dma_resume_rx(priv);
+
+ return received;
+}
+
+static int mtk_mac_poll(struct napi_struct *napi, int budget)
+{
+ struct mtk_mac_priv *priv;
+ int received = 0;
+
+ priv = container_of(napi, struct mtk_mac_priv, napi);
+
+ /* Clean-up all TX descriptors. */
+ mtk_mac_tx_complete_all(priv);
+ /* Receive up to $budget packets. */
+ received = mtk_mac_process_rx(priv, budget);
+
+ if (received < budget) {
+ napi_complete_done(napi, received);
+ mtk_mac_intr_enable_rx(priv);
+ }
+
+ return received;
+}
+
+static void mtk_mac_mdio_rwok_clear(struct mtk_mac_priv *priv)
+{
+ regmap_write(priv->regs, MTK_MAC_REG_PHY_CTRL0,
+ MTK_MAC_BIT_PHY_CTRL0_RWOK);
+}
+
+static int mtk_mac_mdio_rwok_wait(struct mtk_mac_priv *priv)
+{
+ unsigned int val;
+
+ return regmap_read_poll_timeout(priv->regs, MTK_MAC_REG_PHY_CTRL0,
+ val, val & MTK_MAC_BIT_PHY_CTRL0_RWOK,
+ 10, MTK_MAC_WAIT_TIMEOUT);
+}
+
+static int mtk_mac_mdio_read(struct mii_bus *mii, int phy_id, int regnum)
+{
+ struct mtk_mac_priv *priv = mii->priv;
+ unsigned int val, data;
+ int ret;
+
+ if (regnum & MII_ADDR_C45)
+ return -EOPNOTSUPP;
+
+ mtk_mac_mdio_rwok_clear(priv);
+
+ val = (regnum << MTK_MAC_OFF_PHY_CTRL0_PREG);
+ val &= MTK_MAC_MSK_PHY_CTRL0_PREG;
+ val |= MTK_MAC_BIT_PHY_CTRL0_RDCMD;
+
+ regmap_write(priv->regs, MTK_MAC_REG_PHY_CTRL0, val);
+
+ ret = mtk_mac_mdio_rwok_wait(priv);
+ if (ret)
+ return ret;
+
+ regmap_read(priv->regs, MTK_MAC_REG_PHY_CTRL0, &data);
+
+ data &= MTK_MAC_MSK_PHY_CTRL0_RWDATA;
+ data >>= MTK_MAC_OFF_PHY_CTRL0_RWDATA;
+
+ return data;
+}
+
+static int mtk_mac_mdio_write(struct mii_bus *mii, int phy_id,
+ int regnum, u16 data)
+{
+ struct mtk_mac_priv *priv = mii->priv;
+ unsigned int val;
+
+ if (regnum & MII_ADDR_C45)
+ return -EOPNOTSUPP;
+
+ mtk_mac_mdio_rwok_clear(priv);
+
+ val = data;
+ val <<= MTK_MAC_OFF_PHY_CTRL0_RWDATA;
+ val &= MTK_MAC_MSK_PHY_CTRL0_RWDATA;
+ regnum <<= MTK_MAC_OFF_PHY_CTRL0_PREG;
+ regnum &= MTK_MAC_MSK_PHY_CTRL0_PREG;
+ val |= regnum;
+ val |= MTK_MAC_BIT_PHY_CTRL0_WTCMD;
+
+ regmap_write(priv->regs, MTK_MAC_REG_PHY_CTRL0, val);
+
+ return mtk_mac_mdio_rwok_wait(priv);
+}
+
+static int mtk_mac_mdio_init(struct net_device *ndev)
+{
+ struct mtk_mac_priv *priv = netdev_priv(ndev);
+ struct device *dev = mtk_mac_get_dev(priv);
+ struct device_node *of_node, *mdio_node;
+ int ret;
+
+ of_node = dev->of_node;
+
+ mdio_node = of_get_child_by_name(of_node, "mdio");
+ if (!mdio_node)
+ return -ENODEV;
+
+ if (!of_device_is_available(mdio_node)) {
+ ret = -ENODEV;
+ goto out_put_node;
+ }
+
+ priv->mii = devm_mdiobus_alloc(dev);
+ if (!priv->mii) {
+ ret = -ENOMEM;
+ goto out_put_node;
+ }
+
+ snprintf(priv->mii->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
+ priv->mii->name = "mtk-mac-mdio";
+ priv->mii->parent = dev;
+ priv->mii->read = mtk_mac_mdio_read;
+ priv->mii->write = mtk_mac_mdio_write;
+ priv->mii->priv = priv;
+
+ ret = of_mdiobus_register(priv->mii, mdio_node);
+
+out_put_node:
+ of_node_put(mdio_node);
+ return ret;
+}
+
+static int mtk_mac_suspend(struct device *dev)
+{
+ struct mtk_mac_priv *priv;
+ struct net_device *ndev;
+
+ ndev = dev_get_drvdata(dev);
+ priv = netdev_priv(ndev);
+
+ if (netif_running(ndev))
+ mtk_mac_disable(ndev);
+
+ clk_bulk_disable_unprepare(MTK_MAC_NCLKS, priv->clks);
+
+ return 0;
+}
+
+static int mtk_mac_resume(struct device *dev)
+{
+ struct mtk_mac_priv *priv;
+ struct net_device *ndev;
+ int ret;
+
+ ndev = dev_get_drvdata(dev);
+ priv = netdev_priv(ndev);
+
+ ret = clk_bulk_prepare_enable(MTK_MAC_NCLKS, priv->clks);
+ if (ret)
+ return ret;
+
+ if (netif_running(ndev)) {
+ ret = mtk_mac_enable(ndev);
+ if (ret)
+ clk_bulk_disable_unprepare(MTK_MAC_NCLKS, priv->clks);
+ }
+
+ return ret;
+}
+
+static void mtk_mac_clk_disable_unprepare(void *data)
+{
+ struct mtk_mac_priv *priv = data;
+
+ clk_bulk_disable_unprepare(MTK_MAC_NCLKS, priv->clks);
+}
+
+static void mtk_mac_unregister_netdev(void *data)
+{
+ struct net_device *ndev = data;
+
+ unregister_netdev(ndev);
+}
+
+static int mtk_mac_probe(struct platform_device *pdev)
+{
+ struct device_node *of_node;
+ struct mtk_mac_priv *priv;
+ struct net_device *ndev;
+ struct device *dev;
+ void __iomem *base;
+ int ret, i;
+
+ dev = &pdev->dev;
+ of_node = dev->of_node;
+
+ ndev = devm_alloc_etherdev(dev, sizeof(*priv));
+ if (!ndev)
+ return -ENOMEM;
+
+ priv = netdev_priv(ndev);
+ priv->ndev = ndev;
+ SET_NETDEV_DEV(ndev, dev);
+ platform_set_drvdata(pdev, ndev);
+
+ ndev->min_mtu = ETH_ZLEN;
+ ndev->max_mtu = MTK_MAC_MAX_FRAME_SIZE;
+
+ spin_lock_init(&priv->lock);
+ INIT_WORK(&priv->stats_work, mtk_mac_update_stats_work);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ /* We won't be checking the return values of regmap read & write
+ * functions. They can only fail for mmio if there's a clock attached
+ * to regmap which is not the case here.
+ */
+ priv->regs = devm_regmap_init_mmio(dev, base,
+ &mtk_mac_regmap_config);
+ if (IS_ERR(priv->regs))
+ return PTR_ERR(priv->regs);
+
+ priv->pericfg = syscon_regmap_lookup_by_phandle(of_node,
+ "mediatek,pericfg");
+ if (IS_ERR(priv->pericfg)) {
+ dev_err(dev, "Failed to lookup the PERICFG syscon\n");
+ return PTR_ERR(priv->pericfg);
+ }
+
+ ndev->irq = platform_get_irq(pdev, 0);
+ if (ndev->irq < 0)
+ return ndev->irq;
+
+ for (i = 0; i < MTK_MAC_NCLKS; i++)
+ priv->clks[i].id = mtk_mac_clk_names[i];
+ ret = devm_clk_bulk_get(dev, MTK_MAC_NCLKS, priv->clks);
+ if (ret)
+ return ret;
+
+ ret = clk_bulk_prepare_enable(MTK_MAC_NCLKS, priv->clks);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev,
+ mtk_mac_clk_disable_unprepare, priv);
+ if (ret)
+ return ret;
+
+ ret = of_get_phy_mode(of_node, &priv->phy_intf);
+ if (ret) {
+ return ret;
+ } else if (priv->phy_intf != PHY_INTERFACE_MODE_RMII) {
+ dev_err(dev, "unsupported phy mode: %s\n",
+ phy_modes(priv->phy_intf));
+ return -EINVAL;
+ }
+
+ priv->phy_node = of_parse_phandle(of_node, "phy-handle", 0);
+ if (!priv->phy_node) {
+ dev_err(dev, "failed to retrieve the phy handle from device tree\n");
+ return -ENODEV;
+ }
+
+ mtk_mac_set_mode_rmii(priv);
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(dev, "unsupported DMA mask\n");
+ return ret;
+ }
+
+ priv->ring_base = dmam_alloc_coherent(dev, MTK_MAC_DMA_SIZE,
+ &priv->dma_addr,
+ GFP_KERNEL | GFP_DMA);
+ if (!priv->ring_base)
+ return -ENOMEM;
+
+ mtk_mac_nic_disable_pd(priv);
+ mtk_mac_init_config(priv);
+
+ ret = mtk_mac_mdio_init(ndev);
+ if (ret)
+ return ret;
+
+ ret = eth_platform_get_mac_address(dev, ndev->dev_addr);
+ if (ret || !is_valid_ether_addr(ndev->dev_addr))
+ eth_hw_addr_random(ndev);
+
+ ndev->netdev_ops = &mtk_mac_netdev_ops;
+ ndev->ethtool_ops = &mtk_mac_ethtool_ops;
+
+ netif_napi_add(ndev, &priv->napi, mtk_mac_poll, MTK_MAC_NAPI_WEIGHT);
+
+ ret = register_netdev(ndev);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, mtk_mac_unregister_netdev, ndev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct of_device_id mtk_mac_of_match[] = {
+ { .compatible = "mediatek,mt8516-eth", },
+ { .compatible = "mediatek,mt8518-eth", },
+ { .compatible = "mediatek,mt8175-eth", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mtk_mac_of_match);
+
+static SIMPLE_DEV_PM_OPS(mtk_mac_pm_ops,
+ mtk_mac_suspend, mtk_mac_resume);
+
+static struct platform_driver mtk_mac_driver = {
+ .driver = {
+ .name = MTK_MAC_DRVNAME,
+ .pm = &mtk_mac_pm_ops,
+ .of_match_table = of_match_ptr(mtk_mac_of_match),
+ },
+ .probe = mtk_mac_probe,
+};
+module_platform_driver(mtk_mac_driver);
+
+MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>");
+MODULE_DESCRIPTION("Mediatek Ethernet MAC Driver");
+MODULE_LICENSE("GPL");
--
2.25.0
^ permalink raw reply related
* [PATCH v4 10/11] ARM64: dts: mediatek: add ethernet pins for pumpkin boards
From: Bartosz Golaszewski @ 2020-05-20 11:25 UTC (permalink / raw)
To: Rob Herring, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200520112523.30995-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Setup the pin control for the Ethernet MAC.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
index 97d9b000c37e..4b1d5f69aba6 100644
--- a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
+++ b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
@@ -219,4 +219,19 @@ gpio_mux_int_n_pin {
bias-pull-up;
};
};
+
+ ethernet_pins_default: ethernet {
+ pins_ethernet {
+ pinmux = <MT8516_PIN_0_EINT0__FUNC_EXT_TXD0>,
+ <MT8516_PIN_1_EINT1__FUNC_EXT_TXD1>,
+ <MT8516_PIN_5_EINT5__FUNC_EXT_RXER>,
+ <MT8516_PIN_6_EINT6__FUNC_EXT_RXC>,
+ <MT8516_PIN_7_EINT7__FUNC_EXT_RXDV>,
+ <MT8516_PIN_8_EINT8__FUNC_EXT_RXD0>,
+ <MT8516_PIN_9_EINT9__FUNC_EXT_RXD1>,
+ <MT8516_PIN_12_EINT12__FUNC_EXT_TXEN>,
+ <MT8516_PIN_38_MRG_DI__FUNC_EXT_MDIO>,
+ <MT8516_PIN_39_MRG_DO__FUNC_EXT_MDC>;
+ };
+ };
};
--
2.25.0
^ permalink raw reply related
* [PATCH v4 09/11] ARM64: dts: mediatek: add an alias for ethernet0 for pumpkin boards
From: Bartosz Golaszewski @ 2020-05-20 11:25 UTC (permalink / raw)
To: Rob Herring, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200520112523.30995-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add the ethernet0 alias for ethernet so that u-boot can find this node
and fill in the MAC address.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
index a31093d7142b..97d9b000c37e 100644
--- a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
+++ b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
@@ -9,6 +9,7 @@
/ {
aliases {
serial0 = &uart0;
+ ethernet0 = ðernet;
};
chosen {
--
2.25.0
^ permalink raw reply related
* [PATCH v4 11/11] ARM64: dts: mediatek: enable ethernet on pumpkin boards
From: Bartosz Golaszewski @ 2020-05-20 11:25 UTC (permalink / raw)
To: Rob Herring, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200520112523.30995-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add remaining properties to the ethernet node and enable it.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../boot/dts/mediatek/pumpkin-common.dtsi | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
index 4b1d5f69aba6..dfceffe6950a 100644
--- a/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
+++ b/arch/arm64/boot/dts/mediatek/pumpkin-common.dtsi
@@ -167,6 +167,24 @@ &uart0 {
status = "okay";
};
+ðernet {
+ pinctrl-names = "default";
+ pinctrl-0 = <ðernet_pins_default>;
+ phy-handle = <ð_phy>;
+ phy-mode = "rmii";
+ mac-address = [00 00 00 00 00 00];
+ status = "okay";
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eth_phy: ethernet-phy@0 {
+ reg = <0>;
+ };
+ };
+};
+
&usb0 {
status = "okay";
dr_mode = "peripheral";
--
2.25.0
^ permalink raw reply related
* [PATCH v4 08/11] ARM64: dts: mediatek: add the ethernet node to mt8516.dtsi
From: Bartosz Golaszewski @ 2020-05-20 11:25 UTC (permalink / raw)
To: Rob Herring, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200520112523.30995-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Add the Ethernet MAC node to mt8516.dtsi. This defines parameters common
to all the boards based on this SoC.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
arch/arm64/boot/dts/mediatek/mt8516.dtsi | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8516.dtsi b/arch/arm64/boot/dts/mediatek/mt8516.dtsi
index 8cedaf74ae86..89af661e7f63 100644
--- a/arch/arm64/boot/dts/mediatek/mt8516.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8516.dtsi
@@ -406,6 +406,18 @@ mmc2: mmc@11170000 {
status = "disabled";
};
+ ethernet: ethernet@11180000 {
+ compatible = "mediatek,mt8516-eth";
+ reg = <0 0x11180000 0 0x1000>;
+ mediatek,pericfg = <&pericfg>;
+ interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&topckgen CLK_TOP_RG_ETH>,
+ <&topckgen CLK_TOP_66M_ETH>,
+ <&topckgen CLK_TOP_133M_ETH>;
+ clock-names = "core", "reg", "trans";
+ status = "disabled";
+ };
+
rng: rng@1020c000 {
compatible = "mediatek,mt8516-rng",
"mediatek,mt7623-rng";
--
2.25.0
^ permalink raw reply related
* [PATCH v4 01/11] dt-bindings: convert the binding document for mediatek PERICFG to yaml
From: Bartosz Golaszewski @ 2020-05-20 11:25 UTC (permalink / raw)
To: Rob Herring, David S . Miller, Matthias Brugger, John Crispin,
Sean Wang, Mark Lee, Jakub Kicinski, Arnd Bergmann, Fabien Parent,
Heiner Kallweit, Edwin Peer
Cc: devicetree, linux-kernel, netdev, linux-arm-kernel,
linux-mediatek, Stephane Le Provost, Pedro Tsai, Andrew Perepech,
Bartosz Golaszewski
In-Reply-To: <20200520112523.30995-1-brgl@bgdev.pl>
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Convert the DT binding .txt file for MediaTek's peripheral configuration
controller to YAML. There's one special case where the compatible has
three positions. Otherwise, it's a pretty normal syscon.
Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
.../arm/mediatek/mediatek,pericfg.txt | 36 -----------
.../arm/mediatek/mediatek,pericfg.yaml | 63 +++++++++++++++++++
2 files changed, 63 insertions(+), 36 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
deleted file mode 100644
index ecf027a9003a..000000000000
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-Mediatek pericfg controller
-===========================
-
-The Mediatek pericfg controller provides various clocks and reset
-outputs to the system.
-
-Required Properties:
-
-- compatible: Should be one of:
- - "mediatek,mt2701-pericfg", "syscon"
- - "mediatek,mt2712-pericfg", "syscon"
- - "mediatek,mt7622-pericfg", "syscon"
- - "mediatek,mt7623-pericfg", "mediatek,mt2701-pericfg", "syscon"
- - "mediatek,mt7629-pericfg", "syscon"
- - "mediatek,mt8135-pericfg", "syscon"
- - "mediatek,mt8173-pericfg", "syscon"
- - "mediatek,mt8183-pericfg", "syscon"
-- #clock-cells: Must be 1
-- #reset-cells: Must be 1
-
-The pericfg controller uses the common clk binding from
-Documentation/devicetree/bindings/clock/clock-bindings.txt
-The available clocks are defined in dt-bindings/clock/mt*-clk.h.
-Also it uses the common reset controller binding from
-Documentation/devicetree/bindings/reset/reset.txt.
-The available reset outputs are defined in
-dt-bindings/reset/mt*-resets.h
-
-Example:
-
-pericfg: power-controller@10003000 {
- compatible = "mediatek,mt8173-pericfg", "syscon";
- reg = <0 0x10003000 0 0x1000>;
- #clock-cells = <1>;
- #reset-cells = <1>;
-};
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml
new file mode 100644
index 000000000000..1340c6288024
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/arm/mediatek/mediatek,pericfg.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: MediaTek Peripheral Configuration Controller
+
+maintainers:
+ - Bartosz Golaszewski <bgolaszewski@baylibre.com>
+
+description:
+ The Mediatek pericfg controller provides various clocks and reset outputs
+ to the system.
+
+properties:
+ compatible:
+ oneOf:
+ - items:
+ - enum:
+ - mediatek,mt2701-pericfg
+ - mediatek,mt2712-pericfg
+ - mediatek,mt7622-pericfg
+ - mediatek,mt7629-pericfg
+ - mediatek,mt8135-pericfg
+ - mediatek,mt8173-pericfg
+ - mediatek,mt8183-pericfg
+ - const: syscon
+ - items:
+ # Special case for mt7623 for backward compatibility
+ - const: mediatek,mt7623-pericfg
+ - const: mediatek,mt2701-pericfg
+ - const: syscon
+
+ reg:
+ maxItems: 1
+
+ '#clock-cells':
+ const: 1
+
+ '#reset-cells':
+ const: 1
+
+required:
+ - compatible
+ - reg
+
+examples:
+ - |
+ pericfg@10003000 {
+ compatible = "mediatek,mt8173-pericfg", "syscon";
+ reg = <0x10003000 0x1000>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ - |
+ pericfg@10003000 {
+ compatible = "mediatek,mt7623-pericfg", "mediatek,mt2701-pericfg", "syscon";
+ reg = <0x10003000 0x1000>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
--
2.25.0
^ permalink raw reply related
* Re: [PATCH V2] arm64: tegra: Fix flag for 64-bit resources in 'ranges' property
From: Thierry Reding @ 2020-05-20 11:24 UTC (permalink / raw)
To: Vidya Sagar
Cc: robh+dt, jonathanh, Lorenzo Pieralisi, amurray, bhelgaas,
linux-kernel, kthota, mmaddireddy, sagar.tv, devicetree,
linux-tegra
In-Reply-To: <e64ccace-d2b9-0e03-db3f-e65ed6f56230@nvidia.com>
[-- Attachment #1: Type: text/plain, Size: 354 bytes --]
On Mon, May 18, 2020 at 07:52:24PM +0530, Vidya Sagar wrote:
> Thanks Lorenzo.
> I've moved linux-pci to BCC and included
> devicetree and linux-tegra mailing lists to CC.
>
> Rob, Could you please review this patch?
I don't think there's any need for Rob to review this. It's a simple
bugfix and I've applied it for v5.8.
Thanks,
Thierry
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* Re: [PATCH V1 3/3] mmc: sdhci: Allow platform controlled voltage switching
From: Veerabhadrarao Badiganti @ 2020-05-20 11:19 UTC (permalink / raw)
To: Adrian Hunter, ulf.hansson, robh+dt
Cc: linux-mmc, linux-kernel, linux-arm-msm, devicetree,
Vijay Viswanath
In-Reply-To: <480cc8ee-27ae-2538-68d6-c382dbaca6bb@intel.com>
On 5/19/2020 11:36 AM, Adrian Hunter wrote:
> On 15/05/20 2:18 pm, Veerabhadrarao Badiganti wrote:
>> From: Vijay Viswanath <vviswana@codeaurora.org>
>>
>> If vendor platform drivers are controlling whole logic of voltage
>> switching, then sdhci driver no need control vqmmc regulator.
>> So skip enabling/disable vqmmc from SDHC driver.
>>
>> Signed-off-by: Vijay Viswanath <vviswana@codeaurora.org>
>> Signed-off-by: Veerabhadrarao Badiganti <vbadigan@codeaurora.org>
>> ---
>> drivers/mmc/host/sdhci.c | 32 +++++++++++++++++++-------------
>> drivers/mmc/host/sdhci.h | 1 +
>> 2 files changed, 20 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>> index 1bb6b67..c010823 100644
>> --- a/drivers/mmc/host/sdhci.c
>> +++ b/drivers/mmc/host/sdhci.c
>> @@ -4098,6 +4098,7 @@ int sdhci_setup_host(struct sdhci_host *host)
>> unsigned int override_timeout_clk;
>> u32 max_clk;
>> int ret;
>> + bool enable_vqmmc = false;
>>
>> WARN_ON(host == NULL);
>> if (host == NULL)
>> @@ -4111,9 +4112,12 @@ int sdhci_setup_host(struct sdhci_host *host)
>> * the host can take the appropriate action if regulators are not
>> * available.
>> */
>> - ret = mmc_regulator_get_supply(mmc);
>> - if (ret)
>> - return ret;
>> + if (!mmc->supply.vqmmc) {
>> + ret = mmc_regulator_get_supply(mmc);
>> + if (ret)
>> + return ret;
>> + enable_vqmmc = true;
>> + }
>>
>> DBG("Version: 0x%08x | Present: 0x%08x\n",
>> sdhci_readw(host, SDHCI_HOST_VERSION),
>> @@ -4373,7 +4377,15 @@ int sdhci_setup_host(struct sdhci_host *host)
>> mmc->caps |= MMC_CAP_NEEDS_POLL;
>>
>> if (!IS_ERR(mmc->supply.vqmmc)) {
>> - ret = regulator_enable(mmc->supply.vqmmc);
>> + if (enable_vqmmc) {
>> + ret = regulator_enable(mmc->supply.vqmmc);
>> + if (ret) {
>> + pr_warn("%s: Failed to enable vqmmc regulator: %d\n",
>> + mmc_hostname(mmc), ret);
>> + mmc->supply.vqmmc = ERR_PTR(-EINVAL);
>> + }
>> + host->vqmmc_enabled = !ret;
>> + }
>>
>> /* If vqmmc provides no 1.8V signalling, then there's no UHS */
>> if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000,
>> @@ -4386,12 +4398,6 @@ int sdhci_setup_host(struct sdhci_host *host)
>> if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 2700000,
>> 3600000))
>> host->flags &= ~SDHCI_SIGNALING_330;
>> -
>> - if (ret) {
>> - pr_warn("%s: Failed to enable vqmmc regulator: %d\n",
>> - mmc_hostname(mmc), ret);
>> - mmc->supply.vqmmc = ERR_PTR(-EINVAL);
>> - }
>> }
>>
>> if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V) {
>> @@ -4625,7 +4631,7 @@ int sdhci_setup_host(struct sdhci_host *host)
>> return 0;
>>
>> unreg:
>> - if (!IS_ERR(mmc->supply.vqmmc))
>> + if (host->vqmmc_enabled)
>> regulator_disable(mmc->supply.vqmmc);
>> undma:
>> if (host->align_buffer)
>> @@ -4643,7 +4649,7 @@ void sdhci_cleanup_host(struct sdhci_host *host)
>> {
>> struct mmc_host *mmc = host->mmc;
>>
>> - if (!IS_ERR(mmc->supply.vqmmc))
>> + if (host->vqmmc_enabled)
>> regulator_disable(mmc->supply.vqmmc);
>>
>> if (host->align_buffer)
>> @@ -4780,7 +4786,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
>>
>> destroy_workqueue(host->complete_wq);
>>
>> - if (!IS_ERR(mmc->supply.vqmmc))
>> + if (host->vqmmc_enabled)
>> regulator_disable(mmc->supply.vqmmc);
>>
>> if (host->align_buffer)
>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
>> index 8d2a096..24d27e1 100644
>> --- a/drivers/mmc/host/sdhci.h
>> +++ b/drivers/mmc/host/sdhci.h
>> @@ -570,6 +570,7 @@ struct sdhci_host {
>> u32 caps1; /* CAPABILITY_1 */
>> bool read_caps; /* Capability flags have been read */
>>
>> + bool vqmmc_enabled; /* Vqmmc is enabled */
> Last time around there was dissatisfaction with this variable name. Perhaps
> change it to sdhci_core_to_disable_vqmmc
Sure Adrian. Will update this variable name.
>
>> unsigned int ocr_avail_sdio; /* OCR bit masks */
>> unsigned int ocr_avail_sd;
>> unsigned int ocr_avail_mmc;
>>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox