mirror of
				https://github.com/Ysurac/openmptcprouter.git
				synced 2025-03-09 15:40:20 +00:00 
			
		
		
		
	Update 6.12 kernel patches
This commit is contained in:
		
							parent
							
								
									bdb9b0046f
								
							
						
					
					
						commit
						9d83c70ced
					
				
					 247 changed files with 53301 additions and 589 deletions
				
			
		| 
						 | 
				
			
			@ -0,0 +1,91 @@
 | 
			
		|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
 | 
			
		||||
%YAML 1.2
 | 
			
		||||
---
 | 
			
		||||
$id: http://devicetree.org/schemas/mtd/partitions/openwrt,uimage.yaml#
 | 
			
		||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
 | 
			
		||||
 | 
			
		||||
title: OpenWrt variations of U-Boot Image partitions
 | 
			
		||||
 | 
			
		||||
maintainers:
 | 
			
		||||
  - Bjørn Mork <bjorn@mork.no>
 | 
			
		||||
 | 
			
		||||
description: |
 | 
			
		||||
  The image format defined by the boot loader "Das U-Boot" is often
 | 
			
		||||
  modified or extended by device vendors. This defines a few optional
 | 
			
		||||
  properties which can be used to describe such modifications.
 | 
			
		||||
 | 
			
		||||
# partition.txt defines common properties, but has not yet been
 | 
			
		||||
# converted to YAML
 | 
			
		||||
#allOf:
 | 
			
		||||
#  - $ref: ../partition.yaml#
 | 
			
		||||
 | 
			
		||||
properties:
 | 
			
		||||
  compatible:
 | 
			
		||||
    items:
 | 
			
		||||
      - enum:
 | 
			
		||||
          - openwrt,uimage
 | 
			
		||||
      - const: denx,uimage
 | 
			
		||||
 | 
			
		||||
  openwrt,padding:
 | 
			
		||||
    description: Number of padding bytes between header and data
 | 
			
		||||
    $ref: /schemas/types.yaml#/definitions/uint32
 | 
			
		||||
    default: 0
 | 
			
		||||
 | 
			
		||||
  openwrt,ih-magic:
 | 
			
		||||
    description: U-Boot Image Header magic number.
 | 
			
		||||
    $ref: /schemas/types.yaml#/definitions/uint32
 | 
			
		||||
    default: 0x27051956 # IH_MAGIC
 | 
			
		||||
 | 
			
		||||
  openwrt,ih-type:
 | 
			
		||||
    description: U-Boot Image type
 | 
			
		||||
    $ref: /schemas/types.yaml#/definitions/uint32
 | 
			
		||||
    default: 2 # IH_TYPE_KERNEL
 | 
			
		||||
 | 
			
		||||
  openwrt,offset:
 | 
			
		||||
    description:
 | 
			
		||||
      Offset between partition start and U-Boot Image in bytes
 | 
			
		||||
    $ref: /schemas/types.yaml#/definitions/uint32
 | 
			
		||||
    default: 0
 | 
			
		||||
 | 
			
		||||
  openwrt,partition-magic:
 | 
			
		||||
    description:
 | 
			
		||||
      Magic number found at the start of the partition. Will only be
 | 
			
		||||
      validated if both this property and openwrt,offset is non-zero
 | 
			
		||||
    $ref: /schemas/types.yaml#/definitions/uint32
 | 
			
		||||
    default: 0
 | 
			
		||||
 | 
			
		||||
required:
 | 
			
		||||
  - compatible
 | 
			
		||||
  - reg
 | 
			
		||||
 | 
			
		||||
#unevaluatedProperties: false
 | 
			
		||||
additionalProperties: false
 | 
			
		||||
 | 
			
		||||
examples:
 | 
			
		||||
  - |
 | 
			
		||||
    // device with non-default magic
 | 
			
		||||
    partition@300000 {
 | 
			
		||||
          compatible = "openwrt,uimage", "denx,uimage";
 | 
			
		||||
          reg = <0x00300000 0xe80000>;
 | 
			
		||||
          label = "firmware";
 | 
			
		||||
          openwrt,ih-magic = <0x4e474520>;
 | 
			
		||||
    };
 | 
			
		||||
  - |
 | 
			
		||||
    // device with U-Boot Image at an offset, with a partition magic value
 | 
			
		||||
    partition@70000 {
 | 
			
		||||
          compatible = "openwrt,uimage", "denx,uimage";
 | 
			
		||||
          reg = <0x00070000 0x00790000>;
 | 
			
		||||
          label = "firmware";
 | 
			
		||||
          openwrt,offset = <20>;
 | 
			
		||||
          openwrt,partition-magic = <0x43535953>;
 | 
			
		||||
    };
 | 
			
		||||
  - |
 | 
			
		||||
    // device using a non-default image type
 | 
			
		||||
    #include "dt-bindings/mtd/partitions/uimage.h"
 | 
			
		||||
    partition@6c0000 {
 | 
			
		||||
          compatible = "openwrt,uimage", "denx,uimage";
 | 
			
		||||
          reg = <0x6c0000 0x1900000>;
 | 
			
		||||
          label = "firmware";
 | 
			
		||||
          openwrt,ih-magic = <0x33373033>;
 | 
			
		||||
          openwrt,ih-type = <IH_TYPE_FILESYSTEM>;
 | 
			
		||||
    };
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,110 @@
 | 
			
		|||
------- 
 | 
			
		||||
 | 
			
		||||
ADM6996FC / ADM6996M switch chip driver
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
1. General information
 | 
			
		||||
 | 
			
		||||
  This driver supports the FC and M models only. The ADM6996F and L are
 | 
			
		||||
  completely different chips.
 | 
			
		||||
  
 | 
			
		||||
  Support for the FC model is extremely limited at the moment. There is no VLAN
 | 
			
		||||
  support as of yet. The driver will not offer an swconfig interface for the FC
 | 
			
		||||
  chip.
 | 
			
		||||
 
 | 
			
		||||
1.1 VLAN IDs
 | 
			
		||||
 | 
			
		||||
  It is possible to define 16 different VLANs. Every VLAN has an identifier, its
 | 
			
		||||
  VLAN ID. It is easiest if you use at most VLAN IDs 0-15. In that case, the
 | 
			
		||||
  swconfig based configuration is very straightforward. To define two VLANs with
 | 
			
		||||
  IDs 4 and 5, you can invoke, for example:
 | 
			
		||||
  
 | 
			
		||||
      # swconfig dev ethX vlan 4 set ports '0 1t 2 5t' 
 | 
			
		||||
      # swconfig dev ethX vlan 5 set ports '0t 1t 5t'
 | 
			
		||||
  
 | 
			
		||||
  The swconfig framework will automatically invoke 'port Y set pvid Z' for every
 | 
			
		||||
  port that is an untagged member of VLAN Y, setting its Primary VLAN ID. In
 | 
			
		||||
  this example, ports 0 and 2 would get "pvid 4". The Primary VLAN ID of a port
 | 
			
		||||
  is the VLAN ID associated with untagged packets coming in on that port.
 | 
			
		||||
  
 | 
			
		||||
  But if you wish to use VLAN IDs outside the range 0-15, this automatic
 | 
			
		||||
  behaviour of the swconfig framework becomes a problem. The 16 VLANs that
 | 
			
		||||
  swconfig can configure on the ADM6996 also have a "vid" setting. By default,
 | 
			
		||||
  this is the same as the number of the VLAN entry, to make the simple behaviour
 | 
			
		||||
  above possible. To still support a VLAN with a VLAN ID higher than 15
 | 
			
		||||
  (presumably because you are in a network where such VLAN IDs are already in
 | 
			
		||||
  use), you can change the "vid" setting of the VLAN to anything in the range
 | 
			
		||||
  0-1023. But suppose you did the following:
 | 
			
		||||
  
 | 
			
		||||
      # swconfig dev ethX vlan 0 set vid 998 
 | 
			
		||||
      # swconfig dev ethX vlan 0 set ports '0 2 5t'
 | 
			
		||||
 
 | 
			
		||||
  Now the swconfig framework will issue 'port 0 set pvid 0' and 'port 2 set pvid
 | 
			
		||||
  0'. But the "pvid" should be set to 998, so you are responsible for manually
 | 
			
		||||
  fixing this!
 | 
			
		||||
 | 
			
		||||
1.2 VLAN filtering
 | 
			
		||||
 | 
			
		||||
  The switch is configured to apply source port filtering. This means that
 | 
			
		||||
  packets are only accepted when the port the packets came in on is a member of
 | 
			
		||||
  the VLAN the packet should go to.
 | 
			
		||||
 | 
			
		||||
  Only membership of a VLAN is tested, it does not matter whether it is a tagged
 | 
			
		||||
  or untagged membership.
 | 
			
		||||
 | 
			
		||||
  For untagged packets, the destination VLAN is the Primary VLAN ID of the
 | 
			
		||||
  incoming port. So if the PVID of a port is 0, but that port is not a member of
 | 
			
		||||
  the VLAN with ID 0, this means that untagged packets on that port are dropped.
 | 
			
		||||
  This can be used as a roundabout way of dropping untagged packets from a port,
 | 
			
		||||
  a mode often referred to as "Admit only tagged packets".
 | 
			
		||||
 | 
			
		||||
1.3 Reset
 | 
			
		||||
 | 
			
		||||
  The two supported chip models do not have a sofware-initiated reset. When the
 | 
			
		||||
  driver is initialised, as well as when the 'reset' swconfig option is invoked,
 | 
			
		||||
  the driver will set those registers it knows about and supports to the correct
 | 
			
		||||
  default value. But there are a lot of registers in the chip that the driver
 | 
			
		||||
  does not support. If something changed those registers, invoking 'reset' or
 | 
			
		||||
  performing a warm reboot might still leave the chip in a "broken" state. Only
 | 
			
		||||
  a hardware reset will bring it back in the default state.
 | 
			
		||||
 | 
			
		||||
2. Technical details on PHYs and the ADM6996
 | 
			
		||||
 | 
			
		||||
  From the viewpoint of the Linux kernel, it is common that an Ethernet adapter
 | 
			
		||||
  can be seen as a separate MAC entity and a separate PHY entity. The PHY entity
 | 
			
		||||
  can be queried and set through registers accessible via an MDIO bus. A PHY
 | 
			
		||||
  normally has a single address on that bus, in the range 0 through 31.
 | 
			
		||||
 | 
			
		||||
  The ADM6996 has special-purpose registers in the range of PHYs 0 through 10.
 | 
			
		||||
  Even though all these registers control a single ADM6996 chip, the Linux
 | 
			
		||||
  kernel treats this as 11 separate PHYs.  The driver will bind to these
 | 
			
		||||
  addresses to prevent a different PHY driver from binding and corrupting these
 | 
			
		||||
  registers.
 | 
			
		||||
 | 
			
		||||
  What Linux sees as the PHY on address 0 is meant for the Ethernet MAC
 | 
			
		||||
  connected to the CPU port of the ADM6996 switch chip (port 5). This is the
 | 
			
		||||
  Ethernet MAC you will use to send and receive data through the switch.
 | 
			
		||||
 | 
			
		||||
  The PHYs at addresses 16 through 20 map to the PHYs on ports 0 through 4 of
 | 
			
		||||
  the switch chip. These can be accessed with the Generic PHY driver, as the
 | 
			
		||||
  registers have the common layout.
 | 
			
		||||
 | 
			
		||||
  If a second Ethernet MAC on your board is wired to the port 4 PHY, that MAC
 | 
			
		||||
  needs to bind to PHY address 20 for the port to work correctly.
 | 
			
		||||
 | 
			
		||||
  The ADM6996 switch driver will reset the ports 0 through 3 on startup and when
 | 
			
		||||
  'reset' is invoked. This could clash with a different PHY driver if the kernel
 | 
			
		||||
  binds a PHY driver to address 16 through 19.
 | 
			
		||||
 | 
			
		||||
  If Linux binds a PHY on addresses 1 through 10 to an Ethernet MAC, the ADM6996
 | 
			
		||||
  driver will simply always report a connected 100 Mbit/s full-duplex link for
 | 
			
		||||
  that PHY, and provide no other functionality. This is most likely not what you
 | 
			
		||||
  want. So if you see a message in your log
 | 
			
		||||
 | 
			
		||||
  	ethX: PHY overlaps ADM6996, providing fixed PHY yy.
 | 
			
		||||
 | 
			
		||||
  This is most likely an indication that ethX will not work properly, and your
 | 
			
		||||
  kernel needs to be configured to attach a different PHY to that Ethernet MAC.
 | 
			
		||||
 | 
			
		||||
  Controlling the mapping between MACs and PHYs is usually done in platform- or
 | 
			
		||||
  board-specific fixup code. The ADM6996 driver has no influence over this.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
#
 | 
			
		||||
# Makefile for the Compex's MyLoader support on MIPS architecture
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
lib-y += myloader.o
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Compex's MyLoader specific prom routines
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 *  by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/addrspace.h>
 | 
			
		||||
#include <asm/fw/myloader/myloader.h>
 | 
			
		||||
 | 
			
		||||
#define SYS_PARAMS_ADDR		KSEG1ADDR(0x80000800)
 | 
			
		||||
#define BOARD_PARAMS_ADDR	KSEG1ADDR(0x80000A00)
 | 
			
		||||
#define PART_TABLE_ADDR		KSEG1ADDR(0x80000C00)
 | 
			
		||||
#define BOOT_PARAMS_ADDR	KSEG1ADDR(0x80000E00)
 | 
			
		||||
 | 
			
		||||
static struct myloader_info myloader_info __initdata;
 | 
			
		||||
static int myloader_found __initdata;
 | 
			
		||||
 | 
			
		||||
struct myloader_info * __init myloader_get_info(void)
 | 
			
		||||
{
 | 
			
		||||
	struct mylo_system_params *sysp;
 | 
			
		||||
	struct mylo_board_params *boardp;
 | 
			
		||||
	struct mylo_partition_table *parts;
 | 
			
		||||
 | 
			
		||||
	if (myloader_found)
 | 
			
		||||
		return &myloader_info;
 | 
			
		||||
 | 
			
		||||
	sysp = (struct mylo_system_params *)(SYS_PARAMS_ADDR);
 | 
			
		||||
	boardp = (struct mylo_board_params *)(BOARD_PARAMS_ADDR);
 | 
			
		||||
	parts = (struct mylo_partition_table *)(PART_TABLE_ADDR);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_DEBUG "MyLoader: sysp=%08x, boardp=%08x, parts=%08x\n",
 | 
			
		||||
		sysp->magic, boardp->magic, parts->magic);
 | 
			
		||||
 | 
			
		||||
	/* Check for some magic numbers */
 | 
			
		||||
	if (sysp->magic != MYLO_MAGIC_SYS_PARAMS ||
 | 
			
		||||
	    boardp->magic != MYLO_MAGIC_BOARD_PARAMS ||
 | 
			
		||||
	    le32_to_cpu(parts->magic) != MYLO_MAGIC_PARTITIONS)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	printk(KERN_DEBUG "MyLoader: id=%04x:%04x, sub_id=%04x:%04x\n",
 | 
			
		||||
		sysp->vid, sysp->did, sysp->svid, sysp->sdid);
 | 
			
		||||
 | 
			
		||||
	myloader_info.vid = sysp->vid;
 | 
			
		||||
	myloader_info.did = sysp->did;
 | 
			
		||||
	myloader_info.svid = sysp->svid;
 | 
			
		||||
	myloader_info.sdid = sysp->sdid;
 | 
			
		||||
 | 
			
		||||
	memcpy(myloader_info.macs, boardp->addr, sizeof(myloader_info.macs));
 | 
			
		||||
 | 
			
		||||
	myloader_found = 1;
 | 
			
		||||
 | 
			
		||||
	return &myloader_info;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										534
									
								
								6.12/target/linux/generic/files/drivers/bcma/fallback-sprom.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										534
									
								
								6.12/target/linux/generic/files/drivers/bcma/fallback-sprom.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,534 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
/*
 | 
			
		||||
 * BCMA Fallback SPROM Driver
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
 | 
			
		||||
 * Copyright (C) 2014 Jonas Gorski <jonas.gorski@gmail.com>
 | 
			
		||||
 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
 | 
			
		||||
 * Copyright (C) 2008 Florian Fainelli <f.fainelli@gmail.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/bcma/bcma.h>
 | 
			
		||||
#include <linux/etherdevice.h>
 | 
			
		||||
#include <linux/firmware.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/of_net.h>
 | 
			
		||||
#include <linux/of_platform.h>
 | 
			
		||||
#include "fallback-sprom.h"
 | 
			
		||||
 | 
			
		||||
#define BCMA_FBS_MAX_SIZE 468
 | 
			
		||||
 | 
			
		||||
/* SPROM Extraction */
 | 
			
		||||
#define SPOFF(offset)	((offset) / sizeof(u16))
 | 
			
		||||
 | 
			
		||||
#define SPEX(_outvar, _offset, _mask, _shift)	\
 | 
			
		||||
	out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
 | 
			
		||||
 | 
			
		||||
#define SPEX32(_outvar, _offset, _mask, _shift)	\
 | 
			
		||||
	out->_outvar = ((((u32)in[SPOFF((_offset)+2)] << 16 | \
 | 
			
		||||
			   in[SPOFF(_offset)]) & (_mask)) >> (_shift))
 | 
			
		||||
 | 
			
		||||
#define SPEX_ARRAY8(_field, _offset, _mask, _shift)	\
 | 
			
		||||
	do {	\
 | 
			
		||||
		SPEX(_field[0], _offset +  0, _mask, _shift);	\
 | 
			
		||||
		SPEX(_field[1], _offset +  2, _mask, _shift);	\
 | 
			
		||||
		SPEX(_field[2], _offset +  4, _mask, _shift);	\
 | 
			
		||||
		SPEX(_field[3], _offset +  6, _mask, _shift);	\
 | 
			
		||||
		SPEX(_field[4], _offset +  8, _mask, _shift);	\
 | 
			
		||||
		SPEX(_field[5], _offset + 10, _mask, _shift);	\
 | 
			
		||||
		SPEX(_field[6], _offset + 12, _mask, _shift);	\
 | 
			
		||||
		SPEX(_field[7], _offset + 14, _mask, _shift);	\
 | 
			
		||||
	} while (0)
 | 
			
		||||
 | 
			
		||||
struct bcma_fbs {
 | 
			
		||||
	struct device *dev;
 | 
			
		||||
	struct list_head list;
 | 
			
		||||
	struct ssb_sprom sprom;
 | 
			
		||||
	u32 pci_bus;
 | 
			
		||||
	u32 pci_dev;
 | 
			
		||||
	bool devid_override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static DEFINE_SPINLOCK(bcma_fbs_lock);
 | 
			
		||||
static struct list_head bcma_fbs_list = LIST_HEAD_INIT(bcma_fbs_list);
 | 
			
		||||
 | 
			
		||||
int bcma_get_fallback_sprom(struct bcma_bus *bus, struct ssb_sprom *out)
 | 
			
		||||
{
 | 
			
		||||
	struct bcma_fbs *pos;
 | 
			
		||||
	u32 pci_bus, pci_dev;
 | 
			
		||||
 | 
			
		||||
	if (bus->hosttype != BCMA_HOSTTYPE_PCI)
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	pci_bus = bus->host_pci->bus->number;
 | 
			
		||||
	pci_dev = PCI_SLOT(bus->host_pci->devfn);
 | 
			
		||||
 | 
			
		||||
	list_for_each_entry(pos, &bcma_fbs_list, list) {
 | 
			
		||||
		if (pos->pci_bus != pci_bus ||
 | 
			
		||||
		    pos->pci_dev != pci_dev)
 | 
			
		||||
		    	continue;
 | 
			
		||||
 | 
			
		||||
		if (pos->devid_override)
 | 
			
		||||
			bus->host_pci->device = pos->sprom.dev_id;
 | 
			
		||||
 | 
			
		||||
		memcpy(out, &pos->sprom, sizeof(struct ssb_sprom));
 | 
			
		||||
		dev_info(pos->dev, "requested by [%x:%x]",
 | 
			
		||||
			 pos->pci_bus, pos->pci_dev);
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pr_err("unable to fill SPROM for [%x:%x]\n", pci_bus, pci_dev);
 | 
			
		||||
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static s8 sprom_extract_antgain(const u16 *in, u16 offset, u16 mask, u16 shift)
 | 
			
		||||
{
 | 
			
		||||
	u16 v;
 | 
			
		||||
	u8 gain;
 | 
			
		||||
 | 
			
		||||
	v = in[SPOFF(offset)];
 | 
			
		||||
	gain = (v & mask) >> shift;
 | 
			
		||||
	if (gain == 0xFF) {
 | 
			
		||||
		gain = 8; /* If unset use 2dBm */
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Q5.2 Fractional part is stored in 0xC0 */
 | 
			
		||||
		gain = ((gain & 0xC0) >> 6) | ((gain & 0x3F) << 2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (s8)gain;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
 | 
			
		||||
{
 | 
			
		||||
	static const u16 pwr_info_offset[] = {
 | 
			
		||||
		SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1,
 | 
			
		||||
		SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3
 | 
			
		||||
	};
 | 
			
		||||
	u16 o;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
 | 
			
		||||
		     ARRAY_SIZE(out->core_pwr_info));
 | 
			
		||||
 | 
			
		||||
	SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0);
 | 
			
		||||
	SPEX(board_type, SSB_SPROM1_SPID, ~0, 0);
 | 
			
		||||
 | 
			
		||||
	SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G0,
 | 
			
		||||
	     SSB_SPROM4_TXPID2G0_SHIFT);
 | 
			
		||||
	SPEX(txpid2g[1], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G1,
 | 
			
		||||
	     SSB_SPROM4_TXPID2G1_SHIFT);
 | 
			
		||||
	SPEX(txpid2g[2], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G2,
 | 
			
		||||
	     SSB_SPROM4_TXPID2G2_SHIFT);
 | 
			
		||||
	SPEX(txpid2g[3], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G3,
 | 
			
		||||
	     SSB_SPROM4_TXPID2G3_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(txpid5gl[0], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL0,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GL0_SHIFT);
 | 
			
		||||
	SPEX(txpid5gl[1], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL1,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GL1_SHIFT);
 | 
			
		||||
	SPEX(txpid5gl[2], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL2,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GL2_SHIFT);
 | 
			
		||||
	SPEX(txpid5gl[3], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL3,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GL3_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(txpid5g[0], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G0,
 | 
			
		||||
	     SSB_SPROM4_TXPID5G0_SHIFT);
 | 
			
		||||
	SPEX(txpid5g[1], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G1,
 | 
			
		||||
	     SSB_SPROM4_TXPID5G1_SHIFT);
 | 
			
		||||
	SPEX(txpid5g[2], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G2,
 | 
			
		||||
	     SSB_SPROM4_TXPID5G2_SHIFT);
 | 
			
		||||
	SPEX(txpid5g[3], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G3,
 | 
			
		||||
	     SSB_SPROM4_TXPID5G3_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(txpid5gh[0], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH0,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GH0_SHIFT);
 | 
			
		||||
	SPEX(txpid5gh[1], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH1,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GH1_SHIFT);
 | 
			
		||||
	SPEX(txpid5gh[2], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH2,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GH2_SHIFT);
 | 
			
		||||
	SPEX(txpid5gh[3], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH3,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GH3_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(boardflags_lo, SSB_SPROM8_BFLLO, ~0, 0);
 | 
			
		||||
	SPEX(boardflags_hi, SSB_SPROM8_BFLHI, ~0, 0);
 | 
			
		||||
	SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, ~0, 0);
 | 
			
		||||
	SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, ~0, 0);
 | 
			
		||||
 | 
			
		||||
	SPEX(alpha2[0], SSB_SPROM8_CCODE, 0xff00, 8);
 | 
			
		||||
	SPEX(alpha2[1], SSB_SPROM8_CCODE, 0x00ff, 0);
 | 
			
		||||
 | 
			
		||||
	/* Extract core's power info */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
 | 
			
		||||
		o = pwr_info_offset[i];
 | 
			
		||||
		SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
 | 
			
		||||
			SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT);
 | 
			
		||||
		SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
 | 
			
		||||
			SSB_SPROM8_2G_MAXP, 0);
 | 
			
		||||
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0);
 | 
			
		||||
 | 
			
		||||
		SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
 | 
			
		||||
			SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT);
 | 
			
		||||
		SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
 | 
			
		||||
			SSB_SPROM8_5G_MAXP, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP,
 | 
			
		||||
			SSB_SPROM8_5GH_MAXP, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP,
 | 
			
		||||
			SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT);
 | 
			
		||||
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TSSIPOS,
 | 
			
		||||
	     SSB_SROM8_FEM_TSSIPOS_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz2.extpa_gain, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_EXTPA_GAIN,
 | 
			
		||||
	     SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz2.pdet_range, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_PDET_RANGE,
 | 
			
		||||
	     SSB_SROM8_FEM_PDET_RANGE_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz2.tr_iso, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TR_ISO,
 | 
			
		||||
	     SSB_SROM8_FEM_TR_ISO_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz2.antswlut, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_ANTSWLUT,
 | 
			
		||||
	     SSB_SROM8_FEM_ANTSWLUT_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(fem.ghz5.tssipos, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TSSIPOS,
 | 
			
		||||
	     SSB_SROM8_FEM_TSSIPOS_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz5.extpa_gain, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_EXTPA_GAIN,
 | 
			
		||||
	     SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz5.pdet_range, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_PDET_RANGE,
 | 
			
		||||
	     SSB_SROM8_FEM_PDET_RANGE_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz5.tr_iso, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TR_ISO,
 | 
			
		||||
	     SSB_SROM8_FEM_TR_ISO_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_ANTSWLUT,
 | 
			
		||||
	     SSB_SROM8_FEM_ANTSWLUT_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
 | 
			
		||||
	     SSB_SPROM8_ANTAVAIL_A_SHIFT);
 | 
			
		||||
	SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
 | 
			
		||||
	     SSB_SPROM8_ANTAVAIL_BG_SHIFT);
 | 
			
		||||
	SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0);
 | 
			
		||||
	SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG,
 | 
			
		||||
	     SSB_SPROM8_ITSSI_BG_SHIFT);
 | 
			
		||||
	SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0);
 | 
			
		||||
	SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A,
 | 
			
		||||
	     SSB_SPROM8_ITSSI_A_SHIFT);
 | 
			
		||||
	SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0);
 | 
			
		||||
	SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK,
 | 
			
		||||
	     SSB_SPROM8_MAXP_AL_SHIFT);
 | 
			
		||||
	SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0);
 | 
			
		||||
	SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1,
 | 
			
		||||
	     SSB_SPROM8_GPIOA_P1_SHIFT);
 | 
			
		||||
	SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0);
 | 
			
		||||
	SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3,
 | 
			
		||||
	     SSB_SPROM8_GPIOB_P3_SHIFT);
 | 
			
		||||
	SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0);
 | 
			
		||||
	SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G,
 | 
			
		||||
	     SSB_SPROM8_TRI5G_SHIFT);
 | 
			
		||||
	SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0);
 | 
			
		||||
	SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH,
 | 
			
		||||
	     SSB_SPROM8_TRI5GH_SHIFT);
 | 
			
		||||
	SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G,
 | 
			
		||||
	     SSB_SPROM8_RXPO2G_SHIFT);
 | 
			
		||||
	SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G,
 | 
			
		||||
	     SSB_SPROM8_RXPO5G_SHIFT);
 | 
			
		||||
	SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0);
 | 
			
		||||
	SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G,
 | 
			
		||||
	     SSB_SPROM8_RSSISMC2G_SHIFT);
 | 
			
		||||
	SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G,
 | 
			
		||||
	     SSB_SPROM8_RSSISAV2G_SHIFT);
 | 
			
		||||
	SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G,
 | 
			
		||||
	     SSB_SPROM8_BXA2G_SHIFT);
 | 
			
		||||
	SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0);
 | 
			
		||||
	SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G,
 | 
			
		||||
	     SSB_SPROM8_RSSISMC5G_SHIFT);
 | 
			
		||||
	SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G,
 | 
			
		||||
	     SSB_SPROM8_RSSISAV5G_SHIFT);
 | 
			
		||||
	SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G,
 | 
			
		||||
	     SSB_SPROM8_BXA5G_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(pa0b0, SSB_SPROM8_PA0B0, ~0, 0);
 | 
			
		||||
	SPEX(pa0b1, SSB_SPROM8_PA0B1, ~0, 0);
 | 
			
		||||
	SPEX(pa0b2, SSB_SPROM8_PA0B2, ~0, 0);
 | 
			
		||||
	SPEX(pa1b0, SSB_SPROM8_PA1B0, ~0, 0);
 | 
			
		||||
	SPEX(pa1b1, SSB_SPROM8_PA1B1, ~0, 0);
 | 
			
		||||
	SPEX(pa1b2, SSB_SPROM8_PA1B2, ~0, 0);
 | 
			
		||||
	SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, ~0, 0);
 | 
			
		||||
	SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, ~0, 0);
 | 
			
		||||
	SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, ~0, 0);
 | 
			
		||||
	SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, ~0, 0);
 | 
			
		||||
	SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, ~0, 0);
 | 
			
		||||
	SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, ~0, 0);
 | 
			
		||||
	SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, ~0, 0);
 | 
			
		||||
	SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, ~0, 0);
 | 
			
		||||
	SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, ~0, 0);
 | 
			
		||||
	SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, ~0, 0);
 | 
			
		||||
	SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, ~0, 0);
 | 
			
		||||
 | 
			
		||||
	/* Extract the antenna gain values. */
 | 
			
		||||
	out->antenna_gain.a0 = sprom_extract_antgain(in,
 | 
			
		||||
						     SSB_SPROM8_AGAIN01,
 | 
			
		||||
						     SSB_SPROM8_AGAIN0,
 | 
			
		||||
						     SSB_SPROM8_AGAIN0_SHIFT);
 | 
			
		||||
	out->antenna_gain.a1 = sprom_extract_antgain(in,
 | 
			
		||||
						     SSB_SPROM8_AGAIN01,
 | 
			
		||||
						     SSB_SPROM8_AGAIN1,
 | 
			
		||||
						     SSB_SPROM8_AGAIN1_SHIFT);
 | 
			
		||||
	out->antenna_gain.a2 = sprom_extract_antgain(in,
 | 
			
		||||
						     SSB_SPROM8_AGAIN23,
 | 
			
		||||
						     SSB_SPROM8_AGAIN2,
 | 
			
		||||
						     SSB_SPROM8_AGAIN2_SHIFT);
 | 
			
		||||
	out->antenna_gain.a3 = sprom_extract_antgain(in,
 | 
			
		||||
						     SSB_SPROM8_AGAIN23,
 | 
			
		||||
						     SSB_SPROM8_AGAIN3,
 | 
			
		||||
						     SSB_SPROM8_AGAIN3_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(leddc_on_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_ON,
 | 
			
		||||
	     SSB_SPROM8_LEDDC_ON_SHIFT);
 | 
			
		||||
	SPEX(leddc_off_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_OFF,
 | 
			
		||||
	     SSB_SPROM8_LEDDC_OFF_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(txchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_TXCHAIN,
 | 
			
		||||
	     SSB_SPROM8_TXRXC_TXCHAIN_SHIFT);
 | 
			
		||||
	SPEX(rxchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_RXCHAIN,
 | 
			
		||||
	     SSB_SPROM8_TXRXC_RXCHAIN_SHIFT);
 | 
			
		||||
	SPEX(antswitch, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_SWITCH,
 | 
			
		||||
	     SSB_SPROM8_TXRXC_SWITCH_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(opo, SSB_SPROM8_OFDM2GPO, 0x00ff, 0);
 | 
			
		||||
 | 
			
		||||
	SPEX_ARRAY8(mcs2gpo, SSB_SPROM8_2G_MCSPO, ~0, 0);
 | 
			
		||||
	SPEX_ARRAY8(mcs5gpo, SSB_SPROM8_5G_MCSPO, ~0, 0);
 | 
			
		||||
	SPEX_ARRAY8(mcs5glpo, SSB_SPROM8_5GL_MCSPO, ~0, 0);
 | 
			
		||||
	SPEX_ARRAY8(mcs5ghpo, SSB_SPROM8_5GH_MCSPO, ~0, 0);
 | 
			
		||||
 | 
			
		||||
	SPEX(rawtempsense, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_RAWTEMP,
 | 
			
		||||
	     SSB_SPROM8_RAWTS_RAWTEMP_SHIFT);
 | 
			
		||||
	SPEX(measpower, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_MEASPOWER,
 | 
			
		||||
	     SSB_SPROM8_RAWTS_MEASPOWER_SHIFT);
 | 
			
		||||
	SPEX(tempsense_slope, SSB_SPROM8_OPT_CORRX,
 | 
			
		||||
	     SSB_SPROM8_OPT_CORRX_TEMP_SLOPE,
 | 
			
		||||
	     SSB_SPROM8_OPT_CORRX_TEMP_SLOPE_SHIFT);
 | 
			
		||||
	SPEX(tempcorrx, SSB_SPROM8_OPT_CORRX, SSB_SPROM8_OPT_CORRX_TEMPCORRX,
 | 
			
		||||
	     SSB_SPROM8_OPT_CORRX_TEMPCORRX_SHIFT);
 | 
			
		||||
	SPEX(tempsense_option, SSB_SPROM8_OPT_CORRX,
 | 
			
		||||
	     SSB_SPROM8_OPT_CORRX_TEMP_OPTION,
 | 
			
		||||
	     SSB_SPROM8_OPT_CORRX_TEMP_OPTION_SHIFT);
 | 
			
		||||
	SPEX(freqoffset_corr, SSB_SPROM8_HWIQ_IQSWP,
 | 
			
		||||
	     SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR,
 | 
			
		||||
	     SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR_SHIFT);
 | 
			
		||||
	SPEX(iqcal_swp_dis, SSB_SPROM8_HWIQ_IQSWP,
 | 
			
		||||
	     SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP,
 | 
			
		||||
	     SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP_SHIFT);
 | 
			
		||||
	SPEX(hw_iqcal_en, SSB_SPROM8_HWIQ_IQSWP, SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL,
 | 
			
		||||
	     SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(bw40po, SSB_SPROM8_BW40PO, ~0, 0);
 | 
			
		||||
	SPEX(cddpo, SSB_SPROM8_CDDPO, ~0, 0);
 | 
			
		||||
	SPEX(stbcpo, SSB_SPROM8_STBCPO, ~0, 0);
 | 
			
		||||
	SPEX(bwduppo, SSB_SPROM8_BWDUPPO, ~0, 0);
 | 
			
		||||
 | 
			
		||||
	SPEX(tempthresh, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_TRESH,
 | 
			
		||||
	     SSB_SPROM8_THERMAL_TRESH_SHIFT);
 | 
			
		||||
	SPEX(tempoffset, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_OFFSET,
 | 
			
		||||
	     SSB_SPROM8_THERMAL_OFFSET_SHIFT);
 | 
			
		||||
	SPEX(phycal_tempdelta, SSB_SPROM8_TEMPDELTA,
 | 
			
		||||
	     SSB_SPROM8_TEMPDELTA_PHYCAL,
 | 
			
		||||
	     SSB_SPROM8_TEMPDELTA_PHYCAL_SHIFT);
 | 
			
		||||
	SPEX(temps_period, SSB_SPROM8_TEMPDELTA, SSB_SPROM8_TEMPDELTA_PERIOD,
 | 
			
		||||
	     SSB_SPROM8_TEMPDELTA_PERIOD_SHIFT);
 | 
			
		||||
	SPEX(temps_hysteresis, SSB_SPROM8_TEMPDELTA,
 | 
			
		||||
	     SSB_SPROM8_TEMPDELTA_HYSTERESIS,
 | 
			
		||||
	     SSB_SPROM8_TEMPDELTA_HYSTERESIS_SHIFT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sprom_extract(struct bcma_fbs *priv, const u16 *in, u16 size)
 | 
			
		||||
{
 | 
			
		||||
	struct ssb_sprom *out = &priv->sprom;
 | 
			
		||||
 | 
			
		||||
	memset(out, 0, sizeof(*out));
 | 
			
		||||
 | 
			
		||||
	out->revision = in[size - 1] & 0x00FF;
 | 
			
		||||
	if (out->revision < 8 || out->revision > 11) {
 | 
			
		||||
		dev_warn(priv->dev,
 | 
			
		||||
			 "Unsupported SPROM revision %d detected."
 | 
			
		||||
			 " Will extract v8\n",
 | 
			
		||||
			 out->revision);
 | 
			
		||||
		out->revision = 8;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sprom_extract_r8(out, in);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bcma_fbs_fixup(struct bcma_fbs *priv, u16 *sprom)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *node = priv->dev->of_node;
 | 
			
		||||
	u32 fixups, off, val;
 | 
			
		||||
	int i = 0;
 | 
			
		||||
 | 
			
		||||
	if (!of_get_property(node, "brcm,sprom-fixups", &fixups))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	fixups /= sizeof(u32);
 | 
			
		||||
 | 
			
		||||
	dev_info(priv->dev, "patching SPROM with %u fixups...\n", fixups >> 1);
 | 
			
		||||
 | 
			
		||||
	while (i < fixups) {
 | 
			
		||||
		if (of_property_read_u32_index(node, "brcm,sprom-fixups",
 | 
			
		||||
					       i++, &off)) {
 | 
			
		||||
			dev_err(priv->dev, "error reading fixup[%u] offset\n",
 | 
			
		||||
				i - 1);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (of_property_read_u32_index(node, "brcm,sprom-fixups",
 | 
			
		||||
					       i++, &val)) {
 | 
			
		||||
			dev_err(priv->dev, "error reading fixup[%u] value\n",
 | 
			
		||||
				i - 1);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dev_dbg(priv->dev, "fixup[%d]=0x%04x\n", off, val);
 | 
			
		||||
 | 
			
		||||
		sprom[off] = val;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool sprom_override_devid(struct bcma_fbs *priv, struct ssb_sprom *out,
 | 
			
		||||
				 const u16 *in)
 | 
			
		||||
{
 | 
			
		||||
	SPEX(dev_id, 0x0060, 0xFFFF, 0);
 | 
			
		||||
	return !!out->dev_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void bcma_fbs_set(struct bcma_fbs *priv, struct device_node *node)
 | 
			
		||||
{
 | 
			
		||||
	struct ssb_sprom *sprom = &priv->sprom;
 | 
			
		||||
	const struct firmware *fw;
 | 
			
		||||
	const char *sprom_name;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	if (of_property_read_string(node, "brcm,sprom", &sprom_name))
 | 
			
		||||
		sprom_name = NULL;
 | 
			
		||||
 | 
			
		||||
	if (sprom_name) {
 | 
			
		||||
		err = request_firmware_direct(&fw, sprom_name, priv->dev);
 | 
			
		||||
		if (err)
 | 
			
		||||
			dev_err(priv->dev, "%s load error\n", sprom_name);
 | 
			
		||||
	} else {
 | 
			
		||||
		err = -ENOENT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (err) {
 | 
			
		||||
		sprom->revision = 0x02;
 | 
			
		||||
		sprom->board_rev = 0x0017;
 | 
			
		||||
		sprom->country_code = 0x00;
 | 
			
		||||
		sprom->ant_available_bg = 0x03;
 | 
			
		||||
		sprom->pa0b0 = 0x15ae;
 | 
			
		||||
		sprom->pa0b1 = 0xfa85;
 | 
			
		||||
		sprom->pa0b2 = 0xfe8d;
 | 
			
		||||
		sprom->pa1b0 = 0xffff;
 | 
			
		||||
		sprom->pa1b1 = 0xffff;
 | 
			
		||||
		sprom->pa1b2 = 0xffff;
 | 
			
		||||
		sprom->gpio0 = 0xff;
 | 
			
		||||
		sprom->gpio1 = 0xff;
 | 
			
		||||
		sprom->gpio2 = 0xff;
 | 
			
		||||
		sprom->gpio3 = 0xff;
 | 
			
		||||
		sprom->maxpwr_bg = 0x4c;
 | 
			
		||||
		sprom->itssi_bg = 0x00;
 | 
			
		||||
		sprom->boardflags_lo = 0x2848;
 | 
			
		||||
		sprom->boardflags_hi = 0x0000;
 | 
			
		||||
		priv->devid_override = false;
 | 
			
		||||
 | 
			
		||||
		dev_warn(priv->dev, "using basic SPROM\n");
 | 
			
		||||
	} else {
 | 
			
		||||
		size_t size = min(fw->size, (size_t) BCMA_FBS_MAX_SIZE);
 | 
			
		||||
		u16 tmp_sprom[BCMA_FBS_MAX_SIZE >> 1];
 | 
			
		||||
		u32 i, j;
 | 
			
		||||
 | 
			
		||||
		for (i = 0, j = 0; i < size; i += 2, j++)
 | 
			
		||||
			tmp_sprom[j] = (fw->data[i] << 8) | fw->data[i + 1];
 | 
			
		||||
 | 
			
		||||
		release_firmware(fw);
 | 
			
		||||
		bcma_fbs_fixup(priv, tmp_sprom);
 | 
			
		||||
		sprom_extract(priv, tmp_sprom, size >> 1);
 | 
			
		||||
 | 
			
		||||
		priv->devid_override = sprom_override_devid(priv, sprom,
 | 
			
		||||
							    tmp_sprom);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int bcma_fbs_probe(struct platform_device *pdev)
 | 
			
		||||
{
 | 
			
		||||
	struct device *dev = &pdev->dev;
 | 
			
		||||
	struct device_node *node = dev->of_node;
 | 
			
		||||
	struct bcma_fbs *priv;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	u8 mac[ETH_ALEN];
 | 
			
		||||
 | 
			
		||||
	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 | 
			
		||||
	if (!priv)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	priv->dev = dev;
 | 
			
		||||
 | 
			
		||||
	bcma_fbs_set(priv, node);
 | 
			
		||||
 | 
			
		||||
	of_property_read_u32(node, "pci-bus", &priv->pci_bus);
 | 
			
		||||
	of_property_read_u32(node, "pci-dev", &priv->pci_dev);
 | 
			
		||||
 | 
			
		||||
	of_get_mac_address(node, mac);
 | 
			
		||||
	if (is_valid_ether_addr(mac)) {
 | 
			
		||||
		dev_info(dev, "mtd mac %pM\n", mac);
 | 
			
		||||
	} else {
 | 
			
		||||
		eth_random_addr(mac);
 | 
			
		||||
		dev_info(dev, "random mac %pM\n", mac);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memcpy(priv->sprom.il0mac, mac, ETH_ALEN);
 | 
			
		||||
 	memcpy(priv->sprom.et0mac, mac, ETH_ALEN);
 | 
			
		||||
 	memcpy(priv->sprom.et1mac, mac, ETH_ALEN);
 | 
			
		||||
	memcpy(priv->sprom.et2mac, mac, ETH_ALEN);
 | 
			
		||||
 | 
			
		||||
	spin_lock_irqsave(&bcma_fbs_lock, flags);
 | 
			
		||||
	list_add(&priv->list, &bcma_fbs_list);
 | 
			
		||||
	spin_unlock_irqrestore(&bcma_fbs_lock, flags);
 | 
			
		||||
 | 
			
		||||
	dev_info(dev, "registered SPROM for [%x:%x]\n",
 | 
			
		||||
		 priv->pci_bus, priv->pci_dev);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id bcma_fbs_of_match[] = {
 | 
			
		||||
	{ .compatible = "brcm,bcma-sprom", },
 | 
			
		||||
	{ /* sentinel */ }
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, bcma_fbs_of_match);
 | 
			
		||||
 | 
			
		||||
static struct platform_driver bcma_fbs_driver = {
 | 
			
		||||
	.probe = bcma_fbs_probe,
 | 
			
		||||
	.driver	= {
 | 
			
		||||
		.name = "bcma-sprom",
 | 
			
		||||
		.of_match_table = bcma_fbs_of_match,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int __init bcma_fbs_register(void)
 | 
			
		||||
{
 | 
			
		||||
	return platform_driver_register(&bcma_fbs_driver);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
#ifndef _FALLBACK_SPROM_H
 | 
			
		||||
#define _FALLBACK_SPROM_H
 | 
			
		||||
 | 
			
		||||
int __init bcma_fbs_register(void);
 | 
			
		||||
int bcma_get_fallback_sprom(struct bcma_bus *dev, struct ssb_sprom *out);
 | 
			
		||||
 | 
			
		||||
#endif /* _FALLBACK_SPROM_H */
 | 
			
		||||
							
								
								
									
										112
									
								
								6.12/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								6.12/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,112 @@
 | 
			
		|||
config MTD_SPLIT
 | 
			
		||||
	def_bool n
 | 
			
		||||
	help
 | 
			
		||||
	  Generic MTD split support.
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_SUPPORT
 | 
			
		||||
	def_bool MTD = y
 | 
			
		||||
 | 
			
		||||
comment "Rootfs partition parsers"
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_SQUASHFS_ROOT
 | 
			
		||||
	bool "Squashfs based root partition parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
	help
 | 
			
		||||
	  This provides a parsing function which allows to detect the
 | 
			
		||||
	  offset and size of the unused portion of a rootfs partition
 | 
			
		||||
	  containing a squashfs.
 | 
			
		||||
 | 
			
		||||
comment "Firmware partition parsers"
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_BCM63XX_FW
 | 
			
		||||
	bool "BCM63xx firmware parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_BCM_WFI_FW
 | 
			
		||||
	bool "Broadcom Whole Flash Image parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_CFE_BOOTFS
 | 
			
		||||
	bool "Parser finding rootfs appended to the CFE bootfs"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT && (ARCH_BCM4908 || ARCH_BCMBCA)
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
	help
 | 
			
		||||
	  cferom on BCM4908 (and bcm63xx) uses JFFS2 bootfs partition
 | 
			
		||||
	  for storing kernel, cferam and some device specific files.
 | 
			
		||||
	  There isn't any straight way of storing rootfs so it gets
 | 
			
		||||
	  appended to the JFFS2 bootfs partition. Kernel needs to find
 | 
			
		||||
	  it and run init from it. This parser is responsible for
 | 
			
		||||
	  finding appended rootfs.
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_SEAMA_FW
 | 
			
		||||
	bool "Seama firmware parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_WRGG_FW
 | 
			
		||||
	bool "WRGG firmware parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_UIMAGE_FW
 | 
			
		||||
	bool "uImage based firmware partition parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_FIT_FW
 | 
			
		||||
	bool "FIT based firmware partition parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_LZMA_FW
 | 
			
		||||
	bool "LZMA compressed kernel based firmware partition parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_TPLINK_FW
 | 
			
		||||
	bool "TP-Link firmware parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_TRX_FW
 | 
			
		||||
	bool "TRX image based firmware partition parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_BRNIMAGE_FW
 | 
			
		||||
	bool "brnImage (brnboot image) firmware parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_EVA_FW
 | 
			
		||||
	bool "EVA image based firmware partition parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_MINOR_FW
 | 
			
		||||
	bool "Mikrotik NOR image based firmware partition parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_JIMAGE_FW
 | 
			
		||||
	bool "JBOOT Image based firmware partition parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_ELF_FW
 | 
			
		||||
	bool "ELF loader firmware partition parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_H3C_VFS
 | 
			
		||||
	bool "Parser finding rootfs appended to H3C VFS"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
 | 
			
		||||
config MTD_SPLIT_SEIL_FW
 | 
			
		||||
	bool "IIJ SEIL firmware parser"
 | 
			
		||||
	depends on MTD_SPLIT_SUPPORT
 | 
			
		||||
	select MTD_SPLIT
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
obj-$(CONFIG_MTD_SPLIT)		+= mtdsplit.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_BCM63XX_FW) += mtdsplit_bcm63xx.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_BCM_WFI_FW) += mtdsplit_bcm_wfi.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_CFE_BOOTFS) += mtdsplit_cfe_bootfs.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_SEAMA_FW) += mtdsplit_seama.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_SEIL_FW) += mtdsplit_seil.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_SQUASHFS_ROOT) += mtdsplit_squashfs.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_UIMAGE_FW) += mtdsplit_uimage.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_FIT_FW) += mtdsplit_fit.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_LZMA_FW) += mtdsplit_lzma.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_TPLINK_FW) += mtdsplit_tplink.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_TRX_FW) += mtdsplit_trx.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_BRNIMAGE_FW) += mtdsplit_brnimage.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_EVA_FW) += mtdsplit_eva.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_WRGG_FW) += mtdsplit_wrgg.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_MINOR_FW) += mtdsplit_minor.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_JIMAGE_FW) += mtdsplit_jimage.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_ELF_FW) += mtdsplit_elf.o
 | 
			
		||||
obj-$(CONFIG_MTD_SPLIT_H3C_VFS) += mtdsplit_h3c_vfs.o
 | 
			
		||||
							
								
								
									
										130
									
								
								6.12/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								6.12/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,130 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (C) 2009-2013 Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
 * Copyright (C) 2009-2013 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 * Copyright (C) 2012 Jonas Gorski <jogo@openwrt.org>
 | 
			
		||||
 * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 * by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define pr_fmt(fmt)	"mtdsplit: " fmt
 | 
			
		||||
 | 
			
		||||
#include <linux/export.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/magic.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define UBI_EC_MAGIC			0x55424923	/* UBI# */
 | 
			
		||||
 | 
			
		||||
struct squashfs_super_block {
 | 
			
		||||
	__le32 s_magic;
 | 
			
		||||
	__le32 pad0[9];
 | 
			
		||||
	__le64 bytes_used;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int mtd_get_squashfs_len(struct mtd_info *master,
 | 
			
		||||
			 size_t offset,
 | 
			
		||||
			 size_t *squashfs_len)
 | 
			
		||||
{
 | 
			
		||||
	struct squashfs_super_block sb;
 | 
			
		||||
	size_t retlen;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	err = mtd_read(master, offset, sizeof(sb), &retlen, (void *)&sb);
 | 
			
		||||
	if (err || (retlen != sizeof(sb))) {
 | 
			
		||||
		pr_alert("error occured while reading from \"%s\"\n",
 | 
			
		||||
			 master->name);
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (le32_to_cpu(sb.s_magic) != SQUASHFS_MAGIC) {
 | 
			
		||||
		pr_alert("no squashfs found in \"%s\"\n", master->name);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	retlen = le64_to_cpu(sb.bytes_used);
 | 
			
		||||
	if (retlen <= 0) {
 | 
			
		||||
		pr_alert("squashfs is empty in \"%s\"\n", master->name);
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (offset + retlen > master->size) {
 | 
			
		||||
		pr_alert("squashfs has invalid size in \"%s\"\n",
 | 
			
		||||
			 master->name);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*squashfs_len = retlen;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(mtd_get_squashfs_len);
 | 
			
		||||
 | 
			
		||||
static ssize_t mtd_next_eb(struct mtd_info *mtd, size_t offset)
 | 
			
		||||
{
 | 
			
		||||
	return mtd_rounddown_to_eb(offset, mtd) + mtd->erasesize;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
 | 
			
		||||
			   enum mtdsplit_part_type *type)
 | 
			
		||||
{
 | 
			
		||||
	u32 magic;
 | 
			
		||||
	size_t retlen;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = mtd_read(mtd, offset, sizeof(magic), &retlen,
 | 
			
		||||
		       (unsigned char *) &magic);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	if (retlen != sizeof(magic))
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	if (le32_to_cpu(magic) == SQUASHFS_MAGIC) {
 | 
			
		||||
		if (type)
 | 
			
		||||
			*type = MTDSPLIT_PART_TYPE_SQUASHFS;
 | 
			
		||||
		return 0;
 | 
			
		||||
	} else if (magic == 0x19852003) {
 | 
			
		||||
		if (type)
 | 
			
		||||
			*type = MTDSPLIT_PART_TYPE_JFFS2;
 | 
			
		||||
		return 0;
 | 
			
		||||
	} else if (be32_to_cpu(magic) == UBI_EC_MAGIC) {
 | 
			
		||||
		if (type)
 | 
			
		||||
			*type = MTDSPLIT_PART_TYPE_UBI;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(mtd_check_rootfs_magic);
 | 
			
		||||
 | 
			
		||||
int mtd_find_rootfs_from(struct mtd_info *mtd,
 | 
			
		||||
			 size_t from,
 | 
			
		||||
			 size_t limit,
 | 
			
		||||
			 size_t *ret_offset,
 | 
			
		||||
			 enum mtdsplit_part_type *type)
 | 
			
		||||
{
 | 
			
		||||
	size_t offset;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	for (offset = from; offset < limit;
 | 
			
		||||
	     offset = mtd_next_eb(mtd, offset)) {
 | 
			
		||||
		err = mtd_check_rootfs_magic(mtd, offset, type);
 | 
			
		||||
		if (err)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		*ret_offset = offset;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -ENODEV;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(mtd_find_rootfs_from);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,67 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (C) 2009-2013 Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
 * Copyright (C) 2009-2013 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 * Copyright (C) 2012 Jonas Gorski <jogo@openwrt.org>
 | 
			
		||||
 * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 * by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _MTDSPLIT_H
 | 
			
		||||
#define _MTDSPLIT_H
 | 
			
		||||
 | 
			
		||||
#define KERNEL_PART_NAME	"kernel"
 | 
			
		||||
#define ROOTFS_PART_NAME	"rootfs"
 | 
			
		||||
#define UBI_PART_NAME		"ubi"
 | 
			
		||||
 | 
			
		||||
#define ROOTFS_SPLIT_NAME	"rootfs_data"
 | 
			
		||||
 | 
			
		||||
enum mtdsplit_part_type {
 | 
			
		||||
	MTDSPLIT_PART_TYPE_UNK = 0,
 | 
			
		||||
	MTDSPLIT_PART_TYPE_SQUASHFS,
 | 
			
		||||
	MTDSPLIT_PART_TYPE_JFFS2,
 | 
			
		||||
	MTDSPLIT_PART_TYPE_UBI,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_MTD_SPLIT
 | 
			
		||||
int mtd_get_squashfs_len(struct mtd_info *master,
 | 
			
		||||
			 size_t offset,
 | 
			
		||||
			 size_t *squashfs_len);
 | 
			
		||||
 | 
			
		||||
int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
 | 
			
		||||
			   enum mtdsplit_part_type *type);
 | 
			
		||||
 | 
			
		||||
int mtd_find_rootfs_from(struct mtd_info *mtd,
 | 
			
		||||
			 size_t from,
 | 
			
		||||
			 size_t limit,
 | 
			
		||||
			 size_t *ret_offset,
 | 
			
		||||
			 enum mtdsplit_part_type *type);
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
static inline int mtd_get_squashfs_len(struct mtd_info *master,
 | 
			
		||||
				       size_t offset,
 | 
			
		||||
				       size_t *squashfs_len)
 | 
			
		||||
{
 | 
			
		||||
	return -ENODEV;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
 | 
			
		||||
					 enum mtdsplit_part_type *type)
 | 
			
		||||
{
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int mtd_find_rootfs_from(struct mtd_info *mtd,
 | 
			
		||||
				       size_t from,
 | 
			
		||||
				       size_t limit,
 | 
			
		||||
				       size_t *ret_offset,
 | 
			
		||||
				       enum mtdsplit_part_type *type)
 | 
			
		||||
{
 | 
			
		||||
	return -ENODEV;
 | 
			
		||||
}
 | 
			
		||||
#endif /* CONFIG_MTD_SPLIT */
 | 
			
		||||
 | 
			
		||||
#endif /* _MTDSPLIT_H */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,186 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Firmware MTD split for BCM63XX, based on bcm63xxpart.c
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2006-2008 Florian Fainelli <florian@openwrt.org>
 | 
			
		||||
 * Copyright (C) 2006-2008 Mike Albon <malbon@openwrt.org>
 | 
			
		||||
 * Copyright (C) 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net>
 | 
			
		||||
 * Copyright (C) 2011-2013 Jonas Gorski <jonas.gorski@gmail.com>
 | 
			
		||||
 * Copyright (C) 2015 Simon Arlott <simon@fire.lp0.eu>
 | 
			
		||||
 * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 * by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 | 
			
		||||
 | 
			
		||||
#include <linux/bcm963xx_tag.h>
 | 
			
		||||
#include <linux/crc32.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
/* Ensure strings read from flash structs are null terminated */
 | 
			
		||||
#define STR_NULL_TERMINATE(x) \
 | 
			
		||||
	do { char *_str = (x); _str[sizeof(x) - 1] = 0; } while (0)
 | 
			
		||||
 | 
			
		||||
#define BCM63XX_NR_PARTS 2
 | 
			
		||||
 | 
			
		||||
static int bcm63xx_read_image_tag(struct mtd_info *master, loff_t offset,
 | 
			
		||||
				  struct bcm_tag *hdr)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	size_t retlen;
 | 
			
		||||
	u32 computed_crc;
 | 
			
		||||
 | 
			
		||||
	ret = mtd_read(master, offset, sizeof(*hdr), &retlen, (void *) hdr);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	if (retlen != sizeof(*hdr))
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	computed_crc = crc32_le(IMAGETAG_CRC_START, (u8 *)hdr,
 | 
			
		||||
				offsetof(struct bcm_tag, header_crc));
 | 
			
		||||
	if (computed_crc == hdr->header_crc) {
 | 
			
		||||
	    STR_NULL_TERMINATE(hdr->board_id);
 | 
			
		||||
	    STR_NULL_TERMINATE(hdr->tag_version);
 | 
			
		||||
 | 
			
		||||
		pr_info("CFE image tag found at 0x%llx with version %s, "
 | 
			
		||||
			"board type %s\n", offset, hdr->tag_version,
 | 
			
		||||
			hdr->board_id);
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	} else {
 | 
			
		||||
		pr_err("CFE image tag at 0x%llx CRC invalid "
 | 
			
		||||
		       "(expected %08x, actual %08x)\n",
 | 
			
		||||
		       offset, hdr->header_crc, computed_crc);
 | 
			
		||||
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int bcm63xx_parse_partitions(struct mtd_info *master,
 | 
			
		||||
				    const struct mtd_partition **pparts,
 | 
			
		||||
				    struct bcm_tag *hdr)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	unsigned int flash_image_start;
 | 
			
		||||
	unsigned int kernel_address;
 | 
			
		||||
	unsigned int kernel_length;
 | 
			
		||||
	size_t kernel_offset = 0, kernel_size = 0;
 | 
			
		||||
	size_t rootfs_offset = 0, rootfs_size = 0;
 | 
			
		||||
	int kernel_part, rootfs_part;
 | 
			
		||||
 | 
			
		||||
	STR_NULL_TERMINATE(hdr->flash_image_start);
 | 
			
		||||
	if (kstrtouint(hdr->flash_image_start, 10, &flash_image_start) ||
 | 
			
		||||
	    flash_image_start < BCM963XX_EXTENDED_SIZE) {
 | 
			
		||||
		pr_err("invalid rootfs address: %*ph\n",
 | 
			
		||||
		       (int) sizeof(hdr->flash_image_start),
 | 
			
		||||
		       hdr->flash_image_start);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	STR_NULL_TERMINATE(hdr->kernel_address);
 | 
			
		||||
	if (kstrtouint(hdr->kernel_address, 10, &kernel_address) ||
 | 
			
		||||
	    kernel_address < BCM963XX_EXTENDED_SIZE) {
 | 
			
		||||
		pr_err("invalid kernel address: %*ph\n",
 | 
			
		||||
		       (int) sizeof(hdr->kernel_address), hdr->kernel_address);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	STR_NULL_TERMINATE(hdr->kernel_length);
 | 
			
		||||
	if (kstrtouint(hdr->kernel_length, 10, &kernel_length) ||
 | 
			
		||||
	    !kernel_length) {
 | 
			
		||||
		pr_err("invalid kernel length: %*ph\n",
 | 
			
		||||
		       (int) sizeof(hdr->kernel_length), hdr->kernel_length);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kernel_offset = kernel_address - BCM963XX_EXTENDED_SIZE -
 | 
			
		||||
			mtdpart_get_offset(master);
 | 
			
		||||
	kernel_size = kernel_length;
 | 
			
		||||
 | 
			
		||||
	if (flash_image_start < kernel_address) {
 | 
			
		||||
		/* rootfs first */
 | 
			
		||||
		rootfs_part = 0;
 | 
			
		||||
		kernel_part = 1;
 | 
			
		||||
		rootfs_offset = flash_image_start - BCM963XX_EXTENDED_SIZE -
 | 
			
		||||
				mtdpart_get_offset(master);
 | 
			
		||||
		rootfs_size = kernel_offset - rootfs_offset;
 | 
			
		||||
	} else {
 | 
			
		||||
		/* kernel first */
 | 
			
		||||
		kernel_part = 0;
 | 
			
		||||
		rootfs_part = 1;
 | 
			
		||||
		rootfs_offset = kernel_offset + kernel_size;
 | 
			
		||||
		rootfs_size = master->size - rootfs_offset;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (mtd_check_rootfs_magic(master, rootfs_offset, NULL))
 | 
			
		||||
		pr_warn("rootfs magic not found\n");
 | 
			
		||||
 | 
			
		||||
	parts = kzalloc(BCM63XX_NR_PARTS * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	parts[kernel_part].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[kernel_part].offset = kernel_offset;
 | 
			
		||||
	parts[kernel_part].size = kernel_size;
 | 
			
		||||
 | 
			
		||||
	parts[rootfs_part].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[rootfs_part].offset = rootfs_offset;
 | 
			
		||||
	parts[rootfs_part].size = rootfs_size;
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return BCM63XX_NR_PARTS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_parse_bcm63xx(struct mtd_info *master,
 | 
			
		||||
				  const struct mtd_partition **pparts,
 | 
			
		||||
				  struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct bcm_tag hdr;
 | 
			
		||||
	loff_t offset;
 | 
			
		||||
 | 
			
		||||
	if (mtd_type_is_nand(master))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* find bcm63xx_cfe image on erase block boundaries */
 | 
			
		||||
	for (offset = 0; offset < master->size; offset += master->erasesize) {
 | 
			
		||||
		if (!bcm63xx_read_image_tag(master, offset, (void *) &hdr))
 | 
			
		||||
			return bcm63xx_parse_partitions(master, pparts,
 | 
			
		||||
							(void *) &hdr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_fit_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "brcm,bcm963xx-imagetag" },
 | 
			
		||||
	{ },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_bcm63xx_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "bcm63xx-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_fit_of_match_table,
 | 
			
		||||
	.parse_fn = mtdsplit_parse_bcm63xx,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_bcm63xx_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&mtdsplit_bcm63xx_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module_init(mtdsplit_bcm63xx_init);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,535 @@
 | 
			
		|||
/*
 | 
			
		||||
 * MTD split for Broadcom Whole Flash Image
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 * by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define je16_to_cpu(x) ((x).v16)
 | 
			
		||||
#define je32_to_cpu(x) ((x).v32)
 | 
			
		||||
 | 
			
		||||
#include <linux/crc32.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/jffs2.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define char_to_num(c)		((c >= '0' && c <= '9') ? (c - '0') : (0))
 | 
			
		||||
 | 
			
		||||
#define BCM_WFI_PARTS		3
 | 
			
		||||
#define BCM_WFI_SPLIT_PARTS	2
 | 
			
		||||
 | 
			
		||||
#define CFERAM_NAME		"cferam"
 | 
			
		||||
#define CFERAM_NAME_LEN		(sizeof(CFERAM_NAME) - 1)
 | 
			
		||||
#define CFERAM_NAME_MAX_LEN	32
 | 
			
		||||
#define KERNEL_NAME		"vmlinux.lz"
 | 
			
		||||
#define KERNEL_NAME_LEN		(sizeof(KERNEL_NAME) - 1)
 | 
			
		||||
#define OPENWRT_NAME		"1-openwrt"
 | 
			
		||||
#define OPENWRT_NAME_LEN	(sizeof(OPENWRT_NAME) - 1)
 | 
			
		||||
 | 
			
		||||
#define UBI_MAGIC		0x55424923
 | 
			
		||||
 | 
			
		||||
#define CFE_MAGIC_PFX		"cferam."
 | 
			
		||||
#define CFE_MAGIC_PFX_LEN	(sizeof(CFE_MAGIC_PFX) - 1)
 | 
			
		||||
#define CFE_MAGIC		"cferam.000"
 | 
			
		||||
#define CFE_MAGIC_LEN		(sizeof(CFE_MAGIC) - 1)
 | 
			
		||||
#define SERCOMM_MAGIC_PFX	"eRcOmM."
 | 
			
		||||
#define SERCOMM_MAGIC_PFX_LEN	(sizeof(SERCOMM_MAGIC_PFX) - 1)
 | 
			
		||||
#define SERCOMM_MAGIC		"eRcOmM.000"
 | 
			
		||||
#define SERCOMM_MAGIC_LEN	(sizeof(SERCOMM_MAGIC) - 1)
 | 
			
		||||
 | 
			
		||||
#define PART_CFERAM		"cferam"
 | 
			
		||||
#define PART_FIRMWARE		"firmware"
 | 
			
		||||
#define PART_IMAGE_1		"img1"
 | 
			
		||||
#define PART_IMAGE_2		"img2"
 | 
			
		||||
 | 
			
		||||
static u32 jffs2_dirent_crc(struct jffs2_raw_dirent *node)
 | 
			
		||||
{
 | 
			
		||||
	return crc32(0, node, sizeof(struct jffs2_raw_dirent) - 8);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool jffs2_dirent_valid(struct jffs2_raw_dirent *node)
 | 
			
		||||
{
 | 
			
		||||
	return ((je16_to_cpu(node->magic) == JFFS2_MAGIC_BITMASK) &&
 | 
			
		||||
		(je16_to_cpu(node->nodetype) == JFFS2_NODETYPE_DIRENT) &&
 | 
			
		||||
		je32_to_cpu(node->ino) &&
 | 
			
		||||
		je32_to_cpu(node->node_crc) == jffs2_dirent_crc(node));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int jffs2_find_file(struct mtd_info *mtd, uint8_t *buf,
 | 
			
		||||
			   const char *name, size_t name_len,
 | 
			
		||||
			   loff_t *offs, loff_t size,
 | 
			
		||||
			   char **out_name, size_t *out_name_len)
 | 
			
		||||
{
 | 
			
		||||
	const loff_t end = *offs + size;
 | 
			
		||||
	struct jffs2_raw_dirent *node;
 | 
			
		||||
	bool valid = false;
 | 
			
		||||
	size_t retlen;
 | 
			
		||||
	uint16_t magic;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	for (; *offs < end; *offs += mtd->erasesize) {
 | 
			
		||||
		unsigned int block_offs = 0;
 | 
			
		||||
 | 
			
		||||
		/* Skip CFE erased blocks */
 | 
			
		||||
		rc = mtd_read(mtd, *offs, sizeof(magic), &retlen,
 | 
			
		||||
			      (void *) &magic);
 | 
			
		||||
		if (rc || retlen != sizeof(magic)) {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Skip blocks not starting with JFFS2 magic */
 | 
			
		||||
		if (magic != JFFS2_MAGIC_BITMASK)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* Read full block */
 | 
			
		||||
		rc = mtd_read(mtd, *offs, mtd->erasesize, &retlen,
 | 
			
		||||
			      (void *) buf);
 | 
			
		||||
		if (rc)
 | 
			
		||||
			return rc;
 | 
			
		||||
		if (retlen != mtd->erasesize)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		while (block_offs < mtd->erasesize) {
 | 
			
		||||
			node = (struct jffs2_raw_dirent *) &buf[block_offs];
 | 
			
		||||
 | 
			
		||||
			if (!jffs2_dirent_valid(node)) {
 | 
			
		||||
				block_offs += 4;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (!memcmp(node->name, OPENWRT_NAME,
 | 
			
		||||
				    OPENWRT_NAME_LEN)) {
 | 
			
		||||
				valid = true;
 | 
			
		||||
			} else if (!memcmp(node->name, name, name_len)) {
 | 
			
		||||
				if (!valid)
 | 
			
		||||
					return -EINVAL;
 | 
			
		||||
 | 
			
		||||
				if (out_name)
 | 
			
		||||
					*out_name = kstrndup(node->name,
 | 
			
		||||
							     node->nsize,
 | 
			
		||||
							     GFP_KERNEL);
 | 
			
		||||
 | 
			
		||||
				if (out_name_len)
 | 
			
		||||
					*out_name_len = node->nsize;
 | 
			
		||||
 | 
			
		||||
				return 0;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			block_offs += je32_to_cpu(node->totlen);
 | 
			
		||||
			block_offs = (block_offs + 0x3) & ~0x3;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -ENOENT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ubifs_find(struct mtd_info *mtd, loff_t *offs, loff_t size)
 | 
			
		||||
{
 | 
			
		||||
	const loff_t end = *offs + size;
 | 
			
		||||
	uint32_t magic;
 | 
			
		||||
	size_t retlen;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	for (; *offs < end; *offs += mtd->erasesize) {
 | 
			
		||||
		rc = mtd_read(mtd, *offs, sizeof(magic), &retlen,
 | 
			
		||||
			      (unsigned char *) &magic);
 | 
			
		||||
		if (rc || retlen != sizeof(magic))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (be32_to_cpu(magic) == UBI_MAGIC)
 | 
			
		||||
			return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -ENOENT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parse_bcm_wfi(struct mtd_info *master,
 | 
			
		||||
			 const struct mtd_partition **pparts,
 | 
			
		||||
			 uint8_t *buf, loff_t off, loff_t size, bool cfe_part)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *mtd_node;
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	loff_t cfe_off, kernel_off, rootfs_off;
 | 
			
		||||
	unsigned int num_parts = BCM_WFI_PARTS, cur_part = 0;
 | 
			
		||||
	const char *cferam_name = CFERAM_NAME;
 | 
			
		||||
	size_t cferam_name_len;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mtd_node = mtd_get_of_node(master);
 | 
			
		||||
	if (mtd_node)
 | 
			
		||||
		of_property_read_string(mtd_node, "brcm,cferam", &cferam_name);
 | 
			
		||||
 | 
			
		||||
	cferam_name_len = strnlen(cferam_name, CFERAM_NAME_MAX_LEN);
 | 
			
		||||
	if (cferam_name_len > 0)
 | 
			
		||||
		cferam_name_len--;
 | 
			
		||||
 | 
			
		||||
	if (cfe_part) {
 | 
			
		||||
		num_parts++;
 | 
			
		||||
		cfe_off = off;
 | 
			
		||||
 | 
			
		||||
		ret = jffs2_find_file(master, buf, cferam_name,
 | 
			
		||||
				      cferam_name_len, &cfe_off,
 | 
			
		||||
				      size - (cfe_off - off), NULL, NULL);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			return ret;
 | 
			
		||||
 | 
			
		||||
		kernel_off = cfe_off + master->erasesize;
 | 
			
		||||
	} else {
 | 
			
		||||
		kernel_off = off;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = jffs2_find_file(master, buf, KERNEL_NAME, KERNEL_NAME_LEN,
 | 
			
		||||
			      &kernel_off, size - (kernel_off - off),
 | 
			
		||||
			      NULL, NULL);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	rootfs_off = kernel_off + master->erasesize;
 | 
			
		||||
	ret = ubifs_find(master, &rootfs_off, size - (rootfs_off - off));
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	parts = kzalloc(num_parts * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	if (cfe_part) {
 | 
			
		||||
		parts[cur_part].name = PART_CFERAM;
 | 
			
		||||
		parts[cur_part].mask_flags = MTD_WRITEABLE;
 | 
			
		||||
		parts[cur_part].offset = cfe_off;
 | 
			
		||||
		parts[cur_part].size = kernel_off - cfe_off;
 | 
			
		||||
		cur_part++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parts[cur_part].name = PART_FIRMWARE;
 | 
			
		||||
	parts[cur_part].offset = kernel_off;
 | 
			
		||||
	parts[cur_part].size = size - (kernel_off - off);
 | 
			
		||||
	cur_part++;
 | 
			
		||||
 | 
			
		||||
	parts[cur_part].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[cur_part].offset = kernel_off;
 | 
			
		||||
	parts[cur_part].size = rootfs_off - kernel_off;
 | 
			
		||||
	cur_part++;
 | 
			
		||||
 | 
			
		||||
	parts[cur_part].name = UBI_PART_NAME;
 | 
			
		||||
	parts[cur_part].offset = rootfs_off;
 | 
			
		||||
	parts[cur_part].size = size - (rootfs_off - off);
 | 
			
		||||
	cur_part++;
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
 | 
			
		||||
	return num_parts;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_parse_bcm_wfi(struct mtd_info *master,
 | 
			
		||||
				  const struct mtd_partition **pparts,
 | 
			
		||||
				  struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *mtd_node;
 | 
			
		||||
	bool cfe_part = true;
 | 
			
		||||
	uint8_t *buf;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mtd_node = mtd_get_of_node(master);
 | 
			
		||||
	if (!mtd_node)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	buf = kzalloc(master->erasesize, GFP_KERNEL);
 | 
			
		||||
	if (!buf)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	if (of_property_read_bool(mtd_node, "brcm,no-cferam"))
 | 
			
		||||
		cfe_part = false;
 | 
			
		||||
 | 
			
		||||
	ret = parse_bcm_wfi(master, pparts, buf, 0, master->size, cfe_part);
 | 
			
		||||
 | 
			
		||||
	kfree(buf);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_bcm_wfi_of_match[] = {
 | 
			
		||||
	{ .compatible = "brcm,wfi" },
 | 
			
		||||
	{ },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_bcm_wfi_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "bcm-wfi-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_bcm_wfi_of_match,
 | 
			
		||||
	.parse_fn = mtdsplit_parse_bcm_wfi,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int cferam_bootflag_value(const char *name, size_t name_len)
 | 
			
		||||
{
 | 
			
		||||
	int rc = -ENOENT;
 | 
			
		||||
 | 
			
		||||
	if (name &&
 | 
			
		||||
	    (name_len >= CFE_MAGIC_LEN) &&
 | 
			
		||||
	    !memcmp(name, CFE_MAGIC_PFX, CFE_MAGIC_PFX_LEN)) {
 | 
			
		||||
		rc = char_to_num(name[CFE_MAGIC_PFX_LEN + 0]) * 100;
 | 
			
		||||
		rc += char_to_num(name[CFE_MAGIC_PFX_LEN + 1]) * 10;
 | 
			
		||||
		rc += char_to_num(name[CFE_MAGIC_PFX_LEN + 2]) * 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_parse_bcm_wfi_split(struct mtd_info *master,
 | 
			
		||||
					const struct mtd_partition **pparts,
 | 
			
		||||
					struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	loff_t cfe_off;
 | 
			
		||||
	loff_t img1_off = 0;
 | 
			
		||||
	loff_t img2_off = master->size / 2;
 | 
			
		||||
	loff_t img1_size = (img2_off - img1_off);
 | 
			
		||||
	loff_t img2_size = (master->size - img2_off);
 | 
			
		||||
	loff_t active_off, inactive_off;
 | 
			
		||||
	loff_t active_size, inactive_size;
 | 
			
		||||
	const char *inactive_name;
 | 
			
		||||
	uint8_t *buf;
 | 
			
		||||
	char *cfe1_name = NULL, *cfe2_name = NULL;
 | 
			
		||||
	size_t cfe1_size = 0, cfe2_size = 0;
 | 
			
		||||
	int ret;
 | 
			
		||||
	int bf1, bf2;
 | 
			
		||||
 | 
			
		||||
	buf = kzalloc(master->erasesize, GFP_KERNEL);
 | 
			
		||||
	if (!buf)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	cfe_off = img1_off;
 | 
			
		||||
	ret = jffs2_find_file(master, buf, CFERAM_NAME, CFERAM_NAME_LEN,
 | 
			
		||||
			      &cfe_off, img1_size, &cfe1_name, &cfe1_size);
 | 
			
		||||
 | 
			
		||||
	cfe_off = img2_off;
 | 
			
		||||
	ret = jffs2_find_file(master, buf, CFERAM_NAME, CFERAM_NAME_LEN,
 | 
			
		||||
			      &cfe_off, img2_size, &cfe2_name, &cfe2_size);
 | 
			
		||||
 | 
			
		||||
	bf1 = cferam_bootflag_value(cfe1_name, cfe1_size);
 | 
			
		||||
	if (bf1 >= 0)
 | 
			
		||||
		printk("cferam: bootflag1=%d\n", bf1);
 | 
			
		||||
 | 
			
		||||
	bf2 = cferam_bootflag_value(cfe2_name, cfe2_size);
 | 
			
		||||
	if (bf2 >= 0)
 | 
			
		||||
		printk("cferam: bootflag2=%d\n", bf2);
 | 
			
		||||
 | 
			
		||||
	kfree(cfe1_name);
 | 
			
		||||
	kfree(cfe2_name);
 | 
			
		||||
 | 
			
		||||
	if (bf1 >= bf2) {
 | 
			
		||||
		active_off = img1_off;
 | 
			
		||||
		active_size = img1_size;
 | 
			
		||||
		inactive_off = img2_off;
 | 
			
		||||
		inactive_size = img2_size;
 | 
			
		||||
		inactive_name = PART_IMAGE_2;
 | 
			
		||||
	} else {
 | 
			
		||||
		active_off = img2_off;
 | 
			
		||||
		active_size = img2_size;
 | 
			
		||||
		inactive_off = img1_off;
 | 
			
		||||
		inactive_size = img1_size;
 | 
			
		||||
		inactive_name = PART_IMAGE_1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = parse_bcm_wfi(master, pparts, buf, active_off, active_size, true);
 | 
			
		||||
 | 
			
		||||
	kfree(buf);
 | 
			
		||||
 | 
			
		||||
	if (ret > 0) {
 | 
			
		||||
		parts = kzalloc((ret + 1) * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
		if (!parts)
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
		memcpy(parts, *pparts, ret * sizeof(*parts));
 | 
			
		||||
		kfree(*pparts);
 | 
			
		||||
 | 
			
		||||
		parts[ret].name = inactive_name;
 | 
			
		||||
		parts[ret].offset = inactive_off;
 | 
			
		||||
		parts[ret].size = inactive_size;
 | 
			
		||||
		ret++;
 | 
			
		||||
 | 
			
		||||
		*pparts = parts;
 | 
			
		||||
	} else {
 | 
			
		||||
		parts = kzalloc(BCM_WFI_SPLIT_PARTS * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
 | 
			
		||||
		parts[0].name = PART_IMAGE_1;
 | 
			
		||||
		parts[0].offset = img1_off;
 | 
			
		||||
		parts[0].size = img1_size;
 | 
			
		||||
 | 
			
		||||
		parts[1].name = PART_IMAGE_2;
 | 
			
		||||
		parts[1].offset = img2_off;
 | 
			
		||||
		parts[1].size = img2_size;
 | 
			
		||||
 | 
			
		||||
		*pparts = parts;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_bcm_wfi_split_of_match[] = {
 | 
			
		||||
	{ .compatible = "brcm,wfi-split" },
 | 
			
		||||
	{ },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_bcm_wfi_split_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "bcm-wfi-split-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_bcm_wfi_split_of_match,
 | 
			
		||||
	.parse_fn = mtdsplit_parse_bcm_wfi_split,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int sercomm_bootflag_value(struct mtd_info *mtd, uint8_t *buf)
 | 
			
		||||
{
 | 
			
		||||
	size_t retlen;
 | 
			
		||||
	loff_t offs;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	for (offs = 0; offs < mtd->size; offs += mtd->erasesize) {
 | 
			
		||||
		rc = mtd_read(mtd, offs, SERCOMM_MAGIC_LEN, &retlen, buf);
 | 
			
		||||
		if (rc || retlen != SERCOMM_MAGIC_LEN)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (memcmp(buf, SERCOMM_MAGIC_PFX, SERCOMM_MAGIC_PFX_LEN))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		rc = char_to_num(buf[SERCOMM_MAGIC_PFX_LEN + 0]) * 100;
 | 
			
		||||
		rc += char_to_num(buf[SERCOMM_MAGIC_PFX_LEN + 1]) * 10;
 | 
			
		||||
		rc += char_to_num(buf[SERCOMM_MAGIC_PFX_LEN + 2]) * 1;
 | 
			
		||||
 | 
			
		||||
		return rc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -ENOENT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_parse_ser_wfi(struct mtd_info *master,
 | 
			
		||||
				  const struct mtd_partition **pparts,
 | 
			
		||||
				  struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	struct mtd_info *mtd_bf1, *mtd_bf2;
 | 
			
		||||
	loff_t img1_off = 0;
 | 
			
		||||
	loff_t img2_off = master->size / 2;
 | 
			
		||||
	loff_t img1_size = (img2_off - img1_off);
 | 
			
		||||
	loff_t img2_size = (master->size - img2_off);
 | 
			
		||||
	loff_t active_off, inactive_off;
 | 
			
		||||
	loff_t active_size, inactive_size;
 | 
			
		||||
	const char *inactive_name;
 | 
			
		||||
	uint8_t *buf;
 | 
			
		||||
	int bf1, bf2;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mtd_bf1 = get_mtd_device_nm("bootflag1");
 | 
			
		||||
	if (IS_ERR(mtd_bf1))
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	mtd_bf2 = get_mtd_device_nm("bootflag2");
 | 
			
		||||
	if (IS_ERR(mtd_bf2))
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	buf = kzalloc(master->erasesize, GFP_KERNEL);
 | 
			
		||||
	if (!buf)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	bf1 = sercomm_bootflag_value(mtd_bf1, buf);
 | 
			
		||||
	if (bf1 >= 0)
 | 
			
		||||
		printk("sercomm: bootflag1=%d\n", bf1);
 | 
			
		||||
 | 
			
		||||
	bf2 = sercomm_bootflag_value(mtd_bf2, buf);
 | 
			
		||||
	if (bf2 >= 0)
 | 
			
		||||
		printk("sercomm: bootflag2=%d\n", bf2);
 | 
			
		||||
 | 
			
		||||
	if (bf1 == bf2 && bf2 >= 0) {
 | 
			
		||||
		struct erase_info bf_erase;
 | 
			
		||||
 | 
			
		||||
		bf2 = -ENOENT;
 | 
			
		||||
		bf_erase.addr = 0;
 | 
			
		||||
		bf_erase.len = mtd_bf2->size;
 | 
			
		||||
		mtd_erase(mtd_bf2, &bf_erase);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (bf1 >= bf2) {
 | 
			
		||||
		active_off = img1_off;
 | 
			
		||||
		active_size = img1_size;
 | 
			
		||||
		inactive_off = img2_off;
 | 
			
		||||
		inactive_size = img2_size;
 | 
			
		||||
		inactive_name = PART_IMAGE_2;
 | 
			
		||||
	} else {
 | 
			
		||||
		active_off = img2_off;
 | 
			
		||||
		active_size = img2_size;
 | 
			
		||||
		inactive_off = img1_off;
 | 
			
		||||
		inactive_size = img1_size;
 | 
			
		||||
		inactive_name = PART_IMAGE_1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = parse_bcm_wfi(master, pparts, buf, active_off, active_size, false);
 | 
			
		||||
 | 
			
		||||
	kfree(buf);
 | 
			
		||||
 | 
			
		||||
	if (ret > 0) {
 | 
			
		||||
		parts = kzalloc((ret + 1) * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
		if (!parts)
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
		memcpy(parts, *pparts, ret * sizeof(*parts));
 | 
			
		||||
		kfree(*pparts);
 | 
			
		||||
 | 
			
		||||
		parts[ret].name = inactive_name;
 | 
			
		||||
		parts[ret].offset = inactive_off;
 | 
			
		||||
		parts[ret].size = inactive_size;
 | 
			
		||||
		ret++;
 | 
			
		||||
 | 
			
		||||
		*pparts = parts;
 | 
			
		||||
	} else {
 | 
			
		||||
		parts = kzalloc(BCM_WFI_SPLIT_PARTS * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
 | 
			
		||||
		parts[0].name = PART_IMAGE_1;
 | 
			
		||||
		parts[0].offset = img1_off;
 | 
			
		||||
		parts[0].size = img1_size;
 | 
			
		||||
 | 
			
		||||
		parts[1].name = PART_IMAGE_2;
 | 
			
		||||
		parts[1].offset = img2_off;
 | 
			
		||||
		parts[1].size = img2_size;
 | 
			
		||||
 | 
			
		||||
		*pparts = parts;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_ser_wfi_of_match[] = {
 | 
			
		||||
	{ .compatible = "sercomm,wfi" },
 | 
			
		||||
	{ },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_ser_wfi_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "ser-wfi-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_ser_wfi_of_match,
 | 
			
		||||
	.parse_fn = mtdsplit_parse_ser_wfi,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_bcm_wfi_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&mtdsplit_bcm_wfi_parser);
 | 
			
		||||
	register_mtd_parser(&mtdsplit_bcm_wfi_split_parser);
 | 
			
		||||
	register_mtd_parser(&mtdsplit_ser_wfi_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module_init(mtdsplit_bcm_wfi_init);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,104 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Copyright (C) 2012 John Crispin <blogic@openwrt.org>
 | 
			
		||||
 *  Copyright (C) 2015 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 *  by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define BRNIMAGE_NR_PARTS	2
 | 
			
		||||
 | 
			
		||||
#define BRNIMAGE_ALIGN_BYTES	0x400
 | 
			
		||||
#define BRNIMAGE_FOOTER_SIZE	12
 | 
			
		||||
 | 
			
		||||
#define BRNIMAGE_MIN_OVERHEAD	(BRNIMAGE_FOOTER_SIZE)
 | 
			
		||||
#define BRNIMAGE_MAX_OVERHEAD	(BRNIMAGE_ALIGN_BYTES + BRNIMAGE_FOOTER_SIZE)
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_parse_brnimage(struct mtd_info *master,
 | 
			
		||||
				const struct mtd_partition **pparts,
 | 
			
		||||
				struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	uint32_t buf;
 | 
			
		||||
	unsigned long rootfs_offset, rootfs_size, kernel_size;
 | 
			
		||||
	size_t len;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	for (rootfs_offset = 0; rootfs_offset < master->size;
 | 
			
		||||
	     rootfs_offset += BRNIMAGE_ALIGN_BYTES) {
 | 
			
		||||
		ret = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
 | 
			
		||||
		if (!ret)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	if (rootfs_offset >= master->size)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	ret = mtd_read(master, rootfs_offset - BRNIMAGE_FOOTER_SIZE, 4, &len,
 | 
			
		||||
			(void *)&buf);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	if (len != 4)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	kernel_size = le32_to_cpu(buf);
 | 
			
		||||
 | 
			
		||||
	if (kernel_size > (rootfs_offset - BRNIMAGE_MIN_OVERHEAD))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (kernel_size < (rootfs_offset - BRNIMAGE_MAX_OVERHEAD))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The footer must be untouched as it contains the checksum of the
 | 
			
		||||
	 * original brnImage (kernel + squashfs)!
 | 
			
		||||
	 */
 | 
			
		||||
	rootfs_size = master->size - rootfs_offset - BRNIMAGE_FOOTER_SIZE;
 | 
			
		||||
 | 
			
		||||
	parts = kzalloc(BRNIMAGE_NR_PARTS * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	parts[0].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[0].offset = 0;
 | 
			
		||||
	parts[0].size = kernel_size;
 | 
			
		||||
 | 
			
		||||
	parts[1].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[1].offset = rootfs_offset;
 | 
			
		||||
	parts[1].size = rootfs_size;
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return BRNIMAGE_NR_PARTS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_brnimage_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "brnimage-fw",
 | 
			
		||||
	.parse_fn = mtdsplit_parse_brnimage,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_brnimage_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&mtdsplit_brnimage_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subsys_initcall(mtdsplit_brnimage_init);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,90 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-only
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/jffs2.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define je16_to_cpu(x) ((x).v16)
 | 
			
		||||
#define je32_to_cpu(x) ((x).v32)
 | 
			
		||||
 | 
			
		||||
#define NR_PARTS		2
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_cfe_bootfs_parse(struct mtd_info *mtd,
 | 
			
		||||
				     const struct mtd_partition **pparts,
 | 
			
		||||
				     struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct jffs2_raw_dirent node;
 | 
			
		||||
	enum mtdsplit_part_type type;
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	size_t rootfs_offset;
 | 
			
		||||
	size_t retlen;
 | 
			
		||||
	size_t offset;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	/* Don't parse backup partitions */
 | 
			
		||||
	if (strcmp(mtd->name, "firmware"))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Find the end of JFFS2 bootfs partition */
 | 
			
		||||
	offset = 0;
 | 
			
		||||
	do {
 | 
			
		||||
		err = mtd_read(mtd, offset, sizeof(node), &retlen, (void *)&node);
 | 
			
		||||
		if (err || retlen != sizeof(node))
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		if (je16_to_cpu(node.magic) != JFFS2_MAGIC_BITMASK)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		offset += je32_to_cpu(node.totlen);
 | 
			
		||||
		offset = (offset + 0x3) & ~0x3;
 | 
			
		||||
	} while (offset < mtd->size);
 | 
			
		||||
 | 
			
		||||
	/* Find rootfs partition that follows the bootfs */
 | 
			
		||||
	err = mtd_find_rootfs_from(mtd, mtd->erasesize, mtd->size, &rootfs_offset, &type);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	parts = kzalloc(NR_PARTS * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	parts[0].name = "bootfs";
 | 
			
		||||
	parts[0].offset = 0;
 | 
			
		||||
	parts[0].size = rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	if (type == MTDSPLIT_PART_TYPE_UBI)
 | 
			
		||||
		parts[1].name = UBI_PART_NAME;
 | 
			
		||||
	else
 | 
			
		||||
		parts[1].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[1].offset = rootfs_offset;
 | 
			
		||||
	parts[1].size = mtd->size - rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
 | 
			
		||||
	return NR_PARTS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_cfe_bootfs_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "brcm,bcm4908-firmware" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, mtdsplit_cfe_bootfs_of_match_table);
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_cfe_bootfs_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "cfe-bootfs",
 | 
			
		||||
	.of_match_table = mtdsplit_cfe_bootfs_of_match_table,
 | 
			
		||||
	.parse_fn = mtdsplit_cfe_bootfs_parse,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module_mtd_part_parser(mtdsplit_cfe_bootfs_parser);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,287 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0-only */
 | 
			
		||||
/*
 | 
			
		||||
 *  MTD splitter for ELF loader firmware partitions
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2020 Sander Vanheule <sander@svanheule.net>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License as published by the Free
 | 
			
		||||
 *  Software Foundation; version 2.
 | 
			
		||||
 *
 | 
			
		||||
 *  To parse the ELF kernel loader, a small ELF parser is used that can
 | 
			
		||||
 *  handle both ELF32 or ELF64 class loaders. The splitter assumes that the
 | 
			
		||||
 *  kernel is always located before the rootfs, whether it is embedded in the
 | 
			
		||||
 *  loader or not.
 | 
			
		||||
 *
 | 
			
		||||
 *  The kernel image is preferably embedded inside the ELF loader, so the end
 | 
			
		||||
 *  of the loader equals the end of the kernel partition. This is due to the
 | 
			
		||||
 *  way mtd_find_rootfs_from searches for the the rootfs:
 | 
			
		||||
 *  - if the kernel image is embedded in the loader, the appended rootfs may
 | 
			
		||||
 *    follow the loader immediately, within the same erase block.
 | 
			
		||||
 *  - if the kernel image is not embedded in the loader, but placed at some
 | 
			
		||||
 *    offset behind the loader (OKLI-style loader), the rootfs must be
 | 
			
		||||
 *    aligned to an erase-block after the loader and kernel image.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define ELF_NR_PARTS	2
 | 
			
		||||
 | 
			
		||||
#define ELF_MAGIC	0x7f454c46 /* 0x7f E L F */
 | 
			
		||||
#define ELF_CLASS_32	1
 | 
			
		||||
#define ELF_CLASS_64	2
 | 
			
		||||
 | 
			
		||||
struct elf_header_ident {
 | 
			
		||||
	uint32_t magic;
 | 
			
		||||
	uint8_t class;
 | 
			
		||||
	uint8_t data;
 | 
			
		||||
	uint8_t version;
 | 
			
		||||
	uint8_t osabi;
 | 
			
		||||
	uint8_t abiversion;
 | 
			
		||||
	uint8_t pad[7];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct elf_header_32 {
 | 
			
		||||
	uint16_t type;
 | 
			
		||||
	uint16_t machine;
 | 
			
		||||
	uint32_t version;
 | 
			
		||||
	uint32_t entry;
 | 
			
		||||
	uint32_t phoff;
 | 
			
		||||
	uint32_t shoff;
 | 
			
		||||
	uint32_t flags;
 | 
			
		||||
	uint16_t ehsize;
 | 
			
		||||
	uint16_t phentsize;
 | 
			
		||||
	uint16_t phnum;
 | 
			
		||||
	uint16_t shentsize;
 | 
			
		||||
	uint16_t shnum;
 | 
			
		||||
	uint16_t shstrndx;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct elf_header_64 {
 | 
			
		||||
	uint16_t type;
 | 
			
		||||
	uint16_t machine;
 | 
			
		||||
	uint32_t version;
 | 
			
		||||
	uint64_t entry;
 | 
			
		||||
	uint64_t phoff;
 | 
			
		||||
	uint64_t shoff;
 | 
			
		||||
	uint32_t flags;
 | 
			
		||||
	uint16_t ehsize;
 | 
			
		||||
	uint16_t phentsize;
 | 
			
		||||
	uint16_t phnum;
 | 
			
		||||
	uint16_t shentsize;
 | 
			
		||||
	uint16_t shnum;
 | 
			
		||||
	uint16_t shstrndx;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct elf_header {
 | 
			
		||||
	struct elf_header_ident ident;
 | 
			
		||||
	union {
 | 
			
		||||
		struct elf_header_32 elf32;
 | 
			
		||||
		struct elf_header_64 elf64;
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct elf_program_header_32 {
 | 
			
		||||
	uint32_t type;
 | 
			
		||||
	uint32_t offset;
 | 
			
		||||
	uint32_t vaddr;
 | 
			
		||||
	uint32_t paddr;
 | 
			
		||||
	uint32_t filesize;
 | 
			
		||||
	uint32_t memsize;
 | 
			
		||||
	uint32_t flags;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct elf_program_header_64 {
 | 
			
		||||
	uint32_t type;
 | 
			
		||||
	uint32_t flags;
 | 
			
		||||
	uint64_t offset;
 | 
			
		||||
	uint64_t vaddr;
 | 
			
		||||
	uint64_t paddr;
 | 
			
		||||
	uint64_t filesize;
 | 
			
		||||
	uint64_t memsize;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_elf_read_mtd(struct mtd_info *mtd, size_t offset,
 | 
			
		||||
				 uint8_t *dst, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	size_t retlen;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = mtd_read(mtd, offset, len, &retlen, dst);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_debug("read error in \"%s\"\n", mtd->name);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (retlen != len) {
 | 
			
		||||
		pr_debug("short read in \"%s\"\n", mtd->name);
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int elf32_determine_size(struct mtd_info *mtd, struct elf_header *hdr,
 | 
			
		||||
				size_t *size)
 | 
			
		||||
{
 | 
			
		||||
	struct elf_header_32 *hdr32 = &(hdr->elf32);
 | 
			
		||||
	int err;
 | 
			
		||||
	size_t section_end, ph_table_end, ph_entry;
 | 
			
		||||
	struct elf_program_header_32 ph;
 | 
			
		||||
 | 
			
		||||
	*size = 0;
 | 
			
		||||
 | 
			
		||||
	if (hdr32->shoff > 0) {
 | 
			
		||||
		*size = hdr32->shoff + hdr32->shentsize * hdr32->shnum;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ph_entry = hdr32->phoff;
 | 
			
		||||
	ph_table_end = hdr32->phoff + hdr32->phentsize * hdr32->phnum;
 | 
			
		||||
 | 
			
		||||
	while (ph_entry < ph_table_end) {
 | 
			
		||||
		err = mtdsplit_elf_read_mtd(mtd, ph_entry, (uint8_t *)(&ph),
 | 
			
		||||
					    sizeof(ph));
 | 
			
		||||
		if (err)
 | 
			
		||||
			return err;
 | 
			
		||||
 | 
			
		||||
		section_end = ph.offset + ph.filesize;
 | 
			
		||||
		if (section_end > *size)
 | 
			
		||||
			*size = section_end;
 | 
			
		||||
 | 
			
		||||
		ph_entry += hdr32->phentsize;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int elf64_determine_size(struct mtd_info *mtd, struct elf_header *hdr,
 | 
			
		||||
				size_t *size)
 | 
			
		||||
{
 | 
			
		||||
	struct elf_header_64 *hdr64 = &(hdr->elf64);
 | 
			
		||||
	int err;
 | 
			
		||||
	size_t section_end, ph_table_end, ph_entry;
 | 
			
		||||
	struct elf_program_header_64 ph;
 | 
			
		||||
 | 
			
		||||
	*size = 0;
 | 
			
		||||
 | 
			
		||||
	if (hdr64->shoff > 0) {
 | 
			
		||||
		*size = hdr64->shoff + hdr64->shentsize * hdr64->shnum;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ph_entry = hdr64->phoff;
 | 
			
		||||
	ph_table_end = hdr64->phoff + hdr64->phentsize * hdr64->phnum;
 | 
			
		||||
 | 
			
		||||
	while (ph_entry < ph_table_end) {
 | 
			
		||||
		err = mtdsplit_elf_read_mtd(mtd, ph_entry, (uint8_t *)(&ph),
 | 
			
		||||
					    sizeof(ph));
 | 
			
		||||
		if (err)
 | 
			
		||||
			return err;
 | 
			
		||||
 | 
			
		||||
		section_end = ph.offset + ph.filesize;
 | 
			
		||||
		if (section_end > *size)
 | 
			
		||||
			*size = section_end;
 | 
			
		||||
 | 
			
		||||
		ph_entry += hdr64->phentsize;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_parse_elf(struct mtd_info *mtd,
 | 
			
		||||
			      const struct mtd_partition **pparts,
 | 
			
		||||
			      struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct elf_header hdr;
 | 
			
		||||
	size_t loader_size, rootfs_offset;
 | 
			
		||||
	enum mtdsplit_part_type type;
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	err = mtdsplit_elf_read_mtd(mtd, 0, (uint8_t *)&hdr, sizeof(hdr));
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	if (be32_to_cpu(hdr.ident.magic) != ELF_MAGIC) {
 | 
			
		||||
		pr_debug("invalid ELF magic %08x\n",
 | 
			
		||||
			 be32_to_cpu(hdr.ident.magic));
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (hdr.ident.class) {
 | 
			
		||||
	case ELF_CLASS_32:
 | 
			
		||||
		err = elf32_determine_size(mtd, &hdr, &loader_size);
 | 
			
		||||
		break;
 | 
			
		||||
	case ELF_CLASS_64:
 | 
			
		||||
		err = elf64_determine_size(mtd, &hdr, &loader_size);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		pr_debug("invalid ELF class %i\n", hdr.ident.class);
 | 
			
		||||
		err = -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	err = mtd_find_rootfs_from(mtd, loader_size, mtd->size,
 | 
			
		||||
				   &rootfs_offset, &type);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	if (rootfs_offset == mtd->size) {
 | 
			
		||||
		pr_debug("no rootfs found in \"%s\"\n", mtd->name);
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parts = kzalloc(ELF_NR_PARTS * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	parts[0].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[0].offset = 0;
 | 
			
		||||
	parts[0].size = rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	if (type == MTDSPLIT_PART_TYPE_UBI)
 | 
			
		||||
		parts[1].name = UBI_PART_NAME;
 | 
			
		||||
	else
 | 
			
		||||
		parts[1].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[1].offset = rootfs_offset;
 | 
			
		||||
	parts[1].size = mtd->size - rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return ELF_NR_PARTS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_elf_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "openwrt,elf" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, mtdsplit_elf_of_match_table);
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_elf_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "elf-loader-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_elf_of_match_table,
 | 
			
		||||
	.parse_fn = mtdsplit_parse_elf,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_elf_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&mtdsplit_elf_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subsys_initcall(mtdsplit_elf_init);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,103 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Copyright (C) 2012 John Crispin <blogic@openwrt.org>
 | 
			
		||||
 *  Copyright (C) 2015 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 *  by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define EVA_NR_PARTS		2
 | 
			
		||||
#define EVA_MAGIC		0xfeed1281
 | 
			
		||||
#define EVA_FOOTER_SIZE		0x18
 | 
			
		||||
#define EVA_DUMMY_SQUASHFS_SIZE	0x100
 | 
			
		||||
 | 
			
		||||
struct eva_image_header {
 | 
			
		||||
	uint32_t	magic;
 | 
			
		||||
	uint32_t	size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_parse_eva(struct mtd_info *master,
 | 
			
		||||
				const struct mtd_partition **pparts,
 | 
			
		||||
				struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	struct eva_image_header hdr;
 | 
			
		||||
	size_t retlen;
 | 
			
		||||
	unsigned long kernel_size, rootfs_offset;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	err = mtd_read(master, 0, sizeof(hdr), &retlen, (void *) &hdr);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	if (retlen != sizeof(hdr))
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	if (le32_to_cpu(hdr.magic) != EVA_MAGIC)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	kernel_size = le32_to_cpu(hdr.size) + EVA_FOOTER_SIZE;
 | 
			
		||||
 | 
			
		||||
	/* rootfs starts at the next 0x10000 boundary: */
 | 
			
		||||
	rootfs_offset = round_up(kernel_size, 0x10000);
 | 
			
		||||
 | 
			
		||||
	/* skip the dummy EVA squashfs partition (with wrong endianness): */
 | 
			
		||||
	rootfs_offset += EVA_DUMMY_SQUASHFS_SIZE;
 | 
			
		||||
 | 
			
		||||
	if (rootfs_offset >= master->size)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	err = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	parts = kzalloc(EVA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	parts[0].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[0].offset = 0;
 | 
			
		||||
	parts[0].size = kernel_size;
 | 
			
		||||
 | 
			
		||||
	parts[1].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[1].offset = rootfs_offset;
 | 
			
		||||
	parts[1].size = master->size - rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return EVA_NR_PARTS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_eva_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "avm,eva-firmware" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_eva_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "eva-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_eva_of_match_table,
 | 
			
		||||
	.parse_fn = mtdsplit_parse_eva,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_eva_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&mtdsplit_eva_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subsys_initcall(mtdsplit_eva_init);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,365 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2015 The Linux Foundation
 | 
			
		||||
 * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and/or distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/libfdt.h>
 | 
			
		||||
#include <linux/of_fdt.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
// string macros from git://git.denx.de/u-boot.git/include/image.h
 | 
			
		||||
 | 
			
		||||
#define FIT_IMAGES_PATH         "/images"
 | 
			
		||||
#define FIT_DATA_PROP           "data"
 | 
			
		||||
#define FIT_DATA_POSITION_PROP  "data-position"
 | 
			
		||||
#define FIT_DATA_OFFSET_PROP    "data-offset"
 | 
			
		||||
#define FIT_DATA_SIZE_PROP      "data-size"
 | 
			
		||||
 | 
			
		||||
// functions from git://git.denx.de/u-boot.git/common/image-fit.c
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * fit_image_get_data - get data property and its size for a given component image node
 | 
			
		||||
 * @fit: pointer to the FIT format image header
 | 
			
		||||
 * @noffset: component image node offset
 | 
			
		||||
 * @data: double pointer to void, will hold data property's data address
 | 
			
		||||
 * @size: pointer to size_t, will hold data property's data size
 | 
			
		||||
 *
 | 
			
		||||
 * fit_image_get_data() finds data property in a given component image node.
 | 
			
		||||
 * If the property is found its data start address and size are returned to
 | 
			
		||||
 * the caller.
 | 
			
		||||
 *
 | 
			
		||||
 * returns:
 | 
			
		||||
 *     0, on success
 | 
			
		||||
 *     -1, on failure
 | 
			
		||||
 */
 | 
			
		||||
static int fit_image_get_data(const void *fit, int noffset,
 | 
			
		||||
		const void **data, size_t *size)
 | 
			
		||||
{
 | 
			
		||||
	int len;
 | 
			
		||||
 | 
			
		||||
	*data = fdt_getprop(fit, noffset, FIT_DATA_PROP, &len);
 | 
			
		||||
	if (*data == NULL) {
 | 
			
		||||
		*size = 0;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*size = len;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get 'data-offset' property from a given image node.
 | 
			
		||||
 *
 | 
			
		||||
 * @fit: pointer to the FIT image header
 | 
			
		||||
 * @noffset: component image node offset
 | 
			
		||||
 * @data_offset: holds the data-offset property
 | 
			
		||||
 *
 | 
			
		||||
 * returns:
 | 
			
		||||
 *     0, on success
 | 
			
		||||
 *     -ENOENT if the property could not be found
 | 
			
		||||
 */
 | 
			
		||||
static int fit_image_get_data_offset(const void *fit, int noffset, int *data_offset)
 | 
			
		||||
{
 | 
			
		||||
	const fdt32_t *val;
 | 
			
		||||
 | 
			
		||||
	val = fdt_getprop(fit, noffset, FIT_DATA_OFFSET_PROP, NULL);
 | 
			
		||||
	if (!val)
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	*data_offset = fdt32_to_cpu(*val);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get 'data-position' property from a given image node.
 | 
			
		||||
 *
 | 
			
		||||
 * @fit: pointer to the FIT image header
 | 
			
		||||
 * @noffset: component image node offset
 | 
			
		||||
 * @data_position: holds the data-position property
 | 
			
		||||
 *
 | 
			
		||||
 * returns:
 | 
			
		||||
 *     0, on success
 | 
			
		||||
 *     -ENOENT if the property could not be found
 | 
			
		||||
 */
 | 
			
		||||
static int fit_image_get_data_position(const void *fit, int noffset,
 | 
			
		||||
				int *data_position)
 | 
			
		||||
{
 | 
			
		||||
	const fdt32_t *val;
 | 
			
		||||
 | 
			
		||||
	val = fdt_getprop(fit, noffset, FIT_DATA_POSITION_PROP, NULL);
 | 
			
		||||
	if (!val)
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	*data_position = fdt32_to_cpu(*val);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get 'data-size' property from a given image node.
 | 
			
		||||
 *
 | 
			
		||||
 * @fit: pointer to the FIT image header
 | 
			
		||||
 * @noffset: component image node offset
 | 
			
		||||
 * @data_size: holds the data-size property
 | 
			
		||||
 *
 | 
			
		||||
 * returns:
 | 
			
		||||
 *     0, on success
 | 
			
		||||
 *     -ENOENT if the property could not be found
 | 
			
		||||
 */
 | 
			
		||||
static int fit_image_get_data_size(const void *fit, int noffset, int *data_size)
 | 
			
		||||
{
 | 
			
		||||
	const fdt32_t *val;
 | 
			
		||||
 | 
			
		||||
	val = fdt_getprop(fit, noffset, FIT_DATA_SIZE_PROP, NULL);
 | 
			
		||||
	if (!val)
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	*data_size = fdt32_to_cpu(*val);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * fit_image_get_data_and_size - get data and its size including
 | 
			
		||||
 *				 both embedded and external data
 | 
			
		||||
 * @fit: pointer to the FIT format image header
 | 
			
		||||
 * @noffset: component image node offset
 | 
			
		||||
 * @data: double pointer to void, will hold data property's data address
 | 
			
		||||
 * @size: pointer to size_t, will hold data property's data size
 | 
			
		||||
 *
 | 
			
		||||
 * fit_image_get_data_and_size() finds data and its size including
 | 
			
		||||
 * both embedded and external data. If the property is found
 | 
			
		||||
 * its data start address and size are returned to the caller.
 | 
			
		||||
 *
 | 
			
		||||
 * returns:
 | 
			
		||||
 *     0, on success
 | 
			
		||||
 *     otherwise, on failure
 | 
			
		||||
 */
 | 
			
		||||
static int fit_image_get_data_and_size(const void *fit, int noffset,
 | 
			
		||||
				const void **data, size_t *size)
 | 
			
		||||
{
 | 
			
		||||
	bool external_data = false;
 | 
			
		||||
	int offset;
 | 
			
		||||
	int len;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (!fit_image_get_data_position(fit, noffset, &offset)) {
 | 
			
		||||
		external_data = true;
 | 
			
		||||
	} else if (!fit_image_get_data_offset(fit, noffset, &offset)) {
 | 
			
		||||
		external_data = true;
 | 
			
		||||
		/*
 | 
			
		||||
		 * For FIT with external data, figure out where
 | 
			
		||||
		 * the external images start. This is the base
 | 
			
		||||
		 * for the data-offset properties in each image.
 | 
			
		||||
		 */
 | 
			
		||||
		offset += ((fdt_totalsize(fit) + 3) & ~3);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (external_data) {
 | 
			
		||||
		ret = fit_image_get_data_size(fit, noffset, &len);
 | 
			
		||||
		if (!ret) {
 | 
			
		||||
			*data = fit + offset;
 | 
			
		||||
			*size = len;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		ret = fit_image_get_data(fit, noffset, data, size);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
mtdsplit_fit_parse(struct mtd_info *mtd,
 | 
			
		||||
		   const struct mtd_partition **pparts,
 | 
			
		||||
	           struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *np = mtd_get_of_node(mtd);
 | 
			
		||||
	const char *cmdline_match = NULL;
 | 
			
		||||
	struct fdt_header hdr;
 | 
			
		||||
	size_t hdr_len, retlen;
 | 
			
		||||
	size_t offset;
 | 
			
		||||
	u32 offset_start = 0;
 | 
			
		||||
	size_t fit_offset, fit_size;
 | 
			
		||||
	size_t rootfs_offset, rootfs_size;
 | 
			
		||||
	size_t data_size, img_total, max_size = 0;
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	int ret, ndepth, noffset, images_noffset;
 | 
			
		||||
	const void *img_data;
 | 
			
		||||
	void *fit;
 | 
			
		||||
 | 
			
		||||
	of_property_read_string(np, "openwrt,cmdline-match", &cmdline_match);
 | 
			
		||||
	if (cmdline_match && !strstr(saved_command_line, cmdline_match))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	of_property_read_u32(np, "openwrt,fit-offset", &offset_start);
 | 
			
		||||
 | 
			
		||||
	hdr_len = sizeof(struct fdt_header);
 | 
			
		||||
 | 
			
		||||
	/* Parse the MTD device & search for the FIT image location */
 | 
			
		||||
	for(offset = 0; offset + hdr_len <= mtd->size; offset += mtd->erasesize) {
 | 
			
		||||
		ret = mtd_read(mtd, offset + offset_start, hdr_len, &retlen, (void*) &hdr);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			pr_err("read error in \"%s\" at offset 0x%llx\n",
 | 
			
		||||
			       mtd->name, (unsigned long long) offset);
 | 
			
		||||
			return ret;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (retlen != hdr_len) {
 | 
			
		||||
			pr_err("short read in \"%s\"\n", mtd->name);
 | 
			
		||||
			return -EIO;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Check the magic - see if this is a FIT image */
 | 
			
		||||
		if (be32_to_cpu(hdr.magic) != OF_DT_HEADER) {
 | 
			
		||||
			pr_debug("no valid FIT image found in \"%s\" at offset %llx\n",
 | 
			
		||||
				 mtd->name, (unsigned long long) offset);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* We found a FIT image. Let's keep going */
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fit_offset = offset;
 | 
			
		||||
	fit_size = be32_to_cpu(hdr.totalsize);
 | 
			
		||||
 | 
			
		||||
	if (fit_size == 0) {
 | 
			
		||||
		pr_err("FIT image in \"%s\" at offset %llx has null size\n",
 | 
			
		||||
		       mtd->name, (unsigned long long) fit_offset);
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Classic uImage.FIT has all data embedded into the FDT
 | 
			
		||||
	 * data structure. Hence the total size of the image equals
 | 
			
		||||
	 * the total size of the FDT structure.
 | 
			
		||||
	 * Modern uImage.FIT may have only references to data in FDT,
 | 
			
		||||
	 * hence we need to parse FDT structure to find the end of the
 | 
			
		||||
	 * last external data refernced.
 | 
			
		||||
	 */
 | 
			
		||||
	if (fit_size > 0x1000) {
 | 
			
		||||
		enum mtdsplit_part_type type;
 | 
			
		||||
 | 
			
		||||
		/* Search for the rootfs partition after the FIT image */
 | 
			
		||||
		ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size + offset_start, mtd->size,
 | 
			
		||||
					   &rootfs_offset, &type);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			pr_info("no rootfs found after FIT image in \"%s\"\n",
 | 
			
		||||
				mtd->name);
 | 
			
		||||
			return ret;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rootfs_size = mtd->size - rootfs_offset;
 | 
			
		||||
 | 
			
		||||
		parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
		if (!parts)
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
		parts[0].name = KERNEL_PART_NAME;
 | 
			
		||||
		parts[0].offset = fit_offset;
 | 
			
		||||
		parts[0].size = mtd_roundup_to_eb(fit_size + offset_start, mtd);
 | 
			
		||||
 | 
			
		||||
		if (type == MTDSPLIT_PART_TYPE_UBI)
 | 
			
		||||
			parts[1].name = UBI_PART_NAME;
 | 
			
		||||
		else
 | 
			
		||||
			parts[1].name = ROOTFS_PART_NAME;
 | 
			
		||||
		parts[1].offset = rootfs_offset;
 | 
			
		||||
		parts[1].size = rootfs_size;
 | 
			
		||||
 | 
			
		||||
		*pparts = parts;
 | 
			
		||||
 | 
			
		||||
		return 2;
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Search for rootfs_data after FIT external data */
 | 
			
		||||
		fit = kzalloc(fit_size, GFP_KERNEL);
 | 
			
		||||
		ret = mtd_read(mtd, offset, fit_size + offset_start, &retlen, fit);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			pr_err("read error in \"%s\" at offset 0x%llx\n",
 | 
			
		||||
			       mtd->name, (unsigned long long) offset);
 | 
			
		||||
			return ret;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
 | 
			
		||||
		if (images_noffset < 0) {
 | 
			
		||||
			pr_err("Can't find images parent node '%s' (%s)\n",
 | 
			
		||||
			FIT_IMAGES_PATH, fdt_strerror(images_noffset));
 | 
			
		||||
			return -ENODEV;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (ndepth = 0,
 | 
			
		||||
		     noffset = fdt_next_node(fit, images_noffset, &ndepth);
 | 
			
		||||
		     (noffset >= 0) && (ndepth > 0);
 | 
			
		||||
		     noffset = fdt_next_node(fit, noffset, &ndepth)) {
 | 
			
		||||
			if (ndepth == 1) {
 | 
			
		||||
				ret = fit_image_get_data_and_size(fit, noffset, &img_data, &data_size);
 | 
			
		||||
				if (ret)
 | 
			
		||||
					return 0;
 | 
			
		||||
 | 
			
		||||
				img_total = data_size + (img_data - fit);
 | 
			
		||||
 | 
			
		||||
				max_size = (max_size > img_total) ? max_size : img_total;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		parts = kzalloc(sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
		if (!parts)
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
		parts[0].name = ROOTFS_SPLIT_NAME;
 | 
			
		||||
		parts[0].offset = fit_offset + mtd_roundup_to_eb(max_size, mtd);
 | 
			
		||||
		parts[0].size = mtd->size - parts[0].offset;
 | 
			
		||||
 | 
			
		||||
		*pparts = parts;
 | 
			
		||||
 | 
			
		||||
		kfree(fit);
 | 
			
		||||
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_fit_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "denx,fit" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser uimage_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "fit-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_fit_of_match_table,
 | 
			
		||||
	.parse_fn = mtdsplit_fit_parse,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**************************************************
 | 
			
		||||
 * Init
 | 
			
		||||
 **************************************************/
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_fit_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&uimage_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module_init(mtdsplit_fit_init);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,170 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-only
 | 
			
		||||
/*
 | 
			
		||||
 * Some devices made by H3C use a "VFS" filesystem to store firmware images.
 | 
			
		||||
 * This parses the start of the filesystem to read the length of the first
 | 
			
		||||
 * file (the kernel image). It then searches for the rootfs after the end of
 | 
			
		||||
 * the file data. This driver assumes that the filesystem was generated by
 | 
			
		||||
 * mkh3cvfs, and only works if the filesystem matches the expected layout,
 | 
			
		||||
 * which includes the file name of the kernel image.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define	VFS_ERASEBLOCK_SIZE		0x10000
 | 
			
		||||
#define	VFS_BLOCK_SIZE			0x400
 | 
			
		||||
#define VFS_BLOCKS_PER_ERASEBLOCK	(VFS_ERASEBLOCK_SIZE / VFS_BLOCK_SIZE)
 | 
			
		||||
 | 
			
		||||
#define FORMAT_FLAG_OFFSET		0x0
 | 
			
		||||
 | 
			
		||||
#define FORMAT_FLAG			(VFS_ERASEBLOCK_SIZE << 12 | VFS_BLOCK_SIZE)
 | 
			
		||||
 | 
			
		||||
#define FILE_ENTRY_OFFSET		0x800
 | 
			
		||||
 | 
			
		||||
#define FILE_ENTRY_FLAGS		0x3f
 | 
			
		||||
#define FILE_ENTRY_PARENT_BLOCK		0
 | 
			
		||||
#define FILE_ENTRY_PARENT_INDEX 	0
 | 
			
		||||
#define FILE_ENTRY_DATA_BLOCK	 	2
 | 
			
		||||
#define FILE_ENTRY_NAME			"openwrt-kernel.bin"
 | 
			
		||||
 | 
			
		||||
#define NR_PARTS			2
 | 
			
		||||
 | 
			
		||||
struct file_entry {
 | 
			
		||||
	uint8_t flags;
 | 
			
		||||
 | 
			
		||||
	uint8_t res0[5];
 | 
			
		||||
 | 
			
		||||
	uint16_t year;
 | 
			
		||||
	uint8_t month;
 | 
			
		||||
	uint8_t day;
 | 
			
		||||
	uint8_t hour;
 | 
			
		||||
	uint8_t minute;
 | 
			
		||||
	uint8_t second;
 | 
			
		||||
 | 
			
		||||
	uint8_t res1[3];
 | 
			
		||||
 | 
			
		||||
	uint32_t length;
 | 
			
		||||
 | 
			
		||||
	uint32_t parent_block;
 | 
			
		||||
	uint16_t parent_index;
 | 
			
		||||
 | 
			
		||||
	uint8_t res2[2];
 | 
			
		||||
 | 
			
		||||
	uint32_t data_block;
 | 
			
		||||
 | 
			
		||||
	char name[96];
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
static inline size_t block_offset(int block)
 | 
			
		||||
{
 | 
			
		||||
	return VFS_ERASEBLOCK_SIZE * (block / (VFS_BLOCKS_PER_ERASEBLOCK-1))
 | 
			
		||||
		+ VFS_BLOCK_SIZE * (1 + (block % (VFS_BLOCKS_PER_ERASEBLOCK-1)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int block_count(size_t size)
 | 
			
		||||
{
 | 
			
		||||
	return (size + VFS_BLOCK_SIZE - 1) / VFS_BLOCK_SIZE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_h3c_vfs_parse(struct mtd_info *mtd,
 | 
			
		||||
				  const struct mtd_partition **pparts,
 | 
			
		||||
				  struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	uint32_t format_flag;
 | 
			
		||||
	struct file_entry file_entry;
 | 
			
		||||
	size_t retlen;
 | 
			
		||||
	int err;
 | 
			
		||||
	size_t kernel_size;
 | 
			
		||||
	size_t expected_offset;
 | 
			
		||||
	size_t rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	if (mtd->erasesize != VFS_ERASEBLOCK_SIZE)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Check format flag */
 | 
			
		||||
	err = mtd_read(mtd, FORMAT_FLAG_OFFSET, sizeof(format_flag), &retlen,
 | 
			
		||||
		       (void *) &format_flag);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	if (retlen != sizeof(format_flag))
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	if (format_flag != FORMAT_FLAG)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Check file entry */
 | 
			
		||||
	err = mtd_read(mtd, FILE_ENTRY_OFFSET, sizeof(file_entry), &retlen,
 | 
			
		||||
		       (void *) &file_entry);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	if (retlen != sizeof(file_entry))
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	if (file_entry.flags != FILE_ENTRY_FLAGS)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (file_entry.parent_block != FILE_ENTRY_PARENT_BLOCK)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (file_entry.parent_index != FILE_ENTRY_PARENT_INDEX)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (file_entry.data_block != FILE_ENTRY_DATA_BLOCK)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (strncmp(file_entry.name, FILE_ENTRY_NAME, sizeof(file_entry.name)) != 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Find rootfs offset */
 | 
			
		||||
	kernel_size = block_offset(file_entry.data_block +
 | 
			
		||||
				   block_count(file_entry.length) - 1) +
 | 
			
		||||
		      VFS_BLOCK_SIZE;
 | 
			
		||||
 | 
			
		||||
	expected_offset = mtd_roundup_to_eb(kernel_size, mtd);
 | 
			
		||||
 | 
			
		||||
	err = mtd_find_rootfs_from(mtd, expected_offset, mtd->size,
 | 
			
		||||
				   &rootfs_offset, NULL);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	parts = kzalloc(NR_PARTS * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	parts[0].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[0].offset = 0;
 | 
			
		||||
	parts[0].size = rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	parts[1].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[1].offset = rootfs_offset;
 | 
			
		||||
	parts[1].size = mtd->size - rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return NR_PARTS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_h3c_vfs_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "h3c,vfs-firmware" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, mtdsplit_h3c_vfs_of_match_table);
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_h3c_vfs_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "h3c-vfs",
 | 
			
		||||
	.of_match_table = mtdsplit_h3c_vfs_of_match_table,
 | 
			
		||||
	.parse_fn = mtdsplit_h3c_vfs_parse,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module_mtd_part_parser(mtdsplit_h3c_vfs_parser);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,284 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Copyright (C) 2018 Paweł Dembicki <paweldembicki@gmail.com> 
 | 
			
		||||
 *
 | 
			
		||||
 *  Based on: mtdsplit_uimage.c
 | 
			
		||||
 *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 *  by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/vmalloc.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define MAX_HEADER_LEN ( STAG_SIZE + SCH2_SIZE )
 | 
			
		||||
 | 
			
		||||
#define STAG_SIZE 16
 | 
			
		||||
#define STAG_ID 0x04
 | 
			
		||||
#define STAG_MAGIC 0x2B24
 | 
			
		||||
 | 
			
		||||
#define SCH2_SIZE 40
 | 
			
		||||
#define SCH2_MAGIC 0x2124
 | 
			
		||||
#define SCH2_VER 0x02
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Jboot image header,
 | 
			
		||||
 * all data in little endian.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
struct jimage_header		//stag + sch2 jboot joined headers
 | 
			
		||||
{
 | 
			
		||||
	uint8_t stag_cmark;		// in factory 0xFF , in sysupgrade must be the same as stag_id
 | 
			
		||||
	uint8_t stag_id;		// 0x04
 | 
			
		||||
	uint16_t stag_magic;		//magic 0x2B24
 | 
			
		||||
	uint32_t stag_time_stamp;	// timestamp calculated in jboot way
 | 
			
		||||
	uint32_t stag_image_length;	// lentgh of kernel + sch2 header
 | 
			
		||||
	uint16_t stag_image_checksum;	// negated jboot_checksum of sch2 + kernel
 | 
			
		||||
	uint16_t stag_tag_checksum;	// negated jboot_checksum of stag header data
 | 
			
		||||
	uint16_t sch2_magic;		// magic 0x2124
 | 
			
		||||
	uint8_t sch2_cp_type;	// 0x00 for flat, 0x01 for jz, 0x02 for gzip, 0x03 for lzma
 | 
			
		||||
	uint8_t sch2_version;	// 0x02 for sch2
 | 
			
		||||
	uint32_t sch2_ram_addr;	// ram entry address
 | 
			
		||||
	uint32_t sch2_image_len;	// kernel image length
 | 
			
		||||
	uint32_t sch2_image_crc32;	// kernel image crc
 | 
			
		||||
	uint32_t sch2_start_addr;	// ram start address
 | 
			
		||||
	uint32_t sch2_rootfs_addr;	// rootfs flash address
 | 
			
		||||
	uint32_t sch2_rootfs_len;	// rootfls length
 | 
			
		||||
	uint32_t sch2_rootfs_crc32;	// rootfs crc32
 | 
			
		||||
	uint32_t sch2_header_crc32;	// sch2 header crc32, durring calculation this area is replaced by zero
 | 
			
		||||
	uint16_t sch2_header_length;	// sch2 header length: 0x28
 | 
			
		||||
	uint16_t sch2_cmd_line_length;	// cmd line length, known zeros
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
read_jimage_header(struct mtd_info *mtd, size_t offset, u_char *buf,
 | 
			
		||||
		   size_t header_len)
 | 
			
		||||
{
 | 
			
		||||
	size_t retlen;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = mtd_read(mtd, offset, header_len, &retlen, buf);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_debug("read error in \"%s\"\n", mtd->name);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (retlen != header_len) {
 | 
			
		||||
		pr_debug("short read in \"%s\"\n", mtd->name);
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * __mtdsplit_parse_jimage - scan partition and create kernel + rootfs parts
 | 
			
		||||
 *
 | 
			
		||||
 * @find_header: function to call for a block of data that will return offset
 | 
			
		||||
 *      of a valid jImage header if found
 | 
			
		||||
 */
 | 
			
		||||
static int __mtdsplit_parse_jimage(struct mtd_info *master,
 | 
			
		||||
				   const struct mtd_partition **pparts,
 | 
			
		||||
				   struct mtd_part_parser_data *data,
 | 
			
		||||
				   ssize_t (*find_header)(u_char *buf, size_t len))
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	u_char *buf;
 | 
			
		||||
	int nr_parts;
 | 
			
		||||
	size_t offset;
 | 
			
		||||
	size_t jimage_offset;
 | 
			
		||||
	size_t jimage_size = 0;
 | 
			
		||||
	size_t rootfs_offset;
 | 
			
		||||
	size_t rootfs_size = 0;
 | 
			
		||||
	int jimage_part, rf_part;
 | 
			
		||||
	int ret;
 | 
			
		||||
	enum mtdsplit_part_type type;
 | 
			
		||||
 | 
			
		||||
	nr_parts = 2;
 | 
			
		||||
	parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	buf = vmalloc(MAX_HEADER_LEN);
 | 
			
		||||
	if (!buf) {
 | 
			
		||||
		ret = -ENOMEM;
 | 
			
		||||
		goto err_free_parts;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* find jImage on erase block boundaries */
 | 
			
		||||
	for (offset = 0; offset < master->size; offset += master->erasesize) {
 | 
			
		||||
		struct jimage_header *header;
 | 
			
		||||
 | 
			
		||||
		jimage_size = 0;
 | 
			
		||||
 | 
			
		||||
		ret = read_jimage_header(master, offset, buf, MAX_HEADER_LEN);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		ret = find_header(buf, MAX_HEADER_LEN);
 | 
			
		||||
		if (ret < 0) {
 | 
			
		||||
			pr_debug("no valid jImage found in \"%s\" at offset %llx\n",
 | 
			
		||||
				 master->name, (unsigned long long) offset);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		header = (struct jimage_header *)(buf + ret);
 | 
			
		||||
 | 
			
		||||
		jimage_size = sizeof(*header) + header->sch2_image_len + ret;
 | 
			
		||||
		if ((offset + jimage_size) > master->size) {
 | 
			
		||||
			pr_debug("jImage exceeds MTD device \"%s\"\n",
 | 
			
		||||
				 master->name);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (jimage_size == 0) {
 | 
			
		||||
		pr_debug("no jImage found in \"%s\"\n", master->name);
 | 
			
		||||
		ret = -ENODEV;
 | 
			
		||||
		goto err_free_buf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	jimage_offset = offset;
 | 
			
		||||
 | 
			
		||||
	if (jimage_offset == 0) {
 | 
			
		||||
		jimage_part = 0;
 | 
			
		||||
		rf_part = 1;
 | 
			
		||||
 | 
			
		||||
		/* find the roots after the jImage */
 | 
			
		||||
		ret = mtd_find_rootfs_from(master, jimage_offset + jimage_size,
 | 
			
		||||
					   master->size, &rootfs_offset, &type);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			pr_debug("no rootfs after jImage in \"%s\"\n",
 | 
			
		||||
				 master->name);
 | 
			
		||||
			goto err_free_buf;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rootfs_size = master->size - rootfs_offset;
 | 
			
		||||
		jimage_size = rootfs_offset - jimage_offset;
 | 
			
		||||
	} else {
 | 
			
		||||
		rf_part = 0;
 | 
			
		||||
		jimage_part = 1;
 | 
			
		||||
 | 
			
		||||
		/* check rootfs presence at offset 0 */
 | 
			
		||||
		ret = mtd_check_rootfs_magic(master, 0, &type);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			pr_debug("no rootfs before jImage in \"%s\"\n",
 | 
			
		||||
				 master->name);
 | 
			
		||||
			goto err_free_buf;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rootfs_offset = 0;
 | 
			
		||||
		rootfs_size = jimage_offset;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (rootfs_size == 0) {
 | 
			
		||||
		pr_debug("no rootfs found in \"%s\"\n", master->name);
 | 
			
		||||
		ret = -ENODEV;
 | 
			
		||||
		goto err_free_buf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parts[jimage_part].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[jimage_part].offset = jimage_offset;
 | 
			
		||||
	parts[jimage_part].size = jimage_size;
 | 
			
		||||
 | 
			
		||||
	if (type == MTDSPLIT_PART_TYPE_UBI)
 | 
			
		||||
		parts[rf_part].name = UBI_PART_NAME;
 | 
			
		||||
	else
 | 
			
		||||
		parts[rf_part].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[rf_part].offset = rootfs_offset;
 | 
			
		||||
	parts[rf_part].size = rootfs_size;
 | 
			
		||||
 | 
			
		||||
	vfree(buf);
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return nr_parts;
 | 
			
		||||
 | 
			
		||||
err_free_buf:
 | 
			
		||||
	vfree(buf);
 | 
			
		||||
 | 
			
		||||
err_free_parts:
 | 
			
		||||
	kfree(parts);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t jimage_verify_default(u_char *buf, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	struct jimage_header *header = (struct jimage_header *)buf;
 | 
			
		||||
 | 
			
		||||
	/* default sanity checks */
 | 
			
		||||
	if (header->stag_magic != STAG_MAGIC) {
 | 
			
		||||
		pr_debug("invalid jImage stag header magic: %04x\n",
 | 
			
		||||
			 header->stag_magic);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	if (header->sch2_magic != SCH2_MAGIC) {
 | 
			
		||||
		pr_debug("invalid jImage sch2 header magic: %04x\n",
 | 
			
		||||
			 header->stag_magic);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	if (header->stag_cmark != header->stag_id) {
 | 
			
		||||
		pr_debug("invalid jImage stag header cmark: %02x\n",
 | 
			
		||||
			 header->stag_magic);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	if (header->stag_id != STAG_ID) {
 | 
			
		||||
		pr_debug("invalid jImage stag header id: %02x\n",
 | 
			
		||||
			 header->stag_magic);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	if (header->sch2_version != SCH2_VER) {
 | 
			
		||||
		pr_debug("invalid jImage sch2 header version: %02x\n",
 | 
			
		||||
			 header->stag_magic);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
mtdsplit_jimage_parse_generic(struct mtd_info *master,
 | 
			
		||||
			      const struct mtd_partition **pparts,
 | 
			
		||||
			      struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	return __mtdsplit_parse_jimage(master, pparts, data,
 | 
			
		||||
				      jimage_verify_default);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_jimage_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "amit,jimage" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser jimage_generic_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "jimage-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_jimage_of_match_table,
 | 
			
		||||
	.parse_fn = mtdsplit_jimage_parse_generic,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**************************************************
 | 
			
		||||
 * Init
 | 
			
		||||
 **************************************************/
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_jimage_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&jimage_generic_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module_init(mtdsplit_jimage_init);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,104 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 *  by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/unaligned.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define LZMA_NR_PARTS		2
 | 
			
		||||
#define LZMA_PROPERTIES_SIZE	5
 | 
			
		||||
 | 
			
		||||
struct lzma_header {
 | 
			
		||||
	u8 props[LZMA_PROPERTIES_SIZE];
 | 
			
		||||
	u8 size_low[4];
 | 
			
		||||
	u8 size_high[4];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_parse_lzma(struct mtd_info *master,
 | 
			
		||||
			       const struct mtd_partition **pparts,
 | 
			
		||||
			       struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct lzma_header hdr;
 | 
			
		||||
	size_t hdr_len, retlen;
 | 
			
		||||
	size_t rootfs_offset;
 | 
			
		||||
	u32 t;
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	hdr_len = sizeof(hdr);
 | 
			
		||||
	err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	if (retlen != hdr_len)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	/* verify LZMA properties */
 | 
			
		||||
	if (hdr.props[0] >= (9 * 5 * 5))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	t = get_unaligned_le32(&hdr.props[1]);
 | 
			
		||||
	if (!is_power_of_2(t))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	t = get_unaligned_le32(&hdr.size_high);
 | 
			
		||||
	if (t)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	err = mtd_find_rootfs_from(master, master->erasesize, master->size,
 | 
			
		||||
				   &rootfs_offset, NULL);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	parts = kzalloc(LZMA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	parts[0].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[0].offset = 0;
 | 
			
		||||
	parts[0].size = rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	parts[1].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[1].offset = rootfs_offset;
 | 
			
		||||
	parts[1].size = master->size - rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return LZMA_NR_PARTS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_lzma_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "lzma" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, mtdsplit_lzma_of_match_table);
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_lzma_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "lzma-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_lzma_of_match_table,
 | 
			
		||||
	.parse_fn = mtdsplit_parse_lzma,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_lzma_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&mtdsplit_lzma_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subsys_initcall(mtdsplit_lzma_init);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,142 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  MTD splitter for MikroTik NOR devices
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2017 Thibaut VARENE <varenet@parisc-linux.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 *  by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 *  The rootfs is expected at erase-block boundary due to the use of
 | 
			
		||||
 *  mtd_find_rootfs_from(). We use a trimmed down version of the yaffs header
 | 
			
		||||
 *  for two main reasons:
 | 
			
		||||
 *  - the original header uses weakly defined types (int, enum...) which can
 | 
			
		||||
 *    vary in length depending on build host (and the struct is not packed),
 | 
			
		||||
 *    and the name field can have a different total length depending on
 | 
			
		||||
 *    whether or not the yaffs code was _built_ with unicode support.
 | 
			
		||||
 *  - the only field that could be of real use here (file_size_low) contains
 | 
			
		||||
 *    invalid data in the header generated by kernel2minor, so we cannot use
 | 
			
		||||
 *    it to infer the exact position of the rootfs and do away with
 | 
			
		||||
 *    mtd_find_rootfs_from() (and thus have non-EB-aligned rootfs).
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define YAFFS_OBJECT_TYPE_FILE	0x1
 | 
			
		||||
#define YAFFS_OBJECTID_ROOT	0x1
 | 
			
		||||
#define YAFFS_SUM_UNUSED	0xFFFF
 | 
			
		||||
#define YAFFS_MAX_NAME_LENGTH	127
 | 
			
		||||
#define YAFFS_NAME_KERNEL	"kernel"
 | 
			
		||||
#define YAFFS_NAME_BOOTIMAGE	"bootimage"
 | 
			
		||||
 | 
			
		||||
#define MINOR_NR_PARTS		2
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This structure is based on yaffs_obj_hdr from yaffs_guts.h
 | 
			
		||||
 * The weak types match upstream. The fields have cpu-endianness
 | 
			
		||||
 */
 | 
			
		||||
struct minor_header {
 | 
			
		||||
	int yaffs_type;
 | 
			
		||||
	int yaffs_obj_id;
 | 
			
		||||
	u16 yaffs_sum_unused;
 | 
			
		||||
	char yaffs_name[YAFFS_MAX_NAME_LENGTH];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_parse_minor(struct mtd_info *master,
 | 
			
		||||
				const struct mtd_partition **pparts,
 | 
			
		||||
				struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct minor_header hdr;
 | 
			
		||||
	size_t hdr_len, retlen;
 | 
			
		||||
	size_t rootfs_offset;
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	hdr_len = sizeof(hdr);
 | 
			
		||||
	err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		pr_err("MiNOR mtd_read error: %d\n", err);
 | 
			
		||||
		return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (retlen != hdr_len) {
 | 
			
		||||
		pr_err("MiNOR mtd_read too short\n");
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* match header */
 | 
			
		||||
	if (hdr.yaffs_type != YAFFS_OBJECT_TYPE_FILE) {
 | 
			
		||||
		pr_info("MiNOR YAFFS first type not matched\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (hdr.yaffs_obj_id != YAFFS_OBJECTID_ROOT) {
 | 
			
		||||
		pr_info("MiNOR YAFFS first objectid not matched\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (hdr.yaffs_sum_unused != YAFFS_SUM_UNUSED) {
 | 
			
		||||
		pr_info("MiNOR YAFFS first sum not matched\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ((memcmp(hdr.yaffs_name, YAFFS_NAME_KERNEL, sizeof(YAFFS_NAME_KERNEL))) &&
 | 
			
		||||
	    (memcmp(hdr.yaffs_name, YAFFS_NAME_BOOTIMAGE, sizeof(YAFFS_NAME_BOOTIMAGE)))) {
 | 
			
		||||
		pr_info("MiNOR YAFFS first name not matched\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = mtd_find_rootfs_from(master, master->erasesize, master->size,
 | 
			
		||||
				   &rootfs_offset, NULL);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		pr_info("MiNOR mtd_find_rootfs_from error: %d\n", err);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parts = kzalloc(MINOR_NR_PARTS * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	parts[0].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[0].offset = 0;
 | 
			
		||||
	parts[0].size = rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	parts[1].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[1].offset = rootfs_offset;
 | 
			
		||||
	parts[1].size = master->size - rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return MINOR_NR_PARTS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_minor_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "mikrotik,minor" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, mtdsplit_minor_of_match_table);
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_minor_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "minor-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_minor_of_match_table,
 | 
			
		||||
	.parse_fn = mtdsplit_parse_minor,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_minor_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&mtdsplit_minor_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subsys_initcall(mtdsplit_minor_init);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,118 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 *  by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define SEAMA_MAGIC		0x5EA3A417
 | 
			
		||||
#define SEAMA_NR_PARTS		2
 | 
			
		||||
#define SEAMA_MIN_ROOTFS_OFFS	0x80000	/* 512KiB */
 | 
			
		||||
 | 
			
		||||
struct seama_header {
 | 
			
		||||
	__be32	magic;		/* should always be SEAMA_MAGIC. */
 | 
			
		||||
	__be16	reserved;	/* reserved for  */
 | 
			
		||||
	__be16	metasize;	/* size of the META data */
 | 
			
		||||
	__be32	size;		/* size of the image */
 | 
			
		||||
	u8	md5[16];	/* digest */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_parse_seama(struct mtd_info *master,
 | 
			
		||||
				const struct mtd_partition **pparts,
 | 
			
		||||
				struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct seama_header hdr;
 | 
			
		||||
	size_t hdr_len, retlen, kernel_ent_size;
 | 
			
		||||
	size_t rootfs_offset;
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	enum mtdsplit_part_type type;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	hdr_len = sizeof(hdr);
 | 
			
		||||
	err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	if (retlen != hdr_len)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	/* sanity checks */
 | 
			
		||||
	if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	kernel_ent_size = hdr_len + be32_to_cpu(hdr.size) +
 | 
			
		||||
			  be16_to_cpu(hdr.metasize);
 | 
			
		||||
	if (kernel_ent_size > master->size)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Check for the rootfs right after Seama entity with a kernel. */
 | 
			
		||||
	err = mtd_check_rootfs_magic(master, kernel_ent_size, &type);
 | 
			
		||||
	if (!err) {
 | 
			
		||||
		rootfs_offset = kernel_ent_size;
 | 
			
		||||
	} else {
 | 
			
		||||
		/*
 | 
			
		||||
		 * On some devices firmware entity might contain both: kernel
 | 
			
		||||
		 * and rootfs. We can't determine kernel size so we just have to
 | 
			
		||||
		 * look for rootfs magic.
 | 
			
		||||
		 * Start the search from an arbitrary offset.
 | 
			
		||||
		 */
 | 
			
		||||
		err = mtd_find_rootfs_from(master, SEAMA_MIN_ROOTFS_OFFS,
 | 
			
		||||
					   master->size, &rootfs_offset, &type);
 | 
			
		||||
		if (err)
 | 
			
		||||
			return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parts = kzalloc(SEAMA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	parts[0].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[0].offset = sizeof hdr + be16_to_cpu(hdr.metasize);
 | 
			
		||||
	parts[0].size = rootfs_offset - parts[0].offset;
 | 
			
		||||
 | 
			
		||||
	if (type == MTDSPLIT_PART_TYPE_UBI)
 | 
			
		||||
		parts[1].name = UBI_PART_NAME;
 | 
			
		||||
	else
 | 
			
		||||
		parts[1].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[1].offset = rootfs_offset;
 | 
			
		||||
	parts[1].size = master->size - rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return SEAMA_NR_PARTS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_seama_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "seama" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, mtdsplit_seama_of_match_table);
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_seama_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "seama-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_seama_of_match_table,
 | 
			
		||||
	.parse_fn = mtdsplit_parse_seama,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_seama_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&mtdsplit_seama_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subsys_initcall(mtdsplit_seama_init);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,191 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
/* a mtdsplit driver for IIJ SEIL devices */
 | 
			
		||||
 | 
			
		||||
#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/vmalloc.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define NR_PARTS		2
 | 
			
		||||
#define SEIL_VFMT		1
 | 
			
		||||
#define LDR_ENV_PART_NAME	"bootloader-env"
 | 
			
		||||
#define LDR_ENV_KEY_BOOTDEV	"BOOTDEV"
 | 
			
		||||
 | 
			
		||||
struct seil_header {
 | 
			
		||||
	uint64_t	id;		/* Identifier */
 | 
			
		||||
	uint8_t		copy[80];	/* Copyright */
 | 
			
		||||
	uint32_t	dcrc;		/* Data CRC Checksum */
 | 
			
		||||
	uint32_t	vfmt;		/* Image Version Format */
 | 
			
		||||
	uint32_t	vmjr;		/* Image Version Major */
 | 
			
		||||
	uint32_t	vmnr;		/* Image Version Minor */
 | 
			
		||||
	uint8_t		vrel[32];	/* Image Version Release */
 | 
			
		||||
	uint32_t	dxor;		/* xor value for Data? */
 | 
			
		||||
	uint32_t	dlen;		/* Data Length */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * check whether the current mtd device is active or not
 | 
			
		||||
 *
 | 
			
		||||
 * example of BOOTDEV value (IIJ SA-W2):
 | 
			
		||||
 *   - "flash"   : primary image on flash
 | 
			
		||||
 *   - "rescue"  : secondary image on flash
 | 
			
		||||
 *   - "usb"     : usb storage
 | 
			
		||||
 *   - "lan0/1"  : network
 | 
			
		||||
 */
 | 
			
		||||
static bool seil_bootdev_is_active(struct device_node *np)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_info *env_mtd;
 | 
			
		||||
	char *buf, *var, *value, *eq;
 | 
			
		||||
	const char *devnm;
 | 
			
		||||
	size_t rdlen;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * read bootdev name of the partition
 | 
			
		||||
	 * if doesn't exist, return true and skip checking of active device
 | 
			
		||||
	 */
 | 
			
		||||
	ret = of_property_read_string(np, "iij,bootdev-name", &devnm);
 | 
			
		||||
	if (ret == -EINVAL)
 | 
			
		||||
		return true;
 | 
			
		||||
	else if (ret < 0)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	env_mtd = get_mtd_device_nm(LDR_ENV_PART_NAME);
 | 
			
		||||
	if (IS_ERR(env_mtd)) {
 | 
			
		||||
		pr_err("failed to get mtd device \"%s\"", LDR_ENV_PART_NAME);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf = vmalloc(env_mtd->size);
 | 
			
		||||
	if (!buf)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	ret = mtd_read(env_mtd, 0, env_mtd->size, &rdlen, buf);
 | 
			
		||||
	if (ret || rdlen != env_mtd->size) {
 | 
			
		||||
		pr_err("failed to read from mtd (%d)\n", ret);
 | 
			
		||||
		ret = 0;
 | 
			
		||||
		goto exit_vfree;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (var = buf, ret = false;
 | 
			
		||||
	     var < buf + env_mtd->size && *var;
 | 
			
		||||
	     var = value + strlen(value) + 1) {
 | 
			
		||||
		eq = strchr(var, '=');
 | 
			
		||||
		if (!eq)
 | 
			
		||||
			break;
 | 
			
		||||
		*eq = '\0';
 | 
			
		||||
		value = eq + 1;
 | 
			
		||||
 | 
			
		||||
		pr_debug("ENV: %s=%s\n", var, value);
 | 
			
		||||
		if (!strcmp(var, LDR_ENV_KEY_BOOTDEV)) {
 | 
			
		||||
			ret = !strcmp(devnm, value);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
exit_vfree:
 | 
			
		||||
	vfree(buf);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_parse_seil_fw(struct mtd_info *master,
 | 
			
		||||
				  const struct mtd_partition **pparts,
 | 
			
		||||
				  struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *np = mtd_get_of_node(master);
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	struct seil_header header;
 | 
			
		||||
	size_t image_size = 0;
 | 
			
		||||
	size_t rootfs_offset;
 | 
			
		||||
	size_t hdrlen = sizeof(header);
 | 
			
		||||
	size_t retlen;
 | 
			
		||||
	int ret;
 | 
			
		||||
	u64 id;
 | 
			
		||||
 | 
			
		||||
	if (!seil_bootdev_is_active(np))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	ret = of_property_read_u64(np, "iij,seil-id", &id);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_err("failed to get iij,seil-id from dt\n");
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
	pr_debug("got seil-id=0x%016llx from dt\n", id);
 | 
			
		||||
 | 
			
		||||
	parts = kcalloc(NR_PARTS, sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	ret = mtd_read(master, 0, hdrlen, &retlen, (void *)&header);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err_free_parts;
 | 
			
		||||
 | 
			
		||||
	if (retlen != hdrlen) {
 | 
			
		||||
		ret = -EIO;
 | 
			
		||||
		goto err_free_parts;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (be64_to_cpu(header.id) != id ||
 | 
			
		||||
	    be32_to_cpu(header.vfmt) != SEIL_VFMT) {
 | 
			
		||||
		pr_debug("no valid seil image found in \"%s\"\n", master->name);
 | 
			
		||||
		ret = -ENODEV;
 | 
			
		||||
		goto err_free_parts;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	image_size = hdrlen + be32_to_cpu(header.dlen);
 | 
			
		||||
	if (image_size > master->size) {
 | 
			
		||||
		pr_err("seil image exceeds MTD device \"%s\"\n", master->name);
 | 
			
		||||
		ret = -EINVAL;
 | 
			
		||||
		goto err_free_parts;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* find the roots after the seil image */
 | 
			
		||||
	ret = mtd_find_rootfs_from(master, image_size,
 | 
			
		||||
				   master->size, &rootfs_offset, NULL);
 | 
			
		||||
	if (ret || (master->size - rootfs_offset) == 0) {
 | 
			
		||||
		pr_debug("no rootfs after seil image in \"%s\"\n",
 | 
			
		||||
			 master->name);
 | 
			
		||||
		ret = -ENODEV;
 | 
			
		||||
		goto err_free_parts;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parts[0].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[0].offset = 0;
 | 
			
		||||
	parts[0].size = rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	parts[1].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[1].offset = rootfs_offset;
 | 
			
		||||
	parts[1].size = master->size - rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return NR_PARTS;
 | 
			
		||||
 | 
			
		||||
err_free_parts:
 | 
			
		||||
	kfree(parts);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_seil_fw_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "iij,seil-firmware" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, mtdsplit_seil_fw_of_match_table);
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_seil_fw_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "seil-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_seil_fw_of_match_table,
 | 
			
		||||
	.parse_fn = mtdsplit_parse_seil_fw,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module_mtd_part_parser(mtdsplit_seil_fw_parser);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,72 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Copyright (C) 2013 Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
 *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 *  by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/magic.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
mtdsplit_parse_squashfs(struct mtd_info *master,
 | 
			
		||||
			const struct mtd_partition **pparts,
 | 
			
		||||
			struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_partition *part;
 | 
			
		||||
	struct mtd_info *parent_mtd;
 | 
			
		||||
	size_t part_offset;
 | 
			
		||||
	size_t squashfs_len;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	err = mtd_get_squashfs_len(master, 0, &squashfs_len);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	parent_mtd = mtd_get_master(master);
 | 
			
		||||
	part_offset = mtdpart_get_offset(master);
 | 
			
		||||
 | 
			
		||||
	part = kzalloc(sizeof(*part), GFP_KERNEL);
 | 
			
		||||
	if (!part) {
 | 
			
		||||
		pr_alert("unable to allocate memory for \"%s\" partition\n",
 | 
			
		||||
			 ROOTFS_SPLIT_NAME);
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	part->name = ROOTFS_SPLIT_NAME;
 | 
			
		||||
	part->offset = mtd_roundup_to_eb(part_offset + squashfs_len,
 | 
			
		||||
					 parent_mtd) - part_offset;
 | 
			
		||||
	part->size = mtd_rounddown_to_eb(master->size - part->offset, master);
 | 
			
		||||
 | 
			
		||||
	*pparts = part;
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_squashfs_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "squashfs-split",
 | 
			
		||||
	.parse_fn = mtdsplit_parse_squashfs,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_ROOTFS,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_squashfs_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&mtdsplit_squashfs_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subsys_initcall(mtdsplit_squashfs_init);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,176 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 *  by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define TPLINK_NR_PARTS		2
 | 
			
		||||
#define TPLINK_MIN_ROOTFS_OFFS	0x80000	/* 512KiB */
 | 
			
		||||
 | 
			
		||||
#define MD5SUM_LEN  16
 | 
			
		||||
 | 
			
		||||
struct fw_v1 {
 | 
			
		||||
	char		vendor_name[24];
 | 
			
		||||
	char		fw_version[36];
 | 
			
		||||
	uint32_t	hw_id;		/* hardware id */
 | 
			
		||||
	uint32_t	hw_rev;		/* hardware revision */
 | 
			
		||||
	uint32_t	unk1;
 | 
			
		||||
	uint8_t		md5sum1[MD5SUM_LEN];
 | 
			
		||||
	uint32_t	unk2;
 | 
			
		||||
	uint8_t		md5sum2[MD5SUM_LEN];
 | 
			
		||||
	uint32_t	unk3;
 | 
			
		||||
	uint32_t	kernel_la;	/* kernel load address */
 | 
			
		||||
	uint32_t	kernel_ep;	/* kernel entry point */
 | 
			
		||||
	uint32_t	fw_length;	/* total length of the firmware */
 | 
			
		||||
	uint32_t	kernel_ofs;	/* kernel data offset */
 | 
			
		||||
	uint32_t	kernel_len;	/* kernel data length */
 | 
			
		||||
	uint32_t	rootfs_ofs;	/* rootfs data offset */
 | 
			
		||||
	uint32_t	rootfs_len;	/* rootfs data length */
 | 
			
		||||
	uint32_t	boot_ofs;	/* bootloader data offset */
 | 
			
		||||
	uint32_t	boot_len;	/* bootloader data length */
 | 
			
		||||
	uint8_t		pad[360];
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
struct fw_v2 {
 | 
			
		||||
	char		fw_version[48]; /* 0x04: fw version string */
 | 
			
		||||
	uint32_t	hw_id;		/* 0x34: hardware id */
 | 
			
		||||
	uint32_t	hw_rev;		/* 0x38: FIXME: hardware revision? */
 | 
			
		||||
	uint32_t	unk1;	        /* 0x3c: 0x00000000 */
 | 
			
		||||
	uint8_t		md5sum1[MD5SUM_LEN]; /* 0x40 */
 | 
			
		||||
	uint32_t	unk2;		/* 0x50: 0x00000000 */
 | 
			
		||||
	uint8_t		md5sum2[MD5SUM_LEN]; /* 0x54 */
 | 
			
		||||
	uint32_t	unk3;		/* 0x64: 0xffffffff */
 | 
			
		||||
 | 
			
		||||
	uint32_t	kernel_la;	/* 0x68: kernel load address */
 | 
			
		||||
	uint32_t	kernel_ep;	/* 0x6c: kernel entry point */
 | 
			
		||||
	uint32_t	fw_length;	/* 0x70: total length of the image */
 | 
			
		||||
	uint32_t	kernel_ofs;	/* 0x74: kernel data offset */
 | 
			
		||||
	uint32_t	kernel_len;	/* 0x78: kernel data length */
 | 
			
		||||
	uint32_t	rootfs_ofs;	/* 0x7c: rootfs data offset */
 | 
			
		||||
	uint32_t	rootfs_len;	/* 0x80: rootfs data length */
 | 
			
		||||
	uint32_t	boot_ofs;	/* 0x84: FIXME: seems to be unused */
 | 
			
		||||
	uint32_t	boot_len;	/* 0x88: FIXME: seems to be unused */
 | 
			
		||||
	uint16_t	unk4;		/* 0x8c: 0x55aa */
 | 
			
		||||
	uint8_t		sver_hi;	/* 0x8e */
 | 
			
		||||
	uint8_t		sver_lo;	/* 0x8f */
 | 
			
		||||
	uint8_t		unk5;		/* 0x90: magic: 0xa5 */
 | 
			
		||||
	uint8_t		ver_hi;         /* 0x91 */
 | 
			
		||||
	uint8_t		ver_mid;        /* 0x92 */
 | 
			
		||||
	uint8_t		ver_lo;         /* 0x93 */
 | 
			
		||||
	uint8_t		pad[364];
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
struct tplink_fw_header {
 | 
			
		||||
	uint32_t version;
 | 
			
		||||
	union {
 | 
			
		||||
		struct fw_v1 v1;
 | 
			
		||||
		struct fw_v2 v2;
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_parse_tplink(struct mtd_info *master,
 | 
			
		||||
				 const struct mtd_partition **pparts,
 | 
			
		||||
				 struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct tplink_fw_header hdr;
 | 
			
		||||
	size_t hdr_len, retlen, kernel_size;
 | 
			
		||||
	size_t rootfs_offset;
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	hdr_len = sizeof(hdr);
 | 
			
		||||
	err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	if (retlen != hdr_len)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	switch (le32_to_cpu(hdr.version)) {
 | 
			
		||||
	case 1:
 | 
			
		||||
		if (be32_to_cpu(hdr.v1.kernel_ofs) != sizeof(hdr))
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		kernel_size = sizeof(hdr) + be32_to_cpu(hdr.v1.kernel_len);
 | 
			
		||||
		rootfs_offset = be32_to_cpu(hdr.v1.rootfs_ofs);
 | 
			
		||||
		break;
 | 
			
		||||
	case 2:
 | 
			
		||||
	case 3:
 | 
			
		||||
		if (be32_to_cpu(hdr.v2.kernel_ofs) != sizeof(hdr))
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
 | 
			
		||||
		kernel_size = sizeof(hdr) + be32_to_cpu(hdr.v2.kernel_len);
 | 
			
		||||
		rootfs_offset = be32_to_cpu(hdr.v2.rootfs_ofs);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (kernel_size > master->size)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Find the rootfs */
 | 
			
		||||
	err = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * The size in the header might cover the rootfs as well.
 | 
			
		||||
		 * Start the search from an arbitrary offset.
 | 
			
		||||
		 */
 | 
			
		||||
		err = mtd_find_rootfs_from(master, TPLINK_MIN_ROOTFS_OFFS,
 | 
			
		||||
					   master->size, &rootfs_offset, NULL);
 | 
			
		||||
		if (err)
 | 
			
		||||
			return err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parts = kzalloc(TPLINK_NR_PARTS * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	parts[0].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[0].offset = 0;
 | 
			
		||||
	parts[0].size = kernel_size;
 | 
			
		||||
 | 
			
		||||
	parts[1].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[1].offset = rootfs_offset;
 | 
			
		||||
	parts[1].size = master->size - rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return TPLINK_NR_PARTS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_tplink_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "tplink,firmware" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_tplink_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "tplink-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_tplink_of_match_table,
 | 
			
		||||
	.parse_fn = mtdsplit_parse_tplink,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_tplink_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&mtdsplit_tplink_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subsys_initcall(mtdsplit_tplink_init);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,155 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 *  by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define TRX_MAGIC   0x30524448  /* "HDR0" */
 | 
			
		||||
 | 
			
		||||
struct trx_header {
 | 
			
		||||
	__le32 magic;
 | 
			
		||||
	__le32 len;
 | 
			
		||||
	__le32 crc32;
 | 
			
		||||
	__le32 flag_version;
 | 
			
		||||
	__le32 offset[4];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
read_trx_header(struct mtd_info *mtd, size_t offset,
 | 
			
		||||
		   struct trx_header *header)
 | 
			
		||||
{
 | 
			
		||||
	size_t header_len;
 | 
			
		||||
	size_t retlen;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	header_len = sizeof(*header);
 | 
			
		||||
	ret = mtd_read(mtd, offset, header_len, &retlen,
 | 
			
		||||
		       (unsigned char *) header);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_debug("read error in \"%s\"\n", mtd->name);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (retlen != header_len) {
 | 
			
		||||
		pr_debug("short read in \"%s\"\n", mtd->name);
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
mtdsplit_parse_trx(struct mtd_info *master,
 | 
			
		||||
		   const struct mtd_partition **pparts,
 | 
			
		||||
		   struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	struct trx_header hdr;
 | 
			
		||||
	int nr_parts;
 | 
			
		||||
	size_t offset;
 | 
			
		||||
	size_t trx_offset;
 | 
			
		||||
	size_t trx_size = 0;
 | 
			
		||||
	size_t rootfs_offset;
 | 
			
		||||
	size_t rootfs_size = 0;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	nr_parts = 2;
 | 
			
		||||
	parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	/* find trx image on erase block boundaries */
 | 
			
		||||
	for (offset = 0; offset < master->size; offset += master->erasesize) {
 | 
			
		||||
		trx_size = 0;
 | 
			
		||||
 | 
			
		||||
		ret = read_trx_header(master, offset, &hdr);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (hdr.magic != cpu_to_le32(TRX_MAGIC)) {
 | 
			
		||||
			pr_debug("no valid trx header found in \"%s\" at offset %llx\n",
 | 
			
		||||
				 master->name, (unsigned long long) offset);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		trx_size = le32_to_cpu(hdr.len);
 | 
			
		||||
		if ((offset + trx_size) > master->size) {
 | 
			
		||||
			pr_debug("trx image exceeds MTD device \"%s\"\n",
 | 
			
		||||
				 master->name);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (trx_size == 0) {
 | 
			
		||||
		pr_debug("no trx header found in \"%s\"\n", master->name);
 | 
			
		||||
		ret = -ENODEV;
 | 
			
		||||
		goto err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	trx_offset = offset + hdr.offset[0];
 | 
			
		||||
	rootfs_offset = offset + hdr.offset[1];
 | 
			
		||||
	rootfs_size = master->size - rootfs_offset;
 | 
			
		||||
	trx_size = rootfs_offset - trx_offset;
 | 
			
		||||
 | 
			
		||||
	if (rootfs_size == 0) {
 | 
			
		||||
		pr_debug("no rootfs found in \"%s\"\n", master->name);
 | 
			
		||||
		ret = -ENODEV;
 | 
			
		||||
		goto err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parts[0].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[0].offset = trx_offset;
 | 
			
		||||
	parts[0].size = trx_size;
 | 
			
		||||
 | 
			
		||||
	parts[1].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[1].offset = rootfs_offset;
 | 
			
		||||
	parts[1].size = rootfs_size;
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return nr_parts;
 | 
			
		||||
 | 
			
		||||
err:
 | 
			
		||||
	kfree(parts);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id trx_parser_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "openwrt,trx" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, trx_parser_of_match_table);
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser trx_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "trx-fw",
 | 
			
		||||
	.of_match_table = trx_parser_of_match_table,
 | 
			
		||||
	.parse_fn = mtdsplit_parse_trx,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_trx_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&trx_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module_init(mtdsplit_trx_init);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,282 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 *  by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/vmalloc.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/version.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
#include <dt-bindings/mtd/partitions/uimage.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Legacy format image header,
 | 
			
		||||
 * all data in network byte order (aka natural aka bigendian).
 | 
			
		||||
 */
 | 
			
		||||
struct uimage_header {
 | 
			
		||||
	uint32_t	ih_magic;	/* Image Header Magic Number	*/
 | 
			
		||||
	uint32_t	ih_hcrc;	/* Image Header CRC Checksum	*/
 | 
			
		||||
	uint32_t	ih_time;	/* Image Creation Timestamp	*/
 | 
			
		||||
	uint32_t	ih_size;	/* Image Data Size		*/
 | 
			
		||||
	uint32_t	ih_load;	/* Data	 Load  Address		*/
 | 
			
		||||
	uint32_t	ih_ep;		/* Entry Point Address		*/
 | 
			
		||||
	uint32_t	ih_dcrc;	/* Image Data CRC Checksum	*/
 | 
			
		||||
	uint8_t		ih_os;		/* Operating System		*/
 | 
			
		||||
	uint8_t		ih_arch;	/* CPU architecture		*/
 | 
			
		||||
	uint8_t		ih_type;	/* Image Type			*/
 | 
			
		||||
	uint8_t		ih_comp;	/* Compression Type		*/
 | 
			
		||||
	uint8_t		ih_name[IH_NMLEN];	/* Image Name		*/
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
read_uimage_header(struct mtd_info *mtd, size_t offset, u_char *buf,
 | 
			
		||||
		   size_t header_len)
 | 
			
		||||
{
 | 
			
		||||
	size_t retlen;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = mtd_read(mtd, offset, header_len, &retlen, buf);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_debug("read error in \"%s\"\n", mtd->name);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (retlen != header_len) {
 | 
			
		||||
		pr_debug("short read in \"%s\"\n", mtd->name);
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void uimage_parse_dt(struct mtd_info *master, int *extralen,
 | 
			
		||||
			    u32 *ih_magic, u32 *ih_type,
 | 
			
		||||
			    u32 *header_offset, u32 *part_magic)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *np = mtd_get_of_node(master);
 | 
			
		||||
 | 
			
		||||
	if (!np || !of_device_is_compatible(np, "openwrt,uimage"))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!of_property_read_u32(np, "openwrt,padding", extralen))
 | 
			
		||||
		pr_debug("got openwrt,padding=%d from device-tree\n", *extralen);
 | 
			
		||||
	if (!of_property_read_u32(np, "openwrt,ih-magic", ih_magic))
 | 
			
		||||
		pr_debug("got openwrt,ih-magic=%08x from device-tree\n", *ih_magic);
 | 
			
		||||
	if (!of_property_read_u32(np, "openwrt,ih-type", ih_type))
 | 
			
		||||
		pr_debug("got openwrt,ih-type=%08x from device-tree\n", *ih_type);
 | 
			
		||||
	if (!of_property_read_u32(np, "openwrt,offset", header_offset))
 | 
			
		||||
		pr_debug("got ih-start=%u from device-tree\n", *header_offset);
 | 
			
		||||
	if (!of_property_read_u32(np, "openwrt,partition-magic", part_magic))
 | 
			
		||||
		pr_debug("got openwrt,partition-magic=%08x from device-tree\n", *part_magic);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t uimage_verify_default(u_char *buf, u32 ih_magic, u32 ih_type)
 | 
			
		||||
{
 | 
			
		||||
	struct uimage_header *header = (struct uimage_header *)buf;
 | 
			
		||||
 | 
			
		||||
	/* default sanity checks */
 | 
			
		||||
	if (be32_to_cpu(header->ih_magic) != ih_magic) {
 | 
			
		||||
		pr_debug("invalid uImage magic: %08x != %08x\n",
 | 
			
		||||
			 be32_to_cpu(header->ih_magic), ih_magic);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (header->ih_os != IH_OS_LINUX) {
 | 
			
		||||
		pr_debug("invalid uImage OS: %08x != %08x\n",
 | 
			
		||||
			 be32_to_cpu(header->ih_os), IH_OS_LINUX);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (header->ih_type != ih_type) {
 | 
			
		||||
		pr_debug("invalid uImage type: %08x != %08x\n",
 | 
			
		||||
			 be32_to_cpu(header->ih_type), ih_type);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * __mtdsplit_parse_uimage - scan partition and create kernel + rootfs parts
 | 
			
		||||
 *
 | 
			
		||||
 * @find_header: function to call for a block of data that will return offset
 | 
			
		||||
 *      and tail padding length of a valid uImage header if found
 | 
			
		||||
 */
 | 
			
		||||
static int __mtdsplit_parse_uimage(struct mtd_info *master,
 | 
			
		||||
				   const struct mtd_partition **pparts,
 | 
			
		||||
				   struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	u_char *buf;
 | 
			
		||||
	int nr_parts;
 | 
			
		||||
	size_t offset;
 | 
			
		||||
	size_t uimage_offset;
 | 
			
		||||
	size_t uimage_size = 0;
 | 
			
		||||
	size_t rootfs_offset;
 | 
			
		||||
	size_t rootfs_size = 0;
 | 
			
		||||
	size_t buflen;
 | 
			
		||||
	int uimage_part, rf_part;
 | 
			
		||||
	int ret;
 | 
			
		||||
	int extralen = 0;
 | 
			
		||||
	u32 ih_magic = IH_MAGIC;
 | 
			
		||||
	u32 ih_type = IH_TYPE_KERNEL;
 | 
			
		||||
	u32 header_offset = 0;
 | 
			
		||||
	u32 part_magic = 0;
 | 
			
		||||
	enum mtdsplit_part_type type;
 | 
			
		||||
 | 
			
		||||
	nr_parts = 2;
 | 
			
		||||
	parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	uimage_parse_dt(master, &extralen, &ih_magic, &ih_type, &header_offset, &part_magic);
 | 
			
		||||
	buflen = sizeof(struct uimage_header) + header_offset;
 | 
			
		||||
	buf = vmalloc(buflen);
 | 
			
		||||
	if (!buf) {
 | 
			
		||||
		ret = -ENOMEM;
 | 
			
		||||
		goto err_free_parts;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* find uImage on erase block boundaries */
 | 
			
		||||
	for (offset = 0; offset < master->size; offset += master->erasesize) {
 | 
			
		||||
		struct uimage_header *header;
 | 
			
		||||
 | 
			
		||||
		uimage_size = 0;
 | 
			
		||||
 | 
			
		||||
		ret = read_uimage_header(master, offset, buf, buflen);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* verify optional partition magic before uimage header */
 | 
			
		||||
		if (header_offset && part_magic && (be32_to_cpu(*(u32 *)buf) != part_magic))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		ret = uimage_verify_default(buf + header_offset, ih_magic, ih_type);
 | 
			
		||||
		if (ret < 0) {
 | 
			
		||||
			pr_debug("no valid uImage found in \"%s\" at offset %llx\n",
 | 
			
		||||
				 master->name, (unsigned long long) offset);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		header = (struct uimage_header *)(buf + header_offset);
 | 
			
		||||
 | 
			
		||||
		uimage_size = sizeof(*header) +
 | 
			
		||||
				be32_to_cpu(header->ih_size) + header_offset + extralen;
 | 
			
		||||
 | 
			
		||||
		if ((offset + uimage_size) > master->size) {
 | 
			
		||||
			pr_debug("uImage exceeds MTD device \"%s\"\n",
 | 
			
		||||
				 master->name);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (uimage_size == 0) {
 | 
			
		||||
		pr_debug("no uImage found in \"%s\"\n", master->name);
 | 
			
		||||
		ret = -ENODEV;
 | 
			
		||||
		goto err_free_buf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uimage_offset = offset;
 | 
			
		||||
 | 
			
		||||
	if (uimage_offset == 0) {
 | 
			
		||||
		uimage_part = 0;
 | 
			
		||||
		rf_part = 1;
 | 
			
		||||
 | 
			
		||||
		/* find the roots after the uImage */
 | 
			
		||||
		ret = mtd_find_rootfs_from(master, uimage_offset + uimage_size,
 | 
			
		||||
					   master->size, &rootfs_offset, &type);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			pr_debug("no rootfs after uImage in \"%s\"\n",
 | 
			
		||||
				 master->name);
 | 
			
		||||
			goto err_free_buf;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rootfs_size = master->size - rootfs_offset;
 | 
			
		||||
		uimage_size = rootfs_offset - uimage_offset;
 | 
			
		||||
	} else {
 | 
			
		||||
		rf_part = 0;
 | 
			
		||||
		uimage_part = 1;
 | 
			
		||||
 | 
			
		||||
		/* check rootfs presence at offset 0 */
 | 
			
		||||
		ret = mtd_check_rootfs_magic(master, 0, &type);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			pr_debug("no rootfs before uImage in \"%s\"\n",
 | 
			
		||||
				 master->name);
 | 
			
		||||
			goto err_free_buf;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rootfs_offset = 0;
 | 
			
		||||
		rootfs_size = uimage_offset;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (rootfs_size == 0) {
 | 
			
		||||
		pr_debug("no rootfs found in \"%s\"\n", master->name);
 | 
			
		||||
		ret = -ENODEV;
 | 
			
		||||
		goto err_free_buf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parts[uimage_part].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[uimage_part].offset = uimage_offset;
 | 
			
		||||
	parts[uimage_part].size = uimage_size;
 | 
			
		||||
 | 
			
		||||
	if (type == MTDSPLIT_PART_TYPE_UBI)
 | 
			
		||||
		parts[rf_part].name = UBI_PART_NAME;
 | 
			
		||||
	else
 | 
			
		||||
		parts[rf_part].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[rf_part].offset = rootfs_offset;
 | 
			
		||||
	parts[rf_part].size = rootfs_size;
 | 
			
		||||
 | 
			
		||||
	vfree(buf);
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return nr_parts;
 | 
			
		||||
 | 
			
		||||
err_free_buf:
 | 
			
		||||
	vfree(buf);
 | 
			
		||||
 | 
			
		||||
err_free_parts:
 | 
			
		||||
	kfree(parts);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_uimage_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "denx,uimage" },
 | 
			
		||||
	{ .compatible = "openwrt,uimage" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser uimage_generic_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "uimage-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_uimage_of_match_table,
 | 
			
		||||
	.parse_fn = __mtdsplit_parse_uimage,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**************************************************
 | 
			
		||||
 * Init
 | 
			
		||||
 **************************************************/
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_uimage_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&uimage_generic_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module_init(mtdsplit_uimage_init);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,142 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
 *  Copyright (C) 2016 Stijn Tintel <stijn@linux-ipv6.be>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 *  by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/byteorder/generic.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
 | 
			
		||||
#include "mtdsplit.h"
 | 
			
		||||
 | 
			
		||||
#define WRGG_NR_PARTS		2
 | 
			
		||||
#define WRGG_MIN_ROOTFS_OFFS	0x80000	/* 512KiB */
 | 
			
		||||
#define WRGG03_MAGIC		0x20080321
 | 
			
		||||
#define WRG_MAGIC		0x20040220
 | 
			
		||||
 | 
			
		||||
struct wrgg03_header {
 | 
			
		||||
	char		signature[32];
 | 
			
		||||
	uint32_t	magic1;
 | 
			
		||||
	uint32_t	magic2;
 | 
			
		||||
	char		version[16];
 | 
			
		||||
	char		model[16];
 | 
			
		||||
	uint32_t	flag[2];
 | 
			
		||||
	uint32_t	reserve[2];
 | 
			
		||||
	char		buildno[16];
 | 
			
		||||
	uint32_t	size;
 | 
			
		||||
	uint32_t	offset;
 | 
			
		||||
	char		devname[32];
 | 
			
		||||
	char		digest[16];
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
struct wrg_header {
 | 
			
		||||
	char		signature[32];
 | 
			
		||||
	uint32_t	magic1;
 | 
			
		||||
	uint32_t	magic2;
 | 
			
		||||
	uint32_t	size;
 | 
			
		||||
	uint32_t	offset;
 | 
			
		||||
	char		devname[32];
 | 
			
		||||
	char		digest[16];
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int mtdsplit_parse_wrgg(struct mtd_info *master,
 | 
			
		||||
			       const struct mtd_partition **pparts,
 | 
			
		||||
			       struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct wrgg03_header hdr;
 | 
			
		||||
	size_t hdr_len, retlen, kernel_ent_size;
 | 
			
		||||
	size_t rootfs_offset;
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	enum mtdsplit_part_type type;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	hdr_len = sizeof(hdr);
 | 
			
		||||
	err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	if (retlen != hdr_len)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	/* sanity checks */
 | 
			
		||||
	if (le32_to_cpu(hdr.magic1) == WRGG03_MAGIC) {
 | 
			
		||||
		kernel_ent_size = hdr_len + be32_to_cpu(hdr.size);
 | 
			
		||||
		/*
 | 
			
		||||
		 * If this becomes silly big it's probably because the
 | 
			
		||||
		 * WRGG image is little-endian.
 | 
			
		||||
		 */
 | 
			
		||||
		if (kernel_ent_size > master->size)
 | 
			
		||||
			kernel_ent_size = hdr_len + le32_to_cpu(hdr.size);
 | 
			
		||||
 | 
			
		||||
		/* Now what ?! It's neither */
 | 
			
		||||
		if (kernel_ent_size > master->size)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
	} else if (le32_to_cpu(hdr.magic1) == WRG_MAGIC) {
 | 
			
		||||
		kernel_ent_size = sizeof(struct wrg_header) + le32_to_cpu(
 | 
			
		||||
		                  ((struct wrg_header*)&hdr)->size);
 | 
			
		||||
	} else {
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (kernel_ent_size > master->size)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The size in the header covers the rootfs as well.
 | 
			
		||||
	 * Start the search from an arbitrary offset.
 | 
			
		||||
	 */
 | 
			
		||||
	err = mtd_find_rootfs_from(master, WRGG_MIN_ROOTFS_OFFS,
 | 
			
		||||
				   master->size, &rootfs_offset, &type);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	parts = kzalloc(WRGG_NR_PARTS * sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	parts[0].name = KERNEL_PART_NAME;
 | 
			
		||||
	parts[0].offset = 0;
 | 
			
		||||
	parts[0].size = rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	parts[1].name = ROOTFS_PART_NAME;
 | 
			
		||||
	parts[1].offset = rootfs_offset;
 | 
			
		||||
	parts[1].size = master->size - rootfs_offset;
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return WRGG_NR_PARTS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id mtdsplit_wrgg_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "wrg" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, mtdsplit_wrgg_of_match_table);
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser mtdsplit_wrgg_parser = {
 | 
			
		||||
	.owner = THIS_MODULE,
 | 
			
		||||
	.name = "wrgg-fw",
 | 
			
		||||
	.of_match_table = mtdsplit_wrgg_of_match_table,
 | 
			
		||||
	.parse_fn = mtdsplit_parse_wrgg,
 | 
			
		||||
	.type = MTD_PARSER_TYPE_FIRMWARE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init mtdsplit_wrgg_init(void)
 | 
			
		||||
{
 | 
			
		||||
	register_mtd_parser(&mtdsplit_wrgg_parser);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subsys_initcall(mtdsplit_wrgg_init);
 | 
			
		||||
							
								
								
									
										465
									
								
								6.12/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										465
									
								
								6.12/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,465 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2017 MediaTek Inc.
 | 
			
		||||
 * Author: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
 | 
			
		||||
 * Copyright (c) 2020-2022 Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 * published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/gfp.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/bits.h>
 | 
			
		||||
#include "mtk_bmt.h"
 | 
			
		||||
 | 
			
		||||
struct bmt_desc bmtd = {};
 | 
			
		||||
 | 
			
		||||
/* -------- Nand operations wrapper -------- */
 | 
			
		||||
int bbt_nand_copy(u16 dest_blk, u16 src_blk, loff_t max_offset)
 | 
			
		||||
{
 | 
			
		||||
	int pages = bmtd.blk_size >> bmtd.pg_shift;
 | 
			
		||||
	loff_t src = (loff_t)src_blk << bmtd.blk_shift;
 | 
			
		||||
	loff_t dest = (loff_t)dest_blk << bmtd.blk_shift;
 | 
			
		||||
	loff_t offset = 0;
 | 
			
		||||
	uint8_t oob[64];
 | 
			
		||||
	int i, ret;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < pages; i++) {
 | 
			
		||||
		struct mtd_oob_ops rd_ops = {
 | 
			
		||||
			.mode = MTD_OPS_PLACE_OOB,
 | 
			
		||||
			.oobbuf = oob,
 | 
			
		||||
			.ooblen = min_t(int, bmtd.mtd->oobsize / pages, sizeof(oob)),
 | 
			
		||||
			.datbuf = bmtd.data_buf,
 | 
			
		||||
			.len = bmtd.pg_size,
 | 
			
		||||
		};
 | 
			
		||||
		struct mtd_oob_ops wr_ops = {
 | 
			
		||||
			.mode = MTD_OPS_PLACE_OOB,
 | 
			
		||||
			.oobbuf = oob,
 | 
			
		||||
			.datbuf = bmtd.data_buf,
 | 
			
		||||
			.len = bmtd.pg_size,
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		if (offset >= max_offset)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		ret = bmtd._read_oob(bmtd.mtd, src + offset, &rd_ops);
 | 
			
		||||
		if (ret < 0 && !mtd_is_bitflip(ret))
 | 
			
		||||
			return ret;
 | 
			
		||||
 | 
			
		||||
		if (!rd_ops.retlen)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		ret = bmtd._write_oob(bmtd.mtd, dest + offset, &wr_ops);
 | 
			
		||||
		if (ret < 0)
 | 
			
		||||
			return ret;
 | 
			
		||||
 | 
			
		||||
		wr_ops.ooblen = rd_ops.oobretlen;
 | 
			
		||||
		offset += rd_ops.retlen;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* -------- Bad Blocks Management -------- */
 | 
			
		||||
bool mapping_block_in_range(int block, int *start, int *end)
 | 
			
		||||
{
 | 
			
		||||
	const __be32 *cur = bmtd.remap_range;
 | 
			
		||||
	u32 addr = block << bmtd.blk_shift;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (!cur || !bmtd.remap_range_len) {
 | 
			
		||||
		*start = 0;
 | 
			
		||||
		*end = bmtd.total_blks;
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < bmtd.remap_range_len; i++, cur += 2) {
 | 
			
		||||
		if (addr < be32_to_cpu(cur[0]) || addr >= be32_to_cpu(cur[1]))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		*start = be32_to_cpu(cur[0]);
 | 
			
		||||
		*end = be32_to_cpu(cur[1]);
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
mtk_bmt_remap_block(u32 block, u32 mapped_block, int copy_len)
 | 
			
		||||
{
 | 
			
		||||
	int start, end;
 | 
			
		||||
 | 
			
		||||
	if (!mapping_block_in_range(block, &start, &end))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return bmtd.ops->remap_block(block, mapped_block, copy_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
mtk_bmt_read(struct mtd_info *mtd, loff_t from,
 | 
			
		||||
	     struct mtd_oob_ops *ops)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_oob_ops cur_ops = *ops;
 | 
			
		||||
	int retry_count = 0;
 | 
			
		||||
	loff_t cur_from;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
	int max_bitflips = 0;
 | 
			
		||||
 | 
			
		||||
	ops->retlen = 0;
 | 
			
		||||
	ops->oobretlen = 0;
 | 
			
		||||
 | 
			
		||||
	while (ops->retlen < ops->len || ops->oobretlen < ops->ooblen) {
 | 
			
		||||
		int cur_ret;
 | 
			
		||||
 | 
			
		||||
		u32 offset = from & (bmtd.blk_size - 1);
 | 
			
		||||
		u32 block = from >> bmtd.blk_shift;
 | 
			
		||||
		int cur_block;
 | 
			
		||||
 | 
			
		||||
		cur_block = bmtd.ops->get_mapping_block(block);
 | 
			
		||||
		if (cur_block < 0)
 | 
			
		||||
			return -EIO;
 | 
			
		||||
 | 
			
		||||
		cur_from = ((loff_t)cur_block << bmtd.blk_shift) + offset;
 | 
			
		||||
 | 
			
		||||
		cur_ops.oobretlen = 0;
 | 
			
		||||
		cur_ops.retlen = 0;
 | 
			
		||||
		cur_ops.len = min_t(u32, mtd->erasesize - offset,
 | 
			
		||||
					 ops->len - ops->retlen);
 | 
			
		||||
		cur_ret = bmtd._read_oob(mtd, cur_from, &cur_ops);
 | 
			
		||||
		if (cur_ret < 0)
 | 
			
		||||
			ret = cur_ret;
 | 
			
		||||
		else
 | 
			
		||||
			max_bitflips = max_t(int, max_bitflips, cur_ret);
 | 
			
		||||
		if (cur_ret < 0 && !mtd_is_bitflip(cur_ret)) {
 | 
			
		||||
			if (mtk_bmt_remap_block(block, cur_block, mtd->erasesize) &&
 | 
			
		||||
				retry_count++ < 10)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (mtd->bitflip_threshold && cur_ret >= mtd->bitflip_threshold)
 | 
			
		||||
			mtk_bmt_remap_block(block, cur_block, mtd->erasesize);
 | 
			
		||||
 | 
			
		||||
		ops->retlen += cur_ops.retlen;
 | 
			
		||||
		ops->oobretlen += cur_ops.oobretlen;
 | 
			
		||||
 | 
			
		||||
		cur_ops.ooboffs = 0;
 | 
			
		||||
		cur_ops.datbuf += cur_ops.retlen;
 | 
			
		||||
		cur_ops.oobbuf += cur_ops.oobretlen;
 | 
			
		||||
		cur_ops.ooblen -= cur_ops.oobretlen;
 | 
			
		||||
 | 
			
		||||
		if (!cur_ops.len)
 | 
			
		||||
			cur_ops.len = mtd->erasesize - offset;
 | 
			
		||||
 | 
			
		||||
		from += cur_ops.len;
 | 
			
		||||
		retry_count = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	return max_bitflips;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
mtk_bmt_write(struct mtd_info *mtd, loff_t to,
 | 
			
		||||
	      struct mtd_oob_ops *ops)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_oob_ops cur_ops = *ops;
 | 
			
		||||
	int retry_count = 0;
 | 
			
		||||
	loff_t cur_to;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ops->retlen = 0;
 | 
			
		||||
	ops->oobretlen = 0;
 | 
			
		||||
 | 
			
		||||
	while (ops->retlen < ops->len || ops->oobretlen < ops->ooblen) {
 | 
			
		||||
		u32 offset = to & (bmtd.blk_size - 1);
 | 
			
		||||
		u32 block = to >> bmtd.blk_shift;
 | 
			
		||||
		int cur_block;
 | 
			
		||||
 | 
			
		||||
		cur_block = bmtd.ops->get_mapping_block(block);
 | 
			
		||||
		if (cur_block < 0)
 | 
			
		||||
			return -EIO;
 | 
			
		||||
 | 
			
		||||
		cur_to = ((loff_t)cur_block << bmtd.blk_shift) + offset;
 | 
			
		||||
 | 
			
		||||
		cur_ops.oobretlen = 0;
 | 
			
		||||
		cur_ops.retlen = 0;
 | 
			
		||||
		cur_ops.len = min_t(u32, bmtd.blk_size - offset,
 | 
			
		||||
					 ops->len - ops->retlen);
 | 
			
		||||
		ret = bmtd._write_oob(mtd, cur_to, &cur_ops);
 | 
			
		||||
		if (ret < 0) {
 | 
			
		||||
			if (mtk_bmt_remap_block(block, cur_block, offset) &&
 | 
			
		||||
			    retry_count++ < 10)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			return ret;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ops->retlen += cur_ops.retlen;
 | 
			
		||||
		ops->oobretlen += cur_ops.oobretlen;
 | 
			
		||||
 | 
			
		||||
		cur_ops.ooboffs = 0;
 | 
			
		||||
		cur_ops.datbuf += cur_ops.retlen;
 | 
			
		||||
		cur_ops.oobbuf += cur_ops.oobretlen;
 | 
			
		||||
		cur_ops.ooblen -= cur_ops.oobretlen;
 | 
			
		||||
 | 
			
		||||
		if (!cur_ops.len)
 | 
			
		||||
			cur_ops.len = mtd->erasesize - offset;
 | 
			
		||||
 | 
			
		||||
		to += cur_ops.len;
 | 
			
		||||
		retry_count = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
mtk_bmt_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
 | 
			
		||||
{
 | 
			
		||||
	struct erase_info mapped_instr = {
 | 
			
		||||
		.len = bmtd.blk_size,
 | 
			
		||||
	};
 | 
			
		||||
	int retry_count = 0;
 | 
			
		||||
	u64 start_addr, end_addr;
 | 
			
		||||
	int ret;
 | 
			
		||||
	u16 orig_block;
 | 
			
		||||
	int block;
 | 
			
		||||
 | 
			
		||||
	start_addr = instr->addr & (~mtd->erasesize_mask);
 | 
			
		||||
	end_addr = instr->addr + instr->len;
 | 
			
		||||
 | 
			
		||||
	while (start_addr < end_addr) {
 | 
			
		||||
		orig_block = start_addr >> bmtd.blk_shift;
 | 
			
		||||
		block = bmtd.ops->get_mapping_block(orig_block);
 | 
			
		||||
		if (block < 0)
 | 
			
		||||
			return -EIO;
 | 
			
		||||
		mapped_instr.addr = (loff_t)block << bmtd.blk_shift;
 | 
			
		||||
		ret = bmtd._erase(mtd, &mapped_instr);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			if (mtk_bmt_remap_block(orig_block, block, 0) &&
 | 
			
		||||
			    retry_count++ < 10)
 | 
			
		||||
				continue;
 | 
			
		||||
			instr->fail_addr = start_addr;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		start_addr += mtd->erasesize;
 | 
			
		||||
		retry_count = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
static int
 | 
			
		||||
mtk_bmt_block_isbad(struct mtd_info *mtd, loff_t ofs)
 | 
			
		||||
{
 | 
			
		||||
	int retry_count = 0;
 | 
			
		||||
	u16 orig_block = ofs >> bmtd.blk_shift;
 | 
			
		||||
	u16 block;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
retry:
 | 
			
		||||
	block = bmtd.ops->get_mapping_block(orig_block);
 | 
			
		||||
	ret = bmtd._block_isbad(mtd, (loff_t)block << bmtd.blk_shift);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		if (mtk_bmt_remap_block(orig_block, block, bmtd.blk_size) &&
 | 
			
		||||
		    retry_count++ < 10)
 | 
			
		||||
			goto retry;
 | 
			
		||||
	}
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
mtk_bmt_block_markbad(struct mtd_info *mtd, loff_t ofs)
 | 
			
		||||
{
 | 
			
		||||
	u16 orig_block = ofs >> bmtd.blk_shift;
 | 
			
		||||
	int block;
 | 
			
		||||
 | 
			
		||||
	block = bmtd.ops->get_mapping_block(orig_block);
 | 
			
		||||
	if (block < 0)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	mtk_bmt_remap_block(orig_block, block, bmtd.blk_size);
 | 
			
		||||
 | 
			
		||||
	return bmtd._block_markbad(mtd, (loff_t)block << bmtd.blk_shift);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
mtk_bmt_replace_ops(struct mtd_info *mtd)
 | 
			
		||||
{
 | 
			
		||||
	bmtd._read_oob = mtd->_read_oob;
 | 
			
		||||
	bmtd._write_oob = mtd->_write_oob;
 | 
			
		||||
	bmtd._erase = mtd->_erase;
 | 
			
		||||
	bmtd._block_isbad = mtd->_block_isbad;
 | 
			
		||||
	bmtd._block_markbad = mtd->_block_markbad;
 | 
			
		||||
 | 
			
		||||
	mtd->_read_oob = mtk_bmt_read;
 | 
			
		||||
	mtd->_write_oob = mtk_bmt_write;
 | 
			
		||||
	mtd->_erase = mtk_bmt_mtd_erase;
 | 
			
		||||
	mtd->_block_isbad = mtk_bmt_block_isbad;
 | 
			
		||||
	mtd->_block_markbad = mtk_bmt_block_markbad;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtk_bmt_debug_repair(void *data, u64 val)
 | 
			
		||||
{
 | 
			
		||||
	int block = val >> bmtd.blk_shift;
 | 
			
		||||
	int prev_block, new_block;
 | 
			
		||||
 | 
			
		||||
	prev_block = bmtd.ops->get_mapping_block(block);
 | 
			
		||||
	if (prev_block < 0)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	bmtd.ops->unmap_block(block);
 | 
			
		||||
	new_block = bmtd.ops->get_mapping_block(block);
 | 
			
		||||
	if (new_block < 0)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	if (prev_block == new_block)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	bbt_nand_erase(new_block);
 | 
			
		||||
	bbt_nand_copy(new_block, prev_block, bmtd.blk_size);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtk_bmt_debug_mark_good(void *data, u64 val)
 | 
			
		||||
{
 | 
			
		||||
	bmtd.ops->unmap_block(val >> bmtd.blk_shift);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtk_bmt_debug_mark_bad(void *data, u64 val)
 | 
			
		||||
{
 | 
			
		||||
	u32 block = val >> bmtd.blk_shift;
 | 
			
		||||
	int cur_block;
 | 
			
		||||
 | 
			
		||||
	cur_block = bmtd.ops->get_mapping_block(block);
 | 
			
		||||
	if (cur_block < 0)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	mtk_bmt_remap_block(block, cur_block, bmtd.blk_size);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtk_bmt_debug(void *data, u64 val)
 | 
			
		||||
{
 | 
			
		||||
	return bmtd.ops->debug(data, val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_repair, NULL, mtk_bmt_debug_repair, "%llu\n");
 | 
			
		||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_mark_good, NULL, mtk_bmt_debug_mark_good, "%llu\n");
 | 
			
		||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_mark_bad, NULL, mtk_bmt_debug_mark_bad, "%llu\n");
 | 
			
		||||
DEFINE_DEBUGFS_ATTRIBUTE(fops_debug, NULL, mtk_bmt_debug, "%llu\n");
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
mtk_bmt_add_debugfs(void)
 | 
			
		||||
{
 | 
			
		||||
	struct dentry *dir;
 | 
			
		||||
 | 
			
		||||
	dir = bmtd.debugfs_dir = debugfs_create_dir("mtk-bmt", NULL);
 | 
			
		||||
	if (!dir)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	debugfs_create_file_unsafe("repair", S_IWUSR, dir, NULL, &fops_repair);
 | 
			
		||||
	debugfs_create_file_unsafe("mark_good", S_IWUSR, dir, NULL, &fops_mark_good);
 | 
			
		||||
	debugfs_create_file_unsafe("mark_bad", S_IWUSR, dir, NULL, &fops_mark_bad);
 | 
			
		||||
	debugfs_create_file_unsafe("debug", S_IWUSR, dir, NULL, &fops_debug);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mtk_bmt_detach(struct mtd_info *mtd)
 | 
			
		||||
{
 | 
			
		||||
	if (bmtd.mtd != mtd)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (bmtd.debugfs_dir)
 | 
			
		||||
		debugfs_remove_recursive(bmtd.debugfs_dir);
 | 
			
		||||
	bmtd.debugfs_dir = NULL;
 | 
			
		||||
 | 
			
		||||
	kfree(bmtd.bbt_buf);
 | 
			
		||||
	kfree(bmtd.data_buf);
 | 
			
		||||
 | 
			
		||||
	mtd->_read_oob = bmtd._read_oob;
 | 
			
		||||
	mtd->_write_oob = bmtd._write_oob;
 | 
			
		||||
	mtd->_erase = bmtd._erase;
 | 
			
		||||
	mtd->_block_isbad = bmtd._block_isbad;
 | 
			
		||||
	mtd->_block_markbad = bmtd._block_markbad;
 | 
			
		||||
	mtd->size = bmtd.total_blks << bmtd.blk_shift;
 | 
			
		||||
 | 
			
		||||
	memset(&bmtd, 0, sizeof(bmtd));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int mtk_bmt_attach(struct mtd_info *mtd)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *np;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	if (bmtd.mtd)
 | 
			
		||||
		return -ENOSPC;
 | 
			
		||||
 | 
			
		||||
	np = mtd_get_of_node(mtd);
 | 
			
		||||
	if (!np)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (of_property_read_bool(np, "mediatek,bmt-v2"))
 | 
			
		||||
		bmtd.ops = &mtk_bmt_v2_ops;
 | 
			
		||||
	else if (of_property_read_bool(np, "mediatek,nmbm"))
 | 
			
		||||
		bmtd.ops = &mtk_bmt_nmbm_ops;
 | 
			
		||||
	else if (of_property_read_bool(np, "mediatek,bbt"))
 | 
			
		||||
		bmtd.ops = &mtk_bmt_bbt_ops;
 | 
			
		||||
	else
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	bmtd.remap_range = of_get_property(np, "mediatek,bmt-remap-range",
 | 
			
		||||
					   &bmtd.remap_range_len);
 | 
			
		||||
	bmtd.remap_range_len /= 8;
 | 
			
		||||
 | 
			
		||||
	bmtd.mtd = mtd;
 | 
			
		||||
	mtk_bmt_replace_ops(mtd);
 | 
			
		||||
 | 
			
		||||
	bmtd.blk_size = mtd->erasesize;
 | 
			
		||||
	bmtd.blk_shift = ffs(bmtd.blk_size) - 1;
 | 
			
		||||
	bmtd.pg_size = mtd->writesize;
 | 
			
		||||
	bmtd.pg_shift = ffs(bmtd.pg_size) - 1;
 | 
			
		||||
	bmtd.total_blks = mtd->size >> bmtd.blk_shift;
 | 
			
		||||
 | 
			
		||||
	bmtd.data_buf = kzalloc(bmtd.pg_size + bmtd.mtd->oobsize, GFP_KERNEL);
 | 
			
		||||
	if (!bmtd.data_buf) {
 | 
			
		||||
		pr_info("nand: FATAL ERR: allocate buffer failed!\n");
 | 
			
		||||
		ret = -1;
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memset(bmtd.data_buf, 0xff, bmtd.pg_size + bmtd.mtd->oobsize);
 | 
			
		||||
 | 
			
		||||
	ret = bmtd.ops->init(np);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	mtk_bmt_add_debugfs();
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	mtk_bmt_detach(mtd);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
MODULE_LICENSE("GPL");
 | 
			
		||||
MODULE_AUTHOR("Xiangsheng Hou <xiangsheng.hou@mediatek.com>, Felix Fietkau <nbd@nbd.name>");
 | 
			
		||||
MODULE_DESCRIPTION("Bad Block mapping management v2 for MediaTek NAND Flash Driver");
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										137
									
								
								6.12/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								6.12/target/linux/generic/files/drivers/mtd/nand/mtk_bmt.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,137 @@
 | 
			
		|||
#ifndef __MTK_BMT_PRIV_H
 | 
			
		||||
#define __MTK_BMT_PRIV_H
 | 
			
		||||
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/mtd/mtk_bmt.h>
 | 
			
		||||
#include <linux/debugfs.h>
 | 
			
		||||
 | 
			
		||||
#define MAIN_SIGNATURE_OFFSET   0
 | 
			
		||||
#define OOB_SIGNATURE_OFFSET    1
 | 
			
		||||
 | 
			
		||||
#define BBT_LOG(fmt, ...) pr_debug("[BBT][%s|%d] "fmt"\n", __func__, __LINE__, ##__VA_ARGS__)
 | 
			
		||||
 | 
			
		||||
struct mtk_bmt_ops {
 | 
			
		||||
	char *sig;
 | 
			
		||||
	unsigned int sig_len;
 | 
			
		||||
	int (*init)(struct device_node *np);
 | 
			
		||||
	bool (*remap_block)(u16 block, u16 mapped_block, int copy_len);
 | 
			
		||||
	void (*unmap_block)(u16 block);
 | 
			
		||||
	int (*get_mapping_block)(int block);
 | 
			
		||||
	int (*debug)(void *data, u64 val);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct bbbt;
 | 
			
		||||
struct nmbm_instance;
 | 
			
		||||
 | 
			
		||||
struct bmt_desc {
 | 
			
		||||
	struct mtd_info *mtd;
 | 
			
		||||
	unsigned char *bbt_buf;
 | 
			
		||||
	unsigned char *data_buf;
 | 
			
		||||
 | 
			
		||||
	int (*_read_oob) (struct mtd_info *mtd, loff_t from,
 | 
			
		||||
			  struct mtd_oob_ops *ops);
 | 
			
		||||
	int (*_write_oob) (struct mtd_info *mtd, loff_t to,
 | 
			
		||||
			   struct mtd_oob_ops *ops);
 | 
			
		||||
	int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
 | 
			
		||||
	int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
 | 
			
		||||
	int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
 | 
			
		||||
 | 
			
		||||
	const struct mtk_bmt_ops *ops;
 | 
			
		||||
 | 
			
		||||
	union {
 | 
			
		||||
		struct bbbt *bbt;
 | 
			
		||||
		struct nmbm_instance *ni;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct dentry *debugfs_dir;
 | 
			
		||||
 | 
			
		||||
	u32 table_size;
 | 
			
		||||
	u32 pg_size;
 | 
			
		||||
	u32 blk_size;
 | 
			
		||||
	u16 pg_shift;
 | 
			
		||||
	u16 blk_shift;
 | 
			
		||||
	/* bbt logical address */
 | 
			
		||||
	u16 pool_lba;
 | 
			
		||||
	/* bbt physical address */
 | 
			
		||||
	u16 pool_pba;
 | 
			
		||||
	/* Maximum count of bad blocks that the vendor guaranteed */
 | 
			
		||||
	u16 bb_max;
 | 
			
		||||
	/* Total blocks of the Nand Chip */
 | 
			
		||||
	u16 total_blks;
 | 
			
		||||
	/* The block(n) BMT is located at (bmt_tbl[n]) */
 | 
			
		||||
	u16 bmt_blk_idx;
 | 
			
		||||
	/* How many pages needs to store 'struct bbbt' */
 | 
			
		||||
	u32 bmt_pgs;
 | 
			
		||||
 | 
			
		||||
	const __be32 *remap_range;
 | 
			
		||||
	int remap_range_len;
 | 
			
		||||
 | 
			
		||||
	/* to compensate for driver level remapping */
 | 
			
		||||
	u8 oob_offset;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct bmt_desc bmtd;
 | 
			
		||||
extern const struct mtk_bmt_ops mtk_bmt_v2_ops;
 | 
			
		||||
extern const struct mtk_bmt_ops mtk_bmt_bbt_ops;
 | 
			
		||||
extern const struct mtk_bmt_ops mtk_bmt_nmbm_ops;
 | 
			
		||||
 | 
			
		||||
static inline u32 blk_pg(u16 block)
 | 
			
		||||
{
 | 
			
		||||
	return (u32)(block << (bmtd.blk_shift - bmtd.pg_shift));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int
 | 
			
		||||
bbt_nand_read(u32 page, unsigned char *dat, int dat_len,
 | 
			
		||||
	      unsigned char *fdm, int fdm_len)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_oob_ops ops = {
 | 
			
		||||
		.mode = MTD_OPS_PLACE_OOB,
 | 
			
		||||
		.ooboffs = bmtd.oob_offset,
 | 
			
		||||
		.oobbuf = fdm,
 | 
			
		||||
		.ooblen = fdm_len,
 | 
			
		||||
		.datbuf = dat,
 | 
			
		||||
		.len = dat_len,
 | 
			
		||||
	};
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = bmtd._read_oob(bmtd.mtd, page << bmtd.pg_shift, &ops);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
	if (ret)
 | 
			
		||||
		pr_info("%s: %d bitflips\n", __func__, ret);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int bbt_nand_erase(u16 block)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_info *mtd = bmtd.mtd;
 | 
			
		||||
	struct erase_info instr = {
 | 
			
		||||
		.addr = (loff_t)block << bmtd.blk_shift,
 | 
			
		||||
		.len = bmtd.blk_size,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	return bmtd._erase(mtd, &instr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int write_bmt(u16 block, unsigned char *dat)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_oob_ops ops = {
 | 
			
		||||
		.mode = MTD_OPS_PLACE_OOB,
 | 
			
		||||
		.ooboffs = OOB_SIGNATURE_OFFSET + bmtd.oob_offset,
 | 
			
		||||
		.oobbuf = bmtd.ops->sig,
 | 
			
		||||
		.ooblen = bmtd.ops->sig_len,
 | 
			
		||||
		.datbuf = dat,
 | 
			
		||||
		.len = bmtd.bmt_pgs << bmtd.pg_shift,
 | 
			
		||||
	};
 | 
			
		||||
	loff_t addr = (loff_t)block << bmtd.blk_shift;
 | 
			
		||||
 | 
			
		||||
	return bmtd._write_oob(bmtd.mtd, addr, &ops);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bbt_nand_copy(u16 dest_blk, u16 src_blk, loff_t max_offset);
 | 
			
		||||
bool mapping_block_in_range(int block, int *start, int *end);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										203
									
								
								6.12/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_bbt.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								6.12/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_bbt.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,203 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2017 MediaTek Inc.
 | 
			
		||||
 * Author: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
 | 
			
		||||
 * Copyright (c) 2020-2022 Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 * published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include "mtk_bmt.h"
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
bbt_block_is_bad(u16 block)
 | 
			
		||||
{
 | 
			
		||||
	u8 cur = bmtd.bbt_buf[block / 4];
 | 
			
		||||
 | 
			
		||||
	return cur & (3 << ((block % 4) * 2));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
bbt_set_block_state(u16 block, bool bad)
 | 
			
		||||
{
 | 
			
		||||
	u8 mask = (3 << ((block % 4) * 2));
 | 
			
		||||
 | 
			
		||||
	if (bad)
 | 
			
		||||
		bmtd.bbt_buf[block / 4] |= mask;
 | 
			
		||||
	else
 | 
			
		||||
		bmtd.bbt_buf[block / 4] &= ~mask;
 | 
			
		||||
 | 
			
		||||
	bbt_nand_erase(bmtd.bmt_blk_idx);
 | 
			
		||||
	write_bmt(bmtd.bmt_blk_idx, bmtd.bbt_buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
get_mapping_block_index_bbt(int block)
 | 
			
		||||
{
 | 
			
		||||
	int start, end, ofs;
 | 
			
		||||
	int bad_blocks = 0;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (!mapping_block_in_range(block, &start, &end))
 | 
			
		||||
		return block;
 | 
			
		||||
 | 
			
		||||
	start >>= bmtd.blk_shift;
 | 
			
		||||
	end >>= bmtd.blk_shift;
 | 
			
		||||
	/* skip bad blocks within the mapping range */
 | 
			
		||||
	ofs = block - start;
 | 
			
		||||
	for (i = start; i < end; i++) {
 | 
			
		||||
		if (bbt_block_is_bad(i))
 | 
			
		||||
			bad_blocks++;
 | 
			
		||||
		else if (ofs)
 | 
			
		||||
			ofs--;
 | 
			
		||||
		else
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (i < end)
 | 
			
		||||
		return i;
 | 
			
		||||
 | 
			
		||||
	/* when overflowing, remap remaining blocks to bad ones */
 | 
			
		||||
	for (i = end - 1; bad_blocks > 0; i--) {
 | 
			
		||||
		if (!bbt_block_is_bad(i))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		bad_blocks--;
 | 
			
		||||
		if (bad_blocks <= ofs)
 | 
			
		||||
			return i;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool remap_block_bbt(u16 block, u16 mapped_blk, int copy_len)
 | 
			
		||||
{
 | 
			
		||||
	int start, end;
 | 
			
		||||
	u16 new_blk;
 | 
			
		||||
 | 
			
		||||
	if (!mapping_block_in_range(block, &start, &end))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	bbt_set_block_state(mapped_blk, true);
 | 
			
		||||
 | 
			
		||||
	new_blk = get_mapping_block_index_bbt(block);
 | 
			
		||||
	bbt_nand_erase(new_blk);
 | 
			
		||||
	if (copy_len > 0)
 | 
			
		||||
		bbt_nand_copy(new_blk, mapped_blk, copy_len);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
unmap_block_bbt(u16 block)
 | 
			
		||||
{
 | 
			
		||||
	bbt_set_block_state(block, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
mtk_bmt_read_bbt(void)
 | 
			
		||||
{
 | 
			
		||||
	u8 oob_buf[8];
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = bmtd.total_blks - 1; i >= bmtd.total_blks - 5; i--) {
 | 
			
		||||
		u32 page = i << (bmtd.blk_shift - bmtd.pg_shift);
 | 
			
		||||
 | 
			
		||||
		if (bbt_nand_read(page, bmtd.bbt_buf, bmtd.pg_size,
 | 
			
		||||
				  oob_buf, sizeof(oob_buf))) {
 | 
			
		||||
			pr_info("read_bbt: could not read block %d\n", i);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (oob_buf[0] != 0xff) {
 | 
			
		||||
			pr_info("read_bbt: bad block at %d\n", i);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (memcmp(&oob_buf[1], "mtknand", 7) != 0) {
 | 
			
		||||
			pr_info("read_bbt: signature mismatch in block %d\n", i);
 | 
			
		||||
			print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, oob_buf, 8, 1);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pr_info("read_bbt: found bbt at block %d\n", i);
 | 
			
		||||
		bmtd.bmt_blk_idx = i;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -EIO;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
mtk_bmt_init_bbt(struct device_node *np)
 | 
			
		||||
{
 | 
			
		||||
	int buf_size = round_up(bmtd.total_blks >> 2, bmtd.blk_size);
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	bmtd.bbt_buf = kmalloc(buf_size, GFP_KERNEL);
 | 
			
		||||
	if (!bmtd.bbt_buf)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	memset(bmtd.bbt_buf, 0xff, buf_size);
 | 
			
		||||
	bmtd.mtd->size -= 4 * bmtd.mtd->erasesize;
 | 
			
		||||
 | 
			
		||||
	ret = mtk_bmt_read_bbt();
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	bmtd.bmt_pgs = buf_size / bmtd.pg_size;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtk_bmt_debug_bbt(void *data, u64 val)
 | 
			
		||||
{
 | 
			
		||||
	char buf[5];
 | 
			
		||||
	int i, k;
 | 
			
		||||
 | 
			
		||||
	switch (val) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		for (i = 0; i < bmtd.total_blks; i += 4) {
 | 
			
		||||
			u8 cur = bmtd.bbt_buf[i / 4];
 | 
			
		||||
 | 
			
		||||
			for (k = 0; k < 4; k++, cur >>= 2)
 | 
			
		||||
				buf[k] = (cur & 3) ? 'B' : '.';
 | 
			
		||||
 | 
			
		||||
			buf[4] = 0;
 | 
			
		||||
			printk("[%06x] %s\n", i * bmtd.blk_size, buf);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case 100:
 | 
			
		||||
#if 0
 | 
			
		||||
		for (i = bmtd.bmt_blk_idx; i < bmtd.total_blks - 1; i++)
 | 
			
		||||
			bbt_nand_erase(bmtd.bmt_blk_idx);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		bmtd.bmt_blk_idx = bmtd.total_blks - 1;
 | 
			
		||||
		bbt_nand_erase(bmtd.bmt_blk_idx);
 | 
			
		||||
		write_bmt(bmtd.bmt_blk_idx, bmtd.bbt_buf);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct mtk_bmt_ops mtk_bmt_bbt_ops = {
 | 
			
		||||
	.sig = "mtknand",
 | 
			
		||||
	.sig_len = 7,
 | 
			
		||||
	.init = mtk_bmt_init_bbt,
 | 
			
		||||
	.remap_block = remap_block_bbt,
 | 
			
		||||
	.unmap_block = unmap_block_bbt,
 | 
			
		||||
	.get_mapping_block = get_mapping_block_index_bbt,
 | 
			
		||||
	.debug = mtk_bmt_debug_bbt,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										2348
									
								
								6.12/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_nmbm.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2348
									
								
								6.12/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_nmbm.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										506
									
								
								6.12/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_v2.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										506
									
								
								6.12/target/linux/generic/files/drivers/mtd/nand/mtk_bmt_v2.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,506 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2017 MediaTek Inc.
 | 
			
		||||
 * Author: Xiangsheng Hou <xiangsheng.hou@mediatek.com>
 | 
			
		||||
 * Copyright (c) 2020-2022 Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License version 2 as
 | 
			
		||||
 * published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include "mtk_bmt.h"
 | 
			
		||||
 | 
			
		||||
struct bbbt {
 | 
			
		||||
	char signature[3];
 | 
			
		||||
	/* This version is used to distinguish the legacy and new algorithm */
 | 
			
		||||
#define BBMT_VERSION		2
 | 
			
		||||
	unsigned char version;
 | 
			
		||||
	/* Below 2 tables will be written in SLC */
 | 
			
		||||
	u16 bb_tbl[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct bbmt {
 | 
			
		||||
	u16 block;
 | 
			
		||||
#define NO_MAPPED		0
 | 
			
		||||
#define NORMAL_MAPPED	1
 | 
			
		||||
#define BMT_MAPPED		2
 | 
			
		||||
	u16 mapped;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Maximum 8k blocks */
 | 
			
		||||
#define BBPOOL_RATIO		2
 | 
			
		||||
#define BB_TABLE_MAX	bmtd.table_size
 | 
			
		||||
#define BMT_TABLE_MAX	(BB_TABLE_MAX * BBPOOL_RATIO / 100)
 | 
			
		||||
#define BMT_TBL_DEF_VAL	0x0
 | 
			
		||||
 | 
			
		||||
static inline struct bbmt *bmt_tbl(struct bbbt *bbbt)
 | 
			
		||||
{
 | 
			
		||||
	return (struct bbmt *)&bbbt->bb_tbl[bmtd.table_size];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u16 find_valid_block(u16 block)
 | 
			
		||||
{
 | 
			
		||||
	u8 fdm[4];
 | 
			
		||||
	int ret;
 | 
			
		||||
	int loop = 0;
 | 
			
		||||
 | 
			
		||||
retry:
 | 
			
		||||
	if (block >= bmtd.total_blks)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	ret = bbt_nand_read(blk_pg(block), bmtd.data_buf, bmtd.pg_size,
 | 
			
		||||
			    fdm, sizeof(fdm));
 | 
			
		||||
	/* Read the 1st byte of FDM to judge whether it's a bad
 | 
			
		||||
	 * or not
 | 
			
		||||
	 */
 | 
			
		||||
	if (ret || fdm[0] != 0xff) {
 | 
			
		||||
		pr_info("nand: found bad block 0x%x\n", block);
 | 
			
		||||
		if (loop >= bmtd.bb_max) {
 | 
			
		||||
			pr_info("nand: FATAL ERR: too many bad blocks!!\n");
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		loop++;
 | 
			
		||||
		block++;
 | 
			
		||||
		goto retry;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find out all bad blocks, and fill in the mapping table */
 | 
			
		||||
static int scan_bad_blocks(struct bbbt *bbt)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	u16 block = 0;
 | 
			
		||||
 | 
			
		||||
	/* First time download, the block0 MUST NOT be a bad block,
 | 
			
		||||
	 * this is guaranteed by vendor
 | 
			
		||||
	 */
 | 
			
		||||
	bbt->bb_tbl[0] = 0;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Construct the mapping table of Normal data area(non-PMT/BMTPOOL)
 | 
			
		||||
	 * G - Good block; B - Bad block
 | 
			
		||||
	 *			---------------------------
 | 
			
		||||
	 * physical |G|G|B|G|B|B|G|G|G|G|B|G|B|
 | 
			
		||||
	 *			---------------------------
 | 
			
		||||
	 * What bb_tbl[i] looks like:
 | 
			
		||||
	 *   physical block(i):
 | 
			
		||||
	 *			 0 1 2 3 4 5 6 7 8 9 a b c
 | 
			
		||||
	 *   mapped block(bb_tbl[i]):
 | 
			
		||||
	 *			 0 1 3 6 7 8 9 b ......
 | 
			
		||||
	 * ATTENTION:
 | 
			
		||||
	 *		If new bad block ocurred(n), search bmt_tbl to find
 | 
			
		||||
	 *		a available block(x), and fill in the bb_tbl[n] = x;
 | 
			
		||||
	 */
 | 
			
		||||
	for (i = 1; i < bmtd.pool_lba; i++) {
 | 
			
		||||
		bbt->bb_tbl[i] = find_valid_block(bbt->bb_tbl[i - 1] + 1);
 | 
			
		||||
		BBT_LOG("bb_tbl[0x%x] = 0x%x", i, bbt->bb_tbl[i]);
 | 
			
		||||
		if (bbt->bb_tbl[i] == 0)
 | 
			
		||||
			return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Physical Block start Address of BMT pool */
 | 
			
		||||
	bmtd.pool_pba = bbt->bb_tbl[i - 1] + 1;
 | 
			
		||||
	if (bmtd.pool_pba >= bmtd.total_blks - 2) {
 | 
			
		||||
		pr_info("nand: FATAL ERR: Too many bad blocks!!\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	BBT_LOG("pool_pba=0x%x", bmtd.pool_pba);
 | 
			
		||||
	i = 0;
 | 
			
		||||
	block = bmtd.pool_pba;
 | 
			
		||||
	/*
 | 
			
		||||
	 * The bmt table is used for runtime bad block mapping
 | 
			
		||||
	 * G - Good block; B - Bad block
 | 
			
		||||
	 *			---------------------------
 | 
			
		||||
	 * physical |G|G|B|G|B|B|G|G|G|G|B|G|B|
 | 
			
		||||
	 *			---------------------------
 | 
			
		||||
	 *   block:	 0 1 2 3 4 5 6 7 8 9 a b c
 | 
			
		||||
	 * What bmt_tbl[i] looks like in initial state:
 | 
			
		||||
	 *   i:
 | 
			
		||||
	 *			 0 1 2 3 4 5 6 7
 | 
			
		||||
	 *   bmt_tbl[i].block:
 | 
			
		||||
	 *			 0 1 3 6 7 8 9 b
 | 
			
		||||
	 *   bmt_tbl[i].mapped:
 | 
			
		||||
	 *			 N N N N N N N B
 | 
			
		||||
	 *		N - Not mapped(Available)
 | 
			
		||||
	 *		M - Mapped
 | 
			
		||||
	 *		B - BMT
 | 
			
		||||
	 * ATTENTION:
 | 
			
		||||
	 *		BMT always in the last valid block in pool
 | 
			
		||||
	 */
 | 
			
		||||
	while ((block = find_valid_block(block)) != 0) {
 | 
			
		||||
		bmt_tbl(bbt)[i].block = block;
 | 
			
		||||
		bmt_tbl(bbt)[i].mapped = NO_MAPPED;
 | 
			
		||||
		BBT_LOG("bmt_tbl[%d].block = 0x%x", i, block);
 | 
			
		||||
		block++;
 | 
			
		||||
		i++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* i - How many available blocks in pool, which is the length of bmt_tbl[]
 | 
			
		||||
	 * bmtd.bmt_blk_idx - bmt_tbl[bmtd.bmt_blk_idx].block => the BMT block
 | 
			
		||||
	 */
 | 
			
		||||
	bmtd.bmt_blk_idx = i - 1;
 | 
			
		||||
	bmt_tbl(bbt)[bmtd.bmt_blk_idx].mapped = BMT_MAPPED;
 | 
			
		||||
 | 
			
		||||
	if (i < 1) {
 | 
			
		||||
		pr_info("nand: FATAL ERR: no space to store BMT!!\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pr_info("[BBT] %d available blocks in BMT pool\n", i);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool is_valid_bmt(unsigned char *buf, unsigned char *fdm)
 | 
			
		||||
{
 | 
			
		||||
	struct bbbt *bbt = (struct bbbt *)buf;
 | 
			
		||||
	u8 *sig = (u8*)bbt->signature + MAIN_SIGNATURE_OFFSET;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (memcmp(bbt->signature + MAIN_SIGNATURE_OFFSET, "BMT", 3) == 0 &&
 | 
			
		||||
		memcmp(fdm + OOB_SIGNATURE_OFFSET, "bmt", 3) == 0) {
 | 
			
		||||
		if (bbt->version == BBMT_VERSION)
 | 
			
		||||
			return true;
 | 
			
		||||
	}
 | 
			
		||||
	BBT_LOG("[BBT] BMT Version not match,upgrage preloader and uboot please! sig=%02x%02x%02x, fdm=%02x%02x%02x",
 | 
			
		||||
		sig[0], sig[1], sig[2],
 | 
			
		||||
		fdm[1], fdm[2], fdm[3]);
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u16 get_bmt_index(struct bbmt *bmt)
 | 
			
		||||
{
 | 
			
		||||
	int i = 0;
 | 
			
		||||
 | 
			
		||||
	while (bmt[i].block != BMT_TBL_DEF_VAL) {
 | 
			
		||||
		if (bmt[i].mapped == BMT_MAPPED)
 | 
			
		||||
			return i;
 | 
			
		||||
		i++;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write the Burner Bad Block Table to Nand Flash
 | 
			
		||||
 * n - write BMT to bmt_tbl[n]
 | 
			
		||||
 */
 | 
			
		||||
static u16 upload_bmt(struct bbbt *bbt, int n)
 | 
			
		||||
{
 | 
			
		||||
	u16 block;
 | 
			
		||||
 | 
			
		||||
retry:
 | 
			
		||||
	if (n < 0 || bmt_tbl(bbt)[n].mapped == NORMAL_MAPPED) {
 | 
			
		||||
		pr_info("nand: FATAL ERR: no space to store BMT!\n");
 | 
			
		||||
		return (u16)-1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	block = bmt_tbl(bbt)[n].block;
 | 
			
		||||
	BBT_LOG("n = 0x%x, block = 0x%x", n, block);
 | 
			
		||||
	if (bbt_nand_erase(block)) {
 | 
			
		||||
		bmt_tbl(bbt)[n].block = 0;
 | 
			
		||||
		/* erase failed, try the previous block: bmt_tbl[n - 1].block */
 | 
			
		||||
		n--;
 | 
			
		||||
		goto retry;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* The signature offset is fixed set to 0,
 | 
			
		||||
	 * oob signature offset is fixed set to 1
 | 
			
		||||
	 */
 | 
			
		||||
	memcpy(bbt->signature + MAIN_SIGNATURE_OFFSET, "BMT", 3);
 | 
			
		||||
	bbt->version = BBMT_VERSION;
 | 
			
		||||
 | 
			
		||||
	if (write_bmt(block, (unsigned char *)bbt)) {
 | 
			
		||||
		bmt_tbl(bbt)[n].block = 0;
 | 
			
		||||
 | 
			
		||||
		/* write failed, try the previous block in bmt_tbl[n - 1] */
 | 
			
		||||
		n--;
 | 
			
		||||
		goto retry;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Return the current index(n) of BMT pool (bmt_tbl[n]) */
 | 
			
		||||
	return n;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u16 find_valid_block_in_pool(struct bbbt *bbt)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (bmtd.bmt_blk_idx == 0)
 | 
			
		||||
		goto error;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < bmtd.bmt_blk_idx; i++) {
 | 
			
		||||
		if (bmt_tbl(bbt)[i].block != 0 && bmt_tbl(bbt)[i].mapped == NO_MAPPED) {
 | 
			
		||||
			bmt_tbl(bbt)[i].mapped = NORMAL_MAPPED;
 | 
			
		||||
			return bmt_tbl(bbt)[i].block;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	pr_info("nand: FATAL ERR: BMT pool is run out!\n");
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* We met a bad block, mark it as bad and map it to a valid block in pool,
 | 
			
		||||
 * if it's a write failure, we need to write the data to mapped block
 | 
			
		||||
 */
 | 
			
		||||
static bool remap_block_v2(u16 block, u16 mapped_block, int copy_len)
 | 
			
		||||
{
 | 
			
		||||
	u16 new_block;
 | 
			
		||||
	struct bbbt *bbt;
 | 
			
		||||
 | 
			
		||||
	bbt = bmtd.bbt;
 | 
			
		||||
	new_block = find_valid_block_in_pool(bbt);
 | 
			
		||||
	if (new_block == 0)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/* Map new bad block to available block in pool */
 | 
			
		||||
	bbt->bb_tbl[block] = new_block;
 | 
			
		||||
 | 
			
		||||
	/* Erase new block */
 | 
			
		||||
	bbt_nand_erase(new_block);
 | 
			
		||||
	if (copy_len > 0)
 | 
			
		||||
		bbt_nand_copy(new_block, mapped_block, copy_len);
 | 
			
		||||
 | 
			
		||||
	bmtd.bmt_blk_idx = upload_bmt(bbt, bmtd.bmt_blk_idx);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int get_mapping_block_index_v2(int block)
 | 
			
		||||
{
 | 
			
		||||
	int start, end;
 | 
			
		||||
 | 
			
		||||
	if (block >= bmtd.pool_lba)
 | 
			
		||||
		return block;
 | 
			
		||||
 | 
			
		||||
	if (!mapping_block_in_range(block, &start, &end))
 | 
			
		||||
		return block;
 | 
			
		||||
 | 
			
		||||
	return bmtd.bbt->bb_tbl[block];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
unmap_block_v2(u16 block)
 | 
			
		||||
{
 | 
			
		||||
	bmtd.bbt->bb_tbl[block] = block;
 | 
			
		||||
	bmtd.bmt_blk_idx = upload_bmt(bmtd.bbt, bmtd.bmt_blk_idx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned long *
 | 
			
		||||
mtk_bmt_get_mapping_mask(void)
 | 
			
		||||
{
 | 
			
		||||
	struct bbmt *bbmt = bmt_tbl(bmtd.bbt);
 | 
			
		||||
	int main_blocks = bmtd.mtd->size >> bmtd.blk_shift;
 | 
			
		||||
	unsigned long *used;
 | 
			
		||||
	int i, k;
 | 
			
		||||
 | 
			
		||||
	used = kcalloc(sizeof(unsigned long), BIT_WORD(bmtd.bmt_blk_idx) + 1, GFP_KERNEL);
 | 
			
		||||
	if (!used)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	for (i = 1; i < main_blocks; i++) {
 | 
			
		||||
		if (bmtd.bbt->bb_tbl[i] == i)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		for (k = 0; k < bmtd.bmt_blk_idx; k++) {
 | 
			
		||||
			if (bmtd.bbt->bb_tbl[i] != bbmt[k].block)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			set_bit(k, used);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return used;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtk_bmt_debug_v2(void *data, u64 val)
 | 
			
		||||
{
 | 
			
		||||
	struct bbmt *bbmt = bmt_tbl(bmtd.bbt);
 | 
			
		||||
	struct mtd_info *mtd = bmtd.mtd;
 | 
			
		||||
	unsigned long *used;
 | 
			
		||||
	int main_blocks = mtd->size >> bmtd.blk_shift;
 | 
			
		||||
	int n_remap = 0;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	used = mtk_bmt_get_mapping_mask();
 | 
			
		||||
	if (!used)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	switch (val) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		for (i = 1; i < main_blocks; i++) {
 | 
			
		||||
			if (bmtd.bbt->bb_tbl[i] == i)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			printk("remap [%x->%x]\n", i, bmtd.bbt->bb_tbl[i]);
 | 
			
		||||
			n_remap++;
 | 
			
		||||
		}
 | 
			
		||||
		for (i = 0; i <= bmtd.bmt_blk_idx; i++) {
 | 
			
		||||
			char c;
 | 
			
		||||
 | 
			
		||||
			switch (bbmt[i].mapped) {
 | 
			
		||||
			case NO_MAPPED:
 | 
			
		||||
				continue;
 | 
			
		||||
			case NORMAL_MAPPED:
 | 
			
		||||
				c = 'm';
 | 
			
		||||
				if (test_bit(i, used))
 | 
			
		||||
					c = 'M';
 | 
			
		||||
				break;
 | 
			
		||||
			case BMT_MAPPED:
 | 
			
		||||
				c = 'B';
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				c = 'X';
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			printk("[%x:%c] = 0x%x\n", i, c, bbmt[i].block);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case 100:
 | 
			
		||||
		for (i = 0; i <= bmtd.bmt_blk_idx; i++) {
 | 
			
		||||
			if (bbmt[i].mapped != NORMAL_MAPPED)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			if (test_bit(i, used))
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			n_remap++;
 | 
			
		||||
			bbmt[i].mapped = NO_MAPPED;
 | 
			
		||||
			printk("free block [%d:%x]\n", i, bbmt[i].block);
 | 
			
		||||
		}
 | 
			
		||||
		if (n_remap)
 | 
			
		||||
			bmtd.bmt_blk_idx = upload_bmt(bmtd.bbt, bmtd.bmt_blk_idx);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kfree(used);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mtk_bmt_init_v2(struct device_node *np)
 | 
			
		||||
{
 | 
			
		||||
	u32 bmt_pool_size, bmt_table_size;
 | 
			
		||||
	u32 bufsz, block;
 | 
			
		||||
	u16 pmt_block;
 | 
			
		||||
 | 
			
		||||
	if (of_property_read_u32(np, "mediatek,bmt-pool-size",
 | 
			
		||||
				 &bmt_pool_size) != 0)
 | 
			
		||||
		bmt_pool_size = 80;
 | 
			
		||||
 | 
			
		||||
	if (of_property_read_u8(np, "mediatek,bmt-oob-offset",
 | 
			
		||||
				 &bmtd.oob_offset) != 0)
 | 
			
		||||
		bmtd.oob_offset = 0;
 | 
			
		||||
 | 
			
		||||
	if (of_property_read_u32(np, "mediatek,bmt-table-size",
 | 
			
		||||
				 &bmt_table_size) != 0)
 | 
			
		||||
		bmt_table_size = 0x2000U;
 | 
			
		||||
 | 
			
		||||
	bmtd.table_size = bmt_table_size;
 | 
			
		||||
 | 
			
		||||
	pmt_block = bmtd.total_blks - bmt_pool_size - 2;
 | 
			
		||||
 | 
			
		||||
	bmtd.mtd->size = pmt_block << bmtd.blk_shift;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 *  ---------------------------------------
 | 
			
		||||
	 * | PMT(2blks) | BMT POOL(totalblks * 2%) |
 | 
			
		||||
	 *  ---------------------------------------
 | 
			
		||||
	 * ^            ^
 | 
			
		||||
	 * |            |
 | 
			
		||||
	 * pmt_block	pmt_block + 2blocks(pool_lba)
 | 
			
		||||
	 *
 | 
			
		||||
	 * ATTETION!!!!!!
 | 
			
		||||
	 *     The blocks ahead of the boundary block are stored in bb_tbl
 | 
			
		||||
	 *     and blocks behind are stored in bmt_tbl
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	bmtd.pool_lba = (u16)(pmt_block + 2);
 | 
			
		||||
	bmtd.bb_max = bmtd.total_blks * BBPOOL_RATIO / 100;
 | 
			
		||||
 | 
			
		||||
	bufsz = round_up(sizeof(struct bbbt) +
 | 
			
		||||
			 bmt_table_size * sizeof(struct bbmt), bmtd.pg_size);
 | 
			
		||||
	bmtd.bmt_pgs = bufsz >> bmtd.pg_shift;
 | 
			
		||||
 | 
			
		||||
	bmtd.bbt_buf = kzalloc(bufsz, GFP_KERNEL);
 | 
			
		||||
	if (!bmtd.bbt_buf)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	memset(bmtd.bbt_buf, 0xff, bufsz);
 | 
			
		||||
 | 
			
		||||
	/* Scanning start from the first page of the last block
 | 
			
		||||
	 * of whole flash
 | 
			
		||||
	 */
 | 
			
		||||
	bmtd.bbt = NULL;
 | 
			
		||||
	for (u16 block = bmtd.total_blks - 1; !bmtd.bbt && block >= bmtd.pool_lba; block--) {
 | 
			
		||||
		u8 fdm[4];
 | 
			
		||||
 | 
			
		||||
		if (bbt_nand_read(blk_pg(block), bmtd.bbt_buf, bufsz, fdm, sizeof(fdm))) {
 | 
			
		||||
			/* Read failed, try the previous block */
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!is_valid_bmt(bmtd.bbt_buf, fdm)) {
 | 
			
		||||
			/* No valid BMT found, try the previous block */
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bmtd.bmt_blk_idx = get_bmt_index(bmt_tbl((struct bbbt *)bmtd.bbt_buf));
 | 
			
		||||
		if (bmtd.bmt_blk_idx == 0) {
 | 
			
		||||
			pr_info("[BBT] FATAL ERR: bmt block index is wrong!\n");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pr_info("[BBT] BMT.v2 is found at 0x%x\n", block);
 | 
			
		||||
		bmtd.bbt = (struct bbbt *)bmtd.bbt_buf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!bmtd.bbt) {
 | 
			
		||||
		/* BMT not found */
 | 
			
		||||
		if (bmtd.total_blks > BB_TABLE_MAX + BMT_TABLE_MAX) {
 | 
			
		||||
			pr_info("nand: FATAL: Too many blocks, can not support!\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bmtd.bbt = (struct bbbt *)bmtd.bbt_buf;
 | 
			
		||||
		memset(bmt_tbl(bmtd.bbt), BMT_TBL_DEF_VAL,
 | 
			
		||||
		       bmtd.table_size * sizeof(struct bbmt));
 | 
			
		||||
 | 
			
		||||
		if (scan_bad_blocks(bmtd.bbt))
 | 
			
		||||
			return -1;
 | 
			
		||||
 | 
			
		||||
		/* BMT always in the last valid block in pool */
 | 
			
		||||
		bmtd.bmt_blk_idx = upload_bmt(bmtd.bbt, bmtd.bmt_blk_idx);
 | 
			
		||||
		block = bmt_tbl(bmtd.bbt)[bmtd.bmt_blk_idx].block;
 | 
			
		||||
		pr_notice("[BBT] BMT.v2 is written into PBA:0x%x\n", block);
 | 
			
		||||
 | 
			
		||||
		if (bmtd.bmt_blk_idx == 0)
 | 
			
		||||
			pr_info("nand: Warning: no available block in BMT pool!\n");
 | 
			
		||||
		else if (bmtd.bmt_blk_idx == (u16)-1)
 | 
			
		||||
			return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const struct mtk_bmt_ops mtk_bmt_v2_ops = {
 | 
			
		||||
	.sig = "bmt",
 | 
			
		||||
	.sig_len = 3,
 | 
			
		||||
	.init = mtk_bmt_init_v2,
 | 
			
		||||
	.remap_block = remap_block_v2,
 | 
			
		||||
	.unmap_block = unmap_block_v2,
 | 
			
		||||
	.get_mapping_block = get_mapping_block_index_v2,
 | 
			
		||||
	.debug = mtk_bmt_debug_v2,
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,365 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-only
 | 
			
		||||
/*
 | 
			
		||||
 * Parser for MikroTik RouterBoot partitions.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2020 Thibaut VARÈNE <hacks+kernel@slashdirt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 * by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 * This parser builds from the "fixed-partitions" one (see ofpart.c), but it can
 | 
			
		||||
 * handle dynamic partitions as found on routerboot devices.
 | 
			
		||||
 *
 | 
			
		||||
 * DTS nodes are defined as follows:
 | 
			
		||||
 * For fixed partitions:
 | 
			
		||||
 *	node-name@unit-address {
 | 
			
		||||
 *		reg = <prop-encoded-array>;
 | 
			
		||||
 *		label = <string>;
 | 
			
		||||
 *		read-only;
 | 
			
		||||
 *		lock;
 | 
			
		||||
 *	};
 | 
			
		||||
 *
 | 
			
		||||
 * reg property is mandatory; other properties are optional.
 | 
			
		||||
 * reg format is <address length>. length can be 0 if the next partition is
 | 
			
		||||
 * another fixed partition or a "well-known" partition as defined below: in that
 | 
			
		||||
 * case the partition will extend up to the next one.
 | 
			
		||||
 *
 | 
			
		||||
 * For dynamic partitions:
 | 
			
		||||
 *	node-name {
 | 
			
		||||
 *		size = <prop-encoded-array>;
 | 
			
		||||
 *		label = <string>;
 | 
			
		||||
 *		read-only;
 | 
			
		||||
 *		lock;
 | 
			
		||||
 *	};
 | 
			
		||||
 *
 | 
			
		||||
 * size property is normally mandatory. It can only be omitted (or set to 0) if:
 | 
			
		||||
 *	- the partition is a "well-known" one (as defined below), in which case
 | 
			
		||||
 *	  the partition size will be automatically adjusted; or
 | 
			
		||||
 *	- the next partition is a fixed one or a "well-known" one, in which case
 | 
			
		||||
 *	  the current partition will extend up to the next one.
 | 
			
		||||
 * Other properties are optional.
 | 
			
		||||
 * size format is <length>.
 | 
			
		||||
 * By default dynamic partitions are appended after the preceding one, except
 | 
			
		||||
 * for "well-known" ones which are automatically located on flash.
 | 
			
		||||
 *
 | 
			
		||||
 * Well-known partitions (matched via label or node-name):
 | 
			
		||||
 * - "hard_config"
 | 
			
		||||
 * - "soft_config"
 | 
			
		||||
 * - "dtb_config"
 | 
			
		||||
 *
 | 
			
		||||
 * Note: this parser will happily register 0-sized partitions if misused.
 | 
			
		||||
 *
 | 
			
		||||
 * This parser requires the DTS to list partitions in ascending order as
 | 
			
		||||
 * expected on the MTD device.
 | 
			
		||||
 *
 | 
			
		||||
 * Since only the "hard_config" and "soft_config" partitions are used in OpenWRT,
 | 
			
		||||
 * a minimal working DTS could define only these two partitions dynamically (in
 | 
			
		||||
 * the right order, usually hard_config then soft_config).
 | 
			
		||||
 *
 | 
			
		||||
 * Note: some mips RB devices encode the hard_config offset and length in two
 | 
			
		||||
 * consecutive u32 located at offset 0x14 (for ramips) or 0x24 (for ath79) on
 | 
			
		||||
 * the SPI NOR flash. Unfortunately this seems inconsistent across machines and
 | 
			
		||||
 * does not apply to e.g. ipq-based ones, so we ignore that information.
 | 
			
		||||
 *
 | 
			
		||||
 * Note: To find well-known partitions, this parser will go through the entire
 | 
			
		||||
 * top mtd partition parsed, _before_ the DTS nodes are processed. This works
 | 
			
		||||
 * well in the current state of affairs, and is a simpler implementation than
 | 
			
		||||
 * searching for known partitions in the "holes" left between fixed-partition,
 | 
			
		||||
 * _after_ processing DTS nodes.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/mtd/partitions.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
#include <linux/of_fdt.h>
 | 
			
		||||
#include <linux/libfdt_env.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
 | 
			
		||||
#define RB_MAGIC_HARD	(('H') | ('a' << 8) | ('r' << 16) | ('d' << 24))
 | 
			
		||||
#define RB_MAGIC_SOFT	(('S') | ('o' << 8) | ('f' << 16) | ('t' << 24))
 | 
			
		||||
#define RB_BLOCK_SIZE	0x1000
 | 
			
		||||
 | 
			
		||||
struct routerboot_dynpart {
 | 
			
		||||
	const char * const name;
 | 
			
		||||
	const u32 magic;
 | 
			
		||||
	int (* const size_fixup)(struct mtd_info *, struct routerboot_dynpart *);
 | 
			
		||||
	size_t offset;
 | 
			
		||||
	size_t size;
 | 
			
		||||
	bool found;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int routerboot_dtbsfixup(struct mtd_info *, struct routerboot_dynpart *);
 | 
			
		||||
 | 
			
		||||
static struct routerboot_dynpart rb_dynparts[] = {
 | 
			
		||||
	{
 | 
			
		||||
		.name = "hard_config",
 | 
			
		||||
		.magic = RB_MAGIC_HARD,	// stored in CPU-endianness on flash
 | 
			
		||||
		.size_fixup = NULL,
 | 
			
		||||
		.offset = 0x0,
 | 
			
		||||
		.size = RB_BLOCK_SIZE,
 | 
			
		||||
		.found = false,
 | 
			
		||||
	}, {
 | 
			
		||||
		.name = "soft_config",
 | 
			
		||||
		.magic = RB_MAGIC_SOFT,	// stored in CPU-endianness on flash
 | 
			
		||||
		.size_fixup = NULL,
 | 
			
		||||
		.offset = 0x0,
 | 
			
		||||
		.size = RB_BLOCK_SIZE,
 | 
			
		||||
		.found = false,
 | 
			
		||||
	}, {
 | 
			
		||||
		.name = "dtb_config",
 | 
			
		||||
		.magic = fdt32_to_cpu(OF_DT_HEADER),	// stored BE on flash
 | 
			
		||||
		.size_fixup = routerboot_dtbsfixup,
 | 
			
		||||
		.offset = 0x0,
 | 
			
		||||
		.size = 0x0,
 | 
			
		||||
		.found = false,
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int routerboot_dtbsfixup(struct mtd_info *master, struct routerboot_dynpart *rbdpart)
 | 
			
		||||
{
 | 
			
		||||
	int err;
 | 
			
		||||
	size_t bytes_read, psize;
 | 
			
		||||
	struct {
 | 
			
		||||
		fdt32_t magic;
 | 
			
		||||
		fdt32_t totalsize;
 | 
			
		||||
		fdt32_t off_dt_struct;
 | 
			
		||||
		fdt32_t off_dt_strings;
 | 
			
		||||
		fdt32_t off_mem_rsvmap;
 | 
			
		||||
		fdt32_t version;
 | 
			
		||||
		fdt32_t last_comp_version;
 | 
			
		||||
		fdt32_t boot_cpuid_phys;
 | 
			
		||||
		fdt32_t size_dt_strings;
 | 
			
		||||
		fdt32_t size_dt_struct;
 | 
			
		||||
	} fdt_header;
 | 
			
		||||
 | 
			
		||||
	err = mtd_read(master, rbdpart->offset, sizeof(fdt_header),
 | 
			
		||||
		       &bytes_read, (u8 *)&fdt_header);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	if (bytes_read != sizeof(fdt_header))
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	psize = fdt32_to_cpu(fdt_header.totalsize);
 | 
			
		||||
	if (!psize)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	rbdpart->size = psize;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void routerboot_find_dynparts(struct mtd_info *master)
 | 
			
		||||
{
 | 
			
		||||
	size_t bytes_read, offset;
 | 
			
		||||
	bool allfound;
 | 
			
		||||
	int err, i;
 | 
			
		||||
	u32 buf;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Dynamic RouterBoot partitions offsets are aligned to RB_BLOCK_SIZE:
 | 
			
		||||
	 * read the whole partition at RB_BLOCK_SIZE intervals to find sigs.
 | 
			
		||||
	 * Skip partition content when possible.
 | 
			
		||||
	 */
 | 
			
		||||
	offset = 0;
 | 
			
		||||
	while (offset < master->size) {
 | 
			
		||||
		err = mtd_read(master, offset, sizeof(buf), &bytes_read, (u8 *)&buf);
 | 
			
		||||
		if (err) {
 | 
			
		||||
			pr_err("%s: mtd_read error while parsing (offset: 0x%zX): %d\n",
 | 
			
		||||
			       master->name, offset, err);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		allfound = true;
 | 
			
		||||
 | 
			
		||||
		for (i = 0; i < ARRAY_SIZE(rb_dynparts); i++) {
 | 
			
		||||
			if (rb_dynparts[i].found)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			allfound = false;
 | 
			
		||||
 | 
			
		||||
			if (rb_dynparts[i].magic == buf) {
 | 
			
		||||
				rb_dynparts[i].offset = offset;
 | 
			
		||||
 | 
			
		||||
				if (rb_dynparts[i].size_fixup) {
 | 
			
		||||
					err = rb_dynparts[i].size_fixup(master, &rb_dynparts[i]);
 | 
			
		||||
					if (err) {
 | 
			
		||||
						pr_err("%s: size fixup error while parsing \"%s\": %d\n",
 | 
			
		||||
						       master->name, rb_dynparts[i].name, err);
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				rb_dynparts[i].found = true;
 | 
			
		||||
 | 
			
		||||
				/*
 | 
			
		||||
				 * move offset to skip the whole partition on
 | 
			
		||||
				 * next iteration if size > RB_BLOCK_SIZE.
 | 
			
		||||
				 */
 | 
			
		||||
				if (rb_dynparts[i].size > RB_BLOCK_SIZE)
 | 
			
		||||
					offset += ALIGN_DOWN((rb_dynparts[i].size - RB_BLOCK_SIZE), RB_BLOCK_SIZE);
 | 
			
		||||
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		offset += RB_BLOCK_SIZE;
 | 
			
		||||
 | 
			
		||||
		if (allfound)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int routerboot_partitions_parse(struct mtd_info *master,
 | 
			
		||||
				       const struct mtd_partition **pparts,
 | 
			
		||||
				       struct mtd_part_parser_data *data)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *rbpart_node, *pp;
 | 
			
		||||
	struct mtd_partition *parts;
 | 
			
		||||
	const char *partname;
 | 
			
		||||
	size_t master_ofs;
 | 
			
		||||
	int np;
 | 
			
		||||
 | 
			
		||||
	/* Pull of_node from the master device node */
 | 
			
		||||
	rbpart_node = mtd_get_of_node(master);
 | 
			
		||||
	if (!rbpart_node)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* First count the subnodes */
 | 
			
		||||
	np = 0;
 | 
			
		||||
	for_each_child_of_node(rbpart_node,  pp)
 | 
			
		||||
		np++;
 | 
			
		||||
 | 
			
		||||
	if (!np)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	parts = kcalloc(np, sizeof(*parts), GFP_KERNEL);
 | 
			
		||||
	if (!parts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	/* Preemptively look for known parts in flash */
 | 
			
		||||
	routerboot_find_dynparts(master);
 | 
			
		||||
 | 
			
		||||
	np = 0;
 | 
			
		||||
	master_ofs = 0;
 | 
			
		||||
	for_each_child_of_node(rbpart_node, pp) {
 | 
			
		||||
		const __be32 *reg, *sz;
 | 
			
		||||
		size_t offset, size;
 | 
			
		||||
		int i, len, a_cells, s_cells;
 | 
			
		||||
 | 
			
		||||
		partname = of_get_property(pp, "label", &len);
 | 
			
		||||
		/* Allow deprecated use of "name" instead of "label" */
 | 
			
		||||
		if (!partname)
 | 
			
		||||
			partname = of_get_property(pp, "name", &len);
 | 
			
		||||
		/* Fallback to node name per spec if all else fails: partname is always set */
 | 
			
		||||
		if (!partname)
 | 
			
		||||
			partname = pp->name;
 | 
			
		||||
		parts[np].name = partname;
 | 
			
		||||
 | 
			
		||||
		reg = of_get_property(pp, "reg", &len);
 | 
			
		||||
		if (reg) {
 | 
			
		||||
			/* Fixed partition */
 | 
			
		||||
			a_cells = of_n_addr_cells(pp);
 | 
			
		||||
			s_cells = of_n_size_cells(pp);
 | 
			
		||||
 | 
			
		||||
			if ((len / 4) != (a_cells + s_cells)) {
 | 
			
		||||
				pr_debug("%s: routerboot partition %pOF (%pOF) error parsing reg property.\n",
 | 
			
		||||
					 master->name, pp, rbpart_node);
 | 
			
		||||
				goto rbpart_fail;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			offset = of_read_number(reg, a_cells);
 | 
			
		||||
			size = of_read_number(reg + a_cells, s_cells);
 | 
			
		||||
		} else {
 | 
			
		||||
			/* Dynamic partition */
 | 
			
		||||
			/* Default: part starts at current offset, 0 size */
 | 
			
		||||
			offset = master_ofs;
 | 
			
		||||
			size = 0;
 | 
			
		||||
 | 
			
		||||
			/* Check if well-known partition */
 | 
			
		||||
			for (i = 0; i < ARRAY_SIZE(rb_dynparts); i++) {
 | 
			
		||||
				if (!strcmp(partname, rb_dynparts[i].name) && rb_dynparts[i].found) {
 | 
			
		||||
					offset = rb_dynparts[i].offset;
 | 
			
		||||
					size = rb_dynparts[i].size;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* Standalone 'size' property? Override size */
 | 
			
		||||
			sz = of_get_property(pp, "size", &len);
 | 
			
		||||
			if (sz) {
 | 
			
		||||
				s_cells = of_n_size_cells(pp);
 | 
			
		||||
				if ((len / 4) != s_cells) {
 | 
			
		||||
					pr_debug("%s: routerboot partition %pOF (%pOF) error parsing size property.\n",
 | 
			
		||||
						 master->name, pp, rbpart_node);
 | 
			
		||||
					goto rbpart_fail;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				size = of_read_number(sz, s_cells);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (np > 0) {
 | 
			
		||||
			/* Minor sanity check for overlaps */
 | 
			
		||||
			if (offset < (parts[np-1].offset + parts[np-1].size)) {
 | 
			
		||||
				pr_err("%s: routerboot partition %pOF (%pOF) \"%s\" overlaps with previous partition \"%s\".\n",
 | 
			
		||||
				       master->name, pp, rbpart_node,
 | 
			
		||||
				       partname, parts[np-1].name);
 | 
			
		||||
				goto rbpart_fail;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* Fixup end of previous partition if necessary */
 | 
			
		||||
			if (!parts[np-1].size)
 | 
			
		||||
				parts[np-1].size = (offset - parts[np-1].offset);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ((offset + size) > master->size) {
 | 
			
		||||
			pr_err("%s: routerboot partition %pOF (%pOF) \"%s\" extends past end of segment.\n",
 | 
			
		||||
			       master->name, pp, rbpart_node, partname);
 | 
			
		||||
			goto rbpart_fail;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		parts[np].offset = offset;
 | 
			
		||||
		parts[np].size = size;
 | 
			
		||||
		parts[np].of_node = pp;
 | 
			
		||||
 | 
			
		||||
		if (of_get_property(pp, "read-only", &len))
 | 
			
		||||
			parts[np].mask_flags |= MTD_WRITEABLE;
 | 
			
		||||
 | 
			
		||||
		if (of_get_property(pp, "lock", &len))
 | 
			
		||||
			parts[np].mask_flags |= MTD_POWERUP_LOCK;
 | 
			
		||||
 | 
			
		||||
		/* Keep master offset aligned to RB_BLOCK_SIZE */
 | 
			
		||||
		master_ofs = ALIGN(offset + size, RB_BLOCK_SIZE);
 | 
			
		||||
		np++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*pparts = parts;
 | 
			
		||||
	return np;
 | 
			
		||||
 | 
			
		||||
rbpart_fail:
 | 
			
		||||
	pr_err("%s: error parsing routerboot partition %pOF (%pOF)\n",
 | 
			
		||||
	       master->name, pp, rbpart_node);
 | 
			
		||||
	of_node_put(pp);
 | 
			
		||||
	kfree(parts);
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id parse_routerbootpart_match_table[] = {
 | 
			
		||||
	{ .compatible = "mikrotik,routerboot-partitions" },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, parse_routerbootpart_match_table);
 | 
			
		||||
 | 
			
		||||
static struct mtd_part_parser routerbootpart_parser = {
 | 
			
		||||
	.parse_fn = routerboot_partitions_parse,
 | 
			
		||||
	.name = "routerbootpart",
 | 
			
		||||
	.of_match_table = parse_routerbootpart_match_table,
 | 
			
		||||
};
 | 
			
		||||
module_mtd_part_parser(routerbootpart_parser);
 | 
			
		||||
 | 
			
		||||
MODULE_LICENSE("GPL v2");
 | 
			
		||||
MODULE_DESCRIPTION("MTD partitioning for RouterBoot");
 | 
			
		||||
MODULE_AUTHOR("Thibaut VARENE");
 | 
			
		||||
							
								
								
									
										1249
									
								
								6.12/target/linux/generic/files/drivers/net/phy/adm6996.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1249
									
								
								6.12/target/linux/generic/files/drivers/net/phy/adm6996.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										186
									
								
								6.12/target/linux/generic/files/drivers/net/phy/adm6996.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								6.12/target/linux/generic/files/drivers/net/phy/adm6996.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,186 @@
 | 
			
		|||
/*
 | 
			
		||||
 * ADM6996 switch driver
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
 * Copyright (c) 2010,2011 Peter Lebbing <peter@digitalbrains.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute  it and/or modify it
 | 
			
		||||
 * under  the terms of the GNU General Public License v2 as published by the
 | 
			
		||||
 * Free Software Foundation
 | 
			
		||||
 */
 | 
			
		||||
#ifndef __ADM6996_H
 | 
			
		||||
#define __ADM6996_H
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ADM_PHY_PORTS: Number of ports with a PHY.
 | 
			
		||||
 * We only control ports 0 to 3, because if 4 is connected, it is most likely
 | 
			
		||||
 * not connected to the switch but to a separate MII and MAC for the WAN port.
 | 
			
		||||
 */
 | 
			
		||||
#define ADM_PHY_PORTS	4
 | 
			
		||||
#define ADM_NUM_PORTS	6
 | 
			
		||||
#define ADM_CPU_PORT	5
 | 
			
		||||
 | 
			
		||||
#define ADM_NUM_VLANS 16
 | 
			
		||||
#define ADM_VLAN_MAX_ID 4094
 | 
			
		||||
 | 
			
		||||
enum admreg {
 | 
			
		||||
	ADM_EEPROM_BASE		= 0x0,
 | 
			
		||||
		ADM_P0_CFG		= ADM_EEPROM_BASE + 1,
 | 
			
		||||
		ADM_P1_CFG		= ADM_EEPROM_BASE + 3,
 | 
			
		||||
		ADM_P2_CFG		= ADM_EEPROM_BASE + 5,
 | 
			
		||||
		ADM_P3_CFG		= ADM_EEPROM_BASE + 7,
 | 
			
		||||
		ADM_P4_CFG		= ADM_EEPROM_BASE + 8,
 | 
			
		||||
		ADM_P5_CFG		= ADM_EEPROM_BASE + 9,
 | 
			
		||||
		ADM_SYSC0		= ADM_EEPROM_BASE + 0xa,
 | 
			
		||||
		ADM_VLAN_PRIOMAP	= ADM_EEPROM_BASE + 0xe,
 | 
			
		||||
		ADM_SYSC3		= ADM_EEPROM_BASE + 0x11,
 | 
			
		||||
		/* Input Force No Tag Enable */
 | 
			
		||||
		ADM_IFNTE		= ADM_EEPROM_BASE + 0x20,
 | 
			
		||||
		ADM_VID_CHECK		= ADM_EEPROM_BASE + 0x26,
 | 
			
		||||
		ADM_P0_PVID		= ADM_EEPROM_BASE + 0x28,
 | 
			
		||||
		ADM_P1_PVID		= ADM_EEPROM_BASE + 0x29,
 | 
			
		||||
		/* Output Tag Bypass Enable and P2 PVID */
 | 
			
		||||
		ADM_OTBE_P2_PVID	= ADM_EEPROM_BASE + 0x2a,
 | 
			
		||||
		ADM_P3_P4_PVID		= ADM_EEPROM_BASE + 0x2b,
 | 
			
		||||
		ADM_P5_PVID		= ADM_EEPROM_BASE + 0x2c,
 | 
			
		||||
	ADM_EEPROM_EXT_BASE	= 0x40,
 | 
			
		||||
#define ADM_VLAN_FILT_L(n) (ADM_EEPROM_EXT_BASE + 2 * (n))
 | 
			
		||||
#define ADM_VLAN_FILT_H(n) (ADM_EEPROM_EXT_BASE + 1 + 2 * (n))
 | 
			
		||||
#define ADM_VLAN_MAP(n) (ADM_EEPROM_BASE + 0x13 + n)
 | 
			
		||||
	ADM_COUNTER_BASE	= 0xa0,
 | 
			
		||||
		ADM_SIG0		= ADM_COUNTER_BASE + 0,
 | 
			
		||||
		ADM_SIG1		= ADM_COUNTER_BASE + 1,
 | 
			
		||||
		ADM_PS0		= ADM_COUNTER_BASE + 2,
 | 
			
		||||
		ADM_PS1		= ADM_COUNTER_BASE + 3,
 | 
			
		||||
		ADM_PS2		= ADM_COUNTER_BASE + 4,
 | 
			
		||||
		ADM_CL0		= ADM_COUNTER_BASE + 8, /* RxPacket */
 | 
			
		||||
		ADM_CL6		= ADM_COUNTER_BASE + 0x1a, /* RxByte */
 | 
			
		||||
		ADM_CL12		= ADM_COUNTER_BASE + 0x2c, /* TxPacket */
 | 
			
		||||
		ADM_CL18		= ADM_COUNTER_BASE + 0x3e, /* TxByte */
 | 
			
		||||
		ADM_CL24		= ADM_COUNTER_BASE + 0x50, /* Coll */
 | 
			
		||||
		ADM_CL30		= ADM_COUNTER_BASE + 0x62, /* Err */
 | 
			
		||||
#define ADM_OFFSET_PORT(n) ((n * 4) - (n / 4) * 2 - (n / 5) * 2)
 | 
			
		||||
	ADM_PHY_BASE		= 0x200,
 | 
			
		||||
#define ADM_PHY_PORT(n) (ADM_PHY_BASE + (0x20 * n))
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Chip identification patterns */
 | 
			
		||||
#define	ADM_SIG0_MASK	0xffff
 | 
			
		||||
#define ADM_SIG0_VAL	0x1023
 | 
			
		||||
#define ADM_SIG1_MASK	0xffff
 | 
			
		||||
#define ADM_SIG1_VAL	0x0007
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	ADM_PHYCFG_COLTST     = (1 << 7),	/* Enable collision test */
 | 
			
		||||
	ADM_PHYCFG_DPLX       = (1 << 8),	/* Enable full duplex */
 | 
			
		||||
	ADM_PHYCFG_ANEN_RST   = (1 << 9),	/* Restart auto negotiation (self clear) */
 | 
			
		||||
	ADM_PHYCFG_ISO        = (1 << 10),	/* Isolate PHY */
 | 
			
		||||
	ADM_PHYCFG_PDN        = (1 << 11),	/* Power down PHY */
 | 
			
		||||
	ADM_PHYCFG_ANEN       = (1 << 12),	/* Enable auto negotiation */
 | 
			
		||||
	ADM_PHYCFG_SPEED_100  = (1 << 13),	/* Enable 100 Mbit/s */
 | 
			
		||||
	ADM_PHYCFG_LPBK       = (1 << 14),	/* Enable loopback operation */
 | 
			
		||||
	ADM_PHYCFG_RST        = (1 << 15),	/* Reset the port (self clear) */
 | 
			
		||||
	ADM_PHYCFG_INIT = (
 | 
			
		||||
		ADM_PHYCFG_RST |
 | 
			
		||||
		ADM_PHYCFG_SPEED_100 |
 | 
			
		||||
		ADM_PHYCFG_ANEN |
 | 
			
		||||
		ADM_PHYCFG_ANEN_RST
 | 
			
		||||
	)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	ADM_PORTCFG_FC        = (1 << 0),	/* Enable 802.x flow control */
 | 
			
		||||
	ADM_PORTCFG_AN        = (1 << 1),	/* Enable auto-negotiation */
 | 
			
		||||
	ADM_PORTCFG_SPEED_100 = (1 << 2),	/* Enable 100 Mbit/s */
 | 
			
		||||
	ADM_PORTCFG_DPLX      = (1 << 3),	/* Enable full duplex */
 | 
			
		||||
	ADM_PORTCFG_OT        = (1 << 4),	/* Output tagged packets */
 | 
			
		||||
	ADM_PORTCFG_PD        = (1 << 5),	/* Port disable */
 | 
			
		||||
	ADM_PORTCFG_TV_PRIO   = (1 << 6),	/* 0 = VLAN based priority
 | 
			
		||||
	                                 	 * 1 = TOS based priority */
 | 
			
		||||
	ADM_PORTCFG_PPE       = (1 << 7),	/* Port based priority enable */
 | 
			
		||||
	ADM_PORTCFG_PP_S      = (1 << 8),	/* Port based priority, 2 bits */
 | 
			
		||||
	ADM_PORTCFG_PVID_BASE = (1 << 10),	/* Primary VLAN id, 4 bits */
 | 
			
		||||
	ADM_PORTCFG_FSE	      = (1 << 14),	/* Fx select enable */
 | 
			
		||||
	ADM_PORTCFG_CAM       = (1 << 15),	/* Crossover Auto MDIX */
 | 
			
		||||
 | 
			
		||||
	ADM_PORTCFG_INIT = (
 | 
			
		||||
		ADM_PORTCFG_FC |
 | 
			
		||||
		ADM_PORTCFG_AN |
 | 
			
		||||
		ADM_PORTCFG_SPEED_100 |
 | 
			
		||||
		ADM_PORTCFG_DPLX |
 | 
			
		||||
		ADM_PORTCFG_CAM
 | 
			
		||||
	),
 | 
			
		||||
	ADM_PORTCFG_CPU = (
 | 
			
		||||
		ADM_PORTCFG_FC |
 | 
			
		||||
		ADM_PORTCFG_SPEED_100 |
 | 
			
		||||
		ADM_PORTCFG_OT |
 | 
			
		||||
		ADM_PORTCFG_DPLX
 | 
			
		||||
	),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define ADM_PORTCFG_PPID(n) ((n & 0x3) << 8)
 | 
			
		||||
#define ADM_PORTCFG_PVID(n) ((n & 0xf) << 10)
 | 
			
		||||
#define ADM_PORTCFG_PVID_MASK (0xf << 10)
 | 
			
		||||
 | 
			
		||||
#define ADM_IFNTE_MASK (0x3f << 9)
 | 
			
		||||
#define ADM_VID_CHECK_MASK (0x3f << 6)
 | 
			
		||||
 | 
			
		||||
#define ADM_P0_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
 | 
			
		||||
#define ADM_P1_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
 | 
			
		||||
#define ADM_P2_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
 | 
			
		||||
#define ADM_P3_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
 | 
			
		||||
#define ADM_P4_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 8)
 | 
			
		||||
#define ADM_P5_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
 | 
			
		||||
#define ADM_P2_PVID_MASK 0xff
 | 
			
		||||
 | 
			
		||||
#define ADM_OTBE(n) (((n) & 0x3f) << 8)
 | 
			
		||||
#define ADM_OTBE_MASK (0x3f << 8)
 | 
			
		||||
 | 
			
		||||
/* ADM_SYSC0 */
 | 
			
		||||
enum {
 | 
			
		||||
	ADM_NTTE	= (1 << 2),	/* New Tag Transmit Enable */
 | 
			
		||||
	ADM_RVID1	= (1 << 8)	/* Replace VLAN ID 1 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Tag Based VLAN in ADM_SYSC3 */
 | 
			
		||||
#define ADM_MAC_CLONE	BIT(4)
 | 
			
		||||
#define ADM_TBV		BIT(5)
 | 
			
		||||
 | 
			
		||||
static const u8 adm_portcfg[] = {
 | 
			
		||||
	[0] = ADM_P0_CFG,
 | 
			
		||||
	[1] = ADM_P1_CFG,
 | 
			
		||||
	[2] = ADM_P2_CFG,
 | 
			
		||||
	[3] = ADM_P3_CFG,
 | 
			
		||||
	[4] = ADM_P4_CFG,
 | 
			
		||||
	[5] = ADM_P5_CFG,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Fields in ADM_VLAN_FILT_L(x) */
 | 
			
		||||
#define ADM_VLAN_FILT_FID(n) (((n) & 0xf) << 12)
 | 
			
		||||
#define ADM_VLAN_FILT_TAGGED(n) (((n) & 0x3f) << 6)
 | 
			
		||||
#define ADM_VLAN_FILT_MEMBER(n) (((n) & 0x3f) << 0)
 | 
			
		||||
#define ADM_VLAN_FILT_MEMBER_MASK 0x3f
 | 
			
		||||
/* Fields in ADM_VLAN_FILT_H(x) */
 | 
			
		||||
#define ADM_VLAN_FILT_VALID (1 << 15)
 | 
			
		||||
#define ADM_VLAN_FILT_VID(n) (((n) & 0xfff) << 0)
 | 
			
		||||
 | 
			
		||||
/* Convert ports to a form for ADM6996L VLAN map */
 | 
			
		||||
#define ADM_VLAN_FILT(ports) ((ports & 0x01) | ((ports & 0x02) << 1) | \
 | 
			
		||||
			((ports & 0x04) << 2) | ((ports & 0x08) << 3) | \
 | 
			
		||||
			((ports & 0x10) << 3) | ((ports & 0x20) << 3))
 | 
			
		||||
 | 
			
		||||
/* Port status register */
 | 
			
		||||
enum {
 | 
			
		||||
	ADM_PS_LS = (1 << 0),	/* Link status */
 | 
			
		||||
	ADM_PS_SS = (1 << 1),	/* Speed status */
 | 
			
		||||
	ADM_PS_DS = (1 << 2),	/* Duplex status */
 | 
			
		||||
	ADM_PS_FCS = (1 << 3)	/* Flow control status */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Split the register address in phy id and register
 | 
			
		||||
 * it will get combined again by the mdio bus op
 | 
			
		||||
 */
 | 
			
		||||
#define PHYADDR(_reg)	((_reg >> 5) & 0xff), (_reg & 0x1f)
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										2909
									
								
								6.12/target/linux/generic/files/drivers/net/phy/ar8216.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2909
									
								
								6.12/target/linux/generic/files/drivers/net/phy/ar8216.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										725
									
								
								6.12/target/linux/generic/files/drivers/net/phy/ar8216.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										725
									
								
								6.12/target/linux/generic/files/drivers/net/phy/ar8216.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,725 @@
 | 
			
		|||
/*
 | 
			
		||||
 * ar8216.h: AR8216 switch driver
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __AR8216_H
 | 
			
		||||
#define __AR8216_H
 | 
			
		||||
 | 
			
		||||
#define BITS(_s, _n)	(((1UL << (_n)) - 1) << _s)
 | 
			
		||||
 | 
			
		||||
#define AR8XXX_CAP_GIGE			BIT(0)
 | 
			
		||||
#define AR8XXX_CAP_MIB_COUNTERS		BIT(1)
 | 
			
		||||
 | 
			
		||||
#define AR8XXX_NUM_PHYS 	5
 | 
			
		||||
#define AR8216_PORT_CPU	0
 | 
			
		||||
#define AR8216_NUM_PORTS	6
 | 
			
		||||
#define AR8216_NUM_VLANS	16
 | 
			
		||||
#define AR7240SW_NUM_PORTS	5
 | 
			
		||||
#define AR8316_NUM_VLANS	4096
 | 
			
		||||
 | 
			
		||||
/* size of the vlan table */
 | 
			
		||||
#define AR8X16_MAX_VLANS	128
 | 
			
		||||
#define AR83X7_MAX_VLANS	4096
 | 
			
		||||
#define AR8XXX_MAX_VLANS	AR83X7_MAX_VLANS
 | 
			
		||||
 | 
			
		||||
#define AR8X16_PROBE_RETRIES	10
 | 
			
		||||
#define AR8X16_MAX_PORTS	8
 | 
			
		||||
 | 
			
		||||
#define AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS	7
 | 
			
		||||
#define AR8XXX_DEFAULT_ARL_AGE_TIME		300
 | 
			
		||||
 | 
			
		||||
/* Atheros specific MII registers */
 | 
			
		||||
#define MII_ATH_MMD_ADDR		0x0d
 | 
			
		||||
#define MII_ATH_MMD_DATA		0x0e
 | 
			
		||||
#define MII_ATH_DBG_ADDR		0x1d
 | 
			
		||||
#define MII_ATH_DBG_DATA		0x1e
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_CTRL			0x0000
 | 
			
		||||
#define   AR8216_CTRL_REVISION		BITS(0, 8)
 | 
			
		||||
#define   AR8216_CTRL_REVISION_S	0
 | 
			
		||||
#define   AR8216_CTRL_VERSION		BITS(8, 8)
 | 
			
		||||
#define   AR8216_CTRL_VERSION_S		8
 | 
			
		||||
#define   AR8216_CTRL_RESET		BIT(31)
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_FLOOD_MASK		0x002C
 | 
			
		||||
#define   AR8216_FM_UNI_DEST_PORTS	BITS(0, 6)
 | 
			
		||||
#define   AR8216_FM_MULTI_DEST_PORTS	BITS(16, 6)
 | 
			
		||||
#define   AR8216_FM_CPU_BROADCAST_EN	BIT(26)
 | 
			
		||||
#define   AR8229_FLOOD_MASK_UC_DP(_p)	BIT(_p)
 | 
			
		||||
#define   AR8229_FLOOD_MASK_MC_DP(_p)	BIT(16 + (_p))
 | 
			
		||||
#define   AR8229_FLOOD_MASK_BC_DP(_p)	BIT(25 + (_p))
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_GLOBAL_CTRL		0x0030
 | 
			
		||||
#define   AR8216_GCTRL_MTU		BITS(0, 11)
 | 
			
		||||
#define   AR8236_GCTRL_MTU		BITS(0, 14)
 | 
			
		||||
#define   AR8316_GCTRL_MTU		BITS(0, 14)
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_VTU			0x0040
 | 
			
		||||
#define   AR8216_VTU_OP			BITS(0, 3)
 | 
			
		||||
#define   AR8216_VTU_OP_NOOP		0x0
 | 
			
		||||
#define   AR8216_VTU_OP_FLUSH		0x1
 | 
			
		||||
#define   AR8216_VTU_OP_LOAD		0x2
 | 
			
		||||
#define   AR8216_VTU_OP_PURGE		0x3
 | 
			
		||||
#define   AR8216_VTU_OP_REMOVE_PORT	0x4
 | 
			
		||||
#define   AR8216_VTU_ACTIVE		BIT(3)
 | 
			
		||||
#define   AR8216_VTU_FULL		BIT(4)
 | 
			
		||||
#define   AR8216_VTU_PORT		BITS(8, 4)
 | 
			
		||||
#define   AR8216_VTU_PORT_S		8
 | 
			
		||||
#define   AR8216_VTU_VID		BITS(16, 12)
 | 
			
		||||
#define   AR8216_VTU_VID_S		16
 | 
			
		||||
#define   AR8216_VTU_PRIO		BITS(28, 3)
 | 
			
		||||
#define   AR8216_VTU_PRIO_S		28
 | 
			
		||||
#define   AR8216_VTU_PRIO_EN		BIT(31)
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_VTU_DATA		0x0044
 | 
			
		||||
#define   AR8216_VTUDATA_MEMBER		BITS(0, 10)
 | 
			
		||||
#define   AR8236_VTUDATA_MEMBER		BITS(0, 7)
 | 
			
		||||
#define   AR8216_VTUDATA_VALID		BIT(11)
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_ATU_FUNC0		0x0050
 | 
			
		||||
#define   AR8216_ATU_OP			BITS(0, 3)
 | 
			
		||||
#define   AR8216_ATU_OP_NOOP		0x0
 | 
			
		||||
#define   AR8216_ATU_OP_FLUSH		0x1
 | 
			
		||||
#define   AR8216_ATU_OP_LOAD		0x2
 | 
			
		||||
#define   AR8216_ATU_OP_PURGE		0x3
 | 
			
		||||
#define   AR8216_ATU_OP_FLUSH_UNLOCKED	0x4
 | 
			
		||||
#define   AR8216_ATU_OP_FLUSH_PORT	0x5
 | 
			
		||||
#define   AR8216_ATU_OP_GET_NEXT	0x6
 | 
			
		||||
#define   AR8216_ATU_ACTIVE		BIT(3)
 | 
			
		||||
#define   AR8216_ATU_PORT_NUM		BITS(8, 4)
 | 
			
		||||
#define   AR8216_ATU_PORT_NUM_S		8
 | 
			
		||||
#define   AR8216_ATU_FULL_VIO		BIT(12)
 | 
			
		||||
#define   AR8216_ATU_ADDR5		BITS(16, 8)
 | 
			
		||||
#define   AR8216_ATU_ADDR5_S		16
 | 
			
		||||
#define   AR8216_ATU_ADDR4		BITS(24, 8)
 | 
			
		||||
#define   AR8216_ATU_ADDR4_S		24
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_ATU_FUNC1		0x0054
 | 
			
		||||
#define   AR8216_ATU_ADDR3		BITS(0, 8)
 | 
			
		||||
#define   AR8216_ATU_ADDR3_S		0
 | 
			
		||||
#define   AR8216_ATU_ADDR2		BITS(8, 8)
 | 
			
		||||
#define   AR8216_ATU_ADDR2_S		8
 | 
			
		||||
#define   AR8216_ATU_ADDR1		BITS(16, 8)
 | 
			
		||||
#define   AR8216_ATU_ADDR1_S		16
 | 
			
		||||
#define   AR8216_ATU_ADDR0		BITS(24, 8)
 | 
			
		||||
#define   AR8216_ATU_ADDR0_S		24
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_ATU_FUNC2		0x0058
 | 
			
		||||
#define   AR8216_ATU_PORTS		BITS(0, 6)
 | 
			
		||||
#define   AR8216_ATU_PORTS_S		0
 | 
			
		||||
#define   AR8216_ATU_PORT0		BIT(0)
 | 
			
		||||
#define   AR8216_ATU_PORT1		BIT(1)
 | 
			
		||||
#define   AR8216_ATU_PORT2		BIT(2)
 | 
			
		||||
#define   AR8216_ATU_PORT3		BIT(3)
 | 
			
		||||
#define   AR8216_ATU_PORT4		BIT(4)
 | 
			
		||||
#define   AR8216_ATU_PORT5		BIT(5)
 | 
			
		||||
#define   AR8216_ATU_STATUS		BITS(16, 4)
 | 
			
		||||
#define   AR8216_ATU_STATUS_S		16
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_ATU_CTRL		0x005C
 | 
			
		||||
#define   AR8216_ATU_CTRL_AGE_EN	BIT(17)
 | 
			
		||||
#define   AR8216_ATU_CTRL_AGE_TIME	BITS(0, 16)
 | 
			
		||||
#define   AR8216_ATU_CTRL_AGE_TIME_S	0
 | 
			
		||||
#define   AR8236_ATU_CTRL_RES		BIT(20)
 | 
			
		||||
#define   AR8216_ATU_CTRL_LEARN_CHANGE	BIT(18)
 | 
			
		||||
#define   AR8216_ATU_CTRL_RESERVED	BIT(19)
 | 
			
		||||
#define   AR8216_ATU_CTRL_ARP_EN	BIT(20)
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_TAG_PRIORITY	0x0070
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_SERVICE_TAG		0x0074
 | 
			
		||||
#define  AR8216_SERVICE_TAG_M		BITS(0, 16)
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_MIB_FUNC		0x0080
 | 
			
		||||
#define   AR8216_MIB_TIMER		BITS(0, 16)
 | 
			
		||||
#define   AR8216_MIB_AT_HALF_EN		BIT(16)
 | 
			
		||||
#define   AR8216_MIB_BUSY		BIT(17)
 | 
			
		||||
#define   AR8216_MIB_FUNC		BITS(24, 3)
 | 
			
		||||
#define   AR8216_MIB_FUNC_S		24
 | 
			
		||||
#define   AR8216_MIB_FUNC_NO_OP		0x0
 | 
			
		||||
#define   AR8216_MIB_FUNC_FLUSH		0x1
 | 
			
		||||
#define   AR8216_MIB_FUNC_CAPTURE	0x3
 | 
			
		||||
#define   AR8236_MIB_EN			BIT(30)
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_GLOBAL_CPUPORT		0x0078
 | 
			
		||||
#define   AR8216_GLOBAL_CPUPORT_MIRROR_PORT	BITS(4, 4)
 | 
			
		||||
#define   AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S	4
 | 
			
		||||
#define   AR8216_GLOBAL_CPUPORT_EN		BIT(8)
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_MDIO_CTRL		0x98
 | 
			
		||||
#define   AR8216_MDIO_CTRL_DATA_M	BITS(0, 16)
 | 
			
		||||
#define   AR8216_MDIO_CTRL_REG_ADDR_S	16
 | 
			
		||||
#define   AR8216_MDIO_CTRL_PHY_ADDR_S	21
 | 
			
		||||
#define   AR8216_MDIO_CTRL_CMD_WRITE	0
 | 
			
		||||
#define   AR8216_MDIO_CTRL_CMD_READ	BIT(27)
 | 
			
		||||
#define   AR8216_MDIO_CTRL_MASTER_EN	BIT(30)
 | 
			
		||||
#define   AR8216_MDIO_CTRL_BUSY	BIT(31)
 | 
			
		||||
 | 
			
		||||
#define AR8216_PORT_OFFSET(_i)		(0x0100 * (_i + 1))
 | 
			
		||||
#define AR8216_REG_PORT_STATUS(_i)	(AR8216_PORT_OFFSET(_i) + 0x0000)
 | 
			
		||||
#define   AR8216_PORT_STATUS_SPEED	BITS(0,2)
 | 
			
		||||
#define   AR8216_PORT_STATUS_SPEED_S	0
 | 
			
		||||
#define   AR8216_PORT_STATUS_TXMAC	BIT(2)
 | 
			
		||||
#define   AR8216_PORT_STATUS_RXMAC	BIT(3)
 | 
			
		||||
#define   AR8216_PORT_STATUS_TXFLOW	BIT(4)
 | 
			
		||||
#define   AR8216_PORT_STATUS_RXFLOW	BIT(5)
 | 
			
		||||
#define   AR8216_PORT_STATUS_DUPLEX	BIT(6)
 | 
			
		||||
#define   AR8216_PORT_STATUS_LINK_UP	BIT(8)
 | 
			
		||||
#define   AR8216_PORT_STATUS_LINK_AUTO	BIT(9)
 | 
			
		||||
#define   AR8216_PORT_STATUS_LINK_PAUSE	BIT(10)
 | 
			
		||||
#define   AR8216_PORT_STATUS_FLOW_CONTROL  BIT(12)
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_PORT_CTRL(_i)	(AR8216_PORT_OFFSET(_i) + 0x0004)
 | 
			
		||||
 | 
			
		||||
/* port forwarding state */
 | 
			
		||||
#define   AR8216_PORT_CTRL_STATE	BITS(0, 3)
 | 
			
		||||
#define   AR8216_PORT_CTRL_STATE_S	0
 | 
			
		||||
 | 
			
		||||
#define   AR8216_PORT_CTRL_LEARN_LOCK	BIT(7)
 | 
			
		||||
 | 
			
		||||
/* egress 802.1q mode */
 | 
			
		||||
#define   AR8216_PORT_CTRL_VLAN_MODE	BITS(8, 2)
 | 
			
		||||
#define   AR8216_PORT_CTRL_VLAN_MODE_S	8
 | 
			
		||||
 | 
			
		||||
#define   AR8216_PORT_CTRL_IGMP_SNOOP	BIT(10)
 | 
			
		||||
#define   AR8216_PORT_CTRL_HEADER	BIT(11)
 | 
			
		||||
#define   AR8216_PORT_CTRL_MAC_LOOP	BIT(12)
 | 
			
		||||
#define   AR8216_PORT_CTRL_SINGLE_VLAN	BIT(13)
 | 
			
		||||
#define   AR8216_PORT_CTRL_LEARN	BIT(14)
 | 
			
		||||
#define   AR8216_PORT_CTRL_MIRROR_TX	BIT(16)
 | 
			
		||||
#define   AR8216_PORT_CTRL_MIRROR_RX	BIT(17)
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_PORT_VLAN(_i)	(AR8216_PORT_OFFSET(_i) + 0x0008)
 | 
			
		||||
 | 
			
		||||
#define   AR8216_PORT_VLAN_DEFAULT_ID	BITS(0, 12)
 | 
			
		||||
#define   AR8216_PORT_VLAN_DEFAULT_ID_S	0
 | 
			
		||||
 | 
			
		||||
#define   AR8216_PORT_VLAN_DEST_PORTS	BITS(16, 9)
 | 
			
		||||
#define   AR8216_PORT_VLAN_DEST_PORTS_S	16
 | 
			
		||||
 | 
			
		||||
/* bit0 added to the priority field of egress frames */
 | 
			
		||||
#define   AR8216_PORT_VLAN_TX_PRIO	BIT(27)
 | 
			
		||||
 | 
			
		||||
/* port default priority */
 | 
			
		||||
#define   AR8216_PORT_VLAN_PRIORITY	BITS(28, 2)
 | 
			
		||||
#define   AR8216_PORT_VLAN_PRIORITY_S	28
 | 
			
		||||
 | 
			
		||||
/* ingress 802.1q mode */
 | 
			
		||||
#define   AR8216_PORT_VLAN_MODE		BITS(30, 2)
 | 
			
		||||
#define   AR8216_PORT_VLAN_MODE_S	30
 | 
			
		||||
 | 
			
		||||
#define AR8216_REG_PORT_RATE(_i)	(AR8216_PORT_OFFSET(_i) + 0x000c)
 | 
			
		||||
#define AR8216_REG_PORT_PRIO(_i)	(AR8216_PORT_OFFSET(_i) + 0x0010)
 | 
			
		||||
 | 
			
		||||
#define AR8216_STATS_RXBROAD		0x00
 | 
			
		||||
#define AR8216_STATS_RXPAUSE		0x04
 | 
			
		||||
#define AR8216_STATS_RXMULTI		0x08
 | 
			
		||||
#define AR8216_STATS_RXFCSERR		0x0c
 | 
			
		||||
#define AR8216_STATS_RXALIGNERR		0x10
 | 
			
		||||
#define AR8216_STATS_RXRUNT		0x14
 | 
			
		||||
#define AR8216_STATS_RXFRAGMENT		0x18
 | 
			
		||||
#define AR8216_STATS_RX64BYTE		0x1c
 | 
			
		||||
#define AR8216_STATS_RX128BYTE		0x20
 | 
			
		||||
#define AR8216_STATS_RX256BYTE		0x24
 | 
			
		||||
#define AR8216_STATS_RX512BYTE		0x28
 | 
			
		||||
#define AR8216_STATS_RX1024BYTE		0x2c
 | 
			
		||||
#define AR8216_STATS_RXMAXBYTE		0x30
 | 
			
		||||
#define AR8216_STATS_RXTOOLONG		0x34
 | 
			
		||||
#define AR8216_STATS_RXGOODBYTE		0x38
 | 
			
		||||
#define AR8216_STATS_RXBADBYTE		0x40
 | 
			
		||||
#define AR8216_STATS_RXOVERFLOW		0x48
 | 
			
		||||
#define AR8216_STATS_FILTERED		0x4c
 | 
			
		||||
#define AR8216_STATS_TXBROAD		0x50
 | 
			
		||||
#define AR8216_STATS_TXPAUSE		0x54
 | 
			
		||||
#define AR8216_STATS_TXMULTI		0x58
 | 
			
		||||
#define AR8216_STATS_TXUNDERRUN		0x5c
 | 
			
		||||
#define AR8216_STATS_TX64BYTE		0x60
 | 
			
		||||
#define AR8216_STATS_TX128BYTE		0x64
 | 
			
		||||
#define AR8216_STATS_TX256BYTE		0x68
 | 
			
		||||
#define AR8216_STATS_TX512BYTE		0x6c
 | 
			
		||||
#define AR8216_STATS_TX1024BYTE		0x70
 | 
			
		||||
#define AR8216_STATS_TXMAXBYTE		0x74
 | 
			
		||||
#define AR8216_STATS_TXOVERSIZE		0x78
 | 
			
		||||
#define AR8216_STATS_TXBYTE		0x7c
 | 
			
		||||
#define AR8216_STATS_TXCOLLISION	0x84
 | 
			
		||||
#define AR8216_STATS_TXABORTCOL		0x88
 | 
			
		||||
#define AR8216_STATS_TXMULTICOL		0x8c
 | 
			
		||||
#define AR8216_STATS_TXSINGLECOL	0x90
 | 
			
		||||
#define AR8216_STATS_TXEXCDEFER		0x94
 | 
			
		||||
#define AR8216_STATS_TXDEFER		0x98
 | 
			
		||||
#define AR8216_STATS_TXLATECOL		0x9c
 | 
			
		||||
 | 
			
		||||
#define AR8216_MIB_RXB_ID		14	/* RxGoodByte */
 | 
			
		||||
#define AR8216_MIB_TXB_ID		29	/* TxByte */
 | 
			
		||||
 | 
			
		||||
#define AR8229_REG_OPER_MODE0		0x04
 | 
			
		||||
#define   AR8229_OPER_MODE0_MAC_GMII_EN	BIT(6)
 | 
			
		||||
#define   AR8229_OPER_MODE0_PHY_MII_EN	BIT(10)
 | 
			
		||||
 | 
			
		||||
#define AR8229_REG_OPER_MODE1		0x08
 | 
			
		||||
#define   AR8229_REG_OPER_MODE1_PHY4_MII_EN	BIT(28)
 | 
			
		||||
 | 
			
		||||
#define AR8229_REG_QM_CTRL		0x3c
 | 
			
		||||
#define   AR8229_QM_CTRL_ARP_EN		BIT(15)
 | 
			
		||||
 | 
			
		||||
#define AR8236_REG_PORT_VLAN(_i)	(AR8216_PORT_OFFSET((_i)) + 0x0008)
 | 
			
		||||
#define   AR8236_PORT_VLAN_DEFAULT_ID	BITS(16, 12)
 | 
			
		||||
#define   AR8236_PORT_VLAN_DEFAULT_ID_S	16
 | 
			
		||||
#define   AR8236_PORT_VLAN_PRIORITY	BITS(29, 3)
 | 
			
		||||
#define   AR8236_PORT_VLAN_PRIORITY_S	28
 | 
			
		||||
 | 
			
		||||
#define AR8236_REG_PORT_VLAN2(_i)	(AR8216_PORT_OFFSET((_i)) + 0x000c)
 | 
			
		||||
#define   AR8236_PORT_VLAN2_MEMBER	BITS(16, 7)
 | 
			
		||||
#define   AR8236_PORT_VLAN2_MEMBER_S	16
 | 
			
		||||
#define   AR8236_PORT_VLAN2_TX_PRIO	BIT(23)
 | 
			
		||||
#define   AR8236_PORT_VLAN2_VLAN_MODE	BITS(30, 2)
 | 
			
		||||
#define   AR8236_PORT_VLAN2_VLAN_MODE_S	30
 | 
			
		||||
 | 
			
		||||
#define AR8236_STATS_RXBROAD		0x00
 | 
			
		||||
#define AR8236_STATS_RXPAUSE		0x04
 | 
			
		||||
#define AR8236_STATS_RXMULTI		0x08
 | 
			
		||||
#define AR8236_STATS_RXFCSERR		0x0c
 | 
			
		||||
#define AR8236_STATS_RXALIGNERR		0x10
 | 
			
		||||
#define AR8236_STATS_RXRUNT		0x14
 | 
			
		||||
#define AR8236_STATS_RXFRAGMENT		0x18
 | 
			
		||||
#define AR8236_STATS_RX64BYTE		0x1c
 | 
			
		||||
#define AR8236_STATS_RX128BYTE		0x20
 | 
			
		||||
#define AR8236_STATS_RX256BYTE		0x24
 | 
			
		||||
#define AR8236_STATS_RX512BYTE		0x28
 | 
			
		||||
#define AR8236_STATS_RX1024BYTE		0x2c
 | 
			
		||||
#define AR8236_STATS_RX1518BYTE		0x30
 | 
			
		||||
#define AR8236_STATS_RXMAXBYTE		0x34
 | 
			
		||||
#define AR8236_STATS_RXTOOLONG		0x38
 | 
			
		||||
#define AR8236_STATS_RXGOODBYTE		0x3c
 | 
			
		||||
#define AR8236_STATS_RXBADBYTE		0x44
 | 
			
		||||
#define AR8236_STATS_RXOVERFLOW		0x4c
 | 
			
		||||
#define AR8236_STATS_FILTERED		0x50
 | 
			
		||||
#define AR8236_STATS_TXBROAD		0x54
 | 
			
		||||
#define AR8236_STATS_TXPAUSE		0x58
 | 
			
		||||
#define AR8236_STATS_TXMULTI		0x5c
 | 
			
		||||
#define AR8236_STATS_TXUNDERRUN		0x60
 | 
			
		||||
#define AR8236_STATS_TX64BYTE		0x64
 | 
			
		||||
#define AR8236_STATS_TX128BYTE		0x68
 | 
			
		||||
#define AR8236_STATS_TX256BYTE		0x6c
 | 
			
		||||
#define AR8236_STATS_TX512BYTE		0x70
 | 
			
		||||
#define AR8236_STATS_TX1024BYTE		0x74
 | 
			
		||||
#define AR8236_STATS_TX1518BYTE		0x78
 | 
			
		||||
#define AR8236_STATS_TXMAXBYTE		0x7c
 | 
			
		||||
#define AR8236_STATS_TXOVERSIZE		0x80
 | 
			
		||||
#define AR8236_STATS_TXBYTE		0x84
 | 
			
		||||
#define AR8236_STATS_TXCOLLISION	0x8c
 | 
			
		||||
#define AR8236_STATS_TXABORTCOL		0x90
 | 
			
		||||
#define AR8236_STATS_TXMULTICOL		0x94
 | 
			
		||||
#define AR8236_STATS_TXSINGLECOL	0x98
 | 
			
		||||
#define AR8236_STATS_TXEXCDEFER		0x9c
 | 
			
		||||
#define AR8236_STATS_TXDEFER		0xa0
 | 
			
		||||
#define AR8236_STATS_TXLATECOL		0xa4
 | 
			
		||||
 | 
			
		||||
#define AR8236_MIB_RXB_ID		15	/* RxGoodByte */
 | 
			
		||||
#define AR8236_MIB_TXB_ID		31	/* TxByte */
 | 
			
		||||
 | 
			
		||||
#define AR8316_REG_POSTRIP			0x0008
 | 
			
		||||
#define   AR8316_POSTRIP_MAC0_GMII_EN		BIT(0)
 | 
			
		||||
#define   AR8316_POSTRIP_MAC0_RGMII_EN		BIT(1)
 | 
			
		||||
#define   AR8316_POSTRIP_PHY4_GMII_EN		BIT(2)
 | 
			
		||||
#define   AR8316_POSTRIP_PHY4_RGMII_EN		BIT(3)
 | 
			
		||||
#define   AR8316_POSTRIP_MAC0_MAC_MODE		BIT(4)
 | 
			
		||||
#define   AR8316_POSTRIP_RTL_MODE		BIT(5)
 | 
			
		||||
#define   AR8316_POSTRIP_RGMII_RXCLK_DELAY_EN	BIT(6)
 | 
			
		||||
#define   AR8316_POSTRIP_RGMII_TXCLK_DELAY_EN	BIT(7)
 | 
			
		||||
#define   AR8316_POSTRIP_SERDES_EN		BIT(8)
 | 
			
		||||
#define   AR8316_POSTRIP_SEL_ANA_RST		BIT(9)
 | 
			
		||||
#define   AR8316_POSTRIP_GATE_25M_EN		BIT(10)
 | 
			
		||||
#define   AR8316_POSTRIP_SEL_CLK25M		BIT(11)
 | 
			
		||||
#define   AR8316_POSTRIP_HIB_PULSE_HW		BIT(12)
 | 
			
		||||
#define   AR8316_POSTRIP_DBG_MODE_I		BIT(13)
 | 
			
		||||
#define   AR8316_POSTRIP_MAC5_MAC_MODE		BIT(14)
 | 
			
		||||
#define   AR8316_POSTRIP_MAC5_PHY_MODE		BIT(15)
 | 
			
		||||
#define   AR8316_POSTRIP_POWER_DOWN_HW		BIT(16)
 | 
			
		||||
#define   AR8316_POSTRIP_LPW_STATE_EN		BIT(17)
 | 
			
		||||
#define   AR8316_POSTRIP_MAN_EN			BIT(18)
 | 
			
		||||
#define   AR8316_POSTRIP_PHY_PLL_ON		BIT(19)
 | 
			
		||||
#define   AR8316_POSTRIP_LPW_EXIT		BIT(20)
 | 
			
		||||
#define   AR8316_POSTRIP_TXDELAY_S0		BIT(21)
 | 
			
		||||
#define   AR8316_POSTRIP_TXDELAY_S1		BIT(22)
 | 
			
		||||
#define   AR8316_POSTRIP_RXDELAY_S0		BIT(23)
 | 
			
		||||
#define   AR8316_POSTRIP_LED_OPEN_EN		BIT(24)
 | 
			
		||||
#define   AR8316_POSTRIP_SPI_EN			BIT(25)
 | 
			
		||||
#define   AR8316_POSTRIP_RXDELAY_S1		BIT(26)
 | 
			
		||||
#define   AR8316_POSTRIP_POWER_ON_SEL		BIT(31)
 | 
			
		||||
 | 
			
		||||
/* port speed */
 | 
			
		||||
enum {
 | 
			
		||||
        AR8216_PORT_SPEED_10M = 0,
 | 
			
		||||
        AR8216_PORT_SPEED_100M = 1,
 | 
			
		||||
        AR8216_PORT_SPEED_1000M = 2,
 | 
			
		||||
        AR8216_PORT_SPEED_ERR = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* ingress 802.1q mode */
 | 
			
		||||
enum {
 | 
			
		||||
	AR8216_IN_PORT_ONLY = 0,
 | 
			
		||||
	AR8216_IN_PORT_FALLBACK = 1,
 | 
			
		||||
	AR8216_IN_VLAN_ONLY = 2,
 | 
			
		||||
	AR8216_IN_SECURE = 3
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* egress 802.1q mode */
 | 
			
		||||
enum {
 | 
			
		||||
	AR8216_OUT_KEEP = 0,
 | 
			
		||||
	AR8216_OUT_STRIP_VLAN = 1,
 | 
			
		||||
	AR8216_OUT_ADD_VLAN = 2
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* port forwarding state */
 | 
			
		||||
enum {
 | 
			
		||||
	AR8216_PORT_STATE_DISABLED = 0,
 | 
			
		||||
	AR8216_PORT_STATE_BLOCK = 1,
 | 
			
		||||
	AR8216_PORT_STATE_LISTEN = 2,
 | 
			
		||||
	AR8216_PORT_STATE_LEARN = 3,
 | 
			
		||||
	AR8216_PORT_STATE_FORWARD = 4
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* mib counter type */
 | 
			
		||||
enum {
 | 
			
		||||
	AR8XXX_MIB_BASIC = 0,
 | 
			
		||||
	AR8XXX_MIB_EXTENDED = 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	AR8XXX_VER_AR8216 = 0x01,
 | 
			
		||||
	AR8XXX_VER_AR8236 = 0x03,
 | 
			
		||||
	AR8XXX_VER_AR8316 = 0x10,
 | 
			
		||||
	AR8XXX_VER_AR8327 = 0x12,
 | 
			
		||||
	AR8XXX_VER_AR8337 = 0x13,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define AR8XXX_NUM_ARL_RECORDS	100
 | 
			
		||||
 | 
			
		||||
enum arl_op {
 | 
			
		||||
	AR8XXX_ARL_INITIALIZE,
 | 
			
		||||
	AR8XXX_ARL_GET_NEXT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct arl_entry {
 | 
			
		||||
	u16 portmap;
 | 
			
		||||
	u8 mac[6];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ar8xxx_priv;
 | 
			
		||||
 | 
			
		||||
struct ar8xxx_mib_desc {
 | 
			
		||||
	unsigned int size;
 | 
			
		||||
	unsigned int offset;
 | 
			
		||||
	const char *name;
 | 
			
		||||
	u8 type;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ar8xxx_chip {
 | 
			
		||||
	unsigned long caps;
 | 
			
		||||
	bool config_at_probe;
 | 
			
		||||
	bool mii_lo_first;
 | 
			
		||||
 | 
			
		||||
	/* parameters to calculate REG_PORT_STATS_BASE */
 | 
			
		||||
	unsigned reg_port_stats_start;
 | 
			
		||||
	unsigned reg_port_stats_length;
 | 
			
		||||
 | 
			
		||||
	unsigned reg_arl_ctrl;
 | 
			
		||||
 | 
			
		||||
	int (*hw_init)(struct ar8xxx_priv *priv);
 | 
			
		||||
	void (*cleanup)(struct ar8xxx_priv *priv);
 | 
			
		||||
 | 
			
		||||
	const char *name;
 | 
			
		||||
	int vlans;
 | 
			
		||||
	int ports;
 | 
			
		||||
	const struct switch_dev_ops *swops;
 | 
			
		||||
 | 
			
		||||
	void (*init_globals)(struct ar8xxx_priv *priv);
 | 
			
		||||
	void (*init_port)(struct ar8xxx_priv *priv, int port);
 | 
			
		||||
	void (*setup_port)(struct ar8xxx_priv *priv, int port, u32 members);
 | 
			
		||||
	u32 (*read_port_status)(struct ar8xxx_priv *priv, int port);
 | 
			
		||||
	u32 (*read_port_eee_status)(struct ar8xxx_priv *priv, int port);
 | 
			
		||||
	int (*atu_flush)(struct ar8xxx_priv *priv);
 | 
			
		||||
	int (*atu_flush_port)(struct ar8xxx_priv *priv, int port);
 | 
			
		||||
	void (*vtu_flush)(struct ar8xxx_priv *priv);
 | 
			
		||||
	void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vid, u32 port_mask);
 | 
			
		||||
	void (*phy_fixup)(struct ar8xxx_priv *priv, int phy);
 | 
			
		||||
	void (*set_mirror_regs)(struct ar8xxx_priv *priv);
 | 
			
		||||
	void (*get_arl_entry)(struct ar8xxx_priv *priv, struct arl_entry *a,
 | 
			
		||||
			      u32 *status, enum arl_op op);
 | 
			
		||||
	int (*sw_hw_apply)(struct switch_dev *dev);
 | 
			
		||||
	void (*phy_rgmii_set)(struct ar8xxx_priv *priv, struct phy_device *phydev);
 | 
			
		||||
	int (*phy_read)(struct ar8xxx_priv *priv, int addr, int regnum);
 | 
			
		||||
	int (*phy_write)(struct ar8xxx_priv *priv, int addr, int regnum, u16 val);
 | 
			
		||||
 | 
			
		||||
	const struct ar8xxx_mib_desc *mib_decs;
 | 
			
		||||
	unsigned num_mibs;
 | 
			
		||||
	unsigned mib_func;
 | 
			
		||||
	int mib_rxb_id;
 | 
			
		||||
	int mib_txb_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ar8xxx_priv {
 | 
			
		||||
	struct switch_dev dev;
 | 
			
		||||
	struct mii_bus *mii_bus;
 | 
			
		||||
	struct mii_bus *sw_mii_bus;
 | 
			
		||||
	struct phy_device *phy;
 | 
			
		||||
	struct device *pdev;
 | 
			
		||||
 | 
			
		||||
	int (*get_port_link)(unsigned port);
 | 
			
		||||
 | 
			
		||||
	const struct net_device_ops *ndo_old;
 | 
			
		||||
	struct net_device_ops ndo;
 | 
			
		||||
	struct mutex reg_mutex;
 | 
			
		||||
	u8 chip_ver;
 | 
			
		||||
	u8 chip_rev;
 | 
			
		||||
	const struct ar8xxx_chip *chip;
 | 
			
		||||
	void *chip_data;
 | 
			
		||||
	bool initialized;
 | 
			
		||||
	bool port4_phy;
 | 
			
		||||
	char buf[2048];
 | 
			
		||||
	struct arl_entry arl_table[AR8XXX_NUM_ARL_RECORDS];
 | 
			
		||||
	char arl_buf[AR8XXX_NUM_ARL_RECORDS * 32 + 256];
 | 
			
		||||
	bool link_up[AR8X16_MAX_PORTS];
 | 
			
		||||
 | 
			
		||||
	bool init;
 | 
			
		||||
 | 
			
		||||
	struct mutex mib_lock;
 | 
			
		||||
	struct delayed_work mib_work;
 | 
			
		||||
	u64 *mib_stats;
 | 
			
		||||
	u32 mib_poll_interval;
 | 
			
		||||
	u8 mib_type;
 | 
			
		||||
 | 
			
		||||
	struct list_head list;
 | 
			
		||||
	unsigned int use_count;
 | 
			
		||||
 | 
			
		||||
	/* all fields below are cleared on reset */
 | 
			
		||||
	struct_group(ar8xxx_priv_volatile,
 | 
			
		||||
		bool vlan;
 | 
			
		||||
 | 
			
		||||
		u16 vlan_id[AR8XXX_MAX_VLANS];
 | 
			
		||||
		u8 vlan_table[AR8XXX_MAX_VLANS];
 | 
			
		||||
		u8 vlan_tagged;
 | 
			
		||||
		u16 pvid[AR8X16_MAX_PORTS];
 | 
			
		||||
		int arl_age_time;
 | 
			
		||||
 | 
			
		||||
		/* mirroring */
 | 
			
		||||
		bool mirror_rx;
 | 
			
		||||
		bool mirror_tx;
 | 
			
		||||
		int source_port;
 | 
			
		||||
		int monitor_port;
 | 
			
		||||
		u8 port_vlan_prio[AR8X16_MAX_PORTS];
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
u32
 | 
			
		||||
ar8xxx_mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum);
 | 
			
		||||
void
 | 
			
		||||
ar8xxx_mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val);
 | 
			
		||||
u32
 | 
			
		||||
ar8xxx_read(struct ar8xxx_priv *priv, int reg);
 | 
			
		||||
void
 | 
			
		||||
ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val);
 | 
			
		||||
u32
 | 
			
		||||
ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val);
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
ar8xxx_phy_dbg_read(struct ar8xxx_priv *priv, int phy_addr,
 | 
			
		||||
		u16 dbg_addr, u16 *dbg_data);
 | 
			
		||||
void
 | 
			
		||||
ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr,
 | 
			
		||||
		     u16 dbg_addr, u16 dbg_data);
 | 
			
		||||
void
 | 
			
		||||
ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg, u16 data);
 | 
			
		||||
u16
 | 
			
		||||
ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg);
 | 
			
		||||
void
 | 
			
		||||
ar8xxx_phy_init(struct ar8xxx_priv *priv);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
 | 
			
		||||
		   struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
 | 
			
		||||
		   struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_set_reset_mibs(struct switch_dev *dev,
 | 
			
		||||
			 const struct switch_attr *attr,
 | 
			
		||||
			 struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_set_mib_poll_interval(struct switch_dev *dev,
 | 
			
		||||
			       const struct switch_attr *attr,
 | 
			
		||||
			       struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_get_mib_poll_interval(struct switch_dev *dev,
 | 
			
		||||
			       const struct switch_attr *attr,
 | 
			
		||||
			       struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_set_mib_type(struct switch_dev *dev,
 | 
			
		||||
			       const struct switch_attr *attr,
 | 
			
		||||
			       struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_get_mib_type(struct switch_dev *dev,
 | 
			
		||||
			       const struct switch_attr *attr,
 | 
			
		||||
			       struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev,
 | 
			
		||||
			       const struct switch_attr *attr,
 | 
			
		||||
			       struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev,
 | 
			
		||||
			       const struct switch_attr *attr,
 | 
			
		||||
			       struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev,
 | 
			
		||||
			       const struct switch_attr *attr,
 | 
			
		||||
			       struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev,
 | 
			
		||||
			       const struct switch_attr *attr,
 | 
			
		||||
			       struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev,
 | 
			
		||||
				  const struct switch_attr *attr,
 | 
			
		||||
				  struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev,
 | 
			
		||||
				  const struct switch_attr *attr,
 | 
			
		||||
				  struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev,
 | 
			
		||||
				 const struct switch_attr *attr,
 | 
			
		||||
				 struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev,
 | 
			
		||||
				 const struct switch_attr *attr,
 | 
			
		||||
				 struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_hw_apply(struct switch_dev *dev);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_reset_switch(struct switch_dev *dev);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_get_port_link(struct switch_dev *dev, int port,
 | 
			
		||||
			struct switch_port_link *link);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
 | 
			
		||||
                             const struct switch_attr *attr,
 | 
			
		||||
                             struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_get_port_mib(struct switch_dev *dev,
 | 
			
		||||
                       const struct switch_attr *attr,
 | 
			
		||||
                       struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_get_arl_age_time(struct switch_dev *dev,
 | 
			
		||||
			   const struct switch_attr *attr,
 | 
			
		||||
			   struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_set_arl_age_time(struct switch_dev *dev,
 | 
			
		||||
			   const struct switch_attr *attr,
 | 
			
		||||
			   struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_get_arl_table(struct switch_dev *dev,
 | 
			
		||||
			const struct switch_attr *attr,
 | 
			
		||||
			struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_set_flush_arl_table(struct switch_dev *dev,
 | 
			
		||||
			      const struct switch_attr *attr,
 | 
			
		||||
			      struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_set_flush_port_arl_table(struct switch_dev *dev,
 | 
			
		||||
				   const struct switch_attr *attr,
 | 
			
		||||
				   struct switch_val *val);
 | 
			
		||||
int
 | 
			
		||||
ar8xxx_sw_get_port_stats(struct switch_dev *dev, int port,
 | 
			
		||||
			struct switch_port_stats *stats);
 | 
			
		||||
int
 | 
			
		||||
ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val);
 | 
			
		||||
 | 
			
		||||
static inline struct ar8xxx_priv *
 | 
			
		||||
swdev_to_ar8xxx(struct switch_dev *swdev)
 | 
			
		||||
{
 | 
			
		||||
	return container_of(swdev, struct ar8xxx_priv, dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool ar8xxx_has_gige(struct ar8xxx_priv *priv)
 | 
			
		||||
{
 | 
			
		||||
	return priv->chip->caps & AR8XXX_CAP_GIGE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool ar8xxx_has_mib_counters(struct ar8xxx_priv *priv)
 | 
			
		||||
{
 | 
			
		||||
	return priv->chip->caps & AR8XXX_CAP_MIB_COUNTERS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool chip_is_ar8216(struct ar8xxx_priv *priv)
 | 
			
		||||
{
 | 
			
		||||
	return priv->chip_ver == AR8XXX_VER_AR8216;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool chip_is_ar8236(struct ar8xxx_priv *priv)
 | 
			
		||||
{
 | 
			
		||||
	return priv->chip_ver == AR8XXX_VER_AR8236;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool chip_is_ar8316(struct ar8xxx_priv *priv)
 | 
			
		||||
{
 | 
			
		||||
	return priv->chip_ver == AR8XXX_VER_AR8316;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool chip_is_ar8327(struct ar8xxx_priv *priv)
 | 
			
		||||
{
 | 
			
		||||
	return priv->chip_ver == AR8XXX_VER_AR8327;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool chip_is_ar8337(struct ar8xxx_priv *priv)
 | 
			
		||||
{
 | 
			
		||||
	return priv->chip_ver == AR8XXX_VER_AR8337;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void
 | 
			
		||||
ar8xxx_reg_set(struct ar8xxx_priv *priv, int reg, u32 val)
 | 
			
		||||
{
 | 
			
		||||
	ar8xxx_rmw(priv, reg, 0, val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void
 | 
			
		||||
ar8xxx_reg_clear(struct ar8xxx_priv *priv, int reg, u32 val)
 | 
			
		||||
{
 | 
			
		||||
	ar8xxx_rmw(priv, reg, val, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void
 | 
			
		||||
split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
 | 
			
		||||
{
 | 
			
		||||
	regaddr >>= 1;
 | 
			
		||||
	*r1 = regaddr & 0x1e;
 | 
			
		||||
 | 
			
		||||
	regaddr >>= 5;
 | 
			
		||||
	*r2 = regaddr & 0x7;
 | 
			
		||||
 | 
			
		||||
	regaddr >>= 3;
 | 
			
		||||
	*page = regaddr & 0x1ff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void
 | 
			
		||||
wait_for_page_switch(void)
 | 
			
		||||
{
 | 
			
		||||
	udelay(5);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										1551
									
								
								6.12/target/linux/generic/files/drivers/net/phy/ar8327.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1551
									
								
								6.12/target/linux/generic/files/drivers/net/phy/ar8327.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										333
									
								
								6.12/target/linux/generic/files/drivers/net/phy/ar8327.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										333
									
								
								6.12/target/linux/generic/files/drivers/net/phy/ar8327.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,333 @@
 | 
			
		|||
/*
 | 
			
		||||
 * ar8327.h: AR8216 switch driver
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __AR8327_H
 | 
			
		||||
#define __AR8327_H
 | 
			
		||||
 | 
			
		||||
#define AR8327_NUM_PORTS	7
 | 
			
		||||
#define AR8327_NUM_LEDS		15
 | 
			
		||||
#define AR8327_PORTS_ALL	0x7f
 | 
			
		||||
#define AR8327_NUM_LED_CTRL_REGS	4
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_MASK				0x000
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_PAD0_MODE			0x004
 | 
			
		||||
#define AR8327_REG_PAD5_MODE			0x008
 | 
			
		||||
#define AR8327_REG_PAD6_MODE			0x00c
 | 
			
		||||
#define   AR8327_PAD_MAC_MII_RXCLK_SEL		BIT(0)
 | 
			
		||||
#define   AR8327_PAD_MAC_MII_TXCLK_SEL		BIT(1)
 | 
			
		||||
#define   AR8327_PAD_MAC_MII_EN			BIT(2)
 | 
			
		||||
#define   AR8327_PAD_MAC_GMII_RXCLK_SEL		BIT(4)
 | 
			
		||||
#define   AR8327_PAD_MAC_GMII_TXCLK_SEL		BIT(5)
 | 
			
		||||
#define   AR8327_PAD_MAC_GMII_EN		BIT(6)
 | 
			
		||||
#define   AR8327_PAD_SGMII_EN			BIT(7)
 | 
			
		||||
#define   AR8327_PAD_PHY_MII_RXCLK_SEL		BIT(8)
 | 
			
		||||
#define   AR8327_PAD_PHY_MII_TXCLK_SEL		BIT(9)
 | 
			
		||||
#define   AR8327_PAD_PHY_MII_EN			BIT(10)
 | 
			
		||||
#define   AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL	BIT(11)
 | 
			
		||||
#define   AR8327_PAD_PHY_GMII_RXCLK_SEL		BIT(12)
 | 
			
		||||
#define   AR8327_PAD_PHY_GMII_TXCLK_SEL		BIT(13)
 | 
			
		||||
#define   AR8327_PAD_PHY_GMII_EN		BIT(14)
 | 
			
		||||
#define   AR8327_PAD_PHYX_GMII_EN		BIT(16)
 | 
			
		||||
#define   AR8327_PAD_PHYX_RGMII_EN		BIT(17)
 | 
			
		||||
#define   AR8327_PAD_PHYX_MII_EN		BIT(18)
 | 
			
		||||
#define   AR8327_PAD_SGMII_DELAY_EN		BIT(19)
 | 
			
		||||
#define   AR8327_PAD_RGMII_RXCLK_DELAY_SEL	BITS(20, 2)
 | 
			
		||||
#define   AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S	20
 | 
			
		||||
#define   AR8327_PAD_RGMII_TXCLK_DELAY_SEL	BITS(22, 2)
 | 
			
		||||
#define   AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S	22
 | 
			
		||||
#define   AR8327_PAD_RGMII_RXCLK_DELAY_EN	BIT(24)
 | 
			
		||||
#define   AR8327_PAD_RGMII_TXCLK_DELAY_EN	BIT(25)
 | 
			
		||||
#define   AR8327_PAD_RGMII_EN			BIT(26)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_POWER_ON_STRAP		0x010
 | 
			
		||||
#define   AR8327_POWER_ON_STRAP_POWER_ON_SEL	BIT(31)
 | 
			
		||||
#define   AR8327_POWER_ON_STRAP_LED_OPEN_EN	BIT(24)
 | 
			
		||||
#define   AR8327_POWER_ON_STRAP_SERDES_AEN	BIT(7)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_INT_STATUS0			0x020
 | 
			
		||||
#define   AR8327_INT0_VT_DONE			BIT(20)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_INT_STATUS1			0x024
 | 
			
		||||
#define AR8327_REG_INT_MASK0			0x028
 | 
			
		||||
#define AR8327_REG_INT_MASK1			0x02c
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_MODULE_EN			0x030
 | 
			
		||||
#define   AR8327_MODULE_EN_MIB			BIT(0)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_MIB_FUNC			0x034
 | 
			
		||||
#define   AR8327_MIB_CPU_KEEP			BIT(20)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_SERVICE_TAG			0x048
 | 
			
		||||
#define AR8327_REG_LED_CTRL(_i)			(0x050 + (_i) * 4)
 | 
			
		||||
#define AR8327_REG_LED_CTRL0			0x050
 | 
			
		||||
#define AR8327_REG_LED_CTRL1			0x054
 | 
			
		||||
#define AR8327_REG_LED_CTRL2			0x058
 | 
			
		||||
#define AR8327_REG_LED_CTRL3			0x05c
 | 
			
		||||
#define AR8327_REG_MAC_ADDR0			0x060
 | 
			
		||||
#define AR8327_REG_MAC_ADDR1			0x064
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_MAX_FRAME_SIZE		0x078
 | 
			
		||||
#define   AR8327_MAX_FRAME_SIZE_MTU		BITS(0, 14)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_PORT_STATUS(_i)		(0x07c + (_i) * 4)
 | 
			
		||||
#define   AR8327_PORT_STATUS_TXFLOW_AUTO	BIT(10)
 | 
			
		||||
#define   AR8327_PORT_STATUS_RXFLOW_AUTO	BIT(11)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_HEADER_CTRL			0x098
 | 
			
		||||
#define AR8327_REG_PORT_HEADER(_i)		(0x09c + (_i) * 4)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_SGMII_CTRL			0x0e0
 | 
			
		||||
#define   AR8327_SGMII_CTRL_EN_PLL		BIT(1)
 | 
			
		||||
#define   AR8327_SGMII_CTRL_EN_RX		BIT(2)
 | 
			
		||||
#define   AR8327_SGMII_CTRL_EN_TX		BIT(3)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_EEE_CTRL			0x100
 | 
			
		||||
#define   AR8327_EEE_CTRL_DISABLE_PHY(_i)	BIT(4 + (_i) * 2)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_FRAME_ACK_CTRL0		0x210
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN0	BIT(0)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN0	BIT(1)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN0	BIT(2)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN0	BIT(3)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_DHCP_EN0	BIT(4)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN0	BIT(5)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN0	BIT(6)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN1	BIT(8)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN1	BIT(9)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN1	BIT(10)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN1	BIT(11)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_DHCP_EN1	BIT(12)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN1	BIT(13)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN1	BIT(14)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN2	BIT(16)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN2	BIT(17)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN2	BIT(18)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN2	BIT(19)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_DHCP_EN2	BIT(20)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN2	BIT(21)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN2	BIT(22)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN3	BIT(24)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN3	BIT(25)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN3	BIT(26)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN3	BIT(27)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_DHCP_EN3	BIT(28)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN3	BIT(29)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN3	BIT(30)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_FRAME_ACK_CTRL1		0x214
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN4	BIT(0)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN4	BIT(1)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN4	BIT(2)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN4	BIT(3)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_DHCP_EN4	BIT(4)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN4	BIT(5)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN4	BIT(6)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN5	BIT(8)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN5	BIT(9)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN5	BIT(10)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN5	BIT(11)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_DHCP_EN5	BIT(12)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN5	BIT(13)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN5	BIT(14)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN6	BIT(16)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN6	BIT(17)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN6	BIT(18)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN6	BIT(19)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_DHCP_EN6	BIT(20)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN6	BIT(21)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN6	BIT(22)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_V3_EN	BIT(24)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_PPPOE_EN	BIT(25)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_FRAME_ACK_CTRL(_i)		(0x210 + ((_i) / 4) * 0x4)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD	BIT(0)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN	BIT(1)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE	BIT(2)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_EAPOL		BIT(3)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_DHCP		BIT(4)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_ACK		BIT(5)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_ARP_REQ		BIT(6)
 | 
			
		||||
#define   AR8327_FRAME_ACK_CTRL_S(_i)		(((_i) % 4) * 8)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_PORT_VLAN0(_i)		(0x420 + (_i) * 0x8)
 | 
			
		||||
#define   AR8327_PORT_VLAN0_DEF_PRI_MASK	BITS(0, 3)
 | 
			
		||||
#define   AR8327_PORT_VLAN0_DEF_SVID		BITS(0, 12)
 | 
			
		||||
#define   AR8327_PORT_VLAN0_DEF_SVID_S		0
 | 
			
		||||
#define   AR8327_PORT_VLAN0_DEF_SPRI		BITS(13, 3)
 | 
			
		||||
#define   AR8327_PORT_VLAN0_DEF_SPRI_S		13
 | 
			
		||||
#define   AR8327_PORT_VLAN0_DEF_CVID		BITS(16, 12)
 | 
			
		||||
#define   AR8327_PORT_VLAN0_DEF_CVID_S		16
 | 
			
		||||
#define   AR8327_PORT_VLAN0_DEF_CPRI		BITS(29, 3)
 | 
			
		||||
#define   AR8327_PORT_VLAN0_DEF_CPRI_S		29
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_PORT_VLAN1(_i)		(0x424 + (_i) * 0x8)
 | 
			
		||||
#define   AR8327_PORT_VLAN1_VLAN_PRI_PROP	BIT(4)
 | 
			
		||||
#define   AR8327_PORT_VLAN1_PORT_VLAN_PROP	BIT(6)
 | 
			
		||||
#define   AR8327_PORT_VLAN1_OUT_MODE		BITS(12, 2)
 | 
			
		||||
#define   AR8327_PORT_VLAN1_OUT_MODE_S		12
 | 
			
		||||
#define   AR8327_PORT_VLAN1_OUT_MODE_UNMOD	0
 | 
			
		||||
#define   AR8327_PORT_VLAN1_OUT_MODE_UNTAG	1
 | 
			
		||||
#define   AR8327_PORT_VLAN1_OUT_MODE_TAG	2
 | 
			
		||||
#define   AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH	3
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_ATU_DATA0			0x600
 | 
			
		||||
#define   AR8327_ATU_ADDR0			BITS(0, 8)
 | 
			
		||||
#define   AR8327_ATU_ADDR0_S			0
 | 
			
		||||
#define   AR8327_ATU_ADDR1			BITS(8, 8)
 | 
			
		||||
#define   AR8327_ATU_ADDR1_S			8
 | 
			
		||||
#define   AR8327_ATU_ADDR2			BITS(16, 8)
 | 
			
		||||
#define   AR8327_ATU_ADDR2_S			16
 | 
			
		||||
#define   AR8327_ATU_ADDR3			BITS(24, 8)
 | 
			
		||||
#define   AR8327_ATU_ADDR3_S			24
 | 
			
		||||
#define AR8327_REG_ATU_DATA1			0x604
 | 
			
		||||
#define   AR8327_ATU_ADDR4			BITS(0, 8)
 | 
			
		||||
#define   AR8327_ATU_ADDR4_S			0
 | 
			
		||||
#define   AR8327_ATU_ADDR5			BITS(8, 8)
 | 
			
		||||
#define   AR8327_ATU_ADDR5_S			8
 | 
			
		||||
#define   AR8327_ATU_PORTS			BITS(16, 7)
 | 
			
		||||
#define   AR8327_ATU_PORTS_S			16
 | 
			
		||||
#define   AR8327_ATU_PORT0			BIT(16)
 | 
			
		||||
#define   AR8327_ATU_PORT1			BIT(17)
 | 
			
		||||
#define   AR8327_ATU_PORT2			BIT(18)
 | 
			
		||||
#define   AR8327_ATU_PORT3			BIT(19)
 | 
			
		||||
#define   AR8327_ATU_PORT4			BIT(20)
 | 
			
		||||
#define   AR8327_ATU_PORT5			BIT(21)
 | 
			
		||||
#define   AR8327_ATU_PORT6			BIT(22)
 | 
			
		||||
#define AR8327_REG_ATU_DATA2			0x608
 | 
			
		||||
#define   AR8327_ATU_STATUS			BITS(0, 4)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_ATU_FUNC			0x60c
 | 
			
		||||
#define   AR8327_ATU_FUNC_OP			BITS(0, 4)
 | 
			
		||||
#define   AR8327_ATU_FUNC_OP_NOOP		0x0
 | 
			
		||||
#define   AR8327_ATU_FUNC_OP_FLUSH		0x1
 | 
			
		||||
#define   AR8327_ATU_FUNC_OP_LOAD		0x2
 | 
			
		||||
#define   AR8327_ATU_FUNC_OP_PURGE		0x3
 | 
			
		||||
#define   AR8327_ATU_FUNC_OP_FLUSH_UNLOCKED	0x4
 | 
			
		||||
#define   AR8327_ATU_FUNC_OP_FLUSH_PORT		0x5
 | 
			
		||||
#define   AR8327_ATU_FUNC_OP_GET_NEXT		0x6
 | 
			
		||||
#define   AR8327_ATU_FUNC_OP_SEARCH_MAC		0x7
 | 
			
		||||
#define   AR8327_ATU_FUNC_OP_CHANGE_TRUNK	0x8
 | 
			
		||||
#define   AR8327_ATU_PORT_NUM			BITS(8, 4)
 | 
			
		||||
#define   AR8327_ATU_PORT_NUM_S			8
 | 
			
		||||
#define   AR8327_ATU_FUNC_BUSY			BIT(31)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_VTU_FUNC0			0x0610
 | 
			
		||||
#define   AR8327_VTU_FUNC0_EG_MODE		BITS(4, 14)
 | 
			
		||||
#define   AR8327_VTU_FUNC0_EG_MODE_S(_i)	(4 + (_i) * 2)
 | 
			
		||||
#define   AR8327_VTU_FUNC0_EG_MODE_KEEP		0
 | 
			
		||||
#define   AR8327_VTU_FUNC0_EG_MODE_UNTAG	1
 | 
			
		||||
#define   AR8327_VTU_FUNC0_EG_MODE_TAG		2
 | 
			
		||||
#define   AR8327_VTU_FUNC0_EG_MODE_NOT		3
 | 
			
		||||
#define   AR8327_VTU_FUNC0_IVL			BIT(19)
 | 
			
		||||
#define   AR8327_VTU_FUNC0_VALID		BIT(20)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_VTU_FUNC1			0x0614
 | 
			
		||||
#define   AR8327_VTU_FUNC1_OP			BITS(0, 3)
 | 
			
		||||
#define   AR8327_VTU_FUNC1_OP_NOOP		0
 | 
			
		||||
#define   AR8327_VTU_FUNC1_OP_FLUSH		1
 | 
			
		||||
#define   AR8327_VTU_FUNC1_OP_LOAD		2
 | 
			
		||||
#define   AR8327_VTU_FUNC1_OP_PURGE		3
 | 
			
		||||
#define   AR8327_VTU_FUNC1_OP_REMOVE_PORT	4
 | 
			
		||||
#define   AR8327_VTU_FUNC1_OP_GET_NEXT		5
 | 
			
		||||
#define   AR8327_VTU_FUNC1_OP_GET_ONE		6
 | 
			
		||||
#define   AR8327_VTU_FUNC1_FULL			BIT(4)
 | 
			
		||||
#define   AR8327_VTU_FUNC1_PORT			BIT(8, 4)
 | 
			
		||||
#define   AR8327_VTU_FUNC1_PORT_S		8
 | 
			
		||||
#define   AR8327_VTU_FUNC1_VID			BIT(16, 12)
 | 
			
		||||
#define   AR8327_VTU_FUNC1_VID_S		16
 | 
			
		||||
#define   AR8327_VTU_FUNC1_BUSY			BIT(31)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_ARL_CTRL			0x0618
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_FWD_CTRL0			0x620
 | 
			
		||||
#define   AR8327_FWD_CTRL0_CPU_PORT_EN		BIT(10)
 | 
			
		||||
#define   AR8327_FWD_CTRL0_MIRROR_PORT		BITS(4, 4)
 | 
			
		||||
#define   AR8327_FWD_CTRL0_MIRROR_PORT_S	4
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_FWD_CTRL1			0x624
 | 
			
		||||
#define   AR8327_FWD_CTRL1_UC_FLOOD		BITS(0, 7)
 | 
			
		||||
#define   AR8327_FWD_CTRL1_UC_FLOOD_S		0
 | 
			
		||||
#define   AR8327_FWD_CTRL1_MC_FLOOD		BITS(8, 7)
 | 
			
		||||
#define   AR8327_FWD_CTRL1_MC_FLOOD_S		8
 | 
			
		||||
#define   AR8327_FWD_CTRL1_BC_FLOOD		BITS(16, 7)
 | 
			
		||||
#define   AR8327_FWD_CTRL1_BC_FLOOD_S		16
 | 
			
		||||
#define   AR8327_FWD_CTRL1_IGMP			BITS(24, 7)
 | 
			
		||||
#define   AR8327_FWD_CTRL1_IGMP_S		24
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_PORT_LOOKUP(_i)		(0x660 + (_i) * 0xc)
 | 
			
		||||
#define   AR8327_PORT_LOOKUP_MEMBER		BITS(0, 7)
 | 
			
		||||
#define   AR8327_PORT_LOOKUP_IN_MODE		BITS(8, 2)
 | 
			
		||||
#define   AR8327_PORT_LOOKUP_IN_MODE_S		8
 | 
			
		||||
#define   AR8327_PORT_LOOKUP_STATE		BITS(16, 3)
 | 
			
		||||
#define   AR8327_PORT_LOOKUP_STATE_S		16
 | 
			
		||||
#define   AR8327_PORT_LOOKUP_LEARN		BIT(20)
 | 
			
		||||
#define   AR8327_PORT_LOOKUP_ING_MIRROR_EN	BIT(25)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_PORT_PRIO(_i)		(0x664 + (_i) * 0xc)
 | 
			
		||||
 | 
			
		||||
#define AR8327_REG_PORT_HOL_CTRL1(_i)		(0x974 + (_i) * 0x8)
 | 
			
		||||
#define   AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN	BIT(16)
 | 
			
		||||
 | 
			
		||||
#define AR8337_PAD_MAC06_EXCHANGE_EN		BIT(31)
 | 
			
		||||
 | 
			
		||||
#define AR8327_PHY_MODE_SEL			0x12
 | 
			
		||||
#define   AR8327_PHY_MODE_SEL_RGMII		BIT(3)
 | 
			
		||||
#define AR8327_PHY_TEST_CTRL			0x0
 | 
			
		||||
#define   AR8327_PHY_TEST_CTRL_RGMII_RX_DELAY	BIT(15)
 | 
			
		||||
#define AR8327_PHY_SYS_CTRL			0x5
 | 
			
		||||
#define   AR8327_PHY_SYS_CTRL_RGMII_TX_DELAY	BIT(8)
 | 
			
		||||
 | 
			
		||||
enum ar8327_led_pattern {
 | 
			
		||||
	AR8327_LED_PATTERN_OFF = 0,
 | 
			
		||||
	AR8327_LED_PATTERN_BLINK,
 | 
			
		||||
	AR8327_LED_PATTERN_ON,
 | 
			
		||||
	AR8327_LED_PATTERN_RULE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ar8327_led_entry {
 | 
			
		||||
	unsigned reg;
 | 
			
		||||
	unsigned shift;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ar8327_led {
 | 
			
		||||
	struct led_classdev cdev;
 | 
			
		||||
	struct ar8xxx_priv *sw_priv;
 | 
			
		||||
 | 
			
		||||
	char *name;
 | 
			
		||||
	bool active_low;
 | 
			
		||||
	u8 led_num;
 | 
			
		||||
	enum ar8327_led_mode mode;
 | 
			
		||||
 | 
			
		||||
	struct mutex mutex;
 | 
			
		||||
	spinlock_t lock;
 | 
			
		||||
	struct work_struct led_work;
 | 
			
		||||
	bool enable_hw_mode;
 | 
			
		||||
	enum ar8327_led_pattern pattern;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ar8327_data {
 | 
			
		||||
	u32 port0_status;
 | 
			
		||||
	u32 port6_status;
 | 
			
		||||
 | 
			
		||||
	struct ar8327_led **leds;
 | 
			
		||||
	unsigned int num_leds;
 | 
			
		||||
 | 
			
		||||
	/* all fields below are cleared on reset */
 | 
			
		||||
	bool eee[AR8XXX_NUM_PHYS];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										37
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/Kconfig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/Kconfig
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
menuconfig SWCONFIG_B53
 | 
			
		||||
	tristate "Broadcom bcm53xx managed switch support"
 | 
			
		||||
	depends on SWCONFIG
 | 
			
		||||
	help
 | 
			
		||||
	  This driver adds support for Broadcom managed switch chips. It supports
 | 
			
		||||
	  BCM5325E, BCM5365, BCM539x, BCM53115 and BCM53125 as well as BCM63XX
 | 
			
		||||
	  integrated switches.
 | 
			
		||||
 | 
			
		||||
config SWCONFIG_B53_SPI_DRIVER
 | 
			
		||||
	tristate "B53 SPI connected switch driver"
 | 
			
		||||
	depends on SWCONFIG_B53 && SPI
 | 
			
		||||
	help
 | 
			
		||||
	  Select to enable support for registering switches configured through SPI.
 | 
			
		||||
 | 
			
		||||
config SWCONFIG_B53_PHY_DRIVER
 | 
			
		||||
	tristate "B53 MDIO connected switch driver"
 | 
			
		||||
	depends on SWCONFIG_B53
 | 
			
		||||
	select SWCONFIG_B53_PHY_FIXUP
 | 
			
		||||
	help
 | 
			
		||||
	  Select to enable support for registering switches configured through MDIO.
 | 
			
		||||
 | 
			
		||||
config SWCONFIG_B53_MMAP_DRIVER
 | 
			
		||||
	tristate "B53 MMAP connected switch driver"
 | 
			
		||||
	depends on SWCONFIG_B53
 | 
			
		||||
	help
 | 
			
		||||
	  Select to enable support for memory-mapped switches like the BCM63XX
 | 
			
		||||
	  integrated switches.
 | 
			
		||||
 | 
			
		||||
config SWCONFIG_B53_SRAB_DRIVER
 | 
			
		||||
	tristate "B53 SRAB connected switch driver"
 | 
			
		||||
	depends on SWCONFIG_B53
 | 
			
		||||
	help
 | 
			
		||||
	  Select to enable support for memory-mapped Switch Register Access
 | 
			
		||||
	  Bridge Registers (SRAB) like it is found on the BCM53010
 | 
			
		||||
 | 
			
		||||
config SWCONFIG_B53_PHY_FIXUP
 | 
			
		||||
	bool
 | 
			
		||||
							
								
								
									
										10
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/Makefile
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
obj-$(CONFIG_SWCONFIG_B53)		+= b53_common.o
 | 
			
		||||
 | 
			
		||||
obj-$(CONFIG_SWCONFIG_B53_PHY_FIXUP)	+= b53_phy_fixup.o
 | 
			
		||||
 | 
			
		||||
obj-$(CONFIG_SWCONFIG_B53_MMAP_DRIVER)	+= b53_mmap.o
 | 
			
		||||
obj-$(CONFIG_SWCONFIG_B53_SRAB_DRIVER)	+= b53_srab.o
 | 
			
		||||
obj-$(CONFIG_SWCONFIG_B53_PHY_DRIVER)	+= b53_mdio.o
 | 
			
		||||
obj-$(CONFIG_SWCONFIG_B53_SPI_DRIVER)	+= b53_spi.o
 | 
			
		||||
 | 
			
		||||
ccflags-y				+= -Werror
 | 
			
		||||
							
								
								
									
										1724
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1724
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										436
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										436
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,436 @@
 | 
			
		|||
/*
 | 
			
		||||
 * B53 register access through MII registers
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and/or distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/phy.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
 | 
			
		||||
#include "b53_priv.h"
 | 
			
		||||
 | 
			
		||||
#define B53_PSEUDO_PHY	0x1e /* Register Access Pseudo PHY */
 | 
			
		||||
 | 
			
		||||
/* MII registers */
 | 
			
		||||
#define REG_MII_PAGE    0x10    /* MII Page register */
 | 
			
		||||
#define REG_MII_ADDR    0x11    /* MII Address register */
 | 
			
		||||
#define REG_MII_DATA0   0x18    /* MII Data register 0 */
 | 
			
		||||
#define REG_MII_DATA1   0x19    /* MII Data register 1 */
 | 
			
		||||
#define REG_MII_DATA2   0x1a    /* MII Data register 2 */
 | 
			
		||||
#define REG_MII_DATA3   0x1b    /* MII Data register 3 */
 | 
			
		||||
 | 
			
		||||
#define REG_MII_PAGE_ENABLE     BIT(0)
 | 
			
		||||
#define REG_MII_ADDR_WRITE      BIT(0)
 | 
			
		||||
#define REG_MII_ADDR_READ       BIT(1)
 | 
			
		||||
 | 
			
		||||
static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	u16 v;
 | 
			
		||||
	int ret;
 | 
			
		||||
	struct mii_bus *bus = dev->priv;
 | 
			
		||||
 | 
			
		||||
	if (dev->current_page != page) {
 | 
			
		||||
		/* set page number */
 | 
			
		||||
		v = (page << 8) | REG_MII_PAGE_ENABLE;
 | 
			
		||||
		ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_PAGE, v);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			return ret;
 | 
			
		||||
		dev->current_page = page;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* set register address */
 | 
			
		||||
	v = (reg << 8) | op;
 | 
			
		||||
	ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_ADDR, v);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	/* check if operation completed */
 | 
			
		||||
	for (i = 0; i < 5; ++i) {
 | 
			
		||||
		v = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_ADDR);
 | 
			
		||||
		if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ)))
 | 
			
		||||
			break;
 | 
			
		||||
		usleep_range(10, 100);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(i == 5))
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = dev->priv;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	*val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0) & 0xff;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = dev->priv;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	*val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = dev->priv;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	*val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0);
 | 
			
		||||
	*val |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA1) << 16;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = dev->priv;
 | 
			
		||||
	u64 temp = 0;
 | 
			
		||||
	int i;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	for (i = 2; i >= 0; i--) {
 | 
			
		||||
		temp <<= 16;
 | 
			
		||||
		temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*val = temp;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = dev->priv;
 | 
			
		||||
	u64 temp = 0;
 | 
			
		||||
	int i;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	for (i = 3; i >= 0; i--) {
 | 
			
		||||
		temp <<= 16;
 | 
			
		||||
		temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*val = temp;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = dev->priv;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
			     u16 value)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = dev->priv;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
				    u32 value)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = dev->priv;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	u32 temp = value;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < 2; i++) {
 | 
			
		||||
		int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
 | 
			
		||||
				    temp & 0xffff);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			return ret;
 | 
			
		||||
		temp >>= 16;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
				    u64 value)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = dev->priv;
 | 
			
		||||
	unsigned i;
 | 
			
		||||
	u64 temp = value;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < 3; i++) {
 | 
			
		||||
		int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
 | 
			
		||||
				    temp & 0xffff);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			return ret;
 | 
			
		||||
		temp >>= 16;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
			     u64 value)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = dev->priv;
 | 
			
		||||
	unsigned i;
 | 
			
		||||
	u64 temp = value;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < 4; i++) {
 | 
			
		||||
		int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
 | 
			
		||||
				    temp & 0xffff);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			return ret;
 | 
			
		||||
		temp >>= 16;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mdio_phy_read16(struct b53_device *dev, int addr, u8 reg,
 | 
			
		||||
			       u16 *value)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = dev->priv;
 | 
			
		||||
 | 
			
		||||
	*value = mdiobus_read(bus, addr, reg);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mdio_phy_write16(struct b53_device *dev, int addr, u8 reg,
 | 
			
		||||
				u16 value)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = dev->priv;
 | 
			
		||||
 | 
			
		||||
	return mdiobus_write(bus, addr, reg, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct b53_io_ops b53_mdio_ops = {
 | 
			
		||||
	.read8 = b53_mdio_read8,
 | 
			
		||||
	.read16 = b53_mdio_read16,
 | 
			
		||||
	.read32 = b53_mdio_read32,
 | 
			
		||||
	.read48 = b53_mdio_read48,
 | 
			
		||||
	.read64 = b53_mdio_read64,
 | 
			
		||||
	.write8 = b53_mdio_write8,
 | 
			
		||||
	.write16 = b53_mdio_write16,
 | 
			
		||||
	.write32 = b53_mdio_write32,
 | 
			
		||||
	.write48 = b53_mdio_write48,
 | 
			
		||||
	.write64 = b53_mdio_write64,
 | 
			
		||||
	.phy_read16 = b53_mdio_phy_read16,
 | 
			
		||||
	.phy_write16 = b53_mdio_phy_write16,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int b53_phy_probe(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	struct b53_device *dev;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/* allow the generic phy driver to take over */
 | 
			
		||||
	if (phydev->mdio.addr != B53_PSEUDO_PHY && phydev->mdio.addr != 0)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	dev = b53_swconfig_switch_alloc(&phydev->mdio.dev, &b53_mdio_ops, phydev->mdio.bus);
 | 
			
		||||
	if (!dev)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	dev->current_page = 0xff;
 | 
			
		||||
	dev->priv = phydev->mdio.bus;
 | 
			
		||||
	dev->ops = &b53_mdio_ops;
 | 
			
		||||
	dev->pdata = NULL;
 | 
			
		||||
	mutex_init(&dev->reg_mutex);
 | 
			
		||||
 | 
			
		||||
	ret = b53_swconfig_switch_detect(dev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	linkmode_zero(phydev->supported);
 | 
			
		||||
	if (is5325(dev) || is5365(dev))
 | 
			
		||||
		linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, phydev->supported);
 | 
			
		||||
	else
 | 
			
		||||
		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, phydev->supported);
 | 
			
		||||
 | 
			
		||||
	linkmode_copy(phydev->advertising, phydev->supported);
 | 
			
		||||
 | 
			
		||||
	ret = b53_swconfig_switch_register(dev);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		dev_err(dev->dev, "failed to register switch: %i\n", ret);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	phydev->priv = dev;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_phy_config_init(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	struct b53_device *dev = phydev->priv;
 | 
			
		||||
 | 
			
		||||
	/* we don't use page 0xff, so force a page set */
 | 
			
		||||
	dev->current_page = 0xff;
 | 
			
		||||
	/* force the ethX as alias */
 | 
			
		||||
	dev->sw_dev.alias = phydev->attached_dev->name;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void b53_phy_remove(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	struct b53_device *priv = phydev->priv;
 | 
			
		||||
 | 
			
		||||
	if (!priv)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	b53_switch_remove(priv);
 | 
			
		||||
 | 
			
		||||
	phydev->priv = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_phy_config_aneg(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_phy_read_status(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	struct b53_device *priv = phydev->priv;
 | 
			
		||||
 | 
			
		||||
	if (is5325(priv) || is5365(priv))
 | 
			
		||||
		phydev->speed = 100;
 | 
			
		||||
	else
 | 
			
		||||
		phydev->speed = 1000;
 | 
			
		||||
 | 
			
		||||
	phydev->duplex = DUPLEX_FULL;
 | 
			
		||||
	phydev->link = 1;
 | 
			
		||||
	phydev->state = PHY_RUNNING;
 | 
			
		||||
 | 
			
		||||
	netif_carrier_on(phydev->attached_dev);
 | 
			
		||||
	phydev->adjust_link(phydev->attached_dev);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* BCM5325, BCM539x */
 | 
			
		||||
static struct phy_driver b53_phy_driver_id1 = {
 | 
			
		||||
	.phy_id		= 0x0143bc00,
 | 
			
		||||
	.name		= "Broadcom B53 (1)",
 | 
			
		||||
	.phy_id_mask	= 0x1ffffc00,
 | 
			
		||||
	.features	= 0,
 | 
			
		||||
	.probe		= b53_phy_probe,
 | 
			
		||||
	.remove		= b53_phy_remove,
 | 
			
		||||
	.config_aneg	= b53_phy_config_aneg,
 | 
			
		||||
	.config_init	= b53_phy_config_init,
 | 
			
		||||
	.read_status	= b53_phy_read_status,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* BCM53125, BCM53128 */
 | 
			
		||||
static struct phy_driver b53_phy_driver_id2 = {
 | 
			
		||||
	.phy_id		= 0x03625c00,
 | 
			
		||||
	.name		= "Broadcom B53 (2)",
 | 
			
		||||
	.phy_id_mask	= 0x1ffffc00,
 | 
			
		||||
	.features	= 0,
 | 
			
		||||
	.probe		= b53_phy_probe,
 | 
			
		||||
	.remove		= b53_phy_remove,
 | 
			
		||||
	.config_aneg	= b53_phy_config_aneg,
 | 
			
		||||
	.config_init	= b53_phy_config_init,
 | 
			
		||||
	.read_status	= b53_phy_read_status,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* BCM5365 */
 | 
			
		||||
static struct phy_driver b53_phy_driver_id3 = {
 | 
			
		||||
	.phy_id		= 0x00406300,
 | 
			
		||||
	.name		= "Broadcom B53 (3)",
 | 
			
		||||
	.phy_id_mask	= 0x1fffff00,
 | 
			
		||||
	.features	= 0,
 | 
			
		||||
	.probe		= b53_phy_probe,
 | 
			
		||||
	.remove		= b53_phy_remove,
 | 
			
		||||
	.config_aneg	= b53_phy_config_aneg,
 | 
			
		||||
	.config_init	= b53_phy_config_init,
 | 
			
		||||
	.read_status	= b53_phy_read_status,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int __init b53_phy_driver_register(void)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = phy_driver_register(&b53_phy_driver_id1, THIS_MODULE);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	ret = phy_driver_register(&b53_phy_driver_id2, THIS_MODULE);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err1;
 | 
			
		||||
 | 
			
		||||
	ret = phy_driver_register(&b53_phy_driver_id3, THIS_MODULE);
 | 
			
		||||
	if (!ret)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	phy_driver_unregister(&b53_phy_driver_id2);
 | 
			
		||||
err1:
 | 
			
		||||
	phy_driver_unregister(&b53_phy_driver_id1);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void __exit b53_phy_driver_unregister(void)
 | 
			
		||||
{
 | 
			
		||||
	phy_driver_unregister(&b53_phy_driver_id3);
 | 
			
		||||
	phy_driver_unregister(&b53_phy_driver_id2);
 | 
			
		||||
	phy_driver_unregister(&b53_phy_driver_id1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module_init(b53_phy_driver_register);
 | 
			
		||||
module_exit(b53_phy_driver_unregister);
 | 
			
		||||
 | 
			
		||||
MODULE_DESCRIPTION("B53 MDIO access driver");
 | 
			
		||||
MODULE_LICENSE("Dual BSD/GPL");
 | 
			
		||||
							
								
								
									
										248
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,248 @@
 | 
			
		|||
/*
 | 
			
		||||
 * B53 register access through memory mapped registers
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and/or distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/platform_device.h>
 | 
			
		||||
#include <linux/platform_data/b53.h>
 | 
			
		||||
#include <linux/version.h>
 | 
			
		||||
 | 
			
		||||
#include "b53_priv.h"
 | 
			
		||||
 | 
			
		||||
static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
 | 
			
		||||
	*val = readb(regs + (page << 8) + reg);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(reg % 2))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (dev->pdata && dev->pdata->big_endian)
 | 
			
		||||
		*val = readw_be(regs + (page << 8) + reg);
 | 
			
		||||
	else
 | 
			
		||||
		*val = readw(regs + (page << 8) + reg);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(reg % 4))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (dev->pdata && dev->pdata->big_endian)
 | 
			
		||||
		*val = readl_be(regs + (page << 8) + reg);
 | 
			
		||||
	else
 | 
			
		||||
		*val = readl(regs + (page << 8) + reg);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
 | 
			
		||||
{
 | 
			
		||||
	if (WARN_ON(reg % 2))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (reg % 4) {
 | 
			
		||||
		u16 lo;
 | 
			
		||||
		u32 hi;
 | 
			
		||||
 | 
			
		||||
		b53_mmap_read16(dev, page, reg, &lo);
 | 
			
		||||
		b53_mmap_read32(dev, page, reg + 2, &hi);
 | 
			
		||||
 | 
			
		||||
		*val = ((u64)hi << 16) | lo;
 | 
			
		||||
	} else {
 | 
			
		||||
		u32 lo;
 | 
			
		||||
		u16 hi;
 | 
			
		||||
 | 
			
		||||
		b53_mmap_read32(dev, page, reg, &lo);
 | 
			
		||||
		b53_mmap_read16(dev, page, reg + 4, &hi);
 | 
			
		||||
 | 
			
		||||
		*val = ((u64)hi << 32) | lo;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
 | 
			
		||||
{
 | 
			
		||||
	u32 hi, lo;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(reg % 4))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	b53_mmap_read32(dev, page, reg, &lo);
 | 
			
		||||
	b53_mmap_read32(dev, page, reg + 4, &hi);
 | 
			
		||||
 | 
			
		||||
	*val = ((u64)hi << 32) | lo;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
 | 
			
		||||
	writeb(value, regs + (page << 8) + reg);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
			     u16 value)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(reg % 2))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (dev->pdata && dev->pdata->big_endian)
 | 
			
		||||
		writew_be(value, regs + (page << 8) + reg);
 | 
			
		||||
	else
 | 
			
		||||
		writew(value, regs + (page << 8) + reg);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
				    u32 value)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(reg % 4))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (dev->pdata && dev->pdata->big_endian)
 | 
			
		||||
		writel_be(value, regs + (page << 8) + reg);
 | 
			
		||||
	else
 | 
			
		||||
		writel(value, regs + (page << 8) + reg);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
				    u64 value)
 | 
			
		||||
{
 | 
			
		||||
	if (WARN_ON(reg % 2))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (reg % 4) {
 | 
			
		||||
		u32 hi = (u32)(value >> 16);
 | 
			
		||||
		u16 lo = (u16)value;
 | 
			
		||||
 | 
			
		||||
		b53_mmap_write16(dev, page, reg, lo);
 | 
			
		||||
		b53_mmap_write32(dev, page, reg + 2, hi);
 | 
			
		||||
	} else {
 | 
			
		||||
		u16 hi = (u16)(value >> 32);
 | 
			
		||||
		u32 lo = (u32)value;
 | 
			
		||||
 | 
			
		||||
		b53_mmap_write32(dev, page, reg, lo);
 | 
			
		||||
		b53_mmap_write16(dev, page, reg + 4, hi);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
			     u64 value)
 | 
			
		||||
{
 | 
			
		||||
	u32 hi, lo;
 | 
			
		||||
 | 
			
		||||
	hi = (u32)(value >> 32);
 | 
			
		||||
	lo = (u32)value;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(reg % 4))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	b53_mmap_write32(dev, page, reg, lo);
 | 
			
		||||
	b53_mmap_write32(dev, page, reg + 4, hi);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct b53_io_ops b53_mmap_ops = {
 | 
			
		||||
	.read8 = b53_mmap_read8,
 | 
			
		||||
	.read16 = b53_mmap_read16,
 | 
			
		||||
	.read32 = b53_mmap_read32,
 | 
			
		||||
	.read48 = b53_mmap_read48,
 | 
			
		||||
	.read64 = b53_mmap_read64,
 | 
			
		||||
	.write8 = b53_mmap_write8,
 | 
			
		||||
	.write16 = b53_mmap_write16,
 | 
			
		||||
	.write32 = b53_mmap_write32,
 | 
			
		||||
	.write48 = b53_mmap_write48,
 | 
			
		||||
	.write64 = b53_mmap_write64,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int b53_mmap_probe(struct platform_device *pdev)
 | 
			
		||||
{
 | 
			
		||||
	struct b53_platform_data *pdata = pdev->dev.platform_data;
 | 
			
		||||
	struct b53_device *dev;
 | 
			
		||||
 | 
			
		||||
	if (!pdata)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	dev = b53_swconfig_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs);
 | 
			
		||||
	if (!dev)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	if (pdata)
 | 
			
		||||
		dev->pdata = pdata;
 | 
			
		||||
 | 
			
		||||
	platform_set_drvdata(pdev, dev);
 | 
			
		||||
 | 
			
		||||
	return b53_swconfig_switch_register(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,11,0)
 | 
			
		||||
static int b53_mmap_remove(struct platform_device *pdev)
 | 
			
		||||
#else
 | 
			
		||||
static void b53_mmap_remove(struct platform_device *pdev)
 | 
			
		||||
#endif
 | 
			
		||||
{
 | 
			
		||||
	struct b53_device *dev = platform_get_drvdata(pdev);
 | 
			
		||||
 | 
			
		||||
	if (dev)
 | 
			
		||||
		b53_switch_remove(dev);
 | 
			
		||||
 | 
			
		||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,11,0)
 | 
			
		||||
	return 0;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct platform_driver b53_mmap_driver = {
 | 
			
		||||
	.probe = b53_mmap_probe,
 | 
			
		||||
	.remove = b53_mmap_remove,
 | 
			
		||||
	.driver = {
 | 
			
		||||
		.name = "b53-switch",
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module_platform_driver(b53_mmap_driver);
 | 
			
		||||
MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
 | 
			
		||||
MODULE_DESCRIPTION("B53 MMAP access driver");
 | 
			
		||||
MODULE_LICENSE("Dual BSD/GPL");
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
/*
 | 
			
		||||
 * B53 PHY Fixup call
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and/or distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/phy.h>
 | 
			
		||||
 | 
			
		||||
#define B53_PSEUDO_PHY	0x1e /* Register Access Pseudo PHY */
 | 
			
		||||
 | 
			
		||||
#define B53_BRCM_OUI_1	0x0143bc00
 | 
			
		||||
#define B53_BRCM_OUI_2	0x03625c00
 | 
			
		||||
#define B53_BRCM_OUI_3	0x00406300
 | 
			
		||||
 | 
			
		||||
static int b53_phy_fixup(struct phy_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = dev->mdio.bus;
 | 
			
		||||
	u32 phy_id;
 | 
			
		||||
 | 
			
		||||
	if (dev->mdio.addr != B53_PSEUDO_PHY)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	/* read the first port's id */
 | 
			
		||||
	phy_id = mdiobus_read(bus, 0, 2) << 16;
 | 
			
		||||
	phy_id |= mdiobus_read(bus, 0, 3);
 | 
			
		||||
 | 
			
		||||
	if ((phy_id & 0xfffffc00) == B53_BRCM_OUI_1 ||
 | 
			
		||||
	    (phy_id & 0xfffffc00) == B53_BRCM_OUI_2 ||
 | 
			
		||||
	    (phy_id & 0xffffff00) == B53_BRCM_OUI_3) {
 | 
			
		||||
		dev->phy_id = phy_id;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int __init b53_phy_fixup_register(void)
 | 
			
		||||
{
 | 
			
		||||
	return phy_register_fixup_for_id(PHY_ANY_ID, b53_phy_fixup);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
subsys_initcall(b53_phy_fixup_register);
 | 
			
		||||
							
								
								
									
										336
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,336 @@
 | 
			
		|||
/*
 | 
			
		||||
 * B53 common definitions
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and/or distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __B53_PRIV_H
 | 
			
		||||
#define __B53_PRIV_H
 | 
			
		||||
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/mutex.h>
 | 
			
		||||
#include <linux/switch.h>
 | 
			
		||||
 | 
			
		||||
struct b53_device;
 | 
			
		||||
 | 
			
		||||
struct b53_io_ops {
 | 
			
		||||
	int (*read8)(struct b53_device *dev, u8 page, u8 reg, u8 *value);
 | 
			
		||||
	int (*read16)(struct b53_device *dev, u8 page, u8 reg, u16 *value);
 | 
			
		||||
	int (*read32)(struct b53_device *dev, u8 page, u8 reg, u32 *value);
 | 
			
		||||
	int (*read48)(struct b53_device *dev, u8 page, u8 reg, u64 *value);
 | 
			
		||||
	int (*read64)(struct b53_device *dev, u8 page, u8 reg, u64 *value);
 | 
			
		||||
	int (*write8)(struct b53_device *dev, u8 page, u8 reg, u8 value);
 | 
			
		||||
	int (*write16)(struct b53_device *dev, u8 page, u8 reg, u16 value);
 | 
			
		||||
	int (*write32)(struct b53_device *dev, u8 page, u8 reg, u32 value);
 | 
			
		||||
	int (*write48)(struct b53_device *dev, u8 page, u8 reg, u64 value);
 | 
			
		||||
	int (*write64)(struct b53_device *dev, u8 page, u8 reg, u64 value);
 | 
			
		||||
	int (*phy_read16)(struct b53_device *dev, int addr, u8 reg, u16 *value);
 | 
			
		||||
	int (*phy_write16)(struct b53_device *dev, int addr, u8 reg, u16 value);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	BCM5325_DEVICE_ID = 0x25,
 | 
			
		||||
	BCM5365_DEVICE_ID = 0x65,
 | 
			
		||||
	BCM5395_DEVICE_ID = 0x95,
 | 
			
		||||
	BCM5397_DEVICE_ID = 0x97,
 | 
			
		||||
	BCM5398_DEVICE_ID = 0x98,
 | 
			
		||||
	BCM53115_DEVICE_ID = 0x53115,
 | 
			
		||||
	BCM53125_DEVICE_ID = 0x53125,
 | 
			
		||||
	BCM53128_DEVICE_ID = 0x53128,
 | 
			
		||||
	BCM63XX_DEVICE_ID = 0x6300,
 | 
			
		||||
	BCM53010_DEVICE_ID = 0x53010,
 | 
			
		||||
	BCM53011_DEVICE_ID = 0x53011,
 | 
			
		||||
	BCM53012_DEVICE_ID = 0x53012,
 | 
			
		||||
	BCM53018_DEVICE_ID = 0x53018,
 | 
			
		||||
	BCM53019_DEVICE_ID = 0x53019,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define B53_N_PORTS	9
 | 
			
		||||
#define B53_N_PORTS_25	6
 | 
			
		||||
 | 
			
		||||
struct b53_vlan {
 | 
			
		||||
	unsigned int	members:B53_N_PORTS;
 | 
			
		||||
	unsigned int	untag:B53_N_PORTS;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct b53_port {
 | 
			
		||||
	unsigned int	pvid:12;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct b53_device {
 | 
			
		||||
	struct switch_dev sw_dev;
 | 
			
		||||
	struct b53_platform_data *pdata;
 | 
			
		||||
 | 
			
		||||
	struct mutex reg_mutex;
 | 
			
		||||
	const struct b53_io_ops *ops;
 | 
			
		||||
 | 
			
		||||
	/* chip specific data */
 | 
			
		||||
	u32 chip_id;
 | 
			
		||||
	u8 core_rev;
 | 
			
		||||
	u8 vta_regs[3];
 | 
			
		||||
	u8 duplex_reg;
 | 
			
		||||
	u8 jumbo_pm_reg;
 | 
			
		||||
	u8 jumbo_size_reg;
 | 
			
		||||
	int reset_gpio;
 | 
			
		||||
 | 
			
		||||
	/* used ports mask */
 | 
			
		||||
	u16 enabled_ports;
 | 
			
		||||
 | 
			
		||||
	/* connect specific data */
 | 
			
		||||
	u8 current_page;
 | 
			
		||||
	struct device *dev;
 | 
			
		||||
	void *priv;
 | 
			
		||||
 | 
			
		||||
	/* run time configuration */
 | 
			
		||||
	unsigned enable_vlan:1;
 | 
			
		||||
	unsigned enable_jumbo:1;
 | 
			
		||||
	unsigned allow_vid_4095:1;
 | 
			
		||||
 | 
			
		||||
	struct b53_port *ports;
 | 
			
		||||
	struct b53_vlan *vlans;
 | 
			
		||||
 | 
			
		||||
	char *buf;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define b53_for_each_port(dev, i) \
 | 
			
		||||
	for (i = 0; i < B53_N_PORTS; i++) \
 | 
			
		||||
		if (dev->enabled_ports & BIT(i))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static inline int is5325(struct b53_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return dev->chip_id == BCM5325_DEVICE_ID;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int is5365(struct b53_device *dev)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_BCM47XX
 | 
			
		||||
	return dev->chip_id == BCM5365_DEVICE_ID;
 | 
			
		||||
#else
 | 
			
		||||
	return 0;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int is5397_98(struct b53_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return dev->chip_id == BCM5397_DEVICE_ID ||
 | 
			
		||||
		dev->chip_id == BCM5398_DEVICE_ID;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int is539x(struct b53_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return dev->chip_id == BCM5395_DEVICE_ID ||
 | 
			
		||||
		dev->chip_id == BCM5397_DEVICE_ID ||
 | 
			
		||||
		dev->chip_id == BCM5398_DEVICE_ID;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int is531x5(struct b53_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return dev->chip_id == BCM53115_DEVICE_ID ||
 | 
			
		||||
		dev->chip_id == BCM53125_DEVICE_ID ||
 | 
			
		||||
		dev->chip_id == BCM53128_DEVICE_ID;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int is63xx(struct b53_device *dev)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_BCM63XX
 | 
			
		||||
	return dev->chip_id == BCM63XX_DEVICE_ID;
 | 
			
		||||
#else
 | 
			
		||||
	return 0;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int is5301x(struct b53_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return dev->chip_id == BCM53010_DEVICE_ID ||
 | 
			
		||||
		dev->chip_id == BCM53011_DEVICE_ID ||
 | 
			
		||||
		dev->chip_id == BCM53012_DEVICE_ID ||
 | 
			
		||||
		dev->chip_id == BCM53018_DEVICE_ID ||
 | 
			
		||||
		dev->chip_id == BCM53019_DEVICE_ID;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define B53_CPU_PORT_25	5
 | 
			
		||||
#define B53_CPU_PORT	8
 | 
			
		||||
 | 
			
		||||
static inline int is_cpu_port(struct b53_device *dev, int port)
 | 
			
		||||
{
 | 
			
		||||
	return dev->sw_dev.cpu_port == port;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int is_imp_port(struct b53_device *dev, int port)
 | 
			
		||||
{
 | 
			
		||||
	if (is5325(dev) || is5365(dev))
 | 
			
		||||
		return port == B53_CPU_PORT_25;
 | 
			
		||||
	else
 | 
			
		||||
		return port == B53_CPU_PORT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct b53_device *sw_to_b53(struct switch_dev *sw)
 | 
			
		||||
{
 | 
			
		||||
	return container_of(sw, struct b53_device, sw_dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct b53_device *b53_swconfig_switch_alloc(struct device *base, struct b53_io_ops *ops,
 | 
			
		||||
					     void *priv);
 | 
			
		||||
 | 
			
		||||
int b53_swconfig_switch_detect(struct b53_device *dev);
 | 
			
		||||
 | 
			
		||||
int b53_swconfig_switch_register(struct b53_device *dev);
 | 
			
		||||
 | 
			
		||||
static inline void b53_switch_remove(struct b53_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	unregister_switch(&dev->sw_dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int b53_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&dev->reg_mutex);
 | 
			
		||||
	ret = dev->ops->read8(dev, page, reg, val);
 | 
			
		||||
	mutex_unlock(&dev->reg_mutex);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int b53_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&dev->reg_mutex);
 | 
			
		||||
	ret = dev->ops->read16(dev, page, reg, val);
 | 
			
		||||
	mutex_unlock(&dev->reg_mutex);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int b53_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&dev->reg_mutex);
 | 
			
		||||
	ret = dev->ops->read32(dev, page, reg, val);
 | 
			
		||||
	mutex_unlock(&dev->reg_mutex);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int b53_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&dev->reg_mutex);
 | 
			
		||||
	ret = dev->ops->read48(dev, page, reg, val);
 | 
			
		||||
	mutex_unlock(&dev->reg_mutex);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int b53_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&dev->reg_mutex);
 | 
			
		||||
	ret = dev->ops->read64(dev, page, reg, val);
 | 
			
		||||
	mutex_unlock(&dev->reg_mutex);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int b53_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&dev->reg_mutex);
 | 
			
		||||
	ret = dev->ops->write8(dev, page, reg, value);
 | 
			
		||||
	mutex_unlock(&dev->reg_mutex);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int b53_write16(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
			      u16 value)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&dev->reg_mutex);
 | 
			
		||||
	ret = dev->ops->write16(dev, page, reg, value);
 | 
			
		||||
	mutex_unlock(&dev->reg_mutex);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int b53_write32(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
			      u32 value)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&dev->reg_mutex);
 | 
			
		||||
	ret = dev->ops->write32(dev, page, reg, value);
 | 
			
		||||
	mutex_unlock(&dev->reg_mutex);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int b53_write48(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
			      u64 value)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&dev->reg_mutex);
 | 
			
		||||
	ret = dev->ops->write48(dev, page, reg, value);
 | 
			
		||||
	mutex_unlock(&dev->reg_mutex);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int b53_write64(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
			       u64 value)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&dev->reg_mutex);
 | 
			
		||||
	ret = dev->ops->write64(dev, page, reg, value);
 | 
			
		||||
	mutex_unlock(&dev->reg_mutex);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_BCM47XX
 | 
			
		||||
#include <bcm47xx_board.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <linux/version.h>
 | 
			
		||||
#include <linux/bcm47xx_nvram.h>
 | 
			
		||||
 | 
			
		||||
static inline int b53_switch_get_reset_gpio(struct b53_device *dev)
 | 
			
		||||
{
 | 
			
		||||
#ifdef CONFIG_BCM47XX
 | 
			
		||||
	enum bcm47xx_board board = bcm47xx_board_get();
 | 
			
		||||
 | 
			
		||||
	switch (board) {
 | 
			
		||||
	case BCM47XX_BOARD_LINKSYS_WRT300NV11:
 | 
			
		||||
	case BCM47XX_BOARD_LINKSYS_WRT310NV1:
 | 
			
		||||
		return 8;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return bcm47xx_nvram_gpio_pin("robo_reset");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										348
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										348
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,348 @@
 | 
			
		|||
/*
 | 
			
		||||
 * B53 register definitions
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2004 Broadcom Corporation
 | 
			
		||||
 * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and/or distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __B53_REGS_H
 | 
			
		||||
#define __B53_REGS_H
 | 
			
		||||
 | 
			
		||||
/* Management Port (SMP) Page offsets */
 | 
			
		||||
#define B53_CTRL_PAGE			0x00 /* Control */
 | 
			
		||||
#define B53_STAT_PAGE			0x01 /* Status */
 | 
			
		||||
#define B53_MGMT_PAGE			0x02 /* Management Mode */
 | 
			
		||||
#define B53_MIB_AC_PAGE			0x03 /* MIB Autocast */
 | 
			
		||||
#define B53_ARLCTRL_PAGE		0x04 /* ARL Control */
 | 
			
		||||
#define B53_ARLIO_PAGE			0x05 /* ARL Access */
 | 
			
		||||
#define B53_FRAMEBUF_PAGE		0x06 /* Management frame access */
 | 
			
		||||
#define B53_MEM_ACCESS_PAGE		0x08 /* Memory access */
 | 
			
		||||
 | 
			
		||||
/* PHY Registers */
 | 
			
		||||
#define B53_PORT_MII_PAGE(i)		(0x10 + (i)) /* Port i MII Registers */
 | 
			
		||||
#define B53_IM_PORT_PAGE		0x18 /* Inverse MII Port (to EMAC) */
 | 
			
		||||
#define B53_ALL_PORT_PAGE		0x19 /* All ports MII (broadcast) */
 | 
			
		||||
 | 
			
		||||
/* MIB registers */
 | 
			
		||||
#define B53_MIB_PAGE(i)			(0x20 + (i))
 | 
			
		||||
 | 
			
		||||
/* Quality of Service (QoS) Registers */
 | 
			
		||||
#define B53_QOS_PAGE			0x30
 | 
			
		||||
 | 
			
		||||
/* Port VLAN Page */
 | 
			
		||||
#define B53_PVLAN_PAGE			0x31
 | 
			
		||||
 | 
			
		||||
/* VLAN Registers */
 | 
			
		||||
#define B53_VLAN_PAGE			0x34
 | 
			
		||||
 | 
			
		||||
/* Jumbo Frame Registers */
 | 
			
		||||
#define B53_JUMBO_PAGE			0x40
 | 
			
		||||
 | 
			
		||||
/* CFP Configuration Registers Page */
 | 
			
		||||
#define B53_CFP_PAGE			0xa1
 | 
			
		||||
 | 
			
		||||
/*************************************************************************
 | 
			
		||||
 * Control Page registers
 | 
			
		||||
 *************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* Port Control Register (8 bit) */
 | 
			
		||||
#define B53_PORT_CTRL(i)		(0x00 + (i))
 | 
			
		||||
#define   PORT_CTRL_RX_DISABLE		BIT(0)
 | 
			
		||||
#define   PORT_CTRL_TX_DISABLE		BIT(1)
 | 
			
		||||
#define   PORT_CTRL_RX_BCST_EN		BIT(2) /* Broadcast RX (P8 only) */
 | 
			
		||||
#define   PORT_CTRL_RX_MCST_EN		BIT(3) /* Multicast RX (P8 only) */
 | 
			
		||||
#define   PORT_CTRL_RX_UCST_EN		BIT(4) /* Unicast RX (P8 only) */
 | 
			
		||||
#define	  PORT_CTRL_STP_STATE_S		5
 | 
			
		||||
#define   PORT_CTRL_STP_STATE_MASK	(0x7 << PORT_CTRL_STP_STATE_S)
 | 
			
		||||
 | 
			
		||||
/* SMP Control Register (8 bit) */
 | 
			
		||||
#define B53_SMP_CTRL			0x0a
 | 
			
		||||
 | 
			
		||||
/* Switch Mode Control Register (8 bit) */
 | 
			
		||||
#define B53_SWITCH_MODE			0x0b
 | 
			
		||||
#define   SM_SW_FWD_MODE		BIT(0)	/* 1 = Managed Mode */
 | 
			
		||||
#define   SM_SW_FWD_EN			BIT(1)	/* Forwarding Enable */
 | 
			
		||||
 | 
			
		||||
/* IMP Port state override register (8 bit) */
 | 
			
		||||
#define B53_PORT_OVERRIDE_CTRL		0x0e
 | 
			
		||||
#define   PORT_OVERRIDE_LINK		BIT(0)
 | 
			
		||||
#define   PORT_OVERRIDE_FULL_DUPLEX	BIT(1) /* 0 = Half Duplex */
 | 
			
		||||
#define   PORT_OVERRIDE_SPEED_S		2
 | 
			
		||||
#define   PORT_OVERRIDE_SPEED_10M	(0 << PORT_OVERRIDE_SPEED_S)
 | 
			
		||||
#define   PORT_OVERRIDE_SPEED_100M	(1 << PORT_OVERRIDE_SPEED_S)
 | 
			
		||||
#define   PORT_OVERRIDE_SPEED_1000M	(2 << PORT_OVERRIDE_SPEED_S)
 | 
			
		||||
#define   PORT_OVERRIDE_RV_MII_25	BIT(4) /* BCM5325 only */
 | 
			
		||||
#define   PORT_OVERRIDE_RX_FLOW		BIT(4)
 | 
			
		||||
#define   PORT_OVERRIDE_TX_FLOW		BIT(5)
 | 
			
		||||
#define   PORT_OVERRIDE_SPEED_2000M	BIT(6) /* BCM5301X only, requires setting 1000M */
 | 
			
		||||
#define   PORT_OVERRIDE_EN		BIT(7) /* Use the register contents */
 | 
			
		||||
 | 
			
		||||
/* Power-down mode control */
 | 
			
		||||
#define B53_PD_MODE_CTRL_25		0x0f
 | 
			
		||||
 | 
			
		||||
/* IP Multicast control (8 bit) */
 | 
			
		||||
#define B53_IP_MULTICAST_CTRL		0x21
 | 
			
		||||
#define  B53_IPMC_FWD_EN		BIT(1)
 | 
			
		||||
#define  B53_UC_FWD_EN			BIT(6)
 | 
			
		||||
#define  B53_MC_FWD_EN			BIT(7)
 | 
			
		||||
 | 
			
		||||
/* (16 bit) */
 | 
			
		||||
#define B53_UC_FLOOD_MASK		0x32
 | 
			
		||||
#define B53_MC_FLOOD_MASK		0x34
 | 
			
		||||
#define B53_IPMC_FLOOD_MASK		0x36
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Override Ports 0-7 State on devices with xMII interfaces (8 bit)
 | 
			
		||||
 *
 | 
			
		||||
 * For port 8 still use B53_PORT_OVERRIDE_CTRL
 | 
			
		||||
 * Please note that not all ports are available on every hardware, e.g. BCM5301X
 | 
			
		||||
 * don't include overriding port 6, BCM63xx also have some limitations.
 | 
			
		||||
 */
 | 
			
		||||
#define B53_GMII_PORT_OVERRIDE_CTRL(i)	(0x58 + (i))
 | 
			
		||||
#define   GMII_PO_LINK			BIT(0)
 | 
			
		||||
#define   GMII_PO_FULL_DUPLEX		BIT(1) /* 0 = Half Duplex */
 | 
			
		||||
#define   GMII_PO_SPEED_S		2
 | 
			
		||||
#define   GMII_PO_SPEED_10M		(0 << GMII_PO_SPEED_S)
 | 
			
		||||
#define   GMII_PO_SPEED_100M		(1 << GMII_PO_SPEED_S)
 | 
			
		||||
#define   GMII_PO_SPEED_1000M		(2 << GMII_PO_SPEED_S)
 | 
			
		||||
#define   GMII_PO_RX_FLOW		BIT(4)
 | 
			
		||||
#define   GMII_PO_TX_FLOW		BIT(5)
 | 
			
		||||
#define   GMII_PO_EN			BIT(6) /* Use the register contents */
 | 
			
		||||
#define   GMII_PO_SPEED_2000M		BIT(7) /* BCM5301X only, requires setting 1000M */
 | 
			
		||||
 | 
			
		||||
/* Software reset register (8 bit) */
 | 
			
		||||
#define B53_SOFTRESET			0x79
 | 
			
		||||
 | 
			
		||||
/* Fast Aging Control register (8 bit) */
 | 
			
		||||
#define B53_FAST_AGE_CTRL		0x88
 | 
			
		||||
#define   FAST_AGE_STATIC		BIT(0)
 | 
			
		||||
#define   FAST_AGE_DYNAMIC		BIT(1)
 | 
			
		||||
#define   FAST_AGE_PORT			BIT(2)
 | 
			
		||||
#define   FAST_AGE_VLAN			BIT(3)
 | 
			
		||||
#define   FAST_AGE_STP			BIT(4)
 | 
			
		||||
#define   FAST_AGE_MC			BIT(5)
 | 
			
		||||
#define   FAST_AGE_DONE			BIT(7)
 | 
			
		||||
 | 
			
		||||
/*************************************************************************
 | 
			
		||||
 * Status Page registers
 | 
			
		||||
 *************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* Link Status Summary Register (16bit) */
 | 
			
		||||
#define B53_LINK_STAT			0x00
 | 
			
		||||
 | 
			
		||||
/* Link Status Change Register (16 bit) */
 | 
			
		||||
#define B53_LINK_STAT_CHANGE		0x02
 | 
			
		||||
 | 
			
		||||
/* Port Speed Summary Register (16 bit for FE, 32 bit for GE) */
 | 
			
		||||
#define B53_SPEED_STAT			0x04
 | 
			
		||||
#define  SPEED_PORT_FE(reg, port)	(((reg) >> (port)) & 1)
 | 
			
		||||
#define  SPEED_PORT_GE(reg, port)	(((reg) >> 2 * (port)) & 3)
 | 
			
		||||
#define  SPEED_STAT_10M			0
 | 
			
		||||
#define  SPEED_STAT_100M		1
 | 
			
		||||
#define  SPEED_STAT_1000M		2
 | 
			
		||||
 | 
			
		||||
/* Duplex Status Summary (16 bit) */
 | 
			
		||||
#define B53_DUPLEX_STAT_FE		0x06
 | 
			
		||||
#define B53_DUPLEX_STAT_GE		0x08
 | 
			
		||||
#define B53_DUPLEX_STAT_63XX		0x0c
 | 
			
		||||
 | 
			
		||||
/* Revision ID register for BCM5325 */
 | 
			
		||||
#define B53_REV_ID_25			0x50
 | 
			
		||||
 | 
			
		||||
/* Strap Value (48 bit) */
 | 
			
		||||
#define B53_STRAP_VALUE			0x70
 | 
			
		||||
#define   SV_GMII_CTRL_115		BIT(27)
 | 
			
		||||
 | 
			
		||||
/*************************************************************************
 | 
			
		||||
 * Management Mode Page Registers
 | 
			
		||||
 *************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* Global Management Config Register (8 bit) */
 | 
			
		||||
#define B53_GLOBAL_CONFIG		0x00
 | 
			
		||||
#define   GC_RESET_MIB			0x01
 | 
			
		||||
#define   GC_RX_BPDU_EN			0x02
 | 
			
		||||
#define   GC_MIB_AC_HDR_EN		0x10
 | 
			
		||||
#define   GC_MIB_AC_EN			0x20
 | 
			
		||||
#define   GC_FRM_MGMT_PORT_M		0xC0
 | 
			
		||||
#define   GC_FRM_MGMT_PORT_04		0x00
 | 
			
		||||
#define   GC_FRM_MGMT_PORT_MII		0x80
 | 
			
		||||
 | 
			
		||||
/* Broadcom Header control register (8 bit) */
 | 
			
		||||
#define B53_BRCM_HDR			0x03
 | 
			
		||||
#define   BRCM_HDR_P8_EN		BIT(0) /* Enable tagging on port 8 */
 | 
			
		||||
#define   BRCM_HDR_P5_EN		BIT(1) /* Enable tagging on port 5 */
 | 
			
		||||
 | 
			
		||||
/* Device ID register (8 or 32 bit) */
 | 
			
		||||
#define B53_DEVICE_ID			0x30
 | 
			
		||||
 | 
			
		||||
/* Revision ID register (8 bit) */
 | 
			
		||||
#define B53_REV_ID			0x40
 | 
			
		||||
 | 
			
		||||
/*************************************************************************
 | 
			
		||||
 * ARL Access Page Registers
 | 
			
		||||
 *************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* VLAN Table Access Register (8 bit) */
 | 
			
		||||
#define B53_VT_ACCESS			0x80
 | 
			
		||||
#define B53_VT_ACCESS_9798		0x60 /* for BCM5397/BCM5398 */
 | 
			
		||||
#define B53_VT_ACCESS_63XX		0x60 /* for BCM6328/62/68 */
 | 
			
		||||
#define   VTA_CMD_WRITE			0
 | 
			
		||||
#define   VTA_CMD_READ			1
 | 
			
		||||
#define   VTA_CMD_CLEAR			2
 | 
			
		||||
#define   VTA_START_CMD			BIT(7)
 | 
			
		||||
 | 
			
		||||
/* VLAN Table Index Register (16 bit) */
 | 
			
		||||
#define B53_VT_INDEX			0x81
 | 
			
		||||
#define B53_VT_INDEX_9798		0x61
 | 
			
		||||
#define B53_VT_INDEX_63XX		0x62
 | 
			
		||||
 | 
			
		||||
/* VLAN Table Entry Register (32 bit) */
 | 
			
		||||
#define B53_VT_ENTRY			0x83
 | 
			
		||||
#define B53_VT_ENTRY_9798		0x63
 | 
			
		||||
#define B53_VT_ENTRY_63XX		0x64
 | 
			
		||||
#define   VTE_MEMBERS			0x1ff
 | 
			
		||||
#define   VTE_UNTAG_S			9
 | 
			
		||||
#define   VTE_UNTAG			(0x1ff << 9)
 | 
			
		||||
 | 
			
		||||
/*************************************************************************
 | 
			
		||||
 * Port VLAN Registers
 | 
			
		||||
 *************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* Port VLAN mask (16 bit) IMP port is always 8, also on 5325 & co */
 | 
			
		||||
#define B53_PVLAN_PORT_MASK(i)		((i) * 2)
 | 
			
		||||
 | 
			
		||||
/*************************************************************************
 | 
			
		||||
 * 802.1Q Page Registers
 | 
			
		||||
 *************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* Global QoS Control (8 bit) */
 | 
			
		||||
#define B53_QOS_GLOBAL_CTL		0x00
 | 
			
		||||
 | 
			
		||||
/* Enable 802.1Q for individual Ports (16 bit) */
 | 
			
		||||
#define B53_802_1P_EN			0x04
 | 
			
		||||
 | 
			
		||||
/*************************************************************************
 | 
			
		||||
 * VLAN Page Registers
 | 
			
		||||
 *************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* VLAN Control 0 (8 bit) */
 | 
			
		||||
#define B53_VLAN_CTRL0			0x00
 | 
			
		||||
#define   VC0_8021PF_CTRL_MASK		0x3
 | 
			
		||||
#define   VC0_8021PF_CTRL_NONE		0x0
 | 
			
		||||
#define   VC0_8021PF_CTRL_CHANGE_PRI	0x1
 | 
			
		||||
#define   VC0_8021PF_CTRL_CHANGE_VID	0x2
 | 
			
		||||
#define   VC0_8021PF_CTRL_CHANGE_BOTH	0x3
 | 
			
		||||
#define   VC0_8021QF_CTRL_MASK		0xc
 | 
			
		||||
#define   VC0_8021QF_CTRL_CHANGE_PRI	0x1
 | 
			
		||||
#define   VC0_8021QF_CTRL_CHANGE_VID	0x2
 | 
			
		||||
#define   VC0_8021QF_CTRL_CHANGE_BOTH	0x3
 | 
			
		||||
#define   VC0_RESERVED_1		BIT(1)
 | 
			
		||||
#define   VC0_DROP_VID_MISS		BIT(4)
 | 
			
		||||
#define   VC0_VID_HASH_VID		BIT(5)
 | 
			
		||||
#define   VC0_VID_CHK_EN		BIT(6)	/* Use VID,DA or VID,SA */
 | 
			
		||||
#define   VC0_VLAN_EN			BIT(7)	/* 802.1Q VLAN Enabled */
 | 
			
		||||
 | 
			
		||||
/* VLAN Control 1 (8 bit) */
 | 
			
		||||
#define B53_VLAN_CTRL1			0x01
 | 
			
		||||
#define   VC1_RX_MCST_TAG_EN		BIT(1)
 | 
			
		||||
#define   VC1_RX_MCST_FWD_EN		BIT(2)
 | 
			
		||||
#define   VC1_RX_MCST_UNTAG_EN		BIT(3)
 | 
			
		||||
 | 
			
		||||
/* VLAN Control 2 (8 bit) */
 | 
			
		||||
#define B53_VLAN_CTRL2			0x02
 | 
			
		||||
 | 
			
		||||
/* VLAN Control 3 (8 bit when BCM5325, 16 bit else) */
 | 
			
		||||
#define B53_VLAN_CTRL3			0x03
 | 
			
		||||
#define B53_VLAN_CTRL3_63XX		0x04
 | 
			
		||||
#define   VC3_MAXSIZE_1532		BIT(6) /* 5325 only */
 | 
			
		||||
#define   VC3_HIGH_8BIT_EN		BIT(7) /* 5325 only */
 | 
			
		||||
 | 
			
		||||
/* VLAN Control 4 (8 bit) */
 | 
			
		||||
#define B53_VLAN_CTRL4			0x05
 | 
			
		||||
#define B53_VLAN_CTRL4_25		0x04
 | 
			
		||||
#define B53_VLAN_CTRL4_63XX		0x06
 | 
			
		||||
#define   VC4_ING_VID_CHECK_S		6
 | 
			
		||||
#define   VC4_ING_VID_CHECK_MASK	(0x3 << VC4_ING_VID_CHECK_S)
 | 
			
		||||
#define   VC4_ING_VID_VIO_FWD		0 /* forward, but do not learn */
 | 
			
		||||
#define   VC4_ING_VID_VIO_DROP		1 /* drop VID violations */
 | 
			
		||||
#define   VC4_NO_ING_VID_CHK		2 /* do not check */
 | 
			
		||||
#define   VC4_ING_VID_VIO_TO_IMP	3 /* redirect to MII port */
 | 
			
		||||
 | 
			
		||||
/* VLAN Control 5 (8 bit) */
 | 
			
		||||
#define B53_VLAN_CTRL5			0x06
 | 
			
		||||
#define B53_VLAN_CTRL5_25		0x05
 | 
			
		||||
#define B53_VLAN_CTRL5_63XX		0x07
 | 
			
		||||
#define   VC5_VID_FFF_EN		BIT(2)
 | 
			
		||||
#define   VC5_DROP_VTABLE_MISS		BIT(3)
 | 
			
		||||
 | 
			
		||||
/* VLAN Control 6 (8 bit) */
 | 
			
		||||
#define B53_VLAN_CTRL6			0x07
 | 
			
		||||
#define B53_VLAN_CTRL6_63XX		0x08
 | 
			
		||||
 | 
			
		||||
/* VLAN Table Access Register (16 bit) */
 | 
			
		||||
#define B53_VLAN_TABLE_ACCESS_25	0x06	/* BCM5325E/5350 */
 | 
			
		||||
#define B53_VLAN_TABLE_ACCESS_65	0x08	/* BCM5365 */
 | 
			
		||||
#define   VTA_VID_LOW_MASK_25		0xf
 | 
			
		||||
#define   VTA_VID_LOW_MASK_65		0xff
 | 
			
		||||
#define   VTA_VID_HIGH_S_25		4
 | 
			
		||||
#define   VTA_VID_HIGH_S_65		8
 | 
			
		||||
#define   VTA_VID_HIGH_MASK_25		(0xff << VTA_VID_HIGH_S_25E)
 | 
			
		||||
#define   VTA_VID_HIGH_MASK_65		(0xf << VTA_VID_HIGH_S_65)
 | 
			
		||||
#define   VTA_RW_STATE			BIT(12)
 | 
			
		||||
#define   VTA_RW_STATE_RD		0
 | 
			
		||||
#define   VTA_RW_STATE_WR		BIT(12)
 | 
			
		||||
#define   VTA_RW_OP_EN			BIT(13)
 | 
			
		||||
 | 
			
		||||
/* VLAN Read/Write Registers for (16/32 bit) */
 | 
			
		||||
#define B53_VLAN_WRITE_25		0x08
 | 
			
		||||
#define B53_VLAN_WRITE_65		0x0a
 | 
			
		||||
#define B53_VLAN_READ			0x0c
 | 
			
		||||
#define   VA_MEMBER_MASK		0x3f
 | 
			
		||||
#define   VA_UNTAG_S_25			6
 | 
			
		||||
#define   VA_UNTAG_MASK_25		0x3f
 | 
			
		||||
#define   VA_UNTAG_S_65			7
 | 
			
		||||
#define   VA_UNTAG_MASK_65		0x1f
 | 
			
		||||
#define   VA_VID_HIGH_S			12
 | 
			
		||||
#define   VA_VID_HIGH_MASK		(0xffff << VA_VID_HIGH_S)
 | 
			
		||||
#define   VA_VALID_25			BIT(20)
 | 
			
		||||
#define   VA_VALID_25_R4		BIT(24)
 | 
			
		||||
#define   VA_VALID_65			BIT(14)
 | 
			
		||||
 | 
			
		||||
/* VLAN Port Default Tag (16 bit) */
 | 
			
		||||
#define B53_VLAN_PORT_DEF_TAG(i)	(0x10 + 2 * (i))
 | 
			
		||||
 | 
			
		||||
/*************************************************************************
 | 
			
		||||
 * Jumbo Frame Page Registers
 | 
			
		||||
 *************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* Jumbo Enable Port Mask (bit i == port i enabled) (32 bit) */
 | 
			
		||||
#define B53_JUMBO_PORT_MASK		0x01
 | 
			
		||||
#define B53_JUMBO_PORT_MASK_63XX	0x04
 | 
			
		||||
#define   JPM_10_100_JUMBO_EN		BIT(24) /* GigE always enabled */
 | 
			
		||||
 | 
			
		||||
/* Good Frame Max Size without 802.1Q TAG (16 bit) */
 | 
			
		||||
#define B53_JUMBO_MAX_SIZE		0x05
 | 
			
		||||
#define B53_JUMBO_MAX_SIZE_63XX		0x08
 | 
			
		||||
#define   JMS_MIN_SIZE			1518
 | 
			
		||||
#define   JMS_MAX_SIZE			9724
 | 
			
		||||
 | 
			
		||||
/*************************************************************************
 | 
			
		||||
 * CFP Configuration Page Registers
 | 
			
		||||
 *************************************************************************/
 | 
			
		||||
 | 
			
		||||
/* CFP Control Register with ports map (8 bit) */
 | 
			
		||||
#define B53_CFP_CTRL			0x00
 | 
			
		||||
 | 
			
		||||
#endif /* !__B53_REGS_H */
 | 
			
		||||
							
								
								
									
										344
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										344
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,344 @@
 | 
			
		|||
/*
 | 
			
		||||
 * B53 register access through SPI
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and/or distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <asm/unaligned.h>
 | 
			
		||||
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/spi/spi.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
#include <linux/platform_data/b53.h>
 | 
			
		||||
 | 
			
		||||
#include "b53_priv.h"
 | 
			
		||||
 | 
			
		||||
#define B53_SPI_DATA		0xf0
 | 
			
		||||
 | 
			
		||||
#define B53_SPI_STATUS		0xfe
 | 
			
		||||
#define B53_SPI_CMD_SPIF	BIT(7)
 | 
			
		||||
#define B53_SPI_CMD_RACK	BIT(5)
 | 
			
		||||
 | 
			
		||||
#define B53_SPI_CMD_READ	0x00
 | 
			
		||||
#define B53_SPI_CMD_WRITE	0x01
 | 
			
		||||
#define B53_SPI_CMD_NORMAL	0x60
 | 
			
		||||
#define B53_SPI_CMD_FAST	0x10
 | 
			
		||||
 | 
			
		||||
#define B53_SPI_PAGE_SELECT	0xff
 | 
			
		||||
 | 
			
		||||
static inline int b53_spi_read_reg(struct spi_device *spi, u8 reg, u8 *val,
 | 
			
		||||
				     unsigned len)
 | 
			
		||||
{
 | 
			
		||||
	u8 txbuf[2];
 | 
			
		||||
 | 
			
		||||
	txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_READ;
 | 
			
		||||
	txbuf[1] = reg;
 | 
			
		||||
 | 
			
		||||
	return spi_write_then_read(spi, txbuf, 2, val, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int b53_spi_clear_status(struct spi_device *spi)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	u8 rxbuf;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < 10; i++) {
 | 
			
		||||
		ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			return ret;
 | 
			
		||||
 | 
			
		||||
		if (!(rxbuf & B53_SPI_CMD_SPIF))
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		mdelay(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (i == 10)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int b53_spi_set_page(struct spi_device *spi, u8 page)
 | 
			
		||||
{
 | 
			
		||||
	u8 txbuf[3];
 | 
			
		||||
 | 
			
		||||
	txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
 | 
			
		||||
	txbuf[1] = B53_SPI_PAGE_SELECT;
 | 
			
		||||
	txbuf[2] = page;
 | 
			
		||||
 | 
			
		||||
	return spi_write(spi, txbuf, sizeof(txbuf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int b53_prepare_reg_access(struct spi_device *spi, u8 page)
 | 
			
		||||
{
 | 
			
		||||
	int ret = b53_spi_clear_status(spi);
 | 
			
		||||
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	return b53_spi_set_page(spi, page);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_spi_prepare_reg_read(struct spi_device *spi, u8 reg)
 | 
			
		||||
{
 | 
			
		||||
	u8 rxbuf;
 | 
			
		||||
	int retry_count;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = b53_spi_read_reg(spi, reg, &rxbuf, 1);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	for (retry_count = 0; retry_count < 10; retry_count++) {
 | 
			
		||||
		ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			return ret;
 | 
			
		||||
 | 
			
		||||
		if (rxbuf & B53_SPI_CMD_RACK)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		mdelay(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (retry_count == 10)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_spi_read(struct b53_device *dev, u8 page, u8 reg, u8 *data,
 | 
			
		||||
			unsigned len)
 | 
			
		||||
{
 | 
			
		||||
	struct spi_device *spi = dev->priv;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = b53_prepare_reg_access(spi, page);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	ret = b53_spi_prepare_reg_read(spi, reg);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	return b53_spi_read_reg(spi, B53_SPI_DATA, data, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_spi_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
 | 
			
		||||
{
 | 
			
		||||
	return b53_spi_read(dev, page, reg, val, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_spi_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
 | 
			
		||||
{
 | 
			
		||||
	int ret = b53_spi_read(dev, page, reg, (u8 *)val, 2);
 | 
			
		||||
 | 
			
		||||
	if (!ret)
 | 
			
		||||
		*val = le16_to_cpu(*val);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_spi_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
 | 
			
		||||
{
 | 
			
		||||
	int ret = b53_spi_read(dev, page, reg, (u8 *)val, 4);
 | 
			
		||||
 | 
			
		||||
	if (!ret)
 | 
			
		||||
		*val = le32_to_cpu(*val);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_spi_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	*val = 0;
 | 
			
		||||
	ret = b53_spi_read(dev, page, reg, (u8 *)val, 6);
 | 
			
		||||
	if (!ret)
 | 
			
		||||
		*val = le64_to_cpu(*val);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_spi_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
 | 
			
		||||
{
 | 
			
		||||
	int ret = b53_spi_read(dev, page, reg, (u8 *)val, 8);
 | 
			
		||||
 | 
			
		||||
	if (!ret)
 | 
			
		||||
		*val = le64_to_cpu(*val);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_spi_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
 | 
			
		||||
{
 | 
			
		||||
	struct spi_device *spi = dev->priv;
 | 
			
		||||
	int ret;
 | 
			
		||||
	u8 txbuf[3];
 | 
			
		||||
 | 
			
		||||
	ret = b53_prepare_reg_access(spi, page);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
 | 
			
		||||
	txbuf[1] = reg;
 | 
			
		||||
	txbuf[2] = value;
 | 
			
		||||
 | 
			
		||||
	return spi_write(spi, txbuf, sizeof(txbuf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_spi_write16(struct b53_device *dev, u8 page, u8 reg, u16 value)
 | 
			
		||||
{
 | 
			
		||||
	struct spi_device *spi = dev->priv;
 | 
			
		||||
	int ret;
 | 
			
		||||
	u8 txbuf[4];
 | 
			
		||||
 | 
			
		||||
	ret = b53_prepare_reg_access(spi, page);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
 | 
			
		||||
	txbuf[1] = reg;
 | 
			
		||||
	put_unaligned_le16(value, &txbuf[2]);
 | 
			
		||||
 | 
			
		||||
	return spi_write(spi, txbuf, sizeof(txbuf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_spi_write32(struct b53_device *dev, u8 page, u8 reg, u32 value)
 | 
			
		||||
{
 | 
			
		||||
	struct spi_device *spi = dev->priv;
 | 
			
		||||
	int ret;
 | 
			
		||||
	u8 txbuf[6];
 | 
			
		||||
 | 
			
		||||
	ret = b53_prepare_reg_access(spi, page);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
 | 
			
		||||
	txbuf[1] = reg;
 | 
			
		||||
	put_unaligned_le32(value, &txbuf[2]);
 | 
			
		||||
 | 
			
		||||
	return spi_write(spi, txbuf, sizeof(txbuf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_spi_write48(struct b53_device *dev, u8 page, u8 reg, u64 value)
 | 
			
		||||
{
 | 
			
		||||
	struct spi_device *spi = dev->priv;
 | 
			
		||||
	int ret;
 | 
			
		||||
	u8 txbuf[10];
 | 
			
		||||
 | 
			
		||||
	ret = b53_prepare_reg_access(spi, page);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
 | 
			
		||||
	txbuf[1] = reg;
 | 
			
		||||
	put_unaligned_le64(value, &txbuf[2]);
 | 
			
		||||
 | 
			
		||||
	return spi_write(spi, txbuf, sizeof(txbuf) - 2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_spi_write64(struct b53_device *dev, u8 page, u8 reg, u64 value)
 | 
			
		||||
{
 | 
			
		||||
	struct spi_device *spi = dev->priv;
 | 
			
		||||
	int ret;
 | 
			
		||||
	u8 txbuf[10];
 | 
			
		||||
 | 
			
		||||
	ret = b53_prepare_reg_access(spi, page);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
 | 
			
		||||
	txbuf[1] = reg;
 | 
			
		||||
	put_unaligned_le64(value, &txbuf[2]);
 | 
			
		||||
 | 
			
		||||
	return spi_write(spi, txbuf, sizeof(txbuf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct b53_io_ops b53_spi_ops = {
 | 
			
		||||
	.read8 = b53_spi_read8,
 | 
			
		||||
	.read16 = b53_spi_read16,
 | 
			
		||||
	.read32 = b53_spi_read32,
 | 
			
		||||
	.read48 = b53_spi_read48,
 | 
			
		||||
	.read64 = b53_spi_read64,
 | 
			
		||||
	.write8 = b53_spi_write8,
 | 
			
		||||
	.write16 = b53_spi_write16,
 | 
			
		||||
	.write32 = b53_spi_write32,
 | 
			
		||||
	.write48 = b53_spi_write48,
 | 
			
		||||
	.write64 = b53_spi_write64,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int b53_spi_probe(struct spi_device *spi)
 | 
			
		||||
{
 | 
			
		||||
	struct b53_device *dev;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	dev = b53_swconfig_switch_alloc(&spi->dev, &b53_spi_ops, spi);
 | 
			
		||||
	if (!dev)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	if (spi->dev.platform_data)
 | 
			
		||||
		dev->pdata = spi->dev.platform_data;
 | 
			
		||||
 | 
			
		||||
	ret = b53_swconfig_switch_register(dev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	spi_set_drvdata(spi, dev);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_spi_remove(struct spi_device *spi)
 | 
			
		||||
{
 | 
			
		||||
	struct b53_device *dev = spi_get_drvdata(spi);
 | 
			
		||||
 | 
			
		||||
	if (dev)
 | 
			
		||||
		b53_switch_remove(dev);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id b53_of_match[] = {
 | 
			
		||||
	{ .compatible = "brcm,bcm5325" },
 | 
			
		||||
	{ .compatible = "brcm,bcm53115" },
 | 
			
		||||
	{ .compatible = "brcm,bcm53125" },
 | 
			
		||||
	{ .compatible = "brcm,bcm53128" },
 | 
			
		||||
	{ .compatible = "brcm,bcm5365" },
 | 
			
		||||
	{ .compatible = "brcm,bcm5395" },
 | 
			
		||||
	{ .compatible = "brcm,bcm5397" },
 | 
			
		||||
	{ .compatible = "brcm,bcm5398" },
 | 
			
		||||
	{ /* sentinel */ },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct spi_driver b53_spi_driver = {
 | 
			
		||||
	.driver = {
 | 
			
		||||
		.name	= "b53-switch",
 | 
			
		||||
		.bus	= &spi_bus_type,
 | 
			
		||||
		.owner	= THIS_MODULE,
 | 
			
		||||
		.of_match_table = b53_of_match,
 | 
			
		||||
	},
 | 
			
		||||
	.probe	= b53_spi_probe,
 | 
			
		||||
	.remove	= b53_spi_remove,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module_spi_driver(b53_spi_driver);
 | 
			
		||||
 | 
			
		||||
MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
 | 
			
		||||
MODULE_DESCRIPTION("B53 SPI access driver");
 | 
			
		||||
MODULE_LICENSE("Dual BSD/GPL");
 | 
			
		||||
							
								
								
									
										385
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/b53_srab.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										385
									
								
								6.12/target/linux/generic/files/drivers/net/phy/b53/b53_srab.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,385 @@
 | 
			
		|||
/*
 | 
			
		||||
 * B53 register access through Switch Register Access Bridge Registers
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and/or distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/platform_device.h>
 | 
			
		||||
#include <linux/platform_data/b53.h>
 | 
			
		||||
#include <linux/version.h>
 | 
			
		||||
 | 
			
		||||
#include "b53_priv.h"
 | 
			
		||||
 | 
			
		||||
/* command and status register of the SRAB */
 | 
			
		||||
#define B53_SRAB_CMDSTAT		0x2c
 | 
			
		||||
#define  B53_SRAB_CMDSTAT_RST		BIT(2)
 | 
			
		||||
#define  B53_SRAB_CMDSTAT_WRITE		BIT(1)
 | 
			
		||||
#define  B53_SRAB_CMDSTAT_GORDYN	BIT(0)
 | 
			
		||||
#define  B53_SRAB_CMDSTAT_PAGE		24
 | 
			
		||||
#define  B53_SRAB_CMDSTAT_REG		16
 | 
			
		||||
 | 
			
		||||
/* high order word of write data to switch registe */
 | 
			
		||||
#define B53_SRAB_WD_H			0x30
 | 
			
		||||
 | 
			
		||||
/* low order word of write data to switch registe */
 | 
			
		||||
#define B53_SRAB_WD_L			0x34
 | 
			
		||||
 | 
			
		||||
/* high order word of read data from switch register */
 | 
			
		||||
#define B53_SRAB_RD_H			0x38
 | 
			
		||||
 | 
			
		||||
/* low order word of read data from switch register */
 | 
			
		||||
#define B53_SRAB_RD_L			0x3c
 | 
			
		||||
 | 
			
		||||
/* command and status register of the SRAB */
 | 
			
		||||
#define B53_SRAB_CTRLS			0x40
 | 
			
		||||
#define  B53_SRAB_CTRLS_RCAREQ		BIT(3)
 | 
			
		||||
#define  B53_SRAB_CTRLS_RCAGNT		BIT(4)
 | 
			
		||||
#define  B53_SRAB_CTRLS_SW_INIT_DONE	BIT(6)
 | 
			
		||||
 | 
			
		||||
/* the register captures interrupt pulses from the switch */
 | 
			
		||||
#define B53_SRAB_INTR			0x44
 | 
			
		||||
 | 
			
		||||
static int b53_srab_request_grant(struct b53_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
	u32 ctrls;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	ctrls = readl(regs + B53_SRAB_CTRLS);
 | 
			
		||||
	ctrls |= B53_SRAB_CTRLS_RCAREQ;
 | 
			
		||||
	writel(ctrls, regs + B53_SRAB_CTRLS);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < 20; i++) {
 | 
			
		||||
		ctrls = readl(regs + B53_SRAB_CTRLS);
 | 
			
		||||
		if (ctrls & B53_SRAB_CTRLS_RCAGNT)
 | 
			
		||||
			break;
 | 
			
		||||
		usleep_range(10, 100);
 | 
			
		||||
	}
 | 
			
		||||
	if (WARN_ON(i == 5))
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void b53_srab_release_grant(struct b53_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
	u32 ctrls;
 | 
			
		||||
 | 
			
		||||
	ctrls = readl(regs + B53_SRAB_CTRLS);
 | 
			
		||||
	ctrls &= ~B53_SRAB_CTRLS_RCAREQ;
 | 
			
		||||
	writel(ctrls, regs + B53_SRAB_CTRLS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_srab_op(struct b53_device *dev, u8 page, u8 reg, u32 op)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	u32 cmdstat;
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
 | 
			
		||||
	/* set register address */
 | 
			
		||||
	cmdstat = (page << B53_SRAB_CMDSTAT_PAGE) |
 | 
			
		||||
		  (reg << B53_SRAB_CMDSTAT_REG) |
 | 
			
		||||
		  B53_SRAB_CMDSTAT_GORDYN |
 | 
			
		||||
		  op;
 | 
			
		||||
	writel(cmdstat, regs + B53_SRAB_CMDSTAT);
 | 
			
		||||
 | 
			
		||||
	/* check if operation completed */
 | 
			
		||||
	for (i = 0; i < 5; ++i) {
 | 
			
		||||
		cmdstat = readl(regs + B53_SRAB_CMDSTAT);
 | 
			
		||||
		if (!(cmdstat & B53_SRAB_CMDSTAT_GORDYN))
 | 
			
		||||
			break;
 | 
			
		||||
		usleep_range(10, 100);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(i == 5))
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_srab_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_request_grant(dev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_op(dev, page, reg, 0);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	*val = readl(regs + B53_SRAB_RD_L) & 0xff;
 | 
			
		||||
 | 
			
		||||
err:
 | 
			
		||||
	b53_srab_release_grant(dev);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_srab_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_request_grant(dev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_op(dev, page, reg, 0);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	*val = readl(regs + B53_SRAB_RD_L) & 0xffff;
 | 
			
		||||
 | 
			
		||||
err:
 | 
			
		||||
	b53_srab_release_grant(dev);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_srab_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_request_grant(dev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_op(dev, page, reg, 0);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	*val = readl(regs + B53_SRAB_RD_L);
 | 
			
		||||
 | 
			
		||||
err:
 | 
			
		||||
	b53_srab_release_grant(dev);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_srab_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_request_grant(dev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_op(dev, page, reg, 0);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	*val = readl(regs + B53_SRAB_RD_L);
 | 
			
		||||
	*val += ((u64)readl(regs + B53_SRAB_RD_H) & 0xffff) << 32;
 | 
			
		||||
 | 
			
		||||
err:
 | 
			
		||||
	b53_srab_release_grant(dev);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_srab_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_request_grant(dev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_op(dev, page, reg, 0);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	*val = readl(regs + B53_SRAB_RD_L);
 | 
			
		||||
	*val += (u64)readl(regs + B53_SRAB_RD_H) << 32;
 | 
			
		||||
 | 
			
		||||
err:
 | 
			
		||||
	b53_srab_release_grant(dev);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_srab_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_request_grant(dev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	writel(value, regs + B53_SRAB_WD_L);
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
 | 
			
		||||
 | 
			
		||||
err:
 | 
			
		||||
	b53_srab_release_grant(dev);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_srab_write16(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
			     u16 value)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_request_grant(dev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	writel(value, regs + B53_SRAB_WD_L);
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
 | 
			
		||||
 | 
			
		||||
err:
 | 
			
		||||
	b53_srab_release_grant(dev);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_srab_write32(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
				    u32 value)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_request_grant(dev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	writel(value, regs + B53_SRAB_WD_L);
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
 | 
			
		||||
 | 
			
		||||
err:
 | 
			
		||||
	b53_srab_release_grant(dev);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_srab_write48(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
				    u64 value)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_request_grant(dev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	writel((u32)value, regs + B53_SRAB_WD_L);
 | 
			
		||||
	writel((u16)(value >> 32), regs + B53_SRAB_WD_H);
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
 | 
			
		||||
 | 
			
		||||
err:
 | 
			
		||||
	b53_srab_release_grant(dev);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int b53_srab_write64(struct b53_device *dev, u8 page, u8 reg,
 | 
			
		||||
			     u64 value)
 | 
			
		||||
{
 | 
			
		||||
	u8 __iomem *regs = dev->priv;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_request_grant(dev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto err;
 | 
			
		||||
 | 
			
		||||
	writel((u32)value, regs + B53_SRAB_WD_L);
 | 
			
		||||
	writel((u32)(value >> 32), regs + B53_SRAB_WD_H);
 | 
			
		||||
 | 
			
		||||
	ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
 | 
			
		||||
 | 
			
		||||
err:
 | 
			
		||||
	b53_srab_release_grant(dev);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct b53_io_ops b53_srab_ops = {
 | 
			
		||||
	.read8 = b53_srab_read8,
 | 
			
		||||
	.read16 = b53_srab_read16,
 | 
			
		||||
	.read32 = b53_srab_read32,
 | 
			
		||||
	.read48 = b53_srab_read48,
 | 
			
		||||
	.read64 = b53_srab_read64,
 | 
			
		||||
	.write8 = b53_srab_write8,
 | 
			
		||||
	.write16 = b53_srab_write16,
 | 
			
		||||
	.write32 = b53_srab_write32,
 | 
			
		||||
	.write48 = b53_srab_write48,
 | 
			
		||||
	.write64 = b53_srab_write64,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int b53_srab_probe(struct platform_device *pdev)
 | 
			
		||||
{
 | 
			
		||||
	struct b53_platform_data *pdata = pdev->dev.platform_data;
 | 
			
		||||
	struct b53_device *dev;
 | 
			
		||||
 | 
			
		||||
	if (!pdata)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	dev = b53_swconfig_switch_alloc(&pdev->dev, &b53_srab_ops, pdata->regs);
 | 
			
		||||
	if (!dev)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	if (pdata)
 | 
			
		||||
		dev->pdata = pdata;
 | 
			
		||||
 | 
			
		||||
	platform_set_drvdata(pdev, dev);
 | 
			
		||||
 | 
			
		||||
	return b53_swconfig_switch_register(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,11,0)
 | 
			
		||||
static int b53_srab_remove(struct platform_device *pdev)
 | 
			
		||||
#else
 | 
			
		||||
static void b53_srab_remove(struct platform_device *pdev)
 | 
			
		||||
#endif
 | 
			
		||||
{
 | 
			
		||||
	struct b53_device *dev = platform_get_drvdata(pdev);
 | 
			
		||||
 | 
			
		||||
	if (dev)
 | 
			
		||||
		b53_switch_remove(dev);
 | 
			
		||||
 | 
			
		||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,11,0)
 | 
			
		||||
	return 0;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct platform_driver b53_srab_driver = {
 | 
			
		||||
	.probe = b53_srab_probe,
 | 
			
		||||
	.remove = b53_srab_remove,
 | 
			
		||||
	.driver = {
 | 
			
		||||
		.name = "b53-srab-switch",
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module_platform_driver(b53_srab_driver);
 | 
			
		||||
MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");
 | 
			
		||||
MODULE_DESCRIPTION("B53 Switch Register Access Bridge Registers (SRAB) access driver");
 | 
			
		||||
MODULE_LICENSE("Dual BSD/GPL");
 | 
			
		||||
							
								
								
									
										1370
									
								
								6.12/target/linux/generic/files/drivers/net/phy/ip17xx.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1370
									
								
								6.12/target/linux/generic/files/drivers/net/phy/ip17xx.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										443
									
								
								6.12/target/linux/generic/files/drivers/net/phy/psb6970.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										443
									
								
								6.12/target/linux/generic/files/drivers/net/phy/psb6970.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,443 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Lantiq PSB6970 (Tantos) Switch driver
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2009,2010 Team Embedded.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute  it and/or modify it
 | 
			
		||||
 * under  the terms of the GNU General Public License v2 as published by the
 | 
			
		||||
 * Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 * The switch programming done in this driver follows the 
 | 
			
		||||
 * "Ethernet Traffic Separation using VLAN" Application Note as
 | 
			
		||||
 * published by Lantiq.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/netdevice.h>
 | 
			
		||||
#include <linux/switch.h>
 | 
			
		||||
#include <linux/phy.h>
 | 
			
		||||
#include <linux/version.h>
 | 
			
		||||
 | 
			
		||||
#define PSB6970_MAX_VLANS		16
 | 
			
		||||
#define PSB6970_NUM_PORTS		7
 | 
			
		||||
#define PSB6970_DEFAULT_PORT_CPU	6
 | 
			
		||||
#define PSB6970_IS_CPU_PORT(x)		((x) > 4)
 | 
			
		||||
 | 
			
		||||
#define PHYADDR(_reg)		((_reg >> 5) & 0xff), (_reg & 0x1f)
 | 
			
		||||
 | 
			
		||||
/* --- Identification --- */
 | 
			
		||||
#define PSB6970_CI0		0x0100
 | 
			
		||||
#define PSB6970_CI0_MASK	0x000f
 | 
			
		||||
#define PSB6970_CI1		0x0101
 | 
			
		||||
#define PSB6970_CI1_VAL		0x2599
 | 
			
		||||
#define PSB6970_CI1_MASK	0xffff
 | 
			
		||||
 | 
			
		||||
/* --- VLAN filter table --- */
 | 
			
		||||
#define PSB6970_VFxL(i)		((i)*2+0x10)	/* VLAN Filter Low */
 | 
			
		||||
#define PSB6970_VFxL_VV		(1 << 15)	/* VLAN_Valid */
 | 
			
		||||
 | 
			
		||||
#define PSB6970_VFxH(i)		((i)*2+0x11)	/* VLAN Filter High */
 | 
			
		||||
#define PSB6970_VFxH_TM_SHIFT	7		/* Tagged Member */
 | 
			
		||||
 | 
			
		||||
/* --- Port registers --- */
 | 
			
		||||
#define PSB6970_EC(p)		((p)*0x20+2)	/* Extended Control */
 | 
			
		||||
#define PSB6970_EC_IFNTE	(1 << 1)	/* Input Force No Tag Enable */
 | 
			
		||||
 | 
			
		||||
#define PSB6970_PBVM(p)		((p)*0x20+3)	/* Port Base VLAN Map */
 | 
			
		||||
#define PSB6970_PBVM_VMCE	(1 << 8)
 | 
			
		||||
#define PSB6970_PBVM_AOVTP	(1 << 9)
 | 
			
		||||
#define PSB6970_PBVM_VSD	(1 << 10)
 | 
			
		||||
#define PSB6970_PBVM_VC		(1 << 11)	/* VID Check with VID table */
 | 
			
		||||
#define PSB6970_PBVM_TBVE	(1 << 13)	/* Tag-Based VLAN enable */
 | 
			
		||||
 | 
			
		||||
#define PSB6970_DVID(p)		((p)*0x20+4)	/* Default VLAN ID & Priority */
 | 
			
		||||
 | 
			
		||||
struct psb6970_priv {
 | 
			
		||||
	struct switch_dev dev;
 | 
			
		||||
	struct phy_device *phy;
 | 
			
		||||
	u16 (*read) (struct phy_device* phydev, int reg);
 | 
			
		||||
	void (*write) (struct phy_device* phydev, int reg, u16 val);
 | 
			
		||||
	struct mutex reg_mutex;
 | 
			
		||||
 | 
			
		||||
	/* all fields below are cleared on reset */
 | 
			
		||||
	struct_group(psb6970_priv_volatile,
 | 
			
		||||
		bool vlan;
 | 
			
		||||
		u16 vlan_id[PSB6970_MAX_VLANS];
 | 
			
		||||
		u8 vlan_table[PSB6970_MAX_VLANS];
 | 
			
		||||
		u8 vlan_tagged;
 | 
			
		||||
		u16 pvid[PSB6970_NUM_PORTS];
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define to_psb6970(_dev) container_of(_dev, struct psb6970_priv, dev)
 | 
			
		||||
 | 
			
		||||
static u16 psb6970_mii_read(struct phy_device *phydev, int reg)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = phydev->mdio.bus;
 | 
			
		||||
 | 
			
		||||
	return bus->read(bus, PHYADDR(reg));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void psb6970_mii_write(struct phy_device *phydev, int reg, u16 val)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = phydev->mdio.bus;
 | 
			
		||||
 | 
			
		||||
	bus->write(bus, PHYADDR(reg), val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
psb6970_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
 | 
			
		||||
		 struct switch_val *val)
 | 
			
		||||
{
 | 
			
		||||
	struct psb6970_priv *priv = to_psb6970(dev);
 | 
			
		||||
	priv->vlan = !!val->value.i;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
psb6970_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
 | 
			
		||||
		 struct switch_val *val)
 | 
			
		||||
{
 | 
			
		||||
	struct psb6970_priv *priv = to_psb6970(dev);
 | 
			
		||||
	val->value.i = priv->vlan;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int psb6970_set_pvid(struct switch_dev *dev, int port, int vlan)
 | 
			
		||||
{
 | 
			
		||||
	struct psb6970_priv *priv = to_psb6970(dev);
 | 
			
		||||
 | 
			
		||||
	/* make sure no invalid PVIDs get set */
 | 
			
		||||
	if (vlan >= dev->vlans)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	priv->pvid[port] = vlan;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int psb6970_get_pvid(struct switch_dev *dev, int port, int *vlan)
 | 
			
		||||
{
 | 
			
		||||
	struct psb6970_priv *priv = to_psb6970(dev);
 | 
			
		||||
	*vlan = priv->pvid[port];
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
psb6970_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
 | 
			
		||||
		struct switch_val *val)
 | 
			
		||||
{
 | 
			
		||||
	struct psb6970_priv *priv = to_psb6970(dev);
 | 
			
		||||
	priv->vlan_id[val->port_vlan] = val->value.i;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
psb6970_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
 | 
			
		||||
		struct switch_val *val)
 | 
			
		||||
{
 | 
			
		||||
	struct psb6970_priv *priv = to_psb6970(dev);
 | 
			
		||||
	val->value.i = priv->vlan_id[val->port_vlan];
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct switch_attr psb6970_globals[] = {
 | 
			
		||||
	{
 | 
			
		||||
	 .type = SWITCH_TYPE_INT,
 | 
			
		||||
	 .name = "enable_vlan",
 | 
			
		||||
	 .description = "Enable VLAN mode",
 | 
			
		||||
	 .set = psb6970_set_vlan,
 | 
			
		||||
	 .get = psb6970_get_vlan,
 | 
			
		||||
	 .max = 1},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct switch_attr psb6970_port[] = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct switch_attr psb6970_vlan[] = {
 | 
			
		||||
	{
 | 
			
		||||
	 .type = SWITCH_TYPE_INT,
 | 
			
		||||
	 .name = "vid",
 | 
			
		||||
	 .description = "VLAN ID (0-4094)",
 | 
			
		||||
	 .set = psb6970_set_vid,
 | 
			
		||||
	 .get = psb6970_get_vid,
 | 
			
		||||
	 .max = 4094,
 | 
			
		||||
	 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int psb6970_get_ports(struct switch_dev *dev, struct switch_val *val)
 | 
			
		||||
{
 | 
			
		||||
	struct psb6970_priv *priv = to_psb6970(dev);
 | 
			
		||||
	u8 ports = priv->vlan_table[val->port_vlan];
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	val->len = 0;
 | 
			
		||||
	for (i = 0; i < PSB6970_NUM_PORTS; i++) {
 | 
			
		||||
		struct switch_port *p;
 | 
			
		||||
 | 
			
		||||
		if (!(ports & (1 << i)))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		p = &val->value.ports[val->len++];
 | 
			
		||||
		p->id = i;
 | 
			
		||||
		if (priv->vlan_tagged & (1 << i))
 | 
			
		||||
			p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
 | 
			
		||||
		else
 | 
			
		||||
			p->flags = 0;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int psb6970_set_ports(struct switch_dev *dev, struct switch_val *val)
 | 
			
		||||
{
 | 
			
		||||
	struct psb6970_priv *priv = to_psb6970(dev);
 | 
			
		||||
	u8 *vt = &priv->vlan_table[val->port_vlan];
 | 
			
		||||
	int i, j;
 | 
			
		||||
 | 
			
		||||
	*vt = 0;
 | 
			
		||||
	for (i = 0; i < val->len; i++) {
 | 
			
		||||
		struct switch_port *p = &val->value.ports[i];
 | 
			
		||||
 | 
			
		||||
		if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED))
 | 
			
		||||
			priv->vlan_tagged |= (1 << p->id);
 | 
			
		||||
		else {
 | 
			
		||||
			priv->vlan_tagged &= ~(1 << p->id);
 | 
			
		||||
			priv->pvid[p->id] = val->port_vlan;
 | 
			
		||||
 | 
			
		||||
			/* make sure that an untagged port does not
 | 
			
		||||
			 * appear in other vlans */
 | 
			
		||||
			for (j = 0; j < PSB6970_MAX_VLANS; j++) {
 | 
			
		||||
				if (j == val->port_vlan)
 | 
			
		||||
					continue;
 | 
			
		||||
				priv->vlan_table[j] &= ~(1 << p->id);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		*vt |= 1 << p->id;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int psb6970_hw_apply(struct switch_dev *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct psb6970_priv *priv = to_psb6970(dev);
 | 
			
		||||
	int i, j;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&priv->reg_mutex);
 | 
			
		||||
 | 
			
		||||
	if (priv->vlan) {
 | 
			
		||||
		/* into the vlan translation unit */
 | 
			
		||||
		for (j = 0; j < PSB6970_MAX_VLANS; j++) {
 | 
			
		||||
			u8 vp = priv->vlan_table[j];
 | 
			
		||||
 | 
			
		||||
			if (vp) {
 | 
			
		||||
				priv->write(priv->phy, PSB6970_VFxL(j),
 | 
			
		||||
					    PSB6970_VFxL_VV | priv->vlan_id[j]);
 | 
			
		||||
				priv->write(priv->phy, PSB6970_VFxH(j),
 | 
			
		||||
					    ((vp & priv->
 | 
			
		||||
					      vlan_tagged) <<
 | 
			
		||||
					     PSB6970_VFxH_TM_SHIFT) | vp);
 | 
			
		||||
			} else	/* clear VLAN Valid flag for unused vlans */
 | 
			
		||||
				priv->write(priv->phy, PSB6970_VFxL(j), 0);
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* update the port destination mask registers and tag settings */
 | 
			
		||||
	for (i = 0; i < PSB6970_NUM_PORTS; i++) {
 | 
			
		||||
		int dvid = 1, pbvm = 0x7f | PSB6970_PBVM_VSD, ec = 0;
 | 
			
		||||
 | 
			
		||||
		if (priv->vlan) {
 | 
			
		||||
			ec = PSB6970_EC_IFNTE;
 | 
			
		||||
			dvid = priv->vlan_id[priv->pvid[i]];
 | 
			
		||||
			pbvm |= PSB6970_PBVM_TBVE | PSB6970_PBVM_VMCE;
 | 
			
		||||
 | 
			
		||||
			if ((i << 1) & priv->vlan_tagged)
 | 
			
		||||
				pbvm |= PSB6970_PBVM_AOVTP | PSB6970_PBVM_VC;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		priv->write(priv->phy, PSB6970_PBVM(i), pbvm);
 | 
			
		||||
 | 
			
		||||
		if (!PSB6970_IS_CPU_PORT(i)) {
 | 
			
		||||
			priv->write(priv->phy, PSB6970_EC(i), ec);
 | 
			
		||||
			priv->write(priv->phy, PSB6970_DVID(i), dvid);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mutex_unlock(&priv->reg_mutex);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int psb6970_reset_switch(struct switch_dev *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct psb6970_priv *priv = to_psb6970(dev);
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&priv->reg_mutex);
 | 
			
		||||
 | 
			
		||||
	memset(&priv->psb6970_priv_volatile, 0,
 | 
			
		||||
		sizeof(priv->psb6970_priv_volatile));
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < PSB6970_MAX_VLANS; i++)
 | 
			
		||||
		priv->vlan_id[i] = i;
 | 
			
		||||
 | 
			
		||||
	mutex_unlock(&priv->reg_mutex);
 | 
			
		||||
 | 
			
		||||
	return psb6970_hw_apply(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct switch_dev_ops psb6970_ops = {
 | 
			
		||||
	.attr_global = {
 | 
			
		||||
			.attr = psb6970_globals,
 | 
			
		||||
			.n_attr = ARRAY_SIZE(psb6970_globals),
 | 
			
		||||
			},
 | 
			
		||||
	.attr_port = {
 | 
			
		||||
		      .attr = psb6970_port,
 | 
			
		||||
		      .n_attr = ARRAY_SIZE(psb6970_port),
 | 
			
		||||
		      },
 | 
			
		||||
	.attr_vlan = {
 | 
			
		||||
		      .attr = psb6970_vlan,
 | 
			
		||||
		      .n_attr = ARRAY_SIZE(psb6970_vlan),
 | 
			
		||||
		      },
 | 
			
		||||
	.get_port_pvid = psb6970_get_pvid,
 | 
			
		||||
	.set_port_pvid = psb6970_set_pvid,
 | 
			
		||||
	.get_vlan_ports = psb6970_get_ports,
 | 
			
		||||
	.set_vlan_ports = psb6970_set_ports,
 | 
			
		||||
	.apply_config = psb6970_hw_apply,
 | 
			
		||||
	.reset_switch = psb6970_reset_switch,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int psb6970_config_init(struct phy_device *pdev)
 | 
			
		||||
{
 | 
			
		||||
	struct psb6970_priv *priv;
 | 
			
		||||
	struct switch_dev *swdev;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	priv = kzalloc(sizeof(struct psb6970_priv), GFP_KERNEL);
 | 
			
		||||
	if (priv == NULL)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	priv->phy = pdev;
 | 
			
		||||
 | 
			
		||||
	if (pdev->mdio.addr == 0)
 | 
			
		||||
		printk(KERN_INFO "%s: psb6970 switch driver attached.\n",
 | 
			
		||||
		       pdev->attached_dev->name);
 | 
			
		||||
 | 
			
		||||
	if (pdev->mdio.addr != 0) {
 | 
			
		||||
		kfree(priv);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	linkmode_zero(pdev->supported);
 | 
			
		||||
	linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, pdev->supported);
 | 
			
		||||
	linkmode_copy(pdev->advertising, pdev->supported);
 | 
			
		||||
 | 
			
		||||
	mutex_init(&priv->reg_mutex);
 | 
			
		||||
	priv->read = psb6970_mii_read;
 | 
			
		||||
	priv->write = psb6970_mii_write;
 | 
			
		||||
 | 
			
		||||
	pdev->priv = priv;
 | 
			
		||||
 | 
			
		||||
	swdev = &priv->dev;
 | 
			
		||||
	swdev->cpu_port = PSB6970_DEFAULT_PORT_CPU;
 | 
			
		||||
	swdev->ops = &psb6970_ops;
 | 
			
		||||
 | 
			
		||||
	swdev->name = "Lantiq PSB6970";
 | 
			
		||||
	swdev->vlans = PSB6970_MAX_VLANS;
 | 
			
		||||
	swdev->ports = PSB6970_NUM_PORTS;
 | 
			
		||||
 | 
			
		||||
	if ((ret = register_switch(&priv->dev, pdev->attached_dev)) < 0) {
 | 
			
		||||
		kfree(priv);
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = psb6970_reset_switch(&priv->dev);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		kfree(priv);
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int psb6970_read_status(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	phydev->speed = SPEED_100;
 | 
			
		||||
	phydev->duplex = DUPLEX_FULL;
 | 
			
		||||
	phydev->link = 1;
 | 
			
		||||
 | 
			
		||||
	phydev->state = PHY_RUNNING;
 | 
			
		||||
	netif_carrier_on(phydev->attached_dev);
 | 
			
		||||
	phydev->adjust_link(phydev->attached_dev);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int psb6970_config_aneg(struct phy_device *phydev)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int psb6970_probe(struct phy_device *pdev)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void psb6970_remove(struct phy_device *pdev)
 | 
			
		||||
{
 | 
			
		||||
	struct psb6970_priv *priv = pdev->priv;
 | 
			
		||||
 | 
			
		||||
	if (!priv)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (pdev->mdio.addr == 0)
 | 
			
		||||
		unregister_switch(&priv->dev);
 | 
			
		||||
	kfree(priv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int psb6970_fixup(struct phy_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct mii_bus *bus = dev->mdio.bus;
 | 
			
		||||
	u16 reg;
 | 
			
		||||
 | 
			
		||||
	/* look for the switch on the bus */
 | 
			
		||||
	reg = bus->read(bus, PHYADDR(PSB6970_CI1)) & PSB6970_CI1_MASK;
 | 
			
		||||
	if (reg != PSB6970_CI1_VAL)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	dev->phy_id = (reg << 16);
 | 
			
		||||
	dev->phy_id |= bus->read(bus, PHYADDR(PSB6970_CI0)) & PSB6970_CI0_MASK;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct phy_driver psb6970_driver = {
 | 
			
		||||
	.name = "Lantiq PSB6970",
 | 
			
		||||
	.phy_id = PSB6970_CI1_VAL << 16,
 | 
			
		||||
	.phy_id_mask = 0xffff0000,
 | 
			
		||||
	.features = PHY_BASIC_FEATURES,
 | 
			
		||||
	.probe = psb6970_probe,
 | 
			
		||||
	.remove = psb6970_remove,
 | 
			
		||||
	.config_init = &psb6970_config_init,
 | 
			
		||||
	.config_aneg = &psb6970_config_aneg,
 | 
			
		||||
	.read_status = &psb6970_read_status,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int __init psb6970_init(void)
 | 
			
		||||
{
 | 
			
		||||
	phy_register_fixup_for_id(PHY_ANY_ID, psb6970_fixup);
 | 
			
		||||
	return phy_driver_register(&psb6970_driver, THIS_MODULE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module_init(psb6970_init);
 | 
			
		||||
 | 
			
		||||
void __exit psb6970_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	phy_driver_unregister(&psb6970_driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module_exit(psb6970_exit);
 | 
			
		||||
 | 
			
		||||
MODULE_DESCRIPTION("Lantiq PSB6970 Switch");
 | 
			
		||||
MODULE_AUTHOR("Ithamar R. Adema <ithamar.adema@team-embedded.nl>");
 | 
			
		||||
MODULE_LICENSE("GPL");
 | 
			
		||||
							
								
								
									
										1063
									
								
								6.12/target/linux/generic/files/drivers/net/phy/rtl8306.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1063
									
								
								6.12/target/linux/generic/files/drivers/net/phy/rtl8306.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										1625
									
								
								6.12/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1625
									
								
								6.12/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										171
									
								
								6.12/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								6.12/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,171 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Realtek RTL8366 SMI interface driver defines
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 * by the Free Software Foundation.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _RTL8366_SMI_H
 | 
			
		||||
#define _RTL8366_SMI_H
 | 
			
		||||
 | 
			
		||||
#include <linux/phy.h>
 | 
			
		||||
#include <linux/switch.h>
 | 
			
		||||
#include <linux/platform_device.h>
 | 
			
		||||
#include <linux/reset.h>
 | 
			
		||||
 | 
			
		||||
struct rtl8366_smi_ops;
 | 
			
		||||
struct rtl8366_vlan_ops;
 | 
			
		||||
struct mii_bus;
 | 
			
		||||
struct dentry;
 | 
			
		||||
struct inode;
 | 
			
		||||
struct file;
 | 
			
		||||
 | 
			
		||||
typedef enum rtl8367b_chip_e {
 | 
			
		||||
	RTL8367B_CHIP_UNKNOWN,
 | 
			
		||||
 /* Family B */
 | 
			
		||||
	RTL8367B_CHIP_RTL8367RB,
 | 
			
		||||
	RTL8367B_CHIP_RTL8367R_VB, /* chip with exception in extif assignment */
 | 
			
		||||
/* Family C */
 | 
			
		||||
	RTL8367B_CHIP_RTL8367RB_VB,
 | 
			
		||||
	RTL8367B_CHIP_RTL8367S,
 | 
			
		||||
/* Family D */
 | 
			
		||||
	RTL8367B_CHIP_RTL8367S_VB /* chip with exception in extif assignment */
 | 
			
		||||
} rtl8367b_chip_t;
 | 
			
		||||
 | 
			
		||||
struct rtl8366_mib_counter {
 | 
			
		||||
	unsigned	base;
 | 
			
		||||
	unsigned	offset;
 | 
			
		||||
	unsigned	length;
 | 
			
		||||
	const char	*name;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtl8366_smi {
 | 
			
		||||
	struct device		*parent;
 | 
			
		||||
	unsigned int		gpio_sda;
 | 
			
		||||
	unsigned int		gpio_sck;
 | 
			
		||||
	void			(*hw_reset)(struct rtl8366_smi *smi, bool active);
 | 
			
		||||
	unsigned int		clk_delay;	/* ns */
 | 
			
		||||
	u8			cmd_read;
 | 
			
		||||
	u8			cmd_write;
 | 
			
		||||
	spinlock_t		lock;
 | 
			
		||||
	struct mii_bus		*mii_bus;
 | 
			
		||||
	int			mii_irq[PHY_MAX_ADDR];
 | 
			
		||||
	struct switch_dev	sw_dev;
 | 
			
		||||
 | 
			
		||||
	unsigned int		cpu_port;
 | 
			
		||||
	unsigned int		num_ports;
 | 
			
		||||
	unsigned int		num_vlan_mc;
 | 
			
		||||
	unsigned int		num_mib_counters;
 | 
			
		||||
	struct rtl8366_mib_counter *mib_counters;
 | 
			
		||||
 | 
			
		||||
	struct rtl8366_smi_ops	*ops;
 | 
			
		||||
 | 
			
		||||
	int			vlan_enabled;
 | 
			
		||||
	int			vlan4k_enabled;
 | 
			
		||||
 | 
			
		||||
	char			buf[4096];
 | 
			
		||||
 | 
			
		||||
	struct reset_control	*reset;
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
 | 
			
		||||
	struct dentry           *debugfs_root;
 | 
			
		||||
	u16			dbg_reg;
 | 
			
		||||
	u8			dbg_vlan_4k_page;
 | 
			
		||||
#endif
 | 
			
		||||
	u32			phy_id;
 | 
			
		||||
	rtl8367b_chip_t		rtl8367b_chip;
 | 
			
		||||
	struct mii_bus		*ext_mbus;
 | 
			
		||||
	struct rtl8366_vlan_mc *emu_vlanmc;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtl8366_vlan_mc {
 | 
			
		||||
	u16	vid;
 | 
			
		||||
	u16	untag;
 | 
			
		||||
	u16	member;
 | 
			
		||||
	u8	fid;
 | 
			
		||||
	u8	priority;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtl8366_vlan_4k {
 | 
			
		||||
	u16	vid;
 | 
			
		||||
	u16	untag;
 | 
			
		||||
	u16	member;
 | 
			
		||||
	u8	fid;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtl8366_smi_ops {
 | 
			
		||||
	int	(*detect)(struct rtl8366_smi *smi);
 | 
			
		||||
	int	(*reset_chip)(struct rtl8366_smi *smi);
 | 
			
		||||
	int	(*setup)(struct rtl8366_smi *smi);
 | 
			
		||||
 | 
			
		||||
	int	(*mii_read)(struct mii_bus *bus, int addr, int reg);
 | 
			
		||||
	int	(*mii_write)(struct mii_bus *bus, int addr, int reg, u16 val);
 | 
			
		||||
 | 
			
		||||
	int	(*get_vlan_mc)(struct rtl8366_smi *smi, u32 index,
 | 
			
		||||
			       struct rtl8366_vlan_mc *vlanmc);
 | 
			
		||||
	int	(*set_vlan_mc)(struct rtl8366_smi *smi, u32 index,
 | 
			
		||||
			       const struct rtl8366_vlan_mc *vlanmc);
 | 
			
		||||
	int	(*get_vlan_4k)(struct rtl8366_smi *smi, u32 vid,
 | 
			
		||||
			       struct rtl8366_vlan_4k *vlan4k);
 | 
			
		||||
	int	(*set_vlan_4k)(struct rtl8366_smi *smi,
 | 
			
		||||
			       const struct rtl8366_vlan_4k *vlan4k);
 | 
			
		||||
	int	(*get_mc_index)(struct rtl8366_smi *smi, int port, int *val);
 | 
			
		||||
	int	(*set_mc_index)(struct rtl8366_smi *smi, int port, int index);
 | 
			
		||||
	int	(*get_mib_counter)(struct rtl8366_smi *smi, int counter,
 | 
			
		||||
				   int port, unsigned long long *val);
 | 
			
		||||
	int	(*is_vlan_valid)(struct rtl8366_smi *smi, unsigned vlan);
 | 
			
		||||
	int	(*enable_vlan)(struct rtl8366_smi *smi, int enable);
 | 
			
		||||
	int	(*enable_vlan4k)(struct rtl8366_smi *smi, int enable);
 | 
			
		||||
	int	(*enable_port)(struct rtl8366_smi *smi, int port, int enable);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtl8366_smi *rtl8366_smi_alloc(struct device *parent);
 | 
			
		||||
int rtl8366_smi_init(struct rtl8366_smi *smi);
 | 
			
		||||
void rtl8366_smi_cleanup(struct rtl8366_smi *smi);
 | 
			
		||||
int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data);
 | 
			
		||||
int rtl8366_smi_write_reg_noack(struct rtl8366_smi *smi, u32 addr, u32 data);
 | 
			
		||||
int rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data);
 | 
			
		||||
int rtl8366_smi_rmwr(struct rtl8366_smi *smi, u32 addr, u32 mask, u32 data);
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
 | 
			
		||||
int rtl8366_debugfs_open(struct inode *inode, struct file *file);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static inline struct rtl8366_smi *sw_to_rtl8366_smi(struct switch_dev *sw)
 | 
			
		||||
{
 | 
			
		||||
	return container_of(sw, struct rtl8366_smi, sw_dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int rtl8366_sw_reset_switch(struct switch_dev *dev);
 | 
			
		||||
int rtl8366_sw_get_port_pvid(struct switch_dev *dev, int port, int *val);
 | 
			
		||||
int rtl8366_sw_set_port_pvid(struct switch_dev *dev, int port, int val);
 | 
			
		||||
int rtl8366_sw_get_port_mib(struct switch_dev *dev,
 | 
			
		||||
			    const struct switch_attr *attr,
 | 
			
		||||
			    struct switch_val *val);
 | 
			
		||||
int rtl8366_sw_get_vlan_info(struct switch_dev *dev,
 | 
			
		||||
			     const struct switch_attr *attr,
 | 
			
		||||
			     struct switch_val *val);
 | 
			
		||||
int rtl8366_sw_get_vlan_fid(struct switch_dev *dev,
 | 
			
		||||
			     const struct switch_attr *attr,
 | 
			
		||||
			     struct switch_val *val);
 | 
			
		||||
int rtl8366_sw_set_vlan_fid(struct switch_dev *dev,
 | 
			
		||||
			     const struct switch_attr *attr,
 | 
			
		||||
			     struct switch_val *val);
 | 
			
		||||
int rtl8366_sw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val);
 | 
			
		||||
int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val);
 | 
			
		||||
int rtl8366_sw_get_vlan_enable(struct switch_dev *dev,
 | 
			
		||||
			       const struct switch_attr *attr,
 | 
			
		||||
			       struct switch_val *val);
 | 
			
		||||
int rtl8366_sw_set_vlan_enable(struct switch_dev *dev,
 | 
			
		||||
			       const struct switch_attr *attr,
 | 
			
		||||
			       struct switch_val *val);
 | 
			
		||||
int rtl8366_sw_get_port_stats(struct switch_dev *dev, int port,
 | 
			
		||||
				struct switch_port_stats *stats,
 | 
			
		||||
				int txb_id, int rxb_id);
 | 
			
		||||
 | 
			
		||||
struct rtl8366_smi* rtl8366_smi_probe(struct platform_device *pdev);
 | 
			
		||||
 | 
			
		||||
#endif /*  _RTL8366_SMI_H */
 | 
			
		||||
							
								
								
									
										1538
									
								
								6.12/target/linux/generic/files/drivers/net/phy/rtl8366rb.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1538
									
								
								6.12/target/linux/generic/files/drivers/net/phy/rtl8366rb.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										1326
									
								
								6.12/target/linux/generic/files/drivers/net/phy/rtl8366s.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1326
									
								
								6.12/target/linux/generic/files/drivers/net/phy/rtl8366s.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										1868
									
								
								6.12/target/linux/generic/files/drivers/net/phy/rtl8367.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1868
									
								
								6.12/target/linux/generic/files/drivers/net/phy/rtl8367.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										1658
									
								
								6.12/target/linux/generic/files/drivers/net/phy/rtl8367b.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1658
									
								
								6.12/target/linux/generic/files/drivers/net/phy/rtl8367b.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										1242
									
								
								6.12/target/linux/generic/files/drivers/net/phy/swconfig.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1242
									
								
								6.12/target/linux/generic/files/drivers/net/phy/swconfig.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										555
									
								
								6.12/target/linux/generic/files/drivers/net/phy/swconfig_leds.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										555
									
								
								6.12/target/linux/generic/files/drivers/net/phy/swconfig_leds.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,555 @@
 | 
			
		|||
/*
 | 
			
		||||
 * swconfig_led.c: LED trigger support for the switch configuration API
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SWCONFIG_LEDS
 | 
			
		||||
 | 
			
		||||
#include <linux/leds.h>
 | 
			
		||||
#include <linux/ctype.h>
 | 
			
		||||
#include <linux/device.h>
 | 
			
		||||
#include <linux/workqueue.h>
 | 
			
		||||
 | 
			
		||||
#define SWCONFIG_LED_TIMER_INTERVAL	(HZ / 10)
 | 
			
		||||
#define SWCONFIG_LED_NUM_PORTS		32
 | 
			
		||||
 | 
			
		||||
#define SWCONFIG_LED_PORT_SPEED_NA	0x01	/* unknown speed */
 | 
			
		||||
#define SWCONFIG_LED_PORT_SPEED_10	0x02	/* 10 Mbps */
 | 
			
		||||
#define SWCONFIG_LED_PORT_SPEED_100	0x04	/* 100 Mbps */
 | 
			
		||||
#define SWCONFIG_LED_PORT_SPEED_1000	0x08	/* 1000 Mbps */
 | 
			
		||||
#define SWCONFIG_LED_PORT_SPEED_ALL	(SWCONFIG_LED_PORT_SPEED_NA | \
 | 
			
		||||
					 SWCONFIG_LED_PORT_SPEED_10 | \
 | 
			
		||||
					 SWCONFIG_LED_PORT_SPEED_100 | \
 | 
			
		||||
					 SWCONFIG_LED_PORT_SPEED_1000)
 | 
			
		||||
 | 
			
		||||
#define SWCONFIG_LED_MODE_LINK		0x01
 | 
			
		||||
#define SWCONFIG_LED_MODE_TX		0x02
 | 
			
		||||
#define SWCONFIG_LED_MODE_RX		0x04
 | 
			
		||||
#define SWCONFIG_LED_MODE_TXRX		(SWCONFIG_LED_MODE_TX   | \
 | 
			
		||||
					 SWCONFIG_LED_MODE_RX)
 | 
			
		||||
#define SWCONFIG_LED_MODE_ALL		(SWCONFIG_LED_MODE_LINK | \
 | 
			
		||||
					 SWCONFIG_LED_MODE_TX   | \
 | 
			
		||||
					 SWCONFIG_LED_MODE_RX)
 | 
			
		||||
 | 
			
		||||
struct switch_led_trigger {
 | 
			
		||||
	struct led_trigger trig;
 | 
			
		||||
	struct switch_dev *swdev;
 | 
			
		||||
 | 
			
		||||
	struct delayed_work sw_led_work;
 | 
			
		||||
	u32 port_mask;
 | 
			
		||||
	u32 port_link;
 | 
			
		||||
	unsigned long long port_tx_traffic[SWCONFIG_LED_NUM_PORTS];
 | 
			
		||||
	unsigned long long port_rx_traffic[SWCONFIG_LED_NUM_PORTS];
 | 
			
		||||
	u8 link_speed[SWCONFIG_LED_NUM_PORTS];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct swconfig_trig_data {
 | 
			
		||||
	struct led_classdev *led_cdev;
 | 
			
		||||
	struct switch_dev *swdev;
 | 
			
		||||
 | 
			
		||||
	rwlock_t lock;
 | 
			
		||||
	u32 port_mask;
 | 
			
		||||
 | 
			
		||||
	bool prev_link;
 | 
			
		||||
	unsigned long prev_traffic;
 | 
			
		||||
	enum led_brightness prev_brightness;
 | 
			
		||||
	u8 mode;
 | 
			
		||||
	u8 speed_mask;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
swconfig_trig_set_brightness(struct swconfig_trig_data *trig_data,
 | 
			
		||||
			     enum led_brightness brightness)
 | 
			
		||||
{
 | 
			
		||||
	led_set_brightness(trig_data->led_cdev, brightness);
 | 
			
		||||
	trig_data->prev_brightness = brightness;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
swconfig_trig_update_port_mask(struct led_trigger *trigger)
 | 
			
		||||
{
 | 
			
		||||
	struct list_head *entry;
 | 
			
		||||
	struct switch_led_trigger *sw_trig;
 | 
			
		||||
	u32 port_mask;
 | 
			
		||||
 | 
			
		||||
	if (!trigger)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	sw_trig = (void *) trigger;
 | 
			
		||||
 | 
			
		||||
	port_mask = 0;
 | 
			
		||||
	spin_lock(&trigger->leddev_list_lock);
 | 
			
		||||
	list_for_each(entry, &trigger->led_cdevs) {
 | 
			
		||||
		struct led_classdev *led_cdev;
 | 
			
		||||
		struct swconfig_trig_data *trig_data;
 | 
			
		||||
 | 
			
		||||
		led_cdev = list_entry(entry, struct led_classdev, trig_list);
 | 
			
		||||
		trig_data = led_cdev->trigger_data;
 | 
			
		||||
		if (trig_data) {
 | 
			
		||||
			read_lock(&trig_data->lock);
 | 
			
		||||
			port_mask |= trig_data->port_mask;
 | 
			
		||||
			read_unlock(&trig_data->lock);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&trigger->leddev_list_lock);
 | 
			
		||||
 | 
			
		||||
	sw_trig->port_mask = port_mask;
 | 
			
		||||
 | 
			
		||||
	if (port_mask)
 | 
			
		||||
		schedule_delayed_work(&sw_trig->sw_led_work,
 | 
			
		||||
				      SWCONFIG_LED_TIMER_INTERVAL);
 | 
			
		||||
	else
 | 
			
		||||
		cancel_delayed_work_sync(&sw_trig->sw_led_work);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t
 | 
			
		||||
swconfig_trig_port_mask_store(struct device *dev, struct device_attribute *attr,
 | 
			
		||||
			      const char *buf, size_t size)
 | 
			
		||||
{
 | 
			
		||||
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
 | 
			
		||||
	struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
 | 
			
		||||
	unsigned long port_mask;
 | 
			
		||||
	int ret;
 | 
			
		||||
	bool changed;
 | 
			
		||||
 | 
			
		||||
	ret = kstrtoul(buf, 0, &port_mask);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	write_lock(&trig_data->lock);
 | 
			
		||||
	changed = (trig_data->port_mask != port_mask);
 | 
			
		||||
	trig_data->port_mask = port_mask;
 | 
			
		||||
	write_unlock(&trig_data->lock);
 | 
			
		||||
 | 
			
		||||
	if (changed) {
 | 
			
		||||
		if (port_mask == 0)
 | 
			
		||||
			swconfig_trig_set_brightness(trig_data, LED_OFF);
 | 
			
		||||
 | 
			
		||||
		swconfig_trig_update_port_mask(led_cdev->trigger);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t
 | 
			
		||||
swconfig_trig_port_mask_show(struct device *dev, struct device_attribute *attr,
 | 
			
		||||
			     char *buf)
 | 
			
		||||
{
 | 
			
		||||
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
 | 
			
		||||
	struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
 | 
			
		||||
	u32 port_mask;
 | 
			
		||||
 | 
			
		||||
	read_lock(&trig_data->lock);
 | 
			
		||||
	port_mask = trig_data->port_mask;
 | 
			
		||||
	read_unlock(&trig_data->lock);
 | 
			
		||||
 | 
			
		||||
	sprintf(buf, "%#x\n", port_mask);
 | 
			
		||||
 | 
			
		||||
	return strlen(buf) + 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DEVICE_ATTR(port_mask, 0644, swconfig_trig_port_mask_show,
 | 
			
		||||
		   swconfig_trig_port_mask_store);
 | 
			
		||||
 | 
			
		||||
/* speed_mask file handler - display value */
 | 
			
		||||
static ssize_t swconfig_trig_speed_mask_show(struct device *dev,
 | 
			
		||||
					     struct device_attribute *attr,
 | 
			
		||||
					     char *buf)
 | 
			
		||||
{
 | 
			
		||||
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
 | 
			
		||||
	struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
 | 
			
		||||
	u8 speed_mask;
 | 
			
		||||
 | 
			
		||||
	read_lock(&trig_data->lock);
 | 
			
		||||
	speed_mask = trig_data->speed_mask;
 | 
			
		||||
	read_unlock(&trig_data->lock);
 | 
			
		||||
 | 
			
		||||
	sprintf(buf, "%#x\n", speed_mask);
 | 
			
		||||
 | 
			
		||||
	return strlen(buf) + 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* speed_mask file handler - store value */
 | 
			
		||||
static ssize_t swconfig_trig_speed_mask_store(struct device *dev,
 | 
			
		||||
					      struct device_attribute *attr,
 | 
			
		||||
					      const char *buf, size_t size)
 | 
			
		||||
{
 | 
			
		||||
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
 | 
			
		||||
	struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
 | 
			
		||||
	u8 speed_mask;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = kstrtou8(buf, 0, &speed_mask);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	write_lock(&trig_data->lock);
 | 
			
		||||
	trig_data->speed_mask = speed_mask & SWCONFIG_LED_PORT_SPEED_ALL;
 | 
			
		||||
	write_unlock(&trig_data->lock);
 | 
			
		||||
 | 
			
		||||
	return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* speed_mask special file */
 | 
			
		||||
static DEVICE_ATTR(speed_mask, 0644, swconfig_trig_speed_mask_show,
 | 
			
		||||
		   swconfig_trig_speed_mask_store);
 | 
			
		||||
 | 
			
		||||
static ssize_t swconfig_trig_mode_show(struct device *dev,
 | 
			
		||||
		struct device_attribute *attr, char *buf)
 | 
			
		||||
{
 | 
			
		||||
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
 | 
			
		||||
	struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
 | 
			
		||||
	u8 mode;
 | 
			
		||||
 | 
			
		||||
	read_lock(&trig_data->lock);
 | 
			
		||||
	mode = trig_data->mode;
 | 
			
		||||
	read_unlock(&trig_data->lock);
 | 
			
		||||
 | 
			
		||||
	if (mode == 0) {
 | 
			
		||||
		strcpy(buf, "none\n");
 | 
			
		||||
	} else {
 | 
			
		||||
		if (mode & SWCONFIG_LED_MODE_LINK)
 | 
			
		||||
			strcat(buf, "link ");
 | 
			
		||||
		if (mode & SWCONFIG_LED_MODE_TX)
 | 
			
		||||
			strcat(buf, "tx ");
 | 
			
		||||
		if (mode & SWCONFIG_LED_MODE_RX)
 | 
			
		||||
			strcat(buf, "rx ");
 | 
			
		||||
		strcat(buf, "\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return strlen(buf)+1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t swconfig_trig_mode_store(struct device *dev,
 | 
			
		||||
		struct device_attribute *attr, const char *buf, size_t size)
 | 
			
		||||
{
 | 
			
		||||
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
 | 
			
		||||
	struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
 | 
			
		||||
	char copybuf[128];
 | 
			
		||||
	int new_mode = -1;
 | 
			
		||||
	char *p, *token;
 | 
			
		||||
 | 
			
		||||
	/* take a copy since we don't want to trash the inbound buffer when using strsep */
 | 
			
		||||
	strncpy(copybuf, buf, sizeof(copybuf));
 | 
			
		||||
	copybuf[sizeof(copybuf) - 1] = 0;
 | 
			
		||||
	p = copybuf;
 | 
			
		||||
 | 
			
		||||
	while ((token = strsep(&p, " \t\n")) != NULL) {
 | 
			
		||||
		if (!*token)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (new_mode < 0)
 | 
			
		||||
			new_mode = 0;
 | 
			
		||||
 | 
			
		||||
		if (!strcmp(token, "none"))
 | 
			
		||||
			new_mode = 0;
 | 
			
		||||
		else if (!strcmp(token, "tx"))
 | 
			
		||||
			new_mode |= SWCONFIG_LED_MODE_TX;
 | 
			
		||||
		else if (!strcmp(token, "rx"))
 | 
			
		||||
			new_mode |= SWCONFIG_LED_MODE_RX;
 | 
			
		||||
		else if (!strcmp(token, "link"))
 | 
			
		||||
			new_mode |= SWCONFIG_LED_MODE_LINK;
 | 
			
		||||
		else
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (new_mode < 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	write_lock(&trig_data->lock);
 | 
			
		||||
	trig_data->mode = (u8)new_mode;
 | 
			
		||||
	write_unlock(&trig_data->lock);
 | 
			
		||||
 | 
			
		||||
	return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* mode special file */
 | 
			
		||||
static DEVICE_ATTR(mode, 0644, swconfig_trig_mode_show,
 | 
			
		||||
		   swconfig_trig_mode_store);
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
swconfig_trig_activate(struct led_classdev *led_cdev)
 | 
			
		||||
{
 | 
			
		||||
	struct switch_led_trigger *sw_trig;
 | 
			
		||||
	struct swconfig_trig_data *trig_data;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	trig_data = kzalloc(sizeof(struct swconfig_trig_data), GFP_KERNEL);
 | 
			
		||||
	if (!trig_data)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	sw_trig = (void *) led_cdev->trigger;
 | 
			
		||||
 | 
			
		||||
	rwlock_init(&trig_data->lock);
 | 
			
		||||
	trig_data->led_cdev = led_cdev;
 | 
			
		||||
	trig_data->swdev = sw_trig->swdev;
 | 
			
		||||
	trig_data->speed_mask = SWCONFIG_LED_PORT_SPEED_ALL;
 | 
			
		||||
	trig_data->mode = SWCONFIG_LED_MODE_ALL;
 | 
			
		||||
	led_cdev->trigger_data = trig_data;
 | 
			
		||||
 | 
			
		||||
	err = device_create_file(led_cdev->dev, &dev_attr_port_mask);
 | 
			
		||||
	if (err)
 | 
			
		||||
		goto err_free;
 | 
			
		||||
 | 
			
		||||
	err = device_create_file(led_cdev->dev, &dev_attr_speed_mask);
 | 
			
		||||
	if (err)
 | 
			
		||||
		goto err_dev_free;
 | 
			
		||||
 | 
			
		||||
	err = device_create_file(led_cdev->dev, &dev_attr_mode);
 | 
			
		||||
	if (err)
 | 
			
		||||
		goto err_mode_free;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
err_mode_free:
 | 
			
		||||
	device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
 | 
			
		||||
 | 
			
		||||
err_dev_free:
 | 
			
		||||
	device_remove_file(led_cdev->dev, &dev_attr_port_mask);
 | 
			
		||||
 | 
			
		||||
err_free:
 | 
			
		||||
	led_cdev->trigger_data = NULL;
 | 
			
		||||
	kfree(trig_data);
 | 
			
		||||
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
swconfig_trig_deactivate(struct led_classdev *led_cdev)
 | 
			
		||||
{
 | 
			
		||||
	struct swconfig_trig_data *trig_data;
 | 
			
		||||
 | 
			
		||||
	swconfig_trig_update_port_mask(led_cdev->trigger);
 | 
			
		||||
 | 
			
		||||
	trig_data = (void *) led_cdev->trigger_data;
 | 
			
		||||
	if (trig_data) {
 | 
			
		||||
		device_remove_file(led_cdev->dev, &dev_attr_port_mask);
 | 
			
		||||
		device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
 | 
			
		||||
		device_remove_file(led_cdev->dev, &dev_attr_mode);
 | 
			
		||||
		kfree(trig_data);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * link off -> led off (can't be any other reason to turn it on)
 | 
			
		||||
 * link on:
 | 
			
		||||
 *	mode link: led on by default only if speed matches, else off
 | 
			
		||||
 *	mode txrx: blink only if speed matches, else off
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
swconfig_trig_led_event(struct switch_led_trigger *sw_trig,
 | 
			
		||||
			struct led_classdev *led_cdev)
 | 
			
		||||
{
 | 
			
		||||
	struct swconfig_trig_data *trig_data;
 | 
			
		||||
	u32 port_mask;
 | 
			
		||||
	bool link;
 | 
			
		||||
	u8 speed_mask, mode;
 | 
			
		||||
	enum led_brightness led_base, led_blink;
 | 
			
		||||
 | 
			
		||||
	trig_data = led_cdev->trigger_data;
 | 
			
		||||
	if (!trig_data)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	read_lock(&trig_data->lock);
 | 
			
		||||
	port_mask = trig_data->port_mask;
 | 
			
		||||
	speed_mask = trig_data->speed_mask;
 | 
			
		||||
	mode = trig_data->mode;
 | 
			
		||||
	read_unlock(&trig_data->lock);
 | 
			
		||||
 | 
			
		||||
	link = !!(sw_trig->port_link & port_mask);
 | 
			
		||||
	if (!link) {
 | 
			
		||||
		if (trig_data->prev_brightness != LED_OFF)
 | 
			
		||||
			swconfig_trig_set_brightness(trig_data, LED_OFF); /* and stop */
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		unsigned long traffic;
 | 
			
		||||
		int speedok;	/* link speed flag */
 | 
			
		||||
		int i;
 | 
			
		||||
 | 
			
		||||
		led_base = LED_FULL;
 | 
			
		||||
		led_blink = LED_OFF;
 | 
			
		||||
		traffic = 0;
 | 
			
		||||
		speedok = 0;
 | 
			
		||||
		for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
 | 
			
		||||
			if (port_mask & (1 << i)) {
 | 
			
		||||
				if (sw_trig->link_speed[i] & speed_mask) {
 | 
			
		||||
					traffic += ((mode & SWCONFIG_LED_MODE_TX) ?
 | 
			
		||||
						    sw_trig->port_tx_traffic[i] : 0) +
 | 
			
		||||
						((mode & SWCONFIG_LED_MODE_RX) ?
 | 
			
		||||
						 sw_trig->port_rx_traffic[i] : 0);
 | 
			
		||||
					speedok = 1;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (speedok) {
 | 
			
		||||
			/* At least one port speed matches speed_mask */
 | 
			
		||||
			if (!(mode & SWCONFIG_LED_MODE_LINK)) {
 | 
			
		||||
				led_base = LED_OFF;
 | 
			
		||||
				led_blink = LED_FULL;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (trig_data->prev_brightness != led_base)
 | 
			
		||||
				swconfig_trig_set_brightness(trig_data,
 | 
			
		||||
							     led_base);
 | 
			
		||||
			else if (traffic != trig_data->prev_traffic)
 | 
			
		||||
				swconfig_trig_set_brightness(trig_data,
 | 
			
		||||
							     led_blink);
 | 
			
		||||
		} else if (trig_data->prev_brightness != LED_OFF)
 | 
			
		||||
			swconfig_trig_set_brightness(trig_data, LED_OFF);
 | 
			
		||||
 | 
			
		||||
		trig_data->prev_traffic = traffic;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	trig_data->prev_link = link;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
swconfig_trig_update_leds(struct switch_led_trigger *sw_trig)
 | 
			
		||||
{
 | 
			
		||||
	struct list_head *entry;
 | 
			
		||||
	struct led_trigger *trigger;
 | 
			
		||||
 | 
			
		||||
	trigger = &sw_trig->trig;
 | 
			
		||||
	spin_lock(&trigger->leddev_list_lock);
 | 
			
		||||
	list_for_each(entry, &trigger->led_cdevs) {
 | 
			
		||||
		struct led_classdev *led_cdev;
 | 
			
		||||
 | 
			
		||||
		led_cdev = list_entry(entry, struct led_classdev, trig_list);
 | 
			
		||||
		swconfig_trig_led_event(sw_trig, led_cdev);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&trigger->leddev_list_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
swconfig_led_work_func(struct work_struct *work)
 | 
			
		||||
{
 | 
			
		||||
	struct switch_led_trigger *sw_trig;
 | 
			
		||||
	struct switch_dev *swdev;
 | 
			
		||||
	u32 port_mask;
 | 
			
		||||
	u32 link;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	sw_trig = container_of(work, struct switch_led_trigger,
 | 
			
		||||
			       sw_led_work.work);
 | 
			
		||||
 | 
			
		||||
	port_mask = sw_trig->port_mask;
 | 
			
		||||
	swdev = sw_trig->swdev;
 | 
			
		||||
 | 
			
		||||
	link = 0;
 | 
			
		||||
	for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
 | 
			
		||||
		u32 port_bit;
 | 
			
		||||
 | 
			
		||||
		sw_trig->link_speed[i] = 0;
 | 
			
		||||
 | 
			
		||||
		port_bit = BIT(i);
 | 
			
		||||
		if ((port_mask & port_bit) == 0)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (swdev->ops->get_port_link) {
 | 
			
		||||
			struct switch_port_link port_link;
 | 
			
		||||
 | 
			
		||||
			memset(&port_link, '\0', sizeof(port_link));
 | 
			
		||||
			swdev->ops->get_port_link(swdev, i, &port_link);
 | 
			
		||||
 | 
			
		||||
			if (port_link.link) {
 | 
			
		||||
				link |= port_bit;
 | 
			
		||||
				switch (port_link.speed) {
 | 
			
		||||
				case SWITCH_PORT_SPEED_UNKNOWN:
 | 
			
		||||
					sw_trig->link_speed[i] =
 | 
			
		||||
						SWCONFIG_LED_PORT_SPEED_NA;
 | 
			
		||||
					break;
 | 
			
		||||
				case SWITCH_PORT_SPEED_10:
 | 
			
		||||
					sw_trig->link_speed[i] =
 | 
			
		||||
						SWCONFIG_LED_PORT_SPEED_10;
 | 
			
		||||
					break;
 | 
			
		||||
				case SWITCH_PORT_SPEED_100:
 | 
			
		||||
					sw_trig->link_speed[i] =
 | 
			
		||||
						SWCONFIG_LED_PORT_SPEED_100;
 | 
			
		||||
					break;
 | 
			
		||||
				case SWITCH_PORT_SPEED_1000:
 | 
			
		||||
					sw_trig->link_speed[i] =
 | 
			
		||||
						SWCONFIG_LED_PORT_SPEED_1000;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (swdev->ops->get_port_stats) {
 | 
			
		||||
			struct switch_port_stats port_stats;
 | 
			
		||||
 | 
			
		||||
			memset(&port_stats, '\0', sizeof(port_stats));
 | 
			
		||||
			swdev->ops->get_port_stats(swdev, i, &port_stats);
 | 
			
		||||
			sw_trig->port_tx_traffic[i] = port_stats.tx_bytes;
 | 
			
		||||
			sw_trig->port_rx_traffic[i] = port_stats.rx_bytes;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sw_trig->port_link = link;
 | 
			
		||||
 | 
			
		||||
	swconfig_trig_update_leds(sw_trig);
 | 
			
		||||
 | 
			
		||||
	schedule_delayed_work(&sw_trig->sw_led_work,
 | 
			
		||||
			      SWCONFIG_LED_TIMER_INTERVAL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
swconfig_create_led_trigger(struct switch_dev *swdev)
 | 
			
		||||
{
 | 
			
		||||
	struct switch_led_trigger *sw_trig;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	if (!swdev->ops->get_port_link)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	sw_trig = kzalloc(sizeof(struct switch_led_trigger), GFP_KERNEL);
 | 
			
		||||
	if (!sw_trig)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	sw_trig->swdev = swdev;
 | 
			
		||||
	sw_trig->trig.name = swdev->devname;
 | 
			
		||||
	sw_trig->trig.activate = swconfig_trig_activate;
 | 
			
		||||
	sw_trig->trig.deactivate = swconfig_trig_deactivate;
 | 
			
		||||
 | 
			
		||||
	INIT_DELAYED_WORK(&sw_trig->sw_led_work, swconfig_led_work_func);
 | 
			
		||||
 | 
			
		||||
	err = led_trigger_register(&sw_trig->trig);
 | 
			
		||||
	if (err)
 | 
			
		||||
		goto err_free;
 | 
			
		||||
 | 
			
		||||
	swdev->led_trigger = sw_trig;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
err_free:
 | 
			
		||||
	kfree(sw_trig);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
swconfig_destroy_led_trigger(struct switch_dev *swdev)
 | 
			
		||||
{
 | 
			
		||||
	struct switch_led_trigger *sw_trig;
 | 
			
		||||
 | 
			
		||||
	sw_trig = swdev->led_trigger;
 | 
			
		||||
	if (sw_trig) {
 | 
			
		||||
		cancel_delayed_work_sync(&sw_trig->sw_led_work);
 | 
			
		||||
		led_trigger_unregister(&sw_trig->trig);
 | 
			
		||||
		kfree(sw_trig);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else /* SWCONFIG_LEDS */
 | 
			
		||||
static inline int
 | 
			
		||||
swconfig_create_led_trigger(struct switch_dev *swdev) { return 0; }
 | 
			
		||||
 | 
			
		||||
static inline void
 | 
			
		||||
swconfig_destroy_led_trigger(struct switch_dev *swdev) { }
 | 
			
		||||
#endif /* CONFIG_SWCONFIG_LEDS */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
menuconfig MIKROTIK
 | 
			
		||||
	bool "Platform support for MikroTik RouterBoard virtual devices"
 | 
			
		||||
	help
 | 
			
		||||
	  Say Y here to get to see options for the MikroTik RouterBoard platform.
 | 
			
		||||
	  This option alone does not add any kernel code.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if MIKROTIK
 | 
			
		||||
 | 
			
		||||
config MIKROTIK_RB_SYSFS
 | 
			
		||||
	tristate "RouterBoot sysfs support"
 | 
			
		||||
	depends on MTD
 | 
			
		||||
	select LZO_DECOMPRESS
 | 
			
		||||
	select CRC32
 | 
			
		||||
	help
 | 
			
		||||
	  This driver exposes RouterBoot configuration in sysfs.
 | 
			
		||||
 | 
			
		||||
config NVMEM_LAYOUT_MIKROTIK
 | 
			
		||||
	tristate "RouterBoot NVMEM layout support"
 | 
			
		||||
	depends on NVMEM_LAYOUTS
 | 
			
		||||
	help
 | 
			
		||||
	  This driver exposes MikroTik hard_config via NVMEM layout.
 | 
			
		||||
 | 
			
		||||
config MIKROTIK_WLAN_DECOMPRESS_LZ77
 | 
			
		||||
	tristate "Mikrotik factory Wi-Fi caldata LZ77 decompression support"
 | 
			
		||||
	depends on MIKROTIK_RB_SYSFS
 | 
			
		||||
	help
 | 
			
		||||
	  Allow Mikrotik LZ77 factory flashed Wi-Fi calibration data to be
 | 
			
		||||
	  decompressed
 | 
			
		||||
 | 
			
		||||
endif # MIKROTIK
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
#
 | 
			
		||||
# Makefile for MikroTik RouterBoard platform specific drivers
 | 
			
		||||
#
 | 
			
		||||
obj-$(CONFIG_MIKROTIK_RB_SYSFS)     += routerboot.o rb_hardconfig.o rb_softconfig.o
 | 
			
		||||
obj-$(CONFIG_NVMEM_LAYOUT_MIKROTIK)     += rb_nvmem.o
 | 
			
		||||
obj-$(CONFIG_MIKROTIK_WLAN_DECOMPRESS_LZ77)  += rb_lz77.o
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,844 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-only
 | 
			
		||||
/*
 | 
			
		||||
 * Driver for MikroTik RouterBoot hard config.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2020 Thibaut VARÈNE <hacks+kernel@slashdirt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 * by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 * This driver exposes the data encoded in the "hard_config" flash segment of
 | 
			
		||||
 * MikroTik RouterBOARDs devices. It presents the data in a sysfs folder
 | 
			
		||||
 * named "hard_config". The WLAN calibration data is available on demand via
 | 
			
		||||
 * the 'wlan_data' sysfs file in that folder.
 | 
			
		||||
 *
 | 
			
		||||
 * This driver permanently allocates a chunk of RAM as large as the hard_config
 | 
			
		||||
 * MTD partition, although it is technically possible to operate entirely from
 | 
			
		||||
 * the MTD device without using a local buffer (except when requesting WLAN
 | 
			
		||||
 * calibration data), at the cost of a performance penalty.
 | 
			
		||||
 *
 | 
			
		||||
 * Note: PAGE_SIZE is assumed to be >= 4K, hence the device attribute show
 | 
			
		||||
 * routines need not check for output overflow.
 | 
			
		||||
 *
 | 
			
		||||
 * Some constant defines extracted from routerboot.{c,h} by Gabor Juhos
 | 
			
		||||
 * <juhosg@openwrt.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/kobject.h>
 | 
			
		||||
#include <linux/bitops.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/sysfs.h>
 | 
			
		||||
#include <linux/lzo.h>
 | 
			
		||||
 | 
			
		||||
#include "rb_hardconfig.h"
 | 
			
		||||
#include "routerboot.h"
 | 
			
		||||
#include "rb_lz77.h"
 | 
			
		||||
 | 
			
		||||
#define RB_HARDCONFIG_VER		"0.08"
 | 
			
		||||
#define RB_HC_PR_PFX			"[rb_hardconfig] "
 | 
			
		||||
 | 
			
		||||
/* Bit definitions for hardware options */
 | 
			
		||||
#define RB_HW_OPT_NO_UART		BIT(0)
 | 
			
		||||
#define RB_HW_OPT_HAS_VOLTAGE		BIT(1)
 | 
			
		||||
#define RB_HW_OPT_HAS_USB		BIT(2)
 | 
			
		||||
#define RB_HW_OPT_HAS_ATTINY		BIT(3)
 | 
			
		||||
#define RB_HW_OPT_PULSE_DUTY_CYCLE	BIT(9)
 | 
			
		||||
#define RB_HW_OPT_NO_NAND		BIT(14)
 | 
			
		||||
#define RB_HW_OPT_HAS_LCD		BIT(15)
 | 
			
		||||
#define RB_HW_OPT_HAS_POE_OUT		BIT(16)
 | 
			
		||||
#define RB_HW_OPT_HAS_uSD		BIT(17)
 | 
			
		||||
#define RB_HW_OPT_HAS_SIM		BIT(18)
 | 
			
		||||
#define RB_HW_OPT_HAS_SFP		BIT(20)
 | 
			
		||||
#define RB_HW_OPT_HAS_WIFI		BIT(21)
 | 
			
		||||
#define RB_HW_OPT_HAS_TS_FOR_ADC	BIT(22)
 | 
			
		||||
#define RB_HW_OPT_HAS_PLC		BIT(29)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Tag ID values for ERD data.
 | 
			
		||||
 * Mikrotik used to pack all calibration data under a single tag id 0x1, but
 | 
			
		||||
 * recently switched to a new scheme where each radio calibration gets a
 | 
			
		||||
 * separate tag. The new scheme has tag id bit 15 always set and seems to be
 | 
			
		||||
 * mutually exclusive with the old scheme.
 | 
			
		||||
 */
 | 
			
		||||
#define RB_WLAN_ERD_ID_SOLO		0x0001
 | 
			
		||||
#define RB_WLAN_ERD_ID_MULTI_8001	0x8001
 | 
			
		||||
#define RB_WLAN_ERD_ID_MULTI_8201	0x8201
 | 
			
		||||
 | 
			
		||||
static struct kobject *hc_kobj;
 | 
			
		||||
static u8 *hc_buf;		// ro buffer after init(): no locking required
 | 
			
		||||
static size_t hc_buflen;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * For LZOR style WLAN data unpacking.
 | 
			
		||||
 * This binary blob is prepended to the data encoded on some devices as
 | 
			
		||||
 * RB_ID_WLAN_DATA, the result is then first decompressed with LZO, and then
 | 
			
		||||
 * finally RLE-decoded.
 | 
			
		||||
 * This binary blob has been extracted from RouterOS by
 | 
			
		||||
 * https://forum.openwrt.org/u/ius
 | 
			
		||||
 */
 | 
			
		||||
static const u8 hc_lzor_prefix[] = {
 | 
			
		||||
	0x00, 0x05, 0x4c, 0x4c, 0x44, 0x00, 0x34, 0xfe,
 | 
			
		||||
	0xfe, 0x34, 0x11, 0x3c, 0x1e, 0x3c, 0x2e, 0x3c,
 | 
			
		||||
	0x4c, 0x34, 0x00, 0x52, 0x62, 0x92, 0xa2, 0xb2,
 | 
			
		||||
	0xc3, 0x2a, 0x14, 0x00, 0x00, 0x05, 0xfe, 0x6a,
 | 
			
		||||
	0x3c, 0x16, 0x32, 0x16, 0x11, 0x1e, 0x12, 0x46,
 | 
			
		||||
	0x32, 0x46, 0x11, 0x4e, 0x12, 0x36, 0x32, 0x36,
 | 
			
		||||
	0x11, 0x3e, 0x12, 0x5a, 0x9a, 0x64, 0x00, 0x04,
 | 
			
		||||
	0xfe, 0x10, 0x3c, 0x00, 0x01, 0x00, 0x00, 0x28,
 | 
			
		||||
	0x0c, 0x00, 0x0f, 0xfe, 0x14, 0x00, 0x24, 0x24,
 | 
			
		||||
	0x23, 0x24, 0x24, 0x23, 0x25, 0x22, 0x21, 0x21,
 | 
			
		||||
	0x23, 0x22, 0x21, 0x22, 0x21, 0x2d, 0x38, 0x00,
 | 
			
		||||
	0x0c, 0x25, 0x25, 0x24, 0x25, 0x25, 0x24, 0x23,
 | 
			
		||||
	0x22, 0x21, 0x20, 0x23, 0x21, 0x21, 0x22, 0x21,
 | 
			
		||||
	0x2d, 0x38, 0x00, 0x28, 0xb0, 0x00, 0x00, 0x22,
 | 
			
		||||
	0x00, 0x00, 0xc0, 0xfe, 0x03, 0x00, 0xc0, 0x00,
 | 
			
		||||
	0x62, 0xff, 0x62, 0xff, 0xfe, 0x06, 0x00, 0xbb,
 | 
			
		||||
	0xff, 0xba, 0xff, 0xfe, 0x08, 0x00, 0x9e, 0xff,
 | 
			
		||||
	0xfe, 0x0a, 0x00, 0x53, 0xff, 0xfe, 0x02, 0x00,
 | 
			
		||||
	0x20, 0xff, 0xb1, 0xfe, 0xfe, 0xb2, 0xfe, 0xfe,
 | 
			
		||||
	0xed, 0xfe, 0xfe, 0xfe, 0x04, 0x00, 0x3a, 0xff,
 | 
			
		||||
	0x3a, 0xff, 0xde, 0xfd, 0x5f, 0x04, 0x33, 0xff,
 | 
			
		||||
	0x4c, 0x74, 0x03, 0x05, 0x05, 0xff, 0x6d, 0xfe,
 | 
			
		||||
	0xfe, 0x6d, 0xfe, 0xfe, 0xaf, 0x08, 0x63, 0xff,
 | 
			
		||||
	0x64, 0x6f, 0x08, 0xac, 0xff, 0xbf, 0x6d, 0x08,
 | 
			
		||||
	0x7a, 0x6d, 0x08, 0x96, 0x74, 0x04, 0x00, 0x08,
 | 
			
		||||
	0x79, 0xff, 0xda, 0xfe, 0xfe, 0xdb, 0xfe, 0xfe,
 | 
			
		||||
	0x56, 0xff, 0xfe, 0x04, 0x00, 0x5e, 0xff, 0x5e,
 | 
			
		||||
	0xff, 0x6c, 0xfe, 0xfe, 0xfe, 0x06, 0x00, 0x41,
 | 
			
		||||
	0xff, 0x7f, 0x74, 0x03, 0x00, 0x11, 0x44, 0xff,
 | 
			
		||||
	0xa9, 0xfe, 0xfe, 0xa9, 0xfe, 0xfe, 0xa5, 0x8f,
 | 
			
		||||
	0x01, 0x00, 0x08, 0x01, 0x01, 0x02, 0x04, 0x08,
 | 
			
		||||
	0x02, 0x04, 0x08, 0x08, 0x01, 0x01, 0xfe, 0x22,
 | 
			
		||||
	0x00, 0x4c, 0x60, 0x64, 0x8c, 0x90, 0xd0, 0xd4,
 | 
			
		||||
	0xd8, 0x5c, 0x10, 0x09, 0xd8, 0xff, 0xb0, 0xff,
 | 
			
		||||
	0x00, 0x00, 0xba, 0xff, 0x14, 0x00, 0xba, 0xff,
 | 
			
		||||
	0x64, 0x00, 0x00, 0x08, 0xfe, 0x06, 0x00, 0x74,
 | 
			
		||||
	0xff, 0x42, 0xff, 0xce, 0xff, 0x60, 0xff, 0x0a,
 | 
			
		||||
	0x00, 0xb4, 0x00, 0xa0, 0x00, 0xa0, 0xfe, 0x07,
 | 
			
		||||
	0x00, 0x0a, 0x00, 0xb0, 0xff, 0x96, 0x4d, 0x00,
 | 
			
		||||
	0x56, 0x57, 0x18, 0xa6, 0xff, 0x92, 0x70, 0x11,
 | 
			
		||||
	0x00, 0x12, 0x90, 0x90, 0x76, 0x5a, 0x54, 0x54,
 | 
			
		||||
	0x4c, 0x46, 0x38, 0x00, 0x10, 0x10, 0x08, 0xfe,
 | 
			
		||||
	0x05, 0x00, 0x38, 0x29, 0x25, 0x23, 0x22, 0x22,
 | 
			
		||||
	0x1f, 0x00, 0x00, 0x00, 0xf6, 0xe1, 0xdd, 0xf8,
 | 
			
		||||
	0xfe, 0x00, 0xfe, 0x15, 0x00, 0x00, 0xd0, 0x02,
 | 
			
		||||
	0x74, 0x02, 0x08, 0xf8, 0xe5, 0xde, 0x02, 0x04,
 | 
			
		||||
	0x04, 0xfd, 0x00, 0x00, 0x00, 0x07, 0x50, 0x2d,
 | 
			
		||||
	0x01, 0x90, 0x90, 0x76, 0x60, 0xb0, 0x07, 0x07,
 | 
			
		||||
	0x0c, 0x0c, 0x04, 0xfe, 0x05, 0x00, 0x66, 0x66,
 | 
			
		||||
	0x5a, 0x56, 0xbc, 0x01, 0x06, 0xfc, 0xfc, 0xf1,
 | 
			
		||||
	0xfe, 0x07, 0x00, 0x24, 0x95, 0x70, 0x64, 0x18,
 | 
			
		||||
	0x06, 0x2c, 0xff, 0xb5, 0xfe, 0xfe, 0xb5, 0xfe,
 | 
			
		||||
	0xfe, 0xe2, 0x8c, 0x24, 0x02, 0x2f, 0xff, 0x2f,
 | 
			
		||||
	0xff, 0xb4, 0x78, 0x02, 0x05, 0x73, 0xff, 0xed,
 | 
			
		||||
	0xfe, 0xfe, 0x4f, 0xff, 0x36, 0x74, 0x1e, 0x09,
 | 
			
		||||
	0x4f, 0xff, 0x50, 0xff, 0xfe, 0x16, 0x00, 0x70,
 | 
			
		||||
	0xac, 0x70, 0x8e, 0xac, 0x40, 0x0e, 0x01, 0x70,
 | 
			
		||||
	0x7f, 0x8e, 0xac, 0x6c, 0x00, 0x0b, 0xfe, 0x02,
 | 
			
		||||
	0x00, 0xfe, 0x0a, 0x2c, 0x2a, 0x2a, 0x28, 0x26,
 | 
			
		||||
	0x1e, 0x1e, 0xfe, 0x02, 0x20, 0x65, 0x20, 0x00,
 | 
			
		||||
	0x00, 0x05, 0x12, 0x00, 0x11, 0x1e, 0x11, 0x11,
 | 
			
		||||
	0x41, 0x1e, 0x41, 0x11, 0x31, 0x1e, 0x31, 0x11,
 | 
			
		||||
	0x70, 0x75, 0x7a, 0x7f, 0x84, 0x89, 0x8e, 0x93,
 | 
			
		||||
	0x98, 0x30, 0x20, 0x00, 0x02, 0x00, 0xfe, 0x06,
 | 
			
		||||
	0x3c, 0xbc, 0x32, 0x0c, 0x00, 0x00, 0x2a, 0x12,
 | 
			
		||||
	0x1e, 0x12, 0x2e, 0x12, 0xcc, 0x12, 0x11, 0x1a,
 | 
			
		||||
	0x1e, 0x1a, 0x2e, 0x1a, 0x4c, 0x10, 0x1e, 0x10,
 | 
			
		||||
	0x11, 0x18, 0x1e, 0x42, 0x1e, 0x42, 0x2e, 0x42,
 | 
			
		||||
	0xcc, 0x42, 0x11, 0x4a, 0x1e, 0x4a, 0x2e, 0x4a,
 | 
			
		||||
	0x4c, 0x40, 0x1e, 0x40, 0x11, 0x48, 0x1e, 0x32,
 | 
			
		||||
	0x1e, 0x32, 0x2e, 0x32, 0xcc, 0x32, 0x11, 0x3a,
 | 
			
		||||
	0x1e, 0x3a, 0x2e, 0x3a, 0x4c, 0x30, 0x1e, 0x30,
 | 
			
		||||
	0x11, 0x38, 0x1e, 0x27, 0x9a, 0x01, 0x9d, 0xa2,
 | 
			
		||||
	0x2f, 0x28, 0x00, 0x00, 0x46, 0xde, 0xc4, 0xbf,
 | 
			
		||||
	0xa6, 0x9d, 0x81, 0x7b, 0x5c, 0x61, 0x40, 0xc7,
 | 
			
		||||
	0xc0, 0xae, 0xa9, 0x8c, 0x83, 0x6a, 0x62, 0x50,
 | 
			
		||||
	0x3e, 0xce, 0xc2, 0xae, 0xa3, 0x8c, 0x7b, 0x6a,
 | 
			
		||||
	0x5a, 0x50, 0x35, 0xd7, 0xc2, 0xb7, 0xa4, 0x95,
 | 
			
		||||
	0x7e, 0x72, 0x5a, 0x59, 0x37, 0xfe, 0x02, 0xf8,
 | 
			
		||||
	0x8c, 0x95, 0x90, 0x8f, 0x00, 0xd7, 0xc0, 0xb7,
 | 
			
		||||
	0xa2, 0x95, 0x7b, 0x72, 0x56, 0x59, 0x32, 0xc7,
 | 
			
		||||
	0xc3, 0xae, 0xad, 0x8c, 0x85, 0x6a, 0x63, 0x50,
 | 
			
		||||
	0x3e, 0xce, 0xc3, 0xae, 0xa4, 0x8c, 0x7c, 0x6a,
 | 
			
		||||
	0x59, 0x50, 0x34, 0xd7, 0xc2, 0xb7, 0xa5, 0x95,
 | 
			
		||||
	0x7e, 0x72, 0x59, 0x59, 0x36, 0xfc, 0x05, 0x00,
 | 
			
		||||
	0x02, 0xce, 0xc5, 0xae, 0xa5, 0x95, 0x83, 0x72,
 | 
			
		||||
	0x5c, 0x59, 0x36, 0xbf, 0xc6, 0xa5, 0xab, 0x8c,
 | 
			
		||||
	0x8c, 0x6a, 0x67, 0x50, 0x41, 0x64, 0x07, 0x00,
 | 
			
		||||
	0x02, 0x95, 0x8c, 0x72, 0x65, 0x59, 0x3f, 0xce,
 | 
			
		||||
	0xc7, 0xae, 0xa8, 0x95, 0x86, 0x72, 0x5f, 0x59,
 | 
			
		||||
	0x39, 0xfe, 0x02, 0xf8, 0x8b, 0x7c, 0x0b, 0x09,
 | 
			
		||||
	0xb7, 0xc2, 0x9d, 0xa4, 0x83, 0x85, 0x6a, 0x6b,
 | 
			
		||||
	0x50, 0x44, 0xb7, 0xc1, 0x64, 0x01, 0x00, 0x06,
 | 
			
		||||
	0x61, 0x5d, 0x48, 0x3d, 0xae, 0xc4, 0x9d, 0xad,
 | 
			
		||||
	0x7b, 0x85, 0x61, 0x66, 0x48, 0x46, 0xae, 0xc3,
 | 
			
		||||
	0x95, 0xa3, 0x72, 0x7c, 0x59, 0x56, 0x38, 0x31,
 | 
			
		||||
	0x7c, 0x0b, 0x00, 0x0c, 0x96, 0x91, 0x8f, 0x00,
 | 
			
		||||
	0xb7, 0xc0, 0xa5, 0xab, 0x8c, 0x8a, 0x6a, 0x64,
 | 
			
		||||
	0x50, 0x3c, 0xb7, 0xc0, 0x9d, 0xa0, 0x83, 0x80,
 | 
			
		||||
	0x6a, 0x64, 0x50, 0x3d, 0xb7, 0xc5, 0x9d, 0xa5,
 | 
			
		||||
	0x83, 0x87, 0x6c, 0x08, 0x07, 0xae, 0xc0, 0x9d,
 | 
			
		||||
	0xa8, 0x83, 0x88, 0x6a, 0x6d, 0x50, 0x46, 0xfc,
 | 
			
		||||
	0x05, 0x00, 0x16, 0xbf, 0xc0, 0xa5, 0xa2, 0x8c,
 | 
			
		||||
	0x7f, 0x6a, 0x57, 0x50, 0x2f, 0xb7, 0xc7, 0xa5,
 | 
			
		||||
	0xb1, 0x8c, 0x8e, 0x72, 0x6d, 0x59, 0x45, 0xbf,
 | 
			
		||||
	0xc6, 0xa5, 0xa8, 0x8c, 0x87, 0x6a, 0x5f, 0x50,
 | 
			
		||||
	0x37, 0xbf, 0xc2, 0xa5, 0xa4, 0x8c, 0x83, 0x6a,
 | 
			
		||||
	0x5c, 0x50, 0x34, 0xbc, 0x05, 0x00, 0x0e, 0x90,
 | 
			
		||||
	0x00, 0xc7, 0xc2, 0xae, 0xaa, 0x95, 0x82, 0x7b,
 | 
			
		||||
	0x60, 0x61, 0x3f, 0xb7, 0xc6, 0xa5, 0xb1, 0x8c,
 | 
			
		||||
	0x8d, 0x72, 0x6b, 0x61, 0x51, 0xbf, 0xc4, 0xa5,
 | 
			
		||||
	0xa5, 0x8c, 0x82, 0x72, 0x61, 0x59, 0x39, 0x6c,
 | 
			
		||||
	0x26, 0x03, 0x95, 0x82, 0x7b, 0x61, 0x61, 0x40,
 | 
			
		||||
	0xfc, 0x05, 0x00, 0x00, 0x7e, 0xd7, 0xc3, 0xb7,
 | 
			
		||||
	0xa8, 0x9d, 0x80, 0x83, 0x5d, 0x6a, 0x3f, 0xbf,
 | 
			
		||||
	0xc7, 0xa5, 0xa8, 0x8c, 0x84, 0x72, 0x60, 0x61,
 | 
			
		||||
	0x46, 0xbf, 0xc2, 0xae, 0xb0, 0x9d, 0x92, 0x83,
 | 
			
		||||
	0x6f, 0x6a, 0x50, 0xd7, 0xc3, 0xb7, 0xa7, 0x9d,
 | 
			
		||||
	0x80, 0x83, 0x5e, 0x6a, 0x40, 0xfe, 0x02, 0xf8,
 | 
			
		||||
	0x8d, 0x96, 0x90, 0x90, 0xfe, 0x05, 0x00, 0x8a,
 | 
			
		||||
	0xc4, 0x63, 0xb8, 0x3c, 0xa6, 0x29, 0x97, 0x16,
 | 
			
		||||
	0x81, 0x84, 0xb7, 0x5b, 0xa9, 0x33, 0x94, 0x1e,
 | 
			
		||||
	0x83, 0x11, 0x70, 0xb8, 0xc2, 0x70, 0xb1, 0x4d,
 | 
			
		||||
	0xa3, 0x2a, 0x8d, 0x1b, 0x7b, 0xa8, 0xbc, 0x68,
 | 
			
		||||
	0xab, 0x47, 0x9d, 0x27, 0x87, 0x18, 0x75, 0xae,
 | 
			
		||||
	0xc6, 0x7d, 0xbb, 0x4d, 0xaa, 0x1c, 0x84, 0x11,
 | 
			
		||||
	0x72, 0xa3, 0xbb, 0x6e, 0xad, 0x3c, 0x97, 0x24,
 | 
			
		||||
	0x85, 0x16, 0x71, 0x80, 0xb2, 0x57, 0xa4, 0x30,
 | 
			
		||||
	0x8e, 0x1c, 0x7c, 0x10, 0x68, 0xbb, 0xbd, 0x75,
 | 
			
		||||
	0xac, 0x4f, 0x9e, 0x2b, 0x87, 0x1a, 0x76, 0x96,
 | 
			
		||||
	0xc5, 0x5e, 0xb5, 0x3e, 0xa5, 0x1f, 0x8c, 0x12,
 | 
			
		||||
	0x7a, 0xc1, 0xc6, 0x42, 0x9f, 0x27, 0x8c, 0x16,
 | 
			
		||||
	0x77, 0x0f, 0x67, 0x9d, 0xbc, 0x68, 0xad, 0x36,
 | 
			
		||||
	0x95, 0x20, 0x83, 0x11, 0x6d, 0x9b, 0xb8, 0x67,
 | 
			
		||||
	0xa8, 0x34, 0x90, 0x1f, 0x7c, 0x10, 0x67, 0x9e,
 | 
			
		||||
	0xc9, 0x6a, 0xbb, 0x37, 0xa4, 0x20, 0x90, 0x11,
 | 
			
		||||
	0x7b, 0xc6, 0xc8, 0x47, 0xa4, 0x2a, 0x90, 0x18,
 | 
			
		||||
	0x7b, 0x10, 0x6c, 0xae, 0xc4, 0x5d, 0xad, 0x37,
 | 
			
		||||
	0x9a, 0x1f, 0x85, 0x13, 0x75, 0x70, 0xad, 0x42,
 | 
			
		||||
	0x99, 0x25, 0x84, 0x17, 0x74, 0x0b, 0x56, 0x87,
 | 
			
		||||
	0xc8, 0x57, 0xb8, 0x2b, 0x9e, 0x19, 0x8a, 0x0d,
 | 
			
		||||
	0x74, 0xa7, 0xc8, 0x6e, 0xb9, 0x36, 0xa0, 0x1f,
 | 
			
		||||
	0x8b, 0x11, 0x75, 0x94, 0xbe, 0x4b, 0xa5, 0x2a,
 | 
			
		||||
	0x92, 0x18, 0x7c, 0x0f, 0x6b, 0xaf, 0xc0, 0x58,
 | 
			
		||||
	0xa8, 0x34, 0x94, 0x1d, 0x7d, 0x12, 0x6d, 0x82,
 | 
			
		||||
	0xc0, 0x52, 0xb0, 0x25, 0x94, 0x14, 0x7f, 0x0c,
 | 
			
		||||
	0x68, 0x84, 0xbf, 0x3e, 0xa4, 0x22, 0x8e, 0x10,
 | 
			
		||||
	0x76, 0x0b, 0x65, 0x88, 0xb6, 0x42, 0x9b, 0x26,
 | 
			
		||||
	0x87, 0x14, 0x70, 0x0c, 0x5f, 0xc5, 0xc2, 0x3e,
 | 
			
		||||
	0x97, 0x23, 0x83, 0x13, 0x6c, 0x0c, 0x5c, 0xb1,
 | 
			
		||||
	0xc9, 0x76, 0xbc, 0x4a, 0xaa, 0x20, 0x8d, 0x12,
 | 
			
		||||
	0x78, 0x93, 0xbf, 0x46, 0xa3, 0x26, 0x8d, 0x14,
 | 
			
		||||
	0x74, 0x0c, 0x62, 0xc8, 0xc4, 0x3b, 0x97, 0x21,
 | 
			
		||||
	0x82, 0x11, 0x6a, 0x0a, 0x59, 0xa3, 0xb9, 0x68,
 | 
			
		||||
	0xa9, 0x30, 0x8d, 0x1a, 0x78, 0x0f, 0x61, 0xa0,
 | 
			
		||||
	0xc9, 0x73, 0xbe, 0x50, 0xb1, 0x30, 0x9f, 0x14,
 | 
			
		||||
	0x80, 0x83, 0xb7, 0x3c, 0x9a, 0x20, 0x84, 0x0e,
 | 
			
		||||
	0x6a, 0x0a, 0x57, 0xac, 0xc2, 0x68, 0xb0, 0x2e,
 | 
			
		||||
	0x92, 0x19, 0x7c, 0x0d, 0x63, 0x93, 0xbe, 0x62,
 | 
			
		||||
	0xb0, 0x3c, 0x9e, 0x1a, 0x80, 0x0e, 0x6b, 0xbb,
 | 
			
		||||
	0x02, 0xa0, 0x02, 0xa0, 0x02, 0x6f, 0x00, 0x75,
 | 
			
		||||
	0x00, 0x75, 0x00, 0x00, 0x00, 0xad, 0x02, 0xb3,
 | 
			
		||||
	0x02, 0x6f, 0x00, 0x87, 0x00, 0x85, 0xfe, 0x03,
 | 
			
		||||
	0x00, 0xc2, 0x02, 0x82, 0x4d, 0x92, 0x6e, 0x4d,
 | 
			
		||||
	0xb1, 0xa8, 0x84, 0x01, 0x00, 0x07, 0x7e, 0x00,
 | 
			
		||||
	0xa8, 0x02, 0xa4, 0x02, 0xa4, 0x02, 0xa2, 0x00,
 | 
			
		||||
	0xa6, 0x00, 0xa6, 0x00, 0x00, 0x00, 0xb4, 0x02,
 | 
			
		||||
	0xb4, 0x02, 0x92, 0x00, 0x96, 0x00, 0x96, 0x46,
 | 
			
		||||
	0x04, 0xb0, 0x02, 0x64, 0x02, 0x0a, 0x8c, 0x00,
 | 
			
		||||
	0x90, 0x02, 0x98, 0x02, 0x98, 0x02, 0x0e, 0x01,
 | 
			
		||||
	0x11, 0x01, 0x11, 0x50, 0xc3, 0x08, 0x88, 0x02,
 | 
			
		||||
	0x88, 0x02, 0x19, 0x01, 0x02, 0x01, 0x02, 0x01,
 | 
			
		||||
	0xf3, 0x2d, 0x00, 0x00
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Array of known hw_options bits with human-friendly parsing */
 | 
			
		||||
static struct hc_hwopt {
 | 
			
		||||
	const u32 bit;
 | 
			
		||||
	const char *str;
 | 
			
		||||
} const hc_hwopts[] = {
 | 
			
		||||
	{
 | 
			
		||||
		.bit = RB_HW_OPT_NO_UART,
 | 
			
		||||
		.str = "no UART\t\t",
 | 
			
		||||
	}, {
 | 
			
		||||
		.bit = RB_HW_OPT_HAS_VOLTAGE,
 | 
			
		||||
		.str = "has Vreg\t",
 | 
			
		||||
	}, {
 | 
			
		||||
		.bit = RB_HW_OPT_HAS_USB,
 | 
			
		||||
		.str = "has usb\t\t",
 | 
			
		||||
	}, {
 | 
			
		||||
		.bit = RB_HW_OPT_HAS_ATTINY,
 | 
			
		||||
		.str = "has ATtiny\t",
 | 
			
		||||
	}, {
 | 
			
		||||
		.bit = RB_HW_OPT_NO_NAND,
 | 
			
		||||
		.str = "no NAND\t\t",
 | 
			
		||||
	}, {
 | 
			
		||||
		.bit = RB_HW_OPT_HAS_LCD,
 | 
			
		||||
		.str = "has LCD\t\t",
 | 
			
		||||
	}, {
 | 
			
		||||
		.bit = RB_HW_OPT_HAS_POE_OUT,
 | 
			
		||||
		.str = "has POE out\t",
 | 
			
		||||
	}, {
 | 
			
		||||
		.bit = RB_HW_OPT_HAS_uSD,
 | 
			
		||||
		.str = "has MicroSD\t",
 | 
			
		||||
	}, {
 | 
			
		||||
		.bit = RB_HW_OPT_HAS_SIM,
 | 
			
		||||
		.str = "has SIM\t\t",
 | 
			
		||||
	}, {
 | 
			
		||||
		.bit = RB_HW_OPT_HAS_SFP,
 | 
			
		||||
		.str = "has SFP\t\t",
 | 
			
		||||
	}, {
 | 
			
		||||
		.bit = RB_HW_OPT_HAS_WIFI,
 | 
			
		||||
		.str = "has WiFi\t",
 | 
			
		||||
	}, {
 | 
			
		||||
		.bit = RB_HW_OPT_HAS_TS_FOR_ADC,
 | 
			
		||||
		.str = "has TS ADC\t",
 | 
			
		||||
	}, {
 | 
			
		||||
		.bit = RB_HW_OPT_HAS_PLC,
 | 
			
		||||
		.str = "has PLC\t\t",
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The MAC is stored network-endian on all devices, in 2 32-bit segments:
 | 
			
		||||
 * <XX:XX:XX:XX> <XX:XX:00:00>. Kernel print has us covered.
 | 
			
		||||
 */
 | 
			
		||||
static ssize_t hc_tag_show_mac(const u8 *pld, u16 pld_len, char *buf)
 | 
			
		||||
{
 | 
			
		||||
	if (8 != pld_len)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	return sprintf(buf, "%pM\n", pld);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Print HW options in a human readable way:
 | 
			
		||||
 * The raw number and in decoded form
 | 
			
		||||
 */
 | 
			
		||||
static ssize_t hc_tag_show_hwoptions(const u8 *pld, u16 pld_len, char *buf)
 | 
			
		||||
{
 | 
			
		||||
	char *out = buf;
 | 
			
		||||
	u32 data;	// cpu-endian
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (sizeof(data) != pld_len)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	data = *(u32 *)pld;
 | 
			
		||||
	out += sprintf(out, "raw\t\t: 0x%08x\n\n", data);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(hc_hwopts); i++)
 | 
			
		||||
		out += sprintf(out, "%s: %s\n", hc_hwopts[i].str,
 | 
			
		||||
			       (data & hc_hwopts[i].bit) ? "true" : "false");
 | 
			
		||||
 | 
			
		||||
	return out - buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj,
 | 
			
		||||
				     struct bin_attribute *attr, char *buf,
 | 
			
		||||
				     loff_t off, size_t count);
 | 
			
		||||
 | 
			
		||||
static struct hc_wlan_attr {
 | 
			
		||||
	const u16 erd_tag_id;
 | 
			
		||||
	struct bin_attribute battr;
 | 
			
		||||
	u16 pld_ofs;
 | 
			
		||||
	u16 pld_len;
 | 
			
		||||
} hc_wd_multi_battrs[] = {
 | 
			
		||||
	{
 | 
			
		||||
		.erd_tag_id = RB_WLAN_ERD_ID_MULTI_8001,
 | 
			
		||||
		.battr = __BIN_ATTR(data_0, S_IRUSR, hc_wlan_data_bin_read, NULL, 0),
 | 
			
		||||
	}, {
 | 
			
		||||
		.erd_tag_id = RB_WLAN_ERD_ID_MULTI_8201,
 | 
			
		||||
		.battr = __BIN_ATTR(data_2, S_IRUSR, hc_wlan_data_bin_read, NULL, 0),
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct hc_wlan_attr hc_wd_solo_battr = {
 | 
			
		||||
	.erd_tag_id = RB_WLAN_ERD_ID_SOLO,
 | 
			
		||||
	.battr = __BIN_ATTR(wlan_data, S_IRUSR, hc_wlan_data_bin_read, NULL, 0),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static ssize_t hc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
 | 
			
		||||
			    char *buf);
 | 
			
		||||
 | 
			
		||||
/* Array of known tags to publish in sysfs */
 | 
			
		||||
static struct hc_attr {
 | 
			
		||||
	const u16 tag_id;
 | 
			
		||||
	ssize_t (* const tshow)(const u8 *pld, u16 pld_len, char *buf);
 | 
			
		||||
	struct kobj_attribute kattr;
 | 
			
		||||
	u16 pld_ofs;
 | 
			
		||||
	u16 pld_len;
 | 
			
		||||
} hc_attrs[] = {
 | 
			
		||||
	{
 | 
			
		||||
		.tag_id = RB_ID_FLASH_INFO,
 | 
			
		||||
		.tshow = routerboot_tag_show_u32s,
 | 
			
		||||
		.kattr = __ATTR(flash_info, S_IRUSR, hc_attr_show, NULL),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_ID_MAC_ADDRESS_PACK,
 | 
			
		||||
		.tshow = hc_tag_show_mac,
 | 
			
		||||
		.kattr = __ATTR(mac_base, S_IRUSR, hc_attr_show, NULL),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_ID_BOARD_PRODUCT_CODE,
 | 
			
		||||
		.tshow = routerboot_tag_show_string,
 | 
			
		||||
		.kattr = __ATTR(board_product_code, S_IRUSR, hc_attr_show, NULL),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_ID_BIOS_VERSION,
 | 
			
		||||
		.tshow = routerboot_tag_show_string,
 | 
			
		||||
		.kattr = __ATTR(booter_version, S_IRUSR, hc_attr_show, NULL),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_ID_SERIAL_NUMBER,
 | 
			
		||||
		.tshow = routerboot_tag_show_string,
 | 
			
		||||
		.kattr = __ATTR(board_serial, S_IRUSR, hc_attr_show, NULL),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_ID_MEMORY_SIZE,
 | 
			
		||||
		.tshow = routerboot_tag_show_u32s,
 | 
			
		||||
		.kattr = __ATTR(mem_size, S_IRUSR, hc_attr_show, NULL),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_ID_MAC_ADDRESS_COUNT,
 | 
			
		||||
		.tshow = routerboot_tag_show_u32s,
 | 
			
		||||
		.kattr = __ATTR(mac_count, S_IRUSR, hc_attr_show, NULL),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_ID_HW_OPTIONS,
 | 
			
		||||
		.tshow = hc_tag_show_hwoptions,
 | 
			
		||||
		.kattr = __ATTR(hw_options, S_IRUSR, hc_attr_show, NULL),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_ID_WLAN_DATA,
 | 
			
		||||
		.tshow = NULL,
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_ID_BOARD_IDENTIFIER,
 | 
			
		||||
		.tshow = routerboot_tag_show_string,
 | 
			
		||||
		.kattr = __ATTR(board_identifier, S_IRUSR, hc_attr_show, NULL),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_ID_PRODUCT_NAME,
 | 
			
		||||
		.tshow = routerboot_tag_show_string,
 | 
			
		||||
		.kattr = __ATTR(product_name, S_IRUSR, hc_attr_show, NULL),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_ID_DEFCONF,
 | 
			
		||||
		.tshow = routerboot_tag_show_string,
 | 
			
		||||
		.kattr = __ATTR(defconf, S_IRUSR, hc_attr_show, NULL),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_ID_BOARD_REVISION,
 | 
			
		||||
		.tshow = routerboot_tag_show_string,
 | 
			
		||||
		.kattr = __ATTR(board_revision, S_IRUSR, hc_attr_show, NULL),
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * If the RB_ID_WLAN_DATA payload starts with RB_MAGIC_ERD, then past
 | 
			
		||||
 * that magic number the payload itself contains a routerboot tag node
 | 
			
		||||
 * locating the LZO-compressed calibration data. So far this scheme is only
 | 
			
		||||
 * known to use a single tag at id 0x1.
 | 
			
		||||
 */
 | 
			
		||||
static int hc_wlan_data_unpack_erd(const u16 tag_id, const u8 *inbuf, size_t inlen,
 | 
			
		||||
				   void *outbuf, size_t *outlen)
 | 
			
		||||
{
 | 
			
		||||
	u16 lzo_ofs, lzo_len;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/* Find embedded tag */
 | 
			
		||||
	ret = routerboot_tag_find(inbuf, inlen, tag_id, &lzo_ofs, &lzo_len);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_debug(RB_HC_PR_PFX "no ERD data for id 0x%04x\n", tag_id);
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (lzo_len > inlen) {
 | 
			
		||||
		pr_debug(RB_HC_PR_PFX "Invalid ERD data length\n");
 | 
			
		||||
		ret = -EINVAL;
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = lzo1x_decompress_safe(inbuf+lzo_ofs, lzo_len, outbuf, outlen);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		pr_debug(RB_HC_PR_PFX "LZO decompression error (%d)\n", ret);
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * If the RB_ID_WLAN_DATA payload starts with RB_MAGIC_LZOR, then past
 | 
			
		||||
 * that magic number is a payload that must be appended to the hc_lzor_prefix,
 | 
			
		||||
 * the resulting blob is LZO-compressed.
 | 
			
		||||
 * If payload starts with RB_MAGIC_LZ77, a separate (bit level LZ77)
 | 
			
		||||
 * decompression function needs to be used. In the decompressed result,
 | 
			
		||||
 * the RB_MAGIC_ERD magic number (aligned) must be located. Following that
 | 
			
		||||
 * magic, there is one or more routerboot tag node(s) locating the RLE-encoded
 | 
			
		||||
 * calibration data payload.
 | 
			
		||||
 */
 | 
			
		||||
static int hc_wlan_data_unpack_lzor_lz77(const u16 tag_id, const u8 *inbuf, size_t inlen,
 | 
			
		||||
					 void *outbuf, size_t *outlen, u32 magic)
 | 
			
		||||
{
 | 
			
		||||
	u16 rle_ofs, rle_len;
 | 
			
		||||
	const u32 *needle;
 | 
			
		||||
	u8 *tempbuf;
 | 
			
		||||
	size_t templen, lzo_len;
 | 
			
		||||
	int ret;
 | 
			
		||||
	const char lzor[] = "LZOR";
 | 
			
		||||
	const char lz77[] = "LZ77";
 | 
			
		||||
	const char *lz_type;
 | 
			
		||||
 | 
			
		||||
	/* Temporary buffer same size as the outbuf */
 | 
			
		||||
	templen = *outlen;
 | 
			
		||||
	tempbuf = kmalloc(templen, GFP_KERNEL);
 | 
			
		||||
	if (!tempbuf)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	lzo_len = inlen;
 | 
			
		||||
	if (magic == RB_MAGIC_LZOR)
 | 
			
		||||
		lzo_len += sizeof(hc_lzor_prefix);
 | 
			
		||||
	if (lzo_len > *outlen)
 | 
			
		||||
		return -EFBIG;
 | 
			
		||||
 | 
			
		||||
	switch (magic) {
 | 
			
		||||
	case RB_MAGIC_LZOR:
 | 
			
		||||
		lz_type = lzor;
 | 
			
		||||
 | 
			
		||||
		/* Concatenate into the outbuf */
 | 
			
		||||
		memcpy(outbuf, hc_lzor_prefix, sizeof(hc_lzor_prefix));
 | 
			
		||||
		memcpy(outbuf + sizeof(hc_lzor_prefix), inbuf, inlen);
 | 
			
		||||
 | 
			
		||||
		/* LZO-decompress lzo_len bytes of outbuf into the tempbuf */
 | 
			
		||||
		ret = lzo1x_decompress_safe(outbuf, lzo_len, tempbuf, &templen);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			if (LZO_E_INPUT_NOT_CONSUMED == ret) {
 | 
			
		||||
				/*
 | 
			
		||||
				 * The tag length is always aligned thus the LZO payload may be padded,
 | 
			
		||||
				 * which can trigger a spurious error which we ignore here.
 | 
			
		||||
				 */
 | 
			
		||||
				pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n");
 | 
			
		||||
			} else {
 | 
			
		||||
				pr_debug(RB_HC_PR_PFX "LZOR: LZO decompression error (%d)\n", ret);
 | 
			
		||||
				goto fail;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case RB_MAGIC_LZ77:
 | 
			
		||||
		lz_type = lz77;
 | 
			
		||||
		/* LZO-decompress lzo_len bytes of inbuf into the tempbuf */
 | 
			
		||||
		ret = rb_lz77_decompress(inbuf, inlen, tempbuf, &templen);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			pr_err(RB_HC_PR_PFX "LZ77: LZ77 decompress error %d\n", ret);
 | 
			
		||||
			goto fail;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pr_debug(RB_HC_PR_PFX "LZ77: decompressed from %zu to %zu\n",
 | 
			
		||||
				inlen, templen);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Post decompression we have a blob (possibly byproduct of the lzo
 | 
			
		||||
	 * dictionary). We need to find RB_MAGIC_ERD. The magic number seems to
 | 
			
		||||
	 * be 32bit-aligned in the decompression output.
 | 
			
		||||
	 */
 | 
			
		||||
	needle = (const u32 *)tempbuf;
 | 
			
		||||
	while (RB_MAGIC_ERD != *needle++) {
 | 
			
		||||
		if ((u8 *)needle >= tempbuf+templen) {
 | 
			
		||||
			pr_warn(RB_HC_PR_PFX "%s: ERD magic not found. Decompressed first word: 0x%08x\n", lz_type, *(u32 *)tempbuf);
 | 
			
		||||
			ret = -ENODATA;
 | 
			
		||||
			goto fail;
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
	templen -= (u8 *)needle - tempbuf;
 | 
			
		||||
 | 
			
		||||
	/* Past magic. Look for tag node */
 | 
			
		||||
	ret = routerboot_tag_find((u8 *)needle, templen, tag_id, &rle_ofs, &rle_len);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		pr_debug(RB_HC_PR_PFX "%s: no RLE data for id 0x%04x\n", lz_type, tag_id);
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (rle_len > templen) {
 | 
			
		||||
		pr_debug(RB_HC_PR_PFX "%s: Invalid RLE data length\n", lz_type);
 | 
			
		||||
		ret = -EINVAL;
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* RLE-decode tempbuf from needle back into the outbuf */
 | 
			
		||||
	ret = routerboot_rle_decode((u8 *)needle+rle_ofs, rle_len, outbuf, outlen);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		pr_debug(RB_HC_PR_PFX "%s: RLE decoding error (%d)\n", lz_type, ret);
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
	kfree(tempbuf);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int hc_wlan_data_unpack(const u16 tag_id, const size_t tofs, size_t tlen,
 | 
			
		||||
			       void *outbuf, size_t *outlen)
 | 
			
		||||
{
 | 
			
		||||
	const u8 *lbuf;
 | 
			
		||||
	u32 magic;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/* Caller ensure tlen > 0. tofs is aligned */
 | 
			
		||||
	if ((tofs + tlen) > hc_buflen)
 | 
			
		||||
		return -EIO;
 | 
			
		||||
 | 
			
		||||
	lbuf = hc_buf + tofs;
 | 
			
		||||
	magic = *(u32 *)lbuf;
 | 
			
		||||
 | 
			
		||||
	ret = -ENODATA;
 | 
			
		||||
	switch (magic) {
 | 
			
		||||
	case RB_MAGIC_LZ77:
 | 
			
		||||
		/* no known instances of lz77 without 8001/8201 data, skip SOLO */
 | 
			
		||||
		if (tag_id == RB_WLAN_ERD_ID_SOLO) {
 | 
			
		||||
			pr_debug(RB_HC_PR_PFX "skipped LZ77 decompress in search for SOLO tag\n");
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		fallthrough;
 | 
			
		||||
	case RB_MAGIC_LZOR:
 | 
			
		||||
		/* Skip magic */
 | 
			
		||||
		lbuf += sizeof(magic);
 | 
			
		||||
		tlen -= sizeof(magic);
 | 
			
		||||
		ret = hc_wlan_data_unpack_lzor_lz77(tag_id, lbuf, tlen, outbuf, outlen, magic);
 | 
			
		||||
		break;
 | 
			
		||||
	case RB_MAGIC_ERD:
 | 
			
		||||
		/* Skip magic */
 | 
			
		||||
		lbuf += sizeof(magic);
 | 
			
		||||
		tlen -= sizeof(magic);
 | 
			
		||||
		ret = hc_wlan_data_unpack_erd(tag_id, lbuf, tlen, outbuf, outlen);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		/*
 | 
			
		||||
		 * If the RB_ID_WLAN_DATA payload doesn't start with a
 | 
			
		||||
		 * magic number, the payload itself is the raw RLE-encoded
 | 
			
		||||
		 * calibration data. Only RB_WLAN_ERD_ID_SOLO makes sense here.
 | 
			
		||||
		 */
 | 
			
		||||
		if (RB_WLAN_ERD_ID_SOLO == tag_id) {
 | 
			
		||||
			ret = routerboot_rle_decode(lbuf, tlen, outbuf, outlen);
 | 
			
		||||
			if (ret)
 | 
			
		||||
				pr_debug(RB_HC_PR_PFX "RLE decoding error (%d)\n", ret);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t hc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
 | 
			
		||||
			    char *buf)
 | 
			
		||||
{
 | 
			
		||||
	const struct hc_attr *hc_attr;
 | 
			
		||||
	const u8 *pld;
 | 
			
		||||
	u16 pld_len;
 | 
			
		||||
 | 
			
		||||
	hc_attr = container_of(attr, typeof(*hc_attr), kattr);
 | 
			
		||||
 | 
			
		||||
	if (!hc_attr->pld_len)
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	pld = hc_buf + hc_attr->pld_ofs;
 | 
			
		||||
	pld_len = hc_attr->pld_len;
 | 
			
		||||
 | 
			
		||||
	return hc_attr->tshow(pld, pld_len, buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This function will allocate and free memory every time it is called. This
 | 
			
		||||
 * is not the fastest way to do this, but since the data is rarely read (mainly
 | 
			
		||||
 * at boot time to load wlan caldata), this makes it possible to save memory for
 | 
			
		||||
 * the system.
 | 
			
		||||
 */
 | 
			
		||||
static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj,
 | 
			
		||||
				     struct bin_attribute *attr, char *buf,
 | 
			
		||||
				     loff_t off, size_t count)
 | 
			
		||||
{
 | 
			
		||||
	struct hc_wlan_attr *hc_wattr;
 | 
			
		||||
	size_t outlen;
 | 
			
		||||
	void *outbuf;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	hc_wattr = container_of(attr, typeof(*hc_wattr), battr);
 | 
			
		||||
 | 
			
		||||
	if (!hc_wattr->pld_len)
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	outlen = RB_ART_SIZE;
 | 
			
		||||
 | 
			
		||||
	/* Don't bother unpacking if the source is already too large */
 | 
			
		||||
	if (hc_wattr->pld_len > outlen)
 | 
			
		||||
		return -EFBIG;
 | 
			
		||||
 | 
			
		||||
	outbuf = kmalloc(outlen, GFP_KERNEL);
 | 
			
		||||
	if (!outbuf)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	ret = hc_wlan_data_unpack(hc_wattr->erd_tag_id, hc_wattr->pld_ofs, hc_wattr->pld_len, outbuf, &outlen);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		kfree(outbuf);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (off >= outlen) {
 | 
			
		||||
		kfree(outbuf);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (off + count > outlen)
 | 
			
		||||
		count = outlen - off;
 | 
			
		||||
 | 
			
		||||
	memcpy(buf, outbuf + off, count);
 | 
			
		||||
 | 
			
		||||
	kfree(outbuf);
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int rb_hardconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd)
 | 
			
		||||
{
 | 
			
		||||
	struct kobject *hc_wlan_kobj;
 | 
			
		||||
	size_t bytes_read, buflen, outlen;
 | 
			
		||||
	const u8 *buf;
 | 
			
		||||
	void *outbuf;
 | 
			
		||||
	int i, j, ret;
 | 
			
		||||
	u32 magic;
 | 
			
		||||
 | 
			
		||||
	hc_buf = NULL;
 | 
			
		||||
	hc_kobj = NULL;
 | 
			
		||||
	hc_wlan_kobj = NULL;
 | 
			
		||||
 | 
			
		||||
	ret = __get_mtd_device(mtd);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	hc_buflen = mtd->size;
 | 
			
		||||
	hc_buf = kmalloc(hc_buflen, GFP_KERNEL);
 | 
			
		||||
	if (!hc_buf) {
 | 
			
		||||
		__put_mtd_device(mtd);
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = mtd_read(mtd, 0, hc_buflen, &bytes_read, hc_buf);
 | 
			
		||||
	__put_mtd_device(mtd);
 | 
			
		||||
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto fail;
 | 
			
		||||
 | 
			
		||||
	if (bytes_read != hc_buflen) {
 | 
			
		||||
		ret = -EIO;
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check we have what we expect */
 | 
			
		||||
	magic = *(const u32 *)hc_buf;
 | 
			
		||||
	if (RB_MAGIC_HARD != magic) {
 | 
			
		||||
		ret = -EINVAL;
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Skip magic */
 | 
			
		||||
	buf = hc_buf + sizeof(magic);
 | 
			
		||||
	buflen = hc_buflen - sizeof(magic);
 | 
			
		||||
 | 
			
		||||
	/* Populate sysfs */
 | 
			
		||||
	ret = -ENOMEM;
 | 
			
		||||
	hc_kobj = kobject_create_and_add(RB_MTD_HARD_CONFIG, rb_kobj);
 | 
			
		||||
	if (!hc_kobj)
 | 
			
		||||
		goto fail;
 | 
			
		||||
 | 
			
		||||
	/* Locate and publish all known tags */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(hc_attrs); i++) {
 | 
			
		||||
		ret = routerboot_tag_find(buf, buflen, hc_attrs[i].tag_id,
 | 
			
		||||
					  &hc_attrs[i].pld_ofs, &hc_attrs[i].pld_len);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			hc_attrs[i].pld_ofs = hc_attrs[i].pld_len = 0;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Account for skipped magic */
 | 
			
		||||
		hc_attrs[i].pld_ofs += sizeof(magic);
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Special case RB_ID_WLAN_DATA to prep and create the binary attribute.
 | 
			
		||||
		 * We first check if the data is "old style" within a single tag (or no tag at all):
 | 
			
		||||
		 * If it is we publish this single blob as a binary attribute child of hc_kobj to
 | 
			
		||||
		 * preserve backward compatibility.
 | 
			
		||||
		 * If it isn't and instead uses multiple ERD tags, we create a subfolder and
 | 
			
		||||
		 * publish the known ones there.
 | 
			
		||||
		 */
 | 
			
		||||
		if ((RB_ID_WLAN_DATA == hc_attrs[i].tag_id) && hc_attrs[i].pld_len) {
 | 
			
		||||
			outlen = RB_ART_SIZE;
 | 
			
		||||
			outbuf = kmalloc(outlen, GFP_KERNEL);
 | 
			
		||||
			if (!outbuf) {
 | 
			
		||||
				pr_warn(RB_HC_PR_PFX "Out of memory parsing WLAN tag\n");
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* Test ID_SOLO first, if found: done */
 | 
			
		||||
			ret = hc_wlan_data_unpack(RB_WLAN_ERD_ID_SOLO, hc_attrs[i].pld_ofs, hc_attrs[i].pld_len, outbuf, &outlen);
 | 
			
		||||
			if (!ret) {
 | 
			
		||||
				hc_wd_solo_battr.pld_ofs = hc_attrs[i].pld_ofs;
 | 
			
		||||
				hc_wd_solo_battr.pld_len = hc_attrs[i].pld_len;
 | 
			
		||||
 | 
			
		||||
				ret = sysfs_create_bin_file(hc_kobj, &hc_wd_solo_battr.battr);
 | 
			
		||||
				if (ret)
 | 
			
		||||
					pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
 | 
			
		||||
						hc_wd_solo_battr.battr.attr.name, ret);
 | 
			
		||||
			}
 | 
			
		||||
			/* Otherwise, create "wlan_data" subtree and publish known data */
 | 
			
		||||
			else {
 | 
			
		||||
				hc_wlan_kobj = kobject_create_and_add("wlan_data", hc_kobj);
 | 
			
		||||
				if (!hc_wlan_kobj) {
 | 
			
		||||
					kfree(outbuf);
 | 
			
		||||
					pr_warn(RB_HC_PR_PFX "Could not create wlan_data sysfs folder\n");
 | 
			
		||||
					continue;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				for (j = 0; j < ARRAY_SIZE(hc_wd_multi_battrs); j++) {
 | 
			
		||||
					outlen = RB_ART_SIZE;
 | 
			
		||||
					ret = hc_wlan_data_unpack(hc_wd_multi_battrs[j].erd_tag_id,
 | 
			
		||||
								  hc_attrs[i].pld_ofs, hc_attrs[i].pld_len, outbuf, &outlen);
 | 
			
		||||
					if (ret) {
 | 
			
		||||
						hc_wd_multi_battrs[j].pld_ofs = hc_wd_multi_battrs[j].pld_len = 0;
 | 
			
		||||
						continue;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					hc_wd_multi_battrs[j].pld_ofs = hc_attrs[i].pld_ofs;
 | 
			
		||||
					hc_wd_multi_battrs[j].pld_len = hc_attrs[i].pld_len;
 | 
			
		||||
 | 
			
		||||
					ret = sysfs_create_bin_file(hc_wlan_kobj, &hc_wd_multi_battrs[j].battr);
 | 
			
		||||
					if (ret)
 | 
			
		||||
						pr_warn(RB_HC_PR_PFX "Could not create wlan_data/%s sysfs entry (%d)\n",
 | 
			
		||||
							hc_wd_multi_battrs[j].battr.attr.name, ret);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			kfree(outbuf);
 | 
			
		||||
		}
 | 
			
		||||
		/* All other tags are published via standard attributes */
 | 
			
		||||
		else {
 | 
			
		||||
			ret = sysfs_create_file(hc_kobj, &hc_attrs[i].kattr.attr);
 | 
			
		||||
			if (ret)
 | 
			
		||||
				pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
 | 
			
		||||
				       hc_attrs[i].kattr.attr.name, ret);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pr_info("MikroTik RouterBOARD hardware configuration sysfs driver v" RB_HARDCONFIG_VER "\n");
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
fail:
 | 
			
		||||
	kfree(hc_buf);
 | 
			
		||||
	hc_buf = NULL;
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void rb_hardconfig_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	kobject_put(hc_kobj);
 | 
			
		||||
	hc_kobj = NULL;
 | 
			
		||||
	kfree(hc_buf);
 | 
			
		||||
	hc_buf = NULL;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-only
 | 
			
		||||
/*
 | 
			
		||||
 * Common definitions for MikroTik RouterBoot hard config data.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2020 Thibaut VARÈNE <hacks+kernel@slashdirt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * Some constant defines extracted from routerboot.{c,h} by Gabor Juhos
 | 
			
		||||
 * <juhosg@openwrt.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _ROUTERBOOT_HARD_CONFIG_H_
 | 
			
		||||
#define _ROUTERBOOT_HARD_CONFIG_H_
 | 
			
		||||
 | 
			
		||||
/* ID values for hardware settings */
 | 
			
		||||
#define RB_ID_FLASH_INFO		0x03
 | 
			
		||||
#define RB_ID_MAC_ADDRESS_PACK		0x04
 | 
			
		||||
#define RB_ID_BOARD_PRODUCT_CODE	0x05
 | 
			
		||||
#define RB_ID_BIOS_VERSION		0x06
 | 
			
		||||
#define RB_ID_SDRAM_TIMINGS		0x08
 | 
			
		||||
#define RB_ID_DEVICE_TIMINGS		0x09
 | 
			
		||||
#define RB_ID_SOFTWARE_ID		0x0A
 | 
			
		||||
#define RB_ID_SERIAL_NUMBER		0x0B
 | 
			
		||||
#define RB_ID_MEMORY_SIZE		0x0D
 | 
			
		||||
#define RB_ID_MAC_ADDRESS_COUNT		0x0E
 | 
			
		||||
#define RB_ID_HW_OPTIONS		0x15
 | 
			
		||||
#define RB_ID_WLAN_DATA			0x16
 | 
			
		||||
#define RB_ID_BOARD_IDENTIFIER		0x17
 | 
			
		||||
#define RB_ID_PRODUCT_NAME		0x21
 | 
			
		||||
#define RB_ID_DEFCONF			0x26
 | 
			
		||||
#define RB_ID_BOARD_REVISION		0x27
 | 
			
		||||
 | 
			
		||||
#endif /* _ROUTERBOOT_HARD_CONFIG_H_ */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,446 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2023 John Thomson
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/minmax.h>
 | 
			
		||||
 | 
			
		||||
#include "rb_lz77.h"
 | 
			
		||||
 | 
			
		||||
#define MIKRO_LZ77 "[rb lz77] "
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The maximum number of bits used in a counter.
 | 
			
		||||
 * For the look behind window, long instruction match offsets
 | 
			
		||||
 * up to 6449 have been seen in provided compressed caldata blobs
 | 
			
		||||
 * (that would need 21 counter bits: 4 to 12 + 11 to 0).
 | 
			
		||||
 * conservative value here: 27 provides offset up to 0x8000 bytes
 | 
			
		||||
 * uses a u8 in this code
 | 
			
		||||
 */
 | 
			
		||||
#define MIKRO_LZ77_MAX_COUNT_BIT_LEN 27
 | 
			
		||||
 | 
			
		||||
enum rb_lz77_instruction {
 | 
			
		||||
	INSTR_ERROR = -1,
 | 
			
		||||
	INSTR_LITERAL_BYTE = 0,
 | 
			
		||||
	/* a (non aligned) byte follows this instruction,
 | 
			
		||||
	 * which is directly copied into output
 | 
			
		||||
	 */
 | 
			
		||||
	INSTR_PREVIOUS_OFFSET = 1,
 | 
			
		||||
	/* this group is a match, with a bytes length defined by
 | 
			
		||||
	 * following counter bits, starting at bitshift 0,
 | 
			
		||||
	 * less the built-in count of 1
 | 
			
		||||
	 * using the previous offset as source
 | 
			
		||||
	 */
 | 
			
		||||
	INSTR_LONG = 2
 | 
			
		||||
	/* this group has two counters,
 | 
			
		||||
	 * the first counter starts at bitshift 4,
 | 
			
		||||
	 *	 if this counter == 0, this is a non-matching group
 | 
			
		||||
	 *	 the second counter (bytes length) starts at bitshift 4,
 | 
			
		||||
	 *	 less the built-in count of 11+1.
 | 
			
		||||
	 *	 The final match group has this count 0,
 | 
			
		||||
	 *	 and following bits which pad to byte-alignment.
 | 
			
		||||
	 *
 | 
			
		||||
	 *	 if this counter > 0, this is a matching group
 | 
			
		||||
	 *	 this first count is the match offset (in bytes)
 | 
			
		||||
	 *	 the second count is the match length (in bytes),
 | 
			
		||||
	 *	 less the built-in count of 2
 | 
			
		||||
	 *	 these groups can source bytes that are part of this group
 | 
			
		||||
	 */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rb_lz77_instr_opcodes {
 | 
			
		||||
	/* group instruction */
 | 
			
		||||
	enum rb_lz77_instruction instruction;
 | 
			
		||||
	/* if >0, a match group,
 | 
			
		||||
	 * which starts at byte output_position - 1*offset
 | 
			
		||||
	 */
 | 
			
		||||
	size_t offset;
 | 
			
		||||
	/* how long the match group is,
 | 
			
		||||
	 * or how long the (following counter) non-match group is
 | 
			
		||||
	 */
 | 
			
		||||
	size_t length;
 | 
			
		||||
	/* how many bits were used for this instruction + op code(s) */
 | 
			
		||||
	size_t bits_used;
 | 
			
		||||
	/* input char */
 | 
			
		||||
	u8 *in;
 | 
			
		||||
	/* offset where this instruction started */
 | 
			
		||||
	size_t in_pos;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * rb_lz77_get_bit
 | 
			
		||||
 *
 | 
			
		||||
 * @in:			compressed data ptr
 | 
			
		||||
 * @in_offset_bit:	bit offset to extract
 | 
			
		||||
 *
 | 
			
		||||
 * convert the bit offset to byte offset,
 | 
			
		||||
 * shift to modulo of bits per bytes, so that wanted bit is lsb
 | 
			
		||||
 * and to extract only that bit.
 | 
			
		||||
 * Caller is responsible for ensuring that in_offset_bit/8
 | 
			
		||||
 * does not exceed input length
 | 
			
		||||
 */
 | 
			
		||||
static inline u8 rb_lz77_get_bit(const u8 *in, const size_t in_offset_bit)
 | 
			
		||||
{
 | 
			
		||||
	return ((in[in_offset_bit / BITS_PER_BYTE] >>
 | 
			
		||||
		 (in_offset_bit % BITS_PER_BYTE)) &
 | 
			
		||||
		1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * rb_lz77_get_byte
 | 
			
		||||
 *
 | 
			
		||||
 * @in:			compressed data
 | 
			
		||||
 * @in_offset_bit:	bit offset to extract byte
 | 
			
		||||
 */
 | 
			
		||||
static inline u8 rb_lz77_get_byte(const u8 *in, const size_t in_offset_bit)
 | 
			
		||||
{
 | 
			
		||||
	u8 buf = 0;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	/* built a reversed byte from (likely) unaligned bits */
 | 
			
		||||
	for (i = 0; i <= 7; ++i)
 | 
			
		||||
		buf += rb_lz77_get_bit(in, in_offset_bit + i) << (7 - i);
 | 
			
		||||
	return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * rb_lz77_decode_count - decode bits at given offset as a count
 | 
			
		||||
 *
 | 
			
		||||
 * @in:			compressed data
 | 
			
		||||
 * @in_len:		length of compressed data
 | 
			
		||||
 * @in_offset_bit:	bit offset where count starts
 | 
			
		||||
 * @shift:		left shift operand value of first count bit
 | 
			
		||||
 * @count:		initial count
 | 
			
		||||
 * @bits_used:		how many bits were consumed by this count
 | 
			
		||||
 * @max_bits:		maximum bit count for this counter
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the decoded count
 | 
			
		||||
 */
 | 
			
		||||
static int rb_lz77_decode_count(const u8 *in, const size_t in_len,
 | 
			
		||||
				const size_t in_offset_bit, u8 shift,
 | 
			
		||||
				size_t count, u8 *bits_used, const u8 max_bits)
 | 
			
		||||
{
 | 
			
		||||
	size_t pos = in_offset_bit;
 | 
			
		||||
	const size_t max_pos = min(pos + max_bits, in_len * BITS_PER_BYTE);
 | 
			
		||||
	bool up = true;
 | 
			
		||||
 | 
			
		||||
	*bits_used = 0;
 | 
			
		||||
	pr_debug(MIKRO_LZ77
 | 
			
		||||
		 "decode_count inbit: %zu, start shift:%u, initial count:%zu\n",
 | 
			
		||||
		 in_offset_bit, shift, count);
 | 
			
		||||
 | 
			
		||||
	while (true) {
 | 
			
		||||
		/* check the input offset bit does not overflow the minimum of
 | 
			
		||||
		 * a reasonable length for this encoded count, and
 | 
			
		||||
		 * the end of the input */
 | 
			
		||||
		if (unlikely(pos >= max_pos)) {
 | 
			
		||||
			pr_err(MIKRO_LZ77
 | 
			
		||||
			       "max bit index reached before count completed\n");
 | 
			
		||||
			return -EFBIG;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* if the bit value at offset is set */
 | 
			
		||||
		if (rb_lz77_get_bit(in, pos))
 | 
			
		||||
			count += (1 << shift);
 | 
			
		||||
 | 
			
		||||
		/* shift increases until we find an unsed bit */
 | 
			
		||||
		else if (up)
 | 
			
		||||
			up = false;
 | 
			
		||||
 | 
			
		||||
		if (up)
 | 
			
		||||
			++shift;
 | 
			
		||||
		else {
 | 
			
		||||
			if (!shift) {
 | 
			
		||||
				*bits_used = pos - in_offset_bit + 1;
 | 
			
		||||
				return count;
 | 
			
		||||
			}
 | 
			
		||||
			--shift;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		++pos;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * rb_lz77_decode_instruction
 | 
			
		||||
 *
 | 
			
		||||
 * @in:			compressed data
 | 
			
		||||
 * @in_offset_bit:	bit offset where instruction starts
 | 
			
		||||
 * @bits_used:		how many bits were consumed by this count
 | 
			
		||||
 *
 | 
			
		||||
 * Returns the decoded instruction
 | 
			
		||||
 */
 | 
			
		||||
static enum rb_lz77_instruction
 | 
			
		||||
rb_lz77_decode_instruction(const u8 *in, size_t in_offset_bit, u8 *bits_used)
 | 
			
		||||
{
 | 
			
		||||
	if (rb_lz77_get_bit(in, in_offset_bit)) {
 | 
			
		||||
		*bits_used = 2;
 | 
			
		||||
		if (rb_lz77_get_bit(in, ++in_offset_bit))
 | 
			
		||||
			return INSTR_LONG;
 | 
			
		||||
		else
 | 
			
		||||
			return INSTR_PREVIOUS_OFFSET;
 | 
			
		||||
	} else {
 | 
			
		||||
		*bits_used = 1;
 | 
			
		||||
		return INSTR_LITERAL_BYTE;
 | 
			
		||||
	}
 | 
			
		||||
	return INSTR_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * rb_lz77_decode_instruction_operators
 | 
			
		||||
 *
 | 
			
		||||
 * @in:			compressed data
 | 
			
		||||
 * @in_len:		length of compressed data
 | 
			
		||||
 * @in_offset_bit:	bit offset where instruction starts
 | 
			
		||||
 * @previous_offset:	last used match offset
 | 
			
		||||
 * @opcode:		struct to hold instruction & operators
 | 
			
		||||
 *
 | 
			
		||||
 * Returns error code
 | 
			
		||||
 */
 | 
			
		||||
static int rb_lz77_decode_instruction_operators(
 | 
			
		||||
	const u8 *in, const size_t in_len, const size_t in_offset_bit,
 | 
			
		||||
	const size_t previous_offset, struct rb_lz77_instr_opcodes *opcode)
 | 
			
		||||
{
 | 
			
		||||
	enum rb_lz77_instruction instruction;
 | 
			
		||||
	u8 bit_count = 0;
 | 
			
		||||
	u8 bits_used = 0;
 | 
			
		||||
	int offset = 0;
 | 
			
		||||
	int length = 0;
 | 
			
		||||
 | 
			
		||||
	instruction = rb_lz77_decode_instruction(in, in_offset_bit, &bit_count);
 | 
			
		||||
 | 
			
		||||
	/* skip bits used by instruction */
 | 
			
		||||
	bits_used += bit_count;
 | 
			
		||||
 | 
			
		||||
	switch (instruction) {
 | 
			
		||||
	case INSTR_LITERAL_BYTE:
 | 
			
		||||
		/* non-matching char */
 | 
			
		||||
		offset = 0;
 | 
			
		||||
		length = 1;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case INSTR_PREVIOUS_OFFSET:
 | 
			
		||||
		/* matching group uses previous offset */
 | 
			
		||||
		offset = previous_offset;
 | 
			
		||||
 | 
			
		||||
		length = rb_lz77_decode_count(in, in_len,
 | 
			
		||||
					      in_offset_bit + bits_used, 0, 1,
 | 
			
		||||
					      &bit_count,
 | 
			
		||||
					      MIKRO_LZ77_MAX_COUNT_BIT_LEN);
 | 
			
		||||
		if (unlikely(length < 0))
 | 
			
		||||
			return length;
 | 
			
		||||
		/* skip bits used by count */
 | 
			
		||||
		bits_used += bit_count;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case INSTR_LONG:
 | 
			
		||||
		offset = rb_lz77_decode_count(in, in_len,
 | 
			
		||||
					      in_offset_bit + bits_used, 4, 0,
 | 
			
		||||
					      &bit_count,
 | 
			
		||||
					      MIKRO_LZ77_MAX_COUNT_BIT_LEN);
 | 
			
		||||
		if (unlikely(offset < 0))
 | 
			
		||||
			return offset;
 | 
			
		||||
 | 
			
		||||
		/* skip bits used by offset count */
 | 
			
		||||
		bits_used += bit_count;
 | 
			
		||||
 | 
			
		||||
		if (offset == 0) {
 | 
			
		||||
			/* non-matching long group */
 | 
			
		||||
			length = rb_lz77_decode_count(
 | 
			
		||||
				in, in_len, in_offset_bit + bits_used, 4, 12,
 | 
			
		||||
				&bit_count, MIKRO_LZ77_MAX_COUNT_BIT_LEN);
 | 
			
		||||
			if (unlikely(length < 0))
 | 
			
		||||
				return length;
 | 
			
		||||
			/* skip bits used by length count */
 | 
			
		||||
			bits_used += bit_count;
 | 
			
		||||
		} else {
 | 
			
		||||
			/* matching group */
 | 
			
		||||
			length = rb_lz77_decode_count(
 | 
			
		||||
				in, in_len, in_offset_bit + bits_used, 0, 2,
 | 
			
		||||
				&bit_count, MIKRO_LZ77_MAX_COUNT_BIT_LEN);
 | 
			
		||||
			if (unlikely(length < 0))
 | 
			
		||||
				return length;
 | 
			
		||||
			/* skip bits used by length count */
 | 
			
		||||
			bits_used += bit_count;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case INSTR_ERROR:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	opcode->instruction = instruction;
 | 
			
		||||
	opcode->offset = offset;
 | 
			
		||||
	opcode->length = length;
 | 
			
		||||
	opcode->bits_used = bits_used;
 | 
			
		||||
	opcode->in = (u8 *)in;
 | 
			
		||||
	opcode->in_pos = in_offset_bit;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * rb_lz77_decompress
 | 
			
		||||
 *
 | 
			
		||||
 * @in:			compressed data ptr
 | 
			
		||||
 * @in_len:		length of compressed data
 | 
			
		||||
 * @out:		buffer ptr to decompress into
 | 
			
		||||
 * @out_len:		length of decompressed buffer in input,
 | 
			
		||||
 *			length of decompressed data in success
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success, or negative error
 | 
			
		||||
 */
 | 
			
		||||
int rb_lz77_decompress(const u8 *in, const size_t in_len, u8 *out,
 | 
			
		||||
		       size_t *out_len)
 | 
			
		||||
{
 | 
			
		||||
	u8 *output_ptr;
 | 
			
		||||
	size_t input_bit = 0;
 | 
			
		||||
	const u8 *output_end = out + *out_len;
 | 
			
		||||
	struct rb_lz77_instr_opcodes *opcode;
 | 
			
		||||
	size_t match_offset = 0;
 | 
			
		||||
	int rc = 0;
 | 
			
		||||
	size_t match_length, partial_count, i;
 | 
			
		||||
 | 
			
		||||
	output_ptr = out;
 | 
			
		||||
 | 
			
		||||
	if (unlikely((in_len * BITS_PER_BYTE) > SIZE_MAX)) {
 | 
			
		||||
		pr_err(MIKRO_LZ77 "input longer than expected\n");
 | 
			
		||||
		return -EFBIG;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	opcode = kmalloc(sizeof(*opcode), GFP_KERNEL);
 | 
			
		||||
	if (!opcode)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	while (true) {
 | 
			
		||||
		if (unlikely(output_ptr > output_end)) {
 | 
			
		||||
			pr_err(MIKRO_LZ77 "output overrun\n");
 | 
			
		||||
			rc = -EOVERFLOW;
 | 
			
		||||
			goto free_lz77_struct;
 | 
			
		||||
		}
 | 
			
		||||
		if (unlikely(input_bit > in_len * BITS_PER_BYTE)) {
 | 
			
		||||
			pr_err(MIKRO_LZ77 "input overrun\n");
 | 
			
		||||
			rc = -ENODATA;
 | 
			
		||||
			goto free_lz77_struct;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rc = rb_lz77_decode_instruction_operators(in, in_len, input_bit,
 | 
			
		||||
							  match_offset, opcode);
 | 
			
		||||
		if (unlikely(rc < 0)) {
 | 
			
		||||
			pr_err(MIKRO_LZ77
 | 
			
		||||
			       "instruction operands decode error\n");
 | 
			
		||||
			goto free_lz77_struct;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		pr_debug(MIKRO_LZ77 "inbit:0x%zx->outbyte:0x%zx", input_bit,
 | 
			
		||||
			 output_ptr - out);
 | 
			
		||||
 | 
			
		||||
		input_bit += opcode->bits_used;
 | 
			
		||||
		switch (opcode->instruction) {
 | 
			
		||||
		case INSTR_LITERAL_BYTE:
 | 
			
		||||
			pr_debug(" short");
 | 
			
		||||
			fallthrough;
 | 
			
		||||
		case INSTR_LONG:
 | 
			
		||||
			if (opcode->offset == 0) {
 | 
			
		||||
				/* this is a non-matching group */
 | 
			
		||||
				pr_debug(" non-match, len: 0x%zx\n",
 | 
			
		||||
					 opcode->length);
 | 
			
		||||
				/* test end marker */
 | 
			
		||||
				if (opcode->length == 0xc &&
 | 
			
		||||
				    ((input_bit +
 | 
			
		||||
				      opcode->length * BITS_PER_BYTE) >
 | 
			
		||||
				     in_len)) {
 | 
			
		||||
					*out_len = output_ptr - out;
 | 
			
		||||
					pr_debug(
 | 
			
		||||
						MIKRO_LZ77
 | 
			
		||||
						"lz77 decompressed from %zu to %zu\n",
 | 
			
		||||
						in_len, *out_len);
 | 
			
		||||
					rc = 0;
 | 
			
		||||
					goto free_lz77_struct;
 | 
			
		||||
				}
 | 
			
		||||
				for (i = opcode->length; i > 0; --i) {
 | 
			
		||||
					*output_ptr =
 | 
			
		||||
						rb_lz77_get_byte(in, input_bit);
 | 
			
		||||
					++output_ptr;
 | 
			
		||||
					input_bit += BITS_PER_BYTE;
 | 
			
		||||
				}
 | 
			
		||||
				/* do no fallthrough if a non-match group */
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
			match_offset = opcode->offset;
 | 
			
		||||
			fallthrough;
 | 
			
		||||
		case INSTR_PREVIOUS_OFFSET:
 | 
			
		||||
			match_length = opcode->length;
 | 
			
		||||
			partial_count = 0;
 | 
			
		||||
 | 
			
		||||
			pr_debug(" match, offset: 0x%zx, len: 0x%zx",
 | 
			
		||||
				 opcode->offset, match_length);
 | 
			
		||||
 | 
			
		||||
			if (unlikely(opcode->offset == 0)) {
 | 
			
		||||
				pr_err(MIKRO_LZ77
 | 
			
		||||
				       "match group missing opcode->offset\n");
 | 
			
		||||
				rc = -EBADMSG;
 | 
			
		||||
				goto free_lz77_struct;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* overflow */
 | 
			
		||||
			if (unlikely((output_ptr + match_length) >
 | 
			
		||||
				     output_end)) {
 | 
			
		||||
				pr_err(MIKRO_LZ77
 | 
			
		||||
				       "match group output overflow\n");
 | 
			
		||||
				rc = -ENOBUFS;
 | 
			
		||||
				goto free_lz77_struct;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* underflow */
 | 
			
		||||
			if (unlikely((output_ptr - opcode->offset) < out)) {
 | 
			
		||||
				pr_err(MIKRO_LZ77
 | 
			
		||||
				       "match group offset underflow\n");
 | 
			
		||||
				rc = -ESPIPE;
 | 
			
		||||
				goto free_lz77_struct;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* there are cases where the match (length) includes
 | 
			
		||||
			 * data that is a part of the same match
 | 
			
		||||
			 */
 | 
			
		||||
			while (opcode->offset < match_length) {
 | 
			
		||||
				++partial_count;
 | 
			
		||||
				memcpy(output_ptr, output_ptr - opcode->offset,
 | 
			
		||||
				       opcode->offset);
 | 
			
		||||
				output_ptr += opcode->offset;
 | 
			
		||||
				match_length -= opcode->offset;
 | 
			
		||||
			}
 | 
			
		||||
			memcpy(output_ptr, output_ptr - opcode->offset,
 | 
			
		||||
			       match_length);
 | 
			
		||||
			output_ptr += match_length;
 | 
			
		||||
			if (partial_count)
 | 
			
		||||
				pr_debug(" (%zu partial memcpy)",
 | 
			
		||||
					 partial_count);
 | 
			
		||||
			pr_debug("\n");
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case INSTR_ERROR:
 | 
			
		||||
			rc = -EINVAL;
 | 
			
		||||
			goto free_lz77_struct;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pr_err(MIKRO_LZ77 "decode loop broken\n");
 | 
			
		||||
	rc = -EINVAL;
 | 
			
		||||
 | 
			
		||||
free_lz77_struct:
 | 
			
		||||
	kfree(opcode);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(rb_lz77_decompress);
 | 
			
		||||
 | 
			
		||||
MODULE_LICENSE("GPL");
 | 
			
		||||
MODULE_DESCRIPTION("Mikrotik Wi-Fi caldata LZ77 decompressor");
 | 
			
		||||
MODULE_AUTHOR("John Thomson");
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2024 John Thomson
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __MIKROTIK_WLAN_LZ77_H__
 | 
			
		||||
#define __MIKROTIK_WLAN_LZ77_H__
 | 
			
		||||
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_MIKROTIK_WLAN_DECOMPRESS_LZ77
 | 
			
		||||
/**
 | 
			
		||||
 * rb_lz77_decompress
 | 
			
		||||
 *
 | 
			
		||||
 * @in:			compressed data ptr
 | 
			
		||||
 * @in_len:		length of compressed data
 | 
			
		||||
 * @out:		buffer ptr to decompress into
 | 
			
		||||
 * @out_len:		length of decompressed buffer in input,
 | 
			
		||||
 *			length of decompressed data in success
 | 
			
		||||
 *
 | 
			
		||||
 * Returns 0 on success, or negative error
 | 
			
		||||
 */
 | 
			
		||||
int rb_lz77_decompress(const u8 *in, const size_t in_len, u8 *out,
 | 
			
		||||
		       size_t *out_len);
 | 
			
		||||
 | 
			
		||||
#else /* CONFIG_MIKROTIK_WLAN_DECOMPRESS_LZ77 */
 | 
			
		||||
 | 
			
		||||
static inline int rb_lz77_decompress(const u8 *in, const size_t in_len, u8 *out,
 | 
			
		||||
				     size_t *out_len)
 | 
			
		||||
{
 | 
			
		||||
	return -EOPNOTSUPP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_MIKROTIK_WLAN_DECOMPRESS_LZ77 */
 | 
			
		||||
#endif /* __MIKROTIK_WLAN_LZ77_H__ */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,230 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-only
 | 
			
		||||
/*
 | 
			
		||||
 * NVMEM layout driver for MikroTik Routerboard hard config cells
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2024 Robert Marko <robimarko@gmail.com>
 | 
			
		||||
 * Based on the sysfs hard config driver by Thibaut VARÈNE <hacks+kernel@slashdirt.org>
 | 
			
		||||
 * Comments documenting the format carried over from routerboot.c
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/bitfield.h>
 | 
			
		||||
#include <linux/etherdevice.h>
 | 
			
		||||
#include <linux/mod_devicetable.h>
 | 
			
		||||
#include <linux/nvmem-consumer.h>
 | 
			
		||||
#include <linux/nvmem-provider.h>
 | 
			
		||||
#include <linux/of.h>
 | 
			
		||||
#include <linux/platform_device.h>
 | 
			
		||||
 | 
			
		||||
#include "rb_hardconfig.h"
 | 
			
		||||
#include "routerboot.h"
 | 
			
		||||
 | 
			
		||||
#define TLV_TAG_MASK 	GENMASK(15, 0)
 | 
			
		||||
#define TLV_LEN_MASK 	GENMASK(31, 16)
 | 
			
		||||
 | 
			
		||||
static const char *rb_tlv_cell_name(u16 tag)
 | 
			
		||||
{
 | 
			
		||||
	switch (tag) {
 | 
			
		||||
	case RB_ID_FLASH_INFO:
 | 
			
		||||
		return "flash-info";
 | 
			
		||||
	case RB_ID_MAC_ADDRESS_PACK:
 | 
			
		||||
		return "base-mac-address";
 | 
			
		||||
	case RB_ID_BOARD_PRODUCT_CODE:
 | 
			
		||||
		return "board-product-code";
 | 
			
		||||
	case RB_ID_BIOS_VERSION:
 | 
			
		||||
		return "booter-version";
 | 
			
		||||
	case RB_ID_SERIAL_NUMBER:
 | 
			
		||||
		return "board-serial";
 | 
			
		||||
	case RB_ID_MEMORY_SIZE:
 | 
			
		||||
		return "mem-size";
 | 
			
		||||
	case RB_ID_MAC_ADDRESS_COUNT:
 | 
			
		||||
		return "mac-count";
 | 
			
		||||
	case RB_ID_HW_OPTIONS:
 | 
			
		||||
		return "hw-options";
 | 
			
		||||
	case RB_ID_WLAN_DATA:
 | 
			
		||||
		return "wlan-data";
 | 
			
		||||
	case RB_ID_BOARD_IDENTIFIER:
 | 
			
		||||
		return "board-identifier";
 | 
			
		||||
	case RB_ID_PRODUCT_NAME:
 | 
			
		||||
		return "product-name";
 | 
			
		||||
	case RB_ID_DEFCONF:
 | 
			
		||||
		return "defconf";
 | 
			
		||||
	case RB_ID_BOARD_REVISION:
 | 
			
		||||
		return "board-revision";
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int rb_tlv_mac_read_cb(void *priv, const char *id, int index,
 | 
			
		||||
			      unsigned int offset, void *buf,
 | 
			
		||||
			      size_t bytes)
 | 
			
		||||
{
 | 
			
		||||
	if (index < 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (!is_valid_ether_addr(buf))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	eth_addr_add(buf, index);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static nvmem_cell_post_process_t rb_tlv_read_cb(u16 tag)
 | 
			
		||||
{
 | 
			
		||||
	switch (tag) {
 | 
			
		||||
	case RB_ID_MAC_ADDRESS_PACK:
 | 
			
		||||
		return &rb_tlv_mac_read_cb;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int rb_add_cells(struct device *dev, struct nvmem_device *nvmem,
 | 
			
		||||
			const size_t data_len, u8 *data)
 | 
			
		||||
{
 | 
			
		||||
	u32 node, offset = sizeof(RB_MAGIC_HARD);
 | 
			
		||||
	struct nvmem_cell_info cell = {};
 | 
			
		||||
	struct device_node *layout;
 | 
			
		||||
	u16 tlv_tag, tlv_len;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	layout = of_nvmem_layout_get_container(nvmem);
 | 
			
		||||
	if (!layout)
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Routerboot tag nodes are u32 values:
 | 
			
		||||
	 * - The low nibble is the tag identification number,
 | 
			
		||||
	 * - The high nibble is the tag payload length (node excluded) in bytes.
 | 
			
		||||
	 * Tag nodes are CPU-endian.
 | 
			
		||||
	 * Tag nodes are 32bit-aligned.
 | 
			
		||||
	 *
 | 
			
		||||
	 * The payload immediately follows the tag node.
 | 
			
		||||
	 * Payload offset will always be aligned. while length may not end on 32bit
 | 
			
		||||
	 * boundary (the only known case is when parsing ERD data).
 | 
			
		||||
	 * The payload is CPU-endian when applicable.
 | 
			
		||||
	 * Tag nodes are not ordered (by ID) on flash.
 | 
			
		||||
	 */
 | 
			
		||||
	while ((offset + sizeof(node)) <= data_len) {
 | 
			
		||||
		node = *((const u32 *) (data + offset));
 | 
			
		||||
		/* Tag list ends with null node */
 | 
			
		||||
		if (!node)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		tlv_tag = FIELD_GET(TLV_TAG_MASK, node);
 | 
			
		||||
		tlv_len = FIELD_GET(TLV_LEN_MASK, node);
 | 
			
		||||
 | 
			
		||||
		offset += sizeof(node);
 | 
			
		||||
		if (offset + tlv_len > data_len) {
 | 
			
		||||
			dev_err(dev, "Out of bounds field (0x%x bytes at 0x%x)\n",
 | 
			
		||||
			        tlv_len, offset);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cell.name = rb_tlv_cell_name(tlv_tag);
 | 
			
		||||
		if (!cell.name)
 | 
			
		||||
			goto skip;
 | 
			
		||||
 | 
			
		||||
		cell.offset = offset;
 | 
			
		||||
		/*
 | 
			
		||||
		 * MikroTik stores MAC-s with length of 8 bytes,
 | 
			
		||||
		 * but kernel expects it to be ETH_ALEN (6 bytes),
 | 
			
		||||
		 * so we need to make sure that is the case.
 | 
			
		||||
		 */
 | 
			
		||||
		if (tlv_tag == RB_ID_MAC_ADDRESS_PACK)
 | 
			
		||||
			cell.bytes = ETH_ALEN;
 | 
			
		||||
		else
 | 
			
		||||
			cell.bytes = tlv_len;
 | 
			
		||||
		cell.np = of_get_child_by_name(layout, cell.name);
 | 
			
		||||
		cell.read_post_process = rb_tlv_read_cb(tlv_tag);
 | 
			
		||||
 | 
			
		||||
		ret = nvmem_add_one_cell(nvmem, &cell);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			of_node_put(layout);
 | 
			
		||||
			return ret;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * The only known situation where len may not end on 32bit
 | 
			
		||||
		 * boundary is within ERD data. Since we're only extracting
 | 
			
		||||
		 * one tag (the first and only one) from that data, we should
 | 
			
		||||
		 * never need to forcefully ALIGN(). Do it anyway, this is not a
 | 
			
		||||
		 * performance path.
 | 
			
		||||
		 */
 | 
			
		||||
skip:
 | 
			
		||||
		offset += ALIGN(tlv_len, sizeof(offset));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	of_node_put(layout);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int rb_parse_table(struct nvmem_layout *layout)
 | 
			
		||||
{
 | 
			
		||||
	struct nvmem_device *nvmem = layout->nvmem;
 | 
			
		||||
	struct device *dev = &layout->dev;
 | 
			
		||||
	size_t mtd_size;
 | 
			
		||||
	u8 *data;
 | 
			
		||||
	u32 hdr;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = nvmem_device_read(nvmem, 0, sizeof(hdr), &hdr);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	if (hdr != RB_MAGIC_HARD) {
 | 
			
		||||
		dev_err(dev, "Invalid header\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mtd_size = nvmem_dev_size(nvmem);
 | 
			
		||||
 | 
			
		||||
	data = devm_kmalloc(dev, mtd_size, GFP_KERNEL);
 | 
			
		||||
	if (!data)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	ret = nvmem_device_read(nvmem, 0, mtd_size, data);
 | 
			
		||||
	if (ret != mtd_size)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	return rb_add_cells(dev, nvmem, mtd_size, data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int rb_nvmem_probe(struct nvmem_layout *layout)
 | 
			
		||||
{
 | 
			
		||||
	layout->add_cells = rb_parse_table;
 | 
			
		||||
 | 
			
		||||
	return nvmem_layout_register(layout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void rb_nvmem_remove(struct nvmem_layout *layout)
 | 
			
		||||
{
 | 
			
		||||
	nvmem_layout_unregister(layout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id rb_nvmem_of_match_table[] = {
 | 
			
		||||
	{ .compatible = "mikrotik,routerboot-nvmem", },
 | 
			
		||||
	{},
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, rb_nvmem_of_match_table);
 | 
			
		||||
 | 
			
		||||
static struct nvmem_layout_driver rb_nvmem_layout = {
 | 
			
		||||
	.probe = rb_nvmem_probe,
 | 
			
		||||
	.remove = rb_nvmem_remove,
 | 
			
		||||
	.driver = {
 | 
			
		||||
		.owner = THIS_MODULE,
 | 
			
		||||
		.name = "rb_nvmem",
 | 
			
		||||
		.of_match_table = rb_nvmem_of_match_table,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
module_nvmem_layout_driver(rb_nvmem_layout);
 | 
			
		||||
 | 
			
		||||
MODULE_LICENSE("GPL");
 | 
			
		||||
MODULE_AUTHOR("Robert Marko <robimarko@gmail.com>");
 | 
			
		||||
MODULE_DESCRIPTION("NVMEM layout driver for MikroTik Routerboard hard config cells");
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,795 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-only
 | 
			
		||||
/*
 | 
			
		||||
 * Driver for MikroTik RouterBoot soft config.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2020 Thibaut VARÈNE <hacks+kernel@slashdirt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 * by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 * This driver exposes the data encoded in the "soft_config" flash segment of
 | 
			
		||||
 * MikroTik RouterBOARDs devices. It presents the data in a sysfs folder
 | 
			
		||||
 * named "soft_config". The data is presented in a user/machine-friendly way
 | 
			
		||||
 * with just as much parsing as can be generalized across mikrotik platforms
 | 
			
		||||
 * (as inferred from reverse-engineering).
 | 
			
		||||
 *
 | 
			
		||||
 * The known soft_config tags are presented in the "soft_config" sysfs folder,
 | 
			
		||||
 * with the addition of one specific file named "commit", which is only
 | 
			
		||||
 * available if the driver supports writes to the mtd device: no modifications
 | 
			
		||||
 * made to any of the other attributes are actually written back to flash media
 | 
			
		||||
 * until a true value is input into this file (e.g. [Yy1]). This is to avoid
 | 
			
		||||
 * unnecessary flash wear, and to permit to revert all changes by issuing a
 | 
			
		||||
 * false value ([Nn0]). Reading the content of this file shows the current
 | 
			
		||||
 * status of the driver: if the data in sysfs matches the content of the
 | 
			
		||||
 * soft_config partition, the file will read "clean". Otherwise, it will read
 | 
			
		||||
 * "dirty".
 | 
			
		||||
 *
 | 
			
		||||
 * The writeable sysfs files presented by this driver will accept only inputs
 | 
			
		||||
 * which are in a valid range for the given tag. As a design choice, the driver
 | 
			
		||||
 * will not assess whether the inputs are identical to the existing data.
 | 
			
		||||
 *
 | 
			
		||||
 * Note: PAGE_SIZE is assumed to be >= 4K, hence the device attribute show
 | 
			
		||||
 * routines need not check for output overflow.
 | 
			
		||||
 *
 | 
			
		||||
 * Some constant defines extracted from rbcfg.h by Gabor Juhos
 | 
			
		||||
 * <juhosg@openwrt.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/errno.h>
 | 
			
		||||
#include <linux/kobject.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/sysfs.h>
 | 
			
		||||
#include <linux/version.h>
 | 
			
		||||
#include <linux/capability.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <linux/crc32.h>
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_ATH79
 | 
			
		||||
 #include <asm/mach-ath79/ath79.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "routerboot.h"
 | 
			
		||||
 | 
			
		||||
#define RB_SOFTCONFIG_VER		"0.05"
 | 
			
		||||
#define RB_SC_PR_PFX			"[rb_softconfig] "
 | 
			
		||||
 | 
			
		||||
#define RB_SC_HAS_WRITE_SUPPORT	true
 | 
			
		||||
#define RB_SC_WMODE			S_IWUSR
 | 
			
		||||
#define RB_SC_RMODE			S_IRUSR
 | 
			
		||||
 | 
			
		||||
/* ID values for software settings */
 | 
			
		||||
#define RB_SCID_UART_SPEED		0x01	// u32*1
 | 
			
		||||
#define RB_SCID_BOOT_DELAY		0x02	// u32*1
 | 
			
		||||
#define RB_SCID_BOOT_DEVICE		0x03	// u32*1
 | 
			
		||||
#define RB_SCID_BOOT_KEY		0x04	// u32*1
 | 
			
		||||
#define RB_SCID_CPU_MODE		0x05	// u32*1
 | 
			
		||||
#define RB_SCID_BIOS_VERSION		0x06	// str
 | 
			
		||||
#define RB_SCID_BOOT_PROTOCOL		0x09	// u32*1
 | 
			
		||||
#define RB_SCID_CPU_FREQ_IDX		0x0C	// u32*1
 | 
			
		||||
#define RB_SCID_BOOTER			0x0D	// u32*1
 | 
			
		||||
#define RB_SCID_SILENT_BOOT		0x0F	// u32*1
 | 
			
		||||
/*
 | 
			
		||||
 * protected_routerboot seems to use tag 0x1F. It only works in combination with
 | 
			
		||||
 * RouterOS, resulting in a wiped board otherwise, so it's not implemented here.
 | 
			
		||||
 * The tag values are as follows:
 | 
			
		||||
 * - off: 0x0
 | 
			
		||||
 * - on: the lower halfword encodes the max value in s for the reset feature,
 | 
			
		||||
 *	 the higher halfword encodes the min value in s for the reset feature.
 | 
			
		||||
 * Default value when on: 0x00140258: 0x14 = 20s / 0x258= 600s
 | 
			
		||||
 * See details here: https://wiki.mikrotik.com/wiki/Manual:RouterBOARD_settings#Protected_bootloader
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* Tag values */
 | 
			
		||||
 | 
			
		||||
#define RB_UART_SPEED_115200		0
 | 
			
		||||
#define RB_UART_SPEED_57600		1
 | 
			
		||||
#define RB_UART_SPEED_38400		2
 | 
			
		||||
#define RB_UART_SPEED_19200		3
 | 
			
		||||
#define RB_UART_SPEED_9600		4
 | 
			
		||||
#define RB_UART_SPEED_4800		5
 | 
			
		||||
#define RB_UART_SPEED_2400		6
 | 
			
		||||
#define RB_UART_SPEED_1200		7
 | 
			
		||||
#define RB_UART_SPEED_OFF		8
 | 
			
		||||
 | 
			
		||||
/* valid boot delay: 1 - 9s in 1s increment */
 | 
			
		||||
#define RB_BOOT_DELAY_MIN		1
 | 
			
		||||
#define RB_BOOT_DELAY_MAX		9
 | 
			
		||||
 | 
			
		||||
#define RB_BOOT_DEVICE_ETHER		0	// "boot over Ethernet"
 | 
			
		||||
#define RB_BOOT_DEVICE_NANDETH		1	// "boot from NAND, if fail then Ethernet"
 | 
			
		||||
#define RB_BOOT_DEVICE_CFCARD		2	// (not available in rbcfg)
 | 
			
		||||
#define RB_BOOT_DEVICE_ETHONCE		3	// "boot Ethernet once, then NAND"
 | 
			
		||||
#define RB_BOOT_DEVICE_NANDONLY		5	// "boot from NAND only"
 | 
			
		||||
#define RB_BOOT_DEVICE_FLASHCFG		7	// "boot in flash configuration mode"
 | 
			
		||||
#define RB_BOOT_DEVICE_FLSHONCE		8	// "boot in flash configuration mode once, then NAND"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ATH79 9xxx CPU frequency indices.
 | 
			
		||||
 * It is unknown if they apply to all ATH79 RBs, and some do not seem to feature
 | 
			
		||||
 * the upper levels (QCA955x), while F is presumably AR9344-only.
 | 
			
		||||
 */
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_9X_A	(0 << 3)
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_9X_B	(1 << 3)	// 0x8
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_9X_C	(2 << 3)	// 0x10 - factory freq for many devices
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_9X_D	(3 << 3)	// 0x18
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_9X_E	(4 << 3)	// 0x20
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_9X_F	(5 << 3)	// 0x28
 | 
			
		||||
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_9X_MIN		0	// all devices support lowest setting
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_9X_AR9334_MAX	5	// stops at F
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_9X_QCA953X_MAX	4	// stops at E
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_9X_QCA9556_MAX	2	// stops at C
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_9X_QCA9558_MAX	3	// stops at D
 | 
			
		||||
 | 
			
		||||
/* ATH79 7xxx CPU frequency indices. */
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_7X_A	((0 * 9) << 4)
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_7X_B	((1 * 9) << 4)
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_7X_C	((2 * 9) << 4)
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_7X_D	((3 * 9) << 4)
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_7X_E	((4 * 9) << 4)
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_7X_F	((5 * 9) << 4)
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_7X_G	((6 * 9) << 4)
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_7X_H	((7 * 9) << 4)
 | 
			
		||||
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_7X_MIN		0	// all devices support lowest setting
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_7X_AR724X_MAX	3	// stops at D
 | 
			
		||||
#define RB_CPU_FREQ_IDX_ATH79_7X_AR7161_MAX	7	// stops at H - check if applies to all AR71xx devices
 | 
			
		||||
 | 
			
		||||
#define RB_SC_CRC32_OFFSET		4	// located right after magic
 | 
			
		||||
 | 
			
		||||
static struct kobject *sc_kobj;
 | 
			
		||||
static u8 *sc_buf;
 | 
			
		||||
static size_t sc_buflen;
 | 
			
		||||
static rwlock_t sc_bufrwl;		// rw lock to sc_buf
 | 
			
		||||
 | 
			
		||||
/* MUST be used with lock held */
 | 
			
		||||
#define RB_SC_CLRCRC()		*(u32 *)(sc_buf + RB_SC_CRC32_OFFSET) = 0
 | 
			
		||||
#define RB_SC_GETCRC()		*(u32 *)(sc_buf + RB_SC_CRC32_OFFSET)
 | 
			
		||||
#define RB_SC_SETCRC(_crc)	*(u32 *)(sc_buf + RB_SC_CRC32_OFFSET) = (_crc)
 | 
			
		||||
 | 
			
		||||
struct sc_u32tvs {
 | 
			
		||||
	const u32 val;
 | 
			
		||||
	const char *str;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define RB_SC_TVS(_val, _str) {		\
 | 
			
		||||
	.val = (_val),			\
 | 
			
		||||
	.str = (_str),			\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t sc_tag_show_u32tvs(const u8 *pld, u16 pld_len, char *buf,
 | 
			
		||||
				  const struct sc_u32tvs tvs[], const int tvselmts)
 | 
			
		||||
{
 | 
			
		||||
	const char *fmt;
 | 
			
		||||
	char *out = buf;
 | 
			
		||||
	u32 data;	// cpu-endian
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	// fallback to raw hex output if we can't handle the input
 | 
			
		||||
	if (tvselmts < 0)
 | 
			
		||||
		return routerboot_tag_show_u32s(pld, pld_len, buf);
 | 
			
		||||
 | 
			
		||||
	if (sizeof(data) != pld_len)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	read_lock(&sc_bufrwl);
 | 
			
		||||
	data = *(u32 *)pld;		// pld aliases sc_buf
 | 
			
		||||
	read_unlock(&sc_bufrwl);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < tvselmts; i++) {
 | 
			
		||||
		fmt = (tvs[i].val == data) ? "[%s] " : "%s ";
 | 
			
		||||
		out += sprintf(out, fmt, tvs[i].str);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out += sprintf(out, "\n");
 | 
			
		||||
	return out - buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t sc_tag_store_u32tvs(const u8 *pld, u16 pld_len, const char *buf, size_t count,
 | 
			
		||||
				   const struct sc_u32tvs tvs[], const int tvselmts)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (tvselmts < 0)
 | 
			
		||||
		return tvselmts;
 | 
			
		||||
 | 
			
		||||
	if (sizeof(u32) != pld_len)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < tvselmts; i++) {
 | 
			
		||||
		if (sysfs_streq(buf, tvs[i].str)) {
 | 
			
		||||
			write_lock(&sc_bufrwl);
 | 
			
		||||
			*(u32 *)pld = tvs[i].val;	// pld aliases sc_buf
 | 
			
		||||
			RB_SC_CLRCRC();
 | 
			
		||||
			write_unlock(&sc_bufrwl);
 | 
			
		||||
			return count;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct sc_boolts {
 | 
			
		||||
	const char *strfalse;
 | 
			
		||||
	const char *strtrue;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static ssize_t sc_tag_show_boolts(const u8 *pld, u16 pld_len, char *buf,
 | 
			
		||||
				  const struct sc_boolts *bts)
 | 
			
		||||
{
 | 
			
		||||
	const char *fmt;
 | 
			
		||||
	char *out = buf;
 | 
			
		||||
	u32 data;	// cpu-endian
 | 
			
		||||
 | 
			
		||||
	if (sizeof(data) != pld_len)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	read_lock(&sc_bufrwl);
 | 
			
		||||
	data = *(u32 *)pld;		// pld aliases sc_buf
 | 
			
		||||
	read_unlock(&sc_bufrwl);
 | 
			
		||||
 | 
			
		||||
	fmt = (data) ? "%s [%s]\n" : "[%s] %s\n";
 | 
			
		||||
	out += sprintf(out, fmt, bts->strfalse, bts->strtrue);
 | 
			
		||||
 | 
			
		||||
	return out - buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t sc_tag_store_boolts(const u8 *pld, u16 pld_len, const char *buf, size_t count,
 | 
			
		||||
				   const struct sc_boolts *bts)
 | 
			
		||||
{
 | 
			
		||||
	u32 data;	// cpu-endian
 | 
			
		||||
 | 
			
		||||
	if (sizeof(data) != pld_len)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (sysfs_streq(buf, bts->strfalse))
 | 
			
		||||
		data = 0;
 | 
			
		||||
	else if (sysfs_streq(buf, bts->strtrue))
 | 
			
		||||
		data = 1;
 | 
			
		||||
	else
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	write_lock(&sc_bufrwl);
 | 
			
		||||
	*(u32 *)pld = data;		// pld aliases sc_buf
 | 
			
		||||
	RB_SC_CLRCRC();
 | 
			
		||||
	write_unlock(&sc_bufrwl);
 | 
			
		||||
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
static struct sc_u32tvs const sc_uartspeeds[] = {
 | 
			
		||||
	RB_SC_TVS(RB_UART_SPEED_OFF,	"off"),
 | 
			
		||||
	RB_SC_TVS(RB_UART_SPEED_1200,	"1200"),
 | 
			
		||||
	RB_SC_TVS(RB_UART_SPEED_2400,	"2400"),
 | 
			
		||||
	RB_SC_TVS(RB_UART_SPEED_4800,	"4800"),
 | 
			
		||||
	RB_SC_TVS(RB_UART_SPEED_9600,	"9600"),
 | 
			
		||||
	RB_SC_TVS(RB_UART_SPEED_19200,	"19200"),
 | 
			
		||||
	RB_SC_TVS(RB_UART_SPEED_38400,	"38400"),
 | 
			
		||||
	RB_SC_TVS(RB_UART_SPEED_57600,	"57600"),
 | 
			
		||||
	RB_SC_TVS(RB_UART_SPEED_115200,	"115200"),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * While the defines are carried over from rbcfg, use strings that more clearly
 | 
			
		||||
 * show the actual setting purpose (especially since the NAND* settings apply
 | 
			
		||||
 * to both nand- and nor-based devices). "cfcard" was disabled in rbcfg: disable
 | 
			
		||||
 * it here too.
 | 
			
		||||
 */
 | 
			
		||||
static struct sc_u32tvs const sc_bootdevices[] = {
 | 
			
		||||
	RB_SC_TVS(RB_BOOT_DEVICE_ETHER,		"eth"),
 | 
			
		||||
	RB_SC_TVS(RB_BOOT_DEVICE_NANDETH,	"flasheth"),
 | 
			
		||||
	//RB_SC_TVS(RB_BOOT_DEVICE_CFCARD,	"cfcard"),
 | 
			
		||||
	RB_SC_TVS(RB_BOOT_DEVICE_ETHONCE,	"ethonce"),
 | 
			
		||||
	RB_SC_TVS(RB_BOOT_DEVICE_NANDONLY,	"flash"),
 | 
			
		||||
	RB_SC_TVS(RB_BOOT_DEVICE_FLASHCFG,	"cfg"),
 | 
			
		||||
	RB_SC_TVS(RB_BOOT_DEVICE_FLSHONCE,	"cfgonce"),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct sc_boolts const sc_bootkey = {
 | 
			
		||||
	.strfalse = "any",
 | 
			
		||||
	.strtrue = "del",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct sc_boolts const sc_cpumode = {
 | 
			
		||||
	.strfalse = "powersave",
 | 
			
		||||
	.strtrue = "regular",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct sc_boolts const sc_bootproto = {
 | 
			
		||||
	.strfalse = "bootp",
 | 
			
		||||
	.strtrue = "dhcp",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct sc_boolts const sc_booter = {
 | 
			
		||||
	.strfalse = "regular",
 | 
			
		||||
	.strtrue = "backup",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct sc_boolts const sc_silent_boot = {
 | 
			
		||||
	.strfalse = "off",
 | 
			
		||||
	.strtrue = "on",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define SC_TAG_SHOW_STORE_U32TVS_FUNCS(_name)		\
 | 
			
		||||
static ssize_t sc_tag_show_##_name(const u8 *pld, u16 pld_len, char *buf)	\
 | 
			
		||||
{										\
 | 
			
		||||
	return sc_tag_show_u32tvs(pld, pld_len, buf, sc_##_name, ARRAY_SIZE(sc_##_name));	\
 | 
			
		||||
}										\
 | 
			
		||||
static ssize_t sc_tag_store_##_name(const u8 *pld, u16 pld_len, const char *buf, size_t count)	\
 | 
			
		||||
{										\
 | 
			
		||||
	return sc_tag_store_u32tvs(pld, pld_len, buf, count, sc_##_name, ARRAY_SIZE(sc_##_name));	\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define SC_TAG_SHOW_STORE_BOOLTS_FUNCS(_name)		\
 | 
			
		||||
static ssize_t sc_tag_show_##_name(const u8 *pld, u16 pld_len, char *buf)	\
 | 
			
		||||
{										\
 | 
			
		||||
	return sc_tag_show_boolts(pld, pld_len, buf, &sc_##_name);	\
 | 
			
		||||
}										\
 | 
			
		||||
static ssize_t sc_tag_store_##_name(const u8 *pld, u16 pld_len, const char *buf, size_t count)	\
 | 
			
		||||
{										\
 | 
			
		||||
	return sc_tag_store_boolts(pld, pld_len, buf, count, &sc_##_name);	\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SC_TAG_SHOW_STORE_U32TVS_FUNCS(uartspeeds)
 | 
			
		||||
SC_TAG_SHOW_STORE_U32TVS_FUNCS(bootdevices)
 | 
			
		||||
SC_TAG_SHOW_STORE_BOOLTS_FUNCS(bootkey)
 | 
			
		||||
SC_TAG_SHOW_STORE_BOOLTS_FUNCS(cpumode)
 | 
			
		||||
SC_TAG_SHOW_STORE_BOOLTS_FUNCS(bootproto)
 | 
			
		||||
SC_TAG_SHOW_STORE_BOOLTS_FUNCS(booter)
 | 
			
		||||
SC_TAG_SHOW_STORE_BOOLTS_FUNCS(silent_boot)
 | 
			
		||||
 | 
			
		||||
static ssize_t sc_tag_show_bootdelays(const u8 *pld, u16 pld_len, char *buf)
 | 
			
		||||
{
 | 
			
		||||
	const char *fmt;
 | 
			
		||||
	char *out = buf;
 | 
			
		||||
	u32 data;	// cpu-endian
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (sizeof(data) != pld_len)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	read_lock(&sc_bufrwl);
 | 
			
		||||
	data = *(u32 *)pld;		// pld aliases sc_buf
 | 
			
		||||
	read_unlock(&sc_bufrwl);
 | 
			
		||||
 | 
			
		||||
	for (i = RB_BOOT_DELAY_MIN; i <= RB_BOOT_DELAY_MAX; i++) {
 | 
			
		||||
		fmt = (i == data) ? "[%d] " : "%d ";
 | 
			
		||||
		out += sprintf(out, fmt, i);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out += sprintf(out, "\n");
 | 
			
		||||
	return out - buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t sc_tag_store_bootdelays(const u8 *pld, u16 pld_len, const char *buf, size_t count)
 | 
			
		||||
{
 | 
			
		||||
	u32 data;	// cpu-endian
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (sizeof(data) != pld_len)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	ret = kstrtou32(buf, 10, &data);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	if ((data < RB_BOOT_DELAY_MIN) || (RB_BOOT_DELAY_MAX < data))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	write_lock(&sc_bufrwl);
 | 
			
		||||
	*(u32 *)pld = data;		// pld aliases sc_buf
 | 
			
		||||
	RB_SC_CLRCRC();
 | 
			
		||||
	write_unlock(&sc_bufrwl);
 | 
			
		||||
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Support CPU frequency accessors only when the tag format has been asserted */
 | 
			
		||||
#if defined(CONFIG_ATH79)
 | 
			
		||||
/* Use the same letter-based nomenclature as RouterBOOT */
 | 
			
		||||
static struct sc_u32tvs const sc_cpufreq_indexes_ath79_9x[] = {
 | 
			
		||||
	RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_9X_A,	"a"),
 | 
			
		||||
	RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_9X_B,	"b"),
 | 
			
		||||
	RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_9X_C,	"c"),
 | 
			
		||||
	RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_9X_D,	"d"),
 | 
			
		||||
	RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_9X_E,	"e"),
 | 
			
		||||
	RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_9X_F,	"f"),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct sc_u32tvs const sc_cpufreq_indexes_ath79_7x[] = {
 | 
			
		||||
	RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_A,	"a"),
 | 
			
		||||
	RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_B,	"b"),
 | 
			
		||||
	RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_C,	"c"),
 | 
			
		||||
	RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_D,	"d"),
 | 
			
		||||
	RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_E,	"e"),
 | 
			
		||||
	RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_F,	"f"),
 | 
			
		||||
	RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_G,	"g"),
 | 
			
		||||
	RB_SC_TVS(RB_CPU_FREQ_IDX_ATH79_7X_H,	"h"),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int sc_tag_cpufreq_ath79_arraysize(void)
 | 
			
		||||
{
 | 
			
		||||
	int idx_max;
 | 
			
		||||
 | 
			
		||||
	if (ATH79_SOC_AR7161 == ath79_soc)
 | 
			
		||||
		idx_max = RB_CPU_FREQ_IDX_ATH79_7X_AR7161_MAX+1;
 | 
			
		||||
	else if (soc_is_ar724x())
 | 
			
		||||
		idx_max = RB_CPU_FREQ_IDX_ATH79_7X_AR724X_MAX+1;
 | 
			
		||||
	else if (soc_is_ar9344())
 | 
			
		||||
		idx_max = RB_CPU_FREQ_IDX_ATH79_9X_AR9334_MAX+1;
 | 
			
		||||
	else if (soc_is_qca953x())
 | 
			
		||||
		idx_max = RB_CPU_FREQ_IDX_ATH79_9X_QCA953X_MAX+1;
 | 
			
		||||
	else if (soc_is_qca9556())
 | 
			
		||||
		idx_max = RB_CPU_FREQ_IDX_ATH79_9X_QCA9556_MAX+1;
 | 
			
		||||
	else if (soc_is_qca9558())
 | 
			
		||||
		idx_max = RB_CPU_FREQ_IDX_ATH79_9X_QCA9558_MAX+1;
 | 
			
		||||
	else
 | 
			
		||||
		idx_max = -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	return idx_max;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t sc_tag_show_cpufreq_indexes(const u8 *pld, u16 pld_len, char *buf)
 | 
			
		||||
{
 | 
			
		||||
	const struct sc_u32tvs *tvs;
 | 
			
		||||
 | 
			
		||||
	if (soc_is_ar71xx() || soc_is_ar724x())
 | 
			
		||||
		tvs = sc_cpufreq_indexes_ath79_7x;
 | 
			
		||||
	else
 | 
			
		||||
		tvs = sc_cpufreq_indexes_ath79_9x;
 | 
			
		||||
 | 
			
		||||
	return sc_tag_show_u32tvs(pld, pld_len, buf, tvs, sc_tag_cpufreq_ath79_arraysize());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t sc_tag_store_cpufreq_indexes(const u8 *pld, u16 pld_len, const char *buf, size_t count)
 | 
			
		||||
{
 | 
			
		||||
	const struct sc_u32tvs *tvs;
 | 
			
		||||
 | 
			
		||||
	if (soc_is_ar71xx() || soc_is_ar724x())
 | 
			
		||||
		tvs = sc_cpufreq_indexes_ath79_7x;
 | 
			
		||||
	else
 | 
			
		||||
		tvs = sc_cpufreq_indexes_ath79_9x;
 | 
			
		||||
 | 
			
		||||
	return sc_tag_store_u32tvs(pld, pld_len, buf, count, tvs, sc_tag_cpufreq_ath79_arraysize());
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
 /* By default we only show the raw value to help with reverse-engineering */
 | 
			
		||||
 #define sc_tag_show_cpufreq_indexes	routerboot_tag_show_u32s
 | 
			
		||||
 #define sc_tag_store_cpufreq_indexes	NULL
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static ssize_t sc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
 | 
			
		||||
			    char *buf);
 | 
			
		||||
static ssize_t sc_attr_store(struct kobject *kobj, struct kobj_attribute *attr,
 | 
			
		||||
			     const char *buf, size_t count);
 | 
			
		||||
 | 
			
		||||
/* Array of known tags to publish in sysfs */
 | 
			
		||||
static struct sc_attr {
 | 
			
		||||
	const u16 tag_id;
 | 
			
		||||
	/* sysfs tag show attribute. Must lock sc_buf when dereferencing pld */
 | 
			
		||||
	ssize_t (* const tshow)(const u8 *pld, u16 pld_len, char *buf);
 | 
			
		||||
	/* sysfs tag store attribute. Must lock sc_buf when dereferencing pld */
 | 
			
		||||
	ssize_t (* const tstore)(const u8 *pld, u16 pld_len, const char *buf, size_t count);
 | 
			
		||||
	struct kobj_attribute kattr;
 | 
			
		||||
	u16 pld_ofs;
 | 
			
		||||
	u16 pld_len;
 | 
			
		||||
} sc_attrs[] = {
 | 
			
		||||
	{
 | 
			
		||||
		.tag_id = RB_SCID_UART_SPEED,
 | 
			
		||||
		.tshow = sc_tag_show_uartspeeds,
 | 
			
		||||
		.tstore = sc_tag_store_uartspeeds,
 | 
			
		||||
		.kattr = __ATTR(uart_speed, RB_SC_RMODE|RB_SC_WMODE, sc_attr_show, sc_attr_store),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_SCID_BOOT_DELAY,
 | 
			
		||||
		.tshow = sc_tag_show_bootdelays,
 | 
			
		||||
		.tstore = sc_tag_store_bootdelays,
 | 
			
		||||
		.kattr = __ATTR(boot_delay, RB_SC_RMODE|RB_SC_WMODE, sc_attr_show, sc_attr_store),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_SCID_BOOT_DEVICE,
 | 
			
		||||
		.tshow = sc_tag_show_bootdevices,
 | 
			
		||||
		.tstore = sc_tag_store_bootdevices,
 | 
			
		||||
		.kattr = __ATTR(boot_device, RB_SC_RMODE|RB_SC_WMODE, sc_attr_show, sc_attr_store),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_SCID_BOOT_KEY,
 | 
			
		||||
		.tshow = sc_tag_show_bootkey,
 | 
			
		||||
		.tstore = sc_tag_store_bootkey,
 | 
			
		||||
		.kattr = __ATTR(boot_key, RB_SC_RMODE|RB_SC_WMODE, sc_attr_show, sc_attr_store),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_SCID_CPU_MODE,
 | 
			
		||||
		.tshow = sc_tag_show_cpumode,
 | 
			
		||||
		.tstore = sc_tag_store_cpumode,
 | 
			
		||||
		.kattr = __ATTR(cpu_mode, RB_SC_RMODE|RB_SC_WMODE, sc_attr_show, sc_attr_store),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_SCID_BIOS_VERSION,
 | 
			
		||||
		.tshow = routerboot_tag_show_string,
 | 
			
		||||
		.tstore = NULL,
 | 
			
		||||
		.kattr = __ATTR(bios_version, RB_SC_RMODE, sc_attr_show, NULL),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_SCID_BOOT_PROTOCOL,
 | 
			
		||||
		.tshow = sc_tag_show_bootproto,
 | 
			
		||||
		.tstore = sc_tag_store_bootproto,
 | 
			
		||||
		.kattr = __ATTR(boot_proto, RB_SC_RMODE|RB_SC_WMODE, sc_attr_show, sc_attr_store),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_SCID_CPU_FREQ_IDX,
 | 
			
		||||
		.tshow = sc_tag_show_cpufreq_indexes,
 | 
			
		||||
		.tstore = sc_tag_store_cpufreq_indexes,
 | 
			
		||||
		.kattr = __ATTR(cpufreq_index, RB_SC_RMODE|RB_SC_WMODE, sc_attr_show, sc_attr_store),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_SCID_BOOTER,
 | 
			
		||||
		.tshow = sc_tag_show_booter,
 | 
			
		||||
		.tstore = sc_tag_store_booter,
 | 
			
		||||
		.kattr = __ATTR(booter, RB_SC_RMODE|RB_SC_WMODE, sc_attr_show, sc_attr_store),
 | 
			
		||||
	}, {
 | 
			
		||||
		.tag_id = RB_SCID_SILENT_BOOT,
 | 
			
		||||
		.tshow = sc_tag_show_silent_boot,
 | 
			
		||||
		.tstore = sc_tag_store_silent_boot,
 | 
			
		||||
		.kattr = __ATTR(silent_boot, RB_SC_RMODE|RB_SC_WMODE, sc_attr_show, sc_attr_store),
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static ssize_t sc_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
 | 
			
		||||
			    char *buf)
 | 
			
		||||
{
 | 
			
		||||
	const struct sc_attr *sc_attr;
 | 
			
		||||
	const u8 *pld;
 | 
			
		||||
	u16 pld_len;
 | 
			
		||||
 | 
			
		||||
	sc_attr = container_of(attr, typeof(*sc_attr), kattr);
 | 
			
		||||
 | 
			
		||||
	if (!sc_attr->pld_len)
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	pld = sc_buf + sc_attr->pld_ofs;	// pld aliases sc_buf -> lock!
 | 
			
		||||
	pld_len = sc_attr->pld_len;
 | 
			
		||||
 | 
			
		||||
	return sc_attr->tshow(pld, pld_len, buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t sc_attr_store(struct kobject *kobj, struct kobj_attribute *attr,
 | 
			
		||||
			     const char *buf, size_t count)
 | 
			
		||||
{
 | 
			
		||||
	const struct sc_attr *sc_attr;
 | 
			
		||||
	const u8 *pld;
 | 
			
		||||
	u16 pld_len;
 | 
			
		||||
 | 
			
		||||
	if (!RB_SC_HAS_WRITE_SUPPORT)
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	if (!capable(CAP_SYS_ADMIN))
 | 
			
		||||
		return -EACCES;
 | 
			
		||||
 | 
			
		||||
	sc_attr = container_of(attr, typeof(*sc_attr), kattr);
 | 
			
		||||
 | 
			
		||||
	if (!sc_attr->tstore)
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	if (!sc_attr->pld_len)
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	pld = sc_buf + sc_attr->pld_ofs;	// pld aliases sc_buf -> lock!
 | 
			
		||||
	pld_len = sc_attr->pld_len;
 | 
			
		||||
 | 
			
		||||
	return sc_attr->tstore(pld, pld_len, buf, count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Shows the current buffer status:
 | 
			
		||||
 * "clean": the buffer is in sync with the mtd data
 | 
			
		||||
 * "dirty": the buffer is out of sync with the mtd data
 | 
			
		||||
 */
 | 
			
		||||
static ssize_t sc_commit_show(struct kobject *kobj, struct kobj_attribute *attr,
 | 
			
		||||
			      char *buf)
 | 
			
		||||
{
 | 
			
		||||
	const char *str;
 | 
			
		||||
	char *out = buf;
 | 
			
		||||
	u32 crc;
 | 
			
		||||
 | 
			
		||||
	read_lock(&sc_bufrwl);
 | 
			
		||||
	crc = RB_SC_GETCRC();
 | 
			
		||||
	read_unlock(&sc_bufrwl);
 | 
			
		||||
 | 
			
		||||
	str = (crc) ? "clean" : "dirty";
 | 
			
		||||
	out += sprintf(out, "%s\n", str);
 | 
			
		||||
 | 
			
		||||
	return out - buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Performs buffer flushing:
 | 
			
		||||
 * This routine expects an input compatible with kstrtobool().
 | 
			
		||||
 * - a "false" input discards the current changes and reads data back from mtd.
 | 
			
		||||
 * - a "true" input commits the current changes to mtd.
 | 
			
		||||
 * If there is no pending changes, this routine is a no-op.
 | 
			
		||||
 * Handling failures is left as an exercise to userspace.
 | 
			
		||||
 */
 | 
			
		||||
static ssize_t sc_commit_store(struct kobject *kobj, struct kobj_attribute *attr,
 | 
			
		||||
			      const char *buf, size_t count)
 | 
			
		||||
{
 | 
			
		||||
	struct mtd_info *mtd;
 | 
			
		||||
	struct erase_info ei;
 | 
			
		||||
	size_t bytes_rw, ret = count;
 | 
			
		||||
	bool flush;
 | 
			
		||||
	u32 crc;
 | 
			
		||||
 | 
			
		||||
	if (!RB_SC_HAS_WRITE_SUPPORT)
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	read_lock(&sc_bufrwl);
 | 
			
		||||
	crc = RB_SC_GETCRC();
 | 
			
		||||
	read_unlock(&sc_bufrwl);
 | 
			
		||||
 | 
			
		||||
	if (crc)
 | 
			
		||||
		return count;	// NO-OP
 | 
			
		||||
 | 
			
		||||
	ret = kstrtobool(buf, &flush);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	mtd = get_mtd_device_nm(RB_MTD_SOFT_CONFIG);	// TODO allow override
 | 
			
		||||
	if (IS_ERR(mtd))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	write_lock(&sc_bufrwl);
 | 
			
		||||
	if (!flush)	// reread
 | 
			
		||||
		ret = mtd_read(mtd, 0, mtd->size, &bytes_rw, sc_buf);
 | 
			
		||||
	else {	// crc32 + commit
 | 
			
		||||
		/*
 | 
			
		||||
		 * CRC32 is computed on the entire buffer, excluding the CRC
 | 
			
		||||
		 * value itself. CRC is already null when we reach this point,
 | 
			
		||||
		 * so we can compute the CRC32 on the buffer as is.
 | 
			
		||||
		 * The expected CRC32 is Ethernet FCS style, meaning the seed is
 | 
			
		||||
		 * ~0 and the final result is also bitflipped.
 | 
			
		||||
		 */
 | 
			
		||||
 | 
			
		||||
		crc = ~crc32(~0, sc_buf, sc_buflen);
 | 
			
		||||
		RB_SC_SETCRC(crc);
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * The soft_config partition is assumed to be entirely contained
 | 
			
		||||
		 * in a single eraseblock.
 | 
			
		||||
		 */
 | 
			
		||||
 | 
			
		||||
		ei.addr = 0;
 | 
			
		||||
		ei.len = mtd->size;
 | 
			
		||||
		ret = mtd_erase(mtd, &ei);
 | 
			
		||||
		if (!ret)
 | 
			
		||||
			ret = mtd_write(mtd, 0, mtd->size, &bytes_rw, sc_buf);
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Handling mtd_write() failure here is a tricky situation. The
 | 
			
		||||
		 * proposed approach is to let userspace deal with retrying,
 | 
			
		||||
		 * with the caveat that it must try to flush the buffer again as
 | 
			
		||||
		 * rereading the mtd contents could potentially read garbage.
 | 
			
		||||
		 * The rationale is: even if we keep a shadow buffer of the
 | 
			
		||||
		 * original content, there is no guarantee that we will ever be
 | 
			
		||||
		 * able to write it anyway.
 | 
			
		||||
		 * Regardless, it appears that RouterBOOT will ignore an invalid
 | 
			
		||||
		 * soft_config (including a completely wiped segment) and will
 | 
			
		||||
		 * write back factory defaults when it happens.
 | 
			
		||||
		 */
 | 
			
		||||
	}
 | 
			
		||||
	write_unlock(&sc_bufrwl);
 | 
			
		||||
 | 
			
		||||
	put_mtd_device(mtd);
 | 
			
		||||
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto mtdfail;
 | 
			
		||||
 | 
			
		||||
	if (bytes_rw != sc_buflen) {
 | 
			
		||||
		ret = -EIO;
 | 
			
		||||
		goto mtdfail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return count;
 | 
			
		||||
 | 
			
		||||
mtdfail:
 | 
			
		||||
	RB_SC_CLRCRC();	// mark buffer content as dirty/invalid
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct kobj_attribute sc_kattrcommit = __ATTR(commit, RB_SC_RMODE|RB_SC_WMODE, sc_commit_show, sc_commit_store);
 | 
			
		||||
 | 
			
		||||
int rb_softconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd)
 | 
			
		||||
{
 | 
			
		||||
	size_t bytes_read, buflen;
 | 
			
		||||
	const u8 *buf;
 | 
			
		||||
	int i, ret;
 | 
			
		||||
	u32 magic;
 | 
			
		||||
 | 
			
		||||
	sc_buf = NULL;
 | 
			
		||||
	sc_kobj = NULL;
 | 
			
		||||
 | 
			
		||||
	ret = __get_mtd_device(mtd);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	sc_buflen = mtd->size;
 | 
			
		||||
	sc_buf = kmalloc(sc_buflen, GFP_KERNEL);
 | 
			
		||||
	if (!sc_buf) {
 | 
			
		||||
		__put_mtd_device(mtd);
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = mtd_read(mtd, 0, sc_buflen, &bytes_read, sc_buf);
 | 
			
		||||
	__put_mtd_device(mtd);
 | 
			
		||||
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto fail;
 | 
			
		||||
 | 
			
		||||
	if (bytes_read != sc_buflen) {
 | 
			
		||||
		ret = -EIO;
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check we have what we expect */
 | 
			
		||||
	magic = *(const u32 *)sc_buf;
 | 
			
		||||
	if (RB_MAGIC_SOFT != magic) {
 | 
			
		||||
		ret = -EINVAL;
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Skip magic and 32bit CRC located immediately after */
 | 
			
		||||
	buf = sc_buf + (sizeof(magic) + sizeof(u32));
 | 
			
		||||
	buflen = sc_buflen - (sizeof(magic) + sizeof(u32));
 | 
			
		||||
 | 
			
		||||
	/* Populate sysfs */
 | 
			
		||||
	ret = -ENOMEM;
 | 
			
		||||
	sc_kobj = kobject_create_and_add(RB_MTD_SOFT_CONFIG, rb_kobj);
 | 
			
		||||
	if (!sc_kobj)
 | 
			
		||||
		goto fail;
 | 
			
		||||
 | 
			
		||||
	rwlock_init(&sc_bufrwl);
 | 
			
		||||
 | 
			
		||||
	/* Locate and publish all known tags */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(sc_attrs); i++) {
 | 
			
		||||
		ret = routerboot_tag_find(buf, buflen, sc_attrs[i].tag_id,
 | 
			
		||||
					  &sc_attrs[i].pld_ofs, &sc_attrs[i].pld_len);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			sc_attrs[i].pld_ofs = sc_attrs[i].pld_len = 0;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Account for skipped magic and crc32 */
 | 
			
		||||
		sc_attrs[i].pld_ofs += sizeof(magic) + sizeof(u32);
 | 
			
		||||
 | 
			
		||||
		ret = sysfs_create_file(sc_kobj, &sc_attrs[i].kattr.attr);
 | 
			
		||||
		if (ret)
 | 
			
		||||
			pr_warn(RB_SC_PR_PFX "Could not create %s sysfs entry (%d)\n",
 | 
			
		||||
			       sc_attrs[i].kattr.attr.name, ret);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Finally add the 'commit' attribute */
 | 
			
		||||
	if (RB_SC_HAS_WRITE_SUPPORT) {
 | 
			
		||||
		ret = sysfs_create_file(sc_kobj, &sc_kattrcommit.attr);
 | 
			
		||||
		if (ret) {
 | 
			
		||||
			pr_err(RB_SC_PR_PFX "Could not create %s sysfs entry (%d), aborting!\n",
 | 
			
		||||
			       sc_kattrcommit.attr.name, ret);
 | 
			
		||||
			goto sysfsfail;	// required attribute
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pr_info("MikroTik RouterBOARD software configuration sysfs driver v" RB_SOFTCONFIG_VER "\n");
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
sysfsfail:
 | 
			
		||||
	kobject_put(sc_kobj);
 | 
			
		||||
	sc_kobj = NULL;
 | 
			
		||||
fail:
 | 
			
		||||
	kfree(sc_buf);
 | 
			
		||||
	sc_buf = NULL;
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void rb_softconfig_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	kobject_put(sc_kobj);
 | 
			
		||||
	sc_kobj = NULL;
 | 
			
		||||
	kfree(sc_buf);
 | 
			
		||||
	sc_buf = NULL;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,251 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-only
 | 
			
		||||
/*
 | 
			
		||||
 * Driver for MikroTik RouterBoot flash data. Common routines.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2020 Thibaut VARÈNE <hacks+kernel@slashdirt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 * by the Free Software Foundation.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/sysfs.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
 | 
			
		||||
#include "routerboot.h"
 | 
			
		||||
 | 
			
		||||
static struct kobject *rb_kobj;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * routerboot_tag_find() - Locate a given tag in routerboot config data.
 | 
			
		||||
 * @bufhead: the buffer to look into. Must start with a tag node.
 | 
			
		||||
 * @buflen: size of bufhead
 | 
			
		||||
 * @tag_id: the tag identifier to look for
 | 
			
		||||
 * @pld_ofs: will be updated with tag payload offset in bufhead, if tag found
 | 
			
		||||
 * @pld_len: will be updated with tag payload size, if tag found
 | 
			
		||||
 *
 | 
			
		||||
 * This incarnation of tag_find() does only that: it finds a specific routerboot
 | 
			
		||||
 * tag node in the input buffer. Routerboot tag nodes are u32 values:
 | 
			
		||||
 * - The low nibble is the tag identification number,
 | 
			
		||||
 * - The high nibble is the tag payload length (node excluded) in bytes.
 | 
			
		||||
 * The payload immediately follows the tag node. Tag nodes are 32bit-aligned.
 | 
			
		||||
 * The returned pld_ofs will always be aligned. pld_len may not end on 32bit
 | 
			
		||||
 * boundary (the only known case is when parsing ERD data).
 | 
			
		||||
 * The nodes are cpu-endian on the flash media. The payload is cpu-endian when
 | 
			
		||||
 * applicable. Tag nodes are not ordered (by ID) on flash.
 | 
			
		||||
 *
 | 
			
		||||
 * Return: 0 on success (tag found) or errno
 | 
			
		||||
 */
 | 
			
		||||
int routerboot_tag_find(const u8 *bufhead, const size_t buflen, const u16 tag_id,
 | 
			
		||||
			u16 *pld_ofs, u16 *pld_len)
 | 
			
		||||
{
 | 
			
		||||
	const u32 *datum, *bufend;
 | 
			
		||||
	u32 node;
 | 
			
		||||
	u16 id, len;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (!bufhead || !tag_id)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	ret = -ENOENT;
 | 
			
		||||
	datum = (const u32 *)bufhead;
 | 
			
		||||
	bufend = (const u32 *)(bufhead + buflen);
 | 
			
		||||
 | 
			
		||||
	while (datum < bufend) {
 | 
			
		||||
		node = *datum++;
 | 
			
		||||
 | 
			
		||||
		/* Tag list ends with null node */
 | 
			
		||||
		if (!node)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		id = node & 0xFFFF;
 | 
			
		||||
		len = node >> 16;
 | 
			
		||||
 | 
			
		||||
		if (tag_id == id) {
 | 
			
		||||
			if (datum >= bufend)
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			if (pld_ofs)
 | 
			
		||||
				*pld_ofs = (u16)((u8 *)datum - bufhead);
 | 
			
		||||
			if (pld_len)
 | 
			
		||||
				*pld_len = len;
 | 
			
		||||
 | 
			
		||||
			ret = 0;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * The only known situation where len may not end on 32bit
 | 
			
		||||
		 * boundary is within ERD data. Since we're only extracting
 | 
			
		||||
		 * one tag (the first and only one) from that data, we should
 | 
			
		||||
		 * never need to forcefully ALIGN(). Do it anyway, this is not a
 | 
			
		||||
		 * performance path.
 | 
			
		||||
		 */
 | 
			
		||||
		len = ALIGN(len, sizeof(*datum));
 | 
			
		||||
		datum += len / sizeof(*datum);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * routerboot_rle_decode() - Simple RLE (MikroTik variant) decoding routine.
 | 
			
		||||
 * @in: input buffer to decode
 | 
			
		||||
 * @inlen: size of in
 | 
			
		||||
 * @out: output buffer to write decoded data to
 | 
			
		||||
 * @outlen: pointer to out size when function is called, will be updated with
 | 
			
		||||
 * size of decoded output on return
 | 
			
		||||
 *
 | 
			
		||||
 * MikroTik's variant of RLE operates as follows, considering a signed run byte:
 | 
			
		||||
 * - positive run => classic RLE
 | 
			
		||||
 * - negative run => the next -<run> bytes must be copied verbatim
 | 
			
		||||
 * The API is matched to the lzo1x routines for convenience.
 | 
			
		||||
 *
 | 
			
		||||
 * NB: The output buffer cannot overlap with the input buffer.
 | 
			
		||||
 *
 | 
			
		||||
 * Return: 0 on success or errno
 | 
			
		||||
 */
 | 
			
		||||
int routerboot_rle_decode(const u8 *in, size_t inlen, u8 *out, size_t *outlen)
 | 
			
		||||
{
 | 
			
		||||
	int ret, run, nbytes;	// use native types for speed
 | 
			
		||||
	u8 byte;
 | 
			
		||||
 | 
			
		||||
	if (!in || (inlen < 2) || !out)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	ret = -ENOSPC;
 | 
			
		||||
	nbytes = 0;
 | 
			
		||||
	while (inlen >= 2) {
 | 
			
		||||
		run = *in++;
 | 
			
		||||
		inlen--;
 | 
			
		||||
 | 
			
		||||
		/* Verbatim copies */
 | 
			
		||||
		if (run & 0x80) {
 | 
			
		||||
			/* Invert run byte sign */
 | 
			
		||||
			run = ~run & 0xFF;
 | 
			
		||||
			run++;
 | 
			
		||||
 | 
			
		||||
			if (run > inlen)
 | 
			
		||||
				goto fail;
 | 
			
		||||
 | 
			
		||||
			inlen -= run;
 | 
			
		||||
 | 
			
		||||
			nbytes += run;
 | 
			
		||||
			if (nbytes > *outlen)
 | 
			
		||||
				goto fail;
 | 
			
		||||
 | 
			
		||||
			/* Basic memcpy */
 | 
			
		||||
			while (run-- > 0)
 | 
			
		||||
				*out++ = *in++;
 | 
			
		||||
		}
 | 
			
		||||
		/* Stream of half-words RLE: <run><byte>. run == 0 is ignored */
 | 
			
		||||
		else {
 | 
			
		||||
			byte = *in++;
 | 
			
		||||
			inlen--;
 | 
			
		||||
 | 
			
		||||
			nbytes += run;
 | 
			
		||||
			if (nbytes > *outlen)
 | 
			
		||||
				goto fail;
 | 
			
		||||
 | 
			
		||||
			while (run-- > 0)
 | 
			
		||||
				*out++ = byte;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = 0;
 | 
			
		||||
fail:
 | 
			
		||||
	*outlen = nbytes;
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void routerboot_mtd_notifier_add(struct mtd_info *mtd)
 | 
			
		||||
{
 | 
			
		||||
	/* Currently routerboot is only known to live on NOR flash */
 | 
			
		||||
	if (mtd->type != MTD_NORFLASH)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * We ignore the following return values and always register.
 | 
			
		||||
	 * These init() routines are designed so that their failed state is
 | 
			
		||||
	 * always manageable by the corresponding exit() calls.
 | 
			
		||||
	 * Notifier is called with MTD mutex held: use __get/__put variants.
 | 
			
		||||
	 * TODO: allow partition names override
 | 
			
		||||
	 */
 | 
			
		||||
	if (!strcmp(mtd->name, RB_MTD_HARD_CONFIG))
 | 
			
		||||
		rb_hardconfig_init(rb_kobj, mtd);
 | 
			
		||||
	else if (!strcmp(mtd->name, RB_MTD_SOFT_CONFIG))
 | 
			
		||||
		rb_softconfig_init(rb_kobj, mtd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void routerboot_mtd_notifier_remove(struct mtd_info *mtd)
 | 
			
		||||
{
 | 
			
		||||
	if (mtd->type != MTD_NORFLASH)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!strcmp(mtd->name, RB_MTD_HARD_CONFIG))
 | 
			
		||||
		rb_hardconfig_exit();
 | 
			
		||||
	else if (!strcmp(mtd->name, RB_MTD_SOFT_CONFIG))
 | 
			
		||||
		rb_softconfig_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Note: using a notifier prevents qualifying init()/exit() functions with __init/__exit */
 | 
			
		||||
static struct mtd_notifier routerboot_mtd_notifier = {
 | 
			
		||||
	.add = routerboot_mtd_notifier_add,
 | 
			
		||||
	.remove = routerboot_mtd_notifier_remove,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init routerboot_init(void)
 | 
			
		||||
{
 | 
			
		||||
	rb_kobj = kobject_create_and_add("mikrotik", firmware_kobj);
 | 
			
		||||
	if (!rb_kobj)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	register_mtd_user(&routerboot_mtd_notifier);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __exit routerboot_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	unregister_mtd_user(&routerboot_mtd_notifier);
 | 
			
		||||
	/* Exit routines are idempotent */
 | 
			
		||||
	rb_softconfig_exit();
 | 
			
		||||
	rb_hardconfig_exit();
 | 
			
		||||
	kobject_put(rb_kobj);	// recursive afaict
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Common routines */
 | 
			
		||||
 | 
			
		||||
ssize_t routerboot_tag_show_string(const u8 *pld, u16 pld_len, char *buf)
 | 
			
		||||
{
 | 
			
		||||
	return scnprintf(buf, pld_len+1, "%s\n", pld);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ssize_t routerboot_tag_show_u32s(const u8 *pld, u16 pld_len, char *buf)
 | 
			
		||||
{
 | 
			
		||||
	char *out = buf;
 | 
			
		||||
	u32 *data;	// cpu-endian
 | 
			
		||||
 | 
			
		||||
	/* Caller ensures pld_len > 0 */
 | 
			
		||||
	if (pld_len % sizeof(*data))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	data = (u32 *)pld;
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		out += sprintf(out, "0x%08x\n", *data);
 | 
			
		||||
		data++;
 | 
			
		||||
	} while ((pld_len -= sizeof(*data)));
 | 
			
		||||
 | 
			
		||||
	return out - buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module_init(routerboot_init);
 | 
			
		||||
module_exit(routerboot_exit);
 | 
			
		||||
 | 
			
		||||
MODULE_LICENSE("GPL v2");
 | 
			
		||||
MODULE_DESCRIPTION("MikroTik RouterBoot sysfs support");
 | 
			
		||||
MODULE_AUTHOR("Thibaut VARENE");
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-only
 | 
			
		||||
/*
 | 
			
		||||
 * Common definitions for MikroTik RouterBoot data.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2020 Thibaut VARÈNE <hacks+kernel@slashdirt.org>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifndef _ROUTERBOOT_H_
 | 
			
		||||
#define _ROUTERBOOT_H_
 | 
			
		||||
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
 | 
			
		||||
// these magic values are stored in cpu-endianness on flash
 | 
			
		||||
#define RB_MAGIC_HARD	(('H') | ('a' << 8) | ('r' << 16) | ('d' << 24))
 | 
			
		||||
#define RB_MAGIC_SOFT	(('S') | ('o' << 8) | ('f' << 16) | ('t' << 24))
 | 
			
		||||
#define RB_MAGIC_LZOR	(('L') | ('Z' << 8) | ('O' << 16) | ('R' << 24))
 | 
			
		||||
#define RB_MAGIC_LZ77	(('L' << 24) | ('Z' << 16) | ('7' << 8) | ('7'))
 | 
			
		||||
#define RB_MAGIC_ERD	(('E' << 16) | ('R' << 8) | ('D'))
 | 
			
		||||
 | 
			
		||||
#define RB_ART_SIZE	0x10000
 | 
			
		||||
 | 
			
		||||
#define RB_MTD_HARD_CONFIG	"hard_config"
 | 
			
		||||
#define RB_MTD_SOFT_CONFIG	"soft_config"
 | 
			
		||||
 | 
			
		||||
int routerboot_tag_find(const u8 *bufhead, const size_t buflen, const u16 tag_id, u16 *pld_ofs, u16 *pld_len);
 | 
			
		||||
int routerboot_rle_decode(const u8 *in, size_t inlen, u8 *out, size_t *outlen);
 | 
			
		||||
 | 
			
		||||
int rb_hardconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd);
 | 
			
		||||
void rb_hardconfig_exit(void);
 | 
			
		||||
 | 
			
		||||
int rb_softconfig_init(struct kobject *rb_kobj, struct mtd_info *mtd);
 | 
			
		||||
void rb_softconfig_exit(void);
 | 
			
		||||
 | 
			
		||||
ssize_t routerboot_tag_show_string(const u8 *pld, u16 pld_len, char *buf);
 | 
			
		||||
ssize_t routerboot_tag_show_u32s(const u8 *pld, u16 pld_len, char *buf);
 | 
			
		||||
 | 
			
		||||
#endif /* _ROUTERBOOT_H_ */
 | 
			
		||||
							
								
								
									
										745
									
								
								6.12/target/linux/generic/files/drivers/ssb/fallback-sprom.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										745
									
								
								6.12/target/linux/generic/files/drivers/ssb/fallback-sprom.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,745 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
			
		||||
/*
 | 
			
		||||
 * SSB Fallback SPROM Driver
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
 | 
			
		||||
 * Copyright (C) 2014 Jonas Gorski <jonas.gorski@gmail.com>
 | 
			
		||||
 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
 | 
			
		||||
 * Copyright (C) 2008 Florian Fainelli <f.fainelli@gmail.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/etherdevice.h>
 | 
			
		||||
#include <linux/firmware.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/mtd/mtd.h>
 | 
			
		||||
#include <linux/of_net.h>
 | 
			
		||||
#include <linux/of_platform.h>
 | 
			
		||||
#include <linux/ssb/ssb.h>
 | 
			
		||||
#include "fallback-sprom.h"
 | 
			
		||||
 | 
			
		||||
#define SSB_FBS_MAX_SIZE 440
 | 
			
		||||
 | 
			
		||||
/* Get the word-offset for a SSB_SPROM_XXX define. */
 | 
			
		||||
#define SPOFF(offset)	((offset) / sizeof(u16))
 | 
			
		||||
/* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
 | 
			
		||||
#define SPEX16(_outvar, _offset, _mask, _shift)	\
 | 
			
		||||
	out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
 | 
			
		||||
#define SPEX32(_outvar, _offset, _mask, _shift)	\
 | 
			
		||||
	out->_outvar = ((((u32)in[SPOFF((_offset)+2)] << 16 | \
 | 
			
		||||
			   in[SPOFF(_offset)]) & (_mask)) >> (_shift))
 | 
			
		||||
#define SPEX(_outvar, _offset, _mask, _shift) \
 | 
			
		||||
	SPEX16(_outvar, _offset, _mask, _shift)
 | 
			
		||||
 | 
			
		||||
#define SPEX_ARRAY8(_field, _offset, _mask, _shift)	\
 | 
			
		||||
	do {	\
 | 
			
		||||
		SPEX(_field[0], _offset +  0, _mask, _shift);	\
 | 
			
		||||
		SPEX(_field[1], _offset +  2, _mask, _shift);	\
 | 
			
		||||
		SPEX(_field[2], _offset +  4, _mask, _shift);	\
 | 
			
		||||
		SPEX(_field[3], _offset +  6, _mask, _shift);	\
 | 
			
		||||
		SPEX(_field[4], _offset +  8, _mask, _shift);	\
 | 
			
		||||
		SPEX(_field[5], _offset + 10, _mask, _shift);	\
 | 
			
		||||
		SPEX(_field[6], _offset + 12, _mask, _shift);	\
 | 
			
		||||
		SPEX(_field[7], _offset + 14, _mask, _shift);	\
 | 
			
		||||
	} while (0)
 | 
			
		||||
 | 
			
		||||
struct ssb_fbs {
 | 
			
		||||
	struct device *dev;
 | 
			
		||||
	struct list_head list;
 | 
			
		||||
	struct ssb_sprom sprom;
 | 
			
		||||
	u32 pci_bus;
 | 
			
		||||
	u32 pci_dev;
 | 
			
		||||
	bool devid_override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static DEFINE_SPINLOCK(ssb_fbs_lock);
 | 
			
		||||
static struct list_head ssb_fbs_list = LIST_HEAD_INIT(ssb_fbs_list);
 | 
			
		||||
 | 
			
		||||
int ssb_get_fallback_sprom(struct ssb_bus *bus, struct ssb_sprom *out)
 | 
			
		||||
{
 | 
			
		||||
	struct ssb_fbs *pos;
 | 
			
		||||
	u32 pci_bus, pci_dev;
 | 
			
		||||
 | 
			
		||||
	if (bus->bustype != SSB_BUSTYPE_PCI)
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	pci_bus = bus->host_pci->bus->number;
 | 
			
		||||
	pci_dev = PCI_SLOT(bus->host_pci->devfn);
 | 
			
		||||
 | 
			
		||||
	list_for_each_entry(pos, &ssb_fbs_list, list) {
 | 
			
		||||
		if (pos->pci_bus != pci_bus ||
 | 
			
		||||
		    pos->pci_dev != pci_dev)
 | 
			
		||||
		    	continue;
 | 
			
		||||
 | 
			
		||||
		if (pos->devid_override)
 | 
			
		||||
			bus->host_pci->device = pos->sprom.dev_id;
 | 
			
		||||
 | 
			
		||||
		memcpy(out, &pos->sprom, sizeof(struct ssb_sprom));
 | 
			
		||||
		dev_info(pos->dev, "requested by [%x:%x]",
 | 
			
		||||
			 pos->pci_bus, pos->pci_dev);
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pr_err("unable to fill SPROM for [%x:%x]\n", pci_bus, pci_dev);
 | 
			
		||||
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static s8 sprom_extract_antgain(u8 sprom_revision, const u16 *in, u16 offset,
 | 
			
		||||
				u16 mask, u16 shift)
 | 
			
		||||
{
 | 
			
		||||
	u16 v;
 | 
			
		||||
	u8 gain;
 | 
			
		||||
 | 
			
		||||
	v = in[SPOFF(offset)];
 | 
			
		||||
	gain = (v & mask) >> shift;
 | 
			
		||||
	if (gain == 0xFF)
 | 
			
		||||
		gain = 2; /* If unset use 2dBm */
 | 
			
		||||
	if (sprom_revision == 1) {
 | 
			
		||||
		/* Convert to Q5.2 */
 | 
			
		||||
		gain <<= 2;
 | 
			
		||||
	} else {
 | 
			
		||||
		/* Q5.2 Fractional part is stored in 0xC0 */
 | 
			
		||||
		gain = ((gain & 0xC0) >> 6) | ((gain & 0x3F) << 2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (s8)gain;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sprom_extract_r23(struct ssb_sprom *out, const u16 *in)
 | 
			
		||||
{
 | 
			
		||||
	SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0);
 | 
			
		||||
	SPEX(opo, SSB_SPROM2_OPO, SSB_SPROM2_OPO_VALUE, 0);
 | 
			
		||||
	SPEX(pa1lob0, SSB_SPROM2_PA1LOB0, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1lob1, SSB_SPROM2_PA1LOB1, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1lob2, SSB_SPROM2_PA1LOB2, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1hib0, SSB_SPROM2_PA1HIB0, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1hib1, SSB_SPROM2_PA1HIB1, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1hib2, SSB_SPROM2_PA1HIB2, 0xFFFF, 0);
 | 
			
		||||
	SPEX(maxpwr_ah, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_HI, 0);
 | 
			
		||||
	SPEX(maxpwr_al, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_LO,
 | 
			
		||||
	     SSB_SPROM2_MAXP_A_LO_SHIFT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
 | 
			
		||||
{
 | 
			
		||||
	SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0);
 | 
			
		||||
	SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A,
 | 
			
		||||
	     SSB_SPROM1_ETHPHY_ET1A_SHIFT);
 | 
			
		||||
	SPEX(et0mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0M, 14);
 | 
			
		||||
	SPEX(et1mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1M, 15);
 | 
			
		||||
	SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0);
 | 
			
		||||
	SPEX(board_type, SSB_SPROM1_SPID, 0xFFFF, 0);
 | 
			
		||||
	if (out->revision == 1)
 | 
			
		||||
		SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE,
 | 
			
		||||
		     SSB_SPROM1_BINF_CCODE_SHIFT);
 | 
			
		||||
	SPEX(ant_available_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA,
 | 
			
		||||
	     SSB_SPROM1_BINF_ANTA_SHIFT);
 | 
			
		||||
	SPEX(ant_available_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG,
 | 
			
		||||
	     SSB_SPROM1_BINF_ANTBG_SHIFT);
 | 
			
		||||
	SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa0b2, SSB_SPROM1_PA0B2, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1b0, SSB_SPROM1_PA1B0, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1b1, SSB_SPROM1_PA1B1, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1b2, SSB_SPROM1_PA1B2, 0xFFFF, 0);
 | 
			
		||||
	SPEX(gpio0, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P0, 0);
 | 
			
		||||
	SPEX(gpio1, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P1,
 | 
			
		||||
	     SSB_SPROM1_GPIOA_P1_SHIFT);
 | 
			
		||||
	SPEX(gpio2, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P2, 0);
 | 
			
		||||
	SPEX(gpio3, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P3,
 | 
			
		||||
	     SSB_SPROM1_GPIOB_P3_SHIFT);
 | 
			
		||||
	SPEX(maxpwr_a, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_A,
 | 
			
		||||
	     SSB_SPROM1_MAXPWR_A_SHIFT);
 | 
			
		||||
	SPEX(maxpwr_bg, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_BG, 0);
 | 
			
		||||
	SPEX(itssi_a, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_A,
 | 
			
		||||
	     SSB_SPROM1_ITSSI_A_SHIFT);
 | 
			
		||||
	SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0);
 | 
			
		||||
	SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0);
 | 
			
		||||
 | 
			
		||||
	SPEX(alpha2[0], SSB_SPROM1_CCODE, 0xff00, 8);
 | 
			
		||||
	SPEX(alpha2[1], SSB_SPROM1_CCODE, 0x00ff, 0);
 | 
			
		||||
 | 
			
		||||
	/* Extract the antenna gain values. */
 | 
			
		||||
	out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in,
 | 
			
		||||
						     SSB_SPROM1_AGAIN,
 | 
			
		||||
						     SSB_SPROM1_AGAIN_BG,
 | 
			
		||||
						     SSB_SPROM1_AGAIN_BG_SHIFT);
 | 
			
		||||
	out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in,
 | 
			
		||||
						     SSB_SPROM1_AGAIN,
 | 
			
		||||
						     SSB_SPROM1_AGAIN_A,
 | 
			
		||||
						     SSB_SPROM1_AGAIN_A_SHIFT);
 | 
			
		||||
	if (out->revision >= 2)
 | 
			
		||||
		sprom_extract_r23(out, in);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Revs 4 5 and 8 have partially shared layout */
 | 
			
		||||
static void sprom_extract_r458(struct ssb_sprom *out, const u16 *in)
 | 
			
		||||
{
 | 
			
		||||
	SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01,
 | 
			
		||||
	     SSB_SPROM4_TXPID2G0, SSB_SPROM4_TXPID2G0_SHIFT);
 | 
			
		||||
	SPEX(txpid2g[1], SSB_SPROM4_TXPID2G01,
 | 
			
		||||
	     SSB_SPROM4_TXPID2G1, SSB_SPROM4_TXPID2G1_SHIFT);
 | 
			
		||||
	SPEX(txpid2g[2], SSB_SPROM4_TXPID2G23,
 | 
			
		||||
	     SSB_SPROM4_TXPID2G2, SSB_SPROM4_TXPID2G2_SHIFT);
 | 
			
		||||
	SPEX(txpid2g[3], SSB_SPROM4_TXPID2G23,
 | 
			
		||||
	     SSB_SPROM4_TXPID2G3, SSB_SPROM4_TXPID2G3_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(txpid5gl[0], SSB_SPROM4_TXPID5GL01,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GL0, SSB_SPROM4_TXPID5GL0_SHIFT);
 | 
			
		||||
	SPEX(txpid5gl[1], SSB_SPROM4_TXPID5GL01,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GL1, SSB_SPROM4_TXPID5GL1_SHIFT);
 | 
			
		||||
	SPEX(txpid5gl[2], SSB_SPROM4_TXPID5GL23,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GL2, SSB_SPROM4_TXPID5GL2_SHIFT);
 | 
			
		||||
	SPEX(txpid5gl[3], SSB_SPROM4_TXPID5GL23,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GL3, SSB_SPROM4_TXPID5GL3_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(txpid5g[0], SSB_SPROM4_TXPID5G01,
 | 
			
		||||
	     SSB_SPROM4_TXPID5G0, SSB_SPROM4_TXPID5G0_SHIFT);
 | 
			
		||||
	SPEX(txpid5g[1], SSB_SPROM4_TXPID5G01,
 | 
			
		||||
	     SSB_SPROM4_TXPID5G1, SSB_SPROM4_TXPID5G1_SHIFT);
 | 
			
		||||
	SPEX(txpid5g[2], SSB_SPROM4_TXPID5G23,
 | 
			
		||||
	     SSB_SPROM4_TXPID5G2, SSB_SPROM4_TXPID5G2_SHIFT);
 | 
			
		||||
	SPEX(txpid5g[3], SSB_SPROM4_TXPID5G23,
 | 
			
		||||
	     SSB_SPROM4_TXPID5G3, SSB_SPROM4_TXPID5G3_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(txpid5gh[0], SSB_SPROM4_TXPID5GH01,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GH0, SSB_SPROM4_TXPID5GH0_SHIFT);
 | 
			
		||||
	SPEX(txpid5gh[1], SSB_SPROM4_TXPID5GH01,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GH1, SSB_SPROM4_TXPID5GH1_SHIFT);
 | 
			
		||||
	SPEX(txpid5gh[2], SSB_SPROM4_TXPID5GH23,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GH2, SSB_SPROM4_TXPID5GH2_SHIFT);
 | 
			
		||||
	SPEX(txpid5gh[3], SSB_SPROM4_TXPID5GH23,
 | 
			
		||||
	     SSB_SPROM4_TXPID5GH3, SSB_SPROM4_TXPID5GH3_SHIFT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
 | 
			
		||||
{
 | 
			
		||||
	static const u16 pwr_info_offset[] = {
 | 
			
		||||
		SSB_SPROM4_PWR_INFO_CORE0, SSB_SPROM4_PWR_INFO_CORE1,
 | 
			
		||||
		SSB_SPROM4_PWR_INFO_CORE2, SSB_SPROM4_PWR_INFO_CORE3
 | 
			
		||||
	};
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
 | 
			
		||||
		     ARRAY_SIZE(out->core_pwr_info));
 | 
			
		||||
 | 
			
		||||
	SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0);
 | 
			
		||||
	SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A,
 | 
			
		||||
	     SSB_SPROM4_ETHPHY_ET1A_SHIFT);
 | 
			
		||||
	SPEX(board_rev, SSB_SPROM4_BOARDREV, 0xFFFF, 0);
 | 
			
		||||
	SPEX(board_type, SSB_SPROM1_SPID, 0xFFFF, 0);
 | 
			
		||||
	if (out->revision == 4) {
 | 
			
		||||
		SPEX(alpha2[0], SSB_SPROM4_CCODE, 0xff00, 8);
 | 
			
		||||
		SPEX(alpha2[1], SSB_SPROM4_CCODE, 0x00ff, 0);
 | 
			
		||||
		SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0);
 | 
			
		||||
		SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0);
 | 
			
		||||
		SPEX(boardflags2_lo, SSB_SPROM4_BFL2LO, 0xFFFF, 0);
 | 
			
		||||
		SPEX(boardflags2_hi, SSB_SPROM4_BFL2HI, 0xFFFF, 0);
 | 
			
		||||
	} else {
 | 
			
		||||
		SPEX(alpha2[0], SSB_SPROM5_CCODE, 0xff00, 8);
 | 
			
		||||
		SPEX(alpha2[1], SSB_SPROM5_CCODE, 0x00ff, 0);
 | 
			
		||||
		SPEX(boardflags_lo, SSB_SPROM5_BFLLO, 0xFFFF, 0);
 | 
			
		||||
		SPEX(boardflags_hi, SSB_SPROM5_BFLHI, 0xFFFF, 0);
 | 
			
		||||
		SPEX(boardflags2_lo, SSB_SPROM5_BFL2LO, 0xFFFF, 0);
 | 
			
		||||
		SPEX(boardflags2_hi, SSB_SPROM5_BFL2HI, 0xFFFF, 0);
 | 
			
		||||
	}
 | 
			
		||||
	SPEX(ant_available_a, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_A,
 | 
			
		||||
	     SSB_SPROM4_ANTAVAIL_A_SHIFT);
 | 
			
		||||
	SPEX(ant_available_bg, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_BG,
 | 
			
		||||
	     SSB_SPROM4_ANTAVAIL_BG_SHIFT);
 | 
			
		||||
	SPEX(maxpwr_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_MAXP_BG_MASK, 0);
 | 
			
		||||
	SPEX(itssi_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_ITSSI_BG,
 | 
			
		||||
	     SSB_SPROM4_ITSSI_BG_SHIFT);
 | 
			
		||||
	SPEX(maxpwr_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_MAXP_A_MASK, 0);
 | 
			
		||||
	SPEX(itssi_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_ITSSI_A,
 | 
			
		||||
	     SSB_SPROM4_ITSSI_A_SHIFT);
 | 
			
		||||
	if (out->revision == 4) {
 | 
			
		||||
		SPEX(gpio0, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P0, 0);
 | 
			
		||||
		SPEX(gpio1, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P1,
 | 
			
		||||
		     SSB_SPROM4_GPIOA_P1_SHIFT);
 | 
			
		||||
		SPEX(gpio2, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P2, 0);
 | 
			
		||||
		SPEX(gpio3, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P3,
 | 
			
		||||
		     SSB_SPROM4_GPIOB_P3_SHIFT);
 | 
			
		||||
	} else {
 | 
			
		||||
		SPEX(gpio0, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P0, 0);
 | 
			
		||||
		SPEX(gpio1, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P1,
 | 
			
		||||
		     SSB_SPROM5_GPIOA_P1_SHIFT);
 | 
			
		||||
		SPEX(gpio2, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P2, 0);
 | 
			
		||||
		SPEX(gpio3, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P3,
 | 
			
		||||
		     SSB_SPROM5_GPIOB_P3_SHIFT);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Extract the antenna gain values. */
 | 
			
		||||
	out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in,
 | 
			
		||||
						     SSB_SPROM4_AGAIN01,
 | 
			
		||||
						     SSB_SPROM4_AGAIN0,
 | 
			
		||||
						     SSB_SPROM4_AGAIN0_SHIFT);
 | 
			
		||||
	out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in,
 | 
			
		||||
						     SSB_SPROM4_AGAIN01,
 | 
			
		||||
						     SSB_SPROM4_AGAIN1,
 | 
			
		||||
						     SSB_SPROM4_AGAIN1_SHIFT);
 | 
			
		||||
	out->antenna_gain.a2 = sprom_extract_antgain(out->revision, in,
 | 
			
		||||
						     SSB_SPROM4_AGAIN23,
 | 
			
		||||
						     SSB_SPROM4_AGAIN2,
 | 
			
		||||
						     SSB_SPROM4_AGAIN2_SHIFT);
 | 
			
		||||
	out->antenna_gain.a3 = sprom_extract_antgain(out->revision, in,
 | 
			
		||||
						     SSB_SPROM4_AGAIN23,
 | 
			
		||||
						     SSB_SPROM4_AGAIN3,
 | 
			
		||||
						     SSB_SPROM4_AGAIN3_SHIFT);
 | 
			
		||||
 | 
			
		||||
	/* Extract cores power info info */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
 | 
			
		||||
		u16 o = pwr_info_offset[i];
 | 
			
		||||
 | 
			
		||||
		SPEX(core_pwr_info[i].itssi_2g, o + SSB_SPROM4_2G_MAXP_ITSSI,
 | 
			
		||||
			SSB_SPROM4_2G_ITSSI, SSB_SPROM4_2G_ITSSI_SHIFT);
 | 
			
		||||
		SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SPROM4_2G_MAXP_ITSSI,
 | 
			
		||||
			SSB_SPROM4_2G_MAXP, 0);
 | 
			
		||||
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SPROM4_2G_PA_0, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SPROM4_2G_PA_1, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SPROM4_2G_PA_2, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_2g[3], o + SSB_SPROM4_2G_PA_3, ~0, 0);
 | 
			
		||||
 | 
			
		||||
		SPEX(core_pwr_info[i].itssi_5g, o + SSB_SPROM4_5G_MAXP_ITSSI,
 | 
			
		||||
			SSB_SPROM4_5G_ITSSI, SSB_SPROM4_5G_ITSSI_SHIFT);
 | 
			
		||||
		SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SPROM4_5G_MAXP_ITSSI,
 | 
			
		||||
			SSB_SPROM4_5G_MAXP, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM4_5GHL_MAXP,
 | 
			
		||||
			SSB_SPROM4_5GH_MAXP, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM4_5GHL_MAXP,
 | 
			
		||||
			SSB_SPROM4_5GL_MAXP, SSB_SPROM4_5GL_MAXP_SHIFT);
 | 
			
		||||
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SPROM4_5GL_PA_0, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SPROM4_5GL_PA_1, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SPROM4_5GL_PA_2, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gl[3], o + SSB_SPROM4_5GL_PA_3, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SPROM4_5G_PA_0, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SPROM4_5G_PA_1, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SPROM4_5G_PA_2, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5g[3], o + SSB_SPROM4_5G_PA_3, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SPROM4_5GH_PA_0, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SPROM4_5GH_PA_1, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SPROM4_5GH_PA_2, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gh[3], o + SSB_SPROM4_5GH_PA_3, ~0, 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sprom_extract_r458(out, in);
 | 
			
		||||
 | 
			
		||||
	/* TODO - get remaining rev 4 stuff needed */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	u16 o;
 | 
			
		||||
	static const u16 pwr_info_offset[] = {
 | 
			
		||||
		SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1,
 | 
			
		||||
		SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3
 | 
			
		||||
	};
 | 
			
		||||
	BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
 | 
			
		||||
			ARRAY_SIZE(out->core_pwr_info));
 | 
			
		||||
 | 
			
		||||
	SPEX(board_rev, SSB_SPROM8_BOARDREV, 0xFFFF, 0);
 | 
			
		||||
	SPEX(board_type, SSB_SPROM1_SPID, 0xFFFF, 0);
 | 
			
		||||
	SPEX(alpha2[0], SSB_SPROM8_CCODE, 0xff00, 8);
 | 
			
		||||
	SPEX(alpha2[1], SSB_SPROM8_CCODE, 0x00ff, 0);
 | 
			
		||||
	SPEX(boardflags_lo, SSB_SPROM8_BFLLO, 0xFFFF, 0);
 | 
			
		||||
	SPEX(boardflags_hi, SSB_SPROM8_BFLHI, 0xFFFF, 0);
 | 
			
		||||
	SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, 0xFFFF, 0);
 | 
			
		||||
	SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, 0xFFFF, 0);
 | 
			
		||||
	SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
 | 
			
		||||
	     SSB_SPROM8_ANTAVAIL_A_SHIFT);
 | 
			
		||||
	SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
 | 
			
		||||
	     SSB_SPROM8_ANTAVAIL_BG_SHIFT);
 | 
			
		||||
	SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0);
 | 
			
		||||
	SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG,
 | 
			
		||||
	     SSB_SPROM8_ITSSI_BG_SHIFT);
 | 
			
		||||
	SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0);
 | 
			
		||||
	SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A,
 | 
			
		||||
	     SSB_SPROM8_ITSSI_A_SHIFT);
 | 
			
		||||
	SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0);
 | 
			
		||||
	SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK,
 | 
			
		||||
	     SSB_SPROM8_MAXP_AL_SHIFT);
 | 
			
		||||
	SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0);
 | 
			
		||||
	SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1,
 | 
			
		||||
	     SSB_SPROM8_GPIOA_P1_SHIFT);
 | 
			
		||||
	SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0);
 | 
			
		||||
	SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3,
 | 
			
		||||
	     SSB_SPROM8_GPIOB_P3_SHIFT);
 | 
			
		||||
	SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0);
 | 
			
		||||
	SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G,
 | 
			
		||||
	     SSB_SPROM8_TRI5G_SHIFT);
 | 
			
		||||
	SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0);
 | 
			
		||||
	SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH,
 | 
			
		||||
	     SSB_SPROM8_TRI5GH_SHIFT);
 | 
			
		||||
	SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G, 0);
 | 
			
		||||
	SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G,
 | 
			
		||||
	     SSB_SPROM8_RXPO5G_SHIFT);
 | 
			
		||||
	SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0);
 | 
			
		||||
	SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G,
 | 
			
		||||
	     SSB_SPROM8_RSSISMC2G_SHIFT);
 | 
			
		||||
	SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G,
 | 
			
		||||
	     SSB_SPROM8_RSSISAV2G_SHIFT);
 | 
			
		||||
	SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G,
 | 
			
		||||
	     SSB_SPROM8_BXA2G_SHIFT);
 | 
			
		||||
	SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0);
 | 
			
		||||
	SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G,
 | 
			
		||||
	     SSB_SPROM8_RSSISMC5G_SHIFT);
 | 
			
		||||
	SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G,
 | 
			
		||||
	     SSB_SPROM8_RSSISAV5G_SHIFT);
 | 
			
		||||
	SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G,
 | 
			
		||||
	     SSB_SPROM8_BXA5G_SHIFT);
 | 
			
		||||
	SPEX(pa0b0, SSB_SPROM8_PA0B0, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa0b1, SSB_SPROM8_PA0B1, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa0b2, SSB_SPROM8_PA0B2, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1b0, SSB_SPROM8_PA1B0, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1b1, SSB_SPROM8_PA1B1, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1b2, SSB_SPROM8_PA1B2, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, 0xFFFF, 0);
 | 
			
		||||
	SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, 0xFFFF, 0);
 | 
			
		||||
	SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, 0xFFFF, 0);
 | 
			
		||||
	SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, 0xFFFFFFFF, 0);
 | 
			
		||||
	SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, 0xFFFFFFFF, 0);
 | 
			
		||||
	SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, 0xFFFFFFFF, 0);
 | 
			
		||||
	SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, 0xFFFFFFFF, 0);
 | 
			
		||||
 | 
			
		||||
	/* Extract the antenna gain values. */
 | 
			
		||||
	out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in,
 | 
			
		||||
						     SSB_SPROM8_AGAIN01,
 | 
			
		||||
						     SSB_SPROM8_AGAIN0,
 | 
			
		||||
						     SSB_SPROM8_AGAIN0_SHIFT);
 | 
			
		||||
	out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in,
 | 
			
		||||
						     SSB_SPROM8_AGAIN01,
 | 
			
		||||
						     SSB_SPROM8_AGAIN1,
 | 
			
		||||
						     SSB_SPROM8_AGAIN1_SHIFT);
 | 
			
		||||
	out->antenna_gain.a2 = sprom_extract_antgain(out->revision, in,
 | 
			
		||||
						     SSB_SPROM8_AGAIN23,
 | 
			
		||||
						     SSB_SPROM8_AGAIN2,
 | 
			
		||||
						     SSB_SPROM8_AGAIN2_SHIFT);
 | 
			
		||||
	out->antenna_gain.a3 = sprom_extract_antgain(out->revision, in,
 | 
			
		||||
						     SSB_SPROM8_AGAIN23,
 | 
			
		||||
						     SSB_SPROM8_AGAIN3,
 | 
			
		||||
						     SSB_SPROM8_AGAIN3_SHIFT);
 | 
			
		||||
 | 
			
		||||
	/* Extract cores power info info */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
 | 
			
		||||
		o = pwr_info_offset[i];
 | 
			
		||||
		SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
 | 
			
		||||
			SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT);
 | 
			
		||||
		SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
 | 
			
		||||
			SSB_SPROM8_2G_MAXP, 0);
 | 
			
		||||
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0);
 | 
			
		||||
 | 
			
		||||
		SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
 | 
			
		||||
			SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT);
 | 
			
		||||
		SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
 | 
			
		||||
			SSB_SPROM8_5G_MAXP, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP,
 | 
			
		||||
			SSB_SPROM8_5GH_MAXP, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP,
 | 
			
		||||
			SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT);
 | 
			
		||||
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0);
 | 
			
		||||
		SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Extract FEM info */
 | 
			
		||||
	SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G,
 | 
			
		||||
		SSB_SROM8_FEM_TSSIPOS, SSB_SROM8_FEM_TSSIPOS_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz2.extpa_gain, SSB_SPROM8_FEM2G,
 | 
			
		||||
		SSB_SROM8_FEM_EXTPA_GAIN, SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz2.pdet_range, SSB_SPROM8_FEM2G,
 | 
			
		||||
		SSB_SROM8_FEM_PDET_RANGE, SSB_SROM8_FEM_PDET_RANGE_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz2.tr_iso, SSB_SPROM8_FEM2G,
 | 
			
		||||
		SSB_SROM8_FEM_TR_ISO, SSB_SROM8_FEM_TR_ISO_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz2.antswlut, SSB_SPROM8_FEM2G,
 | 
			
		||||
		SSB_SROM8_FEM_ANTSWLUT, SSB_SROM8_FEM_ANTSWLUT_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(fem.ghz5.tssipos, SSB_SPROM8_FEM5G,
 | 
			
		||||
		SSB_SROM8_FEM_TSSIPOS, SSB_SROM8_FEM_TSSIPOS_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz5.extpa_gain, SSB_SPROM8_FEM5G,
 | 
			
		||||
		SSB_SROM8_FEM_EXTPA_GAIN, SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz5.pdet_range, SSB_SPROM8_FEM5G,
 | 
			
		||||
		SSB_SROM8_FEM_PDET_RANGE, SSB_SROM8_FEM_PDET_RANGE_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz5.tr_iso, SSB_SPROM8_FEM5G,
 | 
			
		||||
		SSB_SROM8_FEM_TR_ISO, SSB_SROM8_FEM_TR_ISO_SHIFT);
 | 
			
		||||
	SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G,
 | 
			
		||||
		SSB_SROM8_FEM_ANTSWLUT, SSB_SROM8_FEM_ANTSWLUT_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(leddc_on_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_ON,
 | 
			
		||||
	     SSB_SPROM8_LEDDC_ON_SHIFT);
 | 
			
		||||
	SPEX(leddc_off_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_OFF,
 | 
			
		||||
	     SSB_SPROM8_LEDDC_OFF_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(txchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_TXCHAIN,
 | 
			
		||||
	     SSB_SPROM8_TXRXC_TXCHAIN_SHIFT);
 | 
			
		||||
	SPEX(rxchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_RXCHAIN,
 | 
			
		||||
	     SSB_SPROM8_TXRXC_RXCHAIN_SHIFT);
 | 
			
		||||
	SPEX(antswitch, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_SWITCH,
 | 
			
		||||
	     SSB_SPROM8_TXRXC_SWITCH_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(opo, SSB_SPROM8_OFDM2GPO, 0x00ff, 0);
 | 
			
		||||
 | 
			
		||||
	SPEX_ARRAY8(mcs2gpo, SSB_SPROM8_2G_MCSPO, ~0, 0);
 | 
			
		||||
	SPEX_ARRAY8(mcs5gpo, SSB_SPROM8_5G_MCSPO, ~0, 0);
 | 
			
		||||
	SPEX_ARRAY8(mcs5glpo, SSB_SPROM8_5GL_MCSPO, ~0, 0);
 | 
			
		||||
	SPEX_ARRAY8(mcs5ghpo, SSB_SPROM8_5GH_MCSPO, ~0, 0);
 | 
			
		||||
 | 
			
		||||
	SPEX(rawtempsense, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_RAWTEMP,
 | 
			
		||||
	     SSB_SPROM8_RAWTS_RAWTEMP_SHIFT);
 | 
			
		||||
	SPEX(measpower, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_MEASPOWER,
 | 
			
		||||
	     SSB_SPROM8_RAWTS_MEASPOWER_SHIFT);
 | 
			
		||||
	SPEX(tempsense_slope, SSB_SPROM8_OPT_CORRX,
 | 
			
		||||
	     SSB_SPROM8_OPT_CORRX_TEMP_SLOPE,
 | 
			
		||||
	     SSB_SPROM8_OPT_CORRX_TEMP_SLOPE_SHIFT);
 | 
			
		||||
	SPEX(tempcorrx, SSB_SPROM8_OPT_CORRX, SSB_SPROM8_OPT_CORRX_TEMPCORRX,
 | 
			
		||||
	     SSB_SPROM8_OPT_CORRX_TEMPCORRX_SHIFT);
 | 
			
		||||
	SPEX(tempsense_option, SSB_SPROM8_OPT_CORRX,
 | 
			
		||||
	     SSB_SPROM8_OPT_CORRX_TEMP_OPTION,
 | 
			
		||||
	     SSB_SPROM8_OPT_CORRX_TEMP_OPTION_SHIFT);
 | 
			
		||||
	SPEX(freqoffset_corr, SSB_SPROM8_HWIQ_IQSWP,
 | 
			
		||||
	     SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR,
 | 
			
		||||
	     SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR_SHIFT);
 | 
			
		||||
	SPEX(iqcal_swp_dis, SSB_SPROM8_HWIQ_IQSWP,
 | 
			
		||||
	     SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP,
 | 
			
		||||
	     SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP_SHIFT);
 | 
			
		||||
	SPEX(hw_iqcal_en, SSB_SPROM8_HWIQ_IQSWP, SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL,
 | 
			
		||||
	     SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL_SHIFT);
 | 
			
		||||
 | 
			
		||||
	SPEX(bw40po, SSB_SPROM8_BW40PO, ~0, 0);
 | 
			
		||||
	SPEX(cddpo, SSB_SPROM8_CDDPO, ~0, 0);
 | 
			
		||||
	SPEX(stbcpo, SSB_SPROM8_STBCPO, ~0, 0);
 | 
			
		||||
	SPEX(bwduppo, SSB_SPROM8_BWDUPPO, ~0, 0);
 | 
			
		||||
 | 
			
		||||
	SPEX(tempthresh, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_TRESH,
 | 
			
		||||
	     SSB_SPROM8_THERMAL_TRESH_SHIFT);
 | 
			
		||||
	SPEX(tempoffset, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_OFFSET,
 | 
			
		||||
	     SSB_SPROM8_THERMAL_OFFSET_SHIFT);
 | 
			
		||||
	SPEX(phycal_tempdelta, SSB_SPROM8_TEMPDELTA,
 | 
			
		||||
	     SSB_SPROM8_TEMPDELTA_PHYCAL,
 | 
			
		||||
	     SSB_SPROM8_TEMPDELTA_PHYCAL_SHIFT);
 | 
			
		||||
	SPEX(temps_period, SSB_SPROM8_TEMPDELTA, SSB_SPROM8_TEMPDELTA_PERIOD,
 | 
			
		||||
	     SSB_SPROM8_TEMPDELTA_PERIOD_SHIFT);
 | 
			
		||||
	SPEX(temps_hysteresis, SSB_SPROM8_TEMPDELTA,
 | 
			
		||||
	     SSB_SPROM8_TEMPDELTA_HYSTERESIS,
 | 
			
		||||
	     SSB_SPROM8_TEMPDELTA_HYSTERESIS_SHIFT);
 | 
			
		||||
	sprom_extract_r458(out, in);
 | 
			
		||||
 | 
			
		||||
	/* TODO - get remaining rev 8 stuff needed */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int sprom_extract(struct ssb_fbs *priv, const u16 *in, u16 size)
 | 
			
		||||
{
 | 
			
		||||
	struct ssb_sprom *out = &priv->sprom;
 | 
			
		||||
 | 
			
		||||
	memset(out, 0, sizeof(*out));
 | 
			
		||||
 | 
			
		||||
	out->revision = in[size - 1] & 0x00FF;
 | 
			
		||||
 | 
			
		||||
	switch (out->revision) {
 | 
			
		||||
	case 1:
 | 
			
		||||
	case 2:
 | 
			
		||||
	case 3:
 | 
			
		||||
		sprom_extract_r123(out, in);
 | 
			
		||||
		break;
 | 
			
		||||
	case 4:
 | 
			
		||||
	case 5:
 | 
			
		||||
		sprom_extract_r45(out, in);
 | 
			
		||||
		break;
 | 
			
		||||
	case 8:
 | 
			
		||||
		sprom_extract_r8(out, in);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		dev_warn(priv->dev,
 | 
			
		||||
			 "Unsupported SPROM revision %d detected."
 | 
			
		||||
			 " Will extract v1\n",
 | 
			
		||||
			 out->revision);
 | 
			
		||||
		out->revision = 1;
 | 
			
		||||
		sprom_extract_r123(out, in);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (out->boardflags_lo == 0xFFFF)
 | 
			
		||||
		out->boardflags_lo = 0;  /* per specs */
 | 
			
		||||
	if (out->boardflags_hi == 0xFFFF)
 | 
			
		||||
		out->boardflags_hi = 0;  /* per specs */
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ssb_fbs_fixup(struct ssb_fbs *priv, u16 *sprom)
 | 
			
		||||
{
 | 
			
		||||
	struct device_node *node = priv->dev->of_node;
 | 
			
		||||
	u32 fixups, off, val;
 | 
			
		||||
	int i = 0;
 | 
			
		||||
 | 
			
		||||
	if (!of_get_property(node, "brcm,sprom-fixups", &fixups))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	fixups /= sizeof(u32);
 | 
			
		||||
 | 
			
		||||
	dev_info(priv->dev, "patching SPROM with %u fixups...\n", fixups >> 1);
 | 
			
		||||
 | 
			
		||||
	while (i < fixups) {
 | 
			
		||||
		if (of_property_read_u32_index(node, "brcm,sprom-fixups",
 | 
			
		||||
					       i++, &off)) {
 | 
			
		||||
			dev_err(priv->dev, "error reading fixup[%u] offset\n",
 | 
			
		||||
				i - 1);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (of_property_read_u32_index(node, "brcm,sprom-fixups",
 | 
			
		||||
					       i++, &val)) {
 | 
			
		||||
			dev_err(priv->dev, "error reading fixup[%u] value\n",
 | 
			
		||||
				i - 1);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dev_dbg(priv->dev, "fixup[%d]=0x%04x\n", off, val);
 | 
			
		||||
 | 
			
		||||
		sprom[off] = val;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool sprom_override_devid(struct ssb_fbs *priv, struct ssb_sprom *out,
 | 
			
		||||
				 const u16 *in)
 | 
			
		||||
{
 | 
			
		||||
	SPEX(dev_id, SSB_SPROM1_PID, 0xFFFF, 0);
 | 
			
		||||
	return !!out->dev_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ssb_fbs_set(struct ssb_fbs *priv, struct device_node *node)
 | 
			
		||||
{
 | 
			
		||||
	struct ssb_sprom *sprom = &priv->sprom;
 | 
			
		||||
	const struct firmware *fw;
 | 
			
		||||
	const char *sprom_name;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	if (of_property_read_string(node, "brcm,sprom", &sprom_name))
 | 
			
		||||
		sprom_name = NULL;
 | 
			
		||||
 | 
			
		||||
	if (sprom_name) {
 | 
			
		||||
		err = request_firmware_direct(&fw, sprom_name, priv->dev);
 | 
			
		||||
		if (err)
 | 
			
		||||
			dev_err(priv->dev, "%s load error\n", sprom_name);
 | 
			
		||||
	} else {
 | 
			
		||||
		err = -ENOENT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (err) {
 | 
			
		||||
		sprom->revision = 0x02;
 | 
			
		||||
		sprom->board_rev = 0x0017;
 | 
			
		||||
		sprom->country_code = 0x00;
 | 
			
		||||
		sprom->ant_available_bg = 0x03;
 | 
			
		||||
		sprom->pa0b0 = 0x15ae;
 | 
			
		||||
		sprom->pa0b1 = 0xfa85;
 | 
			
		||||
		sprom->pa0b2 = 0xfe8d;
 | 
			
		||||
		sprom->pa1b0 = 0xffff;
 | 
			
		||||
		sprom->pa1b1 = 0xffff;
 | 
			
		||||
		sprom->pa1b2 = 0xffff;
 | 
			
		||||
		sprom->gpio0 = 0xff;
 | 
			
		||||
		sprom->gpio1 = 0xff;
 | 
			
		||||
		sprom->gpio2 = 0xff;
 | 
			
		||||
		sprom->gpio3 = 0xff;
 | 
			
		||||
		sprom->maxpwr_bg = 0x4c;
 | 
			
		||||
		sprom->itssi_bg = 0x00;
 | 
			
		||||
		sprom->boardflags_lo = 0x2848;
 | 
			
		||||
		sprom->boardflags_hi = 0x0000;
 | 
			
		||||
		priv->devid_override = false;
 | 
			
		||||
 | 
			
		||||
		dev_warn(priv->dev, "using basic SPROM\n");
 | 
			
		||||
	} else {
 | 
			
		||||
		size_t size = min(fw->size, (size_t) SSB_FBS_MAX_SIZE);
 | 
			
		||||
		u16 tmp_sprom[SSB_FBS_MAX_SIZE >> 1];
 | 
			
		||||
		u32 i, j;
 | 
			
		||||
 | 
			
		||||
		for (i = 0, j = 0; i < size; i += 2, j++)
 | 
			
		||||
			tmp_sprom[j] = (fw->data[i] << 8) | fw->data[i + 1];
 | 
			
		||||
 | 
			
		||||
		release_firmware(fw);
 | 
			
		||||
		ssb_fbs_fixup(priv, tmp_sprom);
 | 
			
		||||
		sprom_extract(priv, tmp_sprom, size >> 1);
 | 
			
		||||
 | 
			
		||||
		priv->devid_override = sprom_override_devid(priv, sprom,
 | 
			
		||||
							    tmp_sprom);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ssb_fbs_probe(struct platform_device *pdev)
 | 
			
		||||
{
 | 
			
		||||
	struct device *dev = &pdev->dev;
 | 
			
		||||
	struct device_node *node = dev->of_node;
 | 
			
		||||
	struct ssb_fbs *priv;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	u8 mac[ETH_ALEN];
 | 
			
		||||
 | 
			
		||||
	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 | 
			
		||||
	if (!priv)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	priv->dev = dev;
 | 
			
		||||
 | 
			
		||||
	ssb_fbs_set(priv, node);
 | 
			
		||||
 | 
			
		||||
	of_property_read_u32(node, "pci-bus", &priv->pci_bus);
 | 
			
		||||
	of_property_read_u32(node, "pci-dev", &priv->pci_dev);
 | 
			
		||||
 | 
			
		||||
	of_get_mac_address(node, mac);
 | 
			
		||||
	if (is_valid_ether_addr(mac)) {
 | 
			
		||||
		dev_info(dev, "mtd mac %pM\n", mac);
 | 
			
		||||
	} else {
 | 
			
		||||
		eth_random_addr(mac);
 | 
			
		||||
		dev_info(dev, "random mac %pM\n", mac);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memcpy(priv->sprom.il0mac, mac, ETH_ALEN);
 | 
			
		||||
 	memcpy(priv->sprom.et0mac, mac, ETH_ALEN);
 | 
			
		||||
 	memcpy(priv->sprom.et1mac, mac, ETH_ALEN);
 | 
			
		||||
	memcpy(priv->sprom.et2mac, mac, ETH_ALEN);
 | 
			
		||||
 | 
			
		||||
	spin_lock_irqsave(&ssb_fbs_lock, flags);
 | 
			
		||||
	list_add(&priv->list, &ssb_fbs_list);
 | 
			
		||||
	spin_unlock_irqrestore(&ssb_fbs_lock, flags);
 | 
			
		||||
 | 
			
		||||
	dev_info(dev, "registered SPROM for [%x:%x]\n",
 | 
			
		||||
		 priv->pci_bus, priv->pci_dev);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id ssb_fbs_of_match[] = {
 | 
			
		||||
	{ .compatible = "brcm,ssb-sprom", },
 | 
			
		||||
	{ /* sentinel */ }
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, ssb_fbs_of_match);
 | 
			
		||||
 | 
			
		||||
static struct platform_driver ssb_fbs_driver = {
 | 
			
		||||
	.probe = ssb_fbs_probe,
 | 
			
		||||
	.driver	= {
 | 
			
		||||
		.name = "ssb-sprom",
 | 
			
		||||
		.of_match_table = ssb_fbs_of_match,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int __init ssb_fbs_register(void)
 | 
			
		||||
{
 | 
			
		||||
	return platform_driver_register(&ssb_fbs_driver);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
#ifndef _FALLBACK_SPROM_H
 | 
			
		||||
#define _FALLBACK_SPROM_H
 | 
			
		||||
 | 
			
		||||
int __init ssb_fbs_register(void);
 | 
			
		||||
int ssb_get_fallback_sprom(struct ssb_bus *dev, struct ssb_sprom *out);
 | 
			
		||||
 | 
			
		||||
#endif /* _FALLBACK_SPROM_H */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,198 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0-only */
 | 
			
		||||
/*
 | 
			
		||||
 *    *** IMPORTANT ***
 | 
			
		||||
 * This file is not only included from C-code but also from devicetree source
 | 
			
		||||
 * files. As such this file MUST only contain comments and defines.
 | 
			
		||||
 *
 | 
			
		||||
 * Based on image.h from U-Boot which is
 | 
			
		||||
 * (C) Copyright 2008 Semihalf
 | 
			
		||||
 * (C) Copyright 2000-2005 Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __UIMAGE_H__
 | 
			
		||||
#define __UIMAGE_H__
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Operating System Codes
 | 
			
		||||
 *
 | 
			
		||||
 * The following are exposed to uImage header.
 | 
			
		||||
 * New IDs *MUST* be appended at the end of the list and *NEVER*
 | 
			
		||||
 * inserted for backward compatibility.
 | 
			
		||||
 */
 | 
			
		||||
#define	IH_OS_INVALID		0	/* Invalid OS	*/
 | 
			
		||||
#define	IH_OS_OPENBSD		1	/* OpenBSD	*/
 | 
			
		||||
#define	IH_OS_NETBSD		2	/* NetBSD	*/
 | 
			
		||||
#define	IH_OS_FREEBSD		3	/* FreeBSD	*/
 | 
			
		||||
#define	IH_OS_4_4BSD		4	/* 4.4BSD	*/
 | 
			
		||||
#define	IH_OS_LINUX		5	/* Linux	*/
 | 
			
		||||
#define	IH_OS_SVR4		6	/* SVR4		*/
 | 
			
		||||
#define	IH_OS_ESIX		7	/* Esix		*/
 | 
			
		||||
#define	IH_OS_SOLARIS		8	/* Solaris	*/
 | 
			
		||||
#define	IH_OS_IRIX		9	/* Irix		*/
 | 
			
		||||
#define	IH_OS_SCO	       10	/* SCO		*/
 | 
			
		||||
#define	IH_OS_DELL	       11	/* Dell		*/
 | 
			
		||||
#define	IH_OS_NCR	       12	/* NCR		*/
 | 
			
		||||
#define	IH_OS_LYNXOS	       13	/* LynxOS	*/
 | 
			
		||||
#define	IH_OS_VXWORKS	       14	/* VxWorks	*/
 | 
			
		||||
#define	IH_OS_PSOS	       15	/* pSOS		*/
 | 
			
		||||
#define	IH_OS_QNX	       16	/* QNX		*/
 | 
			
		||||
#define	IH_OS_U_BOOT	       17	/* Firmware	*/
 | 
			
		||||
#define	IH_OS_RTEMS	       18	/* RTEMS	*/
 | 
			
		||||
#define	IH_OS_ARTOS	       19	/* ARTOS	*/
 | 
			
		||||
#define	IH_OS_UNITY	       20	/* Unity OS	*/
 | 
			
		||||
#define	IH_OS_INTEGRITY	       21	/* INTEGRITY	*/
 | 
			
		||||
#define	IH_OS_OSE	       22	/* OSE		*/
 | 
			
		||||
#define	IH_OS_PLAN9	       23	/* Plan 9	*/
 | 
			
		||||
#define	IH_OS_OPENRTOS	       24	/* OpenRTOS	*/
 | 
			
		||||
#define	IH_OS_ARM_TRUSTED_FIRMWARE 25    /* ARM Trusted Firmware */
 | 
			
		||||
#define	IH_OS_TEE	       26	/* Trusted Execution Environment */
 | 
			
		||||
#define	IH_OS_OPENSBI	       27	/* RISC-V OpenSBI */
 | 
			
		||||
#define	IH_OS_EFI	       28	/* EFI Firmware (e.g. GRUB2) */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * CPU Architecture Codes (supported by Linux)
 | 
			
		||||
 *
 | 
			
		||||
 * The following are exposed to uImage header.
 | 
			
		||||
 * New IDs *MUST* be appended at the end of the list and *NEVER*
 | 
			
		||||
 * inserted for backward compatibility.
 | 
			
		||||
 */
 | 
			
		||||
#define	IH_ARCH_INVALID		0	/* Invalid CPU	*/
 | 
			
		||||
#define	IH_ARCH_ALPHA		1	/* Alpha	*/
 | 
			
		||||
#define	IH_ARCH_ARM		2	/* ARM		*/
 | 
			
		||||
#define	IH_ARCH_I386		3	/* Intel x86	*/
 | 
			
		||||
#define	IH_ARCH_IA64		4	/* IA64		*/
 | 
			
		||||
#define	IH_ARCH_MIPS		5	/* MIPS		*/
 | 
			
		||||
#define	IH_ARCH_MIPS64		6	/* MIPS	 64 Bit */
 | 
			
		||||
#define	IH_ARCH_PPC		7	/* PowerPC	*/
 | 
			
		||||
#define	IH_ARCH_S390		8	/* IBM S390	*/
 | 
			
		||||
#define	IH_ARCH_SH		9	/* SuperH	*/
 | 
			
		||||
#define	IH_ARCH_SPARC	       10	/* Sparc	*/
 | 
			
		||||
#define	IH_ARCH_SPARC64	       11	/* Sparc 64 Bit */
 | 
			
		||||
#define	IH_ARCH_M68K	       12	/* M68K		*/
 | 
			
		||||
#define	IH_ARCH_NIOS	       13	/* Nios-32	*/
 | 
			
		||||
#define	IH_ARCH_MICROBLAZE     14	/* MicroBlaze   */
 | 
			
		||||
#define	IH_ARCH_NIOS2	       15	/* Nios-II	*/
 | 
			
		||||
#define	IH_ARCH_BLACKFIN       16	/* Blackfin	*/
 | 
			
		||||
#define	IH_ARCH_AVR32	       17	/* AVR32	*/
 | 
			
		||||
#define	IH_ARCH_ST200	       18	/* STMicroelectronics ST200  */
 | 
			
		||||
#define	IH_ARCH_SANDBOX	       19	/* Sandbox architecture (test only) */
 | 
			
		||||
#define	IH_ARCH_NDS32	       20	/* ANDES Technology - NDS32  */
 | 
			
		||||
#define	IH_ARCH_OPENRISC       21	/* OpenRISC 1000  */
 | 
			
		||||
#define	IH_ARCH_ARM64	       22	/* ARM64	*/
 | 
			
		||||
#define	IH_ARCH_ARC	       23	/* Synopsys DesignWare ARC */
 | 
			
		||||
#define	IH_ARCH_X86_64	       24	/* AMD x86_64, Intel and Via */
 | 
			
		||||
#define	IH_ARCH_XTENSA	       25	/* Xtensa	*/
 | 
			
		||||
#define	IH_ARCH_RISCV	       26	/* RISC-V */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Image Types
 | 
			
		||||
 *
 | 
			
		||||
 * "Standalone Programs" are directly runnable in the environment
 | 
			
		||||
 *	provided by U-Boot; it is expected that (if they behave
 | 
			
		||||
 *	well) you can continue to work in U-Boot after return from
 | 
			
		||||
 *	the Standalone Program.
 | 
			
		||||
 * "OS Kernel Images" are usually images of some Embedded OS which
 | 
			
		||||
 *	will take over control completely. Usually these programs
 | 
			
		||||
 *	will install their own set of exception handlers, device
 | 
			
		||||
 *	drivers, set up the MMU, etc. - this means, that you cannot
 | 
			
		||||
 *	expect to re-enter U-Boot except by resetting the CPU.
 | 
			
		||||
 * "RAMDisk Images" are more or less just data blocks, and their
 | 
			
		||||
 *	parameters (address, size) are passed to an OS kernel that is
 | 
			
		||||
 *	being started.
 | 
			
		||||
 * "Multi-File Images" contain several images, typically an OS
 | 
			
		||||
 *	(Linux) kernel image and one or more data images like
 | 
			
		||||
 *	RAMDisks. This construct is useful for instance when you want
 | 
			
		||||
 *	to boot over the network using BOOTP etc., where the boot
 | 
			
		||||
 *	server provides just a single image file, but you want to get
 | 
			
		||||
 *	for instance an OS kernel and a RAMDisk image.
 | 
			
		||||
 *
 | 
			
		||||
 *	"Multi-File Images" start with a list of image sizes, each
 | 
			
		||||
 *	image size (in bytes) specified by an "uint32_t" in network
 | 
			
		||||
 *	byte order. This list is terminated by an "(uint32_t)0".
 | 
			
		||||
 *	Immediately after the terminating 0 follow the images, one by
 | 
			
		||||
 *	one, all aligned on "uint32_t" boundaries (size rounded up to
 | 
			
		||||
 *	a multiple of 4 bytes - except for the last file).
 | 
			
		||||
 *
 | 
			
		||||
 * "Firmware Images" are binary images containing firmware (like
 | 
			
		||||
 *	U-Boot or FPGA images) which usually will be programmed to
 | 
			
		||||
 *	flash memory.
 | 
			
		||||
 *
 | 
			
		||||
 * "Script files" are command sequences that will be executed by
 | 
			
		||||
 *	U-Boot's command interpreter; this feature is especially
 | 
			
		||||
 *	useful when you configure U-Boot to use a real shell (hush)
 | 
			
		||||
 *	as command interpreter (=> Shell Scripts).
 | 
			
		||||
 *
 | 
			
		||||
 * The following are exposed to uImage header.
 | 
			
		||||
 * New IDs *MUST* be appended at the end of the list and *NEVER*
 | 
			
		||||
 * inserted for backward compatibility.
 | 
			
		||||
 */
 | 
			
		||||
#define	IH_TYPE_INVALID		0	/* Invalid Image		*/
 | 
			
		||||
#define	IH_TYPE_STANDALONE	1	/* Standalone Program		*/
 | 
			
		||||
#define	IH_TYPE_KERNEL		2	/* OS Kernel Image		*/
 | 
			
		||||
#define	IH_TYPE_RAMDISK		3	/* RAMDisk Image		*/
 | 
			
		||||
#define	IH_TYPE_MULTI		4	/* Multi-File Image		*/
 | 
			
		||||
#define	IH_TYPE_FIRMWARE	5	/* Firmware Image		*/
 | 
			
		||||
#define	IH_TYPE_SCRIPT		6	/* Script file			*/
 | 
			
		||||
#define	IH_TYPE_FILESYSTEM	7	/* Filesystem Image (any type)	*/
 | 
			
		||||
#define	IH_TYPE_FLATDT		8	/* Binary Flat Device Tree Blob	*/
 | 
			
		||||
#define	IH_TYPE_KWBIMAGE	9	/* Kirkwood Boot Image		*/
 | 
			
		||||
#define	IH_TYPE_IMXIMAGE       10	/* Freescale IMXBoot Image	*/
 | 
			
		||||
#define	IH_TYPE_UBLIMAGE       11	/* Davinci UBL Image		*/
 | 
			
		||||
#define	IH_TYPE_OMAPIMAGE      12	/* TI OMAP Config Header Image	*/
 | 
			
		||||
#define	IH_TYPE_AISIMAGE       13	/* TI Davinci AIS Image		*/
 | 
			
		||||
	/* OS Kernel Image, can run from any load address */
 | 
			
		||||
#define	IH_TYPE_KERNEL_NOLOAD  14
 | 
			
		||||
#define	IH_TYPE_PBLIMAGE       15	/* Freescale PBL Boot Image	*/
 | 
			
		||||
#define	IH_TYPE_MXSIMAGE       16	/* Freescale MXSBoot Image	*/
 | 
			
		||||
#define	IH_TYPE_GPIMAGE	       17	/* TI Keystone GPHeader Image	*/
 | 
			
		||||
#define	IH_TYPE_ATMELIMAGE     18	/* ATMEL ROM bootable Image	*/
 | 
			
		||||
#define	IH_TYPE_SOCFPGAIMAGE   19	/* Altera SOCFPGA CV/AV Preloader */
 | 
			
		||||
#define	IH_TYPE_X86_SETUP      20	/* x86 setup.bin Image		*/
 | 
			
		||||
#define	IH_TYPE_LPC32XXIMAGE   21	/* x86 setup.bin Image		*/
 | 
			
		||||
#define	IH_TYPE_LOADABLE       22	/* A list of typeless images	*/
 | 
			
		||||
#define	IH_TYPE_RKIMAGE	       23	/* Rockchip Boot Image		*/
 | 
			
		||||
#define	IH_TYPE_RKSD	       24	/* Rockchip SD card		*/
 | 
			
		||||
#define	IH_TYPE_RKSPI	       25	/* Rockchip SPI image		*/
 | 
			
		||||
#define	IH_TYPE_ZYNQIMAGE      26	/* Xilinx Zynq Boot Image */
 | 
			
		||||
#define	IH_TYPE_ZYNQMPIMAGE    27	/* Xilinx ZynqMP Boot Image */
 | 
			
		||||
#define	IH_TYPE_ZYNQMPBIF      28	/* Xilinx ZynqMP Boot Image (bif) */
 | 
			
		||||
#define	IH_TYPE_FPGA	       29	/* FPGA Image */
 | 
			
		||||
#define	IH_TYPE_VYBRIDIMAGE    30	/* VYBRID .vyb Image */
 | 
			
		||||
#define	IH_TYPE_TEE            31	/* Trusted Execution Environment OS Image */
 | 
			
		||||
#define	IH_TYPE_FIRMWARE_IVT   32	/* Firmware Image with HABv4 IVT */
 | 
			
		||||
#define	IH_TYPE_PMMC           33	/* TI Power Management Micro-Controller Firmware */
 | 
			
		||||
#define	IH_TYPE_STM32IMAGE     34	/* STMicroelectronics STM32 Image */
 | 
			
		||||
#define	IH_TYPE_SOCFPGAIMAGE_V1 35	/* Altera SOCFPGA A10 Preloader	*/
 | 
			
		||||
#define	IH_TYPE_MTKIMAGE       36	/* MediaTek BootROM loadable Image */
 | 
			
		||||
#define	IH_TYPE_IMX8MIMAGE     37	/* Freescale IMX8MBoot Image	*/
 | 
			
		||||
#define	IH_TYPE_IMX8IMAGE      38	/* Freescale IMX8Boot Image	*/
 | 
			
		||||
#define	IH_TYPE_COPRO	       39	/* Coprocessor Image for remoteproc*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Compression Types
 | 
			
		||||
 *
 | 
			
		||||
 * The following are exposed to uImage header.
 | 
			
		||||
 * New IDs *MUST* be appended at the end of the list and *NEVER*
 | 
			
		||||
 * inserted for backward compatibility.
 | 
			
		||||
 */
 | 
			
		||||
#define	IH_COMP_NONE		0	/*  No	 Compression Used	*/
 | 
			
		||||
#define	IH_COMP_GZIP		1	/* gzip	 Compression Used	*/
 | 
			
		||||
#define	IH_COMP_BZIP2		2	/* bzip2 Compression Used	*/
 | 
			
		||||
#define	IH_COMP_LZMA		3	/* lzma  Compression Used	*/
 | 
			
		||||
#define	IH_COMP_LZO		4	/* lzo   Compression Used	*/
 | 
			
		||||
#define	IH_COMP_LZ4		5	/* lz4   Compression Used	*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define LZ4F_MAGIC	0x184D2204	/* LZ4 Magic Number		*/
 | 
			
		||||
#define IH_MAGIC	0x27051956	/* Image Magic Number		*/
 | 
			
		||||
#define IH_NMLEN		32	/* Image Name Length		*/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Magic values specific to "openwrt,uimage" partitions
 | 
			
		||||
 */
 | 
			
		||||
#define IH_MAGIC_OKLI	0x4f4b4c49	/* 'OKLI'			*/
 | 
			
		||||
#define FW_EDIMAX_OFFSET	20	/* Edimax Firmware Offset	*/
 | 
			
		||||
#define FW_MAGIC_EDIMAX	0x43535953	/* Edimax Firmware Magic Number */
 | 
			
		||||
 | 
			
		||||
#endif	/* __UIMAGE_H__ */
 | 
			
		||||
							
								
								
									
										133
									
								
								6.12/target/linux/generic/files/include/linux/ar8216_platform.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								6.12/target/linux/generic/files/include/linux/ar8216_platform.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,133 @@
 | 
			
		|||
/*
 | 
			
		||||
 * AR8216 switch driver platform data
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef AR8216_PLATFORM_H
 | 
			
		||||
#define AR8216_PLATFORM_H
 | 
			
		||||
 | 
			
		||||
enum ar8327_pad_mode {
 | 
			
		||||
	AR8327_PAD_NC = 0,
 | 
			
		||||
	AR8327_PAD_MAC2MAC_MII,
 | 
			
		||||
	AR8327_PAD_MAC2MAC_GMII,
 | 
			
		||||
	AR8327_PAD_MAC_SGMII,
 | 
			
		||||
	AR8327_PAD_MAC2PHY_MII,
 | 
			
		||||
	AR8327_PAD_MAC2PHY_GMII,
 | 
			
		||||
	AR8327_PAD_MAC_RGMII,
 | 
			
		||||
	AR8327_PAD_PHY_GMII,
 | 
			
		||||
	AR8327_PAD_PHY_RGMII,
 | 
			
		||||
	AR8327_PAD_PHY_MII,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ar8327_clk_delay_sel {
 | 
			
		||||
	AR8327_CLK_DELAY_SEL0 = 0,
 | 
			
		||||
	AR8327_CLK_DELAY_SEL1,
 | 
			
		||||
	AR8327_CLK_DELAY_SEL2,
 | 
			
		||||
	AR8327_CLK_DELAY_SEL3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ar8327_pad_cfg {
 | 
			
		||||
	enum ar8327_pad_mode mode;
 | 
			
		||||
	bool rxclk_sel;
 | 
			
		||||
	bool txclk_sel;
 | 
			
		||||
	bool pipe_rxclk_sel;
 | 
			
		||||
	bool txclk_delay_en;
 | 
			
		||||
	bool rxclk_delay_en;
 | 
			
		||||
	bool sgmii_delay_en;
 | 
			
		||||
	enum ar8327_clk_delay_sel txclk_delay_sel;
 | 
			
		||||
	enum ar8327_clk_delay_sel rxclk_delay_sel;
 | 
			
		||||
	bool mac06_exchange_dis;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ar8327_port_speed {
 | 
			
		||||
	AR8327_PORT_SPEED_10 = 0,
 | 
			
		||||
	AR8327_PORT_SPEED_100,
 | 
			
		||||
	AR8327_PORT_SPEED_1000,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ar8327_port_cfg {
 | 
			
		||||
	int force_link:1;
 | 
			
		||||
	enum ar8327_port_speed speed;
 | 
			
		||||
	int txpause:1;
 | 
			
		||||
	int rxpause:1;
 | 
			
		||||
	int duplex:1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ar8327_sgmii_cfg {
 | 
			
		||||
	u32 sgmii_ctrl;
 | 
			
		||||
	bool serdes_aen;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ar8327_led_cfg {
 | 
			
		||||
	u32 led_ctrl0;
 | 
			
		||||
	u32 led_ctrl1;
 | 
			
		||||
	u32 led_ctrl2;
 | 
			
		||||
	u32 led_ctrl3;
 | 
			
		||||
	bool open_drain;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ar8327_led_num {
 | 
			
		||||
	AR8327_LED_PHY0_0 = 0,
 | 
			
		||||
	AR8327_LED_PHY0_1,
 | 
			
		||||
	AR8327_LED_PHY0_2,
 | 
			
		||||
	AR8327_LED_PHY1_0,
 | 
			
		||||
	AR8327_LED_PHY1_1,
 | 
			
		||||
	AR8327_LED_PHY1_2,
 | 
			
		||||
	AR8327_LED_PHY2_0,
 | 
			
		||||
	AR8327_LED_PHY2_1,
 | 
			
		||||
	AR8327_LED_PHY2_2,
 | 
			
		||||
	AR8327_LED_PHY3_0,
 | 
			
		||||
	AR8327_LED_PHY3_1,
 | 
			
		||||
	AR8327_LED_PHY3_2,
 | 
			
		||||
	AR8327_LED_PHY4_0,
 | 
			
		||||
	AR8327_LED_PHY4_1,
 | 
			
		||||
	AR8327_LED_PHY4_2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum ar8327_led_mode {
 | 
			
		||||
	AR8327_LED_MODE_HW = 0,
 | 
			
		||||
	AR8327_LED_MODE_SW,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ar8327_led_info {
 | 
			
		||||
	const char *name;
 | 
			
		||||
	const char *default_trigger;
 | 
			
		||||
	bool active_low;
 | 
			
		||||
	enum ar8327_led_num led_num;
 | 
			
		||||
	enum ar8327_led_mode mode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define AR8327_LED_INFO(_led, _mode, _name) {	\
 | 
			
		||||
	.name = (_name), 	   		\
 | 
			
		||||
	.led_num = AR8327_LED_ ## _led,		\
 | 
			
		||||
	.mode = AR8327_LED_MODE_ ## _mode 	\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ar8327_platform_data {
 | 
			
		||||
	struct ar8327_pad_cfg *pad0_cfg;
 | 
			
		||||
	struct ar8327_pad_cfg *pad5_cfg;
 | 
			
		||||
	struct ar8327_pad_cfg *pad6_cfg;
 | 
			
		||||
	struct ar8327_sgmii_cfg *sgmii_cfg;
 | 
			
		||||
	struct ar8327_port_cfg port0_cfg;
 | 
			
		||||
	struct ar8327_port_cfg port6_cfg;
 | 
			
		||||
	struct ar8327_led_cfg *led_cfg;
 | 
			
		||||
 | 
			
		||||
	int (*get_port_link)(unsigned port);
 | 
			
		||||
 | 
			
		||||
	unsigned num_leds;
 | 
			
		||||
	const struct ar8327_led_info *leds;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* AR8216_PLATFORM_H */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2008 Atheros Communications Inc.
 | 
			
		||||
 * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
 | 
			
		||||
 * Copyright (c) 2010 Daniel Golle <daniel.golle@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and/or distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _LINUX_ATH5K_PLATFORM_H
 | 
			
		||||
#define _LINUX_ATH5K_PLATFORM_H
 | 
			
		||||
 | 
			
		||||
#define ATH5K_PLAT_EEP_MAX_WORDS	2048
 | 
			
		||||
 | 
			
		||||
struct ath5k_platform_data {
 | 
			
		||||
	u16 *eeprom_data;
 | 
			
		||||
	u8 *macaddr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* _LINUX_ATH5K_PLATFORM_H */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2008 Atheros Communications Inc.
 | 
			
		||||
 * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission to use, copy, modify, and/or distribute this software for any
 | 
			
		||||
 * purpose with or without fee is hereby granted, provided that the above
 | 
			
		||||
 * copyright notice and this permission notice appear in all copies.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
			
		||||
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
			
		||||
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
			
		||||
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
			
		||||
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
			
		||||
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _LINUX_ATH9K_PLATFORM_H
 | 
			
		||||
#define _LINUX_ATH9K_PLATFORM_H
 | 
			
		||||
 | 
			
		||||
#define ATH9K_PLAT_EEP_MAX_WORDS	2048
 | 
			
		||||
 | 
			
		||||
struct ath9k_platform_data {
 | 
			
		||||
	const char *eeprom_name;
 | 
			
		||||
 | 
			
		||||
	u16 eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS];
 | 
			
		||||
	u8 *macaddr;
 | 
			
		||||
 | 
			
		||||
	int led_pin;
 | 
			
		||||
	u32 gpio_mask;
 | 
			
		||||
	u32 gpio_val;
 | 
			
		||||
 | 
			
		||||
	u32 bt_active_pin;
 | 
			
		||||
	u32 bt_priority_pin;
 | 
			
		||||
	u32 wlan_active_pin;
 | 
			
		||||
 | 
			
		||||
	bool endian_check;
 | 
			
		||||
	bool is_clk_25mhz;
 | 
			
		||||
	bool tx_gain_buffalo;
 | 
			
		||||
	bool disable_2ghz;
 | 
			
		||||
	bool disable_5ghz;
 | 
			
		||||
	bool led_active_high;
 | 
			
		||||
 | 
			
		||||
	int (*get_mac_revision)(void);
 | 
			
		||||
	int (*external_reset)(void);
 | 
			
		||||
 | 
			
		||||
	bool use_eeprom;
 | 
			
		||||
 | 
			
		||||
	int num_leds;
 | 
			
		||||
	const struct gpio_led *leds;
 | 
			
		||||
 | 
			
		||||
	unsigned num_btns;
 | 
			
		||||
	const struct gpio_keys_button *btns;
 | 
			
		||||
	unsigned btn_poll_interval;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* _LINUX_ATH9K_PLATFORM_H */
 | 
			
		||||
							
								
								
									
										18
									
								
								6.12/target/linux/generic/files/include/linux/mtd/mtk_bmt.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								6.12/target/linux/generic/files/include/linux/mtd/mtk_bmt.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
#ifndef __MTK_BMT_H
 | 
			
		||||
#define __MTK_BMT_H
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_MTD_NAND_MTK_BMT
 | 
			
		||||
int mtk_bmt_attach(struct mtd_info *mtd);
 | 
			
		||||
void mtk_bmt_detach(struct mtd_info *mtd);
 | 
			
		||||
#else
 | 
			
		||||
static inline int mtk_bmt_attach(struct mtd_info *mtd)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void mtk_bmt_detach(struct mtd_info *mtd)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										121
									
								
								6.12/target/linux/generic/files/include/linux/myloader.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								6.12/target/linux/generic/files/include/linux/myloader.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,121 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Compex's MyLoader specific definitions
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2006-2008 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 *  by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _MYLOADER_H_
 | 
			
		||||
#define _MYLOADER_H_
 | 
			
		||||
 | 
			
		||||
/* Myloader specific magic numbers */
 | 
			
		||||
#define MYLO_MAGIC_SYS_PARAMS	0x20021107
 | 
			
		||||
#define MYLO_MAGIC_PARTITIONS	0x20021103
 | 
			
		||||
#define MYLO_MAGIC_BOARD_PARAMS	0x20021103
 | 
			
		||||
 | 
			
		||||
/* Vendor ID's (seems to be same as the PCI vendor ID's) */
 | 
			
		||||
#define VENID_COMPEX		0x11F6
 | 
			
		||||
 | 
			
		||||
/* Devices based on the ADM5120 */
 | 
			
		||||
#define DEVID_COMPEX_NP27G	0x0078
 | 
			
		||||
#define DEVID_COMPEX_NP28G	0x044C
 | 
			
		||||
#define DEVID_COMPEX_NP28GHS	0x044E
 | 
			
		||||
#define DEVID_COMPEX_WP54Gv1C	0x0514
 | 
			
		||||
#define DEVID_COMPEX_WP54G	0x0515
 | 
			
		||||
#define DEVID_COMPEX_WP54AG	0x0546
 | 
			
		||||
#define DEVID_COMPEX_WPP54AG	0x0550
 | 
			
		||||
#define DEVID_COMPEX_WPP54G	0x0555
 | 
			
		||||
 | 
			
		||||
/* Devices based on the Atheros AR2317 */
 | 
			
		||||
#define DEVID_COMPEX_NP25G	0x05E6
 | 
			
		||||
#define DEVID_COMPEX_WPE53G	0x05DC
 | 
			
		||||
 | 
			
		||||
/* Devices based on the Atheros AR71xx */
 | 
			
		||||
#define DEVID_COMPEX_WP543	0x0640
 | 
			
		||||
#define DEVID_COMPEX_WPE72	0x0672
 | 
			
		||||
 | 
			
		||||
/* Devices based on the IXP422 */
 | 
			
		||||
#define DEVID_COMPEX_WP18	0x047E
 | 
			
		||||
#define DEVID_COMPEX_NP18A	0x0489
 | 
			
		||||
 | 
			
		||||
/* Other devices */
 | 
			
		||||
#define DEVID_COMPEX_NP26G8M	0x03E8
 | 
			
		||||
#define DEVID_COMPEX_NP26G16M	0x03E9
 | 
			
		||||
 | 
			
		||||
struct mylo_partition {
 | 
			
		||||
	uint16_t	flags;	/* partition flags */
 | 
			
		||||
	uint16_t	type;	/* type of the partition */
 | 
			
		||||
	uint32_t	addr;	/* relative address of the partition from the
 | 
			
		||||
				   flash start */
 | 
			
		||||
	uint32_t	size;	/* size of the partition in bytes */
 | 
			
		||||
	uint32_t	param;	/* if this is the active partition, the
 | 
			
		||||
				   MyLoader load code to this address */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define PARTITION_FLAG_ACTIVE	0x8000 /* this is the active partition,
 | 
			
		||||
					* MyLoader loads firmware from here */
 | 
			
		||||
#define PARTITION_FLAG_ISRAM	0x2000 /* FIXME: this is a RAM partition? */
 | 
			
		||||
#define PARTIIION_FLAG_RAMLOAD	0x1000 /* FIXME: load this partition into the RAM? */
 | 
			
		||||
#define PARTITION_FLAG_PRELOAD	0x0800 /* the partition data preloaded to RAM
 | 
			
		||||
					* before decompression */
 | 
			
		||||
#define PARTITION_FLAG_LZMA	0x0100 /* partition data compressed by LZMA */
 | 
			
		||||
#define PARTITION_FLAG_HAVEHDR  0x0002 /* the partition data have a header */
 | 
			
		||||
 | 
			
		||||
#define PARTITION_TYPE_FREE	0
 | 
			
		||||
#define PARTITION_TYPE_USED	1
 | 
			
		||||
 | 
			
		||||
#define MYLO_MAX_PARTITIONS	8	/* maximum number of partitions in the
 | 
			
		||||
					   partition table */
 | 
			
		||||
 | 
			
		||||
struct mylo_partition_table {
 | 
			
		||||
	uint32_t	magic;		/* must be MYLO_MAGIC_PARTITIONS */
 | 
			
		||||
	uint32_t	res0;		/* unknown/unused */
 | 
			
		||||
	uint32_t	res1;		/* unknown/unused */
 | 
			
		||||
	uint32_t 	res2;		/* unknown/unused */
 | 
			
		||||
	struct mylo_partition partitions[MYLO_MAX_PARTITIONS];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mylo_partition_header {
 | 
			
		||||
	uint32_t	len;		/* length of the partition data */
 | 
			
		||||
	uint32_t	crc;		/* CRC value of the partition data */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mylo_system_params {
 | 
			
		||||
	uint32_t	magic;		/* must be MYLO_MAGIC_SYS_PARAMS */
 | 
			
		||||
	uint32_t	res0;
 | 
			
		||||
	uint32_t	res1;
 | 
			
		||||
	uint32_t	mylo_ver;
 | 
			
		||||
	uint16_t	vid;		/* Vendor ID */
 | 
			
		||||
	uint16_t	did;		/* Device ID */
 | 
			
		||||
	uint16_t	svid;		/* Sub Vendor ID */
 | 
			
		||||
	uint16_t	sdid;		/* Sub Device ID */
 | 
			
		||||
	uint32_t	rev;		/* device revision */
 | 
			
		||||
	uint32_t	fwhi;
 | 
			
		||||
	uint32_t	fwlo;
 | 
			
		||||
	uint32_t	tftp_addr;
 | 
			
		||||
	uint32_t	prog_start;
 | 
			
		||||
	uint32_t	flash_size;	/* size of boot FLASH in bytes */
 | 
			
		||||
	uint32_t	dram_size;	/* size of onboard RAM in bytes */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mylo_eth_addr {
 | 
			
		||||
	uint8_t	mac[6];
 | 
			
		||||
	uint8_t	csum[2];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define MYLO_ETHADDR_COUNT	8	/* maximum number of ethernet address
 | 
			
		||||
					   in the board parameters */
 | 
			
		||||
 | 
			
		||||
struct mylo_board_params {
 | 
			
		||||
	uint32_t	magic;	/* must be MYLO_MAGIC_BOARD_PARAMS */
 | 
			
		||||
	uint32_t	res0;
 | 
			
		||||
	uint32_t	res1;
 | 
			
		||||
	uint32_t	res2;
 | 
			
		||||
	struct mylo_eth_addr addr[MYLO_ETHADDR_COUNT];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* _MYLOADER_H_*/
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
/*
 | 
			
		||||
 * ADM6996 GPIO platform data
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute  it and/or modify it
 | 
			
		||||
 * under  the terms of the GNU General Public License v2 as published by the
 | 
			
		||||
 * Free Software Foundation
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __PLATFORM_ADM6996_GPIO_H
 | 
			
		||||
#define __PLATFORM_ADM6996_GPIO_H
 | 
			
		||||
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
 | 
			
		||||
enum adm6996_model {
 | 
			
		||||
	ADM6996FC = 1,
 | 
			
		||||
	ADM6996M = 2,
 | 
			
		||||
	ADM6996L = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct adm6996_gpio_platform_data {
 | 
			
		||||
	u8 eecs;
 | 
			
		||||
	u8 eesk;
 | 
			
		||||
	u8 eedi;
 | 
			
		||||
	enum adm6996_model model;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										106
									
								
								6.12/target/linux/generic/files/include/linux/routerboot.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								6.12/target/linux/generic/files/include/linux/routerboot.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,106 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Mikrotik's RouterBOOT definitions
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 *  This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 *  under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 *  by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _ROUTERBOOT_H
 | 
			
		||||
#define _ROUTERBOOT_H
 | 
			
		||||
 | 
			
		||||
#define RB_MAC_SIZE		6
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Magic numbers
 | 
			
		||||
 */
 | 
			
		||||
#define RB_MAGIC_HARD	0x64726148 /* "Hard" */
 | 
			
		||||
#define RB_MAGIC_SOFT	0x74666F53 /* "Soft" */
 | 
			
		||||
#define RB_MAGIC_DAWN	0x6E776144 /* "Dawn" */
 | 
			
		||||
 | 
			
		||||
#define RB_ID_TERMINATOR	0
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ID values for Hardware settings
 | 
			
		||||
 */
 | 
			
		||||
#define RB_ID_HARD_01		1
 | 
			
		||||
#define RB_ID_HARD_02		2
 | 
			
		||||
#define RB_ID_FLASH_INFO	3
 | 
			
		||||
#define RB_ID_MAC_ADDRESS_PACK	4
 | 
			
		||||
#define RB_ID_BOARD_NAME	5
 | 
			
		||||
#define RB_ID_BIOS_VERSION	6
 | 
			
		||||
#define RB_ID_HARD_07		7
 | 
			
		||||
#define RB_ID_SDRAM_TIMINGS	8
 | 
			
		||||
#define RB_ID_DEVICE_TIMINGS	9
 | 
			
		||||
#define RB_ID_SOFTWARE_ID	10
 | 
			
		||||
#define RB_ID_SERIAL_NUMBER	11
 | 
			
		||||
#define RB_ID_HARD_12		12
 | 
			
		||||
#define RB_ID_MEMORY_SIZE	13
 | 
			
		||||
#define RB_ID_MAC_ADDRESS_COUNT	14
 | 
			
		||||
#define RB_ID_HW_OPTIONS	21
 | 
			
		||||
#define RB_ID_WLAN_DATA		22
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ID values for Software settings
 | 
			
		||||
 */
 | 
			
		||||
#define RB_ID_UART_SPEED	1
 | 
			
		||||
#define RB_ID_BOOT_DELAY	2
 | 
			
		||||
#define RB_ID_BOOT_DEVICE	3
 | 
			
		||||
#define RB_ID_BOOT_KEY		4
 | 
			
		||||
#define RB_ID_CPU_MODE		5
 | 
			
		||||
#define RB_ID_FW_VERSION	6
 | 
			
		||||
#define RB_ID_SOFT_07		7
 | 
			
		||||
#define RB_ID_SOFT_08		8
 | 
			
		||||
#define RB_ID_BOOT_PROTOCOL	9
 | 
			
		||||
#define RB_ID_SOFT_10		10
 | 
			
		||||
#define RB_ID_SOFT_11		11
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * UART_SPEED values
 | 
			
		||||
 */
 | 
			
		||||
#define RB_UART_SPEED_115200	0
 | 
			
		||||
#define RB_UART_SPEED_57600	1
 | 
			
		||||
#define RB_UART_SPEED_38400	2
 | 
			
		||||
#define RB_UART_SPEED_19200	3
 | 
			
		||||
#define RB_UART_SPEED_9600	4
 | 
			
		||||
#define RB_UART_SPEED_4800	5
 | 
			
		||||
#define RB_UART_SPEED_2400	6
 | 
			
		||||
#define RB_UART_SPEED_1200	7
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * BOOT_DELAY values
 | 
			
		||||
 */
 | 
			
		||||
#define RB_BOOT_DELAY_0SEC	0
 | 
			
		||||
#define RB_BOOT_DELAY_1SEC	1
 | 
			
		||||
#define RB_BOOT_DELAY_2SEC	2
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * BOOT_DEVICE values
 | 
			
		||||
 */
 | 
			
		||||
#define RB_BOOT_DEVICE_ETHER	0
 | 
			
		||||
#define RB_BOOT_DEVICE_NANDETH	1
 | 
			
		||||
#define RB_BOOT_DEVICE_ETHONCE	2
 | 
			
		||||
#define RB_BOOT_DEVICE_NANDONLY	3
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * BOOT_KEY values
 | 
			
		||||
 */
 | 
			
		||||
#define RB_BOOT_KEY_ANY		0
 | 
			
		||||
#define RB_BOOT_KEY_DEL		1
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * CPU_MODE values
 | 
			
		||||
 */
 | 
			
		||||
#define RB_CPU_MODE_POWERSAVE	0
 | 
			
		||||
#define RB_CPU_MODE_REGULAR	1
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * BOOT_PROTOCOL values
 | 
			
		||||
 */
 | 
			
		||||
#define RB_BOOT_PROTOCOL_BOOTP	0
 | 
			
		||||
#define RB_BOOT_PROTOCOL_DHCP	1
 | 
			
		||||
 | 
			
		||||
#endif /* _ROUTERBOOT_H */
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Platform data definition for the rt2x00 driver
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 * by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _RT2X00_PLATFORM_H
 | 
			
		||||
#define _RT2X00_PLATFORM_H
 | 
			
		||||
 | 
			
		||||
struct rt2x00_platform_data {
 | 
			
		||||
	char *eeprom_file_name;
 | 
			
		||||
	const u8 *mac_address;
 | 
			
		||||
 | 
			
		||||
	int disable_2ghz;
 | 
			
		||||
	int disable_5ghz;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* _RT2X00_PLATFORM_H */
 | 
			
		||||
							
								
								
									
										42
									
								
								6.12/target/linux/generic/files/include/linux/rtl8366.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								6.12/target/linux/generic/files/include/linux/rtl8366.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Platform data definition for the Realtek RTL8366RB/S ethernet switch driver
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 * by the Free Software Foundation.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _RTL8366_H
 | 
			
		||||
#define _RTL8366_H
 | 
			
		||||
 | 
			
		||||
#define RTL8366_DRIVER_NAME	"rtl8366"
 | 
			
		||||
#define RTL8366S_DRIVER_NAME	"rtl8366s"
 | 
			
		||||
#define RTL8366RB_DRIVER_NAME	"rtl8366rb"
 | 
			
		||||
 | 
			
		||||
struct rtl8366_smi;
 | 
			
		||||
 | 
			
		||||
enum rtl8366_type {
 | 
			
		||||
	RTL8366_TYPE_UNKNOWN,
 | 
			
		||||
	RTL8366_TYPE_S,
 | 
			
		||||
	RTL8366_TYPE_RB,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtl8366_initval {
 | 
			
		||||
	unsigned	reg;
 | 
			
		||||
	u16		val;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtl8366_platform_data {
 | 
			
		||||
	unsigned	gpio_sda;
 | 
			
		||||
	unsigned	gpio_sck;
 | 
			
		||||
	void		(*hw_reset)(struct rtl8366_smi *smi, bool active);
 | 
			
		||||
 | 
			
		||||
	unsigned	num_initvals;
 | 
			
		||||
	struct rtl8366_initval *initvals;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum rtl8366_type rtl8366_smi_detect(struct rtl8366_platform_data *pdata);
 | 
			
		||||
 | 
			
		||||
#endif /*  _RTL8366_H */
 | 
			
		||||
							
								
								
									
										63
									
								
								6.12/target/linux/generic/files/include/linux/rtl8367.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								6.12/target/linux/generic/files/include/linux/rtl8367.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Platform data definition for the Realtek RTL8367 ethernet switch driver
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms of the GNU General Public License version 2 as published
 | 
			
		||||
 * by the Free Software Foundation.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _RTL8367_H
 | 
			
		||||
#define _RTL8367_H
 | 
			
		||||
 | 
			
		||||
#define RTL8367_DRIVER_NAME	"rtl8367"
 | 
			
		||||
#define RTL8367B_DRIVER_NAME	"rtl8367b"
 | 
			
		||||
 | 
			
		||||
enum rtl8367_port_speed {
 | 
			
		||||
	RTL8367_PORT_SPEED_10 = 0,
 | 
			
		||||
	RTL8367_PORT_SPEED_100,
 | 
			
		||||
	RTL8367_PORT_SPEED_1000,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtl8367_port_ability {
 | 
			
		||||
	int force_mode;
 | 
			
		||||
	int nway;
 | 
			
		||||
	int txpause;
 | 
			
		||||
	int rxpause;
 | 
			
		||||
	int link;
 | 
			
		||||
	int duplex;
 | 
			
		||||
	enum rtl8367_port_speed speed;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum rtl8367_extif_mode {
 | 
			
		||||
	RTL8367_EXTIF_MODE_DISABLED = 0,
 | 
			
		||||
	RTL8367_EXTIF_MODE_RGMII,
 | 
			
		||||
	RTL8367_EXTIF_MODE_MII_MAC,
 | 
			
		||||
	RTL8367_EXTIF_MODE_MII_PHY,
 | 
			
		||||
	RTL8367_EXTIF_MODE_TMII_MAC,
 | 
			
		||||
	RTL8367_EXTIF_MODE_TMII_PHY,
 | 
			
		||||
	RTL8367_EXTIF_MODE_GMII,
 | 
			
		||||
	RTL8367_EXTIF_MODE_RGMII_33V,
 | 
			
		||||
	RTL8367B_EXTIF_MODE_RMII_MAC = 7,
 | 
			
		||||
	RTL8367B_EXTIF_MODE_RMII_PHY,
 | 
			
		||||
	RTL8367B_EXTIF_MODE_RGMII_33V,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtl8367_extif_config {
 | 
			
		||||
	unsigned int txdelay;
 | 
			
		||||
	unsigned int rxdelay;
 | 
			
		||||
	enum rtl8367_extif_mode mode;
 | 
			
		||||
	struct rtl8367_port_ability ability;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct rtl8367_platform_data {
 | 
			
		||||
	unsigned gpio_sda;
 | 
			
		||||
	unsigned gpio_sck;
 | 
			
		||||
	void (*hw_reset)(bool active);
 | 
			
		||||
 | 
			
		||||
	struct rtl8367_extif_config *extif0_cfg;
 | 
			
		||||
	struct rtl8367_extif_config *extif1_cfg;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /*  _RTL8367_H */
 | 
			
		||||
							
								
								
									
										179
									
								
								6.12/target/linux/generic/files/include/linux/switch.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								6.12/target/linux/generic/files/include/linux/switch.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,179 @@
 | 
			
		|||
/*
 | 
			
		||||
 * switch.h: Switch configuration API
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef _LINUX_SWITCH_H
 | 
			
		||||
#define _LINUX_SWITCH_H
 | 
			
		||||
 | 
			
		||||
#include <net/genetlink.h>
 | 
			
		||||
#include <uapi/linux/switch.h>
 | 
			
		||||
 | 
			
		||||
struct switch_dev;
 | 
			
		||||
struct switch_op;
 | 
			
		||||
struct switch_val;
 | 
			
		||||
struct switch_attr;
 | 
			
		||||
struct switch_attrlist;
 | 
			
		||||
struct switch_led_trigger;
 | 
			
		||||
 | 
			
		||||
int register_switch(struct switch_dev *dev, struct net_device *netdev);
 | 
			
		||||
void unregister_switch(struct switch_dev *dev);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * struct switch_attrlist - attribute list
 | 
			
		||||
 *
 | 
			
		||||
 * @n_attr: number of attributes
 | 
			
		||||
 * @attr: pointer to the attributes array
 | 
			
		||||
 */
 | 
			
		||||
struct switch_attrlist {
 | 
			
		||||
	int n_attr;
 | 
			
		||||
	const struct switch_attr *attr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum switch_port_speed {
 | 
			
		||||
	SWITCH_PORT_SPEED_UNKNOWN = 0,
 | 
			
		||||
	SWITCH_PORT_SPEED_10 = 10,
 | 
			
		||||
	SWITCH_PORT_SPEED_100 = 100,
 | 
			
		||||
	SWITCH_PORT_SPEED_1000 = 1000,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct switch_port_link {
 | 
			
		||||
	bool link;
 | 
			
		||||
	bool duplex;
 | 
			
		||||
	bool aneg;
 | 
			
		||||
	bool tx_flow;
 | 
			
		||||
	bool rx_flow;
 | 
			
		||||
	enum switch_port_speed speed;
 | 
			
		||||
	/* in ethtool adv_t format */
 | 
			
		||||
	u32 eee;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct switch_port_stats {
 | 
			
		||||
	unsigned long long tx_bytes;
 | 
			
		||||
	unsigned long long rx_bytes;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * struct switch_dev_ops - switch driver operations
 | 
			
		||||
 *
 | 
			
		||||
 * @attr_global: global switch attribute list
 | 
			
		||||
 * @attr_port: port attribute list
 | 
			
		||||
 * @attr_vlan: vlan attribute list
 | 
			
		||||
 *
 | 
			
		||||
 * Callbacks:
 | 
			
		||||
 *
 | 
			
		||||
 * @get_vlan_ports: read the port list of a VLAN
 | 
			
		||||
 * @set_vlan_ports: set the port list of a VLAN
 | 
			
		||||
 *
 | 
			
		||||
 * @get_port_pvid: get the primary VLAN ID of a port
 | 
			
		||||
 * @set_port_pvid: set the primary VLAN ID of a port
 | 
			
		||||
 *
 | 
			
		||||
 * @apply_config: apply all changed settings to the switch
 | 
			
		||||
 * @reset_switch: resetting the switch
 | 
			
		||||
 */
 | 
			
		||||
struct switch_dev_ops {
 | 
			
		||||
	struct switch_attrlist attr_global, attr_port, attr_vlan;
 | 
			
		||||
 | 
			
		||||
	int (*get_vlan_ports)(struct switch_dev *dev, struct switch_val *val);
 | 
			
		||||
	int (*set_vlan_ports)(struct switch_dev *dev, struct switch_val *val);
 | 
			
		||||
 | 
			
		||||
	int (*get_port_pvid)(struct switch_dev *dev, int port, int *val);
 | 
			
		||||
	int (*set_port_pvid)(struct switch_dev *dev, int port, int val);
 | 
			
		||||
 | 
			
		||||
	int (*apply_config)(struct switch_dev *dev);
 | 
			
		||||
	int (*reset_switch)(struct switch_dev *dev);
 | 
			
		||||
 | 
			
		||||
	int (*get_port_link)(struct switch_dev *dev, int port,
 | 
			
		||||
			     struct switch_port_link *link);
 | 
			
		||||
	int (*set_port_link)(struct switch_dev *dev, int port,
 | 
			
		||||
			     struct switch_port_link *link);
 | 
			
		||||
	int (*get_port_stats)(struct switch_dev *dev, int port,
 | 
			
		||||
			      struct switch_port_stats *stats);
 | 
			
		||||
 | 
			
		||||
	int (*phy_read16)(struct switch_dev *dev, int addr, u8 reg, u16 *value);
 | 
			
		||||
	int (*phy_write16)(struct switch_dev *dev, int addr, u8 reg, u16 value);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct switch_dev {
 | 
			
		||||
	struct device_node *of_node;
 | 
			
		||||
	const struct switch_dev_ops *ops;
 | 
			
		||||
	/* will be automatically filled */
 | 
			
		||||
	char devname[IFNAMSIZ];
 | 
			
		||||
 | 
			
		||||
	const char *name;
 | 
			
		||||
	/* NB: either alias or netdev must be set */
 | 
			
		||||
	const char *alias;
 | 
			
		||||
	struct net_device *netdev;
 | 
			
		||||
 | 
			
		||||
	unsigned int ports;
 | 
			
		||||
	unsigned int vlans;
 | 
			
		||||
	unsigned int cpu_port;
 | 
			
		||||
 | 
			
		||||
	/* the following fields are internal for swconfig */
 | 
			
		||||
	unsigned int id;
 | 
			
		||||
	struct list_head dev_list;
 | 
			
		||||
	unsigned long def_global, def_port, def_vlan;
 | 
			
		||||
 | 
			
		||||
	struct mutex sw_mutex;
 | 
			
		||||
	struct switch_port *portbuf;
 | 
			
		||||
	struct switch_portmap *portmap;
 | 
			
		||||
	struct switch_port_link linkbuf;
 | 
			
		||||
 | 
			
		||||
	char buf[128];
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SWCONFIG_LEDS
 | 
			
		||||
	struct switch_led_trigger *led_trigger;
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct switch_port {
 | 
			
		||||
	u32 id;
 | 
			
		||||
	u32 flags;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct switch_portmap {
 | 
			
		||||
	u32 virt;
 | 
			
		||||
	const char *s;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct switch_val {
 | 
			
		||||
	const struct switch_attr *attr;
 | 
			
		||||
	unsigned int port_vlan;
 | 
			
		||||
	unsigned int len;
 | 
			
		||||
	union {
 | 
			
		||||
		const char *s;
 | 
			
		||||
		u32 i;
 | 
			
		||||
		struct switch_port *ports;
 | 
			
		||||
		struct switch_port_link *link;
 | 
			
		||||
	} value;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct switch_attr {
 | 
			
		||||
	int disabled;
 | 
			
		||||
	int type;
 | 
			
		||||
	const char *name;
 | 
			
		||||
	const char *description;
 | 
			
		||||
 | 
			
		||||
	int (*set)(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val);
 | 
			
		||||
	int (*get)(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val);
 | 
			
		||||
 | 
			
		||||
	/* for driver internal use */
 | 
			
		||||
	int id;
 | 
			
		||||
	int ofs;
 | 
			
		||||
	int max;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int switch_generic_set_link(struct switch_dev *dev, int port,
 | 
			
		||||
			    struct switch_port_link *link);
 | 
			
		||||
 | 
			
		||||
#endif /* _LINUX_SWITCH_H */
 | 
			
		||||
							
								
								
									
										119
									
								
								6.12/target/linux/generic/files/include/uapi/linux/switch.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								6.12/target/linux/generic/files/include/uapi/linux/switch.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,119 @@
 | 
			
		|||
/*
 | 
			
		||||
 * switch.h: Switch configuration API
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU General Public License
 | 
			
		||||
 * as published by the Free Software Foundation; either version 2
 | 
			
		||||
 * of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _UAPI_LINUX_SWITCH_H
 | 
			
		||||
#define _UAPI_LINUX_SWITCH_H
 | 
			
		||||
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/netdevice.h>
 | 
			
		||||
#include <linux/netlink.h>
 | 
			
		||||
#include <linux/genetlink.h>
 | 
			
		||||
#ifndef __KERNEL__
 | 
			
		||||
#include <netlink/netlink.h>
 | 
			
		||||
#include <netlink/genl/genl.h>
 | 
			
		||||
#include <netlink/genl/ctrl.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* main attributes */
 | 
			
		||||
enum {
 | 
			
		||||
	SWITCH_ATTR_UNSPEC,
 | 
			
		||||
	/* global */
 | 
			
		||||
	SWITCH_ATTR_TYPE,
 | 
			
		||||
	/* device */
 | 
			
		||||
	SWITCH_ATTR_ID,
 | 
			
		||||
	SWITCH_ATTR_DEV_NAME,
 | 
			
		||||
	SWITCH_ATTR_ALIAS,
 | 
			
		||||
	SWITCH_ATTR_NAME,
 | 
			
		||||
	SWITCH_ATTR_VLANS,
 | 
			
		||||
	SWITCH_ATTR_PORTS,
 | 
			
		||||
	SWITCH_ATTR_PORTMAP,
 | 
			
		||||
	SWITCH_ATTR_CPU_PORT,
 | 
			
		||||
	/* attributes */
 | 
			
		||||
	SWITCH_ATTR_OP_ID,
 | 
			
		||||
	SWITCH_ATTR_OP_TYPE,
 | 
			
		||||
	SWITCH_ATTR_OP_NAME,
 | 
			
		||||
	SWITCH_ATTR_OP_PORT,
 | 
			
		||||
	SWITCH_ATTR_OP_VLAN,
 | 
			
		||||
	SWITCH_ATTR_OP_VALUE_INT,
 | 
			
		||||
	SWITCH_ATTR_OP_VALUE_STR,
 | 
			
		||||
	SWITCH_ATTR_OP_VALUE_PORTS,
 | 
			
		||||
	SWITCH_ATTR_OP_VALUE_LINK,
 | 
			
		||||
	SWITCH_ATTR_OP_DESCRIPTION,
 | 
			
		||||
	/* port lists */
 | 
			
		||||
	SWITCH_ATTR_PORT,
 | 
			
		||||
	SWITCH_ATTR_MAX
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	/* port map */
 | 
			
		||||
	SWITCH_PORTMAP_PORTS,
 | 
			
		||||
	SWITCH_PORTMAP_SEGMENT,
 | 
			
		||||
	SWITCH_PORTMAP_VIRT,
 | 
			
		||||
	SWITCH_PORTMAP_MAX
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* commands */
 | 
			
		||||
enum {
 | 
			
		||||
	SWITCH_CMD_UNSPEC,
 | 
			
		||||
	SWITCH_CMD_GET_SWITCH,
 | 
			
		||||
	SWITCH_CMD_NEW_ATTR,
 | 
			
		||||
	SWITCH_CMD_LIST_GLOBAL,
 | 
			
		||||
	SWITCH_CMD_GET_GLOBAL,
 | 
			
		||||
	SWITCH_CMD_SET_GLOBAL,
 | 
			
		||||
	SWITCH_CMD_LIST_PORT,
 | 
			
		||||
	SWITCH_CMD_GET_PORT,
 | 
			
		||||
	SWITCH_CMD_SET_PORT,
 | 
			
		||||
	SWITCH_CMD_LIST_VLAN,
 | 
			
		||||
	SWITCH_CMD_GET_VLAN,
 | 
			
		||||
	SWITCH_CMD_SET_VLAN
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* data types */
 | 
			
		||||
enum switch_val_type {
 | 
			
		||||
	SWITCH_TYPE_UNSPEC,
 | 
			
		||||
	SWITCH_TYPE_INT,
 | 
			
		||||
	SWITCH_TYPE_STRING,
 | 
			
		||||
	SWITCH_TYPE_PORTS,
 | 
			
		||||
	SWITCH_TYPE_LINK,
 | 
			
		||||
	SWITCH_TYPE_NOVAL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* port nested attributes */
 | 
			
		||||
enum {
 | 
			
		||||
	SWITCH_PORT_UNSPEC,
 | 
			
		||||
	SWITCH_PORT_ID,
 | 
			
		||||
	SWITCH_PORT_FLAG_TAGGED,
 | 
			
		||||
	SWITCH_PORT_ATTR_MAX
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* link nested attributes */
 | 
			
		||||
enum {
 | 
			
		||||
	SWITCH_LINK_UNSPEC,
 | 
			
		||||
	SWITCH_LINK_FLAG_LINK,
 | 
			
		||||
	SWITCH_LINK_FLAG_DUPLEX,
 | 
			
		||||
	SWITCH_LINK_FLAG_ANEG,
 | 
			
		||||
	SWITCH_LINK_FLAG_TX_FLOW,
 | 
			
		||||
	SWITCH_LINK_FLAG_RX_FLOW,
 | 
			
		||||
	SWITCH_LINK_SPEED,
 | 
			
		||||
	SWITCH_LINK_FLAG_EEE_100BASET,
 | 
			
		||||
	SWITCH_LINK_FLAG_EEE_1000BASET,
 | 
			
		||||
	SWITCH_LINK_ATTR_MAX,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define SWITCH_ATTR_DEFAULTS_OFFSET	0x1000
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* _UAPI_LINUX_SWITCH_H */
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue