766 lines
20 KiB
Plaintext
766 lines
20 KiB
Plaintext
From f0a6e45ae1f7d061ca88156087f5762d4e6dda12 Mon Sep 17 00:00:00 2001
|
|
From: August <mingxin.android@gmail.com>
|
|
Date: Thu, 27 Aug 2015 16:47:51 +0800
|
|
Subject: [PATCH] Bananapi BPI-M1, BPI-M1_Plus, BPI-M2 and BPI-R1 support
|
|
|
|
---
|
|
arch/arm/boot/dts/Makefile | 3 +
|
|
arch/arm/boot/dts/sun6i-a31.dtsi | 7 +
|
|
arch/arm/boot/dts/sun6i-a31s-bananapi-m2.dts | 250 +
|
|
arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts | 277 +
|
|
arch/arm/boot/dts/sun7i-a20-bananapi-r1.dts | 227 +
|
|
arch/arm/boot/dts/sun7i-a20-bananapi.dts | 5 +-
|
|
arch/arm/boot/dts/sun7i-a20.dtsi | 2 +-
|
|
arch/arm/configs/bpi_defconfig | 7031 ++++++++++++++++++++++
|
|
build.sh | 11 +
|
|
drivers/leds/trigger/Kconfig | 18 +
|
|
drivers/leds/trigger/Makefile | 3 +
|
|
drivers/leds/trigger/ledtrig-morse.c | 366 ++
|
|
drivers/leds/trigger/ledtrig-netdev.c | 438 ++
|
|
drivers/leds/trigger/ledtrig-usbdev.c | 348 ++
|
|
drivers/mmc/host/sunxi-mmc.c | 50 +-
|
|
32 files changed, 14406 insertions(+), 8 deletions(-)
|
|
mode change 100644 => 100755 arch/arm/boot/dts/Makefile
|
|
mode change 100644 => 100755 arch/arm/boot/dts/sun6i-a31.dtsi
|
|
create mode 100755 arch/arm/boot/dts/sun6i-a31s-bananapi-m2.dts
|
|
|
|
|
|
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
|
|
old mode 100644
|
|
new mode 100755
|
|
index fa2f403..54fe8bf
|
|
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
|
|
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
|
|
@@ -581,6 +581,13 @@
|
|
allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
|
|
};
|
|
|
|
+ mmc2_pins_a: mmc2@0 {
|
|
+ allwinner,pins = "PC06","PC07","PC08","PC09","PC10","PC11";
|
|
+ allwinner,function = "mmc2";
|
|
+ allwinner,drive = <SUN4I_PINCTRL_30_MA>;
|
|
+ allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
|
|
+ };
|
|
+
|
|
gmac_pins_mii_a: gmac_mii@0 {
|
|
allwinner,pins = "PA0", "PA1", "PA2", "PA3",
|
|
"PA8", "PA9", "PA11",
|
|
|
|
+/*
|
|
+ * Copyright 2014 Hans de Goede <hdegoede@redhat.com>
|
|
+ *
|
|
+ * Hans de Goede <hdegoede@redhat.com>
|
|
+ *
|
|
+ * This file is dual-licensed: you can use it either under the terms
|
|
+ * of the GPL or the X11 license, at your option. Note that this dual
|
|
+ * licensing only applies to this file, and not this project as a
|
|
+ * whole.
|
|
+ *
|
|
+ * a) This file 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 file 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.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public
|
|
+ * License along with this file; if not, write to the Free
|
|
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
|
|
+ * MA 02110-1301 USA
|
|
+ *
|
|
+ * Or, alternatively,
|
|
+ *
|
|
+ * b) Permission is hereby granted, free of charge, to any person
|
|
+ * obtaining a copy of this software and associated documentation
|
|
+ * files (the "Software"), to deal in the Software without
|
|
+ * restriction, including without limitation the rights to use,
|
|
+ * copy, modify, merge, publish, distribute, sublicense, and/or
|
|
+ * sell copies of the Software, and to permit persons to whom the
|
|
+ * Software is furnished to do so, subject to the following
|
|
+ * conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be
|
|
+ * included in all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
+ * OTHER DEALINGS IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+/dts-v1/;
|
|
+#include "sun7i-a20.dtsi"
|
|
+#include "sunxi-common-regulators.dtsi"
|
|
+
|
|
+#include <dt-bindings/gpio/gpio.h>
|
|
+#include <dt-bindings/interrupt-controller/irq.h>
|
|
+#include <dt-bindings/pinctrl/sun4i-a10.h>
|
|
+
|
|
+/ {
|
|
+ model = "Banana Pi BPI-R1";
|
|
+ compatible = "sinovoip,bpi-r1", "allwinner,sun7i-a20";
|
|
+
|
|
+ aliases {
|
|
+ serial0 = &uart0;
|
|
+ serial1 = &uart3;
|
|
+ serial2 = &uart7;
|
|
+ };
|
|
+
|
|
+
|
|
+ leds {
|
|
+ compatible = "gpio-leds";
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&led_pins_bananapi>;
|
|
+
|
|
+ green {
|
|
+ label = "bananapi:green:usr";
|
|
+ gpios = <&pio 7 2 GPIO_ACTIVE_HIGH>;
|
|
+ linux,default-trigger = "heartbeat";
|
|
+ };
|
|
+ };
|
|
+
|
|
+ soc@01c00000 {
|
|
+ spi0: spi@01c05000 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&spi0_pins_a>;
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ mmc0: mmc@01c0f000 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&mmc0_pins_a>, <&mmc0_cd_pin_bananapi>;
|
|
+ vmmc-supply = <®_vcc3v3>;
|
|
+ bus-width = <4>;
|
|
+ cd-gpios = <&pio 7 10 GPIO_ACTIVE_HIGH>; /* PH10 */
|
|
+ cd-inverted;
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ usbphy: phy@01c13400 {
|
|
+ usb1_vbus-supply = <®_usb1_vbus>;
|
|
+ usb2_vbus-supply = <®_usb2_vbus>;
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ ehci0: usb@01c14000 {
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ ohci0: usb@01c14400 {
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ ahci: sata@01c18000 {
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ ehci1: usb@01c1c000 {
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ ohci1: usb@01c1c400 {
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ pinctrl@01c20800 {
|
|
+ mmc0_cd_pin_bananapi: mmc0_cd_pin@0 {
|
|
+ allwinner,pins = "PH10";
|
|
+ allwinner,function = "gpio_in";
|
|
+ allwinner,drive = <SUN4I_PINCTRL_10_MA>;
|
|
+ allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
|
|
+ };
|
|
+
|
|
+ gmac_power_pin_bananapi: gmac_power_pin@0 {
|
|
+ allwinner,pins = "PH23";
|
|
+ allwinner,function = "gpio_out";
|
|
+ allwinner,drive = <SUN4I_PINCTRL_10_MA>;
|
|
+ allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
|
|
+ };
|
|
+
|
|
+ led_pins_bananapi: led_pins@0 {
|
|
+ allwinner,pins = "PH2";
|
|
+ allwinner,function = "gpio_out";
|
|
+ allwinner,drive = <SUN4I_PINCTRL_10_MA>;
|
|
+ allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ ir0: ir@01c21800 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&ir0_pins_a>;
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ uart0: serial@01c28000 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&uart0_pins_a>;
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ uart3: serial@01c28c00 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&uart3_pins_b>;
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ uart7: serial@01c29c00 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&uart7_pins_a>;
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ i2c0: i2c@01c2ac00 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&i2c0_pins_a>;
|
|
+ status = "okay";
|
|
+
|
|
+ axp209: pmic@34 {
|
|
+ compatible = "x-powers,axp209";
|
|
+ reg = <0x34>;
|
|
+ interrupt-parent = <&nmi_intc>;
|
|
+ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
|
|
+
|
|
+ interrupt-controller;
|
|
+ #interrupt-cells = <1>;
|
|
+ };
|
|
+ };
|
|
+
|
|
+ i2c2: i2c@01c2b400 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&i2c2_pins_a>;
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ gmac: ethernet@01c50000 {
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&gmac_pins_rgmii_a>;
|
|
+ phy = <&phy1>;
|
|
+ phy-mode = "rgmii";
|
|
+ phy-supply = <®_gmac_3v3>;
|
|
+ status = "okay";
|
|
+
|
|
+ phy1: ethernet-phy@1 {
|
|
+ reg = <1>;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
+
|
|
+
|
|
+ reg_usb1_vbus: usb1-vbus {
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ reg_usb2_vbus: usb2-vbus {
|
|
+ status = "okay";
|
|
+ };
|
|
+
|
|
+ reg_gmac_3v3: gmac-3v3 {
|
|
+ compatible = "regulator-fixed";
|
|
+ pinctrl-names = "default";
|
|
+ pinctrl-0 = <&gmac_power_pin_bananapi>;
|
|
+ regulator-name = "gmac-3v3";
|
|
+ regulator-min-microvolt = <3300000>;
|
|
+ regulator-max-microvolt = <3300000>;
|
|
+ startup-delay-us = <100000>;
|
|
+ enable-active-high;
|
|
+ gpio = <&pio 7 23 GPIO_ACTIVE_HIGH>;
|
|
+ };
|
|
+};
|
|
diff --git a/arch/arm/boot/dts/sun7i-a20-bananapi.dts b/arch/arm/boot/dts/sun7i-a20-bananapi.dts
|
|
old mode 100644
|
|
new mode 100755
|
|
index 5dd139e..6a75ebd
|
|
--- a/arch/arm/boot/dts/sun7i-a20-bananapi.dts
|
|
+++ b/arch/arm/boot/dts/sun7i-a20-bananapi.dts
|
|
@@ -56,8 +56,8 @@
|
|
#include <dt-bindings/pinctrl/sun4i-a10.h>
|
|
|
|
/ {
|
|
- model = "LeMaker Banana Pi";
|
|
- compatible = "lemaker,bananapi", "allwinner,sun7i-a20";
|
|
+ model = "Banana Pi BPI-M1";
|
|
+ compatible = "sinovoip,bpi-m1", "allwinner,sun7i-a20";
|
|
|
|
aliases {
|
|
serial0 = &uart0;
|
|
@@ -199,6 +199,7 @@
|
|
green {
|
|
label = "bananapi:green:usr";
|
|
gpios = <&pio 7 24 GPIO_ACTIVE_HIGH>;
|
|
+ linux,default-trigger = "heartbeat";
|
|
};
|
|
};
|
|
|
|
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
|
|
old mode 100644
|
|
new mode 100755
|
|
index 2b4847c..79e62ee
|
|
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
|
|
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
|
|
@@ -948,7 +948,7 @@
|
|
allwinner,pins = "PI4","PI5","PI6","PI7","PI8","PI9";
|
|
allwinner,function = "mmc3";
|
|
allwinner,drive = <SUN4I_PINCTRL_30_MA>;
|
|
- allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
|
|
+ allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
|
|
};
|
|
|
|
ir0_pins_a: ir0@0 {
|
|
|
|
|
|
new file mode 100755
|
|
index 0000000..70b0e39
|
|
--- /dev/null
|
|
+++ b/drivers/leds/trigger/ledtrig-usbdev.c
|
|
@@ -0,0 +1,348 @@
|
|
+/*
|
|
+ * LED USB device Trigger
|
|
+ *
|
|
+ * Toggles the LED to reflect the presence and activity of an USB device
|
|
+ * Copyright (C) Gabor Juhos <juhosg@openwrt.org>
|
|
+ *
|
|
+ * derived from ledtrig-netdev.c:
|
|
+ * Copyright 2007 Oliver Jowett <oliver@opencloud.com>
|
|
+ *
|
|
+ * ledtrig-netdev.c derived from ledtrig-timer.c:
|
|
+ * 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/sysdev.h>
|
|
+#include <linux/timer.h>
|
|
+#include <linux/ctype.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/leds.h>
|
|
+#include <linux/usb.h>
|
|
+
|
|
+#include "leds.h"
|
|
+
|
|
+#define DEV_BUS_ID_SIZE 32
|
|
+
|
|
+/*
|
|
+ * Configurable sysfs attributes:
|
|
+ *
|
|
+ * device_name - name of the USB device to monitor
|
|
+ * activity_interval - duration of LED blink, in milliseconds
|
|
+ */
|
|
+
|
|
+struct usbdev_trig_data {
|
|
+ rwlock_t lock;
|
|
+
|
|
+ struct timer_list timer;
|
|
+ struct notifier_block notifier;
|
|
+
|
|
+ struct led_classdev *led_cdev;
|
|
+ struct usb_device *usb_dev;
|
|
+
|
|
+ char device_name[DEV_BUS_ID_SIZE];
|
|
+ unsigned interval;
|
|
+ int last_urbnum;
|
|
+};
|
|
+
|
|
+static void usbdev_trig_update_state(struct usbdev_trig_data *td)
|
|
+{
|
|
+ if (td->usb_dev)
|
|
+ led_set_brightness(td->led_cdev, LED_FULL);
|
|
+ else
|
|
+ led_set_brightness(td->led_cdev, LED_OFF);
|
|
+
|
|
+ if (td->interval && td->usb_dev)
|
|
+ mod_timer(&td->timer, jiffies + td->interval);
|
|
+ else
|
|
+ del_timer(&td->timer);
|
|
+}
|
|
+
|
|
+static ssize_t usbdev_trig_name_show(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
|
+ struct usbdev_trig_data *td = led_cdev->trigger_data;
|
|
+
|
|
+ read_lock(&td->lock);
|
|
+ sprintf(buf, "%s\n", td->device_name);
|
|
+ read_unlock(&td->lock);
|
|
+
|
|
+ return strlen(buf) + 1;
|
|
+}
|
|
+
|
|
+static ssize_t usbdev_trig_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 usbdev_trig_data *td = led_cdev->trigger_data;
|
|
+
|
|
+ if (size < 0 || size >= DEV_BUS_ID_SIZE)
|
|
+ return -EINVAL;
|
|
+
|
|
+ write_lock(&td->lock);
|
|
+
|
|
+ strcpy(td->device_name, buf);
|
|
+ if (size > 0 && td->device_name[size - 1] == '\n')
|
|
+ td->device_name[size - 1] = 0;
|
|
+
|
|
+ if (td->device_name[0] != 0) {
|
|
+ struct usb_device *usb_dev;
|
|
+
|
|
+ /* check for existing device to update from */
|
|
+ usb_dev = usb_find_device_by_name(td->device_name);
|
|
+ if (usb_dev) {
|
|
+ if (td->usb_dev)
|
|
+ usb_put_dev(td->usb_dev);
|
|
+
|
|
+ td->usb_dev = usb_dev;
|
|
+ td->last_urbnum = atomic_read(&usb_dev->urbnum);
|
|
+ }
|
|
+
|
|
+ /* updates LEDs, may start timers */
|
|
+ usbdev_trig_update_state(td);
|
|
+ }
|
|
+
|
|
+ write_unlock(&td->lock);
|
|
+ return size;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(device_name, 0644, usbdev_trig_name_show,
|
|
+ usbdev_trig_name_store);
|
|
+
|
|
+static ssize_t usbdev_trig_interval_show(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
|
+ struct usbdev_trig_data *td = led_cdev->trigger_data;
|
|
+
|
|
+ read_lock(&td->lock);
|
|
+ sprintf(buf, "%u\n", jiffies_to_msecs(td->interval));
|
|
+ read_unlock(&td->lock);
|
|
+
|
|
+ return strlen(buf) + 1;
|
|
+}
|
|
+
|
|
+static ssize_t usbdev_trig_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 usbdev_trig_data *td = led_cdev->trigger_data;
|
|
+ int ret = -EINVAL;
|
|
+ char *after;
|
|
+ unsigned long value = simple_strtoul(buf, &after, 10);
|
|
+ size_t count = after - buf;
|
|
+
|
|
+ if (*after && isspace(*after))
|
|
+ count++;
|
|
+
|
|
+ if (count == size && value <= 10000) {
|
|
+ write_lock(&td->lock);
|
|
+ td->interval = msecs_to_jiffies(value);
|
|
+ usbdev_trig_update_state(td); /* resets timer */
|
|
+ write_unlock(&td->lock);
|
|
+ ret = count;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static DEVICE_ATTR(activity_interval, 0644, usbdev_trig_interval_show,
|
|
+ usbdev_trig_interval_store);
|
|
+
|
|
+static int usbdev_trig_notify(struct notifier_block *nb,
|
|
+ unsigned long evt,
|
|
+ void *data)
|
|
+{
|
|
+ struct usb_device *usb_dev;
|
|
+ struct usbdev_trig_data *td;
|
|
+
|
|
+ if (evt != USB_DEVICE_ADD && evt != USB_DEVICE_REMOVE)
|
|
+ return NOTIFY_DONE;
|
|
+
|
|
+ usb_dev = data;
|
|
+ td = container_of(nb, struct usbdev_trig_data, notifier);
|
|
+
|
|
+ write_lock(&td->lock);
|
|
+
|
|
+ if (strcmp(dev_name(&usb_dev->dev), td->device_name))
|
|
+ goto done;
|
|
+
|
|
+ if (evt == USB_DEVICE_ADD) {
|
|
+ usb_get_dev(usb_dev);
|
|
+ if (td->usb_dev != NULL)
|
|
+ usb_put_dev(td->usb_dev);
|
|
+ td->usb_dev = usb_dev;
|
|
+ td->last_urbnum = atomic_read(&usb_dev->urbnum);
|
|
+ } else if (evt == USB_DEVICE_REMOVE) {
|
|
+ if (td->usb_dev != NULL) {
|
|
+ usb_put_dev(td->usb_dev);
|
|
+ td->usb_dev = NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ usbdev_trig_update_state(td);
|
|
+
|
|
+done:
|
|
+ write_unlock(&td->lock);
|
|
+ return NOTIFY_DONE;
|
|
+}
|
|
+
|
|
+/* here's the real work! */
|
|
+static void usbdev_trig_timer(unsigned long arg)
|
|
+{
|
|
+ struct usbdev_trig_data *td = (struct usbdev_trig_data *)arg;
|
|
+ int new_urbnum;
|
|
+
|
|
+ write_lock(&td->lock);
|
|
+
|
|
+ if (!td->usb_dev || td->interval == 0) {
|
|
+ /*
|
|
+ * we don't need to do timer work, just reflect device presence
|
|
+ */
|
|
+ if (td->usb_dev)
|
|
+ led_set_brightness(td->led_cdev, LED_FULL);
|
|
+ else
|
|
+ led_set_brightness(td->led_cdev, LED_OFF);
|
|
+
|
|
+ goto no_restart;
|
|
+ }
|
|
+
|
|
+ if (td->interval)
|
|
+ new_urbnum = atomic_read(&td->usb_dev->urbnum);
|
|
+ else
|
|
+ new_urbnum = 0;
|
|
+
|
|
+ if (td->usb_dev) {
|
|
+ /*
|
|
+ * Base state is ON (device is present). If there's no device,
|
|
+ * we don't get this far and the LED is off.
|
|
+ * OFF -> ON always
|
|
+ * ON -> OFF on activity
|
|
+ */
|
|
+ if (td->led_cdev->brightness == LED_OFF)
|
|
+ led_set_brightness(td->led_cdev, LED_FULL);
|
|
+ else if (td->last_urbnum != new_urbnum)
|
|
+ led_set_brightness(td->led_cdev, LED_OFF);
|
|
+ } else {
|
|
+ /*
|
|
+ * base state is OFF
|
|
+ * ON -> OFF always
|
|
+ * OFF -> ON on activity
|
|
+ */
|
|
+ if (td->led_cdev->brightness == LED_FULL)
|
|
+ led_set_brightness(td->led_cdev, LED_OFF);
|
|
+ else if (td->last_urbnum != new_urbnum)
|
|
+ led_set_brightness(td->led_cdev, LED_FULL);
|
|
+ }
|
|
+
|
|
+ td->last_urbnum = new_urbnum;
|
|
+ mod_timer(&td->timer, jiffies + td->interval);
|
|
+
|
|
+no_restart:
|
|
+ write_unlock(&td->lock);
|
|
+}
|
|
+
|
|
+static void usbdev_trig_activate(struct led_classdev *led_cdev)
|
|
+{
|
|
+ struct usbdev_trig_data *td;
|
|
+ int rc;
|
|
+
|
|
+ td = kzalloc(sizeof(struct usbdev_trig_data), GFP_KERNEL);
|
|
+ if (!td)
|
|
+ return;
|
|
+
|
|
+ rwlock_init(&td->lock);
|
|
+
|
|
+ td->notifier.notifier_call = usbdev_trig_notify;
|
|
+ td->notifier.priority = 10;
|
|
+
|
|
+ setup_timer(&td->timer, usbdev_trig_timer, (unsigned long) td);
|
|
+
|
|
+ td->led_cdev = led_cdev;
|
|
+ td->interval = msecs_to_jiffies(50);
|
|
+
|
|
+ led_cdev->trigger_data = td;
|
|
+
|
|
+ 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_activity_interval);
|
|
+ if (rc)
|
|
+ goto err_out_device_name;
|
|
+
|
|
+ usb_register_notify(&td->notifier);
|
|
+ return;
|
|
+
|
|
+err_out_device_name:
|
|
+ device_remove_file(led_cdev->dev, &dev_attr_device_name);
|
|
+err_out:
|
|
+ led_cdev->trigger_data = NULL;
|
|
+ kfree(td);
|
|
+}
|
|
+
|
|
+static void usbdev_trig_deactivate(struct led_classdev *led_cdev)
|
|
+{
|
|
+ struct usbdev_trig_data *td = led_cdev->trigger_data;
|
|
+
|
|
+ if (td) {
|
|
+ usb_unregister_notify(&td->notifier);
|
|
+
|
|
+ device_remove_file(led_cdev->dev, &dev_attr_device_name);
|
|
+ device_remove_file(led_cdev->dev, &dev_attr_activity_interval);
|
|
+
|
|
+ write_lock(&td->lock);
|
|
+
|
|
+ if (td->usb_dev) {
|
|
+ usb_put_dev(td->usb_dev);
|
|
+ td->usb_dev = NULL;
|
|
+ }
|
|
+
|
|
+ write_unlock(&td->lock);
|
|
+
|
|
+ del_timer_sync(&td->timer);
|
|
+
|
|
+ kfree(td);
|
|
+ }
|
|
+}
|
|
+
|
|
+static struct led_trigger usbdev_led_trigger = {
|
|
+ .name = "usbdev",
|
|
+ .activate = usbdev_trig_activate,
|
|
+ .deactivate = usbdev_trig_deactivate,
|
|
+};
|
|
+
|
|
+static int __init usbdev_trig_init(void)
|
|
+{
|
|
+ return led_trigger_register(&usbdev_led_trigger);
|
|
+}
|
|
+
|
|
+static void __exit usbdev_trig_exit(void)
|
|
+{
|
|
+ led_trigger_unregister(&usbdev_led_trigger);
|
|
+}
|
|
+
|
|
+module_init(usbdev_trig_init);
|
|
+module_exit(usbdev_trig_exit);
|
|
+
|
|
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
|
|
+MODULE_DESCRIPTION("USB device LED trigger");
|
|
+MODULE_LICENSE("GPL v2");
|
|
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
|
|
old mode 100644
|
|
new mode 100755
|
|
index 4d3e1ff..8906880
|
|
--- a/drivers/mmc/host/sunxi-mmc.c
|
|
+++ b/drivers/mmc/host/sunxi-mmc.c
|
|
@@ -243,6 +243,7 @@ struct sunxi_mmc_host {
|
|
bool wait_dma;
|
|
|
|
struct mmc_request *mrq;
|
|
+ struct mmc_request *mrq_cmd53;
|
|
struct mmc_request *manual_stop_mrq;
|
|
int ferror;
|
|
};
|
|
@@ -448,6 +449,7 @@ static irqreturn_t sunxi_mmc_finalize_request(struct sunxi_mmc_host *host)
|
|
struct mmc_request *mrq = host->mrq;
|
|
struct mmc_data *data = mrq->data;
|
|
u32 rval;
|
|
+ u32 opcode = 0;
|
|
|
|
mmc_writel(host, REG_IMASK, host->sdio_imask);
|
|
mmc_writel(host, REG_IDIE, 0);
|
|
@@ -493,11 +495,15 @@ static irqreturn_t sunxi_mmc_finalize_request(struct sunxi_mmc_host *host)
|
|
|
|
mmc_writel(host, REG_RINTR, 0xffff);
|
|
|
|
- host->mrq = NULL;
|
|
- host->int_sum = 0;
|
|
- host->wait_dma = false;
|
|
+ opcode = host->mrq->cmd->opcode;
|
|
+ if(opcode == 53){
|
|
+ host->mrq_cmd53 = host->mrq;
|
|
+ }
|
|
+ host->mrq = NULL;
|
|
+ host->int_sum = 0;
|
|
+ host->wait_dma = false;
|
|
|
|
- return host->manual_stop_mrq ? IRQ_WAKE_THREAD : IRQ_HANDLED;
|
|
+ return ((host->manual_stop_mrq)||(opcode==53)) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
|
|
}
|
|
|
|
static irqreturn_t sunxi_mmc_irq(int irq, void *dev_id)
|
|
@@ -557,17 +563,51 @@ static irqreturn_t sunxi_mmc_irq(int irq, void *dev_id)
|
|
return ret;
|
|
}
|
|
|
|
+
|
|
+int sunxi_check_r1_ready(struct sunxi_mmc_host *smc_host, unsigned ms)
|
|
+{
|
|
+ //struct sunxi_mmc_host *smc_host = mmc_priv(mmc);
|
|
+ unsigned long expire = jiffies + msecs_to_jiffies(ms);
|
|
+ //dev_info(mmc_dev(smc_host->mmc), "wrd\n");
|
|
+ do {
|
|
+ if (!(mmc_readl(smc_host, REG_STAS) & SDXC_CARD_DATA_BUSY))
|
|
+ break;
|
|
+ } while (time_before(jiffies, expire));
|
|
+
|
|
+ if ((mmc_readl(smc_host, REG_STAS) & SDXC_CARD_DATA_BUSY)) {
|
|
+ dev_err(mmc_dev(smc_host->mmc), "wait r1 rdy %d ms timeout\n", ms);
|
|
+ return -1;
|
|
+ } else{
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
static irqreturn_t sunxi_mmc_handle_manual_stop(int irq, void *dev_id)
|
|
{
|
|
struct sunxi_mmc_host *host = dev_id;
|
|
struct mmc_request *mrq;
|
|
+ struct mmc_request *mrq_cmd53 = NULL;
|
|
unsigned long iflags;
|
|
|
|
spin_lock_irqsave(&host->lock, iflags);
|
|
mrq = host->manual_stop_mrq;
|
|
+ mrq_cmd53 = host->mrq_cmd53;
|
|
spin_unlock_irqrestore(&host->lock, iflags);
|
|
|
|
- if (!mrq) {
|
|
+ if (mrq_cmd53) {
|
|
+ sunxi_check_r1_ready(host,1000);
|
|
+ spin_lock_irqsave(&host->lock, iflags);
|
|
+ host->mrq_cmd53 = NULL;
|
|
+ spin_unlock_irqrestore(&host->lock, iflags);
|
|
+ mmc_request_done(host->mmc, mrq_cmd53);
|
|
+ return IRQ_HANDLED;
|
|
+ }else{
|
|
+ dev_err(mmc_dev(host->mmc), "no request for cmd53 busy\n");
|
|
+ }
|
|
+
|
|
+ if (!mrq) {
|
|
dev_err(mmc_dev(host->mmc), "no request for manual stop\n");
|
|
return IRQ_HANDLED;
|
|
}
|