Initial commit
This commit is contained in:
110
target/linux/generic/files/Documentation/networking/adm6996.txt
Normal file
110
target/linux/generic/files/Documentation/networking/adm6996.txt
Normal file
@@ -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
|
||||
63
target/linux/generic/files/arch/mips/fw/myloader/myloader.c
Normal file
63
target/linux/generic/files/arch/mips/fw/myloader/myloader.c
Normal file
@@ -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;
|
||||
}
|
||||
440
target/linux/generic/files/drivers/leds/ledtrig-netdev.c
Normal file
440
target/linux/generic/files/drivers/leds/ledtrig-netdev.c
Normal file
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
* LED Kernel Netdev Trigger
|
||||
*
|
||||
* Toggles the LED to reflect the link and traffic state of a named net device
|
||||
*
|
||||
* Copyright 2007 Oliver Jowett <oliver@opencloud.com>
|
||||
*
|
||||
* Derived from ledtrig-timer.c which is:
|
||||
* Copyright 2005-2006 Openedhand Ltd.
|
||||
* Author: Richard Purdie <rpurdie@openedhand.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/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/leds.h>
|
||||
|
||||
#include "leds.h"
|
||||
|
||||
/*
|
||||
* Configurable sysfs attributes:
|
||||
*
|
||||
* device_name - network device name to monitor
|
||||
*
|
||||
* interval - duration of LED blink, in milliseconds
|
||||
*
|
||||
* mode - either "none" (LED is off) or a space separated list of one or more of:
|
||||
* link: LED's normal state reflects whether the link is up (has carrier) or not
|
||||
* tx: LED blinks on transmitted data
|
||||
* rx: LED blinks on receive data
|
||||
*
|
||||
* Some suggestions:
|
||||
*
|
||||
* Simple link status LED:
|
||||
* $ echo netdev >someled/trigger
|
||||
* $ echo eth0 >someled/device_name
|
||||
* $ echo link >someled/mode
|
||||
*
|
||||
* Ethernet-style link/activity LED:
|
||||
* $ echo netdev >someled/trigger
|
||||
* $ echo eth0 >someled/device_name
|
||||
* $ echo "link tx rx" >someled/mode
|
||||
*
|
||||
* Modem-style tx/rx LEDs:
|
||||
* $ echo netdev >led1/trigger
|
||||
* $ echo ppp0 >led1/device_name
|
||||
* $ echo tx >led1/mode
|
||||
* $ echo netdev >led2/trigger
|
||||
* $ echo ppp0 >led2/device_name
|
||||
* $ echo rx >led2/mode
|
||||
*
|
||||
*/
|
||||
|
||||
#define MODE_LINK 1
|
||||
#define MODE_TX 2
|
||||
#define MODE_RX 4
|
||||
|
||||
struct led_netdev_data {
|
||||
spinlock_t lock;
|
||||
|
||||
struct delayed_work work;
|
||||
struct notifier_block notifier;
|
||||
|
||||
struct led_classdev *led_cdev;
|
||||
struct net_device *net_dev;
|
||||
|
||||
char device_name[IFNAMSIZ];
|
||||
unsigned interval;
|
||||
unsigned mode;
|
||||
unsigned link_up;
|
||||
unsigned last_activity;
|
||||
};
|
||||
|
||||
static void set_baseline_state(struct led_netdev_data *trigger_data)
|
||||
{
|
||||
if ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up)
|
||||
led_set_brightness(trigger_data->led_cdev, LED_FULL);
|
||||
else
|
||||
led_set_brightness(trigger_data->led_cdev, LED_OFF);
|
||||
|
||||
if ((trigger_data->mode & (MODE_TX | MODE_RX)) != 0 && trigger_data->link_up)
|
||||
schedule_delayed_work(&trigger_data->work, trigger_data->interval);
|
||||
}
|
||||
|
||||
static ssize_t led_device_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct led_netdev_data *trigger_data = led_cdev->trigger_data;
|
||||
|
||||
spin_lock_bh(&trigger_data->lock);
|
||||
sprintf(buf, "%s\n", trigger_data->device_name);
|
||||
spin_unlock_bh(&trigger_data->lock);
|
||||
|
||||
return strlen(buf) + 1;
|
||||
}
|
||||
|
||||
static ssize_t led_device_name_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct led_netdev_data *trigger_data = led_cdev->trigger_data;
|
||||
|
||||
if (size >= IFNAMSIZ)
|
||||
return -EINVAL;
|
||||
|
||||
cancel_delayed_work_sync(&trigger_data->work);
|
||||
|
||||
spin_lock_bh(&trigger_data->lock);
|
||||
|
||||
strcpy(trigger_data->device_name, buf);
|
||||
if (size > 0 && trigger_data->device_name[size-1] == '\n')
|
||||
trigger_data->device_name[size-1] = 0;
|
||||
trigger_data->link_up = 0;
|
||||
trigger_data->last_activity = 0;
|
||||
|
||||
if (trigger_data->device_name[0] != 0) {
|
||||
/* check for existing device to update from */
|
||||
trigger_data->net_dev = dev_get_by_name(&init_net, trigger_data->device_name);
|
||||
if (trigger_data->net_dev != NULL)
|
||||
trigger_data->link_up = (dev_get_flags(trigger_data->net_dev) & IFF_LOWER_UP) != 0;
|
||||
}
|
||||
|
||||
set_baseline_state(trigger_data);
|
||||
spin_unlock_bh(&trigger_data->lock);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(device_name, 0644, led_device_name_show, led_device_name_store);
|
||||
|
||||
static ssize_t led_mode_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct led_netdev_data *trigger_data = led_cdev->trigger_data;
|
||||
|
||||
spin_lock_bh(&trigger_data->lock);
|
||||
|
||||
if (trigger_data->mode == 0) {
|
||||
strcpy(buf, "none\n");
|
||||
} else {
|
||||
if (trigger_data->mode & MODE_LINK)
|
||||
strcat(buf, "link ");
|
||||
if (trigger_data->mode & MODE_TX)
|
||||
strcat(buf, "tx ");
|
||||
if (trigger_data->mode & MODE_RX)
|
||||
strcat(buf, "rx ");
|
||||
strcat(buf, "\n");
|
||||
}
|
||||
|
||||
spin_unlock_bh(&trigger_data->lock);
|
||||
|
||||
return strlen(buf)+1;
|
||||
}
|
||||
|
||||
static ssize_t led_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 led_netdev_data *trigger_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 == -1)
|
||||
new_mode = 0;
|
||||
|
||||
if (!strcmp(token, "none"))
|
||||
new_mode = 0;
|
||||
else if (!strcmp(token, "tx"))
|
||||
new_mode |= MODE_TX;
|
||||
else if (!strcmp(token, "rx"))
|
||||
new_mode |= MODE_RX;
|
||||
else if (!strcmp(token, "link"))
|
||||
new_mode |= MODE_LINK;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (new_mode == -1)
|
||||
return -EINVAL;
|
||||
|
||||
cancel_delayed_work_sync(&trigger_data->work);
|
||||
|
||||
spin_lock_bh(&trigger_data->lock);
|
||||
trigger_data->mode = new_mode;
|
||||
set_baseline_state(trigger_data);
|
||||
spin_unlock_bh(&trigger_data->lock);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(mode, 0644, led_mode_show, led_mode_store);
|
||||
|
||||
static ssize_t led_interval_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct led_netdev_data *trigger_data = led_cdev->trigger_data;
|
||||
|
||||
spin_lock_bh(&trigger_data->lock);
|
||||
sprintf(buf, "%u\n", jiffies_to_msecs(trigger_data->interval));
|
||||
spin_unlock_bh(&trigger_data->lock);
|
||||
|
||||
return strlen(buf) + 1;
|
||||
}
|
||||
|
||||
static ssize_t led_interval_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct led_netdev_data *trigger_data = led_cdev->trigger_data;
|
||||
int ret = -EINVAL;
|
||||
char *after;
|
||||
unsigned long value = simple_strtoul(buf, &after, 10);
|
||||
size_t count = after - buf;
|
||||
|
||||
if (isspace(*after))
|
||||
count++;
|
||||
|
||||
/* impose some basic bounds on the timer interval */
|
||||
if (count == size && value >= 5 && value <= 10000) {
|
||||
cancel_delayed_work_sync(&trigger_data->work);
|
||||
|
||||
spin_lock_bh(&trigger_data->lock);
|
||||
trigger_data->interval = msecs_to_jiffies(value);
|
||||
set_baseline_state(trigger_data); /* resets timer */
|
||||
spin_unlock_bh(&trigger_data->lock);
|
||||
|
||||
ret = count;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(interval, 0644, led_interval_show, led_interval_store);
|
||||
|
||||
static int netdev_trig_notify(struct notifier_block *nb,
|
||||
unsigned long evt,
|
||||
void *dv)
|
||||
{
|
||||
struct net_device *dev = netdev_notifier_info_to_dev((struct netdev_notifier_info *) dv);
|
||||
struct led_netdev_data *trigger_data = container_of(nb, struct led_netdev_data, notifier);
|
||||
|
||||
if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (!(dev == trigger_data->net_dev ||
|
||||
(evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name))))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
cancel_delayed_work_sync(&trigger_data->work);
|
||||
|
||||
spin_lock_bh(&trigger_data->lock);
|
||||
|
||||
switch (evt) {
|
||||
case NETDEV_REGISTER:
|
||||
dev_hold(dev);
|
||||
trigger_data->net_dev = dev;
|
||||
trigger_data->link_up = 0;
|
||||
break;
|
||||
case NETDEV_UNREGISTER:
|
||||
dev_put(trigger_data->net_dev);
|
||||
trigger_data->net_dev = NULL;
|
||||
break;
|
||||
default: /* UP / DOWN / CHANGE */
|
||||
trigger_data->link_up = (evt != NETDEV_DOWN && netif_carrier_ok(dev));
|
||||
set_baseline_state(trigger_data);
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&trigger_data->lock);
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/* here's the real work! */
|
||||
static void netdev_trig_work(struct work_struct *work)
|
||||
{
|
||||
struct led_netdev_data *trigger_data = container_of(work, struct led_netdev_data, work.work);
|
||||
struct rtnl_link_stats64 *dev_stats;
|
||||
unsigned new_activity;
|
||||
struct rtnl_link_stats64 temp;
|
||||
|
||||
if (!trigger_data->link_up || !trigger_data->net_dev || (trigger_data->mode & (MODE_TX | MODE_RX)) == 0) {
|
||||
/* we don't need to do timer work, just reflect link state. */
|
||||
led_set_brightness(trigger_data->led_cdev, ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up) ? LED_FULL : LED_OFF);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
|
||||
new_activity =
|
||||
((trigger_data->mode & MODE_TX) ? dev_stats->tx_packets : 0) +
|
||||
((trigger_data->mode & MODE_RX) ? dev_stats->rx_packets : 0);
|
||||
|
||||
if (trigger_data->mode & MODE_LINK) {
|
||||
/* base state is ON (link present) */
|
||||
/* if there's no link, we don't get this far and the LED is off */
|
||||
|
||||
/* OFF -> ON always */
|
||||
/* ON -> OFF on activity */
|
||||
if (trigger_data->led_cdev->brightness == LED_OFF) {
|
||||
led_set_brightness(trigger_data->led_cdev, LED_FULL);
|
||||
} else if (trigger_data->last_activity != new_activity) {
|
||||
led_set_brightness(trigger_data->led_cdev, LED_OFF);
|
||||
}
|
||||
} else {
|
||||
/* base state is OFF */
|
||||
/* ON -> OFF always */
|
||||
/* OFF -> ON on activity */
|
||||
if (trigger_data->led_cdev->brightness == LED_FULL) {
|
||||
led_set_brightness(trigger_data->led_cdev, LED_OFF);
|
||||
} else if (trigger_data->last_activity != new_activity) {
|
||||
led_set_brightness(trigger_data->led_cdev, LED_FULL);
|
||||
}
|
||||
}
|
||||
|
||||
trigger_data->last_activity = new_activity;
|
||||
schedule_delayed_work(&trigger_data->work, trigger_data->interval);
|
||||
}
|
||||
|
||||
static void netdev_trig_activate(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct led_netdev_data *trigger_data;
|
||||
int rc;
|
||||
|
||||
trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
|
||||
if (!trigger_data)
|
||||
return;
|
||||
|
||||
spin_lock_init(&trigger_data->lock);
|
||||
|
||||
trigger_data->notifier.notifier_call = netdev_trig_notify;
|
||||
trigger_data->notifier.priority = 10;
|
||||
|
||||
INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work);
|
||||
|
||||
trigger_data->led_cdev = led_cdev;
|
||||
trigger_data->net_dev = NULL;
|
||||
trigger_data->device_name[0] = 0;
|
||||
|
||||
trigger_data->mode = 0;
|
||||
trigger_data->interval = msecs_to_jiffies(50);
|
||||
trigger_data->link_up = 0;
|
||||
trigger_data->last_activity = 0;
|
||||
|
||||
led_cdev->trigger_data = trigger_data;
|
||||
|
||||
rc = device_create_file(led_cdev->dev, &dev_attr_device_name);
|
||||
if (rc)
|
||||
goto err_out;
|
||||
rc = device_create_file(led_cdev->dev, &dev_attr_mode);
|
||||
if (rc)
|
||||
goto err_out_device_name;
|
||||
rc = device_create_file(led_cdev->dev, &dev_attr_interval);
|
||||
if (rc)
|
||||
goto err_out_mode;
|
||||
|
||||
register_netdevice_notifier(&trigger_data->notifier);
|
||||
return;
|
||||
|
||||
err_out_mode:
|
||||
device_remove_file(led_cdev->dev, &dev_attr_mode);
|
||||
err_out_device_name:
|
||||
device_remove_file(led_cdev->dev, &dev_attr_device_name);
|
||||
err_out:
|
||||
led_cdev->trigger_data = NULL;
|
||||
kfree(trigger_data);
|
||||
}
|
||||
|
||||
static void netdev_trig_deactivate(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct led_netdev_data *trigger_data = led_cdev->trigger_data;
|
||||
|
||||
if (trigger_data) {
|
||||
unregister_netdevice_notifier(&trigger_data->notifier);
|
||||
|
||||
device_remove_file(led_cdev->dev, &dev_attr_device_name);
|
||||
device_remove_file(led_cdev->dev, &dev_attr_mode);
|
||||
device_remove_file(led_cdev->dev, &dev_attr_interval);
|
||||
|
||||
cancel_delayed_work_sync(&trigger_data->work);
|
||||
|
||||
spin_lock_bh(&trigger_data->lock);
|
||||
|
||||
if (trigger_data->net_dev) {
|
||||
dev_put(trigger_data->net_dev);
|
||||
trigger_data->net_dev = NULL;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&trigger_data->lock);
|
||||
|
||||
kfree(trigger_data);
|
||||
}
|
||||
}
|
||||
|
||||
static struct led_trigger netdev_led_trigger = {
|
||||
.name = "netdev",
|
||||
.activate = netdev_trig_activate,
|
||||
.deactivate = netdev_trig_deactivate,
|
||||
};
|
||||
|
||||
static int __init netdev_trig_init(void)
|
||||
{
|
||||
return led_trigger_register(&netdev_led_trigger);
|
||||
}
|
||||
|
||||
static void __exit netdev_trig_exit(void)
|
||||
{
|
||||
led_trigger_unregister(&netdev_led_trigger);
|
||||
}
|
||||
|
||||
module_init(netdev_trig_init);
|
||||
module_exit(netdev_trig_exit);
|
||||
|
||||
MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
|
||||
MODULE_DESCRIPTION("Netdev LED trigger");
|
||||
MODULE_LICENSE("GPL");
|
||||
246
target/linux/generic/files/drivers/misc/owl-loader.c
Normal file
246
target/linux/generic/files/drivers/misc/owl-loader.c
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Initialize Owl Emulation Devices
|
||||
*
|
||||
* Copyright (C) 2016 Christian Lamparter <chunkeey@googlemail.com>
|
||||
* Copyright (C) 2016 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.
|
||||
*
|
||||
* Some devices (like the Cisco Meraki Z1 Cloud Managed Teleworker Gateway)
|
||||
* need to be able to initialize the PCIe wifi device. Normally, this is done
|
||||
* during the early stages of booting linux, because the necessary init code
|
||||
* is read from the memory mapped SPI and passed to pci_enable_ath9k_fixup.
|
||||
* However,this isn't possible for devices which have the init code for the
|
||||
* Atheros chip stored on NAND. Hence, this module can be used to initialze
|
||||
* the chip when the user-space is ready to extract the init code.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/ath9k_platform.h>
|
||||
|
||||
struct owl_ctx {
|
||||
struct completion eeprom_load;
|
||||
};
|
||||
|
||||
#define EEPROM_FILENAME_LEN 100
|
||||
|
||||
#define AR5416_EEPROM_MAGIC 0xa55a
|
||||
|
||||
static int ath9k_pci_fixup(struct pci_dev *pdev, const u16 *cal_data,
|
||||
size_t cal_len)
|
||||
{
|
||||
void __iomem *mem;
|
||||
const void *cal_end = (void *)cal_data + cal_len;
|
||||
const struct {
|
||||
__be16 reg;
|
||||
__be16 low_val;
|
||||
__be16 high_val;
|
||||
} __packed *data;
|
||||
u16 cmd;
|
||||
u32 bar0;
|
||||
bool swap_needed = false;
|
||||
|
||||
if (*cal_data != AR5416_EEPROM_MAGIC) {
|
||||
if (*cal_data != swab16(AR5416_EEPROM_MAGIC)) {
|
||||
dev_err(&pdev->dev, "invalid calibration data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(&pdev->dev, "calibration data needs swapping\n");
|
||||
swap_needed = true;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "fixup device configuration\n");
|
||||
|
||||
mem = pcim_iomap(pdev, 0, 0);
|
||||
if (!mem) {
|
||||
dev_err(&pdev->dev, "ioremap error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &bar0);
|
||||
pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0,
|
||||
pci_resource_start(pdev, 0));
|
||||
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
|
||||
cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
|
||||
pci_write_config_word(pdev, PCI_COMMAND, cmd);
|
||||
|
||||
/* set pointer to first reg address */
|
||||
for (data = (const void *) (cal_data + 3);
|
||||
(const void *) data <= cal_end && data->reg != cpu_to_be16(~0);
|
||||
data++) {
|
||||
u32 val;
|
||||
u16 reg;
|
||||
|
||||
reg = data->reg;
|
||||
val = data->low_val;
|
||||
val |= data->high_val << 16;
|
||||
|
||||
if (swap_needed) {
|
||||
reg = swab16(reg);
|
||||
val = swahb32(val);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LANTIQ
|
||||
val = swab32(val);
|
||||
#endif
|
||||
|
||||
__raw_writel(val, mem + reg);
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
|
||||
cmd &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
|
||||
pci_write_config_word(pdev, PCI_COMMAND, cmd);
|
||||
|
||||
pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, bar0);
|
||||
pcim_iounmap(pdev, mem);
|
||||
|
||||
pci_disable_device(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void owl_fw_cb(const struct firmware *fw, void *context)
|
||||
{
|
||||
struct pci_dev *pdev = (struct pci_dev *) context;
|
||||
struct owl_ctx *ctx = (struct owl_ctx *) pci_get_drvdata(pdev);
|
||||
struct ath9k_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct pci_bus *bus;
|
||||
|
||||
complete(&ctx->eeprom_load);
|
||||
|
||||
if (!fw) {
|
||||
dev_err(&pdev->dev, "no eeprom data received.\n");
|
||||
goto release;
|
||||
}
|
||||
|
||||
/* also note that we are doing *u16 operations on the file */
|
||||
if (fw->size > sizeof(pdata->eeprom_data) || fw->size < 0x200 ||
|
||||
(fw->size & 1) == 1) {
|
||||
dev_err(&pdev->dev, "eeprom file has an invalid size.\n");
|
||||
goto release;
|
||||
}
|
||||
|
||||
if (pdata) {
|
||||
memcpy(pdata->eeprom_data, fw->data, fw->size);
|
||||
|
||||
/*
|
||||
* eeprom has been successfully loaded - pass the data to ath9k
|
||||
* but remove the eeprom_name, so it doesn't try to load it too.
|
||||
*/
|
||||
pdata->eeprom_name = NULL;
|
||||
}
|
||||
|
||||
if (ath9k_pci_fixup(pdev, (const u16 *) fw->data, fw->size))
|
||||
goto release;
|
||||
|
||||
pci_lock_rescan_remove();
|
||||
bus = pdev->bus;
|
||||
pci_stop_and_remove_bus_device(pdev);
|
||||
/*
|
||||
* the device should come back with the proper
|
||||
* ProductId. But we have to initiate a rescan.
|
||||
*/
|
||||
pci_rescan_bus(bus);
|
||||
pci_unlock_rescan_remove();
|
||||
|
||||
release:
|
||||
release_firmware(fw);
|
||||
}
|
||||
|
||||
static const char *owl_get_eeprom_name(struct pci_dev *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ath9k_platform_data *pdata;
|
||||
char *eeprom_name;
|
||||
|
||||
/* try the existing platform data first */
|
||||
pdata = dev_get_platdata(dev);
|
||||
if (pdata && pdata->eeprom_name)
|
||||
return pdata->eeprom_name;
|
||||
|
||||
dev_dbg(dev, "using auto-generated eeprom filename\n");
|
||||
|
||||
eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL);
|
||||
if (!eeprom_name)
|
||||
return NULL;
|
||||
|
||||
/* this should match the pattern used in ath9k/init.c */
|
||||
scnprintf(eeprom_name, EEPROM_FILENAME_LEN, "ath9k-eeprom-pci-%s.bin",
|
||||
dev_name(dev));
|
||||
|
||||
return eeprom_name;
|
||||
}
|
||||
|
||||
static int owl_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct owl_ctx *ctx;
|
||||
const char *eeprom_name;
|
||||
int err = 0;
|
||||
|
||||
if (pcim_enable_device(pdev))
|
||||
return -EIO;
|
||||
|
||||
pcim_pin_device(pdev);
|
||||
|
||||
eeprom_name = owl_get_eeprom_name(pdev);
|
||||
if (!eeprom_name) {
|
||||
dev_err(&pdev->dev, "no eeprom filename found.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx) {
|
||||
dev_err(&pdev->dev, "failed to alloc device context.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
init_completion(&ctx->eeprom_load);
|
||||
|
||||
pci_set_drvdata(pdev, ctx);
|
||||
err = request_firmware_nowait(THIS_MODULE, true, eeprom_name,
|
||||
&pdev->dev, GFP_KERNEL, pdev, owl_fw_cb);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to request caldata (%d).\n", err);
|
||||
kfree(ctx);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void owl_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct owl_ctx *ctx = pci_get_drvdata(pdev);
|
||||
|
||||
if (ctx) {
|
||||
wait_for_completion(&ctx->eeprom_load);
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
kfree(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct pci_device_id owl_pci_table[] = {
|
||||
{ PCI_VDEVICE(ATHEROS, 0xff1c) }, /* PCIe */
|
||||
{ PCI_VDEVICE(ATHEROS, 0xff1d) }, /* PCI */
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, owl_pci_table);
|
||||
|
||||
static struct pci_driver owl_driver = {
|
||||
.name = "owl-loader",
|
||||
.id_table = owl_pci_table,
|
||||
.probe = owl_probe,
|
||||
.remove = owl_remove,
|
||||
};
|
||||
module_pci_driver(owl_driver);
|
||||
MODULE_AUTHOR("Christian Lamparter <chunkeey@googlemail.com>");
|
||||
MODULE_DESCRIPTION("Initializes Atheros' Owl Emulation devices");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
76
target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig
Normal file
76
target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig
Normal file
@@ -0,0 +1,76 @@
|
||||
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
|
||||
default n
|
||||
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_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
|
||||
13
target/linux/generic/files/drivers/mtd/mtdsplit/Makefile
Normal file
13
target/linux/generic/files/drivers/mtd/mtdsplit/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
obj-$(CONFIG_MTD_SPLIT) += mtdsplit.o
|
||||
obj-$(CONFIG_MTD_SPLIT_SEAMA_FW) += mtdsplit_seama.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
|
||||
130
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit.c
Normal file
130
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);
|
||||
|
||||
67
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit.h
Normal file
67
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit.h
Normal file
@@ -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,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,96 @@
|
||||
/*
|
||||
* 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 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 struct mtd_part_parser mtdsplit_eva_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "eva-fw",
|
||||
.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);
|
||||
141
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c
Normal file
141
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c
Normal file
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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/of_fdt.h>
|
||||
|
||||
#include "mtdsplit.h"
|
||||
|
||||
struct fdt_header {
|
||||
uint32_t magic; /* magic word FDT_MAGIC */
|
||||
uint32_t totalsize; /* total size of DT block */
|
||||
uint32_t off_dt_struct; /* offset to structure */
|
||||
uint32_t off_dt_strings; /* offset to strings */
|
||||
uint32_t off_mem_rsvmap; /* offset to memory reserve map */
|
||||
uint32_t version; /* format version */
|
||||
uint32_t last_comp_version; /* last compatible version */
|
||||
|
||||
/* version 2 fields below */
|
||||
uint32_t boot_cpuid_phys; /* Which physical CPU id we're
|
||||
booting on */
|
||||
/* version 3 fields below */
|
||||
uint32_t size_dt_strings; /* size of the strings block */
|
||||
|
||||
/* version 17 fields below */
|
||||
uint32_t size_dt_struct; /* size of the structure block */
|
||||
};
|
||||
|
||||
static int
|
||||
mtdsplit_fit_parse(struct mtd_info *mtd,
|
||||
const struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct fdt_header hdr;
|
||||
size_t hdr_len, retlen;
|
||||
size_t offset;
|
||||
size_t fit_offset, fit_size;
|
||||
size_t rootfs_offset, rootfs_size;
|
||||
struct mtd_partition *parts;
|
||||
int ret;
|
||||
|
||||
hdr_len = sizeof(struct fdt_header);
|
||||
|
||||
/* Parse the MTD device & search for the FIT image location */
|
||||
for(offset = 0; offset < mtd->size; offset += mtd->erasesize) {
|
||||
ret = mtd_read(mtd, 0, 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;
|
||||
}
|
||||
|
||||
/* Search for the rootfs partition after the FIT image */
|
||||
ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size, mtd->size,
|
||||
&rootfs_offset, NULL);
|
||||
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_rounddown_to_eb(fit_size, mtd) + mtd->erasesize;
|
||||
|
||||
parts[1].name = ROOTFS_PART_NAME;
|
||||
parts[1].offset = rootfs_offset;
|
||||
parts[1].size = rootfs_size;
|
||||
|
||||
*pparts = parts;
|
||||
return 2;
|
||||
}
|
||||
|
||||
static struct mtd_part_parser uimage_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "fit-fw",
|
||||
.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,277 @@
|
||||
/*
|
||||
* 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 "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 struct mtd_part_parser jimage_generic_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "jimage-fw",
|
||||
.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,96 @@
|
||||
/*
|
||||
* 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 <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 struct mtd_part_parser mtdsplit_lzma_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "lzma-fw",
|
||||
.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);
|
||||
117
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_minor.c
Normal file
117
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_minor.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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 "mtdsplit.h"
|
||||
|
||||
#define YAFFS_OBJECT_TYPE_FILE 0x1
|
||||
#define YAFFS_OBJECTID_ROOT 0x1
|
||||
#define YAFFS_SUM_UNUSED 0xFFFF
|
||||
#define YAFFS_NAME "kernel"
|
||||
|
||||
#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[sizeof(YAFFS_NAME)];
|
||||
};
|
||||
|
||||
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)
|
||||
return err;
|
||||
|
||||
if (retlen != hdr_len)
|
||||
return -EIO;
|
||||
|
||||
/* match header */
|
||||
if (hdr.yaffs_type != YAFFS_OBJECT_TYPE_FILE)
|
||||
return -EINVAL;
|
||||
|
||||
if (hdr.yaffs_obj_id != YAFFS_OBJECTID_ROOT)
|
||||
return -EINVAL;
|
||||
|
||||
if (hdr.yaffs_sum_unused != YAFFS_SUM_UNUSED)
|
||||
return -EINVAL;
|
||||
|
||||
if (memcmp(hdr.yaffs_name, YAFFS_NAME, sizeof(YAFFS_NAME)))
|
||||
return -EINVAL;
|
||||
|
||||
err = mtd_find_rootfs_from(master, master->erasesize, master->size,
|
||||
&rootfs_offset, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
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 struct mtd_part_parser mtdsplit_minor_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "minor-fw",
|
||||
.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);
|
||||
110
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_seama.c
Normal file
110
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_seama.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 "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 struct mtd_part_parser mtdsplit_seama_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "seama-fw",
|
||||
.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,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 = mtdpart_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,169 @@
|
||||
/*
|
||||
* 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 "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 struct mtd_part_parser mtdsplit_tplink_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "tplink-fw",
|
||||
.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);
|
||||
147
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_trx.c
Normal file
147
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_trx.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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 "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 struct mtd_part_parser trx_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "trx-fw",
|
||||
.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,361 @@
|
||||
/*
|
||||
* 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 "mtdsplit.h"
|
||||
|
||||
/*
|
||||
* uimage_header itself is only 64B, but it may be prepended with another data.
|
||||
* Currently the biggest size is for Edimax devices: 20B + 64B
|
||||
*/
|
||||
#define MAX_HEADER_LEN 84
|
||||
|
||||
#define IH_MAGIC 0x27051956 /* Image Magic Number */
|
||||
#define IH_NMLEN 32 /* Image Name Length */
|
||||
|
||||
#define IH_OS_LINUX 5 /* Linux */
|
||||
|
||||
#define IH_TYPE_KERNEL 2 /* OS Kernel Image */
|
||||
#define IH_TYPE_FILESYSTEM 7 /* Filesystem Image */
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* __mtdsplit_parse_uimage - scan partition and create kernel + rootfs parts
|
||||
*
|
||||
* @find_header: function to call for a block of data that will return offset
|
||||
* 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,
|
||||
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 uimage_offset;
|
||||
size_t uimage_size = 0;
|
||||
size_t rootfs_offset;
|
||||
size_t rootfs_size = 0;
|
||||
int uimage_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 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, MAX_HEADER_LEN);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
ret = find_header(buf, MAX_HEADER_LEN);
|
||||
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 + ret);
|
||||
|
||||
uimage_size = sizeof(*header) + be32_to_cpu(header->ih_size) + ret;
|
||||
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 ssize_t uimage_verify_default(u_char *buf, size_t len)
|
||||
{
|
||||
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\n",
|
||||
be32_to_cpu(header->ih_magic));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (header->ih_os != IH_OS_LINUX) {
|
||||
pr_debug("invalid uImage OS: %08x\n",
|
||||
be32_to_cpu(header->ih_os));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (header->ih_type != IH_TYPE_KERNEL) {
|
||||
pr_debug("invalid uImage type: %08x\n",
|
||||
be32_to_cpu(header->ih_type));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mtdsplit_uimage_parse_generic(struct mtd_info *master,
|
||||
const struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
return __mtdsplit_parse_uimage(master, pparts, data,
|
||||
uimage_verify_default);
|
||||
}
|
||||
|
||||
static struct mtd_part_parser uimage_generic_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "uimage-fw",
|
||||
.parse_fn = mtdsplit_uimage_parse_generic,
|
||||
.type = MTD_PARSER_TYPE_FIRMWARE,
|
||||
};
|
||||
|
||||
#define FW_MAGIC_WNR2000V1 0x32303031
|
||||
#define FW_MAGIC_WNR2000V3 0x32303033
|
||||
#define FW_MAGIC_WNR2000V4 0x32303034
|
||||
#define FW_MAGIC_WNR2200 0x32323030
|
||||
#define FW_MAGIC_WNR612V2 0x32303631
|
||||
#define FW_MAGIC_WNR1000V2 0x31303031
|
||||
#define FW_MAGIC_WNR1000V2_VC 0x31303030
|
||||
#define FW_MAGIC_WNDR3700 0x33373030
|
||||
#define FW_MAGIC_WNDR3700V2 0x33373031
|
||||
#define FW_MAGIC_WPN824N 0x31313030
|
||||
|
||||
static ssize_t uimage_verify_wndr3700(u_char *buf, size_t len)
|
||||
{
|
||||
struct uimage_header *header = (struct uimage_header *)buf;
|
||||
uint8_t expected_type = IH_TYPE_FILESYSTEM;
|
||||
|
||||
switch (be32_to_cpu(header->ih_magic)) {
|
||||
case FW_MAGIC_WNR612V2:
|
||||
case FW_MAGIC_WNR1000V2:
|
||||
case FW_MAGIC_WNR1000V2_VC:
|
||||
case FW_MAGIC_WNR2000V1:
|
||||
case FW_MAGIC_WNR2000V3:
|
||||
case FW_MAGIC_WNR2200:
|
||||
case FW_MAGIC_WNDR3700:
|
||||
case FW_MAGIC_WNDR3700V2:
|
||||
case FW_MAGIC_WPN824N:
|
||||
break;
|
||||
case FW_MAGIC_WNR2000V4:
|
||||
expected_type = IH_TYPE_KERNEL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (header->ih_os != IH_OS_LINUX ||
|
||||
header->ih_type != expected_type)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mtdsplit_uimage_parse_netgear(struct mtd_info *master,
|
||||
const struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
return __mtdsplit_parse_uimage(master, pparts, data,
|
||||
uimage_verify_wndr3700);
|
||||
}
|
||||
|
||||
static struct mtd_part_parser uimage_netgear_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "netgear-fw",
|
||||
.parse_fn = mtdsplit_uimage_parse_netgear,
|
||||
.type = MTD_PARSER_TYPE_FIRMWARE,
|
||||
};
|
||||
|
||||
/**************************************************
|
||||
* Edimax
|
||||
**************************************************/
|
||||
|
||||
#define FW_EDIMAX_OFFSET 20
|
||||
#define FW_MAGIC_EDIMAX 0x43535953
|
||||
|
||||
static ssize_t uimage_find_edimax(u_char *buf, size_t len)
|
||||
{
|
||||
u32 *magic;
|
||||
|
||||
if (len < FW_EDIMAX_OFFSET + sizeof(struct uimage_header)) {
|
||||
pr_err("Buffer too small for checking Edimax header\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
magic = (u32 *)buf;
|
||||
if (be32_to_cpu(*magic) != FW_MAGIC_EDIMAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (!uimage_verify_default(buf + FW_EDIMAX_OFFSET, len))
|
||||
return FW_EDIMAX_OFFSET;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
mtdsplit_uimage_parse_edimax(struct mtd_info *master,
|
||||
const struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
return __mtdsplit_parse_uimage(master, pparts, data,
|
||||
uimage_find_edimax);
|
||||
}
|
||||
|
||||
static struct mtd_part_parser uimage_edimax_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "edimax-fw",
|
||||
.parse_fn = mtdsplit_uimage_parse_edimax,
|
||||
.type = MTD_PARSER_TYPE_FIRMWARE,
|
||||
};
|
||||
|
||||
/**************************************************
|
||||
* Init
|
||||
**************************************************/
|
||||
|
||||
static int __init mtdsplit_uimage_init(void)
|
||||
{
|
||||
register_mtd_parser(&uimage_generic_parser);
|
||||
register_mtd_parser(&uimage_netgear_parser);
|
||||
register_mtd_parser(&uimage_edimax_parser);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(mtdsplit_uimage_init);
|
||||
124
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_wrgg.c
Normal file
124
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_wrgg.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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 "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);
|
||||
} 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 struct mtd_part_parser mtdsplit_wrgg_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "wrgg-fw",
|
||||
.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);
|
||||
182
target/linux/generic/files/drivers/mtd/myloader.c
Normal file
182
target/linux/generic/files/drivers/mtd/myloader.c
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Parse MyLoader-style flash partition tables and produce a Linux partition
|
||||
* array to match.
|
||||
*
|
||||
* Copyright (C) 2007-2009 Gabor Juhos <juhosg@openwrt.org>
|
||||
*
|
||||
* This file was based on drivers/mtd/redboot.c
|
||||
* Author: Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.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/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
#include <linux/myloader.h>
|
||||
|
||||
#define BLOCK_LEN_MIN 0x10000
|
||||
#define PART_NAME_LEN 32
|
||||
|
||||
struct part_data {
|
||||
struct mylo_partition_table tab;
|
||||
char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN];
|
||||
};
|
||||
|
||||
static int myloader_parse_partitions(struct mtd_info *master,
|
||||
const struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
struct part_data *buf;
|
||||
struct mylo_partition_table *tab;
|
||||
struct mylo_partition *part;
|
||||
struct mtd_partition *mtd_parts;
|
||||
struct mtd_partition *mtd_part;
|
||||
int num_parts;
|
||||
int ret, i;
|
||||
size_t retlen;
|
||||
char *names;
|
||||
unsigned long offset;
|
||||
unsigned long blocklen;
|
||||
|
||||
buf = vmalloc(sizeof(*buf));
|
||||
if (!buf) {
|
||||
return -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
tab = &buf->tab;
|
||||
|
||||
blocklen = master->erasesize;
|
||||
if (blocklen < BLOCK_LEN_MIN)
|
||||
blocklen = BLOCK_LEN_MIN;
|
||||
|
||||
offset = blocklen;
|
||||
|
||||
/* Find the partition table */
|
||||
for (i = 0; i < 4; i++, offset += blocklen) {
|
||||
printk(KERN_DEBUG "%s: searching for MyLoader partition table"
|
||||
" at offset 0x%lx\n", master->name, offset);
|
||||
|
||||
ret = mtd_read(master, offset, sizeof(*buf), &retlen,
|
||||
(void *)buf);
|
||||
if (ret)
|
||||
goto out_free_buf;
|
||||
|
||||
if (retlen != sizeof(*buf)) {
|
||||
ret = -EIO;
|
||||
goto out_free_buf;
|
||||
}
|
||||
|
||||
/* Check for Partition Table magic number */
|
||||
if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS))
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) {
|
||||
printk(KERN_DEBUG "%s: no MyLoader partition table found\n",
|
||||
master->name);
|
||||
ret = 0;
|
||||
goto out_free_buf;
|
||||
}
|
||||
|
||||
/* The MyLoader and the Partition Table is always present */
|
||||
num_parts = 2;
|
||||
|
||||
/* Detect number of used partitions */
|
||||
for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
|
||||
part = &tab->partitions[i];
|
||||
|
||||
if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
|
||||
continue;
|
||||
|
||||
num_parts++;
|
||||
}
|
||||
|
||||
mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) +
|
||||
num_parts * PART_NAME_LEN), GFP_KERNEL);
|
||||
|
||||
if (!mtd_parts) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_buf;
|
||||
}
|
||||
|
||||
mtd_part = mtd_parts;
|
||||
names = (char *)&mtd_parts[num_parts];
|
||||
|
||||
strncpy(names, "myloader", PART_NAME_LEN);
|
||||
mtd_part->name = names;
|
||||
mtd_part->offset = 0;
|
||||
mtd_part->size = offset;
|
||||
mtd_part->mask_flags = MTD_WRITEABLE;
|
||||
mtd_part++;
|
||||
names += PART_NAME_LEN;
|
||||
|
||||
strncpy(names, "partition_table", PART_NAME_LEN);
|
||||
mtd_part->name = names;
|
||||
mtd_part->offset = offset;
|
||||
mtd_part->size = blocklen;
|
||||
mtd_part->mask_flags = MTD_WRITEABLE;
|
||||
mtd_part++;
|
||||
names += PART_NAME_LEN;
|
||||
|
||||
for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
|
||||
part = &tab->partitions[i];
|
||||
|
||||
if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
|
||||
continue;
|
||||
|
||||
if ((buf->names[i][0]) && (buf->names[i][0] != '\xff'))
|
||||
strncpy(names, buf->names[i], PART_NAME_LEN);
|
||||
else
|
||||
snprintf(names, PART_NAME_LEN, "partition%d", i);
|
||||
|
||||
mtd_part->offset = le32_to_cpu(part->addr);
|
||||
mtd_part->size = le32_to_cpu(part->size);
|
||||
mtd_part->name = names;
|
||||
mtd_part++;
|
||||
names += PART_NAME_LEN;
|
||||
}
|
||||
|
||||
*pparts = mtd_parts;
|
||||
ret = num_parts;
|
||||
|
||||
out_free_buf:
|
||||
vfree(buf);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct mtd_part_parser myloader_mtd_parser = {
|
||||
.owner = THIS_MODULE,
|
||||
.parse_fn = myloader_parse_partitions,
|
||||
.name = "MyLoader",
|
||||
};
|
||||
|
||||
static int __init myloader_mtd_parser_init(void)
|
||||
{
|
||||
register_mtd_parser(&myloader_mtd_parser);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit myloader_mtd_parser_exit(void)
|
||||
{
|
||||
deregister_mtd_parser(&myloader_mtd_parser);
|
||||
}
|
||||
|
||||
module_init(myloader_mtd_parser_init);
|
||||
module_exit(myloader_mtd_parser_exit);
|
||||
|
||||
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
|
||||
MODULE_DESCRIPTION("Parsing code for MyLoader partition tables");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
1241
target/linux/generic/files/drivers/net/phy/adm6996.c
Normal file
1241
target/linux/generic/files/drivers/net/phy/adm6996.c
Normal file
File diff suppressed because it is too large
Load Diff
186
target/linux/generic/files/drivers/net/phy/adm6996.h
Normal file
186
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
|
||||
2313
target/linux/generic/files/drivers/net/phy/ar8216.c
Normal file
2313
target/linux/generic/files/drivers/net/phy/ar8216.c
Normal file
File diff suppressed because it is too large
Load Diff
644
target/linux/generic/files/drivers/net/phy/ar8216.h
Normal file
644
target/linux/generic/files/drivers/net/phy/ar8216.h
Normal file
@@ -0,0 +1,644 @@
|
||||
/*
|
||||
* 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 AR8316_NUM_VLANS 4096
|
||||
|
||||
/* size of the vlan table */
|
||||
#define AR8X16_MAX_VLANS 128
|
||||
#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 AR8236_FM_CPU_BROADCAST_EN BIT(26)
|
||||
#define AR8236_FM_CPU_BCAST_FWD_EN BIT(25)
|
||||
|
||||
#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_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_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_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 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 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
|
||||
};
|
||||
|
||||
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 {
|
||||
u8 port;
|
||||
u8 mac[6];
|
||||
};
|
||||
|
||||
struct ar8xxx_priv;
|
||||
|
||||
struct ar8xxx_mib_desc {
|
||||
unsigned int size;
|
||||
unsigned int offset;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
const struct ar8xxx_mib_desc *mib_decs;
|
||||
unsigned num_mibs;
|
||||
unsigned mib_func;
|
||||
};
|
||||
|
||||
struct ar8xxx_priv {
|
||||
struct switch_dev dev;
|
||||
struct mii_bus *mii_bus;
|
||||
struct phy_device *phy;
|
||||
|
||||
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;
|
||||
int mib_next_port;
|
||||
u64 *mib_stats;
|
||||
|
||||
struct list_head list;
|
||||
unsigned int use_count;
|
||||
|
||||
/* all fields below are cleared on reset */
|
||||
bool vlan;
|
||||
u16 vlan_id[AR8X16_MAX_VLANS];
|
||||
u8 vlan_table[AR8X16_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_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_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
|
||||
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
|
||||
1503
target/linux/generic/files/drivers/net/phy/ar8327.c
Normal file
1503
target/linux/generic/files/drivers/net/phy/ar8327.c
Normal file
File diff suppressed because it is too large
Load Diff
325
target/linux/generic/files/drivers/net/phy/ar8327.h
Normal file
325
target/linux/generic/files/drivers/net/phy/ar8327.h
Normal file
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* 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_STRIP 0x010
|
||||
#define AR8327_POWER_ON_STRIP_POWER_ON_SEL BIT(31)
|
||||
#define AR8327_POWER_ON_STRIP_LED_OPEN_EN BIT(24)
|
||||
#define AR8327_POWER_ON_STRIP_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_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)
|
||||
|
||||
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
target/linux/generic/files/drivers/net/phy/b53/Kconfig
Normal file
37
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
target/linux/generic/files/drivers/net/phy/b53/Makefile
Normal file
10
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
|
||||
1722
target/linux/generic/files/drivers/net/phy/b53/b53_common.c
Normal file
1722
target/linux/generic/files/drivers/net/phy/b53/b53_common.c
Normal file
File diff suppressed because it is too large
Load Diff
436
target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c
Normal file
436
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.current_page = 0xff;
|
||||
dev.priv = phydev->mdio.bus;
|
||||
dev.ops = &b53_mdio_ops;
|
||||
dev.pdata = NULL;
|
||||
mutex_init(&dev.reg_mutex);
|
||||
|
||||
ret = b53_switch_detect(&dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (is5325(&dev) || is5365(&dev))
|
||||
phydev->supported = SUPPORTED_100baseT_Full;
|
||||
else
|
||||
phydev->supported = SUPPORTED_1000baseT_Full;
|
||||
|
||||
phydev->advertising = phydev->supported;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int b53_phy_config_init(struct phy_device *phydev)
|
||||
{
|
||||
struct b53_device *dev;
|
||||
int ret;
|
||||
|
||||
dev = b53_switch_alloc(&phydev->mdio.dev, &b53_mdio_ops, phydev->mdio.bus);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
/* 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;
|
||||
|
||||
ret = b53_switch_register(dev);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to register switch: %i\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
phydev->priv = dev;
|
||||
|
||||
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 = 0x00406000,
|
||||
.name = "Broadcom B53 (3)",
|
||||
.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,
|
||||
};
|
||||
|
||||
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");
|
||||
241
target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c
Normal file
241
target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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 "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_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_switch_register(dev);
|
||||
}
|
||||
|
||||
static int b53_mmap_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct b53_device *dev = platform_get_drvdata(pdev);
|
||||
|
||||
if (dev)
|
||||
b53_switch_remove(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 0x00406000
|
||||
|
||||
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 & 0xfffffc00) == 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);
|
||||
341
target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
Normal file
341
target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* 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_switch_alloc(struct device *base, struct b53_io_ops *ops,
|
||||
void *priv);
|
||||
|
||||
int b53_switch_detect(struct b53_device *dev);
|
||||
|
||||
int b53_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>
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
|
||||
#include <linux/bcm47xx_nvram.h>
|
||||
#endif
|
||||
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
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
|
||||
return bcm47xx_nvram_gpio_pin("robo_reset");
|
||||
#else
|
||||
return -ENOENT;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
348
target/linux/generic/files/drivers/net/phy/b53/b53_regs.h
Normal file
348
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
target/linux/generic/files/drivers/net/phy/b53/b53_spi.c
Normal file
344
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_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_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");
|
||||
378
target/linux/generic/files/drivers/net/phy/b53/b53_srab.c
Normal file
378
target/linux/generic/files/drivers/net/phy/b53/b53_srab.c
Normal file
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* 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 "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_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_switch_register(dev);
|
||||
}
|
||||
|
||||
static int b53_srab_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct b53_device *dev = platform_get_drvdata(pdev);
|
||||
|
||||
if (dev)
|
||||
b53_switch_remove(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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");
|
||||
1377
target/linux/generic/files/drivers/net/phy/ip17xx.c
Normal file
1377
target/linux/generic/files/drivers/net/phy/ip17xx.c
Normal file
File diff suppressed because it is too large
Load Diff
947
target/linux/generic/files/drivers/net/phy/mvsw61xx.c
Normal file
947
target/linux/generic/files/drivers/net/phy/mvsw61xx.c
Normal file
@@ -0,0 +1,947 @@
|
||||
/*
|
||||
* Marvell 88E61xx switch driver
|
||||
*
|
||||
* Copyright (c) 2014 Claudio Leite <leitec@staticky.com>
|
||||
* Copyright (c) 2014 Nikita Nazarenko <nnazarenko@radiofid.com>
|
||||
*
|
||||
* Based on code (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 v2 as published by the
|
||||
* Free Software Foundation
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/switch.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "mvsw61xx.h"
|
||||
|
||||
MODULE_DESCRIPTION("Marvell 88E61xx Switch driver");
|
||||
MODULE_AUTHOR("Claudio Leite <leitec@staticky.com>");
|
||||
MODULE_AUTHOR("Nikita Nazarenko <nnazarenko@radiofid.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:mvsw61xx");
|
||||
|
||||
/*
|
||||
* Register access is done through direct or indirect addressing,
|
||||
* depending on how the switch is physically connected.
|
||||
*
|
||||
* Direct addressing: all port and global registers directly
|
||||
* accessible via an address/register pair
|
||||
*
|
||||
* Indirect addressing: switch is mapped at a single address,
|
||||
* port and global registers accessible via a single command/data
|
||||
* register pair
|
||||
*/
|
||||
|
||||
static int
|
||||
mvsw61xx_wait_mask_raw(struct mii_bus *bus, int addr,
|
||||
int reg, u16 mask, u16 val)
|
||||
{
|
||||
int i = 100;
|
||||
u16 r;
|
||||
|
||||
do {
|
||||
r = bus->read(bus, addr, reg);
|
||||
if ((r & mask) == val)
|
||||
return 0;
|
||||
} while (--i > 0);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static u16
|
||||
r16(struct mii_bus *bus, bool indirect, int base_addr, int addr, int reg)
|
||||
{
|
||||
u16 ind_addr;
|
||||
|
||||
if (!indirect)
|
||||
return bus->read(bus, addr, reg);
|
||||
|
||||
/* Indirect read: First, make sure switch is free */
|
||||
mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
|
||||
MV_INDIRECT_INPROGRESS, 0);
|
||||
|
||||
/* Load address and request read */
|
||||
ind_addr = MV_INDIRECT_READ | (addr << MV_INDIRECT_ADDR_S) | reg;
|
||||
bus->write(bus, base_addr, MV_INDIRECT_REG_CMD,
|
||||
ind_addr);
|
||||
|
||||
/* Wait until it's ready */
|
||||
mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
|
||||
MV_INDIRECT_INPROGRESS, 0);
|
||||
|
||||
/* Read the requested data */
|
||||
return bus->read(bus, base_addr, MV_INDIRECT_REG_DATA);
|
||||
}
|
||||
|
||||
static void
|
||||
w16(struct mii_bus *bus, bool indirect, int base_addr, int addr,
|
||||
int reg, u16 val)
|
||||
{
|
||||
u16 ind_addr;
|
||||
|
||||
if (!indirect) {
|
||||
bus->write(bus, addr, reg, val);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Indirect write: First, make sure switch is free */
|
||||
mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
|
||||
MV_INDIRECT_INPROGRESS, 0);
|
||||
|
||||
/* Load the data to be written */
|
||||
bus->write(bus, base_addr, MV_INDIRECT_REG_DATA, val);
|
||||
|
||||
/* Wait again for switch to be free */
|
||||
mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
|
||||
MV_INDIRECT_INPROGRESS, 0);
|
||||
|
||||
/* Load address, and issue write command */
|
||||
ind_addr = MV_INDIRECT_WRITE | (addr << MV_INDIRECT_ADDR_S) | reg;
|
||||
bus->write(bus, base_addr, MV_INDIRECT_REG_CMD,
|
||||
ind_addr);
|
||||
}
|
||||
|
||||
/* swconfig support */
|
||||
|
||||
static inline u16
|
||||
sr16(struct switch_dev *dev, int addr, int reg)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
|
||||
return r16(state->bus, state->is_indirect, state->base_addr, addr, reg);
|
||||
}
|
||||
|
||||
static inline void
|
||||
sw16(struct switch_dev *dev, int addr, int reg, u16 val)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
|
||||
w16(state->bus, state->is_indirect, state->base_addr, addr, reg, val);
|
||||
}
|
||||
|
||||
static int
|
||||
mvsw61xx_wait_mask_s(struct switch_dev *dev, int addr,
|
||||
int reg, u16 mask, u16 val)
|
||||
{
|
||||
int i = 100;
|
||||
u16 r;
|
||||
|
||||
do {
|
||||
r = sr16(dev, addr, reg) & mask;
|
||||
if (r == val)
|
||||
return 0;
|
||||
} while (--i > 0);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int
|
||||
mvsw61xx_mdio_read(struct switch_dev *dev, int addr, int reg)
|
||||
{
|
||||
sw16(dev, MV_GLOBAL2REG(SMI_OP),
|
||||
MV_INDIRECT_READ | (addr << MV_INDIRECT_ADDR_S) | reg);
|
||||
|
||||
if (mvsw61xx_wait_mask_s(dev, MV_GLOBAL2REG(SMI_OP),
|
||||
MV_INDIRECT_INPROGRESS, 0) < 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return sr16(dev, MV_GLOBAL2REG(SMI_DATA));
|
||||
}
|
||||
|
||||
static int
|
||||
mvsw61xx_mdio_write(struct switch_dev *dev, int addr, int reg, u16 val)
|
||||
{
|
||||
sw16(dev, MV_GLOBAL2REG(SMI_DATA), val);
|
||||
|
||||
sw16(dev, MV_GLOBAL2REG(SMI_OP),
|
||||
MV_INDIRECT_WRITE | (addr << MV_INDIRECT_ADDR_S) | reg);
|
||||
|
||||
return mvsw61xx_wait_mask_s(dev, MV_GLOBAL2REG(SMI_OP),
|
||||
MV_INDIRECT_INPROGRESS, 0) < 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvsw61xx_mdio_page_read(struct switch_dev *dev, int port, int page, int reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, page);
|
||||
ret = mvsw61xx_mdio_read(dev, port, reg);
|
||||
mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
mvsw61xx_mdio_page_write(struct switch_dev *dev, int port, int page, int reg,
|
||||
u16 val)
|
||||
{
|
||||
mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, page);
|
||||
mvsw61xx_mdio_write(dev, port, reg, val);
|
||||
mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
mvsw61xx_get_port_mask(struct switch_dev *dev,
|
||||
const struct switch_attr *attr, struct switch_val *val)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
char *buf = state->buf;
|
||||
int port, len, i;
|
||||
u16 reg;
|
||||
|
||||
port = val->port_vlan;
|
||||
reg = sr16(dev, MV_PORTREG(VLANMAP, port)) & MV_PORTS_MASK;
|
||||
|
||||
len = sprintf(buf, "0x%04x: ", reg);
|
||||
|
||||
for (i = 0; i < MV_PORTS; i++) {
|
||||
if (reg & (1 << i))
|
||||
len += sprintf(buf + len, "%d ", i);
|
||||
else if (i == port)
|
||||
len += sprintf(buf + len, "(%d) ", i);
|
||||
}
|
||||
|
||||
val->value.s = buf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvsw61xx_get_port_qmode(struct switch_dev *dev,
|
||||
const struct switch_attr *attr, struct switch_val *val)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
|
||||
val->value.i = state->ports[val->port_vlan].qmode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvsw61xx_set_port_qmode(struct switch_dev *dev,
|
||||
const struct switch_attr *attr, struct switch_val *val)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
|
||||
state->ports[val->port_vlan].qmode = val->value.i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvsw61xx_get_port_pvid(struct switch_dev *dev, int port, int *val)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
|
||||
*val = state->ports[port].pvid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvsw61xx_set_port_pvid(struct switch_dev *dev, int port, int val)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
|
||||
if (val < 0 || val >= MV_VLANS)
|
||||
return -EINVAL;
|
||||
|
||||
state->ports[port].pvid = (u16)val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvsw61xx_get_port_link(struct switch_dev *dev, int port,
|
||||
struct switch_port_link *link)
|
||||
{
|
||||
u16 status, speed;
|
||||
|
||||
status = sr16(dev, MV_PORTREG(STATUS, port));
|
||||
|
||||
link->link = status & MV_PORT_STATUS_LINK;
|
||||
if (!link->link)
|
||||
return 0;
|
||||
|
||||
link->duplex = status & MV_PORT_STATUS_FDX;
|
||||
|
||||
speed = (status & MV_PORT_STATUS_SPEED_MASK) >>
|
||||
MV_PORT_STATUS_SPEED_SHIFT;
|
||||
|
||||
switch (speed) {
|
||||
case MV_PORT_STATUS_SPEED_10:
|
||||
link->speed = SWITCH_PORT_SPEED_10;
|
||||
break;
|
||||
case MV_PORT_STATUS_SPEED_100:
|
||||
link->speed = SWITCH_PORT_SPEED_100;
|
||||
break;
|
||||
case MV_PORT_STATUS_SPEED_1000:
|
||||
link->speed = SWITCH_PORT_SPEED_1000;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvsw61xx_get_vlan_ports(struct switch_dev *dev,
|
||||
struct switch_val *val)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
int i, j, mode, vno;
|
||||
|
||||
vno = val->port_vlan;
|
||||
|
||||
if (vno <= 0 || vno >= dev->vlans)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0, j = 0; i < dev->ports; i++) {
|
||||
if (state->vlans[vno].mask & (1 << i)) {
|
||||
val->value.ports[j].id = i;
|
||||
|
||||
mode = (state->vlans[vno].port_mode >> (i * 4)) & 0xf;
|
||||
if (mode == MV_VTUCTL_EGRESS_TAGGED)
|
||||
val->value.ports[j].flags =
|
||||
(1 << SWITCH_PORT_FLAG_TAGGED);
|
||||
else
|
||||
val->value.ports[j].flags = 0;
|
||||
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
val->len = j;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvsw61xx_set_vlan_ports(struct switch_dev *dev,
|
||||
struct switch_val *val)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
int i, mode, pno, vno;
|
||||
|
||||
vno = val->port_vlan;
|
||||
|
||||
if (vno <= 0 || vno >= dev->vlans)
|
||||
return -EINVAL;
|
||||
|
||||
state->vlans[vno].mask = 0;
|
||||
state->vlans[vno].port_mode = 0;
|
||||
state->vlans[vno].port_sstate = 0;
|
||||
|
||||
if(state->vlans[vno].vid == 0)
|
||||
state->vlans[vno].vid = vno;
|
||||
|
||||
for (i = 0; i < val->len; i++) {
|
||||
pno = val->value.ports[i].id;
|
||||
|
||||
state->vlans[vno].mask |= (1 << pno);
|
||||
if (val->value.ports[i].flags &
|
||||
(1 << SWITCH_PORT_FLAG_TAGGED))
|
||||
mode = MV_VTUCTL_EGRESS_TAGGED;
|
||||
else
|
||||
mode = MV_VTUCTL_EGRESS_UNTAGGED;
|
||||
|
||||
state->vlans[vno].port_mode |= mode << (pno * 4);
|
||||
state->vlans[vno].port_sstate |=
|
||||
MV_STUCTL_STATE_FORWARDING << (pno * 4 + 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* DISCARD is nonzero, so it must be explicitly
|
||||
* set on ports not in the VLAN.
|
||||
*/
|
||||
for (i = 0; i < dev->ports; i++)
|
||||
if (!(state->vlans[vno].mask & (1 << i)))
|
||||
state->vlans[vno].port_mode |=
|
||||
MV_VTUCTL_DISCARD << (i * 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvsw61xx_get_vlan_port_based(struct switch_dev *dev,
|
||||
const struct switch_attr *attr, struct switch_val *val)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
int vno = val->port_vlan;
|
||||
|
||||
if (vno <= 0 || vno >= dev->vlans)
|
||||
return -EINVAL;
|
||||
|
||||
if (state->vlans[vno].port_based)
|
||||
val->value.i = 1;
|
||||
else
|
||||
val->value.i = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvsw61xx_set_vlan_port_based(struct switch_dev *dev,
|
||||
const struct switch_attr *attr, struct switch_val *val)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
int vno = val->port_vlan;
|
||||
|
||||
if (vno <= 0 || vno >= dev->vlans)
|
||||
return -EINVAL;
|
||||
|
||||
if (val->value.i == 1)
|
||||
state->vlans[vno].port_based = true;
|
||||
else
|
||||
state->vlans[vno].port_based = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvsw61xx_get_vid(struct switch_dev *dev,
|
||||
const struct switch_attr *attr, struct switch_val *val)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
int vno = val->port_vlan;
|
||||
|
||||
if (vno <= 0 || vno >= dev->vlans)
|
||||
return -EINVAL;
|
||||
|
||||
val->value.i = state->vlans[vno].vid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvsw61xx_set_vid(struct switch_dev *dev,
|
||||
const struct switch_attr *attr, struct switch_val *val)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
int vno = val->port_vlan;
|
||||
|
||||
if (vno <= 0 || vno >= dev->vlans)
|
||||
return -EINVAL;
|
||||
|
||||
state->vlans[vno].vid = val->value.i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvsw61xx_get_enable_vlan(struct switch_dev *dev,
|
||||
const struct switch_attr *attr, struct switch_val *val)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
|
||||
val->value.i = state->vlan_enabled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvsw61xx_set_enable_vlan(struct switch_dev *dev,
|
||||
const struct switch_attr *attr, struct switch_val *val)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
|
||||
state->vlan_enabled = val->value.i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvsw61xx_vtu_program(struct switch_dev *dev)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
u16 v1, v2, s1, s2;
|
||||
int i;
|
||||
|
||||
/* Flush */
|
||||
mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
|
||||
MV_VTUOP_INPROGRESS, 0);
|
||||
sw16(dev, MV_GLOBALREG(VTU_OP),
|
||||
MV_VTUOP_INPROGRESS | MV_VTUOP_PURGE);
|
||||
|
||||
/* Write VLAN table */
|
||||
for (i = 1; i < dev->vlans; i++) {
|
||||
if (state->vlans[i].mask == 0 ||
|
||||
state->vlans[i].vid == 0 ||
|
||||
state->vlans[i].port_based == true)
|
||||
continue;
|
||||
|
||||
mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
|
||||
MV_VTUOP_INPROGRESS, 0);
|
||||
|
||||
/* Write per-VLAN port state into STU */
|
||||
s1 = (u16) (state->vlans[i].port_sstate & 0xffff);
|
||||
s2 = (u16) ((state->vlans[i].port_sstate >> 16) & 0xffff);
|
||||
|
||||
sw16(dev, MV_GLOBALREG(VTU_VID), MV_VTU_VID_VALID);
|
||||
sw16(dev, MV_GLOBALREG(VTU_SID), i);
|
||||
sw16(dev, MV_GLOBALREG(VTU_DATA1), s1);
|
||||
sw16(dev, MV_GLOBALREG(VTU_DATA2), s2);
|
||||
sw16(dev, MV_GLOBALREG(VTU_DATA3), 0);
|
||||
|
||||
sw16(dev, MV_GLOBALREG(VTU_OP),
|
||||
MV_VTUOP_INPROGRESS | MV_VTUOP_STULOAD);
|
||||
mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
|
||||
MV_VTUOP_INPROGRESS, 0);
|
||||
|
||||
/* Write VLAN information into VTU */
|
||||
v1 = (u16) (state->vlans[i].port_mode & 0xffff);
|
||||
v2 = (u16) ((state->vlans[i].port_mode >> 16) & 0xffff);
|
||||
|
||||
sw16(dev, MV_GLOBALREG(VTU_VID),
|
||||
MV_VTU_VID_VALID | state->vlans[i].vid);
|
||||
sw16(dev, MV_GLOBALREG(VTU_SID), i);
|
||||
sw16(dev, MV_GLOBALREG(VTU_FID), i);
|
||||
sw16(dev, MV_GLOBALREG(VTU_DATA1), v1);
|
||||
sw16(dev, MV_GLOBALREG(VTU_DATA2), v2);
|
||||
sw16(dev, MV_GLOBALREG(VTU_DATA3), 0);
|
||||
|
||||
sw16(dev, MV_GLOBALREG(VTU_OP),
|
||||
MV_VTUOP_INPROGRESS | MV_VTUOP_LOAD);
|
||||
mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
|
||||
MV_VTUOP_INPROGRESS, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mvsw61xx_vlan_port_config(struct switch_dev *dev, int vno)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
int i, mode;
|
||||
|
||||
for (i = 0; i < dev->ports; i++) {
|
||||
if (!(state->vlans[vno].mask & (1 << i)))
|
||||
continue;
|
||||
|
||||
mode = (state->vlans[vno].port_mode >> (i * 4)) & 0xf;
|
||||
|
||||
if(mode != MV_VTUCTL_EGRESS_TAGGED)
|
||||
state->ports[i].pvid = state->vlans[vno].vid;
|
||||
|
||||
if (state->vlans[vno].port_based) {
|
||||
state->ports[i].mask |= state->vlans[vno].mask;
|
||||
state->ports[i].fdb = vno;
|
||||
}
|
||||
else
|
||||
state->ports[i].qmode = MV_8021Q_MODE_SECURE;
|
||||
}
|
||||
}
|
||||
|
||||
static int mvsw61xx_update_state(struct switch_dev *dev)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
int i;
|
||||
u16 reg;
|
||||
|
||||
if (!state->registered)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Set 802.1q-only mode if vlan_enabled is true.
|
||||
*
|
||||
* Without this, even if 802.1q is enabled for
|
||||
* a port/VLAN, it still depends on the port-based
|
||||
* VLAN mask being set.
|
||||
*
|
||||
* With this setting, port-based VLANs are still
|
||||
* functional, provided the VID is not in the VTU.
|
||||
*/
|
||||
reg = sr16(dev, MV_GLOBAL2REG(SDET_POLARITY));
|
||||
|
||||
if (state->vlan_enabled)
|
||||
reg |= MV_8021Q_VLAN_ONLY;
|
||||
else
|
||||
reg &= ~MV_8021Q_VLAN_ONLY;
|
||||
|
||||
sw16(dev, MV_GLOBAL2REG(SDET_POLARITY), reg);
|
||||
|
||||
/*
|
||||
* Set port-based VLAN masks on each port
|
||||
* based only on VLAN definitions known to
|
||||
* the driver (i.e. in state).
|
||||
*
|
||||
* This means any pre-existing port mapping is
|
||||
* wiped out once our driver is initialized.
|
||||
*/
|
||||
for (i = 0; i < dev->ports; i++) {
|
||||
state->ports[i].mask = 0;
|
||||
state->ports[i].qmode = MV_8021Q_MODE_DISABLE;
|
||||
}
|
||||
|
||||
for (i = 0; i < dev->vlans; i++)
|
||||
mvsw61xx_vlan_port_config(dev, i);
|
||||
|
||||
for (i = 0; i < dev->ports; i++) {
|
||||
reg = sr16(dev, MV_PORTREG(VLANID, i)) & ~MV_PVID_MASK;
|
||||
reg |= state->ports[i].pvid;
|
||||
sw16(dev, MV_PORTREG(VLANID, i), reg);
|
||||
|
||||
state->ports[i].mask &= ~(1 << i);
|
||||
|
||||
/* set default forwarding DB number and port mask */
|
||||
reg = sr16(dev, MV_PORTREG(CONTROL1, i)) & ~MV_FDB_HI_MASK;
|
||||
reg |= (state->ports[i].fdb >> MV_FDB_HI_SHIFT) &
|
||||
MV_FDB_HI_MASK;
|
||||
sw16(dev, MV_PORTREG(CONTROL1, i), reg);
|
||||
|
||||
reg = ((state->ports[i].fdb & 0xf) << MV_FDB_LO_SHIFT) |
|
||||
state->ports[i].mask;
|
||||
sw16(dev, MV_PORTREG(VLANMAP, i), reg);
|
||||
|
||||
reg = sr16(dev, MV_PORTREG(CONTROL2, i)) &
|
||||
~MV_8021Q_MODE_MASK;
|
||||
reg |= state->ports[i].qmode << MV_8021Q_MODE_SHIFT;
|
||||
sw16(dev, MV_PORTREG(CONTROL2, i), reg);
|
||||
}
|
||||
|
||||
mvsw61xx_vtu_program(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvsw61xx_apply(struct switch_dev *dev)
|
||||
{
|
||||
return mvsw61xx_update_state(dev);
|
||||
}
|
||||
|
||||
static void mvsw61xx_enable_serdes(struct switch_dev *dev)
|
||||
{
|
||||
int bmcr = mvsw61xx_mdio_page_read(dev, MV_REG_FIBER_SERDES,
|
||||
MV_PAGE_FIBER_SERDES, MII_BMCR);
|
||||
if (bmcr < 0)
|
||||
return;
|
||||
|
||||
if (bmcr & BMCR_PDOWN)
|
||||
mvsw61xx_mdio_page_write(dev, MV_REG_FIBER_SERDES,
|
||||
MV_PAGE_FIBER_SERDES, MII_BMCR,
|
||||
bmcr & ~BMCR_PDOWN);
|
||||
}
|
||||
|
||||
static int _mvsw61xx_reset(struct switch_dev *dev, bool full)
|
||||
{
|
||||
struct mvsw61xx_state *state = get_state(dev);
|
||||
int i;
|
||||
u16 reg;
|
||||
|
||||
/* Disable all ports before reset */
|
||||
for (i = 0; i < dev->ports; i++) {
|
||||
reg = sr16(dev, MV_PORTREG(CONTROL, i)) &
|
||||
~MV_PORTCTRL_FORWARDING;
|
||||
sw16(dev, MV_PORTREG(CONTROL, i), reg);
|
||||
}
|
||||
|
||||
reg = sr16(dev, MV_GLOBALREG(CONTROL)) | MV_CONTROL_RESET;
|
||||
|
||||
sw16(dev, MV_GLOBALREG(CONTROL), reg);
|
||||
if (mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(CONTROL),
|
||||
MV_CONTROL_RESET, 0) < 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
for (i = 0; i < dev->ports; i++) {
|
||||
state->ports[i].fdb = 0;
|
||||
state->ports[i].qmode = 0;
|
||||
state->ports[i].mask = 0;
|
||||
state->ports[i].pvid = 0;
|
||||
|
||||
/* Force flow control off */
|
||||
reg = sr16(dev, MV_PORTREG(PHYCTL, i)) & ~MV_PHYCTL_FC_MASK;
|
||||
reg |= MV_PHYCTL_FC_DISABLE;
|
||||
sw16(dev, MV_PORTREG(PHYCTL, i), reg);
|
||||
|
||||
/* Set port association vector */
|
||||
sw16(dev, MV_PORTREG(ASSOC, i), (1 << i));
|
||||
|
||||
/* power up phys */
|
||||
if (full && i < 5) {
|
||||
mvsw61xx_mdio_write(dev, i, MII_MV_SPEC_CTRL,
|
||||
MV_SPEC_MDI_CROSS_AUTO |
|
||||
MV_SPEC_ENERGY_DETECT |
|
||||
MV_SPEC_DOWNSHIFT_COUNTER);
|
||||
mvsw61xx_mdio_write(dev, i, MII_BMCR, BMCR_RESET |
|
||||
BMCR_ANENABLE | BMCR_FULLDPLX |
|
||||
BMCR_SPEED1000);
|
||||
}
|
||||
|
||||
/* enable SerDes if necessary */
|
||||
if (full && i >= 5 && state->model == MV_IDENT_VALUE_6176) {
|
||||
u16 sts = sr16(dev, MV_PORTREG(STATUS, i));
|
||||
u16 mode = sts & MV_PORT_STATUS_CMODE_MASK;
|
||||
|
||||
if (mode == MV_PORT_STATUS_CMODE_100BASE_X ||
|
||||
mode == MV_PORT_STATUS_CMODE_1000BASE_X ||
|
||||
mode == MV_PORT_STATUS_CMODE_SGMII) {
|
||||
mvsw61xx_enable_serdes(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < dev->vlans; i++) {
|
||||
state->vlans[i].port_based = false;
|
||||
state->vlans[i].mask = 0;
|
||||
state->vlans[i].vid = 0;
|
||||
state->vlans[i].port_mode = 0;
|
||||
state->vlans[i].port_sstate = 0;
|
||||
}
|
||||
|
||||
state->vlan_enabled = 0;
|
||||
|
||||
mvsw61xx_update_state(dev);
|
||||
|
||||
/* Re-enable ports */
|
||||
for (i = 0; i < dev->ports; i++) {
|
||||
reg = sr16(dev, MV_PORTREG(CONTROL, i)) |
|
||||
MV_PORTCTRL_FORWARDING;
|
||||
sw16(dev, MV_PORTREG(CONTROL, i), reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvsw61xx_reset(struct switch_dev *dev)
|
||||
{
|
||||
return _mvsw61xx_reset(dev, false);
|
||||
}
|
||||
|
||||
enum {
|
||||
MVSW61XX_ENABLE_VLAN,
|
||||
};
|
||||
|
||||
enum {
|
||||
MVSW61XX_VLAN_PORT_BASED,
|
||||
MVSW61XX_VLAN_ID,
|
||||
};
|
||||
|
||||
enum {
|
||||
MVSW61XX_PORT_MASK,
|
||||
MVSW61XX_PORT_QMODE,
|
||||
};
|
||||
|
||||
static const struct switch_attr mvsw61xx_global[] = {
|
||||
[MVSW61XX_ENABLE_VLAN] = {
|
||||
.id = MVSW61XX_ENABLE_VLAN,
|
||||
.type = SWITCH_TYPE_INT,
|
||||
.name = "enable_vlan",
|
||||
.description = "Enable 802.1q VLAN support",
|
||||
.get = mvsw61xx_get_enable_vlan,
|
||||
.set = mvsw61xx_set_enable_vlan,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct switch_attr mvsw61xx_vlan[] = {
|
||||
[MVSW61XX_VLAN_PORT_BASED] = {
|
||||
.id = MVSW61XX_VLAN_PORT_BASED,
|
||||
.type = SWITCH_TYPE_INT,
|
||||
.name = "port_based",
|
||||
.description = "Use port-based (non-802.1q) VLAN only",
|
||||
.get = mvsw61xx_get_vlan_port_based,
|
||||
.set = mvsw61xx_set_vlan_port_based,
|
||||
},
|
||||
[MVSW61XX_VLAN_ID] = {
|
||||
.id = MVSW61XX_VLAN_ID,
|
||||
.type = SWITCH_TYPE_INT,
|
||||
.name = "vid",
|
||||
.description = "Get/set VLAN ID",
|
||||
.get = mvsw61xx_get_vid,
|
||||
.set = mvsw61xx_set_vid,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct switch_attr mvsw61xx_port[] = {
|
||||
[MVSW61XX_PORT_MASK] = {
|
||||
.id = MVSW61XX_PORT_MASK,
|
||||
.type = SWITCH_TYPE_STRING,
|
||||
.description = "Port-based VLAN mask",
|
||||
.name = "mask",
|
||||
.get = mvsw61xx_get_port_mask,
|
||||
.set = NULL,
|
||||
},
|
||||
[MVSW61XX_PORT_QMODE] = {
|
||||
.id = MVSW61XX_PORT_QMODE,
|
||||
.type = SWITCH_TYPE_INT,
|
||||
.description = "802.1q mode: 0=off/1=fallback/2=check/3=secure",
|
||||
.name = "qmode",
|
||||
.get = mvsw61xx_get_port_qmode,
|
||||
.set = mvsw61xx_set_port_qmode,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct switch_dev_ops mvsw61xx_ops = {
|
||||
.attr_global = {
|
||||
.attr = mvsw61xx_global,
|
||||
.n_attr = ARRAY_SIZE(mvsw61xx_global),
|
||||
},
|
||||
.attr_vlan = {
|
||||
.attr = mvsw61xx_vlan,
|
||||
.n_attr = ARRAY_SIZE(mvsw61xx_vlan),
|
||||
},
|
||||
.attr_port = {
|
||||
.attr = mvsw61xx_port,
|
||||
.n_attr = ARRAY_SIZE(mvsw61xx_port),
|
||||
},
|
||||
.get_port_link = mvsw61xx_get_port_link,
|
||||
.get_port_pvid = mvsw61xx_get_port_pvid,
|
||||
.set_port_pvid = mvsw61xx_set_port_pvid,
|
||||
.get_vlan_ports = mvsw61xx_get_vlan_ports,
|
||||
.set_vlan_ports = mvsw61xx_set_vlan_ports,
|
||||
.apply_config = mvsw61xx_apply,
|
||||
.reset_switch = mvsw61xx_reset,
|
||||
};
|
||||
|
||||
/* end swconfig stuff */
|
||||
|
||||
static int mvsw61xx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mvsw61xx_state *state;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device_node *mdio;
|
||||
char *model_str;
|
||||
u32 val;
|
||||
int err;
|
||||
|
||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (!state)
|
||||
return -ENOMEM;
|
||||
|
||||
mdio = of_parse_phandle(np, "mii-bus", 0);
|
||||
if (!mdio) {
|
||||
dev_err(&pdev->dev, "Couldn't get MII bus handle\n");
|
||||
err = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
state->bus = of_mdio_find_bus(mdio);
|
||||
if (!state->bus) {
|
||||
dev_err(&pdev->dev, "Couldn't find MII bus from handle\n");
|
||||
err = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
state->is_indirect = of_property_read_bool(np, "is-indirect");
|
||||
|
||||
if (state->is_indirect) {
|
||||
if (of_property_read_u32(np, "reg", &val)) {
|
||||
dev_err(&pdev->dev, "Switch address not specified\n");
|
||||
err = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
state->base_addr = val;
|
||||
} else {
|
||||
state->base_addr = MV_BASE;
|
||||
}
|
||||
|
||||
state->model = r16(state->bus, state->is_indirect, state->base_addr,
|
||||
MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
|
||||
|
||||
switch(state->model) {
|
||||
case MV_IDENT_VALUE_6171:
|
||||
model_str = MV_IDENT_STR_6171;
|
||||
break;
|
||||
case MV_IDENT_VALUE_6172:
|
||||
model_str = MV_IDENT_STR_6172;
|
||||
break;
|
||||
case MV_IDENT_VALUE_6176:
|
||||
model_str = MV_IDENT_STR_6176;
|
||||
break;
|
||||
case MV_IDENT_VALUE_6352:
|
||||
model_str = MV_IDENT_STR_6352;
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "No compatible switch found at 0x%02x\n",
|
||||
state->base_addr);
|
||||
err = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, state);
|
||||
dev_info(&pdev->dev, "Found %s at %s:%02x\n", model_str,
|
||||
state->bus->id, state->base_addr);
|
||||
|
||||
dev_info(&pdev->dev, "Using %sdirect addressing\n",
|
||||
(state->is_indirect ? "in" : ""));
|
||||
|
||||
if (of_property_read_u32(np, "cpu-port-0", &val)) {
|
||||
dev_err(&pdev->dev, "CPU port not set\n");
|
||||
err = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
state->cpu_port0 = val;
|
||||
|
||||
if (!of_property_read_u32(np, "cpu-port-1", &val))
|
||||
state->cpu_port1 = val;
|
||||
else
|
||||
state->cpu_port1 = -1;
|
||||
|
||||
state->dev.vlans = MV_VLANS;
|
||||
state->dev.cpu_port = state->cpu_port0;
|
||||
state->dev.ports = MV_PORTS;
|
||||
state->dev.name = model_str;
|
||||
state->dev.ops = &mvsw61xx_ops;
|
||||
state->dev.alias = dev_name(&pdev->dev);
|
||||
|
||||
_mvsw61xx_reset(&state->dev, true);
|
||||
|
||||
err = register_switch(&state->dev, NULL);
|
||||
if (err < 0)
|
||||
goto out_err;
|
||||
|
||||
state->registered = true;
|
||||
|
||||
return 0;
|
||||
out_err:
|
||||
kfree(state);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
mvsw61xx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mvsw61xx_state *state = platform_get_drvdata(pdev);
|
||||
|
||||
if (state->registered)
|
||||
unregister_switch(&state->dev);
|
||||
|
||||
kfree(state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mvsw61xx_match[] = {
|
||||
{ .compatible = "marvell,88e6171" },
|
||||
{ .compatible = "marvell,88e6172" },
|
||||
{ .compatible = "marvell,88e6176" },
|
||||
{ .compatible = "marvell,88e6352" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mvsw61xx_match);
|
||||
|
||||
static struct platform_driver mvsw61xx_driver = {
|
||||
.probe = mvsw61xx_probe,
|
||||
.remove = mvsw61xx_remove,
|
||||
.driver = {
|
||||
.name = "mvsw61xx",
|
||||
.of_match_table = of_match_ptr(mvsw61xx_match),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init mvsw61xx_module_init(void)
|
||||
{
|
||||
return platform_driver_register(&mvsw61xx_driver);
|
||||
}
|
||||
late_initcall(mvsw61xx_module_init);
|
||||
|
||||
static void __exit mvsw61xx_module_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mvsw61xx_driver);
|
||||
}
|
||||
module_exit(mvsw61xx_module_exit);
|
||||
292
target/linux/generic/files/drivers/net/phy/mvsw61xx.h
Normal file
292
target/linux/generic/files/drivers/net/phy/mvsw61xx.h
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Marvell 88E61xx switch driver
|
||||
*
|
||||
* Copyright (c) 2014 Claudio Leite <leitec@staticky.com>
|
||||
* Copyright (c) 2014 Nikita Nazarenko <nnazarenko@radiofid.com>
|
||||
*
|
||||
* Based on code (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 v2 as published by the
|
||||
* Free Software Foundation
|
||||
*/
|
||||
|
||||
#ifndef __MVSW61XX_H
|
||||
#define __MVSW61XX_H
|
||||
|
||||
#define MV_PORTS 7
|
||||
#define MV_PORTS_MASK ((1 << MV_PORTS) - 1)
|
||||
|
||||
#define MV_BASE 0x10
|
||||
|
||||
#define MV_SWITCHPORT_BASE 0x10
|
||||
#define MV_SWITCHPORT(_n) (MV_SWITCHPORT_BASE + (_n))
|
||||
#define MV_SWITCHREGS (MV_BASE + 0xb)
|
||||
|
||||
#define MV_VLANS 64
|
||||
|
||||
enum {
|
||||
MV_PORT_STATUS = 0x00,
|
||||
MV_PORT_PHYCTL = 0x01,
|
||||
MV_PORT_JAMCTL = 0x02,
|
||||
MV_PORT_IDENT = 0x03,
|
||||
MV_PORT_CONTROL = 0x04,
|
||||
MV_PORT_CONTROL1 = 0x05,
|
||||
MV_PORT_VLANMAP = 0x06,
|
||||
MV_PORT_VLANID = 0x07,
|
||||
MV_PORT_CONTROL2 = 0x08,
|
||||
MV_PORT_ASSOC = 0x0b,
|
||||
MV_PORT_RX_DISCARD_LOW = 0x10,
|
||||
MV_PORT_RX_DISCARD_HIGH = 0x11,
|
||||
MV_PORT_IN_FILTERED = 0x12,
|
||||
MV_PORT_OUT_ACCEPTED = 0x13,
|
||||
};
|
||||
#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type
|
||||
|
||||
enum {
|
||||
MV_PORT_STATUS_FDX = (1 << 10),
|
||||
MV_PORT_STATUS_LINK = (1 << 11),
|
||||
};
|
||||
|
||||
enum {
|
||||
MV_PORT_STATUS_CMODE_100BASE_X = 0x8,
|
||||
MV_PORT_STATUS_CMODE_1000BASE_X = 0x9,
|
||||
MV_PORT_STATUS_CMODE_SGMII = 0xa,
|
||||
};
|
||||
|
||||
#define MV_PORT_STATUS_CMODE_MASK 0xf
|
||||
|
||||
enum {
|
||||
MV_PORT_STATUS_SPEED_10 = 0x00,
|
||||
MV_PORT_STATUS_SPEED_100 = 0x01,
|
||||
MV_PORT_STATUS_SPEED_1000 = 0x02,
|
||||
};
|
||||
#define MV_PORT_STATUS_SPEED_SHIFT 8
|
||||
#define MV_PORT_STATUS_SPEED_MASK (3 << 8)
|
||||
|
||||
enum {
|
||||
MV_PORTCTRL_DISABLED = (0 << 0),
|
||||
MV_PORTCTRL_BLOCKING = (1 << 0),
|
||||
MV_PORTCTRL_LEARNING = (2 << 0),
|
||||
MV_PORTCTRL_FORWARDING = (3 << 0),
|
||||
MV_PORTCTRL_VLANTUN = (1 << 7),
|
||||
MV_PORTCTRL_EGRESS = (1 << 12),
|
||||
};
|
||||
|
||||
#define MV_PHYCTL_FC_MASK (3 << 6)
|
||||
|
||||
enum {
|
||||
MV_PHYCTL_FC_ENABLE = (3 << 6),
|
||||
MV_PHYCTL_FC_DISABLE = (1 << 6),
|
||||
};
|
||||
|
||||
enum {
|
||||
MV_8021Q_EGRESS_UNMODIFIED = 0x00,
|
||||
MV_8021Q_EGRESS_UNTAGGED = 0x01,
|
||||
MV_8021Q_EGRESS_TAGGED = 0x02,
|
||||
MV_8021Q_EGRESS_ADDTAG = 0x03,
|
||||
};
|
||||
|
||||
#define MV_8021Q_MODE_SHIFT 10
|
||||
#define MV_8021Q_MODE_MASK (0x3 << MV_8021Q_MODE_SHIFT)
|
||||
|
||||
enum {
|
||||
MV_8021Q_MODE_DISABLE = 0x00,
|
||||
MV_8021Q_MODE_FALLBACK = 0x01,
|
||||
MV_8021Q_MODE_CHECK = 0x02,
|
||||
MV_8021Q_MODE_SECURE = 0x03,
|
||||
};
|
||||
|
||||
enum {
|
||||
MV_8021Q_VLAN_ONLY = (1 << 15),
|
||||
};
|
||||
|
||||
#define MV_PORTASSOC_MONITOR (1 << 15)
|
||||
|
||||
enum {
|
||||
MV_SWITCH_ATU_FID0 = 0x01,
|
||||
MV_SWITCH_ATU_FID1 = 0x02,
|
||||
MV_SWITCH_ATU_SID = 0x03,
|
||||
MV_SWITCH_CTRL = 0x04,
|
||||
MV_SWITCH_ATU_CTRL = 0x0a,
|
||||
MV_SWITCH_ATU_OP = 0x0b,
|
||||
MV_SWITCH_ATU_DATA = 0x0c,
|
||||
MV_SWITCH_ATU_MAC0 = 0x0d,
|
||||
MV_SWITCH_ATU_MAC1 = 0x0e,
|
||||
MV_SWITCH_ATU_MAC2 = 0x0f,
|
||||
MV_SWITCH_GLOBAL = 0x1b,
|
||||
MV_SWITCH_GLOBAL2 = 0x1c,
|
||||
};
|
||||
#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type
|
||||
|
||||
enum {
|
||||
MV_SWITCHCTL_EEIE = (1 << 0),
|
||||
MV_SWITCHCTL_PHYIE = (1 << 1),
|
||||
MV_SWITCHCTL_ATUDONE = (1 << 2),
|
||||
MV_SWITCHCTL_ATUIE = (1 << 3),
|
||||
MV_SWITCHCTL_CTRMODE = (1 << 8),
|
||||
MV_SWITCHCTL_RELOAD = (1 << 9),
|
||||
MV_SWITCHCTL_MSIZE = (1 << 10),
|
||||
MV_SWITCHCTL_DROP = (1 << 13),
|
||||
};
|
||||
|
||||
enum {
|
||||
#define MV_ATUCTL_AGETIME_MIN 16
|
||||
#define MV_ATUCTL_AGETIME_MAX 4080
|
||||
#define MV_ATUCTL_AGETIME(_n) ((((_n) / 16) & 0xff) << 4)
|
||||
MV_ATUCTL_ATU_256 = (0 << 12),
|
||||
MV_ATUCTL_ATU_512 = (1 << 12),
|
||||
MV_ATUCTL_ATU_1K = (2 << 12),
|
||||
MV_ATUCTL_ATUMASK = (3 << 12),
|
||||
MV_ATUCTL_NO_LEARN = (1 << 14),
|
||||
MV_ATUCTL_RESET = (1 << 15),
|
||||
};
|
||||
|
||||
enum {
|
||||
#define MV_ATUOP_DBNUM(_n) ((_n) & 0x0f)
|
||||
MV_ATUOP_NOOP = (0 << 12),
|
||||
MV_ATUOP_FLUSH_ALL = (1 << 12),
|
||||
MV_ATUOP_FLUSH_U = (2 << 12),
|
||||
MV_ATUOP_LOAD_DB = (3 << 12),
|
||||
MV_ATUOP_GET_NEXT = (4 << 12),
|
||||
MV_ATUOP_FLUSH_DB = (5 << 12),
|
||||
MV_ATUOP_FLUSH_DB_UU = (6 << 12),
|
||||
MV_ATUOP_INPROGRESS = (1 << 15),
|
||||
};
|
||||
|
||||
enum {
|
||||
MV_GLOBAL_STATUS = 0x00,
|
||||
MV_GLOBAL_ATU_FID = 0x01,
|
||||
MV_GLOBAL_VTU_FID = 0x02,
|
||||
MV_GLOBAL_VTU_SID = 0x03,
|
||||
MV_GLOBAL_CONTROL = 0x04,
|
||||
MV_GLOBAL_VTU_OP = 0x05,
|
||||
MV_GLOBAL_VTU_VID = 0x06,
|
||||
MV_GLOBAL_VTU_DATA1 = 0x07,
|
||||
MV_GLOBAL_VTU_DATA2 = 0x08,
|
||||
MV_GLOBAL_VTU_DATA3 = 0x09,
|
||||
MV_GLOBAL_CONTROL2 = 0x1c,
|
||||
};
|
||||
#define MV_GLOBALREG(_type) MV_SWITCH_GLOBAL, MV_GLOBAL_##_type
|
||||
|
||||
enum {
|
||||
MV_GLOBAL2_SMI_OP = 0x18,
|
||||
MV_GLOBAL2_SMI_DATA = 0x19,
|
||||
MV_GLOBAL2_SDET_POLARITY = 0x1d,
|
||||
};
|
||||
#define MV_GLOBAL2REG(_type) MV_SWITCH_GLOBAL2, MV_GLOBAL2_##_type
|
||||
|
||||
enum {
|
||||
MV_VTU_VID_VALID = (1 << 12),
|
||||
};
|
||||
|
||||
enum {
|
||||
MV_VTUOP_PURGE = (1 << 12),
|
||||
MV_VTUOP_LOAD = (3 << 12),
|
||||
MV_VTUOP_INPROGRESS = (1 << 15),
|
||||
MV_VTUOP_STULOAD = (5 << 12),
|
||||
MV_VTUOP_VTU_GET_NEXT = (4 << 12),
|
||||
MV_VTUOP_STU_GET_NEXT = (6 << 12),
|
||||
MV_VTUOP_GET_VIOLATION = (7 << 12),
|
||||
};
|
||||
|
||||
enum {
|
||||
MV_CONTROL_RESET = (1 << 15),
|
||||
MV_CONTROL_PPU_ENABLE = (1 << 14),
|
||||
};
|
||||
|
||||
enum {
|
||||
MV_VTUCTL_EGRESS_UNMODIFIED = (0 << 0),
|
||||
MV_VTUCTL_EGRESS_UNTAGGED = (1 << 0),
|
||||
MV_VTUCTL_EGRESS_TAGGED = (2 << 0),
|
||||
MV_VTUCTL_DISCARD = (3 << 0),
|
||||
};
|
||||
|
||||
enum {
|
||||
MV_STUCTL_STATE_DISABLED = (0 << 0),
|
||||
MV_STUCTL_STATE_BLOCKING = (1 << 0),
|
||||
MV_STUCTL_STATE_LEARNING = (2 << 0),
|
||||
MV_STUCTL_STATE_FORWARDING = (3 << 0),
|
||||
};
|
||||
|
||||
enum {
|
||||
MV_INDIRECT_REG_CMD = 0,
|
||||
MV_INDIRECT_REG_DATA = 1,
|
||||
};
|
||||
|
||||
enum {
|
||||
MV_INDIRECT_INPROGRESS = 0x8000,
|
||||
MV_INDIRECT_WRITE = 0x9400,
|
||||
MV_INDIRECT_READ = 0x9800,
|
||||
};
|
||||
#define MV_INDIRECT_ADDR_S 5
|
||||
|
||||
#define MV_IDENT_MASK 0xfff0
|
||||
|
||||
#define MV_IDENT_VALUE_6171 0x1710
|
||||
#define MV_IDENT_STR_6171 "MV88E6171"
|
||||
|
||||
#define MV_IDENT_VALUE_6172 0x1720
|
||||
#define MV_IDENT_STR_6172 "MV88E6172"
|
||||
|
||||
#define MV_IDENT_VALUE_6176 0x1760
|
||||
#define MV_IDENT_STR_6176 "MV88E6176"
|
||||
|
||||
#define MV_IDENT_VALUE_6352 0x3520
|
||||
#define MV_IDENT_STR_6352 "MV88E6352"
|
||||
|
||||
#define MV_PVID_MASK 0x0fff
|
||||
|
||||
#define MV_FDB_HI_MASK 0x00ff
|
||||
#define MV_FDB_LO_MASK 0xf000
|
||||
#define MV_FDB_HI_SHIFT 4
|
||||
#define MV_FDB_LO_SHIFT 12
|
||||
|
||||
/* Marvell Specific PHY register */
|
||||
#define MII_MV_SPEC_CTRL 16
|
||||
enum {
|
||||
MV_SPEC_MDI_CROSS_AUTO = (0x6 << 4),
|
||||
MV_SPEC_ENERGY_DETECT = (0x3 << 8),
|
||||
MV_SPEC_DOWNSHIFT_COUNTER = (0x3 << 12),
|
||||
};
|
||||
|
||||
#define MII_MV_PAGE 22
|
||||
|
||||
#define MV_REG_FIBER_SERDES 0xf
|
||||
#define MV_PAGE_FIBER_SERDES 0x1
|
||||
|
||||
struct mvsw61xx_state {
|
||||
struct switch_dev dev;
|
||||
struct mii_bus *bus;
|
||||
int base_addr;
|
||||
u16 model;
|
||||
|
||||
bool registered;
|
||||
bool is_indirect;
|
||||
|
||||
int cpu_port0;
|
||||
int cpu_port1;
|
||||
|
||||
int vlan_enabled;
|
||||
struct port_state {
|
||||
u16 fdb;
|
||||
u16 pvid;
|
||||
u16 mask;
|
||||
u8 qmode;
|
||||
} ports[MV_PORTS];
|
||||
|
||||
struct vlan_state {
|
||||
bool port_based;
|
||||
|
||||
u16 mask;
|
||||
u16 vid;
|
||||
u32 port_mode;
|
||||
u32 port_sstate;
|
||||
} vlans[MV_VLANS];
|
||||
|
||||
char buf[128];
|
||||
};
|
||||
|
||||
#define get_state(_dev) container_of((_dev), struct mvsw61xx_state, dev)
|
||||
|
||||
#endif
|
||||
444
target/linux/generic/files/drivers/net/phy/mvswitch.c
Normal file
444
target/linux/generic/files/drivers/net/phy/mvswitch.c
Normal file
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* Marvell 88E6060 switch driver
|
||||
* 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 v2 as published by the
|
||||
* Free Software Foundation
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mii.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/if_vlan.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "mvswitch.h"
|
||||
|
||||
/* Undefine this to use trailer mode instead.
|
||||
* I don't know if header mode works with all chips */
|
||||
#define HEADER_MODE 1
|
||||
|
||||
MODULE_DESCRIPTION("Marvell 88E6060 Switch driver");
|
||||
MODULE_AUTHOR("Felix Fietkau");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define MVSWITCH_MAGIC 0x88E6060
|
||||
|
||||
struct mvswitch_priv {
|
||||
netdev_features_t orig_features;
|
||||
u8 vlans[16];
|
||||
};
|
||||
|
||||
#define to_mvsw(_phy) ((struct mvswitch_priv *) (_phy)->priv)
|
||||
|
||||
static inline u16
|
||||
r16(struct phy_device *phydev, int addr, int reg)
|
||||
{
|
||||
struct mii_bus *bus = phydev->mdio.bus;
|
||||
|
||||
return bus->read(bus, addr, reg);
|
||||
}
|
||||
|
||||
static inline void
|
||||
w16(struct phy_device *phydev, int addr, int reg, u16 val)
|
||||
{
|
||||
struct mii_bus *bus = phydev->mdio.bus;
|
||||
|
||||
bus->write(bus, addr, reg, val);
|
||||
}
|
||||
|
||||
|
||||
static struct sk_buff *
|
||||
mvswitch_mangle_tx(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct mvswitch_priv *priv;
|
||||
char *buf = NULL;
|
||||
u16 vid;
|
||||
|
||||
priv = dev->phy_ptr;
|
||||
if (unlikely(!priv))
|
||||
goto error;
|
||||
|
||||
if (unlikely(skb->len < 16))
|
||||
goto error;
|
||||
|
||||
#ifdef HEADER_MODE
|
||||
if (__vlan_hwaccel_get_tag(skb, &vid))
|
||||
goto error;
|
||||
|
||||
if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) {
|
||||
if (pskb_expand_head(skb, MV_HEADER_SIZE, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC))
|
||||
goto error_expand;
|
||||
if (skb->len < 62)
|
||||
skb->len = 62;
|
||||
}
|
||||
buf = skb_push(skb, MV_HEADER_SIZE);
|
||||
#else
|
||||
if (__vlan_get_tag(skb, &vid))
|
||||
goto error;
|
||||
|
||||
if (unlikely((vid > 15 || !priv->vlans[vid])))
|
||||
goto error;
|
||||
|
||||
if (skb->len <= 64) {
|
||||
if (pskb_expand_head(skb, 0, 64 + MV_TRAILER_SIZE - skb->len, GFP_ATOMIC))
|
||||
goto error_expand;
|
||||
|
||||
buf = skb->data + 64;
|
||||
skb->len = 64 + MV_TRAILER_SIZE;
|
||||
} else {
|
||||
if (skb_cloned(skb) || unlikely(skb_tailroom(skb) < 4)) {
|
||||
if (pskb_expand_head(skb, 0, 4, GFP_ATOMIC))
|
||||
goto error_expand;
|
||||
}
|
||||
buf = skb_put(skb, 4);
|
||||
}
|
||||
|
||||
/* move the ethernet header 4 bytes forward, overwriting the vlan tag */
|
||||
memmove(skb->data + 4, skb->data, 12);
|
||||
skb->data += 4;
|
||||
skb->len -= 4;
|
||||
skb->mac_header += 4;
|
||||
#endif
|
||||
|
||||
if (!buf)
|
||||
goto error;
|
||||
|
||||
|
||||
#ifdef HEADER_MODE
|
||||
/* prepend the tag */
|
||||
*((__be16 *) buf) = cpu_to_be16(
|
||||
((vid << MV_HEADER_VLAN_S) & MV_HEADER_VLAN_M) |
|
||||
((priv->vlans[vid] << MV_HEADER_PORTS_S) & MV_HEADER_PORTS_M)
|
||||
);
|
||||
#else
|
||||
/* append the tag */
|
||||
*((__be32 *) buf) = cpu_to_be32((
|
||||
(MV_TRAILER_OVERRIDE << MV_TRAILER_FLAGS_S) |
|
||||
((priv->vlans[vid] & MV_TRAILER_PORTS_M) << MV_TRAILER_PORTS_S)
|
||||
));
|
||||
#endif
|
||||
|
||||
return skb;
|
||||
|
||||
error_expand:
|
||||
if (net_ratelimit())
|
||||
printk("%s: failed to expand/update skb for the switch\n", dev->name);
|
||||
|
||||
error:
|
||||
/* any errors? drop the packet! */
|
||||
dev_kfree_skb_any(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
mvswitch_mangle_rx(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct mvswitch_priv *priv;
|
||||
unsigned char *buf;
|
||||
int vlan = -1;
|
||||
int i;
|
||||
|
||||
priv = dev->phy_ptr;
|
||||
if (WARN_ON_ONCE(!priv))
|
||||
return;
|
||||
|
||||
#ifdef HEADER_MODE
|
||||
buf = skb->data;
|
||||
skb_pull(skb, MV_HEADER_SIZE);
|
||||
#else
|
||||
buf = skb->data + skb->len - MV_TRAILER_SIZE;
|
||||
if (buf[0] != 0x80)
|
||||
return;
|
||||
#endif
|
||||
|
||||
/* look for the vlan matching the incoming port */
|
||||
for (i = 0; i < ARRAY_SIZE(priv->vlans); i++) {
|
||||
if ((1 << buf[1]) & priv->vlans[i])
|
||||
vlan = i;
|
||||
}
|
||||
|
||||
if (vlan == -1)
|
||||
return;
|
||||
|
||||
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
mvswitch_wait_mask(struct phy_device *pdev, int addr, int reg, u16 mask, u16 val)
|
||||
{
|
||||
int i = 100;
|
||||
u16 r;
|
||||
|
||||
do {
|
||||
r = r16(pdev, addr, reg) & mask;
|
||||
if (r == val)
|
||||
return 0;
|
||||
} while(--i > 0);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_config_init(struct phy_device *pdev)
|
||||
{
|
||||
struct mvswitch_priv *priv = to_mvsw(pdev);
|
||||
struct net_device *dev = pdev->attached_dev;
|
||||
u8 vlmap = 0;
|
||||
int i;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
printk("%s: Marvell 88E6060 PHY driver attached.\n", dev->name);
|
||||
pdev->supported = ADVERTISED_100baseT_Full;
|
||||
pdev->advertising = ADVERTISED_100baseT_Full;
|
||||
dev->phy_ptr = priv;
|
||||
pdev->irq = PHY_POLL;
|
||||
#ifdef HEADER_MODE
|
||||
dev->flags |= IFF_PROMISC;
|
||||
#endif
|
||||
|
||||
/* initialize default vlans */
|
||||
for (i = 0; i < MV_PORTS; i++)
|
||||
priv->vlans[(i == MV_WANPORT ? 2 : 1)] |= (1 << i);
|
||||
|
||||
/* before entering reset, disable all ports */
|
||||
for (i = 0; i < MV_PORTS; i++)
|
||||
w16(pdev, MV_PORTREG(CONTROL, i), 0x00);
|
||||
|
||||
msleep(2); /* wait for the status change to settle in */
|
||||
|
||||
/* put the ATU in reset */
|
||||
w16(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET);
|
||||
|
||||
i = mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET, 0);
|
||||
if (i < 0) {
|
||||
printk("%s: Timeout waiting for the switch to reset.\n", dev->name);
|
||||
return i;
|
||||
}
|
||||
|
||||
/* set the ATU flags */
|
||||
w16(pdev, MV_SWITCHREG(ATU_CTRL),
|
||||
MV_ATUCTL_NO_LEARN |
|
||||
MV_ATUCTL_ATU_1K |
|
||||
MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN) /* minimum without disabling ageing */
|
||||
);
|
||||
|
||||
/* initialize the cpu port */
|
||||
w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT),
|
||||
#ifdef HEADER_MODE
|
||||
MV_PORTCTRL_HEADER |
|
||||
#else
|
||||
MV_PORTCTRL_RXTR |
|
||||
MV_PORTCTRL_TXTR |
|
||||
#endif
|
||||
MV_PORTCTRL_ENABLED
|
||||
);
|
||||
/* wait for the phy change to settle in */
|
||||
msleep(2);
|
||||
for (i = 0; i < MV_PORTS; i++) {
|
||||
u8 pvid = 0;
|
||||
int j;
|
||||
|
||||
vlmap = 0;
|
||||
|
||||
/* look for the matching vlan */
|
||||
for (j = 0; j < ARRAY_SIZE(priv->vlans); j++) {
|
||||
if (priv->vlans[j] & (1 << i)) {
|
||||
vlmap = priv->vlans[j];
|
||||
pvid = j;
|
||||
}
|
||||
}
|
||||
/* leave port unconfigured if it's not part of a vlan */
|
||||
if (!vlmap)
|
||||
continue;
|
||||
|
||||
/* add the cpu port to the allowed destinations list */
|
||||
vlmap |= (1 << MV_CPUPORT);
|
||||
|
||||
/* take port out of its own vlan destination map */
|
||||
vlmap &= ~(1 << i);
|
||||
|
||||
/* apply vlan settings */
|
||||
w16(pdev, MV_PORTREG(VLANMAP, i),
|
||||
MV_PORTVLAN_PORTS(vlmap) |
|
||||
MV_PORTVLAN_ID(i)
|
||||
);
|
||||
|
||||
/* re-enable port */
|
||||
w16(pdev, MV_PORTREG(CONTROL, i),
|
||||
MV_PORTCTRL_ENABLED
|
||||
);
|
||||
}
|
||||
|
||||
w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT),
|
||||
MV_PORTVLAN_ID(MV_CPUPORT)
|
||||
);
|
||||
|
||||
/* set the port association vector */
|
||||
for (i = 0; i <= MV_PORTS; i++) {
|
||||
w16(pdev, MV_PORTREG(ASSOC, i),
|
||||
MV_PORTASSOC_PORTS(1 << i)
|
||||
);
|
||||
}
|
||||
|
||||
/* init switch control */
|
||||
w16(pdev, MV_SWITCHREG(CTRL),
|
||||
MV_SWITCHCTL_MSIZE |
|
||||
MV_SWITCHCTL_DROP
|
||||
);
|
||||
|
||||
dev->eth_mangle_rx = mvswitch_mangle_rx;
|
||||
dev->eth_mangle_tx = mvswitch_mangle_tx;
|
||||
priv->orig_features = dev->features;
|
||||
|
||||
#ifdef HEADER_MODE
|
||||
dev->priv_flags |= IFF_NO_IP_ALIGN;
|
||||
dev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX;
|
||||
#else
|
||||
dev->features |= NETIF_F_HW_VLAN_CTAG_RX;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_read_status(struct phy_device *pdev)
|
||||
{
|
||||
pdev->speed = SPEED_100;
|
||||
pdev->duplex = DUPLEX_FULL;
|
||||
pdev->link = 1;
|
||||
|
||||
/* XXX ugly workaround: we can't force the switch
|
||||
* to gracefully handle hosts moving from one port to another,
|
||||
* so we have to regularly clear the ATU database */
|
||||
|
||||
/* wait for the ATU to become available */
|
||||
mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
|
||||
|
||||
/* flush the ATU */
|
||||
w16(pdev, MV_SWITCHREG(ATU_OP),
|
||||
MV_ATUOP_INPROGRESS |
|
||||
MV_ATUOP_FLUSH_ALL
|
||||
);
|
||||
|
||||
/* wait for operation to complete */
|
||||
mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_aneg_done(struct phy_device *phydev)
|
||||
{
|
||||
return 1; /* Return any positive value */
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mvswitch_detach(struct phy_device *pdev)
|
||||
{
|
||||
struct mvswitch_priv *priv = to_mvsw(pdev);
|
||||
struct net_device *dev = pdev->attached_dev;
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
dev->phy_ptr = NULL;
|
||||
dev->eth_mangle_rx = NULL;
|
||||
dev->eth_mangle_tx = NULL;
|
||||
dev->features = priv->orig_features;
|
||||
dev->priv_flags &= ~IFF_NO_IP_ALIGN;
|
||||
}
|
||||
|
||||
static void
|
||||
mvswitch_remove(struct phy_device *pdev)
|
||||
{
|
||||
struct mvswitch_priv *priv = to_mvsw(pdev);
|
||||
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_probe(struct phy_device *pdev)
|
||||
{
|
||||
struct mvswitch_priv *priv;
|
||||
|
||||
priv = kzalloc(sizeof(struct mvswitch_priv), GFP_KERNEL);
|
||||
if (priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pdev->priv = priv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mvswitch_fixup(struct phy_device *dev)
|
||||
{
|
||||
struct mii_bus *bus = dev->mdio.bus;
|
||||
u16 reg;
|
||||
|
||||
if (dev->mdio.addr != 0x10)
|
||||
return 0;
|
||||
|
||||
reg = bus->read(bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
|
||||
if (reg != MV_IDENT_VALUE)
|
||||
return 0;
|
||||
|
||||
dev->phy_id = MVSWITCH_MAGIC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct phy_driver mvswitch_driver = {
|
||||
.name = "Marvell 88E6060",
|
||||
.phy_id = MVSWITCH_MAGIC,
|
||||
.phy_id_mask = 0xffffffff,
|
||||
.features = PHY_BASIC_FEATURES,
|
||||
.probe = &mvswitch_probe,
|
||||
.remove = &mvswitch_remove,
|
||||
.detach = &mvswitch_detach,
|
||||
.config_init = &mvswitch_config_init,
|
||||
.config_aneg = &mvswitch_config_aneg,
|
||||
.aneg_done = &mvswitch_aneg_done,
|
||||
.read_status = &mvswitch_read_status,
|
||||
};
|
||||
|
||||
static int __init
|
||||
mvswitch_init(void)
|
||||
{
|
||||
phy_register_fixup_for_id(PHY_ANY_ID, mvswitch_fixup);
|
||||
return phy_driver_register(&mvswitch_driver, THIS_MODULE);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
mvswitch_exit(void)
|
||||
{
|
||||
phy_driver_unregister(&mvswitch_driver);
|
||||
}
|
||||
|
||||
module_init(mvswitch_init);
|
||||
module_exit(mvswitch_exit);
|
||||
145
target/linux/generic/files/drivers/net/phy/mvswitch.h
Normal file
145
target/linux/generic/files/drivers/net/phy/mvswitch.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Marvell 88E6060 switch driver
|
||||
* 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 v2 as published by the
|
||||
* Free Software Foundation
|
||||
*/
|
||||
#ifndef __MVSWITCH_H
|
||||
#define __MVSWITCH_H
|
||||
|
||||
#define MV_HEADER_SIZE 2
|
||||
#define MV_HEADER_PORTS_M 0x001f
|
||||
#define MV_HEADER_PORTS_S 0
|
||||
#define MV_HEADER_VLAN_M 0xf000
|
||||
#define MV_HEADER_VLAN_S 12
|
||||
|
||||
#define MV_TRAILER_SIZE 4
|
||||
#define MV_TRAILER_PORTS_M 0x1f
|
||||
#define MV_TRAILER_PORTS_S 16
|
||||
#define MV_TRAILER_FLAGS_S 24
|
||||
#define MV_TRAILER_OVERRIDE 0x80
|
||||
|
||||
|
||||
#define MV_PORTS 5
|
||||
#define MV_WANPORT 4
|
||||
#define MV_CPUPORT 5
|
||||
|
||||
#define MV_BASE 0x10
|
||||
|
||||
#define MV_PHYPORT_BASE (MV_BASE + 0x0)
|
||||
#define MV_PHYPORT(_n) (MV_PHYPORT_BASE + (_n))
|
||||
#define MV_SWITCHPORT_BASE (MV_BASE + 0x8)
|
||||
#define MV_SWITCHPORT(_n) (MV_SWITCHPORT_BASE + (_n))
|
||||
#define MV_SWITCHREGS (MV_BASE + 0xf)
|
||||
|
||||
enum {
|
||||
MV_PHY_CONTROL = 0x00,
|
||||
MV_PHY_STATUS = 0x01,
|
||||
MV_PHY_IDENT0 = 0x02,
|
||||
MV_PHY_IDENT1 = 0x03,
|
||||
MV_PHY_ANEG = 0x04,
|
||||
MV_PHY_LINK_ABILITY = 0x05,
|
||||
MV_PHY_ANEG_EXPAND = 0x06,
|
||||
MV_PHY_XMIT_NEXTP = 0x07,
|
||||
MV_PHY_LINK_NEXTP = 0x08,
|
||||
MV_PHY_CONTROL1 = 0x10,
|
||||
MV_PHY_STATUS1 = 0x11,
|
||||
MV_PHY_INTR_EN = 0x12,
|
||||
MV_PHY_INTR_STATUS = 0x13,
|
||||
MV_PHY_INTR_PORT = 0x14,
|
||||
MV_PHY_RECV_COUNTER = 0x16,
|
||||
MV_PHY_LED_PARALLEL = 0x16,
|
||||
MV_PHY_LED_STREAM = 0x17,
|
||||
MV_PHY_LED_CTRL = 0x18,
|
||||
MV_PHY_LED_OVERRIDE = 0x19,
|
||||
MV_PHY_VCT_CTRL = 0x1a,
|
||||
MV_PHY_VCT_STATUS = 0x1b,
|
||||
MV_PHY_CONTROL2 = 0x1e
|
||||
};
|
||||
#define MV_PHYREG(_type, _port) MV_PHYPORT(_port), MV_PHY_##_type
|
||||
|
||||
enum {
|
||||
MV_PORT_STATUS = 0x00,
|
||||
MV_PORT_IDENT = 0x03,
|
||||
MV_PORT_CONTROL = 0x04,
|
||||
MV_PORT_VLANMAP = 0x06,
|
||||
MV_PORT_ASSOC = 0x0b,
|
||||
MV_PORT_RXCOUNT = 0x10,
|
||||
MV_PORT_TXCOUNT = 0x11,
|
||||
};
|
||||
#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type
|
||||
|
||||
enum {
|
||||
MV_PORTCTRL_BLOCK = (1 << 0),
|
||||
MV_PORTCTRL_LEARN = (2 << 0),
|
||||
MV_PORTCTRL_ENABLED = (3 << 0),
|
||||
MV_PORTCTRL_VLANTUN = (1 << 7), /* Enforce VLANs on packets */
|
||||
MV_PORTCTRL_RXTR = (1 << 8), /* Enable Marvell packet trailer for ingress */
|
||||
MV_PORTCTRL_HEADER = (1 << 11), /* Enable Marvell packet header mode for port */
|
||||
MV_PORTCTRL_TXTR = (1 << 14), /* Enable Marvell packet trailer for egress */
|
||||
MV_PORTCTRL_FORCEFL = (1 << 15), /* force flow control */
|
||||
};
|
||||
|
||||
#define MV_PORTVLAN_ID(_n) (((_n) & 0xf) << 12)
|
||||
#define MV_PORTVLAN_PORTS(_n) ((_n) & 0x3f)
|
||||
|
||||
#define MV_PORTASSOC_PORTS(_n) ((_n) & 0x1f)
|
||||
#define MV_PORTASSOC_MONITOR (1 << 15)
|
||||
|
||||
enum {
|
||||
MV_SWITCH_MAC0 = 0x01,
|
||||
MV_SWITCH_MAC1 = 0x02,
|
||||
MV_SWITCH_MAC2 = 0x03,
|
||||
MV_SWITCH_CTRL = 0x04,
|
||||
MV_SWITCH_ATU_CTRL = 0x0a,
|
||||
MV_SWITCH_ATU_OP = 0x0b,
|
||||
MV_SWITCH_ATU_DATA = 0x0c,
|
||||
MV_SWITCH_ATU_MAC0 = 0x0d,
|
||||
MV_SWITCH_ATU_MAC1 = 0x0e,
|
||||
MV_SWITCH_ATU_MAC2 = 0x0f,
|
||||
};
|
||||
#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type
|
||||
|
||||
enum {
|
||||
MV_SWITCHCTL_EEIE = (1 << 0), /* EEPROM interrupt enable */
|
||||
MV_SWITCHCTL_PHYIE = (1 << 1), /* PHY interrupt enable */
|
||||
MV_SWITCHCTL_ATUDONE= (1 << 2), /* ATU done interrupt enable */
|
||||
MV_SWITCHCTL_ATUIE = (1 << 3), /* ATU interrupt enable */
|
||||
MV_SWITCHCTL_CTRMODE= (1 << 8), /* statistics for rx and tx errors */
|
||||
MV_SWITCHCTL_RELOAD = (1 << 9), /* reload registers from eeprom */
|
||||
MV_SWITCHCTL_MSIZE = (1 << 10), /* increase maximum frame size */
|
||||
MV_SWITCHCTL_DROP = (1 << 13), /* discard frames with excessive collisions */
|
||||
};
|
||||
|
||||
enum {
|
||||
#define MV_ATUCTL_AGETIME_MIN 16
|
||||
#define MV_ATUCTL_AGETIME_MAX 4080
|
||||
#define MV_ATUCTL_AGETIME(_n) ((((_n) / 16) & 0xff) << 4)
|
||||
MV_ATUCTL_ATU_256 = (0 << 12),
|
||||
MV_ATUCTL_ATU_512 = (1 << 12),
|
||||
MV_ATUCTL_ATU_1K = (2 << 12),
|
||||
MV_ATUCTL_ATUMASK = (3 << 12),
|
||||
MV_ATUCTL_NO_LEARN = (1 << 14),
|
||||
MV_ATUCTL_RESET = (1 << 15),
|
||||
};
|
||||
|
||||
enum {
|
||||
#define MV_ATUOP_DBNUM(_n) ((_n) & 0x0f)
|
||||
|
||||
MV_ATUOP_NOOP = (0 << 12),
|
||||
MV_ATUOP_FLUSH_ALL = (1 << 12),
|
||||
MV_ATUOP_FLUSH_U = (2 << 12),
|
||||
MV_ATUOP_LOAD_DB = (3 << 12),
|
||||
MV_ATUOP_GET_NEXT = (4 << 12),
|
||||
MV_ATUOP_FLUSH_DB = (5 << 12),
|
||||
MV_ATUOP_FLUSH_DB_UU= (6 << 12),
|
||||
|
||||
MV_ATUOP_INPROGRESS = (1 << 15),
|
||||
};
|
||||
|
||||
#define MV_IDENT_MASK 0xfff0
|
||||
#define MV_IDENT_VALUE 0x0600
|
||||
|
||||
#endif
|
||||
441
target/linux/generic/files/drivers/net/phy/psb6970.c
Normal file
441
target/linux/generic/files/drivers/net/phy/psb6970.c
Normal file
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#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 */
|
||||
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->vlan, 0, sizeof(struct psb6970_priv) -
|
||||
offsetof(struct psb6970_priv, vlan));
|
||||
|
||||
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 net_device *dev = pdev->attached_dev;
|
||||
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;
|
||||
}
|
||||
|
||||
pdev->supported = pdev->advertising = SUPPORTED_100baseT_Full;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
dev->phy_ptr = priv;
|
||||
|
||||
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");
|
||||
1066
target/linux/generic/files/drivers/net/phy/rtl8306.c
Normal file
1066
target/linux/generic/files/drivers/net/phy/rtl8306.c
Normal file
File diff suppressed because it is too large
Load Diff
1494
target/linux/generic/files/drivers/net/phy/rtl8366_smi.c
Normal file
1494
target/linux/generic/files/drivers/net/phy/rtl8366_smi.c
Normal file
File diff suppressed because it is too large
Load Diff
155
target/linux/generic/files/drivers/net/phy/rtl8366_smi.h
Normal file
155
target/linux/generic/files/drivers/net/phy/rtl8366_smi.h
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
struct rtl8366_smi_ops;
|
||||
struct rtl8366_vlan_ops;
|
||||
struct mii_bus;
|
||||
struct dentry;
|
||||
struct inode;
|
||||
struct file;
|
||||
|
||||
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)(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];
|
||||
#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
|
||||
struct dentry *debugfs_root;
|
||||
u16 dbg_reg;
|
||||
u8 dbg_vlan_4k_page;
|
||||
#endif
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
int rtl8366_reset_vlan(struct rtl8366_smi *smi);
|
||||
int rtl8366_enable_vlan(struct rtl8366_smi *smi, int enable);
|
||||
int rtl8366_enable_all_ports(struct rtl8366_smi *smi, int enable);
|
||||
|
||||
#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 */
|
||||
1532
target/linux/generic/files/drivers/net/phy/rtl8366rb.c
Normal file
1532
target/linux/generic/files/drivers/net/phy/rtl8366rb.c
Normal file
File diff suppressed because it is too large
Load Diff
1320
target/linux/generic/files/drivers/net/phy/rtl8366s.c
Normal file
1320
target/linux/generic/files/drivers/net/phy/rtl8366s.c
Normal file
File diff suppressed because it is too large
Load Diff
1846
target/linux/generic/files/drivers/net/phy/rtl8367.c
Normal file
1846
target/linux/generic/files/drivers/net/phy/rtl8367.c
Normal file
File diff suppressed because it is too large
Load Diff
1613
target/linux/generic/files/drivers/net/phy/rtl8367b.c
Normal file
1613
target/linux/generic/files/drivers/net/phy/rtl8367b.c
Normal file
File diff suppressed because it is too large
Load Diff
1256
target/linux/generic/files/drivers/net/phy/swconfig.c
Normal file
1256
target/linux/generic/files/drivers/net/phy/swconfig.c
Normal file
File diff suppressed because it is too large
Load Diff
556
target/linux/generic/files/drivers/net/phy/swconfig_leds.c
Normal file
556
target/linux/generic/files/drivers/net/phy/swconfig_leds.c
Normal file
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
* 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;
|
||||
read_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);
|
||||
}
|
||||
}
|
||||
read_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 void
|
||||
swconfig_trig_activate(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct switch_led_trigger *sw_trig;
|
||||
struct swconfig_trig_data *trig_data;
|
||||
int err;
|
||||
|
||||
if (led_cdev->trigger->activate != swconfig_trig_activate)
|
||||
return;
|
||||
|
||||
trig_data = kzalloc(sizeof(struct swconfig_trig_data), GFP_KERNEL);
|
||||
if (!trig_data)
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
read_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);
|
||||
}
|
||||
read_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 */
|
||||
133
target/linux/generic/files/include/linux/ar8216_platform.h
Normal file
133
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 */
|
||||
|
||||
30
target/linux/generic/files/include/linux/ath5k_platform.h
Normal file
30
target/linux/generic/files/include/linux/ath5k_platform.h
Normal file
@@ -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 */
|
||||
60
target/linux/generic/files/include/linux/ath9k_platform.h
Normal file
60
target/linux/generic/files/include/linux/ath9k_platform.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
bool ubnt_hsr;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_ATH9K_PLATFORM_H */
|
||||
121
target/linux/generic/files/include/linux/myloader.h
Normal file
121
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
|
||||
36
target/linux/generic/files/include/linux/platform_data/b53.h
Normal file
36
target/linux/generic/files/include/linux/platform_data/b53.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* B53 platform data
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __B53_H
|
||||
#define __B53_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
struct b53_platform_data {
|
||||
u32 chip_id;
|
||||
u16 enabled_ports;
|
||||
|
||||
/* allow to specify an ethX alias */
|
||||
const char *alias;
|
||||
|
||||
/* only used by MMAP'd driver */
|
||||
unsigned big_endian:1;
|
||||
void __iomem *regs;
|
||||
};
|
||||
|
||||
#endif
|
||||
106
target/linux/generic/files/include/linux/routerboot.h
Normal file
106
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 */
|
||||
23
target/linux/generic/files/include/linux/rt2x00_platform.h
Normal file
23
target/linux/generic/files/include/linux/rt2x00_platform.h
Normal file
@@ -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 */
|
||||
40
target/linux/generic/files/include/linux/rtl8366.h
Normal file
40
target/linux/generic/files/include/linux/rtl8366.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
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)(bool active);
|
||||
|
||||
unsigned num_initvals;
|
||||
struct rtl8366_initval *initvals;
|
||||
};
|
||||
|
||||
enum rtl8366_type rtl8366_smi_detect(struct rtl8366_platform_data *pdata);
|
||||
|
||||
#endif /* _RTL8366_H */
|
||||
60
target/linux/generic/files/include/linux/rtl8367.h
Normal file
60
target/linux/generic/files/include/linux/rtl8367.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
||||
|
||||
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
target/linux/generic/files/include/linux/switch.h
Normal file
179
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
target/linux/generic/files/include/uapi/linux/switch.h
Normal file
119
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 */
|
||||
Reference in New Issue
Block a user