WIP: bcm63xx: SFR NB6V support bcm63xx-nb6v
authorÁlvaro Fernández Rojas <noltari@gmail.com>
Tue, 19 May 2020 21:13:32 +0000 (23:13 +0200)
committerÁlvaro Fernández Rojas <noltari@gmail.com>
Mon, 8 Jun 2020 15:52:36 +0000 (17:52 +0200)
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
package/kernel/bcm63xx-cfe/Makefile
target/linux/bcm63xx/base-files/etc/board.d/02_network
target/linux/bcm63xx/base-files/lib/upgrade/platform.sh
target/linux/bcm63xx/config-5.4
target/linux/bcm63xx/dts/bcm63168-sfr-neufbox-6v-foxconn-r0.dts [new file with mode: 0644]
target/linux/bcm63xx/dts/bcm6361-sfr-neufbox-6-sercomm-r0.dts
target/linux/bcm63xx/files/drivers/hwmon/nb6-hwmon.c [new file with mode: 0644]
target/linux/bcm63xx/image/bcm63xx_nand.mk
target/linux/bcm63xx/patches-5.4/568-board-nb6v.patch [new file with mode: 0644]
target/linux/bcm63xx/patches-5.4/900-hwmon-nb6.patch [new file with mode: 0644]

index 33616cc0d52d732d9cbb40e3608f4935d8d6b647..b4697462f03c76b4cfcd6a89b535c73ce8d90ee8 100644 (file)
@@ -2,13 +2,13 @@ include $(TOPDIR)/rules.mk
 include $(INCLUDE_DIR)/kernel.mk
 
 PKG_NAME:=bcm63xx-cfe
-PKG_RELEASE:=2
+PKG_RELEASE:=1
 
 PKG_SOURCE_URL:=https://github.com/openwrt/bcm63xx-cfe.git
 PKG_SOURCE_PROTO:=git
-PKG_SOURCE_DATE:=2020-05-18
-PKG_SOURCE_VERSION:=efe3b81121a84c0b8ba6a7d0d47cd5eeeb23808d
-PKG_MIRROR_HASH:=299dcf6ef1ad034df26daee6446b574abcd7526a2fe90fb8115890e71bc0d58b
+PKG_SOURCE_DATE:=2020-05-20
+PKG_SOURCE_VERSION:=424b57c2433b62094d2941fc9be796a29fd1085f
+PKG_MIRROR_HASH:=e46555e93066dd0a199460a0d4b91c05e1904a2f22bfc5fa8deee6b07e80cfb8
 
 PKG_FLAGS:=nonshared
 
index f02eabe68fdacc4a803967ac5624917c15f66c26..5fcf4f37327cf02bff67f597978003d46ce9ebe5 100755 (executable)
@@ -150,6 +150,10 @@ sfr,neufbox-6-sercomm-r0)
        ucidef_add_switch "switch0" \
                "1:lan" "2:lan" "3:lan" "4:lan" "0:wan" "9t@eth0"
        ;;
+sfr,neufbox-6v-foxconn-r0)
+       ucidef_add_switch "switch0" \
+               "1:lan" "2:lan" "3:lan" "4:lan" "0:wan" "5t@eth0"
+       ;;
 sky,sr102)
        ucidef_set_interfaces_lan_wan "eth0.1" "eth0.2"
        ucidef_add_switch "switch0" \
index 525d3b90b5a8da5851a354cdc4cabd2317d6e1f0..a032289f4ae1f6533a5010aea5af149f09e49383 100644 (file)
@@ -11,7 +11,8 @@ platform_check_image() {
                netgear,dgnd3700-v2|\
                sercomm,ad1018|\
                sercomm,h500-s-lowi|\
-               sercomm,h500-s-vfes)
+               sercomm,h500-s-vfes|\
+               sfr,neufbox-6v-foxconn-r0)
                        # NAND sysupgrade
                        return 0
                        ;;
@@ -73,7 +74,8 @@ platform_do_upgrade() {
                netgear,dgnd3700-v2|\
                sercomm,ad1018|\
                sercomm,h500-s-lowi|\
-               sercomm,h500-s-vfes)
+               sercomm,h500-s-vfes|\
+               sfr,neufbox-6v-foxconn-r0)
                        REQUIRE_IMAGE_METADATA=1
                        cfe_jffs2_upgrade_tar "$1"
                        ;;
index c1587b84d63bd511d378241ea4bbd09e7990ae10..5976443d5a02b1870c9bf1d4890fb889fb407e63 100644 (file)
@@ -146,11 +146,16 @@ CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
 CONFIG_HAVE_RSEQ=y
 CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
 CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
+CONFIG_HWMON=y
 CONFIG_HW_RANDOM=y
 # CONFIG_HW_RANDOM_BCM2835 is not set
 CONFIG_HZ=250
 CONFIG_HZ_250=y
 CONFIG_HZ_PERIODIC=y
+CONFIG_I2C=y
+CONFIG_I2C_ALGOBIT=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_GPIO=y
 CONFIG_INITRAMFS_SOURCE=""
 CONFIG_IRQCHIP=y
 CONFIG_IRQ_DOMAIN=y
@@ -162,6 +167,7 @@ CONFIG_KEXEC_CORE=y
 CONFIG_LEDS_BCM6328=y
 CONFIG_LEDS_BCM6358=y
 CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_PWM=y
 CONFIG_LIBFDT=y
 CONFIG_LOCK_DEBUGGING_SUPPORT=y
 CONFIG_MDIO_BUS=y
@@ -230,11 +236,15 @@ CONFIG_PINCTRL_BCM6368=y
 CONFIG_PINCTRL_BCM63XX=y
 CONFIG_POSIX_MQUEUE=y
 CONFIG_POSIX_MQUEUE_SYSCTL=y
+CONFIG_PWM=y
+CONFIG_PWM_SYSFS=y
 CONFIG_REGMAP=y
 CONFIG_REGMAP_MMIO=y
 CONFIG_RELAY=y
 CONFIG_RTL8366_SMI=y
+CONFIG_RTL8367B_PHY=y
 CONFIG_RTL8367_PHY=y
+CONFIG_SENSORS_NB6_HWMON=y
 # CONFIG_SERIAL_8250 is not set
 CONFIG_SERIAL_BCM63XX=y
 CONFIG_SERIAL_BCM63XX_CONSOLE=y
diff --git a/target/linux/bcm63xx/dts/bcm63168-sfr-neufbox-6v-foxconn-r0.dts b/target/linux/bcm63xx/dts/bcm63168-sfr-neufbox-6v-foxconn-r0.dts
new file mode 100644 (file)
index 0000000..ad74497
--- /dev/null
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/dts-v1/;
+
+#include "bcm63268.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+       model = "SFR Neufbox 6V (Foxconn)";
+       compatible = "sfr,neufbox-6v-foxconn-r0", "brcm,bcm63168", "brcm,bcm63268";
+
+       aliases {
+               led-boot = &led_service1_green;
+               led-failsafe = &led_service1_red;
+               led-running = &led_service1_green;
+               led-upgrade = &led_service1_green;
+       };
+
+       chosen {
+               bootargs = "rootfstype=squashfs,ubifs noinitrd console=ttyS0,115200";
+               stdout-path = "serial0:115200n8";
+       };
+
+       i2c {
+               compatible = "i2c-gpio";
+               sda-gpios = <&pinctrl 1 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
+               scl-gpios = <&pinctrl 0 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               pwm: nb6-hwmon@60 {
+                       compatible = "sfr,nb6_hwmon";
+                       reg = <0x60>;
+                       #pwm-cells = <2>;
+               };
+       };
+
+       keys {
+               compatible = "gpio-keys-polled";
+               #address-cells = <1>;
+               #size-cells = <0>;
+               poll-interval = <20>;
+
+               service {
+                       label = "service";
+                       gpios = <&pinctrl 16 1>;
+                       linux,code = <BTN_0>;
+                       debounce-interval = <60>;
+               };
+
+               wlan {
+                       label = "wlan";
+                       gpios = <&pinctrl 20 0>;
+                       linux,code = <KEY_WLAN>;
+                       debounce-interval = <60>;
+               };
+
+               eco {
+                       label = "eco";
+                       gpios = <&pinctrl 22 0>;
+                       linux,code = <BTN_1>;
+                       debounce-interval = <60>;
+               };
+
+               wps {
+                       label = "wlan";
+                       gpios = <&pinctrl 33 1>;
+                       linux,code = <KEY_WPS_BUTTON>;
+                       debounce-interval = <60>;
+               };
+
+               reset {
+                       label = "reset";
+                       gpios = <&pinctrl 34 1>;
+                       linux,code = <KEY_RESTART>;
+                       debounce-interval = <60>;
+               };
+       };
+
+       pwm-leds {
+               compatible = "pwm-leds";
+
+               led_service1_red: service1_red {
+                       label = "neufbox-6v-foxconn-r0:red:service1";
+                       pwms = <&pwm 0 31>;
+                       max-brightness = <31>;
+               };
+
+               service2_red {
+                       label = "neufbox-6v-foxconn-r0:red:service2";
+                       pwms = <&pwm 1 31>;
+                       max-brightness = <31>;
+               };
+
+               service3_red {
+                       label = "neufbox-6v-foxconn-r0:red:service3";
+                       pwms = <&pwm 2 31>;
+                       max-brightness = <31>;
+               };
+
+               led_service1_green: service1_green {
+                       label = "neufbox-6v-foxconn-r0:green:service1";
+                       pwms = <&pwm 3 31>;
+                       max-brightness = <31>;
+                       default-state = "on";
+               };
+
+               service2_green {
+                       label = "neufbox-6v-foxconn-r0:green:service2";
+                       pwms = <&pwm 4 31>;
+                       max-brightness = <31>;
+               };
+
+               service3_green {
+                       label = "neufbox-6v-foxconn-r0:green:service3";
+                       pwms = <&pwm 5 31>;
+                       max-brightness = <31>;
+               };
+
+               wan_white {
+                       label = "neufbox-6v-foxconn-r0:white:wan";
+                       pwms = <&pwm 6 31>;
+                       max-brightness = <31>;
+               };
+
+               voip_white {
+                       label = "neufbox-6v-foxconn-r0:white:voip";
+                       pwms = <&pwm 7 31>;
+                       max-brightness = <31>;
+               };
+
+               wlan_white {
+                       label = "neufbox-6v-foxconn-r0:white:wlan";
+                       pwms = <&pwm 8 31>;
+                       max-brightness = <31>;
+               };
+       };
+
+       switch {
+               compatible = "realtek,rtl8367b";
+               gpio-sda = <&pinctrl 8 0>;
+               gpio-sck = <&pinctrl 9 0>;
+
+               realtek,extif0 = <1 5 1 1 1 1 1 1 2>;
+       };
+};
+
+&nflash {
+       status = "okay";
+
+       nandcs@0 {
+               compatible = "brcm,nandcs";
+               #size-cells = <1>;
+               #address-cells = <1>;
+               reg = <0>;
+               nand-ecc-step-size = <512>;
+               nand-ecc-strength = <15>;
+
+               partitions {
+                       compatible = "fixed-partitions";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+
+                       nand@0 {
+                               label = "nand";
+                               reg = <0x0000000 0x4000000>;
+                               read-only;
+                       };
+
+#if 0
+                       partition@0 {
+                               label = "cferom";
+                               reg = <0x0000000 0x0004000>;
+                               read-only;
+                       };
+
+                       partition@4000 {
+                               compatible = "brcm,wfi";
+                               label = "wfi";
+                               reg = <0x0004000 0x3efc000>;
+                       };
+#endif
+               };
+       };
+};
+
+&uart0 {
+       status = "ok";
+};
index af172564a24fd605f0eadc0a16345ab50693eac8..55ace95dcb55e23806e6a753448dbf71d9bb70c5 100644 (file)
@@ -2,17 +2,39 @@
 
 #include "bcm6362.dtsi"
 
+#include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/input/input.h>
 
 / {
        model = "SFR Neufbox 6 (Sercomm)";
        compatible = "sfr,neufbox-6-sercomm-r0", "brcm,bcm6361", "brcm,bcm6362";
 
+       aliases {
+               led-boot = &led_service1_green;
+               led-failsafe = &led_service1_red;
+               led-running = &led_service1_green;
+               led-upgrade = &led_service1_green;
+       };
+
        chosen {
                bootargs = "rootfstype=squashfs,jffs2 noinitrd console=ttyS0,115200";
                stdout-path = "serial0:115200n8";
        };
 
+       i2c {
+               compatible = "i2c-gpio";
+               sda-gpios = <&pinctrl 30 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
+               scl-gpios = <&pinctrl 2 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               pwm: nb6-hwmon@60 {
+                       compatible = "sfr,nb6_hwmon";
+                       reg = <0x60>;
+                       #pwm-cells = <2>;
+               };
+       };
+
        keys {
                compatible = "gpio-keys-polled";
                #address-cells = <1>;
                };
        };
 
+       pwm-leds {
+               compatible = "pwm-leds";
+
+               led_service1_red: service1_red {
+                       label = "neufbox-6v-foxconn-r0:red:service1";
+                       pwms = <&pwm 0 31>;
+                       max-brightness = <31>;
+               };
+
+               service2_red {
+                       label = "neufbox-6v-foxconn-r0:red:service2";
+                       pwms = <&pwm 1 31>;
+                       max-brightness = <31>;
+               };
+
+               service3_red {
+                       label = "neufbox-6v-foxconn-r0:red:service3";
+                       pwms = <&pwm 2 31>;
+                       max-brightness = <31>;
+               };
+
+               led_service1_green: service1_green {
+                       label = "neufbox-6v-foxconn-r0:green:service1";
+                       pwms = <&pwm 3 31>;
+                       max-brightness = <31>;
+                       default-state = "on";
+               };
+
+               service2_green {
+                       label = "neufbox-6v-foxconn-r0:green:service2";
+                       pwms = <&pwm 4 31>;
+                       max-brightness = <31>;
+               };
+
+               service3_green {
+                       label = "neufbox-6v-foxconn-r0:green:service3";
+                       pwms = <&pwm 5 31>;
+                       max-brightness = <31>;
+               };
+
+               wan_white {
+                       label = "neufbox-6v-foxconn-r0:white:wan";
+                       pwms = <&pwm 6 31>;
+                       max-brightness = <31>;
+               };
+
+               voip_white {
+                       label = "neufbox-6v-foxconn-r0:white:voip";
+                       pwms = <&pwm 7 31>;
+                       max-brightness = <31>;
+               };
+
+               wlan_white {
+                       label = "neufbox-6v-foxconn-r0:white:wlan";
+                       pwms = <&pwm 8 31>;
+                       max-brightness = <31>;
+               };
+       };
+
        switch {
                compatible = "realtek,rtl8367";
                gpio-sda = <&pinctrl 18 0>;
diff --git a/target/linux/bcm63xx/files/drivers/hwmon/nb6-hwmon.c b/target/linux/bcm63xx/files/drivers/hwmon/nb6-hwmon.c
new file mode 100644 (file)
index 0000000..5a7d2f0
--- /dev/null
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for SFR NeufBox 6 Hardware Monitoring
+ *
+ * Copyright 2020 Álvaro Fernández Rojas <noltari@gmail.com>
+ * Copyright 2010 Miguel Gaio <miguel.gaio@efixo.com>
+ */
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/pwm.h>
+#include <linux/sysfs.h>
+
+#define NB6_LEDS_PWM_REG       0x10
+#define NB6_TEMP_REG           0x22
+#define NB6_VOLTAGE_REG                0x24
+#define NB6_DMESG_CTL_REG      0x26
+#define NB6_DMESG_VAL_REG      0x27
+#define NB6_LEDS_MODE_REG      0x31
+#define NB6_RELEASE_REG                0x90
+#define NB6_STATS_BOOT_REG     0xa0
+#define NB6_STATS_PANIC_REG    0xa1
+#define NB6_STATS_OOPS_REG     0xa2
+#define NB6_WDT_REG            0xee
+
+#define NB6_DELAY              1000
+#define NB6_DMESG_SIZE         512
+#define NB6_I2C_RETRIES                5
+#define NB6_LEDS_MODE_LEN      32
+#define NB6_LEDS_PWM_COUNT     9
+#define NB6_PWM(x)             (NB6_LEDS_PWM_REG + x)
+#define NB6_WDT_LEN            10
+
+#define ADC_quantum(Vref)      ((1000 * (Vref)) / 1024)
+#define ADC_mV(Vref,x)         ((ADC_quantum(Vref) * (x)) / 1000)
+#define ADC_Temperature(t)     (1000 * (100 * ADC_mV(1800, t)) / 349)
+#define MR1                    82
+#define MR2                    20
+#define ADC_Voltage(v)         ((ADC_mV(2400, v) * ((10 * (MR1 + MR2)) / MR2)) / 10)
+
+struct nb6_data {
+       struct pwm_chip pwm;
+       struct i2c_client *i2c;
+       struct device *dev;
+       struct mutex lock;
+       u8 leds_mode;
+       u8 leds_pwm[NB6_LEDS_PWM_COUNT];
+       u8 release;
+       u8 stats_boot;
+       u8 stats_panic;
+       u8 stats_oops;
+       u16 temperature;
+       u16 voltage;
+       u8 watchdog;
+};
+
+enum LEDS_MODE {
+       LEDS_MODE_DISABLE,
+       LEDS_MODE_BOOT,
+       LEDS_MODE_BOOT_MAIN,
+       LEDS_MODE_BOOT_TFTP,
+       LEDS_MODE_BOOT_RESCUE,
+       LEDS_MODE_LOGIN,
+       LEDS_MODE_BURNING,
+       LEDS_MODE_DOWNLOAD,
+       LEDS_MODE_WDT_TEMPERATURE,
+       LEDS_MODE_WDT_VOLTAGE,
+       LEDS_MODE_PANIC,
+       LEDS_MODE_CONTROL,
+       LEDS_MODE_NUM
+};
+
+static char const *leds_modes_str[] = {
+       [LEDS_MODE_DISABLE] = "disable",
+       [LEDS_MODE_BOOT] = "boot",
+       [LEDS_MODE_BOOT_MAIN] = "boot-main",
+       [LEDS_MODE_BOOT_TFTP] = "boot-tftp",
+       [LEDS_MODE_BOOT_RESCUE] = "boot-rescue",
+       [LEDS_MODE_LOGIN] = "login",
+       [LEDS_MODE_BURNING] = "burning",
+       [LEDS_MODE_DOWNLOAD] = "downloading",
+       [LEDS_MODE_WDT_TEMPERATURE] = "wdt-temperature",
+       [LEDS_MODE_WDT_VOLTAGE] = "wdt-voltage",
+       [LEDS_MODE_PANIC] = "panic",
+       [LEDS_MODE_CONTROL] = "control",
+};
+
+/* I2C Helpers */
+
+static u8 nb6_readb(struct nb6_data *data, u8 addr, u8 val)
+{
+       int status;
+       unsigned i;
+
+       for (i = 0; i < NB6_I2C_RETRIES; i++) {
+               status = i2c_smbus_read_byte_data(data->i2c, addr);
+               if (status >= 0)
+                       return status;
+               udelay(NB6_DELAY);
+       }
+
+       dev_err(data->dev, "read error (%d): addr=0x%02x", status, addr);
+
+       return val;
+}
+
+static u16 nb6_readw(struct nb6_data *data, u8 addr, u16 val)
+{
+       u8 tmp;
+
+       tmp = nb6_readb(data, addr, (val >> 8) & 0xff);
+       val = (val & 0xff) | (tmp << 8);
+
+       tmp = nb6_readb(data, addr + 1, val & 0xff);
+       val = (val & 0xff00) | tmp;
+
+       return val;
+}
+
+static s32 nb6_writeb(struct nb6_data *data, u8 addr, u8 val)
+{
+       int status;
+       unsigned i;
+
+       for (i = 0; i < NB6_I2C_RETRIES; i++) {
+               status = i2c_smbus_write_byte_data(data->i2c, addr, val);
+               if (!status)
+                       return 0;
+               udelay(NB6_DELAY);
+       }
+
+       dev_err(data->dev, "write error (%d): addr=0x%02x val=0x%02x", status, addr, val);
+
+       return status;
+}
+
+static inline void leds_mode_update(struct nb6_data *data, u8 val)
+{
+       if ((data->leds_mode != val) &&
+           !nb6_writeb(data, NB6_LEDS_MODE_REG, val))
+               data->leds_mode = val;
+}
+
+static inline void leds_pwm_update(struct nb6_data *data, u8 id, u8 val)
+{
+       if ((data->leds_pwm[id] != val) &&
+           !nb6_writeb(data, NB6_PWM(id), val))
+               data->leds_pwm[id] = val;
+}
+
+/* Hardware Monitoring */
+
+static ssize_t dmesg_show(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       struct nb6_data *data = dev_get_drvdata(dev);
+       unsigned i;
+
+       mutex_lock(&data->lock);
+
+       if (nb6_writeb(data, NB6_DMESG_CTL_REG, 0)) {
+               mutex_unlock(&data->lock);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < NB6_DMESG_SIZE; i++)
+               buf[i] = nb6_readb(data, NB6_DMESG_VAL_REG, ~0);
+
+       mutex_unlock(&data->lock);
+
+       *buf = '\0';
+
+       return i + 1;
+}
+
+static DEVICE_ATTR_RO(dmesg);
+
+static ssize_t leds_mode_show(struct device *dev,
+                             struct device_attribute *attr, char *buf)
+{
+       struct nb6_data *data = dev_get_drvdata(dev);
+       loff_t off = 0;
+       unsigned i;
+
+       mutex_lock(&data->lock);
+       data->leds_mode = nb6_readb(data, NB6_LEDS_MODE_REG, data->leds_mode);
+       mutex_unlock(&data->lock);
+
+       for (i = 0; i < ARRAY_SIZE(leds_modes_str); i++) {
+               off += sprintf(buf + off,
+                              (i == data->leds_mode) ? "[%s] " : "%s ",
+                              leds_modes_str[i]);
+       }
+
+       off += sprintf(buf + off, "\n");
+
+       return off;
+}
+
+static ssize_t leds_mode_store(struct device *dev,
+                              struct device_attribute *attr,
+                              const char *buf, size_t count)
+{
+       struct nb6_data *data = dev_get_drvdata(dev);
+       char _s[NB6_LEDS_MODE_LEN];
+       char *s;
+       unsigned i;
+
+       snprintf(_s, sizeof(_s), "%s", buf);
+       s = strstrip(_s);
+       for (i = 0; i < ARRAY_SIZE(leds_modes_str); i++) {
+               if (!strcmp(s, leds_modes_str[i])) {
+                       leds_mode_update(data, i);
+                       break;
+               }
+       }
+
+       return count;
+}
+
+static DEVICE_ATTR_RW(leds_mode);
+
+static ssize_t release_show(struct device *dev, struct device_attribute *attr,
+                           char *buf)
+{
+       struct nb6_data *data = dev_get_drvdata(dev);
+
+       mutex_lock(&data->lock);
+       data->release = nb6_readb(data, NB6_RELEASE_REG, data->release);
+       mutex_unlock(&data->lock);
+
+       return sprintf(buf, "%u\n", data->release);
+}
+
+static DEVICE_ATTR_RO(release);
+
+static ssize_t stats_show(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       struct nb6_data *data = dev_get_drvdata(dev);
+
+       mutex_lock(&data->lock);
+       data->stats_boot = nb6_readb(data, NB6_STATS_BOOT_REG,
+                                    data->stats_boot);
+       data->stats_panic = nb6_readb(data, NB6_STATS_PANIC_REG,
+                                     data->stats_panic);
+       data->stats_oops = nb6_readb(data, NB6_STATS_OOPS_REG,
+                                    data->stats_oops);
+       mutex_unlock(&data->lock);
+
+       return sprintf(buf, "boot: %u\npanic: %u\noops: %u\n",
+                      data->stats_boot, data->stats_panic, data->stats_oops);
+}
+
+static DEVICE_ATTR_RO(stats);
+
+static ssize_t temperature_show(struct device *dev,
+                               struct device_attribute *da, char *buf)
+{
+       struct nb6_data *data = dev_get_drvdata(dev);
+
+       mutex_lock(&data->lock);
+       data->temperature = nb6_readw(data, NB6_TEMP_REG, data->temperature);
+       mutex_unlock(&data->lock);
+
+       return sprintf(buf, "%u\n", ADC_Temperature(data->temperature));
+}
+
+static DEVICE_ATTR_RO(temperature);
+
+static ssize_t voltage_show(struct device *dev, struct device_attribute *attr,
+                           char *buf)
+{
+       struct nb6_data *data = dev_get_drvdata(dev);
+
+       mutex_lock(&data->lock);
+       data->voltage = nb6_readw(data, NB6_VOLTAGE_REG, data->voltage);
+       mutex_unlock(&data->lock);
+
+       return sprintf(buf, "%u\n", ADC_Voltage(data->voltage));
+}
+
+static DEVICE_ATTR_RO(voltage);
+
+static ssize_t watchdog_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       struct nb6_data *data = dev_get_drvdata(dev);
+
+       mutex_lock(&data->lock);
+       data->watchdog = nb6_readb(data, NB6_WDT_REG, data->watchdog);
+       mutex_unlock(&data->lock);
+
+       return sprintf(buf, "%u\n", data->watchdog);
+}
+
+static ssize_t watchdog_store(struct device *dev,
+                             struct device_attribute *attr, const char *buf,
+                             size_t len)
+{
+       struct nb6_data *data = dev_get_drvdata(dev);
+       unsigned long val;
+       int ret;
+
+       ret = kstrtoul(buf, 0, &val);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&data->lock);
+       if (!nb6_writeb(data, NB6_WDT_REG, val))
+               data->watchdog = val;
+       mutex_unlock(&data->lock);
+
+       return len;
+}
+
+static DEVICE_ATTR_RW(watchdog);
+
+static struct attribute *nb6_attrs[] = {
+       &dev_attr_dmesg.attr,
+       &dev_attr_leds_mode.attr,
+       &dev_attr_release.attr,
+       &dev_attr_stats.attr,
+       &dev_attr_temperature.attr,
+       &dev_attr_voltage.attr,
+       &dev_attr_watchdog.attr,
+       NULL,
+};
+
+ATTRIBUTE_GROUPS(nb6);
+
+/* PWM */
+
+static inline struct nb6_data *to_nb6_pwm(struct pwm_chip *chip)
+{
+       return container_of(chip, struct nb6_data, pwm);
+}
+
+static int nb6_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct nb6_data *data = to_nb6_pwm(chip);
+
+       mutex_lock(&data->lock);
+       if (!nb6_writeb(data, NB6_PWM(pwm->hwpwm), 0))
+               data->leds_pwm[pwm->hwpwm] = 0;
+       else
+               data->leds_pwm[pwm->hwpwm] = ~0;
+       mutex_unlock(&data->lock);
+
+       return 0;
+}
+
+static void nb6_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct nb6_data *data = to_nb6_pwm(chip);
+       unsigned i;
+
+       mutex_lock(&data->lock);
+       for (i = 0; i < NB6_LEDS_PWM_COUNT; i++) {
+               if (data->leds_pwm[i]) {
+                       leds_mode_update(data, LEDS_MODE_DISABLE);
+                       break;
+               }
+       }
+       mutex_unlock(&data->lock);
+}
+
+static int nb6_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
+                         int duty_ns, int period_ns)
+{
+       struct nb6_data *data = to_nb6_pwm(chip);
+
+       mutex_lock(&data->lock);
+       leds_mode_update(data, LEDS_MODE_CONTROL);
+       leds_pwm_update(data, pwm->hwpwm, duty_ns);
+       mutex_unlock(&data->lock);
+
+       return 0;
+}
+
+static int nb6_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct nb6_data *data = to_nb6_pwm(chip);
+
+       mutex_lock(&data->lock);
+       leds_mode_update(data, LEDS_MODE_CONTROL);
+       mutex_unlock(&data->lock);
+
+       return 0;
+}
+
+static void nb6_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct nb6_data *data = to_nb6_pwm(chip);
+
+       mutex_lock(&data->lock);
+       leds_pwm_update(data, pwm->hwpwm, 0);
+       mutex_unlock(&data->lock);
+}
+
+static const struct pwm_ops nb6_pwm_ops = {
+       .request = nb6_pwm_request,
+       .free = nb6_pwm_free,
+       .config = nb6_pwm_config,
+       .enable = nb6_pwm_enable,
+       .disable = nb6_pwm_disable,
+       .owner = THIS_MODULE,
+};
+
+/* Driver */
+
+static int nb6_hwmon_probe(struct i2c_client *client,
+                          const struct i2c_device_id *id)
+{
+       struct device *dev = &client->dev;
+       struct nb6_data *data;
+       struct device *hwmon_dev;
+       int ret;
+
+       data = devm_kzalloc(dev, sizeof(struct nb6_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->dev = dev;
+       data->i2c = client;
+       data->leds_mode = LEDS_MODE_NUM;
+       i2c_set_clientdata(client, data);
+       mutex_init(&data->lock);
+
+       hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+                                                          data,
+                                                          nb6_groups);
+       if (IS_ERR(hwmon_dev))
+               return PTR_ERR(hwmon_dev);
+
+       data->pwm.dev = dev;
+       data->pwm.ops = &nb6_pwm_ops;
+       data->pwm.base = -1;
+       data->pwm.npwm = NB6_LEDS_PWM_COUNT;
+
+       ret = pwmchip_add(&data->pwm);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+};
+
+static const struct of_device_id nb6_hwmon_of_match[] = {
+       { .compatible = "sfr,nb6_hwmon" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, nb6_hwmon_of_match);
+
+static struct i2c_driver nb6_hwmon_driver = {
+       .class = I2C_CLASS_HWMON,
+       .driver = {
+               .name = "nb6-hwmon",
+               .of_match_table = of_match_ptr(nb6_hwmon_of_match),
+       },
+       .probe = nb6_hwmon_probe,
+};
+module_i2c_driver(nb6_hwmon_driver);
index 81c328b712ec0bb31d115cbce87c31b9c5a38d10..458a38ca350a2ef0f1a58666e6e3f335a93106ff 100644 (file)
@@ -203,3 +203,20 @@ define Device/sercomm_h500-s-vfes
   SERCOMM_VERSION := 1001
 endef
 TARGET_DEVICES += sercomm_h500-s-vfes
+
+### SFR ###
+define Device/sfr_neufbox-6v-foxconn-r0
+  $(Device/bcm63xx-nand)
+  DEVICE_VENDOR := SFR
+  DEVICE_MODEL := Neufbox 6V
+  CHIP_ID := 63268
+  SOC := bcm63168
+  CFE_RAM_FILE := sfr,neufbox-6v-foxconn-r0/cferam.000
+  CFE_RAM_JFFS2_NAME := cferam.000
+  BLOCKSIZE := 16k
+  PAGESIZE := 512
+  DEVICE_PACKAGES += $(USB2_PACKAGES)
+  CFE_WFI_FLASH_TYPE := 2
+  CFE_WFI_VERSION := 0x5731
+endef
+TARGET_DEVICES += sfr_neufbox-6v-foxconn-r0
diff --git a/target/linux/bcm63xx/patches-5.4/568-board-nb6v.patch b/target/linux/bcm63xx/patches-5.4/568-board-nb6v.patch
new file mode 100644 (file)
index 0000000..0d93f64
--- /dev/null
@@ -0,0 +1,50 @@
+--- a/arch/mips/bcm63xx/boards/board_bcm963xx.c
++++ b/arch/mips/bcm63xx/boards/board_bcm963xx.c
+@@ -2717,6 +2717,31 @@ static struct board_info __initdata boar
+       },
+ };
++static struct board_info __initdata board_nb6v = {
++      .name = "NB6V-FXC-r0",
++      .expected_cpu_id = 0x63268,
++
++      .has_ehci0 = 1,
++      .has_ohci0 = 1,
++      .num_usbh_ports = 2,
++
++      .has_enetsw = 1,
++      .enetsw = {
++              .used_ports = {
++                      [4] = {
++                              .used = 1,
++                              .phy_id = 0xff,
++                              .bypass_link = 1,
++                              .force_speed = 1000,
++                              .force_duplex_full = 1,
++                              .mii_override = 1,
++                              .timing_sel = 1,
++                              .name = "RGMII",
++                      },
++              },
++      },
++};
++
+ static struct board_info __initdata board_VG8050 = {
+       .name                           = "963169P-1861N5",
+       .expected_cpu_id                = 0x63268,
+@@ -3010,6 +3035,7 @@ static const struct board_info __initcon
+ #ifdef CONFIG_BCM63XX_CPU_63268
+       &board_963268bu_p300,
+       &board_963269bhr,
++      &board_nb6v,
+       &board_VG8050,
+       &board_VR3032u,
+       &board_vw6339gu,
+@@ -3135,6 +3161,7 @@ static struct of_device_id const bcm963x
+       { .compatible = "inteno,vg50", .data = &board_vw6339gu, },
+       { .compatible = "sercomm,h500-s-lowi", .data = &board_H500s, },
+       { .compatible = "sercomm,h500-s-vfes", .data = &board_H500s, },
++      { .compatible = "sfr,neufbox-6v-foxconn-r0", .data = &board_nb6v, },
+       { .compatible = "sky,sr102", .data = &board_BSKYB_63168, },
+ #endif
+ #endif /* CONFIG_OF */
diff --git a/target/linux/bcm63xx/patches-5.4/900-hwmon-nb6.patch b/target/linux/bcm63xx/patches-5.4/900-hwmon-nb6.patch
new file mode 100644 (file)
index 0000000..9997fce
--- /dev/null
@@ -0,0 +1,30 @@
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -992,6 +992,17 @@ config SENSORS_MENF21BMC_HWMON
+         This driver can also be built as a module. If so the module
+         will be called menf21bmc_hwmon.
++config SENSORS_NB6_HWMON
++      tristate "SFR NeufBox 6 (NB6) Hardware Monitoring"
++      depends on I2C
++      depends on PWM
++      help
++        If you say yes here you get support for SFR NeufBox 6 PWM LEDs,
++        temperature and voltage sensors.
++
++        This driver can also be built as a module. If so, the module
++        will be called nb6-hwmon.
++
+ config SENSORS_ADCXX
+       tristate "National Semiconductor ADCxxxSxxx"
+       depends on SPI_MASTER
+--- a/drivers/hwmon/Makefile
++++ b/drivers/hwmon/Makefile
+@@ -132,6 +132,7 @@ obj-$(CONFIG_SENSORS_MCP3021)      += mcp3021
+ obj-$(CONFIG_SENSORS_TC654)   += tc654.o
+ obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o
+ obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
++obj-$(CONFIG_SENSORS_NB6_HWMON)       += nb6-hwmon.o
+ obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
+ obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
+ obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o