mbox series

[net-next,v2,00/10] Adding the Sparx5 Switch Driver

Message ID 20210528123419.1142290-1-steen.hegelund@microchip.com (mailing list archive)
Headers show
Series Adding the Sparx5 Switch Driver | expand

Message

Steen Hegelund May 28, 2021, 12:34 p.m. UTC
This series provides the Microchip Sparx5 Switch Driver

The Sparx5 Carrier Ethernet and Industrial switch family delivers 64
Ethernet ports and up to 200 Gbps of switching bandwidth.

It provides a rich set of Ethernet switching features such as hierarchical
QoS, hardware-based OAM  and service activation testing, protection
switching, IEEE 1588, and Synchronous Ethernet.

Using provider bridging (Q-in-Q) and MPLS/MPLS-TP technology, it delivers
MEF CE
2.0 Ethernet virtual connections (EVCs) and features advanced TCAM
  classification in both ingress and egress.

Per-EVC features include advanced L3-aware classification, a rich set of
statistics, OAM for end-to-end performance monitoring, and dual-rate
policing and shaping.

Time sensitive networking (TSN) is supported through a comprehensive set of
features including frame preemption, cut-through, frame replication and
elimination for reliability, enhanced scheduling: credit-based shaping,
time-aware shaping, cyclic queuing, and forwarding, and per-stream policing
and filtering.

Together with IEEE 1588 and IEEE 802.1AS support, this guarantees
low-latency deterministic networking for Fronthaul, Carrier, and Industrial
Ethernet.

The Sparx5 switch family consists of following SKUs:

- VSC7546 Sparx5-64 up to 64 Gbps of bandwidth with the following primary
  port configurations:
  - 6 *10G
  - 16 * 2.5G + 2 * 10G
  - 24 * 1G + 4 * 10G

- VSC7549 Sparx5-90 up to 90 Gbps of bandwidth with the following primary
  port configurations:
  - 9 * 10G
  - 16 * 2.5G + 4 * 10G
  - 48 * 1G + 4 * 10G

- VSC7552 Sparx5-128 up to 128 Gbps of bandwidth with the following primary
  port configurations:
  - 12 * 10G
  - 16 * 2.5G + 8 * 10G
  - 48 * 1G + 8 * 10G

- VSC7556 Sparx5-160 up to 160 Gbps of bandwidth with the following primary
  port configurations:
  - 16 * 10G
  - 10 * 10G + 2 * 25G
  - 16 * 2.5G + 10 * 10G
  - 48 * 1G + 10 * 10G

- VSC7558 Sparx5-200 up to 200 Gbps of bandwidth with the following primary
  port configurations:
  - 20 * 10G
  - 8 * 25G

In addition, the device supports one 10/100/1000/2500/5000 Mbps
SGMII/SerDes node processor interface (NPI) Ethernet port.

The Sparx5 support is developed on the PCB134 and PCB135 evaluation boards.

- PCB134 main networking features:
  - 12x SFP+ front 10G module slots (connected to Sparx5 through SFI).
  - 8x SFP28 front 25G module slots (connected to Sparx5 through SFI high
    speed).
  - Optional, one additional 10/100/1000BASE-T (RJ45) Ethernet port
    (on-board VSC8211 PHY connected to Sparx5 through SGMII).

- PCB135 main networking features:
  - 48x1G (10/100/1000M) RJ45 front ports using 12xVSC8514 QuadPHY’s each
    connected to VSC7558 through QSGMII.
  - 4x10G (1G/2.5G/5G/10G) RJ45 front ports using the AQR407 10G QuadPHY
    each port connects to VSC7558 through SFI.
  - 4x SFP28 25G module slots on back connected to VSC7558 through SFI high
    speed.
  - Optional, one additional 1G (10/100/1000M) RJ45 port using an on-board
    VSC8211 PHY, which can be connected to VSC7558 NPI port through SGMII
    using a loopback add-on PCB)

This series provides support for:
  - SFPs and DAC cables via PHYLINK with a number of 5G, 10G and 25G
    devices and media types.
  - Port module configuration for 10M to 25G speeds with SGMII, QSGMII,
    1000BASEX, 2500BASEX and 10GBASER as appropriate for these modes.
  - SerDes configuration via the Sparx5 SerDes driver (see below).
  - Host mode providing register based injection and extraction.
  - Switch mode providing MAC/VLAN table learning and Layer2 switching
    offloaded to the Sparx5 switch.
  - STP state, VLAN support, host/bridge port mode, Forwarding DB, and
    configuration and statistics via ethtool.

More support will be added at a later stage.

The Sparx5 Chip Register Model can be browsed at this location:
https://github.com/microchip-ung/sparx-5_reginfo
and the datasheet is available here:
https://ww1.microchip.com/downloads/en/DeviceDoc/SparX-5_Family_L2L3_Enterprise_10G_Ethernet_Switches_Datasheet_00003822B.pdf

The series depends on the following series currently on its way
into the kernel:

- Sparx5 Reset Driver
  Link: https://lore.kernel.org/r/20210416084054.2922327-1-steen.hegelund@microchip.com/

ChangeLog:
    v2:
        - Updated bindings:
            - drop minItems for the reg property
        - Statistics implementation:
            - Reorganized statistics into ethtool groups:
                eth-phy, eth-mac, eth-ctrl, rmon
              as defined by the IEEE 802.3 categories and RFC 2819.
            - The remaining statistics are provided by the classic ethtool
              statistics command.
        - Hostmode support:
            - Removed netdev renaming
            - Validate ethernet address in sparx5_set_mac_address()

Steen Hegelund (10):
  dt-bindings: net: sparx5: Add sparx5-switch bindings
  net: sparx5: add the basic sparx5 driver
  net: sparx5: add hostmode with phylink support
  net: sparx5: add port module support
  net: sparx5: add mactable support
  net: sparx5: add vlan support
  net: sparx5: add switching support
  net: sparx5: add calendar bandwidth allocation support
  net: sparx5: add ethtool configuration and statistics support
  arm64: dts: sparx5: Add the Sparx5 switch node

 .../bindings/net/microchip,sparx5-switch.yaml |  226 +
 arch/arm64/boot/dts/microchip/sparx5.dtsi     |   94 +-
 .../dts/microchip/sparx5_pcb134_board.dtsi    |  481 +-
 .../dts/microchip/sparx5_pcb135_board.dtsi    |  621 ++-
 drivers/net/ethernet/microchip/Kconfig        |    2 +
 drivers/net/ethernet/microchip/Makefile       |    2 +
 drivers/net/ethernet/microchip/sparx5/Kconfig |    9 +
 .../net/ethernet/microchip/sparx5/Makefile    |   10 +
 .../microchip/sparx5/sparx5_calendar.c        |  596 +++
 .../microchip/sparx5/sparx5_ethtool.c         | 1227 +++++
 .../microchip/sparx5/sparx5_mactable.c        |  500 ++
 .../ethernet/microchip/sparx5/sparx5_main.c   |  867 +++
 .../ethernet/microchip/sparx5/sparx5_main.h   |  365 ++
 .../microchip/sparx5/sparx5_main_regs.h       | 4642 +++++++++++++++++
 .../ethernet/microchip/sparx5/sparx5_netdev.c |  249 +
 .../ethernet/microchip/sparx5/sparx5_packet.c |  292 ++
 .../microchip/sparx5/sparx5_phylink.c         |  195 +
 .../ethernet/microchip/sparx5/sparx5_port.c   | 1129 ++++
 .../ethernet/microchip/sparx5/sparx5_port.h   |   98 +
 .../microchip/sparx5/sparx5_switchdev.c       |  508 ++
 .../ethernet/microchip/sparx5/sparx5_vlan.c   |  224 +
 21 files changed, 12253 insertions(+), 84 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
 create mode 100644 drivers/net/ethernet/microchip/sparx5/Kconfig
 create mode 100644 drivers/net/ethernet/microchip/sparx5/Makefile
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_main.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_main.h
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_main_regs.h
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_packet.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_port.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_port.h
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c
 create mode 100644 drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c

--
2.31.1

Comments

Jakub Kicinski May 30, 2021, 8:59 p.m. UTC | #1
On Fri, 28 May 2021 14:34:11 +0200 Steen Hegelund wrote:
> This adds the Sparx5 basic SwitchDev driver framework with IO range
> mapping, switch device detection and core clock configuration.
> 
> Support for ports, phylink, netdev, mactable etc. are in the following
> patches.

> +	for (idx = 0; idx < 3; idx++) {
> +		spx5_rmw(GCB_SIO_CLOCK_SYS_CLK_PERIOD_SET(clk_period / 100),
> +			 GCB_SIO_CLOCK_SYS_CLK_PERIOD,
> +			 sparx5,
> +			 GCB_SIO_CLOCK(idx));
> +	}

braces unnecessary, please fix everywhere.

> +
> +	spx5_rmw(HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY_SET
> +		 ((256 * 1000) / clk_period),
> +		 HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY,
> +		 sparx5,
> +		 HSCH_TAS_STATEMACHINE_CFG);
> +
> +	spx5_rmw(ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT_SET(pol_upd_int),
> +		 ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT,
> +		 sparx5,
> +		 ANA_AC_POL_POL_UPD_INT_CFG);
> +
> +	return 0;
> +}

> +	/* Default values, some from DT */
> +	sparx5->coreclock = SPX5_CORE_CLOCK_DEFAULT;
> +
> +	ports = of_get_child_by_name(np, "ethernet-ports");

Don't you need to release the reference you got on @ports?

> +	if (!ports) {
> +		dev_err(sparx5->dev, "no ethernet-ports child node found\n");
> +		return -ENODEV;
> +	}
> +	sparx5->port_count = of_get_child_count(ports);
> +
> +	configs = kcalloc(sparx5->port_count,
> +			  sizeof(struct initial_port_config), GFP_KERNEL);
> +	if (!configs)
> +		return -ENOMEM;
> +
> +	for_each_available_child_of_node(ports, portnp) {
> +		struct sparx5_port_config *conf;
> +		struct phy *serdes;
> +		u32 portno;
> +
> +		err = of_property_read_u32(portnp, "reg", &portno);
> +		if (err) {
> +			dev_err(sparx5->dev, "port reg property error\n");
> +			continue;
> +		}
> +		config = &configs[idx];
> +		conf = &config->conf;
> +		err = of_get_phy_mode(portnp, &conf->phy_mode);
> +		if (err) {
> +			dev_err(sparx5->dev, "port %u: missing phy-mode\n",
> +				portno);
> +			continue;
> +		}
> +		err = of_property_read_u32(portnp, "microchip,bandwidth",
> +					   &conf->bandwidth);
> +		if (err) {
> +			dev_err(sparx5->dev, "port %u: missing bandwidth\n",
> +				portno);
> +			continue;
> +		}
> +		err = of_property_read_u32(portnp, "microchip,sd-sgpio", &conf->sd_sgpio);
> +		if (err)
> +			conf->sd_sgpio = ~0;
> +		else
> +			sparx5->sd_sgpio_remapping = true;
> +		serdes = devm_of_phy_get(sparx5->dev, portnp, NULL);
> +		if (IS_ERR(serdes)) {
> +			err = PTR_ERR(serdes);
> +			if (err != -EPROBE_DEFER)
> +				dev_err(sparx5->dev,
> +					"port %u: missing serdes\n",
> +					portno);

dev_err_probe()

> +			goto cleanup_config;
> +		}
> +		config->portno = portno;
> +		config->node = portnp;
> +		config->serdes = serdes;
> +
> +		conf->media = PHY_MEDIA_DAC;
> +		conf->serdes_reset = true;
> +		conf->portmode = conf->phy_mode;
> +		if (of_find_property(portnp, "sfp", NULL)) {
> +			conf->has_sfp = true;
> +			conf->power_down = true;
> +		}
> +		idx++;
> +	}
> +
> +	err = sparx5_create_targets(sparx5);
> +	if (err)
> +		goto cleanup_config;
> +
> +	if (of_get_mac_address(np, mac_addr)) {
> +		dev_info(sparx5->dev, "MAC addr was not set, use random MAC\n");
> +		eth_random_addr(sparx5->base_mac);
> +		sparx5->base_mac[5] = 0;
> +	} else {
> +		ether_addr_copy(sparx5->base_mac, mac_addr);
> +	}
> +
> +	/* Inj/Xtr IRQ support to be added in later patches */
> +	/* Read chip ID to check CPU interface */
> +	sparx5->chip_id = spx5_rd(sparx5, GCB_CHIP_ID);
> +
> +	sparx5->target_ct = (enum spx5_target_chiptype)
> +		GCB_CHIP_ID_PART_ID_GET(sparx5->chip_id);
> +
> +	/* Initialize Switchcore and internal RAMs */
> +	if (sparx5_init_switchcore(sparx5)) {
> +		dev_err(sparx5->dev, "Switchcore initialization error\n");
> +		goto cleanup_config;

Should @err be set?

> +	}
> +
> +	/* Initialize the LC-PLL (core clock) and set affected registers */
> +	if (sparx5_init_coreclock(sparx5)) {
> +		dev_err(sparx5->dev, "LC-PLL initialization error\n");
> +		goto cleanup_config;

ditto

> +	}
> +
> +	for (idx = 0; idx < sparx5->port_count; ++idx) {
> +		config = &configs[idx];
> +		if (!config->node)
> +			continue;
> +
> +		err = sparx5_create_port(sparx5, config);
> +		if (err) {
> +			dev_err(sparx5->dev, "port create error\n");
> +			goto cleanup_ports;
> +		}
> +	}
> +
> +	if (sparx5_start(sparx5)) {
> +		dev_err(sparx5->dev, "Start failed\n");
> +		goto cleanup_ports;

and here

> +	}
> +
> +	kfree(configs);
> +	return err;
> +
> +cleanup_ports:
> +	/* Port cleanup to be added in later patches */
> +cleanup_config:
> +	kfree(configs);
> +	return err;
> +}

> +struct sparx5_port_config      {

Spurious tab before {?

> +	phy_interface_t portmode;
> +	bool has_sfp;
> +	u32 bandwidth;
> +	int speed;
> +	int duplex;
> +	enum phy_media media;
> +	bool power_down;
> +	bool autoneg;
> +	u32 pause;
> +	bool serdes_reset;

Group all 4 bools together for better packing?

> +	phy_interface_t phy_mode;
> +	u32 sd_sgpio;
> +};

> +static inline void spx5_rmw(u32 val, u32 mask, struct sparx5 *sparx5,
> +			    int id, int tinst, int tcnt,
> +			    int gbase, int ginst, int gcnt, int gwidth,
> +			    int raddr, int rinst, int rcnt, int rwidth)
> +{
> +	u32 nval;
> +	void __iomem *addr =
> +		spx5_addr(sparx5->regs, id, tinst, tcnt,

Why try to initialize inline when it results in weird looking code and
no saved lines?

> +			  gbase, ginst, gcnt, gwidth,
> +			  raddr, rinst, rcnt, rwidth);

Not to mention that you end up with no new line after variable
declaration.

> +	nval = readl(addr);
> +	nval = (nval & ~mask) | (val & mask);
> +	writel(nval, addr);
> +}
Steen Hegelund May 31, 2021, 1:38 p.m. UTC | #2
Hi Jacub,

Thanks for your comments.

On Sun, 2021-05-30 at 13:59 -0700, Jakub Kicinski wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> On Fri, 28 May 2021 14:34:11 +0200 Steen Hegelund wrote:
> > This adds the Sparx5 basic SwitchDev driver framework with IO range
> > mapping, switch device detection and core clock configuration.
> > 
> > Support for ports, phylink, netdev, mactable etc. are in the following
> > patches.
> 
> > +     for (idx = 0; idx < 3; idx++) {
> > +             spx5_rmw(GCB_SIO_CLOCK_SYS_CLK_PERIOD_SET(clk_period / 100),
> > +                      GCB_SIO_CLOCK_SYS_CLK_PERIOD,
> > +                      sparx5,
> > +                      GCB_SIO_CLOCK(idx));
> > +     }
> 
> braces unnecessary, please fix everywhere.

Will do that.

> 
> > +
> > +     spx5_rmw(HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY_SET
> > +              ((256 * 1000) / clk_period),
> > +              HSCH_TAS_STATEMACHINE_CFG_REVISIT_DLY,
> > +              sparx5,
> > +              HSCH_TAS_STATEMACHINE_CFG);
> > +
> > +     spx5_rmw(ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT_SET(pol_upd_int),
> > +              ANA_AC_POL_POL_UPD_INT_CFG_POL_UPD_INT,
> > +              sparx5,
> > +              ANA_AC_POL_POL_UPD_INT_CFG);
> > +
> > +     return 0;
> > +}
> 
> > +     /* Default values, some from DT */
> > +     sparx5->coreclock = SPX5_CORE_CLOCK_DEFAULT;
> > +
> > +     ports = of_get_child_by_name(np, "ethernet-ports");
> 
> Don't you need to release the reference you got on @ports?

Yes that is missing.  I will update.

> 
> > +     if (!ports) {
> > +             dev_err(sparx5->dev, "no ethernet-ports child node found\n");
> > +             return -ENODEV;
> > +     }
> > +     sparx5->port_count = of_get_child_count(ports);
> > +
> > +     configs = kcalloc(sparx5->port_count,
> > +                       sizeof(struct initial_port_config), GFP_KERNEL);
> > +     if (!configs)
> > +             return -ENOMEM;
> > +
> > +     for_each_available_child_of_node(ports, portnp) {
> > +             struct sparx5_port_config *conf;
> > +             struct phy *serdes;
> > +             u32 portno;
> > +
> > +             err = of_property_read_u32(portnp, "reg", &portno);
> > +             if (err) {
> > +                     dev_err(sparx5->dev, "port reg property error\n");
> > +                     continue;
> > +             }
> > +             config = &configs[idx];
> > +             conf = &config->conf;
> > +             err = of_get_phy_mode(portnp, &conf->phy_mode);
> > +             if (err) {
> > +                     dev_err(sparx5->dev, "port %u: missing phy-mode\n",
> > +                             portno);
> > +                     continue;
> > +             }
> > +             err = of_property_read_u32(portnp, "microchip,bandwidth",
> > +                                        &conf->bandwidth);
> > +             if (err) {
> > +                     dev_err(sparx5->dev, "port %u: missing bandwidth\n",
> > +                             portno);
> > +                     continue;
> > +             }
> > +             err = of_property_read_u32(portnp, "microchip,sd-sgpio", &conf->sd_sgpio);
> > +             if (err)
> > +                     conf->sd_sgpio = ~0;
> > +             else
> > +                     sparx5->sd_sgpio_remapping = true;
> > +             serdes = devm_of_phy_get(sparx5->dev, portnp, NULL);
> > +             if (IS_ERR(serdes)) {
> > +                     err = PTR_ERR(serdes);
> > +                     if (err != -EPROBE_DEFER)
> > +                             dev_err(sparx5->dev,
> > +                                     "port %u: missing serdes\n",
> > +                                     portno);
> 
> dev_err_probe()

OK - did not know that one.

> 
> > +                     goto cleanup_config;
> > +             }
> > +             config->portno = portno;
> > +             config->node = portnp;
> > +             config->serdes = serdes;
> > +
> > +             conf->media = PHY_MEDIA_DAC;
> > +             conf->serdes_reset = true;
> > +             conf->portmode = conf->phy_mode;
> > +             if (of_find_property(portnp, "sfp", NULL)) {
> > +                     conf->has_sfp = true;
> > +                     conf->power_down = true;
> > +             }
> > +             idx++;
> > +     }
> > +
> > +     err = sparx5_create_targets(sparx5);
> > +     if (err)
> > +             goto cleanup_config;
> > +
> > +     if (of_get_mac_address(np, mac_addr)) {
> > +             dev_info(sparx5->dev, "MAC addr was not set, use random MAC\n");
> > +             eth_random_addr(sparx5->base_mac);
> > +             sparx5->base_mac[5] = 0;
> > +     } else {
> > +             ether_addr_copy(sparx5->base_mac, mac_addr);
> > +     }
> > +
> > +     /* Inj/Xtr IRQ support to be added in later patches */
> > +     /* Read chip ID to check CPU interface */
> > +     sparx5->chip_id = spx5_rd(sparx5, GCB_CHIP_ID);
> > +
> > +     sparx5->target_ct = (enum spx5_target_chiptype)
> > +             GCB_CHIP_ID_PART_ID_GET(sparx5->chip_id);
> > +
> > +     /* Initialize Switchcore and internal RAMs */
> > +     if (sparx5_init_switchcore(sparx5)) {
> > +             dev_err(sparx5->dev, "Switchcore initialization error\n");
> > +             goto cleanup_config;
> 
> Should @err be set?

Yes it should.  I will update here and below.

> 
> > +     }
> > +
> > +     /* Initialize the LC-PLL (core clock) and set affected registers */
> > +     if (sparx5_init_coreclock(sparx5)) {
> > +             dev_err(sparx5->dev, "LC-PLL initialization error\n");
> > +             goto cleanup_config;
> 
> ditto

Yes.

> 
> > +     }
> > +
> > +     for (idx = 0; idx < sparx5->port_count; ++idx) {
> > +             config = &configs[idx];
> > +             if (!config->node)
> > +                     continue;
> > +
> > +             err = sparx5_create_port(sparx5, config);
> > +             if (err) {
> > +                     dev_err(sparx5->dev, "port create error\n");
> > +                     goto cleanup_ports;
> > +             }
> > +     }
> > +
> > +     if (sparx5_start(sparx5)) {
> > +             dev_err(sparx5->dev, "Start failed\n");
> > +             goto cleanup_ports;
> 
> and here

Yes.

> 
> > +     }
> > +
> > +     kfree(configs);
> > +     return err;
> > +
> > +cleanup_ports:
> > +     /* Port cleanup to be added in later patches */
> > +cleanup_config:
> > +     kfree(configs);
> > +     return err;
> > +}
> 
> > +struct sparx5_port_config      {
> 
> Spurious tab before {?

Spurious spaces - but they will be removed.

> 
> > +     phy_interface_t portmode;
> > +     bool has_sfp;
> > +     u32 bandwidth;
> > +     int speed;
> > +     int duplex;
> > +     enum phy_media media;
> > +     bool power_down;
> > +     bool autoneg;
> > +     u32 pause;
> > +     bool serdes_reset;
> 
> Group all 4 bools together for better packing?

Yes that saves some bytes.  Would bitfields be preferable or are bools sufficient?

> 
> > +     phy_interface_t phy_mode;
> > +     u32 sd_sgpio;
> > +};
> 
> > +static inline void spx5_rmw(u32 val, u32 mask, struct sparx5 *sparx5,
> > +                         int id, int tinst, int tcnt,
> > +                         int gbase, int ginst, int gcnt, int gwidth,
> > +                         int raddr, int rinst, int rcnt, int rwidth)
> > +{
> > +     u32 nval;
> > +     void __iomem *addr =
> > +             spx5_addr(sparx5->regs, id, tinst, tcnt,
> 
> Why try to initialize inline when it results in weird looking code and
> no saved lines?

Hmm, I had not really noticed that... I will just use the spx5_addr call in both places.

> 
> > +                       gbase, ginst, gcnt, gwidth,
> > +                       raddr, rinst, rcnt, rwidth);
> 
> Not to mention that you end up with no new line after variable
> declaration.

Yes.  I will add an empty line.

> 
> > +     nval = readl(addr);
> > +     nval = (nval & ~mask) | (val & mask);
> > +     writel(nval, addr);
> > +}


Thanks!