From f0a6e45ae1f7d061ca88156087f5762d4e6dda12 Mon Sep 17 00:00:00 2001 From: August 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 = ; }; + mmc2_pins_a: mmc2@0 { + allwinner,pins = "PC06","PC07","PC08","PC09","PC10","PC11"; + allwinner,function = "mmc2"; + allwinner,drive = ; + allwinner,pull = ; + }; + gmac_pins_mii_a: gmac_mii@0 { allwinner,pins = "PA0", "PA1", "PA2", "PA3", "PA8", "PA9", "PA11", +/* + * Copyright 2014 Hans de Goede + * + * Hans de Goede + * + * 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 +#include +#include + +/ { + 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 = ; + allwinner,pull = ; + }; + + gmac_power_pin_bananapi: gmac_power_pin@0 { + allwinner,pins = "PH23"; + allwinner,function = "gpio_out"; + allwinner,drive = ; + allwinner,pull = ; + }; + + led_pins_bananapi: led_pins@0 { + allwinner,pins = "PH2"; + allwinner,function = "gpio_out"; + allwinner,drive = ; + allwinner,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 / { - 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 = ; - allwinner,pull = ; + allwinner,pull = ; }; 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 + * + * derived from ledtrig-netdev.c: + * Copyright 2007 Oliver Jowett + * + * ledtrig-netdev.c derived from ledtrig-timer.c: + * Copyright 2005-2006 Openedhand Ltd. + * Author: Richard Purdie + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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; }